#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Xatom.h>
#include <X11/Xaw/XawInit.h>
#include <X11/Xaw/MultiSinkP.h>
#include <X11/Xaw/MultiSrcP.h>
#include <X11/Xaw/TextP.h>
#include "XawI18n.h"
#include <stdio.h>
#include <ctype.h>
#include "Private.h"
#ifdef GETLASTPOS
#undef GETLASTPOS
#endif
#define GETLASTPOS \
XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True)
static void XawMultiSinkClassInitialize(void);
static void XawMultiSinkInitialize(Widget, Widget, ArgList, Cardinal*);
static void XawMultiSinkDestroy(Widget);
static void XawMultiSinkResize(Widget);
static Boolean XawMultiSinkSetValues(Widget, Widget, Widget,
ArgList, Cardinal*);
static int MaxLines(Widget, unsigned int);
static int MaxHeight(Widget, int);
static void SetTabs(Widget, int, short*);
static void DisplayText(Widget, int, int,
XawTextPosition, XawTextPosition, Bool);
static void InsertCursor(Widget, int, int, XawTextInsertState);
static void FindPosition(Widget, XawTextPosition, int, int, Bool,
XawTextPosition*, int*, int*);
static void FindDistance(Widget, XawTextPosition, int, XawTextPosition, int*,
XawTextPosition*, int*);
static void Resolve(Widget, XawTextPosition, int, int, XawTextPosition*);
static void GetCursorBounds(Widget, XRectangle*);
static void GetGC(MultiSinkObject);
static int CharWidth(MultiSinkObject, XFontSet, int, wchar_t);
static unsigned int PaintText(Widget w, GC gc, int x, int y,
wchar_t *buf, int len, Bool);
void _XawTextSinkClearToBackground(Widget, int, int, unsigned, unsigned);
static wchar_t wspace[2];
#define offset(field) XtOffsetOf(MultiSinkRec, multi_sink.field)
static XtResource resources[] = {
{
XtNfontSet,
XtCFontSet,
XtRFontSet,
sizeof(XFontSet),
offset(fontset),
XtRString,
XtDefaultFontSet
},
{
XtNecho,
XtCOutput,
XtRBoolean,
sizeof(Boolean),
offset(echo),
XtRImmediate,
(XtPointer)True
},
{
XtNdisplayNonprinting,
XtCOutput,
XtRBoolean,
sizeof(Boolean),
offset(display_nonprinting),
XtRImmediate,
(XtPointer)True
},
};
#undef offset
#define SuperClass (&textSinkClassRec)
MultiSinkClassRec multiSinkClassRec = {
{
(WidgetClass)SuperClass,
"MultiSink",
sizeof(MultiSinkRec),
XawMultiSinkClassInitialize,
NULL,
False,
XawMultiSinkInitialize,
NULL,
NULL,
NULL,
0,
resources,
XtNumber(resources),
NULLQUARK,
False,
False,
False,
False,
XawMultiSinkDestroy,
(XtProc)XawMultiSinkResize,
NULL,
XawMultiSinkSetValues,
NULL,
NULL,
NULL,
NULL,
XtVersion,
NULL,
NULL,
NULL,
NULL,
NULL,
},
{
DisplayText,
InsertCursor,
XtInheritClearToBackground,
FindPosition,
FindDistance,
Resolve,
MaxLines,
MaxHeight,
SetTabs,
GetCursorBounds,
},
{
NULL,
}
};
WidgetClass multiSinkObjectClass = (WidgetClass)&multiSinkClassRec;
static int
CharWidth(MultiSinkObject sink, XFontSet fontset, int x, wchar_t c)
{
int width = 0;
if (c == _Xaw_atowc(XawLF))
return (0);
if (c == _Xaw_atowc(XawTAB)) {
int i;
Position *tab;
width = x;
x -= ((TextWidget)XtParent((Widget)sink))->text.left_margin;
i = 0;
tab = sink->text_sink.tabs;
while (1) {
if (x < *tab)
return (*tab - x);
if (++i >= sink->text_sink.tab_count) {
x -= *tab;
i = 0;
tab = sink->text_sink.tabs;
if (width == x)
return (0);
}
else
++tab;
}
}
if (XwcTextEscapement(fontset, &c, 1) == 0) {
if (sink->multi_sink.display_nonprinting)
c = _Xaw_atowc('@');
else
c = _Xaw_atowc(XawSP);
}
width = XwcTextEscapement(fontset, &c, 1);
return (width);
}
static unsigned int
PaintText(Widget w, GC gc, int x, int y, wchar_t *buf, int len, Bool clear_bg)
{
MultiSinkObject sink = (MultiSinkObject)w;
TextWidget ctx = (TextWidget)XtParent(w);
XFontSet fontset = sink->multi_sink.fontset;
unsigned int width = XwcTextEscapement(fontset, buf, len);
if (((int)width) <= -x)
return (width);
if (clear_bg) {
XFontSetExtents *ext = XExtentsOfFontSet(fontset);
_XawTextSinkClearToBackground(w, x, y - abs(ext->max_logical_extent.y),
width, ext->max_logical_extent.height);
XwcDrawString(XtDisplay(ctx), XtWindow(ctx), fontset, gc, x, y, buf, len);
}
else
XwcDrawImageString(XtDisplay(ctx), XtWindow(ctx), fontset, gc,
x, y, buf, len);
return (width);
}
static void
DisplayText(Widget w, int x, int y,
XawTextPosition pos1, XawTextPosition pos2, Bool highlight)
{
TextWidget ctx = (TextWidget)XtParent(w);
MultiSinkObject sink = (MultiSinkObject)w;
XFontSet fontset = sink->multi_sink.fontset;
Widget source = XawTextGetSource(XtParent(w));
wchar_t buf[256];
XFontSetExtents *ext = XExtentsOfFontSet(fontset);
int j, k;
XawTextBlock blk;
GC gc, invgc, tabgc;
int max_x;
Bool clear_bg;
if (!sink->multi_sink.echo || !ctx->text.lt.lines)
return;
max_x = (int)XtWidth(ctx) - ctx->text.r_margin.right;
clear_bg = !highlight && ctx->core.background_pixmap != XtUnspecifiedPixmap;
gc = highlight ? sink->multi_sink.invgc : sink->multi_sink.normgc;
invgc = highlight ? sink->multi_sink.normgc : sink->multi_sink.invgc;
if (highlight && sink->multi_sink.xorgc)
tabgc = sink->multi_sink.xorgc;
else
tabgc = invgc;
y += abs(ext->max_logical_extent.y);
for (j = 0; pos1 < pos2;) {
pos1 = XawTextSourceRead(source, pos1, &blk, (int) pos2 - pos1);
for (k = 0; k < blk.length; k++) {
if ((unsigned) j >= (sizeof(buf) / sizeof(wchar_t)) - 1) {
if ((x += PaintText(w, gc, x, y, buf, j, clear_bg)) >= max_x)
return;
j = 0;
}
buf[j] = ((wchar_t *)blk.ptr)[k];
if (buf[j] == _Xaw_atowc(XawLF))
continue;
else if (buf[j] == _Xaw_atowc(XawTAB)) {
unsigned int width;
if (j != 0 &&
(x += PaintText(w, gc, x, y, buf, j, clear_bg)) >= max_x)
return;
width = CharWidth(sink, fontset, x, _Xaw_atowc(XawTAB));
if (clear_bg)
_XawTextSinkClearToBackground(w,
x, y - abs(ext->max_logical_extent.y),
width, ext->max_logical_extent.height);
else
XFillRectangle(XtDisplayOfObject(w), XtWindowOfObject(w),
tabgc, x,
y - abs(ext->max_logical_extent.y),
width,
ext->max_logical_extent.height);
x += width;
j = -1;
}
else if (XwcTextEscapement(sink->multi_sink.fontset, &buf[j], 1)
== 0) {
if (sink->multi_sink.display_nonprinting)
buf[j] = _Xaw_atowc('@');
else
buf[j] = _Xaw_atowc(XawSP);
}
j++;
}
}
if (j > 0)
(void)PaintText(w, gc, x, y, buf, j, clear_bg);
}
static void
GetCursorBounds(Widget w, XRectangle *rect)
{
MultiSinkObject sink = (MultiSinkObject)w;
rect->width = CharWidth(sink, sink->multi_sink.fontset, 0, _Xaw_atowc(XawSP));
rect->height = (XExtentsOfFontSet(sink->multi_sink.fontset)
->max_logical_extent.height);
rect->x = sink->multi_sink.cursor_x;
rect->y = sink->multi_sink.cursor_y - (short)rect->height;
}
static void
InsertCursor(Widget w, int x, int y, XawTextInsertState state)
{
MultiSinkObject sink = (MultiSinkObject)w;
XFontSet fontset = sink->multi_sink.fontset;
Widget ctx = XtParent(w);
XawTextPosition position = XawTextGetInsertionPoint(ctx);
if (XtIsRealized(ctx)) {
int fheight, fdiff;
XawTextBlock block;
wchar_t c;
XawTextPosition selection_start, selection_end;
Boolean has_selection;
XFontSetExtents *ext = XExtentsOfFontSet(fontset);
XawTextGetSelectionPos((Widget)ctx, &selection_start, &selection_end);
has_selection = selection_start != selection_end;
fheight = ext->max_logical_extent.height;
fdiff = fheight - abs(ext->max_logical_extent.y);
if ((sink->multi_sink.cursor_position != position || state == XawisOff)
&& !has_selection && sink->multi_sink.laststate != XawisOff) {
wchar_t *ochar;
(void)XawTextSourceRead(XawTextGetSource(ctx),
sink->multi_sink.cursor_position,
&block, 1);
if (!block.length)
ochar = NULL;
else {
c = ((wchar_t *)block.ptr)[0];
if (c == _Xaw_atowc(XawLF))
ochar = NULL;
else if (c == _Xaw_atowc(XawTAB))
ochar = wspace;
else
ochar = (wchar_t *)block.ptr;
}
if (!ochar)
_XawTextSinkClearToBackground(w, sink->multi_sink.cursor_x,
(sink->multi_sink.cursor_y - 1 -
fheight), CharWidth(sink, fontset,
0, wspace[0]),
fheight);
else {
if (XwcTextEscapement(sink->multi_sink.fontset, ochar, 1) != 0)
DisplayText(w, sink->multi_sink.cursor_x,
sink->multi_sink.cursor_y - 1 - fheight,
sink->multi_sink.cursor_position,
sink->multi_sink.cursor_position + 1,
False);
else
PaintText(w, sink->multi_sink.normgc,
sink->multi_sink.cursor_x,
sink->multi_sink.cursor_y - 1 - fdiff,
ochar, 1,
ctx->core.background_pixmap != XtUnspecifiedPixmap);
}
}
if (!has_selection && state != XawisOff) {
wchar_t *nchar;
Boolean focus = ((TextWidget)ctx)->text.hasfocus;
(void)XawTextSourceRead(XawTextGetSource(ctx),
position, &block, 1);
c = ((wchar_t *)block.ptr)[0];
if (!block.length || c == _Xaw_atowc(XawLF)
|| c == _Xaw_atowc(XawTAB))
nchar = wspace;
else
nchar = (wchar_t *)block.ptr;
if (focus) {
if (XwcTextEscapement(sink->multi_sink.fontset, nchar, 1) != 0)
XwcDrawImageString(XtDisplay(ctx), XtWindow(ctx),
fontset, sink->multi_sink.invgc,
x, (y - 1 - fdiff), nchar, 1);
else
DisplayText(w, x, y - 1 - fheight,
position, position + 1, True);
}
else
XDrawRectangle(XtDisplay(ctx), XtWindow(ctx),
sink->multi_sink.xorgc ?
sink->multi_sink.xorgc : sink->multi_sink.normgc,
x, y - 1 - fheight,
CharWidth(sink, fontset, 0, *nchar) - 1,
fheight - 1);
}
}
sink->multi_sink.cursor_x = x;
sink->multi_sink.cursor_y = y;
sink->multi_sink.laststate = state;
sink->multi_sink.cursor_position = position;
}
static void
FindDistance(Widget w, XawTextPosition fromPos, int fromx,
XawTextPosition toPos, int *resWidth,
XawTextPosition *resPos, int *resHeight)
{
MultiSinkObject sink = (MultiSinkObject)w;
XFontSet fontset = sink->multi_sink.fontset;
TextWidget ctx = (TextWidget)XtParent(w);
Widget source = ctx->text.source;
XawTextPosition idx, pos;
wchar_t c;
XFontSetExtents *ext = XExtentsOfFontSet(fontset);
XawTextBlock blk;
int i, rWidth;
pos = XawTextSourceRead(source, fromPos, &blk, toPos - fromPos);
rWidth = 0;
for (i = 0, idx = fromPos; idx < toPos; i++, idx++) {
if (i >= blk.length) {
i = 0;
XawTextSourceRead(source, pos, &blk, toPos - pos);
if (blk.length == 0)
break;
}
c = ((wchar_t *)blk.ptr)[i];
rWidth += CharWidth(sink, fontset, fromx + rWidth, c);
if (c == _Xaw_atowc(XawLF)) {
idx++;
break;
}
}
*resPos = idx;
*resWidth = rWidth;
*resHeight = ext->max_logical_extent.height;
}
static void
FindPosition(Widget w, XawTextPosition fromPos, int fromx, int width,
Bool stopAtWordBreak, XawTextPosition *resPos, int *resWidth,
int *resHeight)
{
MultiSinkObject sink = (MultiSinkObject)w;
TextWidget ctx = (TextWidget)XtParent(w);
Widget source = ctx->text.source;
XFontSet fontset = sink->multi_sink.fontset;
XawTextPosition idx, pos, whiteSpacePosition = 0;
int i, lastWidth, whiteSpaceWidth, rWidth;
Boolean whiteSpaceSeen;
wchar_t c;
XFontSetExtents *ext = XExtentsOfFontSet(fontset);
XawTextBlock blk;
pos = XawTextSourceRead(source, fromPos, &blk, BUFSIZ);
rWidth = lastWidth = whiteSpaceWidth = 0;
whiteSpaceSeen = False;
c = 0;
for (i = 0, idx = fromPos; rWidth <= width; i++, idx++) {
if (i >= blk.length) {
i = 0;
pos = XawTextSourceRead(source, pos, &blk, BUFSIZ);
if (blk.length == 0)
break;
}
c = ((wchar_t *)blk.ptr)[i];
lastWidth = rWidth;
rWidth += CharWidth(sink, fontset, fromx + rWidth, c);
if (c == _Xaw_atowc(XawLF)) {
idx++;
break;
}
else if ((c == _Xaw_atowc(XawSP) || c == _Xaw_atowc(XawTAB))
&& rWidth <= width) {
whiteSpaceSeen = True;
whiteSpacePosition = idx;
whiteSpaceWidth = rWidth;
}
}
if (rWidth > width && idx > fromPos) {
idx--;
rWidth = lastWidth;
if (stopAtWordBreak && whiteSpaceSeen) {
idx = whiteSpacePosition + 1;
rWidth = whiteSpaceWidth;
}
}
if (idx >= ctx->text.lastPos && c != _Xaw_atowc(XawLF))
idx = ctx->text.lastPos + 1;
*resPos = idx;
*resWidth = rWidth;
*resHeight = ext->max_logical_extent.height;
}
static void
Resolve(Widget w, XawTextPosition pos, int fromx, int width,
XawTextPosition *pos_return)
{
int resWidth, resHeight;
Widget source = XawTextGetSource(XtParent(w));
FindPosition(w, pos, fromx, width, False, pos_return, &resWidth, &resHeight);
if (*pos_return > GETLASTPOS)
*pos_return = GETLASTPOS;
}
static void
GetGC(MultiSinkObject sink)
{
XtGCMask valuemask = (GCGraphicsExposures | GCClipXOrigin |
GCForeground | GCBackground);
XGCValues values;
values.clip_x_origin = (long)sink;
values.clip_mask = None;
values.graphics_exposures = False;
values.foreground = sink->text_sink.foreground;
values.background = sink->text_sink.background;
sink->multi_sink.normgc = XtAllocateGC((Widget)sink, 0, valuemask, &values,
GCFont | GCClipMask, 0);
values.foreground = sink->text_sink.background;
#ifndef OLDXAW
values.background = sink->text_sink.cursor_color;
#else
values.background = sink->text_sink.foreground;
#endif
sink->multi_sink.invgc = XtAllocateGC((Widget)sink, 0, valuemask, &values,
GCFont | GCClipMask, 0);
#ifndef OLDXAW
if (sink->text_sink.cursor_color != sink->text_sink.foreground) {
values.foreground = sink->text_sink.cursor_color;
values.background = sink->text_sink.foreground;
sink->multi_sink.xorgc = XtAllocateGC((Widget)sink, 0, valuemask,
&values, GCFont | GCClipMask, 0);
}
else
#endif
sink->multi_sink.xorgc = NULL;
XawMultiSinkResize((Widget)sink);
}
static void
XawMultiSinkClassInitialize(void)
{
wspace[0] = _Xaw_atowc(XawSP);
XawInitializeWidgetSet();
}
static void
XawMultiSinkInitialize(Widget request, Widget cnew,
ArgList args, Cardinal *num_args)
{
MultiSinkObject sink = (MultiSinkObject)cnew;
GetGC(sink);
if (!sink->multi_sink.fontset) XtError("Aborting: no fontset found\n");
sink->multi_sink.cursor_position = 0;
sink->multi_sink.laststate = XawisOff;
sink->multi_sink.cursor_x = sink->multi_sink.cursor_y = 0;
}
static void
XawMultiSinkDestroy(Widget w)
{
MultiSinkObject sink = (MultiSinkObject)w;
XtReleaseGC(w, sink->multi_sink.normgc);
XtReleaseGC(w, sink->multi_sink.invgc);
if (sink->multi_sink.xorgc)
XtReleaseGC(w, sink->multi_sink.xorgc);
sink->multi_sink.normgc =
sink->multi_sink.invgc =
sink->multi_sink.xorgc = NULL;
}
static void
XawMultiSinkResize(Widget w)
{
TextWidget ctx = (TextWidget)XtParent(w);
MultiSinkObject sink = (MultiSinkObject)w;
XRectangle rect;
int width, height;
if (w->core.widget_class != multiSinkObjectClass)
return;
rect.x = ctx->text.r_margin.left;
rect.y = ctx->text.r_margin.top;
width = (int)XtWidth(ctx) -
(int)ctx->text.r_margin.right - (int)ctx->text.r_margin.left;
height = (int)XtHeight(ctx) -
(int)ctx->text.r_margin.top - (int)ctx->text.r_margin.bottom;
rect.width = width;
rect.height = height;
if (sink->multi_sink.normgc) {
if (width >= 0 && height >= 0)
XSetClipRectangles(XtDisplay((Widget)ctx), sink->multi_sink.normgc,
0, 0, &rect, 1, Unsorted);
else
XSetClipMask(XtDisplay((Widget)ctx), sink->multi_sink.normgc, None);
}
if (sink->multi_sink.invgc) {
if (width >= 0 && height >= 0)
XSetClipRectangles(XtDisplay((Widget)ctx), sink->multi_sink.invgc,
0, 0, &rect, 1, Unsorted);
else
XSetClipMask(XtDisplay((Widget)ctx), sink->multi_sink.invgc, None);
}
if (sink->multi_sink.xorgc) {
if (width >= 0 && height >= 0)
XSetClipRectangles(XtDisplay((Widget)ctx), sink->multi_sink.xorgc,
0, 0, &rect, 1, Unsorted);
else
XSetClipMask(XtDisplay((Widget)ctx), sink->multi_sink.xorgc, None);
}
}
static Boolean
XawMultiSinkSetValues(Widget current, Widget request, Widget cnew,
ArgList args, Cardinal *num_args)
{
MultiSinkObject w = (MultiSinkObject)cnew;
MultiSinkObject old_w = (MultiSinkObject)current;
if (w->multi_sink.fontset != old_w->multi_sink.fontset) {
((TextWidget)XtParent(cnew))->text.redisplay_needed = True;
#ifndef NO_TAB_FIX
SetTabs((Widget)w, w->text_sink.tab_count, w->text_sink.char_tabs);
#endif
}
if (w->text_sink.background != old_w->text_sink.background
|| w->text_sink.foreground != old_w->text_sink.foreground
#ifndef OLDXAW
|| w->text_sink.cursor_color != old_w->text_sink.cursor_color
#endif
) {
XtReleaseGC(cnew, w->multi_sink.normgc);
XtReleaseGC(cnew, w->multi_sink.invgc);
if (w->multi_sink.xorgc)
XtReleaseGC(cnew, w->multi_sink.xorgc);
GetGC(w);
((TextWidget)XtParent(cnew))->text.redisplay_needed = True;
}
else if (w->multi_sink.echo != old_w->multi_sink.echo
|| w->multi_sink.display_nonprinting
!= old_w->multi_sink.display_nonprinting)
((TextWidget)XtParent(cnew))->text.redisplay_needed = True;
return (False);
}
static int
MaxLines(Widget w, unsigned int height)
{
MultiSinkObject sink = (MultiSinkObject)w;
int font_height;
XFontSetExtents *ext = XExtentsOfFontSet(sink->multi_sink.fontset);
font_height = ext->max_logical_extent.height;
return (height / font_height);
}
static int
MaxHeight(Widget w, int lines)
{
MultiSinkObject sink = (MultiSinkObject)w;
XFontSetExtents *ext = XExtentsOfFontSet(sink->multi_sink.fontset);
return (lines * ext->max_logical_extent.height);
}
static void
SetTabs(Widget w, int tab_count, short* tabs)
{
MultiSinkObject sink = (MultiSinkObject)w;
int i;
Atom XA_FIGURE_WIDTH;
unsigned long figure_width = 0;
XFontStruct *font;
{
XFontStruct **f_list;
char **f_name;
(void)XFontsOfFontSet(sink->multi_sink.fontset, &f_list, &f_name);
font = f_list[0];
}
XA_FIGURE_WIDTH = XInternAtom(XtDisplayOfObject(w), "FIGURE_WIDTH", False);
if (XA_FIGURE_WIDTH != None
&& (!XGetFontProperty(font, XA_FIGURE_WIDTH, &figure_width)
|| figure_width == 0)) {
if (font->per_char && font->min_char_or_byte2 <= '$'
&& font->max_char_or_byte2 >= '$')
figure_width = font->per_char['$' - font->min_char_or_byte2].width;
else
figure_width = font->max_bounds.width;
}
if (tab_count > sink->text_sink.tab_count) {
sink->text_sink.tabs = (Position *)
XtRealloc((char *)sink->text_sink.tabs,
(Cardinal)(tab_count * sizeof(Position)));
sink->text_sink.char_tabs = (short *)
XtRealloc((char *)sink->text_sink.char_tabs,
(Cardinal)(tab_count * sizeof(short)));
}
for (i = 0 ; i < tab_count ; i++) {
sink->text_sink.tabs[i] = tabs[i] * figure_width;
sink->text_sink.char_tabs[i] = tabs[i];
}
sink->text_sink.tab_count = tab_count;
#ifndef NO_TAB_FIX
((TextWidget)XtParent(w))->text.redisplay_needed = True;
#endif
}
void
_XawMultiSinkPosToXY(Widget w, XawTextPosition pos, Position *x, Position *y)
{
MultiSinkObject sink = (MultiSinkObject)((TextWidget)w)->text.sink;
XFontSetExtents *ext = XExtentsOfFontSet(sink->multi_sink.fontset);
_XawTextPosToXY(w, pos, x, y);
*y += abs(ext->max_logical_extent.y);
}