/* motifEmulator.c
 *
 * Copyright (C) 1993-2002 Paul Boersma
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * pb 2002/03/03
 * pb 2002/03/07 GPL
 * pb 2002/05/12
 */
#ifndef UNIX

/* The Motif emulator for Macintosh and Windows. */

#define PRAAT_WINDOW_CLASS_NUMBER  1
#define SCROLL32  1
#define motif_mac_controlFont  (kControlUsesOwningWindowsFontVariant)

#if defined (macintosh)
	#define mac 1
	#define win 0
	/* Temporary, till textfield control code does not access the TEHandle anymore: */
	#define TEXTFIELD_IS_CONTROL  1
#else
	#define mac 0
	#define win 1
#endif

#if mac
	#include "macport_on.h"
	#include <Quickdraw.h>
	#include <AppleEvents.h>
	#include <Controls.h>
	#include <ControlDefinitions.h>
	#include <MacWindows.h>
	#include <TextEdit.h>
	#include <Lists.h>
	#include <Menus.h>
	#include <Fonts.h>
	#include <Dialogs.h>
	#include <Balloons.h>
	#include <Scrap.h>
	#include <ToolUtils.h>
	#include <Devices.h>
	#if ! TARGET_API_MAC_CARBON
		#include <DiskInit.h>
	#endif
	#include <Gestalt.h>
	#include <Sound.h>   /* SysBeep */
	#ifdef __MACH__
		#include <AEMach.h>
		extern OSErr AEProcessAppleEvent (EventRecord *event);
		extern OSErr AEInteractWithUser (SInt32 timeOutInTicks, void *nmReqPtr, void *idleProc);
	#endif
	#include "macport_off.h"
#elif win
	#include <windows.h>
	#include <commctrl.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "melder.h"
#include "motif.h"
#if mac
	#if TARGET_API_MAC_CARBON
		#define carbon 1
		/* The following line is an optimization. The compiler will throw away most non-appearance code. */
		#define haveAppearance 1
	#else
		#define carbon 0
		/* The following line should be commented out unless you want to debug the non-appearance version. */
		/*** #define haveAppearance 0 ***/
		#define GetQDGlobalsScreenBits(bits)  (*(bits) = qd.screenBits, bits)
		#define GetPortBounds(port,dum)  (& (port) -> portRect)
		#define GetRegionBounds(rgn,rect)  (*(rect) = (**(rgn)). rgnBBox, rect)
		#define InvalWindowRect(win,rect)  InvalRect (rect)
		#define ValidWindowRect(win,rect)  ValidRect (rect)
		#define IsControlVisible(control)  ((**(control)). contrlVis)
		#define EnableMenuItem  EnableItem
		#define DisableMenuItem  DisableItem
	#endif
	#include "macport_on.h"
#endif
/*#define MEMBER(widget,klas)  (widget -> widgetClass == xm##klas##WidgetClass)
#define MEMBER2(widget,klas1,klas2)  (MEMBER (widget, klas1) || MEMBER (widget, klas2))
#define MEMBER3(widget,klas1,klas2,klas3)  (MEMBER2 (widget, klas1, klas2) || MEMBER (widget, klas3))
#define MEMBER4(widget,klas1,klas2,klas3,klas4)  (MEMBER3 (widget, klas1, klas2, klas3) || MEMBER (widget, klas4))
#define MEMBER5(widget,klas1,klas2,klas3,klas4,klas5)  (MEMBER4 (widget, klas1, klas2, klas3, klas4) || MEMBER (widget, klas5))*/
#define MEMBER(widget,klas)  ((widget -> widgetClass & xm##klas##WidgetClass) != 0)
#define MEMBER2(widget,klas1,klas2)  ((widget -> widgetClass & (xm##klas1##WidgetClass | xm##klas2##WidgetClass)) != 0)
#define MEMBER3(widget,klas1,klas2,klas3)  ((widget -> widgetClass & (xm##klas1##WidgetClass | xm##klas2##WidgetClass | xm##klas3##WidgetClass)) != 0)
#define MEMBER4(widget,klas1,klas2,klas3,klas4)  ((widget -> widgetClass & (xm##klas1##WidgetClass | xm##klas2##WidgetClass | xm##klas3##WidgetClass | xm##klas4##WidgetClass)) != 0)
#define MEMBER5(widget,klas1,klas2,klas3,klas4,klas5)  ((widget -> widgetClass & (xm##klas1##WidgetClass | xm##klas2##WidgetClass | xm##klas3##WidgetClass | xm##klas4##WidgetClass | xm##klas5##WidgetClass)) != 0)
/*
 * The above macros only work if klas1 etc are no macros themselves.
 * Therefore, we undefine those who are:
 */
#if win
	#undef MessageBox
#endif

#define _motif_SHIFT_MASK  1
#define _motif_COMMAND_MASK  2
#define _motif_OPTION_MASK  4

#if mac
	#ifndef haveAppearance
		static int haveAppearance;
	#endif
	static void motif_mac_textFont (void) {
		static short font;
		if (! font) GetFNum ("\pMonaco", & font);
		TextFont (font);
		TextSize (9);
		TextFace (0);
	}
	static void motif_mac_menuTitleFont (void) {
		TextFont (systemFont);
		TextSize (0);
		TextFace (0);
	}
	void motif_mac_defaultFont (void) {
		TextFont (systemFont);
		TextSize (12);
		TextFace (0);
	}
	static BitMap theScreenBits;
#endif

/* The following variable controls the focus policy of Mac Motif.
 * The Macintosh focus policy is: there is only one active window at a time,
 * and a click on an inactive window causes that window to become active,
 * without responding any further to that click.
 * This is appropriate for small screens, where multiple windows are usually stacked
 * on top of one another, and only a small part of background windows is visible.
 * The Motif focus policy is: all windows are active, and a click in any window causes
 * that window to respond as if it were in front.
 * This is appropriate for large screens, where multiple window are often tiled about the screen.
 * One of the purposes of Mac Motif is to produce Macintosh look-and-feel,
 * which would require the Macintosh focus policy,
 * but as Macintosh screens are growing larger, you might prefer the faster Motif focus policy,
 * if your screen is large.
 * Therefore, you can choose:
 */
static const int BACKGROUND_WINDOWS_ARE_ACTIVE = 0;   /* 0 = Mac, 1 = Motif. */

#if win
	#define CELL_HEIGHT  15
	#define MESSAGE_BOX_BUTTON_WIDTH  100
#elif mac
	static int USE_QUESTION_MARK_HELP_MENU = 0;
	#define CELL_HEIGHT  16
	#define MESSAGE_BOX_BUTTON_WIDTH  120
#endif

#if win
	static void _motif_update (Widget me, void *event);
#elif mac
	static void _motif_update (Widget me, EventRecord *event);
#endif

/********** Resource names. **********/

#define motif_RESOURCE(xxx)  #xxx,
static const char *motif_resourceNames [] = {
	"XmNnull",
	#include "motifEmulator_resources.h"
	"XmNend"
};
#undef motif_RESOURCE

/********** XWindows routines. **********/

void XMapRaised (int displayDummy, Window window) {
	(void) displayDummy;
	#if win
		ShowWindow ((HWND) window, SW_SHOW);
		SetForegroundWindow ((HWND) window);
	#elif mac
		ShowWindow ((WindowPtr) window);
		SelectWindow ((WindowPtr) window);
	#endif
}

int WidthOfScreen (int screen) {
	(void) screen;
	#if win
		return GetSystemMetrics (SM_CXMAXIMIZED);
	#elif mac
		return GetQDGlobalsScreenBits (& theScreenBits) -> bounds.right;
	#endif
}
int HeightOfScreen (int screen) {
	(void) screen;
	#if win
		return GetSystemMetrics (SM_CYMAXIMIZED);
	#elif mac
		return GetQDGlobalsScreenBits (& theScreenBits) -> bounds.bottom - 22;
	#endif
}

/********** X Toolkit **********/

#define MAXNUM_CALLBACKS  3   /* E.g., maximum number of activate callbacks for a PushButton. */

typedef struct XtCallbackList {
	struct { XtCallbackProc proc; XtPointer closure; } pairs [MAXNUM_CALLBACKS];
} XtCallbackList;

static void xt_callCallbacks (Widget w, XtCallbackList *callbacks, XtPointer call) {
	int i; for (i = 0; i < MAXNUM_CALLBACKS; i ++)
		if (callbacks -> pairs [i]. proc) callbacks -> pairs [i]. proc (w, callbacks -> pairs [i]. closure, call);
}

struct Widget {
	unsigned long widgetClass;
	struct Widget *parent, *previousSibling, *nextSibling, *firstChild;
	char *name;
	char inMenu, nativized, managed, insensitive;
	Widget activeTextWidget;   /* For shells. */
	Widget shell;   /* My shell ancestor. */
	Widget focusWidget;   /* For forms that contain text widgets as well as drawing areas. */

	/* Native. */

	#if win
		HWND window;
		union {
			/* XtShell: */
			struct { Widget menuBar; int uniqueItemID, duringMoveWindow; } shell;

			/* XmPushButton (if in menu), XmToggleButton (if in menu), XmCascadeButton (if in menu): */
			struct { HMENU handle; int id; } entry;

			/* XmMenuBar, XmPulldownMenu: */
			struct { HMENU handle; int id; } menu;
		} nat;
	#elif mac
		Rect rect;   /* Window coordinates. */
		WindowPtr macWindow;
		int isControl;
		#if TEXTFIELD_IS_CONTROL
		struct
		#else
		union
		#endif
		{
			/* XtShell: */
			struct { WindowPtr ptr; ControlRef rootControl; } window;

			/* XmPushButton (if not in menu), XmToggleButton (if not in menu), XmScrollBar, XmLabel (sometimes): */
			struct { ControlHandle handle; } control;

			/* XmPushButton (if in menu), XmToggleButton (if in menu), XmCascadeButton (if in menu): */
			struct { MenuHandle handle; int item; } entry;

			/* XmPulldownMenu: */
			struct { MenuHandle handle; int item /* if cascading */; int id; } menu;

			/* XmText: */
			struct { TEHandle handle; } text;

			/* XmList: */
			struct { ListHandle handle; } list;
		} nat;
	#endif

	/* Motif */

	union {
		struct { Widget horizontalBar, verticalBar, clipWindow, workWindow; } scrolledWindow;
		struct { XtCallbackList moveCallbacks; } drawingArea;
		struct { int editable; XtCallbackList motionVerifyCallbacks, valueChangedCallbacks; } text;
		struct { int active, isDialog;
			unsigned long lowAccelerators [8]; XtCallbackProc goAwayCallback; XtPointer goAwayClosure; } shell;
		struct { Widget okButton, cancelButton, helpButton; XtCallbackList okCallbacks, cancelCallbacks, helpCallbacks; } messageBox;
		struct { unsigned char acceleratorChar; int acceleratorModifiers; } pushButton;
		struct { int pushButtonLook; } cascadeButton;
		struct { int indicatorType; XtCallbackList valueChangedCallbacks; } toggleButton;
		struct { XtCallbackProc defaultActionCallback; XtPointer defaultActionClosure; } list;
		struct { XtCallbackList valueChangedCallbacks, dragCallbacks; } scrollBar;
	} motif;

	/* Resources. */

	int x, y, width, height;
	int isRadioButton;   /* For radio buttons and check buttons. */
	int visibleItemCount, selectionPolicy;   /* For lists. */
	int radioBehavior, packing, rowColumnType;   /* For row-columns. */
	int orientation;   /* For row-columns and scroll bars. */
	int alignment;   /* For labels. */
	Widget defaultButton, cancelButton;   /* For forms and shells. */
	int dialogStyle;   /* For forms and shells. */
	int dialogType;   /* For message boxes and shells. */
	Widget messageText;   /* For message boxes. */
	int autoUnmanage;   /* For bulletin boards. */
	Widget subMenuId, popUpButton;   /* For cascade buttons and their menus. */
	long increment, pageIncrement, sliderSize;   /* For scroll bars. */
	long minimum, maximum, value;   /* For scales and scroll bars. */

	XtCallbackProc activateCallback, destroyCallback, exposeCallback,
		extendedSelectionCallback, inputCallback, resizeCallback;
	XtPointer activateClosure, destroyClosure, exposeClosure,
		extendedSelectionClosure, inputClosure, resizeClosure;
	int leftAttachment, rightAttachment, topAttachment, bottomAttachment;
	int leftOffset, rightOffset, topOffset, bottomOffset;
	int deleteResponse;   /* For shells. */
};

#define my  me ->

/* Scratch. */

#if mac
	static Str255 mac_text;
#endif

/* Convenience. */

#if mac
	#define PfromCstr(p,c) p [0] = strlen (c), strcpy ((char *) p + 1, c);
#endif

/* When dispatching events to widgets, we must translate from the identifier of a Macintosh
 * screen object (Window, Control, menu item) to a Widget.
 * Mac windows and controls have RefCon fields in their WindowRecord or ControlRecord,
 * so we put a reference to the widget in the appropriate RefCon field at window or control creation time.
 * Instead of RefCons, the menus are remembered here:
 */
static Widget theMenus [256];   /* We can freely use and reuse the menu ids 1..255 */
static int (*theOpenDocumentCallback) (MelderFile file);
static int (*theQuitApplicationCallback) (void);
#if win
	static HINSTANCE theInstance;   /* First argument of WinMain. */
	static int theCommandShow = False;   /* Last argument of WinMain. */
	static char theApplicationName [100], theWindowClassName [100], theDrawingAreaClassName [100], theApplicationClassName [100];
	static int (*theUserMessageCallback) (void);
#elif mac
	static Widget theMenuBar;   /* There is only one menu bar on the Macintosh. */
	static int theHelpMenuOffset;   /* The number of items in the global help menu before we add the first item. */
	static int (*theUserMessageCallback) (char *message);
#endif

static Widget theApplicationShell;   /* For global menus. */
static Widget theShells [1000];   /* For XmUpdateDisplay and suspend events. */
static int theBackground = False;   /* Set by suspend and resume events; used by Motif-style activation methods. */
static int theDialogHint = False;   /* Should the shell that is currently being created, have dialog or document looks? */
long numberOfWidgets;

static void _motif_addShell (Widget me) {
	int i;
	for (i = 0; i < 1000; i ++)
		if (theShells [i] == NULL) { theShells [i] = me; break; }
}
static void _motif_removeShell (Widget me) {
	int i;
	for (i = 0; i < 1000; i ++)
		if (theShells [i] == me) { theShells [i] = NULL; break; }
}

/* Modes. */

#if mac
	static WindowPtr _motif_modalDialog = NULL;
#endif
static Widget _motif_activeTextWidget = NULL;
static int _motif_duringUpdate = False;

/* For 'automatic' unmanaging. */

static void cb_unmanage (Widget me, XtPointer closure, XtPointer call) {
	(void) closure;
	(void) call;
	XtUnmanageChild (my shell);
}

static void cb_messageBox_ok (Widget okButton, XtPointer closure, XtPointer call) {
	Widget me = (Widget) closure;
	(void) okButton;
	xt_callCallbacks (me, & my motif.messageBox.okCallbacks, call);
	if (my autoUnmanage) XtUnmanageChild (me);
}

static void cb_messageBox_cancel (Widget cancelButton, XtPointer closure, XtPointer call) {
	Widget me = (Widget) closure;
	(void) cancelButton;
	xt_callCallbacks (me, & my motif.messageBox.cancelCallbacks, call);
	if (my autoUnmanage) XtUnmanageChild (me);
}

static void cb_messageBox_help (Widget helpButton, XtPointer closure, XtPointer call) {
	Widget me = (Widget) closure;
	(void) helpButton;
	xt_callCallbacks (me, & my motif.messageBox.helpCallbacks, call);
}

/* AppContext level */

static int theNumberOfWorkProcs;
static XtWorkProc theWorkProcs [10];
static XtPointer theWorkProcClosures [10];

static int theNumberOfTimeOuts;
static XtTimerCallbackProc theTimeOutProcs [10];
static XtPointer theTimeOutClosures [10];
#ifdef __MACH__
	static EventLoopTimerRef theTimers [10];
#else
	static clock_t theTimeOutStarts [10];
	static unsigned long theTimeOutIntervals [10];
#endif

static void Native_move (Widget w, int dx, int dy);   /* Forward. */

static void cb_scroll (Widget scrollBar, XtPointer closure, XtPointer call) {
	Widget scrolledWindow = (Widget) closure;
	Widget workWindow = scrolledWindow -> motif.scrolledWindow.workWindow;
	int previousShift, newShift, distance;
	int horizontal = scrolledWindow -> motif.scrolledWindow.horizontalBar == scrollBar;
	(void) call;
	if (! workWindow) return;
	Melder_assert (scrolledWindow -> motif.scrolledWindow.clipWindow != NULL);
	#if win
		previousShift = horizontal ?
			scrolledWindow -> motif.scrolledWindow.clipWindow -> x - workWindow -> x :
			scrolledWindow -> motif.scrolledWindow.clipWindow -> y - workWindow -> y;
	#elif mac
		previousShift = horizontal ?
			scrolledWindow -> motif.scrolledWindow.clipWindow -> rect. left - workWindow -> rect. left :
			scrolledWindow -> motif.scrolledWindow.clipWindow -> rect. top - workWindow -> rect. top;   /* different? */
	#endif
	newShift = scrollBar -> value;
	distance = previousShift - newShift;
	if (horizontal)
		{ workWindow -> x += distance; Native_move (workWindow, distance, 0); }
	else
		{ workWindow -> y += distance; Native_move (workWindow, 0, distance); }
	XmUpdateDisplay (NULL);   /* If dragging. */
}

/* These are like the toolkit's query_geometry methods: */

static int Native_titleWidth (Widget me) {
	#if win
		if (my parent -> window) {
			HDC dc = GetDC (my parent -> window);
			SIZE size;
			SelectFont (dc, GetStockFont (ANSI_VAR_FONT));   /* Possible BUG. */
			GetTextExtentPoint32 (dc, my name, strlen (my name), & size);
			ReleaseDC (my parent -> window, dc);
			return size. cx;
		} else return 7 * strlen (my name);
	#elif mac
		int width;
		SetPortWindowPort (my macWindow);
		motif_mac_defaultFont ();
		width = TextWidth ((Ptr) my name, 0, strlen (my name));
		motif_mac_defaultFont ();
		return width;
	#endif
}

static int NativeLabel_preferredWidth (Widget me) {
	return Native_titleWidth (me) + 10;
}

static int NativeLabel_preferredHeight (Widget me) {
	(void) me;
	return win ? 17 : 18;
}

static int NativeButton_preferredWidth (Widget me) {
	return Native_titleWidth (me) + ( win ? 10 : 28 );
}

static int NativeToggleButton_preferredWidth (Widget me) {
	return Native_titleWidth (me) + 25;
}

static int NativeToggleButton_preferredHeight (Widget me) {
	(void) me;
	return win ? 17 : 20;
}

static int NativeButton_preferredHeight (Widget me) {
	(void) me;
	return win ? 22 : 20;
}

static int NativeText_preferredHeight (Widget me) {
	(void) me;
	return win ? 17 : 22;
}

/***** WIDGET *****/

static Widget initializeWidget (int widgetClass, Widget parent, const char *name) {
	Widget me = (Widget) Melder_calloc (1, sizeof (struct Widget));
	numberOfWidgets ++;
	my widgetClass = widgetClass;
	my parent = parent;
	my inMenu = parent && MEMBER (parent, PulldownMenu);

	/*
	 * Install me into my parent's list of children.
	 */
	if (parent == NULL) {
		;
	} else if (parent -> firstChild == NULL) {
		parent -> firstChild = me;
	} else {
		Widget lastChild = parent -> firstChild;
		while (lastChild -> nextSibling) lastChild = lastChild -> nextSibling;
		lastChild -> nextSibling = me;
		my previousSibling = lastChild;
	}
	/*
	 * Copy the name into my name.
	 */
	my name = Melder_strdup (name);
	/*
	 * I am in the same shell as my parent, so I inherit my parent's "shell" attribute.
	 */
	if (MEMBER (me, Shell)) {
		my shell = me;
	} else {
		my shell = my parent -> shell;
		#if mac
			/*
			 * I am in the same shell as my parent, so I'll inherit my parent's Macintosh WindowRef.
			 */
			my macWindow = parent -> macWindow;
		#endif
	}

	/* Initial defaults: mainly positioning and sizes. */

	switch (my widgetClass) {
		case xmDrawingAreaWidgetClass: {
			#if win
				my x = 2;
				my y = 2;
				my width = 100;
				my height = 100;
			#endif
		} break; case xmShellWidgetClass: {
			#if win
				my x = 20;
				my y = 3;
				my width = 30;
				my height = 50;
			#elif mac
				my x = 20;
				my y = 30;
				my width = 10;
				my height = 10;
			#endif
			my deleteResponse = XmDESTROY;
		} break; case xmTextWidgetClass: {
			my x = 2;
			my y = 2;
			my width = 102;
			my height = NativeText_preferredHeight (me);
		} break; case xmPushButtonWidgetClass: {
			my x = 2;
			my y = 2;
			my width = NativeButton_preferredWidth (me);
			my height = NativeButton_preferredHeight (me);
		} break; case xmLabelWidgetClass: {
			if (my parent -> rowColumnType == XmMENU_BAR) {   /* Cascade button? */
				char *hyphen = strstr (my name, " -");
				if (/*hyphen*/my parent -> y != 0 || my parent -> parent -> widgetClass == xmRowColumnWidgetClass) {   /* Push-button look required? */
					my motif.cascadeButton.pushButtonLook = 1;
					if (hyphen) hyphen [2] = '\0';   /* Chop any trailing spaces. */
					my x = 2;
					my y = 2;
					my width = NativeButton_preferredWidth (me);
					my height = NativeButton_preferredHeight (me) + 4;   /* BUG: menu bar should be large enough. */
				} else {
					my x = 10;   /* First item */
					my y = 2;
					#if win
						my width = NativeLabel_preferredWidth (me);
					#else
						motif_mac_menuTitleFont ();
						my width = TextWidth ((Ptr) my name, 0, strlen (my name)) + 12;
						motif_mac_defaultFont ();
					#endif
					my height = 18;
				}
			} else {   /* Plain label. */
				my x = 2;
				my y = 2;
				my width = NativeLabel_preferredWidth (me);
				my height = NativeLabel_preferredHeight (me);
			}
			my alignment = XmALIGNMENT_BEGINNING;
		} break; case xmToggleButtonWidgetClass: {
			my x = 2;
			my y = 2;
			my width = NativeToggleButton_preferredWidth (me);
			my height = NativeToggleButton_preferredHeight (me);
		} break; case xmSeparatorWidgetClass: {
			my width = parent -> width;
			my height = 10;
		} break; case xmScrollBarWidgetClass: {
			my width = 16;
			my height = 100;
		} break; case xmMenuBarWidgetClass: {
			my width = 10;
			my height = 10;
		} break; case xmRowColumnWidgetClass: {
			my width = 10;
			my height = 10;
			my orientation = XmVERTICAL;
		} break; case xmScaleWidgetClass: {
			my width = 300;
			my height = win ? 25 : 40;
		} break; case xmFormWidgetClass: {
			if (MEMBER (parent, Shell)) {
				/*
				 * The following trick is necessary for forms that contain scroll bars.
				 */
				my width = parent -> width;
				my height = parent -> height;
			} else {
				/*
				 * EXPERIMENT:
				 * this relies on a certain sequence in building up a window: from top to bottom.
				 * If the form is inside a RowColumn of a certain width established by a widget
				 * closer to the top, this width will be copied. The height, of course, should not be copied.
				 * See Praat's SoundRecorder for an example.
				 */
				my width = parent -> width;
				my height = 10;
			}
		} break; default: {
			my width = parent -> width;
			my height = parent -> height;
		}
	}

	/* Some positions depend on parent. */

	if (parent) switch (parent -> widgetClass) {
		case xmFrameWidgetClass: {
			my x = 1;
			my y = 1;
			my width = parent -> width - 2;
			my height = parent -> height - 2;
		} break; case xmScrolledWindowWidgetClass: {
			my x = 1;
			my y = 1;
			my width = parent -> width - 17;   /* Exact fit: scroll bar (16) plus border (1). */
			my height = parent -> height - 17;
			if (my widgetClass == xmTextWidgetClass) { my width = 3000; my height = 30000; }
		}
	}

	if (my width < 0) my width = 0;
	if (my height < 0) my height = 0;

	#if mac
		/* Determine enclosing rectangle in macwindow co-ordinates. */
		if (! MEMBER (me, Shell) && ! MEMBER (my parent, Shell)) {
			my rect.left = parent -> rect.left + my x;
			my rect.top = parent -> rect.top + my y;
		}
		my rect.right = my rect.left + my width;
		my rect.bottom = my rect.top + my height;
	#endif

	/* Automatic attachment of dialog to parent shell. */

	if (MEMBER3 (me, BulletinBoard, Form, MessageBox) && MEMBER (my parent, Shell))
		my leftAttachment = my rightAttachment = my topAttachment = my bottomAttachment = XmATTACH_FORM;

	if (MEMBER (me, CascadeButton) && strequ (name, "Help"))
		my rightAttachment = XmATTACH_FORM;   /* !!!!!! */

	/* A child of a scrolled window will be installed as the workWindow of that scrolled window,
	 * except if it is a scroll bar or if the clipWindow does not exist yet.
	 * This is because the creations of the scroll bars and the clip window will also arrive here.
	 * Our XmScrolledWindow creation method always creates two scroll bars and a clip window,
	 * before you can create any other children.
	 */

	if (my parent && MEMBER (my parent, ScrolledWindow) &&
		! MEMBER (me, ScrollBar) &&   /* 'me' is one of the two scroll bars, or a new one. */
		my parent -> motif.scrolledWindow.clipWindow != NULL)   /* 'me' is probably the clip window now. */
			my parent -> motif.scrolledWindow.workWindow = me;   /* Install. */
	return me;
}

/********** CLIPPING AND VIEW VALIDATION ROUTINES **********/

#if mac
	/* These routines use Mac position information only (i.e., the 'rect' attribute).
	 * They do not use Motif position information (i.e., the 'x', 'y', 'width', and 'height' attributes.
	 */

	/* The largest possible rect on a Mac, used for "no clip". */
	static Rect _motif_wideRect = { -32768, -32768, 32767, 32767 };
	static Rect _motif_clipRect;

	static void _motif_clipOn (Widget me) {
		/* The 'clipRect' will be the intersection of the rects of all its ancestors,
		 * stopping before a shell and making side steps at scrolled windows.
		 */
		Widget parent;
		Rect clipRect = _motif_wideRect;
		for (parent = my parent; ! MEMBER (parent, Shell); parent = parent -> parent) {
			Rect *parentRect = & parent -> rect;
			if (MEMBER (parent, ScrolledWindow)) {
				if (MEMBER (me, ScrollBar)) {
					parentRect = & my rect;
				} else {
					if (parent -> motif.scrolledWindow.clipWindow == NULL) return;   /* During destruction. */
					parentRect = & parent -> motif.scrolledWindow.clipWindow -> rect;
				}
			}
			if (parentRect -> left > clipRect. left) clipRect. left = parentRect -> left;
			if (parentRect -> right < clipRect. right) clipRect. right = parentRect -> right;
			if (parentRect -> top > clipRect. top) clipRect. top = parentRect -> top;
			if (parentRect -> bottom < clipRect. bottom) clipRect. bottom = parentRect -> bottom;
		}
		#if carbon
			/*if (MEMBER (me, PushButton) && me == my shell -> defaultButton)
				clipRect. left -= 3, clipRect. right += 3, clipRect. top -= 3, clipRect. bottom += 3;*/
		#endif
		SetPortWindowPort (my macWindow);
		ClipRect (& clipRect);
		_motif_clipRect = clipRect;
	}

	void motif_mac_clipOff (void) { ClipRect (& _motif_wideRect); }

	static void _motif_clipOffValid (Widget me) {
		Rect clipRect = _motif_clipRect;
		if (my rect. left > clipRect. left) clipRect. left = my rect. left;
		if (my rect. right < clipRect. right) clipRect. right = my rect. right;
		if (my rect. top > clipRect. top) clipRect. top = my rect. top;
		if (my rect. bottom < clipRect. bottom) clipRect. bottom = my rect. bottom;
		SetPortWindowPort (my macWindow);
		ValidWindowRect (my macWindow, & clipRect);
		ClipRect (& _motif_wideRect);
	}

	static void _motif_clipOffInvalid (Widget me) {
		Rect clipRect = _motif_clipRect;
		if (my rect. left > clipRect. left) clipRect. left = my rect. left;
		if (my rect. right < clipRect. right) clipRect. right = my rect. right;
		if (my rect. top > clipRect. top) clipRect. top = my rect. top;
		if (my rect. bottom < clipRect. bottom) clipRect. bottom = my rect. bottom;
		SetPortWindowPort (my macWindow);
		InvalWindowRect (my macWindow, & clipRect);
		ClipRect (& _motif_wideRect);
	}

	void motif_mac_clipOn (Widget me) {
		Widget parent;
		Rect clipRect = my rect;
		Melder_assert (my widgetClass == xmDrawingAreaWidgetClass);
		/* InsetRect (& clipRect, my marginWidth, my marginHeight); */
		for (parent = my parent; ! MEMBER (parent, Shell); parent = parent -> parent) {
			Rect *parentRect = & parent -> rect;
			if (MEMBER (parent, ScrolledWindow)) {
				if (MEMBER (me, ScrollBar)) {
					parentRect = & my rect;
				} else {
					Melder_assert (parent -> motif.scrolledWindow.clipWindow != NULL);
					parentRect = & parent -> motif.scrolledWindow.clipWindow -> rect;
				}
			}
			if (parentRect -> left > clipRect. left) clipRect. left = parentRect -> left;
			if (parentRect -> right < clipRect. right) clipRect. right = parentRect -> right;
			if (parentRect -> top > clipRect. top) clipRect. top = parentRect -> top;
			if (parentRect -> bottom < clipRect. bottom) clipRect. bottom = parentRect -> bottom;
		}
		SetPortWindowPort (my macWindow);
		ClipRect (& clipRect);
	}
#endif

/***** NATIVE *****/

static void NativeControl_check (Widget me, Boolean value) {
	#if win
		Button_SetCheck (my window, value ? BST_CHECKED : BST_UNCHECKED);
	#elif mac
		SetControlValue (my nat.control.handle, value);
	#endif
}

static void NativeControl_destroy (Widget me) {
	#if win
		DestroyWindow (my window);
	#elif mac
		_motif_clipOn (me);
		DisposeControl (my nat.control.handle);
		motif_mac_clipOff ();
	#endif
}

static void NativeControl_hide (Widget me) {
	#if win
		ShowWindow (my window, SW_HIDE);
	#elif mac
		_motif_clipOn (me);
		HideControl (my nat.control.handle);
		_motif_clipOffValid (me);
	#endif
}

static void NativeControl_setSensitive (Widget me) {
	#if win
		EnableWindow (my window, ! my insensitive);
	#elif mac
		_motif_clipOn (me);
		HiliteControl (my nat.control.handle, my insensitive ? 255 : 0);
		motif_mac_clipOff ();
	#endif
}

static char * motif_win_expandAmpersands (const char *title) {
	static char buffer [300];
	const char *from = title;
	char *to = & buffer [0];
	while (*from) { if (*from == '&') * to ++ = '&'; * to ++ = * from ++; } * to = '\0';
	return buffer;
}

static void NativeControl_setTitle (Widget me) {
	#if win
		HDC dc = GetDC (my window);
		SelectPen (dc, GetStockBrush (NULL_PEN));
		SelectBrush (dc, GetStockBrush (LTGRAY_BRUSH));
		Rectangle (dc, 0, 0, my width, my height);
		SetWindowText (my window, motif_win_expandAmpersands (my name));
	#elif mac
		Melder_assert (my nat.control.handle);
		if (haveAppearance && my widgetClass == xmLabelWidgetClass && my isControl && ! my motif.cascadeButton.pushButtonLook) {
			SetControlData (my nat.control.handle, kControlEntireControl, kControlStaticTextTextTag, strlen (my name), my name);
			/* SetControlData does not redraw the control. */
			if (IsControlVisible (my nat.control.handle))
				Draw1Control (my nat.control.handle);
		} else {
			PfromCstr (mac_text, my name);
			SetControlTitle (my nat.control.handle, mac_text);
		}
	#endif
}

