Chapter 44. Window Managers and Window Information

The window manager controls the size and location of other applications' windows. The window manager is built into Windows and Macintosh, while it is a separate application on UNIX. The wm command provides an interface to the window manager. The winfo command returns information about windows. The tk command provides miscellaneous information about Tk and the windowing system.

Management of top-level windows is done by the window manager. The Macintosh and Windows platforms have the window manager built in to the operating system, but in UNIX the window manager is just another application. The window manager controls the position of top-level windows, provides a way to resize windows, open and close them, and implements a border and decorative title for windows. The wm command interacts with the window manager so that the application can control its size, position, and iconified state.

If you need to fine-tune your display, you may need some detailed information about widgets. The winfo command returns all sorts of information about windows, including interior widgets, not just top-level windows.

The wm Command

The wm command has about 20 operations that interact with the window manager. The general form of the command is:

wm operation win ?args?

In all cases the win argument must be a toplevel. Otherwise, an error is raised. In many cases, the operation either sets or queries a value. If a new value is not specified, then the current settings are returned. For example, this command returns the current window geometry:

wm geometry .
=> 300x200+327+20

This command defines a new geometry:

wm geometry . 400x200+0+0

There are lots of wm operations, and this reflects the complex protocol with UNIX window managers. The summary below lists the subset of operations that I find useful. The operations can be grouped into four main categories:

  • Size, placement and decoration of windowsUse the geometry and title operations to position windows and set the title bar.

  • IconsUse the iconify, deiconify, and withdraw operations to open and close windows. On UNIX, closed windows are represented by an icon.

  • Long-term session stateUse the protocol operation to get a callback when users destroy windows.

  • MiscellaneousUse the transient and overrideredirect operation to get specialized windows. There are platform-specific commands to select different styles of top-level windows.

Toplevel Size, Placement, and Decoration

Each window has a title that appears in the title bar that the window manager places above the window. In a wish script, the default title of the main window is the last component of the file name of the script. Use the wm title command to change the title of the window. The title can also appear in the icon for your window, unless you specify another name with wm iconname.

wm title . "My Application"

Use the wm geometry command to adjust the position or size of your main windows. A geometry specification has the general form WxH+X+Y, where W is the width, H is the height, and X and Y specify the location of the upper-left corner of the window. The location +0+0 is the upper-left corner of the display. You can specify a negative X or Y to position the bottom (right) side of the window relative to the bottom (right) side of the display. For example, +0-0 is the lower-left corner, and -100-100 is offset from the lower-right corner by 100 pixels in the X and Y direction. If you do not specify a geometry, then the current geometry is returned.

Example 44-1. Gridded geometry for a canvas

canvas .c -width 300 -height 150
pack .c -fill both -expand true
wm geometry .
=> 300x200+678+477
wm grid . 30 15 10 10
wm geometry .
=> 30x20+678+477

Example 44-1 sets up gridded geometry for a canvas, which means that the geometry is in terms of some unit other than pixels. With the canvas, use the wm grid command to define the size of the grid. The text and listbox widgets set a grid based on the size of the characters they display. They have a setgrid attribute that turns on gridding, which is described on page 642.

The wm resizable command controls whether a user can resize a window. The following command allows a resize in the X direction, but not in the Y direction:

wm resizable . 1 0

You can constrain the minimum size, maximum size, and the aspect ratio of a toplevel. The aspect ratio is the width divided by the height. The constraint is applied when the user resizes the window interactively. The minsize, maxsize, and aspect operations apply these constraints.

Some window managers insist on having the user position windows. The sizefrom and positionfrom operations let you pretend that the user specified the size and position in order to work around this restriction.

Table 44-1 summarizes the wm commands that deal with size, decorations, placement:

Table 44-1. Size, placement and decoration window manager operations

wm aspect win ?a b c d?

Constrains win's ratio of width to height to be between (a/b and c/d).

wm geometry win ?geometry?

Queries or sets the geometry of win.

wm grid win ?w h dx dy?

Queries or sets the grid size. w and h are the base size, in grid units. dx and dy are the size, in pixels, of a grid unit.

wm maxsize win ?width height?

Constrains the maximum size of win.

wm minsize win ?width height?

Constrains the minimum size of win.

wm positionfrom win ?who?

Queries or sets who to be program or user.

