--- /dev/null
+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
+
--- /dev/null
+
+ 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!
+
+
--- /dev/null
+##
+## 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
--- /dev/null
+
+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.
--- /dev/null
+
+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.
--- /dev/null
+
+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
--- /dev/null
+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").
+
--- /dev/null
+
+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>
+
--- /dev/null
+
+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
+])
+
+
--- /dev/null
+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
--- /dev/null
+##
+## 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
--- /dev/null
+
+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
--- /dev/null
+--
+-- 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
+
+-- }}}
+
--- /dev/null
+
+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}
+};
+]])
+
--- /dev/null
+##
+## 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
--- /dev/null
+# 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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();
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+ }
+}
+
+
+/*}}}*/
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+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
+
--- /dev/null
+ 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.
--- /dev/null
+# 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 \
+ > $@
--- /dev/null
+
+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'.
--- /dev/null
+# 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;
+
--- /dev/null
+\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.
+
--- /dev/null
+\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}
--- /dev/null
+\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.
--- /dev/null
+
+\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}
--- /dev/null
+
+\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.
+
+
--- /dev/null
+\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.
--- /dev/null
+
+\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.
--- /dev/null
+\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}
+
+
--- /dev/null
+
+Context:
+
+[TAG ion-doc-3ds-20061223
+Tuomo Valkonen <tuomov@iki.fi>**20061223150012]
--- /dev/null
+\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}
--- /dev/null
+\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}
--- /dev/null
+\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.
--- /dev/null
+
+\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}
--- /dev/null
+\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}{}
--- /dev/null
+\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}
--- /dev/null
+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.
--- /dev/null
+\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
--- /dev/null
+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).
--- /dev/null
+# LaTeX2HTML 2002-2-1 (1.71)
+# Associate images original text with physical files.
+
+
+1;
+
--- /dev/null
+\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}
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html16"
+ HREF="node1.html">Contents</A></B>
+ <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 © 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>
--- /dev/null
+# 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;
+
--- /dev/null
+/* 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 }
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html16"
+ HREF="node1.html">Contents</A></B>
+ <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 © 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>
--- /dev/null
+# 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;
+
--- /dev/null
+<!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>
+ <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>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html422"
+ HREF="node1.html">Contents</A></B>
+ <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>
+ <B> <A NAME="tex2html422"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html424"
+ HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <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>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html446"
+ HREF="node1.html">Contents</A></B>
+ <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 © 1993, 1994, 1995, 1996,
+Nikos Drakos,
+Computer Based Learning Unit, University of Leeds.
+<BR>
+Copyright © 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>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html223"
+ HREF="node1.html">Contents</A></B>
+ <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>
+ <B> <A NAME="tex2html223"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html225"
+ HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html237"
+ HREF="node1.html">Contents</A></B>
+ <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
+ |-->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)
+</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
+ |-->WScreens
+ |-->WGroupWSs
+ |-->WTilings
+ |-->WClientWins in full screen mode
+ |-->WFrames
+ |-->WGroupCWs
+ |-->WClientWins
+ |-->WFrames for transients
+ |-->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
+ |-->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
+</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>
+ <B> <A NAME="tex2html237"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html239"
+ HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html258"
+ HREF="node1.html">Contents</A></B>
+ <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> 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("[^:]+: (.*)(<[0-9]+>)", "$1$2$|$1$<...$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<3>' to 'barba...<3>'; 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&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"> </TD>
+<TD ALIGN="LEFT"> </TD>
+<TD ALIGN="LEFT"> </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>
+ <B> <A NAME="tex2html258"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html260"
+ HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html294"
+ HREF="node1.html">Contents</A></B>
+ <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"> </TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tab-frame-tiled-alt</TT>,</TD>
+<TD ALIGN="LEFT"> </TD>
+</TR>
+<TR><TD ALIGN="LEFT"><TT>tab-frame-floating</TT>,</TD>
+<TD ALIGN="LEFT"> </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>
+ <B> <A NAME="tex2html294"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html296"
+ HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html325"
+ HREF="node1.html">Contents</A></B>
+ <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>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>
+ <B> <A NAME="tex2html325"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html327"
+ HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html345"
+ HREF="node1.html">Contents</A></B>
+ <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">$<</TD>
+<TD ALIGN="LEFT">Remove characters on the left of this marker to shorten the
+ string.</TD>
+</TR>
+<TR><TD ALIGN="LEFT">$></TD>
+<TD ALIGN="LEFT">Remove characters on the right of this marker to shorten the
+ string. Only the first $< or $> 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><n></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><n></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&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>
+ <B> <A NAME="tex2html345"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html347"
+ HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html393"
+ HREF="node1.html">Contents</A></B>
+ <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 © 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>
+ <B> <A NAME="tex2html393"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html395"
+ HREF="node11.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html408"
+ HREF="node1.html">Contents</A></B>
+ <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
+ |-->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)
+</PRE>
+
+<P>
+<BR><HR>
+
+</BODY>
+</HTML>
--- /dev/null
+\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}{}
--- /dev/null
+\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}
--- /dev/null
+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.
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html8"
+ HREF="node1.html">Contents</A></B>
+ <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 © 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>
--- /dev/null
+# 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;
+
--- /dev/null
+/* 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 { }
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html8"
+ HREF="node1.html">Contents</A></B>
+ <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 © 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>
--- /dev/null
+# 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;
+
--- /dev/null
+<!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>
+ <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>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html79"
+ HREF="node1.html">Contents</A></B>
+ <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
+ |-->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)
+</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
+ |-->WScreens
+ |-->WGroupWSs
+ |-->WTilings
+ |-->WClientWins in full screen mode
+ |-->WFrames
+ |-->WGroupCWs
+ |-->WClientWins
+ |-->WFrames for transients
+ |-->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
+ |-->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
+</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>
+ <B> <A NAME="tex2html79"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html81"
+ HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html98"
+ HREF="node1.html">Contents</A></B>
+ <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>
+ <B> <A NAME="tex2html98"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html100"
+ HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html112"
+ HREF="node1.html">Contents</A></B>
+ <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"> </TD>
+</TR>
+<TR><TD ALIGN="RIGHT">b</TD>
+<TD ALIGN="LEFT">bool</TD>
+<TD ALIGN="LEFT"> </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"> </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>
+ <B> <A NAME="tex2html112"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html114"
+ HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html130"
+ HREF="node1.html">Contents</A></B>
+ <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>
+ <B> <A NAME="tex2html130"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html132"
+ HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html146"
+ HREF="node1.html">Contents</A></B>
+ <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>&&</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 && c+d==e){
+ ...
+ }
+}
+</PRE>
+
+<P>
+has correct style while the block
+
+<P>
+<PRE>
+void foo(int a,int b) {
+ if (a == b && 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>
+ <B> <A NAME="tex2html146"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html148"
+ HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html164"
+ HREF="node1.html">Contents</A></B>
+ <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 © 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>
+ <B> <A NAME="tex2html164"
+ HREF="node1.html">Contents</A></B>
+ <B> <A NAME="tex2html166"
+ HREF="node8.html">Index</A></B> </DIV>
+<!--End of Navigation Panel-->
+
+</BODY>
+</HTML>
--- /dev/null
+<!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>
+ <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>
--- /dev/null
+<!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>
+ <B> <A NAME="tex2html189"
+ HREF="node1.html">Contents</A></B>
+ <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 © 1993, 1994, 1995, 1996,
+Nikos Drakos,
+Computer Based Learning Unit, University of Leeds.
+<BR>
+Copyright © 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>
--- /dev/null
+
+\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*}.)
+
--- /dev/null
+\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}
--- /dev/null
+
+\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}
+
--- /dev/null
+\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.
--- /dev/null
+#!/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
--- /dev/null
+
+\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}
--- /dev/null
+# 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;
+
--- /dev/null
+
+\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}
--- /dev/null
+
+\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}
+
--- /dev/null
+##
+## 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
--- /dev/null
+--
+-- 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
+
--- /dev/null
+--
+-- 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)
--- /dev/null
+--
+-- 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)"),
+})
+
--- /dev/null
+--
+-- 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$<...")
--- /dev/null
+--
+-- 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(_)"),
+})
+
--- /dev/null
+--
+-- 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")
--- /dev/null
+--
+-- 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",
+ },
+}
+--]]
--- /dev/null
+--
+-- 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,
+}
+--]]
--- /dev/null
+--
+-- 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={},
+ },
+}
+
--- /dev/null
+--
+-- 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
+ }
+ }
+ }
+}
+
--- /dev/null
+-- 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()
+
--- /dev/null
+-- 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()
+
--- /dev/null
+-- 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()
+
--- /dev/null
+--
+-- 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()
--- /dev/null
+-- 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()
+
--- /dev/null
+-- 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()
+
--- /dev/null
+-- 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()
+
--- /dev/null
+-- 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()
+
--- /dev/null
+-- 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"
+})
--- /dev/null
+-- 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",
+})
--- /dev/null
+
+Context:
+
+[TAG ion-3ds-20061223
+Tuomo Valkonen <tuomov@iki.fi>**20061223145904]
--- /dev/null
+#!/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
--- /dev/null
+##
+## 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+##
+## 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
--- /dev/null
+/*
+ * 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®ION_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®ION_ACTIVITY);
+}
+
+
+bool region_is_activity_r(WRegion *reg)
+{
+ return (reg->flags®ION_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;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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**)®)){
+ 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;
+}
+
+
+/*}}}*/
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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, ¶m.geom.w, ¶m.geom.h,
+ FALSE);
+ }
+
+ if(!handle_target_prop(cwin, ¶m)){
+ bool managed;
+ void *mrshpm[2];
+
+ mrshpm[0]=cwin;
+ mrshpm[1]=¶m;
+
+ 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, ¶m));
+ 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®ION_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);
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+ }
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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];
+}
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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();
+ }
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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));
+}
+
+
+/*}}}*/
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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();
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
+
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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®ION_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;
+}
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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(®ION_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));
+ }
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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®ION_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®ION_RQGEOM_WEAK_X)
+ rq2.geom.x+=off.x;
+ else
+ rq2.geom.x+=xgravity_deltax(gravity, -off.x, off.x+off.w);
+
+ if(rq->flags®ION_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);
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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.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.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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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=®ION_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);
+}
+
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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, ¶m);
+ }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, ¶m);
+
+ 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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", >)){
+ 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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®ION_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®ION_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, ¶m->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, ®ION_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, ¶m->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®ION_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®ION_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®ION_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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_FIT_WHATEVER)){
+ /* Comm. hack */
+ param.geom_set=TRUE;
+ }
+
+ reg=region_attach_helper((WRegion*)cwg, par, fp,
+ (WRegionDoAttachFn*)grouped_do_attach_final,
+ ¶m, 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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, ®ION_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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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(®ION_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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+--
+-- 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
+
+
--- /dev/null
+--
+-- 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),
+ })
+})
--- /dev/null
+--
+-- 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
--- /dev/null
+--
+-- 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
+
--- /dev/null
+--
+-- 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()
+
--- /dev/null
+--
+-- 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
+--]]
--- /dev/null
+--
+-- 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()
+
--- /dev/null
+--
+-- 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
+
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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);
+ }
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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(®ION_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;
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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®ION_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®ION_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(¶m->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, ¶m, &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(®ION_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", ®ION_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(®ION_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=∥
+ 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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));
+}
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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);
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+ }
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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));
+}
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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®ION_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®ION_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®ION_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®ION_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®ION_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®ION_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®ION_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);
+ }
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
+
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_GOTO_FOCUS)
+ region_maybewarp(res.reg, !(res.flags®ION_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(®ION_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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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®ION_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®ION_ACTIVE)
+#define REGION_IS_TAGGED(R) (((WRegion*)(R))->flags®ION_TAGGED)
+#define REGION_IS_URGENT(R) (((WRegion*)(R))->flags®ION_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 */
--- /dev/null
+/*
+ * 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®ION_RQGEOM_ABSOLUTE)
+ region_managed_rqgeom_absolute(mgr, reg, rq, geomret);
+ else
+ region_managed_rqgeom(mgr, reg, rq, geomret);
+ }else{
+ WRectangle tmp;
+
+ if(rq->flags®ION_RQGEOM_ABSOLUTE)
+ region_absolute_geom_to_parent(reg, &rq->geom, &tmp);
+ else
+ tmp=rq->geom;
+
+ if(geomret!=NULL)
+ *geomret=tmp;
+
+ if(!(rq->flags®ION_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®ION_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;
+ }
+}
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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, ®ION_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);
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
+
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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(®ION_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, ¶m,
+ (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);
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_RQGEOM_WEAK_W) &&
+ (*szplcy&SIZEPOLICY_HORIZ_MASK)==SIZEPOLICY_HORIZ_CENTER);
+ bool fullh=((rq_flags®ION_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®ION_RQGEOM_WEAK_X)
+ && rq_flags®ION_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®ION_RQGEOM_WEAK_Y)
+ && rq_flags®ION_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®ION_RQGEOM_WEAK_X)
+ && rq_flags®ION_RQGEOM_WEAK_W){
+ fp->g.x=x_;
+ }else if(rq_flags®ION_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®ION_RQGEOM_WEAK_Y)
+ && rq_flags®ION_RQGEOM_WEAK_H){
+ fp->g.y=y_;
+ }else if(rq_flags®ION_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®ION_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;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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®ION_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);
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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("");
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_TAGGED);
+ bool nset=libtu_do_setparam(sp, set);
+
+ if(XOR(nset, set)){
+ if(reg->flags®ION_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®ION_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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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*)®)!=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);
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+
+ 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!
+
+
--- /dev/null
+##
+## 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
--- /dev/null
+
+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).
--- /dev/null
+TOPDIR := $(TOPDIR)/..
+include $(TOPDIR)/build/system-inc.mk
--- /dev/null
+
+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]
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#!/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
--- /dev/null
+#!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
+
+-- }}}
+
--- /dev/null
+/*
+ * 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, ¶m);
+ 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, ¶ms);
+}
+
+
+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, ¶ms);
+
+ 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, ¶ms);
+}
+
+
+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, ¶ms);
+}
+
+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, ¶m);
+}
+
+
+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, ¶m);
+}
+
+
+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, ¶m);
+}
+
+
+/*}}}*/
+
+
+/*{{{ 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=¶m;
+
+#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(¶m);
+
+#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,
+ ®data)){
+ 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,
+ ®data);
+ }
+}
+
+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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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, ¶m,
+ 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,
+ ¶m, 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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:
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
+
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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)
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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(¤t_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
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+
+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!
+
+
--- /dev/null
+##
+## 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
--- /dev/null
+
+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.
+
--- /dev/null
+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.
--- /dev/null
+TOPDIR := $(TOPDIR)/..
+include $(TOPDIR)/build/system-inc.mk
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+
+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]
--- /dev/null
+#!/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
--- /dev/null
+/*
+ * 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;
+ }
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+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);
+}
--- /dev/null
+/*
+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);
+}
+
+
--- /dev/null
+/*
+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 */
--- /dev/null
+/*
+ * 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;
+ }
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+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.
--- /dev/null
+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
--- /dev/null
+# 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
--- /dev/null
+
+ 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!
--- /dev/null
+<!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>
+<<a href="mailto:mark.martinec@ijs.si">mark.martinec@ijs.si</a>>,
+April 1999, June 2000
+<br>Copyright © 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: '-', '+', ' ', '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 <mark.martinec@ijs.si>
+<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 <edwiny@autonomy.com> 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 <mark.martinec@ijs.si>
+<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án McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
+</ul>
+
+<dt>2000-06-27 V2.1 Mark Martinec <mark.martinec@ijs.si>
+<dd><ul>
+<li>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;
+<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án McNamara
+</ul>
+
+<dt>2000-10-06 V2.2 Mark Martinec <mark.martinec@ijs.si>
+<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 <= 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;
+<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> -> <i>conversion specifier</i>,
+ <i>C9x</i> -> <i>ISO/IEC 9899:1999 ("ISO C99")</i>,
+ <i>alternative form</i> -> <i>alternate form</i>,
+ <i>data type modifier</i> -> <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
+<<a href="mailto:panos@alumni.cs.colorado.edu">panos@alumni.cs.colorado.edu</a>> 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
+<<a href="mailto:papowell@sdsu.edu">papowell@sdsu.edu</a>>,
+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
+<<a href="mailto:blong@fiction.net">blong@fiction.net</a>>
+<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á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
+<<a href="mailto:mcr@metis.milkyway.com">mcr@metis.milkyway.com</a>>
+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
+<<a href="mailto:ttsalo@ssh.fi">ttsalo@ssh.fi</a>>
+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>
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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));
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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
--- /dev/null
+.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>.
--- /dev/null
+.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
--- /dev/null
+.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>.
--- /dev/null
+.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>.
--- /dev/null
+.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>.
--- /dev/null
+.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
--- /dev/null
+.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>.
--- /dev/null
+.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>.
--- /dev/null
+
+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 ----
+
+
--- /dev/null
+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.
--- /dev/null
+
+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 ----
+
+
--- /dev/null
+
+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 ----
+
+
--- /dev/null
+ 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!
+
+
--- /dev/null
+##
+## 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)
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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®ION_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®ION_RQGEOM_TRYONLY);
+ if(dockapp->reg==reg){
+ thisdockapp=dockapp;
+ if(flags®ION_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®ION_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, ®ION_GEOM(dock), NULL, NULL);
+
+ if(thisdockapp!=NULL && geomret!=NULL)
+ *geomret=thisdockapp->geom;
+ }else{
+ if(thisdockapp!=NULL && geomret!=NULL){
+ dock_arrange_dockapps(dock, ®ION_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=®ION_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);
+
+
+/*}}}*/
+
--- /dev/null
+##
+## 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
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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®ION_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 =
+ ®ION_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®ION_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=®ION_GEOM(p);
+
+ while(menu!=NULL){
+ diff=maxof(diff, calc_diff(®ION_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);
+
+
+/*}}}*/
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+--
+-- 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)
--- /dev/null
+##
+## 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
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+--
+-- 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)
+
--- /dev/null
+/*
+ * 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),
+ ®ION_GEOM(ws),
+ p.layout);
+ extl_unref_table(p.layout);
+ }
+
+ if(ws->tiling.split_tree==NULL)
+ ws->tiling.split_tree=(WSplit*)create_splitunused(®ION_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®ION_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**)®)){
+ 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), ®ION_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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+--
+-- 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)
--- /dev/null
+--
+-- 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)
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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®ION_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®ION_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);
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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);
+
+
+/*}}}*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+ }
+}
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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
--- /dev/null
+--
+-- 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')"),
+})
+
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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
--- /dev/null
+/*
+ * 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);
+}
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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)
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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);
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+--
+-- 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)
--- /dev/null
+--
+-- 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()
+
+
--- /dev/null
+--
+-- 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()
+
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+--
+-- 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
--- /dev/null
+/*
+ * 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, ®ION_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®ION_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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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
+
+
+
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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(®ION_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);
+}
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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, ¶m,
+ (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);
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_RQGEOM_WEAK_X;
+ bool vany=flags®ION_RQGEOM_WEAK_Y;
+ bool tryonly=flags®ION_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®ION_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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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®ION_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®ION_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=®ION_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(®ION_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, ®ION_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);
+
+
+/*}}}*/
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+##
+## 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)
--- /dev/null
+##
+## 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)
--- /dev/null
+
+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/`.
--- /dev/null
+#
+# 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"
--- /dev/null
+#
+# 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"
--- /dev/null
+# -*- 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"
--- /dev/null
+# 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 "óÐÉÓÏË"
+
--- /dev/null
+##
+## 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
--- /dev/null
+--
+-- 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"),
+})
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+##
+## 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
--- /dev/null
+##
+## 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) $@
--- /dev/null
+##
+## 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#!/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
--- /dev/null
+#!/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 "$@"
--- /dev/null
+#define ION_VERSION "3ds-20061223"
+#define ION_API_VERSION "3-"ION_VERSION