static void NativeControl_show (Widget me) {
	#if win
		ShowWindow (my window, SW_SHOW);
	#elif mac
		_motif_clipOn (me);
		if (IsControlVisible (my nat.control.handle))
			Draw1Control (my nat.control.handle);
		else
			ShowControl (my nat.control.handle);
		_motif_clipOffValid (me);
	#endif
}

static int _XmScrollBar_check (Widget me) {
	if (my maximum < my minimum)
		Melder_warning ("XmScrollBar: maximum (%d) less than minimum (%d).", my maximum, my minimum);
	else if (my sliderSize > my maximum - my minimum)
		Melder_warning ("XmScrollBar: slider size (%d) greater than maximum (%d) minus minimum (%d).",
			my sliderSize, my maximum, my minimum);
	else if (my value < my minimum)
		Melder_warning ("XmScrollBar: value (%d) less than minimum (%d).", my value, my minimum);
	else if (my value > my maximum - my sliderSize)
		Melder_warning ("XmScrollBar: value (%d) greater than maximum (%d) minus slider size (%d).",
			my value, my maximum, my sliderSize);
	else return 1;
	return 0;
}

static void NativeScrollBar_set (Widget me) {
	if (! _XmScrollBar_check (me)) return;
	#if win
	{
		SCROLLINFO scrollInfo;
		scrollInfo. cbSize = sizeof (SCROLLINFO);
		scrollInfo. fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
		#if SCROLL32
			if (my maximum == my minimum + my sliderSize) {
				scrollInfo. nMin = 0;
				scrollInfo. nMax = 1;
				scrollInfo. nPage = 1;
				scrollInfo. nPos = 0;
			} else {
				scrollInfo. nMin = 0;
				scrollInfo. nMax = 32767;
				scrollInfo. nPage = (32767.0 * my sliderSize) / (my maximum - my minimum);
				scrollInfo. nPos = ((32767.0 - scrollInfo. nPage) * (my value - my minimum)) / (my maximum - my minimum - my sliderSize);
			}
		#else
			scrollInfo. nMin = my minimum;
			scrollInfo. nMax = my maximum;
			scrollInfo. nPage = my sliderSize;
			scrollInfo. nPos = my value;
		#endif
		if (my window) SetScrollInfo (my window, SB_CTL, & scrollInfo, TRUE);
	}
	#elif mac
		if (! my nat.control.handle) return;
		#if SCROLL32
			if (my maximum == my minimum + my sliderSize) {
				SetControlMinimum (my nat.control.handle, 0);
				SetControlValue (my nat.control.handle, 0);
				SetControlMaximum (my nat.control.handle, 0);
			} else {
				SetControlMinimum (my nat.control.handle, 0);
				SetControlMaximum (my nat.control.handle, 32767);
				SetControlValue (my nat.control.handle, my value == my minimum ? 0 :
					(32767.0 * (my value - my minimum)) / (my maximum - my minimum - my sliderSize));
			}
		#else
			SetControlMinimum (my nat.control.handle, my minimum);
			SetControlMaximum (my nat.control.handle, my maximum - my sliderSize);
			SetControlValue (my nat.control.handle, my value);
		#endif
	#endif
}

static void NativeMenuItem_delete (Widget me) {
	#if win
		RemoveMenu (my nat.entry.handle, my nat.entry.id, MF_BYCOMMAND);
	#elif mac
		Widget subview;
		DeleteMenuItem (my nat.entry.handle, my nat.entry.item);
		for (subview = my parent -> firstChild; subview; subview = subview -> nextSibling) {
			if (subview -> managed && ! MEMBER (subview, PulldownMenu) &&
				subview -> nat.entry.item > my nat.entry.item)
					subview -> nat.entry.item -= 1;
		}
		my nat.entry.item = 0;
	#endif
}

static int NativeMenuItem_getPosition (Widget me) {
	int position = 1;
	#if win
		/*
		 * The following routine could also be used for Mac.
		 */
		Widget sibling;
		for (sibling = my parent -> firstChild; sibling; sibling = sibling -> nextSibling) {
			if (sibling == me) break;
			if (sibling -> managed && ! MEMBER (sibling, PulldownMenu))
				position += 1;
		}
		/*
		 * Bill Gates counts like 0, 1, 2...
		 */
		position -= 1;
	#elif mac
		/*
		 * A Macintosh menu item shall know its "item number",
		 * which is its dynamic position in the current shape of the menu.
		 * We can use this item number to make changes and
		 * it will be sent to us by MenuSelect () while we handle a mouse-down message in the menu.
		 * (Regrettably, this item number has to change whenever items are unmanaged
		 *  or destroyed elsewhere in the menu.)
		 * Basically, the item number is one higher than the item number of the previous sibling.
		 *
		 * "incremental" means that all previous sibling items must have valid item numbers.
		 */
		Widget prev = my previousSibling;
		/*
		 * However, there may also be sibling pulldown menus. These have no item number (the associated cascade buttons have).
		 * Also, unmanaged items are not in the native menu structure, so these have no item number either.
		 */
		while (prev && (MEMBER (prev, PulldownMenu) || ! prev -> managed))
			prev = prev -> previousSibling;
		if (prev) {   /* Is there a previous managed non-pulldown sibling? */
			position = prev -> nat.entry.item + 1;   /* Then I must be the next item in the native menu structure. */
		} else {
			/*
			 * I must be the first item in the native menu structure, as far as the application is concerned.
			 * However, if the menu is the system's help menu (the question mark in System 7),
			 * the menu already contains a button called "Show balloon help".
			 */
			if (my parent -> nat.menu.id == kHMHelpMenuID) {   /* Is this the system help menu? */
				if (theHelpMenuOffset == 0)   /* Haven't we asked yet for the length of the system help menu? */
					theHelpMenuOffset = CountMenuItems (my nat.entry.handle);   /* So ask now. */
				position = theHelpMenuOffset + 1;   /* Then I must be the next item in the native menu structure. */
			} else {   /* This must be a menu of our own. */
				position = 1;   /* I must be the first item in the native menu structure. */
			}
		}
	#endif
	return position;
}

static void NativeMenuItem_check (Widget me, Boolean value) {
	#if win
		if (! my managed) return;
		CheckMenuItem (my nat.entry.handle, my nat.entry.id, MF_BYCOMMAND | ( value ? MF_CHECKED : MF_UNCHECKED ));
	#elif mac
		CheckMenuItem (my nat.entry.handle, my nat.entry.item, value);
	#endif
}

static void NativeMenuItem_setSensitive (Widget me) {
	#if win
		if (! my managed) return;
		EnableMenuItem (my nat.entry.handle, my nat.entry.id, MF_BYCOMMAND | ( my insensitive ? MF_GRAYED : MF_ENABLED ));
	#elif mac
		if (! my nat.entry.item) return;
		if (my insensitive) DisableMenuItem (my nat.entry.handle, my nat.entry.item);
		else EnableMenuItem (my nat.entry.handle, my nat.entry.item);
	#endif
}

