/*                   -*-Mode: c; c-file-style: "GNU"-*-              */
/**
 *
 * $Header: /cvsroot/hungry/lesstif/lib/Xm/TextOut.c,v 1.42 1998/05/06 21:32:11 u27113 Exp $
 *
 * Copyright (C) 1995 Free Software Foundation, Inc.
 *
 * This file is part of the GNU LessTif Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **/

static char rcsid[] = "$Header: /cvsroot/hungry/lesstif/lib/Xm/TextOut.c,v 1.42 1998/05/06 21:32:11 u27113 Exp $";

#include <LTconfig.h>
#include <Xm/XmP.h>
#include <Xm/TextP.h>
#include <Xm/TextOutP.h>
#include <Xm/ScrolledWP.h>
#include <Xm/ScrollBarP.h>
#include <XmI/XmI.h>
#include <X11/Xfuncs.h>
#include <limits.h>		/* for INT_MAX */
#ifdef HAVE_STRING_H
#include <string.h>
#define ANSI_STRING
#else
#include <strings.h>
#endif
#include <stdlib.h>

#include <XmI/DebugUtil.h>

#undef VERBOSE

#ifndef XmUNSPECIFIED
#define XmUNSPECIFIED (~0)
#endif

#ifndef _XmMin
#define _XmMin(a,b)  ((a) < (b)) ? (a) : (b)
#endif
#ifndef _XmMax
#define _XmMax(a,b)  ((a) > (b)) ? (a) : (b)
#endif
static void Draw(XmTextWidget w, LineNum num, XmTextPosition start, XmTextPosition end, XmHighlightMode highlight);

#define Offset(field) XtOffsetOf(XmTextInnerRec, inner.out.field)

/* Resources for the TextInner class */
static XtResource output_resources[] =
{
    {
	XmNblinkRate, XmCBlinkRate, XmRInt,
	sizeof(int), Offset(blinkrate),
	XmRImmediate, (XtPointer)500
    },
    {
	XmNcolumns, XmCColumns, XmRShort,
	sizeof(short), Offset(columns),
	XmRImmediate, (XtPointer)20
    },
    {
	XmNcursorPositionVisible, XmCCursorPositionVisible, XmRBoolean,
	sizeof(Boolean), Offset(cursor_position_visible),
	XmRImmediate, (XtPointer)True
    },
    {
	XmNfontList, XmCFontList, XmRFontList,
	sizeof(XmFontList), Offset(fontlist),
	XmRImmediate, (XtPointer)XmUNSPECIFIED
    },
    {
	XmNresizeHeight, XmCResizeHeight, XmRBoolean,
	sizeof(Boolean), Offset(resizeheight),
	XmRImmediate, (XtPointer)False
    },
    {
	XmNresizeWidth, XmCResizeWidth, XmRBoolean,
	sizeof(Boolean), Offset(resizewidth),
	XmRImmediate, (XtPointer)False
    },
    {
	XmNrows, XmCRows, XmRShort,
	sizeof(short), Offset(rows),
	XmRImmediate, (XtPointer)1
    },
    {
	XmNwordWrap, XmCWordWrap, XmRBoolean,
	sizeof(Boolean), Offset(wordwrap),
	XmRImmediate, (XtPointer)False
    },

    /* ScrolledText resources */
    {
	XmNscrollHorizontal, XmCScroll, XmRBoolean,
	sizeof(Boolean), Offset(scrollhorizontal),
	XmRImmediate, (XtPointer)True
    },
    {
	XmNscrollLeftSide, XmCScrollSide, XmRBoolean,
	sizeof(Boolean), Offset(scrollleftside),
	XmRImmediate, (XtPointer)False
    },
    {
	XmNscrollTopSide, XmCScrollSide, XmRBoolean,
	sizeof(Boolean), Offset(scrolltopside),
	XmRImmediate, (XtPointer)False
    },
    {
	XmNscrollVertical, XmCScroll, XmRBoolean,
	sizeof(Boolean), Offset(scrollvertical),
	XmRImmediate, (XtPointer)True
    },
};
/*
 * Common initialization procedures -------------------------------------------
 */

/* FIX ME - this thing is clearly overly simplistic. */
/* I think it should :
 *    - grab a fontset whenever it can (bypassing fonts)
 *      - make sure you pick any fontset (preferably) or font (if all else
 *        fails) from the list if nothing matches the tag we want
 */
/*
 * Note : never *ever* use XmFONTLIST_DEFAULT_TAG without checking
 * XmSTRING_DEFAULT_CHARSET as well. Pre-1.2 docs use the latter everywhere.
 */
static void
FontInit(XmTextWidget w)
{
    OutputData o = Text_OutputData(w);
    XmFontListEntry entry = NULL;
    XFontStruct *fs;
    int i;

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w, "FontInit"));

    /* This part is probably all wrong ... FIX ME */
    for (i = 0; Out_FontList(o)[i].tag != NULL; i++)
    {
	if (strcmp(XmFONTLIST_DEFAULT_TAG, Out_FontList(o)[i].tag) == 0 ||
	    strcmp(XmSTRING_DEFAULT_CHARSET, Out_FontList(o)[i].tag) == 0)
	{
	    entry = &Out_FontList(o)[i];

	    if (entry->font == NULL)
	    {
		entry = NULL;
		continue;
	    }

	    break;
	}
    }

    if (!entry)
    {
	for (i = 0; Out_FontList(o)[i].tag != NULL; i++)
	{
	    if (Out_FontList(o)[i].type == XmFONT_IS_FONTSET)
	    {
		entry = &Out_FontList(o)[i];

		if (entry->font == NULL)
		{
		    entry = NULL;

		    continue;
		}

		break;
	    }
	}
    }

    if (!entry)
    {
	for (i = 0; Out_FontList(o)[i].tag != NULL; i++)
	{
	    if (Out_FontList(o)[i].type == XmFONT_IS_FONT)
	    {
		entry = &Out_FontList(o)[i];

		if (entry->font == NULL)
		{
		    entry = NULL;
		    continue;
		}

		break;
	    }
	}
    }
    /* (Hopefully) end of the all wrong part */

    /* This should only be the very very last resort. */
    if (entry == NULL || entry->font == NULL)
    {
	Out_FontList(o) = entry =
	    _XmFontListCreateDefault(XtDisplay((Widget)w));
    }

    /* We may get a font or a fontset here */
    if (entry->type == XmFONT_IS_FONT)
    {
	fs = (XFontStruct *)entry->font;

	Out_Font(o) = fs;
	Out_FontHeight(o) = Out_FontAscent(o) + Out_FontDescent(o);
	Out_FontAverageWidth(o) =
	    (fs->max_bounds.width + fs->min_bounds.width) / 2;

	Out_TabWidth(o) = 8 * Out_FontAverageWidth(o);
    }
    else
    {				/* XmFONT_IS_FONTSET */
	XFontStruct **fsl;
	char **nl;
	int r, i;

	r = XFontsOfFontSet((XFontSet)entry->font, &fsl, &nl);
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w, "XFontsOfFontSet : %d\n", r));
	for (i = 0; i < r; i++)
	{
	    DEBUGOUT(XdbDebug0(__FILE__, NULL, "\t%s\n", nl[i]));
	}

	/* FIX ME - just pick the first one */
	if (r == 0)
	{
	    DEBUGOUT(XdbDebug(__FILE__, NULL, "No font in fontset\n"));

	    abort();
	}

	fs = fsl[0];
	Out_Font(o) = fs;
	Out_FontHeight(o) = Out_FontAscent(o) + Out_FontDescent(o);
	Out_FontAverageWidth(o) =
	    (fs->max_bounds.width + fs->min_bounds.width) / 2;

	Out_TabWidth(o) = 8 * Out_FontAverageWidth(o);
    }

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "font: ascent=%d descent=%d height=%d avewidth=%d\n",
		      Out_FontAscent(o), Out_FontDescent(o),
		      Out_FontHeight(o), Out_FontAverageWidth(o)));
}

/*
 * FIX ME
 *
 * This thing still creates GC's the anti-social way - with XCreateGC.
 * By using XtGetGC or XtAllocateGC, Xt will allow us to share GC's
 * with other widgets. We'd just have to be sure about what we want to be
 * the properties of the thing.
 */
static void
GCInitialize(XmTextWidget w)
{
    OutputData o = Text_OutputData(w);
    XGCValues values;
    XtGCMask mask, dynamic, dontcare;

    /*
     * DrawGC
     *      Need to change :        ClipRectangles
     */
    values.line_style = LineSolid;
    values.line_width = 0;
    values.fill_style = FillSolid;
    values.font = Out_Font(o)->fid;
    values.foreground = Prim_Foreground(w);
    values.background = XtBackground(w);
    values.clip_x_origin = 0;
    values.clip_y_origin = 0;
    mask = GCLineStyle | GCLineWidth | GCFillStyle | GCFont |
	GCForeground | GCBackground;
    dynamic = GCClipMask | GCClipXOrigin | GCClipYOrigin |
	GCForeground | GCBackground;
    dontcare = 0;

    Out_DrawGC(o) = XCreateGC(XtDisplay(w), XtWindow(w), mask, &values);
    Out_DrawGCInverted(o) = False;

    /*
     * Cursor GC
     *      Need to change :        Tile/Stipple Origin, Stipple
     */
    values.line_style = LineSolid;
    values.line_width = 0;
    values.fill_style = FillStippled;
    values.foreground = Prim_Foreground(w);
    values.background = XtBackground(w);
    mask = GCLineStyle | GCLineWidth | GCFillStyle |
	GCForeground | GCBackground;
    dynamic = GCTileStipXOrigin | GCTileStipYOrigin | GCStipple;

    dontcare = 0;

    Out_CursorGC(o) = XCreateGC(XtDisplay(w), XtWindow(w), mask, &values);

    /*
     * CopyGC
     *      At least this one is not changed anywhere :-)
     */
    values.line_style = LineSolid;
    values.line_width = 0;
    values.fill_style = FillSolid;
    values.foreground = XtBackground(w);
    values.background = Prim_Foreground(w);
    mask = GCLineStyle | GCLineWidth | GCFillStyle |
	GCForeground | GCBackground;

    Out_CopyGC(o) = XtGetGC((Widget)w, mask, &values);
}