wm resizable win ?xok yok?

Queries or sets ability to resize interactively.

wm sizefrom win ?who?

Queries or sets who to be program or user.

wm stackorder win

Returns a list of toplevel windows in stacking order, from lowest to highest. (Tk 8.4)

wm stackorder win ?isabove|isbelow win?

Returns a boolean result indicating whether or not the first window is currently above or below the second window in the stacking order. (Tk 8.4)

wm title win ?string?

Queries or sets the window title to string.

The stackorder operation, introduced in Tk 8.4, returns information about the stacking order of the application's toplevel windows. Given the name of a toplevel, it returns a list of toplevel children windows in stacking order, from lowest to highest. Only those toplevels that are currently mapped to the screen are returned. The following command returns all mapped toplevels in their stacking order:

wm stackorder .

The stackorder operation can also be used to determine if one toplevel is positioned above or below a second toplevel. When two window arguments separated by either isabove or isbelow are passed, a boolean result indicates whether or not the first window is currently above or below the second window in the stacking order. For example:

wm stackorder . isabove .dialog

Icons

UNIX window managers let you close a window and replace it with an icon. The window still exists in your application, and users can open the window later. You can open and close a window yourself with the deiconify and iconify operations, respectively. Use the withdraw operation to unmap the window without replacing it with an icon. The state operation returns the current state, which is one of normal, iconified, or withdrawn. If you withdraw a window, you can restore it to the normal state with deiconify.

Windows and Macintosh do not implement icons for program windows. Instead, icons represent files and applications in the desktop environment. When you iconify under Windows, the window gets minimized and users can open it by clicking on the taskbar at the bottom of the screen. When you iconify under Macintosh, the window simply gets withdrawn from the screen.

As of Tk 8.3, Windows applications have an additional state, zoomed, which is a full-screen (or “maximized”) display mode. Future versions of Tk may support this state for other operating systems

You can set the attributes of UNIX icons with the iconname, iconposition, iconbitmap, and iconmask operations. The icon's mask is used to get irregularly shaped icons. Chapter 41 describes how masks and bitmaps are defined. In the case of an icon, it is most likely that you have the definition in a file, so your command will look like this:

wm iconbitmap . @myfilename

Starting with Tk 8.3.3, on Windows systems, you can provide the path of a valid Windows icon file (usually .ico or .icr files) when setting the window's icon with the wm iconbitmap command. And if you use the optional -default option, introduced in Tk 8.4, the specified bitmap is used as the default icon for all windows. However, when setting the icon bitmap under windows, remember that the argument you provide must be a filename without a leading “@”. For example:

wm iconbitmap . -default [file join $lib myapp.ico]

Table 44-2 summarizes the wm operations that have to do with icons:

Table 44-2. Window manager commands for icons

wm deiconify win

Opens the window win.

wm iconbitmap win ?bitmap?

Queries or defines the bitmap for the icon. UNIX.

wm iconbitmap win ?-default? file

Sets the icon's bitmap using the specified file. The -default option sets the bitmap as the default for all windows of the application. Windows. (Tk 8.3.3)

wm iconify win

Closes the window win.

wm iconmask win ?mask?

Queries or defines the mask for the icon. UNIX.

wm iconname win ?name?

Queries or sets the name on the icon. UNIX.

wm iconposition win ?x y?

Queries or sets the location of the icon. UNIX.

wm iconwindow win ?window?

Queries or specifies an alternate window to display when in the iconified state. UNIX.

wm state win ?state?

Returns normal, iconic, withdrawn or (Windows only) zoomed. If specified, the window is set to the new state.

wm withdraw win

Unmaps the window. No icon is displayed.

Application Session State

The window manager lets users delete windows with a close operation. When the main Tk window gets deleted, wish normally quits. If you have any special processing that must take place when the user deletes a window, you need to intercept the close action. Use the wm protocol operation to register a command that handles the WM_DELETE_WINDOW message from the window manager. This works on all platforms even though "delete" is a UNIX term and "close" is the Windows and Macintosh term:

wm protocol . WM_DELETE_WINDOW Quit

If you intercept close on the main Tk window (i.e., dot), you must eventually call exit to actually stop your application. However, you can also take the time to prompt the user about unsaved changes, or even let the user change their mind about quitting.