static void NativeMenuItem_setText (Widget me) {
	int acc = my motif.pushButton.acceleratorChar, modifiers = my motif.pushButton.acceleratorModifiers;
	char title [300];
	#if win
		if (acc == 0) {
			strcpy (title, motif_win_expandAmpersands (my name));
		} else {
			static const char *keyStrings [256] = {
				0, "<-", "->", "Up", "Down", "PAUSE", "Del", "Ins", "Backspace", "Tab", "LineFeed", "Home", "End", "Enter", "PageUp", "PageDown",
				"Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", 0, 0, 0,
				"Space", "!", "\"", "#", "$", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/",
				"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
				"@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
				"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_",
				"`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
				"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "Del",
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "[", "]", ",", "?", ".", "\\",
				";", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-", "`", "=", "\'", 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
			const char *keyString = keyStrings [acc] ? keyStrings [acc] : "???";
			sprintf (title, "%s\t%s%s%s%s", motif_win_expandAmpersands (my name),
				modifiers & _motif_COMMAND_MASK ? "Ctrl-" : "",
				modifiers & _motif_OPTION_MASK ? "Alt-" : "",
				modifiers & _motif_SHIFT_MASK ? "Shift-" : "", keyString);
		}
		ModifyMenu (my nat.entry.handle, my nat.entry.id, MF_BYCOMMAND | MF_STRING, my nat.entry.id, title);
	#elif mac
		if (haveAppearance/*&&carbon*/) {   /* BUG */
			static int theGlyphs [1+31] = { 0,
				kMenuLeftArrowDashedGlyph, kMenuRightArrowDashedGlyph, kMenuUpArrowDashedGlyph, kMenuDownwardArrowDashedGlyph, 0,
				kMenuDeleteRightGlyph, 0, kMenuDeleteLeftGlyph, kMenuTabRightGlyph, 0,
				0, 0, kMenuReturnGlyph, kMenuPageUpGlyph, kMenuPageDownGlyph,
				kMenuEscapeGlyph, kMenuF1Glyph, kMenuF2Glyph, kMenuF3Glyph, kMenuF4Glyph,
				kMenuF5Glyph, kMenuF6Glyph, kMenuF7Glyph, kMenuF8Glyph, kMenuF9Glyph,
				kMenuF10Glyph, kMenuF11Glyph, kMenuF12Glyph, 0, 0,
				0 };
			(void) title;
			PfromCstr (mac_text, my name);
			SetMenuItemText (my nat.entry.handle, my nat.entry.item, mac_text);
			if (acc > 32) {
				SetItemCmd (my nat.entry.handle, my nat.entry.item, acc);
			} else {
				Melder_assert (acc > 0 && acc < 32);
				SetItemCmd (my nat.entry.handle, my nat.entry.item, ' ');   /* Funny that this should be needed. */
				SetMenuItemKeyGlyph (my nat.entry.handle, my nat.entry.item, theGlyphs [acc]);
			}
			SetMenuItemModifiers (my nat.entry.handle, my nat.entry.item,
				( modifiers & _motif_OPTION_MASK ? kMenuOptionModifier : 0 ) +
				( modifiers & _motif_SHIFT_MASK ? kMenuShiftModifier : 0 ) +
				( modifiers & _motif_COMMAND_MASK ? 0 : kMenuNoCommandModifier ));
		} else {
			if (acc == 0) {
				PfromCstr (mac_text, my name);
				SetMenuItemText (my nat.entry.handle, my nat.entry.item, mac_text);
			} else if (acc > 32 && modifiers == _motif_COMMAND_MASK) {
				PfromCstr (mac_text, my name);
				SetMenuItemText (my nat.entry.handle, my nat.entry.item, mac_text);
				SetItemCmd (my nat.entry.handle, my nat.entry.item, acc);
			} else {
				const char *optionString = Melder_systemVersion >= 0x0800 ? "\007" : "Alt-";
				const char *shiftString = Melder_systemVersion >= 0x0800 ? "\005" : "Shift-";
				const char *commandString = "\021";
				/*
				 * From System 8 on, the system font contains symbols for Shift and Option.
				 */
				static const char *keyStrings7 [256] = {
					0, "<-", "->", "Up", "Down", "PAUSE", "Del", "Ins", "Backspace", "Tab", "LineFeed", "Home", "End", "Enter", "PageUp", "PageDown",
					"Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", 0, 0, 0,
					"Space", "!", "\"", "#", "$", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/",
					"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
					"@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
					"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_",
					"`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
					"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "Del",
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					0, 0, 0, 0, "\244", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "[", "]", ",", "?", ".", "\\",
					";", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-", "`", "=", "\'", 0,
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
				static const char *keyStrings8 [256] = {
					0, "\030", "\032", "\031", "\020", "PAUSE", "Del", "Ins", "Backspace", "Tab", "LineFeed", "Home", "End", "Enter", "PageUp", "PageDown",
					"Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", 0, 0, 0,
					"Space", "!", "\"", "#", "$", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/",
					"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
					"@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
					"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_",
					"`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
					"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "Del",
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					0, 0, 0, 0, "\244", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "[", "]", ",", "?", ".", "\\",
					";", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-", "`", "=", "\'", 0,
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
				char *spaces = "                                                            ";   /* 60 spaces. */
				const char **keyStrings = Melder_systemVersion >= 0x0800 ? & keyStrings8 [0] : & keyStrings7 [0];
				int width, numberOfSpaces;
				float spaceWidth;
				SetPortWindowPort (my macWindow);
				TextFont (systemFont);
				TextSize (0);
				sprintf (title, "%s%s%s%s%s", my name,
					modifiers & _motif_OPTION_MASK ? optionString : "",
					modifiers & _motif_SHIFT_MASK ? shiftString : "",
					modifiers & _motif_COMMAND_MASK ? commandString : "", keyStrings [acc]);
				width = TextWidth ((Ptr) title, 0, strlen (title));
				spaceWidth = TextWidth ((Ptr) spaces, 0, 1);
				motif_mac_defaultFont ();
				numberOfSpaces = (200 - width) / spaceWidth;
				if (numberOfSpaces < 3) numberOfSpaces = 3;
				else if (numberOfSpaces > 60) numberOfSpaces = 60;
				sprintf (title, "%s%s%s%s%s%s", my name, spaces + 60 - numberOfSpaces,
					modifiers & _motif_OPTION_MASK ? optionString : "",
					modifiers & _motif_SHIFT_MASK ? shiftString : "",
					modifiers & _motif_COMMAND_MASK ? commandString : "", keyStrings [acc]);
				PfromCstr (mac_text, title);
				SetMenuItemText (my nat.entry.handle, my nat.entry.item, mac_text);
			}
		}
	#endif
}

/********** **********/

#if mac
	static int theClickedTextScrolled;
	static void scrollScrolledText (Widget me, short dv) {
		Widget scrollBar = my parent -> motif.scrolledWindow.verticalBar;
		int min = 0, max = scrollBar -> maximum - scrollBar -> sliderSize;
		int newValue = scrollBar -> value + dv;
		if (newValue > max) newValue = max;
		if (newValue < min) newValue = min;
		TEScroll (0, scrollBar -> value - newValue, my nat.text.handle);
		_motif_clipOn (scrollBar);
		XtVaSetValues (scrollBar, XmNvalue, newValue, NULL);
		_motif_clipOn (me);
	}
	static Widget theClickedText;
	static pascal unsigned char _motif_clickLoop (TEPtr pTE) {
		Widget me = theClickedText;
		Point mouseLoc;
		short top, bottom;
		(void) pTE;
		if (! MEMBER (my parent, ScrolledWindow)) return 1;
		top = my parent -> motif.scrolledWindow.clipWindow -> rect. top;
		bottom = my parent -> motif.scrolledWindow.clipWindow -> rect. bottom;
		GetMouse (& mouseLoc);
		if (mouseLoc. v > bottom) scrollScrolledText (me, +6);
		if (mouseLoc. v < top) scrollScrolledText (me, -6);
		return 1;
	}
#endif

/*
 * We now create the native objects associated with this widget,
 * but do not show them on the screen yet (ideally).
 * A reference must be made from widget to native object and back.
 * On Mac, we normally use the RefCon fields of the windows and controls.
 * On Win, we use SetWindowLong (window, GWL_USERDATA, widget).
 */

static void _GuiNativizeWidget (Widget me) {
	if (my nativized) return;
	if (my inMenu) {
		if (MEMBER (me, PulldownMenu)) {
			#if win
				/*
				 * BUG: there cannot be more than 255 menus simultaneously.
				 */
				int id;
				for (id = 1; id <= 255; id ++) if (! theMenus [id]) break;
				my nat.menu.id = id;
				theMenus [my nat.menu.id] = me;   /* Instead of UserData fields. */
				/*
				 * This will be a hierarchical menu.
				 */
				my nat.menu.handle = CreatePopupMenu ();
			#elif mac
				/*
				 * BUG: there cannot be more than 255 menus simultaneously.
				 */
				int id;
				for (id = 1; id <= 255; id ++) if (! theMenus [id]) break;
				my nat.menu.id = id;
				theMenus [my nat.menu.id] = me;   /* Instead of RefCon fields. */
				/*
				 * This will be a hierarchical menu.
				 */
				PfromCstr (mac_text, my name);
				my nat.menu.handle = NewMenu (my nat.menu.id, mac_text);
				InsertMenu (my nat.menu.handle, -1);   /* Submenu. */
			#endif
		} else {
			/*
			 * Any menu item (push button, toggle button, or cascade button) shall contain its native parent menu handle.
			 */
			my nat.entry.handle = my parent -> nat.menu.handle;
			#if win
				/*
				 * A Windows menu item shall have a shell-unique ID,
				 * which we can use to make changes and
				 * which will be sent to us by the WM_COMMAND message.
				 * This ID should be higher than 1000, in order to be different from the menu IDs.
				 */
				if (my shell -> nat.shell.uniqueItemID == 0)   // first menu item?
					my shell -> nat.shell.uniqueItemID = 1000;   // initialize unique ID
				my nat.entry.id = ++ my shell -> nat.shell.uniqueItemID;   // install unique ID
				if (my nat.entry.id == 30000)
					Melder_warning ("Running out of menu resources.\nPlease save your work soon and quit %s.", theApplicationName);
			#endif
		}
	} else switch (my widgetClass) {
		case xmBulletinBoardWidgetClass: {
			#if win
				my window = CreateWindowEx (0, theWindowClassName, "bulletinBoard", WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, NULL, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
			#endif
		} break;
		case xmDrawingAreaWidgetClass: {
			#if win
				my window = CreateWindowEx (0, theDrawingAreaClassName, "drawingArea", WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, NULL, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
			#endif
		} break;
		case xmFormWidgetClass: {
			#if win
				my window = CreateWindowEx (0, theWindowClassName, "form", WS_CHILD | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, NULL, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
			#endif
		} break;
		case xmRowColumnWidgetClass: {
			#if win
				my window = CreateWindowEx (0, theWindowClassName, "rowColumn", WS_CHILD | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, NULL, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
			#endif
		} break;
		case xmListWidgetClass: {
			#if win
				my window = CreateWindowEx (0, "listbox", "list",
					WS_CHILD | WS_BORDER | WS_VSCROLL | LBS_NOTIFY | LBS_EXTENDEDSEL | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, NULL, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
				SetWindowFont (my window, GetStockFont (ANSI_VAR_FONT), FALSE);
				/*if (MEMBER (my parent, ScrolledWindow)) {
					XtDestroyWidget (my parent -> motif.scrolledWindow.horizontalBar);
					my parent -> motif.scrolledWindow.horizontalBar = NULL;
					XtDestroyWidget (my parent -> motif.scrolledWindow.verticalBar);
					my parent -> motif.scrolledWindow.verticalBar = NULL;
				}*/
			#elif mac
				Rect dataBounds = { 0, 0, 0, 1 };
				Point cSize;
				SetPt (& cSize, my rect.right - my rect.left + 1, CELL_HEIGHT);
				my nat.list.handle = LNew (& my rect, & dataBounds, cSize, 0,
					my macWindow, false, false, false, false);
			#endif
		} break;
		case xmMenuBarWidgetClass: {
			#if win
				if (! my shell -> motif.shell.isDialog && my shell -> nat.shell.menuBar == NULL && my parent -> widgetClass != xmRowColumnWidgetClass) {
					HMENU bar = CreateMenu ();
					SetMenu (my shell -> window, bar);
					my nat.menu.handle = bar;
					my shell -> nat.shell.menuBar = me;   // does this have to be?
				} else {
					my widgetClass = xmRowColumnWidgetClass;   /* !!!!!!!!!!!!! */
					my orientation = XmHORIZONTAL;
					my rowColumnType = XmMENU_BAR;
					my window = CreateWindowEx (0, theWindowClassName, "rowColumn", WS_CHILD,
						my x, my y, my width, my height, my parent -> window, NULL, theInstance, NULL);
					SetWindowLong (my window, GWL_USERDATA, (long) me);
				}
			#elif mac
				/*
				 * The first menu bar that occurs in the application shell,
				 * will be positioned in the Macintosh menu bar.
				 * All other menu bars are at the top of their own windows.
				 * This is Motif style, and only works well for Macintoshes with large screens.
				 */
				if ((Widget) GetWRefCon (my macWindow) == theApplicationShell && theMenuBar == NULL) {
					theMenuBar = me;
				} else {
					my widgetClass = xmRowColumnWidgetClass;   /* !!!!!!!!!!!!! */
					my orientation = XmHORIZONTAL;
					my rowColumnType = XmMENU_BAR;
				}
			#endif
		} break;
		case xmMessageBoxWidgetClass: {
			#if win
				my window = CreateWindowEx (0, theWindowClassName, "messageBox", WS_CHILD | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, NULL, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
			#endif
			my motif.messageBox.okButton = XtVaCreateManagedWidget ("OK", xmPushButtonWidgetClass, me,
				XmNx, 10, XmNy, 105, XmNwidth, MESSAGE_BOX_BUTTON_WIDTH, 0);
			XtAddCallback (my motif.messageBox.okButton, XmNactivateCallback, cb_messageBox_ok, (XtPointer) me);
			my motif.messageBox.cancelButton = XtVaCreateManagedWidget ("Cancel", xmPushButtonWidgetClass, me,
				XmNx, 20 + MESSAGE_BOX_BUTTON_WIDTH, XmNy, 105, XmNwidth, MESSAGE_BOX_BUTTON_WIDTH, 0);
			XtAddCallback (my motif.messageBox.cancelButton, XmNactivateCallback, cb_messageBox_cancel, (XtPointer) me);
			my motif.messageBox.helpButton = XtVaCreateManagedWidget ("Help", xmPushButtonWidgetClass, me,
				XmNx, 30 + 2 * MESSAGE_BOX_BUTTON_WIDTH, XmNy, 105, XmNwidth, MESSAGE_BOX_BUTTON_WIDTH, 0);
			XtAddCallback (my motif.messageBox.helpButton, XmNactivateCallback, cb_messageBox_help, (XtPointer) me);
			my messageText = XtVaCreateManagedWidget ("text", xmTextWidgetClass, me,
				XmNwidth, 42 + 3 * MESSAGE_BOX_BUTTON_WIDTH, XmNheight, 100, 0);
		} break;
		case xmPulldownMenuWidgetClass: {
			#if win
				/*
				 * BUG: there cannot be more than 255 menus simultaneously.
				 */
				int id;
				for (id = 1; id <= 255; id ++) if (! theMenus [id]) break;
				my nat.menu.id = id;
				theMenus [my nat.menu.id] = me;   /* Instead of UserData fields. */
				if (MEMBER (my parent, MenuBar)) {
					Widget menu;
					UINT beforeID = -1;
					my nat.menu.handle = CreatePopupMenu ();
					/*
					 * Insert the menu before the Help menu, if that exists; otherwise, at the end.
					 */
					for (menu = my parent -> firstChild; menu != NULL; menu = menu -> nextSibling) {
						if (MEMBER (menu, PulldownMenu) && strequ (menu -> name, "Help") && menu != me) {
							beforeID = (UINT) menu -> nat.menu./*handle*/id;
							break;
						}
					}
					{
						MENUITEMINFO info;
						info. cbSize = sizeof (MENUITEMINFO);
						info. fMask = MIIM_TYPE | MIIM_SUBMENU | MIIM_ID;
						info. fType = MFT_STRING | ( strequ (my name, "Help") ? MFT_RIGHTJUSTIFY : 0 );
						info. dwTypeData = my name;
						info. hSubMenu = my nat.menu.handle;
						info. wID = (UINT) my nat.menu./*handle*/id;
						InsertMenuItem (my parent -> nat.menu.handle, beforeID, 0, & info);
					} /*else if (beforeID >= 0)
						InsertMenu (my parent -> nat.menu.handle, beforeID, MF_STRING | MF_POPUP | MF_BYCOMMAND,
							(UINT) my nat.menu.handle, my name);
					else
						AppendMenu (my parent -> nat.menu.handle, MF_STRING | MF_POPUP, (UINT) my nat.menu.handle, my name);*/
					DrawMenuBar (my shell -> window);
				} else if (MEMBER (my parent, RowColumn) && my parent -> rowColumnType == XmMENU_BAR) {
					my nat.menu.handle = CreatePopupMenu ();
				}
			#elif mac
				Str255 title;
				/*
				 * BUG: there cannot be more than 255 menus simultaneously.
				 */
				int id;
				for (id = 1; id <= 255; id ++) if (! theMenus [id]) break;
				my nat.menu.id = id;
				theMenus [my nat.menu.id] = me;   /* Instead of RefCon fields. */
				PfromCstr (title, my name);
				if (MEMBER (my parent, MenuBar)) {
					/*
					 * This will be a menu in the Macintosh menu bar.
					 */
					if (USE_QUESTION_MARK_HELP_MENU && strequ (my name, "Help")) {
						#if carbon
							HMGetHelpMenu (& my nat.menu.handle, NULL);
						#else
							HMGetHelpMenuHandle (& my nat.menu.handle);
						#endif
						theMenus [my nat.menu.id] = NULL;
						my nat.menu.id = kHMHelpMenuID;
						theMenus [255] = me;
					} else {
						int beforeID = 0;
						my nat.menu.handle = NewMenu (my nat.menu.id, title);
						for (id = 1; id <= 255; id ++) {
							Widget menu = theMenus [id];
							if (menu && strequ (menu -> name, "Help")) {
								beforeID = id;
								break;
							}
						}
						InsertMenu (my nat.menu.handle, beforeID);
					}
				} else if (MEMBER (my parent, RowColumn)) {
					/*
					 * This will be a menu outside the main window,
					 * or at a different place in the main window.
					 * It is implemented as a Mac pop-up menu.
					 */
					my nat.menu.handle = NewMenu (my nat.menu.id, title);
					InsertMenu (my nat.menu.handle, -1);
				}
			#endif
		} break;
		case xmCascadeButtonWidgetClass: {
			if (my motif.cascadeButton.pushButtonLook) {
				#if win
					my window = CreateWindow ("button", motif_win_expandAmpersands (my name),
						WS_CHILD | BS_PUSHBUTTON | WS_CLIPSIBLINGS,
						my x, my y, my width, my height, my parent -> window, (HMENU) 1, theInstance, NULL);
					SetWindowLong (my window, GWL_USERDATA, (long) me);
					SetWindowFont (my window, GetStockFont (ANSI_VAR_FONT), FALSE);
				#elif mac
					PfromCstr (mac_text, my name);
					my nat.control.handle = NewControl (my macWindow, & my rect,
						mac_text, false, 0, 0, 0, pushButProc + motif_mac_controlFont, (long) me);
					Melder_assert (my nat.control.handle);
					my isControl = TRUE;
				#endif
			} else if (! MEMBER (my parent, MenuBar)) {
				#if win
					my window = CreateWindow ("static", motif_win_expandAmpersands (my name), WS_CHILD |
						( my alignment == XmALIGNMENT_END ? SS_RIGHT :
						  my alignment == XmALIGNMENT_CENTER ? SS_CENTER : SS_LEFT ) | SS_CENTERIMAGE,
						my x, my y, my width, my height, my parent -> window, (HMENU) 1, theInstance, NULL);
					SetWindowLong (my window, GWL_USERDATA, (long) me);
					SetWindowFont (my window, GetStockFont (ANSI_VAR_FONT), FALSE);
				#elif mac
					if (haveAppearance && (my parent -> widgetClass != xmRowColumnWidgetClass || my parent -> rowColumnType != XmMENU_BAR)) {
						ControlFontStyleRec fontStyle;
						fontStyle. flags = kControlUseFontMask | kControlUseSizeMask | kControlUseJustMask;
						fontStyle. font = systemFont;
						fontStyle. size = 12;
						fontStyle. just = my alignment == XmALIGNMENT_END ? teFlushRight :
							my alignment == XmALIGNMENT_CENTER ? teCenter : teFlushLeft;
						PfromCstr (mac_text, my name);
						my nat.control.handle = NewControl (my macWindow, & my rect,
							"\p", false, 0, 0, 0, kControlStaticTextProc, (long) me);
						SetControlFontStyle (my nat.control.handle, & fontStyle);
						SetControlData (my nat.control.handle, kControlEntireControl, kControlStaticTextTextTag, strlen (my name),
							my name);
						my isControl = TRUE;
					}
				#endif
			}
		} break;
		case xmPushButtonWidgetClass: {
			#if win
				my window = CreateWindow ("button", motif_win_expandAmpersands (my name),
					WS_CHILD | BS_PUSHBUTTON | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, (HMENU) 1, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
				SetWindowFont (my window, GetStockFont (ANSI_VAR_FONT), FALSE);
			#elif mac
				PfromCstr (mac_text, my name);
				my nat.control.handle = NewControl (my macWindow, & my rect,
					mac_text, false, 0, 0, 0, pushButProc + motif_mac_controlFont, (long) me);
				Melder_assert (my nat.control.handle);
				my isControl = TRUE;
			#endif
		} break;
		case xmToggleButtonWidgetClass: {
			my isRadioButton = my parent -> radioBehavior || my motif.toggleButton.indicatorType == XmONE_OF_MANY;
			#if win
				my window = CreateWindow ("button", motif_win_expandAmpersands (my name), WS_CHILD |
					( my isRadioButton ? ( my parent -> radioBehavior ? BS_AUTORADIOBUTTON : BS_RADIOBUTTON )
						: BS_AUTOCHECKBOX ) | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, (HMENU) 1, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
				SetWindowFont (my window, GetStockFont (ANSI_VAR_FONT), FALSE);
			#elif mac
			{
				/*
				 * Create an invisible radio button or check box.
				 */
				int proc = my isRadioButton ? radioButProc : checkBoxProc;
				PfromCstr (mac_text, my name);
				my nat.control.handle = NewControl (my macWindow, & my rect,
					mac_text, false, 0, 0, 1, proc + motif_mac_controlFont, (long) me);
				Melder_assert (my nat.control.handle);
				my isControl = TRUE;
			}
			#endif
		} break;
		case xmScaleWidgetClass: {
			#if win
				my window = CreateWindow (PROGRESS_CLASS, motif_win_expandAmpersands (my name), WS_CHILD | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, (HMENU) 1, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
				SendMessage (my window, PBM_SETRANGE, (WPARAM) 0, (LPARAM) MAKELONG (0, 1000));
			#endif
		} break;
		case xmScrollBarWidgetClass: {
			#if win
				my window = CreateWindow ("scrollbar", my name, WS_CHILD |
					( strequ (my name, "verticalScrollBar") ? SBS_VERT : SBS_HORZ ) | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, (HMENU) 1, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
				NativeScrollBar_set (me);
			#elif mac
				my nat.control.handle = NewControl (my macWindow, & my rect,
					"\p", false, 0, 0, 0, scrollBarProc, (long) me);
				Melder_assert (my nat.control.handle);
				my isControl = TRUE;
			#endif
			my minimum = 0;
			my maximum = 100;
			my value = 0;
			my sliderSize = 100;
		} break;
		case xmScrolledWindowWidgetClass: {
			/*
			 * The space of the scrolled window is complete filled by three of its children:
			 * the two scroll bars and the clip window. The first child you create yourself will be the
			 * work window: this work window can grow larger than the clip window.
			 * While normally every widget is only clipped to the rects of its ancestors,
			 * for a scrolled window the clip window will be inserted into this chain.
			 * Example: if the widget hierarchy is
			 *      shell.form.scrolledWindow.column.row.pushButton,
			 * the clipping hierarchy will be
			 *      (shell.)form.(scrolledWindow.)clipWindow.column.row.pushButton
			 */
			#if win
				my window = CreateWindowEx (0, theWindowClassName, "scrolledWindow", WS_CHILD | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, NULL, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
			#endif
			my motif.scrolledWindow.horizontalBar = XmCreateScrollBar (me, "horizontalScrollBar", NULL, 0);
			my motif.scrolledWindow.verticalBar = XmCreateScrollBar (me, "verticalScrollBar", NULL, 0);
			XtVaSetValues (my motif.scrolledWindow.horizontalBar, XmNorientation, XmHORIZONTAL,
				XmNleftAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM,
				XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 15, XmNheight, 16,
				XmNminimum, 0, XmNmaximum, 100, XmNsliderSize, 100,
				XmNincrement, CELL_HEIGHT, XmNpageIncrement, 101 - CELL_HEIGHT, 0);
			XtVaSetValues (my motif.scrolledWindow.verticalBar, XmNorientation, XmVERTICAL,
				XmNtopAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM,
				XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 15, XmNwidth, 16,
				XmNminimum, 0, XmNmaximum, 100, XmNsliderSize, 100,
				XmNincrement, CELL_HEIGHT, XmNpageIncrement, 101 - CELL_HEIGHT, 0);
			my motif.scrolledWindow.clipWindow = XmCreateBulletinBoard (me, "clipWindow", NULL, 0);
			XtVaSetValues (my motif.scrolledWindow.clipWindow,
				XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 1,   /* For border. */
				XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 16,   /* For scroll bar. */
				XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 1,   /* For border. */
				XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 16, 0);   /* For scroll bar. */
			XtAddCallback (my motif.scrolledWindow.verticalBar, XmNvalueChangedCallback, cb_scroll, (XtPointer) me);
			XtAddCallback (my motif.scrolledWindow.horizontalBar, XmNvalueChangedCallback, cb_scroll, (XtPointer) me);
		} break;
		case xmShellWidgetClass: {
			#if win
				static LPCTSTR className = theApplicationClassName;   /* Only for first window. */
				my window = CreateWindowEx (theDialogHint ? WS_EX_DLGMODALFRAME /* | WS_EX_TOPMOST */ : 0,
					className, className,
					theDialogHint ? WS_CAPTION | WS_SYSMENU : WS_OVERLAPPEDWINDOW,
					CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,  my parent ? my parent -> window : NULL, NULL, theInstance, NULL);
				className = theWindowClassName;   /* All later windows. */
				SetWindowLong (my window, GWL_USERDATA, (long) me);
			#elif mac
				Rect r = my rect;
				OffsetRect (& r, 0, 22);
				if (haveAppearance) {
					CreateNewWindow (kDocumentWindowClass,
						kWindowCloseBoxAttribute + ( theDialogHint ? 0 : kWindowCollapseBoxAttribute + kWindowResizableAttribute ),
						& r, & my nat.window.ptr);
					SetWRefCon (my nat.window.ptr, (long) me);
					CreateRootControl (my nat.window.ptr, & my nat.window.rootControl);
					if (theDialogHint)
						SetThemeWindowBackground (my nat.window.ptr, kThemeBrushDialogBackgroundActive, False);
				} else {
					my nat.window.ptr = NewCWindow (NULL, & r, "\pPraat",
						false,   /* Invisible. */
						documentProc,
						(WindowPtr) -1,   /* In front of all others (after it is shown). */
						true,   /* Has a go-away button. */
						(long) me   /* So we can find the widget from the event with GetWRefCon (). */
					);
				}
				my macWindow = my nat.window.ptr;   /* Associate drawing context; child widgets will inherit. */
				my motif.shell.active = true;   /* Why? */
				SetPortWindowPort (my macWindow);
				motif_mac_defaultFont ();   /* Will be set back after every change. */
			#endif
			my motif.shell.isDialog = theDialogHint;   /* So we can maintain a single Shell class instead of two different. */
		} break;
		case xmTextWidgetClass: {
			#if win
				my window = CreateWindow ("edit", NULL, WS_CHILD | WS_BORDER | ES_AUTOHSCROLL | ES_MULTILINE | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, (HMENU) 1, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
				SetWindowFont (my window, GetStockFont (ANSI_VAR_FONT), FALSE);
				my motif.text.editable = TRUE;
				Edit_LimitText (my window, 0);
			#elif mac
			{
				if (my parent -> widgetClass == xmScrolledWindowWidgetClass || ! haveAppearance || ! TEXTFIELD_IS_CONTROL) {
					Rect r = my rect;
					static TEClickLoopUPP theTEClickLoopUPP;
					InsetRect (& r, 3, 3);   /* BUG: this causes a border problem in autoscrolling. */
					motif_mac_textFont ();
					my nat.text.handle = TENew (& r, & r);   /* BUG: should clip on r, not my rect. */
					motif_mac_defaultFont ();
					my motif.text.editable = True;
					if (! theTEClickLoopUPP) theTEClickLoopUPP = NewTEClickLoopUPP (_motif_clickLoop);
					TESetClickLoop (theTEClickLoopUPP, my nat.text.handle);
				} else {
					static short font;
					Rect r = my rect;
					Size actualSize;
					ControlFontStyleRec fontStyle;
					if (! font) GetFNum ("\pMonaco", & font);
					fontStyle. flags = kControlUseFontMask | kControlUseSizeMask;
					fontStyle. font = font;
					fontStyle. size = 9;
					InsetRect (& r, 3, 3);
					my nat.control.handle = NewControl (my macWindow, & r,
						"\p", false, 0, 0, 0, kControlEditTextProc + motif_mac_controlFont, (long) me);
					/*SetControlFontStyle (my nat.control.handle, & fontStyle);*/
					GetControlData (my nat.control.handle, kControlEntireControl, kControlEditTextTEHandleTag,
						sizeof (TEHandle), & my nat.text.handle, & actualSize);
					my motif.text.editable = True;
					my isControl = TRUE;
				}
			}
			#endif
			/*
			 * The first created text widget shall attract the input focus.
			 */
			if (! my shell -> activeTextWidget)
				my shell -> activeTextWidget = me;
		} break;
		default: break;
	}
	my nativized = True;
}

static Widget createWidget (int widgetClass, Widget parent, const char *name) {
	Widget me = initializeWidget (widgetClass, parent, name);
	_GuiNativizeWidget (me);
	return me;
}

static Widget vaCreateWidget (int widgetClass, Widget parent, const char *name, va_list arg) {
	Widget me = initializeWidget (widgetClass, parent, name);
	int resource;
	while (resource = va_arg (arg, int), resource != 0) switch (resource) {
		case XmNalignment: {
			my alignment = va_arg (arg, int);
		} break; case XmNindicatorType: {
			Melder_assert (my widgetClass == xmToggleButtonWidgetClass);
			my motif.toggleButton.indicatorType = va_arg (arg, int);
		} break; default: {
			(void) va_arg (arg, int);
		}
	}
	_GuiNativizeWidget (me);
	return me;
}

static void invalidateWidget (Widget me) {
	if (! my managed) return;   /* Should be: visible. */
	if (MEMBER (me, Shell) /*||
		 my widgetClass == xmRowColumnWidgetClass ||
		 my widgetClass == xmFormWidgetClass*/) return;   /* Composites are not invalidated !!!!! ???? */
	#if mac
		_motif_clipOn (me);
		EraseRect (& my rect);
		_motif_clipOffInvalid (me);
	#endif
}

static void validateWidget (Widget me) {
	if (! my managed) return;   /* Should be: visible. */
	if (MEMBER (me, Shell)) return;
	#if mac
		_motif_clipOn (me);
		_motif_clipOffValid (me);   
	#endif
}

static void Native_move (Widget me, int dx, int dy) {
/* Native_move () changes the native attributes and visual position of the widget:
 * No motif attributes (like 'x' and 'y') are changed.
 * Usage:
 *     Native_move () is normally called immediately after changing the 'x' or 'y' attribute.
 *     It can be seen as the 'realization' of a Motif move.
 */
#if win
	(void) dx;
	(void) dy;
	if (! my window) return;   // ignore menu items
	if (MEMBER (me, Shell)) {
		my nat.shell.duringMoveWindow = True;
		if (my motif.shell.isDialog)
			MoveWindow (my window, my x, my y, my width + 2 * GetSystemMetrics (SM_CXFIXEDFRAME),
				my height + 2 * GetSystemMetrics (SM_CYFIXEDFRAME) + GetSystemMetrics (SM_CYCAPTION), TRUE);
		else
			MoveWindow (my window, my x, my y,
				my width + 2 * GetSystemMetrics (SM_CXSIZEFRAME),
				my height + 2 * GetSystemMetrics (SM_CYSIZEFRAME) + GetSystemMetrics (SM_CYCAPTION) +
				( my nat.shell.menuBar != NULL ? GetSystemMetrics (SM_CYMENU) : 0 ), TRUE);
	} else
		MoveWindow (my window, my x, my y, my width, my height, True);
#elif mac
	/*
	 * On Mac, the 'rect' attribute is changed, as is the position information in the Mac object.
	 * Native_move () calls itself with the same arguments for all the widget's children.
	 */
	Widget child;
	if (dx == 0 && dy == 0 || MEMBER (me, MenuBar) || my parent && MEMBER (my parent, PulldownMenu)) return;
	if (! my isControl)
		invalidateWidget (me);   /* At old position. BUG: clipping may be wrong because parent may have moved;
				however, parent will have been erased in that case, so we will not see it. */
	/*
	 * Because all non-shell coordinates are port-relative on Mac, we have to adapt them to the new x and y.
	 */
	my rect.left += dx, my rect.right += dx;
	my rect.top += dy, my rect.bottom += dy;
	if (my isControl) {
		_motif_clipOn (me);
		if (MEMBER (me, Text))
			MoveControl (my nat.control.handle, my rect.left + 3, my rect.top + 3);
		else
			MoveControl (my nat.control.handle, my rect.left, my rect.top);
		_motif_clipOffValid (me);
	} else if (MEMBER (me, List)) {
		(** my nat.list.handle). rView = my rect;
	} else if (MEMBER (me, Text)) {
		Rect r = my rect;
		InsetRect (& r, 3, 3);   /* Smaller than my rect! */
		(** my nat.text.handle). destRect = r;
		(** my nat.text.handle). viewRect = r;
	} else if (MEMBER (me, Shell)) {
		MoveWindow (my nat.window.ptr, my rect.left, my rect.top + 22, 0);
	} else if (MEMBER (me, DrawingArea)) {
		xt_callCallbacks (me, & my motif.drawingArea.moveCallbacks, 0);   /* Only Mac? */
	}
	if (! my isControl) invalidateWidget (me);   /* At new position. */
	/*
	 * Because all non-shell coordinates are port-relative on Mac, we have to recursively move the children.
	 */
	if (! MEMBER (me, Shell))
		for (child = my firstChild; child; child = child -> nextSibling)
			Native_move (child, dx, dy);
#endif
}

#if mac
/*
 * reshowControls is only called by shellResizeWidget,
 * but it must be defined separately because it is recursive.
 */
static void reshowControls (Widget me) {
	Widget child;
	if (my widgetClass == xmPulldownMenuWidgetClass) return;
	for (child = my firstChild; child; child = child -> nextSibling) {
		if (child -> isControl) {
			/*
			 * This is a Mac control widget.
			 * Let Mac do the redrawing if Motif considers it visible and Mac considers it invisible?
			 */
			if (child -> managed && ! IsControlVisible (child -> nat.control.handle)) {
				_motif_clipOn (child);
				ShowControl (child -> nat.control.handle);
				_motif_clipOffValid (child);
			}
		} else if (! MEMBER (child, Shell)) {
			/*
			 * This may be a widget with children.
			 * Some of those children may be controls, or contain controls, so recurse.
			 */
			reshowControls (child);
		}
	}
}
#endif

static void _motif_manageScrolledWindow (Widget w);   /* Forward. */
static void shellResizeWidget (Widget me, int dx, int dy, int dw, int dh) {
	Widget child;
	#if win
	Melder_assert (! my shell || ! my shell -> nat.shell.duringMoveWindow);
		if (my window && ! MEMBER (me, Shell)) {
			/*RECT rect, client;
			GetWindowRect (my window, & rect);
			GetClientRect (my window, & client);
			ScreenToClient (my parent -> window, (LPPOINT) & rect.left);
			ScreenToClient (my parent -> window, (LPPOINT) & rect.right);
			if (rect.left!=my x||rect.right!=my x + my width||rect.top !=my y||rect.bottom!=my y +my height)
			Melder_warning ("class %d x %d left %d y %d top %d width %d right %d height %d bottom %d",
			my widgetClass, my x, rect.left, my y, rect.top, my width, rect.right, my height, rect.bottom);*/
			MoveWindow (my window, my x, my y, my width, my height, TRUE);
			if (MEMBER (me, DrawingArea) && my resizeCallback) my resizeCallback (me, my resizeClosure, NULL);
		}
	#elif mac
		if (dx == 0 && dy == 0 && dw == 0 && dh == 0 || MEMBER (me, MenuBar) || my parent && MEMBER (my parent, PulldownMenu)) return;
		OffsetRect (& my rect, dx, dy);
		my rect.right += dw;
		my rect.bottom += dh;
		if (my isControl) {
			ControlHandle c = my nat.control.handle;
			if (my managed) HideControl (c);
			if (dx || dy)
				if (MEMBER (me, Text))
					MoveControl (c, my rect.left + 3, my rect.top + 3);
				else
					MoveControl (c, my rect.left, my rect.top);
			if (dw || dh)
				if (MEMBER (me, Text))
					SizeControl (c, my width - 6, my height - 6);
				else
					SizeControl (c, my width, my height);
		} else if (MEMBER (me, List)) {
			(** my nat.list.handle). rView = my rect;
			(** my nat.list.handle). cellSize. h = my width;
		} else if (MEMBER (me, Shell)) {
			Rect portRect;
			GetWindowPortBounds (my nat.window.ptr, & portRect);
			SetPortWindowPort (my nat.window.ptr);
			/*
				Erase old area.
			*/
			EraseRect (& portRect);
			SizeWindow (my nat.window.ptr, my width, my height, false);
			/*
				Redraw new area.
			*/
			GetWindowPortBounds (my nat.window.ptr, & portRect);
			InvalWindowRect (my nat.window.ptr, & portRect);
		} else if (MEMBER (me, Text)) {
			Rect r = my rect;
			char *text;
			InsetRect (& r, 3, 3);
			(** my nat.text.handle). destRect = r;
			(** my nat.text.handle). viewRect = r;
			/* TRICK: force geometry management of the Mac TE widget. */
			text = XmTextGetString (me);
			XmTextSetString (me, text);
			XtFree (text);
		} else if (MEMBER (me, DrawingArea)) {
			if (my resizeCallback) my resizeCallback (me, my resizeClosure, NULL);
			if (dx != 0 || dy != 0)
				xt_callCallbacks (me, & my motif.drawingArea.moveCallbacks, 0);
		}
	#endif
	for (child = my firstChild; child; child = child -> nextSibling) {
		int cdx = 0, cdy = 0, cdw = 0, cdh = 0;
		if (MEMBER (child, Shell)) continue;
		if (child -> rightAttachment)
			if (child -> leftAttachment) cdw = dw; else cdx = dw;
		if (child -> bottomAttachment)
			if (child -> topAttachment) cdh = dh; else cdy = dh;
		child -> x += cdx;
		child -> y += cdy;
		child -> width += cdw;
		child -> height += cdh;
		shellResizeWidget (child, dx + cdx, dy + cdy, cdw, cdh);
	}
	if (MEMBER (me, ScrolledWindow))
		_motif_manageScrolledWindow (me);
	#if mac
		if (MEMBER (me, Shell)) reshowControls (me);
	#endif
}

static void resizeWidget (Widget me, int dw, int dh) {
	Widget child;
	#if win
		if (my window && ! MEMBER (me, Shell)) {
			MoveWindow (my window, my x, my y, my width, my height, TRUE);
			if (MEMBER (me, DrawingArea) && my resizeCallback) my resizeCallback (me, my resizeClosure, NULL);
		}
	#elif mac
	{
		if (dw == 0 && dh == 0 || my parent && MEMBER (my parent, PulldownMenu)) return;
		if (! my isControl) invalidateWidget (me);   /* At old position. */
		my rect.right += dw;
		my rect.bottom += dh;
		if (my isControl) {
			if (MEMBER (me, Text))
				SizeControl (my nat.control.handle, my width - 6, my height - 6);
			else
				SizeControl (my nat.control.handle, my width, my height);
			validateWidget (me);
		} else if (MEMBER (me, List)) {
			(** my nat.list.handle). rView = my rect;
			SetPortWindowPort (my macWindow);
			(** my nat.list.handle). cellSize. h = my width;
			if (my parent -> widgetClass == xmScrolledWindowWidgetClass)
				_motif_manageScrolledWindow (my parent);
		} else if (MEMBER (me, Shell)) {
			SizeWindow (my macWindow, my width, my height, true);
		} else if (MEMBER (me, Text)) {
			Rect r = my rect;
			InsetRect (& r, 3, 3);
			(** my nat.text.handle). destRect = r;
			(** my nat.text.handle). viewRect = r;
		} else if (MEMBER (me, DrawingArea)) {
			if (my resizeCallback) my resizeCallback (me, my resizeClosure, NULL);
		}
		if (! my isControl) invalidateWidget (me);   /* At new position. */
	}
	#endif
	if (MEMBER2 (me, Form, ScrolledWindow))
		for (child = my firstChild; child; child = child -> nextSibling) {
			int cdx = 0, cdy = 0, cdw = 0, cdh = 0;
			if (child -> widgetClass == xmShellWidgetClass) continue;
			if (child -> rightAttachment)
				if (child -> leftAttachment) cdw = dw; else cdx = dw;
			if (child -> bottomAttachment)
				if (child -> topAttachment) cdh = dh; else cdy = dh;
			if (cdx || cdy) {
				child -> x += cdx;
				child -> y += cdy;
				Native_move (child, cdx, cdy);
			}
			if (cdw || cdh) {
				child -> width += cdw;
				child -> height += cdh;
				resizeWidget (child, cdw, cdh);
			}
		}
	if (MEMBER (me, Shell)) {
		#if mac
			Rect portRect;
			int right, bottom;
			GetWindowPortBounds (my macWindow, & portRect);
			right = portRect. right, bottom = portRect. bottom;
		#elif win
			int right = 1000, bottom = 500;
		#endif
		#if mac
		{
			Rect rect;
			SetRect (& rect, right - 15, bottom - 15, right + 1, bottom + 1);
			ClipRect (& rect);
			DrawGrowIcon (my macWindow);
			motif_mac_clipOff ();
		}
		#endif
		for (child = my firstChild; child; child = child -> nextSibling)
			if (MEMBER2 (child, Form, ScrolledWindow)) {
				child -> width += dw;
				child -> height += dh;
				resizeWidget (child, dw, dh);
			}
	}
}

static void _motif_setValues (Widget me, va_list arg) {
	int resource;
	Boolean move = False, resize = False, attach = False, scrollset = False;
	char *text;
	#if win
		int oldX = my x, oldY = my y, oldWidth = my width, oldHeight = my height;
	#endif
	#if mac
		Str255 ptext;
	#endif
	while (resource = va_arg (arg, int), resource != 0) switch (resource) {
		case XmNaccelerator: {
			char *string = va_arg (arg, char *), *key = strstr (string, "<Key>");
			Melder_assert (MEMBER (me, PushButton));
			if (! key) break;
			key = & key [5];
			if (my inMenu && key [0]) {
				int kar = 0, modifiers = 0;
				if (strstr (string, "Ctrl")) modifiers |= _motif_OPTION_MASK;
				if (strstr (string, "Shift")) modifiers |= _motif_SHIFT_MASK;
				if (strstr (string, "Mod1")) modifiers |= _motif_COMMAND_MASK;
				if (key [1] == '\0') kar = key [0];   /* Single character. */
				else if (strequ (key, "Return")) kar = 13;
				else if (strequ (key, "BackSpace")) kar = 8;
				else if (strequ (key, "question")) kar = '?';
				else if (strequ (key, "bracketleft")) kar = '[';
				else if (strequ (key, "bracketright")) kar = ']';
				else if (strequ (key, "Left")) kar = motif_LEFT_ARROW;
				else if (strequ (key, "Right")) kar = motif_RIGHT_ARROW;
				else if (strequ (key, "Up")) kar = motif_UP_ARROW;
				else if (strequ (key, "Down")) kar = motif_DOWN_ARROW;
				else if (strequ (key, "Pause")) kar = motif_PAUSE;
				else if (strequ (key, "Delete")) kar = motif_DELETE;
				else if (strequ (key, "Insert")) kar = motif_INSERT;
				else if (strequ (key, "Tab")) kar = motif_TAB;
				else if (strequ (key, "Home")) kar = motif_HOME;
				else if (strequ (key, "End")) kar = motif_END;
				else if (strequ (key, "Page_Up")) kar = motif_PAGE_UP;
				else if (strequ (key, "Page_Down")) kar = motif_PAGE_DOWN;
				else if (strequ (key, "Escape")) kar = motif_ESCAPE;
				else if (strequ (key, "F1")) kar = motif_F1;
				else if (strequ (key, "F2")) kar = motif_F2;
				else if (strequ (key, "F3")) kar = motif_F3;
				else if (strequ (key, "F4")) kar = motif_F4;
				else if (strequ (key, "F5")) kar = motif_F5;
				else if (strequ (key, "F6")) kar = motif_F6;
				else if (strequ (key, "F7")) kar = motif_F7;
				else if (strequ (key, "F8")) kar = motif_F8;
				else if (strequ (key, "F9")) kar = motif_F9;
				else if (strequ (key, "F10")) kar = motif_F10;
				else if (strequ (key, "F11")) kar = motif_F11;
				else if (strequ (key, "F12")) kar = motif_F12;
				if (kar > 0 && kar < 32)
					my shell -> motif.shell.lowAccelerators [modifiers] |= 1 << kar;
				else if (kar == '?' || kar == '{' || kar == '}' || kar == '\"' || kar == '<' || kar == '>' || kar == '|' ||
								kar == '_' || kar == '+' || kar == '~')
					modifiers |= _motif_SHIFT_MASK;
				my motif.pushButton.acceleratorChar = kar;
				my motif.pushButton.acceleratorModifiers = modifiers;
				NativeMenuItem_setText (me);
			}
			break;
		}
		case XmNacceleratorText: {
			char *string = va_arg (arg, char *);
			Melder_assert (MEMBER (me, PushButton));
			break;
		}
		case XmNalignment:
			my alignment = va_arg (arg, int);
			break;
		case XmNautoUnmanage:
			my autoUnmanage = va_arg (arg, int);
			break;
		case XmNborderWidth: (void) va_arg (arg, int); break;
		case XmNbottomAttachment:
			my bottomAttachment = va_arg (arg, int);
			attach = True;
			break;
		case XmNbottomOffset:
			my bottomOffset = va_arg (arg, int);
			attach = True;
			break;
		case XmNcancelButton:
			Melder_assert (MEMBER3 (me, Form, BulletinBoard, MessageBox));
			my shell -> cancelButton = my cancelButton = va_arg (arg, Widget);
			break;
		case XmNcancelLabelString: {
			char *text = va_arg (arg, char *);
			Melder_assert (MEMBER (me, MessageBox));
			if (my motif.messageBox.cancelButton) XtVaSetValues (my motif.messageBox.cancelButton, XmNlabelString, text, NULL);
			break;
		}
		case XmNcolumns: {
			int columns = va_arg (arg, int);
			Melder_assert (MEMBER (me, Text));
			my width = columns * (mac ? 7 : 9) + 4;
			resize = True;
		} break;
		case XmNdefaultButton:
			Melder_assert (MEMBER3 (me, Form, BulletinBoard, MessageBox));
			my shell -> defaultButton = my defaultButton = va_arg (arg, Widget);
			Melder_assert (MEMBER (my defaultButton, PushButton));
			#if mac
				Melder_assert (my defaultButton -> nat.control.handle);
				if (haveAppearance) {
					Boolean set = true;
					SetControlData (my defaultButton -> nat.control.handle, kControlEntireControl, kControlPushButtonDefaultTag, sizeof (Boolean), & set);
				}
			#endif
			break;
		case XmNdeleteResponse:
			Melder_assert (MEMBER (me, Shell));
			my deleteResponse = va_arg (arg, int);
			if (my deleteResponse == XmDO_NOTHING && ! my motif.shell.goAwayCallback) {
				#if mac
					#if carbon
						ChangeWindowAttributes (my macWindow, 0, kWindowCloseBoxAttribute);
					#else
						((WindowPeek) my macWindow) -> goAwayFlag = 0;
					#endif
				#endif
			}
			break;
		case XmNdialogStyle:
			Melder_assert (MEMBER3 (me, Form, BulletinBoard, MessageBox));
			my shell -> dialogStyle = my dialogStyle = va_arg (arg, int);
			break;
		case XmNdialogTitle:
			Melder_assert (MEMBER3 (me, Form, BulletinBoard, MessageBox));
			text = va_arg (arg, char *);
			#if win
				SetWindowText (my shell -> window, text);
			#elif mac
				ptext [0] = strlen (text); strcpy ((char *) ptext + 1, text);
				SetWTitle (my macWindow, ptext);
			#endif
			break;
		case XmNdialogType:
			Melder_assert (MEMBER (me, MessageBox));
			my shell -> dialogType = my dialogType = va_arg (arg, int);
			break;
		case XmNeditMode: (void) va_arg (arg, int); break;
		case XmNeditable:
			Melder_assert (MEMBER (me, Text));
			my motif.text.editable = va_arg (arg, int);
			break;
		case XmNentryAlignment:
			Melder_assert (MEMBER (me, RowColumn));
			(void) va_arg (arg, int);
			break;
		case XmNheight:
			my height = va_arg (arg, int);
			if (MEMBER (me, Shell)) {
				int maximumHeight =
					#if win
						GetSystemMetrics (SM_CYMAXIMIZED) - GetSystemMetrics (SM_CYCAPTION) -
							GetSystemMetrics (SM_CYSIZEFRAME) - GetSystemMetrics (SM_CYMENU) - 15;
					#elif mac
						GetQDGlobalsScreenBits (& theScreenBits) -> bounds.bottom - 41;
					#endif
				if (my height > maximumHeight) my height = maximumHeight;
			}
			resize = True;
			break;
		case XmNhelpLabelString: {
			char *text = va_arg (arg, char *);
			Melder_assert (MEMBER (me, MessageBox));
			if (my motif.messageBox.helpButton) XtVaSetValues (my motif.messageBox.helpButton, XmNlabelString, text, NULL);
			break;
		}
		case XmNhorizontalScrollBar: {
			/* Have to kill my own bar first. */
			XtDestroyWidget (my motif.scrolledWindow.horizontalBar);
			/* Then replace by new bar. */
			my motif.scrolledWindow.horizontalBar = va_arg (arg, Widget);
			/* Make sure it is in the right position. */
			XtVaSetValues (my motif.scrolledWindow.horizontalBar, XmNorientation, XmHORIZONTAL,
				XmNleftAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM,
				XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 15, XmNheight, 16, 0);
			/*
			 * Note that we may now have a horizontal bar with application-defined scrolling policy,
			 * and a vertical bar with automatic scrolling policy.
			 * This is more flexible than X Motif, but may be simulated in X Motif with the following strategy,
			 * which is compatible with MotifEmulator:
			 *   - set the scrolling policy to XmAUTOMATIC: two scroll bars will be created;
			 *   - ask for the horizontal scroll bar with XtVaGetValues;
			 *   - remove all the callbacks from the horizontal scroll bar with XtRemoveAllCallbacks;
			 *   - add your application-defined callback with XtAddCallback.
			 * We just hope now that X Motif does not look into the XmNscrollingPolicy resource after this...
			 */
		} break;
		case XmNiconName:
			Melder_assert (MEMBER (me, Shell));
			text = va_arg (arg, char *);
			/* Ignore. */
			break;
		case XmNincrement:
			Melder_assert (MEMBER (me, ScrollBar));
			my increment = va_arg (arg, int);
			break;
		case XmNitemCount:
			(void) va_arg (arg, int);
			break;
		case XmNindicatorType:
			(void) va_arg (arg, int);
			break;
		case XmNlabelString:
			Melder_assert (MEMBER2 (me, Label, PushButton));
			text = va_arg (arg, char *);
			Melder_free (my name);
			my name = Melder_strdup (text);
			if (my inMenu) {
				NativeMenuItem_setText (me);
			} else {
				if (MEMBER (me, Label) && ! my motif.cascadeButton.pushButtonLook) {
					int preferredWidth = NativeLabel_preferredWidth (me);
					if (preferredWidth > my width) {
						my width = preferredWidth;
						resize = True;
					}
					#if win
						NativeControl_setTitle (me);
					#elif mac
						if (my isControl)
							NativeControl_setTitle (me);
						invalidateWidget (me);
					#endif
				} else {
					NativeControl_setTitle (me);
				}
			}
			break;
		case XmNleftAttachment: my leftAttachment = va_arg (arg, int);
			attach = True;
			break;
		case XmNleftOffset: my leftOffset = va_arg (arg, int);
			attach = True;
			break;
		case XmNlistSizePolicy: (void) va_arg (arg, int); break;
		case XmNmarginHeight: (void) va_arg (arg, int); break;
		case XmNmarginWidth: (void) va_arg (arg, int); break;
		case XmNmaximum:
			my maximum = va_arg (arg, int);
			if (MEMBER (me, ScrollBar)) scrollset = True;
			else if (MEMBER (me, Scale)) invalidateWidget (me);
			break;
		case XmNmenuHelpWidget:
			(void) va_arg (arg, Widget);
			break;
		case XmNmessageString:
			Melder_assert (MEMBER (me, MessageBox));
			text = va_arg (arg, char *);
			XmTextSetString (my messageText, text);
			break;
		case XmNminimum:
			my minimum = va_arg (arg, int);
			if (MEMBER (me, ScrollBar)) scrollset = True;
			else if (MEMBER (me, Scale)) invalidateWidget (me);
			break;
		case XmNokLabelString: {
			char *text = va_arg (arg, char *);
			Melder_assert (MEMBER (me, MessageBox));
			if (my motif.messageBox.okButton) XtVaSetValues (my motif.messageBox.okButton, XmNlabelString, text, NULL);
			break; }
		case XmNorientation:
			Melder_assert (MEMBER3 (me, RowColumn, ScrollBar, Scale));
			my orientation = va_arg (arg, int);
			break;
		case XmNpacking:
			Melder_assert (MEMBER (me, RowColumn));
			my packing = va_arg (arg, int);
			break;
		case XmNpageIncrement:
			Melder_assert (MEMBER (me, ScrollBar));
			my pageIncrement = va_arg (arg, int);
			break;
		case XmNradioBehavior:
			Melder_assert (MEMBER (me, RowColumn));
			my radioBehavior = va_arg (arg, int);
			break;
		case XmNrightAttachment:
			my rightAttachment = va_arg (arg, int);
			attach = True;
			break;
		case XmNrightOffset:
			my rightOffset = va_arg (arg, int);
			attach = True;
			break;
		case XmNrowColumnType:
			Melder_assert (MEMBER (me, RowColumn));
			my rowColumnType = va_arg (arg, int);
			break;
		case XmNrows: (void) va_arg (arg, int); break;
		case XmNscaleHeight:
			Melder_assert (MEMBER (me, Scale));
			my height = va_arg (arg, int);
			resize = True;
			break;
		case XmNscrollBarDisplayPolicy: (void) va_arg (arg, int); break;
		case XmNscrollingPolicy: (void) va_arg (arg, int); break;
		case XmNselectionPolicy:
			Melder_assert (MEMBER (me, List));
			my selectionPolicy = va_arg (arg, int);
			break;
		case XmNset: {
			int set = va_arg (arg, int);
			XmToggleButtonSetState (me, set, 0);
			break; }
		case XmNsliderSize:
			Melder_assert (MEMBER (me, ScrollBar));
			my sliderSize = va_arg (arg, int);
			scrollset = True;
			break;
		case XmNspacing:
			(void) va_arg (arg, int);
			break;
		case XmNsubMenuId:
			Melder_assert (MEMBER (me, CascadeButton));
			my subMenuId = va_arg (arg, Widget);
			my subMenuId -> popUpButton = me;
			if (my inMenu) {
				#if win
				#elif mac
					if (my nat.entry.item) {
						SetItemMark (my nat.entry.handle, my nat.entry.item, my subMenuId -> nat.menu.id);
						SetItemCmd (my nat.entry.handle, my nat.entry.item, '\033');
					}
				#endif
			}
			break;
		case XmNtitle:
			Melder_assert (MEMBER (me, Shell));
			text = va_arg (arg, char *);
			#if mac
				ptext [0] = strlen (text); strcpy ((char *) ptext + 1, text);
				SetWTitle (my nat.window.ptr, ptext);
			#elif win
				SetWindowText (my window, text);
			#endif
			break;
		case XmNtitleString:
			Melder_assert (MEMBER (me, Scale));
			text = va_arg (arg, char *);
			Melder_free (my name);
			my name = Melder_strdup (text);
			invalidateWidget (me);
			break;
		case XmNtopAttachment:
			my topAttachment = va_arg (arg, int);
			attach = True;
			break;
		case XmNtopItemPosition:
			(void) va_arg (arg, int);
			break;
		case XmNtopOffset:
			my topOffset = va_arg (arg, int);
			attach = True;
			break;
		case XmNtraversalOn:
			(void) va_arg (arg, int);
			break;
		case XmNvalue:
			my value = va_arg (arg, int);
			if (MEMBER (me, ScrollBar)) scrollset = True;
			else if (MEMBER (me, Scale)) {
				#if win
					SendMessage (my window, PBM_SETPOS, (WPARAM) my value, 0);
				#elif mac
					invalidateWidget (me);
				#endif
			}
			break;
		case XmNverticalScrollBar: {
			/* Have to kill my own bar first. */
			XtDestroyWidget (my motif.scrolledWindow.verticalBar);
			/* Then replace by new bar. */
			my motif.scrolledWindow.verticalBar = va_arg (arg, Widget);
			/* Make sure it is in the right position. */
			XtVaSetValues (my motif.scrolledWindow.verticalBar, XmNorientation, XmVERTICAL,
				XmNtopAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM,
				XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 15, XmNwidth, 16, 0);
		} break;
		case XmNvisibleItemCount: {
			int height;
			Melder_assert (MEMBER (me, List));
			my visibleItemCount = va_arg (arg, int);
			height = my visibleItemCount * CELL_HEIGHT;
			if (MEMBER (my parent, ScrolledWindow))
				XtVaSetValues (my parent, XmNheight, height + 16, 0);
			else
				XtVaSetValues (me, XmNheight, height, 0);
		} break;
		case XmNwidth:
			my width = va_arg (arg, int);
			resize = True;
			break;
		case XmNwordWrap: {
			int wrap = va_arg (arg, int);
			#if win
				DestroyWindow (my window);
				if (wrap) {
					my window = CreateWindow ("edit", NULL, WS_CHILD | WS_BORDER | ES_AUTOVSCROLL | ES_MULTILINE | WS_CLIPSIBLINGS,
						my x, my y, my width, my height, my parent -> window, (HMENU) 1, theInstance, NULL);
				} else {
					my window = CreateWindow ("edit", NULL, WS_CHILD | WS_BORDER | ES_AUTOHSCROLL | ES_MULTILINE | WS_CLIPSIBLINGS,
						my x, my y, my width, my height, my parent -> window, (HMENU) 1, theInstance, NULL);
				}
				SetWindowLong (my window, GWL_USERDATA, (long) me);
				SetWindowFont (my window, GetStockFont (ANSI_VAR_FONT), FALSE);
			#endif
		} break;
		case XmNx: my x = va_arg (arg, int); move = True; break;
		case XmNy: my y = va_arg (arg, int); move = True; break;

		default: {
			if (resource < 0 || resource >= sizeof motif_resourceNames / sizeof (char *))
				Melder_flushError ("(XtVaSetValues:) Resource out of range (%d).", resource);
			else
				Melder_flushError ("(XtVaSetValues:) Unknown resource \"%s\".",
					motif_resourceNames [resource]);
			return;   /* Because we do not know how to skip this unknown resource." */
		}
	}

	if (attach) {
		Melder_assert (MEMBER2 (my parent, Form, ScrolledWindow));
		if (my leftAttachment) {
			my x = my leftOffset;
			move = True;
			if (my rightAttachment) {
				my width = my parent -> width - my leftOffset - my rightOffset;
				resize = True;
			}
		} else if (my rightAttachment) {
			my x = my parent -> width - my width - my rightOffset;
			move = True;
		}
		if (my topAttachment) {
			my y = my topOffset;
			move = True;
			if (my bottomAttachment) {
				my height = my parent -> height - my topOffset - my bottomOffset;
				resize = True;
			}
		} else if (my bottomAttachment) {
			my y = my parent -> height - my height - my bottomOffset;
			move = True;
		}
	}
	if (move) {
		#if win
			Native_move (me, 0, 0);
		#elif mac
			int dx, dy;
			dx = my x - my rect.left + (my parent && my widgetClass != xmShellWidgetClass ? my parent -> rect.left : 0);
			dy = my y - my rect.top + (my parent && my widgetClass != xmShellWidgetClass ? my parent -> rect.top : 0);
			Native_move (me, dx, dy);
		#endif
	}
	if (resize) {
		#if mac
			int oldWidth = my rect.right - my rect.left, oldHeight = my rect.bottom - my rect.top;
		#endif
		resizeWidget (me, my width - oldWidth, my height - oldHeight);
	}
	if (scrollset) NativeScrollBar_set (me);
}

#if mac
static Widget _motif_findSubwidget (Widget me, int x, int y) {
	if (! my managed) return NULL;
	if (my firstChild == NULL && my parent != NULL) {
		Rect *rect;
		if (MEMBER (my parent, MenuBar)) return NULL;
		rect = ! MEMBER (me, ScrollBar) && my parent && MEMBER (my parent, ScrolledWindow) ?
			& my parent -> motif.scrolledWindow.clipWindow -> rect : & my rect;
		if (x >= rect -> left && x <= rect -> right && y >= rect -> top && y <= rect -> bottom)
			return me;
		else
			return NULL;
	} else {
		Widget child = my firstChild;
		for (child = my firstChild; child != NULL; child = child -> nextSibling) {
			if (! MEMBER (child, Shell)) {
				Widget result = _motif_findSubwidget (child, x, y);
				if (result) return result;
			}
		}
		return NULL;
	}
}
#endif

static void _motif_manageScrolledWindow (Widget me) {
	int workWidth, workHeight, horzAutomatic, vertAutomatic;
	Widget clipWindow, workWindow, horzBar = my motif.scrolledWindow.horizontalBar, vertBar = my motif.scrolledWindow.verticalBar;
	Melder_assert (my widgetClass == xmScrolledWindowWidgetClass);
	clipWindow = my motif.scrolledWindow.clipWindow;
	workWindow = my motif.scrolledWindow.workWindow;
	if (clipWindow == NULL || horzBar == NULL || vertBar == NULL) return;   /* Apparently during destruction of scrolled window. */
	/*
	 * We must find out if the scrolling policy of each bar is automatic.
	 * Otherwise, we must not change them automatically.
	 */
	horzAutomatic = horzBar -> motif.scrollBar.valueChangedCallbacks.pairs [0].proc == cb_scroll;
	vertAutomatic = vertBar -> motif.scrollBar.valueChangedCallbacks.pairs [0].proc == cb_scroll;
	/*
	 * If the work window has been unmanaged or destroyed, the automatic scroll bars should be empty and insensitive.
	 */
	if (workWindow == NULL || ! workWindow -> managed) {
		if (horzAutomatic)
			XtVaSetValues (horzBar, XmNmaximum, 100, XmNsliderSize, 100, XmNvalue, 0, XmNpageIncrement, 1, 0);
		if (vertAutomatic)
			XtVaSetValues (vertBar, XmNmaximum, 100, XmNsliderSize, 100, XmNvalue, 0, XmNpageIncrement, 1, 0);
		return;
	}
	workWidth = workWindow -> width > 10 ? workWindow -> width : 10;
	workHeight = workWindow -> height > 10 ? workWindow -> height : 10;
	/*
	 * If the scroll bar is automatic, the slider width is set to the visible height of the work window,
	 * and the maximum is set to the entire height of the work window.
	 * If the value becomes greater than the maximum minus the slider size,
	 * the value is reduced and the work window is scrolled up (i.e. moved down).
	 */
	if (horzAutomatic) {
		int maximum = workWidth;
		int sliderSize = workWidth < clipWindow -> width ? workWidth : clipWindow -> width;
		int value = horzBar -> value;
		if (value > maximum - sliderSize) {
			value = maximum - sliderSize;
			workWindow -> x += horzBar -> value - value;
			Native_move (workWindow, horzBar -> value - value, 0);
		}
		XtVaSetValues (horzBar, XmNmaximum, maximum, XmNsliderSize, sliderSize, XmNvalue, value,
			XmNpageIncrement, clipWindow -> width - (CELL_HEIGHT - 1), 0);
	}
	if (vertAutomatic) {   /* Automatic? */
		int maximum = workHeight;
		int sliderSize = workHeight < clipWindow -> height ? workHeight : clipWindow -> height;
		int value = vertBar -> value;
		if (value > maximum - sliderSize) {
			value = maximum - sliderSize;
			workWindow -> y += vertBar -> value - value;
			Native_move (workWindow, 0, vertBar -> value - value);
		}
		XtVaSetValues (vertBar, XmNmaximum, maximum, XmNsliderSize, sliderSize, XmNvalue, value,
			XmNpageIncrement, clipWindow -> height - (CELL_HEIGHT - 1), 0);
	}
}

static void _motif_manage (Widget me) {
	Widget child;
	int x = 2, y = 2;
	int width = 0, height = 0, dw, dh;
	#if mac
		Widget helpMenu = NULL;
	#endif
	/*if (my widgetClass == xmScrolledWindowWidgetClass) return;   /* Ignore. */

	/*
	 * My geometry follows the layout of my children.
	 * If I am a RowColumn, I am growing while my children move.
	 * Because these moves may involve drawing, which needs clipping to my rect,
	 * I have to grow with every managed child separately,
	 * not just after managing all of them.
	 */

	for (child = my firstChild; child; child = child -> nextSibling) {
		if (child -> managed) {
			int dx = 0, dy = 0;   /* By default, the child does not move. */
			if (MEMBER (me, RowColumn)) {
				#if mac
					if (my rowColumnType == XmMENU_BAR && MEMBER (child, CascadeButton) &&
						child -> rightAttachment == XmATTACH_FORM) helpMenu = child; else
				#endif
				{
					if (x > child -> x) dx = x - child -> x;
					if (y > child -> y) dy = y - child -> y;
					child -> x += dx; x = child -> x;
					child -> y += dy; y = child -> y;
					if (my orientation != XmHORIZONTAL)
						#if win
							y += child -> height + 1;
						#elif mac
							y += child -> height + 6;
						#endif
					else
						x += child -> width + 3;
				}
			}
			if (child -> x + child -> width > width)
				width = child -> x + child -> width;
			if (child -> y + child -> height > height)
				height = child -> y + child -> height;
			if (MEMBER4 (me, Shell, Form, BulletinBoard, MessageBox)) {
				/* These widgets grow with their children. */
				dw = width - my width, dh = height - my height;
				if (dw < 0) dw = 0;
				if (dh < 0) dh = 0;
			} else if (MEMBER (me, RowColumn)) {
				/* A RowColumn shrinks and grows with its children. */
				dw = width - my width + 2;
				dh = height - my height + 2;
			} else {   /* ? */
				dw = width - my width;
				dh = height - my height;
			}
			my width += dw;
			my height += dh;
			#if mac
				my rect. right += dw;
				my rect. bottom += dh;
			#endif

			/*
			 * Now that I have grown, there is room for my child to move.
			 */
			if (MEMBER (me, RowColumn)) Native_move (child, dx, dy);

			/*
			 * Resize my attached other children.
			 */
			#if 0
			if (MEMBER3 (me, RowColumn, Form, ScrolledWindow)) {
				Widget child2;
				for (child2 = my firstChild; child2; child2 = child2 -> nextSibling) if (child2 != child && child2 -> managed) {
					int cdx = 0, cdy = 0, cdw = 0, cdh = 0;
					if (child2 -> widgetClass == xmShellWidgetClass) continue;
					if (child2 -> rightAttachment)
						if (child2 -> leftAttachment) cdw = dw; else cdx = dw;
					if (child2 -> bottomAttachment)
						if (child2 -> topAttachment) cdh = dh; else cdy = dh;
					if (cdx || cdy) {
						child2 -> x += cdx;
						child2 -> y += cdy;
						Native_move (child2, cdx, cdy);
					}
					if (cdw || cdh) {
						child2 -> width += cdw;
						child2 -> height += cdh;
						resizeWidget (child2, cdw, cdh);
					}
				}
			}
			#endif
		}
	}
	#if mac
		if (helpMenu) {
			my width = my parent -> width;
			my rect.right = my parent -> rect.right;
			helpMenu -> x = my width - helpMenu -> width - 6;
			helpMenu -> rect.left = helpMenu -> x;
			helpMenu -> rect.right = my rect.right - 6;
		}
	#endif

	#if win
		if (my window) Native_move (me, 0, 0);
	#elif mac
		if (MEMBER (me, Shell)) SizeWindow (my nat.window.ptr, my width, my height, true);
	#endif

	/* If I have grown, I have to notify my parent. */

	if (! MEMBER (me, Shell))
		if (MEMBER5 (my parent, RowColumn, Form, BulletinBoard, Shell, MessageBox)) _motif_manage (my parent);
		else if (MEMBER (my parent, ScrolledWindow)) _motif_manageScrolledWindow (my parent);
}

/***** X TOOLKIT *****/

static void xt_addCallback (XtCallbackList *callbacks, XtCallbackProc proc, XtPointer closure) {
	int i;
	for (i = 0; i < MAXNUM_CALLBACKS; i ++) {
		if (! callbacks -> pairs [i]. proc) {
			callbacks -> pairs [i]. proc = proc;
			callbacks -> pairs [i]. closure = closure;
			return;
		}
	}
	Melder_assert (i < MAXNUM_CALLBACKS);   /* Will always fail if we arrive here. */
}
static void xt_removeCallbacks (XtCallbackList *callbacks) {
	int i;
	for (i = 0; i < MAXNUM_CALLBACKS; i ++) {
		callbacks -> pairs [i]. proc = NULL;
		callbacks -> pairs [i]. closure = NULL;
	}
}

void XtAddCallback (Widget me, int kind, XtCallbackProc proc, XtPointer closure) {
	switch (kind) {
		case XmNactivateCallback:
			my activateCallback = proc; my activateClosure = closure;
		break;
		case XmNcancelCallback:
			Melder_assert (my widgetClass == xmMessageBoxWidgetClass);
			xt_addCallback (& my motif.messageBox.cancelCallbacks, proc, closure);
		break;
		case XmNdecrementCallback:
		break;
		case XmNdefaultActionCallback:
			Melder_assert (my widgetClass == xmListWidgetClass);
			my motif.list.defaultActionCallback = proc; my motif.list.defaultActionClosure = closure;
		break;
		case XmNdestroyCallback:
			my destroyCallback = proc; my destroyClosure = closure;
		break;
		case XmNdragCallback:
			Melder_assert (my widgetClass == xmScrollBarWidgetClass);
			xt_addCallback (& my motif.scrollBar.dragCallbacks, proc, closure);
		break;
		case XmNexposeCallback:
			my exposeCallback = proc; my exposeClosure = closure;
		break;
		case XmNbrowseSelectionCallback:
		case XmNextendedSelectionCallback:
		case XmNsingleSelectionCallback:
			my extendedSelectionCallback = proc; my extendedSelectionClosure = closure;
		break;
		case XmNhelpCallback:
			Melder_assert (my widgetClass == xmMessageBoxWidgetClass);
			xt_addCallback (& my motif.messageBox.helpCallbacks, proc, closure);
		break;
		case XmNincrementCallback:
		break;
		case XmNinputCallback:
			my inputCallback = proc; my inputClosure = closure;
		break;
		case XmNmotionVerifyCallback:
			Melder_assert (my widgetClass == xmTextWidgetClass);
			xt_addCallback (& my motif.text.motionVerifyCallbacks, proc, closure);
		break;
		case XmNmoveCallback:
			Melder_assert (my widgetClass == xmDrawingAreaWidgetClass);
			xt_addCallback (& my motif.drawingArea.moveCallbacks, proc, closure);
		break;
		case XmNokCallback:
			Melder_assert (my widgetClass == xmMessageBoxWidgetClass);
			xt_addCallback (& my motif.messageBox.okCallbacks, proc, closure);
		break;
		case XmNpageIncrementCallback:
		break;
		case XmNpageDecrementCallback:
		break;
		case XmNresizeCallback:
			my resizeCallback = proc; my resizeClosure = closure;
		break;
		case XmNvalueChangedCallback:
			if (my widgetClass == xmScrollBarWidgetClass)
				xt_addCallback (& my motif.scrollBar.valueChangedCallbacks, proc, closure);
			else if (my widgetClass == xmTextWidgetClass)
				xt_addCallback (& my motif.text.valueChangedCallbacks, proc, closure);
			else if (my widgetClass == xmToggleButtonWidgetClass)
				xt_addCallback (& my motif.toggleButton.valueChangedCallbacks, proc, closure);
			else Melder_assert (False);
		break;
		default:
			if (kind < 0 || kind >= sizeof motif_resourceNames / sizeof (char *))
				Melder_flushError ("(XtAddCallback:) Callback name out of range (%d).", kind);
			else
				Melder_flushError ("(XtAddCallback:) Unknown callback \"%s\".", motif_resourceNames [kind]);
	}
}

XtWorkProcId XtAppAddWorkProc (XtAppContext appContext, XtWorkProc workProc, XtPointer closure) {
	int i = 1;
	(void) appContext;
	while (i < 10 && theWorkProcs [i]) i ++;
	Melder_assert (i < 10);
	theWorkProcs [i] = workProc;
	theWorkProcClosures [i] = closure;
	theNumberOfWorkProcs ++;
	return i;
}

void XtRemoveWorkProc (XtWorkProcId id) {
	theWorkProcs [id] = NULL;
	theNumberOfWorkProcs --;
}

#ifdef __MACH__
static pascal void timerAction (EventLoopTimerRef timer, void *closure) {
	long i = (int) closure;
	(void) timer;
	theTimeOutProcs [i] (theTimeOutClosures [i], & i);
	XtRemoveTimeOut (i);
}
#endif

XtIntervalId XtAppAddTimeOut (XtAppContext appContext, unsigned long interval, XtTimerCallbackProc proc, XtPointer closure) {
	long i = 1;
	(void) appContext;
	while (i < 10 && theTimeOutProcs [i]) i ++;
	Melder_assert (i < 10);
	theTimeOutProcs [i] = proc;
#ifdef __MACH__
{
	EventLoopRef mainLoop = GetMainEventLoop ();
	static EventLoopTimerUPP timerUPP;
	if (timerUPP == NULL) timerUPP = NewEventLoopTimerUPP (timerAction);
	InstallEventLoopTimer (mainLoop, interval * kEventDurationMillisecond, 0, timerUPP, (void *) i, & theTimers [i]);
}
#else
	theTimeOutStarts [i] = clock ();
	theTimeOutIntervals [i] = (interval * (double) CLOCKS_PER_SEC) / 1000;
#endif
	theTimeOutClosures [i] = closure;
	theNumberOfTimeOuts ++;
	return i;
}

void XtRemoveTimeOut (XtIntervalId id) {
	theTimeOutProcs [id] = NULL;
	theNumberOfTimeOuts --;
	#ifdef __MACH__
		RemoveEventLoopTimer (theTimers [id]);
	#endif
}

void XtDestroyWidget (Widget me) {
	Widget subview = my firstChild;
	/*
	 * Prevent subsequent messages.
	 */
	#if win
		HWND natWindow;
		if (my window) SetWindowLong (my window, GWL_USERDATA, 0);
	#endif
	if (my widgetClass == xmShellWidgetClass) {
		XtUnmanageChild (me);
		#if win
			natWindow = my window;   /* Save for destroy. */
			my window = NULL;
		#elif mac
			my macWindow = NULL;   /* Notify children. */
		#endif
	}
	if (MEMBER3 (me, Form, BulletinBoard, MessageBox) && MEMBER (my parent, Shell) &&
		 #if win
		 	my parent -> window
		 #elif mac
			my parent -> macWindow
		#endif
		)
	{
		XtDestroyWidget (my parent);
		return;
	}
	while (subview) {
		Widget nextSibling = subview -> nextSibling;   /* Save... */
		if (subview -> widgetClass != xmShellWidgetClass)   /* FIX instead of Xm's being_destroyed mark. */
			XtDestroyWidget (subview);
		subview = nextSibling;   /* ...because we can't dereference dead subview. */
	}
	if (my destroyCallback) my destroyCallback (me, my destroyClosure, NULL);
	switch (my widgetClass) {
		case xmLabelWidgetClass:
			if (my parent -> widgetClass == xmPulldownMenuWidgetClass) {
			} else if (my motif.cascadeButton.pushButtonLook) {
				NativeControl_destroy (me);
			} else {
				/* XtUnmanageChild (me); // Crashes! Therefore: */
				#if win
					DestroyWindow (my window);
				#elif mac
					if (my isControl) {
						NativeControl_destroy (me);
					} else {
						_motif_clipOn (me);
						EraseRect (& my rect);
						motif_mac_clipOff ();
					}
				#endif
			}
		break;
		case xmScaleWidgetClass:	 {
			#if win
				DestroyWindow (my window);
			#elif mac
				_motif_clipOn (me);
				EraseRect (& my rect);
				motif_mac_clipOff ();
			#endif
		} break;
		case xmShellWidgetClass: {
			#if win
				DestroyWindow (natWindow);
			#elif mac
				DisposeWindow (my nat.window.ptr);
			#endif
			_motif_removeShell (me);
		} break;
		case xmListWidgetClass: {
			#if win
				DestroyWindow (my window);
			#elif mac
				_motif_clipOn (me);
				LDispose (my nat.list.handle);
				motif_mac_clipOff ();
			#endif
		} break;
		case xmDrawingAreaWidgetClass:
		case xmRowColumnWidgetClass:
		case xmFormWidgetClass:
		case xmMessageBoxWidgetClass:
		case xmBulletinBoardWidgetClass: {
			#if win
				DestroyWindow (my window);
			#endif
		} break;
		case xmTextWidgetClass: {
			if (me == _motif_activeTextWidget)
				_motif_activeTextWidget = NULL;   /* Remove dangling reference. */
			if (me == my shell -> activeTextWidget)
				my shell -> activeTextWidget = NULL;   /* Remove dangling reference. */
			#if win
				DestroyWindow (my window);
			#elif mac
				if (my isControl) {
					NativeControl_destroy (me);
				} else {
					_motif_clipOn (me);
					TEDispose (my nat.text.handle);
					EraseRect (& my rect);
					motif_mac_clipOff ();
				}
			#endif
		} break;
		case xmPushButtonWidgetClass: {
			if (my parent -> widgetClass == xmPulldownMenuWidgetClass) {
				#if mac
					DeleteMenuItem (my nat.entry.handle, my nat.entry.item);
					subview = my parent -> firstChild;
					while (subview) {
						if (subview -> nat.entry.item > my nat.entry.item)
							subview -> nat.entry.item -= 1;
						subview = subview -> nextSibling;
					}
				#endif
			} else {
				if (me == my shell -> defaultButton)
					my shell -> defaultButton = NULL;   /* Remove dangling reference. */
				if (me == my shell -> cancelButton)
					my shell -> cancelButton = NULL;   /* Remove dangling reference. */
				NativeControl_destroy (me);
			}
		} break;
		case xmToggleButtonWidgetClass:
		case xmScrollBarWidgetClass: {
			if (my parent -> widgetClass != xmPulldownMenuWidgetClass) {
				NativeControl_destroy (me);
			}
		} break;
		case xmScrolledWindowWidgetClass: {
			/* The scroll bars will be destroyed automatically because they are my children. */
		} break;
		case xmSeparatorWidgetClass: {
			if (my parent -> widgetClass == xmPulldownMenuWidgetClass) {
				#if mac
					DeleteMenuItem (my nat.entry.handle, my nat.entry.item);
					for (subview = my parent -> firstChild; subview; subview = subview -> nextSibling) {
						if (subview -> nat.entry.item > my nat.entry.item)
							subview -> nat.entry.item -= 1;
					}
				#endif
			}
		} break;
		case xmPulldownMenuWidgetClass: {
			#if win
				if (MEMBER (my parent, MenuBar))
					RemoveMenu (my parent -> nat.menu.handle, (UINT) my nat.menu./*handle*/id, MF_BYCOMMAND);
				DestroyMenu (my nat.menu.handle);
				theMenus [my nat.menu.id] = NULL;
			#elif mac
				DeleteMenu (my nat.menu.id);
				DisposeMenu (my nat.menu.handle);
				theMenus [my nat.menu.id] = NULL;
			#endif
		} break;
	}
	Melder_free (my name);
	if (my parent && me == my parent -> firstChild)   /* Remove dangling reference. */
		my parent -> firstChild = my nextSibling;
	if (my previousSibling)   /* Remove dangling reference. */
		my previousSibling -> nextSibling = my nextSibling;
	if (my nextSibling)   /* Remove dangling reference: two-way linked list. */
		my nextSibling -> previousSibling = my previousSibling;
	if (my parent && MEMBER (my parent, ScrolledWindow)) {
		if (me == my parent -> motif.scrolledWindow.workWindow) {
			my parent -> motif.scrolledWindow.workWindow = NULL;
			_motif_manageScrolledWindow (my parent);
		} else if (me == my parent -> motif.scrolledWindow.horizontalBar) {
			my parent -> motif.scrolledWindow.horizontalBar = NULL;
		} else if (me == my parent -> motif.scrolledWindow.verticalBar) {
			my parent -> motif.scrolledWindow.verticalBar = NULL;
		} else if (me == my parent -> motif.scrolledWindow.clipWindow) {
			my parent -> motif.scrolledWindow.clipWindow = NULL;
		}
	}
	Melder_free (me);
	numberOfWidgets --;
}

void XtFree (char *me) {
	Melder_free (me);
}

Boolean XtIsManaged (Widget me) { return my managed; }

Boolean XtIsShell (Widget me) {
	return my widgetClass == xmShellWidgetClass;
}

void XtMapWidget (Widget me) {
	switch (my widgetClass) {
		case xmShellWidgetClass:
			#if mac
				ShowWindow (my nat.window.ptr);
			#elif win
				ShowWindow (my window, me == theApplicationShell ? theCommandShow : theCommandShow);
				//UpdateWindow (my window);
			#endif
			break;
		default:
			break;
	}
}

static void mapWidget (Widget me) {
	Widget child;
	Melder_assert (my widgetClass != xmPulldownMenuWidgetClass);
	if (my inMenu) {
		#if win
			int position = NativeMenuItem_getPosition (me);
			switch (my widgetClass) {
				case xmPushButtonWidgetClass: {
					InsertMenu (my nat.entry.handle, position, MF_STRING | MF_BYPOSITION | ( my insensitive ? MF_GRAYED : MF_ENABLED ),
						my nat.entry.id, motif_win_expandAmpersands (my name));
				} break;
				case xmToggleButtonWidgetClass: {
					InsertMenu (my nat.entry.handle, position, MF_STRING | MF_UNCHECKED | MF_BYPOSITION | ( my insensitive ? MF_GRAYED : MF_ENABLED ),
						my nat.entry.id, motif_win_expandAmpersands (my name));
				} break;
				case xmCascadeButtonWidgetClass: {
					my nat.entry.id = (UINT) my subMenuId -> nat.menu.handle;
					InsertMenu (my nat.entry.handle, position, MF_POPUP | MF_BYPOSITION | ( my insensitive ? MF_GRAYED : MF_ENABLED ),
						my nat.entry.id, motif_win_expandAmpersands (my name));
				} break;
				case xmSeparatorWidgetClass: {
					InsertMenu (my nat.entry.handle, position, MF_SEPARATOR | MF_BYPOSITION, my nat.entry.id, motif_win_expandAmpersands (my name));
				} break;
			}
		#elif mac
			Widget sibling;
			int item;
			/*
			 * Insert a native menu item.
			 */
			if (my nat.entry.item) return;
			/*
			 * A Macintosh menu item shall know its "item number",
			 * which is its dynamic position in the current shape of the menu.
			 */
			/*
			 * Position of new native item.
			 */
			item = NativeMenuItem_getPosition (me);
			/*
			 * Shift all other item numbers.
			 */
			for (sibling = my parent -> firstChild; sibling; sibling = sibling -> nextSibling) {
				if (sibling -> managed && sibling -> widgetClass != xmPulldownMenuWidgetClass && sibling -> nat.entry.item >= item)
					sibling -> nat.entry.item += 1;
			}
			/*
			 * Remember.
			 */
			my nat.entry.item = item;
			/*
			 * Set text, sensitivity, submenu. BUGS: should also set toggle state and accelerator text.
			 */
			if (my widgetClass == xmSeparatorWidgetClass) {
				InsertMenuItem (my nat.entry.handle, "\p-", my nat.entry.item - 1);
			} else {
				InsertMenuItem (my nat.entry.handle, "\p ", my nat.entry.item - 1);
				PfromCstr (mac_text, my name);
				SetMenuItemText (my nat.entry.handle, my nat.entry.item, mac_text);
				if (my insensitive) DisableMenuItem (my nat.entry.handle, my nat.entry.item);
				if (mac_text [mac_text [0]] == ':')
					SetItemStyle (my nat.entry.handle, my nat.entry.item, underline);
				if (my widgetClass == xmCascadeButtonWidgetClass) {
					if (my subMenuId) SetItemMark (my nat.entry.handle, my nat.entry.item, my subMenuId -> nat.menu.id);
					SetItemCmd (my nat.entry.handle, my nat.entry.item, '\033');
				}
				/*
				 * All the items in the Apple Menu folder are added to the Apple menu.
				 */
				if (my parent -> name [0] == appleMark)
					AppendResMenu (my nat.entry.handle, 'DRVR');
			}
		#endif
	} else switch (my widgetClass) {
		#if win
		case xmBulletinBoardWidgetClass:
		case xmDrawingAreaWidgetClass:
		case xmScrolledWindowWidgetClass:
		case xmFormWidgetClass:
		case xmRowColumnWidgetClass:
		case xmMessageBoxWidgetClass:
			ShowWindow (my window, SW_SHOW); break;
		#endif
		case xmShellWidgetClass: {
			#if win
				ShowWindow (my window, me == theApplicationShell ? theCommandShow : theCommandShow);
			#elif mac
				SelectWindow (my nat.window.ptr);
				ShowWindow (my nat.window.ptr);
			#endif
			if (my dialogStyle == XmDIALOG_FULL_APPLICATION_MODAL)
				#if mac
					_motif_modalDialog = my nat.window.ptr
				#endif
				;
		} break;
		case xmMenuBarWidgetClass: {
			#if win
				DrawMenuBar (my shell -> window);   // every window has its own menu bar
			#elif mac
				DrawMenuBar ();   /* There is a single Mac menu bar for all windows. */
			#endif
		} break;
		case xmPushButtonWidgetClass: NativeControl_show (me); break;
		case xmToggleButtonWidgetClass: NativeControl_show (me); break;
		case xmScrollBarWidgetClass: {
			#if win
			if (! my window) {
				my window = CreateWindow ("scrollbar", my name, WS_CHILD |
					( my orientation == XmHORIZONTAL ? SBS_HORZ : SBS_VERT ) | WS_CLIPSIBLINGS,
					my x, my y, my width, my height, my parent -> window, (HMENU) 1, theInstance, NULL);
				SetWindowLong (my window, GWL_USERDATA, (long) me);
				NativeScrollBar_set (me);
			}
			#endif
			NativeControl_show (me);
		} break;
		case xmLabelWidgetClass: {
			if (my motif.cascadeButton.pushButtonLook) {
				NativeControl_show (me);
			} else if (my parent -> widgetClass != xmMenuBarWidgetClass) {
				#if win
					ShowWindow (my window, SW_SHOW);
				#elif mac
					if (my isControl)
						NativeControl_show (me);
					else
						invalidateWidget (me);
				#endif
			}
		} break;
		case xmScaleWidgetClass: {
			#if win
				NativeControl_show (me);
			#elif mac
				invalidateWidget (me);
			#endif
		} break;
		case xmTextWidgetClass: {
			#if win
				ShowWindow (my window, SW_SHOW);
			#elif mac
				if (my isControl) {
					NativeControl_show (me);
				} else {
					_motif_clipOn (me);
					if (haveAppearance) {
						DrawThemeEditTextFrame (& my rect, kThemeStateActive);
					} else {
						FrameRect (& my rect);
					}
					TEUpdate (& my rect, my nat.text.handle);
					_motif_clipOffValid (me);
				}
			#endif
		} break;
		case xmListWidgetClass: {
			#if win
				ShowWindow (my window, SW_SHOW);
			#elif mac
				_motif_clipOn (me);
				LSetDrawingMode (true, my nat.list.handle);
				_motif_clipOffInvalid (me);
			#endif
		} break;
		default:
			break;
	}
	for (child = my firstChild; child != NULL; child = child -> nextSibling)
		if (child -> managed) mapWidget (child);
}

void XtManageChild (Widget me) {
	if (my managed) return;

	if (MEMBER (me, ScrolledWindow)) {
		XtManageChild (my motif.scrolledWindow.horizontalBar);
		XtManageChild (my motif.scrolledWindow.verticalBar);
		/*XtManageChild (my motif.scrolledWindow.clipWindow);*/
	}

	my managed = 1;

	/* Geometry management if my parent is a manager. */

	if (! MEMBER (me, Shell)) {
		if (MEMBER5 (my parent, RowColumn, Form, BulletinBoard, Shell, MessageBox)) _motif_manage (my parent);
		if (MEMBER (me, ScrolledWindow)) _motif_manageScrolledWindow (me);
		if (MEMBER (my parent, ScrolledWindow)) _motif_manageScrolledWindow (my parent);
	}

	/* Map to the screen (suppose that mapped_when_managed is true). */
	/* Condition: the entire up chain has been managed. */
	/* Shells or their immediate manager children can be mapped directly. */

	if (my parent && MEMBER (my parent, Shell) && MEMBER3 (me, Form, BulletinBoard, MessageBox)) {
		my parent -> managed = 1;
		mapWidget (my parent);
	} else if (my inMenu) {
		mapWidget (me);
	} else {
		int visible = True;
		Widget widget;
		for (widget = me; widget != NULL; widget = widget -> parent) {
			if (! widget -> managed &&   /* If a parent is invisible, so are its children. */
					! MEMBER (widget, PulldownMenu))   /* The exception: "shown" even if not popped up. */
				{ visible = False; break; }
			if (MEMBER (widget, Shell)) break;   /* Root: end of chain. */
		}
		if (visible) mapWidget (me);
	}

	/* I may have been created by XmCreateScrolledText or XmCreateScrolledList. */
	/* In that case, my parent should be managed. */

	if (MEMBER2 (me, Text, List) && MEMBER (my parent, ScrolledWindow)) XtManageChild (my parent);
}

void XtManageChildren (WidgetList children, Cardinal num_children) {
	Cardinal i;
	for (i = 0; i < num_children; i ++) XtManageChild (children [i]);
}

Widget XtParent (Widget me) {
	return my parent;
}

void XtRemoveCallback (Widget w, int kind, XtCallbackProc proc, XtPointer closure) {
	(void) proc;
	(void) closure;
	XtRemoveAllCallbacks (w, kind);
}

void XtRemoveCallbacks (Widget w, int kind, XtCallbackProc proc) {
	(void) proc;
	XtRemoveAllCallbacks (w, kind);
}

void XtRemoveAllCallbacks (Widget me, int kind) {
	switch (kind) {
		case XmNactivateCallback: my activateCallback = NULL; break;
		case XmNdestroyCallback: my destroyCallback = NULL; break;
		case XmNdragCallback: xt_removeCallbacks (& my motif.scrollBar.dragCallbacks); break;
		case XmNexposeCallback: my exposeCallback = NULL; break;
		case XmNextendedSelectionCallback: my extendedSelectionCallback = NULL; break;
		case XmNinputCallback: my inputCallback = NULL; break;
		case XmNresizeCallback: my resizeCallback = NULL; break;
		case XmNvalueChangedCallback: xt_removeCallbacks (& my motif.scrollBar.valueChangedCallbacks); break;
		default:
			if (kind < 0 || kind >= sizeof motif_resourceNames / sizeof (char *))
				Melder_flushError ("(XtRemoveCallback:) Callback name out of range (%d).", kind);
			else
				Melder_flushError ("(XtRemoveCallback:) Unknown callback \"%s\".", motif_resourceNames [kind]);
	}
}

void XtSetKeyboardFocus (Widget tree, Widget descendant) {
	(void) tree;
	(void) descendant;
	#if win
		/*SetFocus (descendant -> window);*/
	#endif
}

void XtSetSensitive (Widget me, Boolean value) {
	if (my insensitive != value) return;
	my insensitive = ! value;
	if (! my parent) return;
	switch (my widgetClass) {
		case xmPushButtonWidgetClass:
		case xmToggleButtonWidgetClass: {
			if (my inMenu) {
				NativeMenuItem_setSensitive (me);
			} else {
				NativeControl_setSensitive (me);
			}
		} break;
		case xmScrollBarWidgetClass: NativeControl_setSensitive (me); break;
		case xmCascadeButtonWidgetClass: {
			if (MEMBER (my parent, MenuBar) || my inMenu) {
				if (my subMenuId) {
					if (value)
						#if mac
							EnableMenuItem (my subMenuId -> nat.menu.handle, 0)
						#endif
						;
					else
						#if mac
							DisableMenuItem (my subMenuId -> nat.menu.handle, 0)
						#endif
						;
					#if mac
						DrawMenuBar ();
					#endif
				}
			} else if (my motif.cascadeButton.pushButtonLook) {
				NativeControl_setSensitive (me);
			} else {
				#if win
					NativeControl_setSensitive (me);
				#elif mac
					if (my isControl)
						NativeControl_setSensitive (me);
					else
						invalidateWidget (me);
				#endif
			}
		} break;
		case xmPulldownMenuWidgetClass: {
			if (my popUpButton)
				XtSetSensitive (my popUpButton, value);
		} break;
		default:
			break;
	}
}

void XtUnmanageChild (Widget me) {
	if (! my managed) return;
	if (my inMenu) {
		if (! MEMBER (me, PulldownMenu)) NativeMenuItem_delete (me);
	} else switch (my widgetClass) {
		case xmShellWidgetClass:
			#if win
				if (my activeTextWidget == _motif_activeTextWidget)
					_motif_activeTextWidget = NULL;
				ShowWindow (my window, SW_HIDE);
			#elif mac
				if (my nat.window.ptr == FrontWindow ())
					_motif_activeTextWidget = NULL;
				if (my nat.window.ptr == _motif_modalDialog)
					_motif_modalDialog = NULL;
				HideWindow (my nat.window.ptr);
			#endif
			if (my firstChild && MEMBER3 (my firstChild, Form, BulletinBoard, MessageBox))
				my firstChild -> managed = 0;
			break;
		case xmPushButtonWidgetClass: NativeControl_hide (me); break;
		case xmToggleButtonWidgetClass: NativeControl_hide (me); break;
		case xmLabelWidgetClass:
			if (my motif.cascadeButton.pushButtonLook) NativeControl_hide (me);
			else if (! MEMBER (my parent, MenuBar))
				#if win
					NativeControl_hide (me);
				#elif mac
					if (my isControl)
						NativeControl_hide (me);
					else
						invalidateWidget (me);
				#endif
			break;
		case xmScrollBarWidgetClass: NativeControl_hide (me); break;
		case xmFormWidgetClass:
		case xmBulletinBoardWidgetClass:
		case xmMessageBoxWidgetClass:
			if (MEMBER (my parent, Shell)) XtUnmanageChild (my parent);
			break;
		case xmTextWidgetClass: {
			invalidateWidget (me);
			if (me == _motif_activeTextWidget) {
				#if mac
					if (my isControl) {
						NativeControl_hide (me);
					} else {
						_motif_clipOn (me);
						TEDeactivate (my nat.text.handle);   /* Remove visible blinking cursor. */
						motif_mac_clipOff ();
					}
				#endif
				_motif_activeTextWidget = NULL;   /* Prevent extraneous cursor blinking. */
			}
		} break;
		default:
			invalidateWidget (me);
			break;
	}

	my managed = 0;

	if (! MEMBER (me, Shell))
		if (MEMBER5 (my parent, RowColumn, Form, BulletinBoard, Shell, MessageBox)) _motif_manage (my parent);
		else if (MEMBER (my parent, ScrolledWindow)) _motif_manageScrolledWindow (my parent);
}

void XtUnmanageChildren (WidgetList children, Cardinal num_children) {
	Cardinal i;
	for (i = 0; i < num_children; i ++) XtUnmanageChild (children [i]);
}

#if mac
	static pascal OSErr _motif_processOpenApplicationMessage (const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefCon) {
		(void) theAppleEvent;
		(void) reply;
		(void) handlerRefCon;
		return noErr;
		#ifdef __MACH__
		Melder_warning ("Open app event.");
		#endif
	}
	static pascal OSErr _motif_processQuitApplicationMessage (const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefCon) {
		/*
			This hook shall return noErr if it is OK to quit,
			otherwise userCanceledErr. It should not exit the application by itself. Here it does!!
		*/
		(void) theAppleEvent;
		(void) reply;
		(void) handlerRefCon;
		if (theQuitApplicationCallback)
			return theQuitApplicationCallback () ? noErr : userCanceledErr;   /* BUG anti Mac documentation */
		exit (0);
		return noErr;
	}
	static pascal OSErr _motif_processOpenDocumentsMessage (const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefCon) {
		AEDescList documentList;
		OSErr err;
		long document, numberOfDocuments;
		(void) reply;
		(void) handlerRefCon;
		err = AEGetParamDesc (theAppleEvent, keyDirectObject, typeAEList, & documentList);
		if (err) return err;
		err = AECountItems (& documentList, & numberOfDocuments);
		for (document = 1; document <= numberOfDocuments; document ++ ) {
			AEKeyword keyWord;
			DescType typeCode;
			Size actualSize;
			#ifdef __MACH__
				FSRef fsref;
				err = AEGetNthPtr (& documentList, document, typeFSRef, & keyWord, & typeCode, & fsref, sizeof (fsref), & actualSize);
				if (! err) {
					struct MelderFile file;
					Melder_machToFile (& fsref, & file);
					if (theOpenDocumentCallback)
						theOpenDocumentCallback (& file);
				}
			#else
				FSSpec fspec;
				err = AEGetNthPtr (& documentList, document, typeFSS, & keyWord, & typeCode, & fspec, sizeof (fspec), & actualSize);
				if (! err) {
					struct MelderFile file;
					Melder_macToFile (& fspec, & file);
					if (theOpenDocumentCallback)
						theOpenDocumentCallback (& file);
				}
			#endif
		}
		AEDisposeDesc (& documentList);
		return noErr;
	}
	static pascal OSErr _motif_processSignal (const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefCon) {
		static int duringAppleEvent = FALSE;
		(void) reply;
		(void) handlerRefCon;
		if (! duringAppleEvent) {
			char *buffer;
			long actualSize;
			duringAppleEvent = TRUE;
			AEInteractWithUser (kNoTimeOut, NULL, NULL);
			AEGetParamPtr (theAppleEvent, 1, typeChar, NULL, NULL, 0, & actualSize);
			buffer = malloc (actualSize);
			AEGetParamPtr (theAppleEvent, 1, typeChar, NULL, & buffer [0], actualSize, NULL);
			if (theUserMessageCallback)
				theUserMessageCallback (buffer);
			free (buffer);
			duringAppleEvent = FALSE;
		}
		return noErr;
	}
#elif win
	static LRESULT CALLBACK windowProc (HWND window, UINT message, WPARAM wParam, LPARAM lParam);
#endif

Widget XtInitialize (void *dum1, const char *name,
	void *dum2, int dum3, unsigned int *argc, char **argv)
{
	(void) dum1;
	(void) dum2;
	(void) dum3;
	(void) argc;
	#if mac
		(void) argv;
		#if ! carbon
			MaxApplZone ();
			InitGraf (& qd.thePort);
			InitFonts ();
			InitWindows ();
			InitMenus ();
			TEInit ();
			InitDialogs (0L);
			LMSetFractEnable (0);
			#ifndef haveAppearance
				haveAppearance = Melder_systemVersion >= 0x0805;   /* Only 1.1 and higher, and assume WindowsLib 8.5. */
			#endif
		#endif
		if (haveAppearance)
			RegisterAppearanceClient ();
		InitCursor ();
		FlushEvents (everyEvent, 0);
		AEInstallEventHandler (kCoreEventClass, kAEOpenApplication, NewAEEventHandlerUPP (_motif_processOpenApplicationMessage), 0, false);
		AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP (_motif_processQuitApplicationMessage), 0, false);
		AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP (_motif_processOpenDocumentsMessage), 0, false);
		AEInstallEventHandler (758934755, 0, NewAEEventHandlerUPP (_motif_processSignal), 0, false);
		if (Melder_systemVersion >= 0x0800) USE_QUESTION_MARK_HELP_MENU = 1;
	#elif win
	{
		HWND window;
		WNDCLASSEX windowClass;
		sprintf (theApplicationName, argv [0] ? argv [0] : "Unknown");
		sprintf (theApplicationClassName, "PraatShell%d %s", PRAAT_WINDOW_CLASS_NUMBER, theApplicationName);
		sprintf (theWindowClassName, "PraatChildWindow%d %s", PRAAT_WINDOW_CLASS_NUMBER, theApplicationName);
		sprintf (theDrawingAreaClassName, "PraatDrawingArea%d %s", PRAAT_WINDOW_CLASS_NUMBER, theApplicationName);
		window = FindWindow (theWindowClassName, NULL);
		if (window != NULL) {
			if (IsIconic (window)) ShowWindow (window, SW_RESTORE);
			SetForegroundWindow (window);
			if (theOpenDocumentCallback && argv [3] [0]) {
				struct MelderFile file;
				Melder_relativePathToFile (argv [3], & file);
				theOpenDocumentCallback (& file);
			}
			exit (0);   // possible problem
		}

		theInstance = (HINSTANCE) atol (argv [1]);

		windowClass. cbSize = sizeof (WNDCLASSEX);
		windowClass. style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS |
			CS_OWNDC   // crucial: see GraphicsWindows95_init ()
			;
		windowClass. lpfnWndProc = windowProc;
		windowClass. cbClsExtra = 0;
		windowClass. cbWndExtra = 0;
		windowClass. hInstance = theInstance;
		windowClass. hIcon = NULL;
		windowClass. hCursor = LoadCursor (NULL, IDC_ARROW);
		windowClass. hbrBackground = /*(HBRUSH) (COLOR_WINDOW + 1)*/ GetStockBrush (LTGRAY_BRUSH);
		windowClass. lpszMenuName = NULL;
		windowClass. lpszClassName = theWindowClassName;
		windowClass. hIconSm = NULL;
		RegisterClassEx (& windowClass);
		windowClass. hbrBackground = GetStockBrush (WHITE_BRUSH);
		windowClass. lpszClassName = theDrawingAreaClassName;
		RegisterClassEx (& windowClass);
		windowClass. lpszClassName = theApplicationClassName;
		RegisterClassEx (& windowClass);
		InitCommonControls ();
		theCommandShow = atoi (argv [2]);
	}
	#endif
	return theApplicationShell = XmCreateShell (NULL, name, NULL, 0);
}

Widget XtVaAppInitialize (XtAppContext *dum1, const char *name,
	void *dum2, int dum3, unsigned int *argc, char **argv, void *dum4, void *dum5)
{
	(void) dum1;
	(void) dum4;
	(void) dum5;
	return XtInitialize (0, name, dum2, dum3, argc, argv);
}

Widget XtVaCreateManagedWidget (const char *name, int widgetClass, Widget parent, ...) {
	Widget me;
	va_list arg;
	va_start (arg, parent);
	me = vaCreateWidget (widgetClass, parent, name, arg);
	_motif_setValues (me, arg);
	va_end (arg);
	XtManageChild (me);
	return me;
}

Widget XtVaCreateWidget (const char *name, int widgetClass, Widget parent, ...) {
	Widget me;
	va_list arg;
	va_start (arg, parent);
	me = vaCreateWidget (widgetClass, parent, name, arg);
	_motif_setValues (me, arg);
	va_end (arg);
	return me;
}

void XtVaGetValues (Widget me, ...) {
	char *text;
	#if mac
		Str255 ptext;
	#endif
	int resource;
	va_list arg;
	va_start (arg, me);
	while ((resource = va_arg (arg, int)) != 0) switch (resource) {
		case XmNx: *va_arg (arg, int *) = my x; break;
		case XmNy: *va_arg (arg, int *) = my y; break;
		case XmNwidth: *va_arg (arg, int *) = my width; break;
		case XmNheight: *va_arg (arg, int *) = my height; break;
		case XmNtitle:
		case XmNiconName:
			Melder_assert (my widgetClass == xmShellWidgetClass);
			#if mac
				GetWTitle (my nat.window.ptr, ptext);
				text = Melder_malloc (ptext [0] + 1);
				strncpy (text, (char *) ptext + 1, ptext [0]);
				text [ptext [0]] = 0;
				*va_arg (arg, char **) = text;
			#elif win
				*va_arg (arg, char **) = NULL;
			#endif
			break;
		case XmNlabelString:
		case XmNtitleString:
			Melder_assert (my widgetClass == xmLabelWidgetClass ||
								my widgetClass == xmScaleWidgetClass);
			text = Melder_strdup (my name);
			*va_arg (arg, char **) = text;
			break;
		case XmNdialogTitle:
			Melder_assert (my widgetClass == xmFormWidgetClass ||
								my widgetClass == xmBulletinBoardWidgetClass ||
								my widgetClass == xmMessageBoxWidgetClass);
			#if mac
				GetWTitle (my macWindow, ptext);
				text = Melder_malloc (ptext [0] + 1);
				strncpy (text, (char *) ptext + 1, ptext [0]);
				text [ptext [0]] = 0;
				*va_arg (arg, char **) = text;
			#elif win
				*va_arg (arg, char **) = NULL;
			#endif
			break;
		case XmNnumChildren: {
			Cardinal numChildren = 0;
			Widget child;
			for (child = my firstChild; child != NULL; child = child -> nextSibling) numChildren ++;
			*va_arg (arg, Cardinal *) = numChildren;
			break; }
		case XmNtopItemPosition: {
			int d, n;
			Melder_assert (my widgetClass == xmListWidgetClass);
			#if win
			d = my parent -> widgetClass == xmScrolledWindowWidgetClass ?
				(my parent -> motif.scrolledWindow.clipWindow -> y -
				 my parent -> motif.scrolledWindow.workWindow -> y + 5) / CELL_HEIGHT + 1 : 1;
			#elif mac
			d = my parent -> widgetClass == xmScrolledWindowWidgetClass ?
				(my parent -> motif.scrolledWindow.clipWindow -> rect.top -
				 my parent -> motif.scrolledWindow.workWindow -> rect.top + 5) / CELL_HEIGHT + 1 : 1;   /* Different? */
			#endif
			if (d < 1) d = 1;
			n =
				#if mac
					(** my nat.list.handle). dataBounds. bottom
				#elif win
					0
				#endif
				;
			if (d > n) d = 0;
			*va_arg (arg, int *) = d;
			break; }
 		case XmNitemCount:
			Melder_assert (my widgetClass == xmListWidgetClass);
			*va_arg (arg, int *) =
				#if mac
					(** my nat.list.handle). dataBounds. bottom
				#elif win
					0
				#endif
				;
			break;
  		case XmNvisibleItemCount:
			Melder_assert (my widgetClass == xmListWidgetClass);
			*va_arg (arg, int *) = my visibleItemCount;
			break;
		case XmNselectionPolicy:
			Melder_assert (my widgetClass == xmListWidgetClass);
			*va_arg (arg, int *) = my selectionPolicy;
			break;
		case XmNradioBehavior:
			Melder_assert (my widgetClass == xmRowColumnWidgetClass);
			*va_arg (arg, int *) = my radioBehavior;
			break;
		case XmNautoUnmanage:
			*va_arg (arg, int *) = my autoUnmanage;
			break;
		case XmNorientation:
			Melder_assert (my widgetClass == xmRowColumnWidgetClass ||
								my widgetClass == xmScrollBarWidgetClass);
			*va_arg (arg, int *) = my orientation;
			break;
		case XmNpacking:
			Melder_assert (my widgetClass == xmRowColumnWidgetClass);
			*va_arg (arg, int *) = my packing;
			break;
		case XmNrowColumnType:
			Melder_assert (my widgetClass == xmRowColumnWidgetClass);
			*va_arg (arg, int *) = my rowColumnType;
			break;
		case XmNentryAlignment:
			Melder_assert (my widgetClass == xmRowColumnWidgetClass);
			*va_arg (arg, int *) = 0;
			break;
		case XmNdefaultButton:
			Melder_assert (my widgetClass == xmFormWidgetClass ||
								my widgetClass == xmBulletinBoardWidgetClass ||
								my widgetClass == xmMessageBoxWidgetClass);
			*va_arg (arg, Widget *) = my defaultButton;
			break;
		case XmNcancelButton:
			Melder_assert (my widgetClass == xmFormWidgetClass ||
								my widgetClass == xmBulletinBoardWidgetClass ||
								my widgetClass == xmMessageBoxWidgetClass);
			*va_arg (arg, Widget *) = my cancelButton;
			break;
		case XmNsubMenuId:
			Melder_assert (my widgetClass == xmCascadeButtonWidgetClass);
			*va_arg (arg, Widget *) = my subMenuId;
			break;
		case XmNdialogStyle:
			Melder_assert (my widgetClass == xmFormWidgetClass ||
								my widgetClass == xmBulletinBoardWidgetClass);
			*va_arg (arg, int *) = my dialogStyle;
			break;
		case XmNdialogType:
			Melder_assert (my widgetClass == xmMessageBoxWidgetClass);
			*va_arg (arg, int *) = my dialogType;
			break;
		case XmNleftAttachment:
			Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
			*va_arg (arg, int *) = my leftAttachment;
			break;
		case XmNrightAttachment:
			Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
			*va_arg (arg, int *) = my rightAttachment;
			break;
		case XmNtopAttachment:
			Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
			*va_arg (arg, int *) = my topAttachment;
			break;
		case XmNbottomAttachment:
			Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
			*va_arg (arg, int *) = my bottomAttachment;
			break;
		case XmNleftOffset:
			Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
			*va_arg (arg, int *) = my leftOffset;
			break;
		case XmNrightOffset:
			Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
			*va_arg (arg, int *) = my rightOffset;
			break;
		case XmNtopOffset:
			Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
			*va_arg (arg, int *) = my topOffset;
			break;
		case XmNbottomOffset:
			Melder_assert (my parent -> widgetClass == xmFormWidgetClass);
			*va_arg (arg, int *) = my bottomOffset;
			break;
		case XmNminimum: *va_arg (arg, int *) = my minimum; break;
		case XmNmaximum: *va_arg (arg, int *) = my maximum; break;
		case XmNvalue: *va_arg (arg, int *) = my value; break;
		case XmNincrement:
			Melder_assert (my widgetClass == xmScrollBarWidgetClass);
			*va_arg (arg, int *) = my increment;
			break;
		case XmNpageIncrement:
			Melder_assert (my widgetClass == xmScrollBarWidgetClass);
			*va_arg (arg, int *) = my pageIncrement;
			break;
		case XmNsliderSize:
			Melder_assert (my widgetClass == xmScrollBarWidgetClass);
			*va_arg (arg, int *) = my sliderSize;
			break;
		case XmNmarginWidth: *va_arg (arg, int *) = 0; break;
		case XmNmarginHeight:
			*va_arg (arg, int *) = 0;
			break;
		case XmNdeleteResponse:
			Melder_assert (my widgetClass == xmShellWidgetClass);
			*va_arg (arg, int *) = my deleteResponse;
			break;
		case XmNcolumns: *va_arg (arg, int *) = 40; break;
		case XmNhorizontalScrollBar: *va_arg (arg, Widget *) = my motif.scrolledWindow.horizontalBar; break;
		case XmNverticalScrollBar: *va_arg (arg, Widget *) = my motif.scrolledWindow.verticalBar; break;
		default: {
			if (resource < 0 || resource >= sizeof motif_resourceNames / sizeof (char *))
				Melder_flushError ("(XtVaGetValues:) Resource out of range (%d).", resource);
			else
				Melder_flushError ("(XtVaGetValues:) Unknown resource \"%s\".",
					motif_resourceNames [resource]);
			return;
		}
	}
	va_end (arg);
}

void XtVaSetValues (Widget me, ...) {
	va_list arg;
	va_start (arg, me);
	_motif_setValues (me, arg);
	va_end (arg);
}

Window XtWindow (Widget me) {
	#if mac
		return (Window) my macWindow;
	#elif win
		return (Window) my window;
	#endif
}

/***** MOTIF *****/

#ifdef UNIX
void XmAddWMProtocolCallback (Widget me, Atom protocol, XtCallbackProc callback, caddr_t closure) {
#else
void XmAddWMProtocolCallback (Widget me, Atom protocol, XtCallbackProc callback, char *closure) {
#endif
	if (protocol == 'delw') {
		my motif.shell.goAwayCallback = callback;
		my motif.shell.goAwayClosure = closure;
		#if mac
			#if carbon
				ChangeWindowAttributes (my macWindow, kWindowCloseBoxAttribute, 0);
			#else
				((WindowPeek) my macWindow) -> goAwayFlag = 1;
			#endif
		#endif
	}
}

void XmAddWMProtocols (Widget shell, Atom *protocols, Cardinal num_protocols) {
	(void) shell;
	(void) protocols;
	(void) num_protocols;
}

Widget XmCreateBulletinBoard (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmBulletinBoardWidgetClass, parent, name);
}

Widget XmCreateBulletinBoardDialog (Widget parent, const char *name, ArgList dum1, int dum2) {
	Widget shell = XmCreateDialogShell (parent, name, dum1, dum2);
	return XmCreateBulletinBoard (shell, name, dum1, dum2);
}

Widget XmCreateCascadeButton (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmCascadeButtonWidgetClass, parent, name);
}

Widget XmCreateCascadeButtonGadget (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmCascadeButtonGadgetClass, parent, name);
}

Widget XmCreateDialogShell (Widget parent, const char *name, ArgList dum1, int dum2) {
	Widget shell;
	theDialogHint = True;
	shell = XmCreateShell (parent, name, dum1, dum2);
	theDialogHint = False;
	return shell;
}

Widget XmCreateDrawingArea (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmDrawingAreaWidgetClass, parent, name);
}

Widget XmCreateForm (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmFormWidgetClass, parent, name);
}

Widget XmCreateFormDialog (Widget parent, const char *name, ArgList dum1, int dum2) {
	Widget shell = XmCreateDialogShell (parent, name, dum1, dum2);
	return XmCreateForm (shell, name, dum1, dum2);
}

Widget XmCreateFrame (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmFrameWidgetClass, parent, name);
}

Widget XmCreateLabel (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmLabelWidgetClass, parent, name);
}

Widget XmCreateLabelGadget (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmLabelWidgetClass, parent, name);
}

Widget XmCreateList (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmListWidgetClass, parent, name);
}

Widget XmCreateMenuBar (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmMenuBarWidgetClass, parent, name);
}

Widget XmCreateMessageBox (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmMessageBoxWidgetClass, parent, name);
}

Widget XmCreateMessageDialog (Widget parent, const char *name, ArgList dum1, int dum2) {
	Widget shell = XmCreateDialogShell (parent, name, dum1, dum2), dialog;
	int screenWidth = WidthOfScreen (DefaultScreenOfDisplay (XtDisplay (parent)));
	int screenHeight = HeightOfScreen (DefaultScreenOfDisplay (XtDisplay (parent))) - 22;
	shell -> deleteResponse = XmUNMAP;
	dialog = XmCreateMessageBox (shell, name, dum1, dum2);
	XtVaSetValues (shell, XmNx, (screenWidth - 300) / 2, XmNy, (screenHeight - 150) / 3, NULL);
	return dialog;
}

Widget XmCreatePulldownMenu (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmPulldownMenuWidgetClass, parent, name);
}

Widget XmCreatePushButton (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmPushButtonWidgetClass, parent, name);
}

Widget XmCreatePushButtonGadget (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmPushButtonWidgetClass, parent, name);
}

Widget XmCreateRadioBox (Widget parent, const char *name, ArgList dum1, int dum2) {
	Widget result = createWidget (xmRowColumnWidgetClass, parent, name);
	(void) dum1;
	(void) dum2;
	XtVaSetValues (result, XmNradioBehavior, True, 0);
	return result;
}

Widget XmCreateRowColumn (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmRowColumnWidgetClass, parent, name);
}

Widget XmCreateScale (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmScaleWidgetClass, parent, name);
}

Widget XmCreateScrollBar (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmScrollBarWidgetClass, parent, name);
}

Widget XmCreateScrolledList (Widget parent, const char *name, ArgList dum1, int dum2) {
	Widget scrolled = createWidget (xmScrolledWindowWidgetClass, parent, name);
	(void) dum1;
	(void) dum2;
	return createWidget (xmListWidgetClass, scrolled, name);
}

Widget XmCreateScrolledText (Widget parent, const char *name, ArgList dum1, int dum2) {
	Widget scrolled = createWidget (xmScrolledWindowWidgetClass, parent, name);
	(void) dum1;
	(void) dum2;
	return createWidget (xmTextWidgetClass, scrolled, name);
}

Widget XmCreateScrolledWindow (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmScrolledWindowWidgetClass, parent, name);
}

Widget XmCreateSeparator (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmSeparatorWidgetClass, parent, name);
}

Widget XmCreateSeparatorGadget (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmSeparatorWidgetClass, parent, name);
}

Widget XmCreateShell (Widget parent, const char *name, ArgList dum1, int dum2) {
	Widget me = createWidget (xmShellWidgetClass, parent, name);
	(void) dum1;
	(void) dum2;
	_motif_addShell (me);
	return me;
}

Widget XmCreateText (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmTextWidgetClass, parent, name);
}

Widget XmCreateToggleButton (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmToggleButtonWidgetClass, parent, name);
}

Widget XmCreateToggleButtonGadget (Widget parent, const char *name, ArgList dum1, int dum2) {
	(void) dum1;
	(void) dum2;
	return createWidget (xmToggleButtonWidgetClass, parent, name);
}

Atom XmInternAtom (Display *display, String name, Boolean only_if_exists) {
	(void) display;
	(void) only_if_exists;
	if (strequ (name, "WM_DELETE_WINDOW")) return 'delw';
	return 0;
}

void XmListAddItem (Widget me, XmString item, int position) {
	#if win
		if (position)
			ListBox_InsertString (my window, position - 1, item);
		else
			ListBox_AddString (my window, item);
	#elif mac
		Cell cell;
		int n = (** my nat.list.handle). dataBounds. bottom;
		if (position == 0)
			position = n + 1;   /* At end. */
		cell.h = 0; cell. v = position - 1;
		_motif_clipOn (me);
		LAddRow (1, position - 1, my nat.list.handle);
		LSetCell (item, (short) strlen (item), cell, my nat.list.handle);
		(** my nat.list.handle). visible. bottom = n + 1;
		_motif_clipOffInvalid (me);
		XtVaSetValues (me, XmNheight, (n + 1) * CELL_HEIGHT, 0);
	#endif
}

void XmListAddItems (Widget me, XmString *items, int n, int position) {
	#if win
		int i;
		if (position)
			for (i = 0; i < n; i ++)
				ListBox_InsertString (my window, (position ++) - 1, items [i]);
		else
			for (i = 0; i < n; i ++)
				ListBox_AddString (my window, items [i]);
	#elif mac
		int i, there = (** my nat.list.handle). dataBounds. bottom;
		Cell cell; cell.h = 0;
		_motif_clipOn (me);
		if (position == 0)
			position = there + 1;   /* At end. */
		LAddRow (n, position - 1, my nat.list.handle);
		for (i = 0; i < n; i ++) {
			cell.v = position - 1 + i;
			LSetCell (items [i], strlen (items [i]), cell, my nat.list.handle);
			LDraw (cell, my nat.list.handle);
		}
		motif_mac_clipOff ();
	#endif
}

void XmListAddItemUnselected (Widget me, XmString item, int pos) {
	XmListAddItem (me, item, pos);
}

void XmListAddItemsUnselected (Widget me, XmString *items, int n, int pos) {
	XmListAddItems (me, items, n, pos);
}

void XmListDeleteAllItems (Widget me) {
	#if win
		ListBox_ResetContent (my window);
	#elif mac
		_motif_clipOn (me);
		LDelRow (0, 0, my nat.list.handle);
		motif_mac_clipOff ();
	#endif
}

void XmListDeleteItem (Widget me, XmString item) {
	#if win
		int i, n = ListBox_GetCount (my window);
		for (i = n - 1; i >= 0; i --) {
			char buffer [301];
			ListBox_GetText (my window, i, buffer);
			if (strequ (buffer, item)) ListBox_DeleteString (my window, i);
		}
	#elif mac
		int i, n = (** my nat.list.handle). dataBounds. bottom;
		Cell cell; cell.h = 0;
		_motif_clipOn (me);
		for (i = n - 1; i >= 0; i --) {
			char buffer [301];
			short int length = 300;
			cell.v = i;
			LGetCell (buffer, & length, cell, my nat.list.handle); buffer [length] = '\0';
			if (strequ (buffer, item)) LDelRow (1, i, my nat.list.handle);
		}
		motif_mac_clipOff ();
	#endif
}

void XmListDeleteItems (Widget me, XmString *items, int n) {
	int i;
	for (i = 0; i < n; i ++) XmListDeleteItem (me, items [i]);
}

void XmListDeleteItemsPos (Widget me, int item_count, int position) {
	#if win
		int i;
		for (i = position + item_count - 2; i >= position - 1; i --)
			ListBox_DeleteString (my window, i);
	#elif mac
		_motif_clipOn (me);
		LDelRow (item_count, position - 1, my nat.list.handle);
		motif_mac_clipOff ();
	#endif
}

void XmListDeletePos (Widget me, int position) {
	#if win
		ListBox_DeleteString (my window, position - 1);
	#elif mac
		_motif_clipOn (me);
		LDelRow (1, position - 1, my nat.list.handle);
		motif_mac_clipOff ();
	#endif
}

void XmListDeselectAllItems (Widget me) {
	#if win
		ListBox_SetSel (my window, False, -1);
	#elif mac
		int i, n = (** my nat.list.handle). dataBounds. bottom;
		Cell cell; cell.h = 0;
		_motif_clipOn (me);
		for (i = 0; i < n; i ++) { cell.v = i; LSetSelect (false, cell, my nat.list.handle); }
		motif_mac_clipOff ();
	#endif
}

void XmListDeselectItem (Widget me, XmString item) {
	#if win
		int i, n = ListBox_GetCount (my window);
		for (i = n - 1; i >= 0; i --) {
			char buffer [301];
			ListBox_GetText (my window, i, buffer);
			if (strequ (buffer, item)) ListBox_SetSel (my window, False, i);
		}
	#elif mac
		int i, n = (** my nat.list.handle). dataBounds. bottom;
		Cell cell; cell.h = 0;
		_motif_clipOn (me);
		for (i = n - 1; i >= 0; i --) {
			char buffer [301];
			short int length = 300;
			cell.v = i;
			LGetCell (buffer, & length, cell, my nat.list.handle); buffer [length] = '\0';
			if (strequ (buffer, item)) LSetSelect (false, cell, my nat.list.handle);
		}
		motif_mac_clipOff ();
	#endif
}

void XmListDeselectPos (Widget me, int position) {
	#if win
		ListBox_SetSel (my window, False, position - 1);
	#elif mac
		Cell cell;
		cell. h = 0;
		cell. v = position - 1; 
		_motif_clipOn (me);
		LSetSelect (false, cell, my nat.list.handle);
		motif_mac_clipOff ();
	#endif
}

Boolean XmListGetMatchPos (Widget me, XmString item, int **position_list, int *position_count) {
	#if win
		int i, n = ListBox_GetCount (my window), count = 0;
		if (n < 1) return False;
		*position_list = Melder_calloc (n, sizeof (int));
		*position_count = 0;
		for (i = 1; i <= n; i ++) {
			char buffer [301];
			ListBox_GetText (my window, i - 1, buffer);
			if (strequ (buffer, item)) {
				(*position_count) ++;
				(*position_list) [*position_count - 1] = i;
			}
		}   
		if (*position_count == 0) { Melder_free (*position_list); return False; }
		return True;
	#elif mac
		int i, n = (** my nat.list.handle). dataBounds. bottom;
		Cell cell;
		cell.h = 0;
		if (n < 1) return False;
		*position_list = Melder_calloc (n, sizeof (int));
		*position_count = 0;
		for (i = 1; i <= n; i ++) {
			char buffer [301];
			short int length = 300;
			cell. v = i - 1;
			LGetCell (buffer, & length, cell, my nat.list.handle); buffer [length] = '\0';
			if (strequ (buffer, item)) {
				(*position_count) ++;
				(*position_list) [*position_count - 1] = i;
			}
		}   
		if (*position_count == 0) { Melder_free (*position_list); return False; }
		return True;
	#endif
}

Boolean XmListGetSelectedPos (Widget me, int **position_list, int *position_count) {
	#if win
		int i, n = ListBox_GetSelCount (my window), *indices;
		if (n == 0) return False;
		if (n == -1) {   // single selection
			int selection = ListBox_GetCurSel (my window);
			if (selection == -1) return False;
			n = 1;
			indices = Melder_calloc (n, sizeof (int));
			indices [0] = selection;
		} else {
			indices = Melder_calloc (n, sizeof (int));
			ListBox_GetSelItems (my window, n, indices);
		}
		for (i = 0; i < n; i ++) indices [i] += 1;
		*position_count = n;
		*position_list = indices;
		return True;
	#elif mac
		int i, n = (** my nat.list.handle). dataBounds. bottom;
		Cell cell; cell.h = 0;
		if (n < 1) return False;
		*position_list = Melder_calloc (n, sizeof (int));
		*position_count = 0;
		for (i = 1; i <= n; i ++) {
			cell. v = i - 1;
			if (LGetSelect (false, & cell, my nat.list.handle)) {
				(*position_count) ++;
				(*position_list) [*position_count - 1] = i;
			}
		}   
		if (*position_count == 0) { Melder_free (*position_list); return False; }
		return True;
	#endif
}

Boolean XmListItemExists (Widget me, XmString item) {
	return XmListItemPos (me, item) != 0;
}

int XmListItemPos (Widget me, XmString item) {
	#if win
		int winPos = ListBox_FindStringExact (my window, 0, item);
		return winPos < 0 ? 0 : winPos + 1;
	#elif mac
		int i, n = (** my nat.list.handle). dataBounds. bottom;
		Cell cell; cell.h = 0;
		for (i = 1; i <= n; i ++) {
			char buffer [301];
			short int length = 300;
			cell. v = i - 1;
			LGetCell (buffer, & length, cell, my nat.list.handle); buffer [length] = '\0';
			if (strequ (buffer, item)) return i;   /* First instance found. */
		}
	#endif
	return 0;   /* Not found. */
}

void XmListReplaceItemsPos (Widget me, XmString *new_items, int item_count, int position) {
	#if win
		int i;
		for (i = 0; i < item_count; i ++) {
			int winPos = position - 1 + i;
			ListBox_DeleteString (my window, winPos);
			ListBox_InsertString (my window, winPos, new_items [i]);
		}
	#elif mac
		int i;
		Cell cell; cell.h = 0;
		_motif_clipOn (me);
		for (i = 0; i < item_count; i ++) {
			cell.v = position - 1 + i;
			LSetCell (new_items [i], strlen (new_items [i]), cell, my nat.list.handle);
			LDraw (cell, my nat.list.handle);      
		}
		motif_mac_clipOff ();
	#endif
}

void XmListReplaceItemsPosUnselected (Widget me, XmString *new_items, int item_count, int position) {
	XmListReplaceItemsPos (me, new_items, item_count, position);
}

void XmListSelectItem (Widget me, XmString item, Boolean notify) {
	#if win
		int i, n = ListBox_GetCount (my window);
		for (i = n - 1; i >= 0; i --) {
			char buffer [301];
			ListBox_GetText (my window, i, buffer);
			if (strequ (buffer, item)) ListBox_SetSel (my window, True, i);
		}
	#elif mac
		int i, n = (** my nat.list.handle). dataBounds. bottom;
		Cell cell; cell.h = 0;
		_motif_clipOn (me);
		for (i = 0; i < n; i ++) {
			char buffer [301];
			short int length = 300;
			cell.v = i;
			LGetCell (buffer, & length, cell, my nat.list.handle); buffer [length] = '\0';
			if (strequ (buffer, item))
				LSetSelect (true, cell, my nat.list.handle);
			else if (my selectionPolicy == XmSINGLE_SELECT || my selectionPolicy == XmBROWSE_SELECT)
				LSetSelect (false, cell, my nat.list.handle);
		}
		motif_mac_clipOff ();
	#endif
	if (notify && my extendedSelectionCallback)
		my extendedSelectionCallback (me, my extendedSelectionClosure, NULL);
}

void XmListSelectPos (Widget me, int position, Boolean notify) {
	#if win
		ListBox_SetSel (my window, True, position - 1);
	#elif mac
		Cell cell; cell.h = 0;
		_motif_clipOn (me);
		if (my selectionPolicy == XmSINGLE_SELECT || my selectionPolicy == XmBROWSE_SELECT) {
			int i, n = (** my nat.list.handle). dataBounds. bottom;
			for (i = 0; i < n; i ++) if (i != position - 1) {
				cell.v = i;
				LSetSelect (false, cell, my nat.list.handle);
			}
		}
		cell.v = position - 1; 
		LSetSelect (true, cell, my nat.list.handle);
		motif_mac_clipOff ();
	#endif
	if (notify && my extendedSelectionCallback)
		my extendedSelectionCallback (me, my extendedSelectionClosure, NULL);
}

void XmListSetAddMode (Widget me, Boolean mode) {
	(void) me;
	(void) mode;
}

void XmListSetBottomItem (Widget me, XmString item) {
	#if mac
		int position = XmListItemPos (me, item);
		_motif_clipOn (me);
		if (position)
			LScroll (0, position - (** my nat.list.handle). visible. bottom - 1, my nat.list.handle);
		motif_mac_clipOff ();
	#else
		(void) me;
		(void) item;
	#endif
}

void XmListSetBottomPos (Widget me, int position) {
	#if mac
		_motif_clipOn (me);
		LScroll (0, position - (** my nat.list.handle). visible. bottom - 1, my nat.list.handle);
		motif_mac_clipOff ();
	#else
		(void) me;
		(void) position;
	#endif
}

void XmListSetHorizPos (Widget me, int position) {
	(void) me;
	(void) position;
}

void XmListSetItem (Widget me, XmString item) {
	int position = XmListItemPos (me, item);
	#if win
		ListBox_SetTopIndex (my window, position - 1);
	#elif mac
		_motif_clipOn (me);
		if (position)
			LScroll (0, position - (** my nat.list.handle). visible. top - 1, my nat.list.handle);
		motif_mac_clipOff ();
	#endif
}

void XmListSetPos (Widget me, int position) {
	#if win
		ListBox_SetTopIndex (my window, position - 1);
	#elif mac
		_motif_clipOn (me);
		LScroll (0, position - (** my nat.list.handle). visible. top - 1, my nat.list.handle);
		motif_mac_clipOff ();
	#endif
}

Widget XmMessageBoxGetChild (Widget me, int child) {
	return
		child == XmDIALOG_OK_BUTTON ? my motif.messageBox.okButton :
		child == XmDIALOG_CANCEL_BUTTON ? my motif.messageBox.cancelButton :
		child == XmDIALOG_HELP_BUTTON ? my motif.messageBox.helpButton :
		NULL;
}

void XmScaleGetValue (Widget me, int *value_return) {
	Melder_assert (my widgetClass == xmScaleWidgetClass);
	*value_return = my value;
}

void XmScaleSetValue (Widget me, int value) {
	Melder_assert (my widgetClass == xmScaleWidgetClass);
	my value = value;
	#if win
		SendMessage (my window, PBM_SETPOS, (WPARAM) value, 0);
	#elif mac
		SetPortWindowPort (my macWindow);
	#endif
	_motif_update (me, 0);
}

void XmScrollBarGetValues (Widget me, int *value, int *sliderSize,
	int *increment, int *pageIncrement)
{
	Melder_assert (my widgetClass == xmScrollBarWidgetClass);
	*value = my value;
	*sliderSize = my sliderSize;
	*increment = my increment;
	*pageIncrement = my pageIncrement;
}

void XmScrollBarSetValues (Widget me, int value, int sliderSize,
	int increment, int pageIncrement, Boolean notify)
{
	Melder_assert (my widgetClass == xmScrollBarWidgetClass);
	my value = value;
	my sliderSize = sliderSize;
	my increment = increment;
	my pageIncrement = pageIncrement;
	NativeScrollBar_set (me);
	#if mac
		if (_motif_duringUpdate)
			invalidateWidget (me);   /* HACK: necessary because VisRgn has temporarily been changed. */
	#endif
	if (notify)	xt_callCallbacks (me, & my motif.scrollBar.valueChangedCallbacks, NULL);
}

XmString XmStringCreateSimple (const char *cstring) {
	return (XmString) Melder_strdup (cstring);
}

void XmStringFree (XmString me) {
	Melder_free (me);
}

Boolean XmTextCopy (Widget me, long time) {
	#if win
		DWORD selection = Edit_GetSel (my window);
		if (HIWORD (selection) <= LOWORD (selection)) return False;
		SendMessage (my window, WM_COPY, 0, 0);
	#elif mac
		me = _motif_activeTextWidget;   /* !!! */
		if (! me) return False;
		if ((**my nat.text.handle). selStart >= (**my nat.text.handle). selEnd)
			return False;
		#if carbon
			ClearCurrentScrap ();
		#else
			ZeroScrap ();
		#endif
		TECopy (my nat.text.handle);
		TEToScrap ();
	#endif
	(void) time;
	return True;
}

Boolean XmTextCut (Widget me, long time) {
	#if win
		DWORD selection = Edit_GetSel (my window);
		(void) time;
		if (HIWORD (selection) <= LOWORD (selection)) return False;
		if (! my motif.text.editable) return False;
		SendMessage (my window, WM_CUT, 0, 0);
	#elif mac
		EventRecord event;
		event. when = time;
		me = _motif_activeTextWidget;   /* !!! */
		if (! me || ! my motif.text.editable) return False;
		if ((**my nat.text.handle). selStart >= (**my nat.text.handle). selEnd)
			return False;
		_motif_clipOn (me);
		#if carbon
			ClearCurrentScrap ();
		#else
			ZeroScrap ();
		#endif
		TECut (my nat.text.handle);
		motif_mac_clipOff ();
		TEToScrap ();
		xt_callCallbacks (me, & my motif.text.valueChangedCallbacks, (XtPointer) & event);
	#endif
	return True;
}

char *XmTextGetSelection (Widget me) {
	long length;
	char *result;
	#if win
		DWORD start, end;
		const char *from;
		char *to;
		Melder_assert (MEMBER (me, Text));
		SendMessage (my window, EM_GETSEL, (WPARAM) & start, (LPARAM) & end);   // 32-bit (R&N: 579)
		if (end <= start) return NULL;
		/*
		 * Get all text.
		 */
		length = Edit_GetTextLength (my window);
		result = Melder_malloc (1 + length);
		Edit_GetText (my window, result, length + 1);
		/*
		 * Zoom in on selection.
		 */
		length = end - start;
		memmove (result, result + start, length);   // Not because of realloc, but because of free!
		result [length] = '\0';
		result = Melder_realloc (result, length + 1);   // Optional.
		/*
		 * Replace CR/LF with LF, i.e. ignore all CR.
		 * This must be done AFTER zooming!
		 */
		for (from = result, to = result; *from != '\0'; from ++) if (*from != 13) *to ++ = *from;
		*to = '\0';   /* Closing null byte. */
	#elif mac
		long i;
		Melder_assert (MEMBER (me, Text));
		length = (**my nat.text.handle). selEnd - (**my nat.text.handle). selStart;
		if (length <= 0) return NULL;
		result = Melder_malloc (1 + length);
		strncpy (result, (char *) *(** my nat.text.handle). hText + (** my nat.text.handle). selStart, length);
		result [length] = '\0';
		/*
		 * Replace all CR with LF.
		 */
		for (i = 0; i <= length; i ++) if (result [i] == 13) result [i] = '\n';   /* Return -> newline. */
	#endif
	return result;
}

Boolean XmTextGetSelectionPosition (Widget me, XmTextPosition *left, XmTextPosition *right) {
	#if win
		SendMessage (my window, EM_GETSEL, (WPARAM) left, (LPARAM) right);   /* BUG: not corrected for CR/LF versus LF. */
	#elif mac
		*left = (**my nat.text.handle). selStart;
		*right = (**my nat.text.handle). selEnd;
	#endif
	if (*right <= *left) return False;
	return True;
}

XmTextPosition XmTextGetInsertionPosition (Widget me) {
	#if win
		long left, right;
		SendMessage (my window, EM_GETSEL, (WPARAM) & left, (LPARAM) & right);
		return left;   /* BUG: not corrected for CR/LF versus LF. */
	#elif mac
		return (**my nat.text.handle). selStart;
	#endif
}

char *XmTextGetString (Widget me) {
	long length;
	char *result;
	#if win
		const char *from;
		char *to;
		Melder_assert (MEMBER (me, Text));
		length = Edit_GetTextLength (my window);
		result = Melder_malloc (1 + length);
		Edit_GetText (my window, result, length + 1);
		result [length] = '\0';
		/*
		 * Replace CR/LF with LF, i.e. ignore all CR.
		 */
		for (from = result, to = result; *from != '\0'; from ++) if (*from != 13) *to ++ = *from;
		*to = '\0';   /* Closing null byte. */
	#elif mac
		long i;
		Melder_assert (MEMBER (me, Text));
		length = (** my nat.text.handle). teLength;
		result = Melder_malloc (1 + length);
		strncpy (result, (char *) *(** my nat.text.handle). hText, length);
		result [length] = '\0';
		/*
		 * Replace all CR with LF.
		 */
		for (i = 0; i < length; i ++) if (result [i] == 13) result [i] = '\n';
	#endif
	return result;
}

Boolean XmTextPaste (Widget me) {
	#if win
		if (! my motif.text.editable) return False;
		SendMessage (my window, WM_PASTE, 0, 0);
	#elif mac
		EventRecord event;
		event. when = 0;
		me = _motif_activeTextWidget;   /* !!! */
		if (! me || ! my motif.text.editable) return False;
		TEFromScrap ();
		_motif_clipOn (me);
		TEPaste (my nat.text.handle);
		motif_mac_clipOff ();
		xt_callCallbacks (me, & my motif.text.valueChangedCallbacks, (XtPointer) & event);
	#endif
	return True;
}

Boolean XmTextRemove (Widget me) {
	#if win
		DWORD selection = Edit_GetSel (my window);
		if (HIWORD (selection) <= LOWORD (selection)) return False;
		if (! my motif.text.editable) return False;
		SendMessage (my window, WM_CLEAR, 0, 0);
	#elif mac
		EventRecord event;
		event. when = 0;
		me = _motif_activeTextWidget; /* !!! */
		if (! me || ! my motif.text.editable) return False;
		if ((**my nat.text.handle). selStart >= (**my nat.text.handle). selEnd)
			return False;
		_motif_clipOn (me);
		TEDelete (my nat.text.handle);
		motif_mac_clipOff ();
		xt_callCallbacks (me, & my motif.text.valueChangedCallbacks, (XtPointer) & event);
	#endif
	return True;
}

void XmTextReplace (Widget me, XmTextPosition from_pos, XmTextPosition to_pos, char *text) {
	#if win
		const char *from;
		char *winText = Melder_malloc (2 * strlen (text) + 1), *to;   /* All new lines plus one null byte. */
		if (! winText) return;
		Melder_assert (MEMBER (me, Text));
		/*
		 * Replace all LF with CR/LF.
		 */
		for (from = text, to = winText; *from != '\0'; from ++, to ++)
			if (*from == '\n') { *to = 13; * ++ to = '\n'; } else *to = *from;
		*to = '\0';
		/*
		 * We DON'T replace any text without selecting it, so we can deselect any other text,
		 * thus allowing ourselves to select [from_pos, to_pos] and use the REPLACESEL message.
		 */
		Edit_SetSel (my window, from_pos, to_pos);
		Edit_ReplaceSel (my window, winText);
		Melder_free (winText);
	#elif mac
		long length = strlen (text), i;
		char *macText;
		if (length > 32000) { length = 32000; Melder_warning ("Text truncated to 32000 characters!!!!!\nDo not save!!!!!"); }
		macText = Melder_malloc (length + 1);
		Melder_assert (my widgetClass == xmTextWidgetClass);
		strncpy (macText, text, length);
		macText [length] = '\0';
		/*
		 * Replace all LF with CR.
		 */
		for (i = 0; i < length; i ++) if (macText [i] == '\n') macText [i] = 13;
		/*
		 * We DON'T replace any text without selecting it, so we can deselect any other text,
		 * thus allowing ourselves to select [from_pos, to_pos] and use selection replacement.
		 */
		if (my managed) _motif_clipOn (me);
		TESetSelect (from_pos, to_pos, my nat.text.handle);
		TEDelete (my nat.text.handle);
		TEInsert (macText, length, my nat.text.handle);
		Melder_free (macText);
		if (my managed && ! my isControl) {
			Rect r = my rect;
			SetPortWindowPort (my macWindow);
			InsetRect (& r, 1, 1);
			EraseRect (& r);
			TEUpdate (& my rect, my nat.text.handle);
			motif_mac_clipOff ();
		}
	#endif
	xt_callCallbacks (me, & my motif.text.valueChangedCallbacks, NULL);
}

void XmTextSetMaxLength (Widget widget, int max_length) {
	(void) widget;
	(void) max_length;
}

void XmTextSetSelection (Widget me, XmTextPosition first, XmTextPosition last, long time) {
	(void) time;
	#if win
		Edit_SetSel (my window, first, last);
	#elif mac
		TESetSelect (first, last, my nat.text.handle);
	#endif
}

void XmTextSetInsertionPosition (Widget me, XmTextPosition position) {
	#if win
		Edit_SetSel (my window, position, position);
	#elif mac
		TESetSelect (position, position, my nat.text.handle);
	#endif
}

void XmTextSetString (Widget me, const char *text) {
	#if win
		const char *from;
		char *winText = Melder_malloc (2 * strlen (text) + 1), *to;   /* All new lines plus one null byte. */
		if (! winText) return;
		/*
		 * Replace all LF with CR/LF.
		 */
		for (from = text, to = winText; *from != '\0'; from ++, to ++)
			if (*from == '\n') { *to = 13; * ++ to = '\n'; } else *to = *from;
		*to = '\0';
		Edit_SetText (my window, winText);
		Melder_free (winText);
	#elif mac
		long length = strlen (text), i;
		char *macText;
		if (length > 32000) { length = 32000; Melder_warning ("Text truncated to 32000 characters!!!!!\nDo not save!!!!!"); }
		macText = Melder_malloc (length + 1);
		Melder_assert (my widgetClass == xmTextWidgetClass);
		strncpy (macText, text, length);
		macText [length] = '\0';
		/*
		 * Replace all LF with CR.
		 */
		for (i = 0; i < length; i ++) if (macText [i] == '\n') macText [i] = 13;
		if (haveAppearance && my isControl) {
			SetControlData (my nat.control.handle, kControlEntireControl, kControlEditTextTextTag, length, macText);
		} else {
			TESetText (macText, length, my nat.text.handle);
		}
		Melder_free (macText);
		if (my managed) {
			if (_motif_duringUpdate) {
				invalidateWidget (me);   /* HACK: necessary because VisRgn has temporarily been changed (not used in Praat any longer). */
			} else {
				if (my isControl) {
					_motif_clipOn (me);
					Draw1Control (my nat.control.handle);
					motif_mac_clipOff ();
				} else {
					Rect r = my rect;
					InsetRect (& r, 1, 1);
					_motif_clipOn (me);
					EraseRect (& r);
					TEUpdate (& my rect, my nat.text.handle);
					motif_mac_clipOff ();
				}
			}
		}
	#endif
	xt_callCallbacks (me, & my motif.text.valueChangedCallbacks, NULL);
}

Boolean XmToggleButtonGadgetGetState (Widget me) {
	Melder_assert (MEMBER (me, ToggleButton));
	if (my inMenu) {
		#if mac
			short mark;
			GetItemMark (my nat.entry.handle, my nat.entry.item, & mark);
			return mark != noMark;
		#elif win
			return GetMenuState (my nat.entry.handle, my nat.entry.id, MF_BYCOMMAND) & MF_CHECKED ? True : False;
		#endif
	}
	#if mac
		return GetControlValue (my nat.control.handle);
	#elif win
		return (Button_GetState (my window) & 0x0003) == BST_CHECKED;
	#endif
}

void XmToggleButtonGadgetSetState (Widget me, Boolean value, Boolean notify) {
	Melder_assert (MEMBER (me, ToggleButton));
	if (my inMenu)
		NativeMenuItem_check (me, value);
	else
		NativeControl_check (me, value);
	if (notify) xt_callCallbacks (me, & my motif.toggleButton.valueChangedCallbacks, NULL);
}

#if mac
	static void _motif_update (Widget me, EventRecord *event) {
		Widget subview;
		RgnHandle visRgn;
		if (! my managed) return;
		for (subview = my firstChild; subview != NULL; subview = subview -> nextSibling) {
			if (subview -> widgetClass != xmShellWidgetClass)   /* Only in same mac window. */
				_motif_update (subview, event);
		}
		#if carbon
			visRgn = NewRgn ();
			GetPortVisibleRegion (GetWindowPort (my macWindow), visRgn);
			if (! RectInRgn (& my rect, visRgn)) { DisposeRgn (visRgn); return; }
		#else
			visRgn = my macWindow -> visRgn;
			if (! RectInRgn (& my rect, visRgn)) return;
		#endif
		SetPortWindowPort (my macWindow);
		switch (my widgetClass) {
			case xmPushButtonWidgetClass:
			case xmToggleButtonWidgetClass:
			case xmScrollBarWidgetClass: {
				_motif_clipOn (me);
				Draw1Control (my nat.control.handle);
				motif_mac_clipOff ();
			} break;
			case xmListWidgetClass: {
				_motif_clipOn (me);
				LUpdate (visRgn, my nat.list.handle);
				motif_mac_clipOff ();
			} break;
			case xmDrawingAreaWidgetClass: {
				if (my exposeCallback) {
					_motif_clipOn (me);
					my exposeCallback (me, my exposeClosure, (XtPointer) event);
					motif_mac_clipOff ();
				}
			} break;
			case xmTextWidgetClass: {
				_motif_clipOn (me);
				if (my isControl) {
					Draw1Control (my nat.control.handle);
				} else {
					EraseRect (& my rect);
					if (haveAppearance) {
						DrawThemeEditTextFrame (& my rect, kThemeStateActive);
					} else {
						FrameRect (& my rect);
					}
					TEUpdate (& my rect, my nat.text.handle);
				}
				motif_mac_clipOff ();
			} break;
			case xmLabelWidgetClass: {
				if (my parent -> widgetClass == xmMenuBarWidgetClass) {   /* In top menu bar. */
					;
				} else if (my motif.cascadeButton.pushButtonLook) {   /* In dynamic menu. */
					_motif_clipOn (me);
					Draw1Control (my nat.control.handle);
					motif_mac_clipOff ();
				} else if (my parent -> rowColumnType == XmMENU_BAR) {   /* In window menu bar. */
					static RGBColor lightGrey = { 32768, 32768, 32768 }, blak = { 0, 0, 0 };
					Rect r = my rect; r.left -= 4; r.right += 4; r.top -= 1;
					_motif_clipOn (me);
					EraseRect (& r);
					/*if (haveAppearance) {
						UseThemeFont (kThemeMenuTitleFont, smSystemScript);
					} else {*/
					motif_mac_menuTitleFont ();
					/*}*/
					r.top += 1;
					if (theBackground || ! ((Widget) GetWRefCon (my macWindow)) -> motif.shell.active ||
					    ! BACKGROUND_WINDOWS_ARE_ACTIVE && my macWindow != FrontWindow () || my insensitive)
					{
						RGBForeColor (& lightGrey);
						TETextBox (my name, strlen (my name), & r, teJustCenter);
						RGBForeColor (& blak);
					} else {
						TETextBox (my name, strlen (my name), & r, teJustCenter);
					}
					motif_mac_defaultFont ();
					motif_mac_clipOff ();
				} else {   /* A label. */
					if (my isControl) {
						_motif_clipOn (me);
						Draw1Control (my nat.control.handle);
						motif_mac_clipOff ();
					} else {
						Rect r = my rect;
						/*r.top += 3;*/
						_motif_clipOn (me);
						/*if (haveAppearance) {
							UseThemeFont (kThemeSystemFont, smSystemScript);
						}*/
						TETextBox (my name, strlen (my name), & r,
							my alignment == XmALIGNMENT_BEGINNING ? teJustLeft :
							my alignment == XmALIGNMENT_CENTER ? teJustCenter : teJustRight);
						motif_mac_clipOff ();
					}
				}
			} break;
			case xmRowColumnWidgetClass: {
				/*if (haveAppearance && my rowColumnType == XmMENU_BAR)
					DrawThemeMenuBarBackground (& my rect, 0, 0);*/
			} break;
			case xmScrolledWindowWidgetClass:
			case xmFrameWidgetClass: {
				_motif_clipOn (me);
				FrameRect (& my rect);
				motif_mac_clipOff ();
			} break;
			case xmScaleWidgetClass: {
				Rect r = my rect;
				_motif_clipOn (me);
				InsetRect (& r, 20, 15);
				FrameRect (& r);
				InsetRect (& r, 1, 1);
				if (my value <= my minimum) {
					EraseRect (& r);
				} else {
					Rect active = r, inactive = r;
					active. right = r. left + (float) (r. right - r. left) * my value / (my maximum - my minimum + 1);
					PaintRect (& active);
					inactive. left = active. right;
					EraseRect ( & inactive);
				}
				motif_mac_clipOff ();
			} break;
			default: break;
		}
		#if carbon
			DisposeRgn (visRgn);
		#endif
	}
#elif win
	static void _motif_update (Widget me, void *event) { (void) me; (void) event; }
#endif

#if mac
static void _motif_processUpdateEvent (EventRecord *event) {
	WindowPtr macwindow = (WindowPtr) event -> message;
	Widget shell = (Widget) GetWRefCon (macwindow);
	Rect rect;
	int right, bottom;
	if (! shell) return;
	BeginUpdate (macwindow);
	_motif_duringUpdate = True;
	SetPortWindowPort (macwindow);
	if (! shell -> motif.shell.isDialog) {
		Rect portRect;
		GetWindowPortBounds (macwindow, & portRect);
		right = portRect. right, bottom = portRect. bottom;
		SetRect (& rect, right - 15, bottom - 15, right + 1, bottom + 1);
		ClipRect (& rect);
		DrawGrowIcon (macwindow);
		motif_mac_clipOff ();
	}
	if (! haveAppearance && shell -> defaultButton) {
		Widget defaultButton = shell -> defaultButton;
		Rect r = defaultButton -> rect;
		InsetRect (& r, -4, -4);
		PenSize (3, 3);
		FrameRoundRect (& r, 16, 16);
		PenSize (1, 1);
	}
	_motif_update (shell, event);
	EndUpdate (macwindow);
	_motif_duringUpdate = False;
}
#endif

void XmUpdateDisplay (Widget displayDummy) {
	/*
	 * Send update messages to all shells.
	 */
	int i;
	(void) displayDummy;
	for (i = 0; i < 1000; i ++) {
		Widget shell = theShells [i];
		#if mac
			if (shell && shell -> managed) {
				RgnHandle updateRegion = NewRgn ();
				#if carbon
					GetWindowRegion (shell -> nat.window.ptr, kWindowUpdateRgn, updateRegion);
				#else
					GetWindowUpdateRgn (shell -> nat.window.ptr, updateRegion);
				#endif
				if (! EmptyRgn (updateRegion)) {
					/*
					 * Hack an update event and send it to me.
					 */
					EventRecord event;
					event. message = (long) shell -> nat.window.ptr;
					_motif_processUpdateEvent (& event);
				}
				DisposeRgn (updateRegion);
			}
		#endif
	}
}

/***** EVENT *****/

#if win
static void on_scroll (Widget me, UINT part, int pos) {
	if (my maximum == my minimum) return;
	switch (part) {
		case SB_LINEUP: my value -= my increment; break;
		case SB_LINEDOWN: my value += my increment; break;
		case SB_PAGEUP: my value -= my pageIncrement; break;
		case SB_PAGEDOWN: my value += my pageIncrement; break;
		#if SCROLL32
		case SB_THUMBTRACK: case SB_THUMBPOSITION: my value = my minimum +
			pos * ((my maximum - my minimum - my sliderSize) / (32767.0 - (32767.0 * my sliderSize) / (my maximum - my minimum))); break;
		#else
		case SB_THUMBTRACK: case SB_THUMBPOSITION: my value = pos; break;
		#endif
		default: break;
	}
	if (my value < my minimum) my value = my minimum;
	if (my value > my maximum - my sliderSize) my value = my maximum - my sliderSize;
	NativeScrollBar_set (me);
	if (part == SB_THUMBTRACK || part == SB_THUMBPOSITION)
		xt_callCallbacks (me, & my motif.scrollBar.dragCallbacks, (XtPointer) (long) part);
	else
		xt_callCallbacks (me, & my motif.scrollBar.valueChangedCallbacks, (XtPointer) (long) part);
}
#elif mac
static pascal void _motif_scrollBarAction (ControlHandle maccontrol, short part) {
	Widget me = (Widget) GetControlReference (maccontrol);
	if (part == 0) return;
	switch (part) {
		case kControlUpButtonPart: my value -= my increment; break;
		case kControlDownButtonPart: my value += my increment; break;
		case kControlPageUpPart: my value -= my pageIncrement; break;
		case kControlPageDownPart: my value += my pageIncrement; break;
		#if SCROLL32
		case kControlIndicatorPart: {
			int macValue = GetControlValue (maccontrol);
			my value = macValue == 0 ? my minimum : macValue == 32767 ? my maximum - my sliderSize :
				my minimum + (my maximum - my minimum - my sliderSize) * (macValue / 32767.0);
		} break;
		#else
		case kControlIndicatorPart: my value = GetControlValue (maccontrol); break;
		#endif
		default: break;
	}
	if (my value < my minimum) my value = my minimum;
	if (my value > my maximum - my sliderSize) my value = my maximum - my sliderSize;
	#if SCROLL32
	SetControlValue (maccontrol, my value == my minimum ? 0 :
			(32767.0 * (my value - my minimum)) / (my maximum - my minimum - my sliderSize));
	#else
	SetControlValue (maccontrol, my value);
	#endif
	if (part == kControlIndicatorPart)
		xt_callCallbacks (me, & my motif.scrollBar.dragCallbacks, (XtPointer) (long) part);
	else
		xt_callCallbacks (me, & my motif.scrollBar.valueChangedCallbacks, (XtPointer) (long) part);
}
#endif

#if mac
static void _motif_activateControls (Widget me, Boolean act) {
	ControlHandle macControl;
	Widget control;
	UInt16 numberOfControls, icontrol;
	Melder_assert (MEMBER (me, Shell));
	if (haveAppearance) {
		Melder_assert (my nat.window.rootControl);
		CountSubControls (my nat.window.rootControl, & numberOfControls);
		SetPortWindowPort (my macWindow);
		for (icontrol = 1; icontrol <= numberOfControls; icontrol ++) {
			GetIndexedSubControl (my nat.window.rootControl, icontrol, & macControl);
			control = (Widget) GetControlReference (macControl);
			_motif_clipOn (control);
			HiliteControl (macControl, act && ! control -> insensitive ? 0 : 255 );
			motif_mac_clipOff ();
		}
#if ! carbon
	} else {
		SetPortWindowPort (my macWindow);
		for (macControl = (ControlHandle) ((WindowPeek) my nat.window.ptr) -> controlList; macControl != NULL; macControl = (**macControl). nextControl) {
			control = (Widget) GetControlReference (macControl);
			_motif_clipOn (control);
			HiliteControl (macControl, act && ! control -> insensitive ? 0 : 255 );
			motif_mac_clipOff ();
		}
#endif
	}
}

static void _motif_activateGrowIcon (Widget me, Boolean act) {
	if (! my motif.shell.isDialog) {
		WindowPtr macwindow = my nat.window.ptr;
		Rect portRect, rect;
		int right, bottom;
		Melder_assert (macwindow != NULL);
		Melder_assert (GetWindowKind (macwindow) == userKind);
		GetWindowPortBounds (macwindow, & portRect);
		right = portRect. right, bottom = portRect. bottom;
		SetRect (& rect, right - 15, bottom - 15, right + 1, bottom + 1);
		SetPortWindowPort (macwindow);
		ClipRect (& rect);
		DrawGrowIcon (macwindow);
		motif_mac_clipOff ();
		if (act) ValidWindowRect (macwindow, & rect);
	}
}

static void _motif_activateText (Widget me, Boolean act) {
	if (act) {
		if (my activeTextWidget) {
			_motif_activeTextWidget = my activeTextWidget;
			_motif_clipOn (_motif_activeTextWidget);
			if (haveAppearance && _motif_activeTextWidget -> isControl) {
				ActivateControl (_motif_activeTextWidget -> nat.control.handle);
				SetKeyboardFocus (_motif_activeTextWidget -> macWindow, _motif_activeTextWidget -> nat.control.handle, kControlEditTextPart);
			} else {
				TEActivate (_motif_activeTextWidget -> nat.text.handle);
			}
			motif_mac_clipOff ();
		}
	} else {
		if (_motif_activeTextWidget) {
			_motif_clipOn (_motif_activeTextWidget);
			if (haveAppearance && _motif_activeTextWidget -> isControl) {
				ClearKeyboardFocus (_motif_activeTextWidget -> macWindow);
				DeactivateControl (_motif_activeTextWidget -> nat.control.handle);
			} else {
				TEDeactivate (_motif_activeTextWidget -> nat.text.handle);
			}
			motif_mac_clipOff ();
		}
		_motif_activeTextWidget = NULL;
	}
}

static void _motif_activateRest (Widget me, Boolean act) {
	Widget sub;
	if (! my managed) return;
	SetPortWindowPort (my macWindow);
	for (sub = my firstChild; sub != NULL; sub = sub -> nextSibling)
		if (sub -> widgetClass != xmShellWidgetClass)   /* Only in same mac window. */
			_motif_activateRest (sub, act);
	if (my isControl) {
		return;   /* Already done by _motif_activateControls. */
	} else if (my widgetClass == xmListWidgetClass) {
		_motif_clipOn (me);
		LActivate (act, my nat.list.handle);
		motif_mac_clipOff ();
	} else if (my widgetClass == xmTextWidgetClass) {
		return;
	} else if (my widgetClass == xmCascadeButtonWidgetClass &&
				my parent -> widgetClass == xmRowColumnWidgetClass &&
				my parent -> rowColumnType == XmMENU_BAR) {
		if (! act) {
			static RGBColor lightGrey = { 32768, 32768, 32768 }, blak;
			GetForeColor (& blak);
			RGBForeColor (& lightGrey);
			_motif_update (me, NULL);
			RGBForeColor (& blak);
		} else {
			_motif_update (me, NULL);
		}
	}
}

static void _motif_processActivateEvent (EventRecord *event) {
	WindowPtr macwindow = (WindowPtr) event -> message;
	int act = event -> modifiers & 1;
	if (GetWindowKind (macwindow) >= userKind) {
		Widget shell = (Widget) GetWRefCon (macwindow);
		if (! shell) return;

		Melder_assert (GetWindowKind (macwindow) == userKind);
		if (! BACKGROUND_WINDOWS_ARE_ACTIVE) shell -> motif.shell.active = act;
		if (! BACKGROUND_WINDOWS_ARE_ACTIVE) _motif_activateControls (shell, act);
		_motif_activateGrowIcon (shell, act);
		if (BACKGROUND_WINDOWS_ARE_ACTIVE && shell -> dialogStyle == XmDIALOG_FULL_APPLICATION_MODAL) {
			/*
			 * Bypass the Mac activation scheme if Motif style is on.
			 * Deactivate all other shells.
			 */
			int i;
			for (i = 0; i < 1000; i ++) {
				Widget otherShell = theShells [i];
				if (otherShell && otherShell -> managed && otherShell != shell) {
					otherShell -> motif.shell.active = ! act;
					_motif_activateControls (otherShell, ! act);
					_motif_activateRest (otherShell, ! act);
				}
			}
		}
		_motif_activateText (shell, act);
		if (! BACKGROUND_WINDOWS_ARE_ACTIVE) _motif_activateRest (shell, act);
	}
}

static void _motif_processOsEvent (EventRecord *event) {
	unsigned char messageKind = ((unsigned long) event -> message & 0xFF000000) >> 24;
	if (messageKind == mouseMovedMessage) {
		Point location = event -> where;
	} else if (messageKind == suspendResumeMessage) {
		WindowPtr frontWindow = FrontWindow ();
		int act = event -> message & resumeFlag ? true : false, i;
		theBackground = ! act;
		for (i = 0; i < 1000; i ++) {
			Widget shell = theShells [i];
			if (! shell || ! shell -> managed) continue;
			if (BACKGROUND_WINDOWS_ARE_ACTIVE) {
				if (_motif_modalDialog && _motif_modalDialog != shell -> nat.window.ptr) continue;
			} else {
				if (shell -> nat.window.ptr != frontWindow) continue;
			}
			shell -> motif.shell.active = act;
			_motif_activateControls (shell, act);
			_motif_activateGrowIcon (shell, act);
			_motif_activateText (shell, act);
			_motif_activateRest (shell, act);
		}
	}
}

static void _motif_processHighLevelEvent (EventRecord *event) {
	AEProcessAppleEvent (event);
}

static Widget _motif_findDrawingArea (Widget me) {
	Widget sub;
	if (my widgetClass == xmDrawingAreaWidgetClass) return me;
	for (sub = my firstChild; sub != NULL; sub = sub -> nextSibling)
		if (sub -> widgetClass != xmShellWidgetClass) {   /* Only in same mac window. */
			Widget drawingArea = _motif_findDrawingArea (sub);
			if (drawingArea) return drawingArea;
		}
	return NULL;   /* No DrawingArea found. */
}

static int _motif_shell_processKeyboardEquivalent (Widget shell, unsigned char kar, int modifiers, EventRecord *event) {
	WindowPtr macWindow;
	int imenu;
	if (! shell) return 0;
	macWindow = shell -> nat.window.ptr;
	/*
	 * If the user presses Command-?, i.e. the Command key plus the Shift key plus the "/?" key,
	 * Macintosh sends us the "/" character instead of the "?" character. Fix this.
	 */
	if (modifiers == (_motif_COMMAND_MASK | _motif_SHIFT_MASK)) {
		switch (kar) {
			case '/': kar = '\?'; break; case '[': kar = '{'; break; case ']': kar = '}'; break; case '\'': kar = '\"'; break;
			case ',': kar = '<'; break; case '.': kar = '>'; break; case '\\': kar = '|'; break; case '-': kar = '_'; break;
			case '=': kar = '+'; break; case '`': kar = '~'; break;
			default: break;
		}
	}
	/*
	 * If the user presses Option-Command-A, Macintosh sends us byte 140 instead of the "A" character.
	 * These bytes are above 128, except Option-Command-I and Option-Command-N, which give 94 and 126 instead,
	 * but since these are shifted characters ("^" and "~"), there will be no confusion. So we fix it all.
	 */
	if (modifiers == (_motif_COMMAND_MASK | _motif_OPTION_MASK)) {
		if (modifiers & _motif_SHIFT_MASK) {
			/* Ignore the triple modifiers! */
		} else {
			switch ((unsigned char) kar) {
				case 94: kar = 'I'; break; case 126: kar = 'N'; break; case 140: kar = 'A'; break; case 141: kar = 'C'; break;
				case 160: kar = 'T'; break; case 167: kar = 'S'; break; case 168: kar = 'R'; break; case 169: kar = 'G'; break;
				case 171: kar = 'E'; break; case 172: kar = 'U'; break; case 180: kar = 'Y'; break; case 181: kar = 'M'; break;
				case 182: kar = 'D'; break; case 183: kar = 'W'; break; case 185: kar = 'P'; break; case 186: kar = 'B'; break;
				case 189: kar = 'Z'; break; case 191: kar = 'O'; break; case 194: kar = 'L'; break; case 195: kar = 'V'; break;
				case 196: kar = 'F'; break; case 197: kar = 'X'; break; case 198: kar = 'J'; break; case 207: kar = 'Q'; break;
				case 250: kar = 'H'; break; case 251: kar = 'K'; break;
				default: break;
			}
		}
	}
	for (imenu = 1; imenu <= 255; imenu ++) if (theMenus [imenu] && theMenus [imenu] -> macWindow == macWindow) {
		Widget child;
		for (child = theMenus [imenu] -> firstChild; child != NULL; child = child -> nextSibling)
			if (child -> widgetClass == xmPushButtonWidgetClass &&
					child -> motif.pushButton.acceleratorChar == kar &&
					child -> motif.pushButton.acceleratorModifiers == modifiers)
				if (child -> activateCallback && ! child -> insensitive) {
					child -> activateCallback (child, child -> activateClosure, (XtPointer) event);
					return 1;
				}
	}
	return 0;
}