static void
GCPixmap(XmTextWidget tw)
{
    OutputData o = Text_OutputData(tw);
    Widget w = (Widget)tw;
    static char dots[] = {2, 1, 1};
    GC gc;
    int width, width_erase, height;
    Display *display;
    XGCValues values;
    XtGCMask mask;

    values.line_style = LineSolid;
    values.line_width = 0;
    values.fill_style = FillSolid;
    values.foreground = 0;
    values.background = 0;

    display = XtDisplay(w);
    width = 5;
    height = Out_FontHeight(o);
    width_erase = 2 * Out_Font(o)->max_bounds.width;

    if (Out_CursorIBeam(o))
    {
	XFreePixmap(XtDisplay(w), Out_CursorIBeam(o));
    }
    Out_CursorIBeam(o) = (Pixmap)NULL;

    if (Out_CursorStipple(o))
    {
	XFreePixmap(XtDisplay(w), Out_CursorStipple(o));
    }
    Out_CursorStipple(o) = (Pixmap)NULL;

    if (Out_CursorSave(o))
    {
	XFreePixmap(XtDisplay(w), Out_CursorSave(o));
    }
    Out_CursorSave(o) = (Pixmap)NULL;

    Out_CursorSaveValid(o) = False;

    if (height > 0)
    {
	Out_CursorIBeam(o) = XCreatePixmap(display,
					   RootWindowOfScreen(XtScreen(w)),
					   width, height, 1);
	Out_CursorStipple(o) = XCreatePixmap(display,
					     RootWindowOfScreen(XtScreen(w)),
					     width, height, 1);
	Out_CursorSave(o) = XCreatePixmap(display,
					  RootWindowOfScreen(XtScreen(w)),
					  width_erase, height, w->core.depth);
	values.line_style = LineSolid;
	values.line_width = 0;
	values.fill_style = FillSolid;
	values.foreground = 0;
	values.background = 0;
	mask = GCLineStyle | GCLineWidth | GCFillStyle |
	    GCForeground | GCBackground;
	gc = XCreateGC(display, Out_CursorIBeam(o), mask, &values);

	XFillRectangle(display, Out_CursorIBeam(o), gc, 0, 0, width, height);
	XFillRectangle(display, Out_CursorStipple(o), gc, 0, 0, width, height);

	XSetForeground(display, gc, 1);

	XDrawLine(display, Out_CursorIBeam(o), gc, 2, 0, 2, height - 2);
	XDrawLine(display, Out_CursorIBeam(o), gc, 0, 0, 4, 0);
	XDrawLine(display, Out_CursorIBeam(o), gc, 0, height - 2,
		  4, height - 2);

	XSetLineAttributes(display, gc, 0, LineOnOffDash, CapRound, JoinRound);
	XSetDashes(display, gc, 0, &dots[1], (int)dots[0]);

	XDrawLine(display, Out_CursorStipple(o), gc, 2, 0, 2, height - 2);
	XDrawLine(display, Out_CursorStipple(o), gc, 1, 0, 3, 0);
	XDrawLine(display, Out_CursorStipple(o), gc, 1, height - 2,
		  3, height - 2);

	XFreeGC(display, gc);
    }
}

static void
GCClip(XmTextWidget w)
{
    OutputData o = Text_OutputData(w);
    XRectangle clip;

    clip.x = 0;
    clip.y = 0;
    clip.width = XtWidth(w) -
	2 * Prim_ShadowThickness(w) - 2 * Prim_HighlightThickness(w) -
	2 * Text_MarginWidth(w);
    clip.height = XtHeight(w) -
	2 * Prim_ShadowThickness(w) - 2 * Prim_HighlightThickness(w) -
	2 * Text_MarginHeight(w);

    /* These calls change the GCs */
    XSetClipRectangles(XtDisplay((Widget)w), Out_DrawGC(o),
	   Out_XDraw(o), Out_YDraw(o) - Out_FontAscent(o), &clip, 1, Unsorted);

    GCPixmap(w);
}

/* Take current dimensions, calculate #rows, #columns from it */
static void
SizeRecalc(XmTextWidget w)
{
    OutputData o = Text_OutputData(w);

    Out_XDraw(o) = Prim_ShadowThickness(w) + Prim_HighlightThickness(w) +
	Text_MarginWidth(w);

    /* Y offset for a row R is calculated as Out_YDraw(o) - R*fontHeight,
     * and I add FontAscent here because y positions for fonts are
     * measured from the baseline of the font. */
    Out_YDraw(o) = Prim_ShadowThickness(w) + Prim_HighlightThickness(w) +
	Text_MarginHeight(w) + Out_FontAscent(o);

    Out_Columns(o) = (XtWidth(w)
		      - 2 * Prim_ShadowThickness(w)
		      - 2 * Text_MarginWidth(w)) / Out_FontAverageWidth(o);

    if (Text_EditMode(w) == XmSINGLE_LINE_EDIT)
    {
	Out_Rows(o) = 1;
    }
    else
    {
	Out_Rows(o) = (XtHeight(w)
		       - 2 * Prim_ShadowThickness(w)
		       - 2 * Text_MarginHeight(w)) / Out_FontHeight(o);
    }

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "SizeRecalc wid %d ht %d -> rows %d cols %d\n",
		      XtWidth(w), XtHeight(w), Out_Rows(o), Out_Columns(o)));
}




/* Text Drawing functions ------------------------------------------------ */

/*
 * This thing calls "Draw" which in turn calls "DrawText".
 * Both expect their last argument to be a flag indicating which type
 * of highlighting is to be used :
 *      - XmHIGHLIGHT_NORMAL (no highlighting)
 *      - XmHIGHLIGHT_SELECTED (reversed colours)
 *      - XmHIGHLIGHT_SECONDARY_SELECTED (underlined)
 *
 * This means that this function should break up those parts of text that
 * are indicated as selected and/or highlighted.
 *
 * Highlighted stuff is recorded in an array. The array size is
 *      Text_Highlight(w).number
 * Each element in the array has a mode and a position, e.g.
 *      Text_Highlight(w).list[n].position
 */
static void
DrawAll(XmTextWidget w)
{
    int i, hi, mh;
    int highlight = XmHIGHLIGHT_NORMAL;		/* initial value */
    XmTextPosition np, first;

    OutputData o = Text_OutputData(w);

    /*
     * Without this, the area under the cursor is saved before it
     * is actuall drawn; I can't figure out how that can happen but
     * it does. Registering when you redraw that the saved image is
     * invalid helps because we no longer restore invalid images...
     */
    Out_CursorSaveValid(o) = False;

    mh = Text_Highlight(w).number;
    hi = 0;
    np = -1;
    if (mh)
    {
	if (Text_Highlight(w).list[0].position == 0)
	{
	    highlight = Text_Highlight(w).list[0].mode;
	    hi++;
	}

	if (hi < mh)
	{
	    np = Text_Highlight(w).list[hi].position;
	}
	else
	{
	    np = -1;
	}
    }

    for (i = 0; i < (Text_LineCount(w) < Out_Rows(o) ?
		     Text_LineCount(w) : Out_Rows(o)); i++)
    {
	Line line = &Text_Line(w)[i];
	Line next = &Text_Line(w)[i + 1];

#ifdef VERBOSE
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		   "DrawAll: line[%d]: start=%d end=%d, highlight change %d\n",
			  i, line->start, next->start - 1, np));
#endif

	/* Does highlighting change in this range of text ?? */
	if (np >= line->start && np <= next->start - 1)
	{			/* Yes */
	    /* Often highlight changes more than once per line */

	    /* Draw the first part */
	    first = line->start;

	    /* Check whether a change is due right now */
	    if (first == np)
	    {
		if (hi < mh)
		{
		    if (Text_Highlight(w).list[hi].position == first)
		    {
			highlight = Text_Highlight(w).list[hi].mode;
			hi++;

			if (hi < mh)
			{
			    np = Text_Highlight(w).list[hi].position;
			}
			else
			{
			    np = -1;
			}
		    }
		}
	    }

	    while (np != -1 && np <= next->start - 1)
	    {
		Draw(w, i, first, np, highlight);
		first = np;

		/* Find the next change */
		highlight = Text_Highlight(w).list[hi].mode;
		hi++;
		if (hi < mh)
		{
		    np = Text_Highlight(w).list[hi].position;
		}
		else
		{
		    np = -1;
		}
	    }

	    /* Draw the last part */
	    if (np != next->start - 1)
	    {
		Draw(w, i, first, next->start - 1, highlight);
	    }
	}
	else
	{
	    /* Sanity checks */
	    if (next->start > 0 && line->start < next->start - 1)
	    {
		Draw(w, i, line->start, next->start - 1, highlight);
	    }
	}
    }
}




/* Utilities ------------------------------------------------------------- */