Other window manager messages that you can intercept are WM_SAVE_YOURSELF and WM_TAKE_FOCUS. The first is called periodically by some UNIX session managers, which are described below. The latter is used in the active focus model. Tk (and this book) assumes a passive focus model where the window manager assigns focus to a top-level window.

Note

Application Session State

Saving session state.

Some UNIX window managers support the notion of a session that lasts between runs of the window system. A session is implemented by saving state about the applications that are running, and using this information to restart the applications when the window system is restarted.

An easy way to participate in the session protocol is to save the command used to start your application. The wm command operation does this. The wish shell saves this information, so it is just a matter of registering it with the window manager. argv0 is the command, and argv is the command-line arguments:

wm command . [linsert $argv 0 $argv0]

If your application is typically run on a different host than the one with the display (like in an Xterminal environment), then you also need to record what host to run the application on. Use the wm client operation for this. You might need to use hostname instead of uname on your system:

wm client . [exec uname -n]

Table 44-3 describes the session-related window manager operations.

Table 44-3. Session-related window manager operations

wm client win ?name?

Records the hostname in the WM_CLIENT_MACHINE property. UNIX.

wm command win ?command?

Records the start-up command in the WM_COMMAND property. UNIX.

wm protocol win ?name? ?command?

Registers a command to handle the protocol request name, which can be WM_DELETE_WINDOW, WM_SAVE_YOURSELF, or WM_TAKE_FOCUS.

Miscellaneous Window Manager Operations

The UNIX window managers work by reparenting an application's window so that it is a child of the window that forms the border and decorative title bar. The wm frame operation returns the window ID of the new parent, or the ID of the window itself if it has not been reparented. The wm overrideredirect operation can set a bit that overrides the reparenting. This means that no title or border will be drawn around the window, and you cannot control the window through the window manager.

The wm group operation defines groups of windows so that the window manager can open and close them together. One window, typically the main window, is chosen as the leader. The other members of the group are iconified when it is iconified. This is not implemented on Windows and Macintosh, and not all UNIX window managers implement this, either.

The wm transient operation informs the window manager that this is a temporary window and there is no need to decorate it with the border and decorative title bar. This is used, for example, on pop-up menus. On Windows, a transient window is a toolbar window that does not appear in the task bar. On Macintosh, the tk::unsupported::MacWindowStyle command, which is described on page 489, lets you create different styles of top-level windows.

The wm attributes command, added in Tk 8.4, allows you to set or query platform-specific attributes associated with a specific window. The following Windows attributes are supported: -disabled gets or sets whether the window is in a disabled state; -toolwindow gets or sets the style of the window to toolwindow (as defined in the MSDN); and -topmost gets or sets whether this window is displayed above all other windows.

Table 44-4 lists the remaining window manager operations:

Table 44-4. Miscellaneous window manager operations

wm attributes win ?...?

Sets or queries platform-specific window attributes. (Tk 8.4)

wm colormapwindows win ?windowList?

Sets or queries the WM_COLORMAP_WINDOWS property that orders windows with different colormaps.

wm focusmodel win ?what?

Sets or queries the focus modelactive or passive. (Tk assumes the passive model.)

wm frame win

Returns the ID of the parent of win if it has been reparented; otherwise, returns the ID of win.

wm group win ?leader?

Queries or sets the group leader (a toplevel) for win. The window manager may unmap all the group at once.

wm overrideredirect win ?boolean?

Sets or queries the override redirect bit that suppresses reparenting by the window manager.

wm transient win ?leader?

Queries or marks a window as a transient window working for leader, another widget.

The winfo Command

The winfo command has about 50 operations that return information about a widget or the display. The operations fall into the following categories:

  • Sending commands between applications.

  • Family relationships.

  • Size.

  • Location.

  • Virtual root coordinates.

  • Atoms and IDs.

  • Colormaps and visuals.

Sending Commands between Applications

Each Tk application has a name that is used when sending commands between applications using the send command, which is described in Chapter 43. The list of Tk applications is returned by the interps operation. The tk appname command is used to get the name of the application, and that command can also be used to set the application name.

Example 44-2 shows how your application might connect up with several existing applications. It contacts each registered Tk interpreter and sends a short command that contains the application's own name as a parameter. The other application can use that name to communicate back.

Example 44-2. Telling other applications what your name is

