970N/A#if (!defined(lint) && !defined(SABER))
970N/Astatic char Xrcsid[] = "$XConsortium: AsciiSink.c,v 1.49 89/12/14 19:15:55 converse Exp $";
970N/A#endif /* lint && SABER */
970N/A
1339N/A/***********************************************************
970N/ACopyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
970N/Aand the Massachusetts Institute of Technology, Cambridge, Massachusetts.
970N/A
970N/A All Rights Reserved
970N/A
970N/APermission to use, copy, modify, and distribute this software and its
970N/Adocumentation for any purpose and without fee is hereby granted,
970N/Aprovided that the above copyright notice appear in all copies and that
970N/Aboth that copyright notice and this permission notice appear in
970N/Asupporting documentation, and that the names of Digital or MIT not be
970N/Aused in advertising or publicity pertaining to distribution of the
970N/Asoftware without specific, written prior permission.
970N/A
970N/ADIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
970N/AALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
970N/ADIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
970N/AANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
970N/AWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
970N/AARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
970N/ASOFTWARE.
970N/A
970N/A******************************************************************/
970N/A
970N/A#include <stdio.h>
970N/A
970N/A#include <X11/Xlib.h>
970N/A#include <X11/Xutil.h>
970N/A#include <X11/Xatom.h>
970N/A#include <X11/IntrinsicP.h>
970N/A#include <X11/StringDefs.h>
970N/A#include <./Xaw3_1XawInit.h>
970N/A#include <./Xaw3_1AsciiSinkP.h>
970N/A#include <./Xaw3_1AsciiSrcP.h> /* For source function defs. */
970N/A#include <./Xaw3_1TextP.h> /* I also reach into the text widget. */
970N/A
970N/A#ifdef GETLASTPOS
970N/A#undef GETLASTPOS /* We will use our own GETLASTPOS. */
970N/A#endif
1105N/A
970N/A#define GETLASTPOS XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, TRUE)
970N/A
970N/Astatic void Initialize(), Destroy();
970N/Astatic Boolean SetValues();
970N/A
970N/Astatic void DisplayText(), InsertCursor(), FindPosition();
970N/Astatic void FindDistance(), Resolve(), GetCursorBounds();
970N/A
970N/A#define offset(field) XtOffset(AsciiSinkObject, ascii_sink.field)
970N/A
970N/Astatic XtResource resources[] = {
970N/A {XtNecho, XtCOutput, XtRBoolean, sizeof(Boolean),
970N/A offset(echo), XtRImmediate, (caddr_t) True},
970N/A {XtNdisplayNonprinting, XtCOutput, XtRBoolean, sizeof(Boolean),
970N/A offset(display_nonprinting), XtRImmediate, (caddr_t) True},
970N/A};
970N/A#undef offset
970N/A
970N/A#define SuperClass (&textSinkClassRec)
970N/AAsciiSinkClassRec asciiSinkClassRec = {
970N/A {
970N/A/* core_class fields */
970N/A /* superclass */ (WidgetClass) SuperClass,
970N/A /* class_name */ "AsciiSink",
970N/A /* widget_size */ sizeof(AsciiSinkRec),
1130N/A /* class_initialize */ XawInitializeWidgetSet,
970N/A /* class_part_initialize */ NULL,
970N/A /* class_inited */ FALSE,
970N/A /* initialize */ Initialize,
970N/A /* initialize_hook */ NULL,
970N/A /* obj1 */ NULL,
970N/A /* obj2 */ NULL,
970N/A /* obj3 */ 0,
970N/A /* resources */ resources,
970N/A /* num_resources */ XtNumber(resources),
970N/A /* xrm_class */ NULLQUARK,
970N/A /* obj4 */ FALSE,
970N/A /* obj5 */ FALSE,
970N/A /* obj6 */ FALSE,
970N/A /* obj7 */ FALSE,
970N/A /* destroy */ Destroy,
970N/A /* obj8 */ NULL,
970N/A /* obj9 */ NULL,
970N/A /* set_values */ SetValues,
970N/A /* set_values_hook */ NULL,
970N/A /* obj10 */ NULL,
970N/A /* get_values_hook */ NULL,
970N/A /* obj11 */ NULL,
970N/A /* version */ XtVersion,
970N/A /* callback_private */ NULL,
970N/A /* obj12 */ NULL,
970N/A /* obj13 */ NULL,
970N/A /* obj14 */ NULL,
970N/A /* extension */ NULL
1105N/A },
1105N/A/* text_sink_class fields */
970N/A {
1105N/A /* DisplayText */ DisplayText,
970N/A /* InsertCursor */ InsertCursor,
970N/A /* ClearToBackground */ XtInheritClearToBackground,
970N/A /* FindPosition */ FindPosition,
970N/A /* FindDistance */ FindDistance,
970N/A /* Resolve */ Resolve,
970N/A /* MaxLines */ XtInheritMaxLines,
1154N/A /* MaxHeight */ XtInheritMaxHeight,
1152N/A /* SetTabs */ XtInheritSetTabs,
970N/A /* GetCursorBounds */ GetCursorBounds
1172N/A },
1172N/A/* ascii_sink_class fields. */
1172N/A {
970N/A /* Keep Compiler happy. */ NULL
1105N/A }
970N/A};
970N/A
1105N/AWidgetClass asciiSinkObjectClass = (WidgetClass)&asciiSinkClassRec;
970N/A
970N/A/* Utilities */
1120N/A
1120N/Astatic int
1120N/ACharWidth (w, x, c)
970N/AWidget w;
1172N/Aint x;
1172N/Aunsigned char c;
970N/A{
1172N/A register int i, width, nonPrinting;
1172N/A AsciiSinkObject sink = (AsciiSinkObject) w;
1172N/A XFontStruct *font = sink->text_sink.font;
1153N/A Position *tab;
970N/A
970N/A if ( c == LF ) return(0);
1339N/A
1339N/A if (c == TAB) {
1339N/A /* Adjust for Left Margin. */
1339N/A x -= ((TextWidget) XtParent(w))->text.margin.left;
1339N/A
1339N/A if (x >= XtParent(w)->core.width) return 0;
1339N/A for (i = 0, tab = sink->text_sink.tabs ;
1339N/A i < sink->text_sink.tab_count ; i++, tab++) {
1339N/A if (x < *tab) {
1339N/A if (*tab < XtParent(w)->core.width)
970N/A return *tab - x;
970N/A else
1105N/A return 0;
1105N/A }
1105N/A }
970N/A return 0;
970N/A }
970N/A
1105N/A if ( (nonPrinting = (c < (unsigned char) SP)) )
970N/A if (sink->ascii_sink.display_nonprinting)
970N/A c += '@';
970N/A else {
970N/A c = SP;
970N/A nonPrinting = False;
970N/A }
970N/A
970N/A if (font->per_char &&
970N/A (c >= font->min_char_or_byte2 && c <= font->max_char_or_byte2))
970N/A width = font->per_char[c - font->min_char_or_byte2].width;
970N/A else
1189N/A width = font->min_bounds.width;
1189N/A
1189N/A if (nonPrinting)
1189N/A width += CharWidth(w, x, (unsigned char) '^');
1189N/A
1189N/A return width;
1189N/A}
1189N/A
970N/A/* Function Name: PaintText
970N/A * Description: Actually paints the text into the windoe.
970N/A * Arguments: w - the text widget.
970N/A * gc - gc to paint text with.
970N/A * x, y - location to paint the text.
1189N/A * buf, len - buffer and length of text to paint.
1189N/A * Returns: the width of the text painted, or 0.
970N/A *
970N/A * NOTE: If this string attempts to paint past the end of the window
970N/A * then this function will return zero.
1132N/A */
1132N/A
1132N/Astatic Dimension
970N/APaintText(w, gc, x, y, buf, len)
970N/AWidget w;
970N/AGC gc;
970N/APosition x, y;
970N/Aunsigned char * buf;
1276N/Aint len;
1132N/A{
970N/A AsciiSinkObject sink = (AsciiSinkObject) w;
970N/A TextWidget ctx = (TextWidget) XtParent(w);
1132N/A
1132N/A Position max_x;
1132N/A Dimension width = XTextWidth(sink->text_sink.font, (char *) buf, len);
1132N/A max_x = (Position) ctx->core.width;
1132N/A
970N/A if ( ((int) width) <= -x) /* Don't draw if we can't see it. */
1132N/A return(width);
1132N/A
1132N/A XDrawImageString(XtDisplay(ctx), XtWindow(ctx), gc,
1132N/A (int) x, (int) y, (char *) buf, len);
1132N/A if ( (((Position) width + x) > max_x) && (ctx->text.margin.right != 0) ) {
1147N/A x = ctx->core.width - ctx->text.margin.right;
1147N/A width = ctx->text.margin.right;
1147N/A XFillRectangle(XtDisplay((Widget) ctx), XtWindow( (Widget) ctx),
1147N/A sink->ascii_sink.normgc, (int) x,
1132N/A (int) y - sink->text_sink.font->ascent,
1132N/A (unsigned int) width,
1132N/A (unsigned int) (sink->text_sink.font->ascent +
1132N/A sink->text_sink.font->descent));
1132N/A return(0);
1132N/A }
1298N/A return(width);
1298N/A}
1298N/A
1298N/A/* Sink Object Functions */
1298N/A
1153N/A/*
1153N/A * This function does not know about drawing more than one line of text.
1153N/A */
1153N/A
1172N/Astatic void
970N/ADisplayText(w, x, y, pos1, pos2, highlight)
970N/AWidget w;
1003N/APosition x, y;
1003N/ABoolean highlight;
1360N/AXawTextPosition pos1, pos2;
970N/A{
970N/A AsciiSinkObject sink = (AsciiSinkObject) w;
1153N/A Widget source = XawTextGetSource(XtParent(w));
1172N/A unsigned char buf[BUFSIZ];
1172N/A
970N/A int j, k;
1172N/A XawTextBlock blk;
1154N/A GC gc = highlight ? sink->ascii_sink.invgc : sink->ascii_sink.normgc;
970N/A GC invgc = highlight ? sink->ascii_sink.normgc : sink->ascii_sink.invgc;
970N/A
970N/A if (!sink->ascii_sink.echo) return;
970N/A
970N/A y += sink->text_sink.font->ascent;
970N/A for ( j = 0 ; pos1 < pos2 ; ) {
970N/A pos1 = XawTextSourceRead(source, pos1, &blk, pos2 - pos1);
970N/A for (k = 0; k < blk.length; k++) {
1105N/A if (j >= BUFSIZ) { /* buffer full, dump the text. */
1105N/A x += PaintText(w, gc, x, y, buf, j);
970N/A j = 0;
1105N/A }
1105N/A buf[j] = blk.ptr[k];
1153N/A if (buf[j] == LF) /* line feeds ('\n') are not printed. */
1153N/A continue;
970N/A
1153N/A else if (buf[j] == '\t') {
1153N/A Position temp = 0;
1153N/A Dimension width;
1153N/A
970N/A if ((j != 0) && ((temp = PaintText(w, gc, x, y, buf, j)) == 0))
970N/A return;
1261N/A
1261N/A x += temp;
1261N/A width = CharWidth(w, x, (unsigned char) '\t');
1261N/A XFillRectangle(XtDisplayOfObject(w), XtWindowOfObject(w),
1261N/A invgc, (int) x,
970N/A (int) y - sink->text_sink.font->ascent,
1261N/A (unsigned int) width,
1261N/A (unsigned int) (sink->text_sink.font->ascent +
1152N/A sink->text_sink.font->descent));
1261N/A x += width;
1152N/A j = -1;
970N/A }
970N/A else if ( buf[j] < (unsigned char) ' ' ) {
970N/A if (sink->ascii_sink.display_nonprinting) {
970N/A buf[j + 1] = buf[j] + '@';
970N/A buf[j] = '^';
970N/A j++;
970N/A }
970N/A else
970N/A buf[j] = ' ';
970N/A }
970N/A j++;
970N/A }
970N/A }
970N/A if (j > 0)
970N/A (void) PaintText(w, gc, x, y, buf, j);
970N/A}
970N/A
1153N/A#define insertCursor_width 6
970N/A#define insertCursor_height 3
970N/Astatic char insertCursor_bits[] = {0x0c, 0x1e, 0x33};
970N/A
970N/Astatic Pixmap
970N/ACreateInsertCursor(s)
970N/AScreen *s;
1152N/A{
970N/A return (XCreateBitmapFromData (DisplayOfScreen(s), RootWindowOfScreen(s),
970N/A insertCursor_bits, insertCursor_width, insertCursor_height));
970N/A}
970N/A
970N/A/* Function Name: GetCursorBounds
1261N/A * Description: Returns the size and location of the cursor.
1261N/A * Arguments: w - the text object.
1261N/A * RETURNED rect - an X rectangle to return the cursor bounds in.
1261N/A * Returns: none.
970N/A */
970N/A
970N/Astatic void
970N/AGetCursorBounds(w, rect)
970N/AWidget w;
1046N/AXRectangle * rect;
1046N/A{
1046N/A AsciiSinkObject sink = (AsciiSinkObject) w;
1046N/A
1261N/A rect->width = (unsigned short) insertCursor_width;
1261N/A rect->height = (unsigned short) insertCursor_height;
1261N/A rect->x = sink->ascii_sink.cursor_x - (short) (rect->width / 2);
1261N/A rect->y = sink->ascii_sink.cursor_y - (short) rect->height;
1261N/A}
1261N/A
1261N/A/*
1261N/A * The following procedure manages the "insert" cursor.
1261N/A */
1261N/A
1152N/Astatic void
1152N/AInsertCursor (w, x, y, state)
1261N/AWidget w;
1152N/APosition x, y;
970N/AXawTextInsertState state;
1152N/A{
1152N/A AsciiSinkObject sink = (AsciiSinkObject) w;
970N/A Widget text_widget = XtParent(w);
970N/A XRectangle rect;
970N/A
1261N/A sink->ascii_sink.cursor_x = x;
1261N/A sink->ascii_sink.cursor_y = y;
1261N/A
970N/A GetCursorBounds(w, &rect);
970N/A if (state != sink->ascii_sink.laststate && XtIsRealized(text_widget))
970N/A XCopyPlane(XtDisplay(text_widget),
970N/A sink->ascii_sink.insertCursorOn,
1152N/A XtWindow(text_widget), sink->ascii_sink.xorgc,
1152N/A 0, 0, (unsigned int) rect.width, (unsigned int) rect.height,
1152N/A (int) rect.x, (int) rect.y, 1);
1152N/A sink->ascii_sink.laststate = state;
1152N/A}
1152N/A
1152N/A/*
1153N/A * Given two positions, find the distance between them.
1153N/A */
1153N/A
1153N/Astatic void
1153N/AFindDistance (w, fromPos, fromx, toPos, resWidth, resPos, resHeight)
1153N/AWidget w;
1152N/AXawTextPosition fromPos; /* First position. */
1153N/Aint fromx; /* Horizontal location of first position. */
1152N/AXawTextPosition toPos; /* Second position. */
1152N/Aint *resWidth; /* Distance between fromPos and resPos. */
1153N/AXawTextPosition *resPos; /* Actual second position used. */
1152N/Aint *resHeight; /* Height required. */
1152N/A{
1152N/A AsciiSinkObject sink = (AsciiSinkObject) w;
970N/A Widget source = XawTextGetSource(XtParent(w));
1153N/A
1153N/A register XawTextPosition index, lastPos;
1153N/A register unsigned char c;
1153N/A XawTextBlock blk;
1153N/A
1153N/A /* we may not need this */
1153N/A lastPos = GETLASTPOS;
1153N/A XawTextSourceRead(source, fromPos, &blk, toPos - fromPos);
1153N/A *resWidth = 0;
1130N/A for (index = fromPos; index != toPos && index < lastPos; index++) {
1130N/A if (index - blk.firstPos >= blk.length)
1130N/A XawTextSourceRead(source, index, &blk, toPos - fromPos);
1130N/A c = blk.ptr[index - blk.firstPos];
1130N/A *resWidth += CharWidth(w, fromx + *resWidth, c);
1130N/A if (c == LF) {
1130N/A index++;
1130N/A break;
1130N/A }
1130N/A }
1130N/A *resPos = index;
1130N/A *resHeight = sink->text_sink.font->ascent +sink->text_sink.font->descent;
1130N/A}
1130N/A
1130N/A
1161N/Astatic void
1130N/AFindPosition(w, fromPos, fromx, width, stopAtWordBreak,
1130N/A resPos, resWidth, resHeight)
1130N/AWidget w;
1130N/AXawTextPosition fromPos; /* Starting position. */
1130N/Aint fromx; /* Horizontal location of starting position.*/
1130N/Aint width; /* Desired width. */
1130N/Aint stopAtWordBreak; /* Whether the resulting position should be at
1172N/A a word break. */
1130N/AXawTextPosition *resPos; /* Resulting position. */
1130N/Aint *resWidth; /* Actual width used. */
1130N/Aint *resHeight; /* Height required. */
1130N/A{
1130N/A AsciiSinkObject sink = (AsciiSinkObject) w;
1161N/A Widget source = XawTextGetSource(XtParent(w));
1130N/A
970N/A XawTextPosition lastPos, index, whiteSpacePosition;
970N/A int lastWidth, whiteSpaceWidth;
970N/A Boolean whiteSpaceSeen;
1161N/A unsigned char c;
970N/A XawTextBlock blk;
970N/A
970N/A lastPos = GETLASTPOS;
970N/A
970N/A XawTextSourceRead(source, fromPos, &blk, BUFSIZ);
1139N/A *resWidth = 0;
1139N/A whiteSpaceSeen = FALSE;
970N/A c = 0;
970N/A for (index = fromPos; *resWidth <= width && index < lastPos; index++) {
970N/A lastWidth = *resWidth;
970N/A if (index - blk.firstPos >= blk.length)
970N/A XawTextSourceRead(source, index, &blk, BUFSIZ);
1339N/A c = blk.ptr[index - blk.firstPos];
1339N/A *resWidth += CharWidth(w, fromx + *resWidth, c);
1130N/A
970N/A if ((c == SP || c == TAB) && *resWidth <= width) {
1130N/A whiteSpaceSeen = TRUE;
970N/A whiteSpacePosition = index;
1339N/A whiteSpaceWidth = *resWidth;
970N/A }
970N/A if (c == LF) {
1139N/A index++;
1339N/A break;
1132N/A }
970N/A }
970N/A if (*resWidth > width && index > fromPos) {
970N/A *resWidth = lastWidth;
970N/A index--;
1339N/A if (stopAtWordBreak && whiteSpaceSeen) {
1339N/A index = whiteSpacePosition + 1;
970N/A *resWidth = whiteSpaceWidth;
970N/A }
970N/A }
970N/A if (index == lastPos && c != LF) index = lastPos + 1;
970N/A *resPos = index;
970N/A *resHeight = sink->text_sink.font->ascent +sink->text_sink.font->descent;
970N/A}
1139N/A
970N/Astatic void
1130N/AResolve (w, pos, fromx, width, leftPos, rightPos)
1130N/AWidget w;
1130N/AXawTextPosition pos;
1130N/Aint fromx, width;
1130N/AXawTextPosition *leftPos, *rightPos;
1130N/A{
1130N/A int resWidth, resHeight;
1130N/A Widget source = XawTextGetSource(XtParent(w));
1130N/A
1130N/A FindPosition(w, pos, fromx, width, FALSE, leftPos, &resWidth, &resHeight);
1130N/A if (*leftPos > GETLASTPOS)
1130N/A *leftPos = GETLASTPOS;
1130N/A *rightPos = *leftPos;
1130N/A}
1139N/A
1139N/Astatic void
1130N/AGetGC(sink)
1130N/AAsciiSinkObject sink;
1130N/A{
970N/A XtGCMask valuemask = (GCFont |
970N/A GCGraphicsExposures | GCForeground | GCBackground );
970N/A XGCValues values;
970N/A
970N/A values.font = sink->text_sink.font->fid;
970N/A values.graphics_exposures = (Bool) FALSE;
970N/A
970N/A values.foreground = sink->text_sink.foreground;
970N/A values.background = sink->text_sink.background;
970N/A sink->ascii_sink.normgc = XtGetGC((Widget)sink, valuemask, &values);
970N/A
970N/A values.foreground = sink->text_sink.background;
970N/A values.background = sink->text_sink.foreground;
970N/A sink->ascii_sink.invgc = XtGetGC((Widget)sink, valuemask, &values);
970N/A
970N/A values.function = GXxor;
970N/A values.background = (unsigned long) 0L; /* (pix ^ 0) = pix */
970N/A values.foreground = (sink->text_sink.background ^
970N/A sink->text_sink.foreground);
970N/A valuemask = GCFunction | GCForeground | GCBackground;
970N/A
970N/A sink->ascii_sink.xorgc = XtGetGC((Widget)sink, valuemask, &values);
970N/A}
1172N/A
970N/A
970N/A/***** Public routines *****/
970N/A
970N/A/* Function Name: Initialize
970N/A * Description: Initializes the TextSink Object.
970N/A * Arguments: request, new - the requested and new values for the object
1207N/A * instance.
1207N/A * Returns: none.
1207N/A *
1207N/A */
1207N/A
1207N/A/* ARGSUSED */
970N/Astatic void
970N/AInitialize(request, new)
970N/AWidget request, new;
970N/A{
970N/A AsciiSinkObject sink = (AsciiSinkObject) new;
970N/A
970N/A GetGC(sink);
sink->ascii_sink.insertCursorOn= CreateInsertCursor(XtScreenOfObject(new));
sink->ascii_sink.laststate = XawisOff;
sink->ascii_sink.cursor_x = sink->ascii_sink.cursor_y = 0;
}
/* Function Name: Destroy
* Description: This function cleans up when the object is
* destroyed.
* Arguments: w - the AsciiSink Object.
* Returns: none.
*/
static void
Destroy(w)
Widget w;
{
AsciiSinkObject sink = (AsciiSinkObject) w;
XtReleaseGC(w, sink->ascii_sink.normgc);
XtReleaseGC(w, sink->ascii_sink.invgc);
XtReleaseGC(w, sink->ascii_sink.xorgc);
XFreePixmap(XtDisplayOfObject(w), sink->ascii_sink.insertCursorOn);
}
/* Function Name: SetValues
* Description: Sets the values for the AsciiSink
* Arguments: current - current state of the object.
* request - what was requested.
* new - what the object will become.
* Returns: True if redisplay is needed.
*/
/* ARGSUSED */
static Boolean
SetValues(current, request, new)
Widget current, request, new;
{
AsciiSinkObject w = (AsciiSinkObject) new;
AsciiSinkObject old_w = (AsciiSinkObject) current;
if (w->text_sink.font != old_w->text_sink.font) {
XtReleaseGC((Widget)w, w->ascii_sink.normgc);
XtReleaseGC((Widget)w, w->ascii_sink.invgc);
GetGC(w);
((TextWidget)XtParent(new))->text.redisplay_needed = True;
} else {
if ( (w->ascii_sink.echo != old_w->ascii_sink.echo) ||
(w->ascii_sink.display_nonprinting !=
old_w->ascii_sink.display_nonprinting) )
((TextWidget)XtParent(new))->text.redisplay_needed = True;
}
return False;
}