#define Out_NextTabStop(o,x) \
    (Out_TabWidth(o)*((x+Out_TabWidth(o))/Out_TabWidth(o)))

/* XTextWidth clone, but taking TABS into account.
 * FIX ME: Must start from col 0 (or a tab boundary) for tab position to be
 *        accurate.
 */
int
_XmTextNextX(XmTextWidget w, int x, char *ptr, int len)
{
    OutputData o = Text_OutputData(w);
    XFontStruct *fs = Out_Font(o);

    while (len > 0)
    {
	unsigned int c;

	c = (unsigned char)*ptr;
	if (c == '\t')
	{
	    x = Out_NextTabStop(o, x);
	}
	else
	{
	    if (c < fs->min_char_or_byte2 || c > fs->max_char_or_byte2)
	    {
		c = fs->default_char;
		/* FIX ME
		 * Hack by vogt@kvi.nl */
		if (c < 32) {
			DEBUGOUT(XdbDebug(__FILE__, (Widget)w, 
				"_XmTextNextX: fs->default_char %d -> 32\n",
				c));
			c = 32;
		}
		/* end hack */
	    }

	    if (fs->per_char)
	    {
		c -= fs->min_char_or_byte2;
		x += fs->per_char[c].width;
	    }
	    else
		x += fs->max_bounds.width;
	}

	ptr++;
	len--;
    }

    return x;
}

static Dimension
FontTextWidth(XmTextWidget w, XmTextPosition pos, XmTextPosition end)
{
    XmTextBlockRec block;
    Dimension width;

    if (Text_Source(w) == NULL)
    {
	_XmWarning((Widget)w, "FontTextWidth: no source\n");

	return 1;
    }

    (*Text_Source(w)->ReadSource) (Text_Source(w), pos, end, &block);

    width = _XmTextNextX(w, 0, block.ptr, block.length);

#ifdef VERBOSE
    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "FontTextWidth(%s) => %d\n", block.ptr, width));
#endif

    XtFree(block.ptr);

    return width;
}

static XmTextPosition
XToPos(XmTextWidget w, LineNum row, int x)
{
    OutputData o = Text_OutputData(w);
    Line line = &Text_Line(w)[row];
    Line next = &Text_Line(w)[row + 1];
    XmTextPosition start, end, pos;
    XmTextBlockRec block;

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "XToPos(row %d, x %d) TotalLines %d\n",
		      row, x, Text_TotalLines(w)));

    /* Just guessing - Is this right ?? Danny */
    if (row >= Text_TotalLines(w))
    {
	next = &Text_Line(w)[Text_TotalLines(w) - 1];
	end = next->start - 1;

	if (end < 0)
	{
	    end = 0;
	}

	return end;
    }

    start = line->start;
    if (next->start == (XmTextPosition)PASTENDPOS)
    {
	end = Text_LastPos(w);	/* Avoid PASTENDPOS */
    }
#if 0
    else if (next->past_end)		/* There's nothing there ... */
    {
	end = start;			/* FIXME - this is probably wrong */
    }
#endif
    else
    {
	end = next->start - 1;
    }

    /* x is now the width from the start of the line to the pixel pos */
    x -= Out_XDraw(o) - Out_XOffset(o);

    (*Text_Source(w)->ReadSource) (Text_Source(w), start, end, &block);

    /* check if the cursor is before the 1st character */
    if (x <= 0)
    {
	pos = start;
    }
    /* OK, how 'bout after the last character */
    else if (x > _XmTextNextX(w, 0, block.ptr, block.length))
    {
	pos = end;
    }
    /* must be in between somewhere... */
    else
    {
	int tot, count, i;

	tot = 0;
	count = end - start;
	pos = -1;
	for (i = 0; i < count; i++)
	{
	    tot = _XmTextNextX(w, tot, &block.ptr[i], 1);
	    if (x < tot)
	    {
		pos = start + i;
		break;
	    }
	}

	if (pos < 0)
	{
	    pos = end;
	}
    }

    XtFree(block.ptr);

    if (XdbInDebug(__FILE__, (Widget)w))
    {
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		 "XToPos: x=%d start=%d end=%d pos=%d\n", x, start, end, pos));
    }

    return pos;
}


static LineNum
YToLineNum(XmTextWidget w, Position y)
{
    OutputData o = Text_OutputData(w);
    LineNum line, l;

    l = line = (LineNum)((y - Prim_ShadowThickness(w)
		- Prim_HighlightThickness(w) - Text_MarginHeight(w))
		/ Out_FontHeight(o));

    if (line >= Text_LineCount(w))
    {
	line = Text_LineCount(w) - 1;
    }

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		"YToLineNum: y=%d line=%d (l %d Text_LineCount %d)\n",
		y, line, l, Text_LineCount(w)));

    return line;
}

static void
DrawText(XmTextWidget w, int x, int y, char *ptr, int len,
	 XmHighlightMode highlight)
{
    OutputData o = Text_OutputData(w);
    int xoff, xstart = x, xend;

#ifdef VERBOSE
    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "DrawText: x %d y %d '%s' len %d hl %d\n",
		      x, y, (ptr == 0) ? "(NULL)" : ptr, len, highlight));
#endif

    xoff = Out_XDraw(o) - Out_XOffset(o);

    /* Only change the GC if we need to */
    if (highlight == XmHIGHLIGHT_SELECTED && !Out_DrawGCInverted(o))
    {
	XSetForeground(XtDisplay(w), Out_DrawGC(o), XtBackground(w));
	XSetBackground(XtDisplay(w), Out_DrawGC(o), Prim_Foreground(w));
	Out_DrawGCInverted(o) = True;
    }
    else if (Out_DrawGCInverted(o) && highlight != XmHIGHLIGHT_SELECTED)
    {
	XSetForeground(XtDisplay(w), Out_DrawGC(o), Prim_Foreground(w));
	XSetBackground(XtDisplay(w), Out_DrawGC(o), XtBackground(w));
	Out_DrawGCInverted(o) = False;
    }

    while (len > 0)
    {
	char *start;
	Dimension piecewidth;
	int count;

	start = ptr;
	count = 0;
	while (len > 0 && *ptr && *ptr != '\t')
	{
	    ptr++, count++, len--;
	}

	if (count > 0)
	{
	    piecewidth = _XmTextNextX(w, 0, start, count);

	    if (count > 0)
	    {
		XDrawImageString(XtDisplay(w), XtWindow(w), Out_DrawGC(o),
				 x + xoff, y, start, count);
	    }

#ifdef VERBOSE
	    if (XdbInDebug(__FILE__, (Widget)w))
	    {
		char temp[256];

		strncpy(temp, start, count);
		temp[count] = '\0';
		DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
				  "Draw: x=%d y=%d -->%s<--\n", x, y, temp));
	    }
#endif

	    x += piecewidth;
	}

	if (len > 0 && *ptr == '\t')
	{
	    x = Out_NextTabStop(o, x);

	    ptr++, len--;
	}
    }

    xend = x;

    if (highlight == XmHIGHLIGHT_SECONDARY_SELECTED)
    {
	XDrawLine(XtDisplay(w), XtWindow(w), Out_DrawGC(o),
		  xoff + xstart, y + 1, xoff + xend, y + 1);
    }
}

static void
ChangeHOffset(XmTextWidget w, int offset)
{
    OutputData o = Text_OutputData(w);

	DEBUGOUT(XdbDebug(__FILE__, (Widget)w, "ChangeHOffset(%d)\n", offset));

    if (Out_XOffset(o) == offset)
    {
	return;
    }

    Out_XOffset(o) = offset;
    Out_CursorSaveValid(o) = False;

    (*Text_Output(w)->expose) ((Widget)w, NULL, (Region)NULL);

}


void
_XmRedisplayHBar(XmTextWidget w, int offset)
{
    OutputData o = Text_OutputData(w);
    Dimension displayWidth, maxWidth;
    int oldMin, oldMax, oldSize, oldValue;
    int newValue;
    int i;

    if (Out_Hbar(o) == NULL)
    {
	return;
    }

    ChangeHOffset(w, offset);
    if (!Out_ScrollHorizontal(o))
    {
	return;
    }

    maxWidth = 0;
    for (i = 0; i < (Text_LineCount(w) < Out_Rows(o) ?
		     Text_LineCount(w) : Out_Rows(o)); i++)
    {
	if (maxWidth < Text_Line(w)[i].extra->width)
	{
	    maxWidth = Text_Line(w)[i].extra->width;
	}
    }

    displayWidth = XtWidth(w) - 2 * (Prim_HighlightThickness(w) +
				Prim_ShadowThickness(w) + Text_MarginWidth(w));

    if (maxWidth < displayWidth)
    {
	maxWidth = displayWidth;
    }

    Out_ScrollWidth(o) = maxWidth;

    XtVaGetValues(Out_Hbar(o),
		  XmNmaximum, &oldMax,
		  XmNminimum, &oldMin,
		  XmNsliderSize, &oldSize,
		  XmNvalue, &oldValue,
		  NULL);

    newValue = _XmMin(Out_XOffset(o), maxWidth - displayWidth);

    if (oldMax != maxWidth || oldMin != 0 ||
	oldSize != displayWidth || oldValue != newValue)
    {
	XtVaSetValues(Out_Hbar(o),
		      XmNmaximum, maxWidth,
		      XmNminimum, 0,
		      XmNsliderSize, displayWidth,
		      XmNvalue, newValue,
		      NULL);
    }

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
	       "_XmRedisplayHBar: oldmax=%d newmax=%d oldsize=%d newsize=%d\n",
		      oldMax, maxWidth, oldSize, displayWidth));
    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "_XmRedisplayHBar: oldvalue=%d newvalue=%d\n",
		      oldValue, newValue));

}