static void _motif_processKeyboardEquivalent (unsigned char kar, int modifiers, EventRecord *event) {
	/*
	 * First try to send the key command to the active shell.
	 * If that fails, try to send the key command to the application shell.
	 */
	if (! _motif_shell_processKeyboardEquivalent ((Widget) GetWRefCon (FrontWindow ()), kar, modifiers, event))
		_motif_shell_processKeyboardEquivalent (theApplicationShell, kar, modifiers, event);
}

static void _motif_processKeyDownEvent (EventRecord *event) {
	/*
	 * This routine determines whether a key-down message is a menu shortcut or is meant to go to a text widget.
	 * It has to find this out fast, so it cannot go through all the menu structures for each key-down.
	 * Therefore, every shell maintains a bit list of which of the lower accelerators are used.
	 */
	Widget me = _motif_activeTextWidget;
	unsigned char kar = event -> message & charCodeMask;
	int modifiers = 0;
	if (event -> modifiers & cmdKey) modifiers |= _motif_COMMAND_MASK;
	if (event -> modifiers & optionKey) modifiers |= _motif_OPTION_MASK;
	if (event -> modifiers & shiftKey) modifiers |= _motif_SHIFT_MASK;
	if (kar < 32) {
		if (kar == 13) {   /* User pressed Return. */
			Widget shell = (Widget) GetWRefCon (FrontWindow ());
			/*
			 * First test for keyboard shortcut.
			 */
			if (shell -> motif.shell.lowAccelerators [modifiers] & 1 << motif_ENTER) {   /* BUG: test for shell=NULL? */
				_motif_processKeyboardEquivalent (motif_ENTER, modifiers, event);
				return;
			/*
			 * Then look for default button in active window.
			 */
			} else if (shell && shell -> defaultButton) {
				Widget defaultButton = shell -> defaultButton;
				if (defaultButton -> activateCallback) {
					defaultButton -> activateCallback (defaultButton, defaultButton -> activateClosure, (XtPointer) event);
					return;
				}
				/*
				 * Otherwise, hand it to a text widget.
				 */
			/*
			 * Then look for a text widget with an activate callback.
			 */
			} else if (me && my activateCallback) {
				my activateCallback (me, my activateClosure, (XtPointer) event);
				return;
			}
			/*
			 * Otherwise, hand it to a text widget.
			 */
		} else if (kar == 27) {   /* User pressed Escape. */
			Widget shell = (Widget) GetWRefCon (FrontWindow ());
			/*
			 * First test for keyboard shortcut.
			 */
			if (shell -> motif.shell.lowAccelerators [modifiers] & 1 << motif_ESCAPE) {
				_motif_processKeyboardEquivalent (motif_ESCAPE, modifiers, event);
				return;
			/*
			 * Then look for cancel button in active window.
			 */
			} else if (shell && shell -> cancelButton) {
				Widget cancelButton = shell -> cancelButton;
				if (cancelButton -> activateCallback) {
					cancelButton -> activateCallback (cancelButton, cancelButton -> activateClosure, (XtPointer) event);
					return;
				}
				/*
				 * Do not hand it to a text widget.
				 */
				return;
			}
			/*
			 * Do not hand it to a text widget.
			 */
			return;
		} else if (kar == 9) {   /* User pressed Tab. */
			Widget shell = (Widget) GetWRefCon (FrontWindow ());
			/*
			 * First test for keyboard shortcut.
			 */
			if (shell -> motif.shell.lowAccelerators [modifiers] & 1 << motif_TAB) {
				_motif_processKeyboardEquivalent (motif_TAB, modifiers, event);
				return;
			}
			/*
			 * Otherwise, hand it to a text widget.
			 */
		} else if (kar == 8) {   /* User pressed Backspace. */
			Widget shell = (Widget) GetWRefCon (FrontWindow ());
			/*
			 * First test for keyboard shortcut.
			 */
			if (shell -> motif.shell.lowAccelerators [modifiers] & 1 << motif_BACKSPACE) {
				_motif_processKeyboardEquivalent (motif_BACKSPACE, modifiers, event);
				return;
			}
			/*
			 * Otherwise, hand it to a text widget.
			 */
		} else if (kar == 5) {   /* Help button. Simulate Command-?. */
			_motif_processKeyboardEquivalent ('?', _motif_COMMAND_MASK | _motif_SHIFT_MASK, event);
			return;
		} else if (kar == 16) {   /* F1... F12 */
			int keyCode = (event -> message & keyCodeMask) >> 8;
			int fkey =
				keyCode == 0x7A ? 1 : keyCode == 0x78 ? 2 : keyCode == 0x63 ? 3 : keyCode == 0x76 ? 4 :
				keyCode == 0x60 ? 5 : keyCode == 0x61 ? 6 : keyCode == 0x62 ? 7 : keyCode == 0x64 ? 8 :
				keyCode == 0x65 ? 9 : keyCode == 0x6D ? 10 : keyCode == 0x67 ? 11 : keyCode == 0x6F ? 12 : 0;
			if (fkey)
				_motif_processKeyboardEquivalent (motif_F1 - 1 + fkey, modifiers, event);
			return;
		} else if (kar == 11) {
			_motif_processKeyboardEquivalent (motif_PAGE_UP, modifiers, event);
			return;   /* BUG: we should implement a scroll up in the text widget (IM V-193). */
		} else if (kar == 12) {
			_motif_processKeyboardEquivalent (motif_PAGE_DOWN, modifiers, event);
			return;   /* BUG: we should implement a scroll down in the text widget (IM V-193). */
		} else if (kar == 1) {
			_motif_processKeyboardEquivalent (motif_HOME, modifiers, event);
			return;   /* BUG: we should implement a top left scroll in the text widget (IM V-192). */
		} else if (kar == 4) {
			_motif_processKeyboardEquivalent (motif_END, modifiers, event);
			return;   /* BUG: we should implement a bottom right scroll in the text widget (IM V-193). */
		} else if (kar == 28) {
			Widget shell = (Widget) GetWRefCon (FrontWindow ());
			/*
			 * First test for keyboard shortcut.
			 */
			if (shell -> motif.shell.lowAccelerators [modifiers] & 1 << motif_LEFT_ARROW) {
				_motif_processKeyboardEquivalent (motif_LEFT_ARROW, modifiers, event);
				return;
			}
			/*
			 * Otherwise, hand it to a text widget.
			 */
		} else if (kar == 29) {
			Widget shell = (Widget) GetWRefCon (FrontWindow ());
			if (shell -> motif.shell.lowAccelerators [modifiers] & 1 << motif_RIGHT_ARROW) {
				_motif_processKeyboardEquivalent (motif_RIGHT_ARROW, modifiers, event);
				return;
			}
		} else if (kar == 30) {
			Widget shell = (Widget) GetWRefCon (FrontWindow ());
			if (shell -> motif.shell.lowAccelerators [modifiers] & 1 << motif_UP_ARROW) {
				_motif_processKeyboardEquivalent (motif_UP_ARROW, modifiers, event);
				return;
			}
		} else if (kar == 31) {
			Widget shell = (Widget) GetWRefCon (FrontWindow ());
			if (shell -> motif.shell.lowAccelerators [modifiers] & 1 << motif_DOWN_ARROW) {
				_motif_processKeyboardEquivalent (motif_DOWN_ARROW, modifiers, event);
				return;
			}
		}
	} else if (kar == 127) {
		Widget shell = (Widget) GetWRefCon (FrontWindow ());
		if (shell -> motif.shell.lowAccelerators [modifiers] & 1 << motif_DELETE) {
			_motif_processKeyboardEquivalent (motif_DELETE, modifiers, event);
			return;
		}
		return;   /* BUG: we should implement a forward delete (or selection removal) in the text widget (IM V-192). */
	}
	/*
	 * If the Command key is pressed with a printable character, this is always a menu shortcut.
	 */
	else if (modifiers & _motif_COMMAND_MASK) {
		/*
		 * For shortcuts, the status of the Caps Lock key is immaterial.
		 */
		kar = toupper (kar);
		/*
		 * Command-X/C/V/period are defined even if there is no menu.
		 */
		if (kar == 'X') {
			/*
			 * The existence of an editable text widget forces Command-X to mean 'Cut'.
			 */
			if (me && my motif.text.editable) {
				if (event -> what != autoKey && (**my nat.text.handle). selStart < (**my nat.text.handle). selEnd) {
					_motif_clipOn (me);
					#if carbon
						ClearCurrentScrap ();
					#else
						ZeroScrap ();
					#endif
					TECut (my nat.text.handle);
					motif_mac_clipOff ();
					TEToScrap ();
					xt_callCallbacks (me, & my motif.text.valueChangedCallbacks, (XtPointer) & event);
				}
				return;
			}
		} else if (kar == 'C') {
			/*
			 * The existence of a text widget forces Command-C to mean 'Copy'.
			 */
			if (me) {
				if (event -> what != autoKey && (**my nat.text.handle). selStart < (**my nat.text.handle). selEnd) {
					#if carbon
						ClearCurrentScrap ();
					#else
						ZeroScrap ();
					#endif
					TECopy (my nat.text.handle);
					TEToScrap ();
				}
				return;
			}
		} else if (kar == 'V') {
			/*
			 * The existence of an editable text widget forces Command-V to mean 'Paste'.
			 */
			if (me && my motif.text.editable) {
				TEFromScrap ();
				_motif_clipOn (me);
				TEPaste (my nat.text.handle);
				motif_mac_clipOff ();
				xt_callCallbacks (me, & my motif.text.valueChangedCallbacks, (XtPointer) & event);
				return;
			}
		} else if (kar == '.') {
			Widget shell = (Widget) GetWRefCon (FrontWindow ());
			/*
			 * Command-period: first test for "Escape" keyboard shortcut.
			 */
			if (shell -> motif.shell.lowAccelerators [0] & 1 << motif_ESCAPE) {
				_motif_processKeyboardEquivalent (motif_ESCAPE, 0, event);
				return;
			/*
			 * Then look for cancel button in active window.
			 */
			} else if (shell && shell -> cancelButton) {
				Widget cancelButton = shell -> cancelButton;
				if (cancelButton -> activateCallback) {
					cancelButton -> activateCallback (cancelButton, cancelButton -> activateClosure, (XtPointer) event);
					return;
				}
			}
		}
		/*
		 * The text widget did not catch any Command-X/C/V/period.
		 */
		_motif_processKeyboardEquivalent (kar, modifiers, event);
		/*
		 * After executing a menu shortcut, do not send the key to a text widget as well.
		 */
		return;
	}
	if (me && my motif.text.editable) {
		_motif_clipOn (me);
		if (haveAppearance && my isControl) {
			HandleControlKey (my nat.control.handle, (event -> message & keyCodeMask) >> 8,
				event -> message & charCodeMask, event -> modifiers);
		} else {
			motif_mac_textFont ();
			TEKey (kar, my nat.text.handle);
			motif_mac_defaultFont ();
		}
		motif_mac_clipOff ();
		if (kar > 31 || kar < 28)   /* Arrows do not change the value of the text. */
			xt_callCallbacks (me, & my motif.text.valueChangedCallbacks, (XtPointer) & event);
	} else {   /* Last chance: try drawingArea. */
		Widget shell = (Widget) GetWRefCon (FrontWindow ());
		if (shell) {
			Widget drawingArea = _motif_findDrawingArea (shell);
			if (drawingArea && drawingArea -> inputCallback)
				drawingArea -> inputCallback (drawingArea, drawingArea -> inputClosure, (XtPointer) event);
		}
	}
}

