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
27Copyright 1987, 1988, 1994, 1998  The Open Group
28
29Permission to use, copy, modify, distribute, and sell this software and its
30documentation for any purpose is hereby granted without fee, provided that
31the above copyright notice appear in all copies and that both that
32copyright notice and this permission notice appear in supporting
33documentation.
34
35The above copyright notice and this permission notice shall be included in
36all copies or substantial portions of the Software.
37
38THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
41OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
42AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
43CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44
45Except as contained in this notice, the name of The Open Group shall not be
46used in advertising or otherwise to promote the sale, use or other dealings
47in this Software without prior written authorization from The Open Group.
48
49
50Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
51
52                        All Rights Reserved
53
54Permission to use, copy, modify, and distribute this software and its
55documentation for any purpose and without fee is hereby granted,
56provided that the above copyright notice appear in all copies and that
57both that copyright notice and this permission notice appear in
58supporting documentation, and that the name of Digital not be
59used in advertising or publicity pertaining to distribution of the
60software without specific, written prior permission.
61
62DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
63ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
64DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
65ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
66WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
67ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
68SOFTWARE.
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 */
97static void XawMultiSinkClassInitialize(void);
98static void XawMultiSinkInitialize(Widget, Widget, ArgList, Cardinal*);
99static void XawMultiSinkDestroy(Widget);
100static void XawMultiSinkResize(Widget);
101static Boolean XawMultiSinkSetValues(Widget, Widget, Widget,
102				     ArgList, Cardinal*);
103static int MaxLines(Widget, unsigned int);
104static int MaxHeight(Widget, int);
105static void SetTabs(Widget, int, short*);
106static void DisplayText(Widget, int, int,
107			XawTextPosition, XawTextPosition, Bool);
108static void InsertCursor(Widget, int, int, XawTextInsertState);
109static void FindPosition(Widget, XawTextPosition, int, int, Bool,
110			 XawTextPosition*, int*, int*);
111static void FindDistance(Widget, XawTextPosition, int, XawTextPosition, int*,
112			 XawTextPosition*, int*);
113static void Resolve(Widget, XawTextPosition, int, int, XawTextPosition*);
114static void GetCursorBounds(Widget, XRectangle*);
115
116/*
117 * Prototypes
118 */
119static void GetGC(MultiSinkObject);
120static int CharWidth(MultiSinkObject, XFontSet, int, wchar_t);
121static 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 */
127void _XawTextSinkClearToBackground(Widget, int, int, unsigned, unsigned);
128
129/*
130 * Initialization
131 */
132static wchar_t wspace[2];
133
134#define offset(field) XtOffsetOf(MultiSinkRec, multi_sink.field)
135static 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)
167MultiSinkClassRec 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
222WidgetClass multiSinkObjectClass = (WidgetClass)&multiSinkClassRec;
223
224/*
225 * Implementation
226 */
227static int
228CharWidth(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 */
305static unsigned int
306PaintText(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 */
334static void
335DisplayText(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 */
425static void
426GetCursorBounds(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 */
440static void
441InsertCursor(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 */
544static void
545FindDistance(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
582static void
583FindPosition(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
643static void
644Resolve(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
655static void
656GetGC(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
695static void
696XawMultiSinkClassInitialize(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 */
714static void
715XawMultiSinkInitialize(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 */
739static void
740XawMultiSinkDestroy(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
753static void
754XawMultiSinkResize(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*/
812static Boolean
813XawMultiSinkSetValues(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*/
864static int
865MaxLines(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*/
889static int
890MaxHeight(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 */
910static void
911SetTabs(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
968void
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