void
_XmChangeVSB(XmTextWidget w, XmTextPosition pos)
{
    OutputData o = Text_OutputData(w);
    int top_index, pos_index, newValue;
    int oldMin = 0, oldMax = 0, oldSize = 0, oldValue = 0, newMax;

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "_XmChangeVSB: pos=%d\n", pos));

    top_index = _XmTextGetTableIndex(w, Text_TopPos(w));
    pos_index = _XmTextGetTableIndex(w, pos);

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "_XmChangeVSB: top_index=%d pos_index=%d\n",
		      top_index, pos_index));

    if (Out_Vbar(o))
    {
	XtVaGetValues(Out_Vbar(o),
		      XmNmaximum, &oldMax,
		      XmNminimum, &oldMin,
		      XmNsliderSize, &oldSize,
		      XmNvalue, &oldValue,
		      NULL);
    }

    newMax = _XmMax(Text_TotalLines(w), Out_Rows(o));

    if (newMax == 0)
    {
	newMax = 1;		/* Avoid another warning message ?? */
    }

    /* Setting XmNminimum == XmNmaximum is not a good idea */

    newValue = pos_index > (newMax - Out_Rows(o))
	? (newMax - Out_Rows(o)) : pos_index;

    if (Out_Vbar(o) && (oldMax != newMax || oldMin != 0 ||
			oldSize != Out_Rows(o) || oldValue != newValue))
    {
	XmScrollBarCallbackStruct cbs;

	XtVaSetValues(Out_Vbar(o),
		      XmNmaximum, newMax,
		      XmNminimum, 0,
		      XmNvalue, newValue,
		      XmNsliderSize, Out_Rows(o),
		      XmNpageIncrement, Out_Rows(o),
		      NULL);

	cbs.event = NULL;
	cbs.value = newValue;
	cbs.reason = XmCR_VALUE_CHANGED;
	cbs.pixel = 0;

	XtCallCallbacks(Out_Vbar(o), XmNvalueChangedCallback, &cbs);
    }

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		   "_XmChangeVSB: oldmax=%d newmax=%d oldsize=%d newsize=%d\n",
		      oldMax, newMax, oldSize, Out_Rows(o)));

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "_XmChangeVSB: oldvalue=%d newvalue=%d\n",
		      oldValue, newValue));

}
/* Cursor functions -------------------------------------------------------- */

static void
CursorInit(XmTextWidget w)
{
    OutputData o = Text_OutputData(w);

    Out_CursorIBeam(o) = (Pixmap)NULL;
    Out_CursorStipple(o) = (Pixmap)NULL;
    Out_CursorSave(o) = (Pixmap)NULL;

    /* Out_CursorPositionVisible(o) = True; */
    Out_CursorSaveValid(o) = False;
}

static void
CursorSet(XmTextWidget w, Boolean focus)
{
    OutputData o = Text_OutputData(w);

    Out_HasFocus(o) = focus;

    /* These change the GC !! (Danny) */
    if (focus)
    {
	XSetStipple(XtDisplay((Widget)w), Out_CursorGC(o),
		    Out_CursorIBeam(o));
    }
    else
    {
	XSetStipple(XtDisplay((Widget)w), Out_CursorGC(o),
		    Out_CursorStipple(o));
    }
}

static void
CursorSaveUnderIBeam(XmTextWidget w, Position x, Position y)
{
    OutputData o = Text_OutputData(w);

    if (!XtIsRealized((Widget)w))
    {
	return;
    }

#ifdef VERBOSE
    DEBUGOUT(XdbDebug(__FILE__, (Widget)w, "CursorSaveUnderIBeam\n"));
#endif

    /* Make sure that the text is drawn before saving the cursor */
    XFlush(XtDisplay((Widget)w));

    /* save the area under the cursor */
    XFillRectangle(XtDisplay((Widget)w), Out_CursorSave(o), Out_CopyGC(o),
		   0, 0, 5, Out_FontHeight(o));

    XCopyArea(XtDisplay((Widget)w), XtWindow((Widget)w), Out_CursorSave(o),
	      Out_CursorGC(o),
	      x - 2, y,
	      5, Out_FontHeight(o), 0, 0);

    Out_CursorSaveValid(o) = True;
}

static void
CursorRestoreUnderIBeam(XmTextWidget w, Position x, Position y)
{
    OutputData o = Text_OutputData(w);

    if (!XtIsRealized((Widget)w))
    {
	return;
    }

#ifdef VERBOSE
    DEBUGOUT(XdbDebug(__FILE__, (Widget)w, "CursorRestoreUnderIBeam\n"));
#endif

    /* Make sure that the text is drawn before erasing the cursor */
    XFlush(XtDisplay((Widget)w));

    XCopyArea(XtDisplay((Widget)w), Out_CursorSave(o), XtWindow((Widget)w),
	      Out_CursorGC(o), 0, 0, 5, Out_FontHeight(o),
	      x - 2, y);

    Out_CursorSaveValid(o) = False;	/* Huh ?? FIX ME */
}

static void
CursorDrawIBeam(XmTextWidget w, int x, int y)
{
    OutputData o = Text_OutputData(w);

    /* save the area under the cursor */
    CursorSaveUnderIBeam(w, x, y);

    /* Bail if the GC's don't exist yet. */
    if (!XtIsRealized((Widget)w))
    {
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w, "CursorDrawIBeam: no GC\n"));
	return;
    }

    /* These change the GC !! (Danny) */
    XSetTSOrigin(XtDisplay((Widget)w), Out_CursorGC(o),
		 x - 2, y);

    XFillRectangle(XtDisplay((Widget)w), XtWindow((Widget)w), Out_CursorGC(o),
		   x - 2, y, 5, Out_FontHeight(o));
}

static void
CursorDraw(XmTextWidget w)
{
    OutputData o = Text_OutputData(w);

    if (Out_CursorPositionVisible(o))
    {
	CursorDrawIBeam(w, Out_CursorX(o), Out_CursorY(o));
	Out_BlinkOn(o) = True;
    }
}

static void
CursorErase(XmTextWidget w)
{
    OutputData o = Text_OutputData(w);

    if (Out_CursorSaveValid(o))
    {
	CursorRestoreUnderIBeam(w, Out_CursorX(o), Out_CursorY(o));
    }

    Out_BlinkOn(o) = False;
}

/* Callbacks --------------------------------------------------------------- */

static void
HandleTimer(XtPointer client_data, XtIntervalId *timer)
{
    XmTextWidget w = (XmTextWidget)client_data;
    OutputData o = Text_OutputData(w);
    Out_BlinkState(o) = Out_BlinkState(o) == on ? off : on;
    (*Text_Output(w)->DrawInsertionPoint) (w, Text_CursorPos(w),
					   Out_BlinkState(o));

    Out_TimerId(o) = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)w),
				     Out_BlinkRate(o), HandleTimer,
				     (XtPointer)w);
}

static void
HandleHBar(Widget sw, XtPointer client_data, XtPointer call_data)
{
    XmTextWidget w = (XmTextWidget)client_data;
    OutputData o = Text_OutputData(w);
    XmScrollBarCallbackStruct *cbs = (XmScrollBarCallbackStruct *)call_data;
    Dimension displayWidth, newOffset = 0;

    switch (cbs->reason)
    {
    case XmCR_DECREMENT:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		  "HandleHbar reason=XmCR_DECREMENT value= %d\n", cbs->value));

	newOffset = Out_XOffset(o) > Out_FontAverageWidth(o) ?
	    Out_XOffset(o) - Out_FontAverageWidth(o) : 0;
	break;

    case XmCR_DRAG:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			"HandleHbar reason=XmCR_DRAG value=%d\n", cbs->value));

	newOffset = cbs->value / Out_FontAverageWidth(o) *
	    Out_FontAverageWidth(o);
	break;

    case XmCR_INCREMENT:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		   "HandleHbar reason=XmCR_INCREMENT value=%d\n", cbs->value));

	displayWidth = XtWidth(w) - 2 * (Prim_HighlightThickness(w) +
				Prim_ShadowThickness(w) + Text_MarginWidth(w));

	if (Out_XOffset(o) < Out_ScrollWidth(o) - displayWidth)
	{
	    newOffset = Out_XOffset(o) + Out_FontAverageWidth(o);
	}
	break;

    case XmCR_PAGE_DECREMENT:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleHbar reason=XmCR_PAGE_DECREMENT value=%d\n",
			  cbs->value));

	/* FIX ME How mutch does we have to move on page_decrement ? */
	newOffset = Out_XOffset(o) > Out_FontAverageWidth(o) ?
	    Out_XOffset(o) - Out_FontAverageWidth(o) : 0;
	break;

    case XmCR_PAGE_INCREMENT:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleHbar reason=XmCR_PAGE_INCREMENT value=%d\n",
			  cbs->value));

	/* FIX ME How mutch does we have to move on page_increment ? */
	displayWidth = XtWidth(w) - 2 * (Prim_HighlightThickness(w) +
				Prim_ShadowThickness(w) + Text_MarginWidth(w));

	if (Out_XOffset(o) < Out_ScrollWidth(o) - displayWidth -
	    Out_FontAverageWidth(o))
	{
	    newOffset = Out_XOffset(o) + Out_FontAverageWidth(o);
	}

	break;

    case XmCR_TO_BOTTOM:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			"HandleHbar reason=XmCR_TO_BOTTOM value=%d pixel=%d\n",
			  cbs->value, cbs->pixel));

	newOffset = 0;
	break;

    case XmCR_TO_TOP:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleHbar reason=XmCR_TO_TOP value=%d pixel=%d\n",
			  cbs->value, cbs->pixel));

	displayWidth = XtWidth(w) - 2 * (Prim_HighlightThickness(w) +
				Prim_ShadowThickness(w) + Text_MarginWidth(w));
	newOffset = Out_ScrollWidth(o) - displayWidth;
	break;

    case XmCR_VALUE_CHANGED:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleHbar reason=XmCR_VALUE_CHANGED value=%d\n",
			  cbs->value));
	newOffset = cbs->value / Out_FontAverageWidth(o) *
	    Out_FontAverageWidth(o);
	break;

    default:
	newOffset = Out_XOffset(o);
	break;
    }

    ChangeHOffset(w, newOffset);

    XtVaSetValues(Out_Hbar(o), XmNvalue, Out_XOffset(o), NULL);
}