static void mac_processMenuChoice (long choice, EventRecord *event) {
	int macMenuID = HiWord (choice), macMenuItem = LoWord (choice);
	Widget menu, item;
	if (macMenuID == 0) return;
	menu = theMenus [macMenuID == kHMHelpMenuID ? 255 : macMenuID];
	if (menu == NULL) return;
	#if ! carbon
		if (menu -> name [0] == appleMark && macMenuItem > 1) {
			GetMenuItemText (menu -> nat.menu.handle, macMenuItem, mac_text);
			OpenDeskAcc (mac_text);
			return;
		}
	#endif
	item = menu -> firstChild;
	if (macMenuID == kHMHelpMenuID) macMenuItem -= theHelpMenuOffset;
	while (item && macMenuItem > 1) {
		if (item -> widgetClass != xmPulldownMenuWidgetClass && item -> managed) macMenuItem --;   /* Count managed buttons and separators only. */
		item = item -> nextSibling;
	}
	Melder_assert (item != NULL);
	while (item && ! item -> managed) item = item -> nextSibling;
	Melder_assert (item != NULL);
	if (item -> activateCallback) item -> activateCallback (item, item -> activateClosure, (XtPointer) event);
	else if (item -> widgetClass == xmToggleButtonWidgetClass) {
		XmToggleButtonGadgetSetState (item, 1 - XmToggleButtonGadgetGetState (item), False);
		xt_callCallbacks (item, & item -> motif.toggleButton.valueChangedCallbacks, (XtPointer) event);
	}
}

