1 /*************************************<+>*************************************
2 *****************************************************************************
8 ** Description: Code for TextEdit widget ascii sink
10 *****************************************************************************
12 ** Copyright (c) 1988 by Hewlett-Packard Company
13 ** Copyright (c) 1987, 1988 by Digital Equipment Corporation, Maynard,
14 ** Massachusetts, and the Massachusetts Institute of Technology,
15 ** Cambridge, Massachusetts
17 ** Permission to use, copy, modify, and distribute this software
18 ** and its documentation for any purpose and without fee is hereby
19 ** granted, provided that the above copyright notice appear in all
20 ** copies and that both that copyright notice and this permission
21 ** notice appear in supporting documentation, and that the names of
22 ** Hewlett-Packard, Digital or M.I.T. not be used in advertising or
23 ** publicity pertaining to distribution of the software without
24 ** written prior permission.
26 ** DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
27 ** ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
28 ** DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
29 ** ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
30 ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
31 ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
34 *****************************************************************************
35 *************************************<+>*************************************/
39 #include <X11/Xutil.h>
40 #include <X11/Xatom.h>
41 #include <X11/Intrinsic.h>
42 #include <X11/IntrinsicP.h>
43 #include <X11/StringDefs.h>
47 #include <Xw/TextEditP.h>
54 #define GETLASTPOS (*(source->scan))(source, 0, XwstLast, XwsdRight, 1, TRUE)
55 /* Private Ascii TextSink Definitions */
57 static unsigned bufferSize
= 200;
59 typedef struct _AsciiSinkData
{
61 GC normgc
, invgc
, xorgc
;
64 Pixmap insertCursorOn
;
65 XwInsertState laststate
;
66 } AsciiSinkData
, *AsciiSinkPtr
;
68 static unsigned char *buf
;
70 /* XXX foreground default should be XtDefaultFGPixel. How do i do that?? */
72 static XtResource SinkResources
[] = {
73 {XtNfont
, XtCFont
, XtRFontStruct
, sizeof (XFontStruct
*),
74 XtOffset(AsciiSinkPtr
, font
), XtRString
, "Fixed"},
75 {XtNforeground
, XtCForeground
, XtRPixel
, sizeof (int),
76 XtOffset(AsciiSinkPtr
, foreground
), XtRString
, "XtDefaultForeground"},
81 /*--------------------------------------------------------------------------+*/
82 static int CharWidth (data
, x
, margin
, c
)
83 /*--------------------------------------------------------------------------+*/
89 int width
, nonPrinting
;
90 XFontStruct
*font
= data
->font
;
93 /* This is totally bogus!! need to know tab settings etc.. */
94 return data
->tabwidth
- ((x
-margin
) % data
->tabwidth
);
97 nonPrinting
= (c
< SP
);
98 if (nonPrinting
) c
+= '@';
100 if (font
->per_char
&&
101 (c
>= font
->min_char_or_byte2
&& c
<= font
->max_char_or_byte2
))
102 width
= font
->per_char
[c
- font
->min_char_or_byte2
].width
;
104 width
= font
->min_bounds
.width
;
107 width
+= CharWidth(data
, x
, margin
, '^');
112 /* Sink Object Functions */
114 #define LBEARING(x) \
115 ((font->per_char != NULL && \
116 ((x) >= font->min_char_or_byte2 && (x) <= font->max_char_or_byte2)) \
117 ? font->per_char[(x) - font->min_char_or_byte2].lbearing \
118 : font->min_bounds.lbearing)
120 /*--------------------------------------------------------------------------+*/
121 static int AsciiDisplayText (w
, x
, y
, pos1
, pos2
, highlight
)
122 /*--------------------------------------------------------------------------+*/
126 XwTextPosition pos1
, pos2
;
128 XwTextSink
*sink
= ((XwTextEditWidget
)w
)->text
.sink
;
129 XwTextSource
*source
= ((XwTextEditWidget
)w
)->text
.source
;
130 AsciiSinkData
*data
= (AsciiSinkData
*) sink
->data
;
131 int margin
= ((XwTextEditWidget
)w
)->text
.leftmargin
;
133 XFontStruct
*font
= data
->font
;
137 GC gc
= highlight
? data
->invgc
: data
->normgc
;
138 GC invgc
= highlight
? data
->normgc
: data
->invgc
;
142 while (pos1
< pos2
) {
143 pos1
= (*(source
->read
))(source
, pos1
, &blk
, pos2
- pos1
);
144 for (k
= 0; k
< blk
.length
; k
++) {
145 if (j
>= bufferSize
- 5) {
147 buf
= (unsigned char *) XtRealloc(buf
, bufferSize
);
152 else if (buf
[j
] == '\t') {
153 XDrawImageString(XtDisplay(w
), XtWindow(w
),
154 gc
, x
- LBEARING(*buf
), y
, buf
, j
);
156 x
+= XTextWidth(data
->font
, buf
, j
);
157 width
= CharWidth(data
, x
, margin
, '\t');
158 XFillRectangle(XtDisplay(w
), XtWindow(w
), invgc
, x
,
159 y
- font
->ascent
, width
,
160 (Dimension
) (data
->font
->ascent
+
161 data
->font
->descent
));
167 buf
[j
+ 1] = buf
[j
] + '@';
174 XDrawImageString(XtDisplay(w
), XtWindow(w
), gc
, x
- LBEARING(*buf
), y
,
179 # define insertCursor_width 6
180 # define insertCursor_height 3
181 static char insertCursor_bits
[] = {0x0c, 0x1e, 0x33};
183 /*--------------------------------------------------------------------------+*/
184 static Pixmap
CreateInsertCursor(s
)
185 /*--------------------------------------------------------------------------+*/
189 return (XCreateBitmapFromData (DisplayOfScreen(s
), RootWindowOfScreen(s
),
190 insertCursor_bits
, insertCursor_width
, insertCursor_height
));
194 * The following procedure manages the "insert" cursor.
197 /*--------------------------------------------------------------------------+*/
198 static AsciiInsertCursor (w
, x
, y
, state
)
199 /*--------------------------------------------------------------------------+*/
204 XwTextSink
*sink
= ((XwTextEditWidget
)w
)->text
.sink
;
205 AsciiSinkData
*data
= (AsciiSinkData
*) sink
->data
;
209 (state == XwisOn) ? data->insertCursorOn : data->insertCursorOff, w,
210 data->normgc, 0, 0, insertCursor_width, insertCursor_height,
211 x - (insertCursor_width >> 1), y - (insertCursor_height));
213 if (state != data->laststate && XtIsRealized(w))
214 XCopyPlane(XtDisplay(w),
215 data->insertCursorOn, XtWindow(w),
216 data->xorgc, 0, 0, insertCursor_width, insertCursor_height,
217 x - (insertCursor_width >> 1), y - (insertCursor_height), 1);
219 /* This change goes with the cursor hack for the broken server */
220 XCopyArea (XtDisplay(w
), data
->insertCursorOn
, XtWindow (w
),
221 data
->xorgc
, 0, 0, insertCursor_width
, insertCursor_height
,
222 x
- (insertCursor_width
>> 1), y
- (insertCursor_height
));
224 data
->laststate
= state
;
228 * Clear the passed region to the background color.
231 /*--------------------------------------------------------------------------+*/
232 static AsciiClearToBackground (w
, x
, y
, width
, height
)
233 /*--------------------------------------------------------------------------+*/
236 Dimension width
, height
;
238 XwTextSink
*sink
= ((XwTextEditWidget
)w
)->text
.sink
;
239 AsciiSinkData
*data
= (AsciiSinkData
*) sink
->data
;
240 XFillRectangle(XtDisplay(w
), XtWindow(w
), data
->invgc
, x
, y
, width
, height
);
244 * Given two positions, find the distance between them.
247 /*--------------------------------------------------------------------------+*/
248 static AsciiFindDistance (w
, fromPos
, fromx
, toPos
,
249 resWidth
, resPos
, resHeight
)
250 /*--------------------------------------------------------------------------+*/
252 XwTextPosition fromPos
; /* First position. */
253 int fromx
; /* Horizontal location of first position. */
254 XwTextPosition toPos
; /* Second position. */
255 int *resWidth
; /* Distance between fromPos and resPos. */
256 int *resPos
; /* Actual second position used. */
257 int *resHeight
; /* Height required. */
259 XwTextSink
*sink
= ((XwTextEditWidget
)w
)->text
.sink
;
260 XwTextSource
*source
= ((XwTextEditWidget
)w
)->text
.source
;
261 int margin
= ((XwTextEditWidget
)w
)->text
.leftmargin
;
264 register XwTextPosition index
, lastPos
;
265 register unsigned char c
;
268 data
= (AsciiSinkData
*) sink
->data
;
269 /* we may not need this */
270 lastPos
= GETLASTPOS
;
271 (*(source
->read
))(source
, fromPos
, &blk
, toPos
- fromPos
);
273 for (index
= fromPos
; index
!= toPos
&& index
< lastPos
; index
++) {
274 if (index
- blk
.firstPos
>= blk
.length
)
275 (*(source
->read
))(source
, index
, &blk
, toPos
- fromPos
);
276 c
= blk
.ptr
[index
- blk
.firstPos
];
278 *resWidth
+= CharWidth(data
, fromx
+ *resWidth
, margin
, SP
);
282 *resWidth
+= CharWidth(data
, fromx
+ *resWidth
, margin
, c
);
285 *resHeight
= data
->font
->ascent
+ data
->font
->descent
;
289 /*--------------------------------------------------------------------------+*/
290 static TextFit
AsciiTextFit (w
, fromPos
, fromx
, width
, wrap
, wrapWhiteSpace
,
291 fitPos
, drawPos
, nextPos
, resWidth
, resHeight
)
292 /*--------------------------------------------------------------------------+*/
294 XwTextPosition fromPos
; /* Starting position. */
295 int fromx
; /* Horizontal location of starting position. */
296 int width
; /* Desired width. */
297 int wrap
; /* Whether line should wrap at all */
298 int wrapWhiteSpace
; /* Whether line should wrap at white space */
300 XwTextPosition
*fitPos
; /* pos of last char which fits in specified
302 XwTextPosition
*drawPos
; /* pos of last char to draw in specified
303 width based on wrap model */
304 XwTextPosition
*nextPos
; /* pos of next char to draw outside specified
305 width based on wrap model */
306 int *resWidth
; /* Actual width used. */
307 int *resHeight
; /* Height required. */
309 XwTextSink
*sink
= ((XwTextEditWidget
)w
)->text
.sink
;
310 XwTextSource
*source
= ((XwTextEditWidget
)w
)->text
.source
;
311 int margin
= ((XwTextEditWidget
)w
)->text
.leftmargin
;
313 XwTextPosition lastPos
, pos
, whiteSpacePosition
;
314 XwTextPosition fitL
, drawL
;
315 /* local equivalents of fitPos, drawPos, nextPos */
316 int lastWidth
, whiteSpaceWidth
, whiteSpaceSeen
;
322 data
= (AsciiSinkData
*) sink
->data
;
323 lastPos
= GETLASTPOS
;
329 useAll
= whiteSpaceSeen
= FALSE
;
333 (*(source
->read
))(source
, fromPos
, &blk
, bufferSize
);
335 while (*resWidth
<= width
)
336 { lastWidth
= *resWidth
;
344 if (pos
- blk
.firstPos
>= blk
.length
)
345 (*(source
->read
))(source
, pos
, &blk
, bufferSize
);
346 c
= blk
.ptr
[pos
- blk
.firstPos
];
354 if (wrapWhiteSpace
&& isWhiteSpace(c
))
355 { whiteSpaceSeen
= TRUE
;
357 whiteSpaceWidth
= *resWidth
;
360 *resWidth
+= CharWidth(data
, fromx
+ *resWidth
, margin
, c
);
369 *resWidth
= lastWidth
;
371 else if (wrapWhiteSpace
&& whiteSpaceSeen
)
373 *nextPos
= drawL
+ 2 ;
374 *resWidth
= whiteSpaceWidth
;
375 fit
= tfWrapWhiteSpace
;
379 *nextPos
= fitL
+ 1 ;
380 *resWidth
= lastWidth
;
385 /* scan source for newline or end */
387 (*(source
->scan
)) (source
, pos
, XwstEOL
, XwsdRight
, 1, TRUE
) + 1 ;
388 *resWidth
= lastWidth
;
391 *resHeight
= data
->font
->ascent
+ data
->font
->descent
;
395 /*--------------------------------------------------------------------------+*/
396 static AsciiFindPosition(w
, fromPos
, fromx
, width
, stopAtWordBreak
,
397 resPos
, resWidth
, resHeight
)
398 /*--------------------------------------------------------------------------+*/
400 XwTextPosition fromPos
; /* Starting position. */
401 int fromx
; /* Horizontal location of starting position. */
402 int width
; /* Desired width. */
403 int stopAtWordBreak
; /* Whether the resulting position should be at
405 XwTextPosition
*resPos
; /* Resulting position. */
406 int *resWidth
; /* Actual width used. */
407 int *resHeight
; /* Height required. */
409 XwTextSink
*sink
= ((XwTextEditWidget
)w
)->text
.sink
;
410 XwTextSource
*source
= ((XwTextEditWidget
)w
)->text
.source
;
411 int margin
= ((XwTextEditWidget
)w
)->text
.leftmargin
;
413 XwTextPosition lastPos
, index
, whiteSpacePosition
;
414 int lastWidth
, whiteSpaceWidth
;
415 Boolean whiteSpaceSeen
;
418 data
= (AsciiSinkData
*) sink
->data
;
419 lastPos
= GETLASTPOS
;
421 (*(source
->read
))(source
, fromPos
, &blk
, bufferSize
);
423 whiteSpaceSeen
= FALSE
;
425 for (index
= fromPos
; *resWidth
<= width
&& index
< lastPos
; index
++) {
426 lastWidth
= *resWidth
;
427 if (index
- blk
.firstPos
>= blk
.length
)
428 (*(source
->read
))(source
, index
, &blk
, bufferSize
);
429 c
= blk
.ptr
[index
- blk
.firstPos
];
431 *resWidth
+= CharWidth(data
, fromx
+ *resWidth
, margin
, SP
);
435 *resWidth
+= CharWidth(data
, fromx
+ *resWidth
, margin
, c
);
436 if ((c
== SP
|| c
== TAB
) && *resWidth
<= width
) {
437 whiteSpaceSeen
= TRUE
;
438 whiteSpacePosition
= index
;
439 whiteSpaceWidth
= *resWidth
;
442 if (*resWidth
> width
&& index
> fromPos
) {
443 *resWidth
= lastWidth
;
445 if (stopAtWordBreak
&& whiteSpaceSeen
) {
446 index
= whiteSpacePosition
+ 1;
447 *resWidth
= whiteSpaceWidth
;
450 if (index
== lastPos
&& c
!= LF
) index
= lastPos
+ 1;
452 *resHeight
= data
->font
->ascent
+ data
->font
->descent
;
456 /*--------------------------------------------------------------------------+*/
457 static int AsciiResolveToPosition (w
, pos
, fromx
, width
,
459 /*--------------------------------------------------------------------------+*/
463 XwTextPosition
*leftPos
, *rightPos
;
465 int resWidth
, resHeight
;
466 XwTextSink
*sink
= ((XwTextEditWidget
)w
)->text
.sink
;
467 XwTextSource
*source
= ((XwTextEditWidget
)w
)->text
.source
;
469 AsciiFindPosition(w
, pos
, fromx
, width
, FALSE
,
470 leftPos
, &resWidth
, &resHeight
);
471 if (*leftPos
> GETLASTPOS
)
472 *leftPos
= GETLASTPOS
;
473 *rightPos
= *leftPos
;
477 /*--------------------------------------------------------------------------+*/
478 static int AsciiMaxLinesForHeight (w
)
479 /*--------------------------------------------------------------------------+*/
483 XwTextSink
*sink
= ((XwTextEditWidget
)w
)->text
.sink
;
485 data
= (AsciiSinkData
*) sink
->data
;
486 return (int) ((w
->core
.height
487 - ((XwTextEditWidget
) w
)->text
.topmargin
488 - ((XwTextEditWidget
) w
)->text
.bottommargin
490 / (data
->font
->ascent
+ data
->font
->descent
)
495 /*--------------------------------------------------------------------------+*/
496 static int AsciiMaxHeightForLines (w
, lines
)
497 /*--------------------------------------------------------------------------+*/
502 XwTextSink
*sink
= ((XwTextEditWidget
)w
)->text
.sink
;
504 data
= (AsciiSinkData
*) sink
->data
;
505 return(lines
* (data
->font
->ascent
+ data
->font
->descent
));
509 /***** Public routines *****/
511 static Boolean initialized
= FALSE
;
512 static XContext asciiSinkContext
;
514 /*--------------------------------------------------------------------------+*/
515 void AsciiSinkInitialize()
516 /*--------------------------------------------------------------------------+*/
522 asciiSinkContext
= XUniqueContext();
524 buf
= (unsigned char *) XtMalloc(bufferSize
);
527 /*--------------------------------------------------------------------------+*/
528 static Boolean
XwAsciiSinkCheckData(self
)
529 /*--------------------------------------------------------------------------+*/
532 XwTextEditWidget tew
= self
->parent
;
534 /* make sure margins are big enough to keep traversal highlight
535 from obscuring text or cursor.
538 minBorder
= (tew
->primitive
.traversal_type
== XwHIGHLIGHT_OFF
)
539 ? RequiredCursorMargin
540 : RequiredCursorMargin
+ tew
->primitive
.highlight_thickness
;
542 if (tew
->text
.topmargin
< minBorder
)
543 tew
->text
.topmargin
= minBorder
;
544 if (tew
->text
.bottommargin
< minBorder
)
545 tew
->text
.bottommargin
= minBorder
;
546 if (tew
->text
.rightmargin
< minBorder
)
547 tew
->text
.rightmargin
= minBorder
;
548 if (tew
->text
.leftmargin
< minBorder
)
549 tew
->text
.leftmargin
= minBorder
;
552 if ((*(self
->maxLines
))(tew
) < 1)
553 XtWarning("TextEdit window too small to display a single line of text.");
557 /*--------------------------------------------------------------------------+*/
558 void XwAsciiSinkDestroy (sink
)
559 /*--------------------------------------------------------------------------+*/
563 data
= (AsciiSinkData
*) sink
->data
;
564 XtFree((char *) data
);
565 XtFree((char *) sink
);
568 /*--------------------------------------------------------------------------+*/
569 XwTextSink
*XwAsciiSinkCreate (w
, args
, num_args
)
570 /*--------------------------------------------------------------------------+*/
577 unsigned long valuemask
= (GCFont
| GCGraphicsExposures
|
578 GCForeground
| GCBackground
| GCFunction
);
584 AsciiSinkInitialize();
586 sink
= XtNew(XwTextSink
);
587 sink
->parent
= (XwTextEditWidget
) w
;
588 sink
->parent
->text
.sink
= sink
; /* disgusting */
589 sink
->display
= AsciiDisplayText
;
590 sink
->insertCursor
= AsciiInsertCursor
;
591 sink
->clearToBackground
= AsciiClearToBackground
;
592 sink
->findPosition
= AsciiFindPosition
;
593 sink
->textFitFn
= AsciiTextFit
;
594 sink
->findDistance
= AsciiFindDistance
;
595 sink
->resolve
= AsciiResolveToPosition
;
596 sink
->maxLines
= AsciiMaxLinesForHeight
;
597 sink
->maxHeight
= AsciiMaxHeightForLines
;
598 sink
->resources
= SinkResources
;
599 sink
->resource_num
= XtNumber(SinkResources
);
600 sink
->check_data
= XwAsciiSinkCheckData
;
601 sink
->destroy
= XwAsciiSinkDestroy
;
602 sink
->LineLastWidth
= 0 ;
603 sink
->LineLastPosition
= 0 ;
604 data
= XtNew(AsciiSinkData
);
605 sink
->data
= (int *)data
;
607 XtGetSubresources (w
, (caddr_t
)data
, "display", "Display",
608 SinkResources
, XtNumber(SinkResources
),
611 /* XXX do i have to XLoadQueryFont or does the resource guy do it for me */
614 values
.function
= GXcopy
;
615 values
.font
= font
->fid
;
616 values
.graphics_exposures
= (Bool
) FALSE
;
617 values
.foreground
= data
->foreground
;
618 values
.background
= w
->core
.background_pixel
;
619 data
->normgc
= XtGetGC(w
, valuemask
, &values
);
620 values
.foreground
= w
->core
.background_pixel
;
621 values
.background
= data
->foreground
;
622 data
->invgc
= XtGetGC(w
, valuemask
, &values
);
623 values
.function
= GXxor
;
624 values
.foreground
= data
->foreground
^ w
->core
.background_pixel
;
625 values
.background
= 0;
626 data
->xorgc
= XtGetGC(w
, valuemask
, &values
);
629 if ((!XGetFontProperty(font
, XA_QUAD_WIDTH
, &wid
)) || wid
<= 0) {
630 if (font
->per_char
&& font
->min_char_or_byte2
<= '0' &&
631 font
->max_char_or_byte2
>= '0')
632 wid
= font
->per_char
['0' - font
->min_char_or_byte2
].width
;
634 wid
= font
->max_bounds
.width
;
636 if (wid
<= 0) wid
= 1;
637 data
->tabwidth
= 8 * wid
;
640 /* data->insertCursorOn = CreateInsertCursor(XtScreen(w)); */
642 {/* Correction from AsciiSink.c on R2 tape */
643 Screen
*screen
= XtScreen(w
);
644 Display
*dpy
= XtDisplay(w
);
645 Window root
= RootWindowOfScreen(screen
);
646 Pixmap bitmap
= XCreateBitmapFromData(dpy
, root
, insertCursor_bits
,
648 insertCursor_height
);
649 Pixmap pixmap
= XCreatePixmap(dpy
, root
,insertCursor_width
,
651 DefaultDepthOfScreen(screen
));
655 gcv
.function
= GXcopy
;
656 gcv
.foreground
= data
->foreground
^ w
->core
.background_pixel
;
658 gcv
.graphics_exposures
= False
;
659 gc
= XtGetGC(w
, (GCFunction
| GCForeground
| GCBackground
|
660 GCGraphicsExposures
), &gcv
);
661 XCopyPlane(dpy
, bitmap
, pixmap
, gc
, 0, 0, insertCursor_width
,
662 insertCursor_height
, 0, 0, 1);
664 data
->insertCursorOn
= pixmap
;
667 data
->laststate
= XwisOff
;
668 (*(sink
->check_data
))(sink
);