void
HandleVBar(Widget sw, XtPointer client_data, XtPointer call_data)
{
    XmTextWidget w = (XmTextWidget)client_data;
    OutputData o = Text_OutputData(w);
    XmScrollBarCallbackStruct *cbs = (XmScrollBarCallbackStruct *)call_data;
    Dimension pos = 0;

    switch (cbs->reason)
    {
    case XmCR_DECREMENT:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleVbar reason=XmCR_DECREMENT\n"));

	XmTextScroll((Widget)w, -1);
	pos = _XmTextGetTableIndex(w, Text_TopPos(w));
	break;

    case XmCR_DRAG:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleVbar reason=XmCR_DRAG value=%d\n",
			  cbs->value));

	pos = cbs->value;
	Text_TopPos(w) = Text_LineTable(w)[pos].start_pos;
	break;

    case XmCR_INCREMENT:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleVbar reason=XmCR_INCREMENT\n"));
	XmTextScroll((Widget)w, 1);
	pos = _XmTextGetTableIndex(w, Text_TopPos(w));
	break;

    case XmCR_PAGE_DECREMENT:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleVbar reason=XmCR_PAGE_DECREMENT\n"));

	XmTextScroll((Widget)w, -Out_Rows(o));
	pos = _XmTextGetTableIndex(w, Text_TopPos(w));
	break;

    case XmCR_PAGE_INCREMENT:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleVbar reason=XmCR_PAGE_INCREMENT\n"));

	XmTextScroll((Widget)w, Out_Rows(o));
	pos = _XmTextGetTableIndex(w, Text_TopPos(w));
	break;

    case XmCR_TO_BOTTOM:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleVbar reason=XmCR_TO_BOTTOM\n"));
	pos = 0;
	Text_TopPos(w) = Text_FirstPos(w);
	break;

    case XmCR_TO_TOP:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleVbar reason=XmCR_TO_TOP\n"));

	pos = _XmTextGetTableIndex(w, Text_TopPos(w)) - Text_LineCount(w) + 1;
	break;

    case XmCR_VALUE_CHANGED:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleVbar reason=XmVALUE_CHANGED\n"));

	pos = cbs->value;
	Text_TopPos(w) = Text_LineTable(w)[pos].start_pos;
	break;

    default:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "HandleVbar reason=UNKNOWN\n %d", cbs->reason));
	break;
    }

#if 0
    /* Peter says don't do this */
    XtVaSetValues(Out_Vbar(o), XmNvalue, pos, NULL);
#endif

    _XmRedisplayHBar(w, Out_XOffset(o));

    Text_NeedsRefigureLines(w) = True;

    (*w->core.widget_class->core_class.expose) ((Widget)w, NULL, (Region)NULL);

}


static void
HandleFocusEvents(Widget aw, XtPointer client_data, XtPointer call_data)
{
    XmAnyCallbackStruct *call_struct = (XmAnyCallbackStruct *)call_data;
    XmTextWidget w = (XmTextWidget)aw;
    OutputData o = Text_OutputData(w);

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w, "HandleFocusEvent reason=%s\n",
		      call_struct->reason == XmCR_FOCUS ? "XmCR_FOCUS" :
		      "XmCR_LOSING_FOCUS"));

    if (call_struct->reason == XmCR_FOCUS)
    {
	Out_HasFocus(o) = True;
	Out_BlinkState(o) = on;

	XSetStipple(XtDisplay(aw), Out_CursorGC(o), Out_CursorIBeam(o));

	if (Out_BlinkRate(o) > 0)
	{
	    Out_TimerId(o) = XtAppAddTimeOut(XtWidgetToApplicationContext(aw),
					     Out_BlinkRate(o), HandleTimer,
					     (XtPointer)w);
	}
    }
    else
    {
	Out_HasFocus(o) = False;

	XSetStipple(XtDisplay(aw), Out_CursorGC(o), Out_CursorStipple(o));

	if (Out_TimerId(o))
	{
	    XtRemoveTimeOut(Out_TimerId(o));
	}
    }

    (*Text_Output(w)->DrawInsertionPoint) (w, Text_CursorPos(w), on);
}



/* Output Methods --------------------------------------------------------- */

static XmTextPosition
XYToPos(XmTextWidget w, Position x, Position y)
{
    LineNum row = YToLineNum(w, y);

    return XToPos(w, row, x);
}


/*
 * Returns whether the cursor is in an area that's currently visible.
 */
static Boolean
PosToXY(XmTextWidget w, XmTextPosition pos, Position *x, Position *y)
{
    OutputData o = Text_OutputData(w);
    int i;

    *x = *y = -1;		/* On purpose ! */

    if (pos < Text_TopPos(w))
    {
	return False;
    }

    for (i = 0; i < Text_LineCount(w); i++)
    {
	Line line = &Text_Line(w)[i];
	Line next = &Text_Line(w)[i + 1];

	if (pos >= line->start && pos < next->start)
	{
	    *y = Out_YDraw(o) + i * Out_FontHeight(o);
	    *x = Out_XDraw(o) - Out_XOffset(o) +
		FontTextWidth(w, line->start, pos);
	    return True;
	}
    }

    return False;
}

static void
MakePositionVisible(XmTextWidget w, XmTextPosition pos)
{
    OutputData o = Text_OutputData(w);
    unsigned int line, newTopLine;
    int start, offset;
    Dimension xpos, last_xpos;
    Boolean UpdateVSB = False;

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "MakePositionVisible pos=%d\n", pos));

    line = _XmTextGetTableIndex(w, pos);
    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "\tline=%d, top_line=%d, line_count=%d, rows=%d redisplay=%s refigure=%s\n", 
		      line,
		      Text_TopLine(w),
		      Text_LineCount(w),
		      Out_Rows(o),
		      Text_NeedsRedisplay(w) ? "True" : "False",
		      Text_NeedsRefigureLines(w) ? "True" : "False" ));

    if (line < Text_TopLine(w))
    {
	newTopLine = line;
    }
    else if (line > Text_TopLine(w) + Out_Rows(o) - 1)
    {
	newTopLine = line - Out_Rows(o) + 1;
    }
    else
    {
	newTopLine = Text_TopLine(w);
    }

    if (newTopLine != Text_TopLine(w))
    {
	Text_TopPos(w) = Text_LineTable(w)[newTopLine].start_pos;
	Text_NeedsRedisplay(w) = True;
	Text_NeedsRefigureLines(w) = True;
	UpdateVSB = True;
    }

    start = Text_LineTable(w)[line].start_pos;
    xpos = FontTextWidth(w, start, pos);
    last_xpos = XtWidth(w) - 2 * (Prim_HighlightThickness(w) +
				  Prim_ShadowThickness(w) +
				  Text_MarginWidth(w));

    if (xpos - Out_XOffset(o) > last_xpos)
    {
	offset = xpos - last_xpos;
    }
    else if (xpos < Out_XOffset(o))
    {
	offset = xpos;
    }
    else
    {
	offset = Out_XOffset(o);
    }

    /* FIX ME should Out_ScrollHorizontal and Out_ScrollVertical be False if
     * Text is not child of scrolledwindow
     */
    if (offset != Out_XOffset(o))
    {
	if (Out_ScrollHorizontal(o) && Out_Hbar(o))
	{
	    _XmRedisplayHBar(w, offset);
	}
	else
	{
	    ChangeHOffset(w, offset);
	}
    }

    if (Out_ScrollVertical(o) && Out_Vbar(o) && UpdateVSB)
    {
	_XmChangeVSB(w, pos);
    }
}


static Boolean
MoveLines(XmTextWidget w, LineNum from, LineNum to, LineNum dest)
{
    return False;
}

static Boolean
MeasureLine(XmTextWidget w, LineNum num, XmTextPosition pos,
	    XmTextPosition *next, LineTableExtraRec **extra)
{
    OutputData o = Text_OutputData(w);
    Dimension width = 0;
    XmTextPosition start, end;
    LineTableExtra e;

#ifdef VERBOSE
    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
	  "MeasureLine: line=%d pos=%d %s %s\n",
	  num, pos, 
	  next ? "wants next" : "",
	  extra ? "wants width": ""));
#endif
    start = pos < Text_LastPos(w) ? pos : Text_LastPos(w);
    if (start < Text_LastPos(w))
    {
	end = (*Text_Source(w)->Scan) (Text_Source(w), pos,
				       XmSELECT_LINE, XmsdRight, -1, False);
    }
    else
    {
	end = Text_LastPos(w);
    }
#ifdef VERBOSE
    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
    	"\tmeasure from %d to %d\n", start, end));