static void _motif_processMouseDownEvent (EventRecord *event) {
	WindowPtr macvenster;
	int part = FindWindow (event -> where, & macvenster);
	switch (part) {
		case inMenuBar: {
			long choice = MenuSelect (event -> where);
			mac_processMenuChoice (choice, event);
			HiliteMenu (0);
		} break;
		case inSysWindow: {
			#if ! carbon
				SystemClick (event, macvenster);
			#endif
		} break;
		case inDrag: {
			RgnHandle greyRegion;
			Rect greyBounds;
			if (macvenster != FrontWindow ()) {
				if (_motif_modalDialog && _motif_modalDialog != macvenster) {
					SysBeep (1);
					return;
				} else {
					SelectWindow (macvenster);
				}
			}
			greyRegion = GetGrayRgn ();
			GetRegionBounds (greyRegion, & greyBounds);
			DragWindow (macvenster, event -> where, & greyBounds);
		} break;
		case inGoAway: {
			if (TrackGoAway (macvenster, event -> where)) {
				Widget shell = (Widget) GetWRefCon (macvenster);
				int deleteResponse;
				if (! shell) return;
				deleteResponse = shell -> deleteResponse;   /* Save this, in case the callback should kill the widget (XmDO_NOTHING). */
				if (shell -> motif.shell.goAwayCallback)
					shell -> motif.shell.goAwayCallback (shell, shell -> motif.shell.goAwayClosure, event);
				if (deleteResponse == XmDESTROY)
					XtDestroyWidget (shell);
				else if (deleteResponse == XmUNMAP)
					HideWindow (macvenster);
			}
		} break;
		case inGrow: {
			RgnHandle greyRegion = GetGrayRgn ();
			Rect greyBounds;
			long newSize;
			GetRegionBounds (greyRegion, & greyBounds);
			newSize = GrowWindow (macvenster, event -> where, & greyBounds);
			if (newSize != 0) {
				Widget shell = (Widget) GetWRefCon (macvenster);
				int oldWidth, oldHeight, newWidth, newHeight;
				if (! shell) return;
				oldWidth = shell -> width, oldHeight = shell -> height;
				newWidth = LoWord (newSize), newHeight = HiWord (newSize);
				shell -> width = newWidth;
				shell -> height = newHeight;
				/*Melder_info ("%d %d %d %d",oldWidth,oldHeight,newWidth,newHeight);*/
				shellResizeWidget (shell, 0, 0, newWidth - oldWidth, newHeight - oldHeight);
			}
		} break;
		case inContent: {
			if (macvenster != FrontWindow ()) {
				if (_motif_modalDialog && _motif_modalDialog != macvenster) { SysBeep (1); return; }
				SelectWindow (macvenster);
				if (! BACKGROUND_WINDOWS_ARE_ACTIVE) return;
				XmUpdateDisplay (NULL);
			}
			if (GetWindowKind (macvenster) == userKind) {
				ControlHandle maccontrol;
				SetPortWindowPort (macvenster);
				GlobalToLocal (& event -> where);
				part = FindControl (event -> where, macvenster, & maccontrol);
				if (maccontrol) {
					Widget control = (Widget) GetControlReference (maccontrol);
					if (! control) return;
					event -> message = part;
					switch (part) {
						case kControlButtonPart: {
							if (control -> widgetClass == xmPushButtonWidgetClass) {   /* Push button. */
								int pushed;
								_motif_clipOn (control);
								pushed = TrackControl (maccontrol, event -> where, NULL);
								motif_mac_clipOff ();
								if (pushed && control -> activateCallback)
									control -> activateCallback (control, control -> activateClosure, (XtPointer) event);
							} else {   /* Cascade button. */
								Widget menu = control -> subMenuId;
								if (menu && ! control -> insensitive) {
									Point pos;
									long choice = 0;
									_motif_clipOn (control);
									HiliteControl (maccontrol, 1);
									motif_mac_clipOff ();
									SetPt (& pos, control -> rect.left + 2, control -> rect.bottom);
									LocalToGlobal (& pos);
									choice = PopUpMenuSelect (menu -> nat.menu.handle, pos.v, pos.h, 1);
									/*
										Beware: we must unhighlight the cascade button NOW,
										because callbacks may destroy it.
									*/
									_motif_clipOn (control);
									HiliteControl (maccontrol, 0);
									motif_mac_clipOff ();
									mac_processMenuChoice (choice, event);
								}
							}
						} break;
						case kControlCheckBoxPart: {   /* Check box or radio button. */
							if (TrackControl (maccontrol, event -> where, NULL)) {
								int value = GetControlValue (maccontrol);
								if (control -> isRadioButton) {

									/* User clicked radio button. */
									/* Select this button. */

									if (value) return;   /* Radio button already set. */
									SetControlValue (maccontrol, 1);
									if (control -> parent -> radioBehavior) {
	
										/* Deselect the other buttons. */

										Widget child = control -> parent -> firstChild;
										while (child) {
											if (child -> widgetClass == xmToggleButtonWidgetClass && child != control)
												SetControlValue (child -> nat.control.handle, 0);
											child = child -> nextSibling;
										}
									}
								} else {

									/* User clicked check box. */
									/* Toggle its state. */

									SetControlValue (maccontrol, 1 - value);
								}
								xt_callCallbacks (control, & control -> motif.toggleButton.valueChangedCallbacks, (XtPointer) event);
							}
						} break;
						case kControlUpButtonPart:
						case kControlDownButtonPart:
						case kControlPageUpPart:
						case kControlPageDownPart: {
							static ControlActionUPP theControlActionUPP;
							if (! theControlActionUPP) theControlActionUPP = NewControlActionUPP (_motif_scrollBarAction);
							TrackControl (maccontrol, event -> where, theControlActionUPP);
						} break;
						case kControlIndicatorPart: {
							if (TrackControl (maccontrol, event -> where, NULL)) {
								#if SCROLL32
								control -> value = control -> minimum + (control -> maximum - control -> minimum - control -> sliderSize) *
									(GetControlValue (maccontrol) / 32767.0);
								#else
								control -> value = GetControlValue (maccontrol);
								#endif
								xt_callCallbacks (control, & control -> motif.scrollBar.valueChangedCallbacks, (XtPointer) (long) part);
							}
						} break;
						case kControlEditTextPart: {
							if (haveAppearance) {
								if (_motif_activeTextWidget != control) {
									if (_motif_activeTextWidget) {
										_motif_clipOn (_motif_activeTextWidget);
										ClearKeyboardFocus (_motif_activeTextWidget -> shell -> nat.window.ptr);
										DeactivateControl (maccontrol);
										motif_mac_clipOff ();
									}
									_motif_activeTextWidget = control;
									_motif_clipOn (control);
									ActivateControl (maccontrol);
									SetKeyboardFocus (control -> shell -> nat.window.ptr, control -> nat.control.handle, kControlEditTextPart);
									motif_mac_clipOff ();
									control -> shell -> activeTextWidget = _motif_activeTextWidget;
								}
								theClickedText = control;
								_motif_clipOn (control);
								HandleControlClick (maccontrol, event -> where, event -> modifiers, NULL);
								motif_mac_clipOff ();
								xt_callCallbacks (control, & control -> motif.text.motionVerifyCallbacks, 0);
							}
						} break;
						default: break;
					}
				} else {   /* Clicked in content region, but not in a control. */
					Widget shell = (Widget) GetWRefCon (macvenster);
					Widget clicked;
					if (! shell) return;
					clicked = _motif_findSubwidget (shell, event -> where. h, event -> where. v);
					if (clicked) {
						if (clicked -> widgetClass == xmListWidgetClass) {
							int doubleClick;
							_motif_clipOn (clicked);
							doubleClick = LClick (event -> where, event -> modifiers,
								clicked -> nat.list.handle);
							motif_mac_clipOff ();
							if (clicked -> extendedSelectionCallback)
								clicked -> extendedSelectionCallback (clicked, clicked -> extendedSelectionClosure, (XtPointer) event);
							if (doubleClick) {
								if (clicked -> motif.list.defaultActionCallback)
									clicked -> motif.list.defaultActionCallback (clicked, clicked -> motif.list.defaultActionClosure, (XtPointer) event);
							}
						} else if (clicked -> widgetClass == xmTextWidgetClass) {
							if (clicked -> isControl) return;   /* A click in the margin of an EditText control !! */
							if (_motif_activeTextWidget != clicked) {
								if (_motif_activeTextWidget) {
									_motif_clipOn (_motif_activeTextWidget);
									TEDeactivate (_motif_activeTextWidget -> nat.text.handle);
									motif_mac_clipOff ();
								}
								_motif_activeTextWidget = clicked;
								_motif_clipOn (clicked);
								TEActivate (clicked -> nat.text.handle);
								motif_mac_clipOff ();
								shell -> activeTextWidget = _motif_activeTextWidget;
							}
							_motif_clipOn (clicked);
							theClickedText = clicked;
							TEClick (event -> where, (event -> modifiers & shiftKey) != 0, clicked -> nat.text.handle);
							motif_mac_clipOff ();
							xt_callCallbacks (clicked, & clicked -> motif.text.motionVerifyCallbacks, 0);
						} else if (clicked -> widgetClass == xmDrawingAreaWidgetClass) {
							if (clicked -> inputCallback)
								clicked -> inputCallback (clicked, clicked -> inputClosure, (XtPointer) event);
						} else if (clicked -> widgetClass == xmCascadeButtonWidgetClass) {
							Widget menu = clicked -> subMenuId;
							if (menu && ! clicked -> insensitive) {
								Point pos;
								long choice = 0;
								Rect r = clicked -> rect; r.left -= 4; r.right += 4; r.top -= 1;
								_motif_clipOn (clicked);
								InvertRect (& r);
								motif_mac_clipOff ();
								SetPt (& pos, r.left + 1, r.bottom);
								LocalToGlobal (& pos);
								choice = PopUpMenuSelect (menu -> nat.menu.handle, pos.v, pos.h, 1);
								/*
									Beware: we must unhighlight the cascade button NOW,
									because callbacks may destroy it.
								*/
								SetPortWindowPort (clicked -> macWindow);
								_motif_update (clicked, NULL);
								mac_processMenuChoice (choice, event);
							}
						}
					}
				}
			}
		} break;
		default: break;
	}
}
#endif