foreach app [winfo interps] {
   catch {send $app [list Iam [tk appname]]}
}

Table 44-5 summarizes these commands:

Table 44-5. send command information

tk appname ?newname?

Queries or sets the name used with send.

winfo name .

Also returns the name used for send, for backward compatibility with Tk 3.6 and earlier.

winfo name pathname

Returns the last component of pathname.

winfo ?-displayof win? interps

Returns the list of registered Tk applications on the same display as win.

Widget Family Relationships

The Tk widgets are arranged in a hierarchy, and you can use the winfo command to find out about the structure of the hierarchy. The winfo children operation returns the children of a window, and the winfo parent operation returns the parent. The parent of the main window is null (i.e., an empty string).

A widget is also a member of a class, which is used for bindings and as a key into the resource database. The winfo class operation returns this information. You can test for the existence of a window with winfo exists, and whether or not a window is mapped onto the screen with winfo viewable. Note that winfo ismapped is true for a widget that is managed by a geometry manager, but if the widget's top-level window is not mapped, then the widget is not viewable.

The winfo manager operation tells you what geometry manager is controlling the placement of the window. This returns the name of the geometry manager command. Examples include pack, place, grid, canvas, and text. The last two indicate the widget is embedded into a canvas or text widget.

Table 44-6 summarizes these winfo operations:

Table 44-6. Window hierarchy information

winfo children win

Returns the list of children widgets of win.

winfo class win

Returns the resource class of win.

winfo exists win

Returns 1 if win exists.

winfo ismapped win

Returns 1 if win is mapped onto the screen.

winfo manager win

Geometry manager: pack, place, grid, canvas, or text.

winfo parent win

Returns the parent widget of win.

winfo viewable win

Returns 1 if win and all its parent windows are mapped.

Widget Size

The winfo width and winfo height operations return the width and height of a window, respectively. Alternatively, you can ask for the requested width and height of a window. Use winfo reqwidth and winfo reqheight for this information. The requested size may not be accurate, however, because the geometry manager may allocate more or less space, and the user may resize the window.

Note

Widget Size

Size is not valid until a window is mapped.

A window's size is not set until a geometry manager maps a window onto the display. Initially, a window starts out with a width and height of 1. You can use tkwait visibility to wait for a window to be mapped before asking its width or height, or you can use update to give Tk a chance to update the display. There are some potential problems with update that are discussed on page 608. Dialog_Wait in Example 39-1 on page 606 uses tkwait visibility.

The winfo geometry operation returns the size and position of the window in the standard geometry format: WxH+X+Y. In this case the X and Y offsets are relative to the parent widget, or relative to the root window in the case of the main window.

You can find out how big the display is, too. The winfo screenwidth and winfo screenheight operations return this information in pixels. The winfo screenmmwidth and winfo screenmmheight return this information in millimeters.

You can convert between pixels and screen distances with the winfo pixels and winfo fpixels operations. Given a number of screen units such as 10m, 3c, or 72p, these return the corresponding number of pixels. The first form rounds to a whole number, while the second form returns a floating point number. The correspondence between pixels and sizes may not be accurate because users can adjust the pixel size on their monitors, and Tk has no way of knowing about that. Chapter 40 explains screen units on page 612. For example:

set pixelsToInch [winfo pixels . 2.54c]

Table 44-7 summarizes these operations:

Table 44-7. Window size information

winfo fpixels win num

Converts num, in screen units, to pixels. Returns a floating point number.

winfo geometry win

Returns the geometry of win, in pixels and relative to the parent in the form WxH+X+Y

winfo height win

Returns the height of win, in pixels.

winfo pixels win num

Converts num to a whole number of pixels.

winfo reqheight win

Returns the requested height of win, in pixels.

winfo reqwidth win

Returns the requested width of win, in pixels.

winfo screenheight win

Returns the height of the screen, in pixels.

winfo screenmmheight win

Returns the height of the screen, in millimeters.

winfo screenmmwidth win

Returns the width of the screen, in millimeters.

winfo screenwidth win

Returns the width of the screen, in pixels.

winfo width win

Returns the width of win, in pixels.

Widget Location

Table 44-8. Window location information

winfo containing ?-displayof win? win x y

Returns the pathname of the window at x and y.

winfo pointerx win