#endif

    if (next)
    {
	if (end >= Text_LastPos(w))
	{
	    *next = (XmTextPosition)PASTENDPOS;
#ifdef VERBOSE
	    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		"\tnext PASTENDPOS\n"));
#endif
	}
	else
	{
	    *next = end + 1;
#ifdef VERBOSE
	    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		"\tnext %d\n", *next));
#endif
	}

	width = FontTextWidth(w, start, end);

	if (extra)
	{
	    e = (LineTableExtra)XtMalloc(sizeof(LineTableExtraRec));
	    e->width = width;
	    e->wrappedbychar = False;

	    *extra = e;
	}
    }

#if VERBOSE
    /* This is incredibly verbose; besides it seems to work. */
    if (XdbInDebug(__FILE__, (Widget)w))
    {
	if (next)
	    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			      "MeasureLine: line=%d pos=%d next=%d width=%d\n",
			      num, pos, *next, width));
	else
	    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			    "MeasureLine: line=%d pos=%d next=NULL width=%d\n",
			      num, pos, width));
    }
#endif

    if (num <= Out_Rows(o) - 1)
    {
	return True;
    }
    else
    {
	_XmRedisplayHBar(w, Out_XOffset(o));

	return False;
    }
}


/*
 * DrawInsertionPoint() gets called all the time from TextIn.c, which doesn't
 *      keep track of whether the insertion point should be visible at all.
 * We should therefore implement XmNcursorPositionVisible here.
 *
 * Between calls of DrawInsertionPoint() we should keep track of whether and
 *      where the cursor is with Out_CursorX(o) and Out_CursorY(o); set them
 *      to -1 for no cursor.
 */
static void
DrawInsertionPoint(XmTextWidget w, XmTextPosition pos, OnOrOff state)
{
    OutputData o = Text_OutputData(w);
    Position x, y;

    if (pos == PASTENDPOS)	/* No No No : don't want this */
    {
	pos = Text_LastPos(w);	/* ??? Danny 6/7/97 */
    }

#ifdef VERBOSE
    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
	      "DrawInsertionPoint Cursor : Pos %d, Save %sValid, %s Visible\n",
		      pos, Out_CursorSaveValid(o) ? "" : "not ",
		      Out_CursorPositionVisible(o) ? "" : "not"));
#endif

#if 0
    /* Experimental */
    /* Figure out what to do with the cursor here, before actually showing it. */
    if (!Out_CursorPositionVisible(o))
    {
	if (state == off)
	    CursorErase(w);
	return;
    }
#endif

    if (!XtIsRealized((Widget)w))
    {
	return;
    }

    /* erase previously visible cursor */
    if (Out_CursorX(o) >= 0 && Out_CursorY(o) >= 0)
    {
	CursorErase(w);
    }

    if (state == on)
    {
	/* draw new cursor
	 * PosToXY returns whether the position is in the area currently
	 * shown
	 */
	if ((*Text_Output(w)->PosToXY) (w, pos, &x, &y))
	{
	    Out_CursorX(o) = x;
	    Out_CursorY(o) = y - Out_FontAscent(o) + 1;

#if 0
	/*
	 * This shouldn't be necessary 'cause CursorDraw calls CursorDrawIBeam
	 * which already takes care of this.
	 */
	    if (!Out_CursorSaveValid(o))
	    {
		CursorSaveUnderIBeam(w, Out_CursorX(o), Out_CursorY(o));
	    }
#endif

#ifdef VERBOSE
	    DEBUGOUT(XdbDebug(__FILE__, (Widget)w, "Draw cursor at %d %d\n",
			      Out_CursorX(o), Out_CursorY(o)));
#endif

	    CursorDraw(w);
	}
    }

#if VERBOSE
    if (XdbInDebug(__FILE__, (Widget)w))
    {
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "DrawInsertionPoint: x=%d pos=%d state=%s\n",
			  Out_CursorX(o), pos, (state == on ? "on" : "off")));
    }
#endif
}


static void
Draw(XmTextWidget w, LineNum num, XmTextPosition start, XmTextPosition end,
     XmHighlightMode highlight)
{
    OutputData o = Text_OutputData(w);
    XmTextBlockRec block;
    Line line = &Text_Line(w)[num];

    if (end > Text_LastPos(w))
    {
	end = Text_LastPos(w);
	if (line->start > end)
	{
	    return;
	}
    }

    (*Text_Source(w)->ReadSource) (Text_Source(w), line->start, end, &block);

    if (block.length > 0)
    {
	int x, y;
	char *ptr;
	int len;

	ptr = block.ptr + start - line->start;
	len = end - start;
	if (start > line->start)
	{
	    x = _XmTextNextX(w, 0, block.ptr, start - line->start);
	}
	else
	{
	    x = 0;
	}

	y = Out_YDraw(o) + num * Out_FontHeight(o);

	DrawText(w, x, y, ptr, len, highlight);
    }

    XtFree(block.ptr);
}


static void
OutputInvalidate(XmTextWidget w, XmTextPosition start, XmTextPosition end,
		 long delta)
{
    OutputData o = Text_OutputData(w);
    Out_CursorSaveValid(o) = False;
}

static void
GetPreferredSize(Widget aw, Dimension *width, Dimension *height)
{
    XmTextWidget w = (XmTextWidget)aw;
    OutputData o = Text_OutputData(w);

    *width = (2 * Prim_ShadowThickness(w) + 2 * Prim_HighlightThickness(w) +
	      2 * Text_MarginWidth(w) +
	      Out_Columns(o) * Out_FontAverageWidth(o));

    *height = (2 * Prim_ShadowThickness(w) + 2 * Prim_HighlightThickness(w) +
	       2 * Text_MarginHeight(w) +
	       Out_Rows(o) * Out_FontHeight(o));
}

static void
OutputGetValues(Widget w, ArgList args, Cardinal num_args)
{
    XmTextInnerWidget iw = Text_InnerWidget(w);

#if VERBOSE
    /* Just print a list of resources requested */
    DEBUGOUT(XdbDebug(__FILE__, w, "OutputGetValues"));

    if (XdbInDebug(__FILE__, w) && num_args != 0)
    {
	int i;

	DEBUGOUT(XdbDebug0(__FILE__, w, "("));
	for (i = 0; i < num_args - 1; i++)
	{
	    DEBUGOUT(XdbDebug0(__FILE__, w, "%s, ", args[i].name));
	}

	DEBUGOUT(XdbDebug0(__FILE__, w, "%s)", args[num_args - 1].name));
    }
    DEBUGOUT(XdbDebug0(__FILE__, w, "\n"));
#endif

    XtGetSubvalues(iw, output_resources, XtNumber(output_resources),
		   args, num_args);
    if (XdbInDebug(__FILE__, w))
    {
	DEBUGOUT(XdbDebug(__FILE__, w, "OutputGetValues :\n"));
	XdbPrintArgList(__FILE__, w, args, num_args, True);
    }
}

/*
 * Oh boy. We're getting called directly, not through Xt. Therefore, we
 * have different old, new widgets (XmText), but they all point to the
 * same OutputData.
 *
 * So, enter the hack of the month : we'll have to take care of this. :-(
 * The trouble is all the stuff that's underneath... we'll see how far this
 * leads us. We may have to find another way to solve this.
 *
 * Note we're replacing the *old* variables by temp variables; so the functions
 * we call from here all get the real stuff and change things in the right
 * places.
 *
 * 19/5/97 : already on two levels (OutputRec and OutputDataRec).
 */