#if mac
	#if carbon
		#define defaultNullEventWaitingTime  1
	#else
		#define defaultNullEventWaitingTime  10
	#endif
	static int nullEventWaitingTime = defaultNullEventWaitingTime;
	void motif_mac_setNullEventWaitingTime (int waitingTime)
		{ nullEventWaitingTime = waitingTime < 0 ? defaultNullEventWaitingTime : waitingTime; }
#endif

void XtNextEvent (XEvent *xevent) {
	#if win
		GetMessage (xevent, NULL, 0, 0);
	#elif mac
		if (_motif_activeTextWidget) {
		 	_motif_clipOn (_motif_activeTextWidget);
			if (haveAppearance && _motif_activeTextWidget -> isControl) {
				IdleControls (_motif_activeTextWidget -> macWindow);
			} else {
				TEIdle (_motif_activeTextWidget -> nat.text.handle);   /* Make caret blink. */
			}
			motif_mac_clipOff ();
		}
		WaitNextEvent (everyEvent, (EventRecord *) xevent, nullEventWaitingTime, NULL);
	#endif
}

static void processWorkProcsAndTimeOuts (void) {
	long i;
	if (theNumberOfWorkProcs) for (i = 9; i >= 1; i --)
		if (theWorkProcs [i])
			if (theWorkProcs [i] (theWorkProcClosures [i])) XtRemoveWorkProc (i);
	#ifndef __MACH__
		if (theNumberOfTimeOuts) {
			clock_t now = clock ();
			for (i = 1; i < 10; i ++) if (theTimeOutProcs [i]) {
				static volatile clock_t timeElapsed;   /* Careful: use 32-bit integers circularly; prevent optimization. */
				timeElapsed = now - theTimeOutStarts [i];
				if (timeElapsed > theTimeOutIntervals [i]) {
					theTimeOutProcs [i] (theTimeOutClosures [i], & i);
					XtRemoveTimeOut (i);
				}
			}
		}
	#endif
}

