]> git.decadent.org.uk Git - ion3.git/commitdiff
[svn-inject] Installing original source of ion3 20061223
authorBen Hutchings <ben@decadent.org.uk>
Sat, 24 Mar 2007 18:52:31 +0000 (18:52 +0000)
committerBen Hutchings <ben@decadent.org.uk>
Sat, 24 Mar 2007 18:52:31 +0000 (18:52 +0000)
507 files changed:
ChangeLog [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
RELNOTES [new file with mode: 0644]
TODO.README [new file with mode: 0644]
TODO.riot [new file with mode: 0644]
build/ac/README.autoconf [new file with mode: 0644]
build/ac/aclocal.m4 [new file with mode: 0644]
build/ac/configure.ac [new file with mode: 0644]
build/ac/system-ac.mk.in [new file with mode: 0644]
build/libs.mk [new file with mode: 0644]
build/mkman.lua [new file with mode: 0644]
build/mkpreload.lua [new file with mode: 0644]
build/rules.mk [new file with mode: 0644]
build/system-inc.mk [new file with mode: 0644]
config.h [new file with mode: 0644]
de/Makefile [new file with mode: 0644]
de/brush.c [new file with mode: 0644]
de/brush.h [new file with mode: 0644]
de/colour.c [new file with mode: 0644]
de/colour.h [new file with mode: 0644]
de/draw.c [new file with mode: 0644]
de/font.c [new file with mode: 0644]
de/font.h [new file with mode: 0644]
de/fontset.c [new file with mode: 0644]
de/fontset.h [new file with mode: 0644]
de/init.c [new file with mode: 0644]
de/init.h [new file with mode: 0644]
de/private.h [new file with mode: 0644]
de/style.c [new file with mode: 0644]
de/style.h [new file with mode: 0644]
doc/ChangeLog [new file with mode: 0644]
doc/LICENSE [new file with mode: 0644]
doc/Makefile [new file with mode: 0644]
doc/README [new file with mode: 0644]
doc/artikel3.perl [new file with mode: 0644]
doc/conf-bindings.tex [new file with mode: 0644]
doc/conf-menus.tex [new file with mode: 0644]
doc/conf-winprops.tex [new file with mode: 0644]
doc/conf.tex [new file with mode: 0644]
doc/confintro.tex [new file with mode: 0644]
doc/cstyle.tex [new file with mode: 0644]
doc/de.tex [new file with mode: 0644]
doc/designnotes.tex [new file with mode: 0644]
doc/exact-version [new file with mode: 0644]
doc/fnref.tex [new file with mode: 0644]
doc/fullhierarchy.tex [new file with mode: 0644]
doc/gpl.tex [new file with mode: 0644]
doc/hookref.tex [new file with mode: 0644]
doc/ionconf.dvi.gz [new file with mode: 0644]
doc/ionconf.out [new file with mode: 0644]
doc/ionconf.ps.gz [new file with mode: 0644]
doc/ionconf.tex [new file with mode: 0644]
doc/ionconf/WARNINGS [new file with mode: 0644]
doc/ionconf/contents.png [new file with mode: 0644]
doc/ionconf/images.aux [new file with mode: 0644]
doc/ionconf/images.idx [new file with mode: 0644]
doc/ionconf/images.log [new file with mode: 0644]
doc/ionconf/images.out [new file with mode: 0644]
doc/ionconf/images.pl [new file with mode: 0644]
doc/ionconf/images.tex [new file with mode: 0644]
doc/ionconf/img1.png [new file with mode: 0644]
doc/ionconf/img2.png [new file with mode: 0644]
doc/ionconf/img3.png [new file with mode: 0644]
doc/ionconf/img4.png [new file with mode: 0644]
doc/ionconf/img5.png [new file with mode: 0644]
doc/ionconf/img6.png [new file with mode: 0644]
doc/ionconf/index.html [new file with mode: 0644]
doc/ionconf/index.png [new file with mode: 0644]
doc/ionconf/internals.pl [new file with mode: 0644]
doc/ionconf/ionconf.css [new file with mode: 0644]
doc/ionconf/ionconf.html [new file with mode: 0644]
doc/ionconf/labels.pl [new file with mode: 0644]
doc/ionconf/next.png [new file with mode: 0644]
doc/ionconf/next_g.png [new file with mode: 0644]
doc/ionconf/node1.html [new file with mode: 0644]
doc/ionconf/node10.html [new file with mode: 0644]
doc/ionconf/node11.html [new file with mode: 0644]
doc/ionconf/node12.html [new file with mode: 0644]
doc/ionconf/node2.html [new file with mode: 0644]
doc/ionconf/node3.html [new file with mode: 0644]
doc/ionconf/node4.html [new file with mode: 0644]
doc/ionconf/node5.html [new file with mode: 0644]
doc/ionconf/node6.html [new file with mode: 0644]
doc/ionconf/node7.html [new file with mode: 0644]
doc/ionconf/node8.html [new file with mode: 0644]
doc/ionconf/node9.html [new file with mode: 0644]
doc/ionconf/prev.png [new file with mode: 0644]
doc/ionconf/prev_g.png [new file with mode: 0644]
doc/ionconf/up.png [new file with mode: 0644]
doc/ionnotes.dvi.gz [new file with mode: 0644]
doc/ionnotes.out [new file with mode: 0644]
doc/ionnotes.ps.gz [new file with mode: 0644]
doc/ionnotes.tex [new file with mode: 0644]
doc/ionnotes/WARNINGS [new file with mode: 0644]
doc/ionnotes/contents.png [new file with mode: 0644]
doc/ionnotes/crossref.png [new file with mode: 0644]
doc/ionnotes/index.html [new file with mode: 0644]
doc/ionnotes/index.png [new file with mode: 0644]
doc/ionnotes/internals.pl [new file with mode: 0644]
doc/ionnotes/ionnotes.css [new file with mode: 0644]
doc/ionnotes/ionnotes.html [new file with mode: 0644]
doc/ionnotes/labels.pl [new file with mode: 0644]
doc/ionnotes/next.png [new file with mode: 0644]
doc/ionnotes/next_g.png [new file with mode: 0644]
doc/ionnotes/node1.html [new file with mode: 0644]
doc/ionnotes/node2.html [new file with mode: 0644]
doc/ionnotes/node3.html [new file with mode: 0644]
doc/ionnotes/node4.html [new file with mode: 0644]
doc/ionnotes/node5.html [new file with mode: 0644]
doc/ionnotes/node6.html [new file with mode: 0644]
doc/ionnotes/node7.html [new file with mode: 0644]
doc/ionnotes/node8.html [new file with mode: 0644]
doc/ionnotes/node9.html [new file with mode: 0644]
doc/ionnotes/prev.png [new file with mode: 0644]
doc/ionnotes/prev_g.png [new file with mode: 0644]
doc/ionnotes/up.png [new file with mode: 0644]
doc/luaif.tex [new file with mode: 0644]
doc/macros.tex [new file with mode: 0644]
doc/objects.tex [new file with mode: 0644]
doc/objectsimpl.tex [new file with mode: 0644]
doc/predist.sh [new file with mode: 0644]
doc/prelim.tex [new file with mode: 0644]
doc/rapport3.perl [new file with mode: 0644]
doc/statusd.tex [new file with mode: 0644]
doc/tricks.tex [new file with mode: 0644]
etc/Makefile [new file with mode: 0644]
etc/cfg_dock.lua [new file with mode: 0644]
etc/cfg_ion.lua [new file with mode: 0644]
etc/cfg_ioncore.lua [new file with mode: 0644]
etc/cfg_kludges.lua [new file with mode: 0644]
etc/cfg_menu.lua [new file with mode: 0644]
etc/cfg_modules.lua [new file with mode: 0644]
etc/cfg_panews.lua [new file with mode: 0644]
etc/cfg_query.lua [new file with mode: 0644]
etc/cfg_statusbar.lua [new file with mode: 0644]
etc/cfg_tiling.lua [new file with mode: 0644]
etc/look_brownsteel.lua [new file with mode: 0644]
etc/look_clean.lua [new file with mode: 0644]
etc/look_cleanios.lua [new file with mode: 0644]
etc/look_cleanviolet.lua [new file with mode: 0644]
etc/look_dusky.lua [new file with mode: 0644]
etc/look_greyviolet.lua [new file with mode: 0644]
etc/look_ios.lua [new file with mode: 0644]
etc/look_simpleblue.lua [new file with mode: 0644]
etc/lookcommon_clean.lua [new file with mode: 0644]
etc/lookcommon_emboss.lua [new file with mode: 0644]
exact-version [new file with mode: 0644]
install-sh [new file with mode: 0755]
ion/Makefile [new file with mode: 0644]
ion/ion.c [new file with mode: 0644]
ioncore/Makefile [new file with mode: 0644]
ioncore/activity.c [new file with mode: 0644]
ioncore/activity.h [new file with mode: 0644]
ioncore/attach.c [new file with mode: 0644]
ioncore/attach.h [new file with mode: 0644]
ioncore/basicpholder.c [new file with mode: 0644]
ioncore/basicpholder.h [new file with mode: 0644]
ioncore/binding.c [new file with mode: 0644]
ioncore/binding.h [new file with mode: 0644]
ioncore/bindmaps.c [new file with mode: 0644]
ioncore/bindmaps.h [new file with mode: 0644]
ioncore/classes.h [new file with mode: 0644]
ioncore/clientwin.c [new file with mode: 0644]
ioncore/clientwin.h [new file with mode: 0644]
ioncore/colormap.c [new file with mode: 0644]
ioncore/colormap.h [new file with mode: 0644]
ioncore/common.h [new file with mode: 0644]
ioncore/conf-bindings.c [new file with mode: 0644]
ioncore/conf-bindings.h [new file with mode: 0644]
ioncore/conf.c [new file with mode: 0644]
ioncore/conf.h [new file with mode: 0644]
ioncore/cursor.c [new file with mode: 0644]
ioncore/cursor.h [new file with mode: 0644]
ioncore/dummywc.h [new file with mode: 0644]
ioncore/event.c [new file with mode: 0644]
ioncore/event.h [new file with mode: 0644]
ioncore/eventh.c [new file with mode: 0644]
ioncore/eventh.h [new file with mode: 0644]
ioncore/exec.c [new file with mode: 0644]
ioncore/exec.h [new file with mode: 0644]
ioncore/extlconv.c [new file with mode: 0644]
ioncore/extlconv.h [new file with mode: 0644]
ioncore/extlrx.c [new file with mode: 0644]
ioncore/float-placement.c [new file with mode: 0644]
ioncore/float-placement.h [new file with mode: 0644]
ioncore/focus.c [new file with mode: 0644]
ioncore/focus.h [new file with mode: 0644]
ioncore/frame-draw.c [new file with mode: 0644]
ioncore/frame-draw.h [new file with mode: 0644]
ioncore/frame-pointer.c [new file with mode: 0644]
ioncore/frame-pointer.h [new file with mode: 0644]
ioncore/frame.c [new file with mode: 0644]
ioncore/frame.h [new file with mode: 0644]
ioncore/framedpholder.c [new file with mode: 0644]
ioncore/framedpholder.h [new file with mode: 0644]
ioncore/framep.h [new file with mode: 0644]
ioncore/fullscreen.c [new file with mode: 0644]
ioncore/fullscreen.h [new file with mode: 0644]
ioncore/global.h [new file with mode: 0644]
ioncore/gr.c [new file with mode: 0644]
ioncore/gr.h [new file with mode: 0644]
ioncore/grab.c [new file with mode: 0644]
ioncore/grab.h [new file with mode: 0644]
ioncore/group-cw.c [new file with mode: 0644]
ioncore/group-cw.h [new file with mode: 0644]
ioncore/group-ws.c [new file with mode: 0644]
ioncore/group-ws.h [new file with mode: 0644]
ioncore/group.c [new file with mode: 0644]
ioncore/group.h [new file with mode: 0644]
ioncore/groupedpholder.c [new file with mode: 0644]
ioncore/groupedpholder.h [new file with mode: 0644]
ioncore/grouppholder.c [new file with mode: 0644]
ioncore/grouppholder.h [new file with mode: 0644]
ioncore/infowin.c [new file with mode: 0644]
ioncore/infowin.h [new file with mode: 0644]
ioncore/ioncore.c [new file with mode: 0644]
ioncore/ioncore.h [new file with mode: 0644]
ioncore/ioncore_bindings.lua [new file with mode: 0644]
ioncore/ioncore_efbb.lua [new file with mode: 0644]
ioncore/ioncore_ext.lua [new file with mode: 0644]
ioncore/ioncore_luaext.lua [new file with mode: 0644]
ioncore/ioncore_menudb.lua [new file with mode: 0644]
ioncore/ioncore_misc.lua [new file with mode: 0644]
ioncore/ioncore_wd.lua [new file with mode: 0644]
ioncore/ioncore_winprops.lua [new file with mode: 0644]
ioncore/kbresize.c [new file with mode: 0644]
ioncore/kbresize.h [new file with mode: 0644]
ioncore/key.c [new file with mode: 0644]
ioncore/key.h [new file with mode: 0644]
ioncore/llist.c [new file with mode: 0644]
ioncore/llist.h [new file with mode: 0644]
ioncore/manage.c [new file with mode: 0644]
ioncore/manage.h [new file with mode: 0644]
ioncore/modules.c [new file with mode: 0644]
ioncore/modules.h [new file with mode: 0644]
ioncore/mplex.c [new file with mode: 0644]
ioncore/mplex.h [new file with mode: 0644]
ioncore/mplexpholder.c [new file with mode: 0644]
ioncore/mplexpholder.h [new file with mode: 0644]
ioncore/mwmhints.c [new file with mode: 0644]
ioncore/mwmhints.h [new file with mode: 0644]
ioncore/names.c [new file with mode: 0644]
ioncore/names.h [new file with mode: 0644]
ioncore/navi.c [new file with mode: 0644]
ioncore/navi.h [new file with mode: 0644]
ioncore/netwm.c [new file with mode: 0644]
ioncore/netwm.h [new file with mode: 0644]
ioncore/pholder.c [new file with mode: 0644]
ioncore/pholder.h [new file with mode: 0644]
ioncore/pointer.c [new file with mode: 0644]
ioncore/pointer.h [new file with mode: 0644]
ioncore/presize.c [new file with mode: 0644]
ioncore/presize.h [new file with mode: 0644]
ioncore/property.c [new file with mode: 0644]
ioncore/property.h [new file with mode: 0644]
ioncore/rectangle.c [new file with mode: 0644]
ioncore/rectangle.h [new file with mode: 0644]
ioncore/regbind.c [new file with mode: 0644]
ioncore/regbind.h [new file with mode: 0644]
ioncore/reginfo.c [new file with mode: 0644]
ioncore/reginfo.h [new file with mode: 0644]
ioncore/region-iter.h [new file with mode: 0644]
ioncore/region.c [new file with mode: 0644]
ioncore/region.h [new file with mode: 0644]
ioncore/resize.c [new file with mode: 0644]
ioncore/resize.h [new file with mode: 0644]
ioncore/rootwin.c [new file with mode: 0644]
ioncore/rootwin.h [new file with mode: 0644]
ioncore/saveload.c [new file with mode: 0644]
ioncore/saveload.h [new file with mode: 0644]
ioncore/screen.c [new file with mode: 0644]
ioncore/screen.h [new file with mode: 0644]
ioncore/selection.c [new file with mode: 0644]
ioncore/selection.h [new file with mode: 0644]
ioncore/sizehint.c [new file with mode: 0644]
ioncore/sizehint.h [new file with mode: 0644]
ioncore/sizepolicy.c [new file with mode: 0644]
ioncore/sizepolicy.h [new file with mode: 0644]
ioncore/stacking.c [new file with mode: 0644]
ioncore/stacking.h [new file with mode: 0644]
ioncore/strings.c [new file with mode: 0644]
ioncore/strings.h [new file with mode: 0644]
ioncore/tags.c [new file with mode: 0644]
ioncore/tags.h [new file with mode: 0644]
ioncore/window.c [new file with mode: 0644]
ioncore/window.h [new file with mode: 0644]
ioncore/xic.c [new file with mode: 0644]
ioncore/xic.h [new file with mode: 0644]
ioncore/xwindow.c [new file with mode: 0644]
ioncore/xwindow.h [new file with mode: 0644]
libextl/LICENSE [new file with mode: 0644]
libextl/Makefile [new file with mode: 0644]
libextl/README [new file with mode: 0644]
libextl/build/system-inc.mk [new file with mode: 0644]
libextl/exact-version [new file with mode: 0644]
libextl/extl.h [new file with mode: 0644]
libextl/install-sh [new file with mode: 0644]
libextl/libextl-mkexports.in [new file with mode: 0644]
libextl/luaextl.c [new file with mode: 0644]
libextl/luaextl.h [new file with mode: 0644]
libextl/misc.c [new file with mode: 0644]
libextl/private.h [new file with mode: 0644]
libextl/readconfig.c [new file with mode: 0644]
libextl/readconfig.h [new file with mode: 0644]
libextl/types.h [new file with mode: 0644]
libmainloop/Makefile [new file with mode: 0644]
libmainloop/defer.c [new file with mode: 0644]
libmainloop/defer.h [new file with mode: 0644]
libmainloop/exec.c [new file with mode: 0644]
libmainloop/exec.h [new file with mode: 0644]
libmainloop/hooks.c [new file with mode: 0644]
libmainloop/hooks.h [new file with mode: 0644]
libmainloop/rx.mk [new file with mode: 0644]
libmainloop/select.c [new file with mode: 0644]
libmainloop/select.h [new file with mode: 0644]
libmainloop/signal.c [new file with mode: 0644]
libmainloop/signal.h [new file with mode: 0644]
libtu/LICENSE [new file with mode: 0644]
libtu/Makefile [new file with mode: 0644]
libtu/README [new file with mode: 0644]
libtu/README.rb [new file with mode: 0644]
libtu/build/system-inc.mk [new file with mode: 0644]
libtu/debug.h [new file with mode: 0644]
libtu/dlist.h [new file with mode: 0644]
libtu/errorlog.c [new file with mode: 0644]
libtu/errorlog.h [new file with mode: 0644]
libtu/exact-version [new file with mode: 0644]
libtu/install-sh [new file with mode: 0644]
libtu/iterable.c [new file with mode: 0644]
libtu/iterable.h [new file with mode: 0644]
libtu/locale.h [new file with mode: 0644]
libtu/map.c [new file with mode: 0644]
libtu/map.h [new file with mode: 0644]
libtu/minmax.h [new file with mode: 0644]
libtu/misc.c [new file with mode: 0644]
libtu/misc.h [new file with mode: 0644]
libtu/np-conv.h [new file with mode: 0644]
libtu/np/np-conv.h [new file with mode: 0644]
libtu/np/numparser2.h [new file with mode: 0644]
libtu/numparser2.h [new file with mode: 0644]
libtu/obj.c [new file with mode: 0644]
libtu/obj.h [new file with mode: 0644]
libtu/objlist.c [new file with mode: 0644]
libtu/objlist.h [new file with mode: 0644]
libtu/objp.h [new file with mode: 0644]
libtu/optparser.c [new file with mode: 0644]
libtu/optparser.h [new file with mode: 0644]
libtu/output.c [new file with mode: 0644]
libtu/output.h [new file with mode: 0644]
libtu/parser.c [new file with mode: 0644]
libtu/parser.h [new file with mode: 0644]
libtu/pointer.h [new file with mode: 0644]
libtu/private.h [new file with mode: 0644]
libtu/ptrlist.c [new file with mode: 0644]
libtu/ptrlist.h [new file with mode: 0644]
libtu/rb-test.c [new file with mode: 0644]
libtu/rb.c [new file with mode: 0644]
libtu/rb.h [new file with mode: 0644]
libtu/setparam.c [new file with mode: 0644]
libtu/setparam.h [new file with mode: 0644]
libtu/snprintf_2.2/INSTALL [new file with mode: 0644]
libtu/snprintf_2.2/LICENSE.txt [new file with mode: 0644]
libtu/snprintf_2.2/Makefile.unused [new file with mode: 0644]
libtu/snprintf_2.2/README [new file with mode: 0644]
libtu/snprintf_2.2/README.html [new file with mode: 0644]
libtu/snprintf_2.2/snprintf-orig.c [new file with mode: 0644]
libtu/snprintf_2.2/snprintf.c [new file with mode: 0644]
libtu/snprintf_2.2/snprintf.h [new file with mode: 0644]
libtu/snprintf_2.2/test.c [new file with mode: 0644]
libtu/stringstore.c [new file with mode: 0644]
libtu/stringstore.h [new file with mode: 0644]
libtu/tester.c [new file with mode: 0644]
libtu/tester2.c [new file with mode: 0644]
libtu/tester3.c [new file with mode: 0644]
libtu/tokenizer.c [new file with mode: 0644]
libtu/tokenizer.h [new file with mode: 0644]
libtu/types.h [new file with mode: 0644]
libtu/util.c [new file with mode: 0644]
libtu/util.h [new file with mode: 0644]
man/Makefile [new file with mode: 0644]
man/ion3.cs.in [new file with mode: 0644]
man/ion3.de.in [new file with mode: 0644]
man/ion3.fi.in [new file with mode: 0644]
man/ion3.in [new file with mode: 0644]
man/pwm3.cs.in [new file with mode: 0644]
man/pwm3.de.in [new file with mode: 0644]
man/pwm3.fi.in [new file with mode: 0644]
man/pwm3.in [new file with mode: 0644]
man/welcome.cs.head [new file with mode: 0644]
man/welcome.de.head [new file with mode: 0644]
man/welcome.fi.head [new file with mode: 0644]
man/welcome.head [new file with mode: 0644]
mod_dock/LICENSE [new file with mode: 0644]
mod_dock/Makefile [new file with mode: 0644]
mod_dock/README.dock [new file with mode: 0644]
mod_dock/dock.c [new file with mode: 0644]
mod_menu/Makefile [new file with mode: 0644]
mod_menu/grabmenu.c [new file with mode: 0644]
mod_menu/main.c [new file with mode: 0644]
mod_menu/main.h [new file with mode: 0644]
mod_menu/menu.c [new file with mode: 0644]
mod_menu/menu.h [new file with mode: 0644]
mod_menu/mkmenu.c [new file with mode: 0644]
mod_menu/mkmenu.h [new file with mode: 0644]
mod_menu/mod_menu.lua [new file with mode: 0644]
mod_mgmtmode/Makefile [new file with mode: 0644]
mod_mgmtmode/main.c [new file with mode: 0644]
mod_mgmtmode/main.h [new file with mode: 0644]
mod_mgmtmode/mgmtmode.c [new file with mode: 0644]
mod_mgmtmode/mgmtmode.h [new file with mode: 0644]
mod_panews/Makefile [new file with mode: 0644]
mod_panews/main.c [new file with mode: 0644]
mod_panews/main.h [new file with mode: 0644]
mod_panews/mod_panews.lua [new file with mode: 0644]
mod_panews/panews.c [new file with mode: 0644]
mod_panews/panews.h [new file with mode: 0644]
mod_panews/placement.c [new file with mode: 0644]
mod_panews/placement.h [new file with mode: 0644]
mod_panews/splitext.c [new file with mode: 0644]
mod_panews/splitext.h [new file with mode: 0644]
mod_panews/unusedwin.c [new file with mode: 0644]
mod_panews/unusedwin.h [new file with mode: 0644]
mod_query/Makefile [new file with mode: 0644]
mod_query/complete.c [new file with mode: 0644]
mod_query/complete.h [new file with mode: 0644]
mod_query/edln.c [new file with mode: 0644]
mod_query/edln.h [new file with mode: 0644]
mod_query/fwarn.c [new file with mode: 0644]
mod_query/fwarn.h [new file with mode: 0644]
mod_query/history.c [new file with mode: 0644]
mod_query/history.h [new file with mode: 0644]
mod_query/input.c [new file with mode: 0644]
mod_query/input.h [new file with mode: 0644]
mod_query/inputp.h [new file with mode: 0644]
mod_query/listing.c [new file with mode: 0644]
mod_query/listing.h [new file with mode: 0644]
mod_query/main.c [new file with mode: 0644]
mod_query/main.h [new file with mode: 0644]
mod_query/mod_query.lua [new file with mode: 0644]
mod_query/mod_query_chdir.lua [new file with mode: 0644]
mod_query/query.c [new file with mode: 0644]
mod_query/query.h [new file with mode: 0644]
mod_query/wedln-wrappers.c [new file with mode: 0644]
mod_query/wedln.c [new file with mode: 0644]
mod_query/wedln.h [new file with mode: 0644]
mod_query/wmessage.c [new file with mode: 0644]
mod_query/wmessage.h [new file with mode: 0644]
mod_sm/Makefile [new file with mode: 0644]
mod_sm/sm.c [new file with mode: 0644]
mod_sm/sm_matchwin.c [new file with mode: 0644]
mod_sm/sm_matchwin.h [new file with mode: 0644]
mod_sm/sm_session.c [new file with mode: 0644]
mod_sm/sm_session.h [new file with mode: 0644]
mod_sp/Makefile [new file with mode: 0644]
mod_sp/cfg_sp.lua [new file with mode: 0644]
mod_sp/main.c [new file with mode: 0644]
mod_sp/main.h [new file with mode: 0644]
mod_statusbar/Makefile [new file with mode: 0644]
mod_statusbar/draw.c [new file with mode: 0644]
mod_statusbar/draw.h [new file with mode: 0644]
mod_statusbar/ion-statusd/Makefile [new file with mode: 0644]
mod_statusbar/ion-statusd/exec.c [new file with mode: 0644]
mod_statusbar/ion-statusd/extlrx.c [new file with mode: 0644]
mod_statusbar/ion-statusd/ion-statusd.c [new file with mode: 0644]
mod_statusbar/ion-statusd/statusd_date.lua [new file with mode: 0644]
mod_statusbar/ion-statusd/statusd_load.lua [new file with mode: 0644]
mod_statusbar/ion-statusd/statusd_mail.lua [new file with mode: 0644]
mod_statusbar/main.c [new file with mode: 0644]
mod_statusbar/main.h [new file with mode: 0644]
mod_statusbar/mod_statusbar.lua [new file with mode: 0644]
mod_statusbar/statusbar.c [new file with mode: 0644]
mod_statusbar/statusbar.h [new file with mode: 0644]
mod_tiling/Makefile [new file with mode: 0644]
mod_tiling/main.c [new file with mode: 0644]
mod_tiling/main.h [new file with mode: 0644]
mod_tiling/ops.c [new file with mode: 0644]
mod_tiling/panehandle.c [new file with mode: 0644]
mod_tiling/panehandle.h [new file with mode: 0644]
mod_tiling/placement.c [new file with mode: 0644]
mod_tiling/placement.h [new file with mode: 0644]
mod_tiling/split-stdisp.c [new file with mode: 0644]
mod_tiling/split-stdisp.h [new file with mode: 0644]
mod_tiling/split.c [new file with mode: 0644]
mod_tiling/split.h [new file with mode: 0644]
mod_tiling/splitfloat.c [new file with mode: 0644]
mod_tiling/splitfloat.h [new file with mode: 0644]
mod_tiling/tiling.c [new file with mode: 0644]
mod_tiling/tiling.h [new file with mode: 0644]
modulelist.mk [new file with mode: 0644]
po/Makefile [new file with mode: 0644]
po/README [new file with mode: 0644]
po/cs.po [new file with mode: 0644]
po/de.po [new file with mode: 0644]
po/fi.po [new file with mode: 0644]
po/ru.po [new file with mode: 0644]
pwm/Makefile [new file with mode: 0644]
pwm/cfg_pwm.lua [new file with mode: 0644]
pwm/pwm.c [new file with mode: 0644]
system.mk [new file with mode: 0644]
utils/Makefile [new file with mode: 0644]
utils/ion-completefile/Makefile [new file with mode: 0644]
utils/ion-completefile/ion-completefile.c [new file with mode: 0644]
utils/ion-completeman.in [new file with mode: 0644]
utils/ion-runinxterm [new file with mode: 0644]
version.h [new file with mode: 0644]

diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..43a323e
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,9624 @@
+2006-12-23 14:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20061223
+
+2006-12-23 14:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Release notes
+
+2006-12-23 11:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Bound 'toggle tag' (Mod1+T) in WMPlex.toplevel instead of WFrame.toplevel
+  - Works for FS stuff too now
+
+2006-12-23 11:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added info window for tagging state of FS stuff
+
+2006-12-22 14:56 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * More mysterious focus tuning
+  (Seems like the focus shit working varies by the sunspots.)
+
+2006-12-21 19:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Returned to old totally-out-of-order Focus/EnterWindow event processing.
+  - It seems the sucky Xlib doesn't let you do it otherwise.
+
+2006-12-20 15:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Yet more focus tracking hacks.
+  (Will one ever work?)
+
+2006-12-14 16:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * The "float" winprop works on transients too now
+
+2006-12-14 16:33 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * If sizehint winprops have been set, correct requested geometry to match these.
+
+2006-12-09 21:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Place new regions after the current and those with activity after it
+  - Controlled with frame_default_index parameter of ioncore.set.
+    To get the old default behaviour, set this to 'next'.
+  - This option obsoletes the frame_add_last option
+
+2006-12-09 11:24 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added next/prev-always alternatives to cycle parameter to mod_query.complete.
+  - If these are used instead of next/prev, cycling occurs despite
+    history/normal completion mode switch.
+
+2006-12-02 18:43 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed query history duplicate elimination code
+
+2006-11-23 22:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Increased query history size from 256 to 1024
+
+2006-11-19 23:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Don't unmanage stdisp when switching to region that can't manage it
+
+2006-11-12 15:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changed region_rqgeom parametrisation
+  - Gravity can now be passed down to the root relative ("absolute")
+    version, allowing removal of WClientWin specific checks/hacks.
+
+2006-11-12 12:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Detach tries to keep root-relative geometry unchanged
+  (For framed detach, the geometry of the frame is tried
+  to be kept unchanged from the origianl geomery of the 
+  detached object.)
+
+2006-11-12 12:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Detach works on transients now
+
+2006-11-12 12:22 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Readjust cursor position for infobox
+
+2006-11-12 10:38 UTC  Miroslav Kure <kurem@debian.cz>
+  * Updated Czech translation
+
+2006-11-11 18:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * statusd startup timeout tunning
+  (incl. typo fix)
+
+2006-11-11 15:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated Finnish translation
+
+2006-11-11 15:33 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Menu name translation hack
+  (These are hidden in strings in the configuration files.)
+
+2006-11-11 13:52 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added info box for history completion mode into queries
+
+2006-11-11 12:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Warp in WMPlex.set_hidden
+
+2006-11-04 14:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Clear a few more flags of frame size hints
+
+2006-11-03 21:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed mod_query.query_attachclient to attach the containing WGroupCW.
+  Also, if one does not exist (e.g. when attaching transients), one is 
+  created.
+
+2006-11-03 21:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Made pholder_do_attach return region instead of boolean.
+
+2006-11-03 07:52 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed switchto/hidden WMPlex attach parameter interaction.
+
+2006-11-09 18:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Do not duplicate entries already in history when pushing.
+  Instead move the first already existing first.
+
+2006-11-09 18:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * strcoll instead of strcmp for completion sorting
+
+2006-11-09 17:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Complete history in history order instead of character set
+
+2006-11-01 20:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Remanage stdisp when bottom attached to group
+
+2006-10-31 16:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed coding style
+  (Remember the coding style, folks!)
+
+2006-10-31 12:03 UTC  David Smith <davidsmith@acm.org>
+  * Handle mods in submapgrab_handler
+
+2006-10-31 16:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Doc. fixes
+
+2006-10-30 21:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * tiling_placement_alt fixes
+
+2006-10-30 21:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Comments about moronic kernels and improved workaround
+
+2006-10-29 13:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Respect REGION_SKIP_FOCUS more often
+
+2006-10-29 13:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops
+
+2006-10-28 23:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20061029
+
+2006-10-28 23:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some release notes
+
+2006-10-28 23:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * mod_menu.grabmenu uses current key event.
+  - It is no longer necessary to specify the cycling key separately.
+
+2006-10-28 23:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Query activation key now cycles completions
+
+2006-10-28 22:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed defmenu for callbacks
+
+2006-10-28 22:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added routine to get current key event, if not in a submap.
+
+2006-10-28 19:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed sizepolicies used for fullsize-stdisp
+
+2006-10-28 19:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Context menu code now knows to use 'Foo.bar' for Foo in mode 'bar-baz'.
+
+2006-10-28 19:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed unused/broken WFrame-as-scratchpad bindmap
+
+2006-10-27 18:56 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Set SIZEPOLICY_FULL_EXACT for group bottom if unspecified.
+
+2006-10-27 18:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops, incomplete backward cycle...
+
+2006-10-27 18:25 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changed parametrisation of WEdln.complete
+
+2006-10-27 18:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added support for history completion
+  - Mod1+R in the default bindings completes in history.
+
+2006-10-22 18:11 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * create_frame parametrisation in mod_scratchpad was wrong.
+  (Weak typing, bah.)
+
+2006-10-21 22:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed rotation support.
+  (Application of size policies did not pass through the information.)
+
+2006-10-21 20:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed layout backwards compatibility hack.
+  - It wasn't setting SIZEPOLICY_FULL_EXACT for WTiling.
+
+2006-10-21 17:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed shading code after the mode stuff had broken it
+
+2006-10-21 17:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Resizing code fixes and other changes
+
+2006-10-20 23:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Allow frames attached to WTiling to have tiled-alt mode
+
+2006-10-20 17:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Workspace switch warp had become disabled.
+
+2006-10-20 15:43 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20061020
+
+2006-10-20 15:43 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated RELNOTES
+
+2006-10-20 15:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added README for po/
+
+2006-10-20 15:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Restored client window unmap fs_pholder return goto
+  (Which could do in theory do the wrong thing now under some
+  strange settings and scripts. TODO: better and more general
+  "return lists".)
+
+2006-10-20 15:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixes to previous focus fixes
+
+2006-10-19 21:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Separate update_$LANG targets in Makefile instead of update_translations
+
+2006-10-19 18:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed unnecessary test file
+
+2006-10-19 16:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated Finnish translation
+
+2006-10-19 16:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use lua-xgettext
+
+2006-10-18 18:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * mplex_current_node falls back to mx_current.
+  (Kludge to get around problems with deferred destroy and groups.)
+
+2006-10-18 17:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WMPlex focus code changes
+
+2006-10-17 21:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added boolean 'float' winprop.
+  - If it is set, groups don't pass prepare_manage to 'bottom'.
+
+2006-10-17 21:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Don't set REGION_PLEASE_WARP on groups
+
+2006-10-16 22:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * predist.sh fix/redundancy removal
+
+2006-10-16 22:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Don't install cfg_panews.lua, as the module is disabled.
+
+2006-10-16 19:55 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Makefile preload hack fixes
+
+2006-10-16 19:49 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * -typo
+
+2006-10-16 12:17 UTC  Miroslav Kure <kurem@debian.cz>
+  * Updated Czech manpage
+
+2006-10-16 12:16 UTC  Miroslav Kure <kurem@debian.cz>
+  * Updated Czech translation
+
+2006-10-02 11:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * README update
+
+2006-10-15 22:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed a typo in workspace query
+  (Stupid dynamic typing.)
+
+2006-10-15 18:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20061015
+
+2006-10-15 17:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Doc fix
+
+2006-10-15 17:52 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some final release note tuning
+
+2006-10-15 17:02 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use the faster direct url in predist.sh
+
+2006-10-15 17:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * predist.sh update
+
+2006-10-15 16:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Do not enforce floating style for transient frames..
+
+2006-10-15 15:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved layout backwards compatibility hack
+
+2006-10-15 14:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Doc fix
+
+2006-10-15 14:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WFrame@WTiling bindmap and menu was actually redundant now.
+
+2006-10-14 23:11 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added some release notes
+
+2006-10-14 23:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Actually, disable tab-bar by deafult for FRAME_MODE_TILED_ALT.
+
+2006-10-14 22:49 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use "@" instead of "-on-" for bindings and menus dependent on manager
+  (for more consistency with "." for mode-dependent stuff).
+
+2006-10-14 22:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Context menus also support modes now.
+  - The context menu "Class.mode" is included, if Class:mode exists and
+    returns "mode".
+
+2006-10-14 22:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improvements to mode stuff
+
+2006-10-14 22:28 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed superfluous frame drawing routine dynamism.
+
+2006-10-14 22:21 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed mkbottom
+
+2006-10-14 22:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Groups and tilings now enforce frame mode.
+
+2006-10-14 22:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added frame mode-switching code.
+  - Frames without a tab bar can be obtained with
+  
+          frame:set_mode('frame-tiled-alt') 
+  
+    in the default styles (or by entirely disabling the tabbar with
+    `bar = 'none'` in the 'frame-tiled' style).
+
+2006-10-14 21:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed WFloatFrame
+  - Frame modes are used instead.
+  - Tabbar-toggling is also gone, and must be reproduced with 
+    modes (TODO).
+
+2006-10-14 17:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Replace frame style parameter with mode parameter.
+
+2006-10-10 22:26 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added guards for _rawsub and renamed it _chld.
+
+2006-10-07 21:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed stdisp unmanage
+
+2006-10-07 16:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added detach entries into menus
+
+2006-10-07 16:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added 'append' option for menus.
+
+2006-10-07 14:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added support for WFoo-on-WBar context menus.
+
+2006-10-07 14:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Manual page binding listing generation improvements etc.
+
+2006-10-06 15:50 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added context menu for screens.
+  - Entries to create workspace with default template or an empty one, and
+    close current workspace (if permitted).
+  - Mod1+M bound at WMPlex level now, to display this menu on empty
+    workspaces.
+
+2006-10-05 18:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated (English and Finnish) man pages.
+
+2006-10-05 17:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Floatframe shading fixes.
+
+2006-10-04 18:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Float placement code ignore group bottom now.
+
+2006-10-04 17:56 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed and cleaned up bitrot in region binding registration code.
+
+2006-10-03 16:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed mod_query.show_clientwin by expanding it into mod_query.show_tree.
+
+2006-10-03 15:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Simplified group attach stuff with separate WFramedPHolder
+
+2006-09-30 20:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * mod_menu was still referring to WMPlex.llist
+
+2006-09-30 20:43 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Group attach size fix (quick&dirty version).
+
+2006-09-30 12:23 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Reparent/weave were in wrong order in group_fitrep.
+
+2006-09-29 19:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Put floating client windows as well in groups (for now).
+  - Also use frames with special style for floating transients as well.
+
+2006-09-29 19:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WGroupedPHolder and other fixes.
+
+2006-09-29 17:48 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Hacks to ignore size hints when in client-requested full screen mode.
+
+2006-09-28 18:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed uninitialised structures.
+  (Stupid C...)
+
+2006-09-28 09:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Restored NotifyPointer focus event ignorance.
+
+2006-09-27 09:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changed email in convirus script to nobody@nowhere.invalid
+  (Stupid piece of shit telling users to report its own idiocy to 
+  that address.)
+
+2006-09-27 09:52 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use NGROUPS_MAX if NGROUPS is not defined.
+
+2006-09-24 16:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed useless rescue code.
+
+2006-09-24 16:14 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops.
+
+2006-09-24 15:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use WGroupedPHolder for grouped attach of client windows to frames.
+
+2006-09-24 15:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed mplex placeholder rearrangements.
+
+2006-09-23 15:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed completed #warning TODO
+
+2006-09-23 15:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Lowering a stacked-above object (transient) lowers the o'parent' object as well now.
+
+2006-09-22 18:11 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed group prepare_manage policy.
+
+2006-09-19 17:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in EnterWindow event handling.
+
+2006-09-19 07:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added zero size check to Xinerama sanity check.
+
+2006-09-17 16:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Transient etc. size fixes
+
+2006-09-16 19:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Sizehint code tuning for requirements of WGroupCW.
+
+2006-09-16 19:25 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops, statusbar attach stuff hadn't been brought up-to-date.
+
+2006-09-16 17:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed redundant size hint correction wrapper code.
+
+2006-09-16 15:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WGroupWS.attach_framed supports arbitrary regions (and not just WClientWins).
+
+2006-09-16 15:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use just "frame-floating" style instead of "frame-floating-groupws".
+
+2006-09-16 15:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WFloatFrame wasn't using region_displayname yet.
+
+2006-09-16 14:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Cleaned up SPLIT_NONE/ANY
+
+2006-09-16 11:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Marked some entries as 'done' on the TODO list.
+
+2006-09-15 12:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed superfluous ion-completeman code
+
+2006-09-15 16:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Focus fixes
+
+2006-09-10 19:28 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed focusing when focus gets restored to root window and we want to focus something else.
+
+2006-09-10 12:44 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Transient initial positioning fixed
+
+2006-09-03 13:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixes to activity propagation code
+
+2006-09-03 12:26 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops, forgot to update bindings earlier.
+
+2006-09-03 11:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops.
+
+2006-09-03 10:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Replaced region_activity_hook with generic region_notify_hook, which
+  is also used to notify of name, tag and other changes, with a string
+  parameter indicating the actual change that has taken place.
+
+2006-09-03 08:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * The activity notification window is now managed normally.
+
+2006-08-31 19:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added basic detach/mkbottom code.
+  TODO: placeholders for reattach, etc. Maybe put into separate 
+  module (instead of mod_tiling)?
+
+2006-08-31 19:33 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Various minor fixes and clean-up.
+
+2006-08-25 02:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved commented-out transpose_words as Control+K T.
+
+2006-08-24 20:52 UTC  Tibor Csögör <tibi@tiborius.net>
+  * Added transpose_chars and transpose_words to mod_query.
+
+2006-08-31 17:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some attach mechanism improvements.
+
+2006-08-21 17:02 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Navigation code fixes
+
+2006-08-21 16:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WMPlex attach code always uses WMPlexPHolders.
+  - The attach callbacks should then be able to safely remove stuff
+    from the mplex, for detach stuff etc.
+
+2006-08-19 17:25 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed initial window order in frame.
+
+2006-08-19 17:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improvements related to navigation and stacking
+  - Added WRegion.rqorder as an abstract raise/lower request.
+
+2006-08-19 16:48 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed unused stacking code
+
+2006-08-18 17:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * More navigation stuff.
+  - In particular, rebound configuration files to use ioncore.goto_next 
+    with appropriate parametrisation.
+
+2006-08-18 17:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some improvements in binding handler compilation code
+
+2006-08-17 17:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added no_ascend/no_descend parameters to the navi functions.
+
+2006-08-17 16:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some fixes binding graph hacks for windowless regions.
+
+2006-08-16 18:55 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Generic navigation code improvements.
+  - Also removed some redundant tiling code.
+
+2006-08-11 16:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed an assert trigger
+
+2006-08-11 16:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some drawing engine code tuning
+
+2006-08-14 15:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added a missing check for nil table.
+
+2006-08-14 18:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed some undefined references in mod_dock.
+
+2006-08-09 17:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use redblack tree for reg->stacking lookup.
+
+2006-08-07 16:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some documentation updates.
+
+2006-08-07 16:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in WMPlex structure.
+  - l1/l2 lists are gone, and instead there just a single list of all
+    managed regions using WStacking nodes, and another list of the
+    “layer 1” or “mutually exclusive” regions.
+
+2006-08-05 18:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed mplex_lcount stuff.
+
+2006-08-02 14:56 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Possible fixes to (initial) effect of stdisp on tiling llayout.
+
+2006-08-03 21:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated to predist.sh to reflect changed paths of *.mk.
+
+2006-08-03 21:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in predist.sh to reflect _darcs/ having been changed.
+
+2006-08-03 15:56 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Simplifications in mplex/group focus code.
+
+2006-08-02 20:02 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Combined WStacking and WLListNode.
+
+2006-08-01 22:11 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some field renames for further changes.
+
+2006-07-16 15:33 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WGroupCW uses the bottom_last_close option now (instead of reinventing it).
+
+2006-07-16 15:28 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added option to auto-destroy groups when 'bottom' is destroyed
+  when nothing "essential" is left after it.
+
+2006-07-16 15:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved handling of stdisp managed by destroyed 'bottom' of a group.
+
+2006-07-15 19:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * PWM config updates.
+
+2006-07-15 18:48 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added default_ws_params to ioncore.set.
+  - cfg_tiling.lua sets a horizontally 1:1 splitted tiled layout as default
+
+2006-07-15 18:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Renamed mod_ionws as mod_tiling and WIonWS as WTiling.
+
+2006-07-14 12:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * mod_floatws is no more.
+  - WFloatWS was moved into ioncore as the WGroupWS base for all workspaces.
+  - Backwards compatibility hack included for saved layouts, but not
+    configuration files.
+
+2006-07-14 11:21 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved some region_register calls to safer places.
+
+2006-07-14 11:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changed remaining lua-side references to WGenWS to WGroupWS.
+
+2006-07-10 20:25 UTC  pfeifer@wait.de
+  * pass-table-fix
+  This patch fixes a bug in mod_panes that would prevent a caller for successfully passing
+  a table as template to the module
+
+2006-07-14 10:26 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed redundant code by using weave for raise/lower.
+
+2006-07-12 12:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed WGenWS entirely.
+
+2006-07-08 10:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * mod_dock was still referring to mplex_layer.
+
+2006-07-12 11:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Temporarily disabled mod_panews.
+
+2006-07-12 05:00 UTC  Etan Reisner <deryni@gmail.com>
+  * Remove an extra space in the Lua query prompt.
+
+2006-07-08 08:48 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixes in initial stacking of regions.
+
+2006-07-08 07:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * floatws redirects prepare_manage to 'bottom' if active.
+
+2006-07-07 18:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added beginnings of more generic navigation code.
+
+2006-07-07 17:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed 'bottom' initial stacking.
+
+2006-07-07 14:42 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Redirect stdisp to WGroup(WS) 'bottom'.
+
+2006-07-05 15:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Ugly Makefile hacks.
+
+2006-07-02 17:24 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some improvements in mplex focusing code and policies.
+
+2006-07-02 15:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some cleanup.
+
+2006-07-02 15:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WGroupCW no longer loads if empty.
+
+2006-07-01 19:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some mplex/group integration.
+
+2006-07-01 18:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Further improvements in focus code.
+
+2006-07-01 16:28 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * region_managed_goto improvements.
+
+2006-06-27 22:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops.
+
+2006-06-27 21:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some more changes, fixes, and damage done in key binding dispatch code.
+
+2006-06-25 20:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixes and simplifications to key handling code.
+
+2006-06-25 17:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Minor fixes to group stuff.
+
+2006-06-25 16:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Reduced use of WGenWS.
+  (Only WIonWS and WPaneWS still refer to it.)
+
+2006-06-25 09:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WGroupCW passes on region_managed_notify for the 'bottom'.
+
+2006-06-25 09:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Stupid C and dependencies..
+
+2006-06-24 18:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some more release notes.
+
+2006-06-24 18:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Groups use minimum size hint from 'bottom'.
+
+2006-06-24 17:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Check that WGenWS actually implements genws_manage_stdisp before calling it.
+
+2006-06-24 17:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Full-screening code updates.
+
+2006-06-24 17:40 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fix in group_fitrep.
+
+2006-06-24 17:33 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Ignore stuff in groups for primitive stacking code.
+
+2006-06-24 17:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed stacking_weave.
+
+2006-06-24 16:24 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added binding set for WClientWin.
+
+2006-06-24 16:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * New mechanism for grabs of windowless regions' bindings.
+
+2006-06-23 09:26 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added stuff to RELNOTES.
+
+2006-06-23 09:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added region_displayname stuff.
+  - Needed to display the main client window's name in the tab-bar for 
+    client window groups (WGroupCW).
+
+2006-06-23 09:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WGroupCW gets destroyed when empty.
+
+2006-06-23 08:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Temporarily removed sticky floating frame support.
+  - To be re-implemented in a completely different manner.
+
+2006-06-23 08:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added group modality stuff.
+
+2006-06-23 07:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some cleanup.
+
+2006-06-22 23:38 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * First steps of client window groups.
+
+2006-06-21 16:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * There were unused fields in WClientWin.
+
+2006-06-21 16:42 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some dates were wrong..
+
+2006-06-21 16:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed group attach geometry stuff.
+
+2006-06-21 08:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Typo fixes.
+
+2006-06-20 19:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Inherited WFloatWS from WGroup.
+
+2006-06-20 18:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved part of WFloatWS as WGroup in ioncore.
+
+2006-06-20 18:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed unused local variable.
+
+2006-06-20 18:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved some more stuff.
+
+2006-06-20 18:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved framed attach stuff from floatws.c to placement.c.
+
+2006-06-20 17:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved floatws pholder.
+
+2006-06-19 16:21 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added SIZEPOLICY_SHRUNK modifier.
+
+2006-06-19 16:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added generic floatws attach routines.
+
+2006-06-19 12:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Allow override of xterm with the XTERM Lua-side variable.
+
+2006-06-18 01:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved and extended floatws sizepolicy usage.
+
+2006-06-17 22:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * stacking_unweave/weave
+
+2006-06-17 22:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops.
+
+2006-06-17 18:18 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * FloatWS special-case stuff points to the stacking structures instead of the regions.
+
+2006-06-17 17:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added manager specific list to WStacking.
+
+2006-06-17 10:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Cleaned up the top directory of building related files.
+  - In particular, autovirus stuff is in build/ac now and must be run there.
+
+2006-06-15 02:51 UTC  Etan Reisner <deryni@gmail.com>
+  * Sun Xinerama support, also fix the --help text to accurately indicate whether we were built with xinerama support or not.
+
+2006-06-13 15:08 UTC  Norbert Tretkowski <norbert@tretkowski.de>
+  * Use META instead MOD1.
+
+2006-06-07 11:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Missing assignment fixed.
+
+2006-06-07 09:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Export ioncore.tags_first.
+
+2006-06-11 17:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improvements to floatws bottom support + use size policies.
+
+2006-06-11 17:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added SIZEPOLICY_UNCONSTRAINED.
+
+2006-06-10 21:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Preliminary floatws "bottom" support.
+
+2006-06-09 14:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Exclude USECS_IN_SEC itself from valid values too..
+
+2006-06-09 14:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Ensure that tv_usec is less than 1000000 (usecs in sec).
+  - Some kernels are a bit picky.
+
+2006-05-29 21:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Size hints weren't being applied on client windows on Ion startup startup.
+  (Now they're applied if we do not modify the old client window size.)
+
+2006-05-29 16:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added levels to stacking code.
+
+2006-05-28 20:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed some redundancy from stacking code.
+
+2006-05-19 21:36 UTC  Etan Reisner <deryni@gmail.com>
+  * s/kpress_waitrel/kpress_wait/ and some s/waitrel/wait/ since I believe waitrel is the old name and this makes things a little cleaner.
+  The switch_bindings.lua script currently has an ugly if/elseif section to do
+  all the binding removal stuff, with this change that whole section can be
+  replaced with one line.
+
+2006-05-28 17:52 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some more stacking code changes.
+
+2006-05-28 11:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some more stacking code cleanup etc.
+
+2006-05-27 15:25 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some improvements/cleanup in floatws stacking code.
+
+2006-05-24 16:22 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20060524
+
+2006-05-23 06:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * An assert had moved to the wrong place.
+
+2006-05-20 09:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * One more gsub update...
+
+2006-05-19 16:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * More Lua 5.1 stuff: use # instead of table.getn.
+
+2006-05-19 07:12 UTC  René van Bevern <rvb@debian.org>
+  * take ioncore_bindings.lua of ion3 to lua 5.1
+
+2006-05-19 06:48 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Few more backticks.
+
+2006-05-18 23:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20060519
+
+2006-05-18 23:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added new release notes.
+
+2006-05-18 23:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated README.autoconf.
+
+2006-05-18 23:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added a few more backticks to RELNOTES and README.
+
+2006-05-19 02:19 UTC  Matthieu.Moy@imag.fr
+  * Use -lm and -ldl in the test for -llua
+
+2006-05-18 13:21 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Markdownized README as well.
+
+2006-05-18 13:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Small changes in RELNOTES for markdown processing for web.
+
+2006-05-17 15:26 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated TODO.riot.
+
+2006-05-17 16:51 UTC  Matthieu.Moy@imag.fr
+  * bugfix in lua 5.1 + other usefull checks
+  The test for the liblua version was completely buggy. Sorry.
+  
+  Tests added for other headers:
+  X11/SM/SMlib.h, X11/Xresource.h and libintl.h
+  (with error messages pointing to the name of Debian packages)
+  
+  Test for xmessage (with a warning only, since ion can probably still
+  work without xmessage)
+  
+  
+
+2006-05-17 13:39 UTC  Matthieu.Moy@imag.fr
+  * Update configure.ac for lua 5.1
+
+2006-05-16 18:39 UTC  Sergej Pupykin <ps@lx-ltd.ru>
+  * sigusr2
+  
+  Allows to create lua hook for SIGUSR2.
+  Can be usefull (for example) for status line changing from media player...
+  
+  Usage example:
+   ioncore.get_hook("ioncore_sigusr2_hook"):add(
+       function(reg)
+           mod_statusbar.inform("status_name", "SIGUSR2 catched")
+           mod_statusbar.update()
+       end
+   )
+
+2006-05-16 17:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * README etc. updates.
+
+2006-05-16 17:24 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed string.gsub issues with Lua 5.1.
+  
+  Lua 5.0:
+  "... If the value returned by this function is a string, then it is
+  used as the replacement string; otherwise, the replacement string is the
+  empty string."
+  
+  Lua 5.1:
+  "If the value returned by the table query or by the function call is a string
+  or a number, then it is used as the replacement string; otherwise, if it is
+  false or nil, then there is no replacement (that is, the original match is
+  kept in the string)."
+  
+
+2006-05-16 16:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Few more missing table-iteration changes.
+
+2006-03-24 00:19 UTC  Etan Reisner <deryni@gmail.com>
+  * lua5.1 incompatible changes
+  The _LOADED variable became package.loaded.
+  The contents of lualib were merged into lua itself, so remove -llualib from the build line.
+  Debian now uses pkg-config for the lua stuff not lua-config.
+
+2006-03-24 00:09 UTC  Etan Reisner <deryni@gmail.com>
+  * lua5.1 backwards compatible changes
+  Whether intentional or not lua 5.1 no longer allows
+  tab={}
+  for k,v in tab do
+      ...
+  end
+  and requires
+  for k,v in pairs(tab) do
+      ...
+  end
+  instead.
+
+2006-05-06 21:18 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Do not spew out stack traces all the time when warn() is called.
+
+2006-04-26 17:49 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * framed_transients is on by default now.
+
+2006-04-22 16:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added toggle for showing tab numbers.
+  - WFrame:set_numbers with the usual 'set'/'unset'/'toggle' parametrisation.
+
+2006-04-20 19:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed obsolete references to -i18n flag.
+
+2006-04-26 17:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added WFrame.toplevel and WMPlex.toplevel binding maps.
+  - These are not used by frames for transients.
+  - Changed the default bindings accordingly.
+
+2006-04-13 22:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * The frame_add_last option had been broken.
+  - Since switching to use of placeholders for initial management 
+    setup, the flag was no longer checked.
+
+2006-04-02 19:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added stuff for accessing regions with activity/urgency bit set.
+
+2006-04-02 19:33 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Extended Mod1+K K binding.
+  - Go to first region demanding attention or previously active region:
+    "ioncore.activity_goto() or ioncore.goto_previous()".
+
+2006-04-01 14:38 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Separated and cleaned up some stacking code from mod_floatws.
+
+2006-03-31 18:14 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in default_ws_type lookup.
+
+2006-03-28 20:18 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use SIZEPOLICY_FULL_BOUNDS for client windows in mplexes.
+  - Needed to communicate available area for transients.
+
+2006-03-26 09:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20060326
+
+2006-03-23 16:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Included a TODO list. 
+  
+  The TODO.riot file has been created with the riot outliner available from
+  <http://iki.fi/tuomov/riot/>. The file is, however, just an mbox file, so
+  you can read it with your favourite threading mail user agent. For example:
+  mutt -f TODO.riot
+
+2006-03-20 23:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Enforce win_gravity=NorthWestGravity for client windows.
+  - Fixes issues with OO.org's transients in framed mode.
+
+2006-03-18 22:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in default appmenu.
+  - Removed firefox (won't promote gtk shit).
+  - Added opera, konqueror, dillo, w3m, links, rxvt.
+
+2006-03-18 12:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Ah, -Wl,-whole-archive is a better hack.
+
+2006-03-18 12:48 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Link PWM also with the -u ptrlist_iter gcc lameness hack.
+
+2006-03-17 20:49 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some more winprop/sizepolicy changes.
+
+2006-03-17 19:42 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20060317
+
+2006-03-17 10:22 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Previous change incomplete.
+
+2006-03-17 10:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * default_ws_type is no longer set by config files and could confuse things. Fixed.
+
+2006-03-15 23:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in look file fonts.
+  - Refer directly to -*-fixed-medium-r-normal-*-13-*-*-*-*-*-*-* as
+    the 'fixed' alias has problems with encodings.
+
+2006-03-15 18:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Timer signal object passing to Lua side was broken.
+  (Weak typing...) 
+
+2006-03-15 18:23 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in the set of configuration files.
+  - cfg_ion.lua now simply load cfg_ioncore.lua and cfg_modules.lua
+    with the default set of modules, and includes some of the most
+    commonly changed options commented-out. This deprecates cfg_user.lua
+  - cfg_menus.lua and cfg_bindings.lua are now cfg_ioncore.lua. This 
+    better matches how things for different modules are in their own
+    files.
+
+2006-03-12 12:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Embedded dock initilisation code had been broken. Fixed.
+
+2006-03-11 09:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. The stretch size policy used wrong variables.
+
+2006-03-11 08:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Different query-menus use different history context.
+
+2006-03-08 20:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. The new client window code used wrong "llist" iterator.
+  This could cause segfaults.
+
+2006-03-08 19:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Closing a transient had started warping to remaining transient(s).
+
+2006-03-07 10:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed binding lookup code to be able to deal with zero keycodes.
+  - This may happen if X keycode to keysym mapping is changed 
+    while Ion is running.
+
+2006-03-06 23:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. If there were winprops set, SIZEPOLICY_DEFAULT was used for transients.
+
+2006-03-05 13:44 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20060305
+
+2006-03-05 13:42 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * 'ru' was missing from list of translations in po/Makefile.
+
+2006-03-05 13:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * frame_rqgeom_clientwin passes rqflags as-is.
+
+2006-03-05 13:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * SIZE_POLICY_FREE_GLUE is now properly used for transients.
+
+2006-03-05 13:28 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added -u ptrlist_iter linking hack to gcc/ld.
+  - Otherwise ptrlist.o from libtu won't get linked as it will no longer
+    used by the main binary, and thus the modules won't have access to it.
+
+2006-03-05 10:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved free_glue and stretch size policies.
+
+2006-03-02 18:49 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added SIZEPOLICY_FREE_GLUE and other size policy code changes.
+  (TODO: client windows need to store state for this for transients.)
+
+2006-02-26 16:25 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Client windows use the same (mplex) size policies for transients now.
+
+2006-02-26 00:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. MPlex attach code changes had broken mgd. region ordering on load.
+
+2006-02-25 17:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added some size policies with gravity.
+
+2006-02-24 19:23 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Separated mplex size policy in new file, and independent of mplex.
+
+2006-02-22 13:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Append '/' to submenu entries in query_menu.
+
+2006-02-19 16:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some mplex attach code cleanup.
+
+2006-02-19 16:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Reduced usage of REGION_FIT_BOUNDS a little.
+
+2006-02-19 00:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated dock to reflect previous mplex size policy changes.
+
+2006-02-18 20:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed the WScratchpad class.
+  - Scratchpads are now simply normal WFrames with MPLEX_SIZEPOLICY_FREE.
+
+2006-02-18 20:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added WMPlex managed region size policy support.
+
+2006-02-18 18:53 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed "./" being included in completed file name in query_exec.
+  - Also brought mod_query.popen_completions documentation up-to-date.
+
+2006-02-15 07:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed load_hint.
+  - Stupid unsafe dynamically-typed languages...
+
+2006-02-12 16:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Let's call it statusbar_ instead of status_ after all...
+
+2006-02-12 15:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Should check for sb meter value being null when shortening it.
+
+2006-02-12 15:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * mod_statusbar tries to load scripts before falling back to statusd.
+  - If status_foobar(.lua|.lc) is found on the search path, -m foobar
+    is not passed to ion-statusd.
+
+2006-02-12 15:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added region_activity_hook.
+  - Called when the activity flag of a region is changed with the 
+    region as parameter.
+
+2006-02-12 15:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Framed transients' size requests get handled now.
+
+2006-02-11 15:35 UTC  dev@triphoenix.de
+  * completefile-groupexec
+  
+  Although ion-completefile does check if any matching executable is
+  world-executable or user-executable, it doesn't check for
+  group-executable. This is problematic for example on systems, where
+  games are installed with r-xr-x--- and root:games (which seems to be a
+  common method). This introduces a check for the group id, basically it
+  gets all groups for the current user and checks if any of them is
+  applicable. This is only done when the user and world checks already
+  have failed and the group-executable bit is set.
+  
+
+2006-02-08 07:42 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * ion-statusd catches SIGCHLD.
+
+2006-02-07 21:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved comments in cfg_statusbar.lua.
+
+2006-02-06 20:56 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changed query_menu title transformation.
+  * Lua's regexp routines don't understand utf-8 or other multibyte encodings.
+  * TODO: conversion to lower case of non-ascii letters.
+
+2006-02-01 18:05 UTC  Vassily Leushin <basileus@kirov.lug.ru>
+  * russian_locale
+
+2006-01-29 15:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Statusbar meter template is respected as maximum size for meter.
+
+2006-01-25 23:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added dummy gettext hack for those labels.
+
+2006-01-25 23:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added context menu label support.
+
+2006-01-25 16:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Do not try to resize the statusbar unnecessarily.
+
+2006-01-21 23:47 UTC  ludal@logilab.fr
+  * fixes random segfaults
+  With ion-3ds-20060107 I see random segfaults I can reproduce navigating
+  some website with galeon. This patch seems to fix the problem (and the
+  original code looked wrong)
+
+2006-01-21 21:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. A function wasn't marked local.
+
+2006-01-21 20:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Counter wasn't incremented in statusbar list building.
+
+2006-01-20 21:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated/fixed PWM bindings configuration.
+
+2006-01-20 17:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Statusbar uses PtrLists instead of ObjLists.
+  (Stuff gets removed from ObjLists before the manager's removal handler 
+  is called.)
+
+2006-01-19 19:24 UTC  Etan Reisner <deryni@eden.rutgers.edu>
+  * Use the table values we are iterating over.
+
+2006-01-14 20:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Previous completion behaviour change broke something..
+
+2006-01-11 17:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed a problem in statusbar winprop usage.
+
+2006-01-07 21:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20060107
+
+2006-01-07 21:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added systray window height limiting.
+
+2006-01-07 19:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added support for multiple systray items in statusbar.
+  Use %systray_whatever in the template, and set the 'statusbar' winprop
+  to "systray_whatever" to put any window at that point the template.
+
+2006-01-01 01:26 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Year changed.
+
+2006-01-01 01:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed mod_menu.grabmenu documentation.
+
+2005-12-31 23:44 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Display transients of systray icons somewhere else.
+
+2005-12-29 22:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * The statusbar supports system tray windows now.
+  These are windows that have the _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR
+  property set.
+
+2005-12-26 19:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved some statusbar code to the C side and removed old (backcompat) kludges.
+
+2005-12-25 17:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added noautoexpand option for mod_query.query_menu.
+  This option should be used to suppress automatic expansion
+  of huge menus e.g. as follows:
+  
+      submenu("Debian",           "Debian", {noautoexpand=true}),
+  
+
+2005-12-23 18:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some improvements and fixes in style files.
+
+2005-12-21 22:14 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed status display mapping on destroyal of ws and switch fs cwin.
+
+2005-12-19 18:44 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * frame_brushes_updated wasn't in the WFrame dynfuntab.
+
+2005-12-18 16:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Redefine 'print' in query_lua execution environment,
+  and display the printed strings, if there are any, after execution 
+  in a message box.
+
+2005-12-18 16:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved fullscreen winprop handling to default clientwin_do_manage_alt handler.
+  Other handlers may now override it, but return-from-fs spaceholder is now
+  obtained. Also, setting the property to false will override initial
+  fullscreen request from the application.
+
+2005-12-18 14:43 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed mod_query.query_menu failing if a submenu could not be found.
+
+2005-12-17 23:43 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Don't warp to newly opened transient, only focus it.
+
+2005-12-17 14:14 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in transient geometry change request handling.
+
+2005-12-16 23:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Forced new completion run (Control+Tab) in auto-show-completions mode doesn't cycle to first alternative.
+
+2005-12-16 23:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Meter names etc. may be enclosed in braces in statusbar template.
+
+2005-12-14 16:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Documented mod_query.query low-level query routine.
+
+2005-12-13 16:53 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated documentation comment.
+
+2005-12-12 21:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed space deletion in query_exec completion.
+
+2005-12-10 20:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20051210
+
+2005-12-10 20:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed kludges from client window resize code.
+
+2005-12-10 20:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in transient window management setup code.
+
+2005-12-08 15:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Floatws config file was out-of-date.
+
+2005-12-05 23:18 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added a metatable kludge for MOD1/MOD2 vs. META/ALTMETA compatibility.
+  - All mixes of old and new configuration files should work correctly.
+
+2005-12-05 23:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Renamed MOD1/MOD2 to META/ALTMETA.
+  - Too many newbies are confused between the MOD1/MOD2 variables and the
+    Mod1/Mod2 X modifiers.
+
+2005-12-03 17:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * mod_query.query_attachclient (Mod1+A) now activates client already in target mplex.
+
+2005-12-03 17:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Empty dock uses (w, h)=tile_size instead of (1, 1).
+
+2005-12-02 07:50 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * mod_statusbar.create should pass fullsize option to WMPlex.set_stdisp.
+
+2005-11-13 22:24 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Experimental: keybindings open query-menus instead of normal menus.
+
+2005-11-26 11:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Failing to open error log file could cause extra trouble..
+
+2005-11-26 11:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Sigh. The default FD_CLOEXEC setting of false is brain-damaged.
+
+2005-11-25 19:54 UTC  Miroslav Kure <kurem@debian.cz>
+  * Updated Czech translation
+
+2005-11-15 17:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Another temporary clientwin resize hack.
+
+2005-11-20 13:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved previous active region tracking.
+  - Replaced explicit and cumbersome previous active object saving
+    at particular points of the code with a simple activity history
+    list/stack.
+
+2005-11-20 12:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Hmm.. active_screen was still in ioncore_g.
+
+2005-11-15 07:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed a typo; wairel -> waitrel.
+
+2005-11-13 22:38 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Forgot to set FRAME_SZH_USEMINMAX on floatframes now.
+
+2005-11-13 22:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Small improvement in date monitor.
+
+2005-11-13 22:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in floatframe size hint adjustment.
+
+2005-11-13 21:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in mod_query.query_menu name conversion.
+
+2005-11-13 21:55 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed mod_query.query_menu submenu support.
+
+2005-11-10 19:44 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed active screen tracking.
+  - Focused region tracking does the job.
+
+2005-11-10 19:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added ioncore.current().
+  - Can be used to find the currently focused region.
+
+2005-11-06 15:51 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed stray debug print statement.
+
+2005-11-03 19:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Command line completor understands pipes now.
+
+2005-11-02 19:40 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed empty frame minimum size hint.
+  - Stupid C ! vs. & operator precedence.
+
+2005-11-01 17:59 UTC  Tom Payne <ion@tompayne.org>
+  * Include <locale.h> in mod_statusbar/ion-statusd/ion-statusd.c for call to setlocale.
+  Gentoo bug https://bugs.gentoo.org/show_bug.cgi?id=110860
+
+2005-11-01 21:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed PWM from restart menu.
+  - Because the default menu file is shared by PWM and Ion.
+
+2005-11-01 21:53 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed floatframe size hints' base height.
+  - For resize indicator to display the current client window's size
+    instead of something else.
+
+2005-10-31 20:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * ion-statusd init code checks CF_NO_LOCALE.
+
+2005-10-29 12:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20051029
+
+2005-10-25 18:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed (unframed) nested transient size issue.
+
+2005-10-13 20:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed -sessionname to -session in manual pages.
+
+2005-10-24 20:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. %filler update had removed stretching space constant part.
+
+2005-10-24 15:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. mod_statusbar was also lagging behind the mplex_get_stdisp parametrisation change.
+
+2005-10-23 16:14 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20051023
+
+2005-10-23 16:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Experimental: added option to turn framed transients on/off.
+  - Use ioncore.set{framed_transients=true} to turn them on (off by default).
+
+2005-10-23 16:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Experimental: framed transients.
+
+2005-10-23 15:55 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed dummy size hint setup.
+
+2005-10-18 16:22 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. mod_dock hadn't been updated to reflect changes in mplex_set/get_stdisp.
+
+2005-10-15 18:11 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in frame autodestroy code.
+
+2005-10-07 21:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed 180 degree rotation.
+
+2005-10-07 18:23 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in ionws rotation support code.
+
+2005-10-05 22:23 UTC  Sadrul H Chowdhury <imadil@gmail.com>
+  * attach_transient crash fix
+
+2005-10-05 03:55 UTC  Sadrul H Chowdhury <imadil@gmail.com>
+  * introduce ioncore.tagged_list() to get a list of tagged regions
+
+2005-09-20 18:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixes related to removal of region from an mplex.
+
+2005-09-18 16:50 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Exported WClientWin.attach_transient.
+
+2005-09-18 16:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved cfg files from module directories to etc/.
+
+2005-09-18 16:43 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved some common look settings to lookcommon_*.lua from look_*.lua.
+
+2005-09-18 15:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Documentation comment fixes.
+
+2005-09-13 18:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some de cleanup.
+
+2005-09-08 18:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some clean-up.
+
+2005-09-06 16:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added fullsize option for space-wasting status displays.
+
+2005-09-06 15:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed -c from msgfmt arguments.
+
+2005-09-05 20:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed mail monitor from default statusbar template.
+
+2005-08-29 05:43 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed floatws_backcirculate (copy-paste bug..)
+
+2005-08-31 10:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed unused parameter of ioncore.x_get_atom_name.
+
+2005-08-27 23:01 UTC  Per Olofsson <pelle@dsv.su.se>
+  * Some updates to README.dock.
+
+2005-08-27 22:56 UTC  Per Olofsson <pelle@dsv.su.se>
+  * README.dock updates from Debian (removing references to ion-devel etc.)
+
+2005-08-28 12:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * (Probably) fixed move/resize indicator positioning on xinerama screens not at (0, 0).
+
+2005-08-27 16:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Replaced stray \a0's with spaces.
+
+2005-08-27 16:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added %filler statusbar element.
+
+2005-08-26 17:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added oneshot winprop option.
+
+2005-08-22 11:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added missing #include.
+
+2005-08-21 08:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed splitting an ancestor of the stdisp.
+
+2005-08-21 07:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * mod_query.query_menu improvements.
+
+2005-08-20 14:44 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Pane handle style name change wasn't supposed to be recorded.
+  - frame-tiled-pane breaks handle drawing completely.
+
+2005-08-20 11:33 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20050820
+
+2005-08-20 11:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. ionws_current_nostdisp was never added after all...
+
+2005-08-19 12:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. ioncore_efbb was still referenced as ioncore-efbb.
+
+2005-08-09 08:24 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed statusbar filler drawing.
+
+2005-08-09 08:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * statusd_load interval parameter should be update_interval.
+
+2005-08-03 08:50 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed indentation in cfg_statusbar.lua.
+
+2005-08-15 16:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Keep track of non-fs placement of initially fullscreen windows.
+  - region_manage_clientwin was replaced with region_prepare_manage that
+    instead of starting to manage the client window should return a placeholder
+    for it. If the window is not put in fs mode, this placeholder is then used 
+    to do the placement. Otherwise the placeholder is stored as the fs return 
+    placeholder.
+
+2005-08-14 18:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added a basic placeholder for transients and the dock.
+
+2005-08-11 21:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved transpose stdisp handling.
+
+2005-08-11 18:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improvements for better Xrandr support.
+
+2005-08-11 16:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * README updates.
+
+2005-08-11 15:38 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Initialise new splits' current field point to the splitted split.
+
+2005-08-11 15:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed stdisp focusing on switch to fresh ionws's.
+  - Initialise stdisp's parent split's 'current' field point to the
+    other child.
+
+2005-08-08 21:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed a few compiler warnings...
+
+2005-08-08 21:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved menu database stuff from mod_menu to ioncore.
+  - Configuration files for other modules can now safely define menus without
+    checking for or loading mod_menu, and 
+  - mod_query.query_menu doesn't depend on mod_menu having been loaded.
+
+2005-08-07 12:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added WDock.attach.
+
+2005-08-07 11:49 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Windows can now be added last in frames.
+  - Enable with ioncore.set{frame_add_last=true}.
+
+2005-08-07 11:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Encoding check only warns if no encoding is given at all in LC_CTYPE.
+  - If wrong encoding is given (vs. nl_langinfo), localisation is still refused.
+  - UTF-8 check is also case-insensitive now.
+
+2005-07-28 18:53 UTC  Norbert Tretkowski <norbert@tretkowski.de>
+  * Use WCOREDUMP() only if it's available
+
+2005-07-28 17:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20050728
+
+2005-07-28 17:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Small fix in set_text_property.
+
+2005-07-22 09:21 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed transient EnterWindow focus.
+
+2005-07-22 08:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changed autoshowcompl mode tab behaviour.
+  - If hitting tab triggers a new list of completions, the first 
+    entry is selected, as would happen when hitting tab after a 
+    delayed modification-triggered completion already finished.
+
+2005-07-21 16:18 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added "Failed to load fallback font" error message.
+
+2005-07-21 16:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed segfault if no font could be loaded.
+
+2005-07-20 21:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed screen_managed_changed_hook calling when fs clientwin is closed.
+
+2005-07-20 22:51 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changed next/prev screen bindings.
+  - They are now consistently with next/prev workspace bindings
+     MOD1+Shift+comma/period.
+
+2005-07-20 21:06 UTC  fshahriar@gmail.com
+  * ion-completeman -- Compatibility issue with FreeBSD's sed
+  
+  Originally, after tab complete wasn't working I tried to do
+  ion-completeman -mkusercache but it was generating an empty file.
+  Tracked down the problem to the sed regex. FreeBSD doesn't like the
+  "\+". Example:
+  $ echo /usr/share/man/man1/ls.1.gz|sed 's:^.*/\([^/]\+\.[0-9].*\)$:\1:p; d'
+  $
+  (No output)
+  
+  Not sure if it will break if used with GNU sed.
+  
+
+2005-07-20 20:02 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved(?) size selection of docked non-dockapps and enabled d&d to dock.
+
+2005-07-17 19:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improvements to statusd launch error logging code.
+
+2005-07-17 19:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Control could reach end of non-void function.
+
+2005-07-15 20:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * ion-statusd startup errors are reported in ion startup errorlog now.
+
+2005-07-15 20:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some changes in libmainloop popen routines.
+
+2005-07-12 16:40 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * statusd_load should work on fbsd now.
+
+2005-07-10 19:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed some variables to be local (on the Lua side).
+
+2005-07-10 09:40 UTC  kurem@debian.cz
+  * Updated Czech translation of Ion3
+
+2005-07-09 20:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved started program stderr catching hack.
+   - If the program terminates with an error code under 10s, 
+     stderr is displayed.
+   - If the program terminates under 2s, stderr is displayed despite
+     errorcode or signal.
+
+2005-07-09 19:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in menu opening function parametrisation.
+   - mod_menu.bigmenu should no longer be used, but remains for
+     compatibility at least for a while. Instead, set big=true
+     in the extra parameter to mod_menu.menu.
+
+2005-07-04 20:14 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added optional directory checking for 'cd'.
+   * If the Lua POSIX library can be loaded, parameters to 'cd' in the
+     run query are checked to be directories. (cd is interpreted as a
+     special case to change workspace working dir.)
+
+2005-07-03 20:42 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Scratchpad toggle now creates a new one on screens if none is found.
+
+2005-07-03 20:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Updated statusd.popen_bgread.
+
+2005-07-02 20:23 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved statusd_mail error message.
+
+2005-07-02 20:11 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added stderr handler parameter to ioncore.popen_bgread.
+
+2005-06-29 09:11 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixes to goto-when-scratchpad-is-active patch.
+
+2005-06-25 15:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20050625
+
+2005-06-25 15:11 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updates to Finnish translation.
+
+2005-06-24 19:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * The scratchpad can now be hidden automatically when going to another region.
+
+2005-06-24 10:33 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed mplex layer1 initial stacking while there were regions visible on layer2.
+
+2005-06-22 10:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Don't set template in default cfg_statusbar.lua so that translated version gets used from mod_statusbar.lua.
+
+2005-06-22 10:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Set up locale in ion-statusd.
+
+2005-06-20 18:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Man-page completion script optimisation for full list.
+
+2005-06-20 17:53 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Invalidate old completion list when timed completion is set up.
+
+2005-06-18 09:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved encoding check error message.
+
+2005-06-16 09:37 UTC  Stephan Wendt <stephanwendt@freenet.de>
+  * Replacement of indenting tabs by spaces in the ssh-hostnickname-completion-patch
+
+2005-06-14 05:35 UTC  Stephan Wendt <stephanwendt@freenet.de>
+  * Hostnickname-completion for ssh
+  
+  Adds the feature to get hostnicknames, defined in ~/.ssh/config, presented
+  as possible completions for the ion-ssh-functionality.
+  
+
+2005-06-15 10:01 UTC  Norbert Tretkowski <tretkowski@inittab.de>
+  * Fixes for german po file from Jens Seidel.
+
+2005-06-14 19:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Initially show first instead of last completions of first column of them.
+
+2005-06-14 19:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Scroll completion list as selected entry is changed.
+
+2005-06-14 18:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved manual completion in auto-show-completions mode.
+
+2005-06-10 16:49 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Better tracking of multiple pending completions.
+  Completor routines now get a WComplProxy object as first parameter and
+  should use WComplProxy.set_completions instead of WEdln.set_completions
+  to set list of completions. Much of old code should be compatible since
+  completors were called in protected mode and only WEdln.set_completions
+  was callable.
+
+2005-06-09 22:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Size hints are now ignored in client-requested full screen mode.
+
+2005-06-09 22:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in fullscreen focus policy.
+  - Inactive screens may switch to showing a client window when it requests
+    full screen mode even the window isn't focused (which is required of
+    client windows on the active screen).
+
+2005-06-07 13:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20050607
+
+2005-06-07 13:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added some release notes.
+
+2005-06-06 20:56 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * ioncore_goto_previous related fix.
+  - clientwin_managed_goto was calling ioncore_protect_previous instead
+    of ioncore_set_previous_of.
+
+2005-06-05 19:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed _NET_SUPPORTING_WM_CHECK and _NET_SUPPORTED property setup.
+
+2005-06-05 19:33 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Call region_notify_change in ioncore_clear_tags.
+
+2005-05-31 16:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Do not automatically run autoconf in predist.sh.
+  (Maybe one or two more people will RTF README now.)
+
+2005-05-27 13:38 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Ignore _NET_ACTIVE_WINDOW request by default.
+  - Can be enabled by setting the ignore_net_active_window winprop to false.
+
+2005-05-27 13:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Exec-in-xterm (::) and command line completion had been broken by auto-show changes.
+
+2005-05-27 11:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Experimental auto-show-completions support.
+
+2005-05-27 11:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Minor improvement to binding compilation code.
+
+2005-05-19 14:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added object parameter support to C-side timers.
+
+2005-05-19 13:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated mod_statusbar.set_sb to not use set_date.
+
+2005-05-19 10:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Encoding sanity check ignores dashes and case.
+
+2005-05-18 13:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved date monitor to ion-statusd.
+
+2005-05-15 19:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed floatws iteration code.
+
+2005-05-14 11:46 UTC  imadil@gmail.com
+  * mod_statusbar.get/set_sb()
+
+2005-05-13 07:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Miscellaneous fixes to recent changes.
+
+2005-05-12 22:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added zeropad to WStatusBar.get_template.
+
+2005-05-12 19:29 UTC  imadil@gmail.com
+  * WStatusBar:get_template()
+
+2005-05-12 18:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed message box wrapping.
+
+2005-05-12 17:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Trap SIGCHLD earlier in startup.
+
+2005-05-12 17:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed dummy implementation of mbrlen...
+
+2005-05-12 17:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some focusing improvements.
+
+2005-05-12 15:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added load_1min, load_5min and load_15min meters to the ion-statusd load script.
+
+2005-05-12 15:02 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Mblen did something else...
+  - Added str_len to ioncore/strings.c
+
+2005-05-12 14:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * The statusbar support zero-padding of meters now.
+  The syntax is %[alignment][0count]<meter name>.
+  For example: %02mail_total
+
+2005-05-12 14:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Organised WIonWS context menu possibly better.
+
+2005-05-11 21:40 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Select events earlier in client window init code.
+
+2005-05-10 18:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added WIonWS.split_at and transpose_at for easier binding.
+
+2005-05-10 17:52 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Save state on SIGTERM if not running under a session manager.
+
+2005-05-10 16:21 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Load mod_sp by default.
+
+2005-05-09 19:53 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Don't shell-escape arguments to man-page viewer.
+
+2005-05-06 20:48 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  UNDO: The CF_DE_USE_XUTF8 option is now on by default. 
+  This will cause Xutf8 routines to be used instead Xmb routines for UTF-8.
+  As of XFree86 4.3.0.1, the Xmb routines only use the iso10646-1 font in
+  a fontset and thus can't well display international text, while the Xutf8
+  routines inconsitently use the iso10646 font only as a fallback. The 
+  setting was differently previously because the Xutf8 routines were having
+  other problems that seem to be gone now...
+
+2005-05-08 16:14 UTC  Jeremy Hankins <nowan@nowan.org>
+  * Don't break old user configs in statusd_mail.lua
+
+2005-05-08 14:12 UTC  Jeremy Hankins <nowan@nowan.org>
+  * Fixed the logic triggering the retry interval in statusd_mail.lua
+
+2005-05-07 21:30 UTC  nowan@nowan.org
+  * Oops, fix statusd_mail.lua
+
+2005-05-07 21:24 UTC  nowan@nowan.org
+  * Added support for multiple mailboxes to statusd_mail.lua
+
+2005-05-08 15:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved ioncore_set_previous_of calls to region_managed_goto.
+
+2005-05-07 15:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Possibly better submenu placement in in-frame mode.
+
+2005-05-06 20:48 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * The CF_DE_USE_XUTF8 option is now on by default. 
+  This will cause Xutf8 routines to be used instead Xmb routines for UTF-8.
+  As of XFree86 4.3.0.1, the Xmb routines only use the iso10646-1 font in
+  a fontset and thus can't well display international text, while the Xutf8
+  routines inconsitently use the iso10646 font only as a fallback. The 
+  setting was differently previously because the Xutf8 routines were having
+  other problems that seem to be gone now...
+
+2005-05-06 20:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Faster fontset kludge for UTF-8 locales.
+  - Try to substitute only with -misc-fixed-... This works in some common
+    cases just like a more comprehensive but slower pattern would, but some
+    users will have to provide extra fonts to the fontset manually.
+
+2005-05-04 20:26 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Dock initial height (temporary) height was uninitialised.
+
+2005-05-02 14:55 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20050502
+
+2005-05-01 20:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some changes in stock style files.
+
+2005-05-01 18:23 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added some node-on-ws checks.
+
+2005-05-01 08:53 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved/fixed/updated WRegion.rqclose* documentation.
+
+2005-05-01 08:49 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WFloatWS client window rescue code ignores the status display.
+
+2005-04-30 14:49 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Bound left/right arrows in menus.
+
+2005-04-29 16:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Execution and file viewing queries catch stderr.
+
+2005-04-29 16:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added stderr piping support to spawning routines.
+
+2005-04-29 13:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Maybe fixed activity notification.
+
+2005-04-29 10:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in ionws context menu.
+  - Replaced floating split menu with floating toggle menu.
+
+2005-04-29 10:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added routines for toggling floating of splits.
+  - WIonWS.set_floating for splits directly, and
+  - WIonWS.set_floating_at for frames. Takes a direction parameter.
+
+2005-04-23 12:55 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added popen_bgread and exec to ion-statusd.
+
+2005-04-23 12:52 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved exec code to libmainloop.
+
+2005-04-22 18:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Oops. Oops.
+
+2005-04-22 18:23 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. mainloop_defer was no longer being exported due to prefix re-export change.
+
+2005-04-21 22:38 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * More maintainable re-exporting of libmainloop routines to Lua side.
+
+2005-04-21 14:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Ion-statusd no longer quits if no meters are loaded with -q.
+
+2005-04-20 13:51 UTC  imadil@gmail.com
+  * cwin namelist
+
+2005-04-10 23:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops.
+
+2005-04-10 23:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added clientwin_property_change_hook.
+  
+  - On the Lua side, the parameters to hook handlers are (WClientWin, atomid).
+  
+
+2005-04-06 11:02 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20050406
+
+2005-04-06 10:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use maximum seen value width for statusbar field width.
+
+2005-04-05 12:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Frame maximize fixes and improvements.
+
+2005-04-03 12:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes (crash fix?) in name allocation code.
+
+2005-04-02 15:38 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Menu scrolling improvements.
+
+2005-04-02 14:33 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved menu positioning patch etc.
+
+2005-04-02 12:36 UTC  imadil@gmail.com
+  * repositioning pmenus that appear at the bottom of the screen
+
+2005-04-01 22:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed new transients being attempted to be stacked over themselves.
+
+2005-04-01 17:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed table.join to check for 'entry==nil' instead of 'not entry'.
+
+2005-04-01 17:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed a typo.
+
+2005-03-29 22:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Example in cfg_sp.lua was missing a comma.
+
+2005-03-29 14:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed remainig statusd_load debug message.
+
+2005-03-28 20:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. New regions within screens were added after current instead of at end.
+
+2005-03-27 18:40 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some statusbar and line editor flicker reduction (?).
+
+2005-03-27 13:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Drawing engine api improvements.
+
+2005-03-26 13:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Brush drawing routines are no longer passed the window.
+
+2005-03-26 12:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some WIonWS routines now accept "any" as direction.
+
+2005-03-26 09:24 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WFrame.set_tabbar parameters were inverted.
+
+2005-03-25 19:20 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Display again ?? when statusbar meter is unset.
+
+2005-03-25 16:48 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added WMPlex.l2_is/set_passive routines.
+
+2005-03-25 11:50 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Menu entry for tagging hadn't been updated.
+
+2005-03-22 14:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20050322
+
+2005-03-22 14:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Prepared release notes for a release.
+
+2005-03-21 10:26 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added some release notes.
+
+2005-03-21 08:52 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use input-menu-pmenu and tab-menuentry-pmenu styles for drop-down menus.
+
+2005-03-21 08:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Lessened string length recalculation in query listing code.
+
+2005-03-20 15:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added ::cmd syntax for running commands with ion-runinxterm -w.
+
+2005-03-20 14:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * ion-runinxterm script improvements.
+
+2005-03-20 13:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Statusbar substyle background colour gets used now.
+
+2005-03-20 13:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Also changed region activity exports.
+  
+  - WRegion.clear_activity and WRegion.notify_activity were replaced with
+    WRegion.set_activity with a set/unset/toggle parameter.
+
+2005-03-20 09:44 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added some release notes (on the toggle export changes).
+
+2005-03-19 22:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * More changes/fixes in layer2 focus policy.
+
+2005-03-19 21:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Many exported toggle functions were changed and renamed.
+    
+  The following functions now take a string parameter indicating
+  whether to toggle, set, or unset the property.
+  
+  - WClientwin.set_fullsreen (replaces WClientWin.toggle_fullscreen)
+  - WRegion.set_tagged (replaces WRegion.tag/untag/toggle_tag)
+  - WFrame.set_tabbar (replaces WFrame.toggle_tabbar)
+  - WFrame.set_shaded (replaces WFrame.toggle_shade)
+  - WFloatFrame.set_sticky (replaces WFloatFrame.toggle_sticky)
+  - WMPlex.l2_set_hidden (replaces WMPlex.l2_hide/show)
+  - mod_sp.set_shown(_on) (replaces mod_sp.toggle(_on))
+  - mod_dock.set_floating_shown_on (replaces mod_dock.toggle_floating_on)
+    
+  Many missing is_<property> exports corresponding to the above were also 
+  added.
+
+2005-03-19 20:40 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. CLIENTWIN_IS_FULLSCREEN macro didn't work anymore.
+
+2005-03-19 19:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. exports.c didn't have dependencies anymore.
+
+2005-03-19 19:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Use exports.h generated by libextl-mkexports.
+
+2005-03-19 18:26 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Client windows can now be un-fullscreened without prior frame.
+
+2005-03-19 00:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * ioncore.popen_bgread also returns pid.
+
+2005-03-18 18:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added ioncore_sigchld_hook.
+
+2005-03-18 18:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Exec routines return PID (or -1 on error).
+
+2005-03-18 18:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved hook code to libmainloop.
+
+2005-03-17 08:57 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Attempts to fix problems with gcc/ld export-dynamic option varying.
+  
+  - Configurable in system.mk as the EXPORT_DYNAMIC variable
+  
+  - Defaults to '-Xlinker --export-dynamic' now instead of '-export-dynamic'
+
+2005-03-16 22:56 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed coding style in contribution.
+  
+  * Contributors _please_ follow my coding style in the future!!
+
+2005-03-16 22:20 UTC  Edwin Steiner <edwin.steiner@gmx.net>
+  * winprop_gravity
+  add 'gravity' and 'transient_gravity' winprops
+
+2005-03-16 19:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Modules should be built before ioncore for PRELOAD_MODULES.
+
+2005-03-16 18:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some more layer list scanning optimisations.
+
+2005-03-16 18:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Split out some code from mplex.c to llist.c.
+
+2005-03-15 23:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Some WMPlex switch code optimisations/simplifications.
+
+2005-03-15 22:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WMPlex layer2 visiblity synchronisation fixes and focus policy changes.
+
+2005-03-15 21:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Ionws flip/transpose fixes.
+
+2005-03-15 14:25 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * mod_query.query_menu can now display context menus.
+
+2005-03-14 18:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Dummy regions are no longer given name/registered.
+  
+  - WPaneHandles, WInfoWins and such are not accessible to the scripter
+    (from ioncore.region_list).
+
+2005-03-14 16:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed goto_previous and tab switch (pointer) interaction.
+
+2005-03-13 21:22 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Alternative fix to bindmap precedence.
+  
+  - "owned" bindmaps are added last on list, while region's own bindmaps 
+    are listed first.
+
+2005-03-13 20:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  UNDO: Changed region bindmap linking order.
+  
+  - Frame bindmaps should now take precedence over workspace bindmaps
+    (that are grabbed on frames as workspaces don't have normal windows
+    associated to them).
+
+2005-03-13 21:18 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Panehandles (floating splits) are now reparented correctly.
+
+2005-03-13 20:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changed region bindmap linking order.
+  
+  - Frame bindmaps should now take precedence over workspace bindmaps
+    (that are grabbed on frames as workspaces don't have normal windows
+    associated to them).
+
+2005-03-13 20:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed #warnings (and fixed a few).
+
+2005-03-13 13:18 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed ioncore.root_windows export.
+  
+  - ioncore.region_list("WRootWin") does the task.
+
+2005-03-13 13:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * ion-runinxterm uses the whole command as title now.
+
+2005-03-13 13:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * PWM menu definition updates and fixes.
+
+2005-03-13 13:02 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed the ion-man script.
+  
+  - ioncore.exec_on now interprets the ':cmd' ion-runinxterm syntax.
+  
+  - mod_query.query_man accepts the man command to use as parameter,
+    defaulting to ':man'.
+  
+  - The default for query_ssh is also ':ssh' now.
+
+2005-03-11 14:44 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * clientwin_unmapped_hook parameter was wrong.
+  (Stupid semi-weakly typed languages...)
+
+2005-03-10 08:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed ion-ssh wrapper script.
+  
+  - mod_query.query_ssh defaults to running ssh with ion-runinxterm, but
+    accepts additional parameter specifying another program.
+
+2005-03-09 21:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added field alignment code to statusbar module.
+
+2005-03-09 12:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated README information on F5/F6 keys.
+
+2005-03-09 12:06 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes in mod_query.query_editfile/viewfile.
+  
+  - These functions take the script (and prompt) as optional parameter now,
+    defaulting to run-mailcap.
+  
+  - Removed the ion-edit and ion-view scripts.
+
+2005-03-09 11:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * More mplex region indexing fixes.
+  After the changes in layer list code recently and following attach index fix,
+  regions were now loaded in wrong order at startup.
+
+2005-03-02 12:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  UNDO: Removed WScreen.set_managed_offset.
+  It seems unncessary now that there's the statusbar, and it's just an ugly
+  kludge for dubious uses anyway.
+
+2005-03-08 14:32 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Scratchpad and dock default toggle bindings changed.
+  
+  - Mod1+space now toggles the scratchpad, and
+  
+  - Mod1+D toggles the dock.
+
+2005-03-08 14:10 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * The dock is floating by default now.
+  
+  - New users shouldn't at firs try be bewildered by the dock not being able
+    to coexist with the statusbar.
+  
+
+2005-03-08 08:25 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Exported classes are now marked with EXTL_EXPORT.
+
+2005-03-08 07:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Stdisp was being destroyed along with floatws.
+
+2005-03-07 17:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed open quote handling in cmdline completion improvements.
+
+2005-03-07 17:04 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Default attach index was wrong for WMPlex.attach*.
+  
+  - Should be after current instead of last.
+  
+
+2005-03-07 08:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed focusingn of non-passive mplex layer2 regions.
+
+2005-03-06 10:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Check that reparenting can be done before trying to use a placeholder.
+  
+  - Added pholder_(do_)target and pholder_(do_)check_reparent
+  
+
+2005-03-06 10:24 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * More helper routines from attach.c are globally available.
+
+2005-03-05 16:25 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed pholder_stale as useless given placeholder redirection.
+
+2005-03-05 12:42 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changed how query and menu cancel and finish routines destroy the region.
+
+2005-03-05 09:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Paths in mod_statusbar file headers were wrong.
+
+2005-03-04 08:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20050304-1
+
+2005-03-04 08:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * predist.sh improvements.
+
+2005-03-04 08:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Completion improvements had been broken by further improvements.
+
+2005-03-04 08:38 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20050304
+
+2005-03-04 08:38 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added some release notes.
+
+2005-03-04 08:24 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Floating split load routine now adds handle widths to given sizes by default.
+  
+  - Override by setting tls_brs_incl_handles to true.
+  
+
+2005-03-03 21:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Improved mod_query.query_exec completion.
+  
+  - Arguments are now parsed, completed and escaped.
+  
+
+2005-03-03 10:15 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed reference to svn from version.h.
+
+2005-03-02 11:55 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Call region_do_warp_alt also in protected mode.
+
+2005-03-02 11:48 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes to ionws_placement_alt hook to make it callable in protected mode.
+
+2005-03-01 23:53 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * New export: ioncore.defer.
+
+2005-03-01 22:59 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Many hook calls are now made in protected mode.
+
+2005-03-02 12:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed WScreen.set_managed_offset.
+  It seems unncessary now that there's the statusbar, and it's just an ugly
+  kludge for dubious uses anyway.
+
+2005-03-01 22:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WIonWS.current and WFloatWS.current were unnecessarily exported.
+  
+  - WRegion.current is already exported.
+
+2005-02-28 21:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Rescue code simplification/clean-up.
+
+2005-02-28 19:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated Finnish translation.
+
+2005-02-28 19:26 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes and unification in rqclose code.
+
+2005-02-28 17:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * The statusd_mail handles missing mailbox more gracefully.
+  - The retry_interval  parameter (default: 10min) controls for how long 
+    it waits for next update attempt on error condition.
+
+2005-02-28 06:50 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed a stupid omission in new client window rescue code.
+
+2005-02-28 06:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed floatws drag&drop segfault.
+  - There was a leftover floatws_add_managed call in floatws_handle_drop after
+    creation of floatws_create_frame.
+
+2005-02-28 06:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. Removed a leftover debug printf.
+
+2005-02-27 13:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3ds-20050227
+
+2005-02-27 13:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated release notes.
+
+2005-02-27 11:47 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Session management module now uses placeholders.
+  - Order of windows in mplexes is now remembered under SM.
+
+2005-02-27 11:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WMPlexPHolder reorganisation fixes.
+
+2005-02-27 08:38 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added routine to go to placeholder location.
+  - Active client window being closed in full screen mode now returns
+    to original position.
+
+2005-02-27 07:17 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Most source files don't need region-iter.h anymore.
+
+2005-02-27 07:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. WMPlex layer list code changes were incomplete.
+
+2005-02-26 23:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WMPlex layer list code clean-up.
+
+2005-02-26 21:11 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Changes to work with new libtu list routines.
+
+2005-02-26 19:01 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Placeholder code improvements.
+  - Client window rescue code now uses placeholders.
+  - Added client window rescue placeholders; returning from full screen mode
+    now works on floatws.
+
+2005-02-26 12:02 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added simple floatws pholder.
+
+2005-02-26 13:30 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added file mtime/ctime checking to ion-statusd.
+  - Mailboxes can be checked a bit more often now without significant load.
+  
+
+2005-02-26 09:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Client window fullscreen toggle uses placeholders now.
+  The code does not yet do anything if the target of the initial placeholder 
+  dies, and thus does not work on floatws's that also do not yet provide
+  placeholders.
+  
+
+2005-02-26 08:40 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added a routine for checking whether a placeholder is stale.
+
+2005-02-26 08:31 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added region_managed_get_pholder for acquiring a placeholder.
+
+2005-02-26 08:21 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added basic placeholder support.
+  - Basic placeholder class WPHolder.
+  - WMPlexPHolder for holding place in mplexes.
+
+2005-02-24 09:28 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Removed traces of old managed lists.
+  Regions no longer contain link pointers for those lists etc. The
+  region_rescue_clientwins stuff is currently disabled and needs to be
+  rewritten when placeholder stuff is implemented.
+
+2005-02-24 09:22 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WRootWin no longer uses a screen_list.
+
+2005-02-24 09:09 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WIonWS now uses symlist for managed list.
+
+2005-02-24 08:49 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WDock no longer uses managed_list.
+
+2005-02-24 08:45 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WFloatWS doesn't use a special managed_list.
+  The stacking list is enough.
+
+2005-02-24 08:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WClientWin uses symlist for transient_list.
+
+2005-02-24 08:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Oops. All mplex stuff was being now saved on layer 2.
+
+2005-02-23 19:40 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * WMPlex managed list changed to use proxy nodes.
+  This should help implementing placeholders.
+
+2005-02-23 06:29 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated predist.sh for darcs.
+
+2005-02-23 06:28 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Set install script to "sh install-sh"
+
+2005-02-23 06:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-3-svn2darcs
+
+2005-02-22 21:30 UTC  tuomov
+  * trunk: changeset 1974
+  Another autoconf hack.
+
+2005-02-20 14:42 UTC  tuomov
+  * trunk: changeset 1973
+  WMPlex layer 2 region geometry is now remembered.
+
+2005-02-20 14:38 UTC  tuomov
+  * trunk: changeset 1972
+  Added missing include.
+
+2005-02-20 12:09 UTC  tuomov
+  * trunk: changeset 1970
+  Autoconf script fixes (by matthieu moy).
+
+2005-02-20 09:59 UTC  tuomov
+  * trunk: changeset 1969
+  Fixed names of workspace-specific frame bindmaps.
+
+2005-02-20 08:43 UTC  tuomov
+  * trunk: changeset 1968
+  Updated cfg_ionws.lua.
+
+2005-02-19 08:09 UTC  tuomov
+  * trunk: changeset 1967
+  Fixed tab switch focus change when a non-passive layer2 region was
+  being displayed.
+
+2005-02-18 21:45 UTC  tuomov
+  * trunk: changeset 1966
+  Fixed a typo (?) that could cause floatws stacking code to lock up.
+
+2005-02-16 05:04 UTC  tuomov
+  * trunk: changeset 1965
+  Update WMPlex.l2_hide/show docs.
+
+2005-02-15 06:06 UTC  tuomov
+  * trunk: changeset 1963
+  Added null checks in mplex_l2_hide/show.
+
+2005-02-14 18:34 UTC  tuomov
+  * trunk: changeset 1962
+  removed redundant ionws stacking code, and fixed a possible problem.
+
+2005-02-14 18:08 UTC  tuomov
+  * trunk: changeset 1961
+  Added -lintl comment in system.mk
+
+2005-02-14 13:59 UTC  tuomov
+  * trunk: changeset 1960
+  Added null check; not a member function.
+
+2005-02-14 09:07 UTC  tuomov
+  * trunk: changeset 1959
+  kludgeconf sun f1x remap detection fix.
+
+2005-02-14 00:04 UTC  tuomov
+  * trunk: changeset 1958
+  Oops.
+
+2005-02-13 23:36 UTC  tuomov
+  * trunk: changeset 1957
+  Oops. pwm3.de.in wasn't svn add'ed.
+
+2005-02-13 22:59 UTC  tuomov
+  * trunk: changeset 1956
+  Added mod_sp.toggle.
+
+2005-02-13 21:32 UTC  tuomov
+  * trunk: changeset 1955
+  Added German translation by Schott Robert.
+
+2005-02-12 18:23 UTC  tuomov
+  * trunk: changeset 1951
+  Event mask is now stored for WWindows.
+
+2005-02-11 18:10 UTC  tuomov
+  * trunk: changeset 1950
+  ion-statusd now communicates width templates to mod_statusbar.
+
+2005-02-11 13:35 UTC  tuomov
+  * trunk: changeset 1949
+  - Parameters can now be passed ion-statusd.
+  
+  - Some cfg_statusbar usage changes.
+
+2005-02-06 10:13 UTC  tuomov
+  * trunk: changeset 1947
+  Added WRegion.is_activity.
+
+2005-02-03 21:44 UTC  tuomov
+  * trunk: changeset 1946
+  Some info window fixes.
+
+2005-02-01 12:01 UTC  tuomov
+  * trunk: changeset 1945
+  Fixed loading of small frames with hidden tab-bar.
+
+2005-01-31 17:45 UTC  tuomov
+  * trunk: changeset 1944
+  Only use resize increment and base size from current client window
+  size hints when deciding frame size hints.
+
+2005-01-31 11:32 UTC  tuomov
+  * trunk: changeset 1941
+  Added support for coloured statusbar elements.
+
+2005-01-31 09:17 UTC  tuomov
+  * trunk: changeset 1940
+  - Moved some infowin code to mod_statusbar.
+  
+  - mod_statusbar template processing/passing status to c side changes.
+
+2005-01-29 23:43 UTC  tuomov
+  * trunk: changeset 1939
+  - Fixed docked window resize handling.
+  
+  - WDock needs to implement region_rqgeom_clientwin, as otherwise the
+    WClientWin configure request code uses the previously available
+    space and makes no request.
+
+2005-01-27 19:00 UTC  tuomov
+  * trunk: changeset 1938
+  Fixed WMPlex.l2_hidden segfault when called with nil region.
+
+2005-01-24 21:59 UTC  tuomov
+  * trunk: changeset 1937
+  Fixed hook copy-paste bug.
+
+2005-01-24 20:58 UTC  tuomov
+  * trunk: changeset 1936
+  Added DL_LIBS to ion-statusd linking flags (although it doesn't itself
+  depend on it) thanks to broken lua installs.
+
+2005-01-16 11:52 UTC  tuomov
+  tagged ion-3ds-20050116
+
+2005-01-16 11:07 UTC  tuomov
+  * trunk: changeset 1929
+  Added documentation for X property access exports.
+
+2005-01-15 21:22 UTC  tuomov
+  * trunk: changeset 1927
+  Oops. Broken rename.
+
+2005-01-15 21:20 UTC  tuomov
+  * trunk: changeset 1925
+  tabularx header changes.
+
+2005-01-15 16:28 UTC  tuomov
+  * trunk: changeset 1921
+  Fixed table in doc. comment.
+
+2005-01-14 14:28 UTC  tuomov
+  * trunk: changeset 1919
+  Fixed doc comment.
+
+2005-01-13 15:02 UTC  tuomov
+  * trunk: changeset 1916
+  Fixed clientwin_unmapped_hook.
+
+2005-01-03 22:24 UTC  tuomov
+  * trunk: changeset 1911
+  Added experimental dock (drag&)drop support. It must be enabled by
+  defining CF_EXPERIMENTAL_DOCK_DROP.
+
+2005-01-02 13:59 UTC  tuomov
+  * trunk: changeset 1905
+  Year changed.
+
+2004-12-27 21:30 UTC  tuomov
+  * trunk: changeset 1903
+  Made complete_name available in mod_query.
+
+2004-12-27 21:30 UTC  tuomov
+  * trunk: changeset 1902
+  Fixed mod_statusbar Makefile to rm before ln.
+
+2005-02-23 00:41 UTC  tuomov
+  * trunk: changeset 1901
+  Made ext_statusbar into a partially C-side module containing a
+  WStatusBar class that can have a bindmap.
+
+2004-12-17 00:02 UTC  tuomov
+  * trunk: changeset 1898
+  oops.
+
+2004-12-16 23:56 UTC  tuomov
+  * trunk: changeset 1897
+  Created directory for ion-knewt branch.
+
+2004-12-11 20:06 UTC  tuomov
+  * trunk: changeset 1896
+  Added mod_mgmtmode management mode module.
+
+2004-12-07 15:42 UTC  tuomov
+  * trunk: changeset 1895
+  Fixed (?) tabdrag focus.
+
+2004-11-26 17:41 UTC  tuomov
+  * trunk: changeset 1894
+  Do not show activity/urgency notification for docked and other client
+  windows that can not be easily focused.
+
+2004-11-26 08:18 UTC  tuomov
+  * trunk: changeset 1893
+  PWM bindings setup script now simply loads Ion bindings and unbinds
+  stuff that would use mod_query.
+
+2004-11-25 08:40 UTC  tuomov
+  * trunk: changeset 1892
+  Control modifier now is required for N/P in menus to not interfere
+  with typeahead.
+
+2004-11-17 18:46 UTC  tuomov
+  * trunk: changeset 1891
+  Applied patch with some minor menu improvements.
+
+2004-11-17 18:45 UTC  tuomov
+  * trunk: changeset 1890
+  Typo fixes.
+
+2004-11-16 23:01 UTC  tuomov
+  * trunk: changeset 1888
+  Removed another dupe call.
+
+2004-11-16 12:51 UTC  tuomov
+  * trunk: changeset 1887
+  Menu definition code and documentation fixes.
+
+2004-11-15 21:32 UTC  tuomov
+  * trunk: changeset 1886
+  Removed duplicate mod_ionws_register_exports call.
+
+2004-11-13 12:40 UTC  tuomov
+  * trunk: changeset 1884
+  mod_query runfile and viewfile use workspace working directory.
+
+2004-11-13 00:52 UTC  tuomov
+  * trunk: changeset 1883
+  Oops.
+
+2004-11-12 20:34 UTC  tuomov
+  * trunk: changeset 1881
+  Oops.
+
+2004-11-12 20:12 UTC  tuomov
+  * trunk: changeset 1880
+  Changed - to _ in ioncore-*.(lua|lc).
+
+2004-11-12 20:12 UTC  tuomov
+  * trunk: changeset 1879
+  Fixed floatws reparent.
+
+2004-11-10 14:41 UTC  tuomov
+  * trunk: changeset 1878
+  Added support for workspace-specific working directories. These can be
+  changed and viewed with the internally overridden commands 'cd' and
+  'pwd' in the F3 execution query.
+
+2004-11-10 01:48 UTC  tuomov
+  * trunk: changeset 1877
+  Set REGION_SKIP_FOCUS on pane handles.
+
+2004-11-07 18:18 UTC  tuomov
+  * trunk: changeset 1876
+  More misc. fixes.
+
+2004-11-07 18:00 UTC  tuomov
+  * trunk: changeset 1875
+  Oops. Fixed a potential segfault.
+
+2004-11-05 17:35 UTC  tuomov
+  * trunk: changeset 1874
+  Winprop name matching now available as ioncore.match_winprop_name.
+
+2004-11-05 16:38 UTC  tuomov
+  * trunk: changeset 1873
+  %% works in statusbar template.
+
+2004-11-05 16:22 UTC  tuomov
+  * trunk: changeset 1872
+  ion-statusd parameters are now automatically deduced by ext_statusbar
+  from the template.
+
+2004-11-04 21:34 UTC  tuomov
+  * trunk: changeset 1870
+  Load cfg_user.lua at end of cfg_ion.lua.
+
+2004-11-04 21:30 UTC  tuomov
+  * trunk: changeset 1869
+  Documented ext_statusbar.update.
+
+2004-11-04 21:20 UTC  tuomov
+  * trunk: changeset 1868
+  Fixed winprop name matching.
+
+2004-11-04 14:59 UTC  tuomov
+  tagged ion-3ds-20041104
+
+2004-11-04 14:56 UTC  tuomov
+  * trunk: changeset 1866
+  OOps.
+
+2004-11-04 14:38 UTC  tuomov
+  * trunk: changeset 1865
+  Removed changelog files.
+
+2004-11-04 14:32 UTC  tuomov
+  * trunk: changeset 1864
+  Remove autom4te.cache in predist.sh
+
+2004-11-04 14:30 UTC  tuomov
+  * trunk: changeset 1863
+  Added some new release notes.
+
+2004-11-04 14:19 UTC  tuomov
+  * trunk: changeset 1862
+  Some more minor statusbar improvements.
+
+2004-11-04 12:05 UTC  tuomov
+  * trunk: changeset 1861
+  Some clean-up.
+
+2004-11-04 11:23 UTC  tuomov
+  * trunk: changeset 1860
+  Added type of menus that grab input and allow cycling through the menu
+  with a given key until all modifiers are released.
+
+2004-11-04 04:16 UTC  tuomov
+  * trunk: changeset 1859
+  Still problems with timers being gc'd in statusd; the local timer
+  references need to be used instead of the timer parameter to the
+  handler.
+
+2004-11-04 04:02 UTC  tuomov
+  * trunk: changeset 1858
+  Some more statusbar/statusd improvements.
+
+2004-11-03 22:10 UTC  tuomov
+  * trunk: changeset 1857
+  Status meters should retain a local reference to their timers or it
+  can be collected as garbage.
+
+2004-11-03 19:56 UTC  tuomov
+  * trunk: changeset 1856
+  Moved potentially blocking statusbar meters to ion-statusd.
+
+2005-02-23 00:39 UTC  tuomov
+  * trunk: changeset 1855
+  Moved some mainloop stuff to libmainloop.
+
+2004-10-30 14:58 UTC  tuomov
+  * trunk: changeset 1853
+  Some README updates.
+
+2004-10-30 14:50 UTC  tuomov
+  * trunk: changeset 1852
+  Added WIonWS.split for splitting at a node instead of just frame/root.
+
+2004-10-23 11:05 UTC  kurem
+  * trunk: changeset 1851
+  Update Czech translation
+
+2004-10-23 10:39 UTC  tuomov
+  * trunk: changeset 1850
+  Arbitrary winprop matching criteria can now be used by setting the
+  'match' field to a matching function with parameters: (winprop, cwin)
+  and a boolean return value.
+
+2004-10-23 00:39 UTC  tuomov
+  * trunk: changeset 1849
+  Removed _ION_KLUDGES property support as the Lua side now has direct
+  access to window properties.
+
+2004-10-23 00:33 UTC  tuomov
+  * trunk: changeset 1848
+  Added exports to access window properties from Lua side.
+
+2004-10-23 00:14 UTC  tuomov
+  * trunk: changeset 1847
+  Added table.map to lua table library extensions.
+
+2004-10-19 16:04 UTC  tuomov
+  * trunk: changeset 1846
+  Changed order of some libs linking commands due to gcc's brain-damaged
+  handling of .a files. (-llib only loads those parts of liblib.a
+  required by objects that have appeared before it on the command line.)
+
+2004-10-19 15:08 UTC  tuomov
+  * trunk: changeset 1845
+  Added raise delay to help with an occasional slight annoyance of
+  floating splits.
+
+2004-10-16 10:35 UTC  tuomov
+  * trunk: changeset 1841
+  predist.sh wasn't making modifications to system.mk
+
+2004-10-16 08:43 UTC  tuomov
+  * trunk: changeset 1840
+  Stdisp parallel movement size calculations were reversed and an old
+  size swap had not been removed.
+
+2004-10-15 16:50 UTC  tuomov
+  * trunk: changeset 1839
+  Updated cs.po.
+
+2004-10-15 08:45 UTC  tuomov
+  * trunk: changeset 1838
+  ion-completefile Makefile had not been updated for new lib build
+  dependency handling.
+
+2004-10-15 08:41 UTC  tuomov
+  * trunk: changeset 1837
+  ...
+
+2004-10-15 08:34 UTC  tuomov
+  * trunk: changeset 1836
+  libs.mk.dist fix.
+
+2004-10-14 12:21 UTC  tuomov
+  * trunk: changeset 1835
+  Removed the file distdep.
+
+2004-10-14 10:55 UTC  tuomov
+  * trunk: changeset 1834
+  Added -snapshot option to predist.sh.
+
+2004-10-11 15:34 UTC  tuomov
+  * trunk: changeset 1827
+  Added new ionws context menu entries for splitting.
+
+2004-10-11 13:44 UTC  tuomov
+  * trunk: changeset 1820
+  Floating splits can now be had on WIonWSs as well.
+
+2004-10-11 13:44 UTC  tuomov
+  * trunk: changeset 1819
+  Parallel stdisp rotations had not been updated to calculate geometries
+  properly now that we switch the places of the splits.
+
+2004-10-11 11:39 UTC  tuomov
+  * trunk: changeset 1818
+  Indentation fix.
+
+2004-10-09 16:27 UTC  tuomov
+  * trunk: changeset 1817
+  Added history search to line editor. (Scrolling through history
+  entries the beginnings of which match bol-point.)
+
+2004-10-09 16:00 UTC  tuomov
+  * trunk: changeset 1816
+  Moved WSplitFloat to another file from the other panews split
+  extensions.
+
+2004-10-09 15:51 UTC  tuomov
+  * trunk: changeset 1815
+  - Added some strings from mkman.lua to be translated for improved
+    translation of manual page bindings reference.
+  
+  - Finnish translation updates.
+
+2004-10-09 12:54 UTC  tuomov
+  * trunk: changeset 1812
+  Minor libextl-related rename.
+
+2004-10-09 11:02 UTC  tuomov
+  * trunk: changeset 1808
+  libextl-related 'make pot' fix.
+
+2004-10-09 06:13 UTC  tuomov
+  * trunk: changeset 1803
+  Moved also mkexports.lua to libextl.
+
+2004-10-09 05:33 UTC  tuomov
+  * trunk: changeset 1797
+  Added predist.sh.
+
+2004-10-09 05:05 UTC  tuomov
+  * trunk: changeset 1795
+  Separated libextl from the Ion source tree.
+
+2004-10-09 04:37 UTC  tuomov
+  * trunk: changeset 1792
+  Workspaces no longer propagate close requests.
+
+2004-10-09 04:30 UTC  tuomov
+  * trunk: changeset 1791
+  Removed obsolete autoconf note from README.
+
+2004-10-06 16:27 UTC  tuomov
+  * trunk: changeset 1790
+  Moved part of ioncore/ to libextl/.
+
+2004-10-06 14:33 UTC  tuomov
+  * trunk: changeset 1788
+  Fixed line editor display update on history selection.
+
+2004-10-06 10:58 UTC  tuomov
+  * trunk: changeset 1787
+  Some WIonWS fields were not being initialised.
+
+2004-10-06 10:17 UTC  tuomov
+  * trunk: changeset 1786
+  ion-completeman now understands manual sections.
+
+2004-10-06 09:39 UTC  tuomov
+  * trunk: changeset 1785
+  ioncore_g.screen_notify was not being initialised.
+
+2004-10-06 09:35 UTC  tuomov
+  * trunk: changeset 1784
+  Don't complain if no charset is given in LC_CTYPE if it is "C" or
+  "POSIX".
+
+2004-10-06 01:34 UTC  tuomov
+  * trunk: changeset 1783
+  Fixed floatws restack.
+
+2004-10-05 09:03 UTC  tuomov
+  * trunk: changeset 1782
+  comma and period were reversed inworkspace cycling.
+
+2004-10-05 07:38 UTC  tuomov
+  * trunk: changeset 1781
+  Changed some set_focus calls to warp calls.
+
+2004-10-05 07:30 UTC  tuomov
+  * trunk: changeset 1780
+  -V and -h options were not being supported after libtu optparser
+  simplifications. Fixed that.
+
+2004-10-05 05:07 UTC  tuomov
+  tagged ion-3ds-20041005
+
+2004-10-05 05:04 UTC  tuomov
+  * trunk: changeset 1778
+  Minor history fix.
+
+2004-10-05 04:47 UTC  tuomov
+  * trunk: changeset 1777
+  - Added _NET_ACTIVE_WINDOW (request and property) support.
+  
+  - Moved netwm atoms away from ioncore_g.
+
+2004-10-05 03:54 UTC  tuomov
+  * trunk: changeset 1776
+  Opening a new window on a panews focuses it if an unused area had the
+  focus.
+
+2004-10-05 00:41 UTC  tuomov
+  * trunk: changeset 1775
+  Moved region_set_await_focus call from xwindow_do_set_focus to where
+  it is called.
+
+2004-10-04 22:50 UTC  tuomov
+  * trunk: changeset 1774
+  Minor goto_previous fix. (It had been broken at some point.)
+
+2004-10-03 14:11 UTC  tuomov
+  * trunk: changeset 1773
+  Warp propagation improvements.
+
+2004-10-02 02:29 UTC  tuomov
+  * trunk: changeset 1772
+  Translation updates.
+
+2004-10-02 02:22 UTC  tuomov
+  * trunk: changeset 1771
+  Oops, history.h was missing.
+
+2004-10-02 02:13 UTC  tuomov
+  * trunk: changeset 1770
+  Added split and transpose bindings to WIonWS context menu.
+
+2004-10-02 02:07 UTC  tuomov
+  * trunk: changeset 1769
+  - Added WSplitSplit.flip.
+  
+  - WSplitFloat should properly transpose now.
+
+2004-09-30 17:24 UTC  tuomov
+  * trunk: changeset 1768
+  Added context-specific query histories.
+
+2004-09-28 16:42 UTC  tuomov
+  * trunk: changeset 1767
+  Added ioncore.set_selection and ioncore.request_selection (with a
+  continuation function as parameter to the latter) to deal with
+  selections from Lua code.
+
+2004-09-28 15:11 UTC  tuomov
+  * trunk: changeset 1763
+  Binding changes: Mod1+K comma/period moves tab left/right.
+  Mod1+comma/period switches to previous/next workspace. Corresponding
+  bindings for Left/Right keys were removed.
+
+2004-09-28 14:58 UTC  tuomov
+  * trunk: changeset 1762
+  Improved support for context menus.
+
+2004-09-17 20:17 UTC  tuomov
+  * trunk: changeset 1761
+  Renamed clientwin_added_hook to clientwin_mapped_hook and added
+  clientwin_unmapped_hook with the X window id as parameter.
+
+2004-09-17 20:09 UTC  tuomov
+  * trunk: changeset 1760
+  Replaced frame_activated_hook (and frame_inactivated_hook) with
+  region_activated_hook (region_inactivated_hook) called when the region
+  is get focus (loses focus).
+
+2004-09-17 19:55 UTC  tuomov
+  * trunk: changeset 1759
+  Some eventmask macro changes/cleanup.
+
+2004-09-17 19:43 UTC  tuomov
+  * trunk: changeset 1758
+  Moved hook parameter structures from source to header files and added
+  notes of parameters of all hooks to headers.
+
+2004-09-16 23:42 UTC  tuomov
+  * trunk: changeset 1757
+  Oops. Workspaces were being destroyed when stdisp was removed.
+
+2004-09-16 23:39 UTC  tuomov
+  * trunk: changeset 1756
+  REGION_PARENT macro changes.
+
+2004-09-16 23:09 UTC  tuomov
+  * trunk: changeset 1755
+  Improved handling of small unused spaces in panews.
+
+2004-09-16 19:32 UTC  tuomov
+  * trunk: changeset 1753
+  Some minor clean-up.
+
+2004-09-16 18:44 UTC  tuomov
+  * trunk: changeset 1752
+  Some region_set/unset/detach_manager/parent code simplifications.
+
+2004-09-16 17:06 UTC  tuomov
+  * trunk: changeset 1751
+  Use 'default' layout by default.
+
+2004-09-16 17:05 UTC  tuomov
+  * trunk: changeset 1750
+  - Added mod_panews.set/get
+  
+  - Moved loading of module configuration file to end of Lua code if the
+    module has such, as otherwise not everything may be available to the
+    configuration file.
+
+2004-09-16 16:27 UTC  tuomov
+  * trunk: changeset 1749
+  WSplitPane should now update markers on transpose.
+
+2004-09-07 18:45 UTC  tuomov
+  * trunk: changeset 1748
+  Changed some exec calls to exec_on calls in mod_query.
+
+2004-09-07 01:15 UTC  tuomov
+  * trunk: changeset 1747
+  Oops, warp setting was no longer used.
+
+2004-09-06 18:29 UTC  tuomov
+  tagged ion-3ds-20040906
+
+2004-09-06 18:28 UTC  tuomov
+  * trunk: changeset 1745
+  Fixed a typo in release notes.
+
+2004-09-06 18:25 UTC  tuomov
+  * trunk: changeset 1744
+  Removed POSIX_SOURCE setting from system-ac.mk.in
+
+2004-09-06 18:21 UTC  tuomov
+  * trunk: changeset 1743
+  Added release notes for a new release.
+
+2004-09-06 17:58 UTC  tuomov
+  * trunk: changeset 1742
+  Status display node loading fixes.
+
+2004-09-06 17:44 UTC  tuomov
+  * trunk: changeset 1741
+  - WPaneWS:s can be closed now.
+  
+  - Pane initial size calculation fix.
+
+2005-02-23 00:34 UTC  tuomov
+  * trunk: changeset 1740
+  Renamed autows to panews.
+
+2004-09-05 22:26 UTC  tuomov
+  * trunk: changeset 1739
+  Added compatibility conversion from old frame style names.
+
+2004-09-05 21:55 UTC  tuomov
+  * trunk: changeset 1738
+  Some focusing code simplifications/fixes/new bugs.
+
+2004-09-05 15:58 UTC  tuomov
+  * trunk: changeset 1737
+  Fixed an mplex layer2 focus issue.
+
+2004-09-05 15:30 UTC  tuomov
+  * trunk: changeset 1736
+  Fixed split tree ws_if_root pointer management bug.
+
+2004-09-05 15:12 UTC  tuomov
+  * trunk: changeset 1735
+  Added 'save' option for dock.
+
+2004-09-05 13:53 UTC  tuomov
+  * trunk: changeset 1734
+  Added frame_managed_changed_hook (improved from
+  frame_content_switched_hook).
+
+2004-09-02 15:20 UTC  tuomov
+  * trunk: changeset 1733
+  Border line drawing fix.
+
+2004-09-01 21:26 UTC  tuomov
+  * trunk: changeset 1732
+  WSplitPanes should now when appropriate recreate a WSplitUnused when
+  emptied.
+
+2004-09-01 18:45 UTC  tuomov
+  * trunk: changeset 1731
+  AutoWS now supports dropping stuff on unused areas.
+
+2004-09-01 13:31 UTC  tuomov
+  * trunk: changeset 1730
+  - Autows unused region focusing improvements.
+  
+  - Install cfg_autows.lua.
+
+2004-08-31 18:51 UTC  tuomov
+  * trunk: changeset 1729
+  Fixes to previous changes (coded without a monitor to test the code
+  on).
+
+2004-08-31 11:26 UTC  tuomov
+  * trunk: changeset 1728
+  Fixed a potential minor memory leak.
+
+2004-08-29 15:33 UTC  tuomov
+  * trunk: changeset 1727
+  Autoconf should work again. (Applied patch to move from
+  libtool/libltdl to plain libdl.)
+
+2004-08-29 07:18 UTC  tuomov
+  * trunk: changeset 1726
+  Some pane handle changes.
+
+2004-08-27 14:54 UTC  tuomov
+  * trunk: changeset 1725
+  Tiled workspace navigation code changes.
+
+2004-08-26 16:23 UTC  tuomov
+  * trunk: changeset 1724
+  - Moved resize code so that WUnusedWin can use it.
+  
+  - Added resize bindings for WUnusedWin.
+
+2004-08-26 14:16 UTC  tuomov
+  * trunk: changeset 1723
+  Added WUnusedWin for WSplitUnused.
+
+2004-08-23 03:27 UTC  tuomov
+  * trunk: changeset 1722
+  WAutoWS layout is now initialised when the workspace is created
+  instead of when the first frame is created.
+
+2004-08-21 15:54 UTC  tuomov
+  * trunk: changeset 1721
+  Added Czech translations of Ion messages.
+
+2004-08-19 15:16 UTC  tuomov
+  * trunk: changeset 1720
+  Added bindmap for WPaneWins.
+
+2004-08-18 14:12 UTC  tuomov
+  * trunk: changeset 1719
+  - AutoWS pane window handling improvements.
+  
+  - Changes in frame style names.
+
+2004-08-17 01:24 UTC  tuomov
+  * trunk: changeset 1718
+  Changed stdisp adaptation code to be more destructive on the split
+  tree to preserve special nodes for autows.
+
+2004-08-15 01:40 UTC  tuomov
+  * trunk: changeset 1717
+  Fixed a reference to mod_menu.
+
+2004-08-09 23:58 UTC  tuomov
+  * trunk: changeset 1716
+  Added beginnings of a Czech translation (by Miroslav Kure).
+
+2004-08-09 23:57 UTC  tuomov
+  * trunk: changeset 1715
+  Simplified regexp for 'uptime' load average method as some locales
+  show it differently.
+
+2004-08-06 02:55 UTC  tuomov
+  * trunk: changeset 1714
+  Some more makefile fixes.
+
+2004-08-06 00:05 UTC  tuomov
+  * trunk: changeset 1713
+  $(INTALLDIR) wasn't being called for translated manual page
+  directories.
+
+2004-08-03 18:40 UTC  tuomov
+  * trunk: changeset 1712
+  Added routine to drawing engine to draw just a borderline and not a
+  full border.
+
+2004-08-03 16:37 UTC  tuomov
+  * trunk: changeset 1711
+  updated cfgfile too.
+
+2004-08-02 19:35 UTC  tuomov
+  * trunk: changeset 1710
+  Submaps now default to AnyModifier.
+
+2004-08-02 16:01 UTC  tuomov
+  * trunk: changeset 1709
+  nroff instead of groff.
+
+2004-08-02 14:51 UTC  tuomov
+  * trunk: changeset 1708
+  Oops, calcmail was global.
+
+2004-08-02 14:49 UTC  tuomov
+  * trunk: changeset 1707
+  - Some more binding documentation and translation improvements.
+  
+  - Use proper welcome.lang.txt.
+
+2004-08-02 05:16 UTC  tuomov
+  * trunk: changeset 1706
+  Oops.
+
+2004-08-02 03:31 UTC  tuomov
+  * trunk: changeset 1705
+  - Removed share directory from the source tree and moved stuff therein
+    to ioncore and utils directories.
+  
+  - Some rules.mk improvements.
+
+2004-08-02 00:51 UTC  tuomov
+  * trunk: changeset 1704
+  - Improved the 'basic concepts' section of the manual page.
+  
+  - Added a Finnish translation of the manual page and welcome message.
+    (Correct version of the welcome message is not yet used.)
+
+2004-08-01 21:51 UTC  tuomov
+  * trunk: changeset 1703
+  ... and also gettext menuentry and submenu.
+
+2004-08-01 21:46 UTC  tuomov
+  * trunk: changeset 1702
+  Removed 'TR' from menu configuration files; include 'submenu' and
+  'menuentry' in keywords to lxgettext instead.
+
+2004-08-01 21:44 UTC  tuomov
+  * trunk: changeset 1701
+  Fixed type in documentation string.
+
+2004-08-01 21:43 UTC  tuomov
+  * trunk: changeset 1700
+  Added translations for binding documentation.
+
+2004-08-01 19:48 UTC  tuomov
+  * trunk: changeset 1699
+  Oops, previous commit was a bit broken.
+
+2004-08-01 19:06 UTC  tuomov
+  * trunk: changeset 1698
+  Binding documentation is now included in the configuration files and
+  the documentation for manual pages is generated from there.
+
+2004-07-31 22:00 UTC  tuomov
+  * trunk: changeset 1696
+  Minor fixes to previous error reporting and other changes.
+
+2004-07-31 20:55 UTC  tuomov
+  * trunk: changeset 1695
+  Added Finnish translation.
+
+2004-07-31 20:53 UTC  tuomov
+  * trunk: changeset 1694
+  - Some more error and other reporting changes for easier and better
+    translations.
+  
+  - Added menu configuration files to be translated.
+
+2004-07-31 20:12 UTC  tuomov
+  * trunk: changeset 1693
+  s/corner/pos/g in statusbar creation code.
+
+2004-07-31 19:11 UTC  tuomov
+  * trunk: changeset 1691
+  - Removed some redundant error reporting that is of no help to the
+    user.
+  
+  - Changes in error reporting policy: trust the source of the problem
+    do it, if it is our code.
+
+2005-02-23 00:02 UTC  tuomov
+  * trunk: changeset 1690
+  Added basic framework for localisation/language translations.
+
+2004-07-30 00:08 UTC  tuomov
+  tagged ion-3ds-20040730
+
+2004-07-29 23:32 UTC  tuomov
+  * trunk: changeset 1687
+  Oops DIST: PREFIX line was broken.
+
+2004-07-29 23:30 UTC  tuomov
+  * trunk: changeset 1686
+  Added new release notes and other preparation for the release of ion-
+  3ds-20040730.
+
+2004-07-29 23:24 UTC  tuomov
+  * trunk: changeset 1685
+  - Changes in default installation paths and binary names: ion->ion3,
+    etc.
+  
+  - Added ioncore.progname so that scripts that want to show the manual
+    page don't need to know if we're running as ion or pwm.
+
+2004-07-29 23:19 UTC  tuomov
+  * trunk: changeset 1684
+  Manual page updates.
+
+2004-07-29 19:58 UTC  tuomov
+  * trunk: changeset 1683
+  Added old release notes to the package.
+
+2004-07-29 19:47 UTC  tuomov
+  * trunk: changeset 1682
+  WIonWS: don't load WSplitST, because we don't save position and
+  orientation and thus think the saved stdispnode was bl/horizontal.
+
+2004-07-29 19:41 UTC  tuomov
+  * trunk: changeset 1681
+  Drawing engine configuration files now also use the underscored
+  "look_" prefix instead of "look-".
+
+2004-07-29 19:36 UTC  tuomov
+  * trunk: changeset 1680
+  Added styles for status displays.
+
+2004-07-29 18:52 UTC  tuomov
+  * trunk: changeset 1679
+  - Changed WFloatWS.circulate and backcirculate to be stacking based.
+  
+  - Fixes to previous stacking code changes.
+
+2004-07-29 18:27 UTC  tuomov
+  * trunk: changeset 1678
+  Floatws stacking code improvements.
+
+2004-07-29 02:18 UTC  tuomov
+  * trunk: changeset 1672
+  All savefiles are now also prefixed with "saved_".
+
+2004-07-29 02:02 UTC  tuomov
+  * trunk: changeset 1671
+  Removed WIonWS.resize_tree and added WSplit.rqgeom.
+
+2004-07-29 01:52 UTC  tuomov
+  * trunk: changeset 1670
+  Renamed redundantly named ioncore.create_new_ws to ioncore.create_ws.
+
+2004-07-29 01:50 UTC  tuomov
+  * trunk: changeset 1669
+  Added WWindow.xid export.
+
+2004-07-29 01:47 UTC  tuomov
+  * trunk: changeset 1668
+  Statically linking modules had been broken with the module stub loader
+  change, and make depend had never worked in this case.
+
+2004-07-29 01:32 UTC  tuomov
+  * trunk: changeset 1667
+  - Removed generic stacking code; stacking is now handled fully by
+    managers, making it simpler and better-working.
+  
+  - Some fixes to WMPlex layer2 new focus policy.
+
+2004-07-28 21:58 UTC  tuomov
+  * trunk: changeset 1666
+  Reorganisation autows code to match the new lua/c division and stub
+  loaders.
+
+2004-07-28 20:57 UTC  tuomov
+  * trunk: changeset 1665
+  Changes in WMPlex passive layer 2 object focus policy.
+
+2004-07-28 02:02 UTC  tuomov
+  * trunk: changeset 1664
+  Oops. Search path order had been reversed.
+
+2004-07-28 01:57 UTC  tuomov
+  * trunk: changeset 1663
+  Style file lookup was broken.
+
+2004-07-28 01:46 UTC  tuomov
+  * trunk: changeset 1662
+  The status display is now skipped by WIonWS navigational routines.
+
+2004-07-28 01:30 UTC  tuomov
+  * trunk: changeset 1661
+  Forgot to update resize_delay->kbresize_delay to config files.
+
+2004-07-28 01:25 UTC  tuomov
+  * trunk: changeset 1660
+  Oops. Forgot to remove references to DEFAULT_WS_TYPE.
+
+2004-07-28 01:17 UTC  tuomov
+  * trunk: changeset 1659
+  - Changes in functions to set some basic settings of ioncore and some
+    modules: for the most part, a single 'set' function now. Also added
+    the 'get' counterpart.
+  
+  - Removed extl_globals as DEFAULT_WS_TYPE is part of ioncore.set/get
+    now.
+
+2004-07-27 21:06 UTC  tuomov
+  * trunk: changeset 1658
+  Added WSplitInner.current dynfun.
+
+2004-07-27 21:01 UTC  tuomov
+  * trunk: changeset 1657
+  Restore extl_loadstring as mod_ionflux needs it.
+
+2004-07-27 19:23 UTC  tuomov
+  * trunk: changeset 1656
+  - Statusbar mail checker died if $MAIL didn't exist.
+  
+  - Added mailbox file setting.
+
+2004-07-27 18:03 UTC  tuomov
+  * trunk: changeset 1655
+  Some statusbar config tuning
+
+2004-07-27 17:49 UTC  tuomov
+  * trunk: changeset 1654
+  - Better cfg_dock.lua; moved code to mod_dock.
+  
+  - Status display is now _not_ saved to layout savefile, for easier
+    switching between different status displays.
+  
+  - Some mplex layer2 access improvements.
+
+2004-07-27 13:48 UTC  tuomov
+  * trunk: changeset 1653
+  Fixed some typos etc.
+
+2004-07-27 13:42 UTC  tuomov
+  * trunk: changeset 1652
+  - Improved ext_statusbar update time calculation.
+  
+  - Added ext_statusbar to list of subdirectories to build in the top-
+    level Makefile.
+
+2004-07-27 04:11 UTC  tuomov
+  * trunk: changeset 1651
+  Oops, mail_last_check wasn't being update.
+
+2004-07-27 04:10 UTC  tuomov
+  * trunk: changeset 1650
+  Added ext_statusbar statusbar script.
+
+2004-07-27 00:43 UTC  tuomov
+  * trunk: changeset 1649
+  - Put ext_misc stuff in ioncore anyway; removed close_current_ws.
+  
+  - Fixed some PWM stuff.
+
+2004-07-27 00:26 UTC  tuomov
+  * trunk: changeset 1648
+  Status display removal is now properly notified by the screen to
+  workspaces over restarts.
+
+2004-07-26 22:03 UTC  tuomov
+  * trunk: changeset 1647
+  Oops, welcome message was no longer installed.
+
+2004-07-26 21:57 UTC  tuomov
+  * trunk: changeset 1645
+  Some documentation fixes and documentation generation changes.
+
+2004-07-26 21:57 UTC  tuomov
+  * trunk: changeset 1644
+  Oops, there was still a reference to draw.lua
+
+2004-07-26 21:48 UTC  tuomov
+  * trunk: changeset 1643
+  ioncore-extras.lua was still lying around after barfed commit.
+
+2004-07-26 21:29 UTC  tuomov
+  * trunk: changeset 1642
+  Fixed a sed vomit in ion-completeman (around sed's brain-damaged
+  refusal to support escaping of square brackets) after GNU sed stopped
+  accepting the previous vomit.
+
+2004-07-26 20:50 UTC  tuomov
+  * trunk: changeset 1641
+  Fixes to previous failed commit.
+
+2004-07-26 20:46 UTC  tuomov
+  * trunk: changeset 1640
+  - United ioncore and ioncorelib; mod_query and querylib; and mod_menu
+    and menulib routines into the single namespace of the first of each
+    to present the user with less different code units.
+  
+  - Changed all configuration files the user might want to edit to be
+    prefixed with 'cfg_'.
+
+2004-07-26 19:17 UTC  tuomov
+  * trunk: changeset 1639
+  Renamed 'include' 'dopath' (to look similar to Lua's 'dofile' that
+  expects complete file name).
+
+2004-07-26 18:58 UTC  tuomov
+  * trunk: changeset 1638
+  Added stub loaders for modules, so users only need a single command to
+  load scripts or modules.
+
+2004-07-26 17:43 UTC  tuomov
+  * trunk: changeset 1637
+  Some minor infowin, stdisp and timer fixes and changes.
+
+2004-07-26 16:13 UTC  tuomov
+  * trunk: changeset 1636
+  Exported timers to Lua side and some other changes in timer code.
+
+2004-07-26 14:38 UTC  tuomov
+  * trunk: changeset 1633
+  Added some code to WInfoWin to eventually allow it to be used as an
+  stdisp.
+
+2004-07-25 19:18 UTC  tuomov
+  * trunk: changeset 1632
+  Some autows overlap and new OO split code fixes.
+
+2004-07-24 14:09 UTC  tuomov
+  * trunk: changeset 1631
+  Some fixes to previous changes in name allocation.
+
+2004-07-24 11:58 UTC  tuomov
+  * trunk: changeset 1630
+  Added still incomplete support for partial floating of WSplitPanes on
+  WAutoWS:s.
+
+2004-07-23 22:27 UTC  tuomov
+  * trunk: changeset 1629
+  Resizing code fixes and simplifications.
+
+2004-07-23 20:50 UTC  tuomov
+  * trunk: changeset 1628
+  Some more name allocation improvements and simplifications.
+
+2004-07-23 19:34 UTC  tuomov
+  * trunk: changeset 1627
+  Fixed problem allocating names that already contain something that
+  looks like an instance number.
+
+2004-07-22 22:31 UTC  tuomov
+  * trunk: changeset 1626
+  - Moved WSplitUnused code to mod_autows from mod_ionws.
+  
+  - Added initial version of WSplitPane split tree node for better
+    organisation of different application classes in WAutoWS split tree.
+
+2004-07-18 18:59 UTC  tuomov
+  * trunk: changeset 1625
+  The split tree now uses inheritance within the Ion object system, so
+  it is easier to add special inner nodes to it.
+
+2004-07-18 02:25 UTC  tuomov
+  * trunk: changeset 1624
+  Some dock changes+fixes.
+
+2004-07-17 00:48 UTC  tuomov
+  * trunk: changeset 1623
+  Autows template code is now aware of stdisp.
+
+2004-07-16 23:54 UTC  tuomov
+  * trunk: changeset 1622
+  Autows classification heuristics improvements.
+
+2004-07-15 15:29 UTC  tuomov
+  * trunk: changeset 1621
+  SM segfault fix.
+
+2004-07-15 13:21 UTC  tuomov
+  * trunk: changeset 1620
+  Fixed config file help.
+
+2004-07-15 01:59 UTC  tuomov
+  * trunk: changeset 1619
+  Removed stdisp from lists of normal managed objects on workspaces.
+
+2004-07-13 16:05 UTC  tuomov
+  * trunk: changeset 1618
+  Fixed WRegion.size_hints export.
+
+2004-07-13 16:04 UTC  tuomov
+  * trunk: changeset 1617
+  Always leave a SPLIT_UNUSED when stacking.
+
+2004-07-13 16:03 UTC  tuomov
+  * trunk: changeset 1616
+  Changes in split tree resize code to better handle SPLIT_UNUSED.
+
+2004-07-12 20:11 UTC  tuomov
+  * trunk: changeset 1615
+  AutoWS layout initialisation code improvements.
+
+2004-07-11 22:57 UTC  tuomov
+  * trunk: changeset 1614
+  Beginnings of a new template-based autows implementation.
+
+2004-07-08 00:18 UTC  tuomov
+  * trunk: changeset 1613
+  Some frame->style handling changes.
+
+2004-07-08 00:17 UTC  tuomov
+  * trunk: changeset 1612
+  Possibly fixed scratchpad focusing (again).
+
+2004-07-03 18:04 UTC  tuomov
+  tagged ion-3ds-20040703
+
+2004-07-03 18:01 UTC  tuomov
+  * trunk: changeset 1610
+  Added a note on ./configure being broken in README.
+
+2004-07-03 17:57 UTC  tuomov
+  * trunk: changeset 1609
+  Default to no dock.
+
+2004-07-03 17:56 UTC  tuomov
+  * trunk: changeset 1608
+  Added code in dock.lua to create dock of either flavour.
+
+2004-07-03 17:56 UTC  tuomov
+  * trunk: changeset 1607
+  Do not automagically redirect region_manage_clientwin requests to
+  passive layer 2 objects.
+
+2004-07-03 17:55 UTC  tuomov
+  * trunk: changeset 1606
+  Added table.join.
+
+2004-07-03 17:54 UTC  tuomov
+  * trunk: changeset 1605
+  Changes in menu stacking.
+
+2004-07-03 17:25 UTC  tuomov
+  * trunk: changeset 1604
+  Implemented WRegion.rqclose for WDock (and removed WDock.destroy), so
+  that empty docks can now be closed in a consistent manner.
+
+2004-07-03 17:21 UTC  tuomov
+  * trunk: changeset 1603
+  Added some region dynfuns.
+
+2004-06-27 17:43 UTC  tuomov
+  * trunk: changeset 1602
+  Dock supports bindings now.
+
+2004-06-27 17:12 UTC  tuomov
+  * trunk: changeset 1601
+  - Removed orientation from stdisp parameters; it is got from the
+    region itself through region_orientation.
+  
+  - WDock.set can now properly request changes when working as WMPlex
+    stdisp.
+
+2004-06-26 20:13 UTC  tuomov
+  * trunk: changeset 1600
+  floatws fix: don't save stdisp configuration.
+
+2004-06-26 20:05 UTC  tuomov
+  * trunk: changeset 1599
+  Ensure FD_CLOEXEC is set.
+
+2004-06-26 19:53 UTC  tuomov
+  * trunk: changeset 1598
+  - Removed generic stacking exports. Raise/lower is only available
+    through WFloatWS.raise/lower now.
+  
+  - Removed WRegion.active_sub export.
+
+2004-06-26 19:35 UTC  tuomov
+  * trunk: changeset 1597
+  Renamed 'reference' field in load/info tables to 'reg'.
+
+2004-06-26 18:40 UTC  tuomov
+  * trunk: changeset 1596
+  Guarantee maximum size to be set in region_size_hints along with base
+  and minimum size.
+
+2004-06-26 18:37 UTC  tuomov
+  * trunk: changeset 1595
+  Removed relw and relh from region_size_hints; use
+  XSizeHints.base_width/height instead.
+
+2004-06-26 16:07 UTC  tuomov
+  * trunk: changeset 1594
+  - Added support for passive mplex layer 2 objects.
+  
+  - Unified generic stdisp and floating dock position parametrisation.
+
+2004-06-23 15:56 UTC  tuomov
+  * trunk: changeset 1593
+  Improved mplex managed object attribute storage.
+
+2004-06-22 00:16 UTC  tuomov
+  * trunk: changeset 1592
+  Added XID to querylib.show_clientwin.
+
+2004-06-21 15:02 UTC  tuomov
+  * trunk: changeset 1591
+  Fixed drawing engine style loading code when neither font nor based_on
+  was set.
+
+2004-06-21 08:18 UTC  tuomov
+  * trunk: changeset 1590
+  Previous "fixes" broken dock resize on workspace change if previous
+  docknode geometry was a good fit.
+
+2004-06-21 07:37 UTC  tuomov
+  * trunk: changeset 1589
+  Some status display/dock support code fixes and improvements.
+
+2004-06-21 00:30 UTC  tuomov
+  * trunk: changeset 1588
+  Support dragging tab of current window by clicking anywhere in a
+  frame.
+
+2004-06-20 16:45 UTC  tuomov
+  * trunk: changeset 1587
+  Initial conversion of mod_dock to work with WMPlex sticky auto-sizing
+  status display mechanisms.
+
+2004-06-20 11:47 UTC  tuomov
+  * trunk: changeset 1586
+  Don't register WRegionSimpleCreateFn as it is not actually needed.
+  Thus only register WRegionLoadCreateFn.
+
+2004-06-20 11:45 UTC  tuomov
+  * trunk: changeset 1584
+  Don't duplicate libtu/stringintmap_key as value2str.
+
+2004-06-20 11:10 UTC  tuomov
+  * trunk: changeset 1582
+  Fixed folds.
+
+2004-06-19 16:39 UTC  tuomov
+  * trunk: changeset 1581
+  - Use of block auto-indentation had broken indentation after some
+    EXTL_EXPORT statements.
+  
+  - Added documentation for WMPlex.set_stdisp and WMPlex.get_stdisp.
+
+2004-06-19 16:26 UTC  tuomov
+  * trunk: changeset 1580
+  Improved support for auto-sizing status display/dock.
+
+2004-06-19 13:26 UTC  tuomov
+  * trunk: changeset 1579
+  Export WScreen.set_managed_offset.
+
+2004-06-18 22:14 UTC  tuomov
+  * trunk: changeset 1576
+  Use libtu's new stringstore for storing frame style names.
+
+2004-06-17 20:42 UTC  tuomov
+  * trunk: changeset 1573
+  Fixed handling of requested geometry in floatws_attach_clientwin.
+
+2004-06-17 17:14 UTC  tuomov
+  * trunk: changeset 1572
+  Added some split-tree manipulation algorithms needed to eventually
+  support an automagically properly sized dock.
+
+2004-06-17 13:58 UTC  tuomov
+  * trunk: changeset 1571
+  Resize primary node fix.
+
+2004-06-14 15:09 UTC  tuomov
+  * trunk: changeset 1570
+  Finished converting (still crappy) autows penalty code to Lua and some
+  tuning.
+
+2004-06-12 20:51 UTC  tuomov
+  * trunk: changeset 1569
+  Moved autows penalty calculation code to Lua side for easier
+  customisation.
+
+2004-06-12 18:10 UTC  tuomov
+  * trunk: changeset 1568
+  - Added WRegion.size_hints to exports
+  
+  - ... and renamed region_resize_hints to region_size_hints.
+
+2004-06-12 14:16 UTC  tuomov
+  * trunk: changeset 1567
+  Fixed querylib.query_exit and renamed it to querylib.query_shutdown.
+
+2004-06-11 19:03 UTC  tuomov
+  * trunk: changeset 1566
+  Changed penalties to be based on proportional shrinkage/growth/free
+  space after split and other autows placement code changes.
+
+2004-06-11 11:03 UTC  tuomov
+  * trunk: changeset 1565
+  Put back WIonWS.create_frame_fn.
+
+2004-06-10 22:48 UTC  tuomov
+  * trunk: changeset 1564
+  - Session saving is no longer disabled on exit if there was an error
+    loading layout. Instead a backup of the layout savefile is made.
+  
+  - The behaviour of ioncore.resign was changed not to save session, but
+    instead just quit and, when running under a session manager,
+    instruct the session manager to not restart the WM again.
+
+2004-06-10 21:13 UTC  tuomov
+  * trunk: changeset 1563
+  Fixed focusing on frame destroy.
+
+2004-06-10 21:01 UTC  tuomov
+  * trunk: changeset 1562
+  Bracing error caused split unused fusing not to work in all cases.
+
+2004-06-10 15:33 UTC  tuomov
+  * trunk: changeset 1561
+  Changes in the set of exported WSplit methods and WIonWS:split_tree().
+
+2004-06-10 15:15 UTC  tuomov
+  * trunk: changeset 1560
+  Added code to fuse adjacent SPLIT_UNUSED nodes.
+
+2004-06-09 16:12 UTC  tuomov
+  * trunk: changeset 1559
+  Removed WIonFrame and WAutoFrame. There's just WFrame and WFloatFrame
+  (also to be unified?) now.
+
+2004-06-08 20:52 UTC  tuomov
+  * trunk: changeset 1558
+  Some minor autows placement tuning.
+
+2004-06-08 20:26 UTC  tuomov
+  * trunk: changeset 1557
+  Moved p_move from WFloatFrame to WFrame.
+
+2004-06-08 17:23 UTC  tuomov
+  * trunk: changeset 1556
+  Binding to nil should again remove the binding.
+
+2004-06-08 17:21 UTC  tuomov
+  * trunk: changeset 1555
+  Fixes in navigation code handling of SPLIT_UNUSED.
+
+2004-06-07 13:41 UTC  tuomov
+  * trunk: changeset 1554
+  Some more unused space information storage changes.
+
+2004-06-07 11:09 UTC  tuomov
+  * trunk: changeset 1553
+  load_autows hand't been updated to new ionws_load_node parameters.
+
+2004-06-06 19:39 UTC  tuomov
+  * trunk: changeset 1552
+  Added querylib.show_clientwin to display information on a client
+  window and put it in the context menu.
+
+2004-06-06 19:27 UTC  tuomov
+  * trunk: changeset 1551
+  Documentation fixes.
+
+2004-06-06 18:32 UTC  tuomov
+  * trunk: changeset 1550
+  Some fixes in split tree load code changes.
+
+2004-06-06 17:58 UTC  tuomov
+  * trunk: changeset 1549
+  Added penalties to attach to an existing frame.
+
+2004-06-06 17:09 UTC  tuomov
+  * trunk: changeset 1548
+  Added querylib.query_menu that can be used to create a query of a
+  menu.
+
+2004-06-06 16:16 UTC  tuomov
+  * trunk: changeset 1547
+  Moved Fx queries to WMPlex level so they're available on empty
+  workspaces and full screen client windows as well.
+
+2004-06-06 16:08 UTC  tuomov
+  * trunk: changeset 1546
+  Added 'lazy' split attribute. If a horizontal/vertical split is
+  "lazy", and a direct child is removed, the split won't be collapsed
+  unless the other child is SPLIT_UNUSED.
+
+2004-06-06 15:47 UTC  tuomov
+  * trunk: changeset 1545
+  Few minor fixes.
+
+2004-06-05 19:24 UTC  tuomov
+  * trunk: changeset 1543
+  - Moved workspace dummy window code from WFloatWS to WGenWS to be
+    available to WAutoWS as well.
+  
+  - Removed unused managed_splits field from WIonWS.
+
+2004-06-05 15:14 UTC  tuomov
+  * trunk: changeset 1542
+  WAutoWS placement code clean-up.
+
+2004-06-05 09:42 UTC  tuomov
+  * trunk: changeset 1541
+  Oops. Fixed sanity check.
+
+2004-06-04 23:15 UTC  tuomov
+  * trunk: changeset 1539
+  Added Xinerama sanity check.
+
+2004-06-04 17:55 UTC  tuomov
+  * trunk: changeset 1537
+  Changed WAutoWS placement code to use new unused space storage and
+  added framework code to handle calculating penalties based on
+  "immediate" available unused space.
+
+2004-06-04 17:29 UTC  tuomov
+  * trunk: changeset 1536
+  Changed storage of used/unused space within
+  SPLIT_VERTICAL/SPLIT_HORIZONTAL.
+
+2004-06-04 15:49 UTC  tuomov
+  * trunk: changeset 1535
+  Fixed WMPlex.l2_show focus.
+
+2004-06-03 11:25 UTC  tuomov
+  * trunk: changeset 1534
+  Added very preliminary WAutoWS layout code.
+
+2004-06-03 11:23 UTC  tuomov
+  * trunk: changeset 1533
+  Code that creates regions from tables now supports a 'reference' to
+  use existing regions.
+
+2004-06-03 11:21 UTC  tuomov
+  * trunk: changeset 1532
+  Fixed some indentation.
+
+2004-06-02 19:15 UTC  tuomov
+  * trunk: changeset 1531
+  Added support for "static" splits.
+
+2004-05-31 10:40 UTC  tuomov
+  * trunk: changeset 1526
+  Splitting fixes.
+
+2004-05-31 10:39 UTC  tuomov
+  * trunk: changeset 1525
+  Added nil check to hook_add_extl.
+
+2004-05-30 16:01 UTC  tuomov
+  * trunk: changeset 1524
+  Fixed a serious brainfart in region name allocation. (Temporary hack;
+  need to do this more efficiently.)
+
+2004-05-30 14:13 UTC  tuomov
+  * trunk: changeset 1523
+  Oops. Alloc one byte too small.
+
+2004-05-29 19:13 UTC  tuomov
+  * trunk: changeset 1522
+  Check that client window is not already managed when loading saved
+  configuration (on restart).
+
+2004-05-29 19:12 UTC  tuomov
+  * trunk: changeset 1521
+  Added -noerrorlog startup option.
+
+2004-05-29 12:03 UTC  tuomov
+  * trunk: changeset 1520
+  Fixed dec/inc_index bindings.
+
+2004-05-29 11:26 UTC  tuomov
+  * trunk: changeset 1519
+  Bindmap binding count was not maintained correctly if a key/button was
+  re-bound.
+
+2004-05-28 14:41 UTC  tuomov
+  * trunk: changeset 1518
+  Added SPLIT_UNUSED nodes and stopped allowing frames that do not use
+  full space of their SPLIT_REGNODE:
+
+2004-05-26 19:26 UTC  tuomov
+  * trunk: changeset 1517
+  querylib.query_renameworkspace had been broken at some point.
+
+2004-05-26 19:24 UTC  tuomov
+  * trunk: changeset 1516
+  Unified module and script search paths. There's only one search path
+  now.
+
+2004-05-26 19:17 UTC  tuomov
+  * trunk: changeset 1515
+  *sigh* install-sh seems to require -c to copy.
+
+2004-05-26 17:24 UTC  tuomov
+  * trunk: changeset 1514
+  Changed INSTALL to install-sh
+
+2004-05-26 17:10 UTC  tuomov
+  * trunk: changeset 1513
+  Build rules fixes.
+
+2004-05-26 16:57 UTC  tuomov
+  * trunk: changeset 1512
+  Libtool is no longer used: problems with one module depending on
+  another.
+
+2004-05-26 14:13 UTC  tuomov
+  * trunk: changeset 1511
+  Added unviewable check in mplex init.
+
+2004-05-26 13:57 UTC  tuomov
+  * trunk: changeset 1510
+  - WMPlex layer2 object hide improved.
+  
+  - Unified WMPlex layer list access functions.
+
+2004-05-21 21:04 UTC  tuomov
+  * trunk: changeset 1509
+  Removed 'exec' from /bin/sh call to execute command so that more
+  complex commands can be entered.
+
+2004-05-21 19:46 UTC  tuomov
+  * trunk: changeset 1507
+  Removed os.execute block.
+
+2004-05-21 18:49 UTC  tuomov
+  * trunk: changeset 1506
+  Improved querylib.query_exec completion to break at spaces.
+
+2004-05-21 18:33 UTC  tuomov
+  * trunk: changeset 1505
+  SSH host completion now understands usernames. (Code taken from the
+  Ion Wiki.)
+
+2004-05-21 18:26 UTC  tuomov
+  * trunk: changeset 1504
+  - Improved integration of exit/restart/save state with session
+    management.
+  
+  - Removed ioncore.exit(); in most cases use ioncore.shutdown() instead
+    (and ioncore.resign() in others).
+
+2004-05-20 21:48 UTC  tuomov
+  * trunk: changeset 1503
+  Added comment headers to the files.
+
+2004-05-20 21:35 UTC  tuomov
+  * trunk: changeset 1502
+  - Added mod_sm.request_save.
+  
+  - Removed support for whatever (older?) session management system
+    defines the macro "XSM".
+
+2004-05-20 18:26 UTC  tuomov
+  * trunk: changeset 1501
+  - Automatically load mod_sm of the SESSION_MANAGER environment
+    variable is set.
+  
+  - Moved session directory setup code to mod_sm.
+
+2004-05-20 16:03 UTC  tuomov
+  * trunk: changeset 1500
+  Added WClientWin.xid export to get X window id.
+
+2004-05-20 15:04 UTC  tuomov
+  * trunk: changeset 1499
+  Improved WAutoFrame "lazy" move/resize behaviour.
+
+2004-05-20 14:38 UTC  tuomov
+  * trunk: changeset 1498
+  Split resizing code now first tries to use any unused space before
+  shrinking a region.
+
+2004-05-19 08:35 UTC  tuomov
+  * trunk: changeset 1497
+  - Do not clear client window activity status when receiving focus if
+    the urgency hint is still set.
+  
+  - Exported WRegion.notify_activity and WRegion.clear_activity.
+
+2004-05-18 20:02 UTC  tuomov
+  * trunk: changeset 1496
+  Fixed exec bindings to correctly set the root window and changed
+  ioncore.exec_on to accept any region as parameter.
+
+2004-05-18 17:55 UTC  tuomov
+  * trunk: changeset 1495
+  Changes in save directory under a session manager; use ~/.ion3/gnome-
+  session-$GNOME_DESKTOP_SESSION_ID (with potentially unallowed
+  characters replaced) if this environment variable is set.
+
+2004-05-17 22:52 UTC  tuomov
+  * trunk: changeset 1494
+  Merged dock kde systray support patch and fixes from stable branch.
+
+2004-05-17 22:41 UTC  tuomov
+  * trunk: changeset 1491
+  Added missing include.
+
+2004-05-15 17:45 UTC  tuomov
+  * trunk: changeset 1490
+  Removed extl_call_named as it is no longer needed and useless bloat.
+
+2004-05-15 17:43 UTC  tuomov
+  * trunk: changeset 1489
+  Converted ionws_placement_method to ionws_placement_alt hook. Removed
+  the hook from WAutoWS code temporarily.
+
+2004-05-15 09:47 UTC  tuomov
+  * trunk: changeset 1488
+  Fixed attempt to free memory at middle of allocated area in menu
+  typeahead find.
+
+2004-05-14 14:14 UTC  tuomov
+  * trunk: changeset 1487
+  - Changed mplex l2 hide/show to warp (if enabled) to new active
+    region.
+  
+  - Some other focus handling fixes/changes.
+
+2004-05-14 13:13 UTC  tuomov
+  * trunk: changeset 1486
+  WMPlex contents are unmapped when either width or height of client
+  managed area becomes at most 1 instead of resizing them. Previously
+  this was only done vertically for frames (shade).
+
+2004-05-14 10:14 UTC  tuomov
+  * trunk: changeset 1483
+  Fixed shade on WIonWS.
+
+2004-05-13 12:01 UTC  tuomov
+  * trunk: changeset 1482
+  Minor cleanup.
+
+2004-05-13 09:02 UTC  tuomov
+  * trunk: changeset 1481
+  Removed some scratch code.
+
+2004-05-12 15:39 UTC  tuomov
+  * trunk: changeset 1480
+  Oops. There was a typo that was causing region flags to be cleared
+  when children were being rescued.
+
+2004-05-12 13:49 UTC  tuomov
+  * trunk: changeset 1479
+  hook_add_extl was missing extl_ref_fn.
+
+2004-05-12 13:30 UTC  tuomov
+  * trunk: changeset 1478
+  Documented hook exports.
+
+2004-05-12 12:56 UTC  tuomov
+  * trunk: changeset 1477
+  Removed deprecated references to defcmd.
+
+2004-05-12 12:50 UTC  tuomov
+  * trunk: changeset 1476
+  New hook system. Both Lua and C-side now have the same hooks except
+  for ioncore_handle_event_alt.
+
+2004-05-12 10:31 UTC  tuomov
+  * trunk: changeset 1475
+  Added functions to test ExtlTab:s and ExtlFn:s for equality.
+
+2004-05-08 23:38 UTC  tuomov
+  * trunk: changeset 1474
+  WM_COLORMAP_WINDOWS handling fixes. (Does something still use
+  this/bother supporting indexed colour models at all?)
+
+2004-05-06 21:01 UTC  tuomov
+  * trunk: changeset 1472
+  Oops. nodecor wan't being initialised if mwm hints are not set.
+
+2004-05-06 09:35 UTC  tuomov
+  * trunk: changeset 1470
+  Space wasn't being reclaimed when unsplitting.
+
+2004-05-03 15:22 UTC  tuomov
+  * trunk: changeset 1469
+  Added extra check. Fixed indentation.
+
+2004-05-03 15:21 UTC  tuomov
+  * trunk: changeset 1468
+  Fixed dynamic function call.
+
+2004-05-03 14:18 UTC  tuomov
+  * trunk: changeset 1466
+  Fixed stippled tab font when -i18n was not set.
+
+2004-05-02 15:57 UTC  tuomov
+  * trunk: changeset 1464
+  Path fixes in file headings.
+
+2004-05-02 15:46 UTC  tuomov
+  * trunk: changeset 1463
+  Added some basic framework for autows. No functionality essentially
+  different from ionws yet.
+
+2004-05-01 11:32 UTC  tuomov
+  * trunk: changeset 1460
+  Transparency configuration reading fix.
+
+2004-04-30 19:45 UTC  tuomov
+  * trunk: changeset 1459
+  Minor session management support improvements; should work with gnome-
+  session now.
+
+2004-04-28 11:34 UTC  tuomov
+  * trunk: changeset 1458
+  Only kill active grab when esc is pressed, not released.
+
+2004-04-24 22:45 UTC  tuomov
+  * trunk: changeset 1456
+  WIonWS supports alternative frame creation routine (for inheritance).
+
+2004-04-20 18:01 UTC  tuomov
+  * trunk: changeset 1455
+  Split size calculation TODOs handled.
+
+2004-04-20 15:03 UTC  tuomov
+  * trunk: changeset 1454
+  Some fixes to new split tree code.
+
+2004-04-17 17:44 UTC  tuomov
+  * trunk: changeset 1453
+  - Added the WARN_FUNC macros.
+  
+  - WIonWS code checks that split_tree!=NULL more often so that it
+    should be possible to inherit it by a workspace class that doesn't
+    always have such a tree.
+
+2004-04-17 16:45 UTC  tuomov
+  * trunk: changeset 1452
+  Fixed split bindings to attach currently displayed region of old frame
+  to new frame.
+
+2004-04-17 15:03 UTC  tuomov
+  * trunk: changeset 1451
+  Added code to transpose splits.
+
+2004-04-16 22:34 UTC  tuomov
+  * trunk: changeset 1450
+  Slightly improved splitting code.
+
+2004-04-11 10:15 UTC  tuomov
+  * trunk: changeset 1448
+  Oops, there was an off-by-one bug in _NET_VIRTUAL_ROOTS setting.
+
+2004-04-05 12:33 UTC  tuomov
+  * trunk: changeset 1444
+  Added a kludge to deal with waitrelease when the modifiers has already
+  been released.
+
+2004-04-05 07:39 UTC  tuomov
+  * trunk: changeset 1442
+  Oops, LCDIR and SHAREDIR were on path in wrong order.
+
+2004-04-04 11:48 UTC  tuomov
+  * trunk: changeset 1441
+  Improved split data structures.
+
+2004-04-02 19:59 UTC  tuomov
+  * trunk: changeset 1440
+  WMPlex is now a proper non-virtual class and objects of this type can
+  be created and nested like any others.
+
+2004-04-02 08:19 UTC  tuomov
+  * trunk: changeset 1439
+  Also put querylib.lc and menulib.lc in LCDIR.
+
+2004-04-02 08:04 UTC  tuomov
+  * trunk: changeset 1437
+  Changed compiled .lc files' path to LIBDIR/ion/lc.
+
+2004-03-27 09:01 UTC  tuomov
+  * trunk: changeset 1435
+  Fixed floatframe client window size issue when shaded.
+
+2004-03-26 22:51 UTC  tuomov
+  * trunk: changeset 1434
+  Split brush and style code in separate files.
+
+2004-03-26 22:37 UTC  tuomov
+  * trunk: changeset 1433
+  Removed object abstraction from drawing engine brushes to help
+  creating drawing engines with other font routines.
+
+2004-03-26 22:12 UTC  tuomov
+  * trunk: changeset 1432
+  Stacking fix(?).
+
+2004-03-24 20:45 UTC  tuomov
+  * trunk: changeset 1431
+  Moved some root position notification code from WRegion to WWindow.
+
+2004-03-24 20:37 UTC  tuomov
+  * trunk: changeset 1430
+  Removed duplicate fitrep code.
+
+2004-03-24 18:55 UTC  tuomov
+  * trunk: changeset 1429
+  Some WIonWS and split tree code reorganisation.
+
+2004-03-24 17:33 UTC  tuomov
+  * trunk: changeset 1428
+  Don't put input method failure complaints in startup error log.
+
+2004-03-23 11:28 UTC  tuomov
+  * trunk: changeset 1426
+  querylib.query_restart and query_exit were not yet converted to the
+  Ion3 scheme of things.
+
+2004-03-23 02:15 UTC  tuomov
+  * trunk: changeset 1424
+  Client window unmap handling fixes (?).
+
+2004-03-22 12:29 UTC  tuomov
+  * trunk: changeset 1423
+  String freeing fix.
+
+2004-03-21 16:58 UTC  tuomov
+  * trunk: changeset 1422
+  Fixed a colourmap update crash.
+
+2004-03-21 16:43 UTC  tuomov
+  * trunk: changeset 1421
+  Binaries were still being stripped.
+
+2004-03-20 21:31 UTC  tuomov
+  * trunk: changeset 1420
+  mod_sm wasn't registering exported functions.
+
+2004-03-20 21:23 UTC  tuomov
+  * trunk: changeset 1419
+  Added very preliminary (and partially broken) session management
+  support based on the 'sm' module for Ion1.
+
+2004-03-20 14:39 UTC  tuomov
+  * trunk: changeset 1418
+  Moved activity notification window code from scratchpad to screen;
+  also works for normal workspaces now.
+
+2004-03-18 21:11 UTC  tuomov
+  * trunk: changeset 1416
+  Improvements and fixes in keyboard mapping changes handling.
+
+2004-03-18 09:39 UTC  tuomov
+  * trunk: changeset 1413
+  Made drawing engine objects inheritable and initialisation code
+  reusable.
+
+2004-03-17 20:17 UTC  tuomov
+  * trunk: changeset 1412
+  Some scratchpad activity notification window changes/fixes.
+
+2004-03-16 17:24 UTC  tuomov
+  tagged ion-3ds-20040316
+
+2004-03-16 17:23 UTC  tuomov
+  * trunk: changeset 1406
+  Added commented-out line to load mod_sp.
+
+2004-03-16 16:42 UTC  tuomov
+  * trunk: changeset 1405
+  Fixed typo Wegion->WRegion.
+
+2004-03-16 16:40 UTC  tuomov
+  * trunk: changeset 1404
+  - Added rectangle_constrain and changed code that did the same thing
+    to use the function.
+  
+  - Some clientwin WFitParams usage improvements and fixes.
+
+2004-03-16 16:09 UTC  tuomov
+  * trunk: changeset 1403
+  Indentation fixes.
+
+2004-03-15 16:32 UTC  tuomov
+  * trunk: changeset 1401
+  Renamed module directories.
+
+2004-03-15 16:02 UTC  tuomov
+  * trunk: changeset 1399
+  Added scratchpad activity notification bubble.
+
+2004-03-15 08:34 UTC  tuomov
+  * trunk: changeset 1397
+  Documentation generation fixes and improvements.
+
+2004-03-15 08:10 UTC  tuomov
+  * trunk: changeset 1396
+  Some function name shortenings and other changes.
+
+2004-03-14 23:40 UTC  tuomov
+  * trunk: changeset 1395
+  Fixed some exec calls to ioncore.exec.
+
+2004-03-14 23:29 UTC  tuomov
+  * trunk: changeset 1394
+  Fixed getbindings for buttons.
+
+2004-03-14 22:53 UTC  tuomov
+  * trunk: changeset 1393
+  Made some more minor changes to binding configuration and added
+  ioncore.getbindings and ioncorelib.getbindings that can be used to get
+  a table of made bindings for all contexts.
+
+2004-03-14 19:04 UTC  tuomov
+  * trunk: changeset 1392
+  Minor glitch introduced by previous changes fixed.
+
+2004-03-14 18:59 UTC  tuomov
+  * trunk: changeset 1391
+  Multiplexers now save layer2 objects in layout savefile.
+
+2004-03-13 21:37 UTC  tuomov
+  * trunk: changeset 1390
+  Removed superfluous region_same_rootwin check.
+
+2004-03-13 21:27 UTC  tuomov
+  * trunk: changeset 1389
+  Added tentative mod_sp scratchpad module.
+
+2004-03-13 18:47 UTC  tuomov
+  * trunk: changeset 1388
+  Region fitting, reparting and creating routines now receive a
+  bounds/exact specification along with geometry to allow managers to
+  specify whether managed regions should take full space available or
+  are they allowed to use only part of it.
+
+2004-03-12 19:06 UTC  tuomov
+  * trunk: changeset 1387
+  Added functions to hide/show mplex layer 2 objects.
+
+2004-03-12 17:58 UTC  tuomov
+  * trunk: changeset 1386
+  Region closing method unifications.
+
+2004-03-12 16:27 UTC  tuomov
+  * trunk: changeset 1385
+  Exported functions can now accept nil objects as parameters except for
+  the first parameter. (Most exported functions have just that first
+  object parameter.)
+
+2004-03-11 17:20 UTC  tuomov
+  * trunk: changeset 1384
+  Unnamed regions can now also bee seen on lists of regions.
+
+2004-03-10 21:12 UTC  tuomov
+  * trunk: changeset 1381
+  GCC linking order brain-damagedness workaround fixes.
+
+2004-03-10 21:03 UTC  tuomov
+  * trunk: changeset 1379
+  Slightly simpler and faster Lua object cache.
+
+2004-03-10 20:41 UTC  tuomov
+  * trunk: changeset 1378
+  - Moved X window code to xwindow.c.
+  
+  - Some other minor clean-up operations.
+
+2004-03-10 18:12 UTC  tuomov
+  * trunk: changeset 1376
+  Slightly reduce flicker with apps that update title too often by
+  removing a redundant change notify call.
+
+2004-03-10 18:01 UTC  tuomov
+  * trunk: changeset 1375
+  Handle bindmaps using red-black trees for simpler code (before
+  bindings were in arrays sorted and searched with qsort and bsearch).
+
+2004-03-10 16:49 UTC  tuomov
+  * trunk: changeset 1372
+  Also handle name allocation using red-black trees instead of Lua
+  tables now that such are available in libtu.
+
+2004-03-10 16:48 UTC  tuomov
+  * trunk: changeset 1371
+  Use red-black trees added to libtu instead of a Lua tables to store
+  split_of pointers of frames on WIonWS:s to avoid problems with object
+  cache clean-up on frame deletion.
+
+2004-03-10 10:47 UTC  tuomov
+  * trunk: changeset 1367
+  names.h wasn't being included.
+
+2004-03-07 21:46 UTC  tuomov
+  * trunk: changeset 1364
+  Bound backspace in menus to clear typeahead buffer.
+
+2004-03-07 21:45 UTC  tuomov
+  * trunk: changeset 1363
+  Moved strcasestr to libtu.
+
+2004-03-07 21:45 UTC  tuomov
+  * trunk: changeset 1361
+  Menus now support typeahead find.
+
+2004-03-07 17:37 UTC  tuomov
+  * trunk: changeset 1359
+  Removed ioncore_g.cwin_list.
+
+2004-03-07 17:29 UTC  tuomov
+  * trunk: changeset 1358
+  Removed completion functions from ioncore, moving them to querylib and
+  replacing with listing functions (ioncore.clientwin_list,
+  ioncore.region_list).
+
+2004-03-07 17:28 UTC  tuomov
+  * trunk: changeset 1357
+  Restored missing workspacelist menu entry.
+
+2004-03-07 15:35 UTC  tuomov
+  * trunk: changeset 1356
+  Fixed pmenu stacking.
+
+2004-03-07 11:13 UTC  tuomov
+  * trunk: changeset 1355
+  Added ioncore_post_layout_setup_hook.
+
+2004-03-07 10:41 UTC  tuomov
+  * trunk: changeset 1354
+  All modules are now 'mod_something'.
+
+2004-03-07 10:39 UTC  tuomov
+  * trunk: changeset 1353
+  Oops, some NULL checks were missing from new stacking code.
+
+2004-03-07 00:26 UTC  tuomov
+  * trunk: changeset 1352
+  Merged recent changes from the stable branch (WFloatWS.attach, better
+  gravity handling, goto_* routines return target).
+
+2004-03-07 00:24 UTC  tuomov
+  * trunk: changeset 1351
+  Brought PWM binding and menu configuration files up to date.
+
+2004-03-06 23:59 UTC  tuomov
+  * trunk: changeset 1350
+  - WMPlex now supports a full "second layer" of objects in addition to
+    a single input.
+  
+  - A lot of WIonFrame code was removed and moved to WFrame.
+  
+  - New rescue and initial management handling code.
+
+2004-03-06 23:53 UTC  tuomov
+  * trunk: changeset 1349
+  region_stack_above wasn't working.
+
+2004-03-06 23:12 UTC  tuomov
+  * trunk: changeset 1348
+  Stacking code relies on XQueryTree instead of maintaining our internal
+  state that might not agree with X.
+
+2004-03-06 23:08 UTC  tuomov
+  * trunk: changeset 1347
+  Fixed dock configuration file for new binding system.
+
+2004-02-18 03:00 UTC  tuomov
+  * trunk: changeset 1336
+  Merged tab drag&drop fixes and size guard additions from the stable
+  branch.
+
+2004-02-17 23:16 UTC  tuomov
+  * trunk: changeset 1330
+  Oops, some old class names were used in the merge.
+
+2004-02-17 18:40 UTC  tuomov
+  * trunk: changeset 1329
+  Merged fixes from the stable branch.
+
+2004-02-17 01:32 UTC  tuomov
+  * trunk: changeset 1326
+  Autosave disabling was missing.
+
+2004-02-16 23:27 UTC  tuomov
+  * trunk: changeset 1324
+  - Changed layout save mechanism to construct configuration tables
+    instead of directly writing data to files.
+  
+  - Some more mainloop changes.
+
+2004-02-16 20:33 UTC  tuomov
+  * trunk: changeset 1322
+  Documentation and documentation generation updates.
+
+2004-02-16 19:22 UTC  tuomov
+  * trunk: changeset 1321
+  Moved SunOS F11->SunF36, F12->SunF37 kludge to build time.
+
+2004-02-16 19:02 UTC  tuomov
+  * trunk: changeset 1320
+  Mainloop X flushing fix.
+
+2004-02-16 17:50 UTC  tuomov
+  * trunk: changeset 1317
+  Switched to using spaces only for indentation. Simple automatic "\t"
+  -> " " conversion; may need more tuning.
+
+2004-02-16 17:08 UTC  tuomov
+  * trunk: changeset 1315
+  Oops. errorlog code was still here.
+
+2004-02-16 17:05 UTC  tuomov
+  * trunk: changeset 1314
+  - Moved object system and other generic code to libtu.
+  
+  - Changed the mainloop to only handle file descriptors with the X
+    connection just a case among other inputfd:s.
+
+2004-02-16 04:42 UTC  tuomov
+  * trunk: changeset 1311
+  Moved stuff to the C side.
+
+2004-02-16 04:29 UTC  tuomov
+  * trunk: changeset 1310
+  Some minor fixes to stuff broken by namespace change.
+
+2004-02-16 04:14 UTC  tuomov
+  * trunk: changeset 1309
+  classes.h was missing from previous commmit.
+
+2004-02-16 04:13 UTC  tuomov
+  * trunk: changeset 1308
+  - Major namespace clean-up, both C and Lua-side.
+  
+  - Yet another binding configuration scheme.
+
+2004-02-15 19:06 UTC  tuomov
+  * trunk: changeset 1300
+  Added function to return line editor "mark".
+
+2004-02-14 23:31 UTC  tuomov
+  * trunk: changeset 1299
+  - Removed gr_get_brush_values and the possibility obtain brush
+    parameters before creating a window.
+  
+  - Added "above" and "below" as supported directions with some ionws
+    functions.
+
+2004-02-14 22:11 UTC  tuomov
+  * trunk: changeset 1298
+  Merged floatframe tab toggle and man page fixes from the stable
+  branch.
+
+2004-02-14 22:11 UTC  tuomov
+  * trunk: changeset 1297
+  Removed delib.lua and moved the stuff on the C side.
+
+2004-02-12 12:40 UTC  tuomov
+  * trunk: changeset 1294
+  Added manual page caching setup help in README.
+
+2004-02-12 11:42 UTC  tuomov
+  * trunk: changeset 1293
+  Removed superfluous navigation functions from WIonWS.
+
+2004-02-11 21:12 UTC  tuomov
+  * trunk: changeset 1292
+  Lua interface improvements.
+
+2004-02-11 09:56 UTC  tuomov
+  * trunk: changeset 1290
+  - Portability fixes merged from stable branch.
+  
+  - Fixed ion-completeman to use = insteaf == with 'test'.
+  
+  - Minor binding changes.
+
+2004-02-11 03:02 UTC  tuomov
+  * trunk: changeset 1288
+  Slightly better pipe stall check.
+
+2004-02-11 02:48 UTC  tuomov
+  * trunk: changeset 1287
+  Added line counter reset.
+
+2004-02-11 02:45 UTC  tuomov
+  * trunk: changeset 1286
+  Querylib tries to waste a little less memory when reading completions
+  from a pipe.
+
+2004-02-11 01:50 UTC  tuomov
+  * trunk: changeset 1285
+  Makefile sed fix.
+
+2004-02-11 01:41 UTC  tuomov
+  * trunk: changeset 1284
+  Merged fixes from the "stable" Ion2 branch.
+
+2004-02-11 01:28 UTC  tuomov
+  * trunk: changeset 1282
+  Forgot to upgrade querylib to new script parameters.
+
+2004-02-11 01:25 UTC  tuomov
+  * trunk: changeset 1281
+  Man-page completion is now implemented with the external script 'ion-
+  completeman'. The script supports cacheng names of known manual pages
+  (as a cronjob) for much faster completion. It can also use the
+  'manpath' program in addition to the MANPATH or ION_MC_MANPATH
+  environment variables to figure out the paths containing man pages.
+
+2004-02-11 00:59 UTC  tuomov
+  * trunk: changeset 1280
+  Completion display wasn't being refreshed, if its size didn't change
+  between completions.
+
+2004-02-10 23:12 UTC  tuomov
+  * trunk: changeset 1277
+  - New binding configuration scheme based on strings instead of direct
+    passing of Lua functions.
+  
+  - Some WMPlex managed object indexing function changes.
+
+2004-02-10 19:47 UTC  tuomov
+  * trunk: changeset 1276
+  Move/resize mode is common to all types of frames.
+
+2004-02-10 19:35 UTC  tuomov
+  * trunk: changeset 1274
+  There were still some WGENFRAME macros.
+
+2004-02-10 18:29 UTC  tuomov
+  * trunk: changeset 1273
+  Changed version and API version.
+
+2004-02-10 10:42 UTC  tuomov
+  * trunk: changeset 1271
+  File name changes and a couple of fixes to the WGenFrame -> WFrame
+  change.
+
+2004-02-10 10:36 UTC  tuomov
+  * trunk: changeset 1270
+  Renamed WGenFrame to WFrame.
+
+2004-02-10 10:35 UTC  tuomov
+  * trunk: changeset 1269
+  Changed default installation path to /usr/local/ion-3 and user
+  configuration file path to ~/.ion3.
+
+2004-02-07 01:56 UTC  tuomov
+  * trunk: changeset 1256
+  Brought WClientWin.get_ident documentation up-to-date.
+
+2004-02-05 22:09 UTC  tuomov
+  * trunk: changeset 1252
+  Implemented a kludge to track "awaiting focus" state. This will allow,
+  for example, windows changing to full screen mode immediately after
+  mapping (instead of doing it right and setting the properties before
+  mapping) to be switched to.
+
+2004-02-05 16:30 UTC  tuomov
+  * trunk: changeset 1250
+  Added extra include for dock.
+
+2004-02-05 16:29 UTC  tuomov
+  * trunk: changeset 1249
+  WIonWS.newframe wasn't adjusting sizes of existing frames nicely.
+
+2004-02-05 09:10 UTC  tuomov
+  * trunk: changeset 1248
+  Removed 'const' from 'spec' entry.
+
+2004-02-03 18:45 UTC  tuomov
+  * trunk: changeset 1246
+  - Minor memory leak removed: drawing engine colour group name.
+  
+  - Some extra safety checks added in line editor.
+
+2004-02-02 20:10 UTC  tuomov
+  * trunk: changeset 1245
+  Changed the autoconf script not to use \" that not all shells
+  apparently support.
+
+2004-02-01 20:58 UTC  tuomov
+  * trunk: changeset 1244
+  Fixed line breaking in really narrow listings.
+
+2004-02-01 11:54 UTC  tuomov
+  * trunk: changeset 1243
+  Man page fixes.
+
+2004-01-30 15:34 UTC  tuomov
+  * trunk: changeset 1241
+  - Unblock signals at startup as GDM in its great wisdom initially
+    blocks the window manager from receiving SIGCHLD resulting in
+    zombies.
+  
+  - More changes to (floatws) stacking policy.
+
+2004-01-29 20:07 UTC  tuomov
+  * trunk: changeset 1240
+  - Transient size calculation fixed.
+  
+  - Creating frame for a transient on a floatws will raise the frame
+    containing the transient_for window.
+  
+  - Added notes on Lua 5.0 in README.
+
+2004-01-28 16:02 UTC  tuomov
+  * trunk: changeset 1238
+  - Minor man page improvements.
+  
+  - Typo fix in system.mk.
+
+2004-01-28 06:41 UTC  tuomov
+  * trunk: changeset 1237
+  Changed libtool minimum version requirement back to safe 1.4.3.
+
+2004-01-27 22:38 UTC  tuomov
+  * trunk: changeset 1236
+  Documentation comment for ioncore_userdir was broken.
+
+2004-01-27 18:10 UTC  tuomov
+  * trunk: changeset 1233
+  Ooops, testing changes were left in development install path.
+
+2004-01-27 18:09 UTC  tuomov
+  * trunk: changeset 1232
+  Removed superfluous call to wedln_draw_completions.
+
+2004-01-27 13:49 UTC  tuomov
+  * trunk: changeset 1229
+  Install documentation and manual pages under $PREFIX/share as per the
+  FHS.
+
+2004-01-26 14:49 UTC  tuomov
+  * trunk: changeset 1228
+  - Added autoconf script support for -DCF_NO_MB_SUPPORT
+  
+  - Remove system-ac.mk in 'make realclean'.
+
+2004-01-26 14:48 UTC  tuomov
+  * trunk: changeset 1227
+  Fixed an 'err' variable to be local.
+
+2004-01-25 23:35 UTC  tuomov
+  * trunk: changeset 1226
+  Trap signals at an earlier stage so that processes forked at startup
+  don't become temporarily defunct.
+
+2004-01-25 18:19 UTC  tuomov
+  * trunk: changeset 1224
+  There was still a reference to ~/.ion/
+
+2004-01-25 18:12 UTC  tuomov
+  * trunk: changeset 1223
+  Class lookup is no longer case-insensitive. (There's no need for that
+  since the workspace creation query was changed.)
+
+2004-01-25 18:05 UTC  tuomov
+  * trunk: changeset 1221
+  Typo and spelling fixes.
+
+2004-01-25 11:07 UTC  tuomov
+  * trunk: changeset 1219
+  Some XX_SOURCE flag changes.
+
+2004-01-24 21:54 UTC  tuomov
+  * trunk: changeset 1218
+  Renames.
+
+2004-01-24 21:49 UTC  tuomov
+  * trunk: changeset 1217
+  - Added dummy multibyte/widechar routines for retarded platforms
+    without even such dummy support.
+  
+  - Some Cygwin note modifications.
+
+2004-01-24 20:28 UTC  tuomov
+  * trunk: changeset 1216
+  Some extra safety checks added.
+
+2004-01-22 21:25 UTC  tuomov
+  * trunk: changeset 1214
+  Fixed LaTeX in dock documentation comments.
+
+2004-01-21 19:32 UTC  tuomov
+  * trunk: changeset 1212
+  Some more polish on floatws focus policy.
+
+2004-01-21 17:32 UTC  tuomov
+  * trunk: changeset 1211
+  - Moved session directory creation to a proper place, so that the
+    directory will exist for the style menu save feature.
+  
+  - Fill _NET_SUPPORTED root window property with the supported features
+    (_NET_WM_FULLSCREEN, _NET_WM_NAME, _NET_VIRTUAL_ROOTS).
+
+2004-01-20 21:11 UTC  tuomov
+  * trunk: changeset 1209
+  Module preloading support was broken.
+
+2004-01-20 19:50 UTC  tuomov
+  * trunk: changeset 1208
+  Message cleaned up.
+
+2004-01-20 18:37 UTC  tuomov
+  * trunk: changeset 1207
+  Binaries are now again stripped when installed.
+
+2004-01-20 18:35 UTC  tuomov
+  * trunk: changeset 1206
+  Fixed a man page typo.
+
+2004-01-19 18:26 UTC  tuomov
+  * trunk: changeset 1205
+  Fixed tab drop on a floatws.
+
+2004-01-19 16:03 UTC  tuomov
+  * trunk: changeset 1204
+  Old submaps were ignored when new entries were added.
+
+2004-01-19 12:22 UTC  tuomov
+  * trunk: changeset 1203
+  Fixed libtool version requirements.
+
+2004-01-19 12:22 UTC  tuomov
+  * trunk: changeset 1202
+  - Autoconf script Xinerama check fixed.
+  
+  - README.autoconf simplified.
+
+2004-01-19 12:20 UTC  tuomov
+  * trunk: changeset 1201
+  Fixed references to ioncore-*.lua.
+
+2004-01-16 18:39 UTC  tuomov
+  * trunk: changeset 1200
+  There were still some references to frame-tab (how tab-frame), causing
+  initial frame sizes on floatws:s to be miscalculated.
+
+2004-01-16 18:38 UTC  tuomov
+  * trunk: changeset 1199
+  Oops. Removed some important code earlier.
+
+2004-01-16 16:58 UTC  tuomov
+  * trunk: changeset 1198
+  - Minor client window management set up fixes.
+  
+  - Pass _ION_KLUDGES (XA_STRING) property in WClientwin.get_ident as
+    .kludges.
+
+2004-01-16 15:35 UTC  tuomov
+  * trunk: changeset 1197
+  - Fixed goto_previous after switching between windows in the same
+    frame with a query.
+  
+  - The dock no longer gets focused when the pointer enters it.
+
+2004-01-15 18:34 UTC  tuomov
+  * trunk: changeset 1196
+  Changed order of mplex old/new selected object map/unmap to reduce
+  flicker.
+
+2004-01-15 16:10 UTC  tuomov
+  * trunk: changeset 1195
+  - A couple manual page and README bugs were fixed.
+  
+  - Removed Mod1+K T rebinding in ionframe_bindings to
+    toggle_transients_pos (it is supposed to be clear_tags).
+
+2004-01-14 22:43 UTC  tuomov
+  * trunk: changeset 1194
+  Style selection saving wasn't working.
+
+2004-01-14 19:37 UTC  tuomov
+  * trunk: changeset 1191
+  Removed extra character in comment header.
+
+2004-01-14 18:03 UTC  tuomov
+  * trunk: changeset 1190
+  - Floating workspace now give focus to highest-stacked frame when the
+    active one is destroyed.
+  
+  - Mod1+N/P were bound to raise/lower on floating workspaces.
+
+2004-01-14 16:55 UTC  tuomov
+  * trunk: changeset 1189
+  Changed version string to ION_VERSION.
+
+2004-01-14 16:39 UTC  tuomov
+  * trunk: changeset 1188
+  - Added optional autoconf script.
+  
+  - Fixed etc/Makefile.
+
+2004-01-13 23:52 UTC  tuomov
+  * trunk: changeset 1187
+  - Renamed do_move_pointer_to do_warp and made redefinable through the
+    do_warp_alt hook.
+  
+  - Fixed reparent_region->region_reparent.
+  
+  - Added some missing includes.
+
+2004-01-13 16:31 UTC  tuomov
+  * trunk: changeset 1186
+  Frame maximize takes active client window size limits into account.
+
+2004-01-13 16:12 UTC  tuomov
+  * trunk: changeset 1185
+  Keyboard move/resize warps (if enabled) pointer back to manipulated
+  frame after finishing.
+
+2004-01-08 23:16 UTC  tuomov
+  * trunk: changeset 1184
+  Fixed some typos in documentation.
+
+2004-01-08 22:05 UTC  tuomov
+  * trunk: changeset 1182
+  Changed winprop selection by title to longest match.
+
+2004-01-08 21:51 UTC  tuomov
+  * trunk: changeset 1181
+  - Support changes in X keyboard map.
+  
+  - Some keymap deinitialisation fixes and bindmap clean-up.
+
+2004-01-06 19:48 UTC  tuomov
+  * trunk: changeset 1180
+  Minor man page fixes.
+
+2004-01-06 12:30 UTC  tuomov
+  * trunk: changeset 1179
+  Removed introduction of a non-existent function.
+
+2004-01-03 00:27 UTC  tuomov
+  * trunk: changeset 1178
+  Updated most of the copyright notices to 2004.
+
+2003-12-29 20:26 UTC  tuomov
+  * trunk: changeset 1177
+  - stylemenu properly saves draw.lua if querylib is not installed.
+  
+  - Separate binding configuration file for PWM.
+
+2003-12-29 20:14 UTC  tuomov
+  * trunk: changeset 1176
+  - Some default menu configuration changes. PWM no longer uses the same
+    menu configuration file.
+  
+  - Removed client window check code mismatch complaint on startup.
+  
+  - PWM startup errorlog was incorrectly referring to Ion.
+
+2003-12-29 20:01 UTC  tuomov
+  * trunk: changeset 1175
+  Removed debug message.
+
+2003-12-29 18:42 UTC  tuomov
+  * trunk: changeset 1174
+  Workspace query asks for type of workspace when creating new.
+
+2003-12-29 15:53 UTC  tuomov
+  * trunk: changeset 1173
+  Previous frame geometry (for maximize and shade) is saved in the
+  workspaces save file.
+
+2003-12-23 21:14 UTC  tuomov
+  * trunk: changeset 1172
+  Added a note on the dock module authors to README.
+
+2003-12-23 21:13 UTC  tuomov
+  * trunk: changeset 1171
+  - Added dock to list of modules to build.
+  
+  - Updated to newer version of dock.
+  
+  - Dock position and growing direction was changed to old PWM defaults.
+  
+  - PWM stock configuration files load the dock module by default.
+
+2003-12-23 20:55 UTC  tuomov
+  * trunk: changeset 1169
+  - Included the dock module with Ion.
+  
+  - Fixed dock_get_tile_size and modified dock module Makefile for
+    inclusion in Ion.
+
+2003-12-22 20:45 UTC  tuomov
+  * trunk: changeset 1100
+  Use also C99_SOURCE where XOPEN_SOURCE is used.
+
+2003-12-22 20:11 UTC  tuomov
+  * trunk: changeset 1099
+  include stdio.h
+
+2003-12-20 13:08 UTC  tuomov
+  * trunk: changeset 1098
+  XFreeGC(xor_gc) on deinit.
+
+2003-12-18 18:08 UTC  tuomov
+  * trunk: changeset 1097
+  Focusing on return from submenu had been broken by previous changes.
+
+2003-12-18 17:57 UTC  tuomov
+  * trunk: changeset 1096
+  look-clean "bigmenu" font changed.
+
+2003-12-18 17:52 UTC  tuomov
+  * trunk: changeset 1095
+  WScreens no longer accept tab drag&drop.
+
+2003-12-18 17:48 UTC  tuomov
+  * trunk: changeset 1094
+  Better error message in querylib.query_workspace.
+
+2003-12-18 17:45 UTC  tuomov
+  * trunk: changeset 1093
+  Message line counting fix.
+
+2003-12-18 17:42 UTC  tuomov
+  * trunk: changeset 1092
+  Class lookup is now case-insensitive.
+
+2003-12-18 17:28 UTC  tuomov
+  * trunk: changeset 1091
+  Added some object destroy safety checks.
+
+2003-12-18 17:22 UTC  tuomov
+  * trunk: changeset 1090
+  Fixed winprop lookup when window name is nil.
+
+2003-12-18 12:08 UTC  tuomov
+  * trunk: changeset 1089
+  Use lua-config50 in Debian settings.
+
+2003-12-16 20:54 UTC  tuomov
+  * trunk: changeset 1088
+  Re-created the file.
+
+2003-12-16 20:52 UTC  tuomov
+  * trunk: changeset 1087
+  Less verbosity.
+
+2003-12-16 18:29 UTC  tuomov
+  * trunk: changeset 1086
+  Another old reference.
+
+2003-12-14 11:52 UTC  tuomov
+  * trunk: changeset 1085
+  Fixed (?) grab release focusing when warping is disabled.
+
+2003-12-11 22:16 UTC  tuomov
+  * trunk: changeset 1084
+  Man pages still had references to old user configuration file
+  directory.
+
+2003-12-11 21:31 UTC  tuomov
+  * trunk: changeset 1083
+  Fixed querylib.query_man default value.
+
+2003-12-11 18:03 UTC  tuomov
+  * trunk: changeset 1082
+  Fixed .welcome_msg_displayed permissions.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20031211
+
+2003-12-11 17:11 UTC  tuomov
+  * trunk: changeset 1081
+  Fixed a potential segfault when window disappears while being set up
+  to be managed.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20031210
+
+2003-12-10 09:17 UTC  tuomov
+  * trunk: changeset 1080
+  Updates.
+
+2003-12-09 20:39 UTC  tuomov
+  * trunk: changeset 1079
+  Removed compat.lua
+
+2003-12-09 19:48 UTC  tuomov
+  * trunk: changeset 1078
+  Removed mention of development branch.
+
+2003-12-09 19:48 UTC  tuomov
+  * trunk: changeset 1077
+  Minor size calculation fix.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20031209
+
+2003-12-09 19:28 UTC  tuomov
+  * trunk: changeset 1076
+  Module compatibility is checked against ION_API_VERSION (2) instead of
+  ION_VERSION.
+
+2003-12-09 19:13 UTC  tuomov
+  * trunk: changeset 1075
+  Refer to ~/.ion2/.
+
+2003-12-09 19:07 UTC  tuomov
+  * trunk: changeset 1074
+  Removed configuration file conversion utilities; people following the
+  development branch should already have converted their files.
+
+2003-12-09 19:05 UTC  tuomov
+  * trunk: changeset 1073
+  - Removed '-devel' from path names.
+  
+  - Changed user configuration file directories to ~/.ion2/ and
+    ~/.pwm2/.
+  
+  - Renamed all ioncore*.lua configuration files to ion*.lua.
+  
+  - The main configuration file for Ion is ion.lua and for PWM pwm.lua.
+
+2003-12-09 00:32 UTC  tuomov
+  * trunk: changeset 1072
+  Changed LUA_PATH to LUA_DIR to avoid conflicts with Lua itself.
+
+2003-12-07 22:05 UTC  tuomov
+  * trunk: changeset 1071
+  Oops.
+
+2003-12-07 22:01 UTC  tuomov
+  * trunk: changeset 1070
+  Use string.shell_safe to encapsulate file names.
+
+2003-12-07 22:01 UTC  tuomov
+  * trunk: changeset 1069
+  Added string.shell_safe function.
+
+2003-12-06 22:20 UTC  tuomov
+  * trunk: changeset 1068
+  Insert new bindmaps at beginning of region bindmap list.
+
+2003-12-06 20:43 UTC  tuomov
+  * trunk: changeset 1067
+  Initial workspace creation code checks default_ws_type.
+
+2003-12-06 20:36 UTC  tuomov
+  * trunk: changeset 1066
+  Changed ion-completefile path.
+
+2003-12-06 17:45 UTC  tuomov
+  * trunk: changeset 1065
+  Added WClientWin.toggle_transients_pos export and transients_at_top
+  winprop.
+
+2003-12-06 17:44 UTC  tuomov
+  * trunk: changeset 1064
+  Bound WClientWin.toggle_transients_pos to DEFAULT_MOD+K T.
+
+2003-12-06 17:43 UTC  tuomov
+  * trunk: changeset 1063
+  Require Lua 5.0.1.
+
+2003-12-06 17:43 UTC  tuomov
+  * trunk: changeset 1062
+  Removed extra whitespace at ends of some lines.
+
+2003-12-06 16:17 UTC  tuomov
+  * trunk: changeset 1061
+  Fixed previous change.
+
+2003-12-06 12:25 UTC  tuomov
+  * trunk: changeset 1060
+  Some transient initial size calculation changes.
+
+2003-12-06 11:44 UTC  tuomov
+  * trunk: changeset 1059
+  Changed export names and added one more.
+
+2003-12-06 11:42 UTC  tuomov
+  * trunk: changeset 1058
+  Added "stylemenu" that displays all look-*.lua files on search path.
+
+2003-12-06 11:42 UTC  tuomov
+  * trunk: changeset 1057
+  Added some search path exports.
+
+2003-12-06 11:15 UTC  tuomov
+  * trunk: changeset 1056
+  ion-completefile directory changed and extended to support multiple
+  paths.
+
+2003-12-05 00:46 UTC  tuomov
+  * trunk: changeset 1055
+  - Submenus can now be generated by functions when needed.
+  
+  - Added "windowlist" and "workspacelist" default menus.
+
+2003-12-04 22:10 UTC  tuomov
+  * trunk: changeset 1054
+  Removed a tab.
+
+2003-12-04 21:56 UTC  tuomov
+  * trunk: changeset 1053
+  In-mplex menus that do not fully fit within the space available now
+  scroll the visible entries.
+
+2003-12-03 23:43 UTC  tuomov
+  * trunk: changeset 1052
+  aboutmsg was missing.
+
+2003-12-03 23:43 UTC  tuomov
+  * trunk: changeset 1051
+  Comment change.
+
+2003-12-03 22:27 UTC  tuomov
+  * trunk: changeset 1050
+  Closing a frame initially contaning transient window will switch focus
+  to the frame that contained the transient_for window.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20031203
+
+2003-12-03 20:33 UTC  tuomov
+  * trunk: changeset 1049
+  Removed mentions of ion-devel in the release tarball; default
+  installation target is now /usr/local with configuration files in
+  /usr/local/etc/ion and ~/.ion. (CVS snapshots will still use
+  /usr/local/ion-devel.)
+
+2003-12-03 20:31 UTC  tuomov
+  * trunk: changeset 1048
+  Just ditch old $ETCDIR/draw.lua.
+
+2003-12-03 20:29 UTC  tuomov
+  * trunk: changeset 1047
+  Added pwm.1 man page.
+
+2003-12-03 20:22 UTC  tuomov
+  * trunk: changeset 1046
+  Use PWM_ETCDIR if set.
+
+2003-12-03 20:14 UTC  tuomov
+  * trunk: changeset 1045
+  Load the query module.
+
+2003-12-03 20:06 UTC  tuomov
+  * trunk: changeset 1044
+  - The ion and pwm scripts were removed and are instead now binaries
+    linked against ioncore.a.
+  
+  - The 'ion' binary uses ~/.ion (no longer ~/.ion-devel) for user's
+    configuration files and the 'pwm' binary uses ~/.pwm for user's
+    configuration files.
+
+2003-12-03 17:59 UTC  tuomov
+  * trunk: changeset 1043
+  Script search path changes; calling file's directly is now only
+  considered if explicitly indicated in the file name. (So now normally
+  "included" files in user's directories will always be preferred over
+  those in system directories.)
+
+2003-12-03 11:37 UTC  tuomov
+  * trunk: changeset 1042
+  etc/ make install changes; ioncore.lua is now always overwritten and
+  draw.lua link restored to a working file.
+
+2003-12-02 23:23 UTC  tuomov
+  * trunk: changeset 1041
+  Display a welcome message and manual page to new users.
+
+2003-12-02 23:22 UTC  tuomov
+  * trunk: changeset 1040
+  - Display a welcome message and manual page to new users.
+  
+  - Some path setting function changes.
+
+2003-12-02 23:20 UTC  tuomov
+  * trunk: changeset 1039
+  - Renamed manual page to ion.1.
+  
+  - Added ion.1.in.txt
+
+2003-12-02 23:11 UTC  tuomov
+  * trunk: changeset 1038
+  Minor modification.
+
+2003-12-02 23:01 UTC  tuomov
+  * trunk: changeset 1037
+  Removed mention of "messages" to reduce chance of confusion.
+
+2003-12-02 18:31 UTC  tuomov
+  * trunk: changeset 1036
+  Added a "basic concepts" section in the manual page.
+
+2003-12-02 18:05 UTC  tuomov
+  * trunk: changeset 1035
+  Fixed message display line copying bug.
+
+2003-12-02 13:37 UTC  tuomov
+  * trunk: changeset 1034
+  Fixed -onescreen to -oneroot.
+
+2003-12-01 20:18 UTC  tuomov
+  * trunk: changeset 1033
+  Button6 and Button7 might be recognised.
+
+2003-12-01 20:18 UTC  tuomov
+  * trunk: changeset 1032
+  Ignore unknown bits in keypress event state.
+
+2003-12-01 19:09 UTC  tuomov
+  * trunk: changeset 1031
+  Minor clean-up.
+
+2003-12-01 18:23 UTC  tuomov
+  * trunk: changeset 1030
+  The line editor and region_set_name strip white space from the
+  beginning and end of strings.
+
+2003-12-01 01:34 UTC  tuomov
+  * trunk: changeset 1029
+  Some more acceleration changes.
+
+2003-11-30 16:51 UTC  tuomov
+  * trunk: changeset 1028
+  - Implemented move/resize edge snapping.
+  
+  - Smoother square root based move/resize acceleration curve.
+
+2003-11-30 16:49 UTC  tuomov
+  * trunk: changeset 1027
+  Uncommented CF_EDGE_RESISTANCE.
+
+2003-11-30 00:51 UTC  tuomov
+  * trunk: changeset 1026
+  Save sticky state in session savefile.
+
+2003-11-30 00:48 UTC  tuomov
+  * trunk: changeset 1025
+  Implemented sticky frames on WFloatWS:s.
+
+2003-11-30 00:34 UTC  tuomov
+  * trunk: changeset 1024
+  Implemented WWatch-node based WObjList and changed tag list to that
+  instead of wasting space in WRegion.
+
+2003-11-29 22:46 UTC  tuomov
+  * trunk: changeset 1023
+  Only redraw menu entries that need redrawing when selected entry is
+  changed.
+
+2003-11-29 16:27 UTC  tuomov
+  * trunk: changeset 1022
+  Some focus handling changes, fixes and clean-up.
+
+2003-11-29 15:35 UTC  tuomov
+  * trunk: changeset 1021
+  "Close" key and menu binding changes.
+
+2003-11-29 15:34 UTC  tuomov
+  * trunk: changeset 1019
+  Minor clean-up.
+
+2003-11-29 14:36 UTC  tuomov
+  * trunk: changeset 1018
+  Fixed selection request when multibyte support is not enabled.
+
+2003-11-29 14:31 UTC  tuomov
+  * trunk: changeset 1017
+  Small documentation fix.
+
+2003-11-29 14:29 UTC  tuomov
+  * trunk: changeset 1016
+  Do not display a query_message error when failing to open
+  ~/.ssh/known_hosts.
+
+2003-11-29 14:21 UTC  tuomov
+  * trunk: changeset 1015
+  Improved documentation.
+
+2003-11-29 14:21 UTC  tuomov
+  * trunk: changeset 1014
+  Fixed underscore and tiled escapes.
+
+2003-11-27 22:26 UTC  tuomov
+  * trunk: changeset 1013
+  Added note on known_hosts in documentation comment.
+
+2003-11-27 22:26 UTC  tuomov
+  * trunk: changeset 1012
+  Keyboard is no longer grabbed in submap mode if normal bindmap is not
+  grabed.
+
+2003-11-27 19:58 UTC  tuomov
+  * trunk: changeset 1011
+  Oops.
+
+2003-11-27 16:18 UTC  tuomov
+  * trunk: changeset 1010
+  Minor clean-up operations.
+
+2003-11-27 15:56 UTC  tuomov
+  * trunk: changeset 1009
+  Added some stacking setup and mplex attach sanity checks.
+
+2003-11-27 15:42 UTC  tuomov
+  * trunk: changeset 1008
+  Added clear_tags ctxmenu entry.
+
+2003-11-27 15:38 UTC  tuomov
+  * trunk: changeset 1007
+  Region rescue code was cleaned up a little and the code is no longer
+  called from deinit handlers.
+
+2003-11-25 20:37 UTC  tuomov
+  * trunk: changeset 1006
+  Added WIonWS.farthest, next_to and goto_dir exports.
+
+2003-11-25 19:44 UTC  tuomov
+  * trunk: changeset 1005
+  CF_STR_EMPTY added.
+
+2003-11-24 16:52 UTC  tuomov
+  * trunk: changeset 1004
+  Oops, 16pt helvetica may not be available. Use 17pt, 18pt is bold even
+  as medium.
+
+2003-11-24 16:32 UTC  tuomov
+  * trunk: changeset 1003
+  Fonts are chosen more consistently in provided look-* style files.
+
+2003-11-24 16:07 UTC  tuomov
+  * trunk: changeset 1002
+  look-cleanios was missing from Makefile.
+
+2003-11-24 16:02 UTC  tuomov
+  * trunk: changeset 1001
+  Removed "for" from get_savefile_for, read_config_for, etc.
+
+2003-11-24 16:02 UTC  tuomov
+  * trunk: changeset 1000
+  - Added 'jumpto' winprop and 'clientwin_added' hook.
+  
+  - 'switchto' is passed as parameter to fullscreen check functions.
+  
+  - Exported 'get_savefile'.
+  
+  - Removed "for" from get_savefile_for, read_config_for, etc.
+  
+  - Moved winprop lookup to the proper point in code after having again
+    moved it where name is not available.
+
+2003-11-24 01:14 UTC  tuomov
+  * trunk: changeset 999
+  Fixed -libdir to -moduledir.
+
+2003-11-23 19:45 UTC  tuomov
+  * trunk: changeset 998
+  Changes in stipple pattern usage in drawing a dragged tab.
+
+2003-11-23 18:37 UTC  tuomov
+  * trunk: changeset 997
+  Added WIonWS.resize_tree function that can be used to resize whole
+  subtrees of the workspace split hierarchy.
+
+2003-11-23 13:19 UTC  tuomov
+  * trunk: changeset 996
+  Removed the styles.
+
+2003-11-23 13:18 UTC  tuomov
+  * trunk: changeset 995
+  Added new style look-cleanios.
+
+2003-11-23 04:54 UTC  tuomov
+  * trunk: changeset 994
+  'pmenu' entry index could be too big by one. Fixed.
+
+2003-11-23 04:53 UTC  tuomov
+  * trunk: changeset 993
+  Look configuration files properly ported the drawing engine model and
+  proper menu styles added to those styles that need it.
+
+2003-11-23 03:12 UTC  tuomov
+  * trunk: changeset 992
+  String shortening rules can now be specified always-on so that the
+  rule is applied even when no shortening is necessary.
+
+2003-11-23 03:10 UTC  tuomov
+  * trunk: changeset 991
+  The same region is not to be passed twice as a _key_ binding handler
+  argument; two regions are only passed when e.g. a workspace is
+  selecting keys on a frame.
+
+2003-11-23 03:08 UTC  tuomov
+  * trunk: changeset 990
+  Changed make_mplex_sub_or_self_fn behaviour.
+
+2003-11-23 02:45 UTC  tuomov
+  * trunk: changeset 989
+  Completable hosts for SSH query are parsed from ~/.ssh/known_hosts
+  (instead of requiring the user to a list of them).
+
+2003-11-22 15:44 UTC  tuomov
+  * trunk: changeset 988
+  Added Button3->rootmenu binding.
+
+2003-11-21 16:45 UTC  tuomov
+  * trunk: changeset 987
+  Added mention of modulelist.mk.
+
+2003-11-21 16:40 UTC  tuomov
+  * trunk: changeset 986
+  Modified the default drawing engine to be more usable as a basis for
+  alternative drawing engines that need per-window data.
+
+2003-11-21 15:09 UTC  tuomov
+  * trunk: changeset 985
+  Title shortening rules are now used always even if the title would fit
+  in the available space without modification.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20031121
+
+2003-11-21 14:43 UTC  tuomov
+  * trunk: changeset 984
+  clean-up variable rename.
+
+2003-11-21 09:11 UTC  tuomov
+  * trunk: changeset 983
+  Invalid multibyte strings could cause the line editor to go to an
+  endless loop.
+
+2003-11-21 09:00 UTC  tuomov
+  * trunk: changeset 982
+  Listing (completions, messages) line breaking wasn't yet multibyte-
+  aware.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20031119
+
+2003-11-21 04:10 UTC  tuomov
+  * trunk: changeset 981
+  New string shortening code could segfault. Fixed it.
+
+2003-11-19 21:39 UTC  tuomov
+  * trunk: changeset 980
+  Replaced UTF-8 support with (almost) general multibyte encoding
+  support.
+
+2003-11-17 21:03 UTC  tuomov
+  * trunk: changeset 978
+  mplex_fit didn't update region geometry data.
+
+2003-11-17 21:03 UTC  tuomov
+  * trunk: changeset 977
+  A minor clean-up operation.
+
+2003-11-16 21:40 UTC  tuomov
+  * trunk: changeset 976
+  Updated documentation.
+
+2003-11-16 17:16 UTC  tuomov
+  * trunk: changeset 975
+  Load querylib.
+
+2003-11-16 17:05 UTC  tuomov
+  * trunk: changeset 974
+  make_mplex_sub_or_self_fn was broken.
+
+2003-11-16 17:05 UTC  tuomov
+  * trunk: changeset 973
+  A variable name was wrong.
+
+2003-11-16 16:51 UTC  tuomov
+  * trunk: changeset 972
+  Added support for _NET_WM_STATE_FULLSCREEN request.
+
+2003-11-16 15:19 UTC  tuomov
+  * trunk: changeset 971
+  Reset font to NULL on deinit.
+
+2003-11-16 14:58 UTC  tuomov
+  * trunk: changeset 970
+  Bindings can now be removed on the fly by passing nil as callback
+  function.
+
+2003-11-16 13:56 UTC  tuomov
+  * trunk: changeset 969
+  Complain of invalid binding table.
+
+2003-11-16 13:48 UTC  tuomov
+  * trunk: changeset 968
+  Updated the Makefile.
+
+2003-11-16 13:48 UTC  tuomov
+  * trunk: changeset 967
+  Renamed ioncore-mplexfns.lua to ioncorelib-mplexfns.lua and moved all
+  mplex-related functions from ioncorelib.lua there.
+
+2003-11-16 01:44 UTC  tuomov
+  * trunk: changeset 966
+  Fixed GC font setup.
+
+2003-11-16 01:43 UTC  tuomov
+  * trunk: changeset 965
+  Don't compiled some code if CF_UTF8 is not set.
+
+2003-11-16 00:29 UTC  tuomov
+  * trunk: changeset 964
+  Updated the man page.
+
+2003-11-15 23:51 UTC  tuomov
+  * trunk: changeset 963
+  UTF8 is not used if locale is C/POSIX (or broken) even if Ion was
+  compiled with UTF8 support.
+
+2003-11-15 23:49 UTC  tuomov
+  * trunk: changeset 962
+  Added font caching/ref.counting so that font loading would take little
+  less time when pattern guessing is required to fullfill locale's
+  requirements.
+
+2003-11-14 20:35 UTC  tuomov
+  * trunk: changeset 961
+  Changed the order in which brushes are freed.
+
+2003-11-14 20:31 UTC  tuomov
+  * trunk: changeset 960
+  The original source of the the fontset code was discovered.
+
+2003-11-14 16:00 UTC  tuomov
+  * trunk: changeset 959
+  The functions to create binding wrappers to operate on WMPlexs'
+  children were extended and given better names, although the old ones
+  are still available.
+
+2003-11-14 15:56 UTC  tuomov
+  * trunk: changeset 958
+  nothing special.
+
+2003-11-14 15:49 UTC  tuomov
+  * trunk: changeset 957
+  Removed fallback font check.
+
+2003-11-14 15:40 UTC  tuomov
+  * trunk: changeset 956
+  Added FontSet guessing code that should be able to figure out enough
+  fonts for XCreateFontSet to fullfill locales' requirements so font
+  loading shouldn't fail so often when UTF8 support is enabled.
+
+2003-11-14 00:42 UTC  tuomov
+  * trunk: changeset 955
+  At least load "de" engine if no engine has been loaded while executing
+  draw.lua.
+
+2003-11-13 22:37 UTC  tuomov
+  * trunk: changeset 954
+  Added some _LOADED checks.
+
+2003-11-13 18:45 UTC  tuomov
+  * trunk: changeset 953
+  Lua files in share/ (but not etc/) are precompiled.
+
+2003-11-13 18:26 UTC  tuomov
+  * trunk: changeset 952
+  'ioncore-startup.lua' kludge was removed and replaced by the loading
+  of 'ioncore-efbb.lua' in case of empty bindmaps from the C side.
+
+2003-11-13 18:04 UTC  tuomov
+  * trunk: changeset 951
+  Changes in default configuration files to make them more legible and
+  to add menu configuration.
+
+2003-11-13 18:03 UTC  tuomov
+  * trunk: changeset 950
+  Call XClearWindow when toggling tab to avoid clutter.
+
+2003-11-13 18:02 UTC  tuomov
+  * trunk: changeset 949
+  Added ioncore_aboutmsg export.
+
+2003-11-13 12:07 UTC  tuomov
+  * trunk: changeset 948
+  Added disabled XMMS kludge.
+
+2003-11-12 17:50 UTC  tuomov
+  * trunk: changeset 947
+  Extension definition changes.
+
+2003-11-12 17:50 UTC  tuomov
+  * trunk: changeset 946
+  - C-side module configuration file loading function also look for
+    compiled .lc files.
+  
+  - Lua-side include() automatically also looks for .lc and .lua files
+    if neither extension nor path component is given.
+
+2003-11-10 17:58 UTC  tuomov
+  * trunk: changeset 945
+  Made find_suitable_screen global.
+
+2003-11-09 16:09 UTC  tuomov
+  * trunk: changeset 944
+  Added WRegion.is_active and is_mapped exports.
+
+2003-11-06 17:50 UTC  tuomov
+  * trunk: changeset 943
+  Some mplex_managed_changed calls were wrong, causing trouble with
+  transparency.
+
+2003-11-05 22:45 UTC  tuomov
+  * trunk: changeset 942
+  include fixes.
+
+2003-11-05 22:45 UTC  tuomov
+  * trunk: changeset 941
+  Stack management fixes.
+
+2003-11-05 12:23 UTC  tuomov
+  * trunk: changeset 940
+  Removed mentions of xft.
+
+2003-11-04 20:27 UTC  tuomov
+  * trunk: changeset 939
+  Fixed a potential segfault point.
+
+2003-11-04 20:26 UTC  tuomov
+  * trunk: changeset 938
+  Quick&dirty stacking fix.
+
+2003-10-30 10:00 UTC  tuomov
+  * trunk: changeset 937
+  Removed CURRENT_FILE kludge for include handling; use the Lua debug
+  interface instead to get the file the calling function was defined in.
+
+2003-10-30 08:34 UTC  tuomov
+  * trunk: changeset 936
+  Some changes in default menus.
+
+2003-10-30 08:34 UTC  tuomov
+  * trunk: changeset 935
+  libtool 1.4.3->1.4.x.
+
+2003-10-28 13:00 UTC  tuomov
+  * trunk: changeset 934
+  submenus weren't being handled correctly.
+
+2003-10-27 16:59 UTC  tuomov
+  * trunk: changeset 933
+  about_msg should be local.
+
+2003-10-27 16:58 UTC  tuomov
+  * trunk: changeset 932
+  Changed styles are automatically translated (and complained of).
+
+2003-10-27 12:26 UTC  tuomov
+  * trunk: changeset 931
+  Fixed cursor and selection substyles.
+
+2003-10-27 12:05 UTC  tuomov
+  * trunk: changeset 930
+  Updated the script to use the new style names.
+
+2003-10-27 12:05 UTC  tuomov
+  * trunk: changeset 929
+  Some style name changes.
+
+2003-10-27 12:05 UTC  tuomov
+  * trunk: changeset 928
+  Changes in menus.
+
+2003-10-27 12:05 UTC  tuomov
+  * trunk: changeset 927
+  Updated the look-* files.
+
+2003-10-27 12:05 UTC  tuomov
+  * trunk: changeset 926
+  look-cleanviolet is now the default style.
+
+2003-10-25 20:23 UTC  tuomov
+  * trunk: changeset 925
+  Check fallback font at startup with XCreateFontSet instead of
+  XLoadQueryFont.
+
+2003-10-24 17:09 UTC  tuomov
+  * trunk: changeset 924
+  Moved list of modules to build from system.mk to modulelist.mk
+
+2003-10-04 14:52 UTC  tuomov
+  * trunk: changeset 923
+  va_list usage changes due to problems on some architechtures.
+
+2003-10-04 12:41 UTC  tuomov
+  * trunk: changeset 922
+  Added genframe_(in)activated hooks.
+
+2003-09-15 18:15 UTC  tuomov
+  * trunk: changeset 921
+  Some target and fullscreen winprop handling fixes.
+
+2003-09-15 18:15 UTC  tuomov
+  * trunk: changeset 920
+  Some fixes to client window rescuing.
+
+2003-09-15 18:15 UTC  tuomov
+  * trunk: changeset 919
+  Some fixes to client window rescuing.
+
+2003-09-15 18:14 UTC  tuomov
+  * trunk: changeset 918
+  Fixed a FALSE to NULL.
+
+2003-09-09 20:07 UTC  tuomov
+  * trunk: changeset 917
+  Fixed an event-missing problem.
+
+2003-09-04 08:22 UTC  tuomov
+  * trunk: changeset 916
+  Menu configuration changes.
+
+2003-09-04 08:22 UTC  tuomov
+  * trunk: changeset 915
+  Ungrab keyboard before calling dispatch_binding (except with submap).
+
+2003-08-31 16:15 UTC  tuomov
+  * trunk: changeset 914
+  Added a note on .xinitrc/.xsession to README.
+
+2003-08-28 20:59 UTC  tuomov
+  * trunk: changeset 913
+  Some border drawing fixes.
+
+2003-08-25 15:25 UTC  tuomov
+  * trunk: changeset 912
+  restart_other_wm fixes.
+
+2003-08-25 15:05 UTC  tuomov
+  * trunk: changeset 911
+  Better grabbing checks.
+
+2003-08-25 15:04 UTC  tuomov
+  * trunk: changeset 910
+  XSync() before fork() -- xlock should work a bit better now.
+
+2003-08-24 14:51 UTC  tuomov
+  * trunk: changeset 909
+  Menu config changes; use submenus.
+
+2003-08-24 14:51 UTC  tuomov
+  * trunk: changeset 908
+  Removed empty_tab area.
+
+2003-08-24 14:51 UTC  tuomov
+  * trunk: changeset 907
+  Moved minof/maxof elsewhere, removed empty_tab.
+
+2003-08-24 14:50 UTC  tuomov
+  * trunk: changeset 906
+  A little clean-up.
+
+2003-08-24 14:50 UTC  tuomov
+  * trunk: changeset 905
+  Added ''pmenu'' off-screen scrolling support as in PWM.
+
+2003-08-24 14:49 UTC  tuomov
+  * trunk: changeset 904
+  Some WTimer enhancements.
+
+2003-08-22 16:37 UTC  tuomov
+  * trunk: changeset 903
+  Fixed a crash problem in case of invalid use of WGenFrame.p_tabdrag.
+
+2003-08-21 18:29 UTC  tuomov
+  * trunk: changeset 902
+  - More changes to pointer handling code for better menu support.
+  
+  - genframe_bindings knows of frame areas.
+
+2003-08-21 18:28 UTC  tuomov
+  * trunk: changeset 901
+  Enhanced menu support.
+
+2003-08-21 18:24 UTC  tuomov
+  * trunk: changeset 900
+  Added a brush that handles submenu entries in menus specially.
+
+2003-08-21 18:22 UTC  tuomov
+  * trunk: changeset 899
+  'make depend' fixed to work with modules.
+
+2003-08-20 16:46 UTC  tuomov
+  * trunk: changeset 898
+  Don't strip white space from query results.
+
+2003-08-19 12:54 UTC  tuomov
+  * trunk: changeset 897
+  Fixed tab drop on floatws:s (was putting clients to full screen mode
+  on occasion).
+
+2003-08-19 09:11 UTC  tuomov
+  * trunk: changeset 896
+  uname SunOS check complaint fix with newer gnu utils.
+
+2003-08-15 17:14 UTC  tuomov
+  * trunk: changeset 895
+  Some changes to pointer event handling code so menus can set handlers
+  immediately on button press.
+
+2003-08-15 13:59 UTC  tuomov
+  * trunk: changeset 894
+  Some files were missing.. again.. *sigh*
+
+2003-08-14 18:16 UTC  tuomov
+  * trunk: changeset 893
+  Added very preliminary and primitive menu support (only query-like
+  embedded-in-an-mplex menus).
+
+2003-08-14 18:08 UTC  tuomov
+  * trunk: changeset 892
+  Added ioncore_version export.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030814
+
+2003-08-14 18:07 UTC  tuomov
+  * trunk: changeset 891
+  Some file location reorganisation.
+
+2003-08-14 18:07 UTC  tuomov
+  * trunk: changeset 890
+  Some file location reorganisation and header cleanup.
+
+2003-08-13 13:09 UTC  tuomov
+  * trunk: changeset 889
+  Call region_notify_change when name is unset.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030811-1
+
+2003-08-13 13:08 UTC  tuomov
+  * trunk: changeset 888
+  Dragging a tab with no title could cause a segfault.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030811
+
+2003-08-12 00:06 UTC  tuomov
+  * trunk: changeset 887
+  Changed location for X shape extension shape.h include.
+
+2003-08-11 23:05 UTC  tuomov
+  * trunk: changeset 886
+  There was an indexing bug in rootwin.c that caused stack corruption
+  and crash when Xinerama was enabled.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030810-1
+
+2003-08-11 05:21 UTC  tuomov
+  * trunk: changeset 885
+  Winprop lookup had been broken.
+
+2003-08-10 17:02 UTC  tuomov
+  * trunk: changeset 884
+  make_exec_fn was broken when the parameter to created function was a
+  frame.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030810
+
+2003-08-10 16:16 UTC  tuomov
+  * trunk: changeset 883
+  Removed duplicate variable.
+
+2003-08-10 16:15 UTC  tuomov
+  * trunk: changeset 882
+  Changes in tab reordering were not being updated to screen correctly.
+
+2003-08-10 13:46 UTC  tuomov
+  * trunk: changeset 881
+  Added some more functions to manipulate object indices within a
+  WMPlex.
+
+2003-08-08 15:40 UTC  tuomov
+  * trunk: changeset 880
+  close_sub_or_self is now WRegion.close_sub_or_self.
+
+2003-08-07 21:32 UTC  tuomov
+  * trunk: changeset 879
+  Modified the winprop patch to use the numerical zero field instead of
+  " ! " to store winprops with no name regexp specified.
+
+2003-08-07 20:49 UTC  tuomov
+  * trunk: changeset 878
+  Applied a patch to add title matching field 'name' (Lua regexp) in
+  winprops.
+
+2003-08-06 18:23 UTC  tuomov
+  * trunk: changeset 877
+  Fixed a typo in UTF8 font code.
+
+2003-08-06 18:21 UTC  tuomov
+  * trunk: changeset 876
+  Changed where get_winprop is called to a later time where the client
+  window's name has been set.
+
+2003-08-06 14:58 UTC  tuomov
+  * trunk: changeset 875
+  foreground colour was wrong.
+
+2003-08-06 14:35 UTC  tuomov
+  * trunk: changeset 874
+  Moved lookconv.lua to utils.
+
+2003-08-06 14:35 UTC  tuomov
+  * trunk: changeset 873
+  - Wrote a conversion script from older .lua workspaces savefiles.
+  
+  - Moved lookconv.lua to utils.
+
+2003-08-06 13:57 UTC  tuomov
+  * trunk: changeset 872
+  Updated style configuration files to draw tabs with the 'activity'
+  attribute set in white on red. 'lookconv.lua' uses these same colours
+  in converted files.
+
+2003-08-06 13:55 UTC  tuomov
+  * trunk: changeset 871
+  Implemented 'activity' display cue that is set when a newly created
+  client window is not displayed or when the urgency hint is set by the
+  client application.
+
+2003-08-06 13:46 UTC  tuomov
+  * trunk: changeset 870
+  Backslash wasn't being escaped in saved strings.
+
+2003-08-05 14:13 UTC  tuomov
+  * trunk: changeset 869
+  Improved handling of substyles with based_on.
+
+2003-08-05 14:09 UTC  tuomov
+  * trunk: changeset 868
+  Removed WRegion/mgr_data; use object-indexed tables instead.
+
+2003-08-05 14:07 UTC  tuomov
+  * trunk: changeset 867
+  Use a table instead of WRegion/mgr_data to store managed regions'
+  immediate splits.
+
+2003-08-05 13:30 UTC  tuomov
+  * trunk: changeset 866
+  Added functions to index tables by any supported type.
+
+2003-08-04 22:15 UTC  tuomov
+  * trunk: changeset 865
+  Wrong file name info in file.
+
+2003-08-04 21:30 UTC  tuomov
+  * trunk: changeset 864
+  Some table accessing routine unifications.
+
+2003-08-04 16:59 UTC  tuomov
+  * trunk: changeset 863
+  ionframe_bar_inside_border instead of bar_inside_frame
+
+2003-08-04 16:54 UTC  tuomov
+  * trunk: changeset 862
+  Enchanced WRegion.close documentation.
+
+2003-08-04 14:58 UTC  tuomov
+  * trunk: changeset 861
+  mdblclick(Button1) bound to shade on ionframes.
+
+2003-08-04 14:56 UTC  tuomov
+  * trunk: changeset 860
+  Ionframe shade fixed.
+
+2003-08-04 10:03 UTC  tuomov
+  * trunk: changeset 859
+  Added table entry type info to a documentation comment for
+  WMPlex.attach_*.
+
+2003-08-03 20:35 UTC  tuomov
+  * trunk: changeset 858
+  Added options to set floatframe bar width limits and tab bar location
+  for ionframes(fields floatframe_bar_max_w_q/floatframe_tab_min_w and
+  ionframe_bar_inside_borderin frame style config).
+
+2003-08-01 16:49 UTC  tuomov
+  * trunk: changeset 857
+  preliminary code to handle changing transient_for property... special
+  interface needed to handle floatws:s.
+
+2003-08-01 16:32 UTC  tuomov
+  * trunk: changeset 856
+  Added some line editing and history exports.
+
+2003-08-01 05:52 UTC  tuomov
+  * trunk: changeset 855
+  Added ignore_cfgrq winprop.
+
+2003-07-31 16:52 UTC  tuomov
+  * trunk: changeset 854
+  added spacing = 1 to input style.
+
+2003-07-31 16:52 UTC  tuomov
+  * trunk: changeset 853
+  listing fitting fixed.
+
+2003-07-31 14:16 UTC  tuomov
+  * trunk: changeset 852
+  Fixed some problems with selections in queries and added
+  wedln_clear_mark function.
+
+2003-07-31 14:16 UTC  tuomov
+  * trunk: changeset 851
+  Bound Control+G to end a query and Control+K G to clear mark in
+  queries.
+
+2003-07-31 13:55 UTC  tuomov
+  * trunk: changeset 850
+  Added some more querylib documentation and simplified a couple
+  functions.
+
+2003-07-31 13:49 UTC  tuomov
+  * trunk: changeset 849
+  Added gr_refresh and renamed reread_draw_config to gr_read_config.
+
+2003-07-31 13:49 UTC  tuomov
+  * trunk: changeset 848
+  Style configuration files now call de_reset and gr_refresh.
+
+2003-07-30 21:56 UTC  tuomov
+  * trunk: changeset 847
+  look-cleanviolet.lua was missing from set of installed files.
+
+2003-07-30 19:37 UTC  tuomov
+  * trunk: changeset 846
+  Added some ugly workarounds to some random Lua API functions not
+  checking the types of objects on stack.
+
+2003-07-30 19:33 UTC  tuomov
+  * trunk: changeset 845
+  Implemented keyboard resize acceleration.
+
+2003-07-30 15:44 UTC  tuomov
+  * trunk: changeset 844
+  Updated manpage with -sessionname.
+
+2003-07-30 15:27 UTC  tuomov
+  * trunk: changeset 843
+  WRectangle passing as argument changed to const WRectangle* mostly.
+
+2003-07-30 14:23 UTC  tuomov
+  * trunk: changeset 842
+  Added "deinit" hook.
+
+2003-07-30 14:21 UTC  tuomov
+  * trunk: changeset 841
+  Updated read_config_for_args call.
+
+2003-07-30 14:17 UTC  tuomov
+  * trunk: changeset 840
+  - Removed support for screen-specific configuration files.
+  
+  - Savefiles now go in ~/.ion-devel/sessionname (instead of ~/.ion-
+    devel/saves) where sessionname is 'default-session-displayname' by
+    default (with colon in display name converted to a dash) but can be
+    changed from the command line.
+  
+  - Workspaces are now saved in a single 'workspaces.lua' file in
+    session directory and screens are also now set with a single
+    'initialise_screen_id' call.
+
+2003-07-30 14:14 UTC  tuomov
+  * trunk: changeset 839
+  Removed CF_SECOND_RATE_OS_FS comment as the setting is no longer
+  necessary.
+
+2003-07-28 18:01 UTC  tuomov
+  * trunk: changeset 838
+  Added --mode=link to libtool parameters.
+
+2003-07-28 13:43 UTC  tuomov
+  * trunk: changeset 837
+  - Fixed a bug in the new split resizing algorithm that caused
+    bottom/right regions in a split to be misplaced if both bottom and
+    top or left and right border were moved of another region.
+  
+  - Added some comments documenting the resizing algorithm.
+
+2003-07-27 18:49 UTC  tuomov
+  * trunk: changeset 836
+  Added some libtool options to system.mk
+
+2003-07-27 18:42 UTC  tuomov
+  * trunk: changeset 835
+  Fixed a problem with the split resizing algorithm and keyboard resize
+  by almost completely rewriting the algorithm.
+
+2003-07-27 18:41 UTC  tuomov
+  * trunk: changeset 834
+  Removed a comment.
+
+2003-07-27 00:15 UTC  tuomov
+  * trunk: changeset 833
+  Added keys for manipulating tags and attaching tagged objects (Mod+T:
+  toggle tag, Mod+K T: clear tags, Mod+K A: attach tagged).
+
+2003-07-27 00:00 UTC  tuomov
+  * trunk: changeset 832
+  Fixed transient_mode = "current" in full-screen mode.
+
+2003-07-26 23:44 UTC  tuomov
+  * trunk: changeset 831
+  Added some documentation comments.
+
+2003-07-26 23:07 UTC  tuomov
+  * trunk: changeset 830
+  Transparency mode switching fixed.
+
+2003-07-26 22:58 UTC  tuomov
+  * trunk: changeset 829
+  Some cleanup.
+
+2003-07-26 22:37 UTC  tuomov
+  * trunk: changeset 828
+  Some colour group initialisation changes.
+
+2003-07-25 20:31 UTC  tuomov
+  * trunk: changeset 827
+  CVS barfed again and refused to remove these files.
+
+2003-07-25 20:24 UTC  tuomov
+  * trunk: changeset 826
+  Ion now supports drawing engines as loadable modules!
+
+2003-07-25 20:13 UTC  tuomov
+  * trunk: changeset 825
+  Colour scheme configuration files were converted to the new format.
+
+2003-07-25 19:34 UTC  tuomov
+  * trunk: changeset 824
+  WRootWin changed to be of type WWindow (instead of just WRegion).
+
+2003-07-23 13:43 UTC  tuomov
+  * trunk: changeset 823
+  Fixed tag pixmap and textbox spacings.
+
+2003-07-22 18:09 UTC  tuomov
+  * trunk: changeset 822
+  Drawing engine Lua library was added.
+
+2003-07-22 18:06 UTC  tuomov
+  * trunk: changeset 821
+  Drawing engine interface code was added although not yet used.
+
+2003-07-22 18:04 UTC  tuomov
+  * trunk: changeset 820
+  The prospective default drawing engine was added.
+
+2003-07-22 16:57 UTC  tuomov
+  * trunk: changeset 819
+  Added a script (etc/lookconv.lua) to convert the old .lua colour
+  schemes to the upcoming format.
+
+2003-07-21 05:25 UTC  tuomov
+  * trunk: changeset 818
+  Added a note on *BSD libtool version brain-damagedness.
+
+2003-07-19 10:50 UTC  tuomov
+  * trunk: changeset 817
+  submap had been broken by previous simplifications.
+
+2003-07-19 10:50 UTC  tuomov
+  * trunk: changeset 816
+  Some va_list passing changes.
+
+2003-07-18 15:30 UTC  tuomov
+  * trunk: changeset 815
+  The fact that there is a configuration manual was made much better
+  visible in the README.
+
+2003-07-16 14:16 UTC  tuomov
+  * trunk: changeset 814
+  Nested workspace handling had been broken by the add managed/attach
+  interface change. Fixed.
+
+2003-07-13 21:14 UTC  tuomov
+  * trunk: changeset 813
+  The function 'exec_on_wm_display' was renamed 'exec'.
+
+2003-07-13 21:10 UTC  tuomov
+  * trunk: changeset 812
+  Lua function binding for classes put into class-tables thus making the
+  bindings more object-oriented in spirit.
+
+2003-07-10 22:56 UTC  tuomov
+  * trunk: changeset 811
+  AnyModifier handling fixes; Xlib was crashing when lock ignore kludge
+  was applied on AnyModifier grabs.
+
+2003-07-08 18:48 UTC  tuomov
+  * trunk: changeset 810
+  Some documentation updates.
+
+2003-07-08 18:22 UTC  tuomov
+  * trunk: changeset 809
+  removed deprecated definitions
+
+2003-07-08 18:08 UTC  tuomov
+  * trunk: changeset 808
+  The 'have region A manage region B' interface was heavily revamped.
+  The generic region_manage(_new) functions are gone and only WMPlexes
+  now export the equivalent interfaces mplex_attach(_new). Only client
+  windows' are now set up with a generic interface that is a lot simpler
+  than the old.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030628
+
+2003-07-04 14:04 UTC  tuomov
+  * trunk: changeset 807
+  Set default_ws_type in pwm-ioncore-example.lua.
+
+2003-06-28 17:40 UTC  tuomov
+  * trunk: changeset 806
+  Fixed a crash when the same key was bound as both submap and normal
+  action on an object.
+
+2003-06-27 21:16 UTC  tuomov
+  * trunk: changeset 805
+  Added more obsolete functions.
+
+2003-06-27 21:15 UTC  tuomov
+  * trunk: changeset 804
+  Fixed some comments.
+
+2003-06-27 21:15 UTC  tuomov
+  * trunk: changeset 803
+  Implemented region_close on WFloatWS:s and renamed floatws_destroy to
+  floatws_relocate_and_close to be consistent with the naming of similar
+  functions on WIonFrames.
+
+2003-06-27 21:15 UTC  tuomov
+  * trunk: changeset 802
+  Fixed documentation in querylib.
+
+2003-06-27 18:55 UTC  tuomov
+  * trunk: changeset 801
+  The CF_LT_DL_ANCIENT option was removed as much more extra code would
+  have been needed to support ancient versions of libtool. Version 1.4.3
+  or newer is now required.
+
+2003-06-27 18:47 UTC  tuomov
+  * trunk: changeset 800
+  The '-or' flag to find apparently was a GNU extension; '-o' seems to
+  work.
+
+2003-06-27 18:40 UTC  tuomov
+  * trunk: changeset 799
+  - The Mod1+F1 Ion man page display binding was broken.
+  
+  - The function exec_in_frame was renamed exec_in.
+
+2003-06-27 14:05 UTC  tuomov
+  * trunk: changeset 798
+  Manual page fixed.
+
+2003-06-25 23:37 UTC  tuomov
+  * trunk: changeset 797
+  FloatWS:s don't warp to new frames.
+
+2003-06-25 23:14 UTC  tuomov
+  * trunk: changeset 796
+  XOR resize rubberand had been broken by previous changes.
+
+2003-06-25 13:37 UTC  tuomov
+  * trunk: changeset 795
+  The default bindings for the F-keys now use the modifier from
+  SECOND_MOD (defaults to the empty string i.e. no modifier).
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030623
+
+2003-06-25 05:36 UTC  tuomov
+  * trunk: changeset 794
+  Tabs' grab area extended to include frame's top border when the
+  frame's y coordinate is zero.
+
+2003-06-23 23:47 UTC  tuomov
+  * trunk: changeset 793
+  A minor fix
+
+2003-06-23 13:14 UTC  tuomov
+  * trunk: changeset 792
+  Resize display was showing incorrect values for keyboard resize.
+
+2003-06-23 13:13 UTC  tuomov
+  * trunk: changeset 791
+  A bug in grab handler calling code could crash Ion when leaving
+  keyboard resize mode manually.
+
+2003-06-23 01:20 UTC  tuomov
+  * trunk: changeset 790
+  Fixed pointer warping on screen change.
+
+2003-06-22 11:12 UTC  tuomov
+  * trunk: changeset 789
+  Removed false comments.
+
+2003-06-21 20:07 UTC  tuomov
+  * trunk: changeset 788
+  Screen lookup had been broken for windows that are not properly on any
+  screen.
+
+2003-06-21 20:06 UTC  tuomov
+  * trunk: changeset 787
+  As the number of dynamic functions has been getting bigger, the
+  functions are now sorted on first use and then binary-searched instead
+  of naive linear searching.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030620
+
+2003-06-21 12:50 UTC  tuomov
+  * trunk: changeset 786
+  Return from full screen mode to floatws had been broken.
+
+2003-06-21 10:56 UTC  tuomov
+  * trunk: changeset 785
+  Client window last height request bookkeeping code had been lost when
+  configure request policy was changed. This caused transient sizes to
+  be calculated incorrectly.
+
+2003-06-20 09:19 UTC  tuomov
+  * trunk: changeset 784
+  Nothing!
+
+2003-06-20 09:19 UTC  tuomov
+  * trunk: changeset 783
+  Constrain frames to their workspaces.
+
+2003-06-20 08:52 UTC  tuomov
+  * trunk: changeset 782
+  Oops.
+
+2003-06-20 08:50 UTC  tuomov
+  * trunk: changeset 781
+  Some initial focus policy changes.
+
+2003-06-20 08:50 UTC  tuomov
+  * trunk: changeset 780
+  The split functions now return the newly created frame.
+
+2003-06-20 07:40 UTC  tuomov
+  * trunk: changeset 779
+  Added a comment.
+
+2003-06-19 22:06 UTC  tuomov
+  * trunk: changeset 778
+  Put new client windows in innermost/deepest nested active workspace,
+  if any, instead of limiting to those attached directly to screens.
+
+2003-06-19 22:05 UTC  tuomov
+  * trunk: changeset 777
+  Split recalculation on WS resize fixed and made proportional.
+
+2003-06-19 20:51 UTC  tuomov
+  * trunk: changeset 776
+  Changes to client window move request handling on WFloatWS:s; while
+  the current behaviour may not be correct, a greater number of apps'
+  requests should work almost as expected even in nested workspaces.
+
+2003-06-19 18:12 UTC  tuomov
+  * trunk: changeset 775
+  New windows weren't being placed on correct screen in Xinerama mode.
+
+2003-06-19 18:11 UTC  tuomov
+  * trunk: changeset 774
+  Removed unnecessary flags from create/init.
+
+2003-06-19 16:16 UTC  tuomov
+  * trunk: changeset 773
+  Tab-bar state wasn't being applied correctly from savefiles.
+
+2003-06-18 18:49 UTC  tuomov
+  * trunk: changeset 772
+  Some transient size management changes.
+
+2003-06-18 18:14 UTC  tuomov
+  * trunk: changeset 771
+  - Added min_size winprop.
+  
+  - Transients weren't properly unattached when the managing client
+    window died. This could cause segfault e.g. at exit.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030617
+
+2003-06-18 13:31 UTC  tuomov
+  * trunk: changeset 770
+  Some title shortening rules were defined in wrong order in ioncore-
+  example.lua and the rules App: doc -> doc... and App: doc<n> ->
+  doc...<n> were missing.
+
+2003-06-18 12:12 UTC  tuomov
+  * trunk: changeset 769
+  Use libtool for make clean.
+
+2003-06-17 20:29 UTC  tuomov
+  * trunk: changeset 768
+  Added Galeon find dialog randomly missing transient_for hint
+  workaround winprop to kludges.lua.
+
+2003-06-17 20:27 UTC  tuomov
+  * trunk: changeset 767
+  Fixed the workaround.
+
+2003-06-17 20:09 UTC  tuomov
+  * trunk: changeset 766
+  The function close_sub_or_self was added.
+
+2003-06-17 20:09 UTC  tuomov
+  * trunk: changeset 765
+  The function region_get_active_leaf was removed and the export
+  region_active_sub added.
+
+2003-06-17 20:08 UTC  tuomov
+  * trunk: changeset 764
+  The Mod1+C binding had been broken by the removal if
+  make_active_leaf_fn: The function close_sub_or_self (not same as
+  make_active_leaf_fn(region_close)) was added and the key bound to this
+  function.
+
+2003-06-17 19:45 UTC  tuomov
+  * trunk: changeset 763
+  Added workaround to the XFree86 textprop bug that caused starting
+  Opera to crash Ion when UTF8 support was enabled.
+
+2003-06-17 19:32 UTC  tuomov
+  * trunk: changeset 762
+  Mention XFree86 version number in the bug comment.
+
+2003-06-17 19:28 UTC  tuomov
+  * trunk: changeset 761
+  Mention WinXP in cygwin comments.
+
+2003-06-17 16:57 UTC  tuomov
+  * trunk: changeset 760
+  Added commented-out options to system.mk for compiling Ion with the
+  Debian Lua package as the paths and file names differ greatly from the
+  official distribution.
+
+2003-06-17 15:56 UTC  tuomov
+  * trunk: changeset 759
+  Added a note to system.mk about the Xlib UTF8 bug.
+
+2003-06-17 09:16 UTC  tuomov
+  * trunk: changeset 758
+  More Cygwin notes.
+
+2003-06-17 08:26 UTC  tuomov
+  * trunk: changeset 757
+  Added some Cygwin installation notes to system.mk.
+
+2003-06-17 08:17 UTC  tuomov
+  * trunk: changeset 756
+  Scripts in share/ still weren't being built.
+
+2003-06-15 18:22 UTC  tuomov
+  * trunk: changeset 755
+  Winprop lookup was cluttering globals.
+
+2003-06-15 10:40 UTC  tuomov
+  * trunk: changeset 754
+  Some more stack trace cleanup.
+
+2003-06-15 10:24 UTC  tuomov
+  * trunk: changeset 753
+  Stack traces are ordered better when there are nested calls with
+  errors and calls to C functions for which no name is known are
+  compressed in the output.
+
+2003-06-15 09:09 UTC  tuomov
+  * trunk: changeset 752
+  make_active_leaf_fn in compat.lua was broken.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030614
+
+2003-06-14 20:28 UTC  tuomov
+  * trunk: changeset 751
+  Some WMPlex/query focusing fixes.
+
+2003-06-14 20:12 UTC  tuomov
+  * trunk: changeset 750
+  The man page query completor also looks for symbolic links.
+
+2003-06-14 15:10 UTC  tuomov
+  * trunk: changeset 749
+  Added more documentation to *frame_begin_resize.
+
+2003-06-14 12:05 UTC  tuomov
+  * trunk: changeset 748
+  Focus was being incorrectly changed when an inactive full screen
+  client window was destroyed.
+
+2003-06-14 11:49 UTC  tuomov
+  * trunk: changeset 747
+  Some fullscreen focus kludges
+
+2003-06-13 23:01 UTC  tuomov
+  * trunk: changeset 746
+  Use luaL_(g|s)etn instead of looking upthe functions from the table
+  library.
+
+2003-06-13 19:36 UTC  tuomov
+  * trunk: changeset 745
+  - Some changes to grab and drag handler setup functions.
+  
+  - Escape key was harcoded to kill any active grab (so that
+    misconfigured resize modes and such can't do harm).
+
+2003-06-12 22:53 UTC  tuomov
+  * trunk: changeset 744
+  Fixed doc comment
+
+2003-06-12 18:48 UTC  tuomov
+  * trunk: changeset 743
+  Some generic resize code simplifications; timers moved to keyboard
+  resize mode code(s).
+
+2003-06-12 18:28 UTC  tuomov
+  * trunk: changeset 742
+  Resize timeout timer was being set up only after some resize action
+  had been performed, not when entering the mode.
+
+2003-06-12 18:14 UTC  tuomov
+  * trunk: changeset 741
+  More error-tolerant configuration reading setup: 1. If Lua fails to
+  load a configuration file (syntax error etc.), the next on path is
+  tried instead of failing. (If the configuration file dies in an error
+  during execution, the next one, however, is not attempted.) 2. The
+  main configuration file (ioncore.lua) is executed through ioncore-
+  startup.lua. This file redefines some of the binding setup functions
+  to monitor binding settings. If some of the binding groups have been
+  left empty by failing configuration files, minimal bindings are
+  created.
+
+2003-06-12 18:04 UTC  tuomov
+  * trunk: changeset 740
+  Some error display format changes.
+
+2003-06-12 18:04 UTC  tuomov
+  * trunk: changeset 739
+  Some error displays were broken.
+
+2003-06-12 18:03 UTC  tuomov
+  * trunk: changeset 738
+  The binding setup functions no return FALSE if no bindings were
+  defined at all.
+
+2003-06-12 18:01 UTC  tuomov
+  * trunk: changeset 737
+  Better commented configuration files.
+
+2003-06-12 15:41 UTC  tuomov
+  * trunk: changeset 736
+  Comments in system.mk were out of place
+
+2003-06-12 00:37 UTC  tuomov
+  * trunk: changeset 735
+  Error log should be somewhat easier to read now.
+
+2003-06-10 18:18 UTC  tuomov
+  * trunk: changeset 734
+  waitrelease fixed.
+
+2003-06-10 16:21 UTC  tuomov
+  * trunk: changeset 733
+  bindmaps.c and bindmaps.h were missing from previous commit.
+
+2003-06-10 16:17 UTC  tuomov
+  * trunk: changeset 732
+  - Binding configuration (hopefully) simplified: bindings previously
+    defined in common-frame-bindings.lua were moved to ioncore-
+    bindings.lua and functions for defining bindings common to all
+    WMPlexes and WGenFrames were added.
+  
+  - The confusing 'make_active_leaf_fn' was also removed (can still be
+    found in compat.lua) and instead client window bindings are defined
+    in mplex_bindings with the help of the perhaps a little less
+    confusing 'make_current_clientwin_fn'.
+
+2003-06-10 16:14 UTC  tuomov
+  * trunk: changeset 731
+  Binding configuration (hopefully) simplified: bindings previously
+  defined in common-frame-bindings.lua were moved to ioncore-
+  bindings.lua and functions for defining bindings common to all
+  WMPlexes and WGenFrames were added.
+
+2003-06-10 11:07 UTC  tuomov
+  * trunk: changeset 730
+  Manual page updates.
+
+2003-06-10 11:05 UTC  tuomov
+  * trunk: changeset 729
+  The functions extl_dofile/string' were removed and
+  extl_loadfile/string added.
+
+2003-06-09 22:24 UTC  tuomov
+  * trunk: changeset 728
+  Warping on workspace switch had been broken by addition of
+  multiplexes.
+
+2003-06-09 21:13 UTC  tuomov
+  * trunk: changeset 727
+  The Lua interfacing code now uses a unique (cached in a weak table)
+  WWatch for Ion's objects instead of creating a new userdata/watch
+  every time an object is passed to Lua. This allows using the objects
+  as indexes in tables.
+
+2003-06-09 20:28 UTC  tuomov
+  * trunk: changeset 726
+  Some client window initial focus policy unification.
+
+2003-06-09 16:14 UTC  tuomov
+  * trunk: changeset 725
+  Added some definitions in header
+
+2003-06-09 15:17 UTC  tuomov
+  * trunk: changeset 724
+  'QueryLib.query_workspace' new creates workspaces of the type set in
+  the variable 'default_ws_type' if no type is otherwise specified and
+  Mod1+F9 was bound to create workspaces of this type without asking for
+  a name. (The default name of default_ws_type<n> is used.)
+
+2003-06-09 15:14 UTC  tuomov
+  * trunk: changeset 723
+  WRegions (except WClientWins) are now given names of the form
+  'ClassName<n>' by default.
+
+2003-06-09 14:24 UTC  tuomov
+  * trunk: changeset 722
+  - Don't complain of disappeared windows at startup phase.
+  
+  - Some client window management setup cleanup.
+
+2003-06-08 19:06 UTC  tuomov
+  * trunk: changeset 721
+  Added 'warn' export.
+
+2003-06-08 18:22 UTC  tuomov
+  * trunk: changeset 720
+  Added -noxinerama command line option.
+
+2003-06-08 17:55 UTC  tuomov
+  * trunk: changeset 719
+  Some small changes.
+
+2003-06-08 17:38 UTC  tuomov
+  * trunk: changeset 718
+  Fixed some documentation comments.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030606
+
+2003-06-08 17:28 UTC  tuomov
+  * trunk: changeset 717
+  Added mplex_managed_count, mplex_managed_index and mplex_current_index
+  functions. The latter two are in ioncore-mplexfns.lua that must
+  specifically be loaded if the functions are needed.
+
+2003-06-08 12:44 UTC  tuomov
+  * trunk: changeset 716
+  Workspace swithing while dragging tabs had been broken by the mplex
+  change.
+
+2003-06-06 13:23 UTC  tuomov
+  * trunk: changeset 715
+  Changed comments.
+
+2003-06-06 13:13 UTC  tuomov
+  * trunk: changeset 714
+  Added 'screen_set_managed_offset' function that statusbars and such
+  should use to allocate space.
+
+2003-06-06 12:42 UTC  tuomov
+  * trunk: changeset 713
+  Some cleanup.
+
+2003-06-05 23:19 UTC  tuomov
+  * trunk: changeset 712
+  Don't execute 'arg' setup code when there are no parameters.
+
+2003-06-05 06:10 UTC  tuomov
+  * trunk: changeset 711
+  Makefile was missing share/ from SUBDIRS:
+
+2003-06-04 16:12 UTC  tuomov
+  * trunk: changeset 710
+  WScreen and WGenFrame now have a common WMPlex base class.
+
+2003-06-02 22:30 UTC  tuomov
+  * trunk: changeset 709
+  Ion-ssh and ion-man scripts were updated to use $SHAREDIR/ion-
+  runinxterm.
+
+2003-06-02 19:52 UTC  tuomov
+  * trunk: changeset 708
+  CF_NO_XINERAMA had been broken at some point.
+
+2003-06-02 17:50 UTC  tuomov
+  * trunk: changeset 707
+  Added -DCF_LTLD_ANCIENT kludge so that it might be possible to use
+  some systems' ancient libltdl.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030602-1
+
+2003-06-02 14:15 UTC  tuomov
+  * trunk: changeset 706
+  Remaining sprintf calls replaced with snprintf.
+
+2003-06-02 14:08 UTC  tuomov
+  * trunk: changeset 705
+  Most of module management code removed as libltdl can handle it.
+
+2003-06-02 10:34 UTC  tuomov
+  * trunk: changeset 704
+  Oops.
+
+2003-06-02 10:32 UTC  tuomov
+  * trunk: changeset 703
+  More ugly-fontixes
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030602
+
+2003-06-02 10:04 UTC  tuomov
+  * trunk: changeset 702
+  Changed $SHAREDIR before $ETCDIR on configuration file/script search
+  path because people weren't removing their old *lib.lua files.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030601
+
+2003-06-02 09:27 UTC  tuomov
+  * trunk: changeset 701
+  Ugly-font support was broken.
+
+2003-06-02 05:40 UTC  tuomov
+  * trunk: changeset 700
+  Some libltdl search path setting changes.
+
+2003-06-01 13:21 UTC  tuomov
+  * trunk: changeset 699
+  Some minor clarifications and fixes.
+
+2003-06-01 12:15 UTC  tuomov
+  * trunk: changeset 698
+  More fixes.
+
+2003-06-01 12:11 UTC  tuomov
+  * trunk: changeset 697
+  Another extl_l1_finalize fix.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030531
+
+2003-06-01 02:02 UTC  tuomov
+  * trunk: changeset 696
+  Fixed a memory leak.
+
+2003-06-01 01:44 UTC  tuomov
+  * trunk: changeset 695
+  An off-by-one error in extl_l1_finalize caused references to some Lua
+  tables (including large completions) never to be released.
+
+2003-05-31 15:33 UTC  tuomov
+  * trunk: changeset 694
+  Ion-runinxterm was not found.
+
+2003-05-31 15:04 UTC  tuomov
+  * trunk: changeset 693
+  License changed: LGPL.
+
+2003-05-31 14:49 UTC  tuomov
+  * trunk: changeset 692
+  The -libdir option aws changed to -moduledir.
+
+2003-05-31 14:48 UTC  tuomov
+  * trunk: changeset 691
+  Manual page updated.
+
+2003-05-31 13:20 UTC  tuomov
+  * trunk: changeset 690
+  Oops.
+
+2003-05-31 13:19 UTC  tuomov
+  * trunk: changeset 689
+  Added CF_ALWAYS_VIRTUAL_ROOT
+
+2003-05-31 13:11 UTC  tuomov
+  * trunk: changeset 688
+  Compatibility wrappers for old resize functions were added.
+
+2003-05-31 13:11 UTC  tuomov
+  * trunk: changeset 687
+  The functions *frame_do_resize were changed to receive four
+  parameters, one for each border/direction.
+
+2003-05-31 13:10 UTC  tuomov
+  * trunk: changeset 686
+  More changes in move/resize mode bindings to be more consistent and
+  predictable: Left/Right/Up/Down and F/B/P/N grow the frame in the
+  specific direction, Shift+keys shrink and in case of floating frames,
+  DEFAULT_MOD+keys move.
+
+2003-05-31 12:45 UTC  tuomov
+  * trunk: changeset 685
+  mkexports.pl => mkexports.lua
+
+2003-05-31 12:43 UTC  tuomov
+  * trunk: changeset 684
+  Transient reparent stack "fix".
+
+2003-05-31 12:35 UTC  tuomov
+  * trunk: changeset 683
+  Removed unused settings.
+
+2003-05-31 11:09 UTC  tuomov
+  * trunk: changeset 682
+  Some drawing fixes.
+
+2003-05-31 10:58 UTC  tuomov
+  * trunk: changeset 681
+  Some (part bad) name allocation fixes.
+
+2003-05-31 00:40 UTC  tuomov
+  * trunk: changeset 680
+  Some reordering of initilisation code.
+
+2003-05-30 21:17 UTC  tuomov
+  * trunk: changeset 679
+  Changes in installation directories: The ion-* shell scripts are
+  installed in $SHAREDIR and are ioncorelib.lua, querylib.lua and
+  compat.lua. 'ion-completefile' is installed in $EXTRABINDIR
+  (=$MODULEDIR) being a binary.
+
+2003-05-30 21:17 UTC  tuomov
+  * trunk: changeset 678
+  - Changes in installation directories: The ion-* shell scripts are
+    installed in $SHAREDIR and are ioncorelib.lua, querylib.lua and
+    compat.lua. 'ion-completefile' is installed in $EXTRABINDIR
+    (=$MODULEDIR) being a binary.
+  
+  - QueryLib functions search the script directories (~/.ion-devel/,
+    $ETCDIR, $SHAREDIR, $EXTRABINDIR; in that order) for the ion-*
+    helper programs instead of assuming them being on $PATH.
+
+2003-05-30 21:15 UTC  tuomov
+  * trunk: changeset 677
+  - The *DIR settings in system.mk are now more detailed.
+  
+  - Changes in installation directories: The ion-* shell scripts are
+    installed in $SHAREDIR and are ioncorelib.lua, querylib.lua and
+    compat.lua. 'ion-completefile' is installed in $EXTRABINDIR
+    (=$MODULEDIR) being a binary.
+
+2003-05-29 22:22 UTC  tuomov
+  * trunk: changeset 676
+  Removed note on C99 requirement as that is no longer true.
+
+2003-05-29 13:11 UTC  tuomov
+  * trunk: changeset 675
+  Removed deprecated definitions.
+
+2003-05-29 12:32 UTC  tuomov
+  * trunk: changeset 674
+  All object destroys should now be handled safely.
+
+2003-05-28 22:21 UTC  tuomov
+  * trunk: changeset 673
+  Name management wasn't updating the changes to frames.
+
+2003-05-28 21:37 UTC  tuomov
+  * trunk: changeset 672
+  Floatframe_handle_drop should now set geometry correctly in nested
+  workspaces.
+
+2003-05-28 18:48 UTC  tuomov
+  * trunk: changeset 671
+  Small global environment cluttering fix.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030528
+
+2003-05-28 18:40 UTC  tuomov
+  * trunk: changeset 670
+  Small UTF8 fix.
+
+2003-05-28 16:31 UTC  tuomov
+  * trunk: changeset 669
+  Examples of query_man_path and query_ssh_hosts were added to the
+  default ioncore.lua main configuration file.
+
+2003-05-27 19:14 UTC  tuomov
+  * trunk: changeset 668
+  WClientWins now save the last height request of transients (and other
+  managed objects) so a decent size should always be restored when the
+  window is in a big enough frame.
+
+2003-05-27 17:17 UTC  tuomov
+  * trunk: changeset 667
+  Mkexports.lua document generation updated to understand
+  EXTL_EXPORT_AS.
+
+2003-05-27 17:07 UTC  tuomov
+  * trunk: changeset 666
+  A minor check added.
+
+2003-05-27 17:02 UTC  tuomov
+  * trunk: changeset 665
+  Added a comment.
+
+2003-05-27 16:59 UTC  tuomov
+  * trunk: changeset 664
+  Some clean-up and region_full_name reference removed.
+
+2003-05-27 16:59 UTC  tuomov
+  * trunk: changeset 663
+  complete/lookup_clientwin removed.
+
+2003-05-27 16:57 UTC  tuomov
+  * trunk: changeset 662
+  New name allocation code: client windows are now in a separate
+  namespace from other objects and "short names" without appended
+  instance number are gone.
+
+2003-05-27 16:55 UTC  tuomov
+  * trunk: changeset 661
+  Some tab bar drawing fixes.
+
+2003-05-27 16:47 UTC  tuomov
+  * trunk: changeset 660
+  Added functions to clear Lua table entries.
+
+2003-05-26 22:13 UTC  tuomov
+  * trunk: changeset 659
+  Some changes in system.mk Lua settings.
+
+2003-05-26 22:06 UTC  tuomov
+  * trunk: changeset 658
+  Changed DATA_MODE.
+
+2003-05-26 22:06 UTC  tuomov
+  * trunk: changeset 657
+  Some client window resize/move request handling changes.
+
+2003-05-25 11:07 UTC  tuomov
+  * trunk: changeset 656
+  Some clean-up.
+
+2003-05-25 11:07 UTC  tuomov
+  * trunk: changeset 655
+  Added the boolean 'fullscreen' winprop.
+
+2003-05-25 11:04 UTC  tuomov
+  * trunk: changeset 654
+  Floatws circulate bindings should not clutter global environment.
+
+2003-05-25 11:03 UTC  tuomov
+  * trunk: changeset 653
+  QueryLib should now remember last directory for file view and edit
+  queries.
+
+2003-05-24 20:37 UTC  tuomov
+  * trunk: changeset 652
+  Fixed extl_globals.
+
+2003-05-24 16:45 UTC  tuomov
+  * trunk: changeset 651
+  Minor bugs fixed.
+
+2003-05-24 15:06 UTC  tuomov
+  * trunk: changeset 650
+  Added query_message.
+
+2003-05-24 14:50 UTC  tuomov
+  * trunk: changeset 649
+  Some documentation was missing.
+
+2003-05-24 12:32 UTC  tuomov
+  * trunk: changeset 648
+  Client window rescue fixed.
+
+2003-05-24 11:25 UTC  tuomov
+  * trunk: changeset 647
+  Removed an unnecessary XClear.
+
+2003-05-23 23:57 UTC  tuomov
+  * trunk: changeset 646
+  More attempts at fixing take_focus.
+
+2003-05-23 23:09 UTC  tuomov
+  * trunk: changeset 645
+  Removed extra space in querylib prompts.
+
+2003-05-23 22:46 UTC  tuomov
+  * trunk: changeset 644
+  Fixed lookup_region.
+
+2003-05-23 22:35 UTC  tuomov
+  * trunk: changeset 643
+  Fixed obj_exists.
+
+2003-05-23 18:00 UTC  tuomov
+  * trunk: changeset 642
+  Removed unnuecessary (?) XClear(Area|Window) calls.
+
+2003-05-23 15:43 UTC  tuomov
+  * trunk: changeset 641
+  Removed rootwin->current_screen redundancy.
+
+2003-05-23 15:30 UTC  tuomov
+  * trunk: changeset 640
+  Some minor clean-up.
+
+2003-05-23 15:19 UTC  tuomov
+  * trunk: changeset 639
+  Xinerama screens now always have a virtual root window for better
+  separation.
+
+2003-05-23 15:14 UTC  tuomov
+  * trunk: changeset 638
+  Removed grab_released.
+
+2003-05-23 14:38 UTC  tuomov
+  * trunk: changeset 637
+  Grab handling code simplified.
+
+2003-05-23 14:38 UTC  tuomov
+  * trunk: changeset 636
+  Fixed UTF8 include stuff.
+
+2003-05-21 16:09 UTC  tuomov
+  * trunk: changeset 635
+  The exports region_set_w/h were replaced with region_request_geom.
+
+2003-05-21 16:08 UTC  tuomov
+  * trunk: changeset 634
+  Added EXTL_EXPORT_AS
+
+2003-05-21 16:07 UTC  tuomov
+  * trunk: changeset 633
+  The exported function specification generation script was extended
+  with EXTL_EXPORT_AS(...).
+
+2003-05-21 05:48 UTC  tuomov
+  * trunk: changeset 632
+  Use get_text_property in clientwin_get_ident.
+
+2003-05-20 18:37 UTC  tuomov
+  * trunk: changeset 631
+  Better (?) frame shading and maximizing code that should also
+  eventually work on tiled workspaces (only partially implemented;
+  better region_request_geom neeeded).
+
+2003-05-20 14:13 UTC  tuomov
+  * trunk: changeset 630
+  Oops.
+
+2003-05-20 13:29 UTC  tuomov
+  * trunk: changeset 629
+  Simpler implementation of 'goto_previous' using watches.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030520
+
+2003-05-20 11:45 UTC  tuomov
+  * trunk: changeset 628
+  Don't add non-strings in a table to list of completions.
+
+2003-05-20 11:44 UTC  tuomov
+  * trunk: changeset 627
+  The exports generation script was ignoring constness of string and
+  could therefore cause Ion to crash or corrupt strings.
+
+2003-05-19 22:46 UTC  tuomov
+  * trunk: changeset 626
+  Removed #include <math.h>
+
+2003-05-19 22:45 UTC  tuomov
+  * trunk: changeset 625
+  Some more mouse resize tuning.
+
+2003-05-19 16:51 UTC  tuomov
+  * trunk: changeset 624
+  Some clean-up and optimisation.
+
+2003-05-19 15:34 UTC  tuomov
+  * trunk: changeset 623
+  Kludges and bloat to a proper (but not necessarily correct) X server
+  time in WM_TAKE_FOCUS messages to get around problems with some
+  programs.
+
+2003-05-19 14:32 UTC  tuomov
+  * trunk: changeset 622
+  Unnecessary function region_request_geom_constrain was removed.
+
+2003-05-19 14:32 UTC  tuomov
+  * trunk: changeset 621
+  Changes to accommodate for changes in lookup_region.
+
+2003-05-19 14:31 UTC  tuomov
+  * trunk: changeset 620
+  Unnecessary function wobj_cast was removed.
+
+2003-05-19 14:30 UTC  tuomov
+  * trunk: changeset 619
+  The functions lookup/complete_region now expect a string class
+  parameter (or null for "WRegion") and the functions
+  complete/lookup_workspace/clientwin were moved to ioncorelib.lua as
+  they are not needed on the C side.
+
+2003-05-18 13:41 UTC  tuomov
+  * trunk: changeset 618
+  The resize angle limits are now universal; borders and tabs are not a
+  special case.
+
+2003-05-18 13:35 UTC  tuomov
+  * trunk: changeset 617
+  Mouse resize fine-tuning: if the angle between the vector from the
+  centre of the frame to the point where the initial mouse press occured
+  and the unit vector pointing (from the centre) to a border of the
+  frame is less than 55 degrees, we allow resize to change that border.
+
+2003-05-18 13:03 UTC  tuomov
+  * trunk: changeset 616
+  Some more focusing policy changes/fixes.
+
+2003-05-18 13:01 UTC  tuomov
+  * trunk: changeset 615
+  Oops.
+
+2003-05-18 12:39 UTC  tuomov
+  * trunk: changeset 614
+  Some more cleanup.
+
+2003-05-18 12:31 UTC  tuomov
+  * trunk: changeset 613
+  Some cleanup.
+
+2003-05-18 12:31 UTC  tuomov
+  * trunk: changeset 612
+  Fix around problems with macros with some (possibly broken?) versions
+  of gcc.
+
+2003-05-17 13:53 UTC  tuomov
+  * trunk: changeset 611
+  Previous modifications had broken tabdrag workspace switch.
+
+2003-05-17 13:37 UTC  tuomov
+  * trunk: changeset 610
+  add_to_viewport should not have been renamed add_to_screen.
+
+2003-05-17 13:31 UTC  tuomov
+  * trunk: changeset 609
+  Fixed the place of a comma.
+
+2003-05-17 13:13 UTC  tuomov
+  * trunk: changeset 608
+  CVS fscked up screen.*
+
+2003-05-17 13:11 UTC  tuomov
+  * trunk: changeset 607
+  Removed viewport.c and viewport.h.
+
+2003-05-17 13:08 UTC  tuomov
+  * trunk: changeset 606
+  - Changes in object and function names to be closer to what users see
+    and think: what previously were screens (WScreen) are now called
+    root windows (WRootWin) and viewport (WViewport) have become screens
+    (WScreen). These changes are so big that no wrappers were added to
+    compat.lua.
+  
+  - Some old root window (old screen) functions were removed.
+  
+  - Bindings in global_bindings should get a WScreen (old WViewport) as
+    an argument instead of WRootWin.
+  
+  - It is possible to build Ion with the CF_WINDOWED_SCREENS flag so
+    that WScreens have "virtual" root windows to better separate
+    Xinerama screens.
+
+2003-05-17 13:09 UTC  tuomov
+  * trunk: changeset 605
+  - Changes in object and function names to be closer to what users see
+    and think: what previously were screens (WScreen) are now called
+    root windows (WRootWin) and viewport (WViewport) have become screens
+    (WScreen). These changes are so big that no wrappers were added to
+    compat.lua.
+  
+  - Some old root window (old screen) functions were removed.
+  
+  - Bindings in global_bindings should get a WScreen (old WViewport) as
+    an argument instead of WRootWin.
+
+2003-05-17 12:16 UTC  tuomov
+  * trunk: changeset 604
+  Default configuration uses XOR-rubberband move/resize (instead of
+  opaque) to be nicer on slower systems.
+
+2003-05-17 11:49 UTC  tuomov
+  * trunk: changeset 603
+  Fixed FloatWS initial focus.
+
+2003-05-16 19:53 UTC  tuomov
+  * trunk: changeset 602
+  Some clean-up.
+
+2003-05-16 18:48 UTC  tuomov
+  * trunk: changeset 601
+  Some changes in property getting functions.
+
+2003-05-16 18:02 UTC  tuomov
+  * trunk: changeset 600
+  Removed unnecessary references to screens.
+
+2003-05-16 16:08 UTC  tuomov
+  * trunk: changeset 599
+  region_list_to_table renamed to managed_list_to_table.
+
+2003-05-16 15:59 UTC  tuomov
+  * trunk: changeset 598
+  Signals are not trapped until the initialisation is at a point where
+  we have found some screens to manage.
+
+2003-05-16 15:56 UTC  tuomov
+  * trunk: changeset 597
+  Removed erroneous cast.
+
+2003-05-16 15:56 UTC  tuomov
+  * trunk: changeset 596
+  Added the extl_globals function for accessing globals.
+
+2003-05-16 15:49 UTC  tuomov
+  * trunk: changeset 595
+  Enchancements in client window "rescueing" and some unifications with
+  return from full screen mode.
+
+2003-05-16 15:48 UTC  tuomov
+  * trunk: changeset 594
+  Changed char* to const char* in ExtlL2Param.
+
+2003-05-16 15:42 UTC  tuomov
+  * trunk: changeset 593
+  Moved fullscreen stuff into a separate file.
+
+2003-05-15 23:05 UTC  tuomov
+  * trunk: changeset 592
+  Added a check for nil functions.
+
+2003-05-15 22:27 UTC  tuomov
+  * trunk: changeset 591
+  Modified mkexports.lua to generate a little less code.
+
+2003-05-15 22:26 UTC  tuomov
+  * trunk: changeset 590
+  Fixed extl_table_is_bool_set.
+
+2003-05-15 21:05 UTC  tuomov
+  * trunk: changeset 589
+  Restored old extl_cpcall interface.
+
+2003-05-15 15:45 UTC  tuomov
+  * trunk: changeset 588
+  Added extl_table_is_bool_set.
+
+2003-05-15 15:45 UTC  tuomov
+  * trunk: changeset 587
+  Some l_st usage cleanup in the Lua code and added extl_cpcallx.
+
+2003-05-15 06:01 UTC  tuomov
+  * trunk: changeset 586
+  Some minor changes in the Lua interface.
+
+2003-05-15 06:00 UTC  tuomov
+  * trunk: changeset 585
+  QueryLib.query_lib should properly return on error.
+
+2003-05-14 21:32 UTC  tuomov
+  * trunk: changeset 584
+  Fixed a problem with bsearch() and Solaris.
+
+2003-05-14 20:13 UTC  tuomov
+  * trunk: changeset 583
+  Client window management setup code simplified by attaching transients
+  the their transient_for by default and having floatws:s override this
+  behaviour by hooking to add_clientwin_alt.
+
+2003-05-14 19:59 UTC  tuomov
+  * trunk: changeset 582
+  Changes in how parameters are passed to Lua code loaded as string or
+  from a file.
+
+2003-05-14 16:49 UTC  tuomov
+  * trunk: changeset 581
+  Return from full screen mode should work with floatws:s now.
+
+2003-05-14 16:40 UTC  tuomov
+  * trunk: changeset 580
+  Added a warning for _NET_WM broken transient_for hint.
+
+2003-05-14 16:38 UTC  tuomov
+  * trunk: changeset 579
+  'ionws_do_clientwin' now calls the Lua function
+  'ionws_placement_method' with parameters (ws, cwin, pos_given_by_user)
+  to determine in which frame to place a window. This can be used to
+  e.g. experiment with placement heuristics.
+
+2003-05-14 14:21 UTC  tuomov
+  * trunk: changeset 578
+  Mouse resize changed to only resize along one coordinate axis when the
+  window is grabbed far enough from borders.
+
+2003-05-13 19:20 UTC  tuomov
+  * trunk: changeset 577
+  Renamed geom.* extlconv.*
+
+2003-05-13 18:07 UTC  tuomov
+  * trunk: changeset 576
+  The 'include' function didn't handle absolute paths.
+
+2003-05-13 18:06 UTC  tuomov
+  * trunk: changeset 575
+  There was a problem getting return values from Lua functions.
+
+2003-05-13 16:09 UTC  tuomov
+  * trunk: changeset 574
+  Added parameter count check and complaint when va_copy is not
+  available.
+
+2003-05-13 16:05 UTC  tuomov
+  * trunk: changeset 573
+  The new Lua calling code allowed removing dependency on C99 va_copy a
+  little more easily than the old so Ion no longer depends on it and
+  should be easier to compile on older systems (apparently including gcc
+  2.9x.x).
+
+2003-05-13 14:46 UTC  tuomov
+  * trunk: changeset 572
+  Transient resizing when the managing WClientWin was resized had been
+  broken at some point.
+
+2003-05-13 05:40 UTC  tuomov
+  * trunk: changeset 571
+  Removed redundant comment.
+
+2003-05-13 05:37 UTC  tuomov
+  * trunk: changeset 570
+  Vertical keyboard resize binding swapped.
+
+2003-05-13 05:38 UTC  tuomov
+  * trunk: changeset 569
+  Keyboard resize should not "cumulate" size increments that do not
+  affect the frame size.
+
+2003-05-12 23:17 UTC  tuomov
+  * trunk: changeset 568
+  Fixed a minor resize glitch.
+
+2003-05-12 19:57 UTC  tuomov
+  * trunk: changeset 567
+  fixed a typo
+
+2003-05-12 17:02 UTC  tuomov
+  * trunk: changeset 566
+  ionws_split_of synopsis changed.
+
+2003-05-12 16:59 UTC  tuomov
+  * trunk: changeset 565
+  Enhanced comments.
+
+2003-05-12 16:38 UTC  tuomov
+  * trunk: changeset 564
+  Region name instances are saved in the workspace savefiles. (However,
+  client windows do not use the saved title because it may have changed
+  so client windows' instance numbers may change over restarts.)
+
+2003-05-12 16:31 UTC  tuomov
+  * trunk: changeset 563
+  QueryLib goto still had a debug complaint in it.
+
+2003-05-12 16:23 UTC  tuomov
+  * trunk: changeset 562
+  Save client window id as %lu instead of %.32f.
+
+2003-05-12 16:20 UTC  tuomov
+  * trunk: changeset 561
+  The wrapper function generated by "obsolete" didn't return any values.
+
+2003-05-12 16:18 UTC  tuomov
+  * trunk: changeset 560
+  Do not add IMPLOBJ(WObj) in exports.c
+
+2003-05-12 16:18 UTC  tuomov
+  * trunk: changeset 559
+  The exports region_get_(x|y|w|h) were replaced with region_geom.
+
+2003-05-12 16:17 UTC  tuomov
+  * trunk: changeset 558
+  Added new exports to get information on splits on WIonWs:s. This
+  should help writing alternative navigation functions.
+
+2003-05-12 16:18 UTC  tuomov
+  * trunk: changeset 557
+  Added wrappers to obsolete functions region_get_(x|y|w|h).
+
+2003-05-11 16:17 UTC  tuomov
+  * trunk: changeset 556
+  Client window check codes are not reset so they're not lost so easily.
+
+2003-05-11 14:24 UTC  tuomov
+  * trunk: changeset 555
+  Cleaned up.
+
+2003-05-11 13:33 UTC  tuomov
+  * trunk: changeset 554
+  Added checks in region_add_managed to prevent from attachinging
+  parent's or manager's to their (grand)children or managed regions.
+
+2003-05-11 00:05 UTC  tuomov
+  * trunk: changeset 553
+  Added a missing newline.
+
+2003-05-10 23:43 UTC  tuomov
+  * trunk: changeset 552
+  Another broken documentation comment found.
+
+2003-05-10 23:42 UTC  tuomov
+  * trunk: changeset 551
+  'ionws_load' no longer requires 'split_tree' to be specified so that
+  new workspaces can be created with region_manage_new without
+  specifying the contents.
+
+2003-05-10 23:41 UTC  tuomov
+  * trunk: changeset 550
+  The rest of the queries (goto/create workspace, attach client) are now
+  finally implemented in Lua as the function region_manage(_new) are
+  available.
+
+2003-05-10 23:15 UTC  tuomov
+  * trunk: changeset 549
+  Documentation was missing for exports in screen.c.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030510
+
+2003-05-10 20:34 UTC  tuomov
+  * trunk: changeset 548
+  Removed efence reference from system.mk.
+
+2003-05-10 19:56 UTC  tuomov
+  * trunk: changeset 547
+  Oops.. region_manage documentation had an extra space that caused it
+  not to be parsed.
+
+2003-05-10 02:16 UTC  tuomov
+  * trunk: changeset 546
+  Some changes in comments.
+
+2003-05-10 02:12 UTC  tuomov
+  * trunk: changeset 545
+  Fixed a bug in check_input_fds.
+
+2003-05-10 02:11 UTC  tuomov
+  * trunk: changeset 544
+  Wiser loading of querylib.
+
+2003-05-10 02:13 UTC  tuomov
+  * trunk: changeset 543
+  The Lua interface code now uses lua_cpcall extensively to make it more
+  tolerant to Lua's longjmp error handling.
+
+2003-05-09 22:21 UTC  tuomov
+  * trunk: changeset 542
+  'extl_dofile' and 'extl_dostring' now pass arguments in the local
+  instead of global variable 'arg'.
+
+2003-05-09 22:19 UTC  tuomov
+  * trunk: changeset 541
+  Setfenv is not necessary. Fixed error reporting.
+
+2003-05-09 21:43 UTC  tuomov
+  * trunk: changeset 540
+  Viewport names are now saved and other changes in savefile format. Old
+  'add_to_viewport' function was kept for compatibility but will be
+  removed eventually.
+
+2003-05-09 17:39 UTC  tuomov
+  * trunk: changeset 539
+  Bleah.
+
+2003-05-09 17:34 UTC  tuomov
+  * trunk: changeset 538
+  Some more README changes.
+
+2003-05-09 17:32 UTC  tuomov
+  * trunk: changeset 537
+  Fixed transient size/position problems and split the
+  REGION_ATTACH_GEOMRQ flag into separate POSRQ and SIZERQ.
+
+2003-05-09 16:28 UTC  tuomov
+  * trunk: changeset 536
+  Updated the README.
+
+2003-05-09 14:57 UTC  tuomov
+  * trunk: changeset 535
+  Fixed a tabularx
+
+2003-05-09 14:36 UTC  tuomov
+  * trunk: changeset 534
+  Added the exports 'region_manage' and 'region_manage_new'.
+
+2003-05-09 14:35 UTC  tuomov
+  * trunk: changeset 533
+  Fixed a bug in extl_table_get that caused it to succeed for NULL
+  WObjs.
+
+2003-05-09 13:33 UTC  tuomov
+  * trunk: changeset 532
+  Query module listings could hang Ion if there was not enough space for
+  a single visible row.
+
+2003-05-08 18:38 UTC  tuomov
+  * trunk: changeset 531
+  Some bindings were still using Mod1 instead of DEFAULT_MOD.
+
+2003-05-08 18:36 UTC  tuomov
+  * trunk: changeset 530
+  Some minor fixes in bindings
+
+2003-05-08 06:05 UTC  tuomov
+  * trunk: changeset 529
+  Fixed a va_arg problem with luaextl.c and strange architechtures.
+
+2003-05-08 06:05 UTC  tuomov
+  * trunk: changeset 528
+  Oops.
+
+2003-05-08 05:25 UTC  tuomov
+  * trunk: changeset 527
+  Updated documentation comments.
+
+2003-05-07 18:49 UTC  tuomov
+  * trunk: changeset 526
+  Fixed a typo.
+
+2003-05-07 18:42 UTC  tuomov
+  * trunk: changeset 525
+  The Lua parser in mkexports.lua now parses for function arguments.
+
+2003-05-07 18:42 UTC  tuomov
+  * trunk: changeset 524
+  Removed make_screen_switch_nth_fn.
+
+2003-05-07 18:41 UTC  tuomov
+  * trunk: changeset 523
+  Added documentation to ioncorelib.lua.
+
+2003-05-07 18:41 UTC  tuomov
+  * trunk: changeset 522
+  Removed Makefile; function reference .texes are now generated in the
+  ion-doc package.
+
+2003-05-07 18:36 UTC  tuomov
+  * trunk: changeset 521
+  Changes in function documentation Makefile.
+
+2003-05-07 18:21 UTC  tuomov
+  * trunk: changeset 520
+  'mkexports.lua' can now parse documentation from Lua code.
+
+2003-05-07 18:20 UTC  tuomov
+  * trunk: changeset 519
+  Added documentation to querylib.lua.
+
+2003-05-07 16:08 UTC  tuomov
+  * trunk: changeset 518
+  The functions 'ionws_split', 'ionws_split_empty' and 'ionws_split_top'
+  were renamed to the more consistent 'ionframe_split',
+  'ionframe_split_empty' and 'ionws_newframe'. As usual, 'compat.lua'
+  has wrappers to the old functions.
+
+2003-05-07 15:57 UTC  tuomov
+  * trunk: changeset 517
+  - The completion handler for QueryLib.query_lua can now descend into
+    tables and complete subexpressions.
+  
+  - QueryLib.query_lua sets the variable '_' in the local environment of
+    the string to be called to point to the frame the query was opened
+    in. The variable 'arg' is also now set in the local environment
+    instead of global.
+
+2003-05-07 14:54 UTC  tuomov
+  * trunk: changeset 516
+  Client window size hints were not used when Ion was restarted. Fixed
+  that.
+
+2003-05-07 14:42 UTC  tuomov
+  * trunk: changeset 515
+  'viewport_display_managed' was calling just 'set_focus' instead of
+  'warp' as it should.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030506
+
+2003-05-07 00:14 UTC  tuomov
+  * trunk: changeset 514
+  FIND_PARENT1 renamed GET_PARENT_CHK. FIND_PARENT removed.
+
+2003-05-06 18:28 UTC  tuomov
+  * trunk: changeset 513
+  Fixed a memory allocation problem discovered by valgrind.
+
+2003-05-06 17:02 UTC  tuomov
+  * trunk: changeset 512
+  x => x!=NULL
+
+2003-05-06 17:02 UTC  tuomov
+  * trunk: changeset 511
+  Added some \hlines in tables
+
+2003-05-06 05:20 UTC  tuomov
+  * trunk: changeset 510
+  Fixed a bug in the title shortening routine.
+
+2003-05-06 05:20 UTC  tuomov
+  * trunk: changeset 509
+  Fixed a bug in the new transient management setup code.
+
+2003-05-05 22:58 UTC  tuomov
+  * trunk: changeset 508
+  A small change in documentation comment.
+
+2003-05-05 22:58 UTC  tuomov
+  * trunk: changeset 507
+  Changes in the Makefile to build the exports.texes.
+
+2003-05-05 22:50 UTC  tuomov
+  * trunk: changeset 506
+  Line editor history is now saved when Ion exits.
+
+2003-05-05 17:40 UTC  tuomov
+  * trunk: changeset 505
+  Fixed extl_stack_get 'double' code.
+
+2003-05-05 17:37 UTC  tuomov
+  * trunk: changeset 504
+  - Removed target_id code.
+  
+  - Client windows are now saved over restarts in saves/workspaces-*.lua
+    instead of using target_ids. A special check code property is added
+    to each window so that we don't incorrectly reparent windows when
+    initially starting Ion.
+
+2003-05-04 16:01 UTC  tuomov
+  * trunk: changeset 503
+  Added hyperlinks in the exported function documentation.
+
+2003-05-04 02:18 UTC  tuomov
+  * trunk: changeset 502
+  Minor fix.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030504
+
+2003-05-04 02:18 UTC  tuomov
+  * trunk: changeset 501
+  Sort functions in documentation alphabetically.
+
+2003-05-04 00:59 UTC  tuomov
+  * trunk: changeset 500
+  minor fixes
+
+2003-05-03 22:34 UTC  tuomov
+  * trunk: changeset 499
+  fix
+
+2003-05-03 22:30 UTC  tuomov
+  * trunk: changeset 498
+  typo fixed
+
+2003-05-03 22:30 UTC  tuomov
+  * trunk: changeset 497
+  typo fixed
+
+2003-05-03 22:04 UTC  tuomov
+  * trunk: changeset 496
+  doc/Makefile added and list_exports.sh removed
+
+2003-05-03 22:04 UTC  tuomov
+  * trunk: changeset 495
+  Documentation was added to the source for exported functions.
+
+2003-05-03 22:03 UTC  tuomov
+  * trunk: changeset 494
+  'mkexports.lua' now parses for documentation of the form /*EXTL_DOC
+  ... */.
+
+2003-05-03 16:21 UTC  tuomov
+  * trunk: changeset 493
+  The 'mkexports' script was rewritten in Lua (was an unmaintainable
+  vomit of Perl).
+
+2003-05-02 19:41 UTC  tuomov
+  * trunk: changeset 492
+  Made 'obsolete' local
+
+2003-05-02 19:39 UTC  tuomov
+  * trunk: changeset 491
+  Added some comments
+
+2003-05-02 19:18 UTC  tuomov
+  * trunk: changeset 490
+  WFloatFrames can now be resized from the keyboard.
+
+2003-05-02 19:17 UTC  tuomov
+  * trunk: changeset 489
+  Simplified resize interface to have just ionframe_do_resize
+
+2003-05-02 18:52 UTC  tuomov
+  * trunk: changeset 488
+  IonFrame keyboard resize mode changed to allow resizing in both
+  directions without leaving and re-entering resize mode. Compatibility
+  functions for the old mode are provided in compat.lua.
+
+2003-05-02 18:52 UTC  tuomov
+  * trunk: changeset 487
+  - IonFrame keyboard resize mode changed to allow resizing in both
+    directions without leaving and re-entering resize mode.
+    Compatibility functions for the old mode are provided in compat.lua.
+  
+  - The compatibility functions in compat.lua now complain of
+    obsoleteness to stderr.
+
+2003-05-02 17:32 UTC  tuomov
+  * trunk: changeset 486
+  Added some comments
+
+2003-05-02 17:27 UTC  tuomov
+  * trunk: changeset 485
+  Added the Lua-side hooks genframe_managed_switched and
+  viewport_workspace_switched.
+
+2003-05-02 17:27 UTC  tuomov
+  * trunk: changeset 484
+  Added support for hooks Lua code can hook on to with add_to_hook(hook,
+  fn).
+
+2003-05-02 16:18 UTC  tuomov
+  * trunk: changeset 483
+  Fixed (floatws) focus problem that reoccured after previous attempt at
+  fixing other focus problems.
+
+2003-05-01 14:23 UTC  tuomov
+  * trunk: changeset 482
+  Added 'eq' metamethod for WObj:s.
+
+2003-05-01 14:12 UTC  tuomov
+  * trunk: changeset 481
+  Added new exports that should e.g. enable writing workspace navigation
+  functions that can also be used to move between viewports or other
+  workspaces instead of just wrapping around.
+
+2003-05-01 13:14 UTC  tuomov
+  * trunk: changeset 480
+  Some unifications in add_clientwin/region_add_managed interface.
+
+2003-04-28 22:42 UTC  tuomov
+  * trunk: changeset 479
+  Fixed CURRENT_FILE maintenance in include().
+
+2003-04-28 16:47 UTC  tuomov
+  * trunk: changeset 478
+  minor fix
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030428
+
+2003-04-28 15:12 UTC  tuomov
+  * trunk: changeset 477
+  The innermost window grabbing on a mouse button should now get to
+  handle the event as is the case with key grabs.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030427-2
+
+2003-04-28 14:53 UTC  tuomov
+  * trunk: changeset 476
+  Fixed WIonFrame subregion load problem
+
+2003-04-28 05:21 UTC  tuomov
+  * trunk: changeset 475
+  Fixed the region destroy focus fix.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030427-1
+
+2003-04-27 21:29 UTC  tuomov
+  * trunk: changeset 474
+  QueryLib.query_exec fixed to use the correct handler.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030427
+
+2003-04-27 21:26 UTC  tuomov
+  * trunk: changeset 473
+  Fixed a warning
+
+2003-04-27 17:44 UTC  tuomov
+  * trunk: changeset 472
+  ion-completefile Makefile fixed.
+
+2003-04-27 11:54 UTC  tuomov
+  * trunk: changeset 471
+  minor fix
+
+2003-04-27 01:15 UTC  tuomov
+  * trunk: changeset 470
+  doh!
+
+2003-04-27 01:10 UTC  tuomov
+  * trunk: changeset 469
+  QueryLib file and man page completors use 'popen_bgread' so the
+  queries can not block the WM from processing other events (or even
+  hang it).
+
+2003-04-27 01:09 UTC  tuomov
+  * trunk: changeset 468
+  Moved file completetion code from the query module into a separate
+  external program ('ion-completefile').
+
+2003-04-27 01:07 UTC  tuomov
+  * trunk: changeset 467
+  Added 'popen_bgread(cmd, lua_fn)' to open read mode pipes that are
+  selected() in the main event loop and the given function called with
+  received data.
+
+2003-04-25 17:32 UTC  tuomov
+  * trunk: changeset 466
+  Most Ion functions should be null-string safe now except for some low-
+  level functions and functions that also receive string length as an
+  argument. This allows Lua scripts to pass nil to functions that have
+  special meaning for NULL strings.
+
+2003-04-25 17:11 UTC  tuomov
+  * trunk: changeset 465
+  Fixed do_complete_region.
+
+2003-04-25 16:59 UTC  tuomov
+  * trunk: changeset 464
+  Some changes on how region close/destroy is handled and how focus is
+  handled when an active region with non-window manager is destroyed.
+
+2003-04-25 16:46 UTC  tuomov
+  * trunk: changeset 463
+  Fixed extl_verify_wobj.
+
+2003-04-24 17:27 UTC  tuomov
+  * trunk: changeset 462
+  Some minor TODOs completed and some minor fixes.
+
+2003-04-23 22:16 UTC  tuomov
+  * trunk: changeset 461
+  Removed string free in extl_stack_push
+
+2003-04-23 16:17 UTC  tuomov
+  * trunk: changeset 460
+  foo
+
+2003-04-23 16:16 UTC  tuomov
+  * trunk: changeset 459
+  The floatws module is now aware of window gravities.
+
+2003-04-22 19:08 UTC  tuomov
+  * trunk: changeset 458
+  Some comments added
+
+2003-04-22 19:05 UTC  tuomov
+  * trunk: changeset 457
+  - Added window stacking management code.
+  
+  - Fixed region_notify_subregions_move.
+
+2003-04-22 19:05 UTC  tuomov
+  * trunk: changeset 456
+  Added window stacking management code.
+
+2003-04-22 19:05 UTC  tuomov
+  * trunk: changeset 455
+  The (exported) functions floatframe_raise/lower are obsolete and
+  replaced with region_raise/lower. The file 'compat.lua' can be
+  included to define these functions.
+
+2003-04-20 17:45 UTC  tuomov
+  * trunk: changeset 454
+  No longer complain of missing workspace savefiles.
+
+2003-04-19 20:11 UTC  tuomov
+  * trunk: changeset 453
+  minor fixes
+
+2003-04-19 20:10 UTC  tuomov
+  * trunk: changeset 452
+  Makefile also changed to note change of name in ioncorelib.lua
+
+2003-04-19 20:09 UTC  tuomov
+  * trunk: changeset 451
+  'ioncore-lib.lua' renamed 'ioncorelib.lua' to be consistent with
+  'querylib.lua'.
+
+2003-04-19 19:36 UTC  tuomov
+  * trunk: changeset 450
+  removed debug messages
+
+2003-04-19 19:35 UTC  tuomov
+  * trunk: changeset 449
+  Extl_init enables Lua loadlib.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030416
+
+2003-04-19 19:34 UTC  tuomov
+  * trunk: changeset 448
+  Winprop management is now implemented in Lua.
+
+2003-04-16 21:44 UTC  tuomov
+  * trunk: changeset 447
+  QueryLib.mancache fixed to contain weak references.
+
+2003-04-16 17:16 UTC  tuomov
+  * trunk: changeset 446
+  Include correct version of libtu. Old version could cause crashes.
+
+2003-04-16 17:14 UTC  tuomov
+  * trunk: changeset 445
+  misc signal.c changes
+
+2003-04-16 17:13 UTC  tuomov
+  * trunk: changeset 444
+  WFloatWS placement code should now handle shaded frames correctly.
+
+2003-04-16 17:11 UTC  tuomov
+  * trunk: changeset 443
+  Added man-page completion to QueryLib.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030412-3
+
+2003-04-16 17:09 UTC  tuomov
+  * trunk: changeset 442
+  Don't waitpid() in the SIGCHLD handler but in the main loop after this
+  handler has been called. For some reason Lua's io.popen() didn't like
+  the old behaviour.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030412-2
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030412-1
+
+2003-04-13 19:07 UTC  tuomov
+  * trunk: changeset 441
+  FloatWS module honours window positions when starting up the WM.
+
+2003-04-12 20:08 UTC  tuomov
+  * trunk: changeset 440
+  - Some transient handling fixes.
+  
+  - region_do_add_managed wasn't passing enough parameters which could
+    cause a crash.
+
+2003-04-12 19:27 UTC  tuomov
+  * trunk: changeset 439
+  Old upvalue syntax removed from Lua code as the just-released Lua 5.0
+  does not support it anymore by default.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030412
+
+2003-04-12 19:06 UTC  tuomov
+  * trunk: changeset 438
+  Added the flag -std=c99 to options to compile luaextl.c as it needs
+  va_copy from C99 and some versions of GCC seem to disable this macro
+  otherwise.
+
+2003-04-12 18:44 UTC  tuomov
+  * trunk: changeset 437
+  Include stdarg.h in luaextl.c
+
+2003-04-12 18:19 UTC  tuomov
+  * trunk: changeset 436
+  minor changes
+
+2003-04-12 17:27 UTC  tuomov
+  * trunk: changeset 435
+  Yet another attempt at perfecting focus handling (before resorting to
+  separate displayed and actual activity states and update delays or
+  similar another kludge in counteracting X's lame key grab focus
+  policy).
+
+2003-04-12 16:44 UTC  tuomov
+  * trunk: changeset 434
+  Added CF_XMESSAGE
+
+2003-04-12 16:44 UTC  tuomov
+  * trunk: changeset 433
+  minor changes
+
+2003-04-12 16:43 UTC  tuomov
+  * trunk: changeset 432
+  flags
+
+2003-04-12 16:43 UTC  tuomov
+  * trunk: changeset 431
+  Prefer _NET_WM_NAME, if it exists, over WM_NAME as apps no longer seem
+  to use WM_NAME for UTF-8 titles.
+
+2003-04-12 16:10 UTC  tuomov
+  * trunk: changeset 430
+  QueryLib.query_lua displays all error messages.
+
+2003-04-12 16:08 UTC  tuomov
+  * trunk: changeset 429
+  The standard modules no longer fail on partially broken configuration
+  files unless no bindings have been configured before the error
+  occured.
+
+2003-04-12 16:07 UTC  tuomov
+  * trunk: changeset 428
+  misc
+
+2003-04-12 16:06 UTC  tuomov
+  * trunk: changeset 427
+  Full error log is displayed with xmessage after startup whether it is
+  possible to continue or not.
+
+2003-04-12 16:05 UTC  tuomov
+  * trunk: changeset 426
+  Added collect_errors(fn, params) for Lua code to be able to e.g.
+  display encountered errors with query_fwarn.
+
+2003-04-11 19:19 UTC  tuomov
+  * trunk: changeset 425
+  key changes
+
+2003-04-11 18:48 UTC  tuomov
+  * trunk: changeset 424
+  Added some XFree(prop.value):s
+
+2003-04-11 18:38 UTC  tuomov
+  * trunk: changeset 423
+  Inconsistently named 'goto_viewport_id' renamed to goto_nth_viewport.
+
+2003-04-11 18:37 UTC  tuomov
+  * trunk: changeset 422
+  misc changes.
+
+2003-04-11 18:36 UTC  tuomov
+  * trunk: changeset 421
+  The line editor's copy-paste features should now at least attempt to
+  support UTF8.
+
+2003-04-11 17:02 UTC  tuomov
+  * trunk: changeset 420
+  Added placement calculation code to the floatws module. Placement
+  method can be configured with
+  'set_floatws_placement_method("method")'. Available methods are udlr,
+  lrud and random. (Maybe placement methods should be implemented in
+  Lua?)
+
+2003-04-11 16:06 UTC  tuomov
+  * trunk: changeset 419
+  Client windows are unmapped when frames are unmapped. This was an
+  overlooked (but lame and redundant) requirement of the ICCCM and
+  fullfilling it might fix some apps. (Ion probably still is far from
+  ICCCM-compliant, but so are most of the badly behaving apps.)
+
+2003-04-11 15:24 UTC  tuomov
+  * trunk: changeset 418
+  If UTF8 support is enabled, Ion will attempt to load CF_FALLBACK_FONT
+  ("fixed" by default) at startup. If this fails (or XSupporsLocale()
+  fails, which it seldom seems to do), it will reset locale back to
+  "POSIX" so that there's a better chance that some fonts can be loaded
+  although non-ASCII (7-bit) characters will be crippled. (If UTF8
+  support is disabled, 8-bit character sets should usually work.)
+
+2003-04-11 14:08 UTC  tuomov
+  * trunk: changeset 417
+  Double-click shades
+
+2003-04-11 14:07 UTC  tuomov
+  * trunk: changeset 416
+  WFloatFrames can now be shaded.
+
+2003-04-11 05:32 UTC  tuomov
+  * trunk: changeset 415
+  minor fixes
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030410
+
+2003-04-11 05:19 UTC  tuomov
+  * trunk: changeset 414
+  complete_function fixed.
+
+2003-04-10 23:28 UTC  tuomov
+  * trunk: changeset 413
+  Transient_mode winprop fixed.
+
+2003-04-10 18:31 UTC  tuomov
+  * trunk: changeset 412
+  Removed some upvalue-marks
+
+2003-04-10 18:26 UTC  tuomov
+  * trunk: changeset 411
+  Remaining "goto_*_name" functions were removed as Lua code will
+  probably mostly use "reg=lookup_*() ... region_goto(reg)"
+
+2003-04-10 18:08 UTC  tuomov
+  * trunk: changeset 410
+  QueryLib.query_yesno fixed.
+
+2003-04-10 16:34 UTC  tuomov
+  * trunk: changeset 409
+  moved obj_exists to ioncore-lib.lua
+
+2003-04-10 16:33 UTC  tuomov
+  * trunk: changeset 408
+  Removed it
+
+2003-04-10 16:32 UTC  tuomov
+  * trunk: changeset 407
+  Added 'obj_exists' export for checking whether an object referenced in
+  Lua still exists in Ion.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030409
+
+2003-04-10 16:03 UTC  tuomov
+  * trunk: changeset 406
+  Updated web page address and my email
+
+2003-04-10 06:01 UTC  tuomov
+  * trunk: changeset 405
+  complete_function implemented in Lua.
+
+2003-04-09 22:51 UTC  tuomov
+  * trunk: changeset 404
+  - A lot of the query code was converted to Lua.
+  
+  - QueryLib.query_ssh query was added. This will tab-complete hosts
+    from the table "query_ssh_hosts" and run the script "ion-ssh" on the
+    entered host.
+
+2003-04-09 21:07 UTC  tuomov
+  * trunk: changeset 403
+  A number of bugs in the Lua interface were fixed.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030408
+
+2003-04-09 19:04 UTC  tuomov
+  * trunk: changeset 402
+  Some fixes
+
+2003-04-09 18:46 UTC  tuomov
+  * trunk: changeset 401
+  Added functions to add entries into Lua tables (for completion
+  handlers).
+
+2003-04-08 17:49 UTC  tuomov
+  * trunk: changeset 400
+  Code to create ~/.ion-devel/saves/ if it didn't exist had been lost at
+  some point.
+
+2003-04-08 17:35 UTC  tuomov
+  * trunk: changeset 399
+  added luaextl to SUBDIRS
+
+2003-04-08 17:33 UTC  tuomov
+  * trunk: changeset 398
+  foo
+
+2003-04-08 17:25 UTC  tuomov
+  * trunk: changeset 397
+  Use libtool and libltdl for module support.
+
+2003-04-07 19:35 UTC  tuomov
+  * trunk: changeset 396
+  Added obj_is and obj_typename exports.
+
+2003-04-07 19:16 UTC  tuomov
+  * trunk: changeset 395
+  Multi-line capability added to WMessage.
+
+2003-04-07 19:16 UTC  tuomov
+  * trunk: changeset 394
+  Added quite useless stack trace displayed when C function called from
+  Lua calls warn().
+
+2003-04-07 19:15 UTC  tuomov
+  * trunk: changeset 393
+  Added verbosity to conf-bindings error messages.
+
+2003-04-07 17:15 UTC  tuomov
+  * trunk: changeset 392
+  Function renames. Most functions that can be considered member
+  functions of some WObj are now rather consistently (although
+  unnaturally) named.
+
+2003-04-07 17:14 UTC  tuomov
+  * trunk: changeset 391
+  Updated configuration files
+
+2003-04-07 17:14 UTC  tuomov
+  * trunk: changeset 390
+  Removed out-of-date documentation
+
+2003-04-07 15:21 UTC  tuomov
+  * trunk: changeset 389
+  Focusing code: iteration n.
+
+2003-04-07 14:59 UTC  tuomov
+  * trunk: changeset 388
+  Defer mechanism supports multiple lists.
+
+2003-04-07 05:47 UTC  tuomov
+  * trunk: changeset 387
+  Added extl_dostring and better extl_dofile
+
+2003-04-07 05:36 UTC  tuomov
+  * trunk: changeset 386
+  Added some checks to conf-draw.c
+
+2003-04-07 05:25 UTC  tuomov
+  * trunk: changeset 385
+  Added wedln-wrappers.c
+
+2003-04-07 05:23 UTC  tuomov
+  * trunk: changeset 384
+  Added extl.h
+
+2003-04-07 00:05 UTC  tuomov
+  * trunk: changeset 383
+  Added luaextl Makefile
+
+2003-04-06 21:47 UTC  tuomov
+  * trunk: changeset 382
+  Added some stack checks
+
+2003-04-06 16:30 UTC  tuomov
+  * trunk: changeset 381
+  Implemented include() support for the lua files
+
+2003-04-06 15:20 UTC  tuomov
+  * trunk: changeset 380
+  Use Lua as extension language.
+
+2003-04-05 15:00 UTC  tuomov
+  * trunk: changeset 379
+  MODULE_CFLAGS fixed.
+
+2003-04-02 18:24 UTC  tuomov
+  * trunk: changeset 378
+  Some code cleanup.
+
+2003-03-30 17:20 UTC  tuomov
+  * trunk: changeset 377
+  Some minor cleanup.
+
+2003-03-30 14:29 UTC  tuomov
+  * trunk: changeset 376
+  More minor object model changes
+
+2003-03-30 14:16 UTC  tuomov
+  * trunk: changeset 375
+  Some simplifications to the object model: WThing removed and
+  functionality split between WObj (watches) and WRegion (child<->parent
+  linking).
+
+2003-03-28 20:48 UTC  tuomov
+  * trunk: changeset 374
+  The region_add_managed mechanism was simplified and generalised.
+
+2003-03-28 16:01 UTC  tuomov
+  * trunk: changeset 373
+  Possible key binding setup bug fixed.
+
+2003-03-28 16:01 UTC  tuomov
+  * trunk: changeset 372
+  Modules are removed by 'make realclean'.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030327
+
+2003-03-28 16:00 UTC  tuomov
+  * trunk: changeset 371
+  foo
+
+2003-03-28 15:59 UTC  tuomov
+  * trunk: changeset 370
+  Scripts are build using ETCDIR and LIBDIR instead of just PREFIX.
+
+2003-03-27 19:40 UTC  tuomov
+  * trunk: changeset 369
+  Don't strip modules
+
+2003-03-27 19:32 UTC  tuomov
+  * trunk: changeset 368
+  fix
+
+2003-03-27 19:30 UTC  tuomov
+  * trunk: changeset 367
+  Simple ioncore startup check added.
+
+2003-03-27 19:26 UTC  tuomov
+  * trunk: changeset 366
+  More minor fixes
+
+2003-03-27 19:16 UTC  tuomov
+  * trunk: changeset 365
+  Minor fix in clientwin_deinit.
+
+2003-03-27 19:01 UTC  tuomov
+  * trunk: changeset 364
+  A minor nested WS fix.
+
+2003-03-27 18:12 UTC  tuomov
+  * trunk: changeset 363
+  - Renamed the main binary 'ioncore'.
+  
+  - Added 'ion' shell script to run 'ioncore' with correct configuration
+    and module file directory parameters. A 'pwm' script to run ioncore
+    in PWM mode was also added but this is not installed by 'make
+    install' at the moment.
+
+2003-03-22 20:22 UTC  tuomov
+  * trunk: changeset 362
+  Applied the toggle_tab patch.
+
+2003-03-20 21:01 UTC  tuomov
+  * trunk: changeset 361
+  - Frames save their saveable contents
+  
+  - EnterWindow event handling changed so that embedded workspaces work
+    as expected.
+
+2003-03-17 18:49 UTC  tuomov
+  * trunk: changeset 360
+  system.mk mods
+
+2003-03-17 18:29 UTC  tuomov
+  * trunk: changeset 359
+  Client window (esp. transient) resize request fixes.
+
+2003-03-17 18:28 UTC  tuomov
+  * trunk: changeset 358
+  Double-click fixed.
+
+2003-03-17 18:28 UTC  tuomov
+  * trunk: changeset 357
+  stuff moved to system.mk
+
+2003-03-17 18:27 UTC  tuomov
+  * trunk: changeset 356
+  Tabdrag detects attempts to drop on oneself
+
+2003-03-15 19:21 UTC  tuomov
+  * trunk: changeset 355
+  ASCII control characters (ch&0x7f<32) are now presented as escaped
+  octals in saved region name strings.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030311-2
+
+2003-03-15 18:15 UTC  tuomov
+  * trunk: changeset 354
+  The functions region_add_bindmap* no longer have the grab argument but
+  instead REGION_BINDINGS_ARE_GRABBED flag is to be set.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030311
+
+2003-03-15 18:15 UTC  tuomov
+  * trunk: changeset 353
+  - The functions region_add_bindmap* no longer have the grab argument
+    but instead REGION_BINDINGS_ARE_GRABBED flag is to be set.
+  
+  - CF_PLACEMENT_GEOM check added in find_suitable_viewport.
+
+2003-03-11 20:29 UTC  tuomov
+  * trunk: changeset 352
+  Fixed a stupid mistake in creating an initial workspace when there is
+  no workspaces.conf.
+
+2003-03-11 19:03 UTC  tuomov
+  * trunk: changeset 351
+  fix
+
+2003-03-11 18:57 UTC  tuomov
+  * trunk: changeset 350
+  cleanup
+
+2003-03-11 18:52 UTC  tuomov
+  * trunk: changeset 349
+  foobar
+
+2003-03-11 18:52 UTC  tuomov
+  * trunk: changeset 348
+  Function lookup order changed from region->parent to region->manager.
+
+2003-03-10 22:01 UTC  tuomov
+  * trunk: changeset 347
+  Tabs can now be dropped on WFloatWS workspaces to create a new frames
+  containing the region corresponding to the dragged tab.
+
+2003-03-10 19:12 UTC  tuomov
+  * trunk: changeset 346
+  query_workspace should now set workspace name
+
+2003-03-10 16:58 UTC  tuomov
+  * trunk: changeset 345
+  Added the compile time option CF_UNDERSCORED_MODULE_SYMBOLS for some
+  strange systems whose libdl insists on the calling program prefixing
+  module symbol names with an underscore.
+
+2003-03-09 22:50 UTC  tuomov
+  * trunk: changeset 344
+  Resize size hint handling properly (?) implemented.
+
+2003-03-09 12:41 UTC  tuomov
+  * trunk: changeset 343
+  *sigh*
+
+2003-03-09 12:40 UTC  tuomov
+  * trunk: changeset 342
+  Support for compiling modules statically in the Ion core binary
+
+2003-03-09 12:24 UTC  tuomov
+  * trunk: changeset 341
+  Added IonWs main.h
+
+2003-03-09 11:46 UTC  tuomov
+  * trunk: changeset 340
+  README updates
+
+2003-03-09 11:43 UTC  tuomov
+  * trunk: changeset 339
+  ioncore-example.conf was missing
+
+2003-03-09 11:41 UTC  tuomov
+  * trunk: changeset 338
+  main.h was missing
+
+2003-03-09 11:39 UTC  tuomov
+  * trunk: changeset 337
+  Resize code was missing
+
+2003-03-09 11:34 UTC  tuomov
+  * trunk: changeset 336
+  Files were missing from last commit
+
+2003-03-09 11:24 UTC  tuomov
+  * trunk: changeset 335
+  - The query module was removed of dependencies to WIonFrame code and
+    is now a loadable module (query.so).
+  
+  - The 'query_workspace' command by default creates workspaces of the
+    first registered (module loaded) kind. Other kinds of workspaces can
+    be created by prefixing workspace name with the class name (WIonWS,
+    WFloatWS) and a colon, e.g. 'WFloatWS:foo'.
+
+2003-03-09 11:20 UTC  tuomov
+  * trunk: changeset 334
+  Tiled workspace and frame code (WIonWS, WIonFrame) modularised
+  (ionws.so) and generic frame and worksapce code moved to Ioncore.
+  Dependencies on the query module were also removed.
+
+2003-03-09 11:17 UTC  tuomov
+  * trunk: changeset 333
+  - Renamed wmcore ioncore.
+  
+  - Main configuration file name changed to 'ioncore.conf'.
+  
+  - Added to ioncore generic workspace and frame classes (WGenWS,
+    WGenFrame) on which modules' implementations are to be based.
+  
+  - Module initialization and deinitialization functions are now named
+    modulename_module_init and -deinit.
+  
+  - Added module version checking. Modules are now supposed to contain
+    the variable 'char modulename_module_ion_version[]=ION_VERSION;',
+    where ION_VERSION can be found in the top-level directory version.h.
+    Ioncore will refuse to load modules which have not set this variable
+    or the version is incorrect.
+  
+  - Resize size calculation should be fixed now.
+  
+  - The 'region_register_load_create_fn' interface was removed and
+    replaced with 'region_register_class'.
+  
+  - Region create and reparent functions now have parent type WWindow
+    because everything expected that anyway.
+  
+  - Workspace setups are now saved in ~/.ion-devel/saves/ to remove
+    clutter from ~/.ion-devel.
+  
+  - Some minor bugs were fixed.
+
+2003-03-09 11:06 UTC  tuomov
+  * trunk: changeset 332
+  Very preliminary and experimental (a lot is still missing) support for
+  PWM-like workspaces and frames: the floatws module.
+
+2003-03-09 11:05 UTC  tuomov
+  * trunk: changeset 331
+  Updated default configuration files. Binding configuration is now
+  divided into multiple module-specific files with some common bindings
+  in common-frame-bindins.conf. Some look configuration files were added
+  and the rest were also changed to reflect changes in the order frame
+  border colours and sizes are specified.
+
+2003-03-09 11:02 UTC  tuomov
+  * trunk: changeset 330
+  Removed ETCDIR setup
+
+2003-03-08 13:53 UTC  tuomov
+  * trunk: changeset 329
+  'make install' code moved from the toplevel Makefile to Makefiles in
+  subdirectories (etc, man, scripts).
+
+2003-03-08 13:53 UTC  tuomov
+  * trunk: changeset 328
+  'make install' code moved from the toplevel Makefile to Makefiles in
+  subdirectories (etc, man, scripts)
+
+2003-03-08 13:52 UTC  tuomov
+  * trunk: changeset 327
+  'make install' code moved from the toplevel Makefile to Makefiles in
+  subdirectories (etc, man, scripts).
+
+2003-03-06 19:22 UTC  tuomov
+  * trunk: changeset 326
+  Fixed a bug in do_fit_clientwin
+
+2003-03-06 19:20 UTC  tuomov
+  * trunk: changeset 325
+  Don't grab buttons that are only bound to an area (border, tab) of the
+  frame instead of the whole frame.
+
+2003-03-05 06:25 UTC  tuomov
+  * trunk: changeset 324
+  handle_configure_event fixed
+
+2003-03-02 15:43 UTC  tuomov
+  * trunk: changeset 323
+  Fixed a typo
+
+2003-03-02 15:21 UTC  tuomov
+  * trunk: changeset 322
+  Renamings and stuff
+
+2003-03-02 15:21 UTC  tuomov
+  * trunk: changeset 321
+  Some renamings that will break configuration files again.
+
+2003-03-02 15:20 UTC  tuomov
+  * trunk: changeset 320
+  Some clean-up
+
+2003-03-01 23:42 UTC  tuomov
+  * trunk: changeset 319
+  Screen setup sets manager for viewports
+
+2003-03-01 23:02 UTC  tuomov
+  * trunk: changeset 318
+  More focusing kludges
+
+2003-03-01 23:02 UTC  tuomov
+  * trunk: changeset 317
+  Comments added
+
+2003-03-01 22:08 UTC  tuomov
+  * trunk: changeset 316
+  foo
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030301
+
+2003-03-01 17:13 UTC  tuomov
+  * trunk: changeset 315
+  Added some locale checks
+
+2003-03-01 15:02 UTC  tuomov
+  * trunk: changeset 314
+  More utf8 kludges
+
+2003-03-01 12:08 UTC  tuomov
+  * trunk: changeset 313
+  Use iconv instead of libunicode
+
+2003-03-01 12:08 UTC  tuomov
+  * trunk: changeset 312
+  Use iconv instead of libunicode
+
+2003-02-28 23:17 UTC  tuomov
+  * trunk: changeset 311
+  missing font assert
+
+2003-02-28 19:15 UTC  tuomov
+  * trunk: changeset 310
+  FONT_HEIGHT -> MAX_FONT_HEIGHT
+
+2003-02-28 18:47 UTC  tuomov
+  * trunk: changeset 309
+  The 'transparent_background' draw.conf option now only applies to
+  empty frames. For client windows with a transparent background the
+  'transparent' winprop should be set to true for transparent frame
+  background.
+
+2003-02-28 18:17 UTC  tuomov
+  * trunk: changeset 308
+  More focus kludging
+
+2003-02-28 16:47 UTC  tuomov
+  * trunk: changeset 307
+  Misc cleanup
+
+2003-02-28 16:24 UTC  tuomov
+  * trunk: changeset 306
+  Preliminary support for UTF8. XFree86 (4.x) and libunicode are
+  required.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030225
+
+2003-02-28 15:27 UTC  tuomov
+  * trunk: changeset 305
+  Maybe focusing would work this time...
+
+2003-02-26 23:40 UTC  tuomov
+  * trunk: changeset 304
+  do_fit_clientwin fixed
+
+2003-02-25 19:29 UTC  tuomov
+  * trunk: changeset 303
+  More kludges in an attempt to fix focus handling
+
+2003-02-25 19:16 UTC  tuomov
+  * trunk: changeset 302
+  Support for optional autoconf-generated system-ac.inc. (The configure
+  script is not finished or included.)
+
+2003-02-24 14:13 UTC  tuomov
+  * trunk: changeset 301
+  Xft support fixed more
+
+2003-02-24 11:24 UTC  tuomov
+  * trunk: changeset 300
+  Xft support fixed
+
+2003-02-24 06:29 UTC  tuomov
+  * trunk: changeset 299
+  Oops
+
+2003-02-24 06:25 UTC  tuomov
+  * trunk: changeset 298
+  Added KP_Enter bound to 'finish' to query bindings
+
+2003-02-24 06:23 UTC  tuomov
+  * trunk: changeset 297
+  Fixed focus and grab handling when warping is not enabled
+
+2003-02-23 18:55 UTC  tuomov
+  * trunk: changeset 296
+  Added the command frame_close_if_empty and bound close command for
+  frames to this.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030223
+
+2003-02-23 18:54 UTC  tuomov
+  * trunk: changeset 295
+  Implemented "close" command for queries.
+
+2003-02-23 18:49 UTC  tuomov
+  * trunk: changeset 294
+  Fixed frame_close
+
+2003-02-23 14:25 UTC  tuomov
+  * trunk: changeset 293
+  Nested submaps are now fully implemented.
+
+2003-02-23 14:14 UTC  tuomov
+  * trunk: changeset 292
+  Minor mods
+
+2003-02-23 13:45 UTC  tuomov
+  * trunk: changeset 291
+  The command clientwin_toggle_fullscreen now works in both directions.
+  However, it should be noted that this toggle does not work well along
+  with client programs' full screen mode toggles. Some means of
+  communication should be devised.
+
+2003-02-23 13:16 UTC  tuomov
+  * trunk: changeset 290
+  Attempts at more focusing fixes
+
+2003-02-23 12:35 UTC  tuomov
+  * trunk: changeset 289
+  Changes to binding file
+
+2003-02-23 12:22 UTC  tuomov
+  * trunk: changeset 288
+  Added the compile-time option CF_SECOND_RATE_OS_FS to change colons to
+  underscores in display name part of configuration file names.
+
+2003-02-23 11:58 UTC  tuomov
+  * trunk: changeset 287
+  Preliminary support for workspace switching while dragging tabs.
+
+2003-02-23 02:40 UTC  tuomov
+  * trunk: changeset 286
+  Oops
+
+2003-02-23 02:35 UTC  tuomov
+  * trunk: changeset 285
+  Added saveload.h
+
+2003-02-23 00:38 UTC  tuomov
+  * trunk: changeset 284
+  Some changes and (hopefully) fixes to focusing policy
+
+2003-02-23 00:14 UTC  tuomov
+  * trunk: changeset 283
+  Submap with the same key can be specified in multiple sections now
+
+2003-02-22 21:48 UTC  tuomov
+  * trunk: changeset 282
+  Tab width calculation fixed
+
+2003-02-22 21:08 UTC  tuomov
+  * trunk: changeset 281
+  Workspace initialization stuff moved to wmcore
+
+2003-02-22 20:49 UTC  tuomov
+  * trunk: changeset 280
+  close_frame fixed
+
+2003-02-22 20:36 UTC  tuomov
+  * trunk: changeset 279
+  Some function names changed
+
+2003-02-22 17:44 UTC  tuomov
+  * trunk: changeset 278
+  Active client window commands can be accessed from other bindings with
+  the command 'commands_at_leaf'.
+
+2003-02-22 17:40 UTC  tuomov
+  * trunk: changeset 277
+  - Key binding setup changed.
+  
+  - X window -less regions no longer contain children. Instead the
+    regions "manage" these objects that share the parent object with the
+    managing object.
+  
+  - Removed clientwin_bindings and viewport_bindings sections.
+  
+  - More consistent and descriptive command names.
+  
+  - New workspace layout saving and loading code that supports arbitrary
+    objects instead of just frames and workspaces..
+
+2003-02-22 17:37 UTC  tuomov
+  * trunk: changeset 276
+  - Key binding setup changed.
+  
+  - X window -less regions no longer contain children. Instead the
+    regions "manage" these objects that share the parent object with the
+    managing object.
+
+2003-02-22 17:34 UTC  tuomov
+  * trunk: changeset 275
+  - Removed clientwin_bindings and viewport_bindings sections.
+  
+  - More consistent and descriptive command names.
+
+2003-02-22 17:32 UTC  tuomov
+  * trunk: changeset 274
+  modified system.mk
+
+2003-02-22 14:24 UTC  tuomov
+  * trunk: changeset 273
+  Resize size display should now be properly positioned on Xinerama
+  screens.
+
+2003-02-20 17:48 UTC  tuomov
+  * trunk: changeset 272
+  load_module searches the directories $LIBDIR and ~/.ion-devel/lib for
+  the module if the name contains no slashes.
+
+2003-02-20 14:59 UTC  tuomov
+  * trunk: changeset 271
+  Xft default compilation options changed in system.mk
+
+2003-02-17 20:34 UTC  tuomov
+  * trunk: changeset 270
+  Fixed region_do_find_new_home
+
+2003-02-16 17:30 UTC  tuomov
+  * trunk: changeset 269
+  Fixed alloc_defer
+
+2003-02-12 21:18 UTC  tuomov
+  * trunk: changeset 268
+  Colour-freeing fix
+
+2003-02-09 16:23 UTC  tuomov
+  * trunk: changeset 267
+  Free unused colours
+
+2003-02-09 15:57 UTC  tuomov
+  * trunk: changeset 266
+  Addresses updated on the man page
+
+2003-02-09 12:45 UTC  tuomov
+  * trunk: changeset 265
+  Support re-reading draw.conf (reread_draw_config) without restart
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030131-2
+
+2003-02-09 12:44 UTC  tuomov
+  * trunk: changeset 264
+  Changed broken_app_resize_kludge a bit. Should work a little better
+  now.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20030131
+
+2003-02-08 13:41 UTC  tuomov
+  * trunk: changeset 263
+  Minor fix
+
+2003-01-31 21:44 UTC  tuomov
+  * trunk: changeset 262
+  Changed hook linking order
+
+2003-01-31 18:16 UTC  tuomov
+  * trunk: changeset 261
+  Year changed to 2003
+
+2003-01-26 22:30 UTC  tuomov
+  * trunk: changeset 260
+  Changed -pedantic-errors to -pedantic in system.mk to get around
+  broken glibc headers
+
+2003-01-17 21:43 UTC  tuomov
+  * trunk: changeset 259
+  Transient mapping fix
+
+2003-01-09 00:29 UTC  tuomov
+  * trunk: changeset 258
+  Xft font names are now to be prefixed with 'xft:', otherwise normal
+  clear fonts are used. Xft support still is not compiled in by default.
+
+2003-01-09 00:01 UTC  tuomov
+  * trunk: changeset 257
+  minor fixes
+
+2003-01-08 15:25 UTC  tuomov
+  * trunk: changeset 256
+  small changes
+
+2003-01-05 23:48 UTC  tuomov
+  * trunk: changeset 255
+  Removed an unnecessary debug fprintf
+
+2003-01-05 06:53 UTC  tuomov
+  * trunk: changeset 254
+  Tab dragging routines can now handle nested frames.
+
+2003-01-04 22:02 UTC  tuomov
+  * trunk: changeset 253
+  Submap handling fix
+
+2003-01-04 21:39 UTC  tuomov
+  * trunk: changeset 252
+  CF_STUBBORN_TRESH fix
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20021229
+
+2003-01-03 22:58 UTC  tuomov
+  * trunk: changeset 251
+  sample.conf micro->milli
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20021219
+
+2002-12-30 23:33 UTC  tuomov
+  * trunk: changeset 250
+  Send more ConfigureNotify events to fix slow startup times of some
+  programs
+
+2002-12-29 02:22 UTC  tuomov
+  * trunk: changeset 249
+  Configurable regular expression based window title shortening rules
+
+2002-12-18 23:04 UTC  tuomov
+  * trunk: changeset 248
+  Fixed a potential memory leak
+
+2002-12-18 22:51 UTC  tuomov
+  * trunk: changeset 247
+  kludges.conf updated
+
+2002-12-18 22:50 UTC  tuomov
+  * trunk: changeset 246
+  Winprop matching improvements: WM_WINDOW_ROLE support and
+  configuration format changed to 'winprop "class", "role", "instance" {
+  ... }'.
+
+2002-12-14 17:55 UTC  tuomov
+  * trunk: changeset 245
+  quote_next returns
+
+2002-12-03 22:32 UTC  tuomov
+  * trunk: changeset 244
+  Some extra abstraction to Xft support code
+
+2002-12-03 22:19 UTC  tuomov
+  * trunk: changeset 243
+  Applied Xft support patch
+
+2002-11-21 17:58 UTC  tuomov
+  * trunk: changeset 242
+  Web page and email address updated
+
+2002-11-14 23:00 UTC  tuomov
+  * trunk: changeset 241
+  Transient window height restrictions lifted
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20021104
+
+2002-11-13 00:00 UTC  tuomov
+  * trunk: changeset 240
+  config.h wasn't included at the proper point in binding.c
+
+2002-11-08 00:14 UTC  tuomov
+  * trunk: changeset 239
+  Fixed pointer warping on workspace change when warps are disabled
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20021103
+
+2002-11-04 13:37 UTC  tuomov
+  * trunk: changeset 238
+  Fixed a segfault problem with symlists
+
+2002-11-03 04:33 UTC  tuomov
+  * trunk: changeset 237
+  Fixed a drawing problem with ion_bar_inside_frame FALSE
+
+2002-11-03 02:44 UTC  tuomov
+  * trunk: changeset 236
+  Added transparent_background (TRUE/FALSE) draw.conf configuration
+  option
+
+2002-11-03 02:33 UTC  tuomov
+  * trunk: changeset 235
+  Bound broken_application_resize_kludge to Mod1+L
+
+2002-11-03 02:26 UTC  tuomov
+  * trunk: changeset 234
+  Added broken_app_resize_kludge function
+
+2002-11-03 02:15 UTC  tuomov
+  * trunk: changeset 233
+  Ad hoc fix for clientwin enter window events
+
+2002-11-02 22:57 UTC  tuomov
+  * trunk: changeset 232
+  Other minor fixes
+
+2002-11-02 22:57 UTC  tuomov
+  * trunk: changeset 231
+  Line editor history scrolling fixed
+
+2002-11-02 22:21 UTC  tuomov
+  * trunk: changeset 230
+  Default (black&white) colour scheme changes
+
+2002-11-02 22:14 UTC  tuomov
+  * trunk: changeset 229
+  Fullscreen windows on separate Xinerama screens should be focused
+  properly now.
+
+2002-11-02 22:03 UTC  tuomov
+  * trunk: changeset 228
+  Fixed a problem concerning resizing of frames containing hidden
+  "acrobatic" windows
+
+2002-11-02 15:54 UTC  tuomov
+  * trunk: changeset 227
+  Added handle_event_alt "alternative hook" for modules that want to
+  handle X events directly.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020926
+
+2002-10-27 22:40 UTC  tuomov
+  * trunk: changeset 226
+  Screen-based configuration file selection fixed
+
+2002-10-15 18:08 UTC  tuomov
+  * trunk: changeset 225
+  Fixed a problem with query boxes and wheel mice
+
+2002-09-25 21:15 UTC  tuomov
+  * trunk: changeset 224
+  Added the function goto_named_region (replaces missing
+  goto_client_name)
+
+2002-09-25 11:53 UTC  tuomov
+  * trunk: changeset 223
+  Added manual tab-ordering functions frame_move_current_tab_left/right
+
+2002-09-25 07:26 UTC  tuomov
+  * trunk: changeset 222
+  Fixed pointer warping on workspace change
+
+2002-09-15 09:36 UTC  tuomov
+  * trunk: changeset 221
+  Minor fixes
+
+2002-09-15 09:36 UTC  tuomov
+  * trunk: changeset 220
+  Added a few missing characters to workspace configuration loading code
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020819
+
+2002-09-13 11:35 UTC  tuomov
+  * trunk: changeset 219
+  Minor fix
+
+2002-08-22 21:13 UTC  tuomov
+  * trunk: changeset 218
+  Self-pointing transient_for hint problem fixed
+
+2002-08-18 17:09 UTC  tuomov
+  * trunk: changeset 217
+  Initial focus changes
+
+2002-08-18 16:51 UTC  tuomov
+  * trunk: changeset 216
+  Tagging restored (frame functions: toggle_sub_tag, attach_tagged and
+  global function clear_tags)
+
+2002-08-12 20:55 UTC  tuomov
+  * trunk: changeset 215
+  Unused code commented out
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020606
+
+2002-07-06 21:59 UTC  tuomov
+  * trunk: changeset 214
+  Function completion in query_function restored
+
+2002-06-14 22:36 UTC  tuomov
+  * trunk: changeset 213
+  Tab drag assertion fixed
+
+2002-06-05 23:10 UTC  tuomov
+  * trunk: changeset 212
+  "Watches" added in pointing device code
+
+2002-06-05 22:51 UTC  tuomov
+  * trunk: changeset 211
+  Minor clean-up
+
+2002-06-05 20:56 UTC  tuomov
+  * trunk: changeset 210
+  Target ID table bugfix
+
+2002-06-05 20:56 UTC  tuomov
+  * trunk: changeset 209
+  Split bugfix
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020604
+
+2002-06-05 14:03 UTC  tuomov
+  * trunk: changeset 208
+  Old window_press restored: mouse actions directed to the client window
+  associated with a tab should work now.
+
+2002-06-03 23:07 UTC  tuomov
+  * trunk: changeset 207
+  Some minor clean-up
+
+2002-06-03 22:50 UTC  tuomov
+  * trunk: changeset 206
+  Makefile fix concerning patch and bindings-sun.conf
+
+2002-06-03 22:39 UTC  tuomov
+  * trunk: changeset 205
+  Preliminary Xinerama support
+
+2002-06-03 15:11 UTC  tuomov
+  * trunk: changeset 204
+  Another key binding related segfault fix
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020531
+
+2002-06-01 00:25 UTC  tuomov
+  * trunk: changeset 203
+  Fixed segfault problem with multihead displays
+
+2002-05-31 06:00 UTC  tuomov
+  * trunk: changeset 202
+  Fixed lockup when a window's title ends in its only colon (and spaces)
+  but even just the three dots and instance number are too long to fit
+  in the tab.
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020529
+
+2002-05-30 19:30 UTC  tuomov
+  * trunk: changeset 201
+  Fixed segfault when unbound key was pressed in submap mode
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020528
+
+2002-05-30 17:04 UTC  tuomov
+  * trunk: changeset 200
+  Fixed 'switch_tab'
+
+2002-05-29 06:51 UTC  tuomov
+  * trunk: changeset 199
+  - Fixed wscurrent callback handlers
+  
+  - Fixed query_function error reporting
+
+2002-05-28 21:49 UTC  tuomov
+  * trunk: changeset 198
+  fixed the fix
+
+2002-05-28 21:46 UTC  tuomov
+  * trunk: changeset 197
+  - Lifted restriction on nested command sequences (now max 32)
+  
+  - More fixes and temporary kludges
+
+2002-05-28 20:08 UTC  tuomov
+  * trunk: changeset 196
+  New binding configuration file
+
+2002-05-28 20:05 UTC  tuomov
+  * trunk: changeset 195
+  regbind.* were missing
+
+2002-05-28 19:59 UTC  tuomov
+  * trunk: changeset 194
+  A few comments
+
+2002-05-28 19:59 UTC  tuomov
+  * trunk: changeset 193
+  Added a notice of out-of-date information to documentation
+
+2002-05-27 21:54 UTC  tuomov
+  * trunk: changeset 192
+  Resize problem fixed
+
+2002-05-26 23:03 UTC  tuomov
+  * trunk: changeset 191
+  Binding callbacks are now entirely command sequence based
+
+2002-05-26 14:40 UTC  tuomov
+  * trunk: changeset 190
+  - Key binding management revised: all X server key grabs are made on
+    the root window and the innermost window with an internal grab gets
+    to receive the events.
+  
+  - Key binding configuration changed: clientwin, screen and frame
+    sections added.
+  
+  - Preliminary full screen client window support:
+    'clientwin_enter_fullscreen' function (no toggle yet) and auto
+    detection (MWM decoration hints set to none when a configure request
+    with width and height set to those of the screen is received).
+
+2002-05-26 14:27 UTC  tuomov
+  * trunk: changeset 189
+  Query updated to new binding model
+
+2002-05-26 14:27 UTC  tuomov
+  * trunk: changeset 188
+  Bindings updated to new binding model
+
+2002-05-21 18:49 UTC  tuomov
+  * trunk: changeset 187
+  Fixed workspace switching on restart
+
+2002-05-21 18:39 UTC  tuomov
+  * trunk: changeset 186
+  Fixed split_empty
+
+2002-05-19 22:30 UTC  tuomov
+  * trunk: changeset 185
+  Title updating fixed
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020510
+
+2002-05-19 22:26 UTC  tuomov
+  * trunk: changeset 184
+  Maximize fixed
+
+2002-05-11 00:09 UTC  tuomov
+  * trunk: changeset 183
+  My email address changed
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020414
+
+2002-05-10 19:33 UTC  tuomov
+  * trunk: changeset 182
+  transient_mode winprop returns
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020411
+
+2002-04-20 20:59 UTC  tuomov
+  * trunk: changeset 181
+  Main loop select() support for multiple file descriptors.
+
+2002-04-12 20:01 UTC  tuomov
+  * trunk: changeset 180
+  The function query_renameframe was added and names of frames are saved
+  now.
+
+2002-04-11 21:31 UTC  tuomov
+  * trunk: changeset 179
+  Some minor glitches like missing includes fixed -- strict compiler
+  warning flags enabled by default again.
+
+2002-04-11 20:51 UTC  tuomov
+  * trunk: changeset 178
+  Command sequence stuff moved to wmcore/
+
+2003-12-23 20:17 UTC  unknown
+  tagged ion-devel-20020405
+
+2002-04-11 15:24 UTC  tuomov
+  * trunk: changeset 177
+  'target' winprop for specifying named workspaces (or any named object
+  with region_attach_sub) as attachment targets for client windows.
+
+2002-04-11 15:06 UTC  tuomov
+  * trunk: changeset 176
+  Added the region_ws_attach_clientwin/region_ws_attach_transient
+  interface that workspace-like objects should implement.
+
+2002-04-04 23:42 UTC  tuomov
+  * trunk: changeset 175
+  oldChangeLog changed
+
+2005-02-15 18:06 UTC  tailor@f281.ttorni.ton.tut.fi
+  * Tailorization of trunk
+  Import of the upstream sources from the repository
+  
+   http://tao.uab.es/ion/svn/ion/trunk
+  
+  as of revision 173
+  
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..cf9b6b9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  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 Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            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 Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+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 other code 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.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+^L
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  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, whereas the latter must
+be combined with the library in order to run.
+^L
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser 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.
+^L
+  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.
+^L
+  6. As an exception to the Sections above, you may also combine 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) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) 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.
+
+    d) 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.
+
+    e) 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 materials to be 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.
+^L
+  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 with
+this License.
+^L
+  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 Lesser 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.
+^L
+  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
+^L
+           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 Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  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..6f7c06a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,36 @@
+##
+## Ion Makefile
+##
+
+# System-specific configuration is in system.mk
+include build/system-inc.mk
+
+# List of modules
+include modulelist.mk
+
+######################################
+
+INSTALL_SUBDIRS=\
+       $(MODULE_LIST) \
+       ioncore ion pwm etc utils man po \
+       
+
+SUBDIRS = $(LIBS_SUBDIRS) $(INSTALL_SUBDIRS)
+
+DOCS = README LICENSE ChangeLog RELNOTES
+
+TO_REALCLEAN = build/ac/system-ac.mk
+
+POTFILE=po/ion.pot
+
+######################################
+
+include build/rules.mk
+
+######################################
+
+_install:
+       $(INSTALLDIR) $(DOCDIR)
+       for i in $(DOCS); do \
+               $(INSTALL) -m $(DATA_MODE) $$i $(DOCDIR); \
+       done
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..62684ee
--- /dev/null
+++ b/README
@@ -0,0 +1,137 @@
+
+Ion
+===
+
+Copyright (c) Tuomo Valkonen 1999-2006.
+
+tuomov at iki.fi
+
+<http://iki.fi/tuomov/ion/>
+          
+
+Building and installing
+-----------------------
+
+1. Make sure you have the following tools and libraries installed:
+
+    * GNU make
+    * Lua 5.1 (see <http://www.lua.org/>). 
+
+2. a) Edit `system.mk` to suit your system. Most GNU/Linux users should
+      need very little modifications to `system.mk`.
+
+   b) Alternatively, go to the directory `build/ac`, run `autoreconf`
+      and then `./configure`. Switch back to the top directory afterwards.
+      See `build/ac/README.autoconf` for details on parameters, especially 
+      if you are using the Debian Lua packages. If you try this method and 
+      it fails for some reason, please make sure `build/ac/system-ac.mk` 
+      does not exist before falling back to alternative a). Also in such
+      a case, find someone else to complain to; I (tuomov) will have 
+      nothing to do with autoconf.
+
+3. If you want to build some extra modules now or do not want to build
+   some of the standard modules, edit `modulelist.mk`.
+   
+4. Run `make`. Note that `make` here refers to GNU make which is usually
+   named `gmake` on systems with some other implementation of make as 
+   default.
+   
+5. Run `make install`, as root if you set `$PREFIX` in `system.mk` to a 
+   directory that requires those privileges.
+   
+   YOU SHOULD NOT SKIP THIS STEP unless you know what you are doing. Ion
+   will refuse to start if it can not find all the necessary uncorrupt
+   configuration files either in `$PREFIX/etc/ion3/` or in `~/.ion3/`.
+
+6. How to best set up `startx` or whatever to start Ion instead of your
+   current window manager depends on your system's setup. A good guess
+   is creating or modifying an executable shell script `.xsession` in your
+   home directory to start Ion. This should usually (but not always) work
+   if you're using some X display/login manager. If `~/.xsession` does not 
+   help and you're not using a display manager, modifying `~/.xinitrc` or 
+   creating one based on your system's `xinitrc` (wherever that may be; 
+   use locate) may be what you need to do. Note that unlike `.xsession`, 
+   a `.xinitrc` should usually do much more setup than simply start a few
+   programs of your choice.
+
+Please see the file `RELNOTES` for additional release-specific installation
+and configuration notes.
+
+
+Some optional installation steps
+--------------------------------
+
+1. The F5 and F6 keys expect to find the program `run-mailcap` to select
+   a program to view a file based on its guessed MIME type. Unless you are
+   using Debian, most likely you don't have it, but any other similar 
+   program (or just plain old text editor) will do as well - just the bindings
+   in `cfg_bindings.lua`. Of course, if you don't want to use the feature at 
+   this time or never, you may simply skip this step. If you want to use 
+   `run-mailcap`, it can be found from the following address, as a source 
+   tarball as well:
+   
+       <http://www.debian.org/Packages/unstable/net/mime-support.html>
+
+2. Ion supports caching known man-pages in a file for faster man-page
+   completion in the F1 man page query. To enable this feature, you must
+   periodically run a cronjob to build this list. To create a system-wide
+   man page cache, run `crontab -e` (might vary depending on platform) as
+   root and enter a line such as follows:
+
+        15 05 * * * $SHAREDIR/ion-completeman -mksyscache
+
+   Replace `$SHAREDIR` with the setting from `system.mk` (or `system-ac.mk`
+   if you used autoconf). This example runs daily at 05:15, but you may 
+   modify the  run times to your needs; see the crontab manual. 
+   
+   If you can't or do not want to build a system-wide man page cache, run
+   `crontab -e` as your normal user and replace `-mksyscache` with
+   `-mkusercache` above. The cache file will be `~/.ion3/mancache`.
+   
+   It may also be useful to run `ion-completeman` with the suitable
+   `-mk*cache` argument once manually to build the initial cache.
+   
+   If the `MANPATH` environment variable is not set on your system and it
+    does not have the `manpath` command (or it does not print anything 
+   sensible), you may also want to set the `ION_MC_MANPATH` environment
+   variable to the list of paths where the system stores manual pages.
+
+
+Configuration
+-------------
+
+For help on modifying Ion's configuration files, PLEASE READ THE DOCUMENT
+"Configuring and extending Ion3 with Lua" available from the Ion web page,
+listed at the top of this file.
+
+
+Questions, comments, problems?
+------------------------------
+
+If the available documentation does not answer your question, please
+post it to the mailing list. Details can be found on the Ion web page
+listed at the top of this file.
+
+
+Credits
+-------
+
+Ion was written by Tuomo Valkonen.
+
+The autoconf script written by Tom Payne for the most part.
+
+The dock module was written by Tom Payne and Per Olofsson.
+
+`share/ion-completefile/ion-completefile.c` is based on editline, (c)
+1992 Simmule Turner and Rich Salz. See the file for details.
+
+The code that `de/fontset.c` is based on was apparently originally written 
+by Tomohiro Kubota; see
+<http://www.debian.org/doc/manuals/intro-i18n/ch-examples.en.html#s13.4.5>.
+
+Various patches have been contributed by other individuals unlisted here.
+See the mailing list archives and the darcs source repository history at
+<http://iki.fi/tuomov/repos/>. For translators see the individual `.po`
+files in `po/`.
+
+See `libtu/README` for code by others integrated into libtu.
diff --git a/RELNOTES b/RELNOTES
new file mode 100644 (file)
index 0000000..589ab54
--- /dev/null
+++ b/RELNOTES
@@ -0,0 +1,1048 @@
+
+ion-3ds-20061223
+----------------
+
+There's nothing major in this release, primarily just some minor fixes 
+and tuning to the previous release, that it was time to release.
+
+
+ion-3ds-20061029
+----------------
+
+Mostly this release still fixes issues in the big 3ds-20061015 release,
+but in addition there are some improvements in the query department:
+
+ * Query activation key now cycles completions (So e.g. the
+   in the context menu activated with META+M, this same key
+   can be used to cycle through the alternatives.) This does
+   not work for queries activated by submap bindings.
+
+ * Likewise, it is no longer necessary to specify the key to
+   use for cycling for `mod_menu.grabmenu`.
+
+ * Control+R can now be used for history completion in queries.
+   (Currently matching is done for full string up to 'point', but 
+   this may be changed to substring match.)
+
+ * Note that the parametrisation of WEdln.complete has changed,
+   and the second cycle parameter must be 'next' now instead of
+   `true`.
+
+
+ion-3ds-20061020
+----------------
+
+Fixes some (expected, but minor) issues in the previous release.
+
+
+ion-3ds-20061015
+----------------
+
+ * WIonWS and WFloatWS and the corresponding modules are gone, and
+   your custom configuration files will be broken with regard to these.
+   However, a partial backwards compatibility hack exists for layout 
+   savefiles.
+
+   The F9 and META-F9 bindings now by default create workspaces with
+   a tiled layout of two frames. To create an "empty" workspace,
+   corresponding to the old WFloatWS, use the context menu (META-M)
+   and choose "new-empty-workspace". It is also possible to change 
+   the default layout.
+
+ * Note that if you restart from an old version to this new version of Ion,
+   transients will stop working as expected for already existing windows.
+   They will work for newly-created windows, however.
+
+ * There are a few new sets of binding (including one for `WClientWin`!),
+   and some old bindings may not work exactly as expected anymore. In
+   particular, those for switching to full-screen mode. 
+
+   **It is probably best to start from scratch with your custom 
+     bindings.**
+
+ * `WFrame.set_tabbar` is gone. If you absolutely want to get rid off 
+   the tabs, you must change the frame's "mode" with `WFrame.set_mode`. 
+   The mode "tiled-alt" has been intended for this, and the corresponding
+   "framed-tiled-alt" style defaults to `bar = "none"`.
+
+ * The rather popular `detach.lua` script from the scripts repository
+   is obsoleted now, as Ion includes detach functionality in itself.
+   To detach a window, use META-K D in the default bindings. To tile
+   an existing frame from a workspace that doesn't have a tiling yet,
+   use META-K B.
+   
+
+ion-3ds-20060519
+----------------
+
+Some notable changes in this release include
+
+ * Lua 5.1 is now required.
+
+ * Framed transients on by default now. New binding contexts
+   "WFrame.toplevel" and "WMPlex.toplevel" were added to allow
+   for separate sets of bindings for nested transient frames and
+   top-level frames. Some of the bindings in the default binding
+   maps that are likely to be unwanted on transient frames were
+   moved to these contexts. Old custom bindings will continue to 
+   work unless they modify the defaults by unbinding some of the
+   moved bindings.
+
+ * Pressing Mod1+K K in the default bindings now switches to any
+   region with the "activity" flag set (indicated by the a box
+   at a corner of the screen), if there's one, before cycling to 
+   previously active region. The same effect can be achieved in
+   your custom bindings with
+
+        ioncore.activity_goto() or ioncore.goto_previous()
+
+
+ion-3ds-20050607
+----------------
+
+This release mostly features minor bugfixes and other improvements. The most
+visible non-bugfix changes are:
+
+ * Faster "fontset" filling kludge: only `-misc-fixed-*` fonts are tried. Ion
+   should now load faster in UTF-8 environments, and usually with no less
+   fonts in the fontset than before. (UTF-8 string drawing still does not
+   fully utilise these fonts under XFree86, but it does under XOrg.)
+
+ * Experimental auto-show-completions support, which is also on by default
+   now. In this mode the Tab key can be used to cycle forward through the
+   completions, and Shift+Tab backwards. Modify the settings seen in the 
+   new `mod_query.lua` to get normal Tab-completion, or change the completion
+   delay.
+
+ * The release scripts do not run automatically autoconf anymore so maybe
+   a few more people would look into the README first.
+
+
+ion-3ds-20050322
+----------------
+
+The most visible changes in this release are:
+
+ * Mod1+space now toggles the scratchpad by default and Mod1+D the dock.
+
+ * The `ion-(ssh|man|view|edit)` wrapper scripts were removed and instead
+   the programs used can be configured in Ion's configuration files.
+
+ * `ioncore.exec(_on)` also support the `:cmd` notation for "run cmd in 
+   xterm" familiar from the run query. The `::cmd` notation can be used 
+   to ask for enter to be pressed even when the program quit succesfully.
+
+ * Those with custom configuration files should note that many exported toggle
+   functions were changed and renamed, and now accept a string parameter
+   incidating whether to toggle, set or unset the property.
+  
+    - `WClientwin.set_fullsreen` (replaces `WClientWin.toggle_fullscreen`)
+    - `WRegion.set_tagged` (replaces `WRegion.tag/untag/toggle_tag`)
+    - `WFrame.set_tabbar` (replaces `WFrame.toggle_tabbar`)
+    - `WFrame.set_shaded` (replaces `WFrame.toggle_shade`)
+    - `WFloatFrame.set_sticky` (replaces `WFloatFrame.toggle_sticky`)
+    - `WMPlex.l2_set_hidden` (replaces `WMPlex.l2_hide/show`)
+    - `mod_sp.set_shown(_on)` (replaces `mod_sp.toggle(_on)`)
+    - `mod_dock.set_floating_shown_on` (replaces `mod_dock.toggle_floating_on`)
+    - `WRegion.set_activity` (replaces `WRegion.clear_activity` and 
+      `WRegion.notify_activity`)
+      
+   For example, `WRegion.set_tagged(_, 'toggle')` should be used in place of
+   `WRegion.toggle_tagged(_)` now.
+
+Obviously there are some other changes and fixes too. See the changelog
+for details as usual.
+
+
+ion-3ds-20050304
+----------------
+
+This is mostly a bug fix release, but also features improved
+`mod_query.query_exec` (F3 key) completion support. Script writers should be
+aware that As a side effect of one of these bug fixes, many hooks are now 
+called in "protected mode" and can not call any functions that modify the 
+internal state of Ion, except ioncore.defer.
+
+
+ion-3ds-20050227
+----------------
+
+The most important changes in this release are:
+
+ * So-called "placeholders". With the help of these the positions of
+   full-screen windows are remembered in their original frames, and don't
+   just get inserted after currently active window when returning from 
+   full-screen mode. Under a session manager placeholders are also used
+   to remember the original order of windows.
+
+ * Improved `mod_statusbar` and `ion-statusd` communication, and colouring
+   of important/critical meters.
+
+ * A number of small fixes.
+
+
+ion-3ds-20041104
+----------------
+
+This monthly snapshot adds a few new and improved features.
+
+ * 'Grabbed menus' that have a single cycling key and activate selected
+   entry when all modifiers have been released. See
+
+   <http://iki.fi/tuomov/repos/ion-scripts-3/scripts/wcirculate.lua>
+   
+   for an application.
+
+ * Potentially blocking status meters are now in a separate ion-statusd
+   program. Please write your additional status meters that do not monitor
+   the state of Ion itself for ion-statusd (and contribute them in the
+   Ion3 scripts repository at <http://iki.fi/tuomov/repos/ion-scripts-3/>).
+   For help on writing such status meters, see e.g. source for
+   `statusd_load` in `ext_statusbar/ion-statusd`.
+
+ * Floating splits are now supported on plain tiled workspaces as well as
+   on pane workspaces. To create such a split, use the workspace context
+   menu (Mod1+M by default) or write your own bindings.
+
+ * Line editor now supports history search; Control+Up/Down only scrolls
+   through history entries with matching initial part.
+
+ * Arbitrary winprop matching criteria is supported. Lua scripts have
+   access to X properties.
+Of course there are some other minor fixes and improvements as well.
+
+
+ion-3ds-20040906
+----------------
+
+This release finally includes a usable (yet still incomplete) version
+of the `mod_panews` workspace module (that has also bored the name 'autows'
+and 'rubberws' previously). The final outcome is not exactly what I
+initially planned, as those plans turned out not to be that workable.
+Instead, what `mod_panews` does is add to the basic tiled ionws approach
+overlappable splits and automatically filled panes that are initially
+empty. Each window is classified and assigned to the pane matching that
+classification. (The default classifications are T(erminal), V(iewer)
+and M(iscellaneous).)
+
+For a better feel for it, try it out yourself and please give feedback.
+The overlapping panes may be a bit confusing (this is one place where
+true translucency might actually be useful and not just eyecandy) at
+first, but you'll get used to them if you use the feature, and e.g.
+the Gimp works quite splendidly with the toolboxes in the 'M' pane and
+image windows in the 'V' pane --  although the simple initial size-based
+classification heuristics don't always get it right and overriding
+winprops (setting: `panews_classification`) should eventually added. 
+
+In addition to the new module, this release adds support for translations
+of program messages and the manual page, regardless of whether such is
+of any use in a program like Ion or not. (Currently Finnish and Czech
+translations are available.) Of course there are some bug fixes and
+other minor additions as well, and the `./configure` script is also back,
+the abandonment of libtool being final now. 
+
+
+ion-3ds-20040730
+----------------
+
+The first thing you'll notice when you start up this release of Ion is
+that isn't reading your old configuration that. The next thing you 
+should notice is a neat layout-adapting statusbar at the bottom of the
+screen. That's right, Ion now includes an `ext_statusbar` Lua script that
+is enabled by default and displays date/time/load/mail count in a
+configurable format.
+
+So why is it not reading your configuration and save files? Firstly,
+all of the `.lua` files were renamed to be indicative of their purpose.
+Secondly, there have been so many changes that your old files would be
+incompatible anyway.
+
+The `.lua` files are now named as follows:
+
+    cfg_*.lua    Configuration file that the user may wish to edit
+    look_*.lua   Drawing engine style file
+    saved_*.lua  Save file
+    mod_*.lua    Module stub loader
+    ext_*.lua    A bigger Lua extension without a C counterpart
+                 so that it is not a module
+                 
+The configuration file for `mod_foobar` or `ext_foobar`, if it has one, is,
+of course, `cfg_foobar.lua`. In the topic of file names, also note that 
+the default installation paths and binary names have changed to include
+the component '3' to reflect the situation with many binary packages
+of Ion.
+
+You perhaps noticed above that modules have stub loaders now, so the 
+user has no need to use the `ioncore.load_module` routine. All extensions
+and additional configuration files can now be loaded  with 'dopath'
+(used to be 'include'). Also, the `menulib`, `querylib`, and `ioncorelib`
+Lua libraries are gone and instead their contents can be found in the same 
+`mod_whatever` namespace with the corresponding module (the "stub" loader
+for these modules is a bit more than just a stub...). Some may also
+want to know that for `mod_*` and `ext_*` only the compiled .lc files are
+now installed, and not the source `.lua` files, thus removing redundant
+files and making the installation slightly smaller.
+
+There are few other changes to the contents of the configuration files
+as well, so you're probably best off simply rewriting your modified
+configuration files based on the new defaults. There quite likely won't
+be any more _big_ changes to the configuration files before the release
+of final Ion3, wherever that will be. (Most likely we won't see 3rc:s
+yet this year.) However, some functions and variables are still likely
+to be renamed or changed.
+
+In addition to being renamed, the layout savefile of this Ion release
+is incompatible with older releases. This is due to the changes made
+to the `WIonWS` (and `WAutoWS`) split tree code to make it more modularly
+extensible with the new kinds of nodes that `WAutoWS` requires.
+
+The `mod_autows` module has infact been through many changes since the 
+last release, and I think I have the final form of it finally figured
+out. However, it is still far from finished and unlikely to be ready 
+for use yet.
+
+
+ion-3ds-20040703
+----------------
+
+The major new features of this snapshot release are:
+
+ * `WMPlex` support for a sticky status display area to which `WIonWS`s
+   adjust properly. Modified to the dock module to support this method. 
+   See the new `dock.lua` to set up the dock in the new embedded or old 
+   floating manner (the API for the latter has also changed).
+ * Primitive session management support.
+
+There are also some bug fixes and many minor improvements; see the ChangeLog
+for details. Work on `mod_autows` has also started, but it isn't ready for 
+use yet.
+
+### Note on ./configure:
+
+./configure does not exist for the moment as the source autoconf script
+is not up-to-date. Ion no longer uses libtool/libltdl due to problems
+with inter-module dependencies, and I have not asked Tom Payne to fix
+the script yet, as this change may not be final (although that is most
+likely the case; those without Linux-compatible libdl and a flexible
+binary format such as ELF will just have to link statically against the
+modules).
+
+
+### Notes on session management:
+
+Ion loads `mod_sm` automatically when the SESSION_MANAGER environment 
+variable that should be set by the session manager is set, so there's 
+no need load it in `ion.lua`. 
+
+When session management is in use, all entries in the 'Session' menu
+(previously 'Exit' menu) actually invoke the session manager to do the
+task. 'Save' asks the SM save the whole session and 'Exit' (ioncore.shutdown)
+asks it to shut down the session instead of just causing the WM to quite.
+To do the latter, use ioncore.resign.
+
+Unfortunately, all session managers I have tried (from Debian/unstable), 
+are broken/incomplete in one way or another:
+
+
+xsm: doesn't support any requests from applications. This makes Ion's
+session menu complete unfunctional. The only way to restart/exit/save
+state is through xsm's window.
+
+
+gnome-session: This seems the most complete of the all the session managers
+and works fine until it is requested to shut down the session, when it dumps
+core and session state is lost if was not explicitly saved previously.
+
+A `~/.gnome2/session` file to use with Ion follows:
+
+    [Default]
+    num_clients=2
+    0,id=default0
+    0,Priority=0
+    0,RestartCommand=gnome-smproxy --sm-client-id default0
+    1,id=default1
+    1,Priority=10
+    1,RestartCommand=/usr/local/ion-3/bin/ion -smclientid default1
+
+
+ksmserver: Only supports a global shutdown request, so that Ion can not be
+restarted or session state saved in the middle of a session.
+
+
+ion-3ds-20040316
+----------------
+
+This is the first development snapshot release of what is to be Ion3. 
+The most visible changes to Ion2 so far are:
+
+  * Default installation directory is `/usr/local/ion-3` while user
+    configuration files go in `~/.ion3`
+
+  * The `mod_sp` module was added. It creates an extra toggleable 
+    "scratchpad" frame on each screen. Toggle is for now bound by default 
+    to Mod1+section, which should be very conveniently located on most 
+    Nordic keyboards (left of "1"), but you may have to change it. The 
+    scratchpad should be nice for xconsole or similar monitors, xmessage 
+    and other popups and just as temporary holding space for windows.
+
+  * All modules except the drawing engine are now called `mod_something`.
+
+  * Man-page complection supports a cache of known man-pages for faster
+    completion. See the README for instructions on setting up a cronjob
+    or manually generating the index if you want to use the feature.
+    Also `query_man_path` is no longer used. Instead we try the 
+    `ION_MC_MANPATH` and `MANPATH` environment variables and the 
+    'manpath' program.
+
+  * Exported functions are now separated into tables (namespaces)
+    instead of cluttering the globals table. Some frequently used
+    configuration functions are imported into the globals table, though.
+    Some functions have also been removed or renamed for simpler and
+    more consistent function set.
+
+  * New binding configuration mechanism. Dozens of `*_bindings` functions
+    were replaced with a single `[ioncorelib.]defbindings` function that
+    accepts a context parameter. Callbacks are specified as strings
+    (although passing functions still works) to make it easier for
+    external configuration programs to understand the configuration 
+    files and perhaps remove some confusion among users who do not
+    care to read a tiny bit of Lua documentation and understand the
+    concept of anonymous functions.
+
+  * It is now also possible to retrieve a list of bindings with
+    `ioncorelib.getbindings`, for example, for self-documentation.
+
+  * Single move/resize mode bindings instead of separate for both
+    ionframe and floatframe.
+
+There are also quite a few internal changes; see the ChangeLog for
+details. No documentation tarball is available at the moment as the
+documentation is out-of-date except for the function reference. If 
+you need the reference, just checkout the documentation from the 
+Subversion repository with
+
+    svn co http://tao.uab.es/ion/svn/ion-doc/trunk ion-doc-3ds
+
+and build the documentation.
+
+
+ion-2rc-20040114
+----------------
+
+This release is finally what can be called "Ion 2 release candidate #1". 
+No more new features will be added to "Ion 2" after this release, and the
+configuration interface has already been frozen for a while. I will wait a
+couple of weeks for bug reports, and if nothing serious is found, the new
+stable Ion should finally be released then.
+
+The most notable changes since the previous release are:
+
+  * The dock module is included
+  * An optional autoconf script was added
+  * A few minor bugs were fixed
+  * Some incomplete features were polished, especially focus control on
+    floatws, and:
+  * Changes in X keyboard map are supported now (so e.g. switching to 
+    dvorak after Ion has started should update the bindings to match the
+    locations of symbols in the dvorak layout).
+
+
+About the version numbering scheme:
+
+Due to a demand of a version numbering scheme more indicative of the status
+of the project, I was thinking of various different new version numbering 
+schemes for this release: ion-2rc1 (then 2r1, 3d1, etc.), ion-2-20040114-rc1, 
+ion-2.20040114rc1, ion-20040114-2rc1, etc.). It would've been nice if 
+simple  lexicographical sorting could be used to order the packages, but 
+in the end I decided to stick with a scheme that is consistent with the
+'ion-devel' package names:
+
+    project-branch_and_status-release_date
+
+Therefore this release is 'ion-2rc-20040114', and the "stable" one will be
+'ion-2-20040???'.
+
+
+ion-devel-20031211
+------------------
+
+This is a big clean-up release. The most noticeable changes are that 
+'-devel' has been removed from path names, user configuration files go
+in `~/.ion2/` and the main configuration file is 'ion.lua'. (The other 
+`ioncore-*.lua` files have also been renamed.) There have been no notable
+changes in the configuration files themselves, so your old files will 
+work if you move them to the correct directory and rename the changed
+files. (`mv ~/.ion-devel ~/.ion2; cd ~/.ion2; mv ioncore.lua ion.lua;`
+etc.)
+
+This release also finally contains a working PWM binary. (The ioncore+
+scripts scheme was replaced with separate binaries statically linked
+to ioncore.a.) Floating workspaces now support edge snapping and 
+sticky windows, but some PWM features are probably still missing.
+
+Some bugs were also fixed and users are now force-fed the manual page 
+the first time Ion is started. Transients can be toggled between
+top/bottom of the main window with Mod1+K T. The SSH query uses
+`~/.ssh/known_hosts` for completion instead of a manually defined list.
+
+There may be a few additions (e.g. the dock) and bug fixes, of course,
+before the stable release, but this release should pretty much be what
+the new "stable" Ion should look like. Please upgrade to it to help
+weed out the bugs. Note that you need to upgrade Lua to the 5.0.1 
+pre-release.
+
+
+ion-devel-20031119
+------------------
+
+
+It's finally time for a new release on Ion's development branch. Most
+likely this will also be the last "big" release before finally, after
+almost two years, releasing a new "stable" version of Ion. A few bug 
+fix and code clean-up releases should appear in between, though.
+
+The most important additions, changes and non-changes in this release
+are:
+
+  * Menu module
+  * A little less broken extended character set and string encoding 
+    support
+  * Extended WM hints fullscreen request support
+  * Hopefully a little clarified configuration file layout
+  * Quite a few fixes
+  * And most important of all:
+
+>    Configuration files written for the previous release should
+>    still work this time!
+
+(However, Ion will complain of old drawing engine styles.)
+
+The menu module provides both drop-down menus (in the stock configuration
+pressing Button3 on a tab should show a context menu) and query-like 
+"in-mplex" menus (F12: main menu at screen level; Mod1+M: the same context 
+menu as above at frame level).
+
+What was done enhance support for strings (mostly window titles) in 
+languages that need more than 8-bit character sets is:
+
+  * Remove UTF8 restriction and support almost arbitrary multibyte 
+    encodings instead. (Statefull encodings will not work and combining 
+    characters can cause clutter. Both are a sign of bad encoding, IMHO.)
+
+  * The XCreateFontSet routine that is used to load fonts in the kind
+    of structure the X utf8 and multibyte routines want apparently wants
+    to be able to load glyphs for all character sets specified in the 
+    locale and therefore often fails if only single font is specified. 
+    Therefore a kludge was added that tries loading more fonts while 
+    keeping the fonts' size the same. (The ",*" kludge could load huge
+    fonts). Unfortunately this has a noticeable effect in startup time :(.
+
+  * There is no longer a system.mk option to enable utf8 (now multibyte)
+    support, but must still specifically be enabled with the -i18n
+    command line switch, mostly thanks to troublesome utf-8 locales.
+
+Because Xlib's UTF-8 string drawing code is broken and very unlikely to 
+be fixed, decent text output in an UTF-8 locale is still unlikely and 
+dependent on the fonts loaded in the system. The Xmb routines that are now
+used always use an iso10646-1 font even if other fonts are loaded and the 
+unicode font does not contain a particular glyph to be drawn. The use of 
+the Xutf8 routines  in an utf8 locale can be enabled with the
+`CF_DE_USE_XUTF8` compile-time  option. The advantage of this is that 
+these routines seem to choose the font to draw particular character in 
+most cases more sanely. The downside is that there are other more serious
+problems with unknown characters.
+
+All in all, even if I'd like to support a universal move to UTF-8, thanks 
+to Xlib brokenness I can't really recommend using UTF-8 locales with Ion 
+and the default drawing engine if there's a decent alternative encoding.
+Most if not all other stateless encodings shouldn't have the problems 
+UTF-8 has. An alternative drawing engine that didn't use the Xlib i18n
+string drawing routines might solve the troubles with utf-8 support.
+
+One more note:
+
+If you have saved a custom system.mk, you'll need to set LUAC
+point to the Lua compiler there as some of the share/ files
+are compiled now.
+
+
+ion-devel-20030810
+------------------
+
+It's been a while since the previous release as I wanted to freeze the
+Lua configuration/scripting interface for this release and therefore 
+finish work on a few things. Well, on my part the interface is frozen,
+but I will still accept constructive complaints for a short time and
+after that the implemented parts of the scripting interface will be 
+frozen. But in all likelihood, if no one has anything else to say, but
+"it's ok", "it sucks", the interface will no longer change, only possibly
+grow.
+
+What's new for this release then?
+
+  * Drawing engine module support.
+  * OO-style exported functions. The old `class_fn` functions are now
+    `WClass.fn` (an ugly wrapper is provided in `compat.lua`).
+  * No more screen-specific configuration or savefiles.
+  * Session name (can be specified on command line) instead of display
+    name based savefiles. Also affects query history and not just workspace
+    saves.
+  * Bug fixes, most small and one bigger (almost complete rewrite of 
+    the split resizing algorithm).
+  * Some documentation improvements.
+
+Converting configuration files.
+
+Old `.lua` colour schemes and workspace save files can be automatically
+converted to a format suitable to be loaded by this latest release.
+Other files will have to be ported manually. To convert colour schemes,
+use the script `utils/lookconv.lua`. The usage is
+
+    lookconv.lua look-old.lua > look-new.lua
+
+To convert workspaces savefiles, use the script `utils/saveconv.sh`. 
+The usage is
+
+    saveconf.sh ~/.ion-devel/saves/workspaces-DISPLAY.*.lua \
+        > ~/.ion-devel/SESSIONNAME/workspaces.lua
+
+`DISPLAY` here is the actual display part of `$DISPLAY`, probably just `:0`.
+'*' stands for all screens (if you have only one screen you could do with
+perhaps just `workspaces-:0.0.lua`). SESSIONNAME is the name of the session
+where you want to use the converted savefiles. Default session name for
+`DISPLAY` is `default-session-DISPLAY` with the colon in `DISPLAY` converted
+to a dash. For most people this is `default-session--0`. In the simplest
+case the whole command line is therefore
+
+    saveconv.sh ~/.ion-devel/saves/workspaces-:0.0.lua \
+        > ~/.ion-devel/default-session--0/workspaces.lua
+
+The scripts may or may not work and are only provided as a potential
+convenience that will not be maintained and will be removed eventually.
+
+
+ion-devel-20030623
+------------------
+
+Quite a few bug fixes and one most likely unnoticeable improvement:
+
+2003-06-23:
+
+  * Fixed pointer warping on screen change.
+  * A bug in grab handler calling code could crash Ion when leaving
+    keyboard resize mode manually.
+  * Resize display was showing incorrect values for keyboard resize.
+
+2003-06-21:
+
+  * Client window last height request bookkeeping code had been lost
+    when configure request policy was changed. This caused transient
+    sizes to be calculated incorrectly.
+  * Return from full screen mode to floatws had been broken.
+  * As the number of dynamic functions has been getting bigger, the
+    functions are now sorted on first use and then binary-searched
+    instead of naive linear searching.
+  * Screen lookup had been broken for windows that are not properly
+    on any screen.
+
+
+ion-devel-20030620
+------------------
+
+Just some bug fixes and minor behavioural changes and improvements in
+this release:
+
+
+ion-devel-20030617
+------------------
+
+This release fixes some small problems with the previous release and
+adds a workaround kludge for the XFree86 textprop bug (it's been fixed
+but no release with the fix is available) that could cause Opera to 
+crash Ion when UTF8 support was enabled. I also added the winprop
+needed for galeon's find dialog to default `kludges.lua` and there's
+some extra documentation and defaults for some systems in system.mk.
+
+
+ion-devel-20030614
+------------------
+
+Most changes in this release centre around making Ion more tolerant
+to broken configuration files; for details see the ChangeLog. There
+are also changes in binding configuration as I already mentioned in
+an earlier poting. Namely `common-frame-bindings.lua` is gone and the
+bindings previously set there (into variables that the `*ws.lua`
+files later referenced) were moved to `ioncore-bindings.lua` and are
+set using the new functions `mplex_bindings` and `genframe_bindings`
+are were  added. Old modified configuration files should still work,
+however, but if you use `make_active_leaf_fn`, you will need to 
+include `compat.lua` as the `global_bindings` that used this were
+replaced by bindings in `mplex_bindings` and the function
+`make_current_clientwin_fn`.
+
+There are also some (minor) bug fixes and a few other improvements 
+worth a mention. In particular all regions are now given names of 
+the form  `WFoobar<n>` by default and `DEFAULT_MOD.."F9"` was bound to 
+create a new workspace without asking for a name. This binding and 
+`QueryLib.query_workspace` use the workspace type defined in the 
+variable `default_ws_type` instead of being hardcoded to `WIonWS`.
+
+All objects passed to Lua now have a unique userdata (a `WWatch` 
+cached in a weak table) so they can be used e.g. as indices to tables. 
+
+
+ion-devel-20030606
+------------------
+
+This release unifies some parts of `WScreen` and `WGenFrame`, which makes
+screen-level queries possible. (Later the queries for small frames might
+be changed to be shown at screen level.) The statusbar restriction from
+ion-devel-20030531 was also removed by this some change and a few
+non-fatal bugs have been fixed.
+
+
+ion-devel-20030601
+------------------
+
+A bug was discovered:
+
+2003-06-01:
+
+  * An off-by-one error in `extl_l1_finalize` caused references to some
+    Lua tables (including large completions) never to be released.
+
+It could be that this bug was causing some other errors in Lua as well.
+Sometimes QueryLib bindings failed because some function generators
+returned nil although they shouldn't. Adding dummy lines (!) in those
+functions fixed the problem and for some reason this patch also seemed
+to remove those problems.
+
+
+One more note: as `ioncorelib.lua` and `querylib.lua` are now installed
+in `$SHAREDIR`, you must remove the old files in `$ETCDIR` if installing
+over a previous release or else there will be errors. You should also
+not use any possible old copies you have in `~/.ion-devel/`. (One of the
+reasons for moving these to `$SHAREDIR` is to stop users from making
+copies of them as they are not configuration files.)
+
+
+ion-devel-20030531
+------------------
+
+As the subject line says, Ion-devel-20030531 was just released. The short
+list of changes is:
+
+ * The license was changed from the Clarified Artistic License to the
+   GNU Library/Lesser General Public License (LGPL).
+ * Screen, viewport and root window renames and other changes (see below).
+ * Some installation directory changes; the ion-* helper programs are now
+   installed either in `$SHAREDIR` or `$EXTRABINDIR` and QueryLib searches 
+   for them on the script path (`~/.ion-devel/`, `$ETCDIR`, `$SHAREDIR`, 
+   `$EXTRABINDIR`) instead of assuming them being on `$PATH`.
+ * Client windows are now in a separate namespace.
+ * Resize/maximize/shade changes: Shading should work on `WIonFrame`s too
+   and shade mode is automatic when the client area gets too small when
+   resizing. Maximize toggle restores frame to previous size if shaded
+   instead of maximizing. The move/resize mode bindings were changed again
+   to be more consistent and predictable: Left/Right/Up/Down and F/B/P/N
+   grow the frame in the specific direction, Shift+keys shrink and in
+   case of floating frames, `DEFAULT_MOD+keys` move
+ * Returning from full screen mode should work on `WFloatWS`s too.
+ * Lots of minor fixes, export additions and clean-up.
+
+As usual, see the (long!) list of new ChangeLog entries at the end of
+this message for details.
+
+
+I've finally personally switched from the old "stable" 20020207 to using
+the latest development release at home too and I must say that it is
+finally starting to look good. There are still things to be written, but
+unless you need "proven" stability, I see no reason to sticking to that
+old release anymore. Unless, of course, if you find a problem that I have
+not encountered or simply have no need for the new features and don't want
+to port your configuration files.
+
+
+On screens, viewports, root windows and problems with the new
+implementation
+
+The objects previously called "screens" are now called "root windows"
+and what were called "viewports" are called "screens" to better reflect
+what the user sees. (When Xinerama is not used there's no difference
+between a root window and a screen, but when Xinerama is used a root
+window may be split over multiple screens.)
+
+This release also creates so-called virtual root windows for each
+Xinerama screen when there are more than one. This is to better separate
+the windows on different screens and thus emulate normal multihead, the
+main difference being that windows can be moved between screens.
+Especially the virtual roots are there to keep the windows that are on
+a floatws on the right screen.
+
+Virtual root windows will, however, break a few apps:
+
+ * Mozilla -remote won't work because of its crappy method for looking
+   up existing windows that can't handle multiple levels of WM windows.
+   Nested workspaces have the same problem. There is, however, a simple
+   solution to this problem: gnome-moz-remote. It seems to use a saner
+   method for looking up an existing Gecko browser and also works with
+   at least Phoenix/MozillaFirebird and Galeon.
+
+These two are problems only if you use floatws:s (other problems with
+which virtual root windows are intended to solve):
+
+ * The background-setting apps I am aware of will require a following
+   'xrefresh' for the changes to be updated to the (transparent) virtual
+   roots. Background setting apps that support the `_NET_VIRTUAL_ROOTS`
+   property (which, I think, was meant for root window-sized WS 
+   backgrounds) could be used to set a separate background for each
+   Xinerama screen, though. I am not aware of any such app.
+ * Some apps' resize and move features will behave erratically on those
+   Xinerama screens not at (0, 0) on the root window. This is again
+   because the apps are assuming there's at most one WM window between
+   them and the root and are requesting windows' positions incorrectly.
+   The ICCCM is quite vague on this and I think _all_ apps that I have
+   tried are doing it wrong but the way e.g. XMMS does it certainly is
+   not an interpretation of the ICCCM. The apps that now behave correctly
+   request position for the outermost WM window. Nested workspaces have
+   the same problem.
+
+There is one more temporary problem:
+
+ * Status bar modules (and the dock module's statusbar mode) will be
+   *temporarily* broken when virtual roots are _not_ used. This will be
+   fixed later when parts of screen and frame code are unified (it should
+   then be possible to run Ion without any workspace modules and have
+   queries attached to screens). In the meanwhile, if you don't care about
+   the above-mentioned problems and want a statusbar (which probably needs
+   to be ported to this release), you can defined `CF_ALWAYS_VIRTUAL_ROOT`
+   when compiling Ion.
+
+
+ion-devel-20030510
+------------------
+
+There are quite a few small fixes and minor enhancements invisible to
+the user in this release. The splitting functions were renamed to be
+more consistent and there are a couple of enhancements in the Lua code
+query that should make it much more usable. First, tab-completion can
+now descend into tables and complete subexpressions. Secondly, the local
+variable `_` in addition to 'arg[1]' is set to point to the the frame in
+which the query is executing. 
+
+I have also written some new documentation that is now available from
+the Ion web page.
+
+
+ion-devel-20030506
+------------------
+
+The most notable change in this release is that the `target_id` system
+was removed and instead client window status is also saved over
+restarts in the saves/workspaces-* files. (It shouldn't be too hard
+to hook a session management module over this.) Thanks to this
+modification, floatframes can also save their status now. Note
+that if you restart from older version of Ion to this one, client
+window layout will be messed up.
+
+There are also a few bug fixes and code to save and load line editor
+history was added.
+
+
+ion-devel-20030503
+------------------
+
+There are lots of minor improvements in this release, see the
+new ChangeLog entries for details.
+
+
+ion-devel-20030427
+------------------
+
+Some minor feature enhancements and a few fixes in this new release:
+
+The file system scanning completions are now put in the background
+by receiving the data from external programs through pipes and select()
+for data in the main event loop. This way long taking completions don't
+block Ion from processing other events and NFS problems shouldn't hang
+it.
+
+Basic window stacking management support code was added. Transients
+on floatws:s should now be stacked above their parents. If you don't
+want to change your modified binding configurations at this point and
+need the functions `floatframe_raise/lower`, include `compat.lua`.
+
+
+ion-devel-20030412
+------------------
+
+This release again fixes some minor problems and enhances a few
+features. Most of the enhancements are related to UTF8 support and
+the floatws module. If UTF8 support is enabled, Ion now tries to
+load the "fixed" font at startup after setting up locales. If locales
+aren't properly set up, this probably fails and Ion will reset locale
+back to "POSIX" as this might make fonts loadable although support for
+non-ASCII characters will be crippled. It might now be safe for
+package maintainers to enable UTF8 support by default
+
+As apps seem to have switched to using `_NET_WM_NAME` for UTF8 titles
+and filling `WM_NAME` with crap, this property is also preferred over
+`WM_NAME` if set. Support for some other "extended" WM hints might be
+added in the future but I have no intentions of moking Ion NetWM-
+compliant. I might have thought of attempting to do so a few years
+ago when I last read the specification but it seems that since then
+they've filled it with lots of pointless bloat that may even be in
+opposition to Ion's goals. Take multi-parent transients, for example. 
+
+Full error log is also displayed with xmessage on startup whether
+it is possible to continue or not.
+
+
+ion-devel-20030410
+------------------
+
+There was a bug in QueryLib written yesterday, therefore this release.
+
+
+ion-devel-20030409
+------------------
+
+I started converting the query code to Lua and discovered some rather
+silly bugs in the Lua interface so here's a new release with the new
+and improved query code included. The names of queries have changed
+(they're all in the table QueryLib defined in `querylib.lua` -- only
+`query_query` and a few temporary handlers are implemented in query.so)
+so your modified configuration files will be broken again :(.
+
+
+ion-devel-20030408
+------------------
+
+Be prepared to completely rewrite your configuration files once
+again: a new version of Ion-devel has been released that uses Lua
+<http://www.lua.org/> for all configuration. Version 5.0 of the
+language/library is required. Although scripting possibilities
+are now much better than before, there is one rather big drawback:
+Lua is not nearly as error-tolerant as Ion's old configuration
+parser so a syntax error in a file will cause the file not to be
+executed and may even cause Ion not to be able to start. More
+comments and documentation on scripting will follow at a later time.
+
+Libtool and libltdl are also now used to implement module support.
+Hopefully this will make compiling Ion easier on a wider range of
+platforms. (On the other hand, this could also induce new problems.)
+
+Finally, the preferred linking address for Ion's home page is now
+<http://www.iki.fi/tuomov/ion/>. The pages are still located at the
+old address, but this redirected address should be more permanent.
+
+
+ion-devel-20030327
+------------------
+
+Just some minor fixes, binary rename and better embedded workspace
+support in this release. I'm still contemplating whether to convert
+Ion to C++ and haven't written many things that I intended to as
+that decision will affect how those are implemented.
+
+To create an embedded workspaces in a frame, at the moment you have
+to modify the appropriate workspace save file (when Ion is not running!)
+by  adding lines such as
+
+    region "WFloatWS", "testiupotus" {
+    }
+
+inside the frame definition after the other options (flags, `target_id`;
+those options will be ignored if after any region definitions).
+
+One more thing to note: debugging infos are no longer automatically
+stripped from the ioncore binary or modules by 'make install' so that
+I don't have to explain how to get proper backtraces every time a bug
+is found. If you you are confident that you can send me proper backtraces
+when you find a bug (or maybe can fix it yourself), you can make the
+binary and the modules a _lot_ smaller by running 'strip' on them.
+
+
+ion-devel-20030311
+------------------
+
+The list of new ChangeLog entries is long this time, but I'm finally
+starting to get to what I started working on almost two years ago. Yes,
+there is *experimental* support for PWM-like "floating frame" workspaces.
+A lot of the functionality is still missing -- no need to complain of
+such at the time -- but the floatws.so module and basic functionality
+is there. Most notably perhaps a menu module is not yet implemented.
+(Porting the PWM menu code should a nice little task for whomever
+interested... *wink*.) To create some PWM workspaces, load the floatws.so
+module (should be loaded by default) and in `query_workspace` (F9)
+prefix workspace name with 'WFloatWS:' (or load the module before
+ionws.so). 
+
+This release is also finally fully modularised: In addition to the
+ion core binary that can not function alone, there are the modules
+ionws.so, floatws.so and query.so. It is also possible statically
+compile the modules in the core binary if the system doesn't support
+libdl.
+
+There's also *experimental* UTF8 support that must be specifically
+enabled from system.mk. You must have XFree86 (4.x?) and C99 wide
+char support available (either libc directly or maybe libutf8+libiconv).
+Thanks to bugs (?) in some of the XFree86 Xutf8 functions, your locales
+must be properly set up or else X will stop drawing strings at non-ascii
+characters instead of ignoring them. See my earlier rants on the mailing
+list for reasons on not using the more standard Xmb functions.
+To actually see any special characters, you must load the necessary fonts
+by specifying a comma-separated list of fonts to the font and `tab_font`
+draw.conf options. Multiple font loading does not work when Xft support
+is enabled at the time being.
+
+There has been a lot of changes in the config files, again, and I can
+promise there's still more to come. See the ChangeLog for details.
+If you want to use your old workspaces configurations, move them to
+`~/.ion-devel/saves` and replace the strings "WFrame" and "WWorkspace"
+with "WIonFrame" and "WIonWS", respectively.
+
+
+ion-devel-20030225
+------------------
+
+I've made the bugfix release as promised. Get it from the usual place.
+
+
+ion-devel-20030223
+------------------
+
+There's a new major development release available from Ion web page.
+Not many changes are visible to the end user, though, but a lot was
+rewritten to be more flexible and simpler; see the ChangeLog for a
+full account. The older development release is also still available
+because this release can not be considered as stable after major
+changes to the code. The most visible changes are
+
+  * Changes in binding configuration
+  * A (mostly) working full screen mode toggle (see below)
+  * The ability to switch workspaces while dragging tabs (experimental)
+  * The ability to re-read draw.conf without restarting Ion
+  * Some bug fixes.
+
+
+Some notes on full screen mode toggle:
+
+ * Ion doesn't detect programs trying to leave full screen mode--it
+   should be possible to devise some method using window properties,
+   however.
+ * At least Opera unmaps the window also before changing the size to
+   enter full screen mode, so Ion doesn't remember the last frame.
+ * Mozilla sometimes has trouble entering full screen mode but when it
+   succeeds, Ion remembers the last frame. However, if
+   `clientwin_toggle_fullscreen` is used to leave the full screen mode
+   started from Mozilla, Mozilla doesn't know that the mode has been
+   left.
+
+Clearly there should be some method of communication between Ion and the
+programs for full screen toggles to fully work. At the moment I suggest
+using `clientwin_toggle_fullscreen` instead of the programs' native toggles
+unless the program has some special full screen mode and you really want
+it.
diff --git a/TODO.README b/TODO.README
new file mode 100644 (file)
index 0000000..d02825c
--- /dev/null
@@ -0,0 +1,5 @@
+
+The TODO.riot file containg an Ion TODO list has been created with the
+riot outliner available from <http://iki.fi/tuomov/riot/>. The file is,
+however, just an mbox file, so you can read it with your favourite 
+threading mail user agent. For example: mutt -f TODO.riot
diff --git a/TODO.riot b/TODO.riot
new file mode 100644 (file)
index 0000000..efe689f
--- /dev/null
+++ b/TODO.riot
@@ -0,0 +1,759 @@
+From background-static Thu Mar 23 17:30:06 EET 2006
+Message-Id: <riot.20060323173006.1@riot.invalid>
+Date: Thu Mar 23 17:30:06 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:30:06 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Ion TODO list
+
+Ion TODO list
+
+From background-static Thu Mar 23 18:08:00 EET 2006
+Message-Id: <riot.20060323180800.34@riot.invalid>
+Date: Thu Mar 23 18:08:00 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:08:12 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Core/general
+In-Reply-To: <riot.20060323173006.1@riot.invalid>
+
+Core/general
+
+From background-static Thu Mar 23 17:31:43 EET 2006
+Message-Id: <riot.20060323173143.2@riot.invalid>
+Date: Thu Mar 23 17:31:43 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:31:46 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Startup sequence support
+In-Reply-To: <riot.20060323180800.34@riot.invalid>
+
+Startup sequence support
+
+Support for the FDO startup notification specification should be
+added. Ion should arrange things in the right frame by LAUNCHED_BY.
+
+The specification is at
+
+<http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt>
+
+From background-static Thu Mar 23 17:41:20 EET 2006
+Message-Id: <riot.20060323174120.11@riot.invalid>
+Date: Thu Mar 23 17:41:20 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:41:20 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Better support for the few applicable EWMH hints
+In-Reply-To: <riot.20060323180800.34@riot.invalid>
+
+Better support for the few applicable EWMH hints
+
+From background-static Thu Mar 23 17:52:17 EET 2006
+Message-Id: <riot.20060323175217.6@riot.invalid>
+Date: Thu Mar 23 17:52:17 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Sat Apr 22 19:08:23 EEST 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Numbered tabs
+In-Reply-To: <riot.20060323180800.34@riot.invalid>
+
+Numbered tabs
+
+Either togglable, or perhaps the numbers should be shown when
+a prefix of the key combo to switch tabs has been entered.
+The latter will be quite more complicated to implement.
+
+Done: basic toggleable numbering.
+
+From background-static Thu Mar 23 18:00:25 EET 2006
+Message-Id: <riot.20060323180025.17@riot.invalid>
+Date: Thu Mar 23 18:00:25 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:00:25 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Some way to load/initialise inherited drawing engines properly.
+In-Reply-To: <riot.20060323180800.34@riot.invalid>
+
+Some way to load/initialise inherited drawing engines properly.
+
+From background-static Thu Mar 23 18:04:35 EET 2006
+Message-Id: <riot.20060323180435.27@riot.invalid>
+Date: Thu Mar 23 18:04:35 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:04:35 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: New hooks?
+In-Reply-To: <riot.20060323180800.34@riot.invalid>
+
+New hooks?
+
+From background-static Thu Mar 23 18:10:34 EET 2006
+Message-Id: <riot.20060323181034.36@riot.invalid>
+Date: Thu Mar 23 18:10:34 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:10:34 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Tiled workspaces
+In-Reply-To: <riot.20060323173006.1@riot.invalid>
+
+Tiled workspaces
+
+From background-static Thu Mar 23 17:40:03 EET 2006
+Message-Id: <riot.20060323174003.7@riot.invalid>
+Date: Thu Mar 23 17:40:03 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:40:03 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: A general solution to transients, queries and menus in tiny frames.
+In-Reply-To: <riot.20060323181034.36@riot.invalid>
+
+A general solution to transients, queries and menus in tiny frames.
+
+From background-static Thu Mar 23 17:40:08 EET 2006
+Message-Id: <riot.20060323174008.8@riot.invalid>
+Date: Thu Mar 23 17:40:08 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:40:10 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: PaneWS
+In-Reply-To: <riot.20060323181034.36@riot.invalid>
+
+PaneWS
+
+From background-static Thu Mar 23 17:40:13 EET 2006
+Message-Id: <riot.20060323174013.9@riot.invalid>
+Date: Thu Mar 23 17:40:13 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:40:13 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Lots of fine-tuning
+In-Reply-To: <riot.20060323174008.8@riot.invalid>
+
+Lots of fine-tuning
+
+From background-static Thu Mar 23 17:50:02 EET 2006
+Message-Id: <riot.20060323175002.4@riot.invalid>
+Date: Thu Mar 23 17:50:02 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:50:22 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Pane handles should allow resizing with the rodent.
+In-Reply-To: <riot.20060323181034.36@riot.invalid>
+
+Pane handles should allow resizing with the rodent.
+(You know, those things on the sides of floating splits.)
+
+From background-static Thu Mar 23 18:09:58 EET 2006
+Message-Id: <riot.20060323180958.35@riot.invalid>
+Date: Thu Mar 23 18:09:58 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:10:27 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: mod_statusbar and ion-statusd
+In-Reply-To: <riot.20060323173006.1@riot.invalid>
+
+mod_statusbar and ion-statusd
+
+From background-static Thu Mar 23 17:36:11 EET 2006
+Message-Id: <riot.20060323173611.1@riot.invalid>
+Date: Thu Mar 23 17:36:11 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:36:11 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Multi-line statusbar
+In-Reply-To: <riot.20060323180958.35@riot.invalid>
+
+Multi-line statusbar
+
+From background-static Thu Mar 23 17:36:44 EET 2006
+Message-Id: <riot.20060323173644.2@riot.invalid>
+Date: Thu Mar 23 17:36:44 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:39:08 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Better control of layout etc.
+In-Reply-To: <riot.20060323180958.35@riot.invalid>
+
+Better control of layout etc.
+
+From background-static Thu Mar 23 17:39:20 EET 2006
+Message-Id: <riot.20060323173920.6@riot.invalid>
+Date: Thu Mar 23 17:39:20 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:39:20 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: mpress support for meters or other areas.
+In-Reply-To: <riot.20060323180958.35@riot.invalid>
+
+mpress support for meters or other areas.
+
+From background-static Thu Mar 23 17:54:07 EET 2006
+Message-Id: <riot.20060323175407.8@riot.invalid>
+Date: Thu Mar 23 17:54:07 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:54:07 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Communicating configuration to statusd shouldn't use temp file
+In-Reply-To: <riot.20060323180958.35@riot.invalid>
+
+Communicating configuration to statusd shouldn't use temp file
+
+From background-static Thu Mar 23 18:11:44 EET 2006
+Message-Id: <riot.20060323181144.39@riot.invalid>
+Date: Thu Mar 23 18:11:44 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:11:44 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: mod_query
+In-Reply-To: <riot.20060323173006.1@riot.invalid>
+
+mod_query
+
+From background-static Thu Mar 23 17:40:27 EET 2006
+Message-Id: <riot.20060323174027.10@riot.invalid>
+Date: Thu Mar 23 17:40:27 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:40:35 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: mod_query.message should handle tabs properly
+In-Reply-To: <riot.20060323181144.39@riot.invalid>
+
+mod_query.message should handle tabs properly
+
+From background-static Thu Mar 23 18:11:52 EET 2006
+Message-Id: <riot.20060323181152.40@riot.invalid>
+Date: Thu Mar 23 18:11:52 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:11:52 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: mod_dock
+In-Reply-To: <riot.20060323173006.1@riot.invalid>
+
+mod_dock
+
+From background-static Thu Mar 23 17:58:56 EET 2006
+Message-Id: <riot.20060323175856.15@riot.invalid>
+Date: Thu Mar 23 17:58:56 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:58:56 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: There are some problems with user geometries of docked apps.
+In-Reply-To: <riot.20060323181152.40@riot.invalid>
+
+There are some problems with user geometries of docked apps.
+
+From background-static Thu Mar 23 18:12:02 EET 2006
+Message-Id: <riot.20060323181202.41@riot.invalid>
+Date: Thu Mar 23 18:12:02 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:12:02 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: mod_mgmtmode
+In-Reply-To: <riot.20060323173006.1@riot.invalid>
+
+mod_mgmtmode
+
+From background-static Thu Mar 23 18:04:54 EET 2006
+Message-Id: <riot.20060323180454.28@riot.invalid>
+Date: Thu Mar 23 18:04:54 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:04:54 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: An actual management mode for mod_mgmtmode.
+In-Reply-To: <riot.20060323181202.41@riot.invalid>
+
+An actual management mode for mod_mgmtmode.
+
+From background-static Thu Mar 23 18:11:20 EET 2006
+Message-Id: <riot.20060323181120.38@riot.invalid>
+Date: Thu Mar 23 18:11:20 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:11:20 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Drawing engine(s)
+In-Reply-To: <riot.20060323173006.1@riot.invalid>
+
+Drawing engine(s)
+
+From background-static Thu Mar 23 18:02:32 EET 2006
+Message-Id: <riot.20060323180232.22@riot.invalid>
+Date: Thu Mar 23 18:02:32 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:02:32 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: There should be a sticky attribute for the drawing engine
+In-Reply-To: <riot.20060323181120.38@riot.invalid>
+
+There should be a sticky attribute for the drawing engine
+for sticky floatws frames.
+
+From background-static Thu Mar 23 18:06:40 EET 2006
+Message-Id: <riot.20060323180640.31@riot.invalid>
+Date: Thu Mar 23 18:06:40 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:06:40 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: There are some problems with transparent backgrounds of tabs, IIRC.
+In-Reply-To: <riot.20060323181120.38@riot.invalid>
+
+There are some problems with transparent backgrounds of tabs, IIRC.
+
+From background-static Thu Mar 23 18:07:53 EET 2006
+Message-Id: <riot.20060323180753.33@riot.invalid>
+Date: Thu Mar 23 18:07:53 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:07:53 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Documentation
+In-Reply-To: <riot.20060323173006.1@riot.invalid>
+
+Documentation
+
+From background-static Sat Sep 16 14:14:23 EEST 2006
+Message-Id: <riot.20060916141423.1@riot.invalid>
+Date: Sat Sep 16 14:14:23 EEST 2006
+Status: RO
+X-Riot-Version: 1ds-20060502
+From: Riot
+X-Riot-Edited: Sat Sep 16 14:14:23 EEST 2006
+Content-Type: text/plain; charset=utf-8
+Subject: More of it
+In-Reply-To: <riot.20060323180753.33@riot.invalid>
+
+More of it
+
+From background-static Thu Mar 23 17:33:17 EET 2006
+Message-Id: <riot.20060323173317.5@riot.invalid>
+Date: Thu Mar 23 17:33:17 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:49:42 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Random ideas to try (semi-TODO)
+
+Random ideas to try (semi-TODO)
+
+Just some stuff here for interested people to try. No guarantees
+that even a good patch will ever be accepted though. 
+
+From background-static Thu Mar 23 18:06:20 EET 2006
+Message-Id: <riot.20060323180620.30@riot.invalid>
+Date: Thu Mar 23 18:06:20 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:06:20 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Movement threshold would be better for floating splits, raise delay
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Movement threshold would be better for floating splits, raise delay 
+is just an ugly hack. This is just not implementable in a nice way
+without proper translucency support.
+
+From background-static Thu Mar 23 18:07:15 EET 2006
+Message-Id: <riot.20060323180715.32@riot.invalid>
+Date: Thu Mar 23 18:07:15 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:07:15 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Composition manager and all the nice stuff possible with it
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Composition manager and all the nice stuff possible with it
+
+From background-static Thu Mar 23 18:05:04 EET 2006
+Message-Id: <riot.20060323180504.29@riot.invalid>
+Date: Thu Mar 23 18:05:04 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:05:04 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Key grabs from scripts?
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Key grabs from scripts?
+
+From background-static Thu Mar 23 18:01:56 EET 2006
+Message-Id: <riot.20060323180156.21@riot.invalid>
+Date: Thu Mar 23 18:01:56 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:01:56 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Dock resizing improvements?
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Dock resizing improvements?
+
+From background-static Thu Mar 23 18:01:40 EET 2006
+Message-Id: <riot.20060323180140.20@riot.invalid>
+Date: Thu Mar 23 18:01:40 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:01:40 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Perhaps dock/statusbar should be able to use a different style
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Perhaps dock/statusbar should be able to use a different style
+on floatws.
+
+From background-static Thu Mar 23 18:01:23 EET 2006
+Message-Id: <riot.20060323180123.19@riot.invalid>
+Date: Thu Mar 23 18:01:23 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:01:23 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Make lines with other frame splits "sticking" for resize.
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Make lines with other frame splits "sticking" for resize.
+
+From background-static Thu Mar 23 18:00:54 EET 2006
+Message-Id: <riot.20060323180054.18@riot.invalid>
+Date: Thu Mar 23 18:00:54 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:00:54 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Better support for nesting workspaces etc.
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Better support for nesting workspaces etc.
+
+From background-static Thu Mar 23 17:59:40 EET 2006
+Message-Id: <riot.20060323175940.16@riot.invalid>
+Date: Thu Mar 23 17:59:40 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:59:40 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Maybe parent window should shrink out of the way of transients
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Maybe parent window should shrink out of the way of transients
+at least to some threshold.
+
+From background-static Thu Mar 23 17:57:42 EET 2006
+Message-Id: <riot.20060323175742.13@riot.invalid>
+Date: Thu Mar 23 17:57:42 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:57:42 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Maybe placeholders should be exported to Lua side,
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Maybe placeholders should be exported to Lua side, 
+for initial placement setup and so on.
+
+From background-static Thu Mar 23 17:54:38 EET 2006
+Message-Id: <riot.20060323175438.9@riot.invalid>
+Date: Thu Mar 23 17:54:38 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:54:38 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Some kind of error reporting system for bindings
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Some kind of error reporting system for bindings 
+as well while Ion is running.
+
+From background-static Thu Mar 23 17:51:09 EET 2006
+Message-Id: <riot.20060323175109.5@riot.invalid>
+Date: Thu Mar 23 17:51:09 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:51:09 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Perhaps transients should restore their sizes
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Perhaps transients should restore their sizes 
+when moving from a small to big frame, if the user has not resized it.
+
+From background-static Thu Mar 23 17:48:55 EET 2006
+Message-Id: <riot.20060323174855.2@riot.invalid>
+Date: Thu Mar 23 17:48:55 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:48:55 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Perhaps / should be stripped from completed directory names.
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+Perhaps / should be stripped from completed directory names.
+It should however remain in the list of completions as a hint.
+Then typing the slash would trigger a new completion run in
+auto-show-completions mode.
+
+From background-static Thu Mar 23 17:49:07 EET 2006
+Message-Id: <riot.20060323174907.3@riot.invalid>
+Date: Thu Mar 23 17:49:07 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:49:07 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: How about pane handles on all splits?
+In-Reply-To: <riot.20060323173317.5@riot.invalid>
+
+How about pane handles on all splits?
+
+From background-static Thu Mar 23 17:32:44 EET 2006
+Message-Id: <riot.20060323173244.4@riot.invalid>
+Date: Thu Mar 23 17:32:44 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:32:44 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Done
+
+Done
+
+From background-static Thu Mar 23 17:53:19 EET 2006
+Message-Id: <riot.20060323175319.7@riot.invalid>
+Date: Thu Mar 23 17:53:19 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:53:19 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Query activation key cycles completions
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+Query activation key cycles completions
+
+Maybe it should be possible to use a key activating a query to cycle
+the completions within the query, a bit like grabmenu.
+
+From background-static Thu Mar 23 18:13:35 EET 2006
+Message-Id: <riot.20060323181335.42@riot.invalid>
+Date: Thu Mar 23 18:13:35 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:13:35 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Perhaps the functionality of detach.lua could be implemented
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+Perhaps the functionality of detach.lua could be implemented
+in a better and more integrated manner. (This could be related
+to the problem of transients etc. in tiny frames in the actual
+TODO list.)
+
+From background-static Thu Mar 23 18:03:17 EET 2006
+Message-Id: <riot.20060323180317.24@riot.invalid>
+Date: Thu Mar 23 18:03:17 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:03:17 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: -lintl check
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+-lintl check
+
+From background-static Thu Mar 23 17:32:26 EET 2006
+Message-Id: <riot.20060323173226.3@riot.invalid>
+Date: Thu Mar 23 17:32:26 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:32:26 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Documentation doesn't build ATM.
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+Documentation doesn't build ATM.
+
+It should be changed to build, or converted to some yet non-existent
+latex replacement that doesn't have all the problems of latex.
+
+From background-static Thu Mar 23 17:57:15 EET 2006
+Message-Id: <riot.20060323175715.12@riot.invalid>
+Date: Thu Mar 23 17:57:15 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:57:15 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: It should be possible to re-attach regions within same manager
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+It should be possible to re-attach regions within same manager 
+at a new location. (Not really that important, though, but 
+it's ugly that it has to be forbidden ATM.)
+
+From background-static Thu Mar 23 18:03:33 EET 2006
+Message-Id: <riot.20060323180333.26@riot.invalid>
+Date: Thu Mar 23 18:03:33 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:03:33 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: What else is broken (besides ac itself)?
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+What else is broken (besides ac itself)?
+
+From background-static Thu Mar 23 18:03:25 EET 2006
+Message-Id: <riot.20060323180325.25@riot.invalid>
+Date: Thu Mar 23 18:03:25 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 18:03:25 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: xmessage check?
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+xmessage check?
+
+From background-static Thu Mar 23 17:37:02 EET 2006
+Message-Id: <riot.20060323173702.3@riot.invalid>
+Date: Thu Mar 23 17:37:02 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:37:02 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Transition to Lua 5.1
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+Transition to Lua 5.1
+
+From background-static Thu Mar 23 17:58:26 EET 2006
+Message-Id: <riot.20060323175826.14@riot.invalid>
+Date: Thu Mar 23 17:58:26 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:58:33 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: A version of ioncore.warn() that doesn't dump stack trace
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+A version of ioncore.warn() that doesn't dump stack trace
+should be available for Lua-side Ion code.
+
+From background-static Thu Mar 23 17:54:55 EET 2006
+Message-Id: <riot.20060323175455.10@riot.invalid>
+Date: Thu Mar 23 17:54:55 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:55:31 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Framed transients should be better
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+Framed transients should be better
+
+It's not really nice that the transient blocks tab-switching bindings
+for the containing frame and so on.
+
+From background-static Thu Mar 23 17:38:29 EET 2006
+Message-Id: <riot.20060323173829.4@riot.invalid>
+Date: Thu Mar 23 17:38:29 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:38:29 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: goto_notification_target
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+goto_notification_target
+
+It should be possible to go to a region with activity flag set in
+the order of the flags being set by hitting a key.
+
+From background-static Thu Mar 23 17:47:26 EET 2006
+Message-Id: <riot.20060323174726.1@riot.invalid>
+Date: Thu Mar 23 17:47:26 EET 2006
+Status: RO
+X-Riot-Version: 1ds-yyyymmdd
+From: Riot
+X-Riot-Edited: Thu Mar 23 17:47:26 EET 2006
+Content-Type: text/plain; charset=utf-8
+Subject: Should separate stacking code from mod_floatws
+In-Reply-To: <riot.20060323173244.4@riot.invalid>
+
+Should separate stacking code from mod_floatws 
+into a separate independent source file ("module").
+
diff --git a/build/ac/README.autoconf b/build/ac/README.autoconf
new file mode 100644 (file)
index 0000000..9af494e
--- /dev/null
@@ -0,0 +1,45 @@
+
+Ion autoconf script README
+
+
+CONFIGURE SCRIPT OPTIONS
+
+The Ion-specific options to ./configure are:
+
+    --disable-xinerama
+    --disable-xfree86-textprop-bug-workaround
+    --enable-Xutf8
+    --disable-shared
+    --with-lua* (see DEBIAN NOTES below)
+    --enable-c99-source
+    --enable-xopen-source
+    --disable-locale
+    --enable-sun-f1x-remap
+
+Run ./configure --help for more information on these options. The
+defaults are chosen to generate a configuration very similar to the
+Ion default.
+
+
+DEBIAN NOTES
+
+If you are using the Lua 5.1 package on Debian then use
+
+    ./configure --with-lua-suffix=5.1 \
+    --with-lua-includes=/usr/include/lua5.1
+
+This will allow the script to find the Lua headers, binaries and
+libraries as renamed and located on Debian.
+
+
+CYGWIN NOTES
+
+Under Cygwin you must use statically linked modules:
+
+    ./configure --disabled-shared
+
+
+AUTHORS
+
+Tom Payne <ion@tompayne.org>
+
diff --git a/build/ac/aclocal.m4 b/build/ac/aclocal.m4
new file mode 100644 (file)
index 0000000..c80e0ac
--- /dev/null
@@ -0,0 +1,57 @@
+
+dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not)
+dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page
+dnl also defines GSTUFF_PKG_ERRORS on error
+AC_DEFUN(PKG_CHECK_MODULES, [
+  succeeded=no
+
+  if test -z "$PKG_CONFIG"; then
+    AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+  fi
+
+  if test "$PKG_CONFIG" = "no" ; then
+     echo "*** The pkg-config script could not be found. Make sure it is"
+     echo "*** in your path, or set the PKG_CONFIG environment variable"
+     echo "*** to the full path to pkg-config."
+     echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config."
+  else
+     PKG_CONFIG_MIN_VERSION=0.9.0
+     if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then
+        AC_MSG_CHECKING(for $2)
+
+        if $PKG_CONFIG --exists "$2" ; then
+            AC_MSG_RESULT(yes)
+            succeeded=yes
+
+            AC_MSG_CHECKING($1_CFLAGS)
+            $1_CFLAGS=`$PKG_CONFIG --cflags "$2"`
+            AC_MSG_RESULT($$1_CFLAGS)
+
+            AC_MSG_CHECKING($1_LIBS)
+            $1_LIBS=`$PKG_CONFIG --libs "$2"`
+            AC_MSG_RESULT($$1_LIBS)
+        else
+            $1_CFLAGS=""
+            $1_LIBS=""
+            ## If we have a custom action on failure, don't print errors, but 
+            ## do set a variable so people can do so.
+            $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
+            ifelse([$4], ,echo $$1_PKG_ERRORS,)
+        fi
+
+        AC_SUBST($1_CFLAGS)
+        AC_SUBST($1_LIBS)
+     else
+        echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer."
+        echo "*** See http://www.freedesktop.org/software/pkgconfig"
+     fi
+  fi
+
+  if test $succeeded = yes; then
+     ifelse([$3], , :, [$3])
+  else
+     ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4])
+  fi
+])
+
+
diff --git a/build/ac/configure.ac b/build/ac/configure.ac
new file mode 100644 (file)
index 0000000..c65e7cb
--- /dev/null
@@ -0,0 +1,440 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl Please report bugs in this autoconf script Tom Payne <ion@tompayne.org>
+dnl $Header: /home/twp/cvsroot/twp/ion/ion-devel-autoconf/configure.ac,v 1.16 2004/01/14 11:32:19 twp Exp $
+
+AC_INIT([Ion Window Manager],
+        [3ds-20061223],
+        [nobody@nowhere.invalid],
+        [ion3])
+AC_PREREQ([2.58])
+AC_REVISION([$Revision: 1.16 $])
+
+dnl Checks for programs. {{{
+AC_PROG_CC()
+AC_PROG_EGREP()
+AC_PROG_INSTALL()
+AC_PROG_RANLIB()
+AC_CHECK_TOOL([STRIP], [strip], [:])
+AC_CHECK_TOOL([AR], [ar], [])
+AC_CHECK_PROG(XMESSAGE, xmessage, xmessage)
+if test "x${XMESSAGE}" = "x"; then
+   AC_MSG_WARN([*** ion needs xmessage to run correctly])
+fi
+dnl }}}
+
+dnl Checks for libraries. {{{
+AC_PATH_XTRA()
+AC_CHECK_HEADER([libintl.h], [],
+                [AC_MSG_ERROR([*** libintl.h not found.
+                  *** You probably need to install libc6-dev])])
+dnl Perhaps we should check for -lintl, but on my machine, 
+dnl the library isn't needed. Only the #include is necessary.
+
+test "${X_DISPLAY_MISSING+set}" = set && exit 1
+AC_CHECK_HEADER([X11/SM/SMlib.h], [], 
+                [AC_MSG_ERROR([*** SMlib.h not found.
+                  *** You probably need to install libsm-dev])])
+AC_CHECK_HEADER([X11/Xresource.h], [], 
+                [AC_MSG_ERROR([*** Xresource.h.h not found.
+                  *** You probably need to install libx11-dev])])
+AC_CHECK_LIB([Xext],
+             [XMissingExtension],
+             [X_LIBS="$X_LIBS -lXext"],
+             [AC_MSG_ERROR([*** Missing symbol XMissingExtension in Xext])],
+             [$X_PRE_LIBS -lX11 $X_LIBS $X_EXTRA_LIBS])
+dnl }}}
+
+dnl XINERAMA_LIBS, _DCF_NO_XINERAMA {{{
+
+AC_SUBST([XINERAMA_LIBS])
+AC_SUBST([_DCF_NO_XINERAMA])
+
+AC_ARG_ENABLE([xinerama],
+              [AS_HELP_STRING([--disable-xinerama],
+                              [Disable Xinerama support])])
+
+if test "x$enable_xinerama" != xno; then
+    AC_CHECK_LIB([Xinerama],
+                 [XineramaQueryExtension],
+                 [XINERAMA_LIBS="-lXinerama"],
+                 [AC_MSG_WARN([*** Xinerama disabled (not available)])
+                  enable_xinerama="no"],
+                 [$X_PRE_LIBS -lX11 $X_LIBS $X_EXTRA_LIBS])
+
+fi
+
+if test "x$enable_xinerama" = xno; then
+    _DCF_NO_XINERAMA="-DCF_NO_XINERAMA"
+fi
+
+dnl }}}
+
+dnl _DCF_XFREE86_TEXTPROP_BUG_WORKAROUND {{{
+
+AC_SUBST([_DCF_XFREE86_TEXTPROP_BUG_WORKAROUND])
+
+AC_ARG_ENABLE([xfree86-textprop-bug-workaround],
+              [AS_HELP_STRING([--disable-xfree86-textprop-bug-workaround],
+                              [Disable the XFree86 textprop bug workaround])])
+
+if test "x$enable_xfree86_textprop_bug_workaround" != xno; then
+    _DCF_XFREE86_TEXTPROP_BUG_WORKAROUND="-DCF_XFREE86_TEXTPROP_BUG_WORKAROUND"
+fi
+
+dnl }}}
+
+dnl _DCF_DE_USE_XUTF8 {{{
+
+AC_SUBST([_DCF_DE_USE_XUTF8])
+
+AC_ARG_ENABLE([Xutf8],
+              [AS_HELP_STRING([--enable-Xutf8],
+                              [Use the Xutf8 routines (XFree86 extension)
+                               instead of Xmb routines in an UTF-8 locale.])])
+
+if test "x$enable_Xutf8" = xyes; then
+    AC_MSG_CHECKING([for Xutf8*])
+    save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS $X_CFLAGS"
+    AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <X11/Xlib.h>
+
+int main()
+{
+#ifdef X_HAVE_UTF8_STRING
+    exit(0);
+#else
+    exit(1);
+#endif
+}
+                  ]])],
+                  [AC_MSG_RESULT([yes])
+                   _DCF_DE_USE_XUTF8="-DCF_DE_USE_XUTF8"],
+                  [AC_MSG_RESULT([no])
+                   exit 1])
+    CFLAGS="$save_CFLAGS"
+fi
+
+dnl }}}
+
+dnl _DCF_SUN_F1X_REMAP {{{
+
+AC_SUBST([_DCF_SUN_F1X_REMAP])
+
+AC_ARG_ENABLE([sun-f1x-remap],
+                         [AS_HELP_STRING([--enable-sun-f1x-remap],
+                                                         [Remap F11 key to SunF36 and F12 to SunF37])])
+
+if test "x$enable_sun_f1x_remap" == xyes; then
+       _DCF_SUN_F1X_REMAP="-DCF_SUN_F1X_REMAP"
+fi
+
+dnl }}}
+
+dnl DL_LIBS {{{
+
+AC_SUBST([DL_LIBS])
+
+AC_CHECK_HEADER([dlfcn.h], [], [AC_MSG_ERROR([*** missing header dlfcn.h])])
+AC_CHECK_LIB([dl], [dlopen], [DL_LIBS="-ldl"],
+             [AC_MSG_ERROR([*** missing library dl])])
+
+dnl }}}
+
+dnl PRELOAD_MODULES {{{
+
+AC_SUBST(PRELOAD_MODULES)
+
+AC_ARG_ENABLE([shared],
+              [AS_HELP_STRING([--disable-shared],
+                              [Do not dynamically load modules (preload modules
+                               instead)])])
+
+if test "x$enable_shared" = xno; then
+    PRELOAD_MODULES="1"
+fi
+
+dnl }}}
+
+dnl LUA, LUAC, LUA_INCLUDES, LUA_LIBS {{{
+
+AC_SUBST([LUA])
+AC_SUBST([LUAC])
+AC_SUBST([LUA_INCLUDES])
+AC_SUBST([LUA_LIBS])
+
+dnl Arguments {{{
+AC_ARG_WITH([lua-prefix],
+            [AS_HELP_STRING([--with-lua-prefix=DIR],
+                            [Lua files are in DIR])])
+AC_ARG_WITH([lua-includes],
+            [AS_HELP_STRING([--with-lua-includes=DIR],
+                            [Lua include files are in DIR])])
+AC_ARG_WITH([lua-libraries],
+            [AS_HELP_STRING([--with-lua-libraries=DIR],
+                            [Lua library files are in DIR])])
+AC_ARG_WITH([lua-suffix],
+            [AS_HELP_STRING([--with-lua-suffix=ARG],
+                            [Lua binary and library files are suffixed with
+                             ARG])])
+dnl }}}
+
+dnl LUA {{{
+if test "x$with_lua_prefix" = x; then
+    lua_search_path="$PATH"
+else
+    lua_search_path="$with_lua_prefix/bin"
+fi
+if test "x$LUA" = x; then
+    AC_PATH_PROG([LUA], [lua$with_lua_suffix], [], [$lua_search_path])
+    test "x$LUA" = x && AC_MSG_ERROR([*** Can't build ion without lua])
+fi
+dnl }}}
+
+dnl lua_version {{{
+AC_MSG_CHECKING([for lua version >= 5.1])
+lua_version=$($LUA -v 2>&1 | head -n 1 | cut -d' ' -f2)
+dnl lua_version=$($LUA -v 2>&1 >/dev/null | head -n 1 | $EGREP -o '[0-9]+(\.[0-9]+)+')
+case $lua_version in
+0.* | 1.* | 2.* | 3.* | 4.* | 5.0)
+    AC_MSG_RESULT([no (found $lua_version)])
+    AC_MSG_ERROR([*** can't build ion with this version of lua])
+    ;;
+*)
+    AC_MSG_RESULT([yes (found $lua_version)])
+    ;;
+esac
+dnl }}}
+
+dnl LUAC {{{
+if test "x$LUAC" = x; then
+    AC_PATH_PROG([LUAC], [luac$with_lua_suffix], [], [$lua_search_path])
+    test "x$LUAC" = x && exit 1
+fi
+dnl }}}
+
+dnl luac_version {{{
+AC_MSG_CHECKING([for lua version = luac version])
+luac_version=$($LUAC -v 2>&1 | head -n 1 | cut -d' ' -f2)
+if test "x$lua_version" = "x$luac_version"; then
+    AC_MSG_RESULT([yes (found $luac_version)])
+else
+    AC_MSG_RESULT([no (found $luac_version)])
+    exit 1
+fi
+dnl }}}
+
+dnl LUA_INCLUDES {{{
+if test "x$with_lua_includes" != x; then
+    LUA_INCLUDES="-I$with_lua_includes"
+elif test "x$with_lua_prefix" != x; then
+    LUA_INCLUDES="-I$with_lua_prefix/include"
+fi
+save_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS $LUA_INCLUDES"
+AC_CHECK_HEADERS([lua.h lualib.h], [],
+                 [AC_MSG_ERROR([*** missing headers lua.h or lualib.h.
+                  *** install the lua's -dev package or check --with-lua-includes option.])], [])
+CFLAGS="$save_CFLAGS"
+dnl }}}
+
+dnl LUA_LIBS {{{
+if test "x$with_lua_libraries" != x; then
+    LUA_LIBS="-L$with_lua_libraries"
+elif test "x$with_lua_prefix" != x; then
+    LUA_LIBS="-L$with_lua_prefix/lib"
+fi
+AC_CHECK_LIB([m], [exp], [lua_extra_libs="$lua_extra_libs -lm"], [])
+AC_CHECK_LIB([dl], [dlopen], [lua_extra_libs="$lua_extra_libs -ldl"], [])
+AC_CHECK_LIB([lua$with_lua_suffix],
+             [lua_call],
+             [LUA_LIBS="$LUA_LIBS -llua$with_lua_suffix $lua_extra_libs"],
+             [AC_MSG_ERROR([*** Can't find lua_call in lua$with_lua_suffix.
+                  *** Check for liblua installation or --with-lua-libraries or --with-lua-suffix options])],
+             [$LUA_LIBS $lua_extra_libs])
+dnl }}}
+
+dnl liblua_version {{{
+AC_MSG_CHECKING([for liblua version >= 5.1])
+save_CFLAGS="$CFLAGS"
+save_LIBS="$LIBS"
+CFLAGS="$CFLAGS $LUA_INCLUDES"
+LIBS="$LIBS $LUA_LIBS"
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+
+#include <lua.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int main()
+{
+    printf("(found %s, %d)... ", LUA_VERSION, LUA_VERSION_NUM);
+    if(LUA_VERSION_NUM >= 501)
+        exit(EXIT_SUCCESS);
+    exit(EXIT_FAILURE);
+}
+
+]])],
+              [AC_MSG_RESULT([yes])],
+              [AC_MSG_RESULT([no])
+               exit 1])
+CFLAGS="$save_CFLAGS"
+LIBS="$save_LIBS"
+dnl }}}
+
+dnl }}}
+
+dnl HAS_SYSTEM_ASPRINTF {{{
+
+AC_SUBST([HAS_SYSTEM_ASPRINTF])
+
+AC_CHECK_FUNC([asprintf],
+              [AC_CHECK_FUNC([vasprintf],
+                             [HAS_SYSTEM_ASPRINTF="1"])])
+
+dnl }}}
+
+dnl XOPEN_SOURCE {{{
+
+AC_SUBST([XOPEN_SOURCE])
+
+AC_ARG_ENABLE([xopen-source],
+              [AS_HELP_STRING([--enable-xopen-source],
+                              [Enable X/Open C flags (development use only)])])
+
+if test "x$enable_xopen_source" = xyes; then
+    AC_MSG_CHECKING([for $CC flags to accept X/Open C])
+    case $host in
+    # FIXME: what flags are required for other hosts/compilers?
+    *-*-solaris* | *-*-sunos*)
+        XOPEN_SOURCE="-D__EXTENSIONS__"
+        ;;
+    *)
+        XOPEN_SOURCE="-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED"
+        ;;
+    esac
+    AC_MSG_RESULT([$XOPEN_SOURCE])
+fi
+
+dnl }}}
+
+dnl C99_SOURCE {{{
+
+AC_SUBST([C99_SOURCE])
+
+AC_ARG_ENABLE([c99-source],
+              [AS_HELP_STRING([--enable-c99-source],
+                              [Enable C99 source (development use only)])])
+
+if test "x$enable_c99_source" = xyes; then
+    AC_MSG_CHECKING([for $CC flags to accept C99])
+    case $CC in
+    # FIXME: what flags are required for other hosts/compilers?
+    gcc*)
+        C99_SOURCE="-std=c99"
+        ;;
+    esac
+    AC_MSG_RESULT([$C99_SOURCE])
+fi
+
+dnl }}}
+
+dnl _DCF_HAS_VA_COPY {{{
+
+AC_SUBST([_DCF_HAS_VA_COPY])
+
+dnl check for va_copy {{{
+
+AC_MSG_CHECKING([for va_copy])
+save_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS $C99_SOURCE"
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+void f(void *last, ...)
+{
+    va_list ap, aq;
+    va_start(ap, last);
+    va_copy(aq, ap);
+    va_end(ap);
+    exit(va_arg(aq, int));
+}
+
+int main()
+{
+    f(NULL, EXIT_SUCCESS);
+    exit(EXIT_FAILURE);
+}
+
+]])],
+                  [AC_MSG_RESULT([yes])
+                   _DCF_HAS_VA_COPY="-DCF_HAVE_VA_COPY"],
+                  [AC_MSG_RESULT([no])])
+CFLAGS="$save_CFLAGS"
+
+dnl }}}
+
+dnl check whether -ansi breaks va_copy {{{
+
+if test "x${XOPEN_SOURCE}" != "x"; then
+    AC_MSG_CHECKING([whether XOPEN_SOURCE breaks va_copy])
+    save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS $XOPEN_SOURCE $C99_SOURCE"
+    AC_LINK_IFELSE([AC_LANG_SOURCE([[
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+void f(void *last, ...)
+{
+    va_list ap, aq;
+    va_start(ap, last);
+    va_copy(aq, ap);
+    va_end(ap);
+    va_end(aq);
+}
+
+int main()
+{
+    f(NULL);
+    exit(EXIT_SUCCESS);
+}
+
+]])],
+                      [AC_MSG_RESULT([no])],
+                      [AC_MSG_RESULT([yes, clearing XOPEN_SOURCE])
+                       XOPEN_SOURCE=""])
+    CFLAGS="$save_CFLAGS"
+fi
+dnl }}}
+
+dnl }}}
+
+dnl _DCF_NO_LOCALE {{{
+
+AC_SUBST([_DCF_NO_LOCALE])
+
+AC_ARG_ENABLE([locale],
+                         [AS_HELP_STRING([--disable-locale],
+                                                         [Disable locale support])])
+
+if test "x$enable_locale" == xno; then
+       _DCF_NO_LOCALE="-DCF_NO_LOCALE"
+fi
+
+dnl }}}
+
+
+AC_OUTPUT([system-ac.mk])
+
+if test "x$lua_version" = "x5.0"; then
+    AC_MSG_NOTICE([**])
+    AC_MSG_NOTICE([** lua version 5.0 is buggy, consider upgrading to 5.0.1])
+    AC_MSG_NOTICE([** see http://lua-users.org/lists/lua-l/2003-10/msg00039.html])
+    AC_MSG_NOTICE([**])
+fi
+
+dnl vim: foldmethod=marker tabstop=4 shiftwidth=4
diff --git a/build/ac/system-ac.mk.in b/build/ac/system-ac.mk.in
new file mode 100644 (file)
index 0000000..035a515
--- /dev/null
@@ -0,0 +1,190 @@
+##
+## System settings
+##
+
+
+##
+## Installation paths
+##
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+# Unless you are creating a package conforming to some OS's standards, you
+# probably do not want to modify the following directories:
+
+# Main binaries
+BINDIR=@bindir@
+# Configuration .lua files
+ETCDIR=@sysconfdir@/@PACKAGE_TARNAME@
+# Some .lua files and ion-* shell scripts
+SHAREDIR=@datadir@/@PACKAGE_TARNAME@
+# Manual pages
+MANDIR=@datadir@/man
+# Some documents
+DOCDIR=@datadir@/doc/@PACKAGE_TARNAME@
+# Nothing at the moment
+INCDIR=@includedir@
+# Nothing at the moment
+LIBDIR=@libdir@
+# Modules
+MODULEDIR=@libdir@/@PACKAGE_TARNAME@/mod
+# Compiled Lua source code
+LCDIR=@libdir@/@PACKAGE_TARNAME@/lc
+# ion-completefile (does not belong in SHAREDIR being a binary file)
+EXTRABINDIR=@libexecdir@/@PACKAGE_TARNAME@/bin
+# For ion-completeman system-wide cache
+#VARDIR=@localstatedir@/cache/@PACKAGE_TARNAME@
+VARDIR=/var/cache/@PACKAGE_TARNAME@
+# Message catalogs
+LOCALEDIR=@prefix@/share/locale
+
+
+##
+## Modules
+##
+
+# Set PRELOAD_MODULES=1 if your system does not support dynamically loaded
+# modules through 'libdl' or has non-standard naming conventions.
+PRELOAD_MODULES=@PRELOAD_MODULES@
+
+# Flags to link with libdl.
+DL_LIBS=@DL_LIBS@
+
+
+##
+## Lua
+##
+
+# If you have installed Lua 5.0 from the official tarball without changing
+# paths, this should do it.
+#LUA_DIR=/usr/local
+LUA_LIBS = @LUA_LIBS@
+LUA_INCLUDES = @LUA_INCLUDES@
+LUA=@LUA@
+LUAC=@LUAC@
+
+# If you are using the Debian packages, the following settings should be
+# what you want.
+#LUA_LIBS=`lua-config50 --libs`
+#LUA_INCLUDES=`lua-config50 --include`
+#LUA=lua50
+#LUAC=luac50
+
+
+##
+## X libraries, includes and options
+##
+
+#X11_PREFIX=/usr/X11R6
+# SunOS/Solaris
+#X11_PREFIX=/usr/openwin
+
+X11_LIBS=@X_EXTRA_LIBS@ @X_PRE_LIBS@ -lX11 @X_LIBS@
+X11_INCLUDES=@X_CFLAGS@
+
+# Change commenting to disable Xinerama support
+XINERAMA_LIBS=@XINERAMA_LIBS@
+DEFINES += @_DCF_NO_XINERAMA@
+
+# XFree86 libraries up to 4.3.0 have a bug that will cause Ion to segfault
+# if Opera is used when i18n support is enabled. The following setting
+# should work around that situation.
+DEFINES += @_DCF_XFREE86_TEXTPROP_BUG_WORKAROUND@
+
+# Use the Xutf8 routines (XFree86 extension) instead of Xmb routines in
+# an UTF8 locale.
+DEFINES += @_DCF_DE_USE_XUTF8@
+
+# Remap F11 key to SunF36 and F12 to SunF37? You may want to set this
+# on SunOS.
+DEFINES += @_DCF_SUN_F1X_REMAP@
+
+
+##
+## libc
+##
+
+# You may uncomment this if you know your system has
+# asprintf and vasprintf in the c library. (gnu libc has.)
+# If HAS_SYSTEM_ASPRINTF is not defined, an implementation
+# in sprintf_2.2/ is used.
+HAS_SYSTEM_ASPRINTF=@HAS_SYSTEM_ASPRINTF@
+
+
+# If you're on an archaic system (such as relatively recent *BSD releases)
+# without even dummy multibyte/widechar and localisation support, you may
+# have to uncomment the following line:
+DEFINES += @_DCF_NO_LOCALE@
+
+
+##
+## C compiler
+##
+
+CC=@CC@
+
+# Same as '-Wall -pedantic' without '-Wunused' as callbacks often
+# have unused variables.
+WARN=  -W -Wimplicit -Wreturn-type -Wswitch -Wcomment \
+       -Wtrigraphs -Wformat -Wchar-subscripts \
+       -Wparentheses -pedantic -Wuninitialized
+
+CFLAGS=@CFLAGS@ $(WARN) $(DEFINES) $(EXTRA_INCLUDES) $(INCLUDES)
+LDFLAGS=@LDFLAGS@ $(EXTRA_LIBS) $(LIBS)
+EXPORT_DYNAMIC=-Xlinker --export-dynamic
+
+# The following options are mainly for development use and can be used
+# to check that the code seems to conform to some standards. Depending
+# on the version and vendor of you libc, the options may or may not have
+# expected results. If you define one of C99_SOURCE or XOPEN_SOURCE, you
+# may also have to define the other.
+
+#C89_SOURCE=-ansi
+
+#POSIX_SOURCE=@_D_POSIX_SOURCE@
+
+# Most systems
+#XOPEN_SOURCE=-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED
+# SunOS, (Irix)
+#XOPEN_SOURCE=-D__EXTENSIONS__
+
+#C99_SOURCE=-std=c99 -DCF_HAS_VA_COPY
+
+# The -DCF_HAS_VA_COPY option should allow for some optimisations, and
+# in some cases simply defining
+C99_SOURCE=@_DCF_HAS_VA_COPY@
+# might allow for those optimisations to be taken without any  special
+# libc or compiler options.
+
+
+##
+## make depend
+##
+
+DEPEND_FILE=.depend
+DO_MAKE_DEPEND=$(CC) -MM $(DEFINES) $(EXTRA_INCLUDES) $(INCLUDES)
+MAKE_DEPEND=$(DO_MAKE_DEPEND) $(SOURCES) > $(DEPEND_FILE)
+
+##
+## AR
+##
+
+AR=@AR@
+ARFLAGS=cr
+RANLIB=@RANLIB@
+
+
+##
+## Install & strip
+##
+
+INSTALL=@INSTALL@
+INSTALLDIR=mkdir -p
+
+BIN_MODE=755
+DATA_MODE=644
+
+STRIP=@STRIP@
+
+RM=rm
diff --git a/build/libs.mk b/build/libs.mk
new file mode 100644 (file)
index 0000000..acffc1e
--- /dev/null
@@ -0,0 +1,16 @@
+
+LIBS_SUBDIRS = libtu libextl libmainloop
+
+LIBTU_DIR = $(TOPDIR)/libtu
+LIBTU_INCLUDES = -I$(TOPDIR)
+LIBTU_LIBS = -L$(LIBTU_DIR) -ltu
+
+LIBMAINLOOP_DIR = $(TOPDIR)/libmainloop
+LIBMAINLOOP_INCLUDES = -I$(TOPDIR)
+LIBMAINLOOP_LIBS = -L$(LIBMAINLOOP_DIR) -lmainloop
+
+LIBEXTL_DIR = $(TOPDIR)/libextl
+LIBEXTL_INCLUDES = -I$(TOPDIR)
+LIBEXTL_LIBS = -L$(LIBEXTL_DIR) -lextl
+
+MKEXPORTS = $(LUA) $(LIBEXTL_DIR)/libextl-mkexports
diff --git a/build/mkman.lua b/build/mkman.lua
new file mode 100644 (file)
index 0000000..bd867c6
--- /dev/null
@@ -0,0 +1,306 @@
+--
+-- build/mkman.lua
+--
+-- Translates bindings from Ion configuration into a listing for
+-- manual pages.
+--
+
+
+-- Translations {{{
+
+local translations={}
+
+local function gettext(x)
+    local t=translations[x]
+    if not t or t=="" then
+        return x
+    else
+        return t
+    end
+end
+
+local function TR(x, ...)
+    return string.format(gettext(x), unpack(arg))
+end
+
+local function read_translations(pofile)
+    local f, err=io.open(pofile)
+    if not f then 
+        error(err) 
+    end
+    
+    local msgid, msgstr, st, en
+    
+    for l in f:lines() do
+        if string.find(l, "^msgid") then
+            if msgid then
+                assert(msgstr)
+                translations[msgid]=msgstr
+                msgstr=nil
+            end
+            st, en, msgid=string.find(l, '^msgid%s*"(.*)"%s*$')
+        elseif string.find(l, "^msgstr") then
+            assert(msgid and not msgstr)
+            st, en, msgstr=string.find(l, '^msgstr%s*"(.*)"%s*$')
+        elseif not (string.find(l, "^%s*#") or string.find(l, "^%s*$")) then
+            local st, en, str=string.find(l, '^%s*"(.*)"%s*$')
+            assert(msgid or msgstr)
+            if not msgstr then
+                msgid=msgid..str
+            else
+                msgstr=msgstr..str
+            end
+        end
+    end
+
+    if msgid then
+        assert(msgstr)
+        translations[msgid]=msgstr
+    end
+    
+    f:close()
+end
+
+-- }}}
+
+
+-- File parsing {{{
+
+local function dobindings(fn, bindings)
+    local p={}
+    
+    p.META="Mod1+"
+    p.ALTMETA=""
+    
+    function p.bdoc(text)
+        return {action = "doc", text = text}
+    end
+    
+    function p.submap(kcb_, list)
+        if not list then
+            return function(lst)
+                       return submap(kcb_, lst)
+                   end
+        end
+        return {action = "kpress", kcb = kcb_, submap = list}
+    end
+    
+    local function putcmd(cmd, guard, t)
+        t.cmd=cmd
+        t.guard=guard
+        return t
+    end
+    
+    function p.kpress(keyspec, cmd, guard)
+        return putcmd(cmd, guard, {action = "kpress", kcb = keyspec})
+    end
+    
+    function p.kpress_wait(keyspec, cmd, guard)
+        return putcmd(cmd, guard, {action = "kpress_wait", kcb = keyspec})
+    end
+    
+    local function mact(act_, kcb_, cmd, guard)
+        local st, en, kcb2_, area_=string.find(kcb_, "([^@]*)@(.*)")
+        return putcmd(cmd, guard, {
+            action = act_,
+            kcb = (kcb2_ or kcb_),
+            area = area_,
+        })
+    end
+    
+    function p.mclick(buttonspec, cmd, guard)
+        return mact("mclick", buttonspec, cmd, guard)
+    end
+    
+    function p.mdblclick(buttonspec, cmd, guard)
+        return mact("mdblclick", buttonspec, cmd, guard)
+    end
+    
+    function p.mpress(buttonspec, cmd, guard)
+        return mact("mpress", buttonspec, cmd, guard)
+    end
+
+    function p.mdrag(buttonspec, cmd, guard)
+        return mact("mdrag", buttonspec, cmd, guard)
+    end
+    
+    function p.defbindings(context, bnd)
+        if not bindings[context] then
+            bindings[context]=bnd
+        else
+            for _, v in ipairs(bnd) do
+                table.insert(bindings[context], v)
+            end
+        end
+    end
+
+    local function dummy() 
+    end
+    
+    p.defmenu=dummy
+    p.defctxmenu=dummy
+    p.menuentry=dummy
+    p.submenu=dummy
+
+    p.ioncore={ set=dummy }
+    
+    local env=setmetatable({}, {
+        __index=p, 
+        __newindex=function(x) 
+                       error("Setting global "..tostring(x))
+                   end,
+    })
+    setfenv(fn, env)
+    fn()
+    return bindings
+end
+
+local function parsefile(f, bindings)
+    local fn, err=loadfile(f)
+    if not fn then
+        error(err)
+    end
+    
+    return dobindings(fn, bindings)
+end
+    
+-- }}}
+
+
+-- Binding output {{{
+
+local function docgroup_bindings(bindings)
+    local out={}
+    local outi=0
+    
+    local function parsetable(t, prefix)
+        for _, v in ipairs(t) do
+            if v.kcb then
+                v.kcb=string.gsub(v.kcb, "AnyModifier%+", "")
+            end
+            if v.action=="doc" then
+                outi=outi+1
+                out[outi]={doc=v.text, bindings={}}
+            elseif v.submap then
+                parsetable(v.submap, prefix..v.kcb.." ")
+            else
+                assert(out[outi])
+                v.kcb=prefix..v.kcb
+                table.insert(out[outi].bindings, v)
+            end
+        end
+    end
+    
+    parsetable(bindings, "")
+    
+    return out
+end
+
+
+local function combine_bindings(v)    
+    local nact={
+        ["mpress"]=TR("press"),
+        ["mclick"]=TR("click"),
+        ["mdrag"]=TR("drag"),
+        ["mdblclick"]=TR("double click"),
+    }
+    local first=true
+    local s=""
+    for _, b in ipairs(v.bindings) do
+        if not first then
+            s=s..', '
+        end
+        first=false
+        if b.action=="kpress" or b.action=="kpress_wait" then
+            s=s..b.kcb
+        else
+            if not b.area then
+                s=s..TR("%s %s", b.kcb, nact[b.action])
+            else
+                s=s..TR("%s %s at %s", b.kcb, nact[b.action], b.area)
+            end
+        end
+    end
+    
+    return s
+end    
+
+local function write_bindings_man(db)
+    local function write_binding_man(v)
+        return '.TP\n.B '..combine_bindings(v)..'\n'..gettext(v.doc)..'\n'
+    end
+    
+    local s=""
+    
+    for _, v in ipairs(db) do
+        if #(v.bindings)>0 then
+            s=s..write_binding_man(v)
+        end
+    end
+    
+    return s
+end
+
+-- }}}
+
+-- Main {{{
+
+local infile
+local outfile
+local bindings={}
+local replaces={}
+
+local function doargs(a)
+    local i=1
+    while i<=#a do
+        if a[i]=='-o' then
+            outfile=a[i+1]
+            i=i+2
+        elseif a[i]=='-i' then
+            infile=a[i+1]
+            i=i+2
+        elseif a[i]=='-D' then
+            replaces[a[i+1]]=a[i+2]
+            i=i+3
+        elseif a[i]=='-po' then
+            read_translations(a[i+1])
+            i=i+2
+        else
+            parsefile(a[i], bindings)
+            i=i+1
+        end
+    end
+end
+
+doargs(arg)
+
+local f, err=io.open(infile)
+if not f then
+    error(err)
+end
+
+local of, oerr=io.open(outfile, 'w+')
+if not of then
+    error(oerr)
+end
+
+for l in f:lines() do
+    l=string.gsub(l, '%s*BINDINGS:([%w%.%-]+)%s*', 
+                  function(s)
+                      if not bindings[s] then
+                          --error('No bindings for '..s)
+                          return "?"
+                      end
+                      local db=docgroup_bindings(bindings[s])
+                      return write_bindings_man(db)
+                  end)
+    
+    for pat, rep in pairs(replaces) do
+        l=string.gsub(l, pat, rep)
+    end
+    
+    of:write(l..'\n')
+end
+
+-- }}}
+
diff --git a/build/mkpreload.lua b/build/mkpreload.lua
new file mode 100644 (file)
index 0000000..6b51ceb
--- /dev/null
@@ -0,0 +1,32 @@
+
+io.stdout:write([[
+/* Automatically generated. */
+                  
+#include <ioncore/modules.h>
+
+]]);
+
+for _, v in ipairs(arg) do
+    io.stdout:write(string.format([[
+extern bool %s_init();
+extern void %s_deinit();
+]], v, v));
+
+end
+
+io.stdout:write([[
+
+WStaticModuleInfo ioncore_static_modules[]={
+]])
+
+for _, v in ipairs(arg) do
+    io.stdout:write(string.format(
+        '    {"%s", %s_init, %s_deinit, FALSE},\n',
+        v, v, v));                          
+end
+
+io.stdout:write([[
+    {NULL, NULL, NULL, FALSE}
+};
+]])        
+
diff --git a/build/rules.mk b/build/rules.mk
new file mode 100644 (file)
index 0000000..3f8a42d
--- /dev/null
@@ -0,0 +1,204 @@
+##
+## Some make rules
+##
+
+ifdef MODULE
+ifeq ($(PRELOAD_MODULES),1)
+MODULE_TARGETS := $(MODULE).a $(MODULE).lc
+else
+MODULE_TARGETS := $(MODULE).so $(MODULE).lc
+endif
+TARGETS := $(TARGETS) $(MODULE_TARGETS)
+endif
+
+ifdef LUA_SOURCES
+LUA_COMPILED := $(subst .lua,.lc, $(LUA_SOURCES))
+TARGETS := $(TARGETS) $(LUA_COMPILED)
+endif
+
+
+# Main targets
+######################################
+
+.PHONY: subdirs
+.PHONY: subdirs-clean
+.PHONY: subdirs-realclean
+.PHONY: subdirs-depend
+.PHONY: subdirs-install
+.PHONY: _install
+.PHONY: _depend
+.PHONY: _exports
+
+all: subdirs _exports $(TARGETS)
+
+clean: subdirs-clean _clean
+
+realclean: subdirs-realclean _clean _realclean
+
+depend: subdirs-depend _depend
+
+install: subdirs-install _install
+
+
+# Exports
+######################################
+
+ifdef MAKE_EXPORTS
+
+EXPORTS_C = exports.c
+EXPORTS_H = exports.h
+
+DEPEND_DEPENDS += $(EXPORTS_H)
+
+TO_CLEAN := $(TO_CLEAN) $(EXPORTS_C) $(EXPORTS_H)
+
+_exports: $(EXPORTS_C)
+
+$(EXPORTS_H): $(EXPORTS_C)
+
+$(EXPORTS_C): $(SOURCES)
+       $(MKEXPORTS) -module $(MAKE_EXPORTS) -o $(EXPORTS_C) -h $(EXPORTS_H) \
+       $(SOURCES) $(MKEXPORTS_EXTRAS)
+
+else # !MAKE_EXPORTS
+
+EXPORTS_C = 
+EXPORTS_H = 
+
+endif # !MAKE_EXPORTS
+
+
+# Compilation and linking
+######################################
+
+OBJS=$(subst .c,.o,$(SOURCES) $(EXPORTS_C))
+
+ifdef MODULE
+
+ifneq ($(PRELOAD_MODULES),1)
+
+CC_PICFLAGS=-fPIC -DPIC
+LD_SHAREDFLAGS=-shared
+
+%.o: %.c
+       $(CC) $(CC_PICFLAGS) $(CFLAGS) -c $< -o $@
+
+$(MODULE).so: $(OBJS) $(EXT_OBJS)
+       $(CC) $(LD_SHAREDFLAGS) $(LDFLAGS) $(OBJS) $(EXT_OBJS) -o $@
+
+
+module_install: module_stub_install
+       $(INSTALLDIR) $(MODULEDIR)
+       $(INSTALL) -m $(BIN_MODE) $(MODULE).so $(MODULEDIR)
+
+else # PRELOAD_MODULES
+
+PICOPT=-fPIC -DPIC
+LINKOPT=-shared
+
+%.o: %.c
+       $(CC) $(CFLAGS) -c $< -o $@
+
+$(MODULE).a: $(OBJS) $(EXT_OBJS)
+       $(AR) $(ARFLAGS) $@ $+
+       $(RANLIB) $@
+
+module_install: module_stub_install
+
+endif # PRELOAD_MODULES
+
+module_stub_install:
+       $(INSTALLDIR) $(LCDIR)
+       $(INSTALL) -m $(DATA_MODE) $(MODULE).lc $(LCDIR)
+
+ifndef MODULE_STUB
+
+$(MODULE).lc:
+       echo "ioncore.load_module('$(MODULE)')" | $(LUAC) -o $@ -
+else
+
+LUA_SOURCES += $(MODULE_STUB)
+
+endif #MODULE_STUB
+
+else # !MODULE
+
+
+%.o: %.c
+       $(CC) $(CFLAGS) -c $< -o $@
+
+
+endif# !MODULE
+
+
+# Clean rules
+######################################
+
+_clean:
+       $(RM) -f $(TO_CLEAN) core $(DEPEND_FILE) $(OBJS)
+
+_realclean:
+       $(RM) -f $(TO_REALCLEAN) $(TARGETS)
+
+# Lua rules
+######################################
+
+%.lc: %.lua
+       $(LUAC) -o $@ $<
+
+lc_install:
+       $(INSTALLDIR) $(LCDIR)
+       for i in $(LUA_COMPILED); do \
+               $(INSTALL) -m $(DATA_MODE) $$i $(LCDIR); \
+       done
+
+etc_install:
+       $(INSTALLDIR) $(ETCDIR)
+       for i in $(ETC); do \
+               $(INSTALL) -m $(DATA_MODE) $$i $(ETCDIR); \
+       done
+
+# Dependencies
+######################################
+
+ifdef SOURCES
+
+_depend: $(DEPEND_DEPENDS)
+       $(MAKE_DEPEND)
+
+ifeq ($(DEPEND_FILE),$(wildcard $(DEPEND_FILE)))
+include $(DEPEND_FILE)
+endif
+
+endif
+
+# Subdirectories
+######################################
+
+ifdef SUBDIRS
+
+subdirs:
+       set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
+
+subdirs-depend:
+       set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i depend; done
+
+subdirs-clean:
+       set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i clean; done
+
+subdirs-realclean:
+       set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i realclean; done
+
+subdirs-install:
+       set -e; for i in $(INSTALL_SUBDIRS); do $(MAKE) -C $$i install; done
+
+endif
+
+# Localisation
+######################################
+
+TO_CLEAN += potfiles_c potfiles_lua
+
+_potfiles:
+       echo "$(SOURCES)"|tr ' ' '\n' > potfiles_c
+       echo "$(LUA_SOURCES) $(ETC)"|tr ' ' '\n' > potfiles_lua
diff --git a/build/system-inc.mk b/build/system-inc.mk
new file mode 100644 (file)
index 0000000..39a767a
--- /dev/null
@@ -0,0 +1,18 @@
+# Use system-ac.mk if it exist, system.mk otherwise.
+
+ifndef TOPDIR
+  TOPDIR=.
+endif
+
+SYSTEM_MK = $(TOPDIR)/system.mk
+AC_SYSTEM_MK = $(TOPDIR)/build/ac/system-ac.mk
+
+ifeq ($(AC_SYSTEM_MK),$(wildcard $(AC_SYSTEM_MK)))
+  # Using system-ac.mk
+  include $(AC_SYSTEM_MK)
+else
+  # Not using system-ac.mk
+  include $(SYSTEM_MK)
+endif
+
+include $(TOPDIR)/build/libs.mk
diff --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..23935b7
--- /dev/null
+++ b/config.h
@@ -0,0 +1,49 @@
+/*
+ * config.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_CONFIG_H
+#define ION_CONFIG_H
+
+/* #define CF_NO_LOCK_HACK */
+
+#define CF_FALLBACK_FONT_NAME "fixed"
+/*#define CF_FALLBACK_FONT_NAME "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*"*/
+#define CF_DRAG_TRESHOLD 2
+#define CF_DBLCLICK_DELAY 250
+
+#define CF_MAX_MOVERES_STR_SIZE 32
+
+#define CF_TAB_TEXT_ALIGN ALIGN_CENTER
+#define CF_RESIZE_DELAY 1500
+
+#define CF_XMESSAGE "xmessage -file "
+
+#define CF_EDGE_RESISTANCE 16
+
+#define CF_RAISE_DELAY 500
+
+#define CF_STATUSBAR_SYSTRAY_HEIGHT 24
+
+/* Cursors
+ */
+
+#define CF_CURSOR_DEFAULT XC_left_ptr
+#define CF_CURSOR_RESIZE XC_sizing
+#define CF_CURSOR_MOVE XC_fleur
+#define CF_CURSOR_DRAG XC_cross
+#define CF_CURSOR_WAITKEY XC_icon
+
+#define CF_STR_EMPTY "<empty frame>"
+#define CF_STR_EMPTY_LEN 13
+
+#define CF_STDISP_MIN_SZ 8
+
+#endif /* ION_CONFIG_H */
diff --git a/de/Makefile b/de/Makefile
new file mode 100644 (file)
index 0000000..6ac4bf0
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## Default drawing engine module Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES=init.c draw.c font.c colour.c brush.c fontset.c style.c
+
+MODULE=de
+
+MAKE_EXPORTS=de
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: module_install
diff --git a/de/brush.c b/de/brush.c
new file mode 100644 (file)
index 0000000..a0576a6
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * ion/de/brush.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libextl/extl.h>
+
+#include <ioncore/common.h>
+#include <ioncore/rootwin.h>
+#include <ioncore/extlconv.h>
+#include <ioncore/ioncore.h>
+
+#include "brush.h"
+#include "style.h"
+#include "font.h"
+#include "colour.h"
+#include "private.h"
+
+
+/*{{{ Brush creation and releasing */
+
+
+bool debrush_init(DEBrush *brush, Window win,
+                  const char *stylename, DEStyle *style)
+{
+    brush->d=style;
+    brush->extras_fn=NULL;
+    brush->indicator_w=0;
+    brush->win=win;
+    brush->clip_set=FALSE;
+    
+    style->usecount++;
+
+    if(!grbrush_init(&(brush->grbrush))){
+        style->usecount--;
+        return FALSE;
+    }
+    
+    if(MATCHES("tab-frame", stylename) || MATCHES("tab-info", stylename)){
+        brush->extras_fn=debrush_tab_extras;
+        if(!style->tabbrush_data_ok)
+            destyle_create_tab_gcs(style);
+    }else if(MATCHES("tab-menuentry", stylename)){
+        brush->extras_fn=debrush_menuentry_extras;
+        brush->indicator_w=grbrush_get_text_width((GrBrush*)brush, 
+                                                  DE_SUB_IND, 
+                                                  DE_SUB_IND_LEN);
+    }
+    
+    return TRUE;
+}
+
+
+DEBrush *create_debrush(Window win, const char *stylename, DEStyle *style)
+{
+    CREATEOBJ_IMPL(DEBrush, debrush, (p, win, stylename, style));
+}
+
+
+static DEBrush *do_get_brush(Window win, WRootWin *rootwin, 
+                             const char *stylename, bool slave)
+{
+    DEStyle *style=de_get_style(rootwin, stylename);
+    DEBrush *brush;
+    
+    if(style==NULL)
+        return NULL;
+    
+    brush=create_debrush(win, stylename, style);
+    
+    /* Set background colour */
+    if(brush!=NULL && !slave){
+        grbrush_enable_transparency(&(brush->grbrush), 
+                                    GR_TRANSPARENCY_DEFAULT);
+    }
+    
+    return brush;
+}
+
+
+DEBrush *de_get_brush(Window win, WRootWin *rootwin, const char *stylename)
+{
+    return do_get_brush(win, rootwin, stylename, FALSE);
+}
+
+
+DEBrush *debrush_get_slave(DEBrush *master, WRootWin *rootwin, 
+                           const char *stylename)
+{
+    return do_get_brush(master->win, rootwin, stylename, TRUE);
+}
+
+
+void debrush_deinit(DEBrush *brush)
+{
+    destyle_unref(brush->d);
+    brush->d=NULL;
+    grbrush_deinit(&(brush->grbrush));
+}
+
+
+void debrush_release(DEBrush *brush)
+{
+    destroy_obj((Obj*)brush);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Border widths and extra information */
+
+
+void debrush_get_border_widths(DEBrush *brush, GrBorderWidths *bdw)
+{
+    DEStyle *style=brush->d;
+    DEBorder *bd=&(style->border);
+    uint tmp;
+    
+    switch(bd->style){
+    case DEBORDER_RIDGE:
+    case DEBORDER_GROOVE:
+        tmp=bd->sh+bd->hl+bd->pad;
+        bdw->top=tmp; bdw->bottom=tmp; bdw->left=tmp; bdw->right=tmp;
+        break;
+    case DEBORDER_INLAID:
+        tmp=bd->sh+bd->pad; bdw->top=tmp; bdw->left=tmp;
+        tmp=bd->hl+bd->pad; bdw->bottom=tmp; bdw->right=tmp;
+        break;
+    case DEBORDER_ELEVATED:
+    default:
+        tmp=bd->hl+bd->pad; bdw->top=tmp; bdw->left=tmp;
+        tmp=bd->sh+bd->pad; bdw->bottom=tmp; bdw->right=tmp;
+        break;
+    }
+    
+    bdw->tb_ileft=bdw->left;
+    bdw->tb_iright=bdw->right;
+    bdw->spacing=style->spacing;
+    
+    bdw->right+=brush->indicator_w;
+    bdw->tb_iright+=brush->indicator_w;
+}
+
+
+bool debrush_get_extra(DEBrush *brush, const char *key, char type, void *data)
+{
+    DEStyle *style=brush->d;
+    while(style!=NULL){
+        if(extl_table_get(style->data_table, 's', type, key, data))
+            return TRUE;
+        style=style->based_on;
+    }
+    return FALSE;
+}
+
+
+
+/*}}}*/
+
+
+/*{{{ Class implementation */
+
+
+static DynFunTab debrush_dynfuntab[]={
+    {grbrush_release, debrush_release},
+    {grbrush_draw_border, debrush_draw_border},
+    {grbrush_draw_borderline, debrush_draw_borderline},
+    {grbrush_get_border_widths, debrush_get_border_widths},
+    {grbrush_draw_string, debrush_draw_string},
+    {debrush_do_draw_string, debrush_do_draw_string_default},
+    {grbrush_get_font_extents, debrush_get_font_extents},
+    {(DynFun*)grbrush_get_text_width, (DynFun*)debrush_get_text_width},
+    {grbrush_draw_textbox, debrush_draw_textbox},
+    {grbrush_draw_textboxes, debrush_draw_textboxes},
+    {grbrush_set_window_shape, debrush_set_window_shape},
+    {grbrush_enable_transparency, debrush_enable_transparency},
+    {grbrush_clear_area, debrush_clear_area},
+    {grbrush_fill_area, debrush_fill_area},
+    {(DynFun*)grbrush_get_extra, (DynFun*)debrush_get_extra},
+    {(DynFun*)grbrush_get_slave, (DynFun*)debrush_get_slave},
+    {grbrush_begin, debrush_begin},
+    {grbrush_end, debrush_end},
+    END_DYNFUNTAB
+};
+
+
+IMPLCLASS(DEBrush, GrBrush, debrush_deinit, debrush_dynfuntab);
+
+
+/*}}}*/
+
+
+
diff --git a/de/brush.h b/de/brush.h
new file mode 100644 (file)
index 0000000..a1a6b3f
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * ion/de/brush.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_DE_BRUSH_H
+#define ION_DE_BRUSH_H
+
+#include <libextl/extl.h>
+
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+#include <ioncore/rectangle.h>
+
+INTRCLASS(DEBrush);
+
+#include "style.h"
+#include "colour.h"
+
+
+typedef void DEBrushExtrasFn(DEBrush *brush, 
+                             const WRectangle *g, DEColourGroup *cg,
+                             GrBorderWidths *bdw,
+                             GrFontExtents *fnte,
+                             const char *a1, const char *a2,
+                             bool pre);
+
+DECLCLASS(DEBrush){
+    GrBrush grbrush;
+    DEStyle *d;
+    DEBrushExtrasFn *extras_fn;
+    int indicator_w;
+    Window win;
+    bool clip_set;
+};
+
+extern DEBrush *de_get_brush(Window win, WRootWin *rootwin,
+                             const char *style);
+
+extern DEBrush *create_debrush(Window win, 
+                               const char *stylename, DEStyle *style);
+extern bool debrush_init(DEBrush *brush, Window win,
+                         const char *stylename, DEStyle *style);
+extern void debrush_deinit(DEBrush *brush);
+
+extern DEBrush *debrush_get_slave(DEBrush *brush, WRootWin *rootwin, 
+                                  const char *style);
+
+extern void debrush_release(DEBrush *brush);
+
+
+extern DEColourGroup *debrush_get_colour_group2(DEBrush *brush, 
+                                                const char *attr_p1,
+                                                const char *attr_p2);
+
+extern DEColourGroup *debrush_get_colour_group(DEBrush *brush, 
+                                               const char *attr);
+
+
+/* Begin/end */
+
+extern void debrush_begin(DEBrush *brush, const WRectangle *geom, int flags);
+extern void debrush_end(DEBrush *brush);
+
+/* Information */
+
+extern void debrush_get_border_widths(DEBrush *brush, GrBorderWidths *bdw);
+extern bool debrush_get_extra(DEBrush *brush, const char *key, char type, 
+                              void *data);
+
+/* Borders & boxes */
+
+extern void debrush_draw_border(DEBrush *brush, 
+                                const WRectangle *geom,
+                                const char *attrib);
+extern void debrush_draw_borderline(DEBrush *brush, const WRectangle *geom,
+                                    const char *attrib, GrBorderLine line);
+
+extern void debrush_draw_textbox(DEBrush *brush, const WRectangle *geom,
+                                 const char *text, const char *attr,
+                                 bool needfill);
+
+extern void debrush_draw_textboxes(DEBrush *brush, const WRectangle *geom, 
+                                   int n, const GrTextElem *elem, 
+                                   bool needfill, const char *common_attrib);
+
+extern DEBrushExtrasFn debrush_tab_extras;
+extern DEBrushExtrasFn debrush_menuentry_extras;
+
+/* Misc */
+
+extern void debrush_set_window_shape(DEBrush *brush, bool rough,
+                                     int n, const WRectangle *rects);
+
+extern void debrush_enable_transparency(DEBrush *brush, GrTransparency mode);
+
+extern void debrush_fill_area(DEBrush *brush, const WRectangle *geom, 
+                              const char *attr);
+extern void debrush_clear_area(DEBrush *brush, const WRectangle *geom);
+
+
+#endif /* ION_DE_BRUSH_H */
diff --git a/de/colour.c b/de/colour.c
new file mode 100644 (file)
index 0000000..c3797c3
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * ion/de/colour.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <ioncore/common.h>
+#include "colour.h"
+
+
+bool de_alloc_colour(WRootWin *rootwin, DEColour *ret, const char *name)
+{
+    XColor c;
+    bool ok=FALSE;
+
+    if(name==NULL)
+        return FALSE;
+
+    if(XParseColor(ioncore_g.dpy, rootwin->default_cmap, name, &c)){
+        ok=XAllocColor(ioncore_g.dpy, rootwin->default_cmap, &c);
+        if(ok)
+            *ret=c.pixel;
+    }
+    
+    return ok;
+}
+
+
+bool de_duplicate_colour(WRootWin *rootwin, DEColour in, DEColour *out)
+{
+    XColor c;
+    c.pixel=in;
+    XQueryColor(ioncore_g.dpy, rootwin->default_cmap, &c);
+    if(XAllocColor(ioncore_g.dpy, rootwin->default_cmap, &c)){
+        *out=c.pixel;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+
+void de_free_colour_group(WRootWin *rootwin, DEColourGroup *cg)
+{
+    DEColour pixels[5];
+    
+    pixels[0]=cg->bg;
+    pixels[1]=cg->fg;
+    pixels[2]=cg->hl;
+    pixels[3]=cg->sh;
+    pixels[4]=cg->pad;
+    
+    XFreeColors(ioncore_g.dpy, rootwin->default_cmap, pixels, 5, 0);
+    
+    if(cg->spec!=NULL){
+        free(cg->spec);
+        cg->spec=NULL;
+    }
+}
+
+
+void de_free_colour(WRootWin *rootwin, DEColour col)
+{
+    DEColour pixels[1];
+    
+    pixels[0]=col;
+    
+    XFreeColors(ioncore_g.dpy, rootwin->default_cmap, pixels, 1, 0);
+}
+
diff --git a/de/colour.h b/de/colour.h
new file mode 100644 (file)
index 0000000..8886b97
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * ion/de/colour.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_DE_COLOUR_H
+#define ION_DE_COLOUR_H
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/rootwin.h>
+
+
+INTRSTRUCT(DEColourGroup);
+
+
+typedef unsigned long DEColour;
+
+
+DECLSTRUCT(DEColourGroup){
+    char *spec;
+    DEColour bg, hl, sh, fg, pad;
+};
+
+
+#define DE_BLACK(rootwin) BlackPixel(ioncore_g.dpy, rootwin->xscr)
+#define DE_WHITE(rootwin) WhitePixel(ioncore_g.dpy, rootwin->xscr)
+
+bool de_init_colour_group(WRootWin *rootwin, DEColourGroup *cg);
+bool de_alloc_colour(WRootWin *rootwin, DEColour *ret, const char *name);
+bool de_duplicate_colour(WRootWin *rootwin, DEColour in, DEColour *out);
+void de_free_colour_group(WRootWin *rootwin, DEColourGroup *cg);
+void de_free_colour(WRootWin *rootwin, DEColour col);
+
+#endif /* ION_DE_COLOUR_H */
diff --git a/de/draw.c b/de/draw.c
new file mode 100644 (file)
index 0000000..aae2dae
--- /dev/null
+++ b/de/draw.c
@@ -0,0 +1,576 @@
+/*
+ * ion/de/draw.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <limits.h>
+
+#include <ioncore/global.h>
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+#include "brush.h"
+#include "font.h"
+#include "private.h"
+
+#include <X11/extensions/shape.h>
+
+
+/*{{{ Colour group lookup */
+
+
+static DEColourGroup *destyle_get_colour_group2(DEStyle *style,
+                                                const char *attr_p1,
+                                                const char *attr_p2)
+{
+    int i, score, maxscore=0;
+    DEColourGroup *maxg=&(style->cgrp);
+    
+    while(style!=NULL){
+        for(i=0; i<style->n_extra_cgrps; i++){
+            score=gr_stylespec_score2(style->extra_cgrps[i].spec,
+                                      attr_p1, attr_p2);
+            if(score>maxscore){
+                maxg=&(style->extra_cgrps[i]);
+                maxscore=score;
+            }
+        }
+        style=style->based_on;
+    }
+    
+    return maxg;
+}
+
+
+DEColourGroup *debrush_get_colour_group2(DEBrush *brush, 
+                                         const char *attr_p1,
+                                         const char *attr_p2)
+{
+    return destyle_get_colour_group2(brush->d, attr_p1, attr_p2);
+}
+
+
+DEColourGroup *debrush_get_colour_group(DEBrush *brush, const char *attr)
+{
+    return destyle_get_colour_group2(brush->d, attr, NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Borders */
+
+
+/* Draw a border at x, y with outer width w x h. Top and left 'tl' pixels
+ * wide with color 'tlc' and bottom and right 'br' pixels with colors 'brc'.
+ */
+static void do_draw_border(Window win, GC gc, int x, int y, int w, int h,
+                           uint tl, uint br, DEColour tlc, DEColour brc)
+{
+    XPoint points[3];
+    uint i=0, a=0, b=0;
+    
+    w--;
+    h--;
+
+    XSetForeground(ioncore_g.dpy, gc, tlc);
+
+    
+    a=(br!=0);
+    b=0;
+    
+    for(i=0; i<tl; i++){
+        points[0].x=x+i;        points[0].y=y+h+1-b;
+        points[1].x=x+i;        points[1].y=y+i;
+        points[2].x=x+w+1-a;    points[2].y=y+i;
+
+        if(a<br)
+            a++;
+        if(b<br)
+            b++;
+    
+        XDrawLines(ioncore_g.dpy, win, gc, points, 3, CoordModeOrigin);
+    }
+
+    
+    XSetForeground(ioncore_g.dpy, gc, brc);
+
+    a=(tl!=0);
+    b=0;
+    
+    for(i=0; i<br; i++){
+        points[0].x=x+w-i;        points[0].y=y+b;
+        points[1].x=x+w-i;        points[1].y=y+h-i;
+        points[2].x=x+a;        points[2].y=y+h-i;
+    
+        if(a<tl)
+            a++;
+        if(b<tl)
+            b++;
+        
+        XDrawLines(ioncore_g.dpy, win, gc, points, 3, CoordModeOrigin);
+    }
+}
+
+
+static void draw_border(Window win, GC gc, WRectangle *geom,
+                        uint tl, uint br, DEColour tlc, DEColour brc)
+{
+    do_draw_border(win, gc, geom->x, geom->y, geom->w, geom->h,
+                   tl, br, tlc, brc);
+    geom->x+=tl;
+    geom->y+=tl;
+    geom->w-=tl+br;
+    geom->h-=tl+br;
+}
+
+
+void debrush_do_draw_border(DEBrush *brush, WRectangle geom, 
+                            DEColourGroup *cg)
+{
+    DEBorder *bd=&(brush->d->border);
+    GC gc=brush->d->normal_gc;
+    Window win=brush->win;
+    
+    switch(bd->style){
+    case DEBORDER_RIDGE:
+        draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
+    case DEBORDER_INLAID:
+        draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
+        draw_border(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl);
+        break;
+    case DEBORDER_GROOVE:
+        draw_border(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl);
+        draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
+        draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
+        break;
+    case DEBORDER_ELEVATED:
+    default:
+        draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
+        draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
+        break;
+    }
+}
+
+
+void debrush_draw_border(DEBrush *brush, 
+                         const WRectangle *geom,
+                         const char *attrib)
+{
+    DEColourGroup *cg=debrush_get_colour_group(brush, attrib);
+    if(cg!=NULL)
+        debrush_do_draw_border(brush, *geom, cg);
+}
+
+
+static void draw_borderline(Window win, GC gc, WRectangle *geom,
+                            uint tl, uint br, DEColour tlc, DEColour brc, 
+                            GrBorderLine line)
+{
+    if(line==GR_BORDERLINE_LEFT && geom->h>0){
+        XSetForeground(ioncore_g.dpy, gc, tlc);
+        XDrawRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, tl, geom->h);
+        geom->x+=tl;
+    }else if(line==GR_BORDERLINE_TOP && geom->w>0){
+        XSetForeground(ioncore_g.dpy, gc, tlc);
+        XDrawRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, geom->w, tl);
+        geom->y+=tl;
+    }else if(line==GR_BORDERLINE_RIGHT && geom->h>0){
+        XSetForeground(ioncore_g.dpy, gc, brc);
+        XDrawRectangle(ioncore_g.dpy, win, gc, geom->x+geom->w-1-br, geom->y, br, geom->h);
+        geom->w-=br;
+    }else if(line==GR_BORDERLINE_BOTTOM && geom->w>0){
+        XSetForeground(ioncore_g.dpy, gc, brc);
+        XDrawRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y+geom->h-1-br, geom->w, br);
+        geom->h-=br;
+    }
+}
+
+
+void debrush_do_draw_borderline(DEBrush *brush, WRectangle geom,
+                                DEColourGroup *cg, GrBorderLine line)
+{
+    DEBorder *bd=&(brush->d->border);
+    GC gc=brush->d->normal_gc;
+    Window win=brush->win;
+    
+    switch(bd->style){
+    case DEBORDER_RIDGE:
+        draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
+    case DEBORDER_INLAID:
+        draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
+        draw_borderline(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl, line);
+        break;
+    case DEBORDER_GROOVE:
+        draw_borderline(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl, line);
+        draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
+        draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
+        break;
+    case DEBORDER_ELEVATED:
+    default:
+        draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
+        draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
+        break;
+    }
+}
+
+
+void debrush_draw_borderline(DEBrush *brush, const WRectangle *geom,
+                             const char *attrib, GrBorderLine line)
+{
+    DEColourGroup *cg=debrush_get_colour_group(brush, attrib);
+    if(cg!=NULL)
+        debrush_do_draw_borderline(brush, *geom, cg, line);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Boxes */
+
+
+static void copy_masked(DEBrush *brush, Drawable src, Drawable dst,
+                        int src_x, int src_y, int w, int h,
+                        int dst_x, int dst_y)
+{
+    
+    GC copy_gc=brush->d->copy_gc;
+    
+    XSetClipMask(ioncore_g.dpy, copy_gc, src);
+    XSetClipOrigin(ioncore_g.dpy, copy_gc, dst_x, dst_y);
+    XCopyPlane(ioncore_g.dpy, src, dst, copy_gc, src_x, src_y, w, h,
+               dst_x, dst_y, 1);
+}
+
+
+void debrush_tab_extras(DEBrush *brush, const WRectangle *g, 
+                        DEColourGroup *cg, GrBorderWidths *bdw,
+                        GrFontExtents *fnte,
+                        const char *a1, const char *a2,
+                        bool pre)
+{
+    DEStyle *d=brush->d;
+    GC tmp;
+    /* Not thread-safe, but neither is the rest of the drawing code
+     * with shared GC:s.
+     */
+    static bool swapped=FALSE;
+    
+    if(pre){
+        if(!MATCHES2("*-*-*-dragged", a1, a2))
+            return;
+        
+        tmp=d->normal_gc;
+        d->normal_gc=d->stipple_gc;
+        d->stipple_gc=tmp;
+        swapped=TRUE;
+        XClearArea(ioncore_g.dpy, brush->win, g->x, g->y, g->w, g->h, False);
+        return;
+    }
+    
+    if(MATCHES2("*-*-tagged", a1, a2)){
+        XSetForeground(ioncore_g.dpy, d->copy_gc, cg->fg);
+            
+        copy_masked(brush, d->tag_pixmap, brush->win, 0, 0,
+                    d->tag_pixmap_w, d->tag_pixmap_h,
+                    g->x+g->w-bdw->right-d->tag_pixmap_w, 
+                    g->y+bdw->top);
+    }
+
+    if(swapped){
+        tmp=d->normal_gc;
+        d->normal_gc=d->stipple_gc;
+        d->stipple_gc=tmp;
+        swapped=FALSE;
+    }
+    /*if(MATCHES2("*-*-*-dragged", a1, a2)){
+        XFillRectangle(ioncore_g.dpy, win, d->stipple_gc, 
+                       g->x, g->y, g->w, g->h);
+    }*/
+}
+
+
+void debrush_menuentry_extras(DEBrush *brush, const WRectangle *g, 
+                              DEColourGroup *cg, GrBorderWidths *bdw,
+                              GrFontExtents *fnte,
+                              const char *a1, const char *a2, 
+                              bool pre)
+{
+    int tx, ty;
+
+    if(pre)
+        return;
+    
+    if(!MATCHES2("*-*-submenu", a1, a2))
+        return;
+        
+    ty=(g->y+bdw->top+fnte->baseline
+        +(g->h-bdw->top-bdw->bottom-fnte->max_height)/2);
+    tx=g->x+g->w-bdw->right;
+
+    debrush_do_draw_string(brush, tx, ty, DE_SUB_IND, DE_SUB_IND_LEN, 
+                           FALSE, cg);
+}
+
+
+void debrush_do_draw_box(DEBrush *brush, const WRectangle *geom, 
+                         DEColourGroup *cg, bool needfill)
+{
+    GC gc=brush->d->normal_gc;
+    
+    if(TRUE/*needfill*/){
+        XSetForeground(ioncore_g.dpy, gc, cg->bg);
+        XFillRectangle(ioncore_g.dpy, brush->win, gc, geom->x, geom->y, 
+                       geom->w, geom->h);
+    }
+    
+    debrush_do_draw_border(brush, *geom, cg);
+}
+
+
+static void debrush_do_draw_textbox(DEBrush *brush, const WRectangle *geom, 
+                                    const char *text, DEColourGroup *cg, 
+                                    bool needfill,
+                                    const char *a1, const char *a2)
+{
+    uint len;
+    GrBorderWidths bdw;
+    GrFontExtents fnte;
+    uint tx, ty, tw;
+
+    grbrush_get_border_widths(&(brush->grbrush), &bdw);
+    grbrush_get_font_extents(&(brush->grbrush), &fnte);
+    
+    if(brush->extras_fn!=NULL)
+        brush->extras_fn(brush, geom, cg, &bdw, &fnte, a1, a2, TRUE);
+    
+    debrush_do_draw_box(brush, geom, cg, needfill);
+    
+    do{ /*...while(0)*/
+        if(text==NULL)
+            break;
+        
+        len=strlen(text);
+    
+        if(len==0)
+            break;
+    
+        if(brush->d->textalign!=DEALIGN_LEFT){
+            tw=grbrush_get_text_width((GrBrush*)brush, text, len);
+            
+            if(brush->d->textalign==DEALIGN_CENTER)
+                tx=geom->x+bdw.left+(geom->w-bdw.left-bdw.right-tw)/2;
+            else
+                tx=geom->x+geom->w-bdw.right-tw;
+        }else{
+            tx=geom->x+bdw.left;
+        }
+        
+        ty=(geom->y+bdw.top+fnte.baseline
+            +(geom->h-bdw.top-bdw.bottom-fnte.max_height)/2);
+        
+        debrush_do_draw_string(brush, tx, ty, text, len, FALSE, cg);
+    }while(0);
+    
+    if(brush->extras_fn!=NULL)
+        brush->extras_fn(brush, geom, cg, &bdw, &fnte, a1, a2, FALSE);
+}
+
+
+void debrush_draw_textbox(DEBrush *brush, const WRectangle *geom, 
+                          const char *text, const char *attr, 
+                          bool needfill)
+{
+    DEColourGroup *cg=debrush_get_colour_group(brush, attr);
+    if(cg!=NULL){
+        debrush_do_draw_textbox(brush, geom, text, cg, needfill, 
+                                attr, NULL);
+    }
+}
+
+
+void debrush_draw_textboxes(DEBrush *brush, const WRectangle *geom,
+                            int n, const GrTextElem *elem, 
+                            bool needfill, const char *common_attrib)
+{
+    WRectangle g=*geom;
+    DEColourGroup *cg;
+    GrBorderWidths bdw;
+    int i;
+    
+    grbrush_get_border_widths(&(brush->grbrush), &bdw);
+    
+    for(i=0; i<n; i++){
+        g.w=bdw.left+elem[i].iw+bdw.right;
+        cg=debrush_get_colour_group2(brush, common_attrib, elem[i].attr);
+        
+        if(cg!=NULL){
+            debrush_do_draw_textbox(brush, &g, elem[i].text, cg, needfill,
+                                    common_attrib, elem[i].attr);
+        }
+        
+        g.x+=g.w;
+        if(bdw.spacing>0 && needfill){
+            XClearArea(ioncore_g.dpy, brush->win, g.x, g.y,
+                       brush->d->spacing, g.h, False);
+        }
+        g.x+=bdw.spacing;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+#define MAXSHAPE 16
+
+void debrush_set_window_shape(DEBrush *brush, bool rough,
+                              int n, const WRectangle *rects)
+{
+    XRectangle r[MAXSHAPE];
+    int i;
+    
+    if(n>MAXSHAPE)
+        n=MAXSHAPE;
+    
+    if(n==0){
+       /* n==0 should clear the shape. As there's absolutely no
+        * documentation for XShape (as is typical of all sucky X
+        * extensions), I don't know how the shape should properly 
+        * be cleared. Thus we just use a huge rectangle.
+        */
+       n=1;
+       r[0].x=0;
+       r[0].y=0;
+       r[0].width=USHRT_MAX;
+       r[0].height=USHRT_MAX;
+    }else{
+       for(i=0; i<n; i++){
+           r[i].x=rects[i].x;
+           r[i].y=rects[i].y;
+           r[i].width=rects[i].w;
+           r[i].height=rects[i].h;
+       }
+    }
+    
+    XShapeCombineRectangles(ioncore_g.dpy, brush->win,
+                            ShapeBounding, 0, 0, r, n,
+                            ShapeSet, Unsorted);
+}
+
+
+void debrush_enable_transparency(DEBrush *brush, GrTransparency mode)
+{
+    XSetWindowAttributes attr;
+    ulong attrflags=0;
+
+    if(mode==GR_TRANSPARENCY_DEFAULT)
+        mode=brush->d->transparency_mode;
+    
+    if(mode==GR_TRANSPARENCY_YES){
+        attrflags=CWBackPixmap;
+        attr.background_pixmap=ParentRelative;
+    }else{
+        attrflags=CWBackPixel;
+        attr.background_pixel=brush->d->cgrp.bg;
+    }
+    
+    XChangeWindowAttributes(ioncore_g.dpy, brush->win, attrflags, &attr);
+    XClearWindow(ioncore_g.dpy, brush->win);
+}
+
+
+void debrush_fill_area(DEBrush *brush, const WRectangle *geom, const char *attr)
+{
+    DEColourGroup *cg=debrush_get_colour_group(brush, attr);
+    GC gc=brush->d->normal_gc;
+
+    if(cg==NULL)
+        return;
+    
+    XSetForeground(ioncore_g.dpy, gc, cg->bg);
+    XFillRectangle(ioncore_g.dpy, brush->win, gc, 
+                   geom->x, geom->y, geom->w, geom->h);
+}
+
+
+void debrush_clear_area(DEBrush *brush, const WRectangle *geom)
+{
+    XClearArea(ioncore_g.dpy, brush->win,
+               geom->x, geom->y, geom->w, geom->h, False);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Clipping rectangles */
+
+/* Should actually set the clipping rectangle for all GC:s and use 
+ * window-specific GC:s to do this correctly...
+ */
+
+static void debrush_set_clipping_rectangle(DEBrush *brush, 
+                                           const WRectangle *geom)
+{
+    XRectangle rect;
+    
+    assert(!brush->clip_set);
+    
+    rect.x=geom->x;
+    rect.y=geom->y;
+    rect.width=geom->w;
+    rect.height=geom->h;
+    
+    XSetClipRectangles(ioncore_g.dpy, brush->d->normal_gc,
+                       0, 0, &rect, 1, Unsorted);
+    brush->clip_set=TRUE;
+}
+
+
+static void debrush_clear_clipping_rectangle(DEBrush *brush)
+{
+    if(brush->clip_set){
+        XSetClipMask(ioncore_g.dpy, brush->d->normal_gc, None);
+        brush->clip_set=FALSE;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ debrush_begin/end */
+
+
+void debrush_begin(DEBrush *brush, const WRectangle *geom, int flags)
+{
+    if(flags&GRBRUSH_AMEND)
+        flags|=GRBRUSH_NO_CLEAR_OK;
+    
+    if(!(flags&GRBRUSH_NO_CLEAR_OK))
+        debrush_clear_area(brush, geom);
+    
+    if(flags&GRBRUSH_NEED_CLIP)
+        debrush_set_clipping_rectangle(brush, geom);
+}
+
+
+void debrush_end(DEBrush *brush)
+{
+    debrush_clear_clipping_rectangle(brush);
+}
+
+
+/*}}}*/
+
diff --git a/de/font.c b/de/font.c
new file mode 100644 (file)
index 0000000..9d80c1e
--- /dev/null
+++ b/de/font.c
@@ -0,0 +1,280 @@
+/*
+ * ion/de/font.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <ioncore/common.h>
+#include "font.h"
+#include "fontset.h"
+#include "brush.h"
+
+
+/*{{{ Load/free */
+
+
+static DEFont *fonts=NULL;
+
+
+DEFont *de_load_font(const char *fontname)
+{
+    DEFont *fnt;
+    XFontSet fontset=NULL;
+    XFontStruct *fontstruct=NULL;
+    
+    assert(fontname!=NULL);
+    
+    /* There shouldn't be that many fonts... */
+    for(fnt=fonts; fnt!=NULL; fnt=fnt->next){
+        if(strcmp(fnt->pattern, fontname)==0){
+            fnt->refcount++;
+            return fnt;
+        }
+    }
+    
+    if(ioncore_g.use_mb){
+        fontset=de_create_font_set(fontname);
+        if(fontset!=NULL){
+            if(XContextDependentDrawing(fontset)){
+                warn(TR("Fontset for font pattern '%s' implements context "
+                        "dependent drawing, which is unsupported. Expect "
+                        "clutter."), fontname);
+            }
+        }
+    }else{
+        fontstruct=XLoadQueryFont(ioncore_g.dpy, fontname);
+    }
+    
+    if(fontstruct==NULL && fontset==NULL){
+        if(strcmp(fontname, CF_FALLBACK_FONT_NAME)!=0){
+            DEFont *fnt;
+            warn(TR("Could not load font \"%s\", trying \"%s\""),
+                 fontname, CF_FALLBACK_FONT_NAME);
+            fnt=de_load_font(CF_FALLBACK_FONT_NAME);
+            if(fnt==NULL)
+                warn(TR("Failed to load fallback font."));
+            return fnt;
+        }
+        return NULL;
+    }
+    
+    fnt=ALLOC(DEFont);
+    
+    if(fnt==NULL)
+        return NULL;
+    
+    fnt->fontset=fontset;
+    fnt->fontstruct=fontstruct;
+    fnt->pattern=scopy(fontname);
+    fnt->next=NULL;
+    fnt->prev=NULL;
+    fnt->refcount=1;
+    
+    LINK_ITEM(fonts, fnt, next, prev);
+    
+    return fnt;
+}
+
+
+bool de_set_font_for_style(DEStyle *style, DEFont *font)
+{
+    if(style->font!=NULL)
+        de_free_font(style->font);
+    
+    style->font=font;
+    font->refcount++;
+    
+    if(style->font->fontstruct!=NULL){
+        XSetFont(ioncore_g.dpy, style->normal_gc, 
+                 style->font->fontstruct->fid);
+    }
+
+    return TRUE;
+}
+
+
+bool de_load_font_for_style(DEStyle *style, const char *fontname)
+{
+    if(style->font!=NULL)
+        de_free_font(style->font);
+    
+    style->font=de_load_font(fontname);
+
+    if(style->font==NULL)
+        return FALSE;
+    
+    if(style->font->fontstruct!=NULL){
+        XSetFont(ioncore_g.dpy, style->normal_gc, 
+                 style->font->fontstruct->fid);
+    }
+    
+    return TRUE;
+}
+
+
+void de_free_font(DEFont *font)
+{
+    if(--font->refcount!=0)
+        return;
+    
+    if(font->fontset!=NULL)
+        XFreeFontSet(ioncore_g.dpy, font->fontset);
+    if(font->fontstruct!=NULL)
+        XFreeFont(ioncore_g.dpy, font->fontstruct);
+    if(font->pattern!=NULL)
+        free(font->pattern);
+    
+    UNLINK_ITEM(fonts, font, next, prev);
+    free(font);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Lengths */
+
+
+void debrush_get_font_extents(DEBrush *brush, GrFontExtents *fnte)
+{
+    if(brush->d->font==NULL){
+        DE_RESET_FONT_EXTENTS(fnte);
+        return;
+    }
+    
+    defont_get_font_extents(brush->d->font, fnte);
+}
+
+
+void defont_get_font_extents(DEFont *font, GrFontExtents *fnte)
+{
+    if(font->fontset!=NULL){
+        XFontSetExtents *ext=XExtentsOfFontSet(font->fontset);
+        if(ext==NULL)
+            goto fail;
+        fnte->max_height=ext->max_logical_extent.height;
+        fnte->max_width=ext->max_logical_extent.width;
+        fnte->baseline=-ext->max_logical_extent.y;
+        return;
+    }else if(font->fontstruct!=NULL){
+        XFontStruct *fnt=font->fontstruct;
+        fnte->max_height=fnt->ascent+fnt->descent;
+        fnte->max_width=fnt->max_bounds.width;
+        fnte->baseline=fnt->ascent;
+        return;
+    }
+    
+fail:
+    DE_RESET_FONT_EXTENTS(fnte);
+}
+
+
+uint debrush_get_text_width(DEBrush *brush, const char *text, uint len)
+{
+    if(brush->d->font==NULL || text==NULL || len==0)
+        return 0;
+    
+    return defont_get_text_width(brush->d->font, text, len);
+}
+
+
+uint defont_get_text_width(DEFont *font, const char *text, uint len)
+{
+    if(font->fontset!=NULL){
+        XRectangle lext;
+#ifdef CF_DE_USE_XUTF8
+        if(ioncore_g.enc_utf8)
+            Xutf8TextExtents(font->fontset, text, len, NULL, &lext);
+        else
+#endif
+            XmbTextExtents(font->fontset, text, len, NULL, &lext);
+        return lext.width;
+    }else if(font->fontstruct!=NULL){
+        return XTextWidth(font->fontstruct, text, len);
+    }else{
+        return 0;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ String drawing */
+
+
+void debrush_do_draw_string_default(DEBrush *brush, int x, int y,
+                                    const char *str, int len, bool needfill, 
+                                    DEColourGroup *colours)
+{
+    GC gc=brush->d->normal_gc;
+
+    if(brush->d->font==NULL)
+        return;
+    
+    XSetForeground(ioncore_g.dpy, gc, colours->fg);
+    
+    if(!needfill){
+        if(brush->d->font->fontset!=NULL){
+#ifdef CF_DE_USE_XUTF8
+            if(ioncore_g.enc_utf8)
+                Xutf8DrawString(ioncore_g.dpy, brush->win, 
+                                brush->d->font->fontset,
+                                gc, x, y, str, len);
+            else
+#endif
+                XmbDrawString(ioncore_g.dpy, brush->win, 
+                              brush->d->font->fontset,
+                              gc, x, y, str, len);
+        }else if(brush->d->font->fontstruct!=NULL){
+            XDrawString(ioncore_g.dpy, brush->win, gc, x, y, str, len);
+        }
+    }else{
+        XSetBackground(ioncore_g.dpy, gc, colours->bg);
+        if(brush->d->font->fontset!=NULL){
+#ifdef CF_DE_USE_XUTF8
+            if(ioncore_g.enc_utf8)
+                Xutf8DrawImageString(ioncore_g.dpy, brush->win, 
+                                     brush->d->font->fontset,
+                                     gc, x, y, str, len);
+            else
+#endif
+                XmbDrawImageString(ioncore_g.dpy, brush->win, 
+                                   brush->d->font->fontset,
+                                   gc, x, y, str, len);
+        }else if(brush->d->font->fontstruct!=NULL){
+            XDrawImageString(ioncore_g.dpy, brush->win, gc, x, y, str, len);
+        }
+    }
+}
+
+
+void debrush_do_draw_string(DEBrush *brush, int x, int y,
+                            const char *str, int len, bool needfill, 
+                            DEColourGroup *colours)
+{
+    CALL_DYN(debrush_do_draw_string, brush, (brush, x, y, str, len,
+                                             needfill, colours));
+}
+
+
+void debrush_draw_string(DEBrush *brush, int x, int y,
+                         const char *str, int len, bool needfill,
+                         const char *attrib)
+{
+    DEColourGroup *cg=debrush_get_colour_group(brush, attrib);
+    if(cg!=NULL)
+        debrush_do_draw_string(brush, x, y, str, len, needfill, cg);
+}
+
+
+/*}}}*/
+
diff --git a/de/font.h b/de/font.h
new file mode 100644 (file)
index 0000000..d2f5e3b
--- /dev/null
+++ b/de/font.h
@@ -0,0 +1,58 @@
+/*
+ * ion/de/font.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_DE_FONT_H
+#define ION_DE_FONT_H
+
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+
+INTRSTRUCT(DEFont);
+
+#include "brush.h"
+#include "colour.h"
+#include "style.h"
+
+#define DE_RESET_FONT_EXTENTS(FNTE) \
+   {(FNTE)->max_height=0; (FNTE)->max_width=0; (FNTE)->baseline=0;}
+
+DECLSTRUCT(DEFont){
+    char *pattern;
+    int refcount;
+    XFontSet fontset;
+    XFontStruct *fontstruct;
+    DEFont *next, *prev;
+};
+
+extern bool de_load_font_for_style(DEStyle *style, const char *fontname);
+extern bool de_set_font_for_style(DEStyle *style, DEFont *font);
+extern DEFont *de_load_font(const char *fontname);
+extern void de_free_font(DEFont *font);
+
+extern void debrush_draw_string(DEBrush *brush, int x, int y,
+                                const char *str, int len, bool needfill,
+                                const char *attrib);
+extern void debrush_do_draw_string(DEBrush *brush, int x, int y,
+                                   const char *str, int len, bool needfill, 
+                                   DEColourGroup *colours);
+extern void debrush_do_draw_string_default(DEBrush *brush, int x, int y,
+                                           const char *str, int len, 
+                                           bool needfill, 
+                                           DEColourGroup *colours);
+
+extern void debrush_get_font_extents(DEBrush *brush, GrFontExtents *fnte);
+
+extern uint debrush_get_text_width(DEBrush *brush, const char *text, uint len);
+
+extern uint defont_get_text_width(DEFont *font, const char *text, uint len);
+extern void defont_get_font_extents(DEFont *font, GrFontExtents *fnte);
+
+#endif /* ION_DE_FONT_H */
diff --git a/de/fontset.c b/de/fontset.c
new file mode 100644 (file)
index 0000000..af164be
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * ion/de/fontset.c
+ * 
+ * This file contains routines to attempt to add fonts to a font pattern
+ * so that XCreateFontSet will not fail because the given font(s) do not
+ * contain all the characters required by the locale. The original code
+ * was apparently written by Tomohiro Kubota; see
+ * <http://www.debian.org/doc/manuals/intro-i18n/ch-examples.en.html#s13.4.5>.
+ * 
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <locale.h>
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+
+#ifndef CF_FONT_ELEMENT_SIZE
+#define CF_FONT_ELEMENT_SIZE 50
+#endif
+
+#define FNT_D(X) /*X*/
+
+
+static const char *get_font_element(const char *pattern, char *buf,
+                                    int bufsiz, ...) 
+{
+    const char *p, *v;
+    char *p2;
+    va_list va;
+    
+    va_start(va, bufsiz);
+    buf[bufsiz-1]=0;
+    buf[bufsiz-2]='*';
+    while((v=va_arg(va, char *))!=NULL){
+        p=libtu_strcasestr(pattern, v);
+        if(p){
+            strncpy(buf, p+1, bufsiz-2);
+            p2=strchr(buf, '-');
+            if(p2) *p2=0;
+            va_end(va);
+            return p;
+        }
+    }
+    va_end(va);
+    strncpy(buf, "*", bufsiz);
+    return NULL;
+}
+
+
+static const char *get_font_size(const char *pattern, int *size) 
+{
+    const char *p;
+    const char *p2=NULL;
+    int n=0;
+    
+    for(p=pattern; 1; p++){
+        if(!*p){
+            if(p2!=NULL && n>1 && n<72){
+                *size=n; return p2+1;
+            }else{
+                *size=16; return NULL;
+            }
+        }else if(*p=='-'){
+            if(n>1 && n<72 && p2!=NULL){
+                *size=n;
+                return p2+1;
+            }
+            p2=p; n=0;
+        }else if(*p>='0' && *p<='9' && p2!=NULL){
+            n*=10;
+            n+=*p-'0';
+        }else{
+            p2=NULL; n=0;
+        }
+    }
+}
+
+
+XFontSet de_create_font_set(const char *fontname) 
+{
+    XFontSet fs;
+    char **missing=NULL, *def="-";
+    int nmissing, pixel_size=0;
+    char weight[CF_FONT_ELEMENT_SIZE], slant[CF_FONT_ELEMENT_SIZE];
+    const char *nfontname=fontname;
+    char *pattern2=NULL;
+    int i;
+    
+    FNT_D(fprintf(stderr, "FNTRQ: %s\n", fontname));
+    
+    fs=XCreateFontSet(ioncore_g.dpy, fontname, &missing, &nmissing, &def);
+
+    if(fs && nmissing==0){
+        if(missing!=NULL) 
+            XFreeStringList(missing);
+        return fs;
+    }
+    
+    /* Not a warning, nothing serious */
+    FNT_D(fprintf(stderr, "Failed to load fontset.\n"));
+    
+    if(!fs){
+        char *lcc=NULL;
+        const char *lc;
+        if(missing!=NULL)
+            XFreeStringList(missing);
+        
+        lc=setlocale(LC_CTYPE, NULL);
+        if(lc!=NULL && strcmp(lc, "POSIX")!=0 && strcmp(lc, "C")!=0)
+            lcc=scopy(lc);
+        
+        setlocale(LC_CTYPE, "C");
+        
+        fs=XCreateFontSet(ioncore_g.dpy, fontname, &missing, &nmissing, &def);
+        
+        if(lcc!=NULL){
+            setlocale(LC_CTYPE, lcc);
+            free(lcc);
+        }
+    }
+
+#ifndef CF_NO_FONTSET_KLUDGE    
+
+    if(fs){
+        XFontStruct **fontstructs;
+        char **fontnames;
+        XFontsOfFontSet(fs, &fontstructs, &fontnames);
+        nfontname=fontnames[0];
+    }
+
+    get_font_element(nfontname, weight, CF_FONT_ELEMENT_SIZE,
+                     "-medium-", "-bold-", "-demibold-", "-regular-", NULL);
+    get_font_element(nfontname, slant, CF_FONT_ELEMENT_SIZE,
+                     "-r-", "-i-", "-o-", "-ri-", "-ro-", NULL);
+    get_font_size(nfontname, &pixel_size);
+    
+    if(!strcmp(weight, "*"))
+        strncpy(weight, "medium", CF_FONT_ELEMENT_SIZE);
+    if(!strcmp(slant, "*"))
+        strncpy(slant, "r", CF_FONT_ELEMENT_SIZE);
+    if(pixel_size<3)
+        pixel_size=3;
+    else if(pixel_size>97)
+        pixel_size=97;
+    
+    if(ioncore_g.enc_utf8){
+        libtu_asprintf(&pattern2,
+                       "%s,"
+                       "-misc-fixed-%s-%s-*-*-%d-*-*-*-*-*-*-*,"
+                       "-misc-fixed-*-*-*-*-%d-*-*-*-*-*-*-*",
+                       fontname, weight, slant, pixel_size, pixel_size);
+    }else{
+        libtu_asprintf(&pattern2,
+                       "%s,"
+                       "-*-*-%s-%s-*-*-%d-*-*-*-*-*-*-*,"
+                       "-*-*-*-*-*-*-%d-*-*-*-*-*-*-*",
+                       fontname, weight, slant, pixel_size, pixel_size);
+    }
+    
+    if(pattern2==NULL)
+        return NULL;
+
+    FNT_D(fprintf(stderr, "NRQ: %s\n", pattern2));
+    
+    nfontname=pattern2;
+    
+    if(nmissing)
+        XFreeStringList(missing);
+    if(fs)
+        XFreeFontSet(ioncore_g.dpy, fs);
+
+    FNT_D(if(fs) fprintf(stderr, "Trying '%s'.\n", nfontname));
+    
+    fs=XCreateFontSet(ioncore_g.dpy, nfontname, &missing, &nmissing, &def);
+    
+    free(pattern2);
+    
+#endif
+    
+    if(missing!=NULL) 
+        XFreeStringList(missing);
+
+    return fs;
+}
diff --git a/de/fontset.h b/de/fontset.h
new file mode 100644 (file)
index 0000000..256d1d9
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * ion/de/fontset.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_DE_FONTSET_H
+#define ION_DE_FONTSET_H
+
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+
+extern XFontSet de_create_font_set(const char *fontname);
+
+#endif /* ION_DE_FONTSET_H */
diff --git a/de/init.c b/de/init.c
new file mode 100644 (file)
index 0000000..3e779b3
--- /dev/null
+++ b/de/init.c
@@ -0,0 +1,369 @@
+/*
+ * ion/de/init.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libextl/readconfig.h>
+#include <libextl/extl.h>
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/rootwin.h>
+#include <ioncore/extlconv.h>
+#include <ioncore/ioncore.h>
+
+#include "brush.h"
+#include "font.h"
+#include "colour.h"
+#include "private.h"
+#include "init.h"
+#include "exports.h"
+
+
+/*{{{ Borders */
+
+
+#define CF_BORDER_VAL_SANITY_CHECK 16
+
+void de_get_border_val(uint *val, ExtlTab tab, const char *what)
+{
+    int g;
+    
+    if(extl_table_gets_i(tab, what, &g)){
+        if(g>CF_BORDER_VAL_SANITY_CHECK || g<0)
+            warn(TR("Border attribute %s sanity check failed."), what);
+        else
+            *val=g;
+    }
+}
+
+
+void de_get_border_style(uint *ret, ExtlTab tab)
+{
+    char *style=NULL;
+    
+    if(!extl_table_gets_s(tab, "border_style", &style))
+        return;
+    
+    if(strcmp(style, "inlaid")==0)
+        *ret=DEBORDER_INLAID;
+    else if(strcmp(style, "elevated")==0)
+        *ret=DEBORDER_ELEVATED;
+    else if(strcmp(style, "groove")==0)
+        *ret=DEBORDER_GROOVE;
+    else if(strcmp(style, "ridge")==0)
+        *ret=DEBORDER_RIDGE;
+    else
+        warn(TR("Unknown border style \"%s\"."), style);
+    
+    free(style);
+}
+
+
+void de_get_border(DEBorder *border, ExtlTab tab)
+{
+    de_get_border_val(&(border->sh), tab, "shadow_pixels");
+    de_get_border_val(&(border->hl), tab, "highlight_pixels");
+    de_get_border_val(&(border->pad), tab, "padding_pixels");
+    de_get_border_style(&(border->style), tab);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Colours */
+
+
+bool de_get_colour(WRootWin *rootwin, DEColour *ret, 
+                   ExtlTab tab, DEStyle *based_on,
+                   const char *what, DEColour substitute)
+{
+    char *name=NULL;
+    bool ok=FALSE;
+    
+    if(extl_table_gets_s(tab, what, &name)){
+        ok=de_alloc_colour(rootwin, ret, name);
+    
+        if(!ok)
+            warn(TR("Unable to allocate colour \"%s\"."), name);
+
+        free(name);
+    }else if(based_on!=NULL){
+        return de_get_colour(rootwin, ret, based_on->data_table,
+                             based_on->based_on, what, substitute);
+    }
+    
+    if(!ok)
+        ok=de_duplicate_colour(rootwin, substitute, ret);
+    
+    return ok;
+}
+
+
+void de_get_colour_group(WRootWin *rootwin, DEColourGroup *cg, 
+                         ExtlTab tab, DEStyle *based_on)
+{
+    de_get_colour(rootwin, &(cg->hl), tab, based_on, "highlight_colour",
+                  DE_WHITE(rootwin));
+    de_get_colour(rootwin, &(cg->sh), tab, based_on, "shadow_colour",
+                  DE_WHITE(rootwin));
+    de_get_colour(rootwin, &(cg->bg), tab, based_on, "background_colour",
+                  DE_BLACK(rootwin));
+    de_get_colour(rootwin, &(cg->fg), tab, based_on, "foreground_colour",
+                  DE_WHITE(rootwin));
+    de_get_colour(rootwin, &(cg->pad), tab, based_on, "padding_colour", 
+                  cg->bg);
+}
+
+
+void de_get_extra_cgrps(WRootWin *rootwin, DEStyle *style, ExtlTab tab)
+{
+    
+    uint i=0, nfailed=0, n=extl_table_get_n(tab);
+    char *name;
+    ExtlTab sub;
+    
+    if(n==0)
+        return;
+    
+    style->extra_cgrps=ALLOC_N(DEColourGroup, n);
+    
+    if(style->extra_cgrps==NULL)
+        return;
+
+    for(i=0; i<n-nfailed; i++){
+        if(!extl_table_geti_t(tab, i+1, &sub))
+            goto err;
+        if(!extl_table_gets_s(sub, "substyle_pattern", &name)){
+            extl_unref_table(sub);
+            goto err;
+        }
+        
+        /*de_init_colour_group(rootwin, style->extra_cgrps+i-nfailed);*/
+        style->extra_cgrps[i-nfailed].spec=name;
+        de_get_colour_group(rootwin, style->extra_cgrps+i-nfailed, sub, 
+                            style);
+        
+        extl_unref_table(sub);
+        continue;
+        
+    err:
+        warn(TR("Corrupt substyle table %d."), i);
+        nfailed++;
+    }
+    
+    if(n-nfailed==0){
+        free(style->extra_cgrps);
+        style->extra_cgrps=NULL;
+    }
+    
+    style->n_extra_cgrps=n-nfailed;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+void de_get_text_align(int *alignret, ExtlTab tab)
+{
+    char *align=NULL;
+    
+    if(!extl_table_gets_s(tab, "text_align", &align))
+        return;
+    
+    if(strcmp(align, "left")==0)
+        *alignret=DEALIGN_LEFT;
+    else if(strcmp(align, "right")==0)
+        *alignret=DEALIGN_RIGHT;
+    else if(strcmp(align, "center")==0)
+        *alignret=DEALIGN_CENTER;
+    else
+        warn(TR("Unknown text alignment \"%s\"."), align);
+    
+    free(align);
+}
+
+
+void de_get_transparent_background(uint *mode, ExtlTab tab)
+{
+    bool b;
+    
+    if(extl_table_gets_b(tab, "transparent_background", &b))
+        *mode=b;
+}
+
+
+/*}}}*/
+
+
+/*{{{ de_defstyle */
+
+
+void de_get_nonfont(WRootWin *rootwin, DEStyle *style, ExtlTab tab)
+{
+    DEStyle *based_on=style->based_on;
+    
+    style->data_table=extl_ref_table(tab);
+
+    if(based_on!=NULL){
+        style->border=based_on->border;
+        style->transparency_mode=based_on->transparency_mode;
+        style->textalign=based_on->textalign;
+        style->spacing=based_on->spacing;
+    }
+    
+    de_get_border(&(style->border), tab);
+    de_get_border_val(&(style->spacing), tab, "spacing");
+
+    de_get_text_align(&(style->textalign), tab);
+
+    de_get_transparent_background(&(style->transparency_mode), tab);
+
+    style->cgrp_alloced=TRUE;
+    de_get_colour_group(rootwin, &(style->cgrp), tab, based_on);
+    de_get_extra_cgrps(rootwin, style, tab);
+}
+
+
+/*EXTL_DOC
+ * Define a style for the root window \var{rootwin}. 
+ */
+EXTL_EXPORT
+bool de_defstyle_rootwin(WRootWin *rootwin, const char *name, ExtlTab tab)
+{
+    DEStyle *style;
+    char *fnt;
+    uint n;
+    DEStyle *based_on=NULL;
+    char *based_on_name=NULL;
+
+    if(name==NULL)
+        return FALSE;
+    
+    style=de_create_style(rootwin, name);
+
+    if(style==NULL)
+        return FALSE;
+
+    if(extl_table_gets_s(tab, "based_on", &based_on_name)){
+        based_on=de_get_style(rootwin, based_on_name);
+        if(based_on==style){
+            warn(TR("'based_on' for %s points back to the style itself."),
+                 name);
+        }else if(based_on==NULL){
+            warn(TR("Unknown base style \"%s\"."), based_on);
+        }else{
+            style->based_on=based_on;
+            based_on->usecount++;
+            /* Copy simple parameters */
+        }
+        free(based_on_name);
+    }
+
+    de_get_nonfont(rootwin, style, tab);
+
+    if(extl_table_gets_s(tab, "font", &fnt)){
+        de_load_font_for_style(style, fnt);
+        free(fnt);
+    }else if(based_on!=NULL && based_on->font!=NULL){
+        de_set_font_for_style(style, based_on->font);
+    }
+    
+    if(style->font==NULL)
+        de_load_font_for_style(style, CF_FALLBACK_FONT_NAME);
+    
+    return TRUE;
+}
+
+
+/*EXTL_DOC
+ * Define a style.
+ */
+EXTL_EXPORT
+bool de_defstyle(const char *name, ExtlTab tab)
+{
+    bool ok=TRUE;
+    WRootWin *rw;
+    
+    FOR_ALL_ROOTWINS(rw){
+        if(!de_defstyle_rootwin(rw, name, tab))
+            ok=FALSE;
+    }
+    
+    return ok;
+}
+
+
+/*EXTL_DOC
+ * Define a substyle.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab de_substyle(const char *pattern, ExtlTab tab)
+{
+    extl_table_sets_s(tab, "substyle_pattern", pattern);
+    return extl_ref_table(tab);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Module initialisation */
+
+
+#include "../version.h"
+
+char de_ion_api_version[]=ION_API_VERSION;
+
+
+bool de_init()
+{
+    WRootWin *rootwin;
+    DEStyle *style;
+    
+    if(!de_register_exports())
+        return FALSE;
+    
+    if(!gr_register_engine("de", (GrGetBrushFn*)&de_get_brush))
+        goto fail;
+    
+    /* Create fallback brushes */
+    FOR_ALL_ROOTWINS(rootwin){
+        style=de_create_style(rootwin, "*");
+        if(style!=NULL){
+            style->is_fallback=TRUE;
+            de_load_font_for_style(style, CF_FALLBACK_FONT_NAME);
+        }
+    }
+    
+    return TRUE;
+    
+fail:
+    de_unregister_exports();
+    return FALSE;
+}
+
+
+void de_deinit()
+{
+    gr_unregister_engine("de");
+    de_unregister_exports();
+    de_deinit_styles();
+}
+
+
+/*}}}*/
+
diff --git a/de/init.h b/de/init.h
new file mode 100644 (file)
index 0000000..f3b6f10
--- /dev/null
+++ b/de/init.h
@@ -0,0 +1,41 @@
+/*
+ * ion/de/init.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_DE_INIT_H
+#define ION_DE_INIT_H
+
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+#include "brush.h"
+
+extern void de_get_border_val(uint *val, ExtlTab tab, const char *what);
+extern void de_get_border_style(uint *ret, ExtlTab tab);
+extern void de_get_border(DEBorder *border, ExtlTab tab);
+
+extern bool de_get_colour(WRootWin *rootwin, DEColour *ret, 
+                          ExtlTab tab, DEStyle *based_on,
+                          const char *what, DEColour substitute);
+extern void de_get_colour_group(WRootWin *rootwin, DEColourGroup *cg, 
+                                ExtlTab tab, DEStyle *based_on);
+extern void de_get_extra_cgrps(WRootWin *rootwin, DEStyle *style, 
+                               ExtlTab tab);
+
+extern void de_get_text_align(int *alignret, ExtlTab tab);
+
+extern void de_get_transparent_background(uint *mode, ExtlTab tab);
+
+extern void de_get_nonfont(WRootWin *rw, DEStyle *style, ExtlTab tab);
+
+extern bool de_defstyle_rootwin(WRootWin *rootwin, const char *name, 
+                                ExtlTab tab);
+extern bool de_defstyle(const char *name, ExtlTab tab);
+
+#endif /* ION_DE_INIT_H */
diff --git a/de/private.h b/de/private.h
new file mode 100644 (file)
index 0000000..39141e6
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * ion/de/private.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_DE_PRIVATE_H
+#define ION_DE_PRIVATE_H
+
+#define DE_SUB_IND " ->"
+#define DE_SUB_IND_LEN 3
+
+#define MATCHES(S, A) (gr_stylespec_score(S, A)>0)
+#define MATCHES2(S, A1, A2) (gr_stylespec_score2(S, A1, A2)>0)
+
+#endif /* ION_DE_PRIVATE_H */
diff --git a/de/style.c b/de/style.c
new file mode 100644 (file)
index 0000000..cf44e47
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * ion/de/style.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libextl/extl.h>
+
+#include <ioncore/common.h>
+#include <ioncore/rootwin.h>
+#include <ioncore/extlconv.h>
+#include <ioncore/ioncore.h>
+
+#include "brush.h"
+#include "font.h"
+#include "colour.h"
+#include "private.h"
+#include "style.h"
+
+
+/*{{{ GC creation */
+
+
+static void create_normal_gc(DEStyle *style, WRootWin *rootwin)
+{
+    XGCValues gcv;
+    ulong gcvmask;
+    GC gc;
+
+    /* Create normal gc */
+    gcv.line_style=LineSolid;
+    gcv.line_width=1;
+    gcv.join_style=JoinBevel;
+    gcv.cap_style=CapButt;
+    gcv.fill_style=FillSolid;
+    gcvmask=(GCLineStyle|GCLineWidth|GCFillStyle|
+             GCJoinStyle|GCCapStyle);
+    
+    style->normal_gc=XCreateGC(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), 
+                               gcvmask, &gcv);
+}
+
+
+void destyle_create_tab_gcs(DEStyle *style)
+{
+    Display *dpy=ioncore_g.dpy;
+    WRootWin *rootwin=style->rootwin;
+    Window root=WROOTWIN_ROOT(rootwin);
+    Pixmap stipple_pixmap;
+    XGCValues gcv;
+    ulong gcvmask;
+    GC tmp_gc;
+
+    /* Create a temporary 1-bit GC for drawing the tag and stipple pixmaps */
+    stipple_pixmap=XCreatePixmap(dpy, root, 2, 2, 1);
+    gcv.foreground=1;
+    tmp_gc=XCreateGC(dpy, stipple_pixmap, GCForeground, &gcv);
+
+    /* Create stipple pattern and stipple GC */
+    XDrawPoint(dpy, stipple_pixmap, tmp_gc, 0, 0);
+    XDrawPoint(dpy, stipple_pixmap, tmp_gc, 1, 1);
+    XSetForeground(dpy, tmp_gc, 0);
+    XDrawPoint(dpy, stipple_pixmap, tmp_gc, 1, 0);
+    XDrawPoint(dpy, stipple_pixmap, tmp_gc, 0, 1);
+    
+    gcv.fill_style=FillStippled;
+    /*gcv.function=GXclear;*/
+    gcv.stipple=stipple_pixmap;
+    gcvmask=GCFillStyle|GCStipple/*|GCFunction*/;
+    if(style->font!=NULL && style->font->fontstruct!=NULL){
+        gcv.font=style->font->fontstruct->fid;
+        gcvmask|=GCFont;
+    }
+
+    style->stipple_gc=XCreateGC(dpy, root, gcvmask, &gcv);
+    XCopyGC(dpy, style->normal_gc, 
+            GCLineStyle|GCLineWidth|GCJoinStyle|GCCapStyle,
+            style->stipple_gc);
+            
+    XFreePixmap(dpy, stipple_pixmap);
+    
+    /* Create tag pixmap and copying GC */
+    style->tag_pixmap_w=5;
+    style->tag_pixmap_h=5;
+    style->tag_pixmap=XCreatePixmap(dpy, root, 5, 5, 1);
+    
+    XSetForeground(dpy, tmp_gc, 0);
+    XFillRectangle(dpy, style->tag_pixmap, tmp_gc, 0, 0, 5, 5);
+    XSetForeground(dpy, tmp_gc, 1);
+    XFillRectangle(dpy, style->tag_pixmap, tmp_gc, 0, 0, 5, 2);
+    XFillRectangle(dpy, style->tag_pixmap, tmp_gc, 3, 2, 2, 3);
+    
+    gcv.foreground=DE_BLACK(rootwin);
+    gcv.background=DE_WHITE(rootwin);
+    gcv.line_width=2;
+    gcvmask=GCLineWidth|GCForeground|GCBackground;
+    
+    style->copy_gc=XCreateGC(dpy, root, gcvmask, &gcv);
+    
+    XFreeGC(dpy, tmp_gc);
+    
+    style->tabbrush_data_ok=TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Style lookup */
+
+
+static DEStyle *styles=NULL;
+
+
+DEStyle *de_get_style(WRootWin *rootwin, const char *stylename)
+{
+    DEStyle *style, *maxstyle=NULL;
+    int score, maxscore=0;
+    
+    for(style=styles; style!=NULL; style=style->next){
+        if(style->rootwin!=rootwin)
+            continue;
+        score=gr_stylespec_score(style->style, stylename);
+        if(score>maxscore){
+            maxstyle=style;
+            maxscore=score;
+        }
+    }
+    
+    return maxstyle;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Style initialisation and deinitialisation */
+
+
+void destyle_unref(DEStyle *style)
+{
+    style->usecount--;
+    if(style->usecount==0){
+        destyle_deinit(style);
+        free(style);
+    }
+}
+
+
+void destyle_deinit(DEStyle *style)
+{
+    int i;
+    
+    UNLINK_ITEM(styles, style, next, prev);
+    
+    if(style->style!=NULL)
+        free(style->style);
+    
+    if(style->font!=NULL){
+        de_free_font(style->font);
+        style->font=NULL;
+    }
+    
+    if(style->cgrp_alloced)
+        de_free_colour_group(style->rootwin, &(style->cgrp));
+    
+    for(i=0; i<style->n_extra_cgrps; i++)
+        de_free_colour_group(style->rootwin, style->extra_cgrps+i);
+    
+    if(style->extra_cgrps!=NULL)
+        free(style->extra_cgrps);
+    
+    extl_unref_table(style->data_table);
+    
+    XFreeGC(ioncore_g.dpy, style->normal_gc);
+    
+    if(style->tabbrush_data_ok){
+        XFreeGC(ioncore_g.dpy, style->copy_gc);
+        XFreeGC(ioncore_g.dpy, style->stipple_gc);
+        XFreePixmap(ioncore_g.dpy, style->tag_pixmap);
+    }
+    
+    XSync(ioncore_g.dpy, False);
+    
+    if(style->based_on!=NULL){
+        destyle_unref(style->based_on);
+        style->based_on=NULL;
+    }
+}
+
+
+static void dump_style(DEStyle *style)
+{
+    /* Allow the style still be used but get if off the list. */
+    UNLINK_ITEM(styles, style, next, prev);
+    destyle_unref(style);
+}
+
+
+bool destyle_init(DEStyle *style, WRootWin *rootwin, const char *name)
+{
+    style->style=scopy(name);
+    if(style->style==NULL)
+        return FALSE;
+    
+    style->based_on=NULL;
+    
+    style->usecount=1;
+    /* Fallback brushes are not released on de_reset() */
+    style->is_fallback=FALSE;
+    
+    style->rootwin=rootwin;
+    
+    style->border.sh=1;
+    style->border.hl=1;
+    style->border.pad=1;
+    style->border.style=DEBORDER_INLAID;
+
+    style->spacing=0;
+    
+    style->textalign=DEALIGN_CENTER;
+
+    style->cgrp_alloced=FALSE;
+    style->cgrp.spec=NULL;
+    style->cgrp.bg=DE_BLACK(rootwin);
+    style->cgrp.pad=DE_BLACK(rootwin);
+    style->cgrp.fg=DE_WHITE(rootwin);
+    style->cgrp.hl=DE_WHITE(rootwin);
+    style->cgrp.sh=DE_WHITE(rootwin);
+    
+    style->font=NULL;
+    
+    style->transparency_mode=GR_TRANSPARENCY_NO;
+    
+    style->n_extra_cgrps=0;
+    style->extra_cgrps=NULL;
+    
+    style->data_table=extl_table_none();
+    
+    create_normal_gc(style, rootwin);
+    
+    style->tabbrush_data_ok=FALSE;
+    
+    return TRUE;
+}
+
+
+static DEStyle *do_create_style(WRootWin *rootwin, const char *name)
+{
+    DEStyle *style=ALLOC(DEStyle);
+    if(style!=NULL){
+        if(!destyle_init(style, rootwin, name)){
+            free(style);
+            return NULL;
+        }
+    }
+    return style;
+}
+
+
+DEStyle *de_create_style(WRootWin *rootwin, const char *name)
+{
+    DEStyle *oldstyle, *style;
+
+    style=do_create_style(rootwin, name);
+    
+    if(style==NULL)
+        return NULL;
+    
+    for(oldstyle=styles; oldstyle!=NULL; oldstyle=oldstyle->next){
+        if(oldstyle->rootwin==rootwin && oldstyle->style!=NULL && 
+           strcmp(oldstyle->style, name)==0){
+            break;
+        }
+    }
+    
+    if(oldstyle!=NULL && !oldstyle->is_fallback)
+        dump_style(oldstyle);
+    
+    LINK_ITEM_FIRST(styles, style, next, prev);
+    
+    return style;
+}
+
+
+
+/*EXTL_DOC
+ * Clear all styles from drawing engine memory.
+ */
+EXTL_EXPORT
+void de_reset()
+{
+    DEStyle *style, *next;
+    for(style=styles; style!=NULL; style=next){
+        next=style->next;
+        if(!style->is_fallback)
+            dump_style(style);
+    }
+}
+
+
+void de_deinit_styles()
+{
+    DEStyle *style, *next;
+    for(style=styles; style!=NULL; style=next){
+        next=style->next;
+        if(style->usecount>1){
+            warn(TR("Style %s still in use [%d] but the module "
+                    "is being unloaded!"), style->style, style->usecount);
+        }
+        dump_style(style);
+    }
+}
+
+
+/*}}}*/
+
+
diff --git a/de/style.h b/de/style.h
new file mode 100644 (file)
index 0000000..f25c44c
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * ion/de/style.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_DE_STYLE_H
+#define ION_DE_STYLE_H
+
+#include <libextl/extl.h>
+
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+#include <ioncore/rectangle.h>
+
+INTRSTRUCT(DEBorder);
+INTRSTRUCT(DEStyle);
+
+#include "font.h"
+#include "colour.h"
+
+enum{
+    DEBORDER_INLAID=0,    /* -\xxxxxx/- */
+    DEBORDER_RIDGE,       /* /-\xxxx/-\ */
+    DEBORDER_ELEVATED,    /* /-xxxxxx-\ */
+    DEBORDER_GROOVE       /* \_/xxxx\_/ */
+};
+
+
+enum{
+    DEALIGN_LEFT=0,
+    DEALIGN_RIGHT=1,
+    DEALIGN_CENTER=2
+};
+
+
+DECLSTRUCT(DEBorder){
+    uint sh, hl, pad;
+    uint style;
+};
+
+
+DECLSTRUCT(DEStyle){
+    char *style;
+    int usecount;
+    bool is_fallback;
+    
+    WRootWin *rootwin;
+    
+    DEStyle *based_on;
+    
+    GC normal_gc;    
+    
+    DEBorder border;
+    bool cgrp_alloced;
+    DEColourGroup cgrp;
+    int n_extra_cgrps;
+    DEColourGroup *extra_cgrps;
+    GrTransparency transparency_mode;
+    DEFont *font;
+    int textalign;
+    uint spacing;
+    
+    ExtlTab data_table;
+
+    /* Only initialised if used as a DETabBrush */
+    bool tabbrush_data_ok;
+    GC stipple_gc;
+    GC copy_gc;
+    
+    Pixmap tag_pixmap;
+    int tag_pixmap_w;
+    int tag_pixmap_h;
+    
+    DEStyle *next, *prev;
+};
+
+
+extern bool destyle_init(DEStyle *style, WRootWin *rootwin, const char *name);
+extern void destyle_deinit(DEStyle *style);
+extern DEStyle *de_create_style(WRootWin *rootwin, const char *name);
+extern void destyle_unref(DEStyle *style);
+
+extern void destyle_create_tab_gcs(DEStyle *style);
+
+extern void de_reset();
+extern void de_deinit_styles();
+
+extern DEStyle *de_get_style(WRootWin *rootwin, const char *name);
+
+
+#endif /* ION_DE_STYLE_H */
diff --git a/doc/ChangeLog b/doc/ChangeLog
new file mode 100644 (file)
index 0000000..5e3a3d1
--- /dev/null
@@ -0,0 +1,667 @@
+2006-12-23 15:00 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20061223
+
+2006-10-30 21:08 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updates
+
+2006-10-28 23:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20061029
+
+2006-10-17 22:02 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Mention the 'float' winprop
+
+2006-10-15 17:53 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20061015
+
+2006-10-15 14:36 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Documentation updates.
+
+2006-08-03 09:05 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * much->match
+
+2006-06-20 18:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20060620
+
+2006-06-08 17:58 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moving usepackage{dvipdfm} before everything else seems to fix things.
+
+2006-02-12 15:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added documentation for region_activity_hook.
+
+2006-01-25 23:23 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated bar_inside_border stuf.
+
+2006-01-07 21:03 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20060107
+
+2005-12-10 20:46 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20051210
+
+2005-12-10 00:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added missing min_size winprop and changes in winprop listing.
+
+2005-11-10 20:22 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated frame style documentation.
+
+2005-10-23 22:40 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20051023
+
+2005-08-26 17:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * List oneshot winprop.
+
+2005-08-20 11:37 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20050820
+
+2005-07-28 17:40 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20050728
+
+2005-06-25 15:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20050625
+
+2005-06-07 13:14 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20050607
+
+2005-06-07 13:13 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * predist.sh updates.
+
+2005-06-01 17:07 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added a note on NoModifier.
+
+2005-05-02 14:56 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20050502
+
+2005-03-25 17:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added documentation on ion-statusd monitors.
+
+2005-03-25 17:41 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed a typo, etc.
+
+2005-03-22 14:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20050322
+
+2005-03-21 00:16 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed typos.
+
+2005-03-19 13:55 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added documentation on ioncore_sigchld_hook.
+
+2005-03-13 12:34 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed typos etc.
+
+2005-03-13 12:27 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * fnrefx macro was still wrong.
+
+2005-03-09 23:12 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Moved new layer documentation to scripting chapter.
+
+2005-03-09 22:23 UTC  Matthieu.Moy@imag.fr
+  * More doc on layer 2 and placement hooks
+  
+  Be warned that:
+  
+  * I missed some LaTeX package, and therefore couldn't compile my
+    changes,
+  
+  * I don't know ion's internal well enough to be sure of what I write.
+  
+
+2005-03-07 09:35 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Fixed copy-paste error.
+
+2005-03-04 08:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20050304
+
+2005-03-03 18:42 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Added a note on hooks being called in protected mode.
+
+2005-03-03 18:39 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  * Updated ionws_placement_alt documentation.
+
+2005-02-27 13:19 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3ds-20050227
+
+2005-02-22 22:54 UTC  Tuomo Valkonen <tuomov@iki.fi>
+  tagged ion-doc-3-svn2darcs
+
+2005-01-16 11:52 UTC  tuomov
+  tagged ion-doc-3ds-20050116
+
+2005-01-16 11:29 UTC  tuomov
+  * trunk: changeset 1931
+  fixed predist.sh
+
+2005-01-16 11:08 UTC  tuomov
+  * trunk: changeset 1930
+  Fixed undefined references.
+
+2005-01-15 21:33 UTC  tuomov
+  * trunk: changeset 1928
+  Updated some scripting docs.
+
+2005-01-15 21:20 UTC  tuomov
+  * trunk: changeset 1926
+  tabularx header changes.
+
+2005-01-15 21:03 UTC  tuomov
+  * trunk: changeset 1924
+  Tuning for latex2html etc.
+
+2005-01-15 17:29 UTC  tuomov
+  * trunk: changeset 1923
+  Added hook reference and updated hook documentation.
+
+2005-01-15 17:29 UTC  tuomov
+  * trunk: changeset 1922
+  Improved function reference macros to use lists instead of tables.
+
+2005-01-14 14:47 UTC  tuomov
+  * trunk: changeset 1920
+  Some minor fixes.
+
+2005-01-14 14:25 UTC  tuomov
+  * trunk: changeset 1918
+  - Improved menu documentation.
+  
+  - Other minor improvements.
+
+2005-01-13 16:49 UTC  tuomov
+  * trunk: changeset 1917
+  Updated graphical styles documentation.
+
+2005-01-13 14:40 UTC  tuomov
+  * trunk: changeset 1915
+  Updated winprop documentation.
+
+2005-01-13 14:27 UTC  tuomov
+  * trunk: changeset 1914
+  Updated binding and menu definition documentation.
+
+2005-01-11 20:18 UTC  tuomov
+  * trunk: changeset 1913
+  Brought config file intro up-to-date
+
+2005-01-11 15:31 UTC  tuomov
+  * trunk: changeset 1912
+  Updated class hierarchy documentation etc.
+
+2004-10-11 14:16 UTC  tuomov
+  * trunk: changeset 1821
+  Just testing some settings...
+
+2004-10-09 06:16 UTC  tuomov
+  * trunk: changeset 1804
+  libextl related changes.
+
+2004-10-09 05:56 UTC  tuomov
+  * trunk: changeset 1801
+  Added predist.sh
+
+2004-10-09 05:56 UTC  tuomov
+  * trunk: changeset 1800
+  Fixed a typo.
+
+2004-09-16 22:04 UTC  tuomov
+  * trunk: changeset 1754
+  Source file path updates.
+
+2004-07-31 22:25 UTC  tuomov
+  * trunk: changeset 1697
+  Updated Makefile to point to the new location of mkexports.lua.
+
+2004-07-26 21:57 UTC  tuomov
+  * trunk: changeset 1646
+  Function reference generation updates.
+
+2004-05-14 12:29 UTC  tuomov
+  * trunk: changeset 1485
+  Fixed punctuation.
+
+2004-03-15 16:32 UTC  tuomov
+  * trunk: changeset 1402
+  Changed makefile to account for changes in Ion module directories.
+
+2004-03-15 16:23 UTC  tuomov
+  * trunk: changeset 1400
+  Added install target to Makefile.
+
+2004-03-15 08:35 UTC  tuomov
+  * trunk: changeset 1398
+  Brought document generation up-to-date with module name changes.
+
+2004-03-10 21:57 UTC  tuomov
+  * trunk: changeset 1383
+  Merged omission and other fixes from stable branch.
+
+2004-03-05 23:49 UTC  tuomov
+  * trunk: changeset 1346
+  Some more minor documentation fixes. Just to test this new svn setup.
+
+2004-03-05 23:00 UTC  tuomov
+  * trunk: changeset 1345
+  Minor documentation fixes.
+
+2004-02-16 20:33 UTC  tuomov
+  * trunk: changeset 1323
+  Build process changed for Ion3.
+
+2004-02-07 01:55 UTC  tuomov
+  * trunk: changeset 1255
+  Added documentation on alternative get_winprop implementations.
+
+2004-02-07 01:42 UTC  tuomov
+  * trunk: changeset 1254
+  Removed unobfuscated email addresses.
+
+2004-01-28 22:04 UTC  tuomov
+  * trunk: changeset 1239
+  Removed extra word, added function reference.
+
+2004-01-27 22:30 UTC  tuomov
+  * trunk: changeset 1235
+  Added 'are' in 'that they are transient for'
+
+2004-01-27 22:26 UTC  tuomov
+  * trunk: changeset 1234
+  Improved xprop/transient help and winprop examples.
+
+2004-01-27 13:54 UTC  tuomov
+  * trunk: changeset 1231
+  Minor spelling mistake fix.
+
+2004-01-25 18:42 UTC  tuomov
+  * trunk: changeset 1225
+  Some minor clarifications in a few sentences and put URL:s on their
+  own lines.
+
+2004-01-25 18:05 UTC  tuomov
+  * trunk: changeset 1222
+  Typo and spelling fixes.
+
+2004-01-23 17:32 UTC  tuomov
+  * trunk: changeset 1215
+  Fixed a few typos and added an example.
+
+2004-01-22 21:25 UTC  tuomov
+  * trunk: changeset 1213
+  Added dock documentation.
+
+2004-01-14 22:29 UTC  tuomov
+  * trunk: changeset 1193
+  Minor cleanup.
+
+2004-01-08 22:32 UTC  tuomov
+  * trunk: changeset 1183
+  Updated winprop documentation.
+
+2003-12-22 23:58 UTC  tuomov
+  * trunk: changeset 162
+  Added discussion on modules and improved the main introduction and
+  object hierarchy introduction.
+
+2003-12-21 00:17 UTC  tuomov
+  * trunk: changeset 161
+  Removed duplicate label.
+
+2003-12-20 19:49 UTC  tuomov
+  * trunk: changeset 160
+  Added a list of functions.
+
+2003-12-20 15:18 UTC  tuomov
+  * trunk: changeset 159
+  Fixed a typo.
+
+2003-12-20 15:15 UTC  tuomov
+  * trunk: changeset 158
+  Oops.
+
+2003-12-20 15:14 UTC  tuomov
+  * trunk: changeset 157
+  Added a section on object references.
+
+2003-12-19 22:08 UTC  tuomov
+  * trunk: changeset 156
+  Mention stylemenu.
+
+2003-12-18 21:37 UTC  tuomov
+  * trunk: changeset 155
+  Added a section on special menus.
+
+2003-12-23 20:18 UTC  unknown
+  tagged ion-doc-20031211
+
+2003-12-11 17:29 UTC  tuomov
+  * trunk: changeset 154
+  Updates for 20031211 release.
+
+2003-12-23 20:18 UTC  unknown
+  tagged ion-doc-20031119
+
+2003-11-19 21:57 UTC  tuomov
+  * trunk: changeset 153
+  Removed 'executive' from summary.
+
+2003-11-19 21:57 UTC  tuomov
+  * trunk: changeset 152
+  Removed mplexfns reference.
+
+2003-11-17 00:42 UTC  tuomov
+  * trunk: changeset 151
+  Added an "executive summary" to the object hierarchy chapter.
+
+2003-11-14 18:15 UTC  tuomov
+  * trunk: changeset 150
+  Documented menus. Changes to reflect changes in configuration files.
+
+2003-10-30 10:04 UTC  tuomov
+  * trunk: changeset 149
+  Get documentation from luaextl/*.c as well.
+
+2003-10-29 12:05 UTC  tuomov
+  * trunk: changeset 148
+  LICENSE file changed to GPL.
+
+2003-10-29 11:55 UTC  tuomov
+  * trunk: changeset 147
+  Removed fdl.tex.
+
+2003-10-29 11:55 UTC  tuomov
+  * trunk: changeset 146
+  Changed license to GPL due to problems with the GFDL.
+
+2003-10-29 11:54 UTC  tuomov
+  * trunk: changeset 145
+  Changed \chapter->\xchapter, \section*->\xsectionstar
+
+2003-10-29 11:22 UTC  tuomov
+  * trunk: changeset 144
+  Added gpl.tex; GNU General Public License.
+
+2003-10-29 11:20 UTC  tuomov
+  * trunk: changeset 143
+  Added menu reference and updated drawing engine documentation to
+  reflect the change in style names.
+
+2003-12-23 20:18 UTC  unknown
+  tagged ion-doc-20030811
+
+2003-08-10 23:45 UTC  tuomov
+  * trunk: changeset 142
+  Fixed a few more typos and such.
+
+2003-08-10 23:37 UTC  tuomov
+  * trunk: changeset 141
+  Fixed a typo.
+
+2003-08-10 23:37 UTC  tuomov
+  * trunk: changeset 140
+  The files draw-DISPLAY.SCREEN.lua were still being referenced to.
+
+2003-12-23 20:18 UTC  unknown
+  tagged ion-doc-20030810
+
+2003-08-09 12:48 UTC  tuomov
+  * trunk: changeset 139
+  Added a quote.
+
+2003-08-09 12:45 UTC  tuomov
+  * trunk: changeset 138
+  Added a few spaces.
+
+2003-08-09 12:45 UTC  tuomov
+  * trunk: changeset 137
+  Added a walk-through of ioncore.lua.
+
+2003-08-08 21:56 UTC  tuomov
+  * trunk: changeset 136
+  Added an introduction and some design decision rationale.
+
+2003-08-07 21:45 UTC  tuomov
+  * trunk: changeset 135
+  Added information on winprop name field and improved winprop lookup
+  order explanation.
+
+2003-08-06 15:08 UTC  tuomov
+  * trunk: changeset 134
+  look-cleanviolet.lua was changed and so the example.
+
+2003-08-06 15:06 UTC  tuomov
+  * trunk: changeset 133
+  The 'urgent' attributes was renamed 'activity'.
+
+2003-08-01 05:54 UTC  tuomov
+  * trunk: changeset 132
+  Added mention of ignore_cfgrq.
+
+2003-07-31 21:24 UTC  tuomov
+  * trunk: changeset 131
+  Started a new chapter on assorted tricks.
+
+2003-07-31 15:21 UTC  tuomov
+  * trunk: changeset 130
+  A few fixes.
+
+2003-07-31 15:17 UTC  tuomov
+  * trunk: changeset 129
+  Improved drawing engine and style documentation.
+
+2003-07-30 22:47 UTC  tuomov
+  * trunk: changeset 128
+  Added a \docode kludge to have latex2html interpret figure
+  environments instead of generating images.
+
+2003-07-30 22:27 UTC  tuomov
+  * trunk: changeset 127
+  Started documenting drawing engines.
+
+2003-07-25 20:48 UTC  tuomov
+  * trunk: changeset 126
+  Moved WRootWin under WWindow in class hierarchy.
+
+2003-07-24 19:35 UTC  tuomov
+  * trunk: changeset 125
+  Some bindings setup documentation improvements.
+
+2003-07-13 22:16 UTC  tuomov
+  * trunk: changeset 124
+  Changes to accommodate for more OO bindings.
+
+2003-12-23 20:18 UTC  unknown
+  tagged ion-doc-20030710
+
+2003-07-10 17:18 UTC  tuomov
+  * trunk: changeset 123
+  Class explanations improved.
+
+2003-12-23 20:18 UTC  unknown
+  tagged ion-doc-20030628
+
+2003-06-27 22:58 UTC  tuomov
+  * trunk: changeset 122
+  Typos etc.
+
+2003-06-27 20:56 UTC  tuomov
+  * trunk: changeset 121
+  Fixed QueryLib introduction.
+
+2003-06-17 21:02 UTC  tuomov
+  * trunk: changeset 120
+  Removed mention of region_close and make_current_or_self_fn as this
+  binding does not work.
+
+2003-06-17 10:25 UTC  tuomov
+  * trunk: changeset 119
+  Added notes on C coding style.
+
+2003-12-23 20:18 UTC  unknown
+  tagged ion-doc-20030614
+
+2003-06-14 19:06 UTC  tuomov
+  * trunk: changeset 118
+  Fixed "or"->"are".
+
+2003-06-14 18:01 UTC  tuomov
+  * trunk: changeset 117
+  \date{} setting moved to distribution building script.
+
+2003-06-14 17:54 UTC  tuomov
+  * trunk: changeset 116
+  Minor bug was fixed.
+
+2003-06-14 17:53 UTC  tuomov
+  * trunk: changeset 115
+  Some clarifications were made in a few explanations and a note on
+  anonymous functions was added.
+
+2003-06-14 17:53 UTC  tuomov
+  * trunk: changeset 114
+  Redefined the itemize environment to look better.
+
+2003-06-14 15:10 UTC  tuomov
+  * trunk: changeset 113
+  Updated the documentation to use the new binding setting functions.
+
+2003-06-08 17:58 UTC  tuomov
+  * trunk: changeset 112
+  Added FDL as a plaintext file LICENSE.
+
+2003-06-08 17:56 UTC  tuomov
+  * trunk: changeset 111
+  Documentation updated to mention WMPlexes.
+
+2003-12-23 20:18 UTC  unknown
+  tagged ion-doc-20030531
+
+2003-05-31 15:39 UTC  tuomov
+  * trunk: changeset 110
+  Dates updated.
+
+2003-05-30 21:32 UTC  tuomov
+  * trunk: changeset 109
+  Updated the parts that refer to the files that were moved to SHAREDIR.
+
+2003-05-29 13:11 UTC  tuomov
+  * trunk: changeset 108
+  Added a note on char* and const char *.
+
+2003-05-29 12:51 UTC  tuomov
+  * trunk: changeset 107
+  Added one more note on deferred destroy.
+
+2003-05-29 12:47 UTC  tuomov
+  * trunk: changeset 106
+  Added a note on deferred destroy.
+
+2003-05-17 13:33 UTC  tuomov
+  * trunk: changeset 105
+  Updated the documentation to switch to using WRootWins for X screens
+  and WScreens for physical screens.
+
+2003-05-13 05:22 UTC  tuomov
+  * trunk: changeset 104
+  Some minor fixes.
+
+2003-12-23 20:18 UTC  unknown
+  tagged ion-doc-20030510
+
+2003-05-10 20:09 UTC  tuomov
+  * trunk: changeset 103
+  Removed repeated word.
+
+2003-05-10 20:02 UTC  tuomov
+  * trunk: changeset 102
+  Fixed some typos.
+
+2003-05-10 18:55 UTC  tuomov
+  * trunk: changeset 101
+  Oops. The all-ps rule was broken.
+
+2003-05-10 18:51 UTC  tuomov
+  * trunk: changeset 100
+  License notes changed to use \copyright instead of "(c)"
+
+2003-05-10 18:28 UTC  tuomov
+  * trunk: changeset 99
+  A README was added.
+
+2003-05-10 18:26 UTC  tuomov
+  * trunk: changeset 98
+  File removed.
+
+2003-05-10 18:26 UTC  tuomov
+  * trunk: changeset 97
+  Object system implementation was split out from the chapter/section on
+  object and class hierarchies.
+
+2003-05-10 18:25 UTC  tuomov
+  * trunk: changeset 96
+  Added implementation of artikel3 for latex2html.
+
+2003-05-10 18:24 UTC  tuomov
+  * trunk: changeset 95
+  Implementation of rapport3 for latex2html was added.
+
+2003-05-10 18:24 UTC  tuomov
+  * trunk: changeset 94
+  The macros were cleaned up
+
+2003-05-10 18:24 UTC  tuomov
+  * trunk: changeset 93
+  Better Makefile
+
+2003-05-10 18:23 UTC  tuomov
+  * trunk: changeset 92
+  FDL was modified to use the \xchapter etc. macros to enable embedding
+  it in articles.
+
+2003-05-10 18:23 UTC  tuomov
+  * trunk: changeset 91
+  Clarifications (hopefully) ein the explanation of the object and class
+  hierarchies.
+
+2003-05-10 18:22 UTC  tuomov
+  * trunk: changeset 90
+  The configuration files were documented.
+
+2003-05-10 18:22 UTC  tuomov
+  * trunk: changeset 89
+  The original document "Ion: Objects and extending" was split into
+  separate documents "Ion: Configuring and extending with Lua" and "Ion:
+  notes for the module and patch writer".
+
+2003-05-10 15:50 UTC  tuomov
+  * trunk: changeset 88
+  Added the GNU Free Documentation License.
+
+2003-05-08 18:31 UTC  tuomov
+  * trunk: changeset 87
+  Added more documentation on binding configuration.
+
+2003-05-07 18:43 UTC  tuomov
+  * trunk: changeset 86
+  Added QueryLib and Ioncorelib references. Moved function reference
+  .tex generation code here from ion.
+
+2003-05-06 19:46 UTC  tuomov
+  * trunk: changeset 85
+  Did some reorganization and added links to Lua documentation.
+
+2003-05-06 17:48 UTC  tuomov
+  * trunk: changeset 84
+  Added a \hline in the type table.
+
+2003-05-06 17:35 UTC  tuomov
+  * trunk: changeset 83
+  Added Makefile rules to generate postscript and pdf
+
+2003-05-06 17:33 UTC  tuomov
+  * trunk: changeset 82
+  Added a Makefile
+
+2005-02-16 07:18 UTC  tailor@f281.ttorni.ton.tut.fi
+  * Tailorization of trunk
+  Import of the upstream sources from the repository
+  
+   http://tao.uab.es/ion/svn/ion-doc/trunk
+  
+  as of revision 80
+  
diff --git a/doc/LICENSE b/doc/LICENSE
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, 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 software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) 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; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, 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 executable.  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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+  5. 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 Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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.
+
+  7. 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 Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  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 program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644 (file)
index 0000000..bd580ca
--- /dev/null
@@ -0,0 +1,136 @@
+# Settings
+######################################
+
+TOPDIR=../ion-3
+
+include $(TOPDIR)/build/system-inc.mk
+
+L2H=latex2html -show_section_numbers -short_index -local_icons -noaddress \
+    -up_url http://iki.fi/tuomov/ion/ -up_title "Ion homepage" -nofootnode\
+##    -style greyviolet.css
+
+
+# Function documentation to build
+######################################
+
+DOCS=ionconf ionnotes
+
+FNTEXES=ioncore-fns.tex mod_tiling-fns.tex \
+       mod_query-fns.tex de-fns.tex mod_menu-fns.tex \
+       mod_dock-fns.tex mod_sp-fns.tex
+
+# Generic rules
+######################################
+
+nothing:
+       @ echo "Please read the README first."
+
+%.ps: %.dvi
+       dvips $<
+
+%.pdf: %.dvi
+       dvipdfm -p a4 $<
+
+%.dvi: %.tex
+       latex $<
+
+# Install
+######################################
+
+install:
+       $(INSTALLDIR) $(DOCDIR); \
+       for d in $(DOCS); do \
+           for e in ps pdf dvi; do \
+             test -f $$d.$$e && $(INSTALL) -m $(DATA_MODE) $$d.$$e $(DOCDIR); \
+            done; \
+           $(INSTALLDIR) $(DOCDIR)/$$d; \
+            for i in $$d/*; do \
+                $(INSTALL) -m $(DATA_MODE) $$i $(DOCDIR)/$$i; \
+           done; \
+        done
+
+# ionconf rules
+######################################
+ionconf-dvi-full:
+       latex ionconf
+       latex ionconf
+       latex ionconf
+       makeindex ionconf.idx
+       latex ionconf
+
+ionconf-html: 
+       $(L2H) -split 3 ionconf
+
+fntexes: $(FNTEXES)
+
+ionconf-all: fntexes fnlist.tex ionconf-dvi-full ionconf-html
+
+# ionnotes rules
+######################################
+
+ionnotes-dvi-full:
+       latex ionnotes
+       latex ionnotes
+       latex ionnotes
+       makeindex ionnotes.idx
+       latex ionnotes
+
+ionnotes-html: 
+       $(L2H) -split 4 ionnotes
+
+ionnotes-all: ionnotes-dvi-full ionnotes-html
+
+# More generic rules
+######################################
+
+all: ionconf-all ionnotes-all
+
+all-ps: ionconf.ps ionnotes.ps
+
+all-pdf: ionconf.pdf ionnotes.pdf
+
+
+# Clean
+######################################
+
+clean:
+       rm -f $(FNTEXES) fnlist.tex
+       rm -f *.aux *.toc *.log
+       rm -f *.idx *.ild *.ilg *.ind
+        
+realclean: clean
+       rm -f *.ps *.pdf *.dvi
+       rm -rf $(DOCS)
+
+
+# Function reference rules
+######################################
+
+ioncore-fns.tex: $(TOPDIR)/ioncore/*.c $(TOPDIR)/ioncore/*.lua
+       $(MKEXPORTS) -module ioncore -mkdoc -o $@ $+
+
+mod_tiling-fns.tex: $(TOPDIR)/mod_tiling/*.c
+       $(MKEXPORTS) -module mod_tiling -mkdoc -o $@ $+
+
+mod_query-fns.tex: $(TOPDIR)/mod_query/*.c $(TOPDIR)/mod_query/mod_query.lua
+       $(MKEXPORTS) -module mod_query -mkdoc -o $@ $+
+
+mod_menu-fns.tex: $(TOPDIR)/mod_menu/*.c $(TOPDIR)/mod_menu/mod_menu.lua
+       $(MKEXPORTS) -module mod_menu -mkdoc -o $@ $+
+
+mod_dock-fns.tex: $(TOPDIR)/mod_dock/*.c
+       $(MKEXPORTS) -module mod_dock -mkdoc -o $@ $+
+
+mod_sp-fns.tex: $(TOPDIR)/mod_sp/*.c
+       $(MKEXPORTS) -module mod_sp -mkdoc -o $@ $+
+
+de-fns.tex: $(TOPDIR)/de/*.c
+       $(MKEXPORTS) -module de -mkdoc -o $@ $+
+
+# Function list
+######################################
+
+fnlist.tex: $(FNTEXES)
+       grep hyperlabel $+ | \
+       sed 's/.*fn:\([^}]*\).*/\\fnlisti{\1}/;'|sort -d -f \
+       > $@
diff --git a/doc/README b/doc/README
new file mode 100644 (file)
index 0000000..604bf85
--- /dev/null
@@ -0,0 +1,23 @@
+
+Ion-doc
+
+Tuomo Valkonen <tuomov at iki.fi>
+
+
+This package contains some (advanced user and module writer)
+documentation for the Ion window manager.
+
+You will need to install latex2html to build the HTML documents.
+
+How to build:
+
+ 1. Modify TOPDIR in Makefile point to your top-level Ion source
+    directory, containing a working system.mk (or system-ac.mk).
+
+ 3. After the above has been done, you may run 'make all' to build the
+    (hyperlinked) DVI and HTML versions of the documents. You may have
+    increase TeX pool size.
+
+ 4. If you want Postcript versions of the documents, run 'make all-ps'.
+    For PDF you need to have dvipdfm installed. The PDF:s are obviously
+    built with 'make all-pdf'.
diff --git a/doc/artikel3.perl b/doc/artikel3.perl
new file mode 100644 (file)
index 0000000..110f32c
--- /dev/null
@@ -0,0 +1,12 @@
+# artikel3.perl by Tuomo Valkonen, <tuomov at iki.fi>, 2003-05-10
+#
+# Implementation of the documentclass for latex2html. Just load
+# article.
+#
+
+package main;
+
+&do_require_package("article");
+
+1;
+
diff --git a/doc/conf-bindings.tex b/doc/conf-bindings.tex
new file mode 100644 (file)
index 0000000..9afb2b8
--- /dev/null
@@ -0,0 +1,269 @@
+\section{Keys and rodents}
+\label{sec:bindings}
+
+In the stock configuration file setup, most key and mouse bindings are set
+from the file \file{cfg\_ioncore.lua} while module-specific bindings
+are set from the modules' main configuration files (\file{cfg\_modname.lua}).
+This, however, does not have to be so as long as the module has been
+loaded prior to defining any module-specific bindings.
+
+Bindings are defined by calling the function 
+\fnrefx{ioncore}{defbindings} with the ''context'' of the
+bindings and the a table of new bindings to make. The context is simply
+string indicating one of the classes of regions (or modes such as
+\type{WMoveresMode}) introduced in section \ref{sec:objects}, and fully
+listed in appendix \ref{app:fullhierarchy}, although not all define
+a binding map. For example, the following skeleton would be used to 
+define new bindings for all frames:
+
+\begin{verbatim}
+defbindings("WFrame", {
+    -- List of bindings to make goes here.
+})
+\end{verbatim}
+
+There has been some confusion among users about the need to define the
+''context'' for each binding, so let me try to explain this design
+decision here. The thing is that if there was a just a simple 'bind this 
+key to this action' method without knowledge of the context, some 
+limitations would have to be made on the available actions and writing 
+custom handlers would be more complicated. In addition one may want to 
+bind the same function to different key for different types of objects.
+Indeed, the workspace and frame tab switching functions are the same both
+classes being based on \type{WMPlex}, and in the stock configuration the 
+switch to $n$:th workspaces is bound to \key{Mod1+n} while the switch to 
+$n$:th tab is bound to the sequence \key{Mod1+k n}.
+
+Currently known ''contexts'' include: 
+\code{WScreen},
+\code{WMPlex},
+\code{WMPlex.toplevel},
+\code{WFrame},
+\code{WFrame.toplevel},
+\code{WFrame.floating},
+\code{WFrame.tiled},
+\code{WFrame.transient},
+\code{WMoveresMode},
+\code{WGroup},
+\code{WGroupCW},
+\code{WGroupWS},
+\code{WClientWin},
+\code{WTiling}, and
+\code{WStatusBar}.
+Most of these should be self-explanatory, corresponding to objects
+of class with the same name. The ones with \code{.toplevel} suffix
+refer to screens and ''toplevel''  frames, i.e. frames that are
+not used for transient windows. Likewise \code{.transient} refers
+to frames in transient mode, and \code{.tiled} and \code{.floating}
+to frames in, respectively, tiled and floating modes. 
+
+                                                        
+
+The following subsections describe how to construct elements of the
+binding table. Note that \fnrefx{ioncore}{defbindings} adds
+the the newly defined bindings to the previous bindings of the context,
+overriding duplicates. To unbind an event, set the handler parameter
+to \code{nil} for each of the functions to be described in the following
+subsections.
+
+Also note that when multiple objects want to handle a binding, the 
+innermost (when the root window is considered the outermost) active object
+in the parent--child hierarchy (see Figure \ref{fig:parentship}) of objects 
+gets to handle the action.
+
+
+\subsection{Binding handlers and special variables}
+
+Unlike in Ion2, in Ion3 binding handlers are not normally passed as
+''anonymous functions'', although this is still possible. The preferred
+method now is to pass the code of the handler as a string. Two special 
+variables are available in this code. These are
+
+\begin{tabularx}{\linewidth}{lX}
+    \tabhead{Variable & Description}
+    \code{_} (underscore) &
+      Reference to the object on which the 
+      binding was triggered. The object is of the same class as the the
+      context of the \fnrefx{ioncore}{defbindings} call
+      defining the binding. \\
+    \code{_sub} &
+      Usually, the currently active \emph{managed object} of the 
+      object referred to by \code{_}, but sometimes (e.g. mouse actions
+      on tabs of frames) something else relevant to the action triggering
+      the binding. \\
+    \code{_chld} &
+      Object corresponding to the currently active child window of the
+       object referred to by \code{_}.
+\end{tabularx}
+
+For example, supposing '\code{_}' is a \type{WFrame}, the following
+handler should move the active window to the right, if possible:
+
+\begin{verbatim}
+"_:inc_index(_sub)"
+\end{verbatim}
+
+\subsection{Guards}
+
+To suppress error messages, each binding handler may also be accompanied
+by a ''guard'' expression that blocks the handler from being called when
+the guard condition is not met. Currently the following guard expressions
+are supported (for both \code{_sub} and \code{_chld}):
+
+\begin{tabularx}{\linewidth}{lX}
+    \tabhead{Guard & Description}
+    \code{"_sub:non-nil"} & The \code{_sub} parameter must be set. \\
+    \code{"_sub:SomeClass"} & The \code{_sub} parameter must be member
+      of class \type{SomeClass}. \\
+\end{tabularx}
+
+
+\subsection{Defining the bindings}
+\label{sec:binddef}
+
+The descriptions of the individual bindings in the binding table argument
+to \fnrefx{ioncore}{defbindings} should be constructed with the following
+functions.
+
+Key presses:
+\begin{itemize}
+    \item \fnref{kpress}\code{(keyspec, handler [, guard])},
+    \item \fnref{kpress_wait}\code{(keyspec, handler [, guard])} and
+    \item \fnref{submap}\code{(keyspec, \{ ... more key bindings ... \})}.
+\end{itemize}
+Mouse actions:
+\begin{itemize}
+    \item \fnref{mclick}\code{(buttonspec, handler [, guard])},
+    \item \fnref{mdblclick}\code{(buttonspec, handler [, guard])}, 
+    \item \fnref{mpress}\code{(buttonspec, handler [, guard])} and
+    \item \fnref{mdrag}\code{(buttonspec, handler [, guard])}.
+\end{itemize}
+
+The actions that most of these functions correspond to should be clear
+and as explained in the reference, \fnref{kpress_wait} is simply
+\fnref{kpress} with a flag set instructing Ioncore wait for all
+modifiers to be released before processing any further actions.
+This is to stop one from accidentally calling e.g.
+\fnref{WRegion.rqclose} multiple times in a row. The \fnref{submap}
+function is used to define submaps or ''prefix maps''. The second
+argument to this function is table listing the key press actions
+(\fnref{kpress}) in the submap
+
+The parameters \var{keyspec} and \var{buttonspec} are explained below
+in detail. The parameter \var{handler} is the handler for the binding,
+and the optional parameter \var{guard} its guard. These should normally
+be strings as explained above. 
+
+\subsection{Examples}
+
+For example, to just bind the key \key{Mod1+1} to switch to the first
+workspace and \key{Mod1+Right} to the next workspace, you would make the
+following call
+\begin{verbatim}
+defbindings("WScreen", {
+    kpress("Mod1+Right", "_:switch_next()"),
+    kpress("Mod1+1", "_:switch_nth(1)"),
+})
+\end{verbatim}
+
+Note that \code{_:switch_nth(1)} is the same as calling
+\fnref{WMPlex.switch_next}\code{(_, 1)} as \type{WScreen} inherits
+\type{WMPlex} and this is where the function is actually defined.
+
+Similarly to the above example, to bind the key sequence \key{Mod1+k n} 
+switch to the next managed object within a frame, and \key{Mod1+k 1} to the
+first, you would issue the following call:
+\begin{verbatim}
+defbindings("WFrame", {
+    submap("Mod1+K", {
+        kpress("Right", "_:switch_next()"),
+        kpress("1", "_:switch_nth(1)"),
+   }),
+})
+\end{verbatim}
+
+
+\subsection{Key specifications}
+
+As seen above, the functions that create key binding specifications require
+a \var{keyspec} argument. This argument should be a string containing the
+name of a key as listed in the X header file \file{keysymdef.h}%
+\footnote{This file can usually be found in the directory
+\file{/usr/X11R6/include/X11/}.} without the \code{XK_} prefix.
+\index{keysymdef.h@\file{keysymdef.h}}
+Most of the key names are quite intuitive while some are not. For example,
+the \key{Enter} key on the main part of the keyboard has the less common
+name \key{Return} while the one the numpad is called \key{KP\_Enter}.
+
+The \var{keyspec} string may optionally have multiple ''modifier'' names
+followed by a plus sign (\code{+}) as a prefix. X defines the following
+modifiers:
+\begin{quotation}
+\key{Shift}, \key{Control}, \key{Mod1} to \key{Mod5},
+\key{AnyModifier} and \key{Lock}.
+\index{Shift@\key{Shift}}
+\index{Control@\key{Control}}
+\index{ModN@\key{ModN}}
+\index{AnyModifier@\key{AnyModifier}}
+\index{Lock@\key{Lock}}
+\end{quotation}
+
+X allows binding all of these modifiers to almost any key and while this
+list of modifiers does not explicitly list keys such as 
+\key{Alt}\index{Alt@\key{Alt}} that are common on modern keyboards, such
+keys are bound to one of the \key{ModN}. On systems running XFree86
+\key{Alt} is usually \key{Mod1}. On Suns \key{Mod1} is the diamond key
+and \key{Alt} something else. One of the ''flying window'' keys on so
+called Windows-keyboards is probably mapped to \key{Mod3} if you have
+such a key. Use the program \file{xmodmap}\index{xmodmap@\file{xmodmap}}
+to find out what exactly is bound where. 
+
+Ion defaults to \key{AnyModifier} in submaps. This can sometimes lead to
+unwanted effects when the same key is used with and without explicitly
+specified modifiers in nested regions. For this reason, Ion recognises
+\key{NoModifier} as a special modifier that can be used to reset this
+default.
+
+Ion ignores the \key{Lock} modifier and any \key{ModN} ($N=1{\ldots} 5$)
+bound to \key{NumLock}\index{NumLock@\key{NumLock}} or
+\key{ScrollLock}\index{ScrollLock@\key{ScrollLock}}
+by default because such\footnote{Completely useless keys that should be
+gotten rid of in the author's opinion.} locking keys may otherwise
+cause confusion.
+
+
+\subsection{Button specifications}
+
+Button specifications are similar to key definitions but now
+instead of specifying modifiers and a key, you specify modifiers
+and one of the button names \key{Button1} to
+\key{Button5}\index{Button-n@\key{Button-n}}. Additionally the
+specification may end with an optional area name following an @-sign.
+Only frames currently support areas, and the supported values in this
+case are
+\code{"border"}, \code{"tab"}, \code{"empty_tab"}, \code{"client"} and
+\code{nil} (for the whole frame).
+
+For example, the following code binds dragging a tab with the first 
+button pressed to initiate tab drag\&drop handling:
+
+\begin{verbatim}
+defbindings("WFrame", {
+    mdrag("Button1@tab", "_:p_tabdrag()"),
+})
+\end{verbatim}
+
+
+\subsection{A further note on the default binding configuration}
+
+The default binding configuration contains references to the variables
+\code{META} and \code{ALTMETA} instead of directly using the default
+values of \code{"Mod1+"} and \code{""} (nothing). As explained in
+section \ref{sec:walkthrough}, the definitions of these variables
+appear in \file{cfg\_ion.lua}. This way you can easily change the the
+modifiers used by all bindings in the default configuration without 
+changing the whole binding configuration. Quite a few people prefer 
+to use the Windows keys as modifiers because many applications already
+use \key{Alt}. Nevertheless, \key{Mod1} is the default as a key bound 
+to it is available virtually everywhere.
+
diff --git a/doc/conf-menus.tex b/doc/conf-menus.tex
new file mode 100644 (file)
index 0000000..3f9ee17
--- /dev/null
@@ -0,0 +1,120 @@
+\section{Menus}
+\label{sec:menus}
+
+\subsection{Defining menus}
+
+\index{menus}
+\index{defmenu@\code{defmenu}}
+\index{menuentry@\code{menuentry}}
+\index{submenu@\code{submenu}}
+In the stock configuration file setup, menus are defined in the file
+\file{cfg\_menus.lua} as previously mentioned. The \file{mod\_menu} module
+must be loaded for one to be able to define menus, and this is done with
+the function \fnrefx{mod_menu}{defmenu} provided by it.
+
+Here's an example of the definition of a rather simple menu with a submenu:
+
+\begin{verbatim}
+defmenu("exitmenu", {
+    menuentry("Restart", "ioncore.restart()"),
+    menuentry("Exit", "ioncore.shutdown()"),
+})
+
+defmenu("mainmenu", {
+    menuentry("Lock screen", "ioncore.exec('xlock')"),
+    menuentry("Help", "mod_query.query_man(_)"),
+    submenu("Exit", "exitmenu"),
+})
+\end{verbatim}
+
+
+The \fnrefx{mod_menu}{menuentry} function is used to create an entry in the 
+menu with a title and an entry handler to be called when the menu entry
+is activated. The parameters to the handler are similar to those of binding
+handlers, and usually the same as those of the binding that opened the menu.
+
+The \fnrefx{mod_menu}{submenu} function is used to insert a submenu at that 
+point in the menu. (One could as well just pass a table with the menu
+entries, but it is not encouraged.)
+
+\subsection{Special menus}
+
+The menu module predefines the following special menus. These can be used
+just like the menus defined as above.
+
+\begin{tabularx}{\linewidth}{lX}
+    \tabhead{Menu name & Description}
+    \code{windowlist} & 
+    List of all client windows. Activating an entry jumps to that window. \\
+    \code{workspacelist} & 
+    List of all workspaces. Activating an entry jumps to that workspaces. \\
+    \code{stylemenu} &
+    List of available \file{look\_*.lua} style files. Activating an entry
+    loads that style and ask to save the selection. \\
+    \code{ctxmenu} &
+    Context menu for given object. \\
+\end{tabularx}
+
+
+\subsection{Defining context menus}
+
+The ''ctxmenu'' is a special menu that is assembled from a defined context
+menu for the object for which the menu was opened for, but also includes
+the context menus for the manager objects as submenus.
+
+Context menus for a given region class are defined with the
+\fnrefx{mod_menu}{defctxmenu} function. This is other ways similar to
+\fnrefx{mod_menu}{defmenu}, but the first argument instead being the name
+of the menu, the name of the region class to define context menu for.
+For example, here's part of the stock \type{WFrame} context menu 
+definition:
+
+\begin{verbatim}
+defctxmenu("WFrame", {
+    menuentry("Close", "WRegion.rqclose_propagate(_, _sub)"),
+    menuentry("Kill",  "WClientWin.kill(_sub)", "_sub:WClientWin"),
+})
+\end{verbatim}
+
+Some of the same ''modes'' as were available for some bindings
+may also be used: \code{WFrame.tiled}, \code{WFrame.floating},
+and \code{WFrame.transient}.
+
+
+\subsection{Displaying menus}
+\label{sec:menudisp}
+
+The following functions may be used to display menus from binding
+handlers (and elsewhere):
+
+\begin{tabularx}{\linewidth}{lX}
+    \tabhead{Function & Description}
+    \fnref{mod_menu.menu} &
+      Keyboard (or mouse) operated menus that open in the bottom-left corner
+      of a screen or frame. \\
+    \fnref{mod_menu.bigmenu} &
+      Same as previous, but uses another graphical style. \\
+    \fnref{mod_menu.pmenu} &
+      Mouse-operated drop-down menus. This function can only be called from a
+      mouse press or drag handler. \\
+    \fnref{mod_menu.grabmenu} &
+      A special version of \fnref{mod_menu.menu} that grabs the keyboard
+      and is scrolled with a given key until all modifiers have been released,
+      after which the selected entry is activated. This function is meant to 
+      be used for implementing, for example, Win***s-style \key{Alt-Tab} 
+      handling.\footnote{See the \file{wcirculate.lua} script in the Ion 
+        scripts repository \url{http://iki.fi/tuomov/repos/ion-scripts-3/}.} \\
+\end{tabularx}
+
+The \fnrefx{mod_menu}{grabmenu} function takes the extra key parameter, but
+aside from that each of these functions takes three arguments, which when
+called from a binding handler, should be the parameters to the handler, and
+the name of the menu. For example, the following snippet of of code binds
+the both ways to open a context menu for a frame:
+
+\begin{verbatim}
+defbindings("WFrame", {
+    kpress(MOD1.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"),
+    mpress("Button3", "mod_menu.pmenu(_, _sub, 'ctxmenu')"),
+})
+\end{verbatim}
diff --git a/doc/conf-winprops.tex b/doc/conf-winprops.tex
new file mode 100644 (file)
index 0000000..10c1c10
--- /dev/null
@@ -0,0 +1,279 @@
+\section{Winprops}
+\label{sec:winprops}
+
+The so-called ''winprops''\index{Winprops} can be used to change how
+specific windows are handled and to set up some kludges to deal with
+badly behaving applications. They are defined by calling the function
+\code{defwinprop} with a table containing the properties to set and the
+necessary information to identify a window. The currently supported
+winprops are listed below, and the subsequent subsections explain the
+usual method of identifying windows, and how to obtain this information.
+
+%\begin{table}
+%\begin{htmlonly}
+%\docode % latex2html kludge
+%\end{htmlonly}
+%\caption{Supported winprops}
+%\label{tab:winprops}
+
+\newenvironment{winprop}[2]{
+  \begin{function}%
+      % Sigh. (La)TeX is a mess.
+      %\index{%
+      %  \ifx\\#1\\%
+      %  #2\else#1\fi%
+      %  @\expandafter\var{#2}}
+      \item[Winprop:] \var{#1} (#2)
+      \item[Description:]
+}
+{
+  \end{function}
+}
+
+
+\begin{winprop}{acrobatic}{boolean}
+    \index{acrobatic@\var{acrobatic}}
+    Set this to \code{true} for Acrobat Reader. It has an annoying
+    habit of trying to manage its dialogs instead of setting them as
+    transients and letting the window manager do its job, causing
+    Ion and acrobat go a window-switching loop when a dialog is
+    opened. 
+\end{winprop}
+
+
+\begin{winprop}{aspect}{table}
+    \index{aspect@\var{aspect}}
+    The table should contain the entries \var{w} and \var{h} that
+    override application-supplied aspect ratio hint.
+\end{winprop}
+
+
+\begin{winprop}{float}{boolean}
+    \index{float@\var{float}}
+    Set this to open the window in a floating frame, when
+    in a group.
+\end{winprop}
+
+
+\begin{winprop}{fullscreen}{boolean}
+    \index{fullscreen@\var{fullscreen}}
+    Should the window be initially in full screen mode?
+\end{winprop}
+
+
+\begin{winprop}{ignore_cfgrq}{boolean}
+    \index{ignore-cfgrq@\var{ignore_cfgrq}}
+    Should configure requests on the window be ignored?
+    Only has effect on floating windows.
+\end{winprop}
+
+
+\begin{winprop}{ignore_net_active_window}{boolean}
+    \index{ignore-net-active-window@\var{ignore_net_active_window}}
+    Ignore extended WM hints \code{_NET_ACTIVE_WINDOW} request. 
+\end{winprop}
+
+
+\begin{winprop}{ignore_resizeinc}{boolean}
+    \index{ignore-resizeinc@\var{ignore_resizeinc}}
+    Should application supplied size increments be ignored?
+\end{winprop}
+
+
+\begin{winprop}{jumpto}{boolean}
+    \index{jumpto@\var{jumpto}}
+    Should a newly created client window always be made
+    active, even if the allocated frame isn't. 
+\end{winprop}
+
+
+\begin{winprop}{max_size}{table}
+    \index{max-size@\var{max_size}}
+    The table should contain the entries \var{w} and \var{h} that
+    override application-supplied maximum size hint. 
+\end{winprop}
+
+
+\begin{winprop}{min_size}{table}
+    \index{min-size@\var{min_size}}
+    Similar to \var{max_size} but for the minimum size hint. 
+\end{winprop}
+
+
+\begin{winprop}{oneshot}{boolean}
+    \index{oneshot@\var{oneshot}}
+    Discard this winprop after first use. 
+\end{winprop}
+
+
+\begin{winprop}{switchto}{boolean}
+    \index{switchto@\var{switchto}}
+    Should a newly mapped client window be switched to within
+    its frame.
+\end{winprop}
+        
+
+\begin{winprop}{target}{string}
+    \index{target@\var{target}}
+    The name of an object (workspace, frame) that should manage 
+    windows of this type. 
+\end{winprop}
+
+        
+\begin{winprop}{transient_mode}{string}
+    \index{transient-mode@\var{transient_mode}}
+    "normal": No change in behaviour. "current": The window
+    should be thought of as a transient for the current active
+    client window (if any) even if it is not marked as a
+    transient by the application. "off": The window should be
+    handled as a normal window even if it is marked as a
+    transient by the application. 
+\end{winprop}
+
+
+\begin{winprop}{transients_at_top}{boolean}
+    \index{transients-at-top@\var{transients_at_top}}
+    When transients are managed by the client window itself (as it
+    is the case on tiled workspaces), should the transients be
+    placed at the top of the window instead of bottom? 
+\end{winprop}
+
+
+\begin{winprop}{transparent}{boolean}
+    \index{transparent@\var{transparent}}
+    Should frames be made transparent when this window is selected? \\
+\end{winprop}
+
+
+
+\subsection{Classes, roles and instances}
+\label{sec:classesrolesinstances}
+
+The identification information in the winprop specification is usually the
+\var{class}\index{class@\var{class}!winprop},
+\var{role}\index{role@\var{role}!winprop},
+\var{instance}\index{instance@\var{instance}!winprop} and
+\var{name}
+of the window. The \var{name} field is a Lua-style regular expression
+matched against the window's title and the rest are strings that must
+exactly match the corresponding window information. It is not necessary
+to specify all of these fields.
+
+Ion looks for a matching winprop in the order listed by the following
+table. An 'E' indicates that the field must be set in the winprop
+and it must match the window's corresponding property exactly or, in
+case of \var{name}, the regular expression must match the window
+title. An asterisk '*' indicates that a winprop where the field is
+not specified (or is itself an asterisk in case of the first three
+fields) is tried.
+
+\begin{center}
+\begin{tabular}{llll}
+  \tabhead{\var{class} & \var{role} & \var{instance} & \var{name}}
+  E           & E          & E              & E \\
+  E           & E          & E              & * \\
+  E           & E          & *              & E \\
+  E           & E          & *              & * \\
+  E           & *          & E              & E \\
+  E           & *          & E              & * \\
+  E           & *          & *              & E \\
+  \vdots       & \vdots     & \vdots         & etc. \\
+\end{tabular}
+\end{center}
+
+If there are multiple winprops with other identification information 
+the same but different \var{name}, the longest match is chosen.
+
+\subsection{Finding window identification}
+
+The 'Window info' context menu entry (\key{Mod1+M} or \key{Button3} on a tab)
+can be used to list the identification information required to set winprops
+for a window and all the transient windows managed within it. 
+
+\index{xprop} 
+Another way to get the identification information is to use \command{xprop}.
+Simply run To get class and instance, simply run \command{xprop WM_CLASS}
+and click on the particular window of interest. The class is the latter of
+the strings while the instance is the former.  To get the role -- few
+windows have this property -- use the command \command{xprop WM_ROLE}. 
+This method, however, will not work on transients. 
+
+\index{transient}
+So-called ''transient windows'' are usually short-lived dialogs (although
+some programs abuse this property) that have a parent window that they are
+''transient for''. On tiled workspaces Ion displays these windows 
+simulatenously with the parent window at the bottom of the same frame.
+Unfortunately \command{xprop} is stupid and can't cope with this situation,
+returning the parent window's properties when the transient is clicked on.
+For this reason you'll have to do a little extra work to get the properties
+for that window.\footnote{There's a patch to \command{xprop} to
+fix this, but nothing seems to be happening with respect to including it in 
+XFree86.}
+
+Finally, it should be mentioned that too many authors these days
+''forget'' to set this vital identification to anything meaningful:
+everything except name is the same for all of the programs's 
+windows, for example.
+
+\subsection{Some common examples}
+
+\subsubsection{Acrobat Reader}
+
+The following is absolutely necessary for Acrobat reader:
+
+\begin{verbatim}
+defwinprop{
+    class = "AcroRead",
+    instance = "documentShell",
+    acrobatic = true,
+}
+\end{verbatim}
+
+\subsubsection{Fixing a Mozilla Firebird transient}
+
+Mozilla Firebird (0.7) incorrectly does not set the \code{WM_TRANSIENT_FOR} 
+property for the dialog that is used to ask the action to take for a file.
+It, however, sets the the property point to the main window for the save
+dialog. This can be annoying and confusing, as the first dialog is not 
+closed before the second is displayed.
+
+We'd like the first dialog to be transient to the main window. The closest
+we can get to that is to consider it transient to the current window (if
+there's one). Unfortunately Firebird does not set any meaningful classes, 
+instances or roles for the windows, so we'll have to rely on an ugly title
+match.
+
+\begin{verbatim}
+defwinprop{
+    class = "MozillaFirebird-bin",
+    name = "Opening .*",
+    transient_mode = "current",
+}
+\end{verbatim}
+
+\subsubsection{Forcing newly created windows in named frames}
+
+The following winprop should place xterm started with command-line parameter
+\mbox{\code{-name sysmon}} and running a system monitoring program in a
+particular frame:
+\begin{verbatim}
+defwinprop{
+    class = "XTerm",
+    instance = "sysmon",
+    target = "sysmonframe",
+}
+\end{verbatim}
+
+For this example to work, we have to somehow create a frame named
+\code{sysmonframe}. One way to do this is to make the following
+call in the \key{Mod1+F3} Lua code query:
+
+\begin{verbatim}
+mod_query.query_renameframe(_)
+\end{verbatim}
+
+Recall that \code{_} points to the multiplexer (frame or screen) in which 
+the query was opened. Running this code should open a new query prefilled
+with the current name of the frame. In our example we would change the 
+name to \code{sysmonframe}, but we could just as well have used the 
+default name formed from the frame's class name and an instance number.
diff --git a/doc/conf.tex b/doc/conf.tex
new file mode 100644 (file)
index 0000000..109093b
--- /dev/null
@@ -0,0 +1,178 @@
+
+\chapter{Basic configuration}
+\label{chap:config}
+
+This chapter should help your configure Ion to your liking. As  the your
+probably already know, Ion uses Lua as a configuration and extension 
+language. If you're new to it, you might first want to read some Lua 
+documentation as already suggested and pointed to in the Introduction
+before continuing with this chapter.
+
+Section \ref{sec:conffiles} is an overview of the multiple configuration
+files Ion uses and as a perhaps more understandable introduction to the
+general layout of the configuration files, a walk-through of the main 
+configuration file \file{ion.lua} is provided in section 
+\ref{sec:walkthrough}.
+How keys and mouse action are bound to functions is described in detail
+in \ref{sec:bindings} and in section \ref{sec:winprops} winprops are
+explained. For a reference on exported functions, see section
+\ref{sec:exports}.
+
+\section{The configuration files}
+\label{sec:conffiles}
+
+Ion3, to which document applies, stores its stock configuration files in
+\file{/usr/local/etc/ion3/} unless you, the OS package maintainer or 
+whoever  installed the package on the system has modified the variables
+\code{PREFIX}\index{PREFIX@\code{PREFIX}} or
+\code{ETCDIR}\index{ETCDIR@\code{ETCDIR}} in
+\file{system.mk}\index{system.mk@\file{system.mk}} before compiling Ion.
+In the first case you probably know where to find the files and in 
+the other case the system administrator or the OS package maintainer
+should  have provided documentation to point to the correct location. 
+If these instructions are no help in locating the correct directory, 
+the command \code{locate cfg_ion.lua} might help provided \code{updatedb} 
+has been run recently. 
+
+User configuration files go in \file{\~{}/.ion3/}. 
+Ion always searches the user configuration file directory before the stock
+configuration file directory for files. Therefore, if you want to change
+some setting, it is advised against that you modify the stock configuration
+files in-place as subsequent installs of Ion will restore the stock
+configuration files. Instead you should always make a copy of the stock
+file in \file{\~{}/.ion3/} and modify this file. When searching
+for a file, if no extension or path component is given, compiled \file{.lc} 
+files are attempted before \file{.lua} files.
+
+All the configuration files are named \file{cfg\_*.lua} with the ''\file{*}''
+part varying. The configuration file for each module \file{mod\_modname} is
+\file{cfg\_modname.lua}, with \file{modname} varying by the module in
+question. The following table summarises these and other configuration
+files:
+
+\begin{tabularx}{\linewidth}{
+      p{\widthof{cfg-bindings.lua}}%
+      X}
+    \hline
+    File & Description \\
+    \hline
+    \file{cfg\_ion.lua} & 
+    The main configuration file \\
+    %
+    \file{cfg\_ioncore.lua} & 
+    Configuration file for Ion's core library.
+    Most of the bindings and menus are configured here. Bindings that are
+    specific to some module are configured in the module's configuration
+    file. For details, see section \ref{sec:bindings}. \\
+    %
+    \file{cfg\_kludges.lua} & 
+    Settings to get some applications behave more nicely have been 
+    collected here. See section \ref{sec:winprops}. \\
+    %
+    \file{cfg\_tiling.lua} 
+    \file{cfg\_query.lua} 
+    \file{cfg\_menu.lua} 
+    \file{cfg\_dock.lua} 
+    \file{cfg\_statusbar.lua} 
+    \dots & Configuration files for different modules. \\
+\end{tabularx}
+
+Additionally, there's the file \file{look.lua} that configures the 
+drawing engine, but it is covered in chapter \ref{chap:gr}.
+
+\section{A walk through \file{cfg\_ion.lua}}
+\label{sec:walkthrough}
+
+As already mentioned \file{cfg\_ion.lua} is Ion's main configuration
+file. Some basic 'feel' settings are usually configured there and
+the necessary modules and other configuration files configuring some 
+more specific aspects of Ion are loaded there. In this section we
+take a walk through the stock \file{cfg\_ion.lua}.
+Notice that most of the settings are commented-out (\code{--} is a 
+line comment in Lua) in the actual file, as they're the defaults
+nevertheless.
+
+The first thing one in the file is to set
+\begin{verbatim}
+META="Mod1+"
+ALTMETA=""
+\end{verbatim}
+These settings cause most of Ion's key bindings to use \key{Mod1} as the
+modifier key. If \code{ALTMETA} is set, it is used as modifier for the keys
+that don't normally use a modifier. for details on modifiers and key 
+binding setup in general see section \ref{sec:bindings}.
+
+Next we do some basic feel configuration:
+
+\begin{verbatim}
+ioncore.set{
+    dblclick_delay=250,
+    kbresize_delay=1500,
+}
+\end{verbatim}
+
+These two will set the delay between button presses in a double click, and
+the timeout to quit resize mode in milliseconds.
+
+\begin{verbatim}
+ioncore.set{
+    opaque_resize=true,
+    warp=true
+}
+\end{verbatim}
+
+The first of these two settings enables opaque resize mode: in move/resize
+move frames and other objects mirror you actions immediately. If opaque
+resize is disabled, a XOR rubber band is shown during the mode instead.
+This will, unfortunately, cause Ion to also grab the X server and has some
+side effects.
+
+Next we load the configuration for Ion's core, and some kludges:
+
+\begin{verbatim}
+dopath("cfg_ioncore")
+dopath("cfg_kludges")
+\end{verbatim}
+
+Most bindings and menus are defined in \file{cfg\_ioncore.lua}.
+Details on making such definitions follow in sections \ref{sec:bindings} 
+and \ref{sec:menus}, respectively. 
+some kludges or ''winprops'' to make some applications behave better
+under Ion are colledted in \file{cfg\_kludges.lua}; see section
+\ref{sec:winprops} for details. In addition to these, this file
+lists quite a few statements of the form
+\begin{verbatim}
+ioncore.defshortening("[^:]+: (.*)(<[0-9]+>)", "$1$2$|$1$<...$2")
+\end{verbatim}
+These are used to configure how Ion attempts to shorten window titles
+when they do not fit in a Tab. The first argument is a POSIX regular
+expression that is used to match against the title and the next is
+a rule to construct a new title of a match occurs. This particular
+rule is used to shorten e.g. 'Foo: barbaz<3>' to 'barba{\ldots}<3>'; for
+details see the function reference entry for \fnref{ioncore.defshortening}.
+
+To actually be able to do something besides display windows in full screen
+mode, we must next load some modules:
+
+\begin{verbatim}
+dopath("cfg_modules")
+--dopath("mod_query")
+--dopath("mod_menu")
+--dopath("mod_tiling")
+--dopath("mod_statusbar")
+--dopath("mod_dock")
+--dopath("mod_sp")
+\end{verbatim}
+
+We actually load there another file listing the default selection of
+modules. If you only want to load additional modules, just uncomment
+the corresponding line. If you want to disable loading some modules,
+comment out the the line loading \file{cfg\_modules}, and uncomment
+the lines for the modules you want, or add more.
+
+
+\input{conf-bindings.tex}
+
+\input{conf-menus.tex}
+
+\input{conf-winprops.tex}
diff --git a/doc/confintro.tex b/doc/confintro.tex
new file mode 100644 (file)
index 0000000..9331614
--- /dev/null
@@ -0,0 +1,52 @@
+
+\chapter{Introduction}
+
+This document is an ''advanced user'' manual for Ion, the X11 window manager,
+and version 3 specifically. It is an attempt attempt at documenting what is 
+in Ion's configuration files, how to configure Ion by simple modifications 
+to these files and how to write more complex extensions in Lua, the 
+lightweight configuration and scripting language used by Ion. 
+
+Readers unfamiliar with Lua is advised to first glance at the Lua manual at 
+
+\centerurl{http://www.lua.org/docs.html}
+
+and perhaps some tutorial pages at the lua-users wiki:
+
+\centerurl{http://lua-users.org/wiki/LuaTutorial}
+
+Back in this document, first in chapter \ref{chap:prelim} some key
+concepts and relations are explained. These include the module system
+and Ion's object and class hierarchies. While it might not at first 
+occur that knowing such things would be necessary to \emph{configure} 
+a program, this  material is essential because of the object-oriented 
+nature of most of Ion's scripting interface.
+
+The new user, fed up with the default key bindings and eager to just
+quickly configure Ion to his liking may question the reasons for 
+exposing the ''heavy'' internal OO structure in the scripting and
+configuration interface. I'm not the one to  blame him for that. 
+Sure it would be faster to configure Ion to everyone's liking
+if a simpler binding configuration interface was provided. Such an
+interface would, however, also be far more limited and make writing
+extensions more complicated and the advantages from using a real 
+scripting language would be partly lost. One more advantage from
+a rich scripting and configuration interface is that it allows 
+implementing scripts to read alternate configuration file formats,
+ones that could be, for example, modified by external configuration tools. 
+
+In chapter \ref{chap:config} the very basic Ion configuration know-how
+is provided. All the different configuration files and their locations
+are explained and instructions are given to allow the reader to
+configure bindings and so-called ''winprops''. Chapter \ref{chap:gr}
+explains the notion of drawing engines and graphical styles and how to
+write new looks for Ion and more advanced aspects of Ion's scripting 
+interface are documented in chapter \ref{chap:tricks} (a work in 
+progress).
+
+Finally, most of the functions provided by Ion's scripting interface
+are listed and documented in the Function reference in chapter
+\ref{sec:exports}. At the end of the document is an alphabetical
+listing of all these functions.
+
+
diff --git a/doc/cstyle.tex b/doc/cstyle.tex
new file mode 100644 (file)
index 0000000..328a7c3
--- /dev/null
@@ -0,0 +1,118 @@
+\section{C coding style}
+
+If you want to submit patches to Ion, you MUST follow my coding 
+style, even if you think it is the root of all evil. We don't want
+the code to be an incomprehensible mess of styles and I have better
+things to do than fix other people's style to match mine. The style
+should be obvious by studying the source, but here's a list of some
+things to take note of.
+
+\subsection{Whitespace}
+
+\begin{itemize}
+  \item Indentations of 4 with \emph{tab size=4}. 
+    
+  \item No extra spaces between operators, delimiters etc. except
+    \begin{itemize}
+      \item around logical and, or (\code{&&}, \code{||})
+      \item around the conditional \code{a ? b : c}
+      \item after commas and semicolons
+    \end{itemize}  
+    In my opinion this helps pointing out arithmetic or other
+    expressions within logical expressions or parameter lists.
+    
+  \item All kinds of labels are out-tended to the level of the higher
+    level block. For example:
+
+\begin{verbatim}    
+void foo()
+{
+again:
+    switch(asdf){
+    case 1:
+        ...
+        break;
+    default:
+        ...
+        break;
+    }
+}
+\end{verbatim}
+\end{itemize}
+
+\subsection{Braces}
+
+\begin{itemize}
+\item Opening brace is at the end of the line, except in function
+    bodies, where it is at the beginning of the line following
+    the definition.
+
+\item Never put the body of a control statement on the same line
+    with the statement (e.g. \verb!if(foo){ bar() }!).
+
+For example, the block
+\begin{verbatim}    
+void foo(int a, int b)
+{
+    if(a==b && c+d==e){
+        ...
+    }
+}
+\end{verbatim}
+
+has correct style while the block
+
+\begin{verbatim}   
+void foo(int a,int b) {
+    if (a == b && c + d == e) {
+        ...
+    }
+}
+\end{verbatim}
+
+does not.
+
+  \item The \code{else} keyword follows immediately after the closing brace of
+    previous \code{if}, if any. (This might change so I don't care if you put
+    it on the next line.)
+    
+  \item I have used the convention that control statement bodies containing
+    a single statement do not need braces around the block if, in case of
+    the \code{if} all the blocks in  \code{if ...  else if ... else}
+    contain just one statement. If you want to, just use braces in every 
+    case.
+\end{itemize}
+
+\subsection{Names}
+
+\begin{itemize}
+  \item Function and variable names only have lower case letters. Type
+    names are in mixed case while constants and macros (\code{#define}s)
+    are in upper case letters.
+\end{itemize}
+
+\subsection{Miscellaneous}
+
+\begin{itemize}
+  \item In the definition of a pointer variable, the asterisk is attached
+    to the variable name: \code{char *s;}. (One could claim this an 
+    exception to the second rule.)
+    
+  \item You might optionally want to use Jed's foldings to group blocks
+    of related code in a file to keep it organized:
+
+\begin{verbatim}    
+/*{{{ Many related functions */
+       
+void code()
+{
+    ...        
+}
+
+...
+
+/*}}}*/
+\end{verbatim}
+\end{itemize}
+
+I think that's mostly it. Study the source when in doubt.
diff --git a/doc/de.tex b/doc/de.tex
new file mode 100644 (file)
index 0000000..6c8c2b4
--- /dev/null
@@ -0,0 +1,388 @@
+
+\chapter{Graphical styles}
+\label{chap:gr}
+
+This chapter first gives in section \ref{sec:engines} a general outline 
+of how drawing engines are used, of style specifications and then
+in section \ref{sec:defaultde} describes how to specify styles
+for the default drawing engine.
+
+\section{Drawing engines, style specifications and sub-styles}
+\label{sec:engines}
+\index{style}\index{drawing engine}
+
+Ion's drawing routines are abstracted into so-called drawing engine
+modules that can, again depending on the system, be dynamically
+loaded as needed. The drawing engine modules provide ''brushes''
+that objects can use to draw some high-level primitives such
+as borders and text boxes (in addition to simple text and rectangle
+drawing) on their windows and configure e.g. the shape and 
+background of the window. While the drawing engines therefore
+do not directly implement looks for each possible object (that
+would hardly be maintainable), different brush styles can be
+used to give a distinctive look to different objects and engines
+could interpret some styles as special cases. Style specifications 
+are strings of the form
+
+\begin{verbatim}
+element1-element2-...-elementn
+\end{verbatim}
+
+An example of such a style specification is \code{tab-frame};
+see the table in subsection \ref{sec:styles} for more styles.
+
+When an object asks for a brush of certain style, the selected
+drawing engine will attempt to find the closest match to this
+specification. The styles/brushes defined by the drawing engines 
+may have asterisks (\verb!*!) as some of the elements indicating
+a match to anything. Exact matches are preferred to asterisk
+matches and longer matches to shorter. For example, let a brush
+for style \code{foo-bar-baz} be queried, then the following
+brushes are in order of preference:
+
+\begin{verbatim}
+foo-bar-baz
+foo-*-baz
+foo-bar
+*
+foo-baz   -- Doesn't match, not selected!
+\end{verbatim}
+
+Some of the drawing primitives allow extra attributes to be
+specified, also in the form
+\begin{verbatim}
+attr1-attr2-...-attrn
+\end{verbatim}
+These extra attributes are called \emph{substyles}\index{substyle}
+and allow, for example, the state of the object to be indicated 
+by  different colour sets while keeping the interface at an 
+abstract level and the drawing engine completely ignorant 
+of the semantics -- only the writer of the drawing engine
+configuration file has to know them. However the drawing 
+engine can again interpret known substyles as special cases
+and the default engine indeed does so with frame tab 
+tag and drag states.)
+
+
+\subsection{Known styles and substyles}
+\label{sec:styles}
+
+\subsubsection{Frames}
+
+\begin{tabularx}{\linewidth}{lX}
+\tabhead{Style name & Description}
+\code{frame} & Style for frames. 
+       Substyles: \code{active}, \code{inactive}. \\
+\code{frame-tiled} & A more specific style for tiled frames.
+        Substyles as for \code{frame}. \\
+\code{frame-tiled-alt} & An alternative style for tiled frames.
+        Often used to disable the tab-bar. \\
+\code{frame-floating} & A more specific style for floating
+        frames. \\
+\code{frame-transient} & A more specific style for frames
+        containing transient windows. \\
+\end{tabularx}
+
+\subsubsection{Tabs and menu entries}
+
+\begin{tabularx}{\linewidth}{lX}
+\tabhead{Style name & Description}
+\code{tab} & Style for frames' tabs and menu entries. 
+       Substyles: combinations of the form \code{a-s} where
+       \code{a} is one of \code{active}\nobreak/\code{inactive} and
+       \code{s} is one of \code{selected}\nobreak/\code{unselected} \\
+\code{tab-frame} & A more specific style for frames' tabs.
+       Substyles: combinations of the form \code{a-s-t-d-u} where
+       \code{a} and \code{s} are as above and
+       \code{t} is one of \code{tagged}\nobreak/\code{not_tagged},
+       \code{d} is one of \code{dragged}\nobreak/\code{not_dragged} and
+       \code{u} is one of \code{activity}\nobreak/\code{no_activity}. \\
+\code{tab-frame-tiled}, & \\
+\code{tab-frame-tiled-alt}, & \\
+\code{tab-frame-floating}, & \\
+\code{tab-frame-transient} & More specific styles for frames in the
+        different modes. \\
+\code{tab-menuentry} & A more specific style for entries in \type{WMenu}s. \\
+\code{tab-menuentry-bigmenu} & An alternate style for entries in \type{WMenu}s. \\
+\end{tabularx}
+
+\subsubsection{The rest}
+
+\begin{tabularx}{\linewidth}{lX}
+\tabhead{Style name & Description}
+\code{input} & A style for \type{WInput}s. \\
+\code{input-edln} & A more specific style for \type{WEdln}s. 
+       Substyles: \code{selection} for selected text and
+       \code{cursor} for the cursor indicating current editing point. \\
+\code{input-message} & A more specific style for \type{WMessage}s. \\
+\code{input-menu} & A more specific style for \type{WMenu}s. \\
+\code{input-menu-bigmenu} & An alternate style for \type{WMenu}s. \\
+\code{moveres_display} & The box displaying position/size when
+       moving or resizing frames. \\
+\code{dock} & The dock. \\     
+\end{tabularx}
+
+
+\section{Defining styles for the default drawing engine}
+\label{sec:defaultde}
+
+Drawing engine style files are usually named
+\file{look\_foo.lua} where \file{foo} is the name of the
+style. The file that Ion loads on startup or when
+\fnref{gr.read_config} is called, however, is \file{look.lua}
+and should usually be symlinked to or a copy of of some
+\file{look\_foo.lua}.
+
+\subsection{The structure of the configuration files}
+
+The first thing to do in a stylefile is to choose the drawing
+engine, possibly loading the module as well. This is done
+with the following chunk of code.
+
+\begin{verbatim}
+if not gr.select_engine("de") then 
+    return 
+end
+\end{verbatim}
+
+The \fnref{gr.select_engine} function sees if the engine
+given as argument is registered (the default drawing engine is
+simply called ''de''). If the engine could not be found, it
+tries to load a module of the same name. If the engine still
+is not registered, \fnref{gr.select_engine} returns \code{false}
+and in this case we also exit the style setup script.
+If the engine was found, \fnref{gr.select_engine} sees that
+further requests for brushes are forwarded to that engine
+and returns \code{true}.
+
+Before defining new styles it may be a good idea to clear old
+styles from memory so if the old configuration defines more
+specific styles than the new, the old styles don't override 
+those specified by the new configuration. That can be done by
+calling
+
+\begin{verbatim}
+de.reset()
+\end{verbatim}
+
+After this the new styles can be defined with \fnref{de.defstyle}
+as explained in the next subsection. Finally, after the styles have
+been defined we must ask objects on the screen to look up new brushes
+to reflect the changes in configuration. This is done with
+
+\begin{verbatim}
+gr.refresh()
+\end{verbatim}
+
+\subsection{Defining the styles}
+
+Styles for the default drawing engine are defined with the
+function \fnref{de.defstyle}. It has two arguments the first being
+a style specification as explained in previous sections and the second
+a table whose fields describe the style:
+
+\begin{verbatim}
+de.defstyle("some-style", {
+    attribute = value,
+    ...
+})
+\end{verbatim}
+
+The supported attributes are described in tables below. The different
+border elements and styles referred to there are explained in Figure
+\ref{fig:borders}.
+
+\begin{figure}
+\begin{htmlonly}
+\docode % Kludge to make latex2html interpret contents instead of
+       % creating an image.
+\end{htmlonly}    
+\begin{verbatim}
+Elevated:        Inlaid:         Ridge:            Groove:        
+  hhhhhhhhhhhs     ............           hhhhhhhhhhhs      sssssssssssh
+  h..........s     .sssssssssh.           h..........s      s..........h
+  h.        .s     .s        h.           h.sssssssh.s      s.hhhhhhhs.h
+  h.        .s     .s        h.           h.s      h.s      s.h      s.h
+  h.        .s     .s        h.           h.shhhhhhh.s      s.hsssssss.h
+  h..........s     .shhhhhhhhh.           h..........s      s..........h
+  hsssssssssss     ............           hsssssssssss      shhhhhhhhhhh
+
+h = highlight, s = shadow, . = padding
+\end{verbatim}
+\caption{Sketch of different border styles and elements}
+\label{fig:borders}
+\end{figure}
+
+\subsubsection{Colours}
+
+Each of these fields a string of the form that can be
+passed to \code{XAllocNamedColor}. Valid strings are e.g.
+hexadecimal RGB specifications of the form
+\code{#RRGGBB} and colour names as specified
+in \file{/usr/X11R6/lib/X11/rgb.txt} (exact path varying).
+
+\begin{tabularx}{\linewidth}{lX}
+\tabhead{Field  & Description}
+\var{highlight_colour} &  
+       Colour for the ''highlight'' part of a border. \\
+\var{shadow_colour}    &  
+       Colour for the ''highlight'' part of a border. \\
+\var{foreground_colour} &  
+       Colour for the normal drawing operations, e.g. text. \\
+\var{background_colour} &  
+       Window background colour (unless transparency is enabled) and
+       background colour boxes. \\
+\var{padding_colour} &  
+       Colour for the ''padding'' part of a border border. Set to 
+       \var{background_colour} if unset. \\
+\end{tabularx}
+
+
+\subsubsection{Borders and widths}
+
+All other fields below except \var{border_style} are non-negative integers
+indicating a number of pixels.
+
+\begin{tabularx}{\linewidth}{lX}
+\tabhead{Field  & Description}
+\var{border_style} & A string indicating the style of border; one of
+                    elevated/inlaid/ridge/groove as seen in the
+                    above sketch. \\
+\var{highlight_pixels} &  
+       Width of the highlight part of the border in pixels. \\
+\var{shadow_pixels}    &  
+       Width of the shadow part of the border in pixels. \\
+\var{padding_pixels} &  
+       Width of the padding part of the border in pixels. \\
+\var{spacing} &
+       Space to be left between all kinds of boxes. \\
+\end{tabularx}
+
+
+\subsubsection{Text}
+
+\begin{tabularx}{\linewidth}{lX}
+\tabhead{Field  & Description}
+\var{font} & Font to be used in text-drawing operations; standard X font
+            name. \\
+\var{text_align} & How text is to be aligned in text boxes/tabs; one of
+                  the strings left\nobreak/right\nobreak/center. \\
+\end{tabularx}
+
+
+\subsubsection{Miscellaneous}
+
+
+\begin{tabularx}{\linewidth}{lX}
+\tabhead{Field  & Description}
+\var{transparent_background} & Should windows' that use this style
+       background be transparent? true/false. \\
+\var{based_on} & The name of a previously defined style that this
+       style should be based on. \\
+\end{tabularx}
+
+
+\subsubsection{Substyles}
+
+As discussed in previous sections, styles may have substyles to e.g.
+indicate different states of the object being drawn. The ''de'' engine
+limits what can be configured in substyles to the set of colours in the
+first table above, but also specifically interprets for the main style
+\code{tab-frame} the substyles \code{*-*-tagged} and \code{*-*-*-dragged}
+by, respectively, drawing a right angle shape at the top right corner 
+of a tab and by shading the tab with a stipple pattern. Also for
+menus the substyles \code{*-*-submenu} are handled as a special case.
+
+Substyles are defined with the function \fnref{de.substyle} within the
+table defining the main style. The parameters to this function are 
+similar to those of \fnref{de.defstyle}.
+
+\begin{verbatim}
+de.defstyle("some-style", {
+   ...
+   de.substyle("some-substyle", {
+      ...
+   }),
+   ...
+})
+\end{verbatim}
+
+
+\subsection{An example}
+
+The following shortened segment from \file{look\_cleanviolet.lua}
+should help to clarify the matters discussed in the previous
+subsection.
+
+\begin{verbatim}
+de.defstyle("*", {
+    -- Gray background
+    highlight_colour = "#eeeeee",
+    shadow_colour = "#eeeeee",
+    background_colour = "#aaaaaa",
+    foreground_colour = "#000000",
+    
+    shadow_pixels = 1,
+    highlight_pixels = 1,
+    padding_pixels = 1,
+    spacing = 0,
+    border_style = "elevated",
+    
+    font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*",
+    text_align = "center",
+})
+
+de.defstyle("tab-frame", {
+    based_on = "*",
+    
+    de.substyle("active-selected", {
+        -- Violet tab
+        highlight_colour = "#aaaacc",
+        shadow_colour = "#aaaacc",
+        background_colour = "#666699",
+        foreground_colour = "#eeeeee",
+    }),
+
+    --  More substyles would follow ...
+})
+\end{verbatim}
+
+
+\section{Miscellaneous settings}
+
+The following style fields are independent of the drawing engine used,
+but are related to objects' styles and therefore configured in the drawing
+engine configuration file.
+
+\subsection{Extra fields for style \code{frame}}
+
+\begin{tabularx}{\linewidth}{lX}
+\tabhead{Field  & Description}
+\code{bar} & Controls the style of the tab-bar. Possible values
+        are the strings \code{"none"}, \code{"inside"}, \code{"outside"}
+        and \code{"shaped"}, with the last providing the PWM-style
+        tab-bars for floating frames. \\
+\code{floatframe_tab_min_w} & Minimum tab width in pixels for
+        the shaped style, given that this number times number of tabs
+        doesn't exceed frame width. \\
+\code{floatframe_bar_max_w_q} & Maximum tab-bar width quotient of
+       frame width for the shaped styles. A number in the 
+       interval $(0, 1]$.
+\end{tabularx}
+
+\subsection{Extra fields for style \code{dock}}
+
+\begin{tabularx}{\linewidth}{lX}
+\tabhead{Field  & Description}
+\code{outline_style} & How borders are drawn:
+                       \code{"none"} -- no border,
+                       \code{"all"} -- border around whole dock,
+                       \code{"each"} -- border around each dockapp. \\
+\code{tile_size} & A table with entries \code{width} and \code{height},
+       indicating the width and height of tiles in pixels.
+\end{tabularx}
+
+
+Hopefully that's enough to get you started in writing new style
+configuration files for Ion. When in doubt, study the existing
+style configuration files.
diff --git a/doc/designnotes.tex b/doc/designnotes.tex
new file mode 100644 (file)
index 0000000..4bb6485
--- /dev/null
@@ -0,0 +1,54 @@
+\section{Miscellaneous design notes}
+
+\subsection{Destroying \type{WObj}:s}
+
+To keep Ion's code as simple as possible yet safe, there are restrictions
+when the \type{WObj}
+\code{destroy_obj}\index{destroy-obj@\code{destroy_obj}}
+function that calls watches, the deinit routine and frees memory may
+be called directly. In all other cases the 
+\code{defer_destroy}\index{defer-destroy@\code{defer_destroy}}
+function should be used to defer the call of \code{destroy_obj} until
+Ioncore returns to its main event loop. 
+
+Calling the \code{destroy_obj} function directly is allowed in the
+following cases:
+\begin{itemize}
+    \item In the deinit handler for another object. Usually managed objects
+      are destroyed this way.
+    \item The object was created during the current call to the function
+      that wants to get rid of the object. This is the case, for example,
+      when the function created a frame to manage some other object but for
+      some reason failed to reparent the object to this frame.
+    \item In a deferred action handler set with
+      \code{defer_action}\index{defer-action@\code{defer_action}}.
+      Like deferred destroys, other deferred actions are called when
+      Ioncore has returned to the main loop.
+    \item You are absolute sure that C code outside your code has no
+      references to the object.
+\end{itemize}
+
+If there are no serious side effects from deferring destroying the
+object or you're unsure whether it is safe to destroy the object
+immediately, use \code{defer_destroy}.
+
+\subsection{The types \code{char*} and \code{const char*} as function
+  parameters and return values}
+
+The following rules should apply to using strings as return values and
+parameters to functions. 
+
+\begin{tabularx}{\linewidth}{lXX}
+    \tabhead{Type & Return value & Parameter}
+    \code{const char*} & The string is owned by the called function
+       and the caller is only quaranteed short-term read access to the
+       string. &
+       The called function may only read the string during its execution.
+       For further reference a copy must be made. \\
+    \code{char*} & The string is the caller's responsibility and it
+       \emph{must} free it when no longer needed. &
+       The called function may modify the string but the ''owner'' of
+       the string is case-dependant. \\
+\end{tabularx}
+
+
diff --git a/doc/exact-version b/doc/exact-version
new file mode 100644 (file)
index 0000000..0413a6b
--- /dev/null
@@ -0,0 +1,5 @@
+
+Context:
+
+[TAG ion-doc-3ds-20061223
+Tuomo Valkonen <tuomov@iki.fi>**20061223150012] 
diff --git a/doc/fnref.tex b/doc/fnref.tex
new file mode 100644 (file)
index 0000000..c270a5a
--- /dev/null
@@ -0,0 +1,38 @@
+\chapter{Function reference}
+\label{sec:exports}
+
+\section{Functions defined in \file{ioncore}}
+\label{sec:ioncoreref}
+\input{ioncore-fns.tex}
+
+\section{Functions defined in \file{mod\_tiling}}
+\label{sec:tilingref}
+\input{mod_tiling-fns.tex}
+
+%\section{Functions defined in \file{mod\_panews}}
+%\label{sec:panewsref}
+%\input{mod_panews-fns.tex}
+
+\section{Functions defined in \file{mod\_query}}
+\label{sec:queryref}
+\input{mod_query-fns.tex}
+
+\section{Functions defined in \file{mod\_menu}}
+\label{sec:menuref}
+\input{mod_menu-fns.tex}
+
+\section{Functions defined in \file{mod\_dock}}
+\label{sec:dockref}
+\input{mod_dock-fns.tex}
+
+\section{Functions defined in \file{mod\_sp}}
+\label{sec:spref}
+\input{mod_sp-fns.tex}
+
+\section{Functions defined in \file{de}}
+\label{sec:deref}
+\input{de-fns.tex}
+
+\section{Hooks}
+\label{sec:hookref}
+\input{hookref}
diff --git a/doc/fullhierarchy.tex b/doc/fullhierarchy.tex
new file mode 100644 (file)
index 0000000..d5d5a39
--- /dev/null
@@ -0,0 +1,43 @@
+\chapter{Full class hierarchy visible to Lua-side}
+\label{app:fullhierarchy}
+  
+%\begin{figure}
+%\begin{htmlonly}
+%\docode % latex2html kludge
+%\end{htmlonly}
+{\small
+\begin{verbatim}
+    Obj
+     |-->WHook
+     |-->WTimer
+     |-->WMoveresMode
+     |-->WMgmtMode (mod_mgmtmode)
+     |-->WRegion
+     |    |-->WClientWin
+     |    |-->WWindow
+     |    |    |-->WRootWin
+     |    |    |-->WMPlex
+     |    |    |    |-->WScreen
+     |    |    |    |-->WFrame
+     |    |    |-->WInfoWin
+     |    |    |    |-->WStatusBar (mod_statusbar)
+     |    |    |-->WMenu (mod_menu)
+     |    |    |-->WInput (mod_query)
+     |    |         |-->WEdln (mod_query)
+     |    |         |-->WMessage (mod_query)
+     |    |-->WGroup
+     |    |    |-->WGroupWS
+     |    |    |-->WGroupCW
+     |    |-->WTiling (mod_tiling)
+     |-->WSplit (mod_tiling)
+          |-->WSplitInner (mod_tiling)
+          |    |-->WSplitSplit (mod_tiling)
+          |         |-->WSplitFloat (mod_tiling)
+          |-->WSplitRegion (mod_tiling)
+               |-->WSplitST (mod_tiling)
+\end{verbatim}
+}
+%\caption{Full Ioncore and most common modules' class hierarchy
+%  visible to Lua side.}
+%\label{fig:fullclasshierarchy}
+%\end{figure}
diff --git a/doc/gpl.tex b/doc/gpl.tex
new file mode 100644 (file)
index 0000000..d4de1de
--- /dev/null
@@ -0,0 +1,416 @@
+\xchapter{The GNU General Public License}
+
+\begin{center}
+{\parindent 0in
+
+Version 2, June 1991
+
+Copyright \copyright\ 1989, 1991 Free Software Foundation, Inc.
+
+\bigskip
+
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+\bigskip
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+}
+\end{center}
+
+\begin{center}
+{\bf\large Preamble}
+\end{center}
+
+
+The licenses for most software are designed to take away your freedom to
+share and change it.  By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software---to
+make sure the software is free for all its users.  This General Public
+License applies to most of the Free Software Foundation's software and to
+any other program whose authors commit to using it.  (Some other Free
+Software Foundation software is covered by the GNU Library General Public
+License instead.)  You can apply it to your programs, 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 software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or
+for a fee, you must give the recipients all the rights that you have.  You
+must make sure that they, too, receive or can get the source code.  And
+you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software.  If
+the software is modified by someone else and passed on, we want its
+recipients to know that what they have is not the original, so that any
+problems introduced by others will not reflect on the original authors'
+reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary.  To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+\begin{center}
+{\Large \sc Terms and Conditions For Copying, Distribution and
+  Modification}
+\end{center}
+
+
+%\renewcommand{\theenumi}{\alpha{enumi}}
+\begin{enumerate}
+
+\addtocounter{enumi}{-1}
+
+\item 
+
+This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the
+terms of this General Public License.  The ``Program'', below, refers to
+any such program or work, and a ``work based on the Program'' means either
+the Program or any derivative work under copyright law: that is to say, a
+work containing the Program or a portion of it, either verbatim or with
+modifications and/or translated into another language.  (Hereinafter,
+translation is included without limitation in the term ``modification''.)
+Each licensee is addressed as ``you''.
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+\item You may copy and distribute verbatim copies of the Program's 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 give any other
+  recipients of the Program a copy of this License along with the Program.
+
+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.
+
+\item
+
+You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+\begin{enumerate}
+
+\item 
+
+You must cause the modified files to carry prominent notices stating that
+you changed the files and the date of any change.
+
+\item
+
+You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+\item
+If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License.  (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+\end{enumerate}
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+\item
+You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+\begin{enumerate}
+
+\item
+
+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; or,
+
+\item
+
+Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+\item
+
+Accompany it with the information you received as to the offer
+to distribute corresponding source code.  (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+\end{enumerate}
+
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, 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 executable.  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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+\item
+You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+\item
+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 Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+\item
+Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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.
+
+\item
+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 Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+\item
+If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+\item
+The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+\item
+If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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.
+
+\begin{center}
+{\Large\sc
+No Warranty
+}
+\end{center}
+
+\item
+{\sc Because the program is licensed free of charge, there is no warranty
+for the program, to the extent permitted by applicable law.  Except when
+otherwise stated in writing the copyright holders and/or other parties
+provide the program ``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 program is with you.  Should the
+program prove defective, you assume the cost of all necessary servicing,
+repair or correction.}
+
+\item
+{\sc 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 program 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 program (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 program to operate with any other
+programs), even if such holder or other party has been advised of the
+possibility of such damages.}
+
+\end{enumerate}
+
+
+\begin{center}
+{\Large\sc End of Terms and Conditions}
+\end{center}
+
+
+\pagebreak[2]
+
+\xsectionstar{Appendix: How to Apply These Terms to Your New Programs}
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+  To do so, attach the following notices to the program.  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.
+
+\begin{quote}
+one line to give the program's name and a brief idea of what it does. \\
+Copyright (C) yyyy  name of author \\
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+\end{quote}
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+\begin{quote}
+Gnomovision version 69, Copyright (C) yyyy  name of author \\
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+\end{quote}
+
+
+The hypothetical commands {\tt show w} and {\tt show c} should show the
+appropriate parts of the General Public License.  Of course, the commands
+you use may be called something other than {\tt show w} and {\tt show c};
+they could even be mouse-clicks or menu items---whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a ``copyright disclaimer'' for the program, if
+necessary.  Here is a sample; alter the names:
+
+\begin{quote}
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\
+`Gnomovision' (which makes passes at compilers) written by James Hacker. \\
+
+signature of Ty Coon, 1 April 1989 \\
+Ty Coon, President of Vice
+\end{quote}
+
+
+This General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications
+with the library.  If this is what you want to do, use the GNU Library
+General Public License instead of this License.
diff --git a/doc/hookref.tex b/doc/hookref.tex
new file mode 100644 (file)
index 0000000..f4f2a9e
--- /dev/null
@@ -0,0 +1,241 @@
+
+\begin{function}
+    \index{clientwin-do-manage-alt@\code{clientwin_do_manage_alt}}
+    \hookname{clientwin_do_manage_alt}
+    \hookparams{(WClientWin, table)}
+    \begin{funcdesc}
+      Called when we want to manage a new client window.
+      The table argument contains the following fields:
+      
+      \begin{tabularx}{\linewidth}{llX}
+          \tabhead{Field & Type & Description}
+          \var{switchto} & bool & Do we want to switch to the client window. \\
+          \var{jumpto} & bool & Do we want to jump to the client window. \\
+          \var{userpos} & bool & Geometry set by user. \\
+          \var{dockapp} & bool & Client window is a dockapp. \\
+          \var{maprq} & bool & Map request (and not initialisation scan). \\
+          \var{gravity} & number & Window gravity. \\
+          \var{geom} & table & Requested geometry; \var{x}, \var{y}, \var{w}, \var{h}.\\
+          \var{tfor} & WClientWin & Transient for window.
+      \end{tabularx}
+
+      This hook is not called in protected mode and can be used for
+      arbitrary placement policies (deciding in which workspace a new
+      \type{WClientWin} should go). In this case, you can call
+\begin{verbatim}
+reg:attach(cwin)
+\end{verbatim}
+      where \var{reg} is the region where the window should go, and
+      \var{cwin} is the first argument of the function added to the
+      hook.
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{clientwin-mapped-hook@\code{clientwin_mapped_hook}}
+    \hookname{clientwin_mapped_hook}
+    \hookparams{WClientWin}
+    \begin{funcdesc}
+      Called when we have started to manage a client window.
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{clientwin-unmapped-hook@\code{clientwin_unmapped_hook}}
+    \hookname{clientwin_unmapped_hook}
+    \hookparams{number}
+    \begin{funcdesc}
+      Called when we no longer manage a client window. The parameter
+      is the X ID of the window; see \fnref{WClientWin.xid}.
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{frame-managed-changed-hook@\code{frame_managed_changed_hook}}
+    \hookname{frame_managed_changed_hook}
+    \hookparams{table}
+    \begin{funcdesc}
+      Called when there are changes in the objects managed by a frame
+      or their order. The table parameter has the following fields:
+
+      \begin{tabularx}{\linewidth}{llX}
+          \tabhead{Field & Type & Description}
+          \var{reg} & WFrame & The frame in question \\
+          \var{mode} & string & \var{"switchonly"}, \var{"reorder"},
+                                \var{"add"} or \var{"remove"} \\
+          \var{sw} & bool & Switch occured \\
+          \var{sub} & WRegion & The managed region (primarily) affected \\
+      \end{tabularx}
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{ioncore-sigchld-hook@\code{ioncore_sigchld_hook}}
+    \hookname{ioncore_sigchld_hook}
+    \hookparams{integer}
+    \begin{funcdesc}
+      Called when a child process has exited. The parameter
+      is the PID of the process.
+    \end{funcdesc}
+\end{function}
+
+\begin{function}
+    \index{ioncore-deinit-hook@\code{ioncore_deinit_hook}}
+    \hookname{ioncore_deinit_hook}
+    \hookparams{()}
+    \begin{funcdesc}
+      Called when Ion is deinitialising and about to quit.
+    \end{funcdesc}
+\end{function}
+
+%ioncore_handle_event_alt -- not available to lua side
+
+\begin{function}
+    \index{ioncore-post-layout-setup-hook@\code{ioncore_post_layout_setup_hook}}
+    \hookname{ioncore_post_layout_setup_hook}
+    \hookparams{()}
+    \begin{funcdesc}
+      Called when Ion has done all initialisation and is almost ready to
+      enter the mainloop, except no windows are yet being managed.
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{ioncore-snapshot-hook@\code{ioncore_snapshot_hook}}
+    \hookname{ioncore_snapshot_hook}
+    \hookparams{()}
+    \begin{funcdesc}
+      Called to signal scripts and modules to save their state (if any).
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{tiling-placement-alt@\code{tiling_placement_alt}}
+    \hookname{tiling_placement_alt}
+    \hookparams{table}
+    \begin{funcdesc}
+      Called when a client window is about to be managed by a \type{WTiling}
+      to allow for alternative placement policies. The table has the
+      following fields:
+      \begin{tabularx}{\linewidth}{llX}
+          \tabhead{Field & Type & Description}
+          \var{tiling} & \type{WTiling} & The tiling \\
+          \var{reg} & \type{WRegion} & The region (always a WClientWin at 
+              the moment) to be placed \\
+          \var{mp} & \type{table} & This table contains the same fields as
+            the parameter of \fnref{clientwin_do_manage_alt} \\
+          \var{res_frame} & \type{WFrame} & A succesfull handler should 
+            return the target frame here. \\
+      \end{tabularx}
+      This hook is just for placing within a given workspace after the
+      workspace has been decided by the default workspace selection
+      policy. It is called in protected mode. For arbitrary placement
+      policies, \fnref{clientwin_do_manage_alt} should be used; it
+      isn't called in protected mode,
+    \end{funcdesc}
+\end{function}
+
+
+%\begin{function}
+%    \index{panews-init-layout-alt@\code{panews_init_layout_alt}}
+%    \hookname{panews_init_layout_alt}
+%    \hookparams{table}
+%    \begin{funcdesc}
+%      Called to initialise panews layout. The table parameter has
+%      initially a single field \var{ws} pointing to the workspace,
+%      but the succesfull handler should set the field \var{layout}
+%      to a proper layout (as those saved in the layout savefiles).
+%    \end{funcdesc}
+%\end{function}
+
+
+\begin{function}
+    \index{panews-make-placement-alt@\code{panews_make_placement_alt}}
+    \hookname{panews_make_placement_alt}
+    \hookparams{table}
+    \begin{funcdesc}
+      Called to make a placement on panews. The parameter table has
+      the following fields:
+      
+      \begin{tabularx}{\linewidth}{llX}
+          \tabhead{Field & Type & Description}
+          \var{ws} & \type{WPaneWS} & The workspace \\
+          \var{frame} & \type{WFrame} & A frame initially allocated for the
+              region to be placed \\
+          \var{reg} & \type{WRegion} & The region to be placed \\
+          \var{specifier} & \type{WRegion} & For drag\&drop on handling empty areas\\
+      \end{tabularx}
+      
+      The handler should set some of these fields on success:
+
+      \begin{tabularx}{\linewidth}{llX}
+          \tabhead{Field & Type & Description}
+          \var{res_node} & \type{WSplit} & Target split \\
+          \var{res_config} & \type{WFrame} &  New configuration for it, unless
+              \type{WSplitRegion} \\
+          \var{res_w} & integer & New width for target split (optional) \\
+          \var{res_h} & integer & New height for target split (optional) \\
+      \end{tabularx}
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{region-activated-hook@\code{region_activated_hook}}
+    \hookname{region_activated_hook}
+    \hookparams{WRegion}
+    \begin{funcdesc}
+      Signalled when a region or one of its children has received the focus.
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{region-activity-hook@\code{region_activity_hook}}
+    \hookname{region_activity_hook}
+    \hookparams{WRegion}
+    \begin{funcdesc}
+      This hook is triggered when the activity flag of the parameter 
+      region has been changed.
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{region-do-warp-alt@\code{region_do_warp_alt}}
+    \hookname{region_do_warp_alt}
+    \hookparams{WRegion}
+    \begin{funcdesc}
+      This alt-hook exist to allow for alternative pointer warping
+      implementations.
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{region-inactivated-hook@\code{region_inactivated_hook}}
+    \hookname{region_inactivated_hook}
+    \hookparams{WRegion}
+    \begin{funcdesc}
+      Signalled when the focus has moved from the parameter region or
+      one of its children to a non-child region of the parameter region.
+    \end{funcdesc}
+\end{function}
+
+
+\begin{function}
+    \index{screen-managed-changed-hook@\code{screen_managed_changed_hook}}
+    \hookname{screen_managed_changed_hook}
+    \hookparams{table}
+    \begin{funcdesc}
+      Called when there are changes in the objects managed by a screen
+      or their order. The table parameter is similar to that of
+      \fnref{frame_managed_changed_hook}.
+    \end{funcdesc}
+\end{function}
diff --git a/doc/ionconf.dvi.gz b/doc/ionconf.dvi.gz
new file mode 100644 (file)
index 0000000..905ef66
Binary files /dev/null and b/doc/ionconf.dvi.gz differ
diff --git a/doc/ionconf.out b/doc/ionconf.out
new file mode 100644 (file)
index 0000000..060a02d
--- /dev/null
@@ -0,0 +1,82 @@
+\BOOKMARK [0][-]{chapter.1}{ Introduction}{}
+\BOOKMARK [0][-]{chapter.2}{ Preliminaries: Key concepts and relations}{}
+\BOOKMARK [1][-]{section.2.1}{ Modules}{chapter.2}
+\BOOKMARK [1][-]{section.2.2}{ Class and object hierarchies}{chapter.2}
+\BOOKMARK [2][-]{subsection.2.2.1}{ Class hierarchy}{section.2.2}
+\BOOKMARK [2][-]{subsection.2.2.2}{ Object hierarchies: WRegion parents and managers}{section.2.2}
+\BOOKMARK [2][-]{subsection.2.2.3}{ Summary}{section.2.2}
+\BOOKMARK [0][-]{chapter.3}{ Basic configuration}{}
+\BOOKMARK [1][-]{section.3.1}{ The configuration files}{chapter.3}
+\BOOKMARK [1][-]{section.3.2}{ A walk through cfg\137ion.lua}{chapter.3}
+\BOOKMARK [1][-]{section.3.3}{ Keys and rodents}{chapter.3}
+\BOOKMARK [2][-]{subsection.3.3.1}{ Binding handlers and special variables}{section.3.3}
+\BOOKMARK [2][-]{subsection.3.3.2}{ Guards}{section.3.3}
+\BOOKMARK [2][-]{subsection.3.3.3}{ Defining the bindings}{section.3.3}
+\BOOKMARK [2][-]{subsection.3.3.4}{ Examples}{section.3.3}
+\BOOKMARK [2][-]{subsection.3.3.5}{ Key specifications}{section.3.3}
+\BOOKMARK [2][-]{subsection.3.3.6}{ Button specifications}{section.3.3}
+\BOOKMARK [2][-]{subsection.3.3.7}{ A further note on the default binding configuration}{section.3.3}
+\BOOKMARK [1][-]{section.3.4}{ Menus}{chapter.3}
+\BOOKMARK [2][-]{subsection.3.4.1}{ Defining menus}{section.3.4}
+\BOOKMARK [2][-]{subsection.3.4.2}{ Special menus}{section.3.4}
+\BOOKMARK [2][-]{subsection.3.4.3}{ Defining context menus}{section.3.4}
+\BOOKMARK [2][-]{subsection.3.4.4}{ Displaying menus}{section.3.4}
+\BOOKMARK [1][-]{section.3.5}{ Winprops}{chapter.3}
+\BOOKMARK [2][-]{subsection.3.5.1}{ Classes, roles and instances}{section.3.5}
+\BOOKMARK [2][-]{subsection.3.5.2}{ Finding window identification}{section.3.5}
+\BOOKMARK [2][-]{subsection.3.5.3}{ Some common examples}{section.3.5}
+\BOOKMARK [0][-]{chapter.4}{ Graphical styles}{}
+\BOOKMARK [1][-]{section.4.1}{ Drawing engines, style specifications and sub-styles}{chapter.4}
+\BOOKMARK [2][-]{subsection.4.1.1}{ Known styles and substyles}{section.4.1}
+\BOOKMARK [1][-]{section.4.2}{ Defining styles for the default drawing engine}{chapter.4}
+\BOOKMARK [2][-]{subsection.4.2.1}{ The structure of the configuration files}{section.4.2}
+\BOOKMARK [2][-]{subsection.4.2.2}{ Defining the styles}{section.4.2}
+\BOOKMARK [2][-]{subsection.4.2.3}{ An example}{section.4.2}
+\BOOKMARK [1][-]{section.4.3}{ Miscellaneous settings}{chapter.4}
+\BOOKMARK [2][-]{lstlisting.4.-385}{ Extra fields for style !frame!}{section.4.3}
+\BOOKMARK [2][-]{lstlisting.4.-407}{ Extra fields for style !dock!}{section.4.3}
+\BOOKMARK [0][-]{chapter.5}{ Scripting}{}
+\BOOKMARK [1][-]{section.5.1}{ Hooks}{chapter.5}
+\BOOKMARK [1][-]{section.5.2}{ Referring to regions}{chapter.5}
+\BOOKMARK [2][-]{subsection.5.2.1}{ Direct object references}{section.5.2}
+\BOOKMARK [2][-]{subsection.5.2.2}{ Name-based lookups}{section.5.2}
+\BOOKMARK [1][-]{section.5.3}{ Alternative winprop selection criteria}{chapter.5}
+\BOOKMARK [1][-]{lstlisting.5.-448}{ Writing !ion-statusd! monitors}{chapter.5}
+\BOOKMARK [0][-]{chapter.6}{ Function reference}{}
+\BOOKMARK [1][-]{section.6.1}{ Functions defined in ioncore}{chapter.6}
+\BOOKMARK [2][-]{subsection.6.1.1}{ WClientWin functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.2}{ WFrame functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.3}{ WGroup functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.4}{ WGroupCW functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.5}{ WGroupWS functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.6}{ WInfoWin functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.7}{ WMPlex functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.8}{ WMoveresMode functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.9}{ WRegion functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.10}{ WRootWin functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.11}{ WScreen functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.12}{ WWindow functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.13}{ global functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.14}{ gr functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.15}{ string functions}{section.6.1}
+\BOOKMARK [2][-]{subsection.6.1.16}{ table functions}{section.6.1}
+\BOOKMARK [1][-]{section.6.2}{ Functions defined in mod\137tiling}{chapter.6}
+\BOOKMARK [2][-]{subsection.6.2.1}{ WSplit functions}{section.6.2}
+\BOOKMARK [2][-]{subsection.6.2.2}{ WSplitInner functions}{section.6.2}
+\BOOKMARK [2][-]{subsection.6.2.3}{ WSplitRegion functions}{section.6.2}
+\BOOKMARK [2][-]{subsection.6.2.4}{ WSplitSplit functions}{section.6.2}
+\BOOKMARK [2][-]{subsection.6.2.5}{ WTiling functions}{section.6.2}
+\BOOKMARK [1][-]{section.6.3}{ Functions defined in mod\137query}{chapter.6}
+\BOOKMARK [2][-]{subsection.6.3.1}{ WComplProxy functions}{section.6.3}
+\BOOKMARK [2][-]{subsection.6.3.2}{ WEdln functions}{section.6.3}
+\BOOKMARK [2][-]{subsection.6.3.3}{ WInput functions}{section.6.3}
+\BOOKMARK [1][-]{section.6.4}{ Functions defined in mod\137menu}{chapter.6}
+\BOOKMARK [2][-]{subsection.6.4.1}{ WMenu functions}{section.6.4}
+\BOOKMARK [1][-]{section.6.5}{ Functions defined in mod\137dock}{chapter.6}
+\BOOKMARK [2][-]{subsection.6.5.1}{ WDock functions}{section.6.5}
+\BOOKMARK [1][-]{section.6.6}{ Functions defined in mod\137sp}{chapter.6}
+\BOOKMARK [1][-]{section.6.7}{ Functions defined in de}{chapter.6}
+\BOOKMARK [1][-]{section.6.8}{ Hooks}{chapter.6}
+\BOOKMARK [0][-]{appendix.A}{ The GNU General Public License}{}
+\BOOKMARK [0][-]{appendix.B}{ Full class hierarchy visible to Lua-side}{}
+\BOOKMARK [0][-]{section*.17}{Index}{}
diff --git a/doc/ionconf.ps.gz b/doc/ionconf.ps.gz
new file mode 100644 (file)
index 0000000..055828e
Binary files /dev/null and b/doc/ionconf.ps.gz differ
diff --git a/doc/ionconf.tex b/doc/ionconf.tex
new file mode 100644 (file)
index 0000000..9dd71c8
--- /dev/null
@@ -0,0 +1,73 @@
+\documentclass[english,a4paper,11pt,oldtoc,mctitle]{rapport3}
+\input{macros}
+
+% For including some files from articles
+\newcommand{\xchapter}[1]{\chapter{#1}}
+\newcommand{\xsection}[1]{\section{#1}}
+\newcommand{\xsectionstar}[1]{\section*{#1}}
+\newcommand{\xsubsection}[1]{\subsection{#1}}
+
+
+\title{Configuring and extending Ion3 with Lua}
+\author{Tuomo Valkonen \\ tuomov at iki.fi}
+\date{2006-12-23}
+
+\makeindex
+
+
+\begin{document}
+
+\maketitle
+
+Configuring and extending Ion3 with Lua\\
+Copyright \copyright\  2003--2005 Tuomo Valkonen.
+
+This document is free; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This document 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
+chapter entitled ''GNU General Public License'' for more details.
+
+\tableofcontents
+
+\input{confintro}
+
+\input{prelim}
+
+\input{conf}
+
+\input{de}
+
+\input{tricks}
+
+\input{fnref}
+
+\appendix
+
+\input{gpl}
+
+\input{fullhierarchy}
+
+\chapter*{List of functions}
+\markboth{\MakeUppercase{List of functions}}%
+         {\MakeUppercase{List of functions}}%
+
+%begin{latexonly}
+\makeatletter
+\def\fnlisti#1{\@dottedtocline{1}{0em}{1.5em}{\lstinline!#1!}{\pageref{fn:#1}}}
+{\parskip\z@\input{fnlist}}
+\makeatother
+%end{latexonly}
+
+\begin{htmlonly}
+\newcommand{\fnlisti}[1]{\fnref{#1}\\}
+\input{fnlist}
+\end{htmlonly}
+
+\printindex
+
+\end{document}
diff --git a/doc/ionconf/WARNINGS b/doc/ionconf/WARNINGS
new file mode 100644 (file)
index 0000000..726c0ef
--- /dev/null
@@ -0,0 +1,8 @@
+No implementation found for style `hyperref'
+No implementation found for style `ae'
+No implementation found for style `url'
+No implementation found for style `tocbibind'
+No implementation found for style `geometry'
+No implementation found for style `calc'
+
+Substitution of arg to newlabelxx delayed.
diff --git a/doc/ionconf/contents.png b/doc/ionconf/contents.png
new file mode 100644 (file)
index 0000000..0c752c6
Binary files /dev/null and b/doc/ionconf/contents.png differ
diff --git a/doc/ionconf/images.aux b/doc/ionconf/images.aux
new file mode 100644 (file)
index 0000000..3b687b9
--- /dev/null
@@ -0,0 +1,19 @@
+\relax 
+\ifx\hyper@anchor\@undefined
+\global \let \oldcontentsline\contentsline
+\gdef \contentsline#1#2#3#4{\oldcontentsline{#1}{#2}{#3}}
+\global \let \oldnewlabel\newlabel
+\gdef \newlabel#1#2{\newlabelxx{#1}#2}
+\gdef \newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}}
+\AtEndDocument{\let \contentsline\oldcontentsline
+\let \newlabel\oldnewlabel}
+\else
+\global \let \hyper@last\relax 
+\fi
+
+\select@language{english}
+\@writefile{toc}{\select@language{english}}
+\@writefile{lof}{\select@language{english}}
+\@writefile{lot}{\select@language{english}}
+\@ifundefined{unitindent}{\newdimen\unitindent\let \@indentset\relax }{}
+\global \unitindent=38.41783pt\relax\@indentset\relax
diff --git a/doc/ionconf/images.idx b/doc/ionconf/images.idx
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/ionconf/images.log b/doc/ionconf/images.log
new file mode 100644 (file)
index 0000000..952589a
--- /dev/null
@@ -0,0 +1,396 @@
+This is pdfeTeX, Version 3.141592-1.21a-2.2 (Web2C 7.5.4) (format=latex 2006.11.18)  23 DEC 2006 17:03
+entering extended mode
+**./images.tex
+(./images.tex
+LaTeX2e <2003/12/01>
+Babel <v3.8d> and hyphenation patterns for american, french, german, ngerman, b
+ahasa, basque, bulgarian, catalan, croatian, czech, danish, dutch, esperanto, e
+stonian, finnish, greek, icelandic, irish, italian, latin, magyar, norsk, polis
+h, portuges, romanian, russian, serbian, slovak, slovene, spanish, swedish, tur
+kish, ukrainian, nohyphenation, loaded.
+
+(/usr/share/texmf-tetex/tex/latex/ntgclass/rapport3.cls
+Document Class: rapport3 2004/06/07 v2.1a NTG LaTeX document class
+(/usr/share/texmf-tetex/tex/latex/ntgclass/ntg11.clo
+File: ntg11.clo 2004/06/07 v2.1a NTG LaTeX file (size option)
+)
+\unitindent=\dimen102
+\othermargin=\dimen103
+\c@part=\count79
+\c@chapter=\count80
+\c@section=\count81
+\c@subsection=\count82
+\c@subsubsection=\count83
+\c@paragraph=\count84
+\c@subparagraph=\count85
+\c@figure=\count86
+\c@table=\count87
+\abovecaptionskip=\skip41
+\belowcaptionskip=\skip42
+LaTeX Info: Redefining \em on input line 826.
+\bibindent=\dimen104
+) (/usr/share/texmf-tetex/tex/latex/base/ifthen.sty
+Package: ifthen 2001/05/26 v1.1c Standard LaTeX ifthen package (DPC)
+) (/usr/share/texmf-tetex/tex/generic/babel/babel.sty
+Package: babel 2004/11/20 v3.8d The Babel package
+(/usr/share/texmf-tetex/tex/generic/babel/english.ldf
+Language: english 2004/06/14 v3.3o English support from the babel system
+(/usr/share/texmf-tetex/tex/generic/babel/babel.def
+File: babel.def 2004/11/20 v3.8d Babel common definitions
+\babel@savecnt=\count88
+\U@D=\dimen105
+)
+\l@british = a dialect from \language\l@english 
+\l@UKenglish = a dialect from \language\l@english 
+\l@canadian = a dialect from \language\l@american 
+\l@australian = a dialect from \language\l@british 
+\l@newzealand = a dialect from \language\l@british 
+)) (/usr/share/texmf-tetex/tex/latex/base/inputenc.sty
+Package: inputenc 2004/02/05 v1.0d Input encoding file
+(/usr/share/texmf-tetex/tex/latex/base/latin1.def
+File: latin1.def 2004/02/05 v1.0d Input encoding file
+)) (/usr/share/texmf-tetex/tex/latex/hyperref/hyperref.sty
+Package: hyperref 2003/11/30 v6.74m Hypertext links for LaTeX
+(/usr/share/texmf-tetex/tex/latex/graphics/keyval.sty
+Package: keyval 1999/03/16 v1.13 key=value parser (DPC)
+\KV@toks@=\toks14
+)
+\@linkdim=\dimen106
+\Hy@linkcounter=\count89
+\Hy@pagecounter=\count90
+(/usr/share/texmf-tetex/tex/latex/hyperref/pd1enc.def
+File: pd1enc.def 2003/11/30 v6.74m Hyperref: PDFDocEncoding definition (HO)
+) (/usr/share/texmf-tetex/tex/latex/hyperref/hyperref.cfg
+File: hyperref.cfg 2002/06/06 v1.2 hyperref configuration of TeXLive and teTeX
+)
+Package hyperref Info: Hyper figures OFF on input line 1880.
+Package hyperref Info: Link nesting OFF on input line 1885.
+Package hyperref Info: Hyper index ON on input line 1888.
+Package hyperref Info: Plain pages ON on input line 1893.
+Package hyperref Info: Backreferencing OFF on input line 1900.
+Implicit mode ON; LaTeX internals redefined
+Package hyperref Info: Bookmarks ON on input line 2004.
+(/usr/share/texmf/tex/latex/latex2html/url.sty
+Package: url 1999/03/02  ver 1.4  Verb mode for urls, email addresses, and file
+ names
+)
+LaTeX Info: Redefining \url on input line 2143.
+\Fld@menulength=\count91
+\Field@Width=\dimen107
+\Fld@charsize=\dimen108
+\Choice@toks=\toks15
+\Field@toks=\toks16
+Package hyperref Info: Hyper figures OFF on input line 2618.
+Package hyperref Info: Link nesting OFF on input line 2623.
+Package hyperref Info: Hyper index ON on input line 2626.
+Package hyperref Info: backreferencing OFF on input line 2633.
+Package hyperref Info: Link coloring OFF on input line 2638.
+\c@Item=\count92
+\c@Hfootnote=\count93
+)
+*hyperref using driver hdvipdfm*
+(/usr/share/texmf-tetex/tex/latex/hyperref/hdvipdfm.def
+File: hdvipdfm.def 2003/11/30 v6.74m Hyperref driver for dvipdfm
+\pdfm@box=\box26
+\Fld@listcount=\count94
+\@outlinefile=\write3
+) (/usr/share/texmf-tetex/tex/latex/ae/ae.sty
+Package: ae 2001/02/12 1.3 Almost European Computer Modern
+(/usr/share/texmf-tetex/tex/latex/base/fontenc.sty
+Package: fontenc 2004/02/22 v1.99f Standard LaTeX package
+(/usr/share/texmf-tetex/tex/latex/base/t1enc.def
+File: t1enc.def 2004/02/22 v1.99f Standard LaTeX file
+LaTeX Font Info:    Redeclaring font encoding T1 on input line 43.
+)
+LaTeX Font Info:    Try loading font information for T1+aer on input line 100.
+(/usr/share/texmf-tetex/tex/latex/ae/t1aer.fd
+File: t1aer.fd 1997/11/16 Font definitions for T1/aer.
+))) (/usr/share/texmf/tex/latex/html/html.sty
+Package: html 1999/07/19 v1.38 hypertext commands for latex2html (nd, hws, rrm)
+
+\c@lpart=\count95
+\c@lchapter=\count96
+\c@lsection=\count97
+\c@lsubsection=\count98
+\c@lsubsubsection=\count99
+\c@lparagraph=\count100
+\c@lsubparagraph=\count101
+\c@lsubsubparagraph=\count102
+\ptrfile=\write4
+) (/usr/share/texmf-tetex/tex/latex/base/makeidx.sty
+Package: makeidx 2000/03/29 v1.0m Standard LaTeX package
+) (/usr/share/texmf-tetex/tex/latex/tools/tabularx.sty
+Package: tabularx 1999/01/07 v2.07 `tabularx' package (DPC)
+(/usr/share/texmf-tetex/tex/latex/tools/array.sty
+Package: array 2003/12/17 v2.4a Tabular extension package (FMi)
+\col@sep=\dimen109
+\extrarowheight=\dimen110
+\NC@list=\toks17
+\extratabsurround=\skip43
+\backup@length=\skip44
+)
+\TX@col@width=\dimen111
+\TX@old@table=\dimen112
+\TX@old@col=\dimen113
+\TX@target=\dimen114
+\TX@delta=\dimen115
+\TX@cols=\count103
+\TX@ftn=\toks18
+) (/usr/share/texmf-tetex/tex/latex/base/textcomp.sty
+Package: textcomp 2004/02/22 v1.99f Standard LaTeX package
+Package textcomp Info: Sub-encoding information:
+(textcomp)               5 = only ISO-Adobe without \textcurrency
+(textcomp)               4 = 5 + \texteuro
+(textcomp)               3 = 4 + \textohm
+(textcomp)               2 = 3 + \textestimated + \textcurrency
+(textcomp)               1 = TS1 - \textcircled - \t
+(textcomp)               0 = TS1 (full)
+(textcomp)             Font families with sub-encoding setting implement
+(textcomp)             only a restricted character set as indicated.
+(textcomp)             Family '?' is the default used for unknown fonts.
+(textcomp)             See the documentation for details.
+Package textcomp Info: Setting ? sub-encoding to TS1/1 on input line 71.
+(/usr/share/texmf-tetex/tex/latex/base/ts1enc.def
+File: ts1enc.def 2001/06/05 v3.0e (jk/car/fm) Standard LaTeX file
+)
+LaTeX Info: Redefining \oldstylenums on input line 266.
+Package textcomp Info: Setting cmr sub-encoding to TS1/0 on input line 281.
+Package textcomp Info: Setting cmss sub-encoding to TS1/0 on input line 282.
+Package textcomp Info: Setting cmtt sub-encoding to TS1/0 on input line 283.
+Package textcomp Info: Setting cmvtt sub-encoding to TS1/0 on input line 284.
+Package textcomp Info: Setting cmbr sub-encoding to TS1/0 on input line 285.
+Package textcomp Info: Setting cmtl sub-encoding to TS1/0 on input line 286.
+Package textcomp Info: Setting ccr sub-encoding to TS1/0 on input line 287.
+Package textcomp Info: Setting ptm sub-encoding to TS1/4 on input line 288.
+Package textcomp Info: Setting pcr sub-encoding to TS1/4 on input line 289.
+Package textcomp Info: Setting phv sub-encoding to TS1/4 on input line 290.
+Package textcomp Info: Setting ppl sub-encoding to TS1/3 on input line 291.
+Package textcomp Info: Setting pag sub-encoding to TS1/4 on input line 292.
+Package textcomp Info: Setting pbk sub-encoding to TS1/4 on input line 293.
+Package textcomp Info: Setting pnc sub-encoding to TS1/4 on input line 294.
+Package textcomp Info: Setting pzc sub-encoding to TS1/4 on input line 295.
+Package textcomp Info: Setting bch sub-encoding to TS1/4 on input line 296.
+Package textcomp Info: Setting put sub-encoding to TS1/5 on input line 297.
+Package textcomp Info: Setting uag sub-encoding to TS1/5 on input line 298.
+Package textcomp Info: Setting ugq sub-encoding to TS1/5 on input line 299.
+Package textcomp Info: Setting ul8 sub-encoding to TS1/4 on input line 300.
+Package textcomp Info: Setting ul9 sub-encoding to TS1/4 on input line 301.
+Package textcomp Info: Setting augie sub-encoding to TS1/5 on input line 302.
+Package textcomp Info: Setting dayrom sub-encoding to TS1/3 on input line 303.
+Package textcomp Info: Setting dayroms sub-encoding to TS1/3 on input line 304.
+
+Package textcomp Info: Setting pxr sub-encoding to TS1/0 on input line 305.
+Package textcomp Info: Setting pxss sub-encoding to TS1/0 on input line 306.
+Package textcomp Info: Setting pxtt sub-encoding to TS1/0 on input line 307.
+Package textcomp Info: Setting txr sub-encoding to TS1/0 on input line 308.
+Package textcomp Info: Setting txss sub-encoding to TS1/0 on input line 309.
+Package textcomp Info: Setting txtt sub-encoding to TS1/0 on input line 310.
+Package textcomp Info: Setting futs sub-encoding to TS1/4 on input line 311.
+Package textcomp Info: Setting futx sub-encoding to TS1/4 on input line 312.
+Package textcomp Info: Setting futj sub-encoding to TS1/4 on input line 313.
+Package textcomp Info: Setting hlh sub-encoding to TS1/3 on input line 314.
+Package textcomp Info: Setting hls sub-encoding to TS1/3 on input line 315.
+Package textcomp Info: Setting hlst sub-encoding to TS1/3 on input line 316.
+Package textcomp Info: Setting hlct sub-encoding to TS1/5 on input line 317.
+Package textcomp Info: Setting hlx sub-encoding to TS1/5 on input line 318.
+Package textcomp Info: Setting hlce sub-encoding to TS1/5 on input line 319.
+Package textcomp Info: Setting hlcn sub-encoding to TS1/5 on input line 320.
+Package textcomp Info: Setting hlcw sub-encoding to TS1/5 on input line 321.
+Package textcomp Info: Setting hlcf sub-encoding to TS1/5 on input line 322.
+Package textcomp Info: Setting pplx sub-encoding to TS1/3 on input line 323.
+Package textcomp Info: Setting pplj sub-encoding to TS1/3 on input line 324.
+Package textcomp Info: Setting ptmx sub-encoding to TS1/4 on input line 325.
+Package textcomp Info: Setting ptmj sub-encoding to TS1/4 on input line 326.
+) (/usr/share/texmf-tetex/tex/latex/tocbibind/tocbibind.sty
+Package: tocbibind 2003/03/13 v1.5g extra ToC listings
+
+Package tocbibind Note: The document has chapter divisions.
+
+
+Package tocbibind Note: Using chapter style headings, unless overridden.
+
+) (/usr/share/texmf-tetex/tex/latex/tools/enumerate.sty
+Package: enumerate 1999/03/05 v3.00 enumerate extensions (DPC)
+\@enLab=\toks19
+) (/usr/share/texmf-tetex/tex/latex/geometry/geometry.sty
+Package: geometry 2002/07/08 v3.2 Page Geometry
+\Gm@cnth=\count104
+\Gm@cntv=\count105
+\c@Gm@tempcnt=\count106
+\Gm@bindingoffset=\dimen116
+\Gm@wd@mp=\dimen117
+\Gm@odd@mp=\dimen118
+\Gm@even@mp=\dimen119
+\Gm@dimlist=\toks20
+(/usr/share/texmf-tetex/tex/latex/geometry/geometry.cfg)) (/usr/share/texmf-tet
+ex/tex/latex/tools/calc.sty
+Package: calc 1998/07/07 v4.1b Infix arithmetic (KKT,FJ)
+\calc@Acount=\count107
+\calc@Bcount=\count108
+\calc@Adimen=\dimen120
+\calc@Bdimen=\dimen121
+\calc@Askip=\skip45
+\calc@Bskip=\skip46
+LaTeX Info: Redefining \setlength on input line 59.
+LaTeX Info: Redefining \addtolength on input line 60.
+\calc@denominator=\count109
+)
+\@indexfile=\write5
+\openout5 = `images.idx'.
+
+Writing index file images.idx
+(/usr/share/texmf-tetex/tex/latex/graphics/color.sty
+Package: color 1999/02/16 v1.0i Standard LaTeX Color (DPC)
+(/usr/share/texmf-tetex/tex/latex/graphics/color.cfg
+File: color.cfg 2005/02/03 v1.3 color configuration of teTeX/TeXLive
+)
+Package color Info: Driver file: dvips.def on input line 125.
+(/usr/share/texmf-tetex/tex/latex/graphics/dvips.def
+File: dvips.def 1999/02/16 v3.0i Driver-dependant file (DPC,SPQR)
+) (/usr/share/texmf-tetex/tex/latex/graphics/dvipsnam.def
+File: dvipsnam.def 1999/02/16 v3.0i Driver-dependant file (DPC,SPQR)
+))
+\sizebox=\box27
+\lthtmlwrite=\write6
+(./images.aux)
+\openout1 = `images.aux'.
+
+LaTeX Font Info:    Checking defaults for OML/cmm/m/it on input line 198.
+LaTeX Font Info:    ... okay on input line 198.
+LaTeX Font Info:    Checking defaults for T1/cmr/m/n on input line 198.
+LaTeX Font Info:    ... okay on input line 198.
+LaTeX Font Info:    Checking defaults for OT1/cmr/m/n on input line 198.
+LaTeX Font Info:    ... okay on input line 198.
+LaTeX Font Info:    Checking defaults for OMS/cmsy/m/n on input line 198.
+LaTeX Font Info:    ... okay on input line 198.
+LaTeX Font Info:    Checking defaults for OMX/cmex/m/n on input line 198.
+LaTeX Font Info:    ... okay on input line 198.
+LaTeX Font Info:    Checking defaults for U/cmr/m/n on input line 198.
+LaTeX Font Info:    ... okay on input line 198.
+LaTeX Font Info:    Checking defaults for PD1/pdf/m/n on input line 198.
+LaTeX Font Info:    ... okay on input line 198.
+LaTeX Font Info:    Checking defaults for TS1/cmr/m/n on input line 198.
+LaTeX Font Info:    Try loading font information for TS1+cmr on input line 198.
+
+(/usr/share/texmf-tetex/tex/latex/base/ts1cmr.fd
+File: ts1cmr.fd 1999/05/25 v2.5h Standard LaTeX font definitions
+)
+LaTeX Font Info:    ... okay on input line 198.
+Package hyperref Info: Link coloring OFF on input line 198.
+(/usr/share/texmf-tetex/tex/latex/hyperref/nameref.sty
+Package: nameref 2003/12/03 v2.21 Cross-referencing by name of section
+\c@section@level=\count110
+)
+LaTeX Info: Redefining \ref on input line 198.
+LaTeX Info: Redefining \pageref on input line 198.
+\openout3 = `images.out'.
+
+-------------------- Geometry parameters
+paper: a4paper
+landscape: --
+twocolumn: --
+twoside: --
+asymmetric: --
+h-parts: 89.62709pt, 0.7\paperwidth , 89.6271pt (default)
+v-parts: 101.40665pt, 0.7\paperheight , 152.11pt (default)
+hmarginratio: 1:1
+vmarginratio: 2:3
+lines: --
+heightrounded: --
+bindingoffset: 0.0pt
+truedimen: --
+includehead: --
+includefoot: --
+includemp: --
+driver: 
+-------------------- Page layout dimensions and switches
+\paperwidth  597.50787pt
+\paperheight 845.04684pt
+\textwidth  349.0pt
+\textheight 650.66685pt
+\oddsidemargin  17.3571pt
+\evensidemargin 17.3571pt
+\topmargin  0.0pt
+\headheight 0.0pt
+\headsep    0.0pt
+\footskip   0.0pt
+\marginparwidth 71.0pt
+\marginparsep   10.0pt
+\columnsep  10.0pt
+\skip\footins  10.0pt plus 4.0pt minus 2.0pt
+\hoffset 0.0pt
+\voffset 0.0pt
+\mag 1000
+
+(1in=72.27pt, 1cm=28.45pt)
+-----------------------
+
+latex2htmlLength hsize=349.0pt
+
+latex2htmlLength vsize=650.66685pt
+
+latex2htmlLength hoffset=0.0pt
+
+latex2htmlLength voffset=0.0pt
+
+latex2htmlLength topmargin=0.0pt
+
+latex2htmlLength topskip=0.00003pt
+
+latex2htmlLength headheight=0.0pt
+
+latex2htmlLength headsep=0.0pt
+
+latex2htmlLength parskip=6.8pt plus 1.36008pt minus 1.36008pt
+
+latex2htmlLength oddsidemargin=17.3571pt
+
+latex2htmlLength evensidemargin=17.3571pt
+
+LaTeX Font Info:    External font `cmex10' loaded for size
+(Font)              <10.95> on input line 235.
+LaTeX Font Info:    External font `cmex10' loaded for size
+(Font)              <8> on input line 235.
+LaTeX Font Info:    External font `cmex10' loaded for size
+(Font)              <6> on input line 235.
+l2hSize :tex2html_wrap_inline1028:7.0626pt::0.0pt::7.1201pt.
+[1
+
+
+
+]
+l2hSize :tex2html_wrap_inline1032:7.48248pt::0.0pt::48.86426pt.
+[2
+
+
+]
+l2hSize :tex2html_wrap_inline1981:8.7125pt::8.7125pt::23.6642pt.
+[3
+
+
+]
+l2hSize :tex2html_wrap_inline6076:7.5626pt::7.5626pt::14.53923pt.
+[4
+
+
+]
+l2hSize :tex2html_wrap_inline6080:7.5626pt::7.5626pt::14.53923pt.
+[5
+
+
+]
+l2hSize :tex2html_wrap_inline9064:8.7125pt::8.7125pt::86.9574pt.
+[6
+
+
+] (./images.aux) ) 
+Here is how much of TeX's memory you used:
+ 4428 strings out of 94501
+ 62627 string characters out of 1175793
+ 121207 words of memory out of 1000000
+ 7572 multiletter control sequences out of 10000+50000
+ 7874 words of font info for 24 fonts, out of 500000 for 2000
+ 580 hyphenation exceptions out of 8191
+ 28i,5n,21p,200b,182s stack positions out of 1500i,500n,5000p,200000b,5000s
+
+Output written on images.dvi (6 pages, 3608 bytes).
diff --git a/doc/ionconf/images.out b/doc/ionconf/images.out
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/ionconf/images.pl b/doc/ionconf/images.pl
new file mode 100644 (file)
index 0000000..6d0ccb4
--- /dev/null
@@ -0,0 +1,6 @@
+# LaTeX2HTML 2002-2-1 (1.71)
+# Associate images original text with physical files.
+
+
+1;
+
diff --git a/doc/ionconf/images.tex b/doc/ionconf/images.tex
new file mode 100644 (file)
index 0000000..d950261
--- /dev/null
@@ -0,0 +1,371 @@
+\batchmode
+\documentclass[english,a4paper,11pt,oldtoc,mctitle]{rapport3}
+\RequirePackage{ifthen}
+
+
+\usepackage{babel}
+\usepackage[latin1]{inputenc}
+\usepackage[dvipdfm]{hyperref}
+\usepackage{ae}
+\usepackage{url}
+\usepackage{html}
+\usepackage{makeidx}
+\usepackage{tabularx}
+\usepackage{textcomp}
+\usepackage[nottoc]{tocbibind}
+\usepackage{enumerate} 
+\usepackage[a4paper]{geometry}
+\usepackage{calc}
+
+
+\urlstyle{rm}%
+\providecommand{\centerurl}[1]{\begin{center}\url{#1}\end{center}} 
+
+%
+\providecommand{\note}[1]{\color{red}**#1**}%
+\providecommand{\file}[1]{\emph{#1}}%
+\providecommand{\type}[1]{#1}%
+\providecommand{\spec}[1]{#1}%
+\providecommand{\indextype}[1]{\index{#1@#1}} 
+
+%
+\providecommand{\key}[1]{\textbf{#1}}%
+\providecommand{\code}[1]{\texttt{#1}}%
+\providecommand{\var}[1]{\texttt{#1}}%
+\providecommand{\command}[1]{\texttt{#1}} 
+
+%
+\providecommand{\hyperlabel}[1]{\label{#1}}%
+\providecommand{\fnref}[1]{\htmlref{\texttt{#1}}{fn:#1}}%
+\providecommand{\fnrefx}[2]{\htmlref{\texttt{#2}}{fn:#1.#2}}%
+\providecommand{\myhref}[2]{\htmlref{#2}{#1}} 
+
+%
+\providecommand{\tabhead}[1]{\hline #1 \\\hline} 
+
+
+
+%
+\newenvironment{function}{
+  \begin{description}
+}{
+  \end{description}
+} 
+
+%
+\providecommand{\synopsis}[1]{
+  \item[Synopsis:] \texttt{#1}
+}%
+\providecommand{\funcname}[1]{
+  \item[Function:] \texttt{#1}
+}%
+\providecommand{\hookname}[1]{
+  \item[Hook name:] \label{#1}\texttt{#1}
+}%
+\providecommand{\hookparams}[1]{
+  \item[Parameters:] \texttt{#1}
+} 
+
+%
+\newenvironment{funcdesc}{
+  \item[Description:]
+}{} 
+
+
+
+%
+\providecommand{\xchapter}[1]{\chapter{#1}}%
+\providecommand{\xsection}[1]{\section{#1}}%
+\providecommand{\xsectionstar}[1]{\section*{#1}}%
+\providecommand{\xsubsection}[1]{\subsection{#1}} 
+
+
+\title{Configuring and extending Ion3 with Lua}
+\author{Tuomo Valkonen \\tuomov at iki.fi}
+\date{2006-12-23}
+
+
+\makeindex
+
+
+
+
+\usepackage[dvips]{color}
+
+
+\pagecolor[gray]{.7}
+
+\usepackage[latin1]{inputenc}
+
+
+
+\makeatletter
+
+\makeatletter
+\count@=\the\catcode`\_ \catcode`\_=8 
+\newenvironment{tex2html_wrap}{}{}%
+\catcode`\<=12\catcode`\_=\count@
+\newcommand{\providedcommand}[1]{\expandafter\providecommand\csname #1\endcsname}%
+\newcommand{\renewedcommand}[1]{\expandafter\providecommand\csname #1\endcsname{}%
+  \expandafter\renewcommand\csname #1\endcsname}%
+\newcommand{\newedenvironment}[1]{\newenvironment{#1}{}{}\renewenvironment{#1}}%
+\let\newedcommand\renewedcommand
+\let\renewedenvironment\newedenvironment
+\makeatother
+\let\mathon=$
+\let\mathoff=$
+\ifx\AtBeginDocument\undefined \newcommand{\AtBeginDocument}[1]{}\fi
+\newbox\sizebox
+\setlength{\hoffset}{0pt}\setlength{\voffset}{0pt}
+\addtolength{\textheight}{\footskip}\setlength{\footskip}{0pt}
+\addtolength{\textheight}{\topmargin}\setlength{\topmargin}{0pt}
+\addtolength{\textheight}{\headheight}\setlength{\headheight}{0pt}
+\addtolength{\textheight}{\headsep}\setlength{\headsep}{0pt}
+\setlength{\textwidth}{349pt}
+\newwrite\lthtmlwrite
+\makeatletter
+\let\realnormalsize=\normalsize
+\global\topskip=2sp
+\def\preveqno{}\let\real@float=\@float \let\realend@float=\end@float
+\def\@float{\let\@savefreelist\@freelist\real@float}
+\def\liih@math{\ifmmode$\else\bad@math\fi}
+\def\end@float{\realend@float\global\let\@freelist\@savefreelist}
+\let\real@dbflt=\@dbflt \let\end@dblfloat=\end@float
+\let\@largefloatcheck=\relax
+\let\if@boxedmulticols=\iftrue
+\def\@dbflt{\let\@savefreelist\@freelist\real@dbflt}
+\def\adjustnormalsize{\def\normalsize{\mathsurround=0pt \realnormalsize
+ \parindent=0pt\abovedisplayskip=0pt\belowdisplayskip=0pt}%
+ \def\phantompar{\csname par\endcsname}\normalsize}%
+\def\lthtmltypeout#1{{\let\protect\string \immediate\write\lthtmlwrite{#1}}}%
+\newcommand\lthtmlhboxmathA{\adjustnormalsize\setbox\sizebox=\hbox\bgroup\kern.05em }%
+\newcommand\lthtmlhboxmathB{\adjustnormalsize\setbox\sizebox=\hbox to\hsize\bgroup\hfill }%
+\newcommand\lthtmlvboxmathA{\adjustnormalsize\setbox\sizebox=\vbox\bgroup %
+ \let\ifinner=\iffalse \let\)\liih@math }%
+\newcommand\lthtmlboxmathZ{\@next\next\@currlist{}{\def\next{\voidb@x}}%
+ \expandafter\box\next\egroup}%
+\newcommand\lthtmlmathtype[1]{\gdef\lthtmlmathenv{#1}}%
+\newcommand\lthtmllogmath{\dimen0\ht\sizebox \advance\dimen0\dp\sizebox
+  \ifdim\dimen0>.95\vsize
+   \lthtmltypeout{%
+*** image for \lthtmlmathenv\space is too tall at \the\dimen0, reducing to .95 vsize ***}%
+   \ht\sizebox.95\vsize \dp\sizebox\z@ \fi
+  \lthtmltypeout{l2hSize %
+:\lthtmlmathenv:\the\ht\sizebox::\the\dp\sizebox::\the\wd\sizebox.\preveqno}}%
+\newcommand\lthtmlfigureA[1]{\let\@savefreelist\@freelist
+       \lthtmlmathtype{#1}\lthtmlvboxmathA}%
+\newcommand\lthtmlpictureA{\bgroup\catcode`\_=8 \lthtmlpictureB}%
+\newcommand\lthtmlpictureB[1]{\lthtmlmathtype{#1}\egroup
+       \let\@savefreelist\@freelist \lthtmlhboxmathB}%
+\newcommand\lthtmlpictureZ[1]{\hfill\lthtmlfigureZ}%
+\newcommand\lthtmlfigureZ{\lthtmlboxmathZ\lthtmllogmath\copy\sizebox
+       \global\let\@freelist\@savefreelist}%
+\newcommand\lthtmldisplayA{\bgroup\catcode`\_=8 \lthtmldisplayAi}%
+\newcommand\lthtmldisplayAi[1]{\lthtmlmathtype{#1}\egroup\lthtmlvboxmathA}%
+\newcommand\lthtmldisplayB[1]{\edef\preveqno{(\theequation)}%
+  \lthtmldisplayA{#1}\let\@eqnnum\relax}%
+\newcommand\lthtmldisplayZ{\lthtmlboxmathZ\lthtmllogmath\lthtmlsetmath}%
+\newcommand\lthtmlinlinemathA{\bgroup\catcode`\_=8 \lthtmlinlinemathB}
+\newcommand\lthtmlinlinemathB[1]{\lthtmlmathtype{#1}\egroup\lthtmlhboxmathA
+  \vrule height1.5ex width0pt }%
+\newcommand\lthtmlinlineA{\bgroup\catcode`\_=8 \lthtmlinlineB}%
+\newcommand\lthtmlinlineB[1]{\lthtmlmathtype{#1}\egroup\lthtmlhboxmathA}%
+\newcommand\lthtmlinlineZ{\egroup\expandafter\ifdim\dp\sizebox>0pt %
+  \expandafter\centerinlinemath\fi\lthtmllogmath\lthtmlsetinline}
+\newcommand\lthtmlinlinemathZ{\egroup\expandafter\ifdim\dp\sizebox>0pt %
+  \expandafter\centerinlinemath\fi\lthtmllogmath\lthtmlsetmath}
+\newcommand\lthtmlindisplaymathZ{\egroup %
+  \centerinlinemath\lthtmllogmath\lthtmlsetmath}
+\def\lthtmlsetinline{\hbox{\vrule width.1em \vtop{\vbox{%
+  \kern.1em\copy\sizebox}\ifdim\dp\sizebox>0pt\kern.1em\else\kern.3pt\fi
+  \ifdim\hsize>\wd\sizebox \hrule depth1pt\fi}}}
+\def\lthtmlsetmath{\hbox{\vrule width.1em\kern-.05em\vtop{\vbox{%
+  \kern.1em\kern0.8 pt\hbox{\hglue.17em\copy\sizebox\hglue0.8 pt}}\kern.3pt%
+  \ifdim\dp\sizebox>0pt\kern.1em\fi \kern0.8 pt%
+  \ifdim\hsize>\wd\sizebox \hrule depth1pt\fi}}}
+\def\centerinlinemath{%
+  \dimen1=\ifdim\ht\sizebox<\dp\sizebox \dp\sizebox\else\ht\sizebox\fi
+  \advance\dimen1by.5pt \vrule width0pt height\dimen1 depth\dimen1 
+ \dp\sizebox=\dimen1\ht\sizebox=\dimen1\relax}
+
+\def\lthtmlcheckvsize{\ifdim\ht\sizebox<\vsize 
+  \ifdim\wd\sizebox<\hsize\expandafter\hfill\fi \expandafter\vfill
+  \else\expandafter\vss\fi}%
+\providecommand{\selectlanguage}[1]{}%
+\makeatletter \tracingstats = 1 
+
+
+\begin{document}
+\pagestyle{empty}\thispagestyle{empty}\lthtmltypeout{}%
+\lthtmltypeout{latex2htmlLength hsize=\the\hsize}\lthtmltypeout{}%
+\lthtmltypeout{latex2htmlLength vsize=\the\vsize}\lthtmltypeout{}%
+\lthtmltypeout{latex2htmlLength hoffset=\the\hoffset}\lthtmltypeout{}%
+\lthtmltypeout{latex2htmlLength voffset=\the\voffset}\lthtmltypeout{}%
+\lthtmltypeout{latex2htmlLength topmargin=\the\topmargin}\lthtmltypeout{}%
+\lthtmltypeout{latex2htmlLength topskip=\the\topskip}\lthtmltypeout{}%
+\lthtmltypeout{latex2htmlLength headheight=\the\headheight}\lthtmltypeout{}%
+\lthtmltypeout{latex2htmlLength headsep=\the\headsep}\lthtmltypeout{}%
+\lthtmltypeout{latex2htmlLength parskip=\the\parskip}\lthtmltypeout{}%
+\lthtmltypeout{latex2htmlLength oddsidemargin=\the\oddsidemargin}\lthtmltypeout{}%
+\makeatletter
+\if@twoside\lthtmltypeout{latex2htmlLength evensidemargin=\the\evensidemargin}%
+\else\lthtmltypeout{latex2htmlLength evensidemargin=\the\oddsidemargin}\fi%
+\lthtmltypeout{}%
+\makeatother
+\setcounter{page}{1}
+\onecolumn
+
+% !!! IMAGES START HERE !!!
+
+\stepcounter{chapter}
+\stepcounter{chapter}
+\stepcounter{section}
+\stepcounter{section}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsubsection}
+\stepcounter{subsubsection}
+\stepcounter{subsection}
+\stepcounter{chapter}
+\stepcounter{section}
+\stepcounter{section}
+\stepcounter{section}
+{\newpage\clearpage
+\lthtmlinlinemathA{tex2html_wrap_inline1028}%
+$n$%
+\lthtmlinlinemathZ
+\lthtmlcheckvsize\clearpage}
+
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+{\newpage\clearpage
+\lthtmlinlinemathA{tex2html_wrap_inline1032}%
+$N=1{\ldots} 5$%
+\lthtmlinlinemathZ
+\lthtmlcheckvsize\clearpage}
+
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{section}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{section}
+
+
+\newedenvironment{winprop}[2]{
+  
+  \begin{description}
+%
+      \item[Winprop:] \texttt{#1} (#2)
+      \item[Description:]
+}
+{
+  
+  \end{description}
+}%
+
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsubsection}
+\stepcounter{subsubsection}
+\stepcounter{subsubsection}
+\stepcounter{chapter}
+\stepcounter{section}
+\stepcounter{subsection}
+\stepcounter{subsubsection}
+\stepcounter{subsubsection}
+\stepcounter{subsubsection}
+\stepcounter{section}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsubsection}
+\stepcounter{subsubsection}
+\stepcounter{subsubsection}
+\stepcounter{subsubsection}
+\stepcounter{subsubsection}
+\stepcounter{subsection}
+\stepcounter{section}
+\stepcounter{subsection}
+{\newpage\clearpage
+\lthtmlinlinemathA{tex2html_wrap_inline1981}%
+$(0, 1]$%
+\lthtmlinlinemathZ
+\lthtmlcheckvsize\clearpage}
+
+\stepcounter{subsection}
+\stepcounter{chapter}
+\stepcounter{section}
+\stepcounter{section}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{section}
+\stepcounter{section}
+\stepcounter{chapter}
+\stepcounter{section}
+{\newpage\clearpage
+\lthtmlinlinemathA{tex2html_wrap_inline6076}%
+$-1$%
+\lthtmlinlinemathZ
+\lthtmlcheckvsize\clearpage}
+
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+{\newpage\clearpage
+\lthtmlinlinemathA{tex2html_wrap_inline6080}%
+$-2$%
+\lthtmlinlinemathZ
+\lthtmlcheckvsize\clearpage}
+
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{section}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{section}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{subsection}
+\stepcounter{section}
+\stepcounter{subsection}
+\stepcounter{section}
+\stepcounter{subsection}
+{\newpage\clearpage
+\lthtmlinlinemathA{tex2html_wrap_inline9064}%
+$\{t,m,b\}\times\{t,c,b\}$%
+\lthtmlinlinemathZ
+\lthtmlcheckvsize\clearpage}
+
+\stepcounter{section}
+\stepcounter{section}
+\stepcounter{section}
+\appendix
+\stepcounter{chapter}
+\addtocounter{enumi}{-1}
+\stepcounter{chapter}
+
+%
+\providecommand{\fnlisti}[1]{\htmlref{\texttt{#1}}{fn:#1}\\}%
+
+
+\end{document}
diff --git a/doc/ionconf/img1.png b/doc/ionconf/img1.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/ionconf/img2.png b/doc/ionconf/img2.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/ionconf/img3.png b/doc/ionconf/img3.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/ionconf/img4.png b/doc/ionconf/img4.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/ionconf/img5.png b/doc/ionconf/img5.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/ionconf/img6.png b/doc/ionconf/img6.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/ionconf/index.html b/doc/ionconf/index.html
new file mode 100644 (file)
index 0000000..7a0a20d
--- /dev/null
@@ -0,0 +1,341 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>Configuring and extending Ion3 with Lua</TITLE>
+<META NAME="description" CONTENT="Configuring and extending Ion3 with Lua">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node1.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html19"
+  HREF="node1.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html13"
+  HREF="http://iki.fi/tuomov/ion/">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev_g.png"> 
+<A NAME="tex2html15"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html17"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html20"
+  HREF="node1.html">Contents</A>
+<B> Up:</B> <A NAME="tex2html14"
+  HREF="http://iki.fi/tuomov/ion/">Ion homepage</A>
+ &nbsp; <B>  <A NAME="tex2html16"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html18"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+
+<P>
+<H1 ALIGN="CENTER">Configuring and extending Ion3 with Lua</H1>
+<DIV CLASS="author_info">
+
+<P ALIGN="CENTER"><STRONG>Tuomo Valkonen</STRONG></P>
+<P ALIGN="CENTER"><I>tuomov at iki.fi</I></P>
+<P ALIGN="CENTER"><STRONG>2006-12-23</STRONG></P>
+</DIV>
+
+<P>
+Configuring and extending Ion3 with Lua
+<BR>
+Copyright &#169;  2003-2005 Tuomo Valkonen.
+
+<P>
+This document is free; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+<P>
+This document 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
+chapter entitled ''GNU General Public License'' for more details.
+
+<P>
+<BR><HR>
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html21"
+  HREF="node1.html">Contents</A>
+<LI><A NAME="tex2html22"
+  HREF="node2.html"><SPAN CLASS="arabic">1</SPAN>. Introduction</A>
+<LI><A NAME="tex2html23"
+  HREF="node3.html"><SPAN CLASS="arabic">2</SPAN>. Preliminaries: Key concepts and relations</A>
+<UL>
+<LI><A NAME="tex2html24"
+  HREF="node3.html#SECTION00310000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Modules</A>
+<LI><A NAME="tex2html25"
+  HREF="node3.html#SECTION00320000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Class and object hierarchies</A>
+<UL>
+<LI><A NAME="tex2html26"
+  HREF="node3.html#SECTION00321000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Class hierarchy</A>
+<LI><A NAME="tex2html27"
+  HREF="node3.html#SECTION00322000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Object hierarchies: WRegion parents and managers</A>
+<UL>
+<LI><A NAME="tex2html28"
+  HREF="node3.html#SECTION00322100000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Parent-child relations</A>
+<LI><A NAME="tex2html29"
+  HREF="node3.html#SECTION00322200000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Manager-managed relations</A>
+</UL>
+<LI><A NAME="tex2html30"
+  HREF="node3.html#SECTION00323000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> Summary</A>
+</UL>
+</UL>
+<BR>
+<LI><A NAME="tex2html31"
+  HREF="node4.html"><SPAN CLASS="arabic">3</SPAN>. Basic configuration</A>
+<UL>
+<LI><A NAME="tex2html32"
+  HREF="node4.html#SECTION00410000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> The configuration files</A>
+<LI><A NAME="tex2html33"
+  HREF="node4.html#SECTION00420000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> A walk through <SPAN  CLASS="textit">cfg_ion.lua</SPAN></A>
+<LI><A NAME="tex2html34"
+  HREF="node4.html#SECTION00430000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Keys and rodents</A>
+<UL>
+<LI><A NAME="tex2html35"
+  HREF="node4.html#SECTION00431000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Binding handlers and special variables</A>
+<LI><A NAME="tex2html36"
+  HREF="node4.html#SECTION00432000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Guards</A>
+<LI><A NAME="tex2html37"
+  HREF="node4.html#SECTION00433000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Defining the bindings</A>
+<LI><A NAME="tex2html38"
+  HREF="node4.html#SECTION00434000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Examples</A>
+<LI><A NAME="tex2html39"
+  HREF="node4.html#SECTION00435000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN> Key specifications</A>
+<LI><A NAME="tex2html40"
+  HREF="node4.html#SECTION00436000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">6</SPAN> Button specifications</A>
+<LI><A NAME="tex2html41"
+  HREF="node4.html#SECTION00437000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">7</SPAN> A further note on the default binding configuration</A>
+</UL>
+<LI><A NAME="tex2html42"
+  HREF="node4.html#SECTION00440000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Menus</A>
+<UL>
+<LI><A NAME="tex2html43"
+  HREF="node4.html#SECTION00441000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Defining menus</A>
+<LI><A NAME="tex2html44"
+  HREF="node4.html#SECTION00442000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> Special menus</A>
+<LI><A NAME="tex2html45"
+  HREF="node4.html#SECTION00443000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN> Defining context menus</A>
+<LI><A NAME="tex2html46"
+  HREF="node4.html#SECTION00444000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">4</SPAN> Displaying menus</A>
+</UL>
+<LI><A NAME="tex2html47"
+  HREF="node4.html#SECTION00450000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN> Winprops</A>
+<UL>
+<LI><A NAME="tex2html48"
+  HREF="node4.html#SECTION00451000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Classes, roles and instances</A>
+<LI><A NAME="tex2html49"
+  HREF="node4.html#SECTION00452000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Finding window identification</A>
+<LI><A NAME="tex2html50"
+  HREF="node4.html#SECTION00453000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Some common examples</A>
+<UL>
+<LI><A NAME="tex2html51"
+  HREF="node4.html#SECTION00453100000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Acrobat Reader</A>
+<LI><A NAME="tex2html52"
+  HREF="node4.html#SECTION00453200000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Fixing a Mozilla Firebird transient</A>
+<LI><A NAME="tex2html53"
+  HREF="node4.html#SECTION00453300000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Forcing newly created windows in named frames</A>
+</UL>
+</UL>
+</UL>
+<BR>
+<LI><A NAME="tex2html54"
+  HREF="node5.html"><SPAN CLASS="arabic">4</SPAN>. Graphical styles</A>
+<UL>
+<LI><A NAME="tex2html55"
+  HREF="node5.html#SECTION00510000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Drawing engines, style specifications and sub-styles</A>
+<UL>
+<LI><A NAME="tex2html56"
+  HREF="node5.html#SECTION00511000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Known styles and substyles</A>
+<UL>
+<LI><A NAME="tex2html57"
+  HREF="node5.html#SECTION00511100000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Frames</A>
+<LI><A NAME="tex2html58"
+  HREF="node5.html#SECTION00511200000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> Tabs and menu entries</A>
+<LI><A NAME="tex2html59"
+  HREF="node5.html#SECTION00511300000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> The rest</A>
+</UL>
+</UL>
+<LI><A NAME="tex2html60"
+  HREF="node5.html#SECTION00520000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> Defining styles for the default drawing engine</A>
+<UL>
+<LI><A NAME="tex2html61"
+  HREF="node5.html#SECTION00521000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> The structure of the configuration files</A>
+<LI><A NAME="tex2html62"
+  HREF="node5.html#SECTION00522000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Defining the styles</A>
+<UL>
+<LI><A NAME="tex2html63"
+  HREF="node5.html#SECTION00522100000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Colours</A>
+<LI><A NAME="tex2html64"
+  HREF="node5.html#SECTION00522200000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Borders and widths</A>
+<LI><A NAME="tex2html65"
+  HREF="node5.html#SECTION00522300000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> Text</A>
+<LI><A NAME="tex2html66"
+  HREF="node5.html#SECTION00522400000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous</A>
+<LI><A NAME="tex2html67"
+  HREF="node5.html#SECTION00522500000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN> Substyles</A>
+</UL>
+<LI><A NAME="tex2html68"
+  HREF="node5.html#SECTION00523000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> An example</A>
+</UL>
+<LI><A NAME="tex2html69"
+  HREF="node5.html#SECTION00530000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN> Miscellaneous settings</A>
+<UL>
+<LI><A NAME="tex2html70"
+  HREF="node5.html#SECTION00531000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Extra fields for style <TT>frame</TT></A>
+<LI><A NAME="tex2html71"
+  HREF="node5.html#SECTION00532000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Extra fields for style <TT>dock</TT></A>
+</UL>
+</UL>
+<BR>
+<LI><A NAME="tex2html72"
+  HREF="node6.html"><SPAN CLASS="arabic">5</SPAN>. Scripting</A>
+<UL>
+<LI><A NAME="tex2html73"
+  HREF="node6.html#SECTION00610000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Hooks</A>
+<LI><A NAME="tex2html74"
+  HREF="node6.html#SECTION00620000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Referring to regions</A>
+<UL>
+<LI><A NAME="tex2html75"
+  HREF="node6.html#SECTION00621000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Direct object references</A>
+<LI><A NAME="tex2html76"
+  HREF="node6.html#SECTION00622000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Name-based lookups</A>
+</UL>
+<LI><A NAME="tex2html77"
+  HREF="node6.html#SECTION00630000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Alternative winprop selection criteria</A>
+<LI><A NAME="tex2html78"
+  HREF="node6.html#SECTION00640000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">4</SPAN> Writing <TT>ion-statusd</TT> monitors</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html79"
+  HREF="node7.html"><SPAN CLASS="arabic">6</SPAN>. Function reference</A>
+<UL>
+<LI><A NAME="tex2html80"
+  HREF="node7.html#SECTION00710000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN> Functions defined in <SPAN  CLASS="textit">ioncore</SPAN></A>
+<UL>
+<LI><A NAME="tex2html81"
+  HREF="node7.html#SECTION00711000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> WClientWin functions</A>
+<LI><A NAME="tex2html82"
+  HREF="node7.html#SECTION00712000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> WFrame functions</A>
+<LI><A NAME="tex2html83"
+  HREF="node7.html#SECTION00713000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> WGroup functions</A>
+<LI><A NAME="tex2html84"
+  HREF="node7.html#SECTION00714000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">4</SPAN> WGroupCW functions</A>
+<LI><A NAME="tex2html85"
+  HREF="node7.html#SECTION00715000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">5</SPAN> WGroupWS functions</A>
+<LI><A NAME="tex2html86"
+  HREF="node7.html#SECTION00716000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">6</SPAN> WInfoWin functions</A>
+<LI><A NAME="tex2html87"
+  HREF="node7.html#SECTION00717000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN> WMPlex functions</A>
+<LI><A NAME="tex2html88"
+  HREF="node7.html#SECTION00718000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN> WMoveresMode functions</A>
+<LI><A NAME="tex2html89"
+  HREF="node7.html#SECTION00719000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN> WRegion functions</A>
+<LI><A NAME="tex2html90"
+  HREF="node7.html#SECTION007110000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">10</SPAN> WRootWin functions</A>
+<LI><A NAME="tex2html91"
+  HREF="node7.html#SECTION007111000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">11</SPAN> WScreen functions</A>
+<LI><A NAME="tex2html92"
+  HREF="node7.html#SECTION007112000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">12</SPAN> WWindow functions</A>
+<LI><A NAME="tex2html93"
+  HREF="node7.html#SECTION007113000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">13</SPAN> global functions</A>
+<LI><A NAME="tex2html94"
+  HREF="node7.html#SECTION007114000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">14</SPAN> gr functions</A>
+<LI><A NAME="tex2html95"
+  HREF="node7.html#SECTION007115000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">15</SPAN> string functions</A>
+<LI><A NAME="tex2html96"
+  HREF="node7.html#SECTION007116000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN> table functions</A>
+</UL>
+<LI><A NAME="tex2html97"
+  HREF="node7.html#SECTION00720000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN> Functions defined in <SPAN  CLASS="textit">mod_tiling</SPAN></A>
+<UL>
+<LI><A NAME="tex2html98"
+  HREF="node7.html#SECTION00721000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> WSplit functions</A>
+<LI><A NAME="tex2html99"
+  HREF="node7.html#SECTION00722000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> WSplitInner functions</A>
+<LI><A NAME="tex2html100"
+  HREF="node7.html#SECTION00723000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> WSplitRegion functions</A>
+<LI><A NAME="tex2html101"
+  HREF="node7.html#SECTION00724000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN> WSplitSplit functions</A>
+<LI><A NAME="tex2html102"
+  HREF="node7.html#SECTION00725000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN> WTiling functions</A>
+</UL>
+<LI><A NAME="tex2html103"
+  HREF="node7.html#SECTION00730000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN> Functions defined in <SPAN  CLASS="textit">mod_query</SPAN></A>
+<UL>
+<LI><A NAME="tex2html104"
+  HREF="node7.html#SECTION00731000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> WComplProxy functions</A>
+<LI><A NAME="tex2html105"
+  HREF="node7.html#SECTION00732000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> WEdln functions</A>
+<LI><A NAME="tex2html106"
+  HREF="node7.html#SECTION00733000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> WInput functions</A>
+</UL>
+<LI><A NAME="tex2html107"
+  HREF="node7.html#SECTION00740000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN> Functions defined in <SPAN  CLASS="textit">mod_menu</SPAN></A>
+<UL>
+<LI><A NAME="tex2html108"
+  HREF="node7.html#SECTION00741000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> WMenu functions</A>
+</UL>
+<LI><A NAME="tex2html109"
+  HREF="node7.html#SECTION00750000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN> Functions defined in <SPAN  CLASS="textit">mod_dock</SPAN></A>
+<UL>
+<LI><A NAME="tex2html110"
+  HREF="node7.html#SECTION00751000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> WDock functions</A>
+</UL>
+<LI><A NAME="tex2html111"
+  HREF="node7.html#SECTION00760000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">6</SPAN> Functions defined in <SPAN  CLASS="textit">mod_sp</SPAN></A>
+<LI><A NAME="tex2html112"
+  HREF="node7.html#SECTION00770000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN> Functions defined in <SPAN  CLASS="textit">de</SPAN></A>
+<LI><A NAME="tex2html113"
+  HREF="node7.html#SECTION00780000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN> Hooks</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html114"
+  HREF="node8.html">A. The GNU General Public License</A>
+<UL>
+<LI><A NAME="tex2html115"
+  HREF="node8.html#SECTION00810000000000000000">Appendix: How to Apply These Terms to Your New Programs</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html116"
+  HREF="node9.html">B. Full class hierarchy visible to Lua-side</A>
+<LI><A NAME="tex2html117"
+  HREF="node10.html">List of functions</A>
+<LI><A NAME="tex2html118"
+  HREF="node11.html">Index</A>
+<LI><A NAME="tex2html119"
+  HREF="node12.html">About this document ...</A>
+</UL>
+<!--End of Table of Child-Links-->
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/index.png b/doc/ionconf/index.png
new file mode 100644 (file)
index 0000000..698f09c
Binary files /dev/null and b/doc/ionconf/index.png differ
diff --git a/doc/ionconf/internals.pl b/doc/ionconf/internals.pl
new file mode 100644 (file)
index 0000000..d2d476e
--- /dev/null
@@ -0,0 +1,1306 @@
+# LaTeX2HTML 2002-2-1 (1.71)
+# Associate internals original text with physical files.
+
+
+$key = q/sec:queryref/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.version/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_nth/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:gr.select_engine/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_get_window_property/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.context/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_exec/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_first/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.insstr/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.quote_next/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_renameworkspace/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.is_shaded/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_list/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_activity/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.farthest/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.create_ws/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.finish/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.bskip_word/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.mark/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.switch_prev/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.unsplit_at/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.dec_index/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.current/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:hookref/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:config/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.kpress/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_mapped/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.is_fullscreen/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_prev_screen/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.transpose_words/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:ioncoreref/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defbindings/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WWindow.p_resize/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WComplProxy.set_completions/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.shutdown/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getbindings/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:styles/;
+$ref_files{$key} = "$dir".q|node5.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.get_stdisp/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defmenu/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set_get_winprop_fn/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:borders/;
+$ref_files{$key} = "$dir".q|node5.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.navi_next/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.reset/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_get/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.lookup_script/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WScreen.id/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.set_mode/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.resize/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.select_nth/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.mode/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.warn_traced/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.select_next/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:modules/;
+$ref_files{$key} = "$dir".q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.tl/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.mkbottom/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defctxmenu/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set_selection/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInput.scrollup/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_name_exact/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.switch_nth/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.prev_completion/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.exec_on/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.menuentry/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.forward/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.menu/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.set/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_sp.set_shown/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.find_screen_id/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:bindings/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInfoWin.set_text/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.snapshot/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_do_warp_alt/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.resign/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/clientwin_unmapped_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.write_savefile/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/panews_make_placement_alt/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:exports/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:hooks/;
+$ref_files{$key} = "$dir".q|node6.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WWindow.xid/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.activity_list/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.get_ident/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:classhierarchy/;
+$ref_files{$key} = "$dir".q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_sp.set_shown_on/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.set_floating/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.history_prev/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:binddef/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_to_eol/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_editfile/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:gr.read_config/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split_at/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.p_tabdrag/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.screen_of/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.switch_next/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.bottom/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_search/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.node_of/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRootWin.current_scr/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.navi_first/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_to_bol/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.load_module/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:menuref/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:menudisp/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.maximize_vert/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitInner.current/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.attach/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_tagged/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/app:fullhierarchy/;
+$ref_files{$key} = "$dir".q|node9.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/frame_managed_changed_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:tricks/;
+$ref_files{$key} = "$dir".q|node6.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get_savefile/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_push/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.get/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.lookup_clientwin/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.restart/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_set_text_property/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:winprops/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.parent/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getctxmenu/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_inactivated_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.maximize_horiz/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WWindow.p_move/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:objects/;
+$ref_files{$key} = "$dir".q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WScreen.set_managed_offset/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.set/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get_dir_for/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.point/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.select_prev/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_next/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:engines/;
+$ref_files{$key} = "$dir".q|node5.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.pmenu/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_dock.set_floating_shown_on/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_ssh/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_get_atom_name/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_workspace/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.typeahead_clear/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.copy/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.rqgeom/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.tags_first/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.set_mark/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/screen_managed_changed_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_count/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.attach/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:managership/;
+$ref_files{$key} = "$dir".q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_menu/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.move/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.get/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.flip_at/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.lookup_region/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defshortening/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.refresh_stylelist/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.show_about_ion/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.inc_index/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_delete_property/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqclose/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_activity/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_runfile/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.set_context/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.show_tree/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.contents/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.exec/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.region_list/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_current/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.message/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.compile_cmd/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:deref/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.bol/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.submenu/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.attach_tagged/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.br/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.managed_list/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:string.shell_safe/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_restart/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.managed_list/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.set/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.backspace/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.defstyle/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroupWS.attach_framed/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:dockref/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.set_floating_at/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.warn/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.current/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqclose_propagate/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.clear_mark/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/tiling_placement_alt/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.warn/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.begin_kbresize/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:classesrolesinstances/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_tagged/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.next_completion/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.set_hidden/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.cancel/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.nudge/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_nth_screen/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/clientwin_mapped_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.set_fullscreen/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.paste/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.xid/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.name/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_get_text_property/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_attachclient/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.append/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mclick/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.set_index/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_clear/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.p_switch_tab/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_intern_atom/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.finish/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_gotoclient/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_deinit_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.geom/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_previous/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.find_manager/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get_paths/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:walkthrough/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.set_numbers/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.cancel/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.submap/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_active/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.bkill_word/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.attach_new/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.attach/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mdblclick/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.flip/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split_top/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_activity_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.progname/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.popen_completions/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defwinprop/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.popen_bgread/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mdrag/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.kill/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.transpose/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_snapshot_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.clear_tags/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.map/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.dir/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.set_shaded/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.set_stdisp/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.manager/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_change_property/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInput.cancel/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.parent/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.finish/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.back/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.TR/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:menus/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_renameframe/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.geom/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.kpress_wait/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/clientwin_do_manage_alt/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rootwin_of/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.transpose_at/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_sigchld_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.match_winprop_name/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.nextto/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.get/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.substyle/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.copy/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.is_histcompl/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getmenu/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.is_i18n/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:gr.refresh/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqorder/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.grabmenu/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitRegion.reg/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.size_hints/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.delete/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getwinprop/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.tagged_list/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.request_selection/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_man/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.cut/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.goto/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_next_screen/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set_paths/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:conffiles/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.clientwin_list/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.aboutmsg/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:prelim/;
+$ref_files{$key} = "$dir".q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.managed_list/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:parentship/;
+$ref_files{$key} = "$dir".q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.read_savefile/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.restart_other/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_activity/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqgeom/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInput.scrolldown/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_activated_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_table/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:defaultde/;
+$ref_files{$key} = "$dir".q|node5.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mpress/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:tilingref/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.detach/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_name/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.join/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.activity_first/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:spref/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.is_hidden/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.get/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_yesno/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.history_next/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.eol/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.defcmd/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.bdoc/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.chdir_for/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_lua/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:export/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:gr/;
+$ref_files{$key} = "$dir".q|node5.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.set/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_word/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.defstyle_rootwin/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_post_layout_setup_hook/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.icat/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_line/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.resize/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_shutdown/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.get_index/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.skip_word/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.complete/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.attach_new/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.transpose_chars/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split_tree/;
+$ref_files{$key} = "$dir".q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+1;
+
diff --git a/doc/ionconf/ionconf.css b/doc/ionconf/ionconf.css
new file mode 100644 (file)
index 0000000..f0cd026
--- /dev/null
@@ -0,0 +1,38 @@
+/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */
+.MATH    { font-family: "Century Schoolbook", serif; }
+.MATH I  { font-family: "Century Schoolbook", serif; font-style: italic }
+.BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold }
+
+/* implement both fixed-size and relative sizes */
+SMALL.XTINY            { font-size : xx-small }
+SMALL.TINY             { font-size : x-small  }
+SMALL.SCRIPTSIZE       { font-size : smaller  }
+SMALL.FOOTNOTESIZE     { font-size : small    }
+SMALL.SMALL            {  }
+BIG.LARGE              {  }
+BIG.XLARGE             { font-size : large    }
+BIG.XXLARGE            { font-size : x-large  }
+BIG.HUGE               { font-size : larger   }
+BIG.XHUGE              { font-size : xx-large }
+
+/* heading styles */
+H1             {  }
+H2             {  }
+H3             {  }
+H4             {  }
+H5             {  }
+
+/* mathematics styles */
+DIV.displaymath                { }     /* math displays */
+TD.eqno                        { }     /* equation-number cells */
+
+
+/* document-specific styles come next */
+DIV.navigation         {   }
+DIV.center             {   }
+SPAN.sc                {   }
+DIV.quote              {   }
+PRE.preform            {   }
+SPAN.textit            { font-style: italic  }
+SPAN.arabic            {   }
+SPAN.textbf            { font-weight: bold  }
diff --git a/doc/ionconf/ionconf.html b/doc/ionconf/ionconf.html
new file mode 100644 (file)
index 0000000..7a0a20d
--- /dev/null
@@ -0,0 +1,341 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>Configuring and extending Ion3 with Lua</TITLE>
+<META NAME="description" CONTENT="Configuring and extending Ion3 with Lua">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node1.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html19"
+  HREF="node1.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html13"
+  HREF="http://iki.fi/tuomov/ion/">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev_g.png"> 
+<A NAME="tex2html15"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html17"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html20"
+  HREF="node1.html">Contents</A>
+<B> Up:</B> <A NAME="tex2html14"
+  HREF="http://iki.fi/tuomov/ion/">Ion homepage</A>
+ &nbsp; <B>  <A NAME="tex2html16"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html18"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+
+<P>
+<H1 ALIGN="CENTER">Configuring and extending Ion3 with Lua</H1>
+<DIV CLASS="author_info">
+
+<P ALIGN="CENTER"><STRONG>Tuomo Valkonen</STRONG></P>
+<P ALIGN="CENTER"><I>tuomov at iki.fi</I></P>
+<P ALIGN="CENTER"><STRONG>2006-12-23</STRONG></P>
+</DIV>
+
+<P>
+Configuring and extending Ion3 with Lua
+<BR>
+Copyright &#169;  2003-2005 Tuomo Valkonen.
+
+<P>
+This document is free; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+<P>
+This document 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
+chapter entitled ''GNU General Public License'' for more details.
+
+<P>
+<BR><HR>
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html21"
+  HREF="node1.html">Contents</A>
+<LI><A NAME="tex2html22"
+  HREF="node2.html"><SPAN CLASS="arabic">1</SPAN>. Introduction</A>
+<LI><A NAME="tex2html23"
+  HREF="node3.html"><SPAN CLASS="arabic">2</SPAN>. Preliminaries: Key concepts and relations</A>
+<UL>
+<LI><A NAME="tex2html24"
+  HREF="node3.html#SECTION00310000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Modules</A>
+<LI><A NAME="tex2html25"
+  HREF="node3.html#SECTION00320000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Class and object hierarchies</A>
+<UL>
+<LI><A NAME="tex2html26"
+  HREF="node3.html#SECTION00321000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Class hierarchy</A>
+<LI><A NAME="tex2html27"
+  HREF="node3.html#SECTION00322000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Object hierarchies: WRegion parents and managers</A>
+<UL>
+<LI><A NAME="tex2html28"
+  HREF="node3.html#SECTION00322100000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Parent-child relations</A>
+<LI><A NAME="tex2html29"
+  HREF="node3.html#SECTION00322200000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Manager-managed relations</A>
+</UL>
+<LI><A NAME="tex2html30"
+  HREF="node3.html#SECTION00323000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> Summary</A>
+</UL>
+</UL>
+<BR>
+<LI><A NAME="tex2html31"
+  HREF="node4.html"><SPAN CLASS="arabic">3</SPAN>. Basic configuration</A>
+<UL>
+<LI><A NAME="tex2html32"
+  HREF="node4.html#SECTION00410000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> The configuration files</A>
+<LI><A NAME="tex2html33"
+  HREF="node4.html#SECTION00420000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> A walk through <SPAN  CLASS="textit">cfg_ion.lua</SPAN></A>
+<LI><A NAME="tex2html34"
+  HREF="node4.html#SECTION00430000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Keys and rodents</A>
+<UL>
+<LI><A NAME="tex2html35"
+  HREF="node4.html#SECTION00431000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Binding handlers and special variables</A>
+<LI><A NAME="tex2html36"
+  HREF="node4.html#SECTION00432000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Guards</A>
+<LI><A NAME="tex2html37"
+  HREF="node4.html#SECTION00433000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Defining the bindings</A>
+<LI><A NAME="tex2html38"
+  HREF="node4.html#SECTION00434000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Examples</A>
+<LI><A NAME="tex2html39"
+  HREF="node4.html#SECTION00435000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN> Key specifications</A>
+<LI><A NAME="tex2html40"
+  HREF="node4.html#SECTION00436000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">6</SPAN> Button specifications</A>
+<LI><A NAME="tex2html41"
+  HREF="node4.html#SECTION00437000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">7</SPAN> A further note on the default binding configuration</A>
+</UL>
+<LI><A NAME="tex2html42"
+  HREF="node4.html#SECTION00440000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Menus</A>
+<UL>
+<LI><A NAME="tex2html43"
+  HREF="node4.html#SECTION00441000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Defining menus</A>
+<LI><A NAME="tex2html44"
+  HREF="node4.html#SECTION00442000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> Special menus</A>
+<LI><A NAME="tex2html45"
+  HREF="node4.html#SECTION00443000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN> Defining context menus</A>
+<LI><A NAME="tex2html46"
+  HREF="node4.html#SECTION00444000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">4</SPAN> Displaying menus</A>
+</UL>
+<LI><A NAME="tex2html47"
+  HREF="node4.html#SECTION00450000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN> Winprops</A>
+<UL>
+<LI><A NAME="tex2html48"
+  HREF="node4.html#SECTION00451000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Classes, roles and instances</A>
+<LI><A NAME="tex2html49"
+  HREF="node4.html#SECTION00452000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Finding window identification</A>
+<LI><A NAME="tex2html50"
+  HREF="node4.html#SECTION00453000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Some common examples</A>
+<UL>
+<LI><A NAME="tex2html51"
+  HREF="node4.html#SECTION00453100000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Acrobat Reader</A>
+<LI><A NAME="tex2html52"
+  HREF="node4.html#SECTION00453200000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Fixing a Mozilla Firebird transient</A>
+<LI><A NAME="tex2html53"
+  HREF="node4.html#SECTION00453300000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Forcing newly created windows in named frames</A>
+</UL>
+</UL>
+</UL>
+<BR>
+<LI><A NAME="tex2html54"
+  HREF="node5.html"><SPAN CLASS="arabic">4</SPAN>. Graphical styles</A>
+<UL>
+<LI><A NAME="tex2html55"
+  HREF="node5.html#SECTION00510000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Drawing engines, style specifications and sub-styles</A>
+<UL>
+<LI><A NAME="tex2html56"
+  HREF="node5.html#SECTION00511000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Known styles and substyles</A>
+<UL>
+<LI><A NAME="tex2html57"
+  HREF="node5.html#SECTION00511100000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Frames</A>
+<LI><A NAME="tex2html58"
+  HREF="node5.html#SECTION00511200000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> Tabs and menu entries</A>
+<LI><A NAME="tex2html59"
+  HREF="node5.html#SECTION00511300000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> The rest</A>
+</UL>
+</UL>
+<LI><A NAME="tex2html60"
+  HREF="node5.html#SECTION00520000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> Defining styles for the default drawing engine</A>
+<UL>
+<LI><A NAME="tex2html61"
+  HREF="node5.html#SECTION00521000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> The structure of the configuration files</A>
+<LI><A NAME="tex2html62"
+  HREF="node5.html#SECTION00522000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Defining the styles</A>
+<UL>
+<LI><A NAME="tex2html63"
+  HREF="node5.html#SECTION00522100000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Colours</A>
+<LI><A NAME="tex2html64"
+  HREF="node5.html#SECTION00522200000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Borders and widths</A>
+<LI><A NAME="tex2html65"
+  HREF="node5.html#SECTION00522300000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> Text</A>
+<LI><A NAME="tex2html66"
+  HREF="node5.html#SECTION00522400000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous</A>
+<LI><A NAME="tex2html67"
+  HREF="node5.html#SECTION00522500000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN> Substyles</A>
+</UL>
+<LI><A NAME="tex2html68"
+  HREF="node5.html#SECTION00523000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> An example</A>
+</UL>
+<LI><A NAME="tex2html69"
+  HREF="node5.html#SECTION00530000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN> Miscellaneous settings</A>
+<UL>
+<LI><A NAME="tex2html70"
+  HREF="node5.html#SECTION00531000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Extra fields for style <TT>frame</TT></A>
+<LI><A NAME="tex2html71"
+  HREF="node5.html#SECTION00532000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Extra fields for style <TT>dock</TT></A>
+</UL>
+</UL>
+<BR>
+<LI><A NAME="tex2html72"
+  HREF="node6.html"><SPAN CLASS="arabic">5</SPAN>. Scripting</A>
+<UL>
+<LI><A NAME="tex2html73"
+  HREF="node6.html#SECTION00610000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Hooks</A>
+<LI><A NAME="tex2html74"
+  HREF="node6.html#SECTION00620000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Referring to regions</A>
+<UL>
+<LI><A NAME="tex2html75"
+  HREF="node6.html#SECTION00621000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Direct object references</A>
+<LI><A NAME="tex2html76"
+  HREF="node6.html#SECTION00622000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Name-based lookups</A>
+</UL>
+<LI><A NAME="tex2html77"
+  HREF="node6.html#SECTION00630000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Alternative winprop selection criteria</A>
+<LI><A NAME="tex2html78"
+  HREF="node6.html#SECTION00640000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">4</SPAN> Writing <TT>ion-statusd</TT> monitors</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html79"
+  HREF="node7.html"><SPAN CLASS="arabic">6</SPAN>. Function reference</A>
+<UL>
+<LI><A NAME="tex2html80"
+  HREF="node7.html#SECTION00710000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN> Functions defined in <SPAN  CLASS="textit">ioncore</SPAN></A>
+<UL>
+<LI><A NAME="tex2html81"
+  HREF="node7.html#SECTION00711000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> WClientWin functions</A>
+<LI><A NAME="tex2html82"
+  HREF="node7.html#SECTION00712000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> WFrame functions</A>
+<LI><A NAME="tex2html83"
+  HREF="node7.html#SECTION00713000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> WGroup functions</A>
+<LI><A NAME="tex2html84"
+  HREF="node7.html#SECTION00714000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">4</SPAN> WGroupCW functions</A>
+<LI><A NAME="tex2html85"
+  HREF="node7.html#SECTION00715000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">5</SPAN> WGroupWS functions</A>
+<LI><A NAME="tex2html86"
+  HREF="node7.html#SECTION00716000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">6</SPAN> WInfoWin functions</A>
+<LI><A NAME="tex2html87"
+  HREF="node7.html#SECTION00717000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN> WMPlex functions</A>
+<LI><A NAME="tex2html88"
+  HREF="node7.html#SECTION00718000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN> WMoveresMode functions</A>
+<LI><A NAME="tex2html89"
+  HREF="node7.html#SECTION00719000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN> WRegion functions</A>
+<LI><A NAME="tex2html90"
+  HREF="node7.html#SECTION007110000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">10</SPAN> WRootWin functions</A>
+<LI><A NAME="tex2html91"
+  HREF="node7.html#SECTION007111000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">11</SPAN> WScreen functions</A>
+<LI><A NAME="tex2html92"
+  HREF="node7.html#SECTION007112000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">12</SPAN> WWindow functions</A>
+<LI><A NAME="tex2html93"
+  HREF="node7.html#SECTION007113000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">13</SPAN> global functions</A>
+<LI><A NAME="tex2html94"
+  HREF="node7.html#SECTION007114000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">14</SPAN> gr functions</A>
+<LI><A NAME="tex2html95"
+  HREF="node7.html#SECTION007115000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">15</SPAN> string functions</A>
+<LI><A NAME="tex2html96"
+  HREF="node7.html#SECTION007116000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN> table functions</A>
+</UL>
+<LI><A NAME="tex2html97"
+  HREF="node7.html#SECTION00720000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN> Functions defined in <SPAN  CLASS="textit">mod_tiling</SPAN></A>
+<UL>
+<LI><A NAME="tex2html98"
+  HREF="node7.html#SECTION00721000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> WSplit functions</A>
+<LI><A NAME="tex2html99"
+  HREF="node7.html#SECTION00722000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> WSplitInner functions</A>
+<LI><A NAME="tex2html100"
+  HREF="node7.html#SECTION00723000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> WSplitRegion functions</A>
+<LI><A NAME="tex2html101"
+  HREF="node7.html#SECTION00724000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN> WSplitSplit functions</A>
+<LI><A NAME="tex2html102"
+  HREF="node7.html#SECTION00725000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN> WTiling functions</A>
+</UL>
+<LI><A NAME="tex2html103"
+  HREF="node7.html#SECTION00730000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN> Functions defined in <SPAN  CLASS="textit">mod_query</SPAN></A>
+<UL>
+<LI><A NAME="tex2html104"
+  HREF="node7.html#SECTION00731000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> WComplProxy functions</A>
+<LI><A NAME="tex2html105"
+  HREF="node7.html#SECTION00732000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> WEdln functions</A>
+<LI><A NAME="tex2html106"
+  HREF="node7.html#SECTION00733000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> WInput functions</A>
+</UL>
+<LI><A NAME="tex2html107"
+  HREF="node7.html#SECTION00740000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN> Functions defined in <SPAN  CLASS="textit">mod_menu</SPAN></A>
+<UL>
+<LI><A NAME="tex2html108"
+  HREF="node7.html#SECTION00741000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> WMenu functions</A>
+</UL>
+<LI><A NAME="tex2html109"
+  HREF="node7.html#SECTION00750000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN> Functions defined in <SPAN  CLASS="textit">mod_dock</SPAN></A>
+<UL>
+<LI><A NAME="tex2html110"
+  HREF="node7.html#SECTION00751000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> WDock functions</A>
+</UL>
+<LI><A NAME="tex2html111"
+  HREF="node7.html#SECTION00760000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">6</SPAN> Functions defined in <SPAN  CLASS="textit">mod_sp</SPAN></A>
+<LI><A NAME="tex2html112"
+  HREF="node7.html#SECTION00770000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN> Functions defined in <SPAN  CLASS="textit">de</SPAN></A>
+<LI><A NAME="tex2html113"
+  HREF="node7.html#SECTION00780000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN> Hooks</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html114"
+  HREF="node8.html">A. The GNU General Public License</A>
+<UL>
+<LI><A NAME="tex2html115"
+  HREF="node8.html#SECTION00810000000000000000">Appendix: How to Apply These Terms to Your New Programs</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html116"
+  HREF="node9.html">B. Full class hierarchy visible to Lua-side</A>
+<LI><A NAME="tex2html117"
+  HREF="node10.html">List of functions</A>
+<LI><A NAME="tex2html118"
+  HREF="node11.html">Index</A>
+<LI><A NAME="tex2html119"
+  HREF="node12.html">About this document ...</A>
+</UL>
+<!--End of Table of Child-Links-->
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/labels.pl b/doc/ionconf/labels.pl
new file mode 100644 (file)
index 0000000..cbfa297
--- /dev/null
@@ -0,0 +1,2613 @@
+# LaTeX2HTML 2002-2-1 (1.71)
+# Associate labels original text with physical files.
+
+
+$key = q/sec:queryref/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.version/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_nth/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:gr.select_engine/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_get_window_property/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.context/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_exec/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_first/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.insstr/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.quote_next/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_renameworkspace/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.is_shaded/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_list/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_activity/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.farthest/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.create_ws/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.finish/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.bskip_word/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.mark/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.switch_prev/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.unsplit_at/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.dec_index/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.current/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:hookref/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:config/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.kpress/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_mapped/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.is_fullscreen/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_prev_screen/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.transpose_words/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:ioncoreref/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defbindings/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WWindow.p_resize/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WComplProxy.set_completions/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.shutdown/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getbindings/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:styles/;
+$external_labels{$key} = "$URL/" . q|node5.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.get_stdisp/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defmenu/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set_get_winprop_fn/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:borders/;
+$external_labels{$key} = "$URL/" . q|node5.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.navi_next/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.reset/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_get/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.lookup_script/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WScreen.id/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.set_mode/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.resize/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.select_nth/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.mode/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.warn_traced/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.select_next/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:modules/;
+$external_labels{$key} = "$URL/" . q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.tl/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.mkbottom/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defctxmenu/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set_selection/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInput.scrollup/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_name_exact/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.switch_nth/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.prev_completion/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.exec_on/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.menuentry/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.forward/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.menu/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.set/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_sp.set_shown/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.find_screen_id/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:bindings/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInfoWin.set_text/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.snapshot/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_do_warp_alt/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.resign/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/clientwin_unmapped_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.write_savefile/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/panews_make_placement_alt/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:exports/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:hooks/;
+$external_labels{$key} = "$URL/" . q|node6.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WWindow.xid/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.activity_list/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.get_ident/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:classhierarchy/;
+$external_labels{$key} = "$URL/" . q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_sp.set_shown_on/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.set_floating/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.history_prev/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:binddef/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_to_eol/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_editfile/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:gr.read_config/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split_at/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.p_tabdrag/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.screen_of/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.switch_next/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.bottom/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_search/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.node_of/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRootWin.current_scr/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.navi_first/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_to_bol/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.load_module/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:menuref/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:menudisp/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.maximize_vert/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitInner.current/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.attach/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_tagged/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/app:fullhierarchy/;
+$external_labels{$key} = "$URL/" . q|node9.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/frame_managed_changed_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:tricks/;
+$external_labels{$key} = "$URL/" . q|node6.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get_savefile/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_push/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.get/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.lookup_clientwin/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.restart/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_set_text_property/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:winprops/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.parent/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getctxmenu/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_inactivated_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.maximize_horiz/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WWindow.p_move/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:objects/;
+$external_labels{$key} = "$URL/" . q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WScreen.set_managed_offset/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.set/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get_dir_for/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.point/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.select_prev/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_next/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:engines/;
+$external_labels{$key} = "$URL/" . q|node5.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.pmenu/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_dock.set_floating_shown_on/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_ssh/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_get_atom_name/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_workspace/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.typeahead_clear/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.copy/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.rqgeom/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.tags_first/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.set_mark/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/screen_managed_changed_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_count/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.attach/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:managership/;
+$external_labels{$key} = "$URL/" . q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_menu/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.move/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.get/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.flip_at/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.lookup_region/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defshortening/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.refresh_stylelist/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.show_about_ion/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.inc_index/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_delete_property/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqclose/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_activity/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_runfile/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.set_context/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.show_tree/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.contents/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.exec/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.region_list/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_current/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.message/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.compile_cmd/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:deref/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.bol/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.submenu/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.attach_tagged/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.br/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.managed_list/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:string.shell_safe/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_restart/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.managed_list/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.set/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.backspace/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.defstyle/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroupWS.attach_framed/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:dockref/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.set_floating_at/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.warn/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.current/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqclose_propagate/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.clear_mark/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/tiling_placement_alt/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.warn/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.begin_kbresize/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:classesrolesinstances/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_tagged/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.next_completion/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.set_hidden/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.cancel/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.nudge/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_nth_screen/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/clientwin_mapped_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.set_fullscreen/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.paste/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.xid/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.name/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_get_text_property/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_attachclient/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.append/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mclick/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.set_index/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_clear/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.p_switch_tab/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_intern_atom/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.finish/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_gotoclient/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_deinit_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.geom/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_previous/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.find_manager/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get_paths/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:walkthrough/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.set_numbers/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.cancel/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.submap/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_active/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.bkill_word/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.attach_new/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.attach/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mdblclick/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.flip/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split_top/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_activity_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.progname/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.popen_completions/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defwinprop/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.popen_bgread/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mdrag/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.kill/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.transpose/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_snapshot_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.clear_tags/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.map/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.dir/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.set_shaded/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.set_stdisp/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.manager/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_change_property/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInput.cancel/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.parent/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.finish/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.back/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.TR/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:menus/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_renameframe/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.geom/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.kpress_wait/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/clientwin_do_manage_alt/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rootwin_of/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.transpose_at/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_sigchld_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.match_winprop_name/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.nextto/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.get/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.substyle/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.copy/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.is_histcompl/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getmenu/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.is_i18n/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:gr.refresh/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqorder/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.grabmenu/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitRegion.reg/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.size_hints/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.delete/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getwinprop/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.tagged_list/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.request_selection/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_man/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.cut/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.goto/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_next_screen/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set_paths/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:conffiles/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.clientwin_list/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.aboutmsg/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:prelim/;
+$external_labels{$key} = "$URL/" . q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.managed_list/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:parentship/;
+$external_labels{$key} = "$URL/" . q|node3.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.read_savefile/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.restart_other/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_activity/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqgeom/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInput.scrolldown/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_activated_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_table/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:defaultde/;
+$external_labels{$key} = "$URL/" . q|node5.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mpress/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:tilingref/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.detach/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_name/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.join/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.activity_first/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:spref/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.is_hidden/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.get/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_yesno/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.history_next/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.eol/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.defcmd/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.bdoc/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.chdir_for/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_lua/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:export/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:gr/;
+$external_labels{$key} = "$URL/" . q|node5.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.set/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_word/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.defstyle_rootwin/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_post_layout_setup_hook/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.icat/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_line/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.resize/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_shutdown/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.get_index/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.skip_word/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.complete/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.attach_new/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.transpose_chars/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split_tree/;
+$external_labels{$key} = "$URL/" . q|node7.html|; 
+$noresave{$key} = "$nosave";
+
+1;
+
+
+# LaTeX2HTML 2002-2-1 (1.71)
+# labels from external_latex_labels array.
+
+
+$key = q/sec:queryref/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.version/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_nth/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:gr.select_engine/;
+$external_latex_labels{$key} = q|6.1.14|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_get_window_property/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.context/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_exec/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_first/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.insstr/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.quote_next/;
+$external_latex_labels{$key} = q|6.1.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_renameworkspace/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.is_shaded/;
+$external_latex_labels{$key} = q|6.1.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_list/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_activity/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.farthest/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.create_ws/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.finish/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.bskip_word/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.mark/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.switch_prev/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.unsplit_at/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.dec_index/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.current/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:hookref/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:config/;
+$external_latex_labels{$key} = q|3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.kpress/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_mapped/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.is_fullscreen/;
+$external_latex_labels{$key} = q|6.1.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_prev_screen/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.transpose_words/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:ioncoreref/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defbindings/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WWindow.p_resize/;
+$external_latex_labels{$key} = q|6.1.12|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WComplProxy.set_completions/;
+$external_latex_labels{$key} = q|6.3.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.shutdown/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getbindings/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:styles/;
+$external_latex_labels{$key} = q|4.1.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.get_stdisp/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defmenu/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set_get_winprop_fn/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:borders/;
+$external_latex_labels{$key} = q|4.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.navi_next/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.reset/;
+$external_latex_labels{$key} = q|6.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_get/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.lookup_script/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WScreen.id/;
+$external_latex_labels{$key} = q|6.1.11|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.set_mode/;
+$external_latex_labels{$key} = q|6.1.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.resize/;
+$external_latex_labels{$key} = q|6.1.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.select_nth/;
+$external_latex_labels{$key} = q|6.4.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.mode/;
+$external_latex_labels{$key} = q|6.1.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.warn_traced/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.select_next/;
+$external_latex_labels{$key} = q|6.4.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:modules/;
+$external_latex_labels{$key} = q|2.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.tl/;
+$external_latex_labels{$key} = q|6.2.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.mkbottom/;
+$external_latex_labels{$key} = q|6.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defctxmenu/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set_selection/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInput.scrollup/;
+$external_latex_labels{$key} = q|6.3.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_name_exact/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.switch_nth/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.prev_completion/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.exec_on/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.menuentry/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.forward/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.menu/;
+$external_latex_labels{$key} = q|6.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.set/;
+$external_latex_labels{$key} = q|6.5.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_sp.set_shown/;
+$external_latex_labels{$key} = q|6.6|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.find_screen_id/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:bindings/;
+$external_latex_labels{$key} = q|3.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInfoWin.set_text/;
+$external_latex_labels{$key} = q|6.1.6|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.snapshot/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_do_warp_alt/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.resign/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/clientwin_unmapped_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.write_savefile/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/panews_make_placement_alt/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:exports/;
+$external_latex_labels{$key} = q|6|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:hooks/;
+$external_latex_labels{$key} = q|5.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WWindow.xid/;
+$external_latex_labels{$key} = q|6.1.12|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.activity_list/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.get_ident/;
+$external_latex_labels{$key} = q|6.1.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:classhierarchy/;
+$external_latex_labels{$key} = q|2.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_sp.set_shown_on/;
+$external_latex_labels{$key} = q|6.6|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.set_floating/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.history_prev/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:binddef/;
+$external_latex_labels{$key} = q|3.3.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_to_eol/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_editfile/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:gr.read_config/;
+$external_latex_labels{$key} = q|6.1.14|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split_at/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.p_tabdrag/;
+$external_latex_labels{$key} = q|6.1.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.screen_of/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.switch_next/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.bottom/;
+$external_latex_labels{$key} = q|6.1.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_search/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.node_of/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRootWin.current_scr/;
+$external_latex_labels{$key} = q|6.1.10|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.navi_first/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_to_bol/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.load_module/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:menuref/;
+$external_latex_labels{$key} = q|6.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:menudisp/;
+$external_latex_labels{$key} = q|3.4.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.maximize_vert/;
+$external_latex_labels{$key} = q|6.1.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitInner.current/;
+$external_latex_labels{$key} = q|6.2.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.attach/;
+$external_latex_labels{$key} = q|6.5.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_tagged/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/app:fullhierarchy/;
+$external_latex_labels{$key} = q|B|; 
+$noresave{$key} = "$nosave";
+
+$key = q/frame_managed_changed_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:tricks/;
+$external_latex_labels{$key} = q|5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get_savefile/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_push/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.get/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.lookup_clientwin/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.restart/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_set_text_property/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:winprops/;
+$external_latex_labels{$key} = q|3.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.parent/;
+$external_latex_labels{$key} = q|6.2.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getctxmenu/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_inactivated_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.maximize_horiz/;
+$external_latex_labels{$key} = q|6.1.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WWindow.p_move/;
+$external_latex_labels{$key} = q|6.1.12|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:objects/;
+$external_latex_labels{$key} = q|2.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WScreen.set_managed_offset/;
+$external_latex_labels{$key} = q|6.1.11|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.set/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get_dir_for/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.point/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.select_prev/;
+$external_latex_labels{$key} = q|6.4.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_next/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:engines/;
+$external_latex_labels{$key} = q|4.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.pmenu/;
+$external_latex_labels{$key} = q|6.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_dock.set_floating_shown_on/;
+$external_latex_labels{$key} = q|6.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_ssh/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_get_atom_name/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_workspace/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.typeahead_clear/;
+$external_latex_labels{$key} = q|6.4.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.copy/;
+$external_latex_labels{$key} = q|6.1.16|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.rqgeom/;
+$external_latex_labels{$key} = q|6.2.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.tags_first/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.set_mark/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/screen_managed_changed_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_count/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.attach/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:managership/;
+$external_latex_labels{$key} = q|2.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_menu/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.move/;
+$external_latex_labels{$key} = q|6.1.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.get/;
+$external_latex_labels{$key} = q|6.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.flip_at/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.lookup_region/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defshortening/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.refresh_stylelist/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.show_about_ion/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.inc_index/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_delete_property/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqclose/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_activity/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_runfile/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.set_context/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.show_tree/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.contents/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.exec/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.region_list/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.mx_current/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.message/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.compile_cmd/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:deref/;
+$external_latex_labels{$key} = q|6.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.bol/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.submenu/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.attach_tagged/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.br/;
+$external_latex_labels{$key} = q|6.2.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.managed_list/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:string.shell_safe/;
+$external_latex_labels{$key} = q|6.1.15|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_restart/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.managed_list/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.set/;
+$external_latex_labels{$key} = q|6.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.backspace/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.defstyle/;
+$external_latex_labels{$key} = q|6.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroupWS.attach_framed/;
+$external_latex_labels{$key} = q|6.1.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:dockref/;
+$external_latex_labels{$key} = q|6.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.set_floating_at/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.warn/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.current/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqclose_propagate/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.clear_mark/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/tiling_placement_alt/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.warn/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.begin_kbresize/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:classesrolesinstances/;
+$external_latex_labels{$key} = q|3.5.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_tagged/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.next_completion/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.set_hidden/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.cancel/;
+$external_latex_labels{$key} = q|6.4.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.nudge/;
+$external_latex_labels{$key} = q|6.1.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_nth_screen/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/clientwin_mapped_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.set_fullscreen/;
+$external_latex_labels{$key} = q|6.1.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.paste/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.xid/;
+$external_latex_labels{$key} = q|6.1.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.name/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_get_text_property/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_attachclient/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.append/;
+$external_latex_labels{$key} = q|6.1.16|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mclick/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.set_index/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_clear/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.p_switch_tab/;
+$external_latex_labels{$key} = q|6.1.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_intern_atom/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.finish/;
+$external_latex_labels{$key} = q|6.1.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_gotoclient/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_deinit_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.geom/;
+$external_latex_labels{$key} = q|6.2.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_previous/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.find_manager/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.get_paths/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:walkthrough/;
+$external_latex_labels{$key} = q|3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.set_numbers/;
+$external_latex_labels{$key} = q|6.1.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMoveresMode.cancel/;
+$external_latex_labels{$key} = q|6.1.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.submap/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_active/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.bkill_word/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.attach_new/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.attach/;
+$external_latex_labels{$key} = q|6.1.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mdblclick/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.flip/;
+$external_latex_labels{$key} = q|6.2.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split_top/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_activity_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.progname/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.popen_completions/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.defwinprop/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.popen_bgread/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mdrag/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WClientWin.kill/;
+$external_latex_labels{$key} = q|6.1.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplit.transpose/;
+$external_latex_labels{$key} = q|6.2.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_snapshot_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.clear_tags/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.map/;
+$external_latex_labels{$key} = q|6.1.16|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitSplit.dir/;
+$external_latex_labels{$key} = q|6.2.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WFrame.set_shaded/;
+$external_latex_labels{$key} = q|6.1.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.set_stdisp/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.manager/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.x_change_property/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInput.cancel/;
+$external_latex_labels{$key} = q|6.3.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.parent/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMenu.finish/;
+$external_latex_labels{$key} = q|6.4.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.back/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.TR/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:menus/;
+$external_latex_labels{$key} = q|3.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_renameframe/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.geom/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.kpress_wait/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/clientwin_do_manage_alt/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rootwin_of/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.transpose_at/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_sigchld_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.match_winprop_name/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.nextto/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.get/;
+$external_latex_labels{$key} = q|6.5.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.substyle/;
+$external_latex_labels{$key} = q|6.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.copy/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.is_histcompl/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getmenu/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.is_i18n/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:gr.refresh/;
+$external_latex_labels{$key} = q|6.1.14|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqorder/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.grabmenu/;
+$external_latex_labels{$key} = q|6.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WSplitRegion.reg/;
+$external_latex_labels{$key} = q|6.2.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.size_hints/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.delete/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.getwinprop/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.tagged_list/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.request_selection/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_man/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.cut/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.goto/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.goto_next_screen/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set_paths/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:conffiles/;
+$external_latex_labels{$key} = q|3.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.clientwin_list/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.aboutmsg/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:prelim/;
+$external_latex_labels{$key} = q|2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.managed_list/;
+$external_latex_labels{$key} = q|6.1.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:parentship/;
+$external_latex_labels{$key} = q|2.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.read_savefile/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.restart_other/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.is_activity/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.rqgeom/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WInput.scrolldown/;
+$external_latex_labels{$key} = q|6.3.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/region_activated_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.history_table/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:defaultde/;
+$external_latex_labels{$key} = q|4.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.set/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.mpress/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:tilingref/;
+$external_latex_labels{$key} = q|6.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_tiling.detach/;
+$external_latex_labels{$key} = q|6.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WRegion.set_name/;
+$external_latex_labels{$key} = q|6.1.9|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.join/;
+$external_latex_labels{$key} = q|6.1.16|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.activity_first/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:spref/;
+$external_latex_labels{$key} = q|6.6|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.is_hidden/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.get/;
+$external_latex_labels{$key} = q|6.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_yesno/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.history_next/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.eol/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.defcmd/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.bdoc/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:ioncore.chdir_for/;
+$external_latex_labels{$key} = q|6.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_lua/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:export/;
+$external_latex_labels{$key} = q|6.1.13|; 
+$noresave{$key} = "$nosave";
+
+$key = q/chap:gr/;
+$external_latex_labels{$key} = q|4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_menu.set/;
+$external_latex_labels{$key} = q|6.4|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_word/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:de.defstyle_rootwin/;
+$external_latex_labels{$key} = q|6.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/ioncore_post_layout_setup_hook/;
+$external_latex_labels{$key} = q|6.8|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:table.icat/;
+$external_latex_labels{$key} = q|6.1.16|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.kill_line/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WDock.resize/;
+$external_latex_labels{$key} = q|6.5.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:mod_query.query_shutdown/;
+$external_latex_labels{$key} = q|6.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WMPlex.get_index/;
+$external_latex_labels{$key} = q|6.1.7|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.skip_word/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.complete/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WGroup.attach_new/;
+$external_latex_labels{$key} = q|6.1.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WEdln.transpose_chars/;
+$external_latex_labels{$key} = q|6.3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fn:WTiling.split_tree/;
+$external_latex_labels{$key} = q|6.2.5|; 
+$noresave{$key} = "$nosave";
+
+1;
+
diff --git a/doc/ionconf/next.png b/doc/ionconf/next.png
new file mode 100644 (file)
index 0000000..1628652
Binary files /dev/null and b/doc/ionconf/next.png differ
diff --git a/doc/ionconf/next_g.png b/doc/ionconf/next_g.png
new file mode 100644 (file)
index 0000000..9d3f591
Binary files /dev/null and b/doc/ionconf/next_g.png differ
diff --git a/doc/ionconf/node1.html b/doc/ionconf/node1.html
new file mode 100644 (file)
index 0000000..ee25b4d
--- /dev/null
@@ -0,0 +1,276 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>Contents</TITLE>
+<META NAME="description" CONTENT="Contents">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node2.html">
+<LINK REL="previous" HREF="ionconf.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node2.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html130"
+  HREF="node2.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html126"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html120"
+  HREF="ionconf.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A>  
+<A NAME="tex2html128"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html131"
+  HREF="node2.html">1. Introduction</A>
+<B> Up:</B> <A NAME="tex2html127"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html121"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+ &nbsp; <B>  <A NAME="tex2html129"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<BR>
+
+<H2><A NAME="SECTION00100000000000000000">
+Contents</A>
+</H2>
+<!--Table of Contents-->
+
+<UL CLASS="TofC">
+<LI><A NAME="tex2html132"
+  HREF="node2.html">1. Introduction</A>
+<LI><A NAME="tex2html133"
+  HREF="node3.html">2. Preliminaries: Key concepts and relations</A>
+<UL>
+<LI><A NAME="tex2html134"
+  HREF="node3.html#SECTION00310000000000000000">2.1 Modules</A>
+<LI><A NAME="tex2html135"
+  HREF="node3.html#SECTION00320000000000000000">2.2 Class and object hierarchies</A>
+<UL>
+<LI><A NAME="tex2html136"
+  HREF="node3.html#SECTION00321000000000000000">2.2.1 Class hierarchy</A>
+<LI><A NAME="tex2html137"
+  HREF="node3.html#SECTION00322000000000000000">2.2.2 Object hierarchies: WRegion parents and managers</A>
+<LI><A NAME="tex2html138"
+  HREF="node3.html#SECTION00323000000000000000">2.2.3 Summary</A>
+</UL>
+</UL>
+<BR>
+<LI><A NAME="tex2html139"
+  HREF="node4.html">3. Basic configuration</A>
+<UL>
+<LI><A NAME="tex2html140"
+  HREF="node4.html#SECTION00410000000000000000">3.1 The configuration files</A>
+<LI><A NAME="tex2html141"
+  HREF="node4.html#SECTION00420000000000000000">3.2 A walk through cfg_ion.lua</A>
+<LI><A NAME="tex2html142"
+  HREF="node4.html#SECTION00430000000000000000">3.3 Keys and rodents</A>
+<UL>
+<LI><A NAME="tex2html143"
+  HREF="node4.html#SECTION00431000000000000000">3.3.1 Binding handlers and special variables</A>
+<LI><A NAME="tex2html144"
+  HREF="node4.html#SECTION00432000000000000000">3.3.2 Guards</A>
+<LI><A NAME="tex2html145"
+  HREF="node4.html#SECTION00433000000000000000">3.3.3 Defining the bindings</A>
+<LI><A NAME="tex2html146"
+  HREF="node4.html#SECTION00434000000000000000">3.3.4 Examples</A>
+<LI><A NAME="tex2html147"
+  HREF="node4.html#SECTION00435000000000000000">3.3.5 Key specifications</A>
+<LI><A NAME="tex2html148"
+  HREF="node4.html#SECTION00436000000000000000">3.3.6 Button specifications</A>
+<LI><A NAME="tex2html149"
+  HREF="node4.html#SECTION00437000000000000000">3.3.7 A further note on the default binding configuration</A>
+</UL>
+<LI><A NAME="tex2html150"
+  HREF="node4.html#SECTION00440000000000000000">3.4 Menus</A>
+<UL>
+<LI><A NAME="tex2html151"
+  HREF="node4.html#SECTION00441000000000000000">3.4.1 Defining menus</A>
+<LI><A NAME="tex2html152"
+  HREF="node4.html#SECTION00442000000000000000">3.4.2 Special menus</A>
+<LI><A NAME="tex2html153"
+  HREF="node4.html#SECTION00443000000000000000">3.4.3 Defining context menus</A>
+<LI><A NAME="tex2html154"
+  HREF="node4.html#SECTION00444000000000000000">3.4.4 Displaying menus</A>
+</UL>
+<LI><A NAME="tex2html155"
+  HREF="node4.html#SECTION00450000000000000000">3.5 Winprops</A>
+<UL>
+<LI><A NAME="tex2html156"
+  HREF="node4.html#SECTION00451000000000000000">3.5.1 Classes, roles and instances</A>
+<LI><A NAME="tex2html157"
+  HREF="node4.html#SECTION00452000000000000000">3.5.2 Finding window identification</A>
+<LI><A NAME="tex2html158"
+  HREF="node4.html#SECTION00453000000000000000">3.5.3 Some common examples</A>
+</UL>
+</UL>
+<BR>
+<LI><A NAME="tex2html159"
+  HREF="node5.html">4. Graphical styles</A>
+<UL>
+<LI><A NAME="tex2html160"
+  HREF="node5.html#SECTION00510000000000000000">4.1 Drawing engines, style specifications and sub-styles</A>
+<UL>
+<LI><A NAME="tex2html161"
+  HREF="node5.html#SECTION00511000000000000000">4.1.1 Known styles and substyles</A>
+</UL>
+<LI><A NAME="tex2html162"
+  HREF="node5.html#SECTION00520000000000000000">4.2 Defining styles for the default drawing engine</A>
+<UL>
+<LI><A NAME="tex2html163"
+  HREF="node5.html#SECTION00521000000000000000">4.2.1 The structure of the configuration files</A>
+<LI><A NAME="tex2html164"
+  HREF="node5.html#SECTION00522000000000000000">4.2.2 Defining the styles</A>
+<LI><A NAME="tex2html165"
+  HREF="node5.html#SECTION00523000000000000000">4.2.3 An example</A>
+</UL>
+<LI><A NAME="tex2html166"
+  HREF="node5.html#SECTION00530000000000000000">4.3 Miscellaneous settings</A>
+<UL>
+<LI><A NAME="tex2html167"
+  HREF="node5.html#SECTION00531000000000000000">4.3.1 Extra fields for style frame</A>
+<LI><A NAME="tex2html168"
+  HREF="node5.html#SECTION00532000000000000000">4.3.2 Extra fields for style dock</A>
+</UL>
+</UL>
+<BR>
+<LI><A NAME="tex2html169"
+  HREF="node6.html">5. Scripting</A>
+<UL>
+<LI><A NAME="tex2html170"
+  HREF="node6.html#SECTION00610000000000000000">5.1 Hooks</A>
+<LI><A NAME="tex2html171"
+  HREF="node6.html#SECTION00620000000000000000">5.2 Referring to regions</A>
+<UL>
+<LI><A NAME="tex2html172"
+  HREF="node6.html#SECTION00621000000000000000">5.2.1 Direct object references</A>
+<LI><A NAME="tex2html173"
+  HREF="node6.html#SECTION00622000000000000000">5.2.2 Name-based lookups</A>
+</UL>
+<LI><A NAME="tex2html174"
+  HREF="node6.html#SECTION00630000000000000000">5.3 Alternative winprop selection criteria</A>
+<LI><A NAME="tex2html175"
+  HREF="node6.html#SECTION00640000000000000000">5.4 Writing ion-statusd monitors</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html176"
+  HREF="node7.html">6. Function reference</A>
+<UL>
+<LI><A NAME="tex2html177"
+  HREF="node7.html#SECTION00710000000000000000">6.1 Functions defined in ioncore</A>
+<UL>
+<LI><A NAME="tex2html178"
+  HREF="node7.html#SECTION00711000000000000000">6.1.1 WClientWin functions</A>
+<LI><A NAME="tex2html179"
+  HREF="node7.html#SECTION00712000000000000000">6.1.2 WFrame functions</A>
+<LI><A NAME="tex2html180"
+  HREF="node7.html#SECTION00713000000000000000">6.1.3 WGroup functions</A>
+<LI><A NAME="tex2html181"
+  HREF="node7.html#SECTION00714000000000000000">6.1.4 WGroupCW functions</A>
+<LI><A NAME="tex2html182"
+  HREF="node7.html#SECTION00715000000000000000">6.1.5 WGroupWS functions</A>
+<LI><A NAME="tex2html183"
+  HREF="node7.html#SECTION00716000000000000000">6.1.6 WInfoWin functions</A>
+<LI><A NAME="tex2html184"
+  HREF="node7.html#SECTION00717000000000000000">6.1.7 WMPlex functions</A>
+<LI><A NAME="tex2html185"
+  HREF="node7.html#SECTION00718000000000000000">6.1.8 WMoveresMode functions</A>
+<LI><A NAME="tex2html186"
+  HREF="node7.html#SECTION00719000000000000000">6.1.9 WRegion functions</A>
+<LI><A NAME="tex2html187"
+  HREF="node7.html#SECTION007110000000000000000">6.1.10 WRootWin functions</A>
+<LI><A NAME="tex2html188"
+  HREF="node7.html#SECTION007111000000000000000">6.1.11 WScreen functions</A>
+<LI><A NAME="tex2html189"
+  HREF="node7.html#SECTION007112000000000000000">6.1.12 WWindow functions</A>
+<LI><A NAME="tex2html190"
+  HREF="node7.html#SECTION007113000000000000000">6.1.13 global functions</A>
+<LI><A NAME="tex2html191"
+  HREF="node7.html#SECTION007114000000000000000">6.1.14 gr functions</A>
+<LI><A NAME="tex2html192"
+  HREF="node7.html#SECTION007115000000000000000">6.1.15 string functions</A>
+<LI><A NAME="tex2html193"
+  HREF="node7.html#SECTION007116000000000000000">6.1.16 table functions</A>
+</UL>
+<LI><A NAME="tex2html194"
+  HREF="node7.html#SECTION00720000000000000000">6.2 Functions defined in mod_tiling</A>
+<UL>
+<LI><A NAME="tex2html195"
+  HREF="node7.html#SECTION00721000000000000000">6.2.1 WSplit functions</A>
+<LI><A NAME="tex2html196"
+  HREF="node7.html#SECTION00722000000000000000">6.2.2 WSplitInner functions</A>
+<LI><A NAME="tex2html197"
+  HREF="node7.html#SECTION00723000000000000000">6.2.3 WSplitRegion functions</A>
+<LI><A NAME="tex2html198"
+  HREF="node7.html#SECTION00724000000000000000">6.2.4 WSplitSplit functions</A>
+<LI><A NAME="tex2html199"
+  HREF="node7.html#SECTION00725000000000000000">6.2.5 WTiling functions</A>
+</UL>
+<LI><A NAME="tex2html200"
+  HREF="node7.html#SECTION00730000000000000000">6.3 Functions defined in mod_query</A>
+<UL>
+<LI><A NAME="tex2html201"
+  HREF="node7.html#SECTION00731000000000000000">6.3.1 WComplProxy functions</A>
+<LI><A NAME="tex2html202"
+  HREF="node7.html#SECTION00732000000000000000">6.3.2 WEdln functions</A>
+<LI><A NAME="tex2html203"
+  HREF="node7.html#SECTION00733000000000000000">6.3.3 WInput functions</A>
+</UL>
+<LI><A NAME="tex2html204"
+  HREF="node7.html#SECTION00740000000000000000">6.4 Functions defined in mod_menu</A>
+<UL>
+<LI><A NAME="tex2html205"
+  HREF="node7.html#SECTION00741000000000000000">6.4.1 WMenu functions</A>
+</UL>
+<LI><A NAME="tex2html206"
+  HREF="node7.html#SECTION00750000000000000000">6.5 Functions defined in mod_dock</A>
+<UL>
+<LI><A NAME="tex2html207"
+  HREF="node7.html#SECTION00751000000000000000">6.5.1 WDock functions</A>
+</UL>
+<LI><A NAME="tex2html208"
+  HREF="node7.html#SECTION00760000000000000000">6.6 Functions defined in mod_sp</A>
+<LI><A NAME="tex2html209"
+  HREF="node7.html#SECTION00770000000000000000">6.7 Functions defined in de</A>
+<LI><A NAME="tex2html210"
+  HREF="node7.html#SECTION00780000000000000000">6.8 Hooks</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html211"
+  HREF="node8.html">A. The GNU General Public License</A>
+<LI><A NAME="tex2html212"
+  HREF="node9.html">B. Full class hierarchy visible to Lua-side</A>
+<LI><A NAME="tex2html213"
+  HREF="node11.html">Index</A>
+</UL>
+<!--End of Table of Contents-->
+<P>
+
+<P>
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node10.html b/doc/ionconf/node10.html
new file mode 100644 (file)
index 0000000..f33c3b7
--- /dev/null
@@ -0,0 +1,377 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>List of functions</TITLE>
+<META NAME="description" CONTENT="List of functions">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node11.html">
+<LINK REL="previous" HREF="node9.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node11.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html425"
+  HREF="node11.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html419"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html413"
+  HREF="node9.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html421"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html423"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html426"
+  HREF="node11.html">Index</A>
+<B> Up:</B> <A NAME="tex2html420"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html414"
+  HREF="node9.html">B. Full class hierarchy</A>
+ &nbsp; <B>  <A NAME="tex2html422"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html424"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+
+<H1><A NAME="SECTION001000000000000000000">
+List of functions</A>
+</H1>
+
+<P>
+<A HREF="node7.html#fn:de.defstyle"><TT>de.defstyle</TT></A>
+<BR><A HREF="node7.html#fn:de.defstyle_rootwin"><TT>de.defstyle_rootwin</TT></A>
+<BR><A HREF="node7.html#fn:de.reset"><TT>de.reset</TT></A>
+<BR><A HREF="node7.html#fn:de.substyle"><TT>de.substyle</TT></A>
+<BR><A HREF="node7.html#fn:export"><TT>export</TT></A>
+<BR><A HREF="node7.html#fn:gr.read_config"><TT>gr.read_config</TT></A>
+<BR><A HREF="node7.html#fn:gr.refresh"><TT>gr.refresh</TT></A>
+<BR><A HREF="node7.html#fn:gr.select_engine"><TT>gr.select_engine</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.aboutmsg"><TT>ioncore.aboutmsg</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.activity_first"><TT>ioncore.activity_first</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.activity_list"><TT>ioncore.activity_list</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.bdoc"><TT>ioncore.bdoc</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.chdir_for"><TT>ioncore.chdir_for</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.clear_tags"><TT>ioncore.clear_tags</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.clientwin_list"><TT>ioncore.clientwin_list</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.compile_cmd"><TT>ioncore.compile_cmd</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.create_ws"><TT>ioncore.create_ws</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.current"><TT>ioncore.current</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.defbindings"><TT>ioncore.defbindings</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.defctxmenu"><TT>ioncore.defctxmenu</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.defmenu"><TT>ioncore.defmenu</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.defshortening"><TT>ioncore.defshortening</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.defwinprop"><TT>ioncore.defwinprop</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.exec"><TT>ioncore.exec</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.exec_on"><TT>ioncore.exec_on</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.find_manager"><TT>ioncore.find_manager</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.find_screen_id"><TT>ioncore.find_screen_id</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.get"><TT>ioncore.get</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.getbindings"><TT>ioncore.getbindings</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.getctxmenu"><TT>ioncore.getctxmenu</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.get_dir_for"><TT>ioncore.get_dir_for</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.getmenu"><TT>ioncore.getmenu</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.get_paths"><TT>ioncore.get_paths</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.get_savefile"><TT>ioncore.get_savefile</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.getwinprop"><TT>ioncore.getwinprop</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.goto_activity"><TT>ioncore.goto_activity</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.goto_first"><TT>ioncore.goto_first</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.goto_next"><TT>ioncore.goto_next</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.goto_next_screen"><TT>ioncore.goto_next_screen</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.goto_nth_screen"><TT>ioncore.goto_nth_screen</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.goto_previous"><TT>ioncore.goto_previous</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.goto_prev_screen"><TT>ioncore.goto_prev_screen</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.is_i18n"><TT>ioncore.is_i18n</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.kpress"><TT>ioncore.kpress</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.kpress_wait"><TT>ioncore.kpress_wait</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.load_module"><TT>ioncore.load_module</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.lookup_clientwin"><TT>ioncore.lookup_clientwin</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.lookup_region"><TT>ioncore.lookup_region</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.lookup_script"><TT>ioncore.lookup_script</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.match_winprop_name"><TT>ioncore.match_winprop_name</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.mclick"><TT>ioncore.mclick</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.mdblclick"><TT>ioncore.mdblclick</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.mdrag"><TT>ioncore.mdrag</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.menuentry"><TT>ioncore.menuentry</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.mpress"><TT>ioncore.mpress</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.navi_first"><TT>ioncore.navi_first</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.navi_next"><TT>ioncore.navi_next</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.popen_bgread"><TT>ioncore.popen_bgread</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.progname"><TT>ioncore.progname</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.read_savefile"><TT>ioncore.read_savefile</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.refresh_stylelist"><TT>ioncore.refresh_stylelist</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.region_list"><TT>ioncore.region_list</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.request_selection"><TT>ioncore.request_selection</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.resign"><TT>ioncore.resign</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.restart"><TT>ioncore.restart</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.restart_other"><TT>ioncore.restart_other</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.set"><TT>ioncore.set</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.set_get_winprop_fn"><TT>ioncore.set_get_winprop_fn</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.set_paths"><TT>ioncore.set_paths</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.set_selection"><TT>ioncore.set_selection</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.shutdown"><TT>ioncore.shutdown</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.snapshot"><TT>ioncore.snapshot</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.submap"><TT>ioncore.submap</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.submenu"><TT>ioncore.submenu</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.tagged_list"><TT>ioncore.tagged_list</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.tags_first"><TT>ioncore.tags_first</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.TR"><TT>ioncore.TR</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.version"><TT>ioncore.version</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.warn"><TT>ioncore.warn</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.warn_traced"><TT>ioncore.warn_traced</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.write_savefile"><TT>ioncore.write_savefile</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.x_change_property"><TT>ioncore.x_change_property</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.x_delete_property"><TT>ioncore.x_delete_property</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.x_get_atom_name"><TT>ioncore.x_get_atom_name</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.x_get_text_property"><TT>ioncore.x_get_text_property</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.x_get_window_property"><TT>ioncore.x_get_window_property</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.x_intern_atom"><TT>ioncore.x_intern_atom</TT></A>
+<BR><A HREF="node7.html#fn:ioncore.x_set_text_property"><TT>ioncore.x_set_text_property</TT></A>
+<BR><A HREF="node7.html#fn:mod_dock.set_floating_shown_on"><TT>mod_dock.set_floating_shown_on</TT></A>
+<BR><A HREF="node7.html#fn:mod_menu.get"><TT>mod_menu.get</TT></A>
+<BR><A HREF="node7.html#fn:mod_menu.grabmenu"><TT>mod_menu.grabmenu</TT></A>
+<BR><A HREF="node7.html#fn:mod_menu.menu"><TT>mod_menu.menu</TT></A>
+<BR><A HREF="node7.html#fn:mod_menu.pmenu"><TT>mod_menu.pmenu</TT></A>
+<BR><A HREF="node7.html#fn:mod_menu.set"><TT>mod_menu.set</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.defcmd"><TT>mod_query.defcmd</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.get"><TT>mod_query.get</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.history_clear"><TT>mod_query.history_clear</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.history_get"><TT>mod_query.history_get</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.history_push"><TT>mod_query.history_push</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.history_search"><TT>mod_query.history_search</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.history_table"><TT>mod_query.history_table</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.message"><TT>mod_query.message</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.popen_completions"><TT>mod_query.popen_completions</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query"><TT>mod_query.query</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_attachclient"><TT>mod_query.query_attachclient</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_editfile"><TT>mod_query.query_editfile</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_exec"><TT>mod_query.query_exec</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_gotoclient"><TT>mod_query.query_gotoclient</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_lua"><TT>mod_query.query_lua</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_man"><TT>mod_query.query_man</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_menu"><TT>mod_query.query_menu</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_renameframe"><TT>mod_query.query_renameframe</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_renameworkspace"><TT>mod_query.query_renameworkspace</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_restart"><TT>mod_query.query_restart</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_runfile"><TT>mod_query.query_runfile</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_shutdown"><TT>mod_query.query_shutdown</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_ssh"><TT>mod_query.query_ssh</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_workspace"><TT>mod_query.query_workspace</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.query_yesno"><TT>mod_query.query_yesno</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.set"><TT>mod_query.set</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.show_about_ion"><TT>mod_query.show_about_ion</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.show_tree"><TT>mod_query.show_tree</TT></A>
+<BR><A HREF="node7.html#fn:mod_query.warn"><TT>mod_query.warn</TT></A>
+<BR><A HREF="node7.html#fn:mod_sp.set_shown"><TT>mod_sp.set_shown</TT></A>
+<BR><A HREF="node7.html#fn:mod_sp.set_shown_on"><TT>mod_sp.set_shown_on</TT></A>
+<BR><A HREF="node7.html#fn:mod_tiling.detach"><TT>mod_tiling.detach</TT></A>
+<BR><A HREF="node7.html#fn:mod_tiling.get"><TT>mod_tiling.get</TT></A>
+<BR><A HREF="node7.html#fn:mod_tiling.mkbottom"><TT>mod_tiling.mkbottom</TT></A>
+<BR><A HREF="node7.html#fn:mod_tiling.set"><TT>mod_tiling.set</TT></A>
+<BR><A HREF="node7.html#fn:string.shell_safe"><TT>string.shell_safe</TT></A>
+<BR><A HREF="node7.html#fn:table.append"><TT>table.append</TT></A>
+<BR><A HREF="node7.html#fn:table.copy"><TT>table.copy</TT></A>
+<BR><A HREF="node7.html#fn:table.icat"><TT>table.icat</TT></A>
+<BR><A HREF="node7.html#fn:table.join"><TT>table.join</TT></A>
+<BR><A HREF="node7.html#fn:table.map"><TT>table.map</TT></A>
+<BR><A HREF="node7.html#fn:WClientWin.get_ident"><TT>WClientWin.get_ident</TT></A>
+<BR><A HREF="node7.html#fn:WClientWin.is_fullscreen"><TT>WClientWin.is_fullscreen</TT></A>
+<BR><A HREF="node7.html#fn:WClientWin.kill"><TT>WClientWin.kill</TT></A>
+<BR><A HREF="node7.html#fn:WClientWin.nudge"><TT>WClientWin.nudge</TT></A>
+<BR><A HREF="node7.html#fn:WClientWin.quote_next"><TT>WClientWin.quote_next</TT></A>
+<BR><A HREF="node7.html#fn:WClientWin.set_fullscreen"><TT>WClientWin.set_fullscreen</TT></A>
+<BR><A HREF="node7.html#fn:WClientWin.xid"><TT>WClientWin.xid</TT></A>
+<BR><A HREF="node7.html#fn:WComplProxy.set_completions"><TT>WComplProxy.set_completions</TT></A>
+<BR><A HREF="node7.html#fn:WDock.attach"><TT>WDock.attach</TT></A>
+<BR><A HREF="node7.html#fn:WDock.get"><TT>WDock.get</TT></A>
+<BR><A HREF="node7.html#fn:WDock.resize"><TT>WDock.resize</TT></A>
+<BR><A HREF="node7.html#fn:WDock.set"><TT>WDock.set</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.back"><TT>WEdln.back</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.backspace"><TT>WEdln.backspace</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.bkill_word"><TT>WEdln.bkill_word</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.bol"><TT>WEdln.bol</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.bskip_word"><TT>WEdln.bskip_word</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.clear_mark"><TT>WEdln.clear_mark</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.complete"><TT>WEdln.complete</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.contents"><TT>WEdln.contents</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.context"><TT>WEdln.context</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.copy"><TT>WEdln.copy</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.cut"><TT>WEdln.cut</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.delete"><TT>WEdln.delete</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.eol"><TT>WEdln.eol</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.finish"><TT>WEdln.finish</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.forward"><TT>WEdln.forward</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.history_next"><TT>WEdln.history_next</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.history_prev"><TT>WEdln.history_prev</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.insstr"><TT>WEdln.insstr</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.is_histcompl"><TT>WEdln.is_histcompl</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.kill_line"><TT>WEdln.kill_line</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.kill_to_bol"><TT>WEdln.kill_to_bol</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.kill_to_eol"><TT>WEdln.kill_to_eol</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.kill_word"><TT>WEdln.kill_word</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.mark"><TT>WEdln.mark</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.next_completion"><TT>WEdln.next_completion</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.paste"><TT>WEdln.paste</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.point"><TT>WEdln.point</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.prev_completion"><TT>WEdln.prev_completion</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.set_context"><TT>WEdln.set_context</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.set_mark"><TT>WEdln.set_mark</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.skip_word"><TT>WEdln.skip_word</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.transpose_chars"><TT>WEdln.transpose_chars</TT></A>
+<BR><A HREF="node7.html#fn:WEdln.transpose_words"><TT>WEdln.transpose_words</TT></A>
+<BR><A HREF="node7.html#fn:WFrame.is_shaded"><TT>WFrame.is_shaded</TT></A>
+<BR><A HREF="node7.html#fn:WFrame.maximize_horiz"><TT>WFrame.maximize_horiz</TT></A>
+<BR><A HREF="node7.html#fn:WFrame.maximize_vert"><TT>WFrame.maximize_vert</TT></A>
+<BR><A HREF="node7.html#fn:WFrame.mode"><TT>WFrame.mode</TT></A>
+<BR><A HREF="node7.html#fn:WFrame.p_switch_tab"><TT>WFrame.p_switch_tab</TT></A>
+<BR><A HREF="node7.html#fn:WFrame.p_tabdrag"><TT>WFrame.p_tabdrag</TT></A>
+<BR><A HREF="node7.html#fn:WFrame.set_mode"><TT>WFrame.set_mode</TT></A>
+<BR><A HREF="node7.html#fn:WFrame.set_numbers"><TT>WFrame.set_numbers</TT></A>
+<BR><A HREF="node7.html#fn:WFrame.set_shaded"><TT>WFrame.set_shaded</TT></A>
+<BR><A HREF="node7.html#fn:WGroup.attach"><TT>WGroup.attach</TT></A>
+<BR><A HREF="node7.html#fn:WGroup.attach_new"><TT>WGroup.attach_new</TT></A>
+<BR><A HREF="node7.html#fn:WGroup.bottom"><TT>WGroup.bottom</TT></A>
+<BR><A HREF="node7.html#fn:WGroup.managed_list"><TT>WGroup.managed_list</TT></A>
+<BR><A HREF="node7.html#fn:WGroupWS.attach_framed"><TT>WGroupWS.attach_framed</TT></A>
+<BR><A HREF="node7.html#fn:WInfoWin.set_text"><TT>WInfoWin.set_text</TT></A>
+<BR><A HREF="node7.html#fn:WInput.cancel"><TT>WInput.cancel</TT></A>
+<BR><A HREF="node7.html#fn:WInput.scrolldown"><TT>WInput.scrolldown</TT></A>
+<BR><A HREF="node7.html#fn:WInput.scrollup"><TT>WInput.scrollup</TT></A>
+<BR><A HREF="node7.html#fn:WMenu.cancel"><TT>WMenu.cancel</TT></A>
+<BR><A HREF="node7.html#fn:WMenu.finish"><TT>WMenu.finish</TT></A>
+<BR><A HREF="node7.html#fn:WMenu.select_next"><TT>WMenu.select_next</TT></A>
+<BR><A HREF="node7.html#fn:WMenu.select_nth"><TT>WMenu.select_nth</TT></A>
+<BR><A HREF="node7.html#fn:WMenu.select_prev"><TT>WMenu.select_prev</TT></A>
+<BR><A HREF="node7.html#fn:WMenu.typeahead_clear"><TT>WMenu.typeahead_clear</TT></A>
+<BR><A HREF="node7.html#fn:WMoveresMode.cancel"><TT>WMoveresMode.cancel</TT></A>
+<BR><A HREF="node7.html#fn:WMoveresMode.finish"><TT>WMoveresMode.finish</TT></A>
+<BR><A HREF="node7.html#fn:WMoveresMode.move"><TT>WMoveresMode.move</TT></A>
+<BR><A HREF="node7.html#fn:WMoveresMode.resize"><TT>WMoveresMode.resize</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.attach"><TT>WMPlex.attach</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.attach_new"><TT>WMPlex.attach_new</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.attach_tagged"><TT>WMPlex.attach_tagged</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.dec_index"><TT>WMPlex.dec_index</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.get_index"><TT>WMPlex.get_index</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.get_stdisp"><TT>WMPlex.get_stdisp</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.inc_index"><TT>WMPlex.inc_index</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.is_hidden"><TT>WMPlex.is_hidden</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.managed_list"><TT>WMPlex.managed_list</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.mx_count"><TT>WMPlex.mx_count</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.mx_current"><TT>WMPlex.mx_current</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.mx_list"><TT>WMPlex.mx_list</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.mx_nth"><TT>WMPlex.mx_nth</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.set_hidden"><TT>WMPlex.set_hidden</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.set_index"><TT>WMPlex.set_index</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.set_stdisp"><TT>WMPlex.set_stdisp</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.switch_next"><TT>WMPlex.switch_next</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.switch_nth"><TT>WMPlex.switch_nth</TT></A>
+<BR><A HREF="node7.html#fn:WMPlex.switch_prev"><TT>WMPlex.switch_prev</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.begin_kbresize"><TT>WRegion.begin_kbresize</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.current"><TT>WRegion.current</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.geom"><TT>WRegion.geom</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.goto"><TT>WRegion.goto</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.is_active"><TT>WRegion.is_active</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.is_activity"><TT>WRegion.is_activity</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.is_mapped"><TT>WRegion.is_mapped</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.is_tagged"><TT>WRegion.is_tagged</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.manager"><TT>WRegion.manager</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.name"><TT>WRegion.name</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.parent"><TT>WRegion.parent</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.rootwin_of"><TT>WRegion.rootwin_of</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.rqclose"><TT>WRegion.rqclose</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.rqclose_propagate"><TT>WRegion.rqclose_propagate</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.rqgeom"><TT>WRegion.rqgeom</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.rqorder"><TT>WRegion.rqorder</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.screen_of"><TT>WRegion.screen_of</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.set_activity"><TT>WRegion.set_activity</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.set_name"><TT>WRegion.set_name</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.set_name_exact"><TT>WRegion.set_name_exact</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.set_tagged"><TT>WRegion.set_tagged</TT></A>
+<BR><A HREF="node7.html#fn:WRegion.size_hints"><TT>WRegion.size_hints</TT></A>
+<BR><A HREF="node7.html#fn:WRootWin.current_scr"><TT>WRootWin.current_scr</TT></A>
+<BR><A HREF="node7.html#fn:WScreen.id"><TT>WScreen.id</TT></A>
+<BR><A HREF="node7.html#fn:WScreen.set_managed_offset"><TT>WScreen.set_managed_offset</TT></A>
+<BR><A HREF="node7.html#fn:WSplit.geom"><TT>WSplit.geom</TT></A>
+<BR><A HREF="node7.html#fn:WSplitInner.current"><TT>WSplitInner.current</TT></A>
+<BR><A HREF="node7.html#fn:WSplit.parent"><TT>WSplit.parent</TT></A>
+<BR><A HREF="node7.html#fn:WSplitRegion.reg"><TT>WSplitRegion.reg</TT></A>
+<BR><A HREF="node7.html#fn:WSplit.rqgeom"><TT>WSplit.rqgeom</TT></A>
+<BR><A HREF="node7.html#fn:WSplitSplit.br"><TT>WSplitSplit.br</TT></A>
+<BR><A HREF="node7.html#fn:WSplitSplit.dir"><TT>WSplitSplit.dir</TT></A>
+<BR><A HREF="node7.html#fn:WSplitSplit.flip"><TT>WSplitSplit.flip</TT></A>
+<BR><A HREF="node7.html#fn:WSplitSplit.tl"><TT>WSplitSplit.tl</TT></A>
+<BR><A HREF="node7.html#fn:WSplit.transpose"><TT>WSplit.transpose</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.farthest"><TT>WTiling.farthest</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.flip_at"><TT>WTiling.flip_at</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.managed_list"><TT>WTiling.managed_list</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.nextto"><TT>WTiling.nextto</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.node_of"><TT>WTiling.node_of</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.set_floating"><TT>WTiling.set_floating</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.set_floating_at"><TT>WTiling.set_floating_at</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.split"><TT>WTiling.split</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.split_at"><TT>WTiling.split_at</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.split_top"><TT>WTiling.split_top</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.split_tree"><TT>WTiling.split_tree</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.transpose_at"><TT>WTiling.transpose_at</TT></A>
+<BR><A HREF="node7.html#fn:WTiling.unsplit_at"><TT>WTiling.unsplit_at</TT></A>
+<BR><A HREF="node7.html#fn:WWindow.p_move"><TT>WWindow.p_move</TT></A>
+<BR><A HREF="node7.html#fn:WWindow.p_resize"><TT>WWindow.p_resize</TT></A>
+<BR><A HREF="node7.html#fn:WWindow.xid"><TT>WWindow.xid</TT></A>
+<BR>
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html425"
+  HREF="node11.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html419"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html413"
+  HREF="node9.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html421"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html423"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html426"
+  HREF="node11.html">Index</A>
+<B> Up:</B> <A NAME="tex2html420"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html414"
+  HREF="node9.html">B. Full class hierarchy</A>
+ &nbsp; <B>  <A NAME="tex2html422"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html424"
+  HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node11.html b/doc/ionconf/node11.html
new file mode 100644 (file)
index 0000000..2c12ab2
--- /dev/null
@@ -0,0 +1,1091 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>Index</TITLE>
+<META NAME="description" CONTENT="Index">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node12.html">
+<LINK REL="previous" HREF="node10.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node12.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html437"
+  HREF="node12.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html433"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html427"
+  HREF="node10.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html435"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A>  
+<BR>
+<B> Next:</B> <A NAME="tex2html438"
+  HREF="node12.html">About this document ...</A>
+<B> Up:</B> <A NAME="tex2html434"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html428"
+  HREF="node10.html">List of functions</A>
+ &nbsp; <B>  <A NAME="tex2html436"
+  HREF="node1.html">Contents</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<BR>
+
+<H2><A NAME="SECTION001100000000000000000">
+Index</A>
+</H2><HR><DL>
+<DD><STRONG><TT>aboutmsg</TT></STRONG>
+ : <A HREF="node7.html#4229"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>acrobatic</TT></STRONG>
+ : <A HREF="node4.html#1410"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>activity_first</TT></STRONG>
+ : <A HREF="node7.html#4231"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>activity_list</TT></STRONG>
+ : <A HREF="node7.html#4233"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textbf">Alt</SPAN></STRONG>
+ : <A HREF="node4.html#865"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textbf">AnyModifier</SPAN></STRONG>
+ : <A HREF="node4.html#863"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>append</TT></STRONG>
+ : <A HREF="node7.html#4520"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN></A>
+<DD><STRONG><TT>aspect</TT></STRONG>
+ : <A HREF="node4.html#1411"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>attach</TT></STRONG>
+ : <A HREF="node7.html#4384"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+ | <A HREF="node7.html#4400"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+ | <A HREF="node7.html#9001"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>attach_framed</TT></STRONG>
+ : <A HREF="node7.html#4394"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>attach_new</TT></STRONG>
+ : <A HREF="node7.html#4386"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+ | <A HREF="node7.html#4402"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>attach_tagged</TT></STRONG>
+ : <A HREF="node7.html#4404"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>back</TT></STRONG>
+ : <A HREF="node7.html#7896"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>backspace</TT></STRONG>
+ : <A HREF="node7.html#7898"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>bdoc</TT></STRONG>
+ : <A HREF="node7.html#4191"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>begin_kbresize</TT></STRONG>
+ : <A HREF="node7.html#4448"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>bkill_word</TT></STRONG>
+ : <A HREF="node7.html#7900"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>bol</TT></STRONG>
+ : <A HREF="node7.html#7902"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>bottom</TT></STRONG>
+ : <A HREF="node7.html#4388"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>br</TT></STRONG>
+ : <A HREF="node7.html#6806"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>bskip_word</TT></STRONG>
+ : <A HREF="node7.html#7904"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textbf">Button-n</SPAN></STRONG>
+ : <A HREF="node4.html#869"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">6</SPAN></A>
+<DD><STRONG><TT>cancel</TT></STRONG>
+ : <A HREF="node7.html#4439"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+ | <A HREF="node7.html#7963"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+ | <A HREF="node7.html#8789"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>chdir_for</TT></STRONG>
+ : <A HREF="node7.html#4193"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>class</TT></STRONG><DL>
+<DD><STRONG>winprop</STRONG> : <A HREF="node4.html#1426"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG><TT>clear_mark</TT></STRONG>
+ : <A HREF="node7.html#7906"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>clear_tags</TT></STRONG>
+ : <A HREF="node7.html#4235"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>clientwin_do_manage_alt</TT></STRONG>
+ : <A HREF="node7.html#9401"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>clientwin_list</TT></STRONG>
+ : <A HREF="node7.html#4237"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>clientwin_mapped_hook</TT></STRONG>
+ : <A HREF="node7.html#9402"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>clientwin_unmapped_hook</TT></STRONG>
+ : <A HREF="node7.html#9403"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>compile_cmd</TT></STRONG>
+ : <A HREF="node7.html#4195"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>complete</TT></STRONG>
+ : <A HREF="node7.html#7908"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>contents</TT></STRONG>
+ : <A HREF="node7.html#7910"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>context</TT></STRONG>
+ : <A HREF="node7.html#7912"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textbf">Control</SPAN></STRONG>
+ : <A HREF="node4.html#861"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>copy</TT></STRONG>
+ : <A HREF="node7.html#4522"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN></A>
+ | <A HREF="node7.html#7914"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>create_ws</TT></STRONG>
+ : <A HREF="node7.html#4197"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>current</TT></STRONG>
+ : <A HREF="node7.html#4239"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#4450"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+ | <A HREF="node7.html#6800"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>current_scr</TT></STRONG>
+ : <A HREF="node7.html#4493"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">10</SPAN></A>
+<DD><STRONG><TT>cut</TT></STRONG>
+ : <A HREF="node7.html#7916"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>de</TT></STRONG><DL>
+<DD><STRONG><TT>defstyle</TT></STRONG> : <A HREF="node7.html#9173"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>defstyle_rootwin</TT></STRONG> : <A HREF="node7.html#9175"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>reset</TT></STRONG> : <A HREF="node7.html#9177"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>substyle</TT></STRONG> : <A HREF="node7.html#9179"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+</DL>
+<DD><STRONG><TT>dec_index</TT></STRONG>
+ : <A HREF="node7.html#4406"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>defbindings</TT></STRONG>
+ : <A HREF="node7.html#4199"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>defcmd</TT></STRONG>
+ : <A HREF="node7.html#7833"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>defctxmenu</TT></STRONG>
+ : <A HREF="node7.html#4201"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>defmenu</TT></STRONG>
+ : <A HREF="node4.html#1170"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#4203"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>defshortening</TT></STRONG>
+ : <A HREF="node7.html#4241"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>defstyle</TT></STRONG>
+ : <A HREF="node7.html#9174"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>defstyle_rootwin</TT></STRONG>
+ : <A HREF="node7.html#9176"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>defwinprop</TT></STRONG>
+ : <A HREF="node7.html#4205"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>delete</TT></STRONG>
+ : <A HREF="node7.html#7918"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>detach</TT></STRONG>
+ : <A HREF="node7.html#6782"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>dir</TT></STRONG>
+ : <A HREF="node7.html#6808"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG>drawing engine</STRONG>
+ : <A HREF="node5.html#1666"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>eol</TT></STRONG>
+ : <A HREF="node7.html#7920"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>ETCDIR</TT></STRONG>
+ : <A HREF="node4.html#586"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>exec</TT></STRONG>
+ : <A HREF="node7.html#4243"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>exec_on</TT></STRONG>
+ : <A HREF="node7.html#4207"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>export</TT></STRONG>
+ : <A HREF="node7.html#4507"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">13</SPAN></A>
+<DD><STRONG><TT>farthest</TT></STRONG>
+ : <A HREF="node7.html#6819"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>find_manager</TT></STRONG>
+ : <A HREF="node7.html#4217"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>find_screen_id</TT></STRONG>
+ : <A HREF="node7.html#4245"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>finish</TT></STRONG>
+ : <A HREF="node7.html#4441"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+ | <A HREF="node7.html#7922"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+ | <A HREF="node7.html#8791"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>flip</TT></STRONG>
+ : <A HREF="node7.html#6810"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>flip_at</TT></STRONG>
+ : <A HREF="node7.html#6815"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>float</TT></STRONG>
+ : <A HREF="node4.html#1412"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>forward</TT></STRONG>
+ : <A HREF="node7.html#7924"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>frame_managed_changed_hook</TT></STRONG>
+ : <A HREF="node7.html#9404"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>fullscreen</TT></STRONG>
+ : <A HREF="node4.html#1413"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>geom</TT></STRONG>
+ : <A HREF="node7.html#4452"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+ | <A HREF="node7.html#6791"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get</TT></STRONG>
+ : <A HREF="node7.html#4247"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#6784"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+ | <A HREF="node7.html#7835"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+ | <A HREF="node7.html#8782"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+ | <A HREF="node7.html#9003"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get_dir_for</TT></STRONG>
+ : <A HREF="node7.html#4219"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get_ident</TT></STRONG>
+ : <A HREF="node7.html#4350"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get_index</TT></STRONG>
+ : <A HREF="node7.html#4408"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>get_paths</TT></STRONG>
+ : <A HREF="node7.html#4249"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get_savefile</TT></STRONG>
+ : <A HREF="node7.html#4211"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get_stdisp</TT></STRONG>
+ : <A HREF="node7.html#4410"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>getbindings</TT></STRONG>
+ : <A HREF="node7.html#4221"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>getctxmenu</TT></STRONG>
+ : <A HREF="node7.html#4223"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>getmenu</TT></STRONG>
+ : <A HREF="node7.html#4225"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>getwinprop</TT></STRONG>
+ : <A HREF="node7.html#4227"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto</TT></STRONG>
+ : <A HREF="node7.html#4454"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>goto_activity</TT></STRONG>
+ : <A HREF="node7.html#4251"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_first</TT></STRONG>
+ : <A HREF="node7.html#4253"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_next</TT></STRONG>
+ : <A HREF="node7.html#4255"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_next_screen</TT></STRONG>
+ : <A HREF="node7.html#4257"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_nth_screen</TT></STRONG>
+ : <A HREF="node7.html#4259"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_prev_screen</TT></STRONG>
+ : <A HREF="node7.html#4261"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_previous</TT></STRONG>
+ : <A HREF="node7.html#4263"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>gr</TT></STRONG><DL>
+<DD><STRONG><TT>read_config</TT></STRONG> : <A HREF="node7.html#4509"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">14</SPAN></A>
+<DD><STRONG><TT>refresh</TT></STRONG> : <A HREF="node7.html#4511"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">14</SPAN></A>
+<DD><STRONG><TT>select_engine</TT></STRONG> : <A HREF="node7.html#4513"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">14</SPAN></A>
+</DL>
+<DD><STRONG><TT>grabmenu</TT></STRONG>
+ : <A HREF="node7.html#8778"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>history_clear</TT></STRONG>
+ : <A HREF="node7.html#7837"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>history_get</TT></STRONG>
+ : <A HREF="node7.html#7839"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>history_next</TT></STRONG>
+ : <A HREF="node7.html#7926"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>history_prev</TT></STRONG>
+ : <A HREF="node7.html#7928"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>history_push</TT></STRONG>
+ : <A HREF="node7.html#7841"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>history_search</TT></STRONG>
+ : <A HREF="node7.html#7843"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>history_table</TT></STRONG>
+ : <A HREF="node7.html#7845"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>icat</TT></STRONG>
+ : <A HREF="node7.html#4524"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN></A>
+<DD><STRONG><TT>id</TT></STRONG>
+ : <A HREF="node7.html#4496"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">11</SPAN></A>
+<DD><STRONG><TT>ignore_cfgrq</TT></STRONG>
+ : <A HREF="node4.html#1414"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>ignore_net_active_window</TT></STRONG>
+ : <A HREF="node4.html#1415"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>ignore_resizeinc</TT></STRONG>
+ : <A HREF="node4.html#1416"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>inc_index</TT></STRONG>
+ : <A HREF="node7.html#4412"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>insstr</TT></STRONG>
+ : <A HREF="node7.html#7930"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>instance</TT></STRONG><DL>
+<DD><STRONG>winprop</STRONG> : <A HREF="node4.html#1428"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG><TT>ioncore</TT></STRONG><DL>
+<DD><STRONG><TT>aboutmsg</TT></STRONG> : <A HREF="node7.html#4228"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>activity_first</TT></STRONG> : <A HREF="node7.html#4230"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>activity_list</TT></STRONG> : <A HREF="node7.html#4232"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>bdoc</TT></STRONG> : <A HREF="node7.html#4190"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>chdir_for</TT></STRONG> : <A HREF="node7.html#4192"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>clear_tags</TT></STRONG> : <A HREF="node7.html#4234"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>clientwin_list</TT></STRONG> : <A HREF="node7.html#4236"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>compile_cmd</TT></STRONG> : <A HREF="node7.html#4194"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>create_ws</TT></STRONG> : <A HREF="node7.html#4196"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>current</TT></STRONG> : <A HREF="node7.html#4238"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>defbindings</TT></STRONG> : <A HREF="node7.html#4198"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>defctxmenu</TT></STRONG> : <A HREF="node7.html#4200"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>defmenu</TT></STRONG> : <A HREF="node7.html#4202"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>defshortening</TT></STRONG> : <A HREF="node7.html#4240"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>defwinprop</TT></STRONG> : <A HREF="node7.html#4204"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>exec</TT></STRONG> : <A HREF="node7.html#4242"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>exec_on</TT></STRONG> : <A HREF="node7.html#4206"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>find_manager</TT></STRONG> : <A HREF="node7.html#4216"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>find_screen_id</TT></STRONG> : <A HREF="node7.html#4244"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get</TT></STRONG> : <A HREF="node7.html#4246"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get_dir_for</TT></STRONG> : <A HREF="node7.html#4218"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get_paths</TT></STRONG> : <A HREF="node7.html#4248"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get_savefile</TT></STRONG> : <A HREF="node7.html#4210"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>getbindings</TT></STRONG> : <A HREF="node7.html#4220"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>getctxmenu</TT></STRONG> : <A HREF="node7.html#4222"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>getmenu</TT></STRONG> : <A HREF="node7.html#4224"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>getwinprop</TT></STRONG> : <A HREF="node7.html#4226"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_activity</TT></STRONG> : <A HREF="node7.html#4250"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_first</TT></STRONG> : <A HREF="node7.html#4252"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_next</TT></STRONG> : <A HREF="node7.html#4254"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_next_screen</TT></STRONG> : <A HREF="node7.html#4256"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_nth_screen</TT></STRONG> : <A HREF="node7.html#4258"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_prev_screen</TT></STRONG> : <A HREF="node7.html#4260"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>goto_previous</TT></STRONG> : <A HREF="node7.html#4262"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>is_i18n</TT></STRONG> : <A HREF="node7.html#4264"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>kpress</TT></STRONG> : <A HREF="node7.html#4326"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>kpress_wait</TT></STRONG> : <A HREF="node7.html#4328"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>load_module</TT></STRONG> : <A HREF="node7.html#4266"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>lookup_clientwin</TT></STRONG> : <A HREF="node7.html#4268"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>lookup_region</TT></STRONG> : <A HREF="node7.html#4270"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>lookup_script</TT></STRONG> : <A HREF="node7.html#4212"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>match_winprop_name</TT></STRONG> : <A HREF="node7.html#4330"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>mclick</TT></STRONG> : <A HREF="node7.html#4332"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>mdblclick</TT></STRONG> : <A HREF="node7.html#4334"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>mdrag</TT></STRONG> : <A HREF="node7.html#4336"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>menuentry</TT></STRONG> : <A HREF="node7.html#4338"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>mpress</TT></STRONG> : <A HREF="node7.html#4340"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>navi_first</TT></STRONG> : <A HREF="node7.html#4272"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>navi_next</TT></STRONG> : <A HREF="node7.html#4274"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>popen_bgread</TT></STRONG> : <A HREF="node7.html#4276"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>progname</TT></STRONG> : <A HREF="node7.html#4278"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>read_savefile</TT></STRONG> : <A HREF="node7.html#4208"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>refresh_stylelist</TT></STRONG> : <A HREF="node7.html#4342"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>region_list</TT></STRONG> : <A HREF="node7.html#4280"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>request_selection</TT></STRONG> : <A HREF="node7.html#4282"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>resign</TT></STRONG> : <A HREF="node7.html#4284"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>restart</TT></STRONG> : <A HREF="node7.html#4286"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>restart_other</TT></STRONG> : <A HREF="node7.html#4288"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set</TT></STRONG> : <A HREF="node7.html#4290"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set_get_winprop_fn</TT></STRONG> : <A HREF="node7.html#4292"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set_paths</TT></STRONG> : <A HREF="node7.html#4294"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set_selection</TT></STRONG> : <A HREF="node7.html#4296"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>shutdown</TT></STRONG> : <A HREF="node7.html#4298"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>snapshot</TT></STRONG> : <A HREF="node7.html#4300"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>submap</TT></STRONG> : <A HREF="node7.html#4344"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>submenu</TT></STRONG> : <A HREF="node7.html#4346"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>tagged_list</TT></STRONG> : <A HREF="node7.html#4302"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>tags_first</TT></STRONG> : <A HREF="node7.html#4304"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>TR</TT></STRONG> : <A HREF="node7.html#4188"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>version</TT></STRONG> : <A HREF="node7.html#4306"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>warn</TT></STRONG> : <A HREF="node7.html#4308"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>warn_traced</TT></STRONG> : <A HREF="node7.html#4310"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>write_savefile</TT></STRONG> : <A HREF="node7.html#4214"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_change_property</TT></STRONG> : <A HREF="node7.html#4312"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_delete_property</TT></STRONG> : <A HREF="node7.html#4314"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_get_atom_name</TT></STRONG> : <A HREF="node7.html#4316"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_get_text_property</TT></STRONG> : <A HREF="node7.html#4318"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_get_window_property</TT></STRONG> : <A HREF="node7.html#4320"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_intern_atom</TT></STRONG> : <A HREF="node7.html#4322"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_set_text_property</TT></STRONG> : <A HREF="node7.html#4324"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG><TT>ioncore_deinit_hook</TT></STRONG>
+ : <A HREF="node7.html#9406"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>ioncore_post_layout_setup_hook</TT></STRONG>
+ : <A HREF="node7.html#9407"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>ioncore_sigchld_hook</TT></STRONG>
+ : <A HREF="node7.html#9405"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>ioncore_snapshot_hook</TT></STRONG>
+ : <A HREF="node7.html#9408"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>is_active</TT></STRONG>
+ : <A HREF="node7.html#4456"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>is_activity</TT></STRONG>
+ : <A HREF="node7.html#4458"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>is_fullscreen</TT></STRONG>
+ : <A HREF="node7.html#4352"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>is_hidden</TT></STRONG>
+ : <A HREF="node7.html#4414"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>is_histcompl</TT></STRONG>
+ : <A HREF="node7.html#7932"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>is_i18n</TT></STRONG>
+ : <A HREF="node7.html#4265"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>is_mapped</TT></STRONG>
+ : <A HREF="node7.html#4460"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>is_shaded</TT></STRONG>
+ : <A HREF="node7.html#4365"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>is_tagged</TT></STRONG>
+ : <A HREF="node7.html#4462"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>join</TT></STRONG>
+ : <A HREF="node7.html#4526"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN></A>
+<DD><STRONG><TT>jumpto</TT></STRONG>
+ : <A HREF="node4.html#1417"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textit">keysymdef.h</SPAN></STRONG>
+ : <A HREF="node4.html#859"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>kill</TT></STRONG>
+ : <A HREF="node7.html#4354"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>kill_line</TT></STRONG>
+ : <A HREF="node7.html#7934"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>kill_to_bol</TT></STRONG>
+ : <A HREF="node7.html#7936"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>kill_to_eol</TT></STRONG>
+ : <A HREF="node7.html#7938"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>kill_word</TT></STRONG>
+ : <A HREF="node7.html#7940"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>kpress</TT></STRONG>
+ : <A HREF="node7.html#4327"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>kpress_wait</TT></STRONG>
+ : <A HREF="node7.html#4329"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>load_module</TT></STRONG>
+ : <A HREF="node7.html#4267"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textbf">Lock</SPAN></STRONG>
+ : <A HREF="node4.html#864"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>lookup_clientwin</TT></STRONG>
+ : <A HREF="node7.html#4269"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>lookup_region</TT></STRONG>
+ : <A HREF="node7.html#4271"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>lookup_script</TT></STRONG>
+ : <A HREF="node7.html#4213"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>managed_list</TT></STRONG>
+ : <A HREF="node7.html#4390"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+ | <A HREF="node7.html#4416"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+ | <A HREF="node7.html#6821"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>manager</TT></STRONG>
+ : <A HREF="node3.html#391"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#4464"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>map</TT></STRONG>
+ : <A HREF="node7.html#4528"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN></A>
+<DD><STRONG><TT>mark</TT></STRONG>
+ : <A HREF="node7.html#7942"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>match_winprop_name</TT></STRONG>
+ : <A HREF="node7.html#4331"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>max_size</TT></STRONG>
+ : <A HREF="node4.html#1418"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>maximize_horiz</TT></STRONG>
+ : <A HREF="node7.html#4367"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>maximize_vert</TT></STRONG>
+ : <A HREF="node7.html#4369"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>mclick</TT></STRONG>
+ : <A HREF="node7.html#4333"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>mdblclick</TT></STRONG>
+ : <A HREF="node7.html#4335"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>mdrag</TT></STRONG>
+ : <A HREF="node7.html#4337"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>menu</TT></STRONG>
+ : <A HREF="node7.html#8780"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>menuentry</TT></STRONG>
+ : <A HREF="node4.html#1171"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#4339"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>menus</STRONG>
+ : <A HREF="node4.html#1115"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>message</TT></STRONG>
+ : <A HREF="node7.html#7847"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>min_size</TT></STRONG>
+ : <A HREF="node4.html#1419"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>mkbottom</TT></STRONG>
+ : <A HREF="node7.html#6786"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>mod_dock</TT></STRONG><DL>
+<DD><STRONG><TT>set_floating_shown_on</TT></STRONG> : <A HREF="node7.html#8997"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+</DL>
+<DD><STRONG><TT>mod_menu</TT></STRONG><DL>
+<DD><STRONG><TT>get</TT></STRONG> : <A HREF="node7.html#8781"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>grabmenu</TT></STRONG> : <A HREF="node7.html#8777"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>menu</TT></STRONG> : <A HREF="node7.html#8779"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>pmenu</TT></STRONG> : <A HREF="node7.html#8785"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>set</TT></STRONG> : <A HREF="node7.html#8783"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+</DL>
+<DD><STRONG><TT>mod_query</TT></STRONG><DL>
+<DD><STRONG><TT>defcmd</TT></STRONG> : <A HREF="node7.html#7832"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>get</TT></STRONG> : <A HREF="node7.html#7834"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>history_clear</TT></STRONG> : <A HREF="node7.html#7836"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>history_get</TT></STRONG> : <A HREF="node7.html#7838"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>history_push</TT></STRONG> : <A HREF="node7.html#7840"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>history_search</TT></STRONG> : <A HREF="node7.html#7842"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>history_table</TT></STRONG> : <A HREF="node7.html#7844"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>message</TT></STRONG> : <A HREF="node7.html#7846"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>popen_completions</TT></STRONG> : <A HREF="node7.html#7852"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query</TT></STRONG> : <A HREF="node7.html#7854"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_attachclient</TT></STRONG> : <A HREF="node7.html#7856"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_editfile</TT></STRONG> : <A HREF="node7.html#7858"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_exec</TT></STRONG> : <A HREF="node7.html#7860"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_gotoclient</TT></STRONG> : <A HREF="node7.html#7862"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_lua</TT></STRONG> : <A HREF="node7.html#7864"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_man</TT></STRONG> : <A HREF="node7.html#7866"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_menu</TT></STRONG> : <A HREF="node7.html#7868"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_renameframe</TT></STRONG> : <A HREF="node7.html#7870"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_renameworkspace</TT></STRONG> : <A HREF="node7.html#7872"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_restart</TT></STRONG> : <A HREF="node7.html#7874"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_runfile</TT></STRONG> : <A HREF="node7.html#7876"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_shutdown</TT></STRONG> : <A HREF="node7.html#7878"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_ssh</TT></STRONG> : <A HREF="node7.html#7880"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_workspace</TT></STRONG> : <A HREF="node7.html#7883"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_yesno</TT></STRONG> : <A HREF="node7.html#7885"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>set</TT></STRONG> : <A HREF="node7.html#7848"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>show_about_ion</TT></STRONG> : <A HREF="node7.html#7887"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>show_tree</TT></STRONG> : <A HREF="node7.html#7889"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>warn</TT></STRONG> : <A HREF="node7.html#7850"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+</DL>
+<DD><STRONG><TT>mod_sp</TT></STRONG><DL>
+<DD><STRONG><TT>set_shown</TT></STRONG> : <A HREF="node7.html#9106"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">6</SPAN></A>
+<DD><STRONG><TT>set_shown_on</TT></STRONG> : <A HREF="node7.html#9108"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">6</SPAN></A>
+</DL>
+<DD><STRONG><TT>mod_tiling</TT></STRONG><DL>
+<DD><STRONG><TT>detach</TT></STRONG> : <A HREF="node7.html#6781"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>get</TT></STRONG> : <A HREF="node7.html#6783"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>mkbottom</TT></STRONG> : <A HREF="node7.html#6785"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set</TT></STRONG> : <A HREF="node7.html#6787"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+</DL>
+<DD><STRONG><TT>mode</TT></STRONG>
+ : <A HREF="node7.html#4371"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textbf">ModN</SPAN></STRONG>
+ : <A HREF="node4.html#862"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>move</TT></STRONG>
+ : <A HREF="node7.html#4443"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>mpress</TT></STRONG>
+ : <A HREF="node7.html#4341"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>mx_count</TT></STRONG>
+ : <A HREF="node7.html#4418"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>mx_current</TT></STRONG>
+ : <A HREF="node7.html#4420"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>mx_list</TT></STRONG>
+ : <A HREF="node7.html#4422"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>mx_nth</TT></STRONG>
+ : <A HREF="node7.html#4424"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>name</TT></STRONG>
+ : <A HREF="node7.html#4466"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>navi_first</TT></STRONG>
+ : <A HREF="node7.html#4273"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>navi_next</TT></STRONG>
+ : <A HREF="node7.html#4275"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>next_completion</TT></STRONG>
+ : <A HREF="node7.html#7944"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>nextto</TT></STRONG>
+ : <A HREF="node7.html#6823"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>node_of</TT></STRONG>
+ : <A HREF="node7.html#6825"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>nudge</TT></STRONG>
+ : <A HREF="node7.html#4356"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textbf">NumLock</SPAN></STRONG>
+ : <A HREF="node4.html#867"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG>Obj</STRONG>
+ : <A HREF="node3.html#419"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>oneshot</TT></STRONG>
+ : <A HREF="node4.html#1420"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>p_move</TT></STRONG>
+ : <A HREF="node7.html#4501"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">12</SPAN></A>
+<DD><STRONG><TT>p_resize</TT></STRONG>
+ : <A HREF="node7.html#4503"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">12</SPAN></A>
+<DD><STRONG><TT>p_switch_tab</TT></STRONG>
+ : <A HREF="node7.html#4373"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>p_tabdrag</TT></STRONG>
+ : <A HREF="node7.html#4375"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>panews_make_placement_alt</TT></STRONG>
+ : <A HREF="node7.html#9410"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>parent</TT></STRONG>
+ : <A HREF="node3.html#376"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#4468"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+ | <A HREF="node7.html#6793"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>paste</TT></STRONG>
+ : <A HREF="node7.html#7946"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>pmenu</TT></STRONG>
+ : <A HREF="node7.html#8786"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>point</TT></STRONG>
+ : <A HREF="node7.html#7948"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>popen_bgread</TT></STRONG>
+ : <A HREF="node7.html#4277"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>popen_completions</TT></STRONG>
+ : <A HREF="node7.html#7853"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>PREFIX</TT></STRONG>
+ : <A HREF="node4.html#585"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>prev_completion</TT></STRONG>
+ : <A HREF="node7.html#7950"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>progname</TT></STRONG>
+ : <A HREF="node7.html#4279"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>query</TT></STRONG>
+ : <A HREF="node7.html#7855"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_attachclient</TT></STRONG>
+ : <A HREF="node7.html#7857"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_editfile</TT></STRONG>
+ : <A HREF="node7.html#7859"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_exec</TT></STRONG>
+ : <A HREF="node7.html#7861"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_gotoclient</TT></STRONG>
+ : <A HREF="node7.html#7863"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_lua</TT></STRONG>
+ : <A HREF="node7.html#7865"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_man</TT></STRONG>
+ : <A HREF="node7.html#7867"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_menu</TT></STRONG>
+ : <A HREF="node7.html#7869"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_renameframe</TT></STRONG>
+ : <A HREF="node7.html#7871"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_renameworkspace</TT></STRONG>
+ : <A HREF="node7.html#7873"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_restart</TT></STRONG>
+ : <A HREF="node7.html#7875"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_runfile</TT></STRONG>
+ : <A HREF="node7.html#7877"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_shutdown</TT></STRONG>
+ : <A HREF="node7.html#7879"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_ssh</TT></STRONG>
+ : <A HREF="node7.html#7881"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_workspace</TT></STRONG>
+ : <A HREF="node7.html#7884"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>query_yesno</TT></STRONG>
+ : <A HREF="node7.html#7886"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>quote_next</TT></STRONG>
+ : <A HREF="node7.html#4358"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>read_config</TT></STRONG>
+ : <A HREF="node7.html#4510"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">14</SPAN></A>
+<DD><STRONG><TT>read_savefile</TT></STRONG>
+ : <A HREF="node7.html#4209"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>refresh</TT></STRONG>
+ : <A HREF="node7.html#4512"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">14</SPAN></A>
+<DD><STRONG><TT>refresh_stylelist</TT></STRONG>
+ : <A HREF="node7.html#4343"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>reg</TT></STRONG>
+ : <A HREF="node7.html#6803"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>region_activated_hook</TT></STRONG>
+ : <A HREF="node7.html#9411"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>region_activity_hook</TT></STRONG>
+ : <A HREF="node7.html#9412"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>region_do_warp_alt</TT></STRONG>
+ : <A HREF="node7.html#9413"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>region_inactivated_hook</TT></STRONG>
+ : <A HREF="node7.html#9414"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>region_list</TT></STRONG>
+ : <A HREF="node7.html#4281"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>request_selection</TT></STRONG>
+ : <A HREF="node7.html#4283"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>reset</TT></STRONG>
+ : <A HREF="node7.html#9178"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>resign</TT></STRONG>
+ : <A HREF="node7.html#4285"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>resize</TT></STRONG>
+ : <A HREF="node7.html#4445"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+ | <A HREF="node7.html#9005"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>restart</TT></STRONG>
+ : <A HREF="node7.html#4287"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>restart_other</TT></STRONG>
+ : <A HREF="node7.html#4289"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>role</TT></STRONG><DL>
+<DD><STRONG>winprop</STRONG> : <A HREF="node4.html#1427"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG>root window</STRONG>
+ : <A HREF="node3.html#336"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>rootwin_of</TT></STRONG>
+ : <A HREF="node7.html#4470"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>rqclose</TT></STRONG>
+ : <A HREF="node7.html#4472"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>rqclose_propagate</TT></STRONG>
+ : <A HREF="node7.html#4474"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>rqgeom</TT></STRONG>
+ : <A HREF="node7.html#4476"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+ | <A HREF="node7.html#6795"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>rqorder</TT></STRONG>
+ : <A HREF="node7.html#4478"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG>screen</STRONG><DL>
+<DD><STRONG>physical</STRONG> : <A HREF="node3.html#338"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>X</STRONG> : <A HREF="node3.html#337"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG><TT>screen_managed_changed_hook</TT></STRONG>
+ : <A HREF="node7.html#9415"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>screen_of</TT></STRONG>
+ : <A HREF="node7.html#4480"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>scrolldown</TT></STRONG>
+ : <A HREF="node7.html#7965"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textbf">ScrollLock</SPAN></STRONG>
+ : <A HREF="node4.html#868"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>scrollup</TT></STRONG>
+ : <A HREF="node7.html#7967"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>select_engine</TT></STRONG>
+ : <A HREF="node7.html#4514"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">14</SPAN></A>
+<DD><STRONG><TT>select_next</TT></STRONG>
+ : <A HREF="node7.html#8793"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>select_nth</TT></STRONG>
+ : <A HREF="node7.html#8795"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>select_prev</TT></STRONG>
+ : <A HREF="node7.html#8797"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set</TT></STRONG>
+ : <A HREF="node7.html#4291"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#6788"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+ | <A HREF="node7.html#7849"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+ | <A HREF="node7.html#8784"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+ | <A HREF="node7.html#9007"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set_activity</TT></STRONG>
+ : <A HREF="node7.html#4482"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>set_completions</TT></STRONG>
+ : <A HREF="node7.html#7893"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set_context</TT></STRONG>
+ : <A HREF="node7.html#7952"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set_floating</TT></STRONG>
+ : <A HREF="node7.html#6829"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>set_floating_at</TT></STRONG>
+ : <A HREF="node7.html#6827"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>set_floating_shown_on</TT></STRONG>
+ : <A HREF="node7.html#8998"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>set_fullscreen</TT></STRONG>
+ : <A HREF="node7.html#4360"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set_get_winprop_fn</TT></STRONG>
+ : <A HREF="node7.html#4293"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set_hidden</TT></STRONG>
+ : <A HREF="node7.html#4426"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>set_index</TT></STRONG>
+ : <A HREF="node7.html#4428"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>set_managed_offset</TT></STRONG>
+ : <A HREF="node7.html#4498"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">11</SPAN></A>
+<DD><STRONG><TT>set_mark</TT></STRONG>
+ : <A HREF="node7.html#7954"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set_mode</TT></STRONG>
+ : <A HREF="node7.html#4377"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set_name</TT></STRONG>
+ : <A HREF="node7.html#4484"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>set_name_exact</TT></STRONG>
+ : <A HREF="node7.html#4486"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>set_numbers</TT></STRONG>
+ : <A HREF="node7.html#4379"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set_paths</TT></STRONG>
+ : <A HREF="node7.html#4295"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set_selection</TT></STRONG>
+ : <A HREF="node7.html#4297"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set_shaded</TT></STRONG>
+ : <A HREF="node7.html#4381"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set_shown</TT></STRONG>
+ : <A HREF="node7.html#9107"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">6</SPAN></A>
+<DD><STRONG><TT>set_shown_on</TT></STRONG>
+ : <A HREF="node7.html#9109"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">6</SPAN></A>
+<DD><STRONG><TT>set_stdisp</TT></STRONG>
+ : <A HREF="node7.html#4430"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>set_tagged</TT></STRONG>
+ : <A HREF="node7.html#4488"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>set_text</TT></STRONG>
+ : <A HREF="node7.html#4397"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">6</SPAN></A>
+<DD><STRONG><TT>shell_safe</TT></STRONG>
+ : <A HREF="node7.html#4517"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">15</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textbf">Shift</SPAN></STRONG>
+ : <A HREF="node4.html#860"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>show_about_ion</TT></STRONG>
+ : <A HREF="node7.html#7888"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>show_tree</TT></STRONG>
+ : <A HREF="node7.html#7890"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>shutdown</TT></STRONG>
+ : <A HREF="node7.html#4299"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>size_hints</TT></STRONG>
+ : <A HREF="node7.html#4490"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>skip_word</TT></STRONG>
+ : <A HREF="node7.html#7956"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>snapshot</TT></STRONG>
+ : <A HREF="node7.html#4301"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>split</TT></STRONG>
+ : <A HREF="node7.html#6831"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>split_at</TT></STRONG>
+ : <A HREF="node7.html#6833"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>split_top</TT></STRONG>
+ : <A HREF="node7.html#6835"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>split_tree</TT></STRONG>
+ : <A HREF="node7.html#6837"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>string</TT></STRONG><DL>
+<DD><STRONG><TT>shell_safe</TT></STRONG> : <A HREF="node7.html#4516"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">15</SPAN></A>
+</DL>
+<DD><STRONG>style</STRONG>
+ : <A HREF="node5.html#1665"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>submap</TT></STRONG>
+ : <A HREF="node7.html#4345"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>submenu</TT></STRONG>
+ : <A HREF="node4.html#1172"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#4347"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>substyle</TT></STRONG>
+ : <A HREF="node5.html#1677"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#9180"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>switch_next</TT></STRONG>
+ : <A HREF="node7.html#4432"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>switch_nth</TT></STRONG>
+ : <A HREF="node7.html#4434"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>switch_prev</TT></STRONG>
+ : <A HREF="node7.html#4436"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>switchto</TT></STRONG>
+ : <A HREF="node4.html#1421"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textit">system.mk</SPAN></STRONG>
+ : <A HREF="node4.html#587"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>table</TT></STRONG><DL>
+<DD><STRONG><TT>append</TT></STRONG> : <A HREF="node7.html#4519"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN></A>
+<DD><STRONG><TT>copy</TT></STRONG> : <A HREF="node7.html#4521"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN></A>
+<DD><STRONG><TT>icat</TT></STRONG> : <A HREF="node7.html#4523"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN></A>
+<DD><STRONG><TT>join</TT></STRONG> : <A HREF="node7.html#4525"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN></A>
+<DD><STRONG><TT>map</TT></STRONG> : <A HREF="node7.html#4527"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN></A>
+</DL>
+<DD><STRONG><TT>tagged_list</TT></STRONG>
+ : <A HREF="node7.html#4303"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>tags_first</TT></STRONG>
+ : <A HREF="node7.html#4305"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>target</TT></STRONG>
+ : <A HREF="node4.html#1422"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>tiling_placement_alt</TT></STRONG>
+ : <A HREF="node7.html#9409"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>tl</TT></STRONG>
+ : <A HREF="node7.html#6812"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>TR</TT></STRONG>
+ : <A HREF="node7.html#4189"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>transient</STRONG>
+ : <A HREF="node4.html#1387"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>transient_mode</TT></STRONG>
+ : <A HREF="node4.html#1423"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>transients_at_top</TT></STRONG>
+ : <A HREF="node4.html#1424"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>transparent</TT></STRONG>
+ : <A HREF="node4.html#1425"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>transpose</TT></STRONG>
+ : <A HREF="node7.html#6797"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>transpose_at</TT></STRONG>
+ : <A HREF="node7.html#6817"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>transpose_chars</TT></STRONG>
+ : <A HREF="node7.html#7958"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>transpose_words</TT></STRONG>
+ : <A HREF="node7.html#7960"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>typeahead_clear</TT></STRONG>
+ : <A HREF="node7.html#8799"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>unsplit_at</TT></STRONG>
+ : <A HREF="node7.html#6839"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>version</TT></STRONG>
+ : <A HREF="node7.html#4307"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>warn</TT></STRONG>
+ : <A HREF="node7.html#4309"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#7851"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>warn_traced</TT></STRONG>
+ : <A HREF="node7.html#4311"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>WClientWin</TT></STRONG>
+ : <A HREF="node3.html#421"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>get_ident</TT></STRONG> : <A HREF="node7.html#4349"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>is_fullscreen</TT></STRONG> : <A HREF="node7.html#4351"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>kill</TT></STRONG> : <A HREF="node7.html#4353"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>nudge</TT></STRONG> : <A HREF="node7.html#4355"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>quote_next</TT></STRONG> : <A HREF="node7.html#4357"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set_fullscreen</TT></STRONG> : <A HREF="node7.html#4359"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>xid</TT></STRONG> : <A HREF="node7.html#4361"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG><TT>WComplProxy</TT></STRONG><DL>
+<DD><STRONG><TT>set_completions</TT></STRONG> : <A HREF="node7.html#7892"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG><TT>WDock</TT></STRONG><DL>
+<DD><STRONG><TT>attach</TT></STRONG> : <A HREF="node7.html#9000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>get</TT></STRONG> : <A HREF="node7.html#9002"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>resize</TT></STRONG> : <A HREF="node7.html#9004"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>set</TT></STRONG> : <A HREF="node7.html#9006"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG><TT>WEdln</TT></STRONG>
+ : <A HREF="node3.html#434"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>back</TT></STRONG> : <A HREF="node7.html#7895"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>backspace</TT></STRONG> : <A HREF="node7.html#7897"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>bkill_word</TT></STRONG> : <A HREF="node7.html#7899"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>bol</TT></STRONG> : <A HREF="node7.html#7901"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>bskip_word</TT></STRONG> : <A HREF="node7.html#7903"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>clear_mark</TT></STRONG> : <A HREF="node7.html#7905"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>complete</TT></STRONG> : <A HREF="node7.html#7907"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>contents</TT></STRONG> : <A HREF="node7.html#7909"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>context</TT></STRONG> : <A HREF="node7.html#7911"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>copy</TT></STRONG> : <A HREF="node7.html#7913"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>cut</TT></STRONG> : <A HREF="node7.html#7915"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>delete</TT></STRONG> : <A HREF="node7.html#7917"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>eol</TT></STRONG> : <A HREF="node7.html#7919"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>finish</TT></STRONG> : <A HREF="node7.html#7921"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>forward</TT></STRONG> : <A HREF="node7.html#7923"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>history_next</TT></STRONG> : <A HREF="node7.html#7925"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>history_prev</TT></STRONG> : <A HREF="node7.html#7927"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>insstr</TT></STRONG> : <A HREF="node7.html#7929"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>is_histcompl</TT></STRONG> : <A HREF="node7.html#7931"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>kill_line</TT></STRONG> : <A HREF="node7.html#7933"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>kill_to_bol</TT></STRONG> : <A HREF="node7.html#7935"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>kill_to_eol</TT></STRONG> : <A HREF="node7.html#7937"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>kill_word</TT></STRONG> : <A HREF="node7.html#7939"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>mark</TT></STRONG> : <A HREF="node7.html#7941"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>next_completion</TT></STRONG> : <A HREF="node7.html#7943"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>paste</TT></STRONG> : <A HREF="node7.html#7945"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>point</TT></STRONG> : <A HREF="node7.html#7947"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>prev_completion</TT></STRONG> : <A HREF="node7.html#7949"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set_context</TT></STRONG> : <A HREF="node7.html#7951"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set_mark</TT></STRONG> : <A HREF="node7.html#7953"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>skip_word</TT></STRONG> : <A HREF="node7.html#7955"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>transpose_chars</TT></STRONG> : <A HREF="node7.html#7957"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>transpose_words</TT></STRONG> : <A HREF="node7.html#7959"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+</DL>
+<DD><STRONG><TT>WFrame</TT></STRONG>
+ : <A HREF="node3.html#425"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>is_shaded</TT></STRONG> : <A HREF="node7.html#4364"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>maximize_horiz</TT></STRONG> : <A HREF="node7.html#4366"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>maximize_vert</TT></STRONG> : <A HREF="node7.html#4368"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>mode</TT></STRONG> : <A HREF="node7.html#4370"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>p_switch_tab</TT></STRONG> : <A HREF="node7.html#4372"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>p_tabdrag</TT></STRONG> : <A HREF="node7.html#4374"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set_mode</TT></STRONG> : <A HREF="node7.html#4376"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set_numbers</TT></STRONG> : <A HREF="node7.html#4378"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>set_shaded</TT></STRONG> : <A HREF="node7.html#4380"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+</DL>
+<DD><STRONG><TT>WGroup</TT></STRONG>
+ : <A HREF="node3.html#426"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>attach</TT></STRONG> : <A HREF="node7.html#4383"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>attach_new</TT></STRONG> : <A HREF="node7.html#4385"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>bottom</TT></STRONG> : <A HREF="node7.html#4387"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>managed_list</TT></STRONG> : <A HREF="node7.html#4389"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+</DL>
+<DD><STRONG>WGroupCW</STRONG>
+ : <A HREF="node3.html#428"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>WGroupWS</TT></STRONG>
+ : <A HREF="node3.html#427"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>attach_framed</TT></STRONG> : <A HREF="node7.html#4393"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+</DL>
+<DD><STRONG><TT>WInfoWin</TT></STRONG><DL>
+<DD><STRONG><TT>set_text</TT></STRONG> : <A HREF="node7.html#4396"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">6</SPAN></A>
+</DL>
+<DD><STRONG>Winprops</STRONG>
+ : <A HREF="node4.html#1266"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>WInput</TT></STRONG>
+ : <A HREF="node3.html#433"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>cancel</TT></STRONG> : <A HREF="node7.html#7962"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>scrolldown</TT></STRONG> : <A HREF="node7.html#7964"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>scrollup</TT></STRONG> : <A HREF="node7.html#7966"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+</DL>
+<DD><STRONG><TT>WMenu</TT></STRONG><DL>
+<DD><STRONG><TT>cancel</TT></STRONG> : <A HREF="node7.html#8788"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>finish</TT></STRONG> : <A HREF="node7.html#8790"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>select_next</TT></STRONG> : <A HREF="node7.html#8792"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>select_nth</TT></STRONG> : <A HREF="node7.html#8794"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>select_prev</TT></STRONG> : <A HREF="node7.html#8796"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>typeahead_clear</TT></STRONG> : <A HREF="node7.html#8798"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG>WMessage</STRONG>
+ : <A HREF="node3.html#435"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>WMoveresMode</TT></STRONG><DL>
+<DD><STRONG><TT>cancel</TT></STRONG> : <A HREF="node7.html#4438"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>finish</TT></STRONG> : <A HREF="node7.html#4440"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>move</TT></STRONG> : <A HREF="node7.html#4442"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+<DD><STRONG><TT>resize</TT></STRONG> : <A HREF="node7.html#4444"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN></A>
+</DL>
+<DD><STRONG><TT>WMPlex</TT></STRONG><DL>
+<DD><STRONG><TT>attach</TT></STRONG> : <A HREF="node7.html#4399"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>attach_new</TT></STRONG> : <A HREF="node7.html#4401"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>attach_tagged</TT></STRONG> : <A HREF="node7.html#4403"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>dec_index</TT></STRONG> : <A HREF="node7.html#4405"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>get_index</TT></STRONG> : <A HREF="node7.html#4407"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>get_stdisp</TT></STRONG> : <A HREF="node7.html#4409"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>inc_index</TT></STRONG> : <A HREF="node7.html#4411"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>is_hidden</TT></STRONG> : <A HREF="node7.html#4413"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>managed_list</TT></STRONG> : <A HREF="node7.html#4415"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>mx_count</TT></STRONG> : <A HREF="node7.html#4417"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>mx_current</TT></STRONG> : <A HREF="node7.html#4419"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>mx_list</TT></STRONG> : <A HREF="node7.html#4421"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>mx_nth</TT></STRONG> : <A HREF="node7.html#4423"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>set_hidden</TT></STRONG> : <A HREF="node7.html#4425"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>set_index</TT></STRONG> : <A HREF="node7.html#4427"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>set_stdisp</TT></STRONG> : <A HREF="node7.html#4429"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>switch_next</TT></STRONG> : <A HREF="node7.html#4431"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>switch_nth</TT></STRONG> : <A HREF="node7.html#4433"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+<DD><STRONG><TT>switch_prev</TT></STRONG> : <A HREF="node7.html#4435"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN></A>
+</DL>
+<DD><STRONG><TT>WRegion</TT></STRONG>
+ : <A HREF="node3.html#420"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>begin_kbresize</TT></STRONG> : <A HREF="node7.html#4447"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>current</TT></STRONG> : <A HREF="node7.html#4449"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>geom</TT></STRONG> : <A HREF="node7.html#4451"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>goto</TT></STRONG> : <A HREF="node7.html#4453"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>is_active</TT></STRONG> : <A HREF="node7.html#4455"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>is_activity</TT></STRONG> : <A HREF="node7.html#4457"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>is_mapped</TT></STRONG> : <A HREF="node7.html#4459"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>is_tagged</TT></STRONG> : <A HREF="node7.html#4461"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>manager</TT></STRONG> : <A HREF="node7.html#4463"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>name</TT></STRONG> : <A HREF="node7.html#4465"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>parent</TT></STRONG> : <A HREF="node7.html#4467"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>rootwin_of</TT></STRONG> : <A HREF="node7.html#4469"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>rqclose</TT></STRONG> : <A HREF="node7.html#4471"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>rqclose_propagate</TT></STRONG> : <A HREF="node7.html#4473"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>rqgeom</TT></STRONG> : <A HREF="node7.html#4475"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>rqorder</TT></STRONG> : <A HREF="node7.html#4477"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>screen_of</TT></STRONG> : <A HREF="node7.html#4479"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>set_activity</TT></STRONG> : <A HREF="node7.html#4481"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>set_name</TT></STRONG> : <A HREF="node7.html#4483"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>set_name_exact</TT></STRONG> : <A HREF="node7.html#4485"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>set_tagged</TT></STRONG> : <A HREF="node7.html#4487"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+<DD><STRONG><TT>size_hints</TT></STRONG> : <A HREF="node7.html#4489"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN></A>
+</DL>
+<DD><STRONG><TT>write_savefile</TT></STRONG>
+ : <A HREF="node7.html#4215"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>WRootWin</TT></STRONG>
+ : <A HREF="node3.html#423"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>current_scr</TT></STRONG> : <A HREF="node7.html#4492"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">10</SPAN></A>
+</DL>
+<DD><STRONG><TT>WScreen</TT></STRONG>
+ : <A HREF="node3.html#424"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>id</TT></STRONG> : <A HREF="node7.html#4495"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">11</SPAN></A>
+<DD><STRONG><TT>set_managed_offset</TT></STRONG> : <A HREF="node7.html#4497"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">11</SPAN></A>
+</DL>
+<DD><STRONG><TT>WSplit</TT></STRONG>
+ : <A HREF="node3.html#431"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>geom</TT></STRONG> : <A HREF="node7.html#6790"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>parent</TT></STRONG> : <A HREF="node7.html#6792"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>rqgeom</TT></STRONG> : <A HREF="node7.html#6794"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>transpose</TT></STRONG> : <A HREF="node7.html#6796"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG><TT>WSplitInner</TT></STRONG><DL>
+<DD><STRONG><TT>current</TT></STRONG> : <A HREF="node7.html#6799"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+</DL>
+<DD><STRONG><TT>WSplitRegion</TT></STRONG><DL>
+<DD><STRONG><TT>reg</TT></STRONG> : <A HREF="node7.html#6802"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+</DL>
+<DD><STRONG><TT>WSplitSplit</TT></STRONG><DL>
+<DD><STRONG><TT>br</TT></STRONG> : <A HREF="node7.html#6805"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>dir</TT></STRONG> : <A HREF="node7.html#6807"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>flip</TT></STRONG> : <A HREF="node7.html#6809"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG><TT>tl</TT></STRONG> : <A HREF="node7.html#6811"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+</DL>
+<DD><STRONG><TT>WTiling</TT></STRONG>
+ : <A HREF="node3.html#430"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>farthest</TT></STRONG> : <A HREF="node7.html#6818"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>flip_at</TT></STRONG> : <A HREF="node7.html#6814"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>managed_list</TT></STRONG> : <A HREF="node7.html#6820"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>nextto</TT></STRONG> : <A HREF="node7.html#6822"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>node_of</TT></STRONG> : <A HREF="node7.html#6824"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>set_floating</TT></STRONG> : <A HREF="node7.html#6828"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>set_floating_at</TT></STRONG> : <A HREF="node7.html#6826"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>split</TT></STRONG> : <A HREF="node7.html#6830"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>split_at</TT></STRONG> : <A HREF="node7.html#6832"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>split_top</TT></STRONG> : <A HREF="node7.html#6834"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>split_tree</TT></STRONG> : <A HREF="node7.html#6836"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>transpose_at</TT></STRONG> : <A HREF="node7.html#6816"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG><TT>unsplit_at</TT></STRONG> : <A HREF="node7.html#6838"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+</DL>
+<DD><STRONG><TT>WWindow</TT></STRONG>
+ : <A HREF="node3.html#422"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DL>
+<DD><STRONG><TT>p_move</TT></STRONG> : <A HREF="node7.html#4500"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">12</SPAN></A>
+<DD><STRONG><TT>p_resize</TT></STRONG> : <A HREF="node7.html#4502"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">12</SPAN></A>
+<DD><STRONG><TT>xid</TT></STRONG> : <A HREF="node7.html#4504"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">12</SPAN></A>
+</DL>
+<DD><STRONG><TT>x_change_property</TT></STRONG>
+ : <A HREF="node7.html#4313"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_delete_property</TT></STRONG>
+ : <A HREF="node7.html#4315"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_get_atom_name</TT></STRONG>
+ : <A HREF="node7.html#4317"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_get_text_property</TT></STRONG>
+ : <A HREF="node7.html#4319"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_get_window_property</TT></STRONG>
+ : <A HREF="node7.html#4321"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_intern_atom</TT></STRONG>
+ : <A HREF="node7.html#4323"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>x_set_text_property</TT></STRONG>
+ : <A HREF="node7.html#4325"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>xid</TT></STRONG>
+ : <A HREF="node7.html#4362"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+ | <A HREF="node7.html#4505"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">12</SPAN></A>
+<DD><STRONG>Xinerama</STRONG>
+ : <A HREF="node3.html#339"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><SPAN  CLASS="textit">xmodmap</SPAN></STRONG>
+ : <A HREF="node4.html#866"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN></A>
+<DD><STRONG>xprop</STRONG>
+ : <A HREF="node4.html#1383"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+
+</DL>
+
+<P>
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node12.html b/doc/ionconf/node12.html
new file mode 100644 (file)
index 0000000..90201d6
--- /dev/null
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>About this document ...</TITLE>
+<META NAME="description" CONTENT="About this document ...">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="previous" HREF="node11.html">
+<LINK REL="up" HREF="ionconf.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next_g.png"> 
+<A NAME="tex2html443"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html439"
+  HREF="node11.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html445"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html447"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Up:</B> <A NAME="tex2html444"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html440"
+  HREF="node11.html">Index</A>
+ &nbsp; <B>  <A NAME="tex2html446"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html448"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+
+<H1><A NAME="SECTION001200000000000000000">
+About this document ...</A>
+</H1>
+ <STRONG>Configuring and extending Ion3 with Lua</STRONG><P>
+This document was generated using the
+<A HREF="http://www.latex2html.org/"><STRONG>LaTeX</STRONG>2<tt>HTML</tt></A> translator Version 2002-2-1 (1.71)
+<P>
+Copyright &#169; 1993, 1994, 1995, 1996,
+Nikos Drakos, 
+Computer Based Learning Unit, University of Leeds.
+<BR>
+Copyright &#169; 1997, 1998, 1999,
+<A HREF="http://www.maths.mq.edu.au/~ross/">Ross Moore</A>, 
+Mathematics Department, Macquarie University, Sydney.
+<P>
+The command line arguments were: <BR>
+ <STRONG>latex2html</STRONG> <TT>-show_section_numbers -short_index -local_icons -noaddress -up_url http://iki.fi/tuomov/ion/ -up_title 'Ion homepage' -nofootnode -split 3 ionconf</TT>
+<P>
+The translation was initiated by tuomov on 2006-12-23
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node2.html b/doc/ionconf/node2.html
new file mode 100644 (file)
index 0000000..9d85352
--- /dev/null
@@ -0,0 +1,163 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>1. Introduction</TITLE>
+<META NAME="description" CONTENT="1. Introduction">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node3.html">
+<LINK REL="previous" HREF="node1.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node3.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html226"
+  HREF="node3.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html220"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html214"
+  HREF="node1.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html222"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html224"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html227"
+  HREF="node3.html">2. Preliminaries: Key concepts</A>
+<B> Up:</B> <A NAME="tex2html221"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html215"
+  HREF="node1.html">Contents</A>
+ &nbsp; <B>  <A NAME="tex2html223"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html225"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+
+<H1><A NAME="SECTION00200000000000000000">
+<SPAN CLASS="arabic">1</SPAN>. Introduction</A>
+</H1>
+
+<P>
+This document is an ''advanced user'' manual for Ion, the X11 window manager,
+and version 3 specifically. It is an attempt attempt at documenting what is 
+in Ion's configuration files, how to configure Ion by simple modifications 
+to these files and how to write more complex extensions in Lua, the 
+lightweight configuration and scripting language used by Ion. 
+
+<P>
+Readers unfamiliar with Lua is advised to first glance at the Lua manual at 
+
+<P>
+<DIV ALIGN="CENTER">
+<TT><A NAME="tex2html1"
+  HREF="http://www.lua.org/docs.html">http://www.lua.org/docs.html</A></TT>
+</DIV>
+
+<P>
+and perhaps some tutorial pages at the lua-users wiki:
+
+<P>
+<DIV ALIGN="CENTER">
+<TT><A NAME="tex2html2"
+  HREF="http://lua-users.org/wiki/LuaTutorial">http://lua-users.org/wiki/LuaTutorial</A></TT>
+</DIV>
+
+<P>
+Back in this document, first in chapter <A HREF="node3.html#chap:prelim">2</A> some key
+concepts and relations are explained. These include the module system
+and Ion's object and class hierarchies. While it might not at first 
+occur that knowing such things would be necessary to <SPAN  CLASS="textit">configure</SPAN> 
+a program, this  material is essential because of the object-oriented 
+nature of most of Ion's scripting interface.
+
+<P>
+The new user, fed up with the default key bindings and eager to just
+quickly configure Ion to his liking may question the reasons for 
+exposing the ''heavy'' internal OO structure in the scripting and
+configuration interface. I'm not the one to  blame him for that. 
+Sure it would be faster to configure Ion to everyone's liking
+if a simpler binding configuration interface was provided. Such an
+interface would, however, also be far more limited and make writing
+extensions more complicated and the advantages from using a real 
+scripting language would be partly lost. One more advantage from
+a rich scripting and configuration interface is that it allows 
+implementing scripts to read alternate configuration file formats,
+ones that could be, for example, modified by external configuration tools. 
+
+<P>
+In chapter <A HREF="node4.html#chap:config">3</A> the very basic Ion configuration know-how
+is provided. All the different configuration files and their locations
+are explained and instructions are given to allow the reader to
+configure bindings and so-called ''winprops''. Chapter <A HREF="node5.html#chap:gr">4</A>
+explains the notion of drawing engines and graphical styles and how to
+write new looks for Ion and more advanced aspects of Ion's scripting 
+interface are documented in chapter <A HREF="node6.html#chap:tricks">5</A> (a work in 
+progress).
+
+<P>
+Finally, most of the functions provided by Ion's scripting interface
+are listed and documented in the Function reference in chapter
+<A HREF="node7.html#sec:exports">6</A>. At the end of the document is an alphabetical
+listing of all these functions.
+
+<P>
+
+<P>
+
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html226"
+  HREF="node3.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html220"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html214"
+  HREF="node1.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html222"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html224"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html227"
+  HREF="node3.html">2. Preliminaries: Key concepts</A>
+<B> Up:</B> <A NAME="tex2html221"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html215"
+  HREF="node1.html">Contents</A>
+ &nbsp; <B>  <A NAME="tex2html223"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html225"
+  HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node3.html b/doc/ionconf/node3.html
new file mode 100644 (file)
index 0000000..f3dbd29
--- /dev/null
@@ -0,0 +1,543 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>2. Preliminaries: Key concepts and relations</TITLE>
+<META NAME="description" CONTENT="2. Preliminaries: Key concepts and relations">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node4.html">
+<LINK REL="previous" HREF="node2.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node4.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html240"
+  HREF="node4.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html234"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html228"
+  HREF="node2.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html236"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html238"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html241"
+  HREF="node4.html">3. Basic configuration</A>
+<B> Up:</B> <A NAME="tex2html235"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html229"
+  HREF="node2.html">1. Introduction</A>
+ &nbsp; <B>  <A NAME="tex2html237"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html239"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html242"
+  HREF="node3.html#SECTION00310000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Modules</A>
+<LI><A NAME="tex2html243"
+  HREF="node3.html#SECTION00320000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Class and object hierarchies</A>
+<UL>
+<LI><A NAME="tex2html244"
+  HREF="node3.html#SECTION00321000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Class hierarchy</A>
+<LI><A NAME="tex2html245"
+  HREF="node3.html#SECTION00322000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Object hierarchies: WRegion parents and managers</A>
+<UL>
+<LI><A NAME="tex2html246"
+  HREF="node3.html#SECTION00322100000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Parent-child relations</A>
+<LI><A NAME="tex2html247"
+  HREF="node3.html#SECTION00322200000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Manager-managed relations</A>
+</UL>
+<LI><A NAME="tex2html248"
+  HREF="node3.html#SECTION00323000000000000000"><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> Summary</A>
+</UL></UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00300000000000000000"></A>
+<A NAME="chap:prelim"></A>
+<BR>
+<SPAN CLASS="arabic">2</SPAN>. Preliminaries: Key concepts and relations
+</H1>
+
+<P>
+The purpose of this chapter to explain some of key concepts and
+relations you need to understand before reading the following
+chapters. These include modules explained in section <A HREF="#sec:modules">2.1</A>
+and the Ion class and object hierarchies, section <A HREF="#sec:objects">2.2</A>.
+
+<P>
+
+<H2><A NAME="SECTION00310000000000000000"></A>
+<A NAME="sec:modules"></A>
+<BR>
+<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Modules
+</H2>
+
+<P>
+Ion has been designed so that the 'ion' executable only implements some
+basic services on top of which very different kinds of window managers
+could be build by loading the appropriate 'modules'. On modern system
+these modules are simply dynamically loaded <SPAN  CLASS="textit">.so</SPAN> libraries. On 
+more primitive systems, or if you want to squeeze total size of the 
+executable and libraries, the modules can optionally be statically 
+linked to the main binary, but must nevertheless be loaded with the
+<A HREF="#fn:dopath"><TT>dopath</TT></A> function. Modules may also include Lua code.
+
+<P>
+If no modules are loaded, all client windows appear in full screen mode.
+To get better window management support, one or more workspace modules
+should be loaded. Currently Ion provides the following modules:
+
+<P>
+<DL>
+<DT><STRONG><SPAN  CLASS="textit">mod_tiling</SPAN></STRONG></DT>
+<DD>Tilings for workspaces of the original tiled
+       Ion kind.
+    
+</DD>
+<DT><STRONG><SPAN  CLASS="textit">mod_query</SPAN></STRONG></DT>
+<DD>Queries (for starting programs and so on)
+      and message boxes.
+    
+</DD>
+<DT><STRONG><SPAN  CLASS="textit">mod_menu</SPAN></STRONG></DT>
+<DD>Support for menus, both pull-down and
+      keyboard-operated in-frame menus.
+    
+</DD>
+<DT><STRONG><SPAN  CLASS="textit">mod_statusbar</SPAN></STRONG></DT>
+<DD>Module that implements a statusbar that
+      can be adaptively embedded in each workspace's layout.
+    
+</DD>
+<DT><STRONG><SPAN  CLASS="textit">mod_dock</SPAN></STRONG></DT>
+<DD>Module for docking Window Maker dockapps.
+      The dock can both float and be embedded as the statusbar.
+    
+</DD>
+<DT><STRONG><SPAN  CLASS="textit">mod_sp</SPAN></STRONG></DT>
+<DD>This module implements a scratchpad frame that can
+      be toggled on/off everywhere. Think of the 'console' in some 
+      first-person shooters.
+    
+</DD>
+<DT><STRONG><SPAN  CLASS="textit">mod_mgmtmode</SPAN></STRONG></DT>
+<DD>Support module for implementing ''management
+      modes'' with a XOR-frame similar to move/resize mode around selected
+      region.
+    
+</DD>
+<DT><STRONG><SPAN  CLASS="textit">mod_sm</SPAN></STRONG></DT>
+<DD>Session management support module.
+      <SPAN  CLASS="textit">Loaded automatically when needed!</SPAN>
+</DD>
+</DL>
+
+<P>
+So-called drawing engines are also implemented as a modules,
+but they are not discussed here; see chapter <A HREF="node5.html#chap:gr">4</A>.
+
+<P>
+The stock configuration for the 'ion3' executable loads all of the modules
+mentioned above except <SPAN  CLASS="textit">mod_dock</SPAN> and filemod_mgmtmode.
+The stock configuration for the 'pwm3' executable (which differs from the 
+'ion3' executable in a few configuration details, such as Xinerama usage)
+loads another set of modules.
+
+<P>
+
+<P>
+
+<H2><A NAME="SECTION00320000000000000000"></A>
+<A NAME="sec:objects"></A>
+<BR>
+<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Class and object hierarchies
+</H2>
+
+<P>
+While Ion does not not have a truly object-oriented design
+<A NAME="tex2html3"
+  HREF="#foot306"><SUP><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></SUP></A>,
+things that appear on the computer screen are, however, quite
+naturally expressed as such ''objects''. Therefore Ion implements
+a rather primitive OO system for these screen objects and some
+other things. 
+
+<P>
+It is essential for the module writer to learn this object
+system, but also people who write their own binding configuration files
+necessarily come into contact with the class and object hierarchies
+- you need to know which binding setup routines apply where, 
+and what functions can be used as handlers in which bindings.
+It is the purpose of this section to attempt to explain these 
+hierarchies. If you do not wish the read the full section, at least
+read the summary at the end of it, so that you understand the very
+basic relations.
+
+<P>
+For simplicity we consider only the essential-for-basic-configuration
+Ioncore, <SPAN  CLASS="textit">mod_tiling</SPAN> and <SPAN  CLASS="textit">mod_query</SPAN> classes. 
+See Appendix <A HREF="node9.html#app:fullhierarchy">B</A> for the full class hierachy visible
+to Lua side.
+
+<P>
+
+<H3><A NAME="SECTION00321000000000000000">
+<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Class hierarchy</A>
+</H3>
+
+<P>
+One of the most important principles of object-oriented design methodology
+is inheritance; roughly how classes (objects are instances of classes)
+extend on others' features. Inheritance gives rise to class hierarchy.
+In the case of single-inheritance this hierarchy can be expressed as a
+tree where the class at the root is inherited by all others below it
+and so on. Figure <A HREF="#fig:classhierarchy">2.1</A> lists out the Ion class 
+hierarchy and below we explain what features of Ion the classes 
+implement.
+
+<P>
+
+<DIV ALIGN="CENTER"><A NAME="fig:classhierarchy"></A><A NAME="413"></A>
+<TABLE>
+<CAPTION ALIGN="BOTTOM"><STRONG>Figure 2.1:</STRONG>
+Partial Ioncore, <SPAN  CLASS="textit">mod_tiling</SPAN> and <SPAN  CLASS="textit">mod_query</SPAN> 
+    class hierarchy.</CAPTION>
+<TR><TD><PRE>
+    Obj
+     |--&gt;WRegion
+     |    |--&gt;WClientWin
+     |    |--&gt;WWindow
+     |    |    |--&gt;WRootWin
+     |    |    |--&gt;WMPlex
+     |    |    |    |--&gt;WScreen
+     |    |    |    |--&gt;WFrame
+     |    |    |--&gt;WInput (mod_query)
+     |    |         |--&gt;WEdln (mod_query)
+     |    |         |--&gt;WMessage (mod_query)
+     |    |--&gt;WGroup
+     |    |    |--&gt;WGroupWS
+     |    |    |--&gt;WGroupCW
+     |    |--&gt;WTiling (mod_tiling)
+     |--&gt;WSplit (mod_tiling)
+</PRE></TD></TR>
+</TABLE>
+</DIV>
+
+<P>
+The core classes:
+
+<P>
+<DL>
+<DT><STRONG>Obj</STRONG></DT>
+<DD><A NAME="419"></A>
+    Is the base of Ion's object system.
+
+<P>
+</DD>
+<DT><STRONG>WRegion</STRONG></DT>
+<DD><A NAME="420"></A>
+    is the base class for everything corresponding to something on the
+    screen. Each object of type WRegion has a size and  position
+    relative to the parent WRegion. While a big part of Ion 
+    operates on these instead of more specialised classes, WRegion
+    is a ''virtual''  base class in that there are no objects of ''pure''
+    type WRegion; all concrete regions are objects of some class 
+    that inherits WRegion.
+
+<P>
+</DD>
+<DT><STRONG>WClientWin</STRONG></DT>
+<DD><A NAME="421"></A> is a class for
+    client window objects, the objects that window managers are
+    supposed to manage.
+
+<P>
+</DD>
+<DT><STRONG>WWindow</STRONG></DT>
+<DD><A NAME="422"></A> is the base class for all
+    internal objects having an X window associated to them
+    (WClientWins also have X windows associated to them).
+
+<P>
+</DD>
+<DT><STRONG>WRootWin</STRONG></DT>
+<DD><A NAME="423"></A> is the class for
+    root windows<A NAME="336"></A> of X screens<A NAME="337"></A>.
+    Note that an ''X screen'' or root window is not necessarily a
+    single  physical screen<A NAME="338"></A> as a root window
+    may be split over multiple screens when multi-head extensions 
+    such as Xinerama<A NAME="339"></A> are used. (Actually there
+    can be only one WRootWin when Xinerama is used.)
+
+<P>
+</DD>
+<DT><STRONG>WMPlex</STRONG></DT>
+<DD>is a base class for all regions that''multiplex'' 
+    other regions. This means that of the regions managed by the multiplexer,
+    only one can be displayed at a time. Classes that inhereit WMPlex
+    include screens and frames.
+
+<P>
+</DD>
+<DT><STRONG>WScreen</STRONG></DT>
+<DD><A NAME="424"></A> is the class for objects
+    corresponding to physical screens. Screens may share a root
+    window when Xinerama multihead extensions are used as explained
+    above.
+
+<P>
+</DD>
+<DT><STRONG>WFrame</STRONG></DT>
+<DD><A NAME="425"></A> is the class for frames.
+    While most Ion's objects have no graphical presentation, frames basically
+    add to WMPlexes the decorations around client windows 
+    (borders, tabs).
+
+<P>
+</DD>
+<DT><STRONG>WGroup</STRONG></DT>
+<DD><A NAME="426"></A> is the base class for groups.
+    Particular types of groups are workspaces 
+    (WGroupWS<A NAME="427"></A>)
+    and groups of client windows
+    (WGroupCW<A NAME="428"></A>).
+</DD>
+</DL>
+
+<P>
+Classes implemented by the <SPAN  CLASS="textit">mod_tiling</SPAN> module:
+
+<P>
+<DL>
+<DT><STRONG>WTiling</STRONG></DT>
+<DD><A NAME="430"></A> is the class for tilings
+    of frames.
+  
+</DD>
+<DT><STRONG>WSplit</STRONG></DT>
+<DD><A NAME="431"></A> (or, more specifically, classes
+    that inherit it) encode the WTiling tree structure.
+</DD>
+</DL>
+
+<P>
+Classes implemented by the <SPAN  CLASS="textit">mod_query</SPAN> module:
+
+<P>
+<DL>
+<DT><STRONG>WInput</STRONG></DT>
+<DD><A NAME="433"></A> is a virtual base class for the
+    two classes below.
+  
+</DD>
+<DT><STRONG>WEdln</STRONG></DT>
+<DD><A NAME="434"></A> is the class for the ''queries'',
+    the text inputs that usually appear at bottoms of frames and sometimes
+    screens. Queries are the functional equivalent of ''mini buffers'' in
+    many text editors.
+  
+</DD>
+<DT><STRONG>WMessage</STRONG></DT>
+<DD><A NAME="435"></A> implements the boxes for 
+    warning and other messages that Ion may wish to display to the user. 
+    These also usually appear at bottoms of frames.
+</DD>
+</DL>
+
+<P>
+There are also some other ''proxy'' classes that do not refer
+to objects on the screen. The only important one of these for
+basic configuration is WMoveresMode that is used for
+binding callbacks in the move and resize mode.
+
+<P>
+
+<H3><A NAME="SECTION00322000000000000000">
+<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Object hierarchies: WRegion parents and managers</A>
+</H3>
+
+<P>
+
+<H4><A NAME="SECTION00322100000000000000">
+<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Parent-child relations</A>
+</H4>
+Each object of type WRegion has a parent and possibly a manager
+associated to it. The parent<A NAME="376"></A> for an object is always a 
+WWindow and for WRegion with an X window (WClientWin,
+WWindow) the parent WWindow is given by the same relation of
+the X windows. For other WRegions the relation is not as clear.
+There is generally very few restrictions other than the above on the
+parent--child relation but the most common is as described in
+Figure <A HREF="#fig:parentship">2.2</A>.
+
+<P>
+
+<DIV ALIGN="CENTER"><A NAME="fig:parentship"></A><A NAME="387"></A>
+<TABLE>
+<CAPTION ALIGN="BOTTOM"><STRONG>Figure 2.2:</STRONG>
+Most common parent-child relations</CAPTION>
+<TR><TD><PRE>
+    WRootWins
+     |--&gt;WScreens
+          |--&gt;WGroupWSs
+          |--&gt;WTilings
+          |--&gt;WClientWins in full screen mode
+          |--&gt;WFrames
+               |--&gt;WGroupCWs
+               |--&gt;WClientWins
+               |--&gt;WFrames for transients
+               |--&gt;a possible WEdln or WMessage
+</PRE></TD></TR>
+</TABLE>
+</DIV>
+
+<P>
+WRegions have very little control over their children as a parent.
+The manager<A NAME="391"></A> WRegion has much more control over its
+managed WRegions. Managers, for example, handle resize requests,
+focusing and displaying of the managed regions. Indeed the manager--managed
+relationship gives a better picture of the logical ordering of objects on
+the screen. Again, there are generally few limits, but the most common
+hierarchy is given in Figure <A HREF="#fig:managership">2.3</A>. Note that sometimes
+the parent and manager are the same object and not all objects may have
+a manager (e.g. the dock in the dock module at the time of writing this)
+but all have a parent-a screen if not anything else.
+
+<P>
+
+<H4><A NAME="SECTION00322200000000000000">
+<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Manager-managed relations</A>
+</H4>
+
+<P>
+
+<DIV ALIGN="CENTER"><A NAME="fig:managership"></A><A NAME="399"></A>
+<TABLE>
+<CAPTION ALIGN="BOTTOM"><STRONG>Figure 2.3:</STRONG>
+Most common manager-managed relations</CAPTION>
+<TR><TD><PRE>
+    WRootWins
+     |--&gt;WScreens
+          |--&gt;WGroupCWs for full screen WClientWins
+          |    |--&gt;WClientWins
+          |    |--&gt;WFrames for transients (dialogs)
+          |         |--&gt; WClientWin
+          |--&gt;WGroupWSs for workspaces
+          |    |--&gt;WTiling
+          |    |    |--&gt;possibly a WEdln, WMessage or WMenu
+          |    |    |--&gt;WFrames
+          |    |         |--&gt;WGroupCWs (with contents as above)
+          |    |--&gt;WFrames for floating content
+          |--&gt;WFrames for sticky stuff, such as the scratchpad
+</PRE></TD></TR>
+</TABLE>
+</DIV>
+
+<P>
+Note that a workspace can manage another workspace. This can be
+achieved with the <A HREF="#fn:attach_new"><TT>attach_new</TT></A> function, and allows you to nest
+workspaces as deep as you want.
+
+<P>
+
+<H3><A NAME="SECTION00323000000000000000">
+<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> Summary</A>
+</H3>
+
+<P>
+In the standard setup, keeping queries, messages and menus out of
+consideration:
+
+<P>
+
+<UL>
+<LI>The top-level objects that matter are screens and they correspond
+    to physical screens. The class for screens is WScreen.
+</LI>
+<LI>Screens contain (multiplex) groups (WGroup) and other 
+    objects, such as WFrames. Some of these are mutually exclusive
+    to be viewed at a time.
+</LI>
+<LI>Groups of the specific kind WGroupWS often contain a
+    WTiling tiling for tiling frames (WFrame), but 
+    groups may also directly contain floating frames.
+</LI>
+<LI>Frames are the objects with decorations such as tabs and borders.
+    Frames contain (multiplex) among others (groups of) client windows, 
+    to each of which corresponds a tab in the frame's decoration. Only 
+    one client window (or other object) can be shown at a time in each 
+    frame. The class for client windows is WClientWin.
+</LI>
+</UL>
+
+<P>
+<BR><HR><H4>Footnotes</H4>
+<DL>
+<DT><A NAME="foot306">... design</A><A
+ HREF="node3.html#tex2html3"><SUP><SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></SUP></A></DT>
+<DD>the author doesn't like such artificial designs
+
+</DD>
+</DL>
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html240"
+  HREF="node4.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html234"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html228"
+  HREF="node2.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html236"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html238"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html241"
+  HREF="node4.html">3. Basic configuration</A>
+<B> Up:</B> <A NAME="tex2html235"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html229"
+  HREF="node2.html">1. Introduction</A>
+ &nbsp; <B>  <A NAME="tex2html237"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html239"
+  HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node4.html b/doc/ionconf/node4.html
new file mode 100644 (file)
index 0000000..60acef3
--- /dev/null
@@ -0,0 +1,1369 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>3. Basic configuration</TITLE>
+<META NAME="description" CONTENT="3. Basic configuration">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node5.html">
+<LINK REL="previous" HREF="node3.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node5.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html261"
+  HREF="node5.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html255"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html249"
+  HREF="node3.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html257"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html259"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html262"
+  HREF="node5.html">4. Graphical styles</A>
+<B> Up:</B> <A NAME="tex2html256"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html250"
+  HREF="node3.html">2. Preliminaries: Key concepts</A>
+ &nbsp; <B>  <A NAME="tex2html258"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html260"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html263"
+  HREF="node4.html#SECTION00410000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> The configuration files</A>
+<LI><A NAME="tex2html264"
+  HREF="node4.html#SECTION00420000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> A walk through <SPAN  CLASS="textit">cfg_ion.lua</SPAN></A>
+<LI><A NAME="tex2html265"
+  HREF="node4.html#SECTION00430000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Keys and rodents</A>
+<UL>
+<LI><A NAME="tex2html266"
+  HREF="node4.html#SECTION00431000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Binding handlers and special variables</A>
+<LI><A NAME="tex2html267"
+  HREF="node4.html#SECTION00432000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Guards</A>
+<LI><A NAME="tex2html268"
+  HREF="node4.html#SECTION00433000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Defining the bindings</A>
+<LI><A NAME="tex2html269"
+  HREF="node4.html#SECTION00434000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Examples</A>
+<LI><A NAME="tex2html270"
+  HREF="node4.html#SECTION00435000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN> Key specifications</A>
+<LI><A NAME="tex2html271"
+  HREF="node4.html#SECTION00436000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">6</SPAN> Button specifications</A>
+<LI><A NAME="tex2html272"
+  HREF="node4.html#SECTION00437000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">7</SPAN> A further note on the default binding configuration</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html273"
+  HREF="node4.html#SECTION00440000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Menus</A>
+<UL>
+<LI><A NAME="tex2html274"
+  HREF="node4.html#SECTION00441000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Defining menus</A>
+<LI><A NAME="tex2html275"
+  HREF="node4.html#SECTION00442000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> Special menus</A>
+<LI><A NAME="tex2html276"
+  HREF="node4.html#SECTION00443000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN> Defining context menus</A>
+<LI><A NAME="tex2html277"
+  HREF="node4.html#SECTION00444000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">4</SPAN> Displaying menus</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html278"
+  HREF="node4.html#SECTION00450000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN> Winprops</A>
+<UL>
+<LI><A NAME="tex2html279"
+  HREF="node4.html#SECTION00451000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Classes, roles and instances</A>
+<LI><A NAME="tex2html280"
+  HREF="node4.html#SECTION00452000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Finding window identification</A>
+<LI><A NAME="tex2html281"
+  HREF="node4.html#SECTION00453000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Some common examples</A>
+<UL>
+<LI><A NAME="tex2html282"
+  HREF="node4.html#SECTION00453100000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Acrobat Reader</A>
+<LI><A NAME="tex2html283"
+  HREF="node4.html#SECTION00453200000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Fixing a Mozilla Firebird transient</A>
+<LI><A NAME="tex2html284"
+  HREF="node4.html#SECTION00453300000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Forcing newly created windows in named frames</A>
+</UL></UL></UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00400000000000000000"></A>
+<A NAME="chap:config"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>. Basic configuration
+</H1>
+
+<P>
+This chapter should help your configure Ion to your liking. As  the your
+probably already know, Ion uses Lua as a configuration and extension 
+language. If you're new to it, you might first want to read some Lua 
+documentation as already suggested and pointed to in the Introduction
+before continuing with this chapter.
+
+<P>
+Section <A HREF="#sec:conffiles">3.1</A>&nbsp;is an overview of the multiple configuration
+files Ion uses and as a perhaps more understandable introduction to the
+general layout of the configuration files, a walk-through of the main 
+configuration file <SPAN  CLASS="textit">ion.lua</SPAN> is provided in section 
+<A HREF="#sec:walkthrough">3.2</A>.
+How keys and mouse action are bound to functions is described in detail
+in <A HREF="#sec:bindings">3.3</A> and in section <A HREF="#sec:winprops">3.5</A> winprops are
+explained. For a reference on exported functions, see section
+<A HREF="node7.html#sec:exports">6</A>.
+
+<P>
+
+<H2><A NAME="SECTION00410000000000000000"></A>
+<A NAME="sec:conffiles"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> The configuration files
+</H2>
+
+<P>
+Ion3, to which document applies, stores its stock configuration files in
+<SPAN  CLASS="textit">/usr/local/etc/ion3/</SPAN> unless you, the OS package maintainer or 
+whoever  installed the package on the system has modified the variables
+<TT>PREFIX</TT><A NAME="585"></A> or
+<TT>ETCDIR</TT><A NAME="586"></A> in
+<SPAN  CLASS="textit">system.mk</SPAN><A NAME="587"></A> before compiling Ion.
+In the first case you probably know where to find the files and in 
+the other case the system administrator or the OS package maintainer
+should  have provided documentation to point to the correct location. 
+If these instructions are no help in locating the correct directory, 
+the command <TT>locate cfg_ion.lua</TT> might help provided <TT>updatedb</TT> 
+has been run recently. 
+
+<P>
+User configuration files go in <SPAN  CLASS="textit">~/.ion3/</SPAN>. 
+Ion always searches the user configuration file directory before the stock
+configuration file directory for files. Therefore, if you want to change
+some setting, it is advised against that you modify the stock configuration
+files in-place as subsequent installs of Ion will restore the stock
+configuration files. Instead you should always make a copy of the stock
+file in <SPAN  CLASS="textit">~/.ion3/</SPAN> and modify this file. When searching
+for a file, if no extension or path component is given, compiled <SPAN  CLASS="textit">.lc</SPAN> 
+files are attempted before <SPAN  CLASS="textit">.lua</SPAN> files.
+
+<P>
+All the configuration files are named <SPAN  CLASS="textit">cfg_*.lua</SPAN> with the ''<SPAN  CLASS="textit">*</SPAN>''
+part varying. The configuration file for each module <SPAN  CLASS="textit">mod_modname</SPAN> is
+<SPAN  CLASS="textit">cfg_modname.lua</SPAN>, with <SPAN  CLASS="textit">modname</SPAN> varying by the module in
+question. The following table summarises these and other configuration
+files:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT" VALIGN="TOP" WIDTH=1>File</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT" VALIGN="TOP" WIDTH=1><SPAN  CLASS="textit">cfg_ion.lua</SPAN></TD>
+<TD ALIGN="LEFT">The main configuration file</TD>
+</TR>
+<TR><TD ALIGN="LEFT" VALIGN="TOP" WIDTH=1><SPAN  CLASS="textit">cfg_ioncore.lua</SPAN></TD>
+<TD ALIGN="LEFT">Configuration file for Ion's core library.
+    Most of the bindings and menus are configured here. Bindings that are
+    specific to some module are configured in the module's configuration
+    file. For details, see section <A HREF="#sec:bindings">3.3</A>.</TD>
+</TR>
+<TR><TD ALIGN="LEFT" VALIGN="TOP" WIDTH=1><SPAN  CLASS="textit">cfg_kludges.lua</SPAN></TD>
+<TD ALIGN="LEFT">Settings to get some applications behave more nicely have been 
+    collected here. See section <A HREF="#sec:winprops">3.5</A>.</TD>
+</TR>
+<TR><TD ALIGN="LEFT" VALIGN="TOP" WIDTH=1><SPAN  CLASS="textit">cfg_tiling.lua</SPAN> 
+    <SPAN  CLASS="textit">cfg_query.lua</SPAN> 
+    <SPAN  CLASS="textit">cfg_menu.lua</SPAN> 
+    <SPAN  CLASS="textit">cfg_dock.lua</SPAN> 
+    <SPAN  CLASS="textit">cfg_statusbar.lua</SPAN> 
+    ...</TD>
+<TD ALIGN="LEFT">Configuration files for different modules.</TD>
+</TR>
+</TABLE>
+
+<P>
+Additionally, there's the file <SPAN  CLASS="textit">look.lua</SPAN> that configures the 
+drawing engine, but it is covered in chapter <A HREF="node5.html#chap:gr">4</A>.
+
+<P>
+
+<H2><A NAME="SECTION00420000000000000000"></A>
+<A NAME="sec:walkthrough"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> A walk through <SPAN  CLASS="textit">cfg_ion.lua</SPAN>
+</H2>
+
+<P>
+As already mentioned <SPAN  CLASS="textit">cfg_ion.lua</SPAN> is Ion's main configuration
+file. Some basic 'feel' settings are usually configured there and
+the necessary modules and other configuration files configuring some 
+more specific aspects of Ion are loaded there. In this section we
+take a walk through the stock <SPAN  CLASS="textit">cfg_ion.lua</SPAN>.
+Notice that most of the settings are commented-out (<TT>-</TT> is a 
+line comment in Lua) in the actual file, as they're the defaults
+nevertheless.
+
+<P>
+The first thing one in the file is to set
+<PRE>
+META="Mod1+"
+ALTMETA=""
+</PRE>
+These settings cause most of Ion's key bindings to use <SPAN  CLASS="textbf">Mod1</SPAN> as the
+modifier key. If <TT>ALTMETA</TT> is set, it is used as modifier for the keys
+that don't normally use a modifier. for details on modifiers and key 
+binding setup in general see section <A HREF="#sec:bindings">3.3</A>.
+
+<P>
+Next we do some basic feel configuration:
+
+<P>
+<PRE>
+ioncore.set{
+    dblclick_delay=250,
+    kbresize_delay=1500,
+}
+</PRE>
+
+<P>
+These two will set the delay between button presses in a double click, and
+the timeout to quit resize mode in milliseconds.
+
+<P>
+<PRE>
+ioncore.set{
+    opaque_resize=true,
+    warp=true
+}
+</PRE>
+
+<P>
+The first of these two settings enables opaque resize mode: in move/resize
+move frames and other objects mirror you actions immediately. If opaque
+resize is disabled, a XOR rubber band is shown during the mode instead.
+This will, unfortunately, cause Ion to also grab the X server and has some
+side effects.
+
+<P>
+Next we load the configuration for Ion's core, and some kludges:
+
+<P>
+<PRE>
+dopath("cfg_ioncore")
+dopath("cfg_kludges")
+</PRE>
+
+<P>
+Most bindings and menus are defined in <SPAN  CLASS="textit">cfg_ioncore.lua</SPAN>.
+Details on making such definitions follow in sections <A HREF="#sec:bindings">3.3</A> 
+and <A HREF="#sec:menus">3.4</A>, respectively. 
+some kludges or ''winprops'' to make some applications behave better
+under Ion are colledted in <SPAN  CLASS="textit">cfg_kludges.lua</SPAN>; see section
+<A HREF="#sec:winprops">3.5</A> for details. In addition to these, this file
+lists quite a few statements of the form
+<PRE>
+ioncore.defshortening("[^:]+: (.*)(&lt;[0-9]+&gt;)", "$1$2$|$1$&lt;...$2")
+</PRE>
+These are used to configure how Ion attempts to shorten window titles
+when they do not fit in a Tab. The first argument is a POSIX regular
+expression that is used to match against the title and the next is
+a rule to construct a new title of a match occurs. This particular
+rule is used to shorten e.g. 'Foo: barbaz&lt;3&gt;' to 'barba...&lt;3&gt;'; for
+details see the function reference entry for <A HREF="node7.html#fn:ioncore.defshortening"><TT>ioncore.defshortening</TT></A>.
+
+<P>
+To actually be able to do something besides display windows in full screen
+mode, we must next load some modules:
+
+<P>
+<PRE>
+dopath("cfg_modules")
+--dopath("mod_query")
+--dopath("mod_menu")
+--dopath("mod_tiling")
+--dopath("mod_statusbar")
+--dopath("mod_dock")
+--dopath("mod_sp")
+</PRE>
+
+<P>
+We actually load there another file listing the default selection of
+modules. If you only want to load additional modules, just uncomment
+the corresponding line. If you want to disable loading some modules,
+comment out the the line loading <SPAN  CLASS="textit">cfg_modules</SPAN>, and uncomment
+the lines for the modules you want, or add more.
+
+<P>
+
+<H2><A NAME="SECTION00430000000000000000"></A>
+<A NAME="sec:bindings"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Keys and rodents
+</H2>
+
+<P>
+In the stock configuration file setup, most key and mouse bindings are set
+from the file <SPAN  CLASS="textit">cfg_ioncore.lua</SPAN> while module-specific bindings
+are set from the modules' main configuration files (<SPAN  CLASS="textit">cfg_modname.lua</SPAN>).
+This, however, does not have to be so as long as the module has been
+loaded prior to defining any module-specific bindings.
+
+<P>
+Bindings are defined by calling the function 
+<A HREF="node7.html#fn:ioncore.defbindings"><TT>defbindings</TT></A> with the ''context'' of the
+bindings and the a table of new bindings to make. The context is simply
+string indicating one of the classes of regions (or modes such as
+WMoveresMode) introduced in section <A HREF="node3.html#sec:objects">2.2</A>, and fully
+listed in appendix <A HREF="node9.html#app:fullhierarchy">B</A>, although not all define
+a binding map. For example, the following skeleton would be used to 
+define new bindings for all frames:
+
+<P>
+<PRE>
+defbindings("WFrame", {
+    -- List of bindings to make goes here.
+})
+</PRE>
+
+<P>
+There has been some confusion among users about the need to define the
+''context'' for each binding, so let me try to explain this design
+decision here. The thing is that if there was a just a simple 'bind this 
+key to this action' method without knowledge of the context, some 
+limitations would have to be made on the available actions and writing 
+custom handlers would be more complicated. In addition one may want to 
+bind the same function to different key for different types of objects.
+Indeed, the workspace and frame tab switching functions are the same both
+classes being based on WMPlex, and in the stock configuration the 
+switch to <SPAN CLASS="MATH"></SPAN>:th workspaces is bound to <SPAN  CLASS="textbf">Mod1+n</SPAN> while the switch to 
+<SPAN CLASS="MATH"></SPAN>:th tab is bound to the sequence <SPAN  CLASS="textbf">Mod1+k n</SPAN>.
+
+<P>
+Currently known ''contexts'' include: 
+<TT>WScreen</TT>,
+<TT>WMPlex</TT>,
+<TT>WMPlex.toplevel</TT>,
+<TT>WFrame</TT>,
+<TT>WFrame.toplevel</TT>,
+<TT>WFrame.floating</TT>,
+<TT>WFrame.tiled</TT>,
+<TT>WFrame.transient</TT>,
+<TT>WMoveresMode</TT>,
+<TT>WGroup</TT>,
+<TT>WGroupCW</TT>,
+<TT>WGroupWS</TT>,
+<TT>WClientWin</TT>,
+<TT>WTiling</TT>, and
+<TT>WStatusBar</TT>.
+Most of these should be self-explanatory, corresponding to objects
+of class with the same name. The ones with <TT>.toplevel</TT> suffix
+refer to screens and ''toplevel''  frames, i.e. frames that are
+not used for transient windows. Likewise <TT>.transient</TT> refers
+to frames in transient mode, and <TT>.tiled</TT> and <TT>.floating</TT>
+to frames in, respectively, tiled and floating modes. 
+
+<P>
+The following subsections describe how to construct elements of the
+binding table. Note that <A HREF="node7.html#fn:ioncore.defbindings"><TT>defbindings</TT></A> adds
+the the newly defined bindings to the previous bindings of the context,
+overriding duplicates. To unbind an event, set the handler parameter
+to <TT>nil</TT> for each of the functions to be described in the following
+subsections.
+
+<P>
+Also note that when multiple objects want to handle a binding, the 
+innermost (when the root window is considered the outermost) active object
+in the parent-child hierarchy (see Figure <A HREF="node3.html#fig:parentship">2.2</A>) of objects 
+gets to handle the action.
+
+<P>
+
+<H3><A NAME="SECTION00431000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Binding handlers and special variables</A>
+</H3>
+
+<P>
+Unlike in Ion2, in Ion3 binding handlers are not normally passed as
+''anonymous functions'', although this is still possible. The preferred
+method now is to pass the code of the handler as a string. Two special 
+variables are available in this code. These are
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Variable</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>_</TT> (underscore)</TD>
+<TD ALIGN="LEFT">Reference to the object on which the 
+      binding was triggered. The object is of the same class as the the
+      context of the <A HREF="node7.html#fn:ioncore.defbindings"><TT>defbindings</TT></A> call
+      defining the binding.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>_sub</TT></TD>
+<TD ALIGN="LEFT">Usually, the currently active <SPAN  CLASS="textit">managed object</SPAN> of the 
+      object referred to by <TT>_</TT>, but sometimes (e.g. mouse actions
+      on tabs of frames) something else relevant to the action triggering
+      the binding.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>_chld</TT></TD>
+<TD ALIGN="LEFT">Object corresponding to the currently active child window of the
+       object referred to by <TT>_</TT>.</TD>
+</TR>
+</TABLE>
+
+<P>
+For example, supposing '<TT>_</TT>' is a WFrame, the following
+handler should move the active window to the right, if possible:
+
+<P>
+<PRE>
+"_:inc_index(_sub)"
+</PRE>
+
+<P>
+
+<H3><A NAME="SECTION00432000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Guards</A>
+</H3>
+
+<P>
+To suppress error messages, each binding handler may also be accompanied
+by a ''guard'' expression that blocks the handler from being called when
+the guard condition is not met. Currently the following guard expressions
+are supported (for both <TT>_sub</TT> and <TT>_chld</TT>):
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Guard</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>"_sub:non-nil"</TT></TD>
+<TD ALIGN="LEFT">The <TT>_sub</TT> parameter must be set.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>"_sub:SomeClass"</TT></TD>
+<TD ALIGN="LEFT">The <TT>_sub</TT> parameter must be member
+      of class SomeClass.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<H3><A NAME="SECTION00433000000000000000"></A>
+<A NAME="sec:binddef"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Defining the bindings
+</H3>
+
+<P>
+The descriptions of the individual bindings in the binding table argument
+to <A HREF="node7.html#fn:ioncore.defbindings"><TT>defbindings</TT></A> should be constructed with the following
+functions.
+
+<P>
+Key presses:
+
+<UL>
+<LI><A HREF="#fn:kpress"><TT>kpress</TT></A><TT>(keyspec, handler [, guard])</TT>,
+</LI>
+<LI><A HREF="#fn:kpress_wait"><TT>kpress_wait</TT></A><TT>(keyspec, handler [, guard])</TT> and
+</LI>
+<LI><A HREF="#fn:submap"><TT>submap</TT></A><TT>(keyspec, { ... more key bindings ... })</TT>.
+</LI>
+</UL>
+Mouse actions:
+
+<UL>
+<LI><A HREF="#fn:mclick"><TT>mclick</TT></A><TT>(buttonspec, handler [, guard])</TT>,
+</LI>
+<LI><A HREF="#fn:mdblclick"><TT>mdblclick</TT></A><TT>(buttonspec, handler [, guard])</TT>, 
+</LI>
+<LI><A HREF="#fn:mpress"><TT>mpress</TT></A><TT>(buttonspec, handler [, guard])</TT> and
+</LI>
+<LI><A HREF="#fn:mdrag"><TT>mdrag</TT></A><TT>(buttonspec, handler [, guard])</TT>.
+</LI>
+</UL>
+
+<P>
+The actions that most of these functions correspond to should be clear
+and as explained in the reference, <A HREF="#fn:kpress_wait"><TT>kpress_wait</TT></A> is simply
+<A HREF="#fn:kpress"><TT>kpress</TT></A> with a flag set instructing Ioncore wait for all
+modifiers to be released before processing any further actions.
+This is to stop one from accidentally calling e.g.
+<A HREF="node7.html#fn:WRegion.rqclose"><TT>WRegion.rqclose</TT></A> multiple times in a row. The <A HREF="#fn:submap"><TT>submap</TT></A>
+function is used to define submaps or ''prefix maps''. The second
+argument to this function is table listing the key press actions
+(<A HREF="#fn:kpress"><TT>kpress</TT></A>) in the submap
+
+<P>
+The parameters <TT>keyspec</TT> and <TT>buttonspec</TT> are explained below
+in detail. The parameter <TT>handler</TT> is the handler for the binding,
+and the optional parameter <TT>guard</TT> its guard. These should normally
+be strings as explained above. 
+
+<P>
+
+<H3><A NAME="SECTION00434000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Examples</A>
+</H3>
+
+<P>
+For example, to just bind the key <SPAN  CLASS="textbf">Mod1+1</SPAN> to switch to the first
+workspace and <SPAN  CLASS="textbf">Mod1+Right</SPAN> to the next workspace, you would make the
+following call
+<PRE>
+defbindings("WScreen", {
+    kpress("Mod1+Right", "_:switch_next()"),
+    kpress("Mod1+1", "_:switch_nth(1)"),
+})
+</PRE>
+
+<P>
+Note that <TT>_:switch_nth(1)</TT> is the same as calling
+<A HREF="node7.html#fn:WMPlex.switch_next"><TT>WMPlex.switch_next</TT></A><TT>(_, 1)</TT> as WScreen inherits
+WMPlex and this is where the function is actually defined.
+
+<P>
+Similarly to the above example, to bind the key sequence <SPAN  CLASS="textbf">Mod1+k n</SPAN> 
+switch to the next managed object within a frame, and <SPAN  CLASS="textbf">Mod1+k 1</SPAN> to the
+first, you would issue the following call:
+<PRE>
+defbindings("WFrame", {
+    submap("Mod1+K", {
+        kpress("Right", "_:switch_next()"),
+        kpress("1", "_:switch_nth(1)"),
+   }),
+})
+</PRE>
+
+<P>
+
+<H3><A NAME="SECTION00435000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN> Key specifications</A>
+</H3>
+
+<P>
+As seen above, the functions that create key binding specifications require
+a <TT>keyspec</TT> argument. This argument should be a string containing the
+name of a key as listed in the X header file <SPAN  CLASS="textit">keysymdef.h</SPAN><A NAME="tex2html7"
+  HREF="#foot858"><SUP><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN></SUP></A> without the <TT>XK_</TT> prefix.
+<A NAME="859"></A>
+Most of the key names are quite intuitive while some are not. For example,
+the <SPAN  CLASS="textbf">Enter</SPAN> key on the main part of the keyboard has the less common
+name <SPAN  CLASS="textbf">Return</SPAN> while the one the numpad is called <SPAN  CLASS="textbf">KP_Enter</SPAN>.
+
+<P>
+The <TT>keyspec</TT> string may optionally have multiple ''modifier'' names
+followed by a plus sign (<TT>+</TT>) as a prefix. X defines the following
+modifiers:
+<BLOCKQUOTE>
+<SPAN  CLASS="textbf">Shift</SPAN>, <SPAN  CLASS="textbf">Control</SPAN>, <SPAN  CLASS="textbf">Mod1</SPAN> to <SPAN  CLASS="textbf">Mod5</SPAN>,
+<SPAN  CLASS="textbf">AnyModifier</SPAN> and <SPAN  CLASS="textbf">Lock</SPAN>.
+<A NAME="860"></A>
+<A NAME="861"></A>
+<A NAME="862"></A>
+<A NAME="863"></A>
+<A NAME="864"></A>
+
+</BLOCKQUOTE>
+
+<P>
+X allows binding all of these modifiers to almost any key and while this
+list of modifiers does not explicitly list keys such as 
+<SPAN  CLASS="textbf">Alt</SPAN><A NAME="865"></A> that are common on modern keyboards, such
+keys are bound to one of the <SPAN  CLASS="textbf">ModN</SPAN>. On systems running XFree86
+<SPAN  CLASS="textbf">Alt</SPAN> is usually <SPAN  CLASS="textbf">Mod1</SPAN>. On Suns <SPAN  CLASS="textbf">Mod1</SPAN> is the diamond key
+and <SPAN  CLASS="textbf">Alt</SPAN> something else. One of the ''flying window'' keys on so
+called Windows-keyboards is probably mapped to <SPAN  CLASS="textbf">Mod3</SPAN> if you have
+such a key. Use the program <SPAN  CLASS="textit">xmodmap</SPAN><A NAME="866"></A>
+to find out what exactly is bound where. 
+
+<P>
+Ion defaults to <SPAN  CLASS="textbf">AnyModifier</SPAN> in submaps. This can sometimes lead to
+unwanted effects when the same key is used with and without explicitly
+specified modifiers in nested regions. For this reason, Ion recognises
+<SPAN  CLASS="textbf">NoModifier</SPAN> as a special modifier that can be used to reset this
+default.
+
+<P>
+Ion ignores the <SPAN  CLASS="textbf">Lock</SPAN> modifier and any <SPAN  CLASS="textbf">ModN</SPAN> (<SPAN CLASS="MATH"></SPAN>)
+bound to <SPAN  CLASS="textbf">NumLock</SPAN><A NAME="867"></A> or
+<SPAN  CLASS="textbf">ScrollLock</SPAN><A NAME="868"></A>
+by default because such<A NAME="tex2html8"
+  HREF="#foot837"><SUP><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></SUP></A> locking keys may otherwise
+cause confusion.
+
+<P>
+
+<H3><A NAME="SECTION00436000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">6</SPAN> Button specifications</A>
+</H3>
+
+<P>
+Button specifications are similar to key definitions but now
+instead of specifying modifiers and a key, you specify modifiers
+and one of the button names <SPAN  CLASS="textbf">Button1</SPAN> to
+<SPAN  CLASS="textbf">Button5</SPAN><A NAME="869"></A>. Additionally the
+specification may end with an optional area name following an @-sign.
+Only frames currently support areas, and the supported values in this
+case are
+<TT>"border"</TT>, <TT>"tab"</TT>, <TT>"empty_tab"</TT>, <TT>"client"</TT> and
+<TT>nil</TT> (for the whole frame).
+
+<P>
+For example, the following code binds dragging a tab with the first 
+button pressed to initiate tab drag&amp;drop handling:
+
+<P>
+<PRE>
+defbindings("WFrame", {
+    mdrag("Button1@tab", "_:p_tabdrag()"),
+})
+</PRE>
+
+<P>
+
+<H3><A NAME="SECTION00437000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">7</SPAN> A further note on the default binding configuration</A>
+</H3>
+
+<P>
+The default binding configuration contains references to the variables
+<TT>META</TT> and <TT>ALTMETA</TT> instead of directly using the default
+values of <TT>"Mod1+"</TT> and <TT>""</TT> (nothing). As explained in
+section <A HREF="#sec:walkthrough">3.2</A>, the definitions of these variables
+appear in <SPAN  CLASS="textit">cfg_ion.lua</SPAN>. This way you can easily change the the
+modifiers used by all bindings in the default configuration without 
+changing the whole binding configuration. Quite a few people prefer 
+to use the Windows keys as modifiers because many applications already
+use <SPAN  CLASS="textbf">Alt</SPAN>. Nevertheless, <SPAN  CLASS="textbf">Mod1</SPAN> is the default as a key bound 
+to it is available virtually everywhere.
+
+<P>
+
+<H2><A NAME="SECTION00440000000000000000"></A>
+<A NAME="sec:menus"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Menus
+</H2>
+
+<P>
+
+<H3><A NAME="SECTION00441000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Defining menus</A>
+</H3>
+
+<P>
+<A NAME="1115"></A>
+<A NAME="1170"></A>
+<A NAME="1171"></A>
+<A NAME="1172"></A>
+In the stock configuration file setup, menus are defined in the file
+<SPAN  CLASS="textit">cfg_menus.lua</SPAN> as previously mentioned. The <SPAN  CLASS="textit">mod_menu</SPAN> module
+must be loaded for one to be able to define menus, and this is done with
+the function <A HREF="#fn:mod_menu.defmenu"><TT>defmenu</TT></A> provided by it.
+
+<P>
+Here's an example of the definition of a rather simple menu with a submenu:
+
+<P>
+<PRE>
+defmenu("exitmenu", {
+    menuentry("Restart", "ioncore.restart()"),
+    menuentry("Exit", "ioncore.shutdown()"),
+})
+
+defmenu("mainmenu", {
+    menuentry("Lock screen", "ioncore.exec('xlock')"),
+    menuentry("Help", "mod_query.query_man(_)"),
+    submenu("Exit", "exitmenu"),
+})
+</PRE>
+
+<P>
+The <A HREF="#fn:mod_menu.menuentry"><TT>menuentry</TT></A> function is used to create an entry in the 
+menu with a title and an entry handler to be called when the menu entry
+is activated. The parameters to the handler are similar to those of binding
+handlers, and usually the same as those of the binding that opened the menu.
+
+<P>
+The <A HREF="#fn:mod_menu.submenu"><TT>submenu</TT></A> function is used to insert a submenu at that 
+point in the menu. (One could as well just pass a table with the menu
+entries, but it is not encouraged.)
+
+<P>
+
+<H3><A NAME="SECTION00442000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> Special menus</A>
+</H3>
+
+<P>
+The menu module predefines the following special menus. These can be used
+just like the menus defined as above.
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Menu name</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>windowlist</TT></TD>
+<TD ALIGN="LEFT">List of all client windows. Activating an entry jumps to that window.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>workspacelist</TT></TD>
+<TD ALIGN="LEFT">List of all workspaces. Activating an entry jumps to that workspaces.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>stylemenu</TT></TD>
+<TD ALIGN="LEFT">List of available <SPAN  CLASS="textit">look_*.lua</SPAN> style files. Activating an entry
+    loads that style and ask to save the selection.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>ctxmenu</TT></TD>
+<TD ALIGN="LEFT">Context menu for given object.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<H3><A NAME="SECTION00443000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN> Defining context menus</A>
+</H3>
+
+<P>
+The ''ctxmenu'' is a special menu that is assembled from a defined context
+menu for the object for which the menu was opened for, but also includes
+the context menus for the manager objects as submenus.
+
+<P>
+Context menus for a given region class are defined with the
+<A HREF="#fn:mod_menu.defctxmenu"><TT>defctxmenu</TT></A> function. This is other ways similar to
+<A HREF="#fn:mod_menu.defmenu"><TT>defmenu</TT></A>, but the first argument instead being the name
+of the menu, the name of the region class to define context menu for.
+For example, here's part of the stock WFrame context menu 
+definition:
+
+<P>
+<PRE>
+defctxmenu("WFrame", {
+    menuentry("Close", "WRegion.rqclose_propagate(_, _sub)"),
+    menuentry("Kill",  "WClientWin.kill(_sub)", "_sub:WClientWin"),
+})
+</PRE>
+
+<P>
+Some of the same ''modes'' as were available for some bindings
+may also be used: <TT>WFrame.tiled</TT>, <TT>WFrame.floating</TT>,
+and <TT>WFrame.transient</TT>.
+
+<P>
+
+<H3><A NAME="SECTION00444000000000000000"></A>
+<A NAME="sec:menudisp"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">4</SPAN> Displaying menus
+</H3>
+
+<P>
+The following functions may be used to display menus from binding
+handlers (and elsewhere):
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Function</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><A HREF="node7.html#fn:mod_menu.menu"><TT>mod_menu.menu</TT></A></TD>
+<TD ALIGN="LEFT">Keyboard (or mouse) operated menus that open in the bottom-left corner
+      of a screen or frame.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><A HREF="#fn:mod_menu.bigmenu"><TT>mod_menu.bigmenu</TT></A></TD>
+<TD ALIGN="LEFT">Same as previous, but uses another graphical style.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><A HREF="node7.html#fn:mod_menu.pmenu"><TT>mod_menu.pmenu</TT></A></TD>
+<TD ALIGN="LEFT">Mouse-operated drop-down menus. This function can only be called from a
+      mouse press or drag handler.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><A HREF="node7.html#fn:mod_menu.grabmenu"><TT>mod_menu.grabmenu</TT></A></TD>
+<TD ALIGN="LEFT">A special version of <A HREF="node7.html#fn:mod_menu.menu"><TT>mod_menu.menu</TT></A> that grabs the keyboard
+      and is scrolled with a given key until all modifiers have been released,
+      after which the selected entry is activated. This function is meant to 
+      be used for implementing, for example, Win***s-style <SPAN  CLASS="textbf">Alt-Tab</SPAN> 
+      handling.<A NAME="tex2html9"
+  HREF="#foot1173"><SUP><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></SUP></A></TD>
+</TR>
+</TABLE>
+
+<P>
+The <A HREF="node7.html#fn:mod_menu.grabmenu"><TT>grabmenu</TT></A> function takes the extra key parameter, but
+aside from that each of these functions takes three arguments, which when
+called from a binding handler, should be the parameters to the handler, and
+the name of the menu. For example, the following snippet of of code binds
+the both ways to open a context menu for a frame:
+
+<P>
+<PRE>
+defbindings("WFrame", {
+    kpress(MOD1.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"),
+    mpress("Button3", "mod_menu.pmenu(_, _sub, 'ctxmenu')"),
+})
+</PRE>
+
+<P>
+
+<H2><A NAME="SECTION00450000000000000000"></A>
+<A NAME="sec:winprops"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN> Winprops
+</H2>
+
+<P>
+The so-called ''winprops''<A NAME="1266"></A> can be used to change how
+specific windows are handled and to set up some kludges to deal with
+badly behaving applications. They are defined by calling the function
+<TT>defwinprop</TT> with a table containing the properties to set and the
+necessary information to identify a window. The currently supported
+winprops are listed below, and the subsequent subsections explain the
+usual method of identifying windows, and how to obtain this information.
+
+<P>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>acrobatic</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1410"></A>
+    Set this to <TT>true</TT> for Acrobat Reader. It has an annoying
+    habit of trying to manage its dialogs instead of setting them as
+    transients and letting the window manager do its job, causing
+    Ion and acrobat go a window-switching loop when a dialog is
+    opened.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>aspect</TT> (table)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1411"></A>
+    The table should contain the entries <TT>w</TT> and <TT>h</TT> that
+    override application-supplied aspect ratio hint.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>float</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1412"></A>
+    Set this to open the window in a floating frame, when
+    in a group.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>fullscreen</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1413"></A>
+    Should the window be initially in full screen mode?
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>ignore_cfgrq</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1414"></A>
+    Should configure requests on the window be ignored?
+    Only has effect on floating windows.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>ignore_net_active_window</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1415"></A>
+    Ignore extended WM hints <TT>_NET_ACTIVE_WINDOW</TT> request.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>ignore_resizeinc</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1416"></A>
+    Should application supplied size increments be ignored?
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>jumpto</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1417"></A>
+    Should a newly created client window always be made
+    active, even if the allocated frame isn't.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>max_size</TT> (table)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1418"></A>
+    The table should contain the entries <TT>w</TT> and <TT>h</TT> that
+    override application-supplied maximum size hint.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>min_size</TT> (table)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1419"></A>
+    Similar to <TT>max_size</TT> but for the minimum size hint.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>oneshot</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1420"></A>
+    Discard this winprop after first use.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>switchto</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1421"></A>
+    Should a newly mapped client window be switched to within
+    its frame.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>target</TT> (string)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1422"></A>
+    The name of an object (workspace, frame) that should manage 
+    windows of this type.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>transient_mode</TT> (string)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1423"></A>
+    "normal": No change in behaviour. "current": The window
+    should be thought of as a transient for the current active
+    client window (if any) even if it is not marked as a
+    transient by the application. "off": The window should be
+    handled as a normal window even if it is marked as a
+    transient by the application.
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>transients_at_top</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1424"></A>
+    When transients are managed by the client window itself (as it
+    is the case on tiled workspaces), should the transients be
+    placed at the top of the window instead of bottom?
+
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DT><STRONG>Winprop:</STRONG></DT>
+<DD><TT>transparent</TT> (boolean)
+      
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD><A NAME="1425"></A>
+    Should frames be made transparent when this window is selected? 
+<BR>  
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00451000000000000000"></A>
+<A NAME="sec:classesrolesinstances"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Classes, roles and instances
+</H3>
+
+<P>
+The identification information in the winprop specification is usually the
+<TT>class</TT><A NAME="1426"></A>,
+<TT>role</TT><A NAME="1427"></A>,
+<TT>instance</TT><A NAME="1428"></A> and
+<TT>name</TT>
+of the window. The <TT>name</TT> field is a Lua-style regular expression
+matched against the window's title and the rest are strings that must
+exactly match the corresponding window information. It is not necessary
+to specify all of these fields.
+
+<P>
+Ion looks for a matching winprop in the order listed by the following
+table. An 'E' indicates that the field must be set in the winprop
+and it must match the window's corresponding property exactly or, in
+case of <TT>name</TT>, the regular expression must match the window
+title. An asterisk '*' indicates that a winprop where the field is
+not specified (or is itself an asterisk in case of the first three
+fields) is tried.
+
+<P>
+<DIV ALIGN="CENTER">
+<TABLE CELLPADDING=3 BORDER="1">
+<TR><TD ALIGN="LEFT"><TT>class</TT></TD>
+<TD ALIGN="LEFT"><TT>role</TT></TD>
+<TD ALIGN="LEFT"><TT>instance</TT></TD>
+<TD ALIGN="LEFT"><TT>name</TT></TD>
+</TR>
+<TR><TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">E</TD>
+</TR>
+<TR><TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">*</TD>
+</TR>
+<TR><TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">*</TD>
+<TD ALIGN="LEFT">E</TD>
+</TR>
+<TR><TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">*</TD>
+<TD ALIGN="LEFT">*</TD>
+</TR>
+<TR><TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">*</TD>
+<TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">E</TD>
+</TR>
+<TR><TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">*</TD>
+<TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">*</TD>
+</TR>
+<TR><TD ALIGN="LEFT">E</TD>
+<TD ALIGN="LEFT">*</TD>
+<TD ALIGN="LEFT">*</TD>
+<TD ALIGN="LEFT">E</TD>
+</TR>
+<TR><TD ALIGN="LEFT">&nbsp;</TD>
+<TD ALIGN="LEFT">&nbsp;</TD>
+<TD ALIGN="LEFT">&nbsp;</TD>
+<TD ALIGN="LEFT">etc.</TD>
+</TR>
+</TABLE>
+</DIV>
+
+<P>
+If there are multiple winprops with other identification information 
+the same but different <TT>name</TT>, the longest match is chosen.
+
+<P>
+
+<H3><A NAME="SECTION00452000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Finding window identification</A>
+</H3>
+
+<P>
+The 'Window info' context menu entry (<SPAN  CLASS="textbf">Mod1+M</SPAN> or <SPAN  CLASS="textbf">Button3</SPAN> on a tab)
+can be used to list the identification information required to set winprops
+for a window and all the transient windows managed within it. 
+
+<P>
+<A NAME="1383"></A> 
+Another way to get the identification information is to use <TT>xprop</TT>.
+Simply run To get class and instance, simply run <TT>xprop WM_CLASS</TT>
+and click on the particular window of interest. The class is the latter of
+the strings while the instance is the former.  To get the role - few
+windows have this property - use the command <TT>xprop WM_ROLE</TT>. 
+This method, however, will not work on transients. 
+
+<P>
+<A NAME="1387"></A>
+So-called ''transient windows'' are usually short-lived dialogs (although
+some programs abuse this property) that have a parent window that they are
+''transient for''. On tiled workspaces Ion displays these windows 
+simulatenously with the parent window at the bottom of the same frame.
+Unfortunately <TT>xprop</TT> is stupid and can't cope with this situation,
+returning the parent window's properties when the transient is clicked on.
+For this reason you'll have to do a little extra work to get the properties
+for that window.<A NAME="tex2html11"
+  HREF="#foot1430"><SUP><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN></SUP></A>
+<P>
+Finally, it should be mentioned that too many authors these days
+''forget'' to set this vital identification to anything meaningful:
+everything except name is the same for all of the programs's 
+windows, for example.
+
+<P>
+
+<H3><A NAME="SECTION00453000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Some common examples</A>
+</H3>
+
+<P>
+
+<H4><A NAME="SECTION00453100000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Acrobat Reader</A>
+</H4>
+
+<P>
+The following is absolutely necessary for Acrobat reader:
+
+<P>
+<PRE>
+defwinprop{
+    class = "AcroRead",
+    instance = "documentShell",
+    acrobatic = true,
+}
+</PRE>
+
+<P>
+
+<H4><A NAME="SECTION00453200000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Fixing a Mozilla Firebird transient</A>
+</H4>
+
+<P>
+Mozilla Firebird (0.7) incorrectly does not set the <TT>WM_TRANSIENT_FOR</TT> 
+property for the dialog that is used to ask the action to take for a file.
+It, however, sets the the property point to the main window for the save
+dialog. This can be annoying and confusing, as the first dialog is not 
+closed before the second is displayed.
+
+<P>
+We'd like the first dialog to be transient to the main window. The closest
+we can get to that is to consider it transient to the current window (if
+there's one). Unfortunately Firebird does not set any meaningful classes, 
+instances or roles for the windows, so we'll have to rely on an ugly title
+match.
+
+<P>
+<PRE>
+defwinprop{
+    class = "MozillaFirebird-bin",
+    name = "Opening .*",
+    transient_mode = "current",
+}
+</PRE>
+
+<P>
+
+<H4><A NAME="SECTION00453300000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Forcing newly created windows in named frames</A>
+</H4>
+
+<P>
+The following winprop should place xterm started with command-line parameter
+<TT>-name sysmon</TT> and running a system monitoring program in a
+particular frame:
+<PRE>
+defwinprop{
+    class = "XTerm",
+    instance = "sysmon",
+    target = "sysmonframe",
+}
+</PRE>
+
+<P>
+For this example to work, we have to somehow create a frame named
+<TT>sysmonframe</TT>. One way to do this is to make the following
+call in the <SPAN  CLASS="textbf">Mod1+F3</SPAN> Lua code query:
+
+<P>
+<PRE>
+mod_query.query_renameframe(_)
+</PRE>
+
+<P>
+Recall that <TT>_</TT> points to the multiplexer (frame or screen) in which 
+the query was opened. Running this code should open a new query prefilled
+with the current name of the frame. In our example we would change the 
+name to <TT>sysmonframe</TT>, but we could just as well have used the 
+default name formed from the frame's class name and an instance number.
+
+<P>
+
+<P>
+<BR><HR><H4>Footnotes</H4>
+<DL>
+<DT><A NAME="foot858">...keysymdef.h</A><A
+ HREF="node4.html#tex2html7"><SUP><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN></SUP></A></DT>
+<DD>This file can usually be found in the directory
+<SPAN  CLASS="textit">/usr/X11R6/include/X11/</SPAN>.
+
+</DD>
+<DT><A NAME="foot837">... such</A><A
+ HREF="node4.html#tex2html8"><SUP><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></SUP></A></DT>
+<DD>Completely useless keys that should be
+gotten rid of in the author's opinion.
+
+</DD>
+<DT><A NAME="foot1173">... handling.</A><A
+ HREF="node4.html#tex2html9"><SUP><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></SUP></A></DT>
+<DD>See the <SPAN  CLASS="textit">wcirculate.lua</SPAN> script in the Ion 
+        scripts repository <TT><A NAME="tex2html10"
+  HREF="http://iki.fi/tuomov/repos/ion-scripts-3/">http://iki.fi/tuomov/repos/ion-scripts-3/</A></TT>.
+
+</DD>
+<DT><A NAME="foot1430">... window.</A><A
+ HREF="node4.html#tex2html11"><SUP><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN></SUP></A></DT>
+<DD>There's a patch to <TT>xprop</TT> to
+fix this, but nothing seems to be happening with respect to including it in 
+XFree86.
+
+</DD>
+</DL>
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html261"
+  HREF="node5.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html255"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html249"
+  HREF="node3.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html257"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html259"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html262"
+  HREF="node5.html">4. Graphical styles</A>
+<B> Up:</B> <A NAME="tex2html256"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html250"
+  HREF="node3.html">2. Preliminaries: Key concepts</A>
+ &nbsp; <B>  <A NAME="tex2html258"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html260"
+  HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node5.html b/doc/ionconf/node5.html
new file mode 100644 (file)
index 0000000..b6951d4
--- /dev/null
@@ -0,0 +1,720 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>4. Graphical styles</TITLE>
+<META NAME="description" CONTENT="4. Graphical styles">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node6.html">
+<LINK REL="previous" HREF="node4.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node6.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html297"
+  HREF="node6.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html291"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html285"
+  HREF="node4.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html293"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html295"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html298"
+  HREF="node6.html">5. Scripting</A>
+<B> Up:</B> <A NAME="tex2html292"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html286"
+  HREF="node4.html">3. Basic configuration</A>
+ &nbsp; <B>  <A NAME="tex2html294"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html296"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html299"
+  HREF="node5.html#SECTION00510000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Drawing engines, style specifications and sub-styles</A>
+<UL>
+<LI><A NAME="tex2html300"
+  HREF="node5.html#SECTION00511000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Known styles and substyles</A>
+<UL>
+<LI><A NAME="tex2html301"
+  HREF="node5.html#SECTION00511100000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Frames</A>
+<LI><A NAME="tex2html302"
+  HREF="node5.html#SECTION00511200000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> Tabs and menu entries</A>
+<LI><A NAME="tex2html303"
+  HREF="node5.html#SECTION00511300000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> The rest</A>
+</UL>
+</UL>
+<BR>
+<LI><A NAME="tex2html304"
+  HREF="node5.html#SECTION00520000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> Defining styles for the default drawing engine</A>
+<UL>
+<LI><A NAME="tex2html305"
+  HREF="node5.html#SECTION00521000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> The structure of the configuration files</A>
+<LI><A NAME="tex2html306"
+  HREF="node5.html#SECTION00522000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Defining the styles</A>
+<UL>
+<LI><A NAME="tex2html307"
+  HREF="node5.html#SECTION00522100000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Colours</A>
+<LI><A NAME="tex2html308"
+  HREF="node5.html#SECTION00522200000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Borders and widths</A>
+<LI><A NAME="tex2html309"
+  HREF="node5.html#SECTION00522300000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> Text</A>
+<LI><A NAME="tex2html310"
+  HREF="node5.html#SECTION00522400000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous</A>
+<LI><A NAME="tex2html311"
+  HREF="node5.html#SECTION00522500000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN> Substyles</A>
+</UL>
+<LI><A NAME="tex2html312"
+  HREF="node5.html#SECTION00523000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> An example</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html313"
+  HREF="node5.html#SECTION00530000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN> Miscellaneous settings</A>
+<UL>
+<LI><A NAME="tex2html314"
+  HREF="node5.html#SECTION00531000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Extra fields for style <TT>frame</TT></A>
+<LI><A NAME="tex2html315"
+  HREF="node5.html#SECTION00532000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Extra fields for style <TT>dock</TT></A>
+</UL></UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00500000000000000000"></A>
+<A NAME="chap:gr"></A>
+<BR>
+<SPAN CLASS="arabic">4</SPAN>. Graphical styles
+</H1>
+
+<P>
+This chapter first gives in section <A HREF="#sec:engines">4.1</A> a general outline 
+of how drawing engines are used, of style specifications and then
+in section <A HREF="#sec:defaultde">4.2</A> describes how to specify styles
+for the default drawing engine.
+
+<P>
+
+<H2><A NAME="SECTION00510000000000000000"></A>
+<A NAME="sec:engines"></A><A NAME="1665"></A><A NAME="1666"></A>
+<BR>
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Drawing engines, style specifications and sub-styles
+</H2>
+
+<P>
+Ion's drawing routines are abstracted into so-called drawing engine
+modules that can, again depending on the system, be dynamically
+loaded as needed. The drawing engine modules provide ''brushes''
+that objects can use to draw some high-level primitives such
+as borders and text boxes (in addition to simple text and rectangle
+drawing) on their windows and configure e.g. the shape and 
+background of the window. While the drawing engines therefore
+do not directly implement looks for each possible object (that
+would hardly be maintainable), different brush styles can be
+used to give a distinctive look to different objects and engines
+could interpret some styles as special cases. Style specifications 
+are strings of the form
+
+<P>
+<PRE>
+element1-element2-...-elementn
+</PRE>
+
+<P>
+An example of such a style specification is <TT>tab-frame</TT>;
+see the table in subsection <A HREF="#sec:styles">4.1.1</A> for more styles.
+
+<P>
+When an object asks for a brush of certain style, the selected
+drawing engine will attempt to find the closest match to this
+specification. The styles/brushes defined by the drawing engines 
+may have asterisks (<code>*</code>) as some of the elements indicating
+a match to anything. Exact matches are preferred to asterisk
+matches and longer matches to shorter. For example, let a brush
+for style <TT>foo-bar-baz</TT> be queried, then the following
+brushes are in order of preference:
+
+<P>
+<PRE>
+foo-bar-baz
+foo-*-baz
+foo-bar
+*
+foo-baz   -- Doesn't match, not selected!
+</PRE>
+
+<P>
+Some of the drawing primitives allow extra attributes to be
+specified, also in the form
+<PRE>
+attr1-attr2-...-attrn
+</PRE>
+These extra attributes are called <SPAN  CLASS="textit">substyles</SPAN><A NAME="1677"></A>
+and allow, for example, the state of the object to be indicated 
+by  different colour sets while keeping the interface at an 
+abstract level and the drawing engine completely ignorant 
+of the semantics - only the writer of the drawing engine
+configuration file has to know them. However the drawing 
+engine can again interpret known substyles as special cases
+and the default engine indeed does so with frame tab 
+tag and drag states.)
+
+<P>
+
+<H3><A NAME="SECTION00511000000000000000"></A>
+<A NAME="sec:styles"></A>
+<BR>
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Known styles and substyles
+</H3>
+
+<P>
+
+<H4><A NAME="SECTION00511100000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Frames</A>
+</H4>
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Style name</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>frame</TT></TD>
+<TD ALIGN="LEFT">Style for frames. 
+       Substyles: <TT>active</TT>, <TT>inactive</TT>.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>frame-tiled</TT></TD>
+<TD ALIGN="LEFT">A more specific style for tiled frames.
+        Substyles as for <TT>frame</TT>.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>frame-tiled-alt</TT></TD>
+<TD ALIGN="LEFT">An alternative style for tiled frames.
+        Often used to disable the tab-bar.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>frame-floating</TT></TD>
+<TD ALIGN="LEFT">A more specific style for floating
+        frames.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>frame-transient</TT></TD>
+<TD ALIGN="LEFT">A more specific style for frames
+        containing transient windows.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<H4><A NAME="SECTION00511200000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> Tabs and menu entries</A>
+</H4>
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Style name</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tab</TT></TD>
+<TD ALIGN="LEFT">Style for frames' tabs and menu entries. 
+       Substyles: combinations of the form <TT>a-s</TT> where
+       <TT>a</TT> is one of <TT>active</TT>/<TT>inactive</TT> and
+       <TT>s</TT> is one of <TT>selected</TT>/<TT>unselected</TT></TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tab-frame</TT></TD>
+<TD ALIGN="LEFT">A more specific style for frames' tabs.
+       Substyles: combinations of the form <TT>a-s-t-d-u</TT> where
+       <TT>a</TT> and <TT>s</TT> are as above and
+       <TT>t</TT> is one of <TT>tagged</TT>/<TT>not_tagged</TT>,
+       <TT>d</TT> is one of <TT>dragged</TT>/<TT>not_dragged</TT> and
+       <TT>u</TT> is one of <TT>activity</TT>/<TT>no_activity</TT>.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tab-frame-tiled</TT>,</TD>
+<TD ALIGN="LEFT">&nbsp;</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tab-frame-tiled-alt</TT>,</TD>
+<TD ALIGN="LEFT">&nbsp;</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tab-frame-floating</TT>,</TD>
+<TD ALIGN="LEFT">&nbsp;</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tab-frame-transient</TT></TD>
+<TD ALIGN="LEFT">More specific styles for frames in the
+        different modes.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tab-menuentry</TT></TD>
+<TD ALIGN="LEFT">A more specific style for entries in WMenus.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tab-menuentry-bigmenu</TT></TD>
+<TD ALIGN="LEFT">An alternate style for entries in WMenus.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<H4><A NAME="SECTION00511300000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> The rest</A>
+</H4>
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Style name</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>input</TT></TD>
+<TD ALIGN="LEFT">A style for WInputs.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>input-edln</TT></TD>
+<TD ALIGN="LEFT">A more specific style for WEdlns. 
+       Substyles: <TT>selection</TT> for selected text and
+       <TT>cursor</TT> for the cursor indicating current editing point.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>input-message</TT></TD>
+<TD ALIGN="LEFT">A more specific style for WMessages.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>input-menu</TT></TD>
+<TD ALIGN="LEFT">A more specific style for WMenus.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>input-menu-bigmenu</TT></TD>
+<TD ALIGN="LEFT">An alternate style for WMenus.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>moveres_display</TT></TD>
+<TD ALIGN="LEFT">The box displaying position/size when
+       moving or resizing frames.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>dock</TT></TD>
+<TD ALIGN="LEFT">The dock.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<H2><A NAME="SECTION00520000000000000000"></A>
+<A NAME="sec:defaultde"></A>
+<BR>
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> Defining styles for the default drawing engine
+</H2>
+
+<P>
+Drawing engine style files are usually named
+<SPAN  CLASS="textit">look_foo.lua</SPAN> where <SPAN  CLASS="textit">foo</SPAN> is the name of the
+style. The file that Ion loads on startup or when
+<A HREF="node7.html#fn:gr.read_config"><TT>gr.read_config</TT></A> is called, however, is <SPAN  CLASS="textit">look.lua</SPAN>
+and should usually be symlinked to or a copy of of some
+<SPAN  CLASS="textit">look_foo.lua</SPAN>.
+
+<P>
+
+<H3><A NAME="SECTION00521000000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> The structure of the configuration files</A>
+</H3>
+
+<P>
+The first thing to do in a stylefile is to choose the drawing
+engine, possibly loading the module as well. This is done
+with the following chunk of code.
+
+<P>
+<PRE>
+if not gr.select_engine("de") then 
+    return 
+end
+</PRE>
+
+<P>
+The <A HREF="node7.html#fn:gr.select_engine"><TT>gr.select_engine</TT></A> function sees if the engine
+given as argument is registered (the default drawing engine is
+simply called ''de''). If the engine could not be found, it
+tries to load a module of the same name. If the engine still
+is not registered, <A HREF="node7.html#fn:gr.select_engine"><TT>gr.select_engine</TT></A> returns <TT>false</TT>
+and in this case we also exit the style setup script.
+If the engine was found, <A HREF="node7.html#fn:gr.select_engine"><TT>gr.select_engine</TT></A> sees that
+further requests for brushes are forwarded to that engine
+and returns <TT>true</TT>.
+
+<P>
+Before defining new styles it may be a good idea to clear old
+styles from memory so if the old configuration defines more
+specific styles than the new, the old styles don't override 
+those specified by the new configuration. That can be done by
+calling
+
+<P>
+<PRE>
+de.reset()
+</PRE>
+
+<P>
+After this the new styles can be defined with <A HREF="node7.html#fn:de.defstyle"><TT>de.defstyle</TT></A>
+as explained in the next subsection. Finally, after the styles have
+been defined we must ask objects on the screen to look up new brushes
+to reflect the changes in configuration. This is done with
+
+<P>
+<PRE>
+gr.refresh()
+</PRE>
+
+<P>
+
+<H3><A NAME="SECTION00522000000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Defining the styles</A>
+</H3>
+
+<P>
+Styles for the default drawing engine are defined with the
+function <A HREF="node7.html#fn:de.defstyle"><TT>de.defstyle</TT></A>. It has two arguments the first being
+a style specification as explained in previous sections and the second
+a table whose fields describe the style:
+
+<P>
+<PRE>
+de.defstyle("some-style", {
+    attribute = value,
+    ...
+})
+</PRE>
+
+<P>
+The supported attributes are described in tables below. The different
+border elements and styles referred to there are explained in Figure
+<A HREF="#fig:borders">4.1</A>.
+
+<P>
+
+<DIV ALIGN="CENTER"><A NAME="fig:borders"></A><A NAME="1777"></A>
+<TABLE>
+<CAPTION ALIGN="BOTTOM"><STRONG>Figure 4.1:</STRONG>
+Sketch of different border styles and elements</CAPTION>
+<TR><TD><PRE>
+Elevated:        Inlaid:         Ridge:            Groove:        
+  hhhhhhhhhhhs     ............           hhhhhhhhhhhs      sssssssssssh
+  h..........s     .sssssssssh.           h..........s      s..........h
+  h.        .s     .s        h.           h.sssssssh.s      s.hhhhhhhs.h
+  h.        .s     .s        h.           h.s      h.s      s.h      s.h
+  h.        .s     .s        h.           h.shhhhhhh.s      s.hsssssss.h
+  h..........s     .shhhhhhhhh.           h..........s      s..........h
+  hsssssssssss     ............           hsssssssssss      shhhhhhhhhhh
+
+h = highlight, s = shadow, . = padding
+</PRE></TD></TR>
+</TABLE>
+</DIV>
+
+<P>
+
+<H4><A NAME="SECTION00522100000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Colours</A>
+</H4>
+
+<P>
+Each of these fields a string of the form that can be
+passed to <TT>XAllocNamedColor</TT>. Valid strings are e.g.
+hexadecimal RGB specifications of the form
+<TT>#RRGGBB</TT> and colour names as specified
+in <SPAN  CLASS="textit">/usr/X11R6/lib/X11/rgb.txt</SPAN> (exact path varying).
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>highlight_colour</TT></TD>
+<TD ALIGN="LEFT">Colour for the ''highlight'' part of a border.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>shadow_colour</TT></TD>
+<TD ALIGN="LEFT">Colour for the ''highlight'' part of a border.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>foreground_colour</TT></TD>
+<TD ALIGN="LEFT">Colour for the normal drawing operations, e.g. text.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>background_colour</TT></TD>
+<TD ALIGN="LEFT">Window background colour (unless transparency is enabled) and
+       background colour boxes.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>padding_colour</TT></TD>
+<TD ALIGN="LEFT">Colour for the ''padding'' part of a border border. Set to 
+       <TT>background_colour</TT> if unset.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<H4><A NAME="SECTION00522200000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Borders and widths</A>
+</H4>
+
+<P>
+All other fields below except <TT>border_style</TT> are non-negative integers
+indicating a number of pixels.
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>border_style</TT></TD>
+<TD ALIGN="LEFT">A string indicating the style of border; one of
+                    elevated/inlaid/ridge/groove as seen in the
+                    above sketch.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>highlight_pixels</TT></TD>
+<TD ALIGN="LEFT">Width of the highlight part of the border in pixels.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>shadow_pixels</TT></TD>
+<TD ALIGN="LEFT">Width of the shadow part of the border in pixels.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>padding_pixels</TT></TD>
+<TD ALIGN="LEFT">Width of the padding part of the border in pixels.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>spacing</TT></TD>
+<TD ALIGN="LEFT">Space to be left between all kinds of boxes.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<H4><A NAME="SECTION00522300000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> Text</A>
+</H4>
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>font</TT></TD>
+<TD ALIGN="LEFT">Font to be used in text-drawing operations; standard X font
+            name.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>text_align</TT></TD>
+<TD ALIGN="LEFT">How text is to be aligned in text boxes/tabs; one of
+                  the strings left/right/center.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<H4><A NAME="SECTION00522400000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous</A>
+</H4>
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>transparent_background</TT></TD>
+<TD ALIGN="LEFT">Should windows' that use this style
+       background be transparent? true/false.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>based_on</TT></TD>
+<TD ALIGN="LEFT">The name of a previously defined style that this
+       style should be based on.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<H4><A NAME="SECTION00522500000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN> Substyles</A>
+</H4>
+
+<P>
+As discussed in previous sections, styles may have substyles to e.g.
+indicate different states of the object being drawn. The ''de'' engine
+limits what can be configured in substyles to the set of colours in the
+first table above, but also specifically interprets for the main style
+<TT>tab-frame</TT> the substyles <TT>*-*-tagged</TT> and <TT>*-*-*-dragged</TT>
+by, respectively, drawing a right angle shape at the top right corner 
+of a tab and by shading the tab with a stipple pattern. Also for
+menus the substyles <TT>*-*-submenu</TT> are handled as a special case.
+
+<P>
+Substyles are defined with the function <A HREF="node7.html#fn:de.substyle"><TT>de.substyle</TT></A> within the
+table defining the main style. The parameters to this function are 
+similar to those of <A HREF="node7.html#fn:de.defstyle"><TT>de.defstyle</TT></A>.
+
+<P>
+<PRE>
+de.defstyle("some-style", {
+   ...
+   de.substyle("some-substyle", {
+      ...
+   }),
+   ...
+})
+</PRE>
+
+<P>
+
+<H3><A NAME="SECTION00523000000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> An example</A>
+</H3>
+
+<P>
+The following shortened segment from <SPAN  CLASS="textit">look_cleanviolet.lua</SPAN>
+should help to clarify the matters discussed in the previous
+subsection.
+
+<P>
+<PRE>
+de.defstyle("*", {
+    -- Gray background
+    highlight_colour = "#eeeeee",
+    shadow_colour = "#eeeeee",
+    background_colour = "#aaaaaa",
+    foreground_colour = "#000000",
+    
+    shadow_pixels = 1,
+    highlight_pixels = 1,
+    padding_pixels = 1,
+    spacing = 0,
+    border_style = "elevated",
+    
+    font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*",
+    text_align = "center",
+})
+
+de.defstyle("tab-frame", {
+    based_on = "*",
+    
+    de.substyle("active-selected", {
+        -- Violet tab
+        highlight_colour = "#aaaacc",
+        shadow_colour = "#aaaacc",
+        background_colour = "#666699",
+        foreground_colour = "#eeeeee",
+    }),
+
+    --  More substyles would follow ...
+})
+</PRE>
+
+<P>
+
+<H2><A NAME="SECTION00530000000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN> Miscellaneous settings</A>
+</H2>
+
+<P>
+The following style fields are independent of the drawing engine used,
+but are related to objects' styles and therefore configured in the drawing
+engine configuration file.
+
+<P>
+
+<H3><A NAME="SECTION00531000000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Extra fields for style <TT>frame</TT></A>
+</H3>
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>bar</TT></TD>
+<TD ALIGN="LEFT">Controls the style of the tab-bar. Possible values
+        are the strings <TT>"none"</TT>, <TT>"inside"</TT>, <TT>"outside"</TT>
+        and <TT>"shaped"</TT>, with the last providing the PWM-style
+        tab-bars for floating frames.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>floatframe_tab_min_w</TT></TD>
+<TD ALIGN="LEFT">Minimum tab width in pixels for
+        the shaped style, given that this number times number of tabs
+        doesn't exceed frame width.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>floatframe_bar_max_w_q</TT></TD>
+<TD ALIGN="LEFT">Maximum tab-bar width quotient of
+       frame width for the shaped styles. A number in the 
+       interval <SPAN CLASS="MATH"></SPAN>.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<H3><A NAME="SECTION00532000000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Extra fields for style <TT>dock</TT></A>
+</H3>
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>outline_style</TT></TD>
+<TD ALIGN="LEFT">How borders are drawn:
+                       <TT>"none"</TT> - no border,
+                       <TT>"all"</TT> - border around whole dock,
+                       <TT>"each"</TT> - border around each dockapp.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tile_size</TT></TD>
+<TD ALIGN="LEFT">A table with entries <TT>width</TT> and <TT>height</TT>,
+       indicating the width and height of tiles in pixels.</TD>
+</TR>
+</TABLE>
+
+<P>
+Hopefully that's enough to get you started in writing new style
+configuration files for Ion. When in doubt, study the existing
+style configuration files.
+
+<P>
+
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html297"
+  HREF="node6.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html291"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html285"
+  HREF="node4.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html293"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html295"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html298"
+  HREF="node6.html">5. Scripting</A>
+<B> Up:</B> <A NAME="tex2html292"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html286"
+  HREF="node4.html">3. Basic configuration</A>
+ &nbsp; <B>  <A NAME="tex2html294"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html296"
+  HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node6.html b/doc/ionconf/node6.html
new file mode 100644 (file)
index 0000000..b1bbe8b
--- /dev/null
@@ -0,0 +1,351 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>5. Scripting</TITLE>
+<META NAME="description" CONTENT="5. Scripting">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node7.html">
+<LINK REL="previous" HREF="node5.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node7.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html328"
+  HREF="node7.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html322"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html316"
+  HREF="node5.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html324"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html326"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html329"
+  HREF="node7.html">6. Function reference</A>
+<B> Up:</B> <A NAME="tex2html323"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html317"
+  HREF="node5.html">4. Graphical styles</A>
+ &nbsp; <B>  <A NAME="tex2html325"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html327"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html330"
+  HREF="node6.html#SECTION00610000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Hooks</A>
+<LI><A NAME="tex2html331"
+  HREF="node6.html#SECTION00620000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Referring to regions</A>
+<UL>
+<LI><A NAME="tex2html332"
+  HREF="node6.html#SECTION00621000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Direct object references</A>
+<LI><A NAME="tex2html333"
+  HREF="node6.html#SECTION00622000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Name-based lookups</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html334"
+  HREF="node6.html#SECTION00630000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Alternative winprop selection criteria</A>
+<LI><A NAME="tex2html335"
+  HREF="node6.html#SECTION00640000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">4</SPAN> Writing <TT>ion-statusd</TT> monitors</A>
+</UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00600000000000000000"></A>
+<A NAME="chap:tricks"></A>
+<BR>
+<SPAN CLASS="arabic">5</SPAN>. Scripting
+</H1>
+
+<P>
+This chapter documents some additional features of the Ion configuration
+and scripting interface that can be used for more advanced scripting than
+the basic configuration exlained in chapter <A HREF="node4.html#chap:config">3</A>.
+
+<P>
+
+<H2><A NAME="SECTION00610000000000000000"></A>
+<A NAME="sec:hooks"></A>
+<BR>
+<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Hooks
+</H2>
+
+<P>
+Hooks are lists of functions to be called when a certain event occurs.
+There are two types of them; normal and ''alternative'' hooks. Normal
+hooks do not return anything, but alt-hooks should return a boolean
+indicating whether it handled its assigned task succesfully. In the case
+that <TT>true</TT> is returned, remaining handlers are not called.
+
+<P>
+Hook handlers are registered by first finding the hook
+with <A HREF="#fn:ioncore.get_hook"><TT>ioncore.get_hook</TT></A> and then calling <A HREF="#fn:WHook.add"><TT>WHook.add</TT></A>
+on the (succesfull) result with the handler as parameter. Similarly
+handlers are unregistered with <A HREF="#fn:WHook.remove"><TT>WHook.remove</TT></A>. For example:
+
+<P>
+<PRE>
+ioncore.get_hook("ioncore_snapshot_hook"):add(
+    function() print("Snapshot hook called.") end
+)
+</PRE>
+
+<P>
+In this example the hook handler has no parameters, but many hook
+handlers do. The types of parameters for each hook are listed in
+the hook reference, section <A HREF="node7.html#sec:hookref">6.8</A>.
+
+<P>
+Note that many of the hooks are called in ''protected mode'' and can not 
+use any functions that modify Ion's internal state. TODO: More detailed 
+documentation when this is final.
+
+<P>
+
+<H2><A NAME="SECTION00620000000000000000">
+<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Referring to regions</A>
+</H2>
+
+<P>
+
+<H3><A NAME="SECTION00621000000000000000">
+<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Direct object references</A>
+</H3>
+
+<P>
+All Ion objects are passed to Lua scriptss as 'userdatas', and you may
+safely store such object references for future use. The C-side object
+may be destroyed while Lua still refers to the object. All exported
+functions gracefully fail in such a case, but if you need to explicitly
+test that the C-side object still exists, use <A HREF="#fn:obj_exists"><TT>obj_exists</TT></A>.
+
+<P>
+As an example, the following short piece of code implements 
+bookmarking:
+
+<P>
+<PRE>
+local bookmarks={}
+
+-- Set bookmark bm point to the region reg
+function set_bookmark(bm, reg)
+    bookmarks[bm]=reg
+end
+
+-- Go to bookmark bm
+function goto_bookmark(bm)
+    if bookmarks[bm] then
+        -- We could check that bookmarks[bm] still exists, if we
+        -- wanted to avoid an error message.
+        bookmarks[bm]:goto()
+    end
+end
+</PRE>
+
+<P>
+
+<H3><A NAME="SECTION00622000000000000000">
+<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Name-based lookups</A>
+</H3>
+
+<P>
+If you want to a single non-WClientWin region with an exact known 
+name, use <A HREF="node7.html#fn:ioncore.lookup_region"><TT>ioncore.lookup_region</TT></A>. If you want a list of all regions,
+use <A HREF="node7.html#fn:ioncore.region_list"><TT>ioncore.region_list</TT></A>. Both functions accept an optional argument
+that can be used to specify that the returned region(s) must be of a more 
+specific type. Client windows live in a different namespace and for them
+you should use the equivalent functions <A HREF="node7.html#fn:ioncore.lookup_clientwin"><TT>ioncore.lookup_clientwin</TT></A>
+and <A HREF="node7.html#fn:ioncore.clientwin_list"><TT>ioncore.clientwin_list</TT></A>.
+
+<P>
+To get the name of an object, use <A HREF="node7.html#fn:WRegion.name"><TT>WRegion.name</TT></A>. Please be
+aware, that the names of client windows reflect their titles and
+are subject to changes. To change the name of a non-client window
+region, use <A HREF="node7.html#fn:WRegion.set_name"><TT>WRegion.set_name</TT></A>.
+
+<P>
+
+<H2><A NAME="SECTION00630000000000000000">
+<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Alternative winprop selection criteria</A>
+</H2>
+
+<P>
+It is possible to write more complex winprop selection routines than
+those described in section <A HREF="node4.html#sec:winprops">3.5</A>. To match a particular
+winprop using whatever way you want to, just set the <TT>match</TT>
+field of the winprop to a function that receives the client window
+as its sole parameter, and that returns <TT>true</TT> if the winprop
+matches, and <TT>false</TT> otherwise.
+
+<P>
+The class, instance and role properties can be obtained with
+<A HREF="node7.html#fn:WClientWin.get_ident"><TT>WClientWin.get_ident</TT></A>, and the title with <A HREF="node7.html#fn:WRegion.name"><TT>WRegion.name</TT></A>.
+If you want to match against (almost) arbitrary window properties,
+have a look at the documentation for the following functions, and
+their standard Xlib counterparts: <A HREF="node7.html#fn:ioncore.x_intern_atom"><TT>ioncore.x_intern_atom</TT></A>
+(XInternAtom), <A HREF="node7.html#fn:ioncore.x_get_window_property"><TT>ioncore.x_get_window_property</TT></A> (XGetWindowProperty),
+and <A HREF="node7.html#fn:ioncore.x_get_text_property"><TT>ioncore.x_get_text_property</TT></A> (XGetTextProperty).
+
+<P>
+
+<P>
+
+<H2><A NAME="SECTION00640000000000000000">
+<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">4</SPAN> Writing <TT>ion-statusd</TT> monitors</A>
+</H2>
+
+<P>
+All statusbar meters that do not monitor the internal state of Ion should
+go in the separate <TT>ion-statusd</TT> program. 
+
+<P>
+Whenever the user requests a meter <TT>%foo</TT> or <TT>%foo_bar</TT> to be 
+inserted in a statusbar, <SPAN  CLASS="textit">mod_statusbar</SPAN> asks <TT>ion-statusd</TT> to
+load <A HREF="#fn:statusd_foo.lua"><TT>statusd_foo.lua</TT></A> on its search path (same as that for Ion-side 
+scripts). This script should then supply all meters with the initial part
+'<TT>foo</TT>'.
+
+<P>
+To provide this value, the script should simply call <TT>statusd.inform</TT>
+with the name of the meter and the value as a string.
+Additionally the script should provide a 'template' for the meter to
+facilitate expected width calculation by <SPAN  CLASS="textit">mod_statusbar</SPAN>, and
+may provide a 'hint' for colour-coding the value. The interpretation
+of hints depends on the graphical style in use, and currently the
+stock styles support the <TT>normal</TT>, <TT>important</TT> and 
+<TT>critical</TT> hints.
+
+<P>
+In our example of the 'foo monitor', at script init we might broadcast
+the template as follows:
+
+<P>
+<PRE>
+statusd.inform("foo_template", "000")
+</PRE>
+
+<P>
+To inform <SPAN  CLASS="textit">mod_statusbar</SPAN> of the actual value of the meter and
+indicate that the value is critical if above 100, we might write the
+following function:
+
+<P>
+<PRE>
+local function inform_foo(foo)
+    statusd.inform("foo", tostring(foo))
+    if foo&gt;100 then
+        statusd.inform("foo_hint", "critical")
+    else
+        statusd.inform("foo_hint", "normal")
+    end
+end
+</PRE>
+
+<P>
+To periodically update the value of the meter, we must use timers.
+First we must create one:
+
+<P>
+<PRE>
+local foo_timer=statusd.create_timer()
+</PRE>
+
+<P>
+Then we write a function to be called whenever the timer expires.
+This function must also restart the timer.
+
+<P>
+<PRE>
+local function update_foo()
+    local foo= ... measure foo somehow ...
+    inform_foo(foo)
+    foo_timer:set(settings.update_interval, update_foo)
+end
+</PRE>
+
+<P>
+Finally, at the end of our script we want to do the initial
+measurement, and set up timer for further measurements:
+
+<P>
+<PRE>
+update_foo()
+</PRE>
+
+<P>
+If our scripts supports configurable parameters, the following code
+(at the beginning of the script) will allow them to be configured in
+<SPAN  CLASS="textit">cfg_statusbar.lua</SPAN> and passed to the status daemon and our script:
+
+<P>
+<PRE>
+local defaults={
+    update_interval=10*1000, -- 10 seconds
+}
+                
+local settings=table.join(statusd.get_config("foo"), defaults)
+</PRE>
+
+<P>
+
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html328"
+  HREF="node7.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html322"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html316"
+  HREF="node5.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html324"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html326"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html329"
+  HREF="node7.html">6. Function reference</A>
+<B> Up:</B> <A NAME="tex2html323"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html317"
+  HREF="node5.html">4. Graphical styles</A>
+ &nbsp; <B>  <A NAME="tex2html325"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html327"
+  HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node7.html b/doc/ionconf/node7.html
new file mode 100644 (file)
index 0000000..5a3e390
--- /dev/null
@@ -0,0 +1,6232 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>6. Function reference</TITLE>
+<META NAME="description" CONTENT="6. Function reference">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node8.html">
+<LINK REL="previous" HREF="node6.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node8.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html348"
+  HREF="node8.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html342"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html336"
+  HREF="node6.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html344"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html346"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html349"
+  HREF="node8.html">A. The GNU General</A>
+<B> Up:</B> <A NAME="tex2html343"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html337"
+  HREF="node6.html">5. Scripting</A>
+ &nbsp; <B>  <A NAME="tex2html345"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html347"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html350"
+  HREF="node7.html#SECTION00710000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN> Functions defined in <SPAN  CLASS="textit">ioncore</SPAN></A>
+<UL>
+<LI><A NAME="tex2html351"
+  HREF="node7.html#SECTION00711000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> WClientWin functions</A>
+<LI><A NAME="tex2html352"
+  HREF="node7.html#SECTION00712000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> WFrame functions</A>
+<LI><A NAME="tex2html353"
+  HREF="node7.html#SECTION00713000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> WGroup functions</A>
+<LI><A NAME="tex2html354"
+  HREF="node7.html#SECTION00714000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">4</SPAN> WGroupCW functions</A>
+<LI><A NAME="tex2html355"
+  HREF="node7.html#SECTION00715000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">5</SPAN> WGroupWS functions</A>
+<LI><A NAME="tex2html356"
+  HREF="node7.html#SECTION00716000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">6</SPAN> WInfoWin functions</A>
+<LI><A NAME="tex2html357"
+  HREF="node7.html#SECTION00717000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN> WMPlex functions</A>
+<LI><A NAME="tex2html358"
+  HREF="node7.html#SECTION00718000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN> WMoveresMode functions</A>
+<LI><A NAME="tex2html359"
+  HREF="node7.html#SECTION00719000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN> WRegion functions</A>
+<LI><A NAME="tex2html360"
+  HREF="node7.html#SECTION007110000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">10</SPAN> WRootWin functions</A>
+<LI><A NAME="tex2html361"
+  HREF="node7.html#SECTION007111000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">11</SPAN> WScreen functions</A>
+<LI><A NAME="tex2html362"
+  HREF="node7.html#SECTION007112000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">12</SPAN> WWindow functions</A>
+<LI><A NAME="tex2html363"
+  HREF="node7.html#SECTION007113000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">13</SPAN> global functions</A>
+<LI><A NAME="tex2html364"
+  HREF="node7.html#SECTION007114000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">14</SPAN> gr functions</A>
+<LI><A NAME="tex2html365"
+  HREF="node7.html#SECTION007115000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">15</SPAN> string functions</A>
+<LI><A NAME="tex2html366"
+  HREF="node7.html#SECTION007116000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN> table functions</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html367"
+  HREF="node7.html#SECTION00720000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN> Functions defined in <SPAN  CLASS="textit">mod_tiling</SPAN></A>
+<UL>
+<LI><A NAME="tex2html368"
+  HREF="node7.html#SECTION00721000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> WSplit functions</A>
+<LI><A NAME="tex2html369"
+  HREF="node7.html#SECTION00722000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> WSplitInner functions</A>
+<LI><A NAME="tex2html370"
+  HREF="node7.html#SECTION00723000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> WSplitRegion functions</A>
+<LI><A NAME="tex2html371"
+  HREF="node7.html#SECTION00724000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN> WSplitSplit functions</A>
+<LI><A NAME="tex2html372"
+  HREF="node7.html#SECTION00725000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN> WTiling functions</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html373"
+  HREF="node7.html#SECTION00730000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN> Functions defined in <SPAN  CLASS="textit">mod_query</SPAN></A>
+<UL>
+<LI><A NAME="tex2html374"
+  HREF="node7.html#SECTION00731000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> WComplProxy functions</A>
+<LI><A NAME="tex2html375"
+  HREF="node7.html#SECTION00732000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> WEdln functions</A>
+<LI><A NAME="tex2html376"
+  HREF="node7.html#SECTION00733000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> WInput functions</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html377"
+  HREF="node7.html#SECTION00740000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN> Functions defined in <SPAN  CLASS="textit">mod_menu</SPAN></A>
+<UL>
+<LI><A NAME="tex2html378"
+  HREF="node7.html#SECTION00741000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> WMenu functions</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html379"
+  HREF="node7.html#SECTION00750000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN> Functions defined in <SPAN  CLASS="textit">mod_dock</SPAN></A>
+<UL>
+<LI><A NAME="tex2html380"
+  HREF="node7.html#SECTION00751000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> WDock functions</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html381"
+  HREF="node7.html#SECTION00760000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">6</SPAN> Functions defined in <SPAN  CLASS="textit">mod_sp</SPAN></A>
+<LI><A NAME="tex2html382"
+  HREF="node7.html#SECTION00770000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN> Functions defined in <SPAN  CLASS="textit">de</SPAN></A>
+<LI><A NAME="tex2html383"
+  HREF="node7.html#SECTION00780000000000000000"><SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN> Hooks</A>
+</UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00700000000000000000"></A>
+<A NAME="sec:exports"></A>
+<BR>
+<SPAN CLASS="arabic">6</SPAN>. Function reference
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION00710000000000000000"></A>
+<A NAME="sec:ioncoreref"></A>
+<BR>
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN> Functions defined in <SPAN  CLASS="textit">ioncore</SPAN>
+</H2>
+
+  <DL>
+<DD><A NAME="4188"></A>
+<A NAME="4189"></A>
+<A NAME="fn:ioncore.TR"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.TR(s, ...)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>gettext+string.format
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4190"></A>
+<A NAME="4191"></A>
+<A NAME="fn:ioncore.bdoc"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.bdoc(text)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Used to enter documentation among bindings so that other programs
+ can read it. Does nothing.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4192"></A>
+<A NAME="4193"></A>
+<A NAME="fn:ioncore.chdir_for"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.chdir_for(reg, dir)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Change default working directory for new programs started in <TT>reg</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4194"></A>
+<A NAME="4195"></A>
+<A NAME="fn:ioncore.compile_cmd"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.compile_cmd(cmd, guard)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Compile string <TT>cmd</TT> into a bindable function. Within <TT>cmd</TT>, the
+ variable ''<TT>_</TT>'' (underscore) can be used to refer to the object 
+ that was selecting for the bound action and chosen to handle it.
+ The  variable ''<TT>_sub</TT>'' refers to a ''currently active'' sub-object 
+ of <TT>_</TT>, or a sub-object where the action loading to the binding 
+ being called actually occured.
+
+<P>
+The string <TT>guard</TT>  maybe set to pose limits on <TT>_sub</TT>. Currently 
+ supported guards are <TT>_sub:non-nil</TT> and <TT>_sub:WFoobar</TT>, where 
+ WFoobar is a class.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4196"></A>
+<A NAME="4197"></A>
+<A NAME="fn:ioncore.create_ws"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.create_ws(scr, tmpl, no_default)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Create new workspace on screen <TT>scr</TT>. The table <TT>tmpl</TT>
+ may be used to override parts of <TT>default_ws_params</TT>,
+ and <TT>no_default</TT> may be set to <TT>true</TT> to complete ignore it.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4198"></A>
+<A NAME="4199"></A>
+<A NAME="fn:ioncore.defbindings"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.defbindings(context, bindings)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Define bindings for context <TT>context</TT>. Here <TT>binding</TT> is
+ a table composed of entries created with <A HREF="#fn:ioncore.kpress"><TT>ioncore.kpress</TT></A>, 
+ etc.; see section <A HREF="node4.html#sec:bindings">3.3</A> for details.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4200"></A>
+<A NAME="4201"></A>
+<A NAME="fn:ioncore.defctxmenu"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.defctxmenu(ctx, ...)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Define context menu for context <TT>ctx</TT>, <TT>tab</TT> being a table 
+ of menu entries.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4202"></A>
+<A NAME="4203"></A>
+<A NAME="fn:ioncore.defmenu"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.defmenu(name, tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Define a new menu with <TT>name</TT> being the menu's name and <TT>tab</TT> 
+ being a table of menu entries. If <TT>tab.append</TT> is set, the entries 
+ are appended to previously-defined ones, if possible.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4204"></A>
+<A NAME="4205"></A>
+<A NAME="fn:ioncore.defwinprop"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.defwinprop(list)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Define a winprop. For more information, see section <A HREF="node4.html#sec:winprops">3.5</A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4206"></A>
+<A NAME="4207"></A>
+<A NAME="fn:ioncore.exec_on"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.exec_on(reg, cmd, merr_internal)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Run <TT>cmd</TT> with the environment variable DISPLAY set to point to the
+ root window of the X screen <TT>reg</TT> is on. If <TT>cmd</TT> is prefixed
+ by a colon (<TT>:</TT>), the following command is executed in an xterm
+ (or other terminal emulator) with the help of the <TT>ion-runinxterm</TT> 
+ script. If the command is prefixed by two colons, <TT>ion-runinxterm</TT>
+ will ask you to press enter after the command is finished, even if it
+ returns succesfully.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4208"></A>
+<A NAME="4209"></A>
+<A NAME="fn:ioncore.read_savefile"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table ioncore.read_savefile(string basename)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Read a savefile.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4210"></A>
+<A NAME="4211"></A>
+<A NAME="fn:ioncore.get_savefile"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string ioncore.get_savefile(string basename)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get a file name to save (session) data in. The string <TT>basename</TT> 
+ should contain no path or extension components.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4212"></A>
+<A NAME="4213"></A>
+<A NAME="fn:ioncore.lookup_script"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string ioncore.lookup_script(string file, string sp)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Lookup script <TT>file</TT>. If <TT>try_in_dir</TT> is set, it is tried
+ before the standard search path.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4214"></A>
+<A NAME="4215"></A>
+<A NAME="fn:ioncore.write_savefile"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool ioncore.write_savefile(string basename, table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Write <TT>tab</TT> in file with basename <TT>basename</TT> in the
+ session directory.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4216"></A>
+<A NAME="4217"></A>
+<A NAME="fn:ioncore.find_manager"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.find_manager(obj, t)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Find an object with type name <TT>t</TT> managing <TT>obj</TT> or one of
+ its managers.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4218"></A>
+<A NAME="4219"></A>
+<A NAME="fn:ioncore.get_dir_for"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.get_dir_for(reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get default working directory for new programs started in <TT>reg</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4220"></A>
+<A NAME="4221"></A>
+<A NAME="fn:ioncore.getbindings"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.getbindings(maybe_context)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get a table of all bindings.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4222"></A>
+<A NAME="4223"></A>
+<A NAME="fn:ioncore.getctxmenu"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.getctxmenu(name)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns a context menu defined with <A HREF="#fn:ioncore.defctxmenu"><TT>ioncore.defctxmenu</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4224"></A>
+<A NAME="4225"></A>
+<A NAME="fn:ioncore.getmenu"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.getmenu(name)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns a menu defined with <A HREF="#fn:ioncore.defmenu"><TT>ioncore.defmenu</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4226"></A>
+<A NAME="4227"></A>
+<A NAME="fn:ioncore.getwinprop"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.getwinprop(cwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Find winprop table for <TT>cwin</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4228"></A>
+<A NAME="4229"></A>
+<A NAME="fn:ioncore.aboutmsg"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string ioncore.aboutmsg()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns an about message (version, author, copyright notice).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4230"></A>
+<A NAME="4231"></A>
+<A NAME="fn:ioncore.activity_first"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion ioncore.activity_first()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return first regio non activity list.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4232"></A>
+<A NAME="4233"></A>
+<A NAME="fn:ioncore.activity_list"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table ioncore.activity_list()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return list of regions with activity/urgency bit set.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4234"></A>
+<A NAME="4235"></A>
+<A NAME="fn:ioncore.clear_tags"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.clear_tags()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Untag all regions.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4236"></A>
+<A NAME="4237"></A>
+<A NAME="fn:ioncore.clientwin_list"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table ioncore.clientwin_list()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return a list of all client windows.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4238"></A>
+<A NAME="4239"></A>
+<A NAME="fn:ioncore.current"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion ioncore.current()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the currently focused region, if any.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4240"></A>
+<A NAME="4241"></A>
+<A NAME="fn:ioncore.defshortening"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool ioncore.defshortening(string rx, string rule, bool always)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Add a rule describing how too long titles should be shortened to fit in tabs.
+ The regular expression <TT>rx</TT> (POSIX, not Lua!) is used to match titles
+ and when <TT>rx</TT> matches, <TT>rule</TT> is attempted to use as a replacement
+ for title. If <TT>always</TT> is set, the rule is used even if no shortening 
+ is necessary.
+
+<P>
+Similarly to sed's 's' command, <TT>rule</TT> may contain characters that are
+ inserted in the resulting string and specials as follows:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Special</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT">$0</TD>
+<TD ALIGN="LEFT">Place the original string here.</TD>
+</TR>
+<TR><TD ALIGN="LEFT">$1 to $9</TD>
+<TD ALIGN="LEFT">Insert n:th capture here (as usual,captures are surrounded
+                 by parentheses in the regex).</TD>
+</TR>
+<TR><TD ALIGN="LEFT">$|</TD>
+<TD ALIGN="LEFT">Alternative shortening separator. The shortening described
+                 before the first this kind of separator is tried first and
+                 if it fails to make the string short enough, the next is 
+                  tried, and so on.</TD>
+</TR>
+<TR><TD ALIGN="LEFT">$&lt;</TD>
+<TD ALIGN="LEFT">Remove characters on the left of this marker to shorten the
+                 string.</TD>
+</TR>
+<TR><TD ALIGN="LEFT">$&gt;</TD>
+<TD ALIGN="LEFT">Remove characters on the right of this marker to shorten the
+                 string. Only the first $&lt; or $&gt; within an alternative 
+                 shortening is used.</TD>
+</TR>
+</TABLE>
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4242"></A>
+<A NAME="4243"></A>
+<A NAME="fn:ioncore.exec"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>integer ioncore.exec(string cmd)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Run <TT>cmd</TT> with the environment variable DISPLAY set to point to the
+ X display the WM is running on. No specific screen is set unlike with
+ <A HREF="#fn:WRootWin.exec_on"><TT>WRootWin.exec_on</TT></A>. The PID of the (shell executing the) new 
+ process is returned.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4244"></A>
+<A NAME="4245"></A>
+<A NAME="fn:ioncore.find_screen_id"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WScreen ioncore.find_screen_id(integer id)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Find the screen with numerical id <TT>id</TT>. If Xinerama is
+ not present, <TT>id</TT> corresponds to X screen numbers. Otherwise
+ the ids are some arbitrary ordering of Xinerama rootwins.
+ If <TT>id</TT> is <SPAN CLASS="MATH"></SPAN>, the screen with the highest id is returned.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4246"></A>
+<A NAME="4247"></A>
+<A NAME="fn:ioncore.get"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table ioncore.get()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get ioncore basic settings. For details see <A HREF="#fn:ioncore.set"><TT>ioncore.set</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4248"></A>
+<A NAME="4249"></A>
+<A NAME="fn:ioncore.get_paths"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table ioncore.get_paths(table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get important directories (userdir, sessiondir, searchpath).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4250"></A>
+<A NAME="4251"></A>
+<A NAME="fn:ioncore.goto_activity"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool ioncore.goto_activity()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Go to first region on activity list.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4252"></A>
+<A NAME="4253"></A>
+<A NAME="fn:ioncore.goto_first"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion ioncore.goto_first(WRegion reg, string dirstr, table param)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Go to first region within <TT>reg</TT> in direction <TT>dirstr</TT>
+ (up/down/left/right/beg/end/any). For information on <TT>param</TT>,
+ see <A HREF="#fn:ioncore.navi_next"><TT>ioncore.navi_next</TT></A>. Additionally this function supports
+ the boolean <TT>nofront</TT> field, for not bringing the object to
+ front.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4254"></A>
+<A NAME="4255"></A>
+<A NAME="fn:ioncore.goto_next"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion ioncore.goto_next(WRegion reg, string dirstr, table param)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Go to region next from <TT>reg</TT> in direction <TT>dirstr</TT>
+ (up/down/left/right/next/prev/any). For information on <TT>param</TT>,
+ see <A HREF="#fn:ioncore.navi_next"><TT>ioncore.navi_next</TT></A>. Additionally this function supports
+ the boolean <TT>nofront</TT> field, for not bringing the object to
+ front.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4256"></A>
+<A NAME="4257"></A>
+<A NAME="fn:ioncore.goto_next_screen"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WScreen ioncore.goto_next_screen()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Switch focus to the next screen and return it.
+
+<P>
+Note that this function is asynchronous; the screen will not
+ actually have received the focus when this function returns.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4258"></A>
+<A NAME="4259"></A>
+<A NAME="fn:ioncore.goto_nth_screen"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WScreen ioncore.goto_nth_screen(integer id)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Switch focus to the screen with id <TT>id</TT> and return it.
+
+<P>
+Note that this function is asynchronous; the screen will not
+ actually have received the focus when this function returns.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4260"></A>
+<A NAME="4261"></A>
+<A NAME="fn:ioncore.goto_prev_screen"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WScreen ioncore.goto_prev_screen()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Switch focus to the previous screen and return it.
+
+<P>
+Note that this function is asynchronous; the screen will not
+ actually have received the focus when this function returns.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4262"></A>
+<A NAME="4263"></A>
+<A NAME="fn:ioncore.goto_previous"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion ioncore.goto_previous()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Go to and return to a previously active region (if any).
+
+<P>
+Note that this function is asynchronous; the region will not
+ actually have received the focus when this function returns.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4264"></A>
+<A NAME="4265"></A>
+<A NAME="fn:ioncore.is_i18n"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool ioncore.is_i18n()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Is Ion supporting locale-specifically multibyte-encoded strings?
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4266"></A>
+<A NAME="4267"></A>
+<A NAME="fn:ioncore.load_module"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool ioncore.load_module(string modname)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to load a C-side module.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4268"></A>
+<A NAME="4269"></A>
+<A NAME="fn:ioncore.lookup_clientwin"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WClientWin ioncore.lookup_clientwin(string name)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to find a client window with name <TT>name</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4270"></A>
+<A NAME="4271"></A>
+<A NAME="fn:ioncore.lookup_region"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion ioncore.lookup_region(string name, string typenam)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to find a non-client window region with name <TT>name</TT> and type
+ inheriting <TT>typenam</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4272"></A>
+<A NAME="4273"></A>
+<A NAME="fn:ioncore.navi_first"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion ioncore.navi_first(WRegion reg, string dirstr, table param)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Find first region within <TT>reg</TT> in direction <TT>dirstr</TT>
+ (up/down/left/right/beg/end/any). For information on <TT>param</TT>,
+ see <A HREF="#fn:ioncore.navi_next"><TT>ioncore.navi_next</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4274"></A>
+<A NAME="4275"></A>
+<A NAME="fn:ioncore.navi_next"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion ioncore.navi_next(WRegion reg, string dirstr, table param)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Find region next from <TT>reg</TT> in direction <TT>dirstr</TT>
+ (up/down/left/right/next/prev/any). The table param may
+ contain the boolean field <TT>nowrap</TT>, instructing not to wrap 
+ around, and the WRegions <TT>no_ascend</TT> and <TT>no_descend</TT>,
+ and functions <TT>ascend_filter</TT> and <TT>descend_filter</TT> from
+ <TT>WRegion</TT>s (<TT>to</TT>, <TT>from</TT>), used to decide when to descend
+ or ascend into another region. (TODO: more detailed explanation.)
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4276"></A>
+<A NAME="4277"></A>
+<A NAME="fn:ioncore.popen_bgread"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>integer ioncore.popen_bgread(string cmd, function h, function errh)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Run <TT>cmd</TT> with a read pipe connected to its stdout.
+ When data is received through the pipe, <TT>handler</TT> is called
+ with that data.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4278"></A>
+<A NAME="4279"></A>
+<A NAME="fn:ioncore.progname"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string ioncore.progname()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the name of program using Ioncore.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4280"></A>
+<A NAME="4281"></A>
+<A NAME="fn:ioncore.region_list"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table ioncore.region_list(string typenam)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Find all non-client window regions inheriting <TT>typenam</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4282"></A>
+<A NAME="4283"></A>
+<A NAME="fn:ioncore.request_selection"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.request_selection(function fn)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Request (string) selection. The function <TT>fn</TT> will be called 
+ with the selection when and if it is received.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4284"></A>
+<A NAME="4285"></A>
+<A NAME="fn:ioncore.resign"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.resign()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Causes the window manager to simply exit without saving
+ state/session.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4286"></A>
+<A NAME="4287"></A>
+<A NAME="fn:ioncore.restart"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.restart()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Restart, saving session first.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4288"></A>
+<A NAME="4289"></A>
+<A NAME="fn:ioncore.restart_other"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.restart_other(string cmd)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to restart another window manager <TT>cmd</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4290"></A>
+<A NAME="4291"></A>
+<A NAME="fn:ioncore.set"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.set(table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set ioncore basic settings. The table <TT>tab</TT> may contain the
+ following fields.
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>opaque_resize</TT></TD>
+<TD ALIGN="LEFT">(boolean) Controls whether interactive move and
+                        resize operations simply draw a rubberband during
+                        the operation (false) or immediately affect the 
+                        object in question at every step (true).</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>warp</TT></TD>
+<TD ALIGN="LEFT">(boolean) Should focusing operations move the 
+                        pointer to the object to be focused?</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>switchto</TT></TD>
+<TD ALIGN="LEFT">(boolean) Should a managing WMPlex switch
+                        to a newly mapped client window?</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>screen_notify</TT></TD>
+<TD ALIGN="LEFT">(boolean) Should notification tooltips be displayed
+                        for hidden workspaces with activity?</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>frame_default_index</TT></TD>
+<TD ALIGN="LEFT">(string) Specifies where to add new regions
+                        on the mutually exclusive list of a frame. One of
+                        ''last'', ''next'' (for after current), ''next-act''
+                        (for after current and anything with activity right
+                        after it).</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>dblclick_delay</TT></TD>
+<TD ALIGN="LEFT">(integer) Delay between clicks of a double click.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>kbresize_delay</TT></TD>
+<TD ALIGN="LEFT">(integer) Delay in milliseconds for ending keyboard
+                         resize mode after inactivity.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>kbresize_t_max</TT></TD>
+<TD ALIGN="LEFT">(integer) Controls keyboard resize acceleration. 
+                         See description below for details.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>kbresize_t_min</TT></TD>
+<TD ALIGN="LEFT">(integer) See below.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>kbresize_step</TT></TD>
+<TD ALIGN="LEFT">(floating point) See below.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>kbresize_maxacc</TT></TD>
+<TD ALIGN="LEFT">(floating point) See below.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>framed_transients</TT></TD>
+<TD ALIGN="LEFT">(boolean) Put transients in nested frames.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>float_placement_method</TT></TD>
+<TD ALIGN="LEFT">(string) How to place floating frames.
+                          One of ''udlr'' (up-down, then left-right), 
+                          ''lrud'' (left-right, then up-down) or ''random''.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>default_ws_params</TT></TD>
+<TD ALIGN="LEFT">(table) Default workspace layout; the 
+                          attach/creation parameters for a WGroup.</TD>
+</TR>
+</TABLE>
+
+<P>
+When a keyboard resize function is called, and at most <TT>kbresize_t_max</TT> 
+ milliseconds has passed from a previous call, acceleration factor is reset 
+ to 1.0. Otherwise, if at least <TT>kbresize_t_min</TT> milliseconds have 
+ passed from the from previous acceleration update or reset the squere root
+ of the acceleration factor is incremented by <TT>kbresize_step</TT>. The 
+ maximum acceleration factor (pixels/call modulo size hints) is given by 
+ <TT>kbresize_maxacc</TT>. The default values are (200, 50, 30, 100).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4292"></A>
+<A NAME="4293"></A>
+<A NAME="fn:ioncore.set_get_winprop_fn"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.set_get_winprop_fn(function fn)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set function used to look up winprops.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4294"></A>
+<A NAME="4295"></A>
+<A NAME="fn:ioncore.set_paths"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool ioncore.set_paths(table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set important directories (sessiondir, searchpath).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4296"></A>
+<A NAME="4297"></A>
+<A NAME="fn:ioncore.set_selection"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.set_selection(string p)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set primary selection and cutbuffer0 to <TT>p</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4298"></A>
+<A NAME="4299"></A>
+<A NAME="fn:ioncore.shutdown"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.shutdown()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>End session saving it first.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4300"></A>
+<A NAME="4301"></A>
+<A NAME="fn:ioncore.snapshot"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.snapshot()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Save session.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4302"></A>
+<A NAME="4303"></A>
+<A NAME="fn:ioncore.tagged_list"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table ioncore.tagged_list()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns a list of tagged regions.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4304"></A>
+<A NAME="4305"></A>
+<A NAME="fn:ioncore.tags_first"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion ioncore.tags_first()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns first tagged object.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4306"></A>
+<A NAME="4307"></A>
+<A NAME="fn:ioncore.version"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string ioncore.version()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns Ioncore version string.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4308"></A>
+<A NAME="4309"></A>
+<A NAME="fn:ioncore.warn"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.warn(string str)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Issue a warning. How the message is displayed depends on the current
+ warning handler.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4310"></A>
+<A NAME="4311"></A>
+<A NAME="fn:ioncore.warn_traced"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.warn_traced(string str)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Similar to <A HREF="#fn:ioncore.warn"><TT>ioncore.warn</TT></A>, but also print Lua stack trace.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4312"></A>
+<A NAME="4313"></A>
+<A NAME="fn:ioncore.x_change_property"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.x_change_property(integer win, integer atom, integer atom_type, integer format, string mode, table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Modify a window property. The <TT>mode</TT> is one of
+ <TT>"replace"</TT>, <TT>"prepend"</TT> or <TT>"append"</TT>, and format
+ is either 8, 16 or 32. Also see <A HREF="#fn:ioncore.x_get_window_property"><TT>ioncore.x_get_window_property</TT></A>
+ and the <TT>XChangeProperty</TT>(3) manual page.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4314"></A>
+<A NAME="4315"></A>
+<A NAME="fn:ioncore.x_delete_property"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.x_delete_property(integer win, integer atom)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Delete a window property.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4316"></A>
+<A NAME="4317"></A>
+<A NAME="fn:ioncore.x_get_atom_name"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string ioncore.x_get_atom_name(integer atom)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get the name of an atom. See <TT>XGetAtomName</TT>(3) manual page for 
+ details.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4318"></A>
+<A NAME="4319"></A>
+<A NAME="fn:ioncore.x_get_text_property"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table ioncore.x_get_text_property(integer win, integer atom)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get a text property for a window (<TT>STRING</TT>, <TT>COMPOUND_TEXT</TT>,
+ or <TT>UTF8_STRING</TT> property converted). The fields in the returned
+ table (starting from 1) are the null-separated parts of the property.
+ See the <TT>XGetTextProperty</TT>(3) manual page for more information.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4320"></A>
+<A NAME="4321"></A>
+<A NAME="fn:ioncore.x_get_window_property"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table ioncore.x_get_window_property(integer win, integer atom, integer atom_type, integer n32expected, bool more)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get a property <TT>atom</TT> of type <TT>atom_type</TT> for window <TT>win</TT>. 
+ The <TT>n32expected</TT> parameter indicates the expected number of 32bit
+ words, and <TT>more</TT> indicates whether all or just this amount of data
+ should be fetched. Each 8, 16 or 32bit element of the property, as
+ deciphered from <TT>atom_type</TT> is a field in the returned table.
+ See <TT>XGetWindowProperty</TT>(3) manual page for more information.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4322"></A>
+<A NAME="4323"></A>
+<A NAME="fn:ioncore.x_intern_atom"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>integer ioncore.x_intern_atom(string name, bool only_if_exists)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Create a new atom. See <TT>XInternAtom</TT>(3) manual page for details.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4324"></A>
+<A NAME="4325"></A>
+<A NAME="fn:ioncore.x_set_text_property"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void ioncore.x_set_text_property(integer win, integer atom, table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set a text property for a window. The fields of <TT>tab</TT> starting from
+ 1 should be the different null-separated parts of the property.
+ See the <TT>XSetTextProperty</TT>(3) manual page for more information.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4326"></A>
+<A NAME="4327"></A>
+<A NAME="fn:ioncore.kpress"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.kpress(keyspec, cmd, guard)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Creates a binding description table for the action of pressing a key given 
+ by <TT>keyspec</TT> (with possible modifiers) to the function <TT>func</TT>.
+ For more information on bindings, see section <A HREF="node4.html#sec:bindings">3.3</A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4328"></A>
+<A NAME="4329"></A>
+<A NAME="fn:ioncore.kpress_wait"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.kpress_wait(keyspec, cmd, guard)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This is similar to <A HREF="#fn:kpress"><TT>kpress</TT></A> but after calling <TT>cmd</TT>, 
+ Ioncore waits for all modifiers to be released before processing
+ any further actions.
+ For more information on bindings, see section <A HREF="node4.html#sec:bindings">3.3</A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4330"></A>
+<A NAME="4331"></A>
+<A NAME="fn:ioncore.match_winprop_name"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.match_winprop_name(prop, cwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>The basic name-based winprop matching criteria.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4332"></A>
+<A NAME="4333"></A>
+<A NAME="fn:ioncore.mclick"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.mclick(buttonspec, cmd, guard)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Creates a binding description table for the action of clicking a mouse 
+ button while possible modifier keys are pressed,
+ both given by <TT>buttonspec</TT>, to the function <TT>func</TT>.
+ For more information, see section <A HREF="node4.html#sec:bindings">3.3</A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4334"></A>
+<A NAME="4335"></A>
+<A NAME="fn:ioncore.mdblclick"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.mdblclick(buttonspec, cmd, guard)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Similar to <A HREF="#fn:mclick"><TT>mclick</TT></A> but for double-click.
+ Also see section <A HREF="node4.html#sec:bindings">3.3</A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4336"></A>
+<A NAME="4337"></A>
+<A NAME="fn:ioncore.mdrag"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.mdrag(buttonspec, cmd, guard)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Creates a binding description table for the action of moving the mouse
+ (or other pointing device) while the button given by <TT>buttonspec</TT>
+ is held pressed and the modifiers given by <TT>buttonspec</TT> were pressed
+ when the button was initially pressed.
+ Also see section <A HREF="node4.html#sec:bindings">3.3</A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4338"></A>
+<A NAME="4339"></A>
+<A NAME="fn:ioncore.menuentry"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.menuentry(name, cmd, guard)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Use this function to define normal menu entries. The string <TT>name</TT> 
+ is the string shown in the visual representation of menu, and the
+ parameter <TT>cmd</TT> and <TT>guard</TT> are similar to those of
+ <A HREF="#fn:ioncore.defbindings"><TT>ioncore.defbindings</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4340"></A>
+<A NAME="4341"></A>
+<A NAME="fn:ioncore.mpress"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.mpress(buttonspec, cmd, guard)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Similar to <A HREF="#fn:mclick"><TT>mclick</TT></A> but for just pressing the mouse button.
+ Also see section <A HREF="node4.html#sec:bindings">3.3</A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4342"></A>
+<A NAME="4343"></A>
+<A NAME="fn:ioncore.refresh_stylelist"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.refresh_stylelist()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Refresh list of known style files.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4344"></A>
+<A NAME="4345"></A>
+<A NAME="fn:ioncore.submap"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.submap(kcb_, list)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns a function that creates a submap binding description table.
+ When the key press action <TT>keyspec</TT> occurs, Ioncore will wait for
+ a further key presse and act according to the submap.
+ For details, see section <A HREF="node4.html#sec:bindings">3.3</A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4346"></A>
+<A NAME="4347"></A>
+<A NAME="fn:ioncore.submenu"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>ioncore.submenu(name, sub_or_name, options)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Use this function to define menu entries for submenus. The parameter
+ <A HREF="#fn:sub_or_name"><TT>sub_or_name</TT></A> is either a table of menu entries or the name
+ of an already defined menu. The initial menu entry to highlight can be
+ specified by <TT>options.initial</TT> as either an integer starting from 1, 
+ or a  function that returns such a number. Another option supported is
+ <TT>options.noautoexpand</TT> that will cause <A HREF="#fn:mod_query.query_menu"><TT>mod_query.query_menu</TT></A>
+ to not automatically expand this submenu.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00711000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> WClientWin functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4349"></A>
+<A NAME="4350"></A>
+<A NAME="fn:WClientWin.get_ident"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WClientWin.get_ident(WClientWin cwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns a table containing the properties <TT>WM_CLASS</TT> (table entries
+ <TT>instance</TT> and <TT>class</TT>) and  <TT>WM_WINDOW_ROLE</TT> (<TT>role</TT>)
+ properties for <TT>cwin</TT>. If a property is not set, the corresponding 
+ field(s) are unset in the  table.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4351"></A>
+<A NAME="4352"></A>
+<A NAME="fn:WClientWin.is_fullscreen"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WClientWin.is_fullscreen(WClientWin cwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Is <TT>cwin</TT> in full screen mode?
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4353"></A>
+<A NAME="4354"></A>
+<A NAME="fn:WClientWin.kill"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WClientWin.kill(WClientWin cwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to kill (with XKillWindow) the client that owns the X
+ window correspoding to <TT>cwin</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4355"></A>
+<A NAME="4356"></A>
+<A NAME="fn:WClientWin.nudge"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WClientWin.nudge(WClientWin cwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempts to fix window size problems with non-ICCCM compliant
+ programs.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4357"></A>
+<A NAME="4358"></A>
+<A NAME="fn:WClientWin.quote_next"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WClientWin.quote_next(WClientWin cwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Send next key press directly to <TT>cwin</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4359"></A>
+<A NAME="4360"></A>
+<A NAME="fn:WClientWin.set_fullscreen"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WClientWin.set_fullscreen(WClientWin cwin, string how)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set client window <TT>cwin</TT> full screen state according to the 
+ parameter <TT>how</TT> (set/unset/toggle). Resulting state is returned,
+ which may not be what was requested.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4361"></A>
+<A NAME="4362"></A>
+<A NAME="fn:WClientWin.xid"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>double WClientWin.xid(WClientWin cwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return the X window id for the client window.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00712000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> WFrame functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4364"></A>
+<A NAME="4365"></A>
+<A NAME="fn:WFrame.is_shaded"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WFrame.is_shaded(WFrame frame)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Is <TT>frame</TT> shaded?
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4366"></A>
+<A NAME="4367"></A>
+<A NAME="fn:WFrame.maximize_horiz"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WFrame.maximize_horiz(WFrame frame)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to toggle horizontal maximisation of <TT>frame</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4368"></A>
+<A NAME="4369"></A>
+<A NAME="fn:WFrame.maximize_vert"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WFrame.maximize_vert(WFrame frame)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to toggle vertical maximisation of <TT>frame</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4370"></A>
+<A NAME="4371"></A>
+<A NAME="fn:WFrame.mode"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string WFrame.mode(WFrame frame)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get frame mode.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4372"></A>
+<A NAME="4373"></A>
+<A NAME="fn:WFrame.p_switch_tab"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WFrame.p_switch_tab(WFrame frame)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Display the region corresponding to the tab that the user pressed on.
+ This function should only be used by binding it to a mouse action.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4374"></A>
+<A NAME="4375"></A>
+<A NAME="fn:WFrame.p_tabdrag"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WFrame.p_tabdrag(WFrame frame)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Start dragging the tab that the user pressed on with the pointing device.
+ This function should only be used by binding it to <SPAN  CLASS="textit">mpress</SPAN> or
+ <SPAN  CLASS="textit">mdrag</SPAN> action with area ''tab''.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4376"></A>
+<A NAME="4377"></A>
+<A NAME="fn:WFrame.set_mode"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WFrame.set_mode(WFrame frame, string modestr)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set frame mode.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4378"></A>
+<A NAME="4379"></A>
+<A NAME="fn:WFrame.set_numbers"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WFrame.set_numbers(WFrame frame, string how)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Control whether tabs show numbers (set/unset/toggle). 
+ Resulting state is returned, which may not be what was 
+ requested.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4380"></A>
+<A NAME="4381"></A>
+<A NAME="fn:WFrame.set_shaded"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WFrame.set_shaded(WFrame frame, string how)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set shading state according to the parameter <TT>how</TT> 
+ (set/unset/toggle). Resulting state is returned, which may not be
+ what was requested.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00713000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> WGroup functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4383"></A>
+<A NAME="4384"></A>
+<A NAME="fn:WGroup.attach"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WGroup.attach(WGroup ws, WRegion reg, table param)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attach and reparent existing region <TT>reg</TT> to <TT>ws</TT>.
+ The table <TT>param</TT> may contain the fields <TT>index</TT> and
+ <TT>switchto</TT> that are interpreted as for <A HREF="#fn:WMPlex.attach_new"><TT>WMPlex.attach_new</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4385"></A>
+<A NAME="4386"></A>
+<A NAME="fn:WGroup.attach_new"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WGroup.attach_new(WGroup ws, table param)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Create a new region to be managed by <TT>ws</TT>. At least the following
+ fields in <TT>param</TT> are understood:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>type</TT></TD>
+<TD ALIGN="LEFT">Class name (a string) of the object to be created. Mandatory.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>name</TT></TD>
+<TD ALIGN="LEFT">Name of the object to be created (a string). Optional.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>switchto</TT></TD>
+<TD ALIGN="LEFT">Should the region be switched to (boolean)? Optional.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>level</TT></TD>
+<TD ALIGN="LEFT">Stacking level; default is 1.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>modal</TT></TD>
+<TD ALIGN="LEFT">Make object modal; ignored if level is set.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>sizepolicy</TT></TD>
+<TD ALIGN="LEFT">Size policy.</TD>
+</TR>
+</TABLE>
+
+<P>
+In addition parameters to the region to be created are passed in this 
+ same table.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4387"></A>
+<A NAME="4388"></A>
+<A NAME="fn:WGroup.bottom"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WGroup.bottom(WGroup ws)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the 'bottom' of <TT>ws</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4389"></A>
+<A NAME="4390"></A>
+<A NAME="fn:WGroup.managed_list"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WGroup.managed_list(WGroup ws)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns a list of regions managed by the workspace (frames, mostly).
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00714000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">4</SPAN> WGroupCW functions</A>
+</H3>
+
+<P>
+
+<H3><A NAME="SECTION00715000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">5</SPAN> WGroupWS functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4393"></A>
+<A NAME="4394"></A>
+<A NAME="fn:WGroupWS.attach_framed"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WGroupWS.attach_framed(WGroupWS ws, WRegion reg, table t)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attach region <TT>reg</TT> on <TT>ws</TT>.
+ At least the following fields in <TT>t</TT> are supported:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>switchto</TT></TD>
+<TD ALIGN="LEFT">Should the region be switched to (boolean)? Optional.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>geom</TT></TD>
+<TD ALIGN="LEFT">Geometry; <TT>x</TT> and <TT>y</TT>, if set, indicates top-left of 
+   the frame to be created while <TT>width</TT> and <TT>height</TT>, if set, indicate
+   the size of the client window within that frame. Optional.</TD>
+</TR>
+</TABLE>
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00716000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">6</SPAN> WInfoWin functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4396"></A>
+<A NAME="4397"></A>
+<A NAME="fn:WInfoWin.set_text"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WInfoWin.set_text(WInfoWin p, string str)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set contents of the info window.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00717000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">7</SPAN> WMPlex functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4399"></A>
+<A NAME="4400"></A>
+<A NAME="fn:WMPlex.attach"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WMPlex.attach(WMPlex mplex, WRegion reg, table param)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attach and reparent existing region <TT>reg</TT> to <TT>mplex</TT>.
+ The table <TT>param</TT> may contain the fields <TT>index</TT> and
+ <TT>switchto</TT> that are interpreted as for <A HREF="#fn:WMPlex.attach_new"><TT>WMPlex.attach_new</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4401"></A>
+<A NAME="4402"></A>
+<A NAME="fn:WMPlex.attach_new"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WMPlex.attach_new(WMPlex mplex, table param)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Create a new region to be managed by <TT>mplex</TT>. At least the following
+ fields in <TT>param</TT> are understood (all but <TT>type</TT> are optional).
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>type</TT></TD>
+<TD ALIGN="LEFT">(string) Class name (a string) of the object to be created.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>name</TT></TD>
+<TD ALIGN="LEFT">(string) Name of the object to be created (a string).</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>switchto</TT></TD>
+<TD ALIGN="LEFT">(boolean) Should the region be switched to (boolean)?</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>unnumbered</TT></TD>
+<TD ALIGN="LEFT">(boolean) Do not put on the numbered mutually 
+                     exclusive list.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>index</TT></TD>
+<TD ALIGN="LEFT">(integer) Index on this list, same as for 
+                <A HREF="#fn:WMPlex.set_index"><TT>WMPlex.set_index</TT></A>.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>level</TT></TD>
+<TD ALIGN="LEFT">(integer) Stacking level.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>modal</TT></TD>
+<TD ALIGN="LEFT">(boolean) Shortcut for modal stacking level.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>hidden</TT></TD>
+<TD ALIGN="LEFT">(boolean) Attach hidden, if not prevented
+                  by e.g. the mutually exclusive list being empty.
+                  This option overrides <TT>switchto</TT>.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>sizepolicy</TT></TD>
+<TD ALIGN="LEFT">(integer) Size policy.
+                     (TODO: document them somewhere.)</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>geom</TT></TD>
+<TD ALIGN="LEFT">(table) Geometry specification.</TD>
+</TR>
+</TABLE>
+
+<P>
+In addition parameters to the region to be created are passed in this 
+ same table.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4403"></A>
+<A NAME="4404"></A>
+<A NAME="fn:WMPlex.attach_tagged"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMPlex.attach_tagged(WMPlex mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attach all tagged regions to <TT>mplex</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4405"></A>
+<A NAME="4406"></A>
+<A NAME="fn:WMPlex.dec_index"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMPlex.dec_index(WMPlex mplex, WRegion r)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Move <TT>r</TT> ''right'' within objects managed by <TT>mplex</TT> on list 1.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4407"></A>
+<A NAME="4408"></A>
+<A NAME="fn:WMPlex.get_index"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>integer WMPlex.get_index(WMPlex mplex, WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get index of <TT>reg</TT> within the multiplexer on list 1. The first region 
+ managed by <TT>mplex</TT> has index zero. If <TT>reg</TT> is not managed by 
+ <TT>mplex</TT>, -1 is returned.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4409"></A>
+<A NAME="4410"></A>
+<A NAME="fn:WMPlex.get_stdisp"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WMPlex.get_stdisp(WMPlex mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get status display information. See <A HREF="#fn:WMPlex.get_stdisp"><TT>WMPlex.get_stdisp</TT></A> for
+ information on the fields.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4411"></A>
+<A NAME="4412"></A>
+<A NAME="fn:WMPlex.inc_index"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMPlex.inc_index(WMPlex mplex, WRegion r)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Move <TT>r</TT> ''right'' within objects managed by <TT>mplex</TT> on list 1.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4413"></A>
+<A NAME="4414"></A>
+<A NAME="fn:WMPlex.is_hidden"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WMPlex.is_hidden(WMPlex mplex, WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Is <TT>reg</TT> on within <TT>mplex</TT> and hidden?
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4415"></A>
+<A NAME="4416"></A>
+<A NAME="fn:WMPlex.managed_list"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WMPlex.managed_list(WMPlex mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns a list of all regions managed by <TT>mplex</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4417"></A>
+<A NAME="4418"></A>
+<A NAME="fn:WMPlex.mx_count"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>integer WMPlex.mx_count(WMPlex mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the number of objects on the mutually exclusive list of <TT>mplex</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4419"></A>
+<A NAME="4420"></A>
+<A NAME="fn:WMPlex.mx_current"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WMPlex.mx_current(WMPlex mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the managed object currently active within the mutually exclusive
+ list of <TT>mplex</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4421"></A>
+<A NAME="4422"></A>
+<A NAME="fn:WMPlex.mx_list"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WMPlex.mx_list(WMPlex mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns a list of regions on the numbered/mutually exclusive list of 
+ <TT>mplex</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4423"></A>
+<A NAME="4424"></A>
+<A NAME="fn:WMPlex.mx_nth"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WMPlex.mx_nth(WMPlex mplex, integer n)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the <TT>n</TT>:th object managed by <TT>mplex</TT> on the
+ <TT>l</TT>:th layer.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4425"></A>
+<A NAME="4426"></A>
+<A NAME="fn:WMPlex.set_hidden"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WMPlex.set_hidden(WMPlex mplex, WRegion reg, string how)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set the visibility of the region <TT>reg</TT> on <TT>mplex</TT>
+ as specified with the parameter <TT>how</TT> (set/unset/toggle).
+ The resulting state is returned.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4427"></A>
+<A NAME="4428"></A>
+<A NAME="fn:WMPlex.set_index"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMPlex.set_index(WMPlex mplex, WRegion reg, integer index)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set index of <TT>reg</TT> within the multiplexer to <TT>index</TT> within 
+ the mutually exclusive list. Special values for <TT>index</TT> are:
+ <TABLE CELLPADDING=3 WIDTH="100%">
+<TR><TD ALIGN="LEFT"><SPAN CLASS="MATH"></SPAN></TD>
+<TD ALIGN="LEFT">After <A HREF="#fn:WMPlex.mx_current"><TT>WMPlex.mx_current</TT></A>.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><SPAN CLASS="MATH"></SPAN></TD>
+<TD ALIGN="LEFT">Last.</TD>
+</TR>
+</TABLE>
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4429"></A>
+<A NAME="4430"></A>
+<A NAME="fn:WMPlex.set_stdisp"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WMPlex.set_stdisp(WMPlex mplex, table t)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set/create status display for <TT>mplex</TT>. Table is a standard
+ description of the object to be created (as passed to e.g. 
+ <A HREF="#fn:WMPlex.attach_new"><TT>WMPlex.attach_new</TT></A>). In addition, the following fields are
+ recognised:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>pos</TT></TD>
+<TD ALIGN="LEFT">The corner of the screen to place the status display
+               in. One of <TT>tl</TT>, <TT>tr</TT>, <TT>bl</TT> or <TT>br</TT>.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>action</TT></TD>
+<TD ALIGN="LEFT">If this field is set to <TT>keep</TT>, <TT>corner</TT>
+                  and <TT>orientation</TT> are changed for the existing
+                  status display. If this field is set to <TT>remove</TT>,
+                  the existing status display is removed. If this
+                  field is not set or is set to <TT>replace</TT>, a 
+                  new status display is created and the old, if any,
+                  removed.</TD>
+</TR>
+</TABLE>
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4431"></A>
+<A NAME="4432"></A>
+<A NAME="fn:WMPlex.switch_next"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMPlex.switch_next(WMPlex mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Have <TT>mplex</TT> display next (wrt. currently selected) object managed 
+ by it.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4433"></A>
+<A NAME="4434"></A>
+<A NAME="fn:WMPlex.switch_nth"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMPlex.switch_nth(WMPlex mplex, integer n)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Have <TT>mplex</TT> display the <TT>n</TT>:th object managed by it.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4435"></A>
+<A NAME="4436"></A>
+<A NAME="fn:WMPlex.switch_prev"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMPlex.switch_prev(WMPlex mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Have <TT>mplex</TT> display previous (wrt. currently selected) object
+ managed by it.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00718000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">8</SPAN> WMoveresMode functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4438"></A>
+<A NAME="4439"></A>
+<A NAME="fn:WMoveresMode.cancel"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMoveresMode.cancel(WMoveresMode mode)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return from move/resize cancelling changes if opaque
+ move/resize has not been enabled.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4440"></A>
+<A NAME="4441"></A>
+<A NAME="fn:WMoveresMode.finish"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMoveresMode.finish(WMoveresMode mode)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return from move/resize mode and apply changes unless opaque
+ move/resize is enabled.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4442"></A>
+<A NAME="4443"></A>
+<A NAME="fn:WMoveresMode.move"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMoveresMode.move(WMoveresMode mode, integer horizmul, integer vertmul)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Move resize mode target one step:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1">
+<TR><TD ALIGN="RIGHT"><TT>horizmul</TT>/<TT>vertmul</TT></TD>
+<TD ALIGN="LEFT">effect</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">-1</TD>
+<TD ALIGN="LEFT">Move left/up</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">0</TD>
+<TD ALIGN="LEFT">No effect</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">1</TD>
+<TD ALIGN="LEFT">Move right/down</TD>
+</TR>
+</TABLE>
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4444"></A>
+<A NAME="4445"></A>
+<A NAME="fn:WMoveresMode.resize"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMoveresMode.resize(WMoveresMode mode, integer left, integer right, integer top, integer bottom)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Shrink or grow resize mode target one step in each direction.
+ Acceptable values for the parameters <TT>left</TT>, <TT>right</TT>, <TT>top</TT>
+ and <TT>bottom</TT> are as follows: -1: shrink along,
+ 0: do not change, 1: grow along corresponding border.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00719000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">9</SPAN> WRegion functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4447"></A>
+<A NAME="4448"></A>
+<A NAME="fn:WRegion.begin_kbresize"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WMoveresMode WRegion.begin_kbresize(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Enter move/resize mode for <TT>reg</TT>. The bindings set with
+ <A HREF="#fn:ioncore.set_bindings"><TT>ioncore.set_bindings</TT></A> for WMoveresMode are used in 
+ this mode. Of the functions exported by the Ion C core, only
+ <A HREF="#fn:WMoveresMode.resize"><TT>WMoveresMode.resize</TT></A>, <A HREF="#fn:WMoveresMode.move"><TT>WMoveresMode.move</TT></A>, 
+ <A HREF="#fn:WMoveresMode.cancel"><TT>WMoveresMode.cancel</TT></A> and <A HREF="#fn:WMoveresMode.end"><TT>WMoveresMode.end</TT></A> are
+ allowed to be called while in this mode.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4449"></A>
+<A NAME="4450"></A>
+<A NAME="fn:WRegion.current"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WRegion.current(WRegion mgr)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return the object, if any, that is considered ''currently active''
+ within the objects managed by <TT>mplex</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4451"></A>
+<A NAME="4452"></A>
+<A NAME="fn:WRegion.geom"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WRegion.geom(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the geometry of <TT>reg</TT> within its parent; a table with fields
+ <TT>x</TT>, <TT>y</TT>, <TT>w</TT> and <TT>h</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4453"></A>
+<A NAME="4454"></A>
+<A NAME="fn:WRegion.goto"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.goto(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to display <TT>reg</TT>, save region activity status and then
+ warp to (or simply set focus to if warping is disabled) <TT>reg</TT>.
+
+<P>
+Note that this function is asynchronous; the region will not
+ actually have received the focus when this function returns.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4455"></A>
+<A NAME="4456"></A>
+<A NAME="fn:WRegion.is_active"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.is_active(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Is <TT>reg</TT> active/does it or one of it's children of focus?
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4457"></A>
+<A NAME="4458"></A>
+<A NAME="fn:WRegion.is_activity"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.is_activity(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Is activity notification set on <TT>reg</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4459"></A>
+<A NAME="4460"></A>
+<A NAME="fn:WRegion.is_mapped"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.is_mapped(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Is <TT>reg</TT> visible/is it and all it's ancestors mapped?
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4461"></A>
+<A NAME="4462"></A>
+<A NAME="fn:WRegion.is_tagged"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.is_tagged(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Is <TT>reg</TT> tagged?
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4463"></A>
+<A NAME="4464"></A>
+<A NAME="fn:WRegion.manager"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WRegion.manager(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the region that manages <TT>reg</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4465"></A>
+<A NAME="4466"></A>
+<A NAME="fn:WRegion.name"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string WRegion.name(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the name for <TT>reg</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4467"></A>
+<A NAME="4468"></A>
+<A NAME="fn:WRegion.parent"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WWindow WRegion.parent(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the parent region of <TT>reg</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4469"></A>
+<A NAME="4470"></A>
+<A NAME="fn:WRegion.rootwin_of"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRootWin WRegion.rootwin_of(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the root window <TT>reg</TT> is on.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4471"></A>
+<A NAME="4472"></A>
+<A NAME="fn:WRegion.rqclose"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.rqclose(WRegion reg, bool relocate)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to close/destroy <TT>reg</TT>. Whether this operation works
+ depends on whether the particular type of region in question has
+ implemented the feature and, in case of client windows, whether
+ the client supports the <TT>WM_DELETE</TT> protocol (see also
+ <A HREF="#fn:WClientWin.kill"><TT>WClientWin.kill</TT></A>). If the operation is likely to succeed,
+ <TT>true</TT> is returned, otherwise <TT>false</TT>. In most cases the
+ region will not have been actually destroyed when this function returns.
+ If <TT>relocate</TT> is not set, and <TT>reg</TT> manages other regions, it
+ will not be closed. Otherwise the managed regions will be attempted
+ to be relocated.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4473"></A>
+<A NAME="4474"></A>
+<A NAME="fn:WRegion.rqclose_propagate"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WRegion.rqclose_propagate(WRegion reg, WRegion maybe_sub)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Recursively attempt to close a region or one of the regions managed by 
+ it. If <TT>sub</TT> is set, it will be used as the managed region, otherwise
+ <A HREF="#fn:WRegion.current"><TT>WRegion.current</TT></A><TT>(reg)</TT>. The object to be closed is
+ returned or NULL if nothing can be closed. Also see notes for
+ <A HREF="#fn:WRegion.rqclose"><TT>WRegion.rqclose</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4475"></A>
+<A NAME="4476"></A>
+<A NAME="fn:WRegion.rqgeom"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WRegion.rqgeom(WRegion reg, table g)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to resize and/or move <TT>reg</TT>. The table <TT>g</TT> is a usual
+ geometry specification (fields <TT>x</TT>, <TT>y</TT>, <TT>w</TT> and <TT>h</TT>),
+ but may contain missing fields, in which case, <TT>reg</TT>'s manager may
+ attempt to leave that attribute unchanged.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4477"></A>
+<A NAME="4478"></A>
+<A NAME="fn:WRegion.rqorder"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.rqorder(WRegion reg, string ord)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Request ordering. Currently supported values for <TT>ord</TT>
+ are 'front' and 'back'.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4479"></A>
+<A NAME="4480"></A>
+<A NAME="fn:WRegion.screen_of"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WScreen WRegion.screen_of(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the screen <TT>reg</TT> is on.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4481"></A>
+<A NAME="4482"></A>
+<A NAME="fn:WRegion.set_activity"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.set_activity(WRegion reg, string how)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set activity flag of <TT>reg</TT>. The <TT>how</TT> parameter most be
+ one of (set/unset/toggle).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4483"></A>
+<A NAME="4484"></A>
+<A NAME="fn:WRegion.set_name"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.set_name(WRegion reg, string p)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set the name of <TT>reg</TT> to <TT>p</TT>. If the name is already in use,
+ an instance number suffix <TT>&lt;n&gt;</TT> will be attempted. If <TT>p</TT> has
+ such a suffix, it will be modified, otherwise such a suffix will be
+ added. Setting <TT>p</TT> to nil will cause current name to be removed.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4485"></A>
+<A NAME="4486"></A>
+<A NAME="fn:WRegion.set_name_exact"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.set_name_exact(WRegion reg, string p)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Similar to <A HREF="#fn:WRegion.set_name"><TT>WRegion.set_name</TT></A> except if the name is already in use,
+ other instance numbers will not be attempted. The string <TT>p</TT> should
+ not contain a <TT>&lt;n&gt;</TT> suffix or this function will fail.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4487"></A>
+<A NAME="4488"></A>
+<A NAME="fn:WRegion.set_tagged"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WRegion.set_tagged(WRegion reg, string how)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Change tagging state of <TT>reg</TT> as defined by <TT>how</TT>
+ (set/unset/toggle). Resulting state is returned.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4489"></A>
+<A NAME="4490"></A>
+<A NAME="fn:WRegion.size_hints"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WRegion.size_hints(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns size hints for <TT>reg</TT>. The returned table always contains the
+ fields <TT>min_?</TT>, <TT>base_?</TT> and sometimes the fields <TT>max_?</TT>,
+ <TT>base_?</TT> and <TT>inc_?</TT>, where <TT>?</TT>=<TT>w</TT>, <TT>h</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION007110000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">10</SPAN> WRootWin functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4492"></A>
+<A NAME="4493"></A>
+<A NAME="fn:WRootWin.current_scr"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WScreen WRootWin.current_scr(WRootWin rootwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns previously active screen on root window <TT>rootwin</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION007111000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">11</SPAN> WScreen functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4495"></A>
+<A NAME="4496"></A>
+<A NAME="fn:WScreen.id"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>integer WScreen.id(WScreen scr)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return the numerical id for screen <TT>scr</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4497"></A>
+<A NAME="4498"></A>
+<A NAME="fn:WScreen.set_managed_offset"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WScreen.set_managed_offset(WScreen scr, table offset)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set offset of objects managed by the screen from actual screen geometry.
+ The table <TT>offset</TT> should contain the entries <TT>x</TT>, <TT>y</TT>, 
+ <TT>w</TT> and <TT>h</TT> indicating offsets of that component of screen 
+ geometry.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION007112000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">12</SPAN> WWindow functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4500"></A>
+<A NAME="4501"></A>
+<A NAME="fn:WWindow.p_move"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WWindow.p_move(WWindow wwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Start moving <TT>wwin</TT> with the mouse or other pointing device.
+ This function should only be used by binding it to <SPAN  CLASS="textit">mpress</SPAN> or
+ <SPAN  CLASS="textit">mdrag</SPAN> action.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4502"></A>
+<A NAME="4503"></A>
+<A NAME="fn:WWindow.p_resize"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WWindow.p_resize(WWindow wwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Start resizing <TT>wwin</TT> with the mouse or other pointing device.
+ This function should only be used by binding it to <SPAN  CLASS="textit">mpress</SPAN> or
+ <SPAN  CLASS="textit">mdrag</SPAN> action.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4504"></A>
+<A NAME="4505"></A>
+<A NAME="fn:WWindow.xid"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>double WWindow.xid(WWindow wwin)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return the X window id for <TT>wwin</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION007113000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">13</SPAN> global functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4507"></A>
+<A NAME="fn:export"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>export(lib, ...)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Export a list of functions from <TT>lib</TT> into global namespace.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION007114000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">14</SPAN> gr functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4509"></A>
+<A NAME="4510"></A>
+<A NAME="fn:gr.read_config"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void gr.read_config()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Read drawing engine configuration file <SPAN  CLASS="textit">draw.lua</SPAN>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4511"></A>
+<A NAME="4512"></A>
+<A NAME="fn:gr.refresh"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void gr.refresh()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Refresh objects' brushes to update them to use newly loaded style.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4513"></A>
+<A NAME="4514"></A>
+<A NAME="fn:gr.select_engine"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool gr.select_engine(string engine)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Future requests for ''brushes'' are to be forwarded to the drawing engine
+ <TT>engine</TT>. If no engine of such name is known, a module with that name
+ is attempted to be loaded. This function is only intended to be called from
+ colour scheme etc. configuration files and can not be used to change the
+ look of existing objects; for that use <A HREF="#fn:gr.read_config"><TT>gr.read_config</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION007115000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">15</SPAN> string functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4516"></A>
+<A NAME="4517"></A>
+<A NAME="fn:string.shell_safe"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string.shell_safe(str)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Make <TT>str</TT> shell-safe.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION007116000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">16</SPAN> table functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="4519"></A>
+<A NAME="4520"></A>
+<A NAME="fn:table.append"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table.append(t1, t2)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Add entries that do not exist in <TT>t1</TT> from <TT>t2</TT> to <TT>t1</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4521"></A>
+<A NAME="4522"></A>
+<A NAME="fn:table.copy"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table.copy(t, deep)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Make copy of <TT>table</TT>. If <TT>deep</TT> is unset, shallow one-level
+ copy is made, otherwise a deep copy is made.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4523"></A>
+<A NAME="4524"></A>
+<A NAME="fn:table.icat"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table.icat(t1, t2)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Insert all positive integer entries from t2 into t1.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4525"></A>
+<A NAME="4526"></A>
+<A NAME="fn:table.join"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table.join(t1, t2)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Create a table containing all entries from <TT>t1</TT> and those from
+ <TT>t2</TT> that are missing from <TT>t1</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="4527"></A>
+<A NAME="4528"></A>
+<A NAME="fn:table.map"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table.map(f, t)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Map all entries of <TT>t</TT> by <TT>f</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+<H2><A NAME="SECTION00720000000000000000"></A>
+<A NAME="sec:tilingref"></A>
+<BR>
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN> Functions defined in <SPAN  CLASS="textit">mod_tiling</SPAN>
+</H2>
+
+  <DL>
+<DD><A NAME="6781"></A>
+<A NAME="6782"></A>
+<A NAME="fn:mod_tiling.detach"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool mod_tiling.detach(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Detach <TT>reg</TT>, i.e. make it managed by its nearest ancestor
+ WGroup, framed if <TT>reg</TT> is not itself WFrame.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6783"></A>
+<A NAME="6784"></A>
+<A NAME="fn:mod_tiling.get"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table mod_tiling.get()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get parameters. For details see <A HREF="#fn:mod_tiling.set"><TT>mod_tiling.set</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6785"></A>
+<A NAME="6786"></A>
+<A NAME="fn:mod_tiling.mkbottom"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool mod_tiling.mkbottom(WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Create a new WTiling 'bottom' for the group of <TT>reg</TT>,
+ consisting of <TT>reg</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6787"></A>
+<A NAME="6788"></A>
+<A NAME="fn:mod_tiling.set"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void mod_tiling.set(table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set parameters. Currently only <TT>raise_delay</TT> (in milliseconds)
+ is supported.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00721000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> WSplit functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="6790"></A>
+<A NAME="6791"></A>
+<A NAME="fn:WSplit.geom"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WSplit.geom(WSplit split)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the area of workspace used by the regions under <TT>split</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6792"></A>
+<A NAME="6793"></A>
+<A NAME="fn:WSplit.parent"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WSplitInner WSplit.parent(WSplit split)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return parent split for <TT>split</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6794"></A>
+<A NAME="6795"></A>
+<A NAME="fn:WSplit.rqgeom"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WSplit.rqgeom(WSplit node, table g)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attempt to resize and/or move the split tree starting at <TT>node</TT>.
+ Behaviour and the <TT>g</TT> parameter are as for <A HREF="#fn:WRegion.rqgeom"><TT>WRegion.rqgeom</TT></A> 
+ operating on <TT>node</TT> (if it were a WRegion).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6796"></A>
+<A NAME="6797"></A>
+<A NAME="fn:WSplit.transpose"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WSplit.transpose(WSplit node)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Transpose contents of <TT>node</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00722000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> WSplitInner functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="6799"></A>
+<A NAME="6800"></A>
+<A NAME="fn:WSplitInner.current"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WSplit WSplitInner.current(WSplitInner node)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the most previously active child node of <TT>split</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00723000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">3</SPAN> WSplitRegion functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="6802"></A>
+<A NAME="6803"></A>
+<A NAME="fn:WSplitRegion.reg"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WSplitRegion.reg(WSplitRegion node)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the region contained in <TT>node</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00724000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">4</SPAN> WSplitSplit functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="6805"></A>
+<A NAME="6806"></A>
+<A NAME="fn:WSplitSplit.br"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WSplit WSplitSplit.br(WSplitSplit split)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the bottom or right child node of <TT>split</TT> depending
+ on the direction of the split.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6807"></A>
+<A NAME="6808"></A>
+<A NAME="fn:WSplitSplit.dir"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string WSplitSplit.dir(WSplitSplit split)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the direction of <TT>split</TT>; either ''vertical'' or
+ ''horizontal''.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6809"></A>
+<A NAME="6810"></A>
+<A NAME="fn:WSplitSplit.flip"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WSplitSplit.flip(WSplitSplit split)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Flip contents of <TT>node</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6811"></A>
+<A NAME="6812"></A>
+<A NAME="fn:WSplitSplit.tl"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WSplit WSplitSplit.tl(WSplitSplit split)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the top or left child node of <TT>split</TT> depending
+ on the direction of the split.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00725000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">5</SPAN> WTiling functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="6814"></A>
+<A NAME="6815"></A>
+<A NAME="fn:WTiling.flip_at"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WTiling.flip_at(WTiling ws, WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Flip <TT>ws</TT> at <TT>reg</TT> or root if nil.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6816"></A>
+<A NAME="6817"></A>
+<A NAME="fn:WTiling.transpose_at"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WTiling.transpose_at(WTiling ws, WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Transpose <TT>ws</TT> at <TT>reg</TT> or root if nil.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6818"></A>
+<A NAME="6819"></A>
+<A NAME="fn:WTiling.farthest"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WTiling.farthest(WTiling ws, string dirstr, bool any)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return the most previously active region on <TT>ws</TT> with no
+ other regions next to it in  direction <TT>dirstr</TT> 
+ (left/right/up/down). If <TT>any</TT> is not set, the status 
+ display is not considered.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6820"></A>
+<A NAME="6821"></A>
+<A NAME="fn:WTiling.managed_list"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WTiling.managed_list(WTiling ws)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns a list of regions managed by the workspace (frames, mostly).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6822"></A>
+<A NAME="6823"></A>
+<A NAME="fn:WTiling.nextto"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WRegion WTiling.nextto(WTiling ws, WRegion reg, string dirstr, bool any)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return the most previously active region next to <TT>reg</TT> in
+ direction <TT>dirstr</TT> (left/right/up/down). The region <TT>reg</TT>
+ must be managed by <TT>ws</TT>. If <TT>any</TT> is not set, the status display
+ is not considered.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6824"></A>
+<A NAME="6825"></A>
+<A NAME="fn:WTiling.node_of"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WSplitRegion WTiling.node_of(WTiling ws, WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>For region <TT>reg</TT> managed by <TT>ws</TT> return the WSplit
+ a leaf of which <TT>reg</TT> is.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6826"></A>
+<A NAME="6827"></A>
+<A NAME="fn:WTiling.set_floating_at"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WTiling.set_floating_at(WTiling ws, WRegion reg, string how, string dirstr)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Toggle floating of the sides of a split containin <TT>reg</TT> as indicated 
+ by the parameters <TT>how</TT> (set/unset/toggle) and <TT>dirstr</TT>
+ (left/right/up/down/any). The new status is returned (and <TT>false</TT>
+ also on error).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6828"></A>
+<A NAME="6829"></A>
+<A NAME="fn:WTiling.set_floating"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WSplitSplit WTiling.set_floating(WTiling ws, WSplitSplit split, string how)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Toggle floating of a split's sides at <TT>split</TT> as indicated by the 
+ parameter <TT>how</TT> (set/unset/toggle). A split of the appropriate is 
+ returned, if there was a change.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6830"></A>
+<A NAME="6831"></A>
+<A NAME="fn:WTiling.split"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WFrame WTiling.split(WTiling ws, WSplit node, string dirstr)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Create a new frame on <TT>ws</TT> above/below/left of/right of
+ <TT>node</TT> as indicated by <TT>dirstr</TT>. If <TT>dirstr</TT> is 
+ prefixed with ''floating:'' a floating split is created.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6832"></A>
+<A NAME="6833"></A>
+<A NAME="fn:WTiling.split_at"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WFrame WTiling.split_at(WTiling ws, WFrame frame, string dirstr, bool attach_current)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Split <TT>frame</TT> creating a new frame to direction <TT>dirstr</TT>
+ (one of ''left'', ''right'', ''top'' or ''bottom'') of <TT>frame</TT>.
+ If <TT>attach_current</TT> is set, the region currently displayed in
+ <TT>frame</TT>, if any, is moved to thenew frame.
+ If <TT>dirstr</TT> is prefixed with ''floating:'' a floating split is
+ created.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6834"></A>
+<A NAME="6835"></A>
+<A NAME="fn:WTiling.split_top"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WFrame WTiling.split_top(WTiling ws, string dirstr)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Same as <A HREF="#fn:WTiling.split"><TT>WTiling.split</TT></A> at the root of the split tree.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6836"></A>
+<A NAME="6837"></A>
+<A NAME="fn:WTiling.split_tree"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WSplit WTiling.split_tree(WTiling ws)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Returns the root of the split tree.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="6838"></A>
+<A NAME="6839"></A>
+<A NAME="fn:WTiling.unsplit_at"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WTiling.unsplit_at(WTiling ws, WFrame frame)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Try to relocate regions managed by <TT>frame</TT> to another frame
+ and, if possible, destroy the frame.
+  
+</DD>
+</DL>
+
+<P>
+
+<H2><A NAME="SECTION00730000000000000000"></A>
+<A NAME="sec:queryref"></A>
+<BR>
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN> Functions defined in <SPAN  CLASS="textit">mod_query</SPAN>
+</H2>
+
+  <DL>
+<DD><A NAME="7832"></A>
+<A NAME="7833"></A>
+<A NAME="fn:mod_query.defcmd"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.defcmd(cmd, fn)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Define a command override for the <A HREF="#fn:mod_query.query_exec"><TT>query_exec</TT></A> query.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7834"></A>
+<A NAME="7835"></A>
+<A NAME="fn:mod_query.get"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table mod_query.get()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get module configuration. For more information see
+ <A HREF="#fn:mod_query.set"><TT>mod_query.set</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7836"></A>
+<A NAME="7837"></A>
+<A NAME="fn:mod_query.history_clear"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void mod_query.history_clear()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Clear line editor history.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7838"></A>
+<A NAME="7839"></A>
+<A NAME="fn:mod_query.history_get"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string mod_query.history_get(integer n)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get entry at index <TT>n</TT> in line editor history, 0 being the latest.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7840"></A>
+<A NAME="7841"></A>
+<A NAME="fn:mod_query.history_push"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool mod_query.history_push(string str)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Push an entry into line editor history.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7842"></A>
+<A NAME="7843"></A>
+<A NAME="fn:mod_query.history_search"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>integer mod_query.history_search(string s, integer from, bool bwd)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Try to find matching history entry. Returns -1 if none was
+ found. The parameter <TT>from</TT> specifies where to start 
+ searching from, and <TT>bwd</TT> causes backward search from
+ that point.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7844"></A>
+<A NAME="7845"></A>
+<A NAME="fn:mod_query.history_table"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table mod_query.history_table()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Return table of history entries.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7846"></A>
+<A NAME="7847"></A>
+<A NAME="fn:mod_query.message"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WMessage mod_query.message(WMPlex mplex, string p)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Display a message in the <TT>mplex</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7848"></A>
+<A NAME="7849"></A>
+<A NAME="fn:mod_query.set"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void mod_query.set(table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set module configuration. The following are supported:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>autoshowcompl</TT></TD>
+<TD ALIGN="LEFT">(boolean) Is auto-show-completions enabled?
+      (default: true).</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>autoshowcompl_delay</TT></TD>
+<TD ALIGN="LEFT">(integer) auto-show-completions delay
+      in milliseconds (default: 250).</TD>
+</TR>
+</TABLE>
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7850"></A>
+<A NAME="7851"></A>
+<A NAME="fn:mod_query.warn"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>WMessage mod_query.warn(WMPlex mplex, string p)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Display an error message box in the multiplexer <TT>mplex</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7852"></A>
+<A NAME="7853"></A>
+<A NAME="fn:mod_query.popen_completions"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.popen_completions(cp, cmd, fn, reshnd)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This function can be used to read completions from an external source.
+ The parameter <TT>cp</TT> is the completion proxy to be used,
+ and the string <TT>cmd</TT> the shell command to be executed. To its stdout, 
+ the command should on the first line write the <TT>common_beg</TT> 
+ parameter of <A HREF="#fn:WComplProxy.set_completions"><TT>WComplProxy.set_completions</TT></A> (which <TT>fn</TT> maybe used
+ to override) and a single actual completion on each of the successive lines.
+ The function <TT>reshnd</TT> may be used to override a result table
+ building routine.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7854"></A>
+<A NAME="7855"></A>
+<A NAME="fn:mod_query.query"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query(mplex, prompt, initvalue, handler, completor,
+                         context)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Low-level query routine. <TT>mplex</TT> is the WMPlex to display
+ the query in, <TT>prompt</TT> the prompt string, and <TT>initvalue</TT>
+ the initial contents of the query box. <TT>handler</TT> is a function
+ that receives (<TT>mplex</TT>, result string) as parameter when the
+ query has been succesfully completed, <TT>completor</TT> the completor
+ routine which receives a (<TT>cp</TT>, <TT>str</TT>, <TT>point</TT>) as parameters.
+ The parameter <TT>str</TT> is the string to be completed and <TT>point</TT>
+ cursor's location within it. Completions should be eventually,
+ possibly asynchronously, set with <A HREF="#fn:WComplProxy.set_completions"><TT>WComplProxy.set_completions</TT></A> 
+ on <TT>cp</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7856"></A>
+<A NAME="7857"></A>
+<A NAME="fn:mod_query.query_attachclient"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_attachclient(mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This query asks for the name of a client window and switches
+ focus to the one entered. It uses the completion function
+ <A HREF="#fn:ioncore.complete_clientwin"><TT>ioncore.complete_clientwin</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7858"></A>
+<A NAME="7859"></A>
+<A NAME="fn:mod_query.query_editfile"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_editfile(mplex, script, prompt)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Asks for a file to be edited. This script uses 
+ <TT>run-mailcap -mode=edit</TT> by default, but you may provide an
+ alternative script to use. The default prompt is "Edit file:" (translated).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7860"></A>
+<A NAME="7861"></A>
+<A NAME="fn:mod_query.query_exec"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_exec(mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This function asks for a command to execute with <SPAN  CLASS="textit">/bin/sh</SPAN>.
+ If the command is prefixed with a colon (':'), the command will
+ be run in an XTerm (or other terminal emulator) using the script
+ <SPAN  CLASS="textit">ion-runinxterm</SPAN>. Two colons ('::') will ask you to press 
+ enter after the command has finished.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7862"></A>
+<A NAME="7863"></A>
+<A NAME="fn:mod_query.query_gotoclient"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_gotoclient(mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This query asks for the name of a client window and attaches
+ it to the frame the query was opened in. It uses the completion
+ function <A HREF="#fn:ioncore.complete_clientwin"><TT>ioncore.complete_clientwin</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7864"></A>
+<A NAME="7865"></A>
+<A NAME="fn:mod_query.query_lua"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_lua(mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This query asks for Lua code to execute. It sets the variable '<TT>_</TT>'
+ in the local environment of the string to point to the mplex where the
+ query was created. It also sets the table <TT>arg</TT> in the local
+ environment to <TT>{_, _:current()}</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7866"></A>
+<A NAME="7867"></A>
+<A NAME="fn:mod_query.query_man"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_man(mplex, prog)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This query asks for a manual page to display. By default it runs the
+ <TT>man</TT> command in an <TT>xterm</TT> using <TT>ion-runinxterm</TT>,
+ but it is possible to pass another program as the <TT>prog</TT> argument.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7868"></A>
+<A NAME="7869"></A>
+<A NAME="fn:mod_query.query_menu"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_menu(mplex, themenu, prompt)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This query can be used to create a query of a defined menu.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7870"></A>
+<A NAME="7871"></A>
+<A NAME="fn:mod_query.query_renameframe"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_renameframe(frame)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This function asks for a name new for the frame where the query
+ was created.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7872"></A>
+<A NAME="7873"></A>
+<A NAME="fn:mod_query.query_renameworkspace"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_renameworkspace(mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This function asks for a name new for the workspace on which the
+ query resides.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7874"></A>
+<A NAME="7875"></A>
+<A NAME="fn:mod_query.query_restart"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_restart(mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This query asks whether the user wants restart Ioncore.
+ If the answer is 'y', 'Y' or 'yes', so will happen.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7876"></A>
+<A NAME="7877"></A>
+<A NAME="fn:mod_query.query_runfile"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_runfile(mplex, script, prompt)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Asks for a file to be viewed. This script uses 
+ <TT>run-mailcap -action=view</TT> by default, but you may provide an
+ alternative script to use. The default prompt is "View file:" (translated).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7878"></A>
+<A NAME="7879"></A>
+<A NAME="fn:mod_query.query_shutdown"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_shutdown(mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This query asks whether the user wants to exit Ion (no session manager)
+ or close the session (running under a session manager that supports such
+ requests). If the answer is 'y', 'Y' or 'yes', so will happen.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7880"></A>
+<A NAME="7881"></A>
+<A NAME="fn:mod_query.query_ssh"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_ssh(mplex, ssh)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This query asks for a host to connect to with SSH. 
+ Hosts to tab-complete are read from <SPAN  CLASS="textit">~/.ssh/known_hosts</SPAN>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7883"></A>
+<A NAME="7884"></A>
+<A NAME="fn:mod_query.query_workspace"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_workspace(mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This query asks for the name of a workspace. If a workspace
+ (an object inheriting WGroupWS) with such a name exists,
+ it will be switched to. Otherwise a new workspace with the
+ entered name will be created and the user will be queried for
+ the type of the workspace.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7885"></A>
+<A NAME="7886"></A>
+<A NAME="fn:mod_query.query_yesno"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.query_yesno(mplex, prompt, handler)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This function query will display a query with prompt <TT>prompt</TT> in
+ <TT>mplex</TT> and if the user answers affirmately, call <TT>handler</TT>
+ with <TT>mplex</TT> as parameter.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7887"></A>
+<A NAME="7888"></A>
+<A NAME="fn:mod_query.show_about_ion"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.show_about_ion(mplex)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Display an "About Ion" message in <TT>mplex</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7889"></A>
+<A NAME="7890"></A>
+<A NAME="fn:mod_query.show_tree"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_query.show_tree(mplex, reg, max_depth)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Show information about a region tree
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00731000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> WComplProxy functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="7892"></A>
+<A NAME="7893"></A>
+<A NAME="fn:WComplProxy.set_completions"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WComplProxy.set_completions(WComplProxy proxy, table compls)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set completion list of the WEdln that <TT>proxy</TT> refers to to
+ <TT>compls</TT>, if it is still waiting for this completion run. The 
+ numerical indexes of <TT>compls</TT> list the found completions. If the
+ entry <TT>common_beg</TT> (<TT>common_end</TT>) exists, it gives an extra 
+ common prefix (suffix) of all found completions.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00732000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> WEdln functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="7895"></A>
+<A NAME="7896"></A>
+<A NAME="fn:WEdln.back"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.back(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Move backward one character.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7897"></A>
+<A NAME="7898"></A>
+<A NAME="fn:WEdln.backspace"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.backspace(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Delete previous character.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7899"></A>
+<A NAME="7900"></A>
+<A NAME="fn:WEdln.bkill_word"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.bkill_word(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Starting from the previous characters, delete possible whitespace and
+ preceding alphanumeric characters until previous non-alphanumeric character.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7901"></A>
+<A NAME="7902"></A>
+<A NAME="fn:WEdln.bol"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.bol(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Go to the beginning of line.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7903"></A>
+<A NAME="7904"></A>
+<A NAME="fn:WEdln.bskip_word"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.bskip_word(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Go to to beginning of current sequence of alphanumeric characters
+ followed by whitespace.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7905"></A>
+<A NAME="7906"></A>
+<A NAME="fn:WEdln.clear_mark"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.clear_mark(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Clear <SPAN  CLASS="textit">mark</SPAN>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7907"></A>
+<A NAME="7908"></A>
+<A NAME="fn:WEdln.complete"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.complete(WEdln wedln, string cycle, string mode)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Call completion handler with the text between the beginning of line and
+ current cursor position, or select next/previous completion from list if in
+ auto-show-completions mode and <TT>cycle</TT> is set to ``next'' or ``prev'',
+ respectively. The <TT>mode</TT> may be ``history'' or ``normal''. If it is 
+ not set, the previous mode is used. Normally next entry is not cycled to
+ despite the setting of <TT>cycle</TT> if mode switch occurs. To override
+ this, use ``next-always'' and ``prev-always'' for <TT>cycle</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7909"></A>
+<A NAME="7910"></A>
+<A NAME="fn:WEdln.contents"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string WEdln.contents(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get line editor contents.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7911"></A>
+<A NAME="7912"></A>
+<A NAME="fn:WEdln.context"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>string WEdln.context(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get history context for <TT>wedln</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7913"></A>
+<A NAME="7914"></A>
+<A NAME="fn:WEdln.copy"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.copy(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Copy text between <SPAN  CLASS="textit">mark</SPAN> and current cursor position to clipboard.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7915"></A>
+<A NAME="7916"></A>
+<A NAME="fn:WEdln.cut"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.cut(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Copy text between <SPAN  CLASS="textit">mark</SPAN> and current cursor position to clipboard
+ and then delete that sequence.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7917"></A>
+<A NAME="7918"></A>
+<A NAME="fn:WEdln.delete"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.delete(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Delete current character.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7919"></A>
+<A NAME="7920"></A>
+<A NAME="fn:WEdln.eol"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.eol(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Go to the end of line.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7921"></A>
+<A NAME="7922"></A>
+<A NAME="fn:WEdln.finish"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.finish(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Close <TT>wedln</TT> and call any handlers.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7923"></A>
+<A NAME="7924"></A>
+<A NAME="fn:WEdln.forward"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.forward(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Move forward one character.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7925"></A>
+<A NAME="7926"></A>
+<A NAME="fn:WEdln.history_next"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.history_next(WEdln wedln, bool match)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Replace line editor contents with next entry in history if one exists.
+ If <TT>match</TT> is <TT>true</TT>, the initial part of the history entry
+ must match the current line from beginning to point.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7927"></A>
+<A NAME="7928"></A>
+<A NAME="fn:WEdln.history_prev"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.history_prev(WEdln wedln, bool match)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Replace line editor contents with previous in history if one exists.
+ If <TT>match</TT> is <TT>true</TT>, the initial part of the history entry
+ must match the current line from beginning to point.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7929"></A>
+<A NAME="7930"></A>
+<A NAME="fn:WEdln.insstr"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.insstr(WEdln wedln, string str)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Input <TT>str</TT> in wedln at current editing point.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7931"></A>
+<A NAME="7932"></A>
+<A NAME="fn:WEdln.is_histcompl"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WEdln.is_histcompl(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get history completion mode.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7933"></A>
+<A NAME="7934"></A>
+<A NAME="fn:WEdln.kill_line"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.kill_line(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Delete the whole line.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7935"></A>
+<A NAME="7936"></A>
+<A NAME="fn:WEdln.kill_to_bol"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.kill_to_bol(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Delete all characters from previous to beginning of line.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7937"></A>
+<A NAME="7938"></A>
+<A NAME="fn:WEdln.kill_to_eol"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.kill_to_eol(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Delete all characters from current to end of line.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7939"></A>
+<A NAME="7940"></A>
+<A NAME="fn:WEdln.kill_word"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.kill_word(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Starting from the current point, delete possible whitespace and
+ following alphanumeric characters until next non-alphanumeric character.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7941"></A>
+<A NAME="7942"></A>
+<A NAME="fn:WEdln.mark"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>integer WEdln.mark(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get current mark (start of selection) for <TT>wedln</TT>.
+ Return value of -1 indicates that there is no mark, and
+ 0 is the beginning of the line.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7943"></A>
+<A NAME="7944"></A>
+<A NAME="fn:WEdln.next_completion"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WEdln.next_completion(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Select next completion.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7945"></A>
+<A NAME="7946"></A>
+<A NAME="fn:WEdln.paste"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.paste(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Request selection from application holding such.
+
+<P>
+Note that this function is asynchronous; the selection will not
+ actually be inserted before Ion receives it. This will be no
+ earlier than Ion return to its main loop.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7947"></A>
+<A NAME="7948"></A>
+<A NAME="fn:WEdln.point"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>integer WEdln.point(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get current editing point. 
+ Beginning of the edited line is point 0.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7949"></A>
+<A NAME="7950"></A>
+<A NAME="fn:WEdln.prev_completion"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WEdln.prev_completion(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Select previous completion.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7951"></A>
+<A NAME="7952"></A>
+<A NAME="fn:WEdln.set_context"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.set_context(WEdln wedln, string context)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set history context for <TT>wedln</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7953"></A>
+<A NAME="7954"></A>
+<A NAME="fn:WEdln.set_mark"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.set_mark(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set <SPAN  CLASS="textit">mark</SPAN> to current cursor position.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7955"></A>
+<A NAME="7956"></A>
+<A NAME="fn:WEdln.skip_word"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.skip_word(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Go to to end of current sequence of whitespace followed by alphanumeric
+ characters..
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7957"></A>
+<A NAME="7958"></A>
+<A NAME="fn:WEdln.transpose_chars"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.transpose_chars(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Transpose characters.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7959"></A>
+<A NAME="7960"></A>
+<A NAME="fn:WEdln.transpose_words"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WEdln.transpose_words(WEdln wedln)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Transpose words.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00733000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> WInput functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="7962"></A>
+<A NAME="7963"></A>
+<A NAME="fn:WInput.cancel"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WInput.cancel(WInput input)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Close input not calling any possible finish handlers.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7964"></A>
+<A NAME="7965"></A>
+<A NAME="fn:WInput.scrolldown"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WInput.scrolldown(WInput input)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Scroll input <TT>input</TT> text contents down.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="7966"></A>
+<A NAME="7967"></A>
+<A NAME="fn:WInput.scrollup"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WInput.scrollup(WInput input)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Scroll input <TT>input</TT> text contents up.
+  
+</DD>
+</DL>
+
+<P>
+
+<H2><A NAME="SECTION00740000000000000000"></A>
+<A NAME="sec:menuref"></A>
+<BR>
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN> Functions defined in <SPAN  CLASS="textit">mod_menu</SPAN>
+</H2>
+
+  <DL>
+<DD><A NAME="8777"></A>
+<A NAME="8778"></A>
+<A NAME="fn:mod_menu.grabmenu"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_menu.grabmenu(mplex, sub, menu_or_name, param)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This function is similar to <A HREF="#fn:mod_menu.menu"><TT>mod_menu.menu</TT></A>, but input
+ is grabbed and the key used to active the menu can be used to
+ cycle through menu entries.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="8779"></A>
+<A NAME="8780"></A>
+<A NAME="fn:mod_menu.menu"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_menu.menu(mplex, sub, menu_or_name, param)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Display a menu in the lower-left corner of <TT>mplex</TT>.
+ The variable <TT>menu_or_name</TT> is either the name of a menu
+ defined with <A HREF="#fn:mod_menu.defmenu"><TT>mod_menu.defmenu</TT></A> or directly a table similar
+ to ones passesd to this function. When this function is
+ called from a binding handler, <TT>sub</TT> should be set to
+ the second argument of to the binding handler (<TT>_sub</TT>)
+ so that the menu handler will get the same parameters as the
+ binding handler. Extra options can be passed in the table
+ <TT>param</TT>. The initial entry can be specified as the field
+ <TT>initial</TT> as an integer starting from 1. Menus can be made
+ to use a bigger style by setting the field <TT>big</TT> to <TT>true</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="8781"></A>
+<A NAME="8782"></A>
+<A NAME="fn:mod_menu.get"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table mod_menu.get()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get module basic settings. For details, see <A HREF="#fn:mod_menu.set"><TT>mod_menu.set</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="8783"></A>
+<A NAME="8784"></A>
+<A NAME="fn:mod_menu.set"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void mod_menu.set(table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Set module basic settings. The parameter table may contain the
+ following fields:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>scroll_amount</TT></TD>
+<TD ALIGN="LEFT">Number of pixels to scroll at a time 
+                        pointer-controlled menus when one extends
+                        beyond a border of the screen and the pointer
+                        touches that border.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>scroll_delay</TT></TD>
+<TD ALIGN="LEFT">Time between such scrolling events in 
+                        milliseconds.</TD>
+</TR>
+</TABLE>
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="8785"></A>
+<A NAME="8786"></A>
+<A NAME="fn:mod_menu.pmenu"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>mod_menu.pmenu(win, sub, menu_or_name)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This function displays a drop-down menu and should only
+ be called from a mouse press handler. The parameters are
+ similar to those of <A HREF="#fn:mod_menu.menu"><TT>mod_menu.menu</TT></A>.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00741000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> WMenu functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="8788"></A>
+<A NAME="8789"></A>
+<A NAME="fn:WMenu.cancel"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMenu.cancel(WMenu menu)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Close <TT>menu</TT> not calling any possible finish handlers.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="8790"></A>
+<A NAME="8791"></A>
+<A NAME="fn:WMenu.finish"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMenu.finish(WMenu menu)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>If selected entry is a submenu, display that.
+ Otherwise destroy the menu and call handler for selected entry.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="8792"></A>
+<A NAME="8793"></A>
+<A NAME="fn:WMenu.select_next"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMenu.select_next(WMenu menu)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Select next entry in menu.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="8794"></A>
+<A NAME="8795"></A>
+<A NAME="fn:WMenu.select_nth"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMenu.select_nth(WMenu menu, integer n)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Select <TT>n</TT>:th entry in menu.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="8796"></A>
+<A NAME="8797"></A>
+<A NAME="fn:WMenu.select_prev"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMenu.select_prev(WMenu menu)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Select previous entry in menu.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="8798"></A>
+<A NAME="8799"></A>
+<A NAME="fn:WMenu.typeahead_clear"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WMenu.typeahead_clear(WMenu menu)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Clear typeahead buffer.
+  
+</DD>
+</DL>
+
+<P>
+
+<H2><A NAME="SECTION00750000000000000000"></A>
+<A NAME="sec:dockref"></A>
+<BR>
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN> Functions defined in <SPAN  CLASS="textit">mod_dock</SPAN>
+</H2>
+
+  <DL>
+<DD><A NAME="8997"></A>
+<A NAME="8998"></A>
+<A NAME="fn:mod_dock.set_floating_shown_on"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void mod_dock.set_floating_shown_on(WMPlex mplex, string how)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Toggle floating docks on <TT>mplex</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+<H3><A NAME="SECTION00751000000000000000">
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> WDock functions</A>
+</H3>
+
+<P>
+
+  <DL>
+<DD><A NAME="9000"></A>
+<A NAME="9001"></A>
+<A NAME="fn:WDock.attach"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool WDock.attach(WDock dock, WRegion reg)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Attach <TT>reg</TT> to <TT>dock</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9002"></A>
+<A NAME="9003"></A>
+<A NAME="fn:WDock.get"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table WDock.get(WDock dock)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Get <TT>dock</TT>'s configuration table. See <A HREF="#fn:WDock.set"><TT>WDock.set</TT></A> for a
+ description of the table.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9004"></A>
+<A NAME="9005"></A>
+<A NAME="fn:WDock.resize"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WDock.resize(WDock dock)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Resizes and refreshes <TT>dock</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9006"></A>
+<A NAME="9007"></A>
+<A NAME="fn:WDock.set"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void WDock.set(WDock dock, table conftab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Configure <TT>dock</TT>. <TT>conftab</TT> is a table of key/value pairs:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Key</TD>
+<TD ALIGN="LEFT">Values</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>name</TT></TD>
+<TD ALIGN="LEFT">string</TD>
+<TD ALIGN="LEFT">Name of dock</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>pos</TT></TD>
+<TD ALIGN="LEFT">string in <!-- MATH
+ $\{t,m,b\}\times\{t,c,b\}$
+ -->
+<SPAN CLASS="MATH"></SPAN></TD>
+<TD ALIGN="LEFT">Dock position. 
+       Can only be used in floating mode.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>grow</TT></TD>
+<TD ALIGN="LEFT">up/down/left/right</TD>
+<TD ALIGN="LEFT">Growth direction where new dockapps are added. Also
+       sets orientation for dock when working as WMPlex status
+       display (see <A HREF="#fn:WMPlex.set_stdisp"><TT>WMPlex.set_stdisp</TT></A>).</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>is_auto</TT></TD>
+<TD ALIGN="LEFT">bool</TD>
+<TD ALIGN="LEFT">Should <TT>dock</TT> automatically manage new dockapps?</TD>
+</TR>
+</TABLE>
+
+<P>
+Any parameters not explicitly set in <TT>conftab</TT> will be left unchanged.
+  
+</DD>
+</DL>
+
+<P>
+
+<H2><A NAME="SECTION00760000000000000000"></A>
+<A NAME="sec:spref"></A>
+<BR>
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">6</SPAN> Functions defined in <SPAN  CLASS="textit">mod_sp</SPAN>
+</H2>
+
+  <DL>
+<DD><A NAME="9106"></A>
+<A NAME="9107"></A>
+<A NAME="fn:mod_sp.set_shown"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool mod_sp.set_shown(WFrame sp, string how)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Toggle displayed status of <TT>sp</TT>.
+ The parameter <TT>how</TT> is one of (set/unset/toggle).
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9108"></A>
+<A NAME="9109"></A>
+<A NAME="fn:mod_sp.set_shown_on"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool mod_sp.set_shown_on(WMPlex mplex, string how)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Change displayed status of some scratchpad on <TT>mplex</TT> if one is 
+ found. The parameter <TT>how</TT> is one of (set/unset/toggle).
+  
+</DD>
+</DL>
+
+<P>
+
+<H2><A NAME="SECTION00770000000000000000"></A>
+<A NAME="sec:deref"></A>
+<BR>
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">7</SPAN> Functions defined in <SPAN  CLASS="textit">de</SPAN>
+</H2>
+
+  <DL>
+<DD><A NAME="9173"></A>
+<A NAME="9174"></A>
+<A NAME="fn:de.defstyle"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool de.defstyle(string name, table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Define a style.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9175"></A>
+<A NAME="9176"></A>
+<A NAME="fn:de.defstyle_rootwin"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>bool de.defstyle_rootwin(WRootWin rootwin, string name, table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Define a style for the root window <TT>rootwin</TT>.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9177"></A>
+<A NAME="9178"></A>
+<A NAME="fn:de.reset"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>void de.reset()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Clear all styles from drawing engine memory.
+  
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9179"></A>
+<A NAME="9180"></A>
+<A NAME="fn:de.substyle"></A>  
+</DD>
+<DT><STRONG>Synopsis:</STRONG></DT>
+<DD><TT>table de.substyle(string pattern, table tab)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Define a substyle.
+  
+</DD>
+</DL>
+
+<P>
+
+<H2><A NAME="SECTION00780000000000000000"></A>
+<A NAME="sec:hookref"></A>
+<BR>
+<SPAN CLASS="arabic">6</SPAN>.<SPAN CLASS="arabic">8</SPAN> Hooks
+</H2>
+
+<P>
+
+  <DL>
+<DD><A NAME="9401"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="clientwin_do_manage_alt"></A><TT>clientwin_do_manage_alt</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>(WClientWin, table)</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called when we want to manage a new client window.
+      The table argument contains the following fields:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Type</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>switchto</TT></TD>
+<TD ALIGN="LEFT">bool</TD>
+<TD ALIGN="LEFT">Do we want to switch to the client window.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>jumpto</TT></TD>
+<TD ALIGN="LEFT">bool</TD>
+<TD ALIGN="LEFT">Do we want to jump to the client window.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>userpos</TT></TD>
+<TD ALIGN="LEFT">bool</TD>
+<TD ALIGN="LEFT">Geometry set by user.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>dockapp</TT></TD>
+<TD ALIGN="LEFT">bool</TD>
+<TD ALIGN="LEFT">Client window is a dockapp.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>maprq</TT></TD>
+<TD ALIGN="LEFT">bool</TD>
+<TD ALIGN="LEFT">Map request (and not initialisation scan).</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>gravity</TT></TD>
+<TD ALIGN="LEFT">number</TD>
+<TD ALIGN="LEFT">Window gravity.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>geom</TT></TD>
+<TD ALIGN="LEFT">table</TD>
+<TD ALIGN="LEFT">Requested geometry; <TT>x</TT>, <TT>y</TT>, <TT>w</TT>, <TT>h</TT>.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tfor</TT></TD>
+<TD ALIGN="LEFT">WClientWin</TD>
+<TD ALIGN="LEFT">Transient for window.</TD>
+</TR>
+</TABLE>
+
+<P>
+This hook is not called in protected mode and can be used for
+      arbitrary placement policies (deciding in which workspace a new
+      WClientWin should go). In this case, you can call
+<PRE>
+reg:attach(cwin)
+</PRE>
+      where <TT>reg</TT> is the region where the window should go, and
+      <TT>cwin</TT> is the first argument of the function added to the
+      hook.
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9402"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="clientwin_mapped_hook"></A><TT>clientwin_mapped_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>WClientWin</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called when we have started to manage a client window.
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9403"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="clientwin_unmapped_hook"></A><TT>clientwin_unmapped_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>number</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called when we no longer manage a client window. The parameter
+      is the X ID of the window; see <A HREF="#fn:WClientWin.xid"><TT>WClientWin.xid</TT></A>.
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9404"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="frame_managed_changed_hook"></A><TT>frame_managed_changed_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>table</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called when there are changes in the objects managed by a frame
+      or their order. The table parameter has the following fields:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Type</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>reg</TT></TD>
+<TD ALIGN="LEFT">WFrame</TD>
+<TD ALIGN="LEFT">The frame in question</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>mode</TT></TD>
+<TD ALIGN="LEFT">string</TD>
+<TD ALIGN="LEFT"><TT>"switchonly"</TT>, <TT>"reorder"</TT>,
+                                <TT>"add"</TT> or <TT>"remove"</TT></TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>sw</TT></TD>
+<TD ALIGN="LEFT">bool</TD>
+<TD ALIGN="LEFT">Switch occured</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>sub</TT></TD>
+<TD ALIGN="LEFT">WRegion</TD>
+<TD ALIGN="LEFT">The managed region (primarily) affected</TD>
+</TR>
+</TABLE>
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9405"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="ioncore_sigchld_hook"></A><TT>ioncore_sigchld_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>integer</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called when a child process has exited. The parameter
+      is the PID of the process.
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9406"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="ioncore_deinit_hook"></A><TT>ioncore_deinit_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called when Ion is deinitialising and about to quit.
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9407"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="ioncore_post_layout_setup_hook"></A><TT>ioncore_post_layout_setup_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called when Ion has done all initialisation and is almost ready to
+      enter the mainloop, except no windows are yet being managed.
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9408"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="ioncore_snapshot_hook"></A><TT>ioncore_snapshot_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>()</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called to signal scripts and modules to save their state (if any).
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9409"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="tiling_placement_alt"></A><TT>tiling_placement_alt</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>table</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called when a client window is about to be managed by a WTiling
+      to allow for alternative placement policies. The table has the
+      following fields:
+      <TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Type</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tiling</TT></TD>
+<TD ALIGN="LEFT">WTiling</TD>
+<TD ALIGN="LEFT">The tiling</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>reg</TT></TD>
+<TD ALIGN="LEFT">WRegion</TD>
+<TD ALIGN="LEFT">The region (always a WClientWin at 
+              the moment) to be placed</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>mp</TT></TD>
+<TD ALIGN="LEFT">table</TD>
+<TD ALIGN="LEFT">This table contains the same fields as
+            the parameter of <A HREF="#fn:clientwin_do_manage_alt"><TT>clientwin_do_manage_alt</TT></A></TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>res_frame</TT></TD>
+<TD ALIGN="LEFT">WFrame</TD>
+<TD ALIGN="LEFT">A succesfull handler should 
+            return the target frame here.</TD>
+</TR>
+</TABLE>
+      This hook is just for placing within a given workspace after the
+      workspace has been decided by the default workspace selection
+      policy. It is called in protected mode. For arbitrary placement
+      policies, <A HREF="#fn:clientwin_do_manage_alt"><TT>clientwin_do_manage_alt</TT></A> should be used; it
+      isn't called in protected mode,
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9410"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="panews_make_placement_alt"></A><TT>panews_make_placement_alt</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>table</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called to make a placement on panews. The parameter table has
+      the following fields:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Type</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>ws</TT></TD>
+<TD ALIGN="LEFT">WPaneWS</TD>
+<TD ALIGN="LEFT">The workspace</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>frame</TT></TD>
+<TD ALIGN="LEFT">WFrame</TD>
+<TD ALIGN="LEFT">A frame initially allocated for the
+              region to be placed</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>reg</TT></TD>
+<TD ALIGN="LEFT">WRegion</TD>
+<TD ALIGN="LEFT">The region to be placed</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>specifier</TT></TD>
+<TD ALIGN="LEFT">WRegion</TD>
+<TD ALIGN="LEFT">For drag&amp;drop on handling empty areas</TD>
+</TR>
+</TABLE>
+
+<P>
+The handler should set some of these fields on success:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Field</TD>
+<TD ALIGN="LEFT">Type</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>res_node</TT></TD>
+<TD ALIGN="LEFT">WSplit</TD>
+<TD ALIGN="LEFT">Target split</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>res_config</TT></TD>
+<TD ALIGN="LEFT">WFrame</TD>
+<TD ALIGN="LEFT">New configuration for it, unless
+              WSplitRegion</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>res_w</TT></TD>
+<TD ALIGN="LEFT">integer</TD>
+<TD ALIGN="LEFT">New width for target split (optional)</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>res_h</TT></TD>
+<TD ALIGN="LEFT">integer</TD>
+<TD ALIGN="LEFT">New height for target split (optional)</TD>
+</TR>
+</TABLE>
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9411"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="region_activated_hook"></A><TT>region_activated_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>WRegion</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Signalled when a region or one of its children has received the focus.
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9412"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="region_activity_hook"></A><TT>region_activity_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>WRegion</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This hook is triggered when the activity flag of the parameter 
+      region has been changed.
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9413"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="region_do_warp_alt"></A><TT>region_do_warp_alt</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>WRegion</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>This alt-hook exist to allow for alternative pointer warping
+      implementations.
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9414"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="region_inactivated_hook"></A><TT>region_inactivated_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>WRegion</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Signalled when the focus has moved from the parameter region or
+      one of its children to a non-child region of the parameter region.
+      
+</DD>
+</DL>
+
+<P>
+
+  <DL>
+<DD><A NAME="9415"></A>
+
+</DD>
+<DT><STRONG>Hook name:</STRONG></DT>
+<DD><A NAME="screen_managed_changed_hook"></A><TT>screen_managed_changed_hook</TT>
+
+</DD>
+<DT><STRONG>Parameters:</STRONG></DT>
+<DD><TT>table</TT>
+
+</DD>
+<DT><STRONG>Description:</STRONG></DT>
+<DD>Called when there are changes in the objects managed by a screen
+      or their order. The table parameter is similar to that of
+      <A HREF="#fn:frame_managed_changed_hook"><TT>frame_managed_changed_hook</TT></A>.
+      
+</DD>
+</DL>
+
+<P>
+
+<P>
+
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html348"
+  HREF="node8.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html342"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html336"
+  HREF="node6.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html344"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html346"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html349"
+  HREF="node8.html">A. The GNU General</A>
+<B> Up:</B> <A NAME="tex2html343"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html337"
+  HREF="node6.html">5. Scripting</A>
+ &nbsp; <B>  <A NAME="tex2html345"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html347"
+  HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node8.html b/doc/ionconf/node8.html
new file mode 100644 (file)
index 0000000..7b79bc0
--- /dev/null
@@ -0,0 +1,597 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>A. The GNU General Public License</TITLE>
+<META NAME="description" CONTENT="A. The GNU General Public License">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node9.html">
+<LINK REL="previous" HREF="node7.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node9.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html396"
+  HREF="node9.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html390"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html384"
+  HREF="node7.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html392"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html394"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html397"
+  HREF="node9.html">B. Full class hierarchy</A>
+<B> Up:</B> <A NAME="tex2html391"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html385"
+  HREF="node7.html">6. Function reference</A>
+ &nbsp; <B>  <A NAME="tex2html393"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html395"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html398"
+  HREF="node8.html#SECTION00810000000000000000">Appendix: How to Apply These Terms to Your New Programs</A>
+</UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00800000000000000000">
+A. The GNU General Public License</A>
+</H1>
+
+<P>
+<DIV ALIGN="CENTER">
+</DIV>
+<P>
+<DIV ALIGN="CENTER">Version 2, June 1991
+</DIV>
+<P>
+<DIV ALIGN="CENTER">Copyright &#169; 1989, 1991 Free Software Foundation, Inc.
+</DIV>
+<P>
+<DIV ALIGN="CENTER"></DIV>
+<P><P>
+<BR>
+<DIV ALIGN="CENTER"></DIV>
+<P>
+<DIV ALIGN="CENTER">59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+</DIV>
+<P>
+<DIV ALIGN="CENTER"></DIV>
+<P><P>
+<BR>
+<DIV ALIGN="CENTER"></DIV>
+<P>
+<DIV ALIGN="CENTER">Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+
+</DIV>
+
+<P>
+<DIV ALIGN="CENTER">
+<B><BIG CLASS="LARGE">Preamble</BIG></B>
+
+</DIV>
+
+<P>
+The licenses for most software are designed to take away your freedom to
+share and change it.  By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users.  This General Public
+License applies to most of the Free Software Foundation's software and to
+any other program whose authors commit to using it.  (Some other Free
+Software Foundation software is covered by the GNU Library General Public
+License instead.)  You can apply it to your programs, too.
+
+<P>
+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.
+
+<P>
+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 software, or if you modify it.
+
+<P>
+For example, if you distribute copies of such a program, whether gratis or
+for a fee, you must give the recipients all the rights that you have.  You
+must make sure that they, too, receive or can get the source code.  And
+you must show them these terms so they know their rights.
+
+<P>
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+<P>
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software.  If
+the software is modified by someone else and passed on, we want its
+recipients to know that what they have is not the original, so that any
+problems introduced by others will not reflect on the original authors'
+reputations.
+
+<P>
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary.  To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+<P>
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+<P>
+<DIV ALIGN="CENTER">
+<BIG CLASS="XLARGE">T<SMALL>ERMS AND </SMALL>C<SMALL>ONDITIONS </SMALL>F<SMALL>OR </SMALL>C<SMALL>OPYING, </SMALL>D<SMALL>ISTRIBUTION AND
+  </SMALL>M<SMALL>ODIFICATION</SMALL></BIG>
+
+</DIV>
+
+<P>
+
+<OL>
+<LI><P>
+This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the
+terms of this General Public License.  The ``Program'', below, refers to
+any such program or work, and a ``work based on the Program'' means either
+the Program or any derivative work under copyright law: that is to say, a
+work containing the Program or a portion of it, either verbatim or with
+modifications and/or translated into another language.  (Hereinafter,
+translation is included without limitation in the term ``modification''.)
+Each licensee is addressed as ``you''.
+
+<P>
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+<P>
+</LI>
+<LI>You may copy and distribute verbatim copies of the Program's 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 give any other
+  recipients of the Program a copy of this License along with the Program.
+
+<P>
+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.
+
+<P>
+</LI>
+<LI><P>
+You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+<P>
+
+<OL>
+<LI><P>
+You must cause the modified files to carry prominent notices stating that
+you changed the files and the date of any change.
+
+<P>
+</LI>
+<LI><P>
+You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+<P>
+</LI>
+<LI>If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License.  (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+<P>
+</LI>
+</OL>
+
+<P>
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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.
+
+<P>
+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 Program.
+
+<P>
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+<P>
+</LI>
+<LI>You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+<P>
+
+<OL>
+<LI><P>
+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; or,
+
+<P>
+</LI>
+<LI><P>
+Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+<P>
+</LI>
+<LI><P>
+Accompany it with the information you received as to the offer
+to distribute corresponding source code.  (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+<P>
+</LI>
+</OL>
+
+<P>
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, 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 executable.  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.
+
+<P>
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+<P>
+</LI>
+<LI>You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+<P>
+</LI>
+<LI>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 Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+<P>
+</LI>
+<LI>Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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.
+
+<P>
+</LI>
+<LI>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 Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+<P>
+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.
+
+<P>
+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.
+
+<P>
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+<P>
+</LI>
+<LI>If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+<P>
+</LI>
+<LI>The Free Software Foundation may publish revised and/or new versions
+of the 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.
+
+<P>
+Each version is given a distinguishing version number.  If the Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+<P>
+</LI>
+<LI>If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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.
+
+<P>
+<DIV ALIGN="CENTER">
+<BIG CLASS="XLARGE">
+N<SMALL>O </SMALL>W<SMALL>ARRANTY
+</SMALL></BIG>
+
+</DIV>
+
+<P>
+</LI>
+<LI>B<SMALL>ECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  </SMALL>E<SMALL>XCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM ``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.  </SMALL>T<SMALL>HE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  </SMALL>S<SMALL>HOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.</SMALL>
+
+<P>
+</LI>
+<LI>I<SMALL>N 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.</SMALL>
+
+<P>
+</LI>
+</OL>
+
+<P>
+<DIV ALIGN="CENTER">
+<BIG CLASS="XLARGE">E<SMALL>ND OF </SMALL>T<SMALL>ERMS AND </SMALL>C<SMALL>ONDITIONS</SMALL></BIG>
+
+</DIV>
+
+<P>
+
+<P>
+
+<H2><A NAME="SECTION00810000000000000000">
+Appendix: How to Apply These Terms to Your New Programs</A>
+</H2>
+
+<P>
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+<P>
+To do so, attach the following notices to the program.  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.
+
+<P>
+<BLOCKQUOTE>
+one line to give the program's name and a brief idea of what it does. 
+<BR>
+Copyright (C) yyyy  name of author 
+<BR></BLOCKQUOTE>
+<P>
+<BLOCKQUOTE>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+</BLOCKQUOTE>
+<P>
+<BLOCKQUOTE>This program 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 General Public License for more details.
+</BLOCKQUOTE>
+<P>
+<BLOCKQUOTE>You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+</BLOCKQUOTE>
+
+<P>
+Also add information on how to contact you by electronic and paper mail.
+
+<P>
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+<P>
+<BLOCKQUOTE>
+Gnomovision version 69, Copyright (C) yyyy  name of author 
+<BR>
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 
+<BR>
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+</BLOCKQUOTE>
+
+<P>
+The hypothetical commands <TT>show w</TT> and <TT>show c</TT> should show the
+appropriate parts of the General Public License.  Of course, the commands
+you use may be called something other than <TT>show w</TT> and <TT>show c</TT>;
+they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+<P>
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a ``copyright disclaimer'' for the program, if
+necessary.  Here is a sample; alter the names:
+
+<P>
+<BLOCKQUOTE>
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program 
+<BR>`Gnomovision' (which makes passes at compilers) written by James Hacker. 
+<BR></BLOCKQUOTE>
+<P>
+<BLOCKQUOTE>signature of Ty Coon, 1 April 1989 
+<BR>
+Ty Coon, President of Vice
+
+</BLOCKQUOTE>
+
+<P>
+This General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications
+with the library.  If this is what you want to do, use the GNU Library
+General Public License instead of this License.
+
+<P>
+
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html396"
+  HREF="node9.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html390"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html384"
+  HREF="node7.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html392"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html394"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html397"
+  HREF="node9.html">B. Full class hierarchy</A>
+<B> Up:</B> <A NAME="tex2html391"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html385"
+  HREF="node7.html">6. Function reference</A>
+ &nbsp; <B>  <A NAME="tex2html393"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html395"
+  HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/node9.html b/doc/ionconf/node9.html
new file mode 100644 (file)
index 0000000..69a5aff
--- /dev/null
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>B. Full class hierarchy visible to Lua-side</TITLE>
+<META NAME="description" CONTENT="B. Full class hierarchy visible to Lua-side">
+<META NAME="keywords" CONTENT="ionconf">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionconf.css">
+
+<LINK REL="next" HREF="node10.html">
+<LINK REL="previous" HREF="node8.html">
+<LINK REL="up" HREF="ionconf.html">
+<LINK REL="next" HREF="node10.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html411"
+  HREF="node10.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html405"
+  HREF="ionconf.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html399"
+  HREF="node8.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html407"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html409"
+  HREF="node11.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html412"
+  HREF="node10.html">List of functions</A>
+<B> Up:</B> <A NAME="tex2html406"
+  HREF="ionconf.html">Configuring and extending Ion3</A>
+<B> Previous:</B> <A NAME="tex2html400"
+  HREF="node8.html">A. The GNU General</A>
+ &nbsp; <B>  <A NAME="tex2html408"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html410"
+  HREF="node11.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+
+<H1><A NAME="SECTION00900000000000000000"></A>
+<A NAME="app:fullhierarchy"></A>
+<BR>
+B. Full class hierarchy visible to Lua-side
+</H1>
+
+<P>
+<PRE>
+    Obj
+     |--&gt;WHook
+     |--&gt;WTimer
+     |--&gt;WMoveresMode
+     |--&gt;WMgmtMode (mod_mgmtmode)
+     |--&gt;WRegion
+     |    |--&gt;WClientWin
+     |    |--&gt;WWindow
+     |    |    |--&gt;WRootWin
+     |    |    |--&gt;WMPlex
+     |    |    |    |--&gt;WScreen
+     |    |    |    |--&gt;WFrame
+     |    |    |--&gt;WInfoWin
+     |    |    |    |--&gt;WStatusBar (mod_statusbar)
+     |    |    |--&gt;WMenu (mod_menu)
+     |    |    |--&gt;WInput (mod_query)
+     |    |         |--&gt;WEdln (mod_query)
+     |    |         |--&gt;WMessage (mod_query)
+     |    |--&gt;WGroup
+     |    |    |--&gt;WGroupWS
+     |    |    |--&gt;WGroupCW
+     |    |--&gt;WTiling (mod_tiling)
+     |--&gt;WSplit (mod_tiling)
+          |--&gt;WSplitInner (mod_tiling)
+          |    |--&gt;WSplitSplit (mod_tiling)
+          |         |--&gt;WSplitFloat (mod_tiling)
+          |--&gt;WSplitRegion (mod_tiling)
+               |--&gt;WSplitST (mod_tiling)
+</PRE>
+
+<P>
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionconf/prev.png b/doc/ionconf/prev.png
new file mode 100644 (file)
index 0000000..e60b8b4
Binary files /dev/null and b/doc/ionconf/prev.png differ
diff --git a/doc/ionconf/prev_g.png b/doc/ionconf/prev_g.png
new file mode 100644 (file)
index 0000000..476d956
Binary files /dev/null and b/doc/ionconf/prev_g.png differ
diff --git a/doc/ionconf/up.png b/doc/ionconf/up.png
new file mode 100644 (file)
index 0000000..3937e16
Binary files /dev/null and b/doc/ionconf/up.png differ
diff --git a/doc/ionnotes.dvi.gz b/doc/ionnotes.dvi.gz
new file mode 100644 (file)
index 0000000..2922df5
Binary files /dev/null and b/doc/ionnotes.dvi.gz differ
diff --git a/doc/ionnotes.out b/doc/ionnotes.out
new file mode 100644 (file)
index 0000000..f29e5e3
--- /dev/null
@@ -0,0 +1,22 @@
+\BOOKMARK [1][-]{section.1}{ Class and object hierarchies}{}
+\BOOKMARK [2][-]{subsection.1.1}{ Class hierarchy}{section.1}
+\BOOKMARK [2][-]{subsection.1.2}{ Object hierarchies: WRegion parents and managers}{section.1}
+\BOOKMARK [3][-]{subsubsection.1.2.1}{ Parent--child relations}{subsection.1.2}
+\BOOKMARK [3][-]{subsubsection.1.2.2}{ Manager--managed relations}{subsection.1.2}
+\BOOKMARK [2][-]{subsection.1.3}{ Summary}{section.1}
+\BOOKMARK [1][-]{section.2}{ Object system implementation}{}
+\BOOKMARK [1][-]{section.3}{ The Lua interface}{}
+\BOOKMARK [2][-]{subsection.3.1}{ Supported types}{section.3}
+\BOOKMARK [2][-]{subsection.3.2}{ Exporting functions}{section.3}
+\BOOKMARK [2][-]{subsection.3.3}{ Calling Lua functions and code}{section.3}
+\BOOKMARK [2][-]{subsection.3.4}{ Miscellaneous notes}{section.3}
+\BOOKMARK [1][-]{section.4}{ Miscellaneous design notes}{}
+\BOOKMARK [2][-]{subsection.4.1}{ Destroying WObj:s}{section.4}
+\BOOKMARK [2][-]{lstlisting.-36}{ The types !char*! and !const char*! as function parameters and return values}{section.4}
+\BOOKMARK [1][-]{section.5}{ C coding style}{}
+\BOOKMARK [2][-]{subsection.5.1}{ Whitespace}{section.5}
+\BOOKMARK [2][-]{subsection.5.2}{ Braces}{section.5}
+\BOOKMARK [2][-]{subsection.5.3}{ Names}{section.5}
+\BOOKMARK [2][-]{subsection.5.4}{ Miscellaneous}{section.5}
+\BOOKMARK [1][-]{section.A}{ The GNU General Public License}{}
+\BOOKMARK [1][-]{section*.4}{Index}{}
diff --git a/doc/ionnotes.ps.gz b/doc/ionnotes.ps.gz
new file mode 100644 (file)
index 0000000..d432ffb
Binary files /dev/null and b/doc/ionnotes.ps.gz differ
diff --git a/doc/ionnotes.tex b/doc/ionnotes.tex
new file mode 100644 (file)
index 0000000..d8c10f3
--- /dev/null
@@ -0,0 +1,60 @@
+\documentclass[english,a4paper,11pt,oldtoc,mctitle]{artikel3}
+\input{macros}
+
+% For including some files from reports
+\newcommand{\xchapter}[1]{\section{#1}}
+\newcommand{\xsection}[1]{\subsection{#1}}
+\newcommand{\xsectionstar}[1]{\subsection*{#1}}
+\newcommand{\xsubsection}[1]{\subsubsection{#1}}
+
+
+\title{Ion: Notes for the module and patch writer}
+\author{Tuomo Valkonen \\ tuomov at iki.fi}
+%%DATE
+
+\makeindex
+
+
+\begin{document}
+
+\maketitle
+
+Ion: Notes for the module and patch writer\\
+Copyright \copyright\  2003--2004 Tuomo Valkonen.
+
+This document is free; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This document 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
+section entitled ''GNU General Public License'' for more details.
+
+\bigskip
+
+\begin{abstract}
+    This document is an unorganized collection of notes for
+    those who want to write modules or patches to Ion.
+\end{abstract}
+
+\tableofcontents
+
+\input{objects}
+
+\input{objectsimpl}
+
+\input{luaif}
+
+\input{designnotes}
+
+\input{cstyle}
+
+\appendix
+
+\input{gpl}
+
+\printindex
+
+\end{document}
diff --git a/doc/ionnotes/WARNINGS b/doc/ionnotes/WARNINGS
new file mode 100644 (file)
index 0000000..726c0ef
--- /dev/null
@@ -0,0 +1,8 @@
+No implementation found for style `hyperref'
+No implementation found for style `ae'
+No implementation found for style `url'
+No implementation found for style `tocbibind'
+No implementation found for style `geometry'
+No implementation found for style `calc'
+
+Substitution of arg to newlabelxx delayed.
diff --git a/doc/ionnotes/contents.png b/doc/ionnotes/contents.png
new file mode 100644 (file)
index 0000000..0c752c6
Binary files /dev/null and b/doc/ionnotes/contents.png differ
diff --git a/doc/ionnotes/crossref.png b/doc/ionnotes/crossref.png
new file mode 100644 (file)
index 0000000..7dd2ddd
Binary files /dev/null and b/doc/ionnotes/crossref.png differ
diff --git a/doc/ionnotes/index.html b/doc/ionnotes/index.html
new file mode 100644 (file)
index 0000000..27c31f2
--- /dev/null
@@ -0,0 +1,172 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>Ion: Notes for the module and patch writer</TITLE>
+<META NAME="description" CONTENT="Ion: Notes for the module and patch writer">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="next" HREF="node1.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html11"
+  HREF="node1.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html5"
+  HREF="http://iki.fi/tuomov/ion/">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev_g.png"> 
+<A NAME="tex2html7"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html9"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html12"
+  HREF="node1.html">Contents</A>
+<B> Up:</B> <A NAME="tex2html6"
+  HREF="http://iki.fi/tuomov/ion/">Ion homepage</A>
+ &nbsp; <B>  <A NAME="tex2html8"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html10"
+  HREF="node8.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+
+<P>
+<H1 ALIGN="CENTER">Ion: Notes for the module and patch writer</H1>
+<DIV CLASS="author_info">
+
+<P ALIGN="CENTER"><STRONG>Tuomo Valkonen</STRONG></P>
+<P ALIGN="CENTER"><I>tuomov at iki.fi</I></P>
+</DIV>
+
+<P>
+Ion: Notes for the module and patch writer
+<BR>
+Copyright &#169;  2003-2004 Tuomo Valkonen.
+
+<P>
+This document is free; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+<P>
+This document 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
+section entitled ''GNU General Public License'' for more details.
+
+<P>
+
+<P><P>
+<BR>
+
+<P>
+
+<H3>Abstract:</H3>
+<DIV CLASS="ABSTRACT">
+    This document is an unorganized collection of notes for
+    those who want to write modules or patches to Ion.
+</DIV>
+<P>
+
+<P>
+<BR><HR>
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html13"
+  HREF="node1.html">Contents</A>
+<LI><A NAME="tex2html14"
+  HREF="node2.html"><SPAN CLASS="arabic">1</SPAN> Class and object hierarchies</A>
+<UL>
+<LI><A NAME="tex2html15"
+  HREF="node2.html#SECTION00021000000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Class hierarchy</A>
+<LI><A NAME="tex2html16"
+  HREF="node2.html#SECTION00022000000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> Object hierarchies: WRegion parents and managers</A>
+<UL>
+<LI><A NAME="tex2html17"
+  HREF="node2.html#SECTION00022100000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Parent-child relations</A>
+<LI><A NAME="tex2html18"
+  HREF="node2.html#SECTION00022200000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Manager-managed relations</A>
+</UL>
+<LI><A NAME="tex2html19"
+  HREF="node2.html#SECTION00023000000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> Summary</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html20"
+  HREF="node3.html"><SPAN CLASS="arabic">2</SPAN> Object system implementation</A>
+<LI><A NAME="tex2html21"
+  HREF="node4.html"><SPAN CLASS="arabic">3</SPAN> The Lua interface</A>
+<UL>
+<LI><A NAME="tex2html22"
+  HREF="node4.html#SECTION00041000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Supported types</A>
+<LI><A NAME="tex2html23"
+  HREF="node4.html#SECTION00042000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Exporting functions</A>
+<LI><A NAME="tex2html24"
+  HREF="node4.html#SECTION00043000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Calling Lua functions and code</A>
+<LI><A NAME="tex2html25"
+  HREF="node4.html#SECTION00044000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous notes</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html26"
+  HREF="node5.html"><SPAN CLASS="arabic">4</SPAN> Miscellaneous design notes</A>
+<UL>
+<LI><A NAME="tex2html27"
+  HREF="node5.html#SECTION00051000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Destroying WObj:s</A>
+<LI><A NAME="tex2html28"
+  HREF="node5.html#SECTION00052000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> The types <TT>char*</TT> and <TT>const char*</TT> as function
+  parameters and return values</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html29"
+  HREF="node6.html"><SPAN CLASS="arabic">5</SPAN> C coding style</A>
+<UL>
+<LI><A NAME="tex2html30"
+  HREF="node6.html#SECTION00061000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Whitespace</A>
+<LI><A NAME="tex2html31"
+  HREF="node6.html#SECTION00062000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Braces</A>
+<LI><A NAME="tex2html32"
+  HREF="node6.html#SECTION00063000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Names</A>
+<LI><A NAME="tex2html33"
+  HREF="node6.html#SECTION00064000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html34"
+  HREF="node7.html">A. The GNU General Public License</A>
+<UL>
+<LI><A NAME="tex2html35"
+  HREF="node7.html#SECTION00071000000000000000">Appendix: How to Apply These Terms to Your New Programs</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html36"
+  HREF="node8.html">Index</A>
+<LI><A NAME="tex2html37"
+  HREF="node9.html">About this document ...</A>
+</UL>
+<!--End of Table of Child-Links-->
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/index.png b/doc/ionnotes/index.png
new file mode 100644 (file)
index 0000000..698f09c
Binary files /dev/null and b/doc/ionnotes/index.png differ
diff --git a/doc/ionnotes/internals.pl b/doc/ionnotes/internals.pl
new file mode 100644 (file)
index 0000000..89e33bb
--- /dev/null
@@ -0,0 +1,34 @@
+# LaTeX2HTML 2002-2-1 (1.71)
+# Associate internals original text with physical files.
+
+
+$key = q/sec:objects/;
+$ref_files{$key} = "$dir".q|node2.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:parentship/;
+$ref_files{$key} = "$dir".q|node2.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:supptypes/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:calling/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:managership/;
+$ref_files{$key} = "$dir".q|node2.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:exporting/;
+$ref_files{$key} = "$dir".q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:classhierarchy/;
+$ref_files{$key} = "$dir".q|node2.html|; 
+$noresave{$key} = "$nosave";
+
+1;
+
diff --git a/doc/ionnotes/ionnotes.css b/doc/ionnotes/ionnotes.css
new file mode 100644 (file)
index 0000000..d22be91
--- /dev/null
@@ -0,0 +1,37 @@
+/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */
+.MATH    { font-family: "Century Schoolbook", serif; }
+.MATH I  { font-family: "Century Schoolbook", serif; font-style: italic }
+.BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold }
+
+/* implement both fixed-size and relative sizes */
+SMALL.XTINY            { font-size : xx-small }
+SMALL.TINY             { font-size : x-small  }
+SMALL.SCRIPTSIZE       { font-size : smaller  }
+SMALL.FOOTNOTESIZE     { font-size : small    }
+SMALL.SMALL            {  }
+BIG.LARGE              {  }
+BIG.XLARGE             { font-size : large    }
+BIG.XXLARGE            { font-size : x-large  }
+BIG.HUGE               { font-size : larger   }
+BIG.XHUGE              { font-size : xx-large }
+
+/* heading styles */
+H1             {  }
+H2             {  }
+H3             {  }
+H4             {  }
+H5             {  }
+
+/* mathematics styles */
+DIV.displaymath                { }     /* math displays */
+TD.eqno                        { }     /* equation-number cells */
+
+
+/* document-specific styles come next */
+DIV.navigation         {   }
+DIV.center             {   }
+SPAN.sc                {   }
+DIV.quote              {   }
+PRE.preform            {   }
+SPAN.textit            { font-style: italic  }
+SPAN.arabic            {   }
diff --git a/doc/ionnotes/ionnotes.html b/doc/ionnotes/ionnotes.html
new file mode 100644 (file)
index 0000000..27c31f2
--- /dev/null
@@ -0,0 +1,172 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>Ion: Notes for the module and patch writer</TITLE>
+<META NAME="description" CONTENT="Ion: Notes for the module and patch writer">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="next" HREF="node1.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html11"
+  HREF="node1.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html5"
+  HREF="http://iki.fi/tuomov/ion/">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev_g.png"> 
+<A NAME="tex2html7"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html9"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html12"
+  HREF="node1.html">Contents</A>
+<B> Up:</B> <A NAME="tex2html6"
+  HREF="http://iki.fi/tuomov/ion/">Ion homepage</A>
+ &nbsp; <B>  <A NAME="tex2html8"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html10"
+  HREF="node8.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+
+<P>
+<H1 ALIGN="CENTER">Ion: Notes for the module and patch writer</H1>
+<DIV CLASS="author_info">
+
+<P ALIGN="CENTER"><STRONG>Tuomo Valkonen</STRONG></P>
+<P ALIGN="CENTER"><I>tuomov at iki.fi</I></P>
+</DIV>
+
+<P>
+Ion: Notes for the module and patch writer
+<BR>
+Copyright &#169;  2003-2004 Tuomo Valkonen.
+
+<P>
+This document is free; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+<P>
+This document 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
+section entitled ''GNU General Public License'' for more details.
+
+<P>
+
+<P><P>
+<BR>
+
+<P>
+
+<H3>Abstract:</H3>
+<DIV CLASS="ABSTRACT">
+    This document is an unorganized collection of notes for
+    those who want to write modules or patches to Ion.
+</DIV>
+<P>
+
+<P>
+<BR><HR>
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html13"
+  HREF="node1.html">Contents</A>
+<LI><A NAME="tex2html14"
+  HREF="node2.html"><SPAN CLASS="arabic">1</SPAN> Class and object hierarchies</A>
+<UL>
+<LI><A NAME="tex2html15"
+  HREF="node2.html#SECTION00021000000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Class hierarchy</A>
+<LI><A NAME="tex2html16"
+  HREF="node2.html#SECTION00022000000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> Object hierarchies: WRegion parents and managers</A>
+<UL>
+<LI><A NAME="tex2html17"
+  HREF="node2.html#SECTION00022100000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Parent-child relations</A>
+<LI><A NAME="tex2html18"
+  HREF="node2.html#SECTION00022200000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Manager-managed relations</A>
+</UL>
+<LI><A NAME="tex2html19"
+  HREF="node2.html#SECTION00023000000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> Summary</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html20"
+  HREF="node3.html"><SPAN CLASS="arabic">2</SPAN> Object system implementation</A>
+<LI><A NAME="tex2html21"
+  HREF="node4.html"><SPAN CLASS="arabic">3</SPAN> The Lua interface</A>
+<UL>
+<LI><A NAME="tex2html22"
+  HREF="node4.html#SECTION00041000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Supported types</A>
+<LI><A NAME="tex2html23"
+  HREF="node4.html#SECTION00042000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Exporting functions</A>
+<LI><A NAME="tex2html24"
+  HREF="node4.html#SECTION00043000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Calling Lua functions and code</A>
+<LI><A NAME="tex2html25"
+  HREF="node4.html#SECTION00044000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous notes</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html26"
+  HREF="node5.html"><SPAN CLASS="arabic">4</SPAN> Miscellaneous design notes</A>
+<UL>
+<LI><A NAME="tex2html27"
+  HREF="node5.html#SECTION00051000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Destroying WObj:s</A>
+<LI><A NAME="tex2html28"
+  HREF="node5.html#SECTION00052000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> The types <TT>char*</TT> and <TT>const char*</TT> as function
+  parameters and return values</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html29"
+  HREF="node6.html"><SPAN CLASS="arabic">5</SPAN> C coding style</A>
+<UL>
+<LI><A NAME="tex2html30"
+  HREF="node6.html#SECTION00061000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Whitespace</A>
+<LI><A NAME="tex2html31"
+  HREF="node6.html#SECTION00062000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Braces</A>
+<LI><A NAME="tex2html32"
+  HREF="node6.html#SECTION00063000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Names</A>
+<LI><A NAME="tex2html33"
+  HREF="node6.html#SECTION00064000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html34"
+  HREF="node7.html">A. The GNU General Public License</A>
+<UL>
+<LI><A NAME="tex2html35"
+  HREF="node7.html#SECTION00071000000000000000">Appendix: How to Apply These Terms to Your New Programs</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html36"
+  HREF="node8.html">Index</A>
+<LI><A NAME="tex2html37"
+  HREF="node9.html">About this document ...</A>
+</UL>
+<!--End of Table of Child-Links-->
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/labels.pl b/doc/ionnotes/labels.pl
new file mode 100644 (file)
index 0000000..2c1867a
--- /dev/null
@@ -0,0 +1,69 @@
+# LaTeX2HTML 2002-2-1 (1.71)
+# Associate labels original text with physical files.
+
+
+$key = q/sec:objects/;
+$external_labels{$key} = "$URL/" . q|node2.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:parentship/;
+$external_labels{$key} = "$URL/" . q|node2.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:supptypes/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:calling/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:managership/;
+$external_labels{$key} = "$URL/" . q|node2.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:exporting/;
+$external_labels{$key} = "$URL/" . q|node4.html|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:classhierarchy/;
+$external_labels{$key} = "$URL/" . q|node2.html|; 
+$noresave{$key} = "$nosave";
+
+1;
+
+
+# LaTeX2HTML 2002-2-1 (1.71)
+# labels from external_latex_labels array.
+
+
+$key = q/sec:objects/;
+$external_latex_labels{$key} = q|1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:parentship/;
+$external_latex_labels{$key} = q|2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:supptypes/;
+$external_latex_labels{$key} = q|3.1|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:calling/;
+$external_latex_labels{$key} = q|3.3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:managership/;
+$external_latex_labels{$key} = q|3|; 
+$noresave{$key} = "$nosave";
+
+$key = q/sec:exporting/;
+$external_latex_labels{$key} = q|3.2|; 
+$noresave{$key} = "$nosave";
+
+$key = q/fig:classhierarchy/;
+$external_latex_labels{$key} = q|1|; 
+$noresave{$key} = "$nosave";
+
+1;
+
diff --git a/doc/ionnotes/next.png b/doc/ionnotes/next.png
new file mode 100644 (file)
index 0000000..1628652
Binary files /dev/null and b/doc/ionnotes/next.png differ
diff --git a/doc/ionnotes/next_g.png b/doc/ionnotes/next_g.png
new file mode 100644 (file)
index 0000000..9d3f591
Binary files /dev/null and b/doc/ionnotes/next_g.png differ
diff --git a/doc/ionnotes/node1.html b/doc/ionnotes/node1.html
new file mode 100644 (file)
index 0000000..b174939
--- /dev/null
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>Contents</TITLE>
+<META NAME="description" CONTENT="Contents">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="next" HREF="node2.html">
+<LINK REL="previous" HREF="ionnotes.html">
+<LINK REL="up" HREF="ionnotes.html">
+<LINK REL="next" HREF="node2.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html48"
+  HREF="node2.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html44"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html38"
+  HREF="ionnotes.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A>  
+<A NAME="tex2html46"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html49"
+  HREF="node2.html">1 Class and object</A>
+<B> Up:</B> <A NAME="tex2html45"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html39"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+ &nbsp; <B>  <A NAME="tex2html47"
+  HREF="node8.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<BR>
+
+<H2><A NAME="SECTION00010000000000000000">
+Contents</A>
+</H2>
+<!--Table of Contents-->
+
+<UL CLASS="TofC">
+<LI><A NAME="tex2html50"
+  HREF="node2.html">1 Class and object hierarchies</A>
+<UL>
+<LI><A NAME="tex2html51"
+  HREF="node2.html#SECTION00021000000000000000">1.1 Class hierarchy</A>
+<LI><A NAME="tex2html52"
+  HREF="node2.html#SECTION00022000000000000000">1.2 Object hierarchies: WRegion parents and managers</A>
+<LI><A NAME="tex2html53"
+  HREF="node2.html#SECTION00023000000000000000">1.3 Summary</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html54"
+  HREF="node3.html">2 Object system implementation</A>
+<LI><A NAME="tex2html55"
+  HREF="node4.html">3 The Lua interface</A>
+<UL>
+<LI><A NAME="tex2html56"
+  HREF="node4.html#SECTION00041000000000000000">3.1 Supported types</A>
+<LI><A NAME="tex2html57"
+  HREF="node4.html#SECTION00042000000000000000">3.2 Exporting functions</A>
+<LI><A NAME="tex2html58"
+  HREF="node4.html#SECTION00043000000000000000">3.3 Calling Lua functions and code</A>
+<LI><A NAME="tex2html59"
+  HREF="node4.html#SECTION00044000000000000000">3.4 Miscellaneous notes</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html60"
+  HREF="node5.html">4 Miscellaneous design notes</A>
+<UL>
+<LI><A NAME="tex2html61"
+  HREF="node5.html#SECTION00051000000000000000">4.1 Destroying WObj:s</A>
+<LI><A NAME="tex2html62"
+  HREF="node5.html#SECTION00052000000000000000">4.2 The types char* and const char* as function parameters and return values</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html63"
+  HREF="node6.html">5 C coding style</A>
+<UL>
+<LI><A NAME="tex2html64"
+  HREF="node6.html#SECTION00061000000000000000">5.1 Whitespace</A>
+<LI><A NAME="tex2html65"
+  HREF="node6.html#SECTION00062000000000000000">5.2 Braces</A>
+<LI><A NAME="tex2html66"
+  HREF="node6.html#SECTION00063000000000000000">5.3 Names</A>
+<LI><A NAME="tex2html67"
+  HREF="node6.html#SECTION00064000000000000000">5.4 Miscellaneous</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html68"
+  HREF="node7.html">A. The GNU General Public License</A>
+<LI><A NAME="tex2html69"
+  HREF="node8.html">Index</A>
+</UL>
+<!--End of Table of Contents-->
+<P>
+
+<P>
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/node2.html b/doc/ionnotes/node2.html
new file mode 100644 (file)
index 0000000..e25fd2d
--- /dev/null
@@ -0,0 +1,444 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>1 Class and object hierarchies</TITLE>
+<META NAME="description" CONTENT="1 Class and object hierarchies">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="next" HREF="node3.html">
+<LINK REL="previous" HREF="node1.html">
+<LINK REL="up" HREF="ionnotes.html">
+<LINK REL="next" HREF="node3.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html82"
+  HREF="node3.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html76"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html70"
+  HREF="node1.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html78"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html80"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html83"
+  HREF="node3.html">2 Object system implementation</A>
+<B> Up:</B> <A NAME="tex2html77"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html71"
+  HREF="node1.html">Contents</A>
+ &nbsp; <B>  <A NAME="tex2html79"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html81"
+  HREF="node8.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html84"
+  HREF="node2.html#SECTION00021000000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Class hierarchy</A>
+<LI><A NAME="tex2html85"
+  HREF="node2.html#SECTION00022000000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> Object hierarchies: WRegion parents and managers</A>
+<UL>
+<LI><A NAME="tex2html86"
+  HREF="node2.html#SECTION00022100000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Parent-child relations</A>
+<LI><A NAME="tex2html87"
+  HREF="node2.html#SECTION00022200000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Manager-managed relations</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html88"
+  HREF="node2.html#SECTION00023000000000000000"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> Summary</A>
+</UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00020000000000000000"></A>
+<A NAME="sec:objects"></A>
+<BR>
+<SPAN CLASS="arabic">1</SPAN> Class and object hierarchies
+</H1>
+
+<P>
+While Ion does not not have a truly object-oriented design
+<A NAME="tex2html1"
+  HREF="#foot212"><SUP><SPAN CLASS="arabic">1</SPAN></SUP></A>,
+things that appear on the computer screen are, however, quite
+naturally expressed as such ''objects''. Therefore Ion implements
+a rather primitive OO system for these screen objects and some
+other things. 
+
+<P>
+It is essential for the module writer to learn this object
+system, but also people who write their own binding configuration files
+necessarily come into contact with the class and object hierarchies
+- you need to know which binding setup routines apply where, 
+and what functions can be used as handlers in which bindings.
+It is the purpose of this section to attempt to explain these 
+hierarchies. If you do not wish the read the full section, at least
+read the summary at the end of it, so that you understand the very
+basic relations.
+
+<P>
+For simplicity we consider only the essential-for-basic-configuration
+Ioncore, <SPAN  CLASS="textit">mod_tiling</SPAN> and <SPAN  CLASS="textit">mod_query</SPAN> classes. 
+See Appendix <A HREF="#app:fullhierarchy"><IMG  ALIGN="BOTTOM" BORDER="1" ALT="[*]" SRC="crossref.png"></A> for the full class hierachy visible
+to Lua side.
+
+<P>
+
+<H2><A NAME="SECTION00021000000000000000">
+<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN> Class hierarchy</A>
+</H2>
+
+<P>
+One of the most important principles of object-oriented design methodology
+is inheritance; roughly how classes (objects are instances of classes)
+extend on others' features. Inheritance gives rise to class hierarchy.
+In the case of single-inheritance this hierarchy can be expressed as a
+tree where the class at the root is inherited by all others below it
+and so on. Figure <A HREF="#fig:classhierarchy">1</A> lists out the Ion class 
+hierarchy and below we explain what features of Ion the classes 
+implement.
+
+<P>
+
+<DIV ALIGN="CENTER"><A NAME="fig:classhierarchy"></A><A NAME="319"></A>
+<TABLE>
+<CAPTION ALIGN="BOTTOM"><STRONG>Figure 1:</STRONG>
+Partial Ioncore, <SPAN  CLASS="textit">mod_tiling</SPAN> and <SPAN  CLASS="textit">mod_query</SPAN> 
+    class hierarchy.</CAPTION>
+<TR><TD><PRE>
+    Obj
+     |--&gt;WRegion
+     |    |--&gt;WClientWin
+     |    |--&gt;WWindow
+     |    |    |--&gt;WRootWin
+     |    |    |--&gt;WMPlex
+     |    |    |    |--&gt;WScreen
+     |    |    |    |--&gt;WFrame
+     |    |    |--&gt;WInput (mod_query)
+     |    |         |--&gt;WEdln (mod_query)
+     |    |         |--&gt;WMessage (mod_query)
+     |    |--&gt;WGroup
+     |    |    |--&gt;WGroupWS
+     |    |    |--&gt;WGroupCW
+     |    |--&gt;WTiling (mod_tiling)
+     |--&gt;WSplit (mod_tiling)
+</PRE></TD></TR>
+</TABLE>
+</DIV>
+
+<P>
+The core classes:
+
+<P>
+<DL>
+<DT><STRONG>Obj</STRONG></DT>
+<DD><A NAME="325"></A>
+    Is the base of Ion's object system.
+
+<P>
+</DD>
+<DT><STRONG>WRegion</STRONG></DT>
+<DD><A NAME="326"></A>
+    is the base class for everything corresponding to something on the
+    screen. Each object of type WRegion has a size and  position
+    relative to the parent WRegion. While a big part of Ion 
+    operates on these instead of more specialised classes, WRegion
+    is a ''virtual''  base class in that there are no objects of ''pure''
+    type WRegion; all concrete regions are objects of some class 
+    that inherits WRegion.
+
+<P>
+</DD>
+<DT><STRONG>WClientWin</STRONG></DT>
+<DD><A NAME="327"></A> is a class for
+    client window objects, the objects that window managers are
+    supposed to manage.
+
+<P>
+</DD>
+<DT><STRONG>WWindow</STRONG></DT>
+<DD><A NAME="328"></A> is the base class for all
+    internal objects having an X window associated to them
+    (WClientWins also have X windows associated to them).
+
+<P>
+</DD>
+<DT><STRONG>WRootWin</STRONG></DT>
+<DD><A NAME="329"></A> is the class for
+    root windows<A NAME="242"></A> of X screens<A NAME="243"></A>.
+    Note that an ''X screen'' or root window is not necessarily a
+    single  physical screen<A NAME="244"></A> as a root window
+    may be split over multiple screens when multi-head extensions 
+    such as Xinerama<A NAME="245"></A> are used. (Actually there
+    can be only one WRootWin when Xinerama is used.)
+
+<P>
+</DD>
+<DT><STRONG>WMPlex</STRONG></DT>
+<DD>is a base class for all regions that''multiplex'' 
+    other regions. This means that of the regions managed by the multiplexer,
+    only one can be displayed at a time. Classes that inhereit WMPlex
+    include screens and frames.
+
+<P>
+</DD>
+<DT><STRONG>WScreen</STRONG></DT>
+<DD><A NAME="330"></A> is the class for objects
+    corresponding to physical screens. Screens may share a root
+    window when Xinerama multihead extensions are used as explained
+    above.
+
+<P>
+</DD>
+<DT><STRONG>WFrame</STRONG></DT>
+<DD><A NAME="331"></A> is the class for frames.
+    While most Ion's objects have no graphical presentation, frames basically
+    add to WMPlexes the decorations around client windows 
+    (borders, tabs).
+
+<P>
+</DD>
+<DT><STRONG>WGroup</STRONG></DT>
+<DD><A NAME="332"></A> is the base class for groups.
+    Particular types of groups are workspaces 
+    (WGroupWS<A NAME="333"></A>)
+    and groups of client windows
+    (WGroupCW<A NAME="334"></A>).
+</DD>
+</DL>
+
+<P>
+Classes implemented by the <SPAN  CLASS="textit">mod_tiling</SPAN> module:
+
+<P>
+<DL>
+<DT><STRONG>WTiling</STRONG></DT>
+<DD><A NAME="336"></A> is the class for tilings
+    of frames.
+  
+</DD>
+<DT><STRONG>WSplit</STRONG></DT>
+<DD><A NAME="337"></A> (or, more specifically, classes
+    that inherit it) encode the WTiling tree structure.
+</DD>
+</DL>
+
+<P>
+Classes implemented by the <SPAN  CLASS="textit">mod_query</SPAN> module:
+
+<P>
+<DL>
+<DT><STRONG>WInput</STRONG></DT>
+<DD><A NAME="339"></A> is a virtual base class for the
+    two classes below.
+  
+</DD>
+<DT><STRONG>WEdln</STRONG></DT>
+<DD><A NAME="340"></A> is the class for the ''queries'',
+    the text inputs that usually appear at bottoms of frames and sometimes
+    screens. Queries are the functional equivalent of ''mini buffers'' in
+    many text editors.
+  
+</DD>
+<DT><STRONG>WMessage</STRONG></DT>
+<DD><A NAME="341"></A> implements the boxes for 
+    warning and other messages that Ion may wish to display to the user. 
+    These also usually appear at bottoms of frames.
+</DD>
+</DL>
+
+<P>
+There are also some other ''proxy'' classes that do not refer
+to objects on the screen. The only important one of these for
+basic configuration is WMoveresMode that is used for
+binding callbacks in the move and resize mode.
+
+<P>
+
+<H2><A NAME="SECTION00022000000000000000">
+<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN> Object hierarchies: WRegion parents and managers</A>
+</H2>
+
+<P>
+
+<H3><A NAME="SECTION00022100000000000000">
+<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN> Parent-child relations</A>
+</H3>
+Each object of type WRegion has a parent and possibly a manager
+associated to it. The parent<A NAME="282"></A> for an object is always a 
+WWindow and for WRegion with an X window (WClientWin,
+WWindow) the parent WWindow is given by the same relation of
+the X windows. For other WRegions the relation is not as clear.
+There is generally very few restrictions other than the above on the
+parent--child relation but the most common is as described in
+Figure <A HREF="#fig:parentship">2</A>.
+
+<P>
+
+<DIV ALIGN="CENTER"><A NAME="fig:parentship"></A><A NAME="293"></A>
+<TABLE>
+<CAPTION ALIGN="BOTTOM"><STRONG>Figure 2:</STRONG>
+Most common parent-child relations</CAPTION>
+<TR><TD><PRE>
+    WRootWins
+     |--&gt;WScreens
+          |--&gt;WGroupWSs
+          |--&gt;WTilings
+          |--&gt;WClientWins in full screen mode
+          |--&gt;WFrames
+               |--&gt;WGroupCWs
+               |--&gt;WClientWins
+               |--&gt;WFrames for transients
+               |--&gt;a possible WEdln or WMessage
+</PRE></TD></TR>
+</TABLE>
+</DIV>
+
+<P>
+WRegions have very little control over their children as a parent.
+The manager<A NAME="297"></A> WRegion has much more control over its
+managed WRegions. Managers, for example, handle resize requests,
+focusing and displaying of the managed regions. Indeed the manager--managed
+relationship gives a better picture of the logical ordering of objects on
+the screen. Again, there are generally few limits, but the most common
+hierarchy is given in Figure <A HREF="#fig:managership">3</A>. Note that sometimes
+the parent and manager are the same object and not all objects may have
+a manager (e.g. the dock in the dock module at the time of writing this)
+but all have a parent-a screen if not anything else.
+
+<P>
+
+<H3><A NAME="SECTION00022200000000000000">
+<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">2</SPAN> Manager-managed relations</A>
+</H3>
+
+<P>
+
+<DIV ALIGN="CENTER"><A NAME="fig:managership"></A><A NAME="305"></A>
+<TABLE>
+<CAPTION ALIGN="BOTTOM"><STRONG>Figure 3:</STRONG>
+Most common manager-managed relations</CAPTION>
+<TR><TD><PRE>
+    WRootWins
+     |--&gt;WScreens
+          |--&gt;WGroupCWs for full screen WClientWins
+          |    |--&gt;WClientWins
+          |    |--&gt;WFrames for transients (dialogs)
+          |         |--&gt; WClientWin
+          |--&gt;WGroupWSs for workspaces
+          |    |--&gt;WTiling
+          |    |    |--&gt;possibly a WEdln, WMessage or WMenu
+          |    |    |--&gt;WFrames
+          |    |         |--&gt;WGroupCWs (with contents as above)
+          |    |--&gt;WFrames for floating content
+          |--&gt;WFrames for sticky stuff, such as the scratchpad
+</PRE></TD></TR>
+</TABLE>
+</DIV>
+
+<P>
+Note that a workspace can manage another workspace. This can be
+achieved with the <A HREF="#fn:attach_new"><TT>attach_new</TT></A> function, and allows you to nest
+workspaces as deep as you want.
+
+<P>
+
+<H2><A NAME="SECTION00023000000000000000">
+<SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">3</SPAN> Summary</A>
+</H2>
+
+<P>
+In the standard setup, keeping queries, messages and menus out of
+consideration:
+
+<P>
+
+<UL>
+<LI>The top-level objects that matter are screens and they correspond
+    to physical screens. The class for screens is WScreen.
+</LI>
+<LI>Screens contain (multiplex) groups (WGroup) and other 
+    objects, such as WFrames. Some of these are mutually exclusive
+    to be viewed at a time.
+</LI>
+<LI>Groups of the specific kind WGroupWS often contain a
+    WTiling tiling for tiling frames (WFrame), but 
+    groups may also directly contain floating frames.
+</LI>
+<LI>Frames are the objects with decorations such as tabs and borders.
+    Frames contain (multiplex) among others (groups of) client windows, 
+    to each of which corresponds a tab in the frame's decoration. Only 
+    one client window (or other object) can be shown at a time in each 
+    frame. The class for client windows is WClientWin.
+</LI>
+</UL>
+
+<P>
+<BR><HR><H4>Footnotes</H4>
+<DL>
+<DT><A NAME="foot212">... design</A><A
+ HREF="node2.html#tex2html1"><SUP><SPAN CLASS="arabic">1</SPAN></SUP></A></DT>
+<DD>the author doesn't like such artificial designs
+
+</DD>
+</DL>
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html82"
+  HREF="node3.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html76"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html70"
+  HREF="node1.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html78"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html80"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html83"
+  HREF="node3.html">2 Object system implementation</A>
+<B> Up:</B> <A NAME="tex2html77"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html71"
+  HREF="node1.html">Contents</A>
+ &nbsp; <B>  <A NAME="tex2html79"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html81"
+  HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/node3.html b/doc/ionnotes/node3.html
new file mode 100644 (file)
index 0000000..4f17c4e
--- /dev/null
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>2 Object system implementation</TITLE>
+<META NAME="description" CONTENT="2 Object system implementation">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="next" HREF="node4.html">
+<LINK REL="previous" HREF="node2.html">
+<LINK REL="up" HREF="ionnotes.html">
+<LINK REL="next" HREF="node4.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html101"
+  HREF="node4.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html95"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html89"
+  HREF="node2.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html97"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html99"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html102"
+  HREF="node4.html">3 The Lua interface</A>
+<B> Up:</B> <A NAME="tex2html96"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html90"
+  HREF="node2.html">1 Class and object</A>
+ &nbsp; <B>  <A NAME="tex2html98"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html100"
+  HREF="node8.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+
+<H1><A NAME="SECTION00030000000000000000">
+<SPAN CLASS="arabic">2</SPAN> Object system implementation</A>
+</H1>
+
+<P>
+First, to get things clear, what are considered objects here are C
+structures containing a properly initialized <A NAME="428"></A>
+structure defined in <SPAN  CLASS="textit">ioncore/obj.h</SPAN> as the first element (or the
+first element of the structure which is the first element and so on which
+gives rise to inheritance). The WObj structure contains a pointer
+to a WObjDescr<A NAME="430"></A> class type info structure and
+a list of so called ''watches''. The WObjDescr structure simply
+lists the class name, a table of dynamic functions and a pointer to
+deinitialisation function (or ''destructor'').
+
+<P>
+Ion does not do any reference counting, garbage collecting or other
+fancy things related to automatic safe freeing of objects with its
+simplistic object system. Instead special watches (the WWatch
+<A NAME="431"></A> structure) may be used to create safe references to
+objects that might be destroyed during the time the specific pointer is
+needed. When an object is destroyed, its list of watches is processed,
+setting the pointers in the watches to NULL and the watch handlers for
+each watch are called. 
+
+<P>
+
+<P>
+
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html101"
+  HREF="node4.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html95"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html89"
+  HREF="node2.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html97"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html99"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html102"
+  HREF="node4.html">3 The Lua interface</A>
+<B> Up:</B> <A NAME="tex2html96"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html90"
+  HREF="node2.html">1 Class and object</A>
+ &nbsp; <B>  <A NAME="tex2html98"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html100"
+  HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/node4.html b/doc/ionnotes/node4.html
new file mode 100644 (file)
index 0000000..037e8bc
--- /dev/null
@@ -0,0 +1,310 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>3 The Lua interface</TITLE>
+<META NAME="description" CONTENT="3 The Lua interface">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="next" HREF="node5.html">
+<LINK REL="previous" HREF="node3.html">
+<LINK REL="up" HREF="ionnotes.html">
+<LINK REL="next" HREF="node5.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html115"
+  HREF="node5.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html109"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html103"
+  HREF="node3.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html111"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html113"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html116"
+  HREF="node5.html">4 Miscellaneous design notes</A>
+<B> Up:</B> <A NAME="tex2html110"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html104"
+  HREF="node3.html">2 Object system implementation</A>
+ &nbsp; <B>  <A NAME="tex2html112"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html114"
+  HREF="node8.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html117"
+  HREF="node4.html#SECTION00041000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Supported types</A>
+<LI><A NAME="tex2html118"
+  HREF="node4.html#SECTION00042000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Exporting functions</A>
+<LI><A NAME="tex2html119"
+  HREF="node4.html#SECTION00043000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Calling Lua functions and code</A>
+<LI><A NAME="tex2html120"
+  HREF="node4.html#SECTION00044000000000000000"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous notes</A>
+</UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00040000000000000000">
+<SPAN CLASS="arabic">3</SPAN> The Lua interface</A>
+</H1>
+
+<P>
+This section finally describes the implementation details and how modules
+should us the Lua interface. First, in section <A HREF="#sec:supptypes">3.1</A>
+we look at types supported by the interface, how objects are passed to Lua
+code and how Lua tables should be accessed from Ion and modules. In section
+<A HREF="#sec:exporting">3.2</A> the methods for exporting functions and how they
+are called from Lua are explained and in section <A HREF="#sec:calling">3.3</A> the
+method for calling Lua functions is explained.
+
+<P>
+
+<H2><A NAME="SECTION00041000000000000000"></A>
+<A NAME="sec:supptypes"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN> Supported types
+</H2>
+
+<P>
+The following types are supported in passing parameters between the
+C side of Ion and Lua:
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1">
+<TR><TD ALIGN="RIGHT">Identifier
+ character</TD>
+<TD ALIGN="LEFT">C type</TD>
+<TD ALIGN="LEFT">Description</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">i</TD>
+<TD ALIGN="LEFT">int</TD>
+<TD ALIGN="LEFT">Integer</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">s</TD>
+<TD ALIGN="LEFT">char*</TD>
+<TD ALIGN="LEFT">String</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">S</TD>
+<TD ALIGN="LEFT">const char*</TD>
+<TD ALIGN="LEFT">Constant string</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">d</TD>
+<TD ALIGN="LEFT">double</TD>
+<TD ALIGN="LEFT">&nbsp;</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">b</TD>
+<TD ALIGN="LEFT">bool</TD>
+<TD ALIGN="LEFT">&nbsp;</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">t</TD>
+<TD ALIGN="LEFT">ExtlTab<A NAME="521"></A></TD>
+<TD ALIGN="LEFT">Reference to Lua table</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">f</TD>
+<TD ALIGN="LEFT">ExltFn<A NAME="522"></A></TD>
+<TD ALIGN="LEFT">Reference to Lua function.</TD>
+</TR>
+<TR><TD ALIGN="RIGHT">o</TD>
+<TD ALIGN="LEFT">Any WObj*</TD>
+<TD ALIGN="LEFT">&nbsp;</TD>
+</TR>
+</TABLE>
+
+<P>
+The difference between identifiers 's' and 'S' is that constant
+strings as return values are not free'd by the level 1 call handler
+(see below) after passing to Lua (<TT>lua_pushstring</TT> always makes a
+copy) unlike normal strings. String parameters are always assumed to be
+the property of the caller and thus implicitly const.
+
+<P>
+Likewise, if a reference to 't' or 'f' is wished to be stored
+beyond the lifetime of a function receiving such as an argument, a new
+reference should be created with <TT>extl_ref_table</TT>/<TT>fn</TT>.
+References can be free'd with
+<TT>extl_unref_table</TT>/<TT>fn</TT>. References gotten as return values with
+the <TT>extl_table_get</TT> (how these work should be self-explanatory!)
+functions are property of the caller and should be unreferenced with the
+above-mentioned functions when no longer needed.
+The functions <TT>extl_fn</TT>/<TT>table_none()</TT>
+return the equivalent of NULL.
+
+<P>
+WObjs are passed to Lua code with WWatch userdatas pointing to
+them so the objects can be safely deleted although Lua code might still be
+referencing them. (This is why SWIG or tolua would not have helped in
+creating the interface: extra wrappers for each function would still
+have been needed to nicely integrate into Ion's object system. Even in
+the case that Ion was written in C++ this would be so unless extra bloat
+adding pointer-like objects were used everywhere instead of pointers.)
+It may be sometimes necessary check in Lua code that a value known to
+be an Ion WObj is of certain type. This can be accomplished with
+<TT>obj_is(obj, "typename")</TT>. <TT>obj_typename(obj)</TT> returns type
+name for a WObj.
+
+<P>
+
+<H2><A NAME="SECTION00042000000000000000"></A>
+<A NAME="sec:exporting"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN> Exporting functions
+</H2>
+
+<P>
+Exported functions (those available to the extension language) are
+defined by placing <TT>EXTL_EXPORT</TT> before the function implementation
+in the C source. The script mkexports.pl is then used to automatically
+generate <SPAN  CLASS="textit">exports.c</SPAN> from the source files if
+<TT>MAKE_EXPORTS=modulename</TT>
+is specified in the Makefile. All pointers with type beginning with a 'W'
+are assumed to be pointers to something inheriting WObj. In
+addition to a table of exported functions and second level call handlers
+for these, <SPAN  CLASS="textit">exports.c</SPAN> will contain two functions
+<TT>module_register_exports()</TT> and
+<TT>module_unregister_exports()</TT> that should then be called in module
+initialisation and deinitialisation code.
+
+<P>
+You've seen the terms level 1 and 2 call handler mentioned above. 
+<A NAME="491"></A>
+The Lua support code uses two so called call handlers to convert and check
+the types of parameters passed from Lua to C and back to Lua. The first
+one of these call handlers is the same for all exported functions and
+indeed lua sees all exported as the same C function (the L1 call handler)
+but with different upvalues passing a structure describing the actual
+function and the second level call handler. The L1 call handler checks
+that the parameters received from Lua match a template given as a string
+of the identifier characters defined above. If everything checks out ok,
+the parameters are then put in an array of C unions that can contain
+anyof these known types and the L2 call handler is called.
+
+<P>
+The L2 call handler (which is automatically generated by the mkexports.pl
+script) for each exported function checks that the passed WObjs
+are of the more refined type required by the function and then calls the
+actual function. While the WObj checking could be done in the L1 handler
+too, the L2 call handlers are needed because we may not know how the target
+platform passes each parameter type to the called function. Thefore we
+must let the C compiler generate the code to convert from a simple and
+known enough parameter passing method (the unions) to the actual
+parameter passing method. When the called function returns everything
+is done in reverse order for return values (only one return value is
+supported by the generated L2 call handlers).
+
+<P>
+
+<H2><A NAME="SECTION00043000000000000000"></A>
+<A NAME="sec:calling"></A>
+<BR>
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN> Calling Lua functions and code
+</H2>
+
+<P>
+The functions
+<TT>extl_call</TT><A NAME="515"></A>,
+<TT>extl_call_named</TT><A NAME="516"></A>,
+<TT>extl_dofile</TT><A NAME="517"></A> and
+<TT>extl_dostring</TT><A NAME="518"></A>
+call a referenced function (ExtlFn), named function, execute a
+string and a file, respectively. The rest of the parameters for all these
+functions are similar. The 'spec' argument is a string of identifier
+characters (see above) describing the parameters to be passed. These
+parameters follow after 'rspec'. For dofile and dostring these parameters
+are passed in the global table arg (same as used for program command
+lien parameters) and for functions as you might expect. The parameter
+'rspec' is a similar description of return values. Pointers to variables
+that should be set to the return values follow after the input values.
+The return value of all these functions tells if the call and parameter
+passing succeeded or not.
+
+<P>
+Sometimes it is necessary to block calls to all but a limited set of
+Ion functions. This can be accomplished with
+<TT>extl_set_safelist</TT><A NAME="519"></A>.
+The parameter to this function is a NULL-terminated array of strings
+and the return value is a similar old safelist.
+The call <TT>extl_set_safelist(NULL)</TT> removes any safelist and allows
+calls to all exported functions.
+
+<P>
+
+<H2><A NAME="SECTION00044000000000000000">
+<SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous notes</A>
+</H2>
+
+<P>
+Configuration files should be read as before with the function
+<TT>read_config_for</TT><A NAME="520"></A>
+except that the list of known options is no longer present.
+
+<P>
+Winprops are now stored in Lua tables and can contain arbitrary
+properties. The 'proptab' entry in each WClientWin is a reference
+to a winprop table or <TT>extl_table_none()</TT> if such does not exist
+and properties may be read with the <TT>extl_table_gets</TT> functions.
+(It is perfectly legal to pass <TT>extl_table_none()</TT> references to
+<TT>extl_table_get*</TT>.)
+
+<P>
+
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html115"
+  HREF="node5.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html109"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html103"
+  HREF="node3.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html111"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html113"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html116"
+  HREF="node5.html">4 Miscellaneous design notes</A>
+<B> Up:</B> <A NAME="tex2html110"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html104"
+  HREF="node3.html">2 Object system implementation</A>
+ &nbsp; <B>  <A NAME="tex2html112"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html114"
+  HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/node5.html b/doc/ionnotes/node5.html
new file mode 100644 (file)
index 0000000..51c46f8
--- /dev/null
@@ -0,0 +1,188 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>4 Miscellaneous design notes</TITLE>
+<META NAME="description" CONTENT="4 Miscellaneous design notes">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="next" HREF="node6.html">
+<LINK REL="previous" HREF="node4.html">
+<LINK REL="up" HREF="ionnotes.html">
+<LINK REL="next" HREF="node6.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html133"
+  HREF="node6.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html127"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html121"
+  HREF="node4.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html129"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html131"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html134"
+  HREF="node6.html">5 C coding style</A>
+<B> Up:</B> <A NAME="tex2html128"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html122"
+  HREF="node4.html">3 The Lua interface</A>
+ &nbsp; <B>  <A NAME="tex2html130"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html132"
+  HREF="node8.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html135"
+  HREF="node5.html#SECTION00051000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Destroying WObj:s</A>
+<LI><A NAME="tex2html136"
+  HREF="node5.html#SECTION00052000000000000000"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> The types <TT>char*</TT> and <TT>const char*</TT> as function
+  parameters and return values</A>
+</UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00050000000000000000">
+<SPAN CLASS="arabic">4</SPAN> Miscellaneous design notes</A>
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION00051000000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN> Destroying WObj:s</A>
+</H2>
+
+<P>
+To keep Ion's code as simple as possible yet safe, there are restrictions
+when the WObj
+<TT>destroy_obj</TT><A NAME="619"></A>
+function that calls watches, the deinit routine and frees memory may
+be called directly. In all other cases the 
+<TT>defer_destroy</TT><A NAME="620"></A>
+function should be used to defer the call of <TT>destroy_obj</TT> until
+Ioncore returns to its main event loop. 
+
+<P>
+Calling the <TT>destroy_obj</TT> function directly is allowed in the
+following cases:
+
+<UL>
+<LI>In the deinit handler for another object. Usually managed objects
+      are destroyed this way.
+</LI>
+<LI>The object was created during the current call to the function
+      that wants to get rid of the object. This is the case, for example,
+      when the function created a frame to manage some other object but for
+      some reason failed to reparent the object to this frame.
+</LI>
+<LI>In a deferred action handler set with
+      <TT>defer_action</TT><A NAME="621"></A>.
+      Like deferred destroys, other deferred actions are called when
+      Ioncore has returned to the main loop.
+</LI>
+<LI>You are absolute sure that C code outside your code has no
+      references to the object.
+</LI>
+</UL>
+
+<P>
+If there are no serious side effects from deferring destroying the
+object or you're unsure whether it is safe to destroy the object
+immediately, use <TT>defer_destroy</TT>.
+
+<P>
+
+<H2><A NAME="SECTION00052000000000000000">
+<SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">2</SPAN> The types <TT>char*</TT> and <TT>const char*</TT> as function
+  parameters and return values</A>
+</H2>
+
+<P>
+The following rules should apply to using strings as return values and
+parameters to functions. 
+
+<P>
+<TABLE CELLPADDING=3 BORDER="1" WIDTH="100%">
+<TR><TD ALIGN="LEFT">Type</TD>
+<TD ALIGN="LEFT">Return value</TD>
+<TD ALIGN="LEFT">Parameter</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>const char*</TT></TD>
+<TD ALIGN="LEFT">The string is owned by the called function
+       and the caller is only quaranteed short-term read access to the
+       string.</TD>
+<TD ALIGN="LEFT">The called function may only read the string during its execution.
+       For further reference a copy must be made.</TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>char*</TT></TD>
+<TD ALIGN="LEFT">The string is the caller's responsibility and it
+       <SPAN  CLASS="textit">must</SPAN> free it when no longer needed.</TD>
+<TD ALIGN="LEFT">The called function may modify the string but the ''owner'' of
+       the string is case-dependant.</TD>
+</TR>
+</TABLE>
+
+<P>
+
+<P>
+
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html133"
+  HREF="node6.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html127"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html121"
+  HREF="node4.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html129"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html131"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html134"
+  HREF="node6.html">5 C coding style</A>
+<B> Up:</B> <A NAME="tex2html128"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html122"
+  HREF="node4.html">3 The Lua interface</A>
+ &nbsp; <B>  <A NAME="tex2html130"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html132"
+  HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/node6.html b/doc/ionnotes/node6.html
new file mode 100644 (file)
index 0000000..8c20249
--- /dev/null
@@ -0,0 +1,285 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>5 C coding style</TITLE>
+<META NAME="description" CONTENT="5 C coding style">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="next" HREF="node7.html">
+<LINK REL="previous" HREF="node5.html">
+<LINK REL="up" HREF="ionnotes.html">
+<LINK REL="next" HREF="node7.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html149"
+  HREF="node7.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html143"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html137"
+  HREF="node5.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html145"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html147"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html150"
+  HREF="node7.html">A. The GNU General</A>
+<B> Up:</B> <A NAME="tex2html144"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html138"
+  HREF="node5.html">4 Miscellaneous design notes</A>
+ &nbsp; <B>  <A NAME="tex2html146"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html148"
+  HREF="node8.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html151"
+  HREF="node6.html#SECTION00061000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Whitespace</A>
+<LI><A NAME="tex2html152"
+  HREF="node6.html#SECTION00062000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Braces</A>
+<LI><A NAME="tex2html153"
+  HREF="node6.html#SECTION00063000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Names</A>
+<LI><A NAME="tex2html154"
+  HREF="node6.html#SECTION00064000000000000000"><SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous</A>
+</UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00060000000000000000">
+<SPAN CLASS="arabic">5</SPAN> C coding style</A>
+</H1>
+
+<P>
+If you want to submit patches to Ion, you MUST follow my coding 
+style, even if you think it is the root of all evil. We don't want
+the code to be an incomprehensible mess of styles and I have better
+things to do than fix other people's style to match mine. The style
+should be obvious by studying the source, but here's a list of some
+things to take note of.
+
+<P>
+
+<H2><A NAME="SECTION00061000000000000000">
+<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">1</SPAN> Whitespace</A>
+</H2>
+
+<P>
+
+<UL>
+<LI>Indentations of 4 with <SPAN  CLASS="textit">tab size=4</SPAN>. 
+
+<P>
+</LI>
+<LI>No extra spaces between operators, delimiters etc. except
+    
+<UL>
+<LI>around logical and, or (<TT>&amp;&amp;</TT>, <TT>||</TT>)
+</LI>
+<LI>around the conditional <TT>a ? b : c</TT>
+</LI>
+<LI>after commas and semicolons
+    
+</LI>
+</UL>  
+    In my opinion this helps pointing out arithmetic or other
+    expressions within logical expressions or parameter lists.
+
+<P>
+</LI>
+<LI>All kinds of labels are out-tended to the level of the higher
+    level block. For example:
+
+<P>
+<PRE>    
+void foo()
+{
+again:
+    switch(asdf){
+    case 1:
+        ...
+        break;
+    default:
+        ...
+        break;
+    }
+}
+</PRE>
+</LI>
+</UL>
+
+<P>
+
+<H2><A NAME="SECTION00062000000000000000">
+<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">2</SPAN> Braces</A>
+</H2>
+
+<P>
+
+<UL>
+<LI>Opening brace is at the end of the line, except in function
+    bodies, where it is at the beginning of the line following
+    the definition.
+
+<P>
+</LI>
+<LI>Never put the body of a control statement on the same line
+    with the statement (e.g. <code>if(foo){ bar() }</code>).
+
+<P>
+For example, the block
+<PRE>    
+void foo(int a, int b)
+{
+    if(a==b &amp;&amp; c+d==e){
+        ...
+    }
+}
+</PRE>
+
+<P>
+has correct style while the block
+
+<P>
+<PRE>   
+void foo(int a,int b) {
+    if (a == b &amp;&amp; c + d == e) {
+        ...
+    }
+}
+</PRE>
+
+<P>
+does not.
+
+<P>
+</LI>
+<LI>The <TT>else</TT> keyword follows immediately after the closing brace of
+    previous <TT>if</TT>, if any. (This might change so I don't care if you put
+    it on the next line.)
+
+<P>
+</LI>
+<LI>I have used the convention that control statement bodies containing
+    a single statement do not need braces around the block if, in case of
+    the <TT>if</TT> all the blocks in  <TT>if ...  else if ... else</TT>
+    contain just one statement. If you want to, just use braces in every 
+    case.
+</LI>
+</UL>
+
+<P>
+
+<H2><A NAME="SECTION00063000000000000000">
+<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">3</SPAN> Names</A>
+</H2>
+
+<P>
+
+<UL>
+<LI>Function and variable names only have lower case letters. Type
+    names are in mixed case while constants and macros (<TT>#define</TT>s)
+    are in upper case letters.
+</LI>
+</UL>
+
+<P>
+
+<H2><A NAME="SECTION00064000000000000000">
+<SPAN CLASS="arabic">5</SPAN>.<SPAN CLASS="arabic">4</SPAN> Miscellaneous</A>
+</H2>
+
+<P>
+
+<UL>
+<LI>In the definition of a pointer variable, the asterisk is attached
+    to the variable name: <TT>char *s;</TT>. (One could claim this an 
+    exception to the second rule.)
+
+<P>
+</LI>
+<LI>You might optionally want to use Jed's foldings to group blocks
+    of related code in a file to keep it organized:
+
+<P>
+<PRE>    
+/*{{{ Many related functions */
+       
+void code()
+{
+    ...        
+}
+
+...
+
+/*}}}*/
+</PRE>
+</LI>
+</UL>
+
+<P>
+I think that's mostly it. Study the source when in doubt.
+
+<P>
+
+<P>
+
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html149"
+  HREF="node7.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html143"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html137"
+  HREF="node5.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html145"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html147"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html150"
+  HREF="node7.html">A. The GNU General</A>
+<B> Up:</B> <A NAME="tex2html144"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html138"
+  HREF="node5.html">4 Miscellaneous design notes</A>
+ &nbsp; <B>  <A NAME="tex2html146"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html148"
+  HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/node7.html b/doc/ionnotes/node7.html
new file mode 100644 (file)
index 0000000..7e36403
--- /dev/null
@@ -0,0 +1,597 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>A. The GNU General Public License</TITLE>
+<META NAME="description" CONTENT="A. The GNU General Public License">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="next" HREF="node8.html">
+<LINK REL="previous" HREF="node6.html">
+<LINK REL="up" HREF="ionnotes.html">
+<LINK REL="next" HREF="node8.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html167"
+  HREF="node8.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html161"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html155"
+  HREF="node6.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html163"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html165"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html168"
+  HREF="node8.html">Index</A>
+<B> Up:</B> <A NAME="tex2html162"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html156"
+  HREF="node6.html">5 C coding style</A>
+ &nbsp; <B>  <A NAME="tex2html164"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html166"
+  HREF="node8.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<!--Table of Child-Links-->
+<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></A>
+
+<UL CLASS="ChildLinks">
+<LI><A NAME="tex2html169"
+  HREF="node7.html#SECTION00071000000000000000">Appendix: How to Apply These Terms to Your New Programs</A>
+</UL>
+<!--End of Table of Child-Links-->
+<HR>
+
+<H1><A NAME="SECTION00070000000000000000">
+A. The GNU General Public License</A>
+</H1>
+
+<P>
+<DIV ALIGN="CENTER">
+</DIV>
+<P>
+<DIV ALIGN="CENTER">Version 2, June 1991
+</DIV>
+<P>
+<DIV ALIGN="CENTER">Copyright &#169; 1989, 1991 Free Software Foundation, Inc.
+</DIV>
+<P>
+<DIV ALIGN="CENTER"></DIV>
+<P><P>
+<BR>
+<DIV ALIGN="CENTER"></DIV>
+<P>
+<DIV ALIGN="CENTER">59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+</DIV>
+<P>
+<DIV ALIGN="CENTER"></DIV>
+<P><P>
+<BR>
+<DIV ALIGN="CENTER"></DIV>
+<P>
+<DIV ALIGN="CENTER">Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+
+</DIV>
+
+<P>
+<DIV ALIGN="CENTER">
+<B><BIG CLASS="LARGE">Preamble</BIG></B>
+
+</DIV>
+
+<P>
+The licenses for most software are designed to take away your freedom to
+share and change it.  By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users.  This General Public
+License applies to most of the Free Software Foundation's software and to
+any other program whose authors commit to using it.  (Some other Free
+Software Foundation software is covered by the GNU Library General Public
+License instead.)  You can apply it to your programs, too.
+
+<P>
+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.
+
+<P>
+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 software, or if you modify it.
+
+<P>
+For example, if you distribute copies of such a program, whether gratis or
+for a fee, you must give the recipients all the rights that you have.  You
+must make sure that they, too, receive or can get the source code.  And
+you must show them these terms so they know their rights.
+
+<P>
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+<P>
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software.  If
+the software is modified by someone else and passed on, we want its
+recipients to know that what they have is not the original, so that any
+problems introduced by others will not reflect on the original authors'
+reputations.
+
+<P>
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary.  To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+<P>
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+<P>
+<DIV ALIGN="CENTER">
+<BIG CLASS="XLARGE">T<SMALL>ERMS AND </SMALL>C<SMALL>ONDITIONS </SMALL>F<SMALL>OR </SMALL>C<SMALL>OPYING, </SMALL>D<SMALL>ISTRIBUTION AND
+  </SMALL>M<SMALL>ODIFICATION</SMALL></BIG>
+
+</DIV>
+
+<P>
+
+<OL>
+<LI><P>
+This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the
+terms of this General Public License.  The ``Program'', below, refers to
+any such program or work, and a ``work based on the Program'' means either
+the Program or any derivative work under copyright law: that is to say, a
+work containing the Program or a portion of it, either verbatim or with
+modifications and/or translated into another language.  (Hereinafter,
+translation is included without limitation in the term ``modification''.)
+Each licensee is addressed as ``you''.
+
+<P>
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+<P>
+</LI>
+<LI>You may copy and distribute verbatim copies of the Program's 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 give any other
+  recipients of the Program a copy of this License along with the Program.
+
+<P>
+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.
+
+<P>
+</LI>
+<LI><P>
+You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+<P>
+
+<OL>
+<LI><P>
+You must cause the modified files to carry prominent notices stating that
+you changed the files and the date of any change.
+
+<P>
+</LI>
+<LI><P>
+You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+<P>
+</LI>
+<LI>If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License.  (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+<P>
+</LI>
+</OL>
+
+<P>
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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.
+
+<P>
+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 Program.
+
+<P>
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+<P>
+</LI>
+<LI>You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+<P>
+
+<OL>
+<LI><P>
+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; or,
+
+<P>
+</LI>
+<LI><P>
+Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+<P>
+</LI>
+<LI><P>
+Accompany it with the information you received as to the offer
+to distribute corresponding source code.  (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+<P>
+</LI>
+</OL>
+
+<P>
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, 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 executable.  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.
+
+<P>
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+<P>
+</LI>
+<LI>You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+<P>
+</LI>
+<LI>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 Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+<P>
+</LI>
+<LI>Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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.
+
+<P>
+</LI>
+<LI>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 Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+<P>
+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.
+
+<P>
+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.
+
+<P>
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+<P>
+</LI>
+<LI>If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+<P>
+</LI>
+<LI>The Free Software Foundation may publish revised and/or new versions
+of the 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.
+
+<P>
+Each version is given a distinguishing version number.  If the Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+<P>
+</LI>
+<LI>If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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.
+
+<P>
+<DIV ALIGN="CENTER">
+<BIG CLASS="XLARGE">
+N<SMALL>O </SMALL>W<SMALL>ARRANTY
+</SMALL></BIG>
+
+</DIV>
+
+<P>
+</LI>
+<LI>B<SMALL>ECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  </SMALL>E<SMALL>XCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM ``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.  </SMALL>T<SMALL>HE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  </SMALL>S<SMALL>HOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.</SMALL>
+
+<P>
+</LI>
+<LI>I<SMALL>N 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.</SMALL>
+
+<P>
+</LI>
+</OL>
+
+<P>
+<DIV ALIGN="CENTER">
+<BIG CLASS="XLARGE">E<SMALL>ND OF </SMALL>T<SMALL>ERMS AND </SMALL>C<SMALL>ONDITIONS</SMALL></BIG>
+
+</DIV>
+
+<P>
+
+<P>
+
+<H2><A NAME="SECTION00071000000000000000">
+Appendix: How to Apply These Terms to Your New Programs</A>
+</H2>
+
+<P>
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+<P>
+To do so, attach the following notices to the program.  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.
+
+<P>
+<BLOCKQUOTE>
+one line to give the program's name and a brief idea of what it does. 
+<BR>
+Copyright (C) yyyy  name of author 
+<BR></BLOCKQUOTE>
+<P>
+<BLOCKQUOTE>This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+</BLOCKQUOTE>
+<P>
+<BLOCKQUOTE>This program 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 General Public License for more details.
+</BLOCKQUOTE>
+<P>
+<BLOCKQUOTE>You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+</BLOCKQUOTE>
+
+<P>
+Also add information on how to contact you by electronic and paper mail.
+
+<P>
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+<P>
+<BLOCKQUOTE>
+Gnomovision version 69, Copyright (C) yyyy  name of author 
+<BR>
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 
+<BR>
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+</BLOCKQUOTE>
+
+<P>
+The hypothetical commands <TT>show w</TT> and <TT>show c</TT> should show the
+appropriate parts of the General Public License.  Of course, the commands
+you use may be called something other than <TT>show w</TT> and <TT>show c</TT>;
+they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+<P>
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a ``copyright disclaimer'' for the program, if
+necessary.  Here is a sample; alter the names:
+
+<P>
+<BLOCKQUOTE>
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program 
+<BR>`Gnomovision' (which makes passes at compilers) written by James Hacker. 
+<BR></BLOCKQUOTE>
+<P>
+<BLOCKQUOTE>signature of Ty Coon, 1 April 1989 
+<BR>
+Ty Coon, President of Vice
+
+</BLOCKQUOTE>
+
+<P>
+This General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications
+with the library.  If this is what you want to do, use the GNU Library
+General Public License instead of this License.
+
+<P>
+
+<DIV CLASS="navigation"><HR>
+<!--Navigation Panel-->
+<A NAME="tex2html167"
+  HREF="node8.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html161"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html155"
+  HREF="node6.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html163"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html165"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Next:</B> <A NAME="tex2html168"
+  HREF="node8.html">Index</A>
+<B> Up:</B> <A NAME="tex2html162"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html156"
+  HREF="node6.html">5 C coding style</A>
+ &nbsp; <B>  <A NAME="tex2html164"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html166"
+  HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/node8.html b/doc/ionnotes/node8.html
new file mode 100644 (file)
index 0000000..0ec574a
--- /dev/null
@@ -0,0 +1,139 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>Index</TITLE>
+<META NAME="description" CONTENT="Index">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="next" HREF="node9.html">
+<LINK REL="previous" HREF="node7.html">
+<LINK REL="up" HREF="ionnotes.html">
+<LINK REL="next" HREF="node9.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<A NAME="tex2html180"
+  HREF="node9.html">
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next.png"></A> 
+<A NAME="tex2html176"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html170"
+  HREF="node7.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html178"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A>  
+<BR>
+<B> Next:</B> <A NAME="tex2html181"
+  HREF="node9.html">About this document ...</A>
+<B> Up:</B> <A NAME="tex2html177"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html171"
+  HREF="node7.html">A. The GNU General</A>
+ &nbsp; <B>  <A NAME="tex2html179"
+  HREF="node1.html">Contents</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+<BR>
+
+<H2><A NAME="SECTION00080000000000000000">
+Index</A>
+</H2><HR><DL>
+<DD><STRONG>call handler</STRONG>
+ : <A HREF="node4.html#491"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG><TT>defer_action</TT></STRONG>
+ : <A HREF="node5.html#621"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>defer_destroy</TT></STRONG>
+ : <A HREF="node5.html#620"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>destroy_obj</TT></STRONG>
+ : <A HREF="node5.html#619"><SPAN CLASS="arabic">4</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>extl_call</TT></STRONG>
+ : <A HREF="node4.html#515"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>extl_call_named</TT></STRONG>
+ : <A HREF="node4.html#516"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>extl_dofile</TT></STRONG>
+ : <A HREF="node4.html#517"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>extl_dostring</TT></STRONG>
+ : <A HREF="node4.html#518"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG><TT>extl_set_safelist</TT></STRONG>
+ : <A HREF="node4.html#519"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">3</SPAN></A>
+<DD><STRONG>ExtlFn</STRONG>
+ : <A HREF="node4.html#522"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>ExtlTab</STRONG>
+ : <A HREF="node4.html#521"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>manager</STRONG>
+ : <A HREF="node2.html#297"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>Obj</STRONG>
+ : <A HREF="node2.html#325"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>parent</STRONG>
+ : <A HREF="node2.html#282"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">2</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG><TT>read_config_for</TT></STRONG>
+ : <A HREF="node4.html#520"><SPAN CLASS="arabic">3</SPAN>.<SPAN CLASS="arabic">4</SPAN></A>
+<DD><STRONG>root window</STRONG>
+ : <A HREF="node2.html#242"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>screen</STRONG><DL>
+<DD><STRONG>physical</STRONG> : <A HREF="node2.html#244"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>X</STRONG> : <A HREF="node2.html#243"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+</DL>
+<DD><STRONG>WClientWin</STRONG>
+ : <A HREF="node2.html#327"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WEdln</STRONG>
+ : <A HREF="node2.html#340"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WFrame</STRONG>
+ : <A HREF="node2.html#331"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WGroup</STRONG>
+ : <A HREF="node2.html#332"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WGroupCW</STRONG>
+ : <A HREF="node2.html#334"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WGroupWS</STRONG>
+ : <A HREF="node2.html#333"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WInput</STRONG>
+ : <A HREF="node2.html#339"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WMessage</STRONG>
+ : <A HREF="node2.html#341"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WObj</STRONG>
+ : <A HREF="node3.html#428"><SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG>WObjDescr</STRONG>
+ : <A HREF="node3.html#430"><SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG>WRegion</STRONG>
+ : <A HREF="node2.html#326"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WRootWin</STRONG>
+ : <A HREF="node2.html#329"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WScreen</STRONG>
+ : <A HREF="node2.html#330"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WSplit</STRONG>
+ : <A HREF="node2.html#337"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WTiling</STRONG>
+ : <A HREF="node2.html#336"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>WWatch</STRONG>
+ : <A HREF="node3.html#431"><SPAN CLASS="arabic">2</SPAN></A>
+<DD><STRONG>WWindow</STRONG>
+ : <A HREF="node2.html#328"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+<DD><STRONG>Xinerama</STRONG>
+ : <A HREF="node2.html#245"><SPAN CLASS="arabic">1</SPAN>.<SPAN CLASS="arabic">1</SPAN></A>
+
+</DL>
+
+<P>
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/node9.html b/doc/ionnotes/node9.html
new file mode 100644 (file)
index 0000000..a4ce07f
--- /dev/null
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+
+<!--Converted with LaTeX2HTML 2002-2-1 (1.71)
+original version by:  Nikos Drakos, CBLU, University of Leeds
+* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
+* with significant contributions from:
+  Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
+<HTML>
+<HEAD>
+<TITLE>About this document ...</TITLE>
+<META NAME="description" CONTENT="About this document ...">
+<META NAME="keywords" CONTENT="ionnotes">
+<META NAME="resource-type" CONTENT="document">
+<META NAME="distribution" CONTENT="global">
+
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META NAME="Generator" CONTENT="LaTeX2HTML v2002-2-1">
+<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+
+<LINK REL="STYLESHEET" HREF="ionnotes.css">
+
+<LINK REL="previous" HREF="node8.html">
+<LINK REL="up" HREF="ionnotes.html">
+</HEAD>
+
+<BODY >
+
+<DIV CLASS="navigation"><!--Navigation Panel-->
+<IMG WIDTH="37" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="next" SRC="next_g.png"> 
+<A NAME="tex2html186"
+  HREF="ionnotes.html">
+<IMG WIDTH="26" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="up" SRC="up.png"></A> 
+<A NAME="tex2html182"
+  HREF="node8.html">
+<IMG WIDTH="63" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="previous" SRC="prev.png"></A> 
+<A NAME="tex2html188"
+  HREF="node1.html">
+<IMG WIDTH="65" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="contents" SRC="contents.png"></A> 
+<A NAME="tex2html190"
+  HREF="node8.html">
+<IMG WIDTH="43" HEIGHT="24" ALIGN="BOTTOM" BORDER="0" ALT="index" SRC="index.png"></A> 
+<BR>
+<B> Up:</B> <A NAME="tex2html187"
+  HREF="ionnotes.html">Ion: Notes for the</A>
+<B> Previous:</B> <A NAME="tex2html183"
+  HREF="node8.html">Index</A>
+ &nbsp; <B>  <A NAME="tex2html189"
+  HREF="node1.html">Contents</A></B> 
+ &nbsp; <B>  <A NAME="tex2html191"
+  HREF="node8.html">Index</A></B> 
+<BR>
+<BR></DIV>
+<!--End of Navigation Panel-->
+
+<H1><A NAME="SECTION00090000000000000000">
+About this document ...</A>
+</H1>
+ <STRONG>Ion: Notes for the module and patch writer</STRONG><P>
+This document was generated using the
+<A HREF="http://www.latex2html.org/"><STRONG>LaTeX</STRONG>2<tt>HTML</tt></A> translator Version 2002-2-1 (1.71)
+<P>
+Copyright &#169; 1993, 1994, 1995, 1996,
+Nikos Drakos, 
+Computer Based Learning Unit, University of Leeds.
+<BR>
+Copyright &#169; 1997, 1998, 1999,
+<A HREF="http://www.maths.mq.edu.au/~ross/">Ross Moore</A>, 
+Mathematics Department, Macquarie University, Sydney.
+<P>
+The command line arguments were: <BR>
+ <STRONG>latex2html</STRONG> <TT>-show_section_numbers -short_index -local_icons -noaddress -up_url http://iki.fi/tuomov/ion/ -up_title 'Ion homepage' -nofootnode -split 4 ionnotes</TT>
+<P>
+The translation was initiated by tuomov on 2006-12-23
+<BR><HR>
+
+</BODY>
+</HTML>
diff --git a/doc/ionnotes/prev.png b/doc/ionnotes/prev.png
new file mode 100644 (file)
index 0000000..e60b8b4
Binary files /dev/null and b/doc/ionnotes/prev.png differ
diff --git a/doc/ionnotes/prev_g.png b/doc/ionnotes/prev_g.png
new file mode 100644 (file)
index 0000000..476d956
Binary files /dev/null and b/doc/ionnotes/prev_g.png differ
diff --git a/doc/ionnotes/up.png b/doc/ionnotes/up.png
new file mode 100644 (file)
index 0000000..3937e16
Binary files /dev/null and b/doc/ionnotes/up.png differ
diff --git a/doc/luaif.tex b/doc/luaif.tex
new file mode 100644 (file)
index 0000000..76789a7
--- /dev/null
@@ -0,0 +1,148 @@
+
+\section{The Lua interface}
+
+This section finally describes the implementation details and how modules
+should us the Lua interface. First, in section \ref{sec:supptypes}
+we look at types supported by the interface, how objects are passed to Lua
+code and how Lua tables should be accessed from Ion and modules. In section
+\ref{sec:exporting} the methods for exporting functions and how they
+are called from Lua are explained and in section \ref{sec:calling} the
+method for calling Lua functions is explained.
+
+\subsection{Supported types}
+\label{sec:supptypes}
+
+The following types are supported in passing parameters between the
+C side of Ion and Lua:
+
+\begin{tabular}{rll}
+\hline
+ Identifier
+ character &   C type &        Description\\
+\hline
+       \spec{i} & \type{int} & Integer\\
+       \spec{s} & \type{char*} &       String\\
+       \spec{S} & \type{const char*} & Constant string\\
+       \spec{d} & \type{double} &\\
+       \spec{b} & \type{bool} &\\
+       \spec{t} & \type{ExtlTab}\indextype{ExtlTab} &
+                       Reference to Lua table\\
+       \spec{f} & \type{ExltFn}\indextype{ExtlFn} &
+                       Reference to Lua function.\\
+       \spec{o} & Any \type{WObj*} &\\
+\end{tabular}
+
+The difference between identifiers '\spec{s}' and '\spec{S}' is that constant
+strings as return values are not free'd by the level 1 call handler
+(see below) after passing to Lua (\code{lua_pushstring} always makes a
+copy) unlike normal strings. String parameters are always assumed to be
+the property of the caller and thus implicitly const.
+
+Likewise, if a reference to '\spec{t}' or '\spec{f}' is wished to be stored
+beyond the lifetime of a function receiving such as an argument, a new
+reference should be created with \code{extl_ref_table}/\code{fn}.
+References can be free'd with
+\code{extl_unref_table}/\code{fn}. References gotten as return values with
+the \code{extl_table_get} (how these work should be self-explanatory!)
+functions are property of the caller and should be unreferenced with the
+above-mentioned functions when no longer needed.
+The functions \code{extl_fn}/\code{table_none()}
+return the equivalent of NULL.
+
+\type{WObj}s are passed to Lua code with WWatch userdatas pointing to
+them so the objects can be safely deleted although Lua code might still be
+referencing them. (This is why SWIG or tolua would not have helped in
+creating the interface: extra wrappers for each function would still
+have been needed to nicely integrate into Ion's object system. Even in
+the case that Ion was written in C++ this would be so unless extra bloat
+adding pointer-like objects were used everywhere instead of pointers.)
+It may be sometimes necessary check in Lua code that a value known to
+be an Ion \type{WObj} is of certain type. This can be accomplished with
+\code{obj_is(obj, "typename")}. \code{obj_typename(obj)} returns type
+name for a \type{WObj}.
+
+
+\subsection{Exporting functions}
+\label{sec:exporting}
+
+Exported functions (those available to the extension language) are
+defined by placing \code{EXTL_EXPORT} before the function implementation
+in the C source. The script mkexports.pl is then used to automatically
+generate \file{exports.c} from the source files if
+\code{MAKE_EXPORTS=modulename}
+is specified in the Makefile. All pointers with type beginning with a 'W'
+are assumed to be pointers to something inheriting \type{WObj}. In
+addition to a table of exported functions and second level call handlers
+for these, \file{exports.c} will contain two functions
+\code{module_register_exports()} and
+\code{module_unregister_exports()} that should then be called in module
+initialisation and deinitialisation code.
+
+You've seen the terms level 1 and 2 call handler mentioned above. 
+\index{call handler}
+The Lua support code uses two so called call handlers to convert and check
+the types of parameters passed from Lua to C and back to Lua. The first
+one of these call handlers is the same for all exported functions and
+indeed lua sees all exported as the same C function (the L1 call handler)
+but with different upvalues passing a structure describing the actual
+function and the second level call handler. The L1 call handler checks
+that the parameters received from Lua match a template given as a string
+of the identifier characters defined above. If everything checks out ok,
+the parameters are then put in an array of C unions that can contain
+anyof these known types and the L2 call handler is called.
+
+The L2 call handler (which is automatically generated by the mkexports.pl
+script) for each exported function checks that the passed \type{WObj}s
+are of the more refined type required by the function and then calls the
+actual function. While the WObj checking could be done in the L1 handler
+too, the L2 call handlers are needed because we may not know how the target
+platform passes each parameter type to the called function. Thefore we
+must let the C compiler generate the code to convert from a simple and
+known enough parameter passing method (the unions) to the actual
+parameter passing method. When the called function returns everything
+is done in reverse order for return values (only one return value is
+supported by the generated L2 call handlers).
+
+
+\subsection{Calling Lua functions and code}
+\label{sec:calling}
+
+The functions
+\code{extl_call}\index{extl-call@\code{extl_call}},
+\code{extl_call_named}\index{extl-call-named@\code{extl_call_named}},
+\code{extl_dofile}\index{extl-dofile@\code{extl_dofile}} and
+\code{extl_dostring}\index{extl-dostring@\code{extl_dostring}}
+call a referenced function (\type{ExtlFn}), named function, execute a
+string and a file, respectively. The rest of the parameters for all these
+functions are similar. The 'spec' argument is a string of identifier
+characters (see above) describing the parameters to be passed. These
+parameters follow after 'rspec'. For dofile and dostring these parameters
+are passed in the global table arg (same as used for program command
+lien parameters) and for functions as you might expect. The parameter
+'rspec' is a similar description of return values. Pointers to variables
+that should be set to the return values follow after the input values.
+The return value of all these functions tells if the call and parameter
+passing succeeded or not.
+
+Sometimes it is necessary to block calls to all but a limited set of
+Ion functions. This can be accomplished with
+\code{extl_set_safelist}\index{extl-set-safelist@\code{extl_set_safelist}}.
+The parameter to this function is a NULL-terminated array of strings
+and the return value is a similar old safelist.
+The call \code{extl_set_safelist(NULL)} removes any safelist and allows
+calls to all exported functions.
+
+
+\subsection{Miscellaneous notes}
+
+Configuration files should be read as before with the function
+\code{read_config_for}\index{read-config-for@\code{read_config_for}}
+except that the list of known options is no longer present.
+
+Winprops are now stored in Lua tables and can contain arbitrary
+properties. The 'proptab' entry in each \type{WClientWin} is a reference
+to a winprop table or \code{extl_table_none()} if such does not exist
+and properties may be read with the \code{extl_table_gets} functions.
+(It is perfectly legal to pass \code{extl_table_none()} references to
+\code{extl_table_get*}.)
+
diff --git a/doc/macros.tex b/doc/macros.tex
new file mode 100644 (file)
index 0000000..0e172d3
--- /dev/null
@@ -0,0 +1,151 @@
+\usepackage{babel}
+\usepackage[latin1]{inputenc}
+\usepackage[dvipdfm]{hyperref}
+\usepackage{ae}
+\usepackage{url}
+\usepackage{html}
+%\usepackage{graphicx}
+%\usepackage{color}
+\usepackage{makeidx}
+\usepackage{tabularx}
+\usepackage{textcomp}
+\usepackage[nottoc]{tocbibind}
+\usepackage{enumerate} % GNU FDL needs this
+\usepackage[a4paper]{geometry}
+\usepackage{calc}
+
+% URL settings
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\urlstyle{rm}
+\newcommand{\centerurl}[1]{\begin{center}\url{#1}\end{center}}
+
+% listings package
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%begin{latexonly}
+\usepackage{listings}
+
+\lstset{
+  extendedchars=true,
+  breaklines=true,
+  basicstyle=\tt,
+  alsoletter={",},
+  alsoother={\_},
+}
+%end{latexonly}
+
+% Some markup
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\newcommand{\note}[1]{\color{red}**#1**}
+\newcommand{\file}[1]{\emph{#1}}
+\newcommand{\type}[1]{#1}
+\newcommand{\spec}[1]{#1}
+\newcommand{\indextype}[1]{\index{#1@\type{#1}}}
+
+%begin{latexonly}
+\newcommand{\key}[1]{\mbox{\textbf{#1}}}
+\newcommand{\code}[1]{\lstinline!#1!}
+\newcommand{\var}[1]{\lstinline!#1!}
+\newcommand{\command}[1]{\lstinline!#1!}
+
+\newcommand{\hyperlabel}[1]{\hypertarget{#1}{}\label{#1}}
+\newcommand{\fnrefx}[2]{\hyperlink{fn:#1.#2}{\code{#2}}}
+\newcommand{\fnref}[1]{\hyperlink{fn:#1}{\code{#1}}}
+\newcommand{\myhref}[2]{\hyperlink{#1}{#2}}
+
+%end{latexonly}
+\begin{htmlonly}
+    
+\newcommand{\key}[1]{\textbf{#1}}
+\newcommand{\code}[1]{\texttt{#1}}
+\newcommand{\var}[1]{\texttt{#1}}
+\newcommand{\command}[1]{\texttt{#1}}
+
+\newcommand{\hyperlabel}[1]{\label{#1}}
+\newcommand{\fnref}[1]{\htmlref{\texttt{#1}}{fn:#1}}
+\newcommand{\fnrefx}[2]{\htmlref{\texttt{#2}}{fn:#1.#2}}
+\newcommand{\myhref}[2]{\htmlref{#2}{#1}}
+
+\end{htmlonly}
+
+\newcommand{\tabhead}[1]{\hline #1 \\ \hline}
+
+% function tables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%begin{latexonly}
+
+\newcommand{\funclistlabel}[1]{#1\hfill}
+\newenvironment{function}{
+  \pagebreak[3]
+  \begin{list}{}{
+        \settowidth{\labelwidth}{Description:}
+        \setlength{\leftmargin}{\labelwidth}
+        \addtolength{\leftmargin}{0.5em}
+        \setlength{\labelsep}{0.5em}
+        \setlength{\itemsep}{0pt}
+        \setlength\parsep{0pt}
+        \setlength\topsep{0pt}
+        %\setlength{\itemsep}{-\parskip}
+        %\addtolength{\itemsep}{\lineskip}
+        \let\makelabel\funclistlabel
+  }
+}{
+  \end{list}
+}
+
+%end{latexonly}
+\begin{htmlonly}
+
+\newenvironment{function}{
+  \begin{description}
+}{
+  \end{description}
+}
+
+\end{htmlonly}
+
+
+\newcommand{\synopsis}[1]{
+  \item[Synopsis:] \code{#1}
+}
+\newcommand{\funcname}[1]{
+  \item[Function:] \code{#1}
+}
+\newcommand{\hookname}[1]{
+  \item[Hook name:] \hyperlabel{#1}\code{#1}
+}
+\newcommand{\hookparams}[1]{
+  \item[Parameters:] \code{#1}
+}
+\newenvironment{funcdesc}{
+  \item[Description:]
+}{}
+
+
+% While rapport3/artikel3 are otherwise nice classes, 
+% itemize looks awful.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%begin{latexonly}
+\makeatletter
+\renewenvironment{itemize}{%
+  \ifnum \@itemdepth >3
+    \@toodeep
+  \else
+    \advance\@itemdepth \@ne
+    \edef\@itemitem{labelitem\romannumeral\the\@itemdepth}%
+    \list{\csname\@itemitem\endcsname}%
+         {%
+            \if@revlabel
+              \def\makelabel##1{\hskip .5\unitindent{\hfil ##1}}\else
+                                                   %^^^^^^^^^ Changed
+              \def\makelabel##1{\hfil ##1}
+                              %^^^^^^^^^ Changed
+            \fi
+          }%
+  \fi}
+ {\global\@ignoretrue \endlist}
+\makeatletter
+%end{latexonly}
diff --git a/doc/objects.tex b/doc/objects.tex
new file mode 100644 (file)
index 0000000..e751eab
--- /dev/null
@@ -0,0 +1,246 @@
+
+\section{Class and object hierarchies}
+\label{sec:objects}
+
+While Ion does not not have a truly object-oriented design
+\footnote{the author doesn't like such artificial designs},
+things that appear on the computer screen are, however, quite
+naturally expressed as such ''objects''. Therefore Ion implements
+a rather primitive OO system for these screen objects and some
+other things. 
+
+It is essential for the module writer to learn this object
+system, but also people who write their own binding configuration files
+necessarily come into contact with the class and object hierarchies
+-- you need to know which binding setup routines apply where, 
+and what functions can be used as handlers in which bindings.
+It is the purpose of this section to attempt to explain these 
+hierarchies. If you do not wish the read the full section, at least
+read the summary at the end of it, so that you understand the very
+basic relations.
+
+For simplicity we consider only the essential-for-basic-configuration
+Ioncore, \file{mod\_tiling} and \file{mod\_query} classes. 
+See Appendix \ref{app:fullhierarchy} for the full class hierachy visible
+to Lua side.
+
+\subsection{Class hierarchy}
+
+One of the most important principles of object-oriented design methodology
+is inheritance; roughly how classes (objects are instances of classes)
+extend on others' features. Inheritance gives rise to class hierarchy.
+In the case of single-inheritance this hierarchy can be expressed as a
+tree where the class at the root is inherited by all others below it
+and so on. Figure \ref{fig:classhierarchy} lists out the Ion class 
+hierarchy and below we explain what features of Ion the classes 
+implement.
+
+\begin{figure}
+\begin{htmlonly}
+\docode % latex2html kludge
+\end{htmlonly}
+\begin{verbatim}
+    Obj
+     |-->WRegion
+     |    |-->WClientWin
+     |    |-->WWindow
+     |    |    |-->WRootWin
+     |    |    |-->WMPlex
+     |    |    |    |-->WScreen
+     |    |    |    |-->WFrame
+     |    |    |-->WInput (mod_query)
+     |    |         |-->WEdln (mod_query)
+     |    |         |-->WMessage (mod_query)
+     |    |-->WGroup
+     |    |    |-->WGroupWS
+     |    |    |-->WGroupCW
+     |    |-->WTiling (mod_tiling)
+     |-->WSplit (mod_tiling)
+\end{verbatim}
+\caption{Partial Ioncore, \file{mod\_tiling} and \file{mod\_query} 
+    class hierarchy.}
+\label{fig:classhierarchy}
+\end{figure}
+
+The core classes:
+
+\begin{description}
+  \item[\type{Obj}]\indextype{Obj}
+    Is the base of Ion's object system.
+
+  \item[\type{WRegion}]\indextype{WRegion}
+    is the base class for everything corresponding to something on the
+    screen. Each object of type \type{WRegion} has a size and  position
+    relative to the parent \type{WRegion}. While a big part of Ion 
+    operates on these instead of more specialised classes, \type{WRegion}
+    is a ''virtual''  base class in that there are no objects of ''pure''
+    type \type{WRegion}; all concrete regions are objects of some class 
+    that inherits \type{WRegion}.
+
+  \item[\type{WClientWin}]\indextype{WClientWin} is a class for
+    client window objects, the objects that window managers are
+    supposed to manage.
+
+  \item[\type{WWindow}]\indextype{WWindow} is the base class for all
+    internal objects having an X window associated to them
+    (\type{WClientWins} also have X windows associated to them).
+    
+  \item[\type{WRootWin}]\indextype{WRootWin} is the class for
+    root windows\index{root window} of X screens\index{screen!X}.
+    Note that an ''X screen'' or root window is not necessarily a
+    single  physical screen\index{screen!physical} as a root window
+    may be split over multiple screens when multi-head extensions 
+    such as Xinerama\index{Xinerama} are used. (Actually there
+    can be only one \type{WRootWin} when Xinerama is used.)
+       
+  \item[\type{WMPlex}] is a base class for all regions that''multiplex'' 
+    other regions. This means that of the regions managed by the multiplexer,
+    only one can be displayed at a time. Classes that inhereit \type{WMPlex}
+    include screens and frames.
+    
+  \item[\type{WScreen}]\indextype{WScreen} is the class for objects
+    corresponding to physical screens. Screens may share a root
+    window when Xinerama multihead extensions are used as explained
+    above.
+
+  \item[\type{WFrame}]\indextype{WFrame} is the class for frames.
+    While most Ion's objects have no graphical presentation, frames basically
+    add to \type{WMPlex}es the decorations around client windows 
+    (borders, tabs).
+    
+  \item[\type{WGroup}]\indextype{WGroup} is the base class for groups.
+    Particular types of groups are workspaces 
+    (\type{WGroupWS}\indextype{WGroupWS})
+    and groups of client windows
+    (\type{WGroupCW}\indextype{WGroupCW}).
+\end{description}
+
+
+Classes implemented by the \file{mod\_tiling} module:
+
+\begin{description}
+  \item[\type{WTiling}]\indextype{WTiling} is the class for tilings
+    of frames.
+  \item[\type{WSplit}]\indextype{WSplit} (or, more specifically, classes
+    that inherit it) encode the \type{WTiling} tree structure.
+\end{description}
+
+
+Classes implemented by the \file{mod\_query} module:
+
+\begin{description}
+  \item[\type{WInput}]\indextype{WInput} is a virtual base class for the
+    two classes below.
+  \item[\type{WEdln}]\indextype{WEdln} is the class for the ''queries'',
+    the text inputs that usually appear at bottoms of frames and sometimes
+    screens. Queries are the functional equivalent of ''mini buffers'' in
+    many text editors.
+  \item[\type{WMessage}]\indextype{WMessage} implements the boxes for 
+    warning and other messages that Ion may wish to display to the user. 
+    These also usually appear at bottoms of frames.
+\end{description}
+
+There are also some other ''proxy'' classes that do not refer
+to objects on the screen. The only important one of these for
+basic configuration is \type{WMoveresMode} that is used for
+binding callbacks in the move and resize mode.
+
+
+\subsection{Object hierarchies: \type{WRegion} parents and managers}
+
+\subsubsection{Parent--child relations}
+Each object of type \type{WRegion} has a parent and possibly a manager
+associated to it. The parent\index{parent} for an object is always a 
+\type{WWindow} and for \type{WRegion} with an X window (\type{WClientWin},
+\type{WWindow}) the parent \type{WWindow} is given by the same relation of
+the X windows. For other \type{WRegion}s the relation is not as clear.
+There is generally very few restrictions other than the above on the
+parent---child relation but the most common is as described in
+Figure \ref{fig:parentship}.
+
+\begin{figure}
+\begin{htmlonly}
+\docode % latex2html kludge
+\end{htmlonly}
+\begin{verbatim}
+    WRootWins
+     |-->WScreens
+          |-->WGroupWSs
+          |-->WTilings
+          |-->WClientWins in full screen mode
+          |-->WFrames
+               |-->WGroupCWs
+               |-->WClientWins
+               |-->WFrames for transients
+               |-->a possible WEdln or WMessage
+\end{verbatim}
+\caption{Most common parent--child relations}
+\label{fig:parentship}
+\end{figure}
+
+\type{WRegion}s have very little control over their children as a parent.
+The manager\index{manager} \type{WRegion} has much more control over its
+managed \type{WRegion}s. Managers, for example, handle resize requests,
+focusing and displaying of the managed regions. Indeed the manager---managed
+relationship gives a better picture of the logical ordering of objects on
+the screen. Again, there are generally few limits, but the most common
+hierarchy is given in Figure \ref{fig:managership}. Note that sometimes
+the parent and manager are the same object and not all objects may have
+a manager (e.g. the dock in the dock module at the time of writing this)
+but all have a parent--a screen if not anything else.
+
+\subsubsection{Manager--managed relations}
+
+\begin{figure}
+\begin{htmlonly}
+\docode % latex2html kludge
+\end{htmlonly}
+\begin{verbatim}
+    WRootWins
+     |-->WScreens
+          |-->WGroupCWs for full screen WClientWins
+          |    |-->WClientWins
+          |    |-->WFrames for transients (dialogs)
+          |         |--> WClientWin
+          |-->WGroupWSs for workspaces
+          |    |-->WTiling
+          |    |    |-->possibly a WEdln, WMessage or WMenu
+          |    |    |-->WFrames
+          |    |         |-->WGroupCWs (with contents as above)
+          |    |-->WFrames for floating content
+          |-->WFrames for sticky stuff, such as the scratchpad
+\end{verbatim}
+\caption{Most common manager--managed relations}
+\label{fig:managership}
+\end{figure}
+
+Note that a workspace can manage another workspace. This can be
+achieved with the \fnref{attach_new} function, and allows you to nest
+workspaces as deep as you want.
+
+%Note how the \type{WClientWin}s managed by \type{WFloatFrame}s don't have
+%transients managed by them. This is because WFloatWSs choose to handle
+%transients differently (transients are put in separate frames like normal
+%windows).
+
+\subsection{Summary}
+
+In the standard setup, keeping queries, messages and menus out of
+consideration:
+
+\begin{itemize}
+  \item The top-level objects that matter are screens and they correspond
+    to physical screens. The class for screens is \type{WScreen}.
+  \item Screens contain (multiplex) groups (\type{WGroup}) and other 
+    objects, such as \type{WFrames}. Some of these are mutually exclusive
+    to be viewed at a time.
+  \item Groups of the specific kind \type{WGroupWS} often contain a
+    \type{WTiling} tiling for tiling frames (\type{WFrame}), but 
+    groups may also directly contain floating frames.
+  \item Frames are the objects with decorations such as tabs and borders.
+    Frames contain (multiplex) among others (groups of) client windows, 
+    to each of which corresponds a tab in the frame's decoration. Only 
+    one client window (or other object) can be shown at a time in each 
+    frame. The class for client windows is \type{WClientWin}.
+\end{itemize}
+
diff --git a/doc/objectsimpl.tex b/doc/objectsimpl.tex
new file mode 100644 (file)
index 0000000..ccc440d
--- /dev/null
@@ -0,0 +1,20 @@
+\section{Object system implementation}
+
+First, to get things clear, what are considered objects here are C
+structures containing a properly initialized \type\indextype{WObj}
+structure defined in \file{ioncore/obj.h} as the first element (or the
+first element of the structure which is the first element and so on which
+gives rise to inheritance). The \type{WObj} structure contains a pointer
+to a \type{WObjDescr}\indextype{WObjDescr} class type info structure and
+a list of so called ''watches''. The \type{WObjDescr} structure simply
+lists the class name, a table of dynamic functions and a pointer to
+deinitialisation function (or ''destructor'').
+
+Ion does not do any reference counting, garbage collecting or other
+fancy things related to automatic safe freeing of objects with its
+simplistic object system. Instead special watches (the \type{WWatch}
+\indextype{WWatch} structure) may be used to create safe references to
+objects that might be destroyed during the time the specific pointer is
+needed. When an object is destroyed, its list of watches is processed,
+setting the pointers in the watches to NULL and the watch handlers for
+each watch are called. 
diff --git a/doc/predist.sh b/doc/predist.sh
new file mode 100644 (file)
index 0000000..31e7405
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+##
+## Versioning
+##
+
+pwd=`pwd`
+dir=`basename "$pwd"`
+
+release=`echo "$dir"|sed 's/^.\+-\([^-]\+-[0-9]\+\)$/\1/p; d'`
+
+if test "$release" == ""; then
+    echo "Invalid package name $dir."
+    exit 1
+fi
+
+##
+## Ion path
+##
+
+if test "$ION_PATH" = ""; then
+    ION_PATH="../ion-${release}"
+fi
+
+##
+## Build
+##
+
+set -e
+
+d=`echo $release|sed 's/[^-]\+-\(....\)\(..\)\(..\)/\1-\2-\3/'`
+
+perl -p -i -e "s/%%DATE/\\\\date{$d}/" ionconf.tex
+sed "s:^TOPDIR=.*:TOPDIR=${ION_PATH}:" Makefile > Makefile.tmp
+make -f Makefile.tmp all
+make -f Makefile.tmp all-ps
+make -f Makefile.tmp clean
+rm Makefile.tmp
+gzip *.dvi *.ps
diff --git a/doc/prelim.tex b/doc/prelim.tex
new file mode 100644 (file)
index 0000000..c5db5f8
--- /dev/null
@@ -0,0 +1,58 @@
+
+\chapter{Preliminaries: Key concepts and relations}
+\label{chap:prelim}
+
+The purpose of this chapter to explain some of key concepts and
+relations you need to understand before reading the following
+chapters. These include modules explained in section \ref{sec:modules}
+and the Ion class and object hierarchies, section \ref{sec:objects}.
+
+
+\section{Modules}
+\label{sec:modules}
+
+Ion has been designed so that the 'ion' executable only implements some
+basic services on top of which very different kinds of window managers
+could be build by loading the appropriate 'modules'. On modern system
+these modules are simply dynamically loaded \file{.so} libraries. On 
+more primitive systems, or if you want to squeeze total size of the 
+executable and libraries, the modules can optionally be statically 
+linked to the main binary, but must nevertheless be loaded with the
+\fnref{dopath} function. Modules may also include Lua code.
+
+If no modules are loaded, all client windows appear in full screen mode.
+To get better window management support, one or more workspace modules
+should be loaded. Currently Ion provides the following modules:
+
+\begin{description}
+    \item[\file{mod\_tiling}] Tilings for workspaces of the original tiled
+       Ion kind.
+    \item[\file{mod\_query}] Queries (for starting programs and so on)
+      and message boxes.
+    \item[\file{mod\_menu}] Support for menus, both pull-down and
+      keyboard-operated in-frame menus.
+    \item[\file{mod\_statusbar}] Module that implements a statusbar that
+      can be adaptively embedded in each workspace's layout.
+    \item[\file{mod\_dock}] Module for docking Window Maker dockapps.
+      The dock can both float and be embedded as the statusbar.
+    \item[\file{mod\_sp}] This module implements a scratchpad frame that can
+      be toggled on/off everywhere. Think of the 'console' in some 
+      first-person shooters.
+    \item[\file{mod\_mgmtmode}] Support module for implementing ''management
+      modes'' with a XOR-frame similar to move/resize mode around selected
+      region.
+    \item[\file{mod\_sm}] Session management support module.
+      \emph{Loaded automatically when needed!}
+\end{description}
+
+So-called drawing engines are also implemented as a modules,
+but they are not discussed here; see chapter \ref{chap:gr}.
+
+The stock configuration for the 'ion3' executable loads all of the modules
+mentioned above except \file{mod\_dock} and file{mod\_mgmtmode}.
+The stock configuration for the 'pwm3' executable (which differs from the 
+'ion3' executable in a few configuration details, such as Xinerama usage)
+loads another set of modules.
+
+
+\input{objects}
diff --git a/doc/rapport3.perl b/doc/rapport3.perl
new file mode 100644 (file)
index 0000000..77b2cbd
--- /dev/null
@@ -0,0 +1,38 @@
+# rapport3.perl by Tuomo Valkonen, <tuomov at iki.fi>, 2003-05-10
+#
+# Implementation of the documentclass for latex2html. Just make some
+# sectioning commands saner and load report.
+#
+
+package main;
+
+#
+# Sections should start at H2 instead of the insane default H1.
+#
+
+%standard_section_headings =
+  ('part' , 'H1' , 'chapter' , 'H1', 'section', 'H2', 'subsection', 'H3'
+      , 'subsubsection', 'H4', 'paragraph', 'H4', 'subparagraph', 'H5');
+
+&generate_sectioning_subs;
+
+%section_headings =
+  ('partstar' , 'H1' , 'chapterstar' , 'H1', 'sectionstar', 'H2'
+      , 'subsectionstar', 'H3', 'subsubsectionstar', 'H4', 'paragraphstar'
+      , 'H4', 'subparagraphstar', 'H5', %section_headings);
+
+#
+# These should be chapters in a report
+#
+
+%section_headings =
+  ('tableofcontents', 'H1', 'listoffigures', 'H1', 'listoftables', 'H1'
+      , 'bibliography', 'H1', 'textohtmlindex', 'H1'
+      , %standard_section_headings
+      , %section_headings);
+
+
+&do_require_package("report");
+
+1;
+
diff --git a/doc/statusd.tex b/doc/statusd.tex
new file mode 100644 (file)
index 0000000..e1fe6b2
--- /dev/null
@@ -0,0 +1,81 @@
+
+\section{Writing \command{ion-statusd} monitors}
+
+All statusbar meters that do not monitor the internal state of Ion should
+go in the separate \command{ion-statusd} program. 
+
+Whenever the user requests a meter \code{\%foo} or \code{\%foo_bar} to be 
+inserted in a statusbar, \file{mod\_statusbar} asks \command{ion-statusd} to
+load \fnref{statusd_foo.lua} on its search path (same as that for Ion-side 
+scripts). This script should then supply all meters with the initial part
+'\code{foo}'.
+
+To provide this value, the script should simply call \code{statusd.inform}
+with the name of the meter and the value as a string.
+Additionally the script should provide a 'template' for the meter to
+facilitate expected width calculation by \file{mod\_statusbar}, and
+may provide a 'hint' for colour-coding the value. The interpretation
+of hints depends on the graphical style in use, and currently the
+stock styles support the \code{normal}, \code{important} and 
+\code{critical} hints.
+
+
+In our example of the 'foo monitor', at script init we might broadcast
+the template as follows:
+
+\begin{verbatim}
+statusd.inform("foo_template", "000")
+\end{verbatim}
+
+To inform \file{mod\_statusbar} of the actual value of the meter and
+indicate that the value is critical if above 100, we might write the
+following function:
+
+\begin{verbatim}
+local function inform_foo(foo)
+    statusd.inform("foo", tostring(foo))
+    if foo>100 then
+        statusd.inform("foo_hint", "critical")
+    else
+        statusd.inform("foo_hint", "normal")
+    end
+end    
+\end{verbatim}
+    
+To periodically update the value of the meter, we must use timers.
+First we must create one:
+
+\begin{verbatim}
+local foo_timer=statusd.create_timer()
+\end{verbatim}
+
+Then we write a function to be called whenever the timer expires.
+This function must also restart the timer.
+
+\begin{verbatim}
+local function update_foo()
+    local foo= ... measure foo somehow ...
+    inform_foo(foo)
+    foo_timer:set(settings.update_interval, update_foo)
+end
+\end{verbatim}
+
+Finally, at the end of our script we want to do the initial
+measurement, and set up timer for further measurements:
+
+\begin{verbatim}
+update_foo()
+\end{verbatim}
+
+
+If our scripts supports configurable parameters, the following code
+(at the beginning of the script) will allow them to be configured in
+\file{cfg\_statusbar.lua} and passed to the status daemon and our script:
+
+\begin{verbatim}
+local defaults={
+    update_interval=10*1000, -- 10 seconds
+}
+                
+local settings=table.join(statusd.get_config("foo"), defaults)
+\end{verbatim}
diff --git a/doc/tricks.tex b/doc/tricks.tex
new file mode 100644 (file)
index 0000000..69fe945
--- /dev/null
@@ -0,0 +1,104 @@
+
+\chapter{Scripting}
+\label{chap:tricks}
+
+This chapter documents some additional features of the Ion configuration
+and scripting interface that can be used for more advanced scripting than
+the basic configuration exlained in chapter \ref{chap:config}.
+
+\section{Hooks}
+\label{sec:hooks}
+
+Hooks are lists of functions to be called when a certain event occurs.
+There are two types of them; normal and ''alternative'' hooks. Normal
+hooks do not return anything, but alt-hooks should return a boolean
+indicating whether it handled its assigned task succesfully. In the case
+that \var{true} is returned, remaining handlers are not called.
+
+Hook handlers are registered by first finding the hook
+with \fnref{ioncore.get_hook} and then calling \fnref{WHook.add}
+on the (succesfull) result with the handler as parameter. Similarly
+handlers are unregistered with \fnref{WHook.remove}. For example:
+
+\begin{verbatim}
+ioncore.get_hook("ioncore_snapshot_hook"):add(
+    function() print("Snapshot hook called.") end
+)
+\end{verbatim}
+
+In this example the hook handler has no parameters, but many hook
+handlers do. The types of parameters for each hook are listed in
+the hook reference, section \ref{sec:hookref}.
+
+
+Note that many of the hooks are called in ''protected mode'' and can not 
+use any functions that modify Ion's internal state. TODO: More detailed 
+documentation when this is final.
+
+\section{Referring to regions}
+
+\subsection{Direct object references}
+
+All Ion objects are passed to Lua scriptss as 'userdatas', and you may
+safely store such object references for future use. The C-side object
+may be destroyed while Lua still refers to the object. All exported
+functions gracefully fail in such a case, but if you need to explicitly
+test that the C-side object still exists, use \fnref{obj_exists}.
+
+As an example, the following short piece of code implements 
+bookmarking:
+
+\begin{verbatim}
+local bookmarks={}
+
+-- Set bookmark bm point to the region reg
+function set_bookmark(bm, reg)
+    bookmarks[bm]=reg
+end
+
+-- Go to bookmark bm
+function goto_bookmark(bm)
+    if bookmarks[bm] then
+        -- We could check that bookmarks[bm] still exists, if we
+        -- wanted to avoid an error message.
+        bookmarks[bm]:goto()
+    end
+end
+\end{verbatim}
+
+\subsection{Name-based lookups}
+
+If you want to a single non-\type{WClientWin} region with an exact known 
+name, use \fnref{ioncore.lookup_region}. If you want a list of all regions,
+use \fnref{ioncore.region_list}. Both functions accept an optional argument
+that can be used to specify that the returned region(s) must be of a more 
+specific type. Client windows live in a different namespace and for them
+you should use the equivalent functions \fnref{ioncore.lookup_clientwin}
+and \fnref{ioncore.clientwin_list}.
+
+To get the name of an object, use \fnref{WRegion.name}. Please be
+aware, that the names of client windows reflect their titles and
+are subject to changes. To change the name of a non-client window
+region, use \fnref{WRegion.set_name}.
+
+
+\section{Alternative winprop selection criteria}
+
+It is possible to write more complex winprop selection routines than
+those described in section \ref{sec:winprops}. To match a particular
+winprop using whatever way you want to, just set the \var{match}
+field of the winprop to a function that receives the client window
+as its sole parameter, and that returns \code{true} if the winprop
+matches, and \code{false} otherwise.
+
+The class, instance and role properties can be obtained with
+\fnref{WClientWin.get_ident}, and the title with \fnref{WRegion.name}.
+If you want to match against (almost) arbitrary window properties,
+have a look at the documentation for the following functions, and
+their standard Xlib counterparts: \fnref{ioncore.x_intern_atom}
+(XInternAtom), \fnref{ioncore.x_get_window_property} (XGetWindowProperty),
+and \fnref{ioncore.x_get_text_property} (XGetTextProperty).
+
+
+\input{statusd}
+
diff --git a/etc/Makefile b/etc/Makefile
new file mode 100644 (file)
index 0000000..81ba5c1
--- /dev/null
@@ -0,0 +1,27 @@
+##
+## Ion etc Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+ETC =  cfg_ion.lua cfg_ioncore.lua cfg_kludges.lua cfg_modules.lua \
+       cfg_tiling.lua cfg_query.lua cfg_menu.lua \
+       cfg_statusbar.lua cfg_dock.lua \
+       look_brownsteel.lua look_clean.lua look_dusky.lua \
+       look_greyviolet.lua look_ios.lua look_cleanviolet.lua \
+       look_simpleblue.lua look_cleanios.lua \
+        lookcommon_clean.lua lookcommon_emboss.lua
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: etc_install
+       rm -f $(ETCDIR)/look.lua
+       ln -s look_cleanviolet.lua $(ETCDIR)/look.lua
diff --git a/etc/cfg_dock.lua b/etc/cfg_dock.lua
new file mode 100644 (file)
index 0000000..70dcf3c
--- /dev/null
@@ -0,0 +1,51 @@
+--
+-- Ion dock module configuration
+--
+
+-- Create a dock
+mod_dock.create{
+    -- Dock mode: embedded|floating
+    mode="floating",
+    -- The screen to create the dock on
+    screen=0,
+    -- Corner or side of the screen to place the dock on.
+    -- For embedded dock the valid values are: tl|tr|bl|br
+    -- For floating dock the following are also valid: tc|bc|ml|mc|mr
+    pos="bl",
+    -- Growth direction: left|right|up|down
+    grow="right",
+    -- Whether new dockapps should be added automatically to this dock
+    is_auto=true, 
+    -- Show floating dock initially?
+    floating_hidden=false,
+    -- Name of the dock
+    name="*dock*",
+}
+
+
+-- For floating docks, you may want the following toggle binding.
+defbindings("WScreen", {
+    bdoc("Toggle floating dock."),
+    kpress(META.."D", "mod_dock.set_floating_shown_on(_, 'toggle')")
+})
+
+
+-- Dock settings menu. For this to work, mod_menu must have been loaded 
+-- previously.
+if mod_menu then
+    defmenu("dock-settings", {
+        menuentry("Pos-TL", "_:set{pos='tl'}"),
+        menuentry("Pos-TR", "_:set{pos='tr'}"),
+        menuentry("Pos-BL", "_:set{pos='bl'}"),
+        menuentry("Pos-BR", "_:set{pos='br'}"),
+        menuentry("Grow-L", "_:set{grow='left'}"),
+        menuentry("Grow-R", "_:set{grow='right'}"),
+        menuentry("Grow-U", "_:set{grow='up'}"),
+        menuentry("Grow-D", "_:set{grow='down'}"),
+    })
+    
+    defbindings("WDock", {
+        mpress("Button3", "mod_menu.pmenu(_, _sub, 'dock-settings')"),
+    })
+end
+
diff --git a/etc/cfg_ion.lua b/etc/cfg_ion.lua
new file mode 100644 (file)
index 0000000..8e8d02e
--- /dev/null
@@ -0,0 +1,56 @@
+--
+-- Ion main configuration file
+--
+-- This file only includes some settings that are rather frequently altered.
+-- The rest of the settings are in cfg_ioncore.lua and individual modules'
+-- configuration files (cfg_modulename.lua).
+--
+
+-- Set default modifiers. Alt should usually be mapped to Mod1 on
+-- XFree86-based systems. The flying window keys are probably Mod3
+-- or Mod4; see the output of 'xmodmap'.
+--META="Mod1+"
+--ALTMETA=""
+
+-- Terminal emulator
+--XTERM="xterm"
+
+-- Some basic settings
+ioncore.set{
+    -- Maximum delay between clicks in milliseconds to be considered a
+    -- double click.
+    --dblclick_delay=250,
+
+    -- For keyboard resize, time (in milliseconds) to wait after latest
+    -- key press before automatically leaving resize mode (and doing
+    -- the resize in case of non-opaque move).
+    --kbresize_delay=1500,
+
+    -- Opaque resize?
+    --opaque_resize=false,
+
+    -- Movement commands warp the pointer to frames instead of just
+    -- changing focus. Enabled by default.
+    --warp=true,
+}
+
+-- cfg_ioncore contains configuration of the Ion 'core'
+dopath("cfg_ioncore")
+
+-- Load some kludges to make apps behave better.
+dopath("cfg_kludges")
+
+-- Load some modules. Disable the loading of cfg_modules by commenting out 
+-- the corresponding line with -- if you don't want the whole default set 
+-- (everything except mod_dock). Then uncomment the lines for the modules
+-- you want. 
+dopath("cfg_modules")
+--dopath("mod_query")
+--dopath("mod_menu")
+--dopath("mod_tiling")
+--dopath("mod_statusbar")
+--dopath("mod_dock")
+--dopath("mod_sp")
+
+-- Deprecated.
+dopath("cfg_user", true)
diff --git a/etc/cfg_ioncore.lua b/etc/cfg_ioncore.lua
new file mode 100644 (file)
index 0000000..acf7e94
--- /dev/null
@@ -0,0 +1,377 @@
+--
+-- Ion core configuration file
+--
+
+
+-- 
+-- Bindings. This includes global bindings and bindings common to
+-- screens and all types of frames only. See modules' configuration 
+-- files for other bindings.
+--
+
+
+-- WScreen context bindings
+--
+-- The bindings in this context are available all the time.
+--
+-- The variable META should contain a string of the form 'Mod1+'
+-- where Mod1 maybe replaced with the modifier you want to use for most
+-- of the bindings. Similarly ALTMETA may be redefined to add a 
+-- modifier to some of the F-key bindings.
+
+defbindings("WScreen", {
+    bdoc("Switch to n:th object (workspace, full screen client window) "..
+         "within current screen."),
+    kpress(META.."1", "WScreen.switch_nth(_, 0)"),
+    kpress(META.."2", "WScreen.switch_nth(_, 1)"),
+    kpress(META.."3", "WScreen.switch_nth(_, 2)"),
+    kpress(META.."4", "WScreen.switch_nth(_, 3)"),
+    kpress(META.."5", "WScreen.switch_nth(_, 4)"),
+    kpress(META.."6", "WScreen.switch_nth(_, 5)"),
+    kpress(META.."7", "WScreen.switch_nth(_, 6)"),
+    kpress(META.."8", "WScreen.switch_nth(_, 7)"),
+    kpress(META.."9", "WScreen.switch_nth(_, 8)"),
+    kpress(META.."0", "WScreen.switch_nth(_, 9)"),
+    
+    bdoc("Switch to next/previous object within current screen."),
+    kpress(META.."comma", "WScreen.switch_prev(_)"),
+    kpress(META.."period", "WScreen.switch_next(_)"),
+    
+    submap(META.."K", {
+        bdoc("Go to first region demanding attention or previously active one."),
+        kpress("K", "ioncore.goto_activity() or ioncore.goto_previous()"),
+
+        --bdoc("Go to previous active object."),
+        --kpress("K", "ioncore.goto_previous()"),
+        
+        --bdoc("Go to first object on activity/urgency list."),
+        --kpress("I", "ioncore.goto_activity()"),
+        
+        bdoc("Clear all tags."),
+        kpress("T", "ioncore.clear_tags()"),
+    }),
+
+    bdoc("Go to n:th screen on multihead setup."),
+    kpress(META.."Shift+1", "ioncore.goto_nth_screen(0)"),
+    kpress(META.."Shift+2", "ioncore.goto_nth_screen(1)"),
+    
+    bdoc("Go to next/previous screen on multihead setup."),
+    kpress(META.."Shift+comma", "ioncore.goto_prev_screen()"),
+    kpress(META.."Shift+period", "ioncore.goto_next_screen()"),
+    
+    bdoc("Create a new workspace of chosen default type."),
+    kpress(META.."F9", "ioncore.create_ws(_)"),
+    
+    bdoc("Display the main menu."),
+    kpress(ALTMETA.."F12", "mod_query.query_menu(_, 'mainmenu', 'Main menu:')"),
+    --kpress(ALTMETA.."F12", "mod_menu.menu(_, _sub, 'mainmenu', {big=true})"),
+    mpress("Button3", "mod_menu.pmenu(_, _sub, 'mainmenu')"),
+    
+    bdoc("Display the window list menu."),
+    mpress("Button2", "mod_menu.pmenu(_, _sub, 'windowlist')"),
+
+    bdoc("Forward-circulate focus."),
+    -- '_chld' used here stands to for an actual child window that may not
+    -- be managed by the screen itself, unlike '_sub', that is likely to be
+    -- the managing group of that window. The right/left directions are
+    -- used instead of next/prev, because they work better in conjunction
+    -- with tilings.
+    kpress(META.."Tab", "ioncore.goto_next(_chld, 'right')", 
+           "_chld:non-nil"),
+    submap(META.."K", { 
+        bdoc("Backward-circulate focus."),
+        kpress("AnyModifier+Tab", "ioncore.goto_next(_chld, 'left')", 
+               "_chld:non-nil"),
+        
+        bdoc("Raise focused object, if possible."),
+        kpress("AnyModifier+R", "WRegion.rqorder(_chld, 'front')",
+               "_chld:non-nil"),
+    }),
+
+})
+
+
+-- Client window bindings
+--
+-- These bindings affect client windows directly.
+
+defbindings("WClientWin", {
+    bdoc("Nudge the client window. This might help with some "..
+         "programs' resizing problems."),
+    kpress_wait(META.."L", "WClientWin.nudge(_)"),
+    
+    submap(META.."K", {
+       bdoc("Kill client owning the client window."),
+       kpress("C", "WClientWin.kill(_)"),
+       
+       bdoc("Send next key press to the client window. "..
+            "Some programs may not allow this by default."),
+       kpress("Q", "WClientWin.quote_next(_)"),
+    }),
+})
+
+
+-- Client window group bindings
+
+defbindings("WGroupCW", {
+    bdoc("Toggle client window group full-screen mode"),
+    kpress_wait(META.."Return",
+                "WClientWin.set_fullscreen(_:bottom(), 'toggle')"),
+})
+
+
+-- WMPlex context bindings
+--
+-- These bindings work in frames and on screens. The innermost of such
+-- contexts/objects always gets to handle the key press. Most of these 
+-- bindings define actions on client windows. (Remember that client windows 
+-- can be put in fullscreen mode and therefore may not have a frame.)
+
+defbindings("WMPlex", {
+    bdoc("Close current object."),
+    kpress_wait(META.."C", "WRegion.rqclose_propagate(_, _sub)"),
+})
+
+-- Frames for transient windows ignore this bindmap
+
+defbindings("WMPlex.toplevel", {
+    bdoc("Toggle tag of current object."),
+    kpress(META.."T", "WRegion.set_tagged(_sub, 'toggle')", "_sub:non-nil"),
+
+    bdoc("Query for manual page to be displayed."),
+    kpress(ALTMETA.."F1", "mod_query.query_man(_, ':man')"),
+
+    bdoc("Show the Ion manual page."),
+    kpress(META.."F1", "ioncore.exec_on(_, ':man ion3')"),
+
+    bdoc("Run a terminal emulator."),
+    kpress(ALTMETA.."F2", "ioncore.exec_on(_, XTERM or 'xterm')"),
+    
+    bdoc("Query for command line to execute."),
+    kpress(ALTMETA.."F3", "mod_query.query_exec(_)"),
+
+    bdoc("Query for Lua code to execute."),
+    kpress(META.."F3", "mod_query.query_lua(_)"),
+
+    bdoc("Query for host to connect to with SSH."),
+    kpress(ALTMETA.."F4", "mod_query.query_ssh(_, ':ssh')"),
+
+    bdoc("Query for file to edit."),
+    kpress(ALTMETA.."F5", 
+           "mod_query.query_editfile(_, 'run-mailcap --action=edit')"),
+
+    bdoc("Query for file to view."),
+    kpress(ALTMETA.."F6", 
+           "mod_query.query_runfile(_, 'run-mailcap --action=view')"),
+
+    bdoc("Query for workspace to go to or create a new one."),
+    kpress(ALTMETA.."F9", "mod_query.query_workspace(_)"),
+    
+    bdoc("Query for a client window to go to."),
+    kpress(META.."G", "mod_query.query_gotoclient(_)"),
+    
+    bdoc("Query for a client window to attach."),
+    kpress(META.."A", "mod_query.query_attachclient(_)"),
+    
+    bdoc("Display context menu."),
+    --kpress(META.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"),
+    kpress(META.."M", "mod_query.query_menu(_, 'ctxmenu', 'Context menu:')"),
+
+})
+
+
+-- WFrame context bindings
+--
+-- These bindings are common to all types of frames. The rest of frame
+-- bindings that differ between frame types are defined in the modules' 
+-- configuration files.
+
+defbindings("WFrame", {
+    submap(META.."K", {
+        bdoc("Maximize the frame horizontally/vertically."),
+        kpress("H", "WFrame.maximize_horiz(_)"),
+        kpress("V", "WFrame.maximize_vert(_)"),
+    }),
+    
+    bdoc("Display context menu."),
+    mpress("Button3", "mod_menu.pmenu(_, _sub, 'ctxmenu')"),
+    
+    bdoc("Begin move/resize mode."),
+    kpress(META.."R", "WFrame.begin_kbresize(_)"),
+    
+    bdoc("Switch the frame to display the object indicated by the tab."),
+    mclick("Button1@tab", "WFrame.p_switch_tab(_)"),
+    mclick("Button2@tab", "WFrame.p_switch_tab(_)"),
+    
+    bdoc("Resize the frame."),
+    mdrag("Button1@border", "WFrame.p_resize(_)"),
+    mdrag(META.."Button3", "WFrame.p_resize(_)"),
+    
+    bdoc("Move the frame."),
+    mdrag(META.."Button1", "WFrame.p_move(_)"),
+    
+    bdoc("Move objects between frames by dragging and dropping the tab."),
+    mdrag("Button1@tab", "WFrame.p_tabdrag(_)"),
+    mdrag("Button2@tab", "WFrame.p_tabdrag(_)"),
+           
+})
+
+-- Frames for transient windows ignore this bindmap
+
+defbindings("WFrame.toplevel", {
+    bdoc("Tag current object within the frame."),
+    kpress(META.."T", "WRegion.set_tagged(_sub, 'toggle')", "_sub:non-nil"),
+    
+    submap(META.."K", {
+        bdoc("Switch to n:th object within the frame."),
+        kpress("1", "WFrame.switch_nth(_, 0)"),
+        kpress("2", "WFrame.switch_nth(_, 1)"),
+        kpress("3", "WFrame.switch_nth(_, 2)"),
+        kpress("4", "WFrame.switch_nth(_, 3)"),
+        kpress("5", "WFrame.switch_nth(_, 4)"),
+        kpress("6", "WFrame.switch_nth(_, 5)"),
+        kpress("7", "WFrame.switch_nth(_, 6)"),
+        kpress("8", "WFrame.switch_nth(_, 7)"),
+        kpress("9", "WFrame.switch_nth(_, 8)"),
+        kpress("0", "WFrame.switch_nth(_, 9)"),
+        
+        bdoc("Switch to next/previous object within the frame."),
+        kpress("N", "WFrame.switch_next(_)"),
+        kpress("P", "WFrame.switch_prev(_)"),
+        
+        bdoc("Move current object within the frame left/right."),
+        kpress("comma", "WFrame.dec_index(_, _sub)", "_sub:non-nil"),
+        kpress("period", "WFrame.inc_index(_, _sub)", "_sub:non-nil"),
+               
+        bdoc("Maximize the frame horizontally/vertically."),
+        kpress("H", "WFrame.maximize_horiz(_)"),
+        kpress("V", "WFrame.maximize_vert(_)"),
+
+        bdoc("Attach tagged objects to this frame."),
+        kpress("A", "WFrame.attach_tagged(_)"),
+    }),
+})
+
+-- Bindings for floating frames.
+
+defbindings("WFrame.floating", {
+    bdoc("Toggle shade mode"),
+    mdblclick("Button1@tab", "WFrame.set_shaded(_, 'toggle')"),
+    
+    bdoc("Raise the frame."),
+    mpress("Button1@tab", "WRegion.rqorder(_, 'front')"),
+    mpress("Button1@border", "WRegion.rqorder(_, 'front')"),
+    mclick(META.."Button1", "WRegion.rqorder(_, 'front')"),
+    
+    bdoc("Lower the frame."),
+    mclick(META.."Button3", "WRegion.rqorder(_, 'back')"),
+    
+    bdoc("Move the frame."),
+    mdrag("Button1@tab", "WFrame.p_move(_)"),
+})
+
+
+-- WMoveresMode context bindings
+-- 
+-- These bindings are available keyboard move/resize mode. The mode
+-- is activated on frames with the command begin_kbresize (bound to
+-- META.."R" above by default).
+
+defbindings("WMoveresMode", {
+    bdoc("Cancel the resize mode."),
+    kpress("AnyModifier+Escape","WMoveresMode.cancel(_)"),
+
+    bdoc("End the resize mode."),
+    kpress("AnyModifier+Return","WMoveresMode.finish(_)"),
+
+    bdoc("Grow in specified direction."),
+    kpress("Left",  "WMoveresMode.resize(_, 1, 0, 0, 0)"),
+    kpress("Right", "WMoveresMode.resize(_, 0, 1, 0, 0)"),
+    kpress("Up",    "WMoveresMode.resize(_, 0, 0, 1, 0)"),
+    kpress("Down",  "WMoveresMode.resize(_, 0, 0, 0, 1)"),
+    kpress("F",     "WMoveresMode.resize(_, 1, 0, 0, 0)"),
+    kpress("B",     "WMoveresMode.resize(_, 0, 1, 0, 0)"),
+    kpress("P",     "WMoveresMode.resize(_, 0, 0, 1, 0)"),
+    kpress("N",     "WMoveresMode.resize(_, 0, 0, 0, 1)"),
+    
+    bdoc("Shrink in specified direction."),
+    kpress("Shift+Left",  "WMoveresMode.resize(_,-1, 0, 0, 0)"),
+    kpress("Shift+Right", "WMoveresMode.resize(_, 0,-1, 0, 0)"),
+    kpress("Shift+Up",    "WMoveresMode.resize(_, 0, 0,-1, 0)"),
+    kpress("Shift+Down",  "WMoveresMode.resize(_, 0, 0, 0,-1)"),
+    kpress("Shift+F",     "WMoveresMode.resize(_,-1, 0, 0, 0)"),
+    kpress("Shift+B",     "WMoveresMode.resize(_, 0,-1, 0, 0)"),
+    kpress("Shift+P",     "WMoveresMode.resize(_, 0, 0,-1, 0)"),
+    kpress("Shift+N",     "WMoveresMode.resize(_, 0, 0, 0,-1)"),
+    
+    bdoc("Move in specified direction."),
+    kpress(META.."Left",  "WMoveresMode.move(_,-1, 0)"),
+    kpress(META.."Right", "WMoveresMode.move(_, 1, 0)"),
+    kpress(META.."Up",    "WMoveresMode.move(_, 0,-1)"),
+    kpress(META.."Down",  "WMoveresMode.move(_, 0, 1)"),
+    kpress(META.."F",     "WMoveresMode.move(_,-1, 0)"),
+    kpress(META.."B",     "WMoveresMode.move(_, 1, 0)"),
+    kpress(META.."P",     "WMoveresMode.move(_, 0,-1)"),
+    kpress(META.."N",     "WMoveresMode.move(_, 0, 1)"),
+})
+
+
+--
+-- Menu definitions
+--
+
+
+-- Main menu
+defmenu("mainmenu", {
+    submenu("Programs",         "appmenu"),
+    menuentry("Lock screen",    "ioncore.exec_on(_, 'xlock')"),
+    menuentry("Help",           "mod_query.query_man(_)"),
+    menuentry("About Ion",      "mod_query.show_about_ion(_)"),
+    submenu("Styles",           "stylemenu"),
+    submenu("Session",          "sessionmenu"),
+})
+
+
+-- Application menu
+defmenu("appmenu", {
+    menuentry("XTerm",          "ioncore.exec_on(_, 'xterm')"),
+    menuentry("W3M",            "ioncore.exec_on(_, ':w3m -v')"),
+    menuentry("Rxvt",           "ioncore.exec_on(_, 'rxvt')"),
+    menuentry("Opera",          "ioncore.exec_on(_, 'opera')"),
+    menuentry("Links",          "ioncore.exec_on(_, ':links')"),
+    menuentry("Konqueror",      "ioncore.exec_on(_, 'konqueror')"),
+    menuentry("Dillo",          "ioncore.exec_on(_, 'dillo')"),
+    menuentry("Run...",         "mod_query.query_exec(_)"),
+})
+
+
+-- Session control menu
+defmenu("sessionmenu", {
+    menuentry("Save",           "ioncore.snapshot()"),
+    menuentry("Restart",        "ioncore.restart()"),
+    menuentry("Restart TWM",    "ioncore.restart_other('twm')"),
+    menuentry("Exit",           "ioncore.shutdown()"),
+})
+
+
+-- Context menu (frame/client window actions)
+defctxmenu("WFrame", "Frame", {
+    menuentry("Close",          "WRegion.rqclose_propagate(_, _sub)"),
+    menuentry("Kill",           "WClientWin.kill(_sub)",
+                                "_sub:WClientWin"),
+    menuentry("Toggle tag",     "WRegion.set_tagged(_sub, 'toggle')",
+                                "_sub:non-nil"),
+    menuentry("Attach tagged",  "WFrame.attach_tagged(_)"),
+    menuentry("Clear tags",     "ioncore.clear_tags()"),
+    menuentry("Window info",    "mod_query.show_tree(_, _sub)"),
+})
+
+
+-- Context menu for screens
+defctxmenu("WScreen", "Screen", {
+    menuentry("New workspace",  "ioncore.create_ws(_)"),
+    menuentry("New empty workspace",
+                                "ioncore.create_ws(_, nil, true)"),
+    menuentry("Close workspace","WRegion.rqclose(_sub)"),
+})
+
diff --git a/etc/cfg_kludges.lua b/etc/cfg_kludges.lua
new file mode 100644 (file)
index 0000000..cd6fa00
--- /dev/null
@@ -0,0 +1,46 @@
+--
+-- Options to get some programs work more nicely (or at all)
+--
+
+
+defwinprop{
+    class = "AcroRead",
+    instance = "documentShell",
+    acrobatic = true
+}
+
+-- Galeon's find dialog does not always have its transient_for hint 
+-- set when the window is being mapped.
+defwinprop{
+    class = "galeon_browser",
+    instance = "dialog_find",
+    transient_mode = "current",
+}
+
+-- You might want to enable these if you really must use XMMS. 
+--[[
+defwinprop{
+    class = "xmms",
+    instance = "XMMS_Playlist",
+    transient_mode = "off"
+}
+
+defwinprop{
+    class = "xmms",
+    instance = "XMMS_Player",
+    transient_mode = "off"
+}
+--]]
+
+
+
+-- Define some additional title shortening rules to use when the full
+-- title doesn't fit in the available space. The first-defined matching 
+-- rule that succeeds in making the title short enough is used.
+ioncore.defshortening("(.*) - Mozilla(<[0-9]+>)", "$1$2$|$1$<...$2")
+ioncore.defshortening("(.*) - Mozilla", "$1$|$1$<...")
+ioncore.defshortening("XMMS - (.*)", "$1$|...$>$1")
+ioncore.defshortening("[^:]+: (.*)(<[0-9]+>)", "$1$2$|$1$<...$2")
+ioncore.defshortening("[^:]+: (.*)", "$1$|$1$<...")
+ioncore.defshortening("(.*)(<[0-9]+>)", "$1$2$|$1$<...$2")
+ioncore.defshortening("(.*)", "$1$|$1$<...")
diff --git a/etc/cfg_menu.lua b/etc/cfg_menu.lua
new file mode 100644 (file)
index 0000000..fff7957
--- /dev/null
@@ -0,0 +1,32 @@
+--
+-- Menu module configuration. 
+-- 
+-- Only bindings that are effect in menus are configured here. 
+-- See ion-menus.lua for menu definitions and ion-bindings.lua
+-- for bindings to display menus.
+--
+
+
+defbindings("WMenu", {
+    bdoc("Close the menu."),
+    kpress("Escape", "WMenu.cancel(_)"),
+    kpress("Control+G", "WMenu.cancel(_)"),
+    kpress("Control+C", "WMenu.cancel(_)"),
+    kpress("Left", "WMenu.cancel(_)"),
+    
+    bdoc("Activate current menu entry."),
+    kpress("Return",  "WMenu.finish(_)"),
+    kpress("KP_Enter", "WMenu.finish(_)"),
+    kpress("Control+M", "WMenu.finish(_)"),
+    kpress("Right", "WMenu.finish(_)"),
+    
+    bdoc("Select next/previous menu entry."),
+    kpress("Control+N", "WMenu.select_next(_)"),
+    kpress("Control+P", "WMenu.select_prev(_)"),
+    kpress("Up", "WMenu.select_prev(_)"),
+    kpress("Down", "WMenu.select_next(_)"),
+    
+    bdoc("Clear the menu's typeahead find buffer."),
+    kpress("BackSpace", "WMenu.typeahead_clear(_)"),
+})
+
diff --git a/etc/cfg_modules.lua b/etc/cfg_modules.lua
new file mode 100644 (file)
index 0000000..ccbfb70
--- /dev/null
@@ -0,0 +1,11 @@
+--
+-- Ion default module set.
+--
+
+dopath("mod_query")
+dopath("mod_menu")
+dopath("mod_tiling")
+--dopath("mod_panews")
+dopath("mod_statusbar")
+--dopath("mod_dock")
+dopath("mod_sp")
diff --git a/etc/cfg_panews.lua b/etc/cfg_panews.lua
new file mode 100644 (file)
index 0000000..65761a0
--- /dev/null
@@ -0,0 +1,57 @@
+--
+-- Ion panews module configuration file
+--
+
+-- Bindings for unused area. 
+
+defbindings("WUnusedWin", {
+    bdoc("Begin move/resize mode."),
+    kpress(META.."R", "WUnusedWin.begin_kbresize(_)"),
+    
+    bdoc("Resize the area."),
+    mdrag(META.."Button3", "WUnusedWin.p_resize(_)"),
+    mdrag(META.."Button1", "WUnusedWin.p_move(_)"),
+})
+
+
+mod_panews.set{ 
+    -- Layout template may be one of default|alternative1|alternative2 
+    -- or a template table. (The one for 'default' is reproduced below
+    -- as an example.)
+    --template="default",
+    -- The scale factor parameter controls the size-based classification 
+    -- heuristic. The default of 1.0 is designed for 1280x1024 at 75dpi.
+    --scalef=1.0,
+}
+
+
+-- The layout template for the 'default' layout looks as follows.
+--[[
+{
+    type="WSplitFloat",
+    dir="horizontal",
+    tls=settings.b_ratio,
+    brs=settings.s_ratio,
+    tl={
+        type="WSplitPane",
+        contents={
+            type="WSplitFloat",
+            dir="vertical",
+            tls=settings.b_ratio2,
+            brs=settings.s_ratio2,
+            tl={
+                type="WSplitPane",
+                marker="V:single",
+            },
+            br={
+                type="WSplitPane",
+                marker="M:right",
+            },
+        },
+    },
+    br={
+        type="WSplitPane",
+        marker="T:up",
+    },
+}
+--]]
diff --git a/etc/cfg_query.lua b/etc/cfg_query.lua
new file mode 100644 (file)
index 0000000..84f7da6
--- /dev/null
@@ -0,0 +1,117 @@
+--
+-- Query module configuration.
+--
+-- Only bindings that are in effect in queries and message displays are
+-- configured here. Actions to display queries are configured in
+-- ion-bindings.lua
+-- 
+
+
+defbindings("WEdln", {
+    bdoc("Move one character forward/backward."),
+    kpress("Control+F", "WEdln.forward(_)"),
+    kpress("Control+B", "WEdln.back(_)"),
+    kpress("Right", "WEdln.forward(_)"),
+    kpress("Left", "WEdln.back(_)"),
+    
+    bdoc("Go to end/beginning."),
+    kpress("Control+E", "WEdln.eol(_)"),
+    kpress("Control+A", "WEdln.bol(_)"),
+    kpress("End", "WEdln.eol(_)"),
+    kpress("Home", "WEdln.bol(_)"),
+    
+    bdoc("Skip one word forward/backward."),
+    kpress("Control+X", "WEdln.skip_word(_)"),
+    kpress("Control+Z", "WEdln.bskip_word(_)"),
+
+    bdoc("Delete next character."),
+    kpress("Control+D", "WEdln.delete(_)"),
+    kpress("Delete", "WEdln.delete(_)"),
+    
+    bdoc("Delete previous character."),
+    kpress("BackSpace", "WEdln.backspace(_)"),
+    kpress("Control+H", "WEdln.backspace(_)"),
+    
+    bdoc("Delete one word forward/backward."),
+    kpress("Control+W", "WEdln.kill_word(_)"),
+    kpress("Control+O", "WEdln.bkill_word(_)"),
+
+    bdoc("Delete to end of line."),
+    kpress("Control+J", "WEdln.kill_to_eol(_)"),
+    
+    bdoc("Delete the whole line."),
+    kpress("Control+Y", "WEdln.kill_line(_)"),
+    
+    bdoc("Transpose characters."),
+    kpress("Control+T", "WEdln.transpose_chars(_)"),
+
+    bdoc("Select next/previous (matching) history entry."),
+    kpress("Control+P", "WEdln.history_prev(_)"),
+    kpress("Control+N", "WEdln.history_next(_)"),
+    kpress("Up", "WEdln.history_prev(_)"),
+    kpress("Down", "WEdln.history_next(_)"),
+    kpress("Control+Up", "WEdln.history_prev(_, true)"),
+    kpress("Control+Down", "WEdln.history_next(_, true)"),
+
+    bdoc("Paste from the clipboard."),
+    mclick("Button2", "WEdln.paste(_)"),
+    submap("Control+K", {
+        kpress("C", "WEdln.paste(_)"),
+        
+        bdoc("Set mark/begin selection."),
+        kpress("B", "WEdln.set_mark(_)"),
+        
+        bdoc("Cut selection."),
+        kpress("Y", "WEdln.cut(_)"),
+        
+        bdoc("Copy selection."),
+        kpress("K", "WEdln.copy(_)"),
+        
+        bdoc("Clear mark/cancel selection."),
+        kpress("G", "WEdln.clear_mark(_)"),
+
+        --bdoc("Transpose words."),
+        --kpress("T", "WEdln.transpose_words(_)"),
+    }),
+
+    bdoc("Try to complete the entered text or cycle through completions."),
+    kpress("Tab", "WEdln.complete(_, 'next', 'normal')"), 
+    kpress("Shift+Tab", "WEdln.complete(_, 'prev', 'normal')"),
+    -- Do not cycle; only force evaluation of new completions
+    kpress("Control+Tab", "WEdln.complete(_, nil, 'normal')"),
+    
+    bdoc("Complete from history"),
+    kpress("Control+R", "WEdln.complete(_, 'next', 'history')"),
+    kpress("Control+S", "WEdln.complete(_, 'prev', 'history')"),
+    
+    bdoc("Close the query and execute bound action."),
+    kpress("Control+M", "WEdln.finish(_)"),
+    kpress("Return", "WEdln.finish(_)"),
+    kpress("KP_Enter", "WEdln.finish(_)"),
+})
+
+
+defbindings("WInput", {
+    bdoc("Close the query/message box, not executing bound actions."),
+    kpress("Escape", "WInput.cancel(_)"),
+    kpress("Control+G", "WInput.cancel(_)"),
+    kpress("Control+C", "WInput.cancel(_)"),
+    
+    bdoc("Scroll the message or completions up/down."),
+    kpress("Control+U", "WInput.scrollup(_)"),
+    kpress("Control+V", "WInput.scrolldown(_)"),
+    kpress("Page_Up", "WInput.scrollup(_)"),
+    kpress("Page_Down", "WInput.scrolldown(_)"),
+})
+
+
+-- Some settings
+--[[
+mod_query.set{
+     -- Auto-show completions?    
+     autoshowcompl=true,
+     -- Delay for completion after latest keypress/modification in 
+     -- milliseconds
+     autoshowcompl_delay=250,
+}
+--]]
diff --git a/etc/cfg_statusbar.lua b/etc/cfg_statusbar.lua
new file mode 100644 (file)
index 0000000..82fa506
--- /dev/null
@@ -0,0 +1,87 @@
+--
+-- Ion statusbar module configuration file
+-- 
+
+
+-- Create a statusbar
+mod_statusbar.create{
+    -- First screen, bottom left corner
+    screen=0,
+    pos='bl',
+    -- Set this to true if you want a full-width statusbar
+    fullsize=false,
+    -- Swallow systray windows
+    systray=true,
+
+    -- Template. Tokens %string are replaced with the value of the 
+    -- corresponding meter. Currently supported meters are:
+    --   date          date
+    --   load          load average (1min, 5min, 15min)
+    --   load_Nmin     N minute load average (N=1, 5, 15)
+    --   mail_new      mail count (mbox format file $MAIL)
+    --   mail_unread   mail count
+    --   mail_total    mail count
+    --   mail_*_new    mail count (from an alternate mail folder, see below)
+    --   mail_*_unread mail count
+    --   mail_*_total  mail count
+    --
+    -- Space preceded by % adds stretchable space for alignment of variable
+    -- meter value widths. > before meter name aligns right using this 
+    -- stretchable space , < left, and | centers.
+    -- Meter values may be zero-padded to a width preceding the meter name.
+    -- These alignment and padding specifiers and the meter name may be
+    -- enclosed in braces {}.
+    --
+    -- %filler causes things on the marker's sides to be aligned left and
+    -- right, respectively, and %systray is a placeholder for system tray
+    -- windows and icons.
+    --
+    --template="[ %date || load:% %>load || mail:% %>mail_new/%>mail_total ] %filler%systray",
+    --template="[ %date || load: %05load_1min || mail: %02mail_new/%02mail_total ] %filler%systray",
+}
+
+
+-- Launch ion-statusd. This must be done after creating any statusbars
+-- for necessary statusd modules to be parsed from the templates.
+mod_statusbar.launch_statusd{
+    -- Date meter
+    date={
+        -- ISO-8601 date format with additional abbreviated day name
+        date_format='%a %Y-%m-%d %H:%M',
+        -- Finnish etc. date format
+        --date_format='%a %d.%m.%Y %H:%M',
+        -- Locale date format (usually shows seconds, which would require
+        -- updating rather often and can be distracting)
+        --date_format='%c',
+        
+        -- Additional date formats. 
+        --[[ 
+        formats={ 
+            time = '%H:%M', -- %date_time
+        }
+        --]]
+    },      
+
+    -- Load meter
+    load={
+        --update_interval=10*1000,
+        --important_threshold=1.5,
+        --critical_threshold=4.0,
+    },
+
+    -- Mail meter
+    --
+    -- To monitor more mbox files, add them to the files table.  For
+    -- example, add mail_work_new and mail_junk_new to the template
+    -- above, and define them in the files table:
+    --
+    -- files = { work = "/path/to/work_email", junk = "/path/to/junk" }
+    --
+    -- Don't use the keyword 'spool' as it's reserved for mbox.
+    mail={
+        --update_interval=60*1000,
+        --mbox=os.getenv("MAIL"),
+        --files={},
+    },
+}
+
diff --git a/etc/cfg_tiling.lua b/etc/cfg_tiling.lua
new file mode 100644 (file)
index 0000000..0225d3a
--- /dev/null
@@ -0,0 +1,146 @@
+--
+-- Ion tiling module configuration file
+--
+
+-- Bindings for the tilings. 
+
+defbindings("WTiling", {
+    bdoc("Split current frame vertically."),
+    kpress(META.."S", "WTiling.split_at(_, _sub, 'bottom', true)"),
+    
+    bdoc("Go to frame above/below/right/left of current frame."),
+    kpress(META.."P", "ioncore.goto_next(_sub, 'up', {no_ascend=_})"),
+    kpress(META.."N", "ioncore.goto_next(_sub, 'down', {no_ascend=_})"),
+    kpress(META.."Tab", "ioncore.goto_next(_sub, 'right')"),
+    submap(META.."K", {
+        kpress("Tab", "ioncore.goto_next(_sub, 'left')"),
+        
+        bdoc("Split current frame horizontally."),
+        kpress("S", "WTiling.split_at(_, _sub, 'right', true)"),
+        
+        bdoc("Destroy current frame."),
+        kpress("X", "WTiling.unsplit_at(_, _sub)"),
+    }),
+})
+
+
+-- Frame bindings
+
+defbindings("WFrame.tiled", {
+    submap(META.."K", {
+        bdoc("Detach window from tiled frame"),
+        kpress("D", "mod_tiling.detach(_sub)", "_sub:non-nil"),
+    }),
+})
+
+
+defbindings("WFrame.transient", {
+    submap(META.."K", {
+        bdoc("Detach transient frame"),
+        kpress("D", "mod_tiling.detach(_)", "_sub:non-nil"),
+    }),
+})
+
+
+defbindings("WFrame.floating", {
+    submap(META.."K", {
+        bdoc("Tile frame, if no tiling exists on the workspace"),
+        kpress("B", "mod_tiling.mkbottom(_)"),
+    }),
+})
+
+
+-- Context menu for tiled workspaces.
+
+defctxmenu("WTiling", "Tiling", {
+    menuentry("Destroy frame", 
+              "WTiling.unsplit_at(_, _sub)"),
+
+    menuentry("Split vertically", 
+              "WTiling.split_at(_, _sub, 'bottom', true)"),
+    menuentry("Split horizontally", 
+              "WTiling.split_at(_, _sub, 'right', true)"),
+    
+    menuentry("Flip", "WTiling.flip_at(_, _sub)"),
+    menuentry("Transpose", "WTiling.transpose_at(_, _sub)"),
+    
+    submenu("Float split", {
+        menuentry("At left", 
+                  "WTiling.set_floating_at(_, _sub, 'toggle', 'left')"),
+        menuentry("At right", 
+                  "WTiling.set_floating_at(_, _sub, 'toggle', 'right')"),
+        menuentry("Above",
+                  "WTiling.set_floating_at(_, _sub, 'toggle', 'up')"),
+        menuentry("Below",
+                  "WTiling.set_floating_at(_, _sub, 'toggle', 'down')"),
+    }),
+
+    submenu("At root", {
+        menuentry("Split vertically", 
+                  "WTiling.split_top(_, 'bottom')"),
+        menuentry("Split horizontally", 
+                  "WTiling.split_top(_, 'right')"),
+        menuentry("Flip", "WTiling.flip_at(_)"),
+        menuentry("Transpose", "WTiling.transpose_at(_)"),
+    }),
+})
+
+
+-- Context menu entries for tiled frames.
+
+defctxmenu("WFrame.tiled", "Tiled frame", {
+    menuentry("Detach window", "mod_tiling.detach(_sub)", "_sub:non-nil"),
+})
+
+
+-- Context menu entries for transient frames.
+
+defctxmenu("WFrame.transient", "Transient frame", {
+    append=true,
+    menuentry("Detach", "mod_tiling.detach(_)", "_sub:non-nil"),
+})
+
+
+-- Extra context menu extra entries for floatframes. 
+
+defctxmenu("WFrame.floating", "Floating frame", {
+    append=true,
+    menuentry("New tiling", "mod_tiling.mkbottom(_)"),
+})
+
+
+-- Adjust default workspace layout
+
+local a_frame = {
+    type="WSplitRegion",
+    regparams = {
+        type = "WFrame", 
+        frame_style = "frame-tiled"
+    }
+}
+    
+ioncore.set{
+    default_ws_params = {
+        -- Destroy workspace if the 'bottom' tiling is destroyed last
+        bottom_last_close = true,
+        -- Layout
+        managed = {
+            {
+                type = "WTiling",
+                bottom = true,
+                -- The default is a single 1:1 horizontal split
+                split_tree = {
+                    type = "WSplitSplit",
+                    dir = "horizontal",
+                    tls = 1,
+                    brs = 1,
+                    tl = a_frame,
+                    br = a_frame
+                }
+                -- For a single frame
+                --split_tree = nil
+            }
+        }
+    }
+}
+
diff --git a/etc/look_brownsteel.lua b/etc/look_brownsteel.lua
new file mode 100644 (file)
index 0000000..221c5f5
--- /dev/null
@@ -0,0 +1,108 @@
+-- look_brownsteel.lua drawing engine configuration file for Ion.
+
+if not gr.select_engine("de") then return end
+
+de.reset()
+
+de.defstyle("*", {
+    shadow_colour = "#404040",
+    highlight_colour = "#707070",
+    background_colour = "#505050",
+    foreground_colour = "#a0a0a0",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    border_style = "elevated",
+    font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*",
+    text_align = "center",
+})
+
+de.defstyle("frame", {
+    based_on = "*",
+    shadow_colour = "#404040",
+    highlight_colour = "#707070",
+    padding_colour = "#505050",
+    background_colour = "#000000",
+    foreground_colour = "#ffffff",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+})
+
+de.defstyle("tab", {
+    based_on = "*",
+    font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*",
+    de.substyle("active-selected", {
+        shadow_colour = "#304050",
+        highlight_colour = "#708090",
+        background_colour = "#506070",
+        foreground_colour = "#ffffff",
+    }),
+    de.substyle("active-unselected", {
+        shadow_colour = "#203040",
+        highlight_colour = "#607080",
+        background_colour = "#405060",
+        foreground_colour = "#a0a0a0",
+    }),
+    de.substyle("inactive-selected", {
+        shadow_colour = "#404040",
+        highlight_colour = "#909090",
+        background_colour = "#606060",
+        foreground_colour = "#a0a0a0",
+    }),
+    de.substyle("inactive-unselected", {
+        shadow_colour = "#404040",
+        highlight_colour = "#707070",
+        background_colour = "#505050",
+        foreground_colour = "#a0a0a0",
+    }),
+    text_align = "center",
+})
+
+de.defstyle("tab-menuentry", {
+    based_on = "tab",
+    text_align = "left",
+    highlight_pixels = 0,
+    shadow_pixels = 0,
+})
+
+de.defstyle("tab-menuentry-big", {
+    based_on = "tab-menuentry",
+    font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*",
+    padding_pixels = 7,
+})
+
+de.defstyle("input", {
+    based_on = "*",
+    shadow_colour = "#404040",
+    highlight_colour = "#707070",
+    background_colour = "#000000",
+    foreground_colour = "#ffffff",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    border_style = "elevated",
+    de.substyle("*-cursor", {
+        background_colour = "#ffffff",
+        foreground_colour = "#000000",
+    }),
+    de.substyle("*-selection", {
+        background_colour = "#505050",
+        foreground_colour = "#ffffff",
+    }),
+})
+
+de.defstyle("input-menu", {
+    based_on = "*",
+    de.substyle("active", {
+        shadow_colour = "#304050",
+        highlight_colour = "#708090",
+        background_colour = "#506070",
+        foreground_colour = "#ffffff",
+    }),
+})
+
+dopath("lookcommon_emboss")
+
+gr.refresh()
+
diff --git a/etc/look_clean.lua b/etc/look_clean.lua
new file mode 100644 (file)
index 0000000..7da34a9
--- /dev/null
@@ -0,0 +1,102 @@
+-- look_clean.lua drawing engine configuration file for Ion.
+
+if not gr.select_engine("de") then return end
+
+de.reset()
+
+de.defstyle("*", {
+    shadow_colour = "grey",
+    highlight_colour = "grey",
+    background_colour = "#545d75",
+    foreground_colour = "grey",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    border_style = "elevated",
+    font = "-misc-fixed-medium-r-*-*-13-*-*-*-*-60-*-*",
+    text_align = "center",
+})
+
+de.defstyle("frame", {
+    based_on = "*",
+    padding_colour = "#545d75",
+    background_colour = "black",
+    de.substyle("active", {
+        shadow_colour = "grey",
+        highlight_colour = "grey",
+        padding_colour = "#545d75",
+        background_colour = "black",
+    }),
+})
+
+de.defstyle("tab", {
+    based_on = "*",
+    font = "-misc-fixed-medium-r-*-*-13-*-*-*-*-60-*-*",
+    de.substyle("active-selected", {
+        shadow_colour = "white",
+        highlight_colour = "white",
+        background_colour = "#8a999e",
+        foreground_colour = "white",
+    }),
+    de.substyle("active-unselected", {
+        shadow_colour = "grey",
+        highlight_colour = "grey",
+        background_colour = "#545d75",
+        foreground_colour = "grey",
+    }),
+    de.substyle("inactive-selected", {
+        shadow_colour = "grey",
+        highlight_colour = "grey",
+        background_colour = "#545d75",
+        foreground_colour = "grey",
+    }),
+    de.substyle("inactive-unselected", {
+        shadow_colour = "grey",
+        highlight_colour = "grey",
+        background_colour = "#545d75",
+        foreground_colour = "grey",
+    }),
+    text_align = "center",
+})
+
+de.defstyle("tab-menuentry", {
+    based_on = "tab",
+    text_align = "left",
+    de.substyle("inactive-selected", {
+        shadow_colour = "grey",
+        highlight_colour = "grey",
+        background_colour = "#8a999e",
+        foreground_colour = "grey",
+    }),
+})
+
+de.defstyle("tab-menuentry-big", {
+    based_on = "tab-menuentry",
+    padding_pixels = 7,
+})
+
+de.defstyle("input", {
+    based_on = "*",
+    shadow_colour = "grey",
+    highlight_colour = "grey",
+    background_colour = "#545d75",
+    foreground_colour = "white",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    border_style = "elevated",
+    de.substyle("*-cursor", {
+        background_colour = "white",
+        foreground_colour = "#545d75",
+    }),
+    de.substyle("*-selection", {
+        background_colour = "#aaaaaa",
+        foreground_colour = "black",
+    }),
+    font = "-misc-fixed-medium-r-*-*-13-*-*-*-*-60-*-*",
+})
+
+dopath("lookcommon_clean")
+
+gr.refresh()
+
diff --git a/etc/look_cleanios.lua b/etc/look_cleanios.lua
new file mode 100644 (file)
index 0000000..6a70546
--- /dev/null
@@ -0,0 +1,85 @@
+-- look_cleanios.lua drawing engine configuration file for Ion.
+
+if not gr.select_engine("de") then return end
+
+de.reset()
+
+de.defstyle("*", {
+    shadow_colour = "#ffffff",
+    highlight_colour = "#ffffff",
+    background_colour = "#d8d8d8",
+    foreground_colour = "#000000",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    spacing = 0,
+    border_style = "elevated",
+    font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*",
+    text_align = "center",
+})
+
+de.defstyle("frame", {
+    based_on = "*",
+    padding_colour = "#d8d8d8",
+    background_colour = "#000000",
+    transparent_background = false,
+})
+
+de.defstyle("tab", {
+    based_on = "*",
+    font = "-*-helvetica-bold-r-normal-*-10-*-*-*-*-*-*-*",
+    de.substyle("active-selected", {
+        shadow_colour = "#f0f066",
+        highlight_colour = "#f0f066",
+        background_colour = "#f0c000",
+        foreground_colour = "#000000",
+    }),
+    de.substyle("active-unselected", {
+        shadow_colour = "#ffffff",
+        highlight_colour = "#ffffff",
+        background_colour = "#d8d8d8",
+        foreground_colour = "#000000",
+    }),
+    de.substyle("inactive-selected", {
+        shadow_colour = "#ffffff",
+        highlight_colour = "#ffffff",
+        background_colour = "#a8a8a8",
+        foreground_colour = "#000000",
+    }),
+    de.substyle("inactive-unselected", {
+        shadow_colour = "#ffffff",
+        highlight_colour = "#ffffff",
+        background_colour = "#d8d8d8",
+        foreground_colour = "#000000",
+    }),
+    text_align = "center",
+})
+
+de.defstyle("tab-menuentry", {
+    based_on = "tab",
+    text_align = "left",
+    spacing = 1,
+})
+
+de.defstyle("tab-menuentry-big", {
+    based_on = "tab-menuentry",
+    font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*",
+    padding_pixels = 7,
+})
+
+de.defstyle("input-edln", {
+    based_on = "*",
+    de.substyle("*-cursor", {
+        background_colour = "#000000",
+        foreground_colour = "#d8d8d8",
+    }),
+    de.substyle("*-selection", {
+        background_colour = "#f0c000",
+        foreground_colour = "#000000",
+    }),
+})
+
+dopath("lookcommon_clean")
+
+gr.refresh()
+
diff --git a/etc/look_cleanviolet.lua b/etc/look_cleanviolet.lua
new file mode 100644 (file)
index 0000000..32dce50
--- /dev/null
@@ -0,0 +1,100 @@
+--
+-- Look_cleanviolet for Ion's default drawing engine. 
+-- Based on look-clean and look-violetgrey.
+-- 
+
+if not gr.select_engine("de") then
+    return
+end
+
+-- Clear existing styles from memory.
+de.reset()
+
+-- Base style
+de.defstyle("*", {
+    -- Gray background
+    highlight_colour = "#eeeeee",
+    shadow_colour = "#eeeeee",
+    background_colour = "#aaaaaa",
+    foreground_colour = "#000000",
+    
+    shadow_pixels = 1,
+    highlight_pixels = 1,
+    padding_pixels = 1,
+    spacing = 0,
+    border_style = "elevated",
+    
+    font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*",
+    text_align = "center",
+})
+
+
+de.defstyle("frame", {
+    based_on = "*",
+    padding_colour = "#aaaaaa",
+    background_colour = "#000000",
+    transparent_background = false,
+})
+
+
+de.defstyle("tab", {
+    based_on = "*",
+    font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*",
+    
+    de.substyle("active-selected", {
+        -- Violet tab
+        highlight_colour = "#aaaacc",
+        shadow_colour = "#aaaacc",
+        background_colour = "#666699",
+        foreground_colour = "#eeeeee",
+    }),
+
+    de.substyle("inactive-selected", {
+        -- Greyish violet tab
+        highlight_colour = "#eeeeff",
+        shadow_colour = "#eeeeff",
+        background_colour = "#9999aa",
+        foreground_colour = "#000000",
+    }),
+})
+
+
+de.defstyle("tab-menuentry", {
+    based_on = "tab",
+    text_align = "left",
+    spacing = 1,
+})
+
+
+de.defstyle("tab-menuentry-big", {
+    based_on = "tab-menuentry",
+    font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*",
+    padding_pixels = 7,
+})
+
+
+de.defstyle("input", {
+    based_on = "*",
+    text_align = "left",
+    spacing = 1,
+    -- Greyish violet background
+    highlight_colour = "#eeeeff",
+    shadow_colour = "#eeeeff",
+    background_colour = "#9999aa",
+    foreground_colour = "#000000",
+    
+    de.substyle("*-selection", {
+        background_colour = "#777799",
+        foreground_colour = "#000000",
+    }),
+
+    de.substyle("*-cursor", {
+        background_colour = "#000000",
+        foreground_colour = "#9999aa",
+    }),
+})
+
+dopath("lookcommon_clean")
+    
+-- Refresh objects' brushes.
+gr.refresh()
diff --git a/etc/look_dusky.lua b/etc/look_dusky.lua
new file mode 100644 (file)
index 0000000..5effeee
--- /dev/null
@@ -0,0 +1,105 @@
+-- look_dusky.lua drawing engine configuration file for Ion.
+
+if not gr.select_engine("de") then return end
+
+de.reset()
+
+de.defstyle("*", {
+    shadow_colour = "#404040",
+    highlight_colour = "#707070",
+    background_colour = "#505050",
+    foreground_colour = "#a0a0a0",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    border_style = "elevated",
+    font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*",
+    text_align = "center",
+})
+
+de.defstyle("frame", {
+    based_on = "*",
+    shadow_colour = "#404040",
+    highlight_colour = "#707070",
+    padding_colour = "#505050",
+    background_colour = "#000000",
+    foreground_colour = "#ffffff",
+    padding_pixels = 2,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+})
+
+de.defstyle("tab", {
+    based_on = "*",
+    font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*",
+    de.substyle("active-selected", {
+        shadow_colour = "#452727",
+        highlight_colour = "#866868",
+        background_colour = "#664848",
+        foreground_colour = "#ffffff",
+    }),
+    de.substyle("active-unselected", {
+        shadow_colour = "#351818",
+        highlight_colour = "#765858",
+        background_colour = "#563838",
+        foreground_colour = "#a0a0a0",
+    }),
+    de.substyle("inactive-selected", {
+        shadow_colour = "#404040",
+        highlight_colour = "#909090",
+        background_colour = "#606060",
+        foreground_colour = "#a0a0a0",
+    }),
+    de.substyle("inactive-unselected", {
+        shadow_colour = "#404040",
+        highlight_colour = "#707070",
+        background_colour = "#505050",
+        foreground_colour = "#a0a0a0",
+    }),
+    text_align = "center",
+})
+
+de.defstyle("tab-menuentry", {
+    based_on = "tab",
+    text_align = "left",
+    highlight_pixels = 0,
+    shadow_pixels = 0,
+})
+
+de.defstyle("tab-menuentry-big", {
+    based_on = "tab-menuentry",
+    font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*",
+    padding_pixels = 7,
+})
+
+de.defstyle("input", {
+    based_on = "*",
+    shadow_colour = "#404040",
+    highlight_colour = "#707070",
+    background_colour = "#000000",
+    foreground_colour = "#ffffff",
+    border_style = "elevated",
+    de.substyle("*-cursor", {
+        background_colour = "#ffffff",
+        foreground_colour = "#000000",
+    }),
+    de.substyle("*-selection", {
+        background_colour = "#505050",
+        foreground_colour = "#ffffff",
+    }),
+})
+
+de.defstyle("input-menu", {
+    based_on = "*",
+    de.substyle("active", {
+        shadow_colour = "#452727",
+        highlight_colour = "#866868",
+        background_colour = "#664848",
+        foreground_colour = "#ffffff",
+    }),
+})
+
+dopath("lookcommon_emboss")
+
+gr.refresh()
+
diff --git a/etc/look_greyviolet.lua b/etc/look_greyviolet.lua
new file mode 100644 (file)
index 0000000..ec15a78
--- /dev/null
@@ -0,0 +1,98 @@
+-- look_greyviolet.lua drawing engine configuration file for Ion.
+
+if not gr.select_engine("de") then return end
+
+de.reset()
+
+de.defstyle("*", {
+    shadow_colour = "#777777",
+    highlight_colour = "#eeeeee",
+    background_colour = "#aaaaaa",
+    foreground_colour = "#000000",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    border_style = "elevated",
+    font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*",
+    text_align = "center",
+})
+
+de.defstyle("frame", {
+    based_on = "*",
+    shadow_colour = "#777777",
+    highlight_colour = "#eeeeee",
+    padding_colour = "#aaaaaa",
+    background_colour = "#000000",
+    foreground_colour = "#ffffff",
+    padding_pixels = 2,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+})
+
+de.defstyle("tab", {
+    based_on = "*",
+    font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*",
+    de.substyle("active-selected", {
+        shadow_colour = "#333366",
+        highlight_colour = "#aaaacc",
+        background_colour = "#666699",
+        foreground_colour = "#eeeeee",
+    }),
+    de.substyle("active-unselected", {
+        shadow_colour = "#777777",
+        highlight_colour = "#eeeeee",
+        background_colour = "#aaaaaa",
+        foreground_colour = "#000000",
+    }),
+    de.substyle("inactive-selected", {
+        shadow_colour = "#777788",
+        highlight_colour = "#eeeeff",
+        background_colour = "#9999aa",
+        foreground_colour = "#000000",
+    }),
+    de.substyle("inactive-unselected", {
+        shadow_colour = "#777777",
+        highlight_colour = "#eeeeee",
+        background_colour = "#aaaaaa",
+        foreground_colour = "#000000",
+    }),
+    text_align = "center",
+})
+
+de.defstyle("tab-menuentry", {
+    based_on = "tab",
+    text_align = "left",
+    highlight_pixels = 0,
+    shadow_pixels = 0,
+})
+
+de.defstyle("tab-menuentry-big", {
+    based_on = "tab-menuentry",
+    font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*",
+    padding_pixels = 7,
+})
+
+de.defstyle("input", {
+    based_on = "*",
+    shadow_colour = "#777777",
+    highlight_colour = "#eeeeee",
+    background_colour = "#aaaaaa",
+    foreground_colour = "#000000",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    border_style = "elevated",
+    de.substyle("*-cursor", {
+        background_colour = "#000000",
+        foreground_colour = "#aaaaaa",
+    }),
+    de.substyle("*-selection", {
+        background_colour = "#aaaaaa",
+        foreground_colour = "black",
+    }),
+})
+
+dopath("lookcommon_emboss")
+
+gr.refresh()
+
diff --git a/etc/look_ios.lua b/etc/look_ios.lua
new file mode 100644 (file)
index 0000000..4b8f4da
--- /dev/null
@@ -0,0 +1,104 @@
+-- look_ios.lua drawing engine configuration file for Ion.
+
+if not gr.select_engine("de") then return end
+
+de.reset()
+
+de.defstyle("*", {
+    shadow_colour = "#606060",
+    highlight_colour = "#ffffff",
+    background_colour = "#d8d8d8",
+    foreground_colour = "#000000",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    border_style = "elevated",
+    font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*",
+    text_align = "center",
+})
+
+de.defstyle("frame", {
+    based_on = "*",
+    shadow_colour = "#606060",
+    highlight_colour = "#ffffff",
+    padding_colour = "#d8d8d8",
+    background_colour = "#000000",
+    foreground_colour = "#000000",
+    padding_pixels = 2,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    de.substyle("active", {
+        shadow_colour = "#606060",
+        highlight_colour = "#ffffff",
+        background_colour = "#d8d8d8",
+        foreground_colour = "#000000",
+    }),
+})
+
+de.defstyle("tab", {
+    based_on = "*",
+    font = "-*-helvetica-bold-r-normal-*-10-*-*-*-*-*-*-*",
+    de.substyle("active-selected", {
+        shadow_colour = "#f09000",
+        highlight_colour = "#f0f066",
+        background_colour = "#f0c000",
+        foreground_colour = "#000000",
+    }),
+    de.substyle("active-unselected", {
+        shadow_colour = "#606060",
+        highlight_colour = "#ffffff",
+        background_colour = "#d8d8d8",
+        foreground_colour = "#000000",
+    }),
+    de.substyle("inactive-selected", {
+        shadow_colour = "#606060",
+        highlight_colour = "#efefef",
+        background_colour = "#a8a8a8",
+        foreground_colour = "#000000",
+    }),
+    de.substyle("inactive-unselected", {
+        shadow_colour = "#606060",
+        highlight_colour = "#ffffff",
+        background_colour = "#d8d8d8",
+        foreground_colour = "#000000",
+    }),
+    text_align = "center",
+})
+
+de.defstyle("tab-menuentry", {
+    based_on = "tab",
+    text_align = "left",
+    highlight_pixels = 0,
+    shadow_pixels = 0,
+})
+
+de.defstyle("tab-menuentry-big", {
+    based_on = "tab-menuentry",
+    font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*",
+    padding_pixels = 7,
+})
+
+de.defstyle("input", {
+    based_on = "*",
+    shadow_colour = "#606060",
+    highlight_colour = "#ffffff",
+    background_colour = "#d8d8d8",
+    foreground_colour = "#000000",
+    padding_pixels = 1,
+    highlight_pixels = 1,
+    shadow_pixels = 1,
+    border_style = "elevated",
+    de.substyle("*-cursor", {
+        background_colour = "#000000",
+        foreground_colour = "#d8d8d8",
+    }),
+    de.substyle("*-selection", {
+        background_colour = "#f0c000",
+        foreground_colour = "#000000",
+    }),
+})
+
+dopath("lookcommon_emboss")
+
+gr.refresh()
+
diff --git a/etc/look_simpleblue.lua b/etc/look_simpleblue.lua
new file mode 100644 (file)
index 0000000..0773f7a
--- /dev/null
@@ -0,0 +1,112 @@
+-- look_simpleblue.lua drawing engine configuration file for Ion.
+
+if not gr.select_engine("de") then return end
+
+de.reset()
+
+de.defstyle("*", {
+    shadow_colour = "black",
+    highlight_colour = "black",
+    background_colour = "#0f1f4f",
+    foreground_colour = "#9f9f9f",
+    padding_pixels = 1,
+    highlight_pixels = 0,
+    shadow_pixels = 0,
+    border_style = "elevated",
+    font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*",
+    text_align = "center",
+})
+
+de.defstyle("frame", {
+    based_on = "*",
+    shadow_colour = "black",
+    highlight_colour = "black",
+    padding_colour = "black",
+    background_colour = "black",
+    foreground_colour = "#ffffff",
+    padding_pixels = 0,
+    highlight_pixels = 0,
+    shadow_pixels = 0,
+    de.substyle("active", {
+        shadow_colour = "black",
+        highlight_colour = "black",
+        background_colour = "black",
+        foreground_colour = "#ffffff",
+    }),
+})
+
+de.defstyle("tab", {
+    based_on = "*",
+    font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*",
+    de.substyle("active-selected", {
+        shadow_colour = "black",
+        highlight_colour = "black",
+        background_colour = "#3f3f3f",
+        foreground_colour = "#9f9f9f",
+    }),
+    de.substyle("active-unselected", {
+        shadow_colour = "black",
+        highlight_colour = "black",
+        background_colour = "#0f1f4f",
+        foreground_colour = "#9f9f9f",
+    }),
+    de.substyle("inactive-selected", {
+        shadow_colour = "black",
+        highlight_colour = "black",
+        background_colour = "#0f1f4f",
+        foreground_colour = "#9f9f9f",
+    }),
+    de.substyle("inactive-unselected", {
+        shadow_colour = "black",
+        highlight_colour = "black",
+        background_colour = "#0f1f4f",
+        foreground_colour = "#9f9f9f",
+    }),
+    text_align = "center",
+})
+
+de.defstyle("tab-frame", {
+    based_on = "tab",
+    de.substyle("*-*-*-*-activity", {
+        shadow_colour = "#907070",
+        highlight_colour = "#907070",
+        background_colour = "#990000",
+        foreground_colour = "#eeeeee",
+    }),
+})
+
+de.defstyle("tab-menuentry", {
+    based_on = "tab",
+    text_align = "left",
+})
+
+de.defstyle("tab-menuentry-big", {
+    based_on = "tab-menuentry",
+    font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*",
+    padding_pixels = 7,
+})
+
+de.defstyle("input", {
+    based_on = "*",
+    shadow_colour = "black",
+    highlight_colour = "black",
+    background_colour = "#3f3f3f",
+    foreground_colour = "white",
+    padding_pixels = 1,
+    highlight_pixels = 0,
+    shadow_pixels = 0,
+    border_style = "elevated",
+    de.substyle("*-cursor", {
+        background_colour = "white",
+        foreground_colour = "#3f3f3f",
+    }),
+    de.substyle("*-selection", {
+        background_colour = "black",
+        foreground_colour = "white",
+    }),
+})
+
+dopath("lookcommon_clean")
+
+gr.refresh()
+
diff --git a/etc/lookcommon_clean.lua b/etc/lookcommon_clean.lua
new file mode 100644 (file)
index 0000000..cfb059a
--- /dev/null
@@ -0,0 +1,60 @@
+-- Settings common to some styles.
+
+de.defstyle("stdisp", {
+    based_on = "*",
+    shadow_pixels = 0,
+    highlight_pixels = 0,
+    text_align = "left",
+    background_colour = "#000000",
+    foreground_colour = "grey",
+    font="-misc-fixed-medium-r-*-*-13-*-*-*-*-60-*-*",
+    
+    de.substyle("important", {
+        foreground_colour = "green",
+    }),
+
+    de.substyle("critical", {
+        foreground_colour = "red",
+    }),
+})
+
+de.defstyle("actnotify", {
+    based_on = "*",
+    shadow_colour = "#e0c0c0",
+    highlight_colour = "#e0c0c0",
+    background_colour = "#990000",
+    foreground_colour = "#eeeeee",
+})
+
+de.defstyle("tab-frame", {
+    based_on = "tab",
+    de.substyle("*-*-*-*-activity", {
+        shadow_colour = "#e0c0c0",
+        highlight_colour = "#e0c0c0",
+        background_colour = "#990000",
+        foreground_colour = "#eeeeee",
+    }),
+})
+
+de.defstyle("tab-frame-tiled", {
+    based_on = "tab-frame",
+    spacing = 1,
+})
+
+de.defstyle("frame-tiled", {
+    based_on = "frame",
+    shadow_pixels = 0,
+    highlight_pixels = 0,
+    padding_pixels = 0,
+    spacing = 1,
+})
+
+de.defstyle("frame-tiled-alt", {
+    based_on = "frame-tiled",
+    bar = "none",
+})
+
+de.defstyle("frame-floating", {
+    based_on = "frame",
+    bar = "shaped"
+})
diff --git a/etc/lookcommon_emboss.lua b/etc/lookcommon_emboss.lua
new file mode 100644 (file)
index 0000000..4c558e1
--- /dev/null
@@ -0,0 +1,57 @@
+-- Settings common to some styles.
+
+de.defstyle("stdisp", {
+    based_on = "*",
+    shadow_pixels = 0,
+    highlight_pixels = 0,
+    text_align = "left",
+
+    de.substyle("important", {
+        foreground_colour = "green",
+    }),
+
+    de.substyle("critical", {
+        foreground_colour = "red",
+    }),
+})
+
+de.defstyle("actnotify", {
+    based_on = "*",
+    shadow_colour = "#401010",
+    highlight_colour = "#eec0c0",
+    background_colour = "#990000",
+    foreground_colour = "#eeeeee",
+})
+
+de.defstyle("tab-frame", {
+    based_on = "tab",
+    de.substyle("*-*-*-*-activity", {
+        shadow_colour = "#401010",
+        highlight_colour = "#eec0c0",
+        background_colour = "#990000",
+        foreground_colour = "#eeeeee",
+    }),
+})
+
+de.defstyle("tab-frame-tiled", {
+    based_on = "tab-frame",
+    spacing = 1,
+})
+
+de.defstyle("frame-tiled", {
+    based_on = "frame",
+    border_style = "inlaid",
+    padding_pixels = 1,
+    spacing = 1,
+})
+
+de.defstyle("frame-floating", {
+    based_on = "frame",
+    border_style = "ridge",
+    bar = "shaped"
+})
+
+de.defstyle("frame-tiled-alt", {
+    based_on = "frame-tiled",
+    bar = "none",
+})
diff --git a/exact-version b/exact-version
new file mode 100644 (file)
index 0000000..d04e47f
--- /dev/null
@@ -0,0 +1,5 @@
+
+Context:
+
+[TAG ion-3ds-20061223
+Tuomo Valkonen <tuomov@iki.fi>**20061223145904] 
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..e9de238
--- /dev/null
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+               chmodcmd=""
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/ion/Makefile b/ion/Makefile
new file mode 100644 (file)
index 0000000..a79fa9f
--- /dev/null
@@ -0,0 +1,61 @@
+##
+## Ion Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+# List of modules to possibly preload
+include $(TOPDIR)/modulelist.mk
+
+######################################
+
+SOURCES=ion.c
+
+TARGETS=ion3
+
+INCLUDES += $(X11_INCLUDES) 
+INCLUDES += $(LIBMAINLOOP_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES)
+INCLUDES += -I..
+
+LIBS += $(X11_LIBS) $(XINERAMA_LIBS)
+LIBS += $(WHOLEA) $(LIBMAINLOOP_LIBS) $(LIBEXTL_LIBS) $(LIBTU_LIBS) $(NO_WHOLEA)
+LIBS += $(LUA_LIBS) $(DL_LIBS)
+LIBS += -lm
+
+ifeq ($(PRELOAD_MODULES),1)
+EXT_OBJS += $(foreach mod, $(MODULE_LIST), ../$(mod)/$(mod).a)
+DEPEND_DEPENDS += preload.c
+SOURCES += preload.c
+TO_CLEAN += preload.c
+LIBS += -lSM -lICE
+else
+LINKOPTS = $(EXPORT_DYNAMIC)
+WHOLEA = -Wl,-whole-archive
+NO_WHOLEA = -Wl,-no-whole-archive
+endif
+
+EXT_OBJS += ../ioncore/ioncore.a
+
+DEFINES += -DETCDIR=\"$(ETCDIR)\" -DSHAREDIR=\"$(SHAREDIR)\" \
+           -DEXTRABINDIR=\"$(EXTRABINDIR)\" -DMODULEDIR=\"$(MODULEDIR)\" \
+          -DLCDIR=\"$(LCDIR)\" -DLOCALEDIR=\"$(LOCALEDIR)\"
+
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+ion3: $(OBJS) $(EXT_OBJS)
+       $(CC) $(LINKOPTS) $(OBJS) $(WHOLEA) $(EXT_OBJS) $(NO_WHOLEA) $(LDFLAGS) -o $@
+
+preload.c:
+       $(LUA) ../build/mkpreload.lua $(MODULE_LIST) > preload.c
+
+_install:
+       $(INSTALLDIR) $(BINDIR)
+       $(INSTALL) -m $(BIN_MODE) ion3 $(BINDIR)
diff --git a/ion/ion.c b/ion/ion.c
new file mode 100644 (file)
index 0000000..93a5977
--- /dev/null
+++ b/ion/ion.c
@@ -0,0 +1,293 @@
+/*
+ * ion/ion/ion.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <libtu/util.h>
+#include <libtu/optparser.h>
+#include <libtu/errorlog.h>
+#include <libextl/readconfig.h>
+#include <libmainloop/exec.h>
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/ioncore.h>
+#include <ioncore/exec.h>
+#include <ioncore/event.h>
+#include "../version.h"
+
+
+/* Options. Getopt is not used because getopt_long is quite gnu-specific
+ * and they don't know of '-display foo' -style args anyway.
+ * Instead, I've reinvented the wheel in libtu :(.
+ */
+static OptParserOpt ion_opts[]={
+    {OPT_ID('d'), "display",  OPT_ARG, "host:dpy.scr", 
+     DUMMY_TR("X display to use")},
+    
+    {'c', "conffile", OPT_ARG, "config_file", 
+     DUMMY_TR("Configuration file")},
+    
+    {'s', "searchdir", OPT_ARG, "dir", 
+     DUMMY_TR("Add directory to search path")},
+
+    {OPT_ID('o'), "oneroot",  0, NULL,
+     DUMMY_TR("Manage default root window/non-Xinerama screen only")},
+
+#if defined(CF_XINERAMA) || defined(CF_SUN_XINERAMA)
+    {OPT_ID('x'), "xinerama", OPT_ARG, "1|0", 
+     DUMMY_TR("Use Xinerama screen information (default: 1/yes)")},
+#else
+    {OPT_ID('x'), "xinerama", OPT_ARG, "?", 
+     DUMMY_TR("Ignored: not compiled with Xinerama support")},
+#endif
+    
+    {OPT_ID('s'), "session",  OPT_ARG, "session_name", 
+     DUMMY_TR("Name of session (affects savefiles)")},
+    
+    {OPT_ID('S'), "smclientid", OPT_ARG, "client_id", 
+     DUMMY_TR("Session manager client ID")},
+
+    {OPT_ID('N'), "noerrorlog", 0, NULL, 
+     DUMMY_TR("Do not create startup error log and display it "
+              "with xmessage.")},
+    
+    {'h', "help", 0, NULL, 
+     DUMMY_TR("Show this help")},
+    
+    {'V', "version", 0, NULL,
+     DUMMY_TR("Show program version")},
+    
+    {OPT_ID('a'), "about", 0, NULL,
+     DUMMY_TR("Show about text")},
+    
+    END_OPTPARSEROPTS
+};
+
+
+void check_new_user_help()
+{
+    const char *userdir=extl_userdir();
+    char *oldbeard=NULL;
+    char *tmp=NULL, *cmd=NULL;
+    pid_t pid;
+    bool ret;
+
+    if(userdir==NULL){
+        warn(TR("Could not get user configuration file directory."));
+        return;
+    }
+    
+    libtu_asprintf(&oldbeard, "%s/.welcome_msg_displayed", userdir);
+    
+    if(oldbeard==NULL)
+        return;
+    
+    if(access(oldbeard, F_OK)==0){
+        free(oldbeard);
+        return;
+    }
+
+    libtu_asprintf(&tmp, TR("%s/welcome.txt"), SHAREDIR);
+    
+    if(tmp!=NULL){
+        if(access(tmp, F_OK)==0)
+            libtu_asprintf(&cmd, "%s %s", CF_XMESSAGE, tmp);
+        else
+            libtu_asprintf(&cmd, "%s %s/welcome.txt", CF_XMESSAGE, SHAREDIR);
+    
+        free(tmp);
+        
+        if(cmd!=NULL){
+            ret=ioncore_exec(cmd);
+    
+            free(cmd);
+    
+            if(ret){
+                /* This should actually be done when less or xmessage returns,
+                 * but that would mean yet another script...
+                 */
+                mkdir(userdir, 0700);
+                if(open(oldbeard, O_CREAT|O_RDWR, 0600)<0)
+                    warn_err_obj(oldbeard);
+            }
+        }
+    }
+
+    free(oldbeard);
+}
+
+
+static void help()
+{
+    int i;
+    printf(TR("Usage: %s [options]\n\n"), prog_execname());
+    for(i=0; ion_opts[i].descr!=NULL; i++)
+        ion_opts[i].descr=TR(ion_opts[i].descr);
+    optparser_printhelp(OPTP_MIDLONG, ion_opts);
+    printf("\n");
+}
+
+
+int main(int argc, char*argv[])
+{
+    const char *cfgfile="cfg_ion";
+    const char *display=NULL;
+    char *cmd=NULL;
+    int stflags=0;
+    int opt;
+    ErrorLog el;
+    FILE *ef=NULL;
+    char *efnam=NULL;
+    bool may_continue=FALSE;
+    bool noerrorlog=FALSE;
+
+    libtu_init(argv[0]);
+
+    if(!ioncore_init("ion3", argc, argv, LOCALEDIR))
+        return EXIT_FAILURE;
+
+    extl_add_searchdir(EXTRABINDIR); /* ion-completefile */
+    extl_add_searchdir(MODULEDIR);
+    extl_add_searchdir(ETCDIR);
+    extl_add_searchdir(SHAREDIR);
+    extl_add_searchdir(LCDIR);
+    extl_set_userdirs("ion3");
+
+    optparser_init(argc, argv, OPTP_MIDLONG, ion_opts);
+    
+    while((opt=optparser_get_opt())){
+        switch(opt){
+        case OPT_ID('d'):
+            display=optparser_get_arg();
+            break;
+        case 'c':
+            cfgfile=optparser_get_arg();
+            break;
+        case 's':
+            extl_add_searchdir(optparser_get_arg());
+            break;
+        case OPT_ID('S'):
+            ioncore_g.sm_client_id=optparser_get_arg();
+            break;
+        case OPT_ID('o'):
+            stflags|=IONCORE_STARTUP_ONEROOT;
+            break;
+        case OPT_ID('x'):
+            {
+                const char *p=optparser_get_arg();
+                if(strcmp(p, "1")==0)
+                    stflags&=~IONCORE_STARTUP_NOXINERAMA;
+                else if(strcmp(p, "0")==0)
+                    stflags|=IONCORE_STARTUP_NOXINERAMA;
+                else
+                    warn(TR("Invalid parameter to -xinerama."));
+            }
+            break;
+        case OPT_ID('s'):
+            extl_set_sessiondir(optparser_get_arg());
+            break;
+        case OPT_ID('N'):
+            noerrorlog=TRUE;
+            break;
+        case 'h':
+            help();
+            return EXIT_SUCCESS;
+        case 'V':
+            printf("%s\n", ION_VERSION);
+            return EXIT_SUCCESS;
+        case OPT_ID('a'):
+            printf("%s\n", ioncore_aboutmsg());
+            return EXIT_SUCCESS;
+        default:
+            warn(TR("Invalid command line."));
+            help();
+            return EXIT_FAILURE;
+        }
+    }
+
+    if(!noerrorlog){
+        /* We may have to pass the file to xmessage so just using tmpfile()
+         * isn't sufficient.
+         */
+        libtu_asprintf(&efnam, "%s/ion-%d-startup-errorlog", P_tmpdir,
+                       getpid());
+        if(efnam==NULL){
+            warn_err();
+        }else{
+            ef=fopen(efnam, "wt");
+            if(ef==NULL){
+                warn_err_obj(efnam);
+                free(efnam);
+                efnam=NULL;
+            }else{
+                cloexec_braindamage_fix(fileno(ef));
+                fprintf(ef, TR("Ion startup error log:\n"));
+                errorlog_begin_file(&el, ef);
+            }
+        }
+    }
+
+    if(ioncore_startup(display, cfgfile, stflags))
+        may_continue=TRUE;
+
+fail:
+    if(!may_continue)
+        warn(TR("Refusing to start due to encountered errors."));
+    else
+        check_new_user_help();
+    
+    if(ef!=NULL){
+        pid_t pid=-1;
+        if(errorlog_end(&el) && ioncore_g.dpy!=NULL){
+            fclose(ef);
+            pid=fork();
+            if(pid==0){
+                ioncore_setup_environ(DefaultScreen(ioncore_g.dpy));
+                if(!may_continue)
+                    XCloseDisplay(ioncore_g.dpy);
+                else
+                    close(ioncore_g.conn);
+                libtu_asprintf(&cmd, CF_XMESSAGE " %s", efnam);
+                if(cmd==NULL){
+                    warn_err();
+                }else if(system(cmd)==-1){
+                    warn_err_obj(cmd);
+                }
+                unlink(efnam);
+                exit(EXIT_SUCCESS);
+            }
+            if(!may_continue && pid>0)
+                waitpid(pid, NULL, 0);
+        }else{
+            fclose(ef);
+        }
+        if(pid<0)
+            unlink(efnam);
+        free(efnam);
+    }
+
+    if(!may_continue)
+        return EXIT_FAILURE;
+    
+    ioncore_mainloop();
+    
+    /* The code should never return here */
+    return EXIT_SUCCESS;
+}
diff --git a/ioncore/Makefile b/ioncore/Makefile
new file mode 100644 (file)
index 0000000..c0bd8d9
--- /dev/null
@@ -0,0 +1,53 @@
+##
+## Ioncore Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES=binding.c conf-bindings.c cursor.c event.c exec.c focus.c         \
+       strings.c key.c modules.c mwmhints.c pointer.c property.c         \
+       screen.c sizehint.c window.c ioncore.c                            \
+       xic.c selection.c clientwin.c colormap.c region.c eventh.c        \
+       attach.c resize.c grab.c manage.c regbind.c                       \
+       rootwin.c tags.c names.c saveload.c frame.c                       \
+       frame-pointer.c conf.c reginfo.c extlconv.c fullscreen.c mplex.c  \
+       bindmaps.c gr.c infowin.c activity.c netwm.c frame-draw.c         \
+       kbresize.c rectangle.c xwindow.c presize.c extlrx.c               \
+        pholder.c mplexpholder.c llist.c basicpholder.c sizepolicy.c      \
+        stacking.c group.c grouppholder.c group-cw.c navi.c              \
+        group-ws.c float-placement.c groupedpholder.c framedpholder.c
+
+LUA_SOURCES=\
+       ioncore_ext.lua ioncore_luaext.lua ioncore_bindings.lua \
+       ioncore_winprops.lua ioncore_misc.lua ioncore_efbb.lua \
+       ioncore_wd.lua ioncore_menudb.lua
+
+ifeq ($(PRELOAD_MODULES),1)
+CFLAGS += -DCF_PRELOAD_MODULES
+endif
+
+MAKE_EXPORTS=ioncore
+
+TARGETS=ioncore.a
+
+include $(TOPDIR)/libmainloop/rx.mk
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+ioncore.a: $(OBJS)
+       $(AR) $(ARFLAGS) $@ $+
+       $(RANLIB) $@
+
+_install: lc_install
diff --git a/ioncore/activity.c b/ioncore/activity.c
new file mode 100644 (file)
index 0000000..0f7b7a6
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * ion/ioncore/activity.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/setparam.h>
+#include <libtu/minmax.h>
+#include <libtu/objlist.h>
+#include "common.h"
+#include "global.h"
+#include "region.h"
+#include "activity.h"
+
+
+static ObjList *actlist=NULL;
+
+
+void region_mark_mgd_activity(WRegion *mgr)
+{
+    bool mgr_marked;
+    
+    if(mgr==NULL)
+        return;
+    
+    mgr_marked=region_is_activity_r(mgr);
+    mgr->mgd_activity++;
+    
+    if(!mgr_marked){
+        region_notify_change(mgr, "sub-activity");
+        region_mark_mgd_activity(REGION_MANAGER(mgr));
+    }
+}
+
+
+void region_clear_mgd_activity(WRegion *mgr)
+{
+    if(mgr==NULL)
+        return;
+    
+    mgr->mgd_activity=maxof(0, mgr->mgd_activity-1);
+    
+    if(!region_is_activity_r(mgr)){
+        region_notify_change(mgr, "sub-activity");
+        region_clear_mgd_activity(REGION_MANAGER(mgr));
+    }
+}
+    
+    
+static void propagate_activity(WRegion *reg)
+{
+    region_mark_mgd_activity(REGION_MANAGER(reg));
+}
+
+
+static void propagate_clear(WRegion *reg)
+{
+    region_clear_mgd_activity(REGION_MANAGER(reg));
+}
+
+
+bool region_set_activity(WRegion *reg, int sp)
+{
+    bool set=(reg->flags&REGION_ACTIVITY);
+    bool nset=libtu_do_setparam(sp, set);
+    
+    if(!XOR(set, nset))
+        return nset;
+
+    if(nset){
+        if(REGION_IS_ACTIVE(reg))
+            return FALSE;
+    
+        reg->flags|=REGION_ACTIVITY;
+        objlist_insert_last(&actlist, (Obj*)reg);
+        
+        if(reg->mgd_activity==0)
+            propagate_activity(reg);
+    }else{
+        reg->flags&=~REGION_ACTIVITY;
+        objlist_remove(&actlist, (Obj*)reg);
+        
+        if(reg->mgd_activity==0)
+            propagate_clear(reg);
+    }
+    
+    region_notify_change(reg, "activity");
+    
+    return nset;
+}
+
+
+/*EXTL_DOC
+ * Set activity flag of \var{reg}. The \var{how} parameter most be
+ * one of (set/unset/toggle).
+ */
+EXTL_EXPORT_AS(WRegion, set_activity)
+bool region_set_activity_extl(WRegion *reg, const char *how)
+{
+    return region_set_activity(reg, libtu_string_to_setparam(how));
+}
+
+
+/*EXTL_DOC
+ * Is activity notification set on \var{reg}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+bool region_is_activity(WRegion *reg)
+{
+    return (reg->flags&REGION_ACTIVITY);
+}
+
+
+bool region_is_activity_r(WRegion *reg)
+{
+    return (reg->flags&REGION_ACTIVITY || reg->mgd_activity!=0);
+}
+
+
+/*EXTL_DOC
+ * Return list of regions with activity/urgency bit set.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab ioncore_activity_list()
+{
+    ExtlTab t=extl_create_table();
+    ObjListIterTmp tmp;
+    Obj *obj;
+    int i=1;
+    
+    FOR_ALL_ON_OBJLIST(Obj*, obj, actlist, tmp){
+        extl_table_seti_o(t, i, obj);
+    }
+    
+    return t;
+}
+
+
+/*EXTL_DOC
+ * Return first regio non activity list.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+WRegion *ioncore_activity_first()
+{
+    if(actlist==NULL)
+        return NULL;
+    return (WRegion*)actlist->watch.obj;
+}
+
+
+/*EXTL_DOC
+ * Go to first region on activity list.
+ */
+EXTL_EXPORT
+bool ioncore_goto_activity()
+{
+    WRegion *reg=ioncore_activity_first();
+    
+    if(reg!=NULL)
+        return region_goto(reg);
+    else
+        return FALSE;
+}
+
diff --git a/ioncore/activity.h b/ioncore/activity.h
new file mode 100644 (file)
index 0000000..912e883
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * ion/ioncore/activity.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_ACTIVITY_H
+#define ION_IONCORE_ACTIVITY_H
+
+#include <libtu/setparam.h>
+#include <libmainloop/hooks.h>
+#include <libextl/extl.h>
+#include "region.h"
+
+extern bool region_set_activity(WRegion *reg, int sp);
+extern bool region_is_activity(WRegion* re);
+extern bool region_is_activity_r(WRegion *reg);
+
+extern void region_mark_mgd_activity(WRegion *mgr);
+extern void region_clear_mgd_activity(WRegion *mgr);
+
+extern ExtlTab ioncore_activity_list();
+extern WRegion *ioncore_activity_first();
+extern bool ioncore_goto_activity();
+
+#endif /* ION_IONCORE_ACTIVITY_H */
diff --git a/ioncore/attach.c b/ioncore/attach.c
new file mode 100644 (file)
index 0000000..23004cb
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * ion/ioncore/attach.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <limits.h>
+
+#include "common.h"
+#include "global.h"
+#include "region.h"
+#include "attach.h"
+#include "clientwin.h"
+#include "saveload.h"
+#include "manage.h"
+#include "extlconv.h"
+#include "names.h"
+
+
+/*{{{ Helper */
+
+
+static WRegion *doit_new(WRegion *mgr,
+                         WWindow *par, const WFitParams *fp,
+                         WRegionDoAttachFn *cont, void *cont_param,
+                         WRegionCreateFn *fn, void *fn_param)
+{
+    WRegion *reg=fn(par, fp, fn_param);
+    
+    if(reg==NULL)
+        return NULL;
+    
+    if(!cont(mgr, reg, cont_param)){
+        destroy_obj((Obj*)reg);
+        return NULL;
+    }
+    
+    return reg;
+}
+
+
+static WRegion *doit_reparent(WRegion *mgr,
+                              WWindow *par, const WFitParams *fp,
+                              WRegionDoAttachFn *cont, void *cont_param,
+                              WRegion *reg)
+{
+    WFitParams fp2;
+
+    if(!region_attach_reparent_check(mgr, reg))
+        return NULL;
+    
+    if(fp->mode&REGION_FIT_WHATEVER){
+        /* fp->g is not final; substitute size with current to avoid
+         * useless resizing. 
+         */
+        fp2.mode=fp->mode;
+        fp2.g.x=fp->g.x;
+        fp2.g.y=fp->g.y;
+        fp2.g.w=REGION_GEOM(reg).w;
+        fp2.g.h=REGION_GEOM(reg).h;
+        fp=&fp2;
+    }
+    
+    if(!region_fitrep(reg, par, fp)){
+        warn(TR("Unable to reparent."));
+        return NULL;
+    }
+    
+    region_detach_manager(reg);
+    
+    if(!cont(mgr, reg, cont_param)){
+        #warning "TODO: What?"
+        return NULL;
+    }
+       
+    return reg;
+}
+
+
+static WRegion *wrap_load(WWindow *par, const WFitParams *fp, 
+                          ExtlTab *tab)
+{
+    return create_region_load(par, fp, *tab);
+}
+
+
+static WRegion *doit_load(WRegion *mgr,
+                          WWindow *par, const WFitParams *fp,
+                          WRegionDoAttachFn *cont, void *cont_param,
+                          ExtlTab tab)
+{
+    WRegion *reg;
+    
+    if(extl_table_gets_o(tab, "reg", (Obj**)&reg)){
+        if(!OBJ_IS(reg, WRegion))
+            return FALSE;
+        return doit_reparent(mgr, par, fp, cont, cont_param, reg);
+    }
+    
+    return doit_new(mgr, par, fp, cont, cont_param,
+                    (WRegionCreateFn*)wrap_load, &tab);
+}
+
+WRegion *region_attach_helper(WRegion *mgr,
+                              WWindow *par, const WFitParams *fp,
+                              WRegionDoAttachFn *fn, void *fn_param,
+                              const WRegionAttachData *data)
+{
+    if(data->type==REGION_ATTACH_NEW){
+        return doit_new(mgr, par, fp, fn, fn_param, 
+                        data->u.n.fn, data->u.n.param);
+    }else if(data->type==REGION_ATTACH_LOAD){
+        return doit_load(mgr, par, fp, fn, fn_param, data->u.tab);
+    }else if(data->type==REGION_ATTACH_REPARENT){
+        return doit_reparent(mgr, par, fp, fn, fn_param, data->u.reg);
+    }else{
+        return NULL;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Reparent check */
+
+
+bool region_attach_reparent_check(WRegion *mgr, WRegion *reg)
+{
+    WRegion *reg2;
+    
+    /*if(REGION_MANAGER(reg)==mgr){
+        warn(TR("Same manager."));
+        return FALSE;
+    }*/
+    
+    /* Check that reg is not a parent or manager of mgr */
+    for(reg2=mgr; reg2!=NULL; reg2=REGION_MANAGER(reg2)){
+        if(reg2==reg)
+            goto err;
+    }
+    
+    for(reg2=REGION_PARENT_REG(mgr); reg2!=NULL; reg2=REGION_PARENT_REG(reg2)){
+        if(reg2==reg)
+            goto err;
+    }
+
+    return TRUE;
+    
+err:
+    warn(TR("Attempt to make region %s manage its ancestor %s."),
+         region_name(mgr), region_name(reg));
+    return FALSE;
+}
+
+
+/*}}}*/
+
+
diff --git a/ioncore/attach.h b/ioncore/attach.h
new file mode 100644 (file)
index 0000000..9183ee2
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * ion/ioncore/attach.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_ATTACH_H
+#define ION_IONCORE_ATTACH_H
+
+#include "region.h"
+#include "reginfo.h"
+#include "window.h"
+
+
+typedef WRegion *WRegionCreateFn(WWindow *parent, 
+                                 const WFitParams *fp, 
+                                 void *param);
+
+typedef WRegion *WRegionAttachFn(WRegion *reg, 
+                                 void *param, 
+                                 WRegionAttachData *data);
+
+
+typedef enum{
+    REGION_ATTACH_REPARENT,
+    REGION_ATTACH_NEW,
+    REGION_ATTACH_LOAD
+} WRegionAttachType;
+
+
+DECLSTRUCT(WRegionAttachData){
+    WRegionAttachType type;
+    union{
+        WRegion *reg;
+        struct{
+            WRegionCreateFn *fn;
+            void *param;
+        } n;
+        ExtlTab tab;
+    } u;
+};
+
+
+typedef bool WRegionDoAttachFn(WRegion *reg, WRegion *sub, void *param);
+typedef bool WRegionDoAttachFnSimple(WRegion *reg, WRegion *sub);
+
+extern WRegion *region_attach_helper(WRegion *mgr,
+                                     WWindow *par, const WFitParams *fp,
+                                     WRegionDoAttachFn *fn, void *fn_param,
+                                     const WRegionAttachData *data);
+
+extern bool region_attach_reparent_check(WRegion *mgr, WRegion *reg);
+
+#endif /* ION_IONCORE_ATTACH_H */
diff --git a/ioncore/basicpholder.c b/ioncore/basicpholder.c
new file mode 100644 (file)
index 0000000..b8b2e96
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * ion/ioncore/basicpholder.c
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/objp.h>
+#include <libtu/obj.h>
+#include <libtu/pointer.h>
+
+#include "basicpholder.h"
+
+
+/*{{{ Init/deinit */
+
+
+static void basicpholder_watch_handler(Watch *watch, Obj *reg)
+{
+    WBasicPHolder *ph=FIELD_TO_STRUCT(WBasicPHolder, reg_watch, watch);
+    pholder_redirect(&(ph->ph), (WRegion*)reg);
+}
+
+
+bool basicpholder_init(WBasicPHolder *ph, WRegion *reg, 
+                       WBasicPHolderHandler *hnd)
+{
+    assert(reg!=NULL && hnd!=NULL);
+    
+    pholder_init(&(ph->ph));
+
+    watch_init(&(ph->reg_watch));
+    
+    if(!watch_setup(&(ph->reg_watch), (Obj*)reg, basicpholder_watch_handler)){
+        pholder_deinit(&(ph->ph));
+        return FALSE;
+    }
+    
+    ph->hnd=hnd;
+    
+    return TRUE;
+}
+
+WBasicPHolder *create_basicpholder(WRegion *reg, 
+                                   WBasicPHolderHandler *hnd)
+{
+    CREATEOBJ_IMPL(WBasicPHolder, basicpholder, (p, reg, hnd));
+}
+
+
+void basicpholder_deinit(WBasicPHolder *ph)
+{
+    watch_reset(&(ph->reg_watch));
+    pholder_deinit(&(ph->ph));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuns */
+
+
+WRegion *basicpholder_do_attach(WBasicPHolder *ph, int flags,
+                                WRegionAttachData *data)
+{
+    WRegion *reg=(WRegion*)ph->reg_watch.obj;
+
+    if(reg==NULL || ph->hnd==NULL)
+        return FALSE;
+
+    return ph->hnd(reg, flags, data);
+}
+
+
+bool basicpholder_do_goto(WBasicPHolder *ph)
+{
+    WRegion *reg=(WRegion*)ph->reg_watch.obj;
+    
+    if(reg!=NULL)
+        return region_goto((WRegion*)reg);
+    
+    return FALSE;
+}
+
+
+WRegion *basicpholder_do_target(WBasicPHolder *ph)
+{
+    return (WRegion*)ph->reg_watch.obj;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Class information */
+
+
+static DynFunTab basicpholder_dynfuntab[]={
+    {(DynFun*)pholder_do_attach, 
+     (DynFun*)basicpholder_do_attach},
+
+    {(DynFun*)pholder_do_goto, 
+     (DynFun*)basicpholder_do_goto},
+
+    {(DynFun*)pholder_do_target, 
+     (DynFun*)basicpholder_do_target},
+    
+    END_DYNFUNTAB
+};
+
+IMPLCLASS(WBasicPHolder, WPHolder, basicpholder_deinit, 
+          basicpholder_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/ioncore/basicpholder.h b/ioncore/basicpholder.h
new file mode 100644 (file)
index 0000000..5008537
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * ion/ioncore/basicpholder.h
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_BASICPHOLDER_H
+#define ION_IONCORE_BASICPHOLDER_H
+
+#include "common.h"
+#include "pholder.h"
+#include "attach.h"
+
+
+typedef WRegion *WBasicPHolderHandler(WRegion *reg, int flags,
+                                      WRegionAttachData *data);
+
+INTRCLASS(WBasicPHolder);
+
+DECLCLASS(WBasicPHolder){
+    WPHolder ph;
+    Watch reg_watch;
+    WBasicPHolderHandler* hnd;
+};
+
+extern WBasicPHolder *create_basicpholder(WRegion *reg,
+                                          WBasicPHolderHandler *hnd);
+
+extern bool basicpholder_init(WBasicPHolder *ph, WRegion *reg,
+                              WBasicPHolderHandler *hnd);
+
+extern void basicpholder_deinit(WBasicPHolder *ph);
+
+extern bool basicpholder_do_goto(WBasicPHolder *ph);
+
+extern WRegion *basicpholder_do_target(WBasicPHolder *ph);
+
+extern WRegion *basicpholder_do_attach(WBasicPHolder *ph, int flags,
+                                       WRegionAttachData *data);
+
+#endif /* ION_IONCORE_BASICPHOLDER_H */
diff --git a/ioncore/binding.c b/ioncore/binding.c
new file mode 100644 (file)
index 0000000..4d03abd
--- /dev/null
@@ -0,0 +1,745 @@
+/*
+ * ion/ioncore/binding.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include "common.h"
+#include "event.h"
+#include "binding.h"
+#include "global.h"
+#include <libtu/objp.h>
+#include "regbind.h"
+#include <libextl/extl.h>
+
+
+#ifndef CF_NO_LOCK_HACK
+#define CF_HACK_IGNORE_EVIL_LOCKS
+#endif
+
+#ifdef CF_HACK_IGNORE_EVIL_LOCKS
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+#endif
+
+
+/* */
+
+
+#define N_MODS 8
+
+static const uint modmasks[N_MODS]={
+    ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask,
+    Mod4Mask, Mod5Mask
+};
+
+static XModifierKeymap *modmap=NULL;
+
+#define KNOWN_MODIFIERS_MASK (ShiftMask|LockMask|ControlMask|Mod1Mask|\
+                              Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)
+
+#ifdef CF_HACK_IGNORE_EVIL_LOCKS
+
+#define N_EVILLOCKS 3
+#define N_LOOKUPEVIL 2
+
+static uint evillockmasks[N_EVILLOCKS]={
+     0, 0, LockMask
+};
+
+static const KeySym evillocks[N_LOOKUPEVIL]={
+    XK_Num_Lock, XK_Scroll_Lock
+};
+
+static uint evilignoremask=LockMask;
+
+static void lookup_evil_locks();
+
+static void evil_grab_key(Display *display, uint keycode, uint modifiers,
+                          Window grab_window, bool owner_events,
+                          int pointer_mode, int keyboard_mode);
+
+static void evil_grab_button(Display *display, uint button, uint modifiers,
+                             Window grab_window, bool owner_events,
+                             uint event_mask, int pointer_mode,
+                             int keyboard_mode, Window confine_to,
+                             Cursor cursor);
+
+static void evil_ungrab_key(Display *display, uint keycode, uint modifiers,
+                            Window grab_window);
+
+static void evil_ungrab_button(Display *display, uint button, uint modifiers,
+                               Window grab_window);
+
+#endif
+
+
+#define CVAL(A, B, V) ( A->V < B->V ? -1 : (A->V > B->V ? 1 : 0))
+
+static int compare_bindings(const WBinding *a, const WBinding *b)
+{
+    int r=CVAL(a, b, act);
+    if(r==0){
+        r=CVAL(a, b, kcb);
+        if(r==0){
+            r=CVAL(a, b, state);
+            if(r==0){
+                r=CVAL(a, b, area);
+            }
+        }
+    }
+    return r;
+}
+
+/* This is only used for searching AnyKey etc. */
+static int compare_bindings_ksb(const WBinding *a, const WBinding *b)
+{
+    int r=CVAL(a, b, act);
+    if(r==0){
+        r=CVAL(a, b, ksb);
+        if(r==0){
+            r=CVAL(a, b, state);
+            if(r==0){
+                r=CVAL(a, b, area);
+            }
+        }
+    }
+    return r;
+}
+                    
+#undef CVAL
+
+
+bool init_bindmap(WBindmap *bindmap)
+{
+    bindmap->rbind_list=NULL;
+    bindmap->areamap=NULL;
+    bindmap->nbindings=0;
+    bindmap->bindings=make_rb();
+    if(bindmap->bindings==NULL){
+        warn_err();
+        return FALSE;
+    }
+    return TRUE;
+}
+
+
+WBindmap *create_bindmap()
+{
+    WBindmap *bindmap=ALLOC(WBindmap);
+    
+    if(bindmap==NULL){
+        warn_err();
+        return NULL;
+    }
+    
+    if(!init_bindmap(bindmap)){
+        free(bindmap);
+        return NULL;
+    }
+    
+    return bindmap;
+}
+
+
+void binding_deinit(WBinding *binding)
+{
+    if(binding->submap!=NULL){
+        bindmap_destroy(binding->submap);
+        binding->submap=NULL;
+    }
+
+    binding->func=extl_unref_fn(binding->func);
+}
+
+
+static void do_destroy_binding(WBinding *binding)
+{
+    assert(binding!=NULL);
+    binding_deinit(binding);
+    free(binding);
+}
+
+
+static void bindmap_deinit(WBindmap *bindmap)
+{
+    WBinding *b=NULL;
+    Rb_node node=NULL;
+
+    while(bindmap->rbind_list!=NULL){
+        region_remove_bindmap(bindmap->rbind_list->reg,
+                              bindmap);
+    }
+        
+    if(bindmap->bindings==NULL)
+        return;
+    
+    FOR_ALL_BINDINGS(b, node, bindmap->bindings){
+        do_destroy_binding((WBinding*)rb_val(node));
+        bindmap->nbindings--;
+    }
+
+    assert(bindmap->nbindings==0);
+    
+    rb_free_tree(bindmap->bindings);
+    bindmap->bindings=NULL;
+}
+
+
+void bindmap_destroy(WBindmap *bindmap)
+{
+    bindmap_deinit(bindmap);
+    free(bindmap);
+}
+
+
+static void free_map(Rb_node map)
+{
+    Rb_node node;
+    WBinding *b;
+    
+    FOR_ALL_BINDINGS(b, node, map)
+        free(b);
+    
+    rb_free_tree(map);
+}
+
+
+void bindmap_refresh(WBindmap *bindmap)
+{
+    WRegBindingInfo *rbind;
+    Rb_node newtree, node;
+    WBinding *b, *b2;
+    
+    if(bindmap->bindings==NULL)
+        return;
+    
+    newtree=make_rb();
+    
+    if(newtree==NULL){
+        warn_err();
+        return;
+    }
+    
+    FOR_ALL_BINDINGS(b, node, bindmap->bindings){
+        b2=ALLOC(WBinding);
+        if(b2==NULL){
+            warn_err();
+            free_map(newtree);
+            return;
+        }
+        
+        *b2=*b;
+
+        if(b->act==BINDING_KEYPRESS){
+            for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
+                rbind_binding_removed(rbind, b, bindmap);
+            b2->kcb=XKeysymToKeycode(ioncore_g.dpy, b->ksb);
+        }
+        
+        if(!rb_insertg(newtree, b2, b2, (Rb_compfn*)compare_bindings)){
+            warn_err();
+            free(b2);
+            free_map(newtree);
+            return;
+        }
+    }
+
+    free_map(bindmap->bindings);
+    bindmap->bindings=newtree;
+
+    FOR_ALL_BINDINGS(b, node, bindmap->bindings){
+        if(b->act!=BINDING_KEYPRESS)
+            continue;
+        for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
+            rbind_binding_added(rbind, b, bindmap);
+        if(b->submap!=NULL)
+            bindmap_refresh(b->submap);
+    }
+}
+
+
+bool bindmap_add_binding(WBindmap *bindmap, const WBinding *b)
+{
+    WRegBindingInfo *rbind=NULL;
+    WBinding *binding=NULL;
+    Rb_node node=NULL;
+    int found=0;
+    
+    /* Handle adding the binding */
+    binding=ALLOC(WBinding);
+    
+    if(binding==NULL){
+        warn_err();
+        return FALSE;
+    }
+    
+    memcpy(binding, b, sizeof(*b));
+    
+    node=rb_find_gkey_n(bindmap->bindings, binding,
+                        (Rb_compfn*)compare_bindings, &found);
+    
+    if(found){
+        if(!rb_insert_a(node, binding, binding)){
+            free(binding);
+            return FALSE;
+        }
+        do_destroy_binding((WBinding*)rb_val(node));
+        rb_delete_node(node);
+        bindmap->nbindings--;
+    }else{
+        if(!rb_insertg(bindmap->bindings, binding, binding, 
+                       (Rb_compfn*)compare_bindings)){
+            free(binding);
+            return FALSE;
+        }
+    }
+
+    bindmap->nbindings++;
+    
+    for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
+        rbind_binding_added(rbind, binding, bindmap);
+
+    return TRUE;
+}
+
+
+bool bindmap_remove_binding(WBindmap *bindmap, const WBinding *b)
+{
+    WRegBindingInfo *rbind=NULL;
+    WBinding *binding=NULL;
+    Rb_node node=NULL;
+    int found=0;
+    
+    if(bindmap->bindings==NULL)
+        return FALSE;
+    
+    node=rb_find_gkey_n(bindmap->bindings, b, (Rb_compfn*)compare_bindings,
+                        &found);
+    
+    if(!found)
+        return FALSE;
+    
+    binding=(WBinding*)rb_val(node);
+    
+    for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
+        rbind_binding_removed(rbind, binding, bindmap);
+
+    do_destroy_binding(binding);
+    rb_delete_node(node);
+    
+    bindmap->nbindings--;
+
+    return TRUE;
+}
+
+
+void ioncore_init_bindings()
+{
+    modmap=XGetModifierMapping(ioncore_g.dpy);
+    
+    assert(modmap!=NULL);
+
+#ifdef CF_HACK_IGNORE_EVIL_LOCKS
+    lookup_evil_locks();
+#endif
+}
+
+
+void ioncore_update_modmap()
+{
+    XModifierKeymap *nm=XGetModifierMapping(ioncore_g.dpy);
+    
+    if(nm!=NULL){
+        XFreeModifiermap(modmap);
+        modmap=nm;
+    }
+}
+
+
+/* */
+
+
+void binding_grab_on(const WBinding *binding, Window win)
+{
+    if(binding->act==BINDING_KEYPRESS && binding->kcb!=0){
+#ifndef CF_HACK_IGNORE_EVIL_LOCKS
+        XGrabKey(ioncore_g.dpy, binding->kcb, binding->state, win,
+                 True, GrabModeAsync, GrabModeAsync);
+#else
+        evil_grab_key(ioncore_g.dpy, binding->kcb, binding->state, win,
+                      True, GrabModeAsync, GrabModeAsync);
+#endif
+    }
+    
+    if(binding->act!=BINDING_BUTTONPRESS &&
+       binding->act!=BINDING_BUTTONCLICK &&
+       binding->act!=BINDING_BUTTONDBLCLICK &&
+       binding->act!=BINDING_BUTTONMOTION)
+        return;
+    
+    if(binding->state==0)
+        return;
+    
+#ifndef CF_HACK_IGNORE_EVIL_LOCKS
+    XGrabButton(ioncore_g.dpy, binding->kcb, binding->state, win,
+                True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, GrabModeAsync,
+                None, None);
+#else
+    evil_grab_button(ioncore_g.dpy, binding->kcb, binding->state, win,
+                     True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, GrabModeAsync,
+                     None, None);
+#endif
+}
+
+
+void binding_ungrab_on(const WBinding *binding, Window win)
+{
+    if(binding->act==BINDING_KEYPRESS){
+#ifndef CF_HACK_IGNORE_EVIL_LOCKS
+        XUngrabKey(ioncore_g.dpy, binding->kcb, binding->state, win);
+#else
+        evil_ungrab_key(ioncore_g.dpy, binding->kcb, binding->state, win);
+#endif
+    }
+    
+    if(binding->act!=BINDING_BUTTONPRESS &&
+       binding->act!=BINDING_BUTTONCLICK &&
+       binding->act!=BINDING_BUTTONDBLCLICK &&
+       binding->act!=BINDING_BUTTONMOTION)
+        return;
+    
+    if(binding->state==0)
+        return;
+
+#ifndef CF_HACK_IGNORE_EVIL_LOCKS
+    XUngrabButton(ioncore_g.dpy, binding->kcb, binding->state, win);
+#else
+    evil_ungrab_button(ioncore_g.dpy, binding->kcb, binding->state, win);
+#endif
+}
+
+
+/* */
+
+
+static WBinding *search_binding(WBindmap *bindmap, WBinding *binding)
+{
+    Rb_node node;
+    int found=0;
+
+    if(bindmap->bindings==NULL)
+        return NULL;
+    
+    node=rb_find_gkey_n(bindmap->bindings, binding,
+                        (Rb_compfn*)compare_bindings, &found);
+    
+    if(found==0)
+        return NULL;
+    
+    return (WBinding*)rb_val(node);
+}
+
+
+static WBinding *search_binding_ksb(WBindmap *bindmap, WBinding *binding)
+{
+    Rb_node node;
+    int found=0;
+
+    if(bindmap->bindings==NULL)
+        return NULL;
+    
+    node=rb_find_gkey_n(bindmap->bindings, binding,
+                        (Rb_compfn*)compare_bindings_ksb, &found);
+    
+    if(found==0)
+        return NULL;
+    
+    return (WBinding*)rb_val(node);
+}
+
+
+static WBinding *do_bindmap_lookup_binding(WBindmap *bindmap,
+                                           int act, uint state, 
+                                           uint kcb, int area)
+{
+    WBinding *binding, tmp;
+
+    if(bindmap->nbindings==0)
+        return NULL;
+
+#ifdef CF_HACK_IGNORE_EVIL_LOCKS
+    state&=~evilignoremask;
+#endif
+    state&=KNOWN_MODIFIERS_MASK;
+    
+    tmp.act=act;
+    tmp.kcb=kcb;
+    tmp.state=state;
+    tmp.area=area;
+    
+    binding=search_binding(bindmap, &tmp);
+
+    if(binding==NULL){
+        tmp.state=AnyModifier;
+        binding=search_binding(bindmap, &tmp);
+
+        if(binding==NULL){
+            tmp.state=state;
+            tmp.ksb=(act==BINDING_KEYPRESS ? AnyKey : AnyButton);
+            
+            binding=search_binding_ksb(bindmap, &tmp);
+
+            if(binding==NULL){
+                tmp.state=AnyModifier;
+                binding=search_binding_ksb(bindmap, &tmp);
+            }
+        }
+    }
+                
+    return binding;
+}
+
+
+WBinding *bindmap_lookup_binding(WBindmap *bindmap,
+                                 int act, uint state, uint kcb)
+{
+    return do_bindmap_lookup_binding(bindmap, act, state, kcb, 0);
+}
+
+
+WBinding *bindmap_lookup_binding_area(WBindmap *bindmap,
+                                      int act, uint state, uint kcb, int area)
+{
+    WBinding *binding;
+    
+    binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, area);
+    
+    if(binding==NULL)
+        binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, 0);
+    
+    return binding;
+}
+
+    
+/*
+ * A dirty hack to deal with (==ignore) evil locking modifier keys.
+ */
+
+
+int ioncore_unmod(int state, int keycode)
+{
+    int j;
+    
+#ifdef CF_HACK_IGNORE_EVIL_LOCKS
+    state&=~evilignoremask;
+#endif
+
+    for(j=0; j<N_MODS*modmap->max_keypermod; j++){
+        if(modmap->modifiermap[j]==keycode)
+            return state&~modmasks[j/modmap->max_keypermod];
+    }
+    
+    return state;
+}
+
+
+int ioncore_modstate()
+{
+       char keys[32];
+       int state=0;
+       int j;
+       
+       XQueryKeymap(ioncore_g.dpy, keys);
+    
+       for(j=0; j<N_MODS*modmap->max_keypermod; j++){
+        int a=(modmap->modifiermap[j]&7);
+               int b=(modmap->modifiermap[j]>>3);
+               if(b<32){
+                       if(keys[b]&(1<<a))
+                               state|=modmasks[j/modmap->max_keypermod];
+               }
+    }
+
+#ifdef CF_HACK_IGNORE_EVIL_LOCKS
+    state&=~evilignoremask;
+#endif
+    return state;
+}
+
+
+bool ioncore_ismod(int keycode)
+{
+    int j;
+    
+    for(j=0; j<N_MODS*modmap->max_keypermod; j++){
+        if(modmap->modifiermap[j]==keycode)
+            return TRUE;
+    }
+    
+    return FALSE;
+}
+    
+
+#ifdef CF_HACK_IGNORE_EVIL_LOCKS
+
+static void lookup_evil_locks()
+{
+    uint keycodes[N_LOOKUPEVIL];
+    int i, j;
+    
+    for(i=0; i<N_LOOKUPEVIL; i++)
+        keycodes[i]=XKeysymToKeycode(ioncore_g.dpy, evillocks[i]);
+    
+    for(j=0; j<N_MODS*modmap->max_keypermod; j++){
+        for(i=0; i<N_LOOKUPEVIL; i++){
+            if(keycodes[i]==None)
+                continue;
+            if(modmap->modifiermap[j]==keycodes[i]){
+                evillockmasks[i]=modmasks[j/modmap->max_keypermod];
+                evilignoremask|=evillockmasks[i];
+            }
+        }
+    }
+}
+
+
+static void evil_grab_key(Display *display, uint keycode, uint modifiers,
+                          Window grab_window, bool owner_events,
+                          int pointer_mode, int keyboard_mode)
+{
+    uint mods;
+    int i, j;
+    
+    XGrabKey(display, keycode, modifiers, grab_window, owner_events,
+             pointer_mode, keyboard_mode);
+
+    if(modifiers==AnyModifier)
+        return;
+    
+    for(i=0; i<N_EVILLOCKS; i++){
+        if(evillockmasks[i]==0)
+            continue;
+        mods=modifiers;
+        for(j=i; j<N_EVILLOCKS; j++){
+            if(evillockmasks[j]==0)
+                continue;            
+            mods|=evillockmasks[j];            
+            XGrabKey(display, keycode, mods,
+                     grab_window, owner_events, pointer_mode, keyboard_mode);
+            if(i==j)
+                continue;
+            XGrabKey(display, keycode,
+                     modifiers|evillockmasks[i]|evillockmasks[j],
+                     grab_window, owner_events, pointer_mode, keyboard_mode);
+        }
+    }    
+}
+
+
+static void evil_grab_button(Display *display, uint button, uint modifiers,
+                             Window grab_window, bool owner_events,
+                             uint event_mask, int pointer_mode,
+                             int keyboard_mode, Window confine_to,
+                             Cursor cursor)
+{
+    uint mods;
+    int i, j;
+
+    XGrabButton(display, button, modifiers,
+                grab_window, owner_events, event_mask, pointer_mode,
+                keyboard_mode, confine_to, cursor);
+
+    if(modifiers==AnyModifier)
+        return;
+    
+    for(i=0; i<N_EVILLOCKS; i++){
+        if(evillockmasks[i]==0)
+            continue;
+        mods=modifiers;
+        for(j=i; j<N_EVILLOCKS; j++){            
+            if(evillockmasks[j]==0)
+                continue;            
+            mods|=evillockmasks[j];            
+            XGrabButton(display, button, mods,
+                        grab_window, owner_events, event_mask, pointer_mode,
+                        keyboard_mode, confine_to, cursor);
+            if(i==j)
+                continue;
+            XGrabButton(display, button,
+                        modifiers|evillockmasks[i]|evillockmasks[j],
+                        grab_window, owner_events, event_mask, pointer_mode,
+                        keyboard_mode, confine_to, cursor);
+        }            
+    }
+}
+
+
+static void evil_ungrab_key(Display *display, uint keycode, uint modifiers,
+                            Window grab_window)
+{
+    uint mods;
+    int i, j;
+    
+    XUngrabKey(display, keycode, modifiers, grab_window);
+
+    if(modifiers==AnyModifier)
+        return;
+    
+    for(i=0; i<N_EVILLOCKS; i++){
+        if(evillockmasks[i]==0)
+            continue;
+        mods=modifiers;
+        for(j=i; j<N_EVILLOCKS; j++){
+            if(evillockmasks[j]==0)
+                continue;            
+            mods|=evillockmasks[j];            
+            XUngrabKey(display, keycode, mods, grab_window);
+            if(i==j)
+                continue;
+            XUngrabKey(display, keycode,
+                       modifiers|evillockmasks[i]|evillockmasks[j],
+                       grab_window);
+        }
+    }    
+}
+
+
+static void evil_ungrab_button(Display *display, uint button, uint modifiers,
+                               Window grab_window)
+{
+    uint mods;
+    int i, j;
+    
+    XUngrabButton(display, button, modifiers, grab_window);
+
+    if(modifiers==AnyModifier)
+        return;
+    
+    for(i=0; i<N_EVILLOCKS; i++){
+        if(evillockmasks[i]==0)
+            continue;
+        mods=modifiers;
+        for(j=i; j<N_EVILLOCKS; j++){            
+            if(evillockmasks[j]==0)
+                continue;            
+            mods|=evillockmasks[j];            
+            XUngrabButton(display, button, mods, grab_window);
+            if(i==j)
+                continue;
+            XUngrabButton(display, button,
+                          modifiers|evillockmasks[i]|evillockmasks[j], 
+                          grab_window);
+        }            
+    }
+    
+}
+
+#endif /* CF_HACK_IGNORE_EVIL_LOCKS */
+
diff --git a/ioncore/binding.h b/ioncore/binding.h
new file mode 100644 (file)
index 0000000..8e13d34
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * ion/ioncore/binding.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_BINDING_H
+#define ION_IONCORE_BINDING_H
+
+#include <libtu/obj.h>
+#include <libtu/rb.h>
+#include <libtu/map.h>
+#include "common.h"
+#include "region.h"
+#include <libextl/extl.h>
+
+
+#define BINDING_KEYPRESS        0
+#define BINDING_BUTTONPRESS     1
+#define BINDING_BUTTONMOTION    2
+#define BINDING_BUTTONCLICK     3
+#define BINDING_BUTTONDBLCLICK  4
+
+#define BINDMAP_INIT        {0, NULL, NULL, NULL, NULL}
+
+#define FOR_ALL_BINDINGS(B, NODE, MAP) \
+        rb_traverse(NODE, MAP) if(((B)=(WBinding*)rb_val(NODE))!=NULL)
+
+INTRSTRUCT(WBinding);
+INTRSTRUCT(WBindmap);
+INTRSTRUCT(WRegBindingInfo);
+
+
+DECLSTRUCT(WBinding){
+    uint kcb; /* keycode or button */
+    uint ksb; /* keysym or button */
+    uint state;
+    uint act;
+    int area;
+    bool wait;
+    WBindmap *submap;
+    ExtlFn func;
+};
+
+
+
+DECLSTRUCT(WRegBindingInfo){
+    WBindmap *bindmap;
+    WRegBindingInfo *next, *prev;
+    WRegBindingInfo *bm_next, *bm_prev;
+    WRegion *reg;
+    WRegion *owner;
+    int tmp;
+};
+
+
+
+DECLSTRUCT(WBindmap){
+    int nbindings;
+    Rb_node bindings;
+    WRegBindingInfo *rbind_list;
+    const StringIntMap *areamap;
+};
+
+
+extern void ioncore_init_bindings();
+extern void ioncore_update_modmap();
+extern int ioncore_unmod(int state, int keycode);
+extern bool ioncore_ismod(int keycode);
+extern int ioncore_modstate();
+
+extern WBindmap *create_bindmap();
+
+extern void bindmap_destroy(WBindmap *bindmap);
+extern void bindmap_refresh(WBindmap *bindmap);
+extern bool bindmap_add_binding(WBindmap *bindmap, const WBinding *binding);
+extern bool bindmap_remove_binding(WBindmap *bindmap, const WBinding *binding);
+extern WBinding *bindmap_lookup_binding(WBindmap *bindmap, int act,
+                                uint state, uint kcb);
+extern WBinding *bindmap_lookup_binding_area(WBindmap *bindmap, int act,
+                                     uint state, uint kcb, int area);
+
+extern void binding_deinit(WBinding *binding);
+extern void binding_grab_on(const WBinding *binding, Window win);
+extern void binding_ungrab_on(const WBinding *binding, Window win);
+
+#endif /* ION_IONCORE_BINDING_H */
diff --git a/ioncore/bindmaps.c b/ioncore/bindmaps.c
new file mode 100644 (file)
index 0000000..dd4a1d0
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * ion/ioncore/bindmaps.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/rb.h>
+#include "common.h"
+#include "conf-bindings.h"
+#include "binding.h"
+#include <libextl/extl.h>
+#include "framep.h"
+#include "bindmaps.h"
+
+
+/* 
+ * This file contains higher-level bindmap management code
+ */
+
+
+WBindmap *ioncore_rootwin_bindmap=NULL;
+WBindmap *ioncore_mplex_bindmap=NULL;
+WBindmap *ioncore_mplex_toplevel_bindmap=NULL;
+WBindmap *ioncore_frame_bindmap=NULL;
+WBindmap *ioncore_frame_toplevel_bindmap=NULL;
+WBindmap *ioncore_frame_floating_bindmap=NULL;
+WBindmap *ioncore_frame_tiled_bindmap=NULL;
+WBindmap *ioncore_frame_transient_bindmap=NULL;
+WBindmap *ioncore_moveres_bindmap=NULL;
+WBindmap *ioncore_group_bindmap=NULL;
+WBindmap *ioncore_groupcw_bindmap=NULL;
+WBindmap *ioncore_groupws_bindmap=NULL;
+WBindmap *ioncore_clientwin_bindmap=NULL;
+
+static Rb_node known_bindmaps=NULL;
+
+static StringIntMap frame_areas[]={
+    {"border",      FRAME_AREA_BORDER},
+    {"tab",         FRAME_AREA_TAB},
+    {"empty_tab",   FRAME_AREA_TAB},
+    {"client",      FRAME_AREA_CLIENT},
+    END_STRINGINTMAP
+};
+
+
+#define DO_FREE(X, Y)                                       \
+    if(ioncore_ ## X ## _bindmap!=NULL){                    \
+        ioncore_free_bindmap(Y, ioncore_ ## X ## _bindmap); \
+        ioncore_ ## X ## _bindmap=NULL;                     \
+    }
+
+void ioncore_deinit_bindmaps()
+{
+    DO_FREE(rootwin, "WScreen");
+    DO_FREE(mplex, "WMPlex");
+    DO_FREE(mplex_toplevel, "WMPlex.toplevel");
+    DO_FREE(frame, "WFrame");
+    DO_FREE(frame_toplevel, "WFrame.toplevel");
+    DO_FREE(frame_floating, "WFrame.floating");
+    DO_FREE(frame_tiled, "WFrame.tiled");
+    DO_FREE(frame_transient, "WFrame.transient");
+    DO_FREE(moveres, "WMoveresMode");
+    DO_FREE(group, "WGroup");
+    DO_FREE(groupcw, "WGroupCW");
+    DO_FREE(groupws, "WGroupWS");
+    DO_FREE(clientwin, "WClientWin");
+    rb_free_tree(known_bindmaps);
+    known_bindmaps=NULL;
+}
+
+
+#define DO_ALLOC(X, Y, Z)                                  \
+    ioncore_ ## X ## _bindmap=ioncore_alloc_bindmap(Y, Z); \
+    if(ioncore_ ## X ## _bindmap==NULL)                    \
+        return FALSE;
+
+bool ioncore_init_bindmaps()
+{
+    known_bindmaps=make_rb();
+    
+    if(known_bindmaps==NULL)
+        return FALSE;
+    
+    DO_ALLOC(rootwin, "WScreen", NULL);
+    DO_ALLOC(mplex, "WMPlex", NULL);
+    DO_ALLOC(mplex_toplevel, "WMPlex.toplevel", NULL);
+    DO_ALLOC(frame, "WFrame", frame_areas);
+    DO_ALLOC(frame_toplevel, "WFrame.toplevel", frame_areas);
+    DO_ALLOC(frame_floating, "WFrame.floating", frame_areas);
+    DO_ALLOC(frame_tiled, "WFrame.tiled", frame_areas);
+    DO_ALLOC(frame_transient, "WFrame.transient", frame_areas);
+    DO_ALLOC(moveres, "WMoveresMode", NULL);
+    DO_ALLOC(group, "WGroup", NULL);
+    DO_ALLOC(groupcw, "WGroupCW", NULL);
+    DO_ALLOC(groupws, "WGroupWS", NULL);
+    DO_ALLOC(clientwin, "WClientWin", NULL);
+    
+    return TRUE;
+}
+
+
+
+void ioncore_refresh_bindmaps()
+{
+    Rb_node node;
+    
+    ioncore_update_modmap();
+    
+    rb_traverse(node,known_bindmaps){
+        bindmap_refresh((WBindmap*)rb_val(node));
+    }
+}
+
+
+WBindmap *ioncore_alloc_bindmap(const char *name, const StringIntMap *areas)
+{
+    WBindmap *bm=create_bindmap();
+
+    if(bm==NULL)
+        return NULL;
+    
+    bm->areamap=areas;
+    
+    if(!rb_insert(known_bindmaps, name, bm)){
+        bindmap_destroy(bm);
+        return NULL;
+    }
+    
+    return bm;
+}
+
+
+WBindmap *ioncore_alloc_bindmap_frame(const char *name)
+{
+    return ioncore_alloc_bindmap(name, frame_areas);
+}
+
+
+void ioncore_free_bindmap(const char *name, WBindmap *bm)
+{
+    int found=0;
+    Rb_node node;
+    
+    node=rb_find_key_n(known_bindmaps, name, &found);
+    assert(found!=0 && rb_val(node)==(void*)bm);
+    
+    rb_delete_node(node);
+    bindmap_destroy(bm);
+}
+
+
+WBindmap *ioncore_lookup_bindmap(const char *name)
+{
+    int found=0;
+    Rb_node node;
+    
+    node=rb_find_key_n(known_bindmaps, name, &found);
+    
+    if(found==0)
+        return NULL;
+    
+    return (WBindmap*)rb_val(node);
+}
+
+
+EXTL_EXPORT
+bool ioncore_do_defbindings(const char *name, ExtlTab tab)
+{
+    WBindmap *bm=ioncore_lookup_bindmap(name);
+    if(bm==NULL){
+        warn("Unknown bindmap %s.", name);
+        return FALSE;
+    }
+    return bindmap_defbindings(bm, tab, FALSE);
+}
+
+
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab ioncore_do_getbindings()
+{
+    Rb_node node;
+    ExtlTab tab;
+    
+    tab=extl_create_table();
+    
+    rb_traverse(node, known_bindmaps){
+        ExtlTab bmtab=bindmap_getbindings((WBindmap*)rb_val(node));
+        extl_table_sets_t(tab, (const char*)node->k.key, bmtab);
+        extl_unref_table(bmtab);
+    }
+    
+    return tab;
+}
+
diff --git a/ioncore/bindmaps.h b/ioncore/bindmaps.h
new file mode 100644 (file)
index 0000000..bef0ae0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * ion/ioncore/bindmaps.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/map.h>
+#include "binding.h"
+
+#ifndef ION_IONCORE_BINDMAP_H
+#define ION_IONCORE_BINDMAP_H
+
+extern WBindmap *ioncore_rootwin_bindmap;
+extern WBindmap *ioncore_mplex_bindmap;
+extern WBindmap *ioncore_mplex_toplevel_bindmap;
+extern WBindmap *ioncore_frame_bindmap;
+extern WBindmap *ioncore_frame_toplevel_bindmap;
+extern WBindmap *ioncore_frame_floating_bindmap;
+extern WBindmap *ioncore_frame_tiled_bindmap;
+extern WBindmap *ioncore_frame_transient_bindmap;
+extern WBindmap *ioncore_moveres_bindmap;
+extern WBindmap *ioncore_group_bindmap;
+extern WBindmap *ioncore_groupcw_bindmap;
+extern WBindmap *ioncore_groupws_bindmap;
+extern WBindmap *ioncore_clientwin_bindmap;
+
+extern void ioncore_deinit_bindmaps();
+extern bool ioncore_init_bindmaps();
+extern void ioncore_refresh_bindmaps();
+
+extern WBindmap *ioncore_alloc_bindmap(const char *name, 
+                                       const StringIntMap *areas);
+extern WBindmap *ioncore_alloc_bindmap_frame(const char *name);
+extern void ioncore_free_bindmap(const char *name, WBindmap *bm);
+extern WBindmap *ioncore_lookup_bindmap(const char *name);
+
+extern bool ioncore_do_defbindings(const char *name, ExtlTab tab);
+extern ExtlTab ioncore_do_getbindings();
+
+#endif /* ION_IONCORE_BINDMAP_H */
+
diff --git a/ioncore/classes.h b/ioncore/classes.h
new file mode 100644 (file)
index 0000000..c990b14
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * ion/ioncore/classes.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_CLASSES_H
+#define ION_IONCORE_CLASSES_H
+
+/* Forward declarations of some classes to avoid problems
+ * with the header system.
+ */
+
+#include <libtu/obj.h>
+
+INTRCLASS(WClientWin);
+INTRCLASS(WFrame);
+INTRCLASS(WInfoWin);
+INTRCLASS(WMPlex);
+INTRCLASS(WRegion);
+INTRCLASS(WMoveresMode);
+INTRCLASS(WRootWin);
+INTRCLASS(WScreen);
+INTRCLASS(WWindow);
+INTRCLASS(WGroup);
+INTRCLASS(WGroupCW);
+INTRCLASS(WGroupWS);
+
+INTRCLASS(WPHolder);
+INTRCLASS(WMPlexPHolder);
+
+INTRSTRUCT(WStacking);
+INTRSTRUCT(WLListNode);
+INTRSTRUCT(WStackingIterTmp);
+
+INTRSTRUCT(WSubmapState);
+
+INTRSTRUCT(WRegionAttachData);
+
+INTRSTRUCT(WRQGeomParams);
+
+#endif /* ION_IONCORE_CLASSES_H */
diff --git a/ioncore/clientwin.c b/ioncore/clientwin.c
new file mode 100644 (file)
index 0000000..4b4dbe4
--- /dev/null
@@ -0,0 +1,1403 @@
+/*
+ * ion/ioncore/clientwin.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <libextl/extl.h>
+#include <libmainloop/defer.h>
+#include <libmainloop/hooks.h>
+#include "common.h"
+#include "global.h"
+#include "property.h"
+#include "focus.h"
+#include "sizehint.h"
+#include "event.h"
+#include "clientwin.h"
+#include "colormap.h"
+#include "resize.h"
+#include "attach.h"
+#include "regbind.h"
+#include "bindmaps.h"
+#include "names.h"
+#include "saveload.h"
+#include "manage.h"
+#include "extlconv.h"
+#include "fullscreen.h"
+#include "event.h"
+#include "rootwin.h"
+#include "activity.h"
+#include "netwm.h"
+#include "xwindow.h"
+#include "bindmaps.h"
+
+
+static void set_clientwin_state(WClientWin *cwin, int state);
+static bool send_clientmsg(Window win, Atom a, Time stmp);
+
+
+WHook *clientwin_do_manage_alt=NULL;
+WHook *clientwin_mapped_hook=NULL;
+WHook *clientwin_unmapped_hook=NULL;
+WHook *clientwin_property_change_hook=NULL;
+
+
+/*{{{ Get properties */
+
+
+void clientwin_get_protocols(WClientWin *cwin)
+{
+    Atom *protocols=NULL, *p;
+    int n;
+    
+    cwin->flags&=~(CLIENTWIN_P_WM_DELETE|CLIENTWIN_P_WM_TAKE_FOCUS);
+    
+    if(!XGetWMProtocols(ioncore_g.dpy, cwin->win, &protocols, &n))
+        return;
+    
+    for(p=protocols; n; n--, p++){
+        if(*p==ioncore_g.atom_wm_delete)
+            cwin->flags|=CLIENTWIN_P_WM_DELETE;
+        else if(*p==ioncore_g.atom_wm_take_focus)
+            cwin->flags|=CLIENTWIN_P_WM_TAKE_FOCUS;
+    }
+    
+    if(protocols!=NULL)
+        XFree((char*)protocols);
+}
+
+
+static bool get_winprop_fn_set=FALSE;
+static ExtlFn get_winprop_fn;
+
+/*EXTL_DOC
+ * Set function used to look up winprops.
+ */
+EXTL_EXPORT
+void ioncore_set_get_winprop_fn(ExtlFn fn)
+{
+    if(get_winprop_fn_set)
+        extl_unref_fn(get_winprop_fn);
+    get_winprop_fn=extl_ref_fn(fn);
+    get_winprop_fn_set=TRUE;
+}
+
+
+static WSizePolicy get_sizepolicy_winprop(WClientWin *cwin,
+                                          const char *propname,
+                                          WSizePolicy value)
+{
+    char *szplcy;
+
+    if(extl_table_gets_s(cwin->proptab, propname, &szplcy)){
+        string2sizepolicy(szplcy, &value);
+       free(szplcy);
+    }
+    return value;
+}
+
+
+#define SIZEHINT_PROPS (CLIENTWIN_PROP_MAXSIZE| \
+                        CLIENTWIN_PROP_MINSIZE| \
+                        CLIENTWIN_PROP_ASPECT| \
+                        CLIENTWIN_PROP_IGNORE_RSZINC)
+
+
+static void clientwin_get_winprops(WClientWin *cwin)
+{
+    ExtlTab tab, tab2;
+    int i1, i2;
+    bool ret;
+    
+    if(!get_winprop_fn_set)
+        return;
+    
+    extl_protect(NULL);
+    ret=extl_call(get_winprop_fn, "o", "t", cwin, &tab);
+    extl_unprotect(NULL);
+
+    if(!ret)
+        return;
+    
+    cwin->proptab=tab;
+    
+    if(tab==extl_table_none())
+        return;
+
+    if(extl_table_is_bool_set(tab, "transparent"))
+        cwin->flags|=CLIENTWIN_PROP_TRANSPARENT;
+
+    if(extl_table_is_bool_set(tab, "acrobatic"))
+        cwin->flags|=CLIENTWIN_PROP_ACROBATIC;
+    
+    if(extl_table_gets_t(tab, "max_size", &tab2)){
+        if(extl_table_gets_i(tab2, "w", &i1) &&
+           extl_table_gets_i(tab2, "h", &i2)){
+            cwin->size_hints.max_width=i1;
+            cwin->size_hints.max_height=i2;
+            cwin->size_hints.flags|=PMaxSize;
+            cwin->flags|=CLIENTWIN_PROP_MAXSIZE;
+        }
+        extl_unref_table(tab2);
+    }
+
+    if(extl_table_gets_t(tab, "min_size", &tab2)){
+        if(extl_table_gets_i(tab2, "w", &i1) &&
+           extl_table_gets_i(tab2, "h", &i2)){
+            cwin->size_hints.min_width=i1;
+            cwin->size_hints.min_height=i2;
+            cwin->size_hints.flags|=PMinSize;
+            cwin->flags|=CLIENTWIN_PROP_MINSIZE;
+        }
+        extl_unref_table(tab2);
+    }
+
+    if(extl_table_gets_t(tab, "aspect", &tab2)){
+        if(extl_table_gets_i(tab2, "w", &i1) &&
+           extl_table_gets_i(tab2, "h", &i2)){
+            cwin->size_hints.min_aspect.x=i1;
+            cwin->size_hints.max_aspect.x=i1;
+            cwin->size_hints.min_aspect.y=i2;
+            cwin->size_hints.max_aspect.y=i2;
+            cwin->size_hints.flags|=PAspect;
+            cwin->flags|=CLIENTWIN_PROP_ASPECT;
+        }
+        extl_unref_table(tab2);
+    }
+    
+    if(extl_table_is_bool_set(tab, "ignore_resizeinc"))
+        cwin->flags|=CLIENTWIN_PROP_IGNORE_RSZINC;
+
+    if(extl_table_is_bool_set(tab, "ignore_cfgrq"))
+        cwin->flags|=CLIENTWIN_PROP_IGNORE_CFGRQ;
+
+#if 0    
+    cwin->szplcy=get_sizepolicy_winprop(cwin, "sizepolicy", 
+                                        SIZEPOLICY_DEFAULT);
+    cwin->transient_szplcy=get_sizepolicy_winprop(cwin, 
+                                                  "transient_sizepolicy",
+                                                  DFLT_SZPLCY);
+#endif
+}
+
+
+void clientwin_get_size_hints(WClientWin *cwin)
+{
+    XSizeHints tmp=cwin->size_hints;
+    
+    xwindow_get_sizehints(cwin->win, &(cwin->size_hints));
+    
+    if(cwin->flags&CLIENTWIN_PROP_MAXSIZE){
+        cwin->size_hints.max_width=tmp.max_width;
+        cwin->size_hints.max_height=tmp.max_height;
+        cwin->size_hints.flags|=PMaxSize;
+    }
+
+    if(cwin->flags&CLIENTWIN_PROP_MINSIZE){
+        cwin->size_hints.min_width=tmp.min_width;
+        cwin->size_hints.min_height=tmp.min_height;
+        cwin->size_hints.flags|=PMinSize;
+    }
+    
+    if(cwin->flags&CLIENTWIN_PROP_ASPECT){
+        cwin->size_hints.min_aspect=tmp.min_aspect;
+        cwin->size_hints.max_aspect=tmp.max_aspect;
+        cwin->size_hints.flags|=PAspect;
+    }
+    
+    if(cwin->flags&CLIENTWIN_PROP_IGNORE_RSZINC)
+        cwin->size_hints.flags&=~PResizeInc;
+}
+
+
+void clientwin_get_set_name(WClientWin *cwin)
+{
+    char **list=NULL;
+    int n=0;
+    
+    if(ioncore_g.use_mb)
+        list=netwm_get_name(cwin);
+
+    if(list==NULL){
+        list=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n);
+    }else{
+        cwin->flags|=CLIENTWIN_USE_NET_WM_NAME;
+    }
+
+    if(list==NULL){
+        /* Special condition kludge: property exists, but couldn't
+         * be converted to a string list.
+         */
+        clientwin_set_name(cwin, (n==-1 ? "???" : NULL));
+    }else{
+        clientwin_set_name(cwin, *list);
+        XFreeStringList(list);
+    }
+}
+
+
+/* Some standard winprops */
+
+
+bool clientwin_get_switchto(const WClientWin *cwin)
+{
+    bool b;
+    
+    if(ioncore_g.opmode==IONCORE_OPMODE_INIT)
+        return FALSE;
+    
+    if(extl_table_gets_b(cwin->proptab, "switchto", &b))
+        return b;
+    
+    return ioncore_g.switchto_new;
+}
+
+
+int clientwin_get_transient_mode(const WClientWin *cwin)
+{
+    char *s;
+    int mode=TRANSIENT_MODE_NORMAL;
+    
+    if(extl_table_gets_s(cwin->proptab, "transient_mode", &s)){
+        if(strcmp(s, "current")==0)
+            mode=TRANSIENT_MODE_CURRENT;
+        else if(strcmp(s, "off")==0)
+            mode=TRANSIENT_MODE_OFF;
+        free(s);
+    }
+    return mode;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Manage/create */
+
+
+static void configure_cwin_bw(Window win, int bw)
+{
+    XWindowChanges wc;
+    ulong wcmask=CWBorderWidth;
+    
+    wc.border_width=bw;
+    XConfigureWindow(ioncore_g.dpy, win, wcmask, &wc);
+}
+
+
+static void set_sane_gravity(Window win)
+{
+    XSetWindowAttributes attr;
+    
+    attr.win_gravity=NorthWestGravity;
+    
+    XChangeWindowAttributes(ioncore_g.dpy, win,
+                            CWWinGravity, &attr);
+}
+
+
+static bool clientwin_init(WClientWin *cwin, WWindow *par, Window win,
+                           XWindowAttributes *attr)
+{
+    WFitParams fp;
+
+    cwin->flags=0;
+    cwin->win=win;
+    cwin->state=WithdrawnState;
+    
+    fp.g.x=attr->x;
+    fp.g.y=attr->y;
+    fp.g.w=attr->width;
+    fp.g.h=attr->height;
+    fp.mode=REGION_FIT_EXACT;
+    
+    /* The idiot who invented special server-supported window borders that
+     * are not accounted for in the window size should be "taken behind a
+     * sauna".
+     */
+    cwin->orig_bw=attr->border_width;
+    configure_cwin_bw(cwin->win, 0);
+    if(cwin->orig_bw!=0 && cwin->size_hints.flags&PWinGravity){
+        fp.g.x+=xgravity_deltax(cwin->size_hints.win_gravity, 
+                               -cwin->orig_bw, -cwin->orig_bw);
+        fp.g.y+=xgravity_deltay(cwin->size_hints.win_gravity, 
+                               -cwin->orig_bw, -cwin->orig_bw);
+    }
+    
+    set_sane_gravity(cwin->win);
+
+    cwin->transient_for=None;
+    
+    cwin->n_cmapwins=0;
+    cwin->cmap=attr->colormap;
+    cwin->cmaps=NULL;
+    cwin->cmapwins=NULL;
+    cwin->n_cmapwins=0;
+    cwin->event_mask=IONCORE_EVENTMASK_CLIENTWIN;
+
+    cwin->fs_pholder=NULL;
+
+    region_init(&(cwin->region), par, &fp);
+
+    cwin->region.flags|=REGION_GRAB_ON_PARENT;
+    region_add_bindmap(&cwin->region, ioncore_clientwin_bindmap);
+        
+    XSelectInput(ioncore_g.dpy, win, cwin->event_mask);
+
+    clientwin_register(cwin);
+    clientwin_get_set_name(cwin);
+    clientwin_get_colormaps(cwin);
+    clientwin_get_protocols(cwin);
+    clientwin_get_winprops(cwin);
+    clientwin_get_size_hints(cwin);
+    
+    XSaveContext(ioncore_g.dpy, win, ioncore_g.win_context, (XPointer)cwin);
+    XAddToSaveSet(ioncore_g.dpy, win);
+
+    return TRUE;
+}
+
+
+static WClientWin *create_clientwin(WWindow *par, Window win,
+                                    XWindowAttributes *attr)
+{
+    CREATEOBJ_IMPL(WClientWin, clientwin, (p, par, win, attr));
+}
+
+
+static bool handle_target_prop(WClientWin *cwin, const WManageParams *param)
+{
+    WRegion *r=NULL;
+    char *target_name=NULL;
+    
+    if(extl_table_gets_s(cwin->proptab, "target", &target_name)){
+        r=ioncore_lookup_region(target_name, NULL);
+        
+        free(target_name);
+    
+        if(r!=NULL){
+            if(region_manage_clientwin(r, cwin, param, 
+                                       MANAGE_REDIR_PREFER_NO))
+                return TRUE;
+        }
+    }
+    
+    return FALSE;
+}
+
+
+WClientWin *clientwin_get_transient_for(const WClientWin *cwin)
+{
+    Window tforwin;
+    WClientWin *tfor=NULL;
+    
+    if(clientwin_get_transient_mode(cwin)!=TRANSIENT_MODE_NORMAL)
+        return NULL;
+
+    if(!XGetTransientForHint(ioncore_g.dpy, cwin->win, &tforwin))
+        return NULL;
+    
+    if(tforwin==None)
+        return NULL;
+    
+    tfor=XWINDOW_REGION_OF_T(tforwin, WClientWin);
+    
+    if(tfor==cwin){
+        warn(TR("The transient_for hint for \"%s\" points to itself."),
+             region_name((WRegion*)cwin));
+    }else if(tfor==NULL){
+        if(xwindow_region_of(tforwin)!=NULL){
+            warn(TR("Client window \"%s\" has broken transient_for hint. "
+                    "(\"Extended WM hints\" multi-parent brain damage?)"),
+                 region_name((WRegion*)cwin));
+        }
+    }else if(!region_same_rootwin((WRegion*)cwin, (WRegion*)tfor)){
+        warn(TR("The transient_for window for \"%s\" is not on the same "
+                "screen."), region_name((WRegion*)cwin));
+    }else{
+        return tfor;
+    }
+    
+    return NULL;
+}
+
+
+static bool postmanage_check(WClientWin *cwin, XWindowAttributes *attr)
+{
+    /* Check that the window exists. The previous check and selectinput
+     * do not seem to catch all cases of window destroyal.
+     */
+    XSync(ioncore_g.dpy, False);
+    
+    if(XGetWindowAttributes(ioncore_g.dpy, cwin->win, attr))
+        return TRUE;
+    
+    warn(TR("Window %#x disappeared."), cwin->win);
+    
+    return FALSE;
+}
+
+
+static bool do_manage_mrsh(bool (*fn)(WClientWin *cwin, WManageParams *pm),
+                           void **p)
+{
+    return fn((WClientWin*)p[0], (WManageParams*)p[1]);
+}
+
+
+
+static bool do_manage_mrsh_extl(ExtlFn fn, void **p)
+{
+    WClientWin *cwin=(WClientWin*)p[0];
+    WManageParams *mp=(WManageParams*)p[1];
+    ExtlTab t=manageparams_to_table(mp);
+    bool ret=FALSE;
+    
+    extl_call(fn, "ot", "b", cwin, t, &ret);
+    
+    extl_unref_table(t);
+    
+    return (ret && REGION_MANAGER(cwin)!=NULL);
+}
+
+
+/* This is called when a window is mapped on the root window.
+ * We want to check if we should manage the window and how and
+ * act appropriately.
+ */
+WClientWin* ioncore_manage_clientwin(Window win, bool maprq)
+{
+    WRootWin *rootwin;
+    WClientWin *cwin=NULL;
+    XWindowAttributes attr;
+    XWMHints *hints;
+    int init_state=NormalState;
+    WManageParams param=MANAGEPARAMS_INIT;
+
+    param.dockapp=FALSE;
+    
+again:
+    /* Is the window already being managed? */
+    cwin=XWINDOW_REGION_OF_T(win, WClientWin);
+    if(cwin!=NULL)
+        return cwin;
+    
+    /* Select for UnmapNotify and DestroyNotify as the
+     * window might get destroyed or unmapped in the meanwhile.
+     */
+    xwindow_unmanaged_selectinput(win, StructureNotifyMask);
+
+    
+    /* Is it a dockapp?
+     */
+    hints=XGetWMHints(ioncore_g.dpy, win);
+
+    if(hints!=NULL && hints->flags&StateHint)
+        init_state=hints->initial_state;
+    
+    if(!param.dockapp && init_state==WithdrawnState && 
+       hints->flags&IconWindowHint && hints->icon_window!=None){
+        /* The dockapp might be displaying its "main" window if no
+         * wm that understands dockapps has been managing it.
+         */
+        if(!maprq)
+            XUnmapWindow(ioncore_g.dpy, win);
+        
+        xwindow_unmanaged_selectinput(win, 0);
+        
+        win=hints->icon_window;
+        
+        /* It is a dockapp, do everything again from the beginning, now
+         * with the icon window.
+         */
+        param.dockapp=TRUE;
+        goto again;
+    }
+    
+    if(hints!=NULL)
+        XFree((void*)hints);
+
+    if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){
+        if(maprq)
+            warn(TR("Window %#x disappeared."), win);
+        goto fail2;
+    }
+    
+    attr.width=maxof(attr.width, 1);
+    attr.height=maxof(attr.height, 1);
+
+    /* Do we really want to manage it? */
+    if(!param.dockapp && (attr.override_redirect || 
+        (!maprq && attr.map_state!=IsViewable))){
+        goto fail2;
+    }
+
+    /* Find root window */
+    FOR_ALL_ROOTWINS(rootwin){
+        if(WROOTWIN_ROOT(rootwin)==attr.root)
+            break;
+    }
+
+    if(rootwin==NULL){
+        warn(TR("Unable to find a matching root window!"));
+        goto fail2;
+    }
+
+    /* Allocate and initialize */
+    cwin=create_clientwin((WWindow*)rootwin, win, &attr);
+    
+    if(cwin==NULL){
+        warn_err();
+        goto fail2;
+    }
+
+    param.geom=REGION_GEOM(cwin);
+    param.maprq=maprq;
+    param.userpos=(cwin->size_hints.flags&USPosition);
+    param.switchto=(init_state!=IconicState && clientwin_get_switchto(cwin));
+    param.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto");
+    param.gravity=(cwin->size_hints.flags&PWinGravity
+                   ? cwin->size_hints.win_gravity
+                   : ForgetGravity);
+    param.tfor=clientwin_get_transient_for(cwin);
+    
+    if(cwin->flags&SIZEHINT_PROPS){
+        /* If size hints have been messed with, readjust requested geometry
+         * here. If programs themselves give incompatible geometries and
+         * things don't look good then, it's their fault.
+         */
+        region_size_hints_correct((WRegion*)cwin, &param.geom.w, &param.geom.h,
+                                  FALSE);
+    }
+
+    if(!handle_target_prop(cwin, &param)){
+        bool managed;
+        void *mrshpm[2];
+        
+        mrshpm[0]=cwin;
+        mrshpm[1]=&param;
+        
+        managed=hook_call_alt(clientwin_do_manage_alt, &mrshpm, 
+                              (WHookMarshall*)do_manage_mrsh,
+                              (WHookMarshallExtl*)do_manage_mrsh_extl);
+
+        if(!managed){
+            warn(TR("Unable to manage client window %#x."), win);
+            goto failure;
+        }
+    }
+    
+    if(ioncore_g.opmode==IONCORE_OPMODE_NORMAL &&
+       !region_is_fully_mapped((WRegion*)cwin) && 
+       !region_skip_focus((WRegion*)cwin)){
+        region_set_activity((WRegion*)cwin, SETPARAM_SET);
+    }
+    
+    
+    if(postmanage_check(cwin, &attr)){
+        if(param.jumpto && ioncore_g.focus_next==NULL)
+            region_goto((WRegion*)cwin);
+        hook_call_o(clientwin_mapped_hook, (Obj*)cwin);
+        return cwin;
+    }
+
+failure:
+    clientwin_destroyed(cwin);
+    return NULL;
+
+fail2:
+    xwindow_unmanaged_selectinput(win, 0);
+    return NULL;
+}
+
+
+void clientwin_tfor_changed(WClientWin *cwin)
+{
+#if 0
+    WManageParams param=MANAGEPARAMS_INIT;
+    bool succeeded=FALSE;
+    param.tfor=clientwin_get_transient_for(cwin);
+    if(param.tfor==NULL)
+        return;
+    
+    region_rootpos((WRegion*)cwin, &(param.geom.x), &(param.geom.y));
+    param.geom.w=REGION_GEOM(cwin).w;
+    param.geom.h=REGION_GEOM(cwin).h;
+    param.maprq=FALSE;
+    param.userpos=FALSE;
+    param.switchto=region_may_control_focus((WRegion*)cwin);
+    param.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto");
+    param.gravity=ForgetGravity;
+    
+    CALL_ALT_B(succeeded, clientwin_do_manage_alt, (cwin, &param));
+    warn("WM_TRANSIENT_FOR changed for \"%s\".",
+         region_name((WRegion*)cwin));
+#else
+    warn(TR("Changes is WM_TRANSIENT_FOR property are unsupported."));
+#endif        
+}
+
+
+/*}}}*/
+
+
+/*{{{ Unmanage/destroy */
+
+
+static bool reparent_root(WClientWin *cwin)
+{
+    XWindowAttributes attr;
+    WWindow *par;
+    Window dummy;
+    int x=0, y=0;
+    
+    if(!XGetWindowAttributes(ioncore_g.dpy, cwin->win, &attr))
+        return FALSE;
+    
+    par=REGION_PARENT(cwin);
+    
+    if(par==NULL){
+        x=REGION_GEOM(cwin).x;
+        y=REGION_GEOM(cwin).y;
+    }else{
+        int dr=REGION_GEOM(par).w-REGION_GEOM(cwin).w-REGION_GEOM(cwin).x;
+        int db=REGION_GEOM(par).h-REGION_GEOM(cwin).h-REGION_GEOM(cwin).y;
+        dr=maxof(dr, 0);
+        db=maxof(db, 0);
+        
+        XTranslateCoordinates(ioncore_g.dpy, par->win, attr.root, 0, 0, 
+                              &x, &y, &dummy);
+
+        x-=xgravity_deltax(cwin->size_hints.win_gravity, 
+                           maxof(0, REGION_GEOM(cwin).x), dr);
+        y-=xgravity_deltay(cwin->size_hints.win_gravity, 
+                           maxof(0, REGION_GEOM(cwin).y), db);
+    }
+    
+    XReparentWindow(ioncore_g.dpy, cwin->win, attr.root, x, y);
+    
+    return TRUE;
+}
+
+
+void clientwin_deinit(WClientWin *cwin)
+{
+    WRegion *reg;
+    
+    if(cwin->win!=None){
+        xwindow_unmanaged_selectinput(cwin->win, 0);
+        XUnmapWindow(ioncore_g.dpy, cwin->win);
+        
+        if(cwin->orig_bw!=0)
+            configure_cwin_bw(cwin->win, cwin->orig_bw);
+        
+        if(reparent_root(cwin)){
+            if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT){
+                XMapWindow(ioncore_g.dpy, cwin->win);
+                /* Make sure the topmost window has focus; it doesn't really
+                 * matter which one has as long as some has.
+                 */
+                xwindow_do_set_focus(cwin->win);
+            }else{
+                set_clientwin_state(cwin, WithdrawnState);
+                netwm_delete_state(cwin);
+            }
+        }
+        
+        XRemoveFromSaveSet(ioncore_g.dpy, cwin->win);
+        XDeleteContext(ioncore_g.dpy, cwin->win, ioncore_g.win_context);
+    }
+    
+    clientwin_clear_colormaps(cwin);
+    
+    if(cwin->fs_pholder!=NULL){
+        WPHolder *ph=cwin->fs_pholder;
+        cwin->fs_pholder=NULL;
+        destroy_obj((Obj*)ph);
+    }
+    
+    region_deinit((WRegion*)cwin);
+}
+
+
+
+static bool mrsh_u_c(WHookDummy *fn, void *param)
+{
+    fn(*(Window*)param);
+    return TRUE;
+}
+
+static bool mrsh_u_extl(ExtlFn fn, void *param)
+{
+    double d=*(Window*)param;
+    extl_call(fn, "d", NULL, d);
+    return TRUE;
+}
+
+static void clientwin_do_unmapped(WClientWin *cwin, Window win)
+{
+    bool mcf=region_may_control_focus((WRegion*)cwin);
+
+    if(mcf && cwin->fs_pholder!=NULL)
+        pholder_goto(cwin->fs_pholder);
+    
+    destroy_obj((Obj*)cwin);
+    
+    hook_call(clientwin_unmapped_hook, &win, mrsh_u_c, mrsh_u_extl);
+}
+
+/* Used when the window was unmapped */
+void clientwin_unmapped(WClientWin *cwin)
+{
+    clientwin_do_unmapped(cwin, cwin->win);
+}
+
+
+/* Used when the window was deastroyed */
+void clientwin_destroyed(WClientWin *cwin)
+{
+    Window win=cwin->win;
+    XRemoveFromSaveSet(ioncore_g.dpy, cwin->win);
+    XDeleteContext(ioncore_g.dpy, cwin->win, ioncore_g.win_context);
+    xwindow_unmanaged_selectinput(cwin->win, 0);
+    cwin->win=None;
+    clientwin_do_unmapped(cwin, win);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Kill/close */
+
+
+static bool send_clientmsg(Window win, Atom a, Time stmp)
+{
+    XClientMessageEvent ev;
+    
+    ev.type=ClientMessage;
+    ev.window=win;
+    ev.message_type=ioncore_g.atom_wm_protocols;
+    ev.format=32;
+    ev.data.l[0]=a;
+    ev.data.l[1]=stmp;
+    
+    return (XSendEvent(ioncore_g.dpy, win, False, 0L, (XEvent*)&ev)!=0);
+}
+
+
+/*EXTL_DOC
+ * Attempt to kill (with XKillWindow) the client that owns the X
+ * window correspoding to \var{cwin}.
+ */
+EXTL_EXPORT_MEMBER
+void clientwin_kill(WClientWin *cwin)
+{
+    XKillClient(ioncore_g.dpy, cwin->win);
+}
+
+
+bool clientwin_rqclose(WClientWin *cwin, bool relocate_ignored)
+{
+    /* Ignore relocate parameter -- client windows can always be 
+     * destroyed by the application in any case, so way may just as
+     * well assume relocate is always set.
+     */
+    
+    if(cwin->flags&CLIENTWIN_P_WM_DELETE){
+        send_clientmsg(cwin->win, ioncore_g.atom_wm_delete, 
+                       ioncore_get_timestamp());
+        return TRUE;
+    }else{
+        warn(TR("Client does not support the WM_DELETE protocol."));
+        return FALSE;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ State (hide/show) */
+
+
+static void set_clientwin_state(WClientWin *cwin, int state)
+{
+    if(cwin->state!=state){
+        cwin->state=state;
+        xwindow_set_state_property(cwin->win, state);
+    }
+}
+
+
+static void hide_clientwin(WClientWin *cwin)
+{
+    if(cwin->flags&CLIENTWIN_PROP_ACROBATIC){
+        XMoveWindow(ioncore_g.dpy, cwin->win,
+                    -2*REGION_GEOM(cwin).w, -2*REGION_GEOM(cwin).h);
+        return;
+    }
+    
+    set_clientwin_state(cwin, IconicState);
+    XSelectInput(ioncore_g.dpy, cwin->win,
+                 cwin->event_mask&~(StructureNotifyMask|EnterWindowMask));
+    XUnmapWindow(ioncore_g.dpy, cwin->win);
+    XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
+}
+
+
+static void show_clientwin(WClientWin *cwin)
+{
+    if(cwin->flags&CLIENTWIN_PROP_ACROBATIC){
+        XMoveWindow(ioncore_g.dpy, cwin->win,
+                    REGION_GEOM(cwin).x, REGION_GEOM(cwin).y);
+        if(cwin->state==NormalState)
+            return;
+    }
+    
+    XSelectInput(ioncore_g.dpy, cwin->win,
+                 cwin->event_mask&~(StructureNotifyMask|EnterWindowMask));
+    XMapWindow(ioncore_g.dpy, cwin->win);
+    XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
+    set_clientwin_state(cwin, NormalState);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Resize/reparent/reconf helpers */
+
+
+void clientwin_notify_rootpos(WClientWin *cwin, int rootx, int rooty)
+{
+    XEvent ce;
+    Window win;
+    
+    if(cwin==NULL)
+        return;
+    
+    win=cwin->win;
+    
+    ce.xconfigure.type=ConfigureNotify;
+    ce.xconfigure.event=win;
+    ce.xconfigure.window=win;
+    ce.xconfigure.x=rootx-cwin->orig_bw;
+    ce.xconfigure.y=rooty-cwin->orig_bw;
+    ce.xconfigure.width=REGION_GEOM(cwin).w;
+    ce.xconfigure.height=REGION_GEOM(cwin).h;
+    ce.xconfigure.border_width=cwin->orig_bw;
+    ce.xconfigure.above=None;
+    ce.xconfigure.override_redirect=False;
+    
+    XSelectInput(ioncore_g.dpy, win, cwin->event_mask&~StructureNotifyMask);
+    XSendEvent(ioncore_g.dpy, win, False, StructureNotifyMask, &ce);
+    XSelectInput(ioncore_g.dpy, win, cwin->event_mask);
+}
+
+
+static void sendconfig_clientwin(WClientWin *cwin)
+{
+    int rootx, rooty;
+    
+    region_rootpos(&cwin->region, &rootx, &rooty);
+    clientwin_notify_rootpos(cwin, rootx, rooty);
+}
+
+
+static void do_reparent_clientwin(WClientWin *cwin, Window win, int x, int y)
+{
+    XSelectInput(ioncore_g.dpy, cwin->win,
+                 cwin->event_mask&~StructureNotifyMask);
+    XReparentWindow(ioncore_g.dpy, cwin->win, win, x, y);
+    XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
+}
+
+
+static void convert_geom(const WFitParams *fp, 
+                         WClientWin *cwin, WRectangle *geom)
+{
+    WFitParams fptmp=*fp;
+    WSizePolicy szplcy=SIZEPOLICY_FULL_EXACT;
+    
+    /*if(cwin->szplcy!=SIZEPOLICY_DEFAULT)
+        szplcy=cwin->szplcy;*/
+    
+    sizepolicy(&szplcy, (WRegion*)cwin, NULL, REGION_RQGEOM_WEAK_ALL, &fptmp);
+    
+    *geom=fptmp.g;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Region dynfuns */
+
+
+static bool clientwin_fitrep(WClientWin *cwin, WWindow *np, 
+                             const WFitParams *fp)
+{
+    WRectangle geom;
+    bool changes;
+    int w, h;
+
+    if(np!=NULL && !region_same_rootwin((WRegion*)cwin, (WRegion*)np))
+        return FALSE;
+    
+    if(fp->mode&REGION_FIT_WHATEVER){
+        geom.x=fp->g.x;
+        geom.y=fp->g.y;
+        geom.w=REGION_GEOM(cwin).w;
+        geom.h=REGION_GEOM(cwin).h;
+    }else{
+        geom=fp->g;
+    }
+    
+    changes=(REGION_GEOM(cwin).x!=geom.x ||
+             REGION_GEOM(cwin).y!=geom.y ||
+             REGION_GEOM(cwin).w!=geom.w ||
+             REGION_GEOM(cwin).h!=geom.h);
+    
+    REGION_GEOM(cwin)=geom;
+    
+    if(np==NULL && !changes)
+        return TRUE;
+    
+    if(np!=NULL){
+        region_unset_parent((WRegion*)cwin);
+        do_reparent_clientwin(cwin, np->win, geom.x, geom.y);
+        region_set_parent((WRegion*)cwin, np);
+        sendconfig_clientwin(cwin);
+        
+        if(!REGION_IS_FULLSCREEN(cwin) && cwin->fs_pholder!=NULL){
+            WPHolder *ph=cwin->fs_pholder;
+            cwin->fs_pholder=NULL;
+            cwin->flags&=~CLIENTWIN_FS_RQ;
+            /* Can't destroy it yet - messes up mplex placeholder
+             * reorganisation.
+             */
+            mainloop_defer_destroy((Obj*)ph);
+        }
+
+        netwm_update_state(cwin);
+    }
+    
+    w=maxof(1, geom.w);
+    h=maxof(1, geom.h);
+    
+    if(cwin->flags&CLIENTWIN_PROP_ACROBATIC && !REGION_IS_MAPPED(cwin)){
+        XMoveResizeWindow(ioncore_g.dpy, cwin->win,
+                          -2*REGION_GEOM(cwin).w, -2*REGION_GEOM(cwin).h, 
+                          w, h);
+    }else{
+        XMoveResizeWindow(ioncore_g.dpy, cwin->win, geom.x, geom.y, w, h);
+    }
+    
+    cwin->flags&=~CLIENTWIN_NEED_CFGNTFY;
+    
+    return TRUE;
+}
+
+
+static void clientwin_map(WClientWin *cwin)
+{
+    show_clientwin(cwin);
+    REGION_MARK_MAPPED(cwin);
+}
+
+
+static void clientwin_unmap(WClientWin *cwin)
+{
+    hide_clientwin(cwin);
+    REGION_MARK_UNMAPPED(cwin);
+}
+
+
+static void clientwin_do_set_focus(WClientWin *cwin, bool warp)
+{
+    if(cwin->flags&CLIENTWIN_P_WM_TAKE_FOCUS){
+        Time stmp=ioncore_get_timestamp();
+        send_clientmsg(cwin->win, ioncore_g.atom_wm_take_focus, stmp);
+    }
+
+    region_finalise_focusing((WRegion*)cwin, cwin->win, warp);
+    
+    XSync(ioncore_g.dpy, 0);
+}
+
+
+void clientwin_restack(WClientWin *cwin, Window other, int mode)
+{
+    xwindow_restack(cwin->win, other, mode);
+}
+       
+
+void clientwin_stacking(WClientWin *cwin, Window *bottomret, Window *topret)
+{
+    *bottomret=cwin->win;
+    *topret=cwin->win;
+}
+
+
+static Window clientwin_x_window(WClientWin *cwin)
+{
+    return cwin->win;
+}
+
+
+static void clientwin_activated(WClientWin *cwin)
+{
+    clientwin_install_colormap(cwin);
+}
+
+
+static void clientwin_size_hints(WClientWin *cwin, WSizeHints *hints_ret)
+{
+    if(cwin->flags&CLIENTWIN_FS_RQ){
+        /* Do not use size hints, when full screen mode has been
+         * requested by the client window itself.
+         */
+        sizehints_clear(hints_ret);
+    }else{
+        xsizehints_to_sizehints(&cwin->size_hints, hints_ret);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Identity & lookup */
+
+
+/*EXTL_DOC
+ * Returns a table containing the properties \code{WM_CLASS} (table entries
+ * \var{instance} and \var{class}) and  \code{WM_WINDOW_ROLE} (\var{role})
+ * properties for \var{cwin}. If a property is not set, the corresponding 
+ * field(s) are unset in the  table.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+ExtlTab clientwin_get_ident(WClientWin *cwin)
+{
+    char **p=NULL, *wrole=NULL;
+    int n=0, n2=0, n3=0, tmp=0;
+    ExtlTab tab;
+    
+    p=xwindow_get_text_property(cwin->win, XA_WM_CLASS, &n);
+    wrole=xwindow_get_string_property(cwin->win, ioncore_g.atom_wm_window_role, &n2);
+    
+    tab=extl_create_table();
+    if(n>=2 && p[1]!=NULL)
+        extl_table_sets_s(tab, "class", p[1]);
+    if(n>=1 && p[0]!=NULL)
+        extl_table_sets_s(tab, "instance", p[0]);
+    if(wrole!=NULL)
+        extl_table_sets_s(tab, "role", wrole);
+    
+    if(p!=NULL)
+        XFreeStringList(p);
+    if(wrole!=NULL)
+        free(wrole);
+    
+    return tab;
+}
+
+
+/*}}}*/
+
+
+/*{{{ ConfigureRequest */
+
+
+void clientwin_handle_configure_request(WClientWin *cwin,
+                                        XConfigureRequestEvent *ev)
+{
+    if(ev->value_mask&CWBorderWidth)
+        cwin->orig_bw=ev->border_width;
+    
+    if(cwin->flags&CLIENTWIN_PROP_IGNORE_CFGRQ){
+        sendconfig_clientwin(cwin);
+        return;
+    }
+
+    /* check full screen request */
+    if((ev->value_mask&(CWWidth|CWHeight))==(CWWidth|CWHeight)){
+        bool sw=clientwin_fullscreen_may_switchto(cwin);
+        if(clientwin_check_fullscreen_request(cwin, ev->width, ev->height, sw))
+            return;
+    }
+
+    cwin->flags|=CLIENTWIN_NEED_CFGNTFY;
+
+    if(ev->value_mask&(CWX|CWY|CWWidth|CWHeight)){
+        WRQGeomParams rq=RQGEOMPARAMS_INIT;
+        int gdx=0, gdy=0;
+
+        rq.flags=REGION_RQGEOM_WEAK_ALL|REGION_RQGEOM_ABSOLUTE;
+        
+        if(cwin->size_hints.flags&PWinGravity){
+            rq.flags|=REGION_RQGEOM_GRAVITY;
+            rq.gravity=cwin->size_hints.win_gravity;
+        }
+        
+        /* Do I need to insert another disparaging comment on the person who
+         * invented special server-supported window borders that are not 
+         * accounted for in the window size? Keep it simple, stupid!
+         */
+        if(cwin->size_hints.flags&PWinGravity){
+            gdx=xgravity_deltax(cwin->size_hints.win_gravity, 
+                               -cwin->orig_bw, -cwin->orig_bw);
+            gdy=xgravity_deltay(cwin->size_hints.win_gravity, 
+                               -cwin->orig_bw, -cwin->orig_bw);
+        }
+        
+        region_rootpos((WRegion*)cwin, &(rq.geom.x), &(rq.geom.y));
+        rq.geom.w=REGION_GEOM(cwin).w;
+        rq.geom.h=REGION_GEOM(cwin).h;
+        
+        if(ev->value_mask&CWWidth){
+            /* If x was not changed, keep reference point where it was */
+            if(cwin->size_hints.flags&PWinGravity){
+                rq.geom.x+=xgravity_deltax(cwin->size_hints.win_gravity, 0,
+                                           ev->width-rq.geom.w);
+            }
+            rq.geom.w=maxof(ev->width, 1);
+            rq.flags&=~REGION_RQGEOM_WEAK_W;
+        }
+        if(ev->value_mask&CWHeight){
+            /* If y was not changed, keep reference point where it was */
+            if(cwin->size_hints.flags&PWinGravity){
+                rq.geom.y+=xgravity_deltay(cwin->size_hints.win_gravity, 0,
+                                           ev->height-rq.geom.h);
+            }
+            rq.geom.h=maxof(ev->height, 1);
+            rq.flags&=~REGION_RQGEOM_WEAK_H;
+        }
+        if(ev->value_mask&CWX){
+            rq.geom.x=ev->x+gdx;
+            rq.flags&=~REGION_RQGEOM_WEAK_X;
+        }
+        if(ev->value_mask&CWY){
+            rq.geom.y=ev->y+gdy;
+            rq.flags&=~REGION_RQGEOM_WEAK_Y;
+        }
+        
+        region_rqgeom((WRegion*)cwin, &rq, NULL);
+    }
+
+    if(cwin->flags&CLIENTWIN_NEED_CFGNTFY){
+        sendconfig_clientwin(cwin);
+        cwin->flags&=~CLIENTWIN_NEED_CFGNTFY;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Kludges */
+
+
+/*EXTL_DOC
+ * Attempts to fix window size problems with non-ICCCM compliant
+ * programs.
+ */
+EXTL_EXPORT_MEMBER
+void clientwin_nudge(WClientWin *cwin)
+{
+    XResizeWindow(ioncore_g.dpy, cwin->win, 
+                  2*REGION_GEOM(cwin).w, 2*REGION_GEOM(cwin).h);
+    XFlush(ioncore_g.dpy);
+    XResizeWindow(ioncore_g.dpy, cwin->win, 
+                  REGION_GEOM(cwin).w, REGION_GEOM(cwin).h);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+/*EXTL_DOC
+ * Return the X window id for the client window.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+double clientwin_xid(WClientWin *cwin)
+{
+    return cwin->win;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Save/load */
+
+
+static int last_checkcode=1;
+
+
+static ExtlTab clientwin_get_configuration(WClientWin *cwin)
+{
+    int chkc=0;
+    ExtlTab tab;
+    SMCfgCallback *cfg_cb;
+    SMAddCallback *add_cb;
+    
+    tab=region_get_base_configuration((WRegion*)cwin);
+
+    extl_table_sets_d(tab, "windowid", (double)(cwin->win));
+    
+    if(last_checkcode!=0){
+        chkc=last_checkcode++;
+        xwindow_set_integer_property(cwin->win, ioncore_g.atom_checkcode, 
+                                     chkc);
+        extl_table_sets_i(tab, "checkcode", chkc);
+    }
+
+    ioncore_get_sm_callbacks(&add_cb, &cfg_cb);
+    
+    if(cfg_cb!=NULL)
+        cfg_cb(cwin, tab);
+    
+    return tab;
+}
+
+
+WRegion *clientwin_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
+{
+    double wind=0;
+    Window win=None;
+    int chkc=0, real_chkc=0;
+    WClientWin *cwin=NULL;
+    XWindowAttributes attr;
+    WRectangle rg;
+    bool got_chkc;
+
+    if(!extl_table_gets_d(tab, "windowid", &wind) ||
+       !extl_table_gets_i(tab, "checkcode", &chkc)){
+        return NULL;
+    }
+    
+    win=(Window)wind;
+
+    if(XWINDOW_REGION_OF(win)!=NULL){
+        warn("Client window %x already managed.", win);
+        return NULL;
+    }
+
+    got_chkc=xwindow_get_integer_property(win, ioncore_g.atom_checkcode, 
+                                          &real_chkc);
+    
+    if(!got_chkc || real_chkc!=chkc){
+        ioncore_clientwin_load_missing();
+        return NULL;
+    }
+
+    /* Found it! */
+    
+    if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){
+        warn(TR("Window %#x disappeared."), win);
+        return NULL;
+    }
+    
+    if(attr.override_redirect || 
+       (ioncore_g.opmode==IONCORE_OPMODE_INIT && attr.map_state!=IsViewable)){
+        warn(TR("Saved client window does not want to be managed."));
+        return NULL;
+    }
+
+    /*
+    attr.x=fp->g.x;
+    attr.y=fp->g.y;
+    attr.width=fp->g.w;
+    attr.height=fp->g.h;
+     */
+
+    cwin=create_clientwin(par, win, &attr);
+    
+    if(cwin==NULL)
+        return FALSE;
+    
+    /* Reparent and resize taking limits set by size hints into account */
+    convert_geom(fp, cwin, &rg);
+    REGION_GEOM(cwin)=rg;
+    do_reparent_clientwin(cwin, par->win, rg.x, rg.y);
+    XResizeWindow(ioncore_g.dpy, win, maxof(1, rg.w), maxof(1, rg.h));
+    
+    if(!postmanage_check(cwin, &attr)){
+        clientwin_destroyed(cwin);
+        return NULL;
+    }
+    
+    return (WRegion*)cwin;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuntab and class info */
+
+
+static DynFunTab clientwin_dynfuntab[]={
+    {(DynFun*)region_fitrep,
+     (DynFun*)clientwin_fitrep},
+
+    {region_map,
+     clientwin_map},
+    
+    {region_unmap,
+     clientwin_unmap},
+    
+    {region_do_set_focus, 
+     clientwin_do_set_focus},
+    
+    {region_notify_rootpos, 
+     clientwin_notify_rootpos},
+    
+    {region_restack, 
+     clientwin_restack},
+
+    {region_stacking, 
+     clientwin_stacking},
+    
+    {(DynFun*)region_xwindow, 
+     (DynFun*)clientwin_x_window},
+    
+    {region_activated, 
+     clientwin_activated},
+    
+    {region_size_hints, 
+     clientwin_size_hints},
+    
+    {(DynFun*)region_rqclose, 
+     (DynFun*)clientwin_rqclose},
+    
+    {(DynFun*)region_get_configuration,
+     (DynFun*)clientwin_get_configuration},
+
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WClientWin, WRegion, clientwin_deinit, clientwin_dynfuntab);
+
+
+/*}}}*/
diff --git a/ioncore/clientwin.h b/ioncore/clientwin.h
new file mode 100644 (file)
index 0000000..2080d3d
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * ion/ioncore/clientwin.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_CLIENTWIN_H
+#define ION_IONCORE_CLIENTWIN_H
+
+#include <libextl/extl.h>
+#include <libtu/ptrlist.h>
+#include <libmainloop/hooks.h>
+#include "common.h"
+#include "region.h"
+#include "window.h"
+#include "rectangle.h"
+#include "attach.h"
+#include "manage.h"
+#include "pholder.h"
+#include "sizepolicy.h"
+
+
+#define CLIENTWIN_P_WM_DELETE        0x00001
+#define CLIENTWIN_P_WM_TAKE_FOCUS    0x00002
+#define CLIENTWIN_PROP_ACROBATIC     0x00010
+#define CLIENTWIN_PROP_MAXSIZE       0x00020
+#define CLIENTWIN_PROP_ASPECT        0x00040
+#define CLIENTWIN_PROP_TRANSPARENT   0x00080
+#define CLIENTWIN_PROP_IGNORE_RSZINC 0x00100
+#define CLIENTWIN_PROP_MINSIZE       0x00200
+#define CLIENTWIN_PROP_IGNORE_CFGRQ  0x00400
+#define CLIENTWIN_NEED_CFGNTFY       0x01000
+#define CLIENTWIN_USE_NET_WM_NAME    0x10000
+#define CLIENTWIN_FS_RQ              0x20000
+
+
+DECLCLASS(WClientWin){
+    WRegion region;
+    
+    int flags;
+    int state;
+    int event_mask;
+    Window win;
+    
+    int orig_bw;
+
+    Window transient_for;
+    
+    Colormap cmap;
+    Colormap *cmaps;
+    Window *cmapwins;
+    int n_cmapwins;
+
+    XSizeHints size_hints;
+    
+    WPHolder *fs_pholder;
+    
+    ExtlTab proptab;
+};
+
+
+extern void clientwin_get_protocols(WClientWin *cwin);
+extern void clientwin_get_size_hints(WClientWin *cwin);
+extern void clientwin_unmapped(WClientWin *cwin);
+extern void clientwin_destroyed(WClientWin *cwin);
+extern void clientwin_kill(WClientWin *cwin);
+extern bool clientwin_rqclose(WClientWin *cwin, bool relocate_ignored);
+
+extern void clientwin_tfor_changed(WClientWin *cwin);
+
+extern void clientwin_get_set_name(WClientWin *cwin);
+
+extern void clientwin_handle_configure_request(WClientWin *cwin,
+                                               XConfigureRequestEvent *ev);
+
+extern void clientwin_broken_app_resize_kludge(WClientWin *cwin);
+
+extern WRegion *clientwin_load(WWindow *par, const WFitParams *fp,
+                               ExtlTab tab);
+
+/* Some standard winprops */
+
+enum{
+    TRANSIENT_MODE_NORMAL,
+    TRANSIENT_MODE_CURRENT,
+    TRANSIENT_MODE_OFF
+};
+
+extern bool clientwin_get_switchto(const WClientWin *cwin);
+extern int clientwin_get_transient_mode(const WClientWin *cwin);
+extern WClientWin *clientwin_get_transient_for(const WClientWin *cwin);
+
+/* Hooks */
+
+/* This hook has parameters (WClientWin*, WManageParams*). */
+extern WHook *clientwin_do_manage_alt;
+/* This hook has just WClientWin* as parameter. */
+extern WHook *clientwin_mapped_hook;
+/* This hook has an X Window id as parameter. */
+extern WHook *clientwin_unmapped_hook;
+/* This hook has (WClientWin*, const XPropertyEvent *) as parameters on
+ * C side, and (WClientWin*, int atom) on Lua side.
+ */
+extern WHook *clientwin_property_change_hook;
+
+/* Manage */
+
+extern WClientWin *ioncore_manage_clientwin(Window win, bool maprq);
+
+#endif /* ION_IONCORE_CLIENTWIN_H */
diff --git a/ioncore/colormap.c b/ioncore/colormap.c
new file mode 100644 (file)
index 0000000..83c3b75
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * ion/ioncore/colormap.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/rb.h>
+#include "common.h"
+#include "global.h"
+#include "property.h"
+#include "clientwin.h"
+#include "colormap.h"
+#include "region.h"
+#include "names.h"
+#include "xwindow.h"
+
+
+/*{{{ Installing colormaps */
+
+
+void rootwin_install_colormap(WRootWin *rootwin, Colormap cmap)
+{
+    if(cmap==None)
+        cmap=rootwin->default_cmap;
+    XInstallColormap(ioncore_g.dpy, cmap);
+}
+
+
+void clientwin_install_colormap(WClientWin *cwin)
+{
+    WRootWin *rw=region_rootwin_of((WRegion*)cwin);
+    bool found=FALSE;
+    int i;
+
+    for(i=cwin->n_cmapwins-1; i>=0; i--){
+        rootwin_install_colormap(rw, cwin->cmaps[i]);
+        if(cwin->cmapwins[i]==cwin->win)
+            found=TRUE;
+    }
+    
+    if(found)
+        return;
+    
+    rootwin_install_colormap(rw, cwin->cmap);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Management */
+
+
+static XContext ctx=None;
+
+
+void xwindow_unmanaged_selectinput(Window win, long mask)
+{
+    int *p=NULL;
+    
+    /* We may be monitoring for colourmap changes */
+    if(ctx!=None){
+        if(XFindContext(ioncore_g.dpy, win, ctx, (XPointer*)&p)==0){
+            if(*p>0)
+                mask|=ColormapChangeMask;
+        }
+    }
+    
+    XSelectInput(ioncore_g.dpy, win, mask);
+}
+
+                
+        
+static void xwindow_selcmap(Window win)
+{
+    int *p=NULL;
+    XWindowAttributes attr;
+    
+    if(ctx==None)
+        ctx=XUniqueContext();
+    
+    if(XFindContext(ioncore_g.dpy, win, ctx, (XPointer*)&p)==0){
+        (*p)++;
+    }else{
+        p=ALLOC(int);
+        if(p==NULL)
+            return;
+        
+        *p=1;
+        if(XSaveContext(ioncore_g.dpy, win, ctx, (XPointer)p)!=0){
+            warn(TR("Unable to store colourmap watch info."));
+            return;
+        }
+
+        if(XWINDOW_REGION_OF(win)==NULL){
+            XGetWindowAttributes(ioncore_g.dpy, win, &attr);
+            XSelectInput(ioncore_g.dpy, win, 
+                         attr.your_event_mask|ColormapChangeMask);
+        }
+    }
+}
+
+
+static void xwindow_unselcmap(Window win)
+{
+    int *p=NULL;
+    XWindowAttributes attr;
+    
+    if(ctx==None)
+        return;
+
+    if(XFindContext(ioncore_g.dpy, win, ctx, (XPointer*)&p)==0){
+        (*p)--;
+        if(*p==0){
+            XDeleteContext(ioncore_g.dpy, win, ctx);
+            free(p);
+            if(XWINDOW_REGION_OF(win)==NULL){
+                XGetWindowAttributes(ioncore_g.dpy, win, &attr);
+                XSelectInput(ioncore_g.dpy, win, 
+                             attr.your_event_mask&~ColormapChangeMask);
+            }
+        }
+    }
+}
+
+
+void clientwin_get_colormaps(WClientWin *cwin)
+{
+    Window *wins;
+    XWindowAttributes attr;
+    int i, n;
+
+    clientwin_clear_colormaps(cwin);
+    
+    n=xwindow_get_property(cwin->win, ioncore_g.atom_wm_colormaps,
+                           XA_WINDOW, 100L, TRUE, (uchar**)&wins);
+    
+    if(n<=0)
+        return;
+    
+    cwin->cmaps=ALLOC_N(Colormap, n);
+    
+    if(cwin->cmaps==NULL)
+        return;
+    
+    cwin->cmapwins=wins;
+    cwin->n_cmapwins=n;
+    
+    for(i=0; i<n; i++){
+        if(wins[i]==cwin->win){
+            cwin->cmaps[i]=cwin->cmap;
+        }else{
+            xwindow_selcmap(wins[i]);
+            XGetWindowAttributes(ioncore_g.dpy, wins[i], &attr);
+            cwin->cmaps[i]=attr.colormap;
+        }
+    }
+}
+
+
+void clientwin_clear_colormaps(WClientWin *cwin)
+{
+    int i;
+    XWindowAttributes attr;
+
+    if(cwin->n_cmapwins==0)
+        return;
+
+    for(i=0; i<cwin->n_cmapwins; i++){
+        if(cwin->cmapwins[i]!=cwin->win)
+            xwindow_unselcmap(cwin->cmapwins[i]);
+    }
+    
+    free(cwin->cmapwins);
+    free(cwin->cmaps);
+    cwin->n_cmapwins=0;
+    cwin->cmapwins=NULL;
+    cwin->cmaps=NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Event handling */
+
+
+static void handle_cwin_cmap(WClientWin *cwin, const XColormapEvent *ev)
+{
+    int i;
+    
+    if(ev->window==cwin->win){
+        cwin->cmap=ev->colormap;
+        if(REGION_IS_ACTIVE(cwin))
+            clientwin_install_colormap(cwin);
+    }else{
+        for(i=0; i<cwin->n_cmapwins; i++){
+            if(cwin->cmapwins[i]!=ev->window)
+                continue;
+            cwin->cmaps[i]=ev->colormap;
+            if(REGION_IS_ACTIVE(cwin))
+                clientwin_install_colormap(cwin);
+            break;
+        }
+    }
+}
+
+
+static void handle_all_cmaps(const XColormapEvent *ev)
+{
+    Rb_node node;
+    
+    if(!ioncore_clientwin_ns.initialised)
+        return;
+    
+    rb_traverse(node, ioncore_clientwin_ns.rb){
+        WClientWin *cwin=(WClientWin*)rb_val(node);
+        if(cwin!=NULL)
+            handle_cwin_cmap(cwin, ev);
+    }
+}
+
+
+
+void ioncore_handle_colormap_notify(const XColormapEvent *ev)
+{
+    WClientWin *cwin;
+    
+    if(!ev->new)
+        return;
+
+    cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
+
+    if(cwin!=NULL){
+        handle_cwin_cmap(cwin, ev);
+        /*set_cmap(cwin, ev->colormap);*/
+    }else{
+        handle_all_cmaps(ev);
+    }
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/colormap.h b/ioncore/colormap.h
new file mode 100644 (file)
index 0000000..b44f7fd
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * ion/ioncore/colormap.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_COLORMAP_H
+#define ION_IONCORE_COLORMAP_H
+
+#include "common.h"
+#include "clientwin.h"
+
+extern void ioncore_handle_colormap_notify(const XColormapEvent *ev);
+
+extern void rootwin_install_colormap(WRootWin *scr, Colormap cmap);
+
+extern void clientwin_install_colormap(WClientWin *cwin);
+extern void clientwin_get_colormaps(WClientWin *cwin);
+extern void clientwin_clear_colormaps(WClientWin *cwin);
+
+extern void xwindow_unmanaged_selectinput(Window win, long mask);
+
+#endif /* ION_IONCORE_COLORMAP_H */
diff --git a/ioncore/common.h b/ioncore/common.h
new file mode 100644 (file)
index 0000000..4959a5b
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * ion/ioncore/common.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_COMMON_H
+#define ION_IONCORE_COMMON_H
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <libtu/types.h>
+#include <libtu/output.h>
+#include <libtu/misc.h>
+#include <libtu/dlist.h>
+#include <libtu/obj.h>
+#include <libtu/locale.h>
+#include <libtu/debug.h>
+
+#include <libextl/extl.h>
+
+#include "../config.h"
+#include "classes.h"
+
+#endif /* ION_IONCORE_COMMON_H */
diff --git a/ioncore/conf-bindings.c b/ioncore/conf-bindings.c
new file mode 100644 (file)
index 0000000..e39b811
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * ion/ioncore/conf-bindings.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+
+#include <libtu/map.h>
+
+#include "common.h"
+#include "binding.h"
+#include <libextl/readconfig.h>
+#include "global.h"
+#include <libextl/extl.h>
+#include "conf-bindings.h"
+#include "bindmaps.h"
+
+
+/*{{{ parse_keybut */
+
+
+#define MOD5_NDX 7
+
+static StringIntMap state_map[]={
+    {"Shift",       ShiftMask},
+    {"Lock",        LockMask},
+    {"Control",     ControlMask},
+    {"Mod1",        Mod1Mask},
+    {"Mod2",        Mod2Mask},
+    {"Mod3",        Mod3Mask},
+    {"Mod4",        Mod4Mask},
+    {"Mod5",        Mod5Mask},
+    {"AnyModifier", AnyModifier},
+    {"NoModifier",  0},
+    {NULL,          0},
+};
+
+static StringIntMap button_map[]={
+    {"Button1",     Button1},
+    {"Button2",     Button2},
+    {"Button3",     Button3},
+    {"Button4",     Button4},
+    {"Button5",     Button5},
+    {"Button6",     6},
+    {"Button7",     7},
+    {"AnyButton",   AnyButton},
+    {NULL,          0},
+};
+
+
+bool ioncore_parse_keybut(const char *str, uint *mod_ret, uint *ksb_ret,
+                          bool button, bool init_any)
+{
+    char *str2, *p, *p2;
+    int keysym=NoSymbol, i;
+    bool ret=FALSE;
+    
+    *ksb_ret=NoSymbol;
+    *mod_ret=(init_any && !button ? AnyModifier : 0);
+    
+    str2=scopy(str);
+    
+    if(str2==NULL)
+        return FALSE;
+
+    p=str2;
+    
+    while(*p!='\0'){
+        p2=strchr(p, '+');
+        
+        if(p2!=NULL)
+            *p2='\0';
+        
+        if(!button){
+            keysym=XStringToKeysym(p);
+#ifdef CF_SUN_F1X_REMAP
+            if(keysym==XK_F11)
+                keysym=XK_SunF36;
+            else if(keysym==XK_F12)
+                keysym=XK_SunF37;
+#endif
+        }
+        
+        if(!button && keysym!=NoSymbol){
+            int tmp;
+            if(*ksb_ret!=NoSymbol){
+                warn_obj(str, TR("Insane key combination."));
+                break;
+            }
+            if(XKeysymToKeycode(ioncore_g.dpy, keysym)==0){
+                warn_obj(str, TR("Could not convert keysym to keycode."));
+                break;
+            }
+            *ksb_ret=keysym;
+        }else{
+            i=stringintmap_ndx(state_map, p);
+
+            if(i<0){
+                i=stringintmap_ndx(button_map, p);
+                
+                if(i<0){
+                    warn(TR("Unknown button \"%s\"."), p);
+                    break;
+                }
+            
+                if(!button || *ksb_ret!=NoSymbol){
+                    warn_obj(str, TR("Insane button combination."));
+                    break;
+                }
+                *ksb_ret=button_map[i].value;
+            }else{
+                if(*mod_ret==AnyModifier){
+                    if(!init_any){
+                        warn_obj(str, TR("Insane modifier combination."));
+                        break;
+                    }else{
+                        *mod_ret=state_map[i].value;
+                    }
+                }else{
+                    if(*mod_ret!=0 && state_map[i].value==AnyModifier){
+                        warn_obj(str, TR("Insane modifier combination."));
+                        break;
+                    }else{
+                        *mod_ret|=state_map[i].value;
+                    }
+                }
+            }
+        }
+
+        if(p2==NULL){
+            ret=TRUE;
+            break;
+        }
+        
+        p=p2+1;
+    }
+
+    free(str2);
+    
+    return ret;
+}
+
+#undef BUTTON1_NDX
+
+
+/*}}}*/
+
+
+/*{{{ bindmap_defbindings */
+
+
+static bool do_action(WBindmap *bindmap, const char *str,
+                      ExtlFn func, uint act, uint mod, uint ksb,
+                      int area, bool wr)
+{
+    WBinding binding;
+    
+    if(wr && mod==0){
+        warn(TR("Can not wait on modifiers when no modifiers set in \"%s\"."),
+             str);
+        wr=FALSE;
+    }
+    
+    binding.wait=wr;
+    binding.act=act;
+    binding.state=mod;
+    binding.ksb=ksb;
+    binding.kcb=(act==BINDING_KEYPRESS ? XKeysymToKeycode(ioncore_g.dpy, ksb) : ksb);
+    binding.area=area;
+    binding.submap=NULL;
+    
+    if(func!=extl_fn_none()){
+        binding.func=extl_ref_fn(func);
+        if(bindmap_add_binding(bindmap, &binding))
+            return TRUE;
+        extl_unref_fn(binding.func);
+        warn(TR("Unable to add binding %s."), str);
+    }else{
+        binding.func=func;
+        if(bindmap_remove_binding(bindmap, &binding))
+            return TRUE;
+        warn(TR("Unable to remove binding %s."), str);
+    }
+
+    return FALSE;
+}
+
+
+static bool do_submap(WBindmap *bindmap, const char *str,
+                      ExtlTab subtab, uint action, uint mod, uint ksb)
+{
+    WBinding binding, *bnd;
+    uint kcb;
+
+    if(action!=BINDING_KEYPRESS)
+        return FALSE;
+    
+    kcb=XKeysymToKeycode(ioncore_g.dpy, ksb);
+    bnd=bindmap_lookup_binding(bindmap, action, mod, kcb);
+    
+    if(bnd!=NULL && bnd->submap!=NULL && bnd->state==mod)
+        return bindmap_defbindings(bnd->submap, subtab, TRUE);
+
+    binding.wait=FALSE;
+    binding.act=BINDING_KEYPRESS;
+    binding.state=mod;
+    binding.ksb=ksb;
+    binding.kcb=kcb;
+    binding.area=0;
+    binding.func=extl_fn_none();
+    binding.submap=create_bindmap();
+    
+    if(binding.submap==NULL)
+        return FALSE;
+
+    if(bindmap_add_binding(bindmap, &binding))
+        return bindmap_defbindings(binding.submap, subtab, TRUE);
+
+    binding_deinit(&binding);
+    
+    warn(TR("Unable to add submap for binding %s."), str);
+    
+    return FALSE;
+}
+
+
+static StringIntMap action_map[]={
+    {"kpress", BINDING_KEYPRESS},
+    {"mpress", BINDING_BUTTONPRESS},
+    {"mclick", BINDING_BUTTONCLICK},
+    {"mdblclick", BINDING_BUTTONDBLCLICK},
+    {"mdrag", BINDING_BUTTONMOTION},
+    {NULL, 0}
+};
+
+
+static bool do_entry(WBindmap *bindmap, ExtlTab tab, 
+                     const StringIntMap *areamap, bool init_any)
+{
+    bool ret=FALSE;
+    char *action_str=NULL, *ksb_str=NULL, *area_str=NULL;
+    int action=0;
+    uint ksb=0, mod=0;
+    WBinding *bnd=NULL;
+    ExtlTab subtab;
+    ExtlFn func;
+    bool wr=FALSE;
+    int area=0;
+    
+    if(!extl_table_gets_s(tab, "action", &action_str)){
+        warn(TR("Binding type not set."));
+        goto fail;
+    }
+
+    if(strcmp(action_str, "kpress_wait")==0){
+        action=BINDING_KEYPRESS;
+        wr=TRUE;
+    }else{
+        action=stringintmap_value(action_map, action_str, -1);
+        if(action<0){
+            warn(TR("Unknown binding type \"%s\"."), action_str);
+            goto fail;
+        }
+    }
+
+    if(!extl_table_gets_s(tab, "kcb", &ksb_str))
+        goto fail;
+
+    if(!ioncore_parse_keybut(ksb_str, &mod, &ksb,
+                             (action!=BINDING_KEYPRESS && action!=-1), 
+                             init_any)){
+        goto fail;
+    }
+    
+    if(extl_table_gets_t(tab, "submap", &subtab)){
+        ret=do_submap(bindmap, ksb_str, subtab, action, mod, ksb);
+        extl_unref_table(subtab);
+    }else{
+        if(areamap!=NULL){
+            if(extl_table_gets_s(tab, "area", &area_str)){
+                area=stringintmap_value(areamap, area_str, -1);
+                if(area<0){
+                    warn(TR("Unknown area \"%s\" for binding %s."),
+                         area_str, ksb_str);
+                    area=0;
+                }
+            }
+        }
+        
+        if(!extl_table_gets_f(tab, "func", &func)){
+            /*warn("Function for binding %s not set/nil/undefined.", ksb_str);
+            goto fail;*/
+            func=extl_fn_none();
+        }
+        ret=do_action(bindmap, ksb_str, func, action, mod, ksb, area, wr);
+        if(!ret)
+            extl_unref_fn(func);
+    }
+    
+fail:
+    if(action_str!=NULL)
+        free(action_str);
+    if(ksb_str!=NULL)
+        free(ksb_str);
+    if(area_str!=NULL)
+        free(area_str);
+    return ret;
+}
+
+
+bool bindmap_defbindings(WBindmap *bindmap, ExtlTab tab, bool submap)
+{
+    int i, n, nok=0;
+    ExtlTab ent;
+    
+    n=extl_table_get_n(tab);
+    
+    for(i=1; i<=n; i++){
+        if(extl_table_geti_t(tab, i, &ent)){
+            nok+=do_entry(bindmap, ent, bindmap->areamap, submap);
+            extl_unref_table(ent);
+            continue;
+        }
+        warn(TR("Unable to get bindmap entry %d."), i);
+    }
+    return (nok!=0);
+}
+
+
+/*}}}*/
+
+
+/*{{{ bindmap_getbindings */
+
+
+static char *get_mods(uint state)
+{
+    char *ret=NULL;
+    int i;
+    
+    if(state==AnyModifier){
+        ret=scopy("AnyModifier+");
+    }else{
+        ret=scopy("");
+        for(i=0; i<=MOD5_NDX; i++){
+            if(ret==NULL)
+                break;
+            if((int)(state&state_map[i].value)==state_map[i].value){
+                char *ret2=ret;
+                ret=scat3(ret, state_map[i].string, "+");
+                free(ret2);
+            }
+        }
+    }
+    
+    return ret;
+}
+
+
+static char *get_key(char *mods, uint ksb)
+{
+    const char *s=XKeysymToString(ksb);
+    char *ret=NULL;
+    
+    if(s==NULL){
+        warn(TR("Unable to convert keysym to string."));
+        return NULL;
+    }
+    
+    return scat(mods, s);
+}
+
+
+static char *get_button(char *mods, uint ksb)
+{
+    const char *s=stringintmap_key(button_map, ksb, NULL);
+    char *ret=NULL;
+    
+    if(s==NULL){
+        warn(TR("Unable to convert button to string."));
+        return NULL;
+    }
+    
+    return scat(mods, s);
+}
+
+
+static bool get_kpress(WBindmap *bindmap, WBinding *b, ExtlTab t)
+{
+    char *mods;
+    char *key;
+    
+    if(b->wait)
+        extl_table_sets_s(t, "action", "kpress_wait");
+    else
+        extl_table_sets_s(t, "action", "kpress");
+    
+    mods=get_mods(b->state);
+    
+    if(mods==NULL)
+        return FALSE;
+    
+    key=get_key(mods, b->ksb);
+
+    free(mods);
+    
+    if(key==NULL)
+        return FALSE;
+    
+    extl_table_sets_s(t, "kcb", key);
+    
+    free(key);
+    
+    if(b->submap!=NULL){
+        ExtlTab stab=bindmap_getbindings(b->submap);
+        extl_table_sets_t(t, "submap", stab);
+    }else{
+        extl_table_sets_f(t, "func", b->func);
+    }
+    
+    return TRUE;
+}
+
+
+static bool get_mact(WBindmap *bindmap, WBinding *b, ExtlTab t)
+{
+    char *mods;
+    char *button;
+    
+    extl_table_sets_s(t, "action", stringintmap_key(action_map, b->act, NULL));
+    
+    mods=get_mods(b->state);
+    
+    if(mods==NULL)
+        return FALSE;
+    
+    button=get_button(mods, b->ksb);
+
+    free(mods);
+    
+    if(button==NULL)
+        return FALSE;
+    
+    extl_table_sets_s(t, "kcb", button);
+    
+    free(button);
+    
+    if(b->area!=0 && bindmap->areamap!=NULL)
+        extl_table_sets_s(t, "area", 
+                          stringintmap_key(bindmap->areamap, b->area, NULL));
+
+    extl_table_sets_f(t, "func", b->func);
+    
+    return TRUE;
+}
+
+
+static ExtlTab getbinding(WBindmap *bindmap, WBinding *b)
+{
+    ExtlTab t=extl_create_table();
+    
+    if(b->act==BINDING_KEYPRESS){
+        if(get_kpress(bindmap, b, t))
+            return t;
+    }else{
+        if(get_mact(bindmap, b, t))
+            return t;
+    }
+    
+    return extl_unref_table(t);
+}
+
+
+ExtlTab bindmap_getbindings(WBindmap *bindmap)
+{
+    Rb_node node;
+    WBinding *b;
+    ExtlTab tab;
+    ExtlTab btab;
+    int n=0;
+    
+    tab=extl_create_table();
+    
+    FOR_ALL_BINDINGS(b, node, bindmap->bindings){
+        btab=getbinding(bindmap, b);
+        extl_table_seti_t(tab, n+1, btab);
+        extl_unref_table(btab);
+        n++;
+    }
+    
+    return tab;
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/conf-bindings.h b/ioncore/conf-bindings.h
new file mode 100644 (file)
index 0000000..c888a6f
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * ion/ioncore/conf-bindings.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_CONF_BINDINGS_H
+#define ION_IONCORE_CONF_BINDINGS_H
+
+#include <libtu/map.h>
+
+#include "binding.h"
+#include <libextl/extl.h>
+
+extern bool bindmap_defbindings(WBindmap *bindmap, ExtlTab tab, bool submap);
+extern ExtlTab bindmap_getbindings(WBindmap *bindmap);
+
+extern bool ioncore_parse_keybut(const char *str, 
+                                 uint *mod_ret, uint *ksb_ret,
+                                 bool button, bool init_any);
+
+#endif /* ION_IONCORE_CONF_BINDINGS_H */
diff --git a/ioncore/conf.c b/ioncore/conf.c
new file mode 100644 (file)
index 0000000..207c1c1
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * ion/ioncore/conf.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libtu/map.h>
+#include <libtu/minmax.h>
+#include <libtu/objp.h>
+#include <libtu/map.h>
+#include <libextl/readconfig.h>
+
+#include "common.h"
+#include "global.h"
+#include "modules.h"
+#include "rootwin.h"
+#include "bindmaps.h"
+#include "kbresize.h"
+#include "reginfo.h"
+#include "group-ws.h"
+#include "llist.h"
+
+
+StringIntMap frame_idxs[]={
+    {"last", LLIST_INDEX_LAST},
+    {"next",  LLIST_INDEX_AFTER_CURRENT},
+    {"next-act",  LLIST_INDEX_AFTER_CURRENT_ACT},
+    END_STRINGINTMAP
+};
+
+
+/*EXTL_DOC
+ * Set ioncore basic settings. The table \var{tab} may contain the
+ * following fields.
+ * 
+ * \begin{tabularx}{\linewidth}{lX}
+ *  \tabhead{Field & Description}
+ *  \var{opaque_resize} & (boolean) Controls whether interactive move and
+ *                        resize operations simply draw a rubberband during
+ *                        the operation (false) or immediately affect the 
+ *                        object in question at every step (true). \\
+ *  \var{warp} &          (boolean) Should focusing operations move the 
+ *                        pointer to the object to be focused? \\
+ *  \var{switchto} &      (boolean) Should a managing \type{WMPlex} switch
+ *                        to a newly mapped client window? \\
+ *  \var{screen_notify} & (boolean) Should notification tooltips be displayed
+ *                        for hidden workspaces with activity? \\
+ *  \var{frame_default_index} & (string) Specifies where to add new regions
+ *                        on the mutually exclusive list of a frame. One of
+ *                        ''last'', ''next'' (for after current), ''next-act''
+ *                        (for after current and anything with activity right
+ *                        after it). \\
+ *  \var{dblclick_delay} & (integer) Delay between clicks of a double click.\\
+ *  \var{kbresize_delay} & (integer) Delay in milliseconds for ending keyboard
+ *                         resize mode after inactivity. \\
+ *  \var{kbresize_t_max} & (integer) Controls keyboard resize acceleration. 
+ *                         See description below for details. \\
+ *  \var{kbresize_t_min} & (integer) See below. \\
+ *  \var{kbresize_step} & (floating point) See below. \\
+ *  \var{kbresize_maxacc} & (floating point) See below. \\
+ *  \var{framed_transients} & (boolean) Put transients in nested frames. \\
+ *  \var{float_placement_method} & (string) How to place floating frames.
+ *                          One of ''udlr'' (up-down, then left-right), 
+ *                          ''lrud'' (left-right, then up-down) or ''random''. \\
+ *  \var{default_ws_params} & (table) Default workspace layout; the 
+ *                          attach/creation parameters for a \type{WGroup}. \\
+ * \end{tabularx}
+ * 
+ * When a keyboard resize function is called, and at most \var{kbresize_t_max} 
+ * milliseconds has passed from a previous call, acceleration factor is reset 
+ * to 1.0. Otherwise, if at least \var{kbresize_t_min} milliseconds have 
+ * passed from the from previous acceleration update or reset the squere root
+ * of the acceleration factor is incremented by \var{kbresize_step}. The 
+ * maximum acceleration factor (pixels/call modulo size hints) is given by 
+ * \var{kbresize_maxacc}. The default values are (200, 50, 30, 100). 
+ */
+EXTL_EXPORT
+void ioncore_set(ExtlTab tab)
+{
+    int dd, rd;
+    char *wst, *tmp;
+    ExtlTab t;
+    
+    extl_table_gets_b(tab, "opaque_resize", &(ioncore_g.opaque_resize));
+    extl_table_gets_b(tab, "warp", &(ioncore_g.warp_enabled));
+    extl_table_gets_b(tab, "switchto", &(ioncore_g.switchto_new));
+    extl_table_gets_b(tab, "screen_notify", &(ioncore_g.screen_notify));
+    extl_table_gets_b(tab, "framed_transients", &(ioncore_g.framed_transients));
+    
+    if(extl_table_gets_s(tab, "frame_default_index", &tmp)){
+        ioncore_g.frame_default_index=stringintmap_value(frame_idxs, 
+                                                         tmp,
+                                                         ioncore_g.frame_default_index);
+        free(tmp);
+    }
+    
+    if(extl_table_gets_i(tab, "dblclick_delay", &dd))
+        ioncore_g.dblclick_delay=maxof(0, dd);
+    
+    ioncore_set_moveres_accel(tab);
+    
+    ioncore_groupws_set(tab);
+}
+
+
+/*EXTL_DOC
+ * Get ioncore basic settings. For details see \fnref{ioncore.set}.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab ioncore_get()
+{
+    ExtlTab tab=extl_create_table();
+    
+    extl_table_sets_b(tab, "opaque_resize", ioncore_g.opaque_resize);
+    extl_table_sets_b(tab, "warp", ioncore_g.warp_enabled);
+    extl_table_sets_b(tab, "switchto", ioncore_g.switchto_new);
+    extl_table_sets_i(tab, "dblclick_delay", ioncore_g.dblclick_delay);
+    extl_table_sets_b(tab, "screen_notify", ioncore_g.screen_notify);
+
+    extl_table_sets_s(tab, "frame_default_index", 
+                      stringintmap_key(frame_idxs, 
+                                       ioncore_g.frame_default_index,
+                                       NULL));
+    
+    ioncore_get_moveres_accel(tab);
+    
+    ioncore_groupws_get(tab);
+    
+    return tab;
+}
+        
+
+/*EXTL_DOC
+ * Get important directories (userdir, sessiondir, searchpath).
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab ioncore_get_paths(ExtlTab tab)
+{
+    tab=extl_create_table();
+    extl_table_sets_s(tab, "userdir", extl_userdir());
+    extl_table_sets_s(tab, "sessiondir", extl_sessiondir());
+    extl_table_sets_s(tab, "searchpath", extl_searchpath());
+    return tab;
+}
+
+
+/*EXTL_DOC
+ * Set important directories (sessiondir, searchpath).
+ */
+EXTL_EXPORT
+bool ioncore_set_paths(ExtlTab tab)
+{
+    char *s;
+
+    if(extl_table_gets_s(tab, "userdir", &s)){
+        warn(TR("User directory can not be set."));
+        free(s);
+        return FALSE;
+    }
+    
+    if(extl_table_gets_s(tab, "sessiondir", &s)){
+        extl_set_sessiondir(s);
+        free(s);
+        return FALSE;
+    }
+
+    if(extl_table_gets_s(tab, "searchpath", &s)){
+        extl_set_searchpath(s);
+        free(s);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+/* Exports these in ioncore. */
+
+/*EXTL_DOC
+ * Lookup script \var{file}. If \var{try_in_dir} is set, it is tried
+ * before the standard search path.
+ */
+EXTL_SAFE
+EXTL_EXPORT_AS(ioncore, lookup_script)
+char *extl_lookup_script(const char *file, const char *sp);
+
+
+/*EXTL_DOC
+ * Get a file name to save (session) data in. The string \var{basename} 
+ * should contain no path or extension components.
+ */
+EXTL_SAFE
+EXTL_EXPORT_AS(ioncore, get_savefile)
+char *extl_get_savefile(const char *basename);
+
+
+/*EXTL_DOC
+ * Write \var{tab} in file with basename \var{basename} in the
+ * session directory.
+ */
+EXTL_SAFE
+EXTL_EXPORT_AS(ioncore, write_savefile)
+bool extl_write_savefile(const char *basename, ExtlTab tab);
+
+
+/*EXTL_DOC
+ * Read a savefile.
+ */
+EXTL_SAFE
+EXTL_EXPORT_AS(ioncore, read_savefile)
+ExtlTab extl_extl_read_savefile(const char *basename);
+
+    
+
+bool ioncore_read_main_config(const char *cfgfile)
+{
+    bool ret;
+    int unset=0;
+
+    if(cfgfile==NULL)
+        cfgfile="cfg_ion";
+    
+    ret=extl_read_config(cfgfile, ".", TRUE);
+    
+    unset+=(ioncore_rootwin_bindmap->nbindings==0);
+    unset+=(ioncore_mplex_bindmap->nbindings==0);
+    unset+=(ioncore_frame_bindmap->nbindings==0);
+    
+    if(unset>0){
+        warn(TR("Some bindmaps were empty, loading ioncore_efbb."));
+        extl_read_config("ioncore_efbb", NULL, TRUE);
+    }
+    
+    return (ret && unset==0);
+}
diff --git a/ioncore/conf.h b/ioncore/conf.h
new file mode 100644 (file)
index 0000000..da3679b
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * ion/ioncore/conf.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_CONF_H
+#define ION_IONCORE_CONF_H
+
+extern bool ioncore_read_main_config(const char *cfgfile);
+
+#endif /* ION_IONCORE_CONF_H */
diff --git a/ioncore/cursor.c b/ioncore/cursor.c
new file mode 100644 (file)
index 0000000..31ea25b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * ion/ioncore/cursor.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "common.h"
+#include "event.h"
+#include "cursor.h"
+#include "global.h"
+
+
+static Cursor cursors[IONCORE_N_CURSORS];
+
+#define LCURS(TYPE) \
+    cursors[IONCORE_CURSOR_##TYPE]=XCreateFontCursor(ioncore_g.dpy, CF_CURSOR_##TYPE)
+
+void ioncore_init_cursors()
+{
+    LCURS(DEFAULT);
+    LCURS(RESIZE);
+    LCURS(MOVE);
+    LCURS(DRAG);
+    LCURS(WAITKEY);
+}
+
+
+Cursor ioncore_xcursor(int cursor)
+{
+    return cursors[cursor];
+}
+
+
diff --git a/ioncore/cursor.h b/ioncore/cursor.h
new file mode 100644 (file)
index 0000000..0137e8c
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * ion/ioncore/cursor.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_CURSOR_H
+#define ION_IONCORE_CURSOR_H
+
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+
+#define IONCORE_CURSOR_DEFAULT 0
+#define IONCORE_CURSOR_RESIZE  1
+#define IONCORE_CURSOR_MOVE    2
+#define IONCORE_CURSOR_DRAG    3
+#define IONCORE_CURSOR_WAITKEY 4
+#define IONCORE_N_CURSORS      5
+
+extern void ioncore_init_cursors();
+extern Cursor ioncore_xcursor(int cursor);
+
+#endif /* ION_IONCORE_CURSOR_H */
diff --git a/ioncore/dummywc.h b/ioncore/dummywc.h
new file mode 100644 (file)
index 0000000..f3343be
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * ion/ioncore/dummywc.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+/* This file contains dummy implementations of multibyte/widechar routines
+ * used by Ion for retarded platforms.
+ */
+
+#ifndef ION_IONCORE_DUMMYWC_H
+#define ION_IONCORE_DUMMYWC_H
+
+#include <string.h>
+#include <ctype.h>
+
+#define wchar_t int
+#define mbstate_t int
+
+#define iswalnum isalnum
+#define iswprint isprint
+#define iswspace isspace
+
+#define mbrlen dummywc_mbrlen
+#define mbtowc dummywc_mbtowc
+#define mbrtowc dummywc_mbrtowc
+
+static size_t dummywc_mbrlen(const char *s, size_t n, mbstate_t *ps)
+{
+    if(*s=='\0')
+        return 0;
+    return 1;
+}
+
+static int dummywc_mbtowc(wchar_t *pwc, const char *s, size_t n)
+{
+    if(n>0 && *s!='\0'){
+        *pwc=*s;
+        return 1;
+    }
+    return 0;
+}
+
+static size_t dummywc_mbrtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
+{
+    return mbtowc(pwc, s, n);
+}
+
+#endif /* ION_IONCORE_DUMMYWC_H */
+
diff --git a/ioncore/event.c b/ioncore/event.c
new file mode 100644 (file)
index 0000000..84943d5
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * ion/ioncore/event.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <X11/Xmd.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+
+#include <libmainloop/select.h>
+#include <libmainloop/signal.h>
+#include <libmainloop/defer.h>
+
+#include "common.h"
+#include "global.h"
+#include "event.h"
+#include "eventh.h"
+#include "focus.h"
+#include "exec.h"
+#include "ioncore.h"
+
+
+
+/*{{{ Hooks */
+
+
+WHook *ioncore_handle_event_alt=NULL;
+
+
+/*}}}*/
+
+
+/*{{{ Signal check */
+    
+
+static void check_signals()
+{
+    int kill_sig=mainloop_check_signals();
+    
+    if(kill_sig!=0){
+        if(kill_sig==SIGUSR1){
+            ioncore_restart();
+            assert(0);
+        } 
+        if(kill_sig==SIGTERM){
+            /* Save state if not running under a session manager. */
+            ioncore_emergency_snapshot();
+            ioncore_resign();
+            /* We may still return here if running under a session manager. */
+        }else{
+            ioncore_emergency_snapshot();
+            ioncore_deinit();
+            kill(getpid(), kill_sig);
+        }
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Timestamp stuff */
+
+#define CHKEV(E, T) case E: tm=((T*)ev)->time; break;
+#define CLOCK_SKEW_MS 30000
+
+static Time last_timestamp=CurrentTime;
+
+void ioncore_update_timestamp(XEvent *ev)
+{
+    Time tm;
+    
+    switch(ev->type){
+    CHKEV(ButtonPress, XButtonPressedEvent);
+    CHKEV(ButtonRelease, XButtonReleasedEvent);
+    CHKEV(EnterNotify, XEnterWindowEvent);
+    CHKEV(KeyPress, XKeyPressedEvent);
+    CHKEV(KeyRelease, XKeyReleasedEvent);
+    CHKEV(LeaveNotify, XLeaveWindowEvent);
+    CHKEV(MotionNotify, XPointerMovedEvent);
+    CHKEV(PropertyNotify, XPropertyEvent);
+    CHKEV(SelectionClear, XSelectionClearEvent);
+    CHKEV(SelectionNotify, XSelectionEvent);
+    CHKEV(SelectionRequest, XSelectionRequestEvent);
+    default:
+        return;
+    }
+
+    if(tm>last_timestamp || last_timestamp - tm > CLOCK_SKEW_MS)
+        last_timestamp=tm;
+}
+
+
+Time ioncore_get_timestamp()
+{
+    if(last_timestamp==CurrentTime){
+        /* Idea blatantly copied from wmx */
+        XEvent ev;
+        Atom dummy;
+        
+        D(fprintf(stderr, "Attempting to get time from X server."));
+        
+        dummy=XInternAtom(ioncore_g.dpy, "_ION_TIMEREQUEST", False);
+        if(dummy==None){
+            warn(TR("Time request from X server failed."));
+            return 0;
+        }
+        /* TODO: use some other window that should also function as a
+         * NET_WM support check window.
+         */
+        XChangeProperty(ioncore_g.dpy, ioncore_g.rootwins->dummy_win,
+                        dummy, dummy, 8, PropModeAppend,
+                        (unsigned char*)"", 0);
+        ioncore_get_event(&ev, PropertyChangeMask);
+        XPutBackEvent(ioncore_g.dpy, &ev);
+    }
+    
+    return last_timestamp;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Event reading */
+
+
+void ioncore_get_event(XEvent *ev, long mask)
+{
+    fd_set rfds;
+    
+    while(1){
+        check_signals();
+        
+        if(XCheckMaskEvent(ioncore_g.dpy, mask, ev)){
+            ioncore_update_timestamp(ev);
+            return;
+        }
+        
+        FD_ZERO(&rfds);
+        FD_SET(ioncore_g.conn, &rfds);
+
+        /* Other FD:s are _not_ to be handled! */
+        select(ioncore_g.conn+1, &rfds, NULL, NULL, NULL);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Flush */
+
+
+static void skip_enterwindow()
+{
+    XEvent ev;
+    
+    XSync(ioncore_g.dpy, False);
+    
+    while(XCheckMaskEvent(ioncore_g.dpy, EnterWindowMask, &ev)){
+        ioncore_update_timestamp(&ev);
+    }
+}
+
+
+void ioncore_flush()
+{
+    if(ioncore_g.focus_next!=NULL && 
+       ioncore_g.input_mode==IONCORE_INPUTMODE_NORMAL){
+        bool warp=ioncore_g.warp_next;
+        WRegion *next=ioncore_g.focus_next;
+        
+        ioncore_g.focus_next=NULL;
+        
+        region_do_set_focus(next, warp);
+        
+        /* Just greedily eating it all away that X has to offer
+         * seems to be the best we can do with Xlib.
+         */
+        skip_enterwindow();
+    }
+    
+    XFlush(ioncore_g.dpy);
+}
+
+
+/*}}}*/
+
+
+/*{{{ X connection FD handler */
+
+
+void ioncore_x_connection_handler(int conn, void *unused)
+{
+    XEvent ev;
+
+    XNextEvent(ioncore_g.dpy, &ev);
+    ioncore_update_timestamp(&ev);
+
+    hook_call_alt_p(ioncore_handle_event_alt, &ev, NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Mainloop */
+
+
+void ioncore_mainloop()
+{
+    mainloop_trap_signals(NULL);
+    
+    ioncore_g.opmode=IONCORE_OPMODE_NORMAL;
+
+    while(1){
+        check_signals();
+        mainloop_execute_deferred();
+        ioncore_flush();
+
+        if(QLength(ioncore_g.dpy)>0)
+            ioncore_x_connection_handler(ioncore_g.conn, NULL);
+        else
+            mainloop_select();
+    }
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/event.h b/ioncore/event.h
new file mode 100644 (file)
index 0000000..1bf227d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * ion/ioncore/event.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_EVENT_H
+#define ION_IONCORE_EVENT_H
+
+#include <libmainloop/hooks.h>
+#include "common.h"
+#include "region.h"
+
+#define IONCORE_EVENTMASK_PTRGRAB (ButtonPressMask|ButtonReleaseMask| \
+                                   ButtonMotionMask)
+
+#define IONCORE_EVENTMASK_PTRLOOP (IONCORE_EVENTMASK_PTRGRAB|ExposureMask| \
+                                   KeyPressMask|KeyReleaseMask|            \
+                                   EnterWindowMask|FocusChangeMask)
+
+#define IONCORE_EVENTMASK_NORMAL (ExposureMask|KeyPressMask|         \
+                                  ButtonPressMask|ButtonReleaseMask| \
+                                  FocusChangeMask|EnterWindowMask)
+
+#define IONCORE_EVENTMASK_CWINMGR (IONCORE_EVENTMASK_NORMAL| \
+                                   SubstructureRedirectMask)
+
+#define IONCORE_EVENTMASK_ROOT (IONCORE_EVENTMASK_CWINMGR| \
+                                PropertyChangeMask|ColormapChangeMask)
+
+#define IONCORE_EVENTMASK_CLIENTWIN (ColormapChangeMask|                  \
+                                     PropertyChangeMask|FocusChangeMask|  \
+                                     StructureNotifyMask|EnterWindowMask)
+
+extern void ioncore_x_connection_handler(int conn, void *unused);
+extern void ioncore_flush();
+extern void ioncore_get_event(XEvent *ev, long mask);
+
+extern void ioncore_update_timestamp(XEvent *ev);
+extern Time ioncore_get_timestamp();
+
+/* Handlers to this hook should take XEvent* as parameter. */
+extern WHook *ioncore_handle_event_alt;
+
+extern void ioncore_mainloop();
+
+#endif /* ION_IONCORE_EVENT_H */
diff --git a/ioncore/eventh.c b/ioncore/eventh.c
new file mode 100644 (file)
index 0000000..91ab550
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * ion/ioncore/eventh.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <libtu/objp.h>
+#include "common.h"
+#include "global.h"
+#include "rootwin.h"
+#include "property.h"
+#include "pointer.h"
+#include "key.h"
+#include "focus.h"
+#include "selection.h"
+#include "event.h"
+#include "eventh.h"
+#include "clientwin.h"
+#include "colormap.h"
+#include "grab.h"
+#include "bindmaps.h"
+#include "activity.h"
+#include "netwm.h"
+#include "xwindow.h"
+
+
+/*{{{ ioncore_handle_event */
+
+
+#define CASE_EVENT(EV) case EV:  /*\
+    fprintf(stderr, "[%#lx] %s\n", ev->xany.window, #EV);*/
+
+
+bool ioncore_handle_event(XEvent *ev)
+{
+    
+    switch(ev->type){
+    CASE_EVENT(MapRequest)
+        ioncore_handle_map_request(&(ev->xmaprequest));
+        break;
+    CASE_EVENT(ConfigureRequest)
+        ioncore_handle_configure_request(&(ev->xconfigurerequest));
+        break;
+    CASE_EVENT(UnmapNotify)
+        ioncore_handle_unmap_notify(&(ev->xunmap));
+        break;
+    CASE_EVENT(DestroyNotify)
+        ioncore_handle_destroy_notify(&(ev->xdestroywindow));
+        break;
+    CASE_EVENT(ClientMessage)
+        ioncore_handle_client_message(&(ev->xclient));
+        break;
+    CASE_EVENT(PropertyNotify)
+        ioncore_handle_property(&(ev->xproperty));
+        break;
+    CASE_EVENT(FocusIn)
+        ioncore_handle_focus_in(&(ev->xfocus), FALSE);
+        break;
+    CASE_EVENT(FocusOut)
+        ioncore_handle_focus_out(&(ev->xfocus));
+        break;
+    CASE_EVENT(EnterNotify)
+        ioncore_handle_enter_window(ev);
+        break;
+    CASE_EVENT(Expose)        
+        ioncore_handle_expose(&(ev->xexpose));
+        break;
+    CASE_EVENT(KeyPress)
+        ioncore_handle_keyboard(ev);
+        break;
+    CASE_EVENT(KeyRelease)
+        ioncore_handle_keyboard(ev);
+        break;
+    CASE_EVENT(ButtonPress)
+        ioncore_handle_buttonpress(ev);
+        break;
+    CASE_EVENT(ColormapNotify)
+        ioncore_handle_colormap_notify(&(ev->xcolormap));
+        break;
+    CASE_EVENT(MappingNotify)
+        ioncore_handle_mapping_notify(ev);
+        break;
+    CASE_EVENT(SelectionClear)
+        ioncore_clear_selection();
+        break;
+    CASE_EVENT(SelectionNotify)
+        ioncore_handle_selection(&(ev->xselection));
+        break;
+    CASE_EVENT(SelectionRequest)
+        ioncore_handle_selection_request(&(ev->xselectionrequest));
+        break;
+    }
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Map, unmap, destroy */
+
+
+void ioncore_handle_map_request(const XMapRequestEvent *ev)
+{
+    WRegion *reg;
+    
+    reg=XWINDOW_REGION_OF(ev->window);
+    
+    if(reg!=NULL)
+        return;
+    
+    ioncore_manage_clientwin(ev->window, TRUE);
+}
+
+
+void ioncore_handle_unmap_notify(const XUnmapEvent *ev)
+{
+    WClientWin *cwin;
+
+    /* We are not interested in SubstructureNotify -unmaps. */
+    if(ev->event!=ev->window && ev->send_event!=True)
+        return;
+
+    cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
+    
+    if(cwin!=NULL)
+        clientwin_unmapped(cwin);
+}
+
+
+void ioncore_handle_destroy_notify(const XDestroyWindowEvent *ev)
+{
+    WClientWin *cwin;
+
+    cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
+    
+    if(cwin!=NULL)
+        clientwin_destroyed(cwin);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Client configure/property/message */
+
+
+void ioncore_handle_configure_request(XConfigureRequestEvent *ev)
+{
+    WClientWin *cwin;
+    XWindowChanges wc;
+
+    cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
+    
+    if(cwin==NULL){
+        wc.border_width=ev->border_width;
+        wc.sibling=ev->above;
+        wc.stack_mode=ev->detail;
+        wc.x=ev->x;
+        wc.y=ev->y;
+        wc.width=ev->width;
+        wc.height=ev->height;
+        XConfigureWindow(ioncore_g.dpy, ev->window, ev->value_mask, &wc);
+        return;
+    }
+
+    clientwin_handle_configure_request(cwin, ev);
+}
+
+
+void ioncore_handle_client_message(const XClientMessageEvent *ev)
+{
+    netwm_handle_client_message(ev);
+
+#if 0
+    WClientWin *cwin;
+
+    if(ev->message_type!=ioncore_g.atom_wm_change_state)
+        return;
+    
+    cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
+
+    if(cwin==NULL)
+        return;
+    
+    if(ev->format==32 && ev->data.l[0]==IconicState){
+        if(cwin->state==NormalState)
+            iconify_clientwin(cwin);
+    }
+#endif
+}
+
+
+static bool pchg_mrsh_extl(ExtlFn fn, void **p)
+{
+    extl_call(fn, "oi", NULL, p[0], ((XPropertyEvent*)p[1])->atom);
+    return TRUE;
+}
+
+static bool pchg_mrsh(void (*fn)(void *p1, void *p2), void **p)
+{
+    fn(p[0], p[1]);
+    return TRUE;
+}
+                             
+
+void ioncore_handle_property(const XPropertyEvent *ev)
+{
+    WClientWin *cwin;
+    
+    cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
+    
+    if(cwin==NULL)
+        return;
+    
+    if(ev->atom==XA_WM_HINTS){
+        XWMHints *hints;
+        hints=XGetWMHints(ioncore_g.dpy, ev->window);
+        /* region_notify/clear_activity take care of checking current state */
+        if(hints!=NULL){
+            if(hints->flags&XUrgencyHint){
+                if(!region_skip_focus((WRegion*)cwin))
+                    region_set_activity((WRegion*)cwin, SETPARAM_SET);
+            }else{
+                region_set_activity((WRegion*)cwin, SETPARAM_UNSET);
+            }
+        }
+        XFree(hints);
+    }else if(ev->atom==XA_WM_NORMAL_HINTS){
+        clientwin_get_size_hints(cwin);
+    }else if(ev->atom==XA_WM_NAME){
+        if(!(cwin->flags&CLIENTWIN_USE_NET_WM_NAME))
+            clientwin_get_set_name(cwin);
+    }else if(ev->atom==XA_WM_TRANSIENT_FOR){
+        clientwin_tfor_changed(cwin);
+    }else if(ev->atom==ioncore_g.atom_wm_protocols){
+        clientwin_get_protocols(cwin);
+    }else{
+        netwm_handle_property(cwin, ev);
+    }
+    
+    /* Call property hook */
+    {
+        void *p[2];
+        p[0]=(void*)cwin;
+        p[1]=(void*)ev;
+        hook_call(clientwin_property_change_hook, p,
+                  (WHookMarshall*)pchg_mrsh,
+                  (WHookMarshallExtl*)pchg_mrsh_extl);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. notifies */
+
+
+void ioncore_handle_mapping_notify(XEvent *ev)
+{
+    do{
+        XRefreshKeyboardMapping(&(ev->xmapping));
+    }while(XCheckTypedEvent(ioncore_g.dpy, MappingNotify, ev));
+    
+    ioncore_refresh_bindmaps();
+}
+
+
+/*}}}*/
+
+
+/*{{{ Expose */
+
+
+void ioncore_handle_expose(const XExposeEvent *ev)
+{
+    WWindow *wwin;
+    WRootWin *rootwin;
+    XEvent tmp;
+    
+    while(XCheckWindowEvent(ioncore_g.dpy, ev->window, ExposureMask, &tmp))
+        /* nothing */;
+
+    wwin=XWINDOW_REGION_OF_T(ev->window, WWindow);
+
+    if(wwin!=NULL)
+        window_draw(wwin, FALSE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Enter window, focus */
+
+
+/*extern Time ioncore_focus_time;*/
+
+
+static void do_handle_enter_window(XEvent *ev)
+{
+    XEnterWindowEvent *eev=&(ev->xcrossing);
+    WRegion *reg=NULL;
+    
+    if(ioncore_g.input_mode!=IONCORE_INPUTMODE_NORMAL)
+        return;
+    
+    /*if(ioncore_g.focus_next!=NULL && ioncore_focus_time==CurrentTime)
+        return;*/
+        
+    /*if(ioncore_await_warp())
+        return;
+    
+    if(eev->mode!=NotifyNormal && !ioncore_g.warp_enabled)
+        return;*/
+        
+    reg=XWINDOW_REGION_OF_T(eev->window, WRegion);
+    
+    if(reg==NULL)
+        return;
+        
+    if(REGION_IS_ACTIVE(reg))
+        return;
+        
+    if(region_skip_focus(reg))
+        return;
+        
+    /* If a child of 'reg' is to be focused, do not process this
+     * event.
+     */
+    if(ioncore_g.focus_next!=NULL){
+        WRegion *r2=ioncore_g.focus_next;
+        while(r2!=NULL){
+            if(r2==reg)
+                return;
+            r2=REGION_PARENT_REG(r2);
+        }
+    }
+    
+    region_goto_flags(reg, (REGION_GOTO_FOCUS|
+                            REGION_GOTO_NOWARP|
+                            REGION_GOTO_ENTERWINDOW));
+}
+
+
+void ioncore_handle_enter_window(XEvent *ev)
+{
+    do{
+        /* *sigh*, it doesn't seem reasonably simply possible to
+         * process events in-order.
+         */
+        do_handle_enter_window(ev);
+    }while(XCheckMaskEvent(ioncore_g.dpy, EnterWindowMask, ev));
+}
+
+
+static bool pointer_in_root(Window root1)
+{
+    Window root2=None, win;
+    int x, y, wx, wy;
+    uint mask;
+    
+    XQueryPointer(ioncore_g.dpy, root1, &root2, &win,
+                  &x, &y, &wx, &wy, &mask);
+    
+    return (root1==root2);
+}
+
+
+
+void ioncore_handle_focus_in(const XFocusChangeEvent *ev, bool skip)
+{
+    WRegion *reg;
+    WWindow *wwin;
+    
+    reg=XWINDOW_REGION_OF_T(ev->window, WRegion);
+    
+    if(reg==NULL)
+        return;
+
+    D(fprintf(stderr, "FI: %s %p %d %d\n", OBJ_TYPESTR(reg), reg, ev->mode, ev->detail);)
+
+    if(ev->mode==NotifyGrab)
+        return;
+
+    if(ev->detail==NotifyPointer)
+        return;
+    
+    /* Root windows appear either as WRootWins or WScreens */
+    if(ev->window==region_root_of(reg)){
+        D(fprintf(stderr, "scr-in %d %d %d\n", ROOTWIN_OF(reg)->xscr,
+                  ev->mode, ev->detail));
+        if((ev->detail==NotifyPointerRoot || ev->detail==NotifyDetailNone) &&
+           pointer_in_root(ev->window) && ioncore_g.focus_next==NULL){
+            /* Restore focus */
+            if(!skip)
+                region_set_focus(reg);
+            return;
+        }
+        /*return;*/
+    }
+
+    /* Input contexts */
+    if(OBJ_IS(reg, WWindow)){
+        wwin=(WWindow*)reg;
+        if(wwin->xic!=NULL)
+            XSetICFocus(wwin->xic);
+    }
+
+    if(ev->detail!=NotifyInferior)
+        netwm_set_active(reg);
+    
+    region_got_focus(reg);
+}
+
+
+void ioncore_handle_focus_out(const XFocusChangeEvent *ev)
+{
+    WRegion *reg;
+    WWindow *wwin;
+    
+    reg=XWINDOW_REGION_OF_T(ev->window, WRegion);
+    
+    if(reg==NULL)
+        return;
+
+    D(fprintf(stderr, "FO: %s %p %d %d\n", OBJ_TYPESTR(reg), reg, ev->mode, ev->detail);)
+
+    if(ev->mode==NotifyGrab)
+        return;
+
+    if(ev->detail==NotifyPointer)
+        return;
+
+    D(if(OBJ_IS(reg, WRootWin))
+      fprintf(stderr, "scr-out %d %d %d\n", ((WRootWin*)reg)->xscr, ev->mode, ev->detail));
+
+    if(OBJ_IS(reg, WWindow)){
+        wwin=(WWindow*)reg;
+        if(wwin->xic!=NULL)
+            XUnsetICFocus(wwin->xic);
+    }
+    
+    if(ev->detail!=NotifyInferior)
+        region_lost_focus(reg);
+    else
+        region_got_focus(reg);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Pointer, keyboard */
+
+
+void ioncore_handle_buttonpress(XEvent *ev)
+{
+    XEvent tmp;
+    Window win_pressed;
+    bool finished=FALSE;
+
+    if(ioncore_grab_held())
+        return;
+
+    win_pressed=ev->xbutton.window;
+    
+    if(!ioncore_do_handle_buttonpress(&(ev->xbutton)))
+        return;
+
+    while(!finished && ioncore_grab_held()){
+        XFlush(ioncore_g.dpy);
+        ioncore_get_event(ev, IONCORE_EVENTMASK_PTRLOOP);
+        
+        if(ev->type==MotionNotify){
+            /* Handle sequences of MotionNotify (possibly followed by button
+             * release) as one.
+             */
+            if(XPeekEvent(ioncore_g.dpy, &tmp)){
+                if(tmp.type==MotionNotify || tmp.type==ButtonRelease)
+                    XNextEvent(ioncore_g.dpy, ev);
+            }
+        }
+        
+        switch(ev->type){
+        CASE_EVENT(ButtonRelease)
+            if(ioncore_do_handle_buttonrelease(&ev->xbutton))
+                finished=TRUE;
+            break;
+        CASE_EVENT(MotionNotify)
+            ioncore_do_handle_motionnotify(&ev->xmotion);
+            break;
+        CASE_EVENT(Expose)
+            ioncore_handle_expose(&(ev->xexpose));
+            break;
+        CASE_EVENT(KeyPress)
+        CASE_EVENT(KeyRelease)
+            ioncore_handle_grabs(ev);
+            break;
+        CASE_EVENT(FocusIn)
+            ioncore_handle_focus_in(&(ev->xfocus), FALSE);
+            break;
+        CASE_EVENT(FocusOut)
+            ioncore_handle_focus_out(&(ev->xfocus));
+            break;
+        }
+    }
+}
+
+
+void ioncore_handle_keyboard(XEvent *ev)
+{
+    if(ioncore_handle_grabs(ev))
+        return;
+    
+    if(ev->type==KeyPress)
+        ioncore_do_handle_keypress(&(ev->xkey));
+}
+
+
+/*}}}*/
+
+
diff --git a/ioncore/eventh.h b/ioncore/eventh.h
new file mode 100644 (file)
index 0000000..f80257b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * ion/ioncore/eventh.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_EVENTH_H
+#define ION_IONCORE_EVENTH_H
+
+#include "common.h"
+
+extern bool ioncore_handle_event(XEvent *ev);
+extern void ioncore_handle_expose(const XExposeEvent *ev);
+extern void ioncore_handle_map_request(const XMapRequestEvent *ev);
+extern void ioncore_handle_configure_request(XConfigureRequestEvent *ev);
+extern void ioncore_handle_enter_window(XEvent *ev);
+extern void ioncore_handle_unmap_notify(const XUnmapEvent *ev);
+extern void ioncore_handle_destroy_notify(const XDestroyWindowEvent *ev);
+extern void ioncore_handle_client_message(const XClientMessageEvent *ev);
+extern void ioncore_handle_focus_in(const XFocusChangeEvent *ev, bool skip);
+extern void ioncore_handle_focus_out(const XFocusChangeEvent *ev);
+extern void ioncore_handle_property(const XPropertyEvent *ev);
+extern void ioncore_handle_buttonpress(XEvent *ev);
+extern void ioncore_handle_keyboard(XEvent *ev);
+extern void ioncore_handle_mapping_notify(XEvent *ev);
+
+#endif /* ION_IONCORE_EVENTH_H */
diff --git a/ioncore/exec.c b/ioncore/exec.c
new file mode 100644 (file)
index 0000000..10c8267
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * ion/ioncore/exec.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <libmainloop/select.h>
+#include <libmainloop/exec.h>
+
+#include "common.h"
+#include "exec.h"
+#include "property.h"
+#include "global.h"
+#include "ioncore.h"
+#include "saveload.h"
+
+
+/*{{{ Exec */
+
+
+void ioncore_setup_environ(int xscr)
+{
+    char *tmp, *ptr;
+    char *display;
+    
+    display=XDisplayName(ioncore_g.display);
+    
+    /* %ui, UINT_MAX is used to ensure there is enough space for the screen
+     * number
+     */
+    libtu_asprintf(&tmp, "DISPLAY=%s.0123456789a", display);
+
+    if(tmp==NULL)
+        return;
+
+    ptr=strchr(tmp, ':');
+    if(ptr!=NULL){
+        ptr=strchr(ptr, '.');
+        if(ptr!=NULL)
+            *ptr='\0';
+    }
+
+    if(xscr>=0)
+        snprintf(tmp+strlen(tmp), 11, ".%u", (unsigned)xscr);
+    
+    putenv(tmp);
+    
+    /* No need to free it, we'll execve soon */
+    /*free(tmp);*/
+
+    /*XFree(display);*/
+}
+
+
+typedef struct{
+    WRootWin *rw;
+    const char *wd;
+} ExecP;
+
+
+static void setup_exec(void *p_)
+{
+    ExecP *p=(ExecP*)p_;
+    
+    close(ioncore_g.conn);
+    
+    ioncore_g.dpy=NULL;
+    
+#ifndef CF_NO_SETPGID
+    setpgid(0, 0);
+#endif
+    
+    ioncore_setup_environ(p->rw==NULL ? -1 : p->rw->xscr);
+    
+    if(p->wd!=NULL){
+        if(chdir(p->wd)!=0)
+            warn_err_obj(p->wd);
+    }
+}
+
+
+EXTL_EXPORT
+int ioncore_do_exec_rw(WRootWin *rw, const char *cmd, const char *wd,
+                       ExtlFn errh)
+{
+    ExecP p;
+    
+    p.rw=rw;
+    p.wd=wd;
+    
+    return mainloop_popen_bgread(cmd, setup_exec, (void*)&p, 
+                                 extl_fn_none(), errh);
+}
+
+
+/*EXTL_DOC
+ * Run \var{cmd} with the environment variable DISPLAY set to point to the
+ * X display the WM is running on. No specific screen is set unlike with
+ * \fnref{WRootWin.exec_on}. The PID of the (shell executing the) new 
+ * process is returned.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+int ioncore_exec(const char *cmd)
+{
+    return ioncore_do_exec_rw(NULL, cmd, NULL, extl_fn_none());
+}
+
+
+/*EXTL_DOC
+ * Run \var{cmd} with a read pipe connected to its stdout.
+ * When data is received through the pipe, \var{handler} is called
+ * with that data.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+int ioncore_popen_bgread(const char *cmd, ExtlFn h, ExtlFn errh)
+{
+    ExecP p;
+    
+    p.rw=NULL;
+    p.wd=NULL;
+    
+    return mainloop_popen_bgread(cmd, setup_exec, (void*)&p, h, errh);
+}
+
+
+
+/*}}}*/
+
+
+/*{{{ Exit, restart, snapshot */
+
+
+static void (*smhook)(int what);
+
+bool ioncore_set_smhook(void (*fn)(int what))
+{
+    smhook=fn;
+    return TRUE;
+}
+
+
+void ioncore_do_exit()
+{
+    ioncore_deinit();
+    exit(0);
+}
+
+
+bool ioncore_do_snapshot()
+{
+    if(!ioncore_save_layout())
+        return FALSE;
+
+    extl_protect(NULL);
+    hook_call_v(ioncore_snapshot_hook);
+    extl_unprotect(NULL);
+    
+    return TRUE;
+}
+
+
+void ioncore_emergency_snapshot()
+{
+    if(smhook!=NULL)
+        warn(TR("Not saving state: running under session manager."));
+    else
+        ioncore_do_snapshot();
+}
+
+
+
+static char *other=NULL;
+
+static void set_other(const char *s)
+{
+    if(other!=NULL)
+        free(other);
+    other=(s==NULL ? NULL : scopy(s));
+}
+
+
+void ioncore_do_restart()
+{
+    ioncore_deinit();
+    if(other!=NULL){
+        if(ioncore_g.display!=NULL)
+            ioncore_setup_environ(-1);
+        mainloop_do_exec(other);
+        warn_err_obj(other);
+    }
+    execvp(ioncore_g.argv[0], ioncore_g.argv);
+    die_err_obj(ioncore_g.argv[0]);
+}
+
+
+/*EXTL_DOC
+ * Causes the window manager to simply exit without saving
+ * state/session.
+ */
+EXTL_EXPORT
+void ioncore_resign()
+{
+    if(smhook!=NULL){
+        smhook(IONCORE_SM_RESIGN);
+    }else{
+        ioncore_do_exit();
+    }
+}
+
+
+/*EXTL_DOC
+ * End session saving it first.
+ */
+EXTL_EXPORT
+void ioncore_shutdown()
+{
+    if(smhook!=NULL){
+        smhook(IONCORE_SM_SHUTDOWN);
+    }else{
+        ioncore_do_snapshot();
+        ioncore_do_exit();
+    }
+}
+
+
+/*EXTL_DOC
+ * Restart, saving session first.
+ */
+EXTL_EXPORT
+void ioncore_restart()
+{
+    set_other(NULL);
+    
+    if(smhook!=NULL){
+        smhook(IONCORE_SM_RESTART);
+    }else{
+        ioncore_do_snapshot();
+        ioncore_do_restart();
+    }
+}
+
+
+/*EXTL_DOC
+ * Attempt to restart another window manager \var{cmd}.
+ */
+EXTL_EXPORT
+void ioncore_restart_other(const char *cmd)
+{
+    set_other(cmd);
+    
+    if(smhook!=NULL){
+        smhook(IONCORE_SM_RESTART_OTHER);
+    }else{
+        ioncore_do_snapshot();
+        ioncore_do_restart();
+    }
+}
+
+
+/*EXTL_DOC
+ * Save session.
+ */
+EXTL_EXPORT
+void ioncore_snapshot()
+{
+    if(smhook!=NULL)
+        smhook(IONCORE_SM_SNAPSHOT);
+    else
+        ioncore_do_snapshot();
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/exec.h b/ioncore/exec.h
new file mode 100644 (file)
index 0000000..63c8917
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * ion/ioncore/exec.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_EXEC_H
+#define ION_IONCORE_EXEC_H
+
+#include "common.h"
+#include "rootwin.h"
+#include <libextl/extl.h>
+
+enum{
+    IONCORE_SM_RESIGN,
+    IONCORE_SM_SHUTDOWN,
+    IONCORE_SM_RESTART,
+    IONCORE_SM_RESTART_OTHER,
+    IONCORE_SM_SNAPSHOT
+};
+
+extern void ioncore_do_exec(const char *cmd);
+extern bool ioncore_exec_on_rootwin(WRootWin *rootwin, const char *cmd);
+extern bool ioncore_exec(const char *cmd);
+extern void ioncore_setup_environ(int scr);
+extern bool ioncore_popen_bgread(const char *cmd, ExtlFn h, ExtlFn errh);
+
+extern bool ioncore_set_smhook(void (*fn)(int what));
+
+extern void ioncore_restart_other(const char *cmd);
+extern void ioncore_restart();
+extern void ioncore_shutdown();
+extern void ioncore_resign();
+extern void ioncore_snapshot();
+
+extern void ioncore_do_exit();
+extern void ioncore_do_restart();
+extern bool ioncore_do_snapshot();
+
+extern void ioncore_emergency_snapshot();
+
+#endif /* ION_IONCORE_EXEC_H */
diff --git a/ioncore/extlconv.c b/ioncore/extlconv.c
new file mode 100644 (file)
index 0000000..5f23b74
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * ion/ioncore/extlconv.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libextl/extl.h>
+#include "common.h"
+#include "extlconv.h"
+
+
+/*{{{ Object list */
+
+
+ExtlTab extl_obj_iterable_to_table(ObjIterator *iter, void *st)
+{
+    ExtlTab tab=extl_create_table();
+    int i=1;
+    Obj *obj;
+    
+    while(1){
+        obj=iter(st);
+        if(obj==NULL)
+            break;
+        if(extl_table_seti_o(tab, i, obj))
+            i++;
+    }
+    
+    return tab;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Booleans */
+
+
+bool extl_table_is_bool_set(ExtlTab tab, const char *entry)
+{
+    bool b;
+    
+    if(extl_table_gets_b(tab, entry, &b))
+        return b;
+    return FALSE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Rectangles */
+
+
+bool extl_table_to_rectangle(ExtlTab tab, WRectangle *rectret)
+{
+    if(!extl_table_gets_i(tab, "x", &(rectret->x)) ||
+       !extl_table_gets_i(tab, "y", &(rectret->y)) ||
+       !extl_table_gets_i(tab, "w", &(rectret->w)) ||
+       !extl_table_gets_i(tab, "h", &(rectret->h)))
+       return FALSE;
+    
+    return TRUE;
+}
+
+
+ExtlTab extl_table_from_rectangle(const WRectangle *rect)
+{
+    ExtlTab tab=extl_create_table();
+    
+    extl_table_sets_i(tab, "x", rect->x);
+    extl_table_sets_i(tab, "y", rect->y);
+    extl_table_sets_i(tab, "w", rect->w);
+    extl_table_sets_i(tab, "h", rect->h);
+    
+    return tab;
+}
+
+
+void extl_table_sets_rectangle(ExtlTab tab, const char *nam, 
+                               const WRectangle *rect)
+{
+    ExtlTab g=extl_table_from_rectangle(rect);
+    extl_table_sets_t(tab, nam, g);
+    extl_unref_table(g);
+}
+
+
+bool extl_table_gets_rectangle(ExtlTab tab, const char *nam, 
+                               WRectangle *rect)
+{
+    ExtlTab g;
+    bool ok;
+    
+    if(!extl_table_gets_t(tab, nam, &g))
+        return FALSE;
+    
+    ok=extl_table_to_rectangle(g, rect);
+    
+    extl_unref_table(g);
+    
+    return ok;
+}
+
+
+/*}}}*/
+
+
diff --git a/ioncore/extlconv.h b/ioncore/extlconv.h
new file mode 100644 (file)
index 0000000..b4e3b80
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * ion/ioncore/extlconv.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_EXTLCONV_H
+#define ION_IONCORE_EXTLCONV_H
+
+#include <libextl/extl.h>
+#include <libtu/iterable.h>
+#include <libtu/setparam.h>
+#include "common.h"
+#include "region.h"
+
+extern ExtlTab extl_obj_iterable_to_table(ObjIterator *iter, void *st);
+
+extern bool extl_table_is_bool_set(ExtlTab tab, const char *entry);
+
+extern bool extl_table_to_rectangle(ExtlTab tab, WRectangle *rect);
+extern ExtlTab extl_table_from_rectangle(const WRectangle *rect);
+
+extern bool extl_table_gets_rectangle(ExtlTab tab, const char *nam,
+                                      WRectangle *rect);
+extern void extl_table_sets_rectangle(ExtlTab tab, const char *nam,
+                                      const WRectangle *rect);
+
+#endif /* ION_IONCORE_EXTLCONV_H */
+
diff --git a/ioncore/extlrx.c b/ioncore/extlrx.c
new file mode 100644 (file)
index 0000000..b9a380e
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * ion/ioncore/extlrx.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include "common.h"
+
+
+/*{{{ libtu */
+
+
+/*EXTL_DOC
+ * Issue a warning. How the message is displayed depends on the current
+ * warning handler.
+ */
+EXTL_SAFE
+EXTL_UNTRACED
+EXTL_EXPORT
+void ioncore_warn(const char *str)
+{
+    warn("%s", str);
+}
+
+
+/*EXTL_DOC
+ * Similar to \fnref{ioncore.warn}, but also print Lua stack trace.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+void ioncore_warn_traced(const char *str)
+{
+    warn("%s", str);
+}
+
+
+EXTL_SAFE
+EXTL_EXPORT
+const char *ioncore_gettext(const char *s)
+{
+    if(s==NULL)
+        return NULL;
+    else
+        return TR(s);
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/float-placement.c b/ioncore/float-placement.c
new file mode 100644 (file)
index 0000000..399f5ec
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * ion/ioncore/float-placement.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include "common.h"
+#include "group.h"
+#include "float-placement.h"
+
+
+WFloatPlacement ioncore_placement_method=PLACEMENT_LRUD;
+
+
+static void random_placement(WRectangle box, WRectangle *g)
+{
+    box.w-=g->w;
+    box.h-=g->h;
+    g->x=box.x+(box.w<=0 ? 0 : rand()%box.w);
+    g->y=box.y+(box.h<=0 ? 0 : rand()%box.h);
+}
+
+
+static void ggeom(WRegion *reg, WRectangle *geom)
+{
+    *geom=REGION_GEOM(reg);
+}
+
+
+#define IGNORE_ST(ST, WS) ((ST)->reg==NULL || (ST)==(WS)->bottom)
+
+static WRegion* is_occupied(WGroup *ws, const WRectangle *r)
+{
+    WGroupIterTmp tmp;
+    WStacking *st;
+    WRectangle p;
+    
+    FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
+        if(IGNORE_ST(st, ws))
+            continue;
+        
+        ggeom(st->reg, &p);
+        
+        if(r->x>=p.x+p.w)
+            continue;
+        if(r->y>=p.y+p.h)
+            continue;
+        if(r->x+r->w<=p.x)
+            continue;
+        if(r->y+r->h<=p.y)
+            continue;
+        return st->reg;
+    }
+    
+    return NULL;
+}
+
+
+static int next_least_x(WGroup *ws, int x)
+{
+    WRectangle p;
+    int retx=REGION_GEOM(ws).x+REGION_GEOM(ws).w;
+    WGroupIterTmp tmp;
+    WStacking *st;
+    
+    FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
+        if(IGNORE_ST(st, ws))
+            continue;
+        
+        ggeom(st->reg, &p);
+        
+        if(p.x+p.w>x && p.x+p.w<retx)
+            retx=p.x+p.w;
+    }
+    
+    return retx+1;
+}
+
+
+static int next_lowest_y(WGroup *ws, int y)
+{
+    WRectangle p;
+    int rety=REGION_GEOM(ws).y+REGION_GEOM(ws).h;
+    WGroupIterTmp tmp;
+    WStacking *st;
+    
+    FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
+        if(IGNORE_ST(st, ws))
+            continue;
+        
+        ggeom(st->reg, &p);
+        
+        if(p.y+p.h>y && p.y+p.h<rety)
+            rety=p.y+p.h;
+    }
+    
+    return rety+1;
+}
+
+
+static bool tiling_placement(WGroup *ws, WRectangle *g)
+{
+    WRegion *p;
+    WRectangle r, r2;
+    int maxx, maxy;
+    
+    r=REGION_GEOM(ws);
+    r.w=g->w;
+    r.h=g->h;
+
+    maxx=REGION_GEOM(ws).x+REGION_GEOM(ws).w;
+    maxy=REGION_GEOM(ws).y+REGION_GEOM(ws).h;
+    
+    if(ioncore_placement_method==PLACEMENT_UDLR){
+        while(r.x<maxx){
+            p=is_occupied(ws, &r);
+            while(p!=NULL && r.y+r.h<maxy){
+                ggeom(p, &r2);
+                r.y=r2.y+r2.h+1;
+                p=is_occupied(ws, &r);
+            }
+            if(r.y+r.h<maxy && r.x+r.w<maxx){
+                g->x=r.x;
+                g->y=r.y;
+                return TRUE;
+            }else{
+                r.x=next_least_x(ws, r.x);
+                r.y=0;
+            }
+        }
+    }else{
+        while(r.y<maxy){
+            p=is_occupied(ws, &r);
+            while(p!=NULL && r.x+r.w<maxx){
+                ggeom(p, &r2);
+                r.x=r2.x+r2.w+1;
+                p=is_occupied(ws, &r);
+            }
+            if(r.y+r.h<maxy && r.x+r.w<maxx){
+                g->x=r.x;
+                g->y=r.y;
+                return TRUE;
+            }else{
+                r.y=next_lowest_y(ws, r.y);
+                r.x=0;
+            }
+        }
+    }
+
+    return FALSE;
+
+}
+
+
+void group_calc_placement(WGroup *ws, WRectangle *geom)
+{
+    if(ioncore_placement_method!=PLACEMENT_RANDOM){
+        if(tiling_placement(ws, geom))
+            return;
+    }
+    random_placement(REGION_GEOM(ws), geom);
+}
+
diff --git a/ioncore/float-placement.h b/ioncore/float-placement.h
new file mode 100644 (file)
index 0000000..0f52daf
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * ion/ioncore/float-placement.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_FLOAT_PLACEMENT_H
+#define ION_IONCORE_FLOAT_PLACEMENT_H
+
+#include "common.h"
+#include "group.h"
+
+
+typedef enum{
+    PLACEMENT_LRUD, PLACEMENT_UDLR, PLACEMENT_RANDOM
+} WFloatPlacement;
+
+extern WFloatPlacement ioncore_placement_method;
+
+extern void group_calc_placement(WGroup *ws, WRectangle *geom);
+
+#endif /* ION_IONCORE_FLOAT_PLACEMENT_H */
diff --git a/ioncore/focus.c b/ioncore/focus.c
new file mode 100644 (file)
index 0000000..fb2159b
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * ion/ioncore/focus.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libmainloop/hooks.h>
+#include "common.h"
+#include "focus.h"
+#include "global.h"
+#include "window.h"
+#include "region.h"
+#include "colormap.h"
+#include "activity.h"
+#include "xwindow.h"
+#include "regbind.h"
+
+
+/*{{{ Hooks. */
+
+
+WHook *region_do_warp_alt=NULL;
+WHook *region_activated_hook=NULL;
+WHook *region_inactivated_hook=NULL;
+
+
+/*}}}*/
+
+
+/*{{{ Focus list */
+
+
+void region_focuslist_remove(WRegion *reg)
+{
+    UNLINK_ITEM(ioncore_g.focus_current, reg, active_next, active_prev);
+}
+
+
+void region_focuslist_push(WRegion *reg)
+{
+    region_focuslist_remove(reg);
+    LINK_ITEM_FIRST(ioncore_g.focus_current, reg, active_next, active_prev);
+}
+
+
+void region_focuslist_move_after(WRegion *reg, WRegion *after)
+{
+    region_focuslist_remove(reg);
+    LINK_ITEM_AFTER(ioncore_g.focus_current, after, reg, 
+                    active_next, active_prev);
+}
+
+
+void region_focuslist_deinit(WRegion *reg)
+{
+    WRegion *replace=region_manager_or_parent(reg);
+    
+    if(replace!=NULL)
+        region_focuslist_move_after(replace, reg);
+    
+    region_focuslist_remove(reg);
+}
+
+
+/*EXTL_DOC
+ * Go to and return to a previously active region (if any).
+ * 
+ * Note that this function is asynchronous; the region will not
+ * actually have received the focus when this function returns.
+ */
+EXTL_EXPORT
+WRegion *ioncore_goto_previous()
+{
+    WRegion *next;
+    
+    if(ioncore_g.focus_current==NULL)
+        return NULL;
+    
+    /* Find the first region on focus history list that isn't currently
+     * active.
+     */
+    for(next=ioncore_g.focus_current->active_next;
+        next!=NULL; 
+        next=next->active_next){
+        
+        if(!REGION_IS_ACTIVE(next))
+            break;
+    }
+    
+    if(next!=NULL)
+        region_goto(next);
+    
+    return next;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Await focus */
+
+
+static Watch await_watch=WATCH_INIT;
+
+
+static void await_watch_handler(Watch *watch, WRegion *prev)
+{
+    WRegion *r;
+    while(1){
+        r=REGION_PARENT_REG(prev);
+        if(r==NULL)
+            break;
+        
+        if(watch_setup(&await_watch, (Obj*)r, 
+                       (WatchHandler*)await_watch_handler))
+            break;
+        prev=r;
+    }
+}
+
+
+void region_set_await_focus(WRegion *reg)
+{
+    if(reg==NULL){
+        watch_reset(&await_watch);
+    }else{
+        watch_setup(&await_watch, (Obj*)reg,
+                    (WatchHandler*)await_watch_handler);
+    }
+}
+
+
+static bool region_is_await(WRegion *reg)
+{
+    WRegion *aw=(WRegion*)await_watch.obj;
+    
+    while(aw!=NULL){
+        if(aw==reg)
+            return TRUE;
+        aw=REGION_PARENT_REG(aw);
+    }
+    
+    return FALSE;
+}
+
+
+/* Only keep await status if focus event is to an ancestor of the await 
+ * region.
+ */
+static void check_clear_await(WRegion *reg)
+{
+    if(region_is_await(reg) && reg!=(WRegion*)await_watch.obj)
+        return;
+    
+    watch_reset(&await_watch);
+}
+
+
+bool ioncore_await_focus()
+{
+    return (await_watch.obj!=NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Events */
+
+
+void region_got_focus(WRegion *reg)
+{
+    WRegion *par, *mgr, *tmp;
+    
+    check_clear_await(reg);
+    
+    region_set_activity(reg, SETPARAM_UNSET);
+
+    if(reg->active_sub==NULL){
+        region_focuslist_push(reg);
+        /*ioncore_g.focus_current=reg;*/
+    }
+    
+    if(!REGION_IS_ACTIVE(reg)){
+        D(fprintf(stderr, "got focus (inact) %s [%p]\n", OBJ_TYPESTR(reg), reg);)
+        reg->flags|=REGION_ACTIVE;
+        
+        par=REGION_PARENT_REG(reg);
+        if(par!=NULL){
+            par->active_sub=reg;
+            region_update_owned_grabs(par);
+        }
+
+        region_activated(reg);
+        
+        mgr=REGION_MANAGER(reg);
+        tmp=reg;
+        while(mgr!=NULL){
+            /* We need to loop over managing non-windows (workspaces) here to
+             * signal their managers.
+             */
+            region_managed_activated(mgr, tmp);
+            
+            if(REGION_PARENT_REG(reg)==mgr)
+                break;
+            
+            tmp=mgr;
+            mgr=REGION_MANAGER(mgr);
+        }
+    }else{
+        D(fprintf(stderr, "got focus (act) %s [%p]\n", OBJ_TYPESTR(reg), reg);)
+    }
+
+    /* Install default colour map only if there is no active subregion;
+     * their maps should come first. WClientWins will install their maps
+     * in region_activated. Other regions are supposed to use the same
+     * default map.
+     */
+    if(reg->active_sub==NULL && !OBJ_IS(reg, WClientWin))
+        rootwin_install_colormap(region_rootwin_of(reg), None); 
+    
+    extl_protect(NULL);
+    hook_call_o(region_activated_hook, (Obj*)reg);
+    extl_unprotect(NULL);
+}
+
+
+void region_lost_focus(WRegion *reg)
+{
+    WRegion *r, *par;
+    
+    if(!REGION_IS_ACTIVE(reg)){
+        D(fprintf(stderr, "lost focus (inact) %s [%p:]\n", OBJ_TYPESTR(reg), reg);)
+        return;
+    }
+    
+    par=REGION_PARENT_REG(reg);
+    if(par!=NULL && par->active_sub==reg){
+        par->active_sub=NULL;
+        region_update_owned_grabs(par);
+    }
+
+
+#if 0    
+    if(ioncore_g.focus_current==reg){
+        /* Find the closest active parent, or if none is found, stop at the
+         * screen and mark it "currently focused".
+         */
+        while(par!=NULL && !REGION_IS_ACTIVE(par) && !OBJ_IS(par, WScreen))
+            par=REGION_PARENT_REG(par);
+        ioncore_g.focus_current=par;
+    }
+#endif
+
+    D(fprintf(stderr, "lost focus (act) %s [%p:]\n", OBJ_TYPESTR(reg), reg);)
+    
+    reg->flags&=~REGION_ACTIVE;
+    region_inactivated(reg);
+    r=REGION_MANAGER(reg);
+    if(r!=NULL)
+        region_managed_inactivated(r, reg);
+    
+    extl_protect(NULL);
+    hook_call_o(region_inactivated_hook, (Obj*)reg);
+    extl_unprotect(NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Focus status requests */
+
+
+/*EXTL_DOC
+ * Is \var{reg} active/does it or one of it's children of focus?
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+bool region_is_active(WRegion *reg)
+{
+    return REGION_IS_ACTIVE(reg);
+}
+
+
+bool region_may_control_focus(WRegion *reg)
+{
+    WRegion *par, *r2;
+    
+    if(OBJ_IS_BEING_DESTROYED(reg))
+        return FALSE;
+
+    if(REGION_IS_ACTIVE(reg))
+        return TRUE;
+    
+    if(region_is_await(reg))
+        return TRUE;
+    
+    par=REGION_PARENT_REG(reg);
+    
+    if(par==NULL || !REGION_IS_ACTIVE(par))
+        return FALSE;
+    
+    r2=par->active_sub;
+    while(r2!=NULL && r2!=par){
+        if(r2==reg)
+            return TRUE;
+        r2=REGION_MANAGER(r2);
+    }
+
+    return FALSE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ set_focus, warp */
+
+
+/*Time ioncore_focus_time=CurrentTime;*/
+
+
+void region_finalise_focusing(WRegion* reg, Window win, bool warp)
+{
+    if(warp)
+        region_do_warp(reg);
+    
+    region_set_await_focus(reg);
+    /*xwindow_do_set_focus(win);*/
+    XSetInputFocus(ioncore_g.dpy, win, RevertToParent, 
+                   CurrentTime/*ioncore_focus_time*/);
+    /*ioncore_focus_time=CurrentTime;*/
+}
+
+
+
+static WRegion *find_warp_to_reg(WRegion *reg)
+{
+    if(reg==NULL)
+        return NULL;
+    if(reg->flags&REGION_PLEASE_WARP)
+        return reg;
+    return find_warp_to_reg(region_manager_or_parent(reg));
+}
+
+
+bool region_do_warp_default(WRegion *reg)
+{
+    int x, y, w, h, px=0, py=0;
+    Window root;
+    
+    reg=find_warp_to_reg(reg);
+    
+    if(reg==NULL)
+        return FALSE;
+    
+    D(fprintf(stderr, "region_do_warp %p %s\n", reg, OBJ_TYPESTR(reg)));
+    
+    root=region_root_of(reg);
+    
+    region_rootpos(reg, &x, &y);
+    w=REGION_GEOM(reg).w;
+    h=REGION_GEOM(reg).h;
+
+    if(xwindow_pointer_pos(root, &px, &py)){
+        if(px>=x && py>=y && px<x+w && py<y+h)
+            return TRUE;
+    }
+    
+    XWarpPointer(ioncore_g.dpy, None, root, 0, 0, 0, 0,
+                 x+5, y+5);
+        
+    return TRUE;
+}
+
+
+void region_do_warp(WRegion *reg)
+{
+    extl_protect(NULL);
+    hook_call_alt_o(region_do_warp_alt, (Obj*)reg);
+    extl_unprotect(NULL);
+}
+
+
+void region_maybewarp(WRegion *reg, bool warp)
+{
+    ioncore_g.focus_next=reg;
+    ioncore_g.warp_next=(warp && ioncore_g.warp_enabled);
+}
+
+
+void region_set_focus(WRegion *reg)
+{
+    region_maybewarp(reg, FALSE);
+}
+
+
+void region_warp(WRegion *reg)
+{
+    region_maybewarp(reg, TRUE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+bool region_skip_focus(WRegion *reg)
+{
+    while(reg!=NULL){
+        if(reg->flags&REGION_SKIP_FOCUS)
+            return TRUE;
+        reg=REGION_PARENT_REG(reg);
+    }
+    return FALSE;
+}
+
+/*EXTL_DOC
+ * Returns the currently focused region, if any.
+ */
+EXTL_EXPORT
+WRegion *ioncore_current()
+{
+    return ioncore_g.focus_current;
+}
+
+/*}}}*/
diff --git a/ioncore/focus.h b/ioncore/focus.h
new file mode 100644 (file)
index 0000000..e995d6d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * ion/ioncore/focus.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_FOCUS_H
+#define ION_IONCORE_FOCUS_H
+
+#include <libmainloop/hooks.h>
+#include "common.h"
+#include "window.h"
+#include "region.h"
+
+DYNFUN void region_do_set_focus(WRegion *reg, bool warp);
+
+/* Delayed (until return to main loop) warp/focus */
+extern void region_warp(WRegion *reg);
+extern void region_set_focus(WRegion *reg);
+extern void region_maybewarp(WRegion *reg, bool warp);
+
+/* Immediate warp/focus */
+extern void region_do_warp(WRegion *reg);
+extern bool region_do_warp_default(WRegion *reg);
+
+extern void region_finalise_focusing(WRegion* reg, Window win, bool warp);
+
+/* Awaiting focus state */
+extern void region_set_await_focus(WRegion *reg);
+extern bool ioncore_await_focus();
+
+/* Event handling */
+extern void region_got_focus(WRegion *reg);
+extern void region_lost_focus(WRegion *reg);
+
+/* May reg transfer focus to its children? */
+extern bool region_may_control_focus(WRegion *reg);
+
+/* Does reg have focus? */
+extern bool region_is_active(WRegion *reg);
+
+/* Focus history */
+extern void region_focuslist_remove(WRegion *reg);
+extern void region_focuslist_push(WRegion *reg);
+extern void region_focuslist_move_after(WRegion *reg, WRegion *after);
+extern void region_focuslist_deinit(WRegion *reg);
+
+extern WRegion *ioncore_goto_previous();
+
+/* Handlers to these shook should take WRegion* as parameter. */
+extern WHook *region_do_warp_alt;
+extern WHook *region_activated_hook;
+extern WHook *region_inactivated_hook;
+
+/* Misc. */
+extern bool region_skip_focus(WRegion *reg);
+WRegion *ioncore_current();
+
+#endif /* ION_IONCORE_FOCUS_H */
diff --git a/ioncore/frame-draw.c b/ioncore/frame-draw.c
new file mode 100644 (file)
index 0000000..116f10c
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * ion/ioncore/frame-draw.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <libtu/map.h>
+
+#include "common.h"
+#include "frame.h"
+#include "framep.h"
+#include "frame-draw.h"
+#include "strings.h"
+#include "names.h"
+#include "gr.h"
+
+
+#define BAR_INSIDE_BORDER(FRAME) \
+    ((FRAME)->barmode==FRAME_BAR_INSIDE || (FRAME)->barmode==FRAME_BAR_NONE)
+#define BAR_EXISTS(FRAME) ((FRAME)->barmode!=FRAME_BAR_NONE)
+#define BAR_H(FRAME) (FRAME)->bar_h
+
+
+/*{{{ (WFrame) dynfun default implementations */
+
+
+static uint get_spacing(const WFrame *frame)
+{
+    GrBorderWidths bdw;
+    
+    if(frame->brush==NULL)
+        return 0;
+    
+    grbrush_get_border_widths(frame->brush, &bdw);
+    
+    return bdw.spacing;
+}
+
+
+void frame_border_geom(const WFrame *frame, WRectangle *geom)
+{
+    geom->x=0;
+    geom->y=0;
+    geom->w=REGION_GEOM(frame).w;
+    geom->h=REGION_GEOM(frame).h;
+    
+    if(!BAR_INSIDE_BORDER(frame) && frame->brush!=NULL){
+        geom->y+=frame->bar_h;
+        geom->h=maxof(0, geom->h-frame->bar_h);
+    }
+}
+
+
+void frame_border_inner_geom(const WFrame *frame, WRectangle *geom)
+{
+    GrBorderWidths bdw;
+    
+    frame_border_geom(frame, geom);
+
+    if(frame->brush!=NULL){
+        grbrush_get_border_widths(frame->brush, &bdw);
+
+        geom->x+=bdw.left;
+        geom->y+=bdw.top;
+        geom->w=maxof(0, geom->w-(bdw.left+bdw.right));
+        geom->h=maxof(0, geom->h-(bdw.top+bdw.bottom));
+    }
+}
+
+
+void frame_bar_geom(const WFrame *frame, WRectangle *geom)
+{
+    uint off;
+    
+    if(BAR_INSIDE_BORDER(frame)){
+        off=get_spacing(frame);
+        frame_border_inner_geom(frame, geom);
+    }else{
+        off=0;
+        geom->x=0;
+        geom->y=0;
+        geom->w=(frame->barmode==FRAME_BAR_SHAPED
+                 ? frame->bar_w
+                 : REGION_GEOM(frame).w);
+    }
+    geom->x+=off;
+    geom->y+=off;
+    geom->w=maxof(0, geom->w-2*off);
+    geom->h=BAR_H(frame);
+}
+
+
+void frame_managed_geom(const WFrame *frame, WRectangle *geom)
+{
+    uint spacing=get_spacing(frame);
+    
+    frame_border_inner_geom(frame, geom);
+    
+    geom->x+=spacing;
+    geom->y+=spacing;
+    geom->w-=2*spacing;
+    geom->h-=2*spacing;
+    
+    if(BAR_INSIDE_BORDER(frame) && BAR_EXISTS(frame)){
+        geom->y+=frame->bar_h+spacing;
+        geom->h-=frame->bar_h+spacing;
+    }
+    
+    geom->w=maxof(geom->w, 0);
+    geom->h=maxof(geom->h, 0);
+}
+
+
+void frame_set_shape(WFrame *frame)
+{
+    WRectangle gs[2];
+    int n=0;
+    
+    if(frame->brush!=NULL){
+        if(BAR_EXISTS(frame)){
+            frame_bar_geom(frame, gs+n);
+            n++;
+        }
+        frame_border_geom(frame, gs+n);
+        n++;
+    
+        grbrush_set_window_shape(frame->brush, TRUE, n, gs);
+    }
+}
+
+
+void frame_clear_shape(WFrame *frame)
+{
+    grbrush_set_window_shape(frame->brush, TRUE, 0, NULL);
+}
+
+
+#define CF_TAB_MAX_TEXT_X_OFF 10
+
+
+static void frame_shaped_recalc_bar_size(WFrame *frame)
+{
+    int bar_w=0, textw=0, tmaxw=frame->tab_min_w, tmp=0;
+    WLListIterTmp itmp;
+    WRegion *sub;
+    const char *p;
+    GrBorderWidths bdw;
+    char *title;
+    uint bdtotal;
+    int i, m;
+    
+    if(frame->bar_brush==NULL)
+        return;
+    
+    m=FRAME_MCOUNT(frame);
+    
+    if(m>0){
+        grbrush_get_border_widths(frame->bar_brush, &bdw);
+        bdtotal=((m-1)*(bdw.tb_ileft+bdw.tb_iright)
+                 +bdw.right+bdw.left);
+
+        FRAME_MX_FOR_ALL(sub, frame, itmp){
+            p=region_displayname(sub);
+            if(p==NULL)
+                continue;
+            
+            textw=grbrush_get_text_width(frame->bar_brush,
+                                         p, strlen(p));
+            if(textw>tmaxw)
+                tmaxw=textw;
+        }
+
+        bar_w=frame->bar_max_width_q*REGION_GEOM(frame).w;
+        if(bar_w<frame->tab_min_w && 
+           REGION_GEOM(frame).w>frame->tab_min_w)
+            bar_w=frame->tab_min_w;
+        
+        tmp=bar_w-bdtotal-m*tmaxw;
+        
+        if(tmp>0){
+            /* No label truncation needed, good. See how much can be padded. */
+            tmp/=m*2;
+            if(tmp>CF_TAB_MAX_TEXT_X_OFF)
+                tmp=CF_TAB_MAX_TEXT_X_OFF;
+            bar_w=(tmaxw+tmp*2)*m+bdtotal;
+        }else{
+            /* Some labels must be truncated */
+        }
+    }else{
+        bar_w=frame->tab_min_w;
+        if(bar_w>frame->bar_max_width_q*REGION_GEOM(frame).w)
+            bar_w=frame->bar_max_width_q*REGION_GEOM(frame).w;
+    }
+
+    if(frame->bar_w!=bar_w){
+        frame->bar_w=bar_w;
+        frame_set_shape(frame);
+    }
+}
+
+
+static int init_title(WFrame *frame, int i)
+{
+    int textw;
+    
+    if(frame->titles[i].text!=NULL){
+        free(frame->titles[i].text);
+        frame->titles[i].text=NULL;
+    }
+    
+    textw=frame_nth_tab_iw((WFrame*)frame, i);
+    frame->titles[i].iw=textw;
+    return textw;
+}
+
+
+void frame_recalc_bar(WFrame *frame)
+{
+    int textw, i;
+    WLListIterTmp tmp;
+    WRegion *sub;
+    char *title;
+
+    if(frame->bar_brush==NULL || frame->titles==NULL)
+        return;
+    
+    if(frame->barmode==FRAME_BAR_SHAPED)
+        frame_shaped_recalc_bar_size(frame);
+    
+    i=0;
+    
+    if(FRAME_MCOUNT(frame)==0){
+        textw=init_title(frame, i);
+        if(textw>0){
+            title=grbrush_make_label(frame->bar_brush, CF_STR_EMPTY, textw);
+            frame->titles[i].text=title;
+        }
+        return;
+    }
+    
+    FRAME_MX_FOR_ALL(sub, frame, tmp){
+        textw=init_title(frame, i);
+        if(textw>0){
+            if(frame->flags&FRAME_SHOW_NUMBERS){
+                char *s=NULL;
+                libtu_asprintf(&s, "[%d]", i+1);
+                if(s!=NULL){
+                    title=grbrush_make_label(frame->bar_brush, s, textw);
+                    free(s);
+                }else{
+                    title=NULL;
+                }
+            }else{
+                title=region_make_label(sub, textw, frame->bar_brush);
+            }
+            frame->titles[i].text=title;
+        }
+        i++;
+    }
+}
+
+
+void frame_draw_bar(const WFrame *frame, bool complete)
+{
+    WRectangle geom;
+    const char *cattr=(REGION_IS_ACTIVE(frame) 
+                       ? "active" : "inactive");
+    
+    if(frame->bar_brush==NULL
+       || !BAR_EXISTS(frame)
+       || frame->titles==NULL){
+        return;
+    }
+    
+    frame_bar_geom(frame, &geom);
+
+    grbrush_begin(frame->bar_brush, &geom, GRBRUSH_AMEND);
+    
+    grbrush_draw_textboxes(frame->bar_brush, &geom, frame->titles_n, 
+                           frame->titles, complete, cattr);
+    
+    grbrush_end(frame->bar_brush);
+}
+
+
+void frame_draw(const WFrame *frame, bool complete)
+{
+    WRectangle geom;
+    const char *attr=(REGION_IS_ACTIVE(frame) 
+                      ? "active" : "inactive");
+    
+    if(frame->brush==NULL)
+        return;
+    
+    frame_border_geom(frame, &geom);
+    
+    grbrush_begin(frame->brush, &geom, (complete ? 0 : GRBRUSH_NO_CLEAR_OK));
+    
+    grbrush_draw_border(frame->brush, &geom, attr);
+    frame_draw_bar(frame, TRUE);
+    
+    grbrush_end(frame->brush);
+}
+
+
+void frame_brushes_updated(WFrame *frame)
+{
+    WFrameBarMode barmode=FRAME_BAR_INSIDE;
+    ExtlTab tab;
+    char *s;
+
+    if(frame->brush==NULL)
+        return;
+    
+    if(frame->mode==FRAME_MODE_FLOATING)
+        barmode=FRAME_BAR_SHAPED;
+    else if(frame->mode==FRAME_MODE_TRANSIENT)
+        barmode=FRAME_BAR_NONE;
+    else if(frame->mode==FRAME_MODE_TILED_ALT)
+        barmode=FRAME_BAR_NONE;
+    
+    if(grbrush_get_extra(frame->brush, "bar", 's', &s)){
+        if(strcmp(s, "inside")==0)
+            barmode=FRAME_BAR_INSIDE;
+        else if(strcmp(s, "outside")==0)
+            barmode=FRAME_BAR_OUTSIDE;
+        else if(strcmp(s, "shaped")==0)
+            barmode=FRAME_BAR_SHAPED;
+        else if(strcmp(s, "none")==0)
+            barmode=FRAME_BAR_NONE;
+       free(s);
+    }
+        
+    frame->barmode=barmode;
+    
+    if(barmode==FRAME_BAR_NONE || frame->bar_brush==NULL){
+        frame->bar_h=0;
+    }else{
+        GrBorderWidths bdw;
+        GrFontExtents fnte;
+
+        grbrush_get_border_widths(frame->bar_brush, &bdw);
+        grbrush_get_font_extents(frame->bar_brush, &fnte);
+
+        frame->bar_h=bdw.top+bdw.bottom+fnte.max_height;
+    }
+    
+    /* shaped mode stuff */
+    frame->tab_min_w=100;
+    frame->bar_max_width_q=0.95;
+    
+    if(grbrush_get_extra(frame->brush, "floatframe_tab_min_w",
+                         'i', &(frame->tab_min_w))){
+        if(frame->tab_min_w<=0)
+            frame->tab_min_w=1;
+    }
+    
+    if(grbrush_get_extra(frame->brush, "floatframe_bar_max_w_q", 
+                         'd', &(frame->bar_max_width_q))){
+        if(frame->bar_max_width_q<=0.0 || frame->bar_max_width_q>1.0)
+            frame->bar_max_width_q=1.0;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+void frame_updategr(WFrame *frame)
+{
+    frame_release_brushes(frame);
+    
+    frame_initialise_gr(frame);
+    
+    /* Update children */
+    region_updategr_default((WRegion*)frame);
+    
+    mplex_fit_managed(&frame->mplex);
+    frame_recalc_bar(frame);
+    frame_set_background(frame, TRUE);
+}
+
+
+StringIntMap frame_tab_styles[]={
+    {"tab-frame-tiled", FRAME_MODE_TILED},
+    {"tab-frame-tiled-alt", FRAME_MODE_TILED_ALT},
+    {"tab-frame-floating", FRAME_MODE_FLOATING},
+    {"tab-frame-transient", FRAME_MODE_TRANSIENT},
+    END_STRINGINTMAP
+};
+
+
+const char *framemode_get_tab_style(WFrameMode mode)
+{
+    return stringintmap_key(frame_tab_styles, mode, "tab-frame");
+}
+
+
+const char *framemode_get_style(WFrameMode mode)
+{
+    const char *p=framemode_get_tab_style(mode);
+    assert(p!=NULL);
+    return (p+4);
+}
+
+
+void frame_initialise_gr(WFrame *frame)
+{
+    Window win=frame->mplex.win.win;
+    WRootWin *rw=region_rootwin_of((WRegion*)frame);
+    const char *style=framemode_get_style(frame->mode);
+    const char *tab_style=framemode_get_tab_style(frame->mode);
+    
+    frame->brush=gr_get_brush(win, rw, style);
+    
+    if(frame->brush==NULL)
+        return;
+    
+    frame->bar_brush=grbrush_get_slave(frame->brush, rw, tab_style);
+    
+    if(frame->bar_brush==NULL)
+        return;
+    
+    frame_brushes_updated(frame);
+}
+
+
+void frame_release_brushes(WFrame *frame)
+{
+    if(frame->bar_brush!=NULL){
+        grbrush_release(frame->bar_brush);
+        frame->bar_brush=NULL;
+    }
+    
+    if(frame->brush!=NULL){
+        grbrush_release(frame->brush);
+        frame->brush=NULL;
+    }
+}
+
+
+bool frame_set_background(WFrame *frame, bool set_always)
+{
+    GrTransparency mode=GR_TRANSPARENCY_DEFAULT;
+    
+    if(FRAME_CURRENT(frame)!=NULL){
+        if(OBJ_IS(FRAME_CURRENT(frame), WClientWin)){
+            WClientWin *cwin=(WClientWin*)FRAME_CURRENT(frame);
+            mode=(cwin->flags&CLIENTWIN_PROP_TRANSPARENT
+                  ? GR_TRANSPARENCY_YES : GR_TRANSPARENCY_NO);
+        }else if(!OBJ_IS(FRAME_CURRENT(frame), WGroup)){
+            mode=GR_TRANSPARENCY_NO;
+        }
+    }
+    
+    if(mode!=frame->tr_mode || set_always){
+        frame->tr_mode=mode;
+        if(frame->brush!=NULL){
+            grbrush_enable_transparency(frame->brush, mode);
+            window_draw((WWindow*)frame, TRUE);
+        }
+        return TRUE;
+    }
+    
+    return FALSE;
+}
+
+
+/*}}}*/
diff --git a/ioncore/frame-draw.h b/ioncore/frame-draw.h
new file mode 100644 (file)
index 0000000..4086dec
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * ion/ioncore/frame-draw.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_FRAME_DRAW_H
+#define ION_IONCORE_FRAME_DRAW_H
+
+#include "frame.h"
+#include "rectangle.h"
+
+extern void frame_draw(const WFrame *frame, bool complete);
+extern void frame_draw_bar(const WFrame *frame, bool complete);
+extern void frame_recalc_bar(WFrame *frame);
+extern void frame_bar_geom(const WFrame *frame, WRectangle *geom);
+extern void frame_border_geom(const WFrame *frame, WRectangle *geom);
+extern void frame_border_inner_geom(const WFrame *frame, WRectangle *geom);
+extern void frame_brushes_updated(WFrame *frame);
+extern void frame_managed_geom(const WFrame *frame, WRectangle *geom);
+
+extern void frame_initialise_gr(WFrame *frame);
+extern void frame_release_brushes(WFrame *frame);
+extern bool frame_set_background(WFrame *frame, bool set_always);
+extern void frame_updategr(WFrame *frame);
+
+extern void frame_set_shape(WFrame *frame);
+extern void frame_clear_shape(WFrame *frame);
+
+extern const char *framemode_get_style(WFrameMode mode);
+extern const char *framemode_get_tab_style(WFrameMode mode);
+
+#endif /* ION_IONCORE_FRAME_DRAW_H */
diff --git a/ioncore/frame-pointer.c b/ioncore/frame-pointer.c
new file mode 100644 (file)
index 0000000..1d9a196
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * ion/ioncore/frame-pointer.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+
+#include "common.h"
+#include "global.h"
+#include "pointer.h"
+#include "cursor.h"
+#include "focus.h"
+#include "attach.h"
+#include "resize.h"
+#include "grab.h"
+#include "frame.h"
+#include "framep.h"
+#include "frame-pointer.h"
+#include "frame-draw.h"
+#include "bindmaps.h"
+#include "infowin.h"
+#include "rectangle.h"
+#include "xwindow.h"
+#include "names.h"
+#include "presize.h"
+#include "llist.h"
+
+
+static int p_tab_x=0, p_tab_y=0, p_tabnum=-1;
+static WInfoWin *tabdrag_infowin=NULL;
+
+
+/*{{{ Frame press */
+
+
+static WRegion *sub_at_tab(WFrame *frame)
+{
+    return mplex_mx_nth((WMPlex*)frame, p_tabnum);
+}
+
+
+int frame_press(WFrame *frame, XButtonEvent *ev, WRegion **reg_ret)
+{
+    WRegion *sub=NULL;
+    WRectangle g;
+    
+    p_tabnum=-1;
+
+    window_p_resize_prepare((WWindow*)frame, ev);
+    
+    /* Check tab */
+    
+    frame_bar_geom(frame, &g);
+    
+    /* Borders act like tabs at top of the parent region */
+    if(REGION_GEOM(frame).y==0){
+        g.h+=g.y;
+        g.y=0;
+    }
+
+    if(rectangle_contains(&g, ev->x, ev->y)){
+        p_tabnum=frame_tab_at_x(frame, ev->x);
+
+        region_rootpos((WRegion*)frame, &p_tab_x, &p_tab_y);
+        p_tab_x+=frame_nth_tab_x(frame, p_tabnum);
+        p_tab_y+=g.y;
+        
+        sub=mplex_mx_nth(&(frame->mplex), p_tabnum);
+
+        if(reg_ret!=NULL)
+            *reg_ret=sub;
+        
+        return FRAME_AREA_TAB;
+    }else{
+        WLListIterTmp tmp;
+        FRAME_MX_FOR_ALL(sub, frame, tmp){
+            p_tabnum++;
+            if(sub==FRAME_CURRENT(frame))
+                break;
+        }
+        
+        if(sub!=NULL){
+            p_tab_x=ev->x_root-frame_nth_tab_w(frame, p_tabnum)/2;
+            p_tab_y=ev->y_root-frame->bar_h/2;
+        }else{
+            p_tabnum=-1;
+        }
+    }
+    
+
+    /* Check border */
+    
+    frame_border_inner_geom(frame, &g);
+    
+    if(rectangle_contains(&g, ev->x, ev->y))
+        return FRAME_AREA_CLIENT;
+    
+    return FRAME_AREA_BORDER;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Tab drag */
+
+
+static ExtlExportedFn *tabdrag_safe_fns[]={
+    (ExtlExportedFn*)&mplex_switch_nth,
+    (ExtlExportedFn*)&mplex_switch_next,
+    (ExtlExportedFn*)&mplex_switch_prev,
+    NULL
+};
+
+static ExtlSafelist tabdrag_safelist=EXTL_SAFELIST_INIT(tabdrag_safe_fns);
+
+
+#define BUTTONS_MASK \
+    (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
+
+
+static bool tabdrag_kbd_handler(WRegion *reg, XEvent *xev)
+{
+    XKeyEvent *ev=&xev->xkey;
+    WBinding *binding=NULL;
+    WBindmap **bindptr;
+    
+    if(ev->type==KeyRelease)
+        return FALSE;
+    
+    assert(reg!=NULL);
+
+    binding=bindmap_lookup_binding(ioncore_rootwin_bindmap, BINDING_KEYPRESS,
+                                   ev->state&~BUTTONS_MASK, ev->keycode);
+    
+    if(binding!=NULL && binding->func!=extl_fn_none()){
+        extl_protect(&tabdrag_safelist);
+        extl_call(binding->func, "o", NULL, region_screen_of(reg));
+        extl_unprotect(&tabdrag_safelist);
+    }
+    
+    return FALSE;
+}
+
+
+static void setup_dragwin(WFrame *frame, uint tab)
+{
+    WRectangle g;
+    WRootWin *rw;
+    WFitParams fp;
+    const char *tab_style=framemode_get_tab_style(frame->mode);
+    
+    assert(tabdrag_infowin==NULL);
+    
+    rw=region_rootwin_of((WRegion*)frame);
+    
+    fp.mode=REGION_FIT_EXACT;
+    fp.g.x=p_tab_x;
+    fp.g.y=p_tab_y;
+    fp.g.w=frame_nth_tab_w(frame, tab);
+    fp.g.h=frame->bar_h;
+    
+    tabdrag_infowin=create_infowin((WWindow*)rw, &fp, tab_style);
+    
+    if(tabdrag_infowin==NULL)
+        return;
+    
+    infowin_set_attr2(tabdrag_infowin, (REGION_IS_ACTIVE(frame) 
+                                        ? "active" : "inactive"),
+                      frame->titles[tab].attr);
+    
+    if(frame->titles[tab].text!=NULL){
+        char *buf=INFOWIN_BUFFER(tabdrag_infowin);
+        strncpy(buf, frame->titles[tab].text, INFOWIN_BUFFER_LEN-1);
+        buf[INFOWIN_BUFFER_LEN-1]='\0';
+    }
+}
+
+
+static void p_tabdrag_motion(WFrame *frame, XMotionEvent *ev,
+                             int dx, int dy)
+{
+    WRootWin *rootwin=region_rootwin_of((WRegion*)frame);
+
+    p_tab_x+=dx;
+    p_tab_y+=dy;
+    
+    if(tabdrag_infowin!=NULL){
+        WRectangle g;
+        g.x=p_tab_x;
+        g.y=p_tab_y;
+        g.w=REGION_GEOM(tabdrag_infowin).w;
+        g.h=REGION_GEOM(tabdrag_infowin).h;
+        region_fit((WRegion*)tabdrag_infowin, &g, REGION_FIT_EXACT);
+    }
+}
+
+
+static void p_tabdrag_begin(WFrame *frame, XMotionEvent *ev,
+                            int dx, int dy)
+{
+    WRootWin *rootwin=region_rootwin_of((WRegion*)frame);
+
+    if(p_tabnum<0)
+        return;
+    
+    ioncore_change_grab_cursor(IONCORE_CURSOR_DRAG);
+        
+    setup_dragwin(frame, p_tabnum);
+    
+    frame->tab_dragged_idx=p_tabnum;
+    frame_update_attr_nth(frame, p_tabnum);
+
+    frame_draw_bar(frame, FALSE);
+    
+    p_tabdrag_motion(frame, ev, dx, dy);
+    
+    if(tabdrag_infowin!=NULL)
+        window_map((WWindow*)tabdrag_infowin);
+}
+
+
+static WRegion *fnd(Window root, int x, int y)
+{
+    Window win=root;
+    int dstx, dsty;
+    WRegion *reg=NULL;
+    WWindow *w=NULL;
+    WScreen *scr;
+    
+    FOR_ALL_SCREENS(scr){
+        if(region_root_of((WRegion*)scr)==root &&
+           rectangle_contains(&REGION_GEOM(scr), x, y)){
+            break;
+        }
+    }
+    
+    w=(WWindow*)scr;
+    
+    while(w!=NULL){
+        if(HAS_DYN(w, region_handle_drop))
+            reg=(WRegion*)w;
+        
+        if(!XTranslateCoordinates(ioncore_g.dpy, root, w->win,
+                                  x, y, &dstx, &dsty, &win)){
+            break;
+        }
+        
+        w=XWINDOW_REGION_OF_T(win, WWindow);
+        /*x=dstx;
+        y=dsty;*/
+    }
+
+    return reg;
+}
+
+
+static bool drop_ok(WRegion *mgr, WRegion *reg)
+{
+    WRegion *reg2=mgr;
+    for(reg2=mgr; reg2!=NULL; reg2=region_manager(reg2)){
+        if(reg2==reg)
+            goto err;
+    }
+    
+    for(reg2=REGION_PARENT_REG(mgr); reg2!=NULL; reg2=REGION_PARENT_REG(reg2)){
+        if(reg2==reg)
+            goto err;
+    }
+    
+    return TRUE;
+    
+err:
+    warn(TR("Attempt to make region %s manage its ancestor %s."),
+         region_name(mgr), region_name(reg));
+    return FALSE;
+}
+
+
+static void tabdrag_deinit(WFrame *frame)
+{
+    int idx=frame->tab_dragged_idx;
+    frame->tab_dragged_idx=-1;
+    frame_update_attr_nth(frame, idx);
+    
+    if(tabdrag_infowin!=NULL){
+        destroy_obj((Obj*)tabdrag_infowin);
+        tabdrag_infowin=NULL;
+    }
+}
+
+
+static void tabdrag_killed(WFrame *frame)
+{
+    tabdrag_deinit(frame);
+    if(!OBJ_IS_BEING_DESTROYED(frame))
+        frame_draw_bar(frame, TRUE);
+}
+
+
+static void p_tabdrag_end(WFrame *frame, XButtonEvent *ev)
+{
+    WRegion *sub=NULL;
+    WRegion *dropped_on;
+    Window win=None;
+
+    sub=sub_at_tab(frame);
+    
+    tabdrag_deinit(frame);
+    
+    /* Must be same root window */
+    if(sub==NULL || ev->root!=region_root_of(sub))
+        return;
+    
+    dropped_on=fnd(ev->root, ev->x_root, ev->y_root);
+
+    if(dropped_on==NULL || dropped_on==(WRegion*)frame || 
+       dropped_on==sub || !drop_ok(dropped_on, sub)){
+        frame_draw_bar(frame, TRUE);
+        return;
+    }
+    
+    if(region_handle_drop(dropped_on, p_tab_x, p_tab_y, sub))
+        region_goto(dropped_on);
+    else
+        frame_draw_bar(frame, TRUE);
+}
+
+
+/*EXTL_DOC
+ * Start dragging the tab that the user pressed on with the pointing device.
+ * This function should only be used by binding it to \emph{mpress} or
+ * \emph{mdrag} action with area ''tab''.
+ */
+EXTL_EXPORT_MEMBER
+void frame_p_tabdrag(WFrame *frame)
+{
+    if(p_tabnum<0)
+        return;
+    
+    ioncore_set_drag_handlers((WRegion*)frame,
+                        (WMotionHandler*)p_tabdrag_begin,
+                        (WMotionHandler*)p_tabdrag_motion,
+                        (WButtonHandler*)p_tabdrag_end,
+                        tabdrag_kbd_handler,
+                        (GrabKilledHandler*)tabdrag_killed);
+}
+
+
+/*}}}*/
+
+
+/*{{{ switch_tab */
+
+
+/*EXTL_DOC
+ * Display the region corresponding to the tab that the user pressed on.
+ * This function should only be used by binding it to a mouse action.
+ */
+EXTL_EXPORT_MEMBER
+void frame_p_switch_tab(WFrame *frame)
+{
+    WRegion *sub;
+    
+    if(ioncore_pointer_grab_region()!=(WRegion*)frame)
+        return;
+    
+    sub=sub_at_tab(frame);
+    
+    if(sub!=NULL){
+        bool mcf=region_may_control_focus((WRegion*)frame);
+        region_goto_flags(sub, (mcf 
+                                ? REGION_GOTO_FOCUS|REGION_GOTO_NOWARP 
+                                : 0));
+    }
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/frame-pointer.h b/ioncore/frame-pointer.h
new file mode 100644 (file)
index 0000000..092b90a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * ion/ioncore/frame-pointer.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_FRAME_POINTER_H
+#define ION_IONCORE_FRAME_POINTER_H
+
+#include "common.h"
+#include "pointer.h"
+#include "frame.h"
+
+extern void frame_p_resize(WFrame *frame);
+extern void frame_p_tabdrag(WFrame *frame);
+extern void frame_p_move(WFrame *frame);
+extern void frame_p_switch_tab(WFrame *frame);
+
+extern int frame_press(WFrame *frame, XButtonEvent *ev,
+                          WRegion **reg_ret);
+extern void frame_release(WFrame *frame);
+
+#endif /* ION_IONCORE_FRAME_POINTER_H */
diff --git a/ioncore/frame.c b/ioncore/frame.c
new file mode 100644 (file)
index 0000000..0f3e8f2
--- /dev/null
@@ -0,0 +1,927 @@
+/*
+ * ion/ioncore/frame.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/obj.h>
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <libtu/map.h>
+#include <libmainloop/defer.h>
+
+#include "common.h"
+#include "window.h"
+#include "global.h"
+#include "rootwin.h"
+#include "focus.h"
+#include "event.h"
+#include "attach.h"
+#include "resize.h"
+#include "tags.h"
+#include "names.h"
+#include "saveload.h"
+#include "framep.h"
+#include "frame-pointer.h"
+#include "frame-draw.h"
+#include "sizehint.h"
+#include "extlconv.h"
+#include "mplex.h"
+#include "bindmaps.h"
+#include "regbind.h"
+#include "gr.h"
+#include "activity.h"
+#include "llist.h"
+
+
+extern bool frame_set_background(WFrame *frame, bool set_always);
+extern void frame_initialise_gr(WFrame *frame);
+
+static bool frame_initialise_titles(WFrame *frame);
+static void frame_free_titles(WFrame *frame);
+
+static void frame_add_mode_bindmaps(WFrame *frame);
+
+
+WHook *frame_managed_changed_hook=NULL;
+
+#define IS_FLOATING_MODE(FRAME) \
+    ((FRAME)->mode==FRAME_MODE_FLOATING || (FRAME)->mode==FRAME_MODE_TRANSIENT)
+#define FORWARD_CWIN_RQGEOM(FRAME) IS_FLOATING_MODE(FRAME)
+#define USE_MINMAX(FRAME) IS_FLOATING_MODE(FRAME)
+#define DEST_EMPTY(FRAME) IS_FLOATING_MODE(FRAME)
+
+
+/*{{{ Destroy/create frame */
+
+
+bool frame_init(WFrame *frame, WWindow *parent, const WFitParams *fp,
+                WFrameMode mode)
+{
+    WRectangle mg;
+
+    frame->flags=0;
+    frame->saved_w=0;
+    frame->saved_h=0;
+    frame->saved_x=0;
+    frame->saved_y=0;
+    frame->tab_dragged_idx=-1;
+    frame->titles=NULL;
+    frame->titles_n=0;
+    frame->bar_h=0;
+    frame->bar_w=fp->g.w;
+    frame->tr_mode=GR_TRANSPARENCY_DEFAULT;
+    frame->brush=NULL;
+    frame->bar_brush=NULL;
+    frame->mode=mode;
+    frame->tab_min_w=0;
+    frame->bar_max_width_q=1.0;
+    
+    if(!mplex_init((WMPlex*)frame, parent, fp))
+        return FALSE;
+    
+    frame_initialise_gr(frame);
+    frame_initialise_titles(frame);
+    
+    region_add_bindmap((WRegion*)frame, ioncore_frame_bindmap);
+    region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
+    
+    frame_add_mode_bindmaps(frame);
+    
+    mplex_managed_geom((WMPlex*)frame, &mg);
+    
+    if(mg.h<=1)
+        frame->flags|=FRAME_SHADED;
+    
+    ((WRegion*)frame)->flags|=REGION_PLEASE_WARP;
+    
+    return TRUE;
+}
+
+
+WFrame *create_frame(WWindow *parent, const WFitParams *fp, WFrameMode mode)
+{
+    CREATEOBJ_IMPL(WFrame, frame, (p, parent, fp, mode));
+}
+
+
+void frame_deinit(WFrame *frame)
+{
+    frame_free_titles(frame);
+    frame_release_brushes(frame);
+    mplex_deinit((WMPlex*)frame);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Mode switching */
+
+
+static void frame_add_mode_bindmaps(WFrame *frame)
+{
+    WFrameMode mode=frame->mode;
+    
+    if(mode==FRAME_MODE_TILED || mode==FRAME_MODE_TILED_ALT){
+       region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
+        region_add_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap);
+    }else if(mode==FRAME_MODE_FLOATING){
+       region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
+        region_add_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
+    }else if(mode==FRAME_MODE_TRANSIENT){
+        region_add_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap);
+    }
+}
+
+
+void frame_set_mode(WFrame *frame, WFrameMode mode)
+{
+    if(frame->mode==mode)
+        return;
+    
+    frame_clear_shape(frame);
+    
+    frame_release_brushes(frame);
+    
+    region_remove_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
+    region_remove_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap);
+    region_remove_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
+    region_remove_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap);
+    
+    frame->mode=mode;
+
+    frame_add_mode_bindmaps(frame);
+    
+    frame_initialise_gr(frame);
+    
+    mplex_fit_managed(&frame->mplex);
+    frame_recalc_bar(frame);
+    frame_set_background(frame, TRUE);
+}
+
+
+WFrameMode frame_mode(WFrame *frame)
+{
+    return frame->mode;
+}
+
+
+StringIntMap frame_modes[]={
+    {"tiled", FRAME_MODE_TILED},
+    {"tiled-alt", FRAME_MODE_TILED_ALT},
+    {"floating", FRAME_MODE_FLOATING},
+    {"transient", FRAME_MODE_TRANSIENT},
+    END_STRINGINTMAP
+};
+
+
+/*EXTL_DOC
+ * Get frame mode.
+ */
+EXTL_EXPORT_AS(WFrame, mode)
+const char *frame_mode_extl(WFrame *frame)
+{
+    return stringintmap_key(frame_modes, frame->mode, NULL);
+}
+
+
+/*EXTL_DOC
+ * Set frame mode.
+ */
+EXTL_EXPORT_AS(WFrame, set_mode)
+bool frame_set_mode_extl(WFrame *frame, const char *modestr)
+{
+    WFrameMode mode;
+    int idx;
+    
+    idx=stringintmap_ndx(frame_modes, modestr);
+    if(idx<0)
+       return FALSE;
+    
+    frame_set_mode(frame, frame_modes[idx].value);
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Tabs */
+
+
+int frame_tab_at_x(WFrame *frame, int x)
+{
+    WRectangle bg;
+    int tab, tx;
+    
+    frame_bar_geom(frame, &bg);
+    
+    if(x>=bg.x+bg.w || x<bg.x)
+        return -1;
+    
+    tx=bg.x;
+
+    for(tab=0; tab<FRAME_MCOUNT(frame); tab++){
+        tx+=frame_nth_tab_w(frame, tab);
+        if(x<tx)
+            break;
+    }
+    
+    return tab;
+}
+
+
+int frame_nth_tab_x(WFrame *frame, int n)
+{
+    uint x=0;
+    int i;
+    
+    for(i=0; i<n; i++)
+        x+=frame_nth_tab_w(frame, i);
+    
+    return x;
+}
+
+
+static int frame_nth_tab_w_iw(WFrame *frame, int n, bool inner)
+{
+    WRectangle bg;
+    GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT;
+    int m=FRAME_MCOUNT(frame);
+    uint w;
+    
+    frame_bar_geom(frame, &bg);
+
+    if(m==0)
+        m=1;
+
+    if(frame->bar_brush!=NULL)
+        grbrush_get_border_widths(frame->bar_brush, &bdw);
+    
+    /* Remove borders */
+    w=bg.w-bdw.left-bdw.right-(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)*(m-1);
+    
+    if(w<=0)
+        return 0;
+    
+    /* Get n:th tab's portion of free area */
+    w=(((n+1)*w)/m-(n*w)/m);
+    
+    /* Add n:th tab's borders back */
+    if(!inner){
+        w+=(n==0 ? bdw.left : bdw.tb_ileft);
+        w+=(n==m-1 ? bdw.right : bdw.tb_iright+bdw.spacing);
+    }
+            
+    return w;
+}
+
+
+int frame_nth_tab_w(WFrame *frame, int n)
+{
+    return frame_nth_tab_w_iw(frame, n, FALSE);
+}
+
+
+int frame_nth_tab_iw(WFrame *frame, int n)
+{
+    return frame_nth_tab_w_iw(frame, n, TRUE);
+}
+
+
+
+static void update_attr(WFrame *frame, int i, WRegion *reg)
+{
+    int flags=0;
+    static char *attrs[]={
+        "unselected-not_tagged-not_dragged-no_activity",
+        "selected-not_tagged-not_dragged-no_activity",
+        "unselected-tagged-not_dragged-no_activity",
+        "selected-tagged-not_dragged-no_activity",
+        "unselected-not_tagged-dragged-no_activity",
+        "selected-not_tagged-dragged-no_activity",
+        "unselected-tagged-dragged-no_activity",
+        "selected-tagged-dragged-no_activity",
+        "unselected-not_tagged-not_dragged-activity",
+        "selected-not_tagged-not_dragged-activity",
+        "unselected-tagged-not_dragged-activity",
+        "selected-tagged-not_dragged-activity",
+        "unselected-not_tagged-dragged-activity",
+        "selected-not_tagged-dragged-activity",
+        "unselected-tagged-dragged-activity",
+        "selected-tagged-dragged-activity"
+    };
+
+    if(i>=frame->titles_n){
+        /* Might happen when deinitialising */
+        return;
+    }
+    
+    if(reg==FRAME_CURRENT(frame))
+        flags|=0x01;
+    if(reg!=NULL && reg->flags&REGION_TAGGED)
+        flags|=0x02;
+    if(i==frame->tab_dragged_idx)
+        flags|=0x04;
+    if(reg!=NULL && region_is_activity_r(reg))
+        flags|=0x08;
+    
+    frame->titles[i].attr=attrs[flags];
+}
+
+
+void frame_update_attr_nth(WFrame *frame, int i)
+{
+    WRegion *reg;
+    
+    if(i<0 || i>=frame->titles_n)
+        return;
+
+    update_attr(frame, i, mplex_mx_nth((WMPlex*)frame, i));
+}
+
+
+static void update_attrs(WFrame *frame)
+{
+    int i=0;
+    WRegion *sub;
+    WLListIterTmp tmp;
+    
+    FRAME_MX_FOR_ALL(sub, frame, tmp){
+        update_attr(frame, i, sub);
+        i++;
+    }
+}
+
+
+static void frame_free_titles(WFrame *frame)
+{
+    int i;
+    
+    if(frame->titles!=NULL){
+        for(i=0; i<frame->titles_n; i++){
+            if(frame->titles[i].text)
+                free(frame->titles[i].text);
+        }
+        free(frame->titles);
+        frame->titles=NULL;
+    }
+    frame->titles_n=0;
+}
+
+
+static void do_init_title(WFrame *frame, int i, WRegion *sub)
+{
+    frame->titles[i].text=NULL;
+    frame->titles[i].iw=frame_nth_tab_iw(frame, i);
+    update_attr(frame, i, sub);
+}
+
+
+static bool frame_initialise_titles(WFrame *frame)
+{
+    int i, n=FRAME_MCOUNT(frame);
+    
+    frame_free_titles(frame);
+
+    if(n==0)
+        n=1;
+    
+    frame->titles=ALLOC_N(GrTextElem, n);
+    if(frame->titles==NULL)
+        return FALSE;
+    frame->titles_n=n;
+    
+    if(FRAME_MCOUNT(frame)==0){
+        do_init_title(frame, 0, NULL);
+    }else{
+        WLListIterTmp tmp;
+        WRegion *sub;
+        i=0;
+        FRAME_MX_FOR_ALL(sub, frame, tmp){
+            do_init_title(frame, i, sub);
+            i++;
+        }
+    }
+    
+    frame_recalc_bar(frame);
+
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Resize and reparent */
+
+
+bool frame_fitrep(WFrame *frame, WWindow *par, const WFitParams *fp)
+{
+    WRectangle old_geom, mg;
+    bool wchg=(REGION_GEOM(frame).w!=fp->g.w);
+    bool hchg=(REGION_GEOM(frame).h!=fp->g.h);
+    
+    old_geom=REGION_GEOM(frame);
+    
+    window_do_fitrep(&(frame->mplex.win), par, &(fp->g));
+
+    mplex_managed_geom((WMPlex*)frame, &mg);
+    
+    if(hchg){
+        if(mg.h<=1){
+            frame->flags|=(FRAME_SHADED|FRAME_SAVED_VERT);
+            frame->saved_y=old_geom.y;
+            frame->saved_h=old_geom.h;
+        }else{
+            frame->flags&=~FRAME_SHADED;
+        }
+        frame->flags&=~FRAME_MAXED_VERT;
+    }
+    
+    if(wchg){
+        if(mg.w<=1){
+            frame->flags|=(FRAME_MIN_HORIZ|FRAME_SAVED_HORIZ);
+            frame->saved_x=old_geom.x;
+            frame->saved_w=old_geom.w;
+        }else{
+            frame->flags&=~FRAME_MIN_HORIZ;
+        }
+        frame->flags&=~FRAME_MAXED_HORIZ;
+    }
+
+    if(wchg || hchg){
+        mplex_fit_managed((WMPlex*)frame);
+        mplex_size_changed((WMPlex*)frame, wchg, hchg);
+    }
+    
+    return TRUE;
+}
+
+
+void frame_size_hints(WFrame *frame, WSizeHints *hints_ret)
+{
+    WRectangle subgeom;
+    WLListIterTmp tmp;
+    WRegion *sub;
+    int woff, hoff;
+    
+    mplex_managed_geom((WMPlex*)frame, &subgeom);
+    
+    woff=maxof(REGION_GEOM(frame).w-subgeom.w, 0);
+    hoff=maxof(REGION_GEOM(frame).h-subgeom.h, 0);
+
+    if(FRAME_CURRENT(frame)!=NULL){
+        region_size_hints(FRAME_CURRENT(frame), hints_ret);
+        if(!USE_MINMAX(frame)){
+            hints_ret->max_set=0;
+            hints_ret->min_set=0;
+            hints_ret->base_set=0;
+            hints_ret->aspect_set=0;
+            hints_ret->no_constrain=FALSE;
+            /*hints_ret->no_constrain=TRUE;*/
+        }
+    }else{
+        sizehints_clear(hints_ret);
+    }
+    
+    FRAME_MX_FOR_ALL(sub, frame, tmp){
+        sizehints_adjust_for(hints_ret, sub);
+    }
+    
+    if(!hints_ret->base_set){
+        hints_ret->base_width=0;
+        hints_ret->base_height=0;
+        hints_ret->base_set=TRUE;
+    }
+
+    if(!hints_ret->min_set){
+        hints_ret->min_width=0;
+        hints_ret->min_height=0;
+        hints_ret->min_set=TRUE;
+    }
+    
+    hints_ret->base_width+=woff;
+    hints_ret->base_height+=hoff;
+    hints_ret->max_width+=woff;
+    hints_ret->max_height+=hoff;
+    hints_ret->min_width+=woff;
+    hints_ret->min_height+=hoff;
+    
+    if(frame->barmode==FRAME_BAR_SHAPED){
+        int f=frame->flags&(FRAME_SHADED|FRAME_SHADED_TOGGLE);
+        
+        if(f==FRAME_SHADED || f==FRAME_SHADED_TOGGLE){
+            hints_ret->min_height=frame->bar_h;
+            hints_ret->max_height=frame->bar_h;
+            hints_ret->base_height=frame->bar_h;
+            if(!hints_ret->max_set){
+                hints_ret->max_width=INT_MAX;
+                hints_ret->max_set=TRUE;
+            }
+        }
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Focus  */
+
+
+void frame_inactivated(WFrame *frame)
+{
+    window_draw((WWindow*)frame, FALSE);
+}
+
+
+void frame_activated(WFrame *frame)
+{
+    window_draw((WWindow*)frame, FALSE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Client window rqgeom */
+
+
+static void frame_managed_rqgeom_absolute(WFrame *frame, WRegion *sub,
+                                          const WRQGeomParams *rq,
+                                          WRectangle *geomret)
+{
+    if(!FORWARD_CWIN_RQGEOM(frame)){
+        region_managed_rqgeom_absolute_default((WRegion*)frame, sub, 
+                                               rq, geomret);
+    }else{
+        WRQGeomParams rq2=RQGEOMPARAMS_INIT;
+        int gravity=ForgetGravity;
+        WRectangle off;
+        WRegion *par;
+        
+        rq2.geom=rq->geom;
+        rq2.flags=rq->flags&(REGION_RQGEOM_WEAK_ALL
+                             |REGION_RQGEOM_TRYONLY
+                             |REGION_RQGEOM_ABSOLUTE);
+
+        if(rq->flags&REGION_RQGEOM_GRAVITY)
+            gravity=rq->gravity;
+    
+        mplex_managed_geom(&frame->mplex, &off);
+        off.x=-off.x;
+        off.y=-off.y;
+        off.w=REGION_GEOM(frame).w-off.w;
+        off.h=REGION_GEOM(frame).h-off.h;
+
+        rq2.geom.w=maxof(rq2.geom.w+off.w, 0);
+        rq2.geom.h=maxof(rq2.geom.h+off.h, 0);
+    
+        /*region_size_hints_correct((WRegion*)frame, &(geom.w), &(geom.h), TRUE);*/
+    
+        /* If WEAK_? is set, then geom.(x|y) is root-relative as it was not 
+         * requested by the client and clientwin_handle_configure_request has
+         * no better guess. Otherwise the coordinates are those requested by 
+         * the client (modulo borders/gravity) and we interpret them to be 
+         * root-relative coordinates for this frame modulo gravity.
+         */
+        if(rq->flags&REGION_RQGEOM_WEAK_X)
+            rq2.geom.x+=off.x;
+        else
+            rq2.geom.x+=xgravity_deltax(gravity, -off.x, off.x+off.w);
+       
+        if(rq->flags&REGION_RQGEOM_WEAK_Y)
+            rq2.geom.y+=off.y;
+        else
+            rq2.geom.y+=xgravity_deltay(gravity, -off.y, off.y+off.h);
+        
+        region_rqgeom((WRegion*)frame, &rq2, geomret);
+        
+        if(geomret!=NULL){
+            geomret->x-=off.x;
+            geomret->y-=off.y;
+            geomret->w-=off.w;
+            geomret->h-=off.h;
+        }
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+bool frame_set_shaded(WFrame *frame, int sp)
+{
+    bool set=(frame->flags&FRAME_SHADED);
+    bool nset=libtu_do_setparam(sp, set);
+    WRQGeomParams rq=RQGEOMPARAMS_INIT;
+    GrBorderWidths bdw;
+    int h;
+
+    if(!XOR(nset, set))
+        return nset;
+
+    rq.flags=REGION_RQGEOM_H_ONLY;
+    rq.geom=REGION_GEOM(frame);
+        
+    if(!nset){
+        if(!(frame->flags&FRAME_SAVED_VERT))
+            return FALSE;
+        rq.geom.h=frame->saved_h;
+    }else{
+        if(frame->barmode==FRAME_BAR_NONE){
+            return FALSE;
+        }else if(frame->barmode==FRAME_BAR_SHAPED){
+            rq.geom.h=frame->bar_h;
+        }else{
+            WRectangle tmp;
+            
+            frame_border_inner_geom(frame, &tmp);
+            
+            rq.geom.h=rq.geom.h-tmp.h;
+        }
+    }
+    
+    frame->flags|=FRAME_SHADED_TOGGLE;
+    
+    region_rqgeom((WRegion*)frame, &rq, NULL);
+    
+    frame->flags&=~FRAME_SHADED_TOGGLE;
+    
+    return (frame->flags&FRAME_SHADED);
+}
+
+
+/*EXTL_DOC
+ * Set shading state according to the parameter \var{how} 
+ * (set/unset/toggle). Resulting state is returned, which may not be
+ * what was requested.
+ */
+EXTL_EXPORT_AS(WFrame, set_shaded)
+bool frame_set_shaded_extl(WFrame *frame, const char *how)
+{
+    return frame_set_shaded(frame, libtu_string_to_setparam(how));
+}
+
+
+/*EXTL_DOC
+ * Is \var{frame} shaded?
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+bool frame_is_shaded(WFrame *frame)
+{
+    return ((frame->flags&FRAME_SHADED)!=0);
+}
+
+
+bool frame_set_numbers(WFrame *frame, int sp)
+{
+    bool set=frame->flags&FRAME_SHOW_NUMBERS;
+    bool nset=libtu_do_setparam(sp, set);
+    
+    if(XOR(nset, set)){
+        frame->flags^=FRAME_SHOW_NUMBERS;
+        frame_recalc_bar(frame);
+        frame_draw_bar(frame, TRUE);
+    }
+    
+    return frame->flags&FRAME_SHOW_NUMBERS;
+}
+
+
+/*EXTL_DOC
+ * Control whether tabs show numbers (set/unset/toggle). 
+ * Resulting state is returned, which may not be what was 
+ * requested.
+ */
+EXTL_EXPORT_AS(WFrame, set_numbers)
+bool frame_set_numbers_extl(WFrame *frame, const char *how)
+{
+    return frame_set_numbers(frame, libtu_string_to_setparam(how));
+}
+
+
+/*EXTL_DOC
+ * Does \var{frame} show numbers for tabs?
+ */
+bool frame_is_numbers(WFrame *frame)
+{
+    return frame->flags&FRAME_SHOW_NUMBERS;
+}
+
+
+void frame_managed_notify(WFrame *frame, WRegion *sub, const char *how)
+{
+    update_attrs(frame);
+    frame_recalc_bar(frame);
+    frame_draw_bar(frame, FALSE);
+}
+
+
+static void frame_size_changed_default(WFrame *frame,
+                                       bool wchg, bool hchg)
+{
+    int bar_w=frame->bar_w;
+    
+    if(wchg)
+        frame_recalc_bar(frame);
+    
+    if(frame->barmode==FRAME_BAR_SHAPED &&
+       ((!wchg && hchg) || (wchg && bar_w==frame->bar_w))){
+        frame_set_shape(frame);
+    }
+}
+
+
+static void frame_managed_changed(WFrame *frame, int mode, bool sw,
+                                  WRegion *reg)
+{
+    bool need_draw=TRUE;
+    
+    if(mode!=MPLEX_CHANGE_SWITCHONLY)
+        frame_initialise_titles(frame);
+    else
+        update_attrs(frame);
+
+    if(sw)
+        need_draw=!frame_set_background(frame, FALSE);
+    
+    if(need_draw)
+        frame_draw_bar(frame, mode!=MPLEX_CHANGE_SWITCHONLY);
+
+    mplex_call_changed_hook((WMPlex*)frame,
+                            frame_managed_changed_hook,
+                            mode, sw, reg);
+}
+
+
+#define EMPTY_AND_SHOULD_BE_DESTROYED(FRAME) \
+    (DEST_EMPTY(frame) && FRAME_MCOUNT(FRAME)==0 && \
+     !OBJ_IS_BEING_DESTROYED(frame))
+
+
+static void frame_destroy_empty(WFrame *frame)
+{
+    if(EMPTY_AND_SHOULD_BE_DESTROYED(frame))
+        destroy_obj((Obj*)frame);
+}
+
+
+void frame_managed_remove(WFrame *frame, WRegion *reg)
+{
+    mplex_managed_remove((WMPlex*)frame, reg);
+    if(EMPTY_AND_SHOULD_BE_DESTROYED(frame)){
+        mainloop_defer_action((Obj*)frame, 
+                              (WDeferredAction*)frame_destroy_empty);
+    }
+}
+
+
+int frame_default_index(WFrame *frame)
+{
+    return ioncore_g.frame_default_index;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Save/load */
+
+
+ExtlTab frame_get_configuration(WFrame *frame)
+{
+    ExtlTab tab=mplex_get_configuration(&frame->mplex);
+    
+    extl_table_sets_i(tab, "mode", frame->mode);
+
+    if(frame->flags&FRAME_SAVED_VERT){
+        extl_table_sets_i(tab, "saved_y", frame->saved_y);
+        extl_table_sets_i(tab, "saved_h", frame->saved_h);
+    }
+
+    if(frame->flags&FRAME_SAVED_HORIZ){
+        extl_table_sets_i(tab, "saved_x", frame->saved_x);
+        extl_table_sets_i(tab, "saved_w", frame->saved_w);
+    }
+    
+    return tab;
+}
+
+
+
+void frame_do_load(WFrame *frame, ExtlTab tab)
+{
+    int flags=0;
+    int p=0, s=0;
+    
+    if(extl_table_gets_i(tab, "saved_x", &p) &&
+       extl_table_gets_i(tab, "saved_w", &s)){
+        frame->saved_x=p;
+        frame->saved_w=s;
+        frame->flags|=FRAME_SAVED_HORIZ;
+    }
+
+    if(extl_table_gets_i(tab, "saved_y", &p) &&
+       extl_table_gets_i(tab, "saved_h", &s)){
+        frame->saved_y=p;
+        frame->saved_h=s;
+        frame->flags|=FRAME_SAVED_VERT;
+    }
+    
+    mplex_load_contents(&frame->mplex, tab);
+}
+
+
+WRegion *frame_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
+{
+    int mode=FRAME_MODE_UNKNOWN;
+    WFrame *frame;
+    
+    if(!extl_table_gets_i(tab, "mode", &mode)){
+        #warning "TODO: Remove backwards compatibility hack"
+        char *style=NULL;
+        if(extl_table_gets_s(tab, "frame_style", &style)){
+            if(strcmp(style, "frame-tiled")==0)
+                mode=FRAME_MODE_TILED;
+            else if(strcmp(style, "frame-floating")==0)
+                mode=FRAME_MODE_FLOATING;
+            else if(strcmp(style, "frame-transientcontainer")==0)
+                mode=FRAME_MODE_TRANSIENT;
+            free(style);
+        }
+    }
+    
+    frame=create_frame(par, fp, mode);
+    
+    if(frame!=NULL)
+        frame_do_load(frame, tab);
+    
+    return (WRegion*)frame;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuntab and class info */
+
+
+static DynFunTab frame_dynfuntab[]={
+    {region_size_hints, frame_size_hints},
+
+    {mplex_managed_changed, frame_managed_changed},
+    {mplex_size_changed, frame_size_changed_default},
+    {region_managed_notify, frame_managed_notify},
+    
+    {region_activated, frame_activated},
+    {region_inactivated, frame_inactivated},
+
+    {(DynFun*)window_press, (DynFun*)frame_press},
+    
+    {(DynFun*)region_get_configuration,
+     (DynFun*)frame_get_configuration},
+
+    {window_draw, 
+     frame_draw},
+    
+    {mplex_managed_geom, 
+     frame_managed_geom},
+
+    {region_updategr, 
+     frame_updategr},
+
+    {(DynFun*)region_fitrep,
+     (DynFun*)frame_fitrep},
+
+    {region_managed_rqgeom_absolute, 
+     frame_managed_rqgeom_absolute},
+
+    {region_managed_remove, frame_managed_remove},
+    
+    {(DynFun*)mplex_default_index,
+     (DynFun*)frame_default_index},
+    
+    END_DYNFUNTAB
+};
+                                       
+
+EXTL_EXPORT
+IMPLCLASS(WFrame, WMPlex, frame_deinit, frame_dynfuntab);
+
+
+/*}}}*/
diff --git a/ioncore/frame.h b/ioncore/frame.h
new file mode 100644 (file)
index 0000000..ce37814
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * ion/ioncore/frame.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_FRAME_H
+#define ION_IONCORE_FRAME_H
+
+#include <libtu/stringstore.h>
+#include <libtu/setparam.h>
+#include <libextl/extl.h>
+
+#include "common.h"
+#include "window.h"
+#include "attach.h"
+#include "mplex.h"
+#include "gr.h"
+#include "rectangle.h"
+#include "sizehint.h"
+
+#define FRAME_SAVED_VERT  0x0008
+#define FRAME_SAVED_HORIZ 0x0010
+#define FRAME_SHADED      0x0020
+#define FRAME_SHADED_TOGGLE 0x0040
+/*#define FRAME_DEST_EMPTY  0x0100*/
+#define FRAME_MAXED_VERT  0x0200
+#define FRAME_MAXED_HORIZ 0x0400
+#define FRAME_MIN_HORIZ   0x0800
+
+/*#define FRAME_SZH_USEMINMAX 0x1000 */
+/*#define FRAME_FWD_CWIN_RQGEOM 0x2000 */
+
+#define FRAME_SHOW_NUMBERS 0x4000
+
+typedef enum{
+    FRAME_MODE_UNKNOWN,
+    FRAME_MODE_TILED,
+    FRAME_MODE_TILED_ALT,
+    FRAME_MODE_FLOATING,
+    FRAME_MODE_TRANSIENT
+} WFrameMode;
+
+typedef enum{
+    FRAME_BAR_INSIDE,
+    FRAME_BAR_OUTSIDE,
+    FRAME_BAR_SHAPED,
+    FRAME_BAR_NONE
+} WFrameBarMode;
+        
+
+
+DECLCLASS(WFrame){
+    WMPlex mplex;
+    
+    int flags;
+    WFrameMode mode;
+    int saved_w, saved_h;
+    int saved_x, saved_y;
+    
+    int tab_dragged_idx;
+    
+    GrBrush *brush;
+    GrBrush *bar_brush;
+    GrTransparency tr_mode;
+    GrTextElem *titles;
+    int titles_n;
+    
+    /* Bar stuff */
+    WFrameBarMode barmode;
+    int bar_w, bar_h;
+    double bar_max_width_q;
+    int tab_min_w;
+};
+
+
+/* Create/destroy */
+extern WFrame *create_frame(WWindow *parent, const WFitParams *fp,
+                            WFrameMode mode);
+extern bool frame_init(WFrame *frame, WWindow *parent, const WFitParams *fp,
+                       WFrameMode mode);
+extern void frame_deinit(WFrame *frame);
+extern bool frame_rqclose(WFrame *frame);
+
+/* Mode */
+
+extern void frame_set_mode(WFrame *frame, WFrameMode mode);
+extern WFrameMode frame_mode(WFrame *frame);
+
+/* Resize and reparent */
+extern bool frame_fitrep(WFrame *frame, WWindow *par, const WFitParams *fp);
+extern void frame_size_hints(WFrame *frame, WSizeHints *hints_ret);
+
+/* Focus */
+extern void frame_activated(WFrame *frame);
+extern void frame_inactivated(WFrame *frame);
+
+/* Tabs */
+extern int frame_nth_tab_w(WFrame *frame, int n);
+extern int frame_nth_tab_iw(WFrame *frame, int n);
+extern int frame_nth_tab_x(WFrame *frame, int n);
+extern int frame_tab_at_x(WFrame *frame, int x);
+extern void frame_update_attr_nth(WFrame *frame, int i);
+
+extern bool frame_set_shaded(WFrame *frame, int sp);
+extern bool frame_is_shaded(WFrame *frame);
+extern bool frame_set_numbers(WFrame *frame, int sp);
+extern bool frame_is_numbers(WFrame *frame);
+
+extern int frame_default_index(WFrame *frame);
+
+/* Misc */
+extern void frame_managed_notify(WFrame *frame, WRegion *sub, const char *how);
+extern void frame_managed_remove(WFrame *frame, WRegion *reg);
+
+/* Save/load */
+extern ExtlTab frame_get_configuration(WFrame *frame);
+extern WRegion *frame_load(WWindow *par, const WFitParams *fp, ExtlTab tab);
+extern void frame_do_load(WFrame *frame, ExtlTab tab);
+
+extern WHook *frame_managed_changed_hook;
+
+#endif /* ION_IONCORE_FRAME_H */
diff --git a/ioncore/framedpholder.c b/ioncore/framedpholder.c
new file mode 100644 (file)
index 0000000..43375f2
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * ion/ioncore/framedpholder.c
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/objp.h>
+#include <libtu/obj.h>
+#include <libtu/minmax.h>
+
+#include "frame.h"
+#include "framedpholder.h"
+#include "sizehint.h"
+
+
+/*{{{ Init/deinit */
+
+
+bool framedpholder_init(WFramedPHolder *ph, WPHolder *cont,
+                        const WFramedParam *param)
+{
+    assert(cont!=NULL);
+    
+    pholder_init(&(ph->ph));
+
+    ph->cont=cont;
+    ph->param=*param;
+    
+    return TRUE;
+}
+
+WFramedPHolder *create_framedpholder(WPHolder *cont,
+                                     const WFramedParam *param)
+{
+    CREATEOBJ_IMPL(WFramedPHolder, framedpholder, (p, cont, param));
+}
+
+
+void framedpholder_deinit(WFramedPHolder *ph)
+{
+    if(ph->cont!=NULL){
+        destroy_obj((Obj*)ph->cont);
+        ph->cont=NULL;
+    }
+    
+    pholder_deinit(&(ph->ph));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Attach */
+
+
+typedef struct{
+    WRegionAttachData *data;
+    WFramedParam *param;
+} AP;
+
+
+WRegion *framed_handler(WWindow *par, 
+                        const WFitParams *fp, 
+                        void *ap_)
+{
+    AP *ap=(AP*)ap_;
+    WMPlexAttachParams mp=MPLEXATTACHPARAMS_INIT;
+    WFramedParam *param=ap->param;
+    WRectangle rqg, mg;
+    WFrame *frame;
+    WRegion *reg;
+    
+    if(param->mkframe!=NULL)
+        frame=(WFrame*)(param->mkframe)(par, fp);
+    else
+        frame=create_frame(par, fp, FRAME_MODE_FLOATING);
+    
+    if(frame==NULL)
+        return NULL;
+    
+    if(fp->mode&(REGION_FIT_BOUNDS|REGION_FIT_WHATEVER))
+        mp.flags|=MPLEX_ATTACH_WHATEVER;
+
+    reg=mplex_do_attach(&frame->mplex, &mp, ap->data);
+    
+    if(reg==NULL){
+        destroy_obj((Obj*)frame);
+        return NULL;
+    }
+
+    if(!(fp->mode&(REGION_FIT_BOUNDS|REGION_FIT_WHATEVER)))
+        return (WRegion*)frame;
+
+    mplex_managed_geom((WMPlex*)frame, &mg);
+
+    /* Adjust geometry */
+    if(!param->inner_geom_gravity_set){
+        rqg.x=REGION_GEOM(frame).x;
+        rqg.y=REGION_GEOM(frame).y;
+        rqg.w=maxof(1, REGION_GEOM(reg).w+(REGION_GEOM(frame).w-mg.w));
+        rqg.h=maxof(1, REGION_GEOM(reg).h+(REGION_GEOM(frame).h-mg.h));
+    }else{
+        int bl=mg.x;
+        int br=REGION_GEOM(frame).w-(mg.x+mg.w);
+        int bt=mg.y;
+        int bb=REGION_GEOM(frame).h-(mg.y+mg.h);
+        
+        rqg.x=(fp->g.x+param->inner_geom.x+
+               xgravity_deltax(param->gravity, bl, br));
+        rqg.y=(fp->g.y+param->inner_geom.y+
+               xgravity_deltay(param->gravity, bt, bb));
+        rqg.w=maxof(1, param->inner_geom.w+(REGION_GEOM(frame).w-mg.w));
+        rqg.h=maxof(1, param->inner_geom.h+(REGION_GEOM(frame).h-mg.h));
+    }
+
+    if(!(fp->mode&REGION_FIT_WHATEVER))
+        rectangle_constrain(&rqg, &fp->g);
+    
+    region_fit((WRegion*)frame, &rqg, REGION_FIT_EXACT);
+    
+    return (WRegion*)frame;
+}
+
+
+WRegion *region_attach_framed(WRegion *reg, WFramedParam *param,
+                              WRegionAttachFn *fn, void *fn_param,
+                              WRegionAttachData *data)
+{
+    WRegionAttachData data2;
+    AP ap;
+
+    data2.type=REGION_ATTACH_NEW;
+    data2.u.n.fn=framed_handler;
+    data2.u.n.param=&ap;
+    
+    ap.data=data;
+    ap.param=param;
+    
+    return fn(reg, fn_param, &data2);
+}
+
+
+WRegion *framedpholder_do_attach(WFramedPHolder *ph, int flags,
+                                 WRegionAttachData *data)
+{
+    WRegionAttachData data2;
+    AP ap;
+    
+    if(ph->cont==NULL)
+        return FALSE;
+
+    data2.type=REGION_ATTACH_NEW;
+    data2.u.n.fn=framed_handler;
+    data2.u.n.param=&ap;
+    
+    ap.data=data;
+    ap.param=&ph->param;
+        
+    return pholder_attach_(ph->cont, flags, &data2);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Other dynfuns */
+
+
+bool framedpholder_do_goto(WFramedPHolder *ph)
+{
+    if(ph->cont!=NULL)
+        return pholder_goto(ph->cont);
+    
+    return FALSE;
+}
+
+
+WRegion *framedpholder_do_target(WFramedPHolder *ph)
+{
+    if(ph->cont!=NULL)
+        return pholder_target(ph->cont);
+    
+    return NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Class information */
+
+
+static DynFunTab framedpholder_dynfuntab[]={
+    {(DynFun*)pholder_do_attach, 
+     (DynFun*)framedpholder_do_attach},
+
+    {(DynFun*)pholder_do_goto, 
+     (DynFun*)framedpholder_do_goto},
+
+    {(DynFun*)pholder_do_target, 
+     (DynFun*)framedpholder_do_target},
+    
+    END_DYNFUNTAB
+};
+
+IMPLCLASS(WFramedPHolder, WPHolder, framedpholder_deinit, 
+          framedpholder_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/ioncore/framedpholder.h b/ioncore/framedpholder.h
new file mode 100644 (file)
index 0000000..2bd7f1b
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * ion/ioncore/framedpholder.h
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_FRAMEDPHOLDER_H
+#define ION_IONCORE_FRAMEDPHOLDER_H
+
+#include "common.h"
+#include "pholder.h"
+#include "attach.h"
+
+INTRCLASS(WFramedPHolder);
+INTRSTRUCT(WFramedParam);
+
+
+#define FRAMEDPARAM_INIT {0, 0, {0, 0, 0, 0}, NULL}
+
+
+DECLSTRUCT(WFramedParam){
+    uint inner_geom_gravity_set:1;
+    int gravity;
+    WRectangle inner_geom;
+    WRegionSimpleCreateFn *mkframe;
+};
+
+
+DECLCLASS(WFramedPHolder){
+    WPHolder ph;
+    WPHolder *cont;
+    WFramedParam param;
+};
+
+
+extern WFramedPHolder *create_framedpholder(WPHolder *cont,
+                                            const WFramedParam *param);
+
+extern bool framedpholder_init(WFramedPHolder *ph, WPHolder *cont,
+                               const WFramedParam *param);
+
+extern void framedpholder_deinit(WFramedPHolder *ph);
+
+extern bool framedpholder_do_goto(WFramedPHolder *ph);
+
+extern WRegion *framedpholder_do_target(WFramedPHolder *ph);
+
+extern WRegion *framedpholder_do_attach(WFramedPHolder *ph, int flags,
+                                        WRegionAttachData *data);
+
+extern WRegion *region_attach_framed(WRegion *reg, WFramedParam *param,
+                                     WRegionAttachFn *fn, void *fn_param,
+                                     WRegionAttachData *data);
+
+#endif /* ION_IONCORE_FRAMEDPHOLDER_H */
diff --git a/ioncore/framep.h b/ioncore/framep.h
new file mode 100644 (file)
index 0000000..a3b1283
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * ion/ioncore/framep.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_FRAMEP_H
+#define ION_IONCORE_FRAMEP_H
+
+#include "frame.h"
+#include "llist.h"
+
+#define FRAME_MCOUNT(FRAME) mplex_mx_count(&(FRAME)->mplex)
+#define FRAME_CURRENT(FRAME) mplex_mx_current(&(FRAME)->mplex)
+
+#define FRAME_MX_FOR_ALL(REG, FRAME, TMP) \
+    FOR_ALL_REGIONS_ON_LLIST(REG, (FRAME)->mplex.mx_list, TMP)
+
+enum{
+    FRAME_AREA_NONE=0,
+    FRAME_AREA_BORDER=1,
+    FRAME_AREA_TAB=2,
+    FRAME_AREA_CLIENT=3
+};
+
+#define IONCORE_EVENTMASK_FRAME    (FocusChangeMask|          \
+                                    ButtonPressMask|          \
+                                    ButtonReleaseMask|        \
+                                    KeyPressMask|             \
+                                    EnterWindowMask|          \
+                                    ExposureMask|             \
+                                    SubstructureRedirectMask)
+
+#endif /* ION_IONCORE_FRAMEP_H */
diff --git a/ioncore/fullscreen.c b/ioncore/fullscreen.c
new file mode 100644 (file)
index 0000000..e11a2d9
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * ion/ioncore/fullscreen.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/setparam.h>
+#include "common.h"
+#include "global.h"
+#include "sizehint.h"
+#include "clientwin.h"
+#include "attach.h"
+#include "screen.h"
+#include "manage.h"
+#include "fullscreen.h"
+#include "mwmhints.h"
+#include "focus.h"
+#include "group-cw.h"
+
+
+bool clientwin_fullscreen_may_switchto(WClientWin *cwin)
+{
+    return (region_may_control_focus((WRegion*)cwin)
+            || !REGION_IS_ACTIVE(region_screen_of((WRegion*)cwin)));
+}
+
+
+bool clientwin_check_fullscreen_request(WClientWin *cwin, int w, int h,
+                                        bool sw)
+{
+    WScreen *scr;
+    WMwmHints *mwm;
+    WRectangle *rwgeom;
+    
+    mwm=xwindow_get_mwmhints(cwin->win);
+    if(mwm==NULL || !(mwm->flags&MWM_HINTS_DECORATIONS) ||
+       mwm->decorations!=0)
+        return FALSE;
+    
+    FOR_ALL_SCREENS(scr){
+        if(!region_same_rootwin((WRegion*)scr, (WRegion*)cwin))
+            continue;
+        /* TODO: if there are multiple possible rootwins, use the one with
+         * requested position, if any.
+         */
+        if(REGION_GEOM(scr).w==w && REGION_GEOM(scr).h==h){
+            cwin->flags|=CLIENTWIN_FS_RQ;
+            if(!clientwin_fullscreen_scr(cwin, (WScreen*)scr, sw)){
+                cwin->flags&=~CLIENTWIN_FS_RQ;
+                return FALSE;
+            }
+            return TRUE;
+        }
+    }
+    
+    rwgeom=&REGION_GEOM(region_rootwin_of((WRegion*)cwin));
+                        
+    /* Catch Xinerama-unaware apps here */
+    if(rwgeom->w==w && rwgeom->h==h){
+        cwin->flags|=CLIENTWIN_FS_RQ;
+        if(clientwin_enter_fullscreen(cwin, sw))
+            return TRUE;
+        cwin->flags&=~CLIENTWIN_FS_RQ;
+    }
+
+    return FALSE;
+}
+
+
+static void destroy_pholder(WPHolder **fs_pholder)
+{
+    WPHolder *ph=*fs_pholder;
+    *fs_pholder=NULL;
+    destroy_obj((Obj*)ph);
+}
+
+
+static bool do_fullscreen_scr(WRegion *reg, WPHolder **fs_pholder,
+                              WScreen *scr, bool switchto)
+{
+    int rootx, rooty;
+    bool wasfs=TRUE;
+    int swf=(switchto ? MPLEX_ATTACH_SWITCHTO : 0);
+    WRegion *mgr=REGION_MANAGER(reg);
+    
+    if(*fs_pholder!=NULL)
+        destroy_pholder(fs_pholder);
+        
+    if(*fs_pholder==NULL && mgr!=NULL)
+        *fs_pholder=region_managed_get_pholder(mgr, reg);
+    
+    if(!mplex_attach_simple((WMPlex*)scr, reg, swf)){
+        warn(TR("Failed to enter full screen mode."));
+        if(*fs_pholder!=NULL)
+            destroy_pholder(fs_pholder);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+static bool do_leave_fullscreen(WRegion *reg, WPHolder **fs_pholder, 
+                                bool switchto)
+{    
+    bool cf;
+    int swf=(switchto ? PHOLDER_ATTACH_SWITCHTO : 0);
+    
+    if(*fs_pholder==NULL)
+        return FALSE;
+    
+    cf=region_may_control_focus(reg);
+    
+    if(!pholder_attach(*fs_pholder, swf, reg)){
+        warn(TR("Failed to return from full screen mode; remaining manager "
+                "or parent from previous location refused to manage us."));
+        return FALSE;
+    }
+    
+    if(*fs_pholder!=NULL)
+        destroy_pholder(fs_pholder);
+        
+    if(cf)
+        region_goto(reg);
+    
+    return TRUE;
+}
+
+
+static WRegion *get_group(WClientWin *cwin)
+{
+    WGroupCW *cwg=OBJ_CAST(REGION_MANAGER(cwin), WGroupCW);
+    
+    return ((cwg!=NULL && group_bottom(&cwg->grp)==(WRegion*)cwin)
+            ? (WRegion*)cwg
+            : (WRegion*)cwin);
+}
+
+
+bool clientwin_fullscreen_scr(WClientWin *cwin, WScreen *scr, bool switchto)
+{
+    WRegion *reg=get_group(cwin);
+    return do_fullscreen_scr(reg, &cwin->fs_pholder, scr, switchto);
+}
+
+
+bool clientwin_enter_fullscreen(WClientWin *cwin, bool switchto)
+{
+    WScreen *scr=region_screen_of((WRegion*)cwin);
+    
+    if(scr==NULL){
+        scr=rootwin_current_scr(region_rootwin_of((WRegion*)cwin));
+        if(scr==NULL)
+            return FALSE;
+    }
+    
+    return clientwin_fullscreen_scr(cwin, scr, switchto);
+}
+
+bool clientwin_leave_fullscreen(WClientWin *cwin, bool switchto)
+{
+    WRegion *reg=get_group(cwin);
+    return do_leave_fullscreen(reg, &cwin->fs_pholder, switchto);
+}
+
+
+bool clientwin_set_fullscreen(WClientWin *cwin, int sp)
+{
+    bool set=REGION_IS_FULLSCREEN(cwin);
+    bool nset=libtu_do_setparam(sp, set);
+    
+    if(!XOR(nset, set))
+        return set;
+
+    if(nset)
+        clientwin_enter_fullscreen(cwin, TRUE);
+    else
+        clientwin_leave_fullscreen(cwin, TRUE);
+    
+    return REGION_IS_FULLSCREEN(cwin);
+}
+
+
+/*EXTL_DOC
+ * Set client window \var{cwin} full screen state according to the 
+ * parameter \var{how} (set/unset/toggle). Resulting state is returned,
+ * which may not be what was requested.
+ */
+EXTL_EXPORT_AS(WClientWin, set_fullscreen)
+bool clientwin_set_fullscreen_extl(WClientWin *cwin, const char *how)
+{
+    return clientwin_set_fullscreen(cwin, libtu_string_to_setparam(how));
+}
+
+
+/*EXTL_DOC
+ * Is \var{cwin} in full screen mode?
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+bool clientwin_is_fullscreen(WClientWin *cwin)
+{
+    return REGION_IS_FULLSCREEN(cwin);
+}
+
+
+
diff --git a/ioncore/fullscreen.h b/ioncore/fullscreen.h
new file mode 100644 (file)
index 0000000..8c820a4
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * ion/ioncore/fullscreen.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_FULLSCREEN_H
+#define ION_IONCORE_FULLSCREEN_H
+
+#include <libtu/setparam.h>
+#include "common.h"
+#include "screen.h"
+#include "clientwin.h"
+
+#define REGION_IS_FULLSCREEN(REG) OBJ_IS(REGION_PARENT(REG), WScreen)
+
+extern bool clientwin_check_fullscreen_request(WClientWin *cwin, 
+                                               int w, int h, bool switchto);
+extern bool clientwin_fullscreen_scr(WClientWin *cwin, WScreen *vp,
+                                     bool switchto);
+extern bool clientwin_enter_fullscreen(WClientWin *cwin, bool switchto);
+extern bool clientwin_leave_fullscreen(WClientWin *cwin, bool switchto);
+extern bool clientwin_set_fullscreen(WClientWin *cwin, int sp);
+extern bool clientwin_is_fullscreen(WClientWin *cwin);
+extern bool clientwin_fullscreen_may_switchto(WClientWin *cwin);
+
+#endif /* ION_IONCORE_FULLSCREEN_H */
diff --git a/ioncore/global.h b/ioncore/global.h
new file mode 100644 (file)
index 0000000..3756910
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * ion/ioncore/global.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_GLOBAL_H
+#define ION_IONCORE_GLOBAL_H
+
+#include "common.h"
+
+#include <X11/Xutil.h>
+#include <X11/Xresource.h>
+
+#include "rootwin.h"
+#include "screen.h"
+#include "window.h"
+#include "clientwin.h"
+
+
+enum{
+    IONCORE_INPUTMODE_NORMAL,
+    IONCORE_INPUTMODE_GRAB,
+    IONCORE_INPUTMODE_WAITRELEASE
+};
+
+enum{
+    IONCORE_OPMODE_INIT,
+    IONCORE_OPMODE_NORMAL,
+    IONCORE_OPMODE_DEINIT
+};
+
+INTRSTRUCT(WGlobal);
+
+DECLSTRUCT(WGlobal){
+    int argc;
+    char **argv;
+    
+    Display *dpy;
+    const char *display;
+    int conn;
+    
+    XContext win_context;
+    Atom atom_wm_state;
+    Atom atom_wm_change_state;
+    Atom atom_wm_protocols;
+    Atom atom_wm_delete;
+    Atom atom_wm_take_focus;
+    Atom atom_wm_colormaps;
+    Atom atom_wm_window_role;
+    Atom atom_checkcode;
+    Atom atom_selection;
+    Atom atom_mwm_hints;
+
+    WRootWin *rootwins;
+    WScreen *screens;
+    WRegion *focus_next;
+    bool warp_next;
+    
+    /* We could have a display WRegion but the screen-link could impose
+     * some problems so these are handled as a special case.
+     */
+    WRegion *focus_current;
+    
+    int input_mode;
+    int opmode;
+    
+    Time dblclick_delay;
+    int opaque_resize;
+    bool warp_enabled;
+    bool switchto_new;
+    bool screen_notify;
+    int frame_default_index;
+    bool framed_transients;
+    
+    /*bool save_enabled;*/
+    
+    bool use_mb; /* use mb routines? */
+    bool enc_sb; /* 8-bit charset? If unset, use_mb must be set. */
+    bool enc_utf8; /* mb encoding is utf8? */
+    
+    const char *sm_client_id;
+};
+
+
+extern WGlobal ioncore_g;
+
+#endif /* ION_IONCORE_GLOBAL_H */
diff --git a/ioncore/gr.c b/ioncore/gr.c
new file mode 100644 (file)
index 0000000..d4f5a15
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * ion/ioncore/gr.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libextl/readconfig.h>
+#include <libmainloop/hooks.h>
+#include "common.h"
+#include "global.h"
+#include "modules.h"
+#include "gr.h"
+
+
+/*{{{ Lookup and registration */
+
+INTRSTRUCT(GrEngine);
+
+DECLSTRUCT(GrEngine){
+    char *name;
+    GrGetBrushFn *fn;
+    GrEngine *next, *prev;
+};
+
+
+static GrEngine *engines=NULL, *current_engine=NULL;
+
+
+bool gr_register_engine(const char *engine, GrGetBrushFn *fn)
+{
+    GrEngine *eng;
+    
+    if(engine==NULL || fn==NULL)
+        return FALSE;
+    
+    eng=ALLOC(GrEngine);
+    
+    if(eng==NULL)
+        return FALSE;
+    
+    eng->name=scopy(engine);
+    
+    if(eng->name==NULL){
+        free(eng);
+        return FALSE;
+    }
+    
+    eng->fn=fn;
+    
+    LINK_ITEM(engines, eng, next, prev);
+
+    return TRUE;
+}
+
+
+void gr_unregister_engine(const char *engine)
+{
+    GrEngine *eng;
+    
+    for(eng=engines; eng!=NULL; eng=eng->next){
+        if(strcmp(eng->name, engine)==0)
+            break;
+    }
+    
+    if(eng==NULL)
+        return;
+    
+    UNLINK_ITEM(engines, eng, next, prev);
+    free(eng->name);
+    if(current_engine==eng)
+        current_engine=NULL;
+    free(eng);
+}
+
+
+static bool gr_do_select_engine(const char *engine)
+{
+    GrEngine *eng;
+
+    for(eng=engines; eng!=NULL; eng=eng->next){
+        if(strcmp(eng->name, engine)==0){
+            current_engine=eng;
+            return TRUE;
+        }
+    }
+    
+    return FALSE;
+}
+
+
+/*EXTL_DOC
+ * Future requests for ''brushes'' are to be forwarded to the drawing engine
+ * \var{engine}. If no engine of such name is known, a module with that name
+ * is attempted to be loaded. This function is only intended to be called from
+ * colour scheme etc. configuration files and can not be used to change the
+ * look of existing objects; for that use \fnref{gr.read_config}.
+ */
+EXTL_EXPORT_AS(gr, select_engine)
+bool gr_select_engine(const char *engine)
+{
+    if(engine==NULL)
+        return FALSE;
+    
+    if(gr_do_select_engine(engine))
+        return TRUE;
+    
+    if(!ioncore_load_module(engine))
+        return FALSE;
+    
+    if(!gr_do_select_engine(engine)){
+        warn(TR("Drawing engine %s is not registered!"), engine);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+GrBrush *gr_get_brush(Window win, WRootWin *rootwin, const char *style)
+{
+    GrEngine *eng=(current_engine!=NULL ? current_engine : engines);
+    GrBrush *ret;
+    
+    if(eng==NULL || eng->fn==NULL)
+        return NULL;
+    
+    ret=(eng->fn)(win, rootwin, style);
+    
+    if(ret==NULL)
+        warn(TR("Unable to find brush for style '%s'."), style);
+        
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Scoring */
+
+
+uint gr_stylespec_score2(const char *spec, const char *attrib, 
+                         const char *attrib_p2)
+{
+    uint score=0;
+    uint a=0;
+    uint mult=1;
+
+    if(attrib==NULL){
+        if(spec==NULL || strcmp(spec, "*")==0)
+            return 1;
+        return 0;
+    }
+    
+    while(1){
+        if(*spec=='*'){
+            score=score+mult;
+            spec++;
+            attrib=strchr(attrib, '-');
+        }else{
+            while(1){
+                if(*attrib=='\0'){
+                    attrib=NULL;
+                    break;
+                }
+                if(*attrib=='-')
+                    break;
+                if(*spec!=*attrib)
+                    return 0;
+                attrib++;
+                spec++;
+            }
+            score=score+2*mult;
+        }
+        
+        if(*spec=='\0')
+            return score;
+        else if(*spec!='-')
+            return 0;
+        
+        if(attrib==NULL){
+            if(a==0 && attrib_p2!=NULL){
+                attrib=attrib_p2;
+                a++;
+            }else{
+                return 0;
+            }
+        }else{
+            attrib++;
+        }
+
+        spec++;
+        mult=mult*3;
+    }
+}
+
+
+uint gr_stylespec_score(const char *spec, const char *attrib)
+{
+    return gr_stylespec_score2(spec, attrib, NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init, deinit */
+
+
+bool grbrush_init(GrBrush *brush)
+{
+    return TRUE;
+}
+
+
+void grbrush_deinit(GrBrush *brush)
+{
+}
+
+
+void grbrush_release(GrBrush *brush)
+{
+    CALL_DYN(grbrush_release, brush, (brush));
+}
+
+
+GrBrush *grbrush_get_slave(GrBrush *brush, WRootWin *rootwin, 
+                           const char *style)
+{
+    GrBrush *slave=NULL;
+    CALL_DYN_RET(slave, GrBrush*, grbrush_get_slave, brush,
+                 (brush, rootwin, style));
+    return slave;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuns/begin/end/replay */
+
+
+void grbrush_begin(GrBrush *brush, const WRectangle *geom, int flags)
+{
+    CALL_DYN(grbrush_begin, brush, (brush, geom, flags));
+}
+
+
+void grbrush_end(GrBrush *brush)
+{
+    CALL_DYN(grbrush_end, brush, (brush));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuns/values */
+
+
+void grbrush_get_font_extents(GrBrush *brush, GrFontExtents *fnte)
+{
+    CALL_DYN(grbrush_get_font_extents, brush, (brush, fnte));
+}
+
+
+void grbrush_get_border_widths(GrBrush *brush, GrBorderWidths *bdw)
+{
+    CALL_DYN(grbrush_get_border_widths, brush, (brush, bdw));
+}
+
+
+DYNFUN bool grbrush_get_extra(GrBrush *brush, const char *key, 
+                              char type, void *data)
+{
+    bool ret=FALSE;
+    CALL_DYN_RET(ret, bool, grbrush_get_extra, brush,
+                 (brush, key, type, data));
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuns/Borders */
+
+
+void grbrush_draw_border(GrBrush *brush, const WRectangle *geom,
+                         const char *attrib)
+{
+    CALL_DYN(grbrush_draw_border, brush, (brush, geom, attrib));
+}
+
+
+void grbrush_draw_borderline(GrBrush *brush, const WRectangle *geom,
+                             const char *attrib, GrBorderLine line)
+{
+    CALL_DYN(grbrush_draw_borderline, brush, (brush, geom, attrib, line));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuns/Strings */
+
+
+void grbrush_draw_string(GrBrush *brush, int x, int y,
+                         const char *str, int len, bool needfill,
+                         const char *attrib)
+{
+    CALL_DYN(grbrush_draw_string, brush, 
+             (brush, x, y, str, len, needfill, attrib));
+}
+
+
+uint grbrush_get_text_width(GrBrush *brush, const char *text, uint len)
+{
+    uint ret=0;
+    CALL_DYN_RET(ret, uint, grbrush_get_text_width, brush, 
+                 (brush, text, len));
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuns/Textboxes */
+
+
+void grbrush_draw_textbox(GrBrush *brush, const WRectangle *geom,
+                          const char *text, const char *attr,
+                          bool needfill)
+{
+    CALL_DYN(grbrush_draw_textbox, brush, 
+             (brush, geom, text, attr, needfill));
+}
+
+void grbrush_draw_textboxes(GrBrush *brush, const WRectangle *geom,
+                            int n, const GrTextElem *elem, 
+                            bool needfill, const char *common_attrib)
+{
+    CALL_DYN(grbrush_draw_textboxes, brush, 
+             (brush, geom, n, elem, needfill, common_attrib));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuns/Misc */
+
+
+void grbrush_set_window_shape(GrBrush *brush, bool rough,
+                              int n, const WRectangle *rects)
+{
+    CALL_DYN(grbrush_set_window_shape, brush, (brush, rough, n, rects));
+}
+
+
+void grbrush_enable_transparency(GrBrush *brush, GrTransparency tr)
+{
+    CALL_DYN(grbrush_enable_transparency, brush, (brush, tr));
+}
+
+
+void grbrush_fill_area(GrBrush *brush, const WRectangle *geom,
+                       const char *attr)
+{
+    CALL_DYN(grbrush_fill_area, brush, (brush, geom, attr));
+}
+
+
+void grbrush_clear_area(GrBrush *brush, const WRectangle *geom)
+{
+    CALL_DYN(grbrush_clear_area, brush, (brush, geom));
+}
+
+
+/*}}}*/
+
+
+/*{{{ ioncore_read_config/refresh */
+
+
+/*EXTL_DOC
+ * Read drawing engine configuration file \file{draw.lua}.
+ */
+EXTL_EXPORT_AS(gr, read_config)
+void gr_read_config()
+{
+    extl_read_config("look", NULL, TRUE);
+    
+    /* If nothing has been loaded, try the default engine with
+     * default settings.
+     */
+    if(engines==NULL){
+        warn(TR("No drawing engines loaded, trying \"de\"."));
+        gr_select_engine("de");
+    }
+}
+
+
+/*EXTL_DOC
+ * Refresh objects' brushes to update them to use newly loaded style.
+ */
+EXTL_EXPORT_AS(gr, refresh)
+void gr_refresh()
+{
+    WRootWin *rootwin;
+    
+    FOR_ALL_ROOTWINS(rootwin){
+        region_updategr((WRegion*)rootwin);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Class implementation */
+
+
+static DynFunTab grbrush_dynfuntab[]={
+    END_DYNFUNTAB
+};
+                                       
+
+IMPLCLASS(GrBrush, Obj, grbrush_deinit, grbrush_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/ioncore/gr.h b/ioncore/gr.h
new file mode 100644 (file)
index 0000000..911edc6
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * ion/ioncore/gr.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_GR_H
+#define ION_IONCORE_GR_H
+
+#include "common.h"
+#include "rectangle.h"
+
+
+INTRCLASS(GrBrush);
+DECLCLASS(GrBrush){
+    Obj obj;
+};
+
+
+#include "rootwin.h"
+
+/* Types */
+
+#define GR_FONT_EXTENTS_INIT {0, 0, 0}
+
+typedef struct{
+    uint max_height;
+    uint max_width;
+    uint baseline;
+} GrFontExtents;
+
+#define GR_BORDER_WIDTHS_INIT {0, 0, 0, 0, 0, 0, 0}
+
+typedef struct{
+    uint top, bottom, left, right;
+    uint tb_ileft, tb_iright;
+    uint spacing;
+} GrBorderWidths;
+
+typedef struct{
+    char *text;
+    int iw;
+    char *attr;
+} GrTextElem;
+
+typedef enum{
+    GR_TRANSPARENCY_NO,
+    GR_TRANSPARENCY_YES,
+    GR_TRANSPARENCY_DEFAULT
+} GrTransparency;
+
+typedef enum{
+    GR_BORDERLINE_NONE,
+    GR_BORDERLINE_LEFT,
+    GR_BORDERLINE_RIGHT,
+    GR_BORDERLINE_TOP,
+    GR_BORDERLINE_BOTTOM
+} GrBorderLine;
+
+/* Flags to grbrush_begin */
+#define GRBRUSH_AMEND       0x0001
+#define GRBRUSH_NEED_CLIP   0x0004
+#define GRBRUSH_NO_CLEAR_OK 0x0008 /* implied by GRBRUSH_AMEND */
+
+/* Engines etc. */
+
+typedef GrBrush *GrGetBrushFn(Window win, WRootWin *rootwin,
+                              const char *style);
+
+extern bool gr_register_engine(const char *engine,  GrGetBrushFn *fn);
+extern void gr_unregister_engine(const char *engine);
+extern bool gr_select_engine(const char *engine);
+extern void gr_refresh();
+extern void gr_read_config();
+
+/* Stylespecs are of the from attr1-attr2-etc. We require that each attr in
+ * 'spec' matches the one at same index in 'attrib' when '*' matches anything.
+ * The score increment for exact match is 2*3^index and 1*3^index for '*' 
+ * match. If all elements of 'spec' match those of 'attrib' exactly, the 
+ * accumulated score is returned. Otherwise the matching fails and zero is
+ * returned. For example:
+ *  
+ *  spec        attrib            score
+ *     foo-*-baz     foo-bar-baz        2+1*3+2*3^2 = 23
+ *  foo-bar          foo-bar-baz        2+2*3         = 8
+ *  foo-baz          foo-bar-baz        0
+ * 
+ * gr_stylespec_score2 continues matching from attrib_p2 (if not NULL) when
+ * it has reached end of attrib.
+ */
+extern uint gr_stylespec_score(const char *spec, const char *attrib);
+extern uint gr_stylespec_score2(const char *spec, const char *attrib, 
+                                const char *attrib_p2);
+
+/* GrBrush */
+
+extern GrBrush *gr_get_brush(Window win, WRootWin *rootwin,
+                             const char *style);
+
+extern GrBrush *grbrush_get_slave(GrBrush *brush, WRootWin *rootwin, 
+                                  const char *style);
+
+extern void grbrush_release(GrBrush *brush);
+
+extern bool grbrush_init(GrBrush *brush);
+extern void grbrush_deinit(GrBrush *brush);
+
+extern void grbrush_begin(GrBrush *brush, const WRectangle *geom,
+                          int flags);
+extern void grbrush_end(GrBrush *brush);
+
+/* Border drawing */
+
+DYNFUN void grbrush_get_border_widths(GrBrush *brush, GrBorderWidths *bdi);
+
+DYNFUN void grbrush_draw_border(GrBrush *brush, const WRectangle *geom,
+                                const char *attrib);
+DYNFUN void grbrush_draw_borderline(GrBrush *brush, const WRectangle *geom,
+                                    const char *attrib, GrBorderLine line);
+
+/* String drawing */
+
+DYNFUN void grbrush_get_font_extents(GrBrush *brush, GrFontExtents *fnti);
+
+DYNFUN uint grbrush_get_text_width(GrBrush *brush, const char *text, uint len);
+
+DYNFUN void grbrush_draw_string(GrBrush *brush, int x, int y,
+                                const char *str, int len, bool needfill,
+                                const char *attrib);
+
+/* Textbox drawing */
+
+DYNFUN void grbrush_draw_textbox(GrBrush *brush, const WRectangle *geom,
+                                 const char *text, const char *attr,
+                                 bool needfill);
+
+DYNFUN void grbrush_draw_textboxes(GrBrush *brush, const WRectangle *geom,
+                                   int n, const GrTextElem *elem, 
+                                   bool needfill, const char *common_attrib);
+
+/* Misc */
+
+/* Behaviour of the following two functions for "slave brushes" is undefined. 
+ * If the parameter rough to grbrush_set_window_shape is set, the actual 
+ * shape may be changed for corner smoothing and other superfluous effects.
+ * (This feature is only used by floatframes.)
+ */
+DYNFUN void grbrush_set_window_shape(GrBrush *brush, bool rough,
+                                     int n, const WRectangle *rects);
+
+DYNFUN void grbrush_enable_transparency(GrBrush *brush, GrTransparency mode);
+
+DYNFUN void grbrush_fill_area(GrBrush *brush, const WRectangle *geom, 
+                              const char *attr);
+DYNFUN void grbrush_clear_area(GrBrush *brush, const WRectangle *geom);
+
+DYNFUN bool grbrush_get_extra(GrBrush *brush, const char *key, 
+                              char type, void *data);
+
+#endif /* ION_IONCORE_GR_H */
diff --git a/ioncore/grab.c b/ioncore/grab.c
new file mode 100644 (file)
index 0000000..d3bbcad
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * ion/ioncore/grab.c
+ * 
+ * Copyright (c) Lukas Schroeder 2002,
+ *               Tuomo Valkonen 2003.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+
+#include "common.h"
+#include "global.h"
+#include "event.h"
+#include "cursor.h"
+#include "grab.h"
+
+
+/*{{{ Definitions */
+
+
+typedef struct _grab_status{
+    WRegion *holder;
+    GrabHandler *handler;
+    GrabKilledHandler *killedhandler;
+    Watch watch;
+    long eventmask;
+    long flags;
+    
+    bool remove;    /* TRUE, if entry marked for removal by do_grab_remove() */
+    int cursor;
+    Window confine_to;
+    int sqid;
+}GrabStatus;
+
+#define MAX_GRABS 4
+static GrabStatus grabs[MAX_GRABS];
+static GrabStatus *current_grab;
+static int idx_grab=0;
+static int last_sqid=0;
+
+
+/*}}}*/
+
+
+/*{{{ do_grab/ungrab */
+
+
+static void grab_kb_ptr(Window win, Window confine_to, int cursor, 
+                        long eventmask)
+{
+    ioncore_g.input_mode=IONCORE_INPUTMODE_GRAB;
+    
+    XSelectInput(ioncore_g.dpy, win, IONCORE_EVENTMASK_ROOT&~eventmask);
+    XGrabPointer(ioncore_g.dpy, win, True, IONCORE_EVENTMASK_PTRGRAB,
+                 GrabModeAsync, GrabModeAsync, confine_to,
+                 ioncore_xcursor(cursor), CurrentTime);
+    XGrabKeyboard(ioncore_g.dpy, win, False, GrabModeAsync,
+                  GrabModeAsync, CurrentTime);
+    XSync(ioncore_g.dpy, False);
+    XSelectInput(ioncore_g.dpy, win, IONCORE_EVENTMASK_ROOT);
+}
+
+
+static void ungrab_kb_ptr()
+{
+    XUngrabKeyboard(ioncore_g.dpy, CurrentTime);
+    XUngrabPointer(ioncore_g.dpy, CurrentTime);
+    
+    ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Functions for installing grabs */
+
+
+static void do_holder_remove(WRegion *holder, bool killed);
+
+
+static void grab_watch_handler(Watch *w, Obj *obj)
+{
+    do_holder_remove((WRegion*)obj, TRUE);
+}
+
+
+static void do_grab_install(GrabStatus *grab)
+{
+    watch_setup(&grab->watch, (Obj*)grab->holder, grab_watch_handler);
+    grab_kb_ptr(region_root_of(grab->holder), grab->confine_to, 
+                grab->cursor, grab->eventmask);
+    current_grab=grab;
+}
+
+
+void ioncore_grab_establish(WRegion *reg, GrabHandler *func, 
+                            GrabKilledHandler *kh,
+                            long eventmask)
+{
+    assert((~eventmask)&(KeyPressMask|KeyReleaseMask));
+    
+    if(idx_grab<MAX_GRABS){
+        current_grab=&grabs[idx_grab++];
+        current_grab->holder=reg;
+        current_grab->handler=func;
+        current_grab->killedhandler=kh;
+        current_grab->eventmask=eventmask;
+        current_grab->remove=FALSE;
+        current_grab->cursor=IONCORE_CURSOR_DEFAULT;
+        current_grab->confine_to=region_root_of(reg);
+        current_grab->sqid=last_sqid++;
+        do_grab_install(current_grab);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Grab removal functions */
+
+
+static void do_grab_remove()
+{
+    current_grab=NULL;
+    ungrab_kb_ptr();
+    
+    while(idx_grab>0 && grabs[idx_grab-1].remove==TRUE){
+        watch_reset(&grabs[idx_grab-1].watch);
+        idx_grab--;
+    }
+    
+    assert(idx_grab>=0);
+    
+    if(idx_grab>0){
+        current_grab=&grabs[idx_grab-1];
+        do_grab_install(current_grab);
+    }
+}
+
+
+static void mark_for_removal(GrabStatus *grab, bool killed)
+{
+    if(!grab->remove){
+        grab->remove=TRUE;
+        if(killed && grab->killedhandler!=NULL && grab->holder!=NULL)
+            grab->killedhandler(grab->holder);
+    }
+    
+    if(grabs[idx_grab-1].remove)
+        do_grab_remove();
+}
+
+
+static void do_holder_remove(WRegion *holder, bool killed)
+{
+    int i;
+    
+    for(i=idx_grab-1; i>=0; i--){
+        if(grabs[i].holder==holder)
+            mark_for_removal(grabs+i, killed);
+    }
+}
+
+
+void ioncore_grab_holder_remove(WRegion *holder)
+{
+    do_holder_remove(holder, FALSE);
+}
+
+
+void ioncore_grab_remove(GrabHandler *func)
+{
+    int i;
+    for(i=idx_grab-1; i>=0; i--){
+        if(grabs[i].handler==func){
+            mark_for_removal(grabs+i, FALSE);
+            break;
+        }
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Grab handler calling */
+
+
+bool ioncore_handle_grabs(XEvent *ev)
+{
+    GrabStatus *gr;
+    int gr_sqid;
+    
+    while(current_grab && current_grab->remove)
+        do_grab_remove();
+    
+    if(current_grab==NULL || current_grab->holder==NULL ||
+       current_grab->handler==NULL){
+        return FALSE;
+    }
+    
+    /* Escape key is harcoded to always kill active grab. */
+    if(ev->type==KeyPress && XLookupKeysym(&(ev->xkey), 0)==XK_Escape){
+        mark_for_removal(current_grab, TRUE);
+        return TRUE;
+    }
+    
+    if(ev->type!=KeyRelease && ev->type!=KeyPress)
+        return FALSE;
+    
+    /* We must check that the grab pointed to by current_grab still
+     * is the same grab and not already released or replaced by
+     * another grab.
+     */
+    gr=current_grab;
+    gr_sqid=gr->sqid;
+    if(gr->handler(gr->holder, ev) && gr->sqid==gr_sqid)
+        mark_for_removal(gr, FALSE);
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+bool ioncore_grab_held()
+{
+    return idx_grab>0;
+}
+
+
+void ioncore_change_grab_cursor(int cursor)
+{
+    if(current_grab!=NULL){
+        current_grab->cursor=cursor;
+        XChangeActivePointerGrab(ioncore_g.dpy, IONCORE_EVENTMASK_PTRGRAB,
+                                 ioncore_xcursor(cursor), CurrentTime);
+    }
+}
+
+
+void ioncore_grab_confine_to(Window confine_to)
+{
+    if(current_grab!=NULL){
+        current_grab->confine_to=confine_to;
+        XGrabPointer(ioncore_g.dpy, region_root_of(current_grab->holder),
+                     True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, 
+                     GrabModeAsync, confine_to, 
+                     ioncore_xcursor(IONCORE_CURSOR_DEFAULT), 
+                     CurrentTime);
+    }
+}
+
+
+WRegion *ioncore_grab_get_holder()
+{
+    if (ioncore_grab_held())
+        return grabs[idx_grab-1].holder;
+    return NULL;
+}
+
+
+WRegion *ioncore_grab_get_my_holder(GrabHandler *func)
+{
+    int i;
+    for(i=idx_grab-1; i>=0; i--)
+        if(grabs[i].handler==func)
+            return grabs[i].holder;
+    return NULL;
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/grab.h b/ioncore/grab.h
new file mode 100644 (file)
index 0000000..0b09c0a
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * ion/ioncore/grab.h
+ *
+ * Copyright (c) Lukas Schroeder 2002,
+ *                 Tuomo Valkonen 2003.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_GRAB_H
+#define ION_IONCORE_GRAB_H
+
+#include "global.h" /* for InputHandler and InputHandlerContext */
+#include "common.h"
+#include "region.h"
+
+/* GrabHandler:
+   the default_keyboard_handler now simplifies access to subsequent keypresses
+   when you establish a grab using grab_establish().
+
+   if your GrabHandler returns TRUE, your grab will be removed, otherwise it's
+   kept active and you get more grabbed events passed to your handler.
+ */
+typedef bool GrabHandler(WRegion *reg, XEvent *ev);
+typedef void GrabKilledHandler(WRegion *reg);
+
+extern void ioncore_grab_establish(WRegion *reg, GrabHandler *func,
+                                   GrabKilledHandler *kh,long eventmask);
+extern void ioncore_grab_remove(GrabHandler *func);
+extern void ioncore_grab_holder_remove(WRegion *holder);
+extern WRegion *ioncore_grab_get_holder();
+extern WRegion *ioncore_grab_get_my_holder(GrabHandler *func);
+extern bool ioncore_grab_held();
+extern void ioncore_change_grab_cursor(int cursor);
+extern void ioncore_grab_confine_to(Window confine_to);
+
+extern bool ioncore_handle_grabs(XEvent *ev);
+
+#endif /* ION_IONCORE_GRAB_H */
+
diff --git a/ioncore/group-cw.c b/ioncore/group-cw.c
new file mode 100644 (file)
index 0000000..dc5cc69
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * ion/ioncore/group-cw.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libmainloop/defer.h>
+
+#include "common.h"
+#include "group-cw.h"
+#include "clientwin.h"
+#include "regbind.h"
+#include "bindmaps.h"
+#include "frame.h"
+#include "resize.h"
+#include "pholder.h"
+#include "names.h"
+#include "framedpholder.h"
+#include "grouppholder.h"
+
+
+#define DFLT_SZPLCY SIZEPOLICY_FREE_GLUE__SOUTH
+
+
+/*{{{ Add/remove managed */
+
+
+WRegion *create_transient_frame(WWindow *par, 
+                                const WFitParams *fp)
+{
+    return (WRegion*)create_frame(par, fp, FRAME_MODE_TRANSIENT);
+}
+
+
+static WPHolder *groupcw_transient_pholder(WGroupCW *cwg, 
+                                           const WClientWin *cwin,
+                                           const WManageParams *mp)
+{
+    WGroupAttachParams param=GROUPATTACHPARAMS_INIT;
+    WFramedParam fp=FRAMEDPARAM_INIT;
+    WPHolder *ph;
+    
+    param.level_set=1;
+    param.level=STACKING_LEVEL_MODAL1;
+    
+    param.szplcy_set=1;
+    param.szplcy=cwg->transient_szplcy;
+    
+    param.switchto_set=1;
+    param.switchto=1;
+
+    param.geom_weak_set=1;
+    param.geom_weak=REGION_RQGEOM_WEAK_ALL;
+    
+    if(!ioncore_g.framed_transients){
+        param.geom_set=TRUE;
+        param.geom=mp->geom;
+        
+        return (WPHolder*)create_grouppholder(&cwg->grp, NULL, &param);
+    }else{
+        fp.inner_geom_gravity_set=1;
+        fp.inner_geom=mp->geom;
+        fp.gravity=ForgetGravity;
+        fp.mkframe=create_transient_frame;
+        
+        ph=(WPHolder*)create_grouppholder(&cwg->grp, NULL, &param);
+        
+        return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph);
+    }
+}
+
+
+WPHolder *groupcw_prepare_manage(WGroupCW *cwg, const WClientWin *cwin,
+                                 const WManageParams *param, int redir)
+{
+    if(redir==MANAGE_REDIR_STRICT_YES)
+        return NULL;
+    
+    /* Only catch windows with transient mode set to current here. */
+    if(clientwin_get_transient_mode(cwin)!=TRANSIENT_MODE_CURRENT)
+        return NULL;
+    
+    return groupcw_transient_pholder(cwg, cwin, param);
+}
+
+
+static bool groupcw_should_manage_transient(WGroupCW *cwg, 
+                                            WClientWin *tfor)
+{
+    WRegion *mgr;
+    
+    if(group_find_stacking(&cwg->grp, (WRegion*)tfor))
+        return TRUE;
+    mgr=REGION_MANAGER(tfor);
+    
+    if(mgr!=NULL && ioncore_g.framed_transients && OBJ_IS(mgr, WFrame))
+        return (group_find_stacking(&cwg->grp, mgr)!=NULL);
+    
+    return FALSE;
+}
+
+
+WPHolder *groupcw_prepare_manage_transient(WGroupCW *cwg, 
+                                           const WClientWin *transient,
+                                           const WManageParams *param,
+                                           int unused)
+{
+    WPHolder *ph=region_prepare_manage_transient_default((WRegion*)cwg,
+                                                         transient,
+                                                         param,
+                                                         unused);
+    
+    if(ph==NULL && groupcw_should_manage_transient(cwg, param->tfor))
+        ph=groupcw_transient_pholder(cwg, transient, param);
+
+    return ph;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+/*_EXTL_DOC
+ * Toggle transients managed by \var{cwin} between top/bottom
+ * of the window.
+ */
+EXTL_EXPORT_MEMBER
+void groupcw_toggle_transients_pos(WGroupCW *cwg)
+{
+    WStacking *st;
+    WGroupIterTmp tmp;
+    
+    if((cwg->transient_szplcy&SIZEPOLICY_VERT_MASK)==SIZEPOLICY_VERT_TOP){
+        cwg->transient_szplcy&=~SIZEPOLICY_VERT_MASK;
+        cwg->transient_szplcy|=SIZEPOLICY_VERT_BOTTOM;
+    }else{
+        cwg->transient_szplcy&=~SIZEPOLICY_VERT_MASK;
+        cwg->transient_szplcy|=SIZEPOLICY_VERT_TOP;
+    }
+
+    FOR_ALL_NODES_IN_GROUP(&cwg->grp, st, tmp){
+        st->szplcy&=~SIZEPOLICY_VERT_MASK;
+        st->szplcy|=(cwg->transient_szplcy&SIZEPOLICY_VERT_MASK);
+        
+        if(st->reg!=NULL){
+            WFitParams fp;
+
+            fp.g=REGION_GEOM(cwg);
+            
+            sizepolicy(&st->szplcy, st->reg, NULL, 
+                       REGION_RQGEOM_WEAK_ALL, &fp);
+            region_fitrep(st->reg, NULL, &fp);
+        }
+    }
+}
+
+
+const char *groupcw_displayname(WGroupCW *cwg)
+{
+    const char *name=NULL;
+    
+    if(cwg->grp.bottom!=NULL && cwg->grp.bottom->reg!=NULL)
+        name=region_name(cwg->grp.bottom->reg);
+    
+    if(name==NULL)
+        name=region_name((WRegion*)cwg);
+    
+    return name;
+}
+
+
+void groupcw_managed_notify(WGroupCW *cwg, WRegion *reg, const char *how)
+{
+    if(group_bottom(&cwg->grp)==reg 
+       && strcmp(how, "name")==0){
+        /* Title has changed */
+        region_notify_change((WRegion*)cwg, how);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ WGroupCW class */
+
+
+bool groupcw_init(WGroupCW *cwg, WWindow *parent, const WFitParams *fp)
+{
+    cwg->transient_szplcy=DFLT_SZPLCY;
+    /*cwg->fs_pholder=NULL;*/
+    
+    if(!group_init(&(cwg->grp), parent, fp))
+        return FALSE;
+    
+    cwg->grp.bottom_last_close=TRUE;
+
+    region_add_bindmap((WRegion*)cwg, ioncore_groupcw_bindmap);
+    
+    return TRUE;
+}
+
+
+WGroupCW *create_groupcw(WWindow *parent, const WFitParams *fp)
+{
+    CREATEOBJ_IMPL(WGroupCW, groupcw, (p, parent, fp));
+}
+
+
+void groupcw_deinit(WGroupCW *cwg)
+{    
+    group_deinit(&(cwg->grp));
+}
+
+
+WRegion *groupcw_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
+{
+    WGroupCW *ws;
+    ExtlTab substab, subtab;
+    int i, n;
+    
+    ws=create_groupcw(par, fp);
+    
+    if(ws==NULL)
+        return NULL;
+        
+    if(!extl_table_gets_t(tab, "managed", &substab))
+        return (WRegion*)ws;
+
+    n=extl_table_get_n(substab);
+    for(i=1; i<=n; i++){
+        if(extl_table_geti_t(substab, i, &subtab)){
+            group_attach_new(&ws->grp, subtab);
+            extl_unref_table(subtab);
+        }
+    }
+    
+    extl_unref_table(substab);
+    
+    if(ws->grp.managed_list==NULL){
+        destroy_obj((Obj*)ws);
+        return NULL;
+    }
+
+    return (WRegion*)ws;
+}
+
+
+static DynFunTab groupcw_dynfuntab[]={
+    {(DynFun*)region_prepare_manage, 
+     (DynFun*)groupcw_prepare_manage},
+    
+    {(DynFun*)region_prepare_manage_transient,
+     (DynFun*)groupcw_prepare_manage_transient},
+    
+    /*
+    {(DynFun*)region_handle_drop,
+     (DynFun*)groupcw_handle_drop},
+    
+    {(DynFun*)group_do_add_managed,
+     (DynFun*)groupcw_do_add_managed},
+    */
+    
+    /*
+    {(DynFun*)region_get_rescue_pholder_for,
+     (DynFun*)groupcw_get_rescue_pholder_for},
+     */
+    
+    {(DynFun*)region_prepare_manage,
+     (DynFun*)groupcw_prepare_manage},
+
+    {(DynFun*)region_prepare_manage_transient,
+     (DynFun*)groupcw_prepare_manage_transient},
+    
+    {(DynFun*)region_displayname,
+     (DynFun*)groupcw_displayname},
+    
+    {region_managed_notify,
+     groupcw_managed_notify},
+    
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WGroupCW, WGroup, groupcw_deinit, groupcw_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/ioncore/group-cw.h b/ioncore/group-cw.h
new file mode 100644 (file)
index 0000000..b389d46
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * ion/ioncore/group-cw.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_GROUPCW_H
+#define ION_IONCORE_GROUPCW_H
+
+#include "common.h"
+#include "group.h"
+#include "clientwin.h"
+
+
+DECLCLASS(WGroupCW){
+    WGroup grp;
+    /*WPHolder *fs_pholder;*/
+    WSizePolicy transient_szplcy; /* default transient size policy */
+};
+
+extern bool groupcw_init(WGroupCW *cwg, WWindow *parent, const WFitParams *fp);
+extern WGroupCW *create_groupcw(WWindow *parent, const WFitParams *fp);
+extern void groupcw_deinit(WGroupCW *cwg);
+
+extern WPHolder *groupcw_prepare_manage(WGroupCW *cwg, 
+                                        const WClientWin *cwin2,
+                                        const WManageParams *param, 
+                                        int redir);
+
+extern WPHolder *groupcw_prepare_manage_transient(WGroupCW *cwg, 
+                                                  const WClientWin *transient,
+                                                  const WManageParams *param,
+                                                  int unused);
+
+extern WRegion *groupcw_load(WWindow *par, const WFitParams *fp, ExtlTab tab);
+
+extern WRegion *create_transient_frame(WWindow *par, 
+                                       const WFitParams *fp);
+
+#endif /* ION_IONCORE_GROUPCW_H */
diff --git a/ioncore/group-ws.c b/ioncore/group-ws.c
new file mode 100644 (file)
index 0000000..b50402c
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * ion/ioncore/group-ws.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/minmax.h>
+#include <libtu/objp.h>
+
+#include "common.h"
+#include "global.h"
+#include "region.h"
+#include "focus.h"
+#include "group.h"
+#include "regbind.h"
+#include "bindmaps.h"
+#include "xwindow.h"
+#include "group-ws.h"
+#include "group-cw.h"
+#include "grouppholder.h"
+#include "groupedpholder.h"
+#include "framedpholder.h"
+#include "float-placement.h"
+#include "resize.h"
+
+
+/*{{{ Settings */
+
+
+static bool default_ws_params_set=FALSE;
+static ExtlTab default_ws_params;
+
+
+/*EXTL_DOC
+ * Set module basic settings. Currently only the \code{placement_method} 
+ * parameter is supported.
+ *
+ * The method can be one  of ''udlr'', ''lrud'' (default) and ''random''. 
+ * The ''udlr'' method looks for free space starting from top the top left
+ * corner of the workspace moving first down keeping the x coordinate fixed.
+ * If it find no free space, it start looking similarly at next x coordinate
+ * unoccupied by other objects and so on. ''lrud' is the same but with the 
+ * role of coordinates changed and both fall back to ''random'' placement 
+ * if no free area was found.
+ */
+
+void ioncore_groupws_set(ExtlTab tab)
+{
+    char *method=NULL;
+    ExtlTab t;
+    
+    if(extl_table_gets_s(tab, "float_placement_method", &method)){
+        if(strcmp(method, "udlr")==0)
+            ioncore_placement_method=PLACEMENT_UDLR;
+        else if(strcmp(method, "lrud")==0)
+            ioncore_placement_method=PLACEMENT_LRUD;
+        else if(strcmp(method, "random")==0)
+            ioncore_placement_method=PLACEMENT_RANDOM;
+        else
+            warn(TR("Unknown placement method \"%s\"."), method);
+        free(method);
+    }
+    
+    if(extl_table_gets_t(tab, "default_ws_params", &t)){
+        if(default_ws_params_set)
+            extl_unref_table(default_ws_params);
+        default_ws_params=t;
+        default_ws_params_set=TRUE;
+    }
+}
+
+
+void ioncore_groupws_get(ExtlTab t)
+{
+    extl_table_sets_s(t, "float_placement_method", 
+                      (ioncore_placement_method==PLACEMENT_UDLR
+                       ? "udlr" 
+                       : (ioncore_placement_method==PLACEMENT_LRUD
+                          ? "lrud" 
+                          : "random")));
+    
+    if(default_ws_params_set)
+        extl_table_sets_t(t, "default_ws_params", default_ws_params);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Attach stuff */
+
+
+static bool groupws_attach_framed(WGroupWS *ws, 
+                                  WGroupAttachParams *ap,
+                                  WFramedParam *fp,
+                                  WRegion *reg)
+{
+    WRegionAttachData data;
+    
+    data.type=REGION_ATTACH_REPARENT;
+    data.u.reg=reg;
+    
+    return (region_attach_framed((WRegion*)ws, fp,
+                                 (WRegionAttachFn*)group_do_attach,
+                                 ap, &data)!=NULL);
+}
+
+
+bool groupws_handle_drop(WGroupWS *ws, int x, int y,
+                         WRegion *dropped)
+{
+    WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
+    WFramedParam fp=FRAMEDPARAM_INIT;
+    
+    ap.switchto_set=TRUE;
+    ap.switchto=TRUE;
+    
+    fp.inner_geom_gravity_set=TRUE;
+    fp.inner_geom.x=x;
+    fp.inner_geom.y=y;
+    fp.inner_geom.w=REGION_GEOM(dropped).w;
+    fp.inner_geom.h=REGION_GEOM(dropped).h;
+    fp.gravity=NorthWestGravity;
+    
+    return groupws_attach_framed(ws, &ap, &fp, dropped);
+}
+
+
+/*EXTL_DOC
+ * Attach region \var{reg} on \var{ws}.
+ * At least the following fields in \var{t} are supported:
+ * 
+ * \begin{tabularx}{\linewidth}{lX}
+ *  \tabhead{Field & Description}
+ *  \var{switchto} & Should the region be switched to (boolean)? Optional. \\
+ *  \var{geom} & Geometry; \var{x} and \var{y}, if set, indicates top-left of 
+ *   the frame to be created while \var{width} and \var{height}, if set, indicate
+ *   the size of the client window within that frame. Optional.
+ * \end{tabularx}
+ */
+EXTL_EXPORT_AS(WGroupWS, attach_framed)
+bool groupws_attach_framed_extl(WGroupWS *ws, WRegion *reg, ExtlTab t)
+{
+    WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
+    WFramedParam fp=FRAMEDPARAM_INIT;
+    ExtlTab gt;
+    
+    if(reg==NULL)
+        return FALSE;
+    
+    fp.gravity=ForgetGravity;
+    
+    if(extl_table_is_bool_set(t, "switchto")){
+        ap.switchto_set=TRUE;
+        ap.switchto=TRUE;
+    }
+    
+    if(extl_table_gets_t(t, "geom", &gt)){
+        int pos=0, size=0;
+        
+        fp.inner_geom.x=0;
+        fp.inner_geom.y=0;
+
+        if(extl_table_gets_i(gt, "x", &(ap.geom.x)))
+            pos++;
+        if(extl_table_gets_i(gt, "y", &(ap.geom.y)))
+            pos++;
+    
+        if(extl_table_gets_i(gt, "w", &(ap.geom.w)))
+            size++;
+        if(extl_table_gets_i(gt, "h", &(ap.geom.h)))
+            size++;
+        
+        fp.inner_geom.w=maxof(fp.inner_geom.w, 1);
+        fp.inner_geom.h=maxof(fp.inner_geom.h, 1);
+        
+        fp.inner_geom_gravity_set=(size==2 && pos==2);
+        
+        extl_unref_table(gt);
+    }
+    
+    return groupws_attach_framed(ws, &ap, &fp, reg);
+}
+
+
+/*}}}*/
+
+
+/*{{{ groupws_prepare_manage */
+
+
+#define REG_OK(R) OBJ_IS(R, WMPlex)
+
+
+static WMPlex *find_existing(WGroupWS *ws)
+{
+    WGroupIterTmp tmp;
+    WRegion *r=(ws->grp.current_managed!=NULL 
+                ? ws->grp.current_managed->reg 
+                : NULL);
+    
+    if(r!=NULL && REG_OK(r))
+        return (WMPlex*)r;
+    
+    FOR_ALL_MANAGED_BY_GROUP(&ws->grp, r, tmp){
+        if(REG_OK(r))
+            return (WMPlex*)r;
+    }
+    
+    return NULL;
+}
+
+
+static WPHolder *groupws_do_prepare_manage(WGroupWS *ws, 
+                                           const WClientWin *cwin,
+                                           const WManageParams *param, 
+                                           int redir, int geom_weak)
+{
+    WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
+    WFramedParam fp=FRAMEDPARAM_INIT;
+    WPHolder *ph;
+    
+    if(redir==MANAGE_REDIR_PREFER_YES){
+        WMPlex *m=find_existing(ws);
+        if(m!=NULL){
+            WPHolder *ph;
+            ph=region_prepare_manage((WRegion*)m, cwin, param,
+                                     MANAGE_REDIR_STRICT_YES);
+            if(ph!=NULL)
+                return ph;
+        }
+    }
+    
+    if(redir==MANAGE_REDIR_STRICT_YES)
+        return NULL;
+
+    fp.inner_geom_gravity_set=TRUE;
+    fp.inner_geom=param->geom;
+    fp.gravity=param->gravity;
+    
+    ap.geom_weak_set=1;
+    ap.geom_weak=geom_weak;
+
+    ph=(WPHolder*)create_grouppholder(&ws->grp, NULL, &ap);
+    
+    if(ph!=NULL)
+        ph=pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph);
+    
+    if(ph!=NULL)
+        ph=pholder_either((WPHolder*)create_groupedpholder((WPHolder*)ph), ph);
+    
+    return ph;
+}
+
+
+WPHolder *groupws_prepare_manage(WGroupWS *ws, const WClientWin *cwin,
+                                 const WManageParams *param,
+                                 int redir)
+{
+    WRegion *b=(ws->grp.bottom!=NULL ? ws->grp.bottom->reg : NULL);
+    WPHolder *ph=NULL;
+    bool act_b=(ws->grp.bottom==ws->grp.current_managed);
+    bool always_float, use_bottom;
+    int weak=0;
+    
+    if(param->maprq && ioncore_g.opmode!=IONCORE_OPMODE_INIT
+       && !param->userpos){
+        /* When the window is mapped by application request, position
+         * request is only honoured if the position was given by the user
+         * and in case of a transient (the app may know better where to 
+         * place them) or if we're initialising.
+         */
+        weak=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
+    }
+
+    if(b!=NULL && !HAS_DYN(b, region_prepare_manage))
+        b=NULL;
+    
+    use_bottom=(act_b
+                ? !extl_table_is_bool_set(cwin->proptab, "float")
+                : act_b);
+    
+    if(b!=NULL && use_bottom)
+        ph=region_prepare_manage(b, cwin, param, redir);
+    
+    if(ph==NULL)
+        ph=groupws_do_prepare_manage(ws, cwin, param, redir, weak);
+
+    if(ph==NULL && b!=NULL && !use_bottom)
+        ph=region_prepare_manage(b, cwin, param, redir);
+    
+    return ph;
+}
+
+
+WPHolder *groupws_prepare_manage_transient(WGroupWS *ws, const WClientWin *cwin,
+                                           const WManageParams *param,
+                                           int unused)
+{
+    WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
+    WFramedParam fp=FRAMEDPARAM_INIT;
+    WPHolder *ph;
+    
+    ap.stack_above=OBJ_CAST(REGION_PARENT(param->tfor), WRegion);
+    if(ap.stack_above==NULL)
+        return NULL;
+    
+    fp.inner_geom_gravity_set=TRUE;
+    fp.inner_geom=param->geom;
+    fp.gravity=param->gravity;
+    fp.mkframe=create_transient_frame;
+    
+    ap.geom_weak_set=1;
+    ap.geom_weak=0;
+
+    ph=(WPHolder*)create_grouppholder(&ws->grp, NULL, &ap);
+    
+    return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph);
+}
+
+
+WPHolder *groupws_get_rescue_pholder_for(WGroupWS *ws, 
+                                         WRegion *forwhat)
+{
+    WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
+    WFramedParam fp=FRAMEDPARAM_INIT;
+    WPHolder *ph;
+    
+    ap.geom_set=TRUE;
+    ap.geom=REGION_GEOM(forwhat);
+
+    ap.geom_weak_set=1;
+    ap.geom_weak=(REGION_PARENT(forwhat)!=REGION_PARENT(ws)
+                  ? REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y
+                  : 0);
+
+    ph=(WPHolder*)create_grouppholder(&ws->grp, NULL, &ap);
+    
+    return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph);
+}
+
+
+/*}}}*/
+
+
+/*{{{ WGroupWS class */
+
+
+bool groupws_init(WGroupWS *ws, WWindow *parent, const WFitParams *fp)
+{
+    if(!group_init(&(ws->grp), parent, fp))
+        return FALSE;
+
+    ((WRegion*)ws)->flags|=REGION_GRAB_ON_PARENT;
+    
+    region_add_bindmap((WRegion*)ws, ioncore_groupws_bindmap);
+    
+    return TRUE;
+}
+
+
+WGroupWS *create_groupws(WWindow *parent, const WFitParams *fp)
+{
+    CREATEOBJ_IMPL(WGroupWS, groupws, (p, parent, fp));
+}
+
+
+void groupws_deinit(WGroupWS *ws)
+{    
+    group_deinit(&(ws->grp));
+}
+
+
+WRegion *groupws_load(WWindow *par, const WFitParams *fp, 
+                       ExtlTab tab)
+{
+    WGroupWS *ws;
+    
+    ws=create_groupws(par, fp);
+    
+    if(ws==NULL)
+        return NULL;
+
+    group_do_load(&ws->grp, tab);
+    
+    return (WRegion*)ws;
+}
+
+
+WRegion *groupws_load_default(WWindow *par, const WFitParams *fp)
+{
+    return groupws_load(par, fp, (default_ws_params_set
+                                  ? default_ws_params
+                                  : extl_table_none()));
+}
+
+
+static DynFunTab groupws_dynfuntab[]={
+    {(DynFun*)region_prepare_manage, 
+     (DynFun*)groupws_prepare_manage},
+    
+    {(DynFun*)region_prepare_manage_transient,
+     (DynFun*)groupws_prepare_manage_transient},
+    
+    {(DynFun*)region_handle_drop,
+     (DynFun*)groupws_handle_drop},
+    
+    {(DynFun*)region_get_rescue_pholder_for,
+     (DynFun*)groupws_get_rescue_pholder_for},
+    
+    {region_manage_stdisp,
+     group_manage_stdisp},
+    
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WGroupWS, WGroup, groupws_deinit, groupws_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/ioncore/group-ws.h b/ioncore/group-ws.h
new file mode 100644 (file)
index 0000000..cfc43a9
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * ion/ioncore/groupws.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_GROUPWS_H
+#define ION_IONCORE_GROUPWS_H
+
+#include <ioncore/common.h>
+#include <ioncore/rectangle.h>
+#include <ioncore/group.h>
+#include "classes.h"
+
+
+DECLCLASS(WGroupWS){
+    WGroup grp;
+};
+
+
+extern WPHolder *groupws_prepare_manage(WGroupWS *ws, 
+                                        const WClientWin *cwin,
+                                        const WManageParams *param, 
+                                        int redir);
+
+extern WPHolder *groupws_prepare_manage_transient(WGroupWS *ws, 
+                                                  const WClientWin *cwin,
+                                                  const WManageParams *param,
+                                                  int unused);
+
+extern bool groupws_handle_drop(WGroupWS *ws, int x, int y,
+                                WRegion *dropped);
+
+extern WGroupWS *create_groupws(WWindow *parent, const WFitParams *fp);
+extern bool groupws_init(WGroupWS *ws, WWindow *parent, const WFitParams *fp);
+extern void groupws_deinit(WGroupWS *ws);
+
+extern WRegion *groupws_load_default(WWindow *par, const WFitParams *fp);
+extern WRegion *groupws_load(WWindow *par, const WFitParams *fp, ExtlTab tab);
+
+extern void ioncore_groupws_set(ExtlTab tab);
+extern void ioncore_groupws_get(ExtlTab t);
+
+#endif /* ION_IONCORE_GROUPWS_H */
diff --git a/ioncore/group.c b/ioncore/group.c
new file mode 100644 (file)
index 0000000..591fcd7
--- /dev/null
@@ -0,0 +1,1361 @@
+/*
+ * ion/ioncore/group.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/minmax.h>
+#include <libtu/objp.h>
+#include <libmainloop/defer.h>
+
+#include "common.h"
+#include "rootwin.h"
+#include "focus.h"
+#include "global.h"
+#include "region.h"
+#include "manage.h"
+#include "screen.h"
+#include "names.h"
+#include "saveload.h"
+#include "attach.h"
+#include "regbind.h"
+#include "extlconv.h"
+#include "xwindow.h"
+#include "resize.h"
+#include "stacking.h"
+#include "sizepolicy.h"
+#include "bindmaps.h"
+#include "navi.h"
+#include "sizehint.h"
+#include "llist.h"
+#include "mplex.h"
+#include "group.h"
+#include "grouppholder.h"
+#include "frame.h"
+#include "float-placement.h"
+
+
+static void group_place_stdisp(WGroup *ws, WWindow *parent,
+                                 int pos, WRegion *stdisp);
+
+
+
+/*{{{ Stacking list stuff */
+
+
+WStacking *group_get_stacking(WGroup *ws)
+{
+    WWindow *par=REGION_PARENT(ws);
+    
+    return (par==NULL 
+            ? NULL
+            : window_get_stacking(par));
+}
+
+
+WStacking **group_get_stackingp(WGroup *ws)
+{
+    WWindow *par=REGION_PARENT(ws);
+    
+    return (par==NULL 
+            ? NULL
+            : window_get_stackingp(par));
+}
+
+
+static bool wsfilt(WStacking *st, void *ws)
+{
+    return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)ws);
+}
+
+
+static bool wsfilt_nostdisp(WStacking *st, void *ws)
+{
+    return (wsfilt(st, ws) && ((WGroup*)ws)->managed_stdisp!=st);
+}
+
+
+void group_iter_init(WGroupIterTmp *tmp, WGroup *ws)
+{
+    stacking_iter_mgr_init(tmp, ws->managed_list, NULL, ws);
+}
+
+
+void group_iter_init_nostdisp(WGroupIterTmp *tmp, WGroup *ws)
+{
+    stacking_iter_mgr_init(tmp, ws->managed_list, wsfilt_nostdisp, ws);
+}
+
+
+WRegion *group_iter(WGroupIterTmp *tmp)
+{
+    return stacking_iter_mgr(tmp);
+}
+
+
+WStacking *group_iter_nodes(WGroupIterTmp *tmp)
+{
+    return stacking_iter_mgr_nodes(tmp);
+}
+
+
+WGroupIterTmp group_iter_default_tmp;
+
+
+/*}}}*/
+
+
+/*{{{ region dynfun implementations */
+
+
+static void group_fit(WGroup *ws, const WRectangle *geom)
+{
+    REGION_GEOM(ws)=*geom;
+}
+
+
+bool group_fitrep(WGroup *ws, WWindow *par, const WFitParams *fp)
+{
+    WGroupIterTmp tmp;
+    WStacking *unweaved=NULL;
+    int xdiff=0, ydiff=0;
+    WStacking *st;
+    WWindow *oldpar;
+    WRectangle g;
+    
+    oldpar=REGION_PARENT(ws);
+    
+    if(par==NULL){
+        if(fp->mode&REGION_FIT_WHATEVER)
+            return TRUE;
+        REGION_GEOM(ws)=fp->g;
+    }else{
+        if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
+            return FALSE;
+        
+        if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL)
+            region_detach_manager(ws->managed_stdisp->reg);
+        
+        assert(ws->managed_stdisp==NULL);
+        
+        xdiff=fp->g.x-REGION_GEOM(ws).x;
+        ydiff=fp->g.y-REGION_GEOM(ws).y;
+    
+        region_unset_parent((WRegion*)ws);
+        XReparentWindow(ioncore_g.dpy, ws->dummywin, par->win, -1, -1);
+        region_set_parent((WRegion*)ws, par);
+
+        REGION_GEOM(ws).x=fp->g.x;
+        REGION_GEOM(ws).y=fp->g.y;
+        if(!(fp->mode&REGION_FIT_WHATEVER)){
+            REGION_GEOM(ws).w=fp->g.w;
+            REGION_GEOM(ws).h=fp->g.h;
+        }
+        
+        if(oldpar!=NULL)
+            unweaved=stacking_unweave(&oldpar->stacking, wsfilt, (void*)ws);
+    }
+
+    FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
+        WFitParams fp2=*fp;
+        
+        if(st->reg==NULL)
+            continue;
+        
+        g=REGION_GEOM(st->reg);
+        g.x+=xdiff;
+        g.y+=ydiff;
+
+        if(fp->mode&REGION_FIT_WHATEVER){
+            fp2.g=g;
+        }else{
+            fp2.g=REGION_GEOM(ws);
+            sizepolicy(&st->szplcy, st->reg, &g, REGION_RQGEOM_WEAK_ALL, &fp2);
+        }
+        
+        if(!region_fitrep(st->reg, par, &fp2)){
+            warn(TR("Error reparenting %s."), region_name(st->reg));
+            region_detach_manager(st->reg);
+        }
+    }
+    
+    if(unweaved!=NULL)
+        stacking_weave(&par->stacking, &unweaved, FALSE);
+
+    return TRUE;
+}
+
+
+static void group_map(WGroup *ws)
+{
+    WRegion *reg;
+    WGroupIterTmp tmp;
+
+    REGION_MARK_MAPPED(ws);
+    XMapWindow(ioncore_g.dpy, ws->dummywin);
+    
+    FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
+        region_map(reg);
+    }
+}
+
+
+static void group_unmap(WGroup *ws)
+{
+    WRegion *reg;
+    WGroupIterTmp tmp;
+
+    REGION_MARK_UNMAPPED(ws);
+    XUnmapWindow(ioncore_g.dpy, ws->dummywin);
+
+    FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
+        region_unmap(reg);
+    }
+}
+
+
+static WStacking *find_to_focus(WGroup *ws, WStacking *st, bool group_only)
+{
+    WStacking *stacking=group_get_stacking(ws);
+    
+    if(stacking==NULL)
+        return st;
+                                
+    return stacking_find_to_focus_mapped(stacking, st, 
+                                         (group_only ? (WRegion*)ws : NULL));
+}
+
+
+static bool group_refocus_(WGroup *ws, WStacking *st)
+{
+    if(st!=ws->current_managed && st->reg!=NULL){
+        if(region_may_control_focus((WRegion*)ws))
+            region_set_focus(st->reg);
+        else
+            ws->current_managed=st;
+        return TRUE;
+    }
+    
+    return FALSE;
+}
+
+
+static void group_do_set_focus(WGroup *ws, bool warp)
+{
+    WStacking *st=ws->current_managed;
+    
+    if(st==NULL || st->reg==NULL)
+        st=find_to_focus(ws, NULL, TRUE);
+    
+    if(st!=NULL && st->reg!=NULL)
+        region_do_set_focus(st->reg, warp);
+    else
+        region_finalise_focusing((WRegion*)ws, ws->dummywin, warp);
+}
+
+
+static bool group_managed_prepare_focus(WGroup *ws, WRegion *reg, 
+                                        int flags, WPrepareFocusResult *res)
+{
+    WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
+    WStacking *st=group_find_stacking(ws, reg);    
+    
+    if(st==NULL)
+        return FALSE;
+
+    if(mplex!=NULL){
+        WStacking *node=mplex_find_stacking(mplex, (WRegion*)ws);
+        
+        if(node==NULL)
+            return FALSE;
+        
+        return mplex_do_prepare_focus(mplex, node, st,
+                                      flags, res);
+    }else{
+        WStacking *stacking;
+        
+        if(!region_prepare_focus((WRegion*)ws, flags, res))
+            return FALSE;
+
+        stacking=group_get_stacking(ws);
+        st=find_to_focus(ws, st, FALSE);
+        
+#warning "TODO: raise in some cases (not enter-window)?"
+        
+        if(st==NULL)
+            return FALSE;
+        
+        res->reg=st->reg;
+        res->flags=flags;
+        
+        return (res->reg==reg);
+    }
+}
+
+
+static bool group_essentially_empty(WGroup *ws)
+{
+    WGroupIterTmp tmp;
+    WStacking *st;
+    
+    FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
+        if(st!=ws->managed_stdisp)
+            return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+void group_managed_remove(WGroup *ws, WRegion *reg)
+{
+    bool mcf=region_may_control_focus((WRegion*)ws);
+    bool ds=OBJ_IS_BEING_DESTROYED(ws);
+    WStacking *st, *next_st=NULL;
+    bool was_stdisp=FALSE, was_bottom=FALSE;
+    bool dest=FALSE;
+    bool cur=FALSE;
+    
+    st=group_find_stacking(ws, reg);
+
+    if(st!=NULL){
+        next_st=stacking_unstack(REGION_PARENT(ws), st);
+        
+        UNLINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
+        
+        if(st==ws->managed_stdisp){
+            ws->managed_stdisp=NULL;
+            was_stdisp=TRUE;
+        }
+        
+        if(st==ws->bottom){
+            ws->bottom=NULL;
+            was_bottom=TRUE;
+            if(ws->bottom_last_close && group_essentially_empty(ws))
+                dest=TRUE;
+        }
+            
+        if(st==ws->current_managed){
+            cur=TRUE;
+            ws->current_managed=NULL;
+        }
+        
+        stacking_unassoc(st);
+        stacking_free(st);
+    }
+    
+    region_unset_manager(reg, (WRegion*)ws);
+
+    if(!dest && !ds){
+        if(was_bottom && !was_stdisp && ws->managed_stdisp==NULL){
+            /* We should probably be managing any stdisp, that 'bottom' 
+             * was managing.
+             */
+            WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
+            
+            if(mplex!=NULL 
+               && mplex->mx_current!=NULL 
+               && mplex->mx_current->st->reg==(WRegion*)ws){
+                mplex_remanage_stdisp(mplex);
+            }
+        }
+        
+        if(cur){
+            WStacking *stf=find_to_focus(ws, next_st, TRUE);
+            if(stf!=NULL)
+                region_warp(stf->reg);
+        }
+    }else if(dest && !ds){
+        mainloop_defer_destroy((Obj*)ws);
+    }
+}
+
+
+static void group_managed_activated(WGroup *ws, WRegion *reg)
+{
+    ws->current_managed=group_find_stacking(ws, reg);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Create/destroy */
+
+
+bool group_init(WGroup *ws, WWindow *par, const WFitParams *fp)
+{
+    ws->current_managed=NULL;
+    ws->managed_stdisp=NULL;
+    ws->bottom=NULL;
+    ws->managed_list=NULL;
+    
+    ws->dummywin=XCreateWindow(ioncore_g.dpy, par->win,
+                                fp->g.x, fp->g.y, 1, 1, 0,
+                                CopyFromParent, InputOnly,
+                                CopyFromParent, 0, NULL);
+    if(ws->dummywin==None)
+        return FALSE;
+
+    region_init(&ws->reg, par, fp);
+    region_register(&ws->reg);
+
+    XSelectInput(ioncore_g.dpy, ws->dummywin,
+                 FocusChangeMask|KeyPressMask|KeyReleaseMask|
+                 ButtonPressMask|ButtonReleaseMask);
+    XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
+                 (XPointer)ws);
+    
+    ((WRegion*)ws)->flags|=REGION_GRAB_ON_PARENT;
+    
+    region_add_bindmap((WRegion*)ws, ioncore_group_bindmap);
+    
+    return TRUE;
+}
+
+
+WGroup *create_group(WWindow *par, const WFitParams *fp)
+{
+    CREATEOBJ_IMPL(WGroup, group, (p, par, fp));
+}
+
+
+void group_deinit(WGroup *ws)
+{
+    WGroupIterTmp tmp;
+    WRegion *reg;
+
+    if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL){
+        group_managed_remove(ws, ws->managed_stdisp->reg);
+        assert(ws->managed_stdisp==NULL);
+    }
+
+    FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
+        destroy_obj((Obj*)reg);
+    }
+
+    assert(ws->managed_list==NULL);
+
+    XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
+    XDestroyWindow(ioncore_g.dpy, ws->dummywin);
+    ws->dummywin=None;
+
+    region_deinit(&ws->reg);
+}
+
+
+    
+bool group_rescue_clientwins(WGroup *ws, WPHolder *ph)
+{
+    WGroupIterTmp tmp;
+    
+    group_iter_init_nostdisp(&tmp, ws);
+    
+    return region_rescue_some_clientwins((WRegion*)ws, ph,
+                                         (WRegionIterator*)group_iter,
+                                         &tmp);
+}
+
+
+bool group_may_destroy(WGroup *ws)
+{
+    bool ret=group_essentially_empty(ws);
+    if(!ret)
+        warn(TR("Workspace not empty - refusing to destroy."));
+    return ret;
+}
+
+
+static bool group_managed_may_destroy(WGroup *ws, WRegion *reg)
+{
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ attach */
+
+
+WStacking *group_do_add_managed(WGroup *ws, WRegion *reg, int level,
+                                WSizePolicy szplcy)
+{
+    WStacking *st=NULL;
+    CALL_DYN_RET(st, WStacking*, group_do_add_managed, ws, 
+                 (ws, reg, level, szplcy));
+    return st;
+}
+    
+
+WStacking *group_do_add_managed_default(WGroup *ws, WRegion *reg, int level,
+                                        WSizePolicy szplcy)
+{
+    WStacking *st=NULL, *tmp=NULL;
+    Window bottom=None, top=None;
+    WStacking **stackingp=group_get_stackingp(ws);
+    WFrame *frame;
+    
+    if(stackingp==NULL)
+        return NULL;
+    
+    st=create_stacking();
+    
+    if(st==NULL)
+        return NULL;
+    
+    if(!stacking_assoc(st, reg)){
+        stacking_free(st);
+        return  NULL;
+    }
+    
+    frame=OBJ_CAST(reg, WFrame);
+    if(frame!=NULL){
+        WFrameMode m=frame_mode(frame);
+        if(m!=FRAME_MODE_FLOATING && m!=FRAME_MODE_TRANSIENT)
+            frame_set_mode(frame, FRAME_MODE_FLOATING);
+    }
+
+    st->level=level;
+    st->szplcy=szplcy;
+
+    LINK_ITEM_FIRST(tmp, st, next, prev);
+    stacking_weave(stackingp, &tmp, FALSE);
+    assert(tmp==NULL);
+
+    LINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
+    region_set_manager(reg, (WRegion*)ws);
+
+    if(region_is_fully_mapped((WRegion*)ws))
+        region_map(reg);
+
+    return st;
+}
+
+
+static void geom_group_to_parent(WGroup *ws, const WRectangle *g, 
+                                 WRectangle *wg)
+{
+    wg->x=g->x+REGION_GEOM(ws).x;
+    wg->y=g->y+REGION_GEOM(ws).y;
+    wg->w=maxof(1, g->w);
+    wg->h=maxof(1, g->h);
+}
+
+
+bool group_do_attach_final(WGroup *ws, 
+                           WRegion *reg,
+                           const WGroupAttachParams *param)
+{
+    WStacking *st, *stabove=NULL;
+    WSizePolicy szplcy;
+    WFitParams fp;
+    WRectangle g;
+    uint level;
+    int weak;
+    bool sw;
+    
+    /* Fit */
+    szplcy=(param->szplcy_set
+            ? param->szplcy
+            : (param->bottom
+               ? SIZEPOLICY_FULL_EXACT
+               : SIZEPOLICY_UNCONSTRAINED));
+    
+    weak=(param->geom_weak_set
+          ? param->geom_weak
+          : (param->geom_set
+             ? 0
+             : REGION_RQGEOM_WEAK_ALL));
+    
+    if(param->geom_set)
+        geom_group_to_parent(ws, &param->geom, &g);
+    else
+        g=REGION_GEOM(reg);
+    
+    /* If the requested geometry does not overlap the workspaces's geometry, 
+     * position request is never honoured.
+     */
+    if((g.x+g.w<=REGION_GEOM(ws).x) ||
+       (g.y+g.h<=REGION_GEOM(ws).y) ||
+       (g.x>=REGION_GEOM(ws).x+REGION_GEOM(ws).w) ||
+       (g.y>=REGION_GEOM(ws).y+REGION_GEOM(ws).h)){
+        weak|=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_X;
+    }
+
+    if((weak&(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y))
+       ==(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y) &&
+        (szplcy==SIZEPOLICY_UNCONSTRAINED ||
+         szplcy==SIZEPOLICY_FREE ||
+         szplcy==SIZEPOLICY_FREE_GLUE /* without flags */)){
+        group_calc_placement(ws, &g);
+    }
+
+    fp.g=REGION_GEOM(ws);
+    fp.mode=REGION_FIT_EXACT;
+
+    sizepolicy(&szplcy, reg, &g, weak, &fp);
+
+    if(rectangle_compare(&fp.g, &REGION_GEOM(reg))!=RECTANGLE_SAME)
+        region_fitrep(reg, NULL, &fp);
+    
+    /* Stacking & add */
+    if(param->stack_above!=NULL)
+        stabove=group_find_stacking(ws, param->stack_above);
+
+    level=(stabove!=NULL
+           ? stabove->level
+           : (param->level_set 
+              ? param->level 
+              : STACKING_LEVEL_NORMAL));
+
+    st=group_do_add_managed(ws, reg, level, szplcy);
+    
+    if(st==NULL)
+        return FALSE;
+    
+    if(stabove!=NULL)
+        st->above=stabove;
+
+    /* Misc. */
+    if(param->bottom){
+        ws->bottom=st;
+        
+        if(HAS_DYN(reg, region_manage_stdisp) && ws->managed_stdisp!=NULL){
+            WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
+            if(mplex!=NULL){ /* should always hold */
+                WMPlexSTDispInfo di;
+                WRegion *stdisp=NULL;
+                mplex_get_stdisp(mplex, &stdisp, &di);
+                if(stdisp!=NULL){
+                    assert(stdisp==ws->managed_stdisp->reg);
+                    /* WARNING! Calls back to group code (managed_remove). */
+                    region_manage_stdisp(reg, stdisp, &di);
+                }
+            }
+        }
+    }
+    
+    sw=(param->switchto_set ? param->switchto : ioncore_g.switchto_new);
+    
+    if(sw || st->level>=STACKING_LEVEL_MODAL1){
+        WStacking *stf=find_to_focus(ws, st, FALSE);
+        
+        if(stf==st){
+            /* Ok, the new region can be focused */
+            group_refocus_(ws, stf);
+        }
+    }
+    
+    return TRUE;
+}
+
+
+WRegion *group_do_attach(WGroup *ws, 
+                         /*const*/ WGroupAttachParams *param,
+                         WRegionAttachData *data)
+{
+    WFitParams fp;
+    WWindow *par;
+    WRegion *reg;
+
+    if(ws->bottom!=NULL && param->bottom){
+        warn(TR("'bottom' already set."));
+        return NULL;
+    }
+    
+    par=REGION_PARENT(ws);
+    assert(par!=NULL);
+
+    if(param->geom_set){
+        geom_group_to_parent(ws, &param->geom, &fp.g);
+        fp.mode=REGION_FIT_EXACT;
+    }else{
+        fp.g=REGION_GEOM(ws);
+        fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
+    }
+
+    return region_attach_helper((WRegion*) ws, par, &fp, 
+                                (WRegionDoAttachFn*)group_do_attach_final,
+                                /*(const WRegionAttachParams*)*/param, data);
+    /*                            ^^^^ doesn't seem to work. */
+}
+
+
+static void get_params(WGroup *ws, ExtlTab tab, WGroupAttachParams *par)
+{
+    int tmp;
+    char *tmps;
+    ExtlTab g;
+    
+    par->switchto_set=0;
+    par->level_set=0;
+    par->szplcy_set=0;
+    par->geom_set=0;
+    par->bottom=0;
+    
+    if(extl_table_gets_i(tab, "level", &tmp)){
+        if(tmp>=0){
+            par->level_set=STACKING_LEVEL_NORMAL;
+            par->level=tmp;
+        }
+    }
+    
+    if(extl_table_is_bool_set(tab, "bottom")){
+        par->level=STACKING_LEVEL_BOTTOM;
+        par->level_set=1;
+        par->bottom=1;
+    }
+    
+    if(!par->level_set && extl_table_is_bool_set(tab, "modal")){
+        par->level=STACKING_LEVEL_MODAL1;
+        par->level_set=1;
+    }
+    
+    if(extl_table_is_bool_set(tab, "switchto"))
+        par->switchto=1;
+
+    if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
+        par->szplcy_set=1;
+        par->szplcy=tmp;
+    }else if(extl_table_gets_s(tab, "sizepolicy", &tmps)){
+        if(string2sizepolicy(tmps, &par->szplcy))
+            par->szplcy_set=1;
+        free(tmps);
+    }
+    
+    if(extl_table_gets_t(tab, "geom", &g)){
+        int n=0;
+        
+        if(extl_table_gets_i(g, "x", &(par->geom.x)))
+            n++;
+        if(extl_table_gets_i(g, "y", &(par->geom.y)))
+            n++;
+        if(extl_table_gets_i(g, "w", &(par->geom.w)))
+            n++;
+        if(extl_table_gets_i(g, "h", &(par->geom.h)))
+            n++;
+        
+        if(n==4)
+            par->geom_set=1;
+        
+        extl_unref_table(g);
+    }
+}
+
+
+
+/*EXTL_DOC
+ * Attach and reparent existing region \var{reg} to \var{ws}.
+ * The table \var{param} may contain the fields \var{index} and
+ * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
+ */
+EXTL_EXPORT_MEMBER
+WRegion *group_attach(WGroup *ws, WRegion *reg, ExtlTab param)
+{
+    WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
+    WRegionAttachData data;
+
+    if(reg==NULL)
+        return NULL;
+    
+    get_params(ws, param, &par);
+    
+    data.type=REGION_ATTACH_REPARENT;
+    data.u.reg=reg;
+    
+    return group_do_attach(ws, &par, &data);
+}
+
+
+/*EXTL_DOC
+ * Create a new region to be managed by \var{ws}. At least the following
+ * fields in \var{param} are understood:
+ * 
+ * \begin{tabularx}{\linewidth}{lX}
+ *  \tabhead{Field & Description}
+ *  \var{type} & Class name (a string) of the object to be created. Mandatory. \\
+ *  \var{name} & Name of the object to be created (a string). Optional. \\
+ *  \var{switchto} & Should the region be switched to (boolean)? Optional. \\
+ *  \var{level} & Stacking level; default is 1. \\
+ *  \var{modal} & Make object modal; ignored if level is set. \\
+ *  \var{sizepolicy} & Size policy. \\
+ * \end{tabularx}
+ * 
+ * In addition parameters to the region to be created are passed in this 
+ * same table.
+ */
+EXTL_EXPORT_MEMBER
+WRegion *group_attach_new(WGroup *ws, ExtlTab param)
+{
+    WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
+    WRegionAttachData data;
+
+    get_params(ws, param, &par);
+    
+    data.type=REGION_ATTACH_LOAD;
+    data.u.tab=param;
+    
+    return group_do_attach(ws, &par, &data);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Status display support */
+
+
+static int stdisp_szplcy(const WMPlexSTDispInfo *di, WRegion *stdisp)
+{
+    int pos=di->pos;
+    
+    if(di->fullsize){
+        if(region_orientation(stdisp)==REGION_ORIENTATION_VERTICAL){
+            if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_BL)
+                return SIZEPOLICY_STRETCH_LEFT;
+            else
+                return SIZEPOLICY_STRETCH_RIGHT;
+        }else{
+            if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_TR)
+                return SIZEPOLICY_STRETCH_TOP;
+            else
+                return SIZEPOLICY_STRETCH_BOTTOM;
+        }
+    }else{
+        if(pos==MPLEX_STDISP_TL)
+            return SIZEPOLICY_GRAVITY_NORTHWEST;
+        else if(pos==MPLEX_STDISP_BL)
+            return SIZEPOLICY_GRAVITY_SOUTHWEST;
+        else if(pos==MPLEX_STDISP_TR)
+            return SIZEPOLICY_GRAVITY_NORTHEAST;
+        else /*if(pos=MPLEX_STDISP_BR)*/
+            return SIZEPOLICY_GRAVITY_SOUTHEAST;
+    }
+}
+
+
+void group_manage_stdisp(WGroup *ws, WRegion *stdisp, 
+                         const WMPlexSTDispInfo *di)
+{
+    WFitParams fp;
+    uint szplcy;
+    WRegion *b=(ws->bottom==NULL ? NULL : ws->bottom->reg);
+    
+    /* Check if 'bottom' wants to manage the stdisp. */
+    if(b!=NULL 
+       && !OBJ_IS_BEING_DESTROYED(b)
+       && HAS_DYN(b, region_manage_stdisp)){
+        region_manage_stdisp(b, stdisp, di);
+        if(REGION_MANAGER(stdisp)==b)
+            return;
+    }
+    
+    /* No. */
+    
+    szplcy=stdisp_szplcy(di, stdisp)|SIZEPOLICY_SHRUNK;
+    
+    if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg==stdisp){
+        if(ws->managed_stdisp->szplcy==szplcy)
+            return;
+        ws->managed_stdisp->szplcy=szplcy;
+    }else{
+        region_detach_manager(stdisp);
+        ws->managed_stdisp=group_do_add_managed(ws, stdisp, 
+                                                STACKING_LEVEL_ON_TOP, 
+                                                szplcy);
+    }
+
+    fp.g=REGION_GEOM(ws);
+    sizepolicy(&ws->managed_stdisp->szplcy, stdisp, NULL, 0, &fp);
+
+    region_fitrep(stdisp, NULL, &fp);
+}
+
+
+void group_managed_rqgeom(WGroup *ws, WRegion *reg,
+                          const WRQGeomParams *rq,
+                          WRectangle *geomret)
+{
+    WFitParams fp;
+    WStacking *st;
+        
+    st=group_find_stacking(ws, reg);
+
+    if(st==NULL){
+        fp.g=rq->geom;
+        fp.mode=REGION_FIT_EXACT;
+    }else{
+        fp.g=REGION_GEOM(ws);
+        sizepolicy(&st->szplcy, reg, &rq->geom, rq->flags, &fp);
+    }
+    
+    if(geomret!=NULL)
+        *geomret=fp.g;
+    
+    if(!(rq->flags&REGION_RQGEOM_TRYONLY))
+        region_fitrep(reg, NULL, &fp);
+}
+
+
+void group_managed_rqgeom_absolute(WGroup *grp, WRegion *sub, 
+                                   const WRQGeomParams *rq,
+                                   WRectangle *geomret)
+{
+    if(grp->bottom!=NULL && grp->bottom->reg==sub){
+        region_rqgeom((WRegion*)grp, rq, geomret);
+        if(!(rq->flags&REGION_RQGEOM_TRYONLY) && geomret!=NULL)
+            *geomret=REGION_GEOM(sub);
+    }else{
+        WRQGeomParams rq2=*rq;
+        rq2.flags&=~REGION_RQGEOM_ABSOLUTE;
+
+        region_managed_rqgeom((WRegion*)grp, sub, &rq2, geomret);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Navigation */
+
+
+static WStacking *nxt(WGroup *ws, WStacking *st, bool wrap)
+{
+    return (st->mgr_next!=NULL 
+            ? st->mgr_next 
+            : (wrap ? ws->managed_list : NULL));
+}
+
+
+static WStacking *prv(WGroup *ws, WStacking *st, bool wrap)
+{
+    return (st!=ws->managed_list 
+            ? st->mgr_prev
+            : (wrap ? st->mgr_prev : NULL));
+}
+
+
+typedef WStacking *NxtFn(WGroup *ws, WStacking *st, bool wrap);
+
+
+static bool mapped_filt(WStacking *st, void *unused)
+{
+    return (st->reg!=NULL && REGION_IS_MAPPED(st->reg));
+}
+
+
+static bool focusable(WGroup *ws, WStacking *st, uint min_level)
+{
+    return (st->reg!=NULL
+            && REGION_IS_MAPPED(st->reg)
+            && !(st->reg->flags&REGION_SKIP_FOCUS)
+            && st->level>=min_level);
+}
+
+       
+static WStacking *do_get_next(WGroup *ws, WStacking *sti,
+                              NxtFn *fn, bool wrap, bool sti_ok)
+{
+    WStacking *st, *stacking;
+    uint min_level=0;
+    
+    stacking=group_get_stacking(ws);
+    
+    if(stacking!=NULL)
+        min_level=stacking_min_level(stacking, mapped_filt, NULL);
+
+    st=sti;
+    while(1){
+        st=fn(ws, st, wrap); 
+        
+        if(st==NULL || st==sti)
+            break;
+        
+        if(focusable(ws, st, min_level))
+            return st;
+    }
+
+    if(sti_ok && focusable(ws, sti, min_level))
+        return st;
+    
+    return NULL;
+}
+
+
+static WStacking *group_do_navi_first(WGroup *ws, WRegionNavi nh)
+{
+    WStacking *lst=ws->managed_list;
+    
+    if(lst==NULL)
+        return NULL;
+
+    if(nh==REGION_NAVI_ANY && 
+       ws->current_managed!=NULL &&
+       ws->current_managed->reg!=NULL){
+        return ws->current_managed;
+    }
+    
+    if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || 
+       nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
+        return do_get_next(ws, lst, prv, TRUE, TRUE);
+    }else{
+        return do_get_next(ws, lst->mgr_prev, nxt, TRUE, TRUE);
+    }
+}
+
+
+static WRegion *group_navi_first(WGroup *ws, WRegionNavi nh,
+                                 WRegionNaviData *data)
+{
+    WStacking *st=group_do_navi_first(ws, nh);
+    
+    return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
+}
+
+
+static WStacking *group_do_navi_next(WGroup *ws, WStacking *st, 
+                                     WRegionNavi nh, bool wrap)
+{
+    if(st==NULL)
+        return group_do_navi_first(ws, nh);
+    
+    if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || 
+       nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
+        return do_get_next(ws, st, nxt, wrap, FALSE);
+    }else{
+        return do_get_next(ws, st, prv, wrap, FALSE);
+    }
+}
+
+static WRegion *group_navi_next(WGroup *ws, WRegion *reg, 
+                                WRegionNavi nh, WRegionNaviData *data)
+{
+    WStacking *st=group_find_stacking(ws, reg);
+    
+    st=group_do_navi_next(ws, st, nh, FALSE);
+    
+    return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Stacking */
+
+
+/* 
+ * Note: Managed objects are considered to be stacked separately from the
+ * group, slightly violating expectations.
+ */
+
+void group_stacking(WGroup *ws, Window *bottomret, Window *topret)
+{
+    Window win=region_xwindow((WRegion*)ws);
+    
+    *bottomret=win;
+    *topret=win;
+}
+
+
+void group_restack(WGroup *ws, Window other, int mode)
+{
+    Window win;
+    
+    win=region_xwindow((WRegion*)ws);
+    if(win!=None){
+        xwindow_restack(win, other, mode);
+        other=win;
+        mode=Above;
+    }
+}
+
+
+WStacking *group_find_stacking(WGroup *ws, WRegion *r)
+{
+    WStacking *st;
+    
+    if(r==NULL || REGION_MANAGER(r)!=(WRegion*)ws)
+        return NULL;
+    
+    return ioncore_find_stacking(r);
+}
+
+
+static WStacking *find_stacking_if_not_on_ws(WGroup *ws, Window w)
+{
+    WRegion *r=xwindow_region_of(w);
+    WStacking *st=NULL;
+    
+    while(r!=NULL){
+        if(REGION_MANAGER(r)==(WRegion*)ws)
+            break;
+        st=group_find_stacking(ws, r);
+        if(st!=NULL)
+            break;
+        r=REGION_MANAGER(r);
+    }
+    
+    return st;
+}
+
+
+bool group_managed_rqorder(WGroup *grp, WRegion *reg, WRegionOrder order)
+{
+    WStacking **stackingp=group_get_stackingp(grp);
+    WStacking *st;
+
+    if(stackingp==NULL || *stackingp==NULL)
+        return FALSE;
+
+    st=group_find_stacking(grp, reg);
+    
+    if(st==NULL)
+        return FALSE;
+    
+    stacking_restack(stackingp, st, None, NULL, NULL,
+                     (order!=REGION_ORDER_FRONT));
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+/*EXTL_DOC
+ * Returns the 'bottom' of \var{ws}.
+ */
+EXTL_EXPORT_MEMBER
+WRegion *group_bottom(WGroup *ws)
+{
+    return (ws->bottom!=NULL ? ws->bottom->reg : NULL);
+}
+
+
+/*EXTL_DOC
+ * Returns a list of regions managed by the workspace (frames, mostly).
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+ExtlTab group_managed_list(WGroup *ws)
+{
+    WGroupIterTmp tmp;
+    group_iter_init(&tmp, ws);
+    
+    return extl_obj_iterable_to_table((ObjIterator*)group_iter, &tmp);
+}
+
+
+WRegion* group_current(WGroup *ws)
+{
+    return (ws->current_managed!=NULL ? ws->current_managed->reg : NULL);
+}
+
+
+void group_size_hints(WGroup *ws, WSizeHints *hints_ret)
+{
+    if(ws->bottom==NULL || ws->bottom->reg==NULL){
+        sizehints_clear(hints_ret);
+    }else{
+        region_size_hints(ws->bottom->reg, hints_ret);
+        hints_ret->no_constrain=TRUE;
+    }
+}
+
+
+Window group_xwindow(const WGroup *ws)
+{
+    return ws->dummywin;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Save/load */
+
+
+static ExtlTab group_get_configuration(WGroup *ws)
+{
+    ExtlTab tab, mgds, subtab, g;
+    WStacking *st;
+    WGroupIterTmp tmp;
+    WMPlex *par;
+    int n=0;
+    WRectangle tmpg;
+    
+    tab=region_get_base_configuration((WRegion*)ws);
+    
+    mgds=extl_create_table();
+    
+    extl_table_sets_t(tab, "managed", mgds);
+    
+    /* TODO: stacking order messed up */
+    
+    FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
+        if(st->reg==NULL)
+            continue;
+        
+        subtab=region_get_configuration(st->reg);
+
+        if(subtab!=extl_table_none()){
+            extl_table_sets_i(subtab, "sizepolicy", st->szplcy);
+            extl_table_sets_i(subtab, "level", st->level);
+        
+            tmpg=REGION_GEOM(st->reg);
+            tmpg.x-=REGION_GEOM(ws).x;
+            tmpg.y-=REGION_GEOM(ws).y;
+            
+            g=extl_table_from_rectangle(&tmpg);
+            extl_table_sets_t(subtab, "geom", g);
+            extl_unref_table(g);
+            
+            if(ws->bottom==st)
+                extl_table_sets_b(subtab, "bottom", TRUE);
+        
+            extl_table_seti_t(mgds, ++n, subtab);
+            extl_unref_table(subtab);
+        }
+    }
+    
+    extl_unref_table(mgds);
+    
+    return tab;
+}
+
+
+void group_do_load(WGroup *ws, ExtlTab tab)
+{
+    ExtlTab substab, subtab;
+    int i, n;
+    
+    if(extl_table_gets_t(tab, "managed", &substab)){
+        n=extl_table_get_n(substab);
+        for(i=1; i<=n; i++){
+            if(extl_table_geti_t(substab, i, &subtab)){
+                group_attach_new(ws, subtab);
+                extl_unref_table(subtab);
+            }
+        }
+        
+        extl_unref_table(substab);
+    }
+
+    ws->bottom_last_close=extl_table_is_bool_set(tab, "bottom_last_close");
+}
+
+
+WRegion *group_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
+{
+    WGroup *ws;
+    
+    ws=create_group(par, fp);
+    
+    if(ws==NULL)
+        return NULL;
+        
+    group_do_load(ws, tab);
+    
+    return (WRegion*)ws;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab group_dynfuntab[]={
+    {(DynFun*)region_fitrep,
+     (DynFun*)group_fitrep},
+
+    {region_map, 
+     group_map},
+    
+    {region_unmap, 
+     group_unmap},
+    
+    {(DynFun*)region_managed_prepare_focus, 
+     (DynFun*)group_managed_prepare_focus},
+
+    {region_do_set_focus, 
+     group_do_set_focus},
+    
+    {region_managed_activated, 
+     group_managed_activated},
+    
+    {region_managed_remove,
+     group_managed_remove},
+    
+    {(DynFun*)region_get_configuration, 
+     (DynFun*)group_get_configuration},
+
+    {(DynFun*)region_may_destroy,
+     (DynFun*)group_may_destroy},
+
+    {(DynFun*)region_managed_may_destroy,
+     (DynFun*)group_managed_may_destroy},
+
+    {(DynFun*)region_current,
+     (DynFun*)group_current},
+    
+    {(DynFun*)region_rescue_clientwins,
+     (DynFun*)group_rescue_clientwins},
+    
+    {region_restack,
+     group_restack},
+
+    {region_stacking,
+     group_stacking},
+
+    {(DynFun*)region_managed_get_pholder,
+     (DynFun*)group_managed_get_pholder},
+
+    {region_managed_rqgeom,
+     group_managed_rqgeom},
+     
+    {region_managed_rqgeom_absolute,
+     group_managed_rqgeom_absolute},
+    
+    {(DynFun*)group_do_add_managed,
+     (DynFun*)group_do_add_managed_default},
+    
+    {region_size_hints,
+     group_size_hints},
+    
+    {(DynFun*)region_xwindow,
+     (DynFun*)group_xwindow},
+    
+    {(DynFun*)region_navi_first,
+     (DynFun*)group_navi_first},
+    
+    {(DynFun*)region_navi_next,
+     (DynFun*)group_navi_next},
+    
+    {(DynFun*)region_managed_rqorder,
+     (DynFun*)group_managed_rqorder},
+    
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WGroup, WRegion, group_deinit, group_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/ioncore/group.h b/ioncore/group.h
new file mode 100644 (file)
index 0000000..0de372a
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * ion/ioncore/group.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_GROUP_H
+#define ION_IONCORE_GROUP_H
+
+#include <libextl/extl.h>
+#include <ioncore/common.h>
+#include <ioncore/region.h>
+#include <ioncore/screen.h>
+#include <ioncore/manage.h>
+#include <ioncore/rectangle.h>
+#include <ioncore/pholder.h>
+#include <ioncore/stacking.h>
+#include <ioncore/framedpholder.h>
+
+
+INTRSTRUCT(WGroupAttachParams);
+
+typedef WRegionSimpleCreateFn WGroupMkFrameFn;
+
+
+DECLSTRUCT(WGroupAttachParams){
+    uint level_set:1;
+    uint szplcy_set:1;
+    uint geom_set:1;
+    uint geom_weak_set:1;
+    uint switchto_set:1;
+    
+    uint switchto:1;
+    uint modal:1;
+    uint bottom:1;
+    
+    int geom_weak;
+    WRectangle geom;
+    
+    uint level;
+    WSizePolicy szplcy;
+    WRegion *stack_above;
+};
+
+#define GROUPATTACHPARAMS_INIT \
+    {0, 0, 0, 0, 0,  0, 0, 0,  0, {0, 0, 0, 0},  0, 0, NULL}
+
+
+DECLCLASS(WGroup){
+    WRegion reg;
+    WStacking *managed_list;
+    WStacking *managed_stdisp;
+    WStacking *current_managed;
+    WStacking *bottom;
+    Window dummywin;
+    uint bottom_last_close:1;
+};
+
+
+extern bool group_init(WGroup *grp, WWindow *parent, const WFitParams *fp);
+extern WGroup *create_group(WWindow *parent, const WFitParams *fp);
+extern void group_deinit(WGroup *grp);
+
+extern WRegion *group_load(WWindow *par, const WFitParams *fp, 
+                           ExtlTab tab);
+extern void group_do_load(WGroup *ws, ExtlTab tab);
+
+extern WRegion* group_current(WGroup *group);
+
+DYNFUN WStacking *group_do_add_managed(WGroup *ws, WRegion *reg, 
+                                       int level, WSizePolicy szplcy);
+extern WStacking *group_do_add_managed_default(WGroup *ws, WRegion *reg, 
+                                               int level, WSizePolicy szplcy);
+
+extern WRegion *group_do_attach(WGroup *ws, 
+                                WGroupAttachParams *param,
+                                WRegionAttachData *data);
+extern bool group_do_attach_final(WGroup *ws, 
+                                  WRegion *reg,
+                                  const WGroupAttachParams *param);
+
+extern WRegion *group_attach(WGroup *ws, WRegion *reg, ExtlTab param);
+extern WRegion *group_attach_new(WGroup *ws, ExtlTab param);
+
+extern void group_manage_stdisp(WGroup *ws, WRegion *stdisp, 
+                                const WMPlexSTDispInfo *di);
+
+extern void group_managed_remove(WGroup *ws, WRegion *reg);
+
+extern WRegion *group_bottom(WGroup *ws);
+
+extern bool group_rescue_clientwins(WGroup *ws, WPHolder *ph);
+
+extern bool group_rqclose(WGroup *ws);
+extern bool group_rqclose_relocate(WGroup *ws);
+
+extern bool group_managed_rqorder(WGroup *grp, WRegion *sub, 
+                                  WRegionOrder order);
+
+extern WStacking *group_find_stacking(WGroup *ws, WRegion *r);
+extern WStacking *group_find_to_focus(WGroup *ws, WStacking *to_try);
+
+typedef WStackingFilter WGroupIterFilter;
+typedef WStackingIterTmp WGroupIterTmp;
+
+extern void group_iter_init(WGroupIterTmp *tmp, WGroup *ws);
+extern WRegion *group_iter(WGroupIterTmp *tmp);
+extern WStacking *group_iter_nodes(WGroupIterTmp *tmp);
+
+extern WStacking *group_get_stacking(WGroup *ws);
+extern WStacking **group_get_stackingp(WGroup *ws);
+
+#define FOR_ALL_MANAGED_BY_GROUP(WS, VAR, TMP) \
+    for(group_iter_init(&(TMP), WS),           \
+         VAR=group_iter(&(TMP));               \
+        VAR!=NULL;                             \
+        VAR=group_iter(&(TMP)))
+
+#define FOR_ALL_NODES_IN_GROUP(WS, VAR, TMP) \
+    for(group_iter_init(&(TMP), WS),         \
+         VAR=group_iter_nodes(&(TMP));       \
+        VAR!=NULL;                           \
+        VAR=group_iter_nodes(&(TMP)))
+
+extern WGroupIterTmp group_iter_default_tmp;
+
+
+#endif /* ION_IONCORE_GROUP_H */
diff --git a/ioncore/groupedpholder.c b/ioncore/groupedpholder.c
new file mode 100644 (file)
index 0000000..ac3b692
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * ion/ioncore/groupedpholder.c
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/objp.h>
+#include <libtu/obj.h>
+
+#include "group.h"
+#include "group-cw.h"
+#include "groupedpholder.h"
+
+
+/*{{{ Init/deinit */
+
+
+bool groupedpholder_init(WGroupedPHolder *ph, WPHolder *cont)
+{
+    assert(cont!=NULL);
+    
+    pholder_init(&(ph->ph));
+
+    ph->cont=cont;
+    
+    return TRUE;
+}
+
+WGroupedPHolder *create_groupedpholder(WPHolder *cont)
+{
+    CREATEOBJ_IMPL(WGroupedPHolder, groupedpholder, (p, cont));
+}
+
+
+void groupedpholder_deinit(WGroupedPHolder *ph)
+{
+    if(ph->cont!=NULL){
+        destroy_obj((Obj*)ph->cont);
+        ph->cont=NULL;
+    }
+    
+    pholder_deinit(&(ph->ph));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Attach */
+
+
+static bool grouped_do_attach_final(WGroupCW *cwg, 
+                                    WRegion *reg,
+                                    WGroupAttachParams *param)
+{
+    if(!param->geom_set){
+        /* Comm. hack */
+        REGION_GEOM(cwg)=REGION_GEOM(reg);
+    }
+
+    param->geom_set=1;
+    param->geom.x=0;
+    param->geom.y=0;
+    param->geom.w=REGION_GEOM(reg).w;
+    param->geom.h=REGION_GEOM(reg).h;
+    param->szplcy=SIZEPOLICY_FULL_EXACT;
+    param->szplcy_set=TRUE;
+    
+    return group_do_attach_final(&cwg->grp, reg, param);
+}
+
+
+WRegion *grouped_handler(WWindow *par, 
+                         const WFitParams *fp, 
+                         void *frp_)
+{
+    WRegionAttachData *data=(WRegionAttachData*)frp_;
+    WGroupAttachParams param=GROUPATTACHPARAMS_INIT;
+    WGroupCW *cwg;
+    WRegion *reg;
+    WStacking *st;
+    
+    cwg=create_groupcw(par, fp);
+    
+    if(cwg==NULL)
+        return NULL;
+    
+    param.level_set=1;
+    param.level=STACKING_LEVEL_BOTTOM;
+    param.switchto_set=1;
+    param.switchto=1;
+    param.bottom=1;
+    
+    if(!(fp->mode&REGION_FIT_WHATEVER)){
+        /* Comm. hack */
+        param.geom_set=TRUE;
+    }
+    
+    reg=region_attach_helper((WRegion*)cwg, par, fp, 
+                             (WRegionDoAttachFn*)grouped_do_attach_final,
+                             &param, data);
+    
+    if(reg==NULL){
+        destroy_obj((Obj*)cwg);
+        return NULL;
+    }
+
+    return (WRegion*)cwg;
+}
+
+
+WRegion *groupedpholder_do_attach(WGroupedPHolder *ph, int flags,
+                                  WRegionAttachData *data)
+{
+    WRegionAttachData data2;
+    
+    if(ph->cont==NULL)
+        return FALSE;
+
+    data2.type=REGION_ATTACH_NEW;
+    data2.u.n.fn=grouped_handler;
+    data2.u.n.param=data;
+        
+    return pholder_attach_(ph->cont, flags, &data2);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Other dynfuns */
+
+
+bool groupedpholder_do_goto(WGroupedPHolder *ph)
+{
+    if(ph->cont!=NULL)
+        return pholder_goto(ph->cont);
+    
+    return FALSE;
+}
+
+
+WRegion *groupedpholder_do_target(WGroupedPHolder *ph)
+{
+    if(ph->cont!=NULL)
+        return pholder_target(ph->cont);
+    
+    return NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Class information */
+
+
+static DynFunTab groupedpholder_dynfuntab[]={
+    {(DynFun*)pholder_do_attach, 
+     (DynFun*)groupedpholder_do_attach},
+
+    {(DynFun*)pholder_do_goto, 
+     (DynFun*)groupedpholder_do_goto},
+
+    {(DynFun*)pholder_do_target, 
+     (DynFun*)groupedpholder_do_target},
+    
+    END_DYNFUNTAB
+};
+
+IMPLCLASS(WGroupedPHolder, WPHolder, groupedpholder_deinit, 
+          groupedpholder_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/ioncore/groupedpholder.h b/ioncore/groupedpholder.h
new file mode 100644 (file)
index 0000000..4feb699
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * ion/ioncore/groupedpholder.h
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_GROUPEDPHOLDER_H
+#define ION_IONCORE_GROUPEDPHOLDER_H
+
+#include "common.h"
+#include "pholder.h"
+#include "attach.h"
+
+INTRCLASS(WGroupedPHolder);
+
+DECLCLASS(WGroupedPHolder){
+    WPHolder ph;
+    WPHolder *cont;
+};
+
+extern WGroupedPHolder *create_groupedpholder(WPHolder *cont);
+
+extern bool groupedpholder_init(WGroupedPHolder *ph, WPHolder *cont);
+
+extern void groupedpholder_deinit(WGroupedPHolder *ph);
+
+extern bool groupedpholder_do_goto(WGroupedPHolder *ph);
+
+extern WRegion *groupedpholder_do_target(WGroupedPHolder *ph);
+
+extern WRegion *groupedpholder_do_attach(WGroupedPHolder *ph, int flags,
+                                         WRegionAttachData *data);
+
+#endif /* ION_IONCORE_GROUPEDPHOLDER_H */
diff --git a/ioncore/grouppholder.c b/ioncore/grouppholder.c
new file mode 100644 (file)
index 0000000..d92c5b4
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * ion/ioncore/grouppholder.c
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/objp.h>
+#include <libtu/obj.h>
+#include <libtu/pointer.h>
+
+#include <ioncore/common.h>
+#include "group.h"
+#include "grouppholder.h"
+
+
+static void group_watch_handler(Watch *watch, Obj *ws);
+
+
+/*{{{ Init/deinit */
+
+
+static void group_watch_handler(Watch *watch, Obj *ws)
+{
+    WGroupPHolder *ph=FIELD_TO_STRUCT(WGroupPHolder, 
+                                      group_watch, watch);
+    pholder_redirect(&(ph->ph), (WRegion*)ws);
+}
+
+
+static WGroupAttachParams dummy_param=GROUPATTACHPARAMS_INIT;
+
+
+bool grouppholder_init(WGroupPHolder *ph, WGroup *ws,
+                       const WStacking *st,
+                       const WGroupAttachParams *param)
+{
+    pholder_init(&(ph->ph));
+
+    watch_init(&(ph->group_watch));
+    watch_init(&(ph->stack_above_watch));
+    
+    if(ws!=NULL){
+        if(!watch_setup(&(ph->group_watch), (Obj*)ws, 
+                        group_watch_handler)){
+            pholder_deinit(&(ph->ph));
+            return FALSE;
+        }
+    }
+    
+    if(param==NULL)
+        param=&dummy_param;
+    
+    if(st!=NULL){
+        /* TODO? Just link to the stacking structure to remember 
+         * stacking order? 
+         */
+        
+        ph->param.szplcy_set=TRUE;
+        ph->param.szplcy=st->szplcy;
+        ph->param.level_set=TRUE;
+        ph->param.level=st->level;
+        
+        if(st->reg!=NULL){
+            ph->param.geom_set=TRUE;
+            ph->param.geom=REGION_GEOM(st->reg);
+        }
+        
+        if(st->above!=NULL && st->above->reg!=NULL)
+            ph->param.stack_above=st->above->reg;
+        
+        ph->param.modal=FALSE;
+        ph->param.bottom=(st==ws->bottom);
+        /*ph->passive=FALSE;*/
+    }else{
+        ph->param=*param;
+    }
+
+    ph->param.switchto_set=FALSE;
+
+    if(ph->param.stack_above!=NULL){
+        /* We must move stack_above pointer into a Watch. */
+        watch_setup(&(ph->stack_above_watch), 
+                    (Obj*)ph->param.stack_above, NULL);
+        ph->param.stack_above=NULL;
+    }
+    
+    return TRUE;
+}
+
+WGroupPHolder *create_grouppholder(WGroup *ws,
+                                   const WStacking *st,
+                                   const WGroupAttachParams *param)
+{
+    CREATEOBJ_IMPL(WGroupPHolder, grouppholder, (p, ws, st, param));
+}
+
+
+void grouppholder_deinit(WGroupPHolder *ph)
+{
+    watch_reset(&(ph->group_watch));
+    watch_reset(&(ph->stack_above_watch));
+    pholder_deinit(&(ph->ph));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuns */
+
+
+WRegion *grouppholder_do_attach(WGroupPHolder *ph, int flags,
+                                WRegionAttachData *data)
+{
+    WGroup *ws=(WGroup*)ph->group_watch.obj;
+    WRegion *reg;
+
+    if(ws==NULL)
+        return FALSE;
+
+    ph->param.switchto_set=1;
+    ph->param.switchto=(flags&PHOLDER_ATTACH_SWITCHTO ? 1 : 0);
+    
+    /* Get stack_above from Watch. */
+    ph->param.stack_above=(WRegion*)ph->stack_above_watch.obj;
+    
+    reg=group_do_attach(ws, &ph->param, data);
+    
+    ph->param.stack_above=NULL;
+
+    return reg;
+}
+
+
+bool grouppholder_do_goto(WGroupPHolder *ph)
+{
+    WGroup *ws=(WGroup*)ph->group_watch.obj;
+    
+    if(ws!=NULL)
+        return region_goto((WRegion*)ws);
+    
+    return FALSE;
+}
+
+
+WRegion *grouppholder_do_target(WGroupPHolder *ph)
+{
+    return (WRegion*)ph->group_watch.obj;
+}
+
+
+/*}}}*/
+
+
+/*{{{ WGroup stuff */
+
+
+WGroupPHolder *group_managed_get_pholder(WGroup *ws, WRegion *mgd)
+{
+    WStacking *st=group_find_stacking(ws, mgd);
+    
+    if(mgd==NULL)
+        return NULL;
+    else
+        return create_grouppholder(ws, st, NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Class information */
+
+
+static DynFunTab grouppholder_dynfuntab[]={
+    {(DynFun*)pholder_do_attach, 
+     (DynFun*)grouppholder_do_attach},
+
+    {(DynFun*)pholder_do_goto, 
+     (DynFun*)grouppholder_do_goto},
+
+    {(DynFun*)pholder_do_target, 
+     (DynFun*)grouppholder_do_target},
+    
+    END_DYNFUNTAB
+};
+
+IMPLCLASS(WGroupPHolder, WPHolder, grouppholder_deinit, 
+          grouppholder_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/ioncore/grouppholder.h b/ioncore/grouppholder.h
new file mode 100644 (file)
index 0000000..4b0208a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * ion/ioncore/grouppholder.h
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_GROUPPHOLDER_H
+#define ION_IONCORE_GROUPPHOLDER_H
+
+#include <ioncore/common.h>
+#include <ioncore/pholder.h>
+#include "group.h"
+
+INTRCLASS(WGroupPHolder);
+
+DECLCLASS(WGroupPHolder){
+    WPHolder ph;
+    Watch group_watch;
+    Watch stack_above_watch;
+    WGroupAttachParams param;
+};
+
+extern WGroupPHolder *create_grouppholder(WGroup *group, 
+                                          const WStacking *either_st,
+                                          const WGroupAttachParams *or_param);
+
+extern bool grouppholder_init(WGroupPHolder *ph, 
+                              WGroup *group,
+                              const WStacking *either_st,
+                              const WGroupAttachParams *or_param);
+
+extern void grouppholder_deinit(WGroupPHolder *ph);
+
+extern bool grouppholder_do_goto(WGroupPHolder *ph);
+
+extern WRegion *grouppholder_do_target(WGroupPHolder *ph);
+
+extern WRegion *grouppholder_do_attach(WGroupPHolder *ph, int flags, 
+                                       WRegionAttachData *data);
+
+extern WGroupPHolder *group_managed_get_pholder(WGroup *group, 
+                                                WRegion *mgd);
+
+#endif /* ION_IONCORE_GROUPPHOLDER_H */
diff --git a/ioncore/infowin.c b/ioncore/infowin.c
new file mode 100644 (file)
index 0000000..95622b4
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * ion/ioncore/infowin.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include "common.h"
+#include "global.h"
+#include "window.h"
+#include "infowin.h"
+#include "resize.h"
+#include "gr.h"
+#include "event.h"
+
+
+/*{{{ Init/deinit */
+
+
+bool infowin_init(WInfoWin *p, WWindow *parent, const WFitParams *fp,
+                  const char *style)
+{
+    XSetWindowAttributes attr;
+    
+    if(!window_init(&(p->wwin), parent, fp))
+        return FALSE;
+    
+    p->buffer=ALLOC_N(char, INFOWIN_BUFFER_LEN);
+    if(p->buffer==NULL)
+        goto fail;
+    p->buffer[0]='\0';
+    
+    if(style==NULL)
+        p->style=scopy("*");
+    else
+        p->style=scopy(style);
+    if(p->style==NULL)
+        goto fail2;
+    
+    p->brush=NULL;
+    p->attr=NULL;
+    
+    infowin_updategr(p);
+    
+    if(p->brush==NULL)
+        goto fail3;
+    
+    p->wwin.region.flags|=REGION_SKIP_FOCUS;
+    
+    /* Enable save unders */
+    attr.save_under=True;
+    XChangeWindowAttributes(ioncore_g.dpy, p->wwin.win, CWSaveUnder, &attr);
+    
+    window_select_input(&(p->wwin), IONCORE_EVENTMASK_NORMAL);
+
+    return TRUE;
+
+fail3:    
+    free(p->style);
+fail2:
+    free(p->buffer);
+fail:    
+    window_deinit(&(p->wwin));
+    return FALSE;
+}
+
+
+WInfoWin *create_infowin(WWindow *parent, const WFitParams *fp,
+                         const char *style)
+{
+    CREATEOBJ_IMPL(WInfoWin, infowin, (p, parent, fp, style));
+}
+
+
+void infowin_deinit(WInfoWin *p)
+{
+    if(p->buffer!=NULL){
+        free(p->buffer);
+        p->buffer=NULL;
+    }
+
+    if(p->attr!=NULL){
+        free(p->attr);
+        p->attr=NULL;
+    }
+    
+    if(p->style!=NULL){
+        free(p->style);
+        p->style=NULL;
+    }
+    
+    if(p->brush!=NULL){
+        grbrush_release(p->brush);
+        p->brush=NULL;
+    }
+    
+    window_deinit(&(p->wwin));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Drawing and geometry */
+
+
+void infowin_draw(WInfoWin *p, bool complete)
+{
+    WRectangle g;
+    
+    if(p->brush==NULL)
+        return;
+    
+    g.x=0;
+    g.y=0;
+    g.w=REGION_GEOM(p).w;
+    g.h=REGION_GEOM(p).h;
+
+    grbrush_begin(p->brush, &g, GRBRUSH_NO_CLEAR_OK);
+    grbrush_draw_textbox(p->brush, &g, p->buffer, p->attr, TRUE);
+    grbrush_end(p->brush);
+}
+
+
+void infowin_updategr(WInfoWin *p)
+{
+    GrBrush *nbrush;
+    
+    assert(p->style!=NULL);
+    
+    nbrush=gr_get_brush(p->wwin.win, 
+                        region_rootwin_of((WRegion*)p),
+                        p->style);
+    if(nbrush==NULL)
+        return;
+    
+    if(p->brush!=NULL)
+        grbrush_release(p->brush);
+    
+    p->brush=nbrush;
+    
+    window_draw(&(p->wwin), TRUE);
+}
+
+
+
+/*}}}*/
+
+
+/*{{{ Content-setting */
+
+
+bool infowin_set_attr2(WInfoWin *p, const char *attr1, const char *attr2)
+{
+    char *p2=NULL;
+    
+    if(attr1!=NULL){
+        if(attr2==NULL)
+            p2=scopy(attr1);
+        else
+            libtu_asprintf(&p2, "%s-%s", attr1, attr2);
+        if(p2==NULL)
+            return FALSE;
+    }
+    
+    if(p->attr)
+        free(p->attr);
+    
+    p->attr=p2;
+    
+    return TRUE;
+}
+
+
+static void infowin_do_set_text(WInfoWin *p, const char *str)
+{
+    strncpy(INFOWIN_BUFFER(p), str, INFOWIN_BUFFER_LEN);
+    INFOWIN_BUFFER(p)[INFOWIN_BUFFER_LEN-1]='\0';
+}
+
+
+static void infowin_resize(WInfoWin *p)
+{
+    WRQGeomParams rq=RQGEOMPARAMS_INIT;
+    const char *str=INFOWIN_BUFFER(p);
+    GrBorderWidths bdw;
+    GrFontExtents fnte;
+    
+    rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
+    
+    rq.geom.x=REGION_GEOM(p).x;
+    rq.geom.y=REGION_GEOM(p).y;
+    
+    grbrush_get_border_widths(p->brush, &bdw);
+    grbrush_get_font_extents(p->brush, &fnte);
+        
+    rq.geom.w=bdw.left+bdw.right;
+    rq.geom.w+=grbrush_get_text_width(p->brush, str, strlen(str));
+    rq.geom.h=fnte.max_height+bdw.top+bdw.bottom;
+
+    if(rectangle_compare(&rq.geom, &REGION_GEOM(p))!=RECTANGLE_SAME)
+        region_rqgeom((WRegion*)p, &rq, NULL);
+}
+
+
+/*EXTL_DOC
+ * Set contents of the info window.
+ */
+EXTL_EXPORT_MEMBER
+void infowin_set_text(WInfoWin *p, const char *str)
+{
+    infowin_do_set_text(p, str);
+
+    infowin_resize(p);
+    
+    /* sometimes unnecessary */
+    window_draw((WWindow*)p, TRUE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Load */
+
+
+WRegion *infowin_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
+{
+    char *style=NULL, *text=NULL;
+    WInfoWin *p;
+    
+    extl_table_gets_s(tab, "style", &style);
+    
+    p=create_infowin(par, fp, style);
+    
+    free(style);
+    
+    if(p==NULL)
+        return NULL;
+
+    if(extl_table_gets_s(tab, "text", &text)){
+        infowin_do_set_text(p, text);
+        free(text);
+    }
+    
+    return (WRegion*)p;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab infowin_dynfuntab[]={
+    {window_draw, infowin_draw},
+    {region_updategr, infowin_updategr},
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WInfoWin, WWindow, infowin_deinit, infowin_dynfuntab);
+
+    
+/*}}}*/
+
diff --git a/ioncore/infowin.h b/ioncore/infowin.h
new file mode 100644 (file)
index 0000000..c57dd01
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * ion/ioncore/infowin.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_INFOWIN_H
+#define ION_IONCORE_INFOWIN_H
+
+#include "common.h"
+#include "window.h"
+#include "gr.h"
+#include "rectangle.h"
+
+#define INFOWIN_BUFFER_LEN 256
+
+DECLCLASS(WInfoWin){
+    WWindow wwin;
+    GrBrush *brush;
+    char *buffer;
+    char *attr;
+    char *style;
+};
+
+#define INFOWIN_BRUSH(INFOWIN) ((INFOWIN)->brush)
+#define INFOWIN_BUFFER(INFOWIN) ((INFOWIN)->buffer)
+
+extern bool infowin_init(WInfoWin *p, WWindow *parent, const WFitParams *fp,
+                         const char *style);
+extern WInfoWin *create_infowin(WWindow *parent, const WFitParams *fp,
+                                const char *style);
+
+extern void infowin_deinit(WInfoWin *p);
+
+extern void infowin_set_text(WInfoWin *p, const char *s);
+extern bool infowin_set_attr2(WInfoWin *p, const char *a1, const char *a2);
+
+extern WRegion *infowin_load(WWindow *par, const WFitParams *fp, ExtlTab tab);
+
+extern void infowin_updategr(WInfoWin *p);
+
+#endif /* ION_IONCORE_INFOWIN_H */
diff --git a/ioncore/ioncore.c b/ioncore/ioncore.c
new file mode 100644 (file)
index 0000000..5c88803
--- /dev/null
@@ -0,0 +1,662 @@
+/*
+ * ion/ioncore/ioncore.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <ctype.h>
+#ifndef CF_NO_LOCALE
+#include <locale.h>
+#include <langinfo.h>
+#include <libintl.h>
+#endif
+
+#include <libtu/util.h>
+#include <libtu/optparser.h>
+#include <libextl/readconfig.h>
+#include <libextl/extl.h>
+#include <libmainloop/select.h>
+#include <libmainloop/signal.h>
+#include <libmainloop/hooks.h>
+#include <libmainloop/exec.h>
+
+#include "common.h"
+#include "rootwin.h"
+#include "event.h"
+#include "cursor.h"
+#include "global.h"
+#include "modules.h"
+#include "eventh.h"
+#include "ioncore.h"
+#include "manage.h"
+#include "conf.h"
+#include "binding.h"
+#include "bindmaps.h"
+#include "strings.h"
+#include "gr.h"
+#include "xic.h"
+#include "netwm.h"
+#include "focus.h"
+#include "frame.h"
+#include "saveload.h"
+#include "infowin.h"
+#include "activity.h"
+#include "group-cw.h"
+#include "group-ws.h"
+#include "llist.h"
+
+#include "../version.h"
+#include "exports.h"
+
+
+/*{{{ Variables */
+
+
+WGlobal ioncore_g;
+
+static const char *progname="ion";
+
+static const char ioncore_copy[]=
+    "Ion " ION_VERSION ", copyright (c) Tuomo Valkonen 1999-2006.";
+
+static const char ioncore_license[]=DUMMY_TR(
+    "This program is free software; you can redistribute it and/or\n"
+    "modify it under the terms of the GNU Lesser General Public\n"
+    "License as published by the Free Software Foundation; either\n"
+    "version 2.1 of the License, or (at your option) any later version.\n"
+    "\n"
+    "This program is distributed in the hope that it will be useful,\n"
+    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
+    "Lesser General Public License for more details.\n");
+
+static const char *ioncore_about=NULL;
+
+WHook *ioncore_post_layout_setup_hook=NULL;
+WHook *ioncore_snapshot_hook=NULL;
+WHook *ioncore_deinit_hook=NULL;
+
+
+/*}}}*/
+
+
+/*{{{ warn_nolog */
+
+
+void ioncore_warn_nolog(const char *str)
+{
+    fprintf(stderr, "%s: %s\n", prog_execname(), str);
+}
+
+
+/*}}}*/
+
+
+/*{{{ init_locale */
+
+
+#ifndef CF_NO_LOCALE
+
+
+static bool check_encoding()
+{
+    int i;
+    char chs[8]=" ";
+    wchar_t wc;
+    const char *langi, *ctype, *a, *b;
+    bool enc_check_ok=FALSE;
+
+    langi=nl_langinfo(CODESET);
+    ctype=setlocale(LC_CTYPE, NULL);
+    
+    if(langi==NULL || ctype==NULL)
+        goto integr_err;
+
+    if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0)
+        return TRUE;
+    
+    /* Compare encodings case-insensitively, ignoring dashes (-) */
+    a=langi; 
+    b=strchr(ctype, '.');
+    if(b!=NULL){
+        b=b+1;
+        while(1){
+            if(*a=='-'){
+                a++;
+                continue;
+            }
+            if(*b=='-'){
+                b++;
+                continue;
+            }
+            if(*b=='\0' || *b=='@'){
+                enc_check_ok=(*a=='\0');
+                break;
+            }
+            if(*a=='\0' || tolower(*a)!=tolower(*b))
+                break;
+            a++;
+            b++;
+        }
+        if(!enc_check_ok)
+            goto integr_err;
+    }else{
+        ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
+    }
+        
+    if(strcasecmp(langi, "UTF-8")==0 || strcasecmp(langi, "UTF8")==0){
+        ioncore_g.enc_sb=FALSE;
+        ioncore_g.enc_utf8=TRUE;
+        ioncore_g.use_mb=TRUE;
+        return TRUE;
+    }
+    
+    for(i=0; i<256; i++){
+        chs[0]=i;
+        if(mbtowc(&wc, chs, 8)==-1){
+            /* Doesn't look like a single-byte encoding. */
+            break;
+        }
+        
+    }
+    
+    if(i==256){
+        /* Seems like a single-byte encoding... */
+        ioncore_g.use_mb=TRUE;
+        return TRUE;
+    }
+
+    if(mbtowc(NULL, NULL, 0)!=0){
+        warn("Statefull encodings are unsupported.");
+        return FALSE;
+    }
+    
+    ioncore_g.enc_sb=FALSE;
+    ioncore_g.use_mb=TRUE;
+
+    return TRUE;
+    
+integr_err:
+    warn("Cannot verify locale encoding setting integrity "
+         "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). "
+         "The LC_CTYPE environment variable should be of the form "
+         "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding "
+         "should match the nl_langinfo value above.", ctype, langi);
+    return FALSE;
+}
+
+
+static bool init_locale()
+{
+    const char *p;
+    
+    p=setlocale(LC_ALL, "");
+    
+    if(p==NULL){
+        warn("setlocale() call failed.");
+        return FALSE;
+    }
+
+    /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
+        return TRUE;*/
+
+    if(!XSupportsLocale()){
+        warn("XSupportsLocale() failed.");
+    }else{
+        if(check_encoding())
+            return TRUE;
+    }
+    
+    warn("Reverting locale settings to \"C\".");
+    
+    if(setlocale(LC_ALL, "C")==NULL)
+        warn("setlocale() call failed.");
+        
+    return FALSE;
+}
+
+#define TEXTDOMAIN "ion3"
+
+static bool init_messages(const char *localedir)
+{
+    if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){
+        warn_err_obj("bindtextdomain");
+        return FALSE;
+    }else if(textdomain(TEXTDOMAIN)==NULL){
+        warn_err_obj("textdomain");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+
+#endif
+
+
+/*}}}*/
+
+
+/*{{{ ioncore_init */
+
+
+#define INIT_HOOK_(NM)                             \
+    NM=mainloop_register_hook(#NM, create_hook()); \
+    if(NM==NULL) return FALSE;
+
+#define INIT_HOOK(NM, DFLT)                           \
+    INIT_HOOK_(NM)                                    \
+    if(!hook_add(NM, (void (*)())DFLT)) return FALSE;
+
+
+static bool init_hooks()
+{
+    INIT_HOOK_(ioncore_post_layout_setup_hook);
+    INIT_HOOK_(ioncore_snapshot_hook);
+    INIT_HOOK_(ioncore_deinit_hook);
+    INIT_HOOK_(screen_managed_changed_hook);
+    INIT_HOOK_(frame_managed_changed_hook);
+    INIT_HOOK_(region_activated_hook);
+    INIT_HOOK_(region_inactivated_hook);
+    INIT_HOOK_(clientwin_mapped_hook);
+    INIT_HOOK_(clientwin_unmapped_hook);
+    INIT_HOOK_(clientwin_property_change_hook);
+    INIT_HOOK_(region_notify_hook);
+    
+    INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
+    INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
+    INIT_HOOK(region_do_warp_alt, region_do_warp_default);
+
+    mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
+                                                 create_hook());
+    mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
+                                                 create_hook());
+    
+    return TRUE;
+}
+
+
+static bool register_classes()
+{
+    int fail=0;
+    
+    fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin), 
+                                     (WRegionLoadCreateFn*)clientwin_load);
+    fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex), 
+                                     (WRegionLoadCreateFn*)mplex_load);
+    fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame), 
+                                     (WRegionLoadCreateFn*)frame_load);
+    fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin), 
+                                     (WRegionLoadCreateFn*)infowin_load);
+    fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW), 
+                                     (WRegionLoadCreateFn*)groupcw_load);
+    fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS), 
+                                     (WRegionLoadCreateFn*)groupws_load);
+    
+    return !fail;
+}
+
+
+static void init_global()
+{
+    /* argc, argv must be set be the program */
+    ioncore_g.dpy=NULL;
+    ioncore_g.display=NULL;
+
+    ioncore_g.sm_client_id=NULL;
+    ioncore_g.rootwins=NULL;
+    ioncore_g.screens=NULL;
+    ioncore_g.focus_next=NULL;
+    ioncore_g.warp_next=FALSE;
+    
+    ioncore_g.focus_current=NULL;
+
+    ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
+    ioncore_g.opmode=IONCORE_OPMODE_INIT;
+    ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
+    ioncore_g.opaque_resize=0;
+    ioncore_g.warp_enabled=TRUE;
+    ioncore_g.switchto_new=TRUE;
+    
+    ioncore_g.enc_utf8=FALSE;
+    ioncore_g.enc_sb=TRUE;
+    ioncore_g.use_mb=FALSE;
+    
+    ioncore_g.screen_notify=TRUE;
+    
+    ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
+    
+    ioncore_g.framed_transients=TRUE;
+}
+
+
+bool ioncore_init(const char *prog, int argc, char *argv[],
+                  const char *localedir)
+{
+    init_global();
+    
+    progname=prog;
+    ioncore_g.argc=argc;
+    ioncore_g.argv=argv;
+
+#ifndef CF_NO_LOCALE    
+    init_locale();
+    init_messages(localedir);
+#endif
+
+    ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
+    
+    if(!ioncore_init_bindmaps())
+        return FALSE;
+    
+    if(!register_classes())
+        return FALSE;
+
+    if(!init_hooks())
+        return FALSE;
+
+    if(!ioncore_init_module_support())
+        return FALSE;
+
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ ioncore_startup */
+
+
+static void ioncore_init_session(const char *display)
+{
+    const char *dpyend=NULL;
+    char *tmp=NULL, *colon=NULL;
+    const char *sm=getenv("SESSION_MANAGER");
+    
+    if(sm!=NULL)
+        ioncore_load_module("mod_sm");
+
+    if(extl_sessiondir()!=NULL)
+        return;
+    
+    /* Not running under SM; use display-specific directory */
+    dpyend=strchr(display, ':');
+    if(dpyend!=NULL)
+        dpyend=strchr(dpyend, '.');
+    if(dpyend==NULL){    
+        libtu_asprintf(&tmp, "default-session-%s", display);
+    }else{
+        libtu_asprintf(&tmp, "default-session-%.*s",
+                       (int)(dpyend-display), display);
+    }
+    
+    if(tmp==NULL)
+        return;
+    
+    colon=tmp;
+    while(1){
+        colon=strchr(colon, ':');
+        if(colon==NULL)
+            break;
+        *colon='-';
+    }
+    
+    extl_set_sessiondir(tmp);
+    free(tmp);
+}
+    
+
+static bool ioncore_init_x(const char *display, int stflags)
+{
+    Display *dpy;
+    int i, drw, nrw;
+    static bool called=FALSE;
+
+    /* Sorry, this function can not be re-entered due to laziness
+     * towards implementing checking of things already initialized.
+     * Nobody would call this twice anyway.
+     */
+    assert(!called);
+    called=TRUE;
+
+    /* Open the display. */
+    dpy=XOpenDisplay(display);
+    
+    if(dpy==NULL){
+        warn(TR("Could not connect to X display '%s'"), 
+             XDisplayName(display));
+        return FALSE;
+    }
+
+    if(stflags&IONCORE_STARTUP_ONEROOT){
+        drw=DefaultScreen(dpy);
+        nrw=drw+1;
+    }else{
+        drw=0;
+        nrw=ScreenCount(dpy);
+    }
+    
+    /* Initialize */
+    if(display!=NULL){
+        ioncore_g.display=scopy(display);
+        if(ioncore_g.display==NULL){
+            XCloseDisplay(dpy);
+            return FALSE;
+        }
+    }
+    
+    ioncore_g.dpy=dpy;
+    ioncore_g.win_context=XUniqueContext();
+    ioncore_g.conn=ConnectionNumber(dpy);
+    
+    cloexec_braindamage_fix(ioncore_g.conn);
+    
+    ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
+    ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
+    ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
+    ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+    ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+    ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
+    ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
+    ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
+    ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
+    ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
+
+    ioncore_init_xim();
+    ioncore_init_bindings();
+    ioncore_init_cursors();
+
+    netwm_init();
+    
+    ioncore_init_session(XDisplayName(display));
+
+    for(i=drw; i<nrw; i++)
+        ioncore_manage_rootwin(i, stflags&IONCORE_STARTUP_NOXINERAMA);
+
+    if(ioncore_g.rootwins==NULL){
+        if(nrw-drw>1)
+            warn(TR("Could not find a screen to manage."));
+        return FALSE;
+    }
+
+    if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
+                                   ioncore_x_connection_handler)){
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+static void set_initial_focus()
+{
+    Window root=None, win=None;
+    int x, y, wx, wy;
+    uint mask;
+    WScreen *scr;
+    WWindow *wwin;
+    
+    XQueryPointer(ioncore_g.dpy, None, &root, &win,
+                  &x, &y, &wx, &wy, &mask);
+    
+    FOR_ALL_SCREENS(scr){
+        Window scrroot=region_root_of((WRegion*)scr);
+        if(scrroot==root && rectangle_contains(&REGION_GEOM(scr), x, y)){
+            break;
+        }
+    }
+    
+    if(scr==NULL)
+        scr=ioncore_g.screens;
+    
+    region_focuslist_push((WRegion*)scr);
+    region_do_set_focus((WRegion*)scr, FALSE);
+}
+
+
+bool ioncore_startup(const char *display, const char *cfgfile,
+                     int stflags)
+{
+    WRootWin *rootwin;
+    sigset_t inittrap;
+
+    /* Don't trap termination signals just yet. */
+    sigemptyset(&inittrap);
+    sigaddset(&inittrap, SIGALRM);
+    sigaddset(&inittrap, SIGCHLD);
+    sigaddset(&inittrap, SIGPIPE);
+    sigaddset(&inittrap, SIGUSR2);
+    mainloop_trap_signals(&inittrap);
+
+    if(!extl_init())
+        return FALSE;
+
+    ioncore_register_exports();
+    
+    if(!ioncore_init_x(display, stflags))
+        return FALSE;
+
+    gr_read_config();
+
+    if(!extl_read_config("ioncore_ext", NULL, TRUE))
+        return FALSE;
+    
+    ioncore_read_main_config(cfgfile);
+    
+    if(!ioncore_init_layout())
+        return FALSE;
+    
+    hook_call_v(ioncore_post_layout_setup_hook);
+    
+    FOR_ALL_ROOTWINS(rootwin)
+        rootwin_manage_initial_windows(rootwin);
+    
+    set_initial_focus();
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ ioncore_deinit */
+
+
+void ioncore_deinit()
+{
+    Display *dpy;
+    WRootWin *rootwin;
+    
+    ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
+    
+    if(ioncore_g.dpy==NULL)
+        return;
+
+    hook_call_v(ioncore_deinit_hook);
+
+    while(ioncore_g.screens!=NULL)
+        destroy_obj((Obj*)ioncore_g.screens);
+
+    /*ioncore_unload_modules();*/
+
+    while(ioncore_g.rootwins!=NULL)
+        destroy_obj((Obj*)ioncore_g.rootwins);
+
+    ioncore_deinit_bindmaps();
+
+    mainloop_unregister_input_fd(ioncore_g.conn);
+    
+    dpy=ioncore_g.dpy;
+    ioncore_g.dpy=NULL;
+    
+    XSync(dpy, True);
+    XCloseDisplay(dpy);
+    
+    extl_deinit();
+}
+
+
+/*}}}*/
+
+
+/*{{{ Miscellaneous */
+
+
+/*EXTL_DOC
+ * Is Ion supporting locale-specifically multibyte-encoded strings?
+ */
+EXTL_SAFE
+EXTL_EXPORT
+bool ioncore_is_i18n()
+{
+    return ioncore_g.use_mb;
+}
+
+
+/*EXTL_DOC
+ * Returns Ioncore version string.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+const char *ioncore_version()
+{
+    return ION_VERSION;
+}
+
+/*EXTL_DOC
+ * Returns the name of program using Ioncore.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+const char *ioncore_progname()
+{
+    return progname;
+}
+
+
+/*EXTL_DOC
+ * Returns an about message (version, author, copyright notice).
+ */
+EXTL_SAFE
+EXTL_EXPORT
+const char *ioncore_aboutmsg()
+{
+    return ioncore_about;
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/ioncore.h b/ioncore/ioncore.h
new file mode 100644 (file)
index 0000000..658e966
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ion/ioncore/focus.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_IONCORE_H
+#define ION_IONCORE_IONCORE_H
+
+#include <libmainloop/hooks.h>
+#include "common.h"
+
+#define IONCORE_STARTUP_ONEROOT    0x0001
+#define IONCORE_STARTUP_NOXINERAMA 0x0002
+
+extern bool ioncore_init(const char *prog, int argc, char *argv[],
+                         const char *localedir);
+extern bool ioncore_startup(const char *display, const char *cfgfile, 
+                            int flags);
+extern void ioncore_deinit();
+
+extern const char *ioncore_aboutmsg();
+extern const char *ioncore_version();
+
+/* These hooks have no parameters. */
+extern WHook *ioncore_post_layout_setup_hook;
+extern WHook *ioncore_snapshot_hook;
+extern WHook *ioncore_deinit_hook;
+
+extern void ioncore_warn_nolog(const char *str);
+
+#endif /* ION_IONCORE_IONCORE_H */
diff --git a/ioncore/ioncore_bindings.lua b/ioncore/ioncore_bindings.lua
new file mode 100644 (file)
index 0000000..bf5c9fe
--- /dev/null
@@ -0,0 +1,233 @@
+--
+-- ion/share/ioncore-bindings.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+local ioncore=_G.ioncore
+
+local warn=ioncore.warn
+
+local compiled2str=setmetatable({}, {__mode="k"})
+
+--DOC
+-- Compile string \var{cmd} into a bindable function. Within \var{cmd}, the
+-- variable ''\var{_}'' (underscore) can be used to refer to the object 
+-- that was selecting for the bound action and chosen to handle it.
+-- The  variable ''\var{_sub}'' refers to a ''currently active'' sub-object 
+-- of \var{_}, or a sub-object where the action loading to the binding 
+-- being called actually occured.
+-- 
+-- The string \var{guard}  maybe set to pose limits on \code{_sub}. Currently 
+-- supported guards are \code{_sub:non-nil} and \code{_sub:WFoobar}, where 
+-- \type{WFoobar} is a class.
+function ioncore.compile_cmd(cmd, guard)
+    local guardcode=""
+    local gfn=nil
+    
+    if guard then
+        local st, en, condition=string.find(guard, "^_sub:([%w-_]+)$")
+        local sub='_sub'
+        if not condition then
+            st, en, condition=string.find(guard, "^_chld:([%w-_]+)$")
+            if condition then
+                sub='_chld'
+            end
+        end
+        
+        if not condition then
+            ioncore.warn_traced(TR("Invalid guard %s.", guard))
+        elseif condition=="non-nil" then
+            guardcode='if not '..sub..' then return end; '
+        else
+            guardcode='if not obj_is('..sub..', "'..condition..'") then return end; '
+        end
+        
+        local gfncode="return function(_, _sub, _chld) "..guardcode.." return true end"
+        local gfn, gerr=loadstring(gfncode, guardcode)
+        if not gfn then
+            ioncore.warn_traced(TR("Error compiling guard: %s", gerr))
+        end
+        gfn=gfn()
+    end
+
+    local function guarded(gfn, fn)
+        if not gfn then
+            return fn
+        else
+            return function(_, _sub, _chld) 
+                if gfn(_, _sub, _chld) then 
+                    cmd(_, _sub, _chld) 
+                end
+            end
+        end
+    end
+    
+    if type(cmd)=="string" then
+        local fncode=("return function(_, _sub, _chld) local d = "
+                       ..cmd.." end")
+        local fn, err=loadstring(fncode, cmd)
+        if not fn then
+            ioncore.warn_traced(TR("Error in command string: ")..err)
+            return
+        end
+        compiled2str[fn]=cmd
+        return guarded(gfn, fn())
+    elseif type(cmd)=="function" then
+        return guarded(gfn, cmd)
+    end
+
+    ioncore.warn_traced(TR("Invalid command"))
+end
+
+
+local function putcmd(cmd, guard, tab)
+    local func
+    if cmd then
+        func=ioncore.compile_cmd(cmd, guard)
+        if type(func)~="function" then
+            return
+        end
+    end
+    
+    tab.func=func
+    tab.cmdstr=cmd
+    tab.guard=guard
+    
+    return tab
+end
+
+--DOC
+-- Used to enter documentation among bindings so that other programs
+-- can read it. Does nothing.
+function ioncore.bdoc(text)
+    return {action = "doc", text = text}
+end
+
+--DOC
+-- Returns a function that creates a submap binding description table.
+-- When the key press action \var{keyspec} occurs, Ioncore will wait for
+-- a further key presse and act according to the submap.
+-- For details, see section \ref{sec:bindings}.
+function ioncore.submap(kcb_, list)
+    if not list then
+        return function(lst)
+                   return submap(kcb_, lst)
+               end
+    end
+    return {action = "kpress", kcb = kcb_, submap = list}
+end
+
+--DOC
+-- Creates a binding description table for the action of pressing a key given 
+-- by \var{keyspec} (with possible modifiers) to the function \var{func}.
+-- For more information on bindings, see section \ref{sec:bindings}.
+function ioncore.kpress(keyspec, cmd, guard)
+    return putcmd(cmd, guard, {action = "kpress", kcb = keyspec})
+end
+
+--DOC
+-- This is similar to \fnref{kpress} but after calling \var{cmd}, 
+-- Ioncore waits for all modifiers to be released before processing
+-- any further actions.
+-- For more information on bindings, see section \ref{sec:bindings}.
+function ioncore.kpress_wait(keyspec, cmd, guard)
+    return putcmd(cmd, guard, {action = "kpress_wait", kcb = keyspec})
+end
+
+local function mact(act_, kcb_, cmd, guard)
+    local st, en, kcb2_, area_=string.find(kcb_, "([^@]*)@(.*)")
+    return putcmd(cmd, guard, {
+        action = act_,
+        kcb = (kcb2_ or kcb_),
+        area = area_,
+    })
+end
+
+--DOC
+-- Creates a binding description table for the action of clicking a mouse 
+-- button while possible modifier keys are pressed,
+-- both given by \var{buttonspec}, to the function \var{func}.
+-- For more information, see section \ref{sec:bindings}.
+function ioncore.mclick(buttonspec, cmd, guard)
+    return mact("mclick", buttonspec, cmd, guard)
+end
+
+--DOC
+-- Similar to \fnref{mclick} but for double-click.
+-- Also see section \ref{sec:bindings}.
+function ioncore.mdblclick(buttonspec, cmd, guard)
+    return mact("mdblclick", buttonspec, cmd, guard)
+end
+
+--DOC
+-- Similar to \fnref{mclick} but for just pressing the mouse button.
+-- Also see section \ref{sec:bindings}.
+function ioncore.mpress(buttonspec, cmd, guard)
+    return mact("mpress", buttonspec, cmd, guard)
+end
+
+--DOC
+-- Creates a binding description table for the action of moving the mouse
+-- (or other pointing device) while the button given by \var{buttonspec}
+-- is held pressed and the modifiers given by \var{buttonspec} were pressed
+-- when the button was initially pressed.
+-- Also see section \ref{sec:bindings}.
+function ioncore.mdrag(buttonspec, cmd, guard)
+    return mact("mdrag", buttonspec, cmd, guard)
+end
+
+--DOC
+-- Define bindings for context \var{context}. Here \var{binding} is
+-- a table composed of entries created with \fnref{ioncore.kpress}, 
+-- etc.; see section \ref{sec:bindings} for details.
+function ioncore.defbindings(context, bindings)
+    local function filterdoc(b)
+        local t={}
+        for k, v in ipairs(b) do
+            local v2=v
+            if v2.submap then
+                v2=table.copy(v)
+                v2.submap=filterdoc(v2.submap)
+            end
+            if v2.action~="doc" then
+                table.insert(t, v2)
+            end
+        end
+        return t
+    end
+    return ioncore.do_defbindings(context, filterdoc(bindings))
+end
+
+local function bindings_get_cmds(map)
+    for k, v in pairs(map) do
+        if v.func then
+            v.cmd=compiled2str[v.func]
+        end
+        if v.submap then
+            bindings_get_cmds(v.submap)
+        end
+    end
+end
+
+--DOC
+-- Get a table of all bindings.
+function ioncore.getbindings(maybe_context)
+    local bindings=ioncore.do_getbindings()
+    if maybe_context then
+        bindings_get_cmds(bindings[maybe_context])
+        return bindings[maybe_context]
+    else
+        for k, v in pairs(bindings) do
+            bindings_get_cmds(v)
+        end
+        return bindings
+    end
+end
+
+
diff --git a/ioncore/ioncore_efbb.lua b/ioncore/ioncore_efbb.lua
new file mode 100644 (file)
index 0000000..156a95d
--- /dev/null
@@ -0,0 +1,37 @@
+--
+-- ion/share/ioncore_efbb.lua -- Minimal emergency fallback bindings.
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+warn(TR([[
+Making the following minimal emergency mappings:
+  F2 -> xterm
+  F11 -> restart
+  F12 -> exit
+  Mod1+C -> close
+  Mod1+K P/N -> WFrame.switch_next/switch_prev
+]]))
+
+
+defbindings("WScreen", {
+    kpress("F2", function() ioncore.exec('xterm')  end),
+    kpress("F11", function() ioncore.restart() end),
+    kpress("F12", function() ioncore.exit() end),
+})
+
+defbindings("WMPlex", {
+    kpress_wait("Mod1+C", WRegion.rqclose_propagate),
+})
+
+defbindings("WFrame", {
+    submap("Mod1+K", {
+        kpress("AnyModifier+N", function(f) f:switch_next() end),
+        kpress("AnyModifier+P", function(f) f:switch_prev() end),
+    })
+})
diff --git a/ioncore/ioncore_ext.lua b/ioncore/ioncore_ext.lua
new file mode 100644 (file)
index 0000000..cc56732
--- /dev/null
@@ -0,0 +1,94 @@
+--
+-- ion/share/ioncore_ext.lua -- Ioncore Lua library
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+-- This is a slight abuse of the package.loaded variable perhaps, but
+-- library-like packages should handle checking if they're loaded instead of
+-- confusing the user with require/includer differences.
+if package.loaded["ioncore"] then return end
+
+-- Default modifiers
+--MOD1="Mod1+"
+--MOD2=""
+
+-- Maximum number of bytes to read from pipes
+ioncore.RESULT_DATA_LIMIT=1024^2
+
+-- Bindings, winprops, hooks, menu database and extra commands
+dopath('ioncore_luaext')
+dopath('ioncore_bindings')
+dopath('ioncore_winprops')
+dopath('ioncore_misc')
+dopath('ioncore_wd')
+dopath('ioncore_menudb')
+
+-- Modifier setup compatibility kludge
+local oldindex
+
+local function getmod(t, s)
+    if s=="META" then
+        return rawget(t, "MOD1") or "Mod1+"
+    elseif s=="MOD1" then
+        return rawget(t, "META") or "Mod1+"
+    elseif s=="ALTMETA" then
+        return rawget(t, "MOD2") or ""
+    elseif s=="MOD2" then
+        return rawget(t, "ALTMETA") or ""
+    elseif oldindex then
+        return oldindex(t, s)
+    end
+end
+
+local oldmeta, newmeta=getmetatable(_G), {}
+if oldmeta then
+    newmeta=table.copy(oldmeta)
+    oldindex=oldmeta.__index
+end
+newmeta.__index=getmod
+setmetatable(_G, newmeta)
+
+-- Export some important functions into global namespace.
+export(ioncore, 
+       "submap",
+       "kpress",
+       "kpress_wait",
+       "mpress",
+       "mclick",
+       "mdblclick",
+       "mdrag",
+       "defbindings",
+       "defwinprop",
+       "warn",
+       "exec",
+       "TR",
+       "bdoc",
+       "defmenu",
+       "defctxmenu",
+       "menuentry",
+       "submenu")
+
+-- Mark ourselves loaded.
+package.loaded["ioncore"]=true
+
+
+
+local function dummy_gettext_hack()
+    -- Extra translations for context menus etc. I don't want extra
+    -- TR calls in the configuration files, or parsing the string 
+    -- parameters to kpress etc. for translations.
+    TR("Frame")
+    TR("Screen")
+    TR("Workspace")
+    TR("Tiling")
+    TR("Tiled frame")
+    TR("Floating frame")
+    TR("Context menu:")
+    TR("Main menu:")
+end
diff --git a/ioncore/ioncore_luaext.lua b/ioncore/ioncore_luaext.lua
new file mode 100644 (file)
index 0000000..cb52b97
--- /dev/null
@@ -0,0 +1,92 @@
+--
+-- ion/share/ioncore_luaext.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+
+--DOC
+-- Make \var{str} shell-safe.
+function string.shell_safe(str)
+    return "'"..string.gsub(str, "'", "'\\''").."'"
+end
+
+
+--DOC
+-- Make copy of \var{table}. If \var{deep} is unset, shallow one-level
+-- copy is made, otherwise a deep copy is made.
+function table.copy(t, deep)
+    local function docopy(t, deep, seen)
+        local nt={}
+        for k, v in pairs(t) do
+            local v2=v
+            if deep and type(v)=="table" then
+                if seen[v] then
+                    error(TR("Recursive table - unable to deepcopy"))
+                end
+                seen[v]=true
+                v2=docopy(v, deep, seen)
+                seen[v]=nil
+            end
+            nt[k]=v2
+        end
+        return nt
+    end
+    return docopy(t, deep, deep and {})
+end
+
+
+--DOC
+-- Add entries that do not exist in \var{t1} from \var{t2} to \var{t1}.
+function table.append(t1, t2)
+    for k, v in pairs(t2) do
+        if t1[k]==nil then
+            t1[k]=v
+        end
+    end
+    return t1
+end
+
+
+--DOC
+-- Create a table containing all entries from \var{t1} and those from
+-- \var{t2} that are missing from \var{t1}.
+function table.join(t1, t2)
+    return table.append(table.copy(t1, false), t2)
+end
+
+
+--DOC
+-- Insert all positive integer entries from t2 into t1.
+function table.icat(t1, t2)
+    for _, v in ipairs(t2) do
+        table.insert(t1, v)
+    end
+    return t1
+end
+
+
+--DOC
+-- Map all entries of \var{t} by \var{f}.
+function table.map(f, t)
+    local res={}
+    for k, v in pairs(t) do
+        res[k]=f(v)
+    end
+    return res
+end
+
+
+--DOC
+-- Export a list of functions from \var{lib} into global namespace.
+function export(lib, ...)
+    for k, v in pairs(arg) do
+        _G[v]=lib[v]
+    end
+end
+
diff --git a/ioncore/ioncore_menudb.lua b/ioncore/ioncore_menudb.lua
new file mode 100644 (file)
index 0000000..6032b94
--- /dev/null
@@ -0,0 +1,354 @@
+--
+-- ion/ioncore/ioncore_menudb.lua -- Routines for defining menus.
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+local ioncore=_G.ioncore
+
+
+-- Table to hold defined menus.
+local menus={}
+
+
+-- Menu construction {{{
+
+--DOC
+-- Define a new menu with \var{name} being the menu's name and \var{tab} 
+-- being a table of menu entries. If \var{tab.append} is set, the entries 
+-- are appended to previously-defined ones, if possible.
+function ioncore.defmenu(name, tab)
+    if menus[name] and type(tab)=="table" and tab.append then
+        if type(menus[name])~="table" then
+            ioncore.warn(TR("Unable to append to non-table menu"))
+            return
+        else
+            table.append(menus[name], tab)
+        end
+    else
+        menus[name]=tab
+    end
+end
+
+--DOC
+-- Returns a menu defined with \fnref{ioncore.defmenu}.
+function ioncore.getmenu(name)
+    return menus[name]
+end
+
+--DOC
+-- Define context menu for context \var{ctx}, \var{tab} being a table 
+-- of menu entries.
+function ioncore.defctxmenu(ctx, ...)
+    local tab, add
+    if #arg>1 and type(arg[1])=="string" then
+        tab=arg[2]
+        tab.label=ioncore.gettext(arg[1])
+    else
+        tab=arg[1]
+    end
+    ioncore.defmenu("ctxmenu-"..ctx, tab)
+end
+
+--DOC
+-- Returns a context menu defined with \fnref{ioncore.defctxmenu}.
+function ioncore.getctxmenu(name)
+    return menus["ctxmenu-"..name]
+end
+
+
+function ioncore.evalmenu(menu, args)
+    if type(menu)=="string" then
+        return ioncore.evalmenu(menus[menu], args)
+    elseif type(menu)=="function" then
+        if args then
+            return menu(unpack(args))
+        else
+            return menu()
+        end
+    elseif type(menu)=="table" then
+        return menu
+    end
+end
+
+
+--DOC
+-- Use this function to define normal menu entries. The string \var{name} 
+-- is the string shown in the visual representation of menu, and the
+-- parameter \var{cmd} and \var{guard} are similar to those of
+-- \fnref{ioncore.defbindings}.
+function ioncore.menuentry(name, cmd, guard)
+    local fn, gfn=ioncore.compile_cmd(cmd, guard)
+    if fn then
+        return {name=ioncore.gettext(name), func=fn, guard_func=gfn}
+    end
+end
+
+--DOC
+-- Use this function to define menu entries for submenus. The parameter
+-- \fnref{sub_or_name} is either a table of menu entries or the name
+-- of an already defined menu. The initial menu entry to highlight can be
+-- specified by \var{options.initial} as either an integer starting from 1, 
+-- or a  function that returns such a number. Another option supported is
+-- \var{options.noautoexpand} that will cause \fnref{mod_query.query_menu}
+-- to not automatically expand this submenu.
+function ioncore.submenu(name, sub_or_name, options)
+    if not options then
+        options={}
+    end
+    return {
+        name=ioncore.gettext(name),
+        submenu_fn=function()
+                       return ioncore.evalmenu(sub_or_name)
+                   end,
+        initial=options.initial,
+        noautoexpand=options.noautoexpand,
+    }
+end
+
+
+-- }}}
+
+
+-- Workspace and window lists {{{
+
+local function makelist(list)
+    local function mkentry(tgt)
+        return menuentry(tgt:name(), function() tgt:goto() end)
+    end
+    local entries=table.map(mkentry, list)
+    table.sort(entries, function(a, b) return a.name < b.name end)
+    return entries
+end
+
+function menus.windowlist()
+    return makelist(ioncore.clientwin_list())
+end
+
+function menus.workspacelist()
+    return makelist(ioncore.region_list("WGenWS"))
+end
+
+-- }}}
+
+
+-- Style menu {{{
+
+
+local function mplex_of(reg)
+    while reg and not obj_is(reg, "WMPlex") do
+        reg=reg:parent()
+    end
+    return reg
+end
+
+local function selectstyle(look, where)
+    dopath(look)
+
+    local fname=ioncore.get_savefile('look')
+
+    local function writeit()
+        local f, err=io.open(fname, 'w')
+        if not f then
+            mod_query.message(where, err)
+        else
+            f:write(string.format('dopath("%s")\n', look))
+            f:close()
+        end
+    end
+
+    if not mod_query then
+        if fname then
+            writeit()
+        end
+        return
+    end
+    
+    where=mplex_of(where)
+    if not where then 
+        return 
+    end
+    
+    if not fname then
+        query_message(where, TR("Cannot save selection."))
+        return
+    end
+    
+    mod_query.query_yesno(where, TR("Save look selection in %s?", fname),
+                          writeit)
+end
+
+local function receive_styles(str)
+    local data=""
+    
+    while str do
+        data=data .. str
+        if string.len(data)>ioncore.RESULT_DATA_LIMIT then
+            error(TR("Too much result data"))
+        end
+        str=coroutine.yield()
+    end
+    
+    local found={}
+    local styles={}
+    local stylemenu={}
+    
+    for look in string.gfind(data, "(look[-_][^\n]*)%.lua\n") do
+        if not found[look] then
+            found[look]=true
+            table.insert(styles, look)
+        end
+    end
+    
+    table.sort(styles)
+    
+    for _, look in ipairs(styles) do
+        local look_=look
+        table.insert(stylemenu, menuentry(look,  
+                                          function(where)
+                                              selectstyle(look_, where)
+                                          end))
+    end
+    
+    table.insert(stylemenu, menuentry(TR("Refresh list"),
+                                      ioncore.refresh_stylelist))
+    
+    menus.stylemenu=stylemenu
+end
+
+
+--DOC
+-- Refresh list of known style files.
+function ioncore.refresh_stylelist()
+    local cmd=ioncore.lookup_script("ion-completefile")
+    if cmd then
+        local path=ioncore.get_paths().searchpath
+        local function mkarg(s)
+            if s=="" then
+                return ""
+            else
+                return (" "..string.shell_safe(s).."/look_"..
+                        " "..string.shell_safe(s).."/look-")
+            end
+            return ""
+        end
+
+        cmd=cmd..string.gsub(path..":", "([^:]*):", mkarg)
+        
+        ioncore.popen_bgread(cmd, coroutine.wrap(receive_styles))
+    end
+end
+
+-- }}}
+
+
+-- Context menu {{{
+
+
+local function classes(reg)
+    local function classes_(t)
+        if t.__parentclass then
+            classes_(t.__parentclass)
+        end
+        coroutine.yield(t.__typename)
+    end
+    return coroutine.wrap(function() classes_(reg) end)
+end
+
+
+local function modeparts(mode)
+    if not mode then
+        return function() return end
+    end
+    
+    local f, s, v=string.gmatch(mode, "(%-?[^-]+)");
+    
+    local function nxt(_, m)
+        v = f(s, v)
+        return (v and (m .. v))
+    end
+    
+    return nxt, nil, ""
+end
+
+
+local function get_ctxmenu(reg, sub, is_par)
+    local m={}
+    
+    local function cp(m2)
+        local m3={}
+        for k, v in ipairs(m2) do
+            local v2=table.copy(v)
+            
+            if v2.func then
+                local ofunc=v2.func
+                v2.func=function() return ofunc(reg, sub) end
+            end
+            
+            if v2.submenu_fn then
+                local ofn=v2.submenu_fn
+                v2.submenu_fn=function() return cp(ofn()) end
+            end
+            
+            m3[k]=v2
+        end
+        m3.label=m2.label
+        return m3
+    end
+    
+    local function add_ctxmenu(m2, use_label)
+        if m2 then
+            if is_par then
+                m2=cp(m2)
+            end
+
+            m=table.icat(m, m2)
+            m.label=(use_label and m2.label) or m.label
+        end
+    end
+    
+    local mgr=reg:manager()
+    local mgrname=(mgr and mgr:name()) or nil
+    local mode=(reg.mode and reg:mode())
+
+    for s in classes(reg) do
+        local nm="ctxmenu-"..s
+        add_ctxmenu(ioncore.evalmenu(nm), true)
+        for m in modeparts(mode) do
+            add_ctxmenu(ioncore.evalmenu(nm.."."..m), false)
+        end
+        if mgrname then
+            add_ctxmenu(ioncore.evalmenu(nm.."@"..mgrname), false)
+        end
+    end
+    return m
+end
+
+function menus.ctxmenu(reg, sub)
+    local m=get_ctxmenu(reg, sub, false);
+    
+    sub=reg
+    reg=reg:manager()
+    
+    while reg do
+        local mm = get_ctxmenu(reg, sub, true)
+        if #mm>0 then
+            local nm=mm.label or obj_typename(reg)
+            table.insert(m, ioncore.submenu(nm, mm))
+        end
+        sub=reg
+        reg=reg:manager()
+    end
+    
+    return m
+end
+
+-- }}}
+
+ioncore.refresh_stylelist()
+
diff --git a/ioncore/ioncore_misc.lua b/ioncore/ioncore_misc.lua
new file mode 100644 (file)
index 0000000..fd72b4d
--- /dev/null
@@ -0,0 +1,53 @@
+--
+-- ion/share/ioncore_misc.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+
+local group_tmpl={type="WGroupWS", switchto=true}
+
+--DOC
+-- Create new workspace on screen \var{scr}. The table \var{tmpl}
+-- may be used to override parts of \code{default_ws_params},
+-- and \var{no_default} may be set to \code{true} to complete ignore it.
+function ioncore.create_ws(scr, tmpl, no_default)
+    local dflt=(not no_default and ioncore.get().default_ws_params) or {}
+    local t=table.join(table.join(tmpl or {}, dflt), group_tmpl)
+    
+    return scr:attach_new(t)
+end
+
+
+--DOC
+-- Find an object with type name \var{t} managing \var{obj} or one of
+-- its managers.
+function ioncore.find_manager(obj, t)
+    while obj~=nil do
+        if obj_is(obj, t) then
+            return obj
+        end
+        obj=obj:manager()
+    end
+end
+
+
+--DOC
+-- gettext+string.format
+function ioncore.TR(s, ...)
+    return string.format(ioncore.gettext(s), unpack(arg))
+end
+
+
+--[[DOC
+-- Run \var{cmd} with the environment variable DISPLAY set to point to the
+-- root window of the X screen \var{reg} is on.
+function ioncore.exec_on(reg, cmd)
+    return ioncore.do_exec_rw(reg:rootwin_of(), cmd)
+end
+--]]
diff --git a/ioncore/ioncore_wd.lua b/ioncore/ioncore_wd.lua
new file mode 100644 (file)
index 0000000..84107b8
--- /dev/null
@@ -0,0 +1,167 @@
+--
+-- ion/share/ioncore_wd.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+local savefile="saved_wd"
+local dirs={}
+local px
+
+if pcall(function() return require('posix') end) then
+    px=posix
+end
+
+local function checkdir(d)
+    if not px then
+        return true
+    else
+        local t, err=px.stat(d, "type")
+        if not t then
+            return nil, err
+        elseif t=="link" then
+            local d2, err=px.readlink(d)
+            if not d2 then
+                return nil, err
+            else
+                print('follow')
+                return checkdir(d2)
+            end
+        elseif t=="directory" then
+            return true
+        else
+            return TR("Not a directory.")
+        end
+    end
+end
+
+local function regtreepath_i(reg)
+    local function f(s, v)
+        if v then
+            return v:manager()
+        else
+            return s
+        end
+    end
+    return f, reg, nil
+end
+
+--DOC
+-- Change default working directory for new programs started in \var{reg}.
+function ioncore.chdir_for(reg, dir)
+    assert(type(dir)=="string")
+    if dir=="" or dir==nil then
+        dirs[reg]=nil
+        return true
+    else 
+        local ok, err=checkdir(dir)
+        if ok then
+            dirs[reg]=dir
+        end
+        return ok, err
+    end
+end
+
+--DOC
+-- Get default working directory for new programs started in \var{reg}.
+function ioncore.get_dir_for(reg)
+    for r in regtreepath_i(reg) do
+        if dirs[r] then
+            return dirs[r]
+        end
+    end
+end
+
+
+local function lookup_script_warn(script)
+    local script=ioncore.lookup_script(script)
+    if not script then
+        warn(TR("Could not find %s", script))
+    end
+    return script
+end
+
+
+local function lookup_runinxterm_warn(prog, title, wait)
+    local rx=lookup_script_warn("ion-runinxterm")
+    if rx then
+        if wait then
+            rx=rx.." -w"
+        end
+        if title then
+            rx=rx.." -T "..string.shell_safe(title)
+        end
+        if prog then
+            rx=rx.." -- "..prog
+        end
+    end
+    return rx
+end
+
+
+--DOC
+-- Run \var{cmd} with the environment variable DISPLAY set to point to the
+-- root window of the X screen \var{reg} is on. If \var{cmd} is prefixed
+-- by a colon (\code{:}), the following command is executed in an xterm
+-- (or other terminal emulator) with the help of the \command{ion-runinxterm} 
+-- script. If the command is prefixed by two colons, \command{ion-runinxterm}
+-- will ask you to press enter after the command is finished, even if it
+-- returns succesfully.
+function ioncore.exec_on(reg, cmd, merr_internal)
+    local _, _, col, c=string.find(cmd, "^[%s]*(:+)(.*)")
+    if col then
+        cmd=lookup_runinxterm_warn(c, nil, string.len(col)>1)
+        if not cmd then
+            return
+        end
+        if XTERM then
+            cmd='XTERMCMD='..string.shell_safe(XTERM)..' '..cmd
+        end
+    end
+    return ioncore.do_exec_rw(reg:rootwin_of(), cmd, 
+                              ioncore.get_dir_for(reg), 
+                              merr_internal)
+end
+
+
+local function load_config()
+    local d=ioncore.read_savefile(savefile)
+    if d then
+        dirs={}
+        for nm, d in pairs(d) do
+            local r=ioncore.lookup_region(nm)
+            if r then
+                local ok, err=checkdir(d)
+                if ok then
+                    dirs[r]=d
+                else
+                    warn(err)
+                end
+            end
+        end
+    end
+end
+
+
+local function save_config()
+    local t={}
+    for r, d in pairs(dirs) do
+        t[r:name()]=d
+    end
+    ioncore.write_savefile(savefile, t)
+end
+
+
+local function init()
+    load_config()
+    ioncore.get_hook("ioncore_snapshot_hook"):add(save_config)
+    ioncore.get_hook("ioncore_post_layout_setup_hook"):add(load_config)
+end
+
+init()
+
diff --git a/ioncore/ioncore_winprops.lua b/ioncore/ioncore_winprops.lua
new file mode 100644 (file)
index 0000000..333ff69
--- /dev/null
@@ -0,0 +1,129 @@
+--
+-- ion/share/ioncore_winprops.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+local ioncore=_G.ioncore
+
+local winprops={}
+
+local function ifnil(...)
+    local n=#arg
+    local function nxt(_, i)
+        local j=i+1
+        if i==n then
+            return nil
+        else
+            local j=i+1
+            if not arg[j] then
+                return nxt(nil, j)
+            else
+                return j, arg[j]
+            end
+        end
+    end
+            
+    return nxt, nil, 0
+end
+
+local function ipairs_r(tab)
+    local function nxt(_, n)
+        if n==1 then
+            return nil
+        else
+            return n-1, tab[n-1]
+        end
+    end
+    return nxt, nil, #tab+1
+end
+
+--DOC
+-- Find winprop table for \var{cwin}.
+function ioncore.getwinprop(cwin)
+    local id=cwin:get_ident()
+    local props, prop
+
+    for _, c in ifnil(id.class, "*") do
+        for _, r in ifnil(id.role, "*") do
+            for _, i in ifnil(id.instance, "*") do
+                --printpp(c, r, i)
+                props={}
+                pcall(function() props=winprops[c][r][i] or {} end)
+                for idx, prop in ipairs_r(props) do
+                    if prop:match(cwin) then
+                        if prop.oneshot then
+                            table.remove(props, idx)
+                        end
+                        return prop
+                    end
+                end
+            end
+        end
+    end
+end
+
+ioncore.set_get_winprop_fn(ioncore.getwinprop)
+
+local function ensure_winproptab(class, role, instance)
+    if not winprops[class] then
+        winprops[class]={}
+    end
+    if not winprops[class][role] then
+        winprops[class][role]={}
+    end
+    if not winprops[class][role][instance] then
+        winprops[class][role][instance]={}
+    end
+end    
+
+local function do_add_winprop(class, role, instance, name, prop)
+    ensure_winproptab(class, role, instance)
+    table.insert(winprops[class][role][instance], prop)
+end
+
+
+--DOC
+-- The basic name-based winprop matching criteria.
+function ioncore.match_winprop_name(prop, cwin)
+    local nm=cwin:name()
+    if not prop.name then
+        return true
+    elseif nm then
+        local st, en=string.find(nm, prop.name)
+        return (st and en)
+    else
+        return false
+    end
+end
+
+
+--DOC
+-- Define a winprop. For more information, see section \ref{sec:winprops}.
+function ioncore.defwinprop(list)
+    local list2 = {}
+    local class, role, instance = "*", "*", "*"
+    
+    for k, v in pairs(list) do
+        if k == "class" then
+            class = v
+        elseif k == "role" then
+            role = v
+        elseif k == "instance" then
+            instance = v
+        end
+        list2[k] = v
+    end
+    
+    if not list2.match then
+        list2.match=ioncore.match_winprop_name
+    end
+    
+    do_add_winprop(class, role, instance, name, list2)
+end
+
diff --git a/ioncore/kbresize.c b/ioncore/kbresize.c
new file mode 100644 (file)
index 0000000..b427228
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * ion/ioncore/kbresize.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <math.h>
+#include <sys/time.h>
+#include <time.h>
+#include <limits.h>
+
+#include <libtu/minmax.h>
+
+#include <libmainloop/signal.h>
+
+#include "global.h"
+#include "resize.h"
+#include "kbresize.h"
+#include "grab.h"
+#include "binding.h"
+#include "focus.h"
+#include "bindmaps.h"
+
+
+/*{{{ Resize accelerator */
+
+
+static struct timeval last_action_tv={-1, 0};
+static struct timeval last_update_tv={-1, 0};
+static int last_accel_mode=0;
+static double accel=1, accelinc=30, accelmax=100*100;
+static long actmax=200, uptmin=50;
+static int resize_delay=CF_RESIZE_DELAY;
+
+
+static void accel_reset()
+{
+    last_accel_mode=0;
+    accel=1.0;
+    last_action_tv.tv_sec=-1;
+    last_action_tv.tv_usec=-1;
+}
+
+
+void ioncore_set_moveres_accel(ExtlTab tab)
+{
+    int t_max, t_min, rd;
+    double step, maxacc;
+    
+    if(extl_table_gets_i(tab, "kbresize_t_max", &t_max))
+       actmax=(t_max>0 ? t_max : INT_MAX);
+    if(extl_table_gets_i(tab, "kbresize_t_min", &t_min))
+       uptmin=(t_min>0 ? t_min : INT_MAX);
+    if(extl_table_gets_d(tab, "kbresize_step", &step))
+       accelinc=(step>0 ? step : 1);
+    if(extl_table_gets_d(tab, "kbresize_maxacc", &maxacc))
+       accelmax=(maxacc>0 ? maxacc*maxacc : 1);
+    if(extl_table_gets_i(tab, "kbresize_delay", &rd))
+        resize_delay=maxof(0, rd);
+}
+
+
+void ioncore_get_moveres_accel(ExtlTab tab)
+{
+    extl_table_sets_i(tab, "kbresize_t_max", actmax),
+    extl_table_sets_i(tab, "kbresize_t_min", uptmin);
+    extl_table_sets_d(tab, "kbresize_step", accelinc);
+    extl_table_sets_d(tab, "kbresize_maxacc", accelmax);
+    extl_table_sets_d(tab, "kbresize_delay", resize_delay);
+}
+
+
+static int sign(int x)
+{
+    return (x>0 ? 1 : (x<0 ? -1 : 0));
+}
+
+
+static long tvdiffmsec(struct timeval *tv1, struct timeval *tv2)
+{
+    double t1=1000*(double)tv1->tv_sec+(double)tv1->tv_usec/1000;
+    double t2=1000*(double)tv2->tv_sec+(double)tv2->tv_usec/1000;
+    
+    return (int)(t1-t2);
+}
+
+#define SIGN_NZ(X) ((X) < 0 ? -1 : 1)
+
+static double max(double a, double b)
+{
+    return (a<b ? b : a);
+}
+
+void moveresmode_accel(WMoveresMode *mode, int *wu, int *hu, int accel_mode)
+{
+    struct timeval tv;
+    long adiff, udiff;
+    
+    gettimeofday(&tv, NULL);
+    
+    adiff=tvdiffmsec(&tv, &last_action_tv);
+    udiff=tvdiffmsec(&tv, &last_update_tv);
+    
+    if(last_accel_mode==accel_mode && adiff<actmax){
+        if(udiff>uptmin){
+            accel+=accelinc;
+            if(accel>accelmax)
+                accel=accelmax;
+            last_update_tv=tv;
+        }
+    }else{
+        accel=1.0;
+        last_update_tv=tv;
+    }
+    
+    last_accel_mode=accel_mode;
+    last_action_tv=tv;
+    
+    if(*wu!=0)
+        *wu=(*wu)*ceil(sqrt(accel)/abs(*wu));
+    if(*hu!=0)
+        *hu=(*hu)*ceil(sqrt(accel)/abs(*hu));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Keyboard resize handler */
+
+
+static ExtlExportedFn *moveres_safe_fns[]={
+    (ExtlExportedFn*)&moveresmode_resize,
+    (ExtlExportedFn*)&moveresmode_move,
+    (ExtlExportedFn*)&moveresmode_finish,
+    (ExtlExportedFn*)&moveresmode_cancel,
+    NULL
+};
+
+static ExtlSafelist moveres_safelist=EXTL_SAFELIST_INIT(moveres_safe_fns);
+
+
+static bool resize_handler(WRegion *reg, XEvent *xev)
+{
+    XKeyEvent *ev=&xev->xkey;
+    WBinding *binding=NULL;
+    WBindmap **bindptr;
+    WMoveresMode *mode;
+    
+    if(ev->type==KeyRelease)
+        return FALSE;
+    
+    if(reg==NULL)
+        return FALSE;
+    
+    mode=moveres_mode(reg);
+    
+    if(mode==NULL)
+        return FALSE;
+    
+    binding=bindmap_lookup_binding(ioncore_moveres_bindmap, 
+                                   BINDING_KEYPRESS,
+                                   ev->state, ev->keycode);
+    
+    if(!binding)
+        return FALSE;
+    
+    if(binding!=NULL){
+        extl_protect(&moveres_safelist);
+        extl_call(binding->func, "oo", NULL, mode, reg);
+        extl_unprotect(&moveres_safelist);
+    }
+    
+    return (moveres_mode(reg)==NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Resize timer */
+
+
+static WTimer *resize_timer=NULL;
+
+
+static void tmr_end_resize(WTimer *unused, WMoveresMode *mode)
+{
+    if(mode!=NULL)
+        moveresmode_cancel(mode);
+}
+
+
+static bool setup_resize_timer(WMoveresMode *mode)
+{
+    if(resize_timer==NULL){
+        resize_timer=create_timer();
+        if(resize_timer==NULL)
+            return FALSE;
+    }
+    
+    timer_set(resize_timer, resize_delay, 
+              (WTimerHandler*)tmr_end_resize, (Obj*)mode);
+    
+    return TRUE;
+}
+
+
+static void reset_resize_timer()
+{
+    if(resize_timer!=NULL){
+        timer_reset(resize_timer);
+        destroy_obj((Obj*)resize_timer);
+        resize_timer=NULL;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+static int limit_and_encode_mode(int *left, int *right, 
+                                 int *top, int *bottom)
+{
+    *left=sign(*left);
+    *right=sign(*right);
+    *top=sign(*top);
+    *bottom=sign(*bottom);
+
+    return (*left)+(*right)*3+(*top)*9+(*bottom)*27;
+}
+
+
+static void resize_units(WMoveresMode *mode, int *wret, int *hret)
+{
+    WSizeHints *h=&(mode->hints);
+    *wret=1;
+    *hret=1;
+    if(h->inc_set && (h->width_inc>1 || h->height_inc>1)){
+        *wret=h->width_inc;
+        *hret=h->height_inc;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Keyboard resize interface */
+
+
+/*EXTL_DOC
+ * Shrink or grow resize mode target one step in each direction.
+ * Acceptable values for the parameters \var{left}, \var{right}, \var{top}
+ * and \var{bottom} are as follows: -1: shrink along,
+ * 0: do not change, 1: grow along corresponding border.
+ */
+EXTL_EXPORT_MEMBER
+void moveresmode_resize(WMoveresMode *mode, 
+                        int left, int right, int top, int bottom)
+{
+    int wu=0, hu=0;
+    int accel_mode=0;
+    
+    if(!setup_resize_timer(mode))
+        return;
+    
+    accel_mode=3*limit_and_encode_mode(&left, &right, &top, &bottom);
+    resize_units(mode, &wu, &hu);
+    moveresmode_accel(mode, &wu, &hu, accel_mode);
+
+    moveresmode_delta_resize(mode, -left*wu, right*wu, -top*hu, bottom*hu, 
+                             NULL);
+}
+
+
+/*EXTL_DOC
+ * Move resize mode target one step:
+ *
+ * \begin{tabular}{rl}
+ * \hline
+ * \var{horizmul}/\var{vertmul} & effect \\\hline
+ * -1 & Move left/up \\
+ * 0 & No effect \\
+ * 1 & Move right/down \\
+ * \end{tabular}
+ */
+EXTL_EXPORT_MEMBER
+void moveresmode_move(WMoveresMode *mode, int horizmul, int vertmul)
+{
+    int accel_mode=0, dummy=0;
+
+    if(!setup_resize_timer(mode))
+        return;
+    
+    accel_mode=1+3*limit_and_encode_mode(&horizmul, &vertmul, &dummy, &dummy);
+    moveresmode_accel(mode, &horizmul, &vertmul, accel_mode);
+
+    moveresmode_delta_resize(mode, horizmul, horizmul, vertmul, vertmul, 
+                             NULL);
+}
+
+
+/*EXTL_DOC
+ * Return from move/resize mode and apply changes unless opaque
+ * move/resize is enabled.
+ */
+EXTL_EXPORT_MEMBER
+void moveresmode_finish(WMoveresMode *mode)
+{
+    WRegion *reg=moveresmode_target(mode);
+    if(moveresmode_do_end(mode, TRUE)){
+        reset_resize_timer();
+        region_warp(reg);
+        ioncore_grab_remove(resize_handler);
+    }
+}
+
+
+/*EXTL_DOC
+ * Return from move/resize cancelling changes if opaque
+ * move/resize has not been enabled.
+ */
+EXTL_EXPORT_MEMBER
+void moveresmode_cancel(WMoveresMode *mode)
+{
+    WRegion *reg=moveresmode_target(mode);
+    if(moveresmode_do_end(mode, FALSE)){
+        reset_resize_timer();
+        region_warp(reg);
+        ioncore_grab_remove(resize_handler);
+    }
+}
+
+
+static void cancel_moveres(WRegion *reg)
+{
+    WMoveresMode *mode=moveres_mode(reg);
+    if(mode!=NULL)
+        moveresmode_cancel(mode);
+}
+
+    
+/*EXTL_DOC
+ * Enter move/resize mode for \var{reg}. The bindings set with
+ * \fnref{ioncore.set_bindings} for \type{WMoveresMode} are used in 
+ * this mode. Of the functions exported by the Ion C core, only
+ * \fnref{WMoveresMode.resize}, \fnref{WMoveresMode.move}, 
+ * \fnref{WMoveresMode.cancel} and \fnref{WMoveresMode.end} are
+ * allowed to be called while in this mode.
+ */
+EXTL_EXPORT_MEMBER
+WMoveresMode *region_begin_kbresize(WRegion *reg)
+{
+    WMoveresMode *mode=region_begin_resize(reg, NULL, FALSE);
+
+    if(mode==NULL)
+        return NULL;
+    
+    if(!setup_resize_timer(mode))
+        return NULL;
+
+    accel_reset();
+    
+    ioncore_grab_establish(reg, resize_handler,
+                           (GrabKilledHandler*)cancel_moveres, 0);
+    
+    return mode;
+}
+
+
+/*}}}*/
diff --git a/ioncore/kbresize.h b/ioncore/kbresize.h
new file mode 100644 (file)
index 0000000..3af0781
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * ion/ioncore/kbresize.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_KBRESIZE_H
+#define ION_IONCORE_KBRESIZE_H
+
+#include "common.h"
+#include "frame.h"
+#include "resize.h"
+#include <libextl/extl.h>
+
+extern WMoveresMode *region_begin_kbresize(WRegion *reg);
+
+extern void ioncore_set_moveres_accel(ExtlTab tab);
+extern void ioncore_get_moveres_accel(ExtlTab tab);
+
+extern void moveresmode_finish(WMoveresMode *mode);
+extern void moveresmode_cancel(WMoveresMode *mode);
+extern void moveresmode_move(WMoveresMode *mode, 
+                             int horizmul, int vertmul);
+extern void moveresmode_resize(WMoveresMode *mode, 
+                               int left, int right, int top, int bottom);
+extern void moveresmode_accel(WMoveresMode *mode, 
+                              int *wu, int *hu, int accel_mode);
+
+#endif /* ION_IONCORE_KBRESIZE_H */
diff --git a/ioncore/key.c b/ioncore/key.c
new file mode 100644 (file)
index 0000000..00848a4
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * ion/ioncore/key.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <ctype.h>
+#include <libtu/objp.h>
+#include "common.h"
+#include "key.h"
+#include "binding.h"
+#include "global.h"
+#include "event.h"
+#include "cursor.h"
+#include "grab.h"
+#include "regbind.h"
+#include <libextl/extl.h>
+#include "strings.h"
+#include "xwindow.h"
+
+
+static void waitrelease(WRegion *reg);
+static void submapgrab(WRegion *reg);
+
+
+static void insstr(WWindow *wwin, XKeyEvent *ev)
+{
+    static XComposeStatus cs={NULL, 0};
+    char buf[32]={0,};
+    Status stat;
+    int n, i;
+    KeySym ksym;
+    
+    if(wwin->xic!=NULL){
+        if(XFilterEvent((XEvent*)ev, ev->window))
+           return;
+        n=XmbLookupString(wwin->xic, ev, buf, 16, &ksym, &stat);
+        if(stat!=XLookupChars && stat!=XLookupBoth)
+            return;
+    }else{
+        n=XLookupString(ev, buf, 32, &ksym, &cs);
+    }
+    
+    if(n<=0)
+        return;
+    
+    /* Won't catch bad strings, but should filter out most crap. */
+    if(ioncore_g.use_mb){
+        if(!iswprint(str_wchar_at(buf, 32)))
+            return;
+    }else{
+        if(iscntrl(*buf))
+            return;
+    }
+    
+    window_insstr(wwin, buf, n);
+}
+
+
+static void send_key(XEvent *ev, WClientWin *cwin)
+{
+    Window win=cwin->win;
+    ev->xkey.window=win;
+    ev->xkey.subwindow=None;
+    XSendEvent(ioncore_g.dpy, win, False, KeyPressMask, ev);
+}
+
+
+static bool quote_next_handler(WRegion *reg, XEvent *xev)
+{
+    XKeyEvent *ev=&xev->xkey;
+    if(ev->type!=KeyPress)
+        return FALSE;
+    if(ioncore_ismod(ev->keycode))
+        return FALSE;
+    assert(OBJ_IS(reg, WClientWin));
+    send_key(xev, (WClientWin*)reg);
+    return TRUE; /* remove the grab */
+}
+
+
+/*EXTL_DOC
+ * Send next key press directly to \var{cwin}.
+ */
+EXTL_EXPORT_MEMBER
+void clientwin_quote_next(WClientWin *cwin)
+{
+    ioncore_grab_establish((WRegion*)cwin, quote_next_handler, NULL, 0);
+    ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY);
+}
+
+
+static bool waitrelease_handler(WRegion *reg, XEvent *ev)
+{
+    if(!ioncore_unmod(ev->xkey.state, ev->xkey.keycode))
+        return TRUE;
+    return FALSE;
+}
+
+
+static void waitrelease(WRegion *reg)
+{
+    if(ioncore_modstate()==0)
+        return;
+       
+    /* We need to grab on the root window as <reg> might have been
+     * ioncore_defer_destroy:ed by the binding handler (the most common case
+     * for using this kpress_wait!). In such a case the grab may
+     * be removed before the modifiers are released.
+     */
+    ioncore_grab_establish((WRegion*)region_rootwin_of(reg), 
+                           waitrelease_handler, 
+                           NULL, 0);
+    ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY);
+}
+
+
+static void free_subs(WSubmapState *p)
+{
+    WSubmapState *next;
+    
+    while(p!=NULL){
+        next=p->next;
+        free(p);
+        p=next;
+    }
+}
+
+
+static void clear_subs(WRegion *reg)
+{
+    while(reg->submapstat!=NULL){
+        WSubmapState *tmp=reg->submapstat;
+        reg->submapstat=tmp->next;
+        free(tmp);
+    }
+}
+
+
+static bool add_sub(WRegion *reg, uint key, uint state)
+{
+    WSubmapState **p;
+    WSubmapState *s;
+    
+    if(reg->submapstat==NULL){
+        p=&(reg->submapstat);
+    }else{
+        s=reg->submapstat;
+        while(s->next!=NULL)
+            s=s->next;
+        p=&(s->next);
+    }
+    
+    s=ALLOC(WSubmapState);
+    
+    if(s==NULL)
+        return FALSE;
+    
+    s->key=key;
+    s->state=state;
+    
+    *p=s;
+    
+    return TRUE;
+
+}
+
+
+static XKeyEvent *current_key_event=NULL;
+
+
+XKeyEvent *ioncore_current_key_event()
+{
+    return current_key_event;
+}
+
+
+/* Return value TRUE = grab needed */
+static bool do_key(WRegion *reg, XKeyEvent *ev)
+{
+    WBinding *binding=NULL;
+    WRegion *oreg=NULL, *binding_owner=NULL, *subreg=NULL;
+    bool grabbed;
+    
+    oreg=reg;
+    grabbed=(oreg->flags&REGION_BINDINGS_ARE_GRABBED);
+    
+    if(grabbed){
+        /* Find the deepest nested active window grabbing this key. */
+        while(reg->active_sub!=NULL)
+            reg=reg->active_sub;
+        
+        do{
+            binding=region_lookup_keybinding(reg, ev, oreg->submapstat, 
+                                             &binding_owner);
+            
+            if(binding!=NULL)
+                break;
+            if(OBJ_IS(reg, WRootWin))
+                break;
+            
+            subreg=reg;
+            reg=REGION_PARENT_REG(reg);
+        }while(reg!=NULL);
+    }else{
+        binding=region_lookup_keybinding(oreg, ev, oreg->submapstat, 
+                                         &binding_owner);
+    }
+    
+    if(binding!=NULL){
+        if(binding->submap!=NULL){
+            if(add_sub(oreg, ev->keycode, ev->state))
+                return grabbed;
+            else
+                clear_subs(oreg);
+        }else if(binding_owner!=NULL){
+            WRegion *mgd=region_managed_within(binding_owner, subreg);
+            bool subs=(oreg->submapstat!=NULL);
+            
+            clear_subs(oreg);
+            
+            if(grabbed)
+                XUngrabKeyboard(ioncore_g.dpy, CurrentTime);
+            
+            if(!subs)
+                current_key_event=ev;
+                
+            /* TODO: having to pass both mgd and subreg for some handlers
+             * to work is ugly and complex.
+             */
+            extl_call(binding->func, "ooo", NULL, binding_owner, mgd, subreg);
+            
+            current_key_event=NULL;
+            
+            if(ev->state!=0 && !subs && binding->wait)
+                waitrelease(oreg);
+        }
+    }else if(oreg->submapstat!=NULL){
+        clear_subs(oreg);
+    }else if(OBJ_IS(oreg, WWindow)){
+        insstr((WWindow*)oreg, ev);
+    }
+    
+    return FALSE;
+}
+
+
+static bool submapgrab_handler(WRegion* reg, XEvent *xev)
+{
+    XKeyEvent *ev=&xev->xkey;
+    if(ev->type!=KeyPress)
+        return FALSE;
+    if(ioncore_ismod(ev->keycode))
+        return FALSE;
+    return !do_key(reg, ev);
+}
+
+
+static void submapgrab(WRegion *reg)
+{
+    ioncore_grab_establish(reg, submapgrab_handler, clear_subs, 0);
+    ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY);
+}
+
+
+void ioncore_do_handle_keypress(XKeyEvent *ev)
+{
+    WRegion *reg=(WRegion*)XWINDOW_REGION_OF(ev->window);
+    
+    if(reg!=NULL){
+        if(do_key(reg, ev))
+            submapgrab(reg);
+    }
+}
diff --git a/ioncore/key.h b/ioncore/key.h
new file mode 100644 (file)
index 0000000..e5ba29c
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * ion/ioncore/key.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_KEY_H
+#define ION_IONCORE_KEY_H
+
+#include <X11/keysym.h>
+
+#include "common.h"
+#include "clientwin.h"
+
+extern void ioncore_do_handle_keypress(XKeyEvent *ev);
+extern void clientwin_quote_next(WClientWin *cwin);
+extern XKeyEvent *ioncore_current_key_event();
+
+#endif /* ION_IONCORE_KEY_H */
diff --git a/ioncore/llist.c b/ioncore/llist.c
new file mode 100644 (file)
index 0000000..3baca81
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * ion/ioncore/llist.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "common.h"
+#include "llist.h"
+#include "activity.h"
+
+
+/*{{{ WMPlex numbered/mut.ex. list stuff */
+
+
+void llist_iter_init(WLListIterTmp *tmp, WLListNode *llist)
+{
+    *tmp=llist;
+}
+
+
+WLListNode *llist_iter(WLListIterTmp *tmp)
+{
+    WLListNode *mgd=*tmp;
+    if(mgd!=NULL)
+        *tmp=mgd->next;
+    return mgd;
+}
+
+
+WRegion *llist_iter_regions(WLListIterTmp *tmp)
+{
+    WLListNode *lnode=llist_iter(tmp);
+    return (lnode==NULL ? NULL : lnode->st->reg);
+}
+
+
+WLListNode *llist_nth_node(WLListNode *list, uint n)
+{
+    WLListIterTmp tmp;
+    llist_iter_init(&tmp, list);
+    return (WLListNode*)iterable_nth(n, (VoidIterator*)llist_iter, &tmp);
+}
+
+
+void llist_link_after(WLListNode **list, 
+                      WLListNode *after, WLListNode *node)
+{
+    if(after!=NULL){
+        LINK_ITEM_AFTER(*list, after, node, next, prev);
+    }else{
+        LINK_ITEM_FIRST(*list, node, next, prev);
+    }
+}
+
+
+void llist_link_last(WLListNode **list, WLListNode *node)
+{
+    LINK_ITEM_LAST(*list, node, next, prev);
+}
+
+
+WLListNode *llist_index_to_after(WLListNode *list, 
+                                 WLListNode *current,
+                                 int index)
+{
+    if(index==LLIST_INDEX_AFTER_CURRENT_ACT){
+        WLListNode *after=current;
+        while(after!=NULL){
+            WLListNode *nxt=after->next;
+            if(nxt==NULL || nxt->st==NULL || nxt->st->reg==NULL)
+                break;
+            if(!region_is_activity_r(nxt->st->reg))
+                break;
+            after=nxt;
+        }
+        return after;
+    }else if(index==LLIST_INDEX_AFTER_CURRENT){
+        return current;
+    }else if(index<0){
+        return (list!=NULL ? list->prev : NULL);
+    }else if(index==0){
+        return NULL;
+    }else{ /* index>0 */
+        return llist_nth_node(list, index-1);
+    }
+}
+
+
+void llist_unlink(WLListNode **list, WLListNode *node)
+{
+    UNLINK_ITEM(*list, node, next, prev);
+}
+
+
+/*}}}*/
diff --git a/ioncore/llist.h b/ioncore/llist.h
new file mode 100644 (file)
index 0000000..83d148d
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * ion/ioncore/llist.h
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ * 
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_LLIST_H
+#define ION_IONCORE_LLIST_H
+
+#include <limits.h>
+
+#include "mplex.h"
+#include "mplexpholder.h"
+#include "extlconv.h"
+#include "stacking.h"
+
+
+DECLSTRUCT(WLListNode){
+    WLListNode *next, *prev;
+    WMPlexPHolder *phs;
+    WStacking *st;
+};
+
+typedef WLListNode *WLListIterTmp;
+
+
+#define FOR_ALL_NODES_ON_LLIST(NODE, LL, TMP) \
+    FOR_ALL_ITER(llist_iter_init, llist_iter, NODE, LL, &(TMP))
+
+#define FOR_ALL_REGIONS_ON_LLIST(NODE, LL, TMP) \
+    FOR_ALL_ITER(llist_iter_init, llist_iter_regions, NODE, LL, &(TMP))
+
+
+extern void llist_iter_init(WLListIterTmp *tmp, WLListNode *llist);
+extern WLListNode *llist_iter(WLListIterTmp *tmp);
+extern WRegion *llist_iter_regions(WLListIterTmp *tmp);
+extern WLListNode *llist_nth_node(WLListNode *list, uint n);
+extern ExtlTab llist_to_table(WLListNode *list);
+extern void llist_link_after(WLListNode **list, 
+                             WLListNode *after, WLListNode *node);
+extern void llist_link_last(WLListNode **list, WLListNode *node);
+extern WLListNode *llist_index_to_after(WLListNode *list, 
+                                        WLListNode *current,
+                                        int index);
+extern void llist_unlink(WLListNode **list, WLListNode *node);
+
+#define LLIST_INDEX_LAST              (-1)
+#define LLIST_INDEX_AFTER_CURRENT     (-2)
+#define LLIST_INDEX_AFTER_CURRENT_ACT (-3)
+
+#endif /* ION_IONCORE_LLIST_H */
diff --git a/ioncore/manage.c b/ioncore/manage.c
new file mode 100644 (file)
index 0000000..f1d78a0
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * ion/ioncore/manage.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/objp.h>
+#include <libextl/extl.h>
+#include "global.h"
+#include "common.h"
+#include "region.h"
+#include "manage.h"
+#include "names.h"
+#include "fullscreen.h"
+#include "pointer.h"
+#include "netwm.h"
+#include "extlconv.h"
+
+
+/*{{{ Add */
+
+
+WScreen *clientwin_find_suitable_screen(WClientWin *cwin, 
+                                        const WManageParams *param)
+{
+    WScreen *scr=NULL, *found=NULL;
+    bool respectpos=(param->tfor!=NULL || param->userpos);
+    
+    FOR_ALL_SCREENS(scr){
+        if(!region_same_rootwin((WRegion*)scr, (WRegion*)cwin))
+            continue;
+        if(REGION_IS_ACTIVE(scr)){
+            found=scr;
+            if(!respectpos)
+                break;
+        }
+        
+        if(rectangle_contains(&REGION_GEOM(scr), param->geom.x, param->geom.y)){
+            found=scr;
+            if(respectpos)
+                break;
+        }
+        
+        if(found==NULL)
+            found=scr;
+    }
+    
+    return found;
+}
+
+
+bool clientwin_do_manage_default(WClientWin *cwin, 
+                                 const WManageParams *param)
+{
+    WRegion *r=NULL, *r2;
+    WScreen *scr=NULL;
+    WPHolder *ph=NULL;
+    int fs=-1;
+    int swf;
+    bool ok, tmp;
+
+    /* Check if param->tfor or any of its managers want to manage cwin. */
+    if(param->tfor!=NULL){
+        assert(param->tfor!=cwin);
+        ph=region_prepare_manage_transient((WRegion*)param->tfor, cwin, 
+                                           param, 0);
+    }
+    
+    /* Find a suitable screen */
+    scr=clientwin_find_suitable_screen(cwin, param);
+    if(scr==NULL){
+        warn(TR("Unable to find a screen for a new client window."));
+        return FALSE;
+    }
+    
+    if(ph==NULL){
+        /* Find a placeholder for non-fullscreen state */
+        ph=region_prepare_manage((WRegion*)scr, cwin, param,
+                                 MANAGE_REDIR_PREFER_YES);
+    }
+    
+    /* Check fullscreen mode */
+    if(extl_table_gets_b(cwin->proptab, "fullscreen", &tmp))
+        fs=tmp;
+
+    if(fs<0)
+        fs=netwm_check_initial_fullscreen(cwin, param->switchto);
+    
+    if(fs<0){
+        fs=clientwin_check_fullscreen_request(cwin, 
+                                              param->geom.w,
+                                              param->geom.h,
+                                              param->switchto);
+    }
+
+    if(fs>0){
+        /* Full-screen mode succesfull. */
+        if(pholder_target(ph)==(WRegion*)scr){
+            /* Discard useless placeholder. */
+            destroy_obj((Obj*)ph);
+            return TRUE;
+        }
+        assert(cwin->fs_pholder==NULL);
+        cwin->fs_pholder=ph;
+        return TRUE;
+    }
+        
+    if(ph==NULL)
+        return FALSE;
+    
+    /* Not in full-screen mode; use the placeholder to attach. */
+    
+    swf=(param->switchto ? PHOLDER_ATTACH_SWITCHTO : 0);
+    ok=pholder_attach(ph, swf, (WRegion*)cwin);
+    destroy_obj((Obj*)ph);
+    
+    return ok;
+}
+
+
+/*}}}*/
+
+
+/*{{{ region_prepare_manage/region_manage_clientwin/etc. */
+
+
+WPHolder *region_prepare_manage(WRegion *reg, const WClientWin *cwin,
+                                const WManageParams *param, int redir)
+{
+    WPHolder *ret=NULL;
+    CALL_DYN_RET(ret, WPHolder*, region_prepare_manage, reg, 
+                 (reg, cwin, param, redir));
+    return ret;
+}
+
+
+WPHolder *region_prepare_manage_default(WRegion *reg, const WClientWin *cwin,
+                                        const WManageParams *param, int redir)
+{
+    WRegion *curr;
+    
+    if(redir==MANAGE_REDIR_STRICT_NO)
+        return NULL;
+    
+    curr=region_current(reg);
+    
+    if(curr==NULL)
+        return NULL;
+        
+    return region_prepare_manage(curr, cwin, param, MANAGE_REDIR_PREFER_YES);
+}
+
+
+WPHolder *region_prepare_manage_transient(WRegion *reg, 
+                                          const WClientWin *cwin,
+                                          const WManageParams *param,
+                                          int unused)
+{
+    WPHolder *ret=NULL;
+    CALL_DYN_RET(ret, WPHolder*, region_prepare_manage_transient, reg, 
+                 (reg, cwin, param, unused));
+    return ret;
+}
+
+
+WPHolder *region_prepare_manage_transient_default(WRegion *reg, 
+                                                  const WClientWin *cwin,
+                                                  const WManageParams *param,
+                                                  int unused)
+{
+    WRegion *mgr=REGION_MANAGER(reg);
+    
+    if(mgr!=NULL)
+        return region_prepare_manage_transient(mgr, cwin, param, unused);
+    else
+        return NULL;
+}
+
+
+bool region_manage_clientwin(WRegion *reg, WClientWin *cwin,
+                             const WManageParams *par, int redir)
+{
+    bool ret;
+    WPHolder *ph=region_prepare_manage(reg, cwin, par, redir);
+    int swf=(par->switchto ? PHOLDER_ATTACH_SWITCHTO : 0);
+    
+    if(ph==NULL)
+        return FALSE;
+    
+    ret=pholder_attach(ph, swf, (WRegion*)cwin);
+    
+    destroy_obj((Obj*)ph);
+    
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Rescue */
+
+
+static WRegion *iter_children(void *st)
+{
+    WRegion **nextp=(WRegion**)st;
+    WRegion *next=*nextp;
+    *nextp=(next==NULL ? NULL : next->p_next);
+    return next;   
+}
+
+
+bool region_rescue_child_clientwins(WRegion *reg, WPHolder *ph)
+{
+    WRegion *next=reg->children;
+    return region_rescue_some_clientwins(reg, ph, iter_children, &next);
+}
+
+
+bool region_rescue_some_clientwins(WRegion *reg, WPHolder *ph,
+                                   WRegionIterator *iter, void *st)
+{
+    bool res=FALSE;
+    int fails=0;
+
+    reg->flags|=REGION_CWINS_BEING_RESCUED;
+    
+    while(TRUE){
+        WRegion *tosave=iter(st);
+        
+        if(tosave==NULL)
+            break;
+        
+        if(!OBJ_IS(tosave, WClientWin)){
+            if(!region_rescue_clientwins(tosave, ph))
+                fails++;
+        }else{
+            if(!pholder_attach(ph, 0, tosave))
+                fails++;
+        }
+    }
+    
+    reg->flags&=~REGION_CWINS_BEING_RESCUED;
+
+    return (fails==0);
+}
+
+
+bool region_rescue_clientwins(WRegion *reg, WPHolder *ph)
+{
+    bool ret=FALSE;
+    CALL_DYN_RET(ret, bool, region_rescue_clientwins, reg, (reg, ph));
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+ExtlTab manageparams_to_table(const WManageParams *mp)
+{
+    ExtlTab t=extl_create_table();
+    extl_table_sets_b(t, "switchto", mp->switchto);
+    extl_table_sets_b(t, "jumpto", mp->jumpto);
+    extl_table_sets_b(t, "userpos", mp->userpos);
+    extl_table_sets_b(t, "dockapp", mp->dockapp);
+    extl_table_sets_b(t, "maprq", mp->maprq);
+    extl_table_sets_i(t, "gravity", mp->gravity);
+    extl_table_sets_rectangle(t, "geom", &(mp->geom));
+    extl_table_sets_o(t, "tfor", (Obj*)(mp->tfor));
+    
+    return t;
+}
+
+
+/*}}}*/
diff --git a/ioncore/manage.h b/ioncore/manage.h
new file mode 100644 (file)
index 0000000..5354bea
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * ion/ioncore/manage.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_MANAGE_H
+#define ION_IONCORE_MANAGE_H
+
+#include <libextl/extl.h>
+#include "common.h"
+
+INTRSTRUCT(WManageParams);
+
+#include "clientwin.h"
+#include "attach.h"
+#include "rectangle.h"
+#include "extlconv.h"
+#include "pholder.h"
+
+
+#define MANAGEPARAMS_INIT                                                \
+  {FALSE, FALSE, FALSE, FALSE, FALSE, ForgetGravity, {0, 0, 0, 0}, NULL}
+
+enum{
+    MANAGE_REDIR_PREFER_YES,
+    MANAGE_REDIR_PREFER_NO,
+    MANAGE_REDIR_STRICT_YES,
+    MANAGE_REDIR_STRICT_NO
+};
+
+DECLSTRUCT(WManageParams){
+    bool switchto;
+    bool jumpto;
+    bool userpos;
+    bool dockapp;
+    bool maprq;
+    int gravity;
+    WRectangle geom;
+    WClientWin *tfor;
+};
+
+
+typedef WRegion *WRegionIterator(void *st);
+
+
+extern ExtlTab manageparams_to_table(const WManageParams *mp);
+
+
+extern WScreen *clientwin_find_suitable_screen(WClientWin *cwin,
+                                               const WManageParams *param);
+
+/* Manage */
+
+extern bool clientwin_do_manage_default(WClientWin *cwin,
+                                        const WManageParams *param);
+
+extern bool region_manage_clientwin(WRegion *reg, WClientWin *cwin,
+                                    const WManageParams *par, int redir);
+
+DYNFUN WPHolder *region_prepare_manage(WRegion *reg, const WClientWin *cwin,
+                                       const WManageParams *par, int redir);
+
+extern WPHolder *region_prepare_manage_default(WRegion *reg, 
+                                               const WClientWin *cwin,
+                                               const WManageParams *par, 
+                                               int redir);
+
+
+extern WPHolder *region_prepare_manage_transient(WRegion *reg, 
+                                                 const WClientWin *cwin,
+                                                 const WManageParams *param,
+                                                 int unused);
+
+extern WPHolder *region_prepare_manage_transient_default(WRegion *reg, 
+                                                         const WClientWin *cwin,
+                                                         const WManageParams *param,
+                                                         int unused);
+
+/* Rescue */
+
+extern bool region_rescue_clientwins(WRegion *reg, WPHolder *ph);
+extern bool region_rescue_child_clientwins(WRegion *reg, WPHolder *ph);
+extern bool region_rescue_some_clientwins(WRegion *reg, WPHolder *ph,
+                                          WRegionIterator *iter, void *st);
+
+
+#endif /* ION_IONCORE_MANAGE_H */
diff --git a/ioncore/modules.c b/ioncore/modules.c
new file mode 100644 (file)
index 0000000..f7f6242
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * ion/ioncore/modules.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include <libtu/rb.h>
+#include <libextl/readconfig.h>
+
+#include "common.h"
+#include "modules.h"
+#include "../version.h"
+
+
+#ifndef CF_PRELOAD_MODULES
+
+
+/*{{{ Module list */
+
+
+typedef void *dlhandle;
+
+
+static Rb_node modules=NULL;
+
+
+static dlhandle get_handle(const char *name)
+{
+    int found=0;
+    Rb_node nd;
+    
+    nd=rb_find_key_n(modules, name, &found);
+    if(found)
+        return nd->v.val;
+    return NULL;
+}
+
+
+static const char *get_name(dlhandle handle)
+{
+    Rb_node nd;
+    
+    rb_traverse(nd, modules){
+        if(nd->v.val==handle)
+            return (const char *)(nd->k.key);
+    }
+    return NULL;
+}
+
+
+static Rb_node add_module(char *name, dlhandle handle)
+{
+    return rb_insert(modules, name, handle);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Module symbol access */
+
+
+static void *get_module_symbol(dlhandle handle, 
+                               const char *modulename, 
+                               const char *name)
+{
+    char *p;
+    void *ret;
+
+    p=scat(modulename, name);
+    
+    if(p==NULL)
+        return NULL;
+    
+    ret=dlsym(handle, p);
+    
+    free(p);
+    
+    return ret;
+}
+
+static bool check_version(dlhandle handle, const char *modulename)
+{
+    char *versionstr=(char*)get_module_symbol(handle, modulename, 
+                                              "_ion_api_version");
+    if(versionstr==NULL)
+        return FALSE;
+    return (strcmp(versionstr, ION_API_VERSION)==0);
+}
+
+
+static bool call_init(dlhandle handle, const char *modulename)
+{
+    bool (*initfn)(void);
+    
+    initfn=(bool (*)())get_module_symbol(handle, modulename, "_init");
+    
+    if(initfn==NULL)
+        return TRUE;
+    
+    return initfn();
+}
+
+
+static void call_deinit(dlhandle handle, const char *modulename)
+{
+    void (*deinitfn)(void);
+    
+    deinitfn=(void (*)())get_module_symbol(handle, modulename, "_deinit");
+    
+    if(deinitfn!=NULL)
+        deinitfn();
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init */
+
+
+bool ioncore_init_module_support()
+{
+    modules=make_rb();
+    return (modules!=NULL);
+}
+
+
+static int try_load(const char *file, void *param)
+{
+    dlhandle handle=NULL;
+    const char *slash, *dot;
+    char *name;
+    Rb_node mod;
+    
+    if(access(file, F_OK)!=0)
+        return EXTL_TRYCONFIG_NOTFOUND;
+
+    slash=strrchr(file, '/');
+    dot=strrchr(file, '.');
+    
+    if(slash==NULL)
+        slash=file;
+    else
+        slash++;
+    
+    if(dot<=slash){
+        warn(TR("Invalid module name."));
+        goto err1;
+    }
+    
+    name=ALLOC_N(char, dot-slash+1);
+    if(name==NULL)
+        goto err1;
+    
+    strncpy(name, slash, dot-slash);
+    name[dot-slash]='\0';
+    
+    if(get_handle(name)){
+        warn_obj(file, TR("The module is already loaded."));
+        goto err2;
+    }
+        
+    handle=dlopen(file, RTLD_NOW|RTLD_GLOBAL);
+
+    if(handle==NULL){
+        warn_obj(file, "%s", dlerror());
+        goto err2;
+    }
+    
+    if(get_name(handle))
+        return EXTL_TRYCONFIG_OK;
+    
+    if(!check_version(handle, name)){
+        warn_obj(file, TR("Module version information not found or "
+                          "version mismatch. Refusing to use."));
+        goto err3;
+    }
+    
+    mod=add_module(name, handle);
+    
+    if(mod==NULL)
+        goto err3;
+    
+    if(!call_init(handle, name)){
+        warn_obj(file, TR("Unable to initialise module %s."), name);
+        rb_delete_node(mod);
+        goto err3;
+    }
+    
+    return EXTL_TRYCONFIG_OK;
+
+err3:    
+    dlclose(handle);
+err2:
+    free(name);
+err1:
+    return EXTL_TRYCONFIG_LOAD_FAILED;
+}
+
+
+static bool do_load_module(const char *modname)
+{
+    int retval;
+    
+    retval=extl_try_config(modname, NULL, (ExtlTryConfigFn*)try_load, 
+                           NULL, "so", NULL);
+    
+    if(retval==EXTL_TRYCONFIG_NOTFOUND)
+        warn(TR("Unable to find '%s' on search path."), modname);
+    
+    return (retval==EXTL_TRYCONFIG_OK);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Deinit */
+
+
+static void do_unload_module(Rb_node mod)
+{
+    char *name=(char*)mod->k.key;
+    dlhandle handle=mod->v.val;
+    
+    call_deinit(handle, name);
+
+    dlclose(handle);
+    free(name);
+}
+
+
+void ioncore_unload_modules()
+{
+    Rb_node mod;
+    
+    rb_traverse(mod, modules){
+        do_unload_module(mod);
+    }
+}
+
+
+/*}}}*/
+
+
+#else
+
+
+/*{{{ Static module support */
+
+
+static bool call_init(WStaticModuleInfo *handle)
+{
+    if(handle->init!=NULL)
+        return handle->init();
+    return TRUE;
+}
+
+
+static void call_deinit(WStaticModuleInfo *handle)
+{
+    if(handle->deinit!=NULL)
+        handle->deinit();
+}
+
+
+extern WStaticModuleInfo ioncore_static_modules[];
+
+
+static bool do_load_module(const char *name)
+{
+    WStaticModuleInfo *mod;
+    
+    for(mod=ioncore_static_modules; mod->name!=NULL; mod++){
+        if(strcmp(mod->name, name)==0)
+            break;
+    }
+    
+    if(mod->name==NULL){
+        warn_obj(name, TR("Unknown module."));
+        return FALSE;
+    }
+    
+    if(mod->loaded)
+        return TRUE;
+
+    if(!call_init(mod)){
+        warn_obj(name, TR("Unable to initialise module."));
+        return FALSE;
+    }
+    
+    mod->loaded=TRUE;
+    
+    return TRUE;
+}
+
+
+void ioncore_unload_modules()
+{
+    WStaticModuleInfo *mod;
+        
+    for(mod=ioncore_static_modules; mod->name!=NULL; mod++){
+        if(mod->loaded){
+            call_deinit(mod);
+            mod->loaded=FALSE;
+        }
+    }
+}
+
+
+bool ioncore_init_module_support()
+{
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+#endif
+
+
+/*{{{ Exports */
+
+
+/*EXTL_DOC
+ * Attempt to load a C-side module. 
+ */
+EXTL_EXPORT
+bool ioncore_load_module(const char *modname)
+{
+    if(modname==NULL){
+        warn(TR("No module to load given."));
+        return FALSE;
+    }
+    return do_load_module(modname);
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/modules.h b/ioncore/modules.h
new file mode 100644 (file)
index 0000000..6cc565e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * ion/ioncore/modules.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_MODULES_H
+#define ION_IONCORE_MODULES_H
+
+#include "common.h"
+
+extern bool ioncore_load_module(const char *name);
+extern void ioncore_unload_modules();
+extern bool ioncore_init_module_support();
+
+typedef struct{
+    const char *name;
+    bool (*init)();
+    void (*deinit)();
+    bool loaded;
+} WStaticModuleInfo;
+
+#endif /* ION_IONCORE_MODULES_H */
diff --git a/ioncore/mplex.c b/ioncore/mplex.c
new file mode 100644 (file)
index 0000000..158b9ff
--- /dev/null
@@ -0,0 +1,2083 @@
+/*
+ * ion/ioncore/mplex.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <limits.h>
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <libtu/rb.h>
+#include <libextl/extl.h>
+#include <libmainloop/defer.h>
+
+#include "common.h"
+#include "window.h"
+#include "global.h"
+#include "rootwin.h"
+#include "focus.h"
+#include "event.h"
+#include "attach.h"
+#include "manage.h"
+#include "resize.h"
+#include "tags.h"
+#include "sizehint.h"
+#include "extlconv.h"
+#include "frame-pointer.h"
+#include "bindmaps.h"
+#include "regbind.h"
+#include "saveload.h"
+#include "xwindow.h"
+#include "mplexpholder.h"
+#include "llist.h"
+#include "names.h"
+#include "sizepolicy.h"
+#include "stacking.h"
+#include "group.h"
+#include "navi.h"
+#include "groupedpholder.h"
+
+
+#define SUBS_MAY_BE_MAPPED(MPLEX) \
+    (REGION_IS_MAPPED(MPLEX) && !MPLEX_MGD_UNVIEWABLE(MPLEX))
+
+#define PASSIVE(ST) ((ST)->level<=STACKING_LEVEL_MODAL1          \
+                     && ((ST)->reg==NULL                         \
+                         || (ST)->reg->flags&REGION_SKIP_FOCUS))
+
+#define CAN_MANAGE_STDISP(REG) HAS_DYN(REG, region_manage_stdisp)
+
+
+/*{{{ Stacking list stuff */
+
+
+WStacking *mplex_get_stacking(WMPlex *mplex)
+{
+    return window_get_stacking(&mplex->win);
+}
+
+
+WStacking **mplex_get_stackingp(WMPlex *mplex)
+{
+    return window_get_stackingp(&mplex->win);
+}
+
+
+void mplex_iter_init(WMPlexIterTmp *tmp, WMPlex *mplex)
+{
+    stacking_iter_mgr_init(tmp, mplex->mgd, NULL, mplex);
+}
+
+
+WRegion *mplex_iter(WMPlexIterTmp *tmp)
+{
+    return stacking_iter_mgr(tmp);
+}
+
+
+WStacking *mplex_iter_nodes(WMPlexIterTmp *tmp)
+{
+    return stacking_iter_mgr_nodes(tmp);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Destroy/create mplex */
+
+
+bool mplex_do_init(WMPlex *mplex, WWindow *parent, Window win,
+                   const WFitParams *fp, bool create)
+{
+    mplex->flags=0;
+    
+    mplex->mx_list=NULL;
+    mplex->mx_current=NULL;
+    mplex->mx_phs=NULL;
+    mplex->mx_count=0;
+    
+    mplex->mgd=NULL;
+    
+    watch_init(&(mplex->stdispwatch));
+    mplex->stdispinfo.pos=MPLEX_STDISP_BL;
+    mplex->stdispinfo.fullsize=FALSE;
+    
+    if(create){
+        if(!window_init((WWindow*)mplex, parent, fp))
+            return FALSE;
+    }else{
+        if(!window_do_init((WWindow*)mplex, parent, win, fp))
+            return FALSE;
+    }
+    
+    mplex->win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
+    
+    window_select_input(&(mplex->win), IONCORE_EVENTMASK_CWINMGR);
+    
+    region_add_bindmap((WRegion*)mplex, ioncore_mplex_bindmap);
+    region_add_bindmap((WRegion*)mplex, ioncore_mplex_toplevel_bindmap);
+    
+    region_register((WRegion*)mplex);
+    
+    /* Call this to set MPLEX_MANAGED_UNVIEWABLE if necessary. */
+    mplex_fit_managed(mplex);
+    
+    return TRUE;
+}
+
+
+bool mplex_init(WMPlex *mplex, WWindow *parent, const WFitParams *fp)
+{
+    return mplex_do_init(mplex, parent, None, fp, TRUE);
+}
+
+
+WMPlex *create_mplex(WWindow *parent, const WFitParams *fp)
+{
+    CREATEOBJ_IMPL(WMPlex, mplex, (p, parent, fp));
+}
+
+
+void mplex_deinit(WMPlex *mplex)
+{
+    WMPlexIterTmp tmp;
+    WRegion *reg;
+    
+    FOR_ALL_MANAGED_BY_MPLEX(mplex, reg, tmp){
+        destroy_obj((Obj*)reg);
+    }
+    
+    assert(mplex->mgd==NULL);
+    assert(mplex->mx_list==NULL);
+
+    while(mplex->mx_phs!=NULL){
+        assert(mplexpholder_move(mplex->mx_phs, NULL, NULL, NULL));
+    }
+    
+    window_deinit((WWindow*)mplex);
+}
+
+
+bool mplex_may_destroy(WMPlex *mplex)
+{
+    if(mplex->mgd!=NULL){
+        warn(TR("Refusing to destroy - not empty."));
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Node lookup etc. */
+
+
+WStacking *mplex_find_stacking(WMPlex *mplex, WRegion *reg)
+{
+    WStacking *st;
+    
+    /* Some routines that call us expect us to this check. */
+    if(reg==NULL || REGION_MANAGER(reg)!=(WRegion*)mplex)
+        return NULL;
+    
+    st=ioncore_find_stacking(reg);
+
+    assert(st==NULL || st->mgr_prev!=NULL);
+    
+    return st;
+}
+
+
+WStacking *mplex_current_node(WMPlex *mplex)
+{
+    WStacking *st=NULL;
+    WRegion *reg;
+    
+    reg=REGION_ACTIVE_SUB(mplex);
+    reg=region_managed_within((WRegion*)mplex, reg);
+    if(reg!=NULL)
+        st=mplex_find_stacking(mplex, reg);
+
+    if(st!=NULL)
+        return st;
+    else
+        return (mplex->mx_current!=NULL ? mplex->mx_current->st : NULL);
+}
+
+
+WRegion *mplex_current(WMPlex *mplex)
+{
+    WStacking *node=mplex_current_node(mplex);
+    return (node==NULL ? NULL : node->reg);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Exclusive list management and exports */
+
+/*EXTL_DOC
+ * Returns the number of objects on the mutually exclusive list of \var{mplex}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+int mplex_mx_count(WMPlex *mplex)
+{
+    return mplex->mx_count;
+}
+
+
+/*EXTL_DOC
+ * Returns the managed object currently active within the mutually exclusive
+ * list of \var{mplex}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WRegion *mplex_mx_current(WMPlex *mplex)
+{
+    WLListNode *lnode=mplex->mx_current;
+    return (lnode==NULL ? NULL : lnode->st->reg);
+}
+
+
+/*EXTL_DOC
+ * Returns the \var{n}:th object managed by \var{mplex} on the
+ * \var{l}:th layer.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WRegion *mplex_mx_nth(WMPlex *mplex, uint n)
+{
+    WLListNode *lnode=llist_nth_node(mplex->mx_list, n);
+    return (lnode==NULL ? NULL : lnode->st->reg);
+}
+
+
+/*EXTL_DOC
+ * Returns a list of regions on the numbered/mutually exclusive list of 
+ * \var{mplex}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+ExtlTab mplex_mx_list(WMPlex *mplex)
+{
+    WLListIterTmp tmp;
+    llist_iter_init(&tmp, mplex->mx_list);
+    
+    return extl_obj_iterable_to_table((ObjIterator*)llist_iter_regions, &tmp);
+}
+
+
+/*EXTL_DOC
+ * Returns a list of all regions managed by \var{mplex}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+ExtlTab mplex_managed_list(WMPlex *mplex)
+{
+    WMPlexIterTmp tmp;
+    mplex_iter_init(&tmp, mplex);
+    
+    return extl_obj_iterable_to_table((ObjIterator*)mplex_iter, &tmp);
+}
+
+
+/*EXTL_DOC
+ * Set index of \var{reg} within the multiplexer to \var{index} within 
+ * the mutually exclusive list. Special values for \var{index} are:
+ * \begin{tabularx}{\linewidth}{lX}
+ *   $-1$ & After \fnref{WMPlex.mx_current}. \\
+ *   $-2$ & Last. \\
+ * \end{tabularx}
+ */
+EXTL_EXPORT_MEMBER
+void mplex_set_index(WMPlex *mplex, WRegion *reg, int index)
+{
+    WLListNode *lnode, *after;
+    WStacking *node;
+    
+    node=mplex_find_stacking(mplex, reg);
+
+    if(node==NULL)
+        return;
+    
+    lnode=node->lnode;
+    
+    if(lnode==NULL){
+        lnode=ALLOC(WLListNode);
+        if(lnode==NULL)
+            return;
+        lnode->next=NULL;
+        lnode->prev=NULL;
+        lnode->phs=NULL;
+        lnode->st=node;
+        node->lnode=lnode;
+        mplex->mx_count++;
+    }else{
+        mplex_move_phs_before(mplex, lnode);
+        llist_unlink(&(mplex->mx_list), lnode);
+    }
+    
+    /* TODO: Support remove? */
+    
+    after=llist_index_to_after(mplex->mx_list, mplex->mx_current, index);
+    llist_link_after(&(mplex->mx_list), after, lnode);
+    mplex_managed_changed(mplex, MPLEX_CHANGE_REORDER, FALSE, reg);
+}
+
+
+/*EXTL_DOC
+ * Get index of \var{reg} within the multiplexer on list 1. The first region 
+ * managed by \var{mplex} has index zero. If \var{reg} is not managed by 
+ * \var{mplex}, -1 is returned.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+int mplex_get_index(WMPlex *mplex, WRegion *reg)
+{
+    WLListIterTmp tmp;
+    WLListNode *lnode;
+    int index=0;
+    
+    FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, tmp){
+        if(reg==lnode->st->reg)
+            return index;
+        index++;
+    }
+    
+    return -1;
+}
+
+
+/*EXTL_DOC
+ * Move \var{r} ''right'' within objects managed by \var{mplex} on list 1.
+ */
+EXTL_EXPORT_MEMBER
+void mplex_inc_index(WMPlex *mplex, WRegion *r)
+{
+    if(r==NULL)
+        r=mplex_mx_current(mplex);
+    if(r!=NULL)
+        mplex_set_index(mplex, r, mplex_get_index(mplex, r)+1);
+}
+
+
+/*EXTL_DOC
+ * Move \var{r} ''right'' within objects managed by \var{mplex} on list 1.
+ */
+EXTL_EXPORT_MEMBER
+void mplex_dec_index(WMPlex *mplex, WRegion *r)
+{
+    if(r==NULL)
+        r=mplex_mx_current(mplex);
+    if(r!=NULL)
+        mplex_set_index(mplex, r, mplex_get_index(mplex, r)-1);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Mapping */
+
+
+static void mplex_map_mgd(WMPlex *mplex)
+{
+    WMPlexIterTmp tmp;
+    WStacking *node;
+
+    FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
+        if(!STACKING_IS_HIDDEN(node))
+            region_map(node->reg);
+    }
+}
+
+
+static void mplex_unmap_mgd(WMPlex *mplex)
+{
+    WMPlexIterTmp tmp;
+    WStacking *node;
+
+    FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
+        if(!STACKING_IS_HIDDEN(node))
+            region_unmap(node->reg);
+    }
+}
+
+
+
+void mplex_map(WMPlex *mplex)
+{
+    window_map((WWindow*)mplex);
+    /* A lame requirement of the ICCCM is that client windows should be
+     * unmapped if the parent is unmapped.
+     */
+    if(!MPLEX_MGD_UNVIEWABLE(mplex))
+        mplex_map_mgd(mplex);
+}
+
+
+void mplex_unmap(WMPlex *mplex)
+{
+    window_unmap((WWindow*)mplex);
+    /* A lame requirement of the ICCCM is that client windows should be
+     * unmapped if the parent is unmapped.
+     */
+    if(!MPLEX_MGD_UNVIEWABLE(mplex))
+        mplex_unmap_mgd(mplex);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Resize and reparent */
+
+
+bool mplex_fitrep(WMPlex *mplex, WWindow *par, const WFitParams *fp)
+{
+    bool wchg=(REGION_GEOM(mplex).w!=fp->g.w);
+    bool hchg=(REGION_GEOM(mplex).h!=fp->g.h);
+    
+    window_do_fitrep(&(mplex->win), par, &(fp->g));
+    
+    if(wchg || hchg){
+        mplex_fit_managed(mplex);
+        mplex_size_changed(mplex, wchg, hchg);
+    }
+    
+    return TRUE;
+}
+
+
+void mplex_do_fit_managed(WMPlex *mplex, WFitParams *fp)
+{
+    WRectangle geom;
+    WMPlexIterTmp tmp;
+    WStacking *node;
+    WFitParams fp2;
+    
+    if(!MPLEX_MGD_UNVIEWABLE(mplex) && (fp->g.w<=1 || fp->g.h<=1)){
+        mplex->flags|=MPLEX_MANAGED_UNVIEWABLE;
+        if(REGION_IS_MAPPED(mplex))
+            mplex_unmap_mgd(mplex);
+    }else if(MPLEX_MGD_UNVIEWABLE(mplex) && !(fp->g.w<=1 || fp->g.h<=1)){
+        mplex->flags&=~MPLEX_MANAGED_UNVIEWABLE;
+        if(REGION_IS_MAPPED(mplex))
+            mplex_map_mgd(mplex);
+    }
+    
+    if(!MPLEX_MGD_UNVIEWABLE(mplex)){
+        FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
+            fp2=*fp;
+            sizepolicy(&node->szplcy, node->reg, NULL, 0, &fp2);
+            region_fitrep(node->reg, NULL, &fp2);
+        }
+    }
+}
+
+
+void mplex_fit_managed(WMPlex *mplex)
+{
+    WFitParams fp;
+    
+    fp.mode=REGION_FIT_EXACT;
+    mplex_managed_geom(mplex, &(fp.g));
+
+    mplex_do_fit_managed(mplex, &fp);
+}
+
+
+static void mplex_managed_rqgeom(WMPlex *mplex, WRegion *sub,
+                                 const WRQGeomParams *rq,
+                                 WRectangle *geomret)
+{
+    WRectangle rg;
+    WFitParams fp;
+    WStacking *node;
+
+    node=mplex_find_stacking(mplex, sub);
+    
+    assert(node!=NULL);
+
+    mplex_managed_geom(mplex, &fp.g);
+
+    sizepolicy(&node->szplcy, sub, &rq->geom, &rq->flags, &fp);
+    
+    if(geomret!=NULL)
+        *geomret=fp.g;
+    
+    if(!(rq->flags&REGION_RQGEOM_TRYONLY))
+        region_fitrep(sub, NULL, &fp);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Focus  */
+
+
+static WRegion *mplex_do_to_focus(WMPlex *mplex, WStacking *to_try)
+{
+    WStacking *stacking=mplex_get_stacking(mplex);
+    WStacking *st=NULL;
+    
+    if(stacking==NULL)
+        return NULL;
+
+    if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg)))
+        to_try=NULL;
+
+    st=stacking_find_to_focus_mapped(stacking, to_try, NULL);
+    
+    if(st!=NULL)
+        return st->reg;
+    else if(mplex->mx_current!=NULL)
+        return mplex->mx_current->st->reg;
+    else
+        return NULL;
+}
+
+
+WRegion *mplex_to_focus(WMPlex *mplex)
+{
+    WRegion *reg=REGION_ACTIVE_SUB(mplex);
+    WStacking *to_try=NULL;
+    
+    if(reg!=NULL)
+        to_try=ioncore_find_stacking(reg);
+
+    return mplex_do_to_focus(mplex, to_try);
+}
+
+
+static WRegion *mplex_do_to_focus_on(WMPlex *mplex, WStacking *node,
+                                     WStacking *to_try)
+{
+    WStacking *stacking=mplex_get_stacking(mplex);
+    WStacking *st=NULL;
+    
+    if(stacking==NULL)
+        return NULL;
+
+    if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg)))
+        to_try=NULL;
+    
+    st=stacking_find_to_focus_mapped(stacking, to_try, node->reg);
+    
+    return (st!=NULL ? st->reg : NULL);
+}
+
+
+static WRegion *mplex_to_focus_on(WMPlex *mplex, WStacking *node,
+                                  WStacking *to_try)
+{
+    WRegion *reg;
+    WGroup *grp=OBJ_CAST(node->reg, WGroup);
+    
+    if(grp!=NULL){
+        if(to_try==NULL)
+            to_try=grp->current_managed;
+        reg=mplex_do_to_focus_on(mplex, node, to_try);
+        if(reg!=NULL || to_try!=NULL)
+            return reg;
+        /* We don't know whether something is blocking focus here,
+         * or if there was nothing to focus (as node->reg itself
+         * isn't on the stacking list).
+         */
+    }
+    
+    reg=mplex_do_to_focus(mplex, node);
+    return (reg==node->reg ? reg : NULL);
+}
+
+
+void mplex_do_set_focus(WMPlex *mplex, bool warp)
+{
+    if(!MPLEX_MGD_UNVIEWABLE(mplex)){
+        WRegion *reg=mplex_to_focus(mplex);
+        
+        if(reg!=NULL){
+            region_do_set_focus(reg, warp);
+            return;
+        }
+    }
+    
+    window_do_set_focus((WWindow*)mplex, warp);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Switch */
+
+
+static void mplex_do_remanage_stdisp(WMPlex *mplex, WRegion *sub)
+{
+    WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj);
+
+    /* Move stdisp */
+    if(sub!=NULL && CAN_MANAGE_STDISP(sub)){
+        if(stdisp!=NULL){
+            WRegion *mgrw=region_managed_within((WRegion*)mplex, stdisp);
+            if(mgrw!=sub){
+                WRegion *mgr=REGION_MANAGER(stdisp);
+                if(mgr!=NULL){
+                    if(CAN_MANAGE_STDISP(mgr))
+                        region_unmanage_stdisp(mgr, FALSE, FALSE);
+                    region_detach_manager(stdisp);
+                }
+                
+                region_manage_stdisp(sub, stdisp, 
+                                     &(mplex->stdispinfo));
+            }
+        }else{
+            region_unmanage_stdisp(sub, TRUE, FALSE);
+        }
+    }/*else if(stdisp!=NULL){
+        region_detach_manager(stdisp);
+        region_unmap(stdisp);
+    }*/
+}
+
+
+void mplex_remanage_stdisp(WMPlex *mplex)
+{
+    mplex_do_remanage_stdisp(mplex, (mplex->mx_current!=NULL
+                                     ? mplex->mx_current->st->reg
+                                     : NULL));
+}
+
+
+static void mplex_do_node_display(WMPlex *mplex, WStacking *node,
+                                  bool call_changed)
+{
+    WRegion *sub=node->reg;
+    WLListNode *mxc=mplex->mx_current;
+
+    if(!STACKING_IS_HIDDEN(node))
+        return;
+    
+    if(node->lnode!=NULL && node->lnode!=mxc)
+        mplex_do_remanage_stdisp(mplex, sub);
+
+    node->hidden=FALSE;
+    
+    if(SUBS_MAY_BE_MAPPED(mplex))
+        region_map(sub);
+    else
+        region_unmap(sub);
+    
+    if(node->lnode!=NULL){
+        if(mxc!=NULL){
+            /* Hide current mx region. We do it after mapping the
+             * new one to avoid flicker.
+             */
+            if(REGION_IS_MAPPED(mplex))
+                region_unmap(mxc->st->reg);
+            mxc->st->hidden=TRUE;
+        }
+        
+        mplex->mx_current=node->lnode;
+        
+        /* Ugly hack:
+         * Many programs will get upset if the visible, although only 
+         * such, client window is not the lowest window in the mplex. 
+         * xprop/xwininfo will return the information for the lowest 
+         * window. 'netscape -remote' will not work at all if there are
+         * no visible netscape windows.
+         */
+        {
+            #warning "TODO: less ugly hack"
+            WGroup *grp=(WGroup*)OBJ_CAST(sub, WGroupCW);
+            if(grp!=NULL && grp->bottom!=NULL){
+                region_managed_rqorder((WRegion*)grp, grp->bottom->reg, 
+                                       REGION_ORDER_BACK);
+            }
+        }
+        
+        if(call_changed)
+            mplex_managed_changed(mplex, MPLEX_CHANGE_SWITCHONLY, TRUE, sub);
+    }
+}
+
+
+static bool mplex_refocus(WMPlex *mplex, WStacking *node, bool warp)
+{
+    WRegion *foc=NULL;
+    bool ret=TRUE;
+    
+    if(node!=NULL){
+        foc=mplex_to_focus_on(mplex, node, NULL);
+        ret=(foc!=NULL);
+    }
+        
+    if(foc==NULL){
+        ret=FALSE;
+        foc=mplex_to_focus(mplex);
+    }
+    
+    if(foc!=NULL /* && !REGION_IS_ACTIVE(foc) */ )
+        region_maybewarp(foc, warp);
+    
+    return ret;
+}
+
+
+bool mplex_do_prepare_focus(WMPlex *mplex, WStacking *node,
+                            WStacking *sub, int flags, 
+                            WPrepareFocusResult *res)
+{
+    WRegion *foc;
+    
+    if(sub==NULL && node==NULL)
+        return FALSE;
+    
+    /* Display the node in any case */
+    if(node!=NULL && !(flags&REGION_GOTO_ENTERWINDOW))
+        mplex_do_node_display(mplex, node, TRUE);
+    
+    if(!region_prepare_focus((WRegion*)mplex, flags, res))
+        return FALSE;
+
+    if(node!=NULL)
+        foc=mplex_to_focus_on(mplex, node, sub);
+    else
+        foc=mplex_do_to_focus(mplex, sub);
+
+    if(foc!=NULL){
+        res->reg=foc;
+        res->flags=flags;
+        
+        if(sub==NULL)
+            return (foc==node->reg);
+        else
+            return (foc==sub->reg);
+    }else{
+        return FALSE;
+    }
+}
+
+
+bool mplex_managed_prepare_focus(WMPlex *mplex, WRegion *disp, 
+                                 int flags, WPrepareFocusResult *res)
+{
+    WStacking *node=mplex_find_stacking(mplex, disp);
+    
+    if(node==NULL)
+        return FALSE;
+    else
+        return mplex_do_prepare_focus(mplex, node, NULL, flags, res);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Switch exports */
+
+
+static void do_switch(WMPlex *mplex, WLListNode *lnode)
+{
+    WStacking *node=(lnode!=NULL ? lnode->st : NULL);
+    
+    if(node!=NULL){
+        bool mcf=region_may_control_focus((WRegion*)mplex);
+    
+        mplex_do_node_display(mplex, node, TRUE);
+        
+        if(mcf)
+            mplex_refocus(mplex, node, TRUE);
+    }
+}
+
+
+/*EXTL_DOC
+ * Have \var{mplex} display the \var{n}:th object managed by it.
+ */
+EXTL_EXPORT_MEMBER
+void mplex_switch_nth(WMPlex *mplex, uint n)
+{
+    do_switch(mplex, llist_nth_node(mplex->mx_list, n));
+}
+
+
+/*EXTL_DOC
+ * Have \var{mplex} display next (wrt. currently selected) object managed 
+ * by it.
+ */
+EXTL_EXPORT_MEMBER
+void mplex_switch_next(WMPlex *mplex)
+{
+    do_switch(mplex, LIST_NEXT_WRAP(mplex->mx_list, mplex->mx_current, 
+                                    next, prev));
+}
+
+
+/*EXTL_DOC
+ * Have \var{mplex} display previous (wrt. currently selected) object
+ * managed by it.
+ */
+EXTL_EXPORT_MEMBER
+void mplex_switch_prev(WMPlex *mplex)
+{
+    do_switch(mplex, LIST_PREV_WRAP(mplex->mx_list, mplex->mx_current,
+                                    next, prev));
+}
+
+
+bool mplex_set_hidden(WMPlex *mplex, WRegion *reg, int sp)
+{
+    bool mcf=region_may_control_focus((WRegion*)mplex);
+    WStacking *node=mplex_find_stacking(mplex, reg);
+    bool hidden, nhidden;
+    
+    if(node==NULL)
+        return FALSE;
+    
+    hidden=STACKING_IS_HIDDEN(node);
+    nhidden=libtu_do_setparam(sp, hidden);
+    
+    if(!hidden && nhidden){
+        node->hidden=TRUE;
+        
+        if(REGION_IS_MAPPED(mplex) && !MPLEX_MGD_UNVIEWABLE(mplex))
+            region_unmap(reg);
+        
+        /* lnode -> switch next? */
+    }else if(hidden && !nhidden){
+        mplex_do_node_display(mplex, node, TRUE);
+    }
+    
+    if(mcf && !PASSIVE(node))
+        mplex_refocus(mplex, (nhidden ? NULL : node), TRUE);
+    
+    return STACKING_IS_HIDDEN(node);
+}
+
+
+/*EXTL_DOC
+ * Set the visibility of the region \var{reg} on \var{mplex}
+ * as specified with the parameter \var{how} (set/unset/toggle).
+ * The resulting state is returned.
+ */
+EXTL_EXPORT_AS(WMPlex, set_hidden)
+bool mplex_set_hidden_extl(WMPlex *mplex, WRegion *reg, const char *how)
+{
+    return mplex_set_hidden(mplex, reg, libtu_string_to_setparam(how));
+}
+
+
+/*EXTL_DOC
+ * Is \var{reg} on within \var{mplex} and hidden?
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+bool mplex_is_hidden(WMPlex *mplex, WRegion *reg)
+{
+    WStacking *node=mplex_find_stacking(mplex, reg);
+    
+    return (node!=NULL && STACKING_IS_HIDDEN(node));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Navigation */
+
+
+static WStacking *mplex_nxt(WMPlex *mplex, WStacking *st, bool wrap)
+{
+    return (st->mgr_next!=NULL 
+            ? st->mgr_next 
+            : (wrap ? mplex->mgd : NULL));
+}
+
+
+static WStacking *mplex_prv(WMPlex *mplex, WStacking *st, bool wrap)
+{
+    return (st!=mplex->mgd
+            ? st->mgr_prev
+            : (wrap ? st->mgr_prev : NULL));
+}
+
+
+typedef WStacking *NxtFn(WMPlex *mplex, WStacking *st, bool wrap);
+
+
+static WRegion *do_navi(WMPlex *mplex, WStacking *sti, 
+                        NxtFn *fn, WRegionNaviData *data, bool sti_ok)
+{
+    WStacking *st, *stacking;
+    uint min_level=0;
+    bool wrap=FALSE;
+    
+    stacking=mplex_get_stacking(mplex);
+    
+    if(stacking!=NULL)
+        min_level=stacking_min_level_mapped(stacking);
+
+    st=sti;
+    while(1){
+        st=fn(mplex, st, wrap); 
+        
+        if(st==NULL || (st==sti && !sti_ok))
+            break;
+        
+        if(!st->hidden){
+            if(OBJ_IS(st->reg, WGroup)){
+                /* WGroup navigation code should respect modal stuff. */
+                WRegion *res=region_navi_cont((WRegion*)mplex, st->reg, data);
+                if(res!=NULL){
+                    if(res!=st->reg){
+                        return res;
+                    }else{
+                        #warning "TODO: What to do?"
+                    }
+                }
+            }else{
+                if(st->level>=min_level && !PASSIVE(st))
+                    return region_navi_cont((WRegion*)mplex, st->reg, data);
+            }
+        }
+               
+        if(st==sti)
+            break;
+    }
+    
+    return NULL;
+}
+                        
+
+WRegion *mplex_navi_first(WMPlex *mplex, WRegionNavi nh,
+                          WRegionNaviData *data)
+{
+    WStacking *lst=mplex->mgd;
+
+    if(lst==NULL)
+        return region_navi_cont((WRegion*)mplex, NULL, data);
+    
+    if(nh==REGION_NAVI_ANY){
+        /* ? */
+    }
+    
+    if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || 
+       nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
+        return do_navi(mplex, lst, mplex_prv, data, TRUE);
+    }else{
+        return do_navi(mplex, lst->mgr_prev, mplex_nxt, data, TRUE);
+    }
+}
+
+    
+WRegion *mplex_navi_next(WMPlex *mplex, WRegion *rel, WRegionNavi nh,
+                         WRegionNaviData *data)
+{
+    WStacking *st;
+    
+    if(rel!=NULL){
+        st=mplex_find_stacking(mplex, rel);
+        if(st==NULL)
+            return NULL;
+    }else if(mplex->mx_current!=NULL){
+        st=mplex->mx_current->st;
+    }else{
+        return mplex_navi_first(mplex, nh, data);
+    }
+        
+    if(nh==REGION_NAVI_ANY){
+        /* ? */
+    }
+    
+    if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || 
+       nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
+        return do_navi(mplex, st, mplex_nxt, data, FALSE);
+    }else{
+        return do_navi(mplex, st, mplex_prv, data, FALSE);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Stacking */
+
+
+bool mplex_managed_rqorder(WMPlex *mplex, WRegion *reg, WRegionOrder order)
+{
+    WStacking **stackingp=mplex_get_stackingp(mplex);
+    WStacking *st;
+
+    if(stackingp==NULL || *stackingp==NULL)
+        return FALSE;
+
+    st=mplex_find_stacking(mplex, reg);
+    
+    if(st==NULL)
+        return FALSE;
+    
+    stacking_restack(stackingp, st, None, NULL, NULL,
+                     (order!=REGION_ORDER_FRONT));
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Attach */
+
+
+static bool mplex_stack(WMPlex *mplex, WStacking *st)
+{
+    WStacking *tmp=NULL;
+    Window bottom=None, top=None;
+    WStacking **stackingp=mplex_get_stackingp(mplex);
+    
+    if(stackingp==NULL)
+        return FALSE;
+
+    LINK_ITEM_FIRST(tmp, st, next, prev);
+    stacking_weave(stackingp, &tmp, FALSE);
+    assert(tmp==NULL);
+    
+    return TRUE;
+}
+
+
+static void mplex_unstack(WMPlex *mplex, WStacking *st)
+{
+    WStacking *stacking;
+    
+    stacking=mplex_get_stacking(mplex);
+    
+    stacking_unstack(&mplex->win, st);
+}
+
+
+/* WMPlexWPHolder is used for position marking in order to allow
+ * WLListNodes be safely removed in the attach handler hnd, that
+ * could remove something this mplex is managing.
+ */
+bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
+{
+    WStacking *node=NULL;
+    WLListNode *lnode=NULL;
+    WMPlexAttachParams *param=&ph->param;
+    bool mx_was_empty, sw, modal, mcf, hidden;
+    uint level;
+    
+    mcf=region_may_control_focus((WRegion*)mplex);
+    
+    mx_was_empty=(mplex->mx_list==NULL);
+    
+    modal=(param->flags&MPLEX_ATTACH_LEVEL
+           ? param->level>=STACKING_LEVEL_MODAL1
+           : param->flags&MPLEX_ATTACH_MODAL);
+    
+    level=(param->flags&MPLEX_ATTACH_LEVEL
+           ? param->level
+           : (param->flags&MPLEX_ATTACH_MODAL
+              ? STACKING_LEVEL_MODAL1
+              : (param->flags&MPLEX_ATTACH_UNNUMBERED
+                 ? STACKING_LEVEL_NORMAL
+                 : STACKING_LEVEL_BOTTOM)));
+    
+    hidden=(param->flags&MPLEX_ATTACH_HIDDEN
+            && (param->flags&MPLEX_ATTACH_UNNUMBERED
+                || !mx_was_empty));
+    
+    sw=(!hidden && (param->flags&MPLEX_ATTACH_SWITCHTO 
+                    || (param->flags&MPLEX_ATTACH_UNNUMBERED
+                        ? modal
+                        : (mplex_current_node(mplex)==NULL))));
+    
+    hidden=(hidden || (!sw && !(param->flags&MPLEX_ATTACH_UNNUMBERED)));
+    
+    
+    node=create_stacking();
+    
+    if(node==NULL)
+        return FALSE;
+    
+    if(!(param->flags&MPLEX_ATTACH_UNNUMBERED)){
+        lnode=ALLOC(WLListNode);
+        if(lnode==NULL){
+            stacking_free(node);
+            return FALSE;
+        }
+        lnode->next=NULL;
+        lnode->prev=NULL;
+        lnode->phs=NULL;
+        lnode->st=node;
+        node->lnode=lnode;
+    }
+    
+    if(!stacking_assoc(node, reg)){
+        if(lnode!=NULL){
+            node->lnode=NULL;
+            free(lnode);
+        }
+        stacking_free(node);
+        return FALSE;
+    }
+
+    node->hidden=TRUE;
+    node->szplcy=param->szplcy;
+    node->level=level;
+    
+    if(lnode!=NULL){
+        llist_link_after(&(mplex->mx_list), 
+                         (ph!=NULL ? ph->after : NULL), 
+                         lnode);
+        mplex->mx_count++;
+        
+        /* Move following placeholders after new node */
+        while(ph->next!=NULL)
+            mplexpholder_move(ph->next, mplex, NULL, lnode);
+    }
+    
+    LINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev);
+
+    if(!OBJ_IS(reg, WGroup))
+        mplex_stack(mplex, node);
+    
+    region_set_manager(reg, (WRegion*)mplex);
+    
+    if(!hidden)
+        mplex_do_node_display(mplex, node, FALSE);
+    else
+        region_unmap(reg);
+    
+    if(sw && mcf)
+        mplex_refocus(mplex, node, FALSE);
+    
+    if(lnode!=NULL)
+        mplex_managed_changed(mplex, MPLEX_CHANGE_ADD, sw, reg);
+    
+    return TRUE;
+}
+
+
+WRegion *mplex_do_attach_pholder(WMPlex *mplex, WMPlexPHolder *ph,
+                                 WRegionAttachData *data)
+{
+    WMPlexAttachParams *param=&(ph->param);
+    WSizePolicy szplcy=param->szplcy;
+    WFitParams fp;
+    WRegion *reg;
+    
+    param->szplcy=(param->flags&MPLEX_ATTACH_SIZEPOLICY &&
+                   param->szplcy!=SIZEPOLICY_DEFAULT
+                   ? param->szplcy
+                   : (param->flags&MPLEX_ATTACH_UNNUMBERED
+                      ? SIZEPOLICY_FULL_BOUNDS
+                      : SIZEPOLICY_FULL_EXACT));
+    
+    mplex_managed_geom(mplex, &(fp.g));
+    
+    sizepolicy(&param->szplcy, NULL, 
+               (param->flags&MPLEX_ATTACH_GEOM 
+                ? &(param->geom)
+                : NULL),
+               0, &fp);
+    
+    if(param->flags&MPLEX_ATTACH_WHATEVER)
+        fp.mode|=REGION_FIT_WHATEVER;
+    
+    reg=region_attach_helper((WRegion*)mplex, 
+                             (WWindow*)mplex, &fp,
+                             (WRegionDoAttachFn*)mplex_do_attach_final,
+                             (void*)ph, data);
+    
+    /* restore */
+    ph->param.szplcy=szplcy;
+    
+    return reg;
+}
+
+
+WRegion *mplex_do_attach(WMPlex *mplex, WMPlexAttachParams *param,
+                         WRegionAttachData *data)
+{
+    WMPlexPHolder *ph;
+    WRegion *reg;
+    
+    ph=create_mplexpholder(mplex, NULL, param);
+    
+    if(ph==NULL)
+        return NULL;
+
+    reg=mplex_do_attach_pholder(mplex, ph, data);
+    
+    destroy_obj((Obj*)ph);
+    
+    return reg;
+}
+
+
+WRegion *mplex_do_attach_new(WMPlex *mplex, WMPlexAttachParams *param,
+                             WRegionCreateFn *fn, void *fn_param)
+{
+    WRegionAttachData data;
+    
+    data.type=REGION_ATTACH_NEW;
+    data.u.n.fn=fn;
+    data.u.n.param=fn_param;
+    
+    return mplex_do_attach(mplex, param, &data);
+}
+
+
+#define MPLEX_ATTACH_SET_FLAGS (MPLEX_ATTACH_GEOM|       \
+                                MPLEX_ATTACH_SIZEPOLICY| \
+                                MPLEX_ATTACH_INDEX)
+
+
+WRegion *mplex_attach_simple(WMPlex *mplex, WRegion *reg, int flags)
+{
+    WMPlexAttachParams param;
+    WRegionAttachData data;
+    
+    param.flags=flags&~MPLEX_ATTACH_SET_FLAGS;
+    
+    data.type=REGION_ATTACH_REPARENT;
+    data.u.reg=reg;
+    
+    return mplex_do_attach(mplex, &param, &data);
+}
+
+
+static void get_params(WMPlex *mplex, ExtlTab tab, WMPlexAttachParams *par)
+{
+    int layer=1;
+    int tmp;
+    
+    par->flags=0;
+    
+    if(extl_table_gets_i(tab, "layer", &tmp)){
+        /* backwards compatibility */
+        if(tmp==2){
+            par->flags|=MPLEX_ATTACH_UNNUMBERED;
+            if(!extl_table_is_bool_set(tab, "passive"))
+                par->flags|=MPLEX_ATTACH_MODAL;
+        }
+    }
+
+    if(extl_table_gets_i(tab, "level", &tmp)){
+        if(tmp>=0){
+            par->flags|=MPLEX_ATTACH_LEVEL;
+            par->level=tmp;
+        }
+    }
+    
+    if(extl_table_is_bool_set(tab, "modal"))
+        par->flags|=MPLEX_ATTACH_MODAL;
+
+    if(extl_table_is_bool_set(tab, "unnumbered"))
+        par->flags|=MPLEX_ATTACH_UNNUMBERED;
+    
+    if(extl_table_is_bool_set(tab, "switchto"))
+        par->flags|=MPLEX_ATTACH_SWITCHTO;
+
+    if(extl_table_is_bool_set(tab, "hidden"))
+        par->flags|=MPLEX_ATTACH_HIDDEN;
+
+    if(extl_table_gets_i(tab, "index", &(par->index)))
+        par->flags|=MPLEX_ATTACH_INDEX;
+
+    if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
+        par->flags|=MPLEX_ATTACH_SIZEPOLICY;
+        par->szplcy=tmp;
+    }
+    
+    if(extl_table_gets_rectangle(tab, "geom", &par->geom))
+        par->flags|=MPLEX_ATTACH_GEOM;
+}
+
+
+/*EXTL_DOC
+ * Attach and reparent existing region \var{reg} to \var{mplex}.
+ * The table \var{param} may contain the fields \var{index} and
+ * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
+ */
+EXTL_EXPORT_MEMBER
+WRegion *mplex_attach(WMPlex *mplex, WRegion *reg, ExtlTab param)
+{
+    WMPlexAttachParams par;
+    WRegionAttachData data;
+
+    if(reg==NULL)
+        return NULL;
+    
+    get_params(mplex, param, &par);
+    
+    data.type=REGION_ATTACH_REPARENT;
+    data.u.reg=reg;
+    
+    return mplex_do_attach(mplex, &par, &data);
+}
+
+
+/*EXTL_DOC
+ * Create a new region to be managed by \var{mplex}. At least the following
+ * fields in \var{param} are understood (all but \var{type} are optional).
+ * 
+ * \begin{tabularx}{\linewidth}{lX}
+ *  \tabhead{Field & Description}
+ *  \var{type} & (string) Class name (a string) of the object to be created. \\
+ *  \var{name} & (string) Name of the object to be created (a string). \\
+ *  \var{switchto} & (boolean) Should the region be switched to (boolean)? \\
+ *  \var{unnumbered} & (boolean) Do not put on the numbered mutually 
+ *                     exclusive list. \\
+ *  \var{index} & (integer) Index on this list, same as for 
+ *                \fnref{WMPlex.set_index}. \\
+ *  \var{level} & (integer) Stacking level. \\
+ *  \var{modal} & (boolean) Shortcut for modal stacking level. \\
+ *  \var{hidden} & (boolean) Attach hidden, if not prevented
+ *                  by e.g. the mutually exclusive list being empty.
+ *                  This option overrides \var{switchto}. \\
+ *  \var{sizepolicy} & (integer) Size policy.
+ *                     (TODO: document them somewhere.) \\
+ *  \var{geom} & (table) Geometry specification. \\
+ * \end{tabularx}
+ * 
+ * In addition parameters to the region to be created are passed in this 
+ * same table.
+ */
+EXTL_EXPORT_MEMBER
+WRegion *mplex_attach_new(WMPlex *mplex, ExtlTab param)
+{
+    WMPlexAttachParams par;
+    WRegionAttachData data;
+    
+    get_params(mplex, param, &par);
+    
+    data.type=REGION_ATTACH_LOAD;
+    data.u.tab=param;
+    
+    return mplex_do_attach(mplex, &par, &data);
+}
+
+
+/*EXTL_DOC
+ * Attach all tagged regions to \var{mplex}.
+ */
+EXTL_EXPORT_MEMBER
+void mplex_attach_tagged(WMPlex *mplex)
+{
+    WRegion *reg;
+    
+    while((reg=ioncore_tags_take_first())!=NULL)
+        mplex_attach_simple(mplex, reg, 0);
+}
+
+
+static bool mplex_handle_drop(WMPlex *mplex, int x, int y,
+                              WRegion *dropped)
+{
+    WRegion *curr=mplex_mx_current(mplex);
+    
+    /* This code should handle dropping tabs on floating workspaces. */
+    if(curr && HAS_DYN(curr, region_handle_drop)){
+        int rx, ry;
+        region_rootpos(curr, &rx, &ry);
+        if(rectangle_contains(&REGION_GEOM(curr), x-rx, y-ry)){
+            if(region_handle_drop(curr, x, y, dropped))
+                return TRUE;
+        }
+    }
+    
+    return (NULL!=mplex_attach_simple(mplex, dropped, MPLEX_ATTACH_SWITCHTO));
+}
+
+
+WPHolder *mplex_prepare_manage(WMPlex *mplex, const WClientWin *cwin,
+                               const WManageParams *param, int redir)
+{
+    WMPlexAttachParams ap;
+    WPHolder *ph=NULL;
+    WMPlexPHolder *mph;
+    WLListNode *after;
+    
+    if(redir==MANAGE_REDIR_STRICT_YES || redir==MANAGE_REDIR_PREFER_YES){
+        WStacking *cur=mplex_current_node(mplex);
+        
+        if(cur!=NULL){
+            ph=region_prepare_manage(cur->reg, cwin, param,
+                                     MANAGE_REDIR_PREFER_YES);
+            if(ph!=NULL)
+                return ph;
+        }
+        
+        if(mplex->mx_current!=NULL && mplex->mx_current->st!=cur){
+            ph=region_prepare_manage(mplex->mx_current->st->reg, 
+                                     cwin, param, 
+                                     MANAGE_REDIR_PREFER_YES);
+            if(ph!=NULL)
+                return ph;
+        }
+    }
+    
+    if(redir==MANAGE_REDIR_STRICT_YES)
+        return NULL;
+    
+    ap.flags=((param->switchto ? MPLEX_ATTACH_SWITCHTO : 0)
+              |MPLEX_ATTACH_SIZEPOLICY);
+    ap.szplcy=SIZEPOLICY_FULL_BOUNDS;
+    
+    mph=create_mplexpholder(mplex, NULL, &ap);
+    
+    if(mph!=NULL){
+        WGroupedPHolder *gph=create_groupedpholder((WPHolder*)mph);
+        if(gph!=NULL)
+            return (WPHolder*)gph;
+    }
+    
+    return (WPHolder*)mph;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Remove */
+
+
+void mplex_managed_remove(WMPlex *mplex, WRegion *sub)
+{
+    bool mx=FALSE, hadfocus=FALSE, mcf;
+    WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj);
+    WStacking *node, *next=NULL;
+    mcf=region_may_control_focus((WRegion*)mplex);
+    
+    if(stdisp!=NULL){
+        if(CAN_MANAGE_STDISP(sub) && 
+           region_managed_within((WRegion*)mplex, stdisp)==sub){
+            region_unmanage_stdisp(sub, TRUE, TRUE);
+            region_detach_manager(stdisp);
+        }
+    }
+    
+    node=mplex_find_stacking(mplex, sub);
+    
+    if(node==NULL)
+        return;
+    
+    hadfocus=(mplex_current_node(mplex)==node);
+    
+    if(node->lnode!=NULL){
+        if(mplex->mx_current==node->lnode){
+            WLListNode *lnext;
+            
+            mplex->mx_current=NULL;
+            lnext=LIST_PREV(mplex->mx_list, node->lnode, next, prev);
+            if(lnext==NULL){
+                lnext=LIST_NEXT(mplex->mx_list, node->lnode, next, prev);
+                if(lnext==node->lnode)
+                    lnext=NULL;
+            }
+            if(lnext!=NULL)
+                next=lnext->st;
+        }
+        
+        mplex_move_phs_before(mplex, node->lnode);
+        llist_unlink(&(mplex->mx_list), node->lnode);
+        mplex->mx_count--;
+
+        free(node->lnode);
+        node->lnode=NULL;
+        mx=TRUE;
+    }
+    
+    UNLINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev);
+    
+    mplex_unstack(mplex, node);
+    
+    stacking_unassoc(node);
+    stacking_free(node);
+
+    region_unset_manager(sub, (WRegion*)mplex);
+    
+    if(OBJ_IS_BEING_DESTROYED(mplex))
+        return;
+    
+    if(next!=NULL)
+        mplex_do_node_display(mplex, next, FALSE);
+
+    if(hadfocus && mcf)
+        mplex_refocus(mplex, next, FALSE);
+    
+    if(mx)
+        mplex_managed_changed(mplex, MPLEX_CHANGE_REMOVE, next!=NULL, sub);
+}
+
+
+bool mplex_rescue_clientwins(WMPlex *mplex, WPHolder *ph)
+{
+    bool ret1, ret2;
+    WMPlexIterTmp tmp;
+    
+    mplex_iter_init(&tmp, mplex);
+    ret1=region_rescue_some_clientwins((WRegion*)mplex, ph,
+                                       (WRegionIterator*)mplex_iter,
+                                       &tmp);
+    
+    ret2=region_rescue_child_clientwins((WRegion*)mplex, ph);
+    
+    return (ret1 && ret2);
+}
+
+
+
+void mplex_child_removed(WMPlex *mplex, WRegion *sub)
+{
+    if(sub!=NULL && sub==(WRegion*)(mplex->stdispwatch.obj)){
+        watch_reset(&(mplex->stdispwatch));
+        mplex_set_stdisp(mplex, NULL, NULL);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Status display support */
+
+#ifndef offsetof
+# define offsetof(T,F) ((size_t)((char*)&((T*)0L)->F-(char*)0L))
+#endif
+
+#define STRUCTOF(T, F, FADDR) \
+        ((T*)((char*)(FADDR)-offsetof(T, F)))
+
+
+static void stdisp_watch_handler(Watch *watch, Obj *obj)
+{
+    /*WMPlex *mplex=STRUCTOF(WMPlex, stdispinfo, 
+     STRUCTOF(WMPlexSTDispInfo, regwatch, watch));
+     WMPlexSTDispInfo *di=&(mplex->stdispinfo);
+     WGenWS *ws=OBJ_CAST(REGION_MANAGER(obj), WGenWS);
+     * 
+     if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && ws!=NULL)
+     genws_unmanage_stdisp(ws, TRUE, FALSE);*/
+}
+
+
+bool mplex_set_stdisp(WMPlex *mplex, WRegion *reg, 
+                      const WMPlexSTDispInfo *din)
+{
+    WRegion *oldstdisp=(WRegion*)(mplex->stdispwatch.obj);
+    WRegion *mgr=NULL;
+    
+    assert(reg==NULL || (reg==oldstdisp) ||
+           (REGION_MANAGER(reg)==NULL && 
+            REGION_PARENT(reg)==(WWindow*)mplex));
+    
+    if(oldstdisp!=NULL){
+        mgr=region_managed_within((WRegion*)mplex, oldstdisp);
+        
+        if(!CAN_MANAGE_STDISP(mgr))
+            mgr=NULL;
+        
+        if(oldstdisp!=reg){
+            mainloop_defer_destroy((Obj*)oldstdisp);
+            watch_reset(&(mplex->stdispwatch));
+        }
+    }
+    
+    if(din!=NULL)
+        mplex->stdispinfo=*din;
+    
+    if(reg==NULL){
+        if(mgr!=NULL){
+            region_unmanage_stdisp(mgr, TRUE, FALSE);
+            if(oldstdisp!=NULL)
+                region_detach_manager(oldstdisp);
+        }
+    }else{
+        watch_setup(&(mplex->stdispwatch), (Obj*)reg, stdisp_watch_handler);
+        
+        mplex_remanage_stdisp(mplex);
+    }
+    
+    return TRUE;
+}
+
+
+void mplex_get_stdisp(WMPlex *mplex, WRegion **reg, WMPlexSTDispInfo *di)
+{
+    *di=mplex->stdispinfo;
+    *reg=(WRegion*)mplex->stdispwatch.obj;
+}
+
+
+static StringIntMap pos_map[]={
+    {"tl", MPLEX_STDISP_TL},
+    {"tr", MPLEX_STDISP_TR},
+    {"bl", MPLEX_STDISP_BL},
+    {"br", MPLEX_STDISP_BR},
+    {NULL, 0}
+};
+
+
+static bool do_attach_stdisp(WRegion *mplex, WRegion *reg, void *unused)
+{
+    /* We do not actually manage the stdisp. */
+    return TRUE;
+}
+
+
+/*EXTL_DOC
+ * Set/create status display for \var{mplex}. Table is a standard
+ * description of the object to be created (as passed to e.g. 
+ * \fnref{WMPlex.attach_new}). In addition, the following fields are
+ * recognised:
+ * 
+ * \begin{tabularx}{\linewidth}{lX}
+ *   \tabhead{Field & Description}
+ *   \var{pos} & The corner of the screen to place the status display
+ *               in. One of \code{tl}, \code{tr}, \var{bl} or \var{br}. \\
+ *   \var{action} & If this field is set to \code{keep}, \var{corner}
+ *                  and \var{orientation} are changed for the existing
+ *                  status display. If this field is set to \var{remove},
+ *                  the existing status display is removed. If this
+ *                  field is not set or is set to \code{replace}, a 
+ *                  new status display is created and the old, if any,
+ *                  removed. \\
+ * \end{tabularx}
+ */
+EXTL_EXPORT_AS(WMPlex, set_stdisp)
+WRegion *mplex_set_stdisp_extl(WMPlex *mplex, ExtlTab t)
+{
+    WRegion *stdisp=NULL;
+    WMPlexSTDispInfo din=mplex->stdispinfo;
+    char *s;
+    
+    if(extl_table_gets_s(t, "pos", &s)){
+        din.pos=stringintmap_value(pos_map, s, -1);
+        if(din.pos<0){
+            warn(TR("Invalid position setting."));
+            return NULL;
+        }
+    }
+    
+    extl_table_gets_b(t, "fullsize", &(din.fullsize));
+    
+    s=NULL;
+    extl_table_gets_s(t, "action", &s);
+    
+    if(s==NULL || strcmp(s, "replace")==0){
+        WRegionAttachData data;
+        WFitParams fp;
+        int o2;
+        
+        fp.g.x=0;
+        fp.g.y=0;
+        fp.g.w=REGION_GEOM(mplex).w;
+        fp.g.h=REGION_GEOM(mplex).h;
+        fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
+        
+        /* Full mplex size is stupid so use saved geometry initially
+         * if there's one.
+         */
+        extl_table_gets_rectangle(t, "geom", &(fp.g));
+        
+        data.type=REGION_ATTACH_LOAD;
+        data.u.tab=t;
+            
+        stdisp=region_attach_helper((WRegion*)mplex, 
+                                    (WWindow*)mplex, &fp,
+                                    do_attach_stdisp, NULL,
+                                    &data);
+        
+        if(stdisp==NULL)
+            return NULL;
+        
+    }else if(strcmp(s, "keep")==0){
+        stdisp=(WRegion*)(mplex->stdispwatch.obj);
+    }else if(strcmp(s, "remove")!=0){
+        warn(TR("Invalid action setting."));
+        return FALSE;
+    }
+    
+    if(!mplex_set_stdisp(mplex, stdisp, &din)){
+        destroy_obj((Obj*)stdisp);
+        return NULL;
+    }
+    
+    return stdisp;
+}
+
+
+static ExtlTab mplex_do_get_stdisp_extl(WMPlex *mplex, bool fullconfig)
+{
+    WRegion *reg=(WRegion*)mplex->stdispwatch.obj;
+    ExtlTab t;
+    
+    if(reg==NULL)
+        return extl_table_none();
+    
+    if(fullconfig){
+        t=region_get_configuration(reg);
+        extl_table_sets_rectangle(t, "geom", &REGION_GEOM(reg));
+    }else{
+        t=extl_create_table();
+        extl_table_sets_o(t, "reg", (Obj*)reg);
+    }
+    
+    if(t!=extl_table_none()){
+        WMPlexSTDispInfo *di=&(mplex->stdispinfo);
+        extl_table_sets_s(t, "pos", stringintmap_key(pos_map, di->pos, NULL));
+        extl_table_sets_b(t, "fullsize", di->fullsize);
+    }
+    return t;
+}
+
+
+/*EXTL_DOC
+ * Get status display information. See \fnref{WMPlex.get_stdisp} for
+ * information on the fields.
+ */
+EXTL_SAFE
+EXTL_EXPORT_AS(WMPlex, get_stdisp)
+ExtlTab mplex_get_stdisp_extl(WMPlex *mplex)
+{
+    return mplex_do_get_stdisp_extl(mplex, FALSE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuns */
+
+
+void mplex_managed_geom_default(const WMPlex *mplex, WRectangle *geom)
+{
+    geom->x=0;
+    geom->y=0;
+    geom->w=REGION_GEOM(mplex).w;
+    geom->h=REGION_GEOM(mplex).h;
+}
+
+
+void mplex_managed_geom(const WMPlex *mplex, WRectangle *geom)
+{
+    CALL_DYN(mplex_managed_geom, mplex, (mplex, geom));
+}
+
+
+void mplex_size_changed(WMPlex *mplex, bool wchg, bool hchg)
+{
+    CALL_DYN(mplex_size_changed, mplex, (mplex, wchg, hchg));
+}
+
+
+void mplex_managed_changed(WMPlex *mplex, int mode, bool sw, WRegion *mgd)
+{
+    CALL_DYN(mplex_managed_changed, mplex, (mplex, mode, sw, mgd));
+}
+
+
+int mplex_default_index(WMPlex *mplex)
+{
+    int idx=LLIST_INDEX_LAST;
+    CALL_DYN_RET(idx, int, mplex_default_index, mplex, (mplex));
+    return idx;
+}
+
+
+/* For regions managing stdisps */
+
+void region_manage_stdisp(WRegion *reg, WRegion *stdisp, 
+                          const WMPlexSTDispInfo *info)
+{
+    CALL_DYN(region_manage_stdisp, reg, (reg, stdisp, info));
+}
+
+
+void region_unmanage_stdisp(WRegion *reg, bool permanent, bool nofocus)
+{
+    CALL_DYN(region_unmanage_stdisp, reg, (reg, permanent, nofocus));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Changed hook helper */
+
+
+static const char *mode2str(int mode)
+{
+    if(mode==MPLEX_CHANGE_SWITCHONLY)
+        return "switchonly";
+    else if(mode==MPLEX_CHANGE_REORDER)
+        return "reorder";
+    else if(mode==MPLEX_CHANGE_ADD)
+        return "add";
+    else if(mode==MPLEX_CHANGE_REMOVE)
+        return "remove";
+    return NULL;
+}
+    
+
+static bool mrsh_chg(ExtlFn fn, WMPlexChangedParams *p)
+{
+    ExtlTab t=extl_create_table();
+    bool ret;
+    
+    extl_table_sets_o(t, "reg", (Obj*)p->reg);
+    extl_table_sets_s(t, "mode", mode2str(p->mode));
+    extl_table_sets_b(t, "sw", p->sw);
+    extl_table_sets_o(t, "sub", (Obj*)p->sub);
+    
+    extl_protect(NULL);
+    ret=extl_call(fn, "t", NULL, t);
+    extl_unprotect(NULL);
+    
+    extl_unref_table(t);
+    
+    return ret;
+}
+
+
+void mplex_call_changed_hook(WMPlex *mplex, WHook *hook, 
+                             int mode, bool sw, WRegion *reg)
+{
+    WMPlexChangedParams p;
+    
+    p.reg=mplex;
+    p.mode=mode;
+    p.sw=sw;
+    p.sub=reg;
+
+    hook_call_p(hook, &p, (WHookMarshallExtl*)mrsh_chg);
+}
+
+
+/*}}} */
+
+
+/*{{{ Save/load */
+
+
+static void save_node(WMPlex *mplex, ExtlTab subs, int *n, 
+                      WStacking *node, bool unnumbered)
+{
+    ExtlTab st, g;
+    
+    st=region_get_configuration(node->reg);
+    
+    if(st!=extl_table_none()){
+        /*"TODO: better switchto saving? */
+        if(mplex->mx_current!=NULL && node==mplex->mx_current->st)
+            extl_table_sets_b(st, "switchto", TRUE);
+        extl_table_sets_i(st, "sizepolicy", node->szplcy);
+        extl_table_sets_i(st, "level", node->level);
+        g=extl_table_from_rectangle(&REGION_GEOM(node->reg));
+        extl_table_sets_t(st, "geom", g);
+        extl_unref_table(g);
+        if(STACKING_IS_HIDDEN(node))
+            extl_table_sets_b(st, "hidden", TRUE);
+        if(unnumbered)
+            extl_table_sets_b(st, "unnumbered", TRUE);
+        
+        extl_table_seti_t(subs, ++(*n), st);
+        extl_unref_table(st);
+    }
+}
+
+
+ExtlTab mplex_get_configuration(WMPlex *mplex)
+{
+    ExtlTab tab, subs, stdisptab;
+    WMPlexIterTmp tmp;
+    WLListIterTmp ltmp;
+    WLListNode *lnode;
+    WStacking *node;
+    int n=0;
+    
+    tab=region_get_base_configuration((WRegion*)mplex);
+    
+    subs=extl_create_table();
+    extl_table_sets_t(tab, "subs", subs);
+    
+    /* First the numbered/mutually exclusive nodes */
+    FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, ltmp){
+        save_node(mplex, subs, &n, lnode->st, FALSE);
+    }
+
+    FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){
+        if(node->lnode==NULL)
+            save_node(mplex, subs, &n, node, TRUE);
+    }
+
+    extl_unref_table(subs);
+    
+    /*stdisptab=mplex_do_get_stdisp_extl(mplex, TRUE);
+    if(stdisptab!=extl_table_none()){
+        extl_table_sets_t(tab, "stdisp", stdisptab);
+        extl_unref_table(stdisptab);
+    }*/
+    
+    return tab;
+}
+
+
+static WMPlex *tmp_mplex=NULL;
+static WMPlexAttachParams *tmp_par=NULL;
+
+static WPHolder *pholder_callback()
+{
+    assert(tmp_mplex!=NULL);
+    return (WPHolder*)create_mplexpholder(tmp_mplex, NULL, tmp_par);
+}
+
+
+void mplex_load_contents(WMPlex *mplex, ExtlTab tab)
+{
+    ExtlTab substab, subtab;
+    int n, i;
+
+    /*if(extl_table_gets_t(tab, "stdisp", &subtab)){
+        mplex_set_stdisp_extl(mplex, subtab);
+        extl_unref_table(subtab);
+    }*/
+    
+    if(extl_table_gets_t(tab, "subs", &substab)){
+        n=extl_table_get_n(substab);
+        for(i=1; i<=n; i++){
+            if(extl_table_geti_t(substab, i, &subtab)){
+                /*mplex_attach_new(mplex, subtab);*/
+                WMPlexAttachParams par;
+                WRegionAttachData data;
+                char *tmp=NULL;
+                
+                get_params(mplex, subtab, &par);
+                
+                par.flags|=MPLEX_ATTACH_INDEX;
+                par.index=LLIST_INDEX_LAST;
+                
+                tmp_par=&par;
+                tmp_mplex=mplex;
+                
+                data.type=REGION_ATTACH_LOAD;
+                data.u.tab=subtab;
+                
+                ioncore_set_sm_pholder_callback(pholder_callback);
+    
+                mplex_do_attach(mplex, &par, &data);
+
+                tmp_mplex=NULL;
+                
+                extl_unref_table(subtab);
+            }
+        }
+        extl_unref_table(substab);
+    }
+}
+
+
+WRegion *mplex_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
+{
+    WMPlex *mplex=create_mplex(par, fp);
+    if(mplex!=NULL)
+        mplex_load_contents(mplex, tab);
+    return (WRegion*)mplex;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuntab and class info */
+
+
+static DynFunTab mplex_dynfuntab[]={
+    {region_do_set_focus, 
+     mplex_do_set_focus},
+    
+    {region_managed_remove, 
+     mplex_managed_remove},
+    
+    {region_managed_rqgeom,
+     mplex_managed_rqgeom},
+    
+    {(DynFun*)region_managed_prepare_focus,
+     (DynFun*)mplex_managed_prepare_focus},
+    
+    {(DynFun*)region_handle_drop, 
+     (DynFun*)mplex_handle_drop},
+    
+    {region_map, mplex_map},
+    {region_unmap, mplex_unmap},
+    
+    {(DynFun*)region_prepare_manage,
+     (DynFun*)mplex_prepare_manage},
+    
+    {(DynFun*)region_current,
+     (DynFun*)mplex_current},
+    
+    {(DynFun*)region_rescue_clientwins,
+     (DynFun*)mplex_rescue_clientwins},
+    
+    {(DynFun*)region_get_configuration,
+     (DynFun*)mplex_get_configuration},
+    
+    {mplex_managed_geom, 
+     mplex_managed_geom_default},
+    
+    {(DynFun*)region_fitrep,
+     (DynFun*)mplex_fitrep},
+    
+    {region_child_removed,
+     mplex_child_removed},
+    
+    {(DynFun*)region_managed_get_pholder,
+     (DynFun*)mplex_managed_get_pholder},
+
+    {(DynFun*)region_get_rescue_pholder_for,
+     (DynFun*)mplex_get_rescue_pholder_for},
+
+    {(DynFun*)region_may_destroy,
+     (DynFun*)mplex_may_destroy},
+    
+    {(DynFun*)region_navi_first,
+     (DynFun*)mplex_navi_first},
+    
+    {(DynFun*)region_navi_next,
+     (DynFun*)mplex_navi_next},
+    
+    {(DynFun*)region_managed_rqorder,
+     (DynFun*)mplex_managed_rqorder},
+    
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WMPlex, WWindow, mplex_deinit, mplex_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/ioncore/mplex.h b/ioncore/mplex.h
new file mode 100644 (file)
index 0000000..889f387
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * ion/ioncore/mplex.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ * 
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_MPLEX_H
+#define ION_IONCORE_MPLEX_H
+
+#include <libextl/extl.h>
+#include <libmainloop/hooks.h>
+#include "common.h"
+#include "window.h"
+#include "attach.h"
+#include "manage.h"
+#include "rectangle.h"
+#include "pholder.h"
+#include "sizepolicy.h"
+
+
+#define MPLEX_ADD_TO_END 0x0001
+#define MPLEX_MANAGED_UNVIEWABLE 0x0002
+
+#define MPLEX_MGD_UNVIEWABLE(MPLEX) \
+    ((MPLEX)->flags&MPLEX_MANAGED_UNVIEWABLE)
+
+
+#define MPLEX_ATTACH_SWITCHTO     0x0001 /* switch to region */
+#define MPLEX_ATTACH_UNNUMBERED   0x0002 /* do not put on mut.ex list */
+#define MPLEX_ATTACH_HIDDEN       0x0004 /* should be hidden */
+#define MPLEX_ATTACH_MODAL        0x0008 /* shortcut.. */
+#define MPLEX_ATTACH_LEVEL        0x0010 /* level field set */
+#define MPLEX_ATTACH_GEOM         0x0020 /* geometry field is set */
+#define MPLEX_ATTACH_SIZEPOLICY   0x0040 /* size policy field is set */
+#define MPLEX_ATTACH_INDEX        0x0080 /* index field is set */
+#define MPLEX_ATTACH_WHATEVER     0x0100 /* set REGION_FIT_WHATEVER */
+
+
+enum{
+    MPLEX_CHANGE_SWITCHONLY=0,
+    MPLEX_CHANGE_REORDER=1,
+    MPLEX_CHANGE_ADD=2,
+    MPLEX_CHANGE_REMOVE=3
+};
+
+
+enum{
+    MPLEX_STDISP_TL,
+    MPLEX_STDISP_TR,
+    MPLEX_STDISP_BL,
+    MPLEX_STDISP_BR
+};
+
+
+INTRSTRUCT(WMPlexSTDispInfo);
+INTRSTRUCT(WMPlexChangedParams);
+INTRSTRUCT(WMPlexAttachParams);
+
+
+DECLSTRUCT(WMPlexSTDispInfo){
+    int pos;
+    bool fullsize;
+};
+
+
+DECLSTRUCT(WMPlexChangedParams){
+    WMPlex *reg;
+    int mode;
+    bool sw;
+    WRegion *sub;
+};
+
+
+#define MPLEXATTACHPARAMS_INIT {0, 0, {0, 0, 0, 0}, 0, 0}
+
+DECLSTRUCT(WMPlexAttachParams){
+    int flags;
+    int index;
+    WRectangle geom;
+    WSizePolicy szplcy;
+    uint level;
+};
+
+
+DECLCLASS(WMPlex){
+    WWindow win;
+    int flags;
+    
+    WStacking *mgd;
+    
+    int mx_count;
+    WLListNode *mx_current;
+    WLListNode *mx_list;
+    WMPlexPHolder *mx_phs;
+    
+    Watch stdispwatch;
+    WMPlexSTDispInfo stdispinfo;
+};
+
+
+/* Create/destroy */
+
+extern WMPlex *create_mplex(WWindow *parent, const WFitParams *fp);
+extern bool mplex_init(WMPlex *mplex, WWindow *parent,
+                       const WFitParams *fp);
+extern bool mplex_do_init(WMPlex *mplex, WWindow *parent, Window win,
+                          const WFitParams *fp, bool create);
+extern void mplex_deinit(WMPlex *mplex);
+
+/* Resize and reparent */
+
+extern bool mplex_fitrep(WMPlex *mplex, WWindow *par, const WFitParams *fp);
+extern void mplex_do_fit_managed(WMPlex *mplex, WFitParams *fp);
+extern void mplex_fit_managed(WMPlex *mplex);
+
+/* Mapping */
+
+extern void mplex_map(WMPlex *mplex);
+extern void mplex_unmap(WMPlex *mplex);
+
+/* Attach */
+
+extern WRegion *mplex_attach_simple(WMPlex *mplex, WRegion *reg, 
+                                    int flags);
+
+extern WRegion *mplex_attach(WMPlex *mplex, WRegion *reg, ExtlTab param);
+extern WRegion *mplex_attach_new(WMPlex *mplex, ExtlTab param);
+
+extern WRegion *mplex_do_attach_pholder(WMPlex *mplex, WMPlexPHolder *ph,
+                                        WRegionAttachData *data);
+extern WRegion *mplex_do_attach(WMPlex *mplex, WMPlexAttachParams *param,
+                                WRegionAttachData *data);
+extern WRegion *mplex_do_attach_new(WMPlex *mplex, WMPlexAttachParams *param,
+                                    WRegionCreateFn *fn, void *fn_param);
+
+extern void mplex_attach_tagged(WMPlex *mplex);
+
+extern void mplex_managed_remove(WMPlex *mplex, WRegion *reg);
+extern void mplex_child_removed(WMPlex *mplex, WRegion *sub);
+
+extern bool mplex_rescue_clientwins(WMPlex *mplex, WPHolder *ph);
+
+extern WPHolder *mplex_prepare_manage(WMPlex *mplex, const WClientWin *cwin,
+                                      const WManageParams *param, int redir);
+
+/* Switch */
+
+extern bool mplex_managed_prepare_focus(WMPlex *mplex, WRegion *sub, 
+                                        int flags, WPrepareFocusResult *res);
+extern bool mplex_do_prepare_focus(WMPlex *mplex, WStacking *disp, 
+                                   WStacking *sub, int flags, 
+                                   WPrepareFocusResult *res);
+
+extern void mplex_switch_nth(WMPlex *mplex, uint n);
+extern void mplex_switch_next(WMPlex *mplex);
+extern void mplex_switch_prev(WMPlex *mplex);
+extern bool mplex_is_hidden(WMPlex *mplex, WRegion *reg);
+extern bool mplex_set_hidden(WMPlex *mplex, WRegion *reg, int sp);
+
+/* Focus */
+
+extern void mplex_do_set_focus(WMPlex *mplex, bool warp);
+
+/* Stacking */
+
+extern bool mplex_managed_rqorder(WMPlex *mplex, WRegion *sub, 
+                                  WRegionOrder order);
+
+/* Misc */
+
+extern WRegion *mplex_current(WMPlex *mplex);
+extern bool mplex_may_destroy(WMPlex *mplex);
+
+extern int mplex_mx_count(WMPlex *mplex);
+extern WRegion *mplex_mx_nth(WMPlex *mplex, uint n);
+extern ExtlTab mplex_mx_list(WMPlex *mplex);
+extern WRegion *mplex_mx_current(WMPlex *mplex);
+
+extern void mplex_call_changed_hook(WMPlex *mplex, WHook *hook, 
+                                    int mode, bool sw, WRegion *reg);
+
+extern WLListNode *mplex_find_node(WMPlex *mplex, WRegion *reg);
+
+extern void mplex_remanage_stdisp(WMPlex *mplex);
+
+/* Dynfuns */
+
+DYNFUN void mplex_managed_geom(const WMPlex *mplex, WRectangle *geom);
+DYNFUN void mplex_size_changed(WMPlex *mplex, bool wchg, bool hchg);
+DYNFUN void mplex_managed_changed(WMPlex *mplex, int what, bool sw,
+                                  WRegion *mgd);
+DYNFUN int mplex_default_index(WMPlex *mplex);
+
+/* Save/load */
+
+extern ExtlTab mplex_get_configuration(WMPlex *mplex);
+extern WRegion *mplex_load(WWindow *par, const WFitParams *fp, ExtlTab tab);
+extern void mplex_load_contents(WMPlex *frame, ExtlTab tab);
+
+
+/* Sticky status display support */
+
+extern bool mplex_set_stdisp(WMPlex *mplex, WRegion *stdisp, 
+                             const WMPlexSTDispInfo *info);
+extern void mplex_get_stdisp(WMPlex *mplex, WRegion **stdisp, 
+                             WMPlexSTDispInfo *info);
+
+extern WRegion *mplex_set_stdisp_extl(WMPlex *mplex, ExtlTab t);
+extern ExtlTab mplex_get_stdisp_extl(WMPlex *mplex);
+
+DYNFUN void region_manage_stdisp(WRegion *reg, WRegion *stdisp, 
+                                 const WMPlexSTDispInfo *info);
+DYNFUN void region_unmanage_stdisp(WRegion *reg, bool permanent, bool nofocus);
+
+/* Stacking list stuff */
+
+typedef WStackingIterTmp WMPlexIterTmp;
+
+extern void mplex_iter_init(WMPlexIterTmp *tmp, WMPlex *ws);
+extern WRegion *mplex_iter(WMPlexIterTmp *tmp);
+extern WStacking *mplex_iter_nodes(WMPlexIterTmp *tmp);
+
+extern WStacking *mplex_get_stacking(WMPlex *ws);
+extern WStacking **mplex_get_stackingp(WMPlex *ws);
+
+#define FOR_ALL_MANAGED_BY_MPLEX(MPLEX, VAR, TMP) \
+    for(mplex_iter_init(&(TMP), MPLEX),           \
+         VAR=mplex_iter(&(TMP));                  \
+        VAR!=NULL;                                \
+        VAR=mplex_iter(&(TMP)))
+
+#define FOR_ALL_NODES_IN_MPLEX(MPLEX, VAR, TMP) \
+    for(mplex_iter_init(&(TMP), MPLEX),         \
+         VAR=mplex_iter_nodes(&(TMP));          \
+        VAR!=NULL;                              \
+        VAR=mplex_iter_nodes(&(TMP)))
+
+extern WStacking *mplex_find_stacking(WMPlex *mplex, WRegion *reg);
+
+#endif /* ION_IONCORE_MPLEX_H */
diff --git a/ioncore/mplexpholder.c b/ioncore/mplexpholder.c
new file mode 100644 (file)
index 0000000..51166f7
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * ion/ioncore/mplexpholder.c
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/objp.h>
+#include <libtu/obj.h>
+#include <libtu/pointer.h>
+
+#include "common.h"
+#include "mplex.h"
+#include "mplexpholder.h"
+#include "llist.h"
+
+
+static void mplex_watch_handler(Watch *watch, Obj *mplex);
+
+
+/*{{{ Primitives */
+
+
+static bool on_ph_list(WMPlexPHolder *ll, WMPlexPHolder *ph)
+{
+    return ph->prev!=NULL;
+}
+
+
+static void mplexpholder_do_link(WMPlexPHolder *ph, 
+                                 WMPlex *mplex,
+                                 WMPlexPHolder *after,
+                                 WLListNode *or_after)
+{
+    assert(mplex==(WMPlex*)ph->mplex_watch.obj && mplex!=NULL);
+    
+    if(after!=NULL){
+        assert(after->after==or_after);
+        
+        if(after->after!=NULL){
+            LINK_ITEM_AFTER(after->after->phs, after, ph, next, prev);
+        }else{
+            assert(on_ph_list(mplex->mx_phs, after));
+            LINK_ITEM_AFTER(mplex->mx_phs, after, ph, next, prev);
+        }
+        ph->after=after->after;
+    }else if(or_after!=NULL){
+        LINK_ITEM_FIRST(or_after->phs, ph, next, prev);
+        ph->after=or_after;
+    }else{
+        LINK_ITEM_FIRST(mplex->mx_phs, ph, next, prev);
+        ph->after=NULL;
+    }
+}
+
+
+void mplexpholder_do_unlink(WMPlexPHolder *ph, WMPlex *mplex)
+{
+    if(ph->after!=NULL){
+        UNLINK_ITEM(ph->after->phs, ph, next, prev);
+    }else if(mplex!=NULL && on_ph_list(mplex->mx_phs, ph)){
+        UNLINK_ITEM(mplex->mx_phs, ph, next, prev);
+    }else{
+        assert(ph->next==NULL && ph->prev==NULL);
+    }
+    
+    ph->after=NULL;
+    ph->next=NULL;
+    ph->prev=NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init/deinit */
+
+
+static void mplex_watch_handler(Watch *watch, Obj *mplex)
+{
+    WMPlexPHolder *ph=FIELD_TO_STRUCT(WMPlexPHolder, mplex_watch, watch);
+    mplexpholder_do_unlink(ph, (WMPlex*)mplex);
+    pholder_redirect(&(ph->ph), (WRegion*)mplex);
+}
+
+
+static WMPlexAttachParams dummy_param={0, 0, {0, 0, 0, 0}, 0, 0};
+
+
+bool mplexpholder_init(WMPlexPHolder *ph, WMPlex *mplex, WStacking *st,
+                       WMPlexAttachParams *param)
+{
+    pholder_init(&(ph->ph));
+
+    watch_init(&(ph->mplex_watch));
+    ph->after=NULL;
+    ph->next=NULL;
+    ph->prev=NULL;
+    ph->param.flags=0;
+    
+    if(!watch_setup(&(ph->mplex_watch), (Obj*)mplex, mplex_watch_handler)){
+        pholder_deinit(&(ph->ph));
+        return FALSE;
+    }
+
+    if(param==NULL)
+        param=&dummy_param;
+    
+    if(st!=NULL){
+        if(st->lnode!=NULL)
+            mplexpholder_do_link(ph, mplex, 
+                                 LIST_LAST(st->lnode->phs, next, prev), 
+                                 st->lnode);
+        else
+            ph->param.flags|=MPLEX_ATTACH_UNNUMBERED;
+        
+        ph->param.flags|=(MPLEX_ATTACH_SIZEPOLICY|
+                          MPLEX_ATTACH_GEOM|
+                          MPLEX_ATTACH_LEVEL|
+                          (st->hidden ? MPLEX_ATTACH_HIDDEN : 0));
+        ph->param.szplcy=st->szplcy;
+        ph->param.geom=REGION_GEOM(st->reg);
+        ph->param.level=st->level;
+    }else{
+        ph->param=*param;
+        
+        if(!(param->flags&MPLEX_ATTACH_UNNUMBERED)){
+            int index=(param->flags&MPLEX_ATTACH_INDEX
+                       ? param->index
+                       : mplex_default_index(mplex));
+            WLListNode *or_after=llist_index_to_after(mplex->mx_list, 
+                                                      mplex->mx_current, 
+                                                      index);
+            WMPlexPHolder *after=(index==LLIST_INDEX_LAST
+                                  ? (or_after!=NULL
+                                     ? LIST_LAST(or_after->phs, next, prev) 
+                                     : LIST_LAST(mplex->mx_phs, next, prev))
+                                  : NULL);
+
+            mplexpholder_do_link(ph, mplex, after, or_after);
+        }
+    }
+    
+    return TRUE;
+}
+
+WMPlexPHolder *create_mplexpholder(WMPlex *mplex,
+                                   WStacking *st,
+                                   WMPlexAttachParams *param)
+{
+    CREATEOBJ_IMPL(WMPlexPHolder, mplexpholder, (p, mplex, st, param));
+}
+
+
+void mplexpholder_deinit(WMPlexPHolder *ph)
+{
+    mplexpholder_do_unlink(ph, (WMPlex*)ph->mplex_watch.obj);
+    watch_reset(&(ph->mplex_watch));
+    pholder_deinit(&(ph->ph));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Move, attach, layer */
+
+
+WRegion *mplexpholder_do_attach(WMPlexPHolder *ph, int flags,
+                                WRegionAttachData *data)
+{
+    WMPlex *mplex=(WMPlex*)ph->mplex_watch.obj;
+    WRegion *reg=NULL;
+    
+    if(mplex==NULL)
+        return FALSE;
+    
+    if(flags&PHOLDER_ATTACH_SWITCHTO)
+        ph->param.flags|=MPLEX_ATTACH_SWITCHTO;
+    else
+        ph->param.flags&=~MPLEX_ATTACH_SWITCHTO;
+    
+    return mplex_do_attach_pholder(mplex, ph, data);
+}
+
+
+bool mplexpholder_move(WMPlexPHolder *ph, WMPlex *mplex, 
+                       WMPlexPHolder *after,
+                       WLListNode *or_after)
+
+{
+    mplexpholder_do_unlink(ph, (WMPlex*)ph->mplex_watch.obj);
+
+    watch_reset(&(ph->mplex_watch));
+    
+    if(mplex==NULL)
+        return TRUE;
+    
+    if(!watch_setup(&(ph->mplex_watch), (Obj*)mplex, mplex_watch_handler))
+        return FALSE;
+
+    mplexpholder_do_link(ph, mplex, after, or_after);
+    
+    return TRUE;
+}
+
+
+bool mplexpholder_do_goto(WMPlexPHolder *ph)
+{
+    WMPlex *mplex=(WMPlex*)ph->mplex_watch.obj;
+    
+    if(mplex!=NULL)
+        return region_goto((WRegion*)mplex);
+    
+    return FALSE;
+}
+
+
+WRegion *mplexpholder_do_target(WMPlexPHolder *ph)
+{
+    return (WRegion*)ph->mplex_watch.obj;
+}
+
+
+/*}}}*/
+
+
+/*{{{ WMPlex routines */
+
+
+void mplex_move_phs(WMPlex *mplex, WLListNode *node,
+                    WMPlexPHolder *after,
+                    WLListNode *or_after)
+{
+    WMPlexPHolder *ph;
+    
+    assert(node!=or_after);
+    
+    while(node->phs!=NULL){
+        ph=node->phs;
+        
+        mplexpholder_do_unlink(ph, mplex);
+        mplexpholder_do_link(ph, mplex, after, or_after);
+        
+        after=ph;
+    }
+}
+
+void mplex_move_phs_before(WMPlex *mplex, WLListNode *node)
+{
+    WMPlexPHolder *after=NULL;
+    WLListNode *or_after;
+    
+    or_after=LIST_PREV(mplex->mx_list, node, next, prev);
+                         
+    after=(or_after!=NULL
+           ? LIST_LAST(or_after->phs, next, prev)
+           : LIST_LAST(mplex->mx_phs, next, prev));
+        
+    mplex_move_phs(mplex, node, after, or_after);
+}
+
+
+WMPlexPHolder *mplex_managed_get_pholder(WMPlex *mplex, WRegion *mgd)
+{
+    WStacking *st=mplex_find_stacking(mplex, mgd);
+    
+    if(st==NULL)
+        return NULL;
+    else
+        return create_mplexpholder(mplex, st, NULL);
+}
+
+
+WMPlexPHolder *mplex_get_rescue_pholder_for(WMPlex *mplex, WRegion *mgd)
+{
+    WStacking *st=mplex_find_stacking(mplex, mgd);
+    
+    return create_mplexpholder(mplex, st, NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Class information */
+
+
+static DynFunTab mplexpholder_dynfuntab[]={
+    {(DynFun*)pholder_do_attach, 
+     (DynFun*)mplexpholder_do_attach},
+    
+    {(DynFun*)pholder_do_goto, 
+     (DynFun*)mplexpholder_do_goto},
+
+    {(DynFun*)pholder_do_target, 
+     (DynFun*)mplexpholder_do_target},
+
+    END_DYNFUNTAB
+};
+
+
+IMPLCLASS(WMPlexPHolder, WPHolder, mplexpholder_deinit, 
+          mplexpholder_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/ioncore/mplexpholder.h b/ioncore/mplexpholder.h
new file mode 100644 (file)
index 0000000..4dce782
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * ion/ioncore/mplexpholder.h
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_MPLEXPHOLDER_H
+#define ION_IONCORE_MPLEXPHOLDER_H
+
+#include "common.h"
+#include "pholder.h"
+#include "mplex.h"
+#include "attach.h"
+
+DECLCLASS(WMPlexPHolder){
+    WPHolder ph;
+    Watch mplex_watch;
+    WLListNode *after;
+    WMPlexPHolder *next, *prev;
+    WMPlexAttachParams param;
+};
+
+
+/* If 'after' is set, it is used, otherwise 'or_after', 
+ * and finally 'or_layer' if this is also unset
+ */
+
+extern WMPlexPHolder *create_mplexpholder(WMPlex *mplex, 
+                                          WStacking *either_st,
+                                          WMPlexAttachParams *or_param);
+extern bool mplexpholder_init(WMPlexPHolder *ph, 
+                              WMPlex *mplex, 
+                              WStacking *either_st,
+                              WMPlexAttachParams *or_param);
+extern void mplexpholder_deinit(WMPlexPHolder *ph);
+
+extern int mplexpholder_layer(WMPlexPHolder *ph);
+
+extern WRegion *mplexpholder_do_attach(WMPlexPHolder *ph, int flags,
+                                       WRegionAttachData *data);
+
+extern bool mplexpholder_do_goto(WMPlexPHolder *ph);
+
+extern WRegion *mplexpholder_do_target(WMPlexPHolder *ph);
+
+extern bool mplexpholder_move(WMPlexPHolder *ph, WMPlex *mplex, 
+                              WMPlexPHolder *after,
+                              WLListNode *or_after);
+
+extern void mplexpholder_do_unlink(WMPlexPHolder *ph, WMPlex *mplex);
+
+extern void mplex_move_phs(WMPlex *mplex, WLListNode *node,
+                           WMPlexPHolder *after,
+                           WLListNode *or_after);
+extern void mplex_move_phs_before(WMPlex *mplex, WLListNode *node);
+
+extern WMPlexPHolder *mplex_managed_get_pholder(WMPlex *mplex, 
+                                                WRegion *mgd);
+extern WMPlexPHolder *mplex_get_rescue_pholder_for(WMPlex *mplex, 
+                                                       WRegion *mgd);
+
+#endif /* ION_IONCORE_MPLEXPHOLDER_H */
diff --git a/ioncore/mwmhints.c b/ioncore/mwmhints.c
new file mode 100644 (file)
index 0000000..2ebc683
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * ion/ioncore/mwmhints.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "common.h"
+#include "property.h"
+#include "mwmhints.h"
+#include "global.h"
+
+
+WMwmHints *xwindow_get_mwmhints(Window win)
+{
+    WMwmHints *hints=NULL;
+    int n;
+    
+    n=xwindow_get_property(win, ioncore_g.atom_mwm_hints,
+                           ioncore_g.atom_mwm_hints, 
+                           MWM_N_HINTS, FALSE, (uchar**)&hints);
+    
+    if(n<MWM_N_HINTS && hints!=NULL){
+        XFree((void*)hints);
+        return NULL;
+    }
+    
+    return hints;
+}
+
+
+void xwindow_check_mwmhints_nodecor(Window win, bool *nodecor)
+{
+    WMwmHints *hints;
+    int n;
+
+    *nodecor=FALSE;
+    
+    n=xwindow_get_property(win, ioncore_g.atom_mwm_hints, 
+                           ioncore_g.atom_mwm_hints, 
+                           MWM_N_HINTS, FALSE, (uchar**)&hints);
+    
+    if(n<MWM_DECOR_NDX)
+        return;
+    
+    if(hints->flags&MWM_HINTS_DECORATIONS &&
+       (hints->decorations&MWM_DECOR_ALL)==0){
+        *nodecor=TRUE;
+        
+        if(hints->decorations&MWM_DECOR_BORDER ||
+           hints->decorations&MWM_DECOR_TITLE)
+            *nodecor=FALSE;
+    }
+    
+    XFree((void*)hints);
+}
diff --git a/ioncore/mwmhints.h b/ioncore/mwmhints.h
new file mode 100644 (file)
index 0000000..99ac8ad
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * ion/ioncore/mwmhints.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_MWMHINTS_H
+#define ION_IONCORE_MWMHINTS_H
+
+#include <X11/Xmd.h>
+
+#include "common.h"
+
+
+#define MWM_HINTS_FUNCTIONS     0x0001
+#define MWM_HINTS_DECORATIONS   0x0002
+#define MWM_HINTS_IONCORE_INPUTMODE_MODE    0x0004
+#define MWM_HINTS_IONCORE_INPUTMODE_STATUS  0x0008
+
+#define MWM_FUNC_ALL            0x0001
+#define MWM_FUNC_RESIZE         0x0002
+#define MWM_FUNC_MOVE           0x0004
+#define MWM_FUNC_ICONIFY        0x0008
+#define MWM_FUNC_MAXIMIZE       0x0010
+#define MWM_FUNC_CLOSE          0x0020
+
+#define MWM_DECOR_ALL           0x0001
+#define MWM_DECOR_BORDER        0x0002
+#define MWM_DECOR_HANDLE        0x0004
+#define MWM_DECOR_TITLE         0x0008
+#define MWM_DECOR_MENU          0x0010
+#define MWM_DECOR_ICONIFY       0x0020
+#define MWM_DECOR_MAXIMIZE      0x0040
+
+#define MWM_IONCORE_INPUTMODE_MODELESS 0
+#define MWM_IONCORE_INPUTMODE_PRIMARY_APPLICATION_MODAL 1
+#define MWM_IONCORE_INPUTMODE_SYSTEM_MODAL 2
+#define MWM_IONCORE_INPUTMODE_FULL_APPLICATION_MODAL 3
+
+INTRSTRUCT(WMwmHints);
+    
+DECLSTRUCT(WMwmHints){
+    CARD32 flags;
+    CARD32 functions;
+    CARD32 decorations;
+    INT32 inputmode;
+    CARD32 status;
+};
+
+#define MWM_DECOR_NDX 3
+#define MWM_N_HINTS 5
+
+
+extern WMwmHints *xwindow_get_mwmhints(Window win);
+extern void xwindow_check_mwmhints_nodecor(Window win, bool *nodecor);
+
+
+#endif /* ION_IONCORE_MWMHINTS_H */
diff --git a/ioncore/names.c b/ioncore/names.c
new file mode 100644 (file)
index 0000000..7085504
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * ion/ioncore/names.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <string.h>
+#include <limits.h>
+
+#include <libtu/rb.h>
+#include <libtu/minmax.h>
+#include <libtu/objp.h>
+#include <libextl/extl.h>
+
+#include "common.h"
+#include "region.h"
+#include "clientwin.h"
+#include "names.h"
+#include "strings.h"
+#include "gr.h"
+
+
+/*{{{ Implementation */
+
+
+WNamespace ioncore_internal_ns={NULL, FALSE};
+WNamespace ioncore_clientwin_ns={NULL, FALSE};
+
+
+static bool initialise_ns(WNamespace *ns)
+{
+    if(ns->initialised)
+        return TRUE;
+    
+    if(ns->rb==NULL)
+        ns->rb=make_rb();
+    
+    ns->initialised=(ns->rb!=NULL);
+    
+    return ns->initialised;
+}
+
+
+static int parseinst(const char *name, const char **startinst)
+{
+    const char *p2, *p3=NULL;
+    int inst;
+    int l=strlen(name);
+    
+    *startinst=name+l;
+
+    if(name[l-1]!='>')
+        return -1;
+    
+    p2=strrchr(name, '<');
+    if(p2==NULL)
+        return -1;
+    
+    inst=strtoul(p2+1, (char**)&p3, 10);
+    
+    if(inst<0 || p3!=name+l-1)
+        return -1;
+    
+    *startinst=p2;
+    return inst;
+}
+
+
+static int parseinst_simple(const char *inststr)
+{
+    const char *end=NULL;
+    int inst;
+    
+    if(*inststr=='\0')
+        return 0;
+    
+    if(*inststr=='<'){
+        inst=strtoul(inststr+1, (char**)&end, 10);
+    
+        if(inst>=0 && end!=NULL && *end=='>')
+            return inst;
+    }
+
+    warn(TR("Corrupt instance number %s."), inststr);
+    return -1;
+}
+
+
+#define COMPARE_FN ((Rb_compfn*)compare_nameinfos)
+
+static int compare_nameinfos(const WRegionNameInfo *ni1, 
+                             const WRegionNameInfo *ni2)
+{
+    int l1=0, l2=0;
+    int i1=0, i2=0;
+    int mc=0;
+    
+    /* Handle unnamed regions. */
+
+    if(ni1->name==NULL){
+        if(ni2->name!=NULL)
+            return 1;
+        return (ni1==ni2 ? 0 : (ni1<ni2 ? -1 : 1));
+    }else if(ni2->name==NULL){
+        return -1;
+    }
+
+    /* Special case: inst_off<0 means that -inst_off-1 is the actual
+     * instance number and the name does not contain it.
+     */
+
+    if(ni1->inst_off>=0)
+        l1=ni1->inst_off;
+    else
+        l1=strlen(ni1->name);
+
+    if(ni2->inst_off>=0)
+        l2=ni2->inst_off;
+    else
+        l2=strlen(ni2->name);
+    
+    /* Check name part first */
+    
+    mc=strncmp(ni1->name, ni2->name, minof(l1, l2));
+    
+    if(mc!=0)
+        return mc;
+    
+    if(l1!=l2)
+        return (l1<l2 ? -1 : 1);
+
+    /* Same name, different instance */
+    
+    if(ni1->inst_off>=0)
+        i1=parseinst_simple(ni1->name+ni1->inst_off);
+    else
+        i1=-ni1->inst_off-1; /*???*/
+
+    if(ni2->inst_off>=0)
+        i2=parseinst_simple(ni2->name+ni2->inst_off);
+    else
+        i2=-ni2->inst_off-1; /*???*/
+
+    if(i1!=i2)
+        return (i1<i2 ? -1 : 1);
+    
+    /* Same name and instance */
+    
+    return 0;
+}
+
+static bool insert_reg(Rb_node tree, WRegion *reg)
+{
+    assert(reg->ni.node==NULL);
+    reg->ni.node=(void*)rb_insertg(tree, &(reg->ni), reg, COMPARE_FN);
+    return (reg->ni.node!=NULL);
+}
+
+static bool separated(const WRegionNameInfo *ni1, 
+                      const WRegionNameInfo *ni2, int *i1ret)
+{
+    int l1, l2;
+    int i1, i2;
+    int mc;
+    
+    assert(ni1->name!=NULL);
+    
+    if(ni2->name==NULL){
+        /* Shouldn't happen the way this function is called below; unnamed
+         * regions are before others in the traversal order of the tree.
+         */
+        *i1ret=parseinst_simple(ni1->name+ni1->inst_off);
+        return TRUE;
+    }
+    
+    assert(ni1->inst_off>=0 && ni2->inst_off>=0);
+
+    l1=ni1->inst_off;
+    l2=ni2->inst_off;
+
+    i1=parseinst_simple(ni1->name+ni1->inst_off);
+    *i1ret=i1;
+    
+    if(l1!=l2)
+        return TRUE;
+        
+    if(memcmp(ni1->name, ni2->name, l1)!=0)
+        return TRUE;
+    
+    i2=parseinst_simple(ni2->name+ni2->inst_off);
+    
+    return (i2>(i1+1));
+}
+
+
+
+void region_unregister(WRegion *reg)
+{
+    if(reg->ni.node!=NULL){
+        rb_delete_node((Rb_node)reg->ni.node);
+        reg->ni.node=NULL;
+    }
+    
+    if(reg->ni.name!=NULL){
+        free(reg->ni.name);
+        reg->ni.name=NULL;
+        reg->ni.inst_off=0;
+    }
+}
+
+
+static bool make_full_name(WRegionNameInfo *ni, const char *name, int inst, 
+                           bool append_always)
+{
+    assert(ni->name==NULL);
+    
+    if(inst==0 && !append_always)
+        ni->name=scopy(name);
+    else
+        libtu_asprintf(&(ni->name), "%s<%d>", name, inst);
+        
+    if(ni->name==NULL)
+        return FALSE;
+    
+    ni->inst_off=strlen(name);
+    
+    return TRUE;
+}
+
+
+static bool do_use_name(WRegion *reg, WNamespace *ns, const char *name,
+                        int instrq, bool failchange)
+{
+    int parsed_inst=-1;
+    WRegionNameInfo ni={NULL, 0, NULL};
+    const char *dummy=NULL;
+    Rb_node node;
+    int inst=-1;
+    int found=0;
+
+    parsed_inst=parseinst(name, &dummy);
+    
+    if(!ns->initialised)
+        return FALSE;
+
+    /* If there's something that looks like an instance at the end of
+     * name, we will append the instance number.
+     */
+    if(parsed_inst>=0 && inst==0 && failchange)
+        return FALSE;
+    
+    if(instrq>=0){
+        WRegionNameInfo tmpni;
+        tmpni.name=(char*)name;
+        tmpni.inst_off=-instrq-1;
+        node=rb_find_gkey_n(ns->rb, &tmpni, COMPARE_FN, &found);
+        if(found){
+            if(rb_val(node)==(void*)reg){
+                /* The region already has the requested name */
+                return TRUE;
+            }
+            if(failchange)
+                return FALSE;
+        }else{
+            inst=instrq;
+        }
+    }
+    
+    if(inst<0){
+        WRegionNameInfo tmpni;
+        
+        found=0;
+        inst=0;
+
+        tmpni.name=(char*)name;
+        tmpni.inst_off=-1;
+        node=rb_find_gkey_n(ns->rb, &tmpni, COMPARE_FN, &found);
+        
+        if(found){
+            while(1){
+                Rb_node next=rb_next(node);
+                
+                if(rb_val(node)==(void*)reg){
+                    /* The region already has a name of requested form */
+                    return TRUE;
+                }
+                
+                if(next==rb_nil(ns->rb) ||
+                   separated((const WRegionNameInfo*)node->k.key, 
+                             (const WRegionNameInfo*)next->k.key, &inst)){
+                    /* 'inst' should be next free instance after increment
+                     * as separation was greater then one.
+                     */
+                    inst++;
+                    break;
+                }
+
+                /* 'inst' should be instance of next after increment 
+                 * as separation was one.
+                 */
+                inst++;
+                node=next;
+            }
+        }
+    }
+
+    if(!make_full_name(&ni, name, inst, parsed_inst>=0))
+        return FALSE;
+
+    /*
+    rb_find_gkey_n(ns->rb, &ni, COMPARE_FN, &found);
+    
+    assert(!found);
+     */
+
+    region_unregister(reg);
+    
+    reg->ni.name=ni.name;
+    reg->ni.inst_off=ni.inst_off;
+    
+    if(!insert_reg(ns->rb, reg)){
+        free(reg->ni.name);
+        reg->ni.name=NULL;
+        reg->ni.inst_off=0;
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+static bool use_name_anyinst(WRegion *reg, WNamespace *ns, const char *name)
+{
+    return do_use_name(reg, ns, name, -1, FALSE);
+}
+
+
+static bool use_name_exact(WRegion *reg, WNamespace *ns, const char *name)
+{
+    return do_use_name(reg, ns, name, 0, TRUE);
+}
+
+
+static bool use_name_parseany(WRegion *reg, WNamespace *ns, const char *name)
+{
+    int l, inst;
+    const char *startinst;
+    
+    l=strlen(name);
+    
+    inst=parseinst(name, &startinst);
+    if(inst>=0){
+        bool retval=FALSE;
+        int realnamelen=startinst-name;
+        char *realname=ALLOC_N(char, realnamelen+1);
+        if(realname!=NULL){
+            memcpy(realname, name, realnamelen);
+            realname[realnamelen]='\0';
+            retval=do_use_name(reg, ns, realname, inst, FALSE);
+            free(realname);
+        }
+        return retval;
+    }
+    
+    return do_use_name(reg, ns, name, 0, FALSE);
+}
+
+
+
+/*}}}*/
+
+
+/*{{{ Interface */
+
+
+/*EXTL_DOC
+ * Returns the name for \var{reg}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+const char *region_name(WRegion *reg)
+{
+    return reg->ni.name;
+}
+
+
+static bool do_set_name(bool (*fn)(WRegion *reg, WNamespace *ns, const char *p),
+                        WRegion *reg, WNamespace *ns, const char *p)
+{
+    bool ret=TRUE;
+    char *nm=NULL;
+
+    if(!initialise_ns(ns))
+        return FALSE;
+    
+    if(p!=NULL){
+        nm=scopy(p);
+        if(nm==NULL)
+            return FALSE;
+        str_stripws(nm);
+    }
+
+    if(nm==NULL || *nm=='\0'){
+        region_unregister(reg);
+        ret=insert_reg(ns->rb, reg);
+    }else{
+        ret=fn(reg, ns, nm);
+    }
+    
+    if(nm!=NULL)
+        free(nm);
+
+    region_notify_change(reg, "name");
+    
+    return ret;
+}
+
+
+bool region_register(WRegion *reg)
+{
+    assert(reg->ni.name==NULL);
+    
+    if(!initialise_ns(&ioncore_internal_ns))
+        return FALSE;
+    
+    return use_name_anyinst(reg, &ioncore_internal_ns, OBJ_TYPESTR(reg));
+}
+
+
+bool clientwin_register(WClientWin *cwin)
+{
+    WRegion *reg=(WRegion*)cwin;
+    
+    assert(reg->ni.name==NULL);
+    
+    if(!initialise_ns(&ioncore_clientwin_ns))
+        return FALSE;
+    
+    return insert_reg(ioncore_clientwin_ns.rb, (WRegion*)cwin);
+}
+
+
+/*EXTL_DOC
+ * Set the name of \var{reg} to \var{p}. If the name is already in use,
+ * an instance number suffix \code{<n>} will be attempted. If \var{p} has
+ * such a suffix, it will be modified, otherwise such a suffix will be
+ * added. Setting \var{p} to nil will cause current name to be removed.
+ */
+EXTL_EXPORT_MEMBER
+bool region_set_name(WRegion *reg, const char *p)
+{
+/*    return do_set_name(use_name_parseany, reg, &ioncore_internal_ns, p);*/
+    return do_set_name(use_name_parseany, reg, 
+            OBJ_IS(reg, WClientWin) ? &ioncore_clientwin_ns : &ioncore_internal_ns,
+            p);
+}
+
+
+/*EXTL_DOC
+ * Similar to \fnref{WRegion.set_name} except if the name is already in use,
+ * other instance numbers will not be attempted. The string \var{p} should
+ * not contain a \code{<n>} suffix or this function will fail.
+ */
+EXTL_EXPORT_MEMBER
+bool region_set_name_exact(WRegion *reg, const char *p)
+{
+    return do_set_name(use_name_exact, reg, &ioncore_internal_ns, p);
+}
+
+
+bool clientwin_set_name(WClientWin *cwin, const char *p)
+{
+    return do_set_name(use_name_anyinst, (WRegion*)cwin,
+                       &ioncore_clientwin_ns, p);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Lookup and list */
+
+
+static WRegion *do_lookup_region(WNamespace *ns, const char *cname,
+                                 const char *typenam)
+{
+    WRegionNameInfo ni;
+    Rb_node node;
+    int found=0;
+    const char *instptr=NULL;
+
+    if(cname==NULL || !ns->initialised)
+        return NULL;
+    
+    parseinst(cname, &instptr);
+    assert(instptr!=NULL);
+    
+    ni.name=(char*)cname;
+    ni.inst_off=instptr-cname;
+    
+    node=rb_find_gkey_n(ns->rb, &ni, COMPARE_FN, &found);
+    
+    if(!found)
+        return NULL;
+    
+    return (WRegion*)node->v.val;
+}
+
+
+/*EXTL_DOC
+ * Attempt to find a non-client window region with name \var{name} and type
+ * inheriting \var{typenam}.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+WRegion *ioncore_lookup_region(const char *name, const char *typenam)
+{
+    return do_lookup_region(&ioncore_internal_ns, name, typenam);
+}
+
+
+/*EXTL_DOC
+ * Attempt to find a client window with name \var{name}.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+WClientWin *ioncore_lookup_clientwin(const char *name)
+{
+    return (WClientWin*)do_lookup_region(&ioncore_clientwin_ns, name, 
+                                         "WClientWin");
+}
+
+
+static ExtlTab do_list(WNamespace *ns, const char *typenam)
+{
+    ExtlTab tab;
+    Rb_node node;
+    int n=0;
+    
+    if(!ns->initialised)
+        return extl_table_none();
+    
+    tab=extl_create_table();
+    
+    rb_traverse(node, ns->rb){
+        WRegion *reg=(WRegion*)node->v.val;
+        
+        assert(reg!=NULL);
+
+        if(typenam!=NULL && !obj_is_str((Obj*)reg, typenam))
+            continue;
+
+        if(extl_table_seti_o(tab, n+1, (Obj*)reg))
+            n++;
+    }
+    
+    return tab;
+}
+
+
+/*EXTL_DOC
+ * Find all non-client window regions inheriting \var{typenam}.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab ioncore_region_list(const char *typenam)
+{
+    return do_list(&ioncore_internal_ns, typenam);
+}
+
+
+/*EXTL_DOC
+ * Return a list of all client windows.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab ioncore_clientwin_list()
+{
+    return do_list(&ioncore_clientwin_ns, NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Displayname */
+
+
+const char *region_displayname(WRegion *reg)
+{
+    const char *ret=NULL;
+    CALL_DYN_RET(ret, const char *, region_displayname, reg, (reg));
+    return ret;
+}
+
+
+char *region_make_label(WRegion *reg, int maxw, GrBrush *brush)
+{
+    const char *name=region_displayname(reg);
+
+    if(name==NULL)
+        return NULL;
+    
+    return grbrush_make_label(brush, name, maxw);
+}
+
+
+/*}}}*/
diff --git a/ioncore/names.h b/ioncore/names.h
new file mode 100644 (file)
index 0000000..5a2cc36
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * ion/ioncore/names.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_NAMES_H
+#define ION_IONCORE_NAMES_H
+
+#include "region.h"
+#include "clientwin.h"
+#include "gr.h"
+#include <libextl/extl.h>
+
+
+typedef struct{
+    struct rb_node *rb;
+    bool initialised;
+} WNamespace;
+
+
+extern WNamespace ioncore_internal_ns;
+extern WNamespace ioncore_clientwin_ns;
+
+
+extern bool region_register(WRegion *reg);
+extern bool region_set_name(WRegion *reg, const char *name);
+extern bool region_set_name_exact(WRegion *reg, const char *name);
+
+extern bool clientwin_register(WClientWin *cwin);
+extern bool clientwin_set_name(WClientWin *cwin, const char *name);
+
+extern void region_unregister(WRegion *reg);
+extern void region_do_unuse_name(WRegion *reg, bool insert_unnamed);
+
+extern const char *region_name(WRegion *reg);
+DYNFUN const char *region_displayname(WRegion *reg);
+
+extern char *region_make_label(WRegion *reg, int maxw, GrBrush *brush);
+
+extern ExtlTab ioncore_region_list(const char *typenam);
+extern ExtlTab ioncore_clientwin_list();
+extern WRegion *ioncore_lookup_region(const char *cname, const char *typenam);
+extern WClientWin *ioncore_lookup_clientwin(const char *cname);
+
+#endif /* ION_IONCORE_NAMES_H */
diff --git a/ioncore/navi.c b/ioncore/navi.c
new file mode 100644 (file)
index 0000000..f52a44b
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * ion/ioncore/navi.c
+ *
+ * Copyright (c) Tuomo Valkonen 2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+
+#include "common.h"
+#include "extlconv.h"
+#include "region.h"
+#include "navi.h"
+
+
+WRegion *region_navi_first(WRegion *reg, WRegionNavi nh,
+                           WRegionNaviData *data)
+{
+    WRegion *ret=NULL;
+    CALL_DYN_RET(ret, WRegion*, region_navi_first, reg, 
+                 (reg, nh, data));
+    return ret;
+}
+
+
+WRegion *region_navi_next(WRegion *reg, WRegion *mgd, WRegionNavi nh,
+                          WRegionNaviData *data)
+{
+    WRegion *ret=NULL;
+    CALL_DYN_RET(ret, WRegion*, region_navi_next, reg, 
+                 (reg, mgd, nh, data));
+    return ret;
+}
+
+
+bool ioncore_string_to_navi(const char *str, WRegionNavi *nh)
+{
+    if(str==NULL){
+        warn(TR("Invalid parameter."));
+        return FALSE;
+    }
+    
+    if(!strcmp(str, "any")){
+        *nh=REGION_NAVI_ANY;
+    }else if (!strcmp(str, "end") || 
+              !strcmp(str, "last") || 
+              !strcmp(str, "next")){
+        *nh=REGION_NAVI_END;
+    }else if (!strcmp(str, "beg") || 
+              !strcmp(str, "first") ||
+              !strcmp(str, "prev")){
+        *nh=REGION_NAVI_BEG;
+    }else if(!strcmp(str, "left")){
+        *nh=REGION_NAVI_LEFT;
+    }else if(!strcmp(str, "right")){
+        *nh=REGION_NAVI_RIGHT;
+    }else if(!strcmp(str, "top") || 
+             !strcmp(str, "above") || 
+             !strcmp(str, "up")){
+        *nh=REGION_NAVI_TOP;
+    }else if(!strcmp(str, "bottom") || 
+             !strcmp(str, "below") ||
+             !strcmp(str, "down")){
+        *nh=REGION_NAVI_BOTTOM;
+    }else{
+        warn(TR("Invalid direction parameter."));
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+WRegionNavi ioncore_navi_reverse(WRegionNavi nh)
+{
+    if(nh==REGION_NAVI_BEG)
+        return REGION_NAVI_END;
+    else if(nh==REGION_NAVI_END)
+        return REGION_NAVI_BEG;
+    else if(nh==REGION_NAVI_LEFT)
+        return REGION_NAVI_RIGHT;
+    else if(nh==REGION_NAVI_RIGHT)
+        return REGION_NAVI_LEFT;
+    else if(nh==REGION_NAVI_TOP)
+        return REGION_NAVI_BOTTOM;
+    else if(nh==REGION_NAVI_BOTTOM)
+        return REGION_NAVI_TOP;
+    else
+        return REGION_NAVI_ANY;
+}
+
+
+DECLSTRUCT(WRegionNaviData){
+    WRegionNavi nh;
+    bool descend;
+    ExtlFn ascend_filter;
+    ExtlFn descend_filter;
+    WRegion *startpoint;
+    bool nowrap;
+    Obj *no_ascend;
+    Obj *no_descend;
+    bool nofront;
+};
+
+
+static bool may_ascend(WRegion *to, WRegion *from, WRegionNaviData *data)
+{
+    if(data->ascend_filter!=extl_fn_none()){
+        bool r, v;
+        extl_protect(NULL);
+        r=extl_call(data->ascend_filter, "oo", "b", to, from, &v);
+        extl_unprotect(NULL);
+        return (r && v);
+    }else if(data->no_ascend!=NULL){
+        return (data->no_ascend!=(Obj*)from);
+    }else{
+        return !OBJ_IS(from, WMPlex);
+    }
+}
+
+
+static bool may_descend(WRegion *to, WRegion *from, WRegionNaviData *data)
+{
+    if(data->descend_filter!=extl_fn_none()){
+        bool r, v;
+        extl_protect(NULL);
+        r=extl_call(data->descend_filter, "oo", "b", to, from, &v);
+        extl_unprotect(NULL);
+        return (r && v);
+    }else if(data->no_descend!=NULL){
+        return (data->no_descend!=(Obj*)from);
+    }else{
+        return !OBJ_IS(to, WMPlex);
+    }
+}
+
+
+static WRegion *region_navi_descend(WRegion *reg, WRegionNaviData *data)
+{
+    if(data->descend){
+        return region_navi_first(reg, data->nh, data);
+    }else{
+        WRegion *nxt;
+        
+        data->descend=TRUE;
+        data->nh=ioncore_navi_reverse(data->nh);
+
+        nxt=region_navi_first(reg, data->nh, data);
+        
+        data->descend=FALSE;
+        data->nh=ioncore_navi_reverse(data->nh);
+        
+        return nxt;
+    }
+}
+    
+
+WRegion *region_navi_cont(WRegion *reg, WRegion *res, WRegionNaviData *data)
+{
+    if(res==NULL){
+        if(data->descend){
+            return (reg==data->startpoint ? NULL : reg);
+        }else{
+            WRegion *mgr=REGION_MANAGER(reg);
+            WRegion *nxt=NULL;
+
+            if(mgr!=NULL && may_ascend(mgr, reg, data)){
+                if(data->nowrap){
+                    /* tail-recursive case */
+                    return region_navi_next(mgr, reg, data->nh, data);
+                }else{
+                    nxt=region_navi_next(mgr, reg, data->nh, data);
+                }
+            }
+            
+            if(nxt==NULL && !data->nowrap){
+                /* wrap */
+                nxt=region_navi_descend(reg, data);
+            }
+            
+            return nxt;
+        }
+    }else{
+        if(may_descend(res, reg, data)){
+            return region_navi_descend(res, data);
+        }else{
+            return res;
+        }
+    }
+}
+
+
+static bool get_param(WRegionNaviData *data, const char *dirstr, ExtlTab param)
+{
+    if(!ioncore_string_to_navi(dirstr, &data->nh))
+        return FALSE;
+    
+    data->ascend_filter=extl_fn_none();
+    data->descend_filter=extl_fn_none();
+    data->no_ascend=NULL;
+    data->no_descend=NULL;
+    
+    extl_table_gets_o(param, "no_ascend", &data->no_ascend);
+    extl_table_gets_o(param, "no_descend", &data->no_descend);
+    extl_table_gets_f(param, "ascend_filter", &data->ascend_filter);
+    extl_table_gets_f(param, "descend_filter", &data->descend_filter);
+    data->nowrap=extl_table_is_bool_set(param, "nowrap");
+    data->nofront=extl_table_is_bool_set(param, "nofront");
+    
+    return TRUE;
+}
+
+
+static WRegion *release(WRegionNaviData *data, WRegion *res)
+{
+    extl_unref_fn(data->ascend_filter);
+    extl_unref_fn(data->descend_filter);
+    
+    return res;
+}
+
+
+/*EXTL_DOC
+ * Find region next from \var{reg} in direction \var{dirstr}
+ * (up/down/left/right/next/prev/any). The table param may
+ * contain the boolean field \var{nowrap}, instructing not to wrap 
+ * around, and the \type{WRegion}s \var{no_ascend} and \var{no_descend},
+ * and functions \var{ascend_filter} and \var{descend_filter} from
+ * \var{WRegion}s (\var{to}, \var{from}), used to decide when to descend
+ * or ascend into another region. (TODO: more detailed explanation.)
+ */
+EXTL_EXPORT
+WRegion *ioncore_navi_next(WRegion *reg, const char *dirstr, ExtlTab param)
+{
+    WRegionNaviData data;
+    WRegion *mgr;
+    
+    if(reg==NULL){
+        /* ??? */
+        return NULL;
+    }
+    
+    if(!get_param(&data, dirstr, param))
+        return NULL;
+    
+    mgr=REGION_MANAGER(reg);
+    
+    if(mgr==NULL)
+        return FALSE;
+    
+    data.startpoint=reg;
+    data.descend=FALSE;
+    
+    return release(&data, region_navi_next(mgr, reg, data.nh, &data));
+}
+
+
+/*EXTL_DOC
+ * Find first region within \var{reg} in direction \var{dirstr}
+ * (up/down/left/right/beg/end/any). For information on \var{param},
+ * see \fnref{ioncore.navi_next}.
+ */
+EXTL_EXPORT
+WRegion *ioncore_navi_first(WRegion *reg, const char *dirstr, ExtlTab param)
+{
+    WRegionNaviData data;
+    
+    if(reg==NULL){
+        /* ??? */
+        return NULL;
+    }
+    
+    if(!get_param(&data, dirstr, param))
+        return NULL;
+    
+    data.startpoint=reg;
+    data.descend=TRUE;
+    
+    return release(&data, region_navi_first(reg, data.nh, &data));
+}
+
+
+static WRegion *do_goto(WRegion *res)
+{
+    if(res!=NULL){
+        /* TODO: deep rqorder? */
+        region_rqorder(res, REGION_ORDER_FRONT);
+        region_goto(res);
+    }
+    return res;
+}
+
+
+/*EXTL_DOC
+ * Go to region next from \var{reg} in direction \var{dirstr}
+ * (up/down/left/right/next/prev/any). For information on \var{param},
+ * see \fnref{ioncore.navi_next}. Additionally this function supports
+ * the boolean \var{nofront} field, for not bringing the object to
+ * front.
+ */
+EXTL_EXPORT
+WRegion *ioncore_goto_next(WRegion *reg, const char *dirstr, ExtlTab param)
+{
+    return do_goto(ioncore_navi_next(reg, dirstr, param));
+}
+
+
+/*EXTL_DOC
+ * Go to first region within \var{reg} in direction \var{dirstr}
+ * (up/down/left/right/beg/end/any). For information on \var{param},
+ * see \fnref{ioncore.navi_next}. Additionally this function supports
+ * the boolean \var{nofront} field, for not bringing the object to
+ * front.
+ */
+EXTL_EXPORT
+WRegion *ioncore_goto_first(WRegion *reg, const char *dirstr, ExtlTab param)
+{
+    return do_goto(ioncore_navi_first(reg, dirstr, param));
+}
+
+
diff --git a/ioncore/navi.h b/ioncore/navi.h
new file mode 100644 (file)
index 0000000..3c38973
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * ion/ioncore/navi.h
+ *
+ * Copyright (c) Tuomo Valkonen 2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_NAVI_H
+#define ION_IONCORE_NAVI_H
+
+#include "common.h"
+#include "region.h"
+
+
+typedef enum{
+    REGION_NAVI_ANY,
+    REGION_NAVI_BEG, /* FIRST, PREV */
+    REGION_NAVI_END, /* LAST, NEXT */
+    REGION_NAVI_LEFT,
+    REGION_NAVI_RIGHT, 
+    REGION_NAVI_TOP,
+    REGION_NAVI_BOTTOM
+} WRegionNavi;
+
+INTRSTRUCT(WRegionNaviData);
+
+DYNFUN WRegion *region_navi_next(WRegion *reg, WRegion *rel, WRegionNavi nh,
+                                 WRegionNaviData *data);
+DYNFUN WRegion *region_navi_first(WRegion *reg, WRegionNavi nh,
+                                  WRegionNaviData *data);
+
+extern WRegion *region_navi_cont(WRegion *reg, WRegion *res, 
+                                 WRegionNaviData *data);
+
+extern bool ioncore_string_to_navi(const char *str, WRegionNavi *nv);
+
+extern WRegionNavi ioncore_navi_reverse(WRegionNavi nh);
+
+extern WRegion *ioncore_goto_next(WRegion *reg, const char *dirstr, 
+                                  ExtlTab param);
+extern WRegion *ioncore_goto_first(WRegion *reg, const char *dirstr, 
+                                   ExtlTab param);
+extern WRegion *ioncore_navi_next(WRegion *reg, const char *dirstr, 
+                                  ExtlTab param);
+extern WRegion *ioncore_navi_first(WRegion *reg, const char *dirstr, 
+                                   ExtlTab param);
+
+#endif /* ION_IONCORE_NAVI_H */
diff --git a/ioncore/netwm.c b/ioncore/netwm.c
new file mode 100644 (file)
index 0000000..cd0cb85
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * ion/ioncore/netwm.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <X11/Xatom.h>
+#include <X11/Xmd.h>
+
+#include <libtu/util.h>
+#include "common.h"
+#include "global.h"
+#include "fullscreen.h"
+#include "clientwin.h"
+#include "netwm.h"
+#include "property.h"
+#include "focus.h"
+#include "xwindow.h"
+#include "extlconv.h"
+
+
+/*{{{ Atoms */
+
+static Atom atom_net_wm_name=0;
+static Atom atom_net_wm_state=0;
+static Atom atom_net_wm_state_fullscreen=0;
+static Atom atom_net_supporting_wm_check=0;
+static Atom atom_net_virtual_roots=0;
+static Atom atom_net_active_window=0;
+
+#define N_NETWM 6
+
+static Atom atom_net_supported=0;
+
+/*}}}*/
+
+
+/*{{{ Initialisation */
+
+
+void netwm_init()
+{
+    atom_net_wm_name=XInternAtom(ioncore_g.dpy, "_NET_WM_NAME", False);
+    atom_net_wm_state=XInternAtom(ioncore_g.dpy, "_NET_WM_STATE", False);
+    atom_net_wm_state_fullscreen=XInternAtom(ioncore_g.dpy, "_NET_WM_STATE_FULLSCREEN", False);
+    atom_net_supported=XInternAtom(ioncore_g.dpy, "_NET_SUPPORTED", False);
+    atom_net_supporting_wm_check=XInternAtom(ioncore_g.dpy, "_NET_SUPPORTING_WM_CHECK", False);
+    atom_net_virtual_roots=XInternAtom(ioncore_g.dpy, "_NET_VIRTUAL_ROOTS", False);
+    atom_net_active_window=XInternAtom(ioncore_g.dpy, "_NET_ACTIVE_WINDOW", False);
+}
+
+
+void netwm_init_rootwin(WRootWin *rw)
+{
+    Atom atoms[N_NETWM];
+        const char *p[1];
+
+    atoms[0]=atom_net_wm_name;
+    atoms[1]=atom_net_wm_state;
+    atoms[2]=atom_net_wm_state_fullscreen;
+    atoms[3]=atom_net_supporting_wm_check;
+    atoms[4]=atom_net_virtual_roots;
+    atoms[5]=atom_net_active_window;
+    
+    XChangeProperty(ioncore_g.dpy, WROOTWIN_ROOT(rw),
+                    atom_net_supporting_wm_check, XA_WINDOW,
+                    32, PropModeReplace, (uchar*)&(rw->dummy_win), 1);
+    XChangeProperty(ioncore_g.dpy, WROOTWIN_ROOT(rw),
+                    atom_net_supported, XA_ATOM,
+                    32, PropModeReplace, (uchar*)atoms, N_NETWM);
+    /* Something else should probably be used as WM name here. */
+    p[0]=prog_execname();
+    xwindow_set_text_property(rw->dummy_win, atom_net_wm_name, p, 1);
+}
+
+
+/*}}}*/
+
+
+/*{{{ _NET_WM_STATE */
+
+
+int netwm_check_initial_fullscreen(WClientWin *cwin, bool sw)
+{
+
+    int i, n;
+    int ret=0;
+    long *data;
+    
+    n=xwindow_get_property(cwin->win, atom_net_wm_state, XA_ATOM,
+                   1, TRUE, (uchar**)&data);
+    
+    if(n<0)
+        return -1;
+    
+    for(i=0; i<n; i++){
+        if(data[i]==(long)atom_net_wm_state_fullscreen){
+            ret=clientwin_enter_fullscreen(cwin, sw);
+            break;
+        }
+    }
+    
+    XFree((void*)data);
+
+    return ret;
+}
+
+
+void netwm_update_state(WClientWin *cwin)
+{
+    CARD32 data[1];
+    int n=0;
+    
+    if(REGION_IS_FULLSCREEN(cwin))
+        data[n++]=atom_net_wm_state_fullscreen;
+
+    XChangeProperty(ioncore_g.dpy, cwin->win, atom_net_wm_state, 
+                    XA_ATOM, 32, PropModeReplace, (uchar*)data, n);
+}
+
+
+void netwm_delete_state(WClientWin *cwin)
+{
+    XDeleteProperty(ioncore_g.dpy, cwin->win, atom_net_wm_state);
+}
+
+
+
+static void netwm_state_change_rq(WClientWin *cwin, 
+                                  const XClientMessageEvent *ev)
+{
+    if((ev->data.l[1]==0 ||
+        ev->data.l[1]!=(long)atom_net_wm_state_fullscreen) &&
+       (ev->data.l[2]==0 ||
+        ev->data.l[2]!=(long)atom_net_wm_state_fullscreen)){
+        return;
+    }
+    
+    /* Ok, full screen add/remove/toggle */
+    if(!REGION_IS_FULLSCREEN(cwin)){
+        if(ev->data.l[0]==_NET_WM_STATE_ADD || 
+           ev->data.l[0]==_NET_WM_STATE_TOGGLE){
+            bool sw=clientwin_fullscreen_may_switchto(cwin);
+            cwin->flags|=CLIENTWIN_FS_RQ;
+            if(!clientwin_enter_fullscreen(cwin, sw))
+                cwin->flags&=~CLIENTWIN_FS_RQ;
+        }else{
+            /* Should not be set.. */
+            cwin->flags&=~CLIENTWIN_FS_RQ;
+        }
+    }else{
+        if(ev->data.l[0]==_NET_WM_STATE_REMOVE || 
+           ev->data.l[0]==_NET_WM_STATE_TOGGLE){
+            bool sw=clientwin_fullscreen_may_switchto(cwin);
+            cwin->flags&=~CLIENTWIN_FS_RQ;
+            clientwin_leave_fullscreen(cwin, sw);
+        }else{
+            /* Set the flag */
+            cwin->flags|=CLIENTWIN_FS_RQ;
+        }
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ _NET_ACTIVE_WINDOW */
+
+
+void netwm_set_active(WRegion *reg)
+{
+    CARD32 data[1]={None};
+    
+    if(OBJ_IS(reg, WClientWin))
+        data[0]=region_xwindow(reg);
+
+    /* The spec doesn't say how multihead should be handled, so
+     * we just update the root window the window is on.
+     */
+    XChangeProperty(ioncore_g.dpy, region_root_of(reg), 
+                    atom_net_active_window, XA_WINDOW, 
+                    32, PropModeReplace, (uchar*)data, 1);
+}
+
+
+static void netwm_active_window_rq(WClientWin *cwin, 
+                                   const XClientMessageEvent *ev)
+{
+    bool ignore=TRUE;
+    
+    extl_table_gets_b(cwin->proptab, "ignore_net_active_window", &ignore);
+    
+    if(!ignore)
+        region_goto((WRegion*)cwin);
+}
+
+
+/*}}}*/
+
+
+/*{{{ _NET_WM_NAME */
+
+
+char **netwm_get_name(WClientWin *cwin)
+{
+    return xwindow_get_text_property(cwin->win, atom_net_wm_name, NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ netwm_handle_client_message */
+
+
+void netwm_handle_client_message(const XClientMessageEvent *ev)
+{
+    /* Check _NET_WM_STATE fullscreen request */
+    if(ev->message_type==atom_net_wm_state && ev->format==32){
+        WClientWin *cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
+        if(cwin!=NULL)
+            netwm_state_change_rq(cwin, ev);
+    }
+    /* Check _NET_ACTIVE_WINDOW request */
+    else if(ev->message_type==atom_net_active_window && ev->format==32){
+        WClientWin *cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
+        if(cwin!=NULL)
+            netwm_active_window_rq(cwin, ev);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ netwm_handle_property */
+
+
+bool netwm_handle_property(WClientWin *cwin, const XPropertyEvent *ev)
+{
+    if(ev->atom!=atom_net_wm_name)
+        return FALSE;
+    
+    clientwin_get_set_name(cwin);
+    return TRUE;
+}
+
+
+/*}}}*/
diff --git a/ioncore/netwm.h b/ioncore/netwm.h
new file mode 100644 (file)
index 0000000..bbfa9b9
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * ion/ioncore/netwm.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_NETWM_H
+#define ION_IONCORE_NETWM_H
+
+#include "common.h"
+#include "rootwin.h"
+
+#define _NET_WM_STATE_REMOVE        0    /* remove/unset property */
+#define _NET_WM_STATE_ADD           1    /* add/set property */
+#define _NET_WM_STATE_TOGGLE        2    /* toggle property  */
+
+extern void netwm_init();
+extern void netwm_init_rootwin(WRootWin *rw);
+
+extern int netwm_check_initial_fullscreen(WClientWin *cwin, bool switchto);
+extern void netwm_update_state(WClientWin *cwin);
+extern void netwm_delete_state(WClientWin *cwin);
+extern void netwm_set_active(WRegion *reg);
+extern char **netwm_get_name(WClientWin *cwin);
+
+extern void netwm_handle_client_message(const XClientMessageEvent *ev);
+extern bool netwm_handle_property(WClientWin *cwin, const XPropertyEvent *ev);
+
+#endif /* ION_IONCORE_NETWM_H */
diff --git a/ioncore/pholder.c b/ioncore/pholder.c
new file mode 100644 (file)
index 0000000..4aed2cc
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * ion/ioncore/pholder.c
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/objp.h>
+#include "common.h"
+#include "attach.h"
+#include "pholder.h"
+
+
+bool pholder_init(WPHolder *ph)
+{
+    ph->redirect=NULL;
+    return TRUE;
+}
+
+
+void pholder_deinit(WPHolder *ph)
+{
+    if(ph->redirect!=NULL)
+        destroy_obj((Obj*)ph->redirect);
+}
+
+
+WRegion *pholder_do_attach(WPHolder *ph, int flags,
+                           WRegionAttachData *data)
+
+{
+    WRegion *ret=NULL;
+    CALL_DYN_RET(ret, WRegion*, pholder_do_attach, ph, (ph, flags, data));
+    return ret;
+}
+
+
+static WRegion *add_fn_reparent(WWindow *par, const WFitParams *fp, 
+                                WRegion *reg)
+{
+    if(!region_fitrep(reg, par, fp)){
+        warn(TR("Unable to reparent."));
+        return NULL;
+    }
+    region_detach_manager(reg);
+    return reg;
+}
+
+
+WRegion *pholder_attach_(WPHolder *ph, int flags, WRegionAttachData *data)
+{
+    if(ph->redirect!=NULL)
+        return pholder_attach_(ph->redirect, flags, data);
+    else
+        return pholder_do_attach(ph, flags, data);
+}
+
+
+bool pholder_attach(WPHolder *ph, int flags, WRegion *reg)
+{
+    WRegionAttachData data;
+        
+    data.type=REGION_ATTACH_REPARENT;
+    data.u.reg=reg;
+    
+    return (pholder_attach_(ph, flags, &data)!=NULL);
+}
+
+
+WRegion *pholder_do_target(WPHolder *ph)
+{
+    WRegion *ret=NULL;
+    CALL_DYN_RET(ret, WRegion*, pholder_do_target, ph, (ph));
+    return ret;
+}
+
+
+WRegion *pholder_target(WPHolder *ph)
+{
+    if(ph->redirect!=NULL)
+        return pholder_target(ph->redirect);
+    else
+        return pholder_do_target(ph);
+}
+
+
+static bool pholder_do_check_reparent_default(WPHolder *ph, WRegion *reg)
+{
+    WRegion *target=pholder_do_target(ph);
+    
+    if(target==NULL)
+        return FALSE;
+    else
+        return region_attach_reparent_check(target, reg);
+}
+
+
+DYNFUN bool pholder_do_check_reparent(WPHolder *ph, WRegion *reg)
+{
+    bool ret=FALSE;
+    CALL_DYN_RET(ret, bool, pholder_do_check_reparent, ph, (ph, reg));
+    return ret;
+}
+
+
+bool pholder_check_reparent(WPHolder *ph, WRegion *reg)
+{
+    if(ph->redirect!=NULL)
+        return pholder_check_reparent(ph->redirect, reg);
+    else
+        return pholder_do_check_reparent(ph, reg);
+}
+    
+
+bool pholder_do_goto(WPHolder *ph)
+{
+    bool ret=FALSE;
+    CALL_DYN_RET(ret, bool, pholder_do_goto, ph, (ph));
+    return ret;
+}
+
+
+bool pholder_goto(WPHolder *ph)
+{
+    if(ph->redirect!=NULL)
+        return pholder_goto(ph->redirect);
+    else
+        return pholder_do_goto(ph);
+}
+
+
+
+bool pholder_redirect(WPHolder *ph, WRegion *old_target)
+{
+    WPHolder *ph2=region_get_rescue_pholder(old_target);
+    
+    if(ph2==NULL)
+        return FALSE;
+    
+    if(ph->redirect!=NULL)
+        destroy_obj((Obj*)ph->redirect);
+
+    ph->redirect=ph2;
+    
+    return TRUE;
+}
+
+
+WPHolder *region_managed_get_pholder(WRegion *reg, WRegion *mgd)
+{
+    WPHolder *ret=NULL;
+    CALL_DYN_RET(ret, WPHolder*, region_managed_get_pholder, 
+                 reg, (reg, mgd));
+    return ret;
+}
+
+
+WPHolder *region_get_rescue_pholder_for(WRegion *reg, WRegion *mgd)
+{
+    if(OBJ_IS_BEING_DESTROYED(reg) || reg->flags&REGION_CWINS_BEING_RESCUED){
+        return FALSE;
+    }else{
+        WPHolder *ret=NULL;
+    
+        CALL_DYN_RET(ret, WPHolder*, region_get_rescue_pholder_for,
+                     reg, (reg, mgd));
+        return ret;
+    }
+}
+
+
+WPHolder *region_get_rescue_pholder(WRegion *reg)
+{
+    WRegion *mgr;
+    WPHolder *ph=NULL;
+
+    while(1){
+        mgr=region_manager_or_parent(reg);
+        if(mgr==NULL)
+            break;
+        ph=region_get_rescue_pholder_for(mgr, reg);
+        if(ph!=NULL)
+            break;
+        reg=mgr;
+    }
+    
+    return ph;
+}
+
+
+WPHolder *pholder_either(WPHolder *a, WPHolder *b)
+{
+    return (a!=NULL ? a : b);
+}
+
+
+static DynFunTab pholder_dynfuntab[]={
+    {(DynFun*)pholder_do_check_reparent, 
+     (DynFun*)pholder_do_check_reparent_default},
+
+    END_DYNFUNTAB
+};
+
+
+IMPLCLASS(WPHolder, Obj, pholder_deinit, pholder_dynfuntab);
+
diff --git a/ioncore/pholder.h b/ioncore/pholder.h
new file mode 100644 (file)
index 0000000..7fe2710
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * ion/ioncore/pholder.h
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_PHOLDER_H
+#define ION_IONCORE_PHOLDER_H
+
+#include "common.h"
+#include "attach.h"
+#include "extlconv.h"
+
+
+#define PHOLDER_ATTACH_SWITCHTO 0x0001
+
+
+/* Note: PHolders should be destroyed by their acquirer. */
+
+DECLCLASS(WPHolder){
+    Obj obj;
+    WPHolder *redirect;
+};
+
+
+extern bool pholder_init(WPHolder *ph);
+extern void pholder_deinit(WPHolder *ph);
+
+DYNFUN WRegion *pholder_do_attach(WPHolder *ph, int flags,
+                                  WRegionAttachData *data);
+
+extern WRegion *pholder_attach_(WPHolder *ph, int flags, 
+                                WRegionAttachData *data);
+
+extern bool pholder_attach(WPHolder *ph, int flags, WRegion *reg);
+
+DYNFUN WRegion *pholder_do_target(WPHolder *ph);
+
+extern WRegion *pholder_target(WPHolder *ph);
+
+DYNFUN bool pholder_do_check_reparent(WPHolder *ph, WRegion *reg);
+
+extern bool pholder_check_reparent(WPHolder *ph, WRegion *reg);
+
+DYNFUN bool pholder_do_goto(WPHolder *ph);
+
+extern bool pholder_goto(WPHolder *ph);
+
+extern bool pholder_redirect(WPHolder *ph, WRegion *old_target);
+
+extern WPHolder *pholder_either(WPHolder *a, WPHolder *b);
+
+DYNFUN WPHolder *region_managed_get_pholder(WRegion *reg, WRegion *mgd);
+DYNFUN WPHolder *region_get_rescue_pholder_for(WRegion *reg, WRegion *mgd);
+
+extern WPHolder *region_get_rescue_pholder(WRegion *reg);
+
+#endif /* ION_IONCORE_PHOLDER_H */
diff --git a/ioncore/pointer.c b/ioncore/pointer.c
new file mode 100644 (file)
index 0000000..977940d
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * ion/ioncore/pointer.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "common.h"
+#include "pointer.h"
+#include "cursor.h"
+#include "event.h"
+#include "global.h"
+#include "focus.h"
+#include "regbind.h"
+#include "grab.h"
+#include "xwindow.h"
+
+
+/*{{{ Variables */
+
+
+static uint p_button=0, p_state=0;
+static int p_x=-1, p_y=-1;
+static int p_orig_x=-1, p_orig_y=-1;
+static bool p_motion=FALSE;
+static int p_clickcnt=0;
+static Time p_time=0;
+static int p_area=0;
+static enum{ST_NO, ST_INIT, ST_HELD} p_grabstate=ST_NO;
+
+static WButtonHandler *p_motion_end_handler=NULL;
+static WMotionHandler *p_motion_handler=NULL;
+static WMotionHandler *p_motion_begin_handler=NULL;
+static GrabHandler *p_key_handler=NULL;
+static GrabKilledHandler *p_killed_handler=NULL;
+
+static Watch p_regwatch=WATCH_INIT, p_subregwatch=WATCH_INIT;
+
+#define p_reg ((WRegion*)p_regwatch.obj)
+#define p_subreg ((WRegion*)p_subregwatch.obj)
+
+
+/*}}}*/
+
+
+/*{{{ Handler setup */
+
+
+bool ioncore_set_drag_handlers(WRegion *reg,
+                         WMotionHandler *begin,
+                         WMotionHandler *motion,
+                         WButtonHandler *end,
+                         GrabHandler *keypress,
+                         GrabKilledHandler *killed)
+{
+    if(ioncore_pointer_grab_region()==NULL || p_motion)
+        return FALSE;
+    
+    /* A motion handler set at this point may not set a begin handler */
+    if(p_grabstate!=ST_HELD && begin!=NULL)
+        return FALSE;
+    
+    if(p_reg!=reg){
+        watch_setup(&p_regwatch, (Obj*)reg, NULL);
+        watch_reset(&p_subregwatch);
+    }
+    
+    p_motion_begin_handler=begin;
+    p_motion_handler=motion;
+    p_motion_end_handler=end;
+    p_key_handler=keypress;
+    p_killed_handler=killed;
+    p_motion=TRUE;
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+static bool time_in_threshold(Time time)
+{
+    Time t;
+    
+    if(time<p_time)
+        t=p_time-time;
+    else
+        t=time-p_time;
+    
+    return t<ioncore_g.dblclick_delay;
+}
+
+
+static bool motion_in_threshold(int x, int y)
+{
+    return (x>p_x-CF_DRAG_TRESHOLD && x<p_x+CF_DRAG_TRESHOLD &&
+            y>p_y-CF_DRAG_TRESHOLD && y<p_y+CF_DRAG_TRESHOLD);
+}
+
+
+WRegion *ioncore_pointer_grab_region()
+{
+    if(p_grabstate==ST_NO)
+        return NULL;
+    return p_reg;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Call handlers */
+
+
+static XEvent *p_curr_event=NULL;
+
+
+XEvent *ioncore_current_pointer_event()
+{
+    return p_curr_event;
+}
+
+
+static void call_button(WBinding *binding, XButtonEvent *ev)
+{
+    WButtonHandler *fn;
+
+    if(binding==NULL)
+        return;
+
+    p_curr_event=(XEvent*)ev;
+    extl_call(binding->func, "oo", NULL, p_reg, p_subreg);
+    p_curr_event=NULL;
+}
+
+
+static void call_motion(XMotionEvent *ev, int dx, int dy)
+{
+    if(p_motion_handler!=NULL && p_reg!=NULL){
+        p_curr_event=(XEvent*)ev;
+        p_motion_handler(p_reg, ev, dx, dy);
+        p_curr_event=NULL;
+    }
+}
+
+
+static void call_motion_end(XButtonEvent *ev)
+{
+    if(p_motion_end_handler!=NULL && p_reg!=NULL){
+        p_curr_event=(XEvent*)ev;
+        p_motion_end_handler(p_reg, ev);
+        p_curr_event=NULL;
+    }
+}
+
+
+static void call_motion_begin(WBinding *binding, XMotionEvent *ev,
+                              int dx, int dy)
+{
+    WMotionHandler *fn;
+    
+    if(binding==NULL)
+        return;
+
+    p_curr_event=(XEvent*)ev;
+    
+    extl_call(binding->func, "oo", NULL, p_reg, p_subreg);
+    
+    if(p_motion_begin_handler!=NULL && p_reg!=NULL)
+        p_motion_begin_handler(p_reg, ev, dx, dy);
+    
+    p_motion_begin_handler=NULL;
+    
+    p_curr_event=NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ ioncore_handle_button_press/release/motion */
+
+
+static void finish_pointer()
+{
+    if(p_reg!=NULL)
+        window_release((WWindow*)p_reg);
+    p_grabstate=ST_NO;
+    watch_reset(&p_subregwatch);
+}
+
+
+static bool handle_key(WRegion *reg, XEvent *ev)
+{
+    if(p_key_handler!=NULL){
+        if(p_key_handler(reg, ev)){
+            finish_pointer();
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+
+static void pointer_grab_killed(WRegion *unused)
+{
+    if(p_reg!=NULL && p_killed_handler!=NULL)
+        p_killed_handler(p_reg);
+    watch_reset(&p_regwatch);
+    finish_pointer();
+}
+
+
+bool ioncore_do_handle_buttonpress(XButtonEvent *ev)
+{
+    WBinding *pressbind=NULL;
+    WRegion *reg=NULL;
+    WRegion *subreg=NULL;
+    uint button, state;
+    bool dblclick;
+    
+    state=ev->state;
+    button=ev->button;
+    
+    reg=(WRegion*)XWINDOW_REGION_OF_T(ev->window, WWindow);
+    
+    if(reg==NULL)
+        return FALSE;
+
+    if(ev->subwindow!=None){
+        XButtonEvent ev2=*ev;
+        ev2.window=ev->subwindow;
+        if(XTranslateCoordinates(ioncore_g.dpy, ev->window, ev2.window,
+                                 ev->x, ev->y, &(ev2.x), &(ev2.y),
+                                 &(ev2.subwindow))){
+            if(ioncore_do_handle_buttonpress(&ev2))
+                return TRUE;
+        }
+    }
+
+    dblclick=(p_clickcnt==1 && time_in_threshold(ev->time) && 
+              p_button==button && p_state==state && reg==p_reg);
+    
+    p_motion=FALSE;
+    p_motion_begin_handler=NULL;
+    p_motion_handler=NULL;
+    p_motion_end_handler=NULL;
+    p_key_handler=NULL;
+    p_killed_handler=NULL;
+    p_grabstate=ST_INIT;
+    p_button=button;
+    p_state=state;
+    p_orig_x=p_x=ev->x_root;
+    p_orig_y=p_y=ev->y_root;
+    p_time=ev->time;
+    p_clickcnt=0;
+
+    watch_setup(&p_regwatch, (Obj*)reg, NULL);
+    
+    subreg=region_current(reg);
+    p_area=window_press((WWindow*)reg, ev, &subreg);
+    if(subreg!=NULL)
+        watch_setup(&p_subregwatch, (Obj*)subreg, NULL);
+
+    if(dblclick){
+        pressbind=region_lookup_binding(reg, BINDING_BUTTONDBLCLICK, state,
+                                             button, p_area);
+    }
+    
+    if(pressbind==NULL){
+        pressbind=region_lookup_binding(reg, BINDING_BUTTONPRESS, state,
+                                             button, p_area);
+    }
+    
+    ioncore_grab_establish(reg, handle_key, pointer_grab_killed, 0);
+    p_grabstate=ST_HELD;
+    
+    call_button(pressbind, ev);
+    
+    return TRUE;
+}
+
+
+bool ioncore_do_handle_buttonrelease(XButtonEvent *ev)
+{
+    WBinding *binding=NULL;
+    
+    if(p_button!=ev->button)
+           return FALSE;
+
+    if(p_reg!=NULL){
+        if(p_motion==FALSE){
+            p_clickcnt=1;
+            binding=region_lookup_binding(p_reg, BINDING_BUTTONCLICK,
+                                               p_state, p_button, p_area);
+            call_button(binding, ev);
+        }else{
+            call_motion_end(ev);
+        }
+        
+    }
+    
+    ioncore_grab_remove(handle_key);
+    finish_pointer();
+    
+    return TRUE;
+}
+
+
+void ioncore_do_handle_motionnotify(XMotionEvent *ev)
+{
+    int dx, dy;
+    WBinding *binding=NULL;
+    
+    if(p_reg==NULL)
+        return;
+    
+    if(!p_motion){
+        if(motion_in_threshold(ev->x_root, ev->y_root))
+            return;
+        binding=region_lookup_binding(p_reg, BINDING_BUTTONMOTION,
+                                           p_state, p_button, p_area);
+    }
+    
+    p_time=ev->time;
+    dx=ev->x_root-p_x;
+    dy=ev->y_root-p_y;
+    p_x=ev->x_root;
+    p_y=ev->y_root;    
+    
+    if(!p_motion){
+        call_motion_begin(binding, ev, dx, dy);
+        p_motion=TRUE;
+    }else{
+        call_motion(ev, dx, dy);
+    }
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/pointer.h b/ioncore/pointer.h
new file mode 100644 (file)
index 0000000..bf2e00a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * ion/ioncore/pointer.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_POINTER_H
+#define ION_IONCORE_POINTER_H
+
+#include "common.h"
+#include "region.h"
+#include "grab.h"
+
+typedef void WButtonHandler(WRegion *reg, XButtonEvent *ev);
+typedef void WMotionHandler(WRegion *reg, XMotionEvent *ev, int dx, int dy);
+
+extern bool ioncore_do_handle_buttonpress(XButtonEvent *ev);
+extern bool ioncore_do_handle_buttonrelease(XButtonEvent *ev);
+extern void ioncore_do_handle_motionnotify(XMotionEvent *ev);
+
+extern XEvent *ioncore_current_pointer_event();
+extern WRegion *ioncore_pointer_grab_region();
+
+extern bool ioncore_set_drag_handlers(WRegion *reg, 
+                                      WMotionHandler *begin,
+                                      WMotionHandler *motion, 
+                                      WButtonHandler *end,
+                                      GrabHandler *handler,
+                                      GrabKilledHandler *killhandler);
+
+#endif /* ION_IONCORE_POINTER_H */
diff --git a/ioncore/presize.c b/ioncore/presize.c
new file mode 100644 (file)
index 0000000..80b7203
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * ion/ioncore/presize.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "presize.h"
+#include "resize.h"
+#include "window.h"
+#include "pointer.h"
+#include "grab.h"
+
+
+/*{{{ Resize */
+
+
+static int p_dx1mul=0, p_dx2mul=0, p_dy1mul=0, p_dy2mul=0;
+
+
+#define MINCORNER 16
+
+
+void window_p_resize_prepare(WWindow *wwin, XButtonEvent *ev)
+{
+    int ww=REGION_GEOM(wwin).w/2;
+    int hh=REGION_GEOM(wwin).h/2;
+    int xdiv, ydiv;
+    int tmpx, tmpy, atmpx, atmpy;
+
+    p_dx1mul=0;
+    p_dx2mul=0;
+    p_dy1mul=0;
+    p_dy2mul=0;
+    
+    tmpx=ev->x-ww;
+    tmpy=hh-ev->y;
+    xdiv=ww/2;
+    ydiv=hh/2;
+    atmpx=abs(tmpx);
+    atmpy=abs(tmpy);
+    
+    if(xdiv<MINCORNER && xdiv>1){
+        xdiv=ww-MINCORNER;
+        if(xdiv<1)
+            xdiv=1;
+    }
+    
+    if(ydiv<MINCORNER && ydiv>1){
+        ydiv=hh-MINCORNER;
+        if(ydiv<1)
+            ydiv=1;
+    }
+    
+    if(xdiv==0){
+        p_dx2mul=1;
+    }else if(hh*atmpx/xdiv>=tmpy && -hh*atmpx/xdiv<=tmpy){
+        p_dx1mul=(tmpx<0);
+        p_dx2mul=(tmpx>=0);
+    }
+    
+    if(ydiv==0){
+        p_dy2mul=1;
+    }else if(ww*atmpy/ydiv>=tmpx && -ww*atmpy/ydiv<=tmpx){
+        p_dy1mul=(tmpy>0);
+        p_dy2mul=(tmpy<=0);
+    }
+}
+
+
+static void p_moveres_end(WWindow *wwin, XButtonEvent *ev)
+{
+    WMoveresMode *mode=moveres_mode((WRegion*)wwin);
+    if(mode!=NULL)
+        moveresmode_do_end(mode, TRUE);
+}
+
+
+static void p_moveres_cancel(WWindow *wwin)
+{
+    WMoveresMode *mode=moveres_mode((WRegion*)wwin);
+    if(mode!=NULL)
+        moveresmode_do_end(mode, FALSE);
+}
+
+
+static void confine_to_parent(WWindow *wwin)
+{
+    WRegion *par=REGION_PARENT_REG(wwin);
+    if(par!=NULL)
+        ioncore_grab_confine_to(region_xwindow(par));
+}
+
+
+static void p_resize_motion(WWindow *wwin, XMotionEvent *ev, int dx, int dy)
+{
+    WMoveresMode *mode=moveres_mode((WRegion*)wwin);
+    if(mode!=NULL){
+        moveresmode_delta_resize(mode, p_dx1mul*dx, p_dx2mul*dx,
+                                 p_dy1mul*dy, p_dy2mul*dy, NULL);
+    }
+}
+
+
+static void p_resize_begin(WWindow *wwin, XMotionEvent *ev, int dx, int dy)
+{
+    region_begin_resize((WRegion*)wwin, NULL, TRUE);
+    p_resize_motion(wwin, ev, dx, dy);
+}
+
+
+/*EXTL_DOC
+ * Start resizing \var{wwin} with the mouse or other pointing device.
+ * This function should only be used by binding it to \emph{mpress} or
+ * \emph{mdrag} action.
+ */
+EXTL_EXPORT_MEMBER
+void window_p_resize(WWindow *wwin)
+{
+    if(!ioncore_set_drag_handlers((WRegion*)wwin,
+                                  (WMotionHandler*)p_resize_begin,
+                                  (WMotionHandler*)p_resize_motion,
+                                  (WButtonHandler*)p_moveres_end,
+                                  NULL, 
+                                  (GrabKilledHandler*)p_moveres_cancel))
+        return;
+    
+    confine_to_parent(wwin);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Move */
+
+
+static void p_move_motion(WWindow *wwin, XMotionEvent *ev, int dx, int dy)
+{
+    WMoveresMode *mode=moveres_mode((WRegion*)wwin);
+    if(mode!=NULL)
+        moveresmode_delta_move(mode, dx, dy, NULL);
+}
+
+
+static void p_move_begin(WWindow *wwin, XMotionEvent *ev, int dx, int dy)
+{
+    region_begin_move((WRegion*)wwin, NULL, TRUE);
+    p_move_motion(wwin, ev, dx, dy);
+}
+
+
+/*EXTL_DOC
+ * Start moving \var{wwin} with the mouse or other pointing device.
+ * This function should only be used by binding it to \emph{mpress} or
+ * \emph{mdrag} action.
+ */
+EXTL_EXPORT_MEMBER
+void window_p_move(WWindow *wwin)
+{
+    if(!ioncore_set_drag_handlers((WRegion*)wwin,
+                                  (WMotionHandler*)p_move_begin,
+                                  (WMotionHandler*)p_move_motion,
+                                  (WButtonHandler*)p_moveres_end,
+                                  NULL, 
+                                  (GrabKilledHandler*)p_moveres_cancel))
+        return;
+    
+    confine_to_parent(wwin);
+}
+
+
+/*}}}*/
diff --git a/ioncore/presize.h b/ioncore/presize.h
new file mode 100644 (file)
index 0000000..85fd889
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * ion/ioncore/presize.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_PRESIZE_H
+#define ION_IONCORE_PRESIZE_H
+
+#include "common.h"
+#include "window.h"
+
+extern void window_p_resize_prepare(WWindow *wwin, XButtonEvent *ev);
+extern void window_p_resize(WWindow *wwin);
+extern void window_p_move(WWindow *wwin);
+
+#endif /* ION_IONCORE_PRESIZE_H */
diff --git a/ioncore/property.c b/ioncore/property.c
new file mode 100644 (file)
index 0000000..3bacca1
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * ion/ioncore/property.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <X11/Xmd.h>
+#include <string.h>
+
+#include "common.h"
+#include "property.h"
+#include "global.h"
+
+
+/*{{{ Primitives */
+
+
+static ulong xwindow_get_property_(Window win, Atom atom, Atom type, 
+                                   ulong n32expected, bool more, uchar **p,
+                                   int *format)
+{
+    Atom real_type;
+    ulong n=-1, extra=0;
+    int status;
+    
+    do{
+        status=XGetWindowProperty(ioncore_g.dpy, win, atom, 0L, n32expected, 
+                                  False, type, &real_type, format, &n,
+                                  &extra, p);
+        
+        if(status!=Success || *p==NULL)
+            return -1;
+    
+        if(extra==0 || !more)
+            break;
+        
+        XFree((void*)*p);
+        n32expected+=(extra+4)/4;
+        more=FALSE;
+    }while(1);
+
+    if(n==0){
+        XFree((void*)*p);
+        *p=NULL;
+        return -1;
+    }
+    
+    return n;
+}
+
+
+ulong xwindow_get_property(Window win, Atom atom, Atom type, 
+                           ulong n32expected, bool more, uchar **p)
+{
+    int format=0;
+    return xwindow_get_property_(win, atom, type, n32expected, more, p, 
+                                 &format);
+}
+
+
+/*}}}*/
+
+
+/*{{{ String property stuff */
+
+
+char *xwindow_get_string_property(Window win, Atom a, int *nret)
+{
+    char *p;
+    int n;
+    
+    n=xwindow_get_property(win, a, XA_STRING, 64L, TRUE, (uchar**)&p);
+    
+    if(nret!=NULL)
+        *nret=n;
+    
+    return (n<=0 ? NULL : p);
+}
+
+
+void xwindow_set_string_property(Window win, Atom a, const char *value)
+{
+    if(value==NULL){
+        XDeleteProperty(ioncore_g.dpy, win, a);
+    }else{
+        XChangeProperty(ioncore_g.dpy, win, a, XA_STRING,
+                        8, PropModeReplace, (uchar*)value, strlen(value));
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Integer property stuff */
+
+
+bool xwindow_get_integer_property(Window win, Atom a, int *vret)
+{
+    long *p=NULL;
+    ulong n;
+    
+    n=xwindow_get_property(win, a, XA_INTEGER, 1L, FALSE, (uchar**)&p);
+    
+    if(n>0 && p!=NULL){
+        *vret=*p;
+        XFree((void*)p);
+        return TRUE;
+    }
+    
+    return FALSE;
+}
+
+
+void xwindow_set_integer_property(Window win, Atom a, int value)
+{
+    CARD32 data[2];
+    
+    data[0]=value;
+    
+    XChangeProperty(ioncore_g.dpy, win, a, XA_INTEGER,
+                    32, PropModeReplace, (uchar*)data, 1);
+}
+
+
+/* WM_STATE
+ */
+
+bool xwindow_get_state_property(Window win, int *state)
+{
+    CARD32 *p=NULL;
+    
+    if(xwindow_get_property(win, ioncore_g.atom_wm_state, 
+                            ioncore_g.atom_wm_state, 
+                            2L, FALSE, (uchar**)&p)<=0)
+        return FALSE;
+    
+    *state=*p;
+    
+    XFree((void*)p);
+    
+    return TRUE;
+}
+
+
+void xwindow_set_state_property(Window win, int state)
+{
+    CARD32 data[2];
+    
+    data[0]=state;
+    data[1]=None;
+    
+    XChangeProperty(ioncore_g.dpy, win,
+                    ioncore_g.atom_wm_state, ioncore_g.atom_wm_state,
+                    32, PropModeReplace, (uchar*)data, 2);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Text property stuff */
+
+
+char **xwindow_get_text_property(Window win, Atom a, int *nret)
+{
+    XTextProperty prop;
+    char **list=NULL;
+    int n=0;
+    Status st=0;
+    
+    st=XGetTextProperty(ioncore_g.dpy, win, &prop, a);
+
+    if(nret)
+        *nret=(!st ? 0 : -1);
+    
+    if(!st)
+        return NULL;
+
+#ifdef CF_XFREE86_TEXTPROP_BUG_WORKAROUND
+    while(prop.nitems>0){
+        if(prop.value[prop.nitems-1]=='\0')
+            prop.nitems--;
+        else
+            break;
+    }
+#endif
+
+    if(!ioncore_g.use_mb){
+        st=XTextPropertyToStringList(&prop, &list, &n);
+    }else{
+        st=XmbTextPropertyToTextList(ioncore_g.dpy, &prop, &list, &n);
+        st=!st;
+    }
+
+    XFree(prop.value);
+    
+    if(!st || n==0 || list==NULL)
+        return NULL;
+    
+    if(nret)
+        *nret=n;
+    
+    return list;
+}
+
+
+void xwindow_set_text_property(Window win, Atom a, const char **ptr, int n)
+{
+    XTextProperty prop;
+    Status st;
+
+    if(!ioncore_g.use_mb){
+        st=XStringListToTextProperty((char **)&ptr, n, &prop);
+    }else{
+#ifdef X_HAVE_UTF8_STRING        
+        st=XmbTextListToTextProperty(ioncore_g.dpy, (char **)ptr, n,
+                                     XUTF8StringStyle, &prop);
+#else        
+        st=XmbTextListToTextProperty(ioncore_g.dpy, (char **)ptr, n,
+                                     XTextStyle, &prop);
+#endif        
+        st=!st;
+    }
+    
+    if(!st)
+        return;
+    
+    XSetTextProperty(ioncore_g.dpy, win, &prop, a);
+    XFree(prop.value);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Exports */
+
+
+/*EXTL_DOC
+ * Create a new atom. See \code{XInternAtom}(3) manual page for details.
+ */
+EXTL_EXPORT
+int ioncore_x_intern_atom(const char *name, bool only_if_exists)
+{
+    return XInternAtom(ioncore_g.dpy, name, only_if_exists);
+}
+
+
+/*EXTL_DOC
+ * Get the name of an atom. See \code{XGetAtomName}(3) manual page for 
+ * details.
+ */
+EXTL_EXPORT
+char *ioncore_x_get_atom_name(int atom)
+{
+    return XGetAtomName(ioncore_g.dpy, atom);
+}
+
+
+#define CP(TYPE)                                              \
+    {                                                         \
+        TYPE *d=(TYPE*)p;                                     \
+        for(i=0; i<n; i++) extl_table_seti_i(tab, i+1, d[i]); \
+    }
+
+
+/*EXTL_DOC
+ * Get a property \var{atom} of type \var{atom_type} for window \var{win}. 
+ * The \var{n32expected} parameter indicates the expected number of 32bit
+ * words, and \var{more} indicates whether all or just this amount of data
+ * should be fetched. Each 8, 16 or 32bit element of the property, as
+ * deciphered from \var{atom_type} is a field in the returned table.
+ * See \code{XGetWindowProperty}(3) manual page for more information.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab ioncore_x_get_window_property(int win, int atom, int atom_type,
+                                      int n32expected, bool more)
+{
+    uchar *p=NULL;
+    ExtlTab tab;
+    int format=0;
+    int i, n;
+    
+    n=xwindow_get_property_(win, atom, atom_type, n32expected, more, &p, 
+                            &format);
+    
+    if(p==NULL)
+        return extl_table_none();
+    
+    if(n<=0 || (format!=8 && format!=16 && format!=32)){
+        free(p);
+        return extl_table_none();
+    }
+    
+    tab=extl_create_table();
+    
+    switch(format){
+    case 8: CP(char); break;
+    case 16: CP(short); break;
+    case 32: CP(long); break;
+    }
+
+    return tab;
+}
+
+
+#define GET(TYPE)                                          \
+    {                                                      \
+        TYPE *d=ALLOC_N(TYPE, n);                          \
+        if(d==NULL) return;                                \
+        for(i=0; i<n; i++) {                               \
+            if(!extl_table_geti_i(tab, i+1, &tmp)) return; \
+            d[i]=tmp;                                      \
+        }                                                  \
+        p=(uchar*)d;                                       \
+    }
+
+        
+static bool get_mode(const char *mode, int *m)
+{
+    if(strcmp(mode, "replace")==0)
+        *m=PropModeReplace;
+    else if(strcmp(mode, "prepend")==0)
+        *m=PropModePrepend;
+    else if(strcmp(mode, "append")==0)
+        *m=PropModeAppend;
+    else
+        return FALSE;
+    
+    return TRUE;
+}
+
+
+/*EXTL_DOC
+ * Modify a window property. The \var{mode} is one of
+ * \code{"replace"}, \code{"prepend"} or \code{"append"}, and format
+ * is either 8, 16 or 32. Also see \fnref{ioncore.x_get_window_property}
+ * and the \code{XChangeProperty}(3) manual page.
+ */
+EXTL_EXPORT
+void ioncore_x_change_property(int win, int atom, int atom_type,
+                               int format, const char *mode, ExtlTab tab)
+{
+    int tmp, m, i, n=extl_table_get_n(tab);
+    uchar *p;
+        
+    if(n<0 || !get_mode(mode, &m)){
+        warn(TR("Invalid arguments."));
+        return;
+    }
+
+    switch(format){
+    case 8: GET(char); break;
+    case 16: GET(short); break;
+    case 32: GET(long); break;
+    default:
+        warn(TR("Invalid arguments."));
+        return;
+    }
+    
+    XChangeProperty(ioncore_g.dpy, win, atom, atom_type, format, m, p, n);
+    
+    free(p);
+}
+
+
+/*EXTL_DOC
+ * Delete a window property.
+ */
+EXTL_EXPORT
+void ioncore_x_delete_property(int win, int atom)
+{
+    XDeleteProperty(ioncore_g.dpy, win, atom);
+}
+
+
+/*EXTL_DOC
+ * Get a text property for a window (\code{STRING}, \code{COMPOUND_TEXT},
+ * or \code{UTF8_STRING} property converted). The fields in the returned
+ * table (starting from 1) are the null-separated parts of the property.
+ * See the \code{XGetTextProperty}(3) manual page for more information.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab ioncore_x_get_text_property(int win, int atom)
+{
+    char **list;
+    int i, n;
+    ExtlTab tab=extl_table_none();
+    
+    list=xwindow_get_text_property(win, atom, &n);
+    
+    if(list!=NULL){
+        if(n!=0){
+            tab=extl_create_table();
+            for(i=0; i<n; i++)
+                extl_table_seti_s(tab, i+1, list[i]);
+        }
+        XFreeStringList(list);
+    }
+    
+    return tab;
+}
+
+
+/*EXTL_DOC
+ * Set a text property for a window. The fields of \var{tab} starting from
+ * 1 should be the different null-separated parts of the property.
+ * See the \code{XSetTextProperty}(3) manual page for more information.
+ */
+EXTL_EXPORT
+void ioncore_x_set_text_property(int win, int atom, ExtlTab tab)
+{
+    char **list;
+    int i, n=extl_table_get_n(tab);
+    
+    list=ALLOC_N(char*, n);
+
+    if(list==NULL)
+        return;
+    
+    for(i=0; i<n; i++){
+        list[i]=NULL;
+        extl_table_geti_s(tab, i+1, &(list[i]));
+    }
+    
+    xwindow_set_text_property(win, atom, (const char **)list, n);
+    
+    XFreeStringList(list);
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/property.h b/ioncore/property.h
new file mode 100644 (file)
index 0000000..c340380
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * ion/ioncore/property.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_PROPERTY_H
+#define ION_IONCORE_PROPERTY_H
+
+#include <X11/Xatom.h>
+
+#include "common.h"
+
+extern ulong xwindow_get_property(Window win, Atom atom, Atom type,
+                                  ulong n32expected, bool more, uchar **p);
+extern char *xwindow_get_string_property(Window win, Atom a, int *nret);
+extern void xwindow_set_string_property(Window win, Atom a, const char *value);
+extern bool xwindow_get_integer_property(Window win, Atom a, int *vret);
+extern void xwindow_set_integer_property(Window win, Atom a, int value);
+extern bool xwindow_get_state_property(Window win, int *state);
+extern void xwindow_set_state_property(Window win, int state);
+extern char **xwindow_get_text_property(Window win, Atom a, int *nret);
+extern void xwindow_set_text_property(Window win, Atom a, 
+                                      const char **p, int n);
+
+#endif /* ION_IONCORE_PROPERTY_H */
+
diff --git a/ioncore/rectangle.c b/ioncore/rectangle.c
new file mode 100644 (file)
index 0000000..00cae86
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * ion/ioncore/rectangle.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/minmax.h>
+#include <libextl/extl.h>
+
+#include "common.h"
+#include "rectangle.h"
+
+
+void rectangle_constrain(WRectangle *g, const WRectangle *bounds)
+{
+    const WRectangle tmpg=*g;
+    
+    g->x=minof(maxof(tmpg.x, bounds->x), tmpg.x+tmpg.w-1);
+    g->y=minof(maxof(tmpg.y, bounds->y), tmpg.y+tmpg.h-1);
+    g->w=maxof(1, minof(bounds->x+bounds->w, tmpg.x+tmpg.w)-g->x);
+    g->h=maxof(1, minof(bounds->y+bounds->h, tmpg.y+tmpg.h)-g->y);
+}
+
+
+bool rectangle_contains(const WRectangle *g, int x, int y)
+{
+    return (x>=g->x && x<g->x+g->w && y>=g->y && y<g->y+g->h);
+}
+
+
+void rectangle_debugprint(const WRectangle *g, const char *n)
+{
+    fprintf(stderr, "%s %d, %d; %d, %d\n", n, g->x, g->y, g->w, g->h);
+}
+
+
+int rectangle_compare(const WRectangle *g, const WRectangle *h)
+{
+    return ((g->x!=h->x ? RECTANGLE_X_DIFF : 0) |
+            (g->y!=h->y ? RECTANGLE_Y_DIFF : 0) |
+            (g->w!=h->w ? RECTANGLE_W_DIFF : 0) |
+            (g->h!=h->h ? RECTANGLE_H_DIFF : 0));
+}
diff --git a/ioncore/rectangle.h b/ioncore/rectangle.h
new file mode 100644 (file)
index 0000000..da7b305
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * ion/ioncore/rectangle.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_RECTANGLE_H
+#define ION_IONCORE_RECTANGLE_H
+
+#include <stdio.h>
+#include "common.h"
+#include <libextl/extl.h>
+
+INTRSTRUCT(WRectangle);
+
+DECLSTRUCT(WRectangle){
+    int x, y;
+    int w, h;
+};
+
+#define RECTANGLE_SAME   0x00
+#define RECTANGLE_X_DIFF 0x01
+#define RECTANGLE_Y_DIFF 0x02
+#define RECTANGLE_POS_DIFF (RECTANGLE_X_DIFF|RECTANGLE_Y_DIFF)
+#define RECTANGLE_W_DIFF 0x04
+#define RECTANGLE_H_DIFF 0x08
+#define RECTANGLE_SZ_DIFF (RECTANGLE_W_DIFF|RECTANGLE_H_DIFF)
+
+extern int rectangle_compare(const WRectangle *g, const WRectangle *h);
+extern bool rectangle_contains(const WRectangle *g, int x, int y);
+extern void rectangle_constrain(WRectangle *g, const WRectangle *bounds);
+
+extern void rectange_debugprint(const WRectangle *g, const char *n);
+
+#endif /* ION_IONCORE_RECTANGLE_H */
+
diff --git a/ioncore/regbind.c b/ioncore/regbind.c
new file mode 100644 (file)
index 0000000..43bc3a6
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * ion/regbind.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <libtu/objp.h>
+#include <libmainloop/defer.h>
+#include "common.h"
+#include "region.h"
+#include "binding.h"
+#include "regbind.h"
+
+
+/*{{{ Grab/ungrab */
+
+
+static void do_binding_grab_on_ungrab_on(const WRegion *reg, 
+                                         const WBinding *binding,
+                                         const WBindmap *bindmap, bool grab)
+{
+    Window win=region_xwindow(reg);
+    WRegBindingInfo *r;
+    
+    for(r=reg->bindings; r!=NULL; r=r->next){
+        if(r->bindmap==bindmap)
+            continue;
+        if(bindmap_lookup_binding(r->bindmap, binding->act, binding->state,
+                                  binding->kcb)!=NULL)
+            break;
+    }
+    if(r==NULL && binding->area==0){
+        if(grab)
+            binding_grab_on(binding, win);
+        else
+            binding_ungrab_on(binding, win);
+    }
+}
+
+
+static void do_binding_grab_on_ungrab_ons(const WRegion *reg, 
+                                          const WBindmap *bindmap,
+                                          bool grab)
+{
+    Rb_node node=NULL;
+    WBinding *binding=NULL;
+    
+    if(!(reg->flags&REGION_BINDINGS_ARE_GRABBED) ||
+       bindmap->bindings==NULL){
+        return;
+    }
+    
+    FOR_ALL_BINDINGS(binding, node, bindmap->bindings){
+        do_binding_grab_on_ungrab_on(reg, binding, bindmap, grab);
+    }
+}
+
+
+static void grab_ungrabbed_bindings(const WRegion *reg, const WBindmap *bindmap)
+{
+    do_binding_grab_on_ungrab_ons(reg, bindmap, TRUE);
+}
+
+
+static void ungrab_freed_bindings(const WRegion *reg, const WBindmap *bindmap)
+{
+    do_binding_grab_on_ungrab_ons(reg, bindmap, FALSE);
+}
+
+
+void rbind_binding_added(const WRegBindingInfo *rbind, 
+                         const WBinding *binding,
+                         const WBindmap *bindmap)
+{
+    if(binding->area==0 && rbind->reg->flags&REGION_BINDINGS_ARE_GRABBED)
+        do_binding_grab_on_ungrab_on(rbind->reg, binding, rbind->bindmap, TRUE);
+}
+
+
+void rbind_binding_removed(const WRegBindingInfo *rbind, 
+                           const WBinding *binding,
+                           const WBindmap *bindmap)
+{
+    if(binding->area==0 && rbind->reg->flags&REGION_BINDINGS_ARE_GRABBED)
+        do_binding_grab_on_ungrab_on(rbind->reg, binding, rbind->bindmap, FALSE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Find */
+
+
+static WRegBindingInfo *find_rbind(WRegion *reg, WBindmap *bindmap,
+                                   WRegion *owner)
+{
+    WRegBindingInfo *rbind;
+    
+    for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next){
+        if(rbind->bindmap==bindmap && rbind->owner==owner)
+            return rbind;
+    }
+    
+    return NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Interface */
+
+
+static WRegBindingInfo *region_do_add_bindmap_owned(WRegion *reg, 
+                                                    WBindmap *bindmap, 
+                                                    WRegion *owner,
+                                                    bool first)
+{
+    WRegBindingInfo *rbind;
+    
+    if(bindmap==NULL)
+        return NULL;
+    
+    rbind=ALLOC(WRegBindingInfo);
+    
+    if(rbind==NULL)
+        return NULL;
+    
+    rbind->bindmap=bindmap;
+    rbind->owner=owner;
+    rbind->reg=reg;
+    rbind->tmp=0;
+    
+    LINK_ITEM(bindmap->rbind_list, rbind, bm_next, bm_prev);
+
+    if(region_xwindow(reg)!=None && !(reg->flags&REGION_GRAB_ON_PARENT))
+        grab_ungrabbed_bindings(reg, bindmap);
+    
+    /* Link to reg's rbind list*/ {
+        WRegBindingInfo *b=reg->bindings;
+        if(first){
+            LINK_ITEM_FIRST(b, rbind, next, prev);
+        }else{
+            LINK_ITEM_LAST(b, rbind, next, prev);
+        }
+        reg->bindings=b;
+    }
+    
+    return rbind;
+}
+
+
+bool region_add_bindmap(WRegion *reg, WBindmap *bindmap)
+{
+    if(find_rbind(reg, bindmap, NULL)!=NULL)
+        return FALSE;
+    return (region_do_add_bindmap_owned(reg, bindmap, NULL, TRUE)!=NULL);
+}
+
+
+static void remove_rbind(WRegion *reg, WRegBindingInfo *rbind)
+{
+    UNLINK_ITEM(rbind->bindmap->rbind_list, rbind, bm_next, bm_prev);
+    
+    /* Unlink from reg's rbind list*/ {
+        WRegBindingInfo *b=reg->bindings;
+        UNLINK_ITEM(b, rbind, next, prev);
+        reg->bindings=b;
+    }
+    
+    if(region_xwindow(reg)!=None && !(reg->flags&REGION_GRAB_ON_PARENT))
+        ungrab_freed_bindings(reg, rbind->bindmap);
+
+    free(rbind);
+}
+
+
+void region_remove_bindmap(WRegion *reg, WBindmap *bindmap)
+{
+    WRegBindingInfo *rbind=find_rbind(reg, bindmap, NULL);
+    if(rbind!=NULL)
+        remove_rbind(reg, rbind);
+}
+
+
+void region_remove_bindings(WRegion *reg)
+{
+    WRegBindingInfo *rbind;
+    
+    while((rbind=(WRegBindingInfo*)reg->bindings)!=NULL)
+        remove_rbind(reg, rbind);
+}
+
+
+WBinding *region_lookup_keybinding(WRegion *reg, const XKeyEvent *ev,
+                                   const WSubmapState *sc,
+                                   WRegion **binding_owner_ret)
+{
+    WRegBindingInfo *rbind=NULL;
+    WBinding *binding=NULL;
+    const WSubmapState *s=NULL;
+    WBindmap *bindmap=NULL;
+    int i;
+    
+    *binding_owner_ret=reg;
+
+    for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next){
+        bindmap=rbind->bindmap;
+        
+        for(s=sc; s!=NULL && bindmap!=NULL; s=s->next){
+            binding=bindmap_lookup_binding(bindmap, BINDING_KEYPRESS, s->state, s->key);
+
+            if(binding==NULL){
+                bindmap=NULL;
+                break;
+            }
+            
+            bindmap=binding->submap;
+        }
+        
+        if(bindmap==NULL){
+            /* There may be no next iteration so we must reset binding here
+             * because we have not found a proper binding.
+             */
+            binding=NULL;
+            continue;
+        }
+
+        binding=bindmap_lookup_binding(bindmap, BINDING_KEYPRESS, ev->state, ev->keycode);
+        
+        if(binding!=NULL)
+            break;
+    }
+    
+    if(binding!=NULL && rbind->owner!=NULL)
+        *binding_owner_ret=rbind->owner;
+    
+    return binding;
+}
+
+
+WBinding *region_lookup_binding(WRegion *reg, int act, uint state,
+                                uint kcb, int area)
+{
+    WRegBindingInfo *rbind;
+    WBinding *binding=NULL;
+    
+    for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next){
+        if(rbind->owner!=NULL)
+            continue;
+        binding=bindmap_lookup_binding_area(rbind->bindmap, act, state, kcb, area);
+        if(binding!=NULL)
+            break;
+    }
+    
+    return binding;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Update */
+
+
+static void add_bindings(WRegion *reg, WRegion *r2)
+{
+    WRegion *rx=REGION_MANAGER(r2);
+    WRegBindingInfo *rbind, *rb2;
+    WBinding *binding=NULL;
+    
+    if(rx!=NULL && REGION_PARENT_REG(rx)==reg){
+        /* The recursion is here to get the bindmaps correctly ordered. */
+        add_bindings(reg, rx);
+    }
+    
+    if(r2->flags&REGION_GRAB_ON_PARENT){
+        for(rb2=(WRegBindingInfo*)r2->bindings; rb2!=NULL; rb2=rb2->next){
+            rbind=find_rbind(reg, rb2->bindmap, r2);
+            if(rbind==NULL){
+                rbind=region_do_add_bindmap_owned(reg, rb2->bindmap, 
+                                                  r2, TRUE);
+            }
+            if(rbind!=NULL)
+                rbind->tmp=1;
+        }
+    }
+}
+
+
+void region_do_update_owned_grabs(WRegion *reg)
+{
+    WRegBindingInfo *rbind, *rb2;
+
+    reg->flags&=~REGION_BINDING_UPDATE_SCHEDULED;
+    
+    /* clear flags */
+    for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next)
+        rbind->tmp=0;
+    
+    /* make new grabs */
+    if(reg->active_sub!=NULL)
+        add_bindings(reg, reg->active_sub);
+    
+    /* remove old grabs */
+    for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rb2){
+        rb2=rbind->next;
+        if(rbind->tmp!=1 && rbind->owner!=NULL)
+            remove_rbind(reg, rbind);
+    }
+}
+
+void region_update_owned_grabs(WRegion *reg)
+{
+    if(reg->flags&REGION_BINDING_UPDATE_SCHEDULED 
+       || OBJ_IS_BEING_DESTROYED(reg)
+       || ioncore_g.opmode==IONCORE_OPMODE_DEINIT){
+        return;
+    }
+    
+    if(mainloop_defer_action((Obj*)reg, 
+                             (WDeferredAction*)region_do_update_owned_grabs)){
+        reg->flags|=REGION_BINDING_UPDATE_SCHEDULED;
+    }else{
+        region_do_update_owned_grabs(reg);
+    }
+}
+
+
+/*}}}*/
diff --git a/ioncore/regbind.h b/ioncore/regbind.h
new file mode 100644 (file)
index 0000000..b7c0412
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * ion/ioncore/regbind.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_REGBIND_H
+#define ION_IONCORE_REGBIND_H
+
+#include "global.h"
+#include "common.h"
+#include "region.h"
+#include "binding.h"
+
+
+DECLSTRUCT(WSubmapState){
+    uint key, state;
+    WSubmapState *next;
+};
+
+
+extern bool region_add_bindmap(WRegion *reg, WBindmap *bindmap);
+extern void region_remove_bindmap(WRegion *reg, WBindmap *bindmap);
+
+extern void region_remove_bindings(WRegion *reg);
+
+extern WBinding *region_lookup_keybinding(WRegion *reg, const XKeyEvent *ev,
+                                          const WSubmapState *sc,
+                                          WRegion **binding_owner_ret);
+extern WBinding *region_lookup_binding(WRegion *reg, int act, uint state,
+                                            uint kcb, int area);
+
+extern void rbind_binding_added(const WRegBindingInfo *rbind, 
+                                const WBinding *binding,
+                                const WBindmap *bindmap);
+extern void rbind_binding_removed(const WRegBindingInfo *rbind, 
+                                  const WBinding *binding,
+                                  const WBindmap *bindmap);
+
+extern void region_update_owned_grabs(WRegion *reg);
+extern void region_do_update_owned_grabs(WRegion *reg);
+
+#endif /* ION_IONCORE_REGBIND_H */
+
diff --git a/ioncore/reginfo.c b/ioncore/reginfo.c
new file mode 100644 (file)
index 0000000..5606b09
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * ion/ioncore/reginfo.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <ctype.h>
+
+#include "common.h"
+#include "region.h"
+#include <libtu/objp.h>
+#include "attach.h"
+#include "reginfo.h"
+
+
+static WRegClassInfo *reg_class_infos;
+
+
+/*{{{ Registration */
+
+
+bool ioncore_register_regclass(ClassDescr *descr, WRegionLoadCreateFn *lc_fn)
+{
+    WRegClassInfo *info;
+    
+    if(descr==NULL)
+        return FALSE;
+    
+    info=ALLOC(WRegClassInfo);
+    if(info==NULL)
+        return FALSE;
+    
+    info->descr=descr;
+    info->lc_fn=lc_fn;
+    LINK_ITEM(reg_class_infos, info, next, prev);
+    
+    return TRUE;
+}
+
+
+void ioncore_unregister_regclass(ClassDescr *descr)
+{
+    WRegClassInfo *info;
+    
+    for(info=reg_class_infos; info!=NULL; info=info->next){
+        if(descr==info->descr){
+            UNLINK_ITEM(reg_class_infos, info, next, prev);
+            free(info);
+            return;
+        }
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Lookup */
+
+
+WRegClassInfo *ioncore_lookup_regclass(const char *name, bool inheriting_ok)
+{
+    WRegClassInfo *info;
+    ClassDescr *descr;
+
+    if(name==NULL)
+        return NULL;
+    
+    for(info=reg_class_infos; info!=NULL; info=info->next){
+        for(descr=info->descr; 
+            descr!=NULL; 
+            descr=(inheriting_ok ? descr->ancestor : NULL)){
+            
+            if(strcmp(descr->name, name)==0){
+                if(info->lc_fn!=NULL)
+                    return info;
+            }
+        }
+    }
+    return NULL;
+}
+
+
+/*}}}*/
+
+
diff --git a/ioncore/reginfo.h b/ioncore/reginfo.h
new file mode 100644 (file)
index 0000000..a83c2e2
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * ion/ioncore/reginfo.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_REGINFO_H
+#define ION_IONCORE_REGINFO_H
+
+#include "common.h"
+#include <libtu/obj.h>
+#include "region.h"
+#include "window.h"
+#include <libextl/extl.h>
+#include "rectangle.h"
+
+typedef WRegion *WRegionLoadCreateFn(WWindow *par, const WFitParams *fp,
+                                     ExtlTab tab);
+typedef WRegion *WRegionSimpleCreateFn(WWindow *par, const WFitParams *fp);
+
+INTRSTRUCT(WRegClassInfo);
+    
+DECLSTRUCT(WRegClassInfo){
+    ClassDescr *descr;
+    WRegionLoadCreateFn *lc_fn;
+    WRegClassInfo *next, *prev;
+};
+
+
+extern bool ioncore_register_regclass(ClassDescr *descr,
+                                      WRegionLoadCreateFn *lc_fn);
+extern void ioncore_unregister_regclass(ClassDescr *descr);
+
+extern WRegClassInfo *ioncore_lookup_regclass(const char *name, 
+                                              bool inheriting_ok);
+
+#endif /* ION_IONCORE_REGINFO_H */
+
diff --git a/ioncore/region-iter.h b/ioncore/region-iter.h
new file mode 100644 (file)
index 0000000..bf9f135
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ion/ioncore/region-iter.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_REGION_ITER_H
+#define ION_IONCORE_REGION_ITER_H
+
+#define REGION_FIRST_CHILD(PAR) (((WRegion*)(PAR))->children)
+#define REGION_LAST_CHILD(PAR)                                               \
+ (REGION_FIRST_CHILD(PAR)==NULL ? NULL                                       \
+ : REGION_PREV_CHILD_WRAP(REGION_FIRST_CHILD(PAR), REGION_FIRST_CHILD(PAR)))
+#define REGION_NEXT_CHILD(PAR, REG) (((WRegion*)(REG))->p_next)
+#define REGION_PREV_CHILD(PAR, REG) ((((WRegion*)(REG))->p_prev->p_next) ? \
+                                 (((WRegion*)(REG))->p_prev) : NULL)
+#define REGION_NEXT_CHILD_WRAP(PAR, REG)                 \
+  (((REG) && ((WRegion*)(REG))->p_next)                  \
+  ? ((WRegion*)(REG))->p_next : REGION_FIRST_CHILD(PAR))
+#define REGION_PREV_CHILD_WRAP(PAR, REG) \
+  ((REG) ? ((WRegion*)(REG))->p_prev     \
+  : REGION_FIRST_CHILD(PAR))
+
+#define FOR_ALL_CHILDREN(PAR, REG) \
+    for((REG)=((WRegion*)(PAR))->children; (REG)!=NULL; (REG)=(REG)->p_next)
+
+#define FOR_ALL_CHILDREN_W_NEXT(PAR, REG, NEXT)                              \
+  for((REG)=((WRegion*)(PAR))->children, (NEXT)=((REG)==NULL ? NULL : (REG)->p_next);\
+      (REG)!=NULL;                                                            \
+      (REG)=(NEXT), (NEXT)=((REG)==NULL ? NULL : (REG)->p_next))
+
+#endif /* ION_IONCORE_REGION_ITER_H */
diff --git a/ioncore/region.c b/ioncore/region.c
new file mode 100644 (file)
index 0000000..dede094
--- /dev/null
@@ -0,0 +1,914 @@
+/*
+ * ion/ioncore/region.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libextl/extl.h>
+#include <libmainloop/defer.h>
+
+#include "common.h"
+#include "global.h"
+#include "region.h"
+#include "focus.h"
+#include "regbind.h"
+#include "names.h"
+#include "resize.h"
+#include "manage.h"
+#include "extlconv.h"
+#include "activity.h"
+#include "region-iter.h"
+
+
+#define D2(X)
+
+
+WHook *region_notify_hook=NULL;
+
+static void region_notify_change_(WRegion *reg, const char *how, 
+                                  Obj *detail);
+
+
+/*{{{ Init & deinit */
+
+
+void region_init(WRegion *reg, WWindow *par, const WFitParams *fp)
+{
+    if(fp->g.w<0 || fp->g.h<0)
+        warn(TR("Creating region with negative width or height!"));
+    
+    reg->geom=fp->g;
+    reg->flags=0;
+    reg->bindings=NULL;
+    reg->rootwin=NULL;
+    
+    reg->children=NULL;
+    reg->parent=NULL;
+    reg->p_next=NULL;
+    reg->p_prev=NULL;
+    
+    reg->active_sub=NULL;
+    reg->active_prev=NULL;
+    reg->active_next=NULL;
+    
+    reg->ni.name=NULL;
+    reg->ni.inst_off=0;
+    reg->ni.node=NULL;
+    
+    reg->manager=NULL;
+    
+    reg->submapstat=NULL;
+    
+    reg->mgd_activity=FALSE;
+
+    if(par!=NULL){
+        reg->rootwin=((WRegion*)par)->rootwin;
+        region_set_parent(reg, par);
+    }else{
+        assert(OBJ_IS(reg, WRootWin));/* || OBJ_IS(reg, WScreen));*/
+    }
+}
+
+
+static void destroy_children(WRegion *reg)
+{
+    WRegion *sub, *prev=NULL;
+    bool complained=FALSE;
+    
+    /* destroy children */
+    while(1){
+        sub=reg->children;
+        if(sub==NULL)
+            break;
+        assert(!OBJ_IS_BEING_DESTROYED(sub));
+        assert(sub!=prev);
+        if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && !complained && OBJ_IS(reg, WClientWin)){
+            warn(TR("Destroying object \"%s\" with client windows as "
+                    "children."), region_name(reg));
+            complained=TRUE;
+        }
+        prev=sub;
+        destroy_obj((Obj*)sub);
+    }
+}
+
+
+void region_deinit(WRegion *reg)
+{
+    destroy_children(reg);
+
+    if(ioncore_g.focus_next==reg){
+        D(warn("Region to be focused next destroyed[1]."));
+        ioncore_g.focus_next=NULL;
+    }
+
+    region_detach_manager(reg);
+    region_unset_parent(reg);
+    region_remove_bindings(reg);
+    
+    region_unregister(reg);
+
+    region_focuslist_deinit(reg);
+
+    if(ioncore_g.focus_next==reg){
+        D(warn("Region to be focused next destroyed[2]."));
+        ioncore_g.focus_next=NULL;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfuns */
+
+
+bool region_fitrep(WRegion *reg, WWindow *par, const WFitParams *fp)
+{
+    bool ret=FALSE;
+    CALL_DYN_RET(ret, bool, region_fitrep, reg, (reg, par, fp));
+    return ret;
+}
+
+
+void region_updategr(WRegion *reg)
+{
+    CALL_DYN(region_updategr, reg, (reg));
+}
+
+
+void region_map(WRegion *reg)
+{
+    CALL_DYN(region_map, reg, (reg));
+}
+
+
+void region_unmap(WRegion *reg)
+{
+    CALL_DYN(region_unmap, reg, (reg));
+}
+
+
+void region_notify_rootpos(WRegion *reg, int x, int y)
+{
+    CALL_DYN(region_notify_rootpos, reg, (reg, x, y));
+}
+
+
+Window region_xwindow(const WRegion *reg)
+{
+    Window ret=None;
+    CALL_DYN_RET(ret, Window, region_xwindow, reg, (reg));
+    return ret;
+}
+
+
+void region_activated(WRegion *reg)
+{
+    CALL_DYN(region_activated, reg, (reg));
+}
+
+
+void region_inactivated(WRegion *reg)
+{
+    CALL_DYN(region_inactivated, reg, (reg));
+}
+
+
+void region_do_set_focus(WRegion *reg, bool warp)
+{
+    CALL_DYN(region_do_set_focus, reg, (reg, warp));
+}
+
+
+/*{{{ Manager region dynfuns */
+
+
+void region_managed_activated(WRegion *mgr, WRegion *reg)
+{
+    CALL_DYN(region_managed_activated, mgr, (mgr, reg));
+}
+
+
+void region_managed_inactivated(WRegion *mgr, WRegion *reg)
+{
+    CALL_DYN(region_managed_inactivated, mgr, (mgr, reg));
+}
+
+
+static bool region_managed_prepare_focus_default(WRegion *mgr, WRegion *reg, 
+                                                 int flags, 
+                                                 WPrepareFocusResult *res)
+{
+    if(!region_prepare_focus(mgr, flags, res))
+        return FALSE;
+    
+    res->reg=reg;
+    res->flags=flags;
+    return TRUE;
+}
+
+    
+bool region_managed_prepare_focus(WRegion *mgr, WRegion *reg, 
+                                  int flags, 
+                                  WPrepareFocusResult *res)
+{
+    bool ret=TRUE;
+    CALL_DYN_RET(ret, bool, region_managed_prepare_focus, mgr, 
+                 (mgr, reg, flags, res));
+    return ret;
+}
+
+
+void region_managed_notify(WRegion *mgr, WRegion *reg, const char *how)
+{
+    CALL_DYN(region_managed_notify, mgr, (mgr, reg, how));
+}
+
+
+void region_managed_remove(WRegion *mgr, WRegion *reg)
+{
+    CALL_DYN(region_managed_remove, mgr, (mgr, reg));
+}
+
+
+/*EXTL_DOC
+ * Return the object, if any, that is considered ''currently active''
+ * within the objects managed by \var{mplex}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WRegion *region_current(WRegion *mgr)
+{
+    WRegion *ret=NULL;
+    CALL_DYN_RET(ret, WRegion*, region_current, mgr, (mgr));
+    return ret;
+}
+
+
+void region_child_removed(WRegion *reg, WRegion *sub)
+{
+    CALL_DYN(region_child_removed, reg, (reg, sub));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfun defaults */
+
+
+void region_updategr_default(WRegion *reg)
+{
+    WRegion *sub=NULL;
+    
+    FOR_ALL_CHILDREN(reg, sub){
+        region_updategr(sub);
+    }
+}
+
+
+/*}}}*/
+
+
+/*}}}*/
+
+
+/*{{{ Goto */
+
+
+bool region_prepare_focus(WRegion *reg, int flags, 
+                          WPrepareFocusResult *res)
+{
+    WRegion *mgr=REGION_MANAGER(reg);
+    WRegion *par=REGION_PARENT_REG(reg);
+
+    if(REGION_IS_MAPPED(reg) && region_may_control_focus(reg)){
+        res->reg=reg;
+        res->flags=0;
+        return TRUE;
+    }else{
+        if(mgr!=NULL){
+            return region_managed_prepare_focus(mgr, reg, flags, res);
+        }else if(par!=NULL){
+            if(!region_prepare_focus(par, flags, res))
+                return FALSE;
+            /* Just focus reg, if it has no manager, and parent can be 
+             * focused. 
+             */
+        }
+        res->reg=reg;
+        res->flags=flags;
+        return TRUE;
+    }
+}
+
+
+bool region_goto_flags(WRegion *reg, int flags)
+{
+    WPrepareFocusResult res;
+    bool ret;
+    
+    ret=region_prepare_focus(reg, flags, &res);
+    
+    if(res.reg!=NULL){
+        if(res.flags&REGION_GOTO_FOCUS)
+            region_maybewarp(res.reg, !(res.flags&REGION_GOTO_NOWARP));
+    }
+    
+    return ret;
+}
+
+
+/*EXTL_DOC
+ * Attempt to display \var{reg}, save region activity status and then
+ * warp to (or simply set focus to if warping is disabled) \var{reg}.
+ * 
+ * Note that this function is asynchronous; the region will not
+ * actually have received the focus when this function returns.
+ */
+EXTL_EXPORT_MEMBER
+bool region_goto(WRegion *reg)
+{
+    return region_goto_flags(reg, REGION_GOTO_FOCUS);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Fit/reparent */
+
+
+void region_fit(WRegion *reg, const WRectangle *geom, WRegionFitMode mode)
+{
+    WFitParams fp;
+    fp.g=*geom;
+    fp.mode=mode&~REGION_FIT_GRAVITY;
+    fp.gravity=ForgetGravity;
+    region_fitrep(reg, NULL, &fp);
+}
+
+
+bool region_reparent(WRegion *reg, WWindow *par,
+                     const WRectangle *geom, WRegionFitMode mode)
+{
+    WFitParams fp;
+    fp.g=*geom;
+    fp.mode=mode;
+    return region_fitrep(reg, par, &fp);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Close */
+
+
+static bool region_rqclose_default(WRegion *reg, bool relocate)
+{
+    WPHolder *ph;
+    bool refuse=TRUE;
+    
+    if((!relocate && !region_may_destroy(reg)) ||
+       !region_manager_allows_destroying(reg)){
+        return FALSE;
+    }
+    
+    ph=region_get_rescue_pholder(reg);
+    
+    if(ph!=NULL){
+        refuse=!region_rescue_clientwins(reg, ph);
+        destroy_obj((Obj*)ph);
+    }
+
+    if(refuse){
+        warn(TR("Failed to rescue some client windows - not closing."));
+        return FALSE;
+    }
+
+    mainloop_defer_destroy((Obj*)reg);
+    
+    return TRUE;
+}
+
+
+/*EXTL_DOC
+ * Attempt to close/destroy \var{reg}. Whether this operation works
+ * depends on whether the particular type of region in question has
+ * implemented the feature and, in case of client windows, whether
+ * the client supports the \code{WM_DELETE} protocol (see also
+ * \fnref{WClientWin.kill}). If the operation is likely to succeed,
+ * \code{true} is returned, otherwise \code{false}. In most cases the
+ * region will not have been actually destroyed when this function returns.
+ * If \var{relocate} is not set, and \var{reg} manages other regions, it
+ * will not be closed. Otherwise the managed regions will be attempted
+ * to be relocated.
+ */
+EXTL_EXPORT_MEMBER
+bool region_rqclose(WRegion *reg, bool relocate)
+{
+    bool ret=FALSE;
+    CALL_DYN_RET(ret, bool, region_rqclose, reg, (reg, relocate));
+    return ret;
+}
+
+
+static WRegion *region_rqclose_propagate_default(WRegion *reg, 
+                                                 WRegion *maybe_sub)
+{
+    if(maybe_sub==NULL)
+        maybe_sub=region_current(reg);
+    if(maybe_sub!=NULL)
+        return region_rqclose_propagate(maybe_sub, NULL);
+    return (region_rqclose(reg, FALSE) ? reg : NULL);
+}
+
+
+/*EXTL_DOC
+ * Recursively attempt to close a region or one of the regions managed by 
+ * it. If \var{sub} is set, it will be used as the managed region, otherwise
+ * \fnref{WRegion.current}\code{(reg)}. The object to be closed is
+ * returned or NULL if nothing can be closed. Also see notes for
+ * \fnref{WRegion.rqclose}.
+ */
+EXTL_EXPORT_MEMBER
+WRegion *region_rqclose_propagate(WRegion *reg, WRegion *maybe_sub)
+{
+    WRegion *ret=NULL;
+    CALL_DYN_RET(ret, WRegion*, region_rqclose_propagate, reg,
+                 (reg, maybe_sub));
+    return ret;
+}
+
+
+bool region_may_destroy(WRegion *reg)
+{
+    bool ret=TRUE;
+    CALL_DYN_RET(ret, bool, region_may_destroy, reg, (reg));
+    return ret;
+}
+
+
+bool region_managed_may_destroy(WRegion *mgr, WRegion *reg)
+{
+    bool ret=TRUE;
+    CALL_DYN_RET(ret, bool, region_managed_may_destroy, mgr, (mgr, reg));
+    return ret;
+}
+
+
+bool region_manager_allows_destroying(WRegion *reg)
+{
+    WRegion *mgr=REGION_MANAGER(reg);
+    
+    if(mgr==NULL)
+        return TRUE;
+    
+    return region_managed_may_destroy(mgr, reg);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Manager/parent stuff */
+
+
+/* Routine to call to unmanage a region */
+void region_detach_manager(WRegion *reg)
+{
+    WRegion *mgr;
+    
+    mgr=REGION_MANAGER(reg);
+    
+    if(mgr==NULL)
+        return;
+    
+    /* Restore activity state to non-parent manager */
+    if(region_may_control_focus(reg)){
+        WRegion *par=REGION_PARENT_REG(reg);
+        if(par!=NULL && mgr!=par && REGION_PARENT_REG(mgr)==par){
+            /* REGION_ACTIVE shouldn't be set for windowless regions
+             * but make the parent's active_sub point to it
+             * nevertheless so that region_may_control_focus can
+             * be made to work.
+             */
+            par->active_sub=mgr;
+            /*if(region_xwindow(mgr)!=None){*/
+                region_do_set_focus(mgr, FALSE);
+            /*}*/
+        }
+    }
+
+    region_set_activity(reg, SETPARAM_UNSET);
+
+    region_managed_remove(mgr, reg);
+
+    assert(REGION_MANAGER(reg)==NULL);
+}
+
+
+/* This should only be called within region_managed_remove,
+ * _after_ any managed lists and other essential structures
+ * of mgr have been broken.
+ */
+void region_unset_manager(WRegion *reg, WRegion *mgr)
+{
+    if(reg->manager!=mgr)
+        return;
+    
+    reg->manager=NULL;
+    
+    if(region_is_activity_r(reg))
+        region_clear_mgd_activity(mgr);
+
+    region_notify_change_(reg, "unset_manager", (Obj*)mgr);
+}
+
+
+/* This should be called within region attach routines,
+ * _after_ any managed lists and other essential structures
+ * of mgr have been set up.
+ */
+void region_set_manager(WRegion *reg, WRegion *mgr)
+{
+    assert(reg->manager==NULL);
+    
+    reg->manager=mgr;
+    
+    if(region_is_activity_r(reg))
+        region_mark_mgd_activity(mgr);
+    
+    region_notify_change_(reg, "set_manager", (Obj*)mgr);
+}
+
+
+void region_set_parent(WRegion *reg, WWindow *parent)
+{
+    assert(reg->parent==NULL && parent!=NULL);
+    LINK_ITEM(((WRegion*)parent)->children, reg, p_next, p_prev);
+    reg->parent=parent;
+}
+
+
+void region_unset_parent(WRegion *reg)
+{
+    WRegion *p=REGION_PARENT_REG(reg);
+
+    if(p==NULL || p==reg)
+        return;
+
+    UNLINK_ITEM(p->children, reg, p_next, p_prev);
+    reg->parent=NULL;
+
+    if(p->active_sub==reg){
+        p->active_sub=NULL;
+        region_update_owned_grabs(p);
+    }
+    
+    region_child_removed(p, reg);
+}
+
+
+/*EXTL_DOC
+ * Returns the region that manages \var{reg}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WRegion *region_manager(WRegion *reg)
+{
+    return reg->manager;
+}
+
+
+/*EXTL_DOC
+ * Returns the parent region of \var{reg}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WWindow *region_parent(WRegion *reg)
+{
+    return reg->parent;
+}
+
+
+WRegion *region_manager_or_parent(WRegion *reg)
+{
+    if(reg->manager!=NULL)
+        return reg->manager;
+    else
+        return (WRegion*)(reg->parent);
+}
+
+
+WRegion *region_get_manager_chk(WRegion *p, const ClassDescr *descr)
+{
+    WRegion *mgr=NULL;
+    
+    if(p!=NULL){
+        mgr=REGION_MANAGER(p);
+        if(obj_is((Obj*)mgr, descr))
+            return mgr;
+    }
+    
+    return NULL;
+}
+
+/*}}}*/
+
+
+/*{{{ Stacking and ordering */
+
+
+static void region_stacking_default(WRegion *reg, 
+                                    Window *bottomret, Window *topret)
+{
+    Window win=region_xwindow(reg);
+    *bottomret=win;
+    *topret=win;
+}
+
+
+void region_stacking(WRegion *reg, Window *bottomret, Window *topret)
+{
+    CALL_DYN(region_stacking, reg, (reg, bottomret, topret));
+}
+
+
+void region_restack(WRegion *reg, Window other, int mode)
+{
+    CALL_DYN(region_restack, reg, (reg, other, mode));
+}
+
+
+
+bool region_managed_rqorder(WRegion *reg, WRegion *sub, WRegionOrder order)
+{
+    bool ret=FALSE;
+    CALL_DYN_RET(ret, bool, region_managed_rqorder, reg, (reg, sub, order));
+    return ret;
+}
+
+
+bool region_rqorder(WRegion *reg, WRegionOrder order)
+{
+    WRegion *mgr=REGION_MANAGER(reg);
+    
+    if(mgr==NULL)
+        return FALSE;
+    else
+        return region_managed_rqorder(mgr, reg, order);
+}
+
+
+/*EXTL_DOC
+ * Request ordering. Currently supported values for \var{ord}
+ * are 'front' and 'back'.
+ */
+EXTL_EXPORT_AS(WRegion, rqorder)
+bool region_rqorder_extl(WRegion *reg, const char *ord)
+{
+    WRegionOrder order;
+    
+    if(strcmp(ord, "front")==0){
+        order=REGION_ORDER_FRONT;
+    }else if(strcmp(ord, "back")==0){
+        order=REGION_ORDER_BACK;
+    }else{
+        return FALSE;
+    }
+    
+    return region_rqorder(reg, order);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+/*EXTL_DOC
+ * Returns the root window \var{reg} is on.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WRootWin *region_rootwin_of(const WRegion *reg)
+{
+    WRootWin *rw;
+    assert(reg!=NULL); /* Lua interface should not pass NULL reg. */
+    rw=(WRootWin*)(reg->rootwin);
+    assert(rw!=NULL);
+    return rw;
+}
+
+
+/*EXTL_DOC
+ * Returns the screen \var{reg} is on.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WScreen *region_screen_of(WRegion *reg)
+{
+    while(reg!=NULL){
+        if(OBJ_IS(reg, WScreen))
+            return (WScreen*)reg;
+        reg=REGION_PARENT_REG(reg);
+    }
+    return NULL;
+}
+
+
+Window region_root_of(const WRegion *reg)
+{
+    return WROOTWIN_ROOT(region_rootwin_of(reg));
+}
+
+
+bool region_same_rootwin(const WRegion *reg1, const WRegion *reg2)
+{
+    return (reg1->rootwin==reg2->rootwin);
+}
+
+
+/*EXTL_DOC
+ * Is \var{reg} visible/is it and all it's ancestors mapped?
+ */
+EXTL_SAFE
+EXTL_EXPORT_AS(WRegion, is_mapped)
+bool region_is_fully_mapped(WRegion *reg)
+{
+    for(; reg!=NULL; reg=REGION_PARENT_REG(reg)){
+        if(!REGION_IS_MAPPED(reg))
+            return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+void region_rootpos(WRegion *reg, int *xret, int *yret)
+{
+    WRegion *par;
+
+    par=REGION_PARENT_REG(reg);
+    
+    if(par==NULL || par==reg){
+        *xret=0;
+        *yret=0;
+        return;
+    }
+    
+    region_rootpos(par, xret, yret);
+    
+    *xret+=REGION_GEOM(reg).x;
+    *yret+=REGION_GEOM(reg).y;
+}
+
+
+static bool mrsh_not(WHookDummy *fn, void *p)
+{
+    WRegion *reg=(WRegion*)((void**)p)[0];
+    const char *how=(const char*)((void**)p)[1];
+    Obj *detail=(Obj*)((void**)p)[2];
+    
+    fn(reg, how, detail);
+    
+    return TRUE;
+}
+
+
+static bool mrshe_not(ExtlFn fn, void *p)
+{
+    WRegion *reg=(WRegion*)((void**)p)[0];
+    const char *how=(const char*)((void**)p)[1];
+    Obj *detail=(Obj*)((void**)p)[2];
+    
+    extl_call(fn, "oso", NULL, reg, how, detail);
+    
+    return TRUE;
+}
+
+
+static void region_notify_change_(WRegion *reg, const char *how, 
+                                  Obj *detail)
+{
+    const void *p[3];
+    
+    p[0]=reg;
+    p[1]=how;
+    p[2]=detail;
+
+    extl_protect(NULL);
+    hook_call(region_notify_hook, p, mrsh_not, mrshe_not),
+    extl_unprotect(NULL);
+    
+}
+
+
+void region_notify_change(WRegion *reg, const char *how)
+{
+    WRegion *mgr=REGION_MANAGER(reg);
+
+    if(mgr!=NULL)
+        region_managed_notify(mgr, reg, how);
+    
+    region_notify_change_(reg, how, NULL);
+}
+
+
+/*EXTL_DOC
+ * Returns the geometry of \var{reg} within its parent; a table with fields
+ * \var{x}, \var{y}, \var{w} and \var{h}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+ExtlTab region_geom(WRegion *reg)
+{
+    return extl_table_from_rectangle(&REGION_GEOM(reg));
+}
+
+
+bool region_handle_drop(WRegion *reg, int x, int y, WRegion *dropped)
+{
+    bool ret=FALSE;
+    CALL_DYN_RET(ret, bool, region_handle_drop, reg, (reg, x, y, dropped));
+    return ret;
+}
+
+
+WRegion *region_managed_within(WRegion *reg, WRegion *mgd)
+{
+    while(mgd!=NULL && 
+          (REGION_PARENT_REG(mgd)==reg ||
+           REGION_PARENT_REG(mgd)==REGION_PARENT_REG(reg))){
+        
+        if(REGION_MANAGER(mgd)==reg)
+            return mgd;
+        mgd=REGION_MANAGER(mgd);
+    }
+    
+    return NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab region_dynfuntab[]={
+    {region_managed_rqgeom,
+     region_managed_rqgeom_allow},
+     
+    {region_managed_rqgeom_absolute,
+     region_managed_rqgeom_absolute_default},
+    
+    {region_updategr, 
+     region_updategr_default},
+    
+    {(DynFun*)region_rescue_clientwins,
+     (DynFun*)region_rescue_child_clientwins},
+
+    {(DynFun*)region_prepare_manage,
+     (DynFun*)region_prepare_manage_default},
+
+    {(DynFun*)region_prepare_manage_transient,
+     (DynFun*)region_prepare_manage_transient_default},
+
+    {(DynFun*)region_managed_prepare_focus,
+     (DynFun*)region_managed_prepare_focus_default},
+
+    {(DynFun*)region_rqclose_propagate,
+     (DynFun*)region_rqclose_propagate_default},
+
+    {(DynFun*)region_rqclose,
+     (DynFun*)region_rqclose_default},
+    
+    {(DynFun*)region_displayname,
+     (DynFun*)region_name},
+    
+    {region_stacking, 
+     region_stacking_default},
+    
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WRegion, Obj, region_deinit, region_dynfuntab);
+
+    
+/*}}}*/
+
diff --git a/ioncore/region.h b/ioncore/region.h
new file mode 100644 (file)
index 0000000..b2feff7
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * ion/ioncore/region.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_REGION_H
+#define ION_IONCORE_REGION_H
+
+#include <libtu/obj.h>
+#include <libmainloop/hooks.h>
+#include "common.h"
+#include "rectangle.h"
+
+#define REGION_MAPPED        0x0001
+#define REGION_ACTIVE        0x0002
+#define REGION_HAS_GRABS     0x0004
+#define REGION_TAGGED        0x0008
+#define REGION_BINDINGS_ARE_GRABBED 0x0020
+#define REGION_GRAB_ON_PARENT 0x0040
+#define REGION_ACTIVITY      0x0100
+#define REGION_SKIP_FOCUS    0x0200
+#define REGION_CWINS_BEING_RESCUED 0x0400
+#define REGION_PLEASE_WARP   0x0800
+#define REGION_BINDING_UPDATE_SCHEDULED 0x1000
+
+#define REGION_GOTO_FOCUS    0x0001
+#define REGION_GOTO_NOWARP   0x0002
+#define REGION_GOTO_ENTERWINDOW 0x0004
+
+/* Use region_is_fully_mapped instead for most cases. */
+#define REGION_IS_MAPPED(R)     (((WRegion*)(R))->flags&REGION_MAPPED)
+#define REGION_MARK_MAPPED(R)   (((WRegion*)(R))->flags|=REGION_MAPPED)
+#define REGION_MARK_UNMAPPED(R) (((WRegion*)(R))->flags&=~REGION_MAPPED)
+#define REGION_IS_ACTIVE(R)     (((WRegion*)(R))->flags&REGION_ACTIVE)
+#define REGION_IS_TAGGED(R)     (((WRegion*)(R))->flags&REGION_TAGGED)
+#define REGION_IS_URGENT(R)     (((WRegion*)(R))->flags&REGION_URGENT)
+#define REGION_GEOM(R)          (((WRegion*)(R))->geom)
+#define REGION_ACTIVE_SUB(R)    (((WRegion*)(R))->active_sub)
+
+#define REGION_MANAGER(R)       (((WRegion*)(R))->manager)
+#define REGION_MANAGER_CHK(R, TYPE) OBJ_CAST(REGION_MANAGER(R), TYPE)
+
+#define REGION_PARENT(REG)      (((WRegion*)(REG))->parent)
+#define REGION_PARENT_REG(REG)  ((WRegion*)REGION_PARENT(REG))
+
+#define REGION_FIT_BOUNDS    0x0001 /* Geometry is maximum bounds */
+#define REGION_FIT_ROTATE    0x0002 /* for Xrandr */
+#define REGION_FIT_WHATEVER  0x0004 /* for attach routines; g is not final */
+#define REGION_FIT_GRAVITY   0x0008 /* just a hint; for use with BOUNDS */
+#define REGION_FIT_EXACT     0x0000 /* No flags; exact fit */
+
+
+typedef int WRegionFitMode;
+
+
+typedef enum{
+    REGION_ORDER_FRONT,
+    REGION_ORDER_BACK
+} WRegionOrder;
+
+
+INTRSTRUCT(WFitParams);
+DECLSTRUCT(WFitParams){
+    WRectangle g;
+    WRegionFitMode mode;
+    int gravity;
+    int rotation;
+};
+
+
+INTRSTRUCT(WRegionNameInfo);
+DECLSTRUCT(WRegionNameInfo){
+    char *name;
+    int inst_off;
+    void *node;
+};
+
+
+INTRSTRUCT(WPrepareFocusResult);
+DECLSTRUCT(WPrepareFocusResult){
+    WRegion *reg;
+    int flags;
+};
+
+
+DECLCLASS(WRegion){
+    Obj obj;
+    
+    WRectangle geom;
+    void *rootwin;
+    bool flags;
+
+    WWindow *parent;
+    WRegion *children;
+    WRegion *p_next, *p_prev;
+    
+    void *bindings;
+    WSubmapState *submapstat;
+    
+    WRegion *active_sub;
+    WRegion *active_prev, *active_next;
+    
+    WRegionNameInfo ni;
+    
+    WRegion *manager;
+    
+    int mgd_activity;
+};
+
+
+
+
+extern void region_init(WRegion *reg, WWindow *par, const WFitParams *fp);
+extern void region_deinit(WRegion *reg);
+
+DYNFUN bool region_fitrep(WRegion *reg, WWindow *par, const WFitParams *fp);
+DYNFUN void region_map(WRegion *reg);
+DYNFUN void region_unmap(WRegion *reg);
+DYNFUN Window region_xwindow(const WRegion *reg);
+DYNFUN void region_activated(WRegion *reg);
+DYNFUN void region_inactivated(WRegion *reg);
+DYNFUN void region_updategr(WRegion *reg);
+DYNFUN bool region_rqclose(WRegion *reg, bool relocate);
+DYNFUN WRegion *region_rqclose_propagate(WRegion *reg, WRegion *maybe_sub);
+DYNFUN WRegion *region_current(WRegion *mgr);
+DYNFUN void region_notify_rootpos(WRegion *reg, int x, int y);
+DYNFUN bool region_may_destroy(WRegion *reg);
+DYNFUN WRegion *region_managed_control_focus(WRegion *mgr, WRegion *reg);
+DYNFUN void region_managed_remove(WRegion *reg, WRegion *sub);
+DYNFUN bool region_managed_prepare_focus(WRegion *reg, WRegion *sub, 
+                                         int flags, WPrepareFocusResult *res);
+DYNFUN void region_managed_activated(WRegion *reg, WRegion *sub);
+DYNFUN void region_managed_inactivated(WRegion *reg, WRegion *sub);
+DYNFUN void region_managed_notify(WRegion *reg, WRegion *sub, const char *how);
+DYNFUN bool region_managed_may_destroy(WRegion *mgr, WRegion *reg);
+DYNFUN bool region_managed_rqorder(WRegion *reg, WRegion *sub, 
+                                   WRegionOrder order);
+
+DYNFUN void region_child_removed(WRegion *reg, WRegion *sub);
+
+DYNFUN void region_restack(WRegion *reg, Window other, int mode);
+DYNFUN void region_stacking(WRegion *reg, Window *bottomret, Window *topret);
+
+DYNFUN bool region_handle_drop(WRegion *reg, int x, int y, WRegion *dropped);
+
+extern bool region_rqorder(WRegion *reg, WRegionOrder order);
+
+extern bool region_prepare_focus(WRegion *reg, int flags,
+                                 WPrepareFocusResult *res);
+
+extern void region_fit(WRegion *reg, const WRectangle *geom, 
+                       WRegionFitMode mode);
+extern bool region_reparent(WRegion *reg, WWindow *target, 
+                            const WRectangle *geom, WRegionFitMode mode);
+
+extern void region_updategr_default(WRegion *reg);
+
+extern void region_rootpos(WRegion *reg, int *xret, int *yret);
+extern void region_notify_change(WRegion *reg, const char *how);
+
+extern bool region_goto(WRegion *reg);
+extern bool region_goto_flags(WRegion *reg, int flags);
+
+extern bool region_is_fully_mapped(WRegion *reg);
+
+extern void region_detach_manager(WRegion *reg);
+
+extern WWindow *region_parent(WRegion *reg);
+extern WRegion *region_manager(WRegion *reg);
+extern WRegion *region_manager_or_parent(WRegion *reg);
+extern void region_set_parent(WRegion *reg, WWindow *par);
+extern void region_set_manager(WRegion *reg, WRegion *mgr);
+extern void region_unset_manager(WRegion *reg, WRegion *mgr);
+extern void region_unset_parent(WRegion *reg);
+
+extern WRootWin *region_rootwin_of(const WRegion *reg);
+extern Window region_root_of(const WRegion *reg);
+extern WScreen *region_screen_of(WRegion *reg);
+extern bool region_same_rootwin(const WRegion *reg1, const WRegion *reg2);
+
+extern bool region_manager_allows_destroying(WRegion *reg);
+
+extern WRegion *region_managed_within(WRegion *reg, WRegion *mgd);
+
+extern WHook *region_notify_hook;
+
+#endif /* ION_IONCORE_REGION_H */
diff --git a/ioncore/resize.c b/ioncore/resize.c
new file mode 100644 (file)
index 0000000..a8cff5a
--- /dev/null
@@ -0,0 +1,853 @@
+/* 
+ * ion/ioncore/resize.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <libextl/extl.h>
+#include <libmainloop/defer.h>
+
+#include "common.h"
+#include "global.h"
+#include "resize.h"
+#include "gr.h"
+#include "sizehint.h"
+#include "event.h"
+#include "cursor.h"
+#include "extlconv.h"
+#include "grab.h"
+#include "framep.h"
+#include "infowin.h"
+
+
+#define XOR_RESIZE (!ioncore_g.opaque_resize)
+
+
+/*{{{ Size/position display and rubberband */
+
+
+static void draw_rubberbox(WRootWin *rw, const WRectangle *rect)
+{
+    XPoint fpts[5];
+    
+    fpts[0].x=rect->x;
+    fpts[0].y=rect->y;
+    fpts[1].x=rect->x+rect->w;
+    fpts[1].y=rect->y;
+    fpts[2].x=rect->x+rect->w;
+    fpts[2].y=rect->y+rect->h;
+    fpts[3].x=rect->x;
+    fpts[3].y=rect->y+rect->h;
+    fpts[4].x=rect->x;
+    fpts[4].y=rect->y;
+    
+    XDrawLines(ioncore_g.dpy, WROOTWIN_ROOT(rw), rw->xor_gc, fpts, 5, 
+               CoordModeOrigin);
+}
+
+
+static int max_width(GrBrush *brush, const char *str)
+{
+    int maxw=0, w;
+    
+    while(str && *str!='\0'){
+        w=grbrush_get_text_width(brush, str, 1);
+        if(w>maxw)
+            maxw=w;
+        str++;
+    }
+    
+    return maxw;
+}
+
+
+static int chars_for_num(int d)
+{
+    int n=0;
+    
+    do{
+        n++;
+        d/=10;
+    }while(d);
+    
+    return n;
+}
+
+
+static WInfoWin *setup_moveres_display(WWindow *parent, int cx, int cy)
+{
+    GrBorderWidths bdw;
+    GrFontExtents fnte;
+    WInfoWin *infowin;
+    WFitParams fp;
+    
+    fp.mode=REGION_FIT_EXACT;
+    fp.g.x=0;
+    fp.g.y=0;
+    fp.g.w=1;
+    fp.g.h=1;
+    
+    infowin=create_infowin(parent, &fp, "moveres_display");
+    
+    if(infowin==NULL)
+        return NULL;
+    
+    grbrush_get_border_widths(INFOWIN_BRUSH(infowin), &bdw);
+    grbrush_get_font_extents(INFOWIN_BRUSH(infowin), &fnte);
+    
+    /* Create move/resize position/size display window */
+    fp.g.w=3;
+    fp.g.w+=chars_for_num(REGION_GEOM(parent).w);
+    fp.g.w+=chars_for_num(REGION_GEOM(parent).h);
+    fp.g.w*=max_width(INFOWIN_BRUSH(infowin), "0123456789x+");     
+    fp.g.w+=bdw.left+bdw.right;
+    fp.g.h=fnte.max_height+bdw.top+bdw.bottom;;
+    
+    fp.g.x=cx-fp.g.w/2;
+    fp.g.y=cy-fp.g.h/2;
+    
+    region_fitrep((WRegion*)infowin, NULL, &fp);
+    region_map((WRegion*)infowin);
+    
+    return infowin;
+}
+
+
+static void moveres_draw_infowin(WMoveresMode *mode)
+{
+    WRectangle geom;
+    char *buf;
+    
+    if(mode->infowin==NULL)
+        return;
+    
+    buf=INFOWIN_BUFFER(mode->infowin);
+    
+    if(buf==NULL)
+        return;
+    
+    if(mode->mode==MOVERES_SIZE){
+        int w, h;
+        
+        w=mode->geom.w;
+        h=mode->geom.h;
+        
+        if((mode->hints.inc_set) &&
+           (mode->hints.width_inc>1 || mode->hints.height_inc>1)){
+            if(mode->hints.base_set){
+                w-=mode->hints.base_width;
+                h-=mode->hints.base_height;
+            }
+            w/=mode->hints.width_inc;
+            h/=mode->hints.height_inc;
+        }
+        
+        snprintf(buf, INFOWIN_BUFFER_LEN, "%dx%d", w, h);
+    }else{
+        snprintf(buf, INFOWIN_BUFFER_LEN, "%+d %+d", 
+                 mode->geom.x, mode->geom.y);
+    }
+    
+    window_draw((WWindow*)mode->infowin, TRUE);
+}
+
+
+static void moveres_draw_rubberband(WMoveresMode *mode)
+{
+    WRectangle rgeom=mode->geom;
+    int rx, ry;
+    WRootWin *rootwin=(mode->reg==NULL 
+                       ? NULL 
+                       : region_rootwin_of(mode->reg));
+    
+    if(rootwin==NULL)
+        return;
+    
+    rgeom.x+=mode->parent_rx;
+    rgeom.y+=mode->parent_ry;
+    
+    if(mode->rubfn==NULL)
+        draw_rubberbox(rootwin, &rgeom);
+    else
+        mode->rubfn(rootwin, &rgeom);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Move/resize mode */
+
+
+WMoveresMode *tmpmode=NULL;
+
+
+EXTL_EXPORT
+IMPLCLASS(WMoveresMode, Obj, NULL, NULL);
+
+
+WMoveresMode *moveres_mode(WRegion *reg)
+{
+    if(tmpmode==NULL)
+        return NULL;
+    return ((reg==NULL || tmpmode->reg==reg) ? tmpmode : NULL);
+}
+
+
+WRegion *moveresmode_target(WMoveresMode *mode)
+{
+    return mode->reg;
+}
+
+
+static bool moveresmode_init(WMoveresMode *mode, WRegion *reg,
+                             WDrawRubberbandFn *rubfn, bool cumulative)
+{
+    WWindow *parent;
+    WRegion *mgr;
+    
+    if(tmpmode!=NULL)
+        return FALSE;
+    
+    parent=REGION_PARENT(reg);
+    
+    if(parent==NULL)
+        return FALSE;
+    
+    tmpmode=mode;
+    
+    mode->snap_enabled=FALSE;
+    region_size_hints(reg, &mode->hints);
+    
+    region_rootpos((WRegion*)parent, &mode->parent_rx, &mode->parent_ry);
+    
+    mode->geom=REGION_GEOM(reg);
+    mode->origgeom=REGION_GEOM(reg);
+    mode->dx1=0;
+    mode->dx2=0;
+    mode->dy1=0;
+    mode->dy2=0;
+    mode->rubfn=rubfn;
+    mode->resize_cumulative=cumulative;
+    mode->rqflags=(XOR_RESIZE ? REGION_RQGEOM_TRYONLY : 0);
+    mode->reg=reg;
+    mode->mode=MOVERES_SIZE;
+    
+    /* Get snapping geometry */
+    mgr=REGION_MANAGER(reg);
+    
+    if(mgr!=NULL){
+        mode->snapgeom=REGION_GEOM(mgr);
+        
+        if(mgr==(WRegion*)parent){
+            mode->snapgeom.x=0;
+            mode->snapgeom.y=0;    
+            mode->snap_enabled=FALSE;
+        }else if(REGION_PARENT(mgr)==parent){
+            mode->snap_enabled=TRUE;
+        }
+    }
+    
+    if(!mode->hints.min_set || mode->hints.min_width<1)
+        mode->hints.min_width=1;
+    if(!mode->hints.min_set || mode->hints.min_height<1)
+        mode->hints.min_height=1;
+    
+    /* Set up info window */
+    {
+        int x=mode->geom.x+mode->geom.w/2;
+        int y=mode->geom.y+mode->geom.h/2;
+        mode->infowin=setup_moveres_display(parent, x, y);
+    }
+                                        
+    moveres_draw_infowin(mode);
+    
+    if(XOR_RESIZE){
+        XGrabServer(ioncore_g.dpy);
+        moveres_draw_rubberband(mode);
+    }
+    
+    return TRUE;
+}
+
+
+static WMoveresMode *create_moveresmode(WRegion *reg,
+                                        WDrawRubberbandFn *rubfn,
+                                        bool cumulative)
+{
+    CREATEOBJ_IMPL(WMoveresMode, moveresmode, (p, reg, rubfn, cumulative));
+}
+
+
+WMoveresMode *region_begin_resize(WRegion *reg, WDrawRubberbandFn *rubfn, 
+                                  bool cumulative)
+{
+    WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
+    
+    if(mode!=NULL){
+        mode->mode=MOVERES_SIZE;
+        ioncore_change_grab_cursor(IONCORE_CURSOR_RESIZE);
+    }
+
+    return mode;
+}
+
+
+WMoveresMode *region_begin_move(WRegion *reg, WDrawRubberbandFn *rubfn, 
+                                bool cumulative)
+{
+    WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative);
+    
+    if(mode!=NULL){
+        mode->mode=MOVERES_POS;
+        ioncore_change_grab_cursor(IONCORE_CURSOR_MOVE);
+    }
+    
+    return mode;
+}
+
+
+static void moveresmode_delta(WMoveresMode *mode, 
+                              int dx1, int dx2, int dy1, int dy2,
+                              WRectangle *rret)
+{
+    int realdx1, realdx2, realdy1, realdy2;
+    WRQGeomParams rq=RQGEOMPARAMS_INIT;
+    int w=0, h=0;
+    
+    realdx1=(mode->dx1+=dx1);
+    realdx2=(mode->dx2+=dx2);
+    realdy1=(mode->dy1+=dy1);
+    realdy2=(mode->dy2+=dy2);
+    rq.geom=mode->origgeom;
+    
+    /* snap */
+    if(mode->snap_enabled){
+        WRectangle *sg=&mode->snapgeom;
+        int er=CF_EDGE_RESISTANCE;
+        
+        if(mode->dx1!=0 && rq.geom.x+mode->dx1<sg->x && rq.geom.x+mode->dx1>sg->x-er)
+            realdx1=sg->x-rq.geom.x;
+        if(mode->dx2!=0 && rq.geom.x+rq.geom.w+mode->dx2>sg->x+sg->w && rq.geom.x+rq.geom.w+mode->dx2<sg->x+sg->w+er)
+            realdx2=sg->x+sg->w-rq.geom.x-rq.geom.w;
+        if(mode->dy1!=0 && rq.geom.y+mode->dy1<sg->y && rq.geom.y+mode->dy1>sg->y-er)
+            realdy1=sg->y-rq.geom.y;
+        if(mode->dy2!=0 && rq.geom.y+rq.geom.h+mode->dy2>sg->y+sg->h && rq.geom.y+rq.geom.h+mode->dy2<sg->y+sg->h+er)
+            realdy2=sg->y+sg->h-rq.geom.y-rq.geom.h;
+    }
+    
+    w=mode->origgeom.w-realdx1+realdx2;
+    h=mode->origgeom.h-realdy1+realdy2;
+    
+    if(w<=0)
+        w=mode->hints.min_width;
+    if(h<=0)
+        h=mode->hints.min_height;
+    
+    sizehints_correct(&mode->hints, &w, &h, TRUE, TRUE);
+    
+    /* Do not modify coordinates and sizes that were not requested to be
+     * changed. 
+     */
+    
+    if(mode->dx1==mode->dx2){
+        if(mode->dx1==0 || realdx1!=mode->dx1)
+            rq.geom.x+=realdx1;
+        else
+            rq.geom.x+=realdx2;
+    }else{
+        rq.geom.w=w;
+        if(mode->dx1==0 || realdx1!=mode->dx1)
+            rq.geom.x+=realdx1;
+        else
+            rq.geom.x+=mode->origgeom.w-rq.geom.w;
+    }
+    
+    
+    if(mode->dy1==mode->dy2){
+        if(mode->dy1==0 || realdy1!=mode->dy1)
+            rq.geom.y+=realdy1;
+        else
+            rq.geom.y+=realdy2;
+    }else{
+        rq.geom.h=h;
+        if(mode->dy1==0 || realdy1!=mode->dy1)
+            rq.geom.y+=realdy1;
+        else
+            rq.geom.y+=mode->origgeom.h-rq.geom.h;
+    }
+    
+    if(XOR_RESIZE)
+        moveres_draw_rubberband(mode);
+    
+    if(mode->reg!=NULL){
+        rq.flags=mode->rqflags;
+        region_rqgeom(mode->reg, &rq, &mode->geom);
+    }
+    
+    if(!mode->resize_cumulative){
+        mode->dx1=0;
+        mode->dx2=0;
+        mode->dy1=0;
+        mode->dy2=0;
+        mode->origgeom=mode->geom;
+    }
+    
+    moveres_draw_infowin(mode);
+    
+    if(XOR_RESIZE)
+        moveres_draw_rubberband(mode);
+    
+    if(rret!=NULL)
+        *rret=mode->geom;
+}
+
+
+void moveresmode_delta_resize(WMoveresMode *mode, 
+                              int dx1, int dx2, int dy1, int dy2,
+                              WRectangle *rret)
+{
+    mode->mode=MOVERES_SIZE;
+    moveresmode_delta(mode, dx1, dx2, dy1, dy2, rret);
+}
+
+
+void moveresmode_delta_move(WMoveresMode *mode, 
+                            int dx, int dy, WRectangle *rret)
+{
+    mode->mode=MOVERES_POS;
+    moveresmode_delta(mode, dx, dx, dy, dy, rret);
+}
+
+
+/* It is ugly to do this here, but it will have to do for now... */
+static void set_saved(WMoveresMode *mode, WRegion *reg)
+{
+    WFrame *frame;
+    
+    if(!OBJ_IS(reg, WFrame))
+        return;
+    
+    frame=(WFrame*)reg;
+    
+    /* Restore saved sizes from the beginning of the resize action */
+    if(mode->origgeom.w!=mode->geom.w){
+        frame->saved_x=mode->origgeom.x;
+        frame->saved_w=mode->origgeom.w;
+    }
+    
+    if(mode->origgeom.h!=mode->geom.h){
+        frame->saved_y=mode->origgeom.y;
+        frame->saved_h=mode->origgeom.h;
+    }
+}
+
+
+bool moveresmode_do_end(WMoveresMode *mode, bool apply)
+{
+    WRegion *reg=mode->reg;
+    
+    assert(reg!=NULL);
+    assert(tmpmode==mode);
+    
+    tmpmode=NULL;
+    
+    if(XOR_RESIZE){
+        moveres_draw_rubberband(mode);
+        if(apply){
+            WRQGeomParams rq=RQGEOMPARAMS_INIT;
+            
+            rq.geom=mode->geom;
+            rq.flags=mode->rqflags&~REGION_RQGEOM_TRYONLY;
+
+            region_rqgeom(reg, &rq, &mode->geom);
+        }
+        XUngrabServer(ioncore_g.dpy);
+    }
+    if(apply)
+        set_saved(mode, reg);
+    
+    if(mode->infowin!=NULL){
+        mainloop_defer_destroy((Obj*)mode->infowin);
+        mode->infowin=NULL;
+    }
+    destroy_obj((Obj*)mode);
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Request and other dynfuns */
+
+
+void region_rqgeom(WRegion *reg, const WRQGeomParams *rq,
+                   WRectangle *geomret)
+{
+    WRegion *mgr=REGION_MANAGER(reg);
+    
+    if(mgr!=NULL){
+        if(rq->flags&REGION_RQGEOM_ABSOLUTE)
+            region_managed_rqgeom_absolute(mgr, reg, rq, geomret);
+        else
+            region_managed_rqgeom(mgr, reg, rq, geomret);
+    }else{
+        WRectangle tmp;
+        
+        if(rq->flags&REGION_RQGEOM_ABSOLUTE)
+            region_absolute_geom_to_parent(reg, &rq->geom, &tmp);
+        else
+            tmp=rq->geom;
+        
+        if(geomret!=NULL)
+            *geomret=tmp;
+        
+        if(!(rq->flags&REGION_RQGEOM_TRYONLY))
+            region_fit(reg, &tmp, REGION_FIT_EXACT);
+    }
+}
+
+
+/*EXTL_DOC
+ * Attempt to resize and/or move \var{reg}. The table \var{g} is a usual
+ * geometry specification (fields \var{x}, \var{y}, \var{w} and \var{h}),
+ * but may contain missing fields, in which case, \var{reg}'s manager may
+ * attempt to leave that attribute unchanged.
+ */
+EXTL_EXPORT_AS(WRegion, rqgeom)
+ExtlTab region_rqgeom_extl(WRegion *reg, ExtlTab g)
+{
+    WRectangle ogeom=REGION_GEOM(reg);
+    WRQGeomParams rq=RQGEOMPARAMS_INIT;
+    
+    
+    rq.geom=ogeom;
+    rq.flags=REGION_RQGEOM_WEAK_ALL;
+    
+    
+    if(extl_table_gets_i(g, "x", &(rq.geom.x)))
+       rq.flags&=~REGION_RQGEOM_WEAK_X;
+    if(extl_table_gets_i(g, "y", &(rq.geom.y)))
+       rq.flags&=~REGION_RQGEOM_WEAK_Y;
+    if(extl_table_gets_i(g, "w", &(rq.geom.w)))
+       rq.flags&=~REGION_RQGEOM_WEAK_W;
+    if(extl_table_gets_i(g, "h", &(rq.geom.h)))
+       rq.flags&=~REGION_RQGEOM_WEAK_H;
+
+    rq.geom.w=maxof(1, rq.geom.w);
+    rq.geom.h=maxof(1, rq.geom.h);
+    
+    region_rqgeom(reg, &rq, &ogeom);
+    
+    return extl_table_from_rectangle(&ogeom);
+}
+
+
+void region_managed_rqgeom(WRegion *mgr, WRegion *reg,
+                           const WRQGeomParams *rq,
+                           WRectangle *geomret)
+{
+    CALL_DYN(region_managed_rqgeom, mgr, (mgr, reg, rq, geomret));
+}
+
+
+void region_managed_rqgeom_absolute(WRegion *mgr, WRegion *reg,
+                                    const WRQGeomParams *rq,
+                                    WRectangle *geomret)
+{
+    CALL_DYN(region_managed_rqgeom_absolute, mgr,
+             (mgr, reg, rq, geomret));
+}
+
+
+void region_managed_rqgeom_allow(WRegion *mgr, WRegion *reg,
+                                 const WRQGeomParams *rq,
+                                 WRectangle *geomret)
+{
+    if(geomret!=NULL)
+        *geomret=rq->geom;
+    
+    if(!(rq->flags&REGION_RQGEOM_TRYONLY))
+        region_fit(reg, &rq->geom, REGION_FIT_EXACT);
+}
+
+
+void region_managed_rqgeom_unallow(WRegion *mgr, WRegion *reg,
+                                   const WRQGeomParams *rq,
+                                   WRectangle *geomret)
+{
+    if(geomret!=NULL)
+        *geomret=REGION_GEOM(reg);
+}
+
+
+void region_managed_rqgeom_absolute_default(WRegion *mgr, WRegion *reg,
+                                            const WRQGeomParams *rq,
+                                            WRectangle *geomret)
+{
+    WRQGeomParams rq2=RQGEOMPARAMS_INIT;
+    
+    rq2.flags=rq->flags&~REGION_RQGEOM_ABSOLUTE;
+    region_absolute_geom_to_parent(reg, &rq->geom, &rq2.geom);
+    
+    region_managed_rqgeom(mgr, reg, &rq2, geomret);
+}
+
+
+void region_size_hints(WRegion *reg, WSizeHints *hints_ret)
+{
+    sizehints_clear(hints_ret);
+    
+    {
+        CALL_DYN(region_size_hints, reg, (reg, hints_ret));
+    }
+    
+    if(!hints_ret->min_set){
+        hints_ret->min_width=1;
+        hints_ret->min_height=1;
+    }
+    if(!hints_ret->base_set){
+        hints_ret->base_width=0;
+        hints_ret->base_height=0;
+    }
+    if(!hints_ret->max_set){
+        hints_ret->max_width=INT_MAX;
+        hints_ret->max_height=INT_MAX;
+    }
+}
+
+
+void region_size_hints_correct(WRegion *reg, 
+                               int *wp, int *hp, bool min)
+{
+    WSizeHints hints;
+    
+    region_size_hints(reg, &hints);
+    
+    sizehints_correct(&hints, wp, hp, min, FALSE);
+}
+
+
+int region_orientation(WRegion *reg)
+{
+    int ret=REGION_ORIENTATION_NONE;
+    
+    CALL_DYN_RET(ret, int, region_orientation, reg, (reg));
+    
+    return ret;
+}
+
+
+/*EXTL_DOC
+ * Returns size hints for \var{reg}. The returned table always contains the
+ * fields \code{min_?}, \code{base_?} and sometimes the fields \code{max_?},
+ * \code{base_?} and \code{inc_?}, where \code{?}=\code{w}, \code{h}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_AS(WRegion, size_hints)
+ExtlTab region_size_hints_extl(WRegion *reg)
+{
+    WSizeHints hints;
+    ExtlTab tab;
+    
+    region_size_hints(reg, &hints);
+    
+    tab=extl_create_table();
+    
+    if(hints.base_set){
+        extl_table_sets_i(tab, "base_w", hints.base_width);
+        extl_table_sets_i(tab, "base_h", hints.base_height);
+    }
+    if(hints.min_set){
+        extl_table_sets_i(tab, "min_w", hints.min_width);
+        extl_table_sets_i(tab, "min_h", hints.min_height);
+    }
+    if(hints.max_set){
+        extl_table_sets_i(tab, "max_w", hints.max_width);
+        extl_table_sets_i(tab, "max_h", hints.max_height);
+    }
+    if(hints.inc_set){
+        extl_table_sets_i(tab, "inc_w", hints.width_inc);
+        extl_table_sets_i(tab, "inc_h", hints.height_inc);
+    }
+    
+    return tab;
+}
+
+/*}}}*/
+
+
+/*{{{ Restore size, maximize, shade */
+
+
+static bool rqh(WFrame *frame, int y, int h)
+{
+    WRQGeomParams rq=RQGEOMPARAMS_INIT;
+    WRectangle rgeom;
+    int dummy_w;
+    
+    rq.flags=REGION_RQGEOM_VERT_ONLY;
+    
+    rq.geom.x=REGION_GEOM(frame).x;
+    rq.geom.w=REGION_GEOM(frame).w;
+    rq.geom.y=y;
+    rq.geom.h=h;
+    
+    dummy_w=rq.geom.w;
+    region_size_hints_correct((WRegion*)frame, &dummy_w, &(rq.geom.h), TRUE);
+    
+    region_rqgeom((WRegion*)frame, &rq, &rgeom);
+    
+    return (abs(rgeom.y-REGION_GEOM(frame).y)>1 ||
+            abs(rgeom.h-REGION_GEOM(frame).h)>1);
+}
+
+
+/*EXTL_DOC
+ * Attempt to toggle vertical maximisation of \var{frame}.
+ */
+EXTL_EXPORT_MEMBER
+void frame_maximize_vert(WFrame *frame)
+{
+    WRegion *mp=region_manager((WRegion*)frame);
+    int oy, oh;
+    
+    if(frame->flags&FRAME_SHADED || frame->flags&FRAME_MAXED_VERT){
+        if(frame->flags&FRAME_SAVED_VERT)
+            rqh(frame, frame->saved_y, frame->saved_h);
+        frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
+        return;
+    }
+
+    if(mp==NULL)
+        return;
+    
+    oy=REGION_GEOM(frame).y;
+    oh=REGION_GEOM(frame).h;
+    
+    rqh(frame, 0, REGION_GEOM(mp).h);
+
+    frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT);
+    frame->saved_y=oy;
+    frame->saved_h=oh;
+}
+
+static bool rqw(WFrame *frame, int x, int w)
+{
+    WRQGeomParams rq=RQGEOMPARAMS_INIT;
+    WRectangle rgeom;
+    int dummy_h;
+
+    rq.flags=REGION_RQGEOM_HORIZ_ONLY;
+    
+    rq.geom.x=x;
+    rq.geom.w=w;
+    rq.geom.y=REGION_GEOM(frame).y;
+    rq.geom.h=REGION_GEOM(frame).h;
+    
+    dummy_h=rq.geom.h;
+    region_size_hints_correct((WRegion*)frame, &(rq.geom.w), &dummy_h, TRUE);
+    
+    region_rqgeom((WRegion*)frame, &rq, &rgeom);
+    
+    return (abs(rgeom.x-REGION_GEOM(frame).x)>1 ||
+            abs(rgeom.w-REGION_GEOM(frame).w)>1);
+}
+
+
+/*EXTL_DOC
+ * Attempt to toggle horizontal maximisation of \var{frame}.
+ */
+EXTL_EXPORT_MEMBER
+void frame_maximize_horiz(WFrame *frame)
+{
+    WRegion *mp=region_manager((WRegion*)frame);
+    int ox, ow;
+    
+    if(frame->flags&FRAME_MIN_HORIZ || frame->flags&FRAME_MAXED_HORIZ){
+        if(frame->flags&FRAME_SAVED_HORIZ)
+            rqw(frame, frame->saved_x, frame->saved_w);
+        frame->flags&=~(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
+        return;
+    }
+
+    if(mp==NULL)
+        return;
+    
+    ox=REGION_GEOM(frame).x;
+    ow=REGION_GEOM(frame).w;
+    
+    rqw(frame, 0, REGION_GEOM(mp).w);
+        
+    frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ);
+    frame->saved_x=ox;
+    frame->saved_w=ow;
+}
+
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+uint region_min_h(WRegion *reg)
+{
+    WSizeHints hints;
+    region_size_hints(reg, &hints);
+    return hints.min_height;
+}
+
+
+uint region_min_w(WRegion *reg)
+{
+    WSizeHints hints;
+    region_size_hints(reg, &hints);
+    return hints.min_width;
+}
+
+
+void region_convert_root_geom(WRegion *reg, WRectangle *geom)
+{
+    int rx, ry;
+    if(reg!=NULL){
+        region_rootpos(reg, &rx, &ry);
+        geom->x-=rx;
+        geom->y-=ry;
+    }
+}
+
+
+void region_absolute_geom_to_parent(WRegion *reg, const WRectangle *rgeom,
+                                    WRectangle *pgeom)
+{
+    WRegion *parent=REGION_PARENT_REG(reg);
+    
+    pgeom->w=rgeom->w;
+    pgeom->h=rgeom->h;
+    
+    if(parent==NULL){
+        pgeom->x=rgeom->x;
+        pgeom->y=rgeom->y;
+    }else{
+        region_rootpos(reg, &pgeom->x, &pgeom->y);
+        pgeom->x=rgeom->x-pgeom->x;
+        pgeom->y=rgeom->y-pgeom->y;
+    }
+}
+
+/*}}}*/
+
diff --git a/ioncore/resize.h b/ioncore/resize.h
new file mode 100644 (file)
index 0000000..3a4f062
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * ion/ioncore/resize.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_RESIZE_H
+#define ION_IONCORE_RESIZE_H
+
+#include "common.h"
+#include "frame.h"
+#include "infowin.h"
+#include "rectangle.h"
+#include "sizehint.h"
+
+
+/* To make it easier for region_managed_rqgeom handlers, the geom
+ * parameter contain a complete requested geometry for the region that
+ * wants its geometry changed. The REGION_WEAK_* flags are used to
+ * indicate that the respective geometry value has not been changed or
+ * that the requestor doesn't really care what the result is. In any case,
+ * managers are free to give the managed object whatever geometry it wishes.
+ */
+#define REGION_RQGEOM_WEAK_X    0x0001
+#define REGION_RQGEOM_WEAK_Y    0x0002
+#define REGION_RQGEOM_WEAK_W    0x0004
+#define REGION_RQGEOM_WEAK_H    0x0008
+#define REGION_RQGEOM_WEAK_ALL  0x000f
+#define REGION_RQGEOM_TRYONLY   0x0010
+#define REGION_RQGEOM_ABSOLUTE  0x0020
+#define REGION_RQGEOM_GRAVITY   0x0040
+
+#define REGION_RQGEOM_NORMAL    0
+#define REGION_RQGEOM_VERT_ONLY (REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_W)
+#define REGION_RQGEOM_HORIZ_ONLY (REGION_RQGEOM_WEAK_Y|REGION_RQGEOM_WEAK_H)
+#define REGION_RQGEOM_H_ONLY    (REGION_RQGEOM_VERT_ONLY|REGION_RQGEOM_WEAK_Y)
+#define REGION_RQGEOM_W_ONLY    (REGION_RQGEOM_HORIZ_ONLY|REGION_RQGEOM_WEAK_X)
+
+#define REGION_ORIENTATION_NONE 0
+#define REGION_ORIENTATION_HORIZONTAL 1
+#define REGION_ORIENTATION_VERTICAL 2
+
+
+#define RQGEOMPARAMS_INIT {{0, 0, 0, 0}, 0, 0}
+
+
+DECLSTRUCT(WRQGeomParams){
+    WRectangle geom;
+    int flags;
+    int gravity;
+};
+
+
+typedef void WDrawRubberbandFn(WRootWin *rw, const WRectangle *geom);
+
+
+DECLCLASS(WMoveresMode){
+    Obj obj;
+    WSizeHints hints;
+    int dx1, dx2, dy1, dy2;
+    WRectangle origgeom;
+    WRectangle geom;
+    WRegion *reg;
+    WDrawRubberbandFn *rubfn;
+    int parent_rx, parent_ry;
+    enum {MOVERES_SIZE, MOVERES_POS} mode;
+    bool resize_cumulative;
+    bool snap_enabled;
+    WRectangle snapgeom;
+    int rqflags;
+    WInfoWin *infowin;
+};
+
+
+extern WMoveresMode *region_begin_resize(WRegion *reg, 
+                                         WDrawRubberbandFn *rubfn,
+                                         bool cumulative);
+
+extern WMoveresMode *region_begin_move(WRegion *reg,
+                                       WDrawRubberbandFn *rubfn,
+                                       bool cumulative);
+
+/* dx1/dx2/dy1/dy2: left/right/top/bottom difference to previous values. 
+ * left/top negative, bottom/right positive increases size.
+ */
+extern void moveresmode_delta_resize(WMoveresMode *mode, 
+                                     int dx1, int dx2, int dy1, int dy2,
+                                     WRectangle *rret);
+extern void moveresmode_delta_move(WMoveresMode *mode, 
+                                   int dx, int dy, WRectangle *rret);
+extern bool moveresmode_do_end(WMoveresMode *mode, bool apply);
+extern WRegion *moveresmode_target(WMoveresMode *mode);
+
+extern WMoveresMode *moveres_mode(WRegion *reg);
+
+
+
+/* Note: even if REGION_RQGEOM_(X|Y|W|H) are not all specified, the
+ * geom parameter should contain a proper geometry!
+ */
+DYNFUN void region_managed_rqgeom(WRegion *reg, WRegion *sub, 
+                                  const WRQGeomParams *rqgp,
+                                  WRectangle *geomret);
+DYNFUN void region_managed_rqgeom_absolute(WRegion *reg, WRegion *sub, 
+                                           const WRQGeomParams *rqgp,
+                                           WRectangle *geomret);
+
+extern void region_rqgeom(WRegion *reg, const WRQGeomParams *rqgp,
+                          WRectangle *geomret);
+
+/* Implementation for regions that do not allow subregions to resize
+ * themselves; default is to give the size the region wants.
+ */
+extern void region_managed_rqgeom_unallow(WRegion *reg, WRegion *sub,
+                                          const WRQGeomParams *rq,
+                                          WRectangle *geomret);
+/* default */
+extern void region_managed_rqgeom_allow(WRegion *reg, WRegion *sub,
+                                        const WRQGeomParams *rq,
+                                        WRectangle *geomret);
+
+extern void region_managed_rqgeom_absolute_default(WRegion *reg, WRegion *sub,
+                                                   const WRQGeomParams *rq,
+                                                   WRectangle *geomret);
+
+
+DYNFUN void region_size_hints(WRegion *reg, WSizeHints *hints_ret);
+DYNFUN int region_orientation(WRegion *reg);
+
+extern void region_size_hints_correct(WRegion *reg, 
+                                      int *wp, int *hp, bool min);
+
+extern uint region_min_h(WRegion *reg);
+extern uint region_min_w(WRegion *reg);
+
+extern void frame_maximize_vert(WFrame *frame);
+extern void frame_maximize_horiz(WFrame *frame);
+
+extern void region_convert_root_geom(WRegion *reg, WRectangle *geom);
+
+extern void region_absolute_geom_to_parent(WRegion *reg, 
+                                           const WRectangle *rgeom,
+                                           WRectangle *pgeom);
+
+#endif /* ION_IONCORE_RESIZE_H */
diff --git a/ioncore/rootwin.c b/ioncore/rootwin.c
new file mode 100644 (file)
index 0000000..5fc4891
--- /dev/null
@@ -0,0 +1,625 @@
+/*
+ * ion/ioncore/rootwin.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xproto.h>
+/*#include <X11/Xmu/Error.h>*/
+#ifdef CF_XINERAMA
+#include <X11/extensions/Xinerama.h>
+#elif defined(CF_SUN_XINERAMA)
+#include <X11/extensions/xinerama.h>
+#endif
+
+#include <libtu/objp.h>
+#include "common.h"
+#include "rootwin.h"
+#include "cursor.h"
+#include "global.h"
+#include "event.h"
+#include "gr.h"
+#include "clientwin.h"
+#include "property.h"
+#include "focus.h"
+#include "regbind.h"
+#include "screen.h"
+#include "screen.h"
+#include "bindmaps.h"
+#include <libextl/readconfig.h>
+#include "resize.h"
+#include "saveload.h"
+#include "netwm.h"
+#include "xwindow.h"
+
+
+/*{{{ Error handling */
+
+
+static bool redirect_error=FALSE;
+static bool ignore_badwindow=TRUE;
+
+
+static int my_redirect_error_handler(Display *dpy, XErrorEvent *ev)
+{
+    redirect_error=TRUE;
+    return 0;
+}
+
+
+static int my_error_handler(Display *dpy, XErrorEvent *ev)
+{
+    static char msg[128], request[64], num[32];
+    
+    /* Just ignore bad window and similar errors; makes the rest of
+     * the code simpler.
+     */
+    if((ev->error_code==BadWindow ||
+        (ev->error_code==BadMatch && ev->request_code==X_SetInputFocus) ||
+        (ev->error_code==BadDrawable && ev->request_code==X_GetGeometry)) &&
+       ignore_badwindow)
+        return 0;
+
+#if 0
+    XmuPrintDefaultErrorMessage(dpy, ev, stderr);
+#else
+    XGetErrorText(dpy, ev->error_code, msg, 128);
+    snprintf(num, 32, "%d", ev->request_code);
+    XGetErrorDatabaseText(dpy, "XRequest", num, "", request, 64);
+
+    if(request[0]=='\0')
+        snprintf(request, 64, "<unknown request>");
+
+    if(ev->minor_code!=0){
+        warn("[%d] %s (%d.%d) %#lx: %s", ev->serial, request,
+             ev->request_code, ev->minor_code, ev->resourceid,msg);
+    }else{
+        warn("[%d] %s (%d) %#lx: %s", ev->serial, request,
+             ev->request_code, ev->resourceid,msg);
+    }
+#endif
+
+    kill(getpid(), SIGTRAP);
+    
+    return 0;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init/deinit */
+
+
+static void scan_initial_windows(WRootWin *rootwin)
+{
+    Window dummy_root, dummy_parent, *wins=NULL;
+    uint nwins=0, i, j;
+    XWMHints *hints;
+    
+    XQueryTree(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), &dummy_root, &dummy_parent,
+               &wins, &nwins);
+    
+    for(i=0; i<nwins; i++){
+        if(wins[i]==None)
+            continue;
+        hints=XGetWMHints(ioncore_g.dpy, wins[i]);
+        if(hints!=NULL && hints->flags&IconWindowHint){
+            for(j=0; j<nwins; j++){
+                if(wins[j]==hints->icon_window){
+                    wins[j]=None;
+                    break;
+                }
+            }
+        }
+        if(hints!=NULL)
+            XFree((void*)hints);
+    }
+    
+    rootwin->tmpwins=wins;
+    rootwin->tmpnwins=nwins;
+}
+
+
+void rootwin_manage_initial_windows(WRootWin *rootwin)
+{
+    Window *wins=rootwin->tmpwins;
+    Window tfor=None;
+    int i, nwins=rootwin->tmpnwins;
+
+    rootwin->tmpwins=NULL;
+    rootwin->tmpnwins=0;
+    
+    for(i=0; i<nwins; i++){
+        if(XWINDOW_REGION_OF(wins[i])!=NULL)
+            wins[i]=None;
+        if(wins[i]==None)
+            continue;
+        if(XGetTransientForHint(ioncore_g.dpy, wins[i], &tfor))
+            continue;
+        ioncore_manage_clientwin(wins[i], FALSE);
+        wins[i]=None;
+    }
+
+    for(i=0; i<nwins; i++){
+        if(wins[i]==None)
+            continue;
+        ioncore_manage_clientwin(wins[i], FALSE);
+    }
+    
+    XFree((void*)wins);
+}
+
+
+static void create_wm_windows(WRootWin *rootwin)
+{
+    rootwin->dummy_win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(rootwin),
+                                     0, 0, 1, 1, 0,
+                                     CopyFromParent, InputOnly,
+                                     CopyFromParent, 0, NULL);
+
+    XSelectInput(ioncore_g.dpy, rootwin->dummy_win, PropertyChangeMask);
+}
+
+
+static void preinit_gr(WRootWin *rootwin)
+{
+    XGCValues gcv;
+    ulong gcvmask;
+
+    /* Create XOR gc (for resize) */
+    gcv.line_style=LineSolid;
+    gcv.join_style=JoinBevel;
+    gcv.cap_style=CapButt;
+    gcv.fill_style=FillSolid;
+    gcv.line_width=2;
+    gcv.subwindow_mode=IncludeInferiors;
+    gcv.function=GXxor;
+    gcv.foreground=~0L;
+    
+    gcvmask=(GCLineStyle|GCLineWidth|GCFillStyle|
+             GCJoinStyle|GCCapStyle|GCFunction|
+             GCSubwindowMode|GCForeground);
+
+    rootwin->xor_gc=XCreateGC(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), 
+                              gcvmask, &gcv);
+}
+
+
+static WRootWin *preinit_rootwin(int xscr)
+{
+    Display *dpy=ioncore_g.dpy;
+    WRootWin *rootwin;
+    WFitParams fp;
+    Window root;
+    int i;
+    
+    /* Try to select input on the root window */
+    root=RootWindow(dpy, xscr);
+    
+    redirect_error=FALSE;
+
+    XSetErrorHandler(my_redirect_error_handler);
+    XSelectInput(dpy, root, IONCORE_EVENTMASK_ROOT);
+    XSync(dpy, 0);
+    XSetErrorHandler(my_error_handler);
+
+    if(redirect_error){
+        warn(TR("Unable to redirect root window events for screen %d."),
+             xscr);
+        return NULL;
+    }
+    
+    rootwin=ALLOC(WRootWin);
+    
+    if(rootwin==NULL)
+        return NULL;
+    
+    /* Init the struct */
+    OBJ_INIT(rootwin, WRootWin);
+
+    rootwin->xscr=xscr;
+    rootwin->default_cmap=DefaultColormap(dpy, xscr);
+    rootwin->tmpwins=NULL;
+    rootwin->tmpnwins=0;
+    rootwin->dummy_win=None;
+    rootwin->xor_gc=None;
+
+    fp.mode=REGION_FIT_EXACT;
+    fp.g.x=0; fp.g.y=0;
+    fp.g.w=DisplayWidth(dpy, xscr);
+    fp.g.h=DisplayHeight(dpy, xscr);
+    
+    if(!window_do_init((WWindow*)rootwin, NULL, root, &fp)){
+        free(rootwin);
+        return NULL;
+    }
+
+    /* Note: this mask isn't right if some WScreen auses the same window. */
+    ((WWindow*)rootwin)->event_mask=IONCORE_EVENTMASK_ROOT;
+    
+    ((WRegion*)rootwin)->flags|=REGION_BINDINGS_ARE_GRABBED|REGION_PLEASE_WARP;
+    ((WRegion*)rootwin)->rootwin=rootwin;
+    
+    REGION_MARK_MAPPED(rootwin);
+    
+    scan_initial_windows(rootwin);
+
+    create_wm_windows(rootwin);
+    preinit_gr(rootwin);
+    netwm_init_rootwin(rootwin);
+    
+    region_add_bindmap((WRegion*)rootwin, ioncore_rootwin_bindmap);
+    
+    return rootwin;
+}
+
+
+static Atom net_virtual_roots=None;
+
+
+static WScreen *add_screen(WRootWin *rw, int id, const WRectangle *geom, 
+                           bool useroot)
+{
+    WScreen *scr;
+    CARD32 p[1];
+    WFitParams fp;
+    
+    fp.g=*geom;
+    fp.mode=REGION_FIT_EXACT;
+    
+#ifdef CF_ALWAYS_VIRTUAL_ROOT
+    useroot=FALSE;
+#endif
+
+    scr=create_screen(rw, id, &fp, useroot);
+    
+    if(scr==NULL)
+        return NULL;
+    
+    region_set_manager((WRegion*)scr, (WRegion*)rw);
+    
+    region_map((WRegion*)scr);
+
+    if(!useroot){
+        p[0]=region_xwindow((WRegion*)scr);
+        XChangeProperty(ioncore_g.dpy, WROOTWIN_ROOT(rw), net_virtual_roots,
+                        XA_WINDOW, 32, PropModeAppend, (uchar*)&(p[0]), 1);
+    }
+
+    return scr;
+}
+
+
+#ifdef CF_XINERAMA
+static bool xinerama_sanity_check(XineramaScreenInfo *xi, int nxi)
+{
+    int i, j;
+
+    for(i=0; i<nxi; i++){
+        for(j=0; j<nxi; j++){
+            if(i!=j &&
+               (xi[j].x_org>=xi[i].x_org && xi[j].x_org<xi[i].x_org+xi[i].width) &&
+               (xi[j].y_org>=xi[i].y_org && xi[j].y_org<xi[i].y_org+xi[i].height)){
+                warn(TR("Xinerama sanity check failed; overlapping "
+                        "screens detected."));
+                return FALSE;
+            }
+        }
+        
+        if(xi[i].width<=0 || xi[i].height<=0){
+            warn(TR("Xinerama sanity check failed; zero size detected."));
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+#elif defined(CF_SUN_XINERAMA)
+static bool xinerama_sanity_check(XRectangle *monitors, int nxi)
+{
+    int i, j;
+
+    for(i=0; i<nxi; i++){
+        for(j=0; j<nxi; j++){
+            if(i!=j &&
+               (monitors[j].x>=monitors[i].x &&
+                monitors[j].x<monitors[i].x+monitors[i].width) &&
+               (monitors[j].y>=monitors[i].y &&
+                monitors[j].y<monitors[i].y+monitors[i].height)){
+                warn(TR("Xinerama sanity check failed; overlapping "
+                        "screens detected."));
+                return FALSE;
+            }
+        }
+        
+        if(monitors[i].width<=0 || monitors[i].height<=0){
+            warn(TR("Xinerama sanity check failed; zero size detected."));
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+#endif
+
+
+WRootWin *ioncore_manage_rootwin(int xscr, bool noxinerama)
+{
+    WRootWin *rootwin;
+    int nxi=0, fail=0;
+#ifdef CF_XINERAMA
+    XineramaScreenInfo *xi=NULL;
+    int i;
+    int event_base, error_base;
+#elif defined(CF_SUN_XINERAMA)
+    XRectangle monitors[MAXFRAMEBUFFERS];
+    int i;
+#endif
+
+    if(!noxinerama){
+#ifdef CF_XINERAMA
+        if(XineramaQueryExtension(ioncore_g.dpy, &event_base, &error_base)){
+            xi=XineramaQueryScreens(ioncore_g.dpy, &nxi);
+
+            if(xi!=NULL && ioncore_g.rootwins!=NULL){
+                warn(TR("Don't know how to get Xinerama information for "
+                        "multiple X root windows."));
+                XFree(xi);
+                xi=NULL;
+                nxi=0;
+            }
+        }
+#elif defined(CF_SUN_XINERAMA)
+        if(XineramaGetState(ioncore_g.dpy, xscr)){
+            unsigned char hints[16];
+            int num;
+
+            if(XineramaGetInfo(ioncore_g.dpy, xscr, monitors, hints,
+                               &nxi)==0){
+                warn(TR("Error retrieving Xinerama information."));
+                nxi=0;
+            }else{
+                if(ioncore_g.rootwins!=NULL){
+                    warn(TR("Don't know how to get Xinerama information for "
+                            "multiple X root windows."));
+                    nxi=0;
+                }
+            }
+        }
+#endif
+    }
+
+    rootwin=preinit_rootwin(xscr);
+
+    if(rootwin==NULL){
+#ifdef CF_XINERAMA
+        if(xi!=NULL)
+            XFree(xi);
+#endif
+        return NULL;
+    }
+
+    net_virtual_roots=XInternAtom(ioncore_g.dpy, "_NET_VIRTUAL_ROOTS", False);
+    XDeleteProperty(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), net_virtual_roots);
+
+#ifdef CF_XINERAMA
+    if(xi!=NULL && nxi!=0 && xinerama_sanity_check(xi, nxi)){
+        bool useroot=FALSE;
+        WRectangle geom;
+
+        for(i=0; i<nxi; i++){
+            geom.x=xi[i].x_org;
+            geom.y=xi[i].y_org;
+            geom.w=xi[i].width;
+            geom.h=xi[i].height;
+            /*if(nxi==1)
+                useroot=(geom.x==0 && geom.y==0);*/
+            if(!add_screen(rootwin, i, &geom, useroot)){
+                warn(TR("Unable to setup Xinerama screen %d."), i);
+                fail++;
+            }
+        }
+        XFree(xi);
+    }else
+#elif defined(CF_SUN_XINERAMA)
+    if(nxi!=0 && xinerama_sanity_check(monitors, nxi)){
+        bool useroot=FALSE;
+        WRectangle geom;
+
+        for(i=0; i<nxi; i++){
+            geom.x=monitors[i].x;
+            geom.y=monitors[i].y;
+            geom.w=monitors[i].width;
+            geom.h=monitors[i].height;
+            /*if(nxi==1)
+                useroot=(geom.x==0 && geom.y==0);*/
+            if(!add_screen(rootwin, i, &geom, useroot)){
+                warn(TR("Unable to setup Xinerama screen %d."), i);
+                fail++;
+            }
+        }
+    }else
+#endif
+    {
+        nxi=1;
+        if(!add_screen(rootwin, xscr, &REGION_GEOM(rootwin), TRUE))
+            fail++;
+    }
+    
+    if(fail==nxi){
+        warn(TR("Unable to setup X screen %d."), xscr);
+        destroy_obj((Obj*)rootwin);
+        return NULL;
+    }
+    
+    /* */ {
+        /* TODO: typed LINK_ITEM */
+        WRegion *tmp=(WRegion*)ioncore_g.rootwins;
+        LINK_ITEM(tmp, (WRegion*)rootwin, p_next, p_prev);
+        ioncore_g.rootwins=(WRootWin*)tmp;
+    }
+
+    xwindow_set_cursor(WROOTWIN_ROOT(rootwin), IONCORE_CURSOR_DEFAULT);
+    
+    return rootwin;
+}
+
+
+void rootwin_deinit(WRootWin *rw)
+{
+    WScreen *scr, *next;
+
+    FOR_ALL_SCREENS_W_NEXT(scr, next){
+        if(REGION_MANAGER(scr)==(WRegion*)rw)
+            destroy_obj((Obj*)scr);
+    }
+    
+    /* */ {
+        WRegion *tmp=(WRegion*)ioncore_g.rootwins;
+        UNLINK_ITEM(tmp, (WRegion*)rw, p_next, p_prev);
+        ioncore_g.rootwins=(WRootWin*)tmp;
+    }
+    
+    XSelectInput(ioncore_g.dpy, WROOTWIN_ROOT(rw), 0);
+    
+    XFreeGC(ioncore_g.dpy, rw->xor_gc);
+    
+    window_deinit((WWindow*)rw);
+}
+
+
+/*}}}*/
+
+
+/*{{{ region dynfun implementations */
+
+
+static void rootwin_do_set_focus(WRootWin *rootwin, bool warp)
+{
+    WRegion *sub;
+    
+    sub=REGION_ACTIVE_SUB(rootwin);
+    
+    if(sub==NULL || !REGION_IS_MAPPED(sub)){
+        WScreen *scr;
+        FOR_ALL_SCREENS(scr){
+            if(REGION_IS_MAPPED(scr)){
+                sub=(WRegion*)scr;
+                break;
+            }
+        }
+    }
+
+    if(sub!=NULL)
+        region_do_set_focus(sub, warp);
+    else
+        window_do_set_focus((WWindow*)rootwin, warp);
+}
+
+
+static bool rootwin_fitrep(WRootWin *rootwin, WWindow *par, 
+                           const WFitParams *fp)
+{
+    D(warn("Don't know how to reparent or fit root windows."));
+    return FALSE;
+}
+
+
+static void rootwin_map(WRootWin *rootwin)
+{
+    D(warn("Attempt to map a root window."));
+}
+
+
+static void rootwin_unmap(WRootWin *rootwin)
+{
+    D(warn("Attempt to unmap a root window -- impossible."));
+}
+
+
+static void rootwin_managed_remove(WRootWin *rootwin, WRegion *reg)
+{
+    region_unset_manager(reg, (WRegion*)rootwin);
+}
+
+
+static Window rootwin_x_window(WRootWin *rootwin)
+{
+    return WROOTWIN_ROOT(rootwin);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc */
+
+
+static bool scr_ok(WRegion *r)
+{
+    return (OBJ_IS(r, WScreen) && REGION_IS_MAPPED(r));
+}
+
+
+/*EXTL_DOC
+ * Returns previously active screen on root window \var{rootwin}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WScreen *rootwin_current_scr(WRootWin *rootwin)
+{
+    WRegion *r=REGION_ACTIVE_SUB(rootwin);
+    WScreen *scr;
+    
+    /* There should be no non-WScreen as children or managed by us, but... */
+    
+    if(r!=NULL && scr_ok(r))
+        return (WScreen*)r;
+    
+    FOR_ALL_SCREENS(scr){
+        if(REGION_MANAGER(scr)==(WRegion*)rootwin
+           && REGION_IS_MAPPED(scr)){
+            break;
+        }
+    }
+    
+    return scr;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab rootwin_dynfuntab[]={
+    {region_map, rootwin_map},
+    {region_unmap, rootwin_unmap},
+    {region_do_set_focus, rootwin_do_set_focus},
+    {(DynFun*)region_xwindow, (DynFun*)rootwin_x_window},
+    {(DynFun*)region_fitrep, (DynFun*)rootwin_fitrep},
+    {region_managed_remove, rootwin_managed_remove},
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WRootWin, WWindow, rootwin_deinit, rootwin_dynfuntab);
+
+    
+/*}}}*/
diff --git a/ioncore/rootwin.h b/ioncore/rootwin.h
new file mode 100644 (file)
index 0000000..41dd89c
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * ion/ioncore/rootwin.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_ROOTWIN_H
+#define ION_IONCORE_ROOTWIN_H
+
+#include "common.h"
+#include "window.h"
+#include "screen.h"
+#include "gr.h"
+#include "rectangle.h"
+
+#define WROOTWIN_ROOT(X) ((X)->wwin.win)
+#define FOR_ALL_ROOTWINS(RW)         \
+    for(RW=ioncore_g.rootwins;         \
+        RW!=NULL;                    \
+        RW=OBJ_CAST(((WRegion*)RW)->p_next, WRootWin))
+
+
+DECLCLASS(WRootWin){
+    WWindow wwin;
+    int xscr;
+    
+    Colormap default_cmap;
+    
+    Window *tmpwins;
+    int tmpnwins;
+    
+    Window dummy_win;
+    
+    GC xor_gc;
+};
+
+
+extern void rootwin_deinit(WRootWin *rootwin);
+extern WScreen *rootwin_current_scr(WRootWin *rootwin);
+
+extern void rootwin_manage_initial_windows(WRootWin *rootwin);
+extern WRootWin *ioncore_manage_rootwin(int xscr, bool noxinerama);
+
+#endif /* ION_IONCORE_ROOTWIN_H */
+
diff --git a/ioncore/saveload.c b/ioncore/saveload.c
new file mode 100644 (file)
index 0000000..e5902b3
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * ion/ioncore/saveload.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <libtu/objp.h>
+#include <libextl/readconfig.h>
+#include <libextl/extl.h>
+
+#include "common.h"
+#include "global.h"
+#include "region.h"
+#include "screen.h"
+#include "saveload.h"
+#include "names.h"
+#include "attach.h"
+#include "reginfo.h"
+#include "extlconv.h"
+#include "group-ws.h"
+
+
+static bool loading_layout=FALSE;
+static bool layout_load_error=FALSE;
+
+
+/*{{{ Session management module support */
+
+
+static SMAddCallback *add_cb;
+static SMCfgCallback *cfg_cb;
+static SMPHolderCallback *ph_cb;
+static bool clientwin_was_missing=FALSE;
+
+
+void ioncore_set_sm_callbacks(SMAddCallback *add, SMCfgCallback *cfg)
+{
+    add_cb=add;
+    cfg_cb=cfg;
+}
+
+
+void ioncore_get_sm_callbacks(SMAddCallback **add, SMCfgCallback **cfg)
+{
+    *add=add_cb;
+    *cfg=cfg_cb;
+}
+
+
+void ioncore_set_sm_pholder_callback(SMPHolderCallback *phcb)
+{
+    ph_cb=phcb;
+}
+
+
+void ioncore_clientwin_load_missing()
+{
+    clientwin_was_missing=TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Load support functions */
+
+
+WRegion *create_region_load(WWindow *par, const WFitParams *fp, 
+                            ExtlTab tab)
+{
+    char *objclass=NULL, *name=NULL;
+    WRegionLoadCreateFn* fn=NULL;
+    WRegClassInfo *info=NULL;
+    WRegion *reg=NULL;
+    bool grouped=FALSE;
+    char *grouped_name=NULL;
+    
+    if(!extl_table_gets_s(tab, "type", &objclass))
+        return NULL;
+        
+    /* Backwards compatibility hack. */
+    if(strcmp(objclass, "WFloatWS")==0){
+        objclass=scopy("WGroupWS");
+    }else if(strcmp(objclass, "WIonWS")==0){
+        WGroupWS *ws=create_groupws(par, fp);
+        if(ws!=NULL){
+            extl_table_gets_s(tab, "name", &name);
+            extl_table_sets_s(tab, "type", "WTiling");
+            extl_table_sets_b(tab, "bottom", TRUE);
+            extl_table_sets_b(tab, "bottom_last_close", TRUE);
+            extl_table_sets_i(tab, "sizepolicy", SIZEPOLICY_FULL_EXACT);
+            
+            if(name!=NULL)
+                region_set_name((WRegion*)ws, name);
+            
+            reg=group_attach_new((WGroup*)ws, tab);
+            
+            if(reg!=NULL)
+                return (WRegion*)ws;
+            
+            destroy_obj((Obj*)ws);
+        }
+        objclass=scopy("WTiling");
+    }else if(strcmp(objclass, "WFloatFrame")==0){
+        objclass=scopy("WFrame");
+    }
+    
+    if(objclass==NULL)
+        return NULL;
+    
+    info=ioncore_lookup_regclass(objclass, FALSE);
+    if(info!=NULL)
+        fn=info->lc_fn;
+    
+    if(fn==NULL){
+        warn(TR("Unknown class \"%s\", cannot create region."),
+             objclass);
+        layout_load_error=loading_layout;
+        return NULL;
+    }
+
+    free(objclass);
+    
+    clientwin_was_missing=FALSE;
+    
+    reg=fn(par, fp, tab);
+    
+    if(reg==NULL){
+        if(clientwin_was_missing && add_cb!=NULL && ph_cb!=NULL){
+            WPHolder *ph=ph_cb();
+            if(ph!=NULL){
+                if(!add_cb(ph, tab))
+                    destroy_obj((Obj*)ph);
+            }
+        }
+    }else{
+        if(!OBJ_IS(reg, WClientWin)){
+            if(extl_table_gets_s(tab, "name", &name)){
+                region_set_name(reg, name);
+                free(name);
+            }
+        }
+    }
+    
+    ph_cb=NULL;
+    
+    return reg;
+}
+
+    
+/*}}}*/
+
+
+/*{{{ Save support functions */
+
+
+bool region_supports_save(WRegion *reg)
+{
+    return HAS_DYN(reg, region_get_configuration);
+}
+
+
+ExtlTab region_get_base_configuration(WRegion *reg)
+{
+    const char *name;
+    ExtlTab tab;
+    
+    tab=extl_create_table();
+    
+    extl_table_sets_s(tab, "type", OBJ_TYPESTR(reg));
+    
+    name=region_name(reg);
+    
+    if(name!=NULL && !OBJ_IS(reg, WClientWin))
+        extl_table_sets_s(tab, "name", name);
+    
+    return tab;
+}
+
+
+ExtlTab region_get_configuration(WRegion *reg)
+{
+    ExtlTab tab=extl_table_none();
+    CALL_DYN_RET(tab, ExtlTab, region_get_configuration, reg, (reg));
+    return tab;
+}
+
+
+/*}}}*/
+
+
+/*{{{ save_workspaces, load_workspaces */
+
+
+static const char backup_msg[]=DUMMY_TR(
+"There were errors loading layout. Backing up current layout savefile as\n"
+"%s.\n"
+"If you are _not_ running under a session manager and wish to restore your\n"
+"old layout, copy this backup file over the layout savefile found in the\n"
+"same directory while Ion is not running and after having fixed your other\n"
+"configuration files that are causing this problem. (Maybe a missing\n"
+"module?)");
+
+
+bool ioncore_init_layout()
+{
+    ExtlTab tab;
+    WScreen *scr;
+    bool ok;
+    int n=0;
+    
+    ok=extl_read_savefile("saved_layout", &tab);
+    
+    loading_layout=TRUE;
+    layout_load_error=FALSE;
+    
+    FOR_ALL_SCREENS(scr){
+        ExtlTab scrtab=extl_table_none();
+        bool scrok=FALSE;
+        if(ok)
+            scrok=extl_table_geti_t(tab, screen_id(scr), &scrtab);
+        
+        n+=(TRUE==screen_init_layout(scr, scrtab));
+        
+        if(scrok)
+            extl_unref_table(scrtab);
+    }
+
+    loading_layout=FALSE;
+
+    if(layout_load_error){
+        time_t t=time(NULL);
+        char tm[]="saved_layout.backup-YYYYMMDDHHMMSS\0\0\0\0";
+        char *backup;
+        
+        strftime(tm+20, 15, "%Y%m%d%H%M%S", localtime(&t));
+        backup=extl_get_savefile(tm);
+        if(backup==NULL){
+            warn(TR("Unable to get file for layout backup."));
+            return FALSE;
+        }
+        if(access(backup, F_OK)==0){
+            warn(TR("Backup file %s already exists."), backup);
+            free(backup);
+            return FALSE;
+        }
+        warn(TR(backup_msg), backup);
+        if(!extl_serialise(backup, tab))
+            warn(TR("Failed backup."));
+        free(backup);
+    }
+        
+    if(n==0){
+        warn(TR("Unable to initialise layout on any screen."));
+        return FALSE;
+    }else{
+        return TRUE;
+    }
+}
+
+
+bool ioncore_save_layout()
+{
+    WScreen *scr=NULL;
+    ExtlTab tab=extl_create_table();
+    bool ret;
+    
+    if(tab==extl_table_none())
+        return FALSE;
+    
+    FOR_ALL_SCREENS(scr){
+        ExtlTab scrtab=region_get_configuration((WRegion*)scr);
+        if(scrtab==extl_table_none()){
+            warn(TR("Unable to get configuration for screen %d."),
+                 screen_id(scr));
+        }else{
+            extl_table_seti_t(tab, screen_id(scr), scrtab);
+            extl_unref_table(scrtab);
+        }
+    }
+    
+    ret=extl_write_savefile("saved_layout", tab);
+    
+    extl_unref_table(tab);
+    
+    if(!ret)
+        warn(TR("Unable to save layout."));
+        
+    return ret;
+}
+
+
+/*}}}*/
+
+
diff --git a/ioncore/saveload.h b/ioncore/saveload.h
new file mode 100644 (file)
index 0000000..27e3b3e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * ion/ioncore/saveload.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_SAVELOAD_H
+#define ION_IONCORE_SAVELOAD_H
+
+#include <libextl/extl.h>
+#include "common.h"
+#include "region.h"
+#include "screen.h"
+#include "pholder.h"
+
+extern WRegion *create_region_load(WWindow *par, const WFitParams *fp, 
+                                   ExtlTab tab);
+
+extern bool region_supports_save(WRegion *reg);
+DYNFUN ExtlTab region_get_configuration(WRegion *reg);
+extern ExtlTab region_get_base_configuration(WRegion *reg);
+
+extern bool ioncore_init_layout();
+extern bool ioncore_save_layout();
+
+/* Session management support */
+
+typedef bool SMAddCallback(WPHolder *ph, ExtlTab tab);
+typedef void SMCfgCallback(WClientWin *cwin, ExtlTab tab);
+typedef WPHolder *SMPHolderCallback();
+    
+extern void ioncore_set_sm_callbacks(SMAddCallback *add, SMCfgCallback *cfg);
+extern void ioncore_get_sm_callbacks(SMAddCallback **add, SMCfgCallback **cfg);
+extern void ioncore_set_sm_pholder_callback(SMPHolderCallback *phcb);
+extern void ioncore_clientwin_load_missing();
+
+#endif /* ION_IONCORE_SAVELOAD_H */
+
diff --git a/ioncore/screen.c b/ioncore/screen.c
new file mode 100644 (file)
index 0000000..3b896bf
--- /dev/null
@@ -0,0 +1,732 @@
+/*
+ * ion/ioncore/screen.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <libmainloop/defer.h>
+
+#include "common.h"
+#include "global.h"
+#include "screen.h"
+#include "region.h"
+#include "attach.h"
+#include "manage.h"
+#include "focus.h"
+#include "property.h"
+#include "names.h"
+#include "reginfo.h"
+#include "saveload.h"
+#include "resize.h"
+#include "event.h"
+#include "bindmaps.h"
+#include "regbind.h"
+#include "frame-pointer.h"
+#include "rectangle.h"
+#include "infowin.h"
+#include "activity.h"
+#include "extlconv.h"
+#include "llist.h"
+#include "group-ws.h"
+#include "mplex.h"
+#include "tags.h"
+
+
+WHook *screen_managed_changed_hook=NULL;
+
+
+static void screen_update_infowin(WScreen *scr);
+
+
+
+/*{{{ Init/deinit */
+
+
+static bool screen_init(WScreen *scr, WRootWin *rootwin,
+                        int id, const WFitParams *fp, bool useroot)
+{
+    Window win;
+    XSetWindowAttributes attr;
+    ulong attrflags=0;
+    
+    scr->id=id;
+    scr->atom_workspace=None;
+    scr->uses_root=useroot;
+    scr->managed_off.x=0;
+    scr->managed_off.y=0;
+    scr->managed_off.w=0;
+    scr->managed_off.h=0;
+    scr->next_scr=NULL;
+    scr->prev_scr=NULL;
+    scr->rotation=SCREEN_ROTATION_0;
+    
+    watch_init(&(scr->notifywin_watch));
+    watch_init(&(scr->infowin_watch));
+
+    if(useroot){
+        win=WROOTWIN_ROOT(rootwin);
+    }else{
+        attr.background_pixmap=ParentRelative;
+        attrflags=CWBackPixmap;
+        
+        win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(rootwin),
+                          fp->g.x, fp->g.y, fp->g.w, fp->g.h, 0, 
+                          DefaultDepth(ioncore_g.dpy, rootwin->xscr),
+                          InputOutput,
+                          DefaultVisual(ioncore_g.dpy, rootwin->xscr),
+                          attrflags, &attr);
+        if(win==None)
+            return FALSE;
+    }
+
+    if(!mplex_do_init((WMPlex*)scr, (WWindow*)rootwin, win, fp, FALSE))
+        return FALSE;
+
+    /*scr->mplex.win.region.rootwin=rootwin;
+    region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/
+    scr->mplex.flags|=MPLEX_ADD_TO_END;
+    scr->mplex.win.region.flags|=REGION_BINDINGS_ARE_GRABBED;
+    if(useroot)
+        scr->mplex.win.region.flags|=REGION_MAPPED;
+    
+    window_select_input(&(scr->mplex.win),
+                        FocusChangeMask|EnterWindowMask|
+                        KeyPressMask|KeyReleaseMask|
+                        ButtonPressMask|ButtonReleaseMask|
+                        (useroot ? IONCORE_EVENTMASK_ROOT : 0));
+
+    if(id==0){
+        scr->atom_workspace=XInternAtom(ioncore_g.dpy, 
+                                        "_ION_WORKSPACE", False);
+    }else if(id>=0){
+        char *str;
+        libtu_asprintf(&str, "_ION_WORKSPACE%d", id);
+        if(str!=NULL){
+            scr->atom_workspace=XInternAtom(ioncore_g.dpy, str, False);
+            free(str);
+        }
+    }
+
+    /* Add rootwin's bindings to screens (ungrabbed) so that bindings
+     * are called with the proper region.
+     */
+    region_add_bindmap((WRegion*)scr, ioncore_rootwin_bindmap);
+
+    LINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
+    
+    return TRUE;
+}
+
+
+WScreen *create_screen(WRootWin *rootwin, int id, const WFitParams *fp,
+                       bool useroot)
+{
+    CREATEOBJ_IMPL(WScreen, screen, (p, rootwin, id, fp, useroot));
+}
+
+
+void screen_deinit(WScreen *scr)
+{
+    UNLINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr);
+    
+    if(scr->uses_root)
+        scr->mplex.win.win=None;
+    
+    mplex_deinit((WMPlex*)scr);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Attach/detach */
+
+
+void screen_managed_geom(WScreen *scr, WRectangle *geom)
+{
+    geom->x=scr->managed_off.x;
+    geom->y=scr->managed_off.y;
+    geom->w=REGION_GEOM(scr).w+scr->managed_off.w;
+    geom->h=REGION_GEOM(scr).h+scr->managed_off.h;
+    geom->w=maxof(geom->w, 0);
+    geom->h=maxof(geom->h, 0);
+}
+
+
+static bool screen_handle_drop(WScreen *scr, int x, int y, WRegion *dropped)
+{
+    WRegion *curr=mplex_mx_current(&(scr->mplex));
+
+    /* This code should handle dropping tabs on floating workspaces. */
+    if(curr && HAS_DYN(curr, region_handle_drop)){
+        int rx, ry;
+        region_rootpos(curr, &rx, &ry);
+        if(rectangle_contains(&REGION_GEOM(curr), x-rx, y-ry)){
+            if(region_handle_drop(curr, x, y, dropped))
+                return TRUE;
+        }
+    }
+    
+    /* Do not attach to ourselves unlike generic WMPlex. */
+    return FALSE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Region dynfun implementations */
+
+
+static bool screen_fitrep(WScreen *scr, WWindow *par, const WFitParams *fp)
+{
+    WRegion *sub;
+    
+    if(par==NULL)
+        return FALSE;
+    
+    if(scr->uses_root)
+        return FALSE;
+
+    return mplex_fitrep((WMPlex*)scr, NULL, fp);
+}
+
+
+
+
+static void screen_managed_changed(WScreen *scr, int mode, bool sw, 
+                                   WRegion *reg_)
+{
+    if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT)
+        return;
+    
+    if(sw && scr->atom_workspace!=None){
+        WRegion *reg=mplex_mx_current(&(scr->mplex));
+        const char *n=NULL;
+        
+        if(reg!=NULL)
+            n=region_displayname(reg);
+        
+        xwindow_set_string_property(region_root_of((WRegion*)scr),
+                                    scr->atom_workspace, 
+                                    n==NULL ? "" : n);
+    }
+    
+    screen_update_infowin(scr);
+    
+    mplex_call_changed_hook((WMPlex*)scr,
+                            screen_managed_changed_hook,
+                            mode, sw, reg_);
+}
+
+
+static void screen_map(WScreen *scr)
+{
+    if(scr->uses_root)
+        return;
+    mplex_map((WMPlex*)scr);
+}
+
+
+static void screen_unmap(WScreen *scr)
+{
+    if(scr->uses_root)
+        return;
+    mplex_unmap((WMPlex*)scr);
+}
+
+void screen_inactivated(WScreen *scr)
+{
+    screen_update_infowin(scr);
+}
+
+
+void screen_activated(WScreen *scr)
+{
+    screen_update_infowin(scr);
+}
+
+
+/*}}}*/
+
+
+/*}}}*/
+
+
+/*{{{ Notifications */
+
+
+static void do_notify(WScreen *scr, Watch *watch, bool right,
+                      const char *str,
+                      char *style, const char *attr)
+{
+
+    WInfoWin *iw=(WInfoWin*)(watch->obj);
+    WFitParams fp;
+    
+    if(iw==NULL){
+        WMPlexAttachParams param=MPLEXATTACHPARAMS_INIT;
+        
+        param.flags=(MPLEX_ATTACH_UNNUMBERED|
+                     MPLEX_ATTACH_SIZEPOLICY|
+                     MPLEX_ATTACH_GEOM|
+                     MPLEX_ATTACH_LEVEL);
+        param.level=STACKING_LEVEL_ON_TOP;
+        
+        if(!right){
+            param.szplcy=SIZEPOLICY_GRAVITY_NORTHWEST;
+            param.geom.x=0;
+        }else{
+            param.szplcy=SIZEPOLICY_GRAVITY_NORTHEAST;
+            param.geom.x=REGION_GEOM(scr).w-1;
+        }
+        
+        param.geom.y=0;
+        param.geom.w=1;
+        param.geom.h=1;
+        
+        iw=(WInfoWin*)mplex_do_attach_new(&scr->mplex, &param,
+                                          (WRegionCreateFn*)create_infowin, 
+                                          style);
+        
+        if(iw==NULL)
+            return;
+
+        watch_setup(watch, (Obj*)iw, NULL);
+    }
+
+    infowin_set_attr2(iw, attr, NULL);
+    infowin_set_text(iw, str);
+}
+
+
+void screen_notify(WScreen *scr, const char *str)
+{
+    do_notify(scr, &scr->notifywin_watch, FALSE, str, "actnotify", NULL);
+}
+
+
+void screen_windowinfo(WScreen *scr, const char *str, const char *attr)
+{
+    do_notify(scr, &scr->infowin_watch, TRUE, str, "tab-info", attr);
+}
+
+
+void screen_unnotify(WScreen *scr)
+{
+    Obj *iw=scr->notifywin_watch.obj;
+    if(iw!=NULL){
+        watch_reset(&(scr->notifywin_watch));
+        mainloop_defer_destroy(iw);
+    }
+}
+
+
+void screen_nowindowinfo(WScreen *scr)
+{
+    Obj *iw=scr->infowin_watch.obj;
+    if(iw!=NULL){
+        watch_reset(&(scr->infowin_watch));
+        mainloop_defer_destroy(iw);
+    }
+}
+
+
+static char *addnot(char *str, WRegion *reg)
+{
+    const char *nm=region_name(reg);
+    char *nstr=NULL;
+    
+    if(nm==NULL)
+        return str;
+    
+    if(str==NULL)
+        return scat(TR("act: "), nm);
+
+    nstr=scat3(str, ", ", nm);
+    if(nstr!=NULL)
+        free(str);
+    return nstr;
+}
+
+
+static char *screen_managed_activity(WScreen *scr)
+{
+    char *notstr=NULL;
+    WMPlexIterTmp tmp;
+    WRegion *reg;
+    
+    FOR_ALL_MANAGED_BY_MPLEX(&scr->mplex, reg, tmp){
+        if(region_is_activity_r(reg) && !REGION_IS_MAPPED(reg))
+            notstr=addnot(notstr, reg);
+    }
+    
+    return notstr;
+}
+
+
+static void screen_notify_activity(WScreen *scr)
+{
+    if(ioncore_g.screen_notify){
+        char *notstr=screen_managed_activity(scr);
+        if(notstr!=NULL){
+            screen_notify(scr, notstr);
+            free(notstr);
+            return;
+        }
+    }
+
+    screen_unnotify(scr);
+    
+    screen_update_infowin(scr);
+}
+
+
+static void screen_notify_tag(WScreen *scr)
+{
+    screen_update_infowin(scr);
+}
+
+
+static void screen_update_infowin(WScreen *scr)
+{
+    WRegion *reg=mplex_mx_current(&(scr->mplex));
+    bool tag=(reg!=NULL && region_is_tagged(reg));
+    bool act=(reg!=NULL && region_is_activity_r(reg));
+    
+    if(tag || act){
+        const char *n=region_displayname(reg);
+        char *attr=NULL;
+        
+        libtu_asprintf(&attr, "%s-selected-%s-not_dragged-%s",
+                       (REGION_IS_ACTIVE(scr) ? "active" : "inactive"),
+                       (tag ? "tagged" : "not_tagged"),
+                       (act ? "activity" : "no_activity"));
+        
+        screen_windowinfo(scr, n, attr); /* NULL attr ok */
+    }else{
+        screen_nowindowinfo(scr);
+    }
+}
+
+
+static void screen_managed_notify(WScreen *scr, WRegion *reg, const char *how)
+{
+    if(strcmp(how, "sub-activity")==0){
+        /* TODO: multiple calls */
+        mainloop_defer_action((Obj*)scr, 
+                              (WDeferredAction*)screen_notify_activity);
+    }else if(strcmp(how, "tag")==0){
+        mainloop_defer_action((Obj*)scr, 
+                              (WDeferredAction*)screen_notify_tag);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+/*EXTL_DOC
+ * Find the screen with numerical id \var{id}. If Xinerama is
+ * not present, \var{id} corresponds to X screen numbers. Otherwise
+ * the ids are some arbitrary ordering of Xinerama rootwins.
+ * If \var{id} is $-1$, the screen with the highest id is returned.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+WScreen *ioncore_find_screen_id(int id)
+{
+    WScreen *scr, *maxscr=NULL;
+    
+    FOR_ALL_SCREENS(scr){
+        if(id==-1){
+            if(maxscr==NULL || scr->id>maxscr->id)
+                maxscr=scr;
+        }
+        if(scr->id==id)
+            return scr;
+    }
+    
+    return maxscr;
+}
+
+
+/*EXTL_DOC
+ * Switch focus to the screen with id \var{id} and return it.
+ * 
+ * Note that this function is asynchronous; the screen will not
+ * actually have received the focus when this function returns.
+ */
+EXTL_EXPORT
+WScreen *ioncore_goto_nth_screen(int id)
+{
+    WScreen *scr=ioncore_find_screen_id(id);
+    if(scr!=NULL){
+        if(!region_goto((WRegion*)scr))
+            return NULL;
+    }
+    return scr;
+}
+
+
+static WScreen *current_screen()
+{
+    if(ioncore_g.focus_current==NULL)
+        return ioncore_g.screens;
+    else
+        return region_screen_of(ioncore_g.focus_current);
+}
+
+       
+/*EXTL_DOC
+ * Switch focus to the next screen and return it.
+ * 
+ * Note that this function is asynchronous; the screen will not
+ * actually have received the focus when this function returns.
+ */
+EXTL_EXPORT
+WScreen *ioncore_goto_next_screen()
+{
+    WScreen *scr=current_screen();
+    
+    if(scr!=NULL)
+        scr=scr->next_scr;
+    if(scr==NULL)
+        scr=ioncore_g.screens;
+    if(scr!=NULL){
+        if(!region_goto((WRegion*)scr))
+            return NULL;
+    }
+    return scr;
+}
+
+
+/*EXTL_DOC
+ * Switch focus to the previous screen and return it.
+ * 
+ * Note that this function is asynchronous; the screen will not
+ * actually have received the focus when this function returns.
+ */
+EXTL_EXPORT
+WScreen *ioncore_goto_prev_screen()
+{
+    WScreen *scr=current_screen();
+
+    if(scr!=NULL)
+        scr=scr->prev_scr;
+    else
+        scr=ioncore_g.screens;
+    if(scr!=NULL){
+        if(!region_goto((WRegion*)scr))
+            return NULL;
+    }
+    return scr;
+}
+
+
+/*EXTL_DOC
+ * Return the numerical id for screen \var{scr}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+int screen_id(WScreen *scr)
+{
+    return scr->id;
+}
+
+
+static bool screen_managed_may_destroy(WScreen *scr, WRegion *reg)
+{
+    bool onmxlist=FALSE;
+    WLListNode *lnode;
+    WLListIterTmp tmp;
+
+    if(OBJ_IS(reg, WClientWin))
+        return TRUE;
+    
+    FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){
+        if(lnode->st->reg==reg)
+            onmxlist=TRUE;
+        else /*if(OBJ_IS(node->reg, WGenWS))*/
+            return TRUE;
+    }
+    
+    if(!onmxlist)
+        return TRUE;
+    
+    warn(TR("Only workspace may not be destroyed."));
+    
+    return FALSE;
+}
+
+
+static bool screen_may_destroy(WScreen *scr)
+{
+    warn(TR("Screens may not be destroyed."));
+    return FALSE;
+}
+
+
+
+void screen_set_managed_offset(WScreen *scr, const WRectangle *off)
+{
+    scr->managed_off=*off;
+    mplex_fit_managed((WMPlex*)scr);
+}
+
+
+/*EXTL_DOC
+ * Set offset of objects managed by the screen from actual screen geometry.
+ * The table \var{offset} should contain the entries \code{x}, \code{y}, 
+ * \code{w} and \code{h} indicating offsets of that component of screen 
+ * geometry.
+ */
+EXTL_EXPORT_AS(WScreen, set_managed_offset)
+bool screen_set_managed_offset_extl(WScreen *scr, ExtlTab offset)
+{
+    WRectangle g;
+    
+    if(!extl_table_to_rectangle(offset, &g))
+        goto err;
+    
+    if(-g.w>=REGION_GEOM(scr).w)
+        goto err;
+    if(-g.h>=REGION_GEOM(scr).h)
+        goto err;
+    
+    screen_set_managed_offset(scr, &g);
+    
+    return TRUE;
+err:
+    warn(TR("Invalid offset."));
+    return FALSE;
+}
+
+
+WPHolder *screen_get_rescue_pholder_for(WScreen *scr, WRegion *mgd)
+{
+#warning "TODO: better special case handling for groups"
+    
+    return (WPHolder*)mplex_get_rescue_pholder_for(&(scr->mplex), mgd);
+}
+
+/*}}}*/
+
+
+/*{{{ Save/load */
+
+
+ExtlTab screen_get_configuration(WScreen *scr)
+{
+    return mplex_get_configuration(&scr->mplex);
+}
+
+
+static WRegion *do_create_initial(WWindow *parent, const WFitParams *fp, 
+                                  WRegionLoadCreateFn *fn)
+{
+    return fn(parent, fp, extl_table_none());
+}
+
+
+static bool create_initial_ws(WScreen *scr)
+{
+    WRegion *reg=NULL;
+    WMPlexAttachParams par;
+    
+    par.flags=0;
+    
+    reg=mplex_do_attach_new(&scr->mplex, &par,
+                            (WRegionCreateFn*)groupws_load_default, 
+                            NULL);
+    
+    if(reg==NULL){
+        warn(TR("Unable to create a workspace on screen %d."), scr->id);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+bool screen_init_layout(WScreen *scr, ExtlTab tab)
+{
+    char *name;
+    ExtlTab substab, subtab;
+    int n, i;
+
+    if(tab==extl_table_none())
+        return create_initial_ws(scr);
+    
+    mplex_load_contents(&scr->mplex, tab);
+    
+    return TRUE;
+}
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab screen_dynfuntab[]={
+    {region_map, 
+     screen_map},
+    
+    {region_unmap, 
+     screen_unmap},
+     
+    {region_activated, 
+     screen_activated},
+     
+    {region_inactivated, 
+     screen_inactivated},
+    
+    {(DynFun*)region_managed_may_destroy,
+     (DynFun*)screen_managed_may_destroy},
+
+    {(DynFun*)region_may_destroy,
+     (DynFun*)screen_may_destroy},
+
+    {mplex_managed_changed, 
+     screen_managed_changed},
+    
+    {region_managed_notify, 
+     screen_managed_notify},
+    
+    {mplex_managed_geom, 
+     screen_managed_geom},
+
+    {(DynFun*)region_get_configuration,
+     (DynFun*)screen_get_configuration},
+
+    {(DynFun*)region_handle_drop, 
+     (DynFun*)screen_handle_drop},
+
+    {(DynFun*)region_fitrep,
+     (DynFun*)screen_fitrep},
+
+    {(DynFun*)region_get_rescue_pholder_for,
+     (DynFun*)screen_get_rescue_pholder_for},
+    
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab);
+
+
+/*}}}*/
diff --git a/ioncore/screen.h b/ioncore/screen.h
new file mode 100644 (file)
index 0000000..11c83fa
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * ion/ioncore/screen.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_SCREEN_H
+#define ION_IONCORE_SCREEN_H
+
+#include <libextl/extl.h>
+#include <libmainloop/hooks.h>
+#include "common.h"
+#include "mplex.h"
+#include "rectangle.h"
+#include "rootwin.h"
+#include "pholder.h"
+
+#define FOR_ALL_SCREENS(SCR)   \
+    for((SCR)=ioncore_g.screens; \
+        (SCR)!=NULL;           \
+        (SCR)=(SCR)->next_scr)
+
+#define FOR_ALL_SCREENS_W_NEXT(SCR, NXT)                               \
+    for((SCR)=ioncore_g.screens, NXT=((SCR) ? (SCR)->next_scr : NULL); \
+        (SCR)!=NULL;                                                   \
+        (SCR)=(NXT), NXT=((SCR) ? (SCR)->next_scr : NULL))
+
+enum{
+    SCREEN_ROTATION_0,
+    SCREEN_ROTATION_90,
+    SCREEN_ROTATION_180,
+    SCREEN_ROTATION_270
+};
+
+
+DECLCLASS(WScreen){
+    WMPlex mplex;
+    int id;
+    Atom atom_workspace;
+    bool uses_root;
+    int rotation;
+    WRectangle managed_off;
+    WScreen *next_scr, *prev_scr;
+    Watch notifywin_watch;
+    Watch infowin_watch;
+};
+
+extern WScreen *create_screen(WRootWin *rootwin, int id, 
+                              const WFitParams *fp,
+                              bool useroot);
+
+extern int screen_id(WScreen *scr);
+
+extern void screen_set_managed_offset(WScreen *scr, const WRectangle *off);
+
+extern bool screen_init_layout(WScreen *scr, ExtlTab tab);
+
+extern void screen_notify(WScreen *scr, const char *notstr);
+extern void screen_unnotify(WScreen *scr);
+extern void screen_windowinfo(WScreen *scr, const char *name, const char *attr);
+extern void screen_nowindowinfo(WScreen *scr);
+
+extern WPHolder *screen_get_rescue_pholder_for(WScreen *scr, WRegion *mgd);
+
+/* For viewports corresponding to Xinerama rootwins <id> is initially set
+ * to the Xinerama screen number. When Xinerama is not enabled, <id> is
+ * the X screen number (which is the same for all Xinerama rootwins).
+ * For all other viewports <id> is undefined.
+ */
+extern WScreen *ioncore_find_screen_id(int id);
+extern WScreen *ioncore_goto_screen_id(int id);
+extern WScreen *ioncore_goto_next_screen();
+extern WScreen *ioncore_goto_prev_screen();
+
+/* Handlers of this hook receive a WScreen* as the sole parameter. */
+extern WHook *screen_managed_changed_hook;
+
+#endif /* ION_IONCORE_SCREEN_H */
diff --git a/ioncore/selection.c b/ioncore/selection.c
new file mode 100644 (file)
index 0000000..4bb8cd7
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * ion/ioncore/selection.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <X11/Xmd.h>
+#include <string.h>
+
+#include "common.h"
+#include "global.h"
+#include "property.h"
+#include "xwindow.h"
+#include <libextl/extl.h>
+
+
+static char *selection_data=NULL;
+static int selection_length;
+static bool continuation_set=FALSE;
+static ExtlFn continuation;
+
+void ioncore_handle_selection_request(XSelectionRequestEvent *ev)
+{
+    XSelectionEvent sev;
+    const char *p[1];
+    
+    if(selection_data==NULL)
+        return;
+    
+    p[0]=selection_data;
+    
+    xwindow_set_text_property(ev->requestor, ev->property, p, 1);
+    
+    sev.type=SelectionNotify;
+    sev.requestor=ev->requestor;
+    sev.selection=ev->selection;
+    sev.target=ev->target;
+    sev.time=ev->time;
+    sev.property=ev->property;
+    XSendEvent(ioncore_g.dpy, ev->requestor, False, 0L, (XEvent*)&sev);
+}
+
+
+static void ins(Window win, const char *str, int n)
+{
+    if(!continuation_set){
+        WWindow *wwin=XWINDOW_REGION_OF_T(win, WWindow);
+        if(wwin)
+            window_insstr(wwin, str, n);
+    }else{
+        char *tmp=scopyn(str, n);
+        if(tmp!=NULL){
+            extl_call(continuation, "s", NULL, tmp);
+            free(tmp);
+        }
+    }
+}
+
+    
+static void insert_selection(Window win, Atom prop)
+{
+    char **p=xwindow_get_text_property(win, prop, NULL);
+    if(p!=NULL){
+        ins(win, p[0], strlen(p[0]));
+        XFreeStringList(p);
+    }
+}
+
+
+static void insert_cutbuffer(Window win)
+{
+    char *p;
+    int n;
+    
+    p=XFetchBytes(ioncore_g.dpy, &n);
+    
+    if(n<=0 || p==NULL)
+        return;
+    
+    ins(win, p, n);
+}
+
+
+void ioncore_handle_selection(XSelectionEvent *ev)
+{
+    Atom prop=ev->property;
+    Window win=ev->requestor;
+    WWindow *wwin;
+    
+    if(prop==None){
+        insert_cutbuffer(win);
+    }else{
+        insert_selection(win, prop);
+        XDeleteProperty(ioncore_g.dpy, win, prop);
+    }
+    
+    if(continuation_set){
+        extl_unref_fn(continuation);
+        continuation_set=FALSE;
+    }
+}
+
+
+void ioncore_clear_selection()
+{
+    if(selection_data!=NULL){
+        free(selection_data);
+        selection_data=NULL;
+    }
+}
+
+
+void ioncore_set_selection_n(const char *p, int n)
+{
+    if(selection_data!=NULL)
+        free(selection_data);
+    
+    selection_data=ALLOC_N(char, n+1);
+    
+    if(selection_data==NULL)
+        return;
+    
+    memcpy(selection_data, p, n);
+    selection_data[n]='\0';
+    selection_length=n;
+    
+    XStoreBytes(ioncore_g.dpy, p, n);
+    
+    XSetSelectionOwner(ioncore_g.dpy, XA_PRIMARY,
+                       DefaultRootWindow(ioncore_g.dpy),
+                       CurrentTime);
+}
+
+
+/*EXTL_DOC
+ * Set primary selection and cutbuffer0 to \var{p}.
+ */
+EXTL_EXPORT
+void ioncore_set_selection(const char *p)
+{
+    if(p==NULL)
+        ioncore_clear_selection();
+    else
+        ioncore_set_selection_n(p, strlen(p));
+}
+
+
+void ioncore_request_selection_for(Window win)
+{
+    Atom a=XA_STRING;
+    
+    if(continuation_set){
+        extl_unref_fn(continuation);
+        continuation_set=FALSE;
+    }
+    
+    if(ioncore_g.use_mb){
+#ifdef X_HAVE_UTF8_STRING
+        a=XInternAtom(ioncore_g.dpy, "UTF8_STRING", True);
+#else
+        a=XInternAtom(ioncore_g.dpy, "COMPOUND_TEXT", True);
+#endif
+    }
+    
+    XConvertSelection(ioncore_g.dpy, XA_PRIMARY, a,
+                      ioncore_g.atom_selection, win, CurrentTime);
+}
+
+
+/*EXTL_DOC
+ * Request (string) selection. The function \var{fn} will be called 
+ * with the selection when and if it is received.
+ */
+EXTL_EXPORT
+void ioncore_request_selection(ExtlFn fn)
+{
+    assert(ioncore_g.rootwins!=NULL);
+    ioncore_request_selection_for(ioncore_g.rootwins->dummy_win);
+    continuation=extl_ref_fn(fn);
+    continuation_set=TRUE;
+}
+
diff --git a/ioncore/selection.h b/ioncore/selection.h
new file mode 100644 (file)
index 0000000..6a49786
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * ion/ioncore/selection.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_SELECTION_H
+#define ION_IONCORE_SELECTION_H
+
+#include "common.h"
+
+void ioncore_handle_selection_request(XSelectionRequestEvent *ev);
+void ioncore_handle_selection(XSelectionEvent *ev);
+void ioncore_clear_selection();
+void ioncore_set_selection_n(const char *p, int n);
+void ioncore_set_selection(const char *p);
+void ioncore_request_selection_for(Window win);
+
+#endif /* ION_IONCORE_SELECTION_H */
diff --git a/ioncore/sizehint.c b/ioncore/sizehint.c
new file mode 100644 (file)
index 0000000..50c1c99
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * ion/ioncore/sizehint.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <libtu/minmax.h>
+
+#include "common.h"
+#include "global.h"
+#include "region.h"
+#include "resize.h"
+#include "sizehint.h"
+#include "rootwin.h"
+
+
+/*{{{ xsizehints_correct */
+
+
+static void do_correct_aspect(int max_w, int max_h, int ax, int ay,
+                              int *wret, int *hret)
+{
+    int w=*wret, h=*hret;
+
+    if(ax>ay){
+        h=(w*ay)/ax;
+        if(max_h>0 && h>max_h){
+            h=max_h;
+            w=(h*ax)/ay;
+        }
+    }else{
+        w=(h*ax)/ay;
+        if(max_w>0 && w>max_w){
+            w=max_w;
+            h=(w*ay)/ax;
+        }
+    }
+    
+    *wret=w;
+    *hret=h;
+}
+
+
+static void correct_aspect(int max_w, int max_h, const WSizeHints *hints,
+                           int *wret, int *hret)
+{
+    if(!hints->aspect_set)
+        return;
+    
+    if(*wret*hints->max_aspect.y>*hret*hints->max_aspect.x){
+        do_correct_aspect(max_w, max_h,
+                          hints->min_aspect.x, hints->min_aspect.y,
+                          wret, hret);
+    }
+
+    if(*wret*hints->min_aspect.y<*hret*hints->min_aspect.x){
+        do_correct_aspect(max_w, max_h,
+                          hints->max_aspect.x, hints->max_aspect.y,
+                          wret, hret);
+    }
+}
+
+
+void sizehints_correct(const WSizeHints *hints, int *wp, int *hp, 
+                       bool min, bool override_no_constrain)
+{
+    int w=*wp;
+    int h=*hp;
+    int bs=0;
+    
+    if(min){
+        w=maxof(w, hints->min_width);
+        h=maxof(h, hints->min_height);
+    }
+    
+    if(hints->no_constrain && !override_no_constrain){
+        *wp=w;
+        *hp=h;
+        return;
+    }
+
+    if(w>=hints->min_width && h>=hints->min_height)
+        correct_aspect(w, h, hints, &w, &h);
+    
+    if(hints->max_set){
+        w=minof(w, hints->max_width);
+        h=minof(h, hints->max_height);
+    }
+
+    if(hints->inc_set){
+        /* base size should be set to 0 if none given by user program */
+        bs=(hints->base_set ? hints->base_width : 0);
+        if(w>bs)
+            w=((w-bs)/hints->width_inc)*hints->width_inc+bs;
+        bs=(hints->base_set ? hints->base_height : 0);
+        if(h>bs)
+            h=((h-bs)/hints->height_inc)*hints->height_inc+bs;
+    }
+    
+    *wp=w;
+    *hp=h;
+}
+
+
+/*}}}*/
+
+
+/*{{{ X size hints sanity adjustment */
+
+
+void xsizehints_sanity_adjust(XSizeHints *hints)
+{
+    if(!(hints->flags&PMinSize)){
+        if(hints->flags&PBaseSize){
+            hints->min_width=hints->base_width;
+            hints->min_height=hints->base_height;
+        }else{
+            hints->min_width=0;
+            hints->min_height=0;
+        }
+    }
+
+    if(hints->min_width<0)
+        hints->min_width=0;
+    if(hints->min_height<0)
+        hints->min_height=0;
+
+    if(!(hints->flags&PBaseSize) || hints->base_width<0)
+        hints->base_width=hints->min_width;
+    if(!(hints->flags&PBaseSize) || hints->base_height<0)
+        hints->base_height=hints->min_height;
+
+    
+    if(hints->flags&PMaxSize){
+        if(hints->max_width<hints->min_width)
+            hints->max_width=hints->min_width;
+        if(hints->max_height<hints->min_height)
+            hints->max_height=hints->min_height;
+    }
+    
+    hints->flags|=(PBaseSize|PMinSize);
+
+    if(hints->flags&PResizeInc){
+        if(hints->width_inc<=0 || hints->height_inc<=0){
+            warn(TR("Invalid client-supplied width/height increment."));
+            hints->flags&=~PResizeInc;
+        }
+    }
+    
+    if(hints->flags&PAspect){
+        if(hints->min_aspect.x<=0 || hints->min_aspect.y<=0 ||
+           hints->min_aspect.x<=0 || hints->min_aspect.y<=0){
+            warn(TR("Invalid client-supplied aspect-ratio."));
+            hints->flags&=~PAspect;
+        }
+    }
+    
+    if(!(hints->flags&PWinGravity))
+        hints->win_gravity=ForgetGravity;
+}
+
+
+/*}}}*/
+
+
+/*{{{ xsizehints_adjust_for */
+
+
+void sizehints_adjust_for(WSizeHints *hints, WRegion *reg)
+{
+    WSizeHints tmp_hints;
+    
+    region_size_hints(reg, &tmp_hints);
+    
+    if(tmp_hints.min_set){
+        if(!hints->min_set){
+            hints->min_set=TRUE;
+            hints->min_width=tmp_hints.min_width;
+            hints->min_height=tmp_hints.min_height;
+        }else{
+            hints->min_width=maxof(hints->min_width,
+                                   tmp_hints.min_width);
+            hints->min_height=maxof(hints->min_height,
+                                    tmp_hints.min_height);
+        }
+    }
+    
+    if(tmp_hints.max_set && hints->max_set){
+        hints->max_width=maxof(hints->max_width,
+                               tmp_hints.max_width);
+        hints->max_height=maxof(hints->max_height,
+                                tmp_hints.max_height);
+    }else{
+        hints->max_set=FALSE;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ account_gravity */
+
+
+int xgravity_deltax(int gravity, int left, int right)
+{
+    int woff=left+right;
+
+    if(gravity==StaticGravity || gravity==ForgetGravity){
+        return -left;
+    }else if(gravity==NorthWestGravity || gravity==WestGravity ||
+             gravity==SouthWestGravity){
+        /* */
+    }else if(gravity==NorthEastGravity || gravity==EastGravity ||
+             gravity==SouthEastGravity){
+        /* geom->x=geom->w+geom->x-(geom->w+woff) */
+        return -woff;
+    }else if(gravity==CenterGravity || gravity==NorthGravity ||
+             gravity==SouthGravity){
+        /* geom->x=geom->x+geom->w/2-(geom->w+woff)/2 */
+        return -woff/2;
+    }
+    return 0;
+}
+
+
+int xgravity_deltay(int gravity, int top, int bottom)
+{
+    int hoff=top+bottom;
+    
+    if(gravity==StaticGravity || gravity==ForgetGravity){
+        return -top;
+    }else if(gravity==NorthWestGravity || gravity==NorthGravity ||
+             gravity==NorthEastGravity){
+        /* */
+    }else if(gravity==SouthWestGravity || gravity==SouthGravity ||
+             gravity==SouthEastGravity){
+        /* geom->y=geom->y+geom->h-(geom->h+hoff) */
+        return -hoff;
+    }else if(gravity==CenterGravity || gravity==WestGravity ||
+             gravity==EastGravity){
+        /* geom->y=geom->y+geom->h/2-(geom->h+hoff)/2 */
+        return -hoff/2;
+    }
+    return 0;
+}
+
+
+void xgravity_translate(int gravity, WRegion *reg, WRectangle *geom)
+{
+    int top=0, left=0, bottom=0, right=0;
+    WRootWin *root;
+
+    root=region_rootwin_of(reg);
+    region_rootpos(reg, &left, &top);
+    right=REGION_GEOM(root).w-left-REGION_GEOM(reg).w;
+    bottom=REGION_GEOM(root).h-top-REGION_GEOM(reg).h;
+
+    geom->x+=xgravity_deltax(gravity, left, right);
+    geom->y+=xgravity_deltay(gravity, top, bottom);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init */
+
+
+void xsizehints_to_sizehints(const XSizeHints *xh, WSizeHints *hints)
+{
+    hints->max_width=xh->max_width;
+    hints->max_height=xh->max_height;
+    hints->min_width=xh->min_width;
+    hints->min_height=xh->min_height;
+    hints->base_width=xh->base_width;
+    hints->base_height=xh->base_height;
+    hints->width_inc=xh->width_inc;
+    hints->height_inc=xh->height_inc;
+    hints->min_aspect.x=xh->min_aspect.x;
+    hints->min_aspect.y=xh->min_aspect.y;
+    hints->max_aspect.x=xh->max_aspect.x;
+    hints->max_aspect.y=xh->max_aspect.y;
+    
+    hints->max_set=((xh->flags&PMaxSize)!=0);
+    hints->min_set=((xh->flags&PMinSize)!=0);
+    hints->base_set=((xh->flags&PBaseSize)!=0);
+    hints->inc_set=((xh->flags&PResizeInc)!=0);
+    hints->aspect_set=((xh->flags&PAspect)!=0);
+    hints->no_constrain=0;
+}
+
+
+void sizehints_clear(WSizeHints *hints)
+{
+    hints->max_set=0;
+    hints->min_set=0;
+    hints->inc_set=0;
+    hints->base_set=0;
+    hints->aspect_set=0;
+    hints->no_constrain=0;
+}
+
+
+/*}}}*/
diff --git a/ioncore/sizehint.h b/ioncore/sizehint.h
new file mode 100644 (file)
index 0000000..6065e74
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * ion/ioncore/sizehint.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_SIZEHINT_H
+#define ION_IONCORE_SIZEHINT_H
+
+#include "common.h"
+#include "region.h"
+
+INTRSTRUCT(WSizeHints);
+
+DECLSTRUCT(WSizeHints){
+    uint min_set:1;
+    uint max_set:1;
+    uint inc_set:1;
+    uint base_set:1;
+    uint aspect_set:1;
+    uint no_constrain:1;
+    
+    int min_width, min_height;
+    int max_width, max_height;
+    int width_inc, height_inc;
+    struct {
+        int x;
+        int y;
+    } min_aspect, max_aspect;
+    int base_width, base_height;
+};
+
+extern void xsizehints_to_sizehints(const XSizeHints *xh,
+                                    WSizeHints *hints);
+
+extern void xsizehints_sanity_adjust(XSizeHints *hints);
+
+extern void sizehints_clear(WSizeHints *hints);
+
+extern void sizehints_adjust_for(WSizeHints *hints, WRegion *reg);
+
+extern void sizehints_correct(const WSizeHints *hints, int *wp, int *hp, 
+                              bool min, bool override_no_constrain);
+
+extern int xgravity_deltax(int gravity, int left, int right);
+extern int xgravity_deltay(int gravity, int top, int bottom);
+extern void xgravity_translate(int gravity, WRegion *reg, WRectangle *geom);
+
+#endif /* ION_IONCORE_SIZEHINT_H */
diff --git a/ioncore/sizepolicy.c b/ioncore/sizepolicy.c
new file mode 100644 (file)
index 0000000..6e23875
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * ion/ioncore/sizepolicy.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/minmax.h>
+#include <string.h>
+
+#include "common.h"
+#include "region.h"
+#include "resize.h"
+#include "sizehint.h"
+#include "sizepolicy.h"
+
+
+
+static int fit_x(int x, int w, const WRectangle *max_geom)
+{
+    int mw=maxof(max_geom->w, 1);
+    w=minof(mw, w);
+    return minof(maxof(x, max_geom->x), max_geom->x+mw-w);
+}
+
+
+static int fit_y(int y, int h, const WRectangle *max_geom)
+{
+    int mh=maxof(max_geom->h, 1);
+    h=minof(mh, h);
+    return minof(maxof(y, max_geom->y), max_geom->y+mh-h);
+}
+
+
+static void do_gravity(const WRectangle *max_geom, int szplcy,
+                       WRectangle *geom)
+{
+    /* Assumed: width and height already adjusted within limits */
+    if(geom->h<1)
+        geom->h=1;
+    if(geom->w<1)
+        geom->w=1;
+    
+    switch(szplcy&SIZEPOLICY_HORIZ_MASK){
+    case SIZEPOLICY_HORIZ_LEFT:
+        geom->x=max_geom->x;
+        break;
+        
+    case SIZEPOLICY_HORIZ_RIGHT:
+        geom->x=max_geom->x+max_geom->w-geom->w;
+        break;
+
+    case SIZEPOLICY_HORIZ_CENTER:
+        geom->x=max_geom->x+max_geom->w/2-geom->w/2;
+        break;
+        
+    default:
+        geom->x=fit_x(geom->x, geom->w, max_geom);
+    }
+
+    switch(szplcy&SIZEPOLICY_VERT_MASK){
+    case SIZEPOLICY_VERT_TOP:
+        geom->y=max_geom->y;
+        break;
+        
+    case SIZEPOLICY_VERT_BOTTOM:
+        geom->y=max_geom->y+max_geom->h-geom->h;
+        break;
+
+    case SIZEPOLICY_VERT_CENTER:
+        geom->y=max_geom->y+max_geom->h/2-geom->h/2;
+        break;
+        
+    default:
+        geom->y=fit_x(geom->y, geom->h, max_geom);
+    }
+}
+
+
+static void gravity_stretch_policy(int szplcy, WRegion *reg,
+                                   const WRectangle *rq_geom, WFitParams *fp, 
+                                   bool ws, bool hs)
+{
+    WRectangle max_geom=fp->g;
+    int w, h;
+
+    fp->g=*rq_geom;
+    
+    w=(ws ? max_geom.w : minof(rq_geom->w, max_geom.w));
+    h=(hs ? max_geom.h : minof(rq_geom->h, max_geom.h));
+    
+    if(reg!=NULL)
+        region_size_hints_correct(reg,  &w, &h, FALSE);
+    
+    fp->g.w=w;
+    fp->g.h=h;
+    
+    do_gravity(&max_geom, szplcy, &(fp->g));
+}
+
+
+static void sizepolicy_free_snap(WSizePolicy *szplcy, WRegion *reg,
+                                 WRectangle *rq_geom, int rq_flags,
+                                 WFitParams *fp)
+{
+    WRectangle max_geom=fp->g;
+    bool fullw=((rq_flags&REGION_RQGEOM_WEAK_W) &&
+                (*szplcy&SIZEPOLICY_HORIZ_MASK)==SIZEPOLICY_HORIZ_CENTER);
+    bool fullh=((rq_flags&REGION_RQGEOM_WEAK_H) &&
+                (*szplcy&SIZEPOLICY_VERT_MASK)==SIZEPOLICY_VERT_CENTER);
+
+    int w=(fullw ? max_geom.w : minof(rq_geom->w, max_geom.w));
+    int h=(fullh ? max_geom.h : minof(rq_geom->h, max_geom.h));
+    int x_=0, y_=0;
+
+    
+    if(!(rq_flags&REGION_RQGEOM_WEAK_X) 
+       && rq_flags&REGION_RQGEOM_WEAK_W){
+        x_=fit_x(rq_geom->x, 1, &max_geom);
+        if(((*szplcy)&SIZEPOLICY_HORIZ_MASK)==SIZEPOLICY_HORIZ_RIGHT)
+            w=max_geom.x+max_geom.w-x_;
+        else
+            w=minof(w, max_geom.x+max_geom.w-x_);
+    }
+    
+    if(!(rq_flags&REGION_RQGEOM_WEAK_Y)
+       && rq_flags&REGION_RQGEOM_WEAK_H){
+        y_=fit_x(rq_geom->y, 1, &max_geom);
+        if(((*szplcy)&SIZEPOLICY_VERT_MASK)==SIZEPOLICY_VERT_BOTTOM)
+            h=max_geom.y+max_geom.h-y_;
+        else
+            h=minof(h, max_geom.y+max_geom.h-y_);
+    }
+       
+    if(reg!=NULL)
+        region_size_hints_correct(reg, &w, &h, FALSE);
+    
+    fp->g.w=w;
+    fp->g.h=h;
+    
+    if(!(rq_flags&REGION_RQGEOM_WEAK_X) 
+       && rq_flags&REGION_RQGEOM_WEAK_W){
+        fp->g.x=x_;
+    }else if(rq_flags&REGION_RQGEOM_WEAK_X){
+        switch((*szplcy)&SIZEPOLICY_HORIZ_MASK){
+        case SIZEPOLICY_HORIZ_CENTER:
+            fp->g.x=max_geom.x+(max_geom.w-w)/2;
+            break;
+        case SIZEPOLICY_HORIZ_LEFT:
+            fp->g.x=max_geom.x;
+            break;
+            
+        case SIZEPOLICY_HORIZ_RIGHT:
+            fp->g.x=max_geom.x+max_geom.w-w;
+            break;
+            
+        default:
+            fp->g.x=fit_x(rq_geom->x, w, &max_geom);
+            break;
+        }
+    }else{
+        fp->g.x=fit_x(rq_geom->x, w, &max_geom);
+    }
+    
+    if(!(rq_flags&REGION_RQGEOM_WEAK_Y)
+       && rq_flags&REGION_RQGEOM_WEAK_H){
+        fp->g.y=y_;
+    }else if(rq_flags&REGION_RQGEOM_WEAK_Y){
+        switch((*szplcy)&SIZEPOLICY_VERT_MASK){
+        case SIZEPOLICY_VERT_CENTER:
+            fp->g.y=max_geom.y+(max_geom.h-h)/2;
+            break;
+            
+        case SIZEPOLICY_VERT_TOP:
+            fp->g.y=max_geom.y;
+            break;
+            
+        case SIZEPOLICY_VERT_BOTTOM:
+            fp->g.y=max_geom.y+max_geom.h-h;
+            break;
+            
+        default:
+            fp->g.y=fit_y(rq_geom->y, h, &max_geom);
+            break;
+        }
+    }else{
+        fp->g.y=fit_y(rq_geom->y, h, &max_geom);
+    }
+    
+    (*szplcy)&=~(SIZEPOLICY_VERT_MASK|SIZEPOLICY_HORIZ_MASK);
+    
+    *szplcy|=( (fullw || fp->g.x<=max_geom.x ? SIZEPOLICY_HORIZ_LEFT : 0)
+              |(fullw || fp->g.x+fp->g.w>=max_geom.x+max_geom.w ? SIZEPOLICY_HORIZ_RIGHT : 0)
+              |(fullh || fp->g.y<=max_geom.y ? SIZEPOLICY_VERT_TOP : 0)
+              |(fullh || fp->g.y+fp->g.h>=max_geom.y+max_geom.h ? SIZEPOLICY_VERT_BOTTOM : 0));
+}
+
+
+void sizepolicy(WSizePolicy *szplcy, WRegion *reg,
+                const WRectangle *rq_geom, int rq_flags,
+                WFitParams *fp)
+{
+    uint extra=fp->mode&REGION_FIT_ROTATE;
+    
+    WRectangle tmp;
+    if(rq_geom!=NULL)
+        tmp=*rq_geom;
+    else if(reg!=NULL)
+        tmp=REGION_GEOM(reg);
+    else
+        tmp=fp->g;
+    
+    if((*szplcy)&SIZEPOLICY_SHRUNK){
+        if(reg!=NULL){
+            tmp.w=region_min_w(reg);
+            tmp.h=region_min_h(reg);
+        }else{
+            tmp.w=1;
+            tmp.h=1;
+        }
+        rq_flags&=~(REGION_RQGEOM_WEAK_W|REGION_RQGEOM_WEAK_H);
+    }
+
+    fp->mode=REGION_FIT_EXACT|extra;
+    
+    switch((*szplcy)&SIZEPOLICY_MASK){
+    case SIZEPOLICY_GRAVITY:
+        gravity_stretch_policy(*szplcy, reg, &tmp, fp, FALSE, FALSE);
+        break;
+        
+    case SIZEPOLICY_STRETCH_LEFT:
+        gravity_stretch_policy(SIZEPOLICY_HORIZ_LEFT|SIZEPOLICY_VERT_CENTER, 
+                               reg, &tmp, fp, FALSE, TRUE);
+        break;
+        
+    case SIZEPOLICY_STRETCH_RIGHT:
+        gravity_stretch_policy(SIZEPOLICY_HORIZ_RIGHT|SIZEPOLICY_VERT_CENTER, 
+                               reg, &tmp, fp, FALSE, TRUE);
+        break;
+        
+    case SIZEPOLICY_STRETCH_TOP:
+        gravity_stretch_policy(SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_CENTER, 
+                               reg, &tmp, fp, TRUE, FALSE);
+        break;
+        
+    case SIZEPOLICY_STRETCH_BOTTOM:
+        gravity_stretch_policy(SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_CENTER, 
+                               reg, &tmp, fp, TRUE, FALSE);
+        break;
+        
+    case SIZEPOLICY_FULL_EXACT:
+        gravity_stretch_policy(SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_CENTER, 
+                               reg, &tmp, fp, TRUE, TRUE);
+        break;
+        
+    case SIZEPOLICY_FREE:
+        rectangle_constrain(&tmp, &(fp->g));
+        if(reg!=NULL)
+            region_size_hints_correct(reg, &tmp.w, &tmp.h, FALSE);
+        fp->g=tmp;
+        break;
+        
+    case SIZEPOLICY_UNCONSTRAINED:
+        if(reg!=NULL)
+            region_size_hints_correct(reg, &tmp.w, &tmp.h, TRUE);
+        fp->g=tmp;
+        break;
+
+    case SIZEPOLICY_FREE_GLUE:
+        sizepolicy_free_snap(szplcy, reg, &tmp, rq_flags, fp);
+        break;
+        
+    case SIZEPOLICY_FULL_BOUNDS:
+    default:
+        fp->mode=REGION_FIT_BOUNDS|extra;
+        break;
+    }
+}
+
+
+struct szplcy_spec {
+    const char *spec;
+    int szplcy;
+};
+
+
+/* translation table for sizepolicy specifications */
+static struct szplcy_spec szplcy_specs[] = {
+    {"default",         SIZEPOLICY_DEFAULT},
+    {"full",            SIZEPOLICY_FULL_EXACT},
+    {"full_bounds",     SIZEPOLICY_FULL_BOUNDS},
+    {"free",            SIZEPOLICY_FREE},
+    {"free_glue",       SIZEPOLICY_FREE_GLUE},
+    {"northwest",       SIZEPOLICY_GRAVITY_NORTHWEST},
+    {"north",           SIZEPOLICY_GRAVITY_NORTH},
+    {"northeast",       SIZEPOLICY_GRAVITY_NORTHEAST},
+    {"west",            SIZEPOLICY_GRAVITY_WEST},
+    {"center",          SIZEPOLICY_GRAVITY_CENTER},
+    {"east",            SIZEPOLICY_GRAVITY_EAST},
+    {"southwest",       SIZEPOLICY_GRAVITY_SOUTHWEST},
+    {"south",           SIZEPOLICY_GRAVITY_SOUTH},
+    {"southeast",       SIZEPOLICY_GRAVITY_SOUTHEAST},
+    {"stretch_top",     SIZEPOLICY_STRETCH_TOP},
+    {"stretch_bottom",  SIZEPOLICY_STRETCH_BOTTOM},
+    {"stretch_left",    SIZEPOLICY_STRETCH_LEFT},
+    {"stretch_right",   SIZEPOLICY_STRETCH_RIGHT},
+    {"free_glue_northwest",  SIZEPOLICY_FREE_GLUE__NORTHWEST},
+    {"free_glue_north",      SIZEPOLICY_FREE_GLUE__NORTH},
+    {"free_glue_northeast",  SIZEPOLICY_FREE_GLUE__NORTHEAST},
+    {"free_glue_west",       SIZEPOLICY_FREE_GLUE__WEST},
+    {"free_glue_center",     SIZEPOLICY_FREE_GLUE__CENTER},
+    {"free_glue_east",       SIZEPOLICY_FREE_GLUE__EAST},
+    {"free_glue_southwest",  SIZEPOLICY_FREE_GLUE__SOUTHWEST},
+    {"free_glue_south",      SIZEPOLICY_FREE_GLUE__SOUTH},
+    {"free_glue_southeast",  SIZEPOLICY_FREE_GLUE__SOUTHEAST},
+    { NULL,             SIZEPOLICY_DEFAULT}   /* end marker */
+};
+
+
+bool string2sizepolicy(const char *szplcy, WSizePolicy *value)
+{
+    const struct szplcy_spec *sp;
+    
+    *value=SIZEPOLICY_DEFAULT;
+
+    for(sp=szplcy_specs; sp->spec; ++sp){
+       if(strcasecmp(szplcy,sp->spec)==0){
+           *value=sp->szplcy;
+           return TRUE;
+        }
+    }
+    
+    return FALSE;
+}
+
diff --git a/ioncore/sizepolicy.h b/ioncore/sizepolicy.h
new file mode 100644 (file)
index 0000000..0984737
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * ion/ioncore/sizepolicy.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_SIZEPOLICY_H
+#define ION_IONCORE_SIZEPOLICY_H
+
+#include "common.h"
+#include "region.h"
+
+/* Modifiers for some policies */
+#define SIZEPOLICY_VERT_NONE    0x0000
+#define SIZEPOLICY_VERT_TOP     0x0100
+#define SIZEPOLICY_VERT_BOTTOM  0x0200
+#define SIZEPOLICY_VERT_CENTER  0x0300
+#define SIZEPOLICY_VERT_MASK    0x0300
+
+#define SIZEPOLICY_HORIZ_NONE   0x0000
+#define SIZEPOLICY_HORIZ_LEFT   0x0400
+#define SIZEPOLICY_HORIZ_RIGHT  0x0800
+#define SIZEPOLICY_HORIZ_CENTER 0x0c00
+#define SIZEPOLICY_HORIZ_MASK   0x0c00
+
+#define SIZEPOLICY_SHRUNK       0x1000
+
+/* The policies */
+#define SIZEPOLICY_MASK           0xff
+#define SIZEPOLICY_DEFAULT        0x00
+#define SIZEPOLICY_FULL_EXACT     0x01
+#define SIZEPOLICY_FULL_BOUNDS    0x02
+#define SIZEPOLICY_FREE           0x03
+#define SIZEPOLICY_GRAVITY        0x04 /* uses vert/horiz flags */
+#define SIZEPOLICY_FREE_GLUE      0x05 /* stateful; modifies v/h flags */
+#define SIZEPOLICY_STRETCH_LEFT   0x06
+#define SIZEPOLICY_STRETCH_RIGHT  0x07
+#define SIZEPOLICY_STRETCH_TOP    0x08
+#define SIZEPOLICY_STRETCH_BOTTOM 0x09
+#define SIZEPOLICY_UNCONSTRAINED  0x10
+
+#define SIZEPOLICY_GRAVITY_NORTHWEST (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_LEFT)
+#define SIZEPOLICY_GRAVITY_NORTH     (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_CENTER)
+#define SIZEPOLICY_GRAVITY_NORTHEAST (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_RIGHT)
+#define SIZEPOLICY_GRAVITY_WEST      (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_LEFT)
+#define SIZEPOLICY_GRAVITY_CENTER    (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_CENTER)
+#define SIZEPOLICY_GRAVITY_EAST      (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_RIGHT)
+#define SIZEPOLICY_GRAVITY_SOUTHWEST (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_LEFT)
+#define SIZEPOLICY_GRAVITY_SOUTH     (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_CENTER)
+#define SIZEPOLICY_GRAVITY_SOUTHEAST (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_RIGHT)
+
+#define SIZEPOLICY_FREE_GLUE__NORTHWEST (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_LEFT)
+#define SIZEPOLICY_FREE_GLUE__NORTH     (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_CENTER)
+#define SIZEPOLICY_FREE_GLUE__NORTHEAST (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_RIGHT)
+#define SIZEPOLICY_FREE_GLUE__WEST      (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_LEFT)
+#define SIZEPOLICY_FREE_GLUE__CENTER    (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_CENTER)
+#define SIZEPOLICY_FREE_GLUE__EAST      (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_RIGHT)
+#define SIZEPOLICY_FREE_GLUE__SOUTHWEST (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_LEFT)
+#define SIZEPOLICY_FREE_GLUE__SOUTH     (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_CENTER)
+#define SIZEPOLICY_FREE_GLUE__SOUTHEAST (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_RIGHT)
+
+typedef uint WSizePolicy;
+
+
+extern void sizepolicy(WSizePolicy *szplcy, WRegion *reg,
+                       const WRectangle *rq_geom, int rq_flags,
+                       WFitParams *fp);
+
+
+bool string2sizepolicy(const char *szplcy, WSizePolicy *value);
+
+
+#endif /* ION_IONCORE_SIZEPOLICY_H */
diff --git a/ioncore/stacking.c b/ioncore/stacking.c
new file mode 100644 (file)
index 0000000..8d696cb
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * ion/ioncore/stacking.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/rb.h>
+
+#include "common.h"
+#include "region.h"
+#include "stacking.h"
+#include "window.h"
+#include "sizepolicy.h"
+
+
+/*{{{ Alloc */
+
+
+WStacking *create_stacking()
+{
+    WStacking *st=ALLOC(WStacking);
+    
+    if(st!=NULL){
+        st->reg=NULL;
+        st->above=NULL;
+        st->level=0;
+        st->szplcy=SIZEPOLICY_DEFAULT;
+        st->hidden=FALSE;
+        st->lnode=NULL;
+    }
+    
+    return st;
+}
+
+
+void stacking_free(WStacking *st)
+{
+    assert(st->mgr_next==NULL && st->mgr_prev==NULL &&
+           st->next==NULL && st->prev==NULL &&
+           /*st->above==NULL &&*/
+           st->lnode==NULL &&
+           st->reg==NULL);
+    
+    free(st);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Lookup */
+
+
+static Rb_node stacking_of_reg=NULL;
+
+
+WStacking *ioncore_find_stacking(WRegion *reg)
+{
+    Rb_node node=NULL;
+    int found=0;
+    
+    if(stacking_of_reg!=NULL)
+        node=rb_find_pkey_n(stacking_of_reg, reg, &found);
+    
+    return (found ? (WStacking*)node->v.val : NULL);
+}
+
+
+void stacking_unassoc(WStacking *st)
+{
+    Rb_node node=NULL;
+    int found=0;
+    
+    if(st->reg==NULL)
+        return;
+    
+    if(stacking_of_reg!=NULL)
+        node=rb_find_pkey_n(stacking_of_reg, st->reg, &found);
+    
+    if(node!=NULL)
+        rb_delete_node(node);
+    
+    st->reg=NULL;
+}
+
+
+bool stacking_assoc(WStacking *st, WRegion *reg)
+{
+    assert(st->reg==NULL);
+    
+    if(stacking_of_reg==NULL){
+        stacking_of_reg=make_rb();
+        if(stacking_of_reg==NULL)
+            return FALSE;
+    }
+
+    if(rb_insertp(stacking_of_reg, reg, st)==NULL)
+        return FALSE;
+    
+    st->reg=reg;
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+
+/*{{{ List processing */
+
+
+static WStacking *link_lists(WStacking *l1, WStacking *l2)
+{
+    /* As everywhere, doubly-linked lists without the forward 
+     * link in last item! 
+     */
+    WStacking *tmp=l2->prev;
+    l1->prev->next=l2;
+    l2->prev=l1->prev;
+    l1->prev=tmp;
+    return l1;
+}
+
+
+static WStacking *link_list_before(WStacking *l1, 
+                                   WStacking *i1,
+                                   WStacking *l2)
+{
+    WStacking *tmp;
+    
+    if(i1==l1)
+        return link_lists(l2, l1);
+    
+    l2->prev->next=i1;
+    i1->prev->next=l2;
+    tmp=i1->prev;
+    i1->prev=l2->prev;
+    l2->prev=tmp;
+    
+    return l1;
+}
+
+
+static WStacking *link_list_after(WStacking *l1, 
+                                  WStacking *i1,
+                                  WStacking *l2)
+{
+    WStacking *tmp;
+    
+    if(i1==l1->prev)
+        return link_lists(l1, l2);
+    
+    i1->next->prev=l2->prev;
+    l2->prev->next=i1->next;
+    i1->next=l2;
+    l2->prev=i1;
+    
+    return l1;
+}
+
+
+WStacking *stacking_unstack(WWindow *par, WStacking *regst)
+{
+    WStacking *nxt=NULL, *st;
+    
+    /*st=regst->next;*/
+    
+    UNLINK_ITEM(par->stacking, regst, next, prev);
+            
+    /*while(st!=NULL){*/
+    for(st=par->stacking; st!=NULL; st=st->next){
+        if(st->above==regst){
+            st->above=NULL;
+            nxt=st;
+        }
+        /*st=st->next;*/
+    }
+    
+    if(nxt==NULL)
+        nxt=regst->above;
+    
+    if(regst->above==NULL)
+        regst->above=NULL;
+    
+    return nxt;
+}
+
+
+static bool cf(WStackingFilter *filt, void *filt_data, WStacking *st)
+{
+    return (filt==NULL || filt(st, filt_data));
+}
+
+
+static bool check_unweave(WStacking *st)
+{
+    /* 2: unknown, 1: yes, 0: no */
+    
+    if(st->to_unweave==2){
+        if(st->above!=NULL)
+            st->to_unweave=check_unweave(st->above);
+        else
+            st->to_unweave=0;
+    }
+    
+    return st->to_unweave;
+}
+
+
+WStacking *stacking_unweave(WStacking **stacking, 
+                            WStackingFilter *filt, void *filt_data)
+{
+    WStacking *np=NULL;
+    WStacking *st, *next;
+
+    for(st=*stacking; st!=NULL; st=st->next){
+        st->to_unweave=2;
+        if(st->above==NULL && cf(filt, filt_data, st))
+            st->to_unweave=1;
+    }
+    
+    for(st=*stacking; st!=NULL; st=st->next)
+        check_unweave(st);
+    
+    for(st=*stacking; st!=NULL; st=next){
+        next=st->next;
+        if(st->to_unweave==1){
+            UNLINK_ITEM(*stacking, st, next, prev);
+            LINK_ITEM(np, st, next, prev);
+        }
+    }
+    
+    return np;
+}
+
+
+static int check_above_lvl(WStacking *st)
+{
+    if(st->above==NULL)
+        return st->level;
+    st->level=check_above_lvl(st->above);
+    return st->level;
+}
+
+    
+static void enforce_level_sanity(WStacking **np)
+{
+    WStacking *st;
+    
+    /* Make sure that the levels of stuff stacked 'above' match
+     * the level of the thing stacked above.
+     */
+    for(st=*np; st!=NULL; st=st->next)
+        check_above_lvl(st);
+
+    /* And now make sure things are ordered by levels. */
+    st=*np; 
+    while(st->next!=NULL){
+        if(st->next->level < st->level){
+            WStacking *st2=st->next;
+            UNLINK_ITEM(*np, st2, next, prev);
+            LINK_ITEM_BEFORE(*np, st2, st, next, prev);
+            if(st2->prev!=NULL)
+                st=st2->prev;
+        }else{
+            st=st->next;
+        }
+    }
+}
+
+
+static void get_bottom(WStacking *st, Window fb_win,
+                       Window *other, int *mode)
+{
+    Window bottom=None, top=None;
+    
+    while(st!=NULL){
+        if(st->reg!=NULL){
+            region_stacking(st->reg, &bottom, &top);
+            if(bottom!=None){
+                *other=bottom;
+                *mode=Below;
+                return;
+            }
+        }
+        st=st->next;
+    }
+    
+    *other=fb_win;
+    *mode=Above;
+}
+
+
+static void stacking_do_weave(WStacking **stacking, WStacking **np, 
+                              bool below, Window fb_win)
+{
+    WStacking *st, *ab;
+    uint lvl;
+    Window other;
+    int mode;
+
+    if(*np==NULL)
+        return;
+    
+    /* Should do nothing.. */
+    enforce_level_sanity(np);
+    
+    ab=*stacking;
+    
+    while(*np!=NULL){
+        lvl=(*np)->level;
+        
+        while(ab!=NULL){
+            if(ab->level>lvl || (below && ab->level==lvl))
+                break;
+            ab=ab->next;
+        }
+        get_bottom(ab, fb_win, &other, &mode);
+        
+        st=*np;
+
+        UNLINK_ITEM(*np, st, next, prev);
+        
+        region_restack(st->reg, other, mode);
+
+        if(ab!=NULL){
+            LINK_ITEM_BEFORE(*stacking, ab, st, next, prev);
+        }else{
+            LINK_ITEM_LAST(*stacking, st, next, prev);
+        }
+    }
+}
+
+
+void stacking_weave(WStacking **stacking, WStacking **np, bool below)
+{
+    stacking_do_weave(stacking, np, below, None);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Raise/lower */
+
+
+static bool is_above(WStacking *st, WStacking *p)
+{
+    if(st->above==NULL)
+        return FALSE;
+    else if(st->above==p)
+        return TRUE;
+    else
+        return is_above(st->above, p);
+}
+
+
+static void collect_first(WStacking **dst, WStacking **src, WStacking *st)
+{
+    UNLINK_ITEM(*src, st, next, prev);
+    LINK_ITEM_FIRST(*dst, st, next, prev);
+}
+
+
+static void collect_last(WStacking **dst, WStacking **src, WStacking *st)
+{
+    UNLINK_ITEM(*src, st, next, prev);
+    LINK_ITEM_LAST(*dst, st, next, prev);
+}
+
+
+static void collect_above(WStacking **dst, WStacking **src, WStacking *regst)
+{
+    WStacking *stabove, *stnext;
+    
+    for(stabove=*src; stabove!=NULL; stabove=stnext){
+        stnext=stabove->next;
+        
+        if(is_above(stabove, regst))
+            collect_last(dst, src, stabove);
+    }
+}
+
+
+static WStacking *unweave_subtree(WStacking **stacking, WStacking *regst,
+                                  bool parents)
+{
+    WStacking *tmp=NULL;
+    
+    if(parents){
+        WStacking *st=regst;
+        while(st!=NULL){
+            collect_first(&tmp, stacking, st);
+            st=st->above;
+        }
+    }else{
+        collect_first(&tmp, stacking, regst);
+    }
+    
+    collect_above(&tmp, stacking, regst);
+    
+    return tmp;
+}
+
+
+void stacking_restack(WStacking **stacking, WStacking *st, Window fb_win,
+                      WStackingFilter *filt, void *filt_data, bool lower)
+{
+    WStacking *tmp=unweave_subtree(stacking, st, lower);
+
+    stacking_do_weave(stacking, &tmp, lower, fb_win);
+
+    assert(tmp==NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Stacking lists */
+
+
+WStacking **window_get_stackingp(WWindow *wwin)
+{
+    return &(wwin->stacking);
+}
+
+
+WStacking *window_get_stacking(WWindow *wwin)
+{
+    return wwin->stacking;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Stacking list iteration */
+
+
+void stacking_iter_init(WStackingIterTmp *tmp, 
+                        WStacking *st,
+                        WStackingFilter *filt,
+                        void *filt_data)
+{
+    tmp->st=st;
+    tmp->filt=filt;
+    tmp->filt_data=filt_data;
+}
+
+
+WStacking *stacking_iter_nodes(WStackingIterTmp *tmp)
+{
+    WStacking *next=NULL;
+    
+    while(tmp->st!=NULL){
+        next=tmp->st;
+        tmp->st=tmp->st->next;
+        if(cf(tmp->filt, tmp->filt_data, next))
+            break;
+        next=NULL;
+    }
+    
+    return next;
+}
+
+
+WRegion *stacking_iter(WStackingIterTmp *tmp)
+{
+    WStacking *st=stacking_iter_nodes(tmp);
+    return (st!=NULL ? st->reg : NULL);
+}
+
+
+void stacking_iter_mgr_init(WStackingIterTmp *tmp, 
+                            WStacking *st,
+                            WStackingFilter *filt,
+                            void *filt_data)
+{
+    tmp->st=st;
+    tmp->filt=filt;
+    tmp->filt_data=filt_data;
+}
+
+
+WStacking *stacking_iter_mgr_nodes(WStackingIterTmp *tmp)
+{
+    WStacking *next=NULL;
+    
+    while(tmp->st!=NULL){
+        next=tmp->st;
+        tmp->st=tmp->st->mgr_next;
+        if(cf(tmp->filt, tmp->filt_data, next))
+            break;
+        next=NULL;
+    }
+    
+    return next;
+}
+
+
+WRegion *stacking_iter_mgr(WStackingIterTmp *tmp)
+{
+    WStacking *st=stacking_iter_mgr_nodes(tmp);
+    return (st!=NULL ? st->reg : NULL);
+}
+
+    
+/*}}}*/
+
+
+/*{{{ Focus */
+
+
+uint stacking_min_level(WStacking *stacking, 
+                        WStackingFilter *include_filt, 
+                        void *filt_data)
+{
+    WStacking *st=NULL;
+    uint min_level=0;
+    
+    if(stacking==NULL)
+        return STACKING_LEVEL_BOTTOM;
+    
+    st=stacking;
+    do{
+        st=st->prev;
+        
+        if(st->reg!=NULL 
+           && !(st->reg->flags&REGION_SKIP_FOCUS)
+           && cf(include_filt, filt_data, st)){
+            
+            if(st->level>=STACKING_LEVEL_MODAL1)
+                min_level=st->level;
+            
+            break;
+        }
+    }while(st!=stacking);
+    
+    return min_level;
+}
+
+
+WStacking *stacking_find_to_focus(WStacking *stacking, WStacking *to_try,
+                                  WStackingFilter *include_filt, 
+                                  WStackingFilter *approve_filt, 
+                                  void *filt_data)
+{
+    WStacking *st=NULL;
+    uint min_level=0;
+    
+    if(stacking==NULL)
+        return NULL;
+    
+    min_level=stacking_min_level(stacking, include_filt, filt_data);
+    
+    if(to_try!=NULL && to_try->level>=min_level)
+        return to_try;
+    
+    st=stacking;
+    do{
+        st=st->prev;
+        
+        if(st->level<min_level)
+            break;
+        
+        if(st->reg!=NULL 
+           && !(st->reg->flags&REGION_SKIP_FOCUS)
+           && cf(include_filt, filt_data, st)
+           && cf(approve_filt, filt_data, st)){
+            return st;
+        }
+    }while(st!=stacking);
+    
+    return NULL;
+}
+
+
+static bool mapped_filt(WStacking *st, void *unused)
+{
+    return (st->reg!=NULL && REGION_IS_MAPPED(st->reg));
+}
+
+
+static bool mgr_filt(WStacking *st, void *mgr_)
+{
+    return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)mgr_);
+}
+
+
+WStacking *stacking_find_to_focus_mapped(WStacking *stacking, 
+                                         WStacking *to_try,
+                                         WRegion *mgr)
+{
+    if(mgr==NULL){
+        return stacking_find_to_focus(stacking, to_try, mapped_filt, 
+                                      NULL, NULL);
+    }else{
+        return stacking_find_to_focus(stacking, to_try, mapped_filt, 
+                                      mgr_filt, mgr);
+    }
+}
+
+
+uint stacking_min_level_mapped(WStacking *stacking)
+{
+    return stacking_min_level(stacking, mapped_filt, NULL);
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/stacking.h b/ioncore/stacking.h
new file mode 100644 (file)
index 0000000..a5e3464
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * ion/ioncore/stacking.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_STACKING_H
+#define ION_IONCORE_STACKING_H
+
+#include "common.h"
+#include "region.h"
+#include "sizepolicy.h"
+
+
+#define STACKING_LEVEL_BOTTOM 0
+#define STACKING_LEVEL_NORMAL 1
+#define STACKING_LEVEL_ON_TOP 2
+#define STACKING_LEVEL_MODAL1 1024
+
+
+#define STACKING_IS_HIDDEN(ST) ((ST)->hidden)
+
+
+DECLSTRUCT(WStacking){
+    WRegion *reg;
+    WStacking *next, *prev;
+    WStacking *above;
+    uint level;
+    WSizePolicy szplcy;
+    WStacking *mgr_next, *mgr_prev;
+    
+    /* flags */
+    uint to_unweave:2;
+    uint hidden:1;
+    
+    /* WMPlex stuff */
+    WLListNode *lnode;
+};
+
+
+typedef bool WStackingFilter(WStacking *st, void *data);
+typedef WStacking *WStackingIterator(void *data);
+
+
+DECLSTRUCT(WStackingIterTmp){
+    WStacking *st;
+    WStackingFilter *filt;
+    void *filt_data;
+};
+
+
+WStacking **window_get_stackingp(WWindow *wwin);
+WStacking *window_get_stacking(WWindow *wwin);
+
+
+WStacking *create_stacking();
+
+void stacking_free(WStacking *st);
+
+/* Returns the topmost node with 'above' pointing to st. */
+WStacking *stacking_unstack(WWindow *par, WStacking *st);
+
+void stacking_iter_init(WStackingIterTmp *tmp,
+                        WStacking *st,
+                        WStackingFilter *filt,
+                        void *filt_data);
+WRegion *stacking_iter(WStackingIterTmp *tmp);
+WStacking *stacking_iter_nodes(WStackingIterTmp *tmp);
+
+void stacking_iter_mgr_init(WStackingIterTmp *tmp,
+                            WStacking *st,
+                            WStackingFilter *filt,
+                            void *filt_data);
+WRegion *stacking_iter_mgr(WStackingIterTmp *tmp);
+WStacking *stacking_iter_mgr_nodes(WStackingIterTmp *tmp);
+
+void stacking_weave(WStacking **stacking, WStacking **np, bool below);
+WStacking *stacking_unweave(WStacking **stacking, 
+                            WStackingFilter *filt, void *filt_data);
+
+void stacking_restack(WStacking **stacking, WStacking *st, Window fb_win,
+                      WStackingFilter *filt, void *filt_data, bool lower);
+
+WStacking *stacking_find_to_focus(WStacking *stacking, WStacking *to_try,
+                                  WStackingFilter *include_filt, 
+                                  WStackingFilter *approve_filt, 
+                                  void *filt_data);
+WStacking *stacking_find_to_focus_mapped(WStacking *stacking, 
+                                         WStacking *to_try,
+                                         WRegion *mgr);
+
+uint stacking_min_level(WStacking *stacking, 
+                        WStackingFilter *include_filt, 
+                        void *filt_data);
+
+uint stacking_min_level_mapped(WStacking *stacking);
+
+
+WStacking *ioncore_find_stacking(WRegion *reg);
+void stacking_unassoc(WStacking *stacking);
+bool stacking_assoc(WStacking *stacking, WRegion *reg);
+
+#endif /* ION_IONCORE_STACKING_H */
diff --git a/ioncore/strings.c b/ioncore/strings.c
new file mode 100644 (file)
index 0000000..9f2bbd3
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * ion/ioncore/strings.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/output.h>
+#include <libtu/misc.h>
+#include <string.h>
+#include <regex.h>
+#include "common.h"
+#include "global.h"
+#include "strings.h"
+
+
+/*{{{ String scanning */
+
+
+wchar_t str_wchar_at(char *p, int max)
+{
+    wchar_t wc;
+    if(mbtowc(&wc, p, max)>0)
+        return wc;
+    return 0;
+}
+    
+
+char *str_stripws(char *p)
+{
+    mbstate_t ps;
+    wchar_t wc;
+    int first=-1, pos=0;
+    int n=strlen(p);
+    int ret;
+    
+    memset(&ps, 0, sizeof(ps));
+    
+    while(1){
+        ret=mbrtowc(&wc, p+pos, n-pos, &ps);
+        if(ret<=0)
+            break;
+        if(!iswspace(wc))
+            break;
+        pos+=ret;
+    }
+    
+    if(pos!=0)
+        memmove(p, p+pos, n-pos+1);
+    
+    if(ret<=0)
+        return p;
+    
+    pos=ret;
+    
+    while(1){
+        ret=mbrtowc(&wc, p+pos, n-pos, &ps);
+        if(ret<=0)
+            break;
+        if(iswspace(wc)){
+            if(first==-1)
+                first=pos;
+        }else{
+            first=-1;
+        }
+        pos+=ret;
+    }
+        
+    if(first!=-1)
+        p[first]='\0';
+    
+    return p;
+}
+
+
+int str_prevoff(const char *p, int pos)
+{
+    if(ioncore_g.enc_sb)
+        return (pos>0 ? 1 : 0);
+
+    if(ioncore_g.enc_utf8){
+        int opos=pos;
+        
+        while(pos>0){
+            pos--;
+            if((p[pos]&0xC0)!=0x80)
+                break;
+        }
+        return opos-pos;
+    }
+    
+    assert(ioncore_g.use_mb);
+    {
+        /* *sigh* */
+        int l, prev=0;
+        mbstate_t ps;
+        
+        memset(&ps, 0, sizeof(ps));
+        
+        while(1){
+            l=mbrlen(p+prev, pos-prev, &ps);
+            if(l<0){
+                warn(TR("Invalid multibyte string."));
+                return 0;
+            }
+            if(prev+l>=pos)
+                return pos-prev;
+            prev+=l;
+        }
+        
+    }
+}
+
+
+int str_nextoff(const char *p, int opos)
+{
+    if(ioncore_g.enc_sb)
+        return (*(p+opos)=='\0' ? 0 : 1);
+
+    if(ioncore_g.enc_utf8){
+        int pos=opos;
+        
+        while(p[pos]){
+            pos++;
+            if((p[pos]&0xC0)!=0x80)
+                break;
+        }
+        return pos-opos;
+    }
+
+    assert(ioncore_g.use_mb);
+    {
+        mbstate_t ps;
+        int l;
+        memset(&ps, 0, sizeof(ps));
+        
+        l=mbrlen(p+opos, strlen(p+opos), &ps);
+        if(l<0){
+            warn(TR("Invalid multibyte string."));
+            return 0;
+        }
+        return l;
+    }
+}
+
+
+int str_len(const char *p)
+{
+    if(ioncore_g.enc_sb)
+        return strlen(p);
+
+    if(ioncore_g.enc_utf8){
+        int len=0;
+        
+        while(*p){
+            if(((*p)&0xC0)!=0x80)
+                len++;
+            p++;
+        }
+        return len;
+    }
+
+    assert(ioncore_g.use_mb);
+    {
+        mbstate_t ps;
+        int len=0, bytes=strlen(p), l;
+        memset(&ps, 0, sizeof(ps));
+        
+        while(bytes>0){
+            l=mbrlen(p, bytes, &ps);
+            if(l<=0){
+                warn(TR("Invalid multibyte string."));
+                break;
+            }
+            len++;
+            bytes-=l;
+        }
+        return len;
+    }
+    
+}
+
+/*}}}*/
+
+
+/*{{{ Title shortening */
+
+
+static char *scatn3(const char *p1, int l1,
+                    const char *p2, int l2,
+                    const char *p3, int l3)
+{
+    char *p=ALLOC_N(char, l1+l2+l3+1);
+    
+    if(p!=NULL){
+        strncat(p, p1, l1);
+        strncat(p, p2, l2);
+        strncat(p, p3, l3);
+    }
+    return p;
+}
+
+INTRSTRUCT(SR);
+    
+DECLSTRUCT(SR){
+    regex_t re;
+    char *rule;
+    SR *next, *prev;
+    bool always;
+};
+
+
+static SR *shortenrules=NULL;
+
+
+/*EXTL_DOC
+ * Add a rule describing how too long titles should be shortened to fit in tabs.
+ * The regular expression \var{rx} (POSIX, not Lua!) is used to match titles
+ * and when \var{rx} matches, \var{rule} is attempted to use as a replacement
+ * for title. If \var{always} is set, the rule is used even if no shortening 
+ * is necessary.
+ *
+ * Similarly to sed's 's' command, \var{rule} may contain characters that are
+ * inserted in the resulting string and specials as follows:
+ * 
+ * \begin{tabularx}{\linewidth}{lX}
+ *  \tabhead{Special & Description}
+ *  \$0 &          Place the original string here. \\
+ *  \$1 to \$9 & Insert n:th capture here (as usual,captures are surrounded
+ *                 by parentheses in the regex). \\
+ *  \$| &          Alternative shortening separator. The shortening described
+ *                 before the first this kind of separator is tried first and
+ *                 if it fails to make the string short enough, the next is 
+ *                  tried, and so on. \\
+ *  \$< &         Remove characters on the left of this marker to shorten the
+ *                 string. \\
+ *  \$> &         Remove characters on the right of this marker to shorten the
+ *                 string. Only the first \$< or \$> within an alternative 
+ *                 shortening is used. \\
+ * \end{tabularx}
+ */
+EXTL_EXPORT
+bool ioncore_defshortening(const char *rx, const char *rule, bool always)
+{
+    SR *si;
+    int ret;
+    #define ERRBUF_SIZE 256
+    static char errbuf[ERRBUF_SIZE];
+        
+    if(rx==NULL || rule==NULL)
+        return FALSE;
+    
+    si=ALLOC(SR);
+
+    if(si==NULL)
+        return FALSE;
+    
+    ret=regcomp(&(si->re), rx, REG_EXTENDED);
+    
+    if(ret!=0){
+        errbuf[0]='\0';
+        regerror(ret, &(si->re), errbuf, ERRBUF_SIZE);
+        warn(TR("Error compiling regular expression: %s"), errbuf);
+        goto fail2;
+    }
+    
+    si->rule=scopy(rule);
+    si->always=always;
+    
+    if(si->rule==NULL)
+        goto fail;
+    
+    LINK_ITEM(shortenrules, si, next, prev);
+    
+    return TRUE;
+    
+fail:
+    regfree(&(si->re));
+fail2:
+    free(si);
+    return FALSE;
+}
+
+
+static char *shorten(GrBrush *brush, const char *str, uint maxw,
+                     const char *rule, int nmatch, regmatch_t *pmatch)
+{
+    char *s;
+    int rulelen, slen, i, j, k, ll;
+    int strippt=0;
+    int stripdir=-1;
+    bool more=FALSE;
+    
+    /* Ensure matches are at character boundaries */
+    if(!ioncore_g.enc_sb){
+        int pos=0, len, strl;
+        mbstate_t ps;
+        memset(&ps, 0, sizeof(ps));
+        
+        strl=strlen(str);
+
+        while(pos<strl){
+            len=mbrtowc(NULL, str+pos, strl-pos, &ps);
+            if(len<0){
+                /* Invalid multibyte string */
+                return scopy("???");
+            }
+            if(len==0)
+                break;
+            for(i=0; i<nmatch; i++){
+                if(pmatch[i].rm_so>pos && pmatch[i].rm_so<pos+len)
+                    pmatch[i].rm_so=pos+len;
+                if(pmatch[i].rm_eo>pos && pmatch[i].rm_eo<pos+len)
+                    pmatch[i].rm_eo=pos;
+            }
+            pos+=len;
+        }
+    }
+
+    /* Stupid alloc rule that wastes space */
+    rulelen=strlen(rule);
+    slen=rulelen;
+    
+    for(i=0; i<nmatch; i++){
+        if(pmatch[i].rm_so==-1)
+            continue;
+        slen+=(pmatch[i].rm_eo-pmatch[i].rm_so);
+    }
+    
+    s=ALLOC_N(char, slen);
+    
+    if(s==NULL)
+        return NULL;
+    
+    do{
+        more=FALSE;
+        j=0;
+        strippt=0;
+        stripdir=-1;
+        
+        for(i=0; i<rulelen; i++){
+            if(rule[i]!='$'){
+                s[j++]=rule[i];
+                continue;
+            }
+            
+            i++;
+            
+            if(rule[i]=='|'){
+                rule=rule+i+1;
+                rulelen=rulelen-i-1;
+                more=TRUE;
+                break;
+            }
+            
+            if(rule[i]=='$'){
+                s[j++]='$';
+                continue;
+            }
+            
+            if(rule[i]=='<'){
+                strippt=j;
+                stripdir=-1;
+                continue;
+            }
+            
+            if(rule[i]=='>'){
+                strippt=j;
+                stripdir=1;
+                continue;
+            }
+            
+            if(rule[i]>='0' && rule[i]<='9'){
+                k=(int)(rule[i]-'0');
+                if(k>=nmatch)
+                    continue;
+                if(pmatch[k].rm_so==-1)
+                    continue;
+                ll=(pmatch[k].rm_eo-pmatch[k].rm_so);
+                strncpy(s+j, str+pmatch[k].rm_so, ll);
+                j+=ll;
+            }
+        }
+        
+        slen=j;
+        s[slen]='\0';
+        
+        i=strippt;
+        j=strippt;
+        
+        /* shorten */
+        {
+            uint bl=grbrush_get_text_width(brush, s, i);
+            uint el=grbrush_get_text_width(brush, s+j, slen-j);
+
+            while(1){
+                /* el+bl may not be the actual length, but close enough. */
+                if(el+bl<=maxw){
+                    memmove(s+i, s+j, slen-j+1);
+                    return s;
+                }
+                
+                if(stripdir==-1){
+                    ll=str_prevoff(s, i);
+                    if(ll==0)
+                        break;
+                    i-=ll;
+                    bl=grbrush_get_text_width(brush, s, i);
+                }else{
+                    ll=str_nextoff(s, j);
+                    if(ll==0)
+                        break;
+                    j+=ll;
+                    el=grbrush_get_text_width(brush, s+j, slen-j);
+                }
+            }
+        }
+    }while(more);
+        
+    free(s);
+
+    return NULL;
+}
+
+
+char *grbrush_make_label(GrBrush *brush, const char *str, uint maxw)
+{
+    size_t nmatch=10;
+    regmatch_t pmatch[10];
+    SR *rule;
+    int ret;
+    char *retstr;
+    bool fits=FALSE;
+    
+    if(grbrush_get_text_width(brush, str, strlen(str))<=maxw)
+        fits=TRUE;
+        
+        /*return scopy(str);*/
+    
+    for(rule=shortenrules; rule!=NULL; rule=rule->next){
+        if(fits && !rule->always)
+            continue;
+        ret=regexec(&(rule->re), str, nmatch, pmatch, 0);
+        if(ret!=0)
+            continue;
+        retstr=shorten(brush, str, maxw, rule->rule, nmatch, pmatch);
+        goto rettest;
+    }
+
+    if(fits){
+        retstr=scopy(str);
+    }else{
+        pmatch[0].rm_so=0;
+        pmatch[0].rm_eo=strlen(str)-1;
+        retstr=shorten(brush, str, maxw, "$1$<...", 1, pmatch);
+    }
+    
+rettest:
+    if(retstr!=NULL)
+        return retstr;
+    return scopy("");
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/strings.h b/ioncore/strings.h
new file mode 100644 (file)
index 0000000..0f2a1f4
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ion/ioncore/strings.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_STRINGS_H
+#define ION_IONCORE_STRINGS_H
+
+#include "common.h"
+
+#ifdef CF_NO_LOCALE
+#include "dummywc.h"
+#else
+#include <wchar.h>
+#include <wctype.h>
+#endif
+
+#include "gr.h"
+
+extern bool ioncore_defshortening(const char *rx, const char *rule,
+                                  bool always);
+
+extern char *grbrush_make_label(GrBrush *brush, const char *str, uint maxw);
+                                       
+extern int str_nextoff(const char *p, int pos);
+extern int str_prevoff(const char *p, int pos);
+extern int str_len(const char *p);
+extern wchar_t str_wchar_at(char *p, int max);
+extern char *str_stripws(char *p);
+
+#endif /* ION_IONCORE_STRINGS_H */
diff --git a/ioncore/tags.c b/ioncore/tags.c
new file mode 100644 (file)
index 0000000..6b7d5c2
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * ion/ioncore/tags.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/objlist.h>
+#include <libtu/setparam.h>
+#include "region.h"
+#include "tags.h"
+
+
+static ObjList *taglist=NULL;
+
+
+/*{{{ Adding/removing tags */
+
+
+bool region_set_tagged(WRegion *reg, int sp)
+{
+    bool set=(reg->flags&REGION_TAGGED);
+    bool nset=libtu_do_setparam(sp, set);
+    
+    if(XOR(nset, set)){
+        if(reg->flags&REGION_TAGGED){
+            reg->flags&=~REGION_TAGGED;
+            objlist_remove(&taglist, (Obj*)reg);
+        }else{
+            reg->flags|=REGION_TAGGED;
+            objlist_insert_last(&taglist, (Obj*)reg);
+        }
+        region_notify_change(reg, "tag");
+    }
+
+    return nset;
+}
+
+
+/*EXTL_DOC
+ * Change tagging state of \var{reg} as defined by \var{how}
+ * (set/unset/toggle). Resulting state is returned.
+ */
+EXTL_EXPORT_AS(WRegion, set_tagged)
+bool region_set_tagged_extl(WRegion *reg, const char *how)
+{
+    return region_set_tagged(reg, libtu_string_to_setparam(how));
+}
+
+
+/*EXTL_DOC
+ * Is \var{reg} tagged?
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+bool region_is_tagged(WRegion *reg)
+{
+    return ((reg->flags&REGION_TAGGED)!=0);
+}
+
+
+/*EXTL_DOC
+ * Untag all regions.
+ */
+EXTL_EXPORT
+void ioncore_clear_tags()
+{
+    while(ioncore_tags_take_first()!=NULL)
+        /* nothing */;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Iteration */
+
+
+/*EXTL_DOC
+ * Returns first tagged object.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+WRegion *ioncore_tags_first()
+{
+    return (WRegion*)OBJLIST_FIRST(WRegion*, taglist);
+}
+
+
+WRegion *ioncore_tags_take_first()
+{
+    WRegion *reg=(WRegion*)objlist_take_first(&taglist);
+    
+    if(reg!=NULL){
+        reg->flags&=~REGION_TAGGED;
+        region_notify_change(reg, "tag");
+    }
+    
+    return reg;
+}
+
+/*EXTL_DOC
+ * Returns a list of tagged regions.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab ioncore_tagged_list()
+{
+    int n=0;
+    ExtlTab tab;
+    WRegion *region;
+    ObjListIterTmp tmp;
+
+    region=ioncore_tags_first();
+    if(!region)
+        return extl_table_none();
+
+    tab=extl_create_table();
+
+    FOR_ALL_ON_OBJLIST(WRegion*, region, taglist, tmp){
+        if(extl_table_seti_o(tab, n+1, (Obj*)region))
+            n++;
+    }
+
+    return tab;
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/tags.h b/ioncore/tags.h
new file mode 100644 (file)
index 0000000..478d47b
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * ion/ioncore/tags.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_TAGS_H
+#define ION_IONCORE_TAGS_H
+
+#include <libtu/setparam.h>
+#include "region.h"
+
+extern bool region_set_tagged(WRegion *reg, int sp);
+extern bool region_is_tagged(WRegion *reg);
+
+extern void ioncore_clear_tags();
+extern WRegion *ioncore_tags_first();
+extern WRegion *ioncore_tags_take_first();
+
+#endif /* ION_IONCORE_TAGS_H */
diff --git a/ioncore/window.c b/ioncore/window.c
new file mode 100644 (file)
index 0000000..8d2986f
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * ion/ioncore/window.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+
+#include "common.h"
+#include "global.h"
+#include "window.h"
+#include "focus.h"
+#include "rootwin.h"
+#include "region.h"
+#include "xwindow.h"
+#include "region-iter.h"
+
+
+/*{{{ Dynfuns */
+
+
+void window_draw(WWindow *wwin, bool complete)
+{
+    CALL_DYN(window_draw, wwin, (wwin, complete));
+}
+
+
+void window_insstr(WWindow *wwin, const char *buf, size_t n)
+{
+    CALL_DYN(window_insstr, wwin, (wwin, buf, n));
+}
+
+
+int window_press(WWindow *wwin, XButtonEvent *ev, WRegion **reg_ret)
+{
+    int area=0;
+    CALL_DYN_RET(area, int, window_press, wwin, (wwin, ev, reg_ret));
+    return area;
+}
+
+
+void window_release(WWindow *wwin)
+{
+    CALL_DYN(window_release, wwin, (wwin));
+}
+
+
+/*}}}*/
+    
+
+/*{{{ Init, create */
+
+
+bool window_do_init(WWindow *wwin, WWindow *par, Window win,
+                    const WFitParams *fp)
+{
+    wwin->win=win;
+    wwin->xic=NULL;
+    wwin->event_mask=0;
+    wwin->stacking=NULL;
+    
+    region_init(&(wwin->region), par, fp);
+    
+    if(win!=None){
+        XSaveContext(ioncore_g.dpy, win, ioncore_g.win_context, 
+                     (XPointer)wwin);
+    }
+    
+    return TRUE;
+}
+
+
+bool window_init(WWindow *wwin, WWindow *par, const WFitParams *fp)
+{
+    Window win;
+    
+    win=create_xwindow(region_rootwin_of((WRegion*)par),
+                       par->win, &(fp->g));
+    if(win==None)
+        return FALSE;
+    /* window_init does not fail */
+    return window_do_init(wwin, par, win, fp);
+}
+
+
+void window_deinit(WWindow *wwin)
+{
+    region_deinit((WRegion*)wwin);
+    
+    if(wwin->xic!=NULL)
+        XDestroyIC(wwin->xic);
+
+    if(wwin->win!=None){
+        XDeleteContext(ioncore_g.dpy, wwin->win, ioncore_g.win_context);
+        XDestroyWindow(ioncore_g.dpy, wwin->win);
+    }
+    
+    /* There are no backlinks from WStacking to us, so it is not
+     * necessary to do any deinitialisation there.
+     */
+}
+
+
+/*}}}*/
+
+
+/*{{{ Region dynfuns */
+
+
+static void window_notify_subs_rootpos(WWindow *wwin, int x, int y)
+{
+    WRegion *sub;
+    
+    FOR_ALL_CHILDREN(wwin, sub){
+        region_notify_rootpos(sub,
+                              x+REGION_GEOM(sub).x, 
+                              y+REGION_GEOM(sub).y);
+    }
+}
+
+
+void window_notify_subs_move(WWindow *wwin)
+{
+    int x=0, y=0;
+    region_rootpos(&(wwin->region), &x, &y);
+    window_notify_subs_rootpos(wwin, x, y);
+}
+
+
+void window_do_fitrep(WWindow *wwin, WWindow *par, const WRectangle *geom)
+{
+    bool move=(REGION_GEOM(wwin).x!=geom->x ||
+               REGION_GEOM(wwin).y!=geom->y);
+    int w=maxof(1, geom->w);
+    int h=maxof(1, geom->h);
+
+    if(par!=NULL){
+        region_unset_parent((WRegion*)wwin);
+        XReparentWindow(ioncore_g.dpy, wwin->win, par->win, geom->x, geom->y);
+        XResizeWindow(ioncore_g.dpy, wwin->win, w, h);
+        region_set_parent((WRegion*)wwin, par);
+    }else{
+        XMoveResizeWindow(ioncore_g.dpy, wwin->win, geom->x, geom->y, w, h);
+    }
+    
+    REGION_GEOM(wwin)=*geom;
+
+    if(move)
+        window_notify_subs_move(wwin);
+}
+
+
+bool window_fitrep(WWindow *wwin, WWindow *par, const WFitParams *fp)
+{
+    if(par!=NULL && !region_same_rootwin((WRegion*)wwin, (WRegion*)par))
+        return FALSE;
+    window_do_fitrep(wwin, par, &(fp->g));
+    return TRUE;
+}
+
+
+void window_map(WWindow *wwin)
+{
+    XMapWindow(ioncore_g.dpy, wwin->win);
+    REGION_MARK_MAPPED(wwin);
+}
+
+
+void window_unmap(WWindow *wwin)
+{
+    XUnmapWindow(ioncore_g.dpy, wwin->win);
+    REGION_MARK_UNMAPPED(wwin);
+}
+
+
+void window_do_set_focus(WWindow *wwin, bool warp)
+{
+    region_finalise_focusing((WRegion*)wwin, wwin->win, warp);
+}
+
+
+void window_restack(WWindow *wwin, Window other, int mode)
+{
+    xwindow_restack(wwin->win, other, mode);
+}
+
+
+Window window_xwindow(const WWindow *wwin)
+{
+    return wwin->win;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+/*EXTL_DOC
+ * Return the X window id for \var{wwin}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+double window_xid(WWindow *wwin)
+{
+    return wwin->win;
+}
+
+
+void window_select_input(WWindow *wwin, long event_mask)
+{
+    XSelectInput(ioncore_g.dpy, wwin->win, event_mask);
+    wwin->event_mask=event_mask;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab window_dynfuntab[]={
+    {region_map, window_map},
+    {region_unmap, window_unmap},
+    {region_do_set_focus, window_do_set_focus},
+    {(DynFun*)region_fitrep, (DynFun*)window_fitrep},
+    {(DynFun*)region_xwindow, (DynFun*)window_xwindow},
+    {region_notify_rootpos, window_notify_subs_rootpos},
+    {region_restack, window_restack},
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WWindow, WRegion, window_deinit, window_dynfuntab);
+
+    
+/*}}}*/
+
diff --git a/ioncore/window.h b/ioncore/window.h
new file mode 100644 (file)
index 0000000..b9f4ffa
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * ion/ioncore/window.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_WINDOW_H
+#define ION_IONCORE_WINDOW_H
+
+#include "common.h"
+#include "region.h"
+#include "binding.h"
+#include "rectangle.h"
+
+
+DECLCLASS(WWindow){
+    WRegion region;
+    Window win;
+    XIC xic;
+    long event_mask;
+    WStacking *stacking;
+};
+
+
+extern bool window_init(WWindow *p, WWindow *parent, 
+                        const WFitParams *fp);
+extern bool window_do_init(WWindow *p, WWindow *parent, Window win, 
+                           const WFitParams *fp);
+extern void window_deinit(WWindow *win);
+
+DYNFUN void window_draw(WWindow *wwin, bool complete);
+DYNFUN void window_insstr(WWindow *wwin, const char *buf, size_t n);
+DYNFUN int window_press(WWindow *wwin, XButtonEvent *ev, WRegion **reg_ret);
+DYNFUN void window_release(WWindow *wwin);
+
+/* Only to be used by regions that inherit this */
+extern void window_map(WWindow *wwin);
+extern void window_unmap(WWindow *wwin);
+
+extern void window_do_set_focus(WWindow *wwin, bool warp);
+
+extern void window_do_fitrep(WWindow *wwin, WWindow *parent,
+                             const WRectangle *geom);
+extern bool window_fitrep(WWindow *wwin, WWindow *parent, 
+                          const WFitParams *fp);
+extern void window_notify_subs_move(WWindow *wwin);
+
+extern void window_restack(WWindow *wwin, Window other, int mode);
+
+extern void window_select_input(WWindow *wwin, long event_mask);
+
+#endif /* ION_IONCORE_WINDOW_H */
diff --git a/ioncore/xic.c b/ioncore/xic.c
new file mode 100644 (file)
index 0000000..304adf4
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * ion/ioncore/xic.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include "common.h"
+#include "global.h"
+#include "ioncore.h"
+
+
+static XIM input_method=NULL;
+static XIMStyle input_style=(XIMPreeditNothing|XIMStatusNothing);
+
+
+void ioncore_init_xim(void)
+{
+    char *p;
+    int i;
+    XIM xim=NULL;
+    XIMStyles *xim_styles = NULL;
+      bool found=FALSE;
+
+    if((p=XSetLocaleModifiers(""))!=NULL && *p)
+        xim=XOpenIM(ioncore_g.dpy, NULL, NULL, NULL);
+    
+    if(xim==NULL && (p=XSetLocaleModifiers("@im=none"))!=NULL && *p)
+        xim=XOpenIM(ioncore_g.dpy, NULL, NULL, NULL);
+
+    if(xim==NULL){
+        ioncore_warn_nolog(TR("Failed to open input method."));
+        return;
+    }
+    
+    if(XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL) || !xim_styles) {
+        ioncore_warn_nolog(TR("Input method doesn't support any style."));
+        XCloseIM(xim);
+        return;
+    }
+    
+    for(i=0; (ushort)i<xim_styles->count_styles; i++){
+        if(input_style==xim_styles->supported_styles[i]){
+            found=TRUE;
+            break;
+        }
+    }
+    
+    XFree(xim_styles);
+    
+    if(!found){
+        ioncore_warn_nolog(TR("input method doesn't support my preedit type."));
+        XCloseIM(xim);
+        return;
+    }
+
+    input_method=xim;    
+}
+
+
+XIC xwindow_create_xic(Window win)
+{
+    /*static bool tried=FALSE;*/
+    XIC xic;
+    
+    /*
+    if(input_method==NULL && !tried){
+        init_xlocale();
+        tried=TRUE;
+    }*/
+    
+    if(input_method==NULL)
+        return NULL;
+    
+    xic=XCreateIC(input_method, XNInputStyle, input_style,
+                  XNClientWindow, win, XNFocusWindow, win,
+                  NULL);
+    
+    if(xic==NULL)
+        warn(TR("Failed to create input context."));
+
+    return xic;
+}
+
+
+bool window_create_xic(WWindow *wwin)
+{
+    if(wwin->xic==NULL)
+        wwin->xic=xwindow_create_xic(wwin->win);
+    return (wwin->xic!=NULL);
+}
diff --git a/ioncore/xic.h b/ioncore/xic.h
new file mode 100644 (file)
index 0000000..dcc3de6
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * ion/ioncore/xic.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_XIC_H
+#define ION_IONCORE_XIC_H
+
+#include "common.h"
+#include "window.h"
+
+extern XIC xwindow_create_xic(Window win);
+
+extern bool window_create_xic(WWindow *wwin);
+
+extern void ioncore_init_xim(void);
+
+#endif /* ION_IONCORE_XIC_H */
diff --git a/ioncore/xwindow.c b/ioncore/xwindow.c
new file mode 100644 (file)
index 0000000..16491c7
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * ion/ioncore/xwindow.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/minmax.h>
+#include "common.h"
+#include "global.h"
+#include "xwindow.h"
+#include "cursor.h"
+#include "sizehint.h"
+
+
+/*{{{ X window->region mapping */
+
+
+WRegion *xwindow_region_of(Window win)
+{
+    WRegion *reg;
+    
+    if(XFindContext(ioncore_g.dpy, win, ioncore_g.win_context,
+                    (XPointer*)&reg)!=0)
+        return NULL;
+    
+    return reg;
+}
+
+
+WRegion *xwindow_region_of_t(Window win, const ClassDescr *descr)
+{
+    WRegion *reg=xwindow_region_of(win);
+    
+    if(reg==NULL)
+        return NULL;
+    
+    if(!obj_is((Obj*)reg, descr))
+        return NULL;
+    
+    return reg;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Create */
+
+
+Window create_xwindow(WRootWin *rw, Window par, const WRectangle *geom)
+{
+    int w=maxof(1, geom->w);
+    int h=maxof(1, geom->h);
+    
+    return XCreateSimpleWindow(ioncore_g.dpy, par, geom->x, geom->y, w, h,
+                               0, 0, BlackPixel(ioncore_g.dpy, rw->xscr));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Restack */
+
+
+void xwindow_restack(Window win, Window other, int stack_mode)
+{
+    XWindowChanges wc;
+    int wcmask;
+    
+    wcmask=CWStackMode;
+    wc.stack_mode=stack_mode;
+    if(other!=None){
+        wc.sibling=other;
+        wcmask|=CWSibling;
+    }
+
+    XConfigureWindow(ioncore_g.dpy, win, wcmask, &wc);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Focus */
+
+
+void xwindow_do_set_focus(Window win)
+{
+    XSetInputFocus(ioncore_g.dpy, win, RevertToParent, CurrentTime);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Pointer and cursors */
+
+void xwindow_set_cursor(Window win, int cursor)
+{
+    XDefineCursor(ioncore_g.dpy, win, ioncore_xcursor(cursor));
+}
+
+
+bool xwindow_pointer_pos(Window rel, int *px, int *py)
+{
+    Window win=None, realroot=None;
+    int wx=0, wy=0;
+    uint mask=0;
+    return XQueryPointer(ioncore_g.dpy, rel, &realroot, &win,
+                         px, py, &wx, &wy, &mask);
+}
+
+/*}}}*/
+
+
+/*{{{ Size hints */
+
+
+void xwindow_get_sizehints(Window win, XSizeHints *hints)
+{
+    int minh, minw;
+    long supplied=0;
+    
+    memset(hints, 0, sizeof(*hints));
+    XGetWMNormalHints(ioncore_g.dpy, win, hints, &supplied);
+    
+    xsizehints_sanity_adjust(hints);
+}
+
+
+/*}}}*/
+
diff --git a/ioncore/xwindow.h b/ioncore/xwindow.h
new file mode 100644 (file)
index 0000000..50e7db5
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ion/ioncore/xwindow.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_IONCORE_XWINDOW_H
+#define ION_IONCORE_XWINDOW_H
+
+#include "common.h"
+#include "rectangle.h"
+
+#define XWINDOW_REGION_OF_T(WIN, TYPE) (TYPE*)xwindow_region_of_t(WIN, &CLASSDESCR(TYPE))
+#define XWINDOW_REGION_OF(WIN) xwindow_region_of(WIN)
+
+extern Window create_xwindow(WRootWin *rw, Window par,
+                             const WRectangle *geom);
+
+extern WRegion *xwindow_region_of(Window win);
+extern WRegion *xwindow_region_of_t(Window win, const ClassDescr *descr);
+
+extern void xwindow_restack(Window win, Window other, int stack_mode);
+
+extern void xwindow_do_set_focus(Window win);
+
+extern void xwindow_set_cursor(Window win, int cursor);
+
+extern void xwindow_get_sizehints(Window win, XSizeHints *hints);
+
+extern bool xwindow_pointer_pos(Window rel, int *px, int *py);
+
+#endif /* ION_IONCORE_XWINDOW_H */
diff --git a/libextl/LICENSE b/libextl/LICENSE
new file mode 100644 (file)
index 0000000..cf9b6b9
--- /dev/null
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  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 Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            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 Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+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 other code 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.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+^L
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  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, whereas the latter must
+be combined with the library in order to run.
+^L
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser 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.
+^L
+  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.
+^L
+  6. As an exception to the Sections above, you may also combine 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) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) 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.
+
+    d) 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.
+
+    e) 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 materials to be 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.
+^L
+  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 with
+this License.
+^L
+  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 Lesser 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.
+^L
+  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
+^L
+           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 Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  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/libextl/Makefile b/libextl/Makefile
new file mode 100644 (file)
index 0000000..01f40ac
--- /dev/null
@@ -0,0 +1,42 @@
+##
+## Extl Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=.
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(LIBTU_INCLUDES) $(LUA_INCLUDES)
+
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES=readconfig.c luaextl.c misc.c
+
+HEADERS=readconfig.h extl.h luaextl.h private.h types.h
+
+TARGETS=libextl.a libextl-mkexports
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+libextl.a: $(OBJS)
+       $(AR) $(ARFLAGS) $@ $+
+       $(RANLIB) $@
+
+libextl-mkexports: libextl-mkexports.in
+       sed "1s:LUA50:$(LUA):" $< > $@
+
+install:
+       $(INSTALLDIR) $(BINDIR)
+       $(INSTALL) -m $(BIN_MODE) libextl-mkexports $(BINDIR)
+       $(INSTALLDIR) $(LIBDIR)
+       $(INSTALL) -m $(DATA_MODE) libextl.a $(LIBDIR)
+       $(INSTALLDIR) $(INCDIR)
+       for h in $(HEADERS); do \
+               $(INSTALL) -m $(DATA_MODE) $$h $(INCDIR); \
+       done
diff --git a/libextl/README b/libextl/README
new file mode 100644 (file)
index 0000000..dee3de7
--- /dev/null
@@ -0,0 +1,116 @@
+
+libextl
+
+Copyright (c) Tuomo Valkonen 2003-2005.
+<tuomov at iki.fi>
+
+Libextl is a small library for very easily extending programs with Lua
+scripting. By default the library depends on my libtu available from the
+same repository. However, this can be changed by redefining things in
+private.h and types.h.
+
+Libextl supports exporting functions that operate on basic data types (int,
+bool, double, [const] char*) and references to Lua tables and functions
+(ExtlTab, ExtlFn) simply by prefixing the function definition with the
+keywords EXTL_EXPORT, EXTL_EXPORT_AS or EXTL_EXPORT_MEMBER. More complex
+data must, however, either be proxied libtu objects (or objects of some
+other object system with the appropriate macros redefined), or Lua tables.
+The binding glue is, however, generated as painlessly as for functions that
+operate on basic data types with all pointers to a type with a name that
+begins with an uppercase letter considered as such objects. Libextl also
+provides functions to manipulate Lua tables through references to these, and
+ways to call and load Lua code.
+
+
+BASIC USAGE
+
+Include <libextl/extl.h> in your source files.
+
+In your Makefile, process source files with libextl-mkexports to generate
+exports.c. Pass it the option '-module modname' if you want non-class
+functions to be put in a table "modname". (This will however introduce
+naming restrictions on the C side; see below.)
+
+Call 'exit_init' and '[modname_]register_exports' at the beginning of your
+program and '[modname_]unregister_exports' and 'extl_deinit' at the end of
+it.
+
+Mark exported functions as follows in your source files:
+
+  * Global or module functions:
+  
+      EXTL_EXPORT
+      int foobar(const char *s)
+      {
+          ...
+      }
+
+  * Classes and methods:
+   
+      EXTL_CLASS(Foo, Obj)
+   
+      EXTL_EXPORT_MEMBER
+      void foo_set_callback(Foo *cls, ExtlFn fn)
+
+    'Obj' here stands for the base class of the object system (one in libtu
+     by default), but more generally the second parameter to EXTL_CLASS must
+     be the name of parent class of the first parameter.
+
+  * Export in given module (plain table) or class:
+  
+      EXTL_EXPORT_AS(baz, somefun)
+      ExtlTab just_some_name_we_dont_use_on_the_lua_side()
+  
+
+If you pass libextl-mkexport the option '-module modname', then all
+EXTL_EXPORTed functions must be prefixed with 'modname_' and will be put in
+the global table 'modname'. If you want to export a function directly in the
+global namespace when building a module, use EXTL_EXPORT_AS(global,
+funcname).
+
+
+ADDITIONAL ROUTINES
+
+'luaextl.h' lists a number of routines for accessing references to tables,
+and calling Lua functions. How to use them should be fairly obvious.
+
+'readconfig.h' lists a number of routines to load source or compiled files
+from a specified path. Their usage should, again, be fairly obvious. These
+lookup routines are exported to the Lua side as well in the form of the
+function 'dopath'.
+
+
+USING ANOTHER OBJECT SYSTEM/USING WITHOUT LIBTU
+
+Redefine appropriate macros in private.h and types.h.
+
+
+NOTES ON DATA REFERENCES
+
+* CHAR* VS. CONST CHAR*
+
+libextl follows the following conventions with regard to const and non-const
+char* pointers:
+
+'const char*' as parameter is considered owned by the caller, and the called
+function must do nothing beyond accessing it during its execution.
+
+'char*' as parameter is considered owned by the called function, and it must
+take care of freeing the parameter when no longer needed.
+
+'const char*' as return value is considered owned by the called function,
+and caller must consider it unsafe to use after subsequent calls to related
+code.
+
+'char*' as return value is considered owned by the caller, and it must take
+care of freeing the value when no longer needed.
+
+
+* EXTLTAB AND EXTLFN
+
+These references are always owned as a caller. Thus, if a called function
+wants to store a reference to such a parameter, it must create a new one
+with extl_ref_fn or extl_ref_table. Note that these functions do not return
+the same reference as passed; there are no reference counters, just a table
+of references to hold the garbage collector from claiming the object
+(luaL_ref/luaL_unref).
diff --git a/libextl/build/system-inc.mk b/libextl/build/system-inc.mk
new file mode 100644 (file)
index 0000000..5390bc9
--- /dev/null
@@ -0,0 +1,2 @@
+TOPDIR := $(TOPDIR)/..
+include $(TOPDIR)/build/system-inc.mk
diff --git a/libextl/exact-version b/libextl/exact-version
new file mode 100644 (file)
index 0000000..3ff2401
--- /dev/null
@@ -0,0 +1,81 @@
+
+Context:
+
+[Updated *.mk locations.
+Tuomo Valkonen <tuomov@iki.fi>**20060803211018] 
+
+[Empty initialiser in libextl-mkexports was missing 'untraced'.
+Tuomo Valkonen <tuomov@iki.fi>**20060516162021] 
+
+[lua5.1 changes that aren't backwards compatible
+Etan Reisner <deryni@eden.rutgers.edu>**20060322164425] 
+
+[lua5.1 changes that should be backwards compatible
+Etan Reisner <deryni@eden.rutgers.edu>**20060322163746] 
+
+[Added untraced-option for exports.
+Tuomo Valkonen <tuomov@iki.fi>**20060506211657] 
+
+[Oops. Path lookup order was ignoring user cfgfiles after previous changes.
+Tuomo Valkonen <tuomov@iki.fi>**20050928194026] 
+
+[Added protect mode check function protected(fn|nil).
+Tuomo Valkonen <tuomov@iki.fi>**20050903145957] 
+
+[Oops, do go through files even if cfdir is NULL.
+Tuomo Valkonen <tuomov@iki.fi>**20050818171629] 
+
+[Changes in directory lookup order/policy.
+Tuomo Valkonen <tuomov@iki.fi>**20050817215809] 
+
+[Also try to read config files without an extension.
+Per Olofsson <pelle@dsv.su.se>**20050816144735
+ This change is needed so that /etc/default/ion3 can be read by the
+ ion3 Debian package.
+] 
+
+[Improvements to dead object handling.
+Tuomo Valkonen <tuomov@iki.fi>**20050703204209] 
+
+[Improved error reporting on passing around dead objects.
+Tuomo Valkonen <tuomov@iki.fi>**20050702205641] 
+
+[Documentation in Lua code wasn't being parsed anymore.
+Tuomo Valkonen <tuomov@iki.fi>**20050605194016
+ (Dynamic typing and changes in part of code...)
+] 
+
+[Added -reexport option.
+Tuomo Valkonen <tuomov@iki.fi>**20050421223739] 
+
+[libextl-mkexports can now generate exports.h.
+Tuomo Valkonen <tuomov@iki.fi>**20050319191302] 
+
+[README improvements.
+Tuomo Valkonen <tuomov@iki.fi>**20050316110102] 
+
+[It's 2005 already! (Updated copyright notices.)
+Tuomo Valkonen <tuomov@iki.fi>**20050314101157] 
+
+[Exported classes must now be marked for libextl-mkexports.
+Tuomo Valkonen <tuomov@iki.fi>**20050308082415
+ - EXTL_EXPORT before IMPLCLASS.
+] 
+
+[Added "protected mode".
+Tuomo Valkonen <tuomov@iki.fi>**20050301224511
+ - Exports can be marked "safe" to be called at any time with EXTL_EXPORT.
+ - New "safelist" API.
+] 
+
+[Execute install-sh through sh.
+Tuomo Valkonen <tuomov@iki.fi>**20050301214534] 
+
+[TAG libextl-3-svn2darcs
+Tuomo Valkonen <tuomov@iki.fi>**20050215180651] 
diff --git a/libextl/extl.h b/libextl/extl.h
new file mode 100644 (file)
index 0000000..8de38d3
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * libextl/extl.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#ifndef LIBEXTL_EXTL_H
+#define LIBEXTL_EXTL_H
+
+#include "luaextl.h"
+
+/* Keywords for libextl-mkexports script */
+
+#define EXTL_EXPORT
+#define EXTL_EXPORT_AS(T, F)
+#define EXTL_EXPORT_MEMBER
+#define EXTL_CLASS(CLS, PARCLS)
+#define EXTL_SAFE
+#define EXTL_UNTRACED
+
+#endif /* LIBEXTL_EXTL_H */
diff --git a/libextl/install-sh b/libextl/install-sh
new file mode 100644 (file)
index 0000000..e9de238
--- /dev/null
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+               chmodcmd=""
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/libextl/libextl-mkexports.in b/libextl/libextl-mkexports.in
new file mode 100644 (file)
index 0000000..ff1cac3
--- /dev/null
@@ -0,0 +1,803 @@
+#!LUA50
+-- -*- mode: lua -*-
+-- ion/mkexports.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2003-2005.
+-- 
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+-- 
+-- This is a script to automatically generate exported function registration
+-- code and documentation for those from C source.
+-- 
+-- The script can also parse documentation comments from Lua code.
+-- 
+
+-- Helper functions {{{
+
+function errorf(fmt, ...)
+    error(string.format(fmt, unpack(arg)), 2)
+end
+
+function matcherr(s)
+    error(string.format("Parse error in \"%s...\"", string.sub(s, 1, 50)), 2)
+end
+
+function fprintf(h, fmt, ...)
+    h:write(string.format(fmt, unpack(arg)))
+end
+
+function trim(str)
+    return string.gsub(str, "^[%s\n]*(.-)[%s\n]*$", "%1")
+end
+
+-- }}}
+
+
+-- Some conversion tables {{{
+
+desc2ct={
+    ["v"]="void",
+    ["i"]="int",
+    ["d"]="double",
+    ["b"]="bool",
+    ["t"]="ExtlTab",
+    ["f"]="ExtlFn",
+    ["o"]="Obj*",
+    ["s"]="char*",
+    ["S"]="const char*",
+}
+
+ct2desc={
+    ["uint"] = "i",
+}
+
+for d, t in pairs(desc2ct) do
+    ct2desc[t]=d
+end
+
+desc2human={
+    ["v"]="void",
+    ["i"]="integer",
+    ["d"]="double",
+    ["b"]="bool",
+    ["t"]="table",
+    ["f"]="function",
+    ["o"]="object",
+    ["s"]="string",
+    ["S"]="string",
+}
+
+-- }}}
+
+
+-- Parser {{{
+
+local classes={}
+local chnds={}
+local reexports={}
+
+function add_chnd(fnt)
+    local odesc=string.gsub(fnt.odesc, "S", "s")
+    local idesc=string.gsub(fnt.idesc, "S", "s")
+    local str="l2chnd_" .. odesc .. "_" .. idesc .. "_"
+    
+    for i, t in ipairs(fnt.itypes) do
+        str=str .. "_" .. t
+    end
+    
+    chnds[str]={odesc=odesc, idesc=idesc, itypes=fnt.itypes}
+    fnt.chnd=str
+end
+
+function add_class(cls)
+    if cls~="Obj" and not classes[cls] then
+        classes[cls]={}
+    end
+end
+
+function sort_classes(cls)
+    local sorted={}
+    local inserted={}
+    
+    local function insert(cls)
+        if classes[cls] and not inserted[cls] then
+            if classes[cls].parent then
+                insert(classes[cls].parent)
+            end
+            inserted[cls]=true
+            table.insert(sorted, cls)
+        end
+    end
+    
+    for cls in pairs(classes) do
+        insert(cls)
+    end
+    
+    return sorted
+end
+
+function parse_type(t)
+    local desc, otype, varname="?", "", ""
+    
+    -- Remove whitespace at start end end of the string and compress elsewhere.
+    t=string.gsub(trim(t), "[%s\n]+", " ")
+    -- Remove spaces around asterisks.
+    t=string.gsub(t, " *%* *", "*")
+    -- Add space after asterisks.
+    t=string.gsub(t, "%*", "* ")
+    
+    -- Check for const
+    local is_const=""
+    local s, e=string.find(t, "^const +")
+    if s then
+        is_const="const "
+        t=string.sub(t, e+1)
+    end
+    
+    -- Find variable name part
+    tn=t
+    s, e=string.find(tn, " ")
+    if s then
+        varname=string.sub(tn, e+1)
+        tn=string.sub(tn, 1, s-1)
+        assert(not string.find(varname, " "))
+    end
+    
+    -- Try to check for supported types
+    desc = ct2desc[is_const .. tn]
+    
+    if not desc or desc=="o" then
+        s, e=string.find(tn, "^[A-Z][%w_]*%*$")
+        if s then
+            desc="o"
+            otype=string.sub(tn, s, e-1)
+            add_class(otype)
+        else
+            errorf("Error parsing type from \"%s\"", t)
+        end
+    end
+    
+    return desc, otype, varname
+end
+
+function parse(d)
+    local doc=nil
+    local safe=false
+    local untraced=false
+    
+    -- Handle /*EXTL_DOC ... */
+    local function do_doc(s)
+        --s=string.gsub(s, "/%*EXTL_DOC(.-)%*/", "%1")
+        local st=string.len("/*EXTL_DOC")
+        local en, _=string.find(s, "%*/")
+        if not en then
+            errorf("Could not find end of comment in \"%s...\"",
+                   string.sub(s, 1, 50))
+        end
+        
+        s=string.sub(s, st+1, en-1)
+        s=string.gsub(s, "\n[%s]*%*", "\n")
+        doc=s
+    end
+
+    -- Handle EXTL_SAFE    
+    local function do_safe(s)
+        assert(not safe)
+        safe=true
+    end
+
+    -- Handle EXTL_UNTRACED
+    local function do_untraced(s)
+        assert(not untraced)
+        untraced=true
+    end
+    
+    local function do_do_export(cls, efn, ot, fn, param)
+        local odesc, otype=parse_type(ot)
+        local idesc, itypes, ivars="", {}, {}
+        
+        -- Parse arguments
+        param=string.sub(param, 2, -2)
+        if string.find(param, "[()]") then
+            errorf("Error: parameters to %s contain parantheses", fn)
+        end
+        param=trim(param)
+        if string.len(param)>0 then
+            for p in string.gfind(param .. ",", "([^,]*),") do
+                local spec, objtype, varname=parse_type(p)
+                idesc=idesc .. spec
+                table.insert(itypes, objtype)
+                table.insert(ivars, varname)
+            end
+        end
+        
+        if cls=="?" then
+            if string.sub(idesc, 1, 1)~="o" then
+                error("Invalid class for " .. fn)
+            end
+            cls=itypes[1]
+        end
+        
+        -- Generate call handler name
+        
+        local fninfo={
+            doc=doc,
+            safe=safe,
+            untraced=untraced,
+            odesc=odesc,
+            otype=otype,
+            idesc=idesc,
+            itypes=itypes,
+            ivars=ivars,
+            exported_name=efn,
+            class=cls,
+        }
+        
+        add_chnd(fninfo)
+        add_class(cls)
+        
+        if not classes[cls].fns then
+            classes[cls].fns={}
+        end
+        
+        assert(not classes[cls].fns[fn], "Function " .. fn .. " multiply defined!")
+
+        classes[cls].fns[fn]=fninfo
+
+        -- Reset
+        doc=nil
+        safe=false
+        untraced=false
+    end
+
+    -- Handle EXTL_EXPORT otype fn(args)
+    local function do_export(s)
+        local mdl, efn
+        local pat="EXTL_EXPORT[%s\n]+([%w%s_*]+[%s\n*])([%w_]+)[%s\n]*(%b())"
+        local st, en, ot, fn, param=string.find(s, pat)
+        
+        if not st then matcherr(s) end
+        
+        if module=="global" or not module then
+            efn=fn
+            mdl=module
+        else
+            st, en, efn=string.find(fn, "^"..module.."_(.*)")
+            if efn then
+                mdl=module
+            else
+                for k in pairs(reexports) do
+                    st, en, efn=string.find(fn, "^"..k.."_(.*)")
+                    if efn then
+                        mdl=module
+                        break
+                    end
+                end
+            end
+            
+            if not mdl then
+                error('"'..fn..'" is not a valid function name of format '..
+                      'modulename_fnname.')
+            end
+        end
+        do_do_export(module, efn, ot, fn, param)
+    end
+
+    -- Handle EXTL_EXPORT_MEMBER otype prefix_fn(class, args)
+    local function do_export_member(s)
+        local pat="EXTL_EXPORT_MEMBER[%s\n]+([%w%s_*]+[%s\n*])([%w_]+)[%s\n]*(%b())"
+        local st, en, ot, fn, param=string.find(s, pat)
+        if not st then matcherr(s) end
+        local efn=string.gsub(fn, ".-_(.*)", "%1")
+        do_do_export("?", efn, ot, fn, param)
+    end
+
+    -- Handle EXTL_EXPORT_AS(table, member_fn) otype fn(args)
+    local function do_export_as(s)
+        local pat="EXTL_EXPORT_AS%(%s*([%w_]+)%s*,%s*([%w_]+)%s*%)[%s\n]+([%w%s_*]+[%s\n*])([%w_]+)[%s\n]*(%b())"
+        local st, en, cls, efn, ot, fn, param=string.find(s, pat)
+        if not st then matcherr(s) end
+        do_do_export((reexports[cls] and module or cls), efn, ot, fn, param)
+    end
+    
+    local function do_implobj(s)
+        local pat="IMPLCLASS%(%s*([%w_]+)%s*,%s*([%w_]+)%s*,[^)]*%)"
+        local st, en, cls, par=string.find(s, pat)
+        if not st then matcherr(s) end
+        add_class(cls)
+        classes[cls].parent=par
+    end
+
+    local function do_class(s)
+        local pat="EXTL_CLASS%(%s*([%w_]+)%s*,%s*([%w_]+)%s*%)"
+        local st, en, cls, par=string.find(s, pat)
+        if not st then matcherr(s) end
+        add_class(cls)
+        classes[cls].parent=par
+    end
+    
+    local lookfor={
+        {"/%*EXTL_DOC", do_doc},
+        {"[%s\n]EXTL_SAFE[%s\n]", do_safe},
+        {"[%s\n]EXTL_UNTRACED[%s\n]", do_untraced},
+        {"[%s\n]EXTL_EXPORT[%s\n]+IMPLCLASS", do_implobj},
+        {"[%s\n]EXTL_EXPORT[%s\n]", do_export},
+        {"[%s\n]EXTL_EXPORT_AS", do_export_as},
+        {"[%s\n]EXTL_EXPORT_MEMBER[%s\n]", do_export_member},
+        {"[%s\n]EXTL_CLASS", do_class},
+    }
+    
+    do_parse(d, lookfor)
+end
+
+function do_parse(d, lookfor)
+    while true do
+        local mins, mine, minfn=string.len(d)+1, nil, nil
+        for _, lf in ipairs(lookfor) do
+            local s, e=string.find(d, lf[1])
+            if s and s<mins then
+                mins, mine, minfn=s, e, lf[2]
+            end
+        end
+        
+        if not minfn then
+            return
+        end
+        
+        minfn(string.sub(d, mins))
+        d=string.sub(d, mine)
+    end
+end    
+
+-- }}}
+
+
+-- Parser for Lua code documentation {{{
+
+function parse_luadoc(d)
+    function do_luadoc(s_)
+        local st, en, b, s=string.find(s_, "\n%-%-DOC(.-)(\n.*)")
+        if string.find(b, "[^%s]") then
+            errorf("Syntax error while parsing \"--DOC%s\"", b)
+        end
+        local doc, docl=""
+        while true do
+            st, en, docl=string.find(s, "^\n%s*%-%-([^\n]*\n)")
+            if not st then
+                break
+            end
+            --print(docl)
+            doc=doc .. docl
+            s=string.sub(s, en)
+        end
+        
+        local fn, param
+        
+        st, en, fn, param=string.find(s, "^\n[%s\n]*function%s*([%w_:%.]+)%s*(%b())")
+
+        if not fn then
+            errorf("Syntax error while parsing \"%s\"",
+                   string.sub(s, 1, 50))
+        end
+        local cls, clsfn
+        st, en, cls, clsfn=string.find(fn, "^([^.]*)%.(.*)$")
+        
+        if cls and clsfn then
+            fn=clsfn
+        else
+            cls="global"
+        end
+        
+        fninfo={
+            doc=doc, 
+            paramstr=param,
+            class=cls,
+        }
+        
+        add_class(cls)
+        if not classes[cls].fns then
+            classes[cls].fns={}
+        end
+        classes[cls].fns[fn]=fninfo
+    end
+    
+    do_parse(d, {{"\n%-%-DOC", do_luadoc}})
+end
+    
+-- }}}
+
+
+-- Export output {{{
+
+function writechnd(h, name, info)
+    local oct=desc2ct[info.odesc]
+        
+    -- begin blockwrite
+    fprintf(h, [[
+static bool %s(%s (*fn)(), ExtlL2Param *in, ExtlL2Param *out)
+{
+]], name, oct)
+    -- end blockwrite
+
+    -- Generate type checking code
+    for k, t in pairs(info.itypes) do
+        if t~="" then
+            if k==1 then
+                fprintf(h, "    if(!EXTL_CHKO1(in, %d, %s)) return FALSE;\n",
+                        k-1, t)
+            else
+                fprintf(h, "    if(!EXTL_CHKO(in, %d, %s)) return FALSE;\n",
+                        k-1, t)
+            end
+        end
+    end
+
+    -- Generate function call code
+    if info.odesc=="v" then
+        fprintf(h, "    fn(")
+    else
+       fprintf(h, "    out[0].%s=fn(", info.odesc)
+    end
+    
+    comma=""
+    for k=1, string.len(info.idesc) do
+        fprintf(h, comma .. "in[%d].%s", k-1, string.sub(info.idesc, k, k))
+       comma=", "
+    end
+    fprintf(h, ");\n    return TRUE;\n}\n")
+end    
+
+function bool2str4c(b)
+    return (b and "TRUE" or "FALSE")
+end        
+
+function write_class_fns(h, cls, data)
+    fprintf(h, "\n\nstatic ExtlExportedFnSpec %s_exports[] = {\n", cls)
+    
+    for fn, info in pairs(data.fns) do
+        local ods, ids="NULL", "NULL"
+        if info.odesc~="v" then
+            ods='"' .. info.odesc .. '"'
+        end
+        
+        if info.idesc~="" then
+            ids='"' .. info.idesc .. '"'
+        end
+        
+        fprintf(h, "    {\"%s\", %s, %s, %s, (ExtlL2CallHandler*)%s, %s, %s},\n",
+                info.exported_name, fn, ids, ods, info.chnd, 
+                bool2str4c(info.safe),
+                bool2str4c(info.untraced))
+    end
+    
+    fprintf(h, "    {NULL, NULL, NULL, NULL, NULL, FALSE, FALSE}\n};\n\n")
+end
+
+
+local function pfx(modname)
+    if modname=="global" or not modname then
+        return ""
+    else
+        return modname.."_"
+    end
+end
+
+
+function write_exports(h)
+    
+    -- begin blockwrite
+    h:write([[
+/* Automatically generated by mkexports.lua */
+#include <libextl/extl.h>
+#include <libextl/private.h>
+
+]])
+    -- end blockwrite
+
+    -- Write class infos and check that the class is implemented in the 
+    -- module.
+    for c, data in pairs(classes) do
+        if string.lower(c)==c then
+            data.module=true
+        else
+            fprintf(h, "EXTL_DEFCLASS(%s);\n", c)
+            if data.fns and not data.parent then
+                error(c..": Methods can only be registered if the class "
+                      .. "is implemented in the module in question.")
+            end
+        end
+    end
+    
+    -- Write L2 call handlers
+    for name, info in pairs(chnds) do
+        writechnd(h, name, info)
+    end
+    
+    fprintf(h, "\n")
+    
+    for cls, data in pairs(classes) do
+        if data.fns then
+            -- Write function declarations
+            for fn in pairs(data.fns) do
+                fprintf(h, "extern void %s();\n", fn)
+            end
+            -- Write function table
+            write_class_fns(h, cls, data)
+        else
+            fprintf(h, "#define %s_exports NULL\n", cls)
+        end
+    end
+    
+    fprintf(h, "bool %sregister_exports()\n{\n", pfx(module))
+
+    local sorted_classes=sort_classes()
+    
+    for _, cls in pairs(sorted_classes) do
+        if cls=="global" then
+            fprintf(h, "    if(!extl_register_functions(global_exports)) return FALSE;\n")
+        elseif classes[cls].module then
+            fprintf(h, "    if(!extl_register_module(\"%s\", %s_exports)) return FALSE;\n", 
+                    cls, cls)
+        elseif classes[cls].parent then
+            fprintf(h, "    if(!extl_register_class(\"%s\", %s_exports, \"%s\")) return FALSE;\n",
+                    cls, cls, classes[cls].parent)
+        end
+    end
+
+    fprintf(h, "    return TRUE;\n}\n\nvoid %sunregister_exports()\n{\n", 
+            pfx(module))
+    
+    for _, cls in pairs(sorted_classes) do
+        if cls=="global" then
+            fprintf(h, "    extl_unregister_functions(global_exports);\n")
+        elseif classes[cls].module then
+            fprintf(h, "    extl_unregister_module(\"%s\", %s_exports);\n", 
+                    cls, cls)
+        elseif classes[cls].parent then
+            fprintf(h, "    extl_unregister_class(\"%s\", %s_exports);\n",
+                    cls, cls)
+        end
+    end
+    
+    fprintf(h, "}\n\n")
+end
+
+
+function write_header(h)
+    local p=pfx(module)
+    local u=string.upper(p)
+    fprintf(h, [[
+/* Automatically generated by mkexports.lua */
+#ifndef %sEXTL_EXPORTS_H
+#define %sEXTL_EXPORTS_H
+
+#include <libextl/extl.h>
+
+extern bool %sregister_exports();
+extern void %sunregister_exports();
+
+#endif /* %sEXTL_EXPORTS_H */
+
+]], u, u, p, p, u)
+end
+
+-- }}}
+
+
+-- Documentation output {{{
+
+function tohuman(desc, objtype)
+    if objtype~="" then
+        return objtype
+    else
+        return desc2human[desc]
+    end
+end
+
+function texfriendly(name)
+    return string.gsub(name, "_", "-")
+end
+
+function texfriendly_typeormod(nm)
+    if string.find(nm, "A-Z") then
+        return "\\type{"..string.gsub(nm, '_', '\_').."}"
+    else
+        return "\\code{"..nm.."}"
+    end
+end
+
+function write_fndoc(h, fn, info)
+    if not info.doc then
+        return
+    end
+    fprintf(h, "\\begin{function}\n")
+    if info.exported_name then
+        fn=info.exported_name
+    end
+    
+    if info.class~="global" then
+        fprintf(h, "\\index{%s@%s!", texfriendly(info.class), 
+                texfriendly_typeormod(info.class));
+        fprintf(h, "%s@\\code{%s}}\n", texfriendly(fn), fn)
+    end
+    fprintf(h, "\\index{%s@\\code{%s}}\n", texfriendly(fn), fn)
+    
+    if info.class~="global" then
+        fprintf(h, "\\hyperlabel{fn:%s.%s}", info.class, fn)
+    else
+        fprintf(h, "\\hyperlabel{fn:%s}", fn)
+    end
+    
+    fprintf(h, "\\synopsis{")
+    if info.odesc then
+        h:write(tohuman(info.odesc, info.otype).." ")
+    end
+    
+    if info.class~="global" then
+        fprintf(h, "%s.", info.class)
+    end
+    
+    if not info.ivars then
+        -- Lua input
+        fprintf(h, "%s%s}", fn, info.paramstr)
+    else
+        fprintf(h, "%s(", fn)
+        local comma=""
+        for i, varname in pairs(info.ivars) do
+            fprintf(h, comma .. "%s", tohuman(string.sub(info.idesc, i, i),
+                                              info.itypes[i]))
+            if varname then
+                fprintf(h, " %s", varname)
+            end
+            comma=", "
+        end
+        fprintf(h, ")}\n")
+    end
+    h:write("\\begin{funcdesc}\n" .. trim(info.doc).. "\n\\end{funcdesc}\n")
+    fprintf(h, "\\end{function}\n\n")
+end
+
+
+function write_class_documentation(h, cls, in_subsect)
+    sorted={}
+    
+    if not classes[cls] or not classes[cls].fns then
+        return
+    end
+    
+    if in_subsect then
+        fprintf(h, "\n\n\\subsection{\\type{%s} functions}\n\n", cls)
+    end
+
+    for fn in pairs(classes[cls].fns) do
+        table.insert(sorted, fn)
+    end
+    table.sort(sorted)
+    
+    for _, fn in ipairs(sorted) do
+        write_fndoc(h, fn, classes[cls].fns[fn])
+    end
+end
+
+
+function write_documentation(h)
+    sorted={}
+    
+    write_class_documentation(h, module, false)
+    
+    for cls in pairs(classes) do
+        if cls~=module then
+            table.insert(sorted, cls)
+        end
+    end
+    table.sort(sorted)
+    
+    for _, cls in ipairs(sorted) do
+        write_class_documentation(h, cls, true)
+    end
+end
+
+-- }}}
+
+
+-- main {{{
+
+inputs={}
+outh=io.stdout
+header_file=nil
+output_file=nil
+make_docs=false
+module="global"
+i=1
+
+function usage()
+    print([[
+Usage: libextl-mkexports [options] files...
+
+Where options include:
+    -mkdoc
+    -help
+    -o outfile
+    -h header
+    -module module
+    -reexport module
+]])
+    os.exit()
+end
+
+while arg[i] do
+    if arg[i]=="-help" then
+        usage()
+    elseif arg[i]=="-mkdoc" then
+        make_docs=true
+    elseif arg[i]=="-o" then
+        i=i+1
+        output_file=arg[i]
+    elseif arg[i]=="-h" then
+        i=i+1
+        header_file=arg[i]
+    elseif arg[i]=="-module" then
+        i=i+1
+        module=arg[i]
+        if not module then
+            error("No module given")
+        end
+    elseif arg[i]=="-reexport" then
+        i=i+1
+        reexports[arg[i]]=true
+    else
+        table.insert(inputs, arg[i])
+    end
+    i=i+1
+end
+
+if table.getn(inputs)==0 then
+    usage()
+end
+
+for _, ifnam in pairs(inputs) do
+    h, err=io.open(ifnam, "r")
+    if not h then
+            errorf("Could not open %s: %s", ifnam, err)
+    end
+    print("Scanning " .. ifnam .. " for exports.")
+    data=h:read("*a")
+    h:close()
+    if string.find(ifnam, "%.lua$") then
+        assert(make_docs)
+        parse_luadoc("\n" .. data .. "\n")
+    elseif string.find(ifnam, "%.c$") then
+        parse("\n" .. data .. "\n")
+    else
+        error('Unknown file')
+    end
+    
+end
+
+if output_file then        
+    outh, err=io.open(output_file, "w")
+    if not outh then
+        error(err)
+    end
+end
+
+if make_docs then
+    write_documentation(outh)
+else
+    write_exports(outh)
+    if header_file then
+        local hh, err=io.open(header_file, "w")
+        if not hh then
+            error(err)
+        end
+        write_header(hh)
+        hh:close()
+    end
+end
+
+-- }}}
+
diff --git a/libextl/luaextl.c b/libextl/luaextl.c
new file mode 100644 (file)
index 0000000..c53fde6
--- /dev/null
@@ -0,0 +1,2439 @@
+/*
+ * libextl/luaextl.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#include <time.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <libtu/obj.h>
+#include <libtu/objp.h>
+#include <libtu/dlist.h>
+
+#include "readconfig.h"
+#include "luaextl.h"
+#include "private.h"
+
+#define MAGIC 0xf00ba7
+
+/* Maximum number of parameters and return values for calls from Lua
+ * and (if va_copy is not available) return value from Lua functions.
+ */
+#define MAX_PARAMS 16
+
+static lua_State *l_st=NULL;
+
+static bool extl_stack_get(lua_State *st, int pos, char type, 
+                           bool copystring, bool *wasdeadobject,
+                           void *valret);
+
+static int extl_protected(lua_State *st);
+
+#ifdef EXTL_LOG_ERRORS
+static void flushtrace();
+#else
+#define flushtrace()
+#endif
+
+
+/*{{{ Safer rawget/set/getn */
+
+
+#define CHECK_TABLE(ST, INDEX) luaL_checktype(ST, INDEX, LUA_TTABLE)
+
+static int luaL_getn_check(lua_State *st, int index)
+{
+    CHECK_TABLE(st, index);
+    return luaL_getn(st, index);
+}
+
+
+static void lua_rawset_check(lua_State *st, int index)
+{
+    CHECK_TABLE(st, index);
+    lua_rawset(st, index);
+}
+
+
+static void lua_rawseti_check(lua_State *st, int index, int n)
+{
+    CHECK_TABLE(st, index);
+    lua_rawseti(st, index, n);
+}
+
+
+static void lua_rawget_check(lua_State *st, int index)
+{
+    CHECK_TABLE(st, index);
+    lua_rawget(st, index);
+}
+
+
+static void lua_rawgeti_check(lua_State *st, int index, int n)
+{
+    CHECK_TABLE(st, index);
+    lua_rawgeti(st, index, n);
+}
+
+
+/*}}}*/
+
+
+/*{{{ A cpcall wrapper */
+
+
+typedef bool ExtlCPCallFn(lua_State *st, void *ptr);
+
+
+typedef struct{
+    ExtlCPCallFn *fn;
+    void *udata;
+    bool retval;
+} ExtlCPCallParam;
+
+
+static int extl_docpcall(lua_State *st)
+{
+    ExtlCPCallParam *param=(ExtlCPCallParam*)lua_touserdata(st, -1);
+    
+    /* Should be enough for most things */
+    if(!lua_checkstack(st, 8)){
+        extl_warn(TR("Lua stack full."));
+        return 0;
+    }
+
+    param->retval=param->fn(st, param->udata);
+    return 0;
+}
+
+                            
+static bool extl_cpcall(lua_State *st, ExtlCPCallFn *fn, void *ptr)
+{
+    ExtlCPCallParam param;
+    int oldtop=lua_gettop(st);
+    int err;
+    
+    param.fn=fn;
+    param.udata=ptr;
+    param.retval=FALSE;
+    
+    
+    err=lua_cpcall(st, extl_docpcall, &param);
+    if(err==LUA_ERRRUN){
+        extl_warn("%s", lua_tostring(st, -1));
+    }else if(err==LUA_ERRMEM){
+        extl_warn("%s", strerror(ENOMEM));
+    }else if(err!=0){
+        extl_warn(TR("Unknown Lua error."));
+    }
+    
+    lua_settop(st, oldtop);
+    
+    return param.retval;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Obj userdata handling -- unsafe */
+
+
+static int owned_cache_ref=LUA_NOREF;
+
+
+static Obj *extl_get_obj(lua_State *st, int pos, 
+                         bool *invalid, bool *dead)
+{
+    int val;
+
+    *dead=FALSE;
+    *invalid=TRUE;
+    
+    if(!lua_isuserdata(st, pos)){
+        *invalid=!lua_isnil(st, pos);
+        return NULL;
+    }
+
+    if(!lua_getmetatable(st, pos))
+        return NULL;
+    
+    /* If the userdata object is a proper Obj, metatable[MAGIC] must
+     * have been set to MAGIC.
+     */
+    lua_pushnumber(st, MAGIC);
+    lua_gettable(st, -2);
+    val=lua_tonumber(st, -1);
+    lua_pop(st, 2);
+    
+    if(val==MAGIC){
+        ExtlProxy *proxy=(ExtlProxy*)lua_touserdata(st, pos);
+        
+        *invalid=FALSE;
+        
+        if(proxy!=NULL){
+            Obj *obj=EXTL_PROXY_OBJ(proxy);
+            if(obj==NULL){
+                *dead=TRUE;
+                *invalid=TRUE;
+            }
+            return obj;
+        }
+    }
+
+    return NULL;
+}
+
+
+static void extl_uncache_(lua_State *st, Obj *obj)
+{
+    if(EXTL_OBJ_OWNED(obj)){
+        lua_rawgeti(st, LUA_REGISTRYINDEX, owned_cache_ref);
+        lua_pushlightuserdata(st, obj);
+        lua_pushnil(st);
+        lua_rawset(st, -3);
+    }else{
+        lua_pushlightuserdata(st, obj);
+        lua_pushnil(st);
+        lua_rawset(st, LUA_REGISTRYINDEX);
+    }
+}
+
+
+void extl_uncache(Obj *obj)
+{
+    extl_cpcall(l_st, (ExtlCPCallFn*)extl_uncache_, obj);
+}
+
+
+static void extl_push_obj(lua_State *st, Obj *obj)
+{
+    ExtlProxy *proxy;
+    
+    if(obj==NULL){
+        lua_pushnil(st);
+        return;
+    }
+
+    if(EXTL_OBJ_CACHED(obj)){
+        if(EXTL_OBJ_OWNED(obj)){
+            lua_rawgeti(st, LUA_REGISTRYINDEX, owned_cache_ref);
+            lua_pushlightuserdata(st, obj);
+            lua_rawget(st, -2);
+            lua_remove(st, -2); /* owned_cache */
+        }else{
+            lua_pushlightuserdata(st, obj);
+            lua_rawget(st, LUA_REGISTRYINDEX);
+        }
+        if(lua_isuserdata(st, -1)){
+            D(fprintf(stderr, "found %p cached\n", obj));
+            return;
+        }
+        lua_pop(st, 1);
+    }
+    
+    D(fprintf(stderr, "Creating %p\n", obj));
+
+    proxy=(ExtlProxy*)lua_newuserdata(st, sizeof(ExtlProxy));
+    
+    /* Lua shouldn't return if the allocation fails */
+    
+    lua_pushfstring(st, "luaextl_%s_metatable", OBJ_TYPESTR(obj));
+    lua_gettable(st, LUA_REGISTRYINDEX);
+    if(lua_isnil(st, -1)){
+        lua_pop(st, 2);
+        lua_pushnil(st);
+    }else{
+        lua_setmetatable(st, -2);
+
+        /* Store in cache */
+        if(EXTL_OBJ_OWNED(obj)){
+            lua_rawgeti(st, LUA_REGISTRYINDEX, owned_cache_ref);
+            lua_pushlightuserdata(st, obj);
+            lua_pushvalue(st, -3);  /* the WWatch */
+            lua_rawset_check(st, -3);
+            lua_pop(st, 1); /* owned_cache */
+        }else{
+            lua_pushlightuserdata(st, obj);
+            lua_pushvalue(st, -2); /* the WWatch */
+            lua_rawset_check(st, LUA_REGISTRYINDEX);
+        }
+        EXTL_BEGIN_PROXY_OBJ(proxy, obj);
+    }
+}
+
+    
+/*{{{ Functions available to Lua code */
+
+
+static int extl_obj_gc_handler(lua_State *st)
+{
+    ExtlProxy *proxy;
+    bool dead=FALSE, invalid=FALSE;
+    Obj *obj;
+    
+    obj=extl_get_obj(st, 1, &invalid, &dead);
+    
+    if(obj==NULL){
+        /* This should not happen, actually. Our object cache should
+         * hold references to all objects seen on the Lua side until
+         * they are destroyed.
+         */
+        return 0;
+    }
+
+    proxy=(ExtlProxy*)lua_touserdata(st, 1);
+    
+    if(proxy!=NULL)
+        EXTL_END_PROXY_OBJ(proxy, obj);
+    
+    if(EXTL_OBJ_OWNED(obj))
+        EXTL_DESTROY_OWNED_OBJ(obj);
+    
+    return 0;
+}
+
+
+static int extl_obj_typename(lua_State *st)
+{
+    Obj *obj=NULL;
+
+    if(!extl_stack_get(st, 1, 'o', FALSE, NULL, &obj) || obj==NULL)
+        return 0;
+    
+    lua_pushstring(st, EXTL_OBJ_TYPENAME(obj));
+    return 1;
+}
+
+/* Dummy code for documentation generation. */
+
+/*EXTL_DOC
+ * Return type name of \var{obj}.
+ */
+EXTL_EXPORT_AS(global, obj_typename)
+const char *__obj_typename(Obj *obj);
+
+
+static int extl_obj_exists(lua_State *st)
+{
+    Obj *obj=NULL;
+    
+    extl_stack_get(st, 1, 'o', FALSE, NULL, &obj);
+    
+    lua_pushboolean(st, obj!=NULL);
+    
+    return 1;
+}
+
+/* Dummy code for documentation generation. */
+
+/*EXTL_DOC
+ * Does \var{obj} still exist on the C side of Ion?
+ */
+EXTL_EXPORT_AS(global, obj_exists)
+bool __obj_exists(Obj *obj);
+
+
+static int extl_obj_is(lua_State *st)
+{
+    Obj *obj=NULL;
+    const char *tn;
+    
+    extl_stack_get(st, 1, 'o', FALSE, NULL, &obj);
+    
+    if(obj==NULL){
+        lua_pushboolean(st, 0);
+    }else{
+        tn=lua_tostring(st, 2);
+        lua_pushboolean(st, EXTL_OBJ_IS(obj, tn));
+    }
+    
+    return 1;
+}
+
+/* Dummy code for documentation generation. */
+
+/*EXTL_DOC
+ * Is \var{obj} of type \var{typename}.
+ */
+EXTL_EXPORT_AS(global, obj_is)
+bool __obj_is(Obj *obj, const char *typename);
+
+
+static int extl_current_file_or_dir(lua_State *st, bool dir)
+{
+    int r;
+    lua_Debug ar;
+    const char *s, *p;
+    
+    if(lua_getstack(st, 1, &ar)!=1)
+        goto err;
+    if(lua_getinfo(st, "S", &ar)==0)
+        goto err;
+    
+    if(ar.source==NULL || ar.source[0]!='@')
+        return 0; /* not a file */
+    
+    s=ar.source+1;
+    
+    if(!dir){
+        lua_pushstring(st, s);
+    }else{
+        p=strrchr(s, '/');
+        if(p==NULL){
+            lua_pushstring(st, ".");
+        }else{
+            lua_pushlstring(st, s, p-s);
+        }
+    }
+    return 1;
+    
+err:
+    extl_warn("Unable to get caller file from stack.");
+    return 0;
+}
+
+
+static int extl_dopath(lua_State *st)
+{
+    const char *toincl, *cfdir;
+    bool res, complain;
+    
+    toincl=luaL_checkstring(st, 1);
+    complain=!lua_toboolean(st, 2);
+    
+    if(extl_current_file_or_dir(st, TRUE)!=1){
+        res=extl_read_config(toincl, NULL, complain);
+    }else{
+        cfdir=lua_tostring(st, -1);
+        res=extl_read_config(toincl, cfdir, complain);
+        lua_pop(st, 1);
+    }
+    lua_pushboolean(st, res);
+    return 1;
+}
+
+/* Dummy code for documentation generation. */
+
+/*EXTL_DOC
+ * Look up and execute another file with Lua code.
+ */
+EXTL_EXPORT_AS(global, dopath)
+bool dopath(const char *what);
+
+
+/*}}}*/
+
+
+static bool extl_init_obj_info(lua_State *st)
+{
+    static ExtlExportedFnSpec dummy[]={
+        {NULL, NULL, NULL, NULL, NULL, FALSE, FALSE}
+    };
+    
+    extl_register_class("Obj", dummy, NULL);
+    
+    /* Create cache for proxies to objects owned by Lua-side.
+     * These need to be in a weak table to ever be collected.
+     */
+    lua_newtable(st);
+    lua_newtable(st);
+    lua_pushstring(st, "__mode");
+    lua_pushstring(st, "v");
+    lua_rawset_check(st, -3);
+    lua_setmetatable(st, -2);
+    owned_cache_ref=lua_ref(st, -1);
+
+    lua_pushcfunction(st, extl_obj_typename);
+    lua_setglobal(st, "obj_typename");
+    lua_pushcfunction(st, extl_obj_is);
+    lua_setglobal(st, "obj_is");
+    lua_pushcfunction(st, extl_obj_exists);
+    lua_setglobal(st, "obj_exists");
+    lua_pushcfunction(st, extl_dopath);
+    lua_setglobal(st, "dopath");
+    lua_pushcfunction(st, extl_protected);
+    lua_setglobal(st, "protected");
+
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Error handling and reporting -- unsafe */
+
+
+static int extl_stack_trace(lua_State *st)
+{
+    lua_Debug ar;
+    int lvl=0;
+    int n_skip=0;
+    
+    lua_pushstring(st, TR("Stack trace:"));
+
+    for( ; lua_getstack(st, lvl, &ar); lvl++){
+        bool is_c=FALSE;
+        
+        if(lua_getinfo(st, "Sln", &ar)==0){
+            lua_pushfstring(st, 
+                            TR("\n(Unable to get debug info for level %d)"),
+                            lvl);
+            lua_concat(st, 2);
+            continue;
+        }
+        
+        is_c=(ar.what!=NULL && strcmp(ar.what, "C")==0);
+
+        if(!is_c || ar.name!=NULL){
+            lua_pushfstring(st, "\n%d %s", lvl, ar.short_src);
+            if(ar.currentline!=-1)
+                lua_pushfstring(st, ":%d", ar.currentline);
+            if(ar.name!=NULL)
+                lua_pushfstring(st, ": in '%s'", ar.name);
+            lua_concat(st, 2+(ar.currentline!=-1)+(ar.name!=NULL));
+            n_skip=0;
+        }else{
+            if(n_skip==0){
+                lua_pushstring(st, TR("\n  [Skipping unnamed C functions.]"));
+                /*lua_pushstring(st, "\n...skipping...");*/
+                lua_concat(st, 2);
+            }
+            n_skip++;
+        }
+    }
+    return 1;
+}
+
+
+#ifdef EXTL_LOG_ERRORS
+
+static int extl_do_collect_errors(lua_State *st)
+{
+    int n, err;
+    ErrorLog *el=(ErrorLog*)lua_touserdata(st, -1);
+
+    lua_pop(st, 1);
+    
+    n=lua_gettop(st)-1;
+    err=lua_pcall(st, n, 0, 0);
+    
+    if(err!=0)
+        extl_warn("%s", lua_tostring(st, -1));
+    
+    if(el->msgs_len==0)
+        return 0;
+    lua_pushstring(st, el->msgs);
+    return 1;
+}
+
+
+int extl_collect_errors(lua_State *st)
+{
+    ErrorLog el;
+    int n=lua_gettop(st);
+    int err;
+    
+    lua_pushcfunction(st, extl_do_collect_errors);
+    lua_insert(st, 1);
+    lua_pushlightuserdata(st, &el);
+    
+    errorlog_begin(&el);
+    
+    err=lua_pcall(st, n+1, 1, 0);
+    
+    errorlog_end(&el);
+    errorlog_deinit(&el);
+    
+    if(err!=0)
+        extl_warn(TR("Internal error."));
+    
+    return 1;
+}
+
+#endif
+
+
+/*}}}*/
+
+
+/*{{{ Init -- unsafe, but it doesn't matter at this point */
+
+
+bool extl_init()
+{
+    l_st=luaL_newstate();
+    
+    if(l_st==NULL){
+        extl_warn(TR("Unable to initialize Lua."));
+        return FALSE;
+    }
+
+    /* This is equivalent to calling all the ones below but it also includes
+     * the debug library, so I went with those in case there was a reason not
+     * to include the debug library.
+    luaL_openlibs(l_st);
+    */
+
+    lua_pushcfunction(l_st, luaopen_base);
+    lua_call(l_st, 0, 0);
+
+    lua_pushcfunction(l_st, luaopen_table);
+    lua_call(l_st, 0, 0);
+
+    lua_pushcfunction(l_st, luaopen_io);
+    lua_call(l_st, 0, 0);
+
+    lua_pushcfunction(l_st, luaopen_os);
+    lua_call(l_st, 0, 0);
+
+    lua_pushcfunction(l_st, luaopen_string);
+    lua_call(l_st, 0, 0);
+
+    lua_pushcfunction(l_st, luaopen_math);
+    lua_call(l_st, 0, 0);
+
+    lua_pushcfunction(l_st, luaopen_package);
+    lua_call(l_st, 0, 0);
+
+    if(!extl_init_obj_info(l_st)){
+        lua_close(l_st);
+        return FALSE;
+    }
+
+#ifdef EXTL_LOG_ERRORS
+    lua_pushcfunction(l_st, extl_collect_errors);
+    lua_setglobal(l_st, "collect_errors");
+#endif
+
+    return TRUE;
+}
+
+
+void extl_deinit()
+{
+    lua_close(l_st);
+    l_st=NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Stack get/push -- all unsafe */
+
+
+static bool extl_stack_get(lua_State *st, int pos, char type, 
+                           bool copystring, bool *wasdeadobject,
+                           void *valret)
+{
+    double d=0;
+    const char *str;
+          
+    if(wasdeadobject!=NULL)
+        *wasdeadobject=FALSE;
+    
+    if(type=='i' || type=='d'){
+        if(lua_type(st, pos)!=LUA_TNUMBER)
+            return FALSE;
+        
+        d=lua_tonumber(st, pos);
+        
+        if(type=='i'){
+            if(d-floor(d)!=0)
+                return FALSE;
+            if(valret)
+                *((int*)valret)=d;
+        }else{
+            if(valret)
+                *((double*)valret)=d;
+        }
+        return TRUE;
+    }
+    
+    if(type=='b'){
+        if(valret)
+            *((bool*)valret)=lua_toboolean(st, pos);
+        return TRUE;
+    }
+
+    if(lua_type(st, pos)==LUA_TNIL || lua_type(st, pos)==LUA_TNONE){
+        if(type=='t' || type=='f'){
+            if(valret)
+                *((int*)valret)=LUA_NOREF;
+        }else if(type=='s' || type=='S'){
+            if(valret)
+                *((char**)valret)=NULL;
+        }else if(type=='o'){
+            if(valret)
+                *((Obj**)valret)=NULL;
+        }else{
+            return FALSE;
+        }
+        return TRUE;
+    }
+    
+    if(type=='s' || type=='S'){
+        if(lua_type(st, pos)!=LUA_TSTRING)
+            return FALSE;
+        if(valret){
+            str=lua_tostring(st, pos);
+            if(str!=NULL && copystring){
+                str=extl_scopy(str);
+                if(str==NULL)
+                    return FALSE;
+            }
+            *((const char**)valret)=str;
+        }
+        return TRUE;
+    }
+    
+    if(type=='f'){
+        if(!lua_isfunction(st, pos))
+            return FALSE;
+        if(valret){
+            lua_pushvalue(st, pos);
+            *((int*)valret)=lua_ref(st, 1);
+        }
+        return TRUE;
+    }
+
+    if(type=='t'){
+        if(!lua_istable(st, pos))
+            return FALSE;
+        if(valret){
+            lua_pushvalue(st, pos);
+            *((int*)valret)=lua_ref(st, 1);
+        }
+        return TRUE;
+    }
+
+    if(type=='o'){
+        bool invalid=FALSE, dead=FALSE;
+        Obj *obj=extl_get_obj(st, pos, &invalid, &dead);
+        if(wasdeadobject!=NULL)
+            *wasdeadobject=dead;
+        if(valret){
+            *((Obj**)valret)=obj;
+            D(fprintf(stderr, "Got obj %p, ", obj);
+              fprintf(stderr, "%s\n", OBJ_TYPESTR(obj)));
+        }
+        return !invalid;
+    }
+    
+    return FALSE;
+}
+
+
+static void extl_stack_push(lua_State *st, char spec, void *ptr)
+{
+    if(spec=='i'){
+        lua_pushnumber(st, *(int*)ptr);
+    }else if(spec=='d'){
+        lua_pushnumber(st, *(double*)ptr);
+    }else if(spec=='b'){
+        lua_pushboolean(st, *(bool*)ptr);
+    }else if(spec=='o'){
+        extl_push_obj(st, *(Obj**)ptr);
+    }else if(spec=='s' || spec=='S'){
+        lua_pushstring(st, *(char**)ptr);
+    }else if(spec=='t' || spec=='f'){
+        lua_rawgeti(st, LUA_REGISTRYINDEX, *(int*)ptr);
+    }else{
+        lua_pushnil(st);
+    }
+}
+
+
+static bool extl_stack_push_vararg(lua_State *st, char spec, va_list *argsp)
+{
+    switch(spec){
+    case 'i':
+        lua_pushnumber(st, (double)va_arg(*argsp, int));
+        break;
+    case 'd':
+        lua_pushnumber(st, va_arg(*argsp, double));
+        break;
+    case 'b':
+        lua_pushboolean(st, va_arg(*argsp, bool));
+        break;
+    case 'o':
+        extl_push_obj(st, va_arg(*argsp, Obj*));
+        break;
+    case 'S':
+    case 's':
+        lua_pushstring(st, va_arg(*argsp, char*));
+        break;
+    case 'f':
+    case 't':
+        lua_rawgeti(st, LUA_REGISTRYINDEX, va_arg(*argsp, int));
+        break;
+    default:
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Free */
+
+
+enum{STRINGS_NONE, STRINGS_NONCONST, STRINGS_ALL};
+
+
+static void extl_free(void *ptr, char spec, int strings)
+{
+    if(((spec=='s' && strings!=STRINGS_NONE) ||
+        (spec=='S' && strings==STRINGS_ALL)) && *(char**)ptr!=NULL){
+        if(*(char**)ptr!=NULL)
+            free(*(char**)ptr);
+        *(char**)ptr=NULL;
+    }else if(spec=='t'){
+        extl_unref_table(*(ExtlTab*)ptr);
+    }else if(spec=='f'){
+        extl_unref_fn(*(ExtlFn*)ptr);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Table and function references. */
+
+
+static bool extl_getref(lua_State *st, int ref)
+{
+    lua_rawgeti(st, LUA_REGISTRYINDEX, ref);
+    if(lua_isnil(st, -1)){
+        lua_pop(st, 1);
+        return FALSE;
+    }
+    return TRUE;
+}
+    
+/* Unref */
+
+static bool extl_do_unref(lua_State *st, int *refp)
+{
+    lua_unref(st, *refp);
+    return TRUE;
+}
+
+
+ExtlFn extl_unref_fn(ExtlFn ref)
+{
+    extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_unref, &ref);
+    return LUA_NOREF;
+}
+
+
+ExtlFn extl_unref_table(ExtlTab ref)
+{
+    extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_unref, &ref);
+    return LUA_NOREF;
+}
+
+
+/* noref */
+
+ExtlFn extl_fn_none()
+{
+    return LUA_NOREF;
+}
+
+
+ExtlTab extl_table_none()
+{
+    return LUA_NOREF;
+}
+
+
+/* ref */
+
+static bool extl_do_ref(lua_State *st, int *refp)
+{
+    if(!extl_getref(st, *refp))
+        return FALSE;
+    *refp=lua_ref(st, 1);
+    return TRUE;
+}
+
+
+ExtlTab extl_ref_table(ExtlTab ref)
+{
+    if(extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_ref, &ref))
+        return ref;
+    return LUA_NOREF;
+}
+
+
+ExtlFn extl_ref_fn(ExtlFn ref)
+{
+    if(extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_ref, &ref))
+        return ref;
+    return LUA_NOREF;
+}
+
+
+/* create_table */
+
+static bool extl_do_create_table(lua_State *st, int *refp)
+{
+    lua_newtable(st);
+    *refp=lua_ref(st, 1);
+    return TRUE;
+}
+
+
+ExtlTab extl_create_table()
+{
+    ExtlTab ref;
+    if(extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_create_table, &ref))
+        return ref;
+    return LUA_NOREF;
+}
+
+
+/* eq */
+
+typedef struct{
+    int o1, o2;
+    bool ret;
+} EqParams;
+
+
+static bool extl_do_eq(lua_State *st, EqParams *ep)
+{
+    if(!extl_getref(st, ep->o1))
+        return FALSE;
+    if(!extl_getref(st, ep->o2))
+        return FALSE;
+    ep->ret=lua_equal(st, -1, -2);
+    return TRUE;
+}
+
+
+bool extl_fn_eq(ExtlFn fn1, ExtlFn fn2)
+{
+    EqParams ep;
+    ep.o1=fn1;
+    ep.o2=fn2;
+    ep.ret=FALSE;
+    extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_eq, &ep);
+    return ep.ret;
+}
+
+
+bool extl_table_eq(ExtlTab t1, ExtlTab t2)
+{
+    EqParams ep;
+    ep.o1=t1;
+    ep.o2=t2;
+    ep.ret=FALSE;
+    extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_eq, &ep);
+    return ep.ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Table/get */
+
+
+typedef struct{
+    ExtlTab ref;
+    char type;
+    char itype;
+    va_list *argsp;
+} TableParams2;
+
+
+static bool extl_table_dodo_get2(lua_State *st, TableParams2 *params)
+{
+    if(params->ref<0)
+        return FALSE;
+
+    lua_rawgeti(st, LUA_REGISTRYINDEX, params->ref);
+    extl_stack_push_vararg(st, params->itype, params->argsp);
+    lua_gettable(st, -2);
+    if(lua_isnil(st, -1))
+        return FALSE;
+    
+    return extl_stack_get(st, -1, params->type, TRUE, NULL,
+                          va_arg(*(params->argsp), void*));
+}
+
+
+bool extl_table_get_vararg(ExtlTab ref, char itype, char type, va_list *args)
+{
+    TableParams2 params;
+    
+    params.ref=ref;
+    params.itype=itype;
+    params.type=type;
+    params.argsp=args;
+    
+    return extl_cpcall(l_st, (ExtlCPCallFn*)extl_table_dodo_get2, &params);
+}
+
+
+bool extl_table_get(ExtlTab ref, char itype, char type, ...)
+{
+    va_list args;
+    bool retval;
+    
+    va_start(args, type);
+    retval=extl_table_get_vararg(ref, itype, type, &args);
+    va_end(args);
+    
+    return retval;
+}
+
+
+static bool extl_table_do_gets(ExtlTab ref, const char *entry,
+                               char type, void *valret)
+{
+    return extl_table_get(ref, 's', type, entry, valret);
+}
+
+bool extl_table_gets_o(ExtlTab ref, const char *entry, Obj **ret)
+{
+    return extl_table_do_gets(ref, entry, 'o', (void*)ret);
+}
+
+bool extl_table_gets_i(ExtlTab ref, const char *entry, int *ret)
+{
+    return extl_table_do_gets(ref, entry, 'i', (void*)ret);
+}
+
+bool extl_table_gets_d(ExtlTab ref, const char *entry, double *ret)
+{
+    return extl_table_do_gets(ref, entry, 'd', (void*)ret);
+}
+
+bool extl_table_gets_b(ExtlTab ref, const char *entry, bool *ret)
+{
+    return extl_table_do_gets(ref, entry, 'b', (void*)ret);
+}
+
+bool extl_table_gets_s(ExtlTab ref, const char *entry, char **ret)
+{
+    return extl_table_do_gets(ref, entry, 's', (void*)ret);
+}
+
+bool extl_table_gets_f(ExtlTab ref, const char *entry, ExtlFn *ret)
+{
+    return extl_table_do_gets(ref, entry, 'f', (void*)ret);
+}
+
+bool extl_table_gets_t(ExtlTab ref, const char *entry, ExtlTab *ret)
+{
+    return extl_table_do_gets(ref, entry, 't', (void*)ret);
+}
+
+
+static bool extl_table_do_geti(ExtlTab ref, int entry, char type, void *valret)
+{
+    return extl_table_get(ref, 'i', type, entry, valret);
+}
+
+bool extl_table_geti_o(ExtlTab ref, int entry, Obj **ret)
+{
+    return extl_table_do_geti(ref, entry, 'o', (void*)ret);
+}
+
+bool extl_table_geti_i(ExtlTab ref, int entry, int *ret)
+{
+    return extl_table_do_geti(ref, entry, 'i', (void*)ret);
+}
+
+bool extl_table_geti_d(ExtlTab ref, int entry, double *ret)
+{
+    return extl_table_do_geti(ref, entry, 'd', (void*)ret);
+}
+
+bool extl_table_geti_b(ExtlTab ref, int entry, bool *ret)
+{
+    return extl_table_do_geti(ref, entry, 'b', (void*)ret);
+}
+
+bool extl_table_geti_s(ExtlTab ref, int entry, char **ret)
+{
+    return extl_table_do_geti(ref, entry, 's', (void*)ret);
+}
+
+bool extl_table_geti_f(ExtlTab ref, int entry, ExtlFn *ret)
+{
+    return extl_table_do_geti(ref, entry, 'f', (void*)ret);
+}
+
+bool extl_table_geti_t(ExtlTab ref, int entry, ExtlTab *ret)
+{
+    return extl_table_do_geti(ref, entry, 't', (void*)ret);
+}
+
+
+typedef struct{
+    int ref;
+    int n;
+} GetNParams;
+
+
+static bool extl_table_do_get_n(lua_State *st, GetNParams *params)
+{
+    lua_rawgeti(st, LUA_REGISTRYINDEX, params->ref);
+    params->n=luaL_getn_check(st, -1);
+    return TRUE;
+}
+
+
+int extl_table_get_n(ExtlTab ref)
+{
+    GetNParams params;
+    int oldtop;
+    
+    params.ref=ref;
+    params.n=0;
+    
+    extl_cpcall(l_st, (ExtlCPCallFn*)extl_table_do_get_n, &params);
+    
+    return params.n;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Table/set */
+
+
+static bool extl_table_dodo_set2(lua_State *st, TableParams2 *params)
+{
+    lua_rawgeti(st, LUA_REGISTRYINDEX, params->ref);
+    extl_stack_push_vararg(st, params->itype, params->argsp);
+    extl_stack_push_vararg(st, params->type, params->argsp);
+    lua_rawset_check(st, -3);
+    return TRUE;
+}
+
+
+bool extl_table_set_vararg(ExtlTab ref, char itype, char type, va_list *args)
+{
+    TableParams2 params;
+    
+    params.ref=ref;
+    params.itype=itype;
+    params.type=type;
+    params.argsp=args;
+    
+    return extl_cpcall(l_st, (ExtlCPCallFn*)extl_table_dodo_set2, &params);
+}
+
+
+bool extl_table_set(ExtlTab ref, char itype, char type, ...)
+{
+    va_list args;
+    bool retval;
+    
+    va_start(args, type);
+    retval=extl_table_set_vararg(ref, itype, type, &args);
+    va_end(args);
+    
+    return retval;
+}
+
+
+bool extl_table_sets_o(ExtlTab ref, const char *entry, Obj *val)
+{
+    return extl_table_set(ref, 's', 'o', entry, val);
+}
+
+bool extl_table_sets_i(ExtlTab ref, const char *entry, int val)
+{
+    return extl_table_set(ref, 's', 'i', entry, val);
+}
+
+bool extl_table_sets_d(ExtlTab ref, const char *entry, double val)
+{
+    return extl_table_set(ref, 's', 'd', entry, val);
+}
+
+bool extl_table_sets_b(ExtlTab ref, const char *entry, bool val)
+{
+    return extl_table_set(ref, 's', 'b', entry, val);
+}
+
+bool extl_table_sets_s(ExtlTab ref, const char *entry, const char *val)
+{
+    return extl_table_set(ref, 's', 'S', entry, val);
+}
+
+bool extl_table_sets_f(ExtlTab ref, const char *entry, ExtlFn val)
+{
+    return extl_table_set(ref, 's', 'f', entry, val);
+}
+
+bool extl_table_sets_t(ExtlTab ref, const char *entry, ExtlTab val)
+{
+    return extl_table_set(ref, 's', 't', entry, val);
+}
+
+
+bool extl_table_seti_o(ExtlTab ref, int entry, Obj *val)
+{
+    return extl_table_set(ref, 'i', 'o', entry, val);
+}
+
+bool extl_table_seti_i(ExtlTab ref, int entry, int val)
+{
+    return extl_table_set(ref, 'i', 'i', entry, val);
+}
+
+bool extl_table_seti_d(ExtlTab ref, int entry, double val)
+{
+    return extl_table_set(ref, 'i', 'd', entry, val);
+}
+
+bool extl_table_seti_b(ExtlTab ref, int entry, bool val)
+{
+    return extl_table_set(ref, 'i', 'b', entry, val);
+}
+
+bool extl_table_seti_s(ExtlTab ref, int entry, const char *val)
+{
+    return extl_table_set(ref, 'i', 'S', entry, val);
+}
+
+bool extl_table_seti_f(ExtlTab ref, int entry, ExtlFn val)
+{
+    return extl_table_set(ref, 'i', 'f', entry, val);
+}
+
+bool extl_table_seti_t(ExtlTab ref, int entry, ExtlTab val)
+{
+    return extl_table_set(ref, 'i', 't', entry, val);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Table/clear entry */
+
+
+static bool extl_table_dodo_clear2(lua_State *st, TableParams2 *params)
+{
+    lua_rawgeti(st, LUA_REGISTRYINDEX, params->ref);
+    extl_stack_push_vararg(st, params->itype, params->argsp);
+    lua_pushnil(st);
+    lua_rawset_check(st, -3);
+    return TRUE;
+}
+
+bool extl_table_clear_vararg(ExtlTab ref, char itype, va_list *args)
+{
+    TableParams2 params;
+    
+    params.ref=ref;
+    params.itype=itype;
+    /*params.type='?';*/
+    params.argsp=args;
+    
+    return extl_cpcall(l_st, (ExtlCPCallFn*)extl_table_dodo_clear2, &params);
+}
+
+bool extl_table_clear(ExtlTab ref, char itype, ...)
+{
+    va_list args;
+    bool retval;
+    
+    va_start(args, itype);
+    retval=extl_table_clear_vararg(ref, itype, &args);
+    va_end(args);
+    
+    return retval;
+}
+
+
+bool extl_table_clears(ExtlTab ref, const char *entry)
+{
+    return extl_table_clear(ref, 's', entry);
+}
+
+bool extl_table_cleari(ExtlTab ref, int entry)
+{
+    return extl_table_clear(ref, 'i', entry);
+}
+
+
+                   
+/*}}}*/
+
+
+/*{{{ Function calls to Lua */
+
+
+static bool extl_push_args(lua_State *st, const char *spec, va_list *argsp)
+{
+    int i=1;
+    
+    while(*spec!='\0'){
+        if(!extl_stack_push_vararg(st, *spec, argsp))
+            return FALSE;
+        i++;
+        spec++;
+    }
+    
+    return TRUE;
+}
+
+
+typedef struct{
+    const char *spec;
+    const char *rspec;
+    va_list *args;
+    void *misc;
+    int nret;
+#ifndef CF_HAS_VA_COPY
+    void *ret_ptrs[MAX_PARAMS];
+#endif
+} ExtlDoCallParam;
+
+
+static bool extl_get_retvals(lua_State *st, int m, ExtlDoCallParam *param)
+{
+    void *ptr;
+    const char *spec=param->rspec;
+
+#ifdef CF_HAS_VA_COPY
+    va_list args;
+    va_copy(args, *(param->args));
+#else
+    if(m>MAX_PARAMS){
+        extl_warn(TR("Too many return values. Use a C compiler that has "
+                     "va_copy to support more."));
+        return FALSE;
+    }
+#endif
+    
+    while(m>0){
+        bool dead=FALSE;
+#ifdef CF_HAS_VA_COPY
+        ptr=va_arg(args, void*);
+#else
+        ptr=va_arg(*(param->args), void*);
+        param->ret_ptrs[param->nret]=ptr;
+#endif
+        if(!extl_stack_get(st, -m, *spec, TRUE, &dead, ptr)){
+            /* This is the only place where we allow nil-objects */
+            /*if(*spec=='o' && lua_isnil(st, -m)){
+                *(Obj**)ptr=NULL;
+            }else*/
+            if(dead){
+                extl_warn(TR("Returned dead object."));
+                return FALSE;
+            }else{
+                extl_warn(TR("Invalid return value (expected '%c', "
+                             "got lua type \"%s\")."),
+                     *spec, lua_typename(st, lua_type(st, -m)));
+                return FALSE;
+            }
+        }
+        
+        (param->nret)++;
+        spec++;
+        m--;
+    }
+
+#ifdef CF_HAS_VA_COPY
+    va_end(args);
+#endif
+
+    return TRUE;
+}
+
+
+/* The function to be called is expected on the top of stack st.
+ * This function should be cpcalled through extl_cpcall_call (below), which
+ * will take care that we don't leak anything in case of error.
+ */
+static bool extl_dodo_call_vararg(lua_State *st, ExtlDoCallParam *param)
+{
+    bool ret=TRUE;
+    int n=0, m=0;
+    
+    if(lua_isnil(st, -1))
+        return FALSE;
+
+    if(param->spec!=NULL)
+        n=strlen(param->spec);
+
+    if(!lua_checkstack(st, n+8)){
+        extl_warn(TR("Stack full."));
+        return FALSE;
+    }
+    
+    if(n>0){
+        if(!extl_push_args(st, param->spec, param->args))
+            return FALSE;
+    }
+
+    if(param->rspec!=NULL)
+        m=strlen(param->rspec);
+    
+    flushtrace();
+    
+    if(lua_pcall(st, n, m, 0)!=0){
+        extl_warn("%s", lua_tostring(st, -1));
+        return FALSE;
+    }
+
+    if(m>0)
+        return extl_get_retvals(st, m, param);
+    
+    return TRUE;
+}
+
+
+static bool extl_cpcall_call(lua_State *st, ExtlCPCallFn *fn, 
+                             ExtlDoCallParam *param)
+{
+    void *ptr;
+    int i;
+    
+    param->nret=0;
+    
+    if(extl_cpcall(st, fn, param))
+        return TRUE;
+    
+    /* If param.nret>0, there was an error getting some return value and
+     * we must free what we got.
+     */
+    
+    for(i=0; i<param->nret; i++){
+#ifdef CF_HAS_VA_COPY
+        ptr=va_arg(*(param->args), void*);
+#else
+        ptr=param->ret_ptrs[i];
+#endif
+        extl_free(ptr, *(param->rspec+i), STRINGS_ALL);
+    }
+    
+    return FALSE;
+}
+
+
+static bool extl_do_call_vararg(lua_State *st, ExtlDoCallParam *param)
+{
+    if(!extl_getref(st, *(ExtlFn*)(param->misc)))
+        return FALSE;
+    return extl_dodo_call_vararg(st, param);
+}
+
+
+bool extl_call_vararg(ExtlFn fnref, const char *spec,
+                      const char *rspec, va_list *args)
+{
+    ExtlDoCallParam param;
+    
+    if(fnref==LUA_NOREF || fnref==LUA_REFNIL)
+        return FALSE;
+
+    param.spec=spec;
+    param.rspec=rspec;
+    param.args=args;
+    param.misc=(void*)&fnref;
+
+    return extl_cpcall_call(l_st, (ExtlCPCallFn*)extl_do_call_vararg, &param);
+}
+
+
+bool extl_call(ExtlFn fnref, const char *spec, const char *rspec, ...)
+{
+    bool retval;
+    va_list args;
+    
+    va_start(args, rspec);
+    retval=extl_call_vararg(fnref, spec, rspec, &args);
+    va_end(args);
+    
+    return retval;
+}
+
+
+/*}}}*/
+
+
+/*{{{ extl_loadfile/string */
+
+
+static int call_loaded(lua_State *st)
+{
+    int i, nargs=lua_gettop(st);
+
+    /* Get the loaded file/string as function */
+    lua_pushvalue(st, lua_upvalueindex(1));
+    
+    /* Fill 'arg' */
+    lua_getfenv(st, -1);
+    lua_pushstring(st, "arg");
+    
+    if(nargs>0){
+        lua_newtable(st);
+        for(i=1; i<=nargs; i++){
+            lua_pushvalue(st, i);
+            lua_rawseti_check(st, -2, i);
+        }
+    }else{
+        lua_pushnil(st);
+    }
+    
+    lua_rawset_check(st, -3);
+    lua_pop(st, 1);
+    lua_call(st, 0, LUA_MULTRET);
+    return (lua_gettop(st)-nargs);
+}
+
+
+typedef struct{
+    const char *src;
+    bool isfile;
+    ExtlFn *resptr;
+} ExtlLoadParam;
+
+
+static bool extl_do_load(lua_State *st, ExtlLoadParam *param)
+{
+    int res=0;
+    
+    if(param->isfile){
+        res=luaL_loadfile(st, param->src);
+    }else{
+        res=luaL_loadbuffer(st, param->src, strlen(param->src), param->src);
+    }
+    
+    if(res!=0){
+        extl_warn("%s", lua_tostring(st, -1));
+        return FALSE;
+    }
+    
+    lua_newtable(st); /* Create new environment */
+    /* Now there's fn, newenv in stack */
+    lua_newtable(st); /* Create metatable */
+    lua_pushstring(st, "__index");
+    lua_getfenv(st, -4); /* Get old environment */
+    lua_rawset_check(st, -3); /* Set metatable.__index */
+    lua_pushstring(st, "__newindex");
+    lua_getfenv(st, -4); /* Get old environment */
+    lua_rawset_check(st, -3); /* Set metatable.__newindex */
+    /* Now there's fn, newenv, meta in stack */
+    lua_setmetatable(st, -2); /* Set metatable for new environment */
+    lua_setfenv(st, -2);
+    /* Now there should be just fn in stack */
+
+    /* Callloaded will put any parameters it gets in the table 'arg' in
+     * the newly created environment.
+     */
+    lua_pushcclosure(st, call_loaded, 1);
+    *(param->resptr)=lua_ref(st, -1);
+    
+    return TRUE;
+}
+
+
+bool extl_loadfile(const char *file, ExtlFn *ret)
+{
+    ExtlLoadParam param;
+    param.src=file;
+    param.isfile=TRUE;
+    param.resptr=ret;
+
+    return extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_load, &param);
+}
+
+
+bool extl_loadstring(const char *str, ExtlFn *ret)
+{
+    ExtlLoadParam param;
+    param.src=str;
+    param.isfile=FALSE;
+    param.resptr=ret;
+
+    return extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_load, &param);
+}
+
+
+/*}}}*/
+
+
+/*{{{ L1 CH error logging */
+
+#ifdef EXTL_LOG_ERRORS
+
+INTRSTRUCT(WarnChain);
+DECLSTRUCT(WarnChain){
+    bool need_trace;
+    lua_State *st;
+    WarnHandler *old_handler;
+    WarnChain *prev;
+};
+
+
+static WarnChain *warnchain=NULL;
+static int notrace=0;
+
+
+static void l1_warn_handler(const char *message)
+{
+    WarnChain *ch=warnchain;
+    static int called=0;
+    
+    assert(warnchain!=NULL);
+    
+    if(called==0 && notrace==0)
+        ch->need_trace=TRUE;
+    
+    called++;
+    warnchain=ch->prev;
+    ch->old_handler(message);
+    warnchain=ch;
+    called--;
+}
+
+
+static void do_trace(WarnChain *ch)
+{
+    const char *p;
+
+    if(notrace!=0)
+        return;
+    
+    extl_stack_trace(ch->st);
+    p=lua_tostring(ch->st, -1);
+    notrace++;
+    extl_warn(p);
+    notrace--;
+    ch->need_trace=FALSE;
+    lua_pop(ch->st, 1);
+}
+
+static void flushtrace()
+{
+    if(warnchain && warnchain->need_trace)
+        do_trace(warnchain);
+}
+
+#endif
+
+/*}}}*/
+
+
+/*{{{ L1-CH safe functions */
+
+
+static int protect_count=0;
+static ExtlSafelist *safelists=NULL;
+
+
+void extl_protect(ExtlSafelist *l)
+{
+    protect_count++;
+    if(l!=NULL){
+        if(l->count==0){
+            LINK_ITEM(safelists, l, next, prev);
+        }
+        l->count++;
+    }
+}
+
+
+void extl_unprotect(ExtlSafelist *l)
+{
+    assert(protect_count>0);
+    protect_count--;
+    
+    if(l!=NULL){
+        assert(l->count>0);
+        l->count--;
+        if(l->count==0){
+            UNLINK_ITEM(safelists, l, next, prev);
+        }
+    }
+}
+
+
+static bool extl_check_protected(ExtlExportedFnSpec *spec)
+{
+    ExtlSafelist *l;
+    bool ok=FALSE;
+    int j;
+
+    if(protect_count>0 && !spec->safe){
+        for(l=safelists; l!=NULL; l=l->next){
+            ok=TRUE;
+            for(j=0; l->list[j]!=NULL; j++){
+                if(l->list[j]==spec->fn)
+                    break;
+            }
+            if(l->list[j]==NULL){
+                ok=FALSE;
+                break;
+            }
+        }
+    }else{
+        ok=TRUE;
+    }
+    
+    return ok;
+}
+    
+
+/*}}}*/
+
+
+/*{{{ L1 call handler */
+
+/* To get around potential memory leaks and corruption that could be caused
+ * by Lua's longjmp-on-error lameness, The L1 call handler is divided into
+ * two steps. In the first step we first setup a call to the second step.
+ * At this point it is still fine if Lua raises an error. Then we set up
+ * our warning handlers and stuff--at which point Lua's raising an error
+ * would corrupt our data--and finally call the second step with lua_pcall.
+ * Now the second step can safely call Lua's functions and do what is needed.
+ * When the second step returns, we deallocate our data in the L1Param
+ * structure that was passed to the second step and reset warning handlers.
+ * After that it is again safe to call Lua's functions.
+ */
+
+typedef struct{
+    ExtlL2Param ip[MAX_PARAMS];
+    ExtlL2Param op[MAX_PARAMS];
+    ExtlExportedFnSpec *spec;
+    int ii, ni,  no;
+} L1Param;
+
+static L1Param *current_param=NULL;
+
+
+static int extl_l1_call_handler2(lua_State *st)
+{
+    L1Param *param=current_param;
+    ExtlExportedFnSpec *spec=param->spec;
+    int i;
+
+    D(fprintf(stderr, "%s called\n", spec->name));
+    
+   if(!lua_checkstack(st, MAX_PARAMS+1)){
+        extl_warn(TR("Stack full."));
+        return 0;
+    }
+    
+    param->ni=(spec->ispec==NULL ? 0 : strlen(spec->ispec));
+    
+    for(i=0; i<param->ni; i++){
+        bool dead=FALSE;
+        if(!extl_stack_get(st, i+1, spec->ispec[i], FALSE, &dead,
+                           (void*)&(param->ip[i]))){
+            if(dead){
+                extl_warn(TR("Argument %d to %s is a dead object."),
+                          i+1, spec->name);
+            }else{
+                extl_warn(TR("Argument %d to %s is of invalid type. "
+                             "(Argument template is '%s', got lua type %s)."),
+                          i+1, spec->name, spec->ispec,
+                          lua_typename(st, lua_type(st, i+1)));
+            }
+            return 0;
+        }
+        
+        param->ii=i+1;
+    }
+    
+    if(spec->untraced)
+        notrace++;
+        
+    if(!spec->l2handler(spec->fn, param->ip, param->op))
+        return 0;
+        
+    if(spec->untraced)
+        notrace--;
+    
+    param->no=(spec->ospec==NULL ? 0 : strlen(spec->ospec));
+
+    for(i=0; i<param->no; i++)
+        extl_stack_push(st, spec->ospec[i], (void*)&(param->op[i]));
+    
+    return param->no;
+}
+
+
+static void extl_l1_finalize(L1Param *param)
+{
+    ExtlExportedFnSpec *spec=param->spec;
+    int i;
+    
+    for(i=0; i<param->ii; i++)
+        extl_free((void*)&(param->ip[i]), spec->ispec[i], STRINGS_NONE);
+
+    for(i=0; i<param->no; i++)
+        extl_free((void*)&(param->op[i]), spec->ospec[i], STRINGS_NONCONST);
+}
+
+
+
+static bool extl_l1_just_check_protected=FALSE;
+
+
+static int extl_l1_call_handler(lua_State *st)
+{
+#ifdef EXTL_LOG_ERRORS    
+    WarnChain ch;
+#endif    
+    L1Param param={{NULL, }, {NULL, }, NULL, 0, 0, 0};
+    L1Param *old_param;
+    int ret;
+    int n=lua_gettop(st);
+    
+    
+    /* Get the info we need on the function, check it's ok, and then set
+     * up a safe environment for extl_l1_call_handler2. 
+     */
+    param.spec=(ExtlExportedFnSpec*)lua_touserdata(st, lua_upvalueindex(1));
+
+    if(param.spec==NULL){
+        extl_warn(TR("L1 call handler upvalues corrupt."));
+        return 0;
+    }
+    
+    if(param.spec->fn==NULL){
+        extl_warn(TR("Called function has been unregistered."));
+        return 0;
+    }
+
+    if(extl_l1_just_check_protected){
+        /* Just checking whether the function may be called. */
+        lua_pushboolean(st, !extl_check_protected(param.spec));
+        return 1;
+    }
+    
+    if(!extl_check_protected(param.spec)){
+        extl_warn(TR("Attempt to call an unsafe function \"%s\" in "
+                     "restricted mode."), param.spec->name);
+        return 0;
+    }
+    
+    
+    lua_pushcfunction(st, extl_l1_call_handler2);
+    lua_insert(st, 1);
+    
+    old_param=current_param;
+    current_param=&param;
+    
+#ifdef EXTL_LOG_ERRORS    
+    ch.old_handler=set_warn_handler(l1_warn_handler);
+    ch.need_trace=FALSE;
+    ch.st=st;
+    ch.prev=warnchain;
+    warnchain=&ch;
+#endif
+
+    /* Ok, Lua may now freely fail in extl_l1_call_handler2, we can handle
+     * that.
+     */
+    ret=lua_pcall(st, n, LUA_MULTRET, 0);
+    
+    /* Now that the actual call handler has returned, we need to free
+     * any of our data before calling Lua again.
+     */
+    current_param=old_param;
+    extl_l1_finalize(&param);
+
+#ifdef EXTL_LOG_ERRORS    
+    warnchain=ch.prev;
+    set_warn_handler(ch.old_handler);
+
+    /* Ok, we can now safely use Lua functions again without fear of
+     * leaking.
+     */
+    if(ret!=0){
+        const char *p;
+        param.no=0;
+        p=lua_tostring(st, -1);
+        notrace++;
+        extl_warn("%s", p);
+        notrace--;
+    }
+
+    if(ret!=0 || ch.need_trace)
+        do_trace(&ch);
+#else
+    if(ret!=0)
+        lua_error(st);
+#endif
+
+    return param.no;
+}
+
+
+/*EXTL_DOC
+ * Is calling the function \var{fn} not allowed now? If \var{fn} is nil,
+ * tells if some functions are not allowed to be called now due to
+ * protected mode.
+ */
+EXTL_EXPORT_AS(global, protected)
+bool __protected(ExtlFn fn);
+
+static int extl_protected(lua_State *st)
+{
+    int ret;
+    
+    if(lua_isnil(st, 1)){
+        lua_pushboolean(st, protect_count>0);
+        return 1;
+    }
+
+    if(!lua_isfunction(st, 1)){
+        lua_pushboolean(st, TRUE);
+        return 1;
+    }
+    
+    if(lua_tocfunction(st, 1)!=(lua_CFunction)extl_l1_call_handler){
+        lua_pushboolean(st, FALSE);
+        return 1;
+    }
+     
+    extl_l1_just_check_protected=TRUE;
+    ret=lua_pcall(st, 0, 1, 0);
+    extl_l1_just_check_protected=FALSE;
+    if(ret!=0)
+        lua_pushboolean(st, TRUE);
+    return 1;
+}
+
+/*}}}*/
+    
+
+/*{{{ Function registration */
+
+
+typedef struct{
+    ExtlExportedFnSpec *spec;
+    const char *cls;
+    ExtlTab table;
+} RegData;
+
+
+static bool extl_do_register_function(lua_State *st, RegData *data)
+{
+    ExtlExportedFnSpec *spec=data->spec, *spec2;
+    int ind=LUA_GLOBALSINDEX;
+    
+    if((spec->ispec!=NULL && strlen(spec->ispec)>MAX_PARAMS) ||
+       (spec->ospec!=NULL && strlen(spec->ospec)>MAX_PARAMS)){
+        extl_warn(TR("Function '%s' has more parameters than the level 1 "
+                     "call handler can handle"), spec->name);
+        return FALSE;
+    }
+
+    if(data->table!=LUA_NOREF){
+        lua_rawgeti(st, LUA_REGISTRYINDEX, data->table);
+        ind=-3;
+    }
+
+    lua_pushstring(st, spec->name);
+
+    spec2=lua_newuserdata(st, sizeof(ExtlExportedFnSpec));
+    
+    memcpy(spec2, spec, sizeof(ExtlExportedFnSpec));
+
+    lua_getregistry(st);
+    lua_pushvalue(st, -2); /* Get spec2 */
+    lua_pushfstring(st, "luaextl_%s_%s_upvalue", 
+                    data->cls, spec->name);
+    lua_rawset_check(st, -3); /* Set registry.luaextl_fn_upvalue=spec2 */
+    lua_pop(st, 1); /* Pop registry */
+    lua_pushcclosure(st, extl_l1_call_handler, 1);
+    lua_rawset_check(st, ind);
+
+    return TRUE;
+}
+
+
+static bool extl_do_register_functions(ExtlExportedFnSpec *spec, int max,
+                                       const char *cls, int table)
+{
+    int i;
+    
+    RegData regdata;
+    regdata.spec=spec;
+    regdata.cls=cls;
+    regdata.table=table;
+    
+    for(i=0; spec[i].name && i<max; i++){
+        regdata.spec=&(spec[i]);
+        if(!extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_register_function, 
+                        &regdata)){
+            return FALSE;
+        }
+    }
+    
+    return TRUE;
+}
+
+
+bool extl_register_function(ExtlExportedFnSpec *spec)
+{
+    return extl_do_register_functions(spec, 1, "", LUA_NOREF);
+}
+
+
+bool extl_register_functions(ExtlExportedFnSpec *spec)
+{
+    return extl_do_register_functions(spec, INT_MAX, "", LUA_NOREF);
+}
+
+
+static bool extl_do_unregister_function(lua_State *st, RegData *data)
+{
+    ExtlExportedFnSpec *spec=data->spec, *spec2;
+    int ind=LUA_GLOBALSINDEX;
+
+    lua_getregistry(st);
+    lua_pushfstring(st, "luaextl_%s_%s_upvalue", 
+                    data->cls, spec->name);
+    lua_pushvalue(st, -1);
+    lua_gettable(st, -3); /* Get registry.luaextl_fn_upvalue */
+    spec2=lua_touserdata(st, -1);
+
+    if(spec2==NULL)
+        return FALSE;
+    
+    spec2->ispec=NULL;
+    spec2->ospec=NULL;
+    spec2->fn=NULL;
+    spec2->name=NULL;
+    spec2->l2handler=NULL;
+
+    lua_pop(st, 1); /* Pop the upvalue */
+    lua_pushnil(st);
+    lua_rawset_check(st, -3); /* Clear registry.luaextl_fn_upvalue */
+    
+    if(data->table!=LUA_NOREF){
+        lua_rawgeti(st, LUA_REGISTRYINDEX, data->table);
+        ind=-3;
+    }
+    
+    /* Clear table.fn */
+    lua_pushstring(st, spec->name);
+    lua_pushnil(st); 
+    lua_rawset_check(st, ind);
+    
+    return TRUE;
+}
+
+
+static void extl_do_unregister_functions(ExtlExportedFnSpec *spec, int max,
+                                         const char *cls, int table)
+{
+    int i;
+    
+    RegData regdata;
+    regdata.spec=spec;
+    regdata.cls=cls;
+    regdata.table=table;
+    
+    for(i=0; spec[i].name && i<max; i++){
+        regdata.spec=&(spec[i]);
+        extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_unregister_function,
+                    &regdata);
+    }
+}
+
+void extl_unregister_function(ExtlExportedFnSpec *spec)
+{
+    extl_do_unregister_functions(spec, 1, "", LUA_NOREF);
+}
+
+
+void extl_unregister_functions(ExtlExportedFnSpec *spec)
+{
+    extl_do_unregister_functions(spec, INT_MAX, "", LUA_NOREF);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Class registration */
+
+
+typedef struct{
+    const char *cls, *parent;
+    int refret;
+    bool hide;
+} ClassData;
+
+        
+static bool extl_do_register_class(lua_State *st, ClassData *data)
+{
+    /* Create the globally visible WFoobar table in which the function
+     * references reside.
+     */
+    lua_newtable(st);
+    
+    /* Set type information.
+     */
+    lua_pushstring(st, "__typename");
+    lua_pushstring(st, data->cls);
+    lua_settable(st, -3);
+
+    /* If we have a parent class (i.e. class!=Obj), we want also the parent's
+     * functions visible in this table so set up a metatable to do so.
+     */
+    if(data->parent!=NULL){
+        /* Get luaextl_ParentClass_metatable */
+        lua_pushfstring(st, "luaextl_%s_metatable", data->parent);
+        lua_gettable(st, LUA_REGISTRYINDEX);
+        if(!lua_istable(st, -1)){
+            extl_warn("Could not find metatable for parent class %s of %s.\n",
+                      data->parent, data->cls);
+            return FALSE;
+        }
+        /* Create our metatable */
+        lua_newtable(st);
+        /* Get parent_metatable.__index */
+        lua_pushstring(st, "__index");
+        lua_pushvalue(st, -1);
+        /* Stack: cls, parent_meta, meta, "__index", "__index" */
+        lua_gettable(st, -4);
+        /* Stack: cls, parent_meta, meta, "__index", parent_meta.__index */
+        lua_pushvalue(st, -1);
+        lua_insert(st, -3);
+        /* Stack: cls, parent_meta, meta, parent_meta.__index, "__index", parent_meta.__index */
+        lua_rawset_check(st, -4);
+        /* Stack: cls, parent_meta, meta, parent_meta.__index */
+        lua_pushstring(st, "__parentclass");
+        lua_insert(st, -2);
+        /* Stack: cls, parent_meta, meta, "__parentclass", parent_meta.__index */
+        lua_settable(st, -5);
+        /* Stack: cls, parent_meta, meta, */
+        lua_setmetatable(st, -3);
+        lua_pop(st, 1);
+        /* Stack: cls */
+    }
+    
+    /* Set the global WFoobar */
+    lua_pushvalue(st, -1);
+    data->refret=lua_ref(st, 1); /* TODO: free on failure */
+    if(!data->hide){
+        lua_pushstring(st, data->cls);
+        lua_pushvalue(st, -2);
+        lua_rawset(st, LUA_GLOBALSINDEX);
+    }
+
+    /* New we create a metatable for the actual objects with __gc metamethod
+     * and __index pointing to the table created above. The MAGIC entry is 
+     * used to check that userdatas passed to us really are Watches with a
+     * high likelihood.
+     */
+    lua_newtable(st);
+
+    lua_pushnumber(st, MAGIC);
+    lua_pushnumber(st, MAGIC);
+    lua_rawset_check(st, -3);
+    
+    lua_pushstring(st, "__index");
+    lua_pushvalue(st, -3);
+    lua_rawset_check(st, -3); /* set metatable.__index=WFoobar created above */
+    lua_pushstring(st, "__gc");
+    lua_pushcfunction(st, extl_obj_gc_handler);
+    lua_rawset_check(st, -3); /* set metatable.__gc=extl_obj_gc_handler */
+    lua_pushfstring(st, "luaextl_%s_metatable", data->cls);
+    lua_insert(st, -2);
+    lua_rawset(st, LUA_REGISTRYINDEX);
+    
+    return TRUE;
+}
+
+
+bool extl_register_class(const char *cls, ExtlExportedFnSpec *fns,
+                         const char *parent)
+{
+    ClassData clsdata;
+    clsdata.cls=cls;
+    clsdata.parent=parent;
+    clsdata.refret=LUA_NOREF;
+    clsdata.hide=(strcmp(cls, "Obj")==0);/*(fns==NULL);*/
+    
+    D(assert(strcmp(cls, "Obj")==0 || parent!=NULL));
+           
+    if(!extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_register_class, &clsdata))
+        return FALSE;
+
+    if(fns==NULL)
+        return TRUE;
+    
+    return extl_do_register_functions(fns, INT_MAX, cls, clsdata.refret);
+}
+
+
+static void extl_do_unregister_class(lua_State *st, ClassData *data)
+{
+    /* Get reference from registry to the metatable. */
+    lua_pushfstring(st, "luaextl_%s_metatable", data->cls);
+    lua_pushvalue(st, -1);
+    lua_gettable(st, LUA_REGISTRYINDEX);
+    /* Get __index and return it for resetting the functions. */
+    lua_pushstring(st, "__index");
+    lua_gettable(st, -2);
+    data->refret=lua_ref(st, -1);
+    lua_pop(st, 1);
+    /* Set the entry from registry to nil. */
+    lua_pushnil(st);
+    lua_rawset(st, LUA_REGISTRYINDEX);
+    
+    /* Reset the global reference to the class to nil. */
+    lua_pushstring(st, data->cls);
+    lua_pushnil(st);
+    lua_rawset(st, LUA_GLOBALSINDEX);
+}
+
+
+void extl_unregister_class(const char *cls, ExtlExportedFnSpec *fns)
+{
+    ClassData clsdata;
+    clsdata.cls=cls;
+    clsdata.parent=NULL;
+    clsdata.refret=LUA_NOREF;
+    clsdata.hide=FALSE; /* unused, but initialise */
+    
+    if(!extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_unregister_class, 
+                    &clsdata))
+        return;
+    
+    /* We still need to reset function upvalues. */
+    if(fns!=NULL)
+        extl_do_unregister_functions(fns, INT_MAX, cls, clsdata.refret);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Module registration */
+
+
+static bool extl_do_register_module(lua_State *st, ClassData *clsdata)
+{
+    lua_getglobal(st, clsdata->cls);
+    
+    if(!lua_istable(st, -1)){
+        lua_newtable(st);
+        lua_pushvalue(st, -1);
+        lua_setglobal(st, clsdata->cls);
+    }
+    lua_pushfstring(st, "luaextl_module_%s", clsdata->cls);
+    lua_pushvalue(st, -2);
+    lua_rawset(st, LUA_REGISTRYINDEX);
+    
+    clsdata->refret=lua_ref(st, -1);
+    
+    return TRUE;
+}
+
+
+bool extl_register_module(const char *mdl, ExtlExportedFnSpec *fns)
+{
+    ClassData clsdata;
+    
+    clsdata.cls=mdl;
+    clsdata.parent=NULL;
+    clsdata.refret=LUA_NOREF;
+    clsdata.hide=FALSE; /* unused, but initialise */
+    
+    if(!extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_register_module, &clsdata))
+        return FALSE;
+
+    if(fns==NULL)
+        return TRUE;
+    
+    return extl_do_register_functions(fns, INT_MAX, mdl, clsdata.refret);
+}
+
+
+static bool extl_do_unregister_module(lua_State *st, ClassData *clsdata)
+{
+    lua_pushfstring(st, "luaextl_module_%s", clsdata->cls);
+    lua_pushvalue(st, -1);
+    lua_pushnil(st);
+    lua_rawset(st, LUA_REGISTRYINDEX);
+    clsdata->refret=lua_ref(st, -1);
+    
+    return TRUE;
+}
+
+
+void extl_unregister_module(const char *mdl, ExtlExportedFnSpec *fns)
+{
+    ClassData clsdata;
+    
+    clsdata.cls=mdl;
+    clsdata.parent=NULL;
+    clsdata.refret=LUA_NOREF;
+    clsdata.hide=FALSE; /* unused, but initialise */
+    
+    if(!extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_unregister_module, &clsdata))
+        return;
+
+    if(fns!=NULL)
+        extl_do_unregister_functions(fns, INT_MAX, mdl, clsdata.refret);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Serialise */
+
+typedef struct{
+    FILE *f;
+    ExtlTab tab;
+} SerData;
+
+
+static void write_escaped_string(FILE *f, const char *str)
+{
+    fputc('"', f);
+
+    while(str && *str){
+        if(((*str)&0x7f)<32 || *str=='"' || *str=='\\'){
+            /* Lua uses decimal in escapes */
+            fprintf(f, "\\%03d", (int)(uchar)(*str));
+        }else{
+            fputc(*str, f);
+        }
+        str++;
+    }
+    
+    fputc('"', f);
+}
+
+
+static void indent(FILE *f, int lvl)
+{
+    int i;
+    for(i=0; i<lvl; i++)
+        fprintf(f, "    ");
+}
+
+
+static bool ser(lua_State *st, FILE *f, int lvl)
+{
+    
+    lua_checkstack(st, 5);
+    
+    switch(lua_type(st, -1)){
+    case LUA_TBOOLEAN:
+        fprintf(f, "%s", lua_toboolean(st, -1) ? "true" : "false");
+        break;
+    case LUA_TNUMBER:
+        fprintf(f, "%s", lua_tostring(st, -1));
+        break;
+    case LUA_TNIL:
+        fprintf(f, "nil");
+        break;
+    case LUA_TSTRING:
+        write_escaped_string(f, lua_tostring(st, -1));
+        break;
+    case LUA_TTABLE:
+        if(lvl+1>=EXTL_MAX_SERIALISE_DEPTH){
+            extl_warn(TR("Maximal serialisation depth reached."));
+            fprintf(f, "nil");
+            lua_pop(st, 1);
+            return FALSE;
+        }
+
+        fprintf(f, "{\n");
+        lua_pushnil(st);
+        while(lua_next(st, -2)!=0){
+            lua_pushvalue(st, -2);
+            indent(f, lvl+1);
+            fprintf(f, "[");
+            ser(st, f, lvl+1);
+            fprintf(f, "] = ");
+            ser(st, f, lvl+1);
+            fprintf(f, ",\n");
+        }
+        indent(f, lvl);
+        fprintf(f, "}");
+        break;
+    default:
+        extl_warn(TR("Unable to serialise type %s."), 
+                  lua_typename(st, lua_type(st, -1)));
+    }
+    lua_pop(st, 1);
+    return TRUE;
+}
+
+
+static bool extl_do_serialise(lua_State *st, SerData *d)
+{
+    if(!extl_getref(st, d->tab))
+        return FALSE;
+    
+    return ser(st, d->f, 0);
+}
+
+
+/* Tab must not contain recursive references! */
+extern bool extl_serialise(const char *file, ExtlTab tab)
+{
+    SerData d;
+    bool ret;
+
+    d.tab=tab;
+    d.f=fopen(file, "w");
+    
+    if(d.f==NULL){
+        extl_warn_err_obj(file);
+        return FALSE;
+    }
+    
+    fprintf(d.f, TR("-- This file has been generated by Ion. Do not edit.\n"));
+    fprintf(d.f, "return ");
+    
+    ret=extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_serialise, &d);
+    
+    fprintf(d.f, "\n\n");
+    
+    fclose(d.f);
+    
+    return ret;
+}
+
+
+/*}}}*/
+
diff --git a/libextl/luaextl.h b/libextl/luaextl.h
new file mode 100644 (file)
index 0000000..28cfde8
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * libextl/luaextl.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#ifndef LIBEXTL_LUAEXTL_H
+#define LIBEXTL_LUAEXTL_H
+
+#include <stdarg.h>
+
+#include "types.h"
+
+#define EXTL_EXTENSION "lua"
+#define EXTL_COMPILED_EXTENSION "lc"
+#define EXTL_MAX_SERIALISE_DEPTH 128
+
+/* o: userdata/Obj
+ * i: integer
+ * d: double
+ * b: boolean
+ * s: string
+ * t: table
+ * f: function (c or lua)
+ * v: void
+ */
+
+typedef int ExtlTab;
+typedef int ExtlFn;
+
+typedef union{
+    Obj *o;
+    int i;
+    double d;
+    bool b;
+    const char *s;
+    ExtlFn f;
+    ExtlTab t;
+} ExtlL2Param;
+
+typedef bool ExtlL2CallHandler(void (*fn)(), ExtlL2Param *in,
+                               ExtlL2Param *out);
+
+typedef void ExtlExportedFn(void);
+
+typedef struct{
+    const char *name;
+    ExtlExportedFn *fn;
+    const char *ispec;
+    const char *ospec;
+    ExtlL2CallHandler *l2handler;
+    bool safe;
+    bool untraced;
+} ExtlExportedFnSpec;
+
+typedef struct ExtlSafelist_struct{
+    int count;
+    struct ExtlSafelist_struct *next, *prev;
+    ExtlExportedFn **list;
+} ExtlSafelist;
+
+#define EXTL_SAFELIST_INIT(L) {0, NULL, NULL, L}
+
+extern ExtlFn extl_unref_fn(ExtlFn ref);
+extern ExtlTab extl_unref_table(ExtlTab ref);
+extern ExtlFn extl_fn_none();
+extern ExtlTab extl_table_none();
+
+extern bool extl_fn_eq(ExtlFn fn1, ExtlFn fn2);
+extern bool extl_table_eq(ExtlTab fn1, ExtlTab fn2);
+
+/* These should be called to store function and table references gotten
+ * as arguments to functions.
+ */
+extern ExtlFn extl_ref_fn(ExtlFn ref);
+extern ExtlTab extl_ref_table(ExtlTab ref);
+
+extern ExtlTab extl_create_table();
+
+/* Table/get */
+extern bool extl_table_get_vararg(ExtlTab ref, char itype, char type, 
+                                  va_list *args);
+extern bool extl_table_get(ExtlTab ref, char itype, char type, ...);
+
+extern bool extl_table_gets_o(ExtlTab ref, const char *entry, Obj **ret);
+extern bool extl_table_gets_i(ExtlTab ref, const char *entry, int *ret);
+extern bool extl_table_gets_d(ExtlTab ref, const char *entry, double *ret);
+extern bool extl_table_gets_b(ExtlTab ref, const char *entry, bool *ret);
+extern bool extl_table_gets_s(ExtlTab ref, const char *entry, char **ret);
+extern bool extl_table_gets_f(ExtlTab ref, const char *entry, ExtlFn *ret);
+extern bool extl_table_gets_t(ExtlTab ref, const char *entry, ExtlTab *ret);
+
+extern int extl_table_get_n(ExtlTab ref);
+extern bool extl_table_geti_o(ExtlTab ref, int entry, Obj **ret);
+extern bool extl_table_geti_i(ExtlTab ref, int entry, int *ret);
+extern bool extl_table_geti_d(ExtlTab ref, int entry, double *ret);
+extern bool extl_table_geti_b(ExtlTab ref, int entry, bool *ret);
+extern bool extl_table_geti_s(ExtlTab ref, int entry, char **ret);
+extern bool extl_table_geti_f(ExtlTab ref, int entry, ExtlFn *ret);
+extern bool extl_table_geti_t(ExtlTab ref, int entry, ExtlTab *ret);
+
+/* Table/set */
+extern bool extl_table_set_vararg(ExtlTab ref, char itype, char type, 
+                                  va_list *args);
+extern bool extl_table_set(ExtlTab ref, char itype, char type, ...);
+
+extern bool extl_table_sets_o(ExtlTab ref, const char *entry, Obj *val);
+extern bool extl_table_sets_i(ExtlTab ref, const char *entry, int val);
+extern bool extl_table_sets_d(ExtlTab ref, const char *entry, double val);
+extern bool extl_table_sets_b(ExtlTab ref, const char *entry, bool val);
+extern bool extl_table_sets_s(ExtlTab ref, const char *entry, const char *val);
+extern bool extl_table_sets_f(ExtlTab ref, const char *entry, ExtlFn val);
+extern bool extl_table_sets_t(ExtlTab ref, const char *entry, ExtlTab val);
+
+extern bool extl_table_seti_o(ExtlTab ref, int entry, Obj *val);
+extern bool extl_table_seti_i(ExtlTab ref, int entry, int val);
+extern bool extl_table_seti_d(ExtlTab ref, int entry, double val);
+extern bool extl_table_seti_b(ExtlTab ref, int entry, bool val);
+extern bool extl_table_seti_s(ExtlTab ref, int entry, const char *val);
+extern bool extl_table_seti_f(ExtlTab ref, int entry, ExtlFn val);
+extern bool extl_table_seti_t(ExtlTab ref, int entry, ExtlTab val);
+
+/* Table/clear */
+
+extern bool extl_table_clear_vararg(ExtlTab ref, char itype, va_list *args);
+extern bool extl_table_clear(ExtlTab ref, char itype, ...);
+
+extern bool extl_table_clears(ExtlTab ref, const char *entry);
+extern bool extl_table_cleari(ExtlTab ref, int entry);
+
+/* Call */
+
+extern void extl_protect(ExtlSafelist *sl);
+extern void extl_unprotect(ExtlSafelist *sl);
+
+extern bool extl_call_vararg(ExtlFn fnref, const char *spec,
+                             const char *rspec, va_list *args);
+extern bool extl_call(ExtlFn fnref, const char *spec,
+                      const char *rspec, ...);
+
+/* Files */
+
+extern bool extl_loadfile(const char *file, ExtlFn *ret);
+extern bool extl_loadstring(const char *str, ExtlFn *ret);
+extern bool extl_serialise(const char *file, ExtlTab tab);
+
+/* Register */
+
+extern bool extl_register_function(ExtlExportedFnSpec *spec);
+extern void extl_unregister_function(ExtlExportedFnSpec *spec);
+extern bool extl_register_functions(ExtlExportedFnSpec *spec);
+extern void extl_unregister_functions(ExtlExportedFnSpec *spec);
+
+bool extl_register_class(const char *cls, ExtlExportedFnSpec *fns,
+                         const char *parent);
+void extl_unregister_class(const char *cls, ExtlExportedFnSpec *fns);
+
+bool extl_register_module(const char *cls, ExtlExportedFnSpec *fns);
+void extl_unregister_module(const char *cls, ExtlExportedFnSpec *fns);
+
+/* Misc. */
+
+extern bool extl_init();
+extern void extl_deinit();
+
+#endif /* LIBEXTL_LUAEXTL_H */
diff --git a/libextl/misc.c b/libextl/misc.c
new file mode 100644 (file)
index 0000000..1cdda45
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * libextl/misc.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2005.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#include "private.h"
+
+
+bool extl_obj_error(int ndx, const char *got, const char *wanted)
+{
+    extl_warn(TR("Type checking failed in level 2 call handler for "
+                 "parameter %d (got %s, expected %s)."),
+              ndx, got ? got : "nil", wanted);
+
+    return FALSE;
+}
+
diff --git a/libextl/private.h b/libextl/private.h
new file mode 100644 (file)
index 0000000..b0f576b
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * libextl/private.h
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2005.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#ifndef LIBEXTL_PRIVATE_H
+#define LIBEXTL_PRIVATE_H
+
+#include <libtu/types.h>
+#include <libtu/misc.h>
+#include <libtu/locale.h>
+#include <libtu/output.h>
+#include <libtu/debug.h>
+#include <libtu/objp.h>
+
+#include "types.h"
+
+/*
+ * String routines
+ */
+
+#define extl_scopy(S) scopy(S)
+#define extl_scopyn(S, N) scopyn(S, N)
+#define extl_scat3(S1, S2, S3) scat3(S1, S2, S3)
+#define extl_asprintf libtu_asprintf
+
+/*
+ * Error display
+ */
+
+#if 1
+#include <libtu/errorlog.h>
+#define EXTL_LOG_ERRORS
+/* This part has not been abstracted away from libtu dependence. */
+#endif
+
+#define extl_warn warn
+#define extl_warn_err_obj(NM) warn_err_obj(NM)
+
+/*#define extl_warn(FMT, ...)
+    ({fprintf(stderr, FMT, __VA_ARGS__); fputc('\n', stderr);})*/
+/*#define extl_warn_obj_err(O) perror(O) */
+
+/* 
+ * Level2 type checking
+ */
+
+/* Always returns FALSE. */
+extern bool extl_obj_error(int ndx, const char *got, const char *wanted);
+
+#define EXTL_CHKO1(IN, NDX, TYPE)                          \
+    (OBJ_IS(IN[NDX].o, TYPE)                               \
+     ? TRUE                                                \
+     : extl_obj_error(NDX, OBJ_TYPESTR(IN[NDX].o), #TYPE))
+
+#define EXTL_CHKO(IN, NDX, TYPE)                           \
+    (IN[NDX].o==NULL || OBJ_IS(IN[NDX].o, TYPE)            \
+     ? TRUE                                                \
+     : extl_obj_error(NDX, OBJ_TYPESTR(IN[NDX].o), #TYPE))
+
+#define EXTL_DEFCLASS(C) INTRCLASS(C)
+
+
+/*
+ * Objects.
+ */
+
+typedef Watch ExtlProxy;
+
+#define EXTL_OBJ_CACHED(OBJ) ((OBJ)->flags&OBJ_EXTL_CACHED)
+#define EXTL_OBJ_OWNED(OBJ) ((OBJ)->flags&OBJ_EXTL_OWNED)
+#define EXTL_OBJ_TYPENAME(OBJ) OBJ_TYPESTR(OBJ)
+#define EXTL_OBJ_IS(OBJ, NAME) obj_is_str(OBJ, NAME)
+
+#define EXTL_PROXY_OBJ(PROXY) ((PROXY)->obj)
+
+#define EXTL_BEGIN_PROXY_OBJ(PROXY, OBJ)       \
+    watch_init(PROXY);                         \
+    watch_setup(PROXY, OBJ, obj_dest_handler); \
+    (OBJ)->flags|=OBJ_EXTL_CACHED;
+
+#define EXTL_END_PROXY_OBJ(PROXY, OBJ) \
+    assert((PROXY)->obj==OBJ);         \
+    watch_reset(PROXY);                \
+    (OBJ)->flags&=~OBJ_EXTL_CACHED;
+
+#define EXTL_DESTROY_OWNED_OBJ(OBJ) destroy_obj(OBJ)
+
+extern void extl_uncache(Obj *obj);
+
+static void obj_dest_handler(Watch *watch, Obj *obj)
+{
+    extl_uncache(obj);
+    obj->flags&=~OBJ_EXTL_CACHED;
+}
+
+
+/* 
+ * Miscellaneous.
+ */
+
+/* Translate string X to locale. */
+/*#define TR(X) X */
+
+/* Debugging. */
+/*#define D(X) */
+
+
+#endif /* LIBEXTL_PRIVATE_H */
diff --git a/libextl/readconfig.c b/libextl/readconfig.c
new file mode 100644 (file)
index 0000000..b9c6061
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * libextl/readconfig.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "readconfig.h"
+#include "extl.h"
+#include "private.h"
+
+
+typedef struct{
+    ExtlFn fn;
+    ExtlTab tab;
+    int status;
+} TryCallParam;
+
+
+/*{{{ Path setup */
+
+
+static char *userdir=NULL;
+static char *sessiondir=NULL;
+static char *scriptpath=NULL;
+
+
+bool extl_add_searchdir(const char *dir)
+{
+    if(scriptpath==NULL){
+        scriptpath=extl_scopy(dir);
+        if(scriptpath==NULL)
+            return FALSE;
+    }else{
+        char *p=extl_scat3(dir, ":", scriptpath);
+        if(p==NULL)
+            return FALSE;
+        free(scriptpath);
+        scriptpath=p;
+    }
+    
+    return TRUE;
+}
+
+
+bool extl_set_searchpath(const char *path)
+{
+    char *s=NULL;
+    
+    if(path!=NULL){
+        s=extl_scopy(path);
+        if(s==NULL)
+            return FALSE;
+    }
+    
+    if(scriptpath!=NULL)
+        free(scriptpath);
+    
+    scriptpath=s;
+    return TRUE;
+}
+
+
+bool extl_set_userdirs(const char *appname)
+{
+    const char *home;
+    char *tmp;
+    int fails=2;
+    
+    if(userdir!=NULL)
+        return FALSE;
+    
+    home=getenv("HOME");
+    
+    if(home==NULL){
+        extl_warn(TR("$HOME not set"));
+    }else{
+        libtu_asprintf(&userdir, "%s/.%s", home, appname);
+        if(userdir!=NULL)
+            fails-=extl_add_searchdir(userdir);
+        
+        libtu_asprintf(&tmp, "%s/.%s/lib", home, appname);
+        if(tmp!=NULL){
+            fails-=extl_add_searchdir(tmp);
+            free(tmp);
+        }
+    }
+    
+    return (fails==0);
+}
+
+
+bool extl_set_sessiondir(const char *session)
+{
+    char *tmp;
+    bool ret=FALSE;
+    
+    if(strchr(session, '/')!=NULL){
+        tmp=extl_scopy(session);
+    }else if(userdir!=NULL){
+        libtu_asprintf(&tmp, "%s/%s", userdir, session);
+    }else{
+        extl_warn(TR("User directory not set. "
+                     "Unable to set session directory."));
+        return FALSE;
+    }
+    
+    if(tmp==NULL)
+        return FALSE;
+    
+    if(sessiondir!=NULL)
+        free(sessiondir);
+    
+    sessiondir=tmp;
+    
+    return TRUE;
+}
+
+
+const char *extl_userdir()
+{
+    return userdir;
+}
+
+
+const char *extl_sessiondir()
+{
+    return sessiondir;
+}
+
+
+const char *extl_searchpath()
+{
+    return scriptpath;
+}
+
+
+/*}}}*/
+
+
+/*{{{ try_etcpath, do_include, etc. */
+
+
+static int do_try(const char *dir, const char *file, ExtlTryConfigFn *tryfn,
+                  void *tryfnparam)
+{
+    char *tmp=NULL;
+    int ret;
+    
+    libtu_asprintf(&tmp, "%s/%s", dir, file);
+    if(tmp==NULL)
+        return EXTL_TRYCONFIG_MEMERROR;
+
+    ret=tryfn(tmp, tryfnparam);
+    free(tmp);
+    return ret;
+}
+
+
+static int try_dir(const char *const *files, const char *cfdir,
+                   ExtlTryConfigFn *tryfn, void *tryfnparam)
+{
+    const char *const *file;
+    int ret, ret2=EXTL_TRYCONFIG_NOTFOUND;
+    
+    for(file=files; *file!=NULL; file++){
+        if(cfdir==NULL)
+            ret=tryfn(*file, tryfnparam);
+        else
+            ret=do_try(cfdir, *file, tryfn, tryfnparam);
+        if(ret>=0)
+            return ret;
+        if(ret2==EXTL_TRYCONFIG_NOTFOUND)
+            ret2=ret;
+    }
+    
+    return ret2;
+}
+
+
+static int try_etcpath(const char *const *files, 
+                       ExtlTryConfigFn *tryfn, void *tryfnparam)
+{
+    const char *const *file=NULL;
+    int i, ret, ret2=EXTL_TRYCONFIG_NOTFOUND;
+    char *path, *colon, *dir;
+
+    if(sessiondir!=NULL){
+        for(file=files; *file!=NULL; file++){
+            ret=do_try(sessiondir, *file, tryfn, tryfnparam);
+            if(ret>=0)
+                return ret;
+            if(ret2==EXTL_TRYCONFIG_NOTFOUND)
+                ret2=ret;
+        }
+    }
+    
+    path=scriptpath;
+    while(path!=NULL){
+        colon=strchr(path, ':');
+        if(colon!=NULL){
+            dir=extl_scopyn(path, colon-path);
+            path=colon+1;
+        }else{
+            dir=extl_scopy(path);
+            path=NULL;
+        }
+        
+        if(dir!=NULL){
+            if(*dir!='\0'){
+                for(file=files; *file!=NULL; file++){
+                    ret=do_try(dir, *file, tryfn, tryfnparam);
+                    if(ret>=0){
+                        free(dir);
+                        return ret;
+                    }
+                    if(ret2==EXTL_TRYCONFIG_NOTFOUND)
+                        ret2=ret;
+                }
+            }
+            free(dir);
+        }
+    }
+    
+    return ret2;
+}
+
+
+static int try_lookup(const char *file, char **ptr)
+{
+    if(access(file, F_OK)!=0)
+        return EXTL_TRYCONFIG_NOTFOUND;
+    *ptr=extl_scopy(file);
+    return (*ptr!=NULL);
+}
+
+
+static int try_load(const char *file, TryCallParam *param)
+{
+    if(access(file, F_OK)!=0)
+        return EXTL_TRYCONFIG_NOTFOUND;
+    
+    if(param->status==1)
+        extl_warn(TR("Falling back to %s."), file);
+    
+    if(!extl_loadfile(file, &(param->fn))){
+        param->status=1;
+        return EXTL_TRYCONFIG_LOAD_FAILED;
+    }
+    
+    return EXTL_TRYCONFIG_OK;
+}
+
+
+static int try_call(const char *file, TryCallParam *param)
+{
+    int ret=try_load(file, param);
+    
+    if(ret!=EXTL_TRYCONFIG_OK)
+        return ret;
+    
+    ret=extl_call(param->fn, NULL, NULL);
+    
+    extl_unref_fn(param->fn);
+    
+    return (ret ? EXTL_TRYCONFIG_OK : EXTL_TRYCONFIG_CALL_FAILED);
+}
+
+
+static int try_read_savefile(const char *file, TryCallParam *param)
+{
+    int ret=try_load(file, param);
+    
+    if(ret!=EXTL_TRYCONFIG_OK)
+        return ret;
+    
+    ret=extl_call(param->fn, NULL, "t", &(param->tab));
+    
+    extl_unref_fn(param->fn);
+    
+    return (ret ? EXTL_TRYCONFIG_OK : EXTL_TRYCONFIG_CALL_FAILED);
+}
+
+
+int extl_try_config(const char *fname, const char *cfdir,
+                    ExtlTryConfigFn *tryfn, void *tryfnparam,
+                    const char *ext1, const char *ext2)
+{
+    char *files[]={NULL, NULL, NULL, NULL};
+    int n=0, ret=EXTL_TRYCONFIG_NOTFOUND, ret2;
+    bool search=FALSE, has_ext;
+    
+    /* Search etcpath only if path is not absolute */
+    search=(fname[0]!='/');
+
+    /* Build list of files to look for */
+    has_ext=strrchr(fname, '.')>strrchr(fname, '/');
+
+    if(!has_ext){
+        if(ext1!=NULL){
+            files[n]=extl_scat3(fname, ".", ext1);
+            if(files[n]!=NULL)
+                n++;
+        }
+
+        if(ext2!=NULL){
+            files[n]=extl_scat3(fname, ".", ext2);
+            if(files[n]!=NULL)
+                n++;
+        }
+    }
+    
+    if(has_ext || !search){
+        files[n]=extl_scopy(fname);
+        if(files[n]!=NULL)
+            n++;
+    }
+    
+    /* NOTE for future changes: cfdir must not be scanned first for
+     * user configuration files to take precedence.
+     */
+
+    /* Scan through all possible files */
+    if(search){
+        ret2=try_etcpath((const char**)&files, tryfn, tryfnparam);
+        if(ret==EXTL_TRYCONFIG_NOTFOUND)
+            ret=ret2;
+        if(ret<0)
+            ret=try_dir((const char**)&files, cfdir, tryfn, tryfnparam);
+    }else{
+        ret=try_dir((const char**)&files, NULL, tryfn, tryfnparam);
+    }
+    
+    while(n>0)
+        free(files[--n]);
+    
+    return ret;
+}
+
+
+/*EXTL_DOC
+ * Lookup script \var{file}. If \var{try_in_dir} is set, it is tried
+ * before the standard search path.
+ */
+EXTL_EXPORT
+char *extl_lookup_script(const char *file, const char *sp)
+{
+    const char *files[]={NULL, NULL};
+    char* tmp=NULL;
+    
+    if(file!=NULL){
+        files[0]=file;
+
+        if(sp!=NULL)
+            try_dir(files, sp, (ExtlTryConfigFn*)try_lookup, &tmp);
+        if(tmp==NULL)
+            try_etcpath(files, (ExtlTryConfigFn*)try_lookup, &tmp);
+    }
+    
+    return tmp;
+}
+
+
+bool extl_read_config(const char *file, const char *sp, bool warn_nx)
+{
+    TryCallParam param;
+    int retval;
+    
+    if(file==NULL)
+        return FALSE;
+    
+    param.status=0;
+    
+    retval=extl_try_config(file, sp, (ExtlTryConfigFn*)try_call, &param,
+                              EXTL_COMPILED_EXTENSION, EXTL_EXTENSION);
+    
+    if(retval==EXTL_TRYCONFIG_NOTFOUND && warn_nx)
+        extl_warn(TR("Unable to find '%s' on search path."), file);
+
+    return (retval==EXTL_TRYCONFIG_OK);
+}
+
+
+bool extl_read_savefile(const char *basename, ExtlTab *tabret)
+{
+    TryCallParam param;
+    int retval;
+    
+    param.status=0;
+    param.tab=extl_table_none();
+    
+    retval=extl_try_config(basename, NULL, (ExtlTryConfigFn*)try_read_savefile,
+                              &param, EXTL_EXTENSION, NULL);
+
+    *tabret=param.tab;
+    
+    return (retval==EXTL_TRYCONFIG_OK);
+}
+
+
+/*EXTL_DOC
+ * Read a savefile.
+ */
+EXTL_EXPORT_AS(extl, read_savefile)
+ExtlTab extl_extl_read_savefile(const char *basename)
+{
+    ExtlTab tab;
+    if(!extl_read_savefile(basename, &tab))
+        return extl_table_none();
+    return tab;
+}
+    
+
+/*}}}*/
+
+
+/*{{{ extl_get_savefile */
+
+
+static bool ensuredir(char *f)
+{
+    char *p;
+    int tryno=0;
+    bool ret=TRUE;
+    
+    if(access(f, F_OK)==0)
+        return TRUE;
+    
+    if(mkdir(f, 0700)==0)
+        return TRUE;
+    
+    p=strrchr(f, '/');
+    if(p==NULL){
+        extl_warn_err_obj(f);
+        return FALSE;
+    }
+    
+    *p='\0';
+    if(!ensuredir(f))
+        return FALSE;
+    *p='/';
+        
+    if(mkdir(f, 0700)==0)
+        return TRUE;
+    
+    extl_warn_err_obj(f);
+    return FALSE;
+}
+
+
+/*EXTL_DOC
+ * Get a file name to save (session) data in. The string \var{basename} 
+ * should contain no path or extension components.
+ */
+EXTL_EXPORT
+char *extl_get_savefile(const char *basename)
+{
+    char *res=NULL;
+    
+    if(sessiondir==NULL)
+        return NULL;
+    
+    if(!ensuredir(sessiondir)){
+        extl_warn(TR("Unable to create session directory \"%s\"."), 
+                  sessiondir);
+        return NULL;
+    }
+    
+    libtu_asprintf(&res, "%s/%s." EXTL_EXTENSION, sessiondir, basename);
+    
+    return res;
+}
+
+
+/*EXTL_DOC
+ * Write \var{tab} in file with basename \var{basename} in the
+ * session directory.
+ */
+EXTL_EXPORT
+bool extl_write_savefile(const char *basename, ExtlTab tab)
+{
+    bool ret=FALSE;
+    char *fname=extl_get_savefile(basename);
+    
+    if(fname!=NULL){
+        ret=extl_serialise(fname, tab);
+        free(fname);
+    }
+    
+    return ret;
+}
+
+
+/*}}}*/
+
diff --git a/libextl/readconfig.h b/libextl/readconfig.h
new file mode 100644 (file)
index 0000000..9bd2f30
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * libextl/readconfig.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#ifndef LIBEXTL_READCONFIG_H
+#define LIBEXTL_READCONFIG_H
+
+#include "extl.h"
+
+typedef int ExtlTryConfigFn(const char *file, void *param);
+
+enum{
+    EXTL_TRYCONFIG_MEMERROR=-3,
+    EXTL_TRYCONFIG_NOTFOUND=-2,
+    EXTL_TRYCONFIG_LOAD_FAILED=-1,
+    EXTL_TRYCONFIG_CALL_FAILED=0,
+    EXTL_TRYCONFIG_OK=1
+};
+
+
+extern bool extl_set_userdirs(const char *appname);
+extern bool extl_set_sessiondir(const char *session);
+extern bool extl_set_searchpath(const char *path);
+extern bool extl_add_searchdir(const char *dir);
+
+extern const char *extl_userdir();
+extern const char *extl_sessiondir();
+extern const char *extl_searchpath();
+
+extern int extl_try_config(const char *fname, const char *cfdir,
+                           ExtlTryConfigFn *tryfn, void *tryfnparam,
+                           const char *ext1, const char *ext2);
+
+extern char *extl_lookup_script(const char *file, const char *sp);
+
+extern bool extl_read_config(const char *file, const char *sp, 
+                             bool warn_nx);
+
+extern char *extl_get_savefile(const char *module);
+extern bool extl_read_savefile(const char *module, ExtlTab *tabret);
+extern ExtlTab extl_extl_read_savefile(const char *module);
+extern bool extl_write_savefile(const char *module, ExtlTab tab);
+
+#endif /* LIBEXTL_READCONFIG_H */
diff --git a/libextl/types.h b/libextl/types.h
new file mode 100644 (file)
index 0000000..f6236e0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * libextl/types.h
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2005.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#ifndef LIBEXTL_TYPES_H
+#define LIBEXTL_TYPES_H
+
+#if 1
+
+#include <libtu/types.h>
+#include <libtu/obj.h>
+
+#else
+
+#ifndef bool
+#define bool int
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/*typedef ? Obj;*/
+
+#endif
+
+#endif /* LIBEXTL_TYPES_H */
diff --git a/libmainloop/Makefile b/libmainloop/Makefile
new file mode 100644 (file)
index 0000000..6f887fa
--- /dev/null
@@ -0,0 +1,31 @@
+##
+## Libmainloop Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES = select.c defer.c signal.c hooks.c exec.c
+
+#MAKE_EXPORTS=mainloop
+
+TARGETS = libmainloop.a
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+libmainloop.a: $(OBJS)
+       $(AR) $(ARFLAGS) $@ $+
+       $(RANLIB) $@
+
+_install:
diff --git a/libmainloop/defer.c b/libmainloop/defer.c
new file mode 100644 (file)
index 0000000..c79e2ce
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * ion/libmainloop/defer.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+/* This file contains routines for deferred execution of potentially
+ * dangerous actions. They're called upon returning to the main
+ * loop.
+ */
+
+#include <libtu/obj.h>
+#include <libtu/objp.h>
+#include <libtu/types.h>
+#include <libtu/misc.h>
+#include <libtu/dlist.h>
+#include <libtu/output.h>
+#include <libtu/locale.h>
+#include "defer.h"
+
+
+DECLSTRUCT(WDeferred){
+    Watch watch;
+    WDeferredAction *action;
+    ExtlFn fn;
+    WDeferred *next, *prev;
+    WDeferred **list;
+};
+
+
+static WDeferred *deferred=NULL;
+
+
+#define N_DBUF 16
+
+/* To avoid allocating memory all the time, we use a small
+ * buffer that should be able to contain the small expected
+ * number of simultaneous deferred actions.
+ */
+static WDeferred dbuf[N_DBUF];
+static int dbuf_used=0;
+
+
+static WDeferred *alloc_defer()
+{
+    int i;
+    
+    /* Keeping it simple -- this naive loop should do it
+     * as N_DBUF is small.
+     */
+    for(i=0; i<N_DBUF; i++){
+        if(!dbuf_used&(1<<i)){
+            dbuf_used|=(1<<i);
+            return dbuf+i;
+        }
+    }
+    return ALLOC(WDeferred);
+}
+
+
+static void free_defer(WDeferred *d)
+{
+    if(d>=dbuf && d<dbuf+N_DBUF){
+        dbuf_used&=~1<<((d-dbuf)/sizeof(WDeferred));
+        return;
+    }
+    FREE(d);
+}
+
+
+static void defer_watch_handler(Watch *w, Obj *obj)
+{
+    WDeferred *d=(WDeferred*)w;
+    
+    UNLINK_ITEM(*(WDeferred**)(d->list), d, next, prev);
+    
+    free_defer(d);
+    
+    warn(TR("Object destroyed while deferred actions are still pending."));
+}
+
+    
+bool mainloop_defer_action_on_list(Obj *obj, WDeferredAction *action, 
+                                   WDeferred **list)
+{
+    WDeferred *d;
+    
+    d=alloc_defer();
+    
+    if(d==NULL){
+        warn_err();
+        return FALSE;
+    }
+    
+    d->action=action;
+    d->list=list;
+    d->fn=extl_fn_none();
+    
+    if(obj!=NULL)
+        watch_setup(&(d->watch), obj, defer_watch_handler);
+    
+    LINK_ITEM(*list, d, next, prev);
+    
+    return TRUE;
+}
+
+
+bool mainloop_defer_action(Obj *obj, WDeferredAction *action)
+{
+    return mainloop_defer_action_on_list(obj, action, &deferred);
+}
+
+
+bool mainloop_defer_destroy(Obj *obj)
+{
+    if(OBJ_IS_BEING_DESTROYED(obj))
+        return FALSE;
+    
+    return mainloop_defer_action(obj, destroy_obj);
+}
+    
+
+bool mainloop_defer_extl_on_list(ExtlFn fn, WDeferred **list)
+{
+    WDeferred *d;
+    
+    d=alloc_defer();
+    
+    if(d==NULL){
+        warn_err();
+        return FALSE;
+    }
+    
+    d->action=NULL;
+    d->list=list;
+    d->fn=extl_ref_fn(fn);
+    
+    watch_init(&(d->watch));
+    
+    LINK_ITEM(*list, d, next, prev);
+    
+    return TRUE;
+}
+
+
+/*EXTL_DOC
+ * Defer execution of \var{fn} until the main loop.
+ */
+EXTL_SAFE
+EXTL_EXPORT_AS(mainloop, defer)
+bool mainloop_defer_extl(ExtlFn fn)
+{
+    return mainloop_defer_extl_on_list(fn, &deferred);
+}
+
+
+static void do_execute(WDeferred *d)
+{
+    Obj *obj=d->watch.obj;
+    WDeferredAction *a=d->action;
+    ExtlFn fn=d->fn;
+    
+    watch_reset(&(d->watch));
+    free_defer(d);
+    
+    if(a!=NULL){
+        if(obj!=NULL)
+            a(obj);
+    }else if(fn!=extl_fn_none()){
+        extl_call(fn, NULL, NULL);
+        extl_unref_fn(fn);
+    }
+}
+
+
+void mainloop_execute_deferred_on_list(WDeferred **list)
+{
+    Obj *obj;
+    void (*action)(Obj*);
+    
+    while(*list!=NULL){
+        WDeferred *d=*list;
+        UNLINK_ITEM(*list, d, next, prev);
+        do_execute(d);
+    }
+}
+
+
+void mainloop_execute_deferred()
+{
+    mainloop_execute_deferred_on_list(&deferred);
+}
+
diff --git a/libmainloop/defer.h b/libmainloop/defer.h
new file mode 100644 (file)
index 0000000..3c6807e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * ion/libmainloop/defer.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_LIBMAINLOOP_DEFER_H
+#define ION_LIBMAINLOOP_DEFER_H
+
+#include <libtu/types.h>
+#include <libtu/obj.h>
+#include <libextl/extl.h>
+
+INTRSTRUCT(WDeferred);
+
+typedef void WDeferredAction(Obj*);
+
+extern void mainloop_execute_deferred();
+extern void mainloop_execute_deferred_on_list(WDeferred **list);
+
+extern bool mainloop_defer_action(Obj *obj, WDeferredAction *action);
+extern bool mainloop_defer_action_on_list(Obj *obj, WDeferredAction *action,
+                                          WDeferred **list);
+
+extern bool mainloop_defer_destroy(Obj *obj);
+
+extern bool mainloop_defer_extl(ExtlFn fn);
+extern bool mainloop_defer_extl_on_list(ExtlFn fn, WDeferred **list);
+
+#endif /* ION_LIBMAINLOOP_DEFER_H */
diff --git a/libmainloop/exec.c b/libmainloop/exec.c
new file mode 100644 (file)
index 0000000..3f0f264
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * ion/mainloop/exec.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <libtu/output.h>
+#include <libtu/misc.h>
+#include <libtu/locale.h>
+#include <libtu/types.h>
+
+#include "select.h"
+#include "exec.h"
+
+
+/*{{{ Exec/spawn/fork */
+
+#define SHELL_PATH "/bin/sh"
+#define SHELL_NAME "sh"
+#define SHELL_ARG "-c"
+
+
+void mainloop_do_exec(const char *cmd)
+{
+    char *argv[4];
+
+    argv[0]=SHELL_NAME;
+    argv[1]=SHELL_ARG;
+    argv[2]=(char*)cmd; /* stupid execve... */
+    argv[3]=NULL;
+    execvp(SHELL_PATH, argv);
+}
+
+
+static int mypipe(int *fds)
+{
+    int r=pipe(fds);
+    if(r==0){
+        cloexec_braindamage_fix(fds[0]);
+        cloexec_braindamage_fix(fds[1]);
+    }else{
+        warn_err_obj("pipe()");
+    }
+    return r;
+}
+
+
+static bool unblock(int fd)
+{
+    int fl=fcntl(fd, F_GETFL);
+    if(fl!=-1)
+        fl=fcntl(fd, F_SETFL, fl|O_NONBLOCK);
+    return (fd!=-1);
+}
+
+
+static void duppipe(int fd, int idx, int *fds)
+{
+    close(fd);
+    dup(fds[idx]);
+    close(fds[0]);
+    close(fds[1]);
+}
+
+
+pid_t mainloop_fork(void (*fn)(void *p), void *fnp,
+                    int *infd, int *outfd, int *errfd)
+{
+    int pid;
+    int infds[2];
+    int outfds[2];
+    int errfds[2];
+    
+    if(infd!=NULL){
+        if(mypipe(infds)!=0)
+            return -1;
+    }
+
+    if(outfd!=NULL){
+        if(mypipe(outfds)!=0)
+            goto err1;
+    }
+
+    if(errfd!=NULL){
+        if(mypipe(errfds)!=0)
+            goto err2;
+    }
+    
+
+    pid=fork();
+    
+    if(pid<0)
+        goto err3;
+    
+    if(pid!=0){
+        if(outfd!=NULL){
+            if(!unblock(outfds[0]))
+                goto err3;
+            *outfd=outfds[0];
+            close(outfds[1]);
+        }
+
+        if(errfd!=NULL){
+            if(!unblock(errfds[0]))
+                goto err3;
+            *errfd=errfds[0];
+            close(errfds[1]);
+        }
+        
+        if(infd!=NULL){
+            *infd=infds[1];
+            close(infds[0]);
+        }
+        
+        return pid;
+    }
+
+    if(infd!=NULL)
+        duppipe(0, 0, infds);
+    if(outfd!=NULL)
+        duppipe(1, 1, outfds);
+    if(errfd!=NULL)
+        duppipe(2, 1, errfds);
+    
+    fn(fnp);
+
+    abort();
+
+err3:
+    warn_err();
+    if(errfd!=NULL){
+        close(errfds[0]);
+        close(errfds[1]);
+    }
+err2:
+    if(outfd!=NULL){
+        close(outfds[0]);
+        close(outfds[1]);
+    }
+err1:    
+    if(infd!=NULL){
+        close(infds[0]);
+        close(infds[1]);
+    }
+    return -1;
+}
+
+
+typedef struct{
+    const char *cmd;
+    void (*initenv)(void *p);
+    void *initenvp;
+} SpawnP;
+
+
+static void do_spawn(void *spawnp)
+{
+    SpawnP *p=(SpawnP*)spawnp;
+    
+    if(p->initenv)
+        p->initenv(p->initenvp);
+    mainloop_do_exec(p->cmd);
+}
+
+
+pid_t mainloop_do_spawn(const char *cmd, 
+                        void (*initenv)(void *p), void *p,
+                        int *infd, int *outfd, int *errfd)
+{
+    SpawnP spawnp;
+    
+    spawnp.cmd=cmd;
+    spawnp.initenv=initenv;
+    spawnp.initenvp=p;
+    
+    return mainloop_fork(do_spawn, (void*)&spawnp, infd, outfd, errfd);
+}
+
+
+pid_t mainloop_spawn(const char *cmd)
+{
+    return mainloop_do_spawn(cmd, NULL, NULL, NULL, NULL, NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ popen_bgread */
+
+
+#define BL 1024
+
+bool mainloop_process_pipe_extlfn(int fd, ExtlFn fn)
+{
+    char buf[BL];
+    int n;
+    
+    n=read(fd, buf, BL-1);
+    if(n<0){
+        if(errno==EAGAIN || errno==EINTR)
+            return TRUE;
+        n=0;
+        warn_err_obj(TR("reading a pipe"));
+        return FALSE;
+    }else if(n>0){
+        buf[n]='\0';
+        extl_call(fn, "s", NULL, &buf);
+        return TRUE;
+    }else/* if(n==0)*/{
+        /* Call with no argument/NULL string to signify EOF */
+        extl_call(fn, NULL, NULL);
+        return FALSE;
+    }
+}
+
+
+static void process_pipe(int fd, void *p)
+{
+    if(!mainloop_process_pipe_extlfn(fd, *(ExtlFn*)p)){
+        /* We get here on EOL or if the handler failed */
+        mainloop_unregister_input_fd(fd);
+        close(fd);
+        extl_unref_fn(*(ExtlFn*)p);
+        free(p);
+    }
+}
+
+
+bool mainloop_register_input_fd_extlfn(int fd, ExtlFn fn)
+{
+    ExtlFn *p=ALLOC(ExtlFn);
+    if(p!=NULL){
+        *(ExtlFn*)p=extl_ref_fn(fn);
+        if(mainloop_register_input_fd(fd, p, process_pipe))
+            return TRUE;
+        extl_unref_fn(*(ExtlFn*)p);
+        free(p);
+    }
+    return FALSE;
+}
+
+
+pid_t mainloop_popen_bgread(const char *cmd, 
+                            void (*initenv)(void *p), void *p,
+                            ExtlFn handler, ExtlFn errhandler)
+{
+    pid_t pid=-1;
+    int fd=-1, errfd=-1;
+    ExtlFn none=extl_fn_none();
+    
+    pid=mainloop_do_spawn(cmd, initenv, p, NULL, 
+                          (handler!=none ? &fd : NULL),
+                          (errhandler!=none ? &errfd : NULL));
+    
+    if(pid>0){
+        if(handler!=none){
+            if(!mainloop_register_input_fd_extlfn(fd, handler))
+                goto err;
+        }
+        if(errhandler!=extl_fn_none()){
+            if(!mainloop_register_input_fd_extlfn(errfd, errhandler))
+                goto err;
+        }
+    }
+    
+    return pid;
+
+err:
+    if(fd>=0)
+        close(fd);
+    if(errfd>=0)
+        close(errfd);
+    return -1;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+void cloexec_braindamage_fix(int fd)
+{
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+
+/*}}}*/
+
diff --git a/libmainloop/exec.h b/libmainloop/exec.h
new file mode 100644 (file)
index 0000000..c9278b3
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ion/libmainloop/exec.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_LIBMAINLOOP_EXEC_H
+#define ION_LIBMAINLOOP_EXEC_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libextl/extl.h>
+
+extern void mainloop_do_exec(const char *cmd);
+extern pid_t mainloop_fork(void (*fn)(void *p), void *p,
+                           int *infd, int *outfd, int *errfd);
+extern pid_t mainloop_do_spawn(const char *cmd, 
+                              void (*initenv)(void *p), void *p,
+                              int *infd, int *outfd, int *errfd);
+extern pid_t mainloop_spawn(const char *cmd);
+
+extern pid_t mainloop_popen_bgread(const char *cmd, 
+                                   void (*initenv)(void *p), void *p,
+                                   ExtlFn handler, ExtlFn errhandler);
+
+extern bool mainloop_register_input_fd_extlfn(int fd, ExtlFn fn);
+extern bool mainloop_process_pipe_extlfn(int fd, ExtlFn fn);
+
+extern void cloexec_braindamage_fix(int fd);
+
+#endif /* ION_LIBMAINLOOP_EXEC_H */
diff --git a/libmainloop/hooks.c b/libmainloop/hooks.c
new file mode 100644 (file)
index 0000000..1a15bc9
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * ion/mainloop/hooks.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/types.h>
+#include <libtu/misc.h>
+#include <libtu/dlist.h>
+#include <libtu/output.h>
+#include <libtu/rb.h>
+#include <libtu/objp.h>
+#include <libtu/locale.h>
+#include <libextl/extl.h>
+#include "hooks.h"
+
+
+EXTL_EXPORT
+IMPLCLASS(WHook, Obj, hook_deinit, NULL);
+
+static Rb_node named_hooks=NULL;
+
+
+/*{{{ Named hooks */
+
+
+/* If hk==NULL to register, new is attempted to be created. */
+WHook *mainloop_register_hook(const char *name, WHook *hk)
+{
+    bool created=FALSE;
+    char *nnm;
+    
+    if(hk==NULL)
+        return NULL;
+    
+    if(named_hooks==NULL){
+        named_hooks=make_rb();
+        if(named_hooks==NULL)
+            return NULL;
+    }
+    
+    nnm=scopy(name);
+    
+    if(nnm==NULL)
+        return NULL;
+    
+    if(!rb_insert(named_hooks, nnm, hk)){
+        free(nnm);
+        destroy_obj((Obj*)hk);
+    }
+    
+    return hk;
+}
+
+
+WHook *mainloop_unregister_hook(const char *name, WHook *hk)
+{
+    bool found=FALSE;
+    Rb_node node;
+
+    if(named_hooks==NULL)
+        return NULL;
+    
+    if(hk==NULL){
+        assert(name!=NULL);
+        node=rb_find_key_n(named_hooks, name, &found);
+    }else{
+        rb_traverse(node, named_hooks){
+            if((WHook*)rb_val(node)==hk){
+                found=TRUE;
+                break;
+            }
+        }
+    }
+
+    if(found){
+        hk=(WHook*)rb_val(node);
+        free((char*)node->k.key);
+        rb_delete_node(node);
+    }
+    
+    return hk;
+}
+
+
+/*EXTL_DOC
+ * Find named hook \var{name}.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+WHook *mainloop_get_hook(const char *name)
+{
+    if(named_hooks!=NULL){
+        bool found=FALSE;
+        Rb_node node=rb_find_key_n(named_hooks, name, &found);
+        if(found)
+            return (WHook*)rb_val(node);
+    }
+    
+    return NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init/deinit */
+
+
+static void destroy_item(WHook *hk, WHookItem *item)
+{
+    if(item->fn==NULL)
+        extl_unref_fn(item->efn);
+    UNLINK_ITEM(hk->items, item, next, prev);
+    free(item);
+}
+
+
+static WHookItem *create_item(WHook *hk)
+{
+    WHookItem *item=ALLOC(WHookItem);
+    if(item!=NULL){
+        LINK_ITEM_FIRST(hk->items, item, next, prev);
+        item->fn=NULL;
+        item->efn=extl_fn_none();
+    }
+    
+    return item;
+}
+
+
+bool hook_init(WHook *hk)
+{
+    hk->items=NULL;
+    return TRUE;
+}
+
+
+WHook *create_hook()
+{
+    CREATEOBJ_IMPL(WHook, hook, (p));
+}
+
+
+void hook_deinit(WHook *hk)
+{
+    mainloop_unregister_hook(NULL, hk);
+    while(hk->items!=NULL)
+        destroy_item(hk, hk->items);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Find/add/remove */
+
+
+WHookItem *hook_find(WHook *hk, WHookDummy *fn)
+{
+    WHookItem *hi;
+    
+    for(hi=hk->items; hi!=NULL; hi=hi->next){
+        if(hi->fn==fn)
+            return hi;
+    }
+    
+    return NULL;
+}
+
+
+WHookItem *hook_find_extl(WHook *hk, ExtlFn efn)
+{
+    WHookItem *hi;
+    
+    for(hi=hk->items; hi!=NULL; hi=hi->next){
+        if(extl_fn_eq(hi->efn, efn))
+            return hi;
+    }
+    
+    return NULL;
+}
+
+
+/*EXTL_DOC
+ * Is \var{fn} hooked to hook \var{hk}?
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+bool hook_listed(WHook *hk, ExtlFn efn)
+{
+    return hook_find_extl(hk, efn)!=NULL;
+}
+
+
+bool hook_add(WHook *hk, WHookDummy *fn)
+{
+    WHookItem *item;
+    
+    if(hook_find(hk, fn))
+        return FALSE;
+    
+    item=create_item(hk);
+    if(item==NULL)
+        return FALSE;
+    item->fn=fn;
+    return TRUE;
+}
+
+
+/*EXTL_DOC
+ * Add \var{efn} to the list of functions to be called when the
+ * hook \var{hk} is triggered.
+ */
+EXTL_EXPORT_AS(WHook, add)
+bool hook_add_extl(WHook *hk, ExtlFn efn)
+{
+    WHookItem *item;
+    
+    if(efn==extl_fn_none()){
+        warn(TR("No function given."));
+        return FALSE;
+    }
+
+    if(hook_find_extl(hk, efn))
+        return FALSE;
+    
+    item=create_item(hk);
+    
+    if(item==NULL)
+        return FALSE;
+    
+    item->efn=extl_ref_fn(efn);
+    
+    return TRUE;
+}
+
+
+bool hook_remove(WHook *hk, WHookDummy *fn)
+{
+    WHookItem *item=hook_find(hk, fn);
+    if(item!=NULL)
+        destroy_item(hk, item);
+    return (item!=NULL);
+}
+
+
+/*EXTL_DOC
+ * Remove \var{efn} from the list of functions to be called when the 
+ * hook \var{hk} is triggered.
+ */
+EXTL_EXPORT_AS(WHook, remove)
+bool hook_remove_extl(WHook *hk, ExtlFn efn)
+{
+    WHookItem *item=hook_find_extl(hk, efn);
+    if(item!=NULL)
+        destroy_item(hk, item);
+    return (item!=NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Basic marshallers */
+
+
+static bool marshall_v(WHookDummy *fn, void *param)
+{
+    fn();
+    return TRUE;
+}
+    
+
+static bool marshall_extl_v(ExtlFn fn, void *param)
+{
+    extl_call(fn, NULL, NULL);
+    return TRUE;
+}
+
+
+static bool marshall_o(WHookDummy *fn, void *param)
+{
+    fn((Obj*)param);
+    return TRUE;
+}
+    
+
+static bool marshall_extl_o(ExtlFn fn, void *param)
+{
+    return extl_call(fn, "o", NULL, (Obj*)param);
+}
+
+
+static bool marshall_p(WHookDummy *fn, void *param)
+{
+    fn(param);
+    return TRUE;
+}
+
+
+static bool marshall_alt_v(bool (*fn)(), void *param)
+{
+    return fn();
+}
+    
+
+static bool marshall_extl_alt_v(ExtlFn fn, void *param)
+{
+    bool ret=FALSE;
+    extl_call(fn, NULL, "b", &ret);
+    return ret;
+}
+
+
+static bool marshall_alt_o(bool (*fn)(), void *param)
+{
+    return fn((Obj*)param);
+}
+    
+
+static bool marshall_extl_alt_o(ExtlFn fn, void *param)
+{
+    bool ret=FALSE;
+    extl_call(fn, "o", "b", (Obj*)param, &ret);
+    return ret;
+}
+
+
+static bool marshall_alt_p(bool (*fn)(), void *param)
+{
+    return fn(param);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Call */
+
+
+void hook_call(const WHook *hk, void *p,
+               WHookMarshall *m, WHookMarshallExtl *em)
+{
+    WHookItem *hi, *next;
+    
+    for(hi=hk->items; hi!=NULL; hi=next){
+        next=hi->next;
+        if(hi->fn!=NULL)
+            m(hi->fn, p);
+        else if(em!=NULL)
+            em(hi->efn, p);
+    }
+}
+
+
+bool hook_call_alt(const WHook *hk, void *p,
+                   WHookMarshall *m, WHookMarshallExtl *em)
+{
+    WHookItem *hi, *next;
+    bool ret=FALSE;
+    
+    for(hi=hk->items; hi!=NULL; hi=next){
+        next=hi->next;
+        if(hi->fn!=NULL)
+            ret=m(hi->fn, p);
+        else if(em!=NULL)
+            ret=em(hi->efn, p);
+        if(ret)
+            break;
+    }
+    
+    return ret;
+}
+
+
+void hook_call_v(const WHook *hk)
+{
+    hook_call(hk, NULL, marshall_v, marshall_extl_v);
+}
+
+
+void hook_call_o(const WHook *hk, Obj *o)
+{
+    hook_call(hk, o, marshall_o, marshall_extl_o);
+}
+
+
+void hook_call_p(const WHook *hk, void *p, WHookMarshallExtl *em)
+{
+    hook_call(hk, p, marshall_p, em);
+}
+
+
+bool hook_call_alt_v(const WHook *hk)
+{
+    return hook_call_alt(hk, NULL, (WHookMarshall*)marshall_alt_v, 
+                         (WHookMarshallExtl*)marshall_extl_alt_v);
+}
+
+
+bool hook_call_alt_o(const WHook *hk, Obj *o)
+{
+    return hook_call_alt(hk, o, (WHookMarshall*)marshall_alt_o, 
+                         (WHookMarshallExtl*)marshall_extl_alt_o);
+}
+
+
+bool hook_call_alt_p(const WHook *hk, void *p, WHookMarshallExtl *em)
+{
+    return hook_call_alt(hk, p, (WHookMarshall*)marshall_alt_p, em);
+}
+
+
+/*}}}*/
+
+
+
diff --git a/libmainloop/hooks.h b/libmainloop/hooks.h
new file mode 100644 (file)
index 0000000..63d3554
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * ion/mainloop/hooks.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_LIBMAINLOOP_HOOKS_H
+#define ION_LIBMAINLOOP_HOOKS_H
+
+#include <libtu/types.h>
+#include <libextl/extl.h>
+
+INTRSTRUCT(WHookItem);
+INTRCLASS(WHook);
+
+typedef void WHookDummy();
+typedef bool WHookMarshall(WHookDummy *fn, void *param);
+typedef bool WHookMarshallExtl(ExtlFn fn, void *param);
+
+DECLSTRUCT(WHookItem){
+    WHookDummy *fn;
+    ExtlFn efn;
+    WHookItem *next, *prev;
+};
+
+DECLCLASS(WHook){
+    Obj obj;
+    WHookItem *items;
+};
+
+
+/* If hk==NULL to register, new is attempted to be created. */
+extern WHook *mainloop_register_hook(const char *name, WHook *hk);
+extern WHook *mainloop_unregister_hook(const char *name, WHook *hk);
+extern WHook *mainloop_get_hook(const char *name);
+
+extern WHook *create_hook();
+extern bool hook_init(WHook *hk);
+extern void hook_deinit(WHook *hk);
+
+extern bool hook_add(WHook *hk, WHookDummy *fn);
+extern bool hook_remove(WHook *hk, WHookDummy *fn);
+extern WHookItem *hook_find(WHook *hk, WHookDummy *fn);
+
+extern bool hook_add_extl(WHook *hk, ExtlFn fn);
+extern bool hook_remove_extl(WHook *hk, ExtlFn fn);
+extern WHookItem *hook_find_extl(WHook *hk, ExtlFn efn);
+
+extern void hook_call(const WHook *hk, void *p,
+                      WHookMarshall *m, WHookMarshallExtl *em);
+extern void hook_call_v(const WHook *hk);
+extern void hook_call_o(const WHook *hk, Obj *o);
+extern void hook_call_p(const WHook *hk, void *p, WHookMarshallExtl *em);
+
+extern bool hook_call_alt(const WHook *hk, void *p,
+                          WHookMarshall *m, WHookMarshallExtl *em);
+extern bool hook_call_alt_v(const WHook *hk);
+extern bool hook_call_alt_o(const WHook *hk, Obj *o);
+extern bool hook_call_alt_p(const WHook *hk, void *p, WHookMarshallExtl *em);
+
+
+#endif /* ION_LIBMAINLOOP_HOOKS_H */
diff --git a/libmainloop/rx.mk b/libmainloop/rx.mk
new file mode 100644 (file)
index 0000000..ba54cc9
--- /dev/null
@@ -0,0 +1,11 @@
+##
+## Rules for re-exporting libmainloop exports.
+##
+
+MAINLOOP_DIR = $(TOPDIR)/libmainloop
+
+MAINLOOP_SOURCES_ = select.c defer.c signal.c hooks.c exec.c
+
+MAINLOOP_SOURCES = $(patsubst %,$(MAINLOOP_DIR)/%, $(MAINLOOP_SOURCES_))
+
+MKEXPORTS_EXTRAS += -reexport mainloop $(MAINLOOP_SOURCES)
diff --git a/libmainloop/select.c b/libmainloop/select.c
new file mode 100644 (file)
index 0000000..2876222
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * ion/libmainloop/mainloop.c
+ * 
+ * Partly based on a contributed code.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/types.h>
+#include <libtu/misc.h>
+#include <libtu/dlist.h>
+
+#include "select.h"
+
+
+/*{{{ File descriptor management */
+
+
+static WInputFd *input_fds=NULL;
+
+static WInputFd *find_input_fd(int fd)
+{
+    WInputFd *tmp=input_fds;
+    
+    while(tmp){
+        if(tmp->fd==fd)
+            break;
+        tmp=tmp->next;
+    }
+    return tmp;
+}
+
+bool mainloop_register_input_fd(int fd, void *data,
+                                void (*callback)(int fd, void *d))
+{
+    WInputFd *tmp;
+    
+    if(find_input_fd(fd)!=NULL)
+        return FALSE;
+    
+    tmp=ALLOC(WInputFd);
+    if(tmp==NULL)
+        return FALSE;
+    
+    tmp->fd=fd;
+    tmp->data=data;
+    tmp->process_input_fn=callback;
+    
+    LINK_ITEM(input_fds, tmp, next, prev);
+    
+    return TRUE;
+}
+
+void mainloop_unregister_input_fd(int fd)
+{
+    WInputFd *tmp=find_input_fd(fd);
+    
+    if(tmp!=NULL){
+        UNLINK_ITEM(input_fds, tmp, next, prev);
+        free(tmp);
+    }
+}
+
+static void set_input_fds(fd_set *rfds, int *nfds)
+{
+    WInputFd *tmp=input_fds;
+    
+    while(tmp){
+        FD_SET(tmp->fd, rfds);
+        if(tmp->fd>*nfds)
+            *nfds=tmp->fd;
+        tmp=tmp->next;
+    }
+}
+
+static void check_input_fds(fd_set *rfds)
+{
+    WInputFd *tmp=input_fds, *next=NULL;
+    
+    while(tmp){
+        next=tmp->next;
+        if(FD_ISSET(tmp->fd, rfds))
+            tmp->process_input_fn(tmp->fd, tmp->data);
+        tmp=next;
+    }
+}
+
+/*}}}*/
+
+
+/*{{{ Select */
+
+void mainloop_select()
+{
+    fd_set rfds;
+    int nfds=0;
+            
+    FD_ZERO(&rfds);
+    
+    set_input_fds(&rfds, &nfds);
+    
+    if(select(nfds+1, &rfds, NULL, NULL, NULL)>0)
+        check_input_fds(&rfds);
+}
+
+
+/*}}}*/
diff --git a/libmainloop/select.h b/libmainloop/select.h
new file mode 100644 (file)
index 0000000..465f134
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * ion/mainloop/select.h
+ * 
+ * Based on a contributed readfds code.
+ * 
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_LIBMAINLOOP_SELECT_H
+#define ION_LIBMAINLOOP_SELECT_H
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <libtu/obj.h>
+#include <libtu/types.h>
+
+INTRSTRUCT(WInputFd);
+
+DECLSTRUCT(WInputFd){
+    int fd;
+    void *data;
+    void (*process_input_fn)(int fd, void *data);
+    WInputFd *next, *prev;
+};
+
+extern bool mainloop_register_input_fd(int fd, void *data,
+                                       void (*callback)(int fd, void *data));
+extern void mainloop_unregister_input_fd(int fd);
+
+extern void mainloop_select();
+
+#endif /* ION_LIBMAINLOOP_SELECT_H */
diff --git a/libmainloop/signal.c b/libmainloop/signal.c
new file mode 100644 (file)
index 0000000..ddf6aba
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * ion/libmainloop/signal.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <libtu/objp.h>
+#include <libtu/types.h>
+#include <libtu/misc.h>
+#include <libtu/locale.h>
+#include <libtu/output.h>
+
+#include "signal.h"
+#include "hooks.h"
+
+static int kill_sig=0;
+#if 1
+static int wait_sig=0;
+#endif
+
+static int usr2_sig=0;
+static bool had_tmr=FALSE;
+
+WHook *mainloop_sigchld_hook=NULL;
+WHook *mainloop_sigusr2_hook=NULL;
+
+
+/*{{{ Timers */
+
+
+static WTimer *queue=NULL;
+
+
+#define TIMEVAL_LATER(a, b) \
+    ((a.tv_sec > b.tv_sec) || \
+    ((a.tv_sec == b.tv_sec) && \
+     (a.tv_usec > b.tv_usec)))
+
+#define USECS_IN_SEC 1000000
+
+
+static void do_timer_set()
+{
+    struct itimerval val={{0, 0}, {0, 0}};
+    
+    if(queue==NULL){
+        setitimer(ITIMER_REAL, &val, NULL);
+        return;
+    }
+
+    /* Subtract queue time from current time, don't go below zero */
+    gettimeofday(&(val.it_value), NULL);
+    if(TIMEVAL_LATER((queue)->when, val.it_value)){
+        if(queue->when.tv_usec<val.it_value.tv_usec){
+            queue->when.tv_usec+=USECS_IN_SEC;
+            queue->when.tv_sec--;
+        }
+        val.it_value.tv_usec=queue->when.tv_usec-val.it_value.tv_usec;
+        val.it_value.tv_sec=queue->when.tv_sec-val.it_value.tv_sec;
+        if(val.it_value.tv_usec<0)
+            val.it_value.tv_usec=0;
+        /* POSIX and some kernels have been designed by absolute morons and 
+         * contain idiotic artificial restrictions on the value of tv_usec, 
+         * that will only cause more code being run and clock cycles being 
+         * spent to do the same thing, as the kernel will in any case convert
+         * the seconds to some other units.
+         */
+         val.it_value.tv_sec+=val.it_value.tv_usec/USECS_IN_SEC;
+         val.it_value.tv_usec%=USECS_IN_SEC;
+    }else{
+        had_tmr=TRUE;
+        return;
+    }
+
+    val.it_interval.tv_usec=val.it_value.tv_usec;
+    val.it_interval.tv_sec=val.it_value.tv_sec;
+    
+    if((setitimer(ITIMER_REAL, &val, NULL))){
+        had_tmr=TRUE;
+    }
+}
+
+
+typedef struct{
+    pid_t pid;
+    int code;
+} ChldParams;
+
+
+static bool mrsh_chld(void (*fn)(pid_t, int), ChldParams *p)
+{
+    fn(p->pid, p->code);
+    return TRUE;
+}
+
+
+static bool mrsh_chld_extl(ExtlFn fn, ChldParams *p)
+{
+    ExtlTab t=extl_create_table();
+    bool ret;
+
+    extl_table_sets_i(t, "pid", (int)p->pid);
+    
+    if(WIFEXITED(p->code)){
+        extl_table_sets_b(t, "exited", TRUE);
+        extl_table_sets_i(t, "exitstatus", WEXITSTATUS(p->code));
+    }
+    if(WIFSIGNALED(p->code)){
+        extl_table_sets_b(t, "signaled", TRUE);
+        extl_table_sets_i(t, "termsig", WTERMSIG(p->code));
+#ifdef WCOREDUMP 
+        extl_table_sets_i(t, "coredump", WCOREDUMP(p->code));
+#endif
+    }
+    if(WIFSTOPPED(p->code)){
+        extl_table_sets_b(t, "stopped", TRUE);
+        extl_table_sets_i(t, "stopsig", WSTOPSIG(p->code));
+    }
+    /*if(WIFCONTINUED(p->code)){
+        extl_table_sets_b(t, "continued", TRUE);
+    }*/
+    
+    ret=extl_call(fn, "t", NULL, t);
+    
+    extl_unref_table(t);
+    
+    return ret;
+}
+
+static bool mrsh_usr2(void (*fn)(void), void *p)
+{
+    fn();
+    return TRUE;
+}
+
+static bool mrsh_usr2_extl(ExtlFn fn, void *p)
+{
+    bool ret;
+    ExtlTab t=extl_create_table();
+    ret=extl_call(fn, "t", NULL, t);
+    extl_unref_table(t);
+    return ret;
+}
+
+
+bool mainloop_check_signals()
+{
+    struct timeval current_time;
+    WTimer *q;
+    int ret=0;
+
+    if(usr2_sig!=0){
+        usr2_sig=0;
+        if(mainloop_sigusr2_hook!=NULL){
+            hook_call(mainloop_sigusr2_hook, NULL,
+                      (WHookMarshall*)mrsh_usr2,
+                      (WHookMarshallExtl*)mrsh_usr2_extl);
+        }
+    }
+
+#if 1    
+    if(wait_sig!=0){
+        ChldParams p;
+        wait_sig=0;
+        while((p.pid=waitpid(-1, &p.code, WNOHANG|WUNTRACED))>0){
+            if(mainloop_sigchld_hook!=NULL &&
+               (WIFEXITED(p.code) || WIFSIGNALED(p.code))){
+                hook_call(mainloop_sigchld_hook, &p, 
+                          (WHookMarshall*)mrsh_chld,
+                          (WHookMarshallExtl*)mrsh_chld_extl);
+            }
+        }
+    }
+#endif
+    
+    if(kill_sig!=0)
+        return kill_sig;
+
+    /* Check for timer events in the queue */
+    while(had_tmr && queue!=NULL){
+        had_tmr=FALSE;
+        gettimeofday(&current_time, NULL);
+        while(queue!=NULL){
+            if(TIMEVAL_LATER(current_time, queue->when)){
+                q=queue;
+                queue=q->next;
+                q->next=NULL;
+                if(q->handler!=NULL){
+                    WTimerHandler *handler=q->handler;
+                    Obj *obj=q->objwatch.obj;
+                    q->handler=NULL;
+                    watch_reset(&(q->objwatch));
+                    handler(q, obj);
+                }else if(q->extl_handler!=extl_fn_none()){
+                    ExtlFn fn=q->extl_handler;
+                    Obj *obj=q->objwatch.obj;
+                    watch_reset(&(q->objwatch));
+                    q->extl_handler=extl_fn_none();
+                    extl_call(fn, "o", NULL, obj);
+                    extl_unref_fn(fn);
+                }
+            }else{
+                break;
+            }
+        }
+        do_timer_set();
+    }
+    
+    return ret;
+}
+
+
+static void add_to_current_time(struct timeval *when, uint msecs)
+{
+    long tmp_usec;
+
+    gettimeofday(when, NULL);
+    tmp_usec=when->tv_usec + (msecs * 1000);
+    when->tv_usec=tmp_usec % 1000000;
+    when->tv_sec+=tmp_usec / 1000000;
+}
+
+
+/*EXTL_DOC
+ * Is timer set?
+ */
+EXTL_EXPORT_MEMBER
+bool timer_is_set(WTimer *timer)
+{
+    WTimer *tmr;
+    for(tmr=queue; tmr!=NULL; tmr=tmr->next){
+        if(tmr==timer)
+            return TRUE;
+    }
+    return FALSE;
+}
+
+
+void timer_do_set(WTimer *timer, uint msecs, WTimerHandler *handler,
+                  Obj *obj, ExtlFn fn)
+{
+    WTimer *q, **qptr;
+    bool set=FALSE;
+    
+    timer_reset(timer);
+
+    /* Initialize the new queue timer event */
+    add_to_current_time(&(timer->when), msecs);
+    timer->next=NULL;
+    timer->handler=handler;
+    timer->extl_handler=fn;
+    if(obj!=NULL)
+        watch_setup(&(timer->objwatch), obj, NULL);
+    else
+        watch_reset(&(timer->objwatch));
+
+    /* Add timerevent in place to queue */
+    q=queue;
+    qptr=&queue;
+    
+    while(q!=NULL){
+        if(TIMEVAL_LATER(q->when, timer->when))
+            break;
+        qptr=&(q->next);
+        q=q->next;
+    }
+    
+    timer->next=q;
+    *qptr=timer;
+
+    do_timer_set();
+}
+
+
+void timer_set(WTimer *timer, uint msecs, WTimerHandler *handler,
+               Obj *obj)
+{
+    timer_do_set(timer, msecs, handler, obj, extl_fn_none());
+}
+
+
+/*EXTL_DOC
+ * Set \var{timer} to call \var{fn} in \var{msecs} milliseconds.
+ */
+EXTL_EXPORT_AS(WTimer, set)
+void timer_set_extl(WTimer *timer, uint msecs, ExtlFn fn)
+{
+    timer_do_set(timer, msecs, NULL, NULL, extl_ref_fn(fn));
+}
+
+    
+/*EXTL_DOC
+ * Reset timer.
+ */
+EXTL_EXPORT_MEMBER
+void timer_reset(WTimer *timer)
+{
+    WTimer *q=queue, **qptr=&queue;
+    
+    while(q!=NULL){
+        if(q==timer){
+            *qptr=timer->next;
+            do_timer_set();
+            break;
+        }
+        qptr=&(q->next);
+        q=q->next;
+        
+    }
+    
+    timer->handler=NULL;
+    extl_unref_fn(timer->extl_handler);
+    timer->extl_handler=extl_fn_none();
+    watch_reset(&(timer->objwatch));
+}
+
+
+bool timer_init(WTimer *timer)
+{
+    timer->when.tv_sec=0;
+    timer->when.tv_usec=0;
+    timer->next=NULL;
+    timer->handler=NULL;
+    timer->extl_handler=extl_fn_none();
+    watch_init(&(timer->objwatch));
+    return TRUE;
+}
+
+void timer_deinit(WTimer *timer)
+{
+    timer_reset(timer);
+}
+
+
+WTimer *create_timer()
+{
+    CREATEOBJ_IMPL(WTimer, timer, (p));
+}
+
+/*EXTL_DOC
+ * Create a new timer.
+ */
+EXTL_EXPORT_AS(mainloop, create_timer)
+WTimer *create_timer_extl_owned()
+{
+    WTimer *timer=create_timer();
+    if(timer!=NULL)
+        ((Obj*)timer)->flags|=OBJ_EXTL_OWNED;
+    return timer;
+}
+
+
+EXTL_EXPORT
+IMPLCLASS(WTimer, Obj, timer_deinit, NULL);
+
+
+/*}}}*/
+
+
+/*{{{ Signal handling */
+
+
+static void fatal_signal_handler(int signal_num)
+{
+    set_warn_handler(NULL);
+    warn(TR("Caught fatal signal %d. Dying without deinit."), signal_num); 
+    signal(signal_num, SIG_DFL);
+    kill(getpid(), signal_num);
+}
+
+           
+static void deadly_signal_handler(int signal_num)
+{
+    set_warn_handler(NULL);
+    warn(TR("Caught signal %d. Dying."), signal_num);
+    signal(signal_num, SIG_DFL);
+    /*if(ioncore_g.opmode==IONCORE_OPMODE_INIT)
+        kill(getpid(), signal_num);
+    else*/
+        kill_sig=signal_num;
+}
+
+
+static void chld_handler(int signal_num)
+{
+#if 0
+    pid_t pid;
+
+    while((pid=waitpid(-1, NULL, WNOHANG|WUNTRACED))>0){
+        /* nothing */
+    }
+#else
+    wait_sig=1;
+#endif
+}
+
+static void usr2_handler(int signal_num)
+{
+    usr2_sig=1;
+}
+
+
+static void exit_handler(int signal_num)
+{
+    if(kill_sig>0){
+        warn(TR("Got signal %d while %d is still to be handled."),
+             signal_num, kill_sig);
+    }
+    kill_sig=signal_num;
+}
+
+
+static void timer_handler(int signal_num)
+{
+    had_tmr=TRUE;
+}
+
+
+static void ignore_handler(int signal_num)
+{
+    
+}
+
+
+#ifndef SA_RESTART
+ /* glibc is broken (?) and does not define SA_RESTART with
+  * '-ansi -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED', so just try to live
+  * without it.
+  */
+#define SA_RESTART 0
+#endif
+
+#define IFTRAP(X) if(sigismember(which, X))
+#define DEADLY(X) IFTRAP(X) signal(X, deadly_signal_handler);
+#define FATAL(X) IFTRAP(X) signal(X, fatal_signal_handler);
+#define IGNORE(X) IFTRAP(X) signal(X, SIG_IGN)
+
+void mainloop_trap_signals(const sigset_t *which)
+{
+    struct sigaction sa;
+    sigset_t set, oldset;
+    sigset_t dummy;
+
+    if(which==NULL){
+        sigfillset(&dummy);
+        which=&dummy;
+    }
+    
+    sigemptyset(&set);
+    sigemptyset(&oldset);
+    sigprocmask(SIG_SETMASK, &set, &oldset);
+    
+    DEADLY(SIGHUP);
+    DEADLY(SIGQUIT);
+    DEADLY(SIGINT);
+    DEADLY(SIGABRT);
+
+    FATAL(SIGILL);
+    FATAL(SIGSEGV);
+    FATAL(SIGFPE);
+    FATAL(SIGBUS);
+    
+    IGNORE(SIGTRAP);
+    /*IGNORE(SIGWINCH);*/
+
+    sigemptyset(&(sa.sa_mask));
+
+    IFTRAP(SIGALRM){
+        sa.sa_handler=timer_handler;
+        sa.sa_flags=SA_RESTART;
+        sigaction(SIGALRM, &sa, NULL);
+    }
+
+    IFTRAP(SIGCHLD){
+        sa.sa_handler=chld_handler;
+        sa.sa_flags=SA_NOCLDSTOP|SA_RESTART;
+        sigaction(SIGCHLD, &sa, NULL);
+    }
+
+    IFTRAP(SIGUSR2){
+        sa.sa_handler=usr2_handler;
+        sa.sa_flags=SA_RESTART;
+        sigaction(SIGUSR2, &sa, NULL);
+    }
+
+    IFTRAP(SIGTERM){
+        sa.sa_handler=exit_handler;
+        sa.sa_flags=SA_RESTART;
+        sigaction(SIGTERM, &sa, NULL);
+    }
+    
+    IFTRAP(SIGUSR1){
+        sa.sa_handler=exit_handler;
+        sa.sa_flags=SA_RESTART;
+        sigaction(SIGUSR1, &sa, NULL);
+    }
+    
+    /* SIG_IGN is preserved over execve and since the the default action
+     * for SIGPIPE is not to ignore it, some programs may get upset if
+     * the behaviour is not the default.
+     */
+    IFTRAP(SIGPIPE){
+        sa.sa_handler=ignore_handler;
+        sigaction(SIGPIPE, &sa, NULL);
+    }
+
+}
+
+#undef IGNORE
+#undef FATAL
+#undef DEADLY
+
+
+/*}}}*/
+
diff --git a/libmainloop/signal.h b/libmainloop/signal.h
new file mode 100644 (file)
index 0000000..7867c18
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * ion/mainloop/signal.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_LIBMAINLOOP_SIGNAL_H
+#define ION_LIBMAINLOOP_SIGNAL_H
+
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <libtu/obj.h>
+#include <libtu/types.h>
+#include <libextl/extl.h>
+
+#include "hooks.h"
+
+INTRCLASS(WTimer);
+
+typedef void WTimerHandler(WTimer *timer, Obj *obj);
+
+
+DECLCLASS(WTimer){
+    Obj obj;
+    struct timeval when;
+    WTimer *next;
+    WTimerHandler *handler;
+    Watch objwatch;
+    ExtlFn extl_handler;
+};
+
+extern bool timer_init(WTimer *timer);
+extern void timer_deinit(WTimer *timer);
+
+extern WTimer *create_timer();
+extern WTimer *create_timer_extl_owned();
+
+extern void timer_set(WTimer *timer, uint msecs, WTimerHandler *handler,
+                      Obj *obj);
+extern void timer_set_extl(WTimer *timer, uint msecs, ExtlFn fn);
+
+extern void timer_reset(WTimer *timer);
+extern bool timer_is_set(WTimer *timer);
+
+extern bool mainloop_check_signals();
+extern void mainloop_trap_signals(const sigset_t *set);
+
+extern WHook *mainloop_sigchld_hook;
+extern WHook *mainloop_sigusr2_hook;
+
+#endif /* ION_LIBMAINLOOP_SIGNAL_H */
diff --git a/libtu/LICENSE b/libtu/LICENSE
new file mode 100644 (file)
index 0000000..1ddc996
--- /dev/null
@@ -0,0 +1,640 @@
+
+The Red-Black tree code is under the GNU LGPL. The rest of the files
+you may distribute and modify under either under the Clarified Artistic
+License or the GNU LGPL, version 2.1 or later, both reproduced below.
+
+
+-------------------
+
+
+The Clarified Artistic License
+
+Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package, while
+giving the users of the package the right to use and distribute the Package
+in a more-or-less customary fashion, plus the right to make reasonable
+modifications.
+
+Definitions:
+
+"Package" refers to the collection of files distributed by the Copyright
+Holder, and derivatives of that collection of files created through textual
+modification.
+"Standard Version" refers to such a Package if it has not been modified, or
+has been modified in accordance with the wishes of the Copyright Holder as
+specified below.
+"Copyright Holder" is whoever is named in the copyright or copyrights for
+the package.
+"You" is you, if you're thinking about copying or distributing this
+Package.
+"Distribution fee" is a fee you charge for providing a copy of this Package
+to another party.
+"Freely Available" means that no fee is charged for the right to use the
+item, though there may be fees involved in handling the item. It also means
+that recipients of the item may redistribute it under the same conditions
+they received it.
+   
+ 1. You may make and give away verbatim copies of the source form of the
+    Standard Version of this Package without restriction, provided that you
+    duplicate all of the original copyright notices and associated
+    disclaimers.
+ 2. You may apply bug fixes, portability fixes and other modifications
+    derived from the Public Domain, or those made Freely Available, or from
+    the Copyright Holder. A Package modified in such a way shall still be
+    considered the Standard Version.
+ 3. You may otherwise modify your copy of this Package in any way, provided
+    that you insert a prominent notice in each changed file stating how and
+    when you changed that file, and provided that you do at least ONE of
+    the following:
+       
+     1. place your modifications in the Public Domain or otherwise make them
+        Freely Available, such as by posting said modifications to Usenet
+        or an equivalent medium, or placing the modifications on a major
+        network archive site allowing unrestricted access to them, or by
+        allowing the Copyright Holder to include your modifications in the
+        Standard Version of the Package.
+     2. use the modified Package only within your corporation or
+        organization.
+     3. rename any non-standard executables so the names do not conflict
+        with standard executables, which must also be provided, and provide
+        a separate manual page for each non-standard executable that
+        clearly documents how it differs from the Standard Version.
+     4. make other distribution arrangements with the Copyright Holder.
+     5. permit and encourge anyone who receives a copy of the modified
+        Package permission to make your modifications Freely Available in
+        some specific way.
+   
+ 4. You may distribute the programs of this Package in object code or
+    executable form, provided that you do at least ONE of the following:
+       
+     1. distribute a Standard Version of the executables and library files,
+        together with instructions (in the manual page or equivalent) on
+        where to get the Standard Version.
+     2. accompany the distribution with the machine-readable source of the
+        Package with your modifications.
+     3. give non-standard executables non-standard names, and clearly
+        document the differences in manual pages (or equivalent), together
+        with instructions on where to get the Standard Version.
+     4. make other distribution arrangements with the Copyright Holder.
+     5. offer the machine-readable source of the Package, with your
+        modifications, by mail order.
+   
+ 5. You may charge a distribution fee for any distribution of this Package.
+    If you offer support for this Package, you may charge any fee you
+    choose for that support. You may not charge a license fee for the right
+    to use this Package itself. You may distribute this Package in
+    aggregate with other (possibly commercial and possibly nonfree)
+    programs as part of a larger (possibly commercial and possibly nonfree)
+    software distribution, and charge license fees for other parts of that
+    software distribution, provided that you do not advertise this Package
+    as a product of your own. If the Package includes an interpreter, You
+    may embed this Package's interpreter within an executable of yours (by
+    linking); this shall be construed as a mere form of aggregation,
+    provided that the complete Standard Version of the interpreter is so
+    embedded.
+ 6. The scripts and library files supplied as input to or produced as output
+    from the programs of this Package do not automatically fall under the
+    copyright of this Package, but belong to whoever generated them, and
+    may be sold commercially, and may be aggregated with this Package. If
+    such scripts or library files are aggregated with this Package via the
+    so-called "undump" or "unexec" methods of producing a binary executable
+    image, then distribution of such an image shall neither be construed as
+    a distribution of this Package nor shall it fall under the restrictions
+    of Paragraphs 3 and 4, provided that you do not represent such an
+    executable image as a Standard Version of this Package.
+ 7. C subroutines (or comparably compiled subroutines in other languages)
+    supplied by you and linked into this Package in order to emulate
+    subroutines and variables of the language defined by this Package shall
+    not be considered part of this Package, but are the equivalent of input
+    as in Paragraph 6, provided these subroutines do not change the
+    language in any way that would cause it to fail the regression tests
+    for the language.
+ 8. Aggregation of the Standard Version of the Package with a commercial
+    distribution is always permitted provided that the use of this Package
+    is embedded; that is, when no overt attempt is made to make this
+    Package's interfaces visible to the end user of the commercial
+    distribution. Such use shall not be construed as a distribution of this
+    Package.
+ 9. The name of the Copyright Holder may not be used to endorse or promote
+    products derived from this software without specific prior written
+    permission.
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+    WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+    MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+
+-------------------
+
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  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 Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            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 Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+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 other code 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.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+^L
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  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, whereas the latter must
+be combined with the library in order to run.
+^L
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser 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.
+^L
+  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.
+^L
+  6. As an exception to the Sections above, you may also combine 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) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) 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.
+
+    d) 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.
+
+    e) 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 materials to be 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.
+^L
+  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 with
+this License.
+^L
+  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 Lesser 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.
+^L
+  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
+^L
+           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 Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  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/libtu/Makefile b/libtu/Makefile
new file mode 100644 (file)
index 0000000..88b7a28
--- /dev/null
@@ -0,0 +1,58 @@
+##
+## libtu Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=.
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+CFLAGS += $(C89_SOURCE) $(POSIX_SOURCE) 
+
+SOURCES=misc.c output.c util.c optparser.c parser.c tokenizer.c \
+        map.c obj.c objlist.c errorlog.c ptrlist.c rb.c \
+        stringstore.c iterable.c setparam.c
+
+ifdef LIBTU_NO_ERRMSG
+DEFINES += -DLIBTU_NO_ERRMSG
+else
+ifndef HAS_SYSTEM_ASPRINTF
+SOURCES += snprintf_2.2/snprintf.c
+DEFINES += -DHAVE_SNPRINTF
+else
+DEFINES += -DHAS_SYSTEM_ASPRINTF
+endif
+endif
+
+TARGETS=libtu.a
+TESTERS=tester tester2 tester3
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+testers: $(TESTERS)
+
+libtu.a: $(OBJS)
+       $(AR) $(ARFLAGS) $@ $+
+       $(RANLIB) $@
+       
+tester: tester.c libtu.a
+       $(CC) $(CFLAGS) $< -L. -ltu -lm -o $@
+
+tester2: tester2.c libtu.a
+       $(CC) $(CFLAGS) $< -L. -ltu -lm -o $@
+
+tester3: tester3.c libtu.a
+       $(CC) $(CFLAGS) $< -L. -ltu -lm -o $@
+
+_install:
+       $(INSTALLDIR) $(LIBDIR)
+       $(INSTALLDIR) $(INCDIR)/libtu
+       $(INSTALL) -m $(DATA_MODE) libtu.a $(LIBDIR)
+       for i in *.h; do \
+               $(INSTALL) -m $(DATA_MODE) $$i $(INCDIR)/libtu; \
+       done
diff --git a/libtu/README b/libtu/README
new file mode 100644 (file)
index 0000000..cce76e5
--- /dev/null
@@ -0,0 +1,31 @@
+
+libtu
+
+Copyright (c) Tuomo Valkonen 1999-2004.
+<tuomov at iki.fi>
+
+
+Libtu is a small utility library for programs written in C. 
+
+Most of this library may be distributed and modified under either under
+the Clarified Artistic License or the GNU LGPL, version 2.1 or later, 
+both reproduced in the file LICENSE. The red-black tree code is under
+the GNU LGPL; see README.rb for details.
+
+To build the library, first edit system.mk to customize it for your
+system if necessary. Then 'make depend && make'.
+To install it run 'make install' (perhaps as root depending on
+where you are installing it).
+
+If you want to use it in your programs, you may try to figure out how
+by reading the header files (include/*.h) and test programs (tester*.c).
+Or you could try urging me to write some sort of a manual.
+
+---
+
+Libtu needs the functions asprintf and vasprintf. These do not
+exist on most platforms. One implementation by Mark Martinec
+is included in snprintf_2.2/ and is used by default. To use the
+system's versions of these functions, if available, modify
+system.mk.
+
diff --git a/libtu/README.rb b/libtu/README.rb
new file mode 100644 (file)
index 0000000..471ef7f
--- /dev/null
@@ -0,0 +1,113 @@
+Generic C code for red-black trees.
+Copyright (C) 2000 James S. Plank
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+---------------------------------------------------------------------------
+Jim Plank
+plank@cs.utk.edu
+http://www.cs.utk.edu/~plank
+
+Department of Computer Science
+University of Tennessee
+107 Ayres Hall
+Knoxville, TN 37996
+
+615-974-4397
+$Revision: 1.2 $
+---------------------------------------------------------------------------
+
+Rb.c and rb.h are files for doing general red-black trees.
+
+The directory ansi contains ansi-standard c code for rb-trees (there
+are some gross pointer casts there but don't worry about them.  They
+work).  The directory non-ansi contains straight c code for rb-trees.
+The ansi version can also be used by c++ (it has been tested).
+
+Rb.h contains the typedef for red-black tree structures.  Basically, 
+red-black trees are balanced trees whose external nodes are sorted
+by a key, and connected in a linked list.  The following is how
+you use rb.c and rb.h:
+
+Include rb.h in your source.
+
+Make_rb() returns the head of a red-black tree.  It serves two functions:
+Its p.root pointer points to the root of the red-black tree.  Its 
+c.list.flink and c.list.blink pointers point to the first and last 
+external nodes of the tree.  When the tree is empty, all these pointers
+point to itself.
+
+The external nodes can be traversed in sorted order with their
+c.list.flink and c.list.blink pointers.  The macros rb_first, rb_last,
+rb_next, rb_prev, and rb_traverse can be used to traverse external node lists.
+
+External nodes hold two pieces of information: the key and the value
+(in k.key and v.val, respectively).   The key can be a character 
+string, an integer, or a general pointer.  Val is typed as a character
+pointer, but can be any pointer.  If the key is a character string, 
+then one can insert it, and a value into a tree with rb_insert().  If
+it is an integer, then rb_inserti() should be used.  If it is a general 
+pointer, then rb_insertg() must be used, with a comparison function 
+passed as the fourth argument.  This function takes two keys as arguments,
+and returns a negative value, positive value, or 0, depending on whether
+the first key is less than, greater than or equal to the second.  Thus,
+one could use rb_insertg(t, s, v, strcmp) to insert the value v with 
+a character string s into the tree t.
+
+Rb_find_key(t, k) returns the external node in t whose value is equal
+k or whose value is the smallest value greater than k.  (Here, k is
+a string).  If there is no value greater than or equal to k, then 
+t is returned.  Rb_find_ikey(t,k) works when k is an integer, and 
+Rb_find_gkey(t,k,cmp) works for general pointers.
+
+Rb_find_key_n(t, k, n) is the same as rb_find_key, except that it
+returns whether or not k was found in n (n is an int *).  Rb_find_ikey_n
+and Rb_find_gkey_n are the analogous routines for integer and general
+keys.
+
+Rb_insert_b(e, k, v) makes a new external node with key k and val v, and 
+inserts it before the external node e.  If e is the head of a tree, 
+then the new node is inserted at the end of the external node list.
+If this insertion violates sorted order, no error is flagged.  It is 
+assumed that the user knows what he/she is doing.  Rb_insert_a(e,k,v)
+inserts the new node after e (if e = t, it inserts the new node at the
+beginning of the list).
+
+Rb_insert() is therefore really a combination of Rb_find_key() and
+Rb_insert_b().
+
+Rb_delete_node(e) deletes the external node e from a tree (and thus from 
+the linked list of external nodes).  The node is free'd as well, so
+don't retain pointers to it.
+
+Red-black trees are spiffy because find_key, insert, and delete are all
+done in log(n) time.  Thus, they can be freely used instead of hash-tables,
+with the benifit of having the elements in sorted order at all times, and
+with the guarantee of operations being in log(n) time.
+
+Other routines:
+
+Rb_print_tree() will grossly print out a red-black tree with string keys.
+Rb_iprint_tree() will do the same with trees with integer keys.
+Rb_nblack(e) will report the number of black nodes on the path from external
+  node e to the root.  The path length is less than twice this value.
+Rb_plength(e) reports the length of the path from e to the root.
+
+
+You can find a general description of red-black trees in any basic algorithms
+text.  E.g. ``Introduction to Algorithms'', by Cormen, Leiserson and Rivest
+(McGraw Hill).  An excellent and complete description of red-black trees
+can also be found in Chapter 1 of Heather Booth's PhD disseratation:
+``Some Fast Algorithms on Graphs and Trees'', Princeton University, 1990.
diff --git a/libtu/build/system-inc.mk b/libtu/build/system-inc.mk
new file mode 100644 (file)
index 0000000..5390bc9
--- /dev/null
@@ -0,0 +1,2 @@
+TOPDIR := $(TOPDIR)/..
+include $(TOPDIR)/build/system-inc.mk
diff --git a/libtu/debug.h b/libtu/debug.h
new file mode 100644 (file)
index 0000000..729222d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * libtu/debug.h
+ *
+ * Copyright (c) Tuomo Valkonen 2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_DEBUG_H
+#define LIBTU_DEBUG_H
+
+#ifdef CF_DEBUG
+#define D(X) X
+#else
+#define D(X)
+#endif
+
+#endif /* LIBTU_DEBUG_H */
diff --git a/libtu/dlist.h b/libtu/dlist.h
new file mode 100644 (file)
index 0000000..9576771
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * libtu/common.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_DLIST_H
+#define LIBTU_DLIST_H
+
+
+/*{{{ Linking */
+
+
+#define LINK_ITEM(LIST, ITEM, NEXT, PREV) \
+    (ITEM)->NEXT=NULL;                    \
+    if((LIST)==NULL){                     \
+        (LIST)=(ITEM);                    \
+        (ITEM)->PREV=(ITEM);              \
+    }else{                                \
+        (ITEM)->PREV=(LIST)->PREV;        \
+        (ITEM)->PREV->NEXT=(ITEM);        \
+        (LIST)->PREV=(ITEM);              \
+    }
+
+
+#define LINK_ITEM_FIRST(LIST, ITEM, NEXT, PREV) \
+    (ITEM)->NEXT=(LIST);                        \
+    if((LIST)==NULL){                           \
+        (ITEM)->PREV=(ITEM);                    \
+    }else{                                      \
+        (ITEM)->PREV=(LIST)->PREV;              \
+        (LIST)->PREV=(ITEM);                    \
+    }                                           \
+    (LIST)=(ITEM);
+
+
+#define LINK_ITEM_LAST LINK_ITEM
+
+
+#define LINK_ITEM_BEFORE(LIST, BEFORE, ITEM, NEXT, PREV) \
+    (ITEM)->NEXT=(BEFORE);                               \
+    (ITEM)->PREV=(BEFORE)->PREV;                         \
+    (BEFORE)->PREV=(ITEM);                               \
+    if((BEFORE)==(LIST))                                 \
+        (LIST)=(ITEM);                                   \
+    else                                                 \
+        (ITEM)->PREV->NEXT=(ITEM)
+
+
+#define LINK_ITEM_AFTER(LIST, AFTER, ITEM, NEXT, PREV) \
+    (ITEM)->NEXT=(AFTER)->NEXT;                        \
+    (ITEM)->PREV=(AFTER);                              \
+    (AFTER)->NEXT=(ITEM);                              \
+    if((ITEM)->NEXT==NULL)                             \
+        (LIST)->PREV=(ITEM);                           \
+    else                                               \
+        (ITEM)->NEXT->PREV=ITEM;
+
+
+#define UNLINK_ITEM(LIST, ITEM, NEXT, PREV)  \
+    if((ITEM)->PREV!=NULL){                  \
+        if((ITEM)==(LIST)){                  \
+            (LIST)=(ITEM)->NEXT;             \
+            if((LIST)!=NULL)                 \
+                (LIST)->PREV=(ITEM)->PREV;   \
+        }else if((ITEM)->NEXT==NULL){        \
+            (ITEM)->PREV->NEXT=NULL;         \
+            (LIST)->PREV=(ITEM)->PREV;       \
+        }else{                               \
+            (ITEM)->PREV->NEXT=(ITEM)->NEXT; \
+            (ITEM)->NEXT->PREV=(ITEM)->PREV; \
+        }                                    \
+    }                                        \
+    (ITEM)->NEXT=NULL;                       \
+    (ITEM)->PREV=NULL;
+
+
+/*}}}*/
+
+
+/*{{{ Iteration */
+
+
+#define LIST_FIRST(LIST, NEXT, PREV) \
+    (LIST)
+#define LIST_LAST(LIST, NEXT, PREV) \
+    ((LIST)==NULL ? NULL : LIST_PREV_WRAP(LIST, LIST, NEXT, PREV))
+#define LIST_NEXT(LIST, REG, NEXT, PREV) \
+    ((REG)->NEXT)
+#define LIST_PREV(LIST, REG, NEXT, PREV) \
+    ((REG)->PREV->NEXT ? (REG)->PREV : NULL)
+#define LIST_NEXT_WRAP(LIST, REG, NEXT, PREV) \
+    (((REG) && (REG)->NEXT) ? (REG)->NEXT : (LIST))
+#define LIST_PREV_WRAP(LIST, REG, NEXT, PREV) \
+    ((REG) ? (REG)->PREV : (LIST))
+
+#define LIST_FOR_ALL(LIST, NODE, NEXT, PREV) \
+    for(NODE=LIST; NODE!=NULL; NODE=(NODE)->NEXT)
+
+#define LIST_FOR_ALL_REV(LIST, NODE, NEXT, PREV)     \
+    for(NODE=((LIST)==NULL ? NULL : (LIST)->PREV);   \
+        NODE!=NULL;                                  \
+        NODE=((NODE)==(LIST) ? NULL : (NODE)->PREV))
+
+#define LIST_FOR_ALL_W_NEXT(LIST, NODE, NXT, NEXT, PREV)  \
+    for(NODE=LL, NXT=(NODE==NULL ? NULL : (NODE)->NEXT);  \
+        NODE!=NULL;                                       \
+        NODE=NXT, NXT=(NODE==NULL ? NULL : (NODE)->NEXT))
+
+#define LIST_FOR_ALL_W_NEXT_REV(LIST, NODE, NXT, NEXT, PREV) \
+    for(NODE=((LIST)==NULL ? NULL : (LIST)->PREV),           \
+         NXT=((NODE)==(LIST) ? NULL : (NODE)->PREV);         \
+        NODE!=NULL;                                          \
+        NODE=NXT,                                            \
+         NXT=((NODE)==(LIST) ? NULL : (NODE)->PREV))
+
+
+/*}}}*/
+
+
+#endif /* LIBTU_DLIST_H */
diff --git a/libtu/errorlog.c b/libtu/errorlog.c
new file mode 100644 (file)
index 0000000..da97713
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * libtu/errorlog.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "util.h"
+#include "types.h"
+#include "output.h"
+#include "misc.h"
+#include "errorlog.h"
+
+static ErrorLog *current_log=NULL;
+
+static void add_to_log(ErrorLog *el, const char *message, int l)
+{
+    if(message==NULL)
+        return;
+    
+    /* Also write to stderr */
+    fwrite(message, sizeof(char), l, stderr);
+
+    if(el==NULL)
+        return;
+    
+    if(el->file!=NULL){
+        el->errors=TRUE;
+        fwrite(message, sizeof(char), l, el->file);
+        return;
+    }
+
+    if(el->msgs==NULL){
+        el->msgs=ALLOC_N(char, ERRORLOG_MAX_SIZE);
+        if(el->msgs==NULL){
+            fprintf(stderr, "%s: %s\n", prog_execname(), strerror(errno));
+            return;
+        }
+        el->msgs[0]=0;
+        el->msgs_len=0;
+    }
+            
+    el->errors=TRUE;
+    
+    if(l+el->msgs_len>ERRORLOG_MAX_SIZE-1){
+        int n=0;
+        if(l<ERRORLOG_MAX_SIZE-1){
+            n=ERRORLOG_MAX_SIZE-1-l;
+            memmove(el->msgs, el->msgs+el->msgs_len-n, n);
+        }
+        memcpy(el->msgs+n, message+l-(ERRORLOG_MAX_SIZE-1-n),
+               ERRORLOG_MAX_SIZE-1-n);
+        el->msgs[ERRORLOG_MAX_SIZE]='\0';
+        el->msgs_len=ERRORLOG_MAX_SIZE-1;
+    }else{
+        memcpy(el->msgs+el->msgs_len, message, l);
+        el->msgs[el->msgs_len+l]='\0';
+        el->msgs_len+=l;
+    }
+}
+
+
+static void log_warn_handler(const char *message)
+{
+    const char *p=strchr(message, '\n');
+    static int lineno=0;
+    int alternat=0;
+    
+    add_to_log(current_log, lineno==0 ? ">> " : "   ", 3);
+    
+    if(p!=NULL){
+        add_to_log(current_log, message, p-message+1);
+        lineno++;
+        log_warn_handler(p+1);
+        lineno--;
+        return;
+    }
+    
+    add_to_log(current_log, message, strlen(message));
+    add_to_log(current_log, "\n", 1);
+}
+
+           
+void errorlog_begin_file(ErrorLog *el, FILE *file)
+{
+    el->msgs=NULL;
+    el->msgs_len=0;
+    el->file=file;
+    el->prev=current_log;
+    el->errors=FALSE;
+    el->old_handler=set_warn_handler(log_warn_handler);
+    current_log=el;
+}
+
+
+void errorlog_begin(ErrorLog *el)
+{
+    errorlog_begin_file(el, NULL);
+}
+
+
+bool errorlog_end(ErrorLog *el)
+{
+    current_log=el->prev;
+    set_warn_handler(el->old_handler);
+    el->prev=NULL;
+    el->old_handler=NULL;
+    return el->errors;
+}
+
+
+void errorlog_deinit(ErrorLog *el)
+{
+    if(el->msgs!=NULL)
+        free(el->msgs);
+    el->msgs=NULL;
+    el->msgs_len=0;
+    el->file=NULL;
+    el->errors=FALSE;
+}
+
diff --git a/libtu/errorlog.h b/libtu/errorlog.h
new file mode 100644 (file)
index 0000000..93da8ba
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * libtu/errorlog.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_ERRORLOG_H
+#define LIBTU_ERRORLOG_H
+
+#include <stdio.h>
+
+#include "types.h"
+#include "obj.h"
+#include "output.h"
+
+#define ERRORLOG_MAX_SIZE (1024*4)
+
+INTRSTRUCT(ErrorLog);
+DECLSTRUCT(ErrorLog){
+    char *msgs;
+    int msgs_len;
+    FILE *file;
+    bool errors;
+    ErrorLog *prev;
+    WarnHandler *old_handler;
+};
+
+/* el is assumed to be uninitialised  */
+extern void errorlog_begin(ErrorLog *el);
+extern void errorlog_begin_file(ErrorLog *el, FILE *file);
+/* For errorlog_end el Must be the one errorlog_begin was last called with */
+extern bool errorlog_end(ErrorLog *el);
+extern void errorlog_deinit(ErrorLog *el);
+
+#endif /* LIBTU_ERRORLOG_H */
diff --git a/libtu/exact-version b/libtu/exact-version
new file mode 100644 (file)
index 0000000..20aff08
--- /dev/null
@@ -0,0 +1,56 @@
+
+Context:
+
+[Path fix
+Tuomo Valkonen <tuomov@iki.fi>**20061016223205] 
+
+[Updated locations of *.mk.
+Tuomo Valkonen <tuomov@iki.fi>**20060803210914] 
+
+[*list_remove return true if the item was found (and removed).
+Tuomo Valkonen <tuomov@iki.fi>**20060107210154] 
+
+[Added some coercions to remove gcc complaints in snprintf_2.2.
+Tuomo Valkonen <tuomov@iki.fi>**20050514202320] 
+
+[Added plain dlist reverse forall.
+Tuomo Valkonen <tuomov@iki.fi>**20050325164819] 
+
+[Added libtu_setparam_invert.
+Tuomo Valkonen <tuomov@iki.fi>**20050319210755] 
+
+[Added setparam.c.
+Tuomo Valkonen <tuomov@iki.fi>**20050319202730] 
+
+[Added XOR macro.
+Tuomo Valkonen <tuomov@iki.fi>**20050319195625] 
+
+[Use install-sh.
+Tuomo Valkonen <tuomov@iki.fi>**20050301215757] 
+
+[Added routinesn for generic iterables.
+Tuomo Valkonen <tuomov@iki.fi>**20050226230912] 
+
+[Increased FOR_ALL macro reuse.
+Tuomo Valkonen <tuomov@iki.fi>**20050226221051] 
+
+[Added routines to take first/last elements of objlist and ptrlist.
+Tuomo Valkonen <tuomov@iki.fi>**20050226210933] 
+
+[ObjList changes.
+Tuomo Valkonen <tuomov@iki.fi>**20050226205819] 
+
+[Renamed Symlist PtrList.
+Tuomo Valkonen <tuomov@iki.fi>**20050226203855] 
+
+[Added struct field address macros.
+Tuomo Valkonen <tuomov@iki.fi>**20050226093720] 
+
+[Symlist improvements.
+Tuomo Valkonen <tuomov@iki.fi>**20050224081221] 
+
+[Added dlist iteration macros.
+Tuomo Valkonen <tuomov@iki.fi>**20050223180501] 
+
+[TAG libtu-3-svn2darcs
+Tuomo Valkonen <tuomov@iki.fi>**20050215180637] 
diff --git a/libtu/install-sh b/libtu/install-sh
new file mode 100644 (file)
index 0000000..e9de238
--- /dev/null
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+               chmodcmd=""
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/libtu/iterable.c b/libtu/iterable.c
new file mode 100644 (file)
index 0000000..63b00c1
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * libtu/iterable.c
+ *
+ * Copyright (c) Tuomo Valkonen 2005.
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include "iterable.h"
+
+
+void *iterable_nth(uint n, VoidIterator *iter, void *st)
+{
+    void *p;
+    
+    while(1){
+        p=iter(st);
+        if(p==NULL || n==0)
+            break;
+        n--;
+    }
+    
+    return p;
+}
+
+
+bool iterable_is_on(void *p, VoidIterator *iter, void *st)
+{
+    while(1){
+        void *p2=iter(st);
+        if(p2==NULL)
+            return FALSE;
+        if(p==p2)
+            return TRUE;
+    }
+}
+
+
+void *iterable_find(BoolFilter *f, void *fparam, 
+                    VoidIterator *iter, void *st)
+{
+    while(1){
+        void *p=iter(st);
+        if(p==NULL)
+            return NULL;
+        if(f(p, fparam))
+            return p;
+    }
+}
+        
diff --git a/libtu/iterable.h b/libtu/iterable.h
new file mode 100644 (file)
index 0000000..8b70b09
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * libtu/iterable.h
+ *
+ * Copyright (c) Tuomo Valkonen 2005.
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_ITERABLE_H
+#define LIBTU_ITERABLE_H
+
+#include "types.h"
+#include "obj.h"
+
+typedef void *VoidIterator(void *);
+typedef Obj *ObjIterator(void *);
+
+typedef bool BoolFilter(void *p, void *param);
+
+#define FOR_ALL_ITER(INIT, ITER, VAR, LL, TMP) \
+    for(INIT(TMP, LL), (VAR)=ITER(TMP); (VAR)!=NULL; VAR=ITER(TMP))
+
+extern void *iterable_nth(uint n, VoidIterator *iter, void *st);
+extern bool iterable_is_on(void *p, VoidIterator *iter, void *st);
+extern void *iterable_find(BoolFilter *f, void *fparam, 
+                           VoidIterator *iter, void *st);
+    
+#endif /* LIBTU_ITERABLE_H */
diff --git a/libtu/locale.h b/libtu/locale.h
new file mode 100644 (file)
index 0000000..b50f528
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * libtu/locale.h
+ *
+ * Copyright (c) Tuomo Valkonen 2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_LOCALE_H
+#define LIBTU_LOCALE_H
+
+#ifdef CF_NO_LOCALE
+
+#define TR(X) X
+#define DUMMY_TR(X) X
+
+#else
+
+#include <libintl.h>
+
+#define TR(X) gettext(X)
+#define DUMMY_TR(X) X
+
+#endif
+
+#endif /* LIBTU_LOCALE_H */
diff --git a/libtu/map.c b/libtu/map.c
new file mode 100644 (file)
index 0000000..7e5f202
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * libtu/map.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <string.h>
+#include "map.h"
+
+
+int stringintmap_ndx(const StringIntMap *map, const char *str)
+{
+    int i;
+    
+    for(i=0; map[i].string!=NULL; i++){
+        if(strcmp(str, map[i].string)==0)
+            return i;
+    }
+    
+    return -1;
+}
+
+
+int stringintmap_value(const StringIntMap *map, const char *str, int dflt)
+{
+    int i=stringintmap_ndx(map, str);
+    return (i==-1 ? dflt : map[i].value); 
+}
+
+
+const char *stringintmap_key(const StringIntMap *map, int value, 
+                             const char *dflt)
+{
+    int i;
+    
+    for(i=0; map[i].string!=NULL; ++i){
+        if(map[i].value==value){
+            return map[i].string;
+        }
+    }
+    
+    return dflt;
+    
+}
diff --git a/libtu/map.h b/libtu/map.h
new file mode 100644 (file)
index 0000000..d66ae74
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * libtu/map.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_MAP_H
+#define LIBTU_MAP_H
+
+typedef struct _StringIntMap{
+    const char *string;
+    int value;
+} StringIntMap;
+
+#define END_STRINGINTMAP {NULL, 0}
+
+/* Return the index of str in map or -1 if not found. */
+extern int stringintmap_ndx(const StringIntMap *map, const char *str);
+extern int stringintmap_value(const StringIntMap *map, const char *str,
+                              int dflt);
+extern const char *stringintmap_key(const StringIntMap *map, 
+                                    int value, const char *dflt);
+
+#endif /* LIBTU_MAP_H */
diff --git a/libtu/minmax.h b/libtu/minmax.h
new file mode 100644 (file)
index 0000000..5866d7f
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * libtu/minmax.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_MINMAX_H
+#define LIBTU_MINMAX_H
+
+
+static int minof(int x, int y)
+{
+    return (x<y ? x : y);
+}
+
+
+static int maxof(int x, int y)
+{
+    return (x>y ? x : y);
+}
+
+
+#endif /* LIBTU_MINMAX_H */
+
diff --git a/libtu/misc.c b/libtu/misc.c
new file mode 100644 (file)
index 0000000..39137c3
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * libtu/misc.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "misc.h"
+#include "output.h"
+
+
+void *malloczero(size_t size)
+{
+    void *p=malloc(size);
+    
+    if(p!=NULL)
+        memset(p, 0, size);
+    else
+        warn_err();
+    
+    return p;
+}
+
+
+void *remalloczero(void *ptr, size_t oldsize, size_t newsize)
+{
+    void *p=NULL;
+    
+    if(newsize!=0){
+        p=realloc(ptr, newsize);
+        
+        if(p==NULL){
+            warn_err();
+            return NULL;
+        }
+    
+        if(newsize>oldsize)
+            memset((char*)p+oldsize, 0, newsize-oldsize);
+    }
+
+    return p;
+}
+
+
+char *scopyn(const char *p, size_t l)
+{
+    char *pn=ALLOC_N(char, l+1);
+    
+    if(pn==NULL)
+        return NULL;
+    
+    memcpy(pn, p, l);
+    pn[l]='\0';
+    
+    return pn;
+}
+
+
+char *scopy(const char *p)
+{
+    size_t l=strlen(p);
+    return scopyn(p, l);
+}
+    
+    
+char *scat(const char *p1, const char *p2)
+{
+    size_t l1, l2;
+    char*pn;
+    
+    l1=strlen(p1);
+    l2=strlen(p2);
+    
+    pn=ALLOC_N(char, l1+l2+1);
+    
+    if(pn==NULL)
+        return NULL;
+    
+    memcpy(pn, p1, l1);
+    memcpy(pn+l1, p2, l2+1);
+    
+    return pn;
+}
+
+
+char *scat3(const char *p1, const char *p2, const char *p3)
+{
+    size_t l1, l2, l3;
+    char *pn;
+    
+    l1=strlen(p1);
+    l2=strlen(p2);
+    l3=strlen(p3);
+    
+    pn=ALLOC_N(char, l1+l2+l3+1);
+    
+    if(pn==NULL)
+        return NULL;
+    
+    memcpy(pn, p1, l1);
+    memcpy(pn+l1, p2, l2);
+    memcpy(pn+l1+l2, p3, l3+1);
+    
+    return pn;
+}
+
+
+char *scatn(const char *s1, ssize_t l1, const char *s2, ssize_t l2)
+{
+    size_t tlen=1;
+    char *s;
+    
+    if(l1<0)
+        l1=strlen(s1);
+    
+    if(l2<0)
+        l2=strlen(s2);
+    
+    tlen+=l1+l2;
+    
+    s=ALLOC_N(char, tlen);
+    
+    if(s==NULL)
+        return NULL;
+    
+    memcpy(s, s1, l1);
+    memcpy(s+l1, s2, l2);
+    s[l1+l2]='\0';
+    
+    return s;
+}
+
+
+/* */
+
+
+const char *simple_basename(const char *name)
+{
+    const char *p;
+    
+    p=name+strlen(name)-1;
+    
+    /* Skip any trailing slashes */
+    while(*p=='/'){
+        /* root? */
+        if(p==name)
+            return name;
+        p--;
+    }
+    
+    while(p!=name){
+        if(*p=='/')
+            return p+1;
+        p--;
+    }
+    
+    return name;
+}
+
+
+void stripws(char *p)
+{
+    int l;
+    
+    l=strspn(p, " ");
+    if(l!=0)
+        strcpy(p, p+l);
+    l=strlen(p);
+    
+    while(--l>=0){
+        if(*(p+l)!=' ')
+            break;
+    }
+    *(p+l+1)='\0';
+}
+
+
+const char *libtu_strcasestr(const char *str, const char *ptn)
+{
+    const char *s2, *p2;
+    for(; *str; str++) {
+        for(s2=str, p2=ptn; ; s2++, p2++) {
+            if(!*p2)
+                return str;
+            if(toupper(*s2)!=toupper(*p2)) 
+                break;
+        }
+    }
+    return NULL;
+}
+
+/* */
+
+
+bool readf(FILE *f, void *buf, size_t n)
+{
+    return fread(buf, 1, n, f)!=1;
+}
+
+
+bool writef(FILE *f, const void *buf, size_t n)
+{
+    return fwrite(buf, 1, n, f)!=1;
+}
diff --git a/libtu/misc.h b/libtu/misc.h
new file mode 100644 (file)
index 0000000..a8e6d3f
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * libtu/misc.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005.
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_MISC_H
+#define LIBTU_MISC_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "types.h"
+
+#define ALLOC(X) (X*)malloczero(sizeof(X))
+#define ALLOC_N(X, N) (X*)malloczero(sizeof(X)*(N))
+#define REALLOC_N(PTR, X, S, N) (X*)remalloczero(PTR, sizeof(X)*(S), sizeof(X)*(N))
+
+#define FREE(X) do{if(X!=NULL)free(X);}while(0)
+
+#define XOR(X, Y) (((X)==0) != ((Y)==0))
+
+extern void* malloczero(size_t size);
+extern void* remalloczero(void *ptr, size_t oldsize, size_t newsize);
+
+extern char* scopy(const char *p);
+extern char* scopyn(const char *p, size_t n);
+extern char* scat(const char *p1, const char *p2);
+extern char* scatn(const char *p1, ssize_t n1, const char *p2, ssize_t n2);
+extern char* scat3(const char *p1, const char *p2, const char *p3);
+extern void stripws(char *p);
+extern const char *libtu_strcasestr(const char *str, const char *ptn);
+
+extern const char* simple_basename(const char *name);
+
+/* I dislike fread and fwrite... */
+extern bool readf(FILE *fd, void *buf, size_t n);
+extern bool writef(FILE *fd, const void *buf, size_t n);
+
+#endif /* LIBTU_MISC_H */
diff --git a/libtu/np-conv.h b/libtu/np-conv.h
new file mode 100644 (file)
index 0000000..c6c13c7
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * libtu/np-conv.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <math.h>
+
+#ifdef NP_SIMPLE_IMPL
+
+#define FN_NUM_TO_SIGNED(T, UMAX, MAX, MIN)                          \
+ static int num_to_##T(T *ret, const NPNum *num, bool allow_uns_big) \
+ {                                                                   \
+       if(num->type!=NPNUM_INT)                                         \
+               return E_TOKZ_NOTINT;                                        \
+                                                                        \
+       if(!num->negative){                                              \
+               *ret=num->ival;                                              \
+               if(allow_uns_big?num->ival>UMAX:num->ival>MAX)               \
+               return E_TOKZ_RANGE;                                         \
+       }else{                                                           \
+               *ret=-num->ival;                                             \
+               if(num->ival>-(ulong)MIN)                                    \
+               return E_TOKZ_RANGE;                                         \
+       }                                                                \
+       return 0;                                                        \
+ }
+
+#define FN_NUM_TO_UNSIGNED(T, UMAX, MIN)                         \
+ static int num_to_##T(T *ret, const NPNum *num, bool allow_neg) \
+ {                                                               \
+       if(num->type!=NPNUM_INT)                                     \
+               return E_TOKZ_NOTINT;                                    \
+                                                                    \
+       if(!num->negative){                                          \
+               *ret=num->ival;                                          \
+               if(num->ival>UMAX)                                       \
+               return E_TOKZ_RANGE;                                     \
+       }else{                                                       \
+               *ret=-num->ival;                                         \
+               if(!allow_neg || num->ival>(ulong)-MIN)                  \
+               return E_TOKZ_RANGE;                                     \
+       }                                                            \
+       return 0;                                                    \
+ }
+
+#define FN_NUM_TO_FLOAT(T, POW)                  \
+ static int num_to_##T(T *ret, const NPNum *num) \
+ {                                               \
+       *ret=(num->negative?-num->fval:num->fval);   \
+       return 0;                                    \
+ }
+
+#else /* NP_SIMPLE_IMPL */
+
+#define FN_NUM_TO_SIGNED(T, UMAX, MAX, MIN)                          \
+ static int num_to_##T(T *ret, const NPNum *num, bool allow_uns_big) \
+ {                                                                   \
+       if(num->exponent)                                                \
+               return E_TOKZ_NOTINT;                                        \
+       if(num->nmantissa>0)                                             \
+               return E_TOKZ_RANGE;                                         \
+                                                                     \
+       if(!num->negative){                                              \
+               *ret=num->mantissa[0];                                       \
+               if(allow_uns_big?num->mantissa[0]>UMAX:num->mantissa[0]>MAX) \
+                       return E_TOKZ_RANGE;                                     \
+       }else{                                                           \
+               *ret=-num->mantissa[0];                                      \
+               if(num->mantissa[0]>-(ulong)MIN)                             \
+                       return E_TOKZ_RANGE;                                     \
+       }                                                                \
+       return 0;                                                        \
+}
+
+#define FN_NUM_TO_UNSIGNED(T, UMAX, MIN)                         \
+ static int num_to_##T(T *ret, const NPNum *num, bool allow_neg) \
+ {                                                               \
+       if(num->exponent)                                            \
+               return E_TOKZ_NOTINT;                                    \
+       if(num->nmantissa>0)                                         \
+               return E_TOKZ_RANGE;                                     \
+                                                                 \
+       if(!num->negative){                                          \
+               *ret=num->mantissa[0];                                   \
+               if(num->mantissa[0]>UMAX)                                \
+                       return E_TOKZ_RANGE;                                 \
+       }else{                                                       \
+               *ret=-num->mantissa[0];                                  \
+               if(!allow_neg || num->mantissa[0]>(ulong)-MIN)           \
+                       return E_TOKZ_RANGE;                                 \
+       }                                                            \
+       return 0;                                                    \
+}
+
+
+#define FN_NUM_TO_FLOAT(T, POW)                  \
+ static int num_to_##T(T *ret, const NPNum *num) \
+ {                                               \
+       T d=0;                                       \
+       int i;                                       \
+                                                    \
+       for(i=num->nmantissa;i>=0;i--)               \
+               d=d*(T)(ULONG_MAX+1.0)+num->mantissa[i]; \
+                                                 \
+       d*=POW(num->base, num->exponent);            \
+       *ret=d;                                      \
+                                                    \
+       return 0;                                    \
+ }
+
+#endif /* NP_SIMPLE_IMPL */
+
+FN_NUM_TO_SIGNED(long, ULONG_MAX, LONG_MAX, LONG_MIN)
+FN_NUM_TO_SIGNED(char, UCHAR_MAX, CHAR_MAX, CHAR_MIN)
+FN_NUM_TO_FLOAT(double, pow)
+
+#undef NEG
diff --git a/libtu/np/np-conv.h b/libtu/np/np-conv.h
new file mode 100644 (file)
index 0000000..7a1e537
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * libtu/np-conv.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <math.h>
+
+#ifdef NP_SIMPLE_IMPL
+
+#define FN_NUM_TO_SIGNED(T, UMAX, MAX, MIN)                          \
+ static int num_to_##T(T *ret, const NPNum *num, bool allow_uns_big) \
+ {                                                                   \
+    if(num->type!=NPNUM_INT)                                         \
+        return E_TOKZ_NOTINT;                                        \
+                                                                     \
+    if(!num->negative){                                              \
+        *ret=num->ival;                                              \
+        if(allow_uns_big?num->ival>UMAX:num->ival>MAX)               \
+        return E_TOKZ_RANGE;                                         \
+    }else{                                                           \
+        *ret=-num->ival;                                             \
+        if(num->ival>-(ulong)MIN)                                    \
+        return E_TOKZ_RANGE;                                         \
+    }                                                                \
+    return 0;                                                        \
+ }
+
+#define FN_NUM_TO_UNSIGNED(T, UMAX, MIN)                         \
+ static int num_to_##T(T *ret, const NPNum *num, bool allow_neg) \
+ {                                                               \
+    if(num->type!=NPNUM_INT)                                     \
+        return E_TOKZ_NOTINT;                                    \
+                                                                 \
+    if(!num->negative){                                          \
+        *ret=num->ival;                                          \
+        if(num->ival>UMAX)                                       \
+        return E_TOKZ_RANGE;                                     \
+    }else{                                                       \
+        *ret=-num->ival;                                         \
+        if(!allow_neg || num->ival>(ulong)-MIN)                  \
+        return E_TOKZ_RANGE;                                     \
+    }                                                            \
+    return 0;                                                    \
+ }
+
+#define FN_NUM_TO_FLOAT(T, POW)                  \
+ static int num_to_##T(T *ret, const NPNum *num) \
+ {                                               \
+    *ret=(num->negative?-num->fval:num->fval);   \
+    return 0;                                    \
+ }
+
+#else /* NP_SIMPLE_IMPL */
+
+#define FN_NUM_TO_SIGNED(T, UMAX, MAX, MIN)                          \
+ static int num_to_##T(T *ret, const NPNum *num, bool allow_uns_big) \
+ {                                                                   \
+    if(num->exponent)                                                \
+        return E_TOKZ_NOTINT;                                        \
+    if(num->nmantissa>0)                                             \
+        return E_TOKZ_RANGE;                                         \
+                                                                     \
+    if(!num->negative){                                              \
+        *ret=num->mantissa[0];                                       \
+        if(allow_uns_big?num->mantissa[0]>UMAX:num->mantissa[0]>MAX) \
+            return E_TOKZ_RANGE;                                     \
+    }else{                                                           \
+        *ret=-num->mantissa[0];                                      \
+        if(num->mantissa[0]>-(ulong)MIN)                             \
+            return E_TOKZ_RANGE;                                     \
+    }                                                                \
+    return 0;                                                        \
+}
+
+#define FN_NUM_TO_UNSIGNED(T, UMAX, MIN)                         \
+ static int num_to_##T(T *ret, const NPNum *num, bool allow_neg) \
+ {                                                               \
+    if(num->exponent)                                            \
+        return E_TOKZ_NOTINT;                                    \
+    if(num->nmantissa>0)                                         \
+        return E_TOKZ_RANGE;                                     \
+                                                                 \
+    if(!num->negative){                                          \
+        *ret=num->mantissa[0];                                   \
+        if(num->mantissa[0]>UMAX)                                \
+            return E_TOKZ_RANGE;                                 \
+    }else{                                                       \
+        *ret=-num->mantissa[0];                                  \
+        if(!allow_neg || num->mantissa[0]>(ulong)-MIN)           \
+            return E_TOKZ_RANGE;                                 \
+    }                                                            \
+    return 0;                                                    \
+}
+
+
+#define FN_NUM_TO_FLOAT(T, POW)                  \
+ static int num_to_##T(T *ret, const NPNum *num) \
+ {                                               \
+    T d=0;                                       \
+    int i;                                       \
+                                                 \
+    for(i=num->nmantissa;i>=0;i--)               \
+        d=d*(T)(ULONG_MAX+1.0)+num->mantissa[i]; \
+                                                 \
+    d*=POW(num->base, num->exponent);            \
+    *ret=d;                                      \
+                                                 \
+    return 0;                                    \
+ }
+
+#endif /* NP_SIMPLE_IMPL */
+
+FN_NUM_TO_SIGNED(long, ULONG_MAX, LONG_MAX, LONG_MIN)
+FN_NUM_TO_SIGNED(char, UCHAR_MAX, CHAR_MAX, CHAR_MIN)
+FN_NUM_TO_FLOAT(double, pow)
+
+#undef NEG
diff --git a/libtu/np/numparser2.h b/libtu/np/numparser2.h
new file mode 100644 (file)
index 0000000..213c4e0
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * libtu/numparser2.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#define MAX_MANTISSA 10  /* should be enough for our needs */
+#define ULONG_SIZE (sizeof(ulong)*8)
+
+enum{
+    NPNUM_INT,
+    NPNUM_FLOAT
+};
+
+#ifdef NP_SIMPLE_IMPL
+
+typedef struct _NPNum{
+    int type;
+    int base;
+     bool negative;
+    double fval;
+    ulong ival;
+} NPNum;
+
+#define NUM_INIT {0, 0, 0, 0.0, 0}
+
+static int npnum_mulbase_add(NPNum *num, long base, long v)
+{
+    double iold=num->ival;
+    
+    num->fval=num->fval*base+(double)v;
+    
+    num->ival*=base;
+    
+    if(num->ival<iold)
+        num->type=NPNUM_FLOAT;
+    
+    num->ival+=v;
+    
+    return 0;
+}
+
+#else /* NP_SIMPLE_IMPL */
+
+typedef struct _NPNum{
+    unsigned char nmantissa;
+    int type;
+    int base;
+     bool negative;
+    ulong mantissa[MAX_MANTISSA];
+    long exponent;
+} NPNum;
+
+#define NUM_INIT {0, 0, 0, 0, {0,}, 0}
+
+#define ADD_EXP(NUM, X) (NUM)->exponent+=(X);
+
+#if defined(__GNUG__) && defined(i386) && !defined(NP_NO_I386_ASM)
+ #define NP_I386_ASM
+#endif
+
+static int npnum_mulbase_add(NPNum *num, long base, long v)
+{
+    long i, j;
+    ulong overflow;
+#ifndef NP_I386_ASM
+    ulong val;
+#endif
+    
+    for(i=num->nmantissa;i>=0;i--){
+#ifdef NP_I386_ASM
+        __asm__("mul %4\n"
+                : "=a" (num->mantissa[i]), "=d" (overflow)
+                : "0" (num->mantissa[i]), "1" (0), "q" (base)
+                : "eax", "edx");
+#else
+        overflow=0;
+        val=num->mantissa[i];
+        
+        if(val<ULONG_MAX/base){
+            val*=base;
+        }else if(val){
+            ulong tmp=val;
+            ulong old=val;
+            for(j=0; j<base; j++){
+                val+=tmp;
+                if(val<=old)
+                    overflow++;
+                old=val;
+            }
+        }
+        num->mantissa[i]=val;
+#endif
+        if(overflow){
+            if(i==num->nmantissa){
+                if(num->nmantissa==MAX_MANTISSA)
+                    return E_TOKZ_TOOBIG;
+                num->nmantissa++;
+            }
+            num->mantissa[i+1]+=overflow;
+        }
+    }
+    num->mantissa[0]+=v;
+    
+    return 0;
+}
+
+#undef NP_I386_ASM
+
+#endif /* NP_SIMPLE_IMPL */
+
+
+/* */
+
+
+static bool is_end(int c)
+{
+    /* oops... EOF was missing */
+    return (c==EOF || (c!='.' && ispunct(c)) || isspace(c) || iscntrl(c));
+}
+
+
+/* */
+
+
+static int parse_exponent(NPNum *num, Tokenizer *tokz, int c)
+{
+    long exp=0;
+    bool neg=FALSE;
+    int err=0;
+    
+    c=GETCH();
+    
+    if(c=='-' || c=='+'){
+        if(c=='-')
+            neg=TRUE;
+        c=GETCH();
+    }
+    
+    for(; 1; c=GETCH()){
+        if(isdigit(c)){
+            exp*=10;
+            exp+=c-'0';
+        }else if(is_end(c)){
+            UNGETCH(c);
+            break;
+        }else{
+            err=E_TOKZ_NUMFMT;
+        }
+    }
+
+    if(neg)
+        exp*=-1;
+
+#ifndef NP_SIMPLE_IMPL
+    ADD_EXP(num, exp);
+#else
+    num->fval*=pow(num->base, exp);
+#endif    
+    return err;
+}
+
+
+static int parse_number(NPNum *num, Tokenizer *tokz, int c)
+{
+    int base=10;
+    int dm=1;
+    int err=0;
+    int tmp;
+#ifdef NP_SIMPLE_IMPL
+    double divisor=base;
+#endif    
+    
+    if(c=='-' || c=='+'){
+        if(c=='-')
+            num->negative=TRUE;
+        c=GETCH();
+        if(!isdigit(c))
+            err=E_TOKZ_NUMFMT;
+    }
+    
+    if(c=='0'){
+        dm=0;
+        c=GETCH();
+        if(c=='x' || c=='X'){
+            base=16;
+            c=GETCH();
+        }else if(c=='b' || c=='B'){
+            base=2;
+            c=GETCH();
+        }else if('0'<=c && c<='7'){
+            base=8;
+        }else{
+            dm=2;
+        }
+    }
+    
+    num->base=base;
+    
+    for(; 1; c=GETCH()){
+        if((c=='e' || c=='E') && dm!=0){
+            if(dm<2){
+                err=E_TOKZ_NUMFMT;
+                continue;
+            }
+            tmp=parse_exponent(num, tokz, c);
+            if(err==0)
+                err=tmp;
+            break;
+        }
+        
+        if(isxdigit(c)){
+            if('0'<=c && c<='9')
+                c-='0';
+            else if(isupper(c))
+                c-='A'-10;
+            else
+                c-='a'-10;
+            
+            if(c>=base)
+                err=E_TOKZ_NUMFMT;
+
+#ifdef NP_SIMPLE_IMPL
+            if(dm==3){
+                num->fval+=(double)c/divisor;
+                divisor*=base;
+            }else
+#endif
+            {
+                tmp=npnum_mulbase_add(num, base, c);
+                if(err==0)
+                    err=tmp;
+            }
+            
+            if(dm==1)
+                dm=2;
+#ifndef NP_SIMPLE_IMPL            
+            else if(dm==3)
+                ADD_EXP(num, -1);
+#endif            
+            continue;
+        }
+        
+        if(c=='.'){
+            if(dm!=2){
+                err=E_TOKZ_NUMFMT;
+            }
+            dm=3;
+#ifdef NP_SIMPLE_IMPL
+            num->type=NPNUM_FLOAT;
+            divisor=base;
+#endif
+            continue;
+        }
+        
+        if(is_end(c)){
+            UNGETCH(c);
+            break;
+        }
+        
+        err=E_TOKZ_NUMFMT;
+    }
+    
+#ifndef NP_SIMPLE_IMPL            
+    num->type=(num->exponent==0 ? NPNUM_INT : NPNUM_FLOAT);
+#endif
+
+    return err;
+}
diff --git a/libtu/numparser2.h b/libtu/numparser2.h
new file mode 100644 (file)
index 0000000..b43e2ec
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * libtu/numparser2.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#define MAX_MANTISSA 10  /* should be enough for our needs */
+#define ULONG_SIZE (sizeof(ulong)*8)
+
+enum{
+       NPNUM_INT,
+       NPNUM_FLOAT
+};
+
+#ifdef NP_SIMPLE_IMPL
+
+typedef struct _NPNum{
+       int type;
+       int base;
+       bool negative;
+       double fval;
+       ulong ival;
+} NPNum;
+
+#define NUM_INIT {0, 0, 0, 0.0, 0}
+
+static int npnum_mulbase_add(NPNum *num, long base, long v)
+{
+       double iold=num->ival;
+       
+       num->fval=num->fval*base+(double)v;
+       
+       num->ival*=base;
+       
+       if(num->ival<iold)
+               num->type=NPNUM_FLOAT;
+       
+       num->ival+=v;
+       
+       return 0;
+}
+
+#else /* NP_SIMPLE_IMPL */
+
+typedef struct _NPNum{
+       unsigned char nmantissa;
+       int type;
+       int base;
+       bool negative;
+       ulong mantissa[MAX_MANTISSA];
+       long exponent;
+} NPNum;
+
+#define NUM_INIT {0, 0, 0, 0, {0,}, 0}
+
+#define ADD_EXP(NUM, X) (NUM)->exponent+=(X);
+
+#if defined(__GNUG__) && defined(i386) && !defined(NP_NO_I386_ASM)
+ #define NP_I386_ASM
+#endif
+
+static int npnum_mulbase_add(NPNum *num, long base, long v)
+{
+       long i, j;
+       ulong overflow;
+#ifndef NP_I386_ASM
+       ulong val;
+#endif
+       
+       for(i=num->nmantissa;i>=0;i--){
+#ifdef NP_I386_ASM
+               __asm__("mul %4\n"
+                               : "=a" (num->mantissa[i]), "=d" (overflow)
+                               : "0" (num->mantissa[i]), "1" (0), "q" (base)
+                               : "eax", "edx");
+#else
+               overflow=0;
+               val=num->mantissa[i];
+               
+               if(val<ULONG_MAX/base){
+                       val*=base;
+               }else if(val){
+                       ulong tmp=val;
+                       ulong old=val;
+                       for(j=0; j<base; j++){
+                               val+=tmp;
+                               if(val<=old)
+                                       overflow++;
+                               old=val;
+                       }
+               }
+               num->mantissa[i]=val;
+#endif
+               if(overflow){
+                       if(i==num->nmantissa){
+                               if(num->nmantissa==MAX_MANTISSA)
+                                       return E_TOKZ_TOOBIG;
+                               num->nmantissa++;
+                       }
+                       num->mantissa[i+1]+=overflow;
+               }
+       }
+       num->mantissa[0]+=v;
+       
+       return 0;
+}
+
+#undef NP_I386_ASM
+
+#endif /* NP_SIMPLE_IMPL */
+
+
+/* */
+
+
+static bool is_end(int c)
+{
+       /* oops... EOF was missing */
+       return (c==EOF || (c!='.' && ispunct(c)) || isspace(c) || iscntrl(c));
+}
+
+
+/* */
+
+
+static int parse_exponent(NPNum *num, Tokenizer *tokz, int c)
+{
+       long exp=0;
+       bool neg=FALSE;
+       int err=0;
+       
+       c=GETCH();
+       
+       if(c=='-' || c=='+'){
+               if(c=='-')
+                       neg=TRUE;
+               c=GETCH();
+       }
+       
+       for(; 1; c=GETCH()){
+               if(isdigit(c)){
+                       exp*=10;
+                       exp+=c-'0';
+               }else if(is_end(c)){
+                       UNGETCH(c);
+                       break;
+               }else{
+                       err=E_TOKZ_NUMFMT;
+               }
+       }
+
+       if(neg)
+               exp*=-1;
+
+#ifndef NP_SIMPLE_IMPL
+       ADD_EXP(num, exp);
+#else
+       num->fval*=pow(num->base, exp);
+#endif 
+       return err;
+}
+
+
+static int parse_number(NPNum *num, Tokenizer *tokz, int c)
+{
+       int base=10;
+       int dm=1;
+       int err=0;
+       int tmp;
+#ifdef NP_SIMPLE_IMPL
+       double divisor=base;
+#endif 
+       
+       if(c=='-' || c=='+'){
+               if(c=='-')
+                       num->negative=TRUE;
+               c=GETCH();
+               if(!isdigit(c))
+                       err=E_TOKZ_NUMFMT;
+       }
+       
+       if(c=='0'){
+               dm=0;
+               c=GETCH();
+               if(c=='x' || c=='X'){
+                       base=16;
+                       c=GETCH();
+               }else if(c=='b' || c=='B'){
+                       base=2;
+                       c=GETCH();
+               }else if('0'<=c && c<='7'){
+                       base=8;
+               }else{
+                       dm=2;
+               }
+       }
+       
+       num->base=base;
+       
+       for(; 1; c=GETCH()){
+               if((c=='e' || c=='E') && dm!=0){
+                       if(dm<2){
+                               err=E_TOKZ_NUMFMT;
+                               continue;
+                       }
+                       tmp=parse_exponent(num, tokz, c);
+                       if(err==0)
+                               err=tmp;
+                       break;
+               }
+               
+               if(isxdigit(c)){
+                       if('0'<=c && c<='9')
+                               c-='0';
+                       else if(isupper(c))
+                               c-='A'-10;
+                       else
+                               c-='a'-10;
+                       
+                       if(c>=base)
+                               err=E_TOKZ_NUMFMT;
+
+#ifdef NP_SIMPLE_IMPL
+                       if(dm==3){
+                               num->fval+=(double)c/divisor;
+                               divisor*=base;
+                       }else
+#endif
+                       {
+                               tmp=npnum_mulbase_add(num, base, c);
+                               if(err==0)
+                                       err=tmp;
+                       }
+                       
+                       if(dm==1)
+                               dm=2;
+#ifndef NP_SIMPLE_IMPL                 
+                       else if(dm==3)
+                               ADD_EXP(num, -1);
+#endif                 
+                       continue;
+               }
+               
+               if(c=='.'){
+                       if(dm!=2){
+                               err=E_TOKZ_NUMFMT;
+                       }
+                       dm=3;
+#ifdef NP_SIMPLE_IMPL
+                       num->type=NPNUM_FLOAT;
+                       divisor=base;
+#endif
+                       continue;
+               }
+               
+               if(is_end(c)){
+                       UNGETCH(c);
+                       break;
+               }
+               
+               err=E_TOKZ_NUMFMT;
+       }
+       
+#ifndef NP_SIMPLE_IMPL                 
+       num->type=(num->exponent==0 ? NPNUM_INT : NPNUM_FLOAT);
+#endif
+
+       return err;
+}
diff --git a/libtu/obj.c b/libtu/obj.c
new file mode 100644 (file)
index 0000000..0f4910d
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * libtu/obj.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <string.h>
+
+#include "types.h"
+#include "obj.h"
+#include "objp.h"
+#include "misc.h"
+#include "dlist.h"
+
+
+ClassDescr CLASSDESCR(Obj)={"Obj", NULL, 0, NULL, NULL};
+
+
+static void do_watches(Obj *obj, bool call);
+
+
+/*{{{ Destroy */
+
+
+void destroy_obj(Obj *obj)
+{
+    ClassDescr *d;
+    
+    if(OBJ_IS_BEING_DESTROYED(obj))
+        return;
+    
+    obj->flags|=OBJ_DEST;
+    
+    do_watches(obj, TRUE);
+    
+    d=obj->obj_type;
+    
+    while(d!=NULL){
+        if(d->destroy_fn!=NULL){
+            d->destroy_fn(obj);
+            break;
+        }
+        d=d->ancestor;
+    }
+    
+    do_watches(obj, FALSE);
+
+    free(obj);
+}
+
+
+/*}}}*/
+
+
+/*{{{ is/cast */
+
+
+bool obj_is(const Obj *obj, const ClassDescr *descr)
+{
+    ClassDescr *d;
+    
+    if(obj==NULL)
+        return FALSE;
+    
+    d=obj->obj_type;
+    
+    while(d!=NULL){
+        if(d==descr)
+            return TRUE;
+        d=d->ancestor;
+    }
+    return FALSE;
+}
+
+
+bool obj_is_str(const Obj *obj, const char *str)
+{
+    ClassDescr *d;
+    
+    if(obj==NULL || str==NULL)
+        return FALSE;
+    
+    d=obj->obj_type;
+    
+    while(d!=NULL){
+        if(strcmp(d->name, str)==0)
+            return TRUE;
+        d=d->ancestor;
+    }
+    return FALSE;
+}
+
+
+const void *obj_cast(const Obj *obj, const ClassDescr *descr)
+{
+    ClassDescr *d;
+    
+    if(obj==NULL)
+        return NULL;
+    
+    d=obj->obj_type;
+    
+    while(d!=NULL){
+        if(d==descr)
+            return (void*)obj;
+        d=d->ancestor;
+    }
+    return NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic functions */
+
+
+/* This function is called when no handler is found.
+ */
+static void dummy_dyn()
+{
+}
+
+
+static int comp_fun(const void *a, const void *b)
+{
+    void *af=(void*)((DynFunTab*)a)->func;
+    void *bf=(void*)((DynFunTab*)b)->func;
+    
+    return (af<bf ? -1 : (af==bf ? 0 : 1));
+}
+
+            
+DynFun *lookup_dynfun(const Obj *obj, DynFun *func,
+                      bool *funnotfound)
+{
+    ClassDescr *descr;
+    DynFunTab *df;
+    /*DynFunTab dummy={NULL, NULL};*/
+    /*dummy.func=func;*/
+    
+    if(obj==NULL)
+        return NULL;
+    
+    descr=obj->obj_type;
+    
+    for(; descr!=&Obj_classdescr; descr=descr->ancestor){
+        if(descr->funtab==NULL)
+            continue;
+        
+        if(descr->funtab_n==-1){
+            /* Need to sort the table. */
+            descr->funtab_n=0;
+            df=descr->funtab;
+            while(df->func!=NULL){
+                descr->funtab_n++;
+                df++;
+            }
+            
+            if(descr->funtab_n>0){
+                qsort(descr->funtab, descr->funtab_n, sizeof(DynFunTab),
+                      comp_fun);
+            }
+        }
+        
+        /*
+        if(descr->funtab_n==0)
+            continue;
+        
+        df=(DynFunTab*)bsearch(&dummy, descr->funtab, descr->funtab_n,
+                               sizeof(DynFunTab), comp_fun);
+        if(df!=NULL){
+            *funnotfound=FALSE;
+            return df->handler;
+        }
+        */
+        
+        /* Using custom bsearch instead of the one in libc is probably 
+         * faster as the overhead of calling a comparison function would
+         * be significant given that the comparisons are simple and 
+         * funtab_n not that big.
+         */
+        {
+            int min=0, max=descr->funtab_n-1;
+            int ndx;
+            df=descr->funtab;
+            while(max>=min){
+                ndx=(max+min)/2;
+                if((void*)df[ndx].func==(void*)func){
+                    *funnotfound=FALSE;
+                    return df[ndx].handler;
+                }
+                if((void*)df[ndx].func<(void*)func)
+                    min=ndx+1;
+                else
+                    max=ndx-1;
+            }
+        }
+    }
+    
+    *funnotfound=TRUE;
+    return dummy_dyn;
+}
+
+
+bool has_dynfun(const Obj *obj, DynFun *func)
+{
+    bool funnotfound;
+    lookup_dynfun(obj, func, &funnotfound);
+    return !funnotfound;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Watches */
+
+
+bool watch_setup(Watch *watch, Obj *obj, WatchHandler *handler)
+{
+    if(OBJ_IS_BEING_DESTROYED(obj))
+        return FALSE;
+    
+    watch_reset(watch);
+    
+    watch->handler=handler;
+    LINK_ITEM(obj->obj_watches, watch, next, prev);
+    watch->obj=obj;
+    
+    return TRUE;
+}
+
+void do_watch_reset(Watch *watch, bool call)
+{
+    WatchHandler *handler=watch->handler;
+    Obj *obj=watch->obj;
+    
+    watch->handler=NULL;
+    
+    if(obj==NULL)
+        return;
+    
+    UNLINK_ITEM(obj->obj_watches, watch, next, prev);
+    watch->obj=NULL;
+    
+    if(call && handler!=NULL)
+        handler(watch, obj);
+}
+
+
+void watch_reset(Watch *watch)
+{
+    do_watch_reset(watch, FALSE);
+}
+
+
+bool watch_ok(Watch *watch)
+{
+    return watch->obj!=NULL;
+}
+
+
+static void do_watches(Obj *obj, bool call)
+{
+    Watch *watch, *next;
+
+    watch=obj->obj_watches;
+    
+    while(watch!=NULL){
+        next=watch->next;
+        do_watch_reset(watch, call);
+        watch=next;
+    }
+}
+
+
+void watch_call(Obj *obj)
+{
+    do_watches(obj, FALSE);
+}
+
+
+void watch_init(Watch *watch)
+{
+    watch->obj=NULL;
+    watch->next=NULL;
+    watch->prev=NULL;
+    watch->handler=NULL;
+}
+
+
+/*}}}*/
+
diff --git a/libtu/obj.h b/libtu/obj.h
new file mode 100644 (file)
index 0000000..68ee3df
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * libtu/obj.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_OBJ_H
+#define LIBTU_OBJ_H
+
+#include "types.h"
+
+#define CLASSDESCR(TYPE) TYPE##_classdescr
+
+#define OBJ_IS(OBJ, TYPE) obj_is((Obj*)OBJ, &CLASSDESCR(TYPE))
+#define OBJ_CAST(OBJ, TYPE) (TYPE*)obj_cast((Obj*)OBJ, &CLASSDESCR(TYPE))
+
+#define INTRSTRUCT(STRU) \
+    struct STRU##_struct; typedef struct STRU##_struct STRU
+#define DECLSTRUCT(STRU)  \
+    struct STRU##_struct
+
+#define INTRCLASS(OBJ) INTRSTRUCT(OBJ); extern ClassDescr CLASSDESCR(OBJ)
+#define DECLCLASS(OBJ) DECLSTRUCT(OBJ)
+
+INTRSTRUCT(ClassDescr);
+INTRCLASS(Obj);
+INTRSTRUCT(Watch);
+
+extern bool obj_is(const Obj *obj, const ClassDescr *descr);
+extern bool obj_is_str(const Obj *obj, const char *str);
+extern const void *obj_cast(const Obj *obj, const ClassDescr *descr);
+
+extern void destroy_obj(Obj *obj);
+
+DECLCLASS(Obj){
+    ClassDescr *obj_type;
+    Watch *obj_watches;
+    int flags;
+};
+
+#define OBJ_DEST 0x0001
+#define OBJ_EXTL_CACHED 0x0002
+#define OBJ_EXTL_OWNED 0x0004
+
+#define OBJ_IS_BEING_DESTROYED(OBJ) (((Obj*)(OBJ))->flags&OBJ_DEST)
+
+#define DYNFUN
+
+typedef void WatchHandler(Watch *watch, Obj *obj);
+
+#define WATCH_INIT {NULL, NULL, NULL, NULL}
+
+DECLSTRUCT(Watch){
+    Obj *obj;
+    Watch *next, *prev;
+    WatchHandler *handler;
+};
+
+extern bool watch_setup(Watch *watch, Obj *obj,
+                        WatchHandler *handler);
+extern void watch_reset(Watch *watch);
+extern bool watch_ok(Watch *watch);
+extern void watch_init(Watch *watch);
+extern void watch_call(Obj *obj);
+
+#endif /* LIBTU_OBJ_H */
diff --git a/libtu/objlist.c b/libtu/objlist.c
new file mode 100644 (file)
index 0000000..643f3fa
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * libtu/objlist.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include "obj.h"
+#include "types.h"
+#include "objlist.h"
+#include "dlist.h"
+#include "misc.h"
+
+
+static ObjList *reuse_first(ObjList **objlist)
+{
+    ObjList *node=*objlist;
+    
+    if(node!=NULL && node->watch.obj==NULL){
+        UNLINK_ITEM(*objlist, node, next, prev);
+        return node;
+    }
+    
+    return NULL;
+}
+
+
+static ObjList *reuse_last(ObjList **objlist)
+{
+    ObjList *node=*objlist;
+    
+    if(node==NULL)
+        return NULL;
+    
+    node=node->prev;
+    
+    if(node!=NULL && node->watch.obj==NULL){
+        UNLINK_ITEM(*objlist, node, next, prev);
+        return node;
+    }
+
+    return NULL;
+}
+
+
+static ObjList *reuse(ObjList **objlist)
+{
+    ObjList *first=reuse_first(objlist);
+    ObjList *last=reuse_first(objlist);
+    
+    if(first==NULL){
+        return last;
+    }else{
+        if(last!=NULL)
+            free(last);
+        return first;
+    }
+}
+
+
+static void optimise(ObjList **objlist)
+{
+    ObjList *first=reuse_first(objlist);
+    ObjList *last=reuse_first(objlist);
+    
+    if(first!=NULL)
+        free(first);
+    if(last!=NULL)
+        free(last);
+}
+
+
+void watch_handler(Watch *watch, Obj *obj)
+{
+    ObjList *node=(ObjList*)watch;
+    
+    assert(node->prev!=NULL);
+    
+    if(node->next==NULL){
+        /* Last item - can't free */
+    }else if(node->prev->next==NULL){
+        /* First item - can't free cheaply */
+    }else{
+        ObjList *tmp=node->prev;
+        node->next->prev=node->prev;
+        tmp->next=node->next;
+        free(node);
+    }
+}
+
+
+static void free_node(ObjList **objlist, ObjList *node)
+{
+    watch_reset(&(node->watch));
+    UNLINK_ITEM(*objlist, node, next, prev);
+    free(node);
+}
+
+
+static ObjList *mknode(void *obj)
+{
+    ObjList *node;
+    
+    if(obj==NULL)
+        return NULL;
+    
+    node=ALLOC(ObjList);
+    
+    if(node==NULL)
+        return FALSE;
+        
+    watch_init(&(node->watch));
+    
+    if(!watch_setup(&(node->watch), obj, watch_handler)){
+        free(node);
+        return NULL;
+    }
+    
+    return node;
+}
+
+
+static ObjList *objlist_find_node(ObjList *objlist, Obj *obj)
+{
+    ObjList *node=objlist;
+    
+    while(node!=NULL){
+        if(node->watch.obj==obj)
+            break;
+        node=node->next;
+    }
+    
+    return node;
+}
+
+
+bool objlist_insert_last(ObjList **objlist, Obj *obj)
+{
+    ObjList *node=reuse(objlist);
+    
+    if(node==NULL)
+        node=mknode(obj);
+    
+    if(node==NULL)
+        return FALSE;
+    
+    LINK_ITEM_LAST(*objlist, node, next, prev);
+    
+    return TRUE;
+}
+
+
+bool objlist_insert_first(ObjList **objlist, Obj *obj)
+{
+    ObjList *node=reuse(objlist);
+    
+    if(node==NULL)
+        node=mknode(obj);
+    
+    if(node==NULL)
+        return FALSE;
+    
+    LINK_ITEM_FIRST(*objlist, node, next, prev);
+    
+    return TRUE;
+}
+
+
+bool objlist_reinsert_last(ObjList **objlist, Obj *obj)
+{
+    ObjList *node;
+    
+    optimise(objlist);
+    
+    node=objlist_find_node(*objlist, obj);
+    
+    if(node==NULL)
+        return FALSE;
+    
+    UNLINK_ITEM(*objlist, node, next, prev);
+    LINK_ITEM_LAST(*objlist, node, next, prev);
+    
+    return TRUE;
+}
+
+
+bool objlist_reinsert_first(ObjList **objlist, Obj *obj)
+{
+    ObjList *node;
+
+    optimise(objlist);
+    
+    node=objlist_find_node(*objlist, obj);
+    
+    if(node==NULL)
+        return FALSE;
+    
+    UNLINK_ITEM(*objlist, node, next, prev);
+    LINK_ITEM_FIRST(*objlist, node, next, prev);
+    
+    return TRUE;
+}
+
+
+bool objlist_remove(ObjList **objlist, Obj *obj)
+{
+    ObjList *node=objlist_find_node(*objlist, obj);
+    
+    if(node!=NULL)
+        free_node(objlist, node);
+    
+    optimise(objlist);
+    
+    return (node!=NULL);
+}
+
+
+void objlist_clear(ObjList **objlist)
+{
+    while(*objlist!=NULL)
+        free_node(objlist, *objlist);
+}
+
+
+ObjListIterTmp objlist_iter_tmp=NULL;
+
+
+void objlist_iter_init(ObjListIterTmp *state, ObjList *objlist)
+{
+    *state=objlist;
+}
+
+
+Obj *objlist_iter(ObjListIterTmp *state)
+{
+    Obj *obj=NULL;
+    
+    while(obj==NULL && *state!=NULL){
+        obj=(*state)->watch.obj;
+        (*state)=(*state)->next;
+    }
+    
+    return obj;
+}
+
+
+void objlist_iter_rev_init(ObjListIterTmp *state, ObjList *objlist)
+{
+    *state=(objlist==NULL ? NULL : objlist->prev);
+}
+
+
+Obj *objlist_iter_rev(ObjListIterTmp *state)
+{
+    Obj *obj=NULL;
+    
+    while(obj==NULL && *state!=NULL){
+        obj=(*state)->watch.obj;
+        *state=(*state)->prev;
+        if((*state)->next==NULL)
+            *state=NULL;
+    }
+    
+    return obj;
+}
+
+
+bool objlist_empty(ObjList *objlist)
+{
+    ObjListIterTmp tmp;
+    Obj *obj;
+    
+    FOR_ALL_ON_OBJLIST(Obj*, obj, objlist, tmp){
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+Obj *objlist_take_first(ObjList **objlist)
+{
+    ObjList *node;
+    Obj*obj;
+    
+    optimise(objlist);
+    
+    node=*objlist;
+    
+    if(node==NULL)
+        return NULL;
+    
+    obj=node->watch.obj;
+    
+    assert(obj!=NULL);
+    
+    free_node(objlist, node);
+    
+    return obj;
+}
+    
+        
+Obj *objlist_take_last(ObjList **objlist)
+{
+    ObjList *node;
+    Obj*obj;
+    
+    optimise(objlist);
+    
+    node=*objlist;
+    
+    if(node==NULL)
+        return NULL;
+    
+    node=node->prev;
+    
+    obj=node->watch.obj;
+
+    assert(obj!=NULL);
+    
+    free_node(objlist, node);
+    
+    return obj;
+}
diff --git a/libtu/objlist.h b/libtu/objlist.h
new file mode 100644 (file)
index 0000000..678706f
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * libtu/objlist.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005.
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_OBJLIST_H
+#define LIBTU_OBJLIST_H
+
+#include "types.h"
+#include "iterable.h"
+#include "obj.h"
+
+
+INTRSTRUCT(ObjList);
+
+
+DECLSTRUCT(ObjList){
+    Watch watch; /* Must be kept at head of structure */
+    ObjList *next, *prev;
+    ObjList **list;
+};
+
+
+typedef ObjList* ObjListIterTmp;
+
+#define OBJLIST_FIRST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->watch.obj)
+#define OBJLIST_LAST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->prev->watch.obj)
+#define OBJLIST_EMPTY(LIST) objlist_empty(LIST)
+
+#define FOR_ALL_ON_OBJLIST(TYPE, VAR, LL, TMP) \
+    FOR_ALL_ITER(objlist_iter_init, (TYPE)objlist_iter, VAR, LL, &(TMP))
+
+#define FOR_ALL_ON_OBJLIST_REV(TYPE, VAR, LL, TMP)        \
+    FOR_ALL_ITER(objlist_iter_rev_init,                   \
+                 (TYPE)objlist_iter_rev, VAR, LL, &(TMP))
+
+#define FOR_ALL_ON_OBJLIST_UNSAFE(TYPE, VAR, LL) \
+    FOR_ALL_ON_OBJLIST(TYPE, VAR, LL, objlist_iter_tmp)
+
+extern ObjListIterTmp objlist_iter_tmp;
+
+extern bool objlist_insert_last(ObjList **objlist, Obj *obj);
+extern bool objlist_insert_first(ObjList **objlist, Obj *obj);
+extern bool objlist_reinsert_last(ObjList **objlist, Obj *obj);
+extern bool objlist_reinsert_first(ObjList **objlist, Obj *obj);
+extern bool objlist_remove(ObjList **objlist, Obj *obj);
+extern void objlist_clear(ObjList **objlist);
+extern void objlist_iter_init(ObjListIterTmp *state, ObjList *objlist);
+extern Obj *objlist_iter(ObjListIterTmp *state);
+extern void objlist_iter_rev_init(ObjListIterTmp *state, ObjList *objlist);
+extern Obj *objlist_iter_rev(ObjListIterTmp *state);
+extern bool objlist_empty(ObjList *objlist);
+extern Obj *objlist_take_first(ObjList **objlist);
+extern Obj *objlist_take_last(ObjList **objlist);
+
+#endif /* LIBTU_OBJLIST_H */
diff --git a/libtu/objp.h b/libtu/objp.h
new file mode 100644 (file)
index 0000000..54a7b19
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * libtu/objp.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_OBJP_H
+#define LIBTU_OBJP_H
+
+#include "types.h"
+#include "obj.h"
+
+typedef void DynFun();
+
+INTRSTRUCT(DynFunTab);
+
+DECLSTRUCT(DynFunTab){
+    DynFun *func, *handler;
+};
+
+DECLSTRUCT(ClassDescr){
+    const char *name;
+    ClassDescr *ancestor;
+    int funtab_n;
+    DynFunTab *funtab;
+    void (*destroy_fn)();
+};
+
+#define OBJ_TYPESTR(OBJ) ((OBJ) ? ((Obj*)OBJ)->obj_type->name : NULL)
+
+#define IMPLCLASS(CLS, ANCESTOR, DFN, DYN)                         \
+        ClassDescr CLASSDESCR(CLS)={                              \
+            #CLS, &CLASSDESCR(ANCESTOR), -1, DYN, (void (*)())DFN}
+
+#define OBJ_INIT(O, TYPE) {((Obj*)(O))->obj_type=&CLASSDESCR(TYPE); \
+    ((Obj*)(O))->obj_watches=NULL; ((Obj*)(O))->flags=0;}
+
+#define CREATEOBJ_IMPL(OBJ, LOWOBJ, INIT_ARGS)                     \
+    OBJ *p;  p=ALLOC(OBJ); if(p==NULL){ warn_err(); return NULL; } \
+    OBJ_INIT(p, OBJ);                                             \
+    if(!LOWOBJ ## _init INIT_ARGS) { free((void*)p); return NULL; } return p
+
+#define SIMPLECREATEOBJ_IMPL(OBJ, LOWOBJ)                          \
+    OBJ *p;  p=ALLOC(OBJ); if(p==NULL){ warn_err(); return NULL; } \
+    OBJ_INIT(p, OBJ);                                             \
+    return p;
+
+#define END_DYNFUNTAB {NULL, NULL}
+
+extern DynFun *lookup_dynfun(const Obj *obj, DynFun *func,
+                             bool *funnotfound);
+extern bool has_dynfun(const Obj *obj, DynFun *func);
+
+#define CALL_DYN(FUNC, OBJ, ARGS)                                \
+    bool funnotfound;                                            \
+    lookup_dynfun((Obj*)OBJ, (DynFun*)FUNC, &funnotfound) ARGS;
+
+#define CALL_DYN_RET(RETV, RET, FUNC, OBJ, ARGS)                 \
+    typedef RET ThisDynFun();                                    \
+    bool funnotfound;                                            \
+    ThisDynFun *funtmp;                                          \
+    funtmp=(ThisDynFun*)lookup_dynfun((Obj*)OBJ, (DynFun*)FUNC, \
+                                      &funnotfound);             \
+    if(!funnotfound)                                             \
+        RETV=funtmp ARGS;
+
+#define HAS_DYN(OBJ, FUNC) has_dynfun((Obj*)OBJ, (DynFun*)FUNC)
+
+#endif /* LIBTU_OBJP_H */
diff --git a/libtu/optparser.c b/libtu/optparser.c
new file mode 100644 (file)
index 0000000..fc65523
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * libtu/optparser.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2004.
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "util.h"
+#include "misc.h"
+#include "optparser.h"
+#include "output.h"
+#include "private.h"
+
+
+#define O_ARGS(o)       (o->flags&OPT_OPT_ARG)
+#define O_ARG(o)        (o->flasg&OPT_ARG)
+#define O_OPT_ARG(o)    (O_ARGS(o)==OPT_OPT_ARG)
+#define O_ID(o)         (o->optid)
+
+
+static const OptParserOpt *o_opts=NULL;
+static char *const *o_current=NULL;
+static int o_left=0;
+static const char* o_chain_ptr=NULL;
+static int o_args_left=0;
+static const char*o_tmp=NULL;
+static int o_error=0;
+static int o_mode=OPTP_CHAIN;
+
+
+/* */
+
+
+void optparser_init(int argc, char *const argv[], int mode,
+                    const OptParserOpt *opts)
+{
+    o_mode=mode;
+    o_opts=opts;
+    o_current=argv+1;
+    o_left=argc-1;
+    o_chain_ptr=NULL;
+    o_args_left=0;
+    o_tmp=NULL;
+}
+
+
+/* */
+
+
+static const OptParserOpt *find_chain_opt(char p, const OptParserOpt *o)
+{
+    for(;O_ID(o);o++){
+        if((O_ID(o)&~OPT_ID_RESERVED_FLAG)==p)
+            return o;
+    }
+    return NULL;
+}
+
+
+static bool is_option(const char *p)
+{
+     if(p==NULL)
+          return FALSE;
+     if(*p++!='-')
+          return FALSE;
+     if(*p++!='-')
+          return FALSE;
+     if(*p=='\0')
+          return FALSE;
+     return TRUE;
+}
+    
+
+/* */
+
+enum{
+    SHORT, MIDLONG, LONG
+};
+
+
+int optparser_get_opt()
+{
+#define RET(X) return o_tmp=p, o_error=X
+    const char *p, *p2=NULL;
+    bool dash=TRUE;
+    int type=SHORT;
+    const OptParserOpt *o;
+    int l;
+
+    while(o_args_left)
+        optparser_get_arg();
+
+    o_tmp=NULL;
+
+    /* Are we doing a chain (i.e. opt. of style 'tar xzf')? */
+    if(o_chain_ptr!=NULL){
+        p=o_chain_ptr++;
+        if(!*o_chain_ptr)
+            o_chain_ptr=NULL;
+
+        o=find_chain_opt(*p, o_opts);
+        
+        if(o==NULL)
+            RET(E_OPT_INVALID_CHAIN_OPTION);
+
+        goto do_arg;
+    }
+    
+    if(o_left<1)
+        return OPT_ID_END;
+
+    o_left--;
+    p=*o_current++;
+    
+    if(*p!='-'){
+        dash=FALSE;
+        if(o_mode!=OPTP_NO_DASH)
+            RET(OPT_ID_ARGUMENT);
+        p2=p;
+    }else if(*(p+1)=='-'){
+        /* --foo */
+        if(*(p+2)=='\0'){
+            /* -- arguments */
+            o_args_left=o_left;
+            RET(OPT_ID_ARGUMENT);
+        }
+        type=LONG;
+        p2=p+2;
+    }else{
+        /* -foo */
+        if(*(p+1)=='\0'){
+            /* - */
+            o_args_left=1;
+            RET(OPT_ID_ARGUMENT);
+        }
+        if(*(p+2)!='\0' && o_mode==OPTP_MIDLONG)
+            type=MIDLONG;
+    
+        p2=p+1;
+    }
+
+    o=o_opts;
+
+    for(; O_ID(o); o++){
+        if(type==LONG){
+            /* Do long option (--foo=bar) */
+            if(o->longopt==NULL)
+                continue;
+            l=strlen(o->longopt);
+            if(strncmp(p2, o->longopt, l)!=0)
+                continue;
+            
+            if(p2[l]=='\0'){
+                if(O_ARGS(o)==OPT_ARG)
+                     RET(E_OPT_MISSING_ARGUMENT);
+                return O_ID(o);
+            }else if(p2[l]=='='){
+                if(!O_ARGS(o))
+                     RET(E_OPT_UNEXPECTED_ARGUMENT);
+                if(p2[l+1]=='\0')
+                     RET(E_OPT_MISSING_ARGUMENT);
+                o_tmp=p2+l+1;
+                o_args_left=1;
+                return O_ID(o);
+            }
+            continue;
+        }else if(type==MIDLONG){
+            if(o->longopt==NULL)
+                continue;
+            
+            if(strcmp(p2, o->longopt)!=0)
+                continue;
+        }else{ /* type==SHORT */
+            if(*p2!=(O_ID(o)&~OPT_ID_RESERVED_FLAG))
+                continue;
+               
+            if(*(p2+1)!='\0'){
+                if(o_mode==OPTP_CHAIN || o_mode==OPTP_NO_DASH){
+                    /*valid_chain(p2+1, o_opts)*/
+                    o_chain_ptr=p2+1;
+                    p++;
+                }else if(o_mode==OPTP_IMMEDIATE){
+                    if(!O_ARGS(o)){
+                        if(*(p2+1)!='\0')
+                            RET(E_OPT_UNEXPECTED_ARGUMENT);
+                    }else{
+                        if(*(p2+1)=='\0')
+                            RET(E_OPT_MISSING_ARGUMENT);
+                        o_tmp=p2+1;
+                        o_args_left=1;
+                    }
+                    return O_ID(o);
+                }else{
+                    RET(E_OPT_SYNTAX_ERROR);
+                }
+            }
+        }
+        
+    do_arg:
+        
+        if(!O_ARGS(o))
+            return O_ID(o);
+        
+        if(!o_left || is_option(*o_current)){
+            if(O_ARGS(o)==OPT_OPT_ARG)
+                return O_ID(o);
+            RET(E_OPT_MISSING_ARGUMENT);
+        }
+
+        o_args_left=1;
+        return O_ID(o);
+    }
+    
+    if(dash)
+        RET(E_OPT_INVALID_OPTION);
+    
+    RET(OPT_ID_ARGUMENT);
+#undef RET
+}
+
+
+/* */
+
+
+const char* optparser_get_arg()
+{
+    const char *p;
+    
+    if(o_tmp!=NULL){
+        /* If o_args_left==0, then were returning an invalid option
+         * otherwise an immediate argument (e.g. -funsigned-char
+         * where '-f' is the option and 'unsigned-char' the argument)
+         */
+        if(o_args_left>0)
+            o_args_left--;
+        p=o_tmp;
+        o_tmp=NULL;
+        return p;
+    }
+        
+    if(o_args_left<1 || o_left<1)
+        return NULL;
+
+    o_left--;
+    o_args_left--;
+    return *o_current++;
+}
+
+
+/* */
+
+static void warn_arg(const char *e)
+{
+    const char *p=optparser_get_arg();
+    
+    if(p==NULL)
+        warn("%s (null)", e);
+    else
+        warn("%s \'%s\'", e, p);
+}
+
+
+static void warn_opt(const char *e)
+{
+    if(o_tmp!=NULL && o_chain_ptr!=NULL)
+        warn("%s \'-%c\'", e, *o_tmp);
+    else
+        warn_arg(e);
+}
+
+
+void optparser_print_error()
+{
+    switch(o_error){
+    case E_OPT_INVALID_OPTION:
+    case E_OPT_INVALID_CHAIN_OPTION:
+        warn_opt(TR("Invalid option"));
+        break;
+
+    case E_OPT_SYNTAX_ERROR:
+        warn_arg(TR("Syntax error while parsing"));
+        break;
+                 
+    case E_OPT_MISSING_ARGUMENT:
+        warn_opt(TR("Missing argument to"));
+        break;
+
+    case E_OPT_UNEXPECTED_ARGUMENT:
+        warn_opt(TR("No argument expected:"));
+        break;
+        
+    case OPT_ID_ARGUMENT:
+        warn(TR("Unexpected argument"));
+        break;
+
+    default:
+        warn(TR("(unknown error)"));
+    }
+    
+    o_tmp=NULL;
+    o_error=0;
+}
+
+
+/* */
+
+
+static uint opt_w(const OptParserOpt *opt, bool midlong)
+{
+    uint w=0;
+    
+    if((opt->optid&OPT_ID_NOSHORT_FLAG)==0){
+        w+=2; /* "-o" */
+        if(opt->longopt!=NULL)
+            w+=2; /* ", " */
+    }
+    
+    if(opt->longopt!=NULL)
+        w+=strlen(opt->longopt)+(midlong ? 1 : 2);
+    
+    if(O_ARGS(opt)){
+        if(opt->argname==NULL)
+            w+=4;
+        else
+            w+=1+strlen(opt->argname); /* "=ARG" or " ARG" */
+        
+        if(O_OPT_ARG(opt))
+            w+=2; /* [ARG] */
+    }
+    
+    return w;
+}
+
+
+#define TERM_W 80
+#define OFF1 2
+#define OFF2 2
+#define SPACER1 "  "
+#define SPACER2 "  "
+
+static void print_opt(const OptParserOpt *opt, bool midlong,
+                      uint maxw, uint tw)
+{
+    FILE *f=stdout;
+    const char *p, *p2, *p3;
+    uint w=0;
+    
+    fprintf(f, SPACER1);
+    
+    /* short opt */
+    
+    if((O_ID(opt)&OPT_ID_NOSHORT_FLAG)==0){
+        fprintf(f, "-%c", O_ID(opt)&~OPT_ID_RESERVED_FLAG);
+        w+=2;
+        
+        if(opt->longopt!=NULL){
+            fprintf(f, ", ");
+            w+=2;
+        }
+    }
+    
+    /* long opt */
+    
+    if(opt->longopt!=NULL){
+        if(midlong){
+            w++;
+            fprintf(f, "-%s", opt->longopt);
+        }else{
+            w+=2;
+            fprintf(f, "--%s", opt->longopt);
+        }
+        w+=strlen(opt->longopt);
+    }
+    
+    /* arg */
+    
+    if(O_ARGS(opt)){
+        w++;
+        if(opt->longopt!=NULL && !midlong)
+            putc('=', f);
+        else
+            putc(' ', f);
+        
+        if(O_OPT_ARG(opt)){
+            w+=2;
+            putc('[', f);
+        }
+        
+        if(opt->argname!=NULL){
+            fprintf(f, "%s", opt->argname);
+            w+=strlen(opt->argname);
+        }else{
+            w+=3;
+            fprintf(f, "ARG");
+        }
+
+        if(O_OPT_ARG(opt))
+            putc(']', f);
+    }
+    
+    while(w++<maxw)
+        putc(' ', f);
+    
+    /* descr */
+    
+    p=p2=opt->descr;
+    
+    if(p==NULL){
+        putc('\n', f);
+        return;
+    }
+    
+    fprintf(f, SPACER2);
+
+    maxw+=OFF1+OFF2;
+    tw-=maxw;
+    
+    while(strlen(p)>tw){
+        p3=p2=p+tw-2;
+        
+        while(*p2!=' ' && p2!=p)
+            p2--;
+        
+        while(*p3!=' ' && *p3!='\0')
+            p3++;
+        
+        if((uint)(p3-p2)>tw){
+            /* long word - just wrap */
+            p2=p+tw-2;
+        }
+            
+        writef(f, p, p2-p);
+        if(*p2==' ')
+            putc('\n', f);
+        else
+            fprintf(f, "\\\n");
+        
+        p=p2+1;
+        
+        w=maxw;
+        while(w--)
+            putc(' ', f);
+    }
+    
+    fprintf(f, "%s\n", p);
+}
+
+                      
+void optparser_printhelp(int mode, const OptParserOpt *opts)
+{
+    uint w, maxw=0;
+    const OptParserOpt *o;
+    bool midlong=mode&OPTP_MIDLONG;
+    
+    o=opts;
+    for(; O_ID(o); o++){
+        w=opt_w(o, midlong);
+        if(w>maxw)
+            maxw=w;
+    }
+    
+    o=opts;
+    
+    for(; O_ID(o); o++)
+        print_opt(o, midlong, maxw, TERM_W);
+}
diff --git a/libtu/optparser.h b/libtu/optparser.h
new file mode 100644 (file)
index 0000000..3b6ed5f
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * libtu/optparser.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_OPTPARSER_H
+#define LIBTU_OPTPARSER_H
+
+#include "types.h"
+
+
+#define OPT_ID_NOSHORT_FLAG 0x10000
+#define OPT_ID_RESERVED_FLAG 0x20000
+
+#define OPT_ID(X)            ((X)|OPT_ID_NOSHORT_FLAG)
+#define OPT_ID_RESERVED(X)    ((X)|OPT_ID_RESERVED_FLAG)
+
+/* OPTP_CHAIN is the normal behavior, i.e. single-letter options can be
+ *        "chained" together: 'lr -lR'. Use for normal command line programs.
+ * OPTP_MIDLONG allows '-display foo' -like args but disables chaining
+ *         of single-letter options. X programs should probably use this.
+ * OPTP_IMMEDIATE allows immediate arguments (-I/usr/include) (and disables
+ *        chaining and midlong options).
+ * OPTP_NO_DASH is the same as OPTP_CHAIN but allows the dash to be omitted
+ *         for 'tar xzf foo' -like behavior.
+ * Long '--foo=bar' options are supported in all of the modes.
+ */
+
+enum{
+    OPTP_CHAIN=0,
+    OPTP_DEFAULT=0,
+    OPTP_MIDLONG=1,
+    OPTP_IMMEDIATE=2,
+    OPTP_NO_DASH=3
+};
+    
+enum{
+    OPT_ARG=1,                    /* option has an argument                    */
+    OPT_OPT_ARG=3                /* option may have an argument                */
+};
+
+
+#define END_OPTPARSEROPTS {0, NULL, 0, NULL, NULL}
+
+typedef struct _OptParserOpt{
+    int optid;
+    const char *longopt;
+    int    flags;
+    const char *argname;
+    const char *descr;
+} OptParserOpt;
+
+enum{
+    OPT_ID_END=0,
+    OPT_ID_ARGUMENT=1,
+
+    E_OPT_INVALID_OPTION=-1,
+    E_OPT_INVALID_CHAIN_OPTION=-2,
+    E_OPT_SYNTAX_ERROR=-3,
+    E_OPT_MISSING_ARGUMENT=-4,
+    E_OPT_UNEXPECTED_ARGUMENT=-5
+};
+
+
+extern void optparser_init(int argc, char *const argv[], int mode, 
+                           const OptParserOpt *opts);
+
+extern void optparser_printhelp(int mode, const OptParserOpt *opts);
+
+extern int  optparser_get_opt();
+extern const char* optparser_get_arg();
+extern void optparser_print_error();
+
+#endif /* LIBTU_OPTPARSER_H */
diff --git a/libtu/output.c b/libtu/output.c
new file mode 100644 (file)
index 0000000..6c70186
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * libtu/output.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#if defined(HAS_SYSTEM_ASPRINTF)
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <strings.h>
+#include <string.h>
+
+#include "misc.h"
+#include "output.h"
+#include "util.h"
+#include "private.h"
+
+#if !defined(HAS_SYSTEM_ASPRINTF)
+#include "snprintf_2.2/snprintf.h"
+#endif
+
+
+static void default_warn_handler(const char *message);
+
+
+static bool verbose_mode=FALSE;
+static int verbose_indent_lvl=0;
+static bool progname_enable=TRUE;
+static WarnHandler *current_warn_handler=default_warn_handler;
+
+#define INDENTATOR_LENGTH 4
+
+static char indentator[]={' ', ' ', ' ', ' '};
+
+static void do_dispatch_message(const char *message);
+
+
+void verbose(const char *p, ...)
+{
+    va_list args;
+    
+    va_start(args, p);
+    
+    verbose_v(p, args);
+    
+    va_end(args);
+}
+           
+
+void verbose_v(const char *p, va_list args)
+{
+    int i;
+    
+    if(verbose_mode){
+        for(i=0; i<verbose_indent_lvl; i++)
+            writef(stdout, indentator, INDENTATOR_LENGTH);
+        
+        vprintf(p, args);
+        fflush(stdout);
+    }
+}
+
+
+void verbose_enable(bool enable)
+{
+    verbose_mode=enable;
+}
+
+
+int verbose_indent(int depth)
+{
+    int old=verbose_indent_lvl;
+    
+    if(depth>=0)
+        verbose_indent_lvl=depth;
+    
+    return old;
+}
+        
+
+void warn_progname_enable(bool enable)
+{
+    progname_enable=enable;
+}
+
+
+static void put_prog_name()
+{
+    const char*progname;
+    
+    if(!progname_enable)
+        return;
+    
+    progname=prog_execname();
+    
+    if(progname==NULL)
+        return;
+    
+    fprintf(stderr, "%s: ", (char*)progname);
+}
+
+/* warn
+ */
+
+
+static void fallback_warn()
+{
+    put_prog_name();
+    fprintf(stderr, TR("Oops. Error string compilation failed: %s"),
+            strerror(errno));
+}
+    
+    
+#define CALL_V(NAME, ARGS) \
+    do { va_list args; va_start(args, p); NAME ARGS; va_end(args); } while(0)
+
+#define DO_DISPATCH(NAME, ARGS) \
+    do{ \
+        char *msg; \
+        if((msg=NAME ARGS)!=NULL){ \
+            do_dispatch_message(msg); \
+            free(msg);\
+        }else{ \
+            fallback_warn(); \
+        } \
+    }while(0)
+
+
+void libtu_asprintf(char **ret, const char *p, ...)
+{
+    *ret=NULL;
+    CALL_V(vasprintf, (ret, p, args));
+    if(*ret==NULL)
+        warn_err();
+}
+
+
+void libtu_vasprintf(char **ret, const char *p, va_list args)
+{
+    *ret=NULL;
+    vasprintf(ret, p, args);
+    if(*ret==NULL)
+        warn_err();
+}
+
+
+void warn(const char *p, ...)
+{
+    CALL_V(warn_v, (p, args));
+}
+
+
+void warn_obj(const char *obj, const char *p, ...)
+{
+    CALL_V(warn_obj_v, (obj, p, args));
+}
+
+
+void warn_obj_line(const char *obj, int line, const char *p, ...)
+{
+    CALL_V(warn_obj_line_v, (obj, line, p, args));
+}
+
+
+void warn_obj_v(const char *obj, const char *p, va_list args)
+{
+    warn_obj_line_v(obj, -1, p, args);
+}
+
+
+void warn_v(const char *p, va_list args)
+{
+    DO_DISPATCH(errmsg_v, (p, args));
+}
+
+
+void warn_obj_line_v(const char *obj, int line, const char *p, va_list args)
+{
+    DO_DISPATCH(errmsg_obj_line_v, (obj, line, p, args));
+}
+
+
+void warn_err()
+{
+    DO_DISPATCH(errmsg_err, ());
+}
+
+
+void warn_err_obj(const char *obj)
+{
+    DO_DISPATCH(errmsg_err_obj, (obj));
+}
+
+void warn_err_obj_line(const char *obj, int line)
+{
+    DO_DISPATCH(errmsg_err_obj_line, (obj, line));
+}
+
+
+/* errmsg
+ */
+
+#define CALL_V_RET(NAME, ARGS) \
+    char *ret; va_list args; va_start(args, p); ret=NAME ARGS; \
+    va_end(args); return ret;
+
+
+char* errmsg(const char *p, ...)
+{
+    CALL_V_RET(errmsg_v, (p, args));
+}
+
+
+char *errmsg_obj(const char *obj, const char *p, ...)
+{
+    CALL_V_RET(errmsg_obj_v, (obj, p, args));
+}
+
+
+char *errmsg_obj_line(const char *obj, int line, const char *p, ...)
+{
+    CALL_V_RET(errmsg_obj_line_v, (obj, line, p, args));
+}
+
+
+char* errmsg_obj_v(const char *obj, const char *p, va_list args)
+{
+    return errmsg_obj_line_v(obj, -1, p, args);
+}
+
+
+char *errmsg_v(const char *p, va_list args)
+{
+    char *res;
+    libtu_vasprintf(&res, p, args);
+    return res;
+}
+
+
+char *errmsg_obj_line_v(const char *obj, int line, const char *p, va_list args)
+{
+    char *res1=NULL, *res2, *res3;
+    
+    if(obj!=NULL){
+        if(line>0)
+            libtu_asprintf(&res1, "%s:%d: ", obj, line);
+        else        
+            libtu_asprintf(&res1, "%s: ", obj);
+    }else{
+        if(line>0)
+            libtu_asprintf(&res1, "%d: ", line);
+    }
+    libtu_vasprintf(&res2, p, args);
+    if(res1!=NULL){
+        if(res2==NULL)
+            return NULL;
+        res3=scat(res1, res2);
+        free(res1);
+        free(res2);
+        return res3;
+    }
+    return res2;
+}
+
+
+char *errmsg_err()
+{
+    char *res;
+    libtu_asprintf(&res, "%s\n", strerror(errno));
+    return res;
+}
+
+
+char *errmsg_err_obj(const char *obj)
+{
+    char *res;
+    if(obj!=NULL)
+        libtu_asprintf(&res, "%s: %s\n", obj, strerror(errno));
+    else
+        libtu_asprintf(&res, "%s\n", strerror(errno));
+    return res;
+}
+
+
+char *errmsg_err_obj_line(const char *obj, int line)
+{
+    char *res;
+    if(obj!=NULL){
+        if(line>0)
+            libtu_asprintf(&res, "%s:%d: %s\n", obj, line, strerror(errno));
+        else
+            libtu_asprintf(&res, "%s: %s\n", obj, strerror(errno));
+    }else{
+        if(line>0)
+            libtu_asprintf(&res, "%d: %s\n", line, strerror(errno));
+        else
+            libtu_asprintf(&res, "%s\n", strerror(errno));
+    }
+    return res;
+}
+
+
+/* die
+ */
+
+
+void die(const char *p, ...)
+{
+    set_warn_handler(NULL);
+    CALL_V(die_v, (p, args));
+}
+
+
+void die_v(const char *p, va_list args)
+{
+    set_warn_handler(NULL);
+    warn_v(p, args);
+    exit(EXIT_FAILURE);
+}
+
+
+void die_obj(const char *obj, const char *p, ...)
+{
+    set_warn_handler(NULL);
+    CALL_V(die_obj_v, (obj, p, args));
+}
+
+
+void die_obj_line(const char *obj, int line, const char *p, ...)
+{
+    set_warn_handler(NULL);
+    CALL_V(die_obj_line_v, (obj, line, p, args));
+}
+
+
+void die_obj_v(const char *obj, const char *p, va_list args)
+{
+    set_warn_handler(NULL);
+    warn_obj_v(obj, p, args);
+    exit(EXIT_FAILURE);
+}
+
+
+void die_obj_line_v(const char *obj, int line, const char *p, va_list args)
+{
+    set_warn_handler(NULL);
+    warn_obj_line_v(obj, line, p, args);
+    exit(EXIT_FAILURE);
+}
+
+
+void die_err()
+{
+    set_warn_handler(NULL);
+    warn_err();
+    exit(EXIT_FAILURE);
+}
+
+
+void die_err_obj(const char *obj)
+{
+    set_warn_handler(NULL);
+    warn_err_obj(obj);
+    exit(EXIT_FAILURE);
+}
+
+
+void die_err_obj_line(const char *obj, int line)
+{
+    set_warn_handler(NULL);
+    warn_err_obj_line(obj, line);
+    exit(EXIT_FAILURE);
+}
+
+
+static void default_warn_handler(const char *message)
+{
+    put_prog_name();
+    fprintf(stderr, "%s", message);
+    putc('\n', stderr);
+}
+
+
+static void do_dispatch_message(const char *message)
+{
+    if(current_warn_handler!=NULL)
+        current_warn_handler(message);
+    else
+        default_warn_handler(message);
+}
+
+
+WarnHandler *set_warn_handler(WarnHandler *handler)
+{
+    WarnHandler *old=current_warn_handler;
+    current_warn_handler=(handler!=NULL ? handler : default_warn_handler);
+    return old;
+}
diff --git a/libtu/output.h b/libtu/output.h
new file mode 100644 (file)
index 0000000..8e75de9
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * libtu/output.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_OUTPUT_H
+#define LIBTU_OUTPUT_H
+
+#include <stdarg.h>
+
+#include "types.h"
+
+#if __STDC_VERSION__ >= 199901L
+#define WARN_FUNC(...) warn_obj(__func__, __VA_ARGS__)
+#define WARN_ERR_FUNC() warn_err_obj(__func__)
+#else
+#define WARN_FUNC warn
+#define WARN_ERR_FUNC() warn_err()
+#endif
+
+typedef void WarnHandler(const char *);
+extern WarnHandler *set_warn_handler(WarnHandler *handler);
+
+extern void verbose(const char *p, ...);
+extern void verbose_v(const char *p, va_list args);
+extern void verbose_enable(bool enable);
+extern int verbose_indent(int depth);
+
+extern void warn_progname_enable(bool enable);
+
+extern void die(const char *p, ...);
+extern void die_v(const char *p, va_list args);
+
+extern void die_obj(const char *obj, const char *p, ...);
+extern void die_obj_v(const char *obj, const char *p, va_list args);
+extern void die_obj_line(const char *obj, int line, const char *p, ...);
+extern void die_obj_line_v(const char *obj, int line, const char *p, va_list args);
+
+extern void die_err();
+extern void die_err_obj(const char *obj);
+extern void die_err_obj_line(const char *obj, int line);
+
+
+extern void warn(const char *p, ...);
+extern void warn_v(const char *p, va_list args);
+
+extern void warn_obj(const char *obj, const char *p, ...);
+extern void warn_obj_v(const char *obj, const char *p, va_list args);
+extern void warn_obj_line(const char *obj, int line, const char *p, ...);
+extern void warn_obj_line_v(const char *obj, int line, const char *p, va_list args);
+
+extern void warn_err();
+extern void warn_err_obj(const char *obj);
+extern void warn_err_obj_line(const char *obj, int line);
+
+
+extern char *errmsg(const char *p, ...);
+extern char *errmsg_v(const char *p, va_list args);
+
+extern char *errmsg_obj(const char *obj, const char *p, ...);
+extern char *errmsg_obj_v(const char *obj, const char *p, va_list args);
+extern char *errmsg_obj_line(const char *obj, int line, const char *p, ...);
+extern char *errmsg_obj_line_v(const char *obj, int line, const char *p, va_list args);
+
+extern char *errmsg_err();
+extern char *errmsg_err_obj(const char *obj);
+extern char *errmsg_err_obj_line(const char *obj, int line);
+
+extern void libtu_asprintf(char **ret, const char *fmt, ...);
+extern void libtu_vasprintf(char **ret, const char *fmt, va_list args);
+
+#endif /* LIBTU_OUTPUT_H */
diff --git a/libtu/parser.c b/libtu/parser.c
new file mode 100644 (file)
index 0000000..3a29dd3
--- /dev/null
@@ -0,0 +1,717 @@
+/*
+ * libtu/parser.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include "parser.h"
+#include "misc.h"
+#include "output.h"
+
+#define MAX_TOKENS     256
+#define MAX_NEST    256
+
+
+enum{
+    P_NONE=1,
+    P_EOF,
+    P_STMT,
+    P_STMT_NS,
+    P_STMT_SECT,
+    P_BEG_SECT,
+    P_END_SECT
+};
+
+
+/* */
+
+
+static bool opt_include(Tokenizer *tokz, int n, Token *toks);
+
+
+static ConfOpt common_opts[]={
+    {"include", "s", opt_include, NULL},
+    {NULL, NULL, NULL, NULL}
+};
+
+
+/* */
+
+
+static int read_statement(Tokenizer *tokz, Token *tokens, int *ntok_ret)
+{
+    int ntokens=0;
+    Token *tok=NULL;
+    int had_comma=0; /* 0 - no, 1 - yes, 2 - not had, not expected */
+    int retval=0;
+    int e=0;
+
+    while(1){
+        tok=&tokens[ntokens];
+        
+        if(!tokz_get_token(tokz, tok)){
+            e=1;
+            continue;
+        }
+
+        if(ntokens==MAX_TOKENS-1){
+            e=E_TOKZ_TOKEN_LIMIT;
+            tokz_warn_error(tokz, tok->line, e);
+            if(!(tokz->flags&TOKZ_ERROR_TOLERANT))
+                break;
+        }else{
+            ntokens++;
+        }
+        
+        if(!TOK_IS_OP(tok)){
+            if(ntokens==1 && !had_comma){
+                /* first token */
+                had_comma=2;
+            }else{
+                if(had_comma==0)
+                    goto syntax;
+            
+                had_comma=0;
+            }
+            continue;
+        }
+        
+        /* It is an operator */
+        ntokens--;
+        
+        switch(TOK_OP_VAL(tok)){
+        case OP_SCOLON:
+            retval=(ntokens==0 ? P_NONE : P_STMT_NS);
+            break;
+            
+        case OP_NEXTLINE:
+            retval=(ntokens==0 ? P_NONE : P_STMT);
+            break;
+            
+        case OP_L_BRC:
+            retval=(ntokens==0 ? P_BEG_SECT : P_STMT_SECT);
+            break;
+            
+        case OP_R_BRC:
+            if(ntokens==0){
+                retval=P_END_SECT;
+            }else{
+                tokz_unget_token(tokz, tok);
+                retval=P_STMT_NS;
+            }
+            break;
+
+        case OP_EOF:
+            retval=(ntokens==0 ? P_EOF : P_STMT_NS);
+            
+            if(had_comma==1){
+                e=E_TOKZ_UNEXPECTED_EOF;
+                goto handle_error;
+            }
+            
+            goto end;
+            
+        case OP_COMMA:
+            if(had_comma!=0)
+                goto syntax;
+
+            had_comma=1;
+            continue;
+            
+        default:
+            goto syntax;
+        }
+        
+        if(had_comma!=1)
+            break;
+        
+    syntax:
+        e=E_TOKZ_SYNTAX;
+    handle_error:
+        tokz_warn_error(tokz, tok->line, e);
+        
+        if(!(tokz->flags&TOKZ_ERROR_TOLERANT) || retval!=0)
+            break;
+    }
+    
+end:
+    if(e!=0)
+        retval=-retval;
+    
+    *ntok_ret=ntokens;
+    
+    return retval;
+}
+
+
+static bool find_beg_sect(Tokenizer *tokz)
+{
+    Token tok=TOK_INIT;
+
+    while(tokz_get_token(tokz, &tok)){
+        if(TOK_IS_OP(&tok)){
+            if(TOK_OP_VAL(&tok)==OP_NEXTLINE)
+                continue;
+
+            if(TOK_OP_VAL(&tok)==OP_SCOLON)
+                return FALSE;
+        
+            if(TOK_OP_VAL(&tok)==OP_L_BRC)
+                return TRUE;
+        }
+        
+        tokz_unget_token(tokz, &tok);
+        break;
+    }
+    return FALSE;
+}
+
+
+/* */
+
+
+static const ConfOpt* lookup_option(const ConfOpt *opts, const char *name)
+{
+    while(opts->optname!=NULL){
+        if(strcmp(opts->optname, name)==0)
+            return opts;
+        opts++;
+    }
+    return NULL;
+}
+
+    
+static bool call_end_sect(Tokenizer *tokz, const ConfOpt *opts)
+{    
+    opts=lookup_option(opts, "#end");
+    if(opts!=NULL)
+        return opts->fn(tokz, 0, NULL);
+    
+    return TRUE;
+}
+
+
+static bool call_cancel_sect(Tokenizer *tokz, const ConfOpt *opts)
+{
+    opts=lookup_option(opts, "#cancel");
+    if(opts!=NULL)
+        return opts->fn(tokz, 0, NULL);
+    
+    return TRUE;
+}
+            
+
+/* */
+
+
+bool parse_config_tokz(Tokenizer *tokz, const ConfOpt *options)
+{
+    Token tokens[MAX_TOKENS];
+    bool alloced_optstack=FALSE;
+    int i, t, ntokens=0;
+    int init_nest_lvl;
+    bool had_error;
+    int errornest=0;
+    bool is_default=FALSE;
+
+    /* Allocate tokz->optstack if it does not yet exist (if it does,
+     * we have been called from an option handler)
+     */
+    if(!tokz->optstack){
+        tokz->optstack=ALLOC_N(const ConfOpt*, MAX_NEST);
+        if(!tokz->optstack){
+            warn_err();
+            return FALSE;
+        }
+        
+        memset(tokz->optstack, 0, sizeof(ConfOpt*)*MAX_NEST);
+        init_nest_lvl=tokz->nest_lvl=0;
+        alloced_optstack=TRUE;
+    }else{
+        init_nest_lvl=tokz->nest_lvl;
+    }
+    
+    tokz->optstack[init_nest_lvl]=options;
+    
+    for(i=0; i<MAX_TOKENS; i++)
+        tok_init(&tokens[i]);
+
+    
+    while(1){
+        had_error=FALSE;
+
+        /* free the tokens */
+        while(ntokens--)
+            tok_free(&tokens[ntokens]);
+        
+        /* read the tokens */
+        t=read_statement(tokz, tokens, &ntokens);
+        
+        if((had_error=t<0))
+            t=-t;
+        
+        switch(t){
+        case P_STMT:
+        case P_STMT_NS:
+        case P_STMT_SECT:
+
+            if(errornest)
+                had_error=TRUE;
+            else if(tokz->flags&TOKZ_PARSER_INDENT_MODE)
+                verbose_indent(tokz->nest_lvl);
+            
+            if(!TOK_IS_IDENT(tokens+0)){
+                had_error=TRUE;
+                tokz_warn_error(tokz, tokens->line,
+                                E_TOKZ_IDENTIFIER_EXPECTED);
+            }
+            
+            if(t==P_STMT){
+                if(find_beg_sect(tokz))
+                    t=P_STMT_SECT;
+            }
+            
+            if(had_error)
+                break;
+
+            /* Got the statement and its type */
+            
+            options=lookup_option(tokz->optstack[tokz->nest_lvl],
+                                  TOK_IDENT_VAL(tokens+0));
+            if(options==NULL)
+                options=lookup_option(common_opts, TOK_IDENT_VAL(tokens+0));
+            if(options==NULL && (tokz->flags&TOKZ_DEFAULT_OPTION)){
+                options=lookup_option(tokz->optstack[tokz->nest_lvl], "#default");
+                is_default=(options!=NULL);
+            }
+
+            if(options==NULL){
+                had_error=TRUE;
+                tokz_warn_error(tokz, tokens->line, E_TOKZ_UNKNOWN_OPTION);
+            }else if(!is_default) {            
+                had_error=!check_args(tokz, tokens, ntokens, options->argfmt);
+            }
+            
+            if(had_error)
+                break;
+            
+            /* Found the option and arguments are ok */
+            
+            if(options->opts!=NULL){
+                if(t!=P_STMT_SECT){
+                    had_error=TRUE;
+                    tokz_warn_error(tokz, tokz->line, E_TOKZ_LBRACE_EXPECTED);
+                }else if(tokz->nest_lvl==MAX_NEST-1){
+                    tokz_warn_error(tokz, tokz->line, E_TOKZ_MAX_NEST);
+                    had_error=TRUE;
+                }else{
+                    tokz->nest_lvl++;
+                    tokz->optstack[tokz->nest_lvl]=options->opts;
+                }
+            }else if(t==P_STMT_SECT){
+                had_error=TRUE;
+                tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX);
+            }
+            
+            if(!had_error && options->fn!=NULL){
+                had_error=!options->fn(tokz, ntokens, tokens);
+                if(t==P_STMT_SECT && had_error)
+                    tokz->nest_lvl--;
+            }
+            break;
+            
+        case P_EOF:
+            if(tokz_popf(tokz)){
+                break;
+            }else if(tokz->nest_lvl>0 || errornest>0){
+                tokz_warn_error(tokz, 0, E_TOKZ_UNEXPECTED_EOF);
+                had_error=TRUE;
+            }
+            goto eof;
+            
+        case P_BEG_SECT:
+            had_error=TRUE;
+            errornest++;
+            tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX);
+            break;
+            
+        case P_END_SECT:
+            if(tokz->nest_lvl+errornest==0){
+                tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX);
+                had_error=TRUE;
+            }
+            
+            if(had_error)
+                break;
+            
+            if(errornest!=0){
+                errornest--;
+            }else{
+                had_error=!call_end_sect(tokz, tokz->optstack[tokz->nest_lvl]);
+                tokz->nest_lvl--;
+            }
+            
+            if(tokz->nest_lvl<init_nest_lvl)
+                goto eof;
+        }
+            
+        if(!had_error)
+            continue;
+        
+        if(t==P_STMT_SECT)
+            errornest++;
+        
+        if(!(tokz->flags&TOKZ_ERROR_TOLERANT))
+            break;
+    }
+
+eof:
+    /* free the tokens */
+    while(ntokens--)
+        tok_free(&tokens[ntokens]);
+
+    while(tokz->nest_lvl>=init_nest_lvl){
+        if(tokz->flags&TOKZ_ERROR_TOLERANT || !had_error)
+            call_end_sect(tokz, tokz->optstack[tokz->nest_lvl]);
+        else
+            call_cancel_sect(tokz, tokz->optstack[tokz->nest_lvl]);
+        tokz->nest_lvl--;
+    }
+    
+    /* Free optstack if it was alloced by this call */
+    if(alloced_optstack){
+        free(tokz->optstack);
+        tokz->optstack=NULL;
+        tokz->nest_lvl=0;
+    }
+    
+    if(tokz->flags&TOKZ_PARSER_INDENT_MODE)
+        verbose_indent(init_nest_lvl);
+    
+    return !had_error;
+}
+
+
+/* */
+
+
+bool parse_config(const char *fname, const ConfOpt *options, int flags)
+{
+    Tokenizer *tokz;
+    bool ret;
+    
+    tokz=tokz_open(fname);
+    
+    if(tokz==NULL)
+        return FALSE;
+
+    tokz->flags|=flags&~TOKZ_READ_COMMENTS;
+    
+    ret=parse_config_tokz(tokz, options);
+    
+    tokz_close(tokz);
+    
+    return ret;
+}
+
+
+bool parse_config_file(FILE *file, const ConfOpt *options, int flags)
+{
+    Tokenizer *tokz;
+    bool ret;
+    
+    tokz=tokz_open_file(file, NULL);
+    
+    if(tokz==NULL)
+        return FALSE;
+    
+    tokz->flags|=flags&~TOKZ_READ_COMMENTS;
+    
+    ret=parse_config_tokz(tokz, options);
+    
+    tokz_close(tokz);
+    
+    return ret;
+}
+
+
+/*
+ * Argument validity checking stuff
+ */
+
+
+static int arg_match(Token *tok, char c, bool si)
+{
+    char c2=tok->type;
+    
+    if(c=='.' || c=='*')
+        return 0;
+    
+    if(c2==c)
+        return 0;
+    
+    if(si){
+        if(c2=='i' && c=='s'){
+            TOK_SET_IDENT(tok, TOK_STRING_VAL(tok));
+            return 0;
+        }
+        
+        if(c2=='s' && c=='i'){
+            TOK_SET_STRING(tok, TOK_IDENT_VAL(tok));
+            return 0;
+        }
+    }
+    
+    if(c2=='c' && c=='l'){
+        TOK_SET_LONG(tok, TOK_CHAR_VAL(tok));
+        return 0;
+    }
+    
+    if(c2=='l' && c=='c'){
+        TOK_SET_CHAR(tok, TOK_LONG_VAL(tok));
+        return 0;
+    }
+    
+    if(c2=='l' && c=='d'){
+        TOK_SET_DOUBLE(tok, TOK_LONG_VAL(tok));
+        return 0;
+    }
+       
+    if(c=='b'){
+        if(c2=='l'){
+            TOK_SET_BOOL(tok, TOK_LONG_VAL(tok));
+            return 0;
+        }else if(c2=='i'){
+            if(strcmp(TOK_IDENT_VAL(tok), "TRUE")==0){
+                tok_free(tok);
+                TOK_SET_BOOL(tok, TRUE);
+                return 0;
+            }else if(strcmp(TOK_IDENT_VAL(tok), "FALSE")==0){
+                tok_free(tok);
+                TOK_SET_BOOL(tok, FALSE);
+                return 0;
+            }
+        }
+    }
+                
+    return E_TOKZ_INVALID_ARGUMENT;
+}
+
+
+static int check_argument(const char **pret, Token *tok, const char *p,
+                          bool si)
+{
+    int mode;
+    int e=E_TOKZ_TOO_MANY_ARGS;
+
+again:
+    mode=0;
+    
+    if(*p=='*'){
+        *pret=p;
+        return 0;
+    }else if(*p=='?'){
+        mode=1;
+        p++;
+    }else if(*p==':'){
+        mode=2;
+        p++;
+    }else if(*p=='+'){
+        *pret=p;
+        return arg_match(tok, *(p-1), si);
+    }
+    
+    while(*p!='\0'){
+        e=arg_match(tok, *p, si);
+        if(e==0){
+            p++;
+            while(mode==2 && *p==':'){
+                if(*++p=='\0')
+                    break; /* Invalid argument format string, though... */
+                p++;
+            }
+            *pret=p;
+            return 0;
+        }
+        
+        if(mode==0)
+            break;
+        
+        p++;
+        
+        if(mode==1)
+            goto again;
+        
+        /* mode==2 */
+        
+        if(*p!=':')
+            break;
+        p++;
+        e=E_TOKZ_TOO_MANY_ARGS;
+    }
+    
+    *pret=p;
+    return e;
+}
+
+                           
+static bool args_at_end(const char *p)
+{
+    if(p==NULL)
+        return TRUE;
+    
+    while(*p!='\0'){
+        if(*p=='*' || *p=='+')
+            p++;
+        else if(*p=='?')
+            p+=2;
+        else
+            return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+bool do_check_args(const Tokenizer *tokz, Token *tokens, int ntokens,
+                   const char *fmt, bool si)
+{
+    int i;
+    int e;
+    
+    if(fmt==NULL){
+        if(ntokens!=1)
+            tokz_warn_error(tokz, tokens[0].line, E_TOKZ_TOO_MANY_ARGS);
+        return ntokens==1;
+    }
+
+    for(i=1; i<ntokens; i++){
+        e=check_argument(&fmt, &tokens[i], fmt, si);
+        if(e!=0){
+            tokz_warn_error(tokz, tokens[i].line, e);
+            return FALSE;
+        }
+    }
+
+    if(!args_at_end(fmt)){
+        tokz_warn_error(tokz, tokens[i].line, E_TOKZ_TOO_FEW_ARGS);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+bool check_args(const Tokenizer *tokz, Token *tokens, int ntokens,
+                const char *fmt)
+{
+    return do_check_args(tokz, tokens, ntokens, fmt, FALSE);
+}
+
+
+bool check_args_loose(const Tokenizer *tokz, Token *tokens, int ntokens,
+                const char *fmt)
+{
+    return do_check_args(tokz, tokens, ntokens, fmt, TRUE);
+}
+
+
+/* */
+
+
+static bool try_include(Tokenizer *tokz, const char *fname)
+{
+    FILE *f;
+    
+    f=fopen(fname, "r");
+    
+    if(f==NULL)
+        return FALSE;
+    
+    if(!tokz_pushf_file(tokz, f, fname)){
+        fclose(f);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+static bool try_include_dir(Tokenizer *tokz, const char *dir, int dlen,
+                        const char *file)
+{
+    char *tmpname;
+    bool retval;
+    
+    tmpname=scatn(dir, dlen, file, -1);
+    
+    if(tmpname==NULL){
+        warn_err();
+        return FALSE;
+    }
+    
+    retval=try_include(tokz, tmpname);
+
+    free(tmpname);
+
+    return retval;
+}
+
+
+static bool opt_include(Tokenizer *tokz, int n, Token *toks)
+{
+    const char *fname=TOK_STRING_VAL(toks+1);
+    const char *lastndx=NULL;
+    bool retval, e;
+    int i=0;
+    
+    if(fname[0]!='/' && tokz->name!=NULL)
+        lastndx=strrchr(tokz->name, '/');
+    
+    if(lastndx==NULL)
+        retval=try_include(tokz, fname);
+    else
+        retval=try_include_dir(tokz, tokz->name, lastndx-tokz->name+1, fname);
+    
+    if(retval==TRUE)
+        return TRUE;
+    
+    e=errno;
+    
+    if(tokz->includepaths!=NULL){
+        while(tokz->includepaths[i]!=NULL){
+            if(try_include_dir(tokz, tokz->includepaths[i], -1, fname))
+                return TRUE;
+            i++;
+        }
+    }
+    
+    warn_obj(fname, "%s", strerror(e));
+    
+    return FALSE;
+}
+
+
+extern void tokz_set_includepaths(Tokenizer *tokz, char **paths)
+{
+    tokz->includepaths=paths;
+}
+
+
+
+ConfOpt libtu_dummy_confopts[]={
+    END_CONFOPTS
+};
+
+
+
+bool parse_config_tokz_skip_section(Tokenizer *tokz)
+{
+    return parse_config_tokz(tokz, libtu_dummy_confopts);
+}
diff --git a/libtu/parser.h b/libtu/parser.h
new file mode 100644 (file)
index 0000000..7358053
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * libtu/parser.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_PARSER_H
+#define LIBTU_PARSER_H
+
+#include "tokenizer.h"
+
+/*
+ * format:
+ *     l = long
+ *    d = double
+ *     i = identifier
+ *     s = string
+ *     c = char
+ *  . = 1 times any     ("l.d")
+ *  * = 0 or more times any (must be the last, "sd*")
+ *     ? = optional        ("?c")
+ *     : = conditional        (":c:s")
+ *  + = 1 or more times last (most be the last, "l+")
+ * special entries:
+ * 
+ * "#end"         call this handler at the end of section.
+ * "#cancel"     call this handler when recovering from error
+ */
+
+#define END_CONFOPTS {NULL, NULL, NULL, NULL}
+
+typedef struct _ConfOpt{
+    const char *optname;
+    const char *argfmt;
+    bool (*fn)(Tokenizer *tokz, int n, Token *toks);
+    struct _ConfOpt *opts;
+} ConfOpt;
+
+#define CONFOPTS_NOT_SET libtu_dummy_confopts
+extern ConfOpt libtu_dummy_confopts[];
+
+extern bool parse_config_tokz(Tokenizer *tokz, const ConfOpt *options);
+extern bool parse_config_tokz_skip_section(Tokenizer *tokz);
+extern bool parse_config(const char *fname, const ConfOpt *options, int flags);
+extern bool parse_config_file(FILE *file, const ConfOpt *options, int flags);
+extern bool check_args(const Tokenizer *tokz, Token *tokens, int ntokens,
+                       const char *fmt);
+extern bool check_args_loose(const Tokenizer *tokz, Token *tokens, int ntokens,
+                             const char *fmt);
+
+#endif /* LIBTU_PARSER_H */
diff --git a/libtu/pointer.h b/libtu/pointer.h
new file mode 100644 (file)
index 0000000..f22ecf5
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * libtu/pointer.h
+ *
+ * Copyright (c) Tuomo Valkonen 2005. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_POINTER_H
+#define LIBTU_POINTER_H
+
+#define FIELD_OFFSET(T, F) ((long)((char*)&((T*)0)->F))
+#define FIELD_TO_STRUCT(T, F, A) ((T*)(((char*)A)-FIELD_OFFSET(T, F)))
+
+#endif /* LIBTU_POINTER_H */
diff --git a/libtu/private.h b/libtu/private.h
new file mode 100644 (file)
index 0000000..20068ed
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * libtu/private.h
+ *
+ * Copyright (c) Tuomo Valkonen 2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_PRIVATE_H
+#define LIBTU_PRIVATE_H
+
+#ifndef CF_NO_LOCALE
+
+#include <libintl.h>
+
+#define TR(X) dgettext("libtu", X)
+
+#else
+
+#define TR(X) (X)
+
+#endif
+
+#define DUMMY_TR(X) X
+
+#endif /* LIBTU_PRIVATE_H */
diff --git a/libtu/ptrlist.c b/libtu/ptrlist.c
new file mode 100644 (file)
index 0000000..1ea6726
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * libtu/ptrlist.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include "obj.h"
+#include "ptrlist.h"
+#include "types.h"
+#include "dlist.h"
+#include "misc.h"
+
+
+static void free_node(PtrList **ptrlist, PtrList *node)
+{
+    UNLINK_ITEM(*ptrlist, node, next, prev);
+    free(node);
+}
+
+
+static PtrList *mknode(void *ptr)
+{
+    PtrList *node;
+    
+    if(ptr==NULL)
+        return NULL;
+    
+    node=ALLOC(PtrList);
+    
+    if(node==NULL)
+        return FALSE;
+        
+    node->ptr=ptr;
+    
+    return node;
+}
+
+
+static PtrList *ptrlist_find_node(PtrList *ptrlist, void *ptr)
+{
+    PtrList *node=ptrlist;
+    
+    while(node!=NULL){
+        if(node->ptr==ptr)
+            break;
+        node=node->next;
+    }
+    
+    return node;
+}
+
+
+bool ptrlist_insert_last(PtrList **ptrlist, void *ptr)
+{
+    PtrList *node=mknode(ptr);
+    
+    if(node==NULL)
+        return FALSE;
+    
+    LINK_ITEM_LAST(*ptrlist, node, next, prev);
+    
+    return TRUE;
+}
+
+
+bool ptrlist_insert_first(PtrList **ptrlist, void *ptr)
+{
+    PtrList *node=mknode(ptr);
+    
+    if(node==NULL)
+        return FALSE;
+    
+    LINK_ITEM_FIRST(*ptrlist, node, next, prev);
+    
+    return TRUE;
+}
+
+
+bool ptrlist_reinsert_last(PtrList **ptrlist, void *ptr)
+{
+    PtrList *node=ptrlist_find_node(*ptrlist, ptr);
+    
+    if(node==NULL)
+        return FALSE;
+    
+    UNLINK_ITEM(*ptrlist, node, next, prev);
+    LINK_ITEM_LAST(*ptrlist, node, next, prev);
+    
+    return TRUE;
+}
+
+
+bool ptrlist_reinsert_first(PtrList **ptrlist, void *ptr)
+{
+    PtrList *node=ptrlist_find_node(*ptrlist, ptr);
+    
+    if(node==NULL)
+        return FALSE;
+    
+    UNLINK_ITEM(*ptrlist, node, next, prev);
+    LINK_ITEM_FIRST(*ptrlist, node, next, prev);
+    
+    return TRUE;
+}
+
+
+bool ptrlist_remove(PtrList **ptrlist, void *ptr)
+{
+    PtrList *node=ptrlist_find_node(*ptrlist, ptr);
+    
+    if(node!=NULL)
+        free_node(ptrlist, node);
+    
+    return (node!=NULL);
+}
+
+
+void ptrlist_clear(PtrList **ptrlist)
+{
+    while(*ptrlist!=NULL)
+        free_node(ptrlist, *ptrlist);
+}
+
+
+PtrListIterTmp ptrlist_iter_tmp=NULL;
+
+
+void ptrlist_iter_init(PtrListIterTmp *state, PtrList *ptrlist)
+{
+    *state=ptrlist;
+}
+
+
+void *ptrlist_iter(PtrListIterTmp *state)
+{
+    void *ptr=NULL;
+    
+    if(*state!=NULL){
+        ptr=(*state)->ptr;
+        (*state)=(*state)->next;
+    }
+    
+    return ptr;
+}
+
+
+void ptrlist_iter_rev_init(PtrListIterTmp *state, PtrList *ptrlist)
+{
+    *state=(ptrlist==NULL ? NULL : ptrlist->prev);
+}
+
+
+void *ptrlist_iter_rev(PtrListIterTmp *state)
+{
+    void *ptr=NULL;
+    
+    if(*state!=NULL){
+        ptr=(*state)->ptr;
+        *state=(*state)->prev;
+        if((*state)->next==NULL)
+            *state=NULL;
+    }
+    
+    return ptr;
+}
+
+
+void *ptrlist_take_first(PtrList **ptrlist)
+{
+    PtrList *node=*ptrlist;
+    void *ptr;
+    
+    if(node==NULL)
+        return NULL;
+    
+    ptr=node->ptr;
+    
+    free_node(ptrlist, node);
+    
+    return ptr;
+}
+    
+        
+void *ptrlist_take_last(PtrList **ptrlist)
+{
+    PtrList *node=*ptrlist;
+    void *ptr;
+    
+    if(node==NULL)
+        return NULL;
+    
+    node=node->prev;
+    
+    ptr=node->ptr;
+    
+    free_node(ptrlist, node);
+    
+    return ptr;
+}
diff --git a/libtu/ptrlist.h b/libtu/ptrlist.h
new file mode 100644 (file)
index 0000000..cadec02
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * libtu/ptrlist.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2005.
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_PTRLIST_H
+#define LIBTU_PTRLIST_H
+
+#include "types.h"
+#include "iterable.h"
+
+
+INTRSTRUCT(PtrList);
+
+
+DECLSTRUCT(PtrList){
+    void *ptr;
+    PtrList *next, *prev;
+};
+
+
+typedef PtrList* PtrListIterTmp;
+
+#define PTRLIST_FIRST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->ptr)
+#define PTRLIST_LAST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->prev->ptr)
+#define PTRLIST_EMPTY(LIST) ((LIST)==NULL)
+
+#define FOR_ALL_ON_PTRLIST(TYPE, VAR, LL, TMP) \
+    FOR_ALL_ITER(ptrlist_iter_init, (TYPE)ptrlist_iter, VAR, LL, &(TMP))
+
+#define FOR_ALL_ON_PTRLIST_REV(TYPE, VAR, LL, TMP)        \
+    FOR_ALL_ITER(ptrlist_iter_rev_init,                   \
+                 (TYPE)ptrlist_iter_rev, VAR, LL, &(TMP))
+
+#define FOR_ALL_ON_PTRLIST_UNSAFE(TYPE, VAR, LL) \
+    FOR_ALL_ON_PTRLIST(TYPE, VAR, LL, ptrlist_iter_tmp)
+
+extern PtrListIterTmp ptrlist_iter_tmp;
+
+extern bool ptrlist_insert_last(PtrList **ptrlist, void *ptr);
+extern bool ptrlist_insert_first(PtrList **ptrlist, void *ptr);
+extern bool ptrlist_reinsert_last(PtrList **ptrlist, void *ptr);
+extern bool ptrlist_reinsert_first(PtrList **ptrlist, void *ptr);
+extern bool ptrlist_remove(PtrList **ptrlist, void *ptr);
+extern void ptrlist_clear(PtrList **ptrlist);
+extern void ptrlist_iter_init(PtrListIterTmp *state, PtrList *ptrlist);
+extern void *ptrlist_iter(PtrListIterTmp *state);
+extern void ptrlist_iter_rev_init(PtrListIterTmp *state, PtrList *ptrlist);
+extern void *ptrlist_iter_rev(PtrListIterTmp *state);
+extern void *ptrlist_take_first(PtrList **ptrlist);
+extern void *ptrlist_take_last(PtrList **ptrlist);
+
+#endif /* LIBTU_PTRLIST_H */
diff --git a/libtu/rb-test.c b/libtu/rb-test.c
new file mode 100644 (file)
index 0000000..96ea161
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+Generic C code for red-black trees.
+Copyright (C) 2000 James S. Plank
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/* Revision 1.2.  Jim Plank */
+
+#include "rb.h"
+#include <stdio.h>
+
+/* an example -- this allocates a red-black tree for integers.  For a 
+ * user-specified number of iterations, it does the following:
+
+ * delete a random element in the tree.
+ * make two new random elements, and insert them into the tree
+ * At the end, it prints the sorted list of elements, and then prints
+ * stats of the number of black nodes in any path in the tree, and 
+ * the minimum and maximum path lengths.
+  
+ * Rb_find_ikey and rb_inserti could have been used, but instead
+ * rb_find_gkey and rb_insertg were used to show how they work.
+  
+ */
+
+int icomp(char *i, char *j)
+{
+  int I, J;
+  
+  I = (int) i;
+  J = (int) j;
+  if (I == J) return 0;
+  if (I > J) return -1; else return 1;
+}
+
+main(int argc, char **argv)
+{
+  int i, j, tb, nb, mxp, mnp, p;
+  int iterations;
+  Rb_node argt, t;
+  int *a;
+
+  if (argc != 2) {
+    fprintf(stderr, "usage: main #iterations\n");
+    exit(1);
+  }
+  argt = make_rb();
+  srandom(time(0));
+  iterations = atoi(argv[1]);
+  a = (int *) malloc (iterations*sizeof(int));
+
+  for (i = 0; i < atoi(argv[1]); i++) {
+    if (i > 0) {
+      j = random()%i;
+      
+      rb_delete_node(rb_find_gkey(argt, (char *) (a[j]), icomp));
+      a[j] = random() % 1000;
+      rb_insertg(argt, (char *) (a[j]), NULL, icomp);
+    }
+    a[i] = random() % 1000;
+    rb_insertg(argt, (char *) (a[i]), NULL, icomp);
+  }
+  tb = 0;
+  mxp = 0;
+  mnp = 0;
+  rb_traverse(t, argt) {
+    printf("%d ", t->k.ikey);
+    nb = rb_nblack(t);
+    p = rb_plength(t);
+    if (tb == 0) {
+      tb = nb;
+    } else if (tb != nb) {
+      printf("Conflict: tb=%d, nb=%d\n", tb, nb);
+      exit(1);
+    }
+    mxp = (mxp == 0 || mxp < p) ? p : mxp;
+    mnp = (mnp == 0 || mxp > p) ? p : mnp;
+  }
+  printf("\n");  
+
+  printf("Nblack = %d\n", tb);
+  printf("Max    = %d\n", mxp);
+  printf("Min    = %d\n", mnp);
+}
diff --git a/libtu/rb.c b/libtu/rb.c
new file mode 100644 (file)
index 0000000..d842a27
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+Generic C code for red-black trees.
+Copyright (C) 2000 James S. Plank,
+              2004 Tuomo Valkonen.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* Revision 1.2.  Jim Plank */
+
+/* Original code by Jim Plank (plank@cs.utk.edu) */
+/* modified for THINK C 6.0 for Macintosh by Chris Bartley */
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "rb.h"
+static void mk_new_int(Rb_node l, Rb_node r, Rb_node p, int il);
+static Rb_node lprev(Rb_node n);
+static Rb_node rprev(Rb_node n);
+static void recolor(Rb_node n);
+static void single_rotate(Rb_node y, int l);
+static void rb_print_tree(Rb_node t, int level);
+static void rb_iprint_tree(Rb_node t, int level);
+
+/* Gcc complains if non-char* pointer is passed to printf %p  */
+#define DONT_COMPLAIN (char*)
+#define isred(n) (n->s.red)
+#define isblack(n) (!isred(n))
+#define isleft(n) (n->s.left)
+#define isright(n) (!isleft(n))
+#define isint(n) (n->s.internal)
+#define isext(n) (!isint(n))
+#define ishead(n) (n->s.head)
+#define isroot(n) (n->s.root)
+#define setred(n) n->s.red = 1
+#define setblack(n) n->s.red = 0
+#define setleft(n) n->s.left = 1
+#define setright(n) n->s.left = 0
+#define sethead(n) n->s.head = 1
+#define setroot(n) n->s.root = 1
+#define setint(n) n->s.internal = 1
+#define setext(n) n->s.internal = 0
+#define setnormal(n) { n->s.root = 0; n ->s.head = 0; }
+#define sibling(n) ((isleft(n)) ? n->p.parent->c.child.right \
+                                : n->p.parent->c.child.left)
+static void insert(Rb_node item, Rb_node list) /* Inserts to the end of a list */
+{
+  Rb_node last_node;
+  last_node = list->c.list.blink;
+  list->c.list.blink = item;
+  last_node->c.list.flink = item;
+  item->c.list.blink = last_node;
+  item->c.list.flink = list;
+}
+static void delete_item(Rb_node item)          /* Deletes an arbitrary iterm */
+{
+  item->c.list.flink->c.list.blink = item->c.list.blink;
+  item->c.list.blink->c.list.flink = item->c.list.flink;
+}
+
+#define mk_new_ext(new, kkkey, vvval) {\
+  new = (Rb_node) malloc(sizeof(struct rb_node));\
+  if(new==NULL) return NULL;\
+  new->v.val = vvval;\
+  new->k.key = kkkey;\
+  setext(new);\
+  setblack(new);\
+  setnormal(new);\
+}
+static void mk_new_int(Rb_node l, Rb_node r, Rb_node p, int il)
+{
+  Rb_node newnode;
+  newnode = (Rb_node) malloc(sizeof(struct rb_node));
+  setint(newnode);
+  setred(newnode);
+  setnormal(newnode);
+  newnode->c.child.left = l;
+  newnode->c.child.right = r;
+  newnode->p.parent = p;
+  newnode->k.lext = l;
+  newnode->v.rext = r;
+  l->p.parent = newnode;
+  r->p.parent = newnode;
+  setleft(l);
+  setright(r);
+  if (ishead(p)) {
+    p->p.root = newnode;
+    setroot(newnode);
+  } else if (il) {
+    setleft(newnode);
+    p->c.child.left = newnode;
+  } else {
+    setright(newnode);
+    p->c.child.right = newnode;
+  }
+  recolor(newnode);
+}  
+  
+   
+Rb_node lprev(Rb_node n)
+{
+  if (ishead(n)) return n;
+  while (!isroot(n)) {
+    if (isright(n)) return n->p.parent;
+    n = n->p.parent;
+  }
+  return n->p.parent;
+}
+Rb_node rprev(Rb_node n)
+{
+  if (ishead(n)) return n;
+  while (!isroot(n)) {
+    if (isleft(n)) return n->p.parent;
+    n = n->p.parent;
+  }
+  return n->p.parent;
+}
+Rb_node make_rb()
+{
+  Rb_node head;
+  head = (Rb_node) malloc (sizeof(struct rb_node));
+  if(head!=NULL){
+      head->c.list.flink = head;
+      head->c.list.blink = head;
+      head->p.root = head;
+      head->k.key = "";
+      sethead(head);
+  }
+  return head;
+}
+Rb_node rb_find_ikey_n(Rb_node n, int ikey, int *fnd)
+{
+  *fnd = 0;
+  if (!ishead(n)) {
+    fprintf(stderr, "rb_find_ikey_n called on non-head %p\n", 
+            DONT_COMPLAIN n);
+    exit(1);
+  }
+  if (n->p.root == n) return n;
+  if (ikey == n->c.list.blink->k.ikey) {
+    *fnd = 1;
+    return n->c.list.blink; 
+  }
+  if (ikey > n->c.list.blink->k.ikey) return n; 
+  else n = n->p.root;
+  while (1) {
+    if (isext(n)) return n;
+    if (ikey == n->k.lext->k.ikey) {
+      *fnd = 1;
+      return n->k.lext;
+    }
+    n = (ikey < n->k.lext->k.ikey) ? n->c.child.left : n->c.child.right;
+  }
+}
+Rb_node rb_find_ikey(Rb_node n, int ikey)
+{
+  int fnd;
+  return rb_find_ikey_n(n, ikey, &fnd);
+}
+Rb_node rb_find_gkey_n(Rb_node n, const void *key, Rb_compfn *fxn, int *fnd)
+{
+  int cmp;
+  *fnd = 0;
+  if (!ishead(n)) {
+    fprintf(stderr, "rb_find_gkey_n called on non-head %p\n",
+            DONT_COMPLAIN n);
+    exit(1);
+  }
+  if (n->p.root == n) return n;
+  cmp = (*fxn)(key, n->c.list.blink->k.key);
+  if (cmp == 0) {
+    *fnd = 1;
+    return n->c.list.blink; 
+  }
+  if (cmp > 0) return n; 
+  else n = n->p.root;
+  while (1) {
+    if (isext(n)) return n;
+    cmp = (*fxn)(key, n->k.lext->k.key);
+    if (cmp == 0) {
+      *fnd = 1;
+      return n->k.lext;
+    }
+    if (cmp < 0) n = n->c.child.left ; else n = n->c.child.right;
+  }
+}
+Rb_node rb_find_gkey(Rb_node n, const void *key, Rb_compfn *fxn)
+{
+  int fnd;
+  return rb_find_gkey_n(n, key, fxn, &fnd);
+}
+
+Rb_node rb_find_key_n(Rb_node n, const char *key, int *fnd)
+{
+  return rb_find_gkey_n(n, key, (Rb_compfn*)strcmp, fnd);
+}
+Rb_node rb_find_key(Rb_node n, const char *key)
+{
+  int fnd;
+  return rb_find_gkey_n(n, key, (Rb_compfn*)strcmp, &fnd);
+}
+
+static int ptrcmp(const void *a, const void *b)
+{
+    return (a<b ? -1 : ((a==b) ? 0 : 1));
+}
+
+Rb_node rb_find_pkey_n(Rb_node n, const void *key, int *fnd)
+{
+  return rb_find_gkey_n(n, key, ptrcmp, fnd);
+}
+Rb_node rb_find_pkey(Rb_node n, const void *key)
+{
+  int fnd;
+  return rb_find_gkey_n(n, key, ptrcmp, &fnd);
+}
+
+Rb_node rb_insert_b(Rb_node n, const void *key, void *val)
+{
+  Rb_node newleft, newright, newnode, list, p;
+  if (ishead(n)) {
+    if (n->p.root == n) {         /* Tree is empty */
+      mk_new_ext(newnode, key, val);
+      insert(newnode, n);
+      n->p.root = newnode;
+      newnode->p.parent = n;
+      setroot(newnode);
+      return newnode;
+    } else {
+      mk_new_ext(newright, key, val);
+      insert(newright, n);
+      newleft = newright->c.list.blink;
+      setnormal(newleft);
+      mk_new_int(newleft, newright, newleft->p.parent, isleft(newleft));
+      p = rprev(newright);
+      if (!ishead(p)) p->k.lext = newright;
+      return newright;
+    }
+  } else {
+    mk_new_ext(newleft, key, val);
+    insert(newleft, n);
+    setnormal(n);
+    mk_new_int(newleft, n, n->p.parent, isleft(n));
+    p = lprev(newleft);
+    if (!ishead(p)) p->v.rext = newleft;
+    return newleft;    
+  }
+}
+static void recolor(Rb_node n)
+{  
+  Rb_node p, gp, s;
+  int done = 0;
+  while(!done) {
+    if (isroot(n)) {
+      setblack(n);
+      return;
+    }
+    p = n->p.parent;
+    if (isblack(p)) return;
+    
+    if (isroot(p)) {
+      setblack(p);
+      return;
+    }
+    gp = p->p.parent;
+    s = sibling(p);
+    if (isred(s)) {
+      setblack(p);
+      setred(gp);
+      setblack(s);
+      n = gp;
+    } else {
+      done = 1;
+    }
+  }
+  /* p's sibling is black, p is red, gp is black */
+  
+  if ((isleft(n) == 0) == (isleft(p) == 0)) {
+    single_rotate(gp, isleft(n));
+    setblack(p);
+    setred(gp);
+  } else {
+    single_rotate(p, isleft(n));
+    single_rotate(gp, isleft(n));
+    setblack(n);
+    setred(gp);
+  }
+}
+static void single_rotate(Rb_node y, int l)
+{
+  int rl=0, ir=0;
+  Rb_node x=NULL, yp=NULL;
+  void *tmp=NULL;
+  ir = isroot(y);
+  yp = y->p.parent;
+  if (!ir) {
+    rl = isleft(y);
+  }
+  
+  if (l) {
+    x = y->c.child.left;
+    y->c.child.left = x->c.child.right;
+    setleft(y->c.child.left);
+    y->c.child.left->p.parent = y;
+    x->c.child.right = y;
+    setright(y);  
+  } else {
+    x = y->c.child.right;
+    y->c.child.right = x->c.child.left;
+    setright(y->c.child.right);
+    y->c.child.right->p.parent = y;
+    x->c.child.left = y;
+    setleft(y);  
+  }
+  x->p.parent = yp;
+  y->p.parent = x;
+  if (ir) {
+    yp->p.root = x;
+    setnormal(y);
+    setroot(x);
+  } else {
+    if (rl) {
+      yp->c.child.left = x;
+      setleft(x);
+    } else {
+      yp->c.child.right = x;
+      setright(x);
+    }
+  }
+}
+    
+void rb_delete_node(Rb_node n)
+{
+  Rb_node s, p, gp;
+  char ir;
+  if (isint(n)) {
+    fprintf(stderr, "Cannot delete an internal node: %p\n", DONT_COMPLAIN n);
+    exit(1);
+  }
+  if (ishead(n)) {
+    fprintf(stderr, "Cannot delete the head of an rb_tree: %p\n", DONT_COMPLAIN n);
+    exit(1);
+  }
+  delete_item(n); /* Delete it from the list */
+  p = n->p.parent;  /* The only node */
+  if (isroot(n)) {
+    p->p.root = p;
+    free(n);
+    return;
+  } 
+  s = sibling(n);    /* The only node after deletion */
+  if (isroot(p)) {
+    s->p.parent = p->p.parent;
+    s->p.parent->p.root = s;
+    setroot(s);
+    free(p);
+    free(n);
+    return;
+  }
+  gp = p->p.parent;  /* Set parent to sibling */
+  s->p.parent = gp;
+  if (isleft(p)) {
+    gp->c.child.left = s;
+    setleft(s);
+  } else {
+    gp->c.child.right = s;
+    setright(s);
+  }
+  ir = isred(p);
+  free(p);
+  free(n);
+  
+  if (isext(s)) {      /* Update proper rext and lext values */
+    p = lprev(s); 
+    if (!ishead(p)) p->v.rext = s;
+    p = rprev(s);
+    if (!ishead(p)) p->k.lext = s;
+  } else if (isblack(s)) {
+    fprintf(stderr, "DELETION PROB -- sib is black, internal\n");
+    exit(1);
+  } else {
+    p = lprev(s);
+    if (!ishead(p)) p->v.rext = s->c.child.left;
+    p = rprev(s);
+    if (!ishead(p)) p->k.lext = s->c.child.right;
+    setblack(s);
+    return;
+  }
+  if (ir) return;
+  /* Recolor */
+  
+  n = s;
+  p = n->p.parent;
+  s = sibling(n);
+  while(isblack(p) && isblack(s) && isint(s) && 
+        isblack(s->c.child.left) && isblack(s->c.child.right)) {
+    setred(s);
+    n = p;
+    if (isroot(n)) return;
+    p = n->p.parent;
+    s = sibling(n);
+  }
+  
+  if (isblack(p) && isred(s)) {  /* Rotation 2.3b */
+    single_rotate(p, isright(n));
+    setred(p);
+    setblack(s);
+    s = sibling(n);
+  }
+    
+  { Rb_node x, z; char il;
+    
+    if (isext(s)) {
+      fprintf(stderr, "DELETION ERROR: sibling not internal\n");
+      exit(1);
+    }
+    il = isleft(n);
+    x = il ? s->c.child.left : s->c.child.right ;
+    z = sibling(x);
+    if (isred(z)) {  /* Rotation 2.3f */
+      single_rotate(p, !il);
+      setblack(z);
+      if (isred(p)) setred(s); else setblack(s);
+      setblack(p);
+    } else if (isblack(x)) {   /* Recoloring only (2.3c) */
+      if (isred(s) || isblack(p)) {
+        fprintf(stderr, "DELETION ERROR: 2.3c not quite right\n");
+        exit(1);
+      }
+      setblack(p);
+      setred(s);
+      return;
+    } else if (isred(p)) { /* 2.3d */
+      single_rotate(s, il);
+      single_rotate(p, !il);
+      setblack(x);
+      setred(s);
+      return;
+    } else {  /* 2.3e */
+      single_rotate(s, il);
+      single_rotate(p, !il);
+      setblack(x);
+      return;
+    }
+  }
+}
+void rb_print_tree(Rb_node t, int level)
+{
+  int i;
+  if (ishead(t) && t->p.parent == t) {
+    printf("tree %p is empty\n", DONT_COMPLAIN t);
+  } else if (ishead(t)) {
+    printf("Head: %p.  Root = %p\n", DONT_COMPLAIN t, DONT_COMPLAIN t->p.root);
+    rb_print_tree(t->p.root, 0);
+  } else {
+    if (isext(t)) {
+      for (i = 0; i < level; i++) putchar(' ');
+      printf("Ext node %p: %c,%c: p=%p, k=%s\n", 
+             DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r', 
+             DONT_COMPLAIN t->p.parent, DONT_COMPLAIN t->k.key);
+    } else {
+      rb_print_tree(t->c.child.left, level+2);
+      rb_print_tree(t->c.child.right, level+2);
+      for (i = 0; i < level; i++) putchar(' ');
+      printf("Int node %p: %c,%c: l=%p, r=%p, p=%p, lr=(%s,%s)\n", 
+             DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r',
+             DONT_COMPLAIN t->c.child.left, DONT_COMPLAIN t->c.child.right, 
+             DONT_COMPLAIN t->p.parent, DONT_COMPLAIN t->k.lext->k.key, 
+             DONT_COMPLAIN t->v.rext->k.key);
+    }
+  }
+}
+void rb_iprint_tree(Rb_node t, int level)
+{
+  int i;
+  if (ishead(t) && t->p.parent == t) {
+    printf("tree %p is empty\n", DONT_COMPLAIN t);
+  } else if (ishead(t)) {
+    printf("Head: %p.  Root = %p, < = %p, > = %p\n", 
+           DONT_COMPLAIN t, DONT_COMPLAIN t->p.root,
+           DONT_COMPLAIN t->c.list.blink, 
+           DONT_COMPLAIN t->c.list.flink);
+    rb_iprint_tree(t->p.root, 0);
+  } else {
+    if (isext(t)) {
+      for (i = 0; i < level; i++) putchar(' ');
+      printf("Ext node %p: %c,%c: p=%p, <=%p, >=%p k=%d\n", 
+             DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r',
+             DONT_COMPLAIN t->p.parent, DONT_COMPLAIN t->c.list.blink,
+             DONT_COMPLAIN t->c.list.flink, t->k.ikey);
+    } else {
+      rb_iprint_tree(t->c.child.left, level+2);
+      rb_iprint_tree(t->c.child.right, level+2);
+      for (i = 0; i < level; i++) putchar(' ');
+      printf("Int node %p: %c,%c: l=%p, r=%p, p=%p, lr=(%d,%d)\n", 
+             DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r',
+             DONT_COMPLAIN t->c.child.left, DONT_COMPLAIN t->c.child.right, 
+             DONT_COMPLAIN t->p.parent, t->k.lext->k.ikey, t->v.rext->k.ikey);
+    }
+  }
+}
+      
+int rb_nblack(Rb_node n)
+{
+  int nb;
+  if (ishead(n) || isint(n)) {
+    fprintf(stderr, "ERROR: rb_nblack called on a non-external node %p\n", 
+            DONT_COMPLAIN n);
+    exit(1);
+  }
+  nb = 0;
+  while(!ishead(n)) {
+    if (isblack(n)) nb++;
+    n = n->p.parent;
+  }
+  return nb;
+}
+int rb_plength(Rb_node n)
+{
+  int pl;
+  if (ishead(n) || isint(n)) {
+    fprintf(stderr, "ERROR: rb_plength called on a non-external node %p\n", 
+            DONT_COMPLAIN n);
+    exit(1);
+  }
+  pl = 0;
+  while(!ishead(n)) {
+    pl++;
+    n = n->p.parent;
+  }
+  return pl;
+}
+void rb_free_tree(Rb_node n)
+{
+  if (!ishead(n)) {
+    fprintf(stderr, "ERROR: Rb_free_tree called on a non-head node\n");
+    exit(1);
+  }
+  while(rb_first(n) != rb_nil(n)) {
+    rb_delete_node(rb_first(n));
+  }
+  free(n);
+}
+void *rb_val(Rb_node n)
+{
+  return n->v.val;
+}
+Rb_node rb_insert_a(Rb_node nd, const void *key, void *val)
+{
+  return rb_insert_b(nd->c.list.flink, key, val);
+}
+
+Rb_node rb_insert(Rb_node tree, const char *key, void *val)
+{
+  return rb_insert_b(rb_find_key(tree, key), key, val);
+}
+
+Rb_node rb_inserti(Rb_node tree, int ikey, void *val)
+{
+  return rb_insert_b(rb_find_ikey(tree, ikey), (void *) ikey, val);
+}
+
+Rb_node rb_insertg(Rb_node tree, const void *key, void *val, Rb_compfn *func)
+{
+  return rb_insert_b(rb_find_gkey(tree, key, func), key, val);
+}
+
+Rb_node rb_insertp(Rb_node tree, const void *key, void *val)
+{
+  return rb_insertg(tree, key, val, ptrcmp);
+}
+
+
diff --git a/libtu/rb.h b/libtu/rb.h
new file mode 100644 (file)
index 0000000..9acc2b2
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+Generic C code for red-black trees.
+Copyright (C) 2000 James S. Plank,
+              2004 Tuomo Valkonen.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Revision 1.2.  Jim Plank */
+
+/* Original code by Jim Plank (plank@cs.utk.edu) */
+/* modified for THINK C 6.0 for Macintosh by Chris Bartley */
+
+#ifndef LIBTU_RB_H
+#define LIBTU_RB_H
+
+typedef struct rb_status {
+  unsigned red : 1 ;
+  unsigned internal : 1 ;
+  unsigned left : 1 ;
+  unsigned root : 1 ;
+  unsigned head : 1 ;
+} Rb_status;
+/* Main rb_node.  You only ever use the fields
+
+   c.list.flink
+   c.list.blink
+
+   k.key or k.ikey
+   v.val
+*/
+
+
+typedef struct rb_node {
+  union {
+    struct {
+      struct rb_node *flink;
+      struct rb_node *blink;
+    } list;
+    struct {
+      struct rb_node *left;
+      struct rb_node *right;
+    } child;
+  } c;
+  union {
+    struct rb_node *parent;
+    struct rb_node *root;
+  } p;
+  Rb_status s;
+  union {
+    int ikey;
+    const void *key;
+    struct rb_node *lext;
+  } k;
+  union {
+    int ival;
+    void *val;
+    struct rb_node *rext;
+  } v;
+} *Rb_node;
+
+
+typedef int Rb_compfn(const void *, const void *);
+
+
+extern Rb_node make_rb();   /* Creates a new rb-tree */
+
+/* Creates a node with key key and val val and inserts it into the tree.
+   rb_insert uses strcmp() as comparison funcion.  rb_inserti uses <>=,
+   rb_insertg uses func() */
+
+extern Rb_node rb_insert(Rb_node tree, const char *key, void *val);
+extern Rb_node rb_inserti(Rb_node tree, int ikey, void *val);
+extern Rb_node rb_insertp(Rb_node tree, const void *key, void *val);
+extern Rb_node rb_insertg(Rb_node tree, const void *key, void *val, 
+                          Rb_compfn *func);
+
+
+/* returns an external node in t whose value is equal
+  k or whose value is the smallest value greater than k. */
+
+extern Rb_node rb_find_key(Rb_node root, const char *key);
+extern Rb_node rb_find_ikey(Rb_node root, int ikey);
+extern Rb_node rb_find_pkey(Rb_node root, const void *key);
+extern Rb_node rb_find_gkey(Rb_node root, const void *key, Rb_compfn *func);
+
+
+/* Works just like the find_key versions only it returns whether or not
+   it found the key in the integer variable found */
+
+extern Rb_node rb_find_key_n(Rb_node root, const char *key, int *found);
+extern Rb_node rb_find_ikey_n(Rb_node root, int ikey, int *found);
+extern Rb_node rb_find_pkey_n(Rb_node root, const void *key, int *found);
+extern Rb_node rb_find_gkey_n(Rb_node root, const void *key, 
+                              Rb_compfn *func, int *found);
+
+
+/* Creates a node with key key and val val and inserts it into the 
+   tree before/after node nd.  Does not check to ensure that you are 
+   keeping the correct order */
+
+extern Rb_node rb_insert_b(Rb_node nd, const void *key, void *val);
+extern Rb_node rb_insert_a(Rb_node nd, const void *key, void *val);
+
+
+extern void rb_delete_node(Rb_node node);  /* Deletes and frees a node (but 
+                                              not the key or val) */
+extern void rb_free_tree(Rb_node root);  /* Deletes and frees an entire tree */
+
+extern void *rb_val(Rb_node node);  /* Returns node->v.val -- this is to shut
+                                       lint up */
+
+extern int rb_nblack(Rb_node n); /* returns # of black nodes in path from
+                                    n to the root */
+int rb_plength(Rb_node n);       /* returns the # of nodes in path from
+                                   n to the root */
+#define rb_first(n) (n->c.list.flink)
+#define rb_last(n) (n->c.list.blink)
+#define rb_next(n) (n->c.list.flink)
+#define rb_prev(n) (n->c.list.blink)
+#define rb_empty(t) (t->c.list.flink == t)
+#ifndef rb_nil
+#define rb_nil(t) (t)
+#endif
+#define rb_traverse(ptr, lst) \
+  for(ptr = rb_first(lst); ptr != rb_nil(lst); ptr = rb_next(ptr))
+#endif /* LIBTU_RB_H */
diff --git a/libtu/setparam.c b/libtu/setparam.c
new file mode 100644 (file)
index 0000000..3b5e5eb
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * libtu/setparam.c
+ *
+ * Copyright (c) Tuomo Valkonen 2005.
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <string.h>
+
+#include "setparam.h"
+
+int libtu_string_to_setparam(const char *str)
+{
+    if(str!=NULL){
+        if(strcmp(str, "set")==0 || strcmp(str, "true")==0)
+            return SETPARAM_SET;
+        else if(strcmp(str, "unset")==0 || strcmp(str, "false")==0)
+            return SETPARAM_UNSET;
+        else if(strcmp(str, "toggle")==0)
+            return SETPARAM_TOGGLE;
+    }
+    
+    return SETPARAM_UNKNOWN;
+}
+
+
+bool libtu_do_setparam(int sp, bool val)
+{
+    switch(sp){
+    case SETPARAM_SET:
+        return TRUE;
+    case SETPARAM_UNSET:
+        return FALSE;
+    case SETPARAM_TOGGLE:
+        return (val==FALSE);
+    default:
+        return val;
+    }
+}
+
+bool libtu_do_setparam_str(const char *str, bool val)
+{
+    return libtu_do_setparam(libtu_string_to_setparam(str), val);
+}
+
+
+int libtu_setparam_invert(int sp)
+{
+    switch(sp){
+    case SETPARAM_SET:
+        return SETPARAM_UNSET;
+    case SETPARAM_UNSET:
+        return SETPARAM_SET;
+    default:
+        return sp;
+    }
+}
+    
diff --git a/libtu/setparam.h b/libtu/setparam.h
new file mode 100644 (file)
index 0000000..8e6fad6
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * libtu/setparam.h
+ *
+ * Copyright (c) Tuomo Valkonen 2005.
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_SETPARAM_H
+#define LIBTU_SETPARAM_H
+
+#include "types.h"
+
+enum{
+    SETPARAM_UNKNOWN,
+    SETPARAM_SET,
+    SETPARAM_UNSET,
+    SETPARAM_TOGGLE
+};
+
+extern int libtu_string_to_setparam(const char *str);
+extern bool libtu_do_setparam_str(const char *str, bool val);
+extern bool libtu_do_setparam(int sp, bool val);
+extern int libtu_setparam_invert(int sp);
+
+#endif /* LIBTU_SETPARAM_H */
diff --git a/libtu/snprintf_2.2/INSTALL b/libtu/snprintf_2.2/INSTALL
new file mode 100644 (file)
index 0000000..9cdf469
--- /dev/null
@@ -0,0 +1,24 @@
+HOW TO INSTALL - manually:
+
+  1. Read the description of macros that control the bahaviour
+     of the program at the beginning of the file snprintf.c,
+     change the definitions in snprintf.c or in Makefile if necessary.
+
+  2. make
+
+  3. move the file snprintf.o where your programs will find it
+
+
+
+HOW TO INSTALL - with autoconf:
+
+Contributed by Caolan McNamara <Caolan.McNamara@ul.ie>:
+
+  Though it might be overkill for snprintf I also have
+  an autoconf and automaked version which works out the need
+  for long long support and makes snprintf optionally into
+  a dynamic library on libtool supported platforms.
+
+  1. cd with_autoconf
+
+  2. follow instructions in the file INSTALL there.
diff --git a/libtu/snprintf_2.2/LICENSE.txt b/libtu/snprintf_2.2/LICENSE.txt
new file mode 100644 (file)
index 0000000..aeb06a7
--- /dev/null
@@ -0,0 +1,121 @@
+The Frontier Artistic License Version 1.0
+Derived from the Artistic License at OpenSource.org.
+Submitted to OpenSource.org for Open Source Initiative certification.
+   
+Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to
+make reasonable modifications.
+   
+Definitions
+
+  "Package" refers to the script, suite, file, or collection of
+  scripts, suites, and/or files distributed by the Copyright Holder,
+  and to derivatives of that Package created through textual modification.
+
+  "Standard Version" refers to such a Package if it has not been
+  modified, or has been modified in accordance with the wishes of
+  the Copyright Holder.
+
+  "Copyright Holder" is whoever is named in the copyright statement
+  or statements for the package.
+
+  "You" is you, if you're thinking about copying or distributing
+  this Package.
+
+  "Reasonable copying fee" is whatever you can justify on the basis
+  of media cost, duplication charges, time of people involved, and
+  so on. (You will not be required to justify it to the Copyright
+  Holder, but only to the computing community at large as a market
+  that must bear the fee.)
+
+  "Freely Available" means that no fee is charged for the item
+  itself, though there may be fees involved in handling the item.
+  It also means that recipients of the item may redistribute it under
+  the same conditions they received it.
+       
+
+Terms
+
+1. You may make and give away verbatim copies of the source form of
+the Standard Version of this Package without restriction, provided
+that you duplicate all of the original copyright notices and
+associated disclaimers.
+   
+2. You may apply bug fixes, portability fixes, and other modifications
+derived from the Public Domain or from the Copyright Holder. A Package
+modified in such a way shall still be considered the Standard Version.
+   
+3. You may otherwise modify your copy of this Package in any way,
+provided that you insert a prominent notice in each changed script,
+suite, or file stating how and when you changed that script, suite,
+or file, and provided that you do at least ONE of the following:
+   
+  a) Use the modified Package only within your corporation or
+  organization, or retain the modified Package solely for personal use.
+     
+  b) Place your modifications in the Public Domain or otherwise make
+  them Freely Available, such as by posting said modifications to Usenet
+  or an equivalent medium, or placing the modifications on a major archive
+  site such as ftp.uu.net, or by allowing the Copyright Holder to include
+  your modifications in the Standard Version of the Package.
+     
+  c) Rename any non-standard executables so the names do not conflict
+  with standard executables, which must also be provided, and provide
+  a separate manual page (or equivalent) for each non-standard executable
+  that clearly documents how it differs from the Standard Version.
+     
+  d) Make other distribution arrangements with the Copyright Holder.
+     
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+   
+  a) Distribute a Standard Version of the executables and library
+  files, together with instructions (in the manual page or
+  equivalent) on where to get the Standard Version.
+     
+  b) Accompany the distribution with the machine-readable source of
+  the Package with your modifications.
+     
+  c) Accompany any non-standard executables with their corresponding
+  Standard Version executables, give the non-standard executables
+  non-standard names, and clearly document the differences in manual
+  pages (or equivalent), together with instructions on where to get
+  the Standard Version.
+     
+  d) Make other distribution arrangements with the Copyright Holder.
+     
+5. You may charge a reasonable copying fee for any distribution of
+this Package. You may charge any fee you choose for support of this
+Package. You may not charge a fee for this Package itself. However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial)
+software distribution provided that you do not advertise this Package
+as a product of your own.
+   
+6. The scripts and library files supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whomever generated
+them, and may be sold commercially, and may be aggregated with this
+Package.
+   
+7. Scripts, suites, or programs supplied by you that depend on or
+otherwise make use of this Package shall not be considered part of
+this Package.
+   
+8. The name of the Copyright Holder may not be used to endorse or
+promote products derived from this software without specific prior
+written permission.
+   
+9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+   
+                        The End
+
+
+http://www.spinwardstars.com/frontier/fal.html
diff --git a/libtu/snprintf_2.2/Makefile.unused b/libtu/snprintf_2.2/Makefile.unused
new file mode 100644 (file)
index 0000000..2e022f7
--- /dev/null
@@ -0,0 +1,43 @@
+# Make sure you include -DHAVE_SNPRINTF in CFLAGS if your system
+# does have snprintf!
+
+# If you need (long long int) support and you sprintf supports it,
+# define -DSNPRINTF_LONGLONG_SUPPORT
+
+CC      = gcc
+
+CFLAGS  = -DPREFER_PORTABLE_SNPRINTF -O3 \
+         -Wall -Wpointer-arith -Wwrite-strings \
+         -Wcast-qual -Wcast-align -Waggregate-return \
+         -Wmissing-prototypes -Wmissing-declarations \
+         -Wshadow -Wstrict-prototypes
+
+#        -DNEED_ASPRINTF -DNEED_ASNPRINTF -DNEED_VASPRINTF -DNEED_VASNPRINTF
+#        -DNEED_SNPRINTF_ONLY
+
+# Digital Unix: native compiler usually produces better code than gcc
+#CC     = cc
+#CFLAGS = -DPREFER_PORTABLE_SNPRINTF -O4 -std1 -arch host
+
+# Recommend to leave COMPATIBILITY empty for normal use.
+# Should be set for bug compatibility when running tests
+# too keep them less chatty.
+COMPATIBILITY =
+
+#COMPATIBILITY = -DSOLARIS_BUG_COMPATIBLE
+#COMPATIBILITY = -DHPUX_BUG_COMPATIBLE
+#COMPATIBILITY = -DDIGITAL_UNIX_BUG_COMPATIBLE
+#COMPATIBILITY = -DPERL_BUG_COMPATIBLE
+#COMPATIBILITY = -DLINUX_COMPATIBLE
+
+.c.o:
+       rm -f $@
+       $(CC) $(CFLAGS) $(COMPATIBILITY) -c $*.c
+
+all:snprintf.o Makefile
+
+test::snprintf.o test.c Makefile
+       $(CC) $(CFLAGS) $(COMPATIBILITY) snprintf.o -o $@ test.c
+
+clean:
+       /usr/bin/rm -f *.o test core
diff --git a/libtu/snprintf_2.2/README b/libtu/snprintf_2.2/README
new file mode 100644 (file)
index 0000000..af36f8e
--- /dev/null
@@ -0,0 +1,283 @@
+
+                                  snprintf.c
+                   - a portable implementation of snprintf,
+       including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf
+                                       
+   snprintf is a routine to convert numeric and string arguments to
+   formatted strings. It is similar to sprintf(3) provided in a system's
+   C library, yet it requires an additional argument - the buffer size -
+   and it guarantees never to store anything beyond the given buffer,
+   regardless of the format or arguments to be formatted. Some newer
+   operating systems do provide snprintf in their C library, but many do
+   not or do provide an inadequate (slow or idiosyncratic) version, which
+   calls for a portable implementation of this routine.
+   
+Author
+
+   Mark Martinec <mark.martinec@ijs.si>, April 1999, June 2000
+   Copyright © 1999, Mark Martinec
+   
+Terms and conditions ...
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the Frontier Artistic License which comes with
+   this Kit.
+   
+Features
+
+     * careful adherence to specs regarding flags, field width and
+       precision;
+     * good performance for large string handling (large format, large
+       argument or large paddings). Performance is similar to system's
+       sprintf and in several cases significantly better (make sure you
+       compile with optimizations turned on, tell the compiler the code
+       is strict ANSI if necessary to give it more freedom for
+       optimizations);
+     * return value semantics per ISO/IEC 9899:1999 ("ISO C99");
+     * written in standard ISO/ANSI C - requires an ANSI C compiler.
+       
+Supported conversion specifiers and data types
+
+   This snprintf only supports the following conversion specifiers: s, c,
+   d, o, u, x, X, p (and synonyms: i, D, U, O - see below) with flags:
+   '-', '+', ' ', '0' and '#'. An asterisk is supported for field width
+   as well as precision.
+   
+   Length modifiers 'h' (short int), 'l' (long int), and 'll' (long long
+   int) are supported.
+   
+   NOTE:
+   
+     If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the
+     length modifier 'll' is recognized but treated the same as 'l',
+     which may cause argument value truncation! Defining
+     SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also
+     handles length modifier 'll'. long long int is a language extension
+     which may not be portable.
+     
+   Conversion of numeric data (conversion specifiers d, o, u, x, X, p)
+   with length modifiers (none or h, l, ll) is left to the system routine
+   sprintf, but all handling of flags, field width and precision as well
+   as c and s conversions is done very carefully by this portable
+   routine. If a string precision (truncation) is specified (e.g. %.8s)
+   it is guaranteed the string beyond the specified precision will not be
+   referenced.
+   
+   Length modifiers h, l and ll are ignored for c and s conversions (data
+   types wint_t and wchar_t are not supported).
+   
+   The following common synonyms for conversion characters are supported:
+     * i is a synonym for d
+     * D is a synonym for ld, explicit length modifiers are ignored
+     * U is a synonym for lu, explicit length modifiers are ignored
+     * O is a synonym for lo, explicit length modifiers are ignored
+       
+   The D, O and U conversion characters are nonstandard, they are
+   supported for backward compatibility only, and should not be used for
+   new code.
+   
+   The following is specifically not supported:
+     * flag ' (thousands' grouping character) is recognized but ignored
+     * numeric conversion specifiers: f, e, E, g, G and synonym F, as
+       well as the new a and A conversion specifiers
+     * length modifier 'L' (long double) and 'q' (quad - use 'll'
+       instead)
+     * wide character/string conversions: lc, ls, and nonstandard
+       synonyms C and S
+     * writeback of converted string length: conversion character n
+     * the n$ specification for direct reference to n-th argument
+     * locales
+       
+   It is permitted for str_m to be zero, and it is permitted to specify
+   NULL pointer for resulting string argument if str_m is zero (as per
+   ISO C99).
+   
+   The return value is the number of characters which would be generated
+   for the given input, excluding the trailing null. If this value is
+   greater or equal to str_m, not all characters from the result have
+   been stored in str, output bytes beyond the (str_m-1) -th character
+   are discarded. If str_m is greater than zero it is guaranteed the
+   resulting string will be null-terminated.
+   
+   NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,
+   but is different from some older and vendor implementations, and is
+   also different from XPG, XSH5, SUSv2 specifications. For historical
+   discussion on changes in the semantics and standards of snprintf see
+   printf(3) man page in the Linux programmers manual.
+   
+   Routines asprintf and vasprintf return a pointer (in the ptr argument)
+   to a buffer sufficiently large to hold the resulting string. This
+   pointer should be passed to free(3) to release the allocated storage
+   when it is no longer needed. If sufficient space cannot be allocated,
+   these functions will return -1 and set ptr to be a NULL pointer. These
+   two routines are a GNU C library extensions (glibc).
+   
+   Routines asnprintf and vasnprintf are similar to asprintf and
+   vasprintf, yet, like snprintf and vsnprintf counterparts, will write
+   at most str_m-1 characters into the allocated output string, the last
+   character in the allocated buffer then gets the terminating null. If
+   the formatted string length (the return value) is greater than or
+   equal to the str_m argument, the resulting string was truncated and
+   some of the formatted characters were discarded. These routines
+   present a handy way to limit the amount of allocated memory to some
+   sane value.
+   
+Availability
+
+   http://www.ijs.si/software/snprintf/
+     * snprintf_1.3.tar.gz (1999-06-30), md5 sum: snprintf_1.3.tar.gz.md5
+     * snprintf_2.1.tar.gz (2000-07-14), md5 sum: snprintf_2.1.tar.gz.md5
+     * snprintf_2.2.tar.gz (2000-10-18), md5 sum: snprintf_2.2.tar.gz.md5
+       
+Mailing list
+
+   There is a very low-traffic mailing list snprintf-announce@ijs.si
+   where announcements about new versions will be posted as well as
+   warnings about threatening bugs if discovered. The posting is
+   restricted to snprintf developer(s).
+   
+   To subscribe to (or unsubscribe from) the mailing list please visit
+   the list server's web page
+   http://mailman.ijs.si/listinfo/snprintf-announce
+   
+   You can also subscribe to the list by mailing the command SUBSCRIBE
+   either in the subject or in the message body to the address
+   snprintf-announce-request@ijs.si . You will be asked for confirmation
+   before subscription will be effective.
+   
+   The list of members is only accessible to the list administrator, so
+   there is no need for concern about automatic e-mail address gatherers.
+   
+   Questions about the mailing list and concerns for the attention of a
+   person should be sent to snprintf-announce-admin@ijs.si
+   
+   There is no general discussion list about portable snprintf at the
+   moment. Please send comments and suggestion to the author.
+   
+Revision history
+
+   Version 1.3 fixes a runaway loop problem from 1.2. Please upgrade.
+   
+   1999-06-30 V1.3 Mark Martinec <mark.martinec@ijs.si>
+          
+          + fixed runaway loop (eventually crashing when str_l wraps
+            beyond 2^31) while copying format string without conversion
+            specifiers to a buffer that is too short (thanks to Edwin
+            Young <edwiny@autonomy.com> for spotting the problem);
+          + added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) to
+            snprintf.h
+            
+   2000-02-14 V2.0 (never released) Mark Martinec <mark.martinec@ijs.si>
+          
+          + relaxed license terms: The Artistic License now applies. You
+            may still apply the GNU GENERAL PUBLIC LICENSE as was
+            distributed with previous versions, if you prefer;
+          + changed REVISION HISTORY dates to use ISO 8601 date format;
+          + added vsnprintf (patch also independently proposed by Caolán
+            McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
+            
+   2000-06-27 V2.1 Mark Martinec <mark.martinec@ijs.si>
+          
+          + removed POSIX check for str_m < 1; value 0 for str_m is
+            allowed by ISO C99 (and GNU C library 2.1) (pointed out on
+            2000-05-04 by Caolán McNamara, caolan@ csn dot ul dot ie).
+            Besides relaxed license this change in standards adherence is
+            the main reason to bump up the major version number;
+          + added nonstandard routines asnprintf, vasnprintf, asprintf,
+            vasprintf that dynamically allocate storage for the resulting
+            string; these routines are not compiled by default, see
+            comments where NEED_V?ASN?PRINTF macros are defined;
+          + autoconf contributed by Caolán McNamara
+            
+   2000-10-06 V2.2 Mark Martinec <mark.martinec@ijs.si>
+          
+          + BUG FIX: the %c conversion used a temporary variable that was
+            no longer in scope when referenced, possibly causing
+            incorrect resulting character;
+          + BUG FIX: make precision and minimal field width unsigned to
+            handle huge values (2^31 <= n < 2^32) correctly; also be more
+            careful in the use of signed/unsigned/size_t internal
+            variables -- probably more careful than many vendor
+            implementations, but there may still be a case where huge
+            values of str_m, precision or minimal field could cause
+            incorrect behaviour;
+          + use separate variables for signed/unsigned arguments, and for
+            short/int, long, and long long argument lengths to avoid
+            possible incompatibilities on certain computer architectures.
+            Also use separate variable arg_sign to hold sign of a numeric
+            argument, to make code more transparent;
+          + some fiddling with zero padding and "0x" to make it Linux
+            compatible;
+          + systematically use macros fast_memcpy and fast_memset instead
+            of case-by-case hand optimization; determine some breakeven
+            string lengths for different architectures;
+          + terminology change: format -> conversion specifier, C9x ->
+            ISO/IEC 9899:1999 ("ISO C99"), alternative form -> alternate
+            form, data type modifier -> length modifier;
+          + several comments rephrased and new ones added;
+          + make compiler not complain about 'credits' defined but not
+            used;
+            
+Other implementations of snprintf
+
+   I am aware of some other (more or less) portable implementations of
+   snprintf. I do not claim they are free software - please refer to
+   their respective copyright and licensing terms. If you know of other
+   versions please let me know.
+     * a very thorough implementation (src/util_snprintf.c) by the Apache
+       Group distributed with the Apache web server -
+       http://www.apache.org/ . Does its own floating point conversions
+       using routines ecvt(3), fcvt(3) and gcvt(3) from the standard C
+       library or from the GNU libc.
+       This is from the code:
+       
+     This software [...] was originally based on public domain software
+     written at the National Center for Supercomputing Applications,
+     University of Illinois, Urbana-Champaign.
+     [...] This code is based on, and used with the permission of, the
+     SIO stdio-replacement strx_* functions by Panos Tsirigotis
+     <panos@alumni.cs.colorado.edu> for xinetd.
+     * QCI Utilities use a modified version of snprintf from the Apache
+       group.
+     * implementations as distributed with OpenBSD, FreeBSD, and NetBSD
+       are all wrappers to vfprintf.c, which is derived from software
+       contributed to Berkeley by Chris Torek.
+     * implementation from Prof. Patrick Powell <papowell@sdsu.edu>,
+       Dept. Electrical and Computer Engineering, San Diego State
+       University, San Diego, CA 92182-1309, published in Bugtraq
+       archives for 3rd quarter (Jul-Aug) 1995. No floating point
+       conversions.
+     * Brandon Long's <blong@fiction.net> modified version of Prof.
+       Patrick Powell's snprintf with contributions from others. With
+       minimal floating point support.
+     * implementation (src/snprintf.c) as distributed with sendmail -
+       http://www.sendmail.org/ is a cleaned up Prof. Patrick Powell's
+       version to compile properly and to support .precision and %lx.
+     * implementation from Caolán McNamara available at
+       http://www.csn.ul.ie/~caolan/publink/snprintf-1.1.tar.gz, handles
+       floating point.
+     * implementation used by newlog (a replacement for syslog(3)) made
+       available by the SOS Corporation. Enabling floating point support
+       is a compile-time option.
+     * implementation by Michael Richardson <mcr@metis.milkyway.com> is
+       available at http://sandelman.ottawa.on.ca/SSW/snp/snp.html. It is
+       based on BSD44-lite's vfprintf() call, modified to function on
+       SunOS. Needs internal routines from the 4.4 strtod (included),
+       requires GCC to compile the long long (aka quad_t) portions.
+     * implementation from Tomi Salo <ttsalo@ssh.fi> distributed with SSH
+       2.0 Unix Server. Not in public domain. Floating point conversions
+       done by system's sprintf.
+     * and for completeness: my portable version described in this very
+       document available at http://www.ijs.si/software/snprintf/ .
+       
+   In retrospect, it appears that a lot of effort was wasted by many
+   people for not being aware of what others are doing. Sigh.
+   
+   Also of interest: The Approved Base Working Group Resolution for XSH5,
+   Ref: bwg98-006, Topic: snprintf.
+     _________________________________________________________________
+   
+   mm
+   Last updated: 2000-10-18
+   
+   Valid HTML 4.0! 
diff --git a/libtu/snprintf_2.2/README.html b/libtu/snprintf_2.2/README.html
new file mode 100644 (file)
index 0000000..4ecaab3
--- /dev/null
@@ -0,0 +1,382 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+<link rev="made" href="mailto:mark.martinec@ijs.si">
+<title>
+snprintf.c - a portable implementation of snprintf
+(including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf)
+</title>
+<meta http-equiv="Content-Language" content="en">
+<meta name="author"    content="Mark Martinec">
+<meta name="copyright" content="Copyright 2000 Mark Martinec, All Rights Reserved">
+<meta name="date"      content="2000-10-18">
+<meta name="keywords" lang="en"
+ content="snprintf,portable,vsnprintf,asnprintf,vasnprintf,asprintf,vasprintf
+          ISO/IEC 9899:1999,ISO C99,ISO C9x,POSIX">
+<style type="text/css">
+<!--
+  body { background: white; color: black }
+ -->
+</style>
+</head>
+<body>
+<h1><b>snprintf.c</b>
+<br> - a portable implementation of snprintf,
+<br><font size="+1">including
+vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf</font>
+</h1>
+
+<p><b>snprintf</b> is a routine to convert numeric and string arguments
+to formatted strings. It is similar to sprintf(3) provided in a
+system's C library, yet it requires an additional argument - the buffer
+size - and it guarantees never to store anything beyond the given buffer,
+regardless of the format or arguments to be formatted. Some newer
+operating systems do provide <b>snprintf</b> in their C library,
+but many do not or do provide an inadequate (slow or idiosyncratic)
+version, which calls for a portable implementation of this routine.
+
+<h2>Author</h2>
+
+<p><a href="http://www.ijs.si/people/mark/">Mark Martinec</a>
+&lt;<a href="mailto:mark.martinec@ijs.si">mark.martinec@ijs.si</a>&gt;,
+April 1999, June 2000
+<br>Copyright &copy; 1999, Mark Martinec
+
+<h2>Terms and conditions ...</h2>
+
+<p>This program is free software; you can redistribute it
+and/or modify it under the terms of the
+<i><a href="./LICENSE.txt">Frontier Artistic License</a></i>
+which comes with this Kit.
+
+<h2>Features</h2>
+
+<ul>
+<li>careful adherence to specs regarding flags, field width and precision;
+<li>good performance for large string handling (large format, large argument
+or large paddings). Performance is similar to system's <b>sprintf</b>
+and in several cases significantly better (make sure you compile with
+optimizations turned on, tell the compiler the code is strict ANSI
+if necessary to give it more freedom for optimizations);
+<li>return value semantics per ISO/IEC 9899:1999 ("ISO C99");
+<li>written in standard ISO/ANSI C - requires an ANSI C compiler.
+</ul>
+
+<h2>Supported conversion specifiers and data types</h2>
+
+<p>This <b>snprintf</b> only supports the following conversion specifiers:
+s, c, d, o, u, x, X, p  (and synonyms: i, D, U, O - see below)
+with flags: '-', '+', '&nbsp;', '0' and '#'.
+An asterisk is supported for field width as well as precision.
+
+<p>Length modifiers 'h' (<i>short int</i>), 'l' (<i>long int</i>),
+and 'll' (<i>long long int</i>) are supported.
+
+<p>NOTE:
+<blockquote>
+If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default)
+the length modifier 'll' is recognized but treated the same as 'l',
+which may cause argument value truncation!
+Defining SNPRINTF_LONGLONG_SUPPORT requires that your system's
+<b>sprintf</b> also handles length modifier 'll'.
+<i>long long int</i> is a language extension which may not be portable.
+</blockquote>
+
+<p>Conversion of numeric data (conversion specifiers d, o, u, x, X, p)
+with length modifiers (none or h, l, ll) is left to the system
+routine <b>sprintf</b>, but all handling of flags, field width and precision
+as well as c and s conversions is done very carefully by this portable routine.
+If a string precision (truncation) is specified (e.g. %.8s) it is
+guaranteed the string beyond the specified precision will not be referenced.
+
+<p>Length modifiers h, l and ll are ignored for c and s conversions
+(data types <i>wint_t</i> and <i>wchar_t</i> are not supported).
+
+<p>The following common synonyms for conversion characters are supported:
+<ul>
+<li>i is a synonym for d
+<li>D is a synonym for ld, explicit length modifiers are ignored
+<li>U is a synonym for lu, explicit length modifiers are ignored
+<li>O is a synonym for lo, explicit length modifiers are ignored
+</ul>
+The D, O and U conversion characters are nonstandard, they are supported
+for backward compatibility only, and should not be used for new code.
+
+<p>The following is specifically <b>not</b> supported:
+<ul>
+<li>flag ' (thousands' grouping character) is recognized but ignored
+<li>numeric conversion specifiers: f, e, E, g, G and synonym F,
+as well as the new a and A conversion specifiers
+<li>length modifier 'L' (<i>long double</i>)
+and 'q' (<i>quad</i> - use 'll' instead)
+<li>wide character/string conversions: lc, ls, and nonstandard
+synonyms C and S
+<li>writeback of converted string length: conversion character n
+<li>the n$ specification for direct reference to n-th argument
+<li>locales
+</ul>
+
+<p>It is permitted for str_m to be zero, and it is permitted to specify NULL
+pointer for resulting string argument if str_m is zero (as per ISO C99).
+
+<p>The return value is the number of characters which would be generated
+for the given input, <i>excluding</i> the trailing null. If this value
+is greater or equal to str_m, not all characters from the result
+have been stored in str, output bytes beyond the (str_m-1) -th character
+are discarded. If str_m is greater than zero it is guaranteed
+the resulting string will be null-terminated.
+
+<p>NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,
+but is different from some older and vendor implementations,
+and is also different from XPG, XSH5, SUSv2 specifications.
+For historical discussion on changes in the semantics and standards
+of snprintf see printf(3) man page in the Linux programmers manual.
+
+<p>Routines asprintf and vasprintf return a pointer (in the ptr argument)
+to a buffer sufficiently large to hold the resulting string. This pointer
+should be passed to free(3) to release the allocated storage when it is
+no longer needed. If sufficient space cannot be allocated, these functions
+will return -1 and set ptr to be a NULL pointer. These two routines are a
+GNU C library extensions (glibc).
+
+<p>Routines asnprintf and vasnprintf are similar to asprintf and vasprintf,
+yet, like snprintf and vsnprintf counterparts, will write at most str_m-1
+characters into the allocated output string, the last character in the
+allocated buffer then gets the terminating null. If the formatted string
+length (the return value) is greater than or equal to the str_m argument,
+the resulting string was truncated and some of the formatted characters
+were discarded. These routines present a handy way to limit the amount
+of allocated memory to some sane value.
+
+<h2>Availability</h2>
+
+<p><a href="http://www.ijs.si/software/snprintf/"
+>http://www.ijs.si/software/snprintf/</a>
+
+<ul>
+<li>
+<a href="./snprintf_1.3.tar.gz">snprintf_1.3.tar.gz</a> (1999-06-30),
+md5 sum: <a href="./snprintf_1.3.tar.gz.md5">snprintf_1.3.tar.gz.md5</a>
+
+<li>
+<a href="./snprintf_2.1.tar.gz">snprintf_2.1.tar.gz</a> (2000-07-14),
+md5 sum: <a href="./snprintf_2.1.tar.gz.md5">snprintf_2.1.tar.gz.md5</a>
+
+<li>
+<a href="./snprintf_2.2.tar.gz">snprintf_2.2.tar.gz</a> (2000-10-18),
+md5 sum: <a href="./snprintf_2.2.tar.gz.md5">snprintf_2.2.tar.gz.md5</a>
+</ul>
+
+
+<h2>Mailing list</h2>
+
+<p>There is a very low-traffic mailing list <i>snprintf-announce@ijs.si</i>
+where announcements about new versions will be posted
+as well as warnings about threatening bugs if discovered.
+The posting is restricted to snprintf developer(s).
+
+<p>To subscribe to (or unsubscribe from) the mailing list
+please visit the list server's web page
+<a href="http://mailman.ijs.si/listinfo/snprintf-announce"
+>http://mailman.ijs.si/listinfo/snprintf-announce</a>
+
+<p>You can also subscribe to the list by mailing
+the command SUBSCRIBE either in the subject or in the message body
+to the address <a href="mailto:snprintf-announce-request@ijs.si"
+>snprintf-announce-request@ijs.si</a> . You will be asked for
+confirmation before subscription will be effective.
+
+<p>The list of members is only accessible to the list administrator,
+so there is no need for concern about automatic e-mail address gatherers.
+
+<p>Questions about the mailing list and concerns for the attention
+of a person should be sent to <a href="mailto:snprintf-announce-admin@ijs.si"
+>snprintf-announce-admin@ijs.si</a>
+
+<p>There is no <i>general</i> discussion list about portable snprintf
+at the moment. Please send comments and suggestion to the author.
+
+
+<h2>Revision history</h2>
+
+<p><b>Version 1.3 fixes a runaway loop problem from 1.2. Please upgrade.</b>
+
+<dl>
+<dt>1999-06-30 V1.3  Mark Martinec &lt;mark.martinec@ijs.si&gt;
+<dd><ul>
+<li>fixed runaway loop (eventually crashing when str_l wraps
+  beyond 2^31) while copying format string without
+  conversion specifiers to a buffer that is too short
+  (thanks to Edwin Young &lt;edwiny@autonomy.com&gt; for spotting the problem);
+<li>added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) to snprintf.h
+</ul>
+
+<dt>2000-02-14 V2.0 (never released) Mark Martinec &lt;mark.martinec@ijs.si&gt;
+<dd><ul>
+<li>relaxed license terms:
+  <a href="./LICENSE.txt">The Artistic License</a> now applies.
+  You may still apply the GNU GENERAL PUBLIC LICENSE
+  as was distributed with previous versions, if you prefer;
+<li>changed REVISION HISTORY dates to use
+  <a href="http://www.cl.cam.ac.uk/~mgk25/iso-time.html">ISO 8601
+  date format</a>;
+<li>added vsnprintf (patch also independently proposed by
+  Caol&aacute;n McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
+</ul>
+
+<dt>2000-06-27 V2.1  Mark Martinec &lt;mark.martinec@ijs.si&gt;
+<dd><ul>
+<li>removed POSIX check for str_m &lt; 1; value 0 for str_m is
+  allowed by ISO C99 (and GNU C library 2.1) (pointed out
+  on 2000-05-04 by Caol&aacute;n McNamara, caolan@ csn dot ul dot ie).
+  Besides relaxed license this change in standards adherence
+  is the main reason to bump up the major version number;
+<li>added nonstandard routines asnprintf, vasnprintf, asprintf,
+  vasprintf that dynamically allocate storage for the
+  resulting string; these routines are not compiled by default,
+  see comments where NEED_V?ASN?PRINTF macros are defined;
+<li>autoconf contributed by Caol&aacute;n McNamara
+</ul>
+
+<dt>2000-10-06 V2.2  Mark Martinec &lt;mark.martinec@ijs.si&gt;
+<dd><ul>
+<li><b>BUG FIX</b>: the %c conversion used a temporary variable
+  that was no longer in scope when referenced,
+  possibly causing incorrect resulting character;
+<li>BUG FIX: make precision and minimal field width unsigned
+  to handle huge values (2^31 &lt;= n &lt; 2^32) correctly;
+  also be more careful in the use of signed/unsigned/size_t
+  internal variables -- probably more careful than many
+  vendor implementations, but there may still be a case
+  where huge values of str_m, precision or minimal field
+  could cause incorrect behaviour;
+<li>use separate variables for signed/unsigned arguments,
+  and for short/int, long, and long long argument lengths
+  to avoid possible incompatibilities on certain
+  computer architectures. Also use separate variable
+  arg_sign to hold sign of a numeric argument,
+  to make code more transparent;
+<li>some fiddling with zero padding and "0x" to make it
+  Linux compatible;
+<li>systematically use macros fast_memcpy and fast_memset
+  instead of case-by-case hand optimization; determine some
+  breakeven string lengths for different architectures;
+<li>terminology change: <i>format</i> -&gt; <i>conversion specifier</i>,
+  <i>C9x</i> -&gt; <i>ISO/IEC 9899:1999 ("ISO C99")</i>,
+  <i>alternative form</i> -&gt; <i>alternate form</i>,
+  <i>data type modifier</i> -&gt; <i>length modifier</i>;
+<li>several comments rephrased and new ones added;
+<li>make compiler not complain about 'credits' defined but
+  not used;
+</ul>
+</dl>
+
+<h2>Other implementations of snprintf</h2>
+
+<p>I am aware of some other (more or less) portable implementations
+of <b>snprintf</b>. I do not claim they are free software - please refer
+to their respective copyright and licensing terms.
+If you know of other versions please let
+<a href="http://www.ijs.si/people/mark/">me</a> know.
+
+<ul>
+<li>a very thorough implementation (src/util_snprintf.c)
+by the Apache Group distributed with the
+<a href="http://www.apache.org/">Apache web server
+- http://www.apache.org/</a> .
+Does its own floating point conversions using routines
+ecvt(3), fcvt(3) and gcvt(3) from the standard C library
+or from the GNU libc.
+
+<br>This is from the code:
+<blockquote>
+This software [...] was originally based
+on public domain software written at the
+<a href="http://www.ncsa.uiuc.edu/ncsa.html">National Center
+for Supercomputing Applications</a>, University of Illinois,
+Urbana-Champaign.<br>
+[...] This code is based on, and used with the permission of,
+the SIO stdio-replacement strx_* functions by Panos Tsirigotis
+&lt;<a href="mailto:panos@alumni.cs.colorado.edu">panos@alumni.cs.colorado.edu</a>&gt; for xinetd.
+</blockquote>
+
+<li><a href="http://www.qlue.com/downloads/c_utils_README.html">QCI
+Utilities</a> use a modified version of snprintf from the Apache group.
+
+<li>implementations as distributed with
+<a href="http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdio/">OpenBSD</a>,
+<a href="http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdio/">FreeBSD</a>, and
+<a href="http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/basesrc/lib/libc/stdio/">NetBSD</a>
+are all wrappers to vfprintf.c, which is derived from software
+contributed to Berkeley by Chris Torek.
+
+<li>implementation from Prof. Patrick Powell
+&lt;<a href="mailto:papowell@sdsu.edu">papowell@sdsu.edu</a>&gt;,
+Dept. Electrical and Computer Engineering, San Diego State University,
+San Diego, CA 92182-1309, published in
+<a href="http://www.geek-girl.com/bugtraq/1995_3/0217.html">Bugtraq
+archives for 3rd quarter (Jul-Aug) 1995</a>.
+No floating point conversions.
+
+<li>Brandon Long's
+&lt;<a href="mailto:blong@fiction.net">blong@fiction.net</a>&gt;
+<a href="http://www.fiction.net/~blong/programs/">modified version</a>
+of Prof. Patrick Powell's snprintf with contributions from others.
+With minimal floating point support.
+
+<li>implementation (src/snprintf.c) as distributed with
+<a href="http://www.sendmail.org/">sendmail - http://www.sendmail.org/</a>
+is a cleaned up Prof. Patrick Powell's version
+to compile properly and to support .precision and %lx.
+
+<li>implementation from <a href="http://www.csn.ul.ie/~caolan/"
+>Caol&aacute;n McNamara</a> available at
+<a href="http://www.csn.ul.ie/~caolan/publink/snprintf-1.1.tar.gz"
+>http://www.csn.ul.ie/~caolan/publink/snprintf-1.1.tar.gz</a>,
+handles floating point.
+
+<li>implementation used by
+<a href="ftp://ftp.soscorp.com/pub/sos/lib">newlog</a>
+(a replacement for syslog(3)) made available by
+the <a href="http://www.soscorp.com">SOS Corporation</a>.
+Enabling floating point support is a compile-time option.
+
+<li>implementation by Michael Richardson
+&lt;<a href="mailto:mcr@metis.milkyway.com">mcr@metis.milkyway.com</a>&gt;
+is available at
+<a href="http://sandelman.ottawa.on.ca/SSW/snp/snp.html"
+>http://sandelman.ottawa.on.ca/SSW/snp/snp.html</a>.
+It is based on BSD44-lite's vfprintf() call, modified to function
+on SunOS. Needs internal routines from the 4.4 strtod (included),
+requires GCC to compile the long long (aka quad_t) portions. 
+
+<li>implementation from Tomi Salo
+&lt;<a href="mailto:ttsalo@ssh.fi">ttsalo@ssh.fi</a>&gt;
+distributed with
+<a href="http://www.Europe.DataFellows.com/f-secure/ssh/">SSH 2.0
+Unix Server</a>. Not in public domain.
+Floating point conversions done by system's sprintf.
+
+<li>and for completeness: <a href="http://www.ijs.si/people/mark/">my</a>
+portable version described in this very document available at
+<a href="http://www.ijs.si/software/snprintf/"
+>http://www.ijs.si/software/snprintf/</a> .
+</ul>
+
+In retrospect, it appears that a lot of effort was wasted by many
+people for not being aware of what others are doing. Sigh.
+
+<p>Also of interest:
+<a href="http://www.opengroup.org/platform/resolutions/bwg98-006.html"
+>The Approved Base Working Group Resolution for XSH5,
+Ref: bwg98-006, Topic: snprintf</a>.
+
+<p><hr> 
+<i><a href="http://www.ijs.si/people/mark/">mm</a></i>
+<br>Last updated: 2000-10-18
+
+<p><a href="http://validator.w3.org/check/referer"
+><img src="/images/vh40.gif" alt="Valid HTML 4.0!"
+  border="0" width="88" height="31"></a>
+</body>
+</html>
diff --git a/libtu/snprintf_2.2/snprintf-orig.c b/libtu/snprintf_2.2/snprintf-orig.c
new file mode 100644 (file)
index 0000000..86f42e9
--- /dev/null
@@ -0,0 +1,1025 @@
+/*
+ * snprintf.c - a portable implementation of snprintf
+ *
+ * AUTHOR
+ *   Mark Martinec <mark.martinec@ijs.si>, April 1999.
+ *
+ *   Copyright 1999, Mark Martinec. All rights reserved.
+ *
+ * TERMS AND CONDITIONS
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the "Frontier Artistic License" which comes
+ *   with this Kit.
+ *
+ *   This program 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 Frontier Artistic License for more details.
+ *
+ *   You should have received a copy of the Frontier Artistic License
+ *   with this Kit in the file named LICENSE.txt .
+ *   If not, I'll be glad to provide one.
+ *
+ * FEATURES
+ * - careful adherence to specs regarding flags, field width and precision;
+ * - good performance for large string handling (large format, large
+ *   argument or large paddings). Performance is similar to system's sprintf
+ *   and in several cases significantly better (make sure you compile with
+ *   optimizations turned on, tell the compiler the code is strict ANSI
+ *   if necessary to give it more freedom for optimizations);
+ * - return value semantics per ISO/IEC 9899:1999 ("ISO C99");
+ * - written in standard ISO/ANSI C - requires an ANSI C compiler.
+ *
+ * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES
+ *
+ * This snprintf only supports the following conversion specifiers:
+ * s, c, d, u, o, x, X, p  (and synonyms: i, D, U, O - see below)
+ * with flags: '-', '+', ' ', '0' and '#'.
+ * An asterisk is supported for field width as well as precision.
+ *
+ * Length modifiers 'h' (short int), 'l' (long int),
+ * and 'll' (long long int) are supported.
+ * NOTE:
+ *   If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the
+ *   length modifier 'll' is recognized but treated the same as 'l',
+ *   which may cause argument value truncation! Defining
+ *   SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also
+ *   handles length modifier 'll'.  long long int is a language extension
+ *   which may not be portable.
+ *
+ * Conversion of numeric data (conversion specifiers d, u, o, x, X, p)
+ * with length modifiers (none or h, l, ll) is left to the system routine
+ * sprintf, but all handling of flags, field width and precision as well as
+ * c and s conversions is done very carefully by this portable routine.
+ * If a string precision (truncation) is specified (e.g. %.8s) it is
+ * guaranteed the string beyond the specified precision will not be referenced.
+ *
+ * Length modifiers h, l and ll are ignored for c and s conversions (data
+ * types wint_t and wchar_t are not supported).
+ *
+ * The following common synonyms for conversion characters are supported:
+ *   - i is a synonym for d
+ *   - D is a synonym for ld, explicit length modifiers are ignored
+ *   - U is a synonym for lu, explicit length modifiers are ignored
+ *   - O is a synonym for lo, explicit length modifiers are ignored
+ * The D, O and U conversion characters are nonstandard, they are supported
+ * for backward compatibility only, and should not be used for new code.
+ *
+ * The following is specifically NOT supported:
+ *   - flag ' (thousands' grouping character) is recognized but ignored
+ *   - numeric conversion specifiers: f, e, E, g, G and synonym F,
+ *     as well as the new a and A conversion specifiers
+ *   - length modifier 'L' (long double) and 'q' (quad - use 'll' instead)
+ *   - wide character/string conversions: lc, ls, and nonstandard
+ *     synonyms C and S
+ *   - writeback of converted string length: conversion character n
+ *   - the n$ specification for direct reference to n-th argument
+ *   - locales
+ *
+ * It is permitted for str_m to be zero, and it is permitted to specify NULL
+ * pointer for resulting string argument if str_m is zero (as per ISO C99).
+ *
+ * The return value is the number of characters which would be generated
+ * for the given input, excluding the trailing null. If this value
+ * is greater or equal to str_m, not all characters from the result
+ * have been stored in str, output bytes beyond the (str_m-1) -th character
+ * are discarded. If str_m is greater than zero it is guaranteed
+ * the resulting string will be null-terminated.
+ *
+ * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,
+ * but is different from some older and vendor implementations,
+ * and is also different from XPG, XSH5, SUSv2 specifications.
+ * For historical discussion on changes in the semantics and standards
+ * of snprintf see printf(3) man page in the Linux programmers manual.
+ *
+ * Routines asprintf and vasprintf return a pointer (in the ptr argument)
+ * to a buffer sufficiently large to hold the resulting string. This pointer
+ * should be passed to free(3) to release the allocated storage when it is
+ * no longer needed. If sufficient space cannot be allocated, these functions
+ * will return -1 and set ptr to be a NULL pointer. These two routines are a
+ * GNU C library extensions (glibc).
+ *
+ * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf,
+ * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1
+ * characters into the allocated output string, the last character in the
+ * allocated buffer then gets the terminating null. If the formatted string
+ * length (the return value) is greater than or equal to the str_m argument,
+ * the resulting string was truncated and some of the formatted characters
+ * were discarded. These routines present a handy way to limit the amount
+ * of allocated memory to some sane value.
+ *
+ * AVAILABILITY
+ *   http://www.ijs.si/software/snprintf/
+ *
+ * REVISION HISTORY
+ * 1999-04     V0.9  Mark Martinec
+ *             - initial version, some modifications after comparing printf
+ *               man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10,
+ *               and checking how Perl handles sprintf (differently!);
+ * 1999-04-09  V1.0  Mark Martinec <mark.martinec@ijs.si>
+ *             - added main test program, fixed remaining inconsistencies,
+ *               added optional (long long int) support;
+ * 1999-04-12  V1.1  Mark Martinec <mark.martinec@ijs.si>
+ *             - support the 'p' conversion (pointer to void);
+ *             - if a string precision is specified
+ *               make sure the string beyond the specified precision
+ *               will not be referenced (e.g. by strlen);
+ * 1999-04-13  V1.2  Mark Martinec <mark.martinec@ijs.si>
+ *             - support synonyms %D=%ld, %U=%lu, %O=%lo;
+ *             - speed up the case of long format string with few conversions;
+ * 1999-06-30  V1.3  Mark Martinec <mark.martinec@ijs.si>
+ *             - fixed runaway loop (eventually crashing when str_l wraps
+ *               beyond 2^31) while copying format string without
+ *               conversion specifiers to a buffer that is too short
+ *               (thanks to Edwin Young <edwiny@autonomy.com> for
+ *               spotting the problem);
+ *             - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR)
+ *               to snprintf.h
+ * 2000-02-14  V2.0 (never released) Mark Martinec <mark.martinec@ijs.si>
+ *             - relaxed license terms: The Artistic License now applies.
+ *               You may still apply the GNU GENERAL PUBLIC LICENSE
+ *               as was distributed with previous versions, if you prefer;
+ *             - changed REVISION HISTORY dates to use ISO 8601 date format;
+ *             - added vsnprintf (patch also independently proposed by
+ *               Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
+ * 2000-06-27  V2.1  Mark Martinec <mark.martinec@ijs.si>
+ *             - removed POSIX check for str_m<1; value 0 for str_m is
+ *               allowed by ISO C99 (and GNU C library 2.1) - (pointed out
+ *               on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie).
+ *               Besides relaxed license this change in standards adherence
+ *               is the main reason to bump up the major version number;
+ *             - added nonstandard routines asnprintf, vasnprintf, asprintf,
+ *               vasprintf that dynamically allocate storage for the
+ *               resulting string; these routines are not compiled by default,
+ *               see comments where NEED_V?ASN?PRINTF macros are defined;
+ *             - autoconf contributed by Caolan McNamara
+ * 2000-10-06  V2.2  Mark Martinec <mark.martinec@ijs.si>
+ *             - BUG FIX: the %c conversion used a temporary variable
+ *               that was no longer in scope when referenced,
+ *               possibly causing incorrect resulting character;
+ *             - BUG FIX: make precision and minimal field width unsigned
+ *               to handle huge values (2^31 <= n < 2^32) correctly;
+ *               also be more careful in the use of signed/unsigned/size_t
+ *               internal variables - probably more careful than many
+ *               vendor implementations, but there may still be a case
+ *               where huge values of str_m, precision or minimal field
+ *               could cause incorrect behaviour;
+ *             - use separate variables for signed/unsigned arguments,
+ *               and for short/int, long, and long long argument lengths
+ *               to avoid possible incompatibilities on certain
+ *               computer architectures. Also use separate variable
+ *               arg_sign to hold sign of a numeric argument,
+ *               to make code more transparent;
+ *             - some fiddling with zero padding and "0x" to make it
+ *               Linux compatible;
+ *             - systematically use macros fast_memcpy and fast_memset
+ *               instead of case-by-case hand optimization; determine some
+ *               breakeven string lengths for different architectures;
+ *             - terminology change: 'format' -> 'conversion specifier',
+ *               'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")',
+ *               'alternative form' -> 'alternate form',
+ *               'data type modifier' -> 'length modifier';
+ *             - several comments rephrased and new ones added;
+ *             - make compiler not complain about 'credits' defined but
+ *               not used;
+ */
+
+
+/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf.
+ *
+ * If HAVE_SNPRINTF is defined this module will not produce code for
+ * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well,
+ * causing this portable version of snprintf to be called portable_snprintf
+ * (and portable_vsnprintf).
+ */
+/* #define HAVE_SNPRINTF */
+
+/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and
+ * vsnprintf but you would prefer to use the portable routine(s) instead.
+ * In this case the portable routine is declared as portable_snprintf
+ * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf')
+ * is defined to expand to 'portable_v?snprintf' - see file snprintf.h .
+ * Defining this macro is only useful if HAVE_SNPRINTF is also defined,
+ * but does does no harm if defined nevertheless.
+ */
+/* #define PREFER_PORTABLE_SNPRINTF */
+
+/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support
+ * data type (long long int) and length modifier 'll' (e.g. %lld).
+ * If undefined, 'll' is recognized but treated as a single 'l'.
+ *
+ * If the system's sprintf does not handle 'll'
+ * the SNPRINTF_LONGLONG_SUPPORT must not be defined!
+ *
+ * This is off by default as (long long int) is a language extension.
+ */
+/* #define SNPRINTF_LONGLONG_SUPPORT */
+
+/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf.
+ * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly,
+ * otherwise both snprintf and vsnprintf routines will be defined
+ * and snprintf will be a simple wrapper around vsnprintf, at the expense
+ * of an extra procedure call.
+ */
+/* #define NEED_SNPRINTF_ONLY */
+
+/* Define NEED_V?ASN?PRINTF macros if you need library extension
+ * routines asprintf, vasprintf, asnprintf, vasnprintf respectively,
+ * and your system library does not provide them. They are all small
+ * wrapper routines around portable_vsnprintf. Defining any of the four
+ * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY
+ * and turns on PREFER_PORTABLE_SNPRINTF.
+ *
+ * Watch for name conflicts with the system library if these routines
+ * are already present there.
+ *
+ * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as
+ * specified by C99, to be able to traverse the same list of arguments twice.
+ * I don't know of any other standard and portable way of achieving the same.
+ * With some versions of gcc you may use __va_copy(). You might even get away
+ * with "ap2 = ap", in this case you must not call va_end(ap2) !
+ *   #define va_copy(ap2,ap) ap2 = ap
+ */
+/* #define NEED_ASPRINTF   */
+/* #define NEED_ASNPRINTF  */
+/* #define NEED_VASPRINTF  */
+/* #define NEED_VASNPRINTF */
+
+
+/* Define the following macros if desired:
+ *   SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE,
+ *   HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE,
+ *   DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE,
+ *   PERL_COMPATIBLE, PERL_BUG_COMPATIBLE,
+ *
+ * - For portable applications it is best not to rely on peculiarities
+ *   of a given implementation so it may be best not to define any
+ *   of the macros that select compatibility and to avoid features
+ *   that vary among the systems.
+ *
+ * - Selecting compatibility with more than one operating system
+ *   is not strictly forbidden but is not recommended.
+ *
+ * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE .
+ *
+ * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is
+ *   documented in a sprintf man page on a given operating system
+ *   and actually adhered to by the system's sprintf (but not on
+ *   most other operating systems). It may also refer to and enable
+ *   a behaviour that is declared 'undefined' or 'implementation specific'
+ *   in the man page but a given implementation behaves predictably
+ *   in a certain way.
+ *
+ * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf
+ *   that contradicts the sprintf man page on the same operating system.
+ *
+ * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE
+ *   conditionals take into account all idiosyncrasies of a particular
+ *   implementation, there may be other incompatibilities.
+ */
+
+
+\f
+/* ============================================= */
+/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */
+/* ============================================= */
+
+#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
+#define PORTABLE_SNPRINTF_VERSION_MINOR 2
+
+#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
+# if defined(NEED_SNPRINTF_ONLY)
+# undef NEED_SNPRINTF_ONLY
+# endif
+# if !defined(PREFER_PORTABLE_SNPRINTF)
+# define PREFER_PORTABLE_SNPRINTF
+# endif
+#endif
+
+#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
+#define SOLARIS_COMPATIBLE
+#endif
+
+#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
+#define HPUX_COMPATIBLE
+#endif
+
+#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
+#define DIGITAL_UNIX_COMPATIBLE
+#endif
+
+#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
+#define PERL_COMPATIBLE
+#endif
+
+#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
+#define LINUX_COMPATIBLE
+#endif
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+
+#ifdef isdigit
+#undef isdigit
+#endif
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+
+/* For copying strings longer or equal to 'breakeven_point'
+ * it is more efficient to call memcpy() than to do it inline.
+ * The value depends mostly on the processor architecture,
+ * but also on the compiler and its optimization capabilities.
+ * The value is not critical, some small value greater than zero
+ * will be just fine if you don't care to squeeze every drop
+ * of performance out of the code.
+ *
+ * Small values favor memcpy, large values favor inline code.
+ */
+#if defined(__alpha__) || defined(__alpha)
+#  define breakeven_point   2  /* AXP (DEC Alpha)     - gcc or cc or egcs */
+#endif
+#if defined(__i386__)  || defined(__i386)
+#  define breakeven_point  12  /* Intel Pentium/Linux - gcc 2.96 */
+#endif
+#if defined(__hppa)
+#  define breakeven_point  10  /* HP-PA               - gcc */
+#endif
+#if defined(__sparc__) || defined(__sparc)
+#  define breakeven_point  33  /* Sun Sparc 5         - gcc 2.8.1 */
+#endif
+
+/* some other values of possible interest: */
+/* #define breakeven_point  8 */  /* VAX 4000          - vaxc */
+/* #define breakeven_point 19 */  /* VAX 4000          - gcc 2.7.0 */
+
+#ifndef breakeven_point
+#  define breakeven_point   6  /* some reasonable one-size-fits-all value */
+#endif
+
+#define fast_memcpy(d,s,n) \
+  { register size_t nn = (size_t)(n); \
+    if (nn >= breakeven_point) memcpy((d), (s), nn); \
+    else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
+      register char *dd; register const char *ss; \
+      for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
+
+#define fast_memset(d,c,n) \
+  { register size_t nn = (size_t)(n); \
+    if (nn >= breakeven_point) memset((d), (int)(c), nn); \
+    else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
+      register char *dd; register const int cc=(int)(c); \
+      for (dd=(d); nn>0; nn--) *dd++ = cc; } }
+
+/* prototypes */
+
+#if defined(NEED_ASPRINTF)
+int asprintf   (char **ptr, const char *fmt, /*args*/ ...);
+#endif
+#if defined(NEED_VASPRINTF)
+int vasprintf  (char **ptr, const char *fmt, va_list ap);
+#endif
+#if defined(NEED_ASNPRINTF)
+int asnprintf  (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
+#endif
+#if defined(NEED_VASNPRINTF)
+int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);
+#endif
+
+#if defined(HAVE_SNPRINTF)
+/* declare our portable snprintf  routine under name portable_snprintf  */
+/* declare our portable vsnprintf routine under name portable_vsnprintf */
+#else
+/* declare our portable routines under names snprintf and vsnprintf */
+#define portable_snprintf snprintf
+#if !defined(NEED_SNPRINTF_ONLY)
+#define portable_vsnprintf vsnprintf
+#endif
+#endif
+
+#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
+#if !defined(NEED_SNPRINTF_ONLY)
+int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
+#endif
+#endif
+
+/* declarations */
+
+static char credits[] = "\n\
+@(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\
+@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
+@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
+
+#if defined(NEED_ASPRINTF)
+int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
+  va_list ap;
+  size_t str_m;
+  int str_l;
+
+  *ptr = NULL;
+  va_start(ap, fmt);                            /* measure the required size */
+  str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
+  va_end(ap);
+  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
+  *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
+  if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
+  else {
+    int str_l2;
+    va_start(ap, fmt);
+    str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
+    va_end(ap);
+    assert(str_l2 == str_l);
+  }
+  return str_l;
+}
+#endif
+
+#if defined(NEED_VASPRINTF)
+int vasprintf(char **ptr, const char *fmt, va_list ap) {
+  size_t str_m;
+  int str_l;
+
+  *ptr = NULL;
+  { va_list ap2;
+    va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
+    str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
+    va_end(ap2);
+  }
+  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
+  *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
+  if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
+  else {
+    int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
+    assert(str_l2 == str_l);
+  }
+  return str_l;
+}
+#endif
+
+#if defined(NEED_ASNPRINTF)
+int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
+  va_list ap;
+  int str_l;
+
+  *ptr = NULL;
+  va_start(ap, fmt);                            /* measure the required size */
+  str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
+  va_end(ap);
+  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
+  if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
+  /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
+  if (str_m == 0) {  /* not interested in resulting string, just return size */
+  } else {
+    *ptr = (char *) malloc(str_m);
+    if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
+    else {
+      int str_l2;
+      va_start(ap, fmt);
+      str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
+      va_end(ap);
+      assert(str_l2 == str_l);
+    }
+  }
+  return str_l;
+}
+#endif
+
+#if defined(NEED_VASNPRINTF)
+int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
+  int str_l;
+
+  *ptr = NULL;
+  { va_list ap2;
+    va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
+    str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
+    va_end(ap2);
+  }
+  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
+  if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
+  /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
+  if (str_m == 0) {  /* not interested in resulting string, just return size */
+  } else {
+    *ptr = (char *) malloc(str_m);
+    if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
+    else {
+      int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
+      assert(str_l2 == str_l);
+    }
+  }
+  return str_l;
+}
+#endif
+
+/*
+ * If the system does have snprintf and the portable routine is not
+ * specifically required, this module produces no code for snprintf/vsnprintf.
+ */
+#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+
+#if !defined(NEED_SNPRINTF_ONLY)
+int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
+  va_list ap;
+  int str_l;
+
+  va_start(ap, fmt);
+  str_l = portable_vsnprintf(str, str_m, fmt, ap);
+  va_end(ap);
+  return str_l;
+}
+#endif
+
+#if defined(NEED_SNPRINTF_ONLY)
+int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
+#else
+int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {
+#endif
+
+#if defined(NEED_SNPRINTF_ONLY)
+  va_list ap;
+#endif
+  size_t str_l = 0;
+  const char *p = fmt;
+
+/* In contrast with POSIX, the ISO C99 now says
+ * that str can be NULL and str_m can be 0.
+ * This is more useful than the old:  if (str_m < 1) return -1; */
+
+#if defined(NEED_SNPRINTF_ONLY)
+  va_start(ap, fmt);
+#endif
+  if (!p) p = "";
+  while (*p) {
+    if (*p != '%') {
+   /* if (str_l < str_m) str[str_l++] = *p++;    -- this would be sufficient */
+   /* but the following code achieves better performance for cases
+    * where format string is long and contains few conversions */
+      const char *q = strchr(p+1,'%');
+      size_t n = !q ? strlen(p) : (q-p);
+      if (str_l < str_m) {
+        size_t avail = str_m-str_l;
+        fast_memcpy(str+str_l, p, (n>avail?avail:n));
+      }
+      p += n; str_l += n;
+    } else {
+      const char *starting_p;
+      size_t min_field_width = 0, precision = 0;
+      int zero_padding = 0, precision_specified = 0, justify_left = 0;
+      int alternate_form = 0, force_sign = 0;
+      int space_for_positive = 1; /* If both the ' ' and '+' flags appear,
+                                     the ' ' flag should be ignored. */
+      char length_modifier = '\0';            /* allowed values: \0, h, l, L */
+      char tmp[32];/* temporary buffer for simple numeric->string conversion */
+
+      const char *str_arg;      /* string address in case of string argument */
+      size_t str_arg_l;         /* natural field width of arg without padding
+                                   and sign */
+      unsigned char uchar_arg;
+        /* unsigned char argument value - only defined for c conversion.
+           N.B. standard explicitly states the char argument for
+           the c conversion is unsigned */
+
+      size_t number_of_zeros_to_pad = 0;
+        /* number of zeros to be inserted for numeric conversions
+           as required by the precision or minimal field width */
+
+      size_t zero_padding_insertion_ind = 0;
+        /* index into tmp where zero padding is to be inserted */
+
+      char fmt_spec = '\0';
+        /* current conversion specifier character */
+
+      str_arg = credits;/* just to make compiler happy (defined but not used)*/
+      str_arg = NULL;
+      starting_p = p; p++;  /* skip '%' */
+   /* parse flags */
+      while (*p == '0' || *p == '-' || *p == '+' ||
+             *p == ' ' || *p == '#' || *p == '\'') {
+        switch (*p) {
+        case '0': zero_padding = 1; break;
+        case '-': justify_left = 1; break;
+        case '+': force_sign = 1; space_for_positive = 0; break;
+        case ' ': force_sign = 1;
+     /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
+#ifdef PERL_COMPATIBLE
+     /* ... but in Perl the last of ' ' and '+' applies */
+                  space_for_positive = 1;
+#endif
+                  break;
+        case '#': alternate_form = 1; break;
+        case '\'': break;
+        }
+        p++;
+      }
+   /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
+
+   /* parse field width */
+      if (*p == '*') {
+        int j;
+        p++; j = va_arg(ap, int);
+        if (j >= 0) min_field_width = j;
+        else { min_field_width = -j; justify_left = 1; }
+      } else if (isdigit((int)(*p))) {
+        /* size_t could be wider than unsigned int;
+           make sure we treat argument like common implementations do */
+        unsigned int uj = *p++ - '0';
+        while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
+        min_field_width = uj;
+      }
+   /* parse precision */
+      if (*p == '.') {
+        p++; precision_specified = 1;
+        if (*p == '*') {
+          int j = va_arg(ap, int);
+          p++;
+          if (j >= 0) precision = j;
+          else {
+            precision_specified = 0; precision = 0;
+         /* NOTE:
+          *   Solaris 2.6 man page claims that in this case the precision
+          *   should be set to 0.  Digital Unix 4.0, HPUX 10 and BSD man page
+          *   claim that this case should be treated as unspecified precision,
+          *   which is what we do here.
+          */
+          }
+        } else if (isdigit((int)(*p))) {
+          /* size_t could be wider than unsigned int;
+             make sure we treat argument like common implementations do */
+          unsigned int uj = *p++ - '0';
+          while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
+          precision = uj;
+        }
+      }
+   /* parse 'h', 'l' and 'll' length modifiers */
+      if (*p == 'h' || *p == 'l') {
+        length_modifier = *p; p++;
+        if (length_modifier == 'l' && *p == 'l') {   /* double l = long long */
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+          length_modifier = '2';                  /* double l encoded as '2' */
+#else
+          length_modifier = 'l';                 /* treat it as a single 'l' */
+#endif
+          p++;
+        }
+      }
+      fmt_spec = *p;
+   /* common synonyms: */
+      switch (fmt_spec) {
+      case 'i': fmt_spec = 'd'; break;
+      case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
+      case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
+      case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
+      default: break;
+      }
+   /* get parameter value, do initial processing */
+      switch (fmt_spec) {
+      case '%': /* % behaves similar to 's' regarding flags and field widths */
+      case 'c': /* c behaves similar to 's' regarding flags and field widths */
+      case 's':
+        length_modifier = '\0';          /* wint_t and wchar_t not supported */
+     /* the result of zero padding flag with non-numeric conversion specifier*/
+     /* is undefined. Solaris and HPUX 10 does zero padding in this case,    */
+     /* Digital Unix and Linux does not. */
+#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
+        zero_padding = 0;    /* turn zero padding off for string conversions */
+#endif
+        str_arg_l = 1;
+        switch (fmt_spec) {
+        case '%':
+          str_arg = p; break;
+        case 'c': {
+          int j = va_arg(ap, int);
+          uchar_arg = (unsigned char) j;   /* standard demands unsigned char */
+          str_arg = (const char *) &uchar_arg;
+          break;
+        }
+        case 's':
+          str_arg = va_arg(ap, const char *);
+          if (!str_arg) str_arg_l = 0;
+       /* make sure not to address string beyond the specified precision !!! */
+          else if (!precision_specified) str_arg_l = strlen(str_arg);
+       /* truncate string if necessary as requested by precision */
+          else if (precision == 0) str_arg_l = 0;
+          else {
+       /* memchr on HP does not like n > 2^31  !!! */
+            const char *q = memchr(str_arg, '\0',
+                             precision <= 0x7fffffff ? precision : 0x7fffffff);
+            str_arg_l = !q ? precision : (q-str_arg);
+          }
+          break;
+        default: break;
+        }
+        break;
+      case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
+        /* NOTE: the u, o, x, X and p conversion specifiers imply
+                 the value is unsigned;  d implies a signed value */
+
+        int arg_sign = 0;
+          /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
+            +1 if greater than zero (or nonzero for unsigned arguments),
+            -1 if negative (unsigned argument is never negative) */
+
+        int int_arg = 0;  unsigned int uint_arg = 0;
+          /* only defined for length modifier h, or for no length modifiers */
+
+        long int long_arg = 0;  unsigned long int ulong_arg = 0;
+          /* only defined for length modifier l */
+
+        void *ptr_arg = NULL;
+          /* pointer argument value -only defined for p conversion */
+
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+        long long int long_long_arg = 0;
+        unsigned long long int ulong_long_arg = 0;
+          /* only defined for length modifier ll */
+#endif
+        if (fmt_spec == 'p') {
+        /* HPUX 10: An l, h, ll or L before any other conversion character
+         *   (other than d, i, u, o, x, or X) is ignored.
+         * Digital Unix:
+         *   not specified, but seems to behave as HPUX does.
+         * Solaris: If an h, l, or L appears before any other conversion
+         *   specifier (other than d, i, u, o, x, or X), the behavior
+         *   is undefined. (Actually %hp converts only 16-bits of address
+         *   and %llp treats address as 64-bit data which is incompatible
+         *   with (void *) argument on a 32-bit system).
+         */
+#ifdef SOLARIS_COMPATIBLE
+#  ifdef SOLARIS_BUG_COMPATIBLE
+          /* keep length modifiers even if it represents 'll' */
+#  else
+          if (length_modifier == '2') length_modifier = '\0';
+#  endif
+#else
+          length_modifier = '\0';
+#endif
+          ptr_arg = va_arg(ap, void *);
+          if (ptr_arg != NULL) arg_sign = 1;
+        } else if (fmt_spec == 'd') {  /* signed */
+          switch (length_modifier) {
+          case '\0':
+          case 'h':
+         /* It is non-portable to specify a second argument of char or short
+          * to va_arg, because arguments seen by the called function
+          * are not char or short.  C converts char and short arguments
+          * to int before passing them to a function.
+          */
+            int_arg = va_arg(ap, int);
+            if      (int_arg > 0) arg_sign =  1;
+            else if (int_arg < 0) arg_sign = -1;
+            break;
+          case 'l':
+            long_arg = va_arg(ap, long int);
+            if      (long_arg > 0) arg_sign =  1;
+            else if (long_arg < 0) arg_sign = -1;
+            break;
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+          case '2':
+            long_long_arg = va_arg(ap, long long int);
+            if      (long_long_arg > 0) arg_sign =  1;
+            else if (long_long_arg < 0) arg_sign = -1;
+            break;
+#endif
+          }
+        } else {  /* unsigned */
+          switch (length_modifier) {
+          case '\0':
+          case 'h':
+            uint_arg = va_arg(ap, unsigned int);
+            if (uint_arg) arg_sign = 1;
+            break;
+          case 'l':
+            ulong_arg = va_arg(ap, unsigned long int);
+            if (ulong_arg) arg_sign = 1;
+            break;
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+          case '2':
+            ulong_long_arg = va_arg(ap, unsigned long long int);
+            if (ulong_long_arg) arg_sign = 1;
+            break;
+#endif
+          }
+        }
+        str_arg = tmp; str_arg_l = 0;
+     /* NOTE:
+      *   For d, i, u, o, x, and X conversions, if precision is specified,
+      *   the '0' flag should be ignored. This is so with Solaris 2.6,
+      *   Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
+      */
+#ifndef PERL_COMPATIBLE
+        if (precision_specified) zero_padding = 0;
+#endif
+        if (fmt_spec == 'd') {
+          if (force_sign && arg_sign >= 0)
+            tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
+         /* leave negative numbers for sprintf to handle,
+            to avoid handling tricky cases like (short int)(-32768) */
+#ifdef LINUX_COMPATIBLE
+        } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
+          tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
+#endif
+        } else if (alternate_form) {
+          if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )
+            { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }
+         /* alternate form should have no effect for p conversion, but ... */
+#ifdef HPUX_COMPATIBLE
+          else if (fmt_spec == 'p'
+         /* HPUX 10: for an alternate form of p conversion,
+          *          a nonzero result is prefixed by 0x. */
+#ifndef HPUX_BUG_COMPATIBLE
+         /* Actually it uses 0x prefix even for a zero value. */
+                   && arg_sign != 0
+#endif
+                  ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }
+#endif
+        }
+        zero_padding_insertion_ind = str_arg_l;
+        if (!precision_specified) precision = 1;   /* default precision is 1 */
+        if (precision == 0 && arg_sign == 0
+#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
+            && fmt_spec != 'p'
+         /* HPUX 10 man page claims: With conversion character p the result of
+          * converting a zero value with a precision of zero is a null string.
+          * Actually HP returns all zeroes, and Linux returns "(nil)". */
+#endif
+        ) {
+         /* converted to null string */
+         /* When zero value is formatted with an explicit precision 0,
+            the resulting formatted string is empty (d, i, u, o, x, X, p).   */
+        } else {
+          char f[5]; int f_l = 0;
+          f[f_l++] = '%';    /* construct a simple format string for sprintf */
+          if (!length_modifier) { }
+          else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }
+          else f[f_l++] = length_modifier;
+          f[f_l++] = fmt_spec; f[f_l++] = '\0';
+          if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
+          else if (fmt_spec == 'd') {  /* signed */
+            switch (length_modifier) {
+            case '\0':
+            case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg);  break;
+            case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+            case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;
+#endif
+            }
+          } else {  /* unsigned */
+            switch (length_modifier) {
+            case '\0':
+            case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg);  break;
+            case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+            case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;
+#endif
+            }
+          }
+         /* include the optional minus sign and possible "0x"
+            in the region before the zero padding insertion point */
+          if (zero_padding_insertion_ind < str_arg_l &&
+              tmp[zero_padding_insertion_ind] == '-') {
+            zero_padding_insertion_ind++;
+          }
+          if (zero_padding_insertion_ind+1 < str_arg_l &&
+              tmp[zero_padding_insertion_ind]   == '0' &&
+             (tmp[zero_padding_insertion_ind+1] == 'x' ||
+              tmp[zero_padding_insertion_ind+1] == 'X') ) {
+            zero_padding_insertion_ind += 2;
+          }
+        }
+        { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
+          if (alternate_form && fmt_spec == 'o'
+#ifdef HPUX_COMPATIBLE                                  /* ("%#.o",0) -> ""  */
+              && (str_arg_l > 0)
+#endif
+#ifdef DIGITAL_UNIX_BUG_COMPATIBLE                      /* ("%#o",0) -> "00" */
+#else
+              /* unless zero is already the first character */
+              && !(zero_padding_insertion_ind < str_arg_l
+                   && tmp[zero_padding_insertion_ind] == '0')
+#endif
+          ) {        /* assure leading zero for alternate-form octal numbers */
+            if (!precision_specified || precision < num_of_digits+1) {
+             /* precision is increased to force the first character to be zero,
+                except if a zero value is formatted with an explicit precision
+                of zero */
+              precision = num_of_digits+1; precision_specified = 1;
+            }
+          }
+       /* zero padding to specified precision? */
+          if (num_of_digits < precision) 
+            number_of_zeros_to_pad = precision - num_of_digits;
+        }
+     /* zero padding to specified minimal field width? */
+        if (!justify_left && zero_padding) {
+          int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
+          if (n > 0) number_of_zeros_to_pad += n;
+        }
+        break;
+      }
+      default: /* unrecognized conversion specifier, keep format string as-is*/
+        zero_padding = 0;  /* turn zero padding off for non-numeric convers. */
+#ifndef DIGITAL_UNIX_COMPATIBLE
+        justify_left = 1; min_field_width = 0;                /* reset flags */
+#endif
+#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
+     /* keep the entire format string unchanged */
+        str_arg = starting_p; str_arg_l = p - starting_p;
+     /* well, not exactly so for Linux, which does something inbetween,
+      * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y"  */
+#else
+     /* discard the unrecognized conversion, just keep *
+      * the unrecognized conversion character          */
+        str_arg = p; str_arg_l = 0;
+#endif
+        if (*p) str_arg_l++;  /* include invalid conversion specifier unchanged
+                                 if not at end-of-string */
+        break;
+      }
+      if (*p) p++;      /* step over the just processed conversion specifier */
+   /* insert padding to the left as requested by min_field_width;
+      this does not include the zero padding in case of numerical conversions*/
+      if (!justify_left) {                /* left padding with blank or zero */
+        int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
+        if (n > 0) {
+          if (str_l < str_m) {
+            size_t avail = str_m-str_l;
+            fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));
+          }
+          str_l += n;
+        }
+      }
+   /* zero padding as requested by the precision or by the minimal field width
+    * for numeric conversions required? */
+      if (number_of_zeros_to_pad <= 0) {
+     /* will not copy first part of numeric right now, *
+      * force it to be copied later in its entirety    */
+        zero_padding_insertion_ind = 0;
+      } else {
+     /* insert first part of numerics (sign or '0x') before zero padding */
+        int n = zero_padding_insertion_ind;
+        if (n > 0) {
+          if (str_l < str_m) {
+            size_t avail = str_m-str_l;
+            fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
+          }
+          str_l += n;
+        }
+     /* insert zero padding as requested by the precision or min field width */
+        n = number_of_zeros_to_pad;
+        if (n > 0) {
+          if (str_l < str_m) {
+            size_t avail = str_m-str_l;
+            fast_memset(str+str_l, '0', (n>avail?avail:n));
+          }
+          str_l += n;
+        }
+      }
+   /* insert formatted string
+    * (or as-is conversion specifier for unknown conversions) */
+      { int n = str_arg_l - zero_padding_insertion_ind;
+        if (n > 0) {
+          if (str_l < str_m) {
+            size_t avail = str_m-str_l;
+            fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,
+                        (n>avail?avail:n));
+          }
+          str_l += n;
+        }
+      }
+   /* insert right padding */
+      if (justify_left) {          /* right blank padding to the field width */
+        int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
+        if (n > 0) {
+          if (str_l < str_m) {
+            size_t avail = str_m-str_l;
+            fast_memset(str+str_l, ' ', (n>avail?avail:n));
+          }
+          str_l += n;
+        }
+      }
+    }
+  }
+#if defined(NEED_SNPRINTF_ONLY)
+  va_end(ap);
+#endif
+  if (str_m > 0) { /* make sure the string is null-terminated
+                      even at the expense of overwriting the last character
+                      (shouldn't happen, but just in case) */
+    str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
+  }
+  /* Return the number of characters formatted (excluding trailing null
+   * character), that is, the number of characters that would have been
+   * written to the buffer if it were large enough.
+   *
+   * The value of str_l should be returned, but str_l is of unsigned type
+   * size_t, and snprintf is int, possibly leading to an undetected
+   * integer overflow, resulting in a negative return value, which is illegal.
+   * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
+   * Should errno be set to EOVERFLOW and EOF returned in this case???
+   */
+  return (int) str_l;
+}
+#endif
diff --git a/libtu/snprintf_2.2/snprintf.c b/libtu/snprintf_2.2/snprintf.c
new file mode 100644 (file)
index 0000000..8720be7
--- /dev/null
@@ -0,0 +1,1032 @@
+#include <stdarg.h>
+#define NEED_ASPRINTF
+#define NEED_VASPRINTF
+/*
+ * snprintf.c - a portable implementation of snprintf
+ *
+ * AUTHOR
+ *   Mark Martinec <mark.martinec@ijs.si>, April 1999.
+ *
+ *   Copyright 1999, Mark Martinec. All rights reserved.
+ *
+ * TERMS AND CONDITIONS
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the "Frontier Artistic License" which comes
+ *   with this Kit.
+ *
+ *   This program 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 Frontier Artistic License for more details.
+ *
+ *   You should have received a copy of the Frontier Artistic License
+ *   with this Kit in the file named LICENSE.txt .
+ *   If not, I'll be glad to provide one.
+ *
+ * FEATURES
+ * - careful adherence to specs regarding flags, field width and precision;
+ * - good performance for large string handling (large format, large
+ *   argument or large paddings). Performance is similar to system's sprintf
+ *   and in several cases significantly better (make sure you compile with
+ *   optimizations turned on, tell the compiler the code is strict ANSI
+ *   if necessary to give it more freedom for optimizations);
+ * - return value semantics per ISO/IEC 9899:1999 ("ISO C99");
+ * - written in standard ISO/ANSI C - requires an ANSI C compiler.
+ *
+ * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES
+ *
+ * This snprintf only supports the following conversion specifiers:
+ * s, c, d, u, o, x, X, p  (and synonyms: i, D, U, O - see below)
+ * with flags: '-', '+', ' ', '0' and '#'.
+ * An asterisk is supported for field width as well as precision.
+ *
+ * Length modifiers 'h' (short int), 'l' (long int),
+ * and 'll' (long long int) are supported.
+ * NOTE:
+ *   If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the
+ *   length modifier 'll' is recognized but treated the same as 'l',
+ *   which may cause argument value truncation! Defining
+ *   SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also
+ *   handles length modifier 'll'.  long long int is a language extension
+ *   which may not be portable.
+ *
+ * Conversion of numeric data (conversion specifiers d, u, o, x, X, p)
+ * with length modifiers (none or h, l, ll) is left to the system routine
+ * sprintf, but all handling of flags, field width and precision as well as
+ * c and s conversions is done very carefully by this portable routine.
+ * If a string precision (truncation) is specified (e.g. %.8s) it is
+ * guaranteed the string beyond the specified precision will not be referenced.
+ *
+ * Length modifiers h, l and ll are ignored for c and s conversions (data
+ * types wint_t and wchar_t are not supported).
+ *
+ * The following common synonyms for conversion characters are supported:
+ *   - i is a synonym for d
+ *   - D is a synonym for ld, explicit length modifiers are ignored
+ *   - U is a synonym for lu, explicit length modifiers are ignored
+ *   - O is a synonym for lo, explicit length modifiers are ignored
+ * The D, O and U conversion characters are nonstandard, they are supported
+ * for backward compatibility only, and should not be used for new code.
+ *
+ * The following is specifically NOT supported:
+ *   - flag ' (thousands' grouping character) is recognized but ignored
+ *   - numeric conversion specifiers: f, e, E, g, G and synonym F,
+ *     as well as the new a and A conversion specifiers
+ *   - length modifier 'L' (long double) and 'q' (quad - use 'll' instead)
+ *   - wide character/string conversions: lc, ls, and nonstandard
+ *     synonyms C and S
+ *   - writeback of converted string length: conversion character n
+ *   - the n$ specification for direct reference to n-th argument
+ *   - locales
+ *
+ * It is permitted for str_m to be zero, and it is permitted to specify NULL
+ * pointer for resulting string argument if str_m is zero (as per ISO C99).
+ *
+ * The return value is the number of characters which would be generated
+ * for the given input, excluding the trailing null. If this value
+ * is greater or equal to str_m, not all characters from the result
+ * have been stored in str, output bytes beyond the (str_m-1) -th character
+ * are discarded. If str_m is greater than zero it is guaranteed
+ * the resulting string will be null-terminated.
+ *
+ * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,
+ * but is different from some older and vendor implementations,
+ * and is also different from XPG, XSH5, SUSv2 specifications.
+ * For historical discussion on changes in the semantics and standards
+ * of snprintf see printf(3) man page in the Linux programmers manual.
+ *
+ * Routines asprintf and vasprintf return a pointer (in the ptr argument)
+ * to a buffer sufficiently large to hold the resulting string. This pointer
+ * should be passed to free(3) to release the allocated storage when it is
+ * no longer needed. If sufficient space cannot be allocated, these functions
+ * will return -1 and set ptr to be a NULL pointer. These two routines are a
+ * GNU C library extensions (glibc).
+ *
+ * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf,
+ * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1
+ * characters into the allocated output string, the last character in the
+ * allocated buffer then gets the terminating null. If the formatted string
+ * length (the return value) is greater than or equal to the str_m argument,
+ * the resulting string was truncated and some of the formatted characters
+ * were discarded. These routines present a handy way to limit the amount
+ * of allocated memory to some sane value.
+ *
+ * AVAILABILITY
+ *   http://www.ijs.si/software/snprintf/
+ *
+ * REVISION HISTORY
+ * 1999-04     V0.9  Mark Martinec
+ *             - initial version, some modifications after comparing printf
+ *               man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10,
+ *               and checking how Perl handles sprintf (differently!);
+ * 1999-04-09  V1.0  Mark Martinec <mark.martinec@ijs.si>
+ *             - added main test program, fixed remaining inconsistencies,
+ *               added optional (long long int) support;
+ * 1999-04-12  V1.1  Mark Martinec <mark.martinec@ijs.si>
+ *             - support the 'p' conversion (pointer to void);
+ *             - if a string precision is specified
+ *               make sure the string beyond the specified precision
+ *               will not be referenced (e.g. by strlen);
+ * 1999-04-13  V1.2  Mark Martinec <mark.martinec@ijs.si>
+ *             - support synonyms %D=%ld, %U=%lu, %O=%lo;
+ *             - speed up the case of long format string with few conversions;
+ * 1999-06-30  V1.3  Mark Martinec <mark.martinec@ijs.si>
+ *             - fixed runaway loop (eventually crashing when str_l wraps
+ *               beyond 2^31) while copying format string without
+ *               conversion specifiers to a buffer that is too short
+ *               (thanks to Edwin Young <edwiny@autonomy.com> for
+ *               spotting the problem);
+ *             - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR)
+ *               to snprintf.h
+ * 2000-02-14  V2.0 (never released) Mark Martinec <mark.martinec@ijs.si>
+ *             - relaxed license terms: The Artistic License now applies.
+ *               You may still apply the GNU GENERAL PUBLIC LICENSE
+ *               as was distributed with previous versions, if you prefer;
+ *             - changed REVISION HISTORY dates to use ISO 8601 date format;
+ *             - added vsnprintf (patch also independently proposed by
+ *               Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
+ * 2000-06-27  V2.1  Mark Martinec <mark.martinec@ijs.si>
+ *             - removed POSIX check for str_m<1; value 0 for str_m is
+ *               allowed by ISO C99 (and GNU C library 2.1) - (pointed out
+ *               on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie).
+ *               Besides relaxed license this change in standards adherence
+ *               is the main reason to bump up the major version number;
+ *             - added nonstandard routines asnprintf, vasnprintf, asprintf,
+ *               vasprintf that dynamically allocate storage for the
+ *               resulting string; these routines are not compiled by default,
+ *               see comments where NEED_V?ASN?PRINTF macros are defined;
+ *             - autoconf contributed by Caolan McNamara
+ * 2000-10-06  V2.2  Mark Martinec <mark.martinec@ijs.si>
+ *             - BUG FIX: the %c conversion used a temporary variable
+ *               that was no longer in scope when referenced,
+ *               possibly causing incorrect resulting character;
+ *             - BUG FIX: make precision and minimal field width unsigned
+ *               to handle huge values (2^31 <= n < 2^32) correctly;
+ *               also be more careful in the use of signed/unsigned/size_t
+ *               internal variables - probably more careful than many
+ *               vendor implementations, but there may still be a case
+ *               where huge values of str_m, precision or minimal field
+ *               could cause incorrect behaviour;
+ *             - use separate variables for signed/unsigned arguments,
+ *               and for short/int, long, and long long argument lengths
+ *               to avoid possible incompatibilities on certain
+ *               computer architectures. Also use separate variable
+ *               arg_sign to hold sign of a numeric argument,
+ *               to make code more transparent;
+ *             - some fiddling with zero padding and "0x" to make it
+ *               Linux compatible;
+ *             - systematically use macros fast_memcpy and fast_memset
+ *               instead of case-by-case hand optimization; determine some
+ *               breakeven string lengths for different architectures;
+ *             - terminology change: 'format' -> 'conversion specifier',
+ *               'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")',
+ *               'alternative form' -> 'alternate form',
+ *               'data type modifier' -> 'length modifier';
+ *             - several comments rephrased and new ones added;
+ *             - make compiler not complain about 'credits' defined but
+ *               not used;
+ */
+
+
+/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf.
+ *
+ * If HAVE_SNPRINTF is defined this module will not produce code for
+ * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well,
+ * causing this portable version of snprintf to be called portable_snprintf
+ * (and portable_vsnprintf).
+ */
+/* #define HAVE_SNPRINTF */
+
+/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and
+ * vsnprintf but you would prefer to use the portable routine(s) instead.
+ * In this case the portable routine is declared as portable_snprintf
+ * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf')
+ * is defined to expand to 'portable_v?snprintf' - see file snprintf.h .
+ * Defining this macro is only useful if HAVE_SNPRINTF is also defined,
+ * but does does no harm if defined nevertheless.
+ */
+/* #define PREFER_PORTABLE_SNPRINTF */
+
+/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support
+ * data type (long long int) and length modifier 'll' (e.g. %lld).
+ * If undefined, 'll' is recognized but treated as a single 'l'.
+ *
+ * If the system's sprintf does not handle 'll'
+ * the SNPRINTF_LONGLONG_SUPPORT must not be defined!
+ *
+ * This is off by default as (long long int) is a language extension.
+ */
+/* #define SNPRINTF_LONGLONG_SUPPORT */
+
+/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf.
+ * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly,
+ * otherwise both snprintf and vsnprintf routines will be defined
+ * and snprintf will be a simple wrapper around vsnprintf, at the expense
+ * of an extra procedure call.
+ */
+/* #define NEED_SNPRINTF_ONLY */
+
+/* Define NEED_V?ASN?PRINTF macros if you need library extension
+ * routines asprintf, vasprintf, asnprintf, vasnprintf respectively,
+ * and your system library does not provide them. They are all small
+ * wrapper routines around portable_vsnprintf. Defining any of the four
+ * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY
+ * and turns on PREFER_PORTABLE_SNPRINTF.
+ *
+ * Watch for name conflicts with the system library if these routines
+ * are already present there.
+ *
+ * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as
+ * specified by C99, to be able to traverse the same list of arguments twice.
+ * I don't know of any other standard and portable way of achieving the same.
+ * With some versions of gcc you may use __va_copy(). You might even get away
+ * with "ap2 = ap", in this case you must not call va_end(ap2) !
+ *   #define va_copy(ap2,ap) ap2 = ap
+ */
+#ifndef va_copy
+#define va_copy(ap2,ap) ap2 = ap 
+#endif
+
+/* #define NEED_ASPRINTF   */
+/* #define NEED_ASNPRINTF  */
+/* #define NEED_VASPRINTF  */
+/* #define NEED_VASNPRINTF */
+
+
+/* Define the following macros if desired:
+ *   SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE,
+ *   HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE,
+ *   DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE,
+ *   PERL_COMPATIBLE, PERL_BUG_COMPATIBLE,
+ *
+ * - For portable applications it is best not to rely on peculiarities
+ *   of a given implementation so it may be best not to define any
+ *   of the macros that select compatibility and to avoid features
+ *   that vary among the systems.
+ *
+ * - Selecting compatibility with more than one operating system
+ *   is not strictly forbidden but is not recommended.
+ *
+ * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE .
+ *
+ * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is
+ *   documented in a sprintf man page on a given operating system
+ *   and actually adhered to by the system's sprintf (but not on
+ *   most other operating systems). It may also refer to and enable
+ *   a behaviour that is declared 'undefined' or 'implementation specific'
+ *   in the man page but a given implementation behaves predictably
+ *   in a certain way.
+ *
+ * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf
+ *   that contradicts the sprintf man page on the same operating system.
+ *
+ * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE
+ *   conditionals take into account all idiosyncrasies of a particular
+ *   implementation, there may be other incompatibilities.
+ */
+
+
+\f
+/* ============================================= */
+/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */
+/* ============================================= */
+
+#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
+#define PORTABLE_SNPRINTF_VERSION_MINOR 2
+
+#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
+# if defined(NEED_SNPRINTF_ONLY)
+# undef NEED_SNPRINTF_ONLY
+# endif
+# if !defined(PREFER_PORTABLE_SNPRINTF)
+# define PREFER_PORTABLE_SNPRINTF
+# endif
+#endif
+
+#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
+#define SOLARIS_COMPATIBLE
+#endif
+
+#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
+#define HPUX_COMPATIBLE
+#endif
+
+#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
+#define DIGITAL_UNIX_COMPATIBLE
+#endif
+
+#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
+#define PERL_COMPATIBLE
+#endif
+
+#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
+#define LINUX_COMPATIBLE
+#endif
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+
+#ifdef isdigit
+#undef isdigit
+#endif
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+
+/* For copying strings longer or equal to 'breakeven_point'
+ * it is more efficient to call memcpy() than to do it inline.
+ * The value depends mostly on the processor architecture,
+ * but also on the compiler and its optimization capabilities.
+ * The value is not critical, some small value greater than zero
+ * will be just fine if you don't care to squeeze every drop
+ * of performance out of the code.
+ *
+ * Small values favor memcpy, large values favor inline code.
+ */
+#if defined(__alpha__) || defined(__alpha)
+#  define breakeven_point   2  /* AXP (DEC Alpha)     - gcc or cc or egcs */
+#endif
+#if defined(__i386__)  || defined(__i386)
+#  define breakeven_point  12  /* Intel Pentium/Linux - gcc 2.96 */
+#endif
+#if defined(__hppa)
+#  define breakeven_point  10  /* HP-PA               - gcc */
+#endif
+#if defined(__sparc__) || defined(__sparc)
+#  define breakeven_point  33  /* Sun Sparc 5         - gcc 2.8.1 */
+#endif
+
+/* some other values of possible interest: */
+/* #define breakeven_point  8 */  /* VAX 4000          - vaxc */
+/* #define breakeven_point 19 */  /* VAX 4000          - gcc 2.7.0 */
+
+#ifndef breakeven_point
+#  define breakeven_point   6  /* some reasonable one-size-fits-all value */
+#endif
+
+#define fast_memcpy(d,s,n) \
+  { register size_t nn = (size_t)(n); \
+    if (nn >= breakeven_point) memcpy((d), (s), nn); \
+    else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
+      register char *dd; register const char *ss; \
+      for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
+
+#define fast_memset(d,c,n) \
+  { register size_t nn = (size_t)(n); \
+    if (nn >= breakeven_point) memset((d), (int)(c), nn); \
+    else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
+      register char *dd; register const int cc=(int)(c); \
+      for (dd=(d); nn>0; nn--) *dd++ = cc; } }
+
+/* prototypes */
+
+#if defined(NEED_ASPRINTF)
+int asprintf   (char **ptr, const char *fmt, /*args*/ ...);
+#endif
+#if defined(NEED_VASPRINTF)
+int vasprintf  (char **ptr, const char *fmt, va_list ap);
+#endif
+#if defined(NEED_ASNPRINTF)
+int asnprintf  (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
+#endif
+#if defined(NEED_VASNPRINTF)
+int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);
+#endif
+
+#if defined(HAVE_SNPRINTF)
+/* declare our portable snprintf  routine under name portable_snprintf  */
+/* declare our portable vsnprintf routine under name portable_vsnprintf */
+#else
+/* declare our portable routines under names snprintf and vsnprintf */
+#define portable_snprintf snprintf
+#if !defined(NEED_SNPRINTF_ONLY)
+#define portable_vsnprintf vsnprintf
+#endif
+#endif
+
+#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
+#if !defined(NEED_SNPRINTF_ONLY)
+int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
+#endif
+#endif
+
+/* declarations */
+
+static char credits[] = "\n\
+@(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\
+@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
+@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
+
+#if defined(NEED_ASPRINTF)
+int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
+  va_list ap;
+  size_t str_m;
+  int str_l;
+
+  *ptr = NULL;
+  va_start(ap, fmt);                            /* measure the required size */
+  str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
+  va_end(ap);
+  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
+  *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
+  if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
+  else {
+    int str_l2;
+    va_start(ap, fmt);
+    str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
+    va_end(ap);
+    assert(str_l2 == str_l);
+  }
+  return str_l;
+}
+#endif
+
+#if defined(NEED_VASPRINTF)
+int vasprintf(char **ptr, const char *fmt, va_list ap) {
+  size_t str_m;
+  int str_l;
+
+  *ptr = NULL;
+  { va_list ap2;
+    va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
+    str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
+    va_end(ap2);
+  }
+  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
+  *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
+  if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
+  else {
+    int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
+    assert(str_l2 == str_l);
+  }
+  return str_l;
+}
+#endif
+
+#if defined(NEED_ASNPRINTF)
+int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
+  va_list ap;
+  int str_l;
+
+  *ptr = NULL;
+  va_start(ap, fmt);                            /* measure the required size */
+  str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
+  va_end(ap);
+  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
+  if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
+  /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
+  if (str_m == 0) {  /* not interested in resulting string, just return size */
+  } else {
+    *ptr = (char *) malloc(str_m);
+    if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
+    else {
+      int str_l2;
+      va_start(ap, fmt);
+      str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
+      va_end(ap);
+      assert(str_l2 == str_l);
+    }
+  }
+  return str_l;
+}
+#endif
+
+#if defined(NEED_VASNPRINTF)
+int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
+  int str_l;
+
+  *ptr = NULL;
+  { va_list ap2;
+    va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
+    str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
+    va_end(ap2);
+  }
+  assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
+  if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
+  /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
+  if (str_m == 0) {  /* not interested in resulting string, just return size */
+  } else {
+    *ptr = (char *) malloc(str_m);
+    if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
+    else {
+      int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
+      assert(str_l2 == str_l);
+    }
+  }
+  return str_l;
+}
+#endif
+
+/*
+ * If the system does have snprintf and the portable routine is not
+ * specifically required, this module produces no code for snprintf/vsnprintf.
+ */
+#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+
+#if !defined(NEED_SNPRINTF_ONLY)
+int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
+  va_list ap;
+  int str_l;
+
+  va_start(ap, fmt);
+  str_l = portable_vsnprintf(str, str_m, fmt, ap);
+  va_end(ap);
+  return str_l;
+}
+#endif
+
+#if defined(NEED_SNPRINTF_ONLY)
+int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
+#else
+int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {
+#endif
+
+#if defined(NEED_SNPRINTF_ONLY)
+  va_list ap;
+#endif
+  size_t str_l = 0;
+  const char *p = fmt;
+
+/* In contrast with POSIX, the ISO C99 now says
+ * that str can be NULL and str_m can be 0.
+ * This is more useful than the old:  if (str_m < 1) return -1; */
+
+#if defined(NEED_SNPRINTF_ONLY)
+  va_start(ap, fmt);
+#endif
+  if (!p) p = "";
+  while (*p) {
+    if (*p != '%') {
+   /* if (str_l < str_m) str[str_l++] = *p++;    -- this would be sufficient */
+   /* but the following code achieves better performance for cases
+    * where format string is long and contains few conversions */
+      const char *q = strchr(p+1,'%');
+      size_t n = !q ? strlen(p) : (size_t)(q-p);
+      if (str_l < str_m) {
+        size_t avail = str_m-str_l;
+        fast_memcpy(str+str_l, p, (n>avail?avail:n));
+      }
+      p += n; str_l += n;
+    } else {
+      const char *starting_p;
+      size_t min_field_width = 0, precision = 0;
+      int zero_padding = 0, precision_specified = 0, justify_left = 0;
+      int alternate_form = 0, force_sign = 0;
+      int space_for_positive = 1; /* If both the ' ' and '+' flags appear,
+                                     the ' ' flag should be ignored. */
+      char length_modifier = '\0';            /* allowed values: \0, h, l, L */
+      char tmp[32];/* temporary buffer for simple numeric->string conversion */
+
+      const char *str_arg;      /* string address in case of string argument */
+      size_t str_arg_l;         /* natural field width of arg without padding
+                                   and sign */
+      unsigned char uchar_arg;
+        /* unsigned char argument value - only defined for c conversion.
+           N.B. standard explicitly states the char argument for
+           the c conversion is unsigned */
+
+      size_t number_of_zeros_to_pad = 0;
+        /* number of zeros to be inserted for numeric conversions
+           as required by the precision or minimal field width */
+
+      size_t zero_padding_insertion_ind = 0;
+        /* index into tmp where zero padding is to be inserted */
+
+      char fmt_spec = '\0';
+        /* current conversion specifier character */
+
+      str_arg = credits;/* just to make compiler happy (defined but not used)*/
+      str_arg = NULL;
+      starting_p = p; p++;  /* skip '%' */
+   /* parse flags */
+      while (*p == '0' || *p == '-' || *p == '+' ||
+             *p == ' ' || *p == '#' || *p == '\'') {
+        switch (*p) {
+        case '0': zero_padding = 1; break;
+        case '-': justify_left = 1; break;
+        case '+': force_sign = 1; space_for_positive = 0; break;
+        case ' ': force_sign = 1;
+     /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
+#ifdef PERL_COMPATIBLE
+     /* ... but in Perl the last of ' ' and '+' applies */
+                  space_for_positive = 1;
+#endif
+                  break;
+        case '#': alternate_form = 1; break;
+        case '\'': break;
+        }
+        p++;
+      }
+   /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
+
+   /* parse field width */
+      if (*p == '*') {
+        int j;
+        p++; j = va_arg(ap, int);
+        if (j >= 0) min_field_width = j;
+        else { min_field_width = -j; justify_left = 1; }
+      } else if (isdigit((int)(*p))) {
+        /* size_t could be wider than unsigned int;
+           make sure we treat argument like common implementations do */
+        unsigned int uj = *p++ - '0';
+        while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
+        min_field_width = uj;
+      }
+   /* parse precision */
+      if (*p == '.') {
+        p++; precision_specified = 1;
+        if (*p == '*') {
+          int j = va_arg(ap, int);
+          p++;
+          if (j >= 0) precision = j;
+          else {
+            precision_specified = 0; precision = 0;
+         /* NOTE:
+          *   Solaris 2.6 man page claims that in this case the precision
+          *   should be set to 0.  Digital Unix 4.0, HPUX 10 and BSD man page
+          *   claim that this case should be treated as unspecified precision,
+          *   which is what we do here.
+          */
+          }
+        } else if (isdigit((int)(*p))) {
+          /* size_t could be wider than unsigned int;
+             make sure we treat argument like common implementations do */
+          unsigned int uj = *p++ - '0';
+          while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
+          precision = uj;
+        }
+      }
+   /* parse 'h', 'l' and 'll' length modifiers */
+      if (*p == 'h' || *p == 'l') {
+        length_modifier = *p; p++;
+        if (length_modifier == 'l' && *p == 'l') {   /* double l = long long */
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+          length_modifier = '2';                  /* double l encoded as '2' */
+#else
+          length_modifier = 'l';                 /* treat it as a single 'l' */
+#endif
+          p++;
+        }
+      }
+      fmt_spec = *p;
+   /* common synonyms: */
+      switch (fmt_spec) {
+      case 'i': fmt_spec = 'd'; break;
+      case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
+      case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
+      case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
+      default: break;
+      }
+   /* get parameter value, do initial processing */
+      switch (fmt_spec) {
+      case '%': /* % behaves similar to 's' regarding flags and field widths */
+      case 'c': /* c behaves similar to 's' regarding flags and field widths */
+      case 's':
+        length_modifier = '\0';          /* wint_t and wchar_t not supported */
+     /* the result of zero padding flag with non-numeric conversion specifier*/
+     /* is undefined. Solaris and HPUX 10 does zero padding in this case,    */
+     /* Digital Unix and Linux does not. */
+#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
+        zero_padding = 0;    /* turn zero padding off for string conversions */
+#endif
+        str_arg_l = 1;
+        switch (fmt_spec) {
+        case '%':
+          str_arg = p; break;
+        case 'c': {
+          int j = va_arg(ap, int);
+          uchar_arg = (unsigned char) j;   /* standard demands unsigned char */
+          str_arg = (const char *) &uchar_arg;
+          break;
+        }
+        case 's':
+          str_arg = va_arg(ap, const char *);
+          if (!str_arg) str_arg_l = 0;
+       /* make sure not to address string beyond the specified precision !!! */
+          else if (!precision_specified) str_arg_l = strlen(str_arg);
+       /* truncate string if necessary as requested by precision */
+          else if (precision == 0) str_arg_l = 0;
+          else {
+       /* memchr on HP does not like n > 2^31  !!! */
+            const char *q = memchr(str_arg, '\0',
+                             precision <= 0x7fffffff ? precision : 0x7fffffff);
+            str_arg_l = !q ? precision : (size_t)(q-str_arg);
+          }
+          break;
+        default: break;
+        }
+        break;
+      case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
+        /* NOTE: the u, o, x, X and p conversion specifiers imply
+                 the value is unsigned;  d implies a signed value */
+
+        int arg_sign = 0;
+          /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
+            +1 if greater than zero (or nonzero for unsigned arguments),
+            -1 if negative (unsigned argument is never negative) */
+
+        int int_arg = 0;  unsigned int uint_arg = 0;
+          /* only defined for length modifier h, or for no length modifiers */
+
+        long int long_arg = 0;  unsigned long int ulong_arg = 0;
+          /* only defined for length modifier l */
+
+        void *ptr_arg = NULL;
+          /* pointer argument value -only defined for p conversion */
+
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+        long long int long_long_arg = 0;
+        unsigned long long int ulong_long_arg = 0;
+          /* only defined for length modifier ll */
+#endif
+        if (fmt_spec == 'p') {
+        /* HPUX 10: An l, h, ll or L before any other conversion character
+         *   (other than d, i, u, o, x, or X) is ignored.
+         * Digital Unix:
+         *   not specified, but seems to behave as HPUX does.
+         * Solaris: If an h, l, or L appears before any other conversion
+         *   specifier (other than d, i, u, o, x, or X), the behavior
+         *   is undefined. (Actually %hp converts only 16-bits of address
+         *   and %llp treats address as 64-bit data which is incompatible
+         *   with (void *) argument on a 32-bit system).
+         */
+#ifdef SOLARIS_COMPATIBLE
+#  ifdef SOLARIS_BUG_COMPATIBLE
+          /* keep length modifiers even if it represents 'll' */
+#  else
+          if (length_modifier == '2') length_modifier = '\0';
+#  endif
+#else
+          length_modifier = '\0';
+#endif
+          ptr_arg = va_arg(ap, void *);
+          if (ptr_arg != NULL) arg_sign = 1;
+        } else if (fmt_spec == 'd') {  /* signed */
+          switch (length_modifier) {
+          case '\0':
+          case 'h':
+         /* It is non-portable to specify a second argument of char or short
+          * to va_arg, because arguments seen by the called function
+          * are not char or short.  C converts char and short arguments
+          * to int before passing them to a function.
+          */
+            int_arg = va_arg(ap, int);
+            if      (int_arg > 0) arg_sign =  1;
+            else if (int_arg < 0) arg_sign = -1;
+            break;
+          case 'l':
+            long_arg = va_arg(ap, long int);
+            if      (long_arg > 0) arg_sign =  1;
+            else if (long_arg < 0) arg_sign = -1;
+            break;
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+          case '2':
+            long_long_arg = va_arg(ap, long long int);
+            if      (long_long_arg > 0) arg_sign =  1;
+            else if (long_long_arg < 0) arg_sign = -1;
+            break;
+#endif
+          }
+        } else {  /* unsigned */
+          switch (length_modifier) {
+          case '\0':
+          case 'h':
+            uint_arg = va_arg(ap, unsigned int);
+            if (uint_arg) arg_sign = 1;
+            break;
+          case 'l':
+            ulong_arg = va_arg(ap, unsigned long int);
+            if (ulong_arg) arg_sign = 1;
+            break;
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+          case '2':
+            ulong_long_arg = va_arg(ap, unsigned long long int);
+            if (ulong_long_arg) arg_sign = 1;
+            break;
+#endif
+          }
+        }
+        str_arg = tmp; str_arg_l = 0;
+     /* NOTE:
+      *   For d, i, u, o, x, and X conversions, if precision is specified,
+      *   the '0' flag should be ignored. This is so with Solaris 2.6,
+      *   Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
+      */
+#ifndef PERL_COMPATIBLE
+        if (precision_specified) zero_padding = 0;
+#endif
+        if (fmt_spec == 'd') {
+          if (force_sign && arg_sign >= 0)
+            tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
+         /* leave negative numbers for sprintf to handle,
+            to avoid handling tricky cases like (short int)(-32768) */
+#ifdef LINUX_COMPATIBLE
+        } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
+          tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
+#endif
+        } else if (alternate_form) {
+          if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )
+            { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }
+         /* alternate form should have no effect for p conversion, but ... */
+#ifdef HPUX_COMPATIBLE
+          else if (fmt_spec == 'p'
+         /* HPUX 10: for an alternate form of p conversion,
+          *          a nonzero result is prefixed by 0x. */
+#ifndef HPUX_BUG_COMPATIBLE
+         /* Actually it uses 0x prefix even for a zero value. */
+                   && arg_sign != 0
+#endif
+                  ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }
+#endif
+        }
+        zero_padding_insertion_ind = str_arg_l;
+        if (!precision_specified) precision = 1;   /* default precision is 1 */
+        if (precision == 0 && arg_sign == 0
+#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
+            && fmt_spec != 'p'
+         /* HPUX 10 man page claims: With conversion character p the result of
+          * converting a zero value with a precision of zero is a null string.
+          * Actually HP returns all zeroes, and Linux returns "(nil)". */
+#endif
+        ) {
+         /* converted to null string */
+         /* When zero value is formatted with an explicit precision 0,
+            the resulting formatted string is empty (d, i, u, o, x, X, p).   */
+        } else {
+          char f[5]; int f_l = 0;
+          f[f_l++] = '%';    /* construct a simple format string for sprintf */
+          if (!length_modifier) { }
+          else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }
+          else f[f_l++] = length_modifier;
+          f[f_l++] = fmt_spec; f[f_l++] = '\0';
+          if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
+          else if (fmt_spec == 'd') {  /* signed */
+            switch (length_modifier) {
+            case '\0':
+            case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg);  break;
+            case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+            case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;
+#endif
+            }
+          } else {  /* unsigned */
+            switch (length_modifier) {
+            case '\0':
+            case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg);  break;
+            case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+            case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;
+#endif
+            }
+          }
+         /* include the optional minus sign and possible "0x"
+            in the region before the zero padding insertion point */
+          if (zero_padding_insertion_ind < str_arg_l &&
+              tmp[zero_padding_insertion_ind] == '-') {
+            zero_padding_insertion_ind++;
+          }
+          if (zero_padding_insertion_ind+1 < str_arg_l &&
+              tmp[zero_padding_insertion_ind]   == '0' &&
+             (tmp[zero_padding_insertion_ind+1] == 'x' ||
+              tmp[zero_padding_insertion_ind+1] == 'X') ) {
+            zero_padding_insertion_ind += 2;
+          }
+        }
+        { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
+          if (alternate_form && fmt_spec == 'o'
+#ifdef HPUX_COMPATIBLE                                  /* ("%#.o",0) -> ""  */
+              && (str_arg_l > 0)
+#endif
+#ifdef DIGITAL_UNIX_BUG_COMPATIBLE                      /* ("%#o",0) -> "00" */
+#else
+              /* unless zero is already the first character */
+              && !(zero_padding_insertion_ind < str_arg_l
+                   && tmp[zero_padding_insertion_ind] == '0')
+#endif
+          ) {        /* assure leading zero for alternate-form octal numbers */
+            if (!precision_specified || precision < num_of_digits+1) {
+             /* precision is increased to force the first character to be zero,
+                except if a zero value is formatted with an explicit precision
+                of zero */
+              precision = num_of_digits+1; precision_specified = 1;
+            }
+          }
+       /* zero padding to specified precision? */
+          if (num_of_digits < precision) 
+            number_of_zeros_to_pad = precision - num_of_digits;
+        }
+     /* zero padding to specified minimal field width? */
+        if (!justify_left && zero_padding) {
+          int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
+          if (n > 0) number_of_zeros_to_pad += n;
+        }
+        break;
+      }
+      default: /* unrecognized conversion specifier, keep format string as-is*/
+        zero_padding = 0;  /* turn zero padding off for non-numeric convers. */
+#ifndef DIGITAL_UNIX_COMPATIBLE
+        justify_left = 1; min_field_width = 0;                /* reset flags */
+#endif
+#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
+     /* keep the entire format string unchanged */
+        str_arg = starting_p; str_arg_l = p - starting_p;
+     /* well, not exactly so for Linux, which does something inbetween,
+      * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y"  */
+#else
+     /* discard the unrecognized conversion, just keep *
+      * the unrecognized conversion character          */
+        str_arg = p; str_arg_l = 0;
+#endif
+        if (*p) str_arg_l++;  /* include invalid conversion specifier unchanged
+                                 if not at end-of-string */
+        break;
+      }
+      if (*p) p++;      /* step over the just processed conversion specifier */
+   /* insert padding to the left as requested by min_field_width;
+      this does not include the zero padding in case of numerical conversions*/
+      if (!justify_left) {                /* left padding with blank or zero */
+        int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
+        if (n > 0) {
+          if (str_l < str_m) {
+            ssize_t avail = str_m-str_l;
+            fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));
+          }
+          str_l += n;
+        }
+      }
+   /* zero padding as requested by the precision or by the minimal field width
+    * for numeric conversions required? */
+      if (number_of_zeros_to_pad <= 0) {
+     /* will not copy first part of numeric right now, *
+      * force it to be copied later in its entirety    */
+        zero_padding_insertion_ind = 0;
+      } else {
+     /* insert first part of numerics (sign or '0x') before zero padding */
+        int n = zero_padding_insertion_ind;
+        if (n > 0) {
+          if (str_l < str_m) {
+            ssize_t avail = str_m-str_l;
+            fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
+          }
+          str_l += n;
+        }
+     /* insert zero padding as requested by the precision or min field width */
+        n = number_of_zeros_to_pad;
+        if (n > 0) {
+          if (str_l < str_m) {
+            ssize_t avail = str_m-str_l;
+            fast_memset(str+str_l, '0', (n>avail?avail:n));
+          }
+          str_l += n;
+        }
+      }
+   /* insert formatted string
+    * (or as-is conversion specifier for unknown conversions) */
+      { int n = str_arg_l - zero_padding_insertion_ind;
+        if (n > 0) {
+          if (str_l < str_m) {
+            ssize_t avail = str_m-str_l;
+            fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,
+                        (n>avail?avail:n));
+          }
+          str_l += n;
+        }
+      }
+   /* insert right padding */
+      if (justify_left) {          /* right blank padding to the field width */
+        int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
+        if (n > 0) {
+          if (str_l < str_m) {
+            ssize_t avail = str_m-str_l;
+            fast_memset(str+str_l, ' ', (n>avail?avail:n));
+          }
+          str_l += n;
+        }
+      }
+    }
+  }
+#if defined(NEED_SNPRINTF_ONLY)
+  va_end(ap);
+#endif
+  if (str_m > 0) { /* make sure the string is null-terminated
+                      even at the expense of overwriting the last character
+                      (shouldn't happen, but just in case) */
+    str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
+  }
+  /* Return the number of characters formatted (excluding trailing null
+   * character), that is, the number of characters that would have been
+   * written to the buffer if it were large enough.
+   *
+   * The value of str_l should be returned, but str_l is of unsigned type
+   * size_t, and snprintf is int, possibly leading to an undetected
+   * integer overflow, resulting in a negative return value, which is illegal.
+   * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
+   * Should errno be set to EOVERFLOW and EOF returned in this case???
+   */
+  return (int) str_l;
+}
+#endif
diff --git a/libtu/snprintf_2.2/snprintf.h b/libtu/snprintf_2.2/snprintf.h
new file mode 100644 (file)
index 0000000..70ec841
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef _PORTABLE_SNPRINTF_H_
+#define _PORTABLE_SNPRINTF_H_
+
+#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
+#define PORTABLE_SNPRINTF_VERSION_MINOR 2
+
+#ifdef HAVE_SNPRINTF
+#include <stdio.h>
+#else
+extern int snprintf(char *, size_t, const char *, /*args*/ ...);
+extern int vsnprintf(char *, size_t, const char *, va_list);
+#endif
+
+#if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF)
+extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
+extern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
+#define snprintf  portable_snprintf
+#define vsnprintf portable_vsnprintf
+#endif
+
+extern int asprintf  (char **ptr, const char *fmt, /*args*/ ...);
+extern int vasprintf (char **ptr, const char *fmt, va_list ap);
+extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
+extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap);
+
+#endif
diff --git a/libtu/snprintf_2.2/test.c b/libtu/snprintf_2.2/test.c
new file mode 100644 (file)
index 0000000..5fddd82
--- /dev/null
@@ -0,0 +1,689 @@
+/*
+ * test.c - test a portable implementation of snprintf
+ *
+ * AUTHOR
+ *   Mark Martinec <mark.martinec@ijs.si>, April 1999.
+ *
+ *   Copyright 1999, Mark Martinec. All rights reserved.
+ *
+ * TERMS AND CONDITIONS
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the "Frontier Artistic License" which comes
+ *   with this Kit.
+ *
+ *   This program 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 Frontier Artistic License for more details.
+ *
+ *   You should have received a copy of the Frontier Artistic License
+ *   with this Kit in the file named LICENSE.txt .
+ *   If not, I'll be glad to provide one.
+ *
+ * NOTE:  This test program is a QUICK and DIRTY tool
+ * =====  used while testing and benchmarking my portable snprintf.
+ *        Certain data types are not fully supported, certain test
+ *        cases were fabricated during testing by modifying the code
+ *        or running it by specifying test parameters in the command line.
+ *
+ *        You are on your own if you want to use this test program!
+ */
+
+/* If no command arguments are specified do the exhaustive test.
+ * This takes a long time. You may want to reduce the fw and fp
+ * upper limits in the for loops.
+ * You may also reduce the number of test elements in the array iargs.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <assert.h>
+#include <time.h>
+
+#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
+# if defined(NEED_SNPRINTF_ONLY)
+# undef NEED_SNPRINTF_ONLY
+# endif
+# if !defined(PREFER_PORTABLE_SNPRINTF)
+# define PREFER_PORTABLE_SNPRINTF
+# endif
+#endif
+
+#ifdef HAVE_SNPRINTF
+extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
+#else
+extern int snprintf(char *, size_t, const char *, /*args*/ ...);
+extern int vsnprintf(char *, size_t, const char *, va_list);
+#endif
+
+#ifndef HAVE_SNPRINTF
+#define portable_snprintf  snprintf
+#define portable_vsnprintf vsnprintf
+#endif
+
+#ifndef CLOCKS_PER_SEC
+#define CLOCKS_PER_SEC 100
+#endif
+
+#define min(a,b) ((a)<(b) ? (a) : (b))
+#define max(a,b) ((a)>(b) ? (a) : (b))
+
+extern int asprintf  (char **ptr, const char *fmt, /*args*/ ...);
+extern int vasprintf (char **ptr, const char *fmt, va_list ap);
+extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
+extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap);
+
+#ifndef NEED_SNPRINTF_ONLY
+int wrap_vsnprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
+int wrap_vsnprintf(char *str, size_t str_m, const char *fmt, ...) {
+  va_list ap;
+  int str_l;
+
+  va_start(ap, fmt);
+  str_l = vsnprintf(str, str_m, fmt, ap);
+  va_end(ap);
+  return str_l;
+}
+#endif
+
+#ifdef NEED_VASPRINTF
+int wrap_vasprintf(char **ptr, const char *fmt, /*args*/ ...);
+int wrap_vasprintf(char **ptr, const char *fmt, ...) {
+  va_list ap;
+  int str_l;
+
+  va_start(ap, fmt);
+  str_l = vasprintf(ptr, fmt, ap);
+  va_end(ap);
+  return str_l;
+}
+#endif
+
+#ifdef NEED_VASNPRINTF
+int wrap_vasnprintf(char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
+int wrap_vasnprintf(char **ptr, size_t str_m, const char *fmt, ...) {
+  va_list ap;
+  int str_l;
+
+  va_start(ap, fmt);
+  str_l = vasnprintf(ptr, str_m, fmt, ap);
+  va_end(ap);
+  return str_l;
+}
+#endif
+
+int main(int argc, char *argv[]) {
+  char str1[256], str2[256];
+#ifdef HAVE_SNPRINTF
+  char str3[256];
+#endif
+  int len1, len2, len3;
+  int bad = 0;
+  size_t str_m = 20; /* declared str size */
+
+  if (0) {
+ /* benchmarking */
+    const int cnt = 100000;
+    size_t size;
+    char str[40000];
+    time_t t0,t;
+    int j,len,l1,l2;
+    char *p;
+    int breakpoint;
+
+    size = 18000;
+
+    printf("\n\nsize = %d\n", (int)size);
+    p = malloc(size); assert(p);
+    memset(p,'h',size); p[size-1] = '\0';
+    t0 = clock();
+
+    printf("\ndetermine breakeven point to see when it is worth\n");
+    printf("calling memcpy and when to do inline string copy\n");
+    printf("str_l, memcpy, inline\n");
+    for (breakpoint=0; breakpoint<=35; breakpoint++) {
+      register size_t nnn = (size_t)breakpoint;
+      printf("%5ld", nnn);
+      for (j=10*cnt; j>0; j--) memcpy(str, p, nnn);
+      t = clock(); printf(" %1.3f", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+      for (j=10*cnt; j>0; j--) {
+        register size_t nn = (size_t)breakpoint;
+        if (nn > 0) {
+          register char *dd; register const char *ss;
+          for (ss=p, dd=str; nn>0; nn--) *dd++ = *ss++;
+        }
+      }
+      t = clock(); printf(" %1.3f\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    }
+
+    printf("\nmeasuring time to SKIP a long format with no conversions\n");
+    p[0] = '%'; p[1] = 's';
+    for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p,"1234567890");
+    t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    for (j=cnt; j>0; j--) l2=portable_snprintf(str,(size_t)8,p,"1234567890");
+    t = clock(); printf("t_port     = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    assert(l1==l2);
+    p[0] = p[1] = 'h';
+
+    printf("\nmeasuring time to copy a long format with no conversions\n");
+    for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p);
+    t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),p);
+    t = clock(); printf("t_port     = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    assert(l1==l2);
+    for (j=cnt; j>0; j--) sprintf(str,p);
+    t = clock(); printf("t_sys      = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+    printf("\nmeasuring time to copy a long format with one conversion\n");
+    p[size-10] = '%';
+    for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p);
+    t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),p);
+    t = clock(); printf("t_port     = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    assert(l1==l2);
+    for (j=cnt; j>0; j--) sprintf(str,p);
+    t = clock(); printf("t_sys      = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+    printf("\nmeasuring string argument copy speed\n");
+    for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%.18000s",p);
+    t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%.18000s",p);
+    t = clock(); printf("t_port     = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    assert(l1==l2);
+    for (j=cnt; j>0; j--) sprintf(str,"%.18000s",p);
+    t = clock(); printf("t_sys      = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+    printf("\nmeasuring left padding speed\n");
+    p[0] = '\0';
+    for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%-18000s",p);
+    t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%-18000s",p);
+    t = clock(); printf("t_port     = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    assert(l1==l2);
+    for (j=cnt; j>0; j--) sprintf(str,"%-18000s",p);
+    t = clock(); printf("t_sys      = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+    printf("\nmeasuring right padding speed\n");
+    p[0] = '\0';
+    for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%18000s",p);
+    t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%18000s",p);
+    t = clock(); printf("t_port     = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    assert(l1==l2);
+    for (j=cnt; j>0; j--) sprintf(str,"%18000s",p);
+    t = clock(); printf("t_sys      = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+    printf("\nmeasuring zero padding speed\n");
+    for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%018000d",1);
+    t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%018000d",1);
+    t = clock(); printf("t_port     = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+    assert(l1==l2);
+    for (j=cnt; j>0; j--) sprintf(str,"%018000d",1);
+    t = clock(); printf("t_sys      = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+    printf("\nmeasuring system's sprintf to efficiently handle truncated strings\n");
+    memset(p,'h',size); p[size-1] = '\0';
+    t0 = clock();
+    for (j=cnt; j>0; j--) len = strlen(p);
+    printf("len = %d\n", len);
+    t = clock(); printf("t_strlen = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+/* test if the system sprintf scans the whole string (e.g. by strlen)
+ * before recognizing this was a bad idea since the format specified
+ * a truncated string precision, e.g. "%.8s" .
+ */
+    for (j=cnt; j>0; j--) sprintf(str,"%.2s",p);
+    t = clock(); printf("t_sys    = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+#ifdef HAVE_SNPRINTF
+    for (j=cnt; j>0; j--) snprintf(str,sizeof(str),"%.2s",p);
+    t = clock(); printf("t_sys    = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+#endif
+    for (j=cnt; j>0; j--) portable_snprintf(str,sizeof(str),"%.2s",p);
+    t = clock(); printf("t_port   = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t;
+
+    free(p);
+    return 0;
+  }
+
+
+/* preliminary halfhearted test */
+{
+    const char fmt[] = "Bla%.4s%05iHE%%%-50sTail";
+    char *ptr4=0, *ptr5=0, *ptr6=0, *ptr7=0;
+    int len1f;
+    char str_full[256];
+
+    printf("\npreliminary test: snprintf\n");
+    len1  = snprintf(str1, str_m, fmt, "abcdef",-12,"str");
+    len1f = snprintf(str_full, sizeof(str_full), fmt, "abcdef",-12,"str");
+    assert(len1f==len1);
+    assert(memcmp(str1,str_full,min(len1,str_m-1)) == 0);
+    assert(str1[str_m-1] == '\0');
+    assert(str_full[sizeof(str_full)-1] == '\0');
+
+#ifndef NEED_SNPRINTF_ONLY
+    printf("preliminary test: vsnprintf\n");
+    len2 = wrap_vsnprintf(str2, str_m, fmt, "abcdef",-12,"str");
+    assert(len2==len1);
+    assert(memcmp(str1,str2,min(len1+1,str_m)) == 0);
+    assert(str2[str_m-1] == '\0');
+#endif
+
+#ifdef NEED_ASPRINTF
+    printf("preliminary test: asprintf\n");
+    len4 = asprintf(&ptr4, fmt, "abcdef",-12,"str");
+    assert(ptr4);
+    assert(len4==len1);
+    assert(memcmp(str_full,ptr4,min(len4+1,sizeof(str_full))) == 0);
+    assert(ptr4[len4] == '\0');
+#endif
+
+#ifdef NEED_ASNPRINTF
+    printf("preliminary test: asnprintf\n");
+    len5 = asnprintf(&ptr5, str_m, fmt, "abcdef",-12,"str");
+    assert(ptr5);
+    assert(len5==len1);
+    assert(memcmp(str1,ptr5,min(len5+1,str_m)) == 0);
+    assert(ptr5[len5] == '\0');
+#endif
+
+#ifdef NEED_VASPRINTF
+    printf("preliminary test: vasprintf\n");
+    len6 = wrap_vasprintf(&ptr6, fmt, "abcdef",-12,"str");
+    assert(ptr6);
+    assert(len6==len1);
+    assert(memcmp(str_full,ptr6,min(len6+1,sizeof(str_full))) == 0);
+    assert(ptr6[len6] == '\0');
+#endif
+
+#ifdef NEED_VASNPRINTF
+    printf("preliminary test: vasnprintf\n");
+    len7 = wrap_vasnprintf(&ptr7, str_m, fmt, "abcdef",-12,"str");
+    assert(ptr7);
+    assert(len7==len1);
+    assert(memcmp(str1,ptr7,min(len7+1,str_m)) == 0);
+    assert(ptr7[len7] == '\0');
+#endif
+
+    if (ptr4) free(ptr4);
+    if (ptr5) free(ptr5);
+    if (ptr6) free(ptr6);
+    if (ptr7) free(ptr7);
+}
+
+/* second preliminary halfhearted test */
+{
+    printf("\nsecond preliminary test:\n");
+
+    printf("test 0a\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "");
+    len2 = sprintf (str2,               "");
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 0b\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "YK");
+    len2 = sprintf (str2,               "YK");
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 1\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%+d",0);
+    len2 = sprintf (str2,               "%+d",0);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 2\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%.2147483647s", "13");
+    len2 = sprintf (str2,               "%.2147483647s", "13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 3a\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%.2147483648s", "13");
+    len2 = sprintf (str2,               "%.2147483648s", "13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 3b\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%.2147483649s", "13");
+    len2 = sprintf (str2,               "%.2147483649s", "13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 4\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%-.2147483647s", "13");
+    len2 = sprintf (str2,               "%-.2147483647s", "13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 5\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%-.2147483648s", "13");
+    len2 = sprintf (str2,               "%-.2147483648s", "13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 6\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%.4294967295s", "13");
+    len2 = sprintf (str2,               "%.4294967295s", "13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 7\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%.4294967296s", "13");
+    len2 = sprintf (str2,               "%.4294967296s", "13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+
+    printf("test 12\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%.*s", 2147483647,"13");
+    len2 = sprintf (str2,               "%.*s", 2147483647,"13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 13\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%.*s", 2147483648U,"13");
+    len2 = sprintf (str2,               "%.*s", 2147483648U,"13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 14\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%-.*s", 2147483647,"13");
+    len2 = sprintf (str2,               "%-.*s", 2147483647,"13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 15\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%-.*s", 2147483648U,"13");
+    len2 = sprintf (str2,               "%-.*s", 2147483648U,"13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 16\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%.*s", 4294967295U,"13");
+    len2 = sprintf (str2,               "%.*s", 4294967295U,"13");
+    printf("len1=%d, len2=%d\n", len1,len2);
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 17\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%.*s", 4294967296U,"13");
+/*  len2 = sprintf (str2,               "%.*s", 4294967296U,"13"); */ /* core dumps on HPUX */
+/*  assert(len1==len2);
+ *  assert(memcmp(str1,str2,(size_t)len1) == 0);
+ */
+
+
+    printf("test 95\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%c",'A');
+    len2 = sprintf (str2,               "%c",'A');
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 96\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%10c",'A');
+    len2 = sprintf (str2,               "%10c",'A');
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 97\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%-10c",'A');
+    len2 = sprintf (str2,               "%-10c",'A');
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 98\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%.10c",'A');
+    len2 = sprintf (str2,               "%.10c",'A');
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+    printf("test 99\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, sizeof(str1), "%-.10c",'A');
+    len2 = sprintf (str2,               "%-.10c",'A');
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)len1) == 0);
+
+
+    printf("test 100\n");
+    memset(str1,'x',sizeof(str1));  memset(str2,'x',sizeof(str2));
+    len1 = snprintf(str1, (size_t)8, "blaBhb%shehe%cX","ABCD",'1');
+    len2 = sprintf (str2,            "blaBhb%shehe%cX","ABCD",'1');
+    assert(len1==len2);
+    assert(memcmp(str1,str2,(size_t)7) == 0);
+    assert(str1[7] == '\0');
+    assert(memcmp(str1+14,str2+16,(size_t)(len1-16)) == 0);
+
+}
+
+ /* testing for correctness and compatibility */
+  if (argc >= 3) {
+    char *c; int alldigits = 1;
+    for (c=argv[2]; *c; c++)
+      if (! (*c == '-' || (*c >= '0' && *c <= '9'))) alldigits = 0;
+    if (alldigits) {
+      int j = atoi(argv[2]);
+      len1 = portable_snprintf(str1, str_m, argv[1], j, 3);
+      len2 = sprintf(str2,                  argv[1], j, 3);
+#ifdef HAVE_SNPRINTF
+      len3 = snprintf(str3, str_m,          argv[1], j, 3);
+#endif
+    } else {
+      len1 = portable_snprintf(str1, str_m, argv[1], argv[2], 3);
+      len2 = sprintf(str2,                  argv[1], argv[2], 3);
+#ifdef HAVE_SNPRINTF
+      len3 = snprintf(str3, str_m,          argv[1], argv[2], 3);
+#endif
+    }
+    printf("portable:     |%s|  len = %d\n", str1, len1);
+    printf("sys sprintf:  |%s|  len = %d\n", str2, len2);
+#ifdef HAVE_SNPRINTF
+    printf("sys snprintf: |%s|  len = %d\n", str3, len3);
+#endif
+  } else {  /* exhaustive testing */
+
+    const char flags[] = "+- 0#";     /* set of test flags   (including '\0')*/
+    int flags_l = strlen(flags);
+
+    const char fspec[] = "scdpoxXuiy"; /* set of test formats (including '\0') */
+    int fspec_l = strlen(fspec);
+
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+    const char datatype[] = " hl2"; /* set of datatypes */
+#else
+    const char datatype[] = " hl";  /* set of datatypes */
+#endif
+    int datatype_l = strlen(datatype);
+
+    const long int iargs[] =                /* set of numeric test arguments */
+      { 0,1,9,10,28,99,100,127,128,129,998,1000,32767,32768,32769,
+        -1,-9,-10,-28,-99,-100,-127,-128,-129,
+        -998,-1000,-32767,-32768,-32769 };
+    int iargs_l = sizeof(iargs)/sizeof(iargs[0]);
+    const char *sargs[] =             /* set of string test arguments */
+      { "", "a", "0", "-ab", "abcde", "abcdefghijk mnopqrstuv" };
+    int sargs_l = sizeof(sargs)/sizeof(sargs[0]);
+
+    char fmt[256];
+    int fmt_l;
+    int a, fs, fl1, fl2, fl3, fl4, fl5, fw, fp, dt;
+
+    for (fs=0; fs<=fspec_l; fs++) {  /* format specifier */
+      int strtype = (fspec[fs] == 's' || fspec[fs] == '%');
+      int args_l = (strtype ? sargs_l : iargs_l);
+
+      for (fw= -1; fw<=3; fw++) {  /* minimal field width */
+
+        printf("Trying format  %%");
+        if (fw >= 0) printf("%d", fw);
+        if (fspec[fs]) putchar(fspec[fs]);
+        putchar('\n');
+
+        for (fp= -2; fp<=3; fp++) {   /* format field precision */
+
+          /* data type modifiers */
+          for (dt=0; dt < ((strtype||fspec[fs]=='c') ? 1 : datatype_l); dt++) {
+
+            int dataty = datatype[dt];
+
+            if (fspec[fs] == 'D' || fspec[fs] == 'U' || fspec[fs] == 'O')
+              dataty = 'l';
+
+            if (fspec[fs] == 'p' && dataty == '2') continue;
+
+            for (fl1=0; fl1<=flags_l; fl1++) {  /* flags */
+             for (fl2=0; fl2<=flags_l; fl2++) {
+              for (fl3=0; fl3<=flags_l; fl3++) {
+               for (fl4=0; fl4<=flags_l; fl4++) {
+                for (fl5=0; fl5<=flags_l; fl5++) {
+
+                   for (a=0; a<args_l; a++) {  /* test arguments */
+
+                     fmt_l = 0; fmt[fmt_l++] = '%';
+                     if (flags[fl1]) fmt[fmt_l++] = flags[fl1];
+                     if (flags[fl2]) fmt[fmt_l++] = flags[fl2];
+                     if (flags[fl3]) fmt[fmt_l++] = flags[fl3];
+                     if (flags[fl4]) fmt[fmt_l++] = flags[fl4];
+                     if (flags[fl5]) fmt[fmt_l++] = flags[fl5];
+                     if (fw >= 0) fmt_l += sprintf(fmt+fmt_l, "%d", fw);
+                     if (fp >= -1) {
+                       fmt[fmt_l++] = '.';
+                       if (fp >= 0) fmt_l += sprintf(fmt+fmt_l, "%d", fp);
+                     }
+                     if (dataty == '2')
+                       { fmt[fmt_l++] = 'l'; fmt[fmt_l++] = 'l'; }
+                     else if (dataty != ' ')
+                       { fmt[fmt_l++] = dataty; }
+
+                     if (fspec[fs]) fmt[fmt_l++] = fspec[fs];
+                     fmt[fmt_l++] = '\0';
+
+                     if (a==0 && fl1==flags_l && fl2==flags_l && fl3==flags_l
+                         && fl4==flags_l && fl5==flags_l) printf("%s\n", fmt);
+
+#ifdef HAVE_SNPRINTF
+                     memset(str1,'G',sizeof(str1));
+                     memset(str2,'G',sizeof(str2));
+                     memset(str3,'G',sizeof(str3));
+#endif
+                     len1 = len2 = len3 = 0;
+                     if (strtype) {
+                       len1 = portable_snprintf(str1, str_m, fmt, sargs[a]);
+                       len2 = sprintf(str2,                  fmt, sargs[a]);
+#ifdef HAVE_SNPRINTF
+                       len3 = snprintf(str3, str_m,          fmt, sargs[a]);
+#endif
+                     } else if (fspec[fs] == 'p') {
+                       len1 = portable_snprintf(str1, str_m, fmt, (void *)iargs[a]);
+                       len2 = sprintf(str2,                  fmt, (void *)iargs[a]);
+#ifdef HAVE_SNPRINTF
+                       len3 = snprintf(str3, str_m,          fmt, (void *)iargs[a]);
+#endif
+                     } else {
+                       switch (dataty) {
+                       case '\0':
+                         len1 = portable_snprintf(str1, str_m, fmt, (int) iargs[a]);
+                         len2 = sprintf(str2,                  fmt, (int) iargs[a]);
+#ifdef HAVE_SNPRINTF
+                         len3 = snprintf(str3, str_m,          fmt, (int) iargs[a]);
+#endif
+                         break;
+                       case 'h':
+                         len1 = portable_snprintf(str1, str_m, fmt, (short int)iargs[a]);
+                         len2 = sprintf(str2,                  fmt, (short int)iargs[a]);
+#ifdef HAVE_SNPRINTF
+                         len3 = snprintf(str3, str_m,          fmt, (short int)iargs[a]);
+#endif
+                         break;
+                       case 'l':
+                         len1 = portable_snprintf(str1, str_m, fmt, (long int)iargs[a]);
+                         len2 = sprintf(str2,                  fmt, (long int)iargs[a]);
+#ifdef HAVE_SNPRINTF
+                         len3 = snprintf(str3, str_m,          fmt, (long int)iargs[a]);
+#endif
+                         break;
+#ifdef SNPRINTF_LONGLONG_SUPPORT
+                       case '2':
+                         len1 = portable_snprintf(str1, str_m, fmt, (long long int)iargs[a]);
+                         len2 = sprintf(str2,                  fmt, (long long int)iargs[a]);
+#ifdef HAVE_SNPRINTF
+                         len3 = snprintf(str3, str_m,          fmt, (long long int)iargs[a]);
+#endif
+                         break;
+#endif
+                       }
+                     }
+
+                     if (0) {
+#ifdef HAVE_SNPRINTF
+                     } else if (len1 != len3 ||
+                         memcmp(str1,str3,min(len1+20,sizeof(str1))) != 0) {
+                       bad = 1;
+                       if (strtype) printf("\n2: %s, <%s>\n", fmt, sargs[a]);
+                       else         printf("\n2: %s, %ld\n",   fmt, iargs[a]);
+                       printf("portable:     |%s|  len = %d\n", str1, len1);
+                       printf("sys sprintf:  |%s|  len = %d\n", str2, len2);
+                       printf("sys snprintf: |%s|  len = %d\n", str3, len3);
+#else
+                     } else if (len1 != len2 ||
+                         (len1>0 && memcmp(str1,str2,min(len1,str_m)-1) != 0)) {
+                       bad = 1;
+                       if (strtype) printf("\n1: %s, <%s>\n", fmt, sargs[a]);
+                       else         printf("\n1: %s, %ld\n",   fmt, iargs[a]);
+                       printf("portable:     |%s|  len = %d\n", str1, len1);
+                       printf("sys sprintf:  |%s|  len = %d\n", str2, len2);
+#endif
+                     }
+                     if (bad) return(1);
+                   }
+
+                }
+               }
+              }
+             }
+
+            }
+          }
+        }
+      }
+    }
+  }
+  return (bad?1:0);
+}
diff --git a/libtu/stringstore.c b/libtu/stringstore.c
new file mode 100644 (file)
index 0000000..6f54387
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * libtu/stringstore.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <stdlib.h>
+
+#include "misc.h"
+#include "output.h"
+#include "rb.h"
+#include "stringstore.h"
+
+
+static Rb_node stringstore=NULL;
+
+
+const char *stringstore_get(StringId id)
+{
+    return (const char*)(((Rb_node)id)->k.key);
+}
+
+    
+StringId stringstore_find(const char *str)
+{
+    Rb_node node;
+    int found=0;
+    
+    if(stringstore==NULL)
+        return STRINGID_NONE;
+    
+    node=rb_find_key_n(stringstore, str, &found);
+    
+    if(!found)
+        return STRINGID_NONE;
+    
+    return (StringId)node;
+}
+
+
+StringId stringstore_alloc(const char *str)
+{
+    Rb_node node=(Rb_node)stringstore_find(str);
+    char *s;
+    
+    if(node!=NULL){
+        node->v.ival++;
+        return node;
+    }
+    
+    if(stringstore==NULL){
+        stringstore=make_rb();
+        if(stringstore==NULL)
+            return STRINGID_NONE;
+    }
+    
+    s=scopy(str);
+    
+    if(s==NULL)
+        return STRINGID_NONE;
+    
+    node=rb_insert(stringstore, s, NULL);
+    
+    if(node==NULL)
+        return STRINGID_NONE;
+    
+    node->v.ival=1;
+        
+    return (StringId)node;
+}
+
+
+void stringstore_free(StringId id)
+{
+    Rb_node node=(Rb_node)id;
+    
+    if(node==NULL){
+        warn("Attempt to free un-allocated string from stringstore.");
+        return;
+    }
+    
+    if(node->v.ival<=0){
+        warn("Stringstore reference count corrupted.");
+        return;
+    }
+    
+    node->v.ival--;
+    
+    if(node->v.ival==0){
+        char *s=(char*)(node->k.key);
+        rb_delete_node(node);
+        free(s);
+    }
+}
+
diff --git a/libtu/stringstore.h b/libtu/stringstore.h
new file mode 100644 (file)
index 0000000..752e573
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * libtu/stringstore.h
+ *
+ * Copyright (c) Tuomo Valkonen 2004. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_STRINGSTORE_H
+#define LIBTU_STRINGSTORE_H
+
+typedef void* StringId;
+
+#define STRINGID_NONE ((StringId)0)
+
+extern const char *stringstore_get(StringId id);
+extern StringId stringstore_find(const char *str);
+extern StringId stringstore_alloc(const char *str);
+extern void stringstore_free(StringId id);
+
+#endif /* LIBTU_STRINGSTORE_H */
diff --git a/libtu/tester.c b/libtu/tester.c
new file mode 100644 (file)
index 0000000..4f27fd6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * libtu/tester.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <stdio.h>
+
+#include "misc.h"
+#include "tokenizer.h"
+#include "util.h"
+
+int main(int argc, char *argv[])
+{
+    Tokenizer*tokz;
+    Token tok=TOK_INIT;
+    
+    libtu_init(argv[0]);
+    
+    if(!(tokz=tokz_open_file(stdin, "stdin")))
+        return EXIT_FAILURE;
+
+    while(tokz_get_token(tokz, &tok)){
+        switch(tok.type){
+        case TOK_LONG:
+            printf("long - %ld\n", TOK_LONG_VAL(&tok));
+            break;
+        case TOK_DOUBLE:
+            printf("double - %g\n", TOK_DOUBLE_VAL(&tok));
+            break;
+        case TOK_CHAR:
+            printf("char - '%c'\n", TOK_CHAR_VAL(&tok));
+            break;
+        case TOK_STRING:
+            printf("string - \"%s\"\n", TOK_STRING_VAL(&tok));
+            break;
+        case TOK_IDENT:
+            printf("ident - %s\n", TOK_IDENT_VAL(&tok));
+            break;
+        case TOK_COMMENT:
+            printf("comment - %s\n", TOK_COMMENT_VAL(&tok));
+            break;
+        case TOK_OP:
+            printf("operator - %03x\n", TOK_OP_VAL(&tok));
+            break;
+        }
+    }
+       
+    return EXIT_SUCCESS;
+}
+
diff --git a/libtu/tester2.c b/libtu/tester2.c
new file mode 100644 (file)
index 0000000..832c474
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * libtu/tester2.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <stdio.h>
+
+#include "misc.h"
+#include "tokenizer.h"
+#include "parser.h"
+#include "util.h"
+
+
+static bool test_fn(Tokenizer *tokz, int n, Token *toks)
+{
+    printf("test_fn() %d %s\n", n, TOK_IDENT_VAL(toks)); 
+    
+    return TRUE;
+}
+
+
+
+static bool sect_fn(Tokenizer *tokz, int n, Token *toks)
+{
+    printf("sect_fn() %d %s\n", n, TOK_IDENT_VAL(toks+1));
+    
+    return TRUE;
+}
+
+
+static bool test2_fn(Tokenizer *tokz, int n, Token *toks)
+{
+    printf("test2_fn() %d %s %f\n", n, TOK_BOOL_VAL(toks+1) ? "TRUE" : "FALSE", TOK_DOUBLE_VAL(toks+2));
+
+    return TRUE;
+}
+
+static bool test3_fn(Tokenizer *tokz, int n, Token *toks)
+{
+    if(n<=2)
+        printf("test3_fn() %d \"%s\"\n", n, TOK_STRING_VAL(toks+1));
+    else
+        printf("test3_fn() %d \"%s\" %ld\n", n, TOK_STRING_VAL(toks+1), TOK_LONG_VAL(toks+2));
+
+    return TRUE;
+}
+    
+    
+static ConfOpt opts[]={
+    {"test", NULL, test_fn, NULL},
+    {"t2", "bd", test2_fn, NULL},
+    {"foo", "s?l", test3_fn, NULL},
+    {"sect", "s", sect_fn, opts},
+    {NULL, NULL, NULL, NULL}
+};
+
+             
+int main(int argc, char *argv[])
+{
+    libtu_init(argv[0]);
+    parse_config_file(stdin, opts, TOKZ_ERROR_TOLERANT);
+    
+    return EXIT_SUCCESS;
+}
+
diff --git a/libtu/tester3.c b/libtu/tester3.c
new file mode 100644 (file)
index 0000000..12bda85
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * libtu/tester3.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <stdio.h>
+
+#include "util.h"
+#include "misc.h"
+#include "optparser.h"
+
+
+static const char usage[]=
+    "Usage: $p [options]\n"
+    "\n"
+    "Where options are:\n"
+    "$o\n";
+    
+
+static OptParserOpt opts[]={
+    {'o',    "opt", OPT_ARG, "OPTION", "foo bar baz quk asdf jklö äölk dfgh quik aaaa bbbb cccc dddd eeee ffff"},
+    {'f',    "file", OPT_ARG, "FILE", "asdfsadlfölökjasdflökjasdflkjöasdflkjöas dlöfjkasdfölkjasdfölkjasdfasdflöjasdfkasödjlfkasdlföjasdölfjkölkasjdfasdfölkjasd asdöljfasöldf  asdölfköasdlf asfdlök asdföljkadsfölasdfölasdölkfjasdölfasödlflöskflasdföaölsdf"},
+    {'v',    "view",    0, NULL, "asfasdf"},
+    {'z',    "zip", 0, NULL, "asdfasdf"},
+    {'x',    "extract", 0, NULL, "asdfasdf"},
+    {0, NULL, 0, NULL, NULL}
+};
+    
+static OptParserCommonInfo tester3_cinfo={
+    NULL,
+    usage,
+    NULL
+};
+
+
+int main(int argc, char *argv[])
+{
+    int opt;
+    
+    libtu_init(argv[0]);
+    
+    optparser_init(argc, argv, OPTP_NO_DASH, opts, &tester3_cinfo);
+    
+    while((opt=optparser_get_opt())){
+        switch(opt){
+        case 'o':
+            printf("opt: %s\n", optparser_get_arg());
+            break;
+        case 'f':
+            printf("file: %s\n", optparser_get_arg());
+            break;
+        case 'v':
+            printf("view\n");
+            break;
+        case 'z':
+            printf("zip\n");
+            break;
+        case 'x':
+            printf("extract\n");
+            break;
+        default:
+            optparser_print_error();
+            return 1;
+        }
+    }
+    return 0;
+}
+
diff --git a/libtu/tokenizer.c b/libtu/tokenizer.c
new file mode 100644 (file)
index 0000000..cf92942
--- /dev/null
@@ -0,0 +1,958 @@
+/*
+ * libtu/tokenizer.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <limits.h>
+#include <assert.h>
+#include <math.h>
+#include <string.h>
+
+#include "tokenizer.h"
+#include "misc.h"
+#include "output.h"
+#include "private.h"
+
+
+static const char *errors[]={
+    DUMMY_TR("(no error)"),
+    DUMMY_TR("Unexpected end of file"),                /* E_TOKZ_UNEXPECTED_EOF */
+    DUMMY_TR("Unexpected end of line"),                /* E_TOKZ_UNEXPECTED_EOL */
+    DUMMY_TR("End of line expected"),                /* E_TOKZ_EOL_EXPECTED */
+    DUMMY_TR("Invalid character"),                    /* E_TOKZ_INVALID_CHAR*/
+    DUMMY_TR("Numeric constant too big"),            /* E_TOKZ_TOOBIG */
+    DUMMY_TR("Invalid numberic format"),            /* E_TOKZ_NUMFMT */
+    DUMMY_TR("Junk after numeric constant"),        /* E_TOKZ_NUM_JUNK */
+    DUMMY_TR("Not an integer"),                        /* E_TOKZ_NOTINT */
+    DUMMY_TR("Numeric constant out of range"),        /* E_TOKZ_RANGE */
+    DUMMY_TR("Multi-character character constant"),    /* E_TOKZ_MULTICHAR */
+    DUMMY_TR("Token/statement limit reached"),        /* E_TOKZ_TOKEN_LIMIT */
+    DUMMY_TR("Unknown option"),                        /* E_TOKZ_UNKONWN_OPTION */
+    DUMMY_TR("Syntax error"),                        /* E_TOKZ_SYNTAX */
+    DUMMY_TR("Invalid argument"),                    /* E_TOKZ_INVALID_ARGUMENT */
+    DUMMY_TR("End of statement expected"),            /* E_TOKZ_EOS_EXPECTED */
+    DUMMY_TR("Too few arguments"),                    /* E_TOKZ_TOO_FEW_ARGS */
+    DUMMY_TR("Too many arguments"),                    /* E_TOKZ_TOO_MANY_ARGS */
+    DUMMY_TR("Maximum section nestin level exceeded"), /* E_TOK_Z_MAX_NEST */
+    DUMMY_TR("Identifier expected"),                /* E_TOKZ_IDENTIFIER_EXPECTED */
+    DUMMY_TR("Starting brace ('{') expected"),        /* E_TOKZ_LBRACE_EXPECTED */
+};
+
+
+/* */
+
+#define STRBLEN 32
+
+#define STRING_DECL(X) int err=0; char* X=NULL; char X##_tmp[STRBLEN]; int X##_tmpl=0
+#define STRING_DECL_P(X, P) int err=0; char* X=NULL; char X##_tmp[STRBLEN]=P; int X##_tmpl=sizeof(P)-1
+#define STRING_APPEND(X, C) {if(!_string_append(&X, X##_tmp, &X##_tmpl, c)) err=-ENOMEM;}
+#define STRING_FREE(X) if(X!=NULL) free(X)
+#define STRING_FINISH(X) {if(err!=0) return err; if(!_string_finish(&X, X##_tmp, X##_tmpl)) err=-ENOMEM;}
+
+
+static bool _string_append(char **p, char *tmp, int *tmplen, char c)
+{
+    char *tmp2;
+    
+    if(*tmplen==STRBLEN-1){
+        tmp[STRBLEN-1]='\0';
+        if(*p!=NULL){
+            tmp2=scat(*p, tmp);
+            free(*p);
+            *p=tmp2;
+        }else{
+            *p=scopy(tmp);
+        }
+        *tmplen=1;
+        tmp[0]=c;
+        return *p!=NULL;
+    }else{
+        tmp[(*tmplen)++]=c;
+        return TRUE;
+    }
+}
+
+
+static bool _string_finish(char **p, char *tmp, int tmplen)
+{
+    char *tmp2;
+    
+    if(tmplen==0){
+        if(*p==NULL)
+            *p=scopy("");
+    }else{
+        tmp[tmplen]='\0';
+        if(*p!=NULL){
+            tmp2=scat(*p, tmp);
+            free(*p);
+            *p=tmp2;
+        }else{
+            *p=scopy(tmp);
+        }
+    }
+    return *p!=NULL;
+}
+
+
+/* */
+
+
+#define INC_LINE() tokz->line++
+#define GETCH() _getch(tokz)
+#define UNGETCH(C) _ungetch(tokz, C)
+
+static int _getch(Tokenizer *tokz)
+{
+    int c;
+    
+    if(tokz->ungetc!=-1){
+        c=tokz->ungetc;
+        tokz->ungetc=-1;
+    }else if (tokz->flags&TOKZ_READ_FROM_BUFFER) {
+        assert(tokz->buffer.data!=NULL);
+        if (tokz->buffer.pos==tokz->buffer.len)
+            c=EOF;
+        else
+            c=tokz->buffer.data[tokz->buffer.pos++];
+    }else{
+        c=getc(tokz->file);
+    }
+
+    return c;
+}
+
+
+static void _ungetch(Tokenizer *tokz, int c)
+{
+    tokz->ungetc=c;
+}
+
+
+/* */
+
+
+static int scan_line_comment(Token *tok, Tokenizer *tokz)
+{
+    STRING_DECL_P(s, "#");
+    int c;
+
+    c=GETCH();
+                
+    while(c!='\n' && c!=EOF){
+        STRING_APPEND(s, c);
+        c=GETCH();
+    }
+
+    UNGETCH(c);
+
+    STRING_FINISH(s);
+    
+    TOK_SET_COMMENT(tok, s);
+    
+    return 0;
+}
+
+
+static int skip_line_comment(Tokenizer *tokz)
+{
+    int c;
+    
+    do{
+        c=GETCH();
+    }while(c!='\n' && c!=EOF);
+
+    UNGETCH(c);
+        
+    return 0;
+}
+
+
+/* */
+
+
+static int scan_c_comment(Token *tok, Tokenizer *tokz)
+{
+    STRING_DECL_P(s, "/*");
+    int c;
+    int st=0;
+    
+    while(1){
+        c=GETCH();
+        
+        if(c==EOF){
+            STRING_FREE(s);
+            return E_TOKZ_UNEXPECTED_EOF;
+        }
+        
+        STRING_APPEND(s, c);
+        
+        if(c=='\n'){
+            INC_LINE();
+        }else if(st==0 && c=='*'){
+            st=1;
+        }else if(st==1){
+            if(c=='/')
+                break;
+            st=0;
+        }
+    }
+
+    STRING_FINISH(s);
+
+    TOK_SET_COMMENT(tok, s);
+
+    return 0;
+}
+
+
+static int skip_c_comment(Tokenizer *tokz)
+{
+    int c;
+    int st=0;
+    
+    while(1){
+        c=GETCH();
+        
+        if(c==EOF)
+            return E_TOKZ_UNEXPECTED_EOF;
+        
+        if(c=='\n')
+            INC_LINE();
+        else if(st==0 && c=='*')
+            st=1;
+        else if(st==1){
+            if(c=='/')
+                break;
+            st=0;
+        }
+    }
+    
+    return 0;
+}
+
+
+/* */
+
+
+static int scan_char_escape(Tokenizer *tokz)
+{
+    static char* special_chars="nrtbae";
+    static char* specials="\n\r\t\b\a\033";
+    int base, max;
+    int i ,c;
+    
+    c=GETCH();
+    
+    for(i=0;special_chars[i];i++){
+        if(special_chars[i]==c)
+            return specials[c];
+    }
+    
+    if(c=='x' || c=='X'){
+        base=16;max=2;i=0;
+    }else if(c=='d' || c=='D'){
+        base=10;max=3;i=0;
+    }else if(c=='8' || c=='9'){
+        base=10;max=2;i=c-'0';
+    }else if('0'<=c && c<='7'){
+        base=8;max=2;i=c-'0';
+    }else if(c=='\n'){
+        UNGETCH(c);
+        return -2;
+    }else{
+        return c;
+    }
+    
+        
+    while(--max>=0){
+        c=GETCH();
+        
+        if(c==EOF)
+            return EOF;
+        
+        if(c=='\n'){
+            UNGETCH(c);
+            return -2;
+        }
+        
+        if(base==16){
+            if(!isxdigit(c))
+                break;
+            
+            i<<=4;
+            
+            if(isdigit(c))
+                i+=c-'0';
+            else if(i>='a')
+                i+=0xa+c-'a';
+            else
+                i+=0xa+c-'a';
+            
+        }else if(base==10){
+            if(!isdigit(c))
+                break;
+            i*=10;
+            i+=c-'0';
+        }else{
+            if(c<'0' || c>'7')
+                break;
+            i<<=3;
+            i+=c-'0';
+        }
+    }
+    
+    if(max>=0)
+        UNGETCH(c);
+
+    return i;
+}
+
+
+/* */
+
+
+static int scan_string(Token *tok, Tokenizer *tokz, bool escapes)
+{
+    STRING_DECL(s);
+    int c;
+
+    while(1){    
+        c=GETCH();
+        
+        if(c=='"')
+            break;
+        
+        if(c=='\n'){
+            UNGETCH(c);
+            STRING_FREE(s);
+            return E_TOKZ_UNEXPECTED_EOL;
+        }
+        
+        if(c=='\\' && escapes){
+            c=scan_char_escape(tokz);
+            if(c==-2){
+                STRING_FREE(s);
+                return E_TOKZ_UNEXPECTED_EOL;
+            }
+        }
+        
+        if(c==EOF){
+            STRING_FREE(s);
+            return E_TOKZ_UNEXPECTED_EOF;
+        }
+        
+        STRING_APPEND(s, c);
+    }
+    
+    STRING_FINISH(s);
+    
+    TOK_SET_STRING(tok, s);
+
+    return 0;
+}
+
+
+/* */
+
+
+static int scan_char(Token *tok, Tokenizer *tokz)
+{
+    int c, c2;
+    
+    c=GETCH();
+    
+    if(c==EOF)
+        return E_TOKZ_UNEXPECTED_EOF;
+    
+    if(c=='\n')
+        return E_TOKZ_UNEXPECTED_EOL;
+
+    if(c=='\\'){
+        c=scan_char_escape(tokz);    
+        
+        if(c==EOF)
+            return E_TOKZ_UNEXPECTED_EOF;
+        
+        if(c==-2)
+            return E_TOKZ_UNEXPECTED_EOL;
+    }
+    
+    c2=GETCH();
+    
+    if(c2!='\'')
+        return E_TOKZ_MULTICHAR;
+    
+    TOK_SET_CHAR(tok, c);
+    
+    return 0;
+}
+
+
+/* */
+
+
+#define START_IDENT(X) (isalpha(X) || X=='_' || X=='$')
+
+
+static int scan_identifier(Token *tok, Tokenizer *tokz, int c)
+{
+    STRING_DECL(s);
+    
+    do{
+        STRING_APPEND(s, c);
+        c=GETCH();
+    }while(isalnum(c) || c=='_' || c=='$');
+    
+    UNGETCH(c);
+    
+    STRING_FINISH(s);
+    
+    TOK_SET_IDENT(tok, s);
+
+    return 0;
+}
+
+#define NP_SIMPLE_IMPL
+#include "np/numparser2.h"
+#include "np/np-conv.h"
+
+
+static int scan_number(Token *tok, Tokenizer *tokz, int c)
+{
+    NPNum num=NUM_INIT;
+    int e;
+    
+    if((e=parse_number(&num, tokz, c)))
+        return e;
+    
+    if(num.type==NPNUM_INT){
+        long l;
+        if((e=num_to_long(&l, &num, TRUE)))
+            return e;
+    
+        TOK_SET_LONG(tok, l);
+    }else if(num.type==NPNUM_FLOAT){
+          double d;
+          if((e=num_to_double(&d, &num)))
+              return e;
+            
+        TOK_SET_DOUBLE(tok, d);
+    }else{
+        return E_TOKZ_NUMFMT;
+    }
+
+    return 0;
+}
+
+
+/* */
+
+
+static uchar op_map[]={
+    0x00,        /* ________ 0-7     */
+    0x00,        /* ________ 8-15    */
+    0x00,        /* ________ 16-23   */
+    0x00,        /* ________ 24-31   */
+    0x62,        /* _!___%&_ 32-39   */
+    0xff,        /* ()*+,-./ 40-47   */
+    0x00,        /* ________ 48-55   */
+    0xfc,        /* __:;<=>? 56-63   */
+    0x01,        /* @_______ 64-71   */
+    0x00,        /* ________ 72-79   */
+    0x00,        /* ________ 80-87   */
+    0x78,        /* ___[_]^_ 88-95   */
+    0x00,        /* ________ 96-103  */
+    0x00,        /* ________ 104-111 */
+    0x00,        /* ________ 112-119 */
+    0x38        /* ___{|}__ 120-127 */
+};
+
+
+static bool map_isset(uchar *map, uint ch)
+{
+    if(ch>127)
+        return FALSE;
+
+    return map[ch>>3]&(1<<(ch&7));
+}
+
+
+static bool is_opch(uint ch)
+{
+    return map_isset(op_map, ch);
+}
+
+
+static int scan_op(Token *tok, Tokenizer *tokz,  int c)
+{
+    int c2;
+    int op=-1;
+    
+    /* Quickly check it is an operator character */
+    if(!is_opch(c))
+        return E_TOKZ_INVALID_CHAR;
+
+    switch(c){
+    case '+':
+    case '-':
+    case '*':
+/*    case '/':     Checked elsewhere */
+    case '%':
+    case '^':
+    case '!':
+    case '=':
+    case '<':
+    case '>':
+        c2=GETCH();
+        if(c2=='='){
+            op=c|(c2<<8);
+        }else if(c2==c && (c2!='%' && c2!='!' && c2!='*')){
+            if(c=='<' || c=='>'){
+                int c3=GETCH();
+                if(c3=='='){
+                    op=c|(c2<<8)|(c3<<16);
+                }else{
+                    UNGETCH(c3);
+                    op=c|(c2<<8);
+                }
+            }else{
+                op=c|(c2<<8);
+            }
+        }else{
+            UNGETCH(c2);
+            op=c;
+        }
+        break;
+        
+    /* It is already known that it is a operator so these are not needed
+    case ':':
+    case '~':
+    case '?':
+    case '.':
+    case ';';
+    case '{':
+    case '}':
+    case '@':        
+    case '|':
+    case '&':
+    */
+    default:
+        op=c;
+    }
+    
+    TOK_SET_OP(tok, op);
+
+    return 0;
+}
+
+
+/* */
+
+
+void tokz_warn(const Tokenizer *tokz, int line, const char *fmt, ...)
+{
+    va_list args;
+    
+    va_start(args, fmt);
+    
+    if(tokz!=NULL)
+        warn_obj_line_v(tokz->name, line, fmt, args);
+    else
+        warn(fmt, args);
+    
+    va_end(args);
+}
+
+
+void tokz_warn_error(const Tokenizer *tokz, int line, int e)
+{
+    if(e==E_TOKZ_UNEXPECTED_EOF)
+        line=0;
+    
+    if(e<0)
+        tokz_warn(tokz, line, "%s", strerror(-e));
+    else
+        tokz_warn(tokz, line, "%s", TR(errors[e]));
+}
+
+
+bool tokz_get_token(Tokenizer *tokz, Token *tok)
+{
+    int c, c2, e;
+    
+    if (!(tokz->flags&TOKZ_READ_FROM_BUFFER))
+    assert(tokz->file!=NULL);
+    
+    tok_free(tok);
+    
+    if(!TOK_IS_INVALID(&(tokz->ungettok))){
+        *tok=tokz->ungettok;
+        tokz->ungettok.type=TOK_INVALID;
+        return TRUE;
+    }
+
+    while(1){
+    
+        e=0;
+        
+        do{
+            c=GETCH();
+        }while(c!='\n' && c!=EOF && isspace(c));
+    
+        tok->line=tokz->line;
+    
+        switch(c){
+        case EOF:
+            TOK_SET_OP(tok, OP_EOF);
+            return TRUE;
+            
+        case '\n':
+            INC_LINE();
+            
+            if(tokz->flags&TOKZ_IGNORE_NEXTLINE)
+                continue;
+            
+            TOK_SET_OP(tok, OP_NEXTLINE);
+            
+            return TRUE;
+            
+        case '\\':
+            do{
+                c=GETCH();
+                if(c==EOF){
+                    TOK_SET_OP(tok, OP_EOF);
+                    return FALSE;
+                }
+                if(!isspace(c) && e==0){
+                    e=E_TOKZ_EOL_EXPECTED;
+                    tokz_warn_error(tokz, tokz->line, e);
+                    if(!(tokz->flags&TOKZ_ERROR_TOLERANT))
+                        return FALSE;
+                }
+            }while(c!='\n');
+            
+            INC_LINE();
+            continue;
+
+        case '#':
+            if(tokz->flags&TOKZ_READ_COMMENTS){
+                e=scan_line_comment(tok, tokz);
+                break;
+            }else if((e=skip_line_comment(tokz))){
+                break;
+            }
+            
+            continue;
+            
+        case '/':
+            c2=GETCH();
+            
+            if(c2=='='){
+                TOK_SET_OP(tok, OP_AS_DIV);
+                return TRUE;
+            }
+            
+            if(c2!='*'){
+                UNGETCH(c2);
+                TOK_SET_OP(tok, OP_DIV);
+                return TRUE;
+            }
+            
+            if(tokz->flags&TOKZ_READ_COMMENTS){
+                e=scan_c_comment(tok, tokz);
+                break;
+            }else if((e=skip_c_comment(tokz))){
+                break;
+            }
+            
+            continue;
+            
+        case '\"':
+            e=scan_string(tok, tokz, TRUE);
+            break;
+
+        case '\'':
+            e=scan_char(tok, tokz);
+            break;
+
+        default: 
+            if(('0'<=c && c<='9') || c=='-' || c=='+'){
+                e=scan_number(tok, tokz, c);
+                break;
+            }
+
+             if(START_IDENT(c))
+                e=scan_identifier(tok, tokz, c);
+            else
+                e=scan_op(tok, tokz, c);
+        }
+        
+        if(!e)
+            return TRUE;
+        
+        tokz_warn_error(tokz, tokz->line, e);
+        return FALSE;
+    }
+}
+
+
+void tokz_unget_token(Tokenizer *tokz, Token *tok)
+{
+    tok_free(&(tokz->ungettok));    
+    tokz->ungettok=*tok;
+    tok->type=TOK_INVALID;
+}
+
+
+/*
+ * File open
+ */
+
+static bool do_tokz_pushf(Tokenizer *tokz)
+{
+    Tokenizer_FInfo *finfo;
+    
+    finfo=REALLOC_N(tokz->filestack, Tokenizer_FInfo,
+                    tokz->filestack_n, tokz->filestack_n+1);
+    
+    if(finfo==NULL)
+        return FALSE;
+
+    tokz->filestack=finfo;
+    finfo=&(finfo[tokz->filestack_n++]);
+    
+    finfo->file=tokz->file;
+    finfo->name=tokz->name;
+    finfo->line=tokz->line;
+    finfo->ungetc=tokz->ungetc;
+    finfo->ungettok=tokz->ungettok;
+    
+    return TRUE;
+}
+
+
+bool tokz_pushf_file(Tokenizer *tokz, FILE *file, const char *fname)
+{
+    char *fname_copy=NULL;
+    
+    if(file==NULL)
+        return FALSE;
+
+    if(fname!=NULL){
+        fname_copy=scopy(fname);
+        if(fname_copy==NULL){
+            warn_err();
+            return FALSE;
+        }
+    }
+    
+    if(tokz->file!=NULL){
+        if(!do_tokz_pushf(tokz)){
+            warn_err();
+            if(fname_copy!=NULL)
+                free(fname_copy);
+            return FALSE;
+        }
+    }
+    
+    tokz->file=file;
+    tokz->name=fname_copy;
+    tokz->line=1;
+    tokz->ungetc=-1;    
+    tokz->ungettok.type=TOK_INVALID;
+    
+    return TRUE;
+}
+
+
+bool tokz_pushf(Tokenizer *tokz, const char *fname)
+{
+    FILE *file;
+    
+    file=fopen(fname, "r");
+    
+    if(file==NULL){
+        warn_err_obj(fname);
+        return FALSE;
+    }
+    
+    if(!tokz_pushf_file(tokz, file, fname)){
+        fclose(file);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+               
+static Tokenizer *tokz_create()
+{
+    Tokenizer*tokz;
+    
+    tokz=ALLOC(Tokenizer);
+    
+    if(tokz==NULL){
+        warn_err();
+        return NULL;
+    }
+    
+    tokz->file=NULL;
+    tokz->name=NULL;
+    tokz->line=1;
+    tokz->ungetc=-1;    
+    tokz->ungettok.type=TOK_INVALID;
+    tokz->flags=0;
+    tokz->optstack=NULL;
+    tokz->nest_lvl=0;
+    tokz->filestack_n=0;
+    tokz->filestack=NULL;
+    tokz->buffer.data=0;
+    tokz->buffer.len=0;
+    tokz->buffer.pos=0;
+    
+    return tokz;
+}
+
+    
+Tokenizer *tokz_open(const char *fname)
+{ 
+    Tokenizer *tokz;
+    
+    tokz=tokz_create();
+    
+    if(!tokz_pushf(tokz, fname)){
+        free(tokz);
+        return NULL;
+    }
+    
+    return tokz;
+}
+
+
+Tokenizer *tokz_open_file(FILE *file, const char *fname)
+{
+    Tokenizer *tokz;
+    
+    tokz=tokz_create();
+    
+    if(!tokz_pushf_file(tokz, file, fname)){
+        free(tokz);
+        return NULL;
+    }
+    
+    return tokz;
+}
+
+Tokenizer *tokz_prepare_buffer(char *buffer, int len)
+{
+    Tokenizer *tokz;
+    char old=0;
+
+    tokz=tokz_create();
+    if(len>0){
+        old=buffer[len-1];
+        buffer[len-1]='\0';
+    }
+
+    tokz->flags|=TOKZ_READ_FROM_BUFFER;
+    tokz->buffer.data=scopy(buffer);
+    tokz->buffer.len=(len>0 ? (uint)len : strlen(tokz->buffer.data));
+    tokz->buffer.pos=0;
+
+    if(old>0)
+        buffer[len-1]=old;
+
+    return tokz;
+}
+
+/*
+ * File close
+ */
+
+static bool do_tokz_popf(Tokenizer *tokz, bool shrink)
+{
+    Tokenizer_FInfo *finfo;
+    
+    if(tokz->filestack_n<=0)
+        return FALSE;
+
+    if(tokz->file!=NULL)
+        fclose(tokz->file);
+    if(tokz->name!=NULL)
+        free(tokz->name);
+    
+    finfo=&(tokz->filestack[--tokz->filestack_n]);
+    
+    tokz->file=finfo->file;
+    tokz->name=finfo->name;
+    tokz->line=finfo->line;
+    tokz->ungetc=finfo->ungetc;
+    tokz->ungettok=finfo->ungettok;
+    
+    if(tokz->filestack_n==0){
+        free(tokz->filestack);
+        tokz->filestack=NULL;
+    }else if(shrink){
+        finfo=REALLOC_N(tokz->filestack, Tokenizer_FInfo,
+                        tokz->filestack_n+1, tokz->filestack_n);
+        if(finfo==NULL)
+            warn_err();
+        else
+            tokz->filestack=finfo;
+    }
+    
+    return TRUE;
+}
+
+
+bool tokz_popf(Tokenizer *tokz)
+{
+    return do_tokz_popf(tokz, TRUE);
+}
+    
+
+void tokz_close(Tokenizer *tokz)
+{
+    while(tokz->filestack_n>0)
+        do_tokz_popf(tokz, FALSE);
+          
+    if(tokz->file!=NULL)
+        fclose(tokz->file);
+    if(tokz->name!=NULL)
+        free(tokz->name);
+    tok_free(&(tokz->ungettok));
+    
+    free(tokz);
+}
+
+
+
+/* */
+
+
+void tok_free(Token *tok)
+{
+    if(TOK_IS_STRING(tok) || TOK_IS_IDENT(tok) || TOK_IS_COMMENT(tok)){
+        if(TOK_STRING_VAL(tok)!=NULL)
+            free(TOK_STRING_VAL(tok));
+    }
+    
+    tok->type=TOK_INVALID;
+}
+
+
+void tok_init(Token *tok)
+{
+    static Token dummy=TOK_INIT;
+    
+    memcpy(tok, &dummy, sizeof(*tok));
+}
+
diff --git a/libtu/tokenizer.h b/libtu/tokenizer.h
new file mode 100644 (file)
index 0000000..034a4a7
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * libtu/tokenizer.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_TOKENIZER_H
+#define LIBTU_TOKENIZER_H
+
+#include <stdio.h>
+#include "types.h"
+
+
+#define TOK_SET_BOOL(TOK, VAL)         {(TOK)->type=TOK_BOOL; (TOK)->u.bval=VAL;}
+#define TOK_SET_LONG(TOK, VAL)         {(TOK)->type=TOK_LONG; (TOK)->u.lval=VAL;}
+#define TOK_SET_DOUBLE(TOK, VAL)     {(TOK)->type=TOK_DOUBLE; (TOK)->u.dval=VAL;}
+#define TOK_SET_CHAR(TOK, VAL)         {(TOK)->type=TOK_CHAR; (TOK)->u.cval=VAL;}
+#define TOK_SET_STRING(TOK, VAL)     {(TOK)->type=TOK_STRING; (TOK)->u.sval=VAL;}
+#define TOK_SET_IDENT(TOK, VAL)     {(TOK)->type=TOK_IDENT; (TOK)->u.sval=VAL;}
+#define TOK_SET_COMMENT(TOK, VAL)     {(TOK)->type=TOK_COMMENT; (TOK)->u.sval=VAL;}
+#define TOK_SET_OP(TOK, VAL)         {(TOK)->type=TOK_OP; (TOK)->u.opval=VAL;}
+
+#define TOK_TYPE(TOK)                ((TOK)->type)
+#define TOK_BOOL_VAL(TOK)            ((TOK)->u.bval)
+#define TOK_LONG_VAL(TOK)            ((TOK)->u.lval)
+#define TOK_DOUBLE_VAL(TOK)            ((TOK)->u.dval)
+#define TOK_CHAR_VAL(TOK)            ((TOK)->u.cval)
+#define TOK_STRING_VAL(TOK)            ((TOK)->u.sval)
+#define TOK_IDENT_VAL(TOK)            ((TOK)->u.sval)
+#define TOK_COMMENT_VAL(TOK)        ((TOK)->u.sval)
+#define TOK_OP_VAL(TOK)                ((TOK)->u.opval)
+
+#define TOK_IS_INVALID(TOK)            ((TOK)->type==TOK_INVALID)
+#define TOK_IS_BOOL(TOK)            ((TOK)->type==TOK_BOOL)
+#define TOK_IS_LONG(TOK)            ((TOK)->type==TOK_LONG)
+#define TOK_IS_DOUBLE(TOK)            ((TOK)->type==TOK_DOUBLE)
+#define TOK_IS_CHAR(TOK)            ((TOK)->type==TOK_CHAR)
+#define TOK_IS_STRING(TOK)            ((TOK)->type==TOK_STRING)
+#define TOK_IS_IDENT(TOK)            ((TOK)->type==TOK_IDENT)
+#define TOK_IS_COMMENT(TOK)            ((TOK)->type==TOK_COMMENT)
+#define TOK_IS_OP(TOK)                ((TOK)->type==TOK_OP)
+
+#define TOK_OP_IS(TOK, OP)            ((TOK)->type==TOK_OP && (TOK)->u.opval==(OP))
+
+#define TOK_TAKE_STRING_VAL(TOK)    ((TOK)->type=TOK_INVALID, (TOK)->u.sval)
+#define TOK_TAKE_IDENT_VAL(TOK)        ((TOK)->type=TOK_INVALID, (TOK)->u.sval)
+#define TOK_TAKE_COMMENT_VAL(TOK)    ((TOK)->type=TOK_INVALID, (TOK)->u.sval)
+
+
+enum{
+    TOK_INVALID=0,
+    TOK_LONG='l',
+    TOK_DOUBLE='d',
+    TOK_CHAR='c',
+    TOK_STRING='s',
+    TOK_IDENT='i',
+    TOK_BOOL='b',
+    TOK_COMMENT='#',
+    TOK_OP='+'
+};
+
+
+enum{
+#define OP2(X,Y)   ((X)|((Y)<<8))
+#define OP3(X,Y,Z) ((X)|((Y)<<8)|((Z)<<16))
+
+    OP_L_PAR=    '(', OP_R_PAR=    ')', OP_L_BRK=    '[', OP_R_BRK=    ']',
+    OP_L_BRC=    '{', OP_R_BRC=    '}', OP_COMMA=    ',', OP_SCOLON=    ';',
+
+    OP_PLUS=    '+', OP_MINUS=    '-', OP_MUL=    '*', OP_DIV=    '/',
+    OP_MOD=        '%', OP_POW=    '^', OP_OR=     '|', OP_AND=    '&',
+    /*OP_NOT=    '~',*/ OP_NOT=    '!', OP_ASGN=    '=', OP_LT=        '<',
+    OP_GT=        '>', OP_DOT=    '.', OP_COLON=    ':', OP_QMARK=    '?',
+    OP_AT=        '@',
+    OP_NEXTLINE='\n',OP_EOF=    -1,
+    
+    OP_INC=        OP2('+','+'),         OP_DEC=    OP2('-','-'),
+    OP_LSHIFT=    OP2('<','<'),          OP_RSHIFT=    OP2('>','>'),
+    OP_AS_INC=    OP2('+','='),          OP_AS_DEC= OP2('-','='),
+    OP_AS_MUL=    OP2('*','='),          OP_AS_DIV= OP2('/','='),
+    OP_AS_MOD=    OP2('%','='),          OP_AS_POW= OP2('^','='),
+
+/*    AS_OR=        OP2('|','='),         AS_AND=    OP2('&','='), */
+    OP_EQ=        OP2('=','='),          OP_NE=        OP2('!','='),
+    OP_LE=        OP2('<','='),          OP_GE=        OP2('>','=')
+    
+/*    L_AND=        OP2('&','&'), L_OR=        OP2('|','|'),
+    L_XOR=        OP2('^','^'), */
+
+/*    AsLShift=    OP3('<','<','='),
+    AsRShift=    OP3('>','>','='), */
+        
+#undef OP2
+#undef OP3
+};
+
+
+typedef struct{
+    int type;
+    int line;
+    union{
+        bool bval;
+        long lval;
+        double dval;
+        char cval;
+        char *sval;
+        int opval;
+    } u;
+} Token;
+
+#define TOK_INIT {0, 0, {0}}
+
+
+extern void tok_free(Token*tok);
+extern void tok_init(Token*tok);
+
+
+/* */
+
+
+enum{
+    TOKZ_IGNORE_NEXTLINE=0x1,
+    TOKZ_READ_COMMENTS=0x2,
+    TOKZ_PARSER_INDENT_MODE=0x04,
+    TOKZ_ERROR_TOLERANT=0x8,
+    TOKZ_READ_FROM_BUFFER=0x10,
+    TOKZ_DEFAULT_OPTION=0x20
+};
+
+
+enum{
+    E_TOKZ_UNEXPECTED_EOF=1,
+    E_TOKZ_UNEXPECTED_EOL,
+    E_TOKZ_EOL_EXPECTED,
+    E_TOKZ_INVALID_CHAR,
+    E_TOKZ_TOOBIG,
+    E_TOKZ_NUMFMT,
+    E_TOKZ_NUM_JUNK,
+    E_TOKZ_NOTINT,
+    E_TOKZ_RANGE,
+    E_TOKZ_MULTICHAR,
+    
+    E_TOKZ_TOKEN_LIMIT,
+    E_TOKZ_UNKNOWN_OPTION,
+    E_TOKZ_SYNTAX,
+    E_TOKZ_INVALID_ARGUMENT,
+    E_TOKZ_EOS_EXPECTED,
+    E_TOKZ_TOO_FEW_ARGS,
+    E_TOKZ_TOO_MANY_ARGS,
+    E_TOKZ_MAX_NEST,
+    E_TOKZ_IDENTIFIER_EXPECTED,
+    
+    E_TOKZ_LBRACE_EXPECTED
+};
+
+
+struct _ConfOpt;
+
+typedef struct _Tokenizer_FInfo{
+    FILE *file;
+    char *name;
+    int line;
+    int ungetc;
+    Token ungettok;
+} Tokenizer_FInfo;
+
+typedef struct _Tokenizer_Buffer{
+        char *data;
+        int len;
+        int pos;
+} Tokenizer_Buffer;
+    
+typedef struct _Tokenizer{
+    FILE *file;
+    char *name;
+    int line;
+    int ungetc;
+    Token ungettok;
+    
+    Tokenizer_Buffer buffer;
+
+    int flags;
+    const struct _ConfOpt **optstack;
+    int nest_lvl;
+    void *user_data;
+
+    int filestack_n;
+    Tokenizer_FInfo *filestack;
+    
+    char **includepaths;
+} Tokenizer;
+
+
+extern Tokenizer *tokz_open(const char *fname);
+extern Tokenizer *tokz_open_file(FILE *file, const char *fname);
+extern Tokenizer *tokz_prepare_buffer(char *buffer, int len);
+extern void tokz_close(Tokenizer *tokz);
+extern bool tokz_get_token(Tokenizer *tokz, Token *tok);
+extern void tokz_unget_token(Tokenizer *tokz, Token *tok);
+extern void tokz_warn_error(const Tokenizer *tokz, int line, int e);
+extern void tokz_warn(const Tokenizer *tokz, int line, const char *fmt, ...);
+
+extern bool tokz_pushf(Tokenizer *tokz, const char *fname);
+extern bool tokz_pushf_file(Tokenizer *tokz, FILE *file, const char *fname);
+extern bool tokz_popf(Tokenizer *tokz);
+
+extern void tokz_set_includepaths(Tokenizer *tokz, char **paths);
+
+#endif /* LIBTU_TOKENIZER_H */
diff --git a/libtu/types.h b/libtu/types.h
new file mode 100644 (file)
index 0000000..b2bd6a7
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * libtu/types.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_TYPES_H
+#define LIBTU_TYPES_H
+
+#include <sys/types.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+
+#ifndef LIBTU_TYPEDEF_UXXX
+
+ /* All systems seem to define these whichever way they want to
+  * despite -D_*_SOURCE etc. so there is no easy way to know whether
+  * they can be typedef'd or not. Unless you want to go through using
+  * autoconf or similar methods. ==> Just stick to #define. :-(
+  */
+  
+#ifndef uchar
+#define uchar unsigned char
+#endif
+
+#ifndef ushort
+#define ushort unsigned short
+#endif
+
+#ifndef uint
+#define uint unsigned int
+#endif
+
+#ifndef ulong
+#define ulong unsigned long
+#endif
+
+#else /* LIBTU_TYPEDEF_UXXX */
+
+#ifndef uchar
+typedef unsigned char uchar;
+#endif
+
+#ifndef ushort
+typedef unsigned short ushort;
+#endif
+
+#ifndef uint
+typedef unsigned int uint;
+#endif
+
+#ifndef ulong
+typedef unsigned long ulong;
+#endif
+#endif /* LIBTU_TYPEDEF_UXXX */
+
+
+#ifndef LIBTU_TYPEDEF_BOOL
+
+#ifndef bool
+#define bool int
+#endif
+#else /* LIBTU_TYPEDEF_BOOL */
+
+#ifndef bool
+typedef int bool;
+#endif
+#endif /* LIBTU_TYPEDEF_BOOL */
+
+#endif /* LIBTU_TYPES_H */
diff --git a/libtu/util.c b/libtu/util.c
new file mode 100644 (file)
index 0000000..aafdf13
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * libtu/util.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "locale.h"
+#include "util.h"
+#include "misc.h"
+
+
+static const char *progname=NULL;
+
+
+void libtu_init(const char *argv0)
+{
+    progname=argv0;
+
+#ifndef CF_NO_LOCALE
+    textdomain(simple_basename(argv0));
+#endif
+}
+
+
+const char *prog_execname()
+{
+    return progname;
+}
+
diff --git a/libtu/util.h b/libtu/util.h
new file mode 100644 (file)
index 0000000..c8bddcf
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * libtu/util.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#ifndef LIBTU_UTIL_H
+#define LIBTU_UTIL_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "types.h"
+#include "optparser.h"
+
+extern void libtu_init(const char *argv0);
+extern const char *prog_execname();
+
+#endif /* LIBTU_UTIL_H */
diff --git a/man/Makefile b/man/Makefile
new file mode 100644 (file)
index 0000000..c35c5a9
--- /dev/null
@@ -0,0 +1,70 @@
+##
+## Ion manual page Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+TRANSLATIONS=fi cs de
+
+WELCOME_TARGETS=\
+       welcome.txt \
+       $(foreach tr, $(TRANSLATIONS), welcome.$(tr).txt)
+
+TARGETS=ion3.1 $(foreach tr, $(TRANSLATIONS), ion3.$(tr).1) \
+       pwm3.1 $(foreach tr, $(TRANSLATIONS), pwm3.$(tr).1) \
+       $(WELCOME_TARGETS)
+
+MKMAN=$(LUA) ../build/mkman.lua
+NROFF=nroff -man -Tlatin1
+#FILTERCRAP=perl -p -i -e 's/.\10//g'
+FILTERCRAP=$(LUA) -e 'io.write((string.gsub(io.read("*a"), ".\8", "")))'
+
+CONFIGS=../etc/cfg_ioncore.lua \
+       ../etc/cfg_tiling.lua \
+       ../etc/cfg_query.lua \
+       ../etc/cfg_menu.lua
+
+# TODO: PWM configuration file is undocumented
+PWM_CONFIGS=\
+       ../etc/cfg_ioncore.lua \
+       ../etc/cfg_menu.lua
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+ion3.1: ion3.in $(CONFIGS)
+       $(MKMAN) -i $< -o $@ -D ETCDIR $(ETCDIR) -D DOCDIR $(DOCDIR) $(CONFIGS)
+
+pwm3.1: pwm3.in $(PWM_CONFIGS)
+       $(MKMAN) -i $< -o $@ -D ETCDIR $(ETCDIR) -D DOCDIR $(DOCDIR) $(PWM_CONFIGS)
+
+ion3.%.1: ion3.%.in $(CONFIGS) ../po/%.po
+       $(MKMAN) -po ../po/$*.po -i $< -o $@ -D ETCDIR $(ETCDIR) -D DOCDIR $(DOCDIR) $(CONFIGS)
+
+pwm3.%.1: pwm3.%.in $(PWM_CONFIGS) ../po/%.po
+       $(MKMAN) -po ../po/$*.po -i $< -o $@ -D ETCDIR $(ETCDIR) -D DOCDIR $(DOCDIR) $(PWM_CONFIGS)
+
+welcome%txt: welcome%head ion3%1
+       (cat welcome$*head; \
+       $(NROFF) ion3$*1 | $(FILTERCRAP)) > $@
+
+_install:
+       $(INSTALLDIR) $(MANDIR)/man1
+       $(INSTALL) -m $(DATA_MODE) ion3.1 $(MANDIR)/man1
+       $(INSTALL) -m $(DATA_MODE) pwm3.1 $(MANDIR)/man1
+       for tr in $(TRANSLATIONS); do \
+               $(INSTALLDIR) $(MANDIR)/$$tr/man1 ; \
+               $(INSTALL) -m $(DATA_MODE) ion3.$$tr.1 $(MANDIR)/$$tr/man1/ion3.1 ; \
+               $(INSTALL) -m $(DATA_MODE) pwm3.$$tr.1 $(MANDIR)/$$tr/man1/pwm3.1 ; \
+       done
+       $(INSTALLDIR) $(SHAREDIR)
+       for i in $(WELCOME_TARGETS); do \
+               $(INSTALL) -m $(DATA_MODE) $$i $(SHAREDIR) ; \
+       done
diff --git a/man/ion3.cs.in b/man/ion3.cs.in
new file mode 100644 (file)
index 0000000..b3a8c91
--- /dev/null
@@ -0,0 +1,169 @@
+.TH ION 1
+.SH NÁZEV
+Ion - správce oken systému X11
+.SH PØEHLED
+.B ion3
+.I "[volby]"
+.SH "POPIS"
+
+Ion je dla¾dicový správce oken se zálo¾kami navr¾ený pro klávesnicové u¾ivatele.
+.SH "VOLBY"
+.TP
+.B \-display poèítaè:displej.obrazovka
+X displej, který se má spravovat
+.TP
+.B \-conffile soubor
+Konfiguraèní soubor, který se má pou¾ít
+.TP
+.B \-searchdir adresáø
+Adresáø, ve kterém se budou hledat konfiguraèní soubory a ostatní skripty
+.TP
+.B \-oneroot
+Na X serverech s více obrazovkami (ne-Xinerama) bude spravovat pouze výchozí
+obrazovku (koøenové okno). (Tedy ne v¹echny, které byly zadány v parametru
+-display nebo v promìnné DISPLAY.)
+.TP
+.B \-session název_sezení
+Nastaví název sezení. Tato volba ovlivní, kam se ulo¾í pracovní plochy
+a ostatní soubory. Pokud není nastavená, pou¾ije se ~/.ion3/session_name.
+.TP
+.B \-xinerama 0|1
+Jestli¾e byl Ion sestaven s podporou Xineramy, mù¾ete touto volbou
+povolit/zakázat (1/0) její pou¾ití. Implicitnì je Xinerama povolena.
+.TP
+.B \-help
+Zobrazí nápovìdu k pøíkazovým parametrùm
+.TP
+.B \-version
+Zobrazí verzi
+.TP
+.B \-about
+Zobrazí informace o programu (verze, copyright)
+
+.SH Základní pojetí
+
+Tato sekce pøiná¹í pøehled typù objektù, které se objevují na X displeji
+spravovaném Ionem. To je nezbytné pro pochopení operací s objekty a pro 
+pochopení, proè jsou rùzná klávesová pøiøazení dostupná jen u nìkterých
+objektù. Podrobnìj¹í popis nutný pro psaní vlastních pøizpùsobení naleznete
+na webových stránkách Ionu.
+
+Nejvy¹¹ími objekty jsou \fBobrazovky\fP, které odpovídají fyzickým
+obrazovkám. Obrazovky obsahují \fBpracovní plochy\fP (co¾ je nìco jako
+\fBskupiny\fP) a \fBklientská okna\fP v celoobrazovkovém re¾imu. V jeden
+okam¾ik mù¾e být na obrazovce zobrazen právì jeden takový objekt.
+
+Pracovní plochy mohou obsahovat \fBdla¾dice\fP a odpojené/plovoucí
+\fBrámy\fP. Dla¾dice obsahují \fBrámy\fP tak, aby bezezbytku vyplnily
+(vydlá¾dily) celou obrazovku, a pøípadnì stavový øádek a dok. Podobnì
+jako obrazovky obsahují i rámy dal¹í objekty, ale v tomto pøípadì ji¾
+jde vìt¹inou o \fBskupiny\fP klientských oken. Po vìt¹inu èasu bývají
+rámy jedinou viditelnou èástí Ionu - volitelnì mohou mít kolem sebe
+orámování a pro ka¾dého potomka zobrazují \fBzálo¾ku\fP.
+
+Dal¹í viditelnou èástí jsou \fBdotazy\fP. Nejde o nic jiného ne¾ obdélníky,
+které se objeví ve spodní èásti rámù nebo obrazovek v¾dy, kdy¾ se Ion ptá
+na nìjakou informaci (tøeba název okna pro pøipojení, nebo název programu,
+který se má spustit). Vìt¹ina dotazù podporuje doplòování klávesou tab.
+
+
+.SH PØIØAZENÍ
+
+Toto jsou výchozí pøiøazení klávesových zkratek. Pøiøazení modifikátoru
+(\fBMod1\fP) závisí na systému. Na PCèkách s XFree86 bude nejspí¹ navázán
+na levou klávesu Alt (\fBAlt_L\fP). Na Sunech je obvykle namapován na klávesy
+s diamanty (\fBMeta_L\fP, \fBMeta_R\fP). Konkrétní pøiøazení zjistíte
+programem \fIxmodmap(1)\fP.
+
+Øetìzec v hranatých závorkách urèuje modul, jeho¾ konfiguraèní soubor
+definuje toto pøiøazení.
+
+.SS Globální pøiøazení
+
+BINDINGS:WScreen
+
+.SS Pøiøazení pro práci s nejvy¹¹ími rámy a obrazovkami a jejich potomky
+
+BINDINGS:WMPlex.toplevel
+
+.SS Pøiøazení pro práci s rámy a obrazovkami a jejich potomky
+
+BINDINGS:WMPlex
+
+.SS Pøiøazení pro práci se v¹emi rámy (vèetnì doèasných) a jejich potomky
+
+BINDINGS:WFrame
+
+.SS Pøiøazení pro práci s nejvy¹¹ími rámy (ne s doèasnými) a jejich potomky
+
+BINDINGS:WFrame.toplevel
+
+.SS Pøiøazení pro práci s plovoucími/odpojenými rámy
+
+BINDINGS:WFloatFrame
+
+.SS Pøiøazení pro dla¾dicové plochy a rámy [mod_tiling]
+
+BINDINGS:WTiling
+BINDINGS:WFrame-on-WTiling
+
+.SS Pøiøazení pro kientská okna
+
+BINDINGS:WClientWin
+
+BINDINGS:WGroupWS
+
+.SS Pøiøazení pro pøesun/zmìnu velikosti
+
+BINDINGS:WMoveresMode
+
+.SS Pøiøazení pro informaèní zprávy a dotazy [mod_query]
+
+BINDINGS:WInput
+
+.SS Pøiøazení pro úpravu dotazù [mod_query]
+
+Tyto jsou podobné jako v textovém editoru \fIjoe(1)\fP.
+Vyjmutí, kopírování a vlo¾ení sice pracují mírnì konvenènìji, ale
+klávesy jsou shodné.
+
+BINDINGS:WEdln
+
+.SS Pøiøazení pro menu [mod_menu]
+
+BINDINGS:WMenu
+
+
+.SH SOUBORY A ADRESÁØE
+.TP
+.B ETCDIR/cfg_ion.lua
+Hlavní systémový konfiguraèní soubor
+.TP
+.B ETCDIR/cfg_*.lua
+Ostatní konfiguraèní soubory
+.TP
+.B ETCDIR/look_*.lua
+Soubory nastavující barevné schéma
+.TP
+.B ~/.ion3/
+U¾ivatelské konfiguraèní soubory
+.TP
+.B ~/.ion3/cfg_ion.lua
+Hlavní u¾ivatelský konfiguraèní soubor (pøepisuje systémové nastavení)
+
+.SH VIZ TAKÉ
+Domovská stránka Ionu \fIhttp://iki.fi/tuomov/ion/\fP
+.PP
+Dokument "Ion: Configuring and extending with Lua" k nalezení
+tamté¾.
+.PP
+.I DOCDIR/
+.PP
+\fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP
+
+.SH AUTOR
+Ion napsal Tuomo Valkonen <tuomov at iki.fi>.
+
+.SH PØEKLAD
+Do èe¹tiny pøelo¾il Miroslav Kuøe <kurem at debian.cz>.
diff --git a/man/ion3.de.in b/man/ion3.de.in
new file mode 100644 (file)
index 0000000..3a2106f
--- /dev/null
@@ -0,0 +1,164 @@
+.TH ION 1
+.SH NAME
+Ion - ein X11 Fenster Manager
+.SH ÜBERSICHT
+.B ion3
+.I "[optionen]"
+.SH "BESCHREIBUNG"
+
+Ion ist ein tabellarisch aufgebauter Fenster Manager, entwickelt für 
+Benutzer welche auf die Tastatur spezialisiert sind.  
+.SH "OPTIONEN"
+.TP
+.B \-display host:display.screen
+X Anzeige zum darstellen
+.TP
+.B \-conffile Konfigurationsdatei
+Konfigurationsdatei welche anstelle der Vorgegebenen benutzt werden soll
+.TP
+.B \-searchdir Verzeichnis
+Verzeichnis worin die Konfigurationsdateien und andere Scripte gesucht werden
+.TP
+.B \-oneroot
+Auf X Servern mit mehreren (traditionell non-Xinerama) Screens; handhabt nur den 
+voreingestellten Screen (Wurzelfenster); nicht alle, welche durch die '-display'
+Option oder in der DISPLAY Umgebungsvariable bereitgestellt werden
+.TP
+.B \-session sitzungs_name
+Setzt einen Sitzungsnamen. Diese Option beeinflusst wo der Arbeitsbereich und
+andere Sicherungsdateien abgelegt werden (~/.ion3/sitzungs_name wenn die Option
+gesetzt wurde).
+.TP
+.B \-xinerama 0|1
+Wenn Ion mit Xinerama Unterstützung kompiliert wurde, kann diese Option benutzt
+werden um sie 'an = 1' oder 'aus = 0' zu stellen. Die Voreinstelllung ist
+Xineramas Screeninformation zu benutzen.
+.TP
+.B \-help
+Zeigt die Hilfe für die Kommandozeilenoptionen
+.TP
+.B \-version
+Zeigt die Version
+.TP
+.B \-about
+Zeigt Informationen über den Autor, die Version und das Copyright
+
+.SH BASIS KONZEPTE
+
+Diese Kapitel ist ein Überblick über die Objekttypen welche auf der X Anzeige
+durch Ion behandelt werden. Die Beziehungen sind auf die Standardeinstellungen
+ausgerichtet. Diese Informationen hier sind nötig um die Operationen und
+Möglichkeiten der unterschiedlichen Tastenkombinationen zu verstehen. Siehe
+dazu auch die weiteren Erklärungen unten. Für detailliertere Erklärungen,
+welche zum schreiben eigener Konfigurationsdateien benötigt werden, siehe die
+zusätzlichen Dokumentationen auf der Ion Webseite
+
+Die obersten Objekte welche benutzt werden können sind \fBScreens\fP.
+Sie sind mit physikalischen Sceeens zu vergleichen. Screens enthalten 
+\fBArbeitsbereiche\fP und \fBClient Fenster\fP gepackt in einem Vollbildmodus.
+Diese Objekte sind \fBgebündelt\fP, mit dem Hintergrund, dass nur einer auf
+einmal angezeigt werden kann.   
+
+Derzeit unterstützt Ion zwei Arten von Arbeitsbereichen; \fBgeteilt\fP in den
+traditionellen Ion-Style und den herkömlichen \fBfliessenden\fP Arbeitsbereich
+des PWM. Arbeitsbereiche sind keine sichtbaren Objekte sondern eher eine
+Sammlung von gleichzeitig erkennbaren \fBFrames\fP.
+
+Ähnlich wie Screens, bündeln Frames andere Objekte, in diesem Fall sind dies
+meist Client Fenster. Die meiste Zeit, sind Frames das einzige von Ion was auf
+dem Screen zu sehen ist. Frames können Rahmen haben und sie haben einen 
+\fBReiter\fP für jedes in ihnen gebündelte Objekt.
+
+\fBAnfragen\fP sind Textboxen die am unteren Rand eines Frames oder Screens
+erscheinen. Sie können nach Texteingaben zum ausführen einer Aktion verlangen.
+Diese sind abhängig von den an sie gebundenen Aktionen. Die meisten Anfragen
+unterstützen Autovervollständigung.    
+
+.SH BINDUNGEN
+
+Hier sind die voreingestellten Tasten- und Mauszeigerbindungen beschrieben.
+(\fBMod1\fP) hängt vom jeweiligen verwendeten System ab. Auf PC's mit XFree86
+ist es höchstwahrscheinlich an die linke 'ALT-Taste' gebungen (\fBALT_L\fP).
+Auf SUN Systemen ist es sicherlich an die 'Diamant' Tasten gebunden
+(\fBMETA_L\fP, \fBMETA_R\fP).  Benutze \fIxmodmap(1x)\fP um die Belegung
+herauszufinden. 
+
+Die Zeichenkette in den eckigen Klammern nach einer Bindungsgruppe (wird unten
+gezeigt) deutet auf die Konfigurationsdatei des Modules in welchem die Bindungen
+definiert sind. 
+
+.SS Global verfügbare Bindungen
+
+BINDINGS:WScreen
+
+.".SS Bindungsoperationen auf Frames und Screens und ihren Kindern
+
+BINDINGS:WMPlex
+
+.SS Bindungsoperationen auf Frames und ihren Kindern 
+
+BINDINGS:WFrame
+
+.SS Bindungen zum Bewegungs-/Veränderungsmodus 
+
+BINDINGS:WMoveresMode
+
+.SS Bindungen zum teilen der Arbeitsfläche [mod_ionws]
+
+BINDINGS:WTiling
+
+.SS Bindungen für den fliessenden Arbeitsbereich und Frames [mod_floatws]
+
+BINDINGS:WGroupWS
+
+BINDINGS:WFloatFrame
+
+.SS Bindungen für Message- und Anfrageboxen [mod_query]
+
+BINDINGS:WInput
+
+.SS Bindungen zum Editieren in Anfragen [mod_query]
+
+Diese Bindungen sind vergleichbar mit denen des \fIjoe(1)\fP Texteditors.
+Auschneiden, Kopieren und Einfügen arbeiten nach konventionellen
+Gesichtspunkten. Etwas überarbeitet aber die gleichen Tasten. 
+
+BINDINGS:WEdln
+
+.SS Bindungen für die Menüs [mod_menu]
+
+BINDINGS:WMenu
+
+
+.SH DATEIEN und VERZEICHNISSE
+.TP
+.B ETCDIR/cfg_ion.lua
+Voreingestellte Hauptkonfigurationsdateien
+.TP
+.B ETCDIR/cfg_*.lua
+Andere Konfigurationsdateien
+.TP
+.B ETCDIR/look_*.lua
+Farbschemata Konfigurationsdateien
+.TP
+.B ~/.ion3/
+Benutzerkonfigurationsdateien
+.TP
+.B ~/.ion3/cfg_ion.lua
+Voreingestellte Benutzer Hauptkonfigurationsdateien (überschreiben die
+systemweiten Konfigurationen) 
+
+.SH SIEHE AUCH
+Die Ion Homepage, \fIhttp://iki.fi/tuomov/ion/\fP
+.PP
+Das Dokument "Ion: Configuring and extending with Lua" kann auf der Ion Webseite
+gefunden werden.
+
+.PP
+.I DOCDIR/
+.PP
+\fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP
+
+.SH AUTOR
+Ion wurde von Tuomo Valkonen <tuomov at iki.fi> geschrieben
diff --git a/man/ion3.fi.in b/man/ion3.fi.in
new file mode 100644 (file)
index 0000000..ae82a00
--- /dev/null
@@ -0,0 +1,169 @@
+.TH ION 1
+.SH NIMI
+Ion - X11 ikkunanhallintaohjelma
+.SH SELOSTE
+.B ion3
+.I "[valinnat]"
+.SH "KUVAUS"
+
+Ion on näppäimistökäyttäjiä silmälläpitäen suunniteltu laatoitusta ja 
+välilehtiä hyödyntävä ikkunanhallintaohjelma X11:lle.
+.SH "VALINNAT"
+.TP
+.B \-display kone:palvelin.näyttö
+X palvelin, johon ottaa yhteys.
+.TP
+.B \-conffile asetustiedosto
+Käytettävä asetustiedosto.
+.TP
+.B \-searchdir dir
+Hakupolkuun lisättävä hakemisto, josta löytyy asetus- ja muita tiedostoja.
+.TP
+.B \-oneroot
+Hallitse vain oletusnäyttöä X-palvelimilla, joissa on useampi perinteinen
+(ei Xinerama) näyttö/juuri-ikkuna. Tämä näyttö voidaan valita -display
+parametrillä tai DISPLAY ympäristömuuttujalla.
+.TP
+.B \-session session_name
+Istunnon nimi. Tämä vaikuttaa talletustiedostojen sijaintiin.
+.TP
+.B \-xinerama 0|1
+Jos Ion käännettiin Xinerama-tuella, voidaan Xinerama tieto silti jättää
+hyödyntämättä  asettamalla tämä valinta arvoon 0.
+.TP
+.B \-help
+Näytä komentorivin ohje.
+.TP
+.B \-version
+Näytä Ionin versio.
+.TP
+.B \-about
+Näytä tietoja Ionista (versio, tekijänoikeudet).
+
+.SH PERUSKÄSITTEET
+
+Tämä kappale on katsaus erilaisiin niin kutsuttuihin \fBolioihin\fP,
+joita ilmenee Ionin hallitsemalla X-näytöllä, ja niiden suhteisiin 
+tavallisessa kokoonpanossa. Sinun tarvitse ymmärtää nämä suhteet 
+tietääksesi milloin mitkäkin alempana kuvattavat näppäinsidonnat 
+ovat käytettävissäsi. Jos kaipaat vielä tarkempaa kuvausta, esimerkiksi
+tehdäksesi omat asetustiedostot, katso Ionin seittisivulta saatavaa 
+yksityiskohtaisempaa dokumentaatiota.
+
+Ylimmän tason olioita, joilla on merkitystä tässä yhteydessä, 
+kutsutaan \fBnäytöiksi\fP. Ne vastaavat fyysisiä näyttöjä. Näytöt
+sisältävät \fBtyöpöytiä\fP (jotka ovat itse eräänlaisia \fBryhmiä\fP)
+ja kokoruudun tilassa olevia \fBasiakasikkunoita\fP. Nämä oliot ovat 
+\fBlomitettu\fP siten, että vain yksi voi näkyä kerrallaan.
+
+Työpöydät voivat vuorostaan sisältää \fBlaatoituksia\fP sekä
+kelluvia/irroitettuja \fBkehyksiä\fP. Laatoitukset taasen sisältävät
+\fBkehyksiä\fP laatoitettuna täyttämään koko näytön, sekä mahdollisesti
+tilapalkin tai telakan (dock). Näyttöjen tapaan kehykset lomittavat 
+muita olioita, mutta tässä tapauksessa lähinnä asiakasikkuna\fBryhmiä\fP. 
+Kehykset ovat suurimman osan ajasta ainut asia, minkä näet Ionista. Niillä
+voi olla reunakoristukset, ja lisäksi kehyksissä on \fBvälilehti\fP 
+jokaiselle lomitetulle oliolle.
+
+\fBKyselyt\fP ovat näyttöjen tai kehysten alaosiin imestyviä laatikoita,
+jotka odottavat sinulta tekstin syöttöä jonkin siitä riippuvan toiminnon
+suorittamiseksi. Useimmat kyselyt tukevat tab-täydennystä.
+
+
+.SH SIDONNAT
+
+Nämä ovat oletusarvoiset näppäin- ja osoitinsidonnat. Näppäinmuunninta
+(\fBMod1\fP) vastaava näppäin riippuu järjestelmästäsi. PC-tietokoneilla,
+joissa on XFree86 se on todennäköisesti vasen Alt-näppäin (\fBAlt_L\fP).
+Suneilla se on luultavasti sidottu timanttinäppäimiin. Käytä komentoa
+\fIxmodmap(1x)\fP selvittääksesi asian tarkemmin.
+
+Otsikkojen perässä hakasuluissa oleva merkkijono ilmoittaa moduulin,
+jonka asetustiedosto määrittelee alla olevat sidonnat.
+
+.SS Yleisesti saatavilla olevat sidonnat
+
+BINDINGS:WScreen
+
+.SS Näytöillä tai ylimmän tason kehyksissä toimivat sidonnat
+
+BINDINGS:WMPlex.toplevel
+
+.SS Näytöillä tai (kaikissa) kehyksissä toimivat sidonnat
+
+BINDINGS:WMPlex
+
+.SS Kaikissa kehyksissä toimivat sidonnat
+
+BINDINGS:WFrame
+
+.SS Ylimmän tason kehyksissä (ei ns. transient-dialogi) toimivat sidonnat
+
+BINDINGS:WFrame.toplevel
+
+.SS Kelluvissa/irroitetuissa kehyksissä toimivat sidonnat
+
+BINDINGS:WFloatFrame
+
+.SS Laatoituksien ja laatoitettujen kehysten sidonnat [mod_tiling]
+
+BINDINGS:WTiling
+BINDINGS:WFrame-on-WTiling
+
+.SS Asiakasikkunoiden sidonnat
+
+BINDINGS:WClientWin
+
+BINDINGS:WGroupCW
+
+.SS Siirto- ja koonmuutostilan sidonnat
+
+BINDINGS:WMoveresMode
+
+.SS Viestilaatikoiden ja kyselyiden sidonnat [mod_query]
+
+BINDINGS:WInput
+
+.SS Tekstinmuokkaussidonnat kyselyihin [mod_query]
+
+Nämä näppäinsidonnat vastaavat tekstieditorin \fIjoe(1)\fP sidontoja.
+Leikkaus ja liimaus toimii kuitenkin tavanomaisemmin, joskin näppäimet
+ovat vastaavat.
+
+BINDINGS:WEdln
+
+.SS Valikoiden sidonnat [mod_menu]
+
+BINDINGS:WMenu
+
+
+.SH TIEDOSTOT JA HAKEMISTOT
+.TP
+.B ETCDIR/cfg_ion.lua
+Järjestelmän oletusarvoinen pääasetustiedosto.
+.TP
+.B ETCDIR/cfg_*.lua
+Muita asetustiedostoja.
+.TP
+.B ETCDIR/look_*.lua
+Ulkonäön asetustiedostoja.
+.TP
+.B ~/.ion3/
+Käyttäjän asetustiedostot.
+.TP
+.B ~/.ion3/cfg_ion.lua
+Käyttäjän oletusarvoinen pääasetustiedosto.
+
+.SH KATSO MYÖS
+Ionin kotisivu, \fIhttp://iki.fi/tuomov/ion/\fP (englanniksi).
+.PP
+Dokumentti "Ion: Configuring and extending with Lua" (englanniksi)
+nähtävissä Ionin kotisivulla.
+.PP
+.I DOCDIR/
+.PP
+\fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP
+
+.SH TEKIJÄ
+Ionin on kirjoittanut Tuomo Valkonen <tuomov at iki.fi>.
diff --git a/man/ion3.in b/man/ion3.in
new file mode 100644 (file)
index 0000000..5ddbb33
--- /dev/null
@@ -0,0 +1,167 @@
+.TH ION 1
+.SH NAME
+Ion - an X11 window manager
+.SH SYNOPSIS
+.B ion3
+.I "[options]"
+.SH "DESCRIPTION"
+
+Ion is a tiling tabbed window manager designed with keyboard users in mind.
+.SH "OPTIONS"
+.TP
+.B \-display host:display.screen
+X display to manage
+.TP
+.B \-conffile configfile
+Configuration file to use instead of default
+.TP
+.B \-searchdir dir
+Directory to search for configuration files and other scripts
+.TP
+.B \-oneroot
+On X servers with multiple (traditional non-Xinerama) screens, manage only
+default screen (root window), not all, as given by the -display option or
+in the DISPLAY environment variable.
+.TP
+.B \-session session_name
+Set session name. This option affects where workspace and other save files are
+put (~/.ion3/session_name if option set).
+.TP
+.B \-xinerama 0|1
+If Ion was compiled with Xinerama support, this option can be used to
+enable/disable (1/0) the use of it. The default is to use Xinerama
+screen information.
+.TP
+.B \-help
+Show help on command line options
+.TP
+.B \-version
+Show version
+.TP
+.B \-about
+Show about text (version, copyright)
+
+.SH BASIC CONCEPTS
+
+This section is an overview of the types objects that appear on an X
+display managed by Ion and their relationships in a standard setup. This
+information is necessary to understand the operations and availability
+of the different key bindings explained below. For a more detailed 
+explanation, needed for writing custom bindings configuration files,
+see the additional documentation available from the Ion Web page.
+
+The top-level objects that matter in the case at hand are \fBscreens\fP.
+They correspond to physical screens. Screens contain \fBworkspaces\fP
+(which are a kind of \fBgroup\fP), and \fBclient windows\fP put into full
+screen mode. These objects are \fBmultiplexed\fP in the sense that only
+one can be displayed at a time.
+
+Workspaces themselves may contain \fBtilings\fP and detached/floating
+\fBframes\fP. Tilings themselves contain \fBframes\fP tiled to fill the
+screen, and possibly a statusbar or dock. Akin to screens, frames multiplex
+other objects, but in this case mostly client window \fBgroups\fP. Most of 
+the time, frames are the only trace of Ion you see on the screen. Frames 
+may have border decorations, and they have a \fBtab\fP for each multiplexed
+object.
+
+\fBQueries\fP are boxes that appear at the bottoms of frames or screens 
+to ask you for textual input to execute an action that depends on it.
+Most queries support tab-completion.
+
+
+.SH BINDINGS
+
+These are the default key and pointer bindings. (\fBMod1\fP) depends on your
+system. On PC:s with XFree86 it is probably bound to the left Alt key
+(\fBAlt_L\fP). On Suns it is usually bound to the diamond keys
+(\fBMeta_L\fP, \fBMeta_R\fP). Use \fIxmodmap(1x)\fP to find out.
+
+The string in square brackets after a binding group heading below indicates
+the module that whose configuration file defines these bindings.
+
+.SS Globally available bindings
+
+BINDINGS:WScreen
+
+.SS Bindings operating on top-level frames and screens and their children
+
+BINDINGS:WMPlex.toplevel
+
+.SS Bindings operating on frames and screens and their children
+
+BINDINGS:WMPlex
+
+.SS Bindings operating on all frames (including transient) and their children
+
+BINDINGS:WFrame
+
+.SS Bindings operating on top-level (non-transient) frames and their children
+
+BINDINGS:WFrame.toplevel
+
+.SS Bindings for floating/detached frames
+
+BINDINGS:WFloatFrame
+
+.SS Bindings for tilings and tiled frames [mod_tiling]
+
+BINDINGS:WTiling
+BINDINGS:WFrame-on-WTiling
+
+.SS Bindings for client windows
+
+BINDINGS:WClientWin
+
+BINDINGS:WGroupCW
+
+.SS Move/resize mode bindings
+
+BINDINGS:WMoveresMode
+
+.SS Bindings for message boxes and queries [mod_query]
+
+BINDINGS:WInput
+
+.SS Bindings for editing in queries [mod_query]
+
+These bindings are similar to those of the \fIjoe(1)\fP text editor.
+Cut, copy and paste works in a more conventional manner, though, but
+the keys are equivalent.
+
+BINDINGS:WEdln
+
+.SS Bindings for menus [mod_menu]
+
+BINDINGS:WMenu
+
+
+.SH FILES AND DIRECTORIES
+.TP
+.B ETCDIR/cfg_ion.lua
+System default main configuration files
+.TP
+.B ETCDIR/cfg_*.lua
+Other configuration files.
+.TP
+.B ETCDIR/look_*.lua
+Colour scheme configuration files
+.TP
+.B ~/.ion3/
+User configuration files
+.TP
+.B ~/.ion3/cfg_ion.lua
+User default main configuration file (overrides system default)
+
+.SH SEE ALSO
+The Ion home page, \fIhttp://iki.fi/tuomov/ion/\fP
+.PP
+The document "Ion: Configuring and extending with Lua" found on the
+Ion home page.
+.PP
+.I DOCDIR/
+.PP
+\fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP
+
+.SH AUTHOR
+Ion was written by Tuomo Valkonen <tuomov at iki.fi>.
diff --git a/man/pwm3.cs.in b/man/pwm3.cs.in
new file mode 100644 (file)
index 0000000..a45f74c
--- /dev/null
@@ -0,0 +1,110 @@
+.TH PWM 1
+.SH NÁZEV
+PWM - správce oken systému X11
+.SH PØEHLED
+.B pwm3
+.I "[volby]"
+.SH "POPIS"
+
+Pùvodní PWM byl první správce oken, který pøinesl zálo¾ky. Aktuální verze
+PWM je zalo¾ena na zdrojových kódech Ionu a ve skuteènosti to je jeden a ten
+samý program, pouze s jinými konfiguraèními soubory a nìkolika drobnostmi.
+
+.SH "VOLBY"
+.TP
+.B \-display poèítaè:displej.obrazovka
+X displej, který se má spravovat
+.TP
+.B \-conffile soubor
+Konfiguraèní soubor, který se má pou¾ít
+.TP
+.B \-searchdir adresáø
+Adresáø, ve kterém se budou hledat konfiguraèní soubory a ostatní skripty
+.TP
+.B \-oneroot
+Na X serverech s více obrazovkami (ne-Xinerama) bude spravovat pouze výchozí
+obrazovku (koøenové okno). (Tedy ne v¹echny, které byly zadány v parametru
+-display nebo v promìnné DISPLAY.)
+.TP
+.B \-sessionname session_name
+Nastaví název sezení. Tato volba ovlivní, kam se ulo¾í pracovní plochy
+a ostatní soubory. Pokud není nastavená, pou¾ije se ~/.pwm3/session_name.
+.TP
+.B \-xinerama 0|1
+Jestli¾e byl Ioncore sestaven s podporou Xineramy, mù¾ete touto volbou
+povolit/zakázat (1/0) její pou¾ití. Implicitnì je Xinerama _zakázána_.
+.TP
+.B \-help
+Zobrazí nápovìdu k pøíkazovým parametrùm
+.TP
+.B \-version
+Zobrazí verzi
+.TP
+.B \-about
+Zobrazí informace o programu (verze, copyright)
+
+.SH PØIØAZENÍ
+
+Toto jsou implicitní pøiøazení klávesových zkratek. Pøiøazení modifikátoru
+(\fBMod1\fP) závisí na systému. Na PCèkách s XFree86 bude nejspí¹ navázán
+na levou klávesu Alt (\fBAlt_L\fP). Na Sunech je obvykle namapován na klávesy
+s diamanty (\fBMeta_L\fP, \fBMeta_R\fP). Konkrétní pøiøazení zjistíte
+programem \fIxmodmap(1x)\fP.
+
+Øetìzec v hranatých závorkách urèuje modul, jeho¾ konfiguraèní soubor
+definuje toto pøiøazení.
+
+.SS Globální pøiøazení
+
+BINDINGS:WScreen
+
+.".SS Pøiøazení pro práci s rámy, obrazovkami a jejich potomky
+
+BINDINGS:WMPlex
+
+.SS Pøiøazení pro práci s rámy a jejich potomky
+
+BINDINGS:WFrame
+
+.SS Pøiøazení pro pøesun/zmìnu velikosti
+
+BINDINGS:WMoveresMode
+
+.SS Pøiøazení pro plovoucí pracovní plochy [mod_floatws]
+
+BINDINGS:WGroupWS
+
+BINDINGS:WFloatFrame
+
+.SS Pøiøazení pro menu [mod_menu]
+
+BINDINGS:WMenu
+
+
+.SH SOUBORY A ADRESÁØE
+.TP
+.B ETCDIR/cfg_pwm.lua
+Hlavní systémový konfiguraèní soubor
+.TP
+.B ETCDIR/cfg_*.lua
+Ostatní konfiguraèní soubory
+.TP
+.B ETCDIR/look_*.lua
+Soubory nastavující barevné schéma
+.TP
+.B ~/.pwm3/
+U¾ivatelské konfiguraèní soubory
+.TP
+.B ~/.pwm3/cfg_pwm.lua
+Hlavní u¾ivatelský konfiguraèní soubor (pøepisuje systémové nastavení)
+
+.SH VIZ TAKÉ
+
+Více informací naleznete v
+\fIion3(1)\fP.
+
+.SH AUTOR
+Programy PWM a Ion napsal Tuomo Valkonen <tuomov at iki.fi>.
+
+.SH PØEKLAD
+Do èe¹tiny pøelo¾il Miroslav Kuøe <kurem at debian.cz>.
diff --git a/man/pwm3.de.in b/man/pwm3.de.in
new file mode 100644 (file)
index 0000000..fe38739
--- /dev/null
@@ -0,0 +1,111 @@
+.TH PWM 1
+.SH NAME
+PWM - ein X11 Fenster Manager
+.SH ÜBERSICHT
+.B pwm3
+.I "[optionen]"
+.SH "BESCHREIBUNG"
+
+Der originale PWM war der erste Fenster Manager, welcher einen tabellenartigen 
+Aufbau seiner Objekte unterstützte. Diese Version des PWM basiert auf dem
+Quellcode von Ion und unterscheidet sich zu diesem nur durch Unterschiede in den
+Konfigurationsdateien, den Pfaden und einigen wenigen zusätzliche Optionen. 
+
+.SH "OPTIONEN"
+.TP
+.B \-display host:display.screen
+X Anzeige zum darstellen
+.TP
+.B \-conffile Konfigurationsdatei
+Konfigurationsdatei welche anstelle der Vorgegebenen benutzt werden soll
+.TP
+.B \-searchdir dir
+Verzeichnis worin die Konfigurationsdateien und andere Scripte gesucht werden
+.TP
+.B \-oneroot
+Auf X Servern mit mehreren (traditionell non-Xinerama) Screens; handhabt nur den 
+voreingestellten Screen (Wurzelfenster); nicht alle, welche durch die '-display'
+Option oder in der DISPLAY Umgebungsvariable bereitgestellt werden
+.TP
+.B \-sessionname sitzungs_name
+Setzt einen Sitzungsnamen. Diese Option beeinflusst wo der Arbeitsbereich und
+andere Sicherungsdateien abgelegt werden (~/.pwm3/sitzungs_name wenn die Option
+gesetzt wurde).
+.TP
+.B \-xinerama 0|1
+Wenn Ion mit Xinerama Unterstützung kompiliert wurde, kann diese Option benutzt
+werden um sie 'an = 1' oder 'aus = 0' zu stellen. Die Voreinstelllung ist
+Xineramas Screeninformation 'NICHT' zu benutzen.
+.TP
+.B \-help
+Zeigt die Hilfe für die Kommandozeilenoptionen
+.TP
+.B \-version
+Zeigt die Version
+.TP
+.B \-about
+Zeigt Informationen über den Autor, die Version und das Copyright
+
+.SH BINDUNGEN
+Hier sind die voreingestellten Tasten- und Mauszeigerbindungen beschrieben.
+(\fBMod1\fP) hängt vom jeweiligen verwendeten System ab. Auf PC's mit XFree86
+ist es höchstwahrscheinlich an die linke 'ALT-Taste' gebungen (\fBALT_L\fP).
+Auf SUN Systemen ist es sicherlich an die 'Diamant' Tasten gebunden
+(\fBMETA_L\fP, \fBMETA_R\fP).  Benutze \fIxmodmap(1x)\fP um die Belegung
+herauszufinden. 
+
+Die Zeichenkette in den eckigen Klammern nach einer Bindungsgruppe (wird unten
+gezeigt) deutet auf die Konfigurationsdatei des Modules in welchem die Bindungen
+definiert sind. 
+
+.SS Global verfügbare Bindungen
+
+BINDINGS:WScreen
+
+.".SS Bindungsoperationen auf Frames und Screens und ihren Kindern
+
+BINDINGS:WMPlex
+
+.SS Bindungsoperationen auf Frames und ihren Kindern 
+
+BINDINGS:WFrame
+
+.SS Bindungen zum Bewegungs-/Veränderungsmodus 
+
+BINDINGS:WMoveresMode
+
+.SS Bindungen für den fliessenden Arbeitsbereich und Frames [mod_floatws]
+
+BINDINGS:WGroupWS
+
+BINDINGS:WFloatFrame
+
+.SS Bindungen für die Menüs [mod_menu]
+
+BINDINGS:WMenu
+
+
+.SH DATEIEN und VERZEICHNISSE
+.TP
+.B ETCDIR/cfg_pwm.lua
+Voreingestellte Hauptkonfigurationsdateien
+.TP
+.B ETCDIR/cfg_*.lua
+Andere Konfigurationsdateien
+.TP
+.B ETCDIR/look_*.lua
+Farbschemata Konfigurationsdateien
+.TP
+.B ~/.pwm3/
+Benutzerkonfigurationsdateien
+.TP
+.B ~/.pwm3/cfg_pwm.lua
+Voreingestellte Benutzer Hauptkonfigurationsdateien (überschreiben die
+systemweiten Konfigurationen)
+
+.SH SIEHE AUCH
+
+Weitere Informationen siehe \fIion3(1)\fP.
+
+.SH AUTOR
+PWM und Ion wurde von Tuomo Valkonen <tuomov at iki.fi> geschrieben
diff --git a/man/pwm3.fi.in b/man/pwm3.fi.in
new file mode 100644 (file)
index 0000000..6e634c9
--- /dev/null
@@ -0,0 +1,107 @@
+.TH ION 1
+.SH NIMI
+PWM - X11 ikkunanhallintaohjelma
+.SH SELOSTE
+.B pwm3
+.I "[valinnat]"
+.SH "KUVAUS"
+
+Alkuperäinen PWM oli ensimmäinen ohjelma, joka toi välilehdet
+ikkunanhallintaohjelman tasolle. Tämä versio PWM:stä taas pohjautuu
+Ioniin ja sen mod_floatws moduuliin hieman eri oletusasetuksilla.
+.SH "VALINNAT"
+.TP
+.B \-display kone:palvelin.näyttö
+X palvelin, johon ottaa yhteys.
+.TP
+.B \-conffile asetustiedosto
+Käytettävä asetustiedosto.
+.TP
+.B \-searchdir dir
+Hakupolkuun lisättävä hakemisto, josta löytyy asetus- ja muita tiedostoja.
+.TP
+.B \-oneroot
+Hallitse vain oletusnäyttöä X-palvelimilla, joissa on useampi perinteinen
+(ei Xinerama) näyttö/juuri-ikkuna. Tämä näyttö voidaan valita -display
+parametrillä tai DISPLAY ympäristömuuttujalla.
+.TP
+.B \-sessionname session_name
+Istunnon nimi. Tämä vaikuttaa talletustiedostojen sijaintiin.
+.TP
+.B \-xinerama 0|1
+PWM ei oletuksena käytä Xinerama-tietoa Ionista poiketen. Jos se kuitenkin
+käännettiin Xinerama-tuella, voidaan tuo tieto ottaa käyttää asettamalla
+tämän parametrin arvoksi 1. Tuolloin kaikilla Xinerama-näytöillä on 
+erilliset työpöydät.
+.TP
+.B \-help
+Näytä komentorivin ohje.
+.TP
+.B \-version
+Näytä Ionin versio.
+.TP
+.B \-about
+Näytä tietoja PWM:sta (versio, tekijänoikeudet).
+
+.SH SIDONNAT
+
+Nämä ovat oletusarvoiset näppäin- ja osoitinsidonnat. Näppäinmuunninta
+(\fBMod1\fP) vastaava näppäin riippuu järjestelmästäsi. PC-tietokoneilla,
+joissa on XFree86 se on todennäköisesti vasen Alt-näppäin (\fBAlt_L\fP).
+Suneilla se on luultavasti sidottu timanttinäppäimiin. Käytä komentoa
+\fIxmodmap(1x)\fP selvittääksesi asian tarkemmin.
+
+Otsikkojen perässä hakasuluissa oleva merkkijono ilmoittaa moduulin,
+jonka asetustiedosto määrittelee alla olevat sidonnat.
+
+.SS Yleisesti saatavilla olevat sidonnat
+
+BINDINGS:WScreen
+
+.\".SS Kehyksiä ja näyttöjä, sekä niiden lapsia käsittelevät sidonnat
+
+BINDINGS:WMPlex
+
+.SS Kehyksiä, sekä niiden lapsia käsittelevät sidonnat
+
+BINDINGS:WFrame
+
+.SS Siirto ja koonmuutostilan sidonnat
+
+BINDINGS:WMoveresMode
+
+.SS Kelluvien työpöytien ja kehyksien sidonnat [mod_floatws]
+
+BINDINGS:WGroupWS
+
+BINDINGS:WFloatFrame
+
+.SS Valikoiden sidonnat [mod_menu]
+
+BINDINGS:WMenu
+
+
+.SH TIEDOSTOT JA HAKEMISTOT
+.TP
+.B ETCDIR/cfg_pwm.lua
+Järjestelmän oletusarvoinen pääasetustiedosto.
+.TP
+.B ETCDIR/cfg_*.lua
+Muita asetustiedostoja.
+.TP
+.B ETCDIR/look_*.lua
+Ulkonäön asetustiedostoja.
+.TP
+.B ~/.ion3/
+Käyttäjän asetustiedostot.
+.TP
+.B ~/.ion3/cfg_pwm.lua
+Käyttäjän oletusarvoinen pääasetustiedosto.
+
+.SH KATSO MYÖS
+
+Katso \fIion3(1)\fP.
+
+.SH TEKIJÄ
+PWM:n sekä Ionin on kirjoittanut Tuomo Valkonen <tuomov at iki.fi>.
diff --git a/man/pwm3.in b/man/pwm3.in
new file mode 100644 (file)
index 0000000..50df639
--- /dev/null
@@ -0,0 +1,108 @@
+.TH PWM 1
+.SH NAME
+PWM - An X11 window manager
+.SH SYNOPSIS
+.B pwm3
+.I "[options]"
+.SH "DESCRIPTION"
+
+The original PWM was the first tabbing window manager. This version of
+PWM is based on the code of Ion and is actually exactly the same window
+manager with only differences in default configuration files, configuration
+file lookup paths and some options.
+
+.SH "OPTIONS"
+.TP
+.B \-display host:display.screen
+X display to manage
+.TP
+.B \-conffile configfile
+Configuration file to use instead of default
+.TP
+.B \-searchdir dir
+Directory to search for configuration files and other scripts
+.TP
+.B \-oneroot
+On X servers with multiple (traditional non-Xinerama) screens, manage only
+default screen (root window), not all, as given by the -display option or
+in the DISPLAY environment variable.
+.TP
+.B \-sessionname session_name
+Set session name. This option affects where workspace and other save files are
+put (~/.pwm3/session_name if option set).
+.TP
+.B \-xinerama 0|1
+If Ioncore was compiled with Xinerama support, this option can be used to
+enable/disable (1/0) the use of it. The default is _not_ to use Xinerama
+screen information.
+.TP
+.B \-help
+Show help on command line options
+.TP
+.B \-version
+Show version
+.TP
+.B \-about
+Show about text (version, copyright)
+
+.SH BINDINGS
+
+These are the default key and pointer bindings. (\fBMod1\fP) depends on your
+system. On PC:s with XFree86 it is probably bound to the left Alt key
+(\fBAlt_L\fP). On Suns it is usually bound to the diamond keys
+(\fBMeta_L\fP, \fBMeta_R\fP). Use \fIxmodmap(1x)\fP to find out.
+
+The string in square brackets after a binding group heading below indicates
+the module that whose configuration file defines these bindings.
+
+.SS Globally available bindings
+
+BINDINGS:WScreen
+
+.".SS Bindings operating on both frames and screens and their children
+
+BINDINGS:WMPlex
+
+.SS Bindings operating on frames and their children
+
+BINDINGS:WFrame
+
+.SS Move/resize mode bindings
+
+BINDINGS:WMoveresMode
+
+.SS Bindings for floating workspaces and frames [mod_floatws]
+
+BINDINGS:WGroupWS
+
+BINDINGS:WFloatFrame
+
+.SS Bindings for menus [mod_menu]
+
+BINDINGS:WMenu
+
+
+.SH FILES AND DIRECTORIES
+.TP
+.B ETCDIR/cfg_pwm.lua
+System default main configuration file
+.TP
+.B ETCDIR/cfg_*.lua
+Other configuration files
+.TP
+.B ETCDIR/look_*.lua
+Colour scheme configuration files
+.TP
+.B ~/.pwm3/
+User configuration files
+.TP
+.B ~/.pwm3/cfg_pwm.lua
+User default main configuration file (overrides system default)
+
+.SH SEE ALSO
+
+For more information, see
+\fIion3(1)\fP.
+
+.SH AUTHOR
+Both PWM and Ion have been written by Tuomo Valkonen <tuomov at iki.fi>.
diff --git a/man/welcome.cs.head b/man/welcome.cs.head
new file mode 100644 (file)
index 0000000..3ad07d9
--- /dev/null
@@ -0,0 +1,26 @@
+
+Vítejte v Ionu!
+
+Pokud jste je¹tì nikdy Ion nepou¾ívali, silnì vám doporuèujeme
+prostudovat si pøed pokraèováním jeho manuálovou stránku. Kopii
+manuálové stránky naleznete ní¾e, ale mù¾ete se k ní kdykoliv vrátit
+napøíklad stiskem klávesy <F1> následované klávesou <Enter>, nebo
+v terminálu spu¹tìním pøíkazu 'man ion3'. (Pøedpokládáme, ¾e jste
+Ion nainstalovali standardním zpùsobem a ¾e systémové pøíkazy ví, kde
+naleznou manuálovou stránku.)
+
+Jestli¾e jste pøíli¹ nedoèkaví a pøeze v¹echna doporuèení si
+manuálovou stránku nepøeètete, mìli byste alespoò vìdìt, ¾e klávesou
+<F2> by se mìl spustit emulátor terminálu (xterm) a ¾e do hlavního
+menu se dostanete klávesou <F12>.
+
+A¾ se budete cítit pøipraveni na psaní vlastních konfiguraèních
+souborù (jednou k tomu stejnì dojde), prostudujte si konfiguraèní
+manuál dostupný na webových stránkách Ionu. Pøedtím ale doporuèujeme
+Ion nìjakou dobu pou¾ívat, abyste pochytili základní koncepty nutné
+pro porozumìní konfiguraèních souborù.
+
+
+---- Následuje manuálová stránka ----
+
+
diff --git a/man/welcome.de.head b/man/welcome.de.head
new file mode 100644 (file)
index 0000000..519e364
--- /dev/null
@@ -0,0 +1,22 @@
+Willkommen bei Ion!
+
+Wenn Sie nie zuvor Ion benutzt haben, empfehlen wir Ihnen unbedingt das 
+Ion Handbuch zu studieren, bevor Sie weiter fortfahren. Obwohl es 
+unten aufgeführt ist, können Sie später wieder darauf zurückgreifen, indem 
+Sie <F1> drücken und dann <Enter>, oder, indem Sie 'man ion3' in einem 
+Terminal ausführen. All das setzt voraus, dass Sie Ion in einem 
+Standardspeicherplatz installiert haben, damit die System Kommandos die 
+Handbuchseite finden können. 
+
+Wenn Sie zu wild darauf sind, Ion auszuprobieren und gegen alle 
+Empfehlungen das Lesen des Handbuches überspringen wollen, so sollten Sie
+zumindestens wissen, dass mit drücken der <F2> - Taste ein Terminal  
+gestartet werden kann, und dass man auf das Hauptmenü mit <F12> zugreifen kann. 
+
+Wenn Sie sich bereit fühlen, individuelle Konfigurationsdateien zu 
+schreiben (was jederzeit möglich ist), so schauen sich bitte in das 
+Konfigurationshandbuch, welches auf der Ion Webseite zu finden ist. 
+(Link siehe Ende des Benutzerhandbuches) Ausserdem kann es nuetzlich sein, 
+sich erst ein wenig mit Ion vertraut zu machen bevor man sich eingehend 
+damit befasst. Damit Sie die Grundkonzepte erfassen koennen, die notwendig 
+sind, um die nötigen Konfigurationsdateien zu verstehen.
diff --git a/man/welcome.fi.head b/man/welcome.fi.head
new file mode 100644 (file)
index 0000000..e9a3693
--- /dev/null
@@ -0,0 +1,25 @@
+
+Tervetuloa käyttämään Ionia!
+
+Jos et ole käyttänyt Ionia aiemmin, on hyvin suotiteltavaa, että opiskelet
+Ionin ohjesivun ennen kuin jatkat tätä pidemmälle. Se on toistettu alla,
+mutta voit aukaista sen myös myöhemmin painamalla <F1> ja <Enter>, tai
+suorittamalla 'man ion3' komentoriviltä. (Tämä toimii sillä oletuksella,
+että olet asentanut Ionin järjestelmässäsi sellaiseen vakiosijaintin, 
+mistä sen työkalut osaavat hakea ohjesivuja.)
+
+Jos olet innokas kokeilemaan Ionia nyt heti ja vastoin suosituksia haluat
+jättää ohjesivun lukematta tällä istumalla, sinun tulisi kuitenkin tietää
+ainakin seuraavat näppäimet: <F2> käynnistää pääte-emulaattorin (xterm)
+ja <F12> näyttää päävalikon.
+
+Jossain vaiheessa, opeteltuasi Ionin peruskäsitteet ja toiminnot, haluat
+varmaan muokata sen asetukset itsellesi sopivammiksi. Tähän löydät apua
+(englanniksi) Ionin asetus- ja ohjelmointimanuaalista. Se löytyy ainakin
+Ionin seittisivulta, johon on linkki alla olevan ohjesivun lopussa.
+Huomautettakoon vielä, että Ionin käytön ja peruskäsitteiden opettelu
+on hyvin suositeltvaa ennen asetustiedostojen pariin hyppäämistä.
+
+---- Ohjesivu seuraa ----
+
+
diff --git a/man/welcome.head b/man/welcome.head
new file mode 100644 (file)
index 0000000..6a95c1f
--- /dev/null
@@ -0,0 +1,26 @@
+
+Welcome to Ion!
+
+If you have never used Ion before, it is highly recommended that you study 
+the Ion manual page before proceeding further. It is reproduced below, but 
+you can also access it later on, for example, by pressing <F1> and then 
+<Enter> or, by running 'man ion3' in a terminal. All this supposing that 
+you have installed Ion in a standard location so that the system commands
+can find the manual page.
+
+If you are too eager to try out Ion and against all recommendation want to
+skip reading the manual page, you should at least know that pressing <F2> 
+should start a terminal emulator (xterm) and that the main menu can be 
+accessed with <F12>.
+
+When you feel ready to write customized configuration files (you're going
+to do that in any case), please see the configuration manual available from
+the Ion webpage listed at the end of the user manual below. It may, however,
+be beneficial to become well acquainted with Ion before delving into this 
+so that you have grasped the basic concepts necessary to understand the
+binding configuration files.
+
+
+---- Manual page follows ----
+
+
diff --git a/mod_dock/LICENSE b/mod_dock/LICENSE
new file mode 100644 (file)
index 0000000..b1e3f5a
--- /dev/null
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  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 Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           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 Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+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 other code 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.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  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, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser 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 combine 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) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) 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.
+
+    d) 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.
+
+    e) 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 materials to be 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 with
+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 Lesser 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
+           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 Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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/mod_dock/Makefile b/mod_dock/Makefile
new file mode 100644 (file)
index 0000000..2801471
--- /dev/null
@@ -0,0 +1,35 @@
+##
+## Ion dock module Makefile
+##
+## $Header: /home/twp/cvsroot/twp/ion/ion-devel-dock/Makefile,v 1.15 2003/12/21 18:08:43 twp Exp $
+##
+
+# System specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES=dock.c
+DOCS=LICENSE README.dock
+
+MAKE_EXPORTS=mod_dock
+
+MODULE=mod_dock
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: module_install
+
+######################################
+
+.PHONY: tags
+tags:
+       exuberant-ctags -R . $(TOPDIR)
diff --git a/mod_dock/README.dock b/mod_dock/README.dock
new file mode 100644 (file)
index 0000000..c2d81ba
--- /dev/null
@@ -0,0 +1,98 @@
+Ion dock module
+Copyright (c) Tom Payne 2003
+Copyright (c) Per Olofsson 2003
+
+by Tom Payne <ion@tompayne.org>
+based on code by Per Olofsson <pelle@dsv.su.se>
+
+INTRODUCTION
+
+       This module provides a dock for the Ion window manager.
+
+       Features:
+       - supports most dockapps
+       - configurable layout
+       - configurable dockapp order
+       - supports shaped dockapps
+       - uses ion's drawing engine
+
+DEPENDENCIES
+
+       ion3
+
+USAGE
+
+       1. Copy the configuration files cfg_dock.lua and dock-draw.lua to
+          your ~/.ion3 directory. Edit cfg_dock.lua and dock-draw.lua to
+          suit your preferences.
+
+       2. Add load_module("mod_dock") to ~/.ion3/cfg_ion.lua.
+
+       3. Add include("dock-draw.lua") near the end of ~/.ion3/draw.lua,
+          before gr_refresh().
+
+       4. (Re)start ion3.
+
+CONFIGURATION
+
+       See the comments in cfg_dock.lua and dock-draw.lua.
+
+KNOWN BUGS
+
+       Not all dockapps dock. This is because dockapps use a variety of methods
+       to signal that they are dockapps and the dock doesn't yet
+       recognise all of them. If your favourite dockapp does not work then
+       please inform the author (email address at top of file).
+
+       The following dockapps are known to have problems:
+
+       - gkrellm2 can have the wrong shape if it is started after the dock
+         module is loaded.
+
+       - wmxmms needs a winprop{...}. See the example cfg_dock.lua. Ion
+         complains: Client window "wmxmms" has broken transient_for hint.
+         ("Extended WM hints" multi-parent brain damage?).
+
+IMPLEMENTATION NOTES
+
+       If you would like to help improve the dock then the following
+       notes might be useful.
+
+       You should use folding in your editor when you edit/view the source
+       code. The code follows ion's coding standards, but there are many more
+       folds. VIM and Emacs both support folding.
+
+       Dockapps use a variety of methods to signal to the window manager that
+       they are a dockapp. Ion automatically detects some (see
+       manage_clientwin() in ioncore/clientwin.c), and the dock detects
+       some others (see dock_clientwin_is_dockapp() in dock.c).  Adding more
+       dockapp types will require editing these functions and possibly writing
+       type-specific management code.
+
+       The dock should really implement region_save_to_file()/region_load() to
+       maintain state over a restart and session management. However, over
+       restart a new dock will also be created by cfg_dock.lua, meaning that there
+       will be multiple docks on the same screen. The solution is probably to
+       automatically create docks as they are required, with cfg_dock.lua
+       specifying the default configuration of new docks but not actually
+       creating any docks.
+
+       As far as I can tell the gkrellm2 problem is a race condition and the
+       sequence of events is as follows:
+       1. gkrellm2 starts and gets added to the dock with is initial geom.
+       2. gkrellm2 grows as it loads plugins.
+       3. The dock allocates more space to gkrellm2 and requests
+          gkrellm2's new shape, but gkrellm2 reports its old shape.
+       4. Further requests to gkrellm2 return is new (actual) shape, but by
+           now it's too late...
+       Any assistance in fixing this bug would be much appreciated.
+
+AUTHORS
+
+       Tom Payne <ion@tompayne.org>
+       Per Olofsson <pelle@dsv.su.se>
+       
+LICENSE
+
+       This program is licensed under the terms of the Lesser General Public
+       License (LGPL) version 2.1. See the file COPYING for details.
diff --git a/mod_dock/dock.c b/mod_dock/dock.c
new file mode 100644 (file)
index 0000000..3b18513
--- /dev/null
@@ -0,0 +1,1701 @@
+/*
+ * Ion dock module
+ * Copyright (C) 2003 Tom Payne
+ * Copyright (C) 2003 Per Olofsson
+ * Copyright (C) 2004-2006 Tuomo Valkonen
+ *
+ * by Tom Payne <ion@tompayne.org>
+ * based on code by Per Olofsson <pelle@dsv.su.se>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * $Header: /home/twp/cvsroot/twp/ion/ion-devel-dock/dock.c,v 1.17 2003/12/21 11:59:48 twp Exp $
+ *
+ */
+
+
+/*{{{ Includes */
+
+#include <limits.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/Xext.h>
+
+#include <libtu/objp.h>
+#include <libtu/map.h>
+#include <libtu/minmax.h>
+#include <libextl/extl.h>
+#include <libextl/readconfig.h>
+#include <libmainloop/defer.h>
+
+#include <ioncore/common.h>
+#include <ioncore/clientwin.h>
+#include <ioncore/eventh.h>
+#include <ioncore/global.h>
+#include <ioncore/manage.h>
+#include <ioncore/names.h>
+#include <ioncore/property.h>
+#include <ioncore/resize.h>
+#include <ioncore/window.h>
+#include <ioncore/mplex.h>
+#include <ioncore/saveload.h>
+#include <ioncore/bindmaps.h>
+#include <ioncore/regbind.h>
+#include <ioncore/extlconv.h>
+#include <ioncore/event.h>
+#include <ioncore/resize.h>
+#include <ioncore/sizehint.h>
+#include <ioncore/basicpholder.h>
+
+#include "exports.h"
+
+/*}}}*/
+
+
+/*{{{ Macros */
+
+#ifdef __GNUC__
+#define UNUSED __attribute__ ((unused))
+#else
+#define UNUSED
+#endif
+
+/*}}}*/
+
+
+/*{{{ Variables */
+
+#include "../version.h"
+
+static const char *modname="dock";
+const char mod_dock_ion_api_version[]=ION_API_VERSION;
+
+static bool shape_extension=FALSE;
+static int shape_event_basep=0;
+static int shape_error_basep=0;
+
+static WBindmap *dock_bindmap=NULL;
+
+/*}}}*/
+
+
+/*{{{ Classes */
+
+INTRSTRUCT(WDockParam);
+INTRSTRUCT(WDockApp);
+INTRCLASS(WDock);
+
+DECLSTRUCT(WDockParam){
+    const char *key;
+    const char *desc;
+    const StringIntMap *map;
+    int dflt;
+};
+
+DECLSTRUCT(WDockApp){
+    WDockApp *next, *prev;
+    WRegion *reg;
+    int pos;
+    bool draw_border;
+    bool tile;
+    WRectangle geom;
+    WRectangle tile_geom;
+    WRectangle border_geom;
+};
+
+DECLCLASS(WDock){
+    WWindow win;
+    WDock *dock_next, *dock_prev;
+    int pos, grow;
+    bool is_auto;
+    GrBrush *brush;
+    WDockApp *dockapps;
+    
+    int min_w, min_h;
+    int max_w, max_h;
+    
+    bool arrange_called;
+    bool save;
+};
+
+static WDock *docks=NULL;
+
+/*}}}*/
+
+
+/*{{{ Parameter conversion */
+
+static void dock_param_extl_table_get(const WDockParam *param, 
+                                      ExtlTab conftab, int value)
+{
+    const char *s;
+
+    s=stringintmap_key(param->map, value, NULL);
+    if(s){
+        extl_table_sets_s(conftab, param->key, s);
+    }
+
+}
+
+
+static bool dock_param_do_set(const WDockParam *param, char *s,
+                              int *ret)
+{
+    bool changed=FALSE;
+    int i=stringintmap_value(param->map, s, -1);
+    if(i<0){
+        warn_obj(modname, "Invalid %s \"%s\"", param->desc, s);
+    }else{
+        if(*ret!=i){
+            changed=TRUE;
+        }
+        *ret=i;
+    }
+    free(s);
+
+    return changed;
+
+}
+
+
+static bool dock_param_extl_table_set(const WDockParam *param, ExtlTab conftab,
+                                      int *ret)
+{
+    char *s;
+
+    if(extl_table_gets_s(conftab, param->key, &s))
+        return dock_param_do_set(param, s, ret);
+
+    return FALSE;
+
+}
+
+
+static bool dock_param_brush_set(const WDockParam *param, GrBrush *brush,
+                                 int *ret)
+{
+    char *s;
+
+    if(grbrush_get_extra(brush, param->key, 's', &s))
+        return dock_param_do_set(param, s, ret);
+
+    return FALSE;
+
+}
+
+/*}}}*/
+
+
+/*{{{ Parameter descriptions */
+
+static const WDockParam dock_param_name={
+    "name",
+    "name",
+    NULL,
+    0
+};
+
+
+#define DOCK_HPOS_MASK   0x000f
+#define DOCK_HPOS_LEFT   0x0000
+#define DOCK_HPOS_CENTER 0x0001
+#define DOCK_HPOS_RIGHT  0x0002
+#define DOCK_VPOS_MASK   0x00f0
+#define DOCK_VPOS_TOP    0x0000
+#define DOCK_VPOS_MIDDLE 0x0010
+#define DOCK_VPOS_BOTTOM 0x0020
+
+
+static StringIntMap dock_pos_map[]={
+    {"tl", DOCK_VPOS_TOP|DOCK_HPOS_LEFT},
+    {"tc", DOCK_VPOS_TOP|DOCK_HPOS_CENTER},
+    {"tr", DOCK_VPOS_TOP|DOCK_HPOS_RIGHT},
+    {"ml", DOCK_VPOS_MIDDLE|DOCK_HPOS_LEFT},
+    {"mc", DOCK_VPOS_MIDDLE|DOCK_HPOS_CENTER},
+    {"mr", DOCK_VPOS_MIDDLE|DOCK_HPOS_RIGHT},
+    {"bl", DOCK_VPOS_BOTTOM|DOCK_HPOS_LEFT},
+    {"bc", DOCK_VPOS_BOTTOM|DOCK_HPOS_CENTER},
+    {"br", DOCK_VPOS_BOTTOM|DOCK_HPOS_RIGHT},
+    END_STRINGINTMAP
+};
+
+static WDockParam dock_param_pos={
+    "pos",
+    "dock position",
+    dock_pos_map,
+    DOCK_HPOS_LEFT|DOCK_VPOS_BOTTOM
+};
+
+
+enum WDockGrow{
+    DOCK_GROW_UP,
+    DOCK_GROW_DOWN,
+    DOCK_GROW_LEFT,
+    DOCK_GROW_RIGHT
+};
+
+static StringIntMap dock_grow_map[]={
+    {"up", DOCK_GROW_UP},
+    {"down", DOCK_GROW_DOWN},
+    {"left", DOCK_GROW_LEFT},
+    {"right", DOCK_GROW_RIGHT},
+    END_STRINGINTMAP
+};
+
+WDockParam dock_param_grow={
+    "grow",
+    "growth direction",
+    dock_grow_map,
+    DOCK_GROW_RIGHT
+};
+
+
+static const WDockParam dock_param_is_auto={
+    "is_auto",
+    "is automatic",
+    NULL,
+    TRUE
+};
+
+
+enum WDockOutlineStyle{
+    DOCK_OUTLINE_STYLE_NONE,
+    DOCK_OUTLINE_STYLE_ALL,
+    DOCK_OUTLINE_STYLE_EACH
+};
+
+static StringIntMap dock_outline_style_map[]={
+    {"none", DOCK_OUTLINE_STYLE_NONE},
+    {"all", DOCK_OUTLINE_STYLE_ALL},
+    {"each", DOCK_OUTLINE_STYLE_EACH},
+    END_STRINGINTMAP
+};
+
+WDockParam dock_param_outline_style={
+    "outline_style",
+    "outline style",
+    dock_outline_style_map,
+    DOCK_OUTLINE_STYLE_ALL
+};
+
+
+static const WDockParam dock_param_tile_width={
+    "width",
+    "tile width",
+    NULL,
+    64
+};
+
+static const WDockParam dock_param_tile_height={
+    "height",
+    "tile height",
+    NULL,
+    64
+};
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+#define CLIENTWIN_WINPROP_POSITION "dockposition"
+#define CLIENTWIN_WINPROP_BORDER "dockborder"
+
+static WDockApp *dock_find_dockapp(WDock *dock, WRegion *reg)
+{
+    WDockApp *dockapp;
+
+    for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
+        if(dockapp->reg==reg){
+            return dockapp;
+        }
+    }
+
+    return NULL;
+
+}
+
+
+static void dock_get_outline_style(WDock *dock, int *ret)
+{
+
+    *ret=dock_param_outline_style.dflt;
+    if(dock->brush!=NULL)
+        dock_param_brush_set(&dock_param_outline_style, dock->brush, ret);
+
+}
+
+/*}}}*/
+
+
+/*{{{ Size calculation */
+
+
+static void dock_get_tile_size(WDock *dock, WRectangle *ret)
+{
+    ExtlTab tile_size_table;
+
+    ret->x=0;
+    ret->y=0;
+    ret->w=dock_param_tile_width.dflt;
+    ret->h=dock_param_tile_height.dflt;
+    if(dock->brush==NULL)
+        return;
+    if(grbrush_get_extra(dock->brush, "tile_size", 't', &tile_size_table)){
+        extl_table_gets_i(tile_size_table, dock_param_tile_width.key, &ret->w);
+        extl_table_gets_i(tile_size_table, dock_param_tile_height.key, &ret->h);
+        extl_unref_table(tile_size_table);
+    }
+
+}
+
+
+static void dock_get_pos_grow(WDock *dock, int *pos, int *grow)
+{
+    WMPlex *mplex=OBJ_CAST(REGION_PARENT(dock), WMPlex);
+    WRegion *mplex_stdisp;
+    WMPlexSTDispInfo din;
+    
+    if(mplex!=NULL){
+        mplex_get_stdisp(mplex, &mplex_stdisp, &din);
+        if(mplex_stdisp==(WRegion*)dock){
+            /* Ok, we're assigned as a status display for mplex, so
+             * get parameters from there.
+             */
+            *pos=((din.pos==MPLEX_STDISP_TL || din.pos==MPLEX_STDISP_BL)
+                  ? DOCK_HPOS_LEFT
+                  : DOCK_HPOS_RIGHT) 
+                | ((din.pos==MPLEX_STDISP_TL || din.pos==MPLEX_STDISP_TR)
+                   ? DOCK_VPOS_TOP
+                   : DOCK_VPOS_BOTTOM);
+            *grow=dock->grow;
+            return;
+        }
+    }
+    
+    *grow=dock->grow;
+    *pos=dock->pos;
+}
+
+
+
+static void dock_reshape(WDock *dock)
+{
+    int outline_style;
+    
+    if(!shape_extension){
+        return;
+    }
+    
+    dock_get_outline_style(dock, &outline_style);
+    
+    switch(outline_style){
+    case DOCK_OUTLINE_STYLE_NONE:
+    case DOCK_OUTLINE_STYLE_EACH:
+        {
+            WDockApp *dockapp;
+            
+            /* Start with an empty set */
+            XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
+                                    ShapeBounding, 0, 0, NULL, 0, ShapeSet, 0);
+            
+            /* Union with dockapp shapes */
+            for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
+                WClientWin *cwin=OBJ_CAST(dockapp->reg, WClientWin);
+                if(outline_style==DOCK_OUTLINE_STYLE_EACH
+                   && dockapp->draw_border){
+                    /* Union with border shape */
+                    XRectangle tile_rect;
+                    
+                    tile_rect.x=dockapp->border_geom.x;
+                    tile_rect.y=dockapp->border_geom.y;
+                    tile_rect.width=dockapp->border_geom.w;
+                    tile_rect.height=dockapp->border_geom.h;
+                    XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
+                                            ShapeBounding, 0, 0, &tile_rect, 1,
+                                            ShapeUnion, 0);
+                }else if(cwin!=NULL){
+                    /* Union with dockapp shape */
+                    int count;
+                    int ordering;
+                    
+                    XRectangle *rects=XShapeGetRectangles(ioncore_g.dpy, cwin->win,
+                                                          ShapeBounding, &count,
+                                                          &ordering);
+                    if(rects!=NULL){
+                        WRectangle dockapp_geom=REGION_GEOM(cwin);
+                        XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
+                                                ShapeBounding,
+                                                dockapp_geom.x, dockapp_geom.y,
+                                                rects, count, ShapeUnion, ordering);
+                        XFree(rects);
+                    }
+                }
+            }
+        }
+        break;
+        
+    case DOCK_OUTLINE_STYLE_ALL:
+        {
+            WRectangle geom;
+            XRectangle rect;
+            
+            geom=REGION_GEOM(dock);
+            rect.x=0;
+            rect.y=0;
+            rect.width=geom.w;
+            rect.height=geom.h;
+            XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
+                                    ShapeBounding, 0, 0, &rect, 1, ShapeSet, 0);
+        }
+        break;
+    }
+    
+}
+
+
+static void dock_arrange_dockapps(WDock *dock, const WRectangle *bd_dockg, 
+                                  const WDockApp *replace_this, 
+                                  WDockApp *with_this)
+{
+    GrBorderWidths dock_bdw, dockapp_bdw;
+    WDockApp dummy_copy, *dockapp;
+    int pos, grow, cur_coord=0;
+    WRectangle dock_geom;
+
+    dock->arrange_called=TRUE;
+    
+    dock_get_pos_grow(dock, &pos, &grow);
+
+    /* Determine dock and dockapp border widths */
+    memset(&dock_bdw, 0, sizeof(GrBorderWidths));
+    memset(&dockapp_bdw, 0, sizeof(GrBorderWidths));
+    
+    if(dock->brush){
+        int outline_style;
+
+        dock_get_outline_style(dock, &outline_style);
+        switch(outline_style){
+        case DOCK_OUTLINE_STYLE_NONE:
+            break;
+        case DOCK_OUTLINE_STYLE_ALL:
+            grbrush_get_border_widths(dock->brush, &dock_bdw);
+            dockapp_bdw.spacing=dock_bdw.spacing;
+            break;
+        case DOCK_OUTLINE_STYLE_EACH:
+            grbrush_get_border_widths(dock->brush, &dockapp_bdw);
+            break;
+        }
+    }
+    
+    dock_geom.w=bd_dockg->w-dock_bdw.left-dock_bdw.right;
+    dock_geom.h=bd_dockg->h-dock_bdw.top-dock_bdw.bottom;
+
+    /* Calculate initial co-ordinate for layout algorithm */
+    switch(grow){
+    case DOCK_GROW_UP:
+        cur_coord=dock_bdw.top+dock_geom.h;
+        break;
+    case DOCK_GROW_DOWN:
+        cur_coord=dock_bdw.top;
+        break;
+    case DOCK_GROW_LEFT:
+        cur_coord=dock_bdw.left+dock_geom.w;
+        break;
+    case DOCK_GROW_RIGHT:
+        cur_coord=dock_bdw.left;
+        break;
+    }
+
+    /* Arrange dockapps */
+    for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
+        WDockApp *da=dockapp;
+        
+        if(replace_this!=NULL){
+            if(replace_this==dockapp){
+                da=with_this;
+            }else{
+                dummy_copy=*dockapp;
+                da=&dummy_copy;
+            }
+        }
+            
+        /* Calculate first co-ordinate */
+        switch(grow){
+        case DOCK_GROW_UP:
+        case DOCK_GROW_DOWN:
+            switch(pos&DOCK_HPOS_MASK){
+            case DOCK_HPOS_LEFT:
+                da->border_geom.x=0;
+                break;
+            case DOCK_HPOS_CENTER:
+                da->border_geom.x=(dock_geom.w-da->border_geom.w)/2;
+                break;
+            case DOCK_HPOS_RIGHT:
+                da->border_geom.x=dock_geom.w-da->border_geom.w;
+                break;
+            }
+            da->border_geom.x+=dock_bdw.left;
+            break;
+        case DOCK_GROW_LEFT:
+        case DOCK_GROW_RIGHT:
+            switch(pos&DOCK_VPOS_MASK){
+            case DOCK_VPOS_TOP:
+                da->border_geom.y=0;
+                break;
+            case DOCK_VPOS_MIDDLE:
+                da->border_geom.y=(dock_geom.h-da->border_geom.h)/2;
+                break;
+            case DOCK_VPOS_BOTTOM:
+                da->border_geom.y=dock_geom.h-da->border_geom.h;
+                break;
+            }
+            da->border_geom.y+=dock_bdw.top;
+            break;
+        }
+
+        /* Calculate second co-ordinate */
+        switch(grow){
+        case DOCK_GROW_UP:
+            cur_coord-=da->border_geom.h;
+            da->border_geom.y=cur_coord;
+            cur_coord-=dockapp_bdw.spacing;
+            break;
+        case DOCK_GROW_DOWN:
+            da->border_geom.y=cur_coord;
+            cur_coord+=da->border_geom.h+dockapp_bdw.spacing;
+            break;
+        case DOCK_GROW_LEFT:
+            cur_coord-=da->border_geom.w;
+            da->border_geom.x=cur_coord;
+            cur_coord-=dockapp_bdw.spacing;
+            break;
+        case DOCK_GROW_RIGHT:
+            da->border_geom.x=cur_coord;
+            cur_coord+=da->border_geom.w+dockapp_bdw.spacing;
+            break;
+        }
+
+        /* Calculate tile geom */
+        da->tile_geom.x=da->border_geom.x+dockapp_bdw.left;
+        da->tile_geom.y=da->border_geom.y+dockapp_bdw.top;
+
+        /* Calculate dockapp geom */
+        if(da->tile){
+            da->geom.x=da->tile_geom.x+(da->tile_geom.w-da->geom.w)/2;
+            da->geom.y=da->tile_geom.y+(da->tile_geom.h-da->geom.h)/2;
+        }else{
+            da->geom.x=da->tile_geom.x;
+            da->geom.y=da->tile_geom.y;
+        }
+        
+        if(replace_this==NULL)
+            region_fit(da->reg, &(da->geom), REGION_FIT_BOUNDS);
+    }
+}
+
+
+static void calc_dock_pos(WRectangle *dg, const WRectangle *pg, int pos)
+{
+    switch(pos&DOCK_HPOS_MASK){
+    case DOCK_HPOS_LEFT:
+        dg->x=pg->x;
+        break;
+    case DOCK_HPOS_CENTER:
+        dg->x=pg->x+(pg->w-dg->w)/2;
+        break;
+    case DOCK_HPOS_RIGHT:
+        dg->x=pg->x+(pg->w-dg->w);
+        break;
+    }
+    
+    switch(pos&DOCK_VPOS_MASK){
+    case DOCK_VPOS_TOP:
+        dg->y=pg->y;
+        break;
+    case DOCK_VPOS_MIDDLE:
+        dg->y=pg->y+(pg->h-dg->h)/2;
+        break;
+    case DOCK_VPOS_BOTTOM:
+        dg->y=pg->y+(pg->h-dg->h);
+        break;
+    }
+}
+
+    
+static void dock_set_minmax(WDock *dock, int grow, const WRectangle *g)
+{
+    dock->min_w=g->w;
+    dock->min_h=g->h;
+    if(grow==DOCK_GROW_UP || grow==DOCK_GROW_DOWN){
+        dock->max_w=g->w;
+        dock->max_h=INT_MAX;
+    }else{
+        dock->max_w=INT_MAX;
+        dock->max_h=g->h;
+    }
+}
+
+
+static void dockapp_calc_preferred_size(WDock *dock, int grow, 
+                                        const WRectangle *tile_size,
+                                        WDockApp *da)
+{
+    int w=da->geom.w, h=da->geom.h;
+    
+    if(grow==DOCK_GROW_UP || grow==DOCK_GROW_DOWN){
+        da->geom.w=minof(w, tile_size->w);
+        da->geom.h=h;
+    }else{
+        da->geom.w=w;
+        da->geom.h=minof(h, tile_size->h);
+    }
+    
+    region_size_hints_correct(da->reg, &(da->geom.w), &(da->geom.h), TRUE);
+}
+
+
+
+static void dock_managed_rqgeom_(WDock *dock, WRegion *reg, int flags,
+                                 const WRectangle *geom, WRectangle *geomret,
+                                 bool just_update_minmax)
+{
+    WDockApp *dockapp=NULL, *thisdockapp=NULL, thisdockapp_copy;
+    WRectangle parent_geom, dock_geom, border_dock_geom;
+    GrBorderWidths dock_bdw, dockapp_bdw;
+    int n_dockapps=0, max_w=1, max_h=1, total_w=0, total_h=0;
+    int pos, grow;
+    WRectangle tile_size;
+    WWindow *par=REGION_PARENT(dock);
+    
+    /* dock_resize calls with NULL parameters. */
+    assert(reg!=NULL || (geomret==NULL && !(flags&REGION_RQGEOM_TRYONLY)));
+    
+    dock_get_pos_grow(dock, &pos, &grow);
+
+    /* Determine parent and tile geoms */
+    parent_geom.x=0;
+    parent_geom.y=0;
+    if(par!=NULL){
+        parent_geom.w=REGION_GEOM(par).w;
+        parent_geom.h=REGION_GEOM(par).h;
+    }else{
+        /* Should not happen in normal operation. */
+        parent_geom.w=1;
+        parent_geom.h=1;
+    }
+    
+    dock_get_tile_size(dock, &tile_size);
+
+    /* Determine dock and dockapp border widths */
+    memset(&dock_bdw, 0, sizeof(GrBorderWidths));
+    memset(&dockapp_bdw, 0, sizeof(GrBorderWidths));
+    
+    if(dock->brush){
+        int outline_style;
+
+        dock_get_outline_style(dock, &outline_style);
+        switch(outline_style){
+        case DOCK_OUTLINE_STYLE_NONE:
+            break;
+        case DOCK_OUTLINE_STYLE_ALL:
+            grbrush_get_border_widths(dock->brush, &dock_bdw);
+            dockapp_bdw.spacing=dock_bdw.spacing;
+            break;
+        case DOCK_OUTLINE_STYLE_EACH:
+            grbrush_get_border_widths(dock->brush, &dockapp_bdw);
+            break;
+        }
+    }
+
+    /* Calculate widths and heights */
+    for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
+        WDockApp *da=dockapp;
+        bool update=!(flags&REGION_RQGEOM_TRYONLY);
+        if(dockapp->reg==reg){
+            thisdockapp=dockapp;
+            if(flags&REGION_RQGEOM_TRYONLY){
+                thisdockapp_copy=*dockapp;
+                thisdockapp_copy.geom=*geom;
+                da=&thisdockapp_copy;
+                update=TRUE;
+            }
+            da->geom=*geom;
+        }
+        
+        if(update){
+            /* Calculcate preferred size */
+            dockapp_calc_preferred_size(dock, grow, &tile_size, da);
+                        
+            /* Determine whether dockapp should be placed on a tile */
+            da->tile=da->geom.w<=tile_size.w && da->geom.h<=tile_size.h;
+            
+            /* Calculate width and height */
+            if(da->tile){
+                da->tile_geom.w=tile_size.w;
+                da->tile_geom.h=tile_size.h;
+            }else{
+                da->tile_geom.w=da->geom.w;
+                da->tile_geom.h=da->geom.h;
+            }
+
+            /* Calculate border width and height */
+            da->border_geom.w=dockapp_bdw.left+da->tile_geom.w+dockapp_bdw.right;
+            da->border_geom.h=dockapp_bdw.top+da->tile_geom.h+dockapp_bdw.right;
+        }
+        
+        /* Calculate maximum and accumulated widths and heights */
+        if(da->border_geom.w>max_w)
+            max_w=da->border_geom.w;
+        total_w+=da->border_geom.w+(n_dockapps ? dockapp_bdw.spacing : 0);
+        
+        if(da->border_geom.h>max_h)
+            max_h=da->border_geom.h;
+        total_h+=da->border_geom.h+(n_dockapps ? dockapp_bdw.spacing : 0);
+        
+        /* Count dockapps */
+        ++n_dockapps;
+    }
+    
+    if(thisdockapp==NULL && reg!=NULL){
+        warn("Requesting dockapp not found.");
+        if(geomret)
+            *geomret=REGION_GEOM(reg);
+        return;
+    }
+    
+    /* Calculate width and height of dock */
+    if(n_dockapps){
+        switch(grow){
+        case DOCK_GROW_LEFT:
+        case DOCK_GROW_RIGHT:
+            dock_geom.w=total_w;
+            dock_geom.h=max_h;
+            break;
+        case DOCK_GROW_UP:
+        case DOCK_GROW_DOWN:
+        default:
+            dock_geom.w=max_w;
+            dock_geom.h=total_h;
+            break;
+        }
+    }else{
+        dock_geom.w=tile_size.w;
+        dock_geom.h=tile_size.h;
+    }
+    border_dock_geom.w=dock_bdw.left+dock_geom.w+dock_bdw.right;
+    border_dock_geom.h=dock_bdw.top+dock_geom.h+dock_bdw.bottom;
+    
+    calc_dock_pos(&border_dock_geom, &parent_geom, pos);
+    
+    /* Fit dock to new geom if required */
+    if(!(flags&REGION_RQGEOM_TRYONLY)){
+        WRQGeomParams rq=RQGEOMPARAMS_INIT;
+        
+        dock_set_minmax(dock, grow, &border_dock_geom);
+        
+        if(just_update_minmax)
+            return;
+        
+        rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
+        rq.geom=border_dock_geom;
+
+        dock->arrange_called=FALSE;
+        
+        region_rqgeom((WRegion*)dock, &rq, NULL);
+        
+        if(!dock->arrange_called)
+            dock_arrange_dockapps(dock, &REGION_GEOM(dock), NULL, NULL);
+        
+        if(thisdockapp!=NULL && geomret!=NULL)
+            *geomret=thisdockapp->geom;
+    }else{
+        if(thisdockapp!=NULL && geomret!=NULL){
+            dock_arrange_dockapps(dock, &REGION_GEOM(dock), 
+                                  thisdockapp, &thisdockapp_copy);
+            *geomret=thisdockapp_copy.geom;
+        }
+    }
+}
+
+static void dock_managed_rqgeom(WDock *dock, WRegion *reg, 
+                                const WRQGeomParams *rq, 
+                                WRectangle *geomret)
+{
+    dock_managed_rqgeom_(dock, reg, rq->flags, &rq->geom, geomret, FALSE);
+}
+
+
+void dock_size_hints(WDock *dock, WSizeHints *hints)
+{
+    hints->min_set=TRUE;
+    hints->min_width=dock->min_w;
+    hints->min_height=dock->min_h;
+    
+    hints->max_set=TRUE;
+    hints->max_width=dock->max_w;
+    hints->max_height=dock->max_h;
+}
+
+
+static bool dock_fitrep(WDock *dock, WWindow *parent, const WFitParams *fp)
+{
+    if(!window_fitrep(&(dock->win), parent, fp))
+        return FALSE;
+
+    dock_arrange_dockapps(dock, &(fp->g), NULL, NULL);
+    
+    if(shape_extension)
+        dock_reshape(dock);
+    
+    return TRUE;
+}
+
+
+static int dock_orientation(WDock *dock)
+{
+    return ((dock->grow==DOCK_GROW_LEFT || dock->grow==DOCK_GROW_RIGHT)
+            ? REGION_ORIENTATION_HORIZONTAL
+            : REGION_ORIENTATION_VERTICAL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Drawing */
+
+
+static void dock_draw(WDock *dock, bool complete)
+{
+    int outline_style;
+    WRectangle g;
+    
+    if(dock->brush==NULL)
+        return;
+    
+    g.x=0;
+    g.y=0;
+    g.w=REGION_GEOM(dock).w;
+    g.h=REGION_GEOM(dock).h;
+    
+    grbrush_begin(dock->brush, &g, (complete ? 0 : GRBRUSH_NO_CLEAR_OK));
+    
+    dock_get_outline_style(dock, &outline_style);
+    switch(outline_style){
+    case DOCK_OUTLINE_STYLE_NONE:
+        break;
+    case DOCK_OUTLINE_STYLE_ALL:
+        {
+            WRectangle geom=REGION_GEOM(dock);
+            geom.x=geom.y=0;
+            grbrush_draw_border(dock->brush, &geom, "dock");
+        }
+        break;
+    case DOCK_OUTLINE_STYLE_EACH:
+        {
+            WDockApp *dockapp;
+            for(dockapp=dock->dockapps; dockapp!=NULL;
+                dockapp=dockapp->next){
+                grbrush_draw_border(dock->brush, &dockapp->tile_geom, 
+                                    "dock");
+            }
+        }
+        break;
+    }
+    
+    grbrush_end(dock->brush);
+}
+
+
+/*EXTL_DOC
+ * Resizes and refreshes \var{dock}.
+ */
+EXTL_EXPORT_MEMBER
+void dock_resize(WDock *dock)
+{
+    dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, FALSE);
+    dock_draw(dock, TRUE);
+    
+}
+
+static void dock_brush_release(WDock *dock)
+{
+    
+    if(dock->brush){
+        grbrush_release(dock->brush);
+        dock->brush=NULL;
+    }
+
+}
+
+
+static void dock_brush_get(WDock *dock)
+{
+
+    dock_brush_release(dock);
+    dock->brush=gr_get_brush(((WWindow*)dock)->win, 
+                             region_rootwin_of((WRegion*)dock), 
+                             "stdisp-dock");
+}
+
+
+static void dock_updategr(WDock *dock)
+{
+    dock_brush_get(dock);
+    dock_resize(dock);
+}
+
+/*}}}*/
+
+
+/*{{{ Set/get */
+
+
+static void mplexpos(int pos, int *mpos)
+{
+    int hp=pos&DOCK_HPOS_MASK, vp=pos&DOCK_VPOS_MASK;
+    int p;
+    
+    p=(vp!=DOCK_VPOS_MIDDLE
+       ? (vp==DOCK_VPOS_TOP
+          ? (hp!=DOCK_HPOS_CENTER
+             ? (hp==DOCK_HPOS_RIGHT
+                ? MPLEX_STDISP_TR
+                : MPLEX_STDISP_TL)
+             : -1)
+          : (hp!=DOCK_HPOS_CENTER
+             ? (hp==DOCK_HPOS_RIGHT
+                ? MPLEX_STDISP_BR
+                : MPLEX_STDISP_BL)
+             : -1))
+       : -1);
+    
+    if(p==-1)
+        warn("Invalid dock position while as stdisp.");
+    else
+        *mpos=p;
+}
+    
+
+static void dock_do_set(WDock *dock, ExtlTab conftab, bool resize)
+{
+    char *s;
+    bool b;
+    bool growset=FALSE;
+    bool posset=FALSE;
+    bool save=FALSE;
+    
+    if(extl_table_gets_s(conftab, dock_param_name.key, &s)){
+        if(!region_set_name((WRegion*)dock, s)){
+            warn_obj(modname, "Can't set name to \"%s\"", s);
+        }
+        free(s);
+    }
+
+    if(extl_table_gets_b(conftab, "save", &save))
+        dock->save=save;
+    
+    if(dock_param_extl_table_set(&dock_param_pos, conftab, &dock->pos))
+        posset=TRUE;
+
+    if(dock_param_extl_table_set(&dock_param_grow, conftab, &dock->grow))
+        growset=TRUE;
+    
+    if(extl_table_gets_b(conftab, dock_param_is_auto.key, &b))
+        dock->is_auto=b;
+
+    if(resize && (growset || posset)){
+        WMPlex *par=OBJ_CAST(REGION_PARENT(dock), WMPlex);
+        WRegion *stdisp=NULL;
+        WMPlexSTDispInfo din;
+        
+        if(par!=NULL){
+            mplex_get_stdisp(par, &stdisp, &din);
+            din.fullsize=FALSE; /* not supported. */
+            if(stdisp==(WRegion*)dock){
+                if(posset)
+                    mplexpos(dock->pos, &din.pos);
+                if(growset){
+                    /* Update min/max first */
+                    dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, TRUE);
+                }
+                mplex_set_stdisp(par, (WRegion*)dock, &din);
+            }
+        }
+        
+        dock_resize(dock);
+    }
+}
+
+
+/*EXTL_DOC
+ * Configure \var{dock}. \var{conftab} is a table of key/value pairs:
+ *
+ * \begin{tabularx}{\linewidth}{llX}
+ *  \tabhead{Key & Values & Description}
+ *  \var{name} & string & Name of dock \\
+ *  \var{pos} & string in $\{t,m,b\}\times\{t,c,b\}$ & Dock position. 
+ *       Can only be used in floating mode. \\
+ *  \var{grow} & up/down/left/right & 
+ *       Growth direction where new dockapps are added. Also
+ *       sets orientation for dock when working as WMPlex status
+ *       display (see \fnref{WMPlex.set_stdisp}). \\
+ *  \var{is_auto} & bool & 
+ *       Should \var{dock} automatically manage new dockapps? \\
+ * \end{tabularx}
+ *
+ * Any parameters not explicitly set in \var{conftab} will be left unchanged.
+ */
+EXTL_EXPORT_MEMBER
+void dock_set(WDock *dock, ExtlTab conftab)
+{
+    dock_do_set(dock, conftab, TRUE);
+}
+
+
+static void dock_do_get(WDock *dock, ExtlTab conftab)
+{
+    extl_table_sets_s(conftab, dock_param_name.key,
+                      region_name((WRegion*)dock));
+    dock_param_extl_table_get(&dock_param_pos, conftab, dock->pos);
+    dock_param_extl_table_get(&dock_param_grow, conftab, dock->grow);
+    extl_table_sets_b(conftab, dock_param_is_auto.key, dock->is_auto);
+    extl_table_sets_b(conftab, "save", dock->save);
+}
+
+
+/*EXTL_DOC
+ * Get \var{dock}'s configuration table. See \fnref{WDock.set} for a
+ * description of the table.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+ExtlTab dock_get(WDock *dock)
+{
+    ExtlTab conftab;
+
+    conftab=extl_create_table();
+    dock_do_get(dock, conftab);
+    return conftab;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init/deinit */
+
+
+static bool dock_init(WDock *dock, WWindow *parent, const WFitParams *fp)
+{
+    WFitParams fp2=*fp;
+    
+    dock->pos=dock_param_pos.dflt;
+    dock->grow=dock_param_grow.dflt;
+    dock->is_auto=dock_param_is_auto.dflt;
+    dock->brush=NULL;
+    dock->dockapps=NULL;
+    dock->min_w=1;
+    dock->min_h=1;
+    dock->max_w=1;
+    dock->max_h=1;
+    dock->arrange_called=FALSE;
+    dock->save=TRUE;
+
+    
+    if(!window_init((WWindow*)dock, parent, &fp2))
+        return FALSE;
+    
+    region_add_bindmap((WRegion*)dock, dock_bindmap);
+
+    ((WRegion*)dock)->flags|=REGION_SKIP_FOCUS;
+
+    window_select_input(&(dock->win), IONCORE_EVENTMASK_CWINMGR);
+
+    dock_brush_get(dock);
+
+    LINK_ITEM(docks, dock, dock_next, dock_prev);
+    
+    return TRUE;
+}
+
+
+static WDock *create_dock(WWindow *parent, const WFitParams *fp)
+{
+    CREATEOBJ_IMPL(WDock, dock, (p, parent, fp));
+}
+
+
+static void dock_deinit(WDock *dock)
+{
+    while(dock->dockapps!=NULL)
+        destroy_obj((Obj*)dock->dockapps->reg);
+
+    UNLINK_ITEM(docks, dock, dock_next, dock_prev);
+
+    dock_brush_release(dock);
+
+    window_deinit((WWindow*) dock);
+}
+
+
+bool dock_may_destroy(WDock *dock)
+{
+    if(dock->dockapps!=NULL){
+        warn_obj(modname, "Dock \"%s\" is still managing other objects "
+                " -- refusing to close.", region_name((WRegion*)dock));
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+EXTL_EXPORT
+WDock *mod_dock_create(ExtlTab tab)
+{
+    char *mode=NULL;
+    bool floating=FALSE;
+    int screenid=0;
+    WScreen *screen=NULL;
+    WDock *dock=NULL;
+    WRegion *stdisp=NULL;
+    WMPlexSTDispInfo din;
+    
+    if(extl_table_gets_s(tab, "mode", &mode)){
+        if(strcmp(mode, "floating")==0){
+            floating=TRUE;
+        }else if(strcmp(mode, "embedded")!=0){
+            warn("Invalid dock mode.");
+            free(mode);
+            return NULL;
+        }
+        free(mode);
+    }
+    
+    extl_table_gets_i(tab, "screen", &screenid);
+    screen=ioncore_find_screen_id(screenid);
+    if(screen==NULL){
+        warn("Screen %d does not exist.", screenid);
+        return NULL;
+    }
+    
+    for(dock=docks; dock; dock=dock->dock_next){
+        if(region_screen_of((WRegion*)dock)==screen){
+            warn("Screen %d already has a dock. Refusing to create another.",
+                 screenid);
+            return NULL;
+        }
+    }
+
+    if(!floating){
+        mplex_get_stdisp((WMPlex*)screen, &stdisp, &din);
+        if(stdisp!=NULL && !extl_table_is_bool_set(tab, "force")){
+            warn("Screen %d already has an stdisp. Refusing to add embedded "
+                 "dock.", screenid);
+            return NULL;
+        }
+    }
+
+    /* Create the dock */
+    
+    if(floating){
+        WMPlexAttachParams par;
+        
+        par.flags=(MPLEX_ATTACH_UNNUMBERED
+                   |MPLEX_ATTACH_SIZEPOLICY
+                   |MPLEX_ATTACH_GEOM);
+        
+        par.szplcy=SIZEPOLICY_FREE;
+        par.geom.x=0;
+        par.geom.y=0;
+        par.geom.w=1;
+        par.geom.h=1;
+        
+        if(extl_table_is_bool_set(tab, "floating_hidden"))
+            par.flags|=MPLEX_ATTACH_HIDDEN;
+        
+        dock=(WDock*)mplex_do_attach_new((WMPlex*)screen, &par,
+                                         (WRegionCreateFn*)create_dock, 
+                                         NULL);
+    }else{
+        WFitParams fp;
+        
+        fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
+        fp.g.x=0;
+        fp.g.y=0;
+        fp.g.w=1;
+        fp.g.h=1;
+        
+        dock=create_dock((WWindow*)screen, &fp);
+    }
+
+    if(dock==NULL){
+        warn("Failed to create dock.");
+        return NULL;
+    }
+    
+    /* Get parameters */
+    dock->save=FALSE;
+    dock_do_set(dock, tab, FALSE);
+    
+    /* Final setup */    
+    if(floating){
+        WRQGeomParams rq=RQGEOMPARAMS_INIT;
+        const WRectangle *pg=&REGION_GEOM(screen);
+        
+        /* Just calculate real min/max size */
+        dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, TRUE);
+        
+        rq.geom.w=minof(dock->min_w, pg->w);
+        rq.geom.h=minof(dock->min_h, pg->h);
+        calc_dock_pos(&rq.geom, pg, dock->pos);
+        
+        region_rqgeom((WRegion*)dock, &rq, NULL);
+        
+        return dock;
+    }else{
+        mplexpos(dock->pos, &din.pos);
+        din.fullsize=FALSE; /* not supported */
+        if(mplex_set_stdisp((WMPlex*)screen, (WRegion*)dock, &din))
+            return dock;
+    }
+
+    /* Failed to attach. */
+    warn("Failed to attach dock to screen.");
+    destroy_obj((Obj*)dock);
+    return NULL;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Toggle */
+
+
+/*EXTL_DOC
+ * Toggle floating docks on \var{mplex}.
+ */
+EXTL_EXPORT
+void mod_dock_set_floating_shown_on(WMPlex *mplex, const char *how)
+{
+    int setpar=libtu_setparam_invert(libtu_string_to_setparam(how));
+    WDock *dock;
+    
+    for(dock=docks; dock; dock=dock->dock_next){
+        if(REGION_MANAGER(dock)==(WRegion*)mplex)
+            mplex_set_hidden(mplex, (WRegion*)dock, setpar);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Save/load */
+
+
+ExtlTab dock_get_configuration(WDock *dock)
+{
+    ExtlTab tab;
+    
+    if(dock->save==FALSE)
+        return extl_table_none();
+    
+    tab=region_get_base_configuration((WRegion*)dock);
+    dock_do_get(dock, tab);
+
+    return tab;
+}
+
+
+WRegion *dock_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
+{
+    WDock *dock=create_dock(par, fp);
+    if(dock!=NULL){
+        dock_set(dock, tab);
+        dock_fitrep(dock, NULL, fp);
+    }
+
+    return (WRegion*)dock;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Client window management setup */
+
+
+static bool dock_do_attach_final(WDock *dock, WRegion *reg, void *unused)
+{
+    WDockApp *dockapp, *before_dockapp;
+    WRectangle geom;
+    WFitParams fp;
+    bool draw_border=TRUE;
+    int pos=INT_MAX;
+     
+    /* Create and initialise a new WDockApp struct */
+    dockapp=ALLOC(WDockApp);
+    
+    if(dockapp==NULL)
+        return FALSE;
+    
+    if(OBJ_IS(reg, WClientWin)){
+        ExtlTab proptab=((WClientWin*)reg)->proptab;
+        extl_table_gets_b(proptab, CLIENTWIN_WINPROP_BORDER, &draw_border);
+        extl_table_gets_i(proptab, CLIENTWIN_WINPROP_POSITION, &pos);
+    }
+
+    dockapp->reg=reg;
+    dockapp->draw_border=draw_border;
+    dockapp->pos=pos;
+    dockapp->tile=FALSE;
+
+    /* Insert the dockapp at the correct relative position */
+    before_dockapp=dock->dockapps;
+    for(before_dockapp=dock->dockapps;
+        before_dockapp!=NULL && dockapp->pos>=before_dockapp->pos;
+        before_dockapp=before_dockapp->next){
+    }
+    
+    if(before_dockapp!=NULL){
+        LINK_ITEM_BEFORE(dock->dockapps, before_dockapp, dockapp, next, prev);
+    }else{
+        LINK_ITEM(dock->dockapps, dockapp, next, prev);
+    }
+
+    region_set_manager(reg, (WRegion*)dock);
+    
+    geom=REGION_GEOM(reg);
+    dock_managed_rqgeom_(dock, reg, 
+                         REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y,
+                         &geom, NULL, FALSE);
+    
+    region_map(reg);
+
+    return TRUE;
+}
+
+
+
+static WRegion *dock_do_attach(WDock *dock, WRegionAttachData *data)
+{
+    WFitParams fp;
+    dock_get_tile_size(dock, &(fp.g));
+    fp.g.x=0;
+    fp.g.y=0;
+    fp.mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS;
+    
+    return region_attach_helper((WRegion*)dock, (WWindow*)dock, &fp,
+                                (WRegionDoAttachFn*)dock_do_attach_final, 
+                                NULL, data);
+}
+
+
+/*EXTL_DOC
+ * Attach \var{reg} to \var{dock}.
+ */
+EXTL_EXPORT_MEMBER
+bool dock_attach(WDock *dock, WRegion *reg)
+{
+    WRegionAttachData data;
+    WFitParams fp;
+    
+    data.type=REGION_ATTACH_REPARENT;
+    data.u.reg=reg;
+
+    return (dock_do_attach(dock, &data)!=NULL);
+}
+
+
+static bool dock_handle_drop(WDock *dock, int x, int y,
+                             WRegion *dropped)
+{
+    return dock_attach(dock, dropped);
+}
+
+
+static WRegion *dock_ph_handler(WDock *dock, int flags, WRegionAttachData *data)
+{
+    return dock_do_attach(dock, data);
+}
+
+    
+static WPHolder *dock_managed_get_pholder(WDock *dock, WRegion *mgd)
+{
+    return (WPHolder*)create_basicpholder((WRegion*)dock,
+                                          ((WBasicPHolderHandler*)
+                                           dock_ph_handler));
+}
+
+
+static WPHolder *dock_prepare_manage(WDock *dock, const WClientWin *cwin,
+                                     const WManageParams *param UNUSED,
+                                     int redir)
+{
+    if(redir==MANAGE_REDIR_STRICT_YES)
+        return NULL;
+
+    return (WPHolder*)create_basicpholder((WRegion*)dock,
+                                          ((WBasicPHolderHandler*)
+                                           dock_ph_handler));
+}
+
+
+static void dock_managed_remove(WDock *dock, WRegion *reg)
+{
+
+    WDockApp *dockapp=dock_find_dockapp(dock, reg);
+    
+    if(dockapp==NULL)
+        return;
+    
+    UNLINK_ITEM(dock->dockapps, dockapp, next, prev);
+    free(dockapp);
+
+    region_unset_manager(reg, (WRegion*)dock);
+
+    dock_resize(dock);
+}
+
+
+static bool dock_clientwin_is_dockapp(WClientWin *cwin,
+                                      const WManageParams *param)
+{
+    bool is_dockapp=FALSE;
+
+    /* First, inspect the WManageParams.dockapp parameter */
+    if(param->dockapp){
+        is_dockapp=TRUE;
+    }
+
+    /* Second, inspect the _NET_WM_WINDOW_TYPE property */
+    if(!is_dockapp){
+        static Atom atom__net_wm_window_type=None;
+        static Atom atom__net_wm_window_type_dock=None;
+        Atom actual_type=None;
+        int actual_format;
+        unsigned long nitems;
+        unsigned long bytes_after;
+        unsigned char *prop;
+
+        if(atom__net_wm_window_type==None){
+            atom__net_wm_window_type=XInternAtom(ioncore_g.dpy,
+                                                 "_NET_WM_WINDOW_TYPE",
+                                                 False);
+        }
+        if(atom__net_wm_window_type_dock==None){
+            atom__net_wm_window_type_dock=XInternAtom(ioncore_g.dpy,
+                                                     "_NET_WM_WINDOW_TYPE_DOCK",
+                                                      False);
+        }
+        if(XGetWindowProperty(ioncore_g.dpy, cwin->win, atom__net_wm_window_type,
+                              0, sizeof(Atom), False, XA_ATOM, &actual_type,
+                              &actual_format, &nitems, &bytes_after, &prop)
+           ==Success){
+            if(actual_type==XA_ATOM && nitems>=1
+               && *(Atom*)prop==atom__net_wm_window_type_dock){
+                is_dockapp=TRUE;
+            }
+            XFree(prop);
+        }
+    }
+
+    /* Third, inspect the WM_CLASS property */
+    if(!is_dockapp){
+        char **p;
+        int n;
+
+        p=xwindow_get_text_property(cwin->win, XA_WM_CLASS, &n);
+        if(p!=NULL){
+            if(n>=2 && strcmp(p[1], "DockApp")==0){
+                is_dockapp=TRUE;
+            }
+            XFreeStringList(p);
+        }
+    }
+
+    /* Fourth, inspect the _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR property */
+    if(!is_dockapp){
+        static Atom atom__kde_net_wm_system_tray_window_for=None;
+        Atom actual_type=None;
+        int actual_format;
+        unsigned long nitems;
+        unsigned long bytes_after;
+        unsigned char *prop;
+
+        if(atom__kde_net_wm_system_tray_window_for==None){
+            atom__kde_net_wm_system_tray_window_for=XInternAtom(ioncore_g.dpy,
+                                                               "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
+                                                               False);
+        }
+        if(XGetWindowProperty(ioncore_g.dpy, cwin->win,
+                              atom__kde_net_wm_system_tray_window_for, 0,
+                              sizeof(Atom), False, AnyPropertyType, 
+                              &actual_type, &actual_format, &nitems,
+                              &bytes_after, &prop)==Success){
+            if(actual_type!=None){
+                is_dockapp=TRUE;
+            }
+            XFree(prop);
+        }
+    }
+
+    return is_dockapp;
+
+}
+
+
+static WDock *dock_find_suitable_dock(WClientWin *cwin,
+                                      const WManageParams *param)
+{
+    WDock *dock;
+
+    for(dock=docks; dock; dock=dock->dock_next){
+        if(!dock->is_auto)
+            continue;
+        if(!region_same_rootwin((WRegion*)dock, (WRegion*)cwin))
+            continue;
+        break;
+    }
+
+    return dock;
+}
+
+
+static bool clientwin_do_manage_hook(WClientWin *cwin, const WManageParams *param)
+{
+    WDock *dock;
+
+    if(!dock_clientwin_is_dockapp(cwin, param)){
+        return FALSE;
+    }
+
+    dock=dock_find_suitable_dock(cwin, param);
+    if(!dock){
+        return FALSE;
+    }
+
+    return region_manage_clientwin((WRegion*)dock, cwin, param,
+                                   MANAGE_REDIR_PREFER_NO);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Module init/deinit */
+
+
+bool mod_dock_init()
+{
+
+    if(XShapeQueryExtension(ioncore_g.dpy, &shape_event_basep,
+                            &shape_error_basep)){
+        shape_extension=TRUE;
+    }else{
+        XMissingExtension(ioncore_g.dpy, "SHAPE");
+    }
+
+    if(!ioncore_register_regclass(&CLASSDESCR(WDock), 
+                                  (WRegionLoadCreateFn*)dock_load)){
+        return FALSE;
+    }
+
+    if(!mod_dock_register_exports()){
+        ioncore_unregister_regclass(&CLASSDESCR(WDock));
+        return FALSE;
+    }
+
+    dock_bindmap=ioncore_alloc_bindmap("WDock", NULL);
+    if(dock_bindmap==NULL){
+        warn("Unable to allocate dock bindmap.");
+        mod_dock_unregister_exports();
+        ioncore_unregister_regclass(&CLASSDESCR(WDock));
+    }
+
+    extl_read_config("cfg_dock", NULL, TRUE);
+
+    hook_add(clientwin_do_manage_alt, 
+             (WHookDummy*)clientwin_do_manage_hook);
+
+    return TRUE;
+
+}
+
+
+void mod_dock_deinit()
+{
+    WDock *dock;
+
+    ioncore_unregister_regclass(&CLASSDESCR(WDock));
+
+    hook_remove(clientwin_do_manage_alt, 
+                (WHookDummy*)clientwin_do_manage_hook);
+
+    dock=docks;
+    while(dock!=NULL){
+        WDock *next=dock->dock_next;
+        destroy_obj((Obj*)dock);
+        dock=next;
+    }
+
+    mod_dock_unregister_exports();
+    
+    if(dock_bindmap!=NULL){
+        ioncore_free_bindmap("WDock", dock_bindmap);
+        dock_bindmap=NULL;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ WDock class description and dynfun list */
+
+
+static DynFunTab dock_dynfuntab[]={
+    {window_draw, dock_draw},
+    {region_updategr, dock_updategr},
+    {region_managed_rqgeom, dock_managed_rqgeom},
+    {(DynFun*)region_prepare_manage, (DynFun*)dock_prepare_manage},
+    {region_managed_remove, dock_managed_remove},
+    {(DynFun*)region_get_configuration, (DynFun*)dock_get_configuration},
+    {region_size_hints, dock_size_hints},
+    {(DynFun*)region_fitrep, (DynFun*)dock_fitrep},
+    {(DynFun*)region_orientation, (DynFun*)dock_orientation},
+    {(DynFun*)region_may_destroy, (DynFun*)dock_may_destroy},
+    {(DynFun*)region_handle_drop, (DynFun*)dock_handle_drop},
+
+    {(DynFun*)region_managed_get_pholder,
+     (DynFun*)dock_managed_get_pholder},
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WDock, WWindow, dock_deinit, dock_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/mod_menu/Makefile b/mod_menu/Makefile
new file mode 100644 (file)
index 0000000..9a2fac6
--- /dev/null
@@ -0,0 +1,27 @@
+##
+## Menu module Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES=main.c menu.c mkmenu.c grabmenu.c
+
+MAKE_EXPORTS=mod_menu
+
+MODULE=mod_menu
+MODULE_STUB=mod_menu.lua
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: module_install
diff --git a/mod_menu/grabmenu.c b/mod_menu/grabmenu.c
new file mode 100644 (file)
index 0000000..cc94437
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * ion/mod_menu/grabmenu.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libextl/extl.h>
+
+#include <ioncore/common.h>
+#include <ioncore/pointer.h>
+#include <ioncore/grab.h>
+#include <ioncore/binding.h>
+#include <ioncore/conf-bindings.h>
+#include <ioncore/key.h>
+#include "menu.h"
+#include "mkmenu.h"
+
+
+static bool grabmenu_handler(WRegion *reg, XEvent *xev)
+{
+    XKeyEvent *ev=&xev->xkey;
+    WMenu *menu=(WMenu*)reg;
+    
+    if(ev->type==KeyRelease){
+        if(ioncore_unmod(ev->state, ev->keycode)==0){
+            menu_finish(menu);
+            return TRUE;
+        }
+        return FALSE;
+    }
+    
+    if(reg==NULL)
+        return FALSE;
+    
+    if(menu->gm_state==ev->state && ev->keycode==menu->gm_kcb)
+        menu_select_next(menu);
+    
+    return FALSE;
+}
+
+
+/*--lowlevel routine not to be called by the user--*/
+EXTL_EXPORT
+WMenu *mod_menu_do_grabmenu(WMPlex *mplex, ExtlFn handler, ExtlTab tab,
+                            ExtlTab param)
+{
+    WMenuCreateParams fnp;
+    WMPlexAttachParams par;
+    WMenu *menu;
+    XKeyEvent *ev;
+    
+    ev=ioncore_current_key_event();
+    
+    if(ev==NULL)
+        return NULL;
+    
+    fnp.handler=handler;
+    fnp.tab=tab;
+    fnp.pmenu_mode=FALSE;
+    fnp.submenu_mode=FALSE;
+    fnp.big_mode=extl_table_is_bool_set(param, "big");
+    fnp.initial=0;
+    extl_table_gets_i(param, "initial", &(fnp.initial));
+
+    par.flags=(MPLEX_ATTACH_SWITCHTO|
+               MPLEX_ATTACH_UNNUMBERED|
+               MPLEX_ATTACH_SIZEPOLICY);
+    par.szplcy=SIZEPOLICY_FULL_BOUNDS;
+
+    menu=(WMenu*)mplex_do_attach_new(mplex, &par,
+                                     (WRegionCreateFn*)create_menu,
+                                     (void*)&fnp); 
+    
+    if(menu==NULL)
+        return FALSE;
+    menu->gm_kcb=ev->keycode;
+    menu->gm_state=ev->state;
+    
+    ioncore_grab_establish((WRegion*)menu, grabmenu_handler, NULL, 0);
+    
+    return menu;
+}
+
diff --git a/mod_menu/main.c b/mod_menu/main.c
new file mode 100644 (file)
index 0000000..98df15b
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * ion/mod_menu/main.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libextl/readconfig.h>
+#include <ioncore/saveload.h>
+#include <ioncore/bindmaps.h>
+
+#include "menu.h"
+#include "exports.h"
+
+
+/*{{{ Module information */
+
+
+#include "../version.h"
+
+char mod_menu_ion_api_version[]=ION_API_VERSION;
+
+
+/*}}}*/
+
+
+/*{{{ Bindmaps */
+
+
+WBindmap *mod_menu_menu_bindmap=NULL;
+
+
+/*}}}*/
+
+
+/*{{{ Init & deinit */
+
+
+void mod_menu_deinit()
+{
+    if(mod_menu_menu_bindmap!=NULL){
+        ioncore_free_bindmap("WMenu", mod_menu_menu_bindmap);
+        mod_menu_menu_bindmap=NULL;
+    }
+
+    mod_menu_unregister_exports();
+}
+
+
+bool mod_menu_init()
+{
+    mod_menu_menu_bindmap=ioncore_alloc_bindmap("WMenu", NULL);
+    
+    if(mod_menu_menu_bindmap==NULL)
+        return FALSE;
+
+    if(!mod_menu_register_exports()){
+        mod_menu_deinit();
+        return FALSE;
+    }
+    
+    /*ioncore_read_config("cfg_menu", NULL, TRUE);*/
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
diff --git a/mod_menu/main.h b/mod_menu/main.h
new file mode 100644 (file)
index 0000000..c5568b8
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * ion/mod_menu/main.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_MENU_MAIN_H
+#define ION_MOD_MENU_MAIN_H
+
+#include <ioncore/binding.h>
+
+extern bool mod_menu_init();
+extern void mod_menu_deinit();
+
+extern WBindmap *mod_menu_menu_bindmap;
+
+
+#endif /* ION_MOD_MENU_MAIN_H */
diff --git a/mod_menu/menu.c b/mod_menu/menu.c
new file mode 100644 (file)
index 0000000..b8ec9cf
--- /dev/null
@@ -0,0 +1,1375 @@
+/*
+ * ion/mod_menu/menu.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <limits.h>
+
+#include <libtu/minmax.h>
+#include <libtu/objp.h>
+#include <libtu/obj.h>
+#include <libmainloop/defer.h>
+#include <libmainloop/signal.h>
+
+#include <ioncore/common.h>
+#include <ioncore/window.h>
+#include <ioncore/global.h>
+#include <ioncore/regbind.h>
+#include <ioncore/strings.h>
+#include <ioncore/pointer.h>
+#include <ioncore/focus.h>
+#include <ioncore/event.h>
+#include <ioncore/xwindow.h>
+#include <ioncore/names.h>
+#include "menu.h"
+#include "main.h"
+
+
+#define MENU_WIN(MENU) ((MENU)->win.win)
+
+
+/*{{{ Helpers */
+
+
+static bool extl_table_getis(ExtlTab tab, int i, const char *s, char c,
+                             void *p)
+{
+    ExtlTab sub;
+    bool ret;
+    
+    if(!extl_table_geti_t(tab, i, &sub))
+        return FALSE;
+    ret=extl_table_get(sub, 's', c, s, p);
+    extl_unref_table(sub);
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Drawing routines */
+
+
+static void get_outer_geom(WMenu *menu, WRectangle *geom)
+{
+    geom->x=0;
+    geom->y=0;
+    geom->w=REGION_GEOM(menu).w;
+    geom->h=REGION_GEOM(menu).h;
+}
+
+
+static void get_inner_geom(WMenu *menu, WRectangle *geom)
+{
+    GrBorderWidths bdw;
+    
+    get_outer_geom(menu, geom);
+    
+    if(menu->brush!=NULL){
+        grbrush_get_border_widths(menu->brush, &bdw);
+        geom->x+=bdw.left;
+        geom->y+=bdw.top;
+        geom->w-=bdw.left+bdw.right;
+        geom->h-=bdw.top+bdw.bottom;
+        geom->w=maxof(0, geom->w);
+        geom->h=maxof(0, geom->h);
+    }
+}
+
+
+static void menu_draw_entry(WMenu *menu, int i, const WRectangle *igeom,
+                            bool complete)
+{
+    WRectangle geom;
+    int a;
+
+    static const char *attrs[]={
+        "active-selected-normal",
+        "active-selected-submenu",
+        "active-unselected-normal",
+        "active-unselected-submenu",
+        "inactive-selected-normal",
+        "inactive-selected-submenu",
+        "inactive-unselected-normal",
+        "inactive-unselected-submenu",
+    };
+    
+    if(menu->entry_brush==NULL)
+        return;
+    
+    geom=*igeom;
+    geom.h=menu->entry_h;
+    geom.y+=(i-menu->first_entry)*(menu->entry_h+menu->entry_spacing);
+    
+    a=((REGION_IS_ACTIVE(menu) ? 0 : 4)
+       |(menu->selected_entry==i ? 0 : 2)
+       |(menu->entries[i].flags&WMENUENTRY_SUBMENU ? 1 : 0));
+
+    grbrush_begin(menu->entry_brush, &geom, GRBRUSH_AMEND);
+
+    grbrush_draw_textbox(menu->entry_brush, &geom, menu->entries[i].title, 
+                         attrs[a], complete);
+    
+    grbrush_end(menu->entry_brush);
+}
+
+    
+void menu_draw_entries(WMenu *menu, bool complete)
+{
+    WRectangle igeom;
+    int i, mx;
+
+    if(menu->entry_brush==NULL)
+        return;
+    
+    get_inner_geom(menu, &igeom);
+    
+    mx=menu->first_entry+menu->vis_entries;
+    mx=(mx < menu->n_entries ? mx : menu->n_entries);
+    
+    for(i=menu->first_entry; i<mx; i++)
+        menu_draw_entry(menu, i, &igeom, complete);
+}
+
+
+void menu_draw(WMenu *menu, bool complete)
+{
+    WRectangle geom;
+    const char *substyle=(REGION_IS_ACTIVE(menu) ? "active" : "inactive");
+    
+    if(menu->brush==NULL)
+        return;
+    
+    get_outer_geom(menu, &geom);
+    
+    grbrush_begin(menu->brush, &geom, 
+                  (complete ? 0 : GRBRUSH_NO_CLEAR_OK));
+    
+    grbrush_draw_border(menu->brush, &geom, substyle);
+    
+    menu_draw_entries(menu, FALSE);
+    
+    grbrush_end(menu->brush);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Resize */
+
+
+static void menu_calc_size(WMenu *menu, bool maxexact, 
+                           int maxw, int maxh, 
+                           int *w_ret, int *h_ret)
+{
+    GrBorderWidths bdw, e_bdw;
+    char *str;
+    int i;
+    int nath, bdh, maxew=menu->max_entry_w;
+    
+    grbrush_get_border_widths(menu->brush, &bdw);
+    grbrush_get_border_widths(menu->entry_brush, &e_bdw);
+    
+    if(maxexact || maxew>maxw-(int)bdw.left-(int)bdw.right){
+        maxew=maxw-bdw.left-bdw.right;
+        *w_ret=maxw;
+    }else{
+        *w_ret=maxew+bdw.left+bdw.right;
+    }
+    
+    bdh=bdw.top+bdw.bottom;
+    
+    if(menu->n_entries==0){
+        *h_ret=(maxexact ? maxh : bdh);
+        menu->first_entry=0;
+        menu->vis_entries=0;
+    }else{
+        int vis=(maxh-bdh+e_bdw.spacing)/(e_bdw.spacing+menu->entry_h);
+        if(vis>menu->n_entries){
+            vis=menu->n_entries;
+            menu->first_entry=0;
+        }else if(menu->selected_entry>=0){
+            if(menu->selected_entry<menu->first_entry)
+                menu->first_entry=menu->selected_entry;
+            else if(menu->selected_entry>=menu->first_entry+vis)
+                menu->first_entry=menu->selected_entry-vis+1;
+        }
+        if(vis<=0)
+            vis=1;
+        menu->vis_entries=vis;
+        if(maxexact)
+            *h_ret=maxh;
+        else
+            *h_ret=vis*menu->entry_h+(vis-1)*e_bdw.spacing+bdh;
+    }
+
+    /* Calculate new shortened entry names */
+    maxew-=e_bdw.left+e_bdw.right;
+    
+    for(i=0; i<menu->n_entries; i++){
+        if(menu->entries[i].title){
+            free(menu->entries[i].title);
+            menu->entries[i].title=NULL;
+        }
+        if(maxew<=0)
+            continue;
+        
+        if(extl_table_getis(menu->tab, i+1, "name", 's', &str)){
+            menu->entries[i].title=grbrush_make_label(menu->entry_brush, 
+                                                      str, maxew);
+            free(str);
+        }
+    }
+}
+
+
+void calc_size(WMenu *menu, int *w, int *h)
+{
+    if(menu->pmenu_mode){
+        menu_calc_size(menu, FALSE, INT_MAX, INT_MAX, w, h);
+    }else{
+        menu_calc_size(menu, !(menu->last_fp.mode&REGION_FIT_BOUNDS),
+                       menu->last_fp.g.w, menu->last_fp.g.h, w, h);
+    }
+}
+    
+
+/* Return offset from bottom-left corner of containing mplex or top-right
+ * corner of  parent menu for the respective corner of menu.
+ */
+static void get_placement_offs(WMenu *menu, int *xoff, int *yoff)
+{
+    GrBorderWidths bdw;
+    
+    *xoff=0; 
+    *yoff=0;
+    
+    if(menu->brush!=NULL){
+        grbrush_get_border_widths(menu->brush, &bdw);
+        *xoff+=bdw.right;
+        *yoff+=bdw.top;
+    }
+    
+    if(menu->entry_brush!=NULL){
+        grbrush_get_border_widths(menu->entry_brush, &bdw);
+        *xoff+=bdw.right;
+        *yoff+=bdw.top;
+    }
+}
+    
+    
+#define MINIMUM_Y_VISIBILITY 20
+#define POINTER_OFFSET 5
+
+static void menu_firstfit(WMenu *menu, bool submenu, const WRectangle *refg)
+{
+    WRectangle geom;
+    
+    calc_size(menu, &(geom.w), &(geom.h));
+    
+    if(!(menu->last_fp.mode&REGION_FIT_BOUNDS)){
+        geom.x=menu->last_fp.g.x;
+        geom.y=menu->last_fp.g.y;
+    }else if(menu->pmenu_mode){
+        geom.x=refg->x;
+        geom.y=refg->y;
+        
+        if(!submenu){
+            const WRectangle *maxg = 
+                &REGION_GEOM(REGION_PARENT((WRegion*)menu));
+            
+            geom.x-=geom.w/2;
+            geom.y+=POINTER_OFFSET;
+
+            if(geom.y+MINIMUM_Y_VISIBILITY>maxg->y+maxg->h){
+                geom.y=maxg->y+maxg->h-MINIMUM_Y_VISIBILITY;
+                geom.x=refg->x+POINTER_OFFSET;
+                if(geom.x+geom.w>maxg->x+maxg->w)
+                    geom.x=refg->x-geom.w-POINTER_OFFSET;
+            }else{
+                if(geom.x<0)
+                    geom.x=0;
+                else if(geom.x+geom.w>maxg->x+maxg->w)
+                    geom.x=maxg->x+maxg->w-geom.w;
+            }
+        }
+    }else{
+        const WRectangle *maxg=&(menu->last_fp.g);
+        if(submenu){
+            int l, r, t, b, xoff, yoff;
+            
+            get_placement_offs(menu, &xoff, &yoff);
+            l=refg->x+xoff;
+            r=refg->x+refg->w+xoff;
+            t=refg->y-yoff;
+            b=refg->y+refg->h-yoff;
+            
+            geom.x=maxof(l, r-geom.w);
+            if(geom.x+geom.w>maxg->x+maxg->w)
+                geom.x=maxg->x;
+
+            geom.y=minof(b-geom.h, t);
+            if(geom.y<maxg->y)
+                geom.y=maxg->y;
+        }else{
+            geom.x=maxg->x;
+            geom.y=maxg->y+maxg->h-geom.h;
+        }
+    }
+    
+    window_do_fitrep(&menu->win, NULL, &geom);
+}
+
+
+static void menu_do_refit(WMenu *menu, WWindow *par, const WFitParams *oldfp)
+{
+    WRectangle geom;
+    
+    calc_size(menu, &(geom.w), &(geom.h));
+    
+    if(!(menu->last_fp.mode&REGION_FIT_BOUNDS)){
+        geom.x=menu->last_fp.g.x;
+        geom.y=menu->last_fp.g.y;
+    }else if(menu->pmenu_mode){
+        geom.x=REGION_GEOM(menu).x;
+        geom.y=REGION_GEOM(menu).y;
+    }else{
+        const WRectangle *maxg=&(menu->last_fp.g);
+        int xdiff=REGION_GEOM(menu).x-oldfp->g.x;
+        int ydiff=(REGION_GEOM(menu).y+REGION_GEOM(menu).h
+                   -(oldfp->g.y+oldfp->g.h));
+        geom.x=maxof(0, minof(maxg->x+xdiff, maxg->x+maxg->w-geom.w));
+        geom.y=maxof(0, minof(maxg->y+maxg->h+ydiff, maxg->y+maxg->h)-geom.h);
+    }
+    
+    window_do_fitrep(&menu->win, par, &geom);
+}
+
+
+bool menu_fitrep(WMenu *menu, WWindow *par, const WFitParams *fp)
+{
+    WFitParams oldfp;
+    
+    if(par!=NULL && !region_same_rootwin((WRegion*)par, (WRegion*)menu))
+        return FALSE;
+    
+    oldfp=menu->last_fp;
+    menu->last_fp=*fp;
+    menu_do_refit(menu, par, &oldfp);
+    
+    if(menu->submenu!=NULL && !menu->pmenu_mode)
+        region_fitrep((WRegion*)(menu->submenu), par, fp);
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Brush update */
+
+
+static void calc_entry_dimens(WMenu *menu)
+{
+    int i, n=extl_table_get_n(menu->tab);
+    GrFontExtents fnte;
+    GrBorderWidths bdw;
+    int maxw=0;
+    char *str;
+    
+#if 0    
+    if(extl_table_gets_s(menu->tab, title, &str)){
+        maxw=grbrush_get_text_width(title_brush, str, strlen(str));
+        free(str);
+    }
+#endif
+    
+    for(i=1; i<=n; i++){
+        if(extl_table_getis(menu->tab, i, "name", 's', &str)){
+            int w=grbrush_get_text_width(menu->entry_brush, 
+                                         str, strlen(str));
+            if(w>maxw)
+                maxw=w;
+            free(str);
+        }
+    }
+    
+    grbrush_get_border_widths(menu->entry_brush, &bdw);
+    grbrush_get_font_extents(menu->entry_brush, &fnte);
+    
+    menu->max_entry_w=maxw+bdw.left+bdw.right;
+    menu->entry_h=fnte.max_height+bdw.top+bdw.bottom;
+    menu->entry_spacing=bdw.spacing;
+}
+
+
+static bool menu_init_gr(WMenu *menu, WRootWin *rootwin, Window win)
+{
+    GrBrush *brush, *entry_brush;
+    char *st;
+    const char *style=(menu->big_mode 
+                       ? "input-menu-big"
+                       : (menu->pmenu_mode
+                          ? "input-menu-pmenu"
+                          : "input-menu-normal"));
+
+    const char *entry_style=(menu->big_mode
+                                   ? "tab-menuentry-big"
+                                   : (menu->pmenu_mode
+                                      ? "tab-menuentry-pmenu"
+                                      : "tab-menuentry-normal"));
+    
+    brush=gr_get_brush(win, rootwin, style);
+    
+    if(brush==NULL)
+        return FALSE;
+
+    entry_brush=grbrush_get_slave(brush, rootwin, entry_style);
+    
+    if(entry_brush==NULL){
+        grbrush_release(brush);
+        return FALSE;
+    }
+    
+    if(menu->entry_brush!=NULL)
+        grbrush_release(menu->entry_brush);
+    if(menu->brush!=NULL)
+        grbrush_release(menu->brush);
+
+    menu->brush=brush;
+    menu->entry_brush=entry_brush;
+    
+    calc_entry_dimens(menu);
+    
+    return TRUE;
+}
+
+
+void menu_updategr(WMenu *menu)
+{
+    if(!menu_init_gr(menu, region_rootwin_of((WRegion*)menu),
+                     MENU_WIN(menu))){
+        return;
+    }
+    
+    menu_do_refit(menu, NULL, &(menu->last_fp));
+    
+    region_updategr_default((WRegion*)menu);
+    
+    window_draw((WWindow*)menu, TRUE);
+}
+
+
+static void menu_release_gr(WMenu *menu)
+{
+    if(menu->entry_brush!=NULL){
+        grbrush_release(menu->entry_brush);
+        menu->entry_brush=NULL;
+    }
+    if(menu->brush!=NULL){
+        grbrush_release(menu->brush);
+        menu->brush=NULL;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init/deinit */
+
+
+static WMenuEntry *preprocess_menu(ExtlTab tab, int *n_entries)
+{
+    ExtlTab entry, sub;
+    ExtlFn fn;
+    WMenuEntry *entries;
+    int i, n;
+    
+    n=extl_table_get_n(tab);
+    *n_entries=n;
+    
+    if(n<=0)
+        return NULL;
+
+    entries=ALLOC_N(WMenuEntry, n);  
+    
+    if(entries==NULL)
+        return NULL;
+
+    /* Initialise entries and check submenus */
+    for(i=1; i<=n; i++){
+        entries[i-1].title=NULL;
+        entries[i-1].flags=0;
+        if(extl_table_getis(tab, i, "submenu_fn", 'f', &fn)){
+            entries[i-1].flags|=WMENUENTRY_SUBMENU;
+            extl_unref_fn(fn);
+        }else if(extl_table_getis(tab, i, "submenu", 't', &sub)){
+            entries[i-1].flags|=WMENUENTRY_SUBMENU;
+            extl_unref_table(sub);
+        }
+    }
+    
+    return entries;
+}
+
+
+
+bool menu_init(WMenu *menu, WWindow *par, const WFitParams *fp,
+               const WMenuCreateParams *params)
+{
+    Window win;
+    int i;
+    
+    menu->entries=preprocess_menu(params->tab, &(menu->n_entries));
+    
+    if(menu->entries==NULL){
+        warn(TR("Empty menu."));
+        return FALSE;
+    }
+
+    menu->tab=extl_ref_table(params->tab);
+    menu->handler=extl_ref_fn(params->handler);
+    menu->pmenu_mode=params->pmenu_mode;
+    menu->big_mode=params->big_mode;
+
+    menu->last_fp=*fp;
+    
+    if(params->pmenu_mode)
+        menu->selected_entry=-1;
+    else{
+        menu->selected_entry=params->initial-1;
+        if(menu->selected_entry<0)
+           menu->selected_entry=0;
+        if(params->initial > menu->n_entries)
+           menu->selected_entry=0;
+    }
+    
+    menu->max_entry_w=0;
+    menu->entry_h=0;
+    menu->brush=NULL;
+    menu->entry_brush=NULL;
+    menu->entry_spacing=0;
+    menu->vis_entries=menu->n_entries;
+    menu->first_entry=0;
+    menu->submenu=NULL;
+    menu->typeahead=NULL;
+    
+    menu->gm_kcb=0;
+    menu->gm_state=0;
+    
+    if(!window_init((WWindow*)menu, par, fp))
+        goto fail;
+
+    win=menu->win.win;
+    
+    if(!menu_init_gr(menu, region_rootwin_of((WRegion*)par), win))
+        goto fail2;
+
+    menu_firstfit(menu, params->submenu_mode, &(params->refg));
+    
+    window_select_input(&(menu->win), IONCORE_EVENTMASK_NORMAL);
+    
+    region_add_bindmap((WRegion*)menu, mod_menu_menu_bindmap);
+    
+    region_register((WRegion*)menu);
+    
+    return TRUE;
+
+fail2:
+    window_deinit((WWindow*)menu);
+fail:
+    extl_unref_table(menu->tab);
+    extl_unref_fn(menu->handler);
+    free(menu->entries);
+    return FALSE;
+}
+
+
+WMenu *create_menu(WWindow *par, const WFitParams *fp, 
+                   const WMenuCreateParams *params)
+{
+    CREATEOBJ_IMPL(WMenu, menu, (p, par, fp, params));
+}
+
+
+
+void menu_deinit(WMenu *menu)
+{
+    int i;
+    
+    menu_typeahead_clear(menu);
+    
+    if(menu->submenu!=NULL)
+        destroy_obj((Obj*)menu->submenu);
+    
+    extl_unref_table(menu->tab);
+    extl_unref_fn(menu->handler);
+    
+    for(i=0; i<menu->n_entries; i++)
+        free(menu->entries[i].title);
+    free(menu->entries);
+    
+    menu_release_gr(menu);
+    window_deinit((WWindow*)menu);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Focus  */
+
+
+static void menu_inactivated(WMenu *menu)
+{
+    window_draw((WWindow*)menu, FALSE);
+}
+
+
+static void menu_activated(WMenu *menu)
+{
+    window_draw((WWindow*)menu, FALSE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Submenus */
+
+
+static WMenu *menu_head(WMenu *menu)
+{
+    WMenu *m=REGION_MANAGER_CHK(menu, WMenu);
+    return (m==NULL ? menu : menu_head(m));
+}
+
+
+static WMenu *menu_tail(WMenu *menu)
+{
+    return (menu->submenu==NULL ? menu : menu_tail(menu->submenu));
+}
+
+
+static void menu_managed_remove(WMenu *menu, WRegion *sub)
+{
+    bool mcf=region_may_control_focus((WRegion*)menu);
+    
+    if(sub!=(WRegion*)menu->submenu)
+        return;
+    
+    menu->submenu=NULL;
+
+    region_unset_manager(sub, (WRegion*)menu);
+    
+    if(mcf)
+        region_do_set_focus((WRegion*)menu, FALSE);
+}
+
+
+int get_sub_y_off(WMenu *menu, int n)
+{
+    /* top + sum of above entries and spacings - top_of_sub */
+    return (menu->entry_h+menu->entry_spacing)*(n-menu->first_entry);
+}
+
+
+static void show_sub(WMenu *menu, int n)
+{
+    WFitParams fp;
+    WMenuCreateParams fnp;
+    WMenu *submenu;
+    WWindow *par;
+    
+    par=REGION_PARENT(menu);
+    
+    if(par==NULL)
+        return;
+    
+    fp=menu->last_fp;
+
+    fnp.pmenu_mode=menu->pmenu_mode;
+    fnp.big_mode=menu->big_mode;
+    fnp.submenu_mode=TRUE;
+    
+    if(menu->pmenu_mode){
+        fnp.refg.x=REGION_GEOM(menu).x+REGION_GEOM(menu).w;
+        fnp.refg.y=REGION_GEOM(menu).y+get_sub_y_off(menu, n);
+        fnp.refg.w=0;
+        fnp.refg.h=0;
+    }else{
+        fnp.refg=REGION_GEOM(menu);
+    }
+
+    fnp.tab=extl_table_none();
+    {
+        ExtlFn fn;
+        if(extl_table_getis(menu->tab, n+1, "submenu_fn", 'f', &fn)){
+            extl_protect(NULL);
+            extl_call(fn, NULL, "t", &(fnp.tab));
+            extl_unprotect(NULL);
+            extl_unref_fn(fn);
+        }else{
+            extl_table_getis(menu->tab, n+1, "submenu", 't', &(fnp.tab));
+        }
+        if(fnp.tab==extl_table_none())
+            return;
+    }
+    
+    fnp.handler=extl_ref_fn(menu->handler);
+    
+    fnp.initial=0;
+    {
+        ExtlFn fn;
+        if(extl_table_getis(menu->tab, n+1, "initial", 'f', &fn)){
+            extl_protect(NULL);
+            extl_call(fn, NULL, "i", &(fnp.initial));
+            extl_unprotect(NULL);
+           extl_unref_fn(fn);
+        }else{
+            extl_table_getis(menu->tab, n+1, "initial", 'i', &(fnp.initial));
+       }
+    }
+
+    submenu=create_menu(par, &fp, &fnp);
+    
+    if(submenu==NULL)
+        return;
+    
+    menu->submenu=submenu;
+    region_set_manager((WRegion*)submenu, (WRegion*)menu);
+        
+    region_restack((WRegion*)submenu, MENU_WIN(menu), Above);
+    region_map((WRegion*)submenu);
+    
+    if(!menu->pmenu_mode && region_may_control_focus((WRegion*)menu))
+        region_do_set_focus((WRegion*)submenu, FALSE);
+}
+
+
+static void menu_do_set_focus(WMenu *menu, bool warp)
+{
+    if(menu->submenu!=NULL)
+        region_do_set_focus((WRegion*)menu->submenu, warp);
+    else
+        window_do_set_focus((WWindow*)menu, warp);
+}
+
+
+void menu_restack(WMenu *menu, Window other, int mode)
+{
+    xwindow_restack(MENU_WIN(menu), other, mode);
+    if(menu->submenu!=NULL)
+        region_restack((WRegion*)(menu->submenu), MENU_WIN(menu), Above);
+}
+
+
+void menu_stacking(WMenu *menu, Window *bottomret, Window *topret)
+{
+    *topret=None;
+    
+    if(menu->submenu!=NULL)
+        region_stacking((WRegion*)(menu->submenu), bottomret, topret);
+    
+    *bottomret=MENU_WIN(menu);
+    if(*topret==None)
+        *topret=MENU_WIN(menu);
+
+}
+
+    
+/*}}}*/
+
+
+/*{{{ Exports */
+
+
+static void menu_do_select_nth(WMenu *menu, int n)
+{
+    int oldn=menu->selected_entry;
+    bool drawfull=FALSE;
+    
+    if(oldn==n)
+        return;
+    
+    if(menu->submenu!=NULL)
+        destroy_obj((Obj*)menu->submenu);
+    
+    assert(menu->submenu==NULL);
+
+    menu->selected_entry=n;
+
+    if(n>=0){
+        if(n<menu->first_entry){
+            menu->first_entry=n;
+            drawfull=TRUE;
+        }else if(n>=menu->first_entry+menu->vis_entries){
+            menu->first_entry=n-menu->vis_entries+1;
+            drawfull=TRUE;
+        }
+        
+        if(menu->entries[n].flags&WMENUENTRY_SUBMENU &&
+           menu->pmenu_mode){
+            show_sub(menu, n);
+        }
+    }
+    
+    if(drawfull){
+        menu_draw_entries(menu, TRUE);
+    }else{
+        /* redraw new and old selected entry */ 
+        WRectangle igeom;
+        get_inner_geom(menu, &igeom);
+
+        /* !!!BEGIN!!! */
+        if(oldn!=-1)
+            menu_draw_entry(menu, oldn, &igeom, TRUE);
+        if(n!=-1)
+            menu_draw_entry(menu, n, &igeom, TRUE);
+    }
+}
+
+
+/*EXTL_DOC
+ * Select \var{n}:th entry in menu.
+ */
+EXTL_EXPORT_MEMBER
+void menu_select_nth(WMenu *menu, int n)
+{
+    if(n<0)
+        n=0;
+    if(n>=menu->n_entries)
+        n=menu->n_entries-1;
+
+    menu_typeahead_clear(menu);
+    menu_do_select_nth(menu, n);
+}
+
+
+/*EXTL_DOC
+ * Select previous entry in menu.
+ */
+EXTL_EXPORT_MEMBER
+void menu_select_prev(WMenu *menu)
+{
+    menu_select_nth(menu, (menu->selected_entry<=0 
+                           ? menu->n_entries-1
+                           : menu->selected_entry-1));
+}
+
+
+/*EXTL_DOC
+ * Select next entry in menu.
+ */
+EXTL_EXPORT_MEMBER
+void menu_select_next(WMenu *menu)
+{
+    menu_select_nth(menu, (menu->selected_entry+1)%menu->n_entries);
+}
+
+
+static void menu_do_finish(WMenu *menu)
+{
+    ExtlFn handler;
+    ExtlTab tab;
+    bool ok;
+    WMenu *head=menu_head(menu);
+    
+    handler=menu->handler;
+    menu->handler=extl_fn_none();
+    
+    ok=extl_table_geti_t(menu->tab, menu->selected_entry+1, &tab);
+    
+    if(region_manager_allows_destroying((WRegion*)head))
+        destroy_obj((Obj*)head);
+    else if(head->submenu!=NULL)
+        destroy_obj((Obj*)head->submenu);
+    
+    if(ok)
+        extl_call(handler, "t", NULL, tab);
+
+    extl_unref_fn(handler);
+    extl_unref_table(tab);
+}
+
+
+/*EXTL_DOC
+ * If selected entry is a submenu, display that.
+ * Otherwise destroy the menu and call handler for selected entry.
+ */
+EXTL_EXPORT_MEMBER
+void menu_finish(WMenu *menu)
+{
+    menu_typeahead_clear(menu);
+
+    if(!menu->pmenu_mode && menu->selected_entry>=0 &&
+       menu->entries[menu->selected_entry].flags&WMENUENTRY_SUBMENU){
+        show_sub(menu, menu->selected_entry);
+        return;
+    }
+    
+    mainloop_defer_action((Obj*)menu, (WDeferredAction*)menu_do_finish);
+}
+
+
+
+/*EXTL_DOC
+ * Close \var{menu} not calling any possible finish handlers.
+ */
+EXTL_EXPORT_MEMBER
+void menu_cancel(WMenu *menu)
+{
+    if(region_manager_allows_destroying((WRegion*)menu))
+        mainloop_defer_destroy((Obj*)menu);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Scroll */
+
+static int scroll_time=20;
+static int scroll_amount=3;
+static WTimer *scroll_timer=NULL;
+
+
+static void reset_scroll_timer()
+{
+    if(scroll_timer!=NULL){
+        destroy_obj((Obj*)scroll_timer);
+        scroll_timer=NULL;
+    }
+}
+
+
+/*EXTL_DOC
+ * Set module basic settings. The parameter table may contain the
+ * following fields:
+ * 
+ * \begin{tabularx}{\linewidth}{lX}
+ *  \tabhead{Field & Description}
+ *  \var{scroll_amount} & Number of pixels to scroll at a time 
+ *                        pointer-controlled menus when one extends
+ *                        beyond a border of the screen and the pointer
+ *                        touches that border. \\
+ *  \var{scroll_delay}  & Time between such scrolling events in 
+ *                        milliseconds.
+ * \end{tabularx}
+ */
+EXTL_EXPORT
+void mod_menu_set(ExtlTab tab)
+{
+    int a, t;
+    
+    if(extl_table_gets_i(tab, "scroll_amount", &a))
+        scroll_amount=maxof(0, a);
+    if(extl_table_gets_i(tab, "scroll_delay", &t))
+        scroll_time=maxof(0, t);
+}
+
+
+/*EXTL_DOC
+ * Get module basic settings. For details, see \fnref{mod_menu.set}.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab mod_menu_get()
+{
+    ExtlTab tab=extl_create_table();
+    extl_table_sets_i(tab, "scroll_amount", scroll_amount);
+    extl_table_sets_i(tab, "scroll_delay", scroll_time);
+    return tab;
+}
+
+
+enum{
+    D_LEFT,
+    D_RIGHT,
+    D_DOWN,
+    D_UP
+};
+
+
+static int calc_diff(const WRectangle *mg, const WRectangle *pg, int d)
+{
+    switch(d){
+    case D_LEFT:
+        return mg->x+mg->w-pg->w;
+    case D_UP:
+        return mg->y+mg->h-pg->h;
+    case D_RIGHT:
+        return -mg->x;
+    case D_DOWN:
+        return -mg->y;
+    }
+    return 0;
+}
+
+
+static int scrolld_subs(WMenu *menu, int d)
+{
+    int diff=0;
+    WRegion *p=REGION_PARENT_REG(menu);
+    const WRectangle *pg;
+    
+    if(p==NULL)
+        return 0;
+
+    pg=&REGION_GEOM(p);
+    
+    while(menu!=NULL){
+        diff=maxof(diff, calc_diff(&REGION_GEOM(menu), pg, d));
+        menu=menu->submenu;
+    }
+    
+    return minof(maxof(0, diff), scroll_amount);
+}
+
+
+static void menu_select_entry_at(WMenu *menu, int px, int py);
+
+
+static void do_scroll(WMenu *menu, int xd, int yd)
+{
+    WRectangle g;
+    int px=-1, py=-1;
+    
+    xwindow_pointer_pos(region_root_of((WRegion*)menu), &px, &py);
+
+    while(menu!=NULL){
+        g=REGION_GEOM(menu);
+        g.x+=xd;
+        g.y+=yd;
+        
+        window_do_fitrep((WWindow*)menu, NULL, &g);
+        
+        menu_select_entry_at(menu, px, py);
+
+        menu=menu->submenu;
+    }
+}
+
+
+static void scroll_left(WTimer *timer, WMenu *menu)
+{
+    if(menu!=NULL){
+        do_scroll(menu, -scrolld_subs(menu, D_LEFT), 0);
+        if(scrolld_subs(menu, D_LEFT)>0){
+            timer_set(timer, scroll_time, (WTimerHandler*)scroll_left,
+                      (Obj*)menu);
+            return;
+        }
+    }
+}
+
+
+static void scroll_up(WTimer *timer, WMenu *menu)
+{
+    if(menu!=NULL){
+        do_scroll(menu, 0, -scrolld_subs(menu, D_UP));
+        if(scrolld_subs(menu, D_UP)>0){
+            timer_set(timer, scroll_time, (WTimerHandler*)scroll_up,
+                      (Obj*)menu);
+
+            return;
+        }
+    }
+}
+
+
+static void scroll_right(WTimer *timer, WMenu *menu)
+{
+    if(menu!=NULL){
+        do_scroll(menu, scrolld_subs(menu, D_RIGHT), 0);
+        if(scrolld_subs(menu, D_RIGHT)>0){
+            timer_set(timer, scroll_time, (WTimerHandler*)scroll_right,
+                      (Obj*)menu);
+            return;
+        }
+    }
+}
+
+
+static void scroll_down(WTimer *timer, WMenu *menu)
+{
+    if(menu!=NULL){
+        do_scroll(menu, 0, scrolld_subs(menu, D_DOWN));
+        if(scrolld_subs(menu, D_DOWN)>0){
+            timer_set(timer, scroll_time, (WTimerHandler*)scroll_down,
+                      (Obj*)menu);
+            return;
+        }
+    }
+}
+
+
+static void end_scroll(WMenu *menu)
+{
+    reset_scroll_timer();
+}
+
+#define SCROLL_OFFSET 10
+
+static void check_scroll(WMenu *menu, int x, int y)
+{
+    WRegion *parent=REGION_PARENT_REG(menu);
+    int rx, ry;
+    WTimerHandler *fn=NULL;
+
+    if(!menu->pmenu_mode)
+        return;
+    
+    if(parent==NULL){
+        end_scroll(menu);
+        return;
+    }
+
+    region_rootpos(parent, &rx, &ry);
+    x-=rx;
+    y-=ry;
+    
+    if(x<=SCROLL_OFFSET){
+        fn=(WTimerHandler*)scroll_right;
+    }else if(y<=SCROLL_OFFSET){
+        fn=(WTimerHandler*)scroll_down;
+    }else if(x>=REGION_GEOM(parent).w-SCROLL_OFFSET){
+        fn=(WTimerHandler*)scroll_left;
+    }else if(y>=REGION_GEOM(parent).h-SCROLL_OFFSET){
+        fn=(WTimerHandler*)scroll_up;
+    }else{
+        end_scroll(menu);
+        return;
+    }
+    
+    assert(fn!=NULL);
+    
+    if(scroll_timer!=NULL){
+        if(scroll_timer->handler==(WTimerHandler*)fn &&
+           timer_is_set(scroll_timer)){
+            return;
+        }
+    }else{
+        scroll_timer=create_timer();
+        if(scroll_timer==NULL)
+            return;
+    }
+    
+    fn(scroll_timer, (Obj*)menu_head(menu));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Pointer handlers */
+
+
+int menu_entry_at_root(WMenu *menu, int root_x, int root_y)
+{
+    int rx, ry, x, y, entry;
+    WRectangle ig;
+    region_rootpos((WRegion*)menu, &rx, &ry);
+    
+    get_inner_geom(menu, &ig);
+    
+    x=root_x-rx-ig.x;
+    y=root_y-ry-ig.y;
+    
+    if(x<0 || x>=ig.w || y<0  || y>=ig.h)
+        return -1;
+    
+    entry=y/(menu->entry_h+menu->entry_spacing);
+    if(entry<0 || entry>=menu->vis_entries)
+        return -1;
+    return entry+menu->first_entry;
+}
+
+
+int menu_entry_at_root_tree(WMenu *menu, int root_x, int root_y, 
+                            WMenu **realmenu)
+{
+    int entry=-1;
+    
+    menu=menu_tail(menu);
+    
+    *realmenu=menu;
+    
+    if(!menu->pmenu_mode)
+        return menu_entry_at_root(menu, root_x, root_y);
+    
+    while(menu!=NULL){
+        entry=menu_entry_at_root(menu, root_x, root_y);
+        if(entry>=0){
+            *realmenu=menu;
+            break;
+        }
+        menu=REGION_MANAGER_CHK(menu, WMenu);
+    }
+    
+    return entry;
+}
+
+
+static void menu_select_entry_at(WMenu *menu, int px, int py)
+{
+    int entry=menu_entry_at_root_tree(menu, px, py, &menu);
+    if(entry>=0)
+        menu_do_select_nth(menu, entry);
+}
+
+
+void menu_release(WMenu *menu, XButtonEvent *ev)
+{
+    int entry=menu_entry_at_root_tree(menu, ev->x_root, ev->y_root, &menu);
+    end_scroll(menu);
+    if(entry>=0){
+        menu_select_nth(menu, entry);
+        menu_finish(menu);
+    }else if(menu->pmenu_mode){
+        menu_cancel(menu_head(menu));
+    }
+}
+
+
+void menu_motion(WMenu *menu, XMotionEvent *ev, int dx, int dy)
+{
+    menu_select_entry_at(menu, ev->x_root, ev->y_root);
+    check_scroll(menu, ev->x_root, ev->y_root);
+}
+
+
+void menu_button(WMenu *menu, XButtonEvent *ev)
+{
+    int entry=menu_entry_at_root_tree(menu, ev->x_root, ev->y_root, &menu);
+    if(entry>=0)
+        menu_select_nth(menu, entry);
+}
+
+
+int menu_press(WMenu *menu, XButtonEvent *ev, WRegion **reg_ret)
+{
+    menu_button(menu, ev);
+    menu=menu_head(menu);
+    ioncore_set_drag_handlers((WRegion*)menu,
+                        NULL,
+                        (WMotionHandler*)menu_motion,
+                        (WButtonHandler*)menu_release,
+                        NULL, 
+                        NULL);
+    return 0;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Typeahead find */
+
+
+static void menu_insstr(WMenu *menu, const char *buf, size_t n)
+{
+    size_t oldlen=(menu->typeahead==NULL ? 0 : strlen(menu->typeahead));
+    char *newta=(char*)malloc(oldlen+n+1);
+    char *newta_orig;
+    int entry;
+    
+    if(newta==NULL)
+        return;
+    
+    if(oldlen!=0)
+        memcpy(newta, menu->typeahead, oldlen);
+    if(n!=0)
+        memcpy(newta+oldlen, buf, n);
+    newta[oldlen+n]='\0';
+    newta_orig=newta;
+    
+    while(*newta!='\0'){
+        bool found=FALSE;
+        entry=menu->selected_entry;
+        do{
+            if(menu->entries[entry].title!=NULL){
+                size_t l=strlen(menu->entries[entry].title);
+                if(libtu_strcasestr(menu->entries[entry].title, newta)){
+                    found=TRUE;
+                    break;
+                }
+            }
+            entry=(entry+1)%menu->n_entries;
+        }while(entry!=menu->selected_entry);
+        if(found){
+            menu_do_select_nth(menu, entry);
+            break;
+        }
+        newta++;
+    }
+    
+    if(newta_orig!=newta){
+        if(*newta=='\0'){
+            free(newta_orig);
+            newta=NULL;
+        }else{
+            char *p=scopy(newta);
+            free(newta_orig);
+            newta=p;
+        }
+    }
+    if(menu->typeahead!=NULL)
+        free(menu->typeahead);
+    menu->typeahead=newta;
+}
+
+
+/*EXTL_DOC
+ * Clear typeahead buffer.
+ */
+EXTL_EXPORT_MEMBER
+void menu_typeahead_clear(WMenu *menu)
+{
+    if(menu->typeahead!=NULL){
+        free(menu->typeahead);
+        menu->typeahead=NULL;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab menu_dynfuntab[]={
+    {(DynFun*)region_fitrep, (DynFun*)menu_fitrep},
+    {region_updategr, menu_updategr},
+    {window_draw, menu_draw},
+    {(DynFun*)window_press, (DynFun*)menu_press},
+    {region_managed_remove, menu_managed_remove},
+    {region_do_set_focus, menu_do_set_focus},
+    {region_activated, menu_activated},
+    {region_inactivated, menu_inactivated},
+    {window_insstr, menu_insstr},
+    {region_restack, menu_restack},
+    {region_stacking, menu_stacking},
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WMenu, WWindow, menu_deinit, menu_dynfuntab);
+
+    
+/*}}}*/
+
+
diff --git a/mod_menu/menu.h b/mod_menu/menu.h
new file mode 100644 (file)
index 0000000..a79d36a
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * ion/mod_menu/menu.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_MENU_MENU_H
+#define ION_MOD_MENU_MENU_H
+
+#include <ioncore/common.h>
+#include <ioncore/window.h>
+#include <ioncore/gr.h>
+#include <ioncore/rectangle.h>
+
+INTRCLASS(WMenu);
+INTRSTRUCT(WMenuEntry);
+
+#define WMENUENTRY_SUBMENU 0x0001
+
+DECLSTRUCT(WMenuEntry){
+    char *title;
+    int flags;
+};
+
+DECLCLASS(WMenu){
+    WWindow win;
+    GrBrush *brush;
+    GrBrush *entry_brush;
+
+    WFitParams last_fp;
+    
+    bool pmenu_mode;
+    bool big_mode;
+    int n_entries, selected_entry;
+    int first_entry, vis_entries;
+    int max_entry_w, entry_h, entry_spacing;
+    WMenuEntry *entries;
+    
+    WMenu *submenu;
+    
+    ExtlTab tab;
+    ExtlFn handler;
+    
+    char *typeahead;
+    
+    uint gm_kcb, gm_state;
+};
+
+
+INTRSTRUCT(WMenuCreateParams);
+
+DECLSTRUCT(WMenuCreateParams){
+    ExtlFn handler;
+    ExtlTab tab;
+    bool pmenu_mode;
+    bool submenu_mode;
+    bool big_mode;
+    int initial;
+    WRectangle refg;
+};
+
+
+extern WMenu *create_menu(WWindow *par, const WFitParams *fp,
+                          const WMenuCreateParams *params);
+extern bool menu_init(WMenu *menu, WWindow *par, const WFitParams *fp,
+                      const WMenuCreateParams *params);
+extern void menu_deinit(WMenu *menu);
+
+extern bool menu_fitrep(WMenu *menu, WWindow *par, const WFitParams *fp);
+
+extern void menu_finish(WMenu *menu);
+extern void menu_cancel(WMenu *menu);
+extern bool menu_rqclose(WMenu *menu);
+extern void menu_updategr(WMenu *menu);
+
+extern int menu_entry_at_root(WMenu *menu, int root_x, int root_y);
+extern void menu_release(WMenu *menu, XButtonEvent *ev);
+extern void menu_motion(WMenu *menu, XMotionEvent *ev, int dx, int dy);
+extern void menu_button(WMenu *menu, XButtonEvent *ev);
+extern int menu_press(WMenu *menu, XButtonEvent *ev, WRegion **reg_ret);
+
+extern void mod_menu_set_scroll_params(int delay, int amount);
+
+extern void menu_typeahead_clear(WMenu *menu);
+
+extern void menu_select_nth(WMenu *menu, int n);
+extern void menu_select_prev(WMenu *menu);
+extern void menu_select_next(WMenu *menu);
+
+#endif /* ION_MOD_MENU_MENU_H */
diff --git a/mod_menu/mkmenu.c b/mod_menu/mkmenu.c
new file mode 100644 (file)
index 0000000..5f4fecf
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * ion/mod_menu/mkmenu.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <limits.h>
+#include <ioncore/common.h>
+#include <ioncore/pointer.h>
+#include <ioncore/grab.h>
+#include <libextl/extl.h>
+#include "menu.h"
+#include "mkmenu.h"
+
+
+/*--lowlevel routine not to be called by the user--EXTL_DOC
+ * Display a menu inside multiplexer. The \var{handler} parameter
+ * is a function that gets the selected menu entry as argument and
+ * should call it with proper parameters. The table \var{tab} is a
+ * list of menu entries of the form \code{\{name = ???, [ submenu_fn = ??? ]\}}.
+ * The function \var{submenu_fn} return a similar submenu definition 
+ * when called.
+ * 
+ * Do not use this function directly. Use  \fnref{mod_menu.menu} and
+ * \fnref{mod_menu.bigmenu}.
+ */
+EXTL_EXPORT
+WMenu *mod_menu_do_menu(WMPlex *mplex, ExtlFn handler, ExtlTab tab, 
+                        ExtlTab param)
+{
+    WMenuCreateParams fnp;
+    WMPlexAttachParams par;
+
+    fnp.handler=handler;
+    fnp.tab=tab;
+    fnp.pmenu_mode=FALSE;
+    fnp.submenu_mode=FALSE;
+    fnp.big_mode=extl_table_is_bool_set(param, "big");
+    fnp.initial=0;
+    extl_table_gets_i(param, "initial", &(fnp.initial));
+    fnp.refg.x=0;
+    fnp.refg.y=0;
+    fnp.refg.w=0;
+    fnp.refg.h=0;
+    
+    par.flags=(MPLEX_ATTACH_SWITCHTO|
+               MPLEX_ATTACH_MODAL|
+               MPLEX_ATTACH_UNNUMBERED|
+               MPLEX_ATTACH_SIZEPOLICY);
+    par.szplcy=SIZEPOLICY_FULL_BOUNDS;
+        
+    return (WMenu*)mplex_do_attach_new(mplex, &par,
+                                       (WRegionCreateFn*)create_menu,
+                                       (void*)&fnp); 
+}
+
+
+/*--lowlevel routine not to be called by the user--EXTL_DOC
+ * Display a pop-up menu inside window \var{where}. This function
+ * can only be called from a mouse/pointing device button press handler
+ * and the menu will be placed below the point where the press occured.
+ * The \var{handler} and \var{tab} parameters are similar to those of
+ * \fnref{menu_menu}.
+ * 
+ * Do not use this function directly. Use \fnref{mod_menu.pmenu}.
+ */
+EXTL_EXPORT
+WMenu *mod_menu_do_pmenu(WWindow *where, ExtlFn handler, ExtlTab tab)
+{
+    WScreen *scr;
+    WMenuCreateParams fnp;
+    XEvent *ev=ioncore_current_pointer_event();
+    WMenu *menu;
+    WFitParams fp;
+    
+    if(ev==NULL || ev->type!=ButtonPress)
+        return NULL;
+
+    scr=region_screen_of((WRegion*)where);
+
+    if(scr==NULL)
+        return NULL;
+    
+    fnp.handler=handler;
+    fnp.tab=tab;
+    fnp.pmenu_mode=TRUE;
+    fnp.big_mode=FALSE;
+    fnp.submenu_mode=FALSE;
+    fnp.initial=0;
+    fnp.refg.x=ev->xbutton.x_root-REGION_GEOM(scr).x;
+    fnp.refg.y=ev->xbutton.y_root-REGION_GEOM(scr).y;
+    fnp.refg.w=0;
+    fnp.refg.h=0;
+    
+    fp.mode=REGION_FIT_BOUNDS;
+    fp.g.x=REGION_GEOM(where).x;
+    fp.g.y=REGION_GEOM(where).y;
+    fp.g.w=REGION_GEOM(where).w;
+    fp.g.h=REGION_GEOM(where).h;
+    
+    menu=create_menu((WWindow*)scr, &fp, &fnp);
+    
+    if(menu==NULL)
+        return NULL;
+
+    region_restack((WRegion*)menu, None, Above);
+    
+    if(!ioncore_set_drag_handlers((WRegion*)menu,
+                            NULL,
+                            (WMotionHandler*)menu_motion,
+                            (WButtonHandler*)menu_release,
+                            NULL, 
+                            (GrabKilledHandler*)menu_cancel)){
+        destroy_obj((Obj*)menu);
+        return NULL;
+    }
+    
+    region_map((WRegion*)menu);
+    
+    return menu;
+}
+
diff --git a/mod_menu/mkmenu.h b/mod_menu/mkmenu.h
new file mode 100644 (file)
index 0000000..2efed2f
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * ion/mod_menu/mkmenu.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_MENU_MKMENU_H
+#define ION_MOD_MENU_MKMENU_H
+
+#include <ioncore/common.h>
+#include <libextl/extl.h>
+#include "menu.h"
+
+extern WMenu *mod_menu_do_menu(WMPlex *mplex, ExtlFn handler, ExtlTab tab, 
+                               ExtlTab param);
+extern WMenu *mod_menu_do_pmenu(WWindow *where, ExtlFn handler, ExtlTab tab);
+
+#endif /* ION_MOD_MENU_MKMENU_H */
diff --git a/mod_menu/mod_menu.lua b/mod_menu/mod_menu.lua
new file mode 100644 (file)
index 0000000..6375e9f
--- /dev/null
@@ -0,0 +1,108 @@
+--
+-- ion/mod_menu/mod_menu.lua -- Menu opening helper routines.
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+-- This is a slight abuse of the package.loaded variable perhaps, but
+-- library-like packages should handle checking if they're loaded instead of
+-- confusing the user with require/include differences.
+if package.loaded["mod_menu"] then return end
+
+if not ioncore.load_module("mod_menu") then
+    return
+end
+
+local mod_menu=_G["mod_menu"]
+local menudb=_G["ioncore"]
+
+assert(mod_menu and menudb)
+
+
+-- Menu commands {{{
+
+local function menu_(reg, sub, menu_or_name, fn, check)
+    if check then
+        -- Check that no other menus are open in reg.
+        local l=reg:managed_list()
+        for i, r in pairs(l) do
+            if obj_is(r, "WMenu") then
+                return
+            end
+        end
+    end
+
+    menu=menudb.evalmenu(menu_or_name, {reg, sub})
+    
+    return fn(reg, function(e) e.func(reg, sub) end, menu)
+end
+
+
+--DOC
+-- Display a menu in the lower-left corner of \var{mplex}.
+-- The variable \var{menu_or_name} is either the name of a menu
+-- defined with \fnref{mod_menu.defmenu} or directly a table similar
+-- to ones passesd to this function. When this function is
+-- called from a binding handler, \var{sub} should be set to
+-- the second argument of to the binding handler (\var{_sub})
+-- so that the menu handler will get the same parameters as the
+-- binding handler. Extra options can be passed in the table
+-- \var{param}. The initial entry can be specified as the field
+-- \var{initial} as an integer starting from 1. Menus can be made
+-- to use a bigger style by setting the field \var{big} to \code{true}.
+function mod_menu.menu(mplex, sub, menu_or_name, param) 
+   local function menu_stdmenu(m, s, menu)
+      return mod_menu.do_menu(m, s, menu, param)
+   end
+   return menu_(mplex, sub, menu_or_name, menu_stdmenu, true)
+end
+
+-- Compatibility
+function mod_menu.bigmenu(mplex, sub, menu_or_name, initial) 
+    local function menu_bigmenu(m, s, menu)
+      return mod_menu.do_menu(m, s, menu, {big=true, initial=initial})
+    end
+    return menu_(mplex, sub, menu_or_name, menu_bigmenu, true)
+end
+
+--DOC
+-- This function is similar to \fnref{mod_menu.menu}, but input
+-- is grabbed and the key used to active the menu can be used to
+-- cycle through menu entries.
+function mod_menu.grabmenu(mplex, sub, menu_or_name, param) 
+    local function menu_grabmenu(m, s, menu)
+        return mod_menu.do_grabmenu(m, s, menu, param)
+    end
+    return menu_(mplex, sub, menu_or_name, menu_grabmenu, true)
+end
+
+-- Compatibility
+function mod_menu.biggrabmenu(mplex, sub, menu_or_name, key, initial) 
+    local function menu_biggrabmenu(m, s, menu)
+        return mod_menu.do_grabmenu(m, s, menu, true, key, initial or 0)
+    end
+    return menu_(mplex, sub, menu_or_name, menu_biggrabmenu, true, initial)
+end
+
+--DOC
+-- This function displays a drop-down menu and should only
+-- be called from a mouse press handler. The parameters are
+-- similar to those of \fnref{mod_menu.menu}.
+function mod_menu.pmenu(win, sub, menu_or_name) 
+    return menu_(win, sub, menu_or_name, mod_menu.do_pmenu)
+end
+
+-- }}}
+
+
+-- Mark ourselves loaded.
+package.loaded["mod_menu"]=true
+
+
+-- Load configuration file
+dopath('cfg_menu', true)
diff --git a/mod_mgmtmode/Makefile b/mod_mgmtmode/Makefile
new file mode 100644 (file)
index 0000000..2d0b769
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## Management mode module Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES=main.c mgmtmode.c
+
+MAKE_EXPORTS=mod_mgmtmode
+
+MODULE=mod_mgmtmode
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: module_install
diff --git a/mod_mgmtmode/main.c b/mod_mgmtmode/main.c
new file mode 100644 (file)
index 0000000..fc5ecc6
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * ion/mod_mgmtmode/main.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libextl/readconfig.h>
+#include <ioncore/saveload.h>
+#include <ioncore/bindmaps.h>
+
+#include "exports.h"
+
+/*{{{ Module information */
+
+
+#include "../version.h"
+
+char mod_mgmtmode_ion_api_version[]=ION_API_VERSION;
+
+
+/*}}}*/
+
+
+/*{{{ Bindmaps */
+
+
+WBindmap *mod_mgmtmode_bindmap=NULL;
+
+
+/*}}}*/
+
+
+/*{{{ Init & deinit */
+
+
+void mod_mgmtmode_deinit()
+{
+    if(mod_mgmtmode_bindmap!=NULL){
+        ioncore_free_bindmap("WMgmtMode", mod_mgmtmode_bindmap);
+        mod_mgmtmode_bindmap=NULL;
+    }
+
+    mod_mgmtmode_unregister_exports();
+}
+
+
+bool mod_mgmtmode_init()
+{
+    mod_mgmtmode_bindmap=ioncore_alloc_bindmap("WMgmtMode", NULL);
+    
+    if(mod_mgmtmode_bindmap==NULL)
+        return FALSE;
+
+    if(!mod_mgmtmode_register_exports()){
+        mod_mgmtmode_deinit();
+        return FALSE;
+    }
+
+    extl_read_config("cfg_mgmtmode", NULL, TRUE);
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
diff --git a/mod_mgmtmode/main.h b/mod_mgmtmode/main.h
new file mode 100644 (file)
index 0000000..30f61f3
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * ion/mod_mgmtmode/main.h
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_MGMTMODE_MAIN_H
+#define ION_MOD_MGMTMODE_MAIN_H
+
+#include <ioncore/binding.h>
+
+extern bool mod_mgmtmode_init();
+extern void mod_mgmtmode_deinit();
+
+extern WBindmap *mod_mgmtmode_bindmap;
+
+#endif /* ION_MOD_MGMTMODE_MAIN_H */
diff --git a/mod_mgmtmode/mgmtmode.c b/mod_mgmtmode/mgmtmode.c
new file mode 100644 (file)
index 0000000..92a3d27
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * ion/mod_mgmtmode/mgmtmode.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <math.h>
+#include <sys/time.h>
+#include <time.h>
+#include <limits.h>
+
+#include <libtu/objp.h>
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/region.h>
+#include <ioncore/rootwin.h>
+#include <ioncore/binding.h>
+#include <ioncore/grab.h>
+#include "mgmtmode.h"
+#include "main.h"
+
+
+static WMgmtMode *mgmt_mode=NULL;
+
+
+static void cancel_mgmt(WRegion *reg);
+
+
+/*{{{ WMgmtMode */
+
+
+static bool mgmtmode_init(WMgmtMode *mode, WRegion *reg)
+{
+    watch_init(&(mode->selw));
+    watch_setup(&(mode->selw), (Obj*)reg, NULL);
+    return TRUE;
+}
+
+
+static WMgmtMode *create_mgmtmode(WRegion *reg)
+{
+    CREATEOBJ_IMPL(WMgmtMode, mgmtmode, (p, reg));
+}
+
+
+static void mgmtmode_deinit(WMgmtMode *mode)
+{
+    if(mgmt_mode==mode)
+       mgmt_mode=NULL;
+    
+    watch_reset(&(mode->selw));
+}
+
+
+/*EXTL_DOC
+ * Select management mode target.
+ */
+EXTL_EXPORT_MEMBER
+void mgmtmode_select(WMgmtMode *mode, WRegion *reg)
+{
+    watch_setup(&(mode->selw), (Obj*)reg, NULL);
+}
+
+
+/*EXTL_DOC
+ * Return management mode target.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WRegion *mgmtmode_selected(WMgmtMode *mode)
+{
+    return (WRegion*)(mode->selw.obj);
+}
+
+
+/*EXTL_DOC
+ * End management mode.
+ */
+EXTL_EXPORT_MEMBER
+void mgmtmode_finish(WMgmtMode *mode)
+{
+    if(mgmt_mode==mode)
+        cancel_mgmt(NULL);
+}
+    
+
+EXTL_EXPORT
+IMPLCLASS(WMgmtMode, Obj, mgmtmode_deinit, NULL);
+
+
+/*}}}*/
+
+
+/*{{{ Rubberband */
+
+
+static void draw_rubberbox(WRootWin *rw, const WRectangle *rect)
+{
+    XPoint fpts[5];
+    
+    fpts[0].x=rect->x;
+    fpts[0].y=rect->y;
+    fpts[1].x=rect->x+rect->w;
+    fpts[1].y=rect->y;
+    fpts[2].x=rect->x+rect->w;
+    fpts[2].y=rect->y+rect->h;
+    fpts[3].x=rect->x;
+    fpts[3].y=rect->y+rect->h;
+    fpts[4].x=rect->x;
+    fpts[4].y=rect->y;
+    
+    XDrawLines(ioncore_g.dpy, WROOTWIN_ROOT(rw), rw->xor_gc, fpts, 5, 
+               CoordModeOrigin);
+}
+
+
+static void mgmtmode_draw(WMgmtMode *mode)
+{
+    WRegion *reg=mgmtmode_selected(mode);
+    
+    if(reg!=NULL){
+        WRootWin *rw=region_rootwin_of(reg);
+        WRectangle g=REGION_GEOM(reg);
+        int rx=0, ry=0;
+        
+        region_rootpos(reg, &rx, &ry);
+        
+        g.x=rx;
+        g.y=ry;
+        
+        draw_rubberbox(rw, &g);
+    }
+}
+
+
+static void mgmtmode_erase(WMgmtMode *mode)
+{
+    mgmtmode_draw(mode);
+}
+
+
+/*}}}*/
+
+
+/*{{{ The mode */
+
+
+static bool mgmt_handler(WRegion *reg, XEvent *xev)
+{
+    XKeyEvent *ev=&xev->xkey;
+    WBinding *binding=NULL;
+    WMgmtMode *mode;
+    
+    if(ev->type==KeyRelease)
+        return FALSE;
+    
+    if(reg==NULL)
+        return FALSE;
+    
+    mode=mgmt_mode;
+    
+    if(mode==NULL)
+        return FALSE;
+    
+    binding=bindmap_lookup_binding(mod_mgmtmode_bindmap, 
+                                   BINDING_KEYPRESS,
+                                   ev->state, ev->keycode);
+    
+    if(!binding)
+        return FALSE;
+    
+    if(binding!=NULL){
+        mgmtmode_erase(mode);
+        extl_call(binding->func, "o", NULL, mode);
+        if(mgmt_mode!=NULL)
+            mgmtmode_draw(mgmt_mode);
+    }
+    
+    return (mgmt_mode==NULL);
+}
+
+
+static void cancel_mgmt(WRegion *reg)
+{
+    if(mgmt_mode!=NULL){
+        mgmtmode_erase(mgmt_mode);
+        destroy_obj((Obj*)mgmt_mode);
+    }
+    ioncore_grab_remove(mgmt_handler);
+}
+
+
+/*EXTL_DOC
+ * Begin management mode.
+ */
+EXTL_EXPORT
+WMgmtMode *mod_mgmtmode_begin(WRegion *reg)
+{
+    if(mgmt_mode!=NULL)
+        return NULL;
+    
+    mgmt_mode=create_mgmtmode(reg);
+
+    if(mgmt_mode==NULL)
+        return NULL;
+    
+    ioncore_grab_establish((WRegion*)region_rootwin_of(reg), 
+                           mgmt_handler,
+                           (GrabKilledHandler*)cancel_mgmt, 0);
+    
+    mgmtmode_draw(mgmt_mode);
+    
+    return mgmt_mode;
+}
+
+
+/*}}}*/
diff --git a/mod_mgmtmode/mgmtmode.h b/mod_mgmtmode/mgmtmode.h
new file mode 100644 (file)
index 0000000..1e5ee64
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * ion/mod_mgmtmode/mgmtmode.h
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_MGMTMODE_MGMTMODE_H
+#define ION_MOD_MGMTMODE_MGMTMODE_H
+
+#include <ioncore/common.h>
+#include <libextl/extl.h>
+
+INTRCLASS(WMgmtMode);
+
+DECLCLASS(WMgmtMode){
+    Obj obj;
+    Watch selw;
+};
+
+
+extern WMgmtMode *mod_mgmtmode_begin(WRegion *reg);
+
+extern void mgmtmode_select(WMgmtMode *mode, WRegion *reg);
+extern WRegion *mgmtmode_selected(WMgmtMode *mode);
+
+extern void mgmtmode_finish(WMgmtMode *mode);
+
+#endif /* ION_MOD_MGMTMODE_MGMTMODE_H */
diff --git a/mod_panews/Makefile b/mod_panews/Makefile
new file mode 100644 (file)
index 0000000..c1b79fb
--- /dev/null
@@ -0,0 +1,27 @@
+##
+## mod_panews workspace module Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES=main.c panews.c placement.c splitext.c unusedwin.c
+
+MAKE_EXPORTS=mod_panews
+
+MODULE=mod_panews
+MODULE_STUB=mod_panews.lua
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: module_install
diff --git a/mod_panews/main.c b/mod_panews/main.c
new file mode 100644 (file)
index 0000000..b3f4f10
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * ion/panews/main.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/map.h>
+
+#include <ioncore/common.h>
+#include <ioncore/reginfo.h>
+#include <libextl/readconfig.h>
+#include <ioncore/framep.h>
+#include <ioncore/bindmaps.h>
+#include <ioncore/bindmaps.h>
+
+#include "main.h"
+#include "panews.h"
+#include "placement.h"
+#include "exports.h"
+
+
+/*{{{ Module information */
+
+
+#include "../version.h"
+
+char mod_panews_ion_api_version[]=ION_API_VERSION;
+
+
+/*}}}*/
+
+
+/*{{{ Bindmaps */
+
+
+WBindmap *mod_panews_panews_bindmap=NULL;
+WBindmap *mod_panews_frame_bindmap=NULL;
+WBindmap *mod_panews_unusedwin_bindmap=NULL;
+
+
+/*}}}*/
+
+
+/*{{{ Module init & deinit */
+
+
+void mod_panews_deinit()
+{
+    mod_panews_unregister_exports();
+    ioncore_unregister_regclass(&CLASSDESCR(WPaneWS));
+    
+    if(mod_panews_panews_bindmap!=NULL){
+        ioncore_free_bindmap("WPaneWS", mod_panews_panews_bindmap);
+        mod_panews_panews_bindmap=NULL;
+    }
+
+    if(mod_panews_unusedwin_bindmap!=NULL){
+        ioncore_free_bindmap("WUnusedWin", mod_panews_unusedwin_bindmap);
+        mod_panews_unusedwin_bindmap=NULL;
+    }
+    
+    if(mod_panews_frame_bindmap!=NULL){
+        ioncore_free_bindmap("WFrame-on-WPaneWS", mod_panews_frame_bindmap);
+        mod_panews_frame_bindmap=NULL;
+    }
+    
+    if(panews_init_layout_alt!=NULL){
+        destroy_obj((Obj*)panews_init_layout_alt);
+        panews_init_layout_alt=NULL;
+    }
+
+    if(panews_make_placement_alt!=NULL){
+        destroy_obj((Obj*)panews_make_placement_alt);
+        panews_make_placement_alt=NULL;
+    }
+}
+
+
+static bool register_regions()
+{
+    if(!ioncore_register_regclass(&CLASSDESCR(WPaneWS),
+                                  (WRegionLoadCreateFn*)panews_load)){
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+#define INIT_HOOK_(NM)                             \
+    NM=mainloop_register_hook(#NM, create_hook()); \
+    if(NM==NULL) return FALSE;
+
+
+static bool init_hooks()
+{
+    INIT_HOOK_(panews_init_layout_alt);
+    INIT_HOOK_(panews_make_placement_alt);
+    return TRUE;
+}
+
+
+
+bool mod_panews_init()
+{
+    if(!init_hooks())
+        goto err;
+
+    mod_panews_panews_bindmap=ioncore_alloc_bindmap("WPaneWS", NULL);
+    mod_panews_unusedwin_bindmap=ioncore_alloc_bindmap_frame("WUnusedWin");
+    mod_panews_frame_bindmap=ioncore_alloc_bindmap_frame("WFrame-on-WPaneWS");
+
+    if(mod_panews_panews_bindmap==NULL ||
+       mod_panews_unusedwin_bindmap==NULL ||
+       mod_panews_frame_bindmap==NULL){
+        goto err;
+    }
+
+    if(!mod_panews_register_exports())
+        goto err;
+    
+    if(!register_regions())
+        goto err;
+    
+    /*ioncore_read_config("cfg_panews", NULL, FALSE);*/
+
+    return TRUE;
+    
+err:
+    mod_panews_deinit();
+    return FALSE;
+}
+
+
+/*}}}*/
+
diff --git a/mod_panews/main.h b/mod_panews/main.h
new file mode 100644 (file)
index 0000000..5dc226c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * ion/panews/main.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_PANEWS_MAIN_H
+#define ION_PANEWS_MAIN_H
+
+#include <ioncore/binding.h>
+#include <ioncore/regbind.h>
+
+extern bool mod_panews_init();
+extern void mod_panews_deinit();
+
+extern WBindmap *mod_panews_panews_bindmap;
+extern WBindmap *mod_panews_unusedwin_bindmap;
+extern WBindmap *mod_panews_frame_bindmap;
+
+#endif /* ION_PANEWS_MAIN_H */
diff --git a/mod_panews/mod_panews.lua b/mod_panews/mod_panews.lua
new file mode 100644 (file)
index 0000000..65bf32e
--- /dev/null
@@ -0,0 +1,562 @@
+--
+-- ion/mod_panews/mod_panews.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+-- 
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+
+-- This is a slight abuse of the package.loaded variable perhaps, but
+-- library-like packages should handle checking if they're loaded instead of
+-- confusing the user with require/include differences.
+if package.loaded["templates"] then return end
+
+if not ioncore.load_module("mod_panews") then
+    return
+end
+
+assert(not _G["mod_panews"]);
+local mod_panews={}
+_G["mod_panews"]=mod_panews
+
+local private={}
+local settings={}
+
+-- Settings {{{
+
+-- Classes:
+--  (T)erminal
+--  (V)iewer
+--  (M)isc
+settings.valid_classifications={["V"]=true, ["T"]=true, ["M"]=true,}
+
+-- Xterm, rxvt, aterm, etc. all have "XTerm" as class part of WM_CLASS
+settings.terminal_emulators={["XTerm"]=true,} 
+
+-- Pixel scale factor from 1280x1024/75dpi
+settings.scalef=1.0
+
+--settings.b_ratio=(1+math.sqrt(5))/2
+--settings.s_ratio=1
+settings.b_ratio=3
+settings.s_ratio=2
+
+settings.b_ratio2=7
+settings.s_ratio2=1
+
+settings.templates={}
+
+settings.templates["default"]={
+    type="WSplitFloat",
+    dir="horizontal",
+    tls=settings.b_ratio,
+    brs=settings.s_ratio,
+    tls_brs_incl_handles=true,
+    tl={
+        type="WSplitPane",
+        contents={
+            type="WSplitFloat",
+            dir="vertical",
+            tls=settings.b_ratio2,
+            brs=settings.s_ratio2,
+            tls_brs_incl_handles=true,
+            tl={
+                type="WSplitPane",
+                marker="V:single",
+            },
+            br={
+                type="WSplitPane",
+                marker="M:right",
+            },
+        },
+    },
+    br={
+        type="WSplitPane",
+        marker="T:up",
+    },
+}
+
+
+settings.templates["alternative1"]={
+    type="WSplitFloat",
+    dir="horizontal",
+    tls=settings.s_ratio2,
+    brs=settings.b_ratio2,
+    tls_brs_incl_handles=true,
+    tl={
+        type="WSplitPane",
+        marker="M:down",
+    },
+    br={
+        type="WSplitFloat",
+        dir="vertical",
+        tls=settings.b_ratio,
+        brs=settings.s_ratio,
+        tls_brs_incl_handles=true,
+        tl={
+            type="WSplitPane",
+            marker="V:single",
+        },
+        br={
+            type="WSplitPane",
+            marker="T:right",
+        },
+    },
+}
+
+
+settings.templates["alternative2"]={
+    type="WSplitFloat",
+    dir="vertical",
+    tls=settings.b_ratio,
+    brs=settings.s_ratio,
+    tls_brs_incl_handles=true,
+    tl={
+        type="WSplitFloat",
+        dir="horizontal",
+        tls=settings.s_ratio2,
+        brs=settings.b_ratio2,
+        tls_brs_incl_handles=true,
+        tl={
+            type="WSplitPane",
+            marker="M:down",
+        },
+        br={
+            type="WSplitPane",
+            marker="V:single",
+        },
+    },
+    br={
+        type="WSplitPane",
+        marker="T:right",
+    },
+}
+
+
+settings.template=settings.templates["default"]
+
+settings.shrink_minimum=32
+
+--DOC
+-- Set some module parameters. Currently \var{s} may contain the following
+-- fields:
+-- \begin{tabularx}{\linewidth}{lX}
+--  \tabhead{Field & Description}
+--  \var{template} & layout template for newly created \type{WPaneWS} 
+--                   workspaces. This can be either a table or one of the 
+--                   predefined layouts 'default', 'alternative1', and
+--                   'alternative2'. \\
+--  \var{scalef}   & Scale factor for classification heuristics to work 
+--                   with different screen resolutions. The default is 1.0 
+--                   and is designed for 1280x1024 at 75dpi. \\
+--  \var{valid_classifications} & A table with valid window classifications
+--                                as valid keys. \\
+-- \end{tabularx}
+function mod_panews.set(s)
+    if s.template then
+        local ok=false
+        if type(s.template)=="string" then
+            if settings.templates[s.template] then
+                settings.template=settings.templates[s.template]
+                ok=true
+            end
+        elseif type(s.template)=="table" then
+            settings.template=s.template
+            ok=true
+        end
+        if not ok then
+            ioncore.warn_traced("Invalid template.")
+        end
+    end
+    if s.scalef then
+        if type(s.scalef)~="number" or s.scalef<=0 then
+            ioncore.warn_traced('Invalid scale factor')
+        else
+            settings.scalef=s.scalef
+        end
+    end
+    
+    if type(s.valid_classifications)=="table" then
+        settings.valid_classifications=s.valid_classifications
+    end
+end
+
+
+--DOC
+-- Get some module settings. See \fnref{mod_panews.set} for documentation
+-- on the contents of the returned table.
+function mod_panews.get()
+    return table.copy(settings, true)
+end
+
+                        
+-- }}}
+
+
+-- Helper code {{{
+
+local function sfind(s, p)
+    local function drop2(a, b, ...)
+        return unpack(arg)
+    end
+    return drop2(string.find(s, p))
+end
+
+
+function private.div_length(w, r1, r2)
+    local a=math.ceil(w*r1/(r1+r2))
+    return a, w-a
+end
+
+function private.split3(d, ls, cs, rs, lo, co, ro)
+    return {
+        tls = ls+cs,
+        brs = rs,
+        dir = d,
+        tl = {
+            tls = ls,
+            brs = cs,
+            dir = d,
+            tl = (lo or {}),
+            br = (co or {}),
+        },
+        br = (ro or {}),
+    }
+end
+
+function private.center3(d, ts, cs, lo, co, ro)
+    local sc=math.min(ts, cs)
+    local sl=math.floor((ts-sc)/2)
+    local sr=ts-sc-sl
+    local r=private.split3(d, sl, sc, sr, lo, co, ro)
+    return r
+end
+
+function private.split2(d, ts, ls, rs, lo, ro)
+    if ls and rs then
+        assert(ls+rs==ts)
+    elseif not ls then
+        ls=ts-rs
+    else
+        rs=ts-ls
+    end
+    assert(rs>=0 and ls>=0)
+    return {
+        type="WSplitSplit",
+        dir=d,
+        tls=math.floor(ls),
+        brs=math.ceil(rs),
+        tl=lo,
+        br=ro,
+    }
+end
+
+-- }}}
+
+
+-- Classification {{{
+
+function private.classify(ws, reg)
+    if obj_is(reg, "WClientWin") then
+        -- Check if there's a winprop override
+        local wp=ioncore.getwinprop(reg)
+        if wp and wp.panews_classification then
+            if settings.valid_classifications[wp.panews_classification] then
+                return wp.panews_classification
+            end
+        end
+        
+        -- Handle known terminal emulators.
+        local id=reg:get_ident()
+        if settings.terminal_emulators[id.class] then
+            return "T"
+        end
+    end
+    
+    -- Try size heuristics.
+    local cg=reg:geom()
+
+    if cg.w<3/8*(1280*settings.scalef) then
+        return "M"
+    end
+    
+    if cg.h>4/8*(960*settings.scalef) then
+        return "V"
+    else
+        return "T"
+    end
+end
+
+-- }}}
+
+
+-- Placement code {{{
+
+local max_penalty=5
+local just_some_pixel_count=16
+
+function private.fitlevel_(min1, s1, us1, min2, s2, us2)
+    local p1, p2=4, 0.5
+    
+    if us2>=min2 then
+        p2=0
+    end
+    
+    if us1>=math.max(s1/2, min1) then
+        p1=0
+    elseif us1>=min1+just_some_pixel_count then
+        p1=1
+    elseif us1>=min1 then
+        p1=2
+    elseif us1>0 then
+        p1=3
+    end
+    
+    return p1+p2
+end
+
+function private.fitlevel(frame, node, horiz)
+    local fg=frame:geom()
+    local fsh=frame:size_hints()
+    local sg=node:geom()
+    if not horiz then
+        return private.fitlevel_(fsh.min_h, fg.h, sg.h, fsh.min_w, fg.w, sg.w)
+    else
+        return private.fitlevel_(fsh.min_w, fg.w, sg.w, fsh.min_h, fg.h, sg.h)
+    end
+end
+
+
+function private.use_unused(p, n, d, forcelevel)
+    local f=private.fitlevel(p.frame, n, (d=="left" or d=="right"))
+    if f>forcelevel then
+        return false, f
+    end
+
+    if d=="single" then
+        p.res_node=n
+        p.res_config={reg=p.frame}
+        return true, f
+    end
+
+    -- TODO: Check fit
+    local sg=n:geom()
+    local fg=p.frame:geom()
+    
+    if d=="up" or d=="down" then
+        p.res_node=n
+        local fh=math.min(fg.h, sg.h)
+        if d=="up" then
+            p.res_config=private.split2("vertical", sg.h, nil, fh,
+                                  {}, {reg=p.frame})
+        else
+            p.res_config=private.split2("vertical", sg.h, fh, nil,
+                                  {reg=p.frame}, {})
+        end
+        return true, f
+    elseif d=="left" or d=="right" then
+        p.res_node=n
+        local fw=math.min(fg.w, sg.w)
+        if d=="left" then
+            p.res_config=private.split2("horizontal", sg.w, nil, fw,
+                                  {}, {reg=p.frame})
+        else
+            p.res_config=private.split2("horizontal", sg.w, fw, nil,
+                                  {reg=p.frame}, {})
+        end
+        return true, f
+    end
+    
+    return false, f
+end
+
+
+function private.scan_pane(p, node, d)
+    local function do_scan_active(n, uf)
+        local t=obj_typename(n)
+        if t=="WSplitRegion" then
+            local f=private.fitlevel(p.frame, n, (d=="left" or d=="right"))
+            if f<uf then
+                p.res_node=n
+                return true
+            end
+        elseif t=="WSplitSplit" or t=="WSplitFloat" then
+            local a, b=n:tl(), n:br()
+            if b==n:current() then
+                a, b=b, a
+            end
+            return (do_scan_active(a, uf) or do_scan_active(b, uf))
+        end
+        return false
+    end
+
+    local function do_scan_unused(n, forcelevel)
+        local t=obj_typename(n)
+        if t=="WSplitSplit" or t=="WSplitFloat" then
+            local sd=n:dir()
+            local a, b
+            if (d=="up" and sd=="vertical") or (d=="left" and sd=="horizontal") then
+                a, b=n:tl(), n:br()
+            elseif (d=="down" and sd=="vertical") or (d=="right" and sd=="horizontal") then
+                a, b=n:br(), n:tl()
+            else
+                a, b=n:current(), (n:current()==n:tl() and n:br() or n:tl())
+            end
+
+            local ok, f, f2
+            ok, f=do_scan_unused(a, forcelevel)
+            if not ok then
+                ok, f2=do_scan_unused(b, forcelevel)
+            end
+            return ok, math.min(f, f2 or max_penalty)
+        elseif t=="WSplitUnused" then
+            -- Found it
+            return private.use_unused(p, n, d, forcelevel)
+        end
+        return false, max_penalty
+    end
+    
+    local ok, fitlevel=do_scan_unused(node, 0)
+    if not ok then
+        ok=do_scan_active(node, fitlevel)
+        if not ok then
+            ok=do_scan_unused(node, 3)
+        end
+    end
+    return ok
+end
+    
+
+function private.make_placement(p)
+    if p.specifier then
+        local n=p.specifier
+        local pcls, pdir
+        
+        while n and not obj_is(n, "WSplitPane") do
+            n=n:parent()
+        end
+        
+        if n then
+            pcls, pdir=sfind((n:marker() or ""), "(.*):(.*)")
+        end
+            
+        return private.use_unused(p, p.specifier, (pdir or "single"), 2)
+    end
+    
+    local cls=private.classify(p.ws, p.reg)
+    
+    local function do_scan_cls(n)
+        local t=obj_typename(n)
+        if t=="WSplitPane" then
+            local m=n:marker()
+            if m then
+                local pcls, pdir=sfind(m, "(.*):(.*)")
+                if pcls and pcls==cls then
+                    return private.scan_pane(p, n:contents(), pdir)
+                end
+            else
+                return do_scan_cls(n:contents())
+            end
+        elseif t=="WSplitSplit" or t=="WSplitFloat" then
+            return (do_scan_cls(n:tl()) or do_scan_cls(n:br()))
+        end
+    end
+    
+    local function do_scan_fallback(n)
+        local t=obj_typename(n)
+        if t=="WSplitUnused" then
+            p.res_node=n
+            p.res_config={reg=p.frame}
+            return true
+        elseif t=="WSplitRegion" then
+            p.res_node=n
+            return true
+        elseif t=="WSplitPane" then
+            return do_scan_fallback(n:contents())
+        elseif t=="WSplitSplit" or t=="WSplitFloat" then
+            return (do_scan_fallback(n:tl()) or do_scan_fallback(n:br()))
+        end
+    end
+    
+    local node=p.ws:split_tree()
+    return (do_scan_cls(node) or do_scan_fallback(node))
+end
+
+-- }}}
+
+
+-- Layout initialisation {{{
+
+
+function private.calc_sizes(tmpl, sw, sh)
+    tmpl._w, tmpl._h=sw, sh
+
+    if tmpl.type=="WSplitSplit" or tmpl.type=="WSplitFloat" then
+        local tmps, tlw, tlh, brw, brh
+        -- Calculate pixel sizes of things
+        if tmpl.dir=="vertical" then
+            tmps=sh
+        else
+            tmps=sw
+        end
+        
+        tmpl.tls, tmpl.brs=private.div_length(tmps, tmpl.tls, tmpl.brs)
+    
+        if tmpl.dir=="vertical" then
+            tlw, brw=sw, sw
+            tlh, brh=tmpl.tls, tmpl.brs
+        else
+            tlw, brw=tmpl.tls, tmpl.brs
+            tlh, brh=sh, sh
+        end
+    
+        private.calc_sizes(tmpl.tl, tlw, tlh)
+        private.calc_sizes(tmpl.br, brw, brh)
+    end
+end
+
+
+function private.init_layout(p)
+    p.layout=table.copy(settings.template, true) -- deep copy template
+    local wg=p.ws:geom()
+    private.calc_sizes(p.layout, wg.w, wg.h)
+    return true
+end
+
+-- }}}
+
+
+-- Initialisation {{{
+
+function private.setup_hooks()
+    local function hookto(hkname, fn)
+        local hk=ioncore.get_hook(hkname)
+        if not hk then
+            error("No hook "..hkname)
+        end
+        if not hk:add(fn) then
+            error("Unable to hook to "..hkname)
+        end
+    end
+
+    hookto("panews_init_layout_alt", private.init_layout)
+    hookto("panews_make_placement_alt", private.make_placement)
+end
+
+
+private.setup_hooks()
+
+-- }}}
+
+
+-- Mark ourselves loaded.
+package.loaded["templates"]=true
+
+
+-- Load configuration file
+dopath('cfg_panews', false)
+
diff --git a/mod_panews/panews.c b/mod_panews/panews.c
new file mode 100644 (file)
index 0000000..70f83fe
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * ion/mod_panews/panews.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <libextl/extl.h>
+#include <libmainloop/defer.h>
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/region.h>
+#include <ioncore/focus.h>
+#include <ioncore/manage.h>
+#include <ioncore/saveload.h>
+#include <ioncore/attach.h>
+#include <ioncore/regbind.h>
+#include <ioncore/extlconv.h>
+#include <ioncore/frame.h>
+#include <mod_tiling/tiling.h>
+#include <mod_tiling/split.h>
+#include "panews.h"
+#include "placement.h"
+#include "main.h"
+#include "splitext.h"
+
+
+/*{{{ Create/destroy */
+
+
+bool panews_managed_add(WPaneWS *ws, WRegion *reg)
+{
+    if(OBJ_IS(reg, WFrame))
+        region_add_bindmap(reg, mod_panews_frame_bindmap);
+    
+    return tiling_managed_add_default(&(ws->tiling), reg);
+}
+
+
+static WRegion *create_frame_panews(WWindow *parent, const WFitParams *fp)
+{
+    return (WRegion*)create_frame(parent, fp, "frame-tiled-panews");
+}
+
+
+static bool mrsh_init_layout_extl(ExtlFn fn, WPaneWSInitParams *p)
+{
+    ExtlTab t=extl_create_table();
+    bool ret=FALSE;
+    
+    extl_table_sets_o(t, "ws", (Obj*)p->ws);
+
+    extl_protect(NULL);
+    ret=extl_call(fn, "t", "b", t, &ret);
+    extl_unprotect(NULL);
+    
+    if(ret)
+        ret=extl_table_gets_t(t, "layout", &(p->layout));
+        
+    extl_unref_table(t);
+    return ret;
+}
+
+
+static bool panews_init_layout(WPaneWS *ws)
+{
+    WPaneWSInitParams p;
+    
+    p.ws=ws;
+    p.layout=extl_table_none();
+       
+    hook_call_p(panews_init_layout_alt, &p,
+                (WHookMarshallExtl*)mrsh_init_layout_extl);
+
+    if(p.layout!=extl_table_none()){            
+        ws->tiling.split_tree=tiling_load_node(&(ws->tiling),
+                                             &REGION_GEOM(ws), 
+                                             p.layout);
+        extl_unref_table(p.layout);
+    }
+         
+    if(ws->tiling.split_tree==NULL)
+        ws->tiling.split_tree=(WSplit*)create_splitunused(&REGION_GEOM(ws), ws);
+        
+    if(ws->tiling.split_tree!=NULL)
+        ws->tiling.split_tree->ws_if_root=&(ws->tiling);
+    
+    return (ws->tiling.split_tree!=NULL);
+}
+
+
+bool panews_init(WPaneWS *ws, WWindow *parent, const WFitParams *fp, 
+                 bool ilo)
+{
+    if(!tiling_init(&(ws->tiling), parent, fp, 
+                   create_frame_panews, FALSE))
+        return FALSE;
+    
+    region_add_bindmap((WRegion*)ws, mod_panews_panews_bindmap);
+
+    assert(ws->tiling.split_tree==NULL);
+    
+    if(ilo){
+        if(!panews_init_layout(ws)){
+            panews_deinit(ws);
+            return FALSE;
+        }
+    }
+    
+    return TRUE;
+}
+
+
+WPaneWS *create_panews(WWindow *parent, const WFitParams *fp, bool cu)
+{
+    CREATEOBJ_IMPL(WPaneWS, panews, (p, parent, fp, cu));
+}
+
+
+WPaneWS *create_panews_simple(WWindow *parent, const WFitParams *fp)
+{
+    return create_panews(parent, fp, TRUE);
+}
+
+
+void panews_deinit(WPaneWS *ws)
+{
+    tiling_deinit(&(ws->tiling));
+}
+
+
+static WSplitRegion *get_node_check(WPaneWS *ws, WRegion *reg)
+{
+    WSplitRegion *node;
+
+    if(reg==NULL)
+        return NULL;
+    
+    node=splittree_node_of(reg);
+    
+    if(node==NULL || REGION_MANAGER(reg)!=(WRegion*)ws)
+        return NULL;
+    
+    return node;
+}
+
+
+static void panews_do_managed_remove(WPaneWS *ws, WRegion *reg)
+{
+    tiling_do_managed_remove(&(ws->tiling), reg);
+    if(OBJ_IS(reg, WFrame))
+        region_remove_bindmap(reg, mod_panews_frame_bindmap);
+}
+
+
+static bool plainregionfilter(WSplit *node)
+{
+    return (strcmp(OBJ_TYPESTR(node), "WSplitRegion")==0);
+}
+
+
+
+void panews_managed_remove(WPaneWS *ws, WRegion *reg)
+{
+    bool ds=OBJ_IS_BEING_DESTROYED(ws);
+    bool act=REGION_IS_ACTIVE(reg);
+    bool mcf=region_may_control_focus((WRegion*)ws);
+    WSplitRegion *node=get_node_check(ws, reg);
+    WRegion *other=NULL;
+
+    other=tiling_do_get_nextto(&(ws->tiling), reg, SPLIT_ANY, PRIMN_ANY, FALSE);
+    
+    panews_do_managed_remove(ws, reg);
+
+    if(node==(WSplitRegion*)(ws->tiling.stdispnode))
+        ws->tiling.stdispnode=NULL;
+    
+    if(node==NULL)
+        return;
+    
+    splittree_remove((WSplit*)node, !ds);
+    
+    if(!ds){
+        if(other==NULL){
+            if(ws->tiling.split_tree==NULL){
+                warn(TR("Unable to re-initialise workspace. Destroying."));
+                mainloop_defer_destroy((Obj*)ws);
+            }else if(act && mcf){
+                /* We don't want to give the stdisp focus, even if one exists. 
+                 * Or do we?
+                 */
+                tiling_fallback_focus(&ws->tiling, FALSE);
+            }
+        }else if(act && mcf){
+            region_warp(other);
+        }
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc. */
+
+
+bool panews_managed_prepare_focus(WPaneWS *ws, WRegion *reg,
+                                  int flags, WPrepareFocusResult *res)
+{
+    if(flags&REGION_GOTO_ENTERWINDOW){
+        WSplitRegion *other, *node=get_node_check(ws, reg);
+        if(node!=NULL && OBJ_IS(node, WSplitUnused)){
+            /* An unused region - do not focus unless there are no
+             * normal regions in its pane. 
+             */
+            other=split_tree_find_region_in_pane_of((WSplit*)node);
+            if(other!=NULL){
+                tiling_managed_prepare_focus(&(ws->tiling), other->reg, 
+                                            flags&~REGION_GOTO_ENTERWINDOW, 
+                                            res);
+                return FALSE;
+            }
+        }
+    }
+        
+    return tiling_managed_prepare_focus(&(ws->tiling), reg, flags, res);
+}
+
+
+static bool filter_no_stdisp_unused(WSplit *split)
+{
+    return (OBJ_IS(split, WSplitRegion)
+            && !OBJ_IS(split, WSplitST)
+            && !OBJ_IS(split, WSplitUnused));
+}
+
+
+bool panews_managed_may_destroy(WPaneWS *ws, WRegion *reg)
+{
+    if(region_manager_allows_destroying((WRegion*)ws))
+        return TRUE;
+    
+    if(tiling_do_get_nextto(&(ws->tiling), reg, 
+                           SPLIT_ANY, PRIMN_ANY, FALSE)==NULL){
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+bool panews_may_destroy(WPaneWS *ws)
+{
+    if(split_current_todir(ws->tiling.split_tree, SPLIT_ANY, PRIMN_ANY, 
+                           filter_no_stdisp_unused)!=NULL){
+        warn(TR("Refusing to close non-empty workspace."));
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+/*
+static WRegion *panews_rqclose_propagate(WPaneWS *ws, WRegion *sub)
+{
+    WSplitRegion *node=NULL;
+    WRegion *reg=NULL;
+    
+    if(sub==NULL){
+        if(ws->tiling.split_tree!=NULL){
+            node=(WSplitRegion*)split_current_todir(ws->tiling.split_tree, 
+                                                    SPLIT_ANY, PRIMN_ANY, 
+                                                    filter_no_stdisp_unused);
+        }
+        if(node==NULL){
+            mainloop_defer_destroy((Obj*)ws);
+            return (WRegion*)ws;
+        }
+        if(node->reg==NULL)
+            return NULL;
+        sub=node->reg;
+    }else{
+        node=get_node_check(ws, sub);
+        if(node==NULL)
+            return NULL;
+    }
+    
+    
+    return (region_rqclose(sub) ? sub : NULL);
+}
+*/
+
+
+/*}}}*/
+
+
+/*{{{ Save */
+
+
+ExtlTab panews_get_configuration(WPaneWS *ws)
+{
+    return tiling_get_configuration(&(ws->tiling));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Load */
+
+
+static WSplit *load_splitunused(WPaneWS *ws, const WRectangle *geom, 
+                                ExtlTab tab)
+{
+    return (WSplit*)create_splitunused(geom, (WPaneWS*)ws);
+}
+
+
+static WSplit *load_splitpane(WPaneWS *ws, const WRectangle *geom, ExtlTab tab)
+{
+    ExtlTab t;
+    WSplitPane *pane;
+    WSplit *cnt;
+
+    pane=create_splitpane(geom, NULL);
+    if(pane==NULL)
+        return NULL;
+    
+    if(extl_table_gets_t(tab, "contents", &t)){
+        cnt=tiling_load_node(&(ws->tiling), geom, t);
+        extl_unref_table(t);
+    }else{
+        cnt=load_splitunused(ws, geom, extl_table_none());
+    }
+    
+    if(cnt==NULL){
+        destroy_obj((Obj*)pane);
+        return NULL;
+    }
+    
+    pane->contents=cnt;
+    cnt->parent=&(pane->isplit);
+    
+    assert(pane->marker==NULL);
+    extl_table_gets_s(tab, "marker", &(pane->marker));
+
+    return (WSplit*)pane;
+}
+
+
+static WSplit *panews_load_node(WPaneWS *ws, const WRectangle *geom, 
+                                ExtlTab tab)
+{
+    char *s=NULL;
+    
+    if(!extl_table_gets_s(tab, "type", &s)){
+        WRegion *reg=NULL;
+        /* Shortcuts for templates.lua */
+        if(extl_table_gets_o(tab, "reg", (Obj**)&reg)){
+            if(OBJ_IS(reg, WRegion))
+                return load_splitregion_doit(&(ws->tiling), geom, tab);
+        }else{
+            return load_splitunused(ws, geom, tab);
+        }
+    }else{
+        if(strcmp(s, "WSplitPane")==0)
+            return load_splitpane(ws, geom, tab);
+        else if(strcmp(s, "WSplitUnused")==0)
+            return load_splitunused(ws, geom, tab);
+    }
+
+    return tiling_load_node_default(&(ws->tiling), geom, tab);
+}
+
+
+WRegion *panews_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
+{
+    WPaneWS *ws;
+    ExtlTab treetab;
+
+    ws=create_panews(par, fp, FALSE);
+    
+    if(ws==NULL)
+        return NULL;
+    if(extl_table_gets_t(tab, "split_tree", &treetab)){
+        ws->tiling.split_tree=tiling_load_node(&(ws->tiling), &REGION_GEOM(ws), 
+                                             treetab);
+        extl_unref_table(treetab);
+    }
+    
+    if(ws->tiling.split_tree==NULL){
+        if(!panews_init_layout(ws)){
+            destroy_obj((Obj*)ws);
+            return NULL;
+        }
+    }
+    
+    ws->tiling.split_tree->ws_if_root=ws;
+    split_restack(ws->tiling.split_tree, ws->tiling.dummywin, Above);
+    
+    return (WRegion*)ws;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab panews_dynfuntab[]={
+    {region_managed_remove, 
+     panews_managed_remove},
+
+    {(DynFun*)region_prepare_manage, 
+     (DynFun*)panews_prepare_manage},
+    
+    {(DynFun*)region_get_configuration,
+     (DynFun*)panews_get_configuration},
+
+    {(DynFun*)region_may_destroy,
+     (DynFun*)panews_may_destroy},
+
+    {(DynFun*)region_managed_may_destroy,
+     (DynFun*)panews_managed_may_destroy},
+
+    {(DynFun*)tiling_managed_add,
+     (DynFun*)panews_managed_add},
+    
+    {(DynFun*)tiling_load_node,
+     (DynFun*)panews_load_node},
+
+    {(DynFun*)tiling_do_get_nextto,
+     (DynFun*)panews_do_get_nextto},
+
+    {(DynFun*)tiling_do_get_farthest,
+     (DynFun*)panews_do_get_farthest},
+
+    {(DynFun*)region_managed_prepare_focus,
+     (DynFun*)panews_managed_prepare_focus},
+
+    /*
+    {(DynFun*)region_rqclose_propagate,
+     (DynFun*)panews_rqclose_propagate},*/
+    
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WPaneWS, WTiling, panews_deinit, panews_dynfuntab);
+
+    
+/*}}}*/
+
diff --git a/mod_panews/panews.h b/mod_panews/panews.h
new file mode 100644 (file)
index 0000000..1402523
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * ion/panews/panews.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_PANEWS_PANEWS_H
+#define ION_PANEWS_PANEWS_H
+
+#include <ioncore/common.h>
+#include <ioncore/region.h>
+#include <ioncore/screen.h>
+#include <libextl/extl.h>
+#include <ioncore/rectangle.h>
+
+#include <mod_tiling/tiling.h>
+
+
+INTRCLASS(WPaneWS);
+DECLCLASS(WPaneWS){
+    WTiling tiling;
+};
+
+
+extern void panews_deinit(WPaneWS *ws);
+extern bool panews_init(WPaneWS *ws, WWindow *parent, const WFitParams *fp,
+                        bool cu);
+extern WPaneWS *create_panews(WWindow *parent, const WFitParams *fp, bool cu);
+extern WPaneWS *create_panews_simple(WWindow *parent, const WFitParams *fp);
+extern WRegion *panews_load(WWindow *par, const WFitParams *fp, ExtlTab tab);
+
+/* Dynfun implementations */
+
+extern bool panews_managed_may_destroy(WPaneWS *ws, WRegion *reg);
+extern void panews_managed_remove(WPaneWS *ws, WRegion *reg);
+extern bool panews_managed_add(WPaneWS *ws, WRegion *reg);
+extern WRegion *panews_managed_control_focus(WPaneWS *ws, WRegion *reg);
+
+extern ExtlTab panews_get_configuration(WPaneWS *ws);
+extern WRegion *panews_load(WWindow *par, const WFitParams *fp, ExtlTab tab);
+
+#endif /* ION_PANEWS_PANEWS_H */
diff --git a/mod_panews/placement.c b/mod_panews/placement.c
new file mode 100644 (file)
index 0000000..73e2690
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * ion/mod_panews/placement.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <limits.h>
+#include <math.h>
+#include <string.h>
+
+#include <libtu/minmax.h>
+#include <libtu/objp.h>
+#include <libextl/extl.h>
+#include <libmainloop/defer.h>
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/clientwin.h>
+#include <ioncore/attach.h>
+#include <ioncore/manage.h>
+#include <ioncore/framep.h>
+#include <ioncore/names.h>
+#include <ioncore/resize.h>
+#include <mod_tiling/split.h>
+#include <mod_tiling/split-stdisp.h>
+#include "placement.h"
+#include "panews.h"
+#include "splitext.h"
+#include "unusedwin.h"
+
+
+WHook *panews_init_layout_alt=NULL;
+WHook *panews_make_placement_alt=NULL;
+
+
+/*{{{ create_frame_for */
+
+
+static WFrame *create_frame_for(WPaneWS *ws, WRegion *reg)
+{
+    WWindow *par=REGION_PARENT(ws);
+    WFitParams fp;
+    WRectangle mg;
+    WFrame *frame;
+    
+    if(par==NULL)
+        return NULL;
+    
+    fp.g=REGION_GEOM(ws);
+    fp.mode=REGION_FIT_BOUNDS;
+    
+    frame=(WFrame*)ws->tiling.create_frame_fn(par, &fp);
+    
+    if(frame==NULL)
+        return NULL;
+
+    frame->flags|=FRAME_DEST_EMPTY;
+    
+    mplex_managed_geom((WMPlex*)frame, &mg);
+    
+    fp.g.w=REGION_GEOM(reg).w+(REGION_GEOM(frame).w-mg.w);
+    fp.g.h=REGION_GEOM(reg).h+(REGION_GEOM(frame).h-mg.h);
+    fp.mode=REGION_FIT_EXACT;
+    
+    region_fitrep((WRegion*)frame, NULL, &fp);
+    
+    return (WFrame*)frame;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Placement scan */
+
+
+static bool mrsh_layout_extl(ExtlFn fn, WPaneWSPlacementParams *p)
+{
+    ExtlTab t=extl_create_table();
+    bool ret=FALSE;
+    
+    extl_table_sets_o(t, "ws", (Obj*)p->ws);
+    extl_table_sets_o(t, "frame", (Obj*)p->frame);
+    extl_table_sets_o(t, "reg", (Obj*)p->reg);
+    extl_table_sets_o(t, "specifier", (Obj*)p->specifier);
+
+    extl_protect(NULL);
+    extl_call(fn, "t", "b", t, &ret);
+    extl_unprotect(NULL);
+    
+    if(ret){
+        ret=FALSE;
+
+        extl_table_gets_i(t, "res_w", &(p->res_w));
+        extl_table_gets_i(t, "res_h", &(p->res_h));
+        
+        if(extl_table_gets_o(t, "res_node", (Obj**)&(p->res_node))){
+            if(OBJ_IS(p->res_node, WSplitUnused)){
+                if(!extl_table_gets_t(t, "res_config", &(p->res_config))){
+                    warn(TR("Malfunctioning placement hook; condition #%d."), 1);
+                    goto err;
+                }
+            }else if(!OBJ_IS(p->res_node, WSplitRegion)){
+                warn(TR("Malfunctioning placement hook; condition #%d."), 2);
+                goto err;
+            }
+        }
+    }
+    
+    extl_unref_table(t);
+    
+    return ret;
+    
+err:    
+    p->res_node=NULL;
+    extl_unref_table(t);
+    return FALSE;
+}
+
+
+static bool plainregionfilter(WSplit *node)
+{
+    return (strcmp(OBJ_TYPESTR(node), "WSplitRegion")==0);
+}
+
+
+static bool fallback_filter(WSplit *node)
+{
+    return (OBJ_IS(node, WSplitUnused) || plainregionfilter(node));
+}
+
+
+static bool fallback_layout(WPaneWSPlacementParams *p)
+{
+    if(p->ws->tiling.split_tree==NULL)
+        return FALSE;
+    
+    if(p->specifier!=NULL){
+        p->res_node=(WSplit*)p->specifier;
+    }else{
+        p->res_node=split_current_todir(p->ws->tiling.split_tree, SPLIT_ANY,
+                                        PRIMN_ANY, fallback_filter);
+    }
+
+    if(p->res_node!=NULL && OBJ_IS(p->res_node, WSplitUnused)){
+        p->res_config=extl_create_table();
+        if(p->res_config==extl_table_none() || p->frame==NULL)
+            return FALSE;
+        extl_table_sets_o(p->res_config, "reg", (Obj*)(p->frame));
+    }
+    
+    return (p->res_node!=NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Split/replace unused code */
+
+
+static bool do_replace(WPaneWS *ws, WFrame *frame, WRegion *reg, 
+                       WPaneWSPlacementParams *rs)
+{
+    WSplit *u=rs->res_node;
+    WSplit *node=tiling_load_node(&(ws->tiling), &(u->geom), rs->res_config);
+    
+    assert(OBJ_IS(u, WSplitUnused));
+    
+    if(node==NULL){
+        warn(TR("Malfunctioning placement hook; condition #%d."), 3);
+        return FALSE;
+    }
+
+    if(REGION_MANAGER(frame)!=(WRegion*)ws){
+        warn(TR("Malfunctioning placement hook; condition #%d."), 4);
+        destroy_obj((Obj*)node);
+        return FALSE;
+    }
+    
+    if(u->parent!=NULL)
+        splitinner_replace(u->parent, u, node);
+    else
+        splittree_changeroot((WSplit*)u, node);
+    
+    u->parent=NULL;
+    mainloop_defer_destroy((Obj*)u);
+    
+    if(ws->tiling.stdispnode!=NULL)
+        split_regularise_stdisp(ws->tiling.stdispnode);
+
+    if(ws->tiling.split_tree!=NULL)
+        split_restack(ws->tiling.split_tree, ws->tiling.dummywin, Above);
+
+    return TRUE;
+}
+
+/*}}}*/
+
+
+/*{{{ The main dynfun */
+
+
+static bool current_unused(WPaneWS *ws)
+{
+    return OBJ_IS(tiling_current(&ws->tiling), WUnusedWin);
+}
+
+
+static WRegion *panews_get_target(WPaneWS *ws, WSplitUnused *specifier,
+                                  WRegion *reg)
+{
+    WRegion *target=NULL;
+    WFrame *frame=create_frame_for(ws, reg);
+    WSplit **tree=&(ws->tiling.split_tree);
+    WPaneWSPlacementParams rs;
+    
+    assert(ws->tiling.split_tree!=NULL);
+
+    rs.ws=ws;
+    rs.frame=frame;
+    rs.reg=reg;
+    rs.specifier=specifier;
+    rs.res_node=NULL;
+    rs.res_config=extl_table_none();
+    rs.res_w=-1;
+    rs.res_h=-1;
+    
+    if(frame!=NULL){
+        split_update_bounds(*tree, TRUE);
+        
+        assert(panews_make_placement_alt!=NULL);
+        
+        hook_call_p(panews_make_placement_alt, &rs,
+                    (WHookMarshallExtl*)mrsh_layout_extl);
+    }
+        
+    if(rs.res_node==NULL && specifier==NULL)
+        fallback_layout(&rs);
+        
+    if(rs.res_node!=NULL){
+        /* Resize */
+        if(rs.res_w>0 || rs.res_h>0){
+            WRectangle grq=rs.res_node->geom;
+            int gflags=REGION_RQGEOM_WEAK_ALL;
+            
+            if(rs.res_w>0){
+                grq.w=rs.res_w;
+                gflags&=~REGION_RQGEOM_WEAK_W;
+            }
+            
+            if(rs.res_h>0){
+                grq.h=rs.res_h;
+                gflags&=~REGION_RQGEOM_WEAK_H;
+            }
+            
+            splittree_rqgeom(rs.res_node, gflags, &grq, NULL);
+        }
+        
+        if(OBJ_IS(rs.res_node, WSplitUnused)){
+            if(frame!=NULL){
+                if(do_replace(ws, frame, reg, &rs))
+                    target=(WRegion*)frame;
+            }
+        }else{
+            assert(OBJ_IS(rs.res_node, WSplitRegion));
+            target=((WSplitRegion*)rs.res_node)->reg;
+        }
+        
+        extl_unref_table(rs.res_config);
+    }
+        
+    if(frame!=NULL && target!=(WRegion*)frame)
+        destroy_obj((Obj*)frame);
+    
+    if(target!=NULL && current_unused(ws))
+        region_goto(target);
+    
+    return target;
+}
+
+
+WPHolder *panews_prepare_manage(WPaneWS *ws, const WClientWin *cwin,
+                                const WManageParams *param, int redir)
+{
+    WRegion *target=panews_get_target(ws, NULL, (WRegion*)cwin);
+    WPHolder *ph;
+    
+    if(target!=NULL){
+        ph=region_prepare_manage(target, cwin, param, MANAGE_REDIR_PREFER_YES);
+        if(ph!=NULL)
+            return ph;
+    }
+
+    warn(TR("Ooops... could not find a region to attach client window to "
+            "on workspace %s."), region_name((WRegion*)ws));
+    return NULL;
+}
+
+
+bool panews_handle_unused_drop(WPaneWS *ws, WSplitUnused *specifier, 
+                               WRegion *reg)
+{
+    WRegion *target=panews_get_target(ws, specifier, reg);
+    
+    if(target==NULL || !OBJ_IS(target, WMPlex))
+        return FALSE;
+    
+    return (mplex_attach_simple((WMPlex*)target, reg, 
+                                MPLEX_ATTACH_SWITCHTO)!=NULL);
+}
+
+
+/*}}}*/
+
diff --git a/mod_panews/placement.h b/mod_panews/placement.h
new file mode 100644 (file)
index 0000000..23dd9fe
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * ion/mod_panews/placement.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_PANEWS_PLACEMENT_H
+#define ION_MOD_PANEWS_PLACEMENT_H
+
+#include <libextl/extl.h>
+#include <libmainloop/hooks.h>
+#include <ioncore/common.h>
+#include <ioncore/clientwin.h>
+#include <ioncore/manage.h>
+#include <mod_tiling/split.h>
+#include "panews.h"
+#include "splitext.h"
+
+
+typedef struct{
+    WPaneWS *ws;
+    ExtlTab layout;
+} WPaneWSInitParams;
+
+
+typedef struct{
+    WPaneWS *ws;
+    WFrame *frame;
+    WRegion *reg;
+    WSplitUnused *specifier;
+    
+    WSplit *res_node;
+    ExtlTab res_config;
+    int res_w, res_h;
+} WPaneWSPlacementParams;
+
+
+/* Handlers of this hook should accept WPaneWSInitParams as parameter. */
+extern WHook *panews_init_layout_alt;
+/* Handlers of this hook should accept WPaneWSPlacementParams as parameter. */
+extern WHook *panews_make_placement_alt;
+
+
+extern WPHolder *panews_prepare_manage(WPaneWS *ws, const WClientWin *cwin,
+                                       const WManageParams *param,
+                                       int redir);
+extern bool panews_handle_unused_drop(WPaneWS *ws, WSplitUnused *specifier, 
+                                      WRegion *reg);
+
+#endif /* ION_MOD_PANEWS_PLACEMENT_H */
diff --git a/mod_panews/splitext.c b/mod_panews/splitext.c
new file mode 100644 (file)
index 0000000..ad392c5
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * ion/panews/splitext.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <limits.h>
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/rootwin.h>
+#include <ioncore/xwindow.h>
+#include <ioncore/window.h>
+#include <mod_tiling/split.h>
+#include "splitext.h"
+#include "unusedwin.h"
+
+
+#define GEOM(X) (((WSplit*)(X))->geom)
+
+
+/*{{{ Init/deinit */
+
+
+bool splitunused_init(WSplitUnused *split, const WRectangle *geom,
+                      WPaneWS *ws)
+{
+    WWindow *par=REGION_PARENT(ws);
+    WUnusedWin *uwin;
+    WFitParams fp;
+
+    assert(par!=NULL);
+    
+    fp.g=*geom;
+    fp.mode=REGION_FIT_EXACT;
+    
+    uwin=create_unusedwin(par, &fp);
+    
+    if(uwin==NULL)
+        return FALSE;
+    
+    if(!splitregion_init(&(split->regnode), geom, (WRegion*)uwin)){
+        destroy_obj((Obj*)uwin);
+        return FALSE;
+    }
+    
+    if(!tiling_managed_add(&ws->tiling, (WRegion*)uwin)){
+        split->regnode.reg=NULL;
+        destroy_obj((Obj*)uwin);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+WSplitUnused *create_splitunused(const WRectangle *geom, WPaneWS *ws)
+{
+    CREATEOBJ_IMPL(WSplitUnused, splitunused, (p, geom, ws));
+}
+
+
+bool splitpane_init(WSplitPane *pane, const WRectangle *geom, WSplit *cnt)
+{
+    pane->contents=cnt;
+    pane->marker=NULL;
+    
+    if(!splitinner_init(&(pane->isplit), geom))
+        return FALSE;
+
+    return TRUE;
+}
+
+
+WSplitPane *create_splitpane(const WRectangle *geom, WSplit *cnt)
+{
+    CREATEOBJ_IMPL(WSplitPane, splitpane, (p, geom, cnt));
+}
+
+
+void splitunused_deinit(WSplitUnused *split)
+{
+    if(split->regnode.reg!=NULL){
+        destroy_obj((Obj*)split->regnode.reg);
+        split->regnode.reg=NULL;
+    }
+    
+    splitregion_deinit(&(split->regnode));
+}
+
+
+void splitpane_deinit(WSplitPane *split)
+{
+    if(split->contents!=NULL){
+        WSplit *tmp=split->contents;
+        split->contents=NULL;
+        tmp->parent=NULL;
+        destroy_obj((Obj*)tmp);
+    }
+    splitinner_deinit(&(split->isplit));
+}
+
+
+/*}}}*/
+
+
+/*{{{ X window handling */
+
+
+static void splitpane_stacking(WSplitPane *pane, 
+                               Window *bottomret, Window *topret)
+{
+
+    *bottomret=None;
+    *topret=None;
+    
+    if(pane->contents!=NULL)
+        split_stacking(pane->contents, bottomret, topret);
+}
+
+
+static void splitpane_restack(WSplitPane *pane, Window other, int mode)
+{
+    if(pane->contents!=None)
+        split_restack(pane->contents, other, mode);
+}
+
+
+static void stack_restack_reg(WRegion *reg, Window *other, int *mode)
+{
+    Window b=None, t=None;
+    
+    if(reg!=NULL){
+        region_restack(reg, *other, *mode);
+        region_stacking(reg, &b, &t);
+        if(t!=None){
+            *other=t;
+            *mode=Above;
+        }
+    }
+}
+
+
+static void stack_restack_split(WSplit *split, Window *other, int *mode)
+{
+    Window b=None, t=None;
+    
+    if(split!=NULL){
+        split_restack(split, *other, *mode);
+        split_stacking(split, &b, &t);
+        if(t!=None){
+            *other=t;
+            *mode=Above;
+        }
+    }
+}
+
+
+static void splitpane_reparent(WSplitPane *pane, WWindow *target)
+{
+    if(pane->contents!=NULL)
+        split_reparent(pane->contents, target);
+}
+
+
+static void reparentreg(WRegion *reg, WWindow *target)
+{
+    WRectangle g=REGION_GEOM(reg);
+    region_reparent(reg, target, &g, REGION_FIT_EXACT);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Geometry */
+
+
+static void set_unused_bounds(WSplit *node)
+{
+    node->min_w=0;
+    node->min_h=0;
+    node->max_w=INT_MAX;
+    node->max_h=INT_MAX;
+    node->unused_w=node->geom.w;
+    node->unused_h=node->geom.h;
+}
+
+
+static void copy_bounds(WSplit *dst, const WSplit *src)
+{
+    dst->min_w=src->min_w;
+    dst->min_h=src->min_h;
+    dst->max_w=src->max_w;
+    dst->max_h=src->max_h;
+    dst->unused_w=src->unused_w;
+    dst->unused_h=src->unused_h;
+}
+
+
+static void splitunused_update_bounds(WSplitUnused *node, bool recursive)
+{
+    set_unused_bounds((WSplit*)node);
+}
+
+
+static void splitpane_update_bounds(WSplitPane *node, bool recursive)
+{
+    if(node->contents!=NULL){
+        if(recursive)
+            split_update_bounds(node->contents, recursive);
+        copy_bounds((WSplit*)node, node->contents);
+    }else{
+        set_unused_bounds((WSplit*)node);
+    }
+}
+
+
+static int infadd(int x, int y)
+{
+    return ((x==INT_MAX || y==INT_MAX) ? INT_MAX : (x+y));
+}
+
+
+static void splitpane_do_resize(WSplitPane *pane, const WRectangle *ng, 
+                                int hprimn, int vprimn, bool transpose)
+{
+    if(transpose && pane->marker!=NULL){
+        char *growdir=strchr(pane->marker, ':');
+        if(growdir!=NULL){
+            const char *newdir=NULL;
+            growdir++;
+            
+            if(strcmp(growdir, "right")==0)
+                newdir="down";
+            else if(strcmp(growdir, "left")==0)
+                newdir="up";
+            if(strcmp(growdir, "down")==0)
+                newdir="right";
+            else if(strcmp(growdir, "up")==0)
+                newdir="left";
+            
+            if(newdir!=NULL){
+                char *newmarker=NULL;
+                *growdir='\0';
+                libtu_asprintf(&newmarker, "%s:%s", pane->marker, newdir);
+                if(newmarker==NULL){
+                    *growdir=':';
+                }else{
+                    free(pane->marker);
+                    pane->marker=newmarker;
+                }
+            }
+        }
+        
+    }
+    
+    ((WSplit*)pane)->geom=*ng;
+    
+    if(pane->contents!=NULL)
+        split_do_resize(pane->contents, ng, hprimn, vprimn, transpose);
+}
+
+
+static void splitpane_do_rqsize(WSplitPane *pane, WSplit *node, 
+                                RootwardAmount *ha, RootwardAmount *va, 
+                                WRectangle *rg, bool tryonly)
+{
+    WSplitInner *par=((WSplit*)pane)->parent;
+    
+    if(par!=NULL){
+        splitinner_do_rqsize(par, (WSplit*)pane, ha, va, rg, tryonly);
+        if(!tryonly)
+            ((WSplit*)pane)->geom=*rg;
+    }else{
+        *rg=GEOM(pane);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Tree manipulation */
+
+
+static void splitpane_replace(WSplitPane *pane, WSplit *child, WSplit *what)
+{
+    assert(child==pane->contents && what!=NULL);
+    
+    child->parent=NULL;
+    pane->contents=what;
+    what->parent=(WSplitInner*)pane;
+    what->ws_if_root=NULL; /* May not be needed */
+}
+
+
+static WPaneWS *find_ws(WSplit *split)
+{
+    if(split->parent!=NULL)
+        return find_ws((WSplit*)split->parent);
+    
+    if(split->ws_if_root!=NULL)
+        return OBJ_CAST(split->ws_if_root, WPaneWS);
+    
+    return NULL;
+}
+
+
+static void splitpane_remove(WSplitPane *pane, WSplit *child, 
+                             bool reclaim_space)
+{
+    WSplitInner *parent=((WSplit*)pane)->parent;
+    WSplitUnused *un;
+    WPaneWS *ws=find_ws((WSplit*)pane);
+    
+    assert(child==pane->contents);
+    
+    pane->contents=NULL;
+    child->parent=NULL;
+
+    if(ws!=NULL
+       && !OBJ_IS_BEING_DESTROYED(ws)
+       && !OBJ_IS_BEING_DESTROYED(pane)){
+        pane->contents=(WSplit*)create_splitunused(&GEOM(pane), ws);
+        if(pane->contents!=NULL){
+            pane->contents->parent=(WSplitInner*)pane;
+            return;
+        }
+    }
+    
+    if(parent!=NULL)
+        splitinner_remove(parent, (WSplit*)pane, reclaim_space);
+    else
+        splittree_changeroot((WSplit*)pane, NULL);
+    
+    destroy_obj((Obj*)pane);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Tree traversal */
+
+
+static bool filter_any(WSplit *split)
+{
+    return OBJ_IS(split, WSplitRegion);
+}
+
+
+static bool filter_no_unused(WSplit *split)
+{
+    return (OBJ_IS(split, WSplitRegion)
+            && !OBJ_IS(split, WSplitUnused));
+}
+
+
+static bool filter_no_stdisp(WSplit *split)
+{
+    return (OBJ_IS(split, WSplitRegion)
+            && !OBJ_IS(split, WSplitST));
+}
+
+
+static bool filter_no_stdisp_unused(WSplit *split)
+{
+    return (OBJ_IS(split, WSplitRegion)
+            && !OBJ_IS(split, WSplitST)
+            && !OBJ_IS(split, WSplitUnused));
+            
+}
+
+
+static WSplit *splitpane_current_todir(WSplitPane *pane, int dir, int primn,
+                                       WSplitFilter *filter)
+{
+    WSplit *ret=NULL;
+    
+    if(pane->contents==NULL)
+        return NULL;
+    
+    /* Try non-unused first */
+    if(filter==filter_no_stdisp){
+        ret=split_current_todir(pane->contents, dir, primn, 
+                                filter_no_stdisp_unused);
+    }else if(filter==filter_any){
+        ret=split_current_todir(pane->contents, dir, primn, 
+                                filter_no_unused);
+    }
+    
+    if(ret==NULL)
+        ret=split_current_todir(pane->contents, dir, primn, filter);
+    
+    return ret;
+}
+
+
+static void splitpane_forall(WSplitPane *pane, WSplitFn *fn)
+{
+    if(pane->contents!=NULL)
+        fn(pane->contents);
+}
+
+
+static WSplit *splitpane_current(WSplitPane *pane)
+{
+    return pane->contents;
+}
+
+
+static WSplitRegion *get_node_check(WPaneWS *ws, WRegion *reg)
+{
+    WSplitRegion *node;
+
+    if(reg==NULL)
+        return NULL;
+    
+    node=splittree_node_of(reg);
+    
+    if(node==NULL || REGION_MANAGER(reg)!=(WRegion*)ws)
+        return NULL;
+    
+    return node;
+}
+
+
+static WSplitRegion *do_get_nextto(WSplit *node, int dir, int primn, 
+                                   bool any, bool paneonly)
+{
+    WSplitFilter *filter=(any ? filter_no_unused : filter_no_stdisp_unused);
+    WSplit *nextto=NULL;
+
+    while(node->parent!=NULL){
+        if(OBJ_IS(node, WSplitPane)){
+            if(paneonly)
+                break;
+            filter=(any ? filter_any : filter_no_stdisp);
+        }
+        nextto=splitinner_nextto(node->parent, node, dir, primn, filter);
+        if(nextto!=NULL)
+            break;
+        node=(WSplit*)(node->parent);
+    }
+    
+    if(OBJ_IS(nextto, WSplitRegion))
+        return (WSplitRegion*)nextto;
+    return NULL;
+}
+
+
+WRegion *panews_do_get_nextto(WPaneWS *ws, WRegion *reg,
+                              int dir, int primn, bool any)
+{
+    WSplitRegion *node=get_node_check(ws, reg), *nextto=NULL;
+
+    if(node==NULL)
+        return NULL;
+    
+    nextto=do_get_nextto((WSplit*)node, dir, primn, TRUE, FALSE);
+    
+    if(nextto!=NULL)
+        return nextto->reg;
+
+    return NULL;
+}
+
+WRegion *panews_do_get_farthest(WPaneWS *ws,
+                                int dir, int primn, bool any)
+{
+    WSplitFilter *filter=(any ? filter_any : filter_no_stdisp);
+    WSplit *node=NULL;
+    if(ws->tiling.split_tree!=NULL)
+        node=split_current_todir(ws->tiling.split_tree, dir, primn, filter);
+    if(node!=NULL && OBJ_IS(node, WSplitRegion))
+        return ((WSplitRegion*)node)->reg;
+    return NULL;
+}
+
+
+WSplitRegion *split_tree_find_region_in_pane_of(WSplit *node)
+{
+    return do_get_nextto(node, SPLIT_ANY, PRIMN_ANY, FALSE, TRUE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Markers and other exports */
+
+
+/*EXTL_DOC
+ * Get marker.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+const char *splitpane_marker(WSplitPane *pane)
+{
+    return pane->marker;
+}
+
+
+/*EXTL_DOC
+ * Set marker.
+ */
+EXTL_EXPORT_MEMBER
+bool splitpane_set_marker(WSplitPane *pane, const char *s)
+{
+    char *s2=NULL;
+    
+    if(s!=NULL){
+        s2=scopy(s);
+        if(s2==NULL)
+            return FALSE;
+    }
+    
+    if(pane->marker==NULL)
+        free(pane->marker);
+    
+    pane->marker=s2;
+    
+    return TRUE;
+}
+
+
+/*EXTL_DOC
+ * Get root of contained sub-split tree.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WSplit *splitpane_contents(WSplitPane *pane)
+{
+    return pane->contents;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Save support */
+
+
+static bool splitunused_get_config(WSplitUnused *node, ExtlTab *ret)
+{
+    *ret=split_base_config((WSplit*)node);
+    return TRUE;
+}
+
+
+static bool splitpane_get_config(WSplitPane *pane, ExtlTab *ret)
+{
+    *ret=split_base_config((WSplit*)pane);
+    
+    if(pane->contents!=NULL){
+        ExtlTab t;
+        if(!split_get_config(pane->contents, &t)){
+            extl_unref_table(*ret);
+            return FALSE;
+        }
+        extl_table_sets_t(*ret, "contents", t);
+        extl_unref_table(t);
+    }
+    
+    extl_table_sets_s(*ret, "marker", pane->marker);
+
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ The classes */
+
+
+static DynFunTab splitunused_dynfuntab[]={
+    {split_update_bounds, splitunused_update_bounds},
+    {(DynFun*)split_get_config, (DynFun*)splitunused_get_config},
+    END_DYNFUNTAB,
+};
+
+
+static DynFunTab splitpane_dynfuntab[]={
+    {split_update_bounds, splitpane_update_bounds},
+    {split_do_resize, splitpane_do_resize},
+    {splitinner_do_rqsize, splitpane_do_rqsize},
+    {splitinner_replace, splitpane_replace},
+    {splitinner_remove, splitpane_remove},
+    {(DynFun*)split_current_todir, (DynFun*)splitpane_current_todir},
+    {(DynFun*)splitinner_current, (DynFun*)splitpane_current},
+    {(DynFun*)split_get_config, (DynFun*)splitpane_get_config},
+    {splitinner_forall, splitpane_forall},
+    {split_stacking, splitpane_stacking},
+    {split_restack, splitpane_restack},
+    {split_reparent, splitpane_reparent},
+    END_DYNFUNTAB,
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WSplitUnused, WSplitRegion, splitunused_deinit, splitunused_dynfuntab);
+
+EXTL_EXPORT
+IMPLCLASS(WSplitPane, WSplitInner, splitpane_deinit, splitpane_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/mod_panews/splitext.h b/mod_panews/splitext.h
new file mode 100644 (file)
index 0000000..0bc73e3
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * ion/panews/splitext.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_PANEWS_SPLITEXT_H
+#define ION_PANEWS_SPLITEXT_H
+
+#include <ioncore/common.h>
+#include <mod_tiling/split.h>
+
+INTRCLASS(WSplitUnused);
+INTRCLASS(WSplitPane);
+
+#include "panews.h"
+
+DECLCLASS(WSplitUnused){
+    WSplitRegion regnode;
+};
+
+DECLCLASS(WSplitPane){
+    WSplitInner isplit;
+    WSplit *contents;
+    char *marker;
+};
+
+extern bool splitunused_init(WSplitUnused *split, const WRectangle *geom,
+                             WPaneWS *ws);
+extern bool splitpane_init(WSplitPane *split, const WRectangle *geom,
+                           WSplit *cnt);
+
+extern WSplitUnused *create_splitunused(const WRectangle *geom,
+                                        WPaneWS *ws);
+extern WSplitPane *create_splitpane(const WRectangle *geom, WSplit *cnt);
+
+extern void splitunused_deinit(WSplitUnused *split);
+extern void splitpane_deinit(WSplitPane *split);
+
+extern const char *splitpane_get_marker(WSplitPane *pane);
+extern bool splitpane_set_marker(WSplitPane *pane, const char *s);
+
+extern WRegion *panews_do_get_nextto(WPaneWS *ws, WRegion *reg,
+                                     int dir, int primn, bool any);
+extern WRegion *panews_do_get_farthest(WPaneWS *ws,
+                                       int dir, int primn, bool any);
+
+extern WSplitRegion *split_tree_find_region_in_pane_of(WSplit *node);
+
+#endif /* ION_PANEWS_SPLITEXT_H */
diff --git a/mod_panews/unusedwin.c b/mod_panews/unusedwin.c
new file mode 100644 (file)
index 0000000..7e030f0
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * ion/panews/unusedwin.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/event.h>
+#include <ioncore/gr.h>
+#include <ioncore/regbind.h>
+#include <ioncore/framep.h>
+#include <ioncore/presize.h>
+#include <ioncore/frame-pointer.h>
+#include "unusedwin.h"
+#include "splitext.h"
+#include "placement.h"
+#include "main.h"
+
+
+/*{{{ Init/deinit */
+
+
+static void unusedwin_getbrush(WUnusedWin *uwin)
+{
+    GrBrush *brush=gr_get_brush(uwin->wwin.win,
+                                region_rootwin_of((WRegion*)uwin),
+                                "frame-tiled-panews-unused");
+
+    if(brush!=NULL){
+        if(uwin->brush!=NULL)
+            grbrush_release(uwin->brush);
+        
+        uwin->brush=brush;
+        
+        grbrush_enable_transparency(brush, GR_TRANSPARENCY_YES);
+    }
+}
+
+
+bool unusedwin_init(WUnusedWin *uwin, WWindow *parent, const WFitParams *fp)
+{
+    uwin->brush=NULL;
+    
+    if(!window_init(&(uwin->wwin), parent, fp))
+        return FALSE;
+    
+    unusedwin_getbrush(uwin);
+    
+    region_add_bindmap((WRegion*)uwin, mod_panews_unusedwin_bindmap);
+
+    window_select_input(&(uwin->wwin), IONCORE_EVENTMASK_NORMAL);
+
+    ((WRegion*)uwin)->flags|=REGION_PLEASE_WARP;
+    
+    return TRUE;
+}
+
+
+WUnusedWin *create_unusedwin(WWindow *parent, const WFitParams *fp)
+{
+    CREATEOBJ_IMPL(WUnusedWin, unusedwin, (p, parent, fp));
+}
+
+
+void unusedwin_deinit(WUnusedWin *uwin)
+{
+    if(uwin->brush!=NULL){
+        grbrush_release(uwin->brush);
+        uwin->brush=NULL;
+    }
+    
+    window_deinit(&(uwin->wwin));
+}
+
+
+/*}}}*/
+
+
+/*{{{ unusedwin_press */
+
+
+static void unusedwin_border_inner_geom(const WUnusedWin *uwin, 
+                                        WRectangle *geom)
+{
+    GrBorderWidths bdw;
+    
+    geom->x=0;
+    geom->y=0;
+    geom->w=REGION_GEOM(uwin).w;
+    geom->h=REGION_GEOM(uwin).h;
+    
+    if(uwin->brush!=NULL){
+        grbrush_get_border_widths(uwin->brush, &bdw);
+
+        geom->x+=bdw.left;
+        geom->y+=bdw.top;
+        geom->w-=bdw.left+bdw.right;
+        geom->h-=bdw.top+bdw.bottom;
+    }
+    
+    geom->w=maxof(geom->w, 0);
+    geom->h=maxof(geom->h, 0);
+}
+
+
+static int unusedwin_press(WUnusedWin *uwin, XButtonEvent *ev, 
+                           WRegion **reg_ret)
+{
+    WRectangle g;
+    
+    *reg_ret=NULL;
+    
+    window_p_resize_prepare(&uwin->wwin, ev);
+    
+    /* Check border */
+    
+    unusedwin_border_inner_geom(uwin, &g);
+    
+    if(rectangle_contains(&g, ev->x, ev->y))
+        return FRAME_AREA_CLIENT;
+    
+    return FRAME_AREA_BORDER;
+}
+
+
+/*}}}*/
+
+
+/*{{{ unusedwin_handle_drop */
+
+
+static bool unusedwin_handle_drop(WUnusedWin *uwin, int x, int y,
+                                  WRegion *dropped)
+{
+    WSplitUnused *us=OBJ_CAST(splittree_node_of((WRegion*)uwin),
+                              WSplitUnused);
+    WPaneWS *ws=REGION_MANAGER_CHK(uwin, WPaneWS);
+    
+    if(us==NULL || ws==NULL)
+        return FALSE;
+    
+    return panews_handle_unused_drop(ws, us, dropped);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Drawing */
+
+
+static void unusedwin_updategr(WUnusedWin *uwin)
+{
+    unusedwin_getbrush(uwin);
+    region_updategr_default((WRegion*)uwin);
+}
+
+
+static void unusedwin_draw(WUnusedWin *uwin, bool complete)
+{
+    WRectangle g;
+    const char *substyle=(REGION_IS_ACTIVE(uwin)
+                          ? "active"
+                          : "inactive");
+    
+    if(uwin->brush==NULL)
+        return;
+    
+    g.x=0;
+    g.y=0;
+    g.w=REGION_GEOM(uwin).w;
+    g.h=REGION_GEOM(uwin).h;
+    
+    grbrush_begin(uwin->brush, &g, (complete ? 0 : GRBRUSH_NO_CLEAR_OK));
+    
+    grbrush_draw_border(uwin->brush, &g, substyle);
+    
+    grbrush_end(uwin->brush);
+}
+
+
+/*}}}*/
+
+
+/*{{{ The class */
+
+
+static DynFunTab unusedwin_dynfuntab[]={
+    {region_updategr, unusedwin_updategr},
+    {window_draw, unusedwin_draw},
+    {(DynFun*)window_press, (DynFun*)unusedwin_press},
+    {(DynFun*)region_handle_drop, (DynFun*)unusedwin_handle_drop},
+    END_DYNFUNTAB,
+};
+
+
+IMPLCLASS(WUnusedWin, WWindow, unusedwin_deinit, unusedwin_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/mod_panews/unusedwin.h b/mod_panews/unusedwin.h
new file mode 100644 (file)
index 0000000..bcdc5ba
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * ion/panews/unusedwin.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_PANEWS_UNUSEDWIN_H
+#define ION_PANEWS_UNUSEDWIN_H
+
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+
+INTRCLASS(WUnusedWin);
+
+DECLCLASS(WUnusedWin){
+    WWindow wwin;
+    GrBrush *brush;
+};
+
+extern bool unusedwin_init(WUnusedWin *pwin, 
+                           WWindow *parent, const WFitParams *fp);
+extern WUnusedWin *create_unusedwin(WWindow *parent, const WFitParams *fp);
+
+#endif /* ION_PANEWS_UNUSEDWIN_H */
diff --git a/mod_query/Makefile b/mod_query/Makefile
new file mode 100644 (file)
index 0000000..8008dea
--- /dev/null
@@ -0,0 +1,29 @@
+##
+## Query module Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES=complete.c edln.c input.c listing.c main.c wedln.c \
+        wedln-wrappers.c wmessage.c query.c fwarn.c history.c
+
+MAKE_EXPORTS=mod_query
+
+MODULE=mod_query
+MODULE_STUB=mod_query.lua
+LUA_SOURCES=mod_query_chdir.lua
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: module_install lc_install
diff --git a/mod_query/complete.c b/mod_query/complete.c
new file mode 100644 (file)
index 0000000..0bc2df3
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * ion/mod_query/complete.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#include <libtu/objp.h>
+#include <ioncore/common.h>
+#include "complete.h"
+#include "edln.h"
+#include "wedln.h"
+
+
+/*{{{ Completion list processing */
+
+
+static int str_common_part_l(const char *p1, const char *p2)
+{
+    int i=0;
+    
+    while(1){
+        if(*p1=='\0' || *p1!=*p2)
+            break;
+        p1++; p2++; i++;
+    }
+    
+    return i;
+}
+
+
+/* Get length of part common to all completions
+ * and remove duplicates
+ */
+static int get_common_part_rmdup(char **completions, int *ncomp)
+{
+    int i, j, c=INT_MAX, c2;
+    
+    for(i=0, j=1; j<*ncomp; j++){
+        c2=str_common_part_l(completions[i], completions[j]);
+        if(c2<c)
+            c=c2;
+
+        if(completions[i][c2]=='\0' && completions[j][c2]=='\0'){
+            free(completions[j]);
+            completions[j]=NULL;
+            continue;
+        }
+        i++;
+        if(i!=j){
+            completions[i]=completions[j];
+            completions[j]=NULL;
+        }
+    }
+    *ncomp=i+1;
+    
+    return c;
+}
+
+
+static int compare(const void *p1, const void *p2)
+{
+    const char **v1, **v2;
+    
+    v1=(const char **)p1;
+    v2=(const char **)p2;
+    return strcoll(*v1, *v2);
+}
+
+
+static void edln_reset(Edln *edln)
+{
+    assert(edln->palloced>=1);
+    
+    edln->p[0]='\0';
+    edln->psize=0;
+    edln->point=0;
+    edln->mark=-1;
+    edln->histent=-1;
+}
+
+
+static void edln_do_set_completion(Edln *edln, const char *comp, int len,
+                                   const char *beg, const char *end)
+{
+    edln_reset(edln);
+    
+    if(beg!=NULL)
+        edln_insstr_n(edln, beg, strlen(beg), FALSE, TRUE);
+    
+    if(len>0)
+        edln_insstr_n(edln, comp, len, FALSE, TRUE);
+        
+    if(end!=NULL)
+        edln_insstr_n(edln, end, strlen(end), FALSE, FALSE);
+    
+    if(edln->ui_update!=NULL){
+        edln->ui_update(edln->uiptr, 0,
+                        EDLN_UPDATE_MOVED|EDLN_UPDATE_CHANGED|
+                        EDLN_UPDATE_NEW);
+    }
+
+}
+
+
+void edln_set_completion(Edln *edln, const char *comp, 
+                         const char *beg, const char *end)
+{
+    edln_do_set_completion(edln, comp, strlen(comp), beg, end);
+}
+
+
+int edln_do_completions(Edln *edln, char **completions, int ncomp,
+                        const char *beg, const char *end, bool setcommon,
+                        bool nosort)
+{
+    int len;
+    int i;
+    
+    if(ncomp==0){
+        return 0;
+    }else if(ncomp==1){
+        len=strlen(completions[0]);
+    }else{
+        if(!nosort)
+            qsort(completions, ncomp, sizeof(char**), compare);
+        len=get_common_part_rmdup(completions, &ncomp);
+    }
+    
+    if(setcommon)
+        edln_do_set_completion(edln, completions[0], len, beg, end);
+    
+    return ncomp;
+}
+
+
+/*}}}*/
+
+
+/*{{{ WComplProxy */
+
+
+bool complproxy_init(WComplProxy *proxy, WEdln *wedln, int id, int cycle)
+{
+    watch_init(&(proxy->wedln_watch));
+    if(!watch_setup(&(proxy->wedln_watch), (Obj*)wedln, NULL))
+        return FALSE;
+    
+    proxy->id=id;
+    proxy->cycle=cycle;
+    
+    return TRUE;
+}
+
+
+WComplProxy *create_complproxy(WEdln *wedln, int id, int cycle)
+{
+    CREATEOBJ_IMPL(WComplProxy, complproxy, (p, wedln, id, cycle));
+}
+
+
+void complproxy_deinit(WComplProxy *proxy)
+{
+    watch_reset(&(proxy->wedln_watch));
+}
+
+
+/*EXTL_DOC
+ * Set completion list of the \type{WEdln} that \var{proxy} refers to to
+ * \var{compls}, if it is still waiting for this completion run. The 
+ * numerical indexes of \var{compls} list the found completions. If the
+ * entry \var{common_beg} (\var{common_end}) exists, it gives an extra 
+ * common prefix (suffix) of all found completions.
+ */
+EXTL_EXPORT_MEMBER
+bool complproxy_set_completions(WComplProxy *proxy, ExtlTab compls)
+{
+    WEdln *wedln=(WEdln*)proxy->wedln_watch.obj;
+    
+    if(wedln!=NULL){
+        if(wedln->compl_waiting_id==proxy->id){
+            wedln_set_completions(wedln, compls, proxy->cycle);
+            wedln->compl_current_id=proxy->id;
+            return TRUE;
+        }
+    }
+    
+    return FALSE;
+}
+
+
+EXTL_EXPORT
+IMPLCLASS(WComplProxy, Obj, complproxy_deinit, NULL);
+
+
+/*}}}*/
+
diff --git a/mod_query/complete.h b/mod_query/complete.h
new file mode 100644 (file)
index 0000000..f7aa910
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * ion/mod_query/complete.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_COMPLETE_H
+#define ION_MOD_QUERY_COMPLETE_H
+
+#include <libtu/obj.h>
+#include <libextl/extl.h>
+#include <ioncore/common.h>
+#include "edln.h"
+#include "wedln.h"
+
+INTRCLASS(WComplProxy);
+
+DECLCLASS(WComplProxy){
+    Obj o;
+    Watch wedln_watch;
+    int id;
+    int cycle;
+};
+
+
+extern WComplProxy *create_complproxy(WEdln *wedln, int id, int cycle);
+
+extern bool complproxy_set_completions(WComplProxy *proxy, ExtlTab compls);
+
+
+extern int edln_do_completions(Edln *edln, char **completions, int ncomp,
+                               const char *beg, const char *end,
+                               bool setcommon, bool nosort);
+extern void edln_set_completion(Edln *edln, const char *comp, 
+                                const char *beg, const char *end);
+
+#endif /* ION_MOD_QUERY_COMPLETE_H */
diff --git a/mod_query/edln.c b/mod_query/edln.c
new file mode 100644 (file)
index 0000000..99283f4
--- /dev/null
@@ -0,0 +1,779 @@
+/*
+ * ion/mod_query/edln.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/minmax.h>
+#include <ioncore/common.h>
+#include <ioncore/selection.h>
+#include <ioncore/strings.h>
+
+#include "edln.h"
+#include "wedln.h"
+#include "history.h"
+
+#define EDLN_ALLOCUNIT 16
+
+#define UPDATE(X) \
+    edln->ui_update(edln->uiptr, X, 0)
+
+#define UPDATE_MOVED(X) \
+    edln->ui_update(edln->uiptr, X, EDLN_UPDATE_MOVED)
+
+#define UPDATE_CHANGED(X)                                  \
+    edln->ui_update(edln->uiptr, X,                        \
+                    EDLN_UPDATE_MOVED|EDLN_UPDATE_CHANGED)
+
+#define UPDATE_CHANGED_NOMOVE(X)         \
+    edln->ui_update(edln->uiptr, X,      \
+                    EDLN_UPDATE_CHANGED)
+
+#define UPDATE_NEW()                                                       \
+    edln->ui_update(edln->uiptr, 0,                                        \
+                    EDLN_UPDATE_NEW|EDLN_UPDATE_MOVED|EDLN_UPDATE_CHANGED)
+
+#define CHAR wchar_t
+#define ISALNUM iswalnum
+#define CHAR_AT(P, N) str_wchar_at(P, N)
+
+
+/*{{{ Alloc */
+
+
+static bool edln_pspc(Edln *edln, int n)
+{
+    char *np;
+    int pa;
+    
+    if(edln->palloced<edln->psize+1+n){
+        pa=edln->palloced+n;
+        pa|=(EDLN_ALLOCUNIT-1);
+        np=ALLOC_N(char, pa);
+        
+        if(np==NULL)
+            return FALSE;
+
+        memmove(np, edln->p, edln->point*sizeof(char));
+        memmove(np+edln->point+n, edln->p+edln->point,
+                (edln->psize-edln->point+1)*sizeof(char));
+        free(edln->p);
+        edln->p=np;
+        edln->palloced=pa;
+    }else{
+        memmove(edln->p+edln->point+n, edln->p+edln->point,
+                (edln->psize-edln->point+1)*sizeof(char));
+    }
+
+    if(edln->mark>edln->point)
+        edln->mark+=n;
+    
+    edln->psize+=n;
+
+    edln->modified=1;
+    return TRUE;
+}
+
+
+static bool edln_rspc(Edln *edln, int n)
+{
+    char *np;
+    int pa;
+    
+    if(n+edln->point>=edln->psize)
+        n=edln->psize-edln->point;
+    
+    if(n==0)
+        return TRUE;
+    
+    if((edln->psize+1-n)<(edln->palloced&~(EDLN_ALLOCUNIT-1))){
+        pa=edln->palloced&~(EDLN_ALLOCUNIT-1);
+        np=ALLOC_N(char, pa);
+        
+        if(np==NULL)
+            goto norm;
+        
+        memmove(np, edln->p, edln->point*sizeof(char));
+        memmove(np+edln->point, edln->p+edln->point+n,
+                (edln->psize-edln->point+1-n)*sizeof(char));
+        free(edln->p);
+        edln->p=np;
+        edln->palloced=pa;
+    }else{
+    norm:
+        memmove(edln->p+edln->point, edln->p+edln->point+n,
+                (edln->psize-edln->point+1-n)*sizeof(char));
+    }
+    edln->psize-=n;
+
+    if(edln->mark>edln->point)
+        edln->mark-=n;
+    
+    edln->modified=1;
+    return TRUE;
+}
+
+
+static void edln_clearstr(Edln *edln)
+{
+    if(edln->p!=NULL){
+        free(edln->p);
+        edln->p=NULL;
+    }
+    edln->palloced=0;
+    edln->psize=0;
+}
+
+
+static bool edln_initstr(Edln *edln, const char *p)
+{
+    int l=strlen(p), al;
+    
+    al=(l+1)|(EDLN_ALLOCUNIT-1);
+    
+    edln->p=ALLOC_N(char, al);
+    
+    if(edln->p==NULL)
+        return FALSE;
+
+    edln->palloced=al;
+    edln->psize=l;
+    strcpy(edln->p, p);
+    
+    return TRUE;
+}
+
+
+static bool edln_setstr(Edln *edln, const char *p)
+{
+    edln_clearstr(edln);
+    return edln_initstr(edln, p);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Insert */
+
+
+bool edln_insstr(Edln *edln, const char *str)
+{
+    int l;
+    
+    if(str==NULL)
+        return FALSE;
+    
+    l=strlen(str);
+    
+    return edln_insstr_n(edln, str, l, TRUE, TRUE);
+}
+
+
+bool edln_insstr_n(Edln *edln, const char *str, int l, 
+                   bool update, bool movepoint)
+{
+    if(!edln_pspc(edln, l))
+        return FALSE;
+    
+    memmove(&(edln->p[edln->point]), str, l);
+    if(movepoint){
+        edln->point+=l;
+        if(update)
+            UPDATE_CHANGED(edln->point-l);
+    }else{
+        if(update)
+            UPDATE_CHANGED_NOMOVE(edln->point-l);
+    }
+
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Transpose */
+
+
+bool edln_transpose_chars(Edln *edln)
+{
+    int off1, off2, pos;
+    char *buf;
+
+    if((edln->point==0) || (edln->psize<2))
+        return FALSE;
+
+    pos=edln->point;
+    if(edln->point==edln->psize)
+        pos=pos-str_prevoff(edln->p, edln->point);
+
+    off1=str_nextoff(edln->p, pos);
+    off2=str_prevoff(edln->p, pos);
+
+    buf=ALLOC_N(char, off2);
+    if(buf==NULL)
+        return FALSE;
+    memmove(buf, &(edln->p[pos-off2]), off2);
+    memmove(&(edln->p[pos-off2]), &(edln->p[pos]), off1);
+    memmove(&(edln->p[pos-off2+off1]), buf, off2);
+    FREE(buf);
+
+    if(edln->point!=edln->psize)
+        edln->point+=off1;
+
+    UPDATE_CHANGED(0);
+    return TRUE;
+}
+
+
+bool edln_transpose_words(Edln *edln)
+{
+    int m1, m2, m3, m4, off1, off2, oldp;
+    char *buf;
+
+    if((edln->point==edln->psize) || (edln->psize<3))
+        return FALSE;
+
+    oldp=edln->point;
+    edln_bskip_word(edln);
+    m1=edln->point;
+    edln_skip_word(edln);
+    m2=edln->point;
+    edln_skip_word(edln);
+    if(edln->point==m2)
+        goto noact;
+    m4=edln->point;
+    edln_bskip_word(edln);
+    if(edln->point==m1)
+        goto noact;
+    m3=edln->point;
+
+    off1=m4-m3;
+    off2=m3-m2;
+
+    buf=ALLOC_N(char, m4-m1);
+    if(buf==NULL)
+        goto noact;
+    memmove(buf, &(edln->p[m3]), off1);
+    memmove(buf+off1, &(edln->p[m2]), off2);
+    memmove(buf+off1+off2, &(edln->p[m1]), m2-m1);
+    memmove(&(edln->p[m1]), buf, m4-m1);
+    FREE(buf);
+
+    edln->point=m4;
+    UPDATE_CHANGED(0);
+    return TRUE;
+
+noact:
+    edln->point=oldp;
+    UPDATE_MOVED(edln->point);
+    return FALSE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Movement */
+
+
+static int do_edln_back(Edln *edln)
+{
+    int l=str_prevoff(edln->p, edln->point);
+    edln->point-=l;
+    return l;
+}
+
+
+void edln_back(Edln *edln)
+{
+    int p=edln->point;
+    do_edln_back(edln);
+    /*if(edln->point!=p)*/
+        UPDATE_MOVED(edln->point);
+}
+
+
+static int do_edln_forward(Edln *edln)
+{
+    int l=str_nextoff(edln->p, edln->point);
+    edln->point+=l;
+    return l;
+}
+
+
+void edln_forward(Edln *edln)
+{
+    int p=edln->point;
+    do_edln_forward(edln);
+    /*if(edln->point!=p)*/
+        UPDATE_MOVED(p);
+}
+
+
+void edln_bol(Edln *edln)
+{
+    if(edln->point!=0){
+        edln->point=0;
+        UPDATE_MOVED(0);
+    }
+}
+
+
+void edln_eol(Edln *edln)
+{
+    int o=edln->point;
+    
+    if(edln->point!=edln->psize){
+        edln->point=edln->psize;
+        UPDATE_MOVED(o);
+    }
+}
+
+
+void edln_bskip_word(Edln *edln)
+{
+    int p, n;
+    CHAR c;
+    
+    while(edln->point>0){
+        n=do_edln_back(edln);
+        c=CHAR_AT(edln->p+edln->point, n);
+        if(ISALNUM(c))
+            goto fnd;
+    }
+    UPDATE_MOVED(edln->point);
+    return;
+    
+fnd:
+    while(edln->point>0){
+        p=edln->point;
+        n=do_edln_back(edln);
+        c=CHAR_AT(edln->p+edln->point, n);
+
+        if(!ISALNUM(c)){
+            edln->point=p;
+            break;
+        }
+    }
+    UPDATE_MOVED(edln->point);
+}
+
+
+void edln_skip_word(Edln *edln)
+{
+    int oldp=edln->point;
+    CHAR c;
+    
+    while(edln->point<edln->psize){
+        c=CHAR_AT(edln->p+edln->point, edln->psize-edln->point);
+        if(ISALNUM(c))
+            goto fnd;
+        if(do_edln_forward(edln)==0)
+            break;
+    }
+    UPDATE_MOVED(oldp);
+    return;
+    
+fnd:
+    while(edln->point<edln->psize){
+        c=CHAR_AT(edln->p+edln->point, edln->psize-edln->point);
+        if(!ISALNUM(c))
+            break;
+        if(do_edln_forward(edln)==0)
+            break;
+    }
+    UPDATE_MOVED(oldp);
+}
+
+
+void edln_set_point(Edln *edln, int point)
+{
+    int o=edln->point;
+    
+    if(point<0)
+        point=0;
+    else if(point>edln->psize)
+        point=edln->psize;
+    
+    edln->point=point;
+    
+    if(o<point)
+        UPDATE_MOVED(o);
+    else
+        UPDATE_MOVED(point);
+}
+
+               
+/*}}}*/
+
+
+/*{{{ Delete */
+
+
+void edln_delete(Edln *edln)
+{
+    int left=edln->psize-edln->point;
+    size_t l;
+    
+    if(left<=0)
+        return;
+    
+    l=str_nextoff(edln->p, edln->point);
+    
+    if(l>0)
+        edln_rspc(edln, l);
+    
+    UPDATE_CHANGED_NOMOVE(edln->point);
+}
+
+
+void edln_backspace(Edln *edln)
+{
+    int n;
+    if(edln->point==0)
+        return;
+    n=do_edln_back(edln);
+    if(n!=0){
+        edln_rspc(edln, n);
+        UPDATE_CHANGED(edln->point);
+    }
+}
+
+void edln_kill_to_eol(Edln *edln)
+{
+    edln_rspc(edln, edln->psize-edln->point);
+    UPDATE_CHANGED_NOMOVE(edln->point);
+}
+
+
+void edln_kill_to_bol(Edln *edln)
+{
+    int p=edln->point;
+    
+    edln_bol(edln);
+    edln_rspc(edln, p);
+    edln->point=0;
+    UPDATE_CHANGED(0);
+}
+
+
+void edln_kill_line(Edln *edln)
+{
+    edln_bol(edln);
+    edln_kill_to_eol(edln);
+    UPDATE_CHANGED(0);
+}
+
+
+void edln_kill_word(Edln *edln)
+{
+    int oldp=edln->point;
+    int l;
+    edln_skip_word(edln);
+    
+    if(edln->point==oldp)
+        return;
+    
+    l=edln->point-oldp;
+    edln->point=oldp;
+    edln_rspc(edln, l);
+    
+    UPDATE_CHANGED_NOMOVE(oldp);
+}
+
+
+void edln_bkill_word(Edln *edln)
+{
+    int oldp=edln->point;
+    
+    edln_bskip_word(edln);
+    
+    if(edln->point==oldp)
+        return;
+    
+    edln_rspc(edln, oldp-edln->point);
+    UPDATE_CHANGED(edln->point);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Selection */
+
+
+static void do_set_mark(Edln *edln, int nm)
+{
+    int m=edln->mark;
+    edln->mark=nm;
+    if(m!=-1)
+        UPDATE(m < edln->point ? m : edln->point);
+}
+
+
+void edln_set_mark(Edln *edln)
+{
+    do_set_mark(edln, edln->point);
+}
+
+
+void edln_clear_mark(Edln *edln)
+{
+    do_set_mark(edln, -1);
+}
+
+
+static void edln_do_copy(Edln *edln, bool del)
+{
+    int beg, end;
+    
+    if(edln->mark<0 || edln->point==edln->mark)
+        return;
+    
+    if(edln->point<edln->mark){
+        beg=edln->point;
+        end=edln->mark;
+    }else{
+        beg=edln->mark;
+        end=edln->point;
+    }
+    
+    ioncore_set_selection_n(edln->p+beg, end-beg);
+    
+    if(del){
+        edln->point=beg;
+        edln_rspc(edln, end-beg);
+    }
+    edln->mark=-1;
+    
+    UPDATE(beg);
+}
+
+
+void edln_cut(Edln *edln)
+{
+    edln_do_copy(edln, TRUE);
+}
+
+
+void edln_copy(Edln *edln)
+{
+    edln_do_copy(edln, FALSE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ History */
+
+
+bool edln_set_context(Edln *edln, const char *str)
+{
+    char *s=scat(str, ":"), *cp;
+    
+    if(s==NULL)
+        return FALSE;
+    
+    cp=strchr(s, ':');
+    while(cp!=NULL && *(cp+1)!='\0'){
+        *cp='_';
+        cp=strchr(cp, ':');
+    }
+
+    if(edln->context!=NULL)
+        free(edln->context);
+    edln->context=s;
+        
+    return TRUE;
+}
+
+
+static void edln_do_set_hist(Edln *edln, int e, bool match)
+{
+    const char *str=mod_query_history_get(e), *s2;
+    if(str!=NULL){
+        if(edln->histent<0){
+            edln->tmp_p=edln->p;
+            edln->tmp_palloced=edln->palloced;
+            edln->p=NULL;
+        }
+        
+        /* Skip context label */
+        s2=strchr(str, ':');
+        if(s2!=NULL)
+            str=s2+1;
+        
+        edln->histent=e;
+        edln_setstr(edln, str);
+        edln->point=(match
+                     ? minof(edln->point, edln->psize) 
+                     : edln->psize);
+        edln->mark=-1;
+        edln->modified=FALSE;
+        UPDATE_NEW();
+    }
+}
+
+
+static char *history_search_str(Edln *edln)
+{
+    char *sstr;
+    char tmp=edln->p[edln->point];
+    edln->p[edln->point]='\0';
+    sstr=scat(edln->context ? edln->context : "*:", edln->p);
+    edln->p[edln->point]=tmp;
+    return sstr;
+}
+
+
+static int search(Edln *edln, int from, bool bwd, bool match)
+{
+    int e;
+    
+    if(match && edln->point>0){
+        char *tmpstr=history_search_str(edln);
+        if(tmpstr==NULL)
+            return edln->histent;
+        e=mod_query_history_search(tmpstr, from, bwd);
+        free(tmpstr);
+    }else{
+        e=mod_query_history_search(edln->context, from, bwd);
+    }
+    
+    return e;
+}
+
+
+void edln_history_prev(Edln *edln, bool match)
+{
+    int e=search(edln, edln->histent+1, FALSE, match);
+    if(e>=0)
+        edln_do_set_hist(edln, e, match);
+}
+
+
+void edln_history_next(Edln *edln, bool match)
+{
+    int e=edln->histent;
+    
+    if(edln->histent<0)
+        return;
+
+    e=search(edln, edln->histent-1, TRUE, match);
+    
+    if(e>=0){
+        edln_do_set_hist(edln, e, match);
+    }else{
+        edln->histent=-1;
+        if(edln->p!=NULL)
+            free(edln->p);
+        edln->p=edln->tmp_p;
+        edln->palloced=edln->tmp_palloced;
+        edln->tmp_p=NULL;
+        edln->psize=(edln->p==NULL ? 0 : strlen(edln->p));
+        edln->point=edln->psize;
+        edln->mark=-1;
+        edln->modified=TRUE;
+        UPDATE_NEW();
+    }
+}
+
+
+uint edln_history_matches(Edln *edln, char ***h_ret)
+{
+    char *tmpstr=history_search_str(edln);
+    uint ret;
+    
+    if(tmpstr==NULL){
+        *h_ret=NULL;
+        return 0;
+    }
+        
+    ret=mod_query_history_complete(tmpstr, h_ret);
+    
+    free(tmpstr);
+    
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init/deinit */
+
+
+bool edln_init(Edln *edln, const char *p)
+{
+    if(p==NULL)
+        p="";
+    
+    if(!edln_initstr(edln, p))
+        return FALSE;
+    
+    edln->point=edln->psize;
+    edln->mark=-1;
+    edln->histent=-1;
+    edln->modified=FALSE;
+    edln->tmp_p=NULL;
+    edln->context=NULL;
+    
+    return TRUE;
+}
+
+
+void edln_deinit(Edln *edln)
+{
+    if(edln->p!=NULL){
+        free(edln->p);
+        edln->p=NULL;
+    }
+    if(edln->tmp_p!=NULL){
+        free(edln->tmp_p);
+        edln->tmp_p=NULL;
+    }
+    if(edln->context!=NULL){
+        free(edln->context);
+        edln->context=NULL;
+    }
+}
+
+
+static const char *ctx(Edln *edln)
+{
+    if(edln->context!=NULL)
+        return edln->context;
+    else
+        return "default:";
+}
+
+
+char* edln_finish(Edln *edln)
+{
+    char *p=edln->p, *hist;
+    
+    if(p!=NULL){
+        libtu_asprintf(&hist, "%s%s", ctx(edln), p);
+        if(hist!=NULL)
+            mod_query_history_push_(hist);
+    }
+    
+    edln->p=NULL;
+    edln->psize=edln->palloced=0;
+    
+    /*stripws(p);*/
+    return str_stripws(p);
+}
+
+/*}}}*/
+
diff --git a/mod_query/edln.h b/mod_query/edln.h
new file mode 100644 (file)
index 0000000..9d95de5
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * ion/mod_query/edln.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_EDLN_H
+#define ION_MOD_QUERY_EDLN_H
+
+#include <ioncore/common.h>
+#include <libtu/obj.h>
+
+INTRSTRUCT(Edln);
+
+typedef void EdlnUpdateHandler(void*, int from, int mode);
+
+#define EDLN_UPDATE_MOVED   0x01
+#define EDLN_UPDATE_CHANGED 0x02
+#define EDLN_UPDATE_NEW     0x04
+        
+DECLSTRUCT(Edln){
+    char *p;
+    char *tmp_p;
+    int point;
+    int mark;
+    int psize;
+    int palloced;
+    int tmp_palloced;
+    int modified;
+    int histent;
+    void *uiptr;
+    char *context;
+
+    EdlnUpdateHandler *ui_update;
+};
+
+
+bool edln_insstr(Edln *edln, const char *str);
+bool edln_insstr_n(Edln *edln, const char *str, int len,
+                   bool update, bool movepoint);
+bool edln_transpose_chars(Edln *edln);
+bool edln_transpose_words(Edln *edln);
+void edln_back(Edln *edln);
+void edln_forward(Edln *edln);
+void edln_bol(Edln *edln);
+void edln_eol(Edln *edln);
+void edln_bskip_word(Edln *edln);
+void edln_skip_word(Edln *edln);
+void edln_set_point(Edln *edln, int point);
+void edln_delete(Edln *edln);
+void edln_backspace(Edln *edln);
+void edln_kill_to_eol(Edln *edln);
+void edln_kill_to_bol(Edln *edln);
+void edln_kill_line(Edln *edln);
+void edln_kill_word(Edln *edln);
+void edln_bkill_word(Edln *edln);
+void edln_set_mark(Edln *edln);
+void edln_clear_mark(Edln *edln);
+void edln_cut(Edln *edln);
+void edln_copy(Edln *edln);
+void edln_history_prev(Edln *edln, bool match);
+void edln_history_next(Edln *edln, bool match);
+uint edln_history_matches(Edln *edln, char ***h_ret);
+bool edln_set_context(Edln *edln, const char *str);
+
+bool edln_init(Edln *edln, const char *dflt);
+void edln_deinit(Edln *edln);
+char* edln_finish(Edln *edln);
+
+#endif /* ION_MOD_QUERY_EDLN_H */
diff --git a/mod_query/fwarn.c b/mod_query/fwarn.c
new file mode 100644 (file)
index 0000000..7280f85
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * ion/mod_query/query.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/focus.h>
+#include <ioncore/frame.h>
+#include <libtu/objp.h>
+#include "wmessage.h"
+#include "fwarn.h"
+
+
+/*EXTL_DOC
+ * Display an error message box in the multiplexer \var{mplex}.
+ */
+EXTL_EXPORT
+WMessage *mod_query_warn(WMPlex *mplex, const char *p)
+{
+    char *p2;
+    WMessage *wmsg;
+    
+    if(p==NULL)
+        return NULL;
+    
+    p2=scat(TR("Error:\n"), p);
+    
+    if(p2==NULL)
+        return NULL;
+    
+    wmsg=mod_query_message(mplex, p2);
+    
+    free(p2);
+    
+    return wmsg;
+}
+
+/*EXTL_DOC
+ * Display a message in the \var{mplex}.
+ */
+EXTL_EXPORT
+WMessage *mod_query_message(WMPlex *mplex, const char *p)
+{
+    WMPlexAttachParams par;
+
+    if(p==NULL)
+        return NULL;
+    
+    par.flags=(MPLEX_ATTACH_SWITCHTO|
+               MPLEX_ATTACH_MODAL|
+               MPLEX_ATTACH_UNNUMBERED|
+               MPLEX_ATTACH_SIZEPOLICY);
+    par.szplcy=SIZEPOLICY_FULL_BOUNDS;
+
+    return (WMessage*)mplex_do_attach_new(mplex, &par,
+                                          (WRegionCreateFn*)create_wmsg,
+                                          (void*)p);
+}
+
diff --git a/mod_query/fwarn.h b/mod_query/fwarn.h
new file mode 100644 (file)
index 0000000..6dfee37
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * ion/mod_query/fwarn.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_FWARN_H
+#define ION_MOD_QUERY_FWARN_H
+
+#include <ioncore/mplex.h>
+#include "wmessage.h"
+
+extern WMessage *mod_query_message(WMPlex *mplex, const char *p);
+extern WMessage *mod_query_fwarn(WMPlex *mplex, const char *p);
+
+#endif /* ION_MOD_QUERY_FWARN_H */
diff --git a/mod_query/history.c b/mod_query/history.c
new file mode 100644 (file)
index 0000000..05b6aa5
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * ion/mod_query/history.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <ioncore/common.h>
+#include <libextl/extl.h>
+
+#include "history.h"
+
+
+#define HISTORY_SIZE 1024
+
+
+static int hist_head=HISTORY_SIZE;
+static int hist_count=0;
+static char *hist[HISTORY_SIZE];
+
+
+int get_index(int i)
+{
+    if(i<0 || i>=hist_count)
+        return -1;
+    return (hist_head+i)%HISTORY_SIZE;
+}
+    
+
+/*EXTL_DOC
+ * Push an entry into line editor history.
+ */
+EXTL_EXPORT
+bool mod_query_history_push(const char *str)
+{
+    char *s=scopy(str);
+    
+    if(s==NULL)
+        return FALSE;
+    
+    mod_query_history_push_(s);
+    
+    return TRUE;
+}
+
+
+void mod_query_history_push_(char *str)
+{
+    int ndx=mod_query_history_search(str, 0, FALSE);
+    
+    if(ndx==0){
+        return; /* First entry already */
+    }else if(ndx>0){
+        int i, j;
+        i=get_index(ndx);
+        free(hist[i]);
+        while(++ndx<hist_count){
+            j=get_index(ndx);
+            hist[i]=hist[j];
+            i=j;
+        }
+        hist_count--;
+    }
+    
+    hist_head--;
+    if(hist_head<0)
+        hist_head=HISTORY_SIZE-1;
+    
+    if(hist_count==HISTORY_SIZE)
+        free(hist[hist_head]);
+    else
+        hist_count++;
+    
+    hist[hist_head]=str;
+}
+
+
+/*EXTL_DOC
+ * Get entry at index \var{n} in line editor history, 0 being the latest.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+const char *mod_query_history_get(int n)
+{
+    int i=get_index(n);
+    return (i<0 ? NULL : hist[i]);
+}
+
+
+/*EXTL_DOC
+ * Clear line editor history.
+ */
+EXTL_EXPORT
+void mod_query_history_clear()
+{
+    while(hist_count!=0){
+        free(hist[hist_head]);
+        hist_count--;
+        if(++hist_head==HISTORY_SIZE)
+            hist_head=0;
+    }
+    hist_head=HISTORY_SIZE;
+}
+
+
+
+static bool match(const char *h, const char *b)
+{
+    const char *h_;
+    
+    if(b==NULL)
+        return TRUE;
+    
+    /* Special case: search in any context. */
+    if(*b=='*' && *(b+1)==':'){
+        b=b+2;
+        h_=strchr(h, ':');
+        if(h_!=NULL)
+            h=h_+1;
+    }
+        
+    return (strncmp(h, b, strlen(b))==0);
+}
+
+
+static const char *skip_colon(const char *s)
+{
+    const char *p=strchr(s, ':');
+    return (p!=NULL ? p+1 : s);
+}
+
+
+/*EXTL_DOC
+ * Try to find matching history entry. Returns -1 if none was
+ * found. The parameter \var{from} specifies where to start 
+ * searching from, and \var{bwd} causes backward search from
+ * that point.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+int mod_query_history_search(const char *s, int from, bool bwd)
+{
+    while(1){
+        int i=get_index(from);
+        if(i<0)
+            return -1;
+        if(match(hist[i], s))
+            return from;
+        if(bwd)
+            from--;
+        else
+            from++;
+    }
+}
+
+
+uint mod_query_history_complete(const char *s, char ***h_ret)
+{
+    char **h=ALLOC_N(char *, hist_count);
+    int i, n=0;
+    
+    if(h==NULL)
+        return 0;
+    
+    for(i=0; i<hist_count; i++){
+        int j=get_index(i);
+        if(j<0)
+            break;
+        if(match(hist[j], s)){
+            h[n]=scopy(skip_colon(hist[j]));
+            if(h[n]!=NULL)
+                n++;
+        }
+    }
+    
+    if(n==0)
+        free(h);
+    else
+        *h_ret=h;
+    
+    return n;
+}
+
+
+/*EXTL_DOC
+ * Return table of history entries.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab mod_query_history_table()
+{
+    ExtlTab tab=extl_create_table();
+    int i;
+
+    for(i=0; i<hist_count; i++){
+        int j=get_index(i);
+        extl_table_seti_s(tab, i+1, hist[j]);
+    }
+    
+    return tab;
+}
diff --git a/mod_query/history.h b/mod_query/history.h
new file mode 100644 (file)
index 0000000..005d62e
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * ion/mod_query/history.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_HISTORY_H
+#define ION_MOD_QUERY_HISTORY_H
+
+#include <ioncore/common.h>
+#include <libextl/extl.h>
+
+extern const char *mod_query_history_get(int n);
+extern bool mod_query_history_push(const char *s);
+extern void mod_query_history_push_(char *s);
+extern void mod_query_history_clear();
+extern int mod_query_history_search(const char *s, int from, bool bwd);
+extern uint mod_query_history_complete(const char *s, char ***h_ret);
+extern ExtlTab mod_query_history_table();
+
+#endif /* ION_MOD_QUERY_HISTORY_H */
diff --git a/mod_query/input.c b/mod_query/input.c
new file mode 100644 (file)
index 0000000..ee19026
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * ion/mod_query/input.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libmainloop/defer.h>
+#include <ioncore/common.h>
+#include <ioncore/window.h>
+#include <ioncore/global.h>
+#include <ioncore/regbind.h>
+#include <ioncore/event.h>
+#include <ioncore/names.h>
+#include "inputp.h"
+
+
+/*{{{ Dynfuns */
+
+
+/*EXTL_DOC
+ * Scroll input \var{input} text contents up.
+ */
+EXTL_EXPORT_MEMBER
+void input_scrollup(WInput *input)
+{
+    CALL_DYN(input_scrollup, input, (input));
+}
+
+
+/*EXTL_DOC
+ * Scroll input \var{input} text contents down.
+ */
+EXTL_EXPORT_MEMBER
+void input_scrolldown(WInput *input)
+{
+    CALL_DYN(input_scrolldown, input, (input));
+}
+
+
+void input_calc_size(WInput *input, WRectangle *geom)
+{
+    *geom=input->last_fp.g;
+    {
+        CALL_DYN(input_calc_size, input, (input, geom));
+    }
+}
+
+
+const char *input_style(WInput *input)
+{
+    const char *ret="input";
+    CALL_DYN_RET(ret, const char*, input_style, input, (input));
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Resize and draw config update */
+
+
+static void input_do_refit(WInput *input, WWindow *par)
+{
+    WRectangle g;
+    input_calc_size(input, &g);
+    window_do_fitrep(&input->win, par, &g);
+}
+
+
+void input_refit(WInput *input)
+{
+    input_do_refit(input, NULL);
+}
+
+
+void input_fitrep(WInput *input, WWindow *par, const WFitParams *fp)
+{
+    input->last_fp=*fp;
+    input_do_refit(input, par);
+}
+
+
+void input_updategr(WInput *input)
+{
+    GrBrush *brush;
+    
+    brush=gr_get_brush(input->win.win,
+                       region_rootwin_of((WRegion*)input),
+                       input_style(input));
+    
+    if(brush==NULL)
+        return;
+    
+    if(input->brush!=NULL)
+        grbrush_release(input->brush);
+    input->brush=brush;
+    input_refit(input);
+    
+    region_updategr_default((WRegion*)input);
+    
+    window_draw((WWindow*)input, TRUE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init/deinit */
+
+
+bool input_init(WInput *input, WWindow *par, const WFitParams *fp)
+{
+    Window win;
+
+    input->last_fp=*fp;
+
+    if(!window_init((WWindow*)input, par, fp))
+        return FALSE;
+
+    win=input->win.win;
+    
+    input->brush=gr_get_brush(win, region_rootwin_of((WRegion*)par),
+                              input_style(input));
+    
+    if(input->brush==NULL)
+        goto fail;
+    
+    input_refit(input);
+    window_select_input(&(input->win), IONCORE_EVENTMASK_NORMAL);
+
+    region_add_bindmap((WRegion*)input, mod_query_input_bindmap);
+    
+    region_register((WRegion*)input);
+    
+    return TRUE;
+    
+fail:
+    window_deinit((WWindow*)input);
+    return FALSE;
+}
+
+
+void input_deinit(WInput *input)
+{
+    if(input->brush!=NULL)
+        grbrush_release(input->brush);
+    
+    window_deinit((WWindow*)input);
+}
+
+
+/*EXTL_DOC
+ * Close input not calling any possible finish handlers.
+ */
+EXTL_EXPORT_MEMBER
+void input_cancel(WInput *input)
+{
+    if(region_manager_allows_destroying((WRegion*)input))
+        mainloop_defer_destroy((Obj*)input);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Focus  */
+
+
+static void input_inactivated(WInput *input)
+{
+    window_draw((WWindow*)input, FALSE);
+}
+
+
+static void input_activated(WInput *input)
+{
+    window_draw((WWindow*)input, FALSE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab input_dynfuntab[]={
+    {(DynFun*)region_fitrep, (DynFun*)input_fitrep},
+    {region_updategr, input_updategr},
+    {region_activated, input_activated},
+    {region_inactivated, input_inactivated},
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WInput, WWindow, input_deinit, input_dynfuntab);
+
+    
+/*}}}*/
diff --git a/mod_query/input.h b/mod_query/input.h
new file mode 100644 (file)
index 0000000..21abf71
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * ion/mod_query/input.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_INPUT_H
+#define ION_MOD_QUERY_INPUT_H
+
+#include <ioncore/common.h>
+#include <ioncore/window.h>
+#include <ioncore/gr.h>
+#include <ioncore/rectangle.h>
+
+INTRCLASS(WInput);
+
+DECLCLASS(WInput){
+    WWindow win;
+    WFitParams last_fp;
+    GrBrush *brush;
+};
+
+extern bool input_init(WInput *input, WWindow *par, const WFitParams *fp);
+extern void input_deinit(WInput *input);
+
+extern void input_fitrep(WInput *input, WWindow *par, const WFitParams *fp);
+extern void input_refit(WInput *input);
+extern void input_cancel(WInput *input);
+extern bool input_rqclose(WInput *input);
+extern void input_updategr(WInput *input);
+
+DYNFUN void input_scrollup(WInput *input);
+DYNFUN void input_scrolldown(WInput *input);
+DYNFUN void input_calc_size(WInput *input, WRectangle *geom);
+DYNFUN const char *input_style(WInput *input);
+
+#endif /* ION_MOD_QUERY_INPUT_H */
diff --git a/mod_query/inputp.h b/mod_query/inputp.h
new file mode 100644 (file)
index 0000000..1da92b5
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * ion/mod_query/inputp.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_INPUTP_H
+#define ION_MOD_QUERY_INPUTP_H
+
+#include <ioncore/common.h>
+#include <libtu/objp.h>
+#include <ioncore/binding.h>
+#include <ioncore/rectangle.h>
+#include "input.h"
+
+typedef void WInputCalcSizeFn(WInput*, WRectangle*);
+typedef void WInputScrollFn(WInput*);
+typedef void WInputDrawFn(WInput*, bool complete);
+
+extern WBindmap *mod_query_input_bindmap;
+extern WBindmap *mod_query_wedln_bindmap;
+
+#endif /* ION_MOD_QUERY_INPUTP_H */
diff --git a/mod_query/listing.c b/mod_query/listing.c
new file mode 100644 (file)
index 0000000..9ceb41d
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * ion/mod_query/listing.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <limits.h>
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/gr.h>
+#include <ioncore/strings.h>
+#include "listing.h"
+
+
+#define COL_SPACING 16
+#define CONT_INDENT "xx"
+#define CONT_INDENT_LEN 2
+#define ITEMROWS(L, R) ((L)->iteminfos==NULL ? 1 : (L)->iteminfos[R].n_parts)
+
+
+static int strings_maxw(GrBrush *brush, char **strs, int nstrs)
+{
+    int maxw=0, w, i;
+    
+    for(i=0; i<nstrs; i++){
+        w=grbrush_get_text_width(brush, strs[i], strlen(strs[i]));
+        if(w>maxw)
+            maxw=w;
+    }
+    
+    return maxw;
+}
+
+
+static int getbeg(GrBrush *brush, int maxw, char *str, int l, int *wret)
+{
+    int n=0, nprev=0, w;
+    GrFontExtents fnte;
+    
+    if(maxw<=0){
+        *wret=0;
+        return 0;
+    }
+    
+    grbrush_get_font_extents(brush, &fnte);
+    
+    if(fnte.max_width!=0){
+        /* Do an initial skip. */
+        int n2=maxw/fnte.max_width;
+    
+        n=0;
+        while(n2>0){
+            n+=str_nextoff(str, n);
+            n2--;
+        }
+    }
+    
+    w=grbrush_get_text_width(brush, str, n);
+    nprev=n;
+    *wret=w;
+
+    while(w<=maxw){
+        *wret=w;
+        nprev=n;
+        n+=str_nextoff(str, n);
+        if(n==nprev)
+            break;
+        w=grbrush_get_text_width(brush, str, n);
+    }
+    
+    return nprev;
+}
+
+
+static void reset_iteminfo(WListingItemInfo *iinf)
+{
+    iinf->n_parts=1;
+    if(iinf->part_lens!=NULL){
+        free(iinf->part_lens);
+        iinf->part_lens=NULL;
+    }
+}
+
+
+static void string_do_calc_parts(GrBrush *brush, int maxw, char *str, int l,
+                                 WListingItemInfo *iinf,
+                                 int wrapw, int ciw)
+{
+    int i=iinf->n_parts, l2=l, w;
+    int rmaxw=maxw-(i==0 ? 0 : ciw);
+    
+    iinf->n_parts++;
+    
+    w=grbrush_get_text_width(brush, str, l);
+    
+    if(w>rmaxw){
+        l2=getbeg(brush, rmaxw-wrapw, str, l, &w);
+        if(l2<=0)
+            l2=1;
+    }
+        
+    if(l2<l){
+        string_do_calc_parts(brush, maxw, str+l2, l-l2, iinf, wrapw, ciw);
+    }else{
+        int *p=(int*)realloc(iinf->part_lens, iinf->n_parts*sizeof(int));
+        if(p==NULL){
+            reset_iteminfo(iinf);
+        }else{
+            iinf->part_lens=p;
+        }
+    }
+
+    if(iinf->part_lens!=NULL)
+        iinf->part_lens[i]=l2;
+}
+
+
+static void string_calc_parts(GrBrush *brush, int maxw, char *str,
+                              WListingItemInfo *iinf)
+{
+    int wrapw=grbrush_get_text_width(brush, "\\", 1);
+    int ciw=grbrush_get_text_width(brush, CONT_INDENT, CONT_INDENT_LEN);
+    int i, s;
+
+    iinf->n_parts=0;
+    iinf->len=strlen(str);
+
+    if(maxw<=0)
+        reset_iteminfo(iinf);
+    else
+        string_do_calc_parts(brush, maxw, str, iinf->len, iinf, wrapw, ciw);
+}
+
+
+static void draw_multirow(GrBrush *brush, int x, int y, int h, 
+                          char *str, WListingItemInfo *iinf,
+                          int maxw, int ciw, int wrapw, const char *style)
+{
+    int i, l;
+    
+    if(iinf==NULL){
+        grbrush_draw_string(brush, x, y, str, strlen(str), TRUE, style);
+        return;
+    }
+
+    assert(iinf->n_parts>=1);
+    if(iinf->part_lens==NULL){
+        assert(iinf->n_parts==1);
+        l=iinf->len;
+    }else{
+        l=iinf->part_lens[0];
+    }
+
+    grbrush_draw_string(brush, x, y, str, l, TRUE, style);
+    
+    for(i=1; i<iinf->n_parts; i++){
+        grbrush_draw_string(brush, x+maxw-wrapw, y, "\\", 1, TRUE, style);
+        
+        y+=h;
+        str+=l;
+        if(i==1){
+            x+=ciw;
+            maxw-=ciw;
+        }
+        l=iinf->part_lens[i];
+            
+        grbrush_draw_string(brush, x, y, str, l, TRUE, style);
+    }
+}
+
+                          
+static int col_fit(int w, int itemw, int spacing)
+{
+    int ncol=1;
+    int tmp=w-itemw;
+    itemw+=spacing;
+    
+    if(tmp>0)
+        ncol+=tmp/itemw;
+    
+    return ncol;
+}
+
+static bool one_row_up(WListing *l, int *ip, int *rp)
+{
+    int i=*ip, r=*rp;
+    int ir=ITEMROWS(l, i);
+    
+    if(r>0){
+        (*rp)--;
+        return TRUE;
+    }
+    
+    if(i==0)
+        return FALSE;
+    
+    (*ip)--;
+    *rp=ITEMROWS(l, i-1)-1;
+    return TRUE;
+}
+
+
+static bool one_row_down(WListing *l, int *ip, int *rp)
+{
+    int i=*ip, r=*rp;
+    int ir=ITEMROWS(l, i);
+    
+    if(r<ir-1){
+        (*rp)++;
+        return TRUE;
+    }
+    
+    if(i==l->nitemcol-1)
+        return FALSE;
+    
+    (*ip)++;
+    *rp=0;
+    return TRUE;
+}
+
+
+void setup_listing(WListing *l, char **strs, int nstrs, bool onecol)
+{
+    if(l->strs!=NULL)
+        deinit_listing(l);
+
+    l->iteminfos=ALLOC_N(WListingItemInfo, nstrs);
+    l->strs=strs;
+    l->nstrs=nstrs;
+    l->onecol=onecol;
+    l->selected_str=-1;
+}
+
+
+void fit_listing(GrBrush *brush, const WRectangle *geom, WListing *l)
+{
+    int ncol, nrow=0, visrow=INT_MAX;
+    int i, maxw, w, h;
+    GrFontExtents fnte;
+    GrBorderWidths bdw;
+    
+    grbrush_get_font_extents(brush, &fnte);
+    grbrush_get_border_widths(brush, &bdw);
+    
+    w=geom->w-bdw.left-bdw.right;
+    h=geom->h-bdw.top-bdw.bottom;
+    
+    maxw=strings_maxw(brush, l->strs, l->nstrs);
+    l->itemw=maxw+COL_SPACING;
+    l->itemh=fnte.max_height;
+    
+    if(l->onecol)
+        ncol=1;
+    else
+        ncol=col_fit(w, l->itemw-COL_SPACING, COL_SPACING);
+
+    if(l->iteminfos!=NULL){
+        for(i=0; i<l->nstrs; i++){
+            if(ncol!=1){
+                reset_iteminfo(&(l->iteminfos[i]));
+                l->iteminfos[i].len=strlen(l->strs[i]);
+            }else{
+                string_calc_parts(brush, w, l->strs[i], &(l->iteminfos[i]));
+            }
+            nrow+=l->iteminfos[i].n_parts;
+        }
+    }else{
+        nrow=l->nstrs;
+    }
+    
+    if(ncol>1){
+        nrow=l->nstrs/ncol+(l->nstrs%ncol ? 1 : 0);
+        l->nitemcol=nrow;
+    }else{
+        l->nitemcol=l->nstrs;
+    }
+    
+    if(l->itemh>0)
+        visrow=h/l->itemh;
+    
+    if(visrow>nrow)
+        visrow=nrow;
+    
+    l->ncol=ncol;
+    l->nrow=nrow;
+    l->visrow=visrow;
+    l->toth=visrow*l->itemh;
+
+#if 0
+    l->firstitem=l->nitemcol-1;
+    l->firstoff=ITEMROWS(l, l->nitemcol-1)-1;
+    for(i=1; i<visrow; i++)
+        one_row_up(l, &(l->firstitem), &(l->firstoff));
+#else
+    l->firstitem=0;
+    l->firstoff=0;
+#endif
+
+}
+
+
+void deinit_listing(WListing *l)
+{
+    int i;
+    
+    if(l->strs==NULL)
+        return;
+    
+    while(l->nstrs--){
+        free(l->strs[l->nstrs]);
+        if(l->iteminfos!=NULL)
+            reset_iteminfo(&(l->iteminfos[l->nstrs]));
+    }
+
+    free(l->strs);
+    l->strs=NULL;
+    
+    if(l->iteminfos!=NULL){
+        free(l->iteminfos);
+        l->iteminfos=NULL;
+    }
+}
+
+
+void init_listing(WListing *l)
+{
+    l->nstrs=0;
+    l->strs=NULL;
+    l->iteminfos=NULL;
+    l->nstrs=0;
+    l->selected_str=-1;
+    l->onecol=TRUE;
+    l->itemw=0;
+    l->itemh=0;
+    l->ncol=0;
+    l->nrow=0;
+    l->nitemcol=0;
+    l->visrow=0;
+    l->toth=0;
+}
+
+
+static void do_draw_listing(GrBrush *brush, const WRectangle *geom, 
+                            WListing *l, const char *style,
+                            const char *selstyle)
+{
+    int wrapw=grbrush_get_text_width(brush, "\\", 1);
+    int ciw=grbrush_get_text_width(brush, CONT_INDENT, CONT_INDENT_LEN);
+    int r, c, i, x, y;
+    GrFontExtents fnte;
+    
+    if(l->nitemcol==0 || l->visrow==0)
+        return;
+    
+    grbrush_get_font_extents(brush, &fnte);
+    
+    x=0;
+    c=0;
+    while(1){
+        y=geom->y+fnte.baseline;
+        i=l->firstitem+c*l->nitemcol;
+        r=-l->firstoff;
+        y+=r*l->itemh;
+        while(r<l->visrow){
+            if(i>=l->nstrs)
+                return;
+            
+            draw_multirow(brush, geom->x+x, y, l->itemh, l->strs[i],
+                          (l->iteminfos!=NULL ? &(l->iteminfos[i]) : NULL),
+                          geom->w-x, ciw, wrapw, 
+                          (i==l->selected_str ? selstyle : style));
+
+            y+=l->itemh*ITEMROWS(l, i);
+            r+=ITEMROWS(l, i);
+            i++;
+        }
+        x+=l->itemw;
+        c++;
+    }
+}
+
+
+void draw_listing(GrBrush *brush, const WRectangle *geom,
+                  WListing *l, bool complete, const char *style,
+                  const char *selstyle)
+{
+    WRectangle geom2;
+    GrBorderWidths bdw;
+                         
+    grbrush_begin(brush, geom, GRBRUSH_AMEND|GRBRUSH_NEED_CLIP);
+
+    if(complete)
+        grbrush_clear_area(brush, geom);
+    
+    grbrush_draw_border(brush, geom, style);
+
+    grbrush_get_border_widths(brush, &bdw);
+    
+    geom2.x=geom->x+bdw.left;
+    geom2.y=geom->y+bdw.top;
+    geom2.w=geom->w-bdw.left-bdw.right;
+    geom2.h=geom->h-bdw.top-bdw.bottom;
+    
+    do_draw_listing(brush, &geom2, l, style, selstyle);
+    
+    grbrush_end(brush);
+}
+
+
+static bool do_scrollup_listing(WListing *l, int n)
+{
+    int i=l->firstitem;
+    int r=l->firstoff;
+    bool ret=FALSE;
+    
+    while(n>0){
+        if(!one_row_up(l, &i, &r))
+            break;
+        ret=TRUE;
+        n--;
+    }
+
+    l->firstitem=i;
+    l->firstoff=r;
+    
+    return ret;
+}
+
+
+static bool do_scrolldown_listing(WListing *l, int n)
+{
+    int i=l->firstitem;
+    int r=l->firstoff;
+    int br=r, bi=i;
+    int bc=l->visrow;
+    bool ret=FALSE;
+    
+    while(--bc>0)
+        one_row_down(l, &bi, &br);
+    
+    while(n>0){
+        if(!one_row_down(l, &bi, &br))
+            break;
+        one_row_down(l, &i, &r);
+        ret=TRUE;
+        n--;
+    }
+
+    l->firstitem=i;
+    l->firstoff=r;
+    
+    return ret;
+}
+
+
+bool scrollup_listing(WListing *l)
+{
+    return do_scrollup_listing(l, l->visrow);
+}
+
+
+bool scrolldown_listing(WListing *l)
+{
+    return do_scrolldown_listing(l, l->visrow);
+}
+
+
+static int listing_first_row_of_item(WListing *l, int i)
+{
+    int fci=i%l->nitemcol, j;
+    int r=0;
+    
+    for(j=0; j<fci; j++)
+        r+=ITEMROWS(l, j);
+    
+    return r;
+}
+
+
+static int listing_first_visible_row(WListing *l)
+{
+    return listing_first_row_of_item(l, l->firstitem)+l->firstoff;
+}
+
+
+bool listing_select(WListing *l, int i)
+{
+    int irow, frow, lrow;
+    bool complredraw=FALSE;
+    
+    if(i<0){
+        l->selected_str=-1;
+        return FALSE;
+    }
+    
+    assert(i<l->nstrs);
+    
+    l->selected_str=i;
+    
+    /* Adjust visible area */
+    
+    irow=listing_first_row_of_item(l, i);
+    frow=listing_first_visible_row(l);
+    
+    while(irow<frow){
+        one_row_up(l, &(l->firstitem), &(l->firstoff));
+        frow--;
+        complredraw=TRUE;
+    }
+
+    irow+=ITEMROWS(l, i)-1;
+    lrow=frow+l->visrow-1;
+    
+    while(irow>lrow){
+        one_row_down(l, &(l->firstitem), &(l->firstoff));
+        lrow++;
+        complredraw=TRUE;
+    }
+    
+    return complredraw;
+}
+
diff --git a/mod_query/listing.h b/mod_query/listing.h
new file mode 100644 (file)
index 0000000..457ec08
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * ion/mod_query/listing.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_LISTING_H
+#define ION_MOD_QUERY_LISTING_H
+
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+#include <ioncore/rectangle.h>
+
+INTRSTRUCT(WListing);
+INTRSTRUCT(WListingItemInfo);
+
+DECLSTRUCT(WListingItemInfo){
+    int len;
+    int n_parts;
+    int *part_lens;
+};
+
+DECLSTRUCT(WListing){
+    char **strs;
+    WListingItemInfo *iteminfos;
+    int nstrs;
+    int selected_str;
+    int ncol, nrow, nitemcol, visrow;
+    int firstitem, firstoff;
+    int itemw, itemh, toth;
+    bool onecol;
+};
+
+extern void init_listing(WListing *l);
+extern void setup_listing(WListing *l, char **strs, int nstrs, bool onecol);
+extern void deinit_listing(WListing *l);
+extern void fit_listing(GrBrush *brush, const WRectangle *geom, WListing *l);
+extern void draw_listing(GrBrush *brush, const WRectangle *geom, WListing *l, 
+                         bool complete, const char *style, 
+                         const char *selstyle);
+extern bool scrollup_listing(WListing *l);
+extern bool scrolldown_listing(WListing *l);
+extern bool listing_select(WListing *l, int i);
+
+#endif /* ION_MOD_QUERY_LISTING_H */
diff --git a/mod_query/main.c b/mod_query/main.c
new file mode 100644 (file)
index 0000000..9eb7323
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * ion/mod_query/main.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libextl/readconfig.h>
+#include <libextl/extl.h>
+#include <libtu/minmax.h>
+#include <ioncore/binding.h>
+#include <ioncore/conf-bindings.h>
+#include <ioncore/frame.h>
+#include <ioncore/saveload.h>
+#include <ioncore/bindmaps.h>
+#include <ioncore/ioncore.h>
+
+#include "query.h"
+#include "edln.h"
+#include "wedln.h"
+#include "input.h"
+#include "complete.h"
+#include "history.h"
+#include "exports.h"
+#include "main.h"
+
+
+/*{{{ Module information */
+
+
+#include "../version.h"
+
+char mod_query_ion_api_version[]=ION_API_VERSION;
+
+
+/*}}}*/
+
+
+/*{{{ Bindmaps */
+
+
+WBindmap *mod_query_input_bindmap=NULL;
+WBindmap *mod_query_wedln_bindmap=NULL;
+
+
+/*}}}*/
+
+
+/*{{{ Set & get */
+
+
+ModQueryConfig mod_query_config={
+    250,
+    TRUE
+};
+
+
+/*EXTL_DOC
+ * Set module configuration. The following are supported:
+ *
+ * \begin{tabularx}{\linewidth}{lX}
+ *  \tabhead{Field & Description}
+ *  \var{autoshowcompl} & (boolean) Is auto-show-completions enabled?
+ *      (default: true). \\
+ *  \var{autoshowcompl_delay} & (integer) auto-show-completions delay
+ *      in milliseconds (default: 250). \\
+ * \end{tabularx}
+ */
+EXTL_EXPORT
+void mod_query_set(ExtlTab tab)
+{
+    ModQueryConfig *c=&mod_query_config;
+
+    extl_table_gets_b(tab, "autoshowcompl", &c->autoshowcompl);
+    
+    if(extl_table_gets_i(tab, "autoshowcompl_delay",
+                         &c->autoshowcompl_delay)){
+        c->autoshowcompl_delay=maxof(c->autoshowcompl_delay, 0);
+    }
+}
+
+/*EXTL_DOC
+ * Get module configuration. For more information see
+ * \fnref{mod_query.set}.
+ */
+EXTL_EXPORT
+ExtlTab mod_query_get()
+{
+    ModQueryConfig *c=&mod_query_config;
+    ExtlTab tab=extl_create_table();
+    
+    extl_table_sets_b(tab, "autoshowcompl", c->autoshowcompl);
+    extl_table_sets_i(tab, "autoshowcompl_delay", c->autoshowcompl_delay);
+    
+    return tab;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init & deinit */
+
+
+static void load_history()
+{
+    ExtlTab tab;
+    int i, n;
+
+    if(!extl_read_savefile("saved_queryhist", &tab))
+        return;
+    
+    n=extl_table_get_n(tab);
+    
+    for(i=n; i>=1; i--){
+        char *s=NULL;
+        if(extl_table_geti_s(tab, i, &s)){
+            mod_query_history_push(s);
+            free(s);
+        }
+    }
+    
+    extl_unref_table(tab);
+}
+
+
+static void save_history()
+{
+    ExtlTab tab=mod_query_history_table();
+    
+    extl_write_savefile("saved_queryhist", tab);
+    
+    extl_unref_table(tab);
+}
+
+
+static bool loaded_ok=FALSE;
+
+void mod_query_deinit()
+{
+    mod_query_unregister_exports();
+
+    if(mod_query_input_bindmap!=NULL){
+        ioncore_free_bindmap("WInput", mod_query_input_bindmap);
+        mod_query_input_bindmap=NULL;
+    }
+    
+    if(mod_query_wedln_bindmap!=NULL){
+        ioncore_free_bindmap("WEdln", mod_query_wedln_bindmap);
+        mod_query_wedln_bindmap=NULL;
+    }
+    
+    hook_remove(ioncore_snapshot_hook, save_history);
+}
+
+
+bool mod_query_init()
+{
+    if(!mod_query_register_exports())
+        goto err;
+    
+    mod_query_input_bindmap=ioncore_alloc_bindmap("WInput", NULL);
+    mod_query_wedln_bindmap=ioncore_alloc_bindmap("WEdln", NULL);
+    
+    if(mod_query_wedln_bindmap==NULL ||
+       mod_query_input_bindmap==NULL){
+        goto err;
+    }
+
+    /*ioncore_read_config("cfg_query", NULL, TRUE);*/
+
+    load_history();
+    
+    loaded_ok=TRUE;
+
+    hook_add(ioncore_snapshot_hook, save_history);
+    
+    return TRUE;
+    
+err:
+    mod_query_deinit();
+    return FALSE;
+}
+
+
+/*}}}*/
+
diff --git a/mod_query/main.h b/mod_query/main.h
new file mode 100644 (file)
index 0000000..2d57b13
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * ion/mod_query/main.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_MAIN_H
+#define ION_MOD_QUERY_MAIN_H
+
+extern bool mod_query_init();
+extern void mod_query_deinit();
+
+INTRSTRUCT(ModQueryConfig);
+
+DECLSTRUCT(ModQueryConfig){
+    int autoshowcompl_delay;
+    bool autoshowcompl;
+};
+
+
+extern ModQueryConfig mod_query_config;
+
+#endif /* ION_MOD_QUERY_MAIN_H */
diff --git a/mod_query/mod_query.lua b/mod_query/mod_query.lua
new file mode 100644 (file)
index 0000000..0f51738
--- /dev/null
@@ -0,0 +1,1217 @@
+--
+-- ion/query/mod_query.lua -- Some common queries for Ion
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+-- 
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+
+-- This is a slight abuse of the package.loaded variable perhaps, but
+-- library-like packages should handle checking if they're loaded instead of
+-- confusing the user with require/include differences.
+if package.loaded["mod_query"] then return end
+
+if not ioncore.load_module("mod_query") then
+    return
+end
+
+local mod_query=_G["mod_query"]
+
+assert(mod_query)
+
+
+local DIE_TIMEOUT_ERRORCODE=10 -- 10 seconds
+local DIE_TIMEOUT_NO_ERRORCODE=2 -- 2 seconds
+
+
+-- Generic helper functions {{{
+
+
+function mod_query.make_completor(completefn)
+    local function completor(cp, str, point)
+        cp:set_completions(completefn(str, point))
+    end
+    return completor
+end
+
+
+--DOC
+-- Low-level query routine. \var{mplex} is the \type{WMPlex} to display
+-- the query in, \var{prompt} the prompt string, and \var{initvalue}
+-- the initial contents of the query box. \var{handler} is a function
+-- that receives (\var{mplex}, result string) as parameter when the
+-- query has been succesfully completed, \var{completor} the completor
+-- routine which receives a (\var{cp}, \var{str}, \var{point}) as parameters.
+-- The parameter \var{str} is the string to be completed and \var{point}
+-- cursor's location within it. Completions should be eventually,
+-- possibly asynchronously, set with \fnref{WComplProxy.set_completions} 
+-- on \var{cp}.
+function mod_query.query(mplex, prompt, initvalue, handler, completor,
+                         context)
+    local function handle_it(str)
+        handler(mplex, str)
+    end
+    local function cycle(wedln)
+        wedln:complete('next', 'normal')
+    end
+
+    -- Check that no other queries are open in the mplex.
+    local l=mplex:managed_list()
+    for i, r in pairs(l) do
+        if obj_is(r, "WEdln") then
+            return
+        end
+    end
+    local wedln=mod_query.do_query(mplex, prompt, initvalue, 
+                                   handle_it, completor, cycle)
+    if context then
+        wedln:set_context(context)
+    end
+    
+    return wedln
+end
+
+
+--DOC
+-- This function query will display a query with prompt \var{prompt} in
+-- \var{mplex} and if the user answers affirmately, call \var{handler}
+-- with \var{mplex} as parameter.
+function mod_query.query_yesno(mplex, prompt, handler)
+    local function handler_yesno(mplex, str)
+        if str=="y" or str=="Y" or str=="yes" then
+            handler(mplex)
+        end
+    end
+    return mod_query.query(mplex, prompt, nil, handler_yesno, nil,
+                           "yesno")
+end
+
+
+local errdata={}
+
+local function maybe_finish(pid)
+    local t=errdata[pid]
+
+    if t and t.closed and t.dietime then
+        errdata[pid]=nil
+        local tmd=os.difftime(t.dietime, t.starttime)
+        --if tmd<DIE_TIMEOUT_ERRORCODE and t.signaled then
+        --    local msg=TR("Program received signal ")..t.termsig.."\n"
+        --    mod_query.warn(t.mplex, msg..(t.errs or ""))
+        --else
+        if ((tmd<DIE_TIMEOUT_ERRORCODE and (t.hadcode or t.signaled)) or
+                (tmd<DIE_TIMEOUT_NO_ERRORCODE)) and t.errs then
+            mod_query.warn(t.mplex, t.errs)
+        end
+    end
+end
+
+
+local badsig_={4, 5, 6, 7, 8, 11}
+local badsig={}
+for _, v in pairs(badsig_) do 
+    badsig[v]=true 
+end
+
+local function chld_handler(p)
+    local t=errdata[p.pid]
+    if t then
+        t.dietime=os.time()
+        t.signaled=(p.signaled and badsig[p.termsig])
+        t.termsig=p.termsig
+        t.hadcode=(p.exited and p.exitstatus~=0)
+        maybe_finish(pid)
+    end
+end
+
+ioncore.get_hook("ioncore_sigchld_hook"):add(chld_handler)
+
+function mod_query.exec_on_merr(mplex, cmd)
+    local pid
+    
+    local function monitor(str)
+        if pid then
+            local t=errdata[pid]
+            if t then
+                if str then
+                    t.errs=(t.errs or "")..str
+                else
+                    t.closed=true
+                    maybe_finish(pid)
+                end
+            end
+        end
+    end
+    
+    local function timeout()
+        errdata[pid]=nil
+    end
+    
+    pid=ioncore.exec_on(mplex, cmd, monitor)
+    
+    if pid<=0 then
+        return
+    end
+
+    local tmr=ioncore.create_timer();
+    local tmd=math.max(DIE_TIMEOUT_NO_ERRORCODE, DIE_TIMEOUT_ERRORCODE)
+    local now=os.time()  
+    tmr:set(tmd*1000, timeout)
+    
+    errdata[pid]={tmr=tmr, mplex=mplex, starttime=now}
+end
+
+
+function mod_query.file_completor(wedln, str)
+    local ic=ioncore.lookup_script("ion-completefile")
+    if ic then
+        mod_query.popen_completions(wedln,
+                                   ic.." "..string.shell_safe(str))
+    end
+end
+
+
+function mod_query.query_execfile(mplex, prompt, prog)
+    assert(prog~=nil)
+    local function handle_execwith(mplex, str)
+        mod_query.exec_on_merr(mplex, prog.." "..string.shell_safe(str))
+    end
+    return mod_query.query(mplex, prompt, mod_query.get_initdir(mplex),
+                           handle_execwith, mod_query.file_completor,
+                           "filename")
+end
+
+
+function mod_query.query_execwith(mplex, prompt, dflt, prog, completor,
+                                  context, noquote)
+    local function handle_execwith(frame, str)
+        if not str or str=="" then
+            str=dflt
+        end
+        local args=(noquote and str or string.shell_safe(str))
+        mod_query.exec_on_merr(mplex, prog.." "..args)
+    end
+    return mod_query.query(mplex, prompt, nil, handle_execwith, completor,
+                           context)
+end
+
+
+function mod_query.get_initdir(mplex)
+    --if mod_query.last_dir then
+    --    return mod_query.last_dir
+    --end
+    local wd=(ioncore.get_dir_for(mplex) or os.getenv("PWD"))
+    if wd==nil then
+        wd="/"
+    elseif string.sub(wd, -1)~="/" then
+        wd=wd .. "/"
+    end
+    return wd
+end
+
+
+local MAXDEPTH=10
+
+
+function mod_query.complete_from_list(list, str)
+    local results={}
+    local len=string.len(str)
+    if len==0 then
+        results=list
+    else
+        for _, m in pairs(list) do
+            if string.sub(m, 1, len)==str then
+                table.insert(results, m)
+            end
+        end
+    end
+    
+    return results
+end    
+
+
+local pipes={}
+
+mod_query.COLLECT_THRESHOLD=2000
+
+--DOC
+-- This function can be used to read completions from an external source.
+-- The parameter \var{cp} is the completion proxy to be used,
+-- and the string \var{cmd} the shell command to be executed. To its stdout, 
+-- the command should on the first line write the \var{common_beg} 
+-- parameter of \fnref{WComplProxy.set_completions} (which \var{fn} maybe used
+-- to override) and a single actual completion on each of the successive lines.
+-- The function \var{reshnd} may be used to override a result table
+-- building routine.
+function mod_query.popen_completions(cp, cmd, fn, reshnd)
+    
+    local pst={cp=cp, maybe_stalled=0}
+    
+    if not reshnd then
+        reshnd = function(rs, a)
+                     if not rs.common_beg then
+                         rs.common_beg=a
+                     else
+                         table.insert(rs, a)
+                     end
+                 end
+    end
+
+    local function rcv(str)
+        local data=""
+        local results={}
+        local totallen=0
+        local lines=0
+        
+        while str do
+            if pst.maybe_stalled>=2 then
+                pipes[rcv]=nil
+                return
+            end
+            pst.maybe_stalled=0
+            
+            totallen=totallen+string.len(str)
+            if totallen>ioncore.RESULT_DATA_LIMIT then
+                error(TR("Too much result data"))
+            end
+            
+
+            data=string.gsub(data..str, "([^\n]*)\n", 
+                             function(s) 
+                                 reshnd(results, s) 
+                                 lines=lines+1
+                                 return ""
+                             end)
+            
+            if lines>mod_query.COLLECT_THRESHOLD then
+                collectgarbage()
+                lines=0
+            end
+            
+            str=coroutine.yield()
+        end
+        
+        if not results.common_beg then
+            results.common_beg=beg
+        end
+        
+        (fn or WComplProxy.set_completions)(cp, results)
+        
+        pipes[rcv]=nil
+        results={}
+        
+        collectgarbage()
+    end
+    
+    local found_clean=false
+    
+    for k, v in pairs(pipes) do
+        if v.cp==cp then
+            if v.maybe_stalled<2 then
+                v.maybe_stalled=v.maybe_stalled+1
+                found_clean=true
+            end
+        end
+    end
+    
+    if not found_clean then
+        pipes[rcv]=pst
+        ioncore.popen_bgread(cmd, coroutine.wrap(rcv))
+    end
+end
+
+
+-- }}}
+
+
+-- Simple queries for internal actions {{{
+
+
+function mod_query.call_warn(mplex, fn)
+    local err = collect_errors(fn)
+    if err then
+        mod_query.warn(mplex, err)
+    end
+    return err
+end
+
+
+function mod_query.complete_name(str, list)
+    local entries={}
+    local l=string.len(str)
+    for i, reg in pairs(list) do
+        local nm=reg:name()
+        if nm and string.sub(nm, 1, l)==str then
+            table.insert(entries, nm)
+        end
+    end
+    if #entries==0 then
+        for i, reg in pairs(list) do
+            local nm=reg:name()
+            if nm and string.find(nm, str, 1, true) then
+                table.insert(entries, nm)
+            end
+        end
+    end
+    return entries
+end
+
+function mod_query.complete_clientwin(str)
+    return mod_query.complete_name(str, ioncore.clientwin_list())
+end
+
+function mod_query.complete_workspace(str)
+    return mod_query.complete_name(str, ioncore.region_list("WGroupWS"))
+end
+
+function mod_query.complete_region(str)
+    return mod_query.complete_name(str, ioncore.region_list())
+end
+
+
+function mod_query.gotoclient_handler(frame, str)
+    local cwin=ioncore.lookup_clientwin(str)
+    
+    if cwin==nil then
+        mod_query.warn(frame, TR("Could not find client window %s.", str))
+    else
+        cwin:goto()
+    end
+end
+
+function mod_query.attachclient_handler(frame, str)
+    local cwin=ioncore.lookup_clientwin(str)
+    
+    if not cwin then
+        mod_query.warn(frame, TR("Could not find client window %s.", str))
+        return
+    end
+    
+    local reg=cwin:manager()
+    local attach
+    
+    if not obj_is(reg, "WGroupCW") then
+        reg = cwin
+        attach = function()
+                     frame:attach_new {
+                         type = "WGroupCW", 
+                         switchto = true,
+                         managed = {{ reg = cwin, bottom = true }}
+                     }
+                 end
+    else
+        attach = function()
+                     frame:attach(reg, { switchto = true })
+                 end
+    end
+        
+    if frame:rootwin_of()~=reg:rootwin_of() then
+        mod_query.warn(frame, TR("Cannot attach: different root windows."))
+    elseif reg:manager()==frame then
+        reg:goto()
+    else
+        mod_query.call_warn(frame, attach)
+    end
+end
+
+
+function mod_query.workspace_handler(mplex, name)
+    local ws=ioncore.lookup_region(name, "WGroupWS")
+    if ws then
+        ws:goto()
+        return
+    end
+
+    local scr=mplex:screen_of()
+    
+    local function mkws()
+       if not ioncore.create_ws(scr, {name=name}) then
+            error(TR("Unknown error"))
+        end
+    end
+
+    mod_query.call_warn(mplex, mkws)
+end
+
+
+--DOC
+-- This query asks for the name of a client window and attaches
+-- it to the frame the query was opened in. It uses the completion
+-- function \fnref{ioncore.complete_clientwin}.
+function mod_query.query_gotoclient(mplex)
+    mod_query.query(mplex, TR("Go to window:"), nil,
+                    mod_query.gotoclient_handler,
+                    mod_query.make_completor(mod_query.complete_clientwin),
+                    "windowname")
+end
+
+--DOC
+-- This query asks for the name of a client window and switches
+-- focus to the one entered. It uses the completion function
+-- \fnref{ioncore.complete_clientwin}.
+function mod_query.query_attachclient(mplex)
+    mod_query.query(mplex, TR("Attach window:"), nil,
+                    mod_query.attachclient_handler, 
+                    mod_query.make_completor(mod_query.complete_clientwin),
+                    "windowname")
+end
+
+
+--DOC
+-- This query asks for the name of a workspace. If a workspace
+-- (an object inheriting \type{WGroupWS}) with such a name exists,
+-- it will be switched to. Otherwise a new workspace with the
+-- entered name will be created and the user will be queried for
+-- the type of the workspace.
+function mod_query.query_workspace(mplex)
+    mod_query.query(mplex, TR("Go to or create workspace:"), nil, 
+                    mod_query.workspace_handler,
+                    mod_query.make_completor(mod_query.complete_workspace),
+                    "workspacename")
+end
+
+
+--DOC
+-- This query asks whether the user wants to exit Ion (no session manager)
+-- or close the session (running under a session manager that supports such
+-- requests). If the answer is 'y', 'Y' or 'yes', so will happen.
+function mod_query.query_shutdown(mplex)
+    mod_query.query_yesno(mplex, TR("Exit Ion/Shutdown session (y/n)?"),
+                         ioncore.shutdown)
+end
+
+
+--DOC
+-- This query asks whether the user wants restart Ioncore.
+-- If the answer is 'y', 'Y' or 'yes', so will happen.
+function mod_query.query_restart(mplex)
+    mod_query.query_yesno(mplex, TR("Restart Ion (y/n)?"), ioncore.restart)
+end
+
+
+--DOC
+-- This function asks for a name new for the frame where the query
+-- was created.
+function mod_query.query_renameframe(frame)
+    mod_query.query(frame, TR("Frame name:"), frame:name(),
+                    function(frame, str) frame:set_name(str) end,
+                    nil, "framename")
+end
+
+
+--DOC
+-- This function asks for a name new for the workspace on which the
+-- query resides.
+function mod_query.query_renameworkspace(mplex)
+    local ws=ioncore.find_manager(mplex, "WGroupWS")
+    mod_query.query(mplex, TR("Workspace name:"), ws:name(),
+                    function(mplex, str) ws:set_name(str) end,
+                    nil, "framename")
+end
+
+
+-- }}}
+
+
+-- Run/view/edit {{{
+
+
+--DOC
+-- Asks for a file to be edited. This script uses 
+-- \command{run-mailcap --mode=edit} by default, but you may provide an
+-- alternative script to use. The default prompt is "Edit file:" (translated).
+function mod_query.query_editfile(mplex, script, prompt)
+    mod_query.query_execfile(mplex, 
+                             prompt or TR("Edit file:"), 
+                             script or "run-mailcap --action=edit")
+end
+
+
+--DOC
+-- Asks for a file to be viewed. This script uses 
+-- \command{run-mailcap --action=view} by default, but you may provide an
+-- alternative script to use. The default prompt is "View file:" (translated).
+function mod_query.query_runfile(mplex, script, prompt)
+    mod_query.query_execfile(mplex, 
+                             prompt or TR("View file:"), 
+                             script or "run-mailcap --action=view")
+
+end
+
+
+local function isspace(s)
+    return string.find(s, "^%s*$")~=nil
+end
+
+
+local function break_cmdline(str, no_ws)
+    local st, en, beg, rest, ch, rem
+    local res={""}
+
+    local function ins(str)
+        local n=#res
+        if string.find(res[n], "^%s+$") then
+            table.insert(res, str)
+        else
+            res[n]=res[n]..str
+        end
+    end
+
+    local function ins_space(str)
+        local n=#res
+        if no_ws then
+            if res[n]~="" then
+                table.insert(res, "")
+            end
+        else
+            if isspace(res[n]) then
+                res[n]=res[n]..str
+            else
+                table.insert(res, str)
+            end
+        end
+    end
+
+    -- Handle terminal startup syntax
+    st, en, beg, ch, rest=string.find(str, "^(%s*)(:+)(.*)")
+    if beg then
+        if string.len(beg)>0 then
+            ins_space(beg)
+        end
+        ins(ch)
+        ins_space("")
+        str=rest
+    end
+
+    while str~="" do
+        st, en, beg, rest, ch=string.find(str, "^(.-)(([%s'\"\\|])(.*))")
+        if not beg then
+            ins(str)
+            break
+        end
+
+        ins(beg)
+        str=rest
+        
+        local sp=false
+        
+        if ch=="\\" then
+            st, en, beg, rest=string.find(str, "^(\\.)(.*)")
+        elseif ch=='"' then
+            st, en, beg, rest=string.find(str, "^(\".-[^\\]\")(.*)")
+            
+            if not beg then
+                st, en, beg, rest=string.find(str, "^(\"\")(.*)")
+            end
+        elseif ch=="'" then
+            st, en, beg, rest=string.find(str, "^('.-')(.*)")
+        else
+            if ch=='|' then
+                ins_space('')
+                ins(ch)
+            else -- ch==' '
+                ins_space(ch)
+            end
+            st, en, beg, rest=string.find(str, "^.(%s*)(.*)")
+            assert(beg and rest)
+            ins_space(beg)
+            sp=true
+            str=rest
+        end
+        
+        if not sp then
+            if not beg then
+                beg=str
+                rest=""
+            end
+            ins(beg)
+            str=rest
+        end
+    end
+    
+    return res
+end
+
+
+local function unquote(str)
+    str=string.gsub(str, "^['\"]", "")
+    str=string.gsub(str, "([^\\])['\"]", "%1")
+    str=string.gsub(str, "\\(.)", "%1")
+    return str
+end
+
+
+local function quote(str)
+    return string.gsub(str, "([%(%)\"'\\%*%?%[%]%| ])", "\\%1")
+end
+
+
+local function find_point(strs, point)
+    for i, s in ipairs(strs) do
+        point=point-string.len(s)
+        if point<=1 then
+            return i
+        end
+    end
+    return #strs
+end
+
+
+function mod_query.exec_completor(wedln, str, point)
+    local parts=break_cmdline(str)
+    local complidx=find_point(parts, point+1)
+    
+    local s_compl, s_beg, s_end="", "", ""
+    
+    if complidx==1 and string.find(parts[1], "^:+$") then
+        complidx=complidx+1
+    end
+    
+    if string.find(parts[complidx], "[^%s]") then
+        s_compl=unquote(parts[complidx])
+    end
+    
+    for i=1, complidx-1 do
+        s_beg=s_beg..parts[i]
+    end
+    
+    for i=complidx+1, #parts do
+        s_end=s_end..parts[i]
+    end
+    
+    local wp=" "
+    if complidx==1 or (complidx==2 and isspace(parts[1])) then
+        wp=" -wp "
+    elseif string.find(parts[1], "^:+$") then
+        if complidx==2 then
+            wp=" -wp "
+        elseif string.find(parts[2], "^%s*$") then
+            if complidx==3 then
+                wp=" -wp "
+            end
+        end
+    end
+
+    local function set_fn(cp, res)
+        res=table.map(quote, res)
+        res.common_beg=s_beg..(res.common_beg or "")
+        res.common_end=(res.common_end or "")..s_end
+        cp:set_completions(res)
+    end
+
+    local function filter_fn(res, s)
+        if not res.common_beg then
+            if s=="./" then
+                res.common_beg=""
+            else
+                res.common_beg=s
+            end
+        else
+            table.insert(res, s)
+        end
+    end
+    
+    local ic=ioncore.lookup_script("ion-completefile")
+    if ic then
+        mod_query.popen_completions(wedln,
+                                   ic..wp..string.shell_safe(s_compl),
+                                   set_fn, filter_fn)
+    end
+end
+
+
+local cmd_overrides={}
+
+
+--DOC
+-- Define a command override for the \fnrefx{mod_query}{query_exec} query.
+function mod_query.defcmd(cmd, fn)
+    cmd_overrides[cmd]=fn
+end
+
+
+function mod_query.exec_handler(mplex, cmdline)
+    local parts=break_cmdline(cmdline, true)
+    local cmd=table.remove(parts, 1)
+    
+    if cmd_overrides[cmd] then
+        cmd_overrides[cmd](mplex, table.map(unquote, parts))
+    elseif cmd~="" then
+        mod_query.exec_on_merr(mplex, cmdline)
+    end
+end
+
+
+--DOC
+-- This function asks for a command to execute with \file{/bin/sh}.
+-- If the command is prefixed with a colon (':'), the command will
+-- be run in an XTerm (or other terminal emulator) using the script
+-- \file{ion-runinxterm}. Two colons ('::') will ask you to press 
+-- enter after the command has finished.
+function mod_query.query_exec(mplex)
+    mod_query.query(mplex, TR("Run:"), nil, mod_query.exec_handler, 
+                    mod_query.exec_completor,
+                    "run")
+end
+
+
+-- }}}
+
+
+-- SSH {{{
+
+
+mod_query.known_hosts={}
+
+
+function mod_query.get_known_hosts(mplex)
+    mod_query.known_hosts={}
+    local f
+    local h=os.getenv("HOME")
+    if h then 
+        f=io.open(h.."/.ssh/known_hosts")
+    end
+    if not f then 
+        warn(TR("Failed to open ~/.ssh/known_hosts"))
+        return
+    end
+    for l in f:lines() do
+        local st, en, hostname=string.find(l, "^([^%s,]+)")
+        if hostname then
+            table.insert(mod_query.known_hosts, hostname)
+        end
+    end
+    f:close()
+end
+
+
+mod_query.hostnicks={}
+
+function mod_query.get_hostnicks(mplex)
+    mod_query.hostnicks={}
+    local f
+    local substr, pat, patterns
+    local h=os.getenv("HOME")
+
+    if h then
+        f=io.open(h.."/.ssh/config")
+    end
+    if not f then 
+        warn(TR("Failed to open ~/.ssh/config"))
+        return
+    end
+
+    for l in f:lines() do
+        _, _, substr=string.find(l, "^%s*[hH][oO][sS][tT](.*)")
+        if substr then
+            _, _, pat=string.find(substr, "^%s*[=%s]%s*(%S.*)")
+            if pat then
+                patterns=pat
+            elseif string.find(substr, "^[nN][aA][mM][eE]")
+                and patterns then
+                for s in string.gfind(patterns, "%S+") do
+                    if not string.find(s, "[*?]") then
+                        table.insert(mod_query.hostnicks, s)
+                    end
+                end
+            end
+        end
+    end
+    f:close()
+end
+
+
+function mod_query.complete_ssh(str)
+    local st, en, user, at, host=string.find(str, "^([^@]*)(@?)(.*)$")
+    
+    if string.len(at)==0 and string.len(host)==0 then
+        host = user; user = ""
+    end
+    
+    if at=="@" then 
+        user = user .. at 
+    end
+    
+    local res = {}
+    
+    if string.len(host)==0 then
+        if string.len(user)==0 then
+            return mod_query.ssh_completions
+        end
+        
+        for _, v in ipairs(mod_query.ssh_completions) do
+            table.insert(res, user .. v)
+        end
+        return res
+    end
+    
+    for _, v in ipairs(mod_query.ssh_completions) do
+        local s, e=string.find(v, host, 1, true)
+        if s==1 and e>=1 then
+            table.insert(res, user .. v)
+        end
+    end
+    
+    return res
+end
+
+mod_query.ssh_completions={}
+
+--DOC
+-- This query asks for a host to connect to with SSH. 
+-- Hosts to tab-complete are read from \file{\~{}/.ssh/known\_hosts}.
+function mod_query.query_ssh(mplex, ssh)
+    mod_query.get_known_hosts(mplex)
+    mod_query.get_hostnicks(mplex)
+
+    for _, v in ipairs(mod_query.known_hosts) do
+        table.insert(mod_query.ssh_completions, v)
+    end
+    for _, v in ipairs(mod_query.hostnicks) do
+        table.insert(mod_query.ssh_completions, v)
+    end
+
+    ssh=(ssh or ":ssh")
+
+    local function handle_exec(mplex, str)
+        if not (str and string.find(str, "[^%s]")) then
+            return
+        end
+        
+        mod_query.exec_on_merr(mplex, ssh.." "..string.shell_safe(str))
+    end
+    
+    return mod_query.query(mplex, TR("SSH to:"), nil, handle_exec,
+                           mod_query.make_completor(mod_query.complete_ssh),
+                           "ssh")
+end
+
+-- }}}
+
+
+-- Man pages {{{{
+
+
+function mod_query.man_completor(wedln, str)
+    local mc=ioncore.lookup_script("ion-completeman")
+    if mc then
+        mod_query.popen_completions(wedln, (mc.." -complete "
+                                            ..string.shell_safe(str)))
+    end
+end
+
+
+--DOC
+-- This query asks for a manual page to display. By default it runs the
+-- \command{man} command in an \command{xterm} using \command{ion-runinxterm},
+-- but it is possible to pass another program as the \var{prog} argument.
+function mod_query.query_man(mplex, prog)
+    local dflt=ioncore.progname()
+    mod_query.query_execwith(mplex, TR("Manual page (%s):", dflt), 
+                             dflt, prog or ":man", 
+                             mod_query.man_completor, "man",
+                             true --[[ no quoting ]])
+end
+
+
+-- }}}
+
+
+-- Lua code execution {{{
+
+
+function mod_query.create_run_env(mplex)
+    local origenv=getfenv()
+    local meta={__index=origenv, __newindex=origenv}
+    local env={
+        _=mplex, 
+        _sub=mplex:current(),
+        print=my_print
+    }
+    setmetatable(env, meta)
+    return env
+end
+
+function mod_query.do_handle_lua(mplex, env, code)
+    local print_res
+    local function collect_print(...)
+        local tmp=""
+        local l=#arg
+        for i=1,l do
+            tmp=tmp..tostring(arg[i])..(i==l and "\n" or "\t")
+        end
+        print_res=(print_res and print_res..tmp or tmp)
+    end
+
+    local f, err=loadstring(code)
+    if not f then
+        mod_query.warn(mplex, err)
+        return
+    end
+    
+    env.print=collect_print
+    setfenv(f, env)
+    
+    err=collect_errors(f)
+    if err then
+        mod_query.warn(mplex, err)
+    elseif print_res then
+        mod_query.message(mplex, print_res)
+    end
+end
+
+local function getindex(t)
+    local mt=getmetatable(t)
+    if mt then return mt.__index end
+    return nil
+end
+
+function mod_query.do_complete_lua(env, str)
+    -- Get the variable to complete, including containing tables.
+    -- This will also match string concatenations and such because
+    -- Lua's regexps don't support optional subexpressions, but we
+    -- handle them in the next step.
+    local comptab=env
+    local metas=true
+    local _, _, tocomp=string.find(str, "([%w_.:]*)$")
+    
+    -- Descend into tables
+    if tocomp and string.len(tocomp)>=1 then
+        for t in string.gfind(tocomp, "([^.:]*)[.:]") do
+            metas=false
+            if string.len(t)==0 then
+                comptab=env;
+            elseif comptab then
+                if type(comptab[t])=="table" then
+                    comptab=comptab[t]
+                elseif type(comptab[t])=="userdata" then
+                    comptab=getindex(comptab[t])
+                    metas=true
+                else
+                    comptab=nil
+                end
+            end
+        end
+    end
+    
+    if not comptab then return {} end
+    
+    local compl={}
+    
+    -- Get the actual variable to complete without containing tables
+    _, _, compl.common_beg, tocomp=string.find(str, "(.-)([%w_]*)$")
+    
+    local l=string.len(tocomp)
+    
+    local tab=comptab
+    local seen={}
+    while true do
+        if type(tab) == "table" then
+            for k in pairs(tab) do
+                if type(k)=="string" then
+                    if string.sub(k, 1, l)==tocomp then
+                        table.insert(compl, k)
+                    end
+                end
+            end
+        end
+
+        -- We only want to display full list of functions for objects, not 
+        -- the tables representing the classes.
+        --if not metas then break end
+        
+        seen[tab]=true
+        tab=getindex(tab)
+        if not tab or seen[tab] then break end
+    end
+    
+    -- If there was only one completion and it is a string or function,
+    -- concatenate it with "." or "(", respectively.
+    if #compl==1 then
+        if type(comptab[compl[1]])=="table" then
+            compl[1]=compl[1] .. "."
+        elseif type(comptab[compl[1]])=="function" then
+            compl[1]=compl[1] .. "("
+        end
+    end
+    
+    return compl
+end
+
+
+--DOC
+-- This query asks for Lua code to execute. It sets the variable '\var{\_}'
+-- in the local environment of the string to point to the mplex where the
+-- query was created. It also sets the table \var{arg} in the local
+-- environment to \code{\{_, _:current()\}}.
+function mod_query.query_lua(mplex)
+    local env=mod_query.create_run_env(mplex)
+    
+    local function complete(cp, code)
+        cp:set_completions(mod_query.do_complete_lua(env, code))
+    end
+    
+    local function handler(mplex, code)
+        return mod_query.do_handle_lua(mplex, env, code)
+    end
+    
+    mod_query.query(mplex, TR("Lua code:"), nil, handler, complete, "lua")
+end
+
+-- }}}
+
+
+-- Menu query {{{
+
+--DOC
+-- This query can be used to create a query of a defined menu.
+function mod_query.query_menu(mplex, themenu, prompt)
+    local _sub=mplex:current()
+    local menu=ioncore.evalmenu(themenu, {mplex, _sub})
+    local menuname=(type(themenu)=="string" and themenu or "?")
+    
+    if not menu then
+        mod_query.warn(mplex, TR("Unknown menu %s.", tostring(themenu)))
+        return
+    end
+    
+    if not prompt then
+        prompt=menuname..":"
+    else
+        prompt=TR(prompt)
+    end
+
+    local function xform_name(n, is_submenu)
+        return (string.lower(string.gsub(n, "[-%s]+", "-"))
+                ..(is_submenu and "/" or ""))
+    end
+
+    local function xform_menu(t, m, p)
+        for _, v in ipairs(m) do
+            if v.name then
+                local is_submenu=v.submenu_fn
+                local n=p..xform_name(v.name, is_submenu)
+                while t[n] do
+                    n=n.."'"
+                end
+                t[n]=v
+                if is_submenu and not v.noautoexpand then
+                    local sm=v.submenu_fn()
+                    if sm then
+                        xform_menu(t, sm, n)
+                    else
+                        ioncore.warn_traced(TR("Missing submenu ")
+                                            ..(v.name or ""))
+                    end
+                end
+            end
+        end
+        return t
+    end
+    
+    local ntab=xform_menu({}, menu, "")
+    
+    local function complete(str)
+        local results={}
+        for s, e in pairs(ntab) do
+            if string.find(s, str, 1, true) then
+                table.insert(results, s)
+            end
+        end
+        return results
+    end
+    
+    local function handle(mplex, str)
+        local e=ntab[str]
+        if e then
+            if e.func then
+                local err=collect_errors(function() 
+                                             e.func(mplex, _sub) 
+                                         end)
+                if err then
+                    mod_query.warn(mplex, err)
+                end
+            elseif e.submenu_fn then
+                mod_query.query_menu(mplex, e.submenu_fn(),
+                                     TR("%s:", e.name))
+            end
+        else
+            mod_query.warn(mplex, TR("No entry '%s'", str))
+        end
+    end
+    
+    mod_query.query(mplex, prompt, nil, handle, 
+                    mod_query.make_completor(complete), "menu."..menuname)
+end
+
+-- }}}
+
+
+-- Miscellaneous {{{
+
+
+--DOC 
+-- Display an "About Ion" message in \var{mplex}.
+function mod_query.show_about_ion(mplex)
+    mod_query.message(mplex, ioncore.aboutmsg())
+end
+
+
+--DOC
+-- Show information about a region tree 
+function mod_query.show_tree(mplex, reg, max_depth)
+    local function indent(s)
+        local i="    "
+        return i..string.gsub(s, "\n", "\n"..i)
+    end
+    
+    local function get_info(reg, indent, d)
+        if not reg then
+            return (indent .. "No region")
+        end
+        
+        local function n(s) return (s or "") end
+
+        local s=string.format("%s%s \"%s\"", indent, obj_typename(reg),
+                              n(reg:name()))
+        indent = indent .. "  "
+        if obj_is(reg, "WClientWin") then
+            local i=reg:get_ident()
+            s=s .. TR("\n%sClass: %s\n%sRole: %s\n%sInstance: %s\n%sXID: 0x%x",
+                      indent, n(i.class), 
+                      indent, n(i.role), 
+                      indent, n(i.instance), 
+                      indent, reg:xid())
+        end
+        
+        if (not max_depth or max_depth > d) and reg.managed_list then
+            local mgd=reg:managed_list()
+            if #mgd > 0 then
+                s=s .. "\n" .. indent .. "---"
+                for k, v in pairs(mgd) do
+                    s=s .. "\n" .. get_info(v, indent, d+1)
+                end
+            end
+        end
+        
+        return s
+    end
+    
+    mod_query.message(mplex, get_info(reg, "", 0))
+end
+
+-- }}}
+
+-- Load extras
+dopath('mod_query_chdir')
+
+-- Mark ourselves loaded.
+package.loaded["mod_query"]=true
+
+
+-- Load configuration file
+dopath('cfg_query', true)
diff --git a/mod_query/mod_query_chdir.lua b/mod_query/mod_query_chdir.lua
new file mode 100644 (file)
index 0000000..3b408d2
--- /dev/null
@@ -0,0 +1,26 @@
+--
+-- ion/query/mod_query_chdir.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+-- 
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+local function ws_chdir(mplex, params)
+    ws=assert(ioncore.find_manager(mplex, "WGroupWS"))
+    local ok, err=ioncore.chdir_for(ws, params[1] or "")
+    if not ok then
+        mod_query.warn(mplex, err)
+    end
+end
+
+local function ws_showdir(mplex, params)
+    local dir=assert(ioncore.get_dir_for(mplex) or os.getenv("PWD"))
+    mod_query.message(mplex, dir)
+end
+
+mod_query.defcmd("cd", ws_chdir)
+mod_query.defcmd("pwd", ws_showdir)
diff --git a/mod_query/query.c b/mod_query/query.c
new file mode 100644 (file)
index 0000000..b56c087
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * ion/mod_query/query.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libextl/extl.h>
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/binding.h>
+#include <ioncore/regbind.h>
+#include <ioncore/key.h>
+#include "query.h"
+#include "wedln.h"
+
+
+static void create_cycle_binding(WEdln *wedln, XKeyEvent *ev, ExtlFn cycle)
+{
+    WBindmap *bindmap=create_bindmap();
+    WBinding b;
+    
+    if(bindmap==NULL)
+        return;
+        
+    b.ksb=XKeycodeToKeysym(ioncore_g.dpy, ev->keycode, 0);
+    b.kcb=ev->keycode;
+    b.state=ev->state;
+    b.act=BINDING_KEYPRESS;
+    b.area=0;
+    b.wait=FALSE;
+    b.submap=NULL;
+    b.func=extl_ref_fn(cycle);
+    
+    if(!bindmap_add_binding(bindmap, &b)){
+        extl_unref_fn(b.func);
+        bindmap_destroy(bindmap);
+        return;
+    }
+    
+    if(!region_add_bindmap((WRegion*)wedln, bindmap)){
+        bindmap_destroy(bindmap);
+        return;
+    }
+    
+    wedln->cycle_bindmap=bindmap;
+}
+    
+
+/*--lowlevel routine not to be called by the user--EXTL_DOC
+ * Show a query window in \var{mplex} with prompt \var{prompt}, initial
+ * contents \var{dflt}. The function \var{handler} is called with
+ * the entered string as the sole argument when \fnref{WEdln.finish}
+ * is called. The function \var{completor} is called with the created
+ * \type{WEdln} is first argument and the string to complete is the
+ * second argument when \fnref{WEdln.complete} is called.
+ */
+EXTL_EXPORT
+WEdln *mod_query_do_query(WMPlex *mplex, const char *prompt, const char *dflt,
+                          ExtlFn handler, ExtlFn completor, ExtlFn cycle)
+{
+    WRectangle geom;
+    WEdlnCreateParams fnp;
+    WMPlexAttachParams par;
+    XKeyEvent *ev=ioncore_current_key_event();
+    WEdln *wedln;
+
+    fnp.prompt=prompt;
+    fnp.dflt=dflt;
+    fnp.handler=handler;
+    fnp.completor=completor;
+    
+    par.flags=(MPLEX_ATTACH_SWITCHTO|
+               MPLEX_ATTACH_MODAL|
+               MPLEX_ATTACH_UNNUMBERED|
+               MPLEX_ATTACH_SIZEPOLICY);
+    par.szplcy=SIZEPOLICY_FULL_BOUNDS;
+
+    wedln=(WEdln*)mplex_do_attach_new(mplex, &par,
+                                      (WRegionCreateFn*)create_wedln,
+                                      (void*)&fnp); 
+                                      
+    if(wedln!=NULL && ev!=NULL && cycle!=extl_fn_none())
+        create_cycle_binding(wedln, ev, cycle);
+        
+    
+    return wedln;
+}
+
diff --git a/mod_query/query.h b/mod_query/query.h
new file mode 100644 (file)
index 0000000..9387a17
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * ion/mod_query/query.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_QUERY_H
+#define ION_MOD_QUERY_QUERY_H
+
+#include <ioncore/common.h>
+#include <ioncore/mplex.h>
+#include <libextl/extl.h>
+#include "wedln.h"
+
+extern WEdln *mod_query_do_query(WMPlex *mplex, const char *prompt, 
+                                 const char *dflt, ExtlFn handler, 
+                                 ExtlFn completor, ExtlFn cycle);
+
+#endif /* ION_MOD_QUERY_QUERY_H */
diff --git a/mod_query/wedln-wrappers.c b/mod_query/wedln-wrappers.c
new file mode 100644 (file)
index 0000000..3043d05
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * ion/mod_query/wedln-wrappers.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "wedln.h"
+#include "edln.h"
+#include "complete.h"
+
+/*EXTL_DOC
+ * Move backward one character.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_back(WEdln *wedln)
+{
+    edln_back(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Move forward one character.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_forward(WEdln *wedln)
+{
+    edln_forward(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Transpose characters.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_transpose_chars(WEdln *wedln)
+{
+    edln_transpose_chars(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Transpose words.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_transpose_words(WEdln *wedln)
+{
+    edln_transpose_words(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Go to the beginning of line.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_bol(WEdln *wedln)
+{
+    edln_bol(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Go to the end of line.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_eol(WEdln *wedln)
+{
+    edln_eol(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Go to to end of current sequence of whitespace followed by alphanumeric
+ * characters..
+ */
+EXTL_EXPORT_MEMBER
+void wedln_skip_word(WEdln *wedln)
+{
+    edln_skip_word(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Go to to beginning of current sequence of alphanumeric characters
+ * followed by whitespace.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_bskip_word(WEdln *wedln)
+{
+    edln_bskip_word(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Delete current character.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_delete(WEdln *wedln)
+{
+    edln_delete(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Delete previous character.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_backspace(WEdln *wedln)
+{
+    edln_backspace(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Delete all characters from current to end of line.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_kill_to_eol(WEdln *wedln)
+{
+    edln_kill_to_eol(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Delete all characters from previous to beginning of line.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_kill_to_bol(WEdln *wedln)
+{
+    edln_kill_to_bol(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Delete the whole line.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_kill_line(WEdln *wedln)
+{
+    edln_kill_line(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Starting from the current point, delete possible whitespace and
+ * following alphanumeric characters until next non-alphanumeric character.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_kill_word(WEdln *wedln)
+{
+    edln_kill_word(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Starting from the previous characters, delete possible whitespace and
+ * preceding alphanumeric characters until previous non-alphanumeric character.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_bkill_word(WEdln *wedln)
+{
+    edln_bkill_word(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Set \emph{mark} to current cursor position.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_set_mark(WEdln *wedln)
+{
+    edln_set_mark(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Clear \emph{mark}.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_clear_mark(WEdln *wedln)
+{
+    edln_clear_mark(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Copy text between \emph{mark} and current cursor position to clipboard
+ * and then delete that sequence.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_cut(WEdln *wedln)
+{
+    edln_cut(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Copy text between \emph{mark} and current cursor position to clipboard.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_copy(WEdln *wedln)
+{
+    edln_copy(&(wedln->edln));
+}
+
+/*EXTL_DOC
+ * Replace line editor contents with next entry in history if one exists.
+ * If \var{match} is \code{true}, the initial part of the history entry
+ * must match the current line from beginning to point.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_history_next(WEdln *wedln, bool match)
+{
+    edln_history_next(&(wedln->edln), match);
+}
+
+/*EXTL_DOC
+ * Replace line editor contents with previous in history if one exists.
+ * If \var{match} is \code{true}, the initial part of the history entry
+ * must match the current line from beginning to point.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_history_prev(WEdln *wedln, bool match)
+{
+    edln_history_prev(&(wedln->edln), match);
+}
+
+
+/*EXTL_DOC
+ * Input \var{str} in wedln at current editing point.
+ */
+EXTL_EXPORT_AS(WEdln, insstr)
+void wedln_insstr_exported(WEdln *wedln, const char *str)
+{
+    edln_insstr(&(wedln->edln), str);
+}
+
+
+/*EXTL_DOC
+ * Get line editor contents.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+const char *wedln_contents(WEdln *wedln)
+{
+    return wedln->edln.p;
+}
+
+/*EXTL_DOC
+ * Get current editing point. 
+ * Beginning of the edited line is point 0.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+int wedln_point(WEdln *wedln)
+{
+    return wedln->edln.point;
+}
+
+/*EXTL_DOC
+ * Get current mark (start of selection) for \var{wedln}.
+ * Return value of -1 indicates that there is no mark, and
+ * 0 is the beginning of the line.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+int wedln_mark(WEdln *wedln)
+{
+    return wedln->edln.mark;
+}
+
+
+/*EXTL_DOC
+ * Set history context for \var{wedln}.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_set_context(WEdln *wedln, const char *context)
+{
+    edln_set_context(&(wedln->edln), context);
+}
+
+
+/*EXTL_DOC
+ * Get history context for \var{wedln}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+const char *wedln_context(WEdln *wedln)
+{
+    return wedln->edln.context;
+}
+
diff --git a/mod_query/wedln.c b/mod_query/wedln.c
new file mode 100644 (file)
index 0000000..094f22c
--- /dev/null
@@ -0,0 +1,1072 @@
+/*
+ * ion/mod_query/wedln.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <libtu/setparam.h>
+#include <libextl/extl.h>
+#include <libmainloop/defer.h>
+#include <libmainloop/signal.h>
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/strings.h>
+#include <ioncore/xic.h>
+#include <ioncore/selection.h>
+#include <ioncore/event.h>
+#include <ioncore/regbind.h>
+#include "edln.h"
+#include "wedln.h"
+#include "inputp.h"
+#include "complete.h"
+#include "main.h"
+
+
+#define WEDLN_BRUSH(X) ((X)->input.brush)
+
+
+/*{{{ Drawing primitives */
+
+
+static int calc_text_y(WEdln *wedln, const WRectangle *geom)
+{
+    GrFontExtents fnte;
+        
+    grbrush_get_font_extents(WEDLN_BRUSH(wedln), &fnte);
+        
+    return geom->y+geom->h/2-fnte.max_height/2+fnte.baseline;
+}
+
+
+static int wedln_draw_strsect(WEdln *wedln, const WRectangle *geom, 
+                              int x, int y, const char *str, int len,
+                              const char *attr)
+{
+    if(len==0)
+        return 0;
+    
+    grbrush_draw_string(WEDLN_BRUSH(wedln), x, y, str, len, TRUE, attr);
+    
+    return grbrush_get_text_width(WEDLN_BRUSH(wedln), str, len);
+}
+
+#if 0
+static void dispu(const char* s, int l)
+{
+    while(l>0){
+        int c=(unsigned char)*s;
+        fprintf(stderr, "%d[%c]", c, *s);
+        s++;
+        l--;
+    }
+    fprintf(stderr, "\n");
+}
+#endif
+
+#define DSTRSECT(LEN, INV) \
+    if(LEN>0){tx+=wedln_draw_strsect(wedln, geom, geom->x+tx, ty, str, LEN, INV); \
+     str+=LEN; len-=LEN;}
+
+
+static void wedln_do_draw_str_box(WEdln *wedln, const WRectangle *geom,
+                                  const char *str, int cursor, 
+                                  int mark, int tx)
+{
+    int len=strlen(str), ll=0, ty=0;
+    const char *normalstyle=(REGION_IS_ACTIVE(wedln)
+                             ? "active-normal" : "inactive-normal");
+    const char *selectionstyle=(REGION_IS_ACTIVE(wedln)
+                                ? "active-selection" : "inactive-selection");
+    const char *cursorstyle=(REGION_IS_ACTIVE(wedln)
+                             ? "active-cursor" : "inactive-cursor");
+    
+    /*if(tx<geom->w){
+        WRectangle g=*geom;
+        g.x+=tx;
+        g.w-=tx;
+        grbrush_clear_area(WEDLN_BRUSH(wedln), &g);
+    }*/
+    
+    ty=calc_text_y(wedln, geom);
+
+    if(mark<=cursor){
+        if(mark>=0){
+            DSTRSECT(mark, normalstyle);
+            DSTRSECT(cursor-mark, selectionstyle);
+        }else{
+            DSTRSECT(cursor, normalstyle);
+        }
+        if(len==0){
+            tx+=wedln_draw_strsect(wedln, geom, geom->x+tx, ty,
+                                   " ", 1, cursorstyle);
+        }else{
+            ll=str_nextoff(str, 0);
+            DSTRSECT(ll, cursorstyle);
+        }
+    }else{
+        DSTRSECT(cursor, normalstyle);
+        ll=str_nextoff(str, 0);
+        DSTRSECT(ll, cursorstyle);
+        DSTRSECT(mark-cursor-ll, selectionstyle);
+    }
+    DSTRSECT(len, normalstyle);
+
+    if(tx<geom->w){
+        WRectangle g=*geom;
+        g.x+=tx;
+        g.w-=tx;
+        grbrush_clear_area(WEDLN_BRUSH(wedln), &g);
+    }
+}
+
+
+static void wedln_draw_str_box(WEdln *wedln, const WRectangle *geom,
+                               int vstart, const char *str,
+                               int dstart, int point, int mark)
+{
+    int tx=0;
+
+    /* Some fonts and Xmb/utf8 routines don't work well with dstart!=0. */
+    dstart=0;
+    
+    if(mark>=0){
+        mark-=vstart+dstart;
+        if(mark<0)
+            mark=0;
+    }
+    
+    point-=vstart+dstart;
+    
+    if(dstart!=0)
+        tx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart, dstart);
+
+    grbrush_begin(WEDLN_BRUSH(wedln), geom, GRBRUSH_AMEND|GRBRUSH_NEED_CLIP);
+    
+    wedln_do_draw_str_box(wedln, geom, str+vstart+dstart, point, mark, tx);
+
+    grbrush_end(WEDLN_BRUSH(wedln));
+}
+
+
+static bool wedln_update_cursor(WEdln *wedln, int iw)
+{
+    int cx, l;
+    int vstart=wedln->vstart;
+    int point=wedln->edln.point;
+    int len=wedln->edln.psize;
+    int mark=wedln->edln.mark;
+    const char *str=wedln->edln.p;
+    bool ret;
+    
+    if(point<wedln->vstart)
+        wedln->vstart=point;
+    
+    if(wedln->vstart==point)
+        return FALSE;
+    
+    while(vstart<point){
+        if(point==len){
+            cx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart, 
+                                      point-vstart);
+            cx+=grbrush_get_text_width(WEDLN_BRUSH(wedln), " ", 1);
+        }else{
+            int nxt=str_nextoff(str, point);
+            cx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart, 
+                                      point-vstart+nxt);
+        }
+        
+        if(cx<iw)
+            break;
+        
+        l=str_nextoff(str, vstart);
+        if(l==0)
+            break;
+        vstart+=l;
+    }
+    
+    ret=(wedln->vstart!=vstart);
+    wedln->vstart=vstart;
+    
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Size/location calc */
+
+
+static int get_textarea_height(WEdln *wedln, bool with_spacing)
+{
+    GrBorderWidths bdw;
+    GrFontExtents fnte;
+    
+    grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw);
+    grbrush_get_font_extents(WEDLN_BRUSH(wedln), &fnte);
+    
+    return (fnte.max_height+bdw.top+bdw.bottom+
+            (with_spacing ? bdw.spacing : 0));
+}
+
+
+enum{G_NORESET, G_MAX, G_CURRENT};
+
+
+static void get_geom(WEdln *wedln, int mode, WRectangle *geom)
+{
+    if(mode==G_MAX)
+        *geom=wedln->input.last_fp.g;
+    else if(mode==G_CURRENT)
+        *geom=REGION_GEOM(wedln);
+}
+
+
+static void get_completions_geom(WEdln *wedln, int mode, WRectangle *geom)
+{
+    get_geom(wedln, mode, geom);
+    geom->x=0;
+    geom->y=0;
+    
+    geom->h-=get_textarea_height(wedln, TRUE);
+    if(geom->h<0)
+        geom->h=0;
+}
+
+
+static void get_outer_geom(WEdln *wedln, int mode, WRectangle *geom)
+{
+    int th;
+    
+    get_geom(wedln, mode, geom);
+    geom->x=0;
+    geom->y=0;
+    
+    th=get_textarea_height(wedln, FALSE);
+    
+    geom->y+=geom->h-th;
+    geom->h=th;
+}
+
+
+static void get_inner_geom(WEdln *wedln, int mode, WRectangle *geom)
+{
+    GrBorderWidths bdw;
+    
+    grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw);
+    
+    get_outer_geom(wedln, mode, geom);
+    
+    geom->x+=bdw.left;
+    geom->w-=bdw.left+bdw.right;
+    geom->y+=bdw.top;
+    geom->h-=bdw.top+bdw.bottom;
+    geom->w=maxof(0, geom->w);
+    geom->h=maxof(0, geom->h);
+}
+
+
+static void get_textarea_geom(WEdln *wedln, int mode, WRectangle *geom)
+{
+    get_inner_geom(wedln, mode, geom);
+    geom->x+=wedln->prompt_w;
+    geom->w=maxof(0, geom->w - wedln->prompt_w - wedln->info_w);
+}
+
+
+static void wedln_calc_size(WEdln *wedln, WRectangle *geom)
+{
+    int h, th;
+    GrBorderWidths bdw;
+    WRectangle max_geom=*geom, tageom;
+    
+    if(WEDLN_BRUSH(wedln)==NULL)
+        return;
+    
+    if(wedln->prompt!=NULL){
+        wedln->prompt_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
+                                               wedln->prompt, 
+                                               wedln->prompt_len);
+    }
+    
+    if(wedln->info!=NULL){
+        wedln->info_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
+                                             wedln->info, 
+                                             wedln->info_len);
+    }
+
+    th=get_textarea_height(wedln, wedln->compl_list.strs!=NULL);
+    
+    if(wedln->compl_list.strs==NULL){
+        if(max_geom.h<th || !(wedln->input.last_fp.mode&REGION_FIT_BOUNDS))
+            geom->h=max_geom.h;
+        else
+            geom->h=th;
+    }else{
+        WRectangle g;
+        
+        get_completions_geom(wedln, G_MAX, &g);
+        
+        fit_listing(WEDLN_BRUSH(wedln), &g, &(wedln->compl_list));
+        
+        grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw);
+        
+        h=wedln->compl_list.toth;
+        th+=bdw.top+bdw.bottom;
+        
+        if(h+th>max_geom.h || !(wedln->input.last_fp.mode&REGION_FIT_BOUNDS))
+            h=max_geom.h-th;
+        geom->h=h+th;
+    }
+    
+    geom->w=max_geom.w;
+    geom->y=max_geom.y+max_geom.h-geom->h;
+    geom->x=max_geom.x;
+
+    tageom=*geom;
+    get_textarea_geom(wedln, G_NORESET, &tageom);
+    wedln_update_cursor(wedln, tageom.w);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Draw */
+
+
+void wedln_draw_completions(WEdln *wedln, bool complete)
+{
+    WRectangle geom;
+    
+    if(wedln->compl_list.strs!=NULL && WEDLN_BRUSH(wedln)!=NULL){
+        const char *style=(REGION_IS_ACTIVE(wedln) 
+                           ? "active" 
+                           : "inactive");
+        const char *selstyle=(REGION_IS_ACTIVE(wedln) 
+                              ? "active-selection" 
+                              : "inactive-selection");
+        
+        get_completions_geom(wedln, G_CURRENT, &geom);
+        
+        draw_listing(WEDLN_BRUSH(wedln), &geom, &(wedln->compl_list), 
+                     complete, style, selstyle);
+    }
+}
+
+
+void wedln_draw_textarea(WEdln *wedln)    
+{
+    WRectangle geom;
+    int ty;
+    const char *style=(REGION_IS_ACTIVE(wedln) ? "active" : "inactive");
+
+    if(WEDLN_BRUSH(wedln)==NULL)
+        return;
+    
+    get_outer_geom(wedln, G_CURRENT, &geom);
+    
+    /*grbrush_begin(WEDLN_BRUSH(wedln), &geom, GRBRUSH_AMEND);*/
+    
+    grbrush_draw_border(WEDLN_BRUSH(wedln), &geom, style);
+
+    get_inner_geom(wedln, G_CURRENT, &geom);
+
+    ty=calc_text_y(wedln, &geom);
+
+    if(wedln->prompt!=NULL){
+        const char *promptstyle=(REGION_IS_ACTIVE(wedln) 
+                                 ? "active-prompt" 
+                                 : "inactive-prompt");
+        grbrush_draw_string(WEDLN_BRUSH(wedln), geom.x, ty,
+                            wedln->prompt, wedln->prompt_len, TRUE, 
+                            promptstyle);
+    }
+    
+    if(wedln->info!=NULL){
+        int x=geom.x+geom.w-wedln->info_w;
+        const char *promptstyle=(REGION_IS_ACTIVE(wedln) 
+                                 ? "active-prompt-info" 
+                                 : "inactive-prompt-info");
+        grbrush_draw_string(WEDLN_BRUSH(wedln), x, ty,
+                            wedln->info, wedln->info_len, TRUE, 
+                            promptstyle);
+    }
+        
+    get_textarea_geom(wedln, G_CURRENT, &geom);
+    
+    wedln_draw_str_box(wedln, &geom, wedln->vstart, wedln->edln.p, 0,
+                       wedln->edln.point, wedln->edln.mark);
+
+    /*grbrush_end(WEDLN_BRUSH(wedln));*/
+}
+
+
+void wedln_draw(WEdln *wedln, bool complete)
+{
+    WRectangle g;
+    int f=(complete ? 0 : GRBRUSH_NO_CLEAR_OK);
+    
+    if(WEDLN_BRUSH(wedln)==NULL)
+        return;
+    
+    get_geom(wedln, G_CURRENT, &g);
+    
+    grbrush_begin(WEDLN_BRUSH(wedln), &g, f);
+    
+    wedln_draw_completions(wedln, FALSE);
+    wedln_draw_textarea(wedln);
+    
+    grbrush_end(WEDLN_BRUSH(wedln));
+}
+
+
+/*}}} */
+
+
+/*{{{ Info area */
+
+
+static void wedln_set_info(WEdln *wedln, const char *info)
+{
+    WRectangle tageom;
+    char *p;
+    
+    if(wedln->info!=NULL){
+        free(wedln->info);
+        wedln->info=NULL;
+        wedln->info_w=0;
+        wedln->info_len=0;
+    }
+    
+    if(info!=NULL){
+        wedln->info=scat3("  [", info, "]");
+        if(wedln->info!=NULL){
+            wedln->info_len=strlen(wedln->info);
+            if(WEDLN_BRUSH(wedln)!=NULL){
+                wedln->info_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
+                                                     wedln->info, 
+                                                     wedln->info_len);
+            }
+        }
+    }
+    
+    get_textarea_geom(wedln, G_CURRENT, &tageom);
+    wedln_update_cursor(wedln, tageom.w);
+    
+    wedln_draw_textarea(wedln);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Completions */
+
+
+static void wedln_show_completions(WEdln *wedln, char **strs, int nstrs,
+                                   int selected)
+{
+    int w=REGION_GEOM(wedln).w;
+    int h=REGION_GEOM(wedln).h;
+    
+    if(WEDLN_BRUSH(wedln)==NULL)
+        return;
+    
+    setup_listing(&(wedln->compl_list), strs, nstrs, FALSE);
+    wedln->compl_list.selected_str=selected;
+
+    input_refit((WInput*)wedln);
+    if(w==REGION_GEOM(wedln).w && h==REGION_GEOM(wedln).h)
+        wedln_draw_completions(wedln, TRUE);
+}
+
+
+void wedln_hide_completions(WEdln *wedln)
+{
+    if(wedln->compl_list.strs!=NULL){
+        deinit_listing(&(wedln->compl_list));
+        input_refit((WInput*)wedln);
+    }
+}
+    
+
+void wedln_scrollup_completions(WEdln *wedln)
+{
+    if(wedln->compl_list.strs==NULL)
+        return;
+    if(scrollup_listing(&(wedln->compl_list)))
+        wedln_draw_completions(wedln, TRUE);
+}
+
+
+void wedln_scrolldown_completions(WEdln *wedln)
+{
+    if(wedln->compl_list.strs==NULL)
+        return;
+    if(scrolldown_listing(&(wedln->compl_list)))
+        wedln_draw_completions(wedln, TRUE);
+}
+
+
+static int update_nocompl=0;
+
+
+static void free_completions(char **ptr, int i)
+{
+    while(i>0){
+        i--;
+        free(ptr[i]);
+    }
+    free(ptr);
+}
+
+
+bool wedln_do_set_completions(WEdln *wedln, char **ptr, int n, 
+                              char *beg, char *end, int cycle,
+                              bool nosort)
+{    
+    int sel=-1;
+    
+    if(wedln->compl_beg!=NULL)
+        free(wedln->compl_beg);
+    
+    if(wedln->compl_end!=NULL)
+        free(wedln->compl_end);
+    
+    wedln->compl_beg=beg;
+    wedln->compl_end=end;
+    wedln->compl_current_id=-1;
+    
+    n=edln_do_completions(&(wedln->edln), ptr, n, beg, end, 
+                          !mod_query_config.autoshowcompl, nosort);
+
+    if(mod_query_config.autoshowcompl && n>0 && cycle!=0){
+        update_nocompl++;
+        sel=(cycle>0 ? 0 : n-1);
+        edln_set_completion(&(wedln->edln), ptr[sel], beg, end);
+        update_nocompl--;
+    }
+
+    if(n>1 || (mod_query_config.autoshowcompl && n>0)){
+        wedln_show_completions(wedln, ptr, n, sel);
+        return TRUE;
+    }
+    
+    free_completions(ptr, n);
+    
+    return FALSE;
+}
+
+
+void wedln_set_completions(WEdln *wedln, ExtlTab completions, int cycle)
+{
+    int n=0, i=0;
+    char **ptr=NULL, *beg=NULL, *end=NULL, *p=NULL;
+    
+    n=extl_table_get_n(completions);
+    
+    if(n==0){
+        wedln_hide_completions(wedln);
+        return;
+    }
+    
+    ptr=ALLOC_N(char*, n);
+    if(ptr==NULL)
+        goto allocfail;
+
+    for(i=0; i<n; i++){
+        if(!extl_table_geti_s(completions, i+1, &p)){
+            goto allocfail;
+        }
+        ptr[i]=p;
+    }
+
+    extl_table_gets_s(completions, "common_beg", &beg);
+    extl_table_gets_s(completions, "common_end", &end);
+    
+    if(wedln_do_set_completions(wedln, ptr, n, beg, end, cycle, FALSE))
+        return;
+    
+allocfail:
+    wedln_hide_completions(wedln);
+    free_completions(ptr, i);
+    while(i>0){
+        i--;
+        /* edln_do_completions may NULL things */
+        if(ptr[i]!=NULL)
+            free(ptr[i]);
+    }
+    free(ptr);
+}
+    
+
+static void wedln_do_select_completion(WEdln *wedln, int n)
+{
+    bool complredraw=listing_select(&(wedln->compl_list), n);
+    wedln_draw_completions(wedln, complredraw);
+
+    update_nocompl++;
+    edln_set_completion(&(wedln->edln), wedln->compl_list.strs[n],
+                        wedln->compl_beg, wedln->compl_end);
+    update_nocompl--;
+}
+
+
+
+static ExtlExportedFn *sc_safe_fns[]={
+    (ExtlExportedFn*)&complproxy_set_completions,
+    NULL
+};
+
+
+static ExtlSafelist sc_safelist=EXTL_SAFELIST_INIT(sc_safe_fns);
+
+
+static int wedln_alloc_compl_id(WEdln *wedln)
+{
+    int id=wedln->compl_waiting_id+1;
+    wedln->compl_waiting_id=maxof(0, wedln->compl_waiting_id+1);
+    return id;
+}
+
+static bool wedln_do_call_completor(WEdln *wedln, int id, int cycle)
+{
+    if(wedln->compl_history_mode){
+        uint n;
+        char **h=NULL;
+        
+        wedln->compl_waiting_id=id;
+
+        n=edln_history_matches(&wedln->edln, &h);
+        
+        if(n==0){
+            wedln_hide_completions(wedln);
+            return FALSE;
+        }
+        
+        if(wedln_do_set_completions(wedln, h, n, NULL, NULL, cycle, TRUE)){
+            wedln->compl_current_id=id;
+            return TRUE;
+        }
+        
+        return FALSE;
+    }else{
+        const char *p=wedln->edln.p;
+        int point=wedln->edln.point;
+        WComplProxy *proxy=create_complproxy(wedln, id, cycle);
+        
+        if(proxy==NULL)
+            return FALSE;
+        
+        /* Set Lua-side to own the proxy: it gets freed by Lua's GC */
+        ((Obj*)proxy)->flags|=OBJ_EXTL_OWNED;
+        
+        if(p==NULL){
+            p="";
+            point=0;
+        }
+        
+        extl_protect(&sc_safelist);
+        extl_call(wedln->completor, "osi", NULL, proxy, p, point);
+        extl_unprotect(&sc_safelist);
+        
+        return TRUE;
+    }
+}
+
+
+static void timed_complete(WTimer *tmr, Obj *obj)
+{
+    WEdln *wedln=(WEdln*)obj;
+    
+    if(wedln!=NULL){
+        int id=wedln->compl_timed_id;
+        wedln->compl_timed_id=-1;
+        if(id==wedln->compl_waiting_id && id>=0)
+            wedln_do_call_completor((WEdln*)obj, id, FALSE);
+    }
+}
+
+
+/*EXTL_DOC
+ * Select next completion.
+ */
+EXTL_EXPORT_MEMBER
+bool wedln_next_completion(WEdln *wedln)
+{
+    int n=-1;
+
+    if(wedln->compl_current_id!=wedln->compl_waiting_id)
+        return FALSE;
+    
+    if(wedln->compl_list.nstrs<=0)
+        return FALSE;
+    
+    if(wedln->compl_list.selected_str<0 ||
+       wedln->compl_list.selected_str+1>=wedln->compl_list.nstrs){
+        n=0;
+    }else{
+        n=wedln->compl_list.selected_str+1;
+    }
+    
+    if(n!=wedln->compl_list.selected_str)
+        wedln_do_select_completion(wedln, n);
+    
+    return TRUE;
+}
+
+
+/*EXTL_DOC
+ * Select previous completion.
+ */
+EXTL_EXPORT_MEMBER
+bool wedln_prev_completion(WEdln *wedln)
+{
+    int n=-1;
+
+    if(wedln->compl_current_id!=wedln->compl_waiting_id)
+        return FALSE;
+    
+    if(wedln->compl_list.nstrs<=0)
+        return FALSE;
+    
+    if(wedln->compl_list.selected_str<=0){
+        n=wedln->compl_list.nstrs-1;
+    }else{
+        n=wedln->compl_list.selected_str-1;
+    }
+
+    if(n!=wedln->compl_list.selected_str)
+        wedln_do_select_completion(wedln, n);
+    
+    return TRUE;
+}
+
+
+/*EXTL_DOC
+ * Call completion handler with the text between the beginning of line and
+ * current cursor position, or select next/previous completion from list if in
+ * auto-show-completions mode and \var{cycle} is set to ``next'' or ``prev'',
+ * respectively. The \var{mode} may be ``history'' or ``normal''. If it is 
+ * not set, the previous mode is used. Normally next entry is not cycled to
+ * despite the setting of \var{cycle} if mode switch occurs. To override
+ * this, use ``next-always'' and ``prev-always'' for \var{cycle}.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_complete(WEdln *wedln, const char *cycle, const char *mode)
+{
+    bool valid=TRUE;
+    int cyclei=0;
+    
+    if(mode!=NULL){
+        if(strcmp(mode, "history")==0){
+            valid=wedln->compl_history_mode;
+            wedln->compl_history_mode=TRUE;
+        }else if(strcmp(mode, "normal")==0){
+            valid=!wedln->compl_history_mode;
+            wedln->compl_history_mode=FALSE;
+        }
+        if(!valid){
+            wedln_set_info(wedln,
+                           (wedln->compl_history_mode
+                            ? TR("history")
+                            : NULL));
+        }
+    }
+    
+    if(cycle!=NULL){
+        if((valid && strcmp(cycle, "next")==0) || 
+           strcmp(cycle, "next-always")==0){
+            cyclei=1;
+        }else if((valid && strcmp(cycle, "prev")==0) || 
+                 strcmp(cycle, "prev-always")==0){
+            cyclei=-1;
+        }
+    }
+    
+    if(valid && cyclei!=0 && mod_query_config.autoshowcompl && 
+       wedln->compl_list.nstrs>0){
+        if(cyclei>0)
+            wedln_next_completion(wedln);
+        else
+            wedln_prev_completion(wedln);
+    }else{
+        int oldid=wedln->compl_waiting_id;
+    
+        if(!wedln_do_call_completor(wedln, wedln_alloc_compl_id(wedln), 
+                                    cyclei)){
+            wedln->compl_waiting_id=oldid;
+        }
+    }
+}
+
+
+/*EXTL_DOC
+ * Get history completion mode.
+ */
+EXTL_EXPORT_MEMBER
+bool wedln_is_histcompl(WEdln *wedln)
+{
+    return wedln->compl_history_mode;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Update handler */
+
+
+static void wedln_update_handler(WEdln *wedln, int from, int flags)
+{
+    WRectangle geom;
+    
+    if(WEDLN_BRUSH(wedln)==NULL)
+        return;
+    
+    get_textarea_geom(wedln, G_CURRENT, &geom);
+    
+    if(flags&EDLN_UPDATE_NEW)
+        wedln->vstart=0;
+    
+    if(flags&EDLN_UPDATE_MOVED){
+        if(wedln_update_cursor(wedln, geom.w))
+            from=wedln->vstart;
+    }
+    
+    from=maxof(0, from-wedln->vstart);
+
+    wedln_draw_str_box(wedln, &geom, wedln->vstart, wedln->edln.p, from,
+                       wedln->edln.point, wedln->edln.mark);
+    
+    if(update_nocompl==0 &&
+       mod_query_config.autoshowcompl && 
+       flags&EDLN_UPDATE_CHANGED){
+        wedln->compl_current_id=-1; /* invalidate */
+        if(wedln->autoshowcompl_timer==NULL)
+            wedln->autoshowcompl_timer=create_timer();
+        if(wedln->autoshowcompl_timer!=NULL){
+            wedln->compl_timed_id=wedln_alloc_compl_id(wedln);
+            timer_set(wedln->autoshowcompl_timer, 
+                      mod_query_config.autoshowcompl_delay,
+                      timed_complete, (Obj*)wedln);
+        }
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init, deinit and config update */
+
+
+static bool wedln_init_prompt(WEdln *wedln, const char *prompt)
+{
+    char *p;
+    
+    if(prompt!=NULL){
+        p=scat(prompt, "  ");
+    
+        if(p==NULL)
+            return FALSE;
+
+        wedln->prompt=p;
+        wedln->prompt_len=strlen(p);
+    }else{
+        wedln->prompt=NULL;
+        wedln->prompt_len=0;
+    }
+    wedln->prompt_w=0;
+    
+    return TRUE;
+}
+
+
+static bool wedln_init(WEdln *wedln, WWindow *par, const WFitParams *fp, 
+                       WEdlnCreateParams *params)
+{
+    wedln->vstart=0;
+
+    if(!wedln_init_prompt(wedln, params->prompt))
+        return FALSE;
+    
+    if(!edln_init(&(wedln->edln), params->dflt)){
+        free(wedln->prompt);
+        return FALSE;
+    }
+    
+    wedln->handler=extl_fn_none();
+    wedln->completor=extl_fn_none();
+    
+    wedln->edln.uiptr=wedln;
+    wedln->edln.ui_update=(EdlnUpdateHandler*)wedln_update_handler;
+
+    wedln->autoshowcompl_timer=NULL;
+    
+    init_listing(&(wedln->compl_list));
+
+    wedln->compl_waiting_id=-1;
+    wedln->compl_current_id=-1;
+    wedln->compl_timed_id=-1;
+    wedln->compl_beg=NULL;
+    wedln->compl_end=NULL;
+    wedln->compl_tab=FALSE;
+    wedln->compl_history_mode=FALSE;
+    
+    wedln->info=NULL;
+    wedln->info_len=0;
+    wedln->info_w=0;
+    
+    wedln->cycle_bindmap=NULL;
+    
+    if(!input_init((WInput*)wedln, par, fp)){
+        edln_deinit(&(wedln->edln));
+        free(wedln->prompt);
+        return FALSE;
+    }
+
+    window_create_xic(&wedln->input.win);
+    
+    wedln->handler=extl_ref_fn(params->handler);
+    wedln->completor=extl_ref_fn(params->completor);
+
+    region_add_bindmap((WRegion*)wedln, mod_query_wedln_bindmap);
+    
+    return TRUE;
+}
+
+
+WEdln *create_wedln(WWindow *par, const WFitParams *fp,
+                    WEdlnCreateParams *params)
+{
+    CREATEOBJ_IMPL(WEdln, wedln, (p, par, fp, params));
+}
+
+
+static void wedln_deinit(WEdln *wedln)
+{
+    if(wedln->prompt!=NULL)
+        free(wedln->prompt);
+        
+    if(wedln->info!=NULL)
+        free(wedln->info);
+
+    if(wedln->compl_beg!=NULL)
+        free(wedln->compl_beg);
+
+    if(wedln->compl_end!=NULL)
+        free(wedln->compl_end);
+
+    if(wedln->compl_list.strs!=NULL)
+        deinit_listing(&(wedln->compl_list));
+
+    if(wedln->autoshowcompl_timer!=NULL)
+        destroy_obj((Obj*)wedln->autoshowcompl_timer);
+
+    if(wedln->cycle_bindmap!=NULL)
+        bindmap_destroy(wedln->cycle_bindmap);
+    
+    extl_unref_fn(wedln->completor);
+    extl_unref_fn(wedln->handler);
+    
+    edln_deinit(&(wedln->edln));
+    input_deinit((WInput*)wedln);
+}
+
+
+static void wedln_do_finish(WEdln *wedln)
+{
+    ExtlFn handler;
+    char *p;
+    
+    handler=wedln->handler;
+    wedln->handler=extl_fn_none();
+    p=edln_finish(&(wedln->edln));
+    
+    if(region_manager_allows_destroying((WRegion*)wedln))
+       destroy_obj((Obj*)wedln);
+    
+    if(p!=NULL)
+        extl_call(handler, "s", NULL, p);
+    
+    free(p);
+    extl_unref_fn(handler);
+}
+
+
+/*EXTL_DOC
+ * Close \var{wedln} and call any handlers.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_finish(WEdln *wedln)
+{
+    mainloop_defer_action((Obj*)wedln, (WDeferredAction*)wedln_do_finish);
+}
+
+
+/*}}}*/
+
+
+/*{{{ The rest */
+
+
+/*EXTL_DOC
+ * Request selection from application holding such.
+ * 
+ * Note that this function is asynchronous; the selection will not
+ * actually be inserted before Ion receives it. This will be no
+ * earlier than Ion return to its main loop.
+ */
+EXTL_EXPORT_MEMBER
+void wedln_paste(WEdln *wedln)
+{
+    ioncore_request_selection_for(wedln->input.win.win);
+}
+
+
+void wedln_insstr(WEdln *wedln, const char *buf, size_t n)
+{
+    edln_insstr_n(&(wedln->edln), buf, n, TRUE, TRUE);
+}
+
+
+static const char *wedln_style(WEdln *wedln)
+{
+    return "input-edln";
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab wedln_dynfuntab[]={
+    {window_draw, wedln_draw},
+    {input_calc_size, wedln_calc_size},
+    {input_scrollup, wedln_scrollup_completions},
+    {input_scrolldown, wedln_scrolldown_completions},
+    {window_insstr, wedln_insstr},
+    {(DynFun*)input_style, (DynFun*)wedln_style},
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WEdln, WInput, wedln_deinit, wedln_dynfuntab);
+
+    
+/*}}}*/
diff --git a/mod_query/wedln.h b/mod_query/wedln.h
new file mode 100644 (file)
index 0000000..40c0529
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * ion/mod_query/wedln.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_WEDLN_H
+#define ION_MOD_QUERY_WEDLN_H
+
+#include <libtu/obj.h>
+#include <libextl/extl.h>
+#include <libmainloop/signal.h>
+#include <ioncore/common.h>
+#include <ioncore/window.h>
+#include <ioncore/xic.h>
+#include <ioncore/rectangle.h>
+#include <ioncore/binding.h>
+#include "listing.h"
+#include "input.h"
+#include "edln.h"
+
+INTRCLASS(WEdln);
+INTRSTRUCT(WEdlnCreateParams);
+
+DECLSTRUCT(WEdlnCreateParams){
+    const char *prompt;
+    const char *dflt;
+    ExtlFn handler;
+    ExtlFn completor;
+};
+
+
+DECLCLASS(WEdln){
+    WInput input;
+
+    Edln edln;
+
+    char *prompt;
+    int prompt_len;
+    int prompt_w;
+    
+    char *info;
+    int info_len;
+    int info_w;
+    
+    int vstart;
+    
+    ExtlFn handler;
+    ExtlFn completor;
+    
+    WTimer *autoshowcompl_timer;
+    
+    WListing compl_list;
+    char *compl_beg;
+    char *compl_end;
+    int compl_waiting_id;
+    int compl_current_id;
+    int compl_timed_id;
+    uint compl_tab:1;
+    uint compl_history_mode:1;
+    
+    WBindmap *cycle_bindmap;
+};
+
+extern WEdln *create_wedln(WWindow *par, const WFitParams *fp,
+                           WEdlnCreateParams *p);
+extern void wedln_finish(WEdln *wedln);
+extern void wedln_paste(WEdln *wedln);
+extern void wedln_draw(WEdln *wedln, bool complete);
+extern void wedln_set_completions(WEdln *wedln, ExtlTab completions,
+                                  bool autoshow_select_first);
+extern void wedln_hide_completions(WEdln *wedln);
+extern bool wedln_set_histcompl(WEdln *wedln, int sp);
+extern bool wedln_get_histcompl(WEdln *wedln);
+
+#endif /* ION_MOD_QUERY_WEDLN_H */
diff --git a/mod_query/wmessage.c b/mod_query/wmessage.c
new file mode 100644 (file)
index 0000000..aa6002d
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * ion/mod_query/wmessage.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <ioncore/common.h>
+#include <libtu/objp.h>
+#include <ioncore/strings.h>
+#include <ioncore/global.h>
+#include <ioncore/event.h>
+#include "wmessage.h"
+#include "inputp.h"
+
+
+#define WMSG_BRUSH(WMSG) ((WMSG)->input.brush)
+#define WMSG_WIN(WMSG) ((WMSG)->input.win.win)
+
+
+/*{{{ Sizecalc */
+
+
+static void get_geom(WMessage *wmsg, bool max, WRectangle *geom)
+{
+    if(max){
+        geom->w=wmsg->input.last_fp.g.w;
+        geom->h=wmsg->input.last_fp.g.h;
+    }else{
+        geom->w=REGION_GEOM(wmsg).w;
+        geom->h=REGION_GEOM(wmsg).h;
+    }
+    geom->x=0;
+    geom->y=0;
+}
+
+
+static void wmsg_calc_size(WMessage *wmsg, WRectangle *geom)
+{
+    WRectangle max_geom=*geom;
+    GrBorderWidths bdw;
+    int h=16;
+
+    if(WMSG_BRUSH(wmsg)!=NULL){
+        WRectangle g;
+        g.w=max_geom.w;
+        g.h=max_geom.h;
+        g.x=0;
+        g.y=0;
+        
+        fit_listing(WMSG_BRUSH(wmsg), &g, &(wmsg->listing));
+
+        grbrush_get_border_widths(WMSG_BRUSH(wmsg), &bdw);
+        
+        h=bdw.top+bdw.bottom+wmsg->listing.toth;
+    }
+    
+    if(h>max_geom.h || !(wmsg->input.last_fp.mode&REGION_FIT_BOUNDS))
+        h=max_geom.h;
+    
+    geom->h=h;
+    geom->w=max_geom.w;
+    geom->y=max_geom.y+max_geom.h-geom->h;
+    geom->x=max_geom.x;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Draw */
+
+
+static void wmsg_draw(WMessage *wmsg, bool complete)
+{
+    const char *style=(REGION_IS_ACTIVE(wmsg) ? "active" : "inactive");
+    WRectangle geom;
+    
+    if(WMSG_BRUSH(wmsg)==NULL)
+        return;
+    
+    get_geom(wmsg, FALSE, &geom);
+    
+    grbrush_begin(WMSG_BRUSH(wmsg), &geom, 
+                  (complete ? 0 : GRBRUSH_NO_CLEAR_OK));
+    
+    draw_listing(WMSG_BRUSH(wmsg), &geom, &(wmsg->listing), 
+                 FALSE, style, style);
+    
+    grbrush_end(WMSG_BRUSH(wmsg));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Scroll */
+
+
+static void wmsg_scrollup(WMessage *wmsg)
+{
+    if(scrollup_listing(&(wmsg->listing)))
+        wmsg_draw(wmsg, TRUE);
+}
+
+
+static void wmsg_scrolldown(WMessage *wmsg)
+{
+    if(scrolldown_listing(&(wmsg->listing)))
+        wmsg_draw(wmsg, TRUE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init, deinit draw config update */
+
+
+static bool wmsg_init(WMessage *wmsg, WWindow *par, const WFitParams *fp,
+                      const char *msg)
+{
+    char **ptr;
+    int k, n=0;
+    char *cmsg;
+    const char *p;
+    size_t l;
+    
+    p=msg;
+    while(1){
+        n=n+1;
+        p=strchr(p, '\n');
+        if(p==NULL || *(p+1)=='\0')
+            break;
+        p=p+1;
+    }
+    
+    if(n==0)
+        return FALSE;
+        
+    ptr=ALLOC_N(char*, n);
+    
+    if(ptr==NULL)
+        return FALSE;
+    
+    for(k=0; k<n; k++)
+        ptr[k]=NULL;
+    
+    p=msg;
+    k=0;
+    while(k<n){
+        l=strcspn(p, "\n");
+        cmsg=ALLOC_N(char, l+1);
+        if(cmsg==NULL){
+            while(k>0){
+                k--;
+                free(ptr[k]);
+            }
+            free(ptr);
+            return FALSE;
+        }
+        strncpy(cmsg, p, l);
+        cmsg[l]='\0';
+        ptr[k]=cmsg;
+        k++;
+        if(p[l]=='\0')
+            break;
+        p=p+l+1;
+    }
+    
+    init_listing(&(wmsg->listing));
+    setup_listing(&(wmsg->listing), ptr, k, TRUE);
+    
+    if(!input_init((WInput*)wmsg, par, fp)){
+        deinit_listing(&(wmsg->listing));
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+WMessage *create_wmsg(WWindow *par, const WFitParams *fp, const char *msg)
+{
+    CREATEOBJ_IMPL(WMessage, wmsg, (p, par, fp, msg));
+}
+
+
+static void wmsg_deinit(WMessage *wmsg)
+{
+    if(wmsg->listing.strs!=NULL)
+        deinit_listing(&(wmsg->listing));
+
+    input_deinit((WInput*)wmsg);
+}
+
+
+static const char *wmsg_style(WMessage *wmsg)
+{
+    return "input-message";
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab wmsg_dynfuntab[]={
+    {window_draw,        wmsg_draw},
+    {input_calc_size,     wmsg_calc_size},
+    {input_scrollup,     wmsg_scrollup},
+    {input_scrolldown,    wmsg_scrolldown},
+    {(DynFun*)input_style,
+     (DynFun*)wmsg_style},
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WMessage, WInput, wmsg_deinit, wmsg_dynfuntab);
+
+    
+/*}}}*/
diff --git a/mod_query/wmessage.h b/mod_query/wmessage.h
new file mode 100644 (file)
index 0000000..bc05b52
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * ion/mod_query/wmessage.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_QUERY_WMESSAGE_H
+#define ION_MOD_QUERY_WMESSAGE_H
+
+#include <ioncore/common.h>
+#include <libtu/obj.h>
+#include <ioncore/window.h>
+#include <ioncore/rectangle.h>
+#include "listing.h"
+#include "input.h"
+
+INTRCLASS(WMessage);
+
+DECLCLASS(WMessage){
+    WInput input;
+    WListing listing;
+};
+
+extern WMessage *create_wmsg(WWindow *par, const WFitParams *fp,
+                             const char *msg);
+
+#endif /* ION_MOD_QUERY_WMESSAGE_H */
diff --git a/mod_sm/Makefile b/mod_sm/Makefile
new file mode 100644 (file)
index 0000000..c33f819
--- /dev/null
@@ -0,0 +1,27 @@
+##
+## Session management support module Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+LIBS += $(X11_LIBS) -lSM -lICE
+
+SOURCES=sm.c sm_matchwin.c sm_session.c
+
+MAKE_EXPORTS=mod_sm
+
+MODULE=mod_sm
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: module_install
diff --git a/mod_sm/sm.c b/mod_sm/sm.c
new file mode 100644 (file)
index 0000000..6971f7d
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * ion/mod_sm/sm.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libtu/misc.h>
+#include <libtu/parser.h>
+#include <libtu/tokenizer.h>
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/clientwin.h>
+#include <ioncore/saveload.h>
+#include <ioncore/property.h>
+#include <libextl/readconfig.h>
+#include <ioncore/manage.h>
+#include <ioncore/ioncore.h>
+#include <ioncore/exec.h>
+#include "sm_matchwin.h"
+#include "sm_session.h"
+#include "exports.h"
+
+
+/*{{{ Module information */
+
+
+#include "../version.h"
+
+char mod_sm_ion_api_version[]=ION_API_VERSION;
+
+
+/*}}}*/
+
+
+/*{{{ Manage callback */
+
+
+static bool sm_do_manage(WClientWin *cwin, const WManageParams *param)
+{
+    int transient_mode=TRANSIENT_MODE_OFF;
+    WPHolder *ph;
+    bool ret;
+    
+    if(param->tfor!=NULL)
+        return FALSE;
+    
+    ph=mod_sm_match_cwin_to_saved(cwin);
+    if(ph==NULL)
+        return FALSE;
+    
+    ret=pholder_attach(ph, 0, (WRegion*)cwin);
+    
+    destroy_obj((Obj*)ph);
+    
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init/deinit */
+
+
+static void mod_sm_set_sessiondir()
+{
+    const char *smdir=NULL, *id=NULL;
+    char *tmp;
+    bool ok=FALSE;
+    
+    smdir=getenv("SM_SAVE_DIR");
+    id=getenv("GNOME_DESKTOP_SESSION_ID");
+
+    /* Running under SM, try to use a directory specific
+     * to the session.
+     */
+    if(smdir!=NULL){
+        tmp=scat(smdir, "/ion3"); /* TODO: pwm<=>ion! */
+    }else if(id!=NULL){
+        tmp=scat("gnome-session-", id);
+        if(tmp!=NULL){
+            char *p=tmp;
+            while(1){
+                p=strpbrk(p, "/ :?*");
+                if(p==NULL)
+                    break;
+                *p='-';
+                p++;
+            }
+        }
+    }else{
+        tmp=scopy("default-session-sm");
+    }
+        
+    if(tmp!=NULL){
+        ok=extl_set_sessiondir(tmp);
+        free(tmp);
+    }
+    
+    if(!ok)
+        warn(TR("Failed to set session directory."));
+}
+
+
+
+void mod_sm_deinit()
+{
+    ioncore_set_smhook(NULL);
+
+    hook_remove(clientwin_do_manage_alt, (WHookDummy*)sm_do_manage);
+
+    ioncore_set_sm_callbacks(NULL, NULL);
+    
+    mod_sm_unregister_exports();
+    
+    mod_sm_close();
+}
+
+
+int mod_sm_init()
+{
+    if(ioncore_g.sm_client_id!=NULL)
+        mod_sm_set_ion_id(ioncore_g.sm_client_id);
+    
+    if(!mod_sm_init_session())
+        goto err;
+
+    if(extl_sessiondir()==NULL)
+        mod_sm_set_sessiondir();
+    
+    if(!mod_sm_register_exports())
+        goto err;
+
+    ioncore_set_sm_callbacks(mod_sm_add_match, mod_sm_get_configuration);
+    
+    hook_add(clientwin_do_manage_alt, (WHookDummy*)sm_do_manage);
+
+    ioncore_set_smhook(mod_sm_smhook);
+    
+    return TRUE;
+    
+err:
+    mod_sm_deinit();
+    return FALSE;
+}
+
+
+/*}}}*/
+
diff --git a/mod_sm/sm_matchwin.c b/mod_sm/sm_matchwin.c
new file mode 100644 (file)
index 0000000..d64f671
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * ion/mod_sm/sm_mathcwin.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006. 
+ * 
+ * Based on the code of the 'sm' module for Ion1 by an unknown contributor.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <X11/Xatom.h>
+
+#include <libmainloop/signal.h>
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/exec.h>
+#include <ioncore/names.h>
+#include <ioncore/property.h>
+
+#include "sm_matchwin.h"
+
+#define TIME_OUT 60000
+
+static WWinMatch *match_list=NULL;
+static WTimer *purge_timer=NULL;
+
+
+char *mod_sm_get_window_role(Window window)
+{
+    Atom atom;
+    XTextProperty tp;
+    
+    atom=XInternAtom(ioncore_g.dpy, "WM_WINDOW_ROLE", False);
+    
+    if(XGetTextProperty(ioncore_g.dpy, window, &tp, atom))
+    {
+        if(tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
+            return((char *)tp.value);
+    }
+    
+    return NULL;
+}
+
+Window mod_sm_get_client_leader(Window window)
+{
+    Window client_leader = 0;
+    Atom atom;
+    Atom actual_type;
+    int actual_format;
+    unsigned long nitems;
+    unsigned long bytes_after;
+    unsigned char *prop = NULL;
+    
+    atom=XInternAtom(ioncore_g.dpy, "WM_CLIENT_LEADER", False);
+    
+    if(XGetWindowProperty(ioncore_g.dpy, window, atom,
+                         0L, 1L, False, AnyPropertyType, &actual_type,
+                         &actual_format, &nitems, &bytes_after,
+                         &prop) == Success)
+    {
+        if(actual_type == XA_WINDOW && actual_format == 32
+           && nitems == 1      && bytes_after == 0)
+            client_leader=*((Window *)prop);
+        XFree(prop);
+    }
+    return client_leader;
+}
+
+char *mod_sm_get_client_id(Window window)
+{
+    char *client_id = NULL;
+    Window client_leader;
+    XTextProperty tp;
+    Atom atom;
+    
+    if((client_leader=mod_sm_get_client_leader(window))!=0){
+        atom=XInternAtom(ioncore_g.dpy, "SM_CLIENT_ID", False);        
+        if (XGetTextProperty (ioncore_g.dpy, client_leader, &tp, atom))
+            if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
+                client_id = (char *) tp.value;
+    }
+    
+    return client_id;
+}
+
+char *mod_sm_get_window_cmd(Window window)
+{
+    char **cmd_argv, *command=NULL;
+    int id, i, len=0, cmd_argc=0;
+    
+    if(XGetCommand(ioncore_g.dpy, window, &cmd_argv, &cmd_argc) && (cmd_argc > 0))
+        ;
+    else if((id=mod_sm_get_client_leader(window)))
+        XGetCommand(ioncore_g.dpy, id, &cmd_argv, &cmd_argc);
+    
+    if(cmd_argc > 0){
+        for(i=0; i < cmd_argc; i++)
+            len+=strlen(cmd_argv[i])+1;
+        command=ALLOC_N(char, len+1);
+        strcpy(command, cmd_argv[0]);
+        for(i=1; i < cmd_argc; i++){
+            strcat(command, " ");
+            strcat(command, cmd_argv[i]);
+        }
+        XFreeStringList(cmd_argv);
+    }
+    
+    return command;
+}
+
+static void free_win_match(WWinMatch *match)
+{      
+    UNLINK_ITEM(match_list, match, next, prev);
+    
+    if(match->pholder!=NULL)
+        destroy_obj((Obj*)match->pholder);
+    
+    if(match->client_id)
+        free(match->client_id);
+    if(match->window_role)
+        free(match->window_role);
+    if(match->wclass)
+        free(match->wclass);
+    if(match->wm_name)
+        free(match->wm_name);
+    if(match->wm_cmd)
+        free(match->wm_cmd);           
+    free(match);
+}
+
+static void mod_sm_purge_matches(WTimer *timer)
+{
+    assert(timer==purge_timer);
+    purge_timer=NULL;
+    destroy_obj((Obj*)timer);
+    
+#ifdef DEBUG
+    warn("purging remaining matches\n");
+#endif
+    while(match_list)
+        free_win_match(match_list);
+}
+
+void mod_sm_start_purge_timer()
+{
+    if(purge_timer==NULL)
+        purge_timer=create_timer();
+    if(purge_timer!=NULL){
+        timer_set(purge_timer, TIME_OUT, 
+                  (WTimerHandler*)mod_sm_purge_matches, NULL);
+    }
+}
+
+void mod_sm_register_win_match(WWinMatch *match)
+{
+    LINK_ITEM(match_list, match, next, prev);
+}
+
+bool mod_sm_have_match_list()
+{
+    if(match_list!=NULL)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+#define xstreq(a,b) (a && b && (strcmp(a,b)==0))
+
+/* Tries to match a window against a list of loaded matches */
+
+static WWinMatch *match_cwin(WClientWin *cwin)
+{
+    WWinMatch *match=match_list;
+    int win_match;
+    XClassHint clss;
+    char *client_id=mod_sm_get_client_id(cwin->win);
+    char *window_role=mod_sm_get_window_role(cwin->win);
+    char *wm_cmd=mod_sm_get_window_cmd(cwin->win);
+    char **wm_name=NULL;
+    int n;
+    
+    wm_name=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n);
+    if(n<=0)
+        assert(wm_name==NULL);
+    
+    XGetClassHint(ioncore_g.dpy, cwin->win, &clss);
+    
+    for(; match!=NULL; match=match->next){
+        
+        win_match=0;
+        
+        if(client_id || match->client_id){
+            if(xstreq(match->client_id, client_id)){
+                win_match+=2;
+                if(xstreq(match->window_role, window_role))
+                    win_match++;
+            }
+        }
+        if(win_match<3){
+            if(xstreq(match->wclass, clss.res_class) && xstreq(match->winstance, clss.res_name)){
+                win_match++;
+                if(win_match<3){
+                    if(xstreq(match->wm_cmd, wm_cmd))
+                        win_match++;
+                    if(wm_name!=NULL && *wm_name!=NULL && 
+                       xstreq(match->wm_name, *wm_name)){
+                        win_match++;
+                    }
+                }
+            }
+        }
+        if(win_match>2)
+            break;             
+    }
+    XFree(client_id);
+    XFree(window_role);
+    XFreeStringList(wm_name);
+    free(wm_cmd);
+    return match;
+}
+
+/* Returns frame_id of a match. Called from add_clientwin_alt in sm.c */
+
+WPHolder *mod_sm_match_cwin_to_saved(WClientWin *cwin)
+{
+    WWinMatch *match=NULL;
+    WPHolder *ph=NULL;
+
+    if((match=match_cwin(cwin))){
+        ph=match->pholder;
+        match->pholder=NULL;
+        free_win_match(match);
+    }
+    
+    return ph;
+}
+
+
+bool mod_sm_add_match(WPHolder *ph, ExtlTab tab)
+{
+    WWinMatch *m=NULL;
+    
+    m=ALLOC(WWinMatch);
+    if(m==NULL)
+        return FALSE;
+    
+    m->client_id=NULL;
+    m->window_role=NULL;
+    m->winstance=NULL;
+    m->wclass=NULL;
+    m->wm_name=NULL;
+    m->wm_cmd=NULL;
+    
+    extl_table_gets_s(tab, "mod_sm_client_id", &(m->client_id));
+    extl_table_gets_s(tab, "mod_sm_window_role", &(m->window_role));
+    extl_table_gets_s(tab, "mod_sm_wclass", &(m->wclass));
+    extl_table_gets_s(tab, "mod_sm_winstance", &(m->winstance));
+    extl_table_gets_s(tab, "mod_sm_wm_name", &(m->wm_name));
+    extl_table_gets_s(tab, "mod_sm_wm_cmd", &(m->wm_cmd));
+    
+    m->pholder=ph;
+    
+    mod_sm_register_win_match(m);
+
+    return TRUE;
+}
+
+
+void mod_sm_get_configuration(WClientWin *cwin, ExtlTab tab)
+{
+    XClassHint clss;
+    char *client_id=NULL, *window_role=NULL, *wm_cmd=NULL, **wm_name;
+    int n=0;
+    
+    if((client_id=mod_sm_get_client_id(cwin->win))){
+        extl_table_sets_s(tab, "mod_sm_client_id", client_id);
+        XFree(client_id);
+    }
+     
+    
+    if((window_role=mod_sm_get_window_role(cwin->win))){
+        extl_table_sets_s(tab, "mod_sm_window_role", window_role);
+        XFree(window_role);
+    }
+    
+    if(XGetClassHint(ioncore_g.dpy, cwin->win, &clss) != 0){
+        extl_table_sets_s(tab, "mod_sm_wclass", clss.res_class);
+        extl_table_sets_s(tab, "mod_sm_winstance", clss.res_name);
+    }
+    
+    wm_name=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n);
+
+    if(n>0 && wm_name!=NULL){
+        extl_table_sets_s(tab, "mod_sm_wm_name", *wm_name);
+        XFreeStringList(wm_name);
+        
+    }
+    
+    if((wm_cmd=mod_sm_get_window_cmd(cwin->win))){
+        extl_table_sets_s(tab, "mod_sm_wm_cmd", wm_cmd);
+        free(wm_cmd);
+    }
+}
diff --git a/mod_sm/sm_matchwin.h b/mod_sm/sm_matchwin.h
new file mode 100644 (file)
index 0000000..2dd45f3
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * ion/mod_sm/sm_mathcwin.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006. 
+ * 
+ * Based on the code of the 'sm' module for Ion1 by an unknown contributor.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_SM_MATCHWIN_H
+#define ION_MOD_SM_MATCHWIN_H
+
+#include <libtu/obj.h>
+#include <ioncore/common.h>
+#include <ioncore/pholder.h>
+
+INTRSTRUCT(WWinMatch);
+
+DECLSTRUCT(WWinMatch){
+    WPHolder *pholder;
+    
+    char *client_id;
+    char *window_role;
+    char *wclass;
+    char *winstance;
+    char *wm_name;
+    char *wm_cmd;
+    
+    WWinMatch *next, *prev;
+};
+
+extern WPHolder *mod_sm_match_cwin_to_saved(WClientWin *cwin);
+extern void mod_sm_register_win_match(WWinMatch *match);
+extern char *mod_sm_get_window_cmd(Window window);
+extern char *mod_sm_get_client_id(Window window);
+extern char *mod_sm_get_window_role(Window window);
+extern bool mod_sm_have_match_list();
+extern void mod_sm_start_purge_timer();
+
+extern bool mod_sm_add_match(WPHolder *ph, ExtlTab tab);
+extern void mod_sm_get_configuration(WClientWin *cwin, ExtlTab tab);
+
+#endif /* ION_MOD_SM_MATCHWIN_H */
diff --git a/mod_sm/sm_session.c b/mod_sm/sm_session.c
new file mode 100644 (file)
index 0000000..7472e8b
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * ion/mod_sm/sm_session.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006. 
+ * 
+ * Based on the code of the 'sm' module for Ion1 by an unknown contributor.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libtu/misc.h>
+
+#include <X11/Xlib.h>
+#include <X11/SM/SMlib.h>
+
+#include <libextl/readconfig.h>
+#include <libmainloop/select.h>
+#include <libmainloop/exec.h>
+#include <ioncore/exec.h>
+#include <ioncore/global.h>
+#include <ioncore/ioncore.h>
+#include "sm_session.h"
+
+
+static IceConn ice_sm_conn=NULL;
+static SmcConn sm_conn=NULL;
+static int sm_fd=-1;
+
+static char *sm_client_id=NULL;
+static char restart_hint=SmRestartImmediately;
+
+static Bool sent_save_done=FALSE;
+
+/* Function to be called when sm tells client save is complete */
+static void (*save_complete_fn)();
+
+void mod_sm_set_ion_id(const char *client_id)
+{
+    if(sm_client_id)
+        free(sm_client_id);
+    
+    if(client_id==NULL)
+        sm_client_id=NULL;
+    else
+        sm_client_id=scopy(client_id);
+}
+
+char *mod_sm_get_ion_id()
+{
+    return sm_client_id;
+}
+
+/* Called when there's data to be read.
+ IcePcocessMessages determines message protocol, 
+ unpacks the message and sends it to the client via
+ registered callbacks. */
+
+static void sm_process_messages(int fd, void *data)
+{
+    Bool ret;
+    
+    if(IceProcessMessages(ice_sm_conn, NULL, &ret)==IceProcessMessagesIOError){
+        mod_sm_close();
+    }
+}
+
+/* Callback triggered when an Ice connection is
+ opened or closed. */
+
+static void sm_ice_watch_fd(IceConn conn,
+                            IcePointer client_data,
+                            Bool opening,
+                            IcePointer *watch_data)
+{
+    if(opening){
+        if(sm_fd!=-1){ /* shouldn't happen */
+            warn(TR("Too many ICE connections."));
+        }
+        else{
+            sm_fd=IceConnectionNumber(conn);
+            cloexec_braindamage_fix(sm_fd);
+            mainloop_register_input_fd(sm_fd, NULL, &sm_process_messages);
+        }
+    }
+    else{
+        if (IceConnectionNumber(conn)==sm_fd){
+            mainloop_unregister_input_fd(sm_fd);
+            sm_fd=-1;
+        }
+    }
+}
+
+/* Store restart information and stuff in the session manager */
+
+static void sm_set_some_properties()
+{
+    SmPropValue program_val, userid_val;
+    SmProp program_prop, userid_prop, clone_prop;
+    SmProp *props[3];
+    
+    props[0]=&program_prop;
+    props[1]=&userid_prop;
+    props[2]=&clone_prop;
+    
+    program_val.value=ioncore_g.argv[0];
+    program_val.length=strlen(program_val.value);
+    program_prop.name=SmProgram;       
+    program_prop.type=SmARRAY8;
+    program_prop.num_vals=1;
+    program_prop.vals=&program_val;
+    
+    userid_val.value=getenv("USER");
+    userid_val.length=strlen(userid_val.value);
+    userid_prop.name=SmUserID;
+    userid_prop.type=SmARRAY8;
+    userid_prop.num_vals=1;
+    userid_prop.vals=&userid_val;
+    
+    clone_prop.name=SmCloneCommand;    
+    clone_prop.type=SmLISTofARRAY8;
+    clone_prop.num_vals=1;
+    clone_prop.vals=&program_val;
+    
+    SmcSetProperties(sm_conn,
+                     sizeof(props)/sizeof(props[0]),
+                     (SmProp **)&props);
+}
+
+static void sm_set_other_properties()
+{
+    char *restore="-session";
+    char *clientid="-smclientid";
+    char *rmprog="/bin/rm";
+    char *rmarg="-rf";
+    int nvals=0, i;
+    const char *sdir=NULL, *cid=NULL;
+    
+    SmPropValue discard_val[3];
+    SmProp discard_prop={ SmDiscardCommand, SmLISTofARRAY8, 3, NULL };
+    SmPropValue restart_hint_val, *restart_val=NULL;
+    SmProp restart_hint_prop={ SmRestartStyleHint, SmCARD8, 1, NULL};
+    SmProp restart_prop={ SmRestartCommand, SmLISTofARRAY8, 0, NULL};
+    
+    SmProp *props[2];
+    
+    discard_prop.vals=discard_val;
+    restart_hint_prop.vals=&restart_hint_val;
+    
+    props[0]=&restart_prop;
+    props[1]=&restart_hint_prop;
+    /*props[2]=&discard_prop;*/
+    
+    sdir=extl_sessiondir();
+    cid=mod_sm_get_ion_id();
+    
+    if(sdir==NULL || cid==NULL)
+        return;
+    
+    restart_hint_val.value=&restart_hint;
+    restart_hint_val.length=1;
+    
+    restart_val=(SmPropValue *)malloc((ioncore_g.argc+4)*sizeof(SmPropValue));
+    for(i=0; i<ioncore_g.argc; i++){
+        if(strcmp(ioncore_g.argv[i], restore)==0 ||
+           strcmp(ioncore_g.argv[i], clientid)==0){
+            i++;
+        }else{
+            restart_val[nvals].value=ioncore_g.argv[i];
+            restart_val[nvals++].length=strlen(ioncore_g.argv[i]);
+        }
+    }
+    restart_val[nvals].value=restore;
+    restart_val[nvals++].length=strlen(restore);
+    restart_val[nvals].value=(char*)sdir;
+    restart_val[nvals++].length=strlen(sdir);
+    restart_val[nvals].value=clientid;
+    restart_val[nvals++].length=strlen(clientid);
+    restart_val[nvals].value=(char*)cid;
+    restart_val[nvals++].length=strlen(cid);
+    restart_prop.num_vals=nvals;
+    restart_prop.vals=restart_val;
+    discard_val[0].length=strlen(rmprog);
+    discard_val[0].value=rmprog;
+    discard_val[1].length=strlen(rmarg);
+    discard_val[1].value=rmarg;
+    discard_val[2].length=strlen(sdir);
+    discard_val[2].value=(char*)sdir;
+
+    SmcSetProperties(sm_conn,
+                     sizeof(props)/sizeof(props[0]),
+                     (SmProp **)&props);
+    
+    free(restart_val);
+}
+
+static void sm_set_properties()
+{
+    static bool init=TRUE;
+    
+    if(init){
+        sm_set_some_properties();
+        init=FALSE;
+    }
+    
+    sm_set_other_properties();
+}
+
+
+/* Callback for the save yourself phase 2 message.
+ This message is sent by the sm when other clients in the session are finished
+ saving state. This is requested in the save yourself callback by clients
+ like this one that manages other clients. */
+
+static void sm_save_yourself_phase2(SmcConn conn, SmPointer client_data)
+{
+    Bool success;
+
+    if(!(success=ioncore_do_snapshot()))
+        warn(TR("Failed to save session state"));
+    else
+        sm_set_properties();
+    
+    SmcSaveYourselfDone(conn, success);
+    sent_save_done=TRUE;    
+}
+
+/* Callback. Called when the client recieves a save yourself
+ message from the sm. */
+
+static void sm_save_yourself(SmcConn conn,
+                             SmPointer client_data,
+                             int save_type,
+                             Bool shutdown,
+                             int interact_style,
+                             Bool fast)
+{
+    if(!SmcRequestSaveYourselfPhase2(sm_conn, sm_save_yourself_phase2, NULL)){
+        warn(TR("Failed to request save-yourself-phase2 from "
+                "session manager."));
+        SmcSaveYourselfDone(sm_conn, False);
+        sent_save_done=TRUE;
+    }else{
+        sent_save_done=FALSE;
+    }
+}
+
+/* Response to the shutdown cancelled message */
+
+static void sm_shutdown_cancelled(SmcConn conn, SmPointer client_data)
+{
+    save_complete_fn=NULL;
+    if(!sent_save_done){
+        SmcSaveYourselfDone(conn, False);
+        sent_save_done=True;
+    }
+}
+
+/* Callback */
+
+static void sm_save_complete(SmcConn conn, SmPointer client_data)
+{
+    if(save_complete_fn){
+        save_complete_fn();
+        save_complete_fn=NULL;
+    }
+}
+
+/* Callback */
+
+static void sm_die(SmcConn conn, SmPointer client_data)
+{
+    assert(conn==sm_conn);
+    ioncore_do_exit();
+}
+
+
+/* Connects to the sm and registers
+ callbacks for different messages */
+
+bool mod_sm_init_session()
+{
+    char error_str[256];
+    char *new_client_id=NULL;
+    SmcCallbacks smcall;
+    
+    if(getenv("SESSION_MANAGER")==0){
+        warn(TR("SESSION_MANAGER environment variable not set."));
+        return FALSE;
+    }
+    
+    if(IceAddConnectionWatch(&sm_ice_watch_fd, NULL) == 0){
+        warn(TR("Session Manager: IceAddConnectionWatch failed."));
+        return FALSE;
+    }
+    
+    memset(&smcall, 0, sizeof(smcall));
+    smcall.save_yourself.callback=&sm_save_yourself;
+    smcall.save_yourself.client_data=NULL;
+    smcall.die.callback=&sm_die;
+    smcall.die.client_data=NULL;
+    smcall.save_complete.callback=&sm_save_complete;
+    smcall.save_complete.client_data=NULL;
+    smcall.shutdown_cancelled.callback=&sm_shutdown_cancelled;
+    smcall.shutdown_cancelled.client_data=NULL;
+    
+    if((sm_conn=SmcOpenConnection(NULL, /* network ids */
+                                  NULL, /* context */
+                                  1, 0, /* protocol major, minor */
+                                  SmcSaveYourselfProcMask |
+                                  SmcSaveCompleteProcMask |
+                                  SmcShutdownCancelledProcMask |
+                                  SmcDieProcMask,
+                                  &smcall,
+                                  sm_client_id, &new_client_id,
+                                  sizeof(error_str), error_str)) == NULL)
+    {
+        warn(TR("Unable to connect to the session manager."));
+        return FALSE;
+    }
+    
+    mod_sm_set_ion_id(new_client_id);
+    free(new_client_id);
+    
+    ice_sm_conn=SmcGetIceConnection(sm_conn);
+    
+    return TRUE;
+}
+
+
+void mod_sm_close()
+{
+    if(sm_conn!=NULL){
+        SmcCloseConnection(sm_conn, 0, NULL);
+        sm_conn=NULL;
+    }
+    
+    ice_sm_conn=NULL;
+    
+    if(sm_fd>=0){
+        mainloop_unregister_input_fd(sm_fd);
+        close(sm_fd);
+        sm_fd=-1;
+    }
+
+    if(sm_client_id!=NULL){
+        free(sm_client_id);
+        sm_client_id=NULL;
+    }
+}
+
+
+static void sm_exit()
+{
+    sm_die(sm_conn, NULL);
+}
+
+
+static void sm_restart()
+{
+    ioncore_do_restart();
+}
+
+
+void mod_sm_smhook(int what)
+{
+    save_complete_fn=NULL;
+    
+    /* pending check? */
+    
+    switch(what){
+    case IONCORE_SM_RESIGN:
+        restart_hint=SmRestartIfRunning;
+        sm_set_properties();
+        /*SmcRequestSaveYourself(sm_conn, SmSaveBoth, False,
+                               SmInteractStyleAny, False, False);
+        save_complete_fn=&sm_exit;*/
+        ioncore_do_exit();
+        break;
+    case IONCORE_SM_SHUTDOWN:
+        restart_hint=SmRestartIfRunning;
+        SmcRequestSaveYourself(sm_conn, SmSaveBoth, True,
+                               SmInteractStyleAny, False, True);
+        break;
+    case IONCORE_SM_RESTART:
+        restart_hint=SmRestartImmediately;
+        SmcRequestSaveYourself(sm_conn, SmSaveBoth, False,
+                               SmInteractStyleAny, False, False);
+        save_complete_fn=&sm_exit;
+        break;
+    case IONCORE_SM_RESTART_OTHER:
+        restart_hint=SmRestartIfRunning;
+        SmcRequestSaveYourself(sm_conn, SmSaveBoth, False,
+                               SmInteractStyleAny, False, False);
+        save_complete_fn=&sm_restart;
+        break;
+    case IONCORE_SM_SNAPSHOT:
+        restart_hint=SmRestartImmediately;
+        SmcRequestSaveYourself(sm_conn, SmSaveBoth, False,
+                               SmInteractStyleAny, False, True);
+        break;
+    }
+}
+
+
diff --git a/mod_sm/sm_session.h b/mod_sm/sm_session.h
new file mode 100644 (file)
index 0000000..306bf99
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * ion/mod_sm/sm_session.h
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006. 
+ * 
+ * Based on the code of the 'sm' module for Ion1 by an unknown contributor.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_SM_SESSION_H
+#define ION_MOD_SM_SESSION_H
+
+extern bool mod_sm_init_session();
+extern void mod_sm_set_ion_id(const char *client_id);
+extern char *mod_sm_get_ion_id();
+extern void mod_sm_close();
+extern void mod_sm_smhook(int what);
+
+#endif /* ION_MOD_SM_BINDING_H */
diff --git a/mod_sp/Makefile b/mod_sp/Makefile
new file mode 100644 (file)
index 0000000..e965d92
--- /dev/null
@@ -0,0 +1,27 @@
+##
+## Scratchpad module Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES=main.c
+ETC=cfg_sp.lua
+
+MAKE_EXPORTS=mod_sp
+
+MODULE=mod_sp
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: module_install etc_install
diff --git a/mod_sp/cfg_sp.lua b/mod_sp/cfg_sp.lua
new file mode 100644 (file)
index 0000000..7b2c2ac
--- /dev/null
@@ -0,0 +1,19 @@
+--
+-- Ion mod_sp configuration file
+--
+
+defbindings("WScreen", {
+    bdoc("Toggle scratchpad."),
+    kpress(META.."space", "mod_sp.set_shown_on(_, 'toggle')"),
+    
+    -- A more ideal key for toggling the scratchpad would be the key left of
+    -- the key for numeral 1. Unfortunately the symbols mapped to this key
+    -- vary by the keyboard layout, and to be fully portable to different 
+    -- architechtures and fancy keyboards, we can't rely on keycodes either. 
+    -- However, on standard Finnish/Swedish (and other Nordic) keyboard 
+    -- layouts the following should work:
+    --kpress(META.."section", "mod_sp.set_shown_on(_, 'toggle')"),
+    -- and on UK and US layouts this should work:
+    --kpress(META.."grave", "mod_sp.set_shown_on(_, 'toggle')"),
+})
+
diff --git a/mod_sp/main.c b/mod_sp/main.c
new file mode 100644 (file)
index 0000000..b827ae2
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * ion/mod_sp/main.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/map.h>
+#include <libtu/minmax.h>
+#include <libextl/readconfig.h>
+#include <libmainloop/hooks.h>
+
+#include <ioncore/saveload.h>
+#include <ioncore/screen.h>
+#include <ioncore/mplex.h>
+#include <ioncore/ioncore.h>
+#include <ioncore/global.h>
+#include <ioncore/framep.h>
+#include <ioncore/frame.h>
+#include <ioncore/names.h>
+
+#include "main.h"
+#include "exports.h"
+
+
+/*{{{ Module information */
+
+#include "../version.h"
+
+char mod_sp_ion_api_version[]=ION_API_VERSION;
+
+
+/*}}}*/
+
+
+/*{{{ Bindmaps, config, etc. */
+
+
+#define SP_NAME  "*scratchpad*"
+
+
+/*}}}*/
+
+
+/*{{{ Exports */
+
+
+static WRegion *create_frame_scratchpad(WWindow *parent, const WFitParams *fp,
+                                        void *unused)
+{
+    return (WRegion*)create_frame(parent, fp, FRAME_MODE_UNKNOWN);
+}
+
+
+static WFrame *create(WMPlex *mplex, int flags)
+{
+    WFrame *sp;
+    WMPlexAttachParams par;
+    int sw=REGION_GEOM(mplex).w, sh=REGION_GEOM(mplex).h;
+
+    par.flags=(flags
+               |MPLEX_ATTACH_UNNUMBERED
+               |MPLEX_ATTACH_MODAL
+               |MPLEX_ATTACH_SIZEPOLICY
+               |MPLEX_ATTACH_GEOM);
+    par.szplcy=SIZEPOLICY_FREE_GLUE;
+    
+    par.geom.w=minof(sw, CF_SCRATCHPAD_DEFAULT_W);
+    par.geom.h=minof(sh, CF_SCRATCHPAD_DEFAULT_H);
+    par.geom.x=(sw-par.geom.w)/2;
+    par.geom.y=(sh-par.geom.h)/2;
+
+    sp=(WFrame*)mplex_do_attach_new((WMPlex*)mplex, &par,
+                                    create_frame_scratchpad,
+                                    NULL);
+    
+
+    if(sp==NULL){
+        warn(TR("Unable to create scratchpad."));
+    }
+    
+    region_set_name((WRegion*)sp, SP_NAME);
+    
+    return sp;
+}
+
+
+static bool is_scratchpad(WRegion *reg)
+{
+    char *nm=reg->ni.name;
+    int inst_off=reg->ni.inst_off;
+    
+    if(nm==NULL)
+        return FALSE;
+    
+    if(inst_off<0)
+        return (strcmp(nm, SP_NAME)==0);
+    else
+        return (strncmp(nm, SP_NAME, inst_off)==0);
+}
+
+
+/*EXTL_DOC
+ * Change displayed status of some scratchpad on \var{mplex} if one is 
+ * found. The parameter \var{how} is one of (set/unset/toggle).
+ */
+EXTL_EXPORT
+bool mod_sp_set_shown_on(WMPlex *mplex, const char *how)
+{
+    int setpar=libtu_setparam_invert(libtu_string_to_setparam(how));
+    WMPlexIterTmp tmp;
+    WRegion *reg;
+    bool found=FALSE;
+    
+    FOR_ALL_MANAGED_BY_MPLEX(mplex, reg, tmp){
+        if(is_scratchpad(reg)){
+            mplex_set_hidden(mplex, reg, setpar);
+            found=TRUE;
+        }
+    }
+
+    if(!found){
+        int sp=libtu_string_to_setparam(how);
+        if(sp==SETPARAM_SET || sp==SETPARAM_TOGGLE)
+            found=(create(mplex, 0)!=NULL);
+    }
+    
+    return found;
+}
+
+
+/*EXTL_DOC
+ * Toggle displayed status of \var{sp}.
+ * The parameter \var{how} is one of (set/unset/toggle).
+ */
+EXTL_EXPORT
+bool mod_sp_set_shown(WFrame *sp, const char *how)
+{
+    if(sp!=NULL){
+        int setpar=libtu_setparam_invert(libtu_string_to_setparam(how));
+        WMPlex *mplex=OBJ_CAST(REGION_MANAGER(sp), WMPlex);
+        if(mplex!=NULL)
+            return mplex_set_hidden(mplex, (WRegion*)sp, setpar);
+    }
+    
+    return FALSE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Init & deinit */
+
+
+void mod_sp_deinit()
+{
+    mod_sp_unregister_exports();
+}
+
+
+static void check_and_create()
+{
+    WMPlexIterTmp tmp;
+    WScreen *scr;
+    WRegion *reg;
+    
+    /* No longer needed, free the memory the list uses. */
+    hook_remove(ioncore_post_layout_setup_hook, check_and_create);
+    
+    FOR_ALL_SCREENS(scr){
+        FOR_ALL_MANAGED_BY_MPLEX((WMPlex*)scr, reg, tmp){
+            if(is_scratchpad(reg))
+                return;
+        }
+        
+        create(&scr->mplex, MPLEX_ATTACH_HIDDEN);
+    }
+}
+    
+
+bool mod_sp_init()
+{
+    if(!mod_sp_register_exports())
+        return FALSE;
+
+    extl_read_config("cfg_sp", NULL, FALSE);
+    
+    if(ioncore_g.opmode==IONCORE_OPMODE_INIT){
+        hook_add(ioncore_post_layout_setup_hook, check_and_create);
+    }else{
+        check_and_create();
+    }
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
diff --git a/mod_sp/main.h b/mod_sp/main.h
new file mode 100644 (file)
index 0000000..f290b35
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * ion/mod_sp/main.h
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_SP_MAIN_H
+#define ION_MOD_SP_MAIN_H
+
+#include <ioncore/binding.h>
+
+extern bool mod_sp_init();
+extern void mod_sp_deinit();
+
+extern WBindmap *mod_sp_scratchpad_bindmap;
+
+#define CF_SCRATCHPAD_DEFAULT_W 640
+#define CF_SCRATCHPAD_DEFAULT_H 480
+
+#endif /* ION_MOD_SP_MAIN_H */
diff --git a/mod_statusbar/Makefile b/mod_statusbar/Makefile
new file mode 100644 (file)
index 0000000..0a13f4d
--- /dev/null
@@ -0,0 +1,34 @@
+##
+## Ion mod_statusbar Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES = main.c statusbar.c draw.c
+
+SUBDIRS = ion-statusd
+INSTALL_SUBDIRS = $(SUBDIRS)
+
+MAKE_EXPORTS = mod_statusbar
+
+MODULE = mod_statusbar
+MODULE_STUB = mod_statusbar.lua
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: module_install symlink_install
+
+symlink_install:
+       rm -f $(LCDIR)/ext_statusbar.lc
+       ln -s mod_statusbar.lc $(LCDIR)/ext_statusbar.lc
diff --git a/mod_statusbar/draw.c b/mod_statusbar/draw.c
new file mode 100644 (file)
index 0000000..29e9f02
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * ion/mod_statusbar/draw.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <ioncore/common.h>
+#include <ioncore/mplex.h>
+#include "statusbar.h"
+#include "draw.h"
+
+
+static void calc_elems_x(WRectangle *g, WSBElem *elems, int nelems)
+{
+    int x=g->x;
+    
+    while(nelems>0){
+        elems->x=x;
+        if(elems->type==WSBELEM_STRETCH)
+            x+=elems->text_w+elems->stretch;
+        else
+            x+=elems->text_w;
+        
+        nelems--;
+        elems++;
+    }
+}
+
+
+static void calc_elems_x_ra(WRectangle *g, WSBElem *elems, int nelems)
+{
+    int x=g->x+g->w;
+    
+    elems+=nelems-1;
+    
+    while(nelems>0){
+        if(elems->type==WSBELEM_STRETCH)
+            x-=elems->text_w+elems->stretch;
+        else
+            x-=elems->text_w;
+        elems->x=x;
+        
+        elems--;
+        nelems--;
+    }
+}
+
+
+void statusbar_calculate_xs(WStatusBar *sb)
+{
+    WRectangle g;
+    GrBorderWidths bdw;
+    WMPlex *mgr=NULL;
+    bool right_align=FALSE;
+    int minx, maxx;
+    int nleft=0, nright=0;
+    
+    if(sb->brush==NULL || sb->elems==NULL)
+        return;
+    
+    grbrush_get_border_widths(sb->brush, &bdw);
+
+    g.x=0;
+    g.y=0;
+    g.w=REGION_GEOM(sb).w;
+    g.h=REGION_GEOM(sb).h;
+    
+    mgr=OBJ_CAST(REGION_PARENT(sb), WMPlex);
+    if(mgr!=NULL){
+        WRegion *std=NULL;
+        WMPlexSTDispInfo din;
+        din.pos=MPLEX_STDISP_TL;
+        mplex_get_stdisp(mgr, &std, &din);
+        if(std==(WRegion*)sb)
+            right_align=(din.pos==MPLEX_STDISP_TR || din.pos==MPLEX_STDISP_BR);
+    }
+    
+    g.x+=bdw.left;
+    g.w-=bdw.left+bdw.right;
+    g.y+=bdw.top;
+    g.h-=bdw.top+bdw.bottom;
+
+    if(sb->filleridx>=0){
+        nleft=sb->filleridx;
+        nright=sb->nelems-(sb->filleridx+1);
+    }else if(!right_align){
+        nleft=sb->nelems;
+        nright=0;
+    }else{
+        nleft=0;
+        nright=sb->nelems;
+    }
+
+    if(nleft>0)
+        calc_elems_x(&g, sb->elems, nleft);
+    
+    if(nright>0)
+        calc_elems_x_ra(&g, sb->elems+sb->nelems-nright, nright);
+}
+
+
+
+static void draw_elems(GrBrush *brush, WRectangle *g, int ty,
+                       WSBElem *elems, int nelems, bool needfill, 
+                       const char *dfltattr, bool complete)
+{
+    int prevx=g->x;
+    int maxx=g->x+g->w;
+    
+    while(nelems>0){
+        if(prevx<elems->x){
+            g->x=prevx;
+            g->w=elems->x-prevx;
+            grbrush_clear_area(brush, g);
+        }
+            
+        if(elems->type==WSBELEM_TEXT || elems->type==WSBELEM_METER){
+            const char *s=(elems->text!=NULL
+                           ? elems->text 
+                           : STATUSBAR_NX_STR);
+            grbrush_draw_string(brush, elems->x, ty, s, strlen(s), needfill, 
+                                elems->attr ? elems->attr : dfltattr);
+            prevx=elems->x+elems->text_w;
+        }
+        elems++;
+        nelems--;
+    }
+
+    if(prevx<maxx){
+        g->x=prevx;
+        g->w=maxx-prevx;
+        grbrush_clear_area(brush, g);
+    }
+}
+
+
+void statusbar_draw(WStatusBar *sb, bool complete)
+{
+    WRectangle g;
+    GrBorderWidths bdw;
+    GrFontExtents fnte;
+    Window win=sb->wwin.win;
+    int ty;
+
+    if(sb->brush==NULL)
+        return;
+    
+    grbrush_get_border_widths(sb->brush, &bdw);
+    grbrush_get_font_extents(sb->brush, &fnte);
+
+    g.x=0;
+    g.y=0;
+    g.w=REGION_GEOM(sb).w;
+    g.h=REGION_GEOM(sb).h;
+    
+    grbrush_begin(sb->brush, &g, (complete ? 0 : GRBRUSH_NO_CLEAR_OK));
+    
+    grbrush_draw_border(sb->brush, &g, NULL);
+    
+    if(sb->elems==NULL)
+        return;
+    
+    g.x+=bdw.left;
+    g.w-=bdw.left+bdw.right;
+    g.y+=bdw.top;
+    g.h-=bdw.top+bdw.bottom;
+
+    ty=(g.y+fnte.baseline+(g.h-fnte.max_height)/2);
+        
+    draw_elems(sb->brush, &g, ty, sb->elems, sb->nelems,
+               TRUE, NULL, complete);
+    
+    grbrush_end(sb->brush);
+}
+
+
diff --git a/mod_statusbar/draw.h b/mod_statusbar/draw.h
new file mode 100644 (file)
index 0000000..c30a86f
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * ion/mod_statusbar/draw.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_STATUSBAR_DRAW_H
+#define ION_MOD_STATUSBAR_DRAW_H
+
+#include <libextl/extl.h>
+#include "statusbar.h"
+
+extern void statusbar_draw(WStatusBar *sb, bool complete);
+extern void statusbar_calculate_xs(WStatusBar *sb);
+
+#endif /* ION_MOD_STATUSBAR_DRAW_H */
diff --git a/mod_statusbar/ion-statusd/Makefile b/mod_statusbar/ion-statusd/Makefile
new file mode 100644 (file)
index 0000000..1ed1ac9
--- /dev/null
@@ -0,0 +1,40 @@
+##
+## Ion-statusd Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=../..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(LIBMAINLOOP_INCLUDES) $(LIBEXTL_INCLUDES) $(LIBTU_INCLUDES)
+LIBS += $(LIBMAINLOOP_LIBS) $(LIBEXTL_LIBS) $(LIBTU_LIBS) $(LUA_LIBS) $(DL_LIBS) -lm
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+DEFINES += -DETCDIR=\"$(ETCDIR)\" -DSHAREDIR=\"$(SHAREDIR)\" \
+           -DEXTRABINDIR=\"$(EXTRABINDIR)\" -DMODULEDIR=\"$(MODULEDIR)\" \
+          -DLCDIR=\"$(LCDIR)\" -DLOCALEDIR=\"$(LOCALEDIR)\"
+
+SOURCES = ion-statusd.c exec.c extlrx.c
+
+TARGETS = ion-statusd
+
+LUA_SOURCES = statusd_date.lua statusd_mail.lua statusd_load.lua
+
+MAKE_EXPORTS = statusd
+
+include $(TOPDIR)/libmainloop/rx.mk
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+ion-statusd: $(OBJS) $(EXT_OBJS)
+       $(CC) $(LINKOPTS) $(OBJS) $(EXT_OBJS) $(LDFLAGS) -o $@
+
+_install: lc_install
+       $(INSTALLDIR) $(EXTRABINDIR)
+       $(INSTALL) -s -m $(BIN_MODE) ion-statusd $(EXTRABINDIR)
diff --git a/mod_statusbar/ion-statusd/exec.c b/mod_statusbar/ion-statusd/exec.c
new file mode 100644 (file)
index 0000000..a42c0dc
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * ion/mod_statusbar/ion-statusd/exec.c
+ *
+ * Copyright (c) Tuomo Valkonen 2005-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <libmainloop/select.h>
+#include <libmainloop/exec.h>
+
+
+/*EXTL_DOC
+ * Run \var{cmd} in the background. 
+ */
+EXTL_SAFE
+EXTL_EXPORT
+int statusd_exec(const char *cmd)
+{
+    return mainloop_spawn(cmd);
+}
+
+
+/*EXTL_DOC
+ * Run \var{cmd} with a read pipe connected to its stdout.
+ * When data is received through the pipe, \var{h} is called
+ * with that data.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+int statusd_popen_bgread(const char *cmd, ExtlFn h, ExtlFn errh)
+{
+    return mainloop_popen_bgread(cmd, NULL, NULL, h, errh);
+}
+
diff --git a/mod_statusbar/ion-statusd/extlrx.c b/mod_statusbar/ion-statusd/extlrx.c
new file mode 100644 (file)
index 0000000..9b6ac58
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * ion/mod_statusbar/ion-statusd/extlrx.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libextl/extl.h>
+#include <libtu/output.h>
+#include <libtu/locale.h>
+
+
+/*{{{ libtu */
+
+
+/*EXTL_DOC
+ * Issue a warning. How the message is displayed depends on the current
+ * warning handler.
+ */
+EXTL_EXPORT
+void statusd_warn(const char *str)
+{
+    warn("%s", str);
+}
+
+
+EXTL_EXPORT
+const char *statusd_gettext(const char *s)
+{
+    if(s==NULL)
+        return NULL;
+    else
+        return TR(s);
+}
+
+
+/*}}}*/
+
diff --git a/mod_statusbar/ion-statusd/ion-statusd.c b/mod_statusbar/ion-statusd/ion-statusd.c
new file mode 100644 (file)
index 0000000..0e0cdb0
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * ion/mod_statusbar/ion-statusd/ion-statusd.c
+ *
+ * Copyright (c) Tuomo Valkonen 2004-2006.
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <libtu/util.h>
+#include <libtu/optparser.h>
+#include <libtu/errorlog.h>
+#include <libtu/locale.h>
+#include <libtu/misc.h>
+#include <libextl/readconfig.h>
+#include <libmainloop/select.h>
+#include <libmainloop/signal.h>
+#include <libmainloop/defer.h>
+
+#ifndef CF_NO_LOCALE
+#include <locale.h>
+#endif
+
+#include "../../version.h"
+
+
+static OptParserOpt ion_opts[]={
+    /*{OPT_ID('d'), "display",  OPT_ARG, "host:dpy.scr", 
+     DUMMY_TR("X display to use")},*/
+    
+    {'c', "conffile", OPT_ARG, "config_file", 
+     DUMMY_TR("Configuration file")},
+    
+    {'s', "searchdir", OPT_ARG, "dir", 
+     DUMMY_TR("Add directory to search path")},
+
+    /*{OPT_ID('s'), "session",  OPT_ARG, "session_name", 
+     DUMMY_TR("Name of session (affects savefiles)")},*/
+    
+    {'h', "help", 0, NULL, 
+     DUMMY_TR("Show this help")},
+    
+    {'V', "version", 0, NULL,
+     DUMMY_TR("Show program version")},
+    
+    {OPT_ID('a'), "about", 0, NULL,
+     DUMMY_TR("Show about text")},
+
+    {'q', "quiet", 0, NULL, 
+     DUMMY_TR("Quiet mode")},
+
+    {'m', "meter", OPT_ARG, "meter_module",
+     DUMMY_TR("Load a meter module")},
+
+    END_OPTPARSEROPTS
+};
+
+
+static const char statusd_copy[]=
+    "Ion-statusd " ION_VERSION ", copyright (c) Tuomo Valkonen 2004-2005.";
+
+
+static const char statusd_license[]=DUMMY_TR(
+    "This program is free software; you can redistribute it and/or\n"
+    "modify it under the terms of the GNU Lesser General Public\n"
+    "License as published by the Free Software Foundation; either\n"
+    "version 2.1 of the License, or (at your option) any later version.\n"
+    "\n"
+    "This program is distributed in the hope that it will be useful,\n"
+    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
+    "Lesser General Public License for more details.\n");
+
+
+/* new_informs=TRUE because we should always print period when 
+ * initialisation is done. 
+ */
+static bool new_informs=TRUE;
+static ExtlTab configtab;
+
+static void help()
+{
+    int i;
+    printf(TR("Usage: %s [options]\n\n"), prog_execname());
+    for(i=0; ion_opts[i].descr!=NULL; i++)
+        ion_opts[i].descr=TR(ion_opts[i].descr);
+    optparser_printhelp(OPTP_MIDLONG, ion_opts);
+    printf("\n");
+}
+
+
+static void flush_informs()
+{
+    if(new_informs){
+        printf(".\n");
+        fflush(stdout);
+        new_informs=FALSE;
+    }
+}
+
+
+static void mainloop()
+{
+    sigset_t trapset;
+    
+    sigemptyset(&trapset);
+    sigaddset(&trapset, SIGALRM);
+    sigaddset(&trapset, SIGCHLD);
+    mainloop_trap_signals(&trapset);
+    
+    while(1){
+        int kill_sig=mainloop_check_signals();
+        if(kill_sig!=0 && kill_sig!=SIGUSR1){
+            if(kill_sig==SIGTERM)
+                exit(EXIT_FAILURE);
+            else
+                kill(getpid(), kill_sig);
+        }
+
+        mainloop_execute_deferred();
+
+        flush_informs();
+
+        mainloop_select();
+    }
+}
+
+
+extern bool statusd_register_exports();
+extern void statusd_unregister_exports();
+
+
+static void stdout_closed(int fd, void *data)
+{
+    exit(EXIT_SUCCESS);
+}
+
+
+int main(int argc, char*argv[])
+{
+    const char *mod=NULL;
+    char *mod2=NULL;
+    int loaded=0;
+    int opt;
+    bool quiet=FALSE;
+
+#ifndef CF_NO_LOCALE    
+    if(setlocale(LC_ALL, "")==NULL)
+        warn("setlocale() call failed.");
+#endif
+
+    configtab=extl_table_none();
+    
+    libtu_init(argv[0]);
+    extl_init();
+
+    if(!statusd_register_exports())
+        return EXIT_FAILURE;
+
+    extl_add_searchdir(EXTRABINDIR);
+    extl_add_searchdir(MODULEDIR);
+    extl_add_searchdir(ETCDIR);
+    extl_add_searchdir(SHAREDIR);
+    extl_add_searchdir(LCDIR);
+    extl_set_userdirs("ion3");
+
+    optparser_init(argc, argv, OPTP_MIDLONG, ion_opts);
+    
+    extl_read_config("ioncore_luaext", NULL, TRUE);
+    
+    while((opt=optparser_get_opt())){
+        switch(opt){
+        /*case OPT_ID('d'):
+            display=optparser_get_arg();
+            break;*/
+        case 's':
+            extl_add_searchdir(optparser_get_arg());
+            break;
+        /*case OPT_ID('s'):
+            extl_set_sessiondir(optparser_get_arg());
+            break;*/
+        case 'h':
+            help();
+            return EXIT_SUCCESS;
+        case 'V':
+            printf("%s\n", ION_VERSION);
+            return EXIT_SUCCESS;
+        case OPT_ID('a'):
+            printf("%s\n\n%s", statusd_copy, TR(statusd_license));
+            return EXIT_SUCCESS;
+        case 'c':
+            {
+                ExtlTab t;
+                const char *f=optparser_get_arg();
+                if(extl_read_savefile(f, &t)){
+                    extl_unref_table(configtab);
+                    configtab=t;
+                }else{
+                    warn(TR("Unable to load configuration file %s"), f);
+                }
+            }
+            break;
+        case 'q':
+            quiet=TRUE;
+            break;
+        case 'm':
+            mod=optparser_get_arg();
+            if(strchr(mod, '/')==NULL && strchr(mod, '.')==NULL){
+                mod2=scat("statusd_", mod);
+                if(mod2==NULL)
+                    return EXIT_FAILURE;
+                mod=mod2;
+            }
+            if(extl_read_config(mod, NULL, !quiet))
+                loaded++;
+            if(mod2!=NULL)
+                free(mod2);
+            break;
+        default:
+            warn(TR("Invalid command line."));
+            help();
+            return EXIT_FAILURE;
+        }
+    }
+    
+    if(loaded==0 && !quiet){
+        warn(TR("No meters loaded."));
+        return EXIT_FAILURE;
+    }
+    
+    mainloop();
+    
+    return EXIT_SUCCESS;
+}
+
+
+/*EXTL_DOC
+ * Inform that meter \var{name} has value \var{value}.
+ */
+EXTL_EXPORT
+void statusd_inform(const char *name, const char *value)
+{
+    printf("%s: %s\n", name, value);
+    new_informs=TRUE;
+}
+
+
+/*EXTL_DOC
+ * Get configuration table for module \var{name}
+ */
+EXTL_EXPORT
+ExtlTab statusd_get_config(const char *name)
+{
+    if(name==NULL){
+        return extl_ref_table(configtab);
+    }else{
+        ExtlTab t;
+        if(extl_table_gets_t(configtab, name, &t))
+            return t;
+        else
+            return extl_create_table();
+    }
+}
+
+
+/*EXTL_DOC
+ * Get last file modification time.
+ */
+EXTL_EXPORT
+double statusd_last_modified(const char *fname)
+{
+    struct stat st;
+    
+    if(fname==NULL)
+        return (double)(-1);
+    
+    if(stat(fname, &st)!=0){
+        /*warn_err_obj(fname);*/
+        return (double)(-1);
+    }
+    
+    return (double)(st.st_mtime>st.st_ctime ? st.st_mtime : st.st_ctime);
+}
+
diff --git a/mod_statusbar/ion-statusd/statusd_date.lua b/mod_statusbar/ion-statusd/statusd_date.lua
new file mode 100644 (file)
index 0000000..ea36258
--- /dev/null
@@ -0,0 +1,41 @@
+--
+-- ion/mod_statusbar/ion-statusd/statusd_date.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+
+local timer
+
+local defaults={
+    date_format='%a %Y-%m-%d %H:%M',
+    formats={},
+}
+
+local settings=table.join(statusd.get_config('date'), defaults)
+
+local function update()
+    local tm=os.time()
+    statusd.inform('date', os.date(settings.date_format, tm))
+    for k, f in pairs(settings.formats) do
+        statusd.inform('date_'..k, os.date(f, tm))
+    end
+    return tm
+end
+
+local function timer_handler(tmr)
+    local tm=update()
+    
+    local t=os.date('*t', tm)
+    local d=(60-t.sec)*1000
+    
+    timer:set(d, timer_handler)
+end
+
+timer=statusd.create_timer()
+timer_handler(timer)
diff --git a/mod_statusbar/ion-statusd/statusd_load.lua b/mod_statusbar/ion-statusd/statusd_load.lua
new file mode 100644 (file)
index 0000000..cd62178
--- /dev/null
@@ -0,0 +1,99 @@
+--
+-- ion/mod_statusbar/ion-statusd/statusd_load.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+--
+-- We should really use getloadavg(3) instead and move the meter to
+-- Ion side to get properly up-to-date loads. But until such an export
+-- is made, and we use potentially blocking files and external programs, 
+-- this meter must be in ion-statusd.
+--
+
+local defaults={
+    update_interval=10*1000,
+    load_hint=1,
+    important_threshold=1.5,
+    critical_threshold=4.0
+}
+
+local settings=table.join(statusd.get_config("load"), defaults)
+
+local loadpat='^(%d+%.%d+).*(%d+%.%d+).*(%d+%.%d+)'
+
+local function get_load_proc()
+    local f=io.open('/proc/loadavg', 'r')
+    if not f then
+        return ""
+    end
+    local s=f:read('*l')
+    f:close()
+    local st, en, load=string.find(s, '^(%d+%.%d+ %d+%.%d+ %d+%.%d+)')
+    
+    return string.gsub((load or ""), " ", ", ")
+end
+
+local function get_load_uptime()
+    local f=io.popen('uptime', 'r')
+    if not f then
+        return "??"
+    end
+    local s=f:read('*l')
+    f:close()
+    local st, en, load=string.find(s, 'load averages?:%s*(.*)')
+    return (load or "")
+end
+
+local function detect_load_fn()
+    if get_load_proc()~="" then
+        return get_load_proc
+    else
+        return get_load_uptime
+    end
+end
+
+local get_load, load_timer
+
+local function get_hint(l)
+    local v=tonumber(l)
+    local i="normal"
+    if v then
+        if v>settings.critical_threshold then
+            i="critical"
+        elseif v>settings.important_threshold then
+            i="important"
+        end
+    end
+    return i
+end
+
+local l1min, l5min, l15min=2+1, 2+2, 2+3
+
+local function update_load()
+    local l = get_load()    
+    local lds={string.find(l, loadpat)}
+    statusd.inform("load", l)
+    statusd.inform("load_hint", get_hint(lds[settings.load_hint+2]))
+    statusd.inform("load_1min", lds[l1min])
+    statusd.inform("load_1min_hint", get_hint(lds[l1min]))
+    statusd.inform("load_5min", lds[l5min])
+    statusd.inform("load_5min_hint", get_hint(lds[l5min]))
+    statusd.inform("load_15min", lds[l15min])
+    statusd.inform("load_15min_hint", get_hint(lds[l15min]))
+    load_timer:set(settings.update_interval, update_load)
+end
+
+-- Init
+--statusd.inform("load_template", "0.00, 0.00, 0.00");
+
+get_load=detect_load_fn()
+load_timer=statusd.create_timer()
+update_load()
+
+
diff --git a/mod_statusbar/ion-statusd/statusd_mail.lua b/mod_statusbar/ion-statusd/statusd_mail.lua
new file mode 100644 (file)
index 0000000..71cfea9
--- /dev/null
@@ -0,0 +1,156 @@
+--
+-- ion/mod_statusbar/ion-statusd/statusd_mail.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+-- The keyword for this monitor
+local mon = "mail"
+
+local defaults={
+    update_interval=10*1000,
+    retry_interval=60*10*1000,
+    mbox = os.getenv("MAIL"),
+    files = {}
+}
+
+local settings=table.join(statusd.get_config(mon), defaults)
+
+local function TR(s, ...)
+    return string.format(statusd.gettext(s), unpack(arg))
+end
+
+local function check_spool()
+    if not settings.mbox then
+        statusd.warn(TR("MAIL environment variable not set "..
+                        "and no spool given."))
+    end
+end
+
+if not settings.files["spool"] then
+    check_spool()
+    settings.files["spool"] = settings.mbox
+elseif not(settings.files["spool"] == mbox) then
+    statusd.warn(TR("%s.mbox does not match %s.files['spool']; using %s.mbox",
+                   mon, mon, mon))
+    check_spool()
+    settings.files["spool"] = settings.mbox
+end
+
+local function calcmail(fname)
+    local f, err=io.open(fname, 'r')
+    local total, read, old=0, 0, 0
+    local had_blank=true
+    local in_headers=false
+    local had_status=false
+    
+    if not f then
+        statusd.warn(err)
+        return
+    end
+
+    for l in f:lines() do
+        if had_blank and string.find(l, '^From ') then
+            total=total+1
+            had_status=false
+            in_headers=true
+            had_blank=false
+        else
+            had_blank=false
+            if l=="" then
+                if in_headers then
+                    in_headers=false
+                end
+                had_blank=true
+            elseif in_headers and not had_status then
+                local st, en, stat=string.find(l, '^Status:(.*)')
+                if stat then
+                    had_status=true
+                    if string.find(l, 'R') then
+                        read=read+1
+                    end
+                    if string.find(l, 'O') then
+                        old=old+1
+                    end
+                end
+            end
+        end
+    end
+    
+    f:close()
+    
+    return total, total-read, total-old
+end
+
+
+local mail_timer
+local mail_timestamps = {}
+function init_timestamps ()
+    for key, val in pairs(settings.files) do
+        mail_timestamps[key]=-2.0
+    end
+end
+init_timestamps()
+
+local function update_mail()
+    local failed
+    for key, mbox in pairs(settings.files) do
+        if not mbox then
+            error(TR(key.." not set"))
+        end
+    
+        local old_tm=mail_timestamps[key]
+        mail_timestamps[key]=statusd.last_modified(mbox)
+    
+        if mail_timestamps[key]>old_tm then
+           local mail_total, mail_unread, mail_new=calcmail(mbox)
+           if failed == nil then
+               failed = not mail_total
+           else
+               failed = failed and (not mail_total)
+           end
+        
+           if key == "spool" then
+               meter=mon
+           else
+               meter=mon.."_"..key
+           end
+           if mail_total then
+               statusd.inform(meter.."_new", tostring(mail_new))
+               statusd.inform(meter.."_unread", tostring(mail_unread))
+               statusd.inform(meter.."_total", tostring(mail_total))
+
+               if mail_new>0 then
+                   statusd.inform(meter.."_new_hint", "important")
+               else
+                   statusd.inform(meter.."_new_hint", "normal")
+               end
+        
+               if mail_unread>0 then
+                   statusd.inform(meter.."_unread_hint", "important")
+               else
+                   statusd.inform(meter.."_unread_hint", "normal")
+               end
+           end
+       end
+    end
+
+    if failed then
+        statusd.warn(TR("Disabling mail monitor for %d seconds.",
+                       settings.retry_interval/1000))
+       init_timestamps()
+       mail_timer:set(settings.retry_interval, update_mail)
+       return
+    end
+   
+    mail_timer:set(settings.update_interval, update_mail)
+end
+
+mail_timer=statusd.create_timer()
+update_mail()
+
diff --git a/mod_statusbar/main.c b/mod_statusbar/main.c
new file mode 100644 (file)
index 0000000..9ec4f20
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * ion/mod_statusbar/main.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#include <libtu/minmax.h>
+#include <libextl/readconfig.h>
+#include <libmainloop/exec.h>
+#include <libmainloop/select.h>
+#include <ioncore/saveload.h>
+#include <ioncore/bindmaps.h>
+#include <ioncore/global.h>
+
+#include "statusbar.h"
+#include "exports.h"
+
+
+#define CF_STATUSD_TIMEOUT_SEC 5
+
+
+/*{{{ Module information */
+
+
+#include "../version.h"
+
+char mod_statusbar_ion_api_version[]=ION_API_VERSION;
+
+
+/*}}}*/
+
+
+/*{{{ Bindmaps */
+
+
+WBindmap *mod_statusbar_statusbar_bindmap=NULL;
+
+
+/*}}}*/
+
+
+/*{{{ Statusd launch helper */
+
+
+#define BL 1024
+
+
+static bool process_pipe(int fd, ExtlFn fn, 
+                         bool *doneseen, bool *eagain)
+{
+    char buf[BL];
+    int n;
+    bool fnret;
+    
+    *eagain=FALSE;
+    
+    n=read(fd, buf, BL-1);
+    
+    if(n<0){
+        if(errno==EAGAIN || errno==EINTR){
+            *eagain=(errno==EAGAIN);
+            return TRUE;
+        }
+        warn_err_obj(TR("reading a pipe"));
+        return FALSE;
+    }else if(n>0){
+        buf[n]='\0';
+        *doneseen=FALSE;
+        return extl_call(fn, "s", "b", &buf, doneseen);
+    }
+    
+    return FALSE;
+}
+
+
+#define USEC 1000000
+
+
+static bool wait_statusd_init(int outfd, int errfd, ExtlFn dh, ExtlFn eh)
+{
+    fd_set rfds;
+    struct timeval tv, endtime, now;
+    int nfds=maxof(outfd, errfd);
+    int retval;
+    bool dummy, doneseen, eagain=FALSE;
+    const char *timeout_msg=TR("ion-statusd launch timeout.");
+    
+    if(gettimeofday(&endtime, NULL)!=0){
+        warn_err();
+        return FALSE;
+    }
+    
+    now=endtime;
+    endtime.tv_sec+=CF_STATUSD_TIMEOUT_SEC;
+    
+    while(1){
+        FD_ZERO(&rfds);
+
+        /* Calculate remaining time */
+        if(now.tv_sec>endtime.tv_sec){
+            goto timeout;
+        }else if(now.tv_sec==endtime.tv_sec){
+            if(now.tv_usec>=endtime.tv_usec)
+                goto timeout;
+            tv.tv_sec=0;
+            tv.tv_usec=endtime.tv_usec-now.tv_usec;
+        }else{
+            tv.tv_usec=USEC+endtime.tv_usec-now.tv_usec;
+            tv.tv_sec=-1+endtime.tv_sec-now.tv_sec;
+            /* Kernel lameness tuner: */
+            tv.tv_sec+=tv.tv_usec/USEC;
+            tv.tv_usec%=USEC;
+        }
+        
+        FD_SET(outfd, &rfds);
+        FD_SET(errfd, &rfds);
+    
+        retval=select(nfds+1, &rfds, NULL, NULL, &tv);
+        if(retval>0){
+            if(FD_ISSET(errfd, &rfds)){
+                if(!process_pipe(errfd, eh, &dummy, &eagain))
+                    return FALSE;
+            }
+            if(FD_ISSET(outfd, &rfds)){
+                if(!process_pipe(outfd, dh, &doneseen, &eagain))
+                    return FALSE;
+                if(doneseen){
+                    /* Read rest of errors. */
+                    bool ok;
+                    do{
+                        ok=process_pipe(errfd, eh, &dummy, &eagain);
+                    }while(ok && !eagain);
+                    return TRUE;
+                }
+            }
+        }else if(retval==0){
+            goto timeout;
+        }
+        
+        if(gettimeofday(&now, NULL)!=0){
+            warn_err();
+            return FALSE;
+        }
+    }
+    
+    return TRUE;
+    
+timeout:
+    warn(TR("ion-statusd timed out."));
+    return FALSE;
+}
+
+
+EXTL_EXPORT
+int mod_statusbar__launch_statusd(const char *cmd,
+                                  ExtlFn initdatahandler,
+                                  ExtlFn initerrhandler,
+                                  ExtlFn datahandler,
+                                  ExtlFn errhandler)
+{
+    pid_t pid;
+    int outfd=-1, errfd=-1;
+    
+    if(cmd==NULL)
+        return -1;
+    
+    pid=mainloop_do_spawn(cmd, NULL, NULL,
+                          NULL, &outfd, &errfd);
+    
+    if(pid<0)
+        return -1;
+    
+    if(!wait_statusd_init(outfd, errfd, initdatahandler, initerrhandler))
+        goto err;
+    
+    if(!mainloop_register_input_fd_extlfn(outfd, datahandler))
+        goto err;
+    
+    if(!mainloop_register_input_fd_extlfn(errfd, errhandler))
+        goto err2;
+
+    return pid;
+    
+err2:    
+    mainloop_unregister_input_fd(outfd);
+err:    
+    close(outfd);
+    close(errfd);
+    return -1;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Systray */
+
+
+static bool is_systray(WClientWin *cwin)
+{
+    static Atom atom__kde_net_wm_system_tray_window_for=None;
+    Atom actual_type=None;
+    int actual_format;
+    unsigned long nitems;
+    unsigned long bytes_after;
+    unsigned char *prop;
+    char *dummy;
+    bool is=FALSE;
+
+    if(extl_table_gets_s(cwin->proptab, "statusbar", &dummy)){
+        free(dummy);
+        return TRUE;
+    }
+    
+    if(atom__kde_net_wm_system_tray_window_for==None){
+        atom__kde_net_wm_system_tray_window_for=XInternAtom(ioncore_g.dpy,
+                                                            "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
+                                                            False);
+    }
+    if(XGetWindowProperty(ioncore_g.dpy, cwin->win,
+                          atom__kde_net_wm_system_tray_window_for, 0,
+                          sizeof(Atom), False, AnyPropertyType, 
+                          &actual_type, &actual_format, &nitems,
+                          &bytes_after, &prop)==Success){
+        if(actual_type!=None){
+            is=TRUE;
+        }
+        XFree(prop);
+    }
+    
+    return is;
+}
+
+
+static bool clientwin_do_manage_hook(WClientWin *cwin, const WManageParams *param)
+{
+    WStatusBar *sb=NULL;
+    
+    if(!is_systray(cwin))
+        return FALSE;
+    
+    sb=mod_statusbar_find_suitable(cwin, param);
+    if(sb==NULL)
+        return FALSE;
+
+    return region_manage_clientwin((WRegion*)sb, cwin, param,
+                                   MANAGE_REDIR_PREFER_NO);
+}
+
+    
+/*}}}*/
+
+
+/*{{{ Init & deinit */
+
+
+void mod_statusbar_deinit()
+{
+    hook_remove(clientwin_do_manage_alt, 
+                (WHookDummy*)clientwin_do_manage_hook);
+
+    if(mod_statusbar_statusbar_bindmap!=NULL){
+        ioncore_free_bindmap("WStatusBar", mod_statusbar_statusbar_bindmap);
+        mod_statusbar_statusbar_bindmap=NULL;
+    }
+
+    ioncore_unregister_regclass(&CLASSDESCR(WStatusBar));
+
+    mod_statusbar_unregister_exports();
+}
+
+
+bool mod_statusbar_init()
+{
+    mod_statusbar_statusbar_bindmap=ioncore_alloc_bindmap("WStatusBar", NULL);
+    
+    if(mod_statusbar_statusbar_bindmap==NULL)
+        return FALSE;
+
+    if(!ioncore_register_regclass(&CLASSDESCR(WStatusBar),
+                                  (WRegionLoadCreateFn*) statusbar_load)){
+        mod_statusbar_deinit();
+        return FALSE;
+    }
+
+    if(!mod_statusbar_register_exports()){
+        mod_statusbar_deinit();
+        return FALSE;
+    }
+    
+    hook_add(clientwin_do_manage_alt, 
+             (WHookDummy*)clientwin_do_manage_hook);
+
+    /*ioncore_read_config("cfg_statusbar", NULL, TRUE);*/
+    
+    return TRUE;
+}
+
+
+/*}}}*/
+
diff --git a/mod_statusbar/main.h b/mod_statusbar/main.h
new file mode 100644 (file)
index 0000000..b8ec8d2
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * ion/mod_statusbar/main.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_STATUSBAR_MAIN_H
+#define ION_MOD_STATUSBAR_MAIN_H
+
+#include <ioncore/binding.h>
+
+extern bool mod_statusbar_init();
+extern void mod_statusbar_deinit();
+
+extern WBindmap *mod_statusbar_statusbar_bindmap;
+
+
+#endif /* ION_MOD_STATUSBAR_MAIN_H */
diff --git a/mod_statusbar/mod_statusbar.lua b/mod_statusbar/mod_statusbar.lua
new file mode 100644 (file)
index 0000000..aeef9cb
--- /dev/null
@@ -0,0 +1,340 @@
+--
+-- ion/mod_statusbar/mod_statusbar.lua
+-- 
+-- Copyright (c) Tuomo Valkonen 2004-2006.
+--
+-- Ion is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU Lesser General Public License as published by
+-- the Free Software Foundation; either version 2.1 of the License, or
+-- (at your option) any later version.
+--
+
+-- This is a slight abuse of the package.loaded variable perhaps, but
+-- library-like packages should handle checking if they're loaded instead of
+-- confusing the user with require/include differences.
+if package.loaded["mod_statusbar"] then return end
+
+if not ioncore.load_module("mod_statusbar") then
+    return
+end
+
+local mod_statusbar=_G["mod_statusbar"]
+assert(mod_statusbar)
+
+
+-- Meter list {{{
+
+local meters={}
+
+--DOC
+-- Inform of a value.
+function mod_statusbar.inform(name, value)
+    meters[name]=value
+end
+
+-- }}}
+
+
+-- Template processing {{{
+
+local function process_template(template, meter_f, text_f, stretch_f)
+    local st, en, b, c, r, p, tmp
+    
+    while template~="" do
+        -- Find '%something'
+        st, en, b, r=string.find(template, '^(.-)%%(.*)')
+    
+        if not b then
+            -- Not found
+            text_f(template)
+            break
+        else
+            if b~="" then
+                -- Add preciding text as normal text element
+                text_f(b)
+            end
+            template=r
+
+            -- Check for '% ' and '%%'
+            st, en, c, r=string.find(template, '^([ %%])(.*)')
+
+            if c then
+                if c==' ' then
+                    stretch_f(c)
+                else
+                    text_f(c)
+                end
+                template=r
+            else 
+                -- Extract [alignment][zero padding]<meter name>
+                local pat='([<|>]?)(0*[0-9]*)([a-zA-Z0-9_]+)'
+                -- First within {...}
+                st, en, c, p, b, r=string.find(template, '^{'..pat..'}(.*)')
+                if not st then
+                    -- And then without
+                    st, en, c, p, b, r=string.find(template, '^'..pat..'(.*)')
+                end
+                if b then
+                    meter_f(b, c, tonumber(p))
+                    template=r
+                end
+            end
+        end
+    end
+end
+
+
+
+function mod_statusbar.template_to_table(template)
+    local res={}
+    local m=meters --set_date(stng, meters)
+    local aligns={["<"]=0, ["|"]=1, [">"]=2}
+    
+    process_template(template,
+                     -- meter
+                     function(s, c, p)
+                         if s=="filler" then
+                             table.insert(res, {type=4})
+                         elseif (string.find(s, "^systray$") or
+                                 string.find(s, "^systray_")) then
+                             table.insert(res, {
+                                 type=5,
+                                 meter=s,
+                                 align=aligns[c],
+                             })
+                         else
+                             table.insert(res, {
+                                 type=2,
+                                 meter=s,
+                                 align=aligns[c],
+                                 tmpl=meters[s.."_template"],
+                                 zeropad=p,
+                             })
+                         end
+                     end,
+                     -- text
+                     function(t)
+                         table.insert(res, {
+                             type=1,
+                             text=t,
+                         })
+                     end,
+                     -- stretch
+                     function(t)
+                         table.insert(res, {
+                             type=3,
+                             text=t,
+                         })
+                     end)
+    return res
+end
+
+
+mod_statusbar._set_template_parser(mod_statusbar.template_to_table)
+
+
+-- }}}
+
+-- Update {{{
+
+--DOC
+-- Update statusbar contents. To be called after series
+-- of \fnref{mod_statusbar.inform} calls.
+function mod_statusbar.update(update_templates)
+    for _, sb in pairs(mod_statusbar.statusbars()) do
+        if update_templates then
+            local t=sb:get_template_table()
+            for _, v in pairs(t) do
+                if v.meter then
+                    v.tmpl=meters[v.meter.."_template"]
+                end
+            end
+            sb:set_template_table(t)
+        end
+        sb:update(meters)
+    end
+end
+
+-- }}}
+
+
+-- ion-statusd support {{{
+
+local statusd_pid=0
+
+function mod_statusbar.rcv_statusd(str)
+    local data=""
+    local updatenw=false
+    local updated=false
+
+    local function doline(i)
+        if i=="." then
+            mod_statusbar.update(updatenw)
+            updated=true
+        else
+            local _, _, m, v=string.find(i, "^([^:]+):%s*(.*)")
+            if m and v then
+                mod_statusbar.inform(m, v)
+                updatenw=updatenw or string.find(m, "_template")
+            end
+        end
+        return ""
+    end
+    
+    while str do
+        updated=false
+        data=string.gsub(data..str, "([^\n]*)\n", doline)
+        str=coroutine.yield(updated)
+    end
+    
+    ioncore.warn(TR("ion-statusd quit."))
+    statusd_pid=0
+    meters={}
+    mod_statusbar.update(updatenw)
+end
+
+
+local function get_modules()
+    local mods={}
+    local specials={["filler"]=true, ["systray"]=true}
+    
+    for _, sb in pairs(mod_statusbar.statusbars()) do
+        for _, item in pairs(sb:get_template_table()) do
+            if item.type==2 and not specials[item.meter] then
+                local _, _, m=string.find(item.meter, "^([^_]*)");
+                if m and m~="" then
+                    mods[m]=true
+                end
+            end
+        end
+    end
+    
+    return mods
+end
+
+
+function mod_statusbar.cfg_statusd(cfg)
+    if date_format_backcompat_kludge then
+        if not cfg.date then
+            cfg=table.copy(cfg, false)
+            cfg.date={date_format=date_format_backcompat_kludge}
+        elseif not cfg.date.date_format then
+            cfg=table.copy(cfg, true)
+            cfg.date.date_format=date_format_backcompat_kludge
+        end
+    end
+
+    --TODO: don't construct file name twice.
+    ioncore.write_savefile("cfg_statusd", cfg)
+    return ioncore.get_savefile("cfg_statusd")
+end
+
+
+function mod_statusbar.rcv_statusd_err(str)
+    if str then
+        io.stderr:write(str)
+    end
+end
+
+
+--DOC
+-- Load modules and launch ion-statusd with configuration table \var{cfg}.
+function mod_statusbar.launch_statusd(cfg)
+    if statusd_pid>0 then
+        return
+    end
+    
+    local mods=get_modules()
+    
+    -- Load modules
+    for m in pairs(mods) do
+        if dopath("statusbar_"..m, true) then
+            mods[m]=nil
+        end
+    end
+
+    -- Lookup ion-statusd
+    local statusd=ioncore.lookup_script("ion-statusd")
+    if not statusd then
+        ioncore.warn(TR("Could not find %s", script))
+        return
+    end
+
+    local statusd_errors
+    local function initrcverr(str)
+        statusd_errors=(statusd_errors or "")..str
+    end
+
+    local cfg=mod_statusbar.cfg_statusd(cfg or {})
+    local params=""
+    table.foreach(mods, function(k) params=params.." -m "..k end)
+    local cmd=statusd.." -q -c "..cfg..params
+    
+    local rcv=coroutine.wrap(mod_statusbar.rcv_statusd)
+    local rcverr=mod_statusbar.rcv_statusd_err
+    
+    statusd_pid=mod_statusbar._launch_statusd(cmd, 
+                                              rcv, initrcverr, 
+                                              rcv, rcverr)
+
+    if statusd_errors then
+        warn(TR("Errors starting ion-statusd:\n")..statusd_errors)
+    end
+
+    if statusd_pid<=0 then
+        warn(TR("Failed to start ion-statusd."))
+    end                                              
+end
+
+--}}}
+
+
+-- Initialisation and default settings {{{
+
+--DOC
+-- Create a statusbar.
+function mod_statusbar.create(param)
+    local scr=ioncore.find_screen_id(param.screen or 0)
+    if not scr then
+        error(TR("Screen not found."))
+    end
+    
+    if not param.force then
+        local stdisp=scr:get_stdisp()
+        if stdisp and stdisp.reg then
+            error(TR("Screen already has an stdisp. Refusing to create a "..
+                     "statusbar."))
+        end
+    end
+    
+    local sb=scr:set_stdisp({
+        type="WStatusBar", 
+        pos=(param.pos or "bl"),
+        fullsize=param.fullsize,
+        name="*statusbar*",
+        template=param.template,
+        template_table=param.template_table,
+        systray=param.systray,
+    })
+    
+    if not sb then
+        error(TR("Failed to create statusbar."))
+    end
+    
+    return sb
+end
+
+-- }}}
+
+
+-- Mark ourselves loaded.
+package.loaded["mod_statusbar"]=true
+
+
+-- Load user configuration file
+dopath('cfg_statusbar', true)
+
+-- Launch statusd if the user didn't launch it.
+if statusd_pid<=0 then
+    mod_statusbar.launch_statusd()
+end
diff --git a/mod_statusbar/statusbar.c b/mod_statusbar/statusbar.c
new file mode 100644 (file)
index 0000000..1568b9f
--- /dev/null
@@ -0,0 +1,1092 @@
+/*
+ * ion/mod_statusbar/statusbar.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <libtu/ptrlist.h>
+#include <libtu/misc.h>
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/window.h>
+#include <ioncore/binding.h>
+#include <ioncore/regbind.h>
+#include <ioncore/event.h>
+#include <ioncore/resize.h>
+#include <ioncore/gr.h>
+#include <ioncore/names.h>
+#include <ioncore/strings.h>
+#include <ioncore/basicpholder.h>
+#include <ioncore/sizehint.h>
+
+#include "statusbar.h"
+#include "main.h"
+#include "draw.h"
+
+
+static void statusbar_set_elems(WStatusBar *sb, ExtlTab t);
+static void statusbar_free_elems(WStatusBar *sb);
+static void statusbar_update_natural_size(WStatusBar *p);
+static void statusbar_arrange_systray(WStatusBar *p);
+static int statusbar_systray_x(WStatusBar *p);
+static void statusbar_rearrange(WStatusBar *sb, bool rs);
+static void do_calc_systray_w(WStatusBar *p, WSBElem *el);
+static void statusbar_calc_systray_w(WStatusBar *p);
+
+
+static WStatusBar *statusbars=NULL;
+
+
+/*{{{ Init/deinit */
+
+
+bool statusbar_init(WStatusBar *p, WWindow *parent, const WFitParams *fp)
+{
+    if(!window_init(&(p->wwin), parent, fp))
+        return FALSE;
+
+    p->brush=NULL;
+    p->elems=NULL;
+    p->nelems=0;
+    p->natural_w=1;
+    p->natural_h=1;
+    p->filleridx=-1;
+    p->sb_next=NULL;
+    p->sb_prev=NULL;
+    p->traywins=NULL;
+    p->systray_enabled=TRUE;
+    
+    statusbar_updategr(p);
+
+    if(p->brush==NULL){
+        window_deinit(&(p->wwin));
+        return FALSE;
+    }
+    
+    window_select_input(&(p->wwin), IONCORE_EVENTMASK_NORMAL);
+    
+    region_register((WRegion*)p);
+
+    region_add_bindmap((WRegion*)p, mod_statusbar_statusbar_bindmap);
+    
+    ((WRegion*)p)->flags|=REGION_SKIP_FOCUS;
+
+    LINK_ITEM(statusbars, p, sb_next, sb_prev);
+    
+    return TRUE;
+}
+
+
+
+WStatusBar *create_statusbar(WWindow *parent, const WFitParams *fp)
+{
+    CREATEOBJ_IMPL(WStatusBar, statusbar, (p, parent, fp));
+}
+
+
+void statusbar_deinit(WStatusBar *p)
+{
+    UNLINK_ITEM(statusbars, p, sb_next, sb_prev);
+
+    statusbar_free_elems(p);
+    
+    if(p->brush!=NULL){
+        grbrush_release(p->brush);
+        p->brush=NULL;
+    }
+    
+    window_deinit(&(p->wwin));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Content stuff */
+
+static void init_sbelem(WSBElem *el)
+{
+    el->type=WSBELEM_NONE;
+    el->meter=NULL;
+    el->text_w=0;
+    el->text=NULL;
+    el->max_w=0;
+    el->tmpl=NULL;
+    el->attr=NULL;
+    el->stretch=0;
+    el->align=WSBELEM_ALIGN_CENTER;
+    el->zeropad=0;
+    el->x=0;
+    el->traywins=NULL;
+}
+
+
+static WSBElem *get_sbelems(ExtlTab t, int *nret, int *filleridxret)
+{
+    int i, n=extl_table_get_n(t);
+    WSBElem *el;
+    int systrayidx=-1;
+    
+    *nret=0;
+    *filleridxret=-1;
+    
+    if(n<=0)
+        return NULL;
+    
+    el=ALLOC_N(WSBElem, n); 
+    
+    if(el==NULL)
+        return NULL;
+    
+    for(i=0; i<n; i++){
+        ExtlTab tt;
+        
+        init_sbelem(&el[i]);
+
+        if(extl_table_geti_t(t, i+1, &tt)){
+            if(extl_table_gets_i(tt, "type", &(el[i].type))){
+                if(el[i].type==WSBELEM_TEXT || el[i].type==WSBELEM_STRETCH){
+                    extl_table_gets_s(tt, "text", &(el[i].text));
+                }else if(el[i].type==WSBELEM_METER){
+                    extl_table_gets_s(tt, "meter", &(el[i].meter));
+                    extl_table_gets_s(tt, "tmpl", &(el[i].tmpl));
+                    extl_table_gets_i(tt, "align", &(el[i].align));
+                    extl_table_gets_i(tt, "zeropad", &(el[i].zeropad));
+                    el[i].zeropad=maxof(el[i].zeropad, 0);
+                }else if(el[i].type==WSBELEM_SYSTRAY){
+                    extl_table_gets_s(tt, "meter", &(el[i].meter));
+                    extl_table_gets_i(tt, "align", &(el[i].align));
+                    if(el[i].meter==NULL || strcmp(el[i].meter, "systray")==0)
+                        systrayidx=i;
+                }else if(el[i].type==WSBELEM_FILLER){
+                    *filleridxret=i;
+                }
+            }
+            extl_unref_table(tt);
+        }
+    }
+    
+    if(systrayidx==-1){
+        WSBElem *el2=REALLOC_N(el, WSBElem, n, n+1);
+        if(el2!=NULL){
+            el=el2;
+            init_sbelem(&el[n]);
+            el[n].type=WSBELEM_SYSTRAY;
+            n++;
+        }
+    }
+    
+    *nret=n;
+    
+    return el;
+}
+    
+
+static void free_sbelems(WSBElem *el, int n)
+{
+    int i;
+    
+    for(i=0; i<n; i++){
+        if(el[i].text!=NULL)
+            free(el[i].text);
+        if(el[i].meter!=NULL)
+            free(el[i].meter);
+        if(el[i].tmpl!=NULL)
+            free(el[i].tmpl);
+        if(el[i].attr!=NULL)
+            free(el[i].attr);
+        if(el[i].traywins!=NULL)
+            ptrlist_clear(&el[i].traywins);
+    }
+    
+    free(el);
+}
+
+
+static void statusbar_set_elems(WStatusBar *sb, ExtlTab t)
+{
+    statusbar_free_elems(sb);
+    
+    sb->elems=get_sbelems(t, &(sb->nelems), &(sb->filleridx));
+}
+
+
+static void statusbar_free_elems(WStatusBar *sb)
+{
+    if(sb->elems!=NULL){
+        free_sbelems(sb->elems, sb->nelems);
+        sb->elems=NULL;
+        sb->nelems=0;
+        sb->filleridx=-1;
+    }
+}
+
+
+/*}}}*/
+
+
+
+/*{{{ Size stuff */
+
+
+static void statusbar_resize(WStatusBar *p)
+{
+    WRQGeomParams rq=RQGEOMPARAMS_INIT;
+    
+    rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
+    
+    rq.geom.w=p->natural_w;
+    rq.geom.h=p->natural_h;
+    rq.geom.x=REGION_GEOM(p).x;
+    rq.geom.y=REGION_GEOM(p).y;
+
+    if(rectangle_compare(&rq.geom, &REGION_GEOM(p))!=RECTANGLE_SAME)
+        region_rqgeom((WRegion*)p, &rq, NULL);
+}
+
+
+static void calc_elem_w(WStatusBar *p, WSBElem *el, GrBrush *brush)
+{
+    const char *str;
+
+    if(el->type==WSBELEM_SYSTRAY){
+        do_calc_systray_w(p, el);
+        return;
+    }
+    
+    if(brush==NULL){
+        el->text_w=0;
+        return;
+    }
+    
+    if(el->type==WSBELEM_METER){
+        str=(el->text!=NULL ? el->text : STATUSBAR_NX_STR);
+        el->text_w=grbrush_get_text_width(brush, str, strlen(str));
+        str=el->tmpl;
+        el->max_w=maxof((str!=NULL
+                         ? grbrush_get_text_width(brush, str, strlen(str))
+                         : 0),
+                        el->text_w);
+    }else{
+        str=el->text;
+        el->text_w=(str!=NULL
+                    ? grbrush_get_text_width(brush, str, strlen(str))
+                    : 0);
+        el->max_w=el->text_w;
+    }
+}
+
+
+static void statusbar_calc_widths(WStatusBar *sb)
+{
+    int i;
+    
+    for(i=0; i<sb->nelems; i++)
+        calc_elem_w(sb, &(sb->elems[i]), sb->brush);
+}
+
+
+static void statusbar_do_update_natural_size(WStatusBar *p)
+{
+    GrBorderWidths bdw;
+    GrFontExtents fnte;
+    WRegion *reg;
+    PtrListIterTmp tmp;
+    int totw=0, stmh=0;
+    int i;
+
+    if(p->brush==NULL){
+        bdw.left=0; bdw.right=0;
+        bdw.top=0; bdw.bottom=0;
+        fnte.max_height=4;
+    }else{
+        grbrush_get_border_widths(p->brush, &bdw);
+        grbrush_get_font_extents(p->brush, &fnte);
+    }
+    
+    for(i=0; i<p->nelems; i++)
+        totw+=p->elems[i].max_w;
+        
+    FOR_ALL_ON_PTRLIST(WRegion*, reg, p->traywins, tmp){
+        stmh=maxof(stmh, REGION_GEOM(reg).h);
+    }
+    
+    p->natural_w=bdw.left+totw+bdw.right;
+    p->natural_h=maxof(stmh, fnte.max_height)+bdw.top+bdw.bottom;
+}
+
+
+void statusbar_size_hints(WStatusBar *p, WSizeHints *h)
+{
+    h->min_set=TRUE;
+    h->min_width=p->natural_w;
+    h->min_height=p->natural_h;
+    
+    h->max_set=TRUE;
+    h->max_width=p->natural_w;
+    h->max_height=p->natural_h;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Systray */
+
+
+static WSBElem *statusbar_associate_systray(WStatusBar *sb, WRegion *reg)
+{
+    WClientWin *cwin=OBJ_CAST(reg, WClientWin);
+    WSBElem *el=NULL, *fbel=NULL;
+    char *name=NULL;
+    int i;
+    
+    if(cwin!=NULL)
+        extl_table_gets_s(cwin->proptab, "statusbar", &name);
+    
+    for(i=0; i<sb->nelems; i++){
+        if(sb->elems[i].type!=WSBELEM_SYSTRAY)
+            continue;
+        if(sb->elems[i].meter==NULL){
+            fbel=&sb->elems[i];
+            continue;
+        }
+        if(name!=NULL && strcmp(sb->elems[i].meter, name)==0){
+            el=&sb->elems[i];
+            break;
+        }
+        if(strcmp(sb->elems[i].meter, "systray")==0)
+            fbel=&sb->elems[i];
+    }
+    
+    if(name!=NULL)
+        free(name);
+        
+    if(el==NULL)
+        el=fbel;
+    
+    if(el==NULL)
+        return NULL;
+    
+    ptrlist_insert_last(&el->traywins, (Obj*)reg);
+    
+    return el;
+}
+
+
+static WSBElem *statusbar_unassociate_systray(WStatusBar *sb, WRegion *reg)
+{
+    int i;
+    
+    for(i=0; i<sb->nelems; i++){
+        if(ptrlist_remove(&(sb->elems[i].traywins), (Obj*)reg))
+            return &sb->elems[i];
+    }
+    
+    return NULL;
+}
+
+    
+
+static void do_calc_systray_w(WStatusBar *p, WSBElem *el)
+{
+    WRegion *reg;
+    PtrListIterTmp tmp;
+    int padding=0;
+    int w=-padding;
+    
+    FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){
+        w=w+REGION_GEOM(reg).w+padding;
+    }
+    
+    el->text_w=maxof(0, w);
+    el->max_w=el->text_w; /* for now */
+}
+
+
+static void statusbar_calc_systray_w(WStatusBar *p)
+{
+    int i;
+    
+    for(i=0; i<p->nelems; i++){
+        if(p->elems[i].type==WSBELEM_SYSTRAY)
+            do_calc_systray_w(p, &p->elems[i]);
+    }
+}
+
+
+static void statusbar_arrange_systray(WStatusBar *p)
+{
+    WRegion *reg;
+    PtrListIterTmp tmp;
+    GrBorderWidths bdw;
+    int padding=0, ymiddle;
+    int i, x;
+    
+    if(p->brush!=NULL){
+        grbrush_get_border_widths(p->brush, &bdw);
+    }else{
+        bdw.top=0;
+        bdw.bottom=0;
+    }
+    
+    ymiddle=bdw.top+(REGION_GEOM(p).h-bdw.top-bdw.bottom)/2;
+    
+    for(i=0; i<p->nelems; i++){
+        WSBElem *el=&p->elems[i];
+        if(el->type!=WSBELEM_SYSTRAY)
+            continue;
+        x=el->x;
+        FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){
+            WRectangle g=REGION_GEOM(reg);
+            g.x=x;
+            g.y=ymiddle-g.h/2;
+            region_fit(reg, &g, REGION_FIT_EXACT);
+            x=x+g.w+padding;
+        }
+    }
+}
+
+
+static void systray_adjust_size(WRegion *reg, WRectangle *g)
+{
+    g->h=CF_STATUSBAR_SYSTRAY_HEIGHT;
+    
+    region_size_hints_correct(reg, &g->w, &g->h, TRUE);
+}
+
+
+
+static WRegion *statusbar_do_attach_final(WStatusBar *sb,
+                                          WRegion *reg,
+                                          void *unused)
+{
+    WFitParams fp;
+    WSBElem *el;
+    
+    if(!ptrlist_insert_last(&sb->traywins, (Obj*)reg))
+        return NULL;
+    
+    el=statusbar_associate_systray(sb, reg);
+    if(el==NULL){
+        ptrlist_remove(&sb->traywins, (Obj*)reg);
+        return NULL;
+    }
+
+    fp.g=REGION_GEOM(reg);
+    fp.mode=REGION_FIT_EXACT;
+    systray_adjust_size(reg, &fp.g);
+    
+    region_fitrep(reg, NULL, &fp);
+    
+    do_calc_systray_w(sb, el);
+
+    region_set_manager(reg, (WRegion*)sb);
+    
+    statusbar_rearrange(sb, TRUE);
+    
+    if(REGION_IS_MAPPED(sb))
+        region_map(reg);
+    
+    return reg;
+}
+
+
+static WRegion *statusbar_do_attach(WStatusBar *sb, WRegionAttachData *data)
+{
+    WFitParams fp;
+    
+    fp.g.x=0;
+    fp.g.y=0;
+    fp.g.h=CF_STATUSBAR_SYSTRAY_HEIGHT;
+    fp.g.w=CF_STATUSBAR_SYSTRAY_HEIGHT;
+    fp.mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS;
+    
+    return region_attach_helper((WRegion*)sb, (WWindow*)sb, &fp,
+                                (WRegionDoAttachFn*)statusbar_do_attach_final, 
+                                NULL, data);
+}
+
+
+static WRegion *statusbar_attach_ph(WStatusBar *sb, int flags,
+                                    WRegionAttachData *data)
+{
+    return statusbar_do_attach(sb, data);
+}
+
+
+static WPHolder *statusbar_prepare_manage(WStatusBar *sb, 
+                                          const WClientWin *cwin,
+                                          const WManageParams *param,
+                                          int redir)
+{
+    if(redir==MANAGE_REDIR_STRICT_YES)
+        return NULL;
+    
+    return (WPHolder*)create_basicpholder((WRegion*)sb, 
+                                          ((WBasicPHolderHandler*)
+                                           statusbar_attach_ph));
+}
+
+
+static void statusbar_managed_remove(WStatusBar *sb, WRegion *reg)
+{
+    WSBElem *el;
+        
+    ptrlist_remove(&sb->traywins, (Obj*)reg);
+    
+    el=statusbar_unassociate_systray(sb, reg);
+    
+    region_unset_manager(reg, (WRegion*)sb);
+
+    if(el!=NULL && ioncore_g.opmode!=IONCORE_OPMODE_DEINIT){
+        do_calc_systray_w(sb, el);
+        statusbar_rearrange(sb, TRUE);
+    }
+}
+
+
+static void statusbar_managed_rqgeom(WStatusBar *sb, WRegion *reg, 
+                                     const WRQGeomParams *rq,
+                                     WRectangle *geomret)
+{
+    WRectangle g;
+    
+    g.x=REGION_GEOM(reg).x;
+    g.y=REGION_GEOM(reg).y;
+    g.w=rq->geom.w;
+    g.h=rq->geom.h;
+
+    systray_adjust_size(reg, &g);
+
+    if(rq->flags&REGION_RQGEOM_TRYONLY){
+        if(geomret!=NULL)
+            *geomret=g;
+        return;
+    }
+    
+    region_fit(reg, &g, REGION_FIT_EXACT);
+    
+    statusbar_calc_systray_w(sb);
+    statusbar_rearrange(sb, TRUE);
+    
+    if(geomret!=NULL)
+        *geomret=REGION_GEOM(reg);
+    
+}
+
+
+void statusbar_map(WStatusBar *sb)
+{
+    WRegion *reg;
+    PtrListIterTmp tmp;
+    
+    window_map((WWindow*)sb);
+    
+    FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp)
+        region_map(reg);
+}
+
+
+void statusbar_unmap(WStatusBar *sb)
+{
+    WRegion *reg;
+    PtrListIterTmp tmp;
+    
+    window_unmap((WWindow*)sb);
+    
+    FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp)
+        region_unmap(reg);
+}
+
+
+bool statusbar_fitrep(WStatusBar *sb, WWindow *par, const WFitParams *fp)
+{
+    bool wchg=(REGION_GEOM(sb).w!=fp->g.w);
+    bool hchg=(REGION_GEOM(sb).h!=fp->g.h);
+    
+    window_do_fitrep(&(sb->wwin), par, &(fp->g));
+    
+    if(wchg || hchg){
+        statusbar_calculate_xs(sb);
+        statusbar_arrange_systray(sb);
+        statusbar_draw(sb, TRUE);
+    }
+    
+    return TRUE;
+}
+
+
+WPHolder *statusbar_prepare_manage_transient(WStatusBar *sb, 
+                                             const WClientWin *cwin,
+                                             const WManageParams *param,
+                                             int unused)
+{
+    WRegion *mgr=REGION_MANAGER(sb);
+    
+    if(mgr==NULL)
+        mgr=(WRegion*)region_screen_of((WRegion*)sb);
+    
+    if(mgr!=NULL)
+        return region_prepare_manage(mgr, cwin, param, 
+                                     MANAGE_REDIR_PREFER_NO);
+    else
+        return NULL;
+}
+
+    
+
+/*}}}*/
+
+
+/*{{{ Exports */
+
+
+static ExtlFn parse_template_fn;
+static bool parse_template_fn_set=FALSE;
+
+
+EXTL_EXPORT
+void mod_statusbar__set_template_parser(ExtlFn fn)
+{
+    if(parse_template_fn_set)
+        extl_unref_fn(parse_template_fn);
+    parse_template_fn=extl_ref_fn(fn);
+    parse_template_fn_set=TRUE;
+}
+
+
+/*EXTL_DOC
+ * Set statusbar template.
+ */
+EXTL_EXPORT_MEMBER
+void statusbar_set_template(WStatusBar *sb, const char *tmpl)
+{
+    ExtlTab t=extl_table_none();
+    bool ok=FALSE;
+    
+    if(parse_template_fn_set){
+        extl_protect(NULL);
+        ok=extl_call(parse_template_fn, "s", "t", tmpl, &t);
+        extl_unprotect(NULL);
+    }
+
+    if(ok)
+        statusbar_set_template_table(sb, t);
+}
+
+
+/*EXTL_DOC
+ * Set statusbar template as table.
+ */
+EXTL_EXPORT_MEMBER
+void statusbar_set_template_table(WStatusBar *sb, ExtlTab t)
+{
+    WRegion *reg;
+    PtrListIterTmp tmp;
+    
+    statusbar_set_elems(sb, t);
+
+    FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp){
+        statusbar_associate_systray(sb, reg);
+    }
+    
+    statusbar_calc_widths(sb);
+    statusbar_rearrange(sb, FALSE);
+}
+
+
+/*EXTL_DOC
+ * Get statusbar template as table.
+ */
+EXTL_EXPORT_MEMBER
+ExtlTab statusbar_get_template_table(WStatusBar *sb)
+{
+    int count = sb->nelems;
+    int i;
+
+    ExtlTab t = extl_create_table();
+
+    for(i=0; i<count; i++){
+        ExtlTab tt = extl_create_table();
+        
+        extl_table_sets_i(tt, "type", sb->elems[i].type);
+        extl_table_sets_s(tt, "text", sb->elems[i].text);
+        extl_table_sets_s(tt, "meter", sb->elems[i].meter);
+        extl_table_sets_s(tt, "tmpl", sb->elems[i].tmpl);
+        extl_table_sets_i(tt, "align", sb->elems[i].align);
+        extl_table_sets_i(tt, "zeropad", sb->elems[i].zeropad);
+
+        extl_table_seti_t(t, (i+1), tt);
+        extl_unref_table(tt);
+    }
+
+    return t;
+}
+
+
+static void reset_stretch(WStatusBar *sb)
+{
+    int i;
+    
+    for(i=0; i<sb->nelems; i++)
+        sb->elems[i].stretch=0;
+}
+
+
+static void positive_stretch(WStatusBar *sb)
+{
+    int i;
+    
+    for(i=0; i<sb->nelems; i++)
+        sb->elems[i].stretch=maxof(0, sb->elems[i].stretch);
+}
+
+
+static void spread_stretch(WStatusBar *sb)
+{
+    int i, j, k;
+    int diff;
+    WSBElem *el, *lel, *rel;
+    const char *str;
+    
+    for(i=0; i<sb->nelems; i++){
+        el=&(sb->elems[i]);
+
+        if(el->type!=WSBELEM_METER && el->type!=WSBELEM_SYSTRAY)
+            continue;
+        
+        diff=el->max_w-el->text_w;
+        
+        lel=NULL;
+        rel=NULL;
+        
+        if(el->align!=WSBELEM_ALIGN_RIGHT){
+            for(j=i+1; j<sb->nelems; j++){
+                if(sb->elems[j].type==WSBELEM_STRETCH){
+                    rel=&(sb->elems[j]);
+                    break;
+                }
+            }
+        }
+        
+        if(el->align!=WSBELEM_ALIGN_LEFT){
+            for(k=i-1; k>=0; k--){
+                if(sb->elems[k].type==WSBELEM_STRETCH){
+                    lel=&(sb->elems[k]);
+                    break;
+                }
+            }
+        }
+        
+        if(rel!=NULL && lel!=NULL){
+            int l=diff/2;
+            int r=diff-l;
+            lel->stretch+=l;
+            rel->stretch+=r;
+        }else if(lel!=NULL){
+            lel->stretch+=diff;
+        }else if(rel!=NULL){
+            rel->stretch+=diff;
+        }
+    }
+}
+
+
+static void statusbar_rearrange(WStatusBar *sb, bool rs)
+{
+    if(rs){
+        int onw=sb->natural_w;
+        int onh=sb->natural_h;
+        
+        statusbar_do_update_natural_size(sb);
+        
+        if(    (sb->natural_h>onh && REGION_GEOM(sb).h>=onh)
+            || (sb->natural_h<onh && REGION_GEOM(sb).h<=onh)
+            || (sb->natural_w>onw && REGION_GEOM(sb).w>=onw)
+            || (sb->natural_w<onw && REGION_GEOM(sb).w<=onw)){
+            
+            statusbar_resize(sb);
+        }
+    }
+
+    reset_stretch(sb);
+    spread_stretch(sb);
+    positive_stretch(sb);
+    statusbar_calculate_xs(sb);
+    
+    if(rs)
+        statusbar_arrange_systray(sb);
+}
+
+
+
+/*EXTL_DOC
+ * Set statusbar template.
+ */
+EXTL_EXPORT_MEMBER
+void statusbar_update(WStatusBar *sb, ExtlTab t)
+{
+    int i;
+    WSBElem *el;
+    bool grow=FALSE;
+    
+    if(sb->brush==NULL)
+        return;
+    
+    for(i=0; i<sb->nelems; i++){
+        el=&(sb->elems[i]);
+        
+        if(el->type!=WSBELEM_METER)
+            continue;
+        
+        if(el->text!=NULL){
+            free(el->text);
+            el->text=NULL;
+        }
+
+        if(el->attr!=NULL){
+            free(el->attr);
+            el->attr=NULL;
+        }
+        
+        if(el->meter!=NULL){
+            const char *str;
+            char *attrnm;
+            
+            extl_table_gets_s(t, el->meter, &(el->text));
+            
+            if(el->text==NULL){
+                str=STATUSBAR_NX_STR;
+            }else{
+                /* Zero-pad */
+                int l=strlen(el->text);
+                int ml=str_len(el->text);
+                int diff=el->zeropad-ml;
+                if(diff>0){
+                    char *tmp=ALLOC_N(char, l+diff+1);
+                    if(tmp!=NULL){
+                        memset(tmp, '0', diff);
+                        memcpy(tmp+diff, el->text, l+1);
+                        free(el->text);
+                        el->text=tmp;
+                    }
+                }
+                str=el->text;
+            }
+            
+            if(el->tmpl!=NULL && el->text!=NULL){
+                char *tmp=grbrush_make_label(sb->brush, el->text, el->max_w);
+                if(tmp!=NULL){
+                    free(el->text);
+                    el->text=tmp;
+                    str=tmp;
+                }
+            }
+
+            el->text_w=grbrush_get_text_width(sb->brush, str, strlen(str));
+            
+            if(el->text_w>el->max_w && el->tmpl==NULL){
+                el->max_w=el->text_w;
+                grow=TRUE;
+            }
+            
+            attrnm=scat(el->meter, "_hint");
+            if(attrnm!=NULL){
+                extl_table_gets_s(t, attrnm, &(el->attr));
+                free(attrnm);
+            }
+        }
+    }
+
+    statusbar_rearrange(sb, grow);
+    
+    window_draw((WWindow*)sb, FALSE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Updategr */
+
+
+void statusbar_updategr(WStatusBar *p)
+{
+    GrBrush *nbrush;
+    
+    nbrush=gr_get_brush(p->wwin.win, region_rootwin_of((WRegion*)p),
+                        "stdisp-statusbar");
+    if(nbrush==NULL)
+        return;
+    
+    if(p->brush!=NULL)
+        grbrush_release(p->brush);
+    
+    p->brush=nbrush;
+
+    statusbar_calc_widths(p);
+    statusbar_rearrange(p, TRUE);
+    
+    window_draw(&(p->wwin), TRUE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Misc */
+
+
+int statusbar_orientation(WStatusBar *sb)
+{
+    return REGION_ORIENTATION_HORIZONTAL;
+}
+
+
+/*EXTL_DOC
+ * Returns a list of all statusbars.
+ */
+EXTL_EXPORT
+ExtlTab mod_statusbar_statusbars()
+{
+    ExtlTab t=extl_create_table();
+    WStatusBar *sb;
+    int i=1;
+    
+    for(sb=statusbars; sb!=NULL; sb=sb->sb_next){
+        extl_table_seti_o(t, i, (Obj*)sb);
+        i++;
+    }
+    
+    return t;
+}
+
+
+WStatusBar *mod_statusbar_find_suitable(WClientWin *cwin,
+                                        const WManageParams *param)
+{
+    WStatusBar *sb;
+
+    for(sb=statusbars; sb!=NULL; sb=sb->sb_next){
+        /*if(!sb->is_auto)
+            continue;*/
+        if(!sb->systray_enabled)
+            continue;
+        if(!region_same_rootwin((WRegion*)sb, (WRegion*)cwin))
+            continue;
+        break;
+    }
+
+    return sb;
+}
+
+
+bool statusbar_set_systray(WStatusBar *sb, int sp)
+{
+    bool set=sb->systray_enabled;
+    bool nset=libtu_do_setparam(sp, set);
+    
+    sb->systray_enabled=nset;
+    
+    return nset;
+}
+
+
+/*EXTL_DOC
+ * Enable or disable use of \var{sb} as systray.
+ * The parameter \var{how} can be one of (set/unset/toggle). 
+ * Resulting state is returned.
+ */
+EXTL_EXPORT_AS(WStatusBar, set_systray)
+bool statusbar_set_systray_extl(WStatusBar *sb, const char *how)
+{
+    return statusbar_set_systray(sb, libtu_string_to_setparam(how));
+}
+
+
+/*EXTL_DOC
+ * Is \var{sb} used as a systray?
+ */
+EXTL_EXPORT_MEMBER
+bool statusbar_is_systray_extl(WStatusBar *sb)
+{
+    return sb->systray_enabled;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Load */
+
+
+WRegion *statusbar_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
+{
+    WStatusBar *sb=create_statusbar(par, fp);
+
+    if(sb!=NULL){
+        char *tmpl=NULL;
+        ExtlTab t=extl_table_none();
+        if(extl_table_gets_s(tab, "template", &tmpl)){
+            statusbar_set_template(sb, tmpl);
+            free(tmpl);
+        }else if(extl_table_gets_t(tab, "template_table", &t)){
+            statusbar_set_template_table(sb, t);
+            extl_unref_table(t);
+        }else{
+            const char *tmpl=TR("[ %date || load: %load ] %filler%systray");
+            statusbar_set_template(sb, tmpl);
+        }
+        
+        extl_table_gets_b(tab, "systray", &sb->systray_enabled);
+    }
+    
+    return (WRegion*)sb;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab statusbar_dynfuntab[]={
+    {window_draw, statusbar_draw},
+    {region_updategr, statusbar_updategr},
+    {region_size_hints, statusbar_size_hints},
+    {(DynFun*)region_orientation, (DynFun*)statusbar_orientation},
+
+    {region_managed_rqgeom, statusbar_managed_rqgeom},
+    {(DynFun*)region_prepare_manage, (DynFun*)statusbar_prepare_manage},
+    {region_managed_remove, statusbar_managed_remove},
+    
+    {(DynFun*)region_prepare_manage_transient, 
+     (DynFun*)statusbar_prepare_manage_transient},
+    
+    {region_map, statusbar_map},
+    {region_unmap, statusbar_unmap},
+    
+    {(DynFun*)region_fitrep,
+     (DynFun*)statusbar_fitrep},
+    
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WStatusBar, WWindow, statusbar_deinit, statusbar_dynfuntab);
+
+    
+/*}}}*/
+
diff --git a/mod_statusbar/statusbar.h b/mod_statusbar/statusbar.h
new file mode 100644 (file)
index 0000000..73999ad
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * ion/mod_statusbar/statusbar.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_STATUSBAR_STATUSBAR_H
+#define ION_MOD_STATUSBAR_STATUSBAR_H
+
+#include <libtu/ptrlist.h>
+#include <libextl/extl.h>
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+#include <ioncore/manage.h>
+#include <ioncore/sizehint.h>
+
+
+#define STATUSBAR_NX_STR "?"
+
+
+typedef enum{
+    WSBELEM_ALIGN_LEFT=0,
+    WSBELEM_ALIGN_CENTER=1,
+    WSBELEM_ALIGN_RIGHT=2
+} WSBElemAlign;
+
+
+typedef enum{
+    WSBELEM_NONE=0,
+    WSBELEM_TEXT=1,
+    WSBELEM_METER=2,
+    WSBELEM_STRETCH=3,
+    WSBELEM_FILLER=4,
+    WSBELEM_SYSTRAY=5
+} WSBElemType;
+  
+
+INTRSTRUCT(WSBElem);
+
+DECLSTRUCT(WSBElem){
+    int type;
+    int align;
+    int stretch;
+    int text_w;
+    char *text;
+    char *meter;
+    int max_w;
+    char *tmpl;
+    char *attr;
+    int zeropad;
+    int x;
+    PtrList *traywins;
+};
+
+INTRCLASS(WStatusBar);
+
+DECLCLASS(WStatusBar){
+    WWindow wwin;
+    GrBrush *brush;
+    WSBElem *elems;
+    int nelems;
+    int natural_w, natural_h;
+    int filleridx;
+    WStatusBar *sb_next, *sb_prev;
+    PtrList *traywins;
+    bool systray_enabled;
+};
+
+extern bool statusbar_init(WStatusBar *p, WWindow *parent, 
+                           const WFitParams *fp);
+extern WStatusBar *create_statusbar(WWindow *parent, const WFitParams *fp);
+extern void statusbar_deinit(WStatusBar *p);
+
+extern WRegion *statusbar_load(WWindow *par, const WFitParams *fp, 
+                               ExtlTab tab);
+
+extern void statusbar_set_natural_w(WStatusBar *p, const char *str);
+extern void statusbar_size_hints(WStatusBar *p, WSizeHints *h);
+extern void statusbar_updategr(WStatusBar *p);
+extern void statusbar_set_contents(WStatusBar *sb, ExtlTab t);
+
+extern void statusbar_set_template(WStatusBar *sb, const char *tmpl);
+extern void statusbar_set_template_table(WStatusBar *sb, ExtlTab t);
+extern ExtlTab statusbar_get_template_table(WStatusBar *sb);
+
+extern WStatusBar *mod_statusbar_find_suitable(WClientWin *cwin,
+                                               const WManageParams *param);
+
+#endif /* ION_MOD_STATUSBAR_STATUSBAR_H */
diff --git a/mod_tiling/Makefile b/mod_tiling/Makefile
new file mode 100644 (file)
index 0000000..a921270
--- /dev/null
@@ -0,0 +1,30 @@
+##
+## Ion workspace module Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I..
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+SOURCES=main.c tiling.c placement.c split.c split-stdisp.c \
+       splitfloat.c panehandle.c ops.c
+
+MAKE_EXPORTS=mod_tiling
+
+MODULE=mod_tiling
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install: module_install
+
+
+
diff --git a/mod_tiling/main.c b/mod_tiling/main.c
new file mode 100644 (file)
index 0000000..5901c1c
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * ion/mod_tiling/main.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/map.h>
+
+#include <ioncore/common.h>
+#include <ioncore/reginfo.h>
+#include <libextl/readconfig.h>
+#include <ioncore/framep.h>
+#include <ioncore/bindmaps.h>
+#include <ioncore/bindmaps.h>
+
+#include "main.h"
+#include "tiling.h"
+#include "placement.h"
+#include "exports.h"
+
+
+/*{{{ Module information */
+
+
+#include "../version.h"
+
+char mod_tiling_ion_api_version[]=ION_API_VERSION;
+
+
+/*}}}*/
+
+
+/*{{{ Bindmaps and configuration variables */
+
+
+WBindmap *mod_tiling_tiling_bindmap=NULL;
+
+int mod_tiling_raise_delay=CF_RAISE_DELAY;
+
+
+/*}}}*/
+
+
+/*{{{ Configuration */
+
+
+/*EXTL_DOC
+ * Set parameters. Currently only \var{raise_delay} (in milliseconds)
+ * is supported.
+ */
+EXTL_EXPORT
+void mod_tiling_set(ExtlTab tab)
+{
+    int d;
+    if(extl_table_gets_i(tab, "raise_delay", &d)){
+        if(d>=0)
+            mod_tiling_raise_delay=d;
+    }
+}
+
+
+/*EXTL_DOC
+ * Get parameters. For details see \fnref{mod_tiling.set}.
+ */
+EXTL_SAFE
+EXTL_EXPORT
+ExtlTab mod_tiling_get()
+{
+    ExtlTab tab=extl_create_table();
+    
+    extl_table_sets_i(tab, "raise_delay", mod_tiling_raise_delay);
+    
+    return tab;
+}
+
+
+
+/*}}}*/
+
+
+
+/*{{{ Module init & deinit */
+
+
+void mod_tiling_deinit()
+{
+    mod_tiling_unregister_exports();
+    ioncore_unregister_regclass(&CLASSDESCR(WTiling));
+    
+    if(mod_tiling_tiling_bindmap!=NULL){
+        ioncore_free_bindmap("WTiling", mod_tiling_tiling_bindmap);
+        mod_tiling_tiling_bindmap=NULL;
+    }
+    
+    if(tiling_placement_alt!=NULL){
+        destroy_obj((Obj*)tiling_placement_alt);
+        tiling_placement_alt=NULL;
+    }
+}
+
+
+static bool register_regions()
+{
+    if(!ioncore_register_regclass(&CLASSDESCR(WTiling),
+                                  (WRegionLoadCreateFn*)tiling_load)){
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+#define INIT_HOOK_(NM)                             \
+    NM=mainloop_register_hook(#NM, create_hook()); \
+    if(NM==NULL) return FALSE;
+
+
+static bool init_hooks()
+{
+    INIT_HOOK_(tiling_placement_alt);
+    return TRUE;
+}
+
+
+bool mod_tiling_init()
+{
+    if(!init_hooks())
+        goto err;
+            
+    mod_tiling_tiling_bindmap=ioncore_alloc_bindmap("WTiling", NULL);
+
+    if(mod_tiling_tiling_bindmap==NULL)
+        goto err;
+
+    if(!mod_tiling_register_exports())
+        goto err;
+
+    if(!register_regions())
+        goto err;
+    
+    extl_read_config("cfg_tiling", NULL, TRUE);
+
+    return TRUE;
+    
+err:
+    mod_tiling_deinit();
+    return FALSE;
+}
+
+
+/*}}}*/
+
diff --git a/mod_tiling/main.h b/mod_tiling/main.h
new file mode 100644 (file)
index 0000000..35d9ddf
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * ion/mod_tiling/main.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_TILING_MAIN_H
+#define ION_MOD_TILING_MAIN_H
+
+#include <ioncore/binding.h>
+#include <ioncore/regbind.h>
+
+extern bool mod_tiling_init();
+extern void mod_tiling_deinit();
+
+extern WBindmap *mod_tiling_tiling_bindmap;
+extern WBindmap *mod_tiling_frame_bindmap;
+
+extern int mod_tiling_raise_delay;
+
+#endif /* ION_MOD_TILING_MAIN_H */
diff --git a/mod_tiling/ops.c b/mod_tiling/ops.c
new file mode 100644 (file)
index 0000000..c86a80a
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * ion/mod_tiling/ops.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/objp.h>
+
+#include <ioncore/common.h>
+#include <ioncore/mplex.h>
+#include <ioncore/focus.h>
+#include <ioncore/group.h>
+#include <ioncore/group-ws.h>
+#include <ioncore/framedpholder.h>
+#include "tiling.h"
+
+
+static WGroup *find_group(WRegion *reg, bool *detach_framed)
+{
+    WRegion *mgr=REGION_MANAGER(reg);
+    bool was_grouped=FALSE;
+    
+    if(OBJ_IS(mgr, WMPlex)){
+        WMPlex *mplex=(WMPlex*)mgr;
+        *detach_framed=TRUE;
+        mgr=REGION_MANAGER(mgr);
+        if(OBJ_IS(mgr, WGroup)){
+            assert(mplex->mgd!=NULL);
+            if(mplex->mgd->reg==reg && mplex->mgd->mgr_next==NULL){
+                /* Nothing to detach */
+                return NULL;
+            }
+        }
+    }else{
+        was_grouped=OBJ_IS(mgr, WGroup);
+        *detach_framed=FALSE;
+    }
+    
+    while(mgr!=NULL){
+        mgr=REGION_MANAGER(mgr);
+        if(OBJ_IS(mgr, WGroup))
+            break;
+    }
+
+    if(mgr==NULL && was_grouped)
+        warn(TR("Already detached."));
+    
+    return (WGroup*)mgr;
+}
+
+
+static void get_relative_geom(WRectangle *g, WRegion *reg, WRegion *mgr)
+{
+    WWindow *rel=REGION_PARENT(mgr), *w;
+    
+    *g=REGION_GEOM(reg);
+    
+    for(w=REGION_PARENT(reg); 
+        w!=rel && (WRegion*)w!=mgr; 
+        w=REGION_PARENT(w)){
+        
+        g->x+=REGION_GEOM(w).x;
+        g->y+=REGION_GEOM(w).y;
+    }
+}
+
+
+/*EXTL_DOC
+ * Detach \var{reg}, i.e. make it managed by its nearest ancestor
+ * \type{WGroup}, framed if \var{reg} is not itself \type{WFrame}.
+ */
+EXTL_EXPORT
+bool mod_tiling_detach(WRegion *reg)
+{
+    bool detach_framed=!OBJ_IS(reg, WFrame);
+    WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
+    WRegionAttachData data;
+    WGroup *grp;
+    
+    grp=find_group(reg, &detach_framed);
+    
+    if(grp==NULL)
+        return FALSE;
+    
+    ap.switchto_set=TRUE;
+    ap.switchto=region_may_control_focus(reg);
+    
+    ap.geom_set=TRUE;
+    get_relative_geom(&ap.geom, reg, (WRegion*)grp);
+    
+    /* TODO: Retain (root-relative) geometry of reg for framed 
+     * detach instead of making a frame of this size?
+     */
+    
+    data.type=REGION_ATTACH_REPARENT;
+    data.u.reg=reg;
+    
+    if(detach_framed){
+        WFramedParam fp=FRAMEDPARAM_INIT;
+        
+        return (region_attach_framed((WRegion*)grp, &fp,
+                                     (WRegionAttachFn*)group_do_attach,
+                                     &ap, &data)!=NULL);
+    }else{
+        return (group_do_attach(grp, &ap, &data)!=NULL);
+    }
+}
+
+
+static WRegion *mkbottom_fn(WWindow *parent, const WFitParams *fp, 
+                            void *param)
+{
+    WRegion *reg=(WRegion*)param;
+    WTiling *tiling;
+    WSplitRegion *node=NULL;
+    
+    if(!region_fitrep(reg, parent, fp))
+        return NULL;
+    
+    tiling=create_tiling(parent, fp, NULL, FALSE);
+    
+    if(tiling==NULL)
+        return NULL;
+    
+    node=create_splitregion(&REGION_GEOM(tiling), reg);
+    if(node!=NULL){
+        tiling->split_tree=(WSplit*)node;
+        tiling->split_tree->ws_if_root=tiling;
+        
+        region_detach_manager(reg);
+        
+        if(tiling_managed_add(tiling, reg))
+            return (WRegion*)tiling;
+        
+        #warning "TODO: reattach?"
+        
+        destroy_obj((Obj*)tiling->split_tree);
+        tiling->split_tree=NULL;
+    }
+    
+    destroy_obj((Obj*)tiling);
+    return NULL;
+}
+
+
+/*EXTL_DOC
+ * Create a new \type{WTiling} 'bottom' for the group of \var{reg},
+ * consisting of \var{reg}.
+ */
+EXTL_EXPORT
+bool mod_tiling_mkbottom(WRegion *reg)
+{
+    WGroup *grp=REGION_MANAGER_CHK(reg, WGroup);
+    WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
+    WRegionAttachData data;
+    WRegion *tiling;
+    
+    if(grp==NULL){
+        warn(TR("Not member of a group"));
+        return FALSE;
+    }
+    
+    if(grp->bottom!=NULL){
+        warn(TR("Manager group already has bottom"));
+        return FALSE;
+    }
+    
+    ap.level_set=TRUE;
+    ap.level=STACKING_LEVEL_BOTTOM;
+    
+    ap.szplcy_set=TRUE;
+    ap.szplcy=SIZEPOLICY_FULL_EXACT;
+    
+    ap.switchto_set=TRUE;
+    ap.switchto=region_may_control_focus(reg);
+    
+    ap.bottom=TRUE;
+
+    data.type=REGION_ATTACH_NEW;
+    data.u.n.fn=mkbottom_fn;
+    data.u.n.param=reg;
+    
+    /* kele... poisto samalla kuin attach */
+    return (group_do_attach(grp, &ap, &data)!=NULL);
+}
diff --git a/mod_tiling/panehandle.c b/mod_tiling/panehandle.c
new file mode 100644 (file)
index 0000000..586385d
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * ion/panews/panehandle.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/event.h>
+#include <ioncore/gr.h>
+#include <ioncore/regbind.h>
+#include "panehandle.h"
+#include "main.h"
+
+
+/*{{{ Init/deinit */
+
+
+static void panehandle_getbrush(WPaneHandle *pwin)
+{
+    GrBrush *brush=gr_get_brush(pwin->wwin.win, 
+                                region_rootwin_of((WRegion*)pwin), 
+                                "pane");
+
+    if(brush!=NULL){
+        if(pwin->brush!=NULL)
+            grbrush_release(pwin->brush);
+        
+        pwin->brush=brush;
+        
+        grbrush_get_border_widths(brush, &(pwin->bdw));
+        grbrush_enable_transparency(brush, GR_TRANSPARENCY_YES);
+    }
+}
+
+
+bool panehandle_init(WPaneHandle *pwin, WWindow *parent, const WFitParams *fp)
+{
+    pwin->brush=NULL;
+    pwin->bline=GR_BORDERLINE_NONE;
+    pwin->splitfloat=NULL;
+    
+    if(!window_init(&(pwin->wwin), parent, fp))
+        return FALSE;
+    
+    ((WRegion*)pwin)->flags|=REGION_SKIP_FOCUS;
+    
+    panehandle_getbrush(pwin);
+    
+    if(pwin->brush==NULL){
+        GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT;
+        memcpy(&(pwin->bdw), &bdw, sizeof(bdw));
+    }
+
+    window_select_input(&(pwin->wwin), IONCORE_EVENTMASK_NORMAL);
+    
+    return TRUE;
+}
+
+
+WPaneHandle *create_panehandle(WWindow *parent, const WFitParams *fp)
+{
+    CREATEOBJ_IMPL(WPaneHandle, panehandle, (p, parent, fp));
+}
+
+
+void panehandle_deinit(WPaneHandle *pwin)
+{
+    assert(pwin->splitfloat==NULL);
+    
+    if(pwin->brush!=NULL){
+        grbrush_release(pwin->brush);
+        pwin->brush=NULL;
+    }
+    
+    window_deinit(&(pwin->wwin));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Drawing */
+
+
+static void panehandle_updategr(WPaneHandle *pwin)
+{
+    panehandle_getbrush(pwin);
+    region_updategr_default((WRegion*)pwin);
+}
+
+
+static void panehandle_draw(WPaneHandle *pwin, bool complete)
+{
+    WRectangle g;
+    
+    if(pwin->brush==NULL)
+        return;
+    
+    g.x=0;
+    g.y=0;
+    g.w=REGION_GEOM(pwin).w;
+    g.h=REGION_GEOM(pwin).h;
+    
+    grbrush_begin(pwin->brush, &g, (complete ? 0 : GRBRUSH_NO_CLEAR_OK));
+    
+    grbrush_draw_borderline(pwin->brush, &g, NULL, pwin->bline);
+    
+    grbrush_end(pwin->brush);
+}
+
+
+/*}}}*/
+
+
+/*{{{ The class */
+
+
+static DynFunTab panehandle_dynfuntab[]={
+    {region_updategr, panehandle_updategr},
+    {window_draw, panehandle_draw},
+    END_DYNFUNTAB,
+};
+
+
+IMPLCLASS(WPaneHandle, WWindow, panehandle_deinit, panehandle_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/mod_tiling/panehandle.h b/mod_tiling/panehandle.h
new file mode 100644 (file)
index 0000000..c4ad184
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * ion/panews/panehandle.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_PANEWS_PANEHANDLE_H
+#define ION_PANEWS_PANEHANDLE_H
+
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+
+INTRCLASS(WPaneHandle);
+
+#include "splitfloat.h"
+
+DECLCLASS(WPaneHandle){
+    WWindow wwin;
+    GrBrush *brush;
+    GrBorderLine bline;
+    GrBorderWidths bdw;
+    WSplitFloat *splitfloat;
+};
+
+extern bool panehandle_init(WPaneHandle *pwin, 
+                            WWindow *parent, const WFitParams *fp);
+extern WPaneHandle *create_panehandle(WWindow *parent, const WFitParams *fp);
+
+#endif /* ION_PANEWS_PANEHANDLE_H */
diff --git a/mod_tiling/placement.c b/mod_tiling/placement.c
new file mode 100644 (file)
index 0000000..47cfa76
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * ion/mod_tiling/placement.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/clientwin.h>
+#include <ioncore/attach.h>
+#include <ioncore/manage.h>
+#include <libextl/extl.h>
+#include <ioncore/framep.h>
+#include <ioncore/names.h>
+#include "placement.h"
+#include "tiling.h"
+
+
+WHook *tiling_placement_alt=NULL;
+
+
+static WRegion *find_suitable_target(WTiling *ws)
+{
+    WRegion *r=tiling_current(ws);
+    
+    if(r==NULL){
+        FOR_ALL_MANAGED_BY_TILING_UNSAFE(r, ws)
+            break;
+    }
+    
+    return r;
+}
+
+
+static bool placement_mrsh_extl(ExtlFn fn, WTilingPlacementParams *param)
+{
+    ExtlTab t, mp;
+    bool ret=FALSE;
+    
+    t=extl_create_table();
+    
+    mp=manageparams_to_table(param->mp);
+    
+    extl_table_sets_o(t, "tiling", (Obj*)param->ws);
+    extl_table_sets_o(t, "reg", (Obj*)param->reg);
+    extl_table_sets_t(t, "mp", mp);
+    
+    extl_unref_table(mp);
+    
+    extl_protect(NULL);
+    ret=extl_call(fn, "t", "b", t, &ret);
+    extl_unprotect(NULL);
+    
+    if(ret){
+        Obj *tmp=NULL;
+        
+        extl_table_gets_o(t, "res_frame", &tmp);
+        
+        param->res_frame=OBJ_CAST(tmp, WFrame);
+        ret=(param->res_frame!=NULL);
+    }
+            
+    extl_unref_table(t);
+    
+    return ret;
+}
+
+
+WPHolder *tiling_prepare_manage(WTiling *ws, const WClientWin *cwin,
+                               const WManageParams *mp, int redir)
+{
+    WRegion *target=NULL;
+    WTilingPlacementParams param;
+    WPHolder *ph;
+    bool ret;
+
+    if(redir==MANAGE_REDIR_STRICT_NO)
+        return NULL;
+
+    param.ws=ws;
+    param.reg=(WRegion*)cwin;
+    param.mp=mp;
+    param.res_frame=NULL;
+    
+    ret=hook_call_alt_p(tiling_placement_alt, &param, 
+                        (WHookMarshallExtl*)placement_mrsh_extl);
+        
+    if(ret && param.res_frame!=NULL &&
+       REGION_MANAGER(param.res_frame)==(WRegion*)ws){
+        
+        target=(WRegion*)param.res_frame;
+        
+        ph=region_prepare_manage(target, cwin, mp, redir);
+        if(ph!=NULL)
+            return ph;
+    }
+
+    target=find_suitable_target(ws);
+    
+    if(target==NULL){
+        warn(TR("Ooops... could not find a region to attach client window "
+                "to on workspace %s."), region_name((WRegion*)ws));
+        return NULL;
+    }
+    
+    return region_prepare_manage(target, cwin, mp, redir);
+}
+
diff --git a/mod_tiling/placement.h b/mod_tiling/placement.h
new file mode 100644 (file)
index 0000000..9094b62
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * ion/mod_tiling/placement.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_TILING_PLACEMENT_H
+#define ION_MOD_TILING_PLACEMENT_H
+
+#include <libmainloop/hooks.h>
+#include <ioncore/common.h>
+#include <ioncore/clientwin.h>
+#include <ioncore/manage.h>
+#include "tiling.h"
+
+
+typedef struct{
+    WTiling *ws;
+    WRegion *reg;
+    const WManageParams *mp;
+    
+    WFrame *res_frame;
+} WTilingPlacementParams;
+
+/* Handlers of this hook should take (WClientWin*, WTiling*, WManageParams*) 
+ * as parameter. 
+ */
+extern WHook *tiling_placement_alt;
+
+extern WPHolder *tiling_prepare_manage(WTiling *ws, const WClientWin *cwin,
+                                      const WManageParams *param, int redir);
+
+#endif /* ION_MOD_TILING_PLACEMENT_H */
diff --git a/mod_tiling/split-stdisp.c b/mod_tiling/split-stdisp.c
new file mode 100644 (file)
index 0000000..d1c55f2
--- /dev/null
@@ -0,0 +1,747 @@
+/*
+ * ion/mod_tiling/split-stdisp.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libtu/minmax.h>
+#include <ioncore/common.h>
+#include <ioncore/mplex.h>
+#include <ioncore/resize.h>
+#include "split.h"
+#include "split-stdisp.h"
+#include "tiling.h"
+
+
+/*{{{ Helper routines */
+
+
+#define STDISP_IS_HORIZONTAL(STDISP) \
+        ((STDISP)->orientation==REGION_ORIENTATION_HORIZONTAL)
+
+#define STDISP_IS_VERTICAL(STDISP) \
+        ((STDISP)->orientation==REGION_ORIENTATION_VERTICAL)
+
+#define STDISP_GROWS_L_TO_R(STDISP) (STDISP_IS_HORIZONTAL(STDISP) && \
+    ((STDISP)->corner==MPLEX_STDISP_TL ||                            \
+     (STDISP)->corner==MPLEX_STDISP_BL))
+
+#define STDISP_GROWS_R_TO_L(STDISP) (STDISP_IS_HORIZONTAL(STDISP) && \
+    ((STDISP)->corner==MPLEX_STDISP_TR ||                            \
+     (STDISP)->corner==MPLEX_STDISP_BR))
+
+#define STDISP_GROWS_T_TO_B(STDISP) (STDISP_IS_VERTICAL(STDISP) && \
+    ((STDISP)->corner==MPLEX_STDISP_TL ||                          \
+     (STDISP)->corner==MPLEX_STDISP_TR))
+
+#define STDISP_GROWS_B_TO_T(STDISP) (STDISP_IS_VERTICAL(STDISP) && \
+    ((STDISP)->corner==MPLEX_STDISP_BL ||                          \
+     (STDISP)->corner==MPLEX_STDISP_BR))
+
+#define GEOM(S) (((WSplit*)S)->geom)
+
+#define IMPLIES(X, Y) (!(X) || (Y))
+
+
+static int other_dir(int dir)
+{
+    return (dir==SPLIT_VERTICAL ? SPLIT_HORIZONTAL : SPLIT_VERTICAL);
+}
+
+
+static void swap(int *x, int *y)
+{
+    int z=*x;
+    *x=*y;
+    *y=z;
+}
+
+
+static void swapptr(WSplit **x, WSplit **y)
+{
+    void *z=*x;
+    *x=*y;
+    *y=z;
+}
+
+
+static void swapgeom(WRectangle *g, WRectangle *h)
+{
+    WRectangle tmp=*g;
+    *g=*h;
+    *h=tmp;
+}
+
+
+int stdisp_recommended_w(WSplitST *stdisp)
+{
+    if(stdisp->regnode.reg==NULL)
+        return CF_STDISP_MIN_SZ;
+    
+    if(stdisp->fullsize && stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){
+        WTiling *ws=REGION_MANAGER_CHK(stdisp->regnode.reg, WTiling);
+        assert(ws!=NULL);
+        return REGION_GEOM(ws).w;
+    }
+    
+    return maxof(CF_STDISP_MIN_SZ, region_min_w(stdisp->regnode.reg));
+}
+
+
+int stdisp_recommended_h(WSplitST *stdisp)
+{
+    if(stdisp->regnode.reg==NULL)
+        return CF_STDISP_MIN_SZ;
+    
+    if(stdisp->fullsize && stdisp->orientation==REGION_ORIENTATION_VERTICAL){
+        WTiling *ws=REGION_MANAGER_CHK(stdisp->regnode.reg, WTiling);
+        assert(ws!=NULL);
+        return REGION_GEOM(ws).h;
+    }
+
+    return maxof(CF_STDISP_MIN_SZ, region_min_h(stdisp->regnode.reg));
+}
+
+
+static bool stdisp_dir_ok(WSplitSplit *p, WSplitST *stdisp)
+{
+    assert(p->tl==(WSplit*)stdisp || p->br==(WSplit*)stdisp);
+    
+    return (IMPLIES(STDISP_IS_HORIZONTAL(stdisp), p->dir==SPLIT_VERTICAL) &&
+            IMPLIES(STDISP_IS_VERTICAL(stdisp), p->dir==SPLIT_HORIZONTAL));
+}
+
+
+/*}}}*/
+
+
+/*{{{ New rotation and flipping primitives */
+
+
+static void replace(WSplitSplit *a, WSplitSplit *p)
+{
+    if(((WSplit*)a)->parent!=NULL)
+        splitinner_replace(((WSplit*)a)->parent, (WSplit*)a, (WSplit*)p);
+    else
+        splittree_changeroot((WSplit*)a, (WSplit*)p);
+}
+
+
+/* Yes, it is overparametrised */
+static void rotate_right(WSplitSplit *a, WSplitSplit *p, WSplit *y)
+{
+    assert(a->tl==(WSplit*)p && p->tl==y);
+    
+    /* Right rotation:
+     *        a             p
+     *      /  \          /   \
+     *     p    x   =>   y     a
+     *   /   \               /   \
+     *  y     ?             ?     x
+     */
+    
+    a->tl=p->br;
+    a->tl->parent=(WSplitInner*)a;
+    replace(a, p);
+    p->br=(WSplit*)a;
+    ((WSplit*)a)->parent=(WSplitInner*)p;
+}
+
+
+static void rot_rs_rotate_right(WSplitSplit *a, WSplitSplit *p, WSplit *y)
+{
+    WRectangle xg, yg, pg, ag, qg;
+    WSplit *x=a->br, *q=p->br;
+    
+    assert(a->dir==other_dir(p->dir));
+
+    qg=GEOM(q);
+    xg=GEOM(x);
+    yg=GEOM(y);
+    pg=GEOM(p);
+    ag=GEOM(a);
+    
+    if(a->dir==SPLIT_HORIZONTAL){
+        /* yyxx    yyyy
+         * ??xx => ??xx
+         * ??xx    ??xx
+         */
+        yg.w=ag.w;
+        pg.w=ag.w;
+        xg.h=qg.h;
+        ag.h=qg.h;
+        xg.y=qg.y;
+        ag.y=qg.y;
+    }else{
+        /* y??    y??
+         * y??    y??
+         * xxx => yxx
+         * xxx    yxx
+         */
+        yg.h=ag.h;
+        pg.h=ag.h;
+        xg.w=qg.w;
+        ag.w=qg.w;
+        xg.x=qg.x;
+        ag.x=qg.x;
+    }
+    
+    rotate_right(a, p, y);
+    
+    GEOM(p)=pg;
+    GEOM(a)=ag;
+    
+    split_do_resize(x, &xg, PRIMN_TL, PRIMN_TL, FALSE);
+    split_do_resize(y, &yg, PRIMN_BR, PRIMN_BR, FALSE);
+}
+
+
+
+static void rotate_left(WSplitSplit *a, WSplitSplit *p, WSplit *y)
+{
+    assert(a->br==(WSplit*)p && p->br==y);
+    
+    /* Left rotation:
+     *     a                  p
+     *   /   \              /   \
+     *  x     p     =>     a     y
+     *      /   \        /  \
+     *     ?     y      x    ?
+     */
+    
+    a->br=p->tl;
+    a->br->parent=(WSplitInner*)a;
+    replace(a, p);
+    p->tl=(WSplit*)a;
+    ((WSplit*)a)->parent=(WSplitInner*)p;
+}
+
+
+static void rot_rs_rotate_left(WSplitSplit *a, WSplitSplit *p, WSplit *y)
+{
+    WRectangle xg, yg, pg, ag, qg;
+    WSplit *x=a->tl, *q=p->tl;
+    
+    assert(a->dir==other_dir(p->dir));
+
+    qg=GEOM(q);
+    xg=GEOM(x);
+    yg=GEOM(y);
+    pg=GEOM(p);
+    ag=GEOM(a);
+    
+    if(a->dir==SPLIT_HORIZONTAL){
+        /* xx??    xx??
+         * xx?? => xx??
+         * xxyy    yyyy
+         */
+        yg.w=ag.w;
+        yg.x=ag.x;
+        pg.w=ag.w;
+        pg.x=ag.x;
+        xg.h=qg.h;
+        ag.h=qg.h;
+    }else{
+        /* xxx    xxy
+         * xxx    xxy
+         * ??y => ??y
+         * ??y    ??y
+         */
+        yg.h=ag.h;
+        yg.y=ag.y;
+        pg.h=ag.h;
+        pg.y=ag.y;
+        xg.w=qg.w;
+        ag.w=qg.w;
+    }
+
+    rotate_left(a, p, y);
+    
+    GEOM(p)=pg;
+    GEOM(a)=ag;
+    
+    split_do_resize(x, &xg, PRIMN_BR, PRIMN_BR, FALSE);
+    split_do_resize(y, &yg, PRIMN_TL, PRIMN_TL, FALSE);
+}
+
+
+
+static void flip_right(WSplitSplit *a, WSplitSplit *p)
+{
+    WSplit *tmp;
+    
+    assert(a->tl==(WSplit*)p);
+    
+    /* Right flip:
+     *        a               p 
+     *      /   \           /   \
+     *     p     x   =>    a     y
+     *   /  \            /  \
+     *  ?    y          ?    x
+     */
+
+    a->tl=p->tl;
+    a->tl->parent=(WSplitInner*)a;
+    replace(a, p);
+    p->tl=(WSplit*)a;
+    ((WSplit*)a)->parent=(WSplitInner*)p;
+}
+
+
+static void rot_rs_flip_right(WSplitSplit *a, WSplitSplit *p)
+{
+    WRectangle xg, yg, pg, ag, qg;
+    WSplit *x=a->br, *q=p->tl, *y=p->br;
+    
+    assert(a->dir==other_dir(p->dir));
+
+    qg=GEOM(q);
+    xg=GEOM(x);
+    yg=GEOM(y);
+    pg=GEOM(p);
+    ag=GEOM(a);
+    
+    if(a->dir==SPLIT_HORIZONTAL){
+        /* ??xx    ??xx
+         * ??xx => ??xx
+         * yyxx    yyyy
+         */
+        yg.w=ag.w;
+        pg.w=ag.w;
+        ag.h=qg.h;
+        xg.h=qg.h;
+    }else{
+        /* ??y    ??y
+         * ??y    ??y
+         * xxx => xxy
+         * xxx    xxy
+         */
+        yg.h=ag.h;
+        pg.h=ag.h;
+        ag.w=qg.w;
+        xg.w=qg.w;
+    }
+    
+    flip_right(a, p);
+    
+    GEOM(p)=pg;
+    GEOM(a)=ag;
+    
+    split_do_resize(x, &xg, PRIMN_BR, PRIMN_BR, FALSE);
+    split_do_resize(y, &yg, PRIMN_BR, PRIMN_BR, FALSE);
+}
+
+
+static void flip_left(WSplitSplit *a, WSplitSplit *p)
+{
+    WSplit *tmp;
+    
+    assert(a->br==(WSplit*)p);
+    
+    /* Left flip:
+     *     a               p 
+     *   /   \           /   \
+     *  x     p    =>   y     a
+     *      /  \            /  \
+     *     y    ?          x    ?
+     */
+    
+    a->br=p->br;
+    a->br->parent=(WSplitInner*)a;
+    replace(a, p);
+    p->br=(WSplit*)a;
+    ((WSplit*)a)->parent=(WSplitInner*)p;
+}
+
+
+static void rot_rs_flip_left(WSplitSplit *a, WSplitSplit *p)
+{
+    WRectangle xg, yg, pg, ag, qg;
+    WSplit *x=a->tl, *q=p->br, *y=p->tl;
+    
+    assert(a->dir==other_dir(p->dir));
+
+    qg=GEOM(q);
+    xg=GEOM(x);
+    yg=GEOM(y);
+    pg=GEOM(p);
+    ag=GEOM(a);
+    
+    if(a->dir==SPLIT_HORIZONTAL){
+        /* xxyy    yyyy
+         * xx?? => xx??
+         * xx??    xx??
+         */
+        yg.x=ag.x;
+        yg.w=ag.w;
+        pg.x=ag.x;
+        pg.w=ag.w;
+        xg.h=qg.h;
+        xg.y=qg.y;
+        ag.h=qg.h;
+        ag.y=qg.y;
+    }else{
+        /* xxx    yxx
+         * xxx    yxx
+         * y?? => y??
+         * y??    y??
+         */
+        yg.y=ag.y;
+        yg.h=ag.h;
+        pg.y=ag.y;
+        pg.h=ag.h;
+        xg.w=qg.w;
+        xg.x=qg.x;
+        ag.w=qg.w;
+        ag.x=qg.x;
+    }
+    
+    flip_left(a, p);
+    
+    GEOM(p)=pg;
+    GEOM(a)=ag;
+    
+    split_do_resize(x, &xg, PRIMN_TL, PRIMN_TL, FALSE);
+    split_do_resize(y, &yg, PRIMN_TL, PRIMN_TL, FALSE);
+}
+
+
+static void rot_para_right(WSplitSplit *a, WSplitSplit *p,
+                           WSplit *y)
+{
+    rotate_right(a, p, y);
+    if(a->dir==SPLIT_VERTICAL){
+        GEOM(p).y=GEOM(a).y;
+        GEOM(p).h=GEOM(a).h;
+        GEOM(a).y=GEOM(a->tl).y;
+        GEOM(a).h=GEOM(a->br).y+GEOM(a->br).h-GEOM(a).y;
+    }else{
+        GEOM(p).x=GEOM(a).x;
+        GEOM(p).w=GEOM(a).w;
+        GEOM(a).x=GEOM(a->tl).x;
+        GEOM(a).w=GEOM(a->br).x+GEOM(a->br).w-GEOM(a).x;
+    }
+}
+
+
+static void rot_para_left(WSplitSplit *a, WSplitSplit *p,
+                          WSplit *y)
+{
+    rotate_left(a, p, y);
+    if(a->dir==SPLIT_VERTICAL){
+        GEOM(p).y=GEOM(a).y;
+        GEOM(p).h=GEOM(a).h;
+        GEOM(a).h=GEOM(a->br).y+GEOM(a->br).h-GEOM(a).y;
+    }else{
+        GEOM(p).x=GEOM(a).x;
+        GEOM(p).w=GEOM(a).w;
+        GEOM(a).w=GEOM(a->br).x+GEOM(a->br).w-GEOM(a).x;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Sink */
+
+
+static bool do_try_sink_stdisp_orth(WSplitSplit *p, WSplitST *stdisp, 
+                                    WSplitSplit *other, bool force)
+{
+    bool doit=force;
+    
+    assert(p->dir==other_dir(other->dir));
+    assert(stdisp_dir_ok(p, stdisp));
+    
+    if(STDISP_GROWS_T_TO_B(stdisp) || STDISP_GROWS_L_TO_R(stdisp)){
+        if(STDISP_GROWS_L_TO_R(stdisp)){
+            assert(other->dir==SPLIT_HORIZONTAL);
+            if(other->tl->geom.w>=stdisp_recommended_w(stdisp))
+                doit=TRUE;
+        }else{ /* STDISP_GROWS_T_TO_B */
+            assert(other->dir==SPLIT_VERTICAL);
+            if(other->tl->geom.h>=stdisp_recommended_h(stdisp))
+                doit=TRUE;
+        }
+        
+        if(doit){
+            if(p->br==(WSplit*)stdisp)
+                rot_rs_flip_right(p, other);
+            else /* p->tl==stdisp */
+                rot_rs_rotate_left(p, other, other->br);
+        }
+    }else{ /* STDISP_GROWS_B_TO_T or STDISP_GROW_R_TO_L */
+        if(STDISP_GROWS_R_TO_L(stdisp)){
+            assert(other->dir==SPLIT_HORIZONTAL);
+            if(other->br->geom.w>=stdisp_recommended_w(stdisp))
+                doit=TRUE;
+        }else{ /* STDISP_GROWS_B_TO_T */
+            assert(other->dir==SPLIT_VERTICAL);
+            if(other->br->geom.h>=stdisp_recommended_h(stdisp))
+                doit=TRUE;
+        }
+        
+        if(doit){
+            if(p->tl==(WSplit*)stdisp)
+                rot_rs_flip_left(p, other);
+            else /* p->br==stdisp */
+                rot_rs_rotate_right(p, other, other->tl);
+        }
+    }
+    
+    return doit;
+}
+
+
+static bool do_try_sink_stdisp_para(WSplitSplit *p, WSplitST *stdisp, 
+                                    WSplitSplit *other, bool force)
+{
+    if(!force){
+        if(STDISP_IS_HORIZONTAL(stdisp)){
+            if(stdisp_recommended_w(stdisp)>=GEOM(p).w)
+                return FALSE;
+        }else{
+            if(stdisp_recommended_h(stdisp)>=GEOM(p).h)
+                return FALSE;
+        }
+    }
+    
+    if(p->tl==(WSplit*)stdisp)
+        rot_para_left(p, other, other->br);
+    else
+        rot_para_right(p, other, other->tl);
+    
+    return TRUE;
+}
+
+
+bool split_try_sink_stdisp(WSplitSplit *node, bool iterate, bool force)
+{
+    bool didsomething=FALSE;
+    bool more=TRUE;
+    
+    /*assert(OBJ_IS_EXACTLY(node, WSplitSplit));*/
+    
+    while(more){
+        WSplit *tl=node->tl;
+        WSplit *br=node->br;
+        WSplitSplit *other=NULL;
+        WSplitST *st;
+        
+        if(OBJ_IS(tl, WSplitST)){
+            st=(WSplitST*)tl;
+            other=OBJ_CAST(br, WSplitSplit);
+        }else if(OBJ_IS(br, WSplitST)){
+            st=(WSplitST*)br;
+            other=OBJ_CAST(tl, WSplitSplit);
+        }else{
+            break;
+        }
+        
+        if(other==NULL)
+            break;
+        
+        if(!stdisp_dir_ok(node, st))
+            break;
+        
+        if(other->dir==other_dir(node->dir)){
+            if(!do_try_sink_stdisp_orth(node, st, other, force))
+                break;
+        }else /*if(br->dir==node->dir)*/{
+            if(!do_try_sink_stdisp_para(node, st, other, force))
+                break;
+        }
+        didsomething=TRUE;
+        more=iterate;
+    }
+    
+    return didsomething;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Unsink */
+
+
+static bool do_try_unsink_stdisp_orth(WSplitSplit *a, WSplitSplit *p,
+                                      WSplitST *stdisp, bool force)
+{
+    bool doit=force;
+    
+    assert(p->dir==other_dir(a->dir));
+    assert(stdisp_dir_ok(p, stdisp));
+    
+    if(STDISP_GROWS_T_TO_B(stdisp) || STDISP_GROWS_L_TO_R(stdisp)){
+        if(STDISP_GROWS_L_TO_R(stdisp)){
+            assert(a->dir==SPLIT_HORIZONTAL);
+            if(GEOM(stdisp).w<stdisp_recommended_w(stdisp))
+                doit=TRUE;
+        }else{ /* STDISP_GROWS_T_TO_B */
+            assert(a->dir==SPLIT_VERTICAL);
+            if(GEOM(stdisp).h<stdisp_recommended_h(stdisp))
+                doit=TRUE;
+        }
+        
+        if(doit){
+            if((WSplit*)p==a->tl){
+                if((WSplit*)stdisp==p->br)
+                    rot_rs_flip_right(a, p);
+                else /*stdisp==p->tl*/
+                    rot_rs_rotate_right(a, p, (WSplit*)stdisp);
+            }else{ /*p==a->br*/
+#if 0
+                /* abnormal cases. */
+                warn(TR("Status display in bad split configuration."));
+                return FALSE;
+#else                
+                if((WSplit*)stdisp==p->br)
+                    rot_rs_rotate_left(a, p, (WSplit*)stdisp);
+                else /*stdisp==p->tl*/
+                    rot_rs_flip_left(a, p);
+#endif
+            }
+        }
+    }else{ /*STDISP_GROWS_B_TO_T || STDISP_GROWS_R_TO_L*/
+        if(STDISP_GROWS_R_TO_L(stdisp)){
+            assert(a->dir==SPLIT_HORIZONTAL);
+            if(GEOM(stdisp).w<stdisp_recommended_w(stdisp))
+                doit=TRUE;
+        }else{ /* STDISP_GROWS_B_TO_T */
+            assert(a->dir==SPLIT_VERTICAL);
+            if(GEOM(stdisp).h<stdisp_recommended_h(stdisp))
+                doit=TRUE;
+        }
+        
+        if(doit){
+            if((WSplit*)p==a->tl){
+#if 0
+                /* abnormal cases. */
+                warn(TR("Status display in bad split configuration."));
+                return FALSE;
+#else                
+                if((WSplit*)stdisp==p->br)
+                    rot_rs_flip_right(a, p);
+                else /*stdisp==p->tl*/
+                    rot_rs_rotate_right(a, p, (WSplit*)stdisp);
+#endif                
+            }else{ /*p==a->br*/
+                if((WSplit*)stdisp==p->br)
+                    rot_rs_rotate_left(a, p, (WSplit*)stdisp);
+                else /*stdisp==p->tl*/
+                    rot_rs_flip_left(a, p);
+            }
+        }
+    }
+    
+    return doit;
+}
+
+
+static bool do_try_unsink_stdisp_para(WSplitSplit *a, WSplitSplit *p,
+                                      WSplitST *stdisp,  bool force)
+{
+    if(!force){
+        if(STDISP_IS_HORIZONTAL(stdisp)){
+            if(stdisp_recommended_w(stdisp)<=GEOM(p).w)
+                return FALSE;
+        }else{
+            if(stdisp_recommended_h(stdisp)<=GEOM(p).h)
+                return FALSE;
+        }
+    }
+    
+    
+    if(a->tl==(WSplit*)p && p->tl==(WSplit*)stdisp){
+        rot_para_right(a, p, (WSplit*)stdisp);
+    }else if(a->br==(WSplit*)p && p->br==(WSplit*)stdisp){
+        rot_para_left(a, p, (WSplit*)stdisp);
+    }else{
+        warn(TR("Status display badly located in split tree."));
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+bool split_try_unsink_stdisp(WSplitSplit *node, bool iterate, bool force)
+{
+    bool didsomething=FALSE;
+    bool more=TRUE;
+    
+    /*assert(OBJ_IS_EXACTLY(node, WSplitSplit));*/
+
+    while(more){
+        WSplitSplit *p=OBJ_CAST(((WSplit*)node)->parent, WSplitSplit);
+        WSplit *tl=node->tl;
+        WSplit *br=node->br;
+        WSplitST *st;
+
+        if(p==NULL)
+            break;
+        
+        if(OBJ_IS(tl, WSplitST))
+            st=(WSplitST*)tl;
+        else if(OBJ_IS(br, WSplitST))
+            st=(WSplitST*)br;
+        else
+            break;
+        
+        if(!stdisp_dir_ok(node, st))
+            break;
+        
+        if(p->dir==other_dir(node->dir)){
+            if(!do_try_unsink_stdisp_orth(p, node, st, force))
+                break;
+        }else /*if(p->dir==node->dir)*/{
+            if(!do_try_unsink_stdisp_para(p, node, st, force))
+                break;
+        }
+        
+        didsomething=TRUE;
+        more=iterate;
+    }
+    
+    return didsomething;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Sink or unsink */
+
+
+bool split_regularise_stdisp(WSplitST *stdisp)
+{
+    WSplitSplit *node=OBJ_CAST(((WSplit*)stdisp)->parent, WSplitSplit);
+    
+    if(node==NULL)
+        return FALSE;
+    
+    if(STDISP_IS_HORIZONTAL(stdisp)){
+        if(GEOM(stdisp).w<stdisp_recommended_w(stdisp))
+            return split_try_unsink_stdisp(node, TRUE, FALSE);
+        else if(GEOM(stdisp).w>stdisp_recommended_w(stdisp))
+            return split_try_sink_stdisp(node, TRUE, FALSE);
+    }else{
+        if(GEOM(stdisp).h<stdisp_recommended_h(stdisp))
+            return split_try_unsink_stdisp(node, TRUE, FALSE);
+        else if(GEOM(stdisp).h>stdisp_recommended_h(stdisp))
+            return split_try_sink_stdisp(node, TRUE, FALSE);
+    }
+    
+    return FALSE;
+}
+
+
+/*}}}*/
+
diff --git a/mod_tiling/split-stdisp.h b/mod_tiling/split-stdisp.h
new file mode 100644 (file)
index 0000000..5d41fe1
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * ion/mod_tiling/split-stdisp.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_TILING_SPLIT_STDISP_H
+#define ION_MOD_TILING_SPLIT_STDISP_H
+
+#include "split.h"
+
+extern bool split_try_sink_stdisp(WSplitSplit *node, bool iterate, bool force);
+extern bool split_try_unsink_stdisp(WSplitSplit *node, bool iterate, bool force);
+extern bool split_regularise_stdisp(WSplitST *stdisp);
+
+extern int stdisp_recommended_w(WSplitST *stdisp);
+extern int stdisp_recommended_h(WSplitST *stdisp);
+
+#endif /* ION_MOD_TILING_SPLIT_STDISP_H */
diff --git a/mod_tiling/split.c b/mod_tiling/split.c
new file mode 100644 (file)
index 0000000..54864d7
--- /dev/null
@@ -0,0 +1,2004 @@
+/*
+ * ion/mod_tiling/split.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <limits.h>
+#include <string.h>
+#include <X11/Xmd.h>
+
+#include <libtu/minmax.h>
+#include <libtu/rb.h>
+#include <libtu/objp.h>
+#include <ioncore/common.h>
+#include <ioncore/focus.h>
+#include <ioncore/global.h>
+#include <ioncore/window.h>
+#include <ioncore/resize.h>
+#include <ioncore/attach.h>
+#include <ioncore/manage.h>
+#include <ioncore/extlconv.h>
+#include <ioncore/rectangle.h>
+#include <ioncore/saveload.h>
+#include <ioncore/names.h>
+#include "tiling.h"
+#include "split.h"
+#include "split-stdisp.h"
+
+
+static Rb_node split_of_map=NULL;
+
+
+/*{{{ Geometry helper functions */
+
+
+int split_size(WSplit *split, int dir)
+{
+    return (dir==SPLIT_HORIZONTAL ? split->geom.w : split->geom.h);
+}
+
+int split_other_size(WSplit *split, int dir)
+{
+    return (dir==SPLIT_VERTICAL ? split->geom.w : split->geom.h);
+}
+
+int split_pos(WSplit *split, int dir)
+{
+    return (dir==SPLIT_HORIZONTAL ? split->geom.x : split->geom.y);
+}
+
+int split_other_pos(WSplit *split, int dir)
+{
+    return (dir==SPLIT_VERTICAL ? split->geom.x : split->geom.y);
+}
+
+
+static int reg_calcresize(WRegion *reg, int dir, int nsize)
+{
+    int tmp;
+    
+    if(dir==SPLIT_HORIZONTAL)
+        tmp=region_min_w(reg);
+    else
+        tmp=region_min_h(reg);
+    
+    return (nsize<tmp ? tmp : nsize);
+}
+
+
+/* No, these are not even supposed to be proper/consistent 
+ * Z \cup {\infty, -\infty} calculation rules. 
+ */
+
+static int infadd(int x, int y)
+{
+    if(x==INT_MAX || y==INT_MAX)
+        return INT_MAX;
+    else
+        return x+y;
+}
+
+
+static int infsub(int x, int y)
+{
+    if(x==INT_MAX)
+        return INT_MAX;
+    else if(y==INT_MAX)
+        return 0;
+    else
+        return x-y;
+}
+
+
+/* Negative "unused space" means no SPLIT_UNUSED under a node, while
+ * zero unused space means there's a zero-sized SPLIT_UNUSED under the
+ * node.
+ */
+static int unusedadd(int x, int y)
+{
+    if(x<0 && y<0)
+        return -1;
+    return maxof(x, 0)+maxof(y, 0);
+}
+
+
+static void bound(int *what, int min, int max)
+{
+    if(*what<min)
+        *what=min;
+    else if(*what>max)
+        *what=max;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Functions to get and set a region's containing node */
+
+
+#define node_of_reg splittree_node_of
+
+WSplitRegion *splittree_node_of(WRegion *reg)
+{
+    Rb_node node=NULL;
+    int found=0;
+    
+    /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
+    
+    if(split_of_map!=NULL){
+        node=rb_find_pkey_n(split_of_map, reg, &found);
+        if(found)
+            return (WSplitRegion*)(node->v.val);
+    }
+    
+    return NULL;
+}
+
+
+#define set_node_of_reg splittree_set_node_of
+
+
+bool splittree_set_node_of(WRegion *reg, WSplitRegion *split)
+{
+    Rb_node node=NULL;
+    int found;
+    
+    /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/
+    
+    if(split_of_map==NULL){
+        if(split==NULL)
+            return TRUE;
+        split_of_map=make_rb();
+        if(split_of_map==NULL)
+            return FALSE;
+    }
+    
+    node=rb_find_pkey_n(split_of_map, reg, &found);
+    if(found)
+        rb_delete_node(node);
+    
+    return (rb_insertp(split_of_map, reg, split)!=NULL);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Primn */
+
+
+WPrimn primn_invert(WPrimn primn)
+{
+    return (primn==PRIMN_TL
+            ? PRIMN_BR
+            : (primn==PRIMN_BR
+               ? PRIMN_TL
+               : primn));
+}
+
+
+WPrimn primn_none2any(WPrimn primn)
+{
+    return (primn==PRIMN_NONE ? PRIMN_ANY : primn);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Create */
+
+
+bool split_init(WSplit *split, const WRectangle *geom)
+{
+    split->parent=NULL;
+    split->ws_if_root=NULL;
+    split->geom=*geom;
+    split->min_w=0;
+    split->min_h=0;
+    split->max_w=INT_MAX;
+    split->max_h=INT_MAX;
+    split->unused_w=-1;
+    split->unused_h=-1;
+    return TRUE;
+}
+
+bool splitinner_init(WSplitInner *split, const WRectangle *geom)
+{
+    return split_init(&(split->split), geom);
+}
+
+
+bool splitsplit_init(WSplitSplit *split, const WRectangle *geom, int dir)
+{
+    splitinner_init(&(split->isplit), geom);
+    split->dir=dir;
+    split->tl=NULL;
+    split->br=NULL;
+    split->current=SPLIT_CURRENT_TL;
+    return TRUE;
+}
+
+
+bool splitregion_init(WSplitRegion *split, const WRectangle *geom, 
+                      WRegion *reg)
+{
+    split_init(&(split->split), geom);
+    split->reg=reg;
+    if(reg!=NULL)
+        set_node_of_reg(reg, split);
+    return TRUE;
+}
+
+
+bool splitst_init(WSplitST *split, const WRectangle *geom, WRegion *reg)
+{
+    splitregion_init(&(split->regnode), geom, reg);
+    split->orientation=REGION_ORIENTATION_HORIZONTAL;
+    split->corner=MPLEX_STDISP_BL;
+    return TRUE;
+}
+
+
+WSplitSplit *create_splitsplit(const WRectangle *geom, int dir)
+{
+    CREATEOBJ_IMPL(WSplitSplit, splitsplit, (p, geom, dir));
+}
+
+
+WSplitRegion *create_splitregion(const WRectangle *geom, WRegion *reg)
+{
+    CREATEOBJ_IMPL(WSplitRegion, splitregion, (p, geom, reg));
+}
+
+
+WSplitST *create_splitst(const WRectangle *geom, WRegion *reg)
+{
+    CREATEOBJ_IMPL(WSplitST, splitst, (p, geom, reg));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Deinit */
+
+
+void split_deinit(WSplit *split)
+{
+    assert(split->parent==NULL);
+}
+
+
+void splitinner_deinit(WSplitInner *split)
+{
+    split_deinit(&(split->split));
+}
+
+
+void splitsplit_deinit(WSplitSplit *split)
+{
+    if(split->tl!=NULL){
+        split->tl->parent=NULL;
+        destroy_obj((Obj*)(split->tl));
+    }
+    if(split->br!=NULL){
+        split->br->parent=NULL;
+        destroy_obj((Obj*)(split->br));
+    }
+    
+    splitinner_deinit(&(split->isplit));
+}
+    
+
+void splitregion_deinit(WSplitRegion *split)
+{
+    if(split->reg!=NULL){
+        set_node_of_reg(split->reg, NULL);
+        split->reg=NULL;
+    }
+    
+    split_deinit(&(split->split));
+}
+
+
+void splitst_deinit(WSplitST *split)
+{
+    splitregion_deinit(&(split->regnode));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Size bounds management */
+
+
+static void splitregion_update_bounds(WSplitRegion *node, bool recursive)
+{
+    WSizeHints hints;
+    WSplit *snode=(WSplit*)node;
+    
+    assert(node->reg!=NULL);
+    
+    region_size_hints(node->reg, &hints);
+    
+    snode->min_w=maxof(1, hints.min_set ? hints.min_width : 1);
+    snode->max_w=INT_MAX;
+    snode->unused_w=-1;
+
+    snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1);
+    snode->max_h=INT_MAX;
+    snode->unused_h=-1;
+}
+
+
+static void splitst_update_bounds(WSplitST *node, bool rec)
+{
+    WSplit *snode=(WSplit*)node;
+
+    if(node->regnode.reg==NULL){
+        snode->min_w=CF_STDISP_MIN_SZ;
+        snode->min_h=CF_STDISP_MIN_SZ;
+        snode->max_w=CF_STDISP_MIN_SZ;
+        snode->max_h=CF_STDISP_MIN_SZ;
+    }else{
+        WSizeHints hints;
+        region_size_hints(node->regnode.reg, &hints);
+        snode->min_w=maxof(1, hints.min_set ? hints.min_width : 1);
+        snode->max_w=maxof(snode->min_w, hints.min_width);
+        snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1);
+        snode->max_h=maxof(snode->min_h, hints.min_height);
+    }
+
+    snode->unused_w=-1;
+    snode->unused_h=-1;
+    
+    if(node->orientation==REGION_ORIENTATION_HORIZONTAL){
+        snode->min_w=CF_STDISP_MIN_SZ;
+        snode->max_w=INT_MAX;
+    }else{
+        snode->min_h=CF_STDISP_MIN_SZ;
+        snode->max_h=INT_MAX;
+    }
+}
+
+
+static void splitsplit_update_bounds(WSplitSplit *split, bool recursive)
+{
+    WSplit *tl, *br;
+    WSplit *node=(WSplit*)split;
+
+    assert(split->tl!=NULL && split->br!=NULL);
+    
+    tl=split->tl;
+    br=split->br;
+    
+    if(recursive){
+        split_update_bounds(tl, TRUE);
+        split_update_bounds(br, TRUE);
+    }
+    
+    if(split->dir==SPLIT_HORIZONTAL){
+        node->max_w=infadd(tl->max_w, br->max_w);
+        node->min_w=infadd(tl->min_w, br->min_w);
+        node->unused_w=unusedadd(tl->unused_w, br->unused_w);
+        node->min_h=maxof(tl->min_h, br->min_h);
+        node->max_h=maxof(minof(tl->max_h, br->max_h), node->min_h);
+        node->unused_h=minof(tl->unused_h, br->unused_h);
+    }else{
+        node->max_h=infadd(tl->max_h, br->max_h);
+        node->min_h=infadd(tl->min_h, br->min_h);
+        node->unused_h=unusedadd(tl->unused_h, br->unused_h);
+        node->min_w=maxof(tl->min_w, br->min_w);
+        node->max_w=maxof(minof(tl->max_w, br->max_w), node->min_w);
+        node->unused_w=minof(tl->unused_w, br->unused_w);
+    }
+}
+
+
+void split_update_bounds(WSplit *node, bool recursive)
+{
+    CALL_DYN(split_update_bounds, node, (node, recursive));
+}
+
+
+void splitsplit_update_geom_from_children(WSplitSplit *node)
+{
+    WSplit *split=(WSplit*)node;
+    
+    if(node->dir==SPLIT_VERTICAL){
+        ((WSplit*)node)->geom.h=node->tl->geom.h+node->br->geom.h;
+        ((WSplit*)node)->geom.y=node->tl->geom.y;
+    }else if(node->dir==SPLIT_HORIZONTAL){
+        ((WSplit*)node)->geom.w=node->tl->geom.w+node->br->geom.w;
+        ((WSplit*)node)->geom.x=node->tl->geom.x;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Status display handling helper functions. */
+
+
+static WSplitST *saw_stdisp=NULL;
+
+
+void splittree_begin_resize()
+{
+    saw_stdisp=NULL;
+}
+
+
+void splittree_end_resize()
+{
+    if(saw_stdisp!=NULL){
+        split_regularise_stdisp(saw_stdisp);
+        saw_stdisp=NULL;
+    }
+}
+
+
+static void splittree_scan_stdisp_rootward_(WSplitInner *node_)
+{
+    WSplitSplit *node=OBJ_CAST(node_, WSplitSplit);
+    
+    if(node!=NULL){
+        if(OBJ_IS(node->tl, WSplitST)){
+            saw_stdisp=(WSplitST*)(node->tl);
+            return;
+        }else if(OBJ_IS(node->br, WSplitST)){
+            saw_stdisp=(WSplitST*)(node->br);
+            return;
+        }
+    }
+    
+    if(node_->split.parent!=NULL)
+        splittree_scan_stdisp_rootward_(node_->split.parent);
+}
+
+
+void splittree_scan_stdisp_rootward(WSplit *node)
+{
+    if(node->parent!=NULL)
+        splittree_scan_stdisp_rootward_(node->parent);
+}
+
+
+static WSplitSplit *splittree_scan_stdisp_parent(WSplit *node_, bool set_saw)
+{
+    WSplitSplit *r, *node=OBJ_CAST(node_, WSplitSplit);
+    
+    if(node==NULL)
+        return NULL;
+    
+    if(OBJ_IS(node->tl, WSplitST)){
+        if(set_saw)
+            saw_stdisp=(WSplitST*)node->tl;
+        return node;
+    }
+
+    if(OBJ_IS(node->br, WSplitST)){
+        if(set_saw)
+            saw_stdisp=(WSplitST*)node->br;
+        return node;
+    }
+
+    r=splittree_scan_stdisp_parent(node->tl, set_saw);
+    if(r==NULL)
+        r=splittree_scan_stdisp_parent(node->br, set_saw);
+    return r;
+}
+
+
+static bool stdisp_immediate_child(WSplitSplit *node)
+{
+    return (node!=NULL && (OBJ_IS(node->tl, WSplitST) ||
+                           OBJ_IS(node->br, WSplitST)));
+}
+
+
+static WSplit *move_stdisp_out_of_way(WSplit *node)
+{
+    WSplitSplit *stdispp;
+    
+    if(!OBJ_IS(node, WSplitSplit))
+        return node;
+    
+    stdispp=splittree_scan_stdisp_parent(node, TRUE);
+        
+    if(stdispp==NULL)
+        return node;
+        
+    while(stdispp->tl!=node && stdispp->br!=node){
+        if(!split_try_unsink_stdisp(stdispp, FALSE, TRUE)){
+            warn(TR("Unable to move the status display out of way."));
+            return NULL;
+        }
+    }
+    
+    return (WSplit*)stdispp;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Low-level resize code; from root to leaf */
+
+
+static void split_do_resize_default(WSplit *node, const WRectangle *ng, 
+                                    WPrimn hprimn, WPrimn vprimn, 
+                                    bool transpose)
+{
+    node->geom=*ng;
+}
+
+
+static void splitregion_do_resize(WSplitRegion *node, const WRectangle *ng, 
+                                  WPrimn hprimn, WPrimn vprimn, 
+                                  bool transpose)
+{
+    assert(node->reg!=NULL);
+    region_fit(node->reg, ng, REGION_FIT_EXACT);
+    split_update_bounds(&(node->split), FALSE);
+    node->split.geom=*ng;
+}
+
+
+static void splitst_do_resize(WSplitST *node, const WRectangle *ng, 
+                              WPrimn hprimn, WPrimn vprimn, 
+                              bool transpose)
+{
+    saw_stdisp=node;
+
+    if(node->regnode.reg==NULL){
+        ((WSplit*)node)->geom=*ng;
+    }else{
+        splitregion_do_resize(&(node->regnode), ng, hprimn, vprimn, 
+                               transpose);
+    }
+}
+
+
+static int other_dir(int dir)
+{
+    return (dir==SPLIT_VERTICAL ? SPLIT_HORIZONTAL : SPLIT_VERTICAL);
+}
+
+
+static void adjust_sizes(int *tls_, int *brs_, int nsize, int sz, 
+                         int tlmin, int brmin, int tlmax, int brmax,
+                         int primn)
+{
+    int tls=*tls_;
+    int brs=*brs_;
+    
+    if(primn==PRIMN_TL){
+        tls=tls+nsize-sz;
+        bound(&tls, tlmin, tlmax);
+        brs=nsize-tls;
+        bound(&brs, brmin, brmax);
+        tls=nsize-brs;
+        bound(&tls, tlmin, tlmax);
+    }else if(primn==PRIMN_BR){
+        brs=brs+nsize-sz;
+        bound(&brs, brmin, brmax);
+        tls=nsize-brs;
+        bound(&tls, tlmin, tlmax);
+        brs=nsize-tls;
+        bound(&brs, brmin, brmax);
+    }else{ /* && PRIMN_ANY */
+        tls=tls*nsize/sz;
+        bound(&tls, tlmin, tlmax);
+        brs=nsize-tls;
+        bound(&brs, brmin, brmax);
+        tls=nsize-brs;
+        bound(&tls, tlmin, tlmax);
+    }
+    
+    *tls_=tls;
+    *brs_=brs;
+}
+
+
+static void get_minmaxunused(WSplit *node, int dir, 
+                             int *min, int *max, int *unused)
+{
+    if(dir==SPLIT_VERTICAL){
+        *min=node->min_h;
+        *max=maxof(*min, node->max_h);
+        *unused=minof(node->unused_h, node->geom.h);
+    }else{
+        *min=node->min_w;
+        *max=maxof(*min, node->max_w);
+        *unused=minof(node->unused_w, node->geom.w);
+    }
+}
+
+
+void splitsplit_do_resize(WSplitSplit *node, const WRectangle *ng, 
+                          WPrimn hprimn, WPrimn vprimn, bool transpose)
+{
+    assert(ng->w>=0 && ng->h>=0);
+    assert(node->tl!=NULL && node->br!=NULL);
+    assert(!transpose || (hprimn==PRIMN_ANY && vprimn==PRIMN_ANY));
+    
+    {
+        WSplit *tl=node->tl, *br=node->br;
+        int tls=split_size((WSplit*)tl, node->dir);
+        int brs=split_size((WSplit*)br, node->dir);
+        int sz=tls+brs;
+        /* Status display can not be transposed. */
+        int dir=((transpose && !stdisp_immediate_child(node))
+                 ? other_dir(node->dir)
+                 : node->dir);
+        int nsize=(dir==SPLIT_VERTICAL ? ng->h : ng->w);
+        int primn=(dir==SPLIT_VERTICAL ? vprimn : hprimn);
+        int tlmin, tlmax, tlunused, tlused;
+        int brmin, brmax, brunused, brused;
+        WRectangle tlg=*ng, brg=*ng;
+        
+        get_minmaxunused(tl, dir, &tlmin, &tlmax, &tlunused);
+        get_minmaxunused(br, dir, &brmin, &brmax, &brunused);
+        
+        tlused=maxof(0, tls-maxof(0, tlunused));
+        brused=maxof(0, brs-maxof(0, brunused));
+        /* tlmin,  brmin >= 1 => (tls>=tlmin, brs>=brmin => sz>0) */
+        
+        if(sz>2){
+            if(primn==PRIMN_ANY && (tlunused>=0 || brunused>=0)){
+                if(nsize<=tlused+brused){
+                    /* Need to shrink a tangible node */
+                    adjust_sizes(&tls, &brs, nsize, sz,
+                                 tlmin, brmin, tlused, brused, primn);
+                }else{
+                    /* Just expand or shrink unused space */
+                    adjust_sizes(&tls, &brs, nsize, sz,
+                                 tlused, brused, 
+                                 (tlunused<0 ? tlused : tlmax),
+                                 (brunused<0 ? brused : brmax), primn);
+                }
+                
+            }else{
+                adjust_sizes(&tls, &brs, nsize, sz, 
+                             tlmin, brmin, tlmax, brmax, primn);
+            }
+        }
+        
+        if(tls+brs!=nsize){
+            /* Bad fit; just size proportionally. */
+            if(sz<=2){
+                tls=nsize/2;
+                brs=nsize-tls;
+            }else{
+                tls=split_size(tl, node->dir)*nsize/sz;
+                brs=nsize-tls;
+            }
+        }
+        
+        if(dir==SPLIT_VERTICAL){
+            tlg.h=tls;
+            brg.y+=tls;
+            brg.h=brs;
+        }else{
+            tlg.w=tls;
+            brg.x+=tls;
+            brg.w=brs;
+        }
+        
+        split_do_resize(tl, &tlg, hprimn, vprimn, transpose);
+        split_do_resize(br, &brg, hprimn, vprimn, transpose);
+        
+        node->dir=dir;
+        ((WSplit*)node)->geom=*ng;
+        split_update_bounds((WSplit*)node, FALSE);
+    }
+}
+
+
+void split_do_resize(WSplit *node, const WRectangle *ng, 
+                     WPrimn hprimn, WPrimn vprimn, bool transpose)
+{
+    CALL_DYN(split_do_resize, node, (node, ng, hprimn, vprimn, transpose));
+}
+
+
+void split_resize(WSplit *node, const WRectangle *ng, 
+                  WPrimn hprimn, WPrimn vprimn)
+{
+    split_update_bounds(node, TRUE);
+    splittree_begin_resize();
+    split_do_resize(node, ng, hprimn, vprimn, FALSE);
+    splittree_end_resize();
+}
+
+
+/*}}}*/
+
+
+/*{{{ Low-level resize code; request towards root */
+
+
+static void flexibility(WSplit *node, int dir, int *shrink, int *stretch)
+{
+    if(dir==SPLIT_VERTICAL){
+        *shrink=maxof(0, node->geom.h-node->min_h);
+        if(OBJ_IS(node, WSplitST))
+            *stretch=maxof(0, node->max_h-node->geom.h);
+        else
+            *stretch=INT_MAX;
+    }else{
+        *shrink=maxof(0, node->geom.w-node->min_w);
+        if(OBJ_IS(node, WSplitST))
+            *stretch=maxof(0, node->max_w-node->geom.w);
+        else
+            *stretch=INT_MAX;
+    }
+}
+
+
+static void calc_amount(int *amount, int rs, WSplit *other, int dir)
+{
+    int shrink, stretch;
+    
+    flexibility(other, dir, &shrink, &stretch);
+
+    if(rs>0)
+        *amount=minof(rs, shrink);
+    else if(rs<0)
+        *amount=-minof(-rs, stretch);
+    else
+        *amount=0;
+}
+
+
+
+static void splitsplit_do_rqsize(WSplitSplit *p, WSplit *node, 
+                                 RootwardAmount *ha, RootwardAmount *va, 
+                                 WRectangle *rg, bool tryonly)
+{
+    WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
+    WRectangle og, pg, ng;
+    RootwardAmount *ca;
+    WSplit *other;
+    WPrimn thisnode;
+    int amount;
+    
+    assert(!ha->any || ha->tl==0);
+    assert(!va->any || va->tl==0);
+    assert(p->tl==node || p->br==node);
+    
+    if(p->tl==node){
+        other=p->br;
+        thisnode=PRIMN_TL;
+    }else{
+        other=p->tl;
+        thisnode=PRIMN_BR;
+    }
+
+    ca=(p->dir==SPLIT_VERTICAL ? va : ha);
+
+    if(thisnode==PRIMN_TL || ca->any){
+        calc_amount(&amount, ca->br, other, p->dir);
+        ca->br-=amount;
+    }else/*if(thisnode==PRIMN_BR)*/{
+        calc_amount(&amount, ca->tl, other, p->dir);
+        ca->tl-=amount;
+    }
+    
+    if(((WSplit*)p)->parent==NULL /*|| 
+       (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
+        if(((WSplit*)p)->ws_if_root!=NULL)
+            pg=REGION_GEOM((WTiling*)(((WSplit*)p)->ws_if_root));
+        else
+            pg=((WSplit*)p)->geom;
+    }else{
+        splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va,
+                             &pg, tryonly);
+    }
+    
+    assert(pg.w>=0 && pg.h>=0);
+
+    og=pg;
+    ng=pg;
+
+    if(p->dir==SPLIT_VERTICAL){
+        ng.h=maxof(0, node->geom.h+amount);
+        og.h=maxof(0, other->geom.h-amount);
+        adjust_sizes(&(ng.h), &(og.h), pg.h, ng.h+og.h,
+                     node->min_h, other->min_h, node->max_h, other->max_h, 
+                     PRIMN_TL /* node is passed as tl param */);
+        if(thisnode==PRIMN_TL)
+            og.y=pg.y+pg.h-og.h;
+        else
+            ng.y=pg.y+pg.h-ng.h;
+        vprimn=thisnode;
+    }else{
+        ng.w=maxof(0, node->geom.w+amount);
+        og.w=maxof(0, other->geom.w-amount);
+        adjust_sizes(&(ng.w), &(og.w), pg.w, ng.w+og.w,
+                     node->min_w, other->min_w, node->max_w, other->max_w, 
+                     PRIMN_TL /* node is passed as tl param */);
+        if(thisnode==PRIMN_TL)
+            og.x=pg.x+pg.w-og.w;
+        else
+            ng.x=pg.x+pg.w-ng.w;
+        hprimn=thisnode;
+    }
+    
+    if(!tryonly){
+        /* Entä jos 'other' on stdisp? */
+        split_do_resize(other, &og, hprimn, vprimn, FALSE);
+        
+        ((WSplit*)p)->geom=pg;
+    }
+    
+    *rg=ng;
+}
+
+
+void splitinner_do_rqsize(WSplitInner *p, WSplit *node, 
+                          RootwardAmount *ha, RootwardAmount *va, 
+                          WRectangle *rg, bool tryonly)
+{
+    CALL_DYN(splitinner_do_rqsize, p, (p, node, ha, va, rg, tryonly));
+}
+
+
+static void initra(RootwardAmount *ra, int p, int s, int op, int os, 
+                   bool any)
+{
+    ra->any=any;
+    ra->tl=op-p;
+    ra->br=(p+s)-(op+os);
+    if(any){
+        ra->br+=ra->tl;
+        ra->tl=0;
+    }
+}
+
+
+void split_do_rqgeom_(WSplit *node, const WRectangle *ng, 
+                      bool hany, bool vany, WRectangle *rg, 
+                      bool tryonly)
+{
+    RootwardAmount ha, va;
+
+    if(node->parent==NULL){
+        if(node->ws_if_root!=NULL)
+            *rg=REGION_GEOM((WTiling*)(node->ws_if_root));
+        else
+            *rg=*ng;
+    }else{
+        initra(&ha, ng->x, ng->w, node->geom.x, node->geom.w, hany);
+        initra(&va, ng->y, ng->h, node->geom.y, node->geom.h, vany);
+    
+        splitinner_do_rqsize(node->parent, node, &ha, &va, rg, tryonly);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Resize interface */
+
+
+static void bnd(int *pos, int *sz, int opos, int osz, int minsz, int maxsz)
+{
+    int ud=abs(*pos-opos);
+    int dd=abs((*pos+*sz)-(opos+osz));
+    int szrq=*sz;
+    
+    if(ud+dd!=0){
+        bound(sz, minsz, maxsz);
+        *pos+=(szrq-*sz)*ud/(ud+dd);
+    }
+}
+
+
+WSplit *split_find_root(WSplit *split)
+{
+    if(split->parent==NULL)
+        return split;
+    return split_find_root((WSplit*)split->parent);
+}
+
+
+void splittree_rqgeom(WSplit *sub, int flags, const WRectangle *geom_, 
+                      WRectangle *geomret)
+{
+    bool hany=flags&REGION_RQGEOM_WEAK_X;
+    bool vany=flags&REGION_RQGEOM_WEAK_Y;
+    bool tryonly=flags&REGION_RQGEOM_TRYONLY;
+    WRectangle geom=*geom_;
+    WRectangle retg;
+    WSplit *root=split_find_root(sub);
+    
+    if(geomret==NULL)
+        geomret=&retg;
+
+    split_update_bounds(root, TRUE);
+    
+    if(OBJ_IS(sub, WSplitST)){
+        WSplitST *sub_as_stdisp=(WSplitST*)sub;
+        
+        if(flags&REGION_RQGEOM_TRYONLY){
+            warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display."));
+            *geomret=sub->geom;
+            return;
+        }
+        split_regularise_stdisp(sub_as_stdisp);
+        geom=sub->geom;
+        if(sub_as_stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){
+            if(geom_->h==geom.h)
+                return;
+            geom.h=geom_->h;
+        }else{
+            if(geom_->w==geom.w)
+                return;
+            geom.w=geom_->w;
+        }
+        split_update_bounds(root, TRUE);
+    }
+
+    /* Handle internal size bounds */
+    bnd(&(geom.x), &(geom.w), sub->geom.x, sub->geom.w, 
+        sub->min_w, sub->max_w);
+    bnd(&(geom.y), &(geom.h), sub->geom.y, sub->geom.h, 
+        sub->min_h, sub->max_h);
+
+    /* Check if we should resize to both tl and br */
+    
+    if(hany){
+        geom.w+=sub->geom.x-geom.x;
+        geom.x=sub->geom.x;
+    }
+
+    if(vany){
+        geom.h+=sub->geom.y-geom.y;
+        geom.y=sub->geom.y;
+    }
+    
+    splittree_begin_resize();
+    
+    split_do_rqgeom_(sub, &geom, hany, vany, geomret, tryonly);
+    
+    if(!tryonly){
+        split_do_resize(sub, geomret, hany, vany, FALSE);
+        splittree_end_resize();
+        *geomret=sub->geom;
+    }else{
+        saw_stdisp=NULL;
+    }
+}
+
+
+/*EXTL_DOC
+ * Attempt to resize and/or move the split tree starting at \var{node}.
+ * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom} 
+ * operating on \var{node} (if it were a \type{WRegion}).
+ */
+EXTL_EXPORT_MEMBER
+ExtlTab split_rqgeom(WSplit *node, ExtlTab g)
+{
+    WRectangle geom, ogeom;
+    int flags=REGION_RQGEOM_WEAK_ALL;
+        
+    geom=node->geom;
+    ogeom=geom;
+
+    if(extl_table_gets_i(g, "x", &(geom.x)))
+        flags&=~REGION_RQGEOM_WEAK_X;
+    if(extl_table_gets_i(g, "y", &(geom.y)))
+        flags&=~REGION_RQGEOM_WEAK_Y;
+    if(extl_table_gets_i(g, "w", &(geom.w)))
+        flags&=~REGION_RQGEOM_WEAK_W;
+    if(extl_table_gets_i(g, "h", &(geom.h)))
+        flags&=~REGION_RQGEOM_WEAK_H;
+    
+    geom.w=maxof(1, geom.w);
+    geom.h=maxof(1, geom.h);
+
+    splittree_rqgeom(node, flags, &geom, &ogeom);
+    
+    return extl_table_from_rectangle(&ogeom);
+    
+err:
+    warn(TR("Invalid node."));
+    return extl_table_none();
+}
+
+
+/*}}}*/
+
+
+/*{{{ Split */
+
+
+void splittree_changeroot(WSplit *root, WSplit *node)
+{
+    WTiling *ws=(WTiling*)(root->ws_if_root);
+    assert(ws!=NULL);
+    assert(ws->split_tree==root);
+    root->ws_if_root=NULL;
+    ws->split_tree=node;
+    if(node!=NULL){
+        node->ws_if_root=ws;
+        node->parent=NULL;
+    }
+}
+
+
+static void splitsplit_replace(WSplitSplit *split, WSplit *child,
+                               WSplit *what)
+{
+    assert(split->tl==child || split->br==child);
+    
+    if(split->tl==child)
+        split->tl=what;
+    else
+        split->br=what;
+
+    child->parent=NULL;
+    
+    what->parent=(WSplitInner*)split;
+    what->ws_if_root=NULL; /* May not be needed. */
+}
+
+
+void splitinner_replace(WSplitInner *split, WSplit *child, WSplit *what)
+{
+    CALL_DYN(splitinner_replace, split, (split, child, what));
+}
+
+
+WSplitRegion *splittree_split(WSplit *node, int dir, WPrimn primn,
+                              int minsize, WRegionSimpleCreateFn *fn, 
+                              WWindow *parent)
+{
+    int objmin, objmax;
+    int s, sn, so, pos;
+    WSplitSplit *nsplit;
+    WSplitRegion *nnode;
+    WSplitInner *psplit;
+    WRegion *nreg;
+    WFitParams fp;
+    WRectangle ng, rg;
+    
+    assert(node!=NULL && parent!=NULL);
+    
+    if(OBJ_IS(node, WSplitST)){
+        warn(TR("Splitting the status display is not allowed."));
+        return NULL;
+    }
+
+    splittree_begin_resize();
+    
+    if(!move_stdisp_out_of_way(node))
+        return NULL;
+
+    if(primn!=PRIMN_TL && primn!=PRIMN_BR)
+        primn=PRIMN_BR;
+    if(dir!=SPLIT_HORIZONTAL && dir!=SPLIT_VERTICAL)
+        dir=SPLIT_VERTICAL;
+
+    split_update_bounds(split_find_root(node), TRUE);
+    objmin=(dir==SPLIT_VERTICAL ? node->min_h : node->min_w);
+
+    s=split_size(node, dir);
+    sn=maxof(minsize, s/2);
+    so=maxof(objmin, s-sn);
+
+    if(sn+so!=s){
+        int rs;
+        ng=node->geom;
+        if(dir==SPLIT_VERTICAL)
+            ng.h=sn+so;
+        else
+            ng.w=sn+so;
+        split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, TRUE);
+        rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
+        if(rs<minsize+objmin){
+            warn(TR("Unable to split: not enough free space."));
+            return NULL;
+        }
+        split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, FALSE);
+        rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
+        if(minsize>rs/2){
+            sn=minsize;
+            so=rs-sn;
+        }else{
+            so=maxof(rs/2, objmin);
+            sn=rs-so;
+        }
+    }else{
+        rg=node->geom;
+        splittree_scan_stdisp_rootward(node);
+    }
+
+    /* Create split and new window
+     */
+    fp.mode=REGION_FIT_EXACT;
+    fp.g=rg;
+    
+    nsplit=create_splitsplit(&(fp.g), dir);
+    
+    if(nsplit==NULL)
+        return NULL;
+
+    if(dir==SPLIT_VERTICAL){
+        if(primn==PRIMN_BR)
+            fp.g.y+=so;
+        fp.g.h=sn;
+    }else{
+        if(primn==PRIMN_BR)
+            fp.g.x+=so;
+        fp.g.w=sn;
+    }
+    
+    nreg=fn(parent, &fp);
+    
+    if(nreg==NULL){
+        destroy_obj((Obj*)nsplit);
+        return NULL;
+    }
+
+    nnode=create_splitregion(&(fp.g), nreg);
+    if(nnode==NULL){
+        destroy_obj((Obj*)nreg);
+        destroy_obj((Obj*)nsplit);
+        return NULL;
+    }
+    
+    /* Now that everything's ok, resize and move original node.
+     */    
+    ng=rg;
+    if(dir==SPLIT_VERTICAL){
+        ng.h=so;
+        if(primn==PRIMN_TL)
+            ng.y+=sn;
+    }else{
+        ng.w=so;
+        if(primn==PRIMN_TL)
+            ng.x+=sn;
+    }
+
+    split_do_resize(node, &ng, 
+                    (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY),
+                    (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY),
+                    FALSE);
+
+    /* Set up split structure
+     */
+    psplit=node->parent;
+    
+    if(psplit!=NULL)
+        splitinner_replace(psplit, node, (WSplit*)nsplit);
+    else
+        splittree_changeroot(node, (WSplit*)nsplit);
+        
+    node->parent=(WSplitInner*)nsplit;
+    ((WSplit*)nnode)->parent=(WSplitInner*)nsplit;
+    
+    if(primn==PRIMN_BR){
+        nsplit->tl=node;
+        nsplit->br=(WSplit*)nnode;
+        nsplit->current=SPLIT_CURRENT_TL;
+    }else{
+        nsplit->tl=(WSplit*)nnode;
+        nsplit->br=node;
+        nsplit->current=SPLIT_CURRENT_BR;
+    }
+    
+    splittree_end_resize();
+    
+    return nnode;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Remove */
+
+
+static void splitsplit_remove(WSplitSplit *node, WSplit *child, 
+                              bool reclaim_space)
+{
+    static int nstdisp=0;
+    WSplitInner *parent;
+    WSplit *other;
+    
+    assert(node->tl==child || node->br==child);
+    
+    if(node->tl==child)
+        other=node->br;
+    else
+        other=node->tl;
+    
+    assert(other!=NULL);
+
+    if(nstdisp==0 && reclaim_space && OBJ_IS(other, WSplitST)){
+        /* Try to move stdisp out of the way. */
+        split_try_unsink_stdisp(node, FALSE, TRUE);
+        assert(child->parent!=NULL);
+        nstdisp++;
+        splitinner_remove(child->parent, child, reclaim_space);
+        nstdisp--;
+        return;
+    }
+
+    parent=((WSplit*)node)->parent;
+    
+    if(parent!=NULL)
+        splitinner_replace(parent, (WSplit*)node, other);
+    else
+        splittree_changeroot((WSplit*)node, other);
+    
+    if(reclaim_space)
+        split_resize(other, &(((WSplit*)node)->geom), PRIMN_ANY, PRIMN_ANY);
+    
+    child->parent=NULL;
+    
+    node->tl=NULL;
+    node->br=NULL;
+    ((WSplit*)node)->parent=NULL;
+    destroy_obj((Obj*)node);
+}
+
+
+void splitinner_remove(WSplitInner *node, WSplit *child, bool reclaim_space)
+{
+    CALL_DYN(splitinner_remove, node, (node, child, reclaim_space));
+}
+
+
+void splittree_remove(WSplit *node, bool reclaim_space)
+{
+    if(node->parent!=NULL)
+        splitinner_remove(node->parent, node, reclaim_space);
+    else if(node->ws_if_root!=NULL)
+        splittree_changeroot(node, NULL);
+    
+    destroy_obj((Obj*)node);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Tree traversal */
+
+
+static bool defaultfilter(WSplit *node)
+{
+    return (OBJ_IS(node, WSplitRegion) && 
+            ((WSplitRegion*)node)->reg!=NULL);
+}
+
+
+static WSplit *split_current_todir_default(WSplit *node, 
+                                           WPrimn hprimn, WPrimn vprimn,
+                                           WSplitFilter *filter)
+{
+    if(filter==NULL)
+        filter=defaultfilter;
+
+    return (filter(node) ? node : NULL);
+}
+
+
+static WSplit *splitsplit_current_todir(WSplitSplit *node, 
+                                        WPrimn hprimn, WPrimn vprimn,
+                                        WSplitFilter *filter)
+{
+    WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
+    WSplit *first, *second, *ret;
+    
+    if(primn==PRIMN_TL ||
+       (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_TL)){
+        first=node->tl;
+        second=node->br;
+    }else if(primn==PRIMN_BR ||
+       (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_BR)){
+        first=node->br;
+        second=node->tl;
+    }else{
+        return NULL;
+    }
+        
+    ret=split_current_todir(first, hprimn, vprimn, filter);
+    if(ret==NULL)
+        ret=split_current_todir(second, hprimn, vprimn, filter);
+    if(ret==NULL && filter!=NULL){
+        if(filter((WSplit*)node))
+            ret=(WSplit*)node;
+    }
+        
+    return ret;
+}
+
+
+WSplit *split_current_todir(WSplit *node, WPrimn hprimn, WPrimn vprimn,
+                            WSplitFilter *filter)
+{
+    WSplit *ret=NULL;
+    CALL_DYN_RET(ret, WSplit*, split_current_todir, node, 
+                 (node, hprimn, vprimn, filter));
+    return ret;
+}
+
+
+WSplit *splitsplit_nextto(WSplitSplit *node, WSplit *child,
+                          WPrimn hprimn, WPrimn vprimn, 
+                          WSplitFilter *filter)
+{
+    WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn);
+    WSplit *split=NULL, *nnode=NULL;
+    
+    if(node->tl==child && (primn==PRIMN_BR || primn==PRIMN_ANY)){
+        split=node->br;
+        primn=PRIMN_TL;
+    }else if(node->br==child && (primn==PRIMN_TL || primn==PRIMN_ANY)){
+        split=node->tl;
+        primn=PRIMN_BR;
+    }
+    
+    if(split!=NULL){
+        if(node->dir==SPLIT_HORIZONTAL){
+            hprimn=primn;
+            vprimn=primn_none2any(vprimn);
+        }else{
+            vprimn=primn;
+            hprimn=primn_none2any(vprimn);
+        }
+        
+        nnode=split_current_todir(split, hprimn, vprimn, filter);
+    }
+   
+    return nnode;
+}
+
+
+WSplit *splitinner_nextto(WSplitInner *node, WSplit *child,
+                          WPrimn hprimn, WPrimn vprimn, 
+                          WSplitFilter *filter)
+{
+    WSplit *ret=NULL;
+    CALL_DYN_RET(ret, WSplit*, splitinner_nextto, node, 
+                 (node, child, hprimn, vprimn, filter));
+    return ret;
+}
+
+
+WSplit *split_nextto(WSplit *node, WPrimn hprimn, WPrimn vprimn, 
+                     WSplitFilter *filter)
+{
+    while(node->parent!=NULL){
+        WSplit *ret=splitinner_nextto(node->parent, node, 
+                                      hprimn, vprimn, filter);
+        if(ret!=NULL)
+            return ret;
+        node=(WSplit*)node->parent;
+    }
+    return NULL;
+}
+
+
+void splitinner_mark_current_default(WSplitInner *split, WSplit *child)
+{
+    if(((WSplit*)split)->parent!=NULL)
+        splitinner_mark_current(((WSplit*)split)->parent, (WSplit*)split);
+}
+
+
+void splitsplit_mark_current(WSplitSplit *split, WSplit *child)
+{
+    assert(child==split->tl || child==split->br);
+    
+    split->current=(split->tl==child ? SPLIT_CURRENT_TL : SPLIT_CURRENT_BR);
+    
+    splitinner_mark_current_default(&(split->isplit), child);
+}
+
+
+void splitinner_mark_current(WSplitInner *split, WSplit *child)
+{
+    CALL_DYN(splitinner_mark_current, split, (split, child));
+}
+
+
+static void splitsplit_forall(WSplitSplit *node, WSplitFn *fn)
+{
+    fn(node->tl);
+    fn(node->br);
+}
+
+
+void splitinner_forall(WSplitInner *node, WSplitFn *fn)
+{
+    CALL_DYN(splitinner_forall, node, (node, fn));
+}
+
+
+static WSplit *splitsplit_current(WSplitSplit *split)
+{
+    return (split->current==SPLIT_CURRENT_TL ? split->tl : split->br);
+}
+
+
+/*EXTL_DOC
+ * Returns the most previously active child node of \var{split}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WSplit *splitinner_current(WSplitInner *node)
+{
+    WSplit *ret=NULL;
+    CALL_DYN_RET(ret, WSplit*, splitinner_current, node, (node));
+    return ret;
+}
+    
+
+/*}}}*/
+
+
+/*{{{ X window handling */
+
+
+static void splitregion_stacking(WSplitRegion *split, 
+                                 Window *bottomret, Window *topret)
+{
+    *bottomret=None;
+    *topret=None;
+    if(split->reg!=NULL)
+        region_stacking(split->reg, bottomret, topret);
+}
+
+
+void splitsplit_stacking(WSplitSplit *split, 
+                         Window *bottomret, Window *topret)
+{
+    Window tlb=None, tlt=None;
+    Window brb=None, brt=None;
+    
+    split_stacking(split->tl, &tlb, &tlt);
+    split_stacking(split->br, &brb, &brt);
+    
+    /* To make sure that this condition holds is left to the workspace
+     * code to do after a split tree has been loaded or modified.
+     */
+    if(split->current==SPLIT_CURRENT_TL){
+        *topret=(tlt!=None ? tlt : brt);
+        *bottomret=(brb!=None ? brb : tlb);
+    }else{
+        *topret=(brt!=None ? brt : tlt);
+        *bottomret=(tlb!=None ? tlb : brb);
+    }
+}
+
+void split_stacking(WSplit *split, Window *bottomret, Window *topret)
+{
+    *bottomret=None;
+    *topret=None;
+    {
+        CALL_DYN(split_stacking, split, (split, bottomret, topret));
+    }
+}
+
+
+static void splitregion_restack(WSplitRegion *split, Window other, int mode)
+{
+    if(split->reg!=NULL)
+        region_restack(split->reg, other, mode);
+}
+
+void splitsplit_restack(WSplitSplit *split, Window other, int mode)
+{
+    Window bottom=None, top=None;
+    WSplit *first, *second;
+    
+    if(split->current==SPLIT_CURRENT_TL){
+        first=split->br;
+        second=split->tl;
+    }else{
+        first=split->tl;
+        second=split->br;
+    }
+    
+    split_restack(first, other, mode);
+    split_stacking(first, &bottom, &top);
+    if(top!=None){
+        other=top;
+        mode=Above;
+    }
+    split_restack(second, other, mode);
+}
+
+void split_restack(WSplit *split, Window other, int mode)
+{
+    CALL_DYN(split_restack, split, (split, other, mode));
+}
+
+
+static void splitregion_map(WSplitRegion *split)
+{
+    if(split->reg!=NULL)
+        region_map(split->reg);
+}
+
+static void splitinner_map(WSplitInner *split)
+{
+    splitinner_forall(split, split_map);
+}
+
+void split_map(WSplit *split)
+{
+    CALL_DYN(split_map, split, (split));
+}
+
+
+static void splitregion_unmap(WSplitRegion *split)
+{
+    if(split->reg!=NULL)
+        region_unmap(split->reg);
+}
+
+static void splitinner_unmap(WSplitInner *split)
+{
+    splitinner_forall(split, split_unmap);
+}
+
+void split_unmap(WSplit *split)
+{
+    CALL_DYN(split_unmap, split, (split));
+}
+
+
+static void splitregion_reparent(WSplitRegion *split, WWindow *wwin)
+{
+    if(split->reg!=NULL){
+        WRectangle g=split->split.geom;
+        region_reparent(split->reg, wwin, &g, REGION_FIT_EXACT);
+    }
+}
+
+
+static void splitsplit_reparent(WSplitSplit *split, WWindow *wwin)
+{
+    if(split->current==SPLIT_CURRENT_TL){
+        split_reparent(split->br, wwin);
+        split_reparent(split->tl, wwin);
+    }else{
+        split_reparent(split->tl, wwin);
+        split_reparent(split->br, wwin);
+    }
+}
+
+
+void split_reparent(WSplit *split, WWindow *wwin)
+{
+    CALL_DYN(split_reparent, split, (split, wwin));
+}
+
+
+/*}}}*/
+
+
+/*{{{ Transpose, flip, rotate */
+
+
+void splitsplit_flip_default(WSplitSplit *split)
+{
+    WRectangle tlng, brng;
+    WRectangle *sg=&((WSplit*)split)->geom;
+    WSplit *tmp;
+    
+    assert(split->tl!=NULL && split->br!=NULL);
+    
+    split_update_bounds((WSplit*)split, TRUE);
+
+    tlng=split->tl->geom;
+    brng=split->br->geom;
+    
+    if(split->dir==SPLIT_HORIZONTAL){
+        brng.x=sg->x;
+        tlng.x=sg->x+sg->w-tlng.w;
+    }else{
+        brng.y=sg->y;
+        tlng.y=sg->y+sg->h-tlng.h;
+    }
+    
+    tmp=split->tl;
+    split->tl=split->br;
+    split->br=tmp;
+    split->current=(split->current==SPLIT_CURRENT_TL
+                    ? SPLIT_CURRENT_BR
+                    : SPLIT_CURRENT_TL);
+
+    split_do_resize(split->tl, &brng, PRIMN_ANY, PRIMN_ANY, FALSE);
+    split_do_resize(split->br, &tlng, PRIMN_ANY, PRIMN_ANY, FALSE);
+}
+
+
+static void splitsplit_flip_(WSplitSplit *split)
+{
+    CALL_DYN(splitsplit_flip, split, (split));
+}
+
+
+/*EXTL_DOC
+ * Flip contents of \var{node}.
+ */
+EXTL_EXPORT_MEMBER
+void splitsplit_flip(WSplitSplit *split)
+{
+    splittree_begin_resize();
+
+    if(!move_stdisp_out_of_way((WSplit*)split))
+        return;
+    
+    splitsplit_flip_(split);
+    
+    splittree_end_resize();
+}
+
+typedef enum{
+    FLIP_VERTICAL,
+    FLIP_HORIZONTAL,
+    FLIP_NONE,
+    FLIP_ANY
+} FlipDir;
+
+
+static FlipDir flipdir=FLIP_VERTICAL;
+
+
+static void do_flip(WSplit *split)
+{
+    WSplitSplit *ss=OBJ_CAST(split, WSplitSplit);
+    
+    if(ss!=NULL){
+        if((flipdir==FLIP_ANY
+            || (ss->dir==SPLIT_VERTICAL && flipdir==FLIP_VERTICAL)
+            || (ss->dir==SPLIT_HORIZONTAL && flipdir==FLIP_HORIZONTAL)) 
+           && !OBJ_IS(ss->tl, WSplitST) 
+           && !OBJ_IS(ss->br, WSplitST)){
+            splitsplit_flip_(ss);
+        }
+    }
+    
+    if(OBJ_IS(ss, WSplitInner))
+        splitinner_forall((WSplitInner*)ss, do_flip);
+}
+
+    
+static void splittree_flip_dir(WSplit *splittree, FlipDir dir)
+{
+    /* todo stdisp outta way */
+    if(OBJ_IS(splittree, WSplitInner)){
+        flipdir=dir;
+        splitinner_forall((WSplitInner*)splittree, do_flip);
+    }
+}
+
+
+static bool split_fliptrans_to(WSplit *node, const WRectangle *geom, 
+                              bool trans, FlipDir flip)
+{
+    WRectangle rg;
+    WSplit *node2;
+    
+    splittree_begin_resize();
+    
+    /* split_do_resize can do things right if 'node' has stdisp as child, 
+     * but otherwise transpose will put the stdisp in a bad split
+     * configuration if it is contained within 'node', so we must
+     * first move it and its fixed parent split below node. For correct
+     * geometry calculation we move it immediately below node, and
+     * resize stdisp's fixed parent node instead.
+     */
+    node2=move_stdisp_out_of_way(node);
+    
+    if(node2==NULL)
+        return FALSE;
+    
+    split_update_bounds(node2, TRUE);
+    
+    split_do_rqgeom_(node2, geom, PRIMN_ANY, PRIMN_ANY, &rg, FALSE);
+    
+    split_do_resize(node2, &rg, PRIMN_ANY, PRIMN_ANY, trans);
+    
+    if(flip!=FLIP_NONE)
+        splittree_flip_dir(node2, flip);
+
+    splittree_end_resize();
+    
+    return TRUE;
+}
+
+
+bool split_transpose_to(WSplit *node, const WRectangle *geom)
+{
+    return split_fliptrans_to(node, geom, TRUE, FLIP_ANY);
+}
+
+
+/*EXTL_DOC
+ * Transpose contents of \var{node}. 
+ */
+EXTL_EXPORT_MEMBER
+void split_transpose(WSplit *node)
+{
+    WRectangle g=node->geom;
+    
+    split_transpose_to(node, &g);
+}
+
+
+bool split_rotate_to(WSplit *node, const WRectangle *geom, int rotation)
+{
+    FlipDir flip=FLIP_NONE;
+    bool trans=FALSE;
+    
+    if(rotation==SCREEN_ROTATION_90){
+        flip=FLIP_HORIZONTAL;
+        trans=TRUE;
+    }else if(rotation==SCREEN_ROTATION_180){
+        flip=FLIP_ANY;
+    }else if(rotation==SCREEN_ROTATION_270){
+        flip=FLIP_VERTICAL;
+        trans=TRUE;
+    }
+
+    return split_fliptrans_to(node, geom, trans, flip);
+}
+
+/*}}}*/
+
+
+/*{{{ Exports */
+
+
+/*EXTL_DOC
+ * Return parent split for \var{split}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WSplitInner *split_parent(WSplit *split)
+{
+    return split->parent;
+}
+
+
+/*EXTL_DOC
+ * Returns the area of workspace used by the regions under \var{split}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+ExtlTab split_geom(WSplit *split)
+{
+    return extl_table_from_rectangle(&(split->geom));
+}
+
+
+/*EXTL_DOC
+ * Returns the top or left child node of \var{split} depending
+ * on the direction of the split.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WSplit *splitsplit_tl(WSplitSplit *split)
+{
+    return split->tl;
+}
+
+
+/*EXTL_DOC
+ * Returns the bottom or right child node of \var{split} depending
+ * on the direction of the split.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WSplit *splitsplit_br(WSplitSplit *split)
+{
+    return split->br;
+}
+
+/*EXTL_DOC
+ * Returns the direction of \var{split}; either ''vertical'' or
+ * ''horizontal''.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+const char *splitsplit_dir(WSplitSplit *split)
+{
+    return (split->dir==SPLIT_VERTICAL ? "vertical" : "horizontal");
+}
+
+
+/*EXTL_DOC
+ * Returns the region contained in \var{node}.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WRegion *splitregion_reg(WSplitRegion *node)
+{
+    return node->reg;
+}
+    
+
+/*}}}*/
+
+
+/*{{{ Save support */
+
+
+ExtlTab split_base_config(WSplit *node)
+{
+    ExtlTab t=extl_create_table();
+    extl_table_sets_s(t, "type", OBJ_TYPESTR(node));
+    return t;
+}
+
+
+static bool splitregion_get_config(WSplitRegion *node, ExtlTab *ret)
+{
+    ExtlTab rt, t;
+    
+    if(node->reg==NULL)
+        return FALSE;
+    
+    if(!region_supports_save(node->reg)){
+        warn(TR("Unable to get configuration for %s."), 
+             region_name(node->reg));
+        return FALSE;
+    }
+    
+    rt=region_get_configuration(node->reg);
+    t=split_base_config(&(node->split));
+    extl_table_sets_t(t, "regparams", rt);
+    extl_unref_table(rt);
+    *ret=t;
+    
+    return TRUE;
+}
+
+
+static bool splitst_get_config(WSplitST *node, ExtlTab *ret)
+{
+    *ret=split_base_config((WSplit*)node);
+    return TRUE;
+}
+
+
+static bool splitsplit_get_config(WSplitSplit *node, ExtlTab *ret)
+{
+    ExtlTab tab, tltab, brtab;
+    int tls, brs;
+    
+    if(!split_get_config(node->tl, &tltab))
+        return split_get_config(node->br, ret);
+    
+    if(!split_get_config(node->br, &brtab)){
+        *ret=tltab;
+        return TRUE;
+    }
+
+    tab=split_base_config((WSplit*)node);
+
+    tls=split_size(node->tl, node->dir);
+    brs=split_size(node->br, node->dir);
+        
+    extl_table_sets_s(tab, "dir", (node->dir==SPLIT_VERTICAL
+                                   ? "vertical" : "horizontal"));
+
+    extl_table_sets_i(tab, "tls", tls);
+    extl_table_sets_t(tab, "tl", tltab);
+    extl_unref_table(tltab);
+
+    extl_table_sets_i(tab, "brs", brs);
+    extl_table_sets_t(tab, "br", brtab);
+    extl_unref_table(brtab);
+        
+    *ret=tab;
+    
+    return TRUE;
+}
+
+
+bool split_get_config(WSplit *node, ExtlTab *tabret)
+{
+    bool ret=FALSE;
+    CALL_DYN_RET(ret, bool, split_get_config, node, (node, tabret));
+    return ret;
+}
+
+
+/*}}}*/
+
+
+/*{{{ The classes */
+
+
+static DynFunTab split_dynfuntab[]={
+    {split_do_resize, split_do_resize_default},
+    {(DynFun*)split_current_todir, (DynFun*)split_current_todir_default},
+    END_DYNFUNTAB,
+};
+
+static DynFunTab splitinner_dynfuntab[]={
+    {splitinner_mark_current, splitinner_mark_current_default},
+    {split_map, splitinner_map},
+    {split_unmap, splitinner_unmap},
+    END_DYNFUNTAB,
+};
+
+static DynFunTab splitsplit_dynfuntab[]={
+    {split_update_bounds, splitsplit_update_bounds},
+    {split_do_resize, splitsplit_do_resize},
+    {splitinner_do_rqsize, splitsplit_do_rqsize},
+    {splitinner_replace, splitsplit_replace},
+    {splitinner_remove, splitsplit_remove},
+    {(DynFun*)split_current_todir, (DynFun*)splitsplit_current_todir},
+    {(DynFun*)splitinner_current, (DynFun*)splitsplit_current},
+    {(DynFun*)splitinner_nextto, (DynFun*)splitsplit_nextto},
+    {splitinner_mark_current, splitsplit_mark_current},
+    {(DynFun*)split_get_config, (DynFun*)splitsplit_get_config},
+    {splitinner_forall, splitsplit_forall},
+    {split_restack, splitsplit_restack},
+    {split_stacking, splitsplit_stacking},
+    {split_reparent, splitsplit_reparent},
+    {splitsplit_flip, splitsplit_flip_default},
+    END_DYNFUNTAB,
+};
+
+static DynFunTab splitregion_dynfuntab[]={
+    {split_update_bounds, splitregion_update_bounds},
+    {split_do_resize, splitregion_do_resize},
+    {(DynFun*)split_get_config, (DynFun*)splitregion_get_config},
+    {split_map, splitregion_map},
+    {split_unmap, splitregion_unmap},
+    {split_restack, splitregion_restack},
+    {split_stacking, splitregion_stacking},
+    {split_reparent, splitregion_reparent},
+    END_DYNFUNTAB,
+};
+
+static DynFunTab splitst_dynfuntab[]={
+    {split_update_bounds, splitst_update_bounds},
+    {split_do_resize, splitst_do_resize},
+    {(DynFun*)split_get_config, (DynFun*)splitst_get_config},
+    END_DYNFUNTAB,
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WSplit, Obj, split_deinit, split_dynfuntab);
+
+EXTL_EXPORT
+IMPLCLASS(WSplitInner, WSplit, splitinner_deinit, splitinner_dynfuntab);
+
+EXTL_EXPORT
+IMPLCLASS(WSplitSplit, WSplitInner, splitsplit_deinit, splitsplit_dynfuntab);
+
+EXTL_EXPORT
+IMPLCLASS(WSplitRegion, WSplit, splitregion_deinit, splitregion_dynfuntab);
+
+EXTL_EXPORT
+IMPLCLASS(WSplitST, WSplitRegion, splitst_deinit, splitst_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/mod_tiling/split.h b/mod_tiling/split.h
new file mode 100644 (file)
index 0000000..fa74122
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * ion/mod_tiling/split.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_TILING_SPLIT_H
+#define ION_MOD_TILING_SPLIT_H
+
+#include <ioncore/common.h>
+#include <ioncore/window.h>
+#include <ioncore/reginfo.h>
+#include <ioncore/attach.h>
+#include <ioncore/rectangle.h>
+#include <libextl/extl.h>
+
+
+INTRCLASS(WSplit);
+INTRCLASS(WSplitInner);
+INTRCLASS(WSplitSplit);
+INTRCLASS(WSplitRegion);
+INTRCLASS(WSplitST);
+
+
+typedef enum{
+    SPLIT_HORIZONTAL,
+    SPLIT_VERTICAL
+} WSplitDir;
+
+
+typedef enum{
+    PRIMN_ANY,
+    PRIMN_TL,
+    PRIMN_BR,
+    PRIMN_NONE
+} WPrimn;
+
+
+typedef enum {
+    SPLIT_CURRENT_TL,
+    SPLIT_CURRENT_BR
+} WSplitCurrent;
+
+
+DECLCLASS(WSplit){
+    Obj obj;
+    WRectangle geom;
+    WSplitInner *parent;
+    void *ws_if_root;
+    
+    int min_w, min_h;
+    int max_w, max_h;
+    int unused_w, unused_h;
+};
+
+
+DECLCLASS(WSplitInner){
+    WSplit split;
+};
+
+
+DECLCLASS(WSplitSplit){
+    WSplitInner isplit;
+    int dir;
+    WSplit *tl, *br;
+    int current;
+};
+
+
+DECLCLASS(WSplitRegion){
+    WSplit split;
+    WRegion *reg;
+};
+
+
+DECLCLASS(WSplitST){
+    WSplitRegion regnode;
+    int orientation;
+    int corner;
+    bool fullsize;
+};
+
+
+typedef struct{
+    int tl, br;
+    bool any;
+} RootwardAmount;
+
+
+typedef bool WSplitFilter(WSplit *split);
+typedef void WSplitFn(WSplit *split);
+
+
+/* Misc. */
+
+extern int split_size(WSplit *split, int dir);
+extern int split_pos(WSplit *split, int dir);
+extern int split_other_size(WSplit *split, int dir);
+extern int split_other_pos(WSplit *split, int dir);
+
+extern WSplitRegion *splittree_node_of(WRegion *reg);
+extern bool splittree_set_node_of(WRegion *reg, WSplitRegion *split);
+
+extern WPrimn primn_invert(WPrimn primn);
+extern WPrimn primn_none2any(WPrimn primn);
+
+/* Init/deinit */
+
+extern bool split_init(WSplit *split, const WRectangle *geom);
+extern bool splitinner_init(WSplitInner *split, const WRectangle *geom);
+extern bool splitsplit_init(WSplitSplit *split, const WRectangle *geom, 
+                            int dir);
+extern bool splitregion_init(WSplitRegion *split,const WRectangle *geom,
+                             WRegion *reg);
+extern bool splitst_init(WSplitST *split, const WRectangle *geom,
+                         WRegion *reg);
+
+
+extern WSplitSplit *create_splitsplit(const WRectangle *geom, int dir);
+extern WSplitRegion *create_splitregion(const WRectangle *geom, WRegion *reg);
+extern WSplitST *create_splitst(const WRectangle *geom, WRegion *reg);
+
+
+extern void split_deinit(WSplit *split);
+extern void splitsplit_deinit(WSplitSplit *split);
+extern void splitinner_deinit(WSplitInner *split);
+extern void splitregion_deinit(WSplitRegion *split);
+extern void splitst_deinit(WSplitST *split);
+
+/* Geometry */
+
+DYNFUN void split_update_bounds(WSplit *node, bool recursive);
+extern void splitsplit_update_geom_from_children(WSplitSplit *node);
+DYNFUN void split_do_resize(WSplit *node, const WRectangle *ng, 
+                            WPrimn hprimn, WPrimn vprimn, bool transpose);
+extern void splitsplit_do_resize(WSplitSplit *node, const WRectangle *ng, 
+                                 WPrimn hprimn, WPrimn vprimn, bool transpose);
+extern void split_resize(WSplit *node, const WRectangle *ng, 
+                         WPrimn hprimn, WPrimn vprimn);
+DYNFUN void splitinner_do_rqsize(WSplitInner *p, WSplit *node, 
+                                 RootwardAmount *ha, RootwardAmount *va, 
+                                 WRectangle *rg, bool tryonly);
+extern ExtlTab split_rqgeom(WSplit *node, ExtlTab g);
+
+/* Split */
+
+extern void splittree_rqgeom(WSplit *node, int flags, 
+                             const WRectangle *geom, WRectangle *geomret);
+
+
+DYNFUN void splitinner_replace(WSplitInner *node, WSplit *child, WSplit *what);
+extern WSplitRegion *splittree_split(WSplit *node, int dir, 
+                                     WPrimn primn, int minsize, 
+                                     WRegionSimpleCreateFn *fn,
+                                     WWindow *parent);
+
+extern void splittree_changeroot(WSplit *root, WSplit *node);
+
+/* Remove */
+
+DYNFUN void splitinner_remove(WSplitInner *node, WSplit *child, 
+                              bool reclaim_space);
+extern void splittree_remove(WSplit *node, bool reclaim_space);
+
+/* Tree traversal */
+
+extern WSplit *split_find_root(WSplit *split);
+DYNFUN WSplit *split_current_todir(WSplit *node, 
+                                   WPrimn hprimn, WPrimn vprimn,
+                                   WSplitFilter *filter);
+DYNFUN WSplit *splitinner_nextto(WSplitInner *node, WSplit *child,
+                                 WPrimn hprimn, WPrimn vprimn, 
+                                 WSplitFilter *filter);
+DYNFUN WSplit *splitinner_current(WSplitInner *node);
+DYNFUN void splitinner_mark_current(WSplitInner *split, WSplit *child);
+DYNFUN void splitinner_forall(WSplitInner *node, WSplitFn *fn);
+extern WSplit *split_nextto(WSplit *node,
+                            WPrimn hprimn, WPrimn vprimn,
+                            WSplitFilter *filter);
+
+/* X window handling */
+
+void split_restack(WSplit *split, Window win, int mode);
+void split_stacking(WSplit *split, Window *bottom_ret, Window *top_ret);
+void split_map(WSplit *split);
+void split_unmap(WSplit *split);
+void split_reparent(WSplit *split, WWindow *wwin);
+
+/* Transpose, flip, rotate */
+
+extern void split_transpose(WSplit *split);
+extern bool split_transpose_to(WSplit *split, const WRectangle *geom);
+
+extern void splitsplit_flip_default(WSplitSplit *split);
+DYNFUN void splitsplit_flip(WSplitSplit *split);
+
+extern bool split_rotate_to(WSplit *node, const WRectangle *geom, 
+                            int rotation);
+
+/* Save support */
+
+extern bool split_get_config(WSplit *node, ExtlTab *ret);
+extern ExtlTab split_base_config(WSplit *node);
+
+/* Internal. */
+
+extern void splittree_begin_resize();
+extern void splittree_end_resize();
+extern void splittree_scan_stdisp_rootward(WSplit *node);
+
+extern void split_do_rqgeom_(WSplit *node, const WRectangle *ng, 
+                             bool hany, bool vany, WRectangle *rg, 
+                             bool tryonly);
+
+
+#endif /* ION_MOD_TILING_SPLIT_H */
diff --git a/mod_tiling/splitfloat.c b/mod_tiling/splitfloat.c
new file mode 100644 (file)
index 0000000..6ab6b31
--- /dev/null
@@ -0,0 +1,1009 @@
+/*
+ * ion/panews/splitext.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <limits.h>
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/rootwin.h>
+#include <ioncore/xwindow.h>
+#include <ioncore/window.h>
+
+#include "tiling.h"
+#include "split.h"
+#include "splitfloat.h"
+#include "panehandle.h"
+
+
+#define GEOM(X) (((WSplit*)(X))->geom)
+
+
+/*{{{ Init/deinit */
+
+
+static void splitfloat_set_borderlines(WSplitFloat *split)
+{
+    int dir=split->ssplit.dir;
+    
+    split->tlpwin->bline=(dir==SPLIT_HORIZONTAL
+                          ? GR_BORDERLINE_RIGHT
+                          : GR_BORDERLINE_BOTTOM);
+
+    split->brpwin->bline=(dir==SPLIT_HORIZONTAL
+                          ? GR_BORDERLINE_LEFT
+                          : GR_BORDERLINE_TOP);
+}
+
+
+bool splitfloat_init(WSplitFloat *split, const WRectangle *geom, 
+                     WTiling *ws, int dir)
+{
+    WFitParams fp;
+    WWindow *par=REGION_PARENT(ws);
+    
+    assert(par!=NULL);
+
+    fp.g=*geom;
+    fp.mode=REGION_FIT_EXACT;
+    split->tlpwin=create_panehandle(par, &fp);
+    if(split->tlpwin==NULL)
+        return FALSE;
+
+    fp.g=*geom;
+    fp.mode=REGION_FIT_EXACT;
+    split->brpwin=create_panehandle(par, &fp);
+    if(split->brpwin==NULL){
+        destroy_obj((Obj*)split->tlpwin);
+        return FALSE;
+    }
+    
+    if(!splitsplit_init(&(split->ssplit), geom, dir)){
+        destroy_obj((Obj*)split->brpwin);
+        destroy_obj((Obj*)split->tlpwin);
+        return FALSE;
+    }
+
+    split->tlpwin->splitfloat=split;
+    split->brpwin->splitfloat=split;
+    
+    splitfloat_set_borderlines(split);
+
+    if(REGION_IS_MAPPED(ws)){
+        region_map((WRegion*)(split->tlpwin));
+        region_map((WRegion*)(split->brpwin));
+    }
+    
+    return TRUE;
+}
+
+
+WSplitFloat *create_splitfloat(const WRectangle *geom, WTiling *ws, int dir)
+{
+    CREATEOBJ_IMPL(WSplitFloat, splitfloat, (p, geom, ws, dir));
+}
+
+
+void splitfloat_deinit(WSplitFloat *split)
+{
+    if(split->tlpwin!=NULL){
+        WPaneHandle *tmp=split->tlpwin;
+        split->tlpwin=NULL;
+        tmp->splitfloat=NULL;
+        destroy_obj((Obj*)tmp);
+    }
+
+    if(split->brpwin!=NULL){
+        WPaneHandle *tmp=split->brpwin;
+        split->brpwin=NULL;
+        tmp->splitfloat=NULL;
+        destroy_obj((Obj*)tmp);
+    }
+    
+    splitsplit_deinit(&(split->ssplit));
+}
+
+
+/*}}}*/
+
+
+/*{{{ X window handling */
+
+
+static void stack_stacking_reg(WRegion *reg, 
+                               Window *bottomret, Window *topret)
+{
+    Window b=None, t=None;
+    
+    if(reg!=NULL){
+        region_stacking(reg, &b, &t);
+        if(*bottomret==None)
+            *bottomret=b;
+        if(t!=None)
+            *topret=t;
+    }
+}
+
+
+static void stack_stacking_split(WSplit *split,
+                                 Window *bottomret, Window *topret)
+{
+    Window b=None, t=None;
+    
+    if(split!=NULL){
+        split_stacking(split, &b, &t);
+        if(*bottomret==None)
+            *bottomret=b;
+        if(t!=None)
+            *topret=t;
+    }
+}
+
+
+static void splitfloat_stacking(WSplitFloat *split, 
+                                Window *bottomret, Window *topret)
+{
+    *bottomret=None;
+    *topret=None;
+    
+    if(split->ssplit.current!=SPLIT_CURRENT_TL){
+        stack_stacking_reg((WRegion*)split->tlpwin, bottomret, topret);
+        stack_stacking_split(split->ssplit.tl, bottomret, topret);
+        stack_stacking_reg((WRegion*)split->brpwin, bottomret, topret);
+        stack_stacking_split(split->ssplit.br, bottomret, topret);
+    }else{
+        stack_stacking_reg((WRegion*)split->brpwin, bottomret, topret);
+        stack_stacking_split(split->ssplit.br, bottomret, topret);
+        stack_stacking_reg((WRegion*)split->tlpwin, bottomret, topret);
+        stack_stacking_split(split->ssplit.tl, bottomret, topret);
+    }
+}
+
+
+static void stack_restack_reg(WRegion *reg, Window *other, int *mode)
+{
+    Window b=None, t=None;
+    
+    if(reg!=NULL){
+        region_restack(reg, *other, *mode);
+        region_stacking(reg, &b, &t);
+        if(t!=None){
+            *other=t;
+            *mode=Above;
+        }
+    }
+}
+
+
+static void stack_restack_split(WSplit *split, Window *other, int *mode)
+{
+    Window b=None, t=None;
+    
+    if(split!=NULL){
+        split_restack(split, *other, *mode);
+        split_stacking(split, &b, &t);
+        if(t!=None){
+            *other=t;
+            *mode=Above;
+        }
+    }
+}
+
+
+
+static void splitfloat_restack(WSplitFloat *split, Window other, int mode)
+{
+    if(split->ssplit.current!=SPLIT_CURRENT_TL){
+        stack_restack_reg((WRegion*)split->tlpwin, &other, &mode);
+        stack_restack_split(split->ssplit.tl, &other, &mode);
+        stack_restack_reg((WRegion*)split->brpwin, &other, &mode);
+        stack_restack_split(split->ssplit.br, &other, &mode);
+    }else{
+        stack_restack_reg((WRegion*)split->brpwin, &other, &mode);
+        stack_restack_split(split->ssplit.br, &other, &mode);
+        stack_restack_reg((WRegion*)split->tlpwin, &other, &mode);
+        stack_restack_split(split->ssplit.tl, &other, &mode);
+    }
+}
+
+
+static void splitfloat_map(WSplitFloat *split)
+{
+    region_map((WRegion*)(split->tlpwin));
+    region_map((WRegion*)(split->brpwin));
+    splitinner_forall((WSplitInner*)split, split_map);
+}
+
+
+static void splitfloat_unmap(WSplitFloat *split)
+{
+    region_unmap((WRegion*)(split->tlpwin));
+    region_unmap((WRegion*)(split->brpwin));
+    splitinner_forall((WSplitInner*)split, split_unmap);
+}
+
+
+static void reparentreg(WRegion *reg, WWindow *target)
+{
+    WRectangle g=REGION_GEOM(reg);
+    region_reparent(reg, target, &g, REGION_FIT_EXACT);
+}
+
+
+static void splitfloat_reparent(WSplitFloat *split, WWindow *target)
+{
+    if(split->ssplit.current!=SPLIT_CURRENT_TL){
+        reparentreg((WRegion*)split->tlpwin, target);
+        split_reparent(split->ssplit.tl, target);
+        reparentreg((WRegion*)split->brpwin, target);
+        split_reparent(split->ssplit.br, target);
+    }else{
+        reparentreg((WRegion*)split->brpwin, target);
+        split_reparent(split->ssplit.br, target);
+        reparentreg((WRegion*)split->tlpwin, target);
+        split_reparent(split->ssplit.tl, target);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Geometry */
+
+
+#define TL_BORDER(SF) ((SF)->ssplit.dir==SPLIT_VERTICAL \
+                       ? (SF)->tlpwin->bdw.bottom       \
+                       : (SF)->tlpwin->bdw.right)
+
+#define BR_BORDER(SF) ((SF)->ssplit.dir==SPLIT_VERTICAL \
+                       ? (SF)->brpwin->bdw.top          \
+                       : (SF)->brpwin->bdw.left)
+
+
+void splitfloat_tl_pwin_to_cnt(WSplitFloat *split, WRectangle *g)
+{
+    if(split->ssplit.dir==SPLIT_HORIZONTAL)
+        g->w=maxof(1, g->w-split->tlpwin->bdw.right);
+    else
+        g->h=maxof(1, g->h-split->tlpwin->bdw.bottom);
+}
+
+
+void splitfloat_br_pwin_to_cnt(WSplitFloat *split, WRectangle *g)
+{
+    if(split->ssplit.dir==SPLIT_HORIZONTAL){
+        int delta=split->tlpwin->bdw.left;
+        g->w=maxof(1, g->w-delta);
+        g->x+=delta;
+    }else{
+        int delta=split->tlpwin->bdw.top;
+        g->h=maxof(1, g->h-delta);
+        g->y+=delta;
+    }
+}
+
+
+void splitfloat_tl_cnt_to_pwin(WSplitFloat *split, WRectangle *g)
+{
+    if(split->ssplit.dir==SPLIT_HORIZONTAL)
+        g->w=maxof(1, g->w+split->tlpwin->bdw.right);
+    else
+        g->h=maxof(1, g->h+split->tlpwin->bdw.bottom);
+}
+
+
+void splitfloat_br_cnt_to_pwin(WSplitFloat *split, WRectangle *g)
+{
+    if(split->ssplit.dir==SPLIT_HORIZONTAL){
+        int delta=split->tlpwin->bdw.left;
+        g->w=maxof(1, g->w+delta);
+        g->x-=delta;
+    }else{
+        int delta=split->tlpwin->bdw.top;
+        g->h=maxof(1, g->h+delta);
+        g->y-=delta;
+    }
+}
+
+    
+static int infadd(int x, int y)
+{
+    return ((x==INT_MAX || y==INT_MAX) ? INT_MAX : (x+y));
+}
+
+
+static int splitfloat_get_handle(WSplitFloat *split, int dir, 
+                                  WSplit *other)
+{
+    assert(other==split->ssplit.tl || other==split->ssplit.br);
+    
+    if(dir!=split->ssplit.dir)
+        return 0;
+
+    if(dir==SPLIT_VERTICAL){
+        if(other==split->ssplit.tl)
+            return split->tlpwin->bdw.right;
+        else if(other==split->ssplit.br)
+            return split->tlpwin->bdw.left;
+    }else{
+        if(other==split->ssplit.tl)
+            return split->tlpwin->bdw.bottom;
+        else if(other==split->ssplit.br)
+            return split->tlpwin->bdw.top;
+    }
+    
+    return 0;
+}
+
+
+static int splitfloat_get_max(WSplitFloat *split, int dir, WSplit *other)
+{
+    return infadd((dir==SPLIT_VERTICAL ? other->max_h : other->max_w),
+                  splitfloat_get_handle(split, dir, other));
+}
+
+
+static int splitfloat_get_min(WSplitFloat *split, int dir, WSplit *other)
+{
+    return ((dir==SPLIT_VERTICAL ? other->min_h : other->min_w)
+            +splitfloat_get_handle(split, dir, other));
+}
+
+
+static void splitfloat_update_bounds(WSplitFloat *split, bool recursive)
+{
+    WSplit *tl=split->ssplit.tl, *br=split->ssplit.br;
+    WSplit *node=(WSplit*)split;
+    int tl_max_w, br_max_w, tl_max_h, br_max_h;
+    int tl_min_w, br_min_w, tl_min_h, br_min_h;
+    
+    if(recursive){
+        split_update_bounds(tl, recursive);
+        split_update_bounds(br, recursive);
+    }
+
+    tl_max_w=splitfloat_get_max(split, SPLIT_HORIZONTAL, tl);
+    br_max_w=splitfloat_get_max(split, SPLIT_HORIZONTAL, br);
+    tl_max_h=splitfloat_get_max(split, SPLIT_VERTICAL, tl);
+    br_max_h=splitfloat_get_max(split, SPLIT_VERTICAL, br);
+    tl_min_w=splitfloat_get_min(split, SPLIT_HORIZONTAL, tl);
+    br_min_w=splitfloat_get_min(split, SPLIT_HORIZONTAL, br);
+    tl_min_h=splitfloat_get_min(split, SPLIT_VERTICAL, tl);
+    br_min_h=splitfloat_get_min(split, SPLIT_VERTICAL, br);
+    
+    if(split->ssplit.dir==SPLIT_HORIZONTAL){
+        node->max_w=infadd(tl_max_w, br_max_w);
+        node->min_w=minof(tl_min_w, br_min_w);
+        node->unused_w=0;
+        node->min_h=maxof(tl_min_h, br_min_h);
+        node->max_h=maxof(minof(tl_max_h, br_max_h), node->min_h);
+        node->unused_h=minof(tl->unused_h, br->unused_h);
+    }else{
+        node->max_h=infadd(tl_max_h, br_max_h);
+        node->min_h=minof(tl_min_h, br_min_h);
+        node->unused_h=0;
+        node->min_w=maxof(tl_min_w, br_min_w);
+        node->max_w=maxof(minof(tl_max_w, br_max_w), node->min_w);
+        node->unused_w=minof(tl->unused_w, br->unused_w);
+    }
+}
+
+
+void splitfloat_update_handles(WSplitFloat *split, const WRectangle *tlg_,
+                               const WRectangle *brg_)
+{
+    WRectangle tlg=*tlg_, brg=*brg_;
+    
+    if(split->ssplit.dir==SPLIT_HORIZONTAL){
+        tlg.w=split->tlpwin->bdw.right;
+        tlg.x=tlg_->x+tlg_->w-tlg.w;
+        brg.w=split->brpwin->bdw.left;
+    }else{
+        tlg.h=split->tlpwin->bdw.bottom;
+        tlg.y=tlg_->y+tlg_->h-tlg.h;
+        brg.h=split->brpwin->bdw.top;
+    }
+    
+    region_fit((WRegion*)split->tlpwin, &tlg, REGION_FIT_EXACT);
+    region_fit((WRegion*)split->brpwin, &brg, REGION_FIT_EXACT);
+}
+
+
+static void bound(int *what, int min, int max)
+{
+    if(*what<min)
+        *what=min;
+    else if(*what>max)
+        *what=max;
+}
+
+
+static void adjust_sizes(int *tls_, int *brs_, int nsize, 
+                         int tlmin, int brmin, int tlmax, int brmax,
+                         int primn)
+{
+    int tls=maxof(0, *tls_);
+    int brs=maxof(0, *brs_);
+    nsize=maxof(1, nsize);
+    
+    if(primn==PRIMN_TL){
+        tls=maxof(1, nsize-brs);
+        bound(&tls, tlmin, tlmax);
+        brs=nsize-tls;
+        bound(&brs, brmin, brmax);
+        tls=nsize-brs;
+        bound(&tls, tlmin, tlmax);
+    }else if(primn==PRIMN_BR){
+        brs=maxof(1, nsize-tls);
+        bound(&brs, brmin, brmax);
+        tls=nsize-brs;
+        bound(&tls, tlmin, tlmax);
+        brs=nsize-tls;
+        bound(&brs, brmin, brmax);
+    }else{ /* && PRIMN_ANY */
+        tls=tls*nsize/maxof(2, tls+brs);
+        bound(&tls, tlmin, tlmax);
+        brs=nsize-tls;
+        bound(&brs, brmin, brmax);
+        tls=nsize-brs;
+        bound(&tls, tlmin, tlmax);
+    }
+    
+    *tls_=tls;
+    *brs_=brs;
+}
+
+
+static void adjust_size(int *sz, int dir, WSplitFloat *f, WSplit *s)
+{
+    int mi=splitfloat_get_min(f, dir, s);
+    int ma=splitfloat_get_max(f, dir, s);
+    *sz=maxof(mi, minof(*sz, ma));
+}
+
+
+static void splitfloat_do_resize(WSplitFloat *split, const WRectangle *ng, 
+                                 int hprimn, int vprimn, bool transpose)
+{
+    WRectangle tlg=GEOM(split->ssplit.tl);
+    WRectangle brg=GEOM(split->ssplit.br);
+    WRectangle ntlg=*ng, nbrg=*ng;
+    WRectangle *og=&((WSplit*)split)->geom;
+    int dir=split->ssplit.dir;
+    bool adjust=TRUE;
+    
+    splitfloat_tl_cnt_to_pwin(split, &tlg);
+    splitfloat_br_cnt_to_pwin(split, &brg);
+
+    if(transpose){
+        if(dir==SPLIT_VERTICAL){
+            dir=SPLIT_HORIZONTAL;
+            split->tlpwin->bline=GR_BORDERLINE_RIGHT;
+            split->brpwin->bline=GR_BORDERLINE_LEFT;
+        }else{
+            dir=SPLIT_VERTICAL;
+            split->tlpwin->bline=GR_BORDERLINE_BOTTOM;
+            split->brpwin->bline=GR_BORDERLINE_TOP;
+        }
+        split->ssplit.dir=dir;
+    }
+
+    if(dir==SPLIT_VERTICAL){
+        if(ng->h<=tlg.h+brg.h){
+            if(transpose){
+                ntlg.h=minof(tlg.w, ng->h*2/3);
+                nbrg.h=minof(brg.w, ng->h*2/3);
+                adjust_size(&ntlg.h, dir, split, split->ssplit.tl);
+                adjust_size(&nbrg.h, dir, split, split->ssplit.br);
+                adjust=(ng->h>ntlg.h+nbrg.h);
+            }else{
+                ntlg.h=minof(ng->h, tlg.h);
+                nbrg.h=minof(ng->h, brg.h);
+                adjust=FALSE;
+            }
+        }else{
+            ntlg.h=tlg.h;
+            nbrg.h=brg.h;
+        }
+
+        if(adjust){
+            adjust_sizes(&ntlg.h, &nbrg.h, ng->h,
+                         splitfloat_get_min(split, dir, split->ssplit.tl),
+                         splitfloat_get_min(split, dir, split->ssplit.br),
+                         splitfloat_get_max(split, dir, split->ssplit.tl),
+                         splitfloat_get_max(split, dir, split->ssplit.br),
+                         vprimn);
+        }
+        
+        nbrg.y=ng->y+ng->h-nbrg.h;
+    }else{
+        if(ng->w<=tlg.w+brg.w){
+            if(transpose){
+                ntlg.w=minof(tlg.h, ng->w*2/3);
+                nbrg.w=minof(brg.h, ng->w*2/3);
+                adjust_size(&ntlg.w, dir, split, split->ssplit.tl);
+                adjust_size(&nbrg.w, dir, split, split->ssplit.br);
+                adjust=(ng->w>ntlg.w+nbrg.w);
+            }else{
+                ntlg.w=minof(ng->w, tlg.w);
+                nbrg.w=minof(ng->w, brg.w);
+                adjust=FALSE;
+            }
+        }else{
+            ntlg.w=tlg.w;
+            nbrg.w=brg.w;
+        }
+        
+        if(adjust){
+            adjust_sizes(&ntlg.w, &nbrg.w, ng->w,
+                         splitfloat_get_min(split, dir, split->ssplit.tl),
+                         splitfloat_get_min(split, dir, split->ssplit.br),
+                         splitfloat_get_max(split, dir, split->ssplit.tl),
+                         splitfloat_get_max(split, dir, split->ssplit.br),
+                         hprimn);
+        }
+        
+        nbrg.x=ng->x+ng->w-nbrg.w;
+    }
+
+    GEOM(split)=*ng;
+
+    splitfloat_update_handles(split, &ntlg, &nbrg);
+
+    splitfloat_tl_pwin_to_cnt(split, &ntlg);
+    split_do_resize(split->ssplit.tl, &ntlg, hprimn, vprimn, transpose);
+    splitfloat_br_pwin_to_cnt(split, &nbrg);
+    split_do_resize(split->ssplit.br, &nbrg, hprimn, vprimn, transpose);
+}
+
+
+static void calc_amount(int *amount, int *oamount,
+                        int rs, WSplitSplit *p, int omax,
+                        const WRectangle *ng, const WRectangle *og)
+{
+    *oamount=0;
+    
+    if(rs>=0){
+        if(p->dir==SPLIT_VERTICAL)
+            *amount=maxof(0, minof(rs, GEOM(p).h-ng->h));
+        else
+            *amount=maxof(0, minof(rs, GEOM(p).w-ng->w));
+    }else{
+        if(p->dir==SPLIT_VERTICAL){
+            int overlap=maxof(0, og->h-(GEOM(p).h-ng->h));
+            *amount=-minof(-rs, overlap);
+            *oamount=maxof(0, minof(*amount-rs, omax-og->h));
+            *amount-=*oamount;
+        }else{
+            int overlap=maxof(0, og->w-(GEOM(p).w-ng->w));
+            *amount=-minof(-rs, overlap);
+            *oamount=maxof(0, minof(*amount-rs, omax-og->w));
+            *amount-=*oamount;
+        }
+    }
+}
+
+
+static void splitfloat_do_rqsize(WSplitFloat *split, WSplit *node, 
+                                 RootwardAmount *ha, RootwardAmount *va, 
+                                 WRectangle *rg, bool tryonly)
+{
+    int hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
+    WRectangle pg, og, ng, nog, nng;
+    RootwardAmount *ca;
+    WSplit *other;
+    int amount=0, oamount=0, omax;
+    int thisnode;
+    WSplitSplit *p=&(split->ssplit);
+    
+    assert(!ha->any || ha->tl==0);
+    assert(!va->any || va->tl==0);
+    assert(p->tl==node || p->br==node);
+    
+    if(p->tl==node){
+        other=p->br;
+        thisnode=PRIMN_TL;
+    }else{
+        other=p->tl;
+        thisnode=PRIMN_BR;
+    }
+
+    ng=GEOM(node);
+    og=GEOM(other);
+    
+    if(thisnode==PRIMN_TL){
+        splitfloat_tl_cnt_to_pwin(split, &ng);
+        splitfloat_br_cnt_to_pwin(split, &og);
+    }else{
+        splitfloat_br_cnt_to_pwin(split, &ng);
+        splitfloat_tl_cnt_to_pwin(split, &og);
+    }
+
+    ca=(p->dir==SPLIT_VERTICAL ? va : ha);
+    
+    omax=splitfloat_get_max(split, p->dir, other);
+    
+    if(thisnode==PRIMN_TL || ca->any){
+        calc_amount(&amount, &oamount, ca->br, p, omax, &ng, &og);
+        ca->br-=amount;
+    }else/*if(thisnode==PRIMN_BR)*/{
+        calc_amount(&amount, &oamount, ca->tl, p, omax, &ng, &og);
+        ca->tl-=amount;
+    }
+    
+    if(((WSplit*)p)->parent==NULL /*|| 
+       (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){
+        pg=((WSplit*)p)->geom;
+    }else{
+        splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va,
+                             &pg, tryonly);
+    }
+    
+    assert(pg.w>=0 && pg.h>=0);
+
+    nog=pg;
+    nng=pg;
+
+    if(p->dir==SPLIT_VERTICAL){
+        nog.h=minof(pg.h, maxof(0, og.h+oamount));
+        nng.h=minof(pg.h, maxof(0, ng.h+amount+pg.h-GEOM(p).h));
+        if(thisnode==PRIMN_TL)
+            nog.y=pg.y+pg.h-nog.h;
+        else
+            nng.y=pg.y+pg.h-nng.h;
+        vprimn=thisnode;
+    }else{
+        nog.w=minof(pg.w, maxof(0, og.w+oamount));
+        nng.w=minof(pg.w, maxof(0, ng.w+amount+pg.w-GEOM(p).w));
+        if(thisnode==PRIMN_TL)
+            nog.x=pg.x+pg.w-nog.w;
+        else
+            nng.x=pg.x+pg.w-nng.w;
+        hprimn=thisnode;
+    }
+    
+    if(!tryonly){
+        GEOM(p)=pg;
+
+        if(thisnode==PRIMN_TL){
+            splitfloat_update_handles(split, &nng, &nog);
+            splitfloat_br_pwin_to_cnt(split, &nog);
+        }else{
+            splitfloat_update_handles(split, &nog, &nng);
+            splitfloat_tl_pwin_to_cnt(split, &nog);
+        }
+            
+        /* Entä jos 'other' on stdisp? */
+        split_do_resize(other, &nog, hprimn, vprimn, FALSE);
+    }
+    
+    *rg=nng;
+    if(thisnode==PRIMN_TL)
+        splitfloat_tl_pwin_to_cnt(split, rg);
+    else
+        splitfloat_br_pwin_to_cnt(split, rg);
+}
+
+
+void splitfloat_flip(WSplitFloat *split)
+{
+    WRectangle tlg, brg;
+    
+    splitsplit_flip_default(&split->ssplit);
+    
+    tlg=split->ssplit.tl->geom;
+    brg=split->ssplit.br->geom;
+
+    splitfloat_tl_cnt_to_pwin(split, &tlg);
+    splitfloat_br_cnt_to_pwin(split, &brg);
+    splitfloat_update_handles(split, &tlg, &brg);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Loading code */
+
+
+#define MINS 8
+
+static void adjust_tls_brs(int *tls, int *brs, int total)
+{
+    if(*tls<=0)
+        *tls=MINS;
+    if(*brs<=0)
+        *brs=MINS;
+    
+    if(*tls+*brs<total){
+        *tls=total*(*tls)/(*tls+*brs);
+        *brs=total-(*tls);
+    }
+        
+    *tls=minof(maxof(MINS, *tls), total);
+    *brs=minof(maxof(MINS, *brs), total);
+}
+
+
+static void calc_tlg_brg(const WRectangle *geom, int tls, int brs, int dir,
+                         WRectangle *tlg, WRectangle *brg)
+{
+    *tlg=*geom;
+    *brg=*geom;
+    
+    if(dir==SPLIT_HORIZONTAL){
+        adjust_tls_brs(&tls, &brs, geom->w);
+        tlg->w=tls;
+        brg->w=brs;
+        brg->x=geom->x+geom->w-brs;
+    }else{
+        adjust_tls_brs(&tls, &brs, geom->h);
+        tlg->h=tls;
+        brg->h=brs;
+        brg->y=geom->y+geom->h-brs;
+    }
+}
+
+
+WSplit *load_splitfloat(WTiling *ws, const WRectangle *geom, ExtlTab tab)
+{
+    WSplit *tl=NULL, *br=NULL;
+    WSplitFloat *split;
+    char *dir_str;
+    int dir, brs, tls;
+    ExtlTab subtab;
+    WRectangle tlg, brg;
+    int set=0;
+
+    set+=(extl_table_gets_i(tab, "tls", &tls)==TRUE);
+    set+=(extl_table_gets_i(tab, "brs", &brs)==TRUE);
+    set+=(extl_table_gets_s(tab, "dir", &dir_str)==TRUE);
+    
+    if(set!=3)
+        return NULL;
+    
+    if(strcmp(dir_str, "vertical")==0){
+        dir=SPLIT_VERTICAL;
+    }else if(strcmp(dir_str, "horizontal")==0){
+        dir=SPLIT_HORIZONTAL;
+    }else{
+        warn(TR("Invalid direction."));
+        free(dir_str);
+        return NULL;
+    }
+    free(dir_str);
+
+    split=create_splitfloat(geom, ws, dir);
+    if(split==NULL)
+        return NULL;
+
+    if(!extl_table_is_bool_set(tab, "tls_brs_incl_handles")){
+        if(split->ssplit.dir==SPLIT_HORIZONTAL){
+            tls+=split->tlpwin->bdw.right;
+            brs+=split->brpwin->bdw.left;
+        }else{
+            tls+=split->tlpwin->bdw.bottom;
+            brs+=split->brpwin->bdw.top;
+        }
+    }
+                               
+    calc_tlg_brg(geom, tls, brs, dir, &tlg, &brg);
+    
+    splitfloat_update_handles(split, &tlg, &brg);
+    
+    if(extl_table_gets_t(tab, "tl", &subtab)){
+        WRectangle g=tlg;
+        splitfloat_tl_pwin_to_cnt(split, &g);
+        tl=tiling_load_node(ws, &g, subtab);
+        extl_unref_table(subtab);
+    }
+    
+    if(extl_table_gets_t(tab, "br", &subtab)){
+        WRectangle g;
+        if(tl==NULL){
+            g=*geom;
+        }else{
+            g=brg;
+            splitfloat_br_pwin_to_cnt(split, &g);
+        }
+        br=tiling_load_node(ws, &g, subtab);
+        extl_unref_table(subtab);
+    }
+    
+    if(tl==NULL || br==NULL){
+        destroy_obj((Obj*)split);
+        if(tl!=NULL){
+            split_do_resize(tl, geom, PRIMN_ANY, PRIMN_ANY, FALSE);
+            return tl;
+        }
+        if(br!=NULL){
+            split_do_resize(br, geom, PRIMN_ANY, PRIMN_ANY, FALSE);
+            return br;
+        }
+        return NULL;
+    }
+    
+    tl->parent=(WSplitInner*)split;
+    br->parent=(WSplitInner*)split;
+
+    split->ssplit.tl=tl;
+    split->ssplit.br=br;
+    
+    return (WSplit*)split;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Split */
+
+
+WSplitRegion *splittree_split_floating(WSplit *node, int dir, int primn,
+                                       int nmins, WRegionSimpleCreateFn *fn, 
+                                       WTiling *ws)
+{
+    WSplitFloat *sf;
+    int omins, mins;
+    int sn, so, s, rs;
+    int bn, bo;
+    WRectangle gn, go, gnc, goc;
+    WFitParams fp;
+    WRegion *nreg;
+    WSplitRegion *nnode;
+    WSplitInner *psplit;
+    
+    if(primn!=PRIMN_TL && primn!=PRIMN_BR)
+        primn=PRIMN_BR;
+
+    split_update_bounds(split_find_root(node), TRUE);
+    
+    sf=create_splitfloat(&node->geom, ws, dir);
+    
+    if(sf==NULL)
+        return NULL;
+    
+    omins=(dir==SPLIT_VERTICAL ? node->min_h : node->min_w);
+    s=split_size(node, dir);
+    
+    if(primn==PRIMN_BR){
+        bn=BR_BORDER(sf);
+        bo=TL_BORDER(sf);
+    }else{
+        bn=TL_BORDER(sf);
+        bo=BR_BORDER(sf);
+    }
+    
+    mins=maxof(omins+bo, nmins+bn);
+
+    /* Potentially resize old node. */
+    
+    splittree_begin_resize();
+
+    if(mins>s){
+        WRectangle ng=node->geom, rg;
+        if(dir==SPLIT_VERTICAL)
+            ng.h=mins;
+        else
+            ng.w=mins;
+        
+        split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, TRUE);
+        rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w);
+        if(rs<mins){
+            warn(TR("Unable to split: not enough free space."));
+            destroy_obj((Obj*)sf);
+            return NULL;
+        }
+        split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, FALSE);
+        s=split_size(node, dir);
+    }else{
+        splittree_scan_stdisp_rootward(node);
+    }
+    
+    /* Calculate geometries. */
+    
+    sn=maxof(nmins+bn, s/2);
+    so=maxof(omins+bo, s-s/2);
+
+    ((WSplit*)sf)->geom=node->geom;
+    
+    if(primn==PRIMN_TL){
+        calc_tlg_brg(&(node->geom), sn, so, dir, &gn, &go);
+        splitfloat_update_handles(sf, &gn, &go);
+        gnc=gn; splitfloat_tl_pwin_to_cnt(sf, &gnc);
+        goc=go; splitfloat_br_pwin_to_cnt(sf, &goc);
+    }else{
+        calc_tlg_brg(&(node->geom), so, sn, dir, &go, &gn);
+        splitfloat_update_handles(sf, &go, &gn);
+        goc=go; splitfloat_tl_pwin_to_cnt(sf, &goc);
+        gnc=gn; splitfloat_br_pwin_to_cnt(sf, &gnc);
+    }
+
+    /* Create the region. */
+    
+    fp.mode=REGION_FIT_EXACT;
+    fp.g=gnc;
+    
+    nreg=fn(REGION_PARENT(ws), &fp);
+    
+    if(nreg==NULL){
+        destroy_obj((Obj*)sf);
+        return NULL;
+    }
+
+    nnode=create_splitregion(&(fp.g), nreg);
+    if(nnode==NULL){
+        destroy_obj((Obj*)nreg);
+        destroy_obj((Obj*)sf);
+        return NULL;
+    }
+    
+    /* Now that everything's ok, resize and move original node. */    
+    
+    split_do_resize(node, &goc, 
+                    (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY),
+                    (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY),
+                    FALSE);
+
+    /* Set up split structure. */
+    
+    psplit=node->parent;
+    
+    if(psplit!=NULL)
+        splitinner_replace(psplit, node, (WSplit*)sf);
+    else
+        splittree_changeroot(node, (WSplit*)sf);
+
+    node->parent=(WSplitInner*)sf;
+    ((WSplit*)nnode)->parent=(WSplitInner*)sf;
+    
+    if(primn==PRIMN_BR){
+        sf->ssplit.tl=node;
+        sf->ssplit.br=(WSplit*)nnode;
+    }else{
+        sf->ssplit.tl=(WSplit*)nnode;
+        sf->ssplit.br=node;
+    }
+    
+    /*splittree_end_resize();*/
+    
+    return nnode;
+}
+
+
+/*}}}*/
+
+
+/*{{{ The class */
+
+
+static DynFunTab splitfloat_dynfuntab[]={
+    {split_update_bounds, splitfloat_update_bounds},
+    {split_do_resize, splitfloat_do_resize},
+    {splitinner_do_rqsize, splitfloat_do_rqsize},
+    {split_stacking, splitfloat_stacking},
+    {split_restack, splitfloat_restack},
+    {split_reparent, splitfloat_reparent},
+    {split_map, splitfloat_map},
+    {split_unmap, splitfloat_unmap},
+    {splitsplit_flip, splitfloat_flip},
+    END_DYNFUNTAB,
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WSplitFloat, WSplitSplit, splitfloat_deinit, splitfloat_dynfuntab);
+
+
+/*}}}*/
+
diff --git a/mod_tiling/splitfloat.h b/mod_tiling/splitfloat.h
new file mode 100644 (file)
index 0000000..9bb428e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * ion/panews/splitfloat.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_PANEWS_SPLITFLOAT_H
+#define ION_PANEWS_SPLITFLOAT_H
+
+#include <ioncore/common.h>
+#include <ioncore/gr.h>
+#include "split.h"
+#include "tiling.h"
+
+INTRCLASS(WSplitFloat);
+
+#include "panehandle.h"
+
+DECLCLASS(WSplitFloat){
+    WSplitSplit ssplit;
+    WPaneHandle *tlpwin, *brpwin;
+};
+
+
+extern bool splitfloat_init(WSplitFloat *split, const WRectangle *geom,
+                            WTiling *ws, int dir);
+
+extern WSplitFloat *create_splitfloat(const WRectangle *geom, 
+                                      WTiling *ws, int dir);
+
+extern void splitfloat_deinit(WSplitFloat *split);
+
+extern void splitfloat_update_handles(WSplitFloat *split, 
+                                      const WRectangle *tlg,
+                                      const WRectangle *brg);
+extern void splitfloat_tl_pwin_to_cnt(WSplitFloat *split, WRectangle *g);
+extern void splitfloat_br_pwin_to_cnt(WSplitFloat *split, WRectangle *g);
+extern void splitfloat_tl_cnt_to_pwin(WSplitFloat *split, WRectangle *g);
+extern void splitfloat_br_cnt_to_pwin(WSplitFloat *split, WRectangle *g);
+
+extern void splitfloat_flip(WSplitFloat *split);
+
+extern WSplit *load_splitfloat(WTiling *ws, const WRectangle *geom, 
+                               ExtlTab tab);
+
+extern WSplitRegion *splittree_split_floating(WSplit *node, int dir, 
+                                              int primn, int nmins, 
+                                              WRegionSimpleCreateFn *fn, 
+                                              WTiling *ws);
+
+#endif /* ION_PANEWS_SPLITFLOAT_H */
diff --git a/mod_tiling/tiling.c b/mod_tiling/tiling.c
new file mode 100644 (file)
index 0000000..2ab56b0
--- /dev/null
@@ -0,0 +1,1764 @@
+/*
+ * ion/mod_tiling/tiling.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+
+#include <libtu/objp.h>
+#include <libtu/minmax.h>
+#include <libtu/ptrlist.h>
+#include <libmainloop/defer.h>
+#include <libmainloop/signal.h>
+
+#include <ioncore/common.h>
+#include <ioncore/rootwin.h>
+#include <ioncore/focus.h>
+#include <ioncore/global.h>
+#include <ioncore/region.h>
+#include <ioncore/manage.h>
+#include <ioncore/screen.h>
+#include <ioncore/names.h>
+#include <ioncore/saveload.h>
+#include <ioncore/attach.h>
+#include <ioncore/resize.h>
+#include <libextl/extl.h>
+#include <ioncore/regbind.h>
+#include <ioncore/extlconv.h>
+#include <ioncore/xwindow.h>
+#include <ioncore/navi.h>
+#include "placement.h"
+#include "tiling.h"
+#include "split.h"
+#include "splitfloat.h"
+#include "split-stdisp.h"
+#include "main.h"
+
+
+
+static WTilingIterTmp tiling_iter_default_tmp;
+
+
+/*{{{ Some helper routines */
+
+
+#define STDISP_OF(WS) \
+     ((WS)->stdispnode!=NULL ? (WS)->stdispnode->regnode.reg : NULL)
+
+
+static WSplitRegion *get_node_check(WTiling *ws, WRegion *reg)
+{
+    WSplitRegion *node;
+
+    if(reg==NULL)
+        return NULL;
+    
+    node=splittree_node_of(reg);
+    
+    if(node==NULL || REGION_MANAGER(reg)!=(WRegion*)ws)
+        return NULL;
+    
+    return node;
+}
+
+
+static bool check_node(WTiling *ws, WSplit *split)
+{
+    if(split->parent)
+        return check_node(ws, (WSplit*)split->parent);
+    
+    if((split->ws_if_root!=(void*)ws)){
+        warn(TR("Split not on workspace."));
+        return FALSE;
+    }
+    return TRUE;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynfun implementations */
+
+
+static void reparent_mgd(WRegion *sub, WWindow *par)
+{
+    WFitParams subfp;
+    subfp.g=REGION_GEOM(sub);
+    subfp.mode=REGION_FIT_EXACT;
+    if(!region_fitrep(sub, par, &subfp)){
+        warn(TR("Error reparenting %s."), region_name(sub));
+        region_detach_manager(sub);
+    }
+}
+
+
+bool tiling_fitrep(WTiling *ws, WWindow *par, const WFitParams *fp)
+{
+    WTilingIterTmp tmp;
+    bool ok=FALSE;
+    
+    if(par!=NULL){
+        if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
+            return FALSE;
+    
+        region_unset_parent((WRegion*)ws);
+        
+        XReparentWindow(ioncore_g.dpy, ws->dummywin, 
+                        par->win, fp->g.x, fp->g.y);
+        
+        region_set_parent((WRegion*)ws, par);
+    
+        if(ws->split_tree!=NULL)
+            split_reparent(ws->split_tree, par);
+    }
+    
+    REGION_GEOM(ws)=fp->g;
+    
+    if(ws->split_tree!=NULL){
+        bool done=FALSE;
+        if(fp->mode&REGION_FIT_ROTATE)
+            ok=split_rotate_to(ws->split_tree, &(fp->g), fp->rotation);
+        if(!ok)
+            split_resize(ws->split_tree, &(fp->g), PRIMN_ANY, PRIMN_ANY);
+    }
+    
+    return TRUE;
+}
+
+
+void tiling_managed_rqgeom(WTiling *ws, WRegion *mgd, 
+                           const WRQGeomParams *rq,
+                           WRectangle *geomret)
+{
+    WSplitRegion *node=get_node_check(ws, mgd);
+    if(node!=NULL && ws->split_tree!=NULL)
+        splittree_rqgeom((WSplit*)node, rq->flags, &rq->geom, geomret);
+}
+
+
+void tiling_map(WTiling *ws)
+{
+    REGION_MARK_MAPPED(ws);
+    XMapWindow(ioncore_g.dpy, ws->dummywin);
+    
+    if(ws->split_tree!=NULL)
+        split_map(ws->split_tree);
+}
+
+
+void tiling_unmap(WTiling *ws)
+{
+    REGION_MARK_UNMAPPED(ws);
+    XUnmapWindow(ioncore_g.dpy, ws->dummywin);
+
+    if(ws->split_tree!=NULL)
+        split_unmap(ws->split_tree);
+}
+
+
+void tiling_fallback_focus(WTiling *ws, bool warp)
+{
+    region_finalise_focusing((WRegion*)ws, ws->dummywin, warp);
+}
+
+
+void tiling_do_set_focus(WTiling *ws, bool warp)
+{
+    WRegion *sub=tiling_current(ws);
+    
+    if(sub==NULL){
+        tiling_fallback_focus(ws, warp);
+        return;
+    }
+
+    region_do_set_focus(sub, warp);
+}
+
+
+static WTimer *restack_timer=NULL;
+
+
+static void restack_handler(WTimer *tmr, Obj *obj)
+{
+    if(obj!=NULL){
+        WTiling *ws=(WTiling*)obj;
+        split_restack(ws->split_tree, ws->dummywin, Above);
+    }
+}
+
+
+bool tiling_managed_prepare_focus(WTiling *ws, WRegion *reg, 
+                                 int flags, WPrepareFocusResult *res)
+{
+    WSplitRegion *node; 
+
+    if(!region_prepare_focus((WRegion*)ws, flags, res))
+        return FALSE;
+    
+    node=get_node_check(ws, reg);
+    
+    if(node!=NULL && node->split.parent!=NULL)
+        splitinner_mark_current(node->split.parent, &(node->split));
+        
+    /* WSplitSplit uses activity based stacking as required on WAutoWS,
+     * so we must restack here.
+     */
+    if(ws->split_tree!=NULL){
+        int rd=mod_tiling_raise_delay;
+        bool use_timer=rd>0 && flags&REGION_GOTO_ENTERWINDOW;
+        
+        if(use_timer){
+            if(restack_timer!=NULL){
+                Obj *obj=restack_timer->objwatch.obj;
+                if(obj!=(Obj*)ws){
+                    timer_reset(restack_timer);
+                    restack_handler(restack_timer, obj);
+                }
+            }else{
+                restack_timer=create_timer();
+            }
+        }
+        
+        if(use_timer && restack_timer!=NULL){
+            timer_set(restack_timer, rd, restack_handler, (Obj*)ws);
+        }else{
+            split_restack(ws->split_tree, ws->dummywin, Above);
+        }
+    }
+
+    res->reg=reg;
+    res->flags=flags;
+    return TRUE;
+}
+
+
+
+void tiling_restack(WTiling *ws, Window other, int mode)
+{
+    xwindow_restack(ws->dummywin, other, mode);
+    if(ws->split_tree!=NULL)
+        split_restack(ws->split_tree, ws->dummywin, Above);
+}
+
+
+void tiling_stacking(WTiling *ws, Window *bottomret, Window *topret)
+{
+    Window sbottom=None, stop=None;
+    
+    if(ws->split_tree!=None)
+        split_stacking(ws->split_tree, &sbottom, &stop);
+    
+    *bottomret=ws->dummywin;
+    *topret=(stop!=None ? stop : ws->dummywin);
+}
+
+
+Window tiling_xwindow(const WTiling *ws)
+{
+    return ws->dummywin;
+}
+
+
+/*
+WRegion *tiling_rqclose_propagate(WTiling *ws, WRegion *maybe_sub)
+{
+    return (region_rqclose((WRegion*)ws, FALSE) ? (WRegion*)ws : NULL);
+}
+*/
+
+WPHolder *tiling_prepare_manage_transient(WTiling *ws,
+                                          const WClientWin *transient,
+                                          const WManageParams *param,
+                                          int unused)
+{
+    /* Transient manager searches should not cross tilings unless
+     * explicitly floated.
+     */
+    if(extl_table_is_bool_set(transient->proptab, "float")){
+        return region_prepare_manage_transient_default((WRegion*)ws,
+                                                       transient,
+                                                       param,
+                                                       unused);
+    }else{
+         return NULL;
+    }
+}
+    
+
+/*}}}*/
+
+
+/*{{{ Status display support code */
+
+
+static bool regnodefilter(WSplit *split)
+{
+    return OBJ_IS(split, WSplitRegion);
+}
+
+
+void tiling_unmanage_stdisp(WTiling *ws, bool permanent, bool nofocus)
+{
+    WSplitRegion *tofocus=NULL;
+    bool setfocus=FALSE;
+    WRegion *od;
+    
+    if(ws->stdispnode==NULL)
+        return;
+    
+    od=ws->stdispnode->regnode.reg;
+    
+    if(od!=NULL){
+        if(!nofocus && REGION_IS_ACTIVE(od) && 
+           region_may_control_focus((WRegion*)ws)){
+            setfocus=TRUE;
+            tofocus=(WSplitRegion*)split_nextto((WSplit*)(ws->stdispnode), 
+                                                PRIMN_ANY, PRIMN_ANY,
+                                                regnodefilter);
+        }
+        /* Reset node_of info here so tiling_managed_remove will not
+         * remove the node.
+         */
+        splittree_set_node_of(od, NULL);
+        tiling_do_managed_remove(ws, od);
+    }
+    
+    if(permanent){
+        WSplit *node=(WSplit*)ws->stdispnode;
+        ws->stdispnode=NULL;
+        splittree_remove(node, TRUE);
+    }
+    
+    if(setfocus){
+        if(tofocus!=NULL)
+            region_set_focus(tofocus->reg);
+        else
+            tiling_fallback_focus(ws, FALSE);
+    }
+}
+
+
+static void tiling_create_stdispnode(WTiling *ws, WRegion *stdisp, 
+                                    int corner, int orientation, 
+                                    bool fullsize)
+{
+    int flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
+    WRectangle *wg=&REGION_GEOM(ws), dg;
+    WSplitST *stdispnode;
+    WSplitSplit *split;
+
+    assert(ws->split_tree!=NULL);
+    
+    if(orientation==REGION_ORIENTATION_HORIZONTAL){
+        dg.x=wg->x;
+        dg.w=wg->w;
+        dg.h=0;
+        dg.y=((corner==MPLEX_STDISP_BL || corner==MPLEX_STDISP_BR)
+              ? wg->y+wg->h
+              : 0);
+    }else{
+        dg.y=wg->y;
+        dg.h=wg->h;
+        dg.w=0;
+        dg.x=((corner==MPLEX_STDISP_TR || corner==MPLEX_STDISP_BR)
+              ? wg->x+wg->w
+              : 0);
+    }
+            
+    stdispnode=create_splitst(&dg, stdisp);
+    
+    if(stdispnode==NULL){
+        warn(TR("Unable to create a node for status display."));
+        return;
+    }
+    
+    stdispnode->corner=corner;
+    stdispnode->orientation=orientation;
+    stdispnode->fullsize=fullsize;
+    
+    split=create_splitsplit(wg, (orientation==REGION_ORIENTATION_HORIZONTAL 
+                                 ? SPLIT_VERTICAL
+                                 : SPLIT_HORIZONTAL));
+
+    if(split==NULL){
+        warn(TR("Unable to create new split for status display."));
+        stdispnode->regnode.reg=NULL;
+        destroy_obj((Obj*)stdispnode);
+        return;
+    }
+
+    /* Set up new split tree */
+    ((WSplit*)stdispnode)->parent=(WSplitInner*)split;
+    ws->split_tree->parent=(WSplitInner*)split;
+    ws->split_tree->ws_if_root=NULL;
+    
+    if((orientation==REGION_ORIENTATION_HORIZONTAL && 
+        (corner==MPLEX_STDISP_BL || corner==MPLEX_STDISP_BR)) ||
+       (orientation==REGION_ORIENTATION_VERTICAL && 
+        (corner==MPLEX_STDISP_TR || corner==MPLEX_STDISP_BR))){
+        split->tl=ws->split_tree;
+        split->br=(WSplit*)stdispnode;
+        split->current=SPLIT_CURRENT_TL;
+    }else{
+        split->tl=(WSplit*)stdispnode;
+        split->br=ws->split_tree;
+        split->current=SPLIT_CURRENT_BR;
+    }
+
+    ws->split_tree=(WSplit*)split;
+    ((WSplit*)split)->ws_if_root=ws;
+    ws->stdispnode=stdispnode;
+}
+
+
+void tiling_manage_stdisp(WTiling *ws, WRegion *stdisp, 
+                         const WMPlexSTDispInfo *di)
+{
+    bool mcf=region_may_control_focus((WRegion*)ws);
+    int flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
+    int orientation=region_orientation(stdisp);
+    bool act=FALSE;
+    WRectangle dg, *stdg;
+    
+    if(orientation!=REGION_ORIENTATION_VERTICAL /*&&
+       orientation!=REGION_ORIENTATION_HORIZONTAL*/){
+        orientation=REGION_ORIENTATION_HORIZONTAL;
+    }
+    
+    if(ws->stdispnode==NULL || ws->stdispnode->regnode.reg!=stdisp)
+        region_detach_manager(stdisp);
+
+    /* Remove old stdisp if corner and orientation don't match.
+     */
+    if(ws->stdispnode!=NULL && (di->pos!=ws->stdispnode->corner ||
+                                orientation!=ws->stdispnode->orientation)){
+        tiling_unmanage_stdisp(ws, TRUE, TRUE);
+    }
+
+    if(ws->stdispnode==NULL){
+        tiling_create_stdispnode(ws, stdisp, di->pos, orientation, 
+                                di->fullsize);
+        if(ws->stdispnode==NULL)
+            return;
+    }else{
+        WRegion *od=ws->stdispnode->regnode.reg;
+        if(od!=NULL){
+            act=REGION_IS_ACTIVE(od);
+            splittree_set_node_of(od, NULL);
+            tiling_managed_remove(ws, od);
+            assert(ws->stdispnode->regnode.reg==NULL);
+        }
+        
+        ws->stdispnode->fullsize=di->fullsize;
+        ws->stdispnode->regnode.reg=stdisp;
+        splittree_set_node_of(stdisp, &(ws->stdispnode->regnode));
+    }
+    
+    if(!tiling_managed_add(ws, stdisp)){
+        tiling_unmanage_stdisp(ws, TRUE, TRUE);
+        return;
+    }
+    
+    dg=((WSplit*)(ws->stdispnode))->geom;
+    
+    dg.h=stdisp_recommended_h(ws->stdispnode);
+    dg.w=stdisp_recommended_w(ws->stdispnode);
+    
+    splittree_rqgeom((WSplit*)(ws->stdispnode), flags, &dg, FALSE);
+    
+    stdg=&(((WSplit*)ws->stdispnode)->geom);
+    
+    if(stdisp->geom.x!=stdg->x || stdisp->geom.y!=stdg->y ||
+       stdisp->geom.w!=stdg->w || stdisp->geom.h!=stdg->h){
+        region_fit(stdisp, stdg, REGION_FIT_EXACT);
+    }
+
+    /* Restack to ensure the split tree is stacked in the expected order. */
+    if(ws->split_tree!=NULL)
+        split_restack(ws->split_tree, ws->dummywin, Above);
+    
+    if(mcf && act)
+        region_set_focus(stdisp);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Create/destroy */
+
+
+bool tiling_managed_add_default(WTiling *ws, WRegion *reg)
+{
+    Window bottom=None, top=None;
+    WFrame *frame;
+    
+    if(STDISP_OF(ws)!=reg){
+        if(!ptrlist_insert_last(&(ws->managed_list), reg))
+            return FALSE;
+    }
+    
+    region_set_manager(reg, (WRegion*)ws);
+    
+    frame=OBJ_CAST(reg, WFrame);
+    if(frame!=NULL){
+        WFrameMode mode=frame_mode(frame);
+        if(mode!=FRAME_MODE_TILED && mode!=FRAME_MODE_TILED_ALT)
+            frame_set_mode(frame, FRAME_MODE_TILED);
+    }
+    
+    if(REGION_IS_MAPPED(ws))
+        region_map(reg);
+    
+    if(region_may_control_focus((WRegion*)ws)){
+        WRegion *curr=tiling_current(ws);
+        if(curr==NULL || !REGION_IS_ACTIVE(curr))
+            region_warp(reg);
+    }
+    
+    return TRUE;
+}
+
+
+bool tiling_managed_add(WTiling *ws, WRegion *reg)
+{
+    bool ret=FALSE;
+    CALL_DYN_RET(ret, bool, tiling_managed_add, ws, (ws, reg));
+    return ret;
+}
+
+
+static WRegion *create_initial_frame(WTiling *ws, WWindow *parent,
+                                     const WFitParams *fp)
+{
+    WRegion *reg=ws->create_frame_fn(parent, fp);
+
+    if(reg==NULL)
+        return NULL;
+    
+    ws->split_tree=(WSplit*)create_splitregion(&(fp->g), reg);
+    if(ws->split_tree==NULL){
+        destroy_obj((Obj*)reg);
+        return NULL;
+    }
+    ws->split_tree->ws_if_root=ws;
+    
+    if(!tiling_managed_add(ws, reg)){
+        destroy_obj((Obj*)reg);
+        destroy_obj((Obj*)ws->split_tree);
+        return NULL;
+    }
+
+    return reg;
+}
+
+
+static WRegion *create_frame_tiling(WWindow *parent, const WFitParams *fp)
+{
+    return (WRegion*)create_frame(parent, fp, FRAME_MODE_TILED);
+}
+
+
+bool tiling_init(WTiling *ws, WWindow *parent, const WFitParams *fp, 
+                WRegionSimpleCreateFn *create_frame_fn, bool ci)
+{
+    ws->split_tree=NULL;
+    ws->create_frame_fn=(create_frame_fn 
+                         ? create_frame_fn
+                         : create_frame_tiling);
+    ws->stdispnode=NULL;
+    ws->managed_list=NULL;
+
+    ws->dummywin=XCreateWindow(ioncore_g.dpy, parent->win,
+                                fp->g.x, fp->g.y, 1, 1, 0,
+                                CopyFromParent, InputOnly,
+                                CopyFromParent, 0, NULL);
+    if(ws->dummywin==None)
+        return FALSE;
+
+    region_init(&(ws->reg), parent, fp);
+
+    ws->reg.flags|=(REGION_GRAB_ON_PARENT|
+                    REGION_PLEASE_WARP);
+    
+    if(ci){
+        if(create_initial_frame(ws, parent, fp)==NULL){
+            XDestroyWindow(ioncore_g.dpy, ws->dummywin);
+            return FALSE;
+        }
+    }
+    
+    XSelectInput(ioncore_g.dpy, ws->dummywin,
+                 FocusChangeMask|KeyPressMask|KeyReleaseMask|
+                 ButtonPressMask|ButtonReleaseMask);
+    XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
+                 (XPointer)ws);
+    
+    region_register(&(ws->reg));
+    region_add_bindmap((WRegion*)ws, mod_tiling_tiling_bindmap);
+    
+    return TRUE;
+}
+
+
+WTiling *create_tiling(WWindow *parent, const WFitParams *fp, 
+                     WRegionSimpleCreateFn *create_frame_fn, bool ci)
+{
+    CREATEOBJ_IMPL(WTiling, tiling, (p, parent, fp, create_frame_fn, ci));
+}
+
+
+WTiling *create_tiling_simple(WWindow *parent, const WFitParams *fp)
+{
+    return create_tiling(parent, fp, NULL, TRUE);
+}
+
+
+void tiling_deinit(WTiling *ws)
+{
+    WRegion *reg;
+    WTilingIterTmp tmp;
+    WMPlex *remanage_mplex=NULL;
+    
+    tiling_unmanage_stdisp(ws, FALSE, TRUE);
+
+    FOR_ALL_MANAGED_BY_TILING(reg, ws, tmp){
+        destroy_obj((Obj*)reg);
+    }
+
+    FOR_ALL_MANAGED_BY_TILING(reg, ws, tmp){
+        assert(FALSE);
+    }
+    
+    if(ws->split_tree!=NULL)
+        destroy_obj((Obj*)(ws->split_tree));
+
+    XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
+    XDestroyWindow(ioncore_g.dpy, ws->dummywin);
+    ws->dummywin=None;
+
+    region_deinit(&(ws->reg));
+}
+
+
+bool tiling_managed_may_destroy(WTiling *ws, WRegion *reg)
+{
+    WTilingIterTmp tmp;
+    WRegion *mgd;
+
+    FOR_ALL_MANAGED_BY_TILING(mgd, ws, tmp){
+        if(mgd!=STDISP_OF(ws) && mgd!=reg){
+            return TRUE;
+        }
+    }
+    
+    return region_manager_allows_destroying((WRegion*)ws);
+}
+
+
+bool tiling_may_destroy(WTiling *ws, WRegion *reg)
+{
+    WTilingIterTmp tmp;
+    WRegion *mgd;
+    
+    FOR_ALL_MANAGED_BY_TILING(mgd, ws, tmp){
+        if(mgd!=STDISP_OF(ws)){
+            warn(TR("Workspace not empty - refusing to destroy."));
+            return FALSE;
+        }
+    }
+    
+    return TRUE;
+}
+
+
+bool tiling_rescue_clientwins(WTiling *ws, WPHolder *ph)
+{
+    WTilingIterTmp tmp;
+    
+    ptrlist_iter_init(&tmp, ws->managed_list);
+    
+    return region_rescue_some_clientwins((WRegion*)ws, ph,
+                                         (WRegionIterator*)ptrlist_iter, 
+                                         &tmp);
+}
+
+
+void tiling_do_managed_remove(WTiling *ws, WRegion *reg)
+{
+    if(STDISP_OF(ws)==reg){
+        ws->stdispnode->regnode.reg=NULL;
+    }else{
+        ptrlist_remove(&(ws->managed_list), reg);
+    }
+
+    region_unset_manager(reg, (WRegion*)ws);
+}
+
+
+static bool nostdispfilter(WSplit *node)
+{
+    return (OBJ_IS(node, WSplitRegion) && !OBJ_IS(node, WSplitST));
+}
+
+
+void tiling_managed_remove(WTiling *ws, WRegion *reg)
+{
+    bool ds=OBJ_IS_BEING_DESTROYED(ws);
+    bool act=REGION_IS_ACTIVE(reg);
+    bool mcf=region_may_control_focus((WRegion*)ws);
+    WSplitRegion *node=get_node_check(ws, reg);
+    WRegion *other;
+
+    other=tiling_do_navi_next(ws, reg, REGION_NAVI_ANY, TRUE, FALSE);
+    
+    tiling_do_managed_remove(ws, reg);
+
+    if(node==(WSplitRegion*)(ws->stdispnode))
+        ws->stdispnode=NULL;
+    
+    if(node==NULL)
+        return;
+    
+    splittree_remove((WSplit*)node, (!ds && other!=NULL));
+    
+    if(!ds){
+        if(other==NULL)
+            mainloop_defer_destroy((Obj*)ws);
+        else if(act && mcf)
+            region_warp(other);
+    }
+}
+
+
+static bool mplexfilter(WSplit *node)
+{
+    WSplitRegion *regnode=OBJ_CAST(node, WSplitRegion);
+    
+    return (regnode!=NULL && regnode->reg!=NULL &&
+            OBJ_IS(regnode->reg, WMPlex));
+}
+
+
+static WPHolder *find_ph_result=NULL;
+static WRegion *find_ph_param=NULL;
+
+
+static bool find_ph(WSplit *split)
+{
+    WSplitRegion *sr=OBJ_CAST(split, WSplitRegion);
+
+    assert(find_ph_result==NULL);
+    
+    if(sr==NULL || sr->reg==NULL)
+        return FALSE;
+    
+    find_ph_result=region_get_rescue_pholder_for(sr->reg, find_ph_param);
+
+    return (find_ph_result!=NULL);
+}
+
+
+WPHolder *tiling_get_rescue_pholder_for(WTiling *ws, WRegion *mgd)
+{
+    WSplit *node=(WSplit*)get_node_check(ws, mgd);
+    WPHolder *ph;
+    
+    find_ph_result=NULL;
+    find_ph_param=mgd;
+    
+    if(node==NULL){
+        if(ws->split_tree!=NULL){
+            split_current_todir(ws->split_tree, PRIMN_ANY, PRIMN_ANY, 
+                                find_ph);
+        }
+    }else{
+        while(node!=NULL){
+            split_nextto(node, PRIMN_ANY, PRIMN_ANY, find_ph);
+            if(find_ph_result!=NULL)
+                break;
+            node=(WSplit*)node->parent;
+        }
+    }
+    
+    ph=find_ph_result;
+    find_ph_result=NULL;
+    find_ph_param=NULL;
+     
+    return ph;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Navigation */
+
+
+static void navi_to_primn(WRegionNavi nh, WPrimn *hprimn, WPrimn *vprimn, 
+                          WPrimn choice)
+{
+    /* choice should be PRIMN_ANY or PRIMN_NONE */
+    
+    switch(nh){
+    case REGION_NAVI_BEG:
+        *vprimn=PRIMN_TL;
+        *hprimn=PRIMN_TL;
+        break;
+        
+    case REGION_NAVI_END:
+        *vprimn=PRIMN_BR;
+        *hprimn=PRIMN_BR;
+        break;
+        
+    case REGION_NAVI_LEFT:
+        *hprimn=PRIMN_TL;
+        *vprimn=choice;
+        break;
+        
+    case REGION_NAVI_RIGHT:
+        *hprimn=PRIMN_BR;
+        *vprimn=choice;
+        break;
+        
+    case REGION_NAVI_TOP:
+        *hprimn=choice;
+        *vprimn=PRIMN_TL;
+        break;
+        
+    case REGION_NAVI_BOTTOM:
+        *hprimn=choice;
+        *vprimn=PRIMN_BR;
+        break;
+        
+    default:
+    case REGION_NAVI_ANY:
+        *hprimn=PRIMN_ANY;
+        *vprimn=PRIMN_ANY;
+        break;
+    }
+}
+
+
+static WRegion *node_reg(WSplit *node)
+{
+    WSplitRegion *rnode=OBJ_CAST(node, WSplitRegion);
+    return (rnode!=NULL ? rnode->reg : NULL);
+}
+
+    
+WRegion *tiling_do_navi_next(WTiling *ws, WRegion *reg, 
+                             WRegionNavi nh, bool nowrap,
+                             bool any)
+{
+    WSplitFilter *filter=(any ? NULL : nostdispfilter);
+    WPrimn hprimn, vprimn;
+    WRegion *nxt=NULL;
+
+    navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
+    
+    if(reg==NULL)
+        reg=tiling_current(ws);
+    
+    if(reg!=NULL){
+        WSplitRegion *node=get_node_check(ws, reg);
+        if(node!=NULL){
+            nxt=node_reg(split_nextto((WSplit*)node, hprimn, vprimn, 
+                                      filter));
+        }
+    }
+    
+    if(nxt==NULL && !nowrap){
+        nxt=node_reg(split_current_todir(ws->split_tree, 
+                                         primn_none2any(primn_invert(hprimn)),
+                                         primn_none2any(primn_invert(vprimn)),
+                                         filter));
+    }
+    
+    return nxt;
+}
+
+
+WRegion *tiling_do_navi_first(WTiling *ws, WRegionNavi nh, bool any)
+{
+    WSplitFilter *filter=(any ? NULL : nostdispfilter);
+    WPrimn hprimn, vprimn;
+    
+    navi_to_primn(nh, &hprimn, &vprimn, PRIMN_ANY);
+
+    return node_reg(split_current_todir(ws->split_tree, 
+                                        hprimn, vprimn, filter));
+}
+
+
+WRegion *tiling_navi_next(WTiling *ws, WRegion *reg, 
+                          WRegionNavi nh, WRegionNaviData *data)
+{
+    WRegion *nxt=tiling_do_navi_next(ws, reg, nh, TRUE, FALSE);
+    
+    return region_navi_cont(&ws->reg, nxt, data);
+}
+
+
+WRegion *tiling_navi_first(WTiling *ws, WRegionNavi nh,
+                           WRegionNaviData *data)
+{
+    WRegion *reg=tiling_do_navi_first(ws, nh, FALSE);
+    
+    return region_navi_cont(&ws->reg, reg, data);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Split/unsplit */
+
+
+static bool get_split_dir_primn(const char *str, int *dir, int *primn)
+{
+    WPrimn hprimn, vprimn;
+    WRegionNavi nh;
+
+    if(!ioncore_string_to_navi(str, &nh))
+        return FALSE;
+    
+    navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
+    
+    if(hprimn==PRIMN_NONE){
+        *dir=SPLIT_VERTICAL;
+        *primn=vprimn;
+    }else if(vprimn==PRIMN_NONE){
+        *dir=SPLIT_HORIZONTAL;
+        *primn=hprimn;
+    }else{
+        warn(TR("Invalid direction"));
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+static bool get_split_dir_primn_float(const char *str, int *dir, int *primn,
+                                      bool *floating)
+{
+    if(strncmp(str, "floating:", 9)==0){
+        *floating=TRUE;
+        return get_split_dir_primn(str+9, dir, primn);
+    }else{
+        *floating=FALSE;
+        return get_split_dir_primn(str, dir, primn);
+    }
+}
+
+
+#define SPLIT_MINS 16 /* totally arbitrary */
+
+
+static WFrame *tiling_do_split(WTiling *ws, WSplit *node, 
+                              const char *dirstr, int minw, int minh)
+{
+    int dir, primn, mins;
+    bool floating=FALSE;
+    WFrame *newframe;
+    WSplitRegion *nnode;
+    
+    if(node==NULL || ws->split_tree==NULL){
+        warn(TR("Invalid node."));
+        return NULL;
+    }
+    
+    if(!get_split_dir_primn_float(dirstr, &dir, &primn, &floating))
+        return NULL;
+    
+    mins=(dir==SPLIT_VERTICAL ? minh : minw);
+
+    if(!floating){
+        nnode=splittree_split(node, dir, primn, mins, 
+                              ws->create_frame_fn, 
+                              REGION_PARENT(ws));
+    }else{
+        nnode=splittree_split_floating(node, dir, primn, mins,
+                                       ws->create_frame_fn, ws);
+    }
+    
+    if(nnode==NULL){
+        warn(TR("Unable to split."));
+        return NULL;
+    }
+
+    /* We must restack here to ensure the split tree is stacked in the
+     * expected order.
+     */
+    if(ws->split_tree!=NULL)
+        split_restack(ws->split_tree, ws->dummywin, Above);
+
+    newframe=OBJ_CAST(nnode->reg, WFrame);
+    assert(newframe!=NULL);
+
+    if(!tiling_managed_add(ws, nnode->reg)){
+        nnode->reg=NULL;
+        destroy_obj((Obj*)nnode);
+        destroy_obj((Obj*)newframe);
+        return NULL;
+    }
+
+    /* Restack */
+    if(ws->split_tree!=NULL)
+        split_restack(ws->split_tree, ws->dummywin, Above);
+    
+    return newframe;
+}
+
+
+/*EXTL_DOC
+ * Create a new frame on \var{ws} above/below/left of/right of
+ * \var{node} as indicated by \var{dirstr}. If \var{dirstr} is 
+ * prefixed with ''floating:'' a floating split is created.
+ */
+EXTL_EXPORT_MEMBER
+WFrame *tiling_split(WTiling *ws, WSplit *node, const char *dirstr)
+{
+    if(!check_node(ws, node))
+        return NULL;
+    
+    return tiling_do_split(ws, node, dirstr,
+                          SPLIT_MINS, SPLIT_MINS);
+}
+
+
+/*EXTL_DOC
+ * Same as \fnref{WTiling.split} at the root of the split tree.
+ */
+EXTL_EXPORT_MEMBER
+WFrame *tiling_split_top(WTiling *ws, const char *dirstr)
+{
+    return tiling_do_split(ws, ws->split_tree, dirstr, 
+                          SPLIT_MINS, SPLIT_MINS);
+}
+
+
+/*EXTL_DOC
+ * Split \var{frame} creating a new frame to direction \var{dirstr}
+ * (one of ''left'', ''right'', ''top'' or ''bottom'') of \var{frame}.
+ * If \var{attach_current} is set, the region currently displayed in
+ * \var{frame}, if any, is moved to thenew frame.
+ * If \var{dirstr} is prefixed with ''floating:'' a floating split is
+ * created.
+ */
+EXTL_EXPORT_MEMBER
+WFrame *tiling_split_at(WTiling *ws, WFrame *frame, const char *dirstr, 
+                       bool attach_current)
+{
+    WRegion *curr;
+    WSplitRegion *node;
+    WFrame *newframe;
+    
+    if(frame==NULL)
+        return NULL;
+    
+    node=get_node_check(ws, (WRegion*)frame);
+
+    newframe=tiling_do_split(ws, (WSplit*)node, dirstr, 
+                            region_min_w((WRegion*)frame),
+                            region_min_h((WRegion*)frame));
+
+    if(newframe==NULL)
+        return NULL;
+
+    curr=mplex_mx_current(&(frame->mplex));
+    
+    if(attach_current && curr!=NULL)
+        mplex_attach_simple(&(newframe->mplex), curr, MPLEX_ATTACH_SWITCHTO);
+    
+    if(region_may_control_focus((WRegion*)frame))
+        region_goto((WRegion*)newframe);
+
+    return newframe;
+}
+
+
+/*EXTL_DOC
+ * Try to relocate regions managed by \var{frame} to another frame
+ * and, if possible, destroy the frame.
+ */
+EXTL_EXPORT_MEMBER
+bool tiling_unsplit_at(WTiling *ws, WFrame *frame)
+{
+    if(frame==NULL){
+        warn(TR("Nil frame."));
+        return FALSE;
+    }
+    
+    if(REGION_MANAGER(frame)!=(WRegion*)ws){
+        warn(TR("The frame is not managed by the workspace."));
+        return FALSE;
+    }
+    
+    return region_rqclose((WRegion*)frame, TRUE);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Navigation etc. exports */
+
+
+WRegion *tiling_current(WTiling *ws)
+{
+    WSplitRegion *node=NULL;
+    if(ws->split_tree!=NULL){
+        node=(WSplitRegion*)split_current_todir(ws->split_tree, 
+                                                PRIMN_ANY, PRIMN_ANY, NULL);
+    }
+    return (node ? node->reg : NULL);
+}
+
+
+/*EXTL_DOC
+ * Returns a list of regions managed by the workspace (frames, mostly).
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+ExtlTab tiling_managed_list(WTiling *ws)
+{
+    PtrListIterTmp tmp;
+    
+    ptrlist_iter_init(&tmp, ws->managed_list);
+    
+    return extl_obj_iterable_to_table((ObjIterator*)ptrlist_iter, &tmp);
+}
+
+
+/*EXTL_DOC
+ * Returns the root of the split tree.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WSplit *tiling_split_tree(WTiling *ws)
+{
+    return ws->split_tree;
+}
+
+
+/*EXTL_DOC
+ * Return the most previously active region next to \var{reg} in
+ * direction \var{dirstr} (left/right/up/down). The region \var{reg}
+ * must be managed by \var{ws}. If \var{any} is not set, the status display
+ * is not considered.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WRegion *tiling_nextto(WTiling *ws, WRegion *reg, const char *dirstr,
+                       bool any)
+{
+    WRegionNavi nh;
+    
+    if(!ioncore_string_to_navi(dirstr, &nh))
+        return NULL;
+    
+    return tiling_do_navi_next(ws, reg, nh, FALSE, any);
+}
+
+
+/*EXTL_DOC
+ * Return the most previously active region on \var{ws} with no
+ * other regions next to it in  direction \var{dirstr} 
+ * (left/right/up/down). If \var{any} is not set, the status 
+ * display is not considered.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WRegion *tiling_farthest(WTiling *ws, const char *dirstr, bool any)
+{
+    WRegionNavi nh;
+    
+    if(!ioncore_string_to_navi(dirstr, &nh))
+        return NULL;
+    
+    return tiling_do_navi_first(ws, nh, any);
+}
+
+
+/*EXTL_DOC
+ * For region \var{reg} managed by \var{ws} return the \type{WSplit}
+ * a leaf of which \var{reg} is.
+ */
+EXTL_SAFE
+EXTL_EXPORT_MEMBER
+WSplitRegion *tiling_node_of(WTiling *ws, WRegion *reg)
+{
+    if(reg==NULL){
+        warn(TR("Nil parameter."));
+        return NULL;
+    }
+    
+    if(REGION_MANAGER(reg)!=(WRegion*)ws){
+        warn(TR("Manager doesn't match."));
+        return NULL;
+    }
+    
+    return splittree_node_of(reg);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Flip and transpose */
+
+
+static WSplitSplit *get_at_split(WTiling *ws, WRegion *reg)
+{
+    WSplit *node;
+    WSplitSplit *split;
+    
+    if(reg==NULL){
+        split=OBJ_CAST(ws->split_tree, WSplitSplit);
+        if(split==NULL)
+            return NULL;
+        else if(split->br==(WSplit*)ws->stdispnode)
+            return OBJ_CAST(split->tl, WSplitSplit);
+        else if(split->tl==(WSplit*)ws->stdispnode)
+            return OBJ_CAST(split->br, WSplitSplit);
+        else
+            return split;
+    }
+    
+    node=(WSplit*)get_node_check(ws, reg);
+    
+    if(node==NULL)
+        return NULL;
+    
+    if(node==(WSplit*)ws->stdispnode){
+        warn(TR("The status display is not a valid parameter for "
+                "this routine."));
+        return NULL;
+    }
+        
+    split=OBJ_CAST(node->parent, WSplitSplit);
+    
+    if(split!=NULL && (split->tl==(WSplit*)ws->stdispnode ||
+                       split->br==(WSplit*)ws->stdispnode)){
+        split=OBJ_CAST(((WSplit*)split)->parent, WSplitSplit);
+    }
+       
+    return split;
+}
+
+
+/*EXTL_DOC
+ * Flip \var{ws} at \var{reg} or root if nil.
+ */
+EXTL_EXPORT_MEMBER
+bool iowns_flip_at(WTiling *ws, WRegion *reg)
+{
+    WSplitSplit *split=get_at_split(ws, reg);
+    
+    if(split==NULL){
+        return FALSE;
+    }else{
+        splitsplit_flip(split);
+        return TRUE;
+    }
+}
+
+
+/*EXTL_DOC
+ * Transpose \var{ws} at \var{reg} or root if nil.
+ */
+EXTL_EXPORT_MEMBER
+bool iowns_transpose_at(WTiling *ws, WRegion *reg)
+{
+    WSplitSplit *split=get_at_split(ws, reg);
+    
+    if(split==NULL){
+        return FALSE;
+    }else{
+        split_transpose((WSplit*)split);
+        return TRUE;
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Floating toggle */
+
+
+static void replace(WSplitSplit *split, WSplitSplit *nsplit)
+{
+    WSplitInner *psplit=split->isplit.split.parent;
+
+    nsplit->tl=split->tl;
+    split->tl=NULL;
+    nsplit->tl->parent=(WSplitInner*)nsplit;
+    
+    nsplit->br=split->br;
+    split->br=NULL;
+    nsplit->br->parent=(WSplitInner*)nsplit;
+    
+    if(psplit!=NULL){
+        splitinner_replace((WSplitInner*)psplit, (WSplit*)split, 
+                           (WSplit*)nsplit);
+    }else{
+        splittree_changeroot((WSplit*)split, (WSplit*)nsplit);
+    }
+}
+
+
+WSplitSplit *tiling_set_floating(WTiling *ws, WSplitSplit *split, int sp)
+{
+    bool set=OBJ_IS(split, WSplitFloat);
+    bool nset=libtu_do_setparam(sp, set);
+    const WRectangle *g=&((WSplit*)split)->geom;
+    WSplitSplit *ns;
+    
+    if(!XOR(nset, set))
+        return split;
+    
+    if(nset){
+        ns=(WSplitSplit*)create_splitfloat(g, ws, split->dir);
+    }else{
+        if(OBJ_IS(split->tl, WSplitST) || OBJ_IS(split->br, WSplitST)){
+            warn(TR("Refusing to float split directly containing the "
+                    "status display."));
+            return NULL;
+        }
+        ns=create_splitsplit(g, split->dir);
+    }
+
+    if(ns!=NULL){
+        replace(split, ns);
+        split_resize((WSplit*)ns, g, PRIMN_ANY, PRIMN_ANY);
+        mainloop_defer_destroy((Obj*)split);
+    }
+    
+    return ns;
+}
+
+
+/*EXTL_DOC
+ * Toggle floating of a split's sides at \var{split} as indicated by the 
+ * parameter \var{how} (set/unset/toggle). A split of the appropriate is 
+ * returned, if there was a change.
+ */
+EXTL_EXPORT_AS(WTiling, set_floating)
+WSplitSplit *tiling_set_floating_extl(WTiling *ws, WSplitSplit *split, 
+                                     const char *how)
+{
+    if(!check_node(ws, (WSplit*)split))
+        return NULL;
+    return tiling_set_floating(ws, split, libtu_string_to_setparam(how));
+}
+
+
+/*EXTL_DOC
+ * Toggle floating of the sides of a split containin \var{reg} as indicated 
+ * by the parameters \var{how} (set/unset/toggle) and \var{dirstr}
+ * (left/right/up/down/any). The new status is returned (and \code{false}
+ * also on error).
+ */
+EXTL_EXPORT_AS(WTiling, set_floating_at)
+bool tiling_set_floating_at_extl(WTiling *ws, WRegion *reg, const char *how,
+                                const char *dirstr)
+{
+    WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY;
+    WSplitSplit *split, *nsplit;
+    WSplit *node;
+    
+    node=(WSplit*)get_node_check(ws, reg);
+    if(node==NULL)
+        return FALSE;
+    
+    
+    if(dirstr!=NULL){
+        WRegionNavi nh;
+        
+        if(!ioncore_string_to_navi(dirstr, &nh))
+            return FALSE;
+    
+        navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE);
+    }
+
+    while(TRUE){
+        split=OBJ_CAST(node->parent, WSplitSplit);
+        if(split==NULL){
+            warn(TR("No suitable split here."));
+            return FALSE;
+        }
+
+        if(!OBJ_IS(split->tl, WSplitST) && !OBJ_IS(split->br, WSplitST)){
+            WPrimn tmp=(split->dir==SPLIT_VERTICAL ? vprimn : hprimn);
+            if(tmp==PRIMN_ANY 
+               || (node==split->tl && tmp==PRIMN_BR)
+               || (node==split->br && tmp==PRIMN_TL)){
+                break;
+            }
+        }
+        
+        node=(WSplit*)split;
+    }
+    
+    nsplit=tiling_set_floating(ws, split, libtu_string_to_setparam(how));
+    
+    return OBJ_IS((Obj*)(nsplit==NULL ? split : nsplit), WSplitFloat);
+}
+
+
+/*}}}*/
+
+
+/*{{{ Save */
+
+
+ExtlTab tiling_get_configuration(WTiling *ws)
+{
+    ExtlTab tab, split_tree=extl_table_none();
+    
+    tab=region_get_base_configuration((WRegion*)ws);
+    
+    if(ws->split_tree!=NULL){
+        if(!split_get_config(ws->split_tree, &split_tree))
+            warn(TR("Could not get split tree."));
+    }
+    
+    extl_table_sets_t(tab, "split_tree", split_tree);
+    extl_unref_table(split_tree);
+    
+    return tab;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Load */
+
+
+WSplit *load_splitst(WTiling *ws, const WRectangle *geom, ExtlTab tab)
+{
+    WSplitST *st;
+
+    if(ws->stdispnode!=NULL){
+        warn(TR("Workspace already has a status display node."));
+        return NULL;
+    }
+
+    st=create_splitst(geom, NULL);
+    ws->stdispnode=st;
+    return (WSplit*)st;
+}
+
+
+static bool do_attach(WTiling *ws, WRegion *reg, void *p)
+{
+    WSplitRegion *node=create_splitregion(&REGION_GEOM(reg), reg);
+    
+    if(node==NULL)
+        return FALSE;
+            
+    if(!tiling_managed_add(ws, reg)){
+        node->reg=NULL;
+        destroy_obj((Obj*)node);
+        return FALSE;
+    }
+    
+    *(WSplitRegion**)p=node;
+    
+    return TRUE;
+}
+
+
+WSplit *load_splitregion(WTiling *ws, const WRectangle *geom, ExtlTab tab)
+{
+    WWindow *par=REGION_PARENT(ws);
+    WRegionAttachData data;
+    WSplit *node=NULL;
+    WFitParams fp;
+    ExtlTab rt;
+    
+    if(!extl_table_gets_t(tab, "regparams", &rt)){
+        warn(TR("Missing region parameters."));
+        return NULL;
+    }
+    
+    data.type=REGION_ATTACH_LOAD;
+    data.u.tab=rt;
+    
+    assert(par!=NULL);
+    fp.g=*geom;
+    fp.mode=REGION_FIT_EXACT;
+    
+    region_attach_helper((WRegion*)ws, par, &fp, 
+                         (WRegionDoAttachFn*)do_attach, &node, &data);
+
+    extl_unref_table(rt);
+    
+    return node;
+}
+
+
+#define MINS 1
+
+WSplit *load_splitsplit(WTiling *ws, const WRectangle *geom, ExtlTab tab)
+{
+    WSplit *tl=NULL, *br=NULL;
+    WSplitSplit *split;
+    char *dir_str;
+    int dir, brs, tls;
+    ExtlTab subtab;
+    WRectangle geom2;
+    int set=0;
+
+    set+=(extl_table_gets_i(tab, "tls", &tls)==TRUE);
+    set+=(extl_table_gets_i(tab, "brs", &brs)==TRUE);
+    set+=(extl_table_gets_s(tab, "dir", &dir_str)==TRUE);
+    
+    if(set!=3)
+        return NULL;
+    
+    if(strcmp(dir_str, "vertical")==0){
+        dir=SPLIT_VERTICAL;
+    }else if(strcmp(dir_str, "horizontal")==0){
+        dir=SPLIT_HORIZONTAL;
+    }else{
+        warn(TR("Invalid direction."));
+        free(dir_str);
+        return NULL;
+    }
+    free(dir_str);
+
+    split=create_splitsplit(geom, dir);
+    if(split==NULL)
+        return NULL;
+
+    tls=maxof(tls, MINS);
+    brs=maxof(brs, MINS);
+        
+    geom2=*geom;
+    if(dir==SPLIT_HORIZONTAL){
+        tls=maxof(0, geom->w)*tls/(tls+brs);
+        geom2.w=tls;
+    }else{
+        tls=maxof(0, geom->h)*tls/(tls+brs);
+        geom2.h=tls;
+    }
+    
+    if(extl_table_gets_t(tab, "tl", &subtab)){
+        tl=tiling_load_node(ws, &geom2, subtab);
+        extl_unref_table(subtab);
+    }
+
+    geom2=*geom;
+    if(dir==SPLIT_HORIZONTAL){
+        geom2.w-=tls;
+        geom2.x+=tls;
+    }else{
+        geom2.h-=tls;
+        geom2.y+=tls;
+    }
+            
+    if(extl_table_gets_t(tab, "br", &subtab)){
+        br=tiling_load_node(ws, &geom2, subtab);
+        extl_unref_table(subtab);
+    }
+    
+    if(tl==NULL || br==NULL){
+        /* PRIMN_TL/BR instead of ANY because of stdisp. */
+        destroy_obj((Obj*)split);
+        if(tl!=NULL){
+            split_do_resize(tl, geom, PRIMN_BR, PRIMN_BR, FALSE);
+            return tl;
+        }
+        if(br!=NULL){
+            split_do_resize(br, geom, PRIMN_TL, PRIMN_TL, FALSE);
+            return br;
+        }
+        return NULL;
+    }
+    
+    tl->parent=(WSplitInner*)split;
+    br->parent=(WSplitInner*)split;
+
+    /*split->tmpsize=tls;*/
+    split->tl=tl;
+    split->br=br;
+    
+    return (WSplit*)split;
+}
+
+
+WSplit *tiling_load_node_default(WTiling *ws, const WRectangle *geom, 
+                                ExtlTab tab)
+{
+    char *typestr=NULL;
+    WSplit *node=NULL;
+
+    extl_table_gets_s(tab, "type", &typestr);
+    
+    if(typestr==NULL){
+        warn(TR("No split type given."));
+        return NULL;
+    }
+    
+    if(strcmp(typestr, "WSplitRegion")==0)
+        node=load_splitregion(ws, geom, tab);
+    else if(strcmp(typestr, "WSplitSplit")==0)
+        node=load_splitsplit(ws, geom, tab);
+    else if(strcmp(typestr, "WSplitFloat")==0)
+        node=load_splitfloat(ws, geom, tab);
+    else if(strcmp(typestr, "WSplitST")==0)
+        node=NULL;/*load_splitst(ws, geom, tab);*/
+    else
+        warn(TR("Unknown split type."));
+    
+    free(typestr);
+    
+    return node;
+}
+
+
+WSplit *tiling_load_node(WTiling *ws, const WRectangle *geom, ExtlTab tab)
+{
+    WSplit *ret=NULL;
+    CALL_DYN_RET(ret, WSplit*, tiling_load_node, ws, (ws, geom, tab));
+    return ret;
+}
+
+
+
+WRegion *tiling_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
+{
+    WTiling *ws;
+    ExtlTab treetab;
+    bool ci=TRUE;
+
+    if(extl_table_gets_t(tab, "split_tree", &treetab))
+        ci=FALSE;
+    
+    ws=create_tiling(par, fp, NULL, ci);
+    
+    if(ws==NULL){
+        if(!ci)
+            extl_unref_table(treetab);
+        return NULL;
+    }
+
+    if(!ci){
+        ws->split_tree=tiling_load_node(ws, &REGION_GEOM(ws), treetab);
+        extl_unref_table(treetab);
+    }
+    
+    if(ws->split_tree==NULL){
+        warn(TR("The workspace is empty."));
+        destroy_obj((Obj*)ws);
+        return NULL;
+    }
+    
+    ws->split_tree->ws_if_root=ws;
+    split_restack(ws->split_tree, ws->dummywin, Above);
+    
+    return (WRegion*)ws;
+}
+
+
+/*}}}*/
+
+
+/*{{{ Dynamic function table and class implementation */
+
+
+static DynFunTab tiling_dynfuntab[]={
+    {region_map, 
+     tiling_map},
+    
+    {region_unmap, 
+     tiling_unmap},
+    
+    {region_do_set_focus, 
+     tiling_do_set_focus},
+    
+    {(DynFun*)region_fitrep,
+     (DynFun*)tiling_fitrep},
+    
+    {region_managed_rqgeom, 
+     tiling_managed_rqgeom},
+    
+    {region_managed_remove, 
+     tiling_managed_remove},
+    
+    {(DynFun*)region_managed_prepare_focus,
+     (DynFun*)tiling_managed_prepare_focus},
+    
+    {(DynFun*)region_prepare_manage, 
+     (DynFun*)tiling_prepare_manage},
+    
+    {(DynFun*)region_rescue_clientwins,
+     (DynFun*)tiling_rescue_clientwins},
+
+    {(DynFun*)region_get_rescue_pholder_for,
+     (DynFun*)tiling_get_rescue_pholder_for},
+    
+    {(DynFun*)region_get_configuration,
+     (DynFun*)tiling_get_configuration},
+
+    {(DynFun*)region_managed_may_destroy,
+     (DynFun*)tiling_managed_may_destroy},
+
+    {(DynFun*)region_may_destroy,
+     (DynFun*)tiling_may_destroy},
+
+    {(DynFun*)region_current,
+     (DynFun*)tiling_current},
+
+    {(DynFun*)tiling_managed_add,
+     (DynFun*)tiling_managed_add_default},
+    
+    {region_manage_stdisp,
+     tiling_manage_stdisp},
+
+    {region_unmanage_stdisp,
+     tiling_unmanage_stdisp},
+    
+    {(DynFun*)tiling_load_node,
+     (DynFun*)tiling_load_node_default},
+            
+    {region_restack,
+     tiling_restack},
+
+    {region_stacking,
+     tiling_stacking},
+    
+    {(DynFun*)region_navi_first,
+     (DynFun*)tiling_navi_first},
+    
+    {(DynFun*)region_navi_next,
+     (DynFun*)tiling_navi_next},
+    
+    {(DynFun*)region_xwindow,
+     (DynFun*)tiling_xwindow},
+    
+    {(DynFun*)region_prepare_manage_transient,
+     (DynFun*)tiling_prepare_manage_transient},
+     
+    END_DYNFUNTAB
+};
+
+
+EXTL_EXPORT
+IMPLCLASS(WTiling, WRegion, tiling_deinit, tiling_dynfuntab);
+
+    
+/*}}}*/
+
diff --git a/mod_tiling/tiling.h b/mod_tiling/tiling.h
new file mode 100644 (file)
index 0000000..5b7a8e7
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * ion/mod_tiling/tiling.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ION_MOD_TILING_TILING_H
+#define ION_MOD_TILING_TILING_H
+
+#include <libtu/ptrlist.h>
+#include <libextl/extl.h>
+#include <ioncore/common.h>
+#include <ioncore/region.h>
+#include <ioncore/screen.h>
+#include <ioncore/rectangle.h>
+#include <ioncore/pholder.h>
+#include <ioncore/navi.h>
+#include "split.h"
+
+
+INTRCLASS(WTiling);
+DECLCLASS(WTiling){
+    WRegion reg;
+    WSplit *split_tree;
+    WSplitST *stdispnode;
+    PtrList *managed_list;
+    WRegionSimpleCreateFn *create_frame_fn;
+    Window dummywin;
+};
+
+
+extern bool tiling_init(WTiling *ws, WWindow *parent, const WFitParams *fp,
+                       WRegionSimpleCreateFn *create_frame_fn, bool ci);
+extern WTiling *create_tiling(WWindow *parent, const WFitParams *fp, 
+                            WRegionSimpleCreateFn *create_frame_fn, bool ci);
+extern WTiling *create_tiling_simple(WWindow *parent, const WFitParams *fp);
+extern void tiling_deinit(WTiling *ws);
+
+extern ExtlTab tiling_resize_tree(WTiling *ws, WSplit *node, ExtlTab g);
+
+extern WRegion *tiling_current(WTiling *ws);
+extern WRegion *tiling_nextto(WTiling *ws, WRegion *reg, const char *str, bool any);
+extern WRegion *tiling_farthest(WTiling *ws, const char *str, bool any);
+extern WRegion *tiling_region_at(WTiling *ws, int x, int y);
+
+extern WFrame *tiling_split_top(WTiling *ws, const char *dirstr);
+extern WFrame *tiling_split_at(WTiling *ws, WFrame *frame, 
+                              const char *dirstr, bool attach_current);
+extern bool tiling_unsplit_at(WTiling *ws, WFrame *frame);
+
+extern WSplitSplit *tiling_set_floating(WTiling *ws, WSplitSplit *split, 
+                                       int sp);
+
+extern WSplit *tiling_split_tree(WTiling *ws);
+extern WSplit *tiling_split_of(WTiling *ws, WRegion *reg);
+
+extern void tiling_do_managed_remove(WTiling *ws, WRegion *reg);
+
+DYNFUN bool tiling_managed_add(WTiling *ws, WRegion *reg);
+extern bool tiling_managed_add_default(WTiling *ws, WRegion *reg);
+
+extern WRegion *tiling_do_navi_next(WTiling *ws, WRegion *reg, 
+                                    WRegionNavi nh, bool nowrap,
+                                    bool any);
+extern WRegion *tiling_do_navi_first(WTiling *ws, WRegionNavi nh, 
+                                     bool any);
+extern WRegion *tiling_navi_next(WTiling *ws, WRegion *reg, 
+                                 WRegionNavi nh, WRegionNaviData *data);
+extern WRegion *tiling_navi_first(WTiling *ws, WRegionNavi nh,
+                                  WRegionNaviData *data);
+
+/* Inherited dynfun implementations */
+
+extern bool tiling_fitrep(WTiling *ws, WWindow *par, const WFitParams *fp);
+extern void tiling_map(WTiling *ws);
+extern void tiling_unmap(WTiling *ws);
+extern ExtlTab tiling_get_configuration(WTiling *ws);
+extern void tiling_managed_rqgeom(WTiling *ws, WRegion *reg,
+                                  const WRQGeomParams *rq,
+                                  WRectangle *geomret);
+extern void tiling_managed_remove(WTiling *ws, WRegion *reg);
+extern void tiling_managed_activated(WTiling *ws, WRegion *reg);
+extern bool tiling_rescue_clientwins(WTiling *ws, WPHolder *ph);
+extern WPHolder *tiling_get_rescue_pholder_for(WTiling *ws, WRegion *mgd);
+extern void tiling_do_set_focus(WTiling *ws, bool warp);
+extern bool tiling_managed_prepare_focus(WTiling *ws, WRegion *reg, 
+                                        int flags, WPrepareFocusResult *res);
+extern bool tiling_managed_may_destroy(WTiling *ws, WRegion *reg);
+extern void tiling_manage_stdisp(WTiling *ws, WRegion *stdisp, 
+                                const WMPlexSTDispInfo *di);
+extern void tiling_unmanage_stdisp(WTiling *ws, bool permanent, bool nofocus);
+
+extern void tiling_fallback_focus(WTiling *ws, bool warp);
+
+/* Loading */
+
+/* Stupid C can't handle recursive 'WSplitLoadFn *fn' here, so we have
+ * to use the void pointer.
+ */
+typedef WSplit *WSplitLoadFn(WTiling *ws, const WRectangle *geom, ExtlTab tab);
+
+extern WRegion *tiling_load(WWindow *par, const WFitParams *fp, ExtlTab tab);
+
+DYNFUN WSplit *tiling_load_node(WTiling *ws, const WRectangle *geom, ExtlTab tab);
+extern WSplit *tiling_load_node_default(WTiling *ws, const WRectangle *geom, ExtlTab tab);
+
+extern WSplit *load_splitregion(WTiling *ws, const WRectangle *geom, ExtlTab tab);
+extern WSplit *load_splitsplit(WTiling *ws, const WRectangle *geom, ExtlTab tab);
+extern WSplit *load_splitst(WTiling *ws, const WRectangle *geom, ExtlTab tab);
+
+/* Iteration */
+
+typedef PtrListIterTmp WTilingIterTmp;
+
+#define FOR_ALL_MANAGED_BY_TILING(VAR, WS, TMP) \
+    FOR_ALL_ON_PTRLIST(WRegion*, VAR, (WS)->managed_list, TMP)
+    
+#define FOR_ALL_MANAGED_BY_TILING_UNSAFE(VAR, WS) \
+    FOR_ALL_ON_PTRLIST_UNSAFE(WRegion*, VAR, (WS)->managed_list)
+
+
+#endif /* ION_MOD_TILING_TILING_H */
diff --git a/modulelist.mk b/modulelist.mk
new file mode 100644 (file)
index 0000000..4339987
--- /dev/null
@@ -0,0 +1,11 @@
+##
+## List of modules to build
+##
+
+MODULE_LIST = mod_tiling mod_query mod_menu \
+             mod_dock mod_sp mod_sm de \
+             mod_mgmtmode mod_statusbar
+
+# Modules to -dlpreload into pwm if statically linking.
+
+PWM_MODULE_LIST := $(MODULE_LIST)
diff --git a/po/Makefile b/po/Makefile
new file mode 100644 (file)
index 0000000..2339738
--- /dev/null
@@ -0,0 +1,96 @@
+##
+## Ion po Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+XGETTEXT = xgettext
+#MSGFMT = msgfmt -c
+MSGFMT = msgfmt
+MSGMERGE = msgmerge
+MSGCAT = msgcat
+LXGETTEXT = lua-xgettext
+
+TRANSLATIONS := fi cs de ru
+MO_FILES := $(patsubst %,%.mo, $(TRANSLATIONS))
+PO_FILES := $(patsubst %,%.po, $(TRANSLATIONS))
+
+POTDIRS=../ioncore \
+       ../mod_tiling \
+       ../mod_query \
+       ../mod_menu \
+       ../mod_sm \
+       ../mod_sp \
+       ../mod_statusbar \
+       ../mod_mgmtmode \
+       ../de \
+       ../ion \
+       ../pwm \
+       ../etc \
+       $(LIBEXTL_DIR)
+
+EXTRA_POTFILES_LUA=../build/mkman.lua
+
+POTFILE=ion3.pot
+
+TARGETS = $(MO_FILES)
+
+ifndef LOCALEDIR
+LOCALEDIR := $(PREFIX)/share/locale
+endif
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+potfiles: potdirs_potfiles
+       (for i in $(POTDIRS); do cat $$i/potfiles_c|sed "s:\w\+:$$i/&:"; done) > potfiles_c
+       (for i in $(POTDIRS); do cat $$i/potfiles_lua|sed "s:\w\+:$$i/&:"; done; \
+        for i in $(EXTRA_POTFILES_LUA); do echo $$i; done) > potfiles_lua
+
+potdirs_potfiles:
+       for i in $(POTDIRS); do make -C $$i _potfiles; done
+
+pot: $(POTFILE)
+
+$(POTFILE)_c: potfiles
+       $(XGETTEXT) -kTR -kDUMMY_TR -o $@ -f potfiles_c
+
+$(POTFILE)_lua: potfiles
+       $(LXGETTEXT) -k TR -k bdoc -k submenu -k menuentry -o $@ \
+               `cat potfiles_lua`
+
+$(POTFILE): $(POTFILE)_c $(POTFILE)_lua
+       #
+       # GNU gettext sucks bigtime, and refuses to work on POT
+       # files without encoding set. Therefore we'll just have to
+       # use plain old cat and hope that there aren't dupes.
+       #
+       #msgcat -o $@ $+
+       #
+       cat $+ > $@
+
+%.mo: %.po
+       $(MSGFMT) -o $@ $<
+
+_install:
+       for i in $(TRANSLATIONS); do \
+               $(INSTALLDIR) $(LOCALEDIR)/$$i/LC_MESSAGES ; \
+               $(INSTALL) -m $(DATA_MODE) $$i.mo $(LOCALEDIR)/$$i/LC_MESSAGES/ion3.mo ; \
+       done
+
+update_fi: pot
+       $(MSGMERGE) -U fi.po $(POTFILE)
+
+update_cs: pot
+       $(MSGMERGE) -U cs.po $(POTFILE)
+
+update_ru: pot
+       $(MSGMERGE) -U ru.po $(POTFILE)
+
+update_de: pot
+       $(MSGMERGE) -U de.po $(POTFILE)
diff --git a/po/README b/po/README
new file mode 100644 (file)
index 0000000..ad9e729
--- /dev/null
+++ b/po/README
@@ -0,0 +1,14 @@
+
+To generate the `ion3.pot` file needed for new translations, you 
+will need `lua-xgettext`, which you can download by executing
+
+   darcs get http://modeemi.fi/~tuomov/repos/lua-xgettext/
+
+Build and install it as instructed in its README. Then, `make pot` 
+in this directory, copy `ion3.pot` over as `$LANG.po`, and start 
+translating. To update/merge new strings to an existing translation, 
+run `make update_$LANG`, after you have first copy-pasted this 
+Makefile target, when it does not already exist.
+
+Translations for the manual page and welcome message live in
+`../man/`.
diff --git a/po/cs.po b/po/cs.po
new file mode 100644 (file)
index 0000000..41235cf
--- /dev/null
+++ b/po/cs.po
@@ -0,0 +1,1716 @@
+#
+# Czech language translations for Ion3.
+#
+# Copyright (c) Miroslav Kure 2004,2005,2006.
+#
+# This file is distributed under the same license as the Ion3 package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Ion3\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-11-12 12:07+0100\n"
+"PO-Revision-Date: 2006-11-12 12:16+0100\n"
+"Last-Translator: Miroslav Kure <kurem at debian.cz>\n"
+"Language-Team: none\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-2\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../ioncore/conf-bindings.c:96
+msgid "Insane key combination."
+msgstr "©ílená klávesová kombinace."
+
+#: ../ioncore/conf-bindings.c:100
+msgid "Could not convert keysym to keycode."
+msgstr "Nemohu pøevést keysym na keycode."
+
+#: ../ioncore/conf-bindings.c:111
+#, c-format
+msgid "Unknown button \"%s\"."
+msgstr "Neznámé tlaèítko \"%s\"."
+
+#: ../ioncore/conf-bindings.c:116
+msgid "Insane button combination."
+msgstr "©ílená kombinace tlaèítek."
+
+#: ../ioncore/conf-bindings.c:123 ../ioncore/conf-bindings.c:130
+msgid "Insane modifier combination."
+msgstr "©ílená kombinace modifikátorù."
+
+#: ../ioncore/conf-bindings.c:168
+#, c-format
+msgid "Can not wait on modifiers when no modifiers set in \"%s\"."
+msgstr "Nemohu èekat na modifikátory, kdy¾ ¾ádné nebyly nastaveny v \"%s\"."
+
+#: ../ioncore/conf-bindings.c:186
+#, c-format
+msgid "Unable to add binding %s."
+msgstr "Nemohu pøidat vazbu %s."
+
+#: ../ioncore/conf-bindings.c:191
+#, c-format
+msgid "Unable to remove binding %s."
+msgstr "Nemohu odstranit vazbu %s."
+
+#: ../ioncore/conf-bindings.c:230
+#, c-format
+msgid "Unable to add submap for binding %s."
+msgstr "Nemohu pøidat **** pro vazbu %s."
+
+#
+#: ../ioncore/conf-bindings.c:260
+msgid "Binding type not set."
+msgstr "Typ vazby není nastaven."
+
+#: ../ioncore/conf-bindings.c:270
+#, c-format
+msgid "Unknown binding type \"%s\"."
+msgstr "Neznámý typ vazby \"%s\"."
+
+#: ../ioncore/conf-bindings.c:292
+#, c-format
+msgid "Unknown area \"%s\" for binding %s."
+msgstr "Neznámá oblast \"%s\" pro vazbu %s."
+
+#: ../ioncore/conf-bindings.c:333
+#, c-format
+msgid "Unable to get bindmap entry %d."
+msgstr "Nemohu získat záznam o vazbì %d."
+
+#: ../ioncore/conf-bindings.c:375
+msgid "Unable to convert keysym to string."
+msgstr "Nemohu pøevést keysym na øetìzec."
+
+#: ../ioncore/conf-bindings.c:389
+msgid "Unable to convert button to string."
+msgstr "Nemohu pøevést tlaèítko na øetìzec."
+
+#
+#: ../ioncore/event.c:113
+msgid "Time request from X server failed."
+msgstr "Èasový po¾adavek od X serveru selhal."
+
+#
+#: ../ioncore/exec.c:177
+msgid "Not saving state: running under session manager."
+msgstr "Neukládám stav: bì¾ím pod správcem sezení."
+
+#: ../ioncore/strings.c:107 ../ioncore/strings.c:143 ../ioncore/strings.c:176
+msgid "Invalid multibyte string."
+msgstr "Neplatný vícebajtový øetìzec."
+
+#: ../ioncore/strings.c:267
+#, c-format
+msgid "Error compiling regular expression: %s"
+msgstr "Chyba pøi kompilaci regulárního výrazu: %s"
+
+#: ../ioncore/modules.c:158
+msgid "Invalid module name."
+msgstr "Neplatné jméno modulu."
+
+#
+#: ../ioncore/modules.c:170
+msgid "The module is already loaded."
+msgstr "Modul je ji¾ zaveden."
+
+#: ../ioncore/modules.c:185
+msgid ""
+"Module version information not found or version mismatch. Refusing to use."
+msgstr ""
+"Nemohu najít informace o verzi modulu, nebo verze nesouhlasí. Odmítám modul "
+"pou¾ít."
+
+#: ../ioncore/modules.c:196
+#, c-format
+msgid "Unable to initialise module %s."
+msgstr "Nemohu inicializovat modul %s."
+
+#: ../ioncore/modules.c:220 ../libextl/readconfig.c:388
+#, c-format
+msgid "Unable to find '%s' on search path."
+msgstr "V prohledávané cestì nemohu najít \"%s\"."
+
+#: ../ioncore/modules.c:291
+msgid "Unknown module."
+msgstr "Neznámý modul."
+
+#: ../ioncore/modules.c:299
+msgid "Unable to initialise module."
+msgstr "Nemohu inicializovat modul."
+
+#: ../ioncore/modules.c:344
+msgid "No module to load given."
+msgstr "Nebyl zadán modul pro zavedení."
+
+#: ../ioncore/property.c:355 ../ioncore/property.c:364
+msgid "Invalid arguments."
+msgstr "Neplatné argumenty."
+
+#: ../ioncore/screen.c:298
+msgid "act: "
+msgstr "act: "
+
+#: ../ioncore/screen.c:481
+msgid "Only workspace may not be destroyed."
+msgstr "Poslední pracovní plocha nemù¾e být zru¹ena."
+
+#: ../ioncore/screen.c:489
+msgid "Screens may not be destroyed."
+msgstr "Obrazovky nesmí být znièeny."
+
+#: ../ioncore/screen.c:525
+msgid "Invalid offset."
+msgstr "Neplatné odsazení"
+
+#: ../ioncore/screen.c:568
+#, c-format
+msgid "Unable to create a workspace on screen %d."
+msgstr "Nemohu vytvoøit pracovní plochu na obrazovce %d."
+
+#: ../ioncore/sizehint.c:151
+msgid "Invalid client-supplied width/height increment."
+msgstr "Klient zadal neplatný pøírùstek ¹íøky/vý¹ky."
+
+#: ../ioncore/sizehint.c:159
+msgid "Invalid client-supplied aspect-ratio."
+msgstr "Klient zadal neplatný pomìr stran."
+
+#: ../ioncore/ioncore.c:74
+msgid ""
+"This program is free software; you can redistribute it and/or\n"
+"modify it under the terms of the GNU Lesser General Public\n"
+"License as published by the Free Software Foundation; either\n"
+"version 2.1 of the License, or (at your option) any later version.\n"
+"\n"
+"This program is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
+"Lesser General Public License for more details.\n"
+msgstr ""
+"Tento program je svobodný software; mù¾ete jej ¹íøit a/nebo upravovat\n"
+"podle podmínek licence GNU Lesser General Public Licence verze 2.1 nebo\n"
+"(podle va¹eho uvá¾ení) novìj¹í, jak ji publikuje Free Software Foundation.\n"
+"\n"
+"Tento program je distribuovaný v nadìji, ¾e bude u¾iteèný, ale BEZ\n"
+"JAKÉKOLIV ZÁRUKY. Pro více podrobností se podívejte do licence GNU\n"
+"Lesser General Public License.\n"
+
+#: ../ioncore/ioncore.c:155
+msgid "No encoding given in LC_CTYPE."
+msgstr "V LC_CTYPE není zadáno kódování."
+
+#: ../ioncore/ioncore.c:437
+#, c-format
+msgid "Could not connect to X display '%s'"
+msgstr "Nemohu se pøipojit na X displej \"%s\""
+
+#: ../ioncore/ioncore.c:489
+msgid "Could not find a screen to manage."
+msgstr "Nemohu najít obrazovku, kterou bych mohl spravovat."
+
+#: ../ioncore/xic.c:38
+msgid "Failed to open input method."
+msgstr "Selhalo otevøení vstupní metody."
+
+#: ../ioncore/xic.c:43
+msgid "Input method doesn't support any style."
+msgstr "Vstupní metoda nepodporuje ¾ádný styl."
+
+#: ../ioncore/xic.c:58
+msgid "input method doesn't support my preedit type."
+msgstr "Vstupní metoda nepodporuje mùj pøedpøipravený typ."
+
+#: ../ioncore/xic.c:86
+msgid "Failed to create input context."
+msgstr "Selhalo vytvoøení vstupního kontextu."
+
+#: ../ioncore/clientwin.c:411
+#, c-format
+msgid "The transient_for hint for \"%s\" points to itself."
+msgstr "Nápovìda transient_for pro \"%s\" ukazuje sama na sebe."
+
+#: ../ioncore/clientwin.c:415
+#, c-format
+msgid ""
+"Client window \"%s\" has broken transient_for hint. (\"Extended WM hints\" "
+"multi-parent brain damage?)"
+msgstr ""
+"Klientské okno \"%s\" má poru¹enou nápovìdu transient_for. (Schizofrenie "
+"více rodièù \"Extended WM hints\"?)"
+
+#: ../ioncore/clientwin.c:420
+#, c-format
+msgid "The transient_for window for \"%s\" is not on the same screen."
+msgstr "Okno transient_for pro \"%s\" není na stejné obrazovce."
+
+#: ../ioncore/clientwin.c:440 ../ioncore/clientwin.c:527
+#: ../ioncore/clientwin.c:1300
+#, c-format
+msgid "Window %#x disappeared."
+msgstr "Okno %#x zmizelo."
+
+#: ../ioncore/clientwin.c:547
+msgid "Unable to find a matching root window!"
+msgstr "Nemohu najít odpovídající koøenové okno!"
+
+#: ../ioncore/clientwin.c:582
+#, c-format
+msgid "Unable to manage client window %#x."
+msgstr "Nemohu spravovat klientské okno %#x."
+
+#: ../ioncore/clientwin.c:633
+msgid "Changes is WM_TRANSIENT_FOR property are unsupported."
+msgstr "Zmìny vlastnosti WM_TRANSIENT_FOR nejsou podporovány."
+
+#
+#: ../ioncore/clientwin.c:809
+msgid "Client does not support the WM_DELETE protocol."
+msgstr "Klient nepodporuje protokol WM_DELETE."
+
+#: ../ioncore/clientwin.c:1306
+msgid "Saved client window does not want to be managed."
+msgstr "Ulo¾ené klientské okno nechce být spravováno."
+
+#
+#: ../ioncore/colormap.c:96
+msgid "Unable to store colourmap watch info."
+msgstr "Nemohu ulo¾it informace o barevné mapì."
+
+#: ../ioncore/region.c:46
+msgid "Creating region with negative width or height!"
+msgstr "Vytváøím oblast se zápornou ¹íøkou nebo vý¹kou!"
+
+#: ../ioncore/region.c:94
+#, c-format
+msgid "Destroying object \"%s\" with client windows as children."
+msgstr "Nièím objekt \"%s\" spoleènì s klientskými okny jako dìtmi."
+
+#: ../ioncore/region.c:395
+msgid "Failed to rescue some client windows - not closing."
+msgstr "Záchrana nìkterých klientských oken selhala - nezavírám."
+
+#: ../ioncore/attach.c:71 ../ioncore/pholder.c:46
+msgid "Unable to reparent."
+msgstr "Nemohu zmìnit rodièe."
+
+#: ../ioncore/attach.c:157 ../ioncore/frame-pointer.c:282
+#, c-format
+msgid "Attempt to make region %s manage its ancestor %s."
+msgstr "Pokus, aby oblast %s spravovala svého pøedka %s."
+
+#: ../ioncore/manage.c:77
+msgid "Unable to find a screen for a new client window."
+msgstr "Nemohu najít obrazovku pro nové klientské okno."
+
+#: ../ioncore/rootwin.c:223
+#, c-format
+msgid "Unable to redirect root window events for screen %d."
+msgstr "Nemohu pøesmìrovat události koøenového okna na obrazovce %d."
+
+#: ../ioncore/rootwin.c:319 ../ioncore/rootwin.c:344
+msgid "Xinerama sanity check failed; overlapping screens detected."
+msgstr "Kontrola Xineramy selhala: byly nalezeny pøekrývající se obrazovky."
+
+#: ../ioncore/rootwin.c:326 ../ioncore/rootwin.c:351
+msgid "Xinerama sanity check failed; zero size detected."
+msgstr "Kontrola Xineramy selhala: byla nalezena nulová velikost."
+
+#: ../ioncore/rootwin.c:379 ../ioncore/rootwin.c:397
+msgid "Don't know how to get Xinerama information for multiple X root windows."
+msgstr "Nevím, jak zpracovat informace z Xineramy pro více koøenových oken."
+
+#: ../ioncore/rootwin.c:393
+msgid "Error retrieving Xinerama information."
+msgstr "Chyba pøi získávání informaci z Xineramy."
+
+#: ../ioncore/rootwin.c:432 ../ioncore/rootwin.c:451
+#, c-format
+msgid "Unable to setup Xinerama screen %d."
+msgstr "Nemohu nastavit obrazovku Xineramy %d."
+
+#: ../ioncore/rootwin.c:464
+#, c-format
+msgid "Unable to setup X screen %d."
+msgstr "Nemohu nastavit X obrazovku %d."
+
+#: ../ioncore/names.c:90
+#, c-format
+msgid "Corrupt instance number %s."
+msgstr "Poru¹ená instance èíslo %s."
+
+#: ../ioncore/saveload.c:125
+#, c-format
+msgid "Unknown class \"%s\", cannot create region."
+msgstr "Neznámá tøída \"%s\", nemohu vytvoøit oblast."
+
+#: ../ioncore/saveload.c:205
+#, c-format
+msgid ""
+"There were errors loading layout. Backing up current layout savefile as\n"
+"%s.\n"
+"If you are _not_ running under a session manager and wish to restore your\n"
+"old layout, copy this backup file over the layout savefile found in the\n"
+"same directory while Ion is not running and after having fixed your other\n"
+"configuration files that are causing this problem. (Maybe a missing\n"
+"module?)"
+msgstr ""
+"Pøi nahrávání rozlo¾ení se vyskytly chyby. Zálohuji aktuální soubor\n"
+"s rozlo¾ením jako %s.\n"
+"Pokud _nepou¾íváte_ správce sezení a chcete obnovit své staré rozlo¾ení,\n"
+"ukonèete Ion a zkopírujte tento zálo¾ní soubor pøes soubor s rozlo¾ením.\n"
+"Pøedtím nezapomeòte opravit konfiguraèní soubory, které zpùsobily tyto\n"
+"problémy. (Mo¾ná chybìjící modul?)"
+
+#: ../ioncore/saveload.c:248
+msgid "Unable to get file for layout backup."
+msgstr "Nemohu získat soubor pro zálohu rozlo¾ení."
+
+#: ../ioncore/saveload.c:252
+#, c-format
+msgid "Backup file %s already exists."
+msgstr "Zálo¾ní soubor %s ji¾ existuje."
+
+#: ../ioncore/saveload.c:258
+msgid "Failed backup."
+msgstr "Selhala záloha."
+
+#
+#: ../ioncore/saveload.c:263
+msgid "Unable to initialise layout on any screen."
+msgstr "Na ¾ádné obrazovce nemohu inicializovat rozlo¾ení."
+
+#: ../ioncore/saveload.c:283
+#, c-format
+msgid "Unable to get configuration for screen %d."
+msgstr "Nemohu získat nastavení pro obrazovku %d."
+
+#: ../ioncore/saveload.c:296
+msgid "Unable to save layout."
+msgstr "Nemohu ulo¾it rozlo¾ení."
+
+#: ../ioncore/conf.c:143
+msgid "User directory can not be set."
+msgstr "U¾ivatelský adresáø nemù¾e být nastaven."
+
+#: ../ioncore/conf.c:217
+msgid "Some bindmaps were empty, loading ioncore_efbb."
+msgstr "Nìkteré tabulky kláves jsou prázdné, nahrávám ioncore_efbb."
+
+#: ../ioncore/fullscreen.c:98
+msgid "Failed to enter full screen mode."
+msgstr "Selhal pøechod do celoobrazovkového re¾imu."
+
+#: ../ioncore/fullscreen.c:120
+msgid ""
+"Failed to return from full screen mode; remaining manager or parent from "
+"previous location refused to manage us."
+msgstr ""
+"Návrat z celoobrazovkového re¾imu se nezdaøil; zbývající správce nebo rodiè "
+"z pøedchozího umístìní nás nechce spravovat."
+
+#: ../ioncore/mplex.c:172
+msgid "Refusing to destroy - not empty."
+msgstr "Odmítám zru¹it - není prázdné."
+
+#
+#: ../ioncore/mplex.c:1678
+msgid "Invalid position setting."
+msgstr "Neplatné nastavení pozice."
+
+#
+#: ../ioncore/mplex.c:1718
+msgid "Invalid action setting."
+msgstr "Neplatné nastavení akce."
+
+#: ../ioncore/gr.c:119
+#, c-format
+msgid "Drawing engine %s is not registered!"
+msgstr "Vykreslovací jednotka %s není registrována!"
+
+#: ../ioncore/gr.c:138
+#, c-format
+msgid "Unable to find brush for style '%s'."
+msgstr "Nemohu najít ¹tìtec pro styl \"%s\"."
+
+#: ../ioncore/gr.c:408
+msgid "No drawing engines loaded, trying \"de\"."
+msgstr "Nebyly nahrány ¾ádné vykreslovací jednotky, zkou¹ím \"%s\"."
+
+#: ../ioncore/group.c:184 ../mod_tiling/tiling.c:96
+#, c-format
+msgid "Error reparenting %s."
+msgstr "Chyba zmìny rodièe %s."
+
+#: ../ioncore/group.c:481 ../mod_tiling/tiling.c:668
+msgid "Workspace not empty - refusing to destroy."
+msgstr "Pracovní plocha není prázdná - odmítám zru¹it."
+
+#: ../ioncore/group.c:680
+msgid "'bottom' already set."
+msgstr "'bottom' je ji¾ nastaven."
+
+#: ../ioncore/navi.c:45
+msgid "Invalid parameter."
+msgstr "Neplatný parametr"
+
+#: ../ioncore/navi.c:72
+msgid "Invalid direction parameter."
+msgstr "Neplatný parametr smìru."
+
+#: ../ioncore/group-ws.c:67
+#, c-format
+msgid "Unknown placement method \"%s\"."
+msgstr "Neznámý zpùsob umístìní \"%s\"."
+
+#
+#: ../mod_tiling/tiling.c:77
+msgid "Split not on workspace."
+msgstr "Rozdìlení není na pracovní plo¹e."
+
+#: ../mod_tiling/tiling.c:373
+msgid "Unable to create a node for status display."
+msgstr "Nemohu vytvoøit uzel pro stavový øádek."
+
+#: ../mod_tiling/tiling.c:386
+msgid "Unable to create new split for status display."
+msgstr "Nemohu vytvoøit nové rozdìlení pro stavový øádek."
+
+#: ../mod_tiling/tiling.c:937
+msgid "Invalid direction"
+msgstr "Neplatný smìr"
+
+#
+#: ../mod_tiling/tiling.c:970 ../mod_tiling/split.c:1018
+msgid "Invalid node."
+msgstr "Neplatný uzel."
+
+#
+#: ../mod_tiling/tiling.c:989
+msgid "Unable to split."
+msgstr "Nemohu rozdìlit."
+
+#: ../mod_tiling/tiling.c:1092
+msgid "Nil frame."
+msgstr "Prázdný rám."
+
+#: ../mod_tiling/tiling.c:1097
+msgid "The frame is not managed by the workspace."
+msgstr "Rám není spravován pracovní plochou."
+
+#: ../mod_tiling/tiling.c:1196
+msgid "Nil parameter."
+msgstr "Prázdný parametr."
+
+#: ../mod_tiling/tiling.c:1201
+msgid "Manager doesn't match."
+msgstr "Správce se neshoduje."
+
+#: ../mod_tiling/tiling.c:1238
+msgid "The status display is not a valid parameter for this routine."
+msgstr "Stavový displej není v této rutinì platným parametrem."
+
+#: ../mod_tiling/tiling.c:1329
+msgid "Refusing to float split directly containing the status display."
+msgstr "Odmítám rozdìlit pøímo prvek obsahující stavový displej."
+
+#: ../mod_tiling/tiling.c:1392
+msgid "No suitable split here."
+msgstr "®ádné vhodné rozdìlení."
+
+#
+#: ../mod_tiling/tiling.c:1428
+msgid "Could not get split tree."
+msgstr "Nemohu získat strom rozdìlení"
+
+#: ../mod_tiling/tiling.c:1449
+msgid "Workspace already has a status display node."
+msgstr "Pracovní plocha ji¾ obsahuje stavový displej."
+
+#
+#: ../mod_tiling/tiling.c:1487
+msgid "Missing region parameters."
+msgstr "Chybìjící parametry oblasti."
+
+#: ../mod_tiling/tiling.c:1531 ../mod_tiling/splitfloat.c:780
+msgid "Invalid direction."
+msgstr "Neplatný smìr."
+
+#
+#: ../mod_tiling/tiling.c:1606
+msgid "No split type given."
+msgstr "Nebyl zadán typ rozdìlení."
+
+#: ../mod_tiling/tiling.c:1619
+msgid "Unknown split type."
+msgstr "Neznámý typ rozdìlení."
+
+#
+#
+#: ../mod_tiling/tiling.c:1659
+msgid "The workspace is empty."
+msgstr "Pracovní plocha je prázdná."
+
+#: ../mod_tiling/placement.c:106
+#, c-format
+msgid ""
+"Ooops... could not find a region to attach client window to on workspace %s."
+msgstr ""
+"Ooops... na pracovní plo¹e %s nemohu najít oblast, ke které se má pøipojit "
+"klientské okno."
+
+#
+#: ../mod_tiling/split.c:524
+msgid "Unable to move the status display out of way."
+msgstr "Nemohu pøesunout stavový øádek tak, aby nezavazel."
+
+#: ../mod_tiling/split.c:937
+msgid "REGION_RQGEOM_TRYONLY unsupported for status display."
+msgstr "REGION_RQGEOM_TRYONLY není stavovým displejem podporován."
+
+#
+#: ../mod_tiling/split.c:1082
+msgid "Splitting the status display is not allowed."
+msgstr "Rozdìlení stavového displeje není povoleno."
+
+#: ../mod_tiling/split.c:1113 ../mod_tiling/splitfloat.c:903
+msgid "Unable to split: not enough free space."
+msgstr "Nelze rozdìlit: nedostatek volného místa."
+
+#: ../mod_tiling/split.c:1864
+#, c-format
+msgid "Unable to get configuration for %s."
+msgstr "Nemohu získat nastavení pro %s."
+
+#: ../mod_tiling/split-stdisp.c:602 ../mod_tiling/split-stdisp.c:627
+msgid "Status display in bad split configuration."
+msgstr "Stavový øádek se nachází v chybné konfiguraci rozdìlení."
+
+#: ../mod_tiling/split-stdisp.c:667
+msgid "Status display badly located in split tree."
+msgstr "Stavový øádek je ve stromu rozdìlení umístìn na ¹patném místì."
+
+#: ../mod_tiling/ops.c:28
+msgid "Already detached"
+msgstr "Ji¾ je odpojen"
+
+#: ../mod_tiling/ops.c:142
+msgid "Not member of a group"
+msgstr "Není èlenem skupiny"
+
+#: ../mod_tiling/ops.c:147
+msgid "Manager group already has bottom"
+msgstr "Øídící skupina ji¾ je na dnì"
+
+#: ../mod_query/wedln.c:777
+msgid "history"
+msgstr "historie"
+
+#: ../mod_query/fwarn.c:34
+msgid "Error:\n"
+msgstr "Chyba:\n"
+
+#
+#: ../mod_menu/menu.c:537
+msgid "Empty menu."
+msgstr "Prázdné menu."
+
+#
+#: ../mod_sm/sm.c:110
+msgid "Failed to set session directory."
+msgstr "Selhalo nastavení adresáøe s relací."
+
+#: ../mod_sm/sm_session.c:86
+msgid "Too many ICE connections."
+msgstr "Pøíli¹ mnoho ICE spojení."
+
+#: ../mod_sm/sm_session.c:228
+msgid "Failed to save session state"
+msgstr "Selhalo ulo¾ení stavu sezení."
+
+#: ../mod_sm/sm_session.c:247
+msgid "Failed to request save-yourself-phase2 from session manager."
+msgstr "Selhalo vy¾ádání save-yourself-phase2 od správce sezení."
+
+#
+#: ../mod_sm/sm_session.c:296
+msgid "SESSION_MANAGER environment variable not set."
+msgstr "Promìnná SESSION_MANAGER není nastavena."
+
+#: ../mod_sm/sm_session.c:301
+msgid "Session Manager: IceAddConnectionWatch failed."
+msgstr "Správce sezení: IceAddConnectionWatch selhalo."
+
+#
+#: ../mod_sm/sm_session.c:326
+msgid "Unable to connect to the session manager."
+msgstr "Nemohu se pøipojit ke správci sezení."
+
+#: ../mod_sp/main.c:85
+msgid "Unable to create scratchpad."
+msgstr "Nemohu vytvoøit poznámkový blok."
+
+#: ../mod_statusbar/main.c:75
+msgid "reading a pipe"
+msgstr "ètu rouru"
+
+#: ../mod_statusbar/main.c:97
+msgid "ion-statusd launch timeout."
+msgstr "Èas spou¹tìní ion-statusd vypr¹el."
+
+#: ../mod_statusbar/main.c:160
+msgid "ion-statusd timed out."
+msgstr "Èas ion-statusd vypr¹el."
+
+#: ../mod_statusbar/statusbar.c:1046
+#, c-format
+msgid "[ %date || load: %load ] %filler%systray"
+msgstr "[ %date || vytí¾ení: %load ] %filler%systray"
+
+#: ../de/init.c:42
+#, c-format
+msgid "Border attribute %s sanity check failed."
+msgstr "Selhala kontrola atributu okraje %s."
+
+#: ../de/init.c:65
+#, c-format
+msgid "Unknown border style \"%s\"."
+msgstr "Neznámý styl okraje \"%s\"."
+
+#: ../de/init.c:97
+#, c-format
+msgid "Unable to allocate colour \"%s\"."
+msgstr "Nemohu alokovat barvu \"%s\"."
+
+#: ../de/init.c:160
+#, c-format
+msgid "Corrupt substyle table %d."
+msgstr "Poru¹ená tabulka stylù %d."
+
+#: ../de/init.c:193
+#, c-format
+msgid "Unknown text alignment \"%s\"."
+msgstr "Neznámý textový prvek \"%s\"."
+
+#: ../de/init.c:263
+#, c-format
+msgid "'based_on' for %s points back to the style itself."
+msgstr "Èást 'based_on' stylu %s ukazuje sama na sebe."
+
+#: ../de/init.c:266
+#, c-format
+msgid "Unknown base style \"%s\"."
+msgstr "Neznámý základní styl \"%s\"."
+
+#: ../de/font.c:47
+#, c-format
+msgid ""
+"Fontset for font pattern '%s' implements context dependent drawing, which is "
+"unsupported. Expect clutter."
+msgstr ""
+"Sada fontù pro vzor '%s' implementuje kontextovì závislé kreslení, co¾ není "
+"podporováno. Oèekávejte nesmysly."
+
+#: ../de/font.c:59
+#, c-format
+msgid "Could not load font \"%s\", trying \"%s\""
+msgstr "Nemohu nahrát font \"%s\", zkou¹ím \"%s\"."
+
+#: ../de/font.c:63
+msgid "Failed to load fallback font."
+msgstr "Nahrání zálo¾ního fontu selhalo."
+
+#: ../de/style.c:315
+#, c-format
+msgid "Style %s still in use [%d] but the module is being unloaded!"
+msgstr "Styl %s se stále pou¾ívá [%d], ale modul se ji¾ ru¹í z pamìti!"
+
+#: ../ion/ion.c:42 ../pwm/pwm.c:42
+msgid "X display to use"
+msgstr "X displej, který se má pou¾ít"
+
+#: ../ion/ion.c:45 ../pwm/pwm.c:45
+msgid "Configuration file"
+msgstr "Konfiguraèní soubor"
+
+#: ../ion/ion.c:48 ../pwm/pwm.c:48
+msgid "Add directory to search path"
+msgstr "Pøidá adresáø do prohledávané cesty"
+
+#: ../ion/ion.c:51 ../pwm/pwm.c:51
+msgid "Manage default root window/non-Xinerama screen only"
+msgstr "Spravuje pouze implicitní koøenové okno/obrazovku bez Xineramy"
+
+#: ../ion/ion.c:55
+msgid "Use Xinerama screen information (default: 1/yes)"
+msgstr "Pou¾ít Xineramu (implicitnì: 1/ano)"
+
+#: ../ion/ion.c:58 ../pwm/pwm.c:58
+msgid "Ignored: not compiled with Xinerama support"
+msgstr "Ignorováno: program je sestaven bez podpory Xineramy"
+
+#: ../ion/ion.c:62 ../pwm/pwm.c:62
+msgid "Name of session (affects savefiles)"
+msgstr "Jméno sezení (ovlivní místo ulo¾ení)"
+
+#: ../ion/ion.c:65 ../pwm/pwm.c:65
+msgid "Session manager client ID"
+msgstr "Klientské ID správce sezení"
+
+#: ../ion/ion.c:68 ../pwm/pwm.c:68
+msgid "Do not create startup error log and display it with xmessage."
+msgstr ""
+"Nevytváøet záznam o chybách pøi startu a nezobrazovat jej pomocí xmessage."
+
+#: ../ion/ion.c:72 ../pwm/pwm.c:72
+msgid "Show this help"
+msgstr "Zobrazí tuto nápovìdu"
+
+#: ../ion/ion.c:75 ../pwm/pwm.c:75
+msgid "Show program version"
+msgstr "Zobrazí verzi programu"
+
+#: ../ion/ion.c:78 ../pwm/pwm.c:78
+msgid "Show about text"
+msgstr "Zobrazí nìco o programu"
+
+#: ../ion/ion.c:93
+msgid "Could not get user configuration file directory."
+msgstr "Nemohu získat u¾ivatelùv adresáø s nastavením."
+
+#: ../ion/ion.c:107
+#, c-format
+msgid "%s/welcome.txt"
+msgstr "%s/welcome.cs.txt"
+
+#: ../ion/ion.c:140 ../pwm/pwm.c:87
+#, c-format
+msgid ""
+"Usage: %s [options]\n"
+"\n"
+msgstr ""
+"Pou¾ití: %s [volby]\n"
+"\n"
+
+#: ../ion/ion.c:200 ../pwm/pwm.c:150
+msgid "Invalid parameter to -xinerama."
+msgstr "Neplatný parametr pro -xinerama."
+
+#
+#: ../ion/ion.c:219 ../pwm/pwm.c:169
+msgid "Invalid command line."
+msgstr "Neplatná pøíkazová øádka."
+
+#: ../ion/ion.c:241
+msgid "Ion startup error log:\n"
+msgstr "Záznam chyb pøi startu Ionu:\n"
+
+#: ../ion/ion.c:252 ../pwm/pwm.c:202
+msgid "Refusing to start due to encountered errors."
+msgstr "Odmítám se spustit kvùli zaznamenaným chybám."
+
+#: ../pwm/pwm.c:55
+msgid "Use Xinerama screen information (default: 0/no)"
+msgstr "Pou¾ít Xineramu (implicitnì: 0/ne) "
+
+#: ../pwm/pwm.c:191
+msgid "PWM startup error log:\n"
+msgstr "Záznam chyb pøi startu PWM:\n"
+
+#: ../libextl/readconfig.c:86
+msgid "$HOME not set"
+msgstr "Promìnná $HOME není nastavená"
+
+#: ../libextl/readconfig.c:113
+msgid "User directory not set. Unable to set session directory."
+msgstr "U¾ivatelský adresáø není nastaven. Nemohu nastavit adresáø pro sezení."
+
+#: ../libextl/readconfig.c:254
+#, c-format
+msgid "Falling back to %s."
+msgstr "Nouzovì pou¾ívám %s."
+
+#: ../libextl/readconfig.c:474
+#, c-format
+msgid "Unable to create session directory \"%s\"."
+msgstr "Nemohu vytvoøit adresáø pro sezení \"%s\"."
+
+#: ../libextl/luaextl.c:117
+msgid "Lua stack full."
+msgstr "Lua zásobník je plný."
+
+#
+#: ../libextl/luaextl.c:143
+msgid "Unknown Lua error."
+msgstr "Neznámá Lua chyba."
+
+#: ../libextl/luaextl.c:490
+msgid "Stack trace:"
+msgstr "Výpis zásobníku:"
+
+#: ../libextl/luaextl.c:497
+#, c-format
+msgid ""
+"\n"
+"(Unable to get debug info for level %d)"
+msgstr ""
+"\n"
+"(Nemohu získat ladicí informace pro úroveò %d)"
+
+#: ../libextl/luaextl.c:515
+msgid ""
+"\n"
+"  [Skipping unnamed C functions.]"
+msgstr ""
+"\n"
+"  [Pøeskakuji nepojmenované C funkce.]"
+
+#: ../libextl/luaextl.c:566
+msgid "Internal error."
+msgstr "Interní chyba."
+
+#: ../libextl/luaextl.c:585
+msgid "Unable to initialize Lua."
+msgstr "Nemohu inicializovat Lua."
+
+#: ../libextl/luaextl.c:1336
+msgid ""
+"Too many return values. Use a C compiler that has va_copy to support more."
+msgstr ""
+"Pøíli¹ mnoho návratových hodnot. Pou¾ijte kompilátor C, který obsahuje "
+"va_copy."
+
+#: ../libextl/luaextl.c:1356
+msgid "Returned dead object."
+msgstr "Vrácen mrtvý objekt."
+
+#: ../libextl/luaextl.c:1359
+#, c-format
+msgid "Invalid return value (expected '%c', got lua type \"%s\")."
+msgstr "Neplatná návratová hodnota (oèekávána %c, obdr¾en lua typ \"%s\")."
+
+#: ../libextl/luaextl.c:1395 ../libextl/luaextl.c:1750
+msgid "Stack full."
+msgstr "Zásobník je plný."
+
+#: ../libextl/luaextl.c:1761
+#, c-format
+msgid "Argument %d to %s is a dead object."
+msgstr "Argument %d pro %s je mrtvý objekt."
+
+#: ../libextl/luaextl.c:1764
+#, c-format
+msgid ""
+"Argument %d to %s is of invalid type. (Argument template is '%s', got lua "
+"type %s)."
+msgstr ""
+"Argument %d pro %s má neplatný typ. (©ablona argumentu je '%s', dostal jsem "
+"lua typ %s)."
+
+#: ../libextl/luaextl.c:1827
+msgid "L1 call handler upvalues corrupt."
+msgstr "Obsluha volání L1 je poru¹ená."
+
+#: ../libextl/luaextl.c:1832
+msgid "Called function has been unregistered."
+msgstr "Volaná funkce byla byla odregistrována."
+
+#: ../libextl/luaextl.c:1843
+#, c-format
+msgid "Attempt to call an unsafe function \"%s\" in restricted mode."
+msgstr "Pokus o volání nebezpeèné funkce \"%s\" v omezeném re¾imu."
+
+#: ../libextl/luaextl.c:1956
+#, c-format
+msgid ""
+"Function '%s' has more parameters than the level 1 call handler can handle"
+msgstr "Funkce '%s' má víc parametrù, ne¾ lze zpracovat obsluhou v 1. úrovni"
+
+#
+#: ../libextl/luaextl.c:2373
+msgid "Maximal serialisation depth reached."
+msgstr "Dosa¾ena maximální hloubka serializace."
+
+#: ../libextl/luaextl.c:2394
+#, c-format
+msgid "Unable to serialise type %s."
+msgstr "Nemohu serializovat typ %s."
+
+#: ../libextl/luaextl.c:2425
+msgid "-- This file has been generated by Ion. Do not edit.\n"
+msgstr "-- Tento soubor byl vytvoøen Ionem. Neupravujte jej.\n"
+
+#: ../libextl/misc.c:17
+#, c-format
+msgid ""
+"Type checking failed in level 2 call handler for parameter %d (got %s, "
+"expected %s)."
+msgstr ""
+"Kontrola typu selhala ve druhé úrovni obsluhy volání pro parametr %d "
+"(obdr¾el %s, oèekával %s)."
+
+msgid "Grow-D"
+msgstr "Rùst dolù"
+
+msgid "Grow-U"
+msgstr "Rùst nahoru"
+
+msgid "Grow-R"
+msgstr "Rùst vpravo"
+
+msgid "Grow-L"
+msgstr "Rùst vlevo"
+
+msgid "Pos-BR"
+msgstr "Pozice PD"
+
+msgid "Pos-BL"
+msgstr "Pozice LD"
+
+msgid "Pos-TR"
+msgstr "Pozice PH"
+
+msgid "Pos-TL"
+msgstr "Pozice LH"
+
+msgid "Toggle floating dock."
+msgstr "Zapne plovoucí dok."
+
+msgid "Clear the menu's typeahead find buffer."
+msgstr "Vyma¾e buffer pro dopøedné hledání v menu."
+
+msgid "Select next/previous menu entry."
+msgstr "V menu vybere dal¹í/pøedchozí polo¾ku."
+
+#
+#
+msgid "Activate current menu entry."
+msgstr "Aktivuje vybranou polo¾ku v menu."
+
+msgid "Close the menu."
+msgstr "Zavøe menu."
+
+msgid "New tiling"
+msgstr "Vytvoøit dla¾dice"
+
+msgid "Detach"
+msgstr "Odpojit"
+
+msgid "Transpose"
+msgstr "Transponovat"
+
+msgid "Flip"
+msgstr "Obrátit"
+
+msgid "Split horizontally"
+msgstr "Rozdìlit horizontálnì"
+
+msgid "Split vertically"
+msgstr "Rozdìlit vertikálnì"
+
+msgid "At root"
+msgstr "Na koøenu"
+
+msgid "Below"
+msgstr "Dole"
+
+msgid "Above"
+msgstr "Nahoøe"
+
+msgid "At right"
+msgstr "Vpravo"
+
+msgid "At left"
+msgstr "Vlevo"
+
+msgid "Float split"
+msgstr "Plovoucí okraj"
+
+msgid "Destroy frame"
+msgstr "Znièit rám"
+
+msgid "Tile frame, if no tiling exists on the workspace"
+msgstr ""
+"Zmìní rám na dla¾dice (pokud zatím dla¾dice na pracovní plo¹e neexistují)."
+
+msgid "Detach window from tiled frame"
+msgstr "Odpojí okno od dla¾dicového rámu"
+
+msgid "Destroy current frame."
+msgstr "Znièí aktuální rám."
+
+msgid "Split current frame horizontally."
+msgstr "Rozdìlí rám horizontálnì."
+
+msgid "Go to frame above/below/right/left of current frame."
+msgstr "Pøejde do rámu nad/pod/vpravo/vlevo od aktuálního rámu."
+
+msgid "Split current frame vertically."
+msgstr "Rozdìlí rám vertikálnì."
+
+msgid "Failed to create statusbar."
+msgstr "Selhalo vytvoøení stavového øádku."
+
+msgid "Screen already has an stdisp. Refusing to create a statusbar."
+msgstr "Obrazovka ji¾ obsahuje stdisp. Odmítám vytvoøit stavový øádek."
+
+msgid "Screen not found."
+msgstr "Obrazovka nebyla nalezna."
+
+msgid "Failed to start ion-statusd."
+msgstr "Selhalo spu¹tìní ion-statusd."
+
+msgid "Errors starting ion-statusd:\n"
+msgstr "Chyba pøi startu ion-statusd:\n"
+
+msgid "Could not find %s"
+msgstr "Nemohu najít %s"
+
+msgid "ion-statusd quit."
+msgstr "ion-statusd konèí."
+
+msgid "Not a directory."
+msgstr "Není adresáøem."
+
+msgid "Invalid command"
+msgstr "Neplatný pøíkaz"
+
+msgid "Error in command string: "
+msgstr "Chyba v pøíkazu: "
+
+msgid "Error compiling guard: %s"
+msgstr "Chyba pøi sestavení strá¾ce: %s"
+
+msgid "Invalid guard %s."
+msgstr "Neplatná ochrana %s."
+
+msgid "Main menu:"
+msgstr "Hlavní menu:"
+
+msgid "Context menu:"
+msgstr "Kontextové menu:"
+
+msgid "Floating frame"
+msgstr "Plovoucí rám"
+
+msgid "Tiled frame"
+msgstr "Dla¾dicový rám"
+
+msgid "Tiling"
+msgstr "Dla¾dice"
+
+#
+msgid "Workspace"
+msgstr "Pracovní plocha"
+
+msgid "Screen"
+msgstr "Obrazovka"
+
+msgid "Frame"
+msgstr "Rám"
+
+msgid "Recursive table - unable to deepcopy"
+msgstr "Rekurzivní tabulka - nemohu provést hlubokou kopii"
+
+msgid ""
+"Making the following minimal emergency mappings:\n"
+"  F2 -> xterm\n"
+"  F11 -> restart\n"
+"  F12 -> exit\n"
+"  Mod1+C -> close\n"
+"  Mod1+K P/N -> WFrame.switch_next/switch_prev\n"
+msgstr ""
+"Vytváøím nouzové pøiøazení kláves:\n"
+"  F2 -> xterm\n"
+"  F11 -> restart\n"
+"  F12 -> exit\n"
+"  Mod1+C -> close\n"
+"  Mod1+K P/N -> WFrame.switch_next/switch_prev\n"
+
+msgid "Unable to append to non-table menu"
+msgstr "Nelze pøidat do netabulkového menu"
+
+msgid "Cannot save selection."
+msgstr "Nemohu ulo¾it výbìr."
+
+msgid "Save look selection in %s?"
+msgstr "Ulo¾it nastavení vzhledu do %s?"
+
+msgid "Too much result data"
+msgstr "Pøíli¹ mnoho výsledkù"
+
+msgid "Could not find client window %s."
+msgstr "Nemohu najít klientské okno %s."
+
+msgid "Cannot attach: different root windows."
+msgstr "Nemohu pøipojit: rùzná koøenová okna."
+
+msgid "Unknown error"
+msgstr "Neznámá chyba"
+
+msgid "Go to window:"
+msgstr "Jít do okna:"
+
+msgid "Attach window:"
+msgstr "Pøipojit okno:"
+
+msgid "Go to or create workspace:"
+msgstr "Vytvoøit nebo pøejít na pracovní plochu:"
+
+msgid "Exit Ion/Shutdown session (y/n)?"
+msgstr "Ukonèit Ion/ukonèit sezení (y/n)?"
+
+msgid "Restart Ion (y/n)?"
+msgstr "Restartovat Ion (y/n)?"
+
+msgid "Frame name:"
+msgstr "Název rámu:"
+
+msgid "Workspace name:"
+msgstr "Název pracovní plochy:"
+
+msgid "Edit file:"
+msgstr "Upravit soubor:"
+
+msgid "View file:"
+msgstr "Prohlédnout soubor:"
+
+msgid "Run:"
+msgstr "Spustit:"
+
+msgid "Failed to open ~/.ssh/known_hosts"
+msgstr "Selhalo otevøení ~/.ssh/known_hosts"
+
+msgid "Failed to open ~/.ssh/config"
+msgstr "Selhalo otevøení ~/.ssh/config"
+
+msgid "SSH to:"
+msgstr "SSH na:"
+
+msgid "Manual page (%s):"
+msgstr "Manuálová stránka (%s):"
+
+msgid "Lua code:"
+msgstr "Lua kód:"
+
+msgid "Unknown menu %s."
+msgstr "Neznámé menu %s."
+
+msgid "Missing submenu "
+msgstr "Chybìjící podmenu"
+
+msgid "%s:"
+msgstr "%s:"
+
+msgid "No entry '%s'"
+msgstr "®ádný záznam '%s'"
+
+msgid ""
+"\n"
+"%sClass: %s\n"
+"%sRole: %s\n"
+"%sInstance: %s\n"
+"%sXID: 0x%x"
+msgstr ""
+"\n"
+"%sTøída: %s\n"
+"%sRole: %s\n"
+"%sInstance: %s\n"
+"%sXID: 0x%x"
+
+msgid "Toggle scratchpad."
+msgstr "Zapne poznámkový blok."
+
+msgid "Programs"
+msgstr "Programy"
+
+msgid "Lock screen"
+msgstr "Zamknout obrazovku"
+
+msgid "Help"
+msgstr "Nápovìda"
+
+#
+msgid "Workspaces"
+msgstr "Pracovní plochy"
+
+msgid "New"
+msgstr "Nový"
+
+msgid "Styles"
+msgstr "Styly"
+
+msgid "Session"
+msgstr "Sezení"
+
+msgid "Close"
+msgstr "Zavøít"
+
+msgid "List"
+msgstr "Seznam"
+
+#
+msgid ""
+"Switch to n:th object (workspace, full screen client window) within current "
+"screen."
+msgstr ""
+"Pøepne se na n-tý objekt (pracovní plochu, celoobrazovkové okno) na aktuální "
+"obrazovce."
+
+#
+msgid "Switch to next/previous object within current screen."
+msgstr "Pøepne se na dal¹í/pøedchozí objekt v aktuální obrazovce."
+
+msgid "Go to first region demanding attention or previously active one."
+msgstr ""
+"Pøejde do prvního regionu vy¾adujícího pozornost nebo do pøedchozího "
+"aktivního."
+
+#
+msgid "Clear all tags."
+msgstr "Vyèistí v¹echny znaèky."
+
+#
+msgid "Go to n:th screen on multihead setup."
+msgstr "U víceobrazovkového nastavení pøejde na n-tou obrazovku."
+
+#
+msgid "Go to next/previous screen on multihead setup."
+msgstr "Ve víceobrazovkovém nastavení pøejde na dal¹í/pøedchozí obrazovku."
+
+#
+#
+msgid "Create a new workspace of chosen default type."
+msgstr "Vytvoøí novou pracovní plochu vybraného typu."
+
+msgid "Display the main menu."
+msgstr "Zobrazí hlavní menu."
+
+msgid "Display the window list menu."
+msgstr "Zobrazí menu se seznamem oken."
+
+msgid "Forward-circulate focus."
+msgstr "Cykluje vpøed."
+
+msgid "Backward-circulate focus."
+msgstr "Cykluje vzad."
+
+msgid "Raise focused object, if possible."
+msgstr "Pokud je to mo¾né, pøesune zamìøený objekt nahoru."
+
+#
+msgid ""
+"Nudge the client window. This might help with some programs' resizing "
+"problems."
+msgstr ""
+"Postrèí klientské okno. To mù¾e pomoci nìkterým programùm, které mají "
+"problémy se zmìnou velikosti."
+
+#
+msgid "Kill client owning the client window."
+msgstr "Ukonèí klienta vlastnícího klientské okno."
+
+#
+msgid ""
+"Send next key press to the client window. Some programs may not allow this "
+"by default."
+msgstr ""
+"Po¹le následující stisk klávesy klientskému oknu. Nìkteré programy to "
+"implicitnì nepovolují."
+
+msgid "Toggle client window group full-screen mode"
+msgstr "Pøepne skupinu klientského okna do celoobrazovkového re¾imu."
+
+msgid "Close current object."
+msgstr "Zavøe aktuální objekt."
+
+msgid "Query for manual page to be displayed."
+msgstr "Zeptá se na manuálovou stánku, kterou má zobrazit."
+
+msgid "Show the Ion manual page."
+msgstr "Zobrazí manuálovou stránku Ionu."
+
+#
+msgid "Run a terminal emulator."
+msgstr "Spustí emulátor terminálu."
+
+msgid "Query for command line to execute."
+msgstr "Zeptá se na pøíkaz, který má spustit."
+
+msgid "Query for Lua code to execute."
+msgstr "Zeptá se na lua kód, který má vykonat."
+
+msgid "Query for host to connect to with SSH."
+msgstr "Zeptá se na jméno poèítaèe, ke kterému se má pøipojit pomocí SSH."
+
+msgid "Query for file to edit."
+msgstr "Zeptá se na soubor, který chcete upravit."
+
+msgid "Query for file to view."
+msgstr "Zeptá se na soubor, který chcete zobrazit."
+
+msgid "Query for workspace to go to or create a new one."
+msgstr "Zeptá se na pracovní plochu, na kterou má pøejít, nebo ji vytvoøit."
+
+#
+msgid "Query for a client window to go to."
+msgstr "Dotá¾e se na klientské okno, do nìho¾ má pøejít."
+
+#
+msgid "Query for a client window to attach."
+msgstr "Dotá¾e se na klientské okno, které má pøipojit."
+
+msgid "Display context menu."
+msgstr "Zobrazí kontextové menu."
+
+msgid "Maximize the frame horizontally/vertically."
+msgstr "Maximalizuje rám horizontálnì/vertikálnì."
+
+msgid "Begin move/resize mode."
+msgstr "Zahájí re¾im pøesunu/zmìny velikosti."
+
+msgid "Switch the frame to display the object indicated by the tab."
+msgstr "Pøepne rám, aby zobrazoval objekt urèený zálo¾kou."
+
+msgid "Resize the frame."
+msgstr "Zmìní velikost rámu."
+
+msgid "Move the frame."
+msgstr "Pøesune rám."
+
+msgid "Move objects between frames by dragging and dropping the tab."
+msgstr "Pøesune objekty mezi rámy pomocí ta¾ení za zálo¾ku."
+
+msgid "Tag current object within the frame."
+msgstr "Oznaèí aktuální objekt v rámu."
+
+msgid "Switch to n:th object within the frame."
+msgstr "Pøepne se do n-tého objektu v rámu."
+
+msgid "Switch to next/previous object within the frame."
+msgstr "Pøepne se na dal¹í/pøedchozí objekt v rámu."
+
+msgid "Move current object within the frame left/right."
+msgstr "Pøesune objekt v rámu vlevo/vpravo."
+
+msgid "Attach tagged objects to this frame."
+msgstr "Pøipojí oznaèené objekty k tomuto rámu."
+
+msgid "Toggle shade mode"
+msgstr "Pøepne stínový re¾im"
+
+#
+msgid "Raise the frame."
+msgstr "Pøesune rám do popøedí."
+
+msgid "Lower the frame."
+msgstr "Pøesune rám do pozadí"
+
+msgid "Cancel the resize mode."
+msgstr "Zru¹í re¾im zmìny velikosti."
+
+msgid "End the resize mode."
+msgstr "Ukonèí re¾im zmìny velikosti."
+
+#
+msgid "Grow in specified direction."
+msgstr "Roste v zadaném smìru."
+
+msgid "Shrink in specified direction."
+msgstr "Zmen¹uje v zadaném smìru."
+
+#
+msgid "Move in specified direction."
+msgstr "Posune se v zadaném smìru."
+
+msgid "About Ion"
+msgstr "O Ionu"
+
+msgid "XTerm"
+msgstr "XTerm"
+
+msgid "W3M"
+msgstr "W3M"
+
+msgid "Rxvt"
+msgstr "Rxvt"
+
+msgid "Opera"
+msgstr "Opera"
+
+msgid "Links"
+msgstr "Links"
+
+msgid "Konqueror"
+msgstr "Konqueror"
+
+msgid "Dillo"
+msgstr "Dillo"
+
+#
+msgid "Run..."
+msgstr "Spustit..."
+
+msgid "Save"
+msgstr "Ulo¾it"
+
+msgid "Restart"
+msgstr "Restartovat"
+
+msgid "Restart TWM"
+msgstr "Restartovat TWM"
+
+msgid "Exit"
+msgstr "Ukonèit"
+
+msgid "Kill"
+msgstr "Ukonèit"
+
+msgid "Toggle tag"
+msgstr "(Od)znaèit"
+
+msgid "Attach tagged"
+msgstr "Pøipojit oznaèené"
+
+msgid "Clear tags"
+msgstr "Vyèistit znaèky"
+
+msgid "Window info"
+msgstr "Informace o oknì"
+
+#
+msgid "New workspace"
+msgstr "Nová pracovní plocha"
+
+msgid "New empty workspace"
+msgstr "Nová prázdná pracovní plocha"
+
+#
+msgid "Close workspace"
+msgstr "Zavøít pracovní plochu"
+
+msgid "Move one character forward/backward."
+msgstr "Posune se o znak vpøed/vzad."
+
+msgid "Go to end/beginning."
+msgstr "Skoèí na konec/zaèátek."
+
+msgid "Skip one word forward/backward."
+msgstr "Pøeskoèí slovo dopøedu/dozadu."
+
+msgid "Delete next character."
+msgstr "Sma¾e následující znak."
+
+msgid "Delete previous character."
+msgstr "Sma¾e pøedchozí znak."
+
+msgid "Delete one word forward/backward."
+msgstr "Sma¾e slovo pøed/za kurzorem."
+
+msgid "Delete to end of line."
+msgstr "Sma¾e text do konce øádky."
+
+msgid "Delete the whole line."
+msgstr "Sma¾e celý øádek."
+
+msgid "Transpose characters."
+msgstr "Transponuje znaky."
+
+msgid "Select next/previous (matching) history entry."
+msgstr "Vybere dal¹í/pøedchozí (odpovídající) polo¾ku historie."
+
+msgid "Paste from the clipboard."
+msgstr "Vlo¾í ze schránky."
+
+#
+msgid "Set mark/begin selection."
+msgstr "Nastaví znaèku/zaèátek výbìru."
+
+#
+msgid "Cut selection."
+msgstr "Vyjme výbìr."
+
+#
+msgid "Copy selection."
+msgstr "Zkopíruje výbìr."
+
+#
+msgid "Clear mark/cancel selection."
+msgstr "Sma¾e znaèku/zru¹í výbìr."
+
+msgid "Try to complete the entered text or cycle through completions."
+msgstr "Zkusí doplnit zadaný text nebo bude cyklovat mezi mo¾nostmi."
+
+msgid "Complete from history"
+msgstr "Doplní z historie."
+
+msgid "Close the query and execute bound action."
+msgstr "Zavøe dotaz a spustí svázanou akci."
+
+msgid "Close the query/message box, not executing bound actions."
+msgstr "Zavøe okno s dotazem/zprávou, ani¾ by se spustily svázané akce.."
+
+msgid "Scroll the message or completions up/down."
+msgstr "Roluje zprávu nebo seznam dokonèení nahoru/dolù."
+
+msgid "press"
+msgstr "stisk"
+
+msgid "click"
+msgstr "kliknutí"
+
+msgid "drag"
+msgstr "ta¾ení"
+
+msgid "double click"
+msgstr "dvojité kliknutí"
+
+msgid "%s %s"
+msgstr "%s %s"
+
+msgid "%s %s at %s"
+msgstr "%s %s na %s"
+
+#~ msgid "Unable to re-initialise workspace. Destroying."
+#~ msgstr "Nemohu znovu inicializovat pracovní plochu. Nièím."
+
+#~ msgid "Refusing to close non-empty workspace."
+#~ msgstr "Odmítám zavøít neprázdnou pracovní plochu."
+
+#~ msgid "Malfunctioning placement hook; condition #%d."
+#~ msgstr "Nefunkèní volání pro umístìní; podmínka #%d."
+
+#~ msgid "Resize the area."
+#~ msgstr "Zmìní velikost oblasti."
+
+#~ msgid "Refresh list"
+#~ msgstr "Obnovit seznam"
+
+#~ msgid "Could not find a complete workspace class. Please load some modules."
+#~ msgstr "Nemohu najít úplnou tøídu pracovní plochy. Zaveïte nìjaké moduly."
+
+#~ msgid "Failed to rescue some client windows."
+#~ msgstr "Záchrana nìkterých klientských oken selhala."
+
+#~ msgid "Same manager."
+#~ msgstr "Stejný mana¾er."
+
+#
+#~ msgid "Invalid split type parameter."
+#~ msgstr "Neplatný parametr typu rozdìlení."
+
+#
+#~ msgid "Failure to create a new frame."
+#~ msgstr "Chyba pøi vytváøení nového rámu."
+
+#
+#~ msgid "Region not managed by the workspace."
+#~ msgstr "Oblast není spravována pracovní plochou."
+
+#~ msgid "No geometry specified."
+#~ msgstr "Nebyla zadána geometrie."
+
+#~ msgid "none"
+#~ msgstr "¾ádná"
+
+#
+#~ msgid "mail"
+#~ msgstr "po¹ta"
+
+#~ msgid ""
+#~ "\n"
+#~ "Transients:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "Doèasná okna:\n"
+
+#~ msgid "Workspace type (%s):"
+#~ msgstr "Typ pracovní plochy (%s):"
+
+#~ msgid "Go to previous active object."
+#~ msgstr "Pøejde na pøedchozí aktivní objekt."
+
+#
+#
+#~ msgid "Toggle fullscreen mode of current client window."
+#~ msgstr "Zapne celoobrazovkový re¾im u aktuálního klientského okna."
+
+#~ msgid "WStatusBar expected."
+#~ msgstr "Oèekáván WStatusBar."
+
+#~ msgid "Backwards-circulate focus and raise the newly focused frame."
+#~ msgstr "Zpìtnì cykluje mezi rámy a pøesune je nahoru."
+
+#~ msgid "(Un)stick"
+#~ msgstr "(Pøi/Od)lepit"
+
+#
+#~ msgid "Query for a client window to attach to active frame."
+#~ msgstr "Zeptá se na klientské okno, které se má pøipojit k aktivnímu rámu."
+
+#~ msgid "Raise/lower active frame."
+#~ msgstr "Pøesune aktivní rám do popøedí/pozadí."
+
+#~ msgid "Unable to create workspace: no screen."
+#~ msgstr "Nemohu vytvoøit pracovní plochu: obrazovka neexistuje."
+
+#~ msgid "load"
+#~ msgstr "vytí¾ení"
+
+#~ msgid "Mozilla Firefox"
+#~ msgstr "Mozilla Firefox"
+
+#~ msgid "Circulate focus and raise the newly focused frame."
+#~ msgstr "Cykluje mezi rámy a pøesune je nahoru."
+
+#~ msgid "Restart PWM"
+#~ msgstr "Restartovat PWM"
+
+#~ msgid "(Un)tag"
+#~ msgstr "(Od)znaèit"
+
+#
+#~ msgid "Could not find a root window."
+#~ msgstr "Nemohu najít koøenové okno."
+
+#~ msgid "Caught fatal signal %d. Dying without deinit."
+#~ msgstr "Zachycen signál %d. Umírám bez ulo¾ení."
+
+#~ msgid "Caught signal %d. Dying."
+#~ msgstr "Zachycen signál %d. Umírám."
+
+#
+#~ msgid "Object destroyed while deferred actions are still pending."
+#~ msgstr "Objekt byl zru¹en, zatímco pozdr¾eé akce èekají na vyøízení."
+
+#~ msgid "Unable to rescue \"%s\"."
+#~ msgstr "Nemohu zachránit \"%s\"."
+
+#~ msgid "Frame is not empty."
+#~ msgstr "Rám není prázdný."
+
+#
+#~ msgid "No function given."
+#~ msgstr "Nebyla zadána ¾ádná funkce."
+
+#
+#~ msgid "Frame not managed by the workspace."
+#~ msgstr "Rám není spravován pracovní plochou."
+
+#~ msgid "Failed to rescue managed objects."
+#~ msgstr "Záchrana spravovaných objektù selhala."
+
+#~ msgid "Split"
+#~ msgstr "Rozdìlit"
+
+#~ msgid "Failed to create a timer for statusbar."
+#~ msgstr "Selhalo vytvoøení èasovaèe pro stavový pruh."
+
+#~ msgid "Vertically at root"
+#~ msgstr "Vertikálnì na koøenu"
+
+#~ msgid "Flip&transpose"
+#~ msgstr "Obrátit&transponovat"
+
+#~ msgid "Horizontally at root"
+#~ msgstr "Horizontálnì na koøenu"
diff --git a/po/de.po b/po/de.po
new file mode 100644 (file)
index 0000000..a4f05ef
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,1667 @@
+#
+# German language translations for Ion3.
+#
+# Copyright (c) Schott Robert 2005.
+#
+# This file is distributed under the same license as the Ion3 package.
+#
+
+msgid ""
+msgstr ""
+"Project-Id-Version: Ion3\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2004-10-11 18:33+0300\n"
+"PO-Revision-Date: 2005-02-13 23:50+0300\n"
+"Last-Translator: Schott Robert <r.schott at casepool.de>\n"
+"Language-Team: none\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../ioncore/conf-bindings.c:96
+msgid "Insane key combination."
+msgstr "Unsinnige Tastenkombination."
+
+#: ../ioncore/conf-bindings.c:100
+msgid "Could not convert keysym to keycode."
+msgstr "Konnte 'keysym' nicht in 'keycode' umwandeln."
+
+#: ../ioncore/conf-bindings.c:111
+#, c-format
+msgid "Unknown button \"%s\"."
+msgstr "Unbekannte Taste \"%s\"."
+
+#: ../ioncore/conf-bindings.c:116
+msgid "Insane button combination."
+msgstr "Unsinnige Tastenkombination."
+
+#: ../ioncore/conf-bindings.c:123 ../ioncore/conf-bindings.c:130
+msgid "Insane modifier combination."
+msgstr "Unsinnige Modifikationskombination."
+
+#: ../ioncore/conf-bindings.c:168
+#, c-format
+msgid "Can not wait on modifiers when no modifiers set in \"%s\"."
+msgstr ""
+"Kann nicht auf Modifikator warten, wenn kein Modifikator in \"%s\" gesetzt ist."
+
+#: ../ioncore/conf-bindings.c:186
+#, c-format
+msgid "Unable to add binding %s."
+msgstr "Die Bindung %s konnte nicht eingestellt werden."
+
+#: ../ioncore/conf-bindings.c:191
+#, c-format
+msgid "Unable to remove binding %s."
+msgstr "Die Bindung %s konnte nicht entfernt werden."
+
+#: ../ioncore/conf-bindings.c:230
+#, c-format
+msgid "Unable to add submap for binding %s."
+msgstr "Die 'submap' für die Bindung %s konnte nicht eingestellt werden."
+
+#
+#: ../ioncore/conf-bindings.c:260
+msgid "Binding type not set."
+msgstr "Bindungsart nicht gesetzt."
+
+#: ../ioncore/conf-bindings.c:270
+#, c-format
+msgid "Unknown binding type \"%s\"."
+msgstr "Unbekannte Bindungsart \"%s\"."
+
+#: ../ioncore/conf-bindings.c:291
+#, c-format
+msgid "Unknown area \"%s\" for binding %s."
+msgstr "Unbekannter Bereich \"%s.\" für die Bindung %s."
+
+#: ../ioncore/conf-bindings.c:332
+#, c-format
+msgid "Unable to get bindmap entry %d."
+msgstr "Der 'bindmap' Eintrag %d wurde nicht gefunden."
+
+#: ../ioncore/conf-bindings.c:374
+msgid "Unable to convert keysym to string."
+msgstr "Konnte keysym nicht in Zeichenkette umwandeln."
+
+#: ../ioncore/conf-bindings.c:388
+msgid "Unable to convert button to string."
+msgstr "Konnte Taste nicht in Zeichenkette umwandeln."
+
+#
+#: ../ioncore/event.c:78
+msgid "Time request from X server failed."
+msgstr "Zeitanfrage des X Servers fehlgeschlagen."
+
+#
+#: ../ioncore/exec.c:101
+msgid "Could not find a root window."
+msgstr "Konnte kein Wurzelfenster finden."
+
+#: ../ioncore/exec.c:134
+msgid "reading a pipe"
+msgstr "Weiterleitung wird ausgelesen"
+
+#
+#: ../ioncore/exec.c:296
+msgid "Not saving state: running under session manager."
+msgstr "Nicht gespeicherter Zustand: läuft im Sitzungsmanager."
+
+#: ../ioncore/strings.c:107 ../ioncore/strings.c:143
+msgid "Invalid multibyte string."
+msgstr "Unzulässige multibyte Zeichenkette."
+
+#: ../ioncore/strings.c:234
+#, c-format
+msgid "Error compiling regular expression: %s"
+msgstr "Fehler beim erstellen der regulären Ersetzung: %s"
+
+#: ../ioncore/modules.c:158
+msgid "Invalid module name."
+msgstr "Unzulässiger Modulname."
+
+#
+#: ../ioncore/modules.c:170
+msgid "The module is already loaded."
+msgstr "Das Modul wurde bereits geladen."
+
+#: ../ioncore/modules.c:185
+msgid ""
+"Module version information not found or version mismatch. Refusing to use."
+msgstr ""
+"Information zur Modulversion wurde nicht gefunden oder die Versionen stimmen nicht überein."
+"Benutzung wurde abgelehnt."
+
+#: ../ioncore/modules.c:196
+#, c-format
+msgid "Unable to initialise module %s."
+msgstr "Module %s konnte nicht initialisiert werden."
+
+#: ../ioncore/modules.c:220 ../../libextl/readconfig.c:368
+#, c-format
+msgid "Unable to find '%s' on search path."
+msgstr "Im Suchpfad konnte '%s' nicht gefunden werden."
+
+#: ../ioncore/modules.c:291
+msgid "Unknown module."
+msgstr "Unbekanntes Modul."
+
+#: ../ioncore/modules.c:299
+msgid "Unable to initialise module."
+msgstr "Module konnte nicht initialisiert werden."
+
+#: ../ioncore/modules.c:344
+msgid "No module to load given."
+msgstr "Kein Modul zum laden vorhanden."
+
+#: ../ioncore/screen.c:301
+msgid "act: "
+msgstr "act: "
+
+#: ../ioncore/screen.c:490
+msgid "Invalid offset."
+msgstr "Ungültiges offset."
+
+#: ../ioncore/screen.c:535
+msgid "Could not find a complete workspace class. Please load some modules."
+msgstr ""
+"Konnte keine komplette Workspace-Klasse finden."
+"Laden Sie bitte einige Module."
+
+#: ../ioncore/screen.c:545
+#, c-format
+msgid "Unable to create a workspace on screen %d."
+msgstr "Konnte keinen Arbeitsbereich auf dem Schirm %d erzeugen."
+
+#: ../ioncore/signal.c:291
+#, c-format
+msgid "Caught fatal signal %d. Dying without deinit."
+msgstr ""
+"Abbruch Signal %d aufgefangen. Beendet ohne deinit."
+
+#: ../ioncore/signal.c:300
+#, c-format
+msgid "Caught signal %d. Dying."
+msgstr "Signal %d wurde aufgefangen. Beendet."
+
+#: ../ioncore/sizehint.c:150
+msgid "Invalid client-supplied width/height increment."
+msgstr "Client lieferte ungültige Breiten/Höhen - Schrittweite."
+
+#: ../ioncore/sizehint.c:158
+msgid "Invalid client-supplied aspect-ratio."
+msgstr "Client lieferte ungültiges Seitenverhältnis."
+
+#: ../ioncore/ioncore.c:66
+msgid ""
+"This program is free software; you can redistribute it and/or\n"
+"modify it under the terms of the GNU Lesser General Public\n"
+"License as published by the Free Software Foundation; either\n"
+"version 2.1 of the License, or (at your option) any later version.\n"
+"\n"
+"This program is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
+"Lesser General Public License for more details.\n"
+msgstr ""
+"Dieses Programm ist freie Software; Sie können es weiter verteilen und/oder\n"
+"modifizieren solange es den Bedingungen der \"GNU Lesser General Public\"\n"
+"Lizenz entspricht. Veröffentlicht durch die \"Free Software Foundation\"; jede\n"
+"Lizenz der Version 2.1, oder (nach eigener Wahl) jede spätere Version.\n"
+"\n"
+"Dieses Programm wird mit der Hoffnung das es nützlich ist verteilt, \n"
+"aber OHNE JEGLICHE GARANTIE; ohne jede Garantie von MARKTFÄHIGKEIT oder\n"
+"EIGNUNG ZU EINEM BESTIMMTEN ZWECK. Siehe die \"GNU Lesser General Public\"\n"
+"Lizenz für detailliertere Angaben.\n"
+
+#: ../ioncore/ioncore.c:385
+#, c-format
+msgid "Could not connect to X display '%s'"
+msgstr "Konnte keine Verbindung zur X Anzeige '%s' herstellen."
+
+#: ../ioncore/ioncore.c:438
+msgid "Could not find a screen to manage."
+msgstr "Konnte keinen Bildschirm für diese Aktion finden."
+
+#: ../ioncore/xic.c:43
+msgid "Failed to open input method."
+msgstr "Öffnen der Eingabemethode ist fehlgeschlagen."
+
+#: ../ioncore/xic.c:48
+msgid "Input method doesn't support any style."
+msgstr "Die Eingabemethode unterstützt keine Art von Styles"
+
+#: ../ioncore/xic.c:63
+msgid "input method doesn't support my preedit type."
+msgstr "Die Eingabemethode unterstützt nicht meine Art von 'preedit'."
+
+#: ../ioncore/xic.c:91
+msgid "Failed to create input context."
+msgstr "Erstellen des Eingabezusammenhangs ist fehlgeschlagen. "
+
+#: ../ioncore/clientwin.c:376
+#, c-format
+msgid "The transient_for hint for \"%s\" points to itself."
+msgstr "Der 'transient_for' Tip für \"%s\" zeigt auf sich selbst."
+
+#: ../ioncore/clientwin.c:380
+#, c-format
+msgid ""
+"Client window \"%s\" has broken transient_for hint. (\"Extended WM hints\" "
+"multi-parent brain damage?)"
+msgstr ""
+"Client Fenster \"%s\" hat einen fehlerhaften 'transient_for'-Tip "
+"(\"Erweiterte WM Tips\" *** Gehirnschaden?)"
+
+#: ../ioncore/clientwin.c:385
+#, c-format
+msgid "The transient_for window for \"%s\" is not on the same screen."
+msgstr "Das 'transient_for'-Fenster für \"%s\" ist nicht auf dem gleichen Schirm."
+
+#: ../ioncore/clientwin.c:405 ../ioncore/clientwin.c:492
+#: ../ioncore/clientwin.c:1554
+#, c-format
+msgid "Window %#x disappeared."
+msgstr "Fenster %#x verschwunden."
+
+#: ../ioncore/clientwin.c:512
+msgid "Unable to find a matching root window!"
+msgstr "Unmöglich ein passendes Wurzelfenster zu finden."
+
+#: ../ioncore/clientwin.c:547
+#, c-format
+msgid "Unable to manage client window %#x."
+msgstr "Unmöglich die Client Fenster %#x zu bearbeiten."
+
+#: ../ioncore/clientwin.c:597
+msgid "Changes is WM_TRANSIENT_FOR property are unsupported."
+msgstr "Änderung der 'ist WM_TRANSIENT_FOR' Eigenschaft wird nicht unterstützt."
+
+#
+#: ../ioncore/clientwin.c:874
+msgid "Client does not support the WM_DELETE protocol."
+msgstr "Client unterstützt nicht das WM_DELETE Protocol."
+
+#: ../ioncore/clientwin.c:1103 ../mod_ionws/ionws.c:76
+#: ../mod_floatws/floatws.c:103
+#, c-format
+msgid "Error reparenting %s."
+msgstr "Fehler wieder herstellen %s."
+
+#: ../ioncore/clientwin.c:1560
+msgid "Saved client window does not want to be managed."
+msgstr "Gespeichertes Client Fenster braucht nicht behandelt werden."
+
+#
+#: ../ioncore/colormap.c:96
+msgid "Unable to store colourmap watch info."
+msgstr "Unmöglich die 'colourmap' Überwachungsinformation zu speichern."
+
+#: ../ioncore/region.c:36
+msgid "Creating region with negative width or height!"
+msgstr "Gebiet mit negativer Breite oder Höhe erzeugt!"
+
+#: ../ioncore/region.c:84
+#, c-format
+msgid "Destroying object \"%s\" with client windows as children."
+msgstr "Zerstörtes Objekt \"%s\", mit Client Fenstern."
+
+#: ../ioncore/attach.c:80
+msgid "Unable to reparent."
+msgstr "Unmöglich wieder herzustellen."
+
+#: ../ioncore/attach.c:112 ../ioncore/frame-pointer.c:295
+#, c-format
+msgid "Attempt to make region %s manage its ancestor %s."
+msgstr "Versuche Bereich %s zu erstellen und seine Vorläufer %s zu behandeln."
+
+#
+#: ../ioncore/defer.c:93
+msgid "Object destroyed while deferred actions are still pending."
+msgstr "Objekt zerstört während zurückgestellte Aktionen noch bevorstehen."
+
+#: ../ioncore/manage.c:90
+msgid "Unable to find a screen for a new client window."
+msgstr "Konnte keinen Screen für ein neues Client Fenster finden."
+
+#: ../ioncore/manage.c:187
+#, c-format
+msgid "Unable to rescue \"%s\"."
+msgstr "Konnte \"%s\" nicht retten."
+
+#: ../ioncore/rootwin.c:222
+#, c-format
+msgid "Unable to redirect root window events for screen %d."
+msgstr "Die Ereignisse für das Wurzelfenster des Screens %d können  nicht umgeleitet werden."
+
+#: ../ioncore/rootwin.c:316
+msgid "Xinerama sanity check failed; overlapping screens detected."
+msgstr "Xinerama Zustandsüberprüfung fehlgeschlagen; überlappende Screens gefunden."
+
+#: ../ioncore/rootwin.c:342
+msgid "Don't know how to get Xinerama information for multiple X root windows."
+msgstr "Weiß nicht wie die Xinerama Information für multiple X Wurzelfenster zu bekommen ist."
+
+#: ../ioncore/rootwin.c:378
+#, c-format
+msgid "Unable to setup Xinerama screen %d."
+msgstr "Konnte Xinerama Screen %d nicht einrichten."
+
+#: ../ioncore/rootwin.c:389
+#, c-format
+msgid "Unable to setup X screen %d."
+msgstr "Konnte X Screen %d nicht einrichten."
+
+#: ../ioncore/names.c:90
+#, c-format
+msgid "Corrupt instance number %s."
+msgstr "Unbrauchbare Instanznummer %s."
+
+#: ../ioncore/saveload.c:55
+#, c-format
+msgid "Unknown class \"%s\", cannot create region."
+msgstr "Unbekannte Klasse \"%s\", Bereich konnte nicht erzeugt werden."
+
+#: ../ioncore/saveload.c:124
+#, c-format
+msgid ""
+"There were errors loading layout. Backing up current layout savefile as\n"
+"%s.\n"
+"If you are _not_ running under a session manager and wish to restore your\n"
+"old layout, copy this backup file over the layout savefile found in the\n"
+"same directory while Ion is not running and after having fixed your other\n"
+"configuration files that are causing this problem. (Maybe a missing\n"
+"module?)\n"
+msgstr ""
+"Fehler beim Laden des Layouts. Sichere jetzige Layoutsicherungsdatei als\n"
+"%s.\n"
+"Wenn Sie KEINEN Sitzungsmanager gestartet haben, können sie ihr altes Layout wieder herstellen.\n"
+"Kopieren sie die Sicherungsdatei über die aktuelle Layoutsicherungsdatei welche im selben Verzeichnis\n"
+"zu finden ist.\n"
+"Ion kann neu gestartet werden, nachdem alle anderen Konfigurationsdateien überprüft wurden,\n"
+"welche zu dem Problem beführt haben. (Eventuell fehlende Module?)\n"
+
+#: ../ioncore/saveload.c:167
+msgid "Unable to get file for layout backup."
+msgstr "Konnte keine Datei für die Layoutsicherung finden."
+
+#: ../ioncore/saveload.c:171
+#, c-format
+msgid "Backup file %s already exists."
+msgstr "Sicherungsdatei %s ist bereits vorhanden."
+
+#: ../ioncore/saveload.c:177
+msgid "Failed backup."
+msgstr "Sicherung fehlgeschlagen."
+
+#
+#: ../ioncore/saveload.c:182
+msgid "Unable to initialise layout on any screen."
+msgstr "Konnte für keinen Screen ein Layout initialisieren."
+
+#: ../ioncore/saveload.c:202
+#, c-format
+msgid "Unable to get configuration for screen %d."
+msgstr "Konnte für Screen %d keine Konfiguration finden."
+
+#: ../ioncore/saveload.c:215
+msgid "Unable to save layout."
+msgstr "Konnte Layout nicht sichern."
+
+#: ../ioncore/frame.c:122
+msgid "Frame is not empty."
+msgstr "Frame ist nicht leer."
+
+#: ../ioncore/conf.c:134
+msgid "User directory can not be set."
+msgstr "Benutzerverzeichnis konnte nicht gesetzt werden."
+
+#: ../ioncore/conf.c:197
+msgid "Some bindmaps were empty, loading ioncore-efbb."
+msgstr "Einige 'bindmaps' sind leer, lade 'ioncore-efbb'."
+
+#: ../ioncore/fullscreen.c:100
+msgid "Failed to enter full screen mode."
+msgstr "Vollbildmode fehlgeschlagen."
+
+#: ../ioncore/fullscreen.c:158
+msgid ""
+"WClientWin failed to return from full screen mode; remaining manager or "
+"parent from previous location refused to manage us."
+msgstr ""
+"'WClientWin' konnte nicht aus dem Vollbildmode zurückkehren;"
+"Der vorhandene Manager oder der Elternteil der vorherigen Position verhinterten das."
+
+#
+#: ../ioncore/mplex.c:1247
+msgid "Invalid position setting."
+msgstr "Ungültige Positionseinstellung."
+
+#
+#: ../ioncore/mplex.c:1280
+msgid "Invalid action setting."
+msgstr "Ungültige Aktionseinstellung."
+
+#: ../ioncore/gr.c:119
+#, c-format
+msgid "Drawing engine %s is not registered!"
+msgstr "Zeichenfunktion %s ist nicht registriert."
+
+#: ../ioncore/gr.c:138
+#, c-format
+msgid "Unable to find brush for style '%s'."
+msgstr "Konnte Pinsel für Style '%s' nicht finden."
+
+#: ../ioncore/gr.c:409
+msgid "No drawing engines loaded, trying \"de\"."
+msgstr "Keine Zeichenfunktionen geladen, versuche \"de\"."
+
+#
+#: ../ioncore/hooks.c:219
+msgid "No function given."
+msgstr "Keine Funktion gegeben."
+
+#: ../mod_ionws/ionws.c:311
+msgid "Unable to create a node for status display."
+msgstr "Konnte keinen Knoten für die Statusanzeige erstellen."
+
+#: ../mod_ionws/ionws.c:323
+msgid "Unable to create new split for status display."
+msgstr "Konnte keine neu Teilung für die Statusanzeige erzeugen."
+
+#
+#: ../mod_ionws/ionws.c:742
+msgid "Frame not managed by the workspace."
+msgstr "Frame wird nicht durch den Arbeitsbereich behandelt."
+
+#
+#: ../mod_ionws/ionws.c:747
+msgid "Invalid direction parameter."
+msgstr "Unzulässiger Richtungsparameter."
+
+#
+#: ../mod_ionws/ionws.c:765
+msgid "Unable to split."
+msgstr "Unmöglich zu Teilen."
+
+#: ../mod_ionws/ionws.c:803
+msgid "Nil frame."
+msgstr "'NIL'(leeres) Frame"
+
+#: ../mod_ionws/ionws.c:807
+msgid "The frame is not managed by the workspace."
+msgstr "Das Frame wird nicht durch den Arbeitsbereich behandelt."
+
+#: ../mod_ionws/ionws.c:812
+msgid "Frame may not be destroyed."
+msgstr "Frame darf nicht zerstört werden."
+
+#: ../mod_ionws/ionws.c:817
+msgid "Failed to rescue managed objects."
+msgstr "Rettung der 'managed objects' fehlgeschlagen."
+
+#: ../mod_ionws/ionws.c:1028
+msgid "Nil parameter."
+msgstr " 'NIL' (leer) Parameter"
+
+#: ../mod_ionws/ionws.c:1033
+msgid "Manager doesn't match."
+msgstr "Manager passt nicht."
+
+#
+#: ../mod_ionws/ionws.c:1055
+msgid "Could not get split tree."
+msgstr "Kann Teilungsbaum nicht finden."
+
+#: ../mod_ionws/ionws.c:1076
+msgid "Workspace already has a status display node."
+msgstr "Arbeitsbereich besitzt schon eine Knoten für die Statusanzeige."
+
+#
+#: ../mod_ionws/ionws.c:1126
+msgid "Missing region parameters."
+msgstr "Fehlende Bereichsparameter."
+
+#: ../mod_ionws/ionws.c:1162 ../mod_ionws/splitfloat.c:783
+msgid "Invalid direction."
+msgstr "Ungültige Richtung."
+
+#
+#: ../mod_ionws/ionws.c:1236
+msgid "No split type given."
+msgstr "Kein Teilungstyp gegeben."
+
+#: ../mod_ionws/ionws.c:1249
+msgid "Unknown split type."
+msgstr "Unbekannter Teilungstype."
+
+#
+#
+#: ../mod_ionws/ionws.c:1289
+msgid "The workspace is empty."
+msgstr "Der Arbeitsbereich ist leer."
+
+#: ../mod_ionws/placement.c:97 ../mod_panews/placement.c:299
+#, c-format
+msgid ""
+"Ooops... could not find a region to attach client window to on workspace %s."
+msgstr "Ooops ... konnte keinen Bereich finden um das Client Fenster an den Arbeitsbereich %s einzufügen."
+
+#: ../mod_ionws/split.c:906
+msgid "REGION_RQGEOM_TRYONLY unsupported for status display."
+msgstr "'REGION_RQGEOM_TRYONLY' wird nicht unterstützt für die Statusanzeige."
+
+#
+#: ../mod_ionws/split.c:979
+msgid "Invalid node."
+msgstr "Ungültiger Knoten."
+
+#
+#: ../mod_ionws/split.c:1044
+msgid "Splitting the status display is not allowed."
+msgstr "Teilung der Statusanzeige ist nicht erlaubt."
+
+#: ../mod_ionws/split.c:1072 ../mod_ionws/splitfloat.c:896
+msgid "Unable to split: not enough free space."
+msgstr "Teilung nicht möglich: nicht genügend freier Speicher."
+
+#
+#: ../mod_ionws/split.c:1558
+msgid "Unable to move the status display out of way of transpose."
+msgstr "Es ist unmöglich die Statusanzeige so zu verschieben."
+
+#: ../mod_ionws/split.c:1722
+#, c-format
+msgid "Unable to get configuration for %s."
+msgstr "Konnte keine Konfiguration für \"%s\" finden."
+
+#: ../mod_ionws/split-stdisp.c:591 ../mod_ionws/split-stdisp.c:616
+msgid "Status display in bad split configuration."
+msgstr "Statusanzeige hat eine schlechte Teilungskonfiguration."
+
+#: ../mod_ionws/split-stdisp.c:657
+msgid "Status display badly located in split tree."
+msgstr "Statusanzeige liegt schlecht im Teilungsbaum."
+
+#: ../mod_floatws/floatws.c:351 ../mod_panews/panews.c:259
+msgid "Workspace may not be destroyed."
+msgstr "Arbeitsbereich kann nicht zerstört werden."
+
+#: ../mod_floatws/floatws.c:358
+msgid "Failed to rescue some client windows!"
+msgstr "Rettung einiger Client Fenster fehlgeschlagen."
+
+#: ../mod_floatws/floatws.c:370 ../mod_panews/panews.c:254
+msgid "Refusing to close non-empty workspace."
+msgstr "Schließen von 'nicht-leerem' Arbeitsbereich abgelehnt."
+
+#
+#: ../mod_floatws/floatws.c:462
+msgid "Failure to create a new frame."
+msgstr "Konnte kein neues Frame erzeugen."
+
+#
+#: ../mod_floatws/floatws.c:1037 ../mod_floatws/floatws.c:1115
+msgid "Region not managed by the workspace."
+msgstr "Bereich wird nicht behandelt durch diesen Arbeitsbereich."
+
+#: ../mod_floatws/floatws.c:1236
+msgid "No geometry specified."
+msgstr "Keine Geometrie angegeben."
+
+#: ../mod_floatws/placement.c:125
+#, c-format
+msgid "Unknown placement method \"%s\"."
+msgstr "Unbekannte Platzierungsmethode \"%s\"."
+
+#: ../mod_panews/panews.c:193
+msgid "Unable to re-initialise workspace. Destroying."
+msgstr "Konnte den Arbeitsbereich nicht neu initialisieren. Zerstört."
+
+#: ../mod_panews/placement.c:104 ../mod_panews/placement.c:108
+#: ../mod_panews/placement.c:175 ../mod_panews/placement.c:180
+#, c-format
+msgid "Malfunctioning placement hook; condition #%d."
+msgstr "Fehlfunktion der Platzierung; Zustand #%d."
+
+#: ../mod_query/fwarn.c:34
+msgid "Error:\n"
+msgstr "Fehler:\n"
+
+#
+#: ../mod_menu/menu.c:490
+msgid "Empty menu."
+msgstr "Leeres Menü."
+
+#
+#: ../mod_sm/sm.c:110
+msgid "Failed to set session directory."
+msgstr "Konnte Sitzungsverzeichnis nicht setzen."
+
+#: ../mod_sm/sm_session.c:85
+msgid "Too many ICE connections."
+msgstr "Zu viele 'ICE' Verbindungen."
+
+#: ../mod_sm/sm_session.c:227
+msgid "Failed to save session state"
+msgstr "Konnte Sitzungszustand nicht sichern."
+
+#: ../mod_sm/sm_session.c:246
+msgid "Failed to request save-yourself-phase2 from session manager."
+msgstr "Konnte die 'save-yourself-phase2' Anfrage des Sitzungsmanagers nicht gehandeln."
+
+#
+#: ../mod_sm/sm_session.c:295
+msgid "SESSION_MANAGER environment variable not set."
+msgstr "'SESSION_MANAGER' Umgebungsvariable nicht gesetzt."
+
+#: ../mod_sm/sm_session.c:300
+msgid "Session Manager: IceAddConnectionWatch failed."
+msgstr "Sitzungsmanager: 'IceAddConnectionWatch' fehlgeschlagen."
+
+#
+#: ../mod_sm/sm_session.c:325
+msgid "Unable to connect to the session manager."
+msgstr "Konnte keine Verbindung zu Sitzungsmanager aufbauen."
+
+#: ../mod_sp/main.c:137
+#, c-format
+msgid "Unable to create scratchpad for screen %d."
+msgstr "Konnte 'Scratchpad' (Schmirblock) für Screen %d nicht erstellen."
+
+#: ../de/init.c:41
+#, c-format
+msgid "Border attribute %s sanity check failed."
+msgstr "Überprüfung der Rahmeneigenschaft %s fehlgeschlagen."
+
+#: ../de/init.c:64
+#, c-format
+msgid "Unknown border style \"%s\"."
+msgstr "Unbekannter Rahmenstyle \"%s\"."
+
+#: ../de/init.c:96
+#, c-format
+msgid "Unable to allocate colour \"%s\"."
+msgstr "Konnte Farbe \"%s\" nicht zuweisen."
+
+#: ../de/init.c:160
+#, c-format
+msgid "Corrupt substyle table %d."
+msgstr "Zerstörte 'substyle' Tabelle %d"
+
+#: ../de/init.c:193
+#, c-format
+msgid "Unknown text alignment \"%s\"."
+msgstr "Unbekannte Textausrichtung \"%s\"."
+
+#: ../de/init.c:263
+#, c-format
+msgid "'based_on' for %s points back to the style itself."
+msgstr " 'based_on' für %s zeigt zurück auf den selben Style. "
+
+#: ../de/init.c:266
+#, c-format
+msgid "Unknown base style \"%s\"."
+msgstr "Unbekannter Basisstyle \"%s\"."
+
+#: ../de/font.c:47
+#, c-format
+msgid ""
+"Fontset for font pattern '%s' implements context dependent drawing, which is "
+"unsupported. Expect clutter."
+msgstr ""
+"Schriftart für Schriftmuster '%s' enthält fallspezifische Abhängigkeiten zum Zeichnen"
+"Dies wird nicht unterstützt. Erwartet 'clutter'."
+
+#: ../de/font.c:58
+#, c-format
+msgid "Could not load font \"%s\", trying \"%s\""
+msgstr "Konnte Schrift \"%s\" nicht laden, versuche \"%s\"."
+
+#: ../de/style.c:315
+#, c-format
+msgid "Style %s still in use [%d] but the module is being unloaded!"
+msgstr "Style %s wird derzeit benutzt [%d] aber das Modul ist nicht mehr geladen."
+
+#: ../ion/ion.c:41 ../pwm/pwm.c:41
+msgid "X display to use"
+msgstr "Benutze X Anzeige"
+
+#: ../ion/ion.c:44 ../pwm/pwm.c:44
+msgid "Configuration file"
+msgstr "Konfigurationsdatei"
+
+#: ../ion/ion.c:47 ../pwm/pwm.c:47
+msgid "Add directory to search path"
+msgstr "Addiere Verzeichnis zum Suchpfad "
+
+#: ../ion/ion.c:50 ../pwm/pwm.c:50
+msgid "Manage default root window/non-Xinerama screen only"
+msgstr "Behandle nur das vorgegebene Wurzelfenster/nicht-Xinerama."
+#: ../ion/ion.c:54
+msgid "Use Xinerama screen information (default: 1/yes)"
+msgstr "Benutze Xinerama Screen Information (Vorgabe: 1/ja)"
+
+#: ../ion/ion.c:57 ../pwm/pwm.c:57
+msgid "Ignored: not compiled with Xinerama support"
+msgstr "Ignoriert: keine Xinerama Unterstützung kompiliert."
+
+#: ../ion/ion.c:61 ../pwm/pwm.c:61
+msgid "Name of session (affects savefiles)"
+msgstr "Name der Sitzung (betrifft Sicherungsdateien)"
+
+#: ../ion/ion.c:64 ../pwm/pwm.c:64
+msgid "Session manager client ID"
+msgstr "Sitzungsmanager Client ID"
+
+#: ../ion/ion.c:67 ../pwm/pwm.c:67
+msgid "Do not create startup error log and display it with xmessage."
+msgstr "Erstelle keine Fehlerdateien während des Starts und zeige sie mit 'xmessage'. "
+
+#: ../ion/ion.c:71 ../pwm/pwm.c:71
+msgid "Show this help"
+msgstr "Zeige diese Hilfe"
+
+#: ../ion/ion.c:74 ../pwm/pwm.c:74
+msgid "Show program version"
+msgstr "Zeige Programmversion"
+
+#: ../ion/ion.c:77 ../pwm/pwm.c:77
+msgid "Show about text"
+msgstr "Zeige 'About' Text"
+
+#: ../ion/ion.c:92
+msgid "Could not get user configuration file directory."
+msgstr "Konnte das Verzeichnis mit den Benutzerkonfigurationsdateien nicht finden."
+
+#: ../ion/ion.c:106
+#, c-format
+msgid "%s/welcome.txt"
+msgstr "%s/welcome.de.txt"
+
+#: ../ion/ion.c:139 ../pwm/pwm.c:86
+#, c-format
+msgid ""
+"Usage: %s [options]\n"
+"\n"
+msgstr ""
+"Anwendung: %s [Optionen]\n"
+"\n"
+
+#: ../ion/ion.c:199 ../pwm/pwm.c:149
+msgid "Invalid parameter to -xinerama."
+msgstr "Ungültiger Parameter für '-xinerama'."
+
+#
+#: ../ion/ion.c:218 ../pwm/pwm.c:168
+msgid "Invalid command line."
+msgstr "Ungültige Kommandozeile."
+
+#: ../ion/ion.c:239
+msgid "Ion startup error log:\n"
+msgstr "Ion-Start Fehlerausgabe:\n"
+
+#: ../ion/ion.c:249 ../pwm/pwm.c:199
+msgid "Refusing to start due to encountered errors."
+msgstr "Start abgebrochen wegen aufgetretenen Fehlern."
+
+#: ../pwm/pwm.c:54
+msgid "Use Xinerama screen information (default: 0/no)"
+msgstr "Benutze Xinerama Screen Information (Vorgabe: 0/nein)"
+
+#: ../pwm/pwm.c:189
+msgid "PWM startup error log:\n"
+msgstr "PWM-Start Fehlerausgabe:\n"
+
+#: ../../libextl/readconfig.c:86
+msgid "$HOME not set"
+msgstr "Variable '$HOME' nicht gesetzt."
+
+#: ../../libextl/readconfig.c:113
+msgid "User directory not set. Unable to set session directory."
+msgstr "" 
+"Benutzerverzeichnis nicht gesetzt."
+"Konnte Sitzungsverzeichnis nicht setzen."
+
+#: ../../libextl/readconfig.c:247
+#, c-format
+msgid "Falling back to %s."
+msgstr "Yritetään tiedostoa %s."
+
+#: ../../libextl/readconfig.c:441
+#, c-format
+msgid "Unable to create session directory \"%s\"."
+msgstr "Konntedas Sitzungsverzeichnis \"%s\" nicht erstellen."
+
+#: ../../libextl/luaextl.c:113
+msgid "Lua stack full."
+msgstr "Lua Stapelverarbeitung voll."
+
+#
+#: ../../libextl/luaextl.c:139
+msgid "Unknown Lua error."
+msgstr "Unbekannter Lua Fehler."
+
+#: ../../libextl/luaextl.c:456
+msgid "Stack trace:"
+msgstr "Stapelverfolgung:"
+
+#: ../../libextl/luaextl.c:463
+#, c-format
+msgid ""
+"\n"
+"(Unable to get debug info for level %d)"
+msgstr ""
+"\n"
+"(Konnte Debuginformation für Level %d nicht finden.)"
+
+#: ../../libextl/luaextl.c:481
+msgid ""
+"\n"
+"  [Skipping unnamed C functions.]"
+msgstr ""
+"\n"
+"  [Überspringe unbenannte C Funktionen.]"
+
+#: ../../libextl/luaextl.c:532
+msgid "Internal error."
+msgstr "Interner Fehler."
+
+#: ../../libextl/luaextl.c:551
+msgid "Unable to initialize Lua."
+msgstr "Konnte Lua nicht initialisieren."
+
+#: ../../libextl/luaextl.c:1277
+msgid ""
+"Too many return values. Use a C compiler that has va_copy to support more."
+msgstr ""
+"Zu viele Rückgabewerte. Benutzen Sie einen C Compiler der 'va_copy' benutzt."
+
+#: ../../libextl/luaextl.c:1295
+#, c-format
+msgid "Invalid return value (expected '%c', got lua type \"%s\")."
+msgstr ""
+"Ungültiger Rückgabewert (erwartet '%c', bekommener Luatype \"%s\")."
+
+#: ../../libextl/luaextl.c:1330 ../../libextl/luaextl.c:1594
+msgid "Stack full."
+msgstr "Stapel voll."
+
+#: ../../libextl/luaextl.c:1570
+msgid "L1 call handler upvalues corrupt."
+msgstr "L1 Abruf 'upvalues' fehlerhaft."
+
+#: ../../libextl/luaextl.c:1575
+msgid "Called function has been unregistered."
+msgstr "Aufgerufene Funktion wurde noicht registriert."
+
+#: ../../libextl/luaextl.c:1587
+#, c-format
+msgid "Attempt to call an unsafe function \"%s\" in restricted mode."
+msgstr "Versuchte eine unsichere Funktion \"%s\" im begrenzten Modus aufzurufen."
+
+#: ../../libextl/luaextl.c:1603
+#, c-format
+msgid ""
+"Argument %d to %s is of invalid type. (Argument template is '%s', got lua "
+"type %s)."
+msgstr ""
+"Argument %d von %s hat einen ungültigen Type (Argumentvorgabe ist '%s', "
+"von Lua kam '%s'.)"
+
+#: ../../libextl/luaextl.c:1782
+#, c-format
+msgid ""
+"Function '%s' has more parameters than the level 1 call handler can handle"
+msgstr ""
+"Funktion %s hat mehr Parameter als der Level 1 Handler bearbeiten kann "
+
+#
+#: ../../libextl/luaextl.c:2199
+msgid "Maximal serialisation depth reached."
+msgstr "Maximum der Serialisierungstiefe erreicht."
+
+#: ../../libextl/luaextl.c:2220
+#, c-format
+msgid "Unable to serialise type %s."
+msgstr "Konnte den Type %s nicht serialisieren."
+
+#: ../../libextl/luaextl.c:2251
+msgid "-- This file has been generated by Ion. Do not edit.\n"
+msgstr "-- Dies Datei wurde von Ion erzeugt. Bitte nicht verändern.\n"
+
+#: ../../libextl/misc.c:17
+#, c-format
+msgid ""
+"Type checking failed in level 2 call handler for parameter %d (got %s, "
+"expected %s)."
+msgstr ""
+"Type Überprüfung im Level 2 Handler für den Parameter #%d fehlgeschlagen"
+"(gekam %s, erwartet %s)."
+
+#: ../etc/cfg_floatws.lua:34
+msgid "Lower the frame."
+msgstr "Frame in den Hintergrund."
+
+#: ../etc/cfg_bindings.lua:171
+msgid "Display frame context menu."
+msgstr "Anzeige Frame Kontexmenü"
+
+#
+#: ../etc/cfg_ionws.lua:44
+msgid "Flip at root"
+msgstr "An der Wurzel drehen"
+
+#
+#: ../etc/cfg_bindings.lua:43
+msgid "Go to n:th screen on multihead setup."
+msgstr "Gehe zu n'ten Screen bei 'multihead' Aufbau."
+
+#: ../ext_statusbar/ext_statusbar.lua:308
+msgid "Failed to create statusbar."
+msgstr "Konnte Statusbar nicht erzeugen."
+
+#: ../pwm/cfg_pwm_menus.lua:13 ../etc/cfg_menus.lua:10
+msgid "Help"
+msgstr "Hilfe"
+
+#: ../mod_query/mod_query.lua:566
+msgid "SSH to:"
+msgstr "SSH to:"
+
+#: ../etc/cfg_menus.lua:28
+msgid "Restart"
+msgstr "Neustart"
+
+#: ../mod_menu/mod_menu.lua:231
+msgid "Cannot save selection."
+msgstr "Konnte Auswahl nicht speichern."
+
+#: ../etc/cfg_bindings.lua:206
+msgid "End the resize mode."
+msgstr "Beende den Anpassungsmode."
+
+#
+#
+#: ../etc/cfg_ionws.lua:48 ../etc/cfg_ionws.lua:58
+msgid "Vertically"
+msgstr "Vertikal"
+
+#: ../ioncore/ioncore-bindings.lua:34
+msgid "Invalid guard %s."
+msgstr "Ungültiger Wächter %s."
+
+#: ../etc/cfg_bindings.lua:109
+msgid "Query for Lua code to execute."
+msgstr "Anfrage um Lua Code auszuführen."
+
+#
+#: ../etc/cfg_bindings.lua:47
+msgid "Go to next/previous screen on multihead setup."
+msgstr "Gehe zum nächsten/vorherigen Screen bei 'multihead' Aufbau."
+
+#: ../mod_query/mod_query.lua:344
+msgid "none"
+msgstr "keine"
+
+#: ../etc/cfg_bindings.lua:219
+msgid "Shrink in specified direction."
+msgstr "Verkleinern in angegebener Richtung."
+
+#: ../etc/cfg_bindings.lua:160
+msgid "Maximize the frame horizontally/vertically."
+msgstr "Maximiere das Frame horizontal/vertikal."
+
+#: ../etc/cfg_bindings.lua:156
+msgid "Move current object within the frame left/right."
+msgstr ""
+"Bewege das aktuelle Objekt im Frame nach links/rechts."
+
+#: ../etc/cfg_ionws.lua:12
+msgid "Go to frame above/below/right/left of current frame."
+msgstr ""
+"Gehe zu Frame oben/unten/rechts/links"
+"Bezogen auf aktuelles Frame"
+
+#: ../ext_statusbar/ext_statusbar.lua:289
+msgid "Screen not found."
+msgstr "Screen nicht gefunden."
+
+#: ../etc/cfg_bindings.lua:182
+msgid "Resize the frame."
+msgstr "Anpassen des Frame."
+
+#: ../etc/cfg_bindings.lua:189
+msgid "Move objects between frames by dragging and dropping the tab."
+msgstr "Bewege Objekte zwischen Frames durch 'ziehen/fallen lassen' des Reiters."
+
+#: ../mod_query/mod_query.lua:99
+msgid "Could not find %s"
+msgstr "Ei löydettyä %s:ää."
+
+#: ../ext_statusbar/ext_statusbar.lua:295
+msgid "Screen already has an stdisp. Refusing to create a statusbar."
+msgstr "Screen hat bereits ein 'stdisp'. Erstellen der Statusbar wurde abgelehnt."
+
+#: ../etc/cfg_bindings.lua:112
+msgid "Query for host to connect to with SSH."
+msgstr "Anfrage zur Verbindung mit SSH."
+
+#
+#: ../etc/cfg_bindings.lua:229
+msgid "Move in specified direction."
+msgstr "Bewegt in festgelegte Richtung"
+
+#
+#: ../ext_statusbar/ext_statusbar.lua:27
+msgid "mail"
+msgstr "Post"
+
+#: ../mod_query/mod_query.lua:807
+msgid ""
+"\n"
+"Transients:\n"
+msgstr ""
+"\n"
+"Flüchtige:\n"
+
+#: ../etc/cfg_menu.lua:21
+msgid "Select next/previous menu entry."
+msgstr "Wähle nächsten/vorherigen Menüeintrag."
+
+#: ../ioncore/ioncore-luaext.lua:30
+msgid "Recursive table - unable to deepcopy"
+msgstr "Rekursive Tabelle - unmöglich zu kopieren."
+
+#
+#: ../etc/cfg_bindings.lua:209
+msgid "Grow in specified direction."
+msgstr "Wachse in angegebener Richtung."
+
+#: ../ioncore/ioncore-bindings.lua:68
+msgid "Invalid command"
+msgstr "Ungültiges Kommando"
+
+#: ../mod_query/mod_query.lua:189 ../mod_menu/mod_menu.lua:245
+msgid "Too much result data"
+msgstr "Zu viele Ergebnisdaten"
+
+#: ../mod_query/mod_query.lua:344
+msgid "Workspace type (%s):"
+msgstr "Type des Arbeitsbereichs (%s):"
+
+#: ../etc/cfg_bindings.lua:36
+msgid "Go to previous active object."
+msgstr "Gehe zu vorherigem aktivem Objekt"
+
+#
+#: ../etc/cfg_query.lua:67
+msgid "Clear mark/cancel selection."
+msgstr "Lösche Markierung/Abbruch Auswahl."
+
+#: ../mod_query/mod_query.lua:772
+msgid "No entry '%s'"
+msgstr "Kein Eintrag '%s'."
+
+#
+#
+#: ../etc/cfg_bindings.lua:90
+msgid "Toggle fullscreen mode of current client window."
+msgstr "Schalte das aktuelle Client Fenster in den Vollbildmode."
+
+#: ../etc/cfg_bindings.lua:103
+msgid "Query for manual page to be displayed."
+msgstr "Anfrage um 'Manual' Seiten anzuzeigen."
+
+#: ../etc/cfg_bindings.lua:51
+msgid "Show the Ion manual page."
+msgstr "Zeige die Ion 'Manual' Seite."
+
+#: ../etc/cfg_bindings.lua:60
+msgid "Display the main menu."
+msgstr "Zeige das Hauptmenü."
+
+#
+#: ../etc/cfg_bindings.lua:54
+msgid "Run a terminal emulator."
+msgstr "Starte einen Terminal."
+
+#: ../etc/cfg_bindings.lua:121
+msgid "Query for workspace to go to or create a new one."
+msgstr "Anfrage um auf einen Arbeitsbereich zu wechseln oder einen neuen zu erzeugen."
+
+#: ../etc/cfg_menus.lua:38
+msgid "Kill"
+msgstr "Zerstören"
+
+#: ../pwm/cfg_pwm_menus.lua:25 ../etc/cfg_menus.lua:37
+msgid "Close"
+msgstr "Schließen"
+
+#: ../mod_query/mod_query.lua:381
+msgid "Go to or create workspace:"
+msgstr "Erzeuge einen Arbeitsbereich:"
+
+#: ../etc/cfg_query.lua:35
+msgid "Delete one word forward/backward."
+msgstr "Lösche ein Wort vorwärts/rückwärts."
+
+#: ../etc/cfg_query.lua:17
+msgid "Go to end/beginning."
+msgstr "Gehe zum Ende/Anfang."
+
+#
+#: ../etc/cfg_ionws.lua:43
+msgid "Transpose"
+msgstr "Vertauschen"
+
+#: ../etc/cfg_bindings.lua:152
+msgid "Switch to next/previous object within the frame."
+msgstr "Wechsel zum nächsten/vorherigen Objekt im Frame"
+
+#: ../mod_query/mod_query.lua:402
+msgid "Restart Ion (y/n)?"
+msgstr "Neustart Ion (y/n)?"
+
+#: ../mod_query/mod_query.lua:724
+msgid "Lua code: "
+msgstr "Lua Code:"
+
+#: ../mod_query/mod_query.lua:367
+msgid "Attach window:"
+msgstr "Fenster verknüpfen:"
+
+#: ../etc/cfg_bindings.lua:186 ../etc/cfg_floatws.lua:37
+msgid "Move the frame."
+msgstr "Gehe zu Frame."
+
+#: ../pwm/cfg_pwm_menus.lua:11 ../etc/cfg_menus.lua:8
+msgid "Programs"
+msgstr "Programme"
+
+#: ../etc/cfg_query.lua:23
+msgid "Skip one word forward/backward."
+msgstr "Überspringe ein Wort vorwärts/rückwärts."
+
+#: ../mod_query/mod_query.lua:738
+msgid "Unknown menu %s."
+msgstr "Unbekanntes Menü %s."
+
+#: ../etc/cfg_floatws.lua:12
+msgid "Backwards-circulate focus and raise the newly focused frame."
+msgstr "Rückwärts laufender Fokus und zeige fokussiertes Frame."
+
+#: ../etc/cfg_menus.lua:44
+msgid "Window info"
+msgstr "Fensterinformation"
+
+#
+#: ../etc/cfg_query.lua:58
+msgid "Set mark/begin selection."
+msgstr "Setze Markierungs/Anfangs - Auswahl."
+
+#: ../etc/cfg_menus.lua:30
+msgid "Restart TWM"
+msgstr "Neustart TWM"
+
+#: ../etc/cfg_ionws.lua:9
+msgid "Split current frame vertically."
+msgstr "Teile aktuelles Frame vertikal."
+
+#
+#
+#: ../etc/cfg_bindings.lua:57
+msgid "Create a new workspace of chosen default type."
+msgstr "Erzeuge einen neuen Arbeitsbereich mit ausgewähltem Vorgabetype."
+
+#
+#: ../etc/cfg_floatws.lua:29
+msgid "Raise the frame."
+msgstr "Bringe Frame nach vorne."
+
+#: ../etc/cfg_floatws.lua:46
+msgid "(Un)stick"
+msgstr "anheften/ablösen"
+
+#: ../mod_query/mod_query.lua:287 ../mod_query/mod_query.lua:297
+msgid "Could not find client window %s."
+msgstr "Konnte Client Fenster %s nicht finden."
+
+#: ../etc/cfg_query.lua:82
+msgid "Close the query/message box, not executing bound actions."
+msgstr "Schließe die Anfrage/Message Box und führe keine daran gebundenen Aktionen aus."
+
+#
+#: ../etc/cfg_bindings.lua:168
+msgid "Query for a client window to attach to active frame."
+msgstr "Anfrage für ein Client Fenster um sich mit einem aktivem Frame zu verbinden "
+
+#: ../mod_query/mod_query.lua:488
+msgid "Run:"
+msgstr "Starte:"
+
+#
+#: ../etc/cfg_panews.lua:11
+msgid "Resize the area."
+msgstr "Verändere die Ansicht."
+
+#
+#: ../etc/cfg_bindings.lua:95
+msgid "Kill client owning current client window."
+msgstr "Beende aktuelles Client Fenster."
+
+#: ../mod_query/mod_query.lua:595
+msgid "Manual page (%s):"
+msgstr "Handbuch für (%s):"
+
+#
+#: ../etc/cfg_bindings.lua:124
+msgid "Query for a client window to go to."
+msgstr "Anfrage wohin ein Client Fenster geschickt werden soll."
+
+#
+#: ../etc/cfg_ionws.lua:39
+msgid "Destroy frame"
+msgstr "Zerstöre Frame"
+
+#: ../mod_query/mod_query.lua:339
+msgid "Unknown error"
+msgstr "Unbekannter Fehler"
+
+#: ../etc/cfg_bindings.lua:175 ../etc/cfg_panews.lua:8
+msgid "Begin move/resize mode."
+msgstr "Beginne Bewegungs/Veränderungs - Modus."
+
+#: ../etc/cfg_bindings.lua:136
+msgid "Tag current object within the frame."
+msgstr "Markiere aktuelles Objekt im Frame."
+
+#: ../etc/cfg_bindings.lua:140
+msgid "Switch to n:th object within the frame."
+msgstr "Wechsel zum n'ten Objekt im Frame."
+
+#: ../etc/cfg_bindings.lua:203
+msgid "Cancel the resize mode."
+msgstr "Abbruch des Veränderungsmodus."
+
+#: ../build/mkman.lua:196
+msgid "%s %s"
+msgstr "%s %s"
+
+#: ../etc/cfg_query.lua:27
+msgid "Delete next character."
+msgstr "Lösche nächsten Buchstaben."
+
+#: ../mod_query/mod_query.lua:356
+msgid "Go to window:"
+msgstr "Gehe zu Fenster:"
+
+#: ../build/mkman.lua:182
+msgid "drag"
+msgstr "ziehen"
+
+#: ../build/mkman.lua:181
+msgid "click"
+msgstr "anklicken"
+#: ../build/mkman.lua:180
+msgid "press"
+msgstr "drücken"
+
+#: ../etc/cfg_menu.lua:27
+msgid "Clear the menu's typeahead find buffer."
+msgstr "Lösche den Menüpuffer."
+
+#: ../etc/cfg_ionws.lua:47
+msgid "Split"
+msgstr "Teilung"
+
+#
+#: ../etc/cfg_bindings.lua:98
+msgid ""
+"Send next key press to current client window. Some programs may not allow "
+"this by default."
+msgstr ""
+"Sende den nächsten Tastendruck zum aktuellen Client Fenster. "
+"Einige Programme unterstützen dies nicht in ihrer Voreinstellung."
+
+#: ../etc/cfg_menu.lua:11
+msgid "Close the menu."
+msgstr "Schließe das Menü."
+
+#: ../etc/cfg_query.lua:87
+msgid "Scroll the message or completions up/down."
+msgstr "Verschiebe die Nachricht oder Vervollständigung nach oben/unten."
+
+#: ../etc/cfg_query.lua:74
+msgid "Close the query and execute bound action."
+msgstr "Schließe die Anfrage und führe die daran gebundene Aktion aus."
+
+#
+#: ../etc/cfg_bindings.lua:31
+msgid "Switch to next/previous object within current screen."
+msgstr "Wechsel zum nächsten/vorherigen Objekt im aktuellen Screen."
+
+#: ../etc/cfg_query.lua:39
+msgid "Delete to end of line."
+msgstr "Lösche bis zum ende der Zeile."
+
+#: ../pwm/cfg_pwm_menus.lua:14 ../etc/cfg_menus.lua:11
+msgid "About Ion"
+msgstr "Über Ion"
+
+#: ../mod_query/mod_query.lua:804
+msgid ""
+"Title: %s\n"
+"Class: %s\n"
+"Role: %s\n"
+"Instance: %s\n"
+"XID: 0x%x"
+msgstr ""
+"Titel: %s\n"
+"Klasse(class): %s\n"
+"Rolle(role): %s\n"
+"Instanz(instance): %s\n"
+"XID: 0x%x"
+
+#
+#: ../etc/cfg_query.lua:61
+msgid "Cut selection."
+msgstr "Ausschneiden."
+
+#: ../etc/cfg_query.lua:53
+msgid "Paste from the clipboard."
+msgstr "Einfügen aus Clipboard."
+
+#
+#: ../etc/cfg_query.lua:45
+msgid "Select next/previous (matching) history entry."
+msgstr "Auswahl des nächsten/vorherigen (Treffers) 'History' Eintrags."
+
+#: ../etc/cfg_menus.lua:43
+msgid "Clear tags"
+msgstr "Lösche Markierungen"
+
+#: ../etc/cfg_query.lua:42
+msgid "Delete the whole line."
+msgstr "Lösche die ganze Zeile."
+
+#: ../etc/cfg_query.lua:71
+msgid "Try to complete the entered text."
+msgstr "Versuche den eingegebenen Text zu komplettieren."
+
+#: ../etc/cfg_query.lua:31
+msgid "Delete previous character."
+msgstr "Lösche vorheriges Zeichen."
+
+#: ../mod_query/mod_query.lua:299
+msgid "Cannot attach: different root windows."
+msgstr "Keine Verknüpfung: unterschiedliche Wurzelfenster."
+
+#: ../build/mkman.lua:183
+msgid "double click"
+msgstr "Doppelklick"
+
+#: ../etc/cfg_query.lua:11
+msgid "Move one character forward/backward."
+msgstr "Bewege ein Zeichen vorwärts/rückwärts."
+
+#: ../etc/cfg_menus.lua:29
+msgid "Restart PWM"
+msgstr "Neustart PWM"
+
+#: ../etc/cfg_bindings.lua:115
+msgid "Query for file to edit."
+msgstr "Anfrage um eine Datei zu bearbeiten."
+
+#
+#: ../etc/cfg_bindings.lua:39
+msgid "Clear all tags."
+msgstr "Lösche alle Markierungen."
+
+#: ../etc/cfg_bindings.lua:178
+msgid "Switch the frame to display the object indicated by the tab."
+msgstr "Wechsle das Frame um das Objekt anzuzeigen welches durch den Reiter angegeben ist."
+
+#
+#
+#: ../etc/cfg_ionws.lua:50 ../etc/cfg_ionws.lua:60
+msgid "Horizontally"
+msgstr "Horizontal"
+
+#: ../pwm/cfg_pwm_menus.lua:16 ../pwm/cfg_pwm_menus.lua:24
+msgid "New"
+msgstr "Neu"
+
+#: ../mod_query/mod_query.lua:410
+msgid "Frame name:"
+msgstr "Framename:"
+
+#
+#: ../etc/cfg_bindings.lua:18
+msgid ""
+"Switch to n:th object (workspace, full screen client window) within current "
+"screen."
+msgstr ""
+"Wechsle zum n'ten Objekt (Arbeitsbereich, Vollbild Fenster) im aktuellen Arbeitsbereich."
+
+#: ../etc/cfg_bindings.lua:64
+msgid "Display the window list menu."
+msgstr "Zeige das Menü mit der Fensterliste."
+
+#: ../etc/cfg_floatws.lua:26
+msgid "Toggle shade mode"
+msgstr "Wechsle in den Schattenmodus"
+
+#: ../mod_query/mod_query.lua:393
+msgid "Exit Ion/Shutdown session (y/n)?"
+msgstr "Beende Ion/Schließe Sitzung (y/n)?"
+
+#: ../etc/cfg_floatws.lua:15
+msgid "Raise/lower active frame."
+msgstr "vorbringen/zurücklegen des aktiven Frames"
+
+#: ../mod_menu/mod_menu.lua:235
+msgid "Save look selection in %s?"
+msgstr "Sichere Aussehen in %s?"
+
+#: ../etc/cfg_bindings.lua:82
+msgid "Close current object."
+msgstr "Schließe aktuelles Objekt."
+
+#
+#: ../etc/cfg_query.lua:64
+msgid "Copy selection."
+msgstr "Kopiere Auswahl."
+
+#: ../etc/cfg_bindings.lua:164
+msgid "Attach tagged objects to this frame."
+msgstr "Verknüpfe markierte Objekte mit diesem Frame."
+
+#: ../etc/cfg_ionws.lua:57
+msgid "Floating split"
+msgstr "Fließende Teilung"
+
+#: ../mod_query/mod_query.lua:768
+msgid "%s menu:"
+msgstr "menu: %s"
+
+#: ../ext_statusbar/ext_statusbar.lua:318
+msgid "Failed to create a timer for statusbar."
+msgstr "Erzeugen des Timers für die Statusbar fehlgeschlagen."
+
+#: ../etc/cfg_ionws.lua:52 ../etc/cfg_ionws.lua:62
+msgid "Vertically at root"
+msgstr "Vertikal an der Wurzel"
+
+#
+#
+#: ../etc/cfg_menu.lua:16
+msgid "Activate current menu entry."
+msgstr "Aktiviere aktuellen Menüeintrag."
+
+#: ../ioncore/ioncore-efbb.lua:12
+msgid ""
+"Making the following minimal emergency mappings:\n"
+"  F2 -> xterm\n"
+"  F11 -> restart\n"
+"  F12 -> exit\n"
+"  Mod1+C -> close\n"
+"  Mod1+K P/N -> WFrame.switch_next/switch_prev\n"
+msgstr ""
+"Erstelle die folgenden minimalen Rettungseinstellungen:\n"
+"  F2 -> xterm\n"
+"  F11 -> Neustart\n"
+"  F12 -> Beenden\n"
+"  Mod1+C -> Schließen\n"
+"  Mod1+K P/N -> nächstes/vorheriges Frame\n"
+
+#
+#: ../etc/cfg_ionws.lua:45
+msgid "Transpose at root"
+msgstr "Vertausche an der Wurzel"
+
+#: ../pwm/cfg_pwm_menus.lua:17 ../etc/cfg_menus.lua:12
+msgid "Styles"
+msgstr "Aussehen ändern"
+
+#: ../etc/cfg_bindings.lua:106
+msgid "Query for command line to execute."
+msgstr "Anfrage um Kommandozeile auszuführen."
+
+#: ../mod_query/mod_query.lua:323
+msgid "Unable to create workspace: no screen."
+msgstr "Unmöglich Arbeitsbereich zu erzeugen: kein Screen."
+
+#: ../ext_statusbar/ext_statusbar.lua:27
+msgid "load"
+msgstr "lade"
+
+#
+#: ../etc/cfg_ionws.lua:42
+msgid "Flip"
+msgstr "Umdrehen"
+
+#: ../etc/cfg_ionws.lua:41
+msgid "Flip&transpose"
+msgstr "Umdrehen und Vertauschen"
+
+#: ../pwm/cfg_pwm_menus.lua:12 ../etc/cfg_menus.lua:9
+msgid "Lock screen"
+msgstr "Sperre Screen"
+
+#: ../mod_query/mod_query.lua:449
+msgid "Edit file:"
+msgstr "Bearbeite Datei:"
+
+#: ../etc/cfg_menus.lua:19
+msgid "XTerm"
+msgstr "XTerm"
+
+#: ../build/mkman.lua:198
+msgid "%s %s at %s"
+msgstr "%s %s auf %s"
+
+#
+#: ../etc/cfg_bindings.lua:85
+msgid ""
+"Nudge current client window. This might help with some programs' resizing "
+"problems."
+msgstr ""
+"Anstupsen des aktuellen Fensters. Dies könnte helfen bei einigen Programmen "
+" (xterm) mit Darstellungsproblemen."
+
+#: ../etc/cfg_ionws.lua:22
+msgid "Destroy current frame."
+msgstr "Zerstöre aktuelles Frame."
+
+#: ../ioncore/ioncore-bindings.lua:53
+msgid "Error in command string: "
+msgstr "Fehler in Kommandozeichenkette: "
+
+#: ../etc/cfg_ionws.lua:19
+msgid "Split current frame horizontally."
+msgstr "Teile aktuelles Frame horizontal."
+
+#: ../etc/cfg_menus.lua:42
+msgid "Attach tagged"
+msgstr "Markierte Objekte verknüpfen"
+
+#: ../etc/cfg_ionws.lua:54 ../etc/cfg_ionws.lua:64
+msgid "Horizontally at root"
+msgstr "Horizontal an der Wurzel"
+
+#: ../etc/cfg_menus.lua:40
+msgid "(Un)tag"
+msgstr "Markieren"
+
+#: ../mod_query/mod_query.lua:459
+msgid "View file:"
+msgstr "Zeige Datei:"
+
+#: ../mod_query/mod_query.lua:421
+msgid "Workspace name:"
+msgstr "Name des Arbeitsbereichs:"
+
+#: ../etc/cfg_menus.lua:31
+msgid "Exit"
+msgstr "Beenden"
+
+#: ../etc/cfg_menus.lua:27
+msgid "Save"
+msgstr "Speichern"
+
+#
+#: ../pwm/cfg_pwm_menus.lua:15
+msgid "Workspaces"
+msgstr "Arbeitsbereiche"
+
+#: ../etc/cfg_menus.lua:20
+msgid "Mozilla Firefox"
+msgstr "Mozilla Firefox"
+
+#: ../ioncore/ioncore-bindings.lua:45
+msgid "Error compiling guard: %s"
+msgstr "Fehler Erstellungswächter: %s"
+
+#: ../mod_menu/mod_menu.lua:271
+msgid "Refresh list"
+msgstr "Aktualisiere Liste"
+
+#: ../etc/cfg_floatws.lua:9
+msgid "Circulate focus and raise the newly focused frame."
+msgstr "Rückwärts laufender Fokus und zeige fokussiertes Frame."
+
+#
+#: ../etc/cfg_menus.lua:21
+msgid "Run..."
+msgstr "Starte..."
+
+#: ../etc/cfg_bindings.lua:118
+msgid "Query for file to view."
+msgstr "Anfrage um Datei anzuzeigen."
+
+#: ../mod_query/mod_query.lua:511
+msgid "Failed to open ~/.ssh/known_hosts"
+msgstr "Konnte ~/.ssh/known_hosts nicht öffnen."
+
+#: ../pwm/cfg_pwm_menus.lua:18 ../etc/cfg_menus.lua:13
+msgid "Session"
+msgstr "Sitzung"
+
+#: ../pwm/cfg_pwm_menus.lua:27
+msgid "List"
+msgstr "Anzeige"
+
+#~ msgid "Vertically/root"
+#~ msgstr "Pystysuunnassa juuressa"
+
+#
+#
+#~ msgid "Horizontally/root"
+#~ msgstr "Vaakasuunnassa juuressa"
+
+#~ msgid "Transpose parent"
+#~ msgstr "Käännä vanhempi"
+
+#~ msgid "Flip parent"
+#~ msgstr "Peilaa vanhempi"
diff --git a/po/fi.po b/po/fi.po
new file mode 100644 (file)
index 0000000..229dc45
--- /dev/null
+++ b/po/fi.po
@@ -0,0 +1,1760 @@
+# -*- encoding: iso-8859-1 -*-
+# 
+# Finnish language translations for Ion3.
+#
+# Copyright (c) Tuomo Valkonen 2004.
+#
+# This file is distributed under the same license as the Ion3 package.
+# Tuomo Valkonen <tuomov@iki.fi>, 2004.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Ion3\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-11-11 17:36+0200\n"
+"PO-Revision-Date: 2004-07-31 23:50+0300\n"
+"Last-Translator: Tuomo Valkonen <tuomov at iki.fi>\n"
+"Language-Team: none\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../ioncore/conf-bindings.c:96
+msgid "Insane key combination."
+msgstr "Järjetön näppäinyhdistelmä."
+
+#: ../ioncore/conf-bindings.c:100
+msgid "Could not convert keysym to keycode."
+msgstr "Näppäinsymbolin muunto näppäinkoodiksi epäonnistui."
+
+#: ../ioncore/conf-bindings.c:111
+#, c-format
+msgid "Unknown button \"%s\"."
+msgstr "Tuntematon nappi \"%s\"."
+
+#: ../ioncore/conf-bindings.c:116
+msgid "Insane button combination."
+msgstr "Järjetön nappiyhdistelmä."
+
+#: ../ioncore/conf-bindings.c:123 ../ioncore/conf-bindings.c:130
+msgid "Insane modifier combination."
+msgstr "Järjestön näppäinmuunninyhdistelmä."
+
+#: ../ioncore/conf-bindings.c:168
+#, c-format
+msgid "Can not wait on modifiers when no modifiers set in \"%s\"."
+msgstr ""
+"Näppäinmuuntimia ei voida odottaa sidonnassa \"%s\", koska siinä ei ole "
+"niitä."
+
+#: ../ioncore/conf-bindings.c:186
+#, c-format
+msgid "Unable to add binding %s."
+msgstr "Ei voitu lisätä sidontaa %s."
+
+#: ../ioncore/conf-bindings.c:191
+#, c-format
+msgid "Unable to remove binding %s."
+msgstr "Ei voitu poistaa sidontaa %s."
+
+#: ../ioncore/conf-bindings.c:230
+#, c-format
+msgid "Unable to add submap for binding %s."
+msgstr "Ei voida lisätä alikarttaa sidonnalle %s."
+
+#
+#: ../ioncore/conf-bindings.c:260
+msgid "Binding type not set."
+msgstr "Sidonnan tyyppiä ei ole asetettu."
+
+#: ../ioncore/conf-bindings.c:270
+#, c-format
+msgid "Unknown binding type \"%s\"."
+msgstr "Sidonnalle annettu tyyppi \"%s\" on tuntematon."
+
+#: ../ioncore/conf-bindings.c:292
+#, c-format
+msgid "Unknown area \"%s\" for binding %s."
+msgstr "Sidonnalle %s asetettu tuntematon alue \"%s.\""
+
+#: ../ioncore/conf-bindings.c:333
+#, c-format
+msgid "Unable to get bindmap entry %d."
+msgstr "Sidontakartan alkion %d haussa epäonnistuttiin."
+
+#: ../ioncore/conf-bindings.c:375
+msgid "Unable to convert keysym to string."
+msgstr "Näppäinsymbolia muunto merkkijonoksi epäonnistui."
+
+#: ../ioncore/conf-bindings.c:389
+msgid "Unable to convert button to string."
+msgstr "Napin tunnistetteen muunto merkkijonoksi epäonnistui."
+
+#
+#: ../ioncore/event.c:113
+msgid "Time request from X server failed."
+msgstr "Ajan pyyntö X-palvelimelta epäonnistui."
+
+#
+#: ../ioncore/exec.c:177
+msgid "Not saving state: running under session manager."
+msgstr "Tilaa ei talleteta, koska olemme istunnonhallinnan alaisia."
+
+#: ../ioncore/strings.c:107 ../ioncore/strings.c:143 ../ioncore/strings.c:176
+msgid "Invalid multibyte string."
+msgstr "Virheellinen multibyte-merkkijono."
+
+#: ../ioncore/strings.c:267
+#, c-format
+msgid "Error compiling regular expression: %s"
+msgstr "Virhe säännölisen lausekkeen käännöksessä: %s"
+
+#: ../ioncore/modules.c:158
+msgid "Invalid module name."
+msgstr "Moduulin nimi on tuntematon."
+
+#
+#: ../ioncore/modules.c:170
+msgid "The module is already loaded."
+msgstr "Moduuli on jo ladattu."
+
+#: ../ioncore/modules.c:185
+msgid ""
+"Module version information not found or version mismatch. Refusing to use."
+msgstr ""
+"Moduulin versiotieto joko puuttuu tai on väärä. Näin ollen sitä "
+"kieltäydytään käyttämästä."
+
+#: ../ioncore/modules.c:196
+#, c-format
+msgid "Unable to initialise module %s."
+msgstr "Virhe moduulin %s alustuksessa."
+
+#: ../ioncore/modules.c:220 ../../libextl-3/readconfig.c:388
+#, c-format
+msgid "Unable to find '%s' on search path."
+msgstr "Hakupolulta ei löytynyt '%s':ää."
+
+#: ../ioncore/modules.c:291
+msgid "Unknown module."
+msgstr "Tuntematon moduuli."
+
+#: ../ioncore/modules.c:299
+msgid "Unable to initialise module."
+msgstr "Moduulin alustus epäonnistui."
+
+#: ../ioncore/modules.c:344
+msgid "No module to load given."
+msgstr "Ladattava moduulia ei ole annettu."
+
+#
+#: ../ioncore/property.c:355 ../ioncore/property.c:364
+msgid "Invalid arguments."
+msgstr "Virheelliset parametrit."
+
+#: ../ioncore/screen.c:298
+msgid "act: "
+msgstr "act: "
+
+#
+#: ../ioncore/screen.c:481
+msgid "Only workspace may not be destroyed."
+msgstr "Ainoata työpöytää ei voi tuhota."
+
+#
+#: ../ioncore/screen.c:489
+msgid "Screens may not be destroyed."
+msgstr "Näyttöjä ei voi tuhota."
+
+#: ../ioncore/screen.c:525
+msgid "Invalid offset."
+msgstr "Virheellinen poikkeama."
+
+#: ../ioncore/screen.c:568
+#, c-format
+msgid "Unable to create a workspace on screen %d."
+msgstr "Työpöydän luonti ruudulle %d epäonnistui."
+
+#: ../ioncore/sizehint.c:151
+msgid "Invalid client-supplied width/height increment."
+msgstr "Asiakkaan ilmoittama pysty/vaakalisäys koolle on virheellinen."
+
+#: ../ioncore/sizehint.c:159
+msgid "Invalid client-supplied aspect-ratio."
+msgstr "Asiakkaan ilmoittama sivusuhde on virheellinen."
+
+#: ../ioncore/ioncore.c:74
+msgid ""
+"This program is free software; you can redistribute it and/or\n"
+"modify it under the terms of the GNU Lesser General Public\n"
+"License as published by the Free Software Foundation; either\n"
+"version 2.1 of the License, or (at your option) any later version.\n"
+"\n"
+"This program is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
+"Lesser General Public License for more details.\n"
+msgstr ""
+"Tämä ohjelma on vapaa; tätä ohjelmaa on sallittu levittää edelleen\n"
+"ja muuttaa GNU vähäisemmän yleisen lisenssin (LGPL lisenssin) ehtojen\n"
+"mukaan sellaisina kuin Free Software Foundation on ne julkaissut; joko\n"
+"Lisenssin version 2.1, tai (valinnan mukaan) minkä tahansa myöhemmän\n"
+"version mukaisesti.\n"
+"\n"
+"Tätä ohjelmaa levitetään siinä toivossa, että se olisi hyödyllinen,\n"
+"mutta ilman mitään takuuta; ilman edes hiljaista takuuta kaupallisesti\n"
+"hyväksyttävästä laadusta tai soveltuvuudesta tiettyyn tarkoitukseen.\n"
+"Katso LGPL lisenssistä lisää yksityiskohtia.\n"
+
+#: ../ioncore/ioncore.c:155
+msgid "No encoding given in LC_CTYPE."
+msgstr "LC_CTYPE ei kerro enkoodausta."
+
+#: ../ioncore/ioncore.c:437
+#, c-format
+msgid "Could not connect to X display '%s'"
+msgstr "Yhteydenotto X-palvelimeen '%s' epäonnistui."
+
+#: ../ioncore/ioncore.c:489
+msgid "Could not find a screen to manage."
+msgstr "Yhtään hallittavissa olevaa juuri-ikkunaa ei löytynyt."
+
+#: ../ioncore/xic.c:38
+msgid "Failed to open input method."
+msgstr "Syöttömenetelmän avaaminen epäonnistui."
+
+#: ../ioncore/xic.c:43
+msgid "Input method doesn't support any style."
+msgstr "Syöttömenetelmä ei tue yhtään syöttötapaa."
+
+#: ../ioncore/xic.c:58
+msgid "input method doesn't support my preedit type."
+msgstr "Syöttömenetelmä ei tue kaivattua esimuokkaustyyliä."
+
+#: ../ioncore/xic.c:86
+msgid "Failed to create input context."
+msgstr "Syöttökontekstin luonti epäonnistui."
+
+#: ../ioncore/clientwin.c:411
+#, c-format
+msgid "The transient_for hint for \"%s\" points to itself."
+msgstr "Ikkunan \"%s\" transient_for neuvo osoittaa itseensä."
+
+#: ../ioncore/clientwin.c:415
+#, c-format
+msgid ""
+"Client window \"%s\" has broken transient_for hint. (\"Extended WM hints\" "
+"multi-parent brain damage?)"
+msgstr "Ikkunan \"%s\" transient_for neuvo on rikki."
+
+#: ../ioncore/clientwin.c:420
+#, c-format
+msgid "The transient_for window for \"%s\" is not on the same screen."
+msgstr "Ikkunan \"%s\" transient_for neuvo osoittaa eri näytölle."
+
+#: ../ioncore/clientwin.c:440 ../ioncore/clientwin.c:527
+#: ../ioncore/clientwin.c:1300
+#, c-format
+msgid "Window %#x disappeared."
+msgstr "Ikkuna %#x katosi."
+
+#: ../ioncore/clientwin.c:547
+msgid "Unable to find a matching root window!"
+msgstr "Vastaavaa juuri-ikkunaa ei löytynyt."
+
+#: ../ioncore/clientwin.c:582
+#, c-format
+msgid "Unable to manage client window %#x."
+msgstr "Asiakasikkunan %x hallittavaksi saattaminen epäonnistui."
+
+#: ../ioncore/clientwin.c:633
+msgid "Changes is WM_TRANSIENT_FOR property are unsupported."
+msgstr "Muutoksia WM_TRANSIENT_FOR ominaisuudessa ei tueta."
+
+#
+#: ../ioncore/clientwin.c:809
+msgid "Client does not support the WM_DELETE protocol."
+msgstr "Asiakasikkuna ei tue WM_DELETE-protokollaa."
+
+#: ../ioncore/clientwin.c:1306
+msgid "Saved client window does not want to be managed."
+msgstr "Talletettu asiakasikkuna ei halua sitä hallittavan."
+
+#
+#: ../ioncore/colormap.c:96
+msgid "Unable to store colourmap watch info."
+msgstr "Värikartan valvonnan alustaminen epäonnistui."
+
+#: ../ioncore/region.c:46
+msgid "Creating region with negative width or height!"
+msgstr "Yritys luoda korkeudeltaan tai leveydeltään negatiivinen alue."
+
+#: ../ioncore/region.c:94
+#, c-format
+msgid "Destroying object \"%s\" with client windows as children."
+msgstr "Olio \"%s\", jolla on vielä asiakasikkunoita lapsena, tuhotaan."
+
+#
+#: ../ioncore/region.c:395
+msgid "Failed to rescue some client windows - not closing."
+msgstr "Joidenkin asiakasikkunoiden pelastaminen epäonnistui - ei suljeta."
+
+#: ../ioncore/attach.c:71 ../ioncore/pholder.c:46
+msgid "Unable to reparent."
+msgstr "Vanhemman vaihto epäonnistui."
+
+#: ../ioncore/attach.c:157 ../ioncore/frame-pointer.c:282
+#, c-format
+msgid "Attempt to make region %s manage its ancestor %s."
+msgstr "Yritys saattaa alue %s hallitsemaan sen esivanhempaa %s."
+
+#: ../ioncore/manage.c:77
+msgid "Unable to find a screen for a new client window."
+msgstr "Uudelle asiakasikkunalle ei löytynyt näyttöä."
+
+#: ../ioncore/rootwin.c:223
+#, c-format
+msgid "Unable to redirect root window events for screen %d."
+msgstr "Juuri-ikkunan %d viestien uudelleenohjaus epäonnistui."
+
+#: ../ioncore/rootwin.c:319 ../ioncore/rootwin.c:344
+msgid "Xinerama sanity check failed; overlapping screens detected."
+msgstr "Xinerama-tiedon järkevyystarkastus havaitsi päällekkäisiä ruutuja."
+
+#: ../ioncore/rootwin.c:326 ../ioncore/rootwin.c:351
+msgid "Xinerama sanity check failed; zero size detected."
+msgstr "Xinerama-tiedon järkevyystarkastus havaitsi päällekkäisiä ruutuja."
+
+#: ../ioncore/rootwin.c:379 ../ioncore/rootwin.c:397
+msgid "Don't know how to get Xinerama information for multiple X root windows."
+msgstr "Xinerama-tietoa ei osata käyttää usealle juuri-ikkunalle."
+
+#: ../ioncore/rootwin.c:393
+msgid "Error retrieving Xinerama information."
+msgstr "Xinerama-tietoja ei saatu."
+
+#: ../ioncore/rootwin.c:432 ../ioncore/rootwin.c:451
+#, c-format
+msgid "Unable to setup Xinerama screen %d."
+msgstr "Xinerama-ruudun %d hallittavaksi saattaminen epäonnistui."
+
+#: ../ioncore/rootwin.c:464
+#, c-format
+msgid "Unable to setup X screen %d."
+msgstr "X-ruudun %d hallittavaksi saattaminen epäonnistui."
+
+#: ../ioncore/names.c:90
+#, c-format
+msgid "Corrupt instance number %s."
+msgstr "Rikkinäinen instanssinumero %s."
+
+#: ../ioncore/saveload.c:125
+#, c-format
+msgid "Unknown class \"%s\", cannot create region."
+msgstr "Luokka \"%s\" on tuntematon. Aluetta ei voida luoda."
+
+#: ../ioncore/saveload.c:205
+#, c-format
+msgid ""
+"There were errors loading layout. Backing up current layout savefile as\n"
+"%s.\n"
+"If you are _not_ running under a session manager and wish to restore your\n"
+"old layout, copy this backup file over the layout savefile found in the\n"
+"same directory while Ion is not running and after having fixed your other\n"
+"configuration files that are causing this problem. (Maybe a missing\n"
+"module?)"
+msgstr ""
+"Talletetun sijoittelun latauksessa oli virheitä. Siitä tehdään varmuuskopio\n"
+"%s.\n"
+"Jos _et_ käytä istunnonhallintaohjelmaa ja haluat palauttaa edellisen\n"
+"sijoittelun, kopioi tämä varmuuskopio uuden talletustiedoston päälle\n"
+"samassa hakemistossa kun Ion ei ole ajossa, ja kun olet korjannut tämän\n"
+"tilanteen aiheuttavat virheet (mahdollisesti puuttuva moduuli?) muissa\n"
+"asetustiedostoissasi."
+
+#: ../ioncore/saveload.c:248
+msgid "Unable to get file for layout backup."
+msgstr "Sijoittelun varmuuskopiolle ei voitu muodostaa tiedostonimeä."
+
+#: ../ioncore/saveload.c:252
+#, c-format
+msgid "Backup file %s already exists."
+msgstr "Varmuuskopio %s on jo olemassa."
+
+#: ../ioncore/saveload.c:258
+msgid "Failed backup."
+msgstr "Varmuuskopionti epäonnistui."
+
+#
+#: ../ioncore/saveload.c:263
+msgid "Unable to initialise layout on any screen."
+msgstr "Sijoittelun alustus epäonnistui kaikilla näytöillä."
+
+#: ../ioncore/saveload.c:283
+#, c-format
+msgid "Unable to get configuration for screen %d."
+msgstr "Ruudun %d asetukset puuttuvat."
+
+#: ../ioncore/saveload.c:296
+msgid "Unable to save layout."
+msgstr "Sijoittelun talletus epäonnistui."
+
+#: ../ioncore/conf.c:143
+msgid "User directory can not be set."
+msgstr "Käyttäjän hakemistoa ei pystytä asettamaan."
+
+#: ../ioncore/conf.c:217
+msgid "Some bindmaps were empty, loading ioncore_efbb."
+msgstr "Jotkut sidontakartat olivat tyhjiä. Ladataan ioncore_efbb."
+
+#: ../ioncore/fullscreen.c:98
+msgid "Failed to enter full screen mode."
+msgstr "Vaihto kokoruudun tilaan epäonnistui."
+
+#: ../ioncore/fullscreen.c:120
+msgid ""
+"Failed to return from full screen mode; remaining manager or parent from "
+"previous location refused to manage us."
+msgstr ""
+"Asiakasikkunan paluu kokoruudun tilasta epäonnistui; edellinen hallitsija "
+"tai vanhempi kieltäytyi hallitsemasta ikkunaa."
+
+#
+#: ../ioncore/mplex.c:172
+msgid "Refusing to destroy - not empty."
+msgstr "Tuhoamisesta kieltäydytään - ei tyhjä."
+
+#
+#: ../ioncore/mplex.c:1678
+msgid "Invalid position setting."
+msgstr "Virheellinen paikka-asetus."
+
+#
+#: ../ioncore/mplex.c:1718
+msgid "Invalid action setting."
+msgstr "Virheellinen toiminto-asetus."
+
+#: ../ioncore/gr.c:119
+#, c-format
+msgid "Drawing engine %s is not registered!"
+msgstr "Piirtomoottoria %s ei ole rekisteröity!"
+
+#: ../ioncore/gr.c:138
+#, c-format
+msgid "Unable to find brush for style '%s'."
+msgstr "Tyylille '%s' ei löytynyt pensseliä."
+
+#: ../ioncore/gr.c:408
+msgid "No drawing engines loaded, trying \"de\"."
+msgstr "Yhtään piirtomoottoria ei ole ladattu. Yritetään oletusta \"de\"."
+
+#: ../ioncore/group.c:184 ../mod_tiling/tiling.c:96
+#, c-format
+msgid "Error reparenting %s."
+msgstr "Virhe alueen %s vanhemman vaihdossa."
+
+#
+#: ../ioncore/group.c:481 ../mod_tiling/tiling.c:668
+msgid "Workspace not empty - refusing to destroy."
+msgstr "Työpöytää ei voida tuhota, koska se ei ole tyhjä."
+
+#: ../ioncore/group.c:680
+msgid "'bottom' already set."
+msgstr "'pohja' on jo asetettu."
+
+#
+#: ../ioncore/navi.c:45
+msgid "Invalid parameter."
+msgstr "Virheellinen parametri."
+
+#: ../ioncore/navi.c:72
+msgid "Invalid direction parameter."
+msgstr "Virheellinen suunta."
+
+#: ../ioncore/group-ws.c:67
+#, c-format
+msgid "Unknown placement method \"%s\"."
+msgstr "Tuntematon sijoittelumenetelmä \"%s\"."
+
+#
+#
+#: ../mod_tiling/tiling.c:77
+msgid "Split not on workspace."
+msgstr "Jako ei ole tällä työpöydällä."
+
+#: ../mod_tiling/tiling.c:373
+msgid "Unable to create a node for status display."
+msgstr "Solmun luonti tilanäytölle epäonnistui."
+
+#: ../mod_tiling/tiling.c:386
+msgid "Unable to create new split for status display."
+msgstr "Uuden jaon tekeminen tilanäytölle epäonnistui."
+
+#: ../mod_tiling/tiling.c:937
+msgid "Invalid direction"
+msgstr "Virheellinen suunta."
+
+#
+#: ../mod_tiling/tiling.c:970 ../mod_tiling/split.c:1018
+msgid "Invalid node."
+msgstr "Epäkelpo solmu."
+
+#
+#: ../mod_tiling/tiling.c:989
+msgid "Unable to split."
+msgstr "Halkaisu ei ole mahdollista."
+
+#: ../mod_tiling/tiling.c:1092
+msgid "Nil frame."
+msgstr "Kehys on asettamatta."
+
+#: ../mod_tiling/tiling.c:1097
+msgid "The frame is not managed by the workspace."
+msgstr "Kehys ei ole tämän työpöydän hallinnoima."
+
+#: ../mod_tiling/tiling.c:1196
+msgid "Nil parameter."
+msgstr "Parametri on asettamatta."
+
+#: ../mod_tiling/tiling.c:1201
+msgid "Manager doesn't match."
+msgstr "Hallitsija on väärä."
+
+#: ../mod_tiling/tiling.c:1238
+msgid "The status display is not a valid parameter for this routine."
+msgstr "Tilanäyttö ei ole kelpo parametri tälle toiminnolle."
+
+#: ../mod_tiling/tiling.c:1329
+msgid "Refusing to float split directly containing the status display."
+msgstr "Tilanäytön suoraan sisältävää jakoa ei voida kelluttaa."
+
+#: ../mod_tiling/tiling.c:1392
+msgid "No suitable split here."
+msgstr "Ei sopivaa jakoa tässä."
+
+#
+#: ../mod_tiling/tiling.c:1428
+msgid "Could not get split tree."
+msgstr "Halkaisupuu puuttuu."
+
+#: ../mod_tiling/tiling.c:1449
+msgid "Workspace already has a status display node."
+msgstr "Työpöydällä on jo solmu tilanäytölle."
+
+#
+#: ../mod_tiling/tiling.c:1487
+msgid "Missing region parameters."
+msgstr "Alueen parameterit puuttuu."
+
+#: ../mod_tiling/tiling.c:1531 ../mod_tiling/splitfloat.c:780
+msgid "Invalid direction."
+msgstr "Virheellinen suunta."
+
+#
+#: ../mod_tiling/tiling.c:1606
+msgid "No split type given."
+msgstr "Halkaisun tyyppiä ei ole asetettu."
+
+#: ../mod_tiling/tiling.c:1619
+msgid "Unknown split type."
+msgstr "Tuntematon halkaisutyyppi."
+
+#
+#
+#: ../mod_tiling/tiling.c:1659
+msgid "The workspace is empty."
+msgstr "Työpöytä on tyhjä."
+
+#: ../mod_tiling/placement.c:106
+#, c-format
+msgid ""
+"Ooops... could not find a region to attach client window to on workspace %s."
+msgstr "Työpöydältä %s ei löytynyt aluetta johon liittää asiakasikkuna."
+
+#
+#: ../mod_tiling/split.c:524
+msgid "Unable to move the status display out of way."
+msgstr "Tilanäyttöä ei voida siirtää pois tieltä."
+
+#: ../mod_tiling/split.c:937
+msgid "REGION_RQGEOM_TRYONLY unsupported for status display."
+msgstr "REGION_RQGEOM_TRYONLY:ä ei tueta tilanäytölle."
+
+#
+#: ../mod_tiling/split.c:1082
+msgid "Splitting the status display is not allowed."
+msgstr "Tilanäytön halkaisu ei ole sallittu."
+
+#: ../mod_tiling/split.c:1113 ../mod_tiling/splitfloat.c:903
+msgid "Unable to split: not enough free space."
+msgstr "Ei voida halkaista: liian vähän tilaa."
+
+#: ../mod_tiling/split.c:1864
+#, c-format
+msgid "Unable to get configuration for %s."
+msgstr "Alueelle \"%s\" ei saatu asetuksia."
+
+#: ../mod_tiling/split-stdisp.c:602 ../mod_tiling/split-stdisp.c:627
+msgid "Status display in bad split configuration."
+msgstr "Tilanäyttö huonossa halkaisuasetelmassa."
+
+#: ../mod_tiling/split-stdisp.c:667
+msgid "Status display badly located in split tree."
+msgstr "Tilanäyttö on huonosti sijoittuneena halkaisupuuhun."
+
+#: ../mod_tiling/ops.c:28
+msgid "Already detached"
+msgstr "Irroitettu jo"
+
+#: ../mod_tiling/ops.c:142
+msgid "Not member of a group"
+msgstr "Ei jäsenenä missään ryhmässä"
+
+#: ../mod_tiling/ops.c:147
+msgid "Manager group already has bottom"
+msgstr "Managerilla on jo 'pohja'"
+
+#: ../mod_query/wedln.c:777
+msgid "history"
+msgstr "hist.täyd."
+
+#: ../mod_query/fwarn.c:34
+msgid "Error:\n"
+msgstr "Virhe:\n"
+
+#
+#: ../mod_menu/menu.c:537
+msgid "Empty menu."
+msgstr "Tyhjä valikko."
+
+#
+#: ../mod_sm/sm.c:110
+msgid "Failed to set session directory."
+msgstr "Istuntohakemiston asetus epäonnistui."
+
+#: ../mod_sm/sm_session.c:86
+msgid "Too many ICE connections."
+msgstr "Liian monta ICE-yhteyttä."
+
+#: ../mod_sm/sm_session.c:228
+msgid "Failed to save session state"
+msgstr "Istunnon tilan tallentaminen epäonnistui."
+
+#: ../mod_sm/sm_session.c:247
+msgid "Failed to request save-yourself-phase2 from session manager."
+msgstr "Save-yourself vaiheen kaksi pyyntö istunnonhallitsijalta epäonnistui."
+
+#
+#: ../mod_sm/sm_session.c:296
+msgid "SESSION_MANAGER environment variable not set."
+msgstr "Ympäristömuuttujaa SESSION_MANAGER ei ole asetettu."
+
+#: ../mod_sm/sm_session.c:301
+msgid "Session Manager: IceAddConnectionWatch failed."
+msgstr "Virhe istunnonhallinan kutsussa IceAddConnectionWatch."
+
+#
+#: ../mod_sm/sm_session.c:326
+msgid "Unable to connect to the session manager."
+msgstr "Yhteydenotto istunnonhallintaohjelmaan epäonnistui."
+
+#: ../mod_sp/main.c:85
+msgid "Unable to create scratchpad."
+msgstr "Suttausalueen luonti epäonnistui."
+
+#: ../mod_statusbar/main.c:75
+msgid "reading a pipe"
+msgstr "putken luku"
+
+#: ../mod_statusbar/statusbar.c:1046
+#, c-format
+msgid "[ %date || load: %load ] %filler%systray"
+msgstr ""
+
+#: ../de/init.c:42
+#, c-format
+msgid "Border attribute %s sanity check failed."
+msgstr "Reunan arvon '%s' järkevyystarkistus epäonnistui."
+
+#: ../de/init.c:65
+#, c-format
+msgid "Unknown border style \"%s\"."
+msgstr "Reunan tyyli \"%s\" on tuntematon."
+
+#: ../de/init.c:97
+#, c-format
+msgid "Unable to allocate colour \"%s\"."
+msgstr "Värin \"%s\" varaaminen epäonnistui."
+
+#: ../de/init.c:160
+#, c-format
+msgid "Corrupt substyle table %d."
+msgstr "Alityylin %d taulu on rikki."
+
+#: ../de/init.c:193
+#, c-format
+msgid "Unknown text alignment \"%s\"."
+msgstr "Tuntematon tekstin tasaus \"%s\"."
+
+#: ../de/init.c:263
+#, c-format
+msgid "'based_on' for %s points back to the style itself."
+msgstr "Tyylin %s 'based_on' -asetus osoittaa itseensä."
+
+#: ../de/init.c:266
+#, c-format
+msgid "Unknown base style \"%s\"."
+msgstr "Tuntematon pohjatyyli \"%s\"."
+
+#: ../de/font.c:47
+#, c-format
+msgid ""
+"Fontset for font pattern '%s' implements context dependent drawing, which is "
+"unsupported. Expect clutter."
+msgstr ""
+"Kirjaisinjoukko kirjaisinmallille '%s' vaatii kontekstiriippuvaista piirtoa, "
+"jota ei tueta. Odotettavissa on sotkua."
+
+#: ../de/font.c:59
+#, c-format
+msgid "Could not load font \"%s\", trying \"%s\""
+msgstr "Kirjaisimen \"%s\" lataaminen epäonnistui. Yritetään \"%s\":ää."
+
+#: ../de/font.c:63
+msgid "Failed to load fallback font."
+msgstr "Hätävarakirjaisimen lataus epäonnistui."
+
+#: ../de/style.c:315
+#, c-format
+msgid "Style %s still in use [%d] but the module is being unloaded!"
+msgstr "Tyyli %s on vielä käytössä [%d], mutta moduulia poistetaan!"
+
+#: ../ion/ion.c:42 ../pwm/pwm.c:42
+msgid "X display to use"
+msgstr "Käytettävä X-palvelin/näyttö"
+
+#: ../ion/ion.c:45 ../pwm/pwm.c:45
+msgid "Configuration file"
+msgstr "Ensisijainen asetustiedosto"
+
+#: ../ion/ion.c:48 ../pwm/pwm.c:48
+msgid "Add directory to search path"
+msgstr "Lisää hakemisto hakupolulle"
+
+#: ../ion/ion.c:51 ../pwm/pwm.c:51
+msgid "Manage default root window/non-Xinerama screen only"
+msgstr ""
+"Hallitse vain oletusarvoista juuri-ikkunaa. (Vastaa ensisijaista näyttöä jos "
+"Xinerama ei ole käytössä.)"
+
+#: ../ion/ion.c:55
+msgid "Use Xinerama screen information (default: 1/yes)"
+msgstr "Hyödynnä Xineramaa (oletus: 1/kyllä)"
+
+#: ../ion/ion.c:58 ../pwm/pwm.c:58
+msgid "Ignored: not compiled with Xinerama support"
+msgstr "Jätetään huomiotta: ohjelmaa ei ole käännetty Xinerama-tuella"
+
+#: ../ion/ion.c:62 ../pwm/pwm.c:62
+msgid "Name of session (affects savefiles)"
+msgstr "Istunnon nimi (vaikuttaa talletustiedostoihin)"
+
+#: ../ion/ion.c:65 ../pwm/pwm.c:65
+msgid "Session manager client ID"
+msgstr "Istunnonhallinnan asiakastunniste"
+
+#: ../ion/ion.c:68 ../pwm/pwm.c:68
+msgid "Do not create startup error log and display it with xmessage."
+msgstr "Älä luo käynnistysvirhelokia ja näytä sitä xmessage:lla."
+
+#: ../ion/ion.c:72 ../pwm/pwm.c:72
+msgid "Show this help"
+msgstr "Näytä tämä aputeksti"
+
+#: ../ion/ion.c:75 ../pwm/pwm.c:75
+msgid "Show program version"
+msgstr "Näytä ohjelman versio"
+
+#: ../ion/ion.c:78 ../pwm/pwm.c:78
+msgid "Show about text"
+msgstr "Näytä tietoja ohjelmasta"
+
+#: ../ion/ion.c:93
+msgid "Could not get user configuration file directory."
+msgstr "Käyttäjän asetustiedostohakemisto puuttuu, eikä sitä voitu luoda."
+
+#: ../ion/ion.c:107
+#, c-format
+msgid "%s/welcome.txt"
+msgstr "%s/welcome.fi.txt"
+
+#: ../ion/ion.c:140 ../pwm/pwm.c:87
+#, c-format
+msgid ""
+"Usage: %s [options]\n"
+"\n"
+msgstr ""
+"Käyttö: %s [valintoja]\n"
+"\n"
+
+#: ../ion/ion.c:200 ../pwm/pwm.c:150
+msgid "Invalid parameter to -xinerama."
+msgstr "Virheellinen parametri -xinerama:lle"
+
+#
+#: ../ion/ion.c:219 ../pwm/pwm.c:169
+msgid "Invalid command line."
+msgstr "Virheellinen komentorivi."
+
+#: ../ion/ion.c:241
+msgid "Ion startup error log:\n"
+msgstr "Ionin käynnistysvirheloki:\n"
+
+#: ../ion/ion.c:252 ../pwm/pwm.c:202
+msgid "Refusing to start due to encountered errors."
+msgstr "Ohjelma kieltäytyy käynnistymästä tavattujen virheiden johdosta."
+
+#: ../pwm/pwm.c:55
+msgid "Use Xinerama screen information (default: 0/no)"
+msgstr "Hyödynnä Xineramaa (oletus: 0/ei)"
+
+#: ../pwm/pwm.c:191
+msgid "PWM startup error log:\n"
+msgstr "PWM:n käynnistysvirheloki:\n"
+
+#: ../../libextl-3/readconfig.c:86
+msgid "$HOME not set"
+msgstr "Ympäristömuuttujaa HOME ei ole asetettu."
+
+#: ../../libextl-3/readconfig.c:113
+msgid "User directory not set. Unable to set session directory."
+msgstr ""
+"Käyttäjän hakemistoa ei ole asetettu, joten istuntohakemistoa ei voida "
+"asettaa."
+
+#: ../../libextl-3/readconfig.c:254
+#, c-format
+msgid "Falling back to %s."
+msgstr "Yritetään tiedostoa %s."
+
+#: ../../libextl-3/readconfig.c:474
+#, c-format
+msgid "Unable to create session directory \"%s\"."
+msgstr "Istuntohakemiston \"%s\" luonti epäonnistui."
+
+#: ../../libextl-3/luaextl.c:117
+msgid "Lua stack full."
+msgstr "Luan pino on täysi."
+
+#
+#: ../../libextl-3/luaextl.c:143
+msgid "Unknown Lua error."
+msgstr "Tuntematon Lua:n virhe."
+
+#: ../../libextl-3/luaextl.c:490
+msgid "Stack trace:"
+msgstr "Pinojälki:"
+
+#: ../../libextl-3/luaextl.c:497
+#, c-format
+msgid ""
+"\n"
+"(Unable to get debug info for level %d)"
+msgstr ""
+"\n"
+"(Debuggaustiedot eivät ole saatavilla tasolle %d)"
+
+#: ../../libextl-3/luaextl.c:515
+msgid ""
+"\n"
+"  [Skipping unnamed C functions.]"
+msgstr ""
+"\n"
+"  [Ohitetaan nimettömiä C-funktioita.]"
+
+#: ../../libextl-3/luaextl.c:566
+msgid "Internal error."
+msgstr "Sisäinen virhe."
+
+#: ../../libextl-3/luaextl.c:585
+msgid "Unable to initialize Lua."
+msgstr "Lua:n alustus epäonnistui."
+
+#: ../../libextl-3/luaextl.c:1336
+msgid ""
+"Too many return values. Use a C compiler that has va_copy to support more."
+msgstr ""
+"Liian monta paluuarvoa. Useamman tukemiseksi käytä C-kääntäjää, joka tukee "
+"va_copy:ä."
+
+#: ../../libextl-3/luaextl.c:1356
+msgid "Returned dead object."
+msgstr "Kuollut olio palautettu."
+
+#: ../../libextl-3/luaextl.c:1359
+#, c-format
+msgid "Invalid return value (expected '%c', got lua type \"%s\")."
+msgstr ""
+"Virheellinen paluuarvo (odotettiin tyyppiä '%c', mutta saatiin Lua:n tyyppi "
+"\"%s\")."
+
+#: ../../libextl-3/luaextl.c:1395 ../../libextl-3/luaextl.c:1750
+msgid "Stack full."
+msgstr "Pino täysi."
+
+#: ../../libextl-3/luaextl.c:1761
+#, c-format
+msgid "Argument %d to %s is a dead object."
+msgstr "Parametri %d %s:lle on kuollut olio."
+
+#: ../../libextl-3/luaextl.c:1764
+#, c-format
+msgid ""
+"Argument %d to %s is of invalid type. (Argument template is '%s', got lua "
+"type %s)."
+msgstr ""
+"Parameteri %d funktiolle %s on väärää tyyppiä. (Parametripohja on '%s', "
+"saatiin Lua:n tyyppi '%s'.)"
+
+#: ../../libextl-3/luaextl.c:1827
+msgid "L1 call handler upvalues corrupt."
+msgstr "Tason 1 kutsunkäsittelijän \"upvalue\":t rikki."
+
+#: ../../libextl-3/luaextl.c:1832
+msgid "Called function has been unregistered."
+msgstr "Kutsuttu funktio on poistettu."
+
+#: ../../libextl-3/luaextl.c:1843
+#, c-format
+msgid "Attempt to call an unsafe function \"%s\" in restricted mode."
+msgstr "Yritys kutsua turvatonta funktiota \"%s\" rajoitetussa tilassa."
+
+#: ../../libextl-3/luaextl.c:1956
+#, c-format
+msgid ""
+"Function '%s' has more parameters than the level 1 call handler can handle"
+msgstr ""
+"Funktiolla %s on enemmän parametrejä kuin mihin tason yksi kutsunkäsittelijä "
+"kykenee."
+
+#
+#: ../../libextl-3/luaextl.c:2373
+msgid "Maximal serialisation depth reached."
+msgstr "Suurin mahdollinen talletuksen rekursiosyvyys saavutettu."
+
+#: ../../libextl-3/luaextl.c:2394
+#, c-format
+msgid "Unable to serialise type %s."
+msgstr "Tyyppiä %s ei voida tallettaa."
+
+#: ../../libextl-3/luaextl.c:2425
+msgid "-- This file has been generated by Ion. Do not edit.\n"
+msgstr "-- Tämä tiedosto on Ionin luoma. Älä editoi sitä.\n"
+
+#: ../../libextl-3/misc.c:17
+#, c-format
+msgid ""
+"Type checking failed in level 2 call handler for parameter %d (got %s, "
+"expected %s)."
+msgstr ""
+"Tyyppitarkistus epäonnistui tason 2 kutsunkäsittelijässä parametrille #%d "
+"(oli %s, odotettiin %s)."
+
+msgid "Grow-D"
+msgstr "Kasvu: alas"
+
+msgid "Grow-U"
+msgstr "Kasvu: ylös"
+
+msgid "Grow-R"
+msgstr "Kasvu: oikealle"
+
+msgid "Grow-L"
+msgstr "Kasvu: vasemmalle"
+
+msgid "Pos-BR"
+msgstr "Paikka: alaoikea"
+
+msgid "Pos-BL"
+msgstr "Paikka: alavasen"
+
+msgid "Pos-TR"
+msgstr "Paikka: yläoikea"
+
+msgid "Pos-TL"
+msgstr "Paikka: ylävasen"
+
+msgid "Toggle floating dock."
+msgstr "Näytä/piiloita kelluva laituri (dock)."
+
+msgid "Clear the menu's typeahead find buffer."
+msgstr "Tyhjää valikon hakupuskuri."
+
+msgid "Select next/previous menu entry."
+msgstr "Siirry seuraavaan/edelliseen valintaan."
+
+#
+#
+msgid "Activate current menu entry."
+msgstr "Suorita valinta."
+
+msgid "Close the menu."
+msgstr "Sulje valikko."
+
+msgid "New tiling"
+msgstr "Uusi laatoitus"
+
+msgid "Detach"
+msgstr "Irroita"
+
+#
+msgid "Transpose"
+msgstr "Käännä"
+
+#
+msgid "Flip"
+msgstr "Peilaa"
+
+#
+#
+msgid "Split horizontally"
+msgstr "Jaa vaakasuunnassa"
+
+#
+#
+msgid "Split vertically"
+msgstr "Jaa pystysuunnassa"
+
+#
+msgid "At root"
+msgstr "Juuressa"
+
+msgid "Below"
+msgstr "Alapuolella"
+
+msgid "Above"
+msgstr "Yläpuolella"
+
+msgid "At right"
+msgstr "Oikealla"
+
+msgid "At left"
+msgstr "Vasemmalla"
+
+msgid "Float split"
+msgstr "Kelluta"
+
+#
+msgid "Destroy frame"
+msgstr "Tuhoa kehys"
+
+msgid "Tile frame, if no tiling exists on the workspace"
+msgstr "Laatoita kehys, jos työpöydällä ei ole kehystä"
+
+msgid "Detach window from tiled frame"
+msgstr "Irroita ikkuna laatoitetusta kehyksestä"
+
+msgid "Destroy current frame."
+msgstr "Tuhoa tämänhetkinen kehys."
+
+msgid "Split current frame horizontally."
+msgstr "Halkaise tämänhetkinen kehys vaakasuunnassa."
+
+msgid "Go to frame above/below/right/left of current frame."
+msgstr ""
+"Siirry tämänhetkisen kehyksen yllä/alla/vasemmalle/oikealla olevaan "
+"kehykseen."
+
+msgid "Split current frame vertically."
+msgstr "Halkaise tämänhetkinen kehys pystysuunnassa."
+
+msgid "Failed to create statusbar."
+msgstr "Epäonnistuttiin tilarivin luonnissa"
+
+msgid "Screen already has an stdisp. Refusing to create a statusbar."
+msgstr "Ruudulla on jo tilanäyttö. Kieltäydytään luomasta tilariviä."
+
+msgid "Screen not found."
+msgstr "Ruutua ei löytynyt."
+
+msgid "Failed to start ion-statusd."
+msgstr "Ion-statusd:n käynnistys eopäonnistui."
+
+msgid "Errors starting ion-statusd:\n"
+msgstr "Ion-statusd:n käynnistysvirheet:\n"
+
+msgid "Could not find %s"
+msgstr "Ei löydettyä %s:ää."
+
+msgid "ion-statusd quit."
+msgstr "ion-statusd päätti suorituksen."
+
+msgid "Not a directory."
+msgstr "Ei hakemisto."
+
+msgid "Invalid command"
+msgstr "Virheellinen komento"
+
+msgid "Error in command string: "
+msgstr "Virhe komentojonossa:"
+
+msgid "Error compiling guard: %s"
+msgstr "Virhe vahdin käännössä. %s"
+
+msgid "Invalid guard %s."
+msgstr "Virheellinen vahti %s."
+
+msgid "Main menu:"
+msgstr "Päävalikko:"
+
+msgid "Context menu:"
+msgstr "Kontekstivalikko:"
+
+msgid "Floating frame"
+msgstr "Kelluva kehys"
+
+msgid "Tiled frame"
+msgstr "Laatoitettu kehys"
+
+msgid "Tiling"
+msgstr "Laatoitus"
+
+#
+msgid "Workspace"
+msgstr "Työpöytä"
+
+msgid "Screen"
+msgstr "Näyttö"
+
+msgid "Frame"
+msgstr "Kehys"
+
+msgid "Recursive table - unable to deepcopy"
+msgstr "Rekursiivinen taulu - ei voida syväkopioida."
+
+msgid ""
+"Making the following minimal emergency mappings:\n"
+"  F2 -> xterm\n"
+"  F11 -> restart\n"
+"  F12 -> exit\n"
+"  Mod1+C -> close\n"
+"  Mod1+K P/N -> WFrame.switch_next/switch_prev\n"
+msgstr ""
+"Tehdään seuraavat vähimmäiset hätäsidonnat:\n"
+"  F2 -> xterm\n"
+"  F11 -> uudelleenkäynnistys\n"
+"  F12 -> lopetus\n"
+"  Mod1+C -> sulkeminen\n"
+"  Mod1+K P/N -> vaihto kehyksen sisällä\n"
+
+msgid "Unable to append to non-table menu"
+msgstr "Ei voida lisätä valikkoon, koska sitä ei ole määritelty tauluna "
+
+msgid "Cannot save selection."
+msgstr "Ei voida tallettaa valintaa."
+
+msgid "Save look selection in %s?"
+msgstr "Talletaanko ulkonäköasetus tiedostoon %s?"
+
+msgid "Too much result data"
+msgstr "Liian suuri vastaus"
+
+msgid "Could not find client window %s."
+msgstr "Asiakasikkunaa %s ei löytynyt."
+
+msgid "Cannot attach: different root windows."
+msgstr "Ei voida liittää: eri juuri-ikkunat."
+
+msgid "Unknown error"
+msgstr "Tuntematon virhe"
+
+msgid "Go to window:"
+msgstr "Mene ikkunaan:"
+
+msgid "Attach window:"
+msgstr "Liitä ikkuna:"
+
+msgid "Go to or create workspace:"
+msgstr "Mene tai luo työpöytä:"
+
+msgid "Exit Ion/Shutdown session (y/n)?"
+msgstr "Poistu Ionista/Lopeta istunto (kyllä: y, ei: n)?"
+
+msgid "Restart Ion (y/n)?"
+msgstr "Uudelleenkäynnistä Ion (kyllä: y, ei: n)?"
+
+msgid "Frame name:"
+msgstr "Kehyksen nimi:"
+
+msgid "Workspace name:"
+msgstr "Työpöydän nimi:"
+
+msgid "Edit file:"
+msgstr "Muokkaa tiedostoa:"
+
+msgid "View file:"
+msgstr "Näytä tiedosto:"
+
+msgid "Run:"
+msgstr "Suorita:"
+
+msgid "Failed to open ~/.ssh/known_hosts"
+msgstr "Tiedostoa ~/.ssh/known_hosts avaaminen epäonnistui."
+
+msgid "Failed to open ~/.ssh/config"
+msgstr "Tiedoston ~/.ssh/config avaaminen epäonnistui."
+
+msgid "SSH to:"
+msgstr "Avaa SSH-yhteys:"
+
+msgid "Manual page (%s):"
+msgstr "Ohjesivu (%s):"
+
+msgid "Lua code:"
+msgstr "Lua-koodia:"
+
+msgid "Unknown menu %s."
+msgstr "Tuntematon valikko %s."
+
+msgid "Missing submenu "
+msgstr "Puuttuva alivalikko "
+
+msgid "%s:"
+msgstr ""
+
+msgid "No entry '%s'"
+msgstr "Tuntematon valinta '%s'."
+
+msgid ""
+"\n"
+"%sClass: %s\n"
+"%sRole: %s\n"
+"%sInstance: %s\n"
+"%sXID: 0x%x"
+msgstr ""
+"\n"
+"Luokka(class): %s\n"
+"Rooli(role): %s\n"
+"Instanssi(instance): %s\n"
+"XID: 0x%x"
+
+msgid "Toggle scratchpad."
+msgstr "Kytke suttausalue päälle/pois"
+
+msgid "Programs"
+msgstr "Ohjelmat"
+
+msgid "Lock screen"
+msgstr "Lukitse näyttö"
+
+msgid "Help"
+msgstr "Ohjeet"
+
+#
+msgid "Workspaces"
+msgstr "Työpöydät"
+
+msgid "New"
+msgstr "Uusi"
+
+msgid "Styles"
+msgstr "Tyylit"
+
+msgid "Session"
+msgstr "Istunto"
+
+msgid "Close"
+msgstr "Sulje"
+
+msgid "List"
+msgstr "Lista"
+
+#
+msgid ""
+"Switch to n:th object (workspace, full screen client window) within current "
+"screen."
+msgstr ""
+"Siirry n:teen tämänhetkisen näytön lomittamaan olioon (työpöytä, kokoruudun "
+"asiakasikkuna)."
+
+#
+msgid "Switch to next/previous object within current screen."
+msgstr "Siirry seuraavaan/edelliseen tämänhetkisen näytön lomittamaan olioon."
+
+msgid "Go to first region demanding attention or previously active one."
+msgstr ""
+"Mene ensimmäiseen huomiota vaativaan alueeseen, tai edelliseen aktiiviseen."
+
+#
+msgid "Clear all tags."
+msgstr "Poista kaikki merkinnät."
+
+#
+msgid "Go to n:th screen on multihead setup."
+msgstr "Siirry näytölle n usean näytön järjestelmässä."
+
+#
+msgid "Go to next/previous screen on multihead setup."
+msgstr "Mene seuraavalle/edelliselle näytölle usean näytön järjestelmässä."
+
+#
+#
+msgid "Create a new workspace of chosen default type."
+msgstr "Luo uusi työpöytä ennalta asetettua tyyppiä."
+
+msgid "Display the main menu."
+msgstr "Näytä päävalikko."
+
+msgid "Display the window list menu."
+msgstr "Näytä ikkunavalikko."
+
+msgid "Forward-circulate focus."
+msgstr "Kierrätä fokusta eteenpäin."
+
+msgid "Backward-circulate focus."
+msgstr "Kierrätä fokusta taaksepäin"
+
+msgid "Raise focused object, if possible."
+msgstr "Nosta fokusoitu olio, jos mahdollista."
+
+#
+msgid ""
+"Nudge the client window. This might help with some programs' resizing "
+"problems."
+msgstr ""
+"Tönäise aktiivista asiakasikkunaa. Tämä saattaa auttaa joidenkin ohjelmien "
+"koko-ongelmien kanssa."
+
+#
+msgid "Kill client owning the client window."
+msgstr "Tapa aktiivisen asiakasikkunan omistava ohjelma."
+
+#
+msgid ""
+"Send next key press to the client window. Some programs may not allow this "
+"by default."
+msgstr ""
+"Lähetä seuraava näppäinpainallus aktiiviselle asiakasikkunalle. Kaikki "
+"ohjelmat eivät välttämättä salli tätä oletuksena."
+
+msgid "Toggle client window group full-screen mode"
+msgstr "Muuta asiakasikkunaryhmän kokoruudun tilaa"
+
+msgid "Close current object."
+msgstr "Sulje aktiivinen olio."
+
+msgid "Query for manual page to be displayed."
+msgstr "Kysy ohjesivua näytettäväksi."
+
+msgid "Show the Ion manual page."
+msgstr "Näytä Ionin ohjesivu."
+
+#
+msgid "Run a terminal emulator."
+msgstr "Käynnistä pääte-emulaattori."
+
+msgid "Query for command line to execute."
+msgstr "Kysy komentoriviä suoritettavaksi."
+
+msgid "Query for Lua code to execute."
+msgstr "Kysy Lua-koodia ajettavaksi."
+
+msgid "Query for host to connect to with SSH."
+msgstr "Ota SSH-yhteys kyseltävään koneeseen."
+
+msgid "Query for file to edit."
+msgstr "Kysy tiedostoa muokattavaksi."
+
+msgid "Query for file to view."
+msgstr "Kysy tiedostoa näytettäväksi."
+
+msgid "Query for workspace to go to or create a new one."
+msgstr "Kysy työpöytää jolle siirtyä, tai luo uusi."
+
+#
+msgid "Query for a client window to go to."
+msgstr "Kysy asiakasikkunaa, johon siirtyä."
+
+#
+msgid "Query for a client window to attach."
+msgstr "Kysy liitettävää asiakasikkunaa."
+
+msgid "Display context menu."
+msgstr "Näytä kontekstivalikko."
+
+msgid "Maximize the frame horizontally/vertically."
+msgstr "Maksimoi kehys vaaka/pystysuunnassa."
+
+msgid "Begin move/resize mode."
+msgstr "Aloita siirto ja koonmuutostila."
+
+msgid "Switch the frame to display the object indicated by the tab."
+msgstr "Vaihda kehys näyttämään välilehden ilmoittama olio."
+
+msgid "Resize the frame."
+msgstr "Muuta kehyksen kokoa."
+
+msgid "Move the frame."
+msgstr "Siirrä kehystä."
+
+msgid "Move objects between frames by dragging and dropping the tab."
+msgstr "Siirrä olioita kehysten välillä raahaamalla ja pudottamalla välilehti."
+
+msgid "Tag current object within the frame."
+msgstr "Merkitse kehyksen tällä hetkellä näyttämä olio."
+
+msgid "Switch to n:th object within the frame."
+msgstr "Siirry n:teen kehyksen jakavaan olioon."
+
+msgid "Switch to next/previous object within the frame."
+msgstr "Siirry seuraavaan/edelliseen kehyksen jakavaan olioon."
+
+msgid "Move current object within the frame left/right."
+msgstr ""
+"Siirrä kehyksessä tällä hetkellä näytettävää oliota vasemmalle/oikealle."
+
+msgid "Attach tagged objects to this frame."
+msgstr "Liitä merkityt oliot tähän kehykseen."
+
+msgid "Toggle shade mode"
+msgstr "Kytke litistys päälle/pois."
+
+#
+msgid "Raise the frame."
+msgstr "Nosta kehys."
+
+msgid "Lower the frame."
+msgstr "Alenna kehystä."
+
+msgid "Cancel the resize mode."
+msgstr "Peruuta tilasta."
+
+msgid "End the resize mode."
+msgstr "Siirry tilasta pois."
+
+#
+msgid "Grow in specified direction."
+msgstr "Kasvata vastaavaan suuntaan."
+
+msgid "Shrink in specified direction."
+msgstr "Kutista vastaavaan suuntaan."
+
+#
+msgid "Move in specified direction."
+msgstr "Liiku vastaavaan suuntaan."
+
+msgid "About Ion"
+msgstr "Tietoja Ionista"
+
+msgid "XTerm"
+msgstr ""
+
+msgid "W3M"
+msgstr ""
+
+msgid "Rxvt"
+msgstr ""
+
+msgid "Opera"
+msgstr ""
+
+msgid "Links"
+msgstr ""
+
+msgid "Konqueror"
+msgstr ""
+
+msgid "Dillo"
+msgstr ""
+
+#
+msgid "Run..."
+msgstr "Suorita..."
+
+msgid "Save"
+msgstr "Talleta"
+
+msgid "Restart"
+msgstr "Uudelleenkäynnistä"
+
+msgid "Restart TWM"
+msgstr "Käynnistä TWM"
+
+msgid "Exit"
+msgstr "Poistu"
+
+msgid "Kill"
+msgstr "Tapa"
+
+msgid "Toggle tag"
+msgstr "Muuta lippua"
+
+msgid "Attach tagged"
+msgstr "Liitä merkityt"
+
+msgid "Clear tags"
+msgstr "Poista merkinnät"
+
+msgid "Window info"
+msgstr "Ikkunan tiedot"
+
+#
+msgid "New workspace"
+msgstr "Uusi työpöytä"
+
+msgid "New empty workspace"
+msgstr "Uusi tyhjä työpöytä"
+
+#
+msgid "Close workspace"
+msgstr "Sulje työpöytä"
+
+msgid "Move one character forward/backward."
+msgstr "Siirry yksi merkki eteen/taakse."
+
+msgid "Go to end/beginning."
+msgstr "Mene rivin alkuun/loppuun."
+
+msgid "Skip one word forward/backward."
+msgstr "Ohita yksi sana eteen/taaksepäin"
+
+msgid "Delete next character."
+msgstr "Poista seuraava merkki."
+
+msgid "Delete previous character."
+msgstr "Tuhoa edellinen merkki."
+
+msgid "Delete one word forward/backward."
+msgstr "Tuhoa yksi sana eteen/taakse."
+
+msgid "Delete to end of line."
+msgstr "Tuhoa merkit rivin loppuun saakka."
+
+msgid "Delete the whole line."
+msgstr "Tuhoa koko rivi."
+
+msgid "Transpose characters."
+msgstr "Transponoi merkit."
+
+#
+msgid "Select next/previous (matching) history entry."
+msgstr "Näytä seuraava/edellinen vastaus muistista."
+
+msgid "Paste from the clipboard."
+msgstr "Liimaa teksti leikelaudalta."
+
+#
+msgid "Set mark/begin selection."
+msgstr "Aloita tekstin valinta."
+
+#
+msgid "Cut selection."
+msgstr "Leikkaa valittu teksti."
+
+#
+msgid "Copy selection."
+msgstr "Kopioi valittu teksti."
+
+#
+msgid "Clear mark/cancel selection."
+msgstr "Lopeta tekstin valinta."
+
+msgid "Try to complete the entered text or cycle through completions."
+msgstr "Yritä täydentää syötetty teksti tai selaa täydennyksiä."
+
+msgid "Complete from history"
+msgstr "Täydennä historiasta"
+
+msgid "Close the query and execute bound action."
+msgstr "Sulje kysely ja suorita sidottu toiminta."
+
+msgid "Close the query/message box, not executing bound actions."
+msgstr "Sulje kysely/viesti suorittamatta sidottuja toimintoja."
+
+msgid "Scroll the message or completions up/down."
+msgstr "Vieritä viestiä tai täydennyksiä ylös/alas."
+
+msgid "press"
+msgstr "painallus"
+
+msgid "click"
+msgstr "napsautus"
+
+msgid "drag"
+msgstr "raahaus"
+
+msgid "double click"
+msgstr "kaksoisnapsautus"
+
+msgid "%s %s"
+msgstr ""
+
+msgid "%s %s at %s"
+msgstr "%s %s osassa %s"
+
+#~ msgid "Could not find a complete workspace class. Please load some modules."
+#~ msgstr ""
+#~ "Yhtäkään täydellistä työpöytäluokkaa ei löydy. Ole hyvä ja lataa joitakin "
+#~ "moduuleita."
+
+#
+#
+#~ msgid "Failed to rescue some client windows."
+#~ msgstr "Joidenkin asiakasikkunoiden pelastaminen epäonnistui."
+
+#~ msgid "Same manager."
+#~ msgstr "Sama manageri."
+
+#
+#
+#~ msgid "Invalid split type parameter."
+#~ msgstr "Tuntematon jaon tapa."
+
+#
+#~ msgid "Failure to create a new frame."
+#~ msgstr "Uuden kehyksen luonti epäonnistui."
+
+#
+#~ msgid "Region not managed by the workspace."
+#~ msgstr "Alue ei ole työpöydän hallinnassa."
+
+#~ msgid "No geometry specified."
+#~ msgstr "Geometria on asettamatta."
+
+#~ msgid "Unable to re-initialise workspace. Destroying."
+#~ msgstr "Työpöydän uudelleenalustus epäonnistui, joten se tuhotaan."
+
+#~ msgid "Refusing to close non-empty workspace."
+#~ msgstr "Työpöydän tuhoamisesta kieltäydytään, koska se ei ole tyhjä."
+
+#~ msgid "Malfunctioning placement hook; condition #%d."
+#~ msgstr "Rikkinäinen koukku; tapaus #%d."
+
+#~ msgid "none"
+#~ msgstr "ei mikään"
+
+#
+#~ msgid "mail"
+#~ msgstr "posti"
+
+#~ msgid ""
+#~ "\n"
+#~ "Transients:\n"
+#~ msgstr ""
+#~ "\n"
+#~ "Väliaikaisikkunat:\n"
+
+#~ msgid "Workspace type (%s):"
+#~ msgstr "Työpöydän tyyppi (%s):"
+
+#~ msgid "Go to previous active object."
+#~ msgstr "Mene edelliseen aktiiviseen olioon."
+
+#
+#
+#~ msgid "Toggle fullscreen mode of current client window."
+#~ msgstr "Kytke aktiivisen asiakasikkunan kokoruudun tila päälle/pois."
+
+#~ msgid "WStatusBar expected."
+#~ msgstr "Odotettiin WStatusBaria."
+
+#~ msgid "Backwards-circulate focus and raise the newly focused frame."
+#~ msgstr "Kierrätä fokusta taaksepäin ja nosta fokusoitu kehys."
+
+#~ msgid "(Un)stick"
+#~ msgstr "Aseta tahmaus"
+
+#
+#~ msgid "Query for a client window to attach to active frame."
+#~ msgstr "Kysy asiakasikkunaa liitettäväksi kehykseen."
+
+#
+#~ msgid "Resize the area."
+#~ msgstr "Muuta kehyksen kokoa."
+
+#~ msgid "Raise/lower active frame."
+#~ msgstr "Nosta/alenna aktiivinen kehys."
+
+#~ msgid "Restart PWM"
+#~ msgstr "Käynnistä PWM"
+
+#~ msgid "Refresh list"
+#~ msgstr "Virkistä lista"
+
+#~ msgid "Unable to create workspace: no screen."
+#~ msgstr "Ei voida luoda työpöytää: ei ruutua."
+
+#~ msgid "load"
+#~ msgstr "kuorma"
+
+#~ msgid "(Un)tag"
+#~ msgstr "Vaihda merkintä"
+
+#~ msgid "Circulate focus and raise the newly focused frame."
+#~ msgstr "Kierrätä fokusta ja nosta uudelleen fokusoitu kehys."
+
+#
+#~ msgid "No function given."
+#~ msgstr "Funktiota ei ole annettu."
+
+#~ msgid "Failed to create a timer for statusbar."
+#~ msgstr "Epäonnistuttiin ajastimen luonnissa tilariville."
+
+#~ msgid "Vertically at root"
+#~ msgstr "Pystysuunnassa juuressa"
+
+#~ msgid "Split"
+#~ msgstr "Halkaise"
+
+#
+#~ msgid "Transpose at root"
+#~ msgstr "Käännä juuri"
+
+#~ msgid "Flip&transpose"
+#~ msgstr "Peilaa&käännä"
+
+#~ msgid "Horizontally at root"
+#~ msgstr "Vaakasuunnassa juuressa"
+
+#~ msgid "Frame may not be destroyed."
+#~ msgstr "Kehystä ei saa tuhota."
+
+#~ msgid "Failed to rescue managed objects."
+#~ msgstr "Hallittujen olioiden pelastaminen epäonnistui."
+
+#
+#~ msgid "Could not find a root window."
+#~ msgstr "Juuri-ikkunaa ei löytynyt."
+
+#~ msgid "Caught fatal signal %d. Dying without deinit."
+#~ msgstr ""
+#~ "Ohjelma lopettaa ilman jälkien putsausta tuhoisan signaalin %d "
+#~ "vastaanottoon."
+
+#~ msgid "Caught signal %d. Dying."
+#~ msgstr "Ohjelma lopettaa signaalin %d vastaanottoon."
+
+#
+#~ msgid "Object destroyed while deferred actions are still pending."
+#~ msgstr "Viivästettyä toimintoja jonossa vielä oliota tuhottaessa."
+
+#~ msgid "Unable to rescue \"%s\"."
+#~ msgstr "Alueen \"%s\" pelastaminen epäonnistui."
+
+#~ msgid "Frame is not empty."
+#~ msgstr "Kehys ei ole tyhjä."
+
+#
+#~ msgid "Frame not managed by the workspace."
+#~ msgstr "Kehys ei ole tämän työpöydän hallitsema."
+
+#~ msgid "Vertically/root"
+#~ msgstr "Pystysuunnassa juuressa"
+
+#
+#
+#~ msgid "Horizontally/root"
+#~ msgstr "Vaakasuunnassa juuressa"
+
+#~ msgid "Flip parent"
+#~ msgstr "Peilaa vanhempi"
diff --git a/po/ru.po b/po/ru.po
new file mode 100644 (file)
index 0000000..01d0a08
--- /dev/null
+++ b/po/ru.po
@@ -0,0 +1,1637 @@
+# translation of ru.po to RUSSIAN
+# #
+# This file is distributed under the same license as the Ion3 package.
+# Vassily Leushin <basileus@kirov.lug.ru>, 2006.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ru\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2004-10-11 18:33+0300\n"
+"PO-Revision-Date: 2006-02-01 19:55+0300\n"
+"Last-Translator: Vassily Leushin <basileus@kirov.lug.ru>\n"
+"Language-Team: RUSSIAN <ru@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=KOI8-R\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Russian\n"
+"X-Poedit-Country: RUSSIAN FEDERATION\n"
+"X-Generator: KBabel 1.10.1\n"
+"Plural-Forms:  nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: ../ioncore/conf-bindings.c:96
+msgid "Insane key combination."
+msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ËÏÍÂÉÎÁÃÉÑ ËÌÁ×ÉÛ."
+
+#: ../ioncore/conf-bindings.c:100
+msgid "Could not convert keysym to keycode."
+msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ keysym × keycode"
+
+#: ../ioncore/conf-bindings.c:111
+#, c-format
+msgid "Unknown button \"%s\"."
+msgstr "îÅÉÚ×ÅÓÔÎÁÑ ËÎÏÐËÁ \"%s\" "
+
+#: ../ioncore/conf-bindings.c:116
+msgid "Insane button combination."
+msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ËÏÍÂÉÎÁÃÉÑ ËÎÏÐÏË"
+
+#: ../ioncore/conf-bindings.c:123
+#: ../ioncore/conf-bindings.c:130
+msgid "Insane modifier combination."
+msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ËÏÍÂÉÎÁÃÉÑ ÍÏÄÉÆÉËÁÔÏÒÏ×"
+
+#: ../ioncore/conf-bindings.c:168
+#, c-format
+msgid "Can not wait on modifiers when no modifiers set in \"%s\"."
+msgstr "ïÖÉÄÁÎÉÅ ÍÏÄÉÆÉËÁÔÏÒÏ× ÎÅ×ÏÚÍÏÖÎÏ, ÅÓÌÉ ÏÎÉ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÙ × \"%s\" "
+
+#: ../ioncore/conf-bindings.c:186
+#, c-format
+msgid "Unable to add binding %s."
+msgstr "îÅ×ÏÚÍÏÖÎÏ ÄÏÂÁ×ÉÔØ ÐÒÉ×ÑÚËÕ %s."
+
+#: ../ioncore/conf-bindings.c:191
+#, c-format
+msgid "Unable to remove binding %s."
+msgstr "îÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ ÐÒÉ×ÑÚËÕ %s."
+
+#: ../ioncore/conf-bindings.c:230
+#, c-format
+msgid "Unable to add submap for binding %s."
+msgstr ""
+
+#
+#: ../ioncore/conf-bindings.c:260
+msgid "Binding type not set."
+msgstr "ôÉРÐÒÉ×ÑÚËÉ ÎÅ ÕÓÔÁÎÏ×ÌÅÎ"
+
+#: ../ioncore/conf-bindings.c:270
+#, c-format
+msgid "Unknown binding type \"%s\"."
+msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÔÉРÐÒÉ×ÑÚËÉ \"%s\"."
+
+#: ../ioncore/conf-bindings.c:291
+#, c-format
+msgid "Unknown area \"%s\" for binding %s."
+msgstr "îÅÉÚ×ÅÓÔÎÁÑ ÏÂÌÁÓÔØ \"%s\" ÄÌÑ ÐÒÉ×ÑÚËÉ %s."
+
+#: ../ioncore/conf-bindings.c:332
+#, c-format
+msgid "Unable to get bindmap entry %d."
+msgstr ""
+
+#: ../ioncore/conf-bindings.c:374
+msgid "Unable to convert keysym to string."
+msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ keysym  × ÓÔÒÏËÕ."
+
+#: ../ioncore/conf-bindings.c:388
+msgid "Unable to convert button to string."
+msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ ËÎÏÐËÕ × ÓÔÒÏËÕ."
+
+#
+#: ../ioncore/event.c:78
+msgid "Time request from X server failed."
+msgstr "îÅÕÄÁÞÎÙÊ ÚÁÐÒÏÓ ×ÒÅÍÅÎÉ ÏÔ X ÓÅÒ×ÅÒÁ"
+
+#
+#: ../ioncore/exec.c:101
+msgid "Could not find a root window."
+msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ËÏÒÎÅ×ÏÅ ÏËÎÏ."
+
+#: ../ioncore/exec.c:134
+msgid "reading a pipe"
+msgstr "ÞÔÅÎÉÅ ÉÚ ËÁÎÁÌÁ"
+
+#
+#: ../ioncore/exec.c:296
+msgid "Not saving state: running under session manager."
+msgstr "óÏÓÔÏÑÎÉÅ ÎÅ ÓÏÈÒÁÎÅÎÏ: ÒÁÂÏÔÁ ÐÏÄ ÍÅÎÅÄÖÅÒÏÍ ÓÅÓÓÉÊ."
+
+#: ../ioncore/strings.c:107
+#: ../ioncore/strings.c:143
+msgid "Invalid multibyte string."
+msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ÍÎÏÇÏÂÁÊÔÎÁÑ ÓÔÒÏËÁ"
+
+#: ../ioncore/strings.c:234
+#, c-format
+msgid "Error compiling regular expression: %s"
+msgstr "ïÛÉÂËÁ ËÏÍÐÉÌÑÃÉÉ ÒÅÇ. ×ÙÒÁÖÅÎÉÑ: %s"
+
+#: ../ioncore/modules.c:158
+msgid "Invalid module name."
+msgstr "îÅÐÒÁ×ÉÌØÎÏÅ ÉÍÑ ÍÏÄÕÌÑ."
+
+#
+#: ../ioncore/modules.c:170
+msgid "The module is already loaded."
+msgstr "äÁÎÎÙÊ ÍÏÄÕÌØ ÕÖÅ ÚÁÇÒÕÖÅÎ."
+
+#: ../ioncore/modules.c:185
+msgid "Module version information not found or version mismatch. Refusing to use."
+msgstr ""
+"éÎÆÏÒÍÁÃÉÑ Ï ×ÅÒÓÉÉ ÍÏÄÕÌÑ ÎÅ ÎÁÊÄÅÎÁ, ÉÌÉ ÎÅÓÏÏÔ×ÅÔÓÔ×ÉÅ ×ÅÒÓÉÊ "
+"ïÔËÁÚ × ÉÓÐÏÌØÚÏ×ÁÎÉÉ."
+
+#: ../ioncore/modules.c:196
+#, c-format
+msgid "Unable to initialise module %s."
+msgstr "îÅ ÍÏÇÕ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÍÏÄÕÌØ  %s ."
+
+#: ../ioncore/modules.c:220
+#: ../../libextl/readconfig.c:368
+#, c-format
+msgid "Unable to find '%s' on search path."
+msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ '%s' × ÐÕÔÉ ÐÏÉÓËÁ."
+
+#: ../ioncore/modules.c:291
+msgid "Unknown module."
+msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÍÏÄÕÌØ."
+
+#: ../ioncore/modules.c:299
+msgid "Unable to initialise module."
+msgstr "îÅ ÍÏÇÕ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÍÏÄÕÌØ."
+
+#: ../ioncore/modules.c:344
+msgid "No module to load given."
+msgstr "îÅ ÕËÁÚÁΠÍÏÄÕÌØ ÄÌÑ ÚÁÇÒÕÚËÉ."
+
+#: ../ioncore/screen.c:301
+msgid "act: "
+msgstr "act: "
+
+#: ../ioncore/screen.c:490
+msgid "Invalid offset."
+msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÏÔÓÔÕÐ."
+
+#: ../ioncore/screen.c:535
+msgid "Could not find a complete workspace class. Please load some modules."
+msgstr ""
+"îÅ ÍÏÇÕ ÎÁÊÔÉ ËÌÁÓÓ ÐÏÌÎÏÇÏ ÒÁÂÏÞÅÇÏ ÓÔÏÌÁ. ðÏÖÁÌÕÊÓÔÁ "
+"ÚÁÇÒÕÚÉÔÅ ËÁËÉÅ-ÎÉÂÕÄØ ÍÏÄÕÌÉ."
+
+#: ../ioncore/screen.c:545
+#, c-format
+msgid "Unable to create a workspace on screen %d."
+msgstr "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÒÁÂ. ÐÒÏÓÔÒÁÎÓÔ×Ï ÎÁ ÜËÒÁÎÅ  %d ."
+
+#: ../ioncore/signal.c:291
+#, c-format
+msgid "Caught fatal signal %d. Dying without deinit."
+msgstr "ðÒÉÎÑÔ ÆÁÔÁÌØÎÙÊ ÓÉÇÎÁÌ %d. õÍÉÒÁÀ ÂÅÚ deinit'Á."
+
+#: ../ioncore/signal.c:300
+#, c-format
+msgid "Caught signal %d. Dying."
+msgstr "ðÏÌÕÞÅΠÓÉÇÎÁÌ %d. õÍÉÒÁÀ."
+
+#: ../ioncore/sizehint.c:150
+msgid "Invalid client-supplied width/height increment."
+msgstr "ðÏÌÕÞÅΠÎÅÐÒÁ×ÉÌØÎÙÊ ÉÎËÒÅÍÅÎÔ ÛÉÒÉÎÙ/×ÙÓÏÔÙ ÏÔ ËÌÉÅÎÔÁ."
+
+#: ../ioncore/sizehint.c:158
+msgid "Invalid client-supplied aspect-ratio."
+msgstr "ðÏÌÕÞÅÎÙ ÎÅÐÒÁ×ÉÌØÎÙÅ ÐÒÏÐÏÒÃÉÉ ÏÔ ËÌÉÅÎÔÁ."
+
+#: ../ioncore/ioncore.c:66
+msgid ""
+"This program is free software; you can redistribute it and/or\n"
+"modify it under the terms of the GNU Lesser General Public\n"
+"License as published by the Free Software Foundation; either\n"
+"version 2.1 of the License, or (at your option) any later version.\n"
+"\n"
+"This program is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
+"Lesser General Public License for more details.\n"
+msgstr ""
+
+#: ../ioncore/ioncore.c:385
+#, c-format
+msgid "Could not connect to X display '%s'"
+msgstr "îÅ ÍÏÇÕ ÓÏÅÄÉÎÉÔØÓÑ Ó X ÄÉÓÐÌÅÅÍ '%s'"
+
+#: ../ioncore/ioncore.c:438
+msgid "Could not find a screen to manage."
+msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ÜËÒÁΠÄÌÑ ÕÐÒÁ×ÌÅÎÉÑ."
+
+#: ../ioncore/xic.c:43
+msgid "Failed to open input method."
+msgstr "íÅÔÏÄ ××ÏÄÁ ÎÅ ÏÔËÒÙÔ."
+
+#: ../ioncore/xic.c:48
+msgid "Input method doesn't support any style."
+msgstr "íÅÔÏÄ ××ÏÄÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÓÔÉÌÉ"
+
+#: ../ioncore/xic.c:63
+msgid "input method doesn't support my preedit type."
+msgstr "íÅÔÏÄ ××ÏÄÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÍÏÊ ÔÉРpreedit'Á. "
+
+#: ../ioncore/xic.c:91
+msgid "Failed to create input context."
+msgstr "ëÏÎÔÅËÓÔ ××ÏÄÁ ÎÅ ÓÏÚÄÁÎ."
+
+#: ../ioncore/clientwin.c:376
+#, c-format
+msgid "The transient_for hint for \"%s\" points to itself."
+msgstr "èÉÎÔ transient_for ÄÌÑ \"%s\" ÕËÁÚÙ×ÁÅÔ ÎÁ ÓÁÍÏÇÏ ÓÅÂÑ."
+
+#: ../ioncore/clientwin.c:380
+#, c-format
+msgid "Client window \"%s\" has broken transient_for hint. (\"Extended WM hints\" multi-parent brain damage?)"
+msgstr ""
+
+#: ../ioncore/clientwin.c:385
+#, c-format
+msgid "The transient_for window for \"%s\" is not on the same screen."
+msgstr "ïËÎÏ transient_for ÄÌÑ \"%s\" ÎÅ ÎÁ ÔÏÍ ÖÅ ÜËÒÁÎÅ."
+
+#: ../ioncore/clientwin.c:405
+#: ../ioncore/clientwin.c:492
+#: ../ioncore/clientwin.c:1554
+#, c-format
+msgid "Window %#x disappeared."
+msgstr "ïËÎÏ %#x ÉÓÞÅÚÌÏ."
+
+#: ../ioncore/clientwin.c:512
+msgid "Unable to find a matching root window!"
+msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÅÅ ËÏÒÎÅ×ÏÅ ÏËÎÏ!"
+
+#: ../ioncore/clientwin.c:547
+#, c-format
+msgid "Unable to manage client window %#x."
+msgstr "îÅ ÍÏÇÕ ÕÐÒÁ×ÌÑÔØ ËÌÉÅÎÔÓËÉÍ ÏËÎÏÍ %#x."
+
+#: ../ioncore/clientwin.c:597
+msgid "Changes is WM_TRANSIENT_FOR property are unsupported."
+msgstr ""
+
+#
+#: ../ioncore/clientwin.c:874
+msgid "Client does not support the WM_DELETE protocol."
+msgstr "ëÌÉÅÎÔ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÒÏÔÏËÏÌ WM_DELETE. "
+
+#: ../ioncore/clientwin.c:1103
+#: ../mod_ionws/ionws.c:76
+#: ../mod_floatws/floatws.c:103
+#, c-format
+msgid "Error reparenting %s."
+msgstr "ïÛÉÂËÁ ÐÅÒÅÎÁÚÎÁÞÅÎÉÑ ÒÏÄÉÔÅÌÑ %s."
+
+#: ../ioncore/clientwin.c:1560
+msgid "Saved client window does not want to be managed."
+msgstr "óÏÈÒÁÎÅÎÎÏÅ ÏËÎÏ ËÌÉÅÎÔÁ ÎÅ ÈÏÞÅÔ ÂÙÔØ ÐÏÄ ÕÐÒÁ×ÌÅÎÉÅÍ."
+
+#
+#: ../ioncore/colormap.c:96
+msgid "Unable to store colourmap watch info."
+msgstr ""
+
+#: ../ioncore/region.c:36
+msgid "Creating region with negative width or height!"
+msgstr "óÏÚÄÁÎÉÅ ÒÅÇÉÏÎÁ Ó ÏÔÒÉÃÁÔÅÌØÎÏÊ ×ÙÓÏÔÏÊ ÉÌÉ ÛÉÒÉÎÏÊ!"
+
+#: ../ioncore/region.c:84
+#, c-format
+msgid "Destroying object \"%s\" with client windows as children."
+msgstr "õÄÁÌÅÎÉÅ ÏÂßÅËÔÁ \"%s\" Ó ÏËÎÁÍÉ ËÌÉÅÎÔÁ ËÁË ÐÏÔÏÍËÁÍÉ."
+
+#: ../ioncore/attach.c:80
+msgid "Unable to reparent."
+msgstr "îÅ×ÏÚÍÏÖÎÏ ÐÅÒÅÎÁÚÎÁÞÉÔØ ÒÏÄÉÔÅÌÑ."
+
+#: ../ioncore/attach.c:112
+#: ../ioncore/frame-pointer.c:295
+#, c-format
+msgid "Attempt to make region %s manage its ancestor %s."
+msgstr "ðÏÐÙÔËÁ ÕÐÒÁ×ÌÅÎÉÑ ÒÅÇÉÏÎÏÍ %s ÅÇÏ ÐÒÅÄËÏÍ %s."
+
+#
+#: ../ioncore/defer.c:93
+msgid "Object destroyed while deferred actions are still pending."
+msgstr "ïÂßÅËÔ ÕÄÁÌÅΠÐÒÉ ÏÔÌÏÖÅÎÎÏÍ ÄÅÊÓÔ×ÉÉ."
+
+#: ../ioncore/manage.c:90
+msgid "Unable to find a screen for a new client window."
+msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ÜËÒÁΠÄÌÑ ÎÏ×ÏÇÏ ËÌÉÅÎÔÓËÏÇÏ ÏËÎÁ."
+
+#: ../ioncore/manage.c:187
+#, c-format
+msgid "Unable to rescue \"%s\"."
+msgstr "îÅ ÍÏÇÕ ÓÐÁÓÔÉ \"%s\"."
+
+#: ../ioncore/rootwin.c:222
+#, c-format
+msgid "Unable to redirect root window events for screen %d."
+msgstr "îÅ ÍÏÇÕ ÐÅÒÅÎÁÐÒÁ×ÉÔØ ÓÏÂÙÔÉÑ ËÏÒÎÅ×ÏÇÏ ÏËÎÁ ÄÌÑ ÜËÒÁÎÁ %d."
+
+#: ../ioncore/rootwin.c:316
+msgid "Xinerama sanity check failed; overlapping screens detected."
+msgstr "ðÒÏ×ÅÒËÁ Xinerama ÎÅ ÕÄÁÌÁÓØ; ÏÂÎÁÒÕÖÅÎÏ ÎÁÌÏÖÅÎÉÅ ÜËÒÁÎÏ×."
+
+#: ../ioncore/rootwin.c:342
+msgid "Don't know how to get Xinerama information for multiple X root windows."
+msgstr "îÅ ÚÎÁÀ ËÁË ÐÏÌÕÞÉÔØ ÉÎÆÏÒÍÁÃÉÀ ÐÏ ÍÎÏÖÅÓÔ×ÅÎÎÙÍ ËÏÒÎÅ×ÙÍ ÏËÎÁÍ × Xinerama."
+
+#: ../ioncore/rootwin.c:378
+#, c-format
+msgid "Unable to setup Xinerama screen %d."
+msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ÜËÒÁΠXinerama  %d."
+
+#: ../ioncore/rootwin.c:389
+#, c-format
+msgid "Unable to setup X screen %d."
+msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ÜËÒÁΠX %d."
+
+#: ../ioncore/names.c:90
+#, c-format
+msgid "Corrupt instance number %s."
+msgstr "îÅ×ÅÒÎÙÊ ÎÏÍÅÒ ÜËÚÅÍÐÌÑÒÁ %s."
+
+#: ../ioncore/saveload.c:55
+#, c-format
+msgid "Unknown class \"%s\", cannot create region."
+msgstr "îÅÉÚ×ÅÓÔÎÙÊ ËÌÁÓÓ \"%s\", ÎÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÒÅÇÉÏÎ."
+
+#: ../ioncore/saveload.c:124
+#, c-format
+msgid ""
+"There were errors loading layout. Backing up current layout savefile as\n"
+"%s.\n"
+"If you are _not_ running under a session manager and wish to restore your\n"
+"old layout, copy this backup file over the layout savefile found in the\n"
+"same directory while Ion is not running and after having fixed your other\n"
+"configuration files that are causing this problem. (Maybe a missing\n"
+"module?)\n"
+msgstr ""
+
+#: ../ioncore/saveload.c:167
+msgid "Unable to get file for layout backup."
+msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÒÅÚÅÒ×ÎÙÊ ÆÁÊÌ ÒÁÓÐÏÌÏÖÅÎÉÑ."
+
+#: ../ioncore/saveload.c:171
+#, c-format
+msgid "Backup file %s already exists."
+msgstr "ÒÅÚÅÒ×ÎÙÊ ÆÁÊÌ %s ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ."
+
+#: ../ioncore/saveload.c:177
+msgid "Failed backup."
+msgstr "òÅÚÅÒ×ÎÁÑ ËÏÐÉÑ ÎÅ ÓÏÚÄÁÎÁ."
+
+#
+#: ../ioncore/saveload.c:182
+msgid "Unable to initialise layout on any screen."
+msgstr "îÅ ÍÏÇÕ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÒÁÓÐÏÌÏÖÅÎÉÅ ÎÉ ÎÁ ÏÄÎÏÍ ÜËÒÁÎÅ."
+
+#: ../ioncore/saveload.c:202
+#, c-format
+msgid "Unable to get configuration for screen %d."
+msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ËÏÎÆÉÇÕÒÁÃÉÀ ÜËÒÁÎÁ %d."
+
+#: ../ioncore/saveload.c:215
+msgid "Unable to save layout."
+msgstr "îÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÒÁÓÐÏÌÏÖÅÎÉÅ."
+
+#: ../ioncore/frame.c:122
+msgid "Frame is not empty."
+msgstr "æÒÅÊÍ ÎÅ ÐÕÓÔ."
+
+#: ../ioncore/conf.c:134
+msgid "User directory can not be set."
+msgstr "äÉÒÅËÔÏÒÉÑ ÐÏÌØÚÏ×ÁÔÅÌÑ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ."
+
+#: ../ioncore/conf.c:197
+msgid "Some bindmaps were empty, loading ioncore-efbb."
+msgstr ""
+
+#: ../ioncore/fullscreen.c:100
+msgid "Failed to enter full screen mode."
+msgstr "ïÛÉÂËÁ ÐÏÌÎÏÜËÒÁÎÎÏÇÏ ÒÅÖÉÍÁ."
+
+#: ../ioncore/fullscreen.c:158
+msgid "WClientWin failed to return from full screen mode; remaining manager or parent from previous location refused to manage us."
+msgstr ""
+
+#
+#: ../ioncore/mplex.c:1247
+msgid "Invalid position setting."
+msgstr "îÅÐÒÁ×ÉÌØÎÙÅ ÕÓÔÁÎÏ×ËÉ ÍÅÓÔÏÐÏÌÏÖÅÎÉÑ"
+
+#
+#: ../ioncore/mplex.c:1280
+msgid "Invalid action setting."
+msgstr "îÅÐÒÁ×ÉÌØÎÙÅ ÕÓÔÁÎÏ×ËÉ ÄÅÊÓÔ×ÉÑ."
+
+#: ../ioncore/gr.c:119
+#, c-format
+msgid "Drawing engine %s is not registered!"
+msgstr "ä×ÉÖÏË ÏÔÒÉÓÏ×ËÉ %s ÎÅ ÚÁÒÅÇÉÓÔÒÉÒÏ×ÁÎ."
+
+#: ../ioncore/gr.c:138
+#, c-format
+msgid "Unable to find brush for style '%s'."
+msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ËÉÓÔØ ÄÌÑ ÓÔÉÌÑ '%s'."
+
+#: ../ioncore/gr.c:409
+msgid "No drawing engines loaded, trying \"de\"."
+msgstr "îÉ ÏÄÎÏ Ä×ÉÖËÁ ÏÔÒÉÓÏ×ËÉ ÎÅ ÚÁÇÒÕÖÅÎÏ, ÐÒÏÂÕÀ \"ru\"."
+
+#
+#: ../ioncore/hooks.c:219
+msgid "No function given."
+msgstr "îÅÔ ÆÕÎËÃÉÉ."
+
+#: ../mod_ionws/ionws.c:311
+msgid "Unable to create a node for status display."
+msgstr "îÅ  ÍÏÇÕ ÓÏÚÄÁÔØ ÕÚÅÌ ÄÌÑ ÐÏËÁÚÁ ÓÔÁÔÕÓÁ."
+
+#: ../mod_ionws/ionws.c:323
+msgid "Unable to create new split for status display."
+msgstr "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÎÏ×ÏÅ ÒÁÚÄÅÌÅÎÉÅ ÄÌÑ ÐÏËÁÚÁ ÓÔÁÔÕÓÁ."
+
+#
+#: ../mod_ionws/ionws.c:742
+msgid "Frame not managed by the workspace."
+msgstr "æÒÅÊÍ ÎÅ ÕÐÒÁ×ÌÑÅÔÓÑ ÒÁÂÏÞÉÍ ÓÔÏÌÏÍ."
+
+#
+#: ../mod_ionws/ionws.c:747
+msgid "Invalid direction parameter."
+msgstr "îÅ×ÅÒÎÙÊ ÐÁÒÁÍÅÔÒ ÎÁÐÒÁ×ÌÅÎÉÑ."
+
+#
+#: ../mod_ionws/ionws.c:765
+msgid "Unable to split."
+msgstr "îÅ ÍÏÇÕ ÒÁÚÄÅÌÉÔØ."
+
+#: ../mod_ionws/ionws.c:803
+msgid "Nil frame."
+msgstr "îÕÌÅ×ÏÊ ÆÒÅÊÍ"
+
+#: ../mod_ionws/ionws.c:807
+msgid "The frame is not managed by the workspace."
+msgstr "äÁÎÎÙÊ ÆÒÅÊÍ ÎÅ ÕÐÒÁ×ÌÑÅÔÓÑ ÒÁÂÏÞÉÍ ÓÔÏÌÏÍ."
+
+#: ../mod_ionws/ionws.c:812
+msgid "Frame may not be destroyed."
+msgstr "æÒÅÊÍ ÎÅ ÕÄÁÌÑÅÔÓÑ"
+
+#: ../mod_ionws/ionws.c:817
+msgid "Failed to rescue managed objects."
+msgstr ""
+
+#: ../mod_ionws/ionws.c:1028
+msgid "Nil parameter."
+msgstr "îÕÌÅ×ÏÊ ÐÁÒÁÍÅÔÒ."
+
+#: ../mod_ionws/ionws.c:1033
+msgid "Manager doesn't match."
+msgstr "íÅÎÅÄÖÅÒ ÎÅ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ."
+
+#
+#: ../mod_ionws/ionws.c:1055
+msgid "Could not get split tree."
+msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÒÅ×Ï ÒÁÚÄÅÌÅÎÉÊ."
+
+#: ../mod_ionws/ionws.c:1076
+msgid "Workspace already has a status display node."
+msgstr "òÁÂÏÞÉÊ ÓÔÏÌ ÕÖÅ ÉÍÅÅÔ ÕÚÅÌ ÐÏËÁÚÁ ÓÔÁÔÕÓÁ."
+
+#
+#: ../mod_ionws/ionws.c:1126
+msgid "Missing region parameters."
+msgstr "ïÔÓÕÔÓÔ×ÕÀÔ ÐÁÒÁÍÅÔÒÙ ÒÅÇÉÏÎÁ."
+
+#: ../mod_ionws/ionws.c:1162
+#: ../mod_ionws/splitfloat.c:783
+msgid "Invalid direction."
+msgstr "îÅÐÒÁ×ÉÌØÎÏÅ ÎÁÐÒÁ×ÌÅÎÉÅ"
+
+#
+#: ../mod_ionws/ionws.c:1236
+msgid "No split type given."
+msgstr "îÅÔ ÕËÁÚÁΠÔÉРÒÁÚÄÅÌÅÎÉÑ"
+
+#: ../mod_ionws/ionws.c:1249
+msgid "Unknown split type."
+msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÔÉРÒÁÚÄÅÌÅÎÉÑ"
+
+#
+#
+#: ../mod_ionws/ionws.c:1289
+msgid "The workspace is empty."
+msgstr "òÁÂÏÞÉÊ ÓÔÏÌ ÐÕÓÔ."
+
+#: ../mod_ionws/placement.c:97
+#: ../mod_panews/placement.c:299
+#, c-format
+msgid "Ooops... could not find a region to attach client window to on workspace %s."
+msgstr "ïÊ... ÎÅ ÍÏÇÕ ÎÁÊÔÉ ÒÅÇÉÏΠÄÌÑ ÐÒÉÓÏÅÄÉÎÅÎÉÑ ÏËÎÁ ËÌÉÅÎÔÁ ÎÁ ÒÁÂÏÞÅÍ ÓÔÏÌÅ %s."
+
+#: ../mod_ionws/split.c:906
+msgid "REGION_RQGEOM_TRYONLY unsupported for status display."
+msgstr "REGION_RQGEOM_TRYONLY ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔÓÑ ÄÌÑ ÄÉÓÐÌÅÑ ÓÔÁÔÕÓÁ."
+
+#
+#: ../mod_ionws/split.c:979
+msgid "Invalid node."
+msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÕÚÅÌ."
+
+#
+#: ../mod_ionws/split.c:1044
+msgid "Splitting the status display is not allowed."
+msgstr "òÁÚÄÅÌÅÎÉÅ ÄÉÓÐÌÅÑ ÓÔÁÔÕÓÁ ÎÅ ÄÏÐÕÓËÁÅÔÓÑ."
+
+#: ../mod_ionws/split.c:1072
+#: ../mod_ionws/splitfloat.c:896
+msgid "Unable to split: not enough free space."
+msgstr "îÅ ÍÏÇÕ ÒÁÚÄÅÌÉÔØ: ÎÅÄÏÓÔÁÔÏÞÎÏ Ó×ÏÂÏÄÎÏÇÏ ÐÒÏÓÔÒÁÎÓÔ×Á."
+
+#
+#: ../mod_ionws/split.c:1558
+msgid "Unable to move the status display out of way of transpose."
+msgstr ""
+
+#: ../mod_ionws/split.c:1722
+#, c-format
+msgid "Unable to get configuration for %s."
+msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ÎÁÓÔÒÏÊËÉ ÄÌÑ \"%s\"."
+
+#: ../mod_ionws/split-stdisp.c:591
+#: ../mod_ionws/split-stdisp.c:616
+msgid "Status display in bad split configuration."
+msgstr ""
+
+#: ../mod_ionws/split-stdisp.c:657
+msgid "Status display badly located in split tree."
+msgstr "äÉÓÐÌÅÊ ÓÔÁÔÕÓÁ ÐÌÏÈÏ ÒÁÚÍÅÝÅΠנÄÅÒÅ×Å ÒÁÚÄÅÌÅÎÉÊ."
+
+#: ../mod_floatws/floatws.c:351
+#: ../mod_panews/panews.c:259
+msgid "Workspace may not be destroyed."
+msgstr "òÁÂÏÞÉÊ ÓÔÏÌ ÎÅ ÍÏÖÅÔ ÂÙÔØ ÕÄÁÌÅÎ."
+
+#: ../mod_floatws/floatws.c:358
+msgid "Failed to rescue some client windows!"
+msgstr "îÅËÏÔÏÒÙÅ ÏËÎÁ ËÌÉÅÎÔÏ× ÎÅ ÓÏÈÒÁÎÅÎÙ!"
+
+#: ../mod_floatws/floatws.c:370
+#: ../mod_panews/panews.c:254
+msgid "Refusing to close non-empty workspace."
+msgstr "ïÔËÁÚ ×  ÚÁËÒÙÔÉÉ ÎÅ ÐÕÓÔÏÇÏ ÒÁÂ. ÐÒÏÓÔÒÁÎÓÔ×Á."
+
+#
+#: ../mod_floatws/floatws.c:462
+msgid "Failure to create a new frame."
+msgstr "îÏ×ÙÊ ÆÒÅÊÍ ÎÅ ÓÏÚÄÁÎ."
+
+#
+#: ../mod_floatws/floatws.c:1037
+#: ../mod_floatws/floatws.c:1115
+msgid "Region not managed by the workspace."
+msgstr "òÅÇÉÏΠÎÅ ÕÐÒÁ×ÌÑÅÔÓÑ ÒÁÂÏÞÉÍ ÓÔÏÌÏÍ."
+
+#: ../mod_floatws/floatws.c:1236
+msgid "No geometry specified."
+msgstr "çÅÏÍÅÔÒÉÑ ÎÅ ÕËÁÚÁÎÁ."
+
+#: ../mod_floatws/placement.c:125
+#, c-format
+msgid "Unknown placement method \"%s\"."
+msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÍÅÔÏÄ ÒÁÚÍÅÝÅÎÉÑ \"%s\"."
+
+#: ../mod_panews/panews.c:193
+msgid "Unable to re-initialise workspace. Destroying."
+msgstr "îÅ ÍÏÇÕ ÒÅÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÒÁÂÏÞÅÅ ÐÒÏÓÔÒÁÎÓÔ×Ï. õÄÁÌÅÎÉÅ."
+
+#: ../mod_panews/placement.c:104
+#: ../mod_panews/placement.c:108
+#: ../mod_panews/placement.c:175
+#: ../mod_panews/placement.c:180
+#, c-format
+msgid "Malfunctioning placement hook; condition #%d."
+msgstr ""
+
+#: ../mod_query/fwarn.c:34
+msgid "Error:\n"
+msgstr "ïÛÉÂËÁ:\n"
+
+#
+#: ../mod_menu/menu.c:490
+msgid "Empty menu."
+msgstr "ðÕÓÔÏÅ ÍÅÎÀ."
+
+#
+#: ../mod_sm/sm.c:110
+msgid "Failed to set session directory."
+msgstr "äÉÒÅËÔÏÒÉÑ ÓÅÓÓÉÉ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ."
+
+#: ../mod_sm/sm_session.c:85
+msgid "Too many ICE connections."
+msgstr "óÌÉÛËÏÍ ÍÎÏÇÏ ÓÏÅÄÉÎÅÎÉÊ  'ICE'."
+
+#: ../mod_sm/sm_session.c:227
+msgid "Failed to save session state"
+msgstr "óÏÓÔÏÑÎÉÅ ÓÅÓÓÉÉ ÎÅ ÓÏÈÒÁÎÉÌÏÓØ"
+
+#: ../mod_sm/sm_session.c:246
+msgid "Failed to request save-yourself-phase2 from session manager."
+msgstr ""
+
+#
+#: ../mod_sm/sm_session.c:295
+msgid "SESSION_MANAGER environment variable not set."
+msgstr "ÐÅÒÅÍÅÎÎÁÑ ÏËÒÕÖÅÎÉÑ SESSION_MANAGER ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ."
+
+#: ../mod_sm/sm_session.c:300
+msgid "Session Manager: IceAddConnectionWatch failed."
+msgstr ""
+
+#
+#: ../mod_sm/sm_session.c:325
+msgid "Unable to connect to the session manager."
+msgstr "îÅ ÍÏÇÕ ÓÏÅÄÉÎÉÔØÓÑ Ó ÍÅÎÅÄÖÅÒÏÍ ÓÅÓÓÉÊ."
+
+#: ../mod_sp/main.c:137
+#, c-format
+msgid "Unable to create scratchpad for screen %d."
+msgstr ""
+
+#: ../de/init.c:41
+#, c-format
+msgid "Border attribute %s sanity check failed."
+msgstr ""
+
+#: ../de/init.c:64
+#, c-format
+msgid "Unknown border style \"%s\"."
+msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÓÔÉÌØ ÂÏÒÄÀÒÁ \"%s\"."
+
+#: ../de/init.c:96
+#, c-format
+msgid "Unable to allocate colour \"%s\"."
+msgstr "îÅ ÍÏÇÕ ÒÁÚÍÅÓÔÉÔØ Ã×ÅÔ \"%s\"."
+
+#: ../de/init.c:160
+#, c-format
+msgid "Corrupt substyle table %d."
+msgstr "ðÏ×ÒÅÖÄÅÎÎÁÑ ÔÁÂÌÉÃÁ ÐÏÄÓÔÉÌÅÊ %d."
+
+#: ../de/init.c:193
+#, c-format
+msgid "Unknown text alignment \"%s\"."
+msgstr "îÅÉÚ×ÅÓÔÎÏÅ ×ÙÒÁ×ÎÉ×ÁÎÉÅ ÔÅËÓÔÁ \"%s\"."
+
+#: ../de/init.c:263
+#, c-format
+msgid "'based_on' for %s points back to the style itself."
+msgstr "'based_on' ÄÌÑ %s ÕËÁÚÙ×ÁÅÔ ÎÁ ÓÁÍ ÓÔÉÌØ."
+
+#: ../de/init.c:266
+#, c-format
+msgid "Unknown base style \"%s\"."
+msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÂÁÚÏ×ÙÊ ÓÔÉÌØ \"%s\"."
+
+#: ../de/font.c:47
+#, c-format
+msgid "Fontset for font pattern '%s' implements context dependent drawing, which is unsupported. Expect clutter."
+msgstr ""
+
+#: ../de/font.c:58
+#, c-format
+msgid "Could not load font \"%s\", trying \"%s\""
+msgstr "îÅ ÍÏÇÕ ÚÁÇÒÕÚÉÔØ ÛÒÉÆÔ \"%s\", ÐÒÏÂÕÀ \"%s\""
+
+#: ../de/style.c:315
+#, c-format
+msgid "Style %s still in use [%d] but the module is being unloaded!"
+msgstr "óÔÉÌØ %s ×ÓÅ ÅÝÅ ÉÓÐÏÌØÚÕÅÔÓÑ [%d] ÎÏ ÍÏÄÕÌØ ×ÙÇÒÕÖÁÅÔÓÑ!"
+
+#: ../ion/ion.c:41
+#: ../pwm/pwm.c:41
+msgid "X display to use"
+msgstr "éÓÐÏÌØÚÏ×ÁÔØ X ÄÉÓÐÌÅÊ"
+
+#: ../ion/ion.c:44
+#: ../pwm/pwm.c:44
+msgid "Configuration file"
+msgstr "æÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ"
+
+#: ../ion/ion.c:47
+#: ../pwm/pwm.c:47
+msgid "Add directory to search path"
+msgstr "äÏÂÁ×ÉÔØ ÄÉÒÅËÔÏÒÉÀ Ë ÐÕÔÉ ÐÏÉÓËÁ"
+
+#: ../ion/ion.c:50
+#: ../pwm/pwm.c:50
+msgid "Manage default root window/non-Xinerama screen only"
+msgstr ""
+
+#: ../ion/ion.c:54
+msgid "Use Xinerama screen information (default: 1/yes)"
+msgstr ""
+
+#: ../ion/ion.c:57
+#: ../pwm/pwm.c:57
+msgid "Ignored: not compiled with Xinerama support"
+msgstr "éÇÎÏÒÉÒÕÀ: ÓËÏÍÐÉÌÉÒÏ×ÁÎÏ ÂÅÚ ÐÏÄÄÅÒÖËÉ Xinerama"
+
+#: ../ion/ion.c:61
+#: ../pwm/pwm.c:61
+msgid "Name of session (affects savefiles)"
+msgstr ""
+
+#: ../ion/ion.c:64
+#: ../pwm/pwm.c:64
+msgid "Session manager client ID"
+msgstr ""
+
+#: ../ion/ion.c:67
+#: ../pwm/pwm.c:67
+msgid "Do not create startup error log and display it with xmessage."
+msgstr "îÅ ÓÏÚÄÁ×ÁÔØ ÖÕÒÎÁÌ ÏÛÉÂÏË ÚÁÐÕÓËÁ, Á ÐÏËÁÚÙ×ÁÔØ ÅÇÏ Ó ÐÏÍÏÝØÀ xmessage."
+
+#: ../ion/ion.c:71
+#: ../pwm/pwm.c:71
+msgid "Show this help"
+msgstr "ðÏËÁÚÁÔØ ÐÏÍÏÝØ"
+
+#: ../ion/ion.c:74
+#: ../pwm/pwm.c:74
+msgid "Show program version"
+msgstr "ðÏËÁÚÁÔØ ×ÅÒÓÉÀ ÐÒÏÇÒÁÍÍÙ"
+
+#: ../ion/ion.c:77
+#: ../pwm/pwm.c:77
+msgid "Show about text"
+msgstr "ðÏËÁÚÁÔØ 'ï ÐÒÏÇÒÁÍÍÅ...'"
+
+#: ../ion/ion.c:92
+msgid "Could not get user configuration file directory."
+msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÉÒÅËÔÏÒÉÀ Ó ÆÁÊÌÁÍÉ ÐÏÌØÚÏ×ÁÔÅÌØÓËÉÈ ÎÁÓÔÒÏÅË "
+
+#: ../ion/ion.c:106
+#, c-format
+msgid "%s/welcome.txt"
+msgstr "%s/welcome.ru.txt"
+
+#: ../ion/ion.c:139
+#: ../pwm/pwm.c:86
+#, c-format
+msgid ""
+"Usage: %s [options]\n"
+"\n"
+msgstr ""
+
+#: ../ion/ion.c:199
+#: ../pwm/pwm.c:149
+msgid "Invalid parameter to -xinerama."
+msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ ÄÌÑ  '-xinerama'."
+
+#
+#: ../ion/ion.c:218
+#: ../pwm/pwm.c:168
+msgid "Invalid command line."
+msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ËÏÍÁÎÄÎÁÑ ÓÔÒÏËÁ"
+
+#: ../ion/ion.c:239
+msgid "Ion startup error log:\n"
+msgstr "öÕÒÎÁÌ ÏÛÉÂÏË ÚÁÐÕÓËÁ Ion:\n"
+
+#: ../ion/ion.c:249
+#: ../pwm/pwm.c:199
+msgid "Refusing to start due to encountered errors."
+msgstr "÷ ÚÁÐÕÓËÅ ÏÔËÁÚÁÎÏ × ÓÉÌÕ ×ÏÚÎÉËÛÉÈ ÏÛÉÂÏË."
+
+#: ../pwm/pwm.c:54
+msgid "Use Xinerama screen information (default: 0/no)"
+msgstr ""
+
+#: ../pwm/pwm.c:189
+msgid "PWM startup error log:\n"
+msgstr "öÕÒÎÁÌ ÏÛÉÂÏË ÚÁÐÕÓËÁ PWM:\n"
+
+#: ../../libextl/readconfig.c:86
+msgid "$HOME not set"
+msgstr "$HOME ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ"
+
+#: ../../libextl/readconfig.c:113
+msgid "User directory not set. Unable to set session directory."
+msgstr "äÉÒÅËÔÏÒÉÑ ÐÏÌØÚÏ×ÁÔÅÌÑ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ. îÅ ÍÏÇÕ ÕÓÔÁÎÏ×ÉÔØ ÄÉÒÅËÔÏÒÉÀ ÓÅÓÓÉÉ."
+
+#: ../../libextl/readconfig.c:247
+#, c-format
+msgid "Falling back to %s."
+msgstr "ïÔËÁÔ Ë  %s."
+
+#: ../../libextl/readconfig.c:441
+#, c-format
+msgid "Unable to create session directory \"%s\"."
+msgstr "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÄÉÒÅËÔÏÒÉÀ ÓÅÓÓÉÉ \"%s\"."
+
+#: ../../libextl/luaextl.c:113
+msgid "Lua stack full."
+msgstr "óÔÅË Lua ÐÏÌÏÎ."
+
+#
+#: ../../libextl/luaextl.c:139
+msgid "Unknown Lua error."
+msgstr "îÅÉÚ×ÅÓÔÎÁÑ ÏÛÉÂËÁ × Lua."
+
+#: ../../libextl/luaextl.c:456
+msgid "Stack trace:"
+msgstr "ôÒÁÓÓÉÒÏ×ËÁ ÓÔÅËÁ:"
+
+#: ../../libextl/luaextl.c:463
+#, c-format
+msgid ""
+"\n"
+"(Unable to get debug info for level %d)"
+msgstr ""
+"\n"
+"(îÅ×ÏÚÍÏÖÎÏ ÐÏÌÕÞÉÔØ ÏÔÌÁÄÏÞÎÕÀ ÉÎÆÏÒÍÁÃÉÀ ÄÌÑ ÕÒÏ×ÎÑ %d)"
+
+#: ../../libextl/luaextl.c:481
+msgid ""
+"\n"
+"  [Skipping unnamed C functions.]"
+msgstr ""
+
+#: ../../libextl/luaextl.c:532
+msgid "Internal error."
+msgstr "÷ÎÕÔÒÅÎÎÑÑ ÏÛÉÂËÁ."
+
+#: ../../libextl/luaextl.c:551
+msgid "Unable to initialize Lua."
+msgstr "îÅ ÍÏÇÕ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ Lua."
+
+#: ../../libextl/luaextl.c:1277
+msgid "Too many return values. Use a C compiler that has va_copy to support more."
+msgstr "óÌÉÛËÏÍ ÍÎÏÇÏ ×ÏÚ×ÒÁÝÁÅÍÙÈ ÚÎÁÞÅÎÉÊ. éÓÐÏÌØÚÕÊÔÅ ËÏÍÐÉÌÑÔÏÒ C Õ ËÏÔÏÒÏÇÏ ÅÓÔØ va_copy ÄÌÑ ÂÏÌØÛÅÊ ÐÏÄÄÅÒÖËÉ"
+
+#: ../../libextl/luaextl.c:1295
+#, c-format
+msgid "Invalid return value (expected '%c', got lua type \"%s\")."
+msgstr "îÅÄÏÐÕÓÔÉÍÏÅ ×ÏÚ×ÒÁÝÁÅÍÏÅ ÚÎÁÞÅÎÉÅ (ÏÖÉÄÁÌÏÓØ '%c', ÐÏÌÕÞÅΠlua-ÔÉР\"%s\")."
+
+#: ../../libextl/luaextl.c:1330
+#: ../../libextl/luaextl.c:1594
+msgid "Stack full."
+msgstr "óÔÅË ÐÏÌÏÎ."
+
+#: ../../libextl/luaextl.c:1570
+msgid "L1 call handler upvalues corrupt."
+msgstr ""
+
+#: ../../libextl/luaextl.c:1575
+msgid "Called function has been unregistered."
+msgstr ""
+
+#: ../../libextl/luaextl.c:1587
+#, c-format
+msgid "Attempt to call an unsafe function \"%s\" in restricted mode."
+msgstr "ðÏÐÙÔËÁ ×ÙÚÏ×Á ÎÅÂÅÚÏÐÁÓÎÏÊ ÆÕÎËÃÉÉ \"%s\" × ÏÇÒÁÎÉÞÅÎÎÏÍ ÒÅÖÉÍÅ. "
+
+#: ../../libextl/luaextl.c:1603
+#, c-format
+msgid "Argument %d to %s is of invalid type. (Argument template is '%s', got lua type %s)."
+msgstr ""
+
+#: ../../libextl/luaextl.c:1782
+#, c-format
+msgid "Function '%s' has more parameters than the level 1 call handler can handle"
+msgstr ""
+
+#
+#: ../../libextl/luaextl.c:2199
+msgid "Maximal serialisation depth reached."
+msgstr "äÏÓÔÉÇÎÕÔÁ ÍÁËÓÉÍÁÌØÎÁÑ ÇÌÕÂÉÎÁ ÓÅÒÉÁÌÉÚÁÃÉÉ."
+
+#: ../../libextl/luaextl.c:2220
+#, c-format
+msgid "Unable to serialise type %s."
+msgstr "îÅ×ÏÚÍÏÖÎÏ ÓÅÒÉÁÌÉÚÉÒÏ×ÁÔØ ÔÉР%s."
+
+#: ../../libextl/luaextl.c:2251
+msgid "-- This file has been generated by Ion. Do not edit.\n"
+msgstr "-- äÁÎÎÙÊ ÆÁÊÌ ÂÙÌ ÓÏÚÄÁΠéÏÎÏÍ. îÅ ÒÅÄÁËÔÉÒÏ×ÁÔØ.\n"
+
+#: ../../libextl/misc.c:17
+#, c-format
+msgid "Type checking failed in level 2 call handler for parameter %d (got %s, expected %s)."
+msgstr ""
+
+#: ../etc/cfg_floatws.lua:34
+msgid "Lower the frame."
+msgstr "ïÐÕÓÔÉÔØ ÆÒÅÊÍ"
+
+#: ../etc/cfg_bindings.lua:171
+msgid "Display frame context menu."
+msgstr "ðÏËÁÚÁÔØ ËÏÎÔÅËÓÔÎÏÅ ÍÅÎÀ ÆÒÅÊÍÁ."
+
+#
+#: ../etc/cfg_ionws.lua:44
+msgid "Flip at root"
+msgstr "ðÅÒ×ÅÒÎÕÔØ × ËÏÒÎÅ×ÏÍ"
+
+#
+#: ../etc/cfg_bindings.lua:43
+msgid "Go to n:th screen on multihead setup."
+msgstr ""
+
+#: ../ext_statusbar/ext_statusbar.lua:308
+msgid "Failed to create statusbar."
+msgstr "óÔÁÔÕÓÂÁÒ ÎÅ ÓÏÚÄÁÎ"
+
+#: ../pwm/cfg_pwm_menus.lua:13
+#: ../etc/cfg_menus.lua:10
+msgid "Help"
+msgstr "ðÏÍÏÝØ"
+
+#: ../mod_query/mod_query.lua:566
+msgid "SSH to:"
+msgstr "SSH to:"
+
+#: ../etc/cfg_menus.lua:28
+msgid "Restart"
+msgstr "ðÅÒÅÚÁÐÕÓË"
+
+#: ../mod_menu/mod_menu.lua:231
+msgid "Cannot save selection."
+msgstr "îÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ×ÙÄÅÌÅÎÉÅ"
+
+#: ../etc/cfg_bindings.lua:206
+msgid "End the resize mode."
+msgstr "úÁ×ÅÒÛÉÔØ ÒÅÖÉÍ ÒÁÚÍÅÒÁ."
+
+#
+#
+#: ../etc/cfg_ionws.lua:48
+#: ../etc/cfg_ionws.lua:58
+msgid "Vertically"
+msgstr "÷ÅÒÔÉËÁÌØÎÏ"
+
+#: ../ioncore/ioncore-bindings.lua:34
+msgid "Invalid guard %s."
+msgstr ""
+
+#: ../etc/cfg_bindings.lua:109
+msgid "Query for Lua code to execute."
+msgstr "ïÞÅÒÅÄØ ÎÁ ×ÙÐÏÌÎÅÎÉÅ ËÏÄÁ Lua."
+
+#
+#: ../etc/cfg_bindings.lua:47
+msgid "Go to next/previous screen on multihead setup."
+msgstr " "
+
+#: ../mod_query/mod_query.lua:344
+msgid "none"
+msgstr "ÎÉÞÅÇÏ"
+
+#: ../etc/cfg_bindings.lua:219
+msgid "Shrink in specified direction."
+msgstr "õÍÅÎØÛÉÔØ × ÕËÁÚÁÎÎÏÍ ÎÁÐÒÁ×ÌÅÎÉÉ"
+
+#: ../etc/cfg_bindings.lua:160
+msgid "Maximize the frame horizontally/vertically."
+msgstr "òÁÚ×ÅÒÎÕÔØ ÆÒÅÊÍ ÐÏ ×ÅÒÔÉËÁÌÉ/ÇÏÒÉÚÏÎÔÁÌÉ."
+
+#: ../etc/cfg_bindings.lua:156
+msgid "Move current object within the frame left/right."
+msgstr "ðÅÒÅÍÅÓÔÉÔØ ÔÅËÕÝÉÊ ÏÂßÅËÔ × ÐÒÅÄÅÌÁÈ ÆÒÅÊÍÁ ×ÐÒÁ×Ï/×ÌÅ×Ï"
+
+#: ../etc/cfg_ionws.lua:12
+msgid "Go to frame above/below/right/left of current frame."
+msgstr "ðÅÒÅÊÔÉ Ë ÆÒÅÊÍÕ ×ÙÛÅ/ÎÉÖÅ/ÐÒÁ×ÅÅ/ÌÅ×ÅÅ ÔÅËÕÝÅÇÏ ÆÒÅÊÍÁ"
+
+#: ../ext_statusbar/ext_statusbar.lua:289
+msgid "Screen not found."
+msgstr "üËÒÁΠÎÅ ÎÁÊÄÅÎ"
+
+#: ../etc/cfg_bindings.lua:182
+msgid "Resize the frame."
+msgstr "éÚÍÅÎÉÔØ ÒÁÚÍÅÒ ÆÒÅÊÍÁ"
+
+#: ../etc/cfg_bindings.lua:189
+msgid "Move objects between frames by dragging and dropping the tab."
+msgstr "ðÅÒÅÍÅÓÔÉÔØ ÏÂßÅËÔÙ ÍÅÖÄÕ ÆÒÅÊÍÁÍÉ ÐÒÉ ÐÏÍÏÝÉ ÐÅÒÅÔÁÓËÉ×ÁÎÉÑ ÔÁÂÏ×."
+
+#: ../mod_query/mod_query.lua:99
+msgid "Could not find %s"
+msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ %s:"
+
+#: ../ext_statusbar/ext_statusbar.lua:295
+msgid "Screen already has an stdisp. Refusing to create a statusbar."
+msgstr "üËÒÁΠÕÖÅ ÉÍÅÅÔ stdisp. ïÔËÁÚ × ÓÏÚÄÁÎÉÉ ÓÔÁÔÕÓÂÁÒÁ."
+
+#: ../etc/cfg_bindings.lua:112
+msgid "Query for host to connect to with SSH."
+msgstr "úÁÐÒÏÓ ÈÏÓÔÁ ÄÌÑ ÓÏÅÄÉÎÅÎÉÑ ÞÅÒÅÚ SSH."
+
+#
+#: ../etc/cfg_bindings.lua:229
+msgid "Move in specified direction."
+msgstr "ðÅÒÅÍÅÓÔÉÔØ × ÕËÁÚÁÎÎÏÍ ÎÁÐÒÁ×ÌÅÎÉÉ."
+
+#
+#: ../ext_statusbar/ext_statusbar.lua:27
+msgid "mail"
+msgstr "ÐÏÞÔÁ"
+
+#: ../mod_query/mod_query.lua:807
+msgid ""
+"\n"
+"Transients:\n"
+msgstr ""
+
+#: ../etc/cfg_menu.lua:21
+msgid "Select next/previous menu entry."
+msgstr "×ÙÂÒÁÔØ ÓÌÅÄ/ÐÒÅÄ ÐÕÎËÔ ÍÅÎÀ"
+
+#: ../ioncore/ioncore-luaext.lua:30
+msgid "Recursive table - unable to deepcopy"
+msgstr "òÅËÕÒÓÉ×ÎÁÑ ÔÁÂÌÉÃÁ - ÇÌÕÂÏËÏÅ ËÏÐÉÒÏ×ÁÎÉÅ ÎÅ×ÏÚÍÏÖÎÏ"
+
+#
+#: ../etc/cfg_bindings.lua:209
+msgid "Grow in specified direction."
+msgstr "÷ÙÒÁÓÔÉ × ÕËÁÚÁÎÎÏÍ ÎÁÐÒÁ×ÌÅÎÉÉ."
+
+#: ../ioncore/ioncore-bindings.lua:68
+msgid "Invalid command"
+msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ËÏÍÁÎÄÁ"
+
+#: ../mod_query/mod_query.lua:189
+#: ../mod_menu/mod_menu.lua:245
+msgid "Too much result data"
+msgstr "óÌÉÛËÏÍ ÍÎÏÇÏ ËÏÎÅÞÎÙÈ ÄÁÎÎÙÈ "
+
+#: ../mod_query/mod_query.lua:344
+msgid "Workspace type (%s):"
+msgstr "ôÉРÒÁÂÏÞÅÇÏ ÓÔÏÌÁ (%s):"
+
+#: ../etc/cfg_bindings.lua:36
+msgid "Go to previous active object."
+msgstr "ÐÅÒÅÊÔÉ Ë ÓÌÅÄ. ÁËÔÉ×ÎÏÍÕ ÏÂßÅËÔÕ"
+
+#
+#: ../etc/cfg_query.lua:67
+msgid "Clear mark/cancel selection."
+msgstr "ïÞÉÓÔËÁ ÍÅÔËÉ/ÏÔÍÅÎÁ ×ÙÄÅÌÅÎÉÑ"
+
+#: ../mod_query/mod_query.lua:772
+msgid "No entry '%s'"
+msgstr "îÅÔ ÐÕÎËÔÁ '%s'"
+
+#
+#
+#: ../etc/cfg_bindings.lua:90
+msgid "Toggle fullscreen mode of current client window."
+msgstr "ðÅÒÅËÌÀÞÉÔØ ÔÅËÕÝÅÅ ÏËÎÏ × ÐÏÌÎÏÜËÒÁÎÎÙÊ ÒÅÖÉÍ."
+
+#: ../etc/cfg_bindings.lua:103
+msgid "Query for manual page to be displayed."
+msgstr "úÁÐÒÏÓ ÎÁ ÏÔÏÂÒÁÖÅÎÉÅ ÓÔÒÁÎÉÃÙ man."
+
+#: ../etc/cfg_bindings.lua:51
+msgid "Show the Ion manual page."
+msgstr "ðÏËÁÚÁÔØ ÓÔÒÁÎÉÃÕ ÒÕËÏ×ÏÄÓÔ×Á ÐÏ éÏÎ."
+
+#: ../etc/cfg_bindings.lua:60
+msgid "Display the main menu."
+msgstr "ðÏËÁÚÁÔØ ÇÌÁ×ÎÏÅ ÍÅÎÀ"
+
+#
+#: ../etc/cfg_bindings.lua:54
+msgid "Run a terminal emulator."
+msgstr "úÁÐÕÓË ÜÍÕÌÑÔÏÒÁ ÔÅÒÍÉÎÁÌÁ"
+
+#: ../etc/cfg_bindings.lua:121
+msgid "Query for workspace to go to or create a new one."
+msgstr ""
+
+#: ../etc/cfg_menus.lua:38
+msgid "Kill"
+msgstr "õÂÉÔØ"
+
+#: ../pwm/cfg_pwm_menus.lua:25
+#: ../etc/cfg_menus.lua:37
+msgid "Close"
+msgstr "úÁËÒÙÔØ"
+
+#: ../mod_query/mod_query.lua:381
+msgid "Go to or create workspace:"
+msgstr "ðÅÒÅÊÔÉ Ë ÉÌÉ ÓÏÚÄÁÔØ ÒÁÂÏÞÉÊ ÓÔÏÌ:"
+
+#: ../etc/cfg_query.lua:35
+msgid "Delete one word forward/backward."
+msgstr "õÄÁÌÉÔØ ÏÄÎÏ ÓÌÏ×Ï ×ÐÅÒÅÄÉ/ÓÚÁÄÉ."
+
+#: ../etc/cfg_query.lua:17
+msgid "Go to end/beginning."
+msgstr "ðÅÒÅÊÔÉ × ËÏÎÅÃ/ÎÁÞÁÌÏ"
+
+#
+#: ../etc/cfg_ionws.lua:43
+msgid "Transpose"
+msgstr "ðÅÒÅÍÅÓÔÉÔØ"
+
+#: ../etc/cfg_bindings.lua:152
+msgid "Switch to next/previous object within the frame."
+msgstr "ðÅÒÅËÌÀÞÉÔØÓÑ ÎÁ ÓÌÅÄ/ÐÒÅÄ ÏÂßÅËÔ ×ÎÕÔÒÉ ÆÒÅÊÍÁ."
+
+#: ../mod_query/mod_query.lua:402
+msgid "Restart Ion (y/n)?"
+msgstr "ðÅÒÅÚÁÐÕÓÔÉÔØ éÏΠ(y/n)?"
+
+#: ../mod_query/mod_query.lua:724
+msgid "Lua code: "
+msgstr "ëÏÄ Lua:"
+
+#: ../mod_query/mod_query.lua:367
+msgid "Attach window:"
+msgstr "ðÒÉÃÅÐÉÔØ ÏËÎÏ:"
+
+#: ../etc/cfg_bindings.lua:186
+#: ../etc/cfg_floatws.lua:37
+msgid "Move the frame."
+msgstr "ðÅÒÅÍÅÓÔÉÔØ ÆÒÅÊÍ."
+
+#: ../pwm/cfg_pwm_menus.lua:11
+#: ../etc/cfg_menus.lua:8
+msgid "Programs"
+msgstr "ðÒÏÇÒÁÍÍÙ"
+
+#: ../etc/cfg_query.lua:23
+msgid "Skip one word forward/backward."
+msgstr "ðÒÏÐÕÓÔÉÔØ ÏÄÎÏ ÓÌÏ×Ï ×ÐÅÒÅÄ/ÎÁÚÁÄ."
+
+#: ../mod_query/mod_query.lua:738
+msgid "Unknown menu %s."
+msgstr "îÅÉÚ×ÅÓÔÎÏÅ ÍÅÎÀ %s."
+
+#: ../etc/cfg_floatws.lua:12
+msgid "Backwards-circulate focus and raise the newly focused frame."
+msgstr ""
+
+#: ../etc/cfg_menus.lua:44
+msgid "Window info"
+msgstr "éÎÆÏÒÍÁÃÉÑ Ï ÏËÎÅ"
+
+#
+#: ../etc/cfg_query.lua:58
+msgid "Set mark/begin selection."
+msgstr ""
+
+#: ../etc/cfg_menus.lua:30
+msgid "Restart TWM"
+msgstr "ðÅÒÅÚÁÐÕÓË TWM"
+
+#: ../etc/cfg_ionws.lua:9
+msgid "Split current frame vertically."
+msgstr "òÁÚÄÅÌÉÔØ ÔÅËÕÝÉÊ ÆÒÅÊÍ ÐÏ ×ÅÒÔÉËÁÌÉ"
+
+#
+#
+#: ../etc/cfg_bindings.lua:57
+msgid "Create a new workspace of chosen default type."
+msgstr "óÏÚÄÁÔØ ÎÏ×ÙÊ ÒÁÂÏÞÉÊ ÓÔÏÌ Ó ÔÉÐÏÍ ×ÙÂÒÁÎÎÙÍ ÐÏ-ÕÍÏÌÞÁÎÉÀ."
+
+#
+#: ../etc/cfg_floatws.lua:29
+msgid "Raise the frame."
+msgstr "ðÏÄÎÑÔØ ÆÒÅÊÍ"
+
+#: ../etc/cfg_floatws.lua:46
+msgid "(Un)stick"
+msgstr ""
+
+#: ../mod_query/mod_query.lua:287
+#: ../mod_query/mod_query.lua:297
+msgid "Could not find client window %s."
+msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ÏËÎÏ ËÌÉÅÎÔÁ %s."
+
+#: ../etc/cfg_query.lua:82
+msgid "Close the query/message box, not executing bound actions."
+msgstr ""
+
+#
+#: ../etc/cfg_bindings.lua:168
+msgid "Query for a client window to attach to active frame."
+msgstr ""
+
+#: ../mod_query/mod_query.lua:488
+msgid "Run:"
+msgstr "úÁÐÕÓÔÉÔØ:"
+
+#
+#: ../etc/cfg_panews.lua:11
+msgid "Resize the area."
+msgstr "éÚÍÅÎÉÔØ ÒÁÚÍÅÒ ÏÂÌÁÓÔÉ."
+
+#
+#: ../etc/cfg_bindings.lua:95
+msgid "Kill client owning current client window."
+msgstr "õÂÉÔØ ËÌÉÅÎÔÁ ÔÅËÕÝÅÇÏ ÏËÎÁ."
+
+#: ../mod_query/mod_query.lua:595
+msgid "Manual page (%s):"
+msgstr "óÔÒÁÎÉÃÁ ÒÕËÏ×ÏÄÓÔ×Á (%s):"
+
+#
+#: ../etc/cfg_bindings.lua:124
+msgid "Query for a client window to go to."
+msgstr ""
+
+#
+#: ../etc/cfg_ionws.lua:39
+msgid "Destroy frame"
+msgstr "õÄÁÌÉÔØ ÆÒÅÊÍ"
+
+#: ../mod_query/mod_query.lua:339
+msgid "Unknown error"
+msgstr "ÎÅÉÚ×ÅÓÔÎÁÑ ÏÛÉÂËÁ"
+
+#: ../etc/cfg_bindings.lua:175
+#: ../etc/cfg_panews.lua:8
+msgid "Begin move/resize mode."
+msgstr "÷ÈÏÄ × ÒÅÖÉÍ ÐÅÒÅÍÅÝÅÎÉÑ/ÒÁÚÍÅÒÁ"
+
+#: ../etc/cfg_bindings.lua:136
+msgid "Tag current object within the frame."
+msgstr ""
+
+#: ../etc/cfg_bindings.lua:140
+msgid "Switch to n:th object within the frame."
+msgstr ""
+
+#: ../etc/cfg_bindings.lua:203
+msgid "Cancel the resize mode."
+msgstr "ïÔÍÅÎÁ ÒÅÖÉÍÁ ÒÁÚÍÅÒÁ."
+
+#: ../build/mkman.lua:196
+msgid "%s %s"
+msgstr "%s %s"
+
+#: ../etc/cfg_query.lua:27
+msgid "Delete next character."
+msgstr "õÄÁÌÉÔØ ÓÌÅÄÕÀÝÉÊ ÓÉÍ×ÏÌ"
+
+#: ../mod_query/mod_query.lua:356
+msgid "Go to window:"
+msgstr "ðÅÒÅÊÔÉ Ë ÏËÎÕ:"
+
+#: ../build/mkman.lua:182
+msgid "drag"
+msgstr "ÐÅÒÅÔÁÝÉÔØ"
+
+#: ../build/mkman.lua:181
+msgid "click"
+msgstr "ËÌÉËÎÕÔØ"
+
+#: ../build/mkman.lua:180
+msgid "press"
+msgstr "ÎÁÖÁÔØ"
+
+#: ../etc/cfg_menu.lua:27
+msgid "Clear the menu's typeahead find buffer."
+msgstr ""
+
+#: ../etc/cfg_ionws.lua:47
+msgid "Split"
+msgstr "òÁÚÄÅÌÉÔØ"
+
+#
+#: ../etc/cfg_bindings.lua:98
+msgid "Send next key press to current client window. Some programs may not allow this by default."
+msgstr ""
+
+#: ../etc/cfg_menu.lua:11
+msgid "Close the menu."
+msgstr "úÁËÒÙÔØ ÍÅÎÀ"
+
+#: ../etc/cfg_query.lua:87
+msgid "Scroll the message or completions up/down."
+msgstr ""
+
+#: ../etc/cfg_query.lua:74
+msgid "Close the query and execute bound action."
+msgstr ""
+
+#
+#: ../etc/cfg_bindings.lua:31
+msgid "Switch to next/previous object within current screen."
+msgstr "ðÅÒÅËÌÀÞÉÔØÓÑ ÎÁ ÓÌÅÄ./ÐÒÅÄ. ÏÂßÅËÔ ×ÎÕÔÒÉ ÔÅËÕÝÅÇÏ ÜËÒÁÎÁ."
+
+#: ../etc/cfg_query.lua:39
+msgid "Delete to end of line."
+msgstr "õÄÁÌÉÔØ ÄÏ ËÏÎÃÁ ÓÔÒÏËÉ."
+
+#: ../pwm/cfg_pwm_menus.lua:14
+#: ../etc/cfg_menus.lua:11
+msgid "About Ion"
+msgstr "ï Ion"
+
+#: ../mod_query/mod_query.lua:804
+msgid ""
+"Title: %s\n"
+"Class: %s\n"
+"Role: %s\n"
+"Instance: %s\n"
+"XID: 0x%x"
+msgstr ""
+"îÁÚ×ÁÎÉÅ: %s\n"
+"ëÌÁÓÓ(class): %s\n"
+"òÏÌØ(role): %s\n"
+"üËÚÅÍÐÌÑÒ(instance): %s\n"
+"XID: 0x%x"
+
+#
+#: ../etc/cfg_query.lua:61
+msgid "Cut selection."
+msgstr "÷ÙÒÅÚÁÔØ ×ÙÄÅÌÅÎÎÏÅ."
+
+#: ../etc/cfg_query.lua:53
+msgid "Paste from the clipboard."
+msgstr "÷ÓÔÁ×ÉÔØ ÉÚ ËÌÉÐÂÏÒÄÁ."
+
+#
+#: ../etc/cfg_query.lua:45
+msgid "Select next/previous (matching) history entry."
+msgstr "÷ÙÂÒÁÔØ ÓÌÅÄ./ÐÒÅÄ. (ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÉÊ) ÐÕÎËÔ ÉÓÔÏÒÉÉ."
+
+#: ../etc/cfg_menus.lua:43
+msgid "Clear tags"
+msgstr "ïÞÉÓÔÉÔØ ÔÅÇÉ"
+
+#: ../etc/cfg_query.lua:42
+msgid "Delete the whole line."
+msgstr "õÄÁÌÉÔØ ÓÔÒÏËÕ ÃÅÌÉËÏÍ"
+
+#: ../etc/cfg_query.lua:71
+msgid "Try to complete the entered text."
+msgstr "ðÏÐÙÔËÁ ÚÁ×ÅÒÛÉÔØ ××ÅÄÅÎÎÙÊ ÔÅËÓÔ."
+
+#: ../etc/cfg_query.lua:31
+msgid "Delete previous character."
+msgstr "õÄÁÌÉÔØ ÐÒÅÄÙÄÕÝÉÊ ÓÉÍ×ÏÌ."
+
+#: ../mod_query/mod_query.lua:299
+msgid "Cannot attach: different root windows."
+msgstr "îÅ ÍÏÇÕ ÐÒÉÃÅÐÉÔØ: ÒÁÚÎÙÅ ËÏÒÎÅ×ÙÅ ÏËÎÁ."
+
+#: ../build/mkman.lua:183
+msgid "double click"
+msgstr "Ä×ÏÊÎÏÊ ËÌÉË"
+
+#: ../etc/cfg_query.lua:11
+msgid "Move one character forward/backward."
+msgstr "ðÅÒÅÍÅÓÔÉÔØÓÑ ÎÁ ÏÄÉΠÓÉÍ×ÏÌ ×ÐÅÒÅÄ/ÎÁÚÁÄ."
+
+#: ../etc/cfg_menus.lua:29
+msgid "Restart PWM"
+msgstr "ðÅÒÅÚÁÐÕÓË PWM"
+
+#: ../etc/cfg_bindings.lua:115
+msgid "Query for file to edit."
+msgstr "úÁÐÒÏÓ ÎÁ ÒÅÄÁËÔÉÒÏ×ÁÎÉÅ ÆÁÊÌÁ."
+
+#
+#: ../etc/cfg_bindings.lua:39
+msgid "Clear all tags."
+msgstr "ïÞÉÓÔÉÔØ ×ÓÅ ÔÅÇÉ."
+
+#: ../etc/cfg_bindings.lua:178
+msgid "Switch the frame to display the object indicated by the tab."
+msgstr "ðÅÒÅËÌÀÞÉÔØ ÆÒÅÊÍ ÎÁ ÐÏËÁÚ ÏÂßÅËÔÁ ÕËÁÚÁÎÎÏÇÏ ÔÁÂÏÍ."
+
+#
+#
+#: ../etc/cfg_ionws.lua:50
+#: ../etc/cfg_ionws.lua:60
+msgid "Horizontally"
+msgstr "çÏÒÉÚÏÎÔÁÌØÎÏ"
+
+#: ../pwm/cfg_pwm_menus.lua:16
+#: ../pwm/cfg_pwm_menus.lua:24
+msgid "New"
+msgstr "îÏ×ÙÊ"
+
+#: ../mod_query/mod_query.lua:410
+msgid "Frame name:"
+msgstr "éÍÑ ÆÒÅÊÍÁ:"
+
+#
+#: ../etc/cfg_bindings.lua:18
+msgid "Switch to n:th object (workspace, full screen client window) within current screen."
+msgstr "ðÅÒÅËÌÀÞÉÔØÓÑ ÎÁ n-ÎÙÊ ÏÂßÅËÔ (ÒÁÂ.ÓÔÏÌ, ÐÏÌÎÏÜËÒÁÎÎÏÅ ÏËÎÏ) ×ÎÕÔÒÉ ÔÅËÕÝÅÇÏ ÜËÒÁÎÁ."
+
+#: ../etc/cfg_bindings.lua:64
+msgid "Display the window list menu."
+msgstr "ðÏËÁÚÁÔØ ÍÅÎÀ ÓÐÉÓËÁ ÏËÏÎ."
+
+#: ../etc/cfg_floatws.lua:26
+msgid "Toggle shade mode"
+msgstr "ðÅÒÅËÌÀÞÉÔØ ÒÅÖÉÍ ÔÅÎÉ"
+
+#: ../mod_query/mod_query.lua:393
+msgid "Exit Ion/Shutdown session (y/n)?"
+msgstr "÷ÙÈÏÄ ÉÚ éÏÎ/úÁ×ÅÒÛÉÔØ ÓÅÓÓÉÀ (y/n)?"
+
+#: ../etc/cfg_floatws.lua:15
+msgid "Raise/lower active frame."
+msgstr "ðÏÄÎÑÔØ/ÏÐÕÓÔÉÔØ ÁËÔÉ×ÎÙÊ ÆÒÅÊÍ"
+
+#: ../mod_menu/mod_menu.lua:235
+msgid "Save look selection in %s?"
+msgstr "óÏÈÒÁÎÉÔØ ×ÎÅÛÎÉÊ ×ÉÄ × %s?"
+
+#: ../etc/cfg_bindings.lua:82
+msgid "Close current object."
+msgstr "úÁËÒÙÔØ ÔÅËÕÝÉÊ ÏÂßÅËÔ."
+
+#
+#: ../etc/cfg_query.lua:64
+msgid "Copy selection."
+msgstr "ëÏÐÉÒÏ×ÁÔØ ×ÙÄÅÎÅÎÉÅ"
+
+#: ../etc/cfg_bindings.lua:164
+msgid "Attach tagged objects to this frame."
+msgstr "ðÒÉÃÅÐÉÔØ ÏÂßÅËÔ Ó ÔÅÇÏÍ Ë ÜÔÏÍÕ ÆÒÅÊÍÕ."
+
+#: ../etc/cfg_ionws.lua:57
+msgid "Floating split"
+msgstr "ðÌÁ×ÁÀÝÅÅ ÒÁÚÄÅÌÅÎÉÅ."
+
+#: ../mod_query/mod_query.lua:768
+msgid "%s menu:"
+msgstr "%s ÍÅÎÀ:"
+
+#: ../ext_statusbar/ext_statusbar.lua:318
+msgid "Failed to create a timer for statusbar."
+msgstr "ôÁÊÍÅÒ ÄÌÑ ÓÔÁÔÕÓÂÁÒÁ ÎÅ ÓÏÚÄÁÎ."
+
+#: ../etc/cfg_ionws.lua:52
+#: ../etc/cfg_ionws.lua:62
+msgid "Vertically at root"
+msgstr "÷ÅÒÔÉËÁÌØÎÏ × ËÏÒÎÅ×ÏÍ"
+
+#
+#
+#: ../etc/cfg_menu.lua:16
+msgid "Activate current menu entry."
+msgstr "áËÔÉ×ÉÒÏ×ÁÔØ ÔÅËÕÝÉÊ ÐÕÎËÔ ÍÅÎÀ."
+
+#: ../ioncore/ioncore-efbb.lua:12
+msgid ""
+"Making the following minimal emergency mappings:\n"
+"  F2 -> xterm\n"
+"  F11 -> restart\n"
+"  F12 -> exit\n"
+"  Mod1+C -> close\n"
+"  Mod1+K P/N -> WFrame.switch_next/switch_prev\n"
+msgstr ""
+"Making the following minimal emergency mappings:\n"
+"  F2 -> xterm\n"
+"  F11 -> restart\n"
+"  F12 -> exit\n"
+"  Mod1+C -> close\n"
+"  Mod1+K P/N -> WFrame.switch_next/switch_prev\n"
+
+#
+#: ../etc/cfg_ionws.lua:45
+msgid "Transpose at root"
+msgstr "ðÅÒÅÍÅÓÔÉÔØ × ËÏÒÎÅ×ÏÍ"
+
+#: ../pwm/cfg_pwm_menus.lua:17
+#: ../etc/cfg_menus.lua:12
+msgid "Styles"
+msgstr "óÔÉÌÉ"
+
+#: ../etc/cfg_bindings.lua:106
+msgid "Query for command line to execute."
+msgstr "úÁÐÒÏÓ ÎÁ ÉÓÐÏÌÎÅÎÉÅ ËÏÍÁÎÄÎÏÊ ÓÔÒÏËÉ"
+
+#: ../mod_query/mod_query.lua:323
+msgid "Unable to create workspace: no screen."
+msgstr "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÒÁÂÏÞÉÊ ÓÔÏÌ: ÎÅÔ ÜËÒÁÎÁ."
+
+#: ../ext_statusbar/ext_statusbar.lua:27
+msgid "load"
+msgstr "ÚÁÇÒÕÚËÁ"
+
+#
+#: ../etc/cfg_ionws.lua:42
+msgid "Flip"
+msgstr "ðÅÒÅ×ÅÒÎÕÔØ"
+
+#: ../etc/cfg_ionws.lua:41
+msgid "Flip&transpose"
+msgstr "ðÅÒÅ×ÅÒÎÕÔØ É ÐÅÒÅÍÅÓÔÉÔØ"
+
+#: ../pwm/cfg_pwm_menus.lua:12
+#: ../etc/cfg_menus.lua:9
+msgid "Lock screen"
+msgstr "úÁÂÌÏËÉÒÏ×ÁÔØ ÜËÒÁÎ"
+
+#: ../mod_query/mod_query.lua:449
+msgid "Edit file:"
+msgstr "òÅÄÁËÔÉÒÏ×ÁÔØ ÆÁÊÌ:"
+
+#: ../etc/cfg_menus.lua:19
+msgid "XTerm"
+msgstr "XTerm"
+
+#: ../build/mkman.lua:198
+msgid "%s %s at %s"
+msgstr "%s %s ÎÁ %s"
+
+#
+#: ../etc/cfg_bindings.lua:85
+msgid "Nudge current client window. This might help with some programs' resizing problems."
+msgstr ""
+
+#: ../etc/cfg_ionws.lua:22
+msgid "Destroy current frame."
+msgstr "õÄÁÌÉÔØ ÔÅËÕÝÉÊ ÆÒÅÊÍ."
+
+#: ../ioncore/ioncore-bindings.lua:53
+msgid "Error in command string: "
+msgstr "ïÛÉÂËÁ × ÓÔÒÏËÅ ËÏÍÁÎÄÙ:"
+
+#: ../etc/cfg_ionws.lua:19
+msgid "Split current frame horizontally."
+msgstr "òÁÚÄÅÌÉÔØ ÔÅËÕÝÉÊ ÆÒÅÊÍ ÐÏ ÇÏÒÉÚÏÎÔÁÌÉ."
+
+#: ../etc/cfg_menus.lua:42
+msgid "Attach tagged"
+msgstr "ðÒÉÃÅÐÉÔØ ÏËÎÁ Ó ÔÅÇÁÍÉ"
+
+#: ../etc/cfg_ionws.lua:54
+#: ../etc/cfg_ionws.lua:64
+msgid "Horizontally at root"
+msgstr "çÏÒÉÚÏÎÔÁÌØÎÏ × ËÏÒÎÅ×ÏÍ"
+
+#: ../etc/cfg_menus.lua:40
+msgid "(Un)tag"
+msgstr "ðÏÓÔÁ×ÉÔØ/ÕÄÁÌÉÔØ ÔÅÇ"
+
+#: ../mod_query/mod_query.lua:459
+msgid "View file:"
+msgstr "ðÒÏÓÍÏÔÒ ÆÁÊÌÁ:"
+
+#: ../mod_query/mod_query.lua:421
+msgid "Workspace name:"
+msgstr "éÍÑ ÒÁÂÏÞÅÇÏ ÓÔÏÌÁ:"
+
+#: ../etc/cfg_menus.lua:31
+msgid "Exit"
+msgstr "÷ÙÈÏÄ"
+
+#: ../etc/cfg_menus.lua:27
+msgid "Save"
+msgstr "óÏÈÒÁÎÉÔØ"
+
+#
+#: ../pwm/cfg_pwm_menus.lua:15
+msgid "Workspaces"
+msgstr "òÁÂÏÞÉÅ ÓÔÏÌÙ"
+
+#: ../etc/cfg_menus.lua:20
+msgid "Mozilla Firefox"
+msgstr "Mozilla Firefox"
+
+#: ../ioncore/ioncore-bindings.lua:45
+msgid "Error compiling guard: %s"
+msgstr ""
+
+#: ../mod_menu/mod_menu.lua:271
+msgid "Refresh list"
+msgstr "ïÂÎÏ×ÉÔØ ÓÐÉÓÏË"
+
+#: ../etc/cfg_floatws.lua:9
+msgid "Circulate focus and raise the newly focused frame."
+msgstr "ðÅÒÅÄÁÔØ ÆÏËÕÓ É ÐÏÄÎÑÔØ ÆÒÅÊÍ Ó ÎÏ×ÙÍ ÆÏËÕÓÏÍ."
+
+#
+#: ../etc/cfg_menus.lua:21
+msgid "Run..."
+msgstr "÷ÙÐÏÌÎÉÔØ..."
+
+#: ../etc/cfg_bindings.lua:118
+msgid "Query for file to view."
+msgstr "úÁÐÒÏÓ ÐÒÏÓÍÏÔÒÁ ÆÁÊÌÁ."
+
+#: ../mod_query/mod_query.lua:511
+msgid "Failed to open ~/.ssh/known_hosts"
+msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ ~/.ssh/known_hosts"
+
+#: ../pwm/cfg_pwm_menus.lua:18
+#: ../etc/cfg_menus.lua:13
+msgid "Session"
+msgstr "óÅÓÓÉÑ"
+
+#: ../pwm/cfg_pwm_menus.lua:27
+msgid "List"
+msgstr "óÐÉÓÏË"
+
diff --git a/pwm/Makefile b/pwm/Makefile
new file mode 100644 (file)
index 0000000..84c3946
--- /dev/null
@@ -0,0 +1,73 @@
+##
+## PWM Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+# List of modules to possibly preload
+include $(TOPDIR)/modulelist.mk
+
+######################################
+
+SOURCES=pwm.c
+
+ETC = cfg_pwm.lua
+
+TARGETS = pwm3
+
+INCLUDES += $(X11_INCLUDES) 
+INCLUDES += $(LIBMAINLOOP_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES)
+INCLUDES += -I..
+
+LIBS += $(X11_LIBS) $(XINERAMA_LIBS)
+LIBS += $(WHOLEA) $(LIBMAINLOOP_LIBS) $(LIBEXTL_LIBS) $(LIBTU_LIBS) $(NO_WHOLEA)
+LIBS += $(LUA_LIBS) $(DL_LIBS)
+LIBS += -lm
+
+ifeq ($(PRELOAD_MODULES),1)
+EXT_OBJS += $(foreach mod, $(PWM_MODULE_LIST), ../$(mod)/$(mod).a)
+DEPEND_DEPENDS += preload.c
+SOURCES += preload.c
+TO_CLEAN += preload.c
+LIBS += -lSM -lICE
+else
+LINKOPTS = $(EXPORT_DYNAMIC)
+WHOLEA = -Wl,-whole-archive
+NO_WHOLEA = -Wl,-no-whole-archive
+endif
+
+EXT_OBJS += ../ioncore/ioncore.a
+
+DEFINES += -DETCDIR=\"$(ETCDIR)\" -DSHAREDIR=\"$(SHAREDIR)\" \
+          -DEXTRABINDIR=\"$(EXTRABINDIR)\" -DMODULEDIR=\"$(MODULEDIR)\" \
+          -DLCDIR=\"$(LCDIR)\" -DLOCALEDIR=\"$(LOCALEDIR)\"
+
+ifndef PWM_ETCDIR
+PWM_ETCDIR = $(ETCDIR)
+else
+DEFINES += -DPWM_ETCDIR=\"$(PWM_ETCDIR)\"
+endif
+
+CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE)
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+pwm3: $(OBJS) $(EXT_OBJS)
+       $(CC) $(LINKOPTS) $(OBJS) $(WHOLEA) $(EXT_OBJS) $(NO_WHOLEA) $(LDFLAGS) -o $@
+
+preload.c:
+       $(LUA) ../build/mkpreload.lua $(PWM_MODULE_LIST) > preload.c
+
+_install:
+       $(INSTALLDIR) $(BINDIR)
+       $(INSTALL) -m $(BIN_MODE) pwm3 $(BINDIR)
+       $(INSTALLDIR) $(PWM_ETCDIR)
+       for i in $(ETC); do \
+               $(INSTALL) -m $(DATA_MODE) $$i $(PWM_ETCDIR); \
+       done
diff --git a/pwm/cfg_pwm.lua b/pwm/cfg_pwm.lua
new file mode 100644 (file)
index 0000000..1fe7df4
--- /dev/null
@@ -0,0 +1,97 @@
+--
+-- PWM main configuration file
+--
+-- This file only includes some settings that are rather frequently altered,
+-- and the differences between PWM and Ion. The rest of the settings are in 
+-- cfg_ioncore.lua and individual modules' configuration files 
+-- (cfg_modulename.lua).
+--
+
+-- Set default modifiers. Alt should usually be mapped to Mod1 on
+-- XFree86-based systems. The flying window keys are probably Mod3
+-- or Mod4; see the output of 'xmodmap'.
+--META="Mod1+"
+--ALTMETA=""
+
+-- Some basic settings
+ioncore.set{
+    -- Maximum delay between clicks in milliseconds to be considered a
+    -- double click.
+    --dblclick_delay=250,
+
+    -- For keyboard resize, time (in milliseconds) to wait after latest
+    -- key press before automatically leaving resize mode (and doing
+    -- the resize in case of non-opaque move).
+    --kbresize_delay=1500,
+
+    -- Opaque resize?
+    --opaque_resize=false,
+
+    -- Movement commands warp the pointer to frames instead of just
+    -- changing focus. Enabled by default.
+    --warp=true,
+}
+
+-- cfg_ioncore contains configuration of the Ion 'core'
+dopath("cfg_ioncore")
+
+-- Load some modules. 
+--dopath("cfg_modules")
+--dopath("mod_query")
+dopath("mod_menu")
+--dopath("mod_tiling")
+--dopath("mod_panews")
+--dopath("mod_statusbar")
+dopath("mod_dock")
+--dopath("mod_sp")
+
+
+--
+-- PWM customisations to bindings and menus
+--
+
+
+-- Unbind anything using mod_query and rebinding to mod_menu where
+-- applicable.
+
+defbindings("WScreen", {
+    kpress(ALTMETA.."F12", "mod_menu.menu(_, _sub, 'mainmenu', {big=true})"),
+})
+
+defbindings("WMPlex.toplevel", {
+    kpress(ALTMETA.."F1", nil),
+    kpress(META..   "F1", "ioncore.exec_on(_, ':man pwm3')"),
+    kpress(ALTMETA.."F3", nil),
+    kpress(META..   "F3", nil),
+    kpress(ALTMETA.."F4", nil),
+    kpress(ALTMETA.."F5", nil),
+    kpress(ALTMETA.."F6", nil),
+    kpress(ALTMETA.."F9", nil),
+    kpress(META.."G", nil),
+    kpress(META.."A", nil),
+})
+
+defbindings("WFrame", {
+    kpress(META.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"),
+})
+
+-- Make a new main menu with additional workspace menu.
+
+defmenu("mainmenu", {
+    submenu("Programs",      "appmenu"),
+    menuentry("Lock screen", "ioncore.exec_on(_, 'xlock')"),
+    menuentry("Help",        "ioncore.exec_on(_, ':man pwm3')"),
+    submenu("Workspaces",    "wsmenu"),
+    menuentry("New",         "ioncore.create_ws(_)"),
+    submenu("Styles",        "stylemenu"),
+    submenu("Session",       "sessionmenu"),
+})
+
+-- Workspace menu
+defmenu("wsmenu", {
+    menuentry("New",         "ioncore.create_ws(_)"),
+    menuentry("Close",       "WRegion.rqclose(_sub)",
+                             "_sub:WGroupWS"),
+    submenu("List",          "workspacelist"),
+})
+
diff --git a/pwm/pwm.c b/pwm/pwm.c
new file mode 100644 (file)
index 0000000..773aace
--- /dev/null
+++ b/pwm/pwm.c
@@ -0,0 +1,241 @@
+/*
+ * ion/pwm/pwm.c
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2006. 
+ *
+ * Ion is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <libtu/util.h>
+#include <libtu/optparser.h>
+#include <libtu/errorlog.h>
+#include <libextl/readconfig.h>
+#include <libmainloop/exec.h>
+
+#include <ioncore/common.h>
+#include <ioncore/global.h>
+#include <ioncore/ioncore.h>
+#include <ioncore/exec.h>
+#include <ioncore/event.h>
+#include "../version.h"
+
+
+/* Options. Getopt is not used because getopt_long is quite gnu-specific
+ * and they don't know of '-display foo' -style args anyway.
+ * Instead, I've reinvented the wheel in libtu :(.
+ */
+static OptParserOpt pwm_opts[]={
+    {OPT_ID('d'), "display",  OPT_ARG, "host:dpy.scr", 
+     DUMMY_TR("X display to use")},
+    
+    {'c', "conffile", OPT_ARG, "config_file", 
+     DUMMY_TR("Configuration file")},
+    
+    {'s', "searchdir", OPT_ARG, "dir", 
+     DUMMY_TR("Add directory to search path")},
+
+    {OPT_ID('o'), "oneroot",  0, NULL,
+     DUMMY_TR("Manage default root window/non-Xinerama screen only")},
+
+#if defined(CF_XINERAMA) || defined(CF_SUN_XINERAMA)
+    {OPT_ID('x'), "xinerama", OPT_ARG, "1|0",
+     DUMMY_TR("Use Xinerama screen information (default: 0/no)")},
+#else
+    {OPT_ID('x'), "xinerama", OPT_ARG, "?",
+     DUMMY_TR("Ignored: not compiled with Xinerama support")},
+#endif
+    
+    {OPT_ID('s'), "session",  OPT_ARG, "session_name", 
+     DUMMY_TR("Name of session (affects savefiles)")},
+    
+    {OPT_ID('S'), "smclientid", OPT_ARG, "client_id", 
+     DUMMY_TR("Session manager client ID")},
+
+    {OPT_ID('N'), "noerrorlog", 0, NULL, 
+     DUMMY_TR("Do not create startup error log and display it "
+              "with xmessage.")},
+    
+    {'h', "help", 0, NULL, 
+     DUMMY_TR("Show this help")},
+    
+    {'V', "version", 0, NULL,
+     DUMMY_TR("Show program version")},
+    
+    {OPT_ID('a'), "about", 0, NULL,
+     DUMMY_TR("Show about text")},
+    
+    END_OPTPARSEROPTS
+};
+
+
+static void help()
+{
+    int i;
+    printf(TR("Usage: %s [options]\n\n"), prog_execname());
+    for(i=0; pwm_opts[i].descr!=NULL; i++)
+        pwm_opts[i].descr=TR(pwm_opts[i].descr);
+    optparser_printhelp(OPTP_MIDLONG, pwm_opts);
+    printf("\n");
+}
+
+
+int main(int argc, char*argv[])
+{
+    const char *cfgfile="cfg_pwm";
+    const char *display=NULL;
+    char *cmd=NULL;
+    int stflags=IONCORE_STARTUP_NOXINERAMA;
+    int opt;
+    ErrorLog el;
+    FILE *ef=NULL;
+    char *efnam=NULL;
+    bool may_continue=FALSE;
+    bool noerrorlog=FALSE;
+
+    libtu_init(argv[0]);
+
+    if(!ioncore_init("pwm3", argc, argv, LOCALEDIR))
+        return EXIT_FAILURE;
+
+    extl_add_searchdir(EXTRABINDIR); /* ion-completefile */
+    extl_add_searchdir(MODULEDIR);
+    extl_add_searchdir(ETCDIR);
+#ifdef PWM_ETCDIR    
+    extl_add_searchdir(PWM_ETCDIR);
+#endif
+    extl_add_searchdir(SHAREDIR);
+    extl_add_searchdir(LCDIR);
+    extl_set_userdirs("pwm3");
+
+    optparser_init(argc, argv, OPTP_MIDLONG, pwm_opts);
+    
+    while((opt=optparser_get_opt())){
+        switch(opt){
+        case OPT_ID('d'):
+            display=optparser_get_arg();
+            break;
+        case 'c':
+            cfgfile=optparser_get_arg();
+            break;
+        case 's':
+            extl_add_searchdir(optparser_get_arg());
+            break;
+        case OPT_ID('S'):
+            ioncore_g.sm_client_id=optparser_get_arg();
+            break;
+        case OPT_ID('o'):
+            stflags|=IONCORE_STARTUP_ONEROOT;
+            break;
+        case OPT_ID('x'):
+            {
+                const char *p=optparser_get_arg();
+                if(strcmp(p, "1")==0)
+                    stflags&=~IONCORE_STARTUP_NOXINERAMA;
+                else if(strcmp(p, "0")==0)
+                    stflags|=IONCORE_STARTUP_NOXINERAMA;
+                else
+                    warn(TR("Invalid parameter to -xinerama."));
+            }
+            break;
+        case OPT_ID('s'):
+            extl_set_sessiondir(optparser_get_arg());
+            break;
+        case OPT_ID('N'):
+            noerrorlog=TRUE;
+            break;
+        case 'h':
+            help();
+            return EXIT_SUCCESS;
+        case 'V':
+            printf("%s\n", ION_VERSION);
+            return EXIT_SUCCESS;
+        case OPT_ID('a'):
+            printf("%s\n", ioncore_aboutmsg());
+            return EXIT_SUCCESS;
+        default:
+            warn(TR("Invalid command line."));
+            help();
+            return EXIT_FAILURE;
+        }
+    }
+
+    if(!noerrorlog){
+        /* We may have to pass the file to xmessage so just using tmpfile()
+         * isn't sufficient.
+         */
+        libtu_asprintf(&efnam, "%s/pwm-%d-startup-errorlog", P_tmpdir,
+                       getpid());
+        if(efnam==NULL){
+            warn_err();
+        }else{
+            ef=fopen(efnam, "wt");
+            if(ef==NULL){
+                warn_err_obj(efnam);
+                free(efnam);
+                efnam=NULL;
+            }else{
+                cloexec_braindamage_fix(fileno(ef));
+                fprintf(ef, TR("PWM startup error log:\n"));
+                errorlog_begin_file(&el, ef);
+            }
+        }
+    }
+
+    if(ioncore_startup(display, cfgfile, stflags))
+        may_continue=TRUE;
+
+fail:
+    if(!may_continue)
+        warn(TR("Refusing to start due to encountered errors."));
+    
+    if(ef!=NULL){
+        pid_t pid=-1;
+        if(errorlog_end(&el) && ioncore_g.dpy!=NULL){
+            fclose(ef);
+            pid=fork();
+            if(pid==0){
+                ioncore_setup_environ(DefaultScreen(ioncore_g.dpy));
+                if(!may_continue)
+                    XCloseDisplay(ioncore_g.dpy);
+                else
+                    close(ioncore_g.conn);
+                libtu_asprintf(&cmd, CF_XMESSAGE " %s", efnam);
+                if(cmd==NULL){
+                    warn_err();
+                }else if(system(cmd)==-1){
+                    warn_err_obj(cmd);
+                }
+                unlink(efnam);
+                exit(EXIT_SUCCESS);
+            }
+            if(!may_continue && pid>0)
+                waitpid(pid, NULL, 0);
+        }else{
+            fclose(ef);
+        }
+        if(pid<0)
+            unlink(efnam);
+        free(efnam);
+    }
+
+    if(!may_continue)
+        return EXIT_FAILURE;
+    
+    ioncore_mainloop();
+    
+    /* The code should never return here */
+    return EXIT_SUCCESS;
+}
diff --git a/system.mk b/system.mk
new file mode 100644 (file)
index 0000000..06ce3b1
--- /dev/null
+++ b/system.mk
@@ -0,0 +1,195 @@
+##
+## System settings
+##
+
+
+##
+## Installation paths
+##
+
+PREFIX=/usr/local
+
+# Unless you are creating a package conforming to some OS's standards, you
+# probably do not want to modify the following directories:
+
+# Main binaries
+BINDIR=$(PREFIX)/bin
+# Configuration .lua files
+ETCDIR=$(PREFIX)/etc/ion3
+# Some .lua files and ion-* shell scripts
+SHAREDIR=$(PREFIX)/share/ion3
+# Manual pages
+MANDIR=$(PREFIX)/share/man
+# Some documents
+DOCDIR=$(PREFIX)/share/doc/ion3
+# Nothing at the moment
+INCDIR=$(PREFIX)/include/ion3
+# Nothing at the moment
+LIBDIR=$(PREFIX)/lib
+# Modules
+MODULEDIR=$(LIBDIR)/ion3/mod
+# Compiled Lua source code
+LCDIR=$(LIBDIR)/ion3/lc
+# ion-completefile (does not belong in SHAREDIR being a binary file)
+EXTRABINDIR=$(LIBDIR)/ion3/bin
+# For ion-completeman system-wide cache
+VARDIR=/var/cache/ion3
+# Message catalogs
+LOCALEDIR=$(PREFIX)/share/locale
+
+
+##
+## Modules
+##
+
+# Set PRELOAD_MODULES=1 if your system does not support dynamically loaded
+# modules through 'libdl' or has non-standard naming conventions.
+#PRELOAD_MODULES=1
+
+# Flags to link with libdl.
+DL_LIBS=-ldl
+
+
+##
+## Lua
+##
+
+# If you have installed Lua 5.1 from the official tarball without changing
+# paths, this should do it.
+LUA_DIR=/usr/local
+LUA_LIBS = -L$(LUA_DIR)/lib -llua
+LUA_INCLUDES = -I$(LUA_DIR)/include
+LUA=$(LUA_DIR)/bin/lua
+LUAC=$(LUA_DIR)/bin/luac
+
+# If you are using the Debian packages, the following settings should be
+# what you want.
+#LUA_LIBS=`pkg-config --libs lua5.1`
+#LUA_INCLUDES=`pkg-config --cflags lua5.1`
+#LUA=`which lua5.1`
+#LUAC=`which luac5.1`
+
+
+##
+## X libraries, includes and options
+##
+
+X11_PREFIX=/usr/X11R6
+# SunOS/Solaris
+#X11_PREFIX=/usr/openwin
+
+X11_LIBS=-L$(X11_PREFIX)/lib -lX11 -lXext
+X11_INCLUDES=-I$(X11_PREFIX)/include
+
+# Change commenting to disable Xinerama support
+XINERAMA_LIBS=-lXinerama
+DEFINES += -DCF_XINERAMA
+# For Solaris
+#XINERAMA_LIBS=
+#DEFINES += -DCF_SUN_XINERAMA
+
+# XFree86 libraries up to 4.3.0 have a bug that will cause Ion to segfault
+# if Opera is used when i18n support is enabled. The following setting
+# should work around that situation.
+DEFINES += -DCF_XFREE86_TEXTPROP_BUG_WORKAROUND
+
+# Use the Xutf8 routines (XFree86 extension) instead of Xmb routines in
+# an UTF8 locale.
+#DEFINES += -DCF_DE_USE_XUTF8
+
+# Remap F11 key to SunF36 and F12 to SunF37? You may want to set this
+# on SunOS.
+#DEFINES += -DCF_SUN_F1X_REMAP
+
+
+##
+## libc
+##
+
+# You may uncomment this if you know your system has
+# asprintf and vasprintf in the c library. (gnu libc has.)
+# If HAS_SYSTEM_ASPRINTF is not defined, an implementation
+# in sprintf_2.2/ is used.
+#HAS_SYSTEM_ASPRINTF=1
+
+
+# If you're on an archaic system (such as relatively recent *BSD releases)
+# without even dummy multibyte/widechar and localisation support, you may 
+# have to uncomment the following line:
+#DEFINES += -DCF_NO_LOCALE
+
+# On some other systems you may something like this:
+#EXTRA_LIBS += -lintl
+#EXTRA_INCLUDES +=
+
+
+##
+## C compiler
+##
+
+CC=gcc
+
+# Same as '-Wall -pedantic' without '-Wunused' as callbacks often
+# have unused variables.
+WARN=  -W -Wimplicit -Wreturn-type -Wswitch -Wcomment \
+       -Wtrigraphs -Wformat -Wchar-subscripts \
+       -Wparentheses -pedantic -Wuninitialized
+
+CFLAGS=-g -Os $(WARN) $(DEFINES) $(EXTRA_INCLUDES) $(INCLUDES)
+LDFLAGS=-g -Os $(EXTRA_LIBS) $(LIBS)
+EXPORT_DYNAMIC=-Xlinker --export-dynamic
+
+# The following options are mainly for development use and can be used
+# to check that the code seems to conform to some standards. Depending
+# on the version and vendor of you libc, the options may or may not have
+# expected results. If you define one of C99_SOURCE or XOPEN_SOURCE, you
+# may also have to define the other. 
+
+#C89_SOURCE=-ansi
+
+#POSIX_SOURCE=-D_POSIX_SOURCE
+
+# Most systems
+#XOPEN_SOURCE=-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED
+# SunOS, (Irix)
+#XOPEN_SOURCE=-D__EXTENSIONS__
+
+#C99_SOURCE=-std=c99 -DCF_HAS_VA_COPY
+
+# The -DCF_HAS_VA_COPY option should allow for some optimisations, and 
+# in some cases simply defining
+#C99_SOURCE=-DCF_HAS_VA_COPY
+# might allow for those optimisations to be taken without any  special
+# libc or compiler options.
+
+
+##
+## make depend
+##
+
+DEPEND_FILE=.depend
+DO_MAKE_DEPEND=$(CC) -MM $(DEFINES) $(EXTRA_INCLUDES) $(INCLUDES)
+MAKE_DEPEND=$(DO_MAKE_DEPEND) $(SOURCES) > $(DEPEND_FILE)
+
+##
+## AR
+##
+
+AR=ar
+ARFLAGS=cr
+RANLIB=ranlib
+
+
+##
+## Install & strip
+##
+
+INSTALL=sh $(TOPDIR)/install-sh -c
+INSTALLDIR=mkdir -p
+
+BIN_MODE=755
+DATA_MODE=644
+
+STRIP=strip
+
+RM=rm
diff --git a/utils/Makefile b/utils/Makefile
new file mode 100644 (file)
index 0000000..2c58b50
--- /dev/null
@@ -0,0 +1,33 @@
+##
+## Ion utils/ Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+SUBDIRS=ion-completefile
+INSTALL_SUBDIRS=$(SUBDIRS)
+
+SHELLSCRIPTS = ion-runinxterm ion-completeman
+
+TARGETS = ion-completeman
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+_install:
+       $(INSTALLDIR) $(SHAREDIR)
+       for i in $(SHELLSCRIPTS); do \
+               $(INSTALL) -m $(BIN_MODE) $$i $(SHAREDIR); \
+       done
+
+%: %.in
+       sed 's#@SHAREDIR@#$(SHAREDIR)#g' $< | \
+       sed 's#@VARDIR@#$(VARDIR)#g' > $@
+       chmod $(BIN_MODE) $@
diff --git a/utils/ion-completefile/Makefile b/utils/ion-completefile/Makefile
new file mode 100644 (file)
index 0000000..e974d11
--- /dev/null
@@ -0,0 +1,30 @@
+##
+## Ion-completefile Makefile
+##
+
+# System-specific configuration is in system.mk
+TOPDIR=../..
+include $(TOPDIR)/build/system-inc.mk
+
+######################################
+
+LIBS += $(LIBTU_LIBS)
+INCLUDES += $(LIBTU_INCLUDES)
+CFLAGS += $(XOPEN_SOURCE)
+
+SOURCES=ion-completefile.c
+
+TARGETS=ion-completefile
+
+######################################
+
+include $(TOPDIR)/build/rules.mk
+
+######################################
+
+ion-completefile: $(SOURCES)
+       $(CC) $< $(CFLAGS) $(LDFLAGS) -o $@
+
+_install:
+       $(INSTALLDIR) $(EXTRABINDIR)
+       $(INSTALL) -s -m $(BIN_MODE) ion-completefile $(EXTRABINDIR)
diff --git a/utils/ion-completefile/ion-completefile.c b/utils/ion-completefile/ion-completefile.c
new file mode 100644 (file)
index 0000000..6e68e0a
--- /dev/null
@@ -0,0 +1,711 @@
+/*
+ * ion/share/ion-completefile/ion-completefile.c
+ */
+
+/****************************************************************************/
+/*                                                                          */
+/* Copyright 1992 Simmule Turner and Rich Salz.  All rights reserved.       */
+/*                                                                          */
+/* This software is not subject to any license of the American Telephone    */
+/* and Telegraph Company or of the Regents of the University of California. */
+/*                                                                          */
+/* Permission is granted to anyone to use this software for any purpose on  */
+/* any computer system, and to alter it and redistribute it freely, subject */
+/* to the following restrictions:                                           */
+/* 1. The authors are not responsible for the consequences of use of this   */
+/*    software, no matter how awful, even if they arise from flaws in it.   */
+/* 2. The origin of this software must not be misrepresented, either by     */
+/*    explicit claim or by omission.  Since few users ever read sources,    */
+/*    credits must appear in the documentation.                             */
+/* 3. Altered versions must be plainly marked as such, and must not be      */
+/*    misrepresented as being the original software.  Since few users       */
+/*    ever read sources, credits must appear in the documentation.          */
+/* 4. This notice may not be removed or altered.                            */
+/*                                                                          */
+/****************************************************************************/
+/*                                                                          */
+/*  This is a line-editing library, it can be linked into almost any        */
+/*  program to provide command-line editing and recall.                     */
+/*                                                                          */
+/*  Posted to comp.sources.misc Sun, 2 Aug 1992 03:05:27 GMT                */
+/*      by rsalz@osf.org (Rich $alz)                                        */
+/*                                                                          */
+/****************************************************************************/
+/*                                                                          */
+/*  The version contained here has some modifications by awb@cstr.ed.ac.uk  */
+/*  (Alan W Black) in order to integrate it with the Edinburgh Speech Tools */
+/*  library and Scheme-in-one-defun in particular.  All modifications to    */
+/*  to this work are continued with the same copyright above.  That is      */
+/*  This version editline does not have the the "no commercial use"         */
+/*  restriction that some of the rest of the EST library may have           */
+/*  awb Dec 30 1998                                                         */
+/*                                                                          */
+/****************************************************************************/
+/*  $Revision: 1.2 $
+ **
+ **  History and file completion functions for editline library.
+ */
+
+
+/*
+ * Adapted for use with Ion and tilde-expansion added by
+ * Tuomo Valkonen <tuomov@cc.tut.fi>, 2000-08-23.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <pwd.h>
+
+#include <libtu/util.h>
+#include <libtu/types.h>
+#include <libtu/misc.h>
+#include <libtu/output.h>
+
+#define STRDUP(X) scopy(X)
+#define DISPOSE(X) free(X)
+#define NEW(T, X)  ALLOC_N(T, X)
+#define STATIC static
+#define EL_CONST const
+#define SIZE_T int
+#define MEM_INC 64
+#define COPYFROMTO(new, p, len) \
+       (void)memcpy((char *)(new), (char *)(p), (int)(len))
+
+#ifndef NGROUPS
+/* Hopefully one of these is defined... */
+#define NGROUPS NGROUPS_MAX
+#endif
+
+typedef struct dirent DIRENTRY;
+
+static void el_add_slash(char *path,char *p)
+{
+       struct stat sb;
+       
+       if (stat(path, &sb) >= 0)
+               (void)strcat(p, S_ISDIR(sb.st_mode) ? "/" : " ");
+}
+
+static int el_is_directory(char *path)
+{
+       struct stat sb;
+       
+       if ((stat(path, &sb) >= 0) && S_ISDIR(sb.st_mode))
+               return 1;
+       else
+               return 0;
+}
+
+static int el_is_executable(char *path)
+{
+       unsigned int t_uid = geteuid();
+       gid_t t_gids[NGROUPS];
+       int groupcount;
+       struct stat sb;
+       int i;
+       
+       groupcount = getgroups(NGROUPS, t_gids);
+
+       if((stat(path, &sb) >= 0) && S_ISREG(sb.st_mode)) {
+               /* Normal file, see if we can execute it. */
+               
+               if (sb.st_mode & S_IXOTH) { /* All can execute */
+                       return (1);
+               }
+               if (sb.st_uid == t_uid && (sb.st_mode & S_IXUSR)) {
+                       return (1);
+               }
+               if (sb.st_mode & S_IXGRP) {
+                       for (i = 0; i < groupcount; i++) {
+                           if (sb.st_gid == t_gids[i]) {
+                               return (1);
+                           }
+                       }
+               }
+       }
+       return (0);
+}
+
+
+/*
+ **  strcmp-like sorting predicate for qsort.
+ */
+/*STATIC int compare(EL_CONST void *p1,EL_CONST void *p2)
+ {
+ EL_CONST char **v1;
+ EL_CONST char **v2;
+ * 
+ v1 = (EL_CONST char **)p1;
+ v2 = (EL_CONST char **)p2;
+ return strcmp(*v1, *v2);
+ }*/
+
+/*
+ **  Fill in *avp with an array of names that match file, up to its length.
+ **  Ignore . and .. .
+ */
+static int FindMatches(char *dir,char *file,char ***avp)
+{
+    char       **av;
+    char       **new;
+    char       *p;
+    DIR                *dp;
+    DIRENTRY   *ep;
+    SIZE_T     ac;
+    SIZE_T     len;
+       
+       if(*dir=='\0')
+               dir=".";
+       
+    if ((dp = opendir(dir)) == NULL)
+               return 0;
+       
+    av = NULL;
+    ac = 0;
+    len = strlen(file);
+    while ((ep = readdir(dp)) != NULL) {
+               p = ep->d_name;
+               if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')))
+                       continue;
+               if (len && strncmp(p, file, len) != 0)
+                       continue;
+               
+               if ((ac % MEM_INC) == 0) {
+                       if ((new = NEW(char*, ac + MEM_INC)) == NULL)
+                               break;
+                       if (ac) {
+                               COPYFROMTO(new, av, ac * sizeof (char **));
+                               DISPOSE(av);
+                       }
+                       *avp = av = new;
+               }
+               
+               if ((av[ac] = STRDUP(p)) == NULL) {
+                       if (ac == 0)
+                               DISPOSE(av);
+                       break;
+               }
+               ac++;
+    }
+       
+    /* Clean up and return. */
+    (void)closedir(dp);
+       /*    if (ac)
+        qsort(av, ac, sizeof (char **), compare);*/
+    return ac;
+}
+
+
+/*
+ ** Slight modification on FindMatches, to search through current PATH
+ ** 
+ */
+static int FindFullPathMatches(char *file,char ***avp)
+{
+    char       **av;
+    char       **new;
+    char       *p;
+    char    *path = getenv("PATH");
+    char    t_tmp[1024];
+    char    *t_path;
+    char    *t_t_path;
+    DIR                *dp;
+    DIRENTRY*ep;
+    SIZE_T     ac;
+    SIZE_T     len;
+       
+    t_path = strdup(path);
+    t_t_path = t_path;
+       
+    av = NULL;
+    ac = 0;
+       
+    t_t_path = strtok(t_path, ":\n\0");
+    while (t_t_path) {
+               if ((dp = opendir(t_t_path)) == NULL) {
+                       t_t_path = strtok(NULL, ":\n\0");
+                       continue;
+               }
+               
+               len = strlen(file);
+               while ((ep = readdir(dp)) != NULL) {
+                       p = ep->d_name;
+                       if (p[0] == '.' && (p[1] == '\0' || (p[0] == '.' &&
+                                                                                                p[1] == '.' &&
+                                                                                                p[2] == '\0'))) {
+                               continue;
+                       }
+                       if (len && strncmp(p, file, len) != 0) {
+                               continue;
+                       }
+                       
+                       snprintf(t_tmp, 1024, "%s/%s", t_t_path, p);
+                       if(!el_is_executable(t_tmp)) {
+                               continue;
+                       }
+                       
+                       if ((ac % MEM_INC) == 0) {
+                               if ((new = NEW(char*, ac + MEM_INC)) == NULL) {
+                                       break;
+                               }
+                               if (ac) {
+                                       COPYFROMTO(new, av, ac * sizeof (char **));
+                                       DISPOSE(av);
+                               }
+                               *avp = av = new;
+                       }
+                       if ((av[ac] = STRDUP(p)) == NULL) {
+                               if (ac == 0)
+                                       DISPOSE(av);
+                               break;
+                       }
+                       ac++;
+               }
+               (void)closedir(dp);
+               t_t_path = strtok(NULL, ":\n\0");
+               
+               
+    } /* t_path-while */
+       
+    /* Clean up and return. */
+       
+    /*if (ac)
+        qsort(av, ac, sizeof (char **), compare); */
+    free(t_path);
+       
+    return ac;
+}
+
+
+/*
+ **  Split a pathname into allocated directory and trailing filename parts.
+ */
+STATIC int SplitPath(const char *path,char **dirpart,char **filepart)
+{
+    static char        DOT[] = "./";
+    char       *dpart;
+    char       *fpart;
+       
+    if ((fpart = strrchr(path, '/')) == NULL) {
+               /* No slashes in path */
+               if ((dpart = STRDUP(DOT)) == NULL)
+                       return -1;
+               if ((fpart = STRDUP(path)) == NULL) {
+                       DISPOSE(dpart);
+                       return -1;
+               }
+    }else{
+               if ((dpart = STRDUP(path)) == NULL)
+                       return -1;
+               /* Include the slash -- Tuomo */
+               dpart[fpart - path + 1] = '\0';
+               if ((fpart = STRDUP(++fpart)) == NULL) {
+                       DISPOSE(dpart);
+                       return -1;
+               }
+               /* Root no longer a special case due above -- Tuomo
+                if (dpart[0] == '\0')
+                {
+                dpart[0] = '/';
+                dpart[1] = '\0';
+                }
+                */
+    }
+    *dirpart = dpart;
+    *filepart = fpart;
+    return 0;
+}
+
+/*
+ **  Split a pathname into allocated directory and trailing filename parts.
+ */
+STATIC int SplitRelativePath(const char *path,char **dirpart,char **filepart)
+{
+    static char        DOT[] = "./";
+    static char EOL[] = "\0";
+    char *dpart;
+    char *fpart;
+       
+    if ((fpart = strrchr(path, '/')) == NULL) {
+               /* No slashes in path */
+               if ((dpart = STRDUP(EOL)) == NULL)
+                       return -1;
+               if ((fpart = STRDUP(path)) == NULL) {
+                       DISPOSE(dpart);
+                       return -1;
+               }
+    }
+    else {
+               if ((dpart = STRDUP(path)) == NULL)
+                       return -1;
+               /* Include the slash -- Tuomo */
+               dpart[fpart - path + 1] = '\0';
+               if ((fpart = STRDUP(++fpart)) == NULL) {
+                       DISPOSE(dpart);
+                       return -1;
+               }
+               /* Root no longer a special case due above -- Tuomo
+                if (dpart[0] == '\0')
+                {
+                dpart[0] = '/';
+                dpart[1] = '\0';
+                }
+                */
+    }
+    *dirpart = dpart;
+    *filepart = fpart;
+    return 0;
+}
+
+/*
+ **  Attempt to complete the pathname, returning an allocated copy.
+ **  Fill in *unique if we completed it, or set it to 0 if ambiguous.
+ */
+#if 0
+static char *el_complete(char *pathname,int *unique)
+{
+    char       **av;
+    char       *dir;
+    char       *file;
+    char       *new;
+    char       *p;
+    SIZE_T     ac;
+    SIZE_T     end;
+    SIZE_T     i;
+    SIZE_T     j;
+    SIZE_T     len;
+       
+    if (SplitPath(pathname, &dir, &file) < 0)
+               return NULL;
+       
+    if ((ac = FindMatches(dir, file, &av)) == 0) {
+               DISPOSE(dir);
+               DISPOSE(file);
+               return NULL;
+    }
+       
+    p = NULL;
+    len = strlen(file);
+    if (ac == 1) {
+               /* Exactly one match -- finish it off. */
+               *unique = 1;
+               j = strlen(av[0]) - len + 2;
+               if ((p = NEW(char, j + 1)) != NULL) {
+                       COPYFROMTO(p, av[0] + len, j);
+                       if ((new = NEW(char, strlen(dir) + strlen(av[0]) + 2)) != NULL) {
+                               (void)strcpy(new, dir);
+                               (void)strcat(new, "/");
+                               (void)strcat(new, av[0]);
+                               el_add_slash(new, p);
+                               DISPOSE(new);
+                       }
+               }
+    }
+    else {
+               *unique = 0;
+               if (len) {
+                       /* Find largest matching substring. */
+                       for (i = len, end = strlen(av[0]); i < end; i++)
+                       for (j = 1; j < ac; j++)
+                   if (av[0][i] != av[j][i])
+                               goto breakout;
+               breakout:
+                       if (i > len) {
+                               j = i - len + 1;
+                               if ((p = NEW(char, j)) != NULL) {
+                                       COPYFROMTO(p, av[0] + len, j);
+                                       p[j - 1] = '\0';
+                               }
+                       }
+               }
+    }
+       
+    /* Clean up and return. */
+    DISPOSE(dir);
+    DISPOSE(file);
+    for (i = 0; i < ac; i++)
+               DISPOSE(av[i]);
+    DISPOSE(av);
+    return p;
+}
+#endif
+
+static int complete_homedir(const char *username, char ***cp_ret, char **beg)
+{
+       struct passwd *pw;
+       char *name;
+       char **cp;
+       int n=0, l=strlen(username);
+       
+       *cp_ret=NULL;
+       
+       for(pw=getpwent(); pw!=NULL; pw=getpwent()){
+               name=pw->pw_name;
+               
+               if(l && strncmp(name, username, l))
+                       continue;
+               
+               name=scat3("~", name, "/");
+               
+               if(name==NULL){
+                       warn_err();
+                       continue;
+               }
+               
+               cp=REALLOC_N(*cp_ret, char*, n, n+1);
+               
+               if(cp==NULL){
+                       warn_err();
+                       free(name);
+                       if(*cp_ret!=NULL)
+                       free(*cp_ret);
+               }else{
+                       cp[n]=name;
+                       n++;
+                       *cp_ret=cp;
+               }
+       }
+       
+       endpwent();
+       
+       if(n==1){
+               name=**cp_ret;
+               name[strlen(name)-1]='\0';
+               pw=getpwnam(name+1);
+               if(pw!=NULL && pw->pw_dir!=NULL){
+                       name=scat(pw->pw_dir, "/");
+                       if(name!=NULL){
+                               free(**cp_ret);
+                               **cp_ret=name;
+                       }
+               }
+       }
+       
+       return n;
+}
+
+/* 
+ * ret: 0 not a home directory,
+ *             1 home directory (repath set)
+ *             2 someone's home directory
+ */
+static int tilde_complete(char *path, char **retpath)
+{
+       char *home;
+       char *p;
+       struct passwd *pw;
+       
+       if(*path!='~')
+               return 0;
+       
+       if(*(path+1)!='/' && *(path+1)!='\0'){
+               p=strchr(path, '/');
+               
+               if(p==NULL)
+                       return 2;
+               
+               *p='\0';
+               pw=getpwnam(path+1);
+               *p='/';
+               
+               if(pw==NULL)
+                       return 0;
+               
+               home=pw->pw_dir;
+       }else{
+               p=path+1;
+               home=getenv("HOME");
+       }
+       
+       if(home!=NULL){
+               if(*p=='\0')
+                       *retpath=scat3(home, p, "/");
+               else
+                       *retpath=scat(home, p);
+       }
+       
+       return (*retpath!=NULL);
+}
+
+
+/*
+ **  Return all possible completions.
+ */
+int do_complete_file(char *pathname, char ***avp, char **beg,
+                                        void *unused)
+{
+    char       *dir;
+    char       *file, *path=NULL, *tt;
+    int                ac=0, i;
+    
+    switch(tilde_complete(pathname, &path)){
+    case 0:
+               i=SplitPath(pathname, &dir, &file);
+               break;
+    case 2:
+               return complete_homedir(pathname+1, avp, beg);
+    default:
+               i=SplitPath(path, &dir, &file);
+    }
+    
+    if(i<0)
+               return 0;
+    
+    ac=FindMatches(dir, file, avp);
+    
+    DISPOSE(file);
+    
+    if(ac==0 && path!=NULL){
+               *avp=ALLOC(char*);
+               if(*avp==NULL)
+                       return 0;
+               **avp=path;
+                       return 1;
+    }else if(path!=NULL){
+               free(path);
+    }
+    
+    /* Identify directories with trailing / */
+    for(i=0; i<ac; i++){
+               path = NEW(char,strlen(dir)+strlen((*avp)[i])+3);
+               sprintf(path,"%s/%s",dir,(*avp)[i]);
+               if(el_is_directory(path)){
+                       tt = NEW(char,strlen((*avp)[i])+2);
+                       sprintf(tt,"%s/",(*avp)[i]);
+                       DISPOSE((*avp)[i]);
+                       (*avp)[i] = tt;
+               }
+               DISPOSE(path);
+    }
+    
+    *beg=dir;
+    
+    return ac;
+}
+
+
+/*
+ **  Return all possible completions.
+ */
+int do_complete_file_with_path(char *pathname, char ***avp, char **beg,
+                                                          void *unused)
+{
+    char       *dir;
+    char       *file, *path=NULL, *tt;
+    int                ac=0, i, dcomp=FALSE;
+    
+    switch(tilde_complete(pathname, &path)){
+    case 0:
+               i=SplitRelativePath(pathname, &dir, &file);
+               break;
+    case 2:
+               return complete_homedir(pathname+1, avp, beg);
+    default:
+               i=SplitPath(path, &dir, &file);
+    }
+    
+    if(i<0)
+               return 0;
+    
+    if(*dir=='\0')
+               ac=FindFullPathMatches(file, avp); /* No slashes in path so far. */
+       
+       if(ac==0){
+               if(*dir=='\0'){
+                       dir=scopy("./");
+                       if(dir==NULL){
+                               warn_err();
+                               return 0;
+                       }
+               }
+               ac=FindMatches(dir, file, avp);
+               dcomp=TRUE;
+       }
+    
+    DISPOSE(file);
+    
+    if(ac==0 && path!=NULL){
+               *avp=ALLOC(char*);
+               if(*avp==NULL)
+                       return 0;
+               **avp=path;
+                       return 1;
+    }else if(path!=NULL){
+               free(path);
+    }
+    
+    /* Identify directories with trailing / */
+       if(dcomp){
+           for (i = 0; i < ac; i++) {
+                       path = NEW(char,strlen(dir)+strlen((*avp)[i])+3);
+                       sprintf(path,"%s/%s",dir,(*avp)[i]);
+                       if (el_is_directory(path)) {
+                               tt = NEW(char,strlen((*avp)[i])+2);
+                               sprintf(tt,"%s/",(*avp)[i]);
+                               DISPOSE((*avp)[i]);
+                               (*avp)[i] = tt;
+                       }
+                       DISPOSE(path);
+           }
+       }
+    
+    *beg=dir;
+    
+    return ac;
+}
+
+
+
+int main(int argc, char *argv[])
+{
+       char **avp=NULL;
+       char *beg=NULL;
+       bool wp=FALSE;
+       int i, j, n;
+       
+       libtu_init(argv[0]);
+       
+       for(i=1; i<argc; i++){
+               if(strcmp(argv[i], "-h")==0){
+                       printf("Usage: ion-completefile [-help] [-wp] [to_complete...]\n");
+                       return EXIT_SUCCESS;
+               }
+       }
+       
+       for(i=1; i<argc; i++){
+               if(strcmp(argv[i], "-wp")==0){
+                       wp=TRUE;
+               }else{
+                       if(wp){
+                               n=do_complete_file_with_path(argv[i], &avp, &beg, NULL);
+                       }else{
+                               n=do_complete_file(argv[i], &avp, &beg, NULL);
+                       }
+                       
+                       if(beg){
+                               printf("%s\n", beg);
+                               free(beg);
+                               beg=NULL;
+                       }else{
+                               printf("\n");
+                       }
+       
+
+                       if(avp){
+                               for(j=0; j<n; j++){
+                                       printf("%s\n", avp[j]);
+                                       free(avp[j]);
+                               }
+                               free(avp);
+                               avp=NULL;
+                       }
+               }
+       }
+       
+       return EXIT_SUCCESS;
+}
diff --git a/utils/ion-completeman.in b/utils/ion-completeman.in
new file mode 100644 (file)
index 0000000..9f71f83
--- /dev/null
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+tocompl=""
+section=""
+beg=""
+action=""
+usercache=""
+syscache=""
+
+translate_grepsafe() { 
+    # The regexp below is supposed to be [\[\].*$^\\], but sed sucks
+    # and doesn't support simple and intuitive escaping and we have to
+    # do it the hard way with the collations [.[.] and [.].] standing 
+    # for [ and ], respectively.
+    sed 's:^[ \t]*\(.*\)[ \t]*$:\1:; s:[[.[.][.].].*$^\\]:\\&:g'
+}
+
+case "$1" in
+    -complete)
+        read section tocompl << EOF
+$2
+EOF
+        if test "$tocompl" = ""; then
+            tocompl="$section"
+            section=""
+        else
+            beg="$section "
+        fi
+        tocompl=`echo "$tocompl" | translate_grepsafe`
+        action="complete"
+        ;;
+    -mkusercache)
+        action="mkusercache"
+        ;;
+    -mksyscache)
+        action="mksyscache"
+        ;;
+esac
+   
+if test "x$action" = x; then
+    echo 2>&1 "Usage: $0 (-complete what|-mkusercache|-mksyscache)"
+    exit 1
+fi
+        
+
+filterpath() {
+    sed 's:^.*/\([^/]*\.[0-9].*\)$:\1:p; d'
+}
+
+filtersect() {
+    sed 's:^\(.*\)\.[0-9].*$:\1:p; d'
+}
+
+grepper() {
+    if test "$tocompl" = "" -a "$section" = ""; then
+        cat
+    else
+        if test "$section" = ""; then
+            section="[0-9]"
+        fi
+        grep "^$tocompl.*\.$section"
+    fi
+}
+
+scan() {
+    if test "x$ION_MANPATH" != "x"; then
+        mpath="$ION_MANPATH"
+    elif test "x$MANPATH" != "x"; then
+        mpath="$MANPATH"
+    else
+        mpprog=`which manpath`
+        if test "x$mpprog" = x; then
+            echo "Please set MANPATH, ION_MANPATH or put 'manpath' on PATH" > /dev/stderr
+            exit 1
+        fi
+        mpath=`$mpprog`
+    fi
+    
+    for p in `echo "$mpath"|tr : '\n'`; do
+        find  "$p" -type f -o -type l | filterpath
+    done 
+}
+
+
+cachefile=""
+
+if test "x$HOME" != x; then
+    usercache="$HOME/.ion3/mancache"
+fi
+
+syscache="@VARDIR@/mancache"
+
+case "$action" in
+    complete)
+        if test "x$usercache" != x -a -f "$usercache"; then
+            cachefile="$usercache"
+        fi
+        
+        if test -f "$syscache"; then
+            cachefile="$syscache"
+        fi
+        
+        # Empty "common part" of completions.
+        echo "$beg"
+        
+        if test "x$cachefile" != x; then
+            grepper < "$cachefile" | filtersect
+        else
+            scan | grepper | filtersect
+        fi
+        ;;
+    mkusercache)
+        if test "x$usercache" != x; then
+            scan > "$usercache"
+        else
+            echo >&2 "\$HOME not set."
+        fi
+        ;;
+    mksyscache)
+        mkdir -p "@VARDIR@"
+        scan > "$syscache"
+        ;;
+esac
diff --git a/utils/ion-runinxterm b/utils/ion-runinxterm
new file mode 100644 (file)
index 0000000..fc16dac
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test "$XTERMCMD" || XTERMCMD="xterm"
+
+title=""
+wait=""
+
+while test $# -ge 0; do
+    if test "$1" = "--"; then
+        shift
+        break
+    elif test "$1" = "-phase2"; then
+        shift
+        "$@" && test "$wait" = "" || {
+            echo "Press enter..."
+            read nothing
+        }
+        exit
+    elif test "$1" = "-T"; then
+       if test $# -lt 2; then
+            echo error
+            exit 0
+       fi
+       title="$2"
+       shift 2
+    elif test "$1" = "-w"; then
+       wait="$1"
+       shift
+    else
+        break
+    fi
+done
+
+if test $# -lt 1; then
+    echo error
+    exit 0
+fi
+
+if test "$title" = ""; then
+       title="$*"
+fi
+
+exec $XTERMCMD -T "$title" -e $0 $wait -phase2 "$@"
diff --git a/version.h b/version.h
new file mode 100644 (file)
index 0000000..e5a60c9
--- /dev/null
+++ b/version.h
@@ -0,0 +1,2 @@
+#define ION_VERSION "3ds-20061223"
+#define ION_API_VERSION "3-"ION_VERSION