static Boolean
OutputSetValues(Widget old, Widget request, Widget new,
		ArgList args, Cardinal *num_args)
{
    OutputRec oldOutput;
    OutputDataRec oldData;
    OutputData nd = Text_OutputData(new), od = &oldData;
    XmTextInnerWidget iw = Text_InnerWidget(new);
    Boolean redraw = False;


    DEBUGOUT(XdbDebug(__FILE__, new,
		      "OutputSetValues: %i args\n"
		      "\t    old X %5i Y %5i W %5i H %5i\n"
		      "\trequest X %5i Y %5i W %5i H %5i\n"
		      "\t    new X %5i Y %5i W %5i H %5i\n",
		      *num_args,
		      XtX(old), XtY(old),
		      XtWidth(old), XtHeight(old),
		      XtX(request), XtY(request),
		      XtWidth(request), XtHeight(request),
		      XtX(new), XtY(new),
		      XtWidth(new), XtHeight(new)));
    DEBUGOUT(XdbPrintArgList(__FILE__, new, args, *num_args, False));

    /* Insert new variables */
    oldOutput = *Text_Output(old);	/* Copy old values */
    Text_Output(old) = &oldOutput;
    *od = *nd;			/* Copy old values */
    Text_OutputData(old) = od;

    /* Ready */

    /* Copy whatever we get passed into our data structures */
    XtSetSubvalues(iw, output_resources, XtNumber(output_resources),
		   args, *num_args);

    /* Check if we need to act */

    if (Out_FontList(od) != Out_FontList(nd))
    {
	DEBUGOUT(XdbDebug(__FILE__, new,
			  "OutputSetValues: FontList change.\n"));

	FontInit((XmTextWidget)new);
	/* From OutputCreate : */
	XtHeight(new) = Out_FontHeight(nd)
	    + 2 * (Prim_ShadowThickness(new) + Text_MarginHeight(new));
	XtWidth(new) = Out_Columns(nd) * Out_FontAverageWidth(nd)
	    + 2 * (Prim_ShadowThickness(new) + Text_MarginWidth(new));
	redraw = True;
    }

    if (Out_WordWrap(od) != Out_WordWrap(nd))
    {
	DEBUGOUT(XdbDebug(__FILE__, new,
			  "OutputSetValues: WordWrap change.\n"));

	/* FIX ME */
	redraw = True;
    }

    if (Out_CursorPositionVisible(od) != Out_CursorPositionVisible(nd))
    {
	DEBUGOUT(XdbDebug(__FILE__, new,
			  "OutputSetValues: CursorPosVis change.\n"));

	/* FIX ME ?? */
	DrawInsertionPoint((XmTextWidget)new, Text_CursorPos(new), off);

	redraw = True;
    }

    if (Out_Rows(od) != Out_Rows(nd))
    {
	DEBUGOUT(XdbDebug(__FILE__, new, "OutputSetValues: Rows change.\n"));

	/* FIX ME ? */
	XtHeight(new) = Out_Rows(nd) * Out_FontHeight(nd)
	    + 2 * (Prim_ShadowThickness(new) + Text_MarginHeight(new));

	redraw = True;
    }

    if (Out_Columns(od) != Out_Columns(nd))
    {
	DEBUGOUT(XdbDebug(__FILE__, new, "OutputSetValues: Columns change.\n"));

	/* FIX ME ? */
	XtWidth(new) = Out_Columns(nd) * Out_FontAverageWidth(nd)
	    + 2 * (Prim_ShadowThickness(new) + Text_MarginWidth(new));

	redraw = True;
    }

    if (Out_BlinkRate(od) != Out_BlinkRate(nd))
    {
	/* FIX ME */
    }

    if (Out_ResizeHeight(od) != Out_ResizeHeight(nd))
    {
	/* FIX ME */
    }

    if (Out_ResizeWidth(od) != Out_ResizeWidth(nd))
    {
	/* FIX ME */
    }

    if (Out_ScrollHorizontal(od) != Out_ScrollHorizontal(nd))
    {
	/* FIX ME */
    }

    if (Out_ScrollVertical(od) != Out_ScrollVertical(nd))
    {
	/* FIX ME */
    }

    if (Out_ScrollLeftSide(od) != Out_ScrollLeftSide(nd))
    {
	/* FIX ME */
    }

    if (Out_ScrollTopSide(od) != Out_ScrollTopSide(nd))
    {
	/* FIX ME */
    }

    /* Done, restore old stuff */
    Text_Output(old) = Text_Output(new);
    Text_OutputData(old) = nd;

    return redraw;
}

static void
destroy(Widget aw)
{
    XmTextWidget w = (XmTextWidget)aw;
    OutputData o = Text_OutputData(w);

    DEBUGOUT(XdbDebug(__FILE__, aw, "destroy"));

    if (o && Out_TimerId(o))
    {
	XtRemoveTimeOut(Out_TimerId(o));
    }

    XtReleaseGC((Widget)w, Out_DrawGC(o));
    XtReleaseGC((Widget)w, Out_CursorGC(o));
    XtReleaseGC((Widget)w, Out_CopyGC(o));

    if (Out_CursorIBeam(o))
    {
	XFreePixmap(XtDisplay((Widget)w), Out_CursorIBeam(o));
    }
    if (Out_CursorStipple(o))
    {
	XFreePixmap(XtDisplay((Widget)w), Out_CursorStipple(o));
    }
    if (Out_CursorSave(o))
    {
	XFreePixmap(XtDisplay((Widget)w), Out_CursorSave(o));
    }

    XtFree((char *)Text_Output(w));
}

static void
OutputExpose(Widget aw, XEvent *event, Region region)
{
    XmTextWidget w = (XmTextWidget)aw;

    DEBUGOUT(XdbDebug(__FILE__, aw, "OutputExpose: cursor pos: %d\n",
		      Text_CursorPos(w)));

    if (!XtIsRealized(aw))
    {
	return;
    }

    XClearArea(XtDisplay(w), XtWindow(w),
	       Prim_HighlightThickness(w) + Prim_ShadowThickness(w),
	       Prim_HighlightThickness(w) + Prim_ShadowThickness(w),
	       XtWidth(w) -
	       2 * (Prim_HighlightThickness(w) + Prim_ShadowThickness(w)),
	       XtHeight(w) -
	       2 * (Prim_HighlightThickness(w) + Prim_ShadowThickness(w)),
	       False);

    DrawAll(w);

    _XmDrawShadows(XtDisplay(w),
		   XtWindow(w),
		   Prim_TopShadowGC(w),
		   Prim_BottomShadowGC(w),
		   Prim_HighlightThickness(w), Prim_HighlightThickness(w),
		   XtWidth(w) - 2 * Prim_HighlightThickness(w),
		   XtHeight(w) - 2 * Prim_HighlightThickness(w),
		   Prim_ShadowThickness(w),
		   XmSHADOW_IN);

    (*Text_Output(w)->DrawInsertionPoint) (w, Text_CursorPos(w), on);
}

static void
OutputRealize(Widget aw, XtValueMask *value_mask, XSetWindowAttributes *attributes)
{
    XmTextWidget w = (XmTextWidget)aw;

    DEBUGOUT(XdbDebug(__FILE__, aw, "OutputRealize\n"));

    GCInitialize(w);
    GCClip(w);

    CursorSet(w, False);

#if 0
    CursorSaveUnderIBeam(w, 0, 0);
#endif
}

static void
resize(Widget aw, Boolean state)
{
    XmTextWidget w = (XmTextWidget)aw;
    OutputData o = Text_OutputData(w);

    SizeRecalc(w);

    /* Only adjust the GCs if they have already been created! */
    if (Out_DrawGC(o))
    {
	GCClip(w);
    }
}


OutputRec outputRec =
{
    /* _OutputDataRec            */ NULL,
    /* XYToPosProc               */ XYToPos,
    /* PosToXYProc               */ PosToXY,
    /* MeasureLineProc           */ MeasureLine,
    /* DrawProc                  */ Draw,
    /* DrawInsertionPointProc    */ DrawInsertionPoint,
    /* MakePositionVisibleProc   */ MakePositionVisible,
    /* MoveLinesProc             */ MoveLines,
    /* InvalidateProc            */ OutputInvalidate,
    /* GetPreferredSizeProc      */ GetPreferredSize,
    /* GetValuesProc             */ OutputGetValues,
    /* SetValuesProc             */ OutputSetValues,
    /* XmRealizeOutProc          */ OutputRealize,
    /* XtWidgetProc              */ destroy,
    /* XmResizeFlagProc          */ resize,
    /* XtExposeProc              */ OutputExpose
};