void XtAppNextEvent (XtAppContext appContext, XEvent *xevent) {
	(void) appContext;
	#if win
		if (theNumberOfWorkProcs != 0 || theNumberOfTimeOuts != 0) {
			if (PeekMessage (xevent, 0, 0, 0, PM_REMOVE)) {   // Message available?
				;   // Hand message to XtDispatchEvent.
			} else {
				processWorkProcsAndTimeOuts ();   // Handle chores during idle time.
				xevent -> message = 0;   // Hand null message to XtDispatchEvent.
			}
		} else GetMessage (xevent, NULL, 0, 0);   // Be neighbour-friendly: do not hand null events.
	#elif mac
		processWorkProcsAndTimeOuts ();
		XtNextEvent (xevent);
	#endif
}

#if win

static int win_shell_processKeyboardEquivalent (Widget me, int kar, int modifiers) {
	int imenu;
	for (imenu = 1; imenu <= 255; imenu ++) if (theMenus [imenu] && theMenus [imenu] -> shell == me) {
		Widget child;
		for (child = theMenus [imenu] -> firstChild; child != NULL; child = child -> nextSibling)
			if (child -> widgetClass == xmPushButtonWidgetClass &&
					child -> motif.pushButton.acceleratorChar == kar &&
					child -> motif.pushButton.acceleratorModifiers == modifiers)
				if (child -> activateCallback && ! child -> insensitive) {
					child -> activateCallback (child, child -> activateClosure, 0);
					return 1;
				}
	}
	return 0;
}

static int win_processKeyboardEquivalent (Widget me, int kar, int modifiers) {
	/*
	 * First try to send the key command to the active shell.
	 * If that fails, try to send the key command to the application shell.
	 */
	if (win_shell_processKeyboardEquivalent (me, kar, modifiers)) return 1;
	if (win_shell_processKeyboardEquivalent (theApplicationShell, kar, modifiers)) return 1;
	return 0;
}

	static Widget _motif_findDrawingArea (Widget me) {
		Widget sub;
		if (my widgetClass == xmDrawingAreaWidgetClass) return me;
		for (sub = my firstChild; sub != NULL; sub = sub -> nextSibling)
			if (! MEMBER (sub, Shell)) {   /* Only in same top window. */
				Widget drawingArea = _motif_findDrawingArea (sub);
				if (drawingArea) return drawingArea;
			}
		return NULL;   /* No DrawingArea found. */
	}
#endif

void XtDispatchEvent (XEvent *xevent) {
	#if mac
		EventRecord *event = (EventRecord *) xevent;
		switch (event -> what) {
			case nullEvent: break;
			case mouseDown: _motif_processMouseDownEvent (event); break;
			case mouseUp: break;
			case keyDown: _motif_processKeyDownEvent (event); break;
			case keyUp: break;
			case autoKey: _motif_processKeyDownEvent (event); break;
			case updateEvt: _motif_processUpdateEvent (event); break;
			#if ! carbon
			case diskEvt: if (HiWord (event -> message) != noErr) { Point p = { 100, 100 }; DIBadMount (p, event -> message); } break;
			#endif
			case activateEvt: _motif_processActivateEvent (event); break;
			case osEvt: _motif_processOsEvent (event); break;
			case kHighLevelEvent: _motif_processHighLevelEvent (event); break;
			default: break;
		}
	#else
		MSG *message = (MSG *) xevent;
		if (message -> message == 0) return;   // null message from PeekMessage during work proc or time out.
/*if (message -> message == WM_KEYDOWN || message -> message == WM_SYSKEYDOWN)
{
	int kar = LOWORD (message -> wParam);
	int modifiers = 0;
	Widget me = (Widget) GetWindowLong (message -> hwnd, GWL_USERDATA);
	if (GetKeyState (VK_CONTROL) < 0) modifiers |= _motif_COMMAND_MASK;
	if (GetKeyState (VK_MENU) < 0) modifiers |= _motif_OPTION_MASK;
	if (GetKeyState (VK_SHIFT) < 0) modifiers |= _motif_SHIFT_MASK;
if(kar>=48)Melder_casual ("modifiers:%s%s%s\nmessage: %s\nkar: %d",
modifiers & _motif_COMMAND_MASK ? " control" : "",
modifiers & _motif_OPTION_MASK ? " alt" : "",
modifiers & _motif_SHIFT_MASK ? " shift" : "", message -> message == WM_KEYDOWN ? "keydown" : "syskeydown", kar);
}*/
		/*
		 * Intercept accelerators, which may be function keys or Command combinations.
		 * Some Alt-GR combinations denote special characters (e.g. backslash) on some (e.g. German) keyboards;
		 * in such a case, the message is WM_KEYDOWN, and the reported modifier keys are Control (!) and Alt
		 * (on a German keyboard, the backslash can be generated by Ctrl-Alt-ringel-s as well);
		 * otherwise Alt keys give WM_SYSKEYDOWN messages.
		 */
		if (message -> message == WM_KEYDOWN && (GetKeyState (VK_CONTROL) < 0 || ! (message -> lParam & (1<<29))) ||
			message -> message == WM_SYSKEYDOWN && GetKeyState (VK_MENU) < 0
				&& (message -> lParam & (1<<29)) ||   // R&N 413: Alt key is pressed
			message -> message == WM_SYSKEYDOWN && GetKeyState (VK_CONTROL) < 0)
		{
			int kar = LOWORD (message -> wParam);
			int modifiers = 0;
			Widget me = (Widget) GetWindowLong (message -> hwnd, GWL_USERDATA);
			if (GetKeyState (VK_CONTROL) < 0) modifiers |= _motif_COMMAND_MASK;
			if (GetKeyState (VK_MENU) < 0) modifiers |= _motif_OPTION_MASK;
			if (GetKeyState (VK_SHIFT) < 0) modifiers |= _motif_SHIFT_MASK;
/*if(kar>=48)Melder_casual ("modifiers:%s%s%s\nmessage: %s\nkar: %d",
modifiers & _motif_COMMAND_MASK ? " control" : "",
modifiers & _motif_OPTION_MASK ? " alt" : "",
modifiers & _motif_SHIFT_MASK ? " shift" : "", message -> message == WM_KEYDOWN ? "keydown" : "syskeydown", kar);*/
			if (me && my shell) {
				unsigned long acc = my shell -> motif.shell.lowAccelerators [modifiers];
				if (kar < 48) {
					if (kar == VK_BACK) {   /* Shortcut or text. */
						if (acc & 1 << motif_BACKSPACE) { win_processKeyboardEquivalent (my shell, motif_BACKSPACE, modifiers); return; }
					} else if (kar == VK_TAB) {   /* Shortcut or text. */
						if (acc & 1 << motif_TAB) { win_processKeyboardEquivalent (my shell, motif_TAB, modifiers); return; }
					} else if (kar == VK_RETURN) {   /* Shortcut, default button, or text. */
						if (acc & 1 << motif_ENTER) { win_processKeyboardEquivalent (my shell, motif_ENTER, modifiers); return; }
						else {
							Widget defaultButton = my shell -> defaultButton;
							if (defaultButton && defaultButton -> activateCallback)
								{ defaultButton -> activateCallback (defaultButton, defaultButton -> activateClosure, 0); return; }
						}
					} else if (kar == VK_ESCAPE) {   /* Shortcut or cancel button. */
						if (acc & 1 << motif_ESCAPE) { win_processKeyboardEquivalent (my shell, motif_ESCAPE, modifiers); return; }
						else {
							Widget cancelButton = my shell -> cancelButton;
							if (cancelButton && cancelButton -> activateCallback)
								{ cancelButton -> activateCallback (cancelButton, cancelButton -> activateClosure, 0); return; }
						}
						return;
					} else if (kar == VK_PRIOR) {   /* Shortcut or text. */
						if (acc & 1 << motif_PAGE_UP) { win_processKeyboardEquivalent (my shell, motif_PAGE_UP, modifiers); return; }
					} else if (kar == VK_NEXT) {   /* Shortcut or text. */
						if (acc & 1 << motif_PAGE_DOWN) { win_processKeyboardEquivalent (my shell, motif_PAGE_DOWN, modifiers); return; }
					} else if (kar == VK_HOME) {   /* Shortcut or text. */
						if (acc & 1 << motif_HOME) { win_processKeyboardEquivalent (my shell, motif_HOME, modifiers); return; }
					} else if (kar == VK_END) {   /* Shortcut or text. */
						if (acc & 1 << motif_END) { win_processKeyboardEquivalent (my shell, motif_END, modifiers); return; }
					} else if (kar == VK_LEFT) {   /* Shortcut or text. */
						if (acc & 1 << motif_LEFT_ARROW) { win_processKeyboardEquivalent (my shell, motif_LEFT_ARROW, modifiers); return; }
					} else if (kar == VK_RIGHT) {   /* Shortcut or text. */
						if (acc & 1 << motif_RIGHT_ARROW) { win_processKeyboardEquivalent (my shell, motif_RIGHT_ARROW, modifiers); return; }
					} else if (kar == VK_UP) {   /* Shortcut or text. */
						if (acc & 1 << motif_UP_ARROW) { win_processKeyboardEquivalent (my shell, motif_UP_ARROW, modifiers); return; }
					} else if (kar == VK_DOWN) {   /* Shortcut or text. */
						if (acc & 1 << motif_DOWN_ARROW) { win_processKeyboardEquivalent (my shell, motif_DOWN_ARROW, modifiers); return; }
					} else if (kar == VK_INSERT) {   /* Shortcut. */
						win_processKeyboardEquivalent (my shell, motif_INSERT, modifiers);
						return;
					} else if (kar == VK_DELETE) {   /* Shortcut or text. */
						if (acc & 1 << motif_DELETE) { win_processKeyboardEquivalent (my shell, motif_DELETE, modifiers); return; }
					} else if (kar == VK_HELP) {   /* Simulate Command-?. */
						win_processKeyboardEquivalent (my shell, '?', modifiers | _motif_SHIFT_MASK);
						return;
					}
				} else if (kar >= VK_F1 && kar <= VK_F12) {   /* 112...123 */
					/*
					 * She has pressed one of the F keys.
					 */
					if (win_processKeyboardEquivalent (my shell, kar - VK_F1 + motif_F1, modifiers)) return;
					/* Let windowProc handle Alt-F4 etc. */
				/*
				 * If the Command key is pressed with a printable character, this is often a menu shortcut.
				 */
				} else if (modifiers & _motif_COMMAND_MASK) {
					if (MEMBER (me, Text) && (kar == 'X' || kar == 'C' || kar == 'V' || kar == 'Z')) {
						;   // Let window proc handle text editing.
					} else if (kar >= 186) {
						int shift = modifiers & _motif_SHIFT_MASK;
						/*
						 * BUG: The following is not internationally correct.
						 */
						if (kar == 186 && win_processKeyboardEquivalent (my shell, shift ? ':' : ';', modifiers) ||
						    kar == 187 && win_processKeyboardEquivalent (my shell, shift ? '+' : '=', modifiers) ||
						    kar == 188 && win_processKeyboardEquivalent (my shell, shift ? '<' : ',', modifiers) ||
						    kar == 189 && win_processKeyboardEquivalent (my shell, shift ? '_' : '-', modifiers) ||
						    kar == 190 && win_processKeyboardEquivalent (my shell, shift ? '>' : motif_ESCAPE, shift ? modifiers : 0) ||   /* Command-period */
						    kar == 191 && win_processKeyboardEquivalent (my shell, shift ? '?' : '/', modifiers) ||
						    kar == 192 && win_processKeyboardEquivalent (my shell, shift ? '~' : '`', modifiers) ||
						    kar == 219 && win_processKeyboardEquivalent (my shell, shift ? '{' : '[', modifiers) ||   /* Alt-GR-ringel-s is here. */
						    kar == 220 && win_processKeyboardEquivalent (my shell, shift ? '|' : '\\', modifiers) ||
						    kar == 221 && win_processKeyboardEquivalent (my shell, shift ? '}' : ']', modifiers) ||
						    kar == 222 && win_processKeyboardEquivalent (my shell, shift ? '\"' : '\'', modifiers))
						{
							return;
						}
					} else {
						if (win_processKeyboardEquivalent (my shell, kar, modifiers)) return;   /* Handle shortcuts like Ctrl-T and Ctrl-Alt-T. */
						/* Let window proc handle international Alt-GR (= Ctrl-Alt) sequences, which are plain characters. */
					}
				}
				/* Other characters: to text. */
			}
			/* Not me or not my shell: let windowProc handle. */
		} else if (message -> message == WM_CHAR) {
			int kar = LOWORD (message -> wParam);
			/*
			 * Catch character messages to push buttons and toggle buttons:
			 * divert them to a drawing area, if possible.
			 */
			Widget me = (Widget) GetWindowLong (message -> hwnd, GWL_USERDATA);
			if (me && MEMBER2 (me, PushButton, ToggleButton)) {
				Widget drawingArea = _motif_findDrawingArea (my shell);
				if (drawingArea) {
					if (drawingArea -> inputCallback) {
						WinDrawingAreaEvent event;
						event. message = WM_CHAR;
						event. key = kar;
						event. shiftKeyPressed = GetKeyState (VK_SHIFT) < 0;
						drawingArea -> inputCallback (drawingArea, drawingArea -> inputClosure, (XtPointer) & event);
					}
					return;
				}
			}
		} else if (message -> message == WM_LBUTTONDOWN) {
			/*
			 * Catch mouse-down messages to cascade buttons:
			 * post the associated menu, if any.
			 */
			Widget me = (Widget) GetWindowLong (message -> hwnd, GWL_USERDATA);
			if (me && MEMBER (me, CascadeButton) && my motif.cascadeButton.pushButtonLook && my subMenuId) {
				RECT rect;
				GetWindowRect (my window, & rect);
				TrackPopupMenu (my subMenuId -> nat.menu.handle, TPM_LEFTBUTTON |
					TPM_LEFTALIGN | TPM_TOPALIGN, rect.left, rect.bottom - 3, 0, my parent -> window, NULL);
				return;
			}
		}
		TranslateMessage (xevent);   // Generate WM_CHAR messages.
		DispatchMessage (xevent);
	#endif
}

void XtAppMainLoop (XtAppContext appctxt) {
	for (;;) {
		XEvent event;
		XtAppNextEvent (appctxt, & event);
		XtDispatchEvent (& event);
	}
}

#if win
	extern int main (int argc, char *argv []);
	int APIENTRY WinMain (HINSTANCE instance, HINSTANCE previousInstance, LPSTR commandLine, int commandShow) {
		int argc = 4;
		char instanceString [20], commandShowString [20], *argv [4];
		(void) previousInstance;
		argv [0] = "dummy";
		sprintf (instanceString, "%ld", (long) instance);
		sprintf (commandShowString, "%d", commandShow);
		argv [1] = & instanceString [0];
		argv [2] = & commandShowString [0];
		argv [3] = commandLine;
		return main (argc, & argv [0]);
	}

	static void on_close (HWND window) {
		Widget me = (Widget) GetWindowLong (window, GWL_USERDATA);
		if (me) {
			if (my widgetClass == xmShellWidgetClass) {
				int deleteResponse = my deleteResponse;   /* Save this, in case the callback should kill the widget (XmDO_NOTHING). */
				Widget parent = my parent;
				if (my motif.shell.goAwayCallback)
					my motif.shell.goAwayCallback (me, my motif.shell.goAwayClosure, NULL);
				if (deleteResponse == XmDESTROY)
					XtDestroyWidget (me);
				else if (deleteResponse == XmUNMAP)
					XtUnmanageChild (me);
				return;
			} else FORWARD_WM_CLOSE (window, DefWindowProc);
		} else FORWARD_WM_CLOSE (window, DefWindowProc);
	}
	static Widget findItem (Widget menu, int id) {
		Widget child = menu -> firstChild;
		for (child = menu -> firstChild; child != NULL; child = child -> nextSibling) {
			if (child -> widgetClass == xmPulldownMenuWidgetClass) {
				Widget result = findItem (child, id);
				if (result) return result;
			} else {
				Melder_assert (MEMBER4 (child, PushButton, ToggleButton, CascadeButton, Separator));
				if (child -> nat.entry.id == id) return child;
			}
		}
		return NULL;
	}
	static void on_command (HWND window, int id, HWND controlWindow, UINT codeNotify) {
		Widget parent = (Widget) GetWindowLong (window, GWL_USERDATA);
		if (parent) {
			if (controlWindow) {
				Widget control = (Widget) GetWindowLong (controlWindow, GWL_USERDATA);
				if (control) {
					switch (control -> widgetClass) {
						/*
						 * Clicking on a button takes away the focus from a text widget.
						 * We have to put it back sometimes; this has to be done *before*
						 * calling the callbacks, because these may destroy or hide the widget.
						 */
						case xmPushButtonWidgetClass:
							if (control -> shell -> activeTextWidget) {
								_motif_activeTextWidget = control -> shell -> activeTextWidget;
								SetFocus (_motif_activeTextWidget -> window);
							}
							if (control -> activateCallback)
								control -> activateCallback (control, control -> activateClosure, 0);
							break;
						case xmToggleButtonWidgetClass:
							if (control -> shell -> activeTextWidget) {
								_motif_activeTextWidget = control -> shell -> activeTextWidget;
								SetFocus (_motif_activeTextWidget -> window);
							}
							xt_callCallbacks (control, & control -> motif.toggleButton.valueChangedCallbacks, 0);
							break;
						case xmListWidgetClass:
							if (codeNotify == LBN_SELCHANGE) {
								if (control -> extendedSelectionCallback)
									control -> extendedSelectionCallback (control, control -> extendedSelectionClosure, 0);
							} else FORWARD_WM_COMMAND (window, id, controlWindow, codeNotify, DefWindowProc);
							break;
						case xmTextWidgetClass:
							if (codeNotify == EN_CHANGE)
								xt_callCallbacks (control, & control -> motif.text.valueChangedCallbacks, 0);
							break;
						default: break;
					}
				} else FORWARD_WM_COMMAND (window, id, controlWindow, codeNotify, DefWindowProc);
			} else {   // menu choice
				Widget menuBar = NULL;
				if (MEMBER (parent, Shell))
					menuBar = parent -> nat.shell.menuBar;
				else if (MEMBER (parent, RowColumn))
					menuBar = parent;
				if (menuBar) {
					Widget item = findItem (menuBar, id);
					if (item) {
						if (item -> widgetClass == xmPushButtonWidgetClass) {
							if (item -> activateCallback)
								item -> activateCallback (item, item -> activateClosure, 0);
						} else if (item -> widgetClass == xmToggleButtonWidgetClass) {
							XmToggleButtonGadgetSetState (item, 1 - XmToggleButtonGadgetGetState (item), False);
							xt_callCallbacks (item, & item -> motif.toggleButton.valueChangedCallbacks, 0);
						}
					}
				}
			}
		} else FORWARD_WM_COMMAND (window, id, controlWindow, codeNotify, DefWindowProc);
	}
	static void on_destroy (HWND window) {
		(void) window;
	}
	static void on_lbuttonDown (HWND window, BOOL doubleClick, int x, int y, UINT flags) {
		Widget me = (Widget) GetWindowLong (window, GWL_USERDATA);
		if (me) {
			if (MEMBER (me, DrawingArea)) {
				if (my inputCallback) {
					WinDrawingAreaEvent event;
					event. message = WM_LBUTTONDOWN;
					event. x = x;
					event. y = y;
					event. shiftKeyPressed = GetKeyState (VK_SHIFT) < 0;
					my inputCallback (me, my inputClosure, (XtPointer) & event);
				}
			} else FORWARD_WM_LBUTTONDOWN (window, doubleClick, x, y, flags, DefWindowProc);
		} else FORWARD_WM_LBUTTONDOWN (window, doubleClick, x, y, flags, DefWindowProc);
	}
	static void on_paint (HWND window) {
		Widget me = (Widget) GetWindowLong (window, GWL_USERDATA);
		if (me) {
			if (my widgetClass == xmDrawingAreaWidgetClass) {
				PAINTSTRUCT paintStruct;
				BeginPaint (window, & paintStruct);
				if (my exposeCallback)
					my exposeCallback (me, my exposeClosure, (XtPointer) & paintStruct);
				EndPaint (window, & paintStruct);
				return;
			} else FORWARD_WM_PAINT (window, DefWindowProc);
		} else FORWARD_WM_PAINT (window, DefWindowProc);
	}
	static void on_hscroll (HWND window, HWND controlWindow, UINT code, int pos) {
		Widget parent = (Widget) GetWindowLong (window, GWL_USERDATA);
		if (parent) {
			Widget control = (Widget) GetWindowLong (controlWindow, GWL_USERDATA);
			if (control) {
				on_scroll (control, code, pos);
			} else FORWARD_WM_HSCROLL (window, controlWindow, code, pos, DefWindowProc);
		} else FORWARD_WM_HSCROLL (window, controlWindow, code, pos, DefWindowProc);
	}
	static void on_vscroll (HWND window, HWND controlWindow, UINT code, int pos) {
		Widget parent = (Widget) GetWindowLong (window, GWL_USERDATA);
		if (parent) {
			Widget control = (Widget) GetWindowLong (controlWindow, GWL_USERDATA);
			if (control) {
				on_scroll (control, code, pos);
			} else FORWARD_WM_VSCROLL (window, controlWindow, code, pos, DefWindowProc);
		} else FORWARD_WM_VSCROLL (window, controlWindow, code, pos, DefWindowProc);
	}
	static void on_size (HWND window, UINT state, int cx, int cy) {
		Widget me = (Widget) GetWindowLong (window, GWL_USERDATA);
		if (me && MEMBER (me, Shell) && (state == SIZE_RESTORED || state == SIZE_MAXIMIZED)) {
			int oldWidth = my width, oldHeight = my height;
			int newWidth = cx;
			int newHeight = cy;
			my width = newWidth;
			my height = newHeight;
			FORWARD_WM_SIZE (window, state, cx, cy, DefWindowProc);
			if (! my managed) {
			} else if (my nat.shell.duringMoveWindow)   // Yeah, a BUG fix. Only react to user actions.
				my nat.shell.duringMoveWindow = False;
			else if (newWidth != oldWidth || newHeight != oldHeight) {
				shellResizeWidget (me, 0, 0, newWidth - oldWidth, newHeight - oldHeight);
			}
		} else FORWARD_WM_SIZE (window, state, cx, cy, DefWindowProc);
	}
	static void on_char (HWND window, TCHAR kar, int repeat) {
		Widget me = (Widget) GetWindowLong (window, GWL_USERDATA);
		if (me) {
			if (MEMBER (me, Shell)) {
				Widget drawingArea = _motif_findDrawingArea (me);
				if (drawingArea) {
					Widget activeTextWidget = drawingArea -> parent -> focusWidget;   /* Just hope it does not dangle. */
					/*if (activeTextWidget) {
						SetFocus (activeTextWidget -> window);
						FORWARD_WM_CHAR (activeTextWidget -> window, kar, repeat, DefWindowProc);
					} else */ if (drawingArea -> inputCallback) {
						WinDrawingAreaEvent event;
						event. message = WM_CHAR;
						event. key = kar;
						event. shiftKeyPressed = GetKeyState (VK_SHIFT) < 0;
						drawingArea -> inputCallback (drawingArea, drawingArea -> inputClosure, (XtPointer) & event);
					}
				} else {
					FORWARD_WM_CHAR (window, kar, repeat, DefWindowProc);
				}
			} else FORWARD_WM_CHAR (window, kar, repeat, DefWindowProc);
		} else FORWARD_WM_CHAR (window, kar, repeat, DefWindowProc);
	}
	static void on_move (HWND window, int x, int y) {
		Widget me = (Widget) GetWindowLong (window, GWL_USERDATA);
		/*if (me && MEMBER (me, Shell)) {
			my x = x - ( my motif.shell.isDialog ? GetSystemMetrics (SM_CXFIXEDFRAME) : GetSystemMetrics (SM_CXSIZEFRAME) );
			my y = y - GetSystemMetrics (SM_CYCAPTION) - ( my motif.shell.isDialog ? GetSystemMetrics (SM_CYFIXEDFRAME) :
				GetSystemMetrics (SM_CYSIZEFRAME) + GetSystemMetrics (SM_CYMENU) );
		}*/
		FORWARD_WM_MOVE (window, x, y, DefWindowProc);
	}
	static HBRUSH on_ctlColorStatic (HWND window, HDC hdc, HWND controlWindow, int type) {
		Widget parent = (Widget) GetWindowLong (window, GWL_USERDATA);
		(void) type;
		if (parent) {
			Widget control = (Widget) GetWindowLong (controlWindow, GWL_USERDATA);
			if (control) {
				SetBkMode (hdc, TRANSPARENT);
				return GetStockBrush (LTGRAY_BRUSH);
			}
		}
		return FORWARD_WM_CTLCOLORSTATIC (window, hdc, controlWindow, DefWindowProc);
	}
	static HBRUSH on_ctlColorBtn (HWND window, HDC hdc, HWND controlWindow, int type) {
		Widget parent = (Widget) GetWindowLong (window, GWL_USERDATA);
		(void) type;
		if (parent) {
			Widget control = (Widget) GetWindowLong (controlWindow, GWL_USERDATA);
			if (control) {
				SetBkMode (hdc, TRANSPARENT);
				return GetStockBrush (LTGRAY_BRUSH);
			}
		}
		return FORWARD_WM_CTLCOLORBTN (window, hdc, controlWindow, DefWindowProc);
	}
	static void on_activate (HWND window, UINT state, HWND hActive, BOOL minimized) {
		Widget me = (Widget) GetWindowLong (window, GWL_USERDATA);
		if (me && my widgetClass == xmShellWidgetClass) {
			if (state == WA_INACTIVE || minimized) {
				_motif_activeTextWidget = NULL;
			} else {
				if (my activeTextWidget) {
					_motif_activeTextWidget = my activeTextWidget;
					SetFocus (_motif_activeTextWidget -> window);
				}
			}
			return;
		} else FORWARD_WM_ACTIVATE (window, state, hActive, minimized, DefWindowProc);
	}
	static LRESULT CALLBACK windowProc (HWND window, UINT message, WPARAM wParam, LPARAM lParam) {
		switch (message) {
			HANDLE_MSG (window, WM_CLOSE, on_close);
			HANDLE_MSG (window, WM_COMMAND, on_command);
			HANDLE_MSG (window, WM_DESTROY, on_destroy);
			HANDLE_MSG (window, WM_LBUTTONDOWN, on_lbuttonDown);
			HANDLE_MSG (window, WM_PAINT, on_paint);
			HANDLE_MSG (window, WM_HSCROLL, on_hscroll);
			HANDLE_MSG (window, WM_VSCROLL, on_vscroll);
			HANDLE_MSG (window, WM_SIZE, on_size);
			HANDLE_MSG (window, WM_CHAR, on_char);
			HANDLE_MSG (window, WM_MOVE, on_move);
			HANDLE_MSG (window, WM_CTLCOLORBTN, on_ctlColorBtn);
			HANDLE_MSG (window, WM_CTLCOLORSTATIC, on_ctlColorStatic);
			HANDLE_MSG (window, WM_ACTIVATE, on_activate);
			case WM_USER: {
				/*if (IsIconic (window)) ShowWindow (window, SW_RESTORE);
				SetForegroundWindow (window);*/
				return theUserMessageCallback ? theUserMessageCallback () : 1;
			}
			default: return DefWindowProc (window, message, wParam, lParam);
		}
	}
	int motif_win_mouseStillDown (void) {
		XEvent event;
		if (! GetCapture ()) SetCapture (theApplicationShell -> window);
		if (PeekMessage (& event, 0, 0, 0, PM_REMOVE)) {
			if (event. message == WM_LBUTTONUP) {
				DispatchMessage (& event);
				ReleaseCapture ();
				return False;
			}
		}
		return True;
	}
	void motif_win_setUserMessageCallback (int (*userMessageCallback) (void)) {
		theUserMessageCallback = userMessageCallback;
	}
#endif
#if mac
	void motif_mac_setUserMessageCallback (int (*userMessageCallback) (char *message)) {
		theUserMessageCallback = userMessageCallback;
	}
#endif
void motif_setOpenDocumentCallback (int (*openDocumentCallback) (MelderFile file)) {
	theOpenDocumentCallback = openDocumentCallback;
}
void motif_setQuitApplicationCallback (int (*quitApplicationCallback) (void)) {
	theQuitApplicationCallback = quitApplicationCallback;
}
#endif
/* End of file motifEmulator.c */