Returns the X screen coordinate of the mouse.

winfo pointery win

Returns the Y screen coordinate of the mouse.

winfo pointerxy win

Returns the X and Y coordinates of the mouse.

winfo rootx win

Returns the X screen position of win.

winfo rooty win

Returns the Y screen position of win.

winfo screen win

Returns the display identifier of win's screen.

winfo server win

Returns the version string of the display server.

winfo toplevel win

Returns pathname of toplevel that contains win.

winfo x win

Returns the X position of win in its parent.

winfo y win

Returns the Y position of win in its parent.

The winfo x and winfo y operations return the position of the upper-left corner of a window relative to its parent widget. In the case of the main window, this is its location on the screen. The winfo rootx and winfo rooty return the screen location of the upper-left corner of a widget, even if it is not a toplevel.

The winfo containing operation returns the pathname of the window that contains a point on the screen. This is useful in implementing menus and drag-and-drop applications.

The winfo toplevel operation returns the pathname of the toplevel that contains a widget. If the window is itself a toplevel, then this operation returns its own pathname.

The winfo screen operation returns the display identifier for the screen of the window.

Virtual Root Window

Some window managers use a virtual root window to give the user a larger virtual screen. At any given time, only a portion of the virtual screen is visible, and the user can change the view on the virtual screen to bring different applications into view. In this case, the winfo x and winfo y operations return the coordinates of a main window in the virtual root window (i.e., not the screen).

The winfo vrootheight and winfo vrootwidth operations return the size of the virtual root window. If there is no virtual root window, then these just return the size of the screen.

Note

Virtual Root Windowvirtual root window

Correcting virtual root window coordinates.

The winfo vrootx and winfo vrooty are used to map from the coordinates in the virtual root window to screen-relative coordinates. These operations return 0 if there is no virtual root window. Otherwise, they return a negative number. If you add this number to the value returned by winfo x or winfo y, it gives the screen-relative coordinate of the window:

set screenx [expr [winfo x $win] + [winfo vrootx $win]]

Table 44-9 summarizes these operations:

Table 44-9. Virtual root window information

winfo vrootheight win

Returns the height of the virtual root window for win.

winfo vrootwidth win

Returns the width of the virtual root window for win.

winfo vrootx win

Returns the X position of win in the virtual root.

winfo vrooty win

Returns the Y position of win in the virtual root.

Atoms and IDs

An atom is an X technical term for an identifier that is registered with the X server. Applications map names into atoms, and the X server assigns each atom a 32-bit identifier that can be passed between applications. One of the few places this is used in Tk is when the selection mechanism is used to interface with different toolkits. In some cases the selection is returned as atoms, which appear as 32-bit integers. The winfo atomname operation converts that number into an atom (i.e., a string), and the winfo atom registers a string with the X server and returns the 32-bit identifier as a hexadecimal string

Each widget has an ID assigned by the window system. The winfo id command returns this identifier. The winfo pathname operation returns the Tk pathname of the widget that has a given ID, but only if the window is part of the same application.

Note

Atoms and IDs

Embedding applications.

The id operation is useful if you need to embed another application into your window hierarchy. Wish takes a -use id command-line argument that causes it to use an existing window for its main window. Other toolkits provide similar functionality. For example, to embed another Tk app in a frame:

frame .embed -container true
exec wish -use [winfo id .embed] otherscript.tcl

Table 44-10 summarizes these operations:

Table 44-10. Atom and window ID information

winfo atom ?-displayof win? name

Returns the 32-bit identifier for the atom name.

winfo atomname ?-displayof win? id

Returns the atom that corresponds to the 32-bit ID.

winfo id win

Returns the window ID of win.

winfo pathname ?-displayof win? id

Returns the Tk pathname of the window with id, or null.

Colormaps and Visuals

The winfo depth returns the number of bits used to represent the color in each pixel. The winfo cells command returns the number of colormap entries used by the visual class of a window. These two values are generally related. A window with 8 bits per pixel usually has 256 colormap cells. The winfo screendepth and winfo screencells return this information for the default visual class.

The winfo visualsavailable command returns a list of the visual classes and screen depths that are available. For example, a display with 8 bits per pixel might report the following visual classes are available:

winfo visualsavailable .
=> {staticgray 8} {grayscale 8} {staticcolor 8} 
     {pseudocolor 8}