void
_XmTextOutputCreate(Widget aw, ArgList args, Cardinal num_args)
{
    XmTextWidget w = (XmTextWidget)aw;
    OutputData o;
    XmTextInnerWidget iw = (XmTextInnerWidget)w->text.inner_widget;
    Dimension width, height;

#if VERBOSE
    DEBUGOUT(XdbDebug(__FILE__, aw, "_XmTextOutputCreate("));
    DEBUGOUT(XdbPrintArgList(__FILE__, aw, args, num_args, False));
    DEBUGOUT(XdbDebug0(__FILE__, aw, ")\n"));
#endif

    Text_Output(w) = (Output)XtMalloc(sizeof(OutputRec));
    bcopy(&outputRec, Text_Output(w), sizeof(OutputRec));

    Text_OutputData(w) = o = &iw->inner.out;
    XtGetSubresources(aw, iw,
		      aw->core.name,
		      aw->core.widget_class->core_class.class_name,
		      output_resources,
		      XtNumber(output_resources),
		      args, num_args);

    Out_XOffset(o) = 0;

    XtAddCallback(aw, XmNfocusCallback,
		  HandleFocusEvents,
		  (XtPointer)w);
    XtAddCallback(aw, XmNlosingFocusCallback,
		  HandleFocusEvents,
		  (XtPointer)w);

    CursorInit(w);

    /* Use DrawGC as a flag to see if the GCs have been created */
    Out_DrawGC(o) = NULL;


    /* Get the font information */
    if (Out_FontList(o) == (XmFontList)XmUNSPECIFIED || !Out_FontList(o))
    {
	Out_FontList(o) = _XmGetDefaultFontList((Widget)w, XmTEXT_FONTLIST);
	Out_FontListCreated(o) = False;
	/* we didn't create this, so don't free it */
    }
    else
    {
	Out_FontListCreated(o) = True;
	/* we created this, so free it when the time comes */
    }

    FontInit(w);

    DEBUGOUT(XdbDebug(__FILE__, aw,
		      "OutputCreate (initially) : rows %d cols %d\n",
		      Out_Rows(o), Out_Columns(o)));

    /* Check window dimensions */
    if (XtWidth(w) == (Dimension)0)
    {
	if (Out_Columns(o) <= 0)
	{
	    Out_Columns(o) = 20;
	}
    }
    else
    {
	/* Reverse of this thing also copied into OutputSetValues */
	Out_Columns(o) = (XtWidth(w)
			  - 2 * Prim_ShadowThickness(w)
			  - 2 * Text_MarginWidth(w)) / Out_FontAverageWidth(o);
    }

    if (XtHeight(w) == (Dimension)0)
    {
	if (Out_Rows(o) <= 0)
	{
	    Out_Rows(o) = 10;
	}
    }
    else
    {
	Out_Rows(o) = (XtHeight(w)
		       - 2 * Prim_ShadowThickness(w)
		       - 2 * Text_MarginHeight(w)) / Out_FontHeight(o);
    }

    if (Text_EditMode(w) == XmSINGLE_LINE_EDIT)
    {
	Out_Rows(o) = 1;
	if (XtHeight(w) != 0 &&
	    (XtHeight(w) < Out_FontHeight(o)
	     + 2 * (Prim_ShadowThickness(w) + Text_MarginHeight(w))))
	{
	    /* Statement below also copied into OutputSetValues */
	    XtHeight(w) = Out_FontHeight(o)
		+ 2 * (Prim_ShadowThickness(w) + Text_MarginHeight(w));
	}
    }

    DEBUGOUT(XdbDebug(__FILE__, aw,
		      "OutputCreate: rows %d cols %d\n",
		      Out_Rows(o), Out_Columns(o)));

    if (XtWidth(w) == 0 || XtHeight(w) == 0)
    {
	(*Text_Output(w)->GetPreferredSize) (aw, &width, &height);
	XtWidth(w) = width;
	XtHeight(w) = height;

	DEBUGOUT(XdbDebug(__FILE__, aw, "GetPreferredSize -> wid %d ht %d\n",
			  width, height));
    }

    SizeRecalc(w);

    /* Create scrollbars if child of scrolledwindow */

    if (XmIsScrolledWindow(XtParent(w)))
    {
	Widget sw = XtParent(w);
	Boolean scrollingPolicy;

	XtVaGetValues(sw, XmNscrollingPolicy, &scrollingPolicy, NULL);

	if (scrollingPolicy == XmAUTOMATIC)
	{
	    Out_ScrollVertical(o) = False;
	    Out_ScrollHorizontal(o) = False;
	}

	if (Out_ScrollHorizontal(o))
	{

	    if (SW_HSB(sw) != NULL)
	    {
		Out_Hbar(o) = (Widget)SW_HSB(sw);
	    }
	    else
	    {
		Out_Hbar(o) = XtVaCreateWidget("HorScrollBar",
					       xmScrollBarWidgetClass,
					       sw,
					       XmNorientation, XmHORIZONTAL,
					       NULL);
	    }


	    XtAddCallback((Widget)Out_Hbar(o), XmNdecrementCallback,
			  HandleHBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Hbar(o), XmNdragCallback,
			  HandleHBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Hbar(o), XmNincrementCallback,
			  HandleHBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Hbar(o), XmNpageDecrementCallback,
			  HandleHBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Hbar(o), XmNpageIncrementCallback,
			  HandleHBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Hbar(o), XmNtoBottomCallback,
			  HandleHBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Hbar(o), XmNtoTopCallback,
			  HandleHBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Hbar(o), XmNvalueChangedCallback,
			  HandleHBar,
			  (XtPointer)w);
	    XtManageChild(Out_Hbar(o));
	}
	else
	{
	    Out_Hbar(o) = NULL;
	}

	if (Out_ScrollVertical(o) && Text_EditMode(w) != XmSINGLE_LINE_EDIT)
	{
	    if (SW_VSB(sw) != NULL)
	    {
		Out_Vbar(o) = (Widget)SW_VSB(sw);
	    }
	    else
	    {
		Out_Vbar(o) = XtVaCreateWidget("VertScrollBar",
					       xmScrollBarWidgetClass,
					       sw,
					       NULL);
	    }

	    XtAddCallback((Widget)Out_Vbar(o), XmNdecrementCallback,
			  HandleVBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Vbar(o), XmNdragCallback,
			  HandleVBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Vbar(o), XmNincrementCallback,
			  HandleVBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Vbar(o), XmNpageDecrementCallback,
			  HandleVBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Vbar(o), XmNpageIncrementCallback,
			  HandleVBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Vbar(o), XmNtoBottomCallback,
			  HandleVBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Vbar(o), XmNtoTopCallback,
			  HandleVBar,
			  (XtPointer)w);
	    XtAddCallback((Widget)Out_Vbar(o), XmNvalueChangedCallback,
			  HandleVBar,
			  (XtPointer)w);
	    XtManageChild(Out_Vbar(o));

	    /* Set VSB resources to initially sane values */
	    XtVaSetValues(Out_Vbar(o),
			  XmNincrement, 1,
			  XmNpageIncrement, Out_Rows(o),
			  XmNsliderSize, Out_Rows(o),
			  NULL);
	}
	else
	{
	    Out_Vbar(o) = NULL;
	}

	XmScrolledWindowSetAreas(sw, Out_Hbar(o), Out_Vbar(o), aw);

	if (Out_ScrollLeftSide(o) && Out_ScrollTopSide(o))
	{
	    XtVaSetValues(sw,
			  XmNscrollBarPlacement, XmTOP_LEFT,
			  NULL);
	}
	else if (!Out_ScrollLeftSide(o) && Out_ScrollTopSide(o))
	{
	    XtVaSetValues(sw,
			  XmNscrollBarPlacement, XmTOP_RIGHT,
			  NULL);
	}
	else if (Out_ScrollLeftSide(o) && !Out_ScrollTopSide(o))
	{
	    XtVaSetValues(sw,
			  XmNscrollBarPlacement, XmBOTTOM_LEFT,
			  NULL);
	}
	else if (!Out_ScrollLeftSide(o) && !Out_ScrollTopSide(o))
	{
	    XtVaSetValues(sw,
			  XmNscrollBarPlacement, XmBOTTOM_RIGHT,
			  NULL);
	}
    }

}



/*
 * Quasi-Public functions -----------------------------------------------------
 */


void
_XmTextDrawDestination(XmTextWidget widget)
{
}

void
_XmTextClearDestination(XmTextWidget widget, Boolean ignore_sens)
{
}

void
_XmTextDestinationVisible(Widget w, Boolean turn_on)
{
}

void
_XmTextChangeBlinkBehavior(XmTextWidget widget, Boolean newvalue)
{
}

void
_XmTextAdjustGC(XmTextWidget w)
{
}

Boolean
_XmTextShouldWordWrap(XmTextWidget w)
{
    OutputData o = Text_OutputData(w);

    if (!o->wordwrap)
    {
	return False;
    }
    if (Text_EditMode(w) == XmSINGLE_LINE_EDIT)
    {
	return False;
    }
    if (o->scrollhorizontal &&
	XtClass(XtParent(w)) == xmScrolledWindowWidgetClass)
    {
	return False;
    }
    if (o->resizewidth)
    {
	return False;
    }

    return True;
}

Boolean
_XmTextScrollable(XmTextWidget w)
{
    OutputData o = Text_OutputData(w);

    return o->scrollvertical;
}

void
_XmTextOutputGetSecResData(XmSecondaryResourceData *secResDataRtn)
{
}

int
_XmTextGetNumberLines(XmTextWidget w)
{
    return w->text.total_lines;
}

Boolean
_XmTextGetDisplayRect(Widget w, XRectangle *display_rect)
{
    return False;
}

void
_XmTextMarginsProc(Widget w, XmBaselineMargins *margins_rec)
{
}

void
_XmTextChangeHOffset(XmTextWidget widget, int length)
{
}

void
_XmTextToggleCursorGC(Widget widget)
{
}

void
_XmTextFreeContextData(Widget w, XtPointer clientData, XtPointer callData)
{
}

void
_XmTextResetClipOrigin(XmTextWidget tw, XmTextPosition position,
		       Boolean clip_mask_reset)
{
}

/*
 * Return the position of the end of this line, to implement wrapping.
 * FIX ME (currently quite simplistic)
 * FIX ME (should "extra" be filled up ?)
 */
XmTextPosition
_XmTextFindLineEnd(XmTextWidget w, XmTextPosition pos, LineTableExtra *extra)
{
    Dimension avail, width, oldwidth;
    XmTextPosition wordend, last, end;

    avail = XtWidth(w) - 2 * (Prim_ShadowThickness(w) + Text_MarginWidth(w)
			      + Prim_HighlightThickness(w));

    /* What's our line ? */
    end = (*Text_Source(w)->Scan) (Text_Source(w), pos,
				   XmSELECT_LINE, XmsdRight, -1, False);

    /* Do we need to spend time on this; the line may be short enough */
    width = FontTextWidth(w, pos, end);
    if (width < avail)
    {
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "_XmTextFindLineEnd(%d) => %d [avail %d width %d]\n",
			  pos, end, avail, width));

	return end;
    }

    /* Unfortunately, we need to investigate */
    wordend = pos;
    do
    {
	last = wordend;
	wordend = (*Text_Source(w)->Scan) (Text_Source(w), last + 1,
					   XmSELECT_WORD,
					   XmsdRight, -1, False);
	oldwidth = width;
	width = FontTextWidth(w, pos, wordend);
    }
    while (width <= avail && wordend < end);

    width = oldwidth;

    if (extra)
    {
	LineTableExtra e = (LineTableExtra)XtMalloc(sizeof(LineTableExtraRec));
	e->width = width;
	e->wrappedbychar = False;

	*extra = e;
    }

    /* Take the previous position */
    if (last < end)
    {
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "_XmTextFindLineEnd(%d) => %d [avail %d width %d]\n",
			  pos, last, avail, width));

	return last;
    }
    else
    {
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "_XmTextFindLineEnd(%d) => %d [avail %d width %d]\n",
			  pos, PASTENDPOS, avail, width));

	return PASTENDPOS;
    }
}

void
_XmTextMovingCursorPosition(XmTextWidget tw, XmTextPosition position)
{
    OutputData o = Text_OutputData(tw);
    (*Text_Output(tw)->DrawInsertionPoint) (tw, position, Out_BlinkState(o));
}

Boolean
_XmTextGetBaselines(Widget widget, Dimension **baselines, int *line_count)
{
    return False;
}
