Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright 1991 by OMRON Corporation
      3  *
      4  * Permission to use, copy, modify, distribute, and sell this software and its
      5  * documentation for any purpose is hereby granted without fee, provided that
      6  * the above copyright notice appear in all copies and that both that
      7  * copyright notice and this permission notice appear in supporting
      8  * documentation, and that the name of OMRON not be used in advertising
      9  * or publicity pertaining to distribution of the software without specific,
     10  * written prior permission.  OMRON makes no representations about the
     11  * suitability of this software for any purpose.  It is provided "as is"
     12  * without express or implied warranty.
     13  *
     14  * OMRON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     16  * EVENT SHALL OMRON BE LIABLE FOR ANY SPECIAL, INDIRECT OR
     17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
     18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
     19  * TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
     20  * PERFORMANCE OF THIS SOFTWARE.
     21  *
     22  *      Author: Li Yuhong	 OMRON Corporation
     23  */
     24 
     25 /***********************************************************
     26 
     27 Copyright 1987, 1988, 1994, 1998  The Open Group
     28 
     29 Permission to use, copy, modify, distribute, and sell this software and its
     30 documentation for any purpose is hereby granted without fee, provided that
     31 the above copyright notice appear in all copies and that both that
     32 copyright notice and this permission notice appear in supporting
     33 documentation.
     34 
     35 The above copyright notice and this permission notice shall be included in
     36 all copies or substantial portions of the Software.
     37 
     38 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     39 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     40 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     41 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     42 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     43 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     44 
     45 Except as contained in this notice, the name of The Open Group shall not be
     46 used in advertising or otherwise to promote the sale, use or other dealings
     47 in this Software without prior written authorization from The Open Group.
     48 
     49 
     50 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
     51 
     52                         All Rights Reserved
     53 
     54 Permission to use, copy, modify, and distribute this software and its
     55 documentation for any purpose and without fee is hereby granted,
     56 provided that the above copyright notice appear in all copies and that
     57 both that copyright notice and this permission notice appear in
     58 supporting documentation, and that the name of Digital not be
     59 used in advertising or publicity pertaining to distribution of the
     60 software without specific, written prior permission.
     61 
     62 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     63 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
     64 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     65 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
     66 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
     67 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     68 SOFTWARE.
     69 
     70 ******************************************************************/
     71 
     72 #ifdef HAVE_CONFIG_H
     73 #include <config.h>
     74 #endif
     75 #include <X11/IntrinsicP.h>
     76 #include <X11/StringDefs.h>
     77 #include <X11/Xatom.h>
     78 #include <X11/Xaw/XawInit.h>
     79 #include <X11/Xaw/MultiSinkP.h>
     80 #include <X11/Xaw/MultiSrcP.h>
     81 #include <X11/Xaw/TextP.h>
     82 #include "XawI18n.h"
     83 #include <stdio.h>
     84 #include <ctype.h>
     85 #include "Private.h"
     86 
     87 #ifdef GETLASTPOS
     88 #undef GETLASTPOS		/* We will use our own GETLASTPOS */
     89 #endif
     90 
     91 #define GETLASTPOS	\
     92 	XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True)
     93 
     94 /*
     95  * Class Methods
     96  */
     97 static void XawMultiSinkClassInitialize(void);
     98 static void XawMultiSinkInitialize(Widget, Widget, ArgList, Cardinal*);
     99 static void XawMultiSinkDestroy(Widget);
    100 static void XawMultiSinkResize(Widget);
    101 static Boolean XawMultiSinkSetValues(Widget, Widget, Widget,
    102 				     ArgList, Cardinal*);
    103 static int MaxLines(Widget, unsigned int);
    104 static int MaxHeight(Widget, int);
    105 static void SetTabs(Widget, int, short*);
    106 static void DisplayText(Widget, int, int,
    107 			XawTextPosition, XawTextPosition, Bool);
    108 static void InsertCursor(Widget, int, int, XawTextInsertState);
    109 static void FindPosition(Widget, XawTextPosition, int, int, Bool,
    110 			 XawTextPosition*, int*, int*);
    111 static void FindDistance(Widget, XawTextPosition, int, XawTextPosition, int*,
    112 			 XawTextPosition*, int*);
    113 static void Resolve(Widget, XawTextPosition, int, int, XawTextPosition*);
    114 static void GetCursorBounds(Widget, XRectangle*);
    115 
    116 /*
    117  * Prototypes
    118  */
    119 static void GetGC(MultiSinkObject);
    120 static int CharWidth(MultiSinkObject, XFontSet, int, wchar_t);
    121 static unsigned int PaintText(Widget w, GC gc, int x, int y,
    122 			      wchar_t *buf, int len, Bool);
    123 
    124 /*
    125  * Defined in TextSink.c
    126  */
    127 void _XawTextSinkClearToBackground(Widget, int, int, unsigned, unsigned);
    128 
    129 /*
    130  * Initialization
    131  */
    132 static wchar_t wspace[2];
    133 
    134 #define offset(field) XtOffsetOf(MultiSinkRec, multi_sink.field)
    135 static XtResource resources[] = {
    136   {
    137     XtNfontSet,
    138     XtCFontSet,
    139     XtRFontSet,
    140     sizeof(XFontSet),
    141     offset(fontset),
    142     XtRString,
    143     (XtPointer)XtDefaultFontSet
    144   },
    145   {
    146     XtNecho,
    147     XtCOutput,
    148     XtRBoolean,
    149     sizeof(Boolean),
    150     offset(echo),
    151     XtRImmediate,
    152     (XtPointer)True
    153   },
    154   {
    155     XtNdisplayNonprinting,
    156     XtCOutput,
    157     XtRBoolean,
    158     sizeof(Boolean),
    159     offset(display_nonprinting),
    160     XtRImmediate,
    161     (XtPointer)True
    162   },
    163 };
    164 #undef offset
    165 
    166 #define SuperClass		(&textSinkClassRec)
    167 MultiSinkClassRec multiSinkClassRec = {
    168   /* object */
    169   {
    170     (WidgetClass)SuperClass,		/* superclass */
    171     "MultiSink",			/* class_name */
    172     sizeof(MultiSinkRec),		/* widget_size */
    173     XawMultiSinkClassInitialize,	/* class_initialize */
    174     NULL,				/* class_part_initialize */
    175     False,				/* class_inited */
    176     XawMultiSinkInitialize,		/* initialize */
    177     NULL,				/* initialize_hook */
    178     NULL,				/* obj1 */
    179     NULL,				/* obj2 */
    180     0,					/* obj3 */
    181     resources,				/* resources */
    182     XtNumber(resources),		/* num_resources */
    183     NULLQUARK,				/* xrm_class */
    184     False,				/* obj4 */
    185     False,				/* obj5 */
    186     False,				/* obj6 */
    187     False,				/* obj7 */
    188     XawMultiSinkDestroy,		/* destroy */
    189     (XtProc)XawMultiSinkResize,		/* obj8 */
    190     NULL,				/* obj9 */
    191     XawMultiSinkSetValues,		/* set_values */
    192     NULL,				/* set_values_hook */
    193     NULL,				/* obj10 */
    194     NULL,				/* get_values_hook */
    195     NULL,				/* obj11 */
    196     XtVersion,				/* version */
    197     NULL,				/* callback_private */
    198     NULL,				/* obj12 */
    199     NULL,				/* obj13 */
    200     NULL,				/* obj14 */
    201     NULL,				/* extension */
    202   },
    203     /* text_sink */
    204   {
    205     DisplayText,			/* DisplayText */
    206     InsertCursor,			/* InsertCursor */
    207     XtInheritClearToBackground,		/* ClearToBackground */
    208     FindPosition,			/* FindPosition */
    209     FindDistance,			/* FindDistance */
    210     Resolve,				/* Resolve */
    211     MaxLines,				/* MaxLines */
    212     MaxHeight,				/* MaxHeight */
    213     SetTabs,				/* SetTabs */
    214     GetCursorBounds,			/* GetCursorBounds */
    215   },
    216   /* multi_sink */
    217   {
    218     NULL,				/* extension */
    219   }
    220 };
    221 
    222 WidgetClass multiSinkObjectClass = (WidgetClass)&multiSinkClassRec;
    223 
    224 /*
    225  * Implementation
    226  */
    227 static int
    228 CharWidth(MultiSinkObject sink, XFontSet fontset, int x, wchar_t c)
    229 {
    230     int width = 0;
    231 
    232     if (c == _Xaw_atowc(XawLF))
    233 	return (0);
    234 
    235     if (c == _Xaw_atowc(XawTAB)) {
    236 	int i;
    237 	Position *tab;
    238 
    239 	width = x;
    240 	/* Adjust for Left Margin. */
    241 	x -= ((TextWidget)XtParent((Widget)sink))->text.left_margin;
    242 
    243 	i = 0;
    244 	tab = sink->text_sink.tabs;
    245 	/*CONSTCOND*/
    246 	while (1) {
    247 	    if (x < *tab)
    248 		return (*tab - x);
    249 	    /* Start again */
    250 	    if (++i >= sink->text_sink.tab_count) {
    251 		x -= *tab;
    252 		i = 0;
    253 		tab = sink->text_sink.tabs;
    254 		if (width == x)
    255 		    return (0);
    256 	    }
    257 	    else
    258 		++tab;
    259 	}
    260 	/*NOTREACHED*/
    261     }
    262 
    263     if (XwcTextEscapement(fontset, &c, 1) == 0) {
    264 	if (sink->multi_sink.display_nonprinting)
    265 	    c = _Xaw_atowc('@');
    266 	else
    267 	    c = _Xaw_atowc(XawSP);
    268     }
    269 
    270       /*
    271        * if more efficiency(suppose one column is one ASCII char)
    272 
    273       width = XwcGetColumn(fontset->font_charset, fontset->num_of_fonts, c) *
    274 	      fontset->font_struct_list[0]->min_bounds.width;
    275        *
    276        * WARNING: Very Slower!!!
    277        *
    278        * Li Yuhong.
    279        */
    280 
    281     width = XwcTextEscapement(fontset, &c, 1);
    282 
    283     return (width);
    284 }
    285 
    286 /*
    287  * Function:
    288  *	PaintText
    289  *
    290  * Parameters:
    291  *	w   - text sink object
    292  *	gc  - gc to paint text
    293  *	x   - location to paint the text
    294  *	y   - ""
    295  *	buf - buffer and length of text to paint
    296  *	len - ""
    297  *	clear_bg - clear background before drawing ?
    298  *
    299  * Description:
    300  *	Actually paints the text into the window.
    301  *
    302  * Returns:
    303  *	The width of the text painted
    304  */
    305 static unsigned int
    306 PaintText(Widget w, GC gc, int x, int y, wchar_t *buf, int len, Bool clear_bg)
    307 {
    308     MultiSinkObject sink = (MultiSinkObject)w;
    309     TextWidget ctx = (TextWidget)XtParent(w);
    310     XFontSet fontset = sink->multi_sink.fontset;
    311     unsigned int width = (unsigned)XwcTextEscapement(fontset, buf, len);
    312 
    313     if (((int)width) <= -x)		/* Don't draw if we can't see it */
    314 	return (width);
    315 
    316     if (clear_bg) {
    317 	XFontSetExtents *ext = XExtentsOfFontSet(fontset);
    318 
    319 	_XawTextSinkClearToBackground(w, x, y - abs(ext->max_logical_extent.y),
    320 				      width, ext->max_logical_extent.height);
    321 	XwcDrawString(XtDisplay(ctx), XtWindow(ctx), fontset, gc, x, y, buf, len);
    322     }
    323     else
    324 	XwcDrawImageString(XtDisplay(ctx), XtWindow(ctx), fontset, gc,
    325 			   x, y, buf, len);
    326 
    327     return (width);
    328 }
    329 
    330 /* Sink Object Functions */
    331 /*
    332  * This function does not know about drawing more than one line of text
    333  */
    334 static void
    335 DisplayText(Widget w, int x, int y,
    336 	    XawTextPosition pos1, XawTextPosition pos2, Bool highlight)
    337 {
    338     TextWidget ctx = (TextWidget)XtParent(w);
    339     MultiSinkObject sink = (MultiSinkObject)w;
    340     XFontSet fontset = sink->multi_sink.fontset;
    341     Widget source = XawTextGetSource(XtParent(w));
    342     wchar_t buf[256];
    343     XFontSetExtents *ext = XExtentsOfFontSet(fontset);
    344     int j, k;
    345     XawTextBlock blk;
    346     GC gc, invgc, tabgc;
    347     int max_x;
    348     Bool clear_bg;
    349 
    350     if (!sink->multi_sink.echo || !ctx->text.lt.lines)
    351 	return;
    352 
    353     max_x = (int)XtWidth(ctx) - ctx->text.r_margin.right;
    354     clear_bg = !highlight && ctx->core.background_pixmap != XtUnspecifiedPixmap;
    355 
    356     gc = highlight ? sink->multi_sink.invgc : sink->multi_sink.normgc;
    357     invgc = highlight ? sink->multi_sink.normgc : sink->multi_sink.invgc;
    358 
    359     if (highlight && sink->multi_sink.xorgc)
    360 	tabgc = sink->multi_sink.xorgc;
    361     else
    362 	tabgc = invgc;
    363 
    364     y += abs(ext->max_logical_extent.y);
    365     for (j = 0; pos1 < pos2;) {
    366 	pos1 = XawTextSourceRead(source, pos1, &blk, (int) (pos2 - pos1));
    367 	for (k = 0; k < blk.length; k++) {
    368 	    if ((unsigned) j >= (sizeof(buf) / sizeof(wchar_t)) - 1) {
    369 		/* buffer full, dump the text */
    370 		if ((x = (int)((unsigned)x + PaintText(w, gc, x, y, buf, j, clear_bg))) >= max_x)
    371 		    return;
    372 		j = 0;
    373 	    }
    374 	    buf[j] = ((wchar_t *)blk.ptr)[k];
    375 	    if (buf[j] == _Xaw_atowc(XawLF))
    376 		continue;
    377 
    378 	    else if (buf[j] == _Xaw_atowc(XawTAB)) {
    379 		unsigned int width;
    380 
    381 		if (j != 0 &&
    382 		    (x = (int)((unsigned)x + PaintText(w, gc, x, y, buf, j, clear_bg))) >= max_x)
    383 		    return;
    384 
    385 		width = (unsigned)CharWidth(sink, fontset, x, _Xaw_atowc(XawTAB));
    386 		if (clear_bg)
    387 		    _XawTextSinkClearToBackground(w,
    388 					x, y - abs(ext->max_logical_extent.y),
    389 					width, ext->max_logical_extent.height);
    390 		else
    391 		    XFillRectangle(XtDisplayOfObject(w), XtWindowOfObject(w),
    392 				   tabgc, x,
    393 				   y - abs(ext->max_logical_extent.y),
    394 				   width,
    395 				   ext->max_logical_extent.height);
    396 		x = (int)((unsigned)x + width);
    397 		j = -1;
    398 	    }
    399 	    else if (XwcTextEscapement(sink->multi_sink.fontset, &buf[j], 1)
    400 		     == 0) {
    401 		if (sink->multi_sink.display_nonprinting)
    402 		    buf[j] = _Xaw_atowc('@');
    403 		else
    404 		    buf[j] = _Xaw_atowc(XawSP);
    405 	    }
    406 	    j++;
    407 	}
    408     }
    409 
    410     if (j > 0)
    411 	(void)PaintText(w, gc, x, y, buf, j, clear_bg);
    412 }
    413 
    414 /*
    415  * Function:
    416  *	GetCursorBounds
    417  *
    418  * Parameters:
    419  *	w    - text sink object
    420  *	rect - X rectangle to return the cursor bounds
    421  *
    422  * Description:
    423  *	Returns the size and location of the cursor.
    424  */
    425 static void
    426 GetCursorBounds(Widget w, XRectangle *rect)
    427 {
    428     MultiSinkObject sink = (MultiSinkObject)w;
    429 
    430     rect->width = (unsigned short)CharWidth(sink, sink->multi_sink.fontset, 0, _Xaw_atowc(XawSP));
    431     rect->height = (XExtentsOfFontSet(sink->multi_sink.fontset)
    432 		    ->max_logical_extent.height);
    433     rect->x = sink->multi_sink.cursor_x;
    434     rect->y = (short)(sink->multi_sink.cursor_y - (short)rect->height);
    435 }
    436 
    437 /*
    438  * The following procedure manages the "insert" cursor
    439  */
    440 static void
    441 InsertCursor(Widget w, int x, int y, XawTextInsertState state)
    442 {
    443     MultiSinkObject sink = (MultiSinkObject)w;
    444     XFontSet fontset = sink->multi_sink.fontset;
    445     Widget ctx = XtParent(w);
    446     XawTextPosition position = XawTextGetInsertionPoint(ctx);
    447 
    448     if (XtIsRealized(ctx)) {
    449 	int fheight, fdiff;
    450 	XawTextBlock block;
    451 	wchar_t c;
    452 	XawTextPosition selection_start, selection_end;
    453 	Boolean has_selection;
    454 	XFontSetExtents *ext = XExtentsOfFontSet(fontset);
    455 
    456 	XawTextGetSelectionPos((Widget)ctx, &selection_start, &selection_end);
    457 	has_selection = selection_start != selection_end;
    458 
    459 	fheight = ext->max_logical_extent.height;
    460 	fdiff = fheight - abs(ext->max_logical_extent.y);
    461 
    462 	if ((sink->multi_sink.cursor_position != position || state == XawisOff)
    463 	    && !has_selection && sink->multi_sink.laststate != XawisOff) {
    464 	    wchar_t *ochar;
    465 
    466 	    (void)XawTextSourceRead(XawTextGetSource(ctx),
    467 				    sink->multi_sink.cursor_position,
    468 				    &block, 1);
    469 	    if (!block.length)
    470 		ochar = NULL;
    471 	    else {
    472 		c = ((wchar_t *)block.ptr)[0];
    473 		if (c == _Xaw_atowc(XawLF))
    474 		    ochar = NULL;
    475 		else if (c == _Xaw_atowc(XawTAB))
    476 		    ochar = wspace;
    477 		else
    478 		    ochar = (wchar_t *)block.ptr;
    479 	    }
    480 
    481 	    if (!ochar)
    482 		_XawTextSinkClearToBackground(w, sink->multi_sink.cursor_x,
    483 					      (sink->multi_sink.cursor_y - 1 -
    484 					      fheight),
    485 					      (unsigned)CharWidth(sink, fontset, 0, wspace[0]),
    486 					      (unsigned)fheight);
    487 	    else {
    488 		if (XwcTextEscapement(sink->multi_sink.fontset, ochar, 1) != 0)
    489 		    DisplayText(w, sink->multi_sink.cursor_x,
    490 				sink->multi_sink.cursor_y - 1 - fheight,
    491 				sink->multi_sink.cursor_position,
    492 				sink->multi_sink.cursor_position + 1,
    493 				False);
    494 		else
    495 		    PaintText(w, sink->multi_sink.normgc,
    496 			      sink->multi_sink.cursor_x,
    497 			      sink->multi_sink.cursor_y - 1 - fdiff,
    498 			      ochar, 1,
    499 			      ctx->core.background_pixmap != XtUnspecifiedPixmap);
    500 	    }
    501 	}
    502 
    503 	if (!has_selection && state != XawisOff) {
    504 	    wchar_t *nchar;
    505 	    Boolean focus = ((TextWidget)ctx)->text.hasfocus;
    506 
    507 	    (void)XawTextSourceRead(XawTextGetSource(ctx),
    508 				    position, &block, 1);
    509 	    c = ((wchar_t *)block.ptr)[0];
    510 	    if (!block.length || c == _Xaw_atowc(XawLF)
    511 		|| c == _Xaw_atowc(XawTAB))
    512 		nchar = wspace;
    513 	    else
    514 		nchar = (wchar_t *)block.ptr;
    515 
    516 	    if (focus) {
    517 		if (XwcTextEscapement(sink->multi_sink.fontset, nchar, 1) != 0)
    518 		    XwcDrawImageString(XtDisplay(ctx), XtWindow(ctx),
    519 				       fontset, sink->multi_sink.invgc,
    520 				       x, (y - 1 - fdiff), nchar, 1);
    521 		else
    522 		    DisplayText(w, x, y - 1 - fheight,
    523 				position, position + 1, True);
    524 	    }
    525 	    else
    526 		XDrawRectangle(XtDisplay(ctx), XtWindow(ctx),
    527 			       sink->multi_sink.xorgc ?
    528 			       sink->multi_sink.xorgc : sink->multi_sink.normgc,
    529 			       x, y - 1 - fheight,
    530 			       (unsigned)(CharWidth(sink, fontset, 0, *nchar) - 1),
    531 			       (unsigned)(fheight - 1));
    532 	  }
    533       }
    534 
    535     sink->multi_sink.cursor_x = (short)x;
    536     sink->multi_sink.cursor_y = (short)y;
    537     sink->multi_sink.laststate = state;
    538     sink->multi_sink.cursor_position = position;
    539 }
    540 
    541 /*
    542  * Given two positions, find the distance between them
    543  */
    544 static void
    545 FindDistance(Widget w, XawTextPosition fromPos, int fromx,
    546 	     XawTextPosition toPos, int *resWidth,
    547 	     XawTextPosition *resPos, int *resHeight)
    548 {
    549     MultiSinkObject sink = (MultiSinkObject)w;
    550     XFontSet fontset = sink->multi_sink.fontset;
    551     TextWidget ctx = (TextWidget)XtParent(w);
    552     Widget source = ctx->text.source;
    553     XawTextPosition idx, pos;
    554     XFontSetExtents *ext = XExtentsOfFontSet(fontset);
    555     XawTextBlock blk;
    556     int i, rWidth;
    557 
    558     pos = XawTextSourceRead(source, fromPos, &blk, (int)(toPos - fromPos));
    559     rWidth = 0;
    560     for (i = 0, idx = fromPos; idx < toPos; i++, idx++) {
    561 	wchar_t c;
    562 
    563 	if (i >= blk.length) {
    564 	    i = 0;
    565 	    XawTextSourceRead(source, pos, &blk, (int)(toPos - pos));
    566 	    if (blk.length == 0)
    567 		break;
    568 	}
    569 	c = ((wchar_t *)blk.ptr)[i];
    570 	rWidth += CharWidth(sink, fontset, fromx + rWidth, c);
    571 	if (c == _Xaw_atowc(XawLF)) {
    572 	    idx++;
    573 	    break;
    574 	}
    575     }
    576 
    577     *resPos = idx;
    578     *resWidth = rWidth;
    579     *resHeight = ext->max_logical_extent.height;
    580 }
    581 
    582 static void
    583 FindPosition(Widget w, XawTextPosition fromPos, int fromx, int width,
    584 	     Bool stopAtWordBreak, XawTextPosition *resPos, int *resWidth,
    585 	     int *resHeight)
    586 {
    587     MultiSinkObject sink = (MultiSinkObject)w;
    588     TextWidget ctx = (TextWidget)XtParent(w);
    589     Widget source = ctx->text.source;
    590     XFontSet fontset = sink->multi_sink.fontset;
    591     XawTextPosition idx, pos, whiteSpacePosition = 0;
    592     int i, lastWidth, whiteSpaceWidth, rWidth;
    593     Boolean whiteSpaceSeen;
    594     wchar_t c;
    595     XFontSetExtents *ext = XExtentsOfFontSet(fontset);
    596     XawTextBlock blk;
    597 
    598     pos = XawTextSourceRead(source, fromPos, &blk, BUFSIZ);
    599     rWidth = lastWidth = whiteSpaceWidth = 0;
    600     whiteSpaceSeen = False;
    601     c = 0;
    602 
    603     for (i = 0, idx = fromPos; rWidth <= width; i++, idx++) {
    604 	if (i >= blk.length) {
    605 	    i = 0;
    606 	    pos = XawTextSourceRead(source, pos, &blk, BUFSIZ);
    607 	    if (blk.length == 0)
    608 		break;
    609 	}
    610 	c = ((wchar_t *)blk.ptr)[i];
    611 	lastWidth = rWidth;
    612 	rWidth += CharWidth(sink, fontset, fromx + rWidth, c);
    613 
    614 	if (c == _Xaw_atowc(XawLF)) {
    615 	    idx++;
    616 	    break;
    617 	}
    618 	else if ((c == _Xaw_atowc(XawSP) || c == _Xaw_atowc(XawTAB))
    619 		 && rWidth <= width) {
    620 	    whiteSpaceSeen = True;
    621 	    whiteSpacePosition = idx;
    622 	    whiteSpaceWidth = rWidth;
    623 	}
    624     }
    625 
    626     if (rWidth > width && idx > fromPos) {
    627 	idx--;
    628 	rWidth = lastWidth;
    629 	if (stopAtWordBreak && whiteSpaceSeen) {
    630 	    idx = whiteSpacePosition + 1;
    631 	    rWidth = whiteSpaceWidth;
    632 	}
    633     }
    634 
    635     if (idx >= ctx->text.lastPos && c != _Xaw_atowc(XawLF))
    636 	idx = ctx->text.lastPos + 1;
    637 
    638     *resPos = idx;
    639     *resWidth = rWidth;
    640     *resHeight = ext->max_logical_extent.height;
    641 }
    642 
    643 static void
    644 Resolve(Widget w, XawTextPosition pos, int fromx, int width,
    645 	XawTextPosition *pos_return)
    646 {
    647     int resWidth, resHeight;
    648     Widget source = XawTextGetSource(XtParent(w));
    649 
    650     FindPosition(w, pos, fromx, width, False, pos_return, &resWidth, &resHeight);
    651     if (*pos_return > GETLASTPOS)
    652 	*pos_return = GETLASTPOS;
    653 }
    654 
    655 static void
    656 GetGC(MultiSinkObject sink)
    657 {
    658     XtGCMask valuemask = (GCGraphicsExposures | GCClipXOrigin |
    659 			  GCForeground | GCBackground);
    660     XGCValues values = {
    661 	/* XXX We dont want to share a gc that will change the clip-mask */
    662 	.clip_x_origin = (int)(long)sink,
    663 	.clip_mask = None,
    664 	.graphics_exposures = False,
    665 
    666 	.foreground = sink->text_sink.foreground,
    667 	.background = sink->text_sink.background
    668     };
    669 
    670     sink->multi_sink.normgc = XtAllocateGC((Widget)sink, 0, valuemask, &values,
    671 					   GCFont | GCClipMask, 0);
    672 
    673     values.foreground = sink->text_sink.background;
    674 #ifndef OLDXAW
    675     values.background = sink->text_sink.cursor_color;
    676 #else
    677     values.background = sink->text_sink.foreground;
    678 #endif
    679     sink->multi_sink.invgc = XtAllocateGC((Widget)sink, 0, valuemask, &values,
    680 					  GCFont | GCClipMask, 0);
    681 #ifndef OLDXAW
    682     if (sink->text_sink.cursor_color != sink->text_sink.foreground) {
    683 	values.foreground = sink->text_sink.cursor_color;
    684 	values.background = sink->text_sink.foreground;
    685 	sink->multi_sink.xorgc = XtAllocateGC((Widget)sink, 0, valuemask,
    686 					      &values, GCFont | GCClipMask, 0);
    687     }
    688     else
    689 #endif /* OLDXAW */
    690 	sink->multi_sink.xorgc = NULL;
    691 
    692     XawMultiSinkResize((Widget)sink);
    693 }
    694 
    695 static void
    696 XawMultiSinkClassInitialize(void)
    697 {
    698     wspace[0] = _Xaw_atowc(XawSP);
    699     XawInitializeWidgetSet();
    700 }
    701 
    702 /*
    703  * Function:
    704  *	XawMultiSinkInitialize
    705  *
    706  * Parameters:
    707  *	request - requested and new values for the object instance
    708  *	cnew    - ""
    709  *
    710  * Description:
    711  *	Initializes the TextSink Object.
    712  */
    713 /* ARGSUSED */
    714 static void
    715 XawMultiSinkInitialize(Widget request _X_UNUSED, Widget cnew,
    716 		       ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
    717 {
    718     MultiSinkObject sink = (MultiSinkObject)cnew;
    719 
    720     GetGC(sink);
    721 
    722     if (!sink->multi_sink.fontset) XtError("Aborting: no fontset found\n");
    723 
    724     sink->multi_sink.cursor_position = 0;
    725     sink->multi_sink.laststate = XawisOff;
    726     sink->multi_sink.cursor_x = sink->multi_sink.cursor_y = 0;
    727 }
    728 
    729 /*
    730  * Function:
    731  *	XawMultiSinkDestroy
    732  *
    733  * Parameters:
    734  *	w - MultiSink Object
    735  *
    736  * Description:
    737  *	This function cleans up when the object is destroyed.
    738  */
    739 static void
    740 XawMultiSinkDestroy(Widget w)
    741 {
    742     MultiSinkObject sink = (MultiSinkObject)w;
    743 
    744     XtReleaseGC(w, sink->multi_sink.normgc);
    745     XtReleaseGC(w, sink->multi_sink.invgc);
    746     if (sink->multi_sink.xorgc)
    747 	XtReleaseGC(w, sink->multi_sink.xorgc);
    748     sink->multi_sink.normgc =
    749 	sink->multi_sink.invgc =
    750 	sink->multi_sink.xorgc = NULL;
    751 }
    752 
    753 static void
    754 XawMultiSinkResize(Widget w)
    755 {
    756     TextWidget ctx = (TextWidget)XtParent(w);
    757     MultiSinkObject sink = (MultiSinkObject)w;
    758     XRectangle rect;
    759     int width, height;
    760 
    761     if (w->core.widget_class != multiSinkObjectClass)
    762 	return;
    763 
    764     rect.x = ctx->text.r_margin.left;
    765     rect.y = ctx->text.r_margin.top;
    766     width = (int)XtWidth(ctx) -
    767       (int)ctx->text.r_margin.right - (int)ctx->text.r_margin.left;
    768     height = (int)XtHeight(ctx) -
    769       (int)ctx->text.r_margin.top - (int)ctx->text.r_margin.bottom;
    770     rect.width = (unsigned short)width;
    771     rect.height = (unsigned short)height;
    772 
    773     if (sink->multi_sink.normgc) {
    774 	if (width >= 0 && height >= 0)
    775 	    XSetClipRectangles(XtDisplay((Widget)ctx), sink->multi_sink.normgc,
    776 			       0, 0, &rect, 1, Unsorted);
    777 	else
    778 	    XSetClipMask(XtDisplay((Widget)ctx), sink->multi_sink.normgc, None);
    779     }
    780     if (sink->multi_sink.invgc) {
    781 	if (width >= 0 && height >= 0)
    782 	    XSetClipRectangles(XtDisplay((Widget)ctx), sink->multi_sink.invgc,
    783 			       0, 0, &rect, 1, Unsorted);
    784 	else
    785 	    XSetClipMask(XtDisplay((Widget)ctx), sink->multi_sink.invgc, None);
    786     }
    787     if (sink->multi_sink.xorgc) {
    788 	if (width >= 0 && height >= 0)
    789 	    XSetClipRectangles(XtDisplay((Widget)ctx), sink->multi_sink.xorgc,
    790 			       0, 0, &rect, 1, Unsorted);
    791 	else
    792 	    XSetClipMask(XtDisplay((Widget)ctx), sink->multi_sink.xorgc, None);
    793     }
    794 }
    795 
    796 /*
    797  * Function:
    798  *	XawMultiSinkSetValues
    799  *
    800  * Parameters:
    801  *	current - current state of the object
    802  *	request - what was requested
    803  *	cnew    - what the object will become
    804  *
    805  * Description:
    806  *	Sets the values for the MultiSink.
    807  *
    808  * Returns:
    809  *	True if redisplay is needed
    810  */
    811 /*ARGSUSED*/
    812 static Boolean
    813 XawMultiSinkSetValues(Widget current, Widget request _X_UNUSED, Widget cnew,
    814 		      ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
    815 {
    816     MultiSinkObject w = (MultiSinkObject)cnew;
    817     MultiSinkObject old_w = (MultiSinkObject)current;
    818 
    819     /* Font set is not in the GC! Do not make a new GC when font set changes! */
    820 
    821     if (w->multi_sink.fontset != old_w->multi_sink.fontset) {
    822 	((TextWidget)XtParent(cnew))->text.redisplay_needed = True;
    823 #ifndef NO_TAB_FIX
    824 	SetTabs((Widget)w, w->text_sink.tab_count, w->text_sink.char_tabs);
    825 #endif
    826     }
    827 
    828     if (w->text_sink.background != old_w->text_sink.background
    829 	|| w->text_sink.foreground != old_w->text_sink.foreground
    830 #ifndef OLDXAW
    831 	|| w->text_sink.cursor_color != old_w->text_sink.cursor_color
    832 #endif
    833 	) {
    834 	XtReleaseGC(cnew, w->multi_sink.normgc);
    835 	XtReleaseGC(cnew, w->multi_sink.invgc);
    836 	if (w->multi_sink.xorgc)
    837 	    XtReleaseGC(cnew, w->multi_sink.xorgc);
    838 	GetGC(w);
    839 	((TextWidget)XtParent(cnew))->text.redisplay_needed = True;
    840     }
    841     else if (w->multi_sink.echo != old_w->multi_sink.echo
    842 	     || w->multi_sink.display_nonprinting
    843 	     != old_w->multi_sink.display_nonprinting)
    844       ((TextWidget)XtParent(cnew))->text.redisplay_needed = True;
    845 
    846     return (False);
    847 }
    848 
    849 /*
    850  * Function:
    851  *	MaxLines
    852  *
    853  * Parameters:
    854  *	w - MultiSink Object
    855  *	height - height to fit lines into
    856  *
    857  * Description:
    858  *	Finds the Maximum number of lines that will fit in a given height.
    859  *
    860  * Returns:
    861  *	The number of lines that will fit
    862  */
    863 /*ARGSUSED*/
    864 static int
    865 MaxLines(Widget w, unsigned int height)
    866 {
    867     MultiSinkObject sink = (MultiSinkObject)w;
    868     int font_height;
    869     XFontSetExtents *ext = XExtentsOfFontSet(sink->multi_sink.fontset);
    870 
    871     font_height = ext->max_logical_extent.height;
    872     return (int)(height / (unsigned)font_height);
    873 }
    874 
    875 /*
    876  * Function:
    877  *	MaxHeight
    878  *
    879  * Parameters:
    880  *	w     - MultiSink Object
    881  *	lines - number of lines
    882  *
    883  * Description:
    884  *	Finds the Minimum height that will contain a given number lines.
    885  * Returns:
    886  *	The height
    887  */
    888 /*ARGSUSED*/
    889 static int
    890 MaxHeight(Widget w, int lines)
    891 {
    892     MultiSinkObject sink = (MultiSinkObject)w;
    893     XFontSetExtents *ext = XExtentsOfFontSet(sink->multi_sink.fontset);
    894 
    895     return (lines * ext->max_logical_extent.height);
    896 }
    897 
    898 /*
    899  * Function:
    900  *	SetTabs
    901  *
    902  * Arguments:
    903  *	w	  - MultiSink Object
    904  *	tab_count - number of tabs in the list
    905  *	tabs	  - text positions of the tabs
    906  *
    907  * Description:
    908  *	Sets the Tab stops.
    909  */
    910 static void
    911 SetTabs(Widget w, int tab_count, short* tabs)
    912 {
    913     MultiSinkObject sink = (MultiSinkObject)w;
    914     int i;
    915     Atom XA_FIGURE_WIDTH;
    916     unsigned long figure_width = 0;
    917     XFontStruct *font;
    918 
    919     /*
    920      * Bug:
    921      *	 Suppose the first font of fontset stores the unit of column.
    922      *
    923      * By Li Yuhong, Mar. 14, 1991
    924      */
    925     {
    926 	XFontStruct **f_list;
    927 	char **f_name;
    928 
    929 	(void)XFontsOfFontSet(sink->multi_sink.fontset, &f_list, &f_name);
    930 	font = f_list[0];
    931     }
    932 
    933     /*
    934      * Find the figure width of the current font
    935      */
    936     XA_FIGURE_WIDTH = XInternAtom(XtDisplayOfObject(w), "FIGURE_WIDTH", False);
    937     if (XA_FIGURE_WIDTH != None
    938 	&& (!XGetFontProperty(font, XA_FIGURE_WIDTH, &figure_width)
    939 	    || figure_width == 0)) {
    940 	if (font->per_char && font->min_char_or_byte2 <= '$'
    941 	    && font->max_char_or_byte2 >= '$')
    942 	    figure_width = (unsigned long)font->per_char['$' - font->min_char_or_byte2].width;
    943 	else
    944 	    figure_width = (unsigned long)font->max_bounds.width;
    945     }
    946 
    947     if (tab_count > sink->text_sink.tab_count) {
    948 	sink->text_sink.tabs = (Position *)
    949 	    XtRealloc((char *)sink->text_sink.tabs,
    950 		      (Cardinal)((unsigned long)tab_count * sizeof(Position)));
    951 	sink->text_sink.char_tabs = (short *)
    952 	    XtRealloc((char *)sink->text_sink.char_tabs,
    953 		      (Cardinal)((unsigned long)tab_count * sizeof(short)));
    954       }
    955 
    956     for (i = 0 ; i < tab_count ; i++) {
    957 	sink->text_sink.tabs[i] = (Position)((unsigned long)tabs[i] * figure_width);
    958 	sink->text_sink.char_tabs[i] = tabs[i];
    959     }
    960 
    961     sink->text_sink.tab_count = tab_count;
    962 
    963 #ifndef NO_TAB_FIX
    964     ((TextWidget)XtParent(w))->text.redisplay_needed = True;
    965 #endif
    966 }
    967 
    968 void
    969 _XawMultiSinkPosToXY(Widget w, XawTextPosition pos, Position *x, Position *y)
    970 {
    971     MultiSinkObject sink = (MultiSinkObject)((TextWidget)w)->text.sink;
    972     XFontSetExtents *ext = XExtentsOfFontSet(sink->multi_sink.fontset);
    973 
    974     _XawTextPosToXY(w, pos, x, y);
    975     *y = (Position)(*y + abs(ext->max_logical_extent.y));
    976 }
    977