The winfo visual operation returns the visual class of a window, and the winfo screenvisual returns the default visual class of the screen.

The winfo rgb operation converts from a color name or value to the red, green, and blue components of that color. Three decimal values are returned. Example 41-2 on page 624 uses this command to compute a slightly darker version of the same color.

Table 44-11 summarizes operations that return information about colormaps and visual classes, which are described in Chapter 41:

Table 44-11. Colormap and visual class information

winfo cells win

Returns the number of colormap cells in win's visual.

winfo colormapfull win

Returns 1 if the last color allocation failed.

winfo depth win

Returns the number of bits per pixel for win.

winfo rgb win color

Returns the red, green, and blue values for color.

winfo screencells win

Returns the number of colormap cells in the default visual.

winfo screendepth win

Returns the number of bits per pixel in the screen's default visual.

winfo screenvisual win

Returns the default visual of the screen.

winfo visual win

Returns the visual class of win.

winfo visualsavailable win

Returns a list of pairs that specify the visual type and bits per pixel of the available visual classes.

The tk Command

The tk command provides a few miscellaneous entry points into the Tk library.

The appname operation is used to set or query the application name used with the Tk send command. If you define a new name and it is already in use by another application, (perhaps another instance of yourself), then a number is appended to the name (e.g., #2, #3, and so on). This is the syntax of the command:

tk appname ?name?

Fonts, canvas items, and widget sizes use screen units that are pixels, points, centimeters, millimeters, or inches. There are 72 points per inch. The tk scaling command, which was added in Tk 8.0, is used to set or query the mapping between pixels and points. A scale of 1.0 results in 72 pixels per inch. A scale of 1.25 results in 90 pixels per inch. This gives accurate sizes on a 90 dpi screen or it makes everything 25% larger on a 72 dpi screen. Changing the scale only affects widgets created after the change. This is the syntax of the command:

tk scaling ?num?

Note

The tk Command

Determining the windowing system.

The tk windowingsystem command, added in Tk 8.4, returns one of x11 (X11-based), win32 (MS Windows), classic (Mac OS Classic), or aqua (Mac OS X Aqua). Traditionally, Tk applications that included platform-dependent code could simply switch on the value of the global tcl_platform(platform) element, which is set to macintosh, unix, or windows. But the introduction of Apple's OS X and the Aqua interface complicated matters. Mac OS X reports unix in the tcl_platform(platform) element. But its windowing system is not a native X Windows system, so you must use tk windowingsystem.

The caret operation, introduced in Tk 8.4, sets and queries the caret location for the display of the specified Tk window. The caret is the per-display cursor location used for indicating global focus (for example, to comply with Microsoft Accessibility guidelines), as well as for location of the over-the-spot XIM (X Input Methods) or Windows IME windows.

The useinputmethods operation changes the behavior of Tk on X with X Input Methods (XIM). Before Tk 8.3, XIM was recognized and used without question. As of Tk 8.3, they are recognized and initialized, but not used unless XIM is turned on with the useinputmethods operation:

tk useinputmethods 1

Table 44-12 summarizes the tk command operations:

Table 44-12. The tk command operations

tk appname ?name?

Queries or sets the application name, used by the Tk send command.

tk caret window ?-x x? ?-y y? ?-height height?

Queries or sets the caret location for the display of the specified Tk window. x and y represent window-relative coordinates. height is the height of the current cursor location, or the height of the specified window. Values are returned in option-value pair format. (Tk 8.4)

tk scaling ?-displayof window? ?number?

Queries or sets the current scaling factor used by Tk to convert between physical units and pixels. number is a floating point value that specifies the number of pixels per point on window's display. If window is omitted, it defaults to the main window. If number is omitted, the current value of the scaling factor is returned.

tk useinputmethods ?-displayof window? ?boolean?

Queries or sets the state of whether Tk should use XIM (X Input Methods) for filtering events. The resulting state is returned. If XIM support is not available, this will always return 0. If window is omitted, it defaults to the main window. If boolean is omitted, the current state is returned. (Tk 8.3)

tk windowingsystem

Returns the current windowing system: x11 (X11-based), win32 (MS Windows), classic (Mac OS Classic), or aqua (Mac OS X Aqua). (Tk 8.4)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.18.4