1 | /* |
---|
2 | * $Id: TextDisplay.c,v 1.2 1999-01-22 23:17:02 ghudson Exp $ |
---|
3 | * |
---|
4 | * Copyright 1990, 1991 by the Massachusetts Institute of Technology. |
---|
5 | * |
---|
6 | * For copying and distribution information, please see the file |
---|
7 | * <mit-copyright.h>. |
---|
8 | * |
---|
9 | */ |
---|
10 | |
---|
11 | #if (!defined(lint)) && (!defined(SABER)) |
---|
12 | static char rcsid[] = |
---|
13 | "$Id: TextDisplay.c,v 1.2 1999-01-22 23:17:02 ghudson Exp $"; |
---|
14 | #endif |
---|
15 | |
---|
16 | #include "mit-copyright.h" |
---|
17 | #include <stdio.h> |
---|
18 | #include <string.h> |
---|
19 | #include <ctype.h> |
---|
20 | #include "Jets.h" |
---|
21 | #include "TextDisplay.h" |
---|
22 | #include "xselect.h" |
---|
23 | #include <X11/keysym.h> |
---|
24 | #include <X11/keysymdef.h> |
---|
25 | |
---|
26 | #define START True |
---|
27 | #define END False |
---|
28 | |
---|
29 | #define offset(field) XjOffset(TextDisplayJet,field) |
---|
30 | |
---|
31 | static XjResource resources[] = { |
---|
32 | { XjNx, XjCX, XjRInt, sizeof(int), |
---|
33 | offset(core.x), XjRString, XjInheritValue }, |
---|
34 | { XjNy, XjCY, XjRInt, sizeof(int), |
---|
35 | offset(core.y), XjRString, XjInheritValue }, |
---|
36 | { XjNwidth, XjCWidth, XjRInt, sizeof(int), |
---|
37 | offset(core.width), XjRString, XjInheritValue }, |
---|
38 | { XjNheight, XjCHeight, XjRInt, sizeof(int), |
---|
39 | offset(core.height), XjRString, XjInheritValue }, |
---|
40 | { XjNdisplayWidth, XjCDisplayWidth, XjRInt, sizeof(int), |
---|
41 | offset(textDisplay.displayWidth), XjRString, "80" }, |
---|
42 | { XjNdisplayHeight, XjCDisplayHeight, XjRInt, sizeof(int), |
---|
43 | offset(textDisplay.displayHeight), XjRString, "5" }, |
---|
44 | { XjNtext, XjCText, XjRString, sizeof(char *), |
---|
45 | offset(textDisplay.text), XjRString,""}, |
---|
46 | { XjNforeground, XjCForeground, XjRColor, sizeof(int), |
---|
47 | offset(textDisplay.foreground), XjRString, XjDefaultForeground }, |
---|
48 | { XjNbackground, XjCBackground, XjRColor, sizeof(int), |
---|
49 | offset(textDisplay.background), XjRString, XjDefaultBackground }, |
---|
50 | { XjNhighlightForeground, XjCForeground, XjRString, sizeof(char*), |
---|
51 | offset(textDisplay.hl_fg_name), XjRString, "" }, |
---|
52 | { XjNhighlightBackground, XjCBackground, XjRString, sizeof(char*), |
---|
53 | offset(textDisplay.hl_bg_name), XjRString, "" }, |
---|
54 | { XjNreverseVideo, XjCReverseVideo, XjRBoolean, sizeof(Boolean), |
---|
55 | offset(textDisplay.reverseVideo), XjRBoolean, (caddr_t)False }, |
---|
56 | { XjNfont, XjCFont, XjRFontStruct, sizeof(XFontStruct *), |
---|
57 | offset(textDisplay.font), XjRString, XjDefaultFont }, |
---|
58 | { XjNresizeProc, XjCResizeProc, XjRCallback, sizeof(XjCallback *), |
---|
59 | offset(textDisplay.resizeProc), XjRString, NULL }, |
---|
60 | { XjNscrollProc, XjCScrollProc, XjRCallback, sizeof(XjCallback *), |
---|
61 | offset(textDisplay.scrollProc), XjRString, NULL }, |
---|
62 | { XjNinternalBorder, XjCBorderWidth, XjRInt, sizeof(int), |
---|
63 | offset(textDisplay.internalBorder), XjRString, "2" }, |
---|
64 | { XjNmultiClickTime, XjCMultiClickTime, XjRInt, sizeof(int), |
---|
65 | offset(textDisplay.multiClickTime), XjRString, "250" }, |
---|
66 | { XjNcharClass, XjCCharClass, XjRString, sizeof(char *), |
---|
67 | offset(textDisplay.charClass), XjRString, (caddr_t) NULL}, |
---|
68 | { XjNscrollDelay, XjCScrollDelay, XjRInt, sizeof(int), |
---|
69 | offset(textDisplay.scrollDelay1), XjRString, "100" }, |
---|
70 | { XjNscrollDelay2, XjCScrollDelay, XjRInt, sizeof(int), |
---|
71 | offset(textDisplay.scrollDelay2), XjRString, "50" }, |
---|
72 | }; |
---|
73 | |
---|
74 | #undef offset |
---|
75 | |
---|
76 | static void initialize(), expose(), realize(), querySize(), move(), |
---|
77 | destroy(), resize(), appendLines(), drawChars(); |
---|
78 | static Boolean event_handler(); |
---|
79 | |
---|
80 | TextDisplayClassRec textDisplayClassRec = { |
---|
81 | { |
---|
82 | /* class name */ "TextDisplay", |
---|
83 | /* jet size */ sizeof(TextDisplayRec), |
---|
84 | /* classInitialize */ NULL, |
---|
85 | /* classInitialized? */ 1, |
---|
86 | /* initialize */ initialize, |
---|
87 | /* prerealize */ NULL, |
---|
88 | /* realize */ realize, |
---|
89 | /* event */ event_handler, |
---|
90 | /* expose */ expose, |
---|
91 | /* querySize */ querySize, |
---|
92 | /* move */ move, |
---|
93 | /* resize */ resize, |
---|
94 | /* destroy */ destroy, |
---|
95 | /* resources */ resources, |
---|
96 | /* number of 'em */ XjNumber(resources) |
---|
97 | } |
---|
98 | }; |
---|
99 | |
---|
100 | JetClass textDisplayJetClass = (JetClass)&textDisplayClassRec; |
---|
101 | |
---|
102 | /* The "charClass" table below, and the functions */ |
---|
103 | /* "SetCharacterClassRange" and "set_character_class" following it */ |
---|
104 | /* are taken from the xterm sources, and as such are subject to the */ |
---|
105 | /* copyright included here...: */ |
---|
106 | |
---|
107 | /* |
---|
108 | * Copyright 1988 Massachusetts Institute of Technology |
---|
109 | * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. |
---|
110 | * |
---|
111 | * All Rights Reserved |
---|
112 | * |
---|
113 | * Permission to use, copy, modify, and distribute this software and its |
---|
114 | * documentation for any purpose and without fee is hereby granted, |
---|
115 | * provided that the above copyright notice appear in all copies and that |
---|
116 | * both that copyright notice and this permission notice appear in |
---|
117 | * supporting documentation, and that the name of Digital Equipment |
---|
118 | * Corporation not be used in advertising or publicity pertaining to |
---|
119 | * distribution of the software without specific, written prior permission. |
---|
120 | * |
---|
121 | * |
---|
122 | * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
---|
123 | * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
---|
124 | * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
---|
125 | * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
---|
126 | * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
---|
127 | * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
---|
128 | * SOFTWARE. |
---|
129 | */ |
---|
130 | |
---|
131 | /* |
---|
132 | ** double click table for cut and paste in 8 bits |
---|
133 | ** |
---|
134 | ** This table is divided in four parts : |
---|
135 | ** |
---|
136 | ** - control characters [0,0x1f] U [0x80,0x9f] |
---|
137 | ** - separators [0x20,0x3f] U [0xa0,0xb9] |
---|
138 | ** - binding characters [0x40,0x7f] U [0xc0,0xff] |
---|
139 | ** - execeptions |
---|
140 | */ |
---|
141 | static int charClass[256] = { |
---|
142 | /* NUL SOH STX ETX EOT ENQ ACK BEL */ |
---|
143 | 32, 1, 1, 1, 1, 1, 1, 1, |
---|
144 | /* BS HT NL VT NP CR SO SI */ |
---|
145 | 1, 32, 1, 1, 1, 1, 1, 1, |
---|
146 | /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ |
---|
147 | 1, 1, 1, 1, 1, 1, 1, 1, |
---|
148 | /* CAN EM SUB ESC FS GS RS US */ |
---|
149 | 1, 1, 1, 1, 1, 1, 1, 1, |
---|
150 | /* SP ! " # $ % & ' */ |
---|
151 | 32, 33, 34, 35, 36, 37, 38, 39, |
---|
152 | /* ( ) * + , - . / */ |
---|
153 | 40, 41, 42, 43, 44, 45, 46, 47, |
---|
154 | /* 0 1 2 3 4 5 6 7 */ |
---|
155 | 48, 48, 48, 48, 48, 48, 48, 48, |
---|
156 | /* 8 9 : ; < = > ? */ |
---|
157 | 48, 48, 58, 59, 60, 61, 62, 63, |
---|
158 | /* @ A B C D E F G */ |
---|
159 | 64, 48, 48, 48, 48, 48, 48, 48, |
---|
160 | /* H I J K L M N O */ |
---|
161 | 48, 48, 48, 48, 48, 48, 48, 48, |
---|
162 | /* P Q R S T U V W */ |
---|
163 | 48, 48, 48, 48, 48, 48, 48, 48, |
---|
164 | /* X Y Z [ \ ] ^ _ */ |
---|
165 | 48, 48, 48, 91, 92, 93, 94, 48, |
---|
166 | /* ` a b c d e f g */ |
---|
167 | 96, 48, 48, 48, 48, 48, 48, 48, |
---|
168 | /* h i j k l m n o */ |
---|
169 | 48, 48, 48, 48, 48, 48, 48, 48, |
---|
170 | /* p q r s t u v w */ |
---|
171 | 48, 48, 48, 48, 48, 48, 48, 48, |
---|
172 | /* x y z { | } ~ DEL */ |
---|
173 | 48, 48, 48, 123, 124, 125, 126, 1, |
---|
174 | /* x80 x81 x82 x83 IND NEL SSA ESA */ |
---|
175 | 1, 1, 1, 1, 1, 1, 1, 1, |
---|
176 | /* HTS HTJ VTS PLD PLU RI SS2 SS3 */ |
---|
177 | 1, 1, 1, 1, 1, 1, 1, 1, |
---|
178 | /* DCS PU1 PU2 STS CCH MW SPA EPA */ |
---|
179 | 1, 1, 1, 1, 1, 1, 1, 1, |
---|
180 | /* x98 x99 x9A CSI ST OSC PM APC */ |
---|
181 | 1, 1, 1, 1, 1, 1, 1, 1, |
---|
182 | /* - i c/ L ox Y- | So */ |
---|
183 | 160, 161, 162, 163, 164, 165, 166, 167, |
---|
184 | /* .. c0 ip << _ R0 - */ |
---|
185 | 168, 169, 170, 171, 172, 173, 174, 175, |
---|
186 | /* o +- 2 3 ' u q| . */ |
---|
187 | 176, 177, 178, 179, 180, 181, 182, 183, |
---|
188 | /* , 1 2 >> 1/4 1/2 3/4 ? */ |
---|
189 | 184, 185, 186, 187, 188, 189, 190, 191, |
---|
190 | /* A` A' A^ A~ A: Ao AE C, */ |
---|
191 | 48, 48, 48, 48, 48, 48, 48, 48, |
---|
192 | /* E` E' E^ E: I` I' I^ I: */ |
---|
193 | 48, 48, 48, 48, 48, 48, 48, 48, |
---|
194 | /* D- N~ O` O' O^ O~ O: X */ |
---|
195 | 48, 48, 48, 48, 48, 48, 48, 216, |
---|
196 | /* O/ U` U' U^ U: Y' P B */ |
---|
197 | 48, 48, 48, 48, 48, 48, 48, 48, |
---|
198 | /* a` a' a^ a~ a: ao ae c, */ |
---|
199 | 48, 48, 48, 48, 48, 48, 48, 48, |
---|
200 | /* e` e' e^ e: i` i' i^ i: */ |
---|
201 | 48, 48, 48, 48, 48, 48, 48, 48, |
---|
202 | /* d n~ o` o' o^ o~ o: -: */ |
---|
203 | 48, 48, 48, 48, 48, 48, 48, 248, |
---|
204 | /* o/ u` u' u^ u: y' P y: */ |
---|
205 | 48, 48, 48, 48, 48, 48, 48, 48}; |
---|
206 | |
---|
207 | int SetCharacterClassRange (low, high, value) |
---|
208 | register int low, high; /* in range of [0..255] */ |
---|
209 | register int value; /* arbitrary */ |
---|
210 | { |
---|
211 | |
---|
212 | if (low < 0 || high > 255 || high < low) return (-1); |
---|
213 | |
---|
214 | for (; low <= high; low++) charClass[low] = value; |
---|
215 | |
---|
216 | return (0); |
---|
217 | } |
---|
218 | |
---|
219 | |
---|
220 | /* |
---|
221 | * set_character_class - takes a string of the form |
---|
222 | * |
---|
223 | * low[-high]:val[,low[-high]:val[...]] |
---|
224 | * |
---|
225 | * and sets the indicated ranges to the indicated values. |
---|
226 | */ |
---|
227 | |
---|
228 | int set_character_class (s) |
---|
229 | register char *s; |
---|
230 | { |
---|
231 | register int i; /* iterator, index into s */ |
---|
232 | int len; /* length of s */ |
---|
233 | int acc; /* accumulator */ |
---|
234 | int low, high; /* bounds of range [0..127] */ |
---|
235 | int base; /* 8, 10, 16 (octal, decimal, hex) */ |
---|
236 | int numbers; /* count of numbers per range */ |
---|
237 | int digits; /* count of digits in a number */ |
---|
238 | static char *errfmt = "%s in range string \"%s\" (position %d)\n"; |
---|
239 | char errtext[100]; |
---|
240 | |
---|
241 | if (!s || !s[0]) return -1; |
---|
242 | |
---|
243 | base = 10; /* in case we ever add octal, hex */ |
---|
244 | low = high = -1; /* out of range */ |
---|
245 | |
---|
246 | for (i = 0, len = strlen (s), acc = 0, numbers = digits = 0; |
---|
247 | i < len; i++) { |
---|
248 | char c = s[i]; |
---|
249 | |
---|
250 | if (isspace(c)) { |
---|
251 | continue; |
---|
252 | } else if (isdigit(c)) { |
---|
253 | acc = acc * base + (c - '0'); |
---|
254 | digits++; |
---|
255 | continue; |
---|
256 | } else if (c == '-') { |
---|
257 | low = acc; |
---|
258 | acc = 0; |
---|
259 | if (digits == 0) { |
---|
260 | sprintf (errtext, errfmt, "missing number", s, i); |
---|
261 | XjWarning(errtext); |
---|
262 | return (-1); |
---|
263 | } |
---|
264 | digits = 0; |
---|
265 | numbers++; |
---|
266 | continue; |
---|
267 | } else if (c == ':') { |
---|
268 | if (numbers == 0) |
---|
269 | low = acc; |
---|
270 | else if (numbers == 1) |
---|
271 | high = acc; |
---|
272 | else { |
---|
273 | sprintf (errtext, errfmt, "too many numbers", s, i); |
---|
274 | XjWarning(errtext); |
---|
275 | return (-1); |
---|
276 | } |
---|
277 | digits = 0; |
---|
278 | numbers++; |
---|
279 | acc = 0; |
---|
280 | continue; |
---|
281 | } else if (c == ',') { |
---|
282 | /* |
---|
283 | * now, process it |
---|
284 | */ |
---|
285 | |
---|
286 | if (high < 0) { |
---|
287 | high = low; |
---|
288 | numbers++; |
---|
289 | } |
---|
290 | if (numbers != 2) { |
---|
291 | sprintf (errtext, errfmt, "bad value number", s, i); |
---|
292 | XjWarning(errtext); |
---|
293 | } else if (SetCharacterClassRange (low, high, acc) != 0) { |
---|
294 | sprintf (errtext, errfmt, "bad range", s, i); |
---|
295 | XjWarning(errtext); |
---|
296 | } |
---|
297 | |
---|
298 | low = high = -1; |
---|
299 | acc = 0; |
---|
300 | digits = 0; |
---|
301 | numbers = 0; |
---|
302 | continue; |
---|
303 | } else { |
---|
304 | sprintf (errtext, errfmt, "bad character", s, i); |
---|
305 | XjWarning(errtext); |
---|
306 | return (-1); |
---|
307 | } /* end if else if ... else */ |
---|
308 | |
---|
309 | } |
---|
310 | |
---|
311 | if (low < 0 && high < 0) return (0); |
---|
312 | |
---|
313 | /* |
---|
314 | * now, process it |
---|
315 | */ |
---|
316 | |
---|
317 | if (high < 0) high = low; |
---|
318 | if (numbers < 1 || numbers > 2) { |
---|
319 | sprintf (errtext, errfmt, "bad value number", s, i); |
---|
320 | XjWarning(errtext); |
---|
321 | } else if (SetCharacterClassRange (low, high, acc) != 0) { |
---|
322 | sprintf (errtext, errfmt, "bad range", s, i); |
---|
323 | XjWarning(errtext); |
---|
324 | } |
---|
325 | |
---|
326 | return (0); |
---|
327 | } |
---|
328 | /* The "charClass" table above, and the functions */ |
---|
329 | /* "SetCharacterClassRange" and "set_character_class" following it */ |
---|
330 | /* are taken from the xterm sources, and as such are subject to the */ |
---|
331 | /* copyright included above. */ |
---|
332 | |
---|
333 | |
---|
334 | |
---|
335 | |
---|
336 | static void initialize(me) |
---|
337 | TextDisplayJet me; |
---|
338 | { |
---|
339 | me->textDisplay.realized = 0; |
---|
340 | |
---|
341 | me->textDisplay.charWidth = me->textDisplay.font->max_bounds.width; |
---|
342 | me->textDisplay.charHeight = (me->textDisplay.font->ascent |
---|
343 | + me->textDisplay.font->descent); |
---|
344 | |
---|
345 | me->textDisplay.lineStartsSize = 1000; |
---|
346 | me->textDisplay.lineStarts = (char **)XjMalloc(1000 * sizeof(char *)); |
---|
347 | me->textDisplay.startSelect = 0; |
---|
348 | me->textDisplay.endSelect = 0; |
---|
349 | me->textDisplay.realStart = 0; |
---|
350 | me->textDisplay.realEnd = 0; |
---|
351 | me->textDisplay.selection = NULL; |
---|
352 | me->textDisplay.clickTimes = 0; |
---|
353 | me->textDisplay.buttonDown = False; |
---|
354 | |
---|
355 | /* |
---|
356 | * Initialize the first lines for safety... trust me... |
---|
357 | * (it's in case there's no text in the jet and someone tries to do a |
---|
358 | * multi-click to select some text... it also keeps "appendLines" |
---|
359 | * from breaking...) |
---|
360 | */ |
---|
361 | me->textDisplay.lineStarts[0] = ""; |
---|
362 | |
---|
363 | set_character_class(me->textDisplay.charClass); |
---|
364 | xselInitAtoms(me->core.display); |
---|
365 | } |
---|
366 | |
---|
367 | /* |
---|
368 | * Things are currently broken screenwise. |
---|
369 | * It will be fun to fix later. :) |
---|
370 | */ |
---|
371 | static void realize(me) |
---|
372 | TextDisplayJet me; |
---|
373 | { |
---|
374 | unsigned long valuemask, valuemask2; |
---|
375 | unsigned long pixel; |
---|
376 | XGCValues values; |
---|
377 | |
---|
378 | if (me->textDisplay.reverseVideo) |
---|
379 | { |
---|
380 | int swap = me->textDisplay.foreground; |
---|
381 | me->textDisplay.foreground = me->textDisplay.background; |
---|
382 | me->textDisplay.background = swap; |
---|
383 | } |
---|
384 | |
---|
385 | if (strcmp(me->textDisplay.hl_fg_name, "") |
---|
386 | && !StrToXPixel(XjDisplay(me), me->textDisplay.hl_fg_name, &pixel)) |
---|
387 | me->textDisplay.hl_foreground = pixel; |
---|
388 | else |
---|
389 | me->textDisplay.hl_foreground = me->textDisplay.background; |
---|
390 | |
---|
391 | if (strcmp(me->textDisplay.hl_bg_name, "") |
---|
392 | && !StrToXPixel(XjDisplay(me), me->textDisplay.hl_bg_name, &pixel)) |
---|
393 | me->textDisplay.hl_background = pixel; |
---|
394 | else |
---|
395 | me->textDisplay.hl_background = me->textDisplay.foreground; |
---|
396 | |
---|
397 | values.function = GXcopy; |
---|
398 | values.font = me->textDisplay.font->fid; |
---|
399 | values.foreground = me->textDisplay.foreground; |
---|
400 | values.background = me->textDisplay.background; |
---|
401 | valuemask = GCForeground | GCBackground | GCFont | GCFunction; |
---|
402 | me->textDisplay.gc = XjCreateGC(me->core.display, |
---|
403 | me->core.window, |
---|
404 | valuemask, |
---|
405 | &values); |
---|
406 | |
---|
407 | values.foreground = values.background; |
---|
408 | valuemask2 = GCForeground | GCFunction; |
---|
409 | me->textDisplay.gc_clear = XjCreateGC(me->core.display, |
---|
410 | me->core.window, |
---|
411 | valuemask2, |
---|
412 | &values); |
---|
413 | |
---|
414 | |
---|
415 | values.foreground = ((me->textDisplay.foreground |
---|
416 | == me->textDisplay.hl_foreground) |
---|
417 | ? me->textDisplay.background |
---|
418 | : me->textDisplay.hl_foreground); |
---|
419 | values.background = ((me->textDisplay.background |
---|
420 | == me->textDisplay.hl_background) |
---|
421 | ? me->textDisplay.foreground |
---|
422 | : me->textDisplay.hl_background); |
---|
423 | valuemask = GCForeground | GCBackground | GCFont | GCFunction; |
---|
424 | me->textDisplay.selectgc = XjCreateGC(me->core.display, |
---|
425 | me->core.window, |
---|
426 | valuemask, |
---|
427 | &values); |
---|
428 | |
---|
429 | values.foreground = values.background; |
---|
430 | valuemask2 = GCForeground | GCFunction; |
---|
431 | me->textDisplay.gc_fill = XjCreateGC(me->core.display, |
---|
432 | me->core.window, |
---|
433 | valuemask2, |
---|
434 | &values); |
---|
435 | |
---|
436 | me->textDisplay.visLines = (me->core.height - |
---|
437 | 2 * me->textDisplay.internalBorder) / |
---|
438 | me->textDisplay.charHeight; |
---|
439 | |
---|
440 | /* |
---|
441 | * Usurp events for this window |
---|
442 | */ |
---|
443 | XjRegisterWindow(me->core.window, (Jet) me); |
---|
444 | XjSelectInput(me->core.display, me->core.window, |
---|
445 | ButtonPressMask | ButtonReleaseMask |
---|
446 | | ButtonMotionMask | KeyPressMask ); |
---|
447 | |
---|
448 | me->textDisplay.realized = 1; |
---|
449 | } |
---|
450 | |
---|
451 | static void destroy(me) |
---|
452 | TextDisplayJet me; |
---|
453 | { |
---|
454 | XjFreeGC(me->core.display, me->textDisplay.gc); |
---|
455 | XjFreeGC(me->core.display, me->textDisplay.selectgc); |
---|
456 | XjFreeGC(me->core.display, me->textDisplay.gc_fill); |
---|
457 | XjFreeGC(me->core.display, me->textDisplay.gc_clear); |
---|
458 | |
---|
459 | XjUnregisterWindow(me->core.window, (Jet) me); |
---|
460 | } |
---|
461 | |
---|
462 | static void querySize(me, size) |
---|
463 | TextDisplayJet me; |
---|
464 | XjSize *size; |
---|
465 | { |
---|
466 | size->width = me->textDisplay.displayWidth * me->textDisplay.charWidth + |
---|
467 | 2 * me->textDisplay.internalBorder; |
---|
468 | size->height = me->textDisplay.displayHeight * me->textDisplay.charHeight + |
---|
469 | 2 * me->textDisplay.internalBorder; |
---|
470 | } |
---|
471 | |
---|
472 | static void move(me, x, y) |
---|
473 | TextDisplayJet me; |
---|
474 | int x, y; |
---|
475 | { |
---|
476 | me->core.x = x; |
---|
477 | me->core.y = y; |
---|
478 | } |
---|
479 | |
---|
480 | static void resize(me, size) |
---|
481 | TextDisplayJet me; |
---|
482 | XjSize *size; |
---|
483 | { |
---|
484 | me->core.width = size->width; |
---|
485 | me->core.height = size->height; |
---|
486 | me->textDisplay.visLines = (me->core.height - |
---|
487 | 2 * me->textDisplay.internalBorder) / |
---|
488 | me->textDisplay.charHeight; |
---|
489 | me->textDisplay.columns = (me->core.width - |
---|
490 | 2 * me->textDisplay.internalBorder) / |
---|
491 | me->textDisplay.charWidth; |
---|
492 | if (me->textDisplay.realized) |
---|
493 | appendLines(me, me->textDisplay.text, 0); |
---|
494 | } |
---|
495 | |
---|
496 | static void drawLine(me, line, y) |
---|
497 | TextDisplayJet me; |
---|
498 | int line, y; |
---|
499 | { |
---|
500 | int c = 0, length; |
---|
501 | char *ptr, *end, *last; |
---|
502 | |
---|
503 | ptr = me->textDisplay.lineStarts[line]; |
---|
504 | last = me->textDisplay.lineStarts[line + 1]; |
---|
505 | |
---|
506 | while (ptr != last) |
---|
507 | { |
---|
508 | end = ptr; |
---|
509 | |
---|
510 | while (end < last && *end != '\t') |
---|
511 | end++; |
---|
512 | |
---|
513 | length = end - ptr; |
---|
514 | |
---|
515 | if (length != 0) |
---|
516 | { |
---|
517 | XDrawString(me->core.display, me->core.window, |
---|
518 | me->textDisplay.gc, |
---|
519 | me->core.x + me->textDisplay.internalBorder + |
---|
520 | c * me->textDisplay.charWidth, |
---|
521 | y + me->textDisplay.font->ascent, |
---|
522 | ptr, length - (ptr[length - 1] == '\n')); |
---|
523 | c += length; |
---|
524 | } |
---|
525 | |
---|
526 | if (end < last && *end == '\t') /* could be reduced, but... */ |
---|
527 | { |
---|
528 | c = 8 * ((c / 8) + 1); |
---|
529 | end++; |
---|
530 | length++; |
---|
531 | while (end < last && *end == '\t') |
---|
532 | { |
---|
533 | end++; |
---|
534 | length++; |
---|
535 | c += 8; |
---|
536 | } |
---|
537 | } |
---|
538 | |
---|
539 | ptr += length; |
---|
540 | } |
---|
541 | } |
---|
542 | |
---|
543 | static void drawText(me, start, num, y) |
---|
544 | TextDisplayJet me; |
---|
545 | int start; |
---|
546 | int num, y; |
---|
547 | { |
---|
548 | char *first, *last; |
---|
549 | |
---|
550 | first = me->textDisplay.lineStarts[start]; |
---|
551 | last = me->textDisplay.lineStarts[MIN(start + num, |
---|
552 | me->textDisplay.numLines)]; |
---|
553 | |
---|
554 | |
---|
555 | if (me->textDisplay.startSelect == me->textDisplay.endSelect || |
---|
556 | last <= me->textDisplay.startSelect || |
---|
557 | first >= me->textDisplay.endSelect) |
---|
558 | { |
---|
559 | while (num != 0 && start < me->textDisplay.numLines) |
---|
560 | { |
---|
561 | drawLine(me, start, y); |
---|
562 | start++; |
---|
563 | num--; |
---|
564 | y += me->textDisplay.charHeight; |
---|
565 | } |
---|
566 | /* |
---|
567 | drawChars(me, me->textDisplay.gc, first, last); |
---|
568 | */ |
---|
569 | return; |
---|
570 | } |
---|
571 | |
---|
572 | /* |
---|
573 | * Since we're here, there must be some selected text drawn. |
---|
574 | */ |
---|
575 | if (first <= me->textDisplay.startSelect) /* last > startSelect */ |
---|
576 | { |
---|
577 | drawChars(me, me->textDisplay.gc, |
---|
578 | first, me->textDisplay.startSelect); |
---|
579 | first = me->textDisplay.startSelect; |
---|
580 | } |
---|
581 | |
---|
582 | drawChars(me, me->textDisplay.selectgc, |
---|
583 | first, MIN(me->textDisplay.endSelect, last)); |
---|
584 | |
---|
585 | if (last > me->textDisplay.endSelect) |
---|
586 | drawChars(me, me->textDisplay.gc, |
---|
587 | me->textDisplay.endSelect, last); |
---|
588 | } |
---|
589 | |
---|
590 | static void drawSome(me, gc, y, startOfLine, start, last) |
---|
591 | TextDisplayJet me; |
---|
592 | GC gc; |
---|
593 | int y; |
---|
594 | char *startOfLine, *start, *last; |
---|
595 | { |
---|
596 | int c = 0, length; |
---|
597 | char *ptr, *end; |
---|
598 | |
---|
599 | /* fprintf(stdout, "%d\n", gc == me->textDisplay.selectgc); */ |
---|
600 | /* |
---|
601 | * At the end of this while, c is the column number to start |
---|
602 | * drawing at, and ptr is start. |
---|
603 | */ |
---|
604 | ptr = startOfLine; |
---|
605 | while (ptr != start) |
---|
606 | { |
---|
607 | end = ptr; |
---|
608 | |
---|
609 | while (end < start && *end != '\t') |
---|
610 | end++; |
---|
611 | |
---|
612 | length = end - ptr; |
---|
613 | c += length; |
---|
614 | |
---|
615 | if (end < start && *end == '\t') /* could be reduced, but... */ |
---|
616 | { |
---|
617 | c = 8 * ((c / 8) + 1); |
---|
618 | end++; |
---|
619 | length++; |
---|
620 | while (end < start && *end == '\t') |
---|
621 | { |
---|
622 | end++; |
---|
623 | length++; |
---|
624 | c += 8; |
---|
625 | } |
---|
626 | } |
---|
627 | |
---|
628 | ptr += length; |
---|
629 | } |
---|
630 | |
---|
631 | while (ptr != last) |
---|
632 | { |
---|
633 | end = ptr; |
---|
634 | |
---|
635 | while (end < last && *end != '\t') |
---|
636 | end++; |
---|
637 | |
---|
638 | length = end - ptr; |
---|
639 | |
---|
640 | if (length != 0) |
---|
641 | { |
---|
642 | XDrawImageString(me->core.display, me->core.window, |
---|
643 | gc, |
---|
644 | me->core.x + me->textDisplay.internalBorder + |
---|
645 | c * me->textDisplay.charWidth, |
---|
646 | y + me->textDisplay.font->ascent, |
---|
647 | ptr, length - (ptr[length - 1] == '\n')); |
---|
648 | /* fprintf(stdout, "%.*s", length - (ptr[length - 1] == '\n'), |
---|
649 | ptr); |
---|
650 | fflush(stdout); */ |
---|
651 | c += length; |
---|
652 | |
---|
653 | /* this is gross... */ |
---|
654 | if (ptr[length-1] == '\n') |
---|
655 | { |
---|
656 | int tmp = (c-1) * me->textDisplay.charWidth + |
---|
657 | me->textDisplay.internalBorder; |
---|
658 | |
---|
659 | XFillRectangle(me->core.display, me->core.window, |
---|
660 | ((gc == me->textDisplay.selectgc) |
---|
661 | ? me->textDisplay.gc_fill |
---|
662 | : me->textDisplay.gc_clear), |
---|
663 | me->core.x + tmp, |
---|
664 | y, |
---|
665 | MAX(0, (me->textDisplay.columns + 1 - c) |
---|
666 | * me->textDisplay.charWidth), |
---|
667 | (me->textDisplay.font->ascent + |
---|
668 | me->textDisplay.font->descent)); |
---|
669 | } |
---|
670 | } |
---|
671 | |
---|
672 | if (end < last && *end == '\t') /* could be reduced, but... */ |
---|
673 | { |
---|
674 | XDrawImageString(me->core.display, me->core.window, |
---|
675 | gc, |
---|
676 | me->core.x + me->textDisplay.internalBorder + |
---|
677 | c * me->textDisplay.charWidth, |
---|
678 | y + me->textDisplay.font->ascent, |
---|
679 | " ", 8 - (c%8)); |
---|
680 | /* fprintf(stdout, "%.*s", 8-(c%8), " "); |
---|
681 | fflush(stdout); */ |
---|
682 | c = 8 * ((c / 8) + 1); |
---|
683 | end++; |
---|
684 | length++; |
---|
685 | while (end < last && *end == '\t') |
---|
686 | { |
---|
687 | XDrawImageString(me->core.display, me->core.window, |
---|
688 | gc, |
---|
689 | me->core.x + me->textDisplay.internalBorder + |
---|
690 | c * me->textDisplay.charWidth, |
---|
691 | y + me->textDisplay.font->ascent, |
---|
692 | " ", 8); |
---|
693 | end++; |
---|
694 | length++; |
---|
695 | c += 8; |
---|
696 | /* fprintf(stdout, " "); |
---|
697 | fflush(stdout); */ |
---|
698 | } |
---|
699 | } |
---|
700 | |
---|
701 | ptr += length; |
---|
702 | } |
---|
703 | } |
---|
704 | |
---|
705 | static void drawChars(me, gc, start, end) |
---|
706 | TextDisplayJet me; |
---|
707 | GC gc; |
---|
708 | char *start, *end; |
---|
709 | { |
---|
710 | int y; |
---|
711 | int line; |
---|
712 | |
---|
713 | for (line = me->textDisplay.topLine; |
---|
714 | line < me->textDisplay.topLine + me->textDisplay.visLines && |
---|
715 | line < me->textDisplay.numLines; |
---|
716 | line++) |
---|
717 | if (me->textDisplay.lineStarts[line + 1] > start) |
---|
718 | break; |
---|
719 | |
---|
720 | if (me->textDisplay.lineStarts[line] > start) |
---|
721 | start = me->textDisplay.lineStarts[line]; |
---|
722 | |
---|
723 | if (start >= end) |
---|
724 | return; |
---|
725 | |
---|
726 | if (line >= me->textDisplay.topLine + me->textDisplay.visLines || |
---|
727 | line >= me->textDisplay.numLines) |
---|
728 | return; |
---|
729 | |
---|
730 | y = me->core.y + me->textDisplay.internalBorder + |
---|
731 | (line - me->textDisplay.topLine) * me->textDisplay.charHeight; |
---|
732 | |
---|
733 | for (;line < me->textDisplay.topLine + me->textDisplay.visLines && |
---|
734 | line < me->textDisplay.numLines; line++) |
---|
735 | { |
---|
736 | drawSome(me, gc, y, me->textDisplay.lineStarts[line], |
---|
737 | start, |
---|
738 | MIN(me->textDisplay.lineStarts[line + 1], end)); |
---|
739 | y += me->textDisplay.charHeight; |
---|
740 | start = me->textDisplay.lineStarts[line + 1]; |
---|
741 | if (start > end) |
---|
742 | break; |
---|
743 | } |
---|
744 | } |
---|
745 | |
---|
746 | static void drawCharacters(me, start, end) |
---|
747 | TextDisplayJet me; |
---|
748 | char *start, *end; |
---|
749 | { |
---|
750 | if (start < me->textDisplay.startSelect) |
---|
751 | { |
---|
752 | if (end <= me->textDisplay.startSelect) |
---|
753 | { |
---|
754 | drawChars(me, me->textDisplay.gc, start, end); |
---|
755 | return; |
---|
756 | } |
---|
757 | |
---|
758 | drawChars(me, me->textDisplay.gc, |
---|
759 | start, |
---|
760 | me->textDisplay.startSelect); |
---|
761 | start = me->textDisplay.startSelect; |
---|
762 | } |
---|
763 | |
---|
764 | if (start < me->textDisplay.endSelect) |
---|
765 | { |
---|
766 | if (end <= me->textDisplay.endSelect) |
---|
767 | { |
---|
768 | drawChars(me, me->textDisplay.selectgc, start, end); |
---|
769 | return; |
---|
770 | } |
---|
771 | |
---|
772 | drawChars(me, me->textDisplay.selectgc, |
---|
773 | start, |
---|
774 | me->textDisplay.endSelect); |
---|
775 | start = me->textDisplay.endSelect; |
---|
776 | } |
---|
777 | |
---|
778 | drawChars(me, me->textDisplay.gc, start, end); |
---|
779 | } |
---|
780 | |
---|
781 | /* |
---|
782 | * This draws only the regions of text whose selected |
---|
783 | * state has changed. |
---|
784 | */ |
---|
785 | static void showSelect(me, selStart, selEnd) |
---|
786 | TextDisplayJet me; |
---|
787 | char *selStart, *selEnd; |
---|
788 | { |
---|
789 | int i, j; |
---|
790 | char *k, *s[4]; |
---|
791 | |
---|
792 | s[0] = selStart; |
---|
793 | s[1] = selEnd; |
---|
794 | s[2] = me->textDisplay.startSelect; |
---|
795 | s[3] = me->textDisplay.endSelect; |
---|
796 | |
---|
797 | /* sort the array into order. */ |
---|
798 | for (i = 0; i < 3; i++) |
---|
799 | for (j = i + 1; j < 4; j++) |
---|
800 | if (s[i] > s[j]) |
---|
801 | { |
---|
802 | k = s[i]; |
---|
803 | s[i] = s[j]; |
---|
804 | s[j] = k; |
---|
805 | } |
---|
806 | |
---|
807 | if (s[0] != s[1]) |
---|
808 | drawCharacters(me, s[0], s[1]); |
---|
809 | |
---|
810 | if (s[2] != s[3]) |
---|
811 | drawCharacters(me, s[2], s[3]); |
---|
812 | } |
---|
813 | |
---|
814 | |
---|
815 | static int timerid = -1; |
---|
816 | |
---|
817 | void auto_scroll(me, id) |
---|
818 | TextDisplayJet me; |
---|
819 | int id; /* ARGSUSED */ |
---|
820 | { |
---|
821 | Window junkwin; |
---|
822 | int x, y, junk; |
---|
823 | unsigned int junkmask; |
---|
824 | XEvent event; |
---|
825 | |
---|
826 | timerid = -1; |
---|
827 | |
---|
828 | XQueryPointer(XjDisplay(me), XjWindow(me), |
---|
829 | &junkwin, &junkwin, &junk, &junk, &x, &y, &junkmask); |
---|
830 | |
---|
831 | event.type = MotionNotify; /* Fake out the event_handler into */ |
---|
832 | event.xbutton.x = x; /* thinking that the mouse has moved. */ |
---|
833 | event.xbutton.y = y; /* It will then deal with selecting and */ |
---|
834 | event_handler(me, &event); /* scrolling more, as needed. */ |
---|
835 | } |
---|
836 | |
---|
837 | |
---|
838 | char *where(me, x, y, line) |
---|
839 | TextDisplayJet me; |
---|
840 | int x, y; |
---|
841 | int *line; |
---|
842 | { |
---|
843 | char *ptr, *end; |
---|
844 | int col, tmp, vert, c = 0; |
---|
845 | int clickTimes = me->textDisplay.clickTimes % 5; |
---|
846 | #define LINE *line |
---|
847 | |
---|
848 | if (me->textDisplay.numLines == 0) |
---|
849 | { |
---|
850 | LINE = 0; |
---|
851 | return me->textDisplay.lineStarts[0]; |
---|
852 | } |
---|
853 | |
---|
854 | vert = (y - me->core.y - me->textDisplay.internalBorder); |
---|
855 | LINE = ((y - me->core.y - me->textDisplay.internalBorder) / |
---|
856 | me->textDisplay.charHeight) + me->textDisplay.topLine; |
---|
857 | col = ((x - me->core.x - me->textDisplay.internalBorder) / |
---|
858 | me->textDisplay.charWidth); |
---|
859 | |
---|
860 | /* |
---|
861 | * Deal with autoscrolling... first check if we want to scroll up... |
---|
862 | */ |
---|
863 | if (vert < -20 && me->textDisplay.topLine > 0 |
---|
864 | && timerid == -1 |
---|
865 | && (me->textDisplay.scrollDelay1 >= 0 || |
---|
866 | me->textDisplay.scrollDelay2 >= 0)) |
---|
867 | { |
---|
868 | SetLine(me, me->textDisplay.topLine - 1); |
---|
869 | XjCallCallbacks(me, me->textDisplay.scrollProc, NULL); |
---|
870 | |
---|
871 | timerid = XjAddWakeup(auto_scroll, me, |
---|
872 | (vert < -40 && |
---|
873 | me->textDisplay.scrollDelay2 >= 0) |
---|
874 | ? me->textDisplay.scrollDelay2 |
---|
875 | : me->textDisplay.scrollDelay1); |
---|
876 | } |
---|
877 | |
---|
878 | /* bounds checking */ |
---|
879 | if (LINE < me->textDisplay.topLine) |
---|
880 | LINE = me->textDisplay.topLine; |
---|
881 | #ifdef notdef |
---|
882 | if (LINE < 0) /* is this really necessary??? */ |
---|
883 | LINE = 0; |
---|
884 | #endif |
---|
885 | if (vert < -40) |
---|
886 | return me->textDisplay.lineStarts[LINE]; |
---|
887 | |
---|
888 | /* |
---|
889 | * ...now check if we're past the last line, and the last line is |
---|
890 | * *not* at the bottom of the jet... if this is the case, then we |
---|
891 | * want to return the end of the text... |
---|
892 | */ |
---|
893 | if (LINE > me->textDisplay.numLines |
---|
894 | && me->textDisplay.visLines > me->textDisplay.numLines) |
---|
895 | { |
---|
896 | LINE = me->textDisplay.numLines; |
---|
897 | return me->textDisplay.lineStarts[me->textDisplay.numLines]; |
---|
898 | } |
---|
899 | |
---|
900 | /* |
---|
901 | * ...then check if we want to scroll down... |
---|
902 | */ |
---|
903 | tmp = me->textDisplay.topLine + me->textDisplay.visLines; |
---|
904 | if (LINE >= tmp) |
---|
905 | LINE = tmp - 1; |
---|
906 | |
---|
907 | if (vert - me->core.height > 20 && tmp < me->textDisplay.numLines |
---|
908 | && timerid == -1 |
---|
909 | && (me->textDisplay.scrollDelay1 >= 0 || |
---|
910 | me->textDisplay.scrollDelay2 >= 0)) |
---|
911 | { |
---|
912 | SetLine(me, me->textDisplay.topLine + 1); /* scroll down... */ |
---|
913 | XjCallCallbacks(me, me->textDisplay.scrollProc, NULL); |
---|
914 | LINE += 1; |
---|
915 | |
---|
916 | timerid = XjAddWakeup(auto_scroll, me, |
---|
917 | (vert - me->core.height > 40 && |
---|
918 | me->textDisplay.scrollDelay2 >= 0) |
---|
919 | ? me->textDisplay.scrollDelay2 |
---|
920 | : me->textDisplay.scrollDelay1); |
---|
921 | } |
---|
922 | |
---|
923 | /* bounds checking */ |
---|
924 | #ifdef notdef |
---|
925 | if (LINE == me->textDisplay.numLines - 1) |
---|
926 | { |
---|
927 | #endif |
---|
928 | if (vert - me->core.height > 40) |
---|
929 | return me->textDisplay.lineStarts[LINE + 1]; |
---|
930 | #ifdef notdef |
---|
931 | } |
---|
932 | #endif |
---|
933 | if (LINE >= me->textDisplay.numLines) |
---|
934 | LINE = me->textDisplay.numLines - 1; |
---|
935 | |
---|
936 | |
---|
937 | ptr = me->textDisplay.lineStarts[LINE]; |
---|
938 | end = me->textDisplay.lineStarts[LINE + 1]; |
---|
939 | |
---|
940 | while (c < col && ptr < end) |
---|
941 | { |
---|
942 | while ((c < col) && *ptr != '\t' && ptr < end) |
---|
943 | { |
---|
944 | ptr++; |
---|
945 | c++; |
---|
946 | } |
---|
947 | |
---|
948 | if ((c < col) && ptr < end && *ptr == '\t') |
---|
949 | { |
---|
950 | c = 8 * ((c / 8) + 1); |
---|
951 | ptr++; |
---|
952 | |
---|
953 | while ((c < col) && *ptr == '\t' && ptr < end) |
---|
954 | { |
---|
955 | c += 8; |
---|
956 | ptr++; |
---|
957 | } |
---|
958 | } |
---|
959 | } |
---|
960 | |
---|
961 | if (clickTimes == 1) |
---|
962 | return ptr; |
---|
963 | |
---|
964 | #ifdef notdef |
---|
965 | if (me->textDisplay.columns <= col) /* to deal with dragging off the */ |
---|
966 | return ptr; /* right edge */ |
---|
967 | #endif |
---|
968 | if (ptr == end) |
---|
969 | ptr = end - 1; |
---|
970 | |
---|
971 | return ptr; |
---|
972 | |
---|
973 | #undef LINE |
---|
974 | } |
---|
975 | |
---|
976 | char *find_boundary(me, ptr, line, find_start) |
---|
977 | TextDisplayJet me; |
---|
978 | char **ptr; |
---|
979 | int line; |
---|
980 | Boolean find_start; |
---|
981 | { |
---|
982 | int clickTimes = me->textDisplay.clickTimes % 5; |
---|
983 | |
---|
984 | if (me->textDisplay.numLines == 0) |
---|
985 | return *ptr; |
---|
986 | |
---|
987 | if (clickTimes == 1) /* single-clicks - select by char */ |
---|
988 | return *ptr; |
---|
989 | |
---|
990 | if (clickTimes == 2) /* double-clicks - select by "word" */ |
---|
991 | { |
---|
992 | char *tmp; |
---|
993 | |
---|
994 | if (find_start) |
---|
995 | { |
---|
996 | tmp = *ptr - 1; |
---|
997 | while(tmp >= me->textDisplay.lineStarts[0] && |
---|
998 | charClass[*tmp] == charClass[**ptr]) |
---|
999 | tmp--; |
---|
1000 | tmp++; |
---|
1001 | } |
---|
1002 | else |
---|
1003 | { |
---|
1004 | tmp = *ptr; |
---|
1005 | while(tmp <= me->textDisplay.lineStarts[me->textDisplay.numLines] && |
---|
1006 | charClass[*tmp] == charClass[**ptr]) |
---|
1007 | tmp++; |
---|
1008 | } |
---|
1009 | return tmp; |
---|
1010 | } |
---|
1011 | |
---|
1012 | if (clickTimes == 3) /* triple-clicks - select by "line" */ |
---|
1013 | return me->textDisplay.lineStarts[(find_start) |
---|
1014 | ? line |
---|
1015 | : MIN(me->textDisplay.numLines, |
---|
1016 | line + 1)]; |
---|
1017 | |
---|
1018 | if (clickTimes == 4) /* quad-clicks - select by "paragraph" */ |
---|
1019 | { |
---|
1020 | char *tmp; |
---|
1021 | int blankline, x; |
---|
1022 | |
---|
1023 | if (find_start) |
---|
1024 | { |
---|
1025 | /* |
---|
1026 | * scan backward to the beginning of the "paragraph" |
---|
1027 | */ |
---|
1028 | for (x = line; |
---|
1029 | x >= 0; |
---|
1030 | x--) |
---|
1031 | { |
---|
1032 | blankline = True; |
---|
1033 | if (x < me->textDisplay.numLines) |
---|
1034 | for (tmp = me->textDisplay.lineStarts[x]; |
---|
1035 | blankline && tmp <= (me->textDisplay.lineStarts[x+1] - 1); |
---|
1036 | tmp++) |
---|
1037 | if (*tmp != ' ' && *tmp != '\t' && |
---|
1038 | *tmp != '\n' && *tmp != '\0') |
---|
1039 | blankline = False; |
---|
1040 | |
---|
1041 | if (blankline) |
---|
1042 | return me->textDisplay.lineStarts[(x == line) ? x : x+1]; |
---|
1043 | } |
---|
1044 | return me->textDisplay.lineStarts[0]; |
---|
1045 | } |
---|
1046 | |
---|
1047 | else |
---|
1048 | { |
---|
1049 | /* |
---|
1050 | * scan forward to the end of the "paragraph" |
---|
1051 | */ |
---|
1052 | for (x = line; |
---|
1053 | x < me->textDisplay.numLines; |
---|
1054 | x++) |
---|
1055 | { |
---|
1056 | blankline = True; |
---|
1057 | for (tmp = me->textDisplay.lineStarts[x]; |
---|
1058 | blankline && tmp <= (me->textDisplay.lineStarts[x+1] - 1); |
---|
1059 | tmp++) |
---|
1060 | if (*tmp != ' ' && *tmp != '\t' && |
---|
1061 | *tmp != '\n' && *tmp != '\0') |
---|
1062 | blankline = False; |
---|
1063 | |
---|
1064 | if (blankline) |
---|
1065 | return me->textDisplay.lineStarts[(x == line) ? x+1 : x]; |
---|
1066 | } |
---|
1067 | return me->textDisplay.lineStarts[x]; |
---|
1068 | } |
---|
1069 | } |
---|
1070 | |
---|
1071 | /* quint-clicks - select all text */ |
---|
1072 | return me->textDisplay.lineStarts[(find_start) |
---|
1073 | ? 0 : me->textDisplay.numLines]; |
---|
1074 | } |
---|
1075 | |
---|
1076 | |
---|
1077 | static void expose(me, event) |
---|
1078 | Jet me; |
---|
1079 | XEvent *event; /* ARGSUSED */ |
---|
1080 | { |
---|
1081 | drawText((TextDisplayJet) me, |
---|
1082 | ((TextDisplayJet) me)->textDisplay.topLine, |
---|
1083 | ((TextDisplayJet) me)->textDisplay.visLines, |
---|
1084 | me->core.y + ((TextDisplayJet) me)->textDisplay.internalBorder); |
---|
1085 | } |
---|
1086 | |
---|
1087 | static void appendLines(me, text, num) |
---|
1088 | TextDisplayJet me; |
---|
1089 | char *text; |
---|
1090 | int num; |
---|
1091 | { |
---|
1092 | int c, col; |
---|
1093 | char *top = me->textDisplay.lineStarts[me->textDisplay.topLine]; |
---|
1094 | |
---|
1095 | if (me->textDisplay.columns < 1 || |
---|
1096 | text == NULL) |
---|
1097 | return; |
---|
1098 | |
---|
1099 | while (*text != '\0') |
---|
1100 | { |
---|
1101 | /* The +2 is a useful hack. */ |
---|
1102 | if (num + 2 > me->textDisplay.lineStartsSize) |
---|
1103 | { |
---|
1104 | me->textDisplay.lineStarts = |
---|
1105 | (char **)XjRealloc(me->textDisplay.lineStarts, |
---|
1106 | (1000 + me->textDisplay.lineStartsSize) |
---|
1107 | * sizeof(char *)); |
---|
1108 | me->textDisplay.lineStartsSize += 1000; |
---|
1109 | } |
---|
1110 | |
---|
1111 | me->textDisplay.lineStarts[num] = text; |
---|
1112 | if (text == top) |
---|
1113 | me->textDisplay.topLine = num; |
---|
1114 | |
---|
1115 | col = 0; |
---|
1116 | for (c = 0; col < me->textDisplay.columns; c++) |
---|
1117 | switch(text[c]) |
---|
1118 | { |
---|
1119 | case '\0': |
---|
1120 | c--; |
---|
1121 | case '\n': |
---|
1122 | col = me->textDisplay.columns; |
---|
1123 | break; |
---|
1124 | case '\t': |
---|
1125 | col = 8 * ((col / 8) + 1); |
---|
1126 | break; |
---|
1127 | default: |
---|
1128 | col++; |
---|
1129 | break; |
---|
1130 | } |
---|
1131 | text += c; |
---|
1132 | if (/* text > me->textDisplay.text && not necessary */ |
---|
1133 | text[0] == '\n' && text[-1] != '\n') /* then wrapped */ |
---|
1134 | text++; /* stupid to <cr> here */ |
---|
1135 | |
---|
1136 | num++; |
---|
1137 | } |
---|
1138 | |
---|
1139 | me->textDisplay.lineStarts[num] = text; |
---|
1140 | me->textDisplay.numLines = num; |
---|
1141 | if (text == top) |
---|
1142 | me->textDisplay.topLine = num; |
---|
1143 | |
---|
1144 | if (me->textDisplay.realized) |
---|
1145 | XjCallCallbacks(me, me->textDisplay.resizeProc, NULL); |
---|
1146 | } |
---|
1147 | |
---|
1148 | void showNewLines(me, start) |
---|
1149 | TextDisplayJet me; |
---|
1150 | int start; |
---|
1151 | { |
---|
1152 | if (start >= me->textDisplay.topLine && |
---|
1153 | start <= (me->textDisplay.topLine + me->textDisplay.visLines - 1)) |
---|
1154 | drawText(me, |
---|
1155 | start, |
---|
1156 | me->textDisplay.topLine + me->textDisplay.visLines - start, |
---|
1157 | me->core.y + me->textDisplay.internalBorder + |
---|
1158 | (start - me->textDisplay.topLine) * |
---|
1159 | me->textDisplay.charHeight); |
---|
1160 | } |
---|
1161 | |
---|
1162 | void AddText(me) |
---|
1163 | TextDisplayJet me; |
---|
1164 | { |
---|
1165 | int l; |
---|
1166 | |
---|
1167 | l = me->textDisplay.numLines; |
---|
1168 | if (l > 0) l--; |
---|
1169 | appendLines(me, me->textDisplay.lineStarts[l], l); |
---|
1170 | if (me->textDisplay.realized) |
---|
1171 | showNewLines(me, l); |
---|
1172 | } |
---|
1173 | |
---|
1174 | void MoveText(me, text, offset) |
---|
1175 | TextDisplayJet me; |
---|
1176 | char *text; |
---|
1177 | int offset; |
---|
1178 | { |
---|
1179 | me->textDisplay.startSelect = (char *) |
---|
1180 | MAX((int) text, (int) me->textDisplay.startSelect - offset); |
---|
1181 | me->textDisplay.endSelect = (char *) |
---|
1182 | MAX((int) text, (int) me->textDisplay.endSelect - offset); |
---|
1183 | me->textDisplay.realStart = (char *) |
---|
1184 | MAX((int) text, (int) me->textDisplay.realStart - offset); |
---|
1185 | me->textDisplay.realEnd = (char *) |
---|
1186 | MAX((int) text, (int) me->textDisplay.realEnd - offset); |
---|
1187 | |
---|
1188 | me->textDisplay.text = text; |
---|
1189 | me->textDisplay.topLine = 0; |
---|
1190 | |
---|
1191 | appendLines(me, text, 0); |
---|
1192 | if (me->textDisplay.realized) |
---|
1193 | XClearArea(me->core.display, me->core.window, |
---|
1194 | me->core.x, me->core.y, |
---|
1195 | me->core.width, me->core.height, True); |
---|
1196 | } |
---|
1197 | |
---|
1198 | void SetText(me, text) |
---|
1199 | TextDisplayJet me; |
---|
1200 | char *text; |
---|
1201 | { |
---|
1202 | me->textDisplay.startSelect = text; |
---|
1203 | me->textDisplay.endSelect = text; |
---|
1204 | me->textDisplay.realStart = text; |
---|
1205 | me->textDisplay.realEnd = text; |
---|
1206 | |
---|
1207 | MoveText(me, text, 0); |
---|
1208 | } |
---|
1209 | |
---|
1210 | int TopLine(me) |
---|
1211 | TextDisplayJet me; |
---|
1212 | { |
---|
1213 | return me->textDisplay.topLine; |
---|
1214 | } |
---|
1215 | |
---|
1216 | int VisibleLines(me) |
---|
1217 | TextDisplayJet me; |
---|
1218 | { |
---|
1219 | return me->textDisplay.visLines; |
---|
1220 | } |
---|
1221 | |
---|
1222 | int CountLines(me) |
---|
1223 | TextDisplayJet me; |
---|
1224 | { |
---|
1225 | return me->textDisplay.numLines; |
---|
1226 | } |
---|
1227 | |
---|
1228 | #define abs(x) (((x) < 0) ? (-(x)) : (x)) |
---|
1229 | |
---|
1230 | void SetLine(me, value) |
---|
1231 | TextDisplayJet me; |
---|
1232 | int value; |
---|
1233 | { |
---|
1234 | int diff, charHeight; |
---|
1235 | |
---|
1236 | if (value == me->textDisplay.topLine) |
---|
1237 | return; |
---|
1238 | |
---|
1239 | charHeight = me->textDisplay.charHeight; |
---|
1240 | |
---|
1241 | diff = value - me->textDisplay.topLine; |
---|
1242 | if (diff > 0 && |
---|
1243 | diff < me->textDisplay.visLines) |
---|
1244 | { /* scroll */ |
---|
1245 | XCopyArea(me->core.display, |
---|
1246 | me->core.window, |
---|
1247 | me->core.window, |
---|
1248 | me->textDisplay.gc, |
---|
1249 | me->core.x, |
---|
1250 | me->core.y + me->textDisplay.internalBorder + |
---|
1251 | diff * charHeight, |
---|
1252 | me->core.width, |
---|
1253 | (me->textDisplay.visLines - diff) * charHeight, |
---|
1254 | me->core.x, |
---|
1255 | me->core.y + me->textDisplay.internalBorder); |
---|
1256 | |
---|
1257 | XClearArea(me->core.display, |
---|
1258 | me->core.window, |
---|
1259 | me->core.x, |
---|
1260 | me->core.y + me->textDisplay.internalBorder + |
---|
1261 | (me->textDisplay.visLines - diff) * charHeight, |
---|
1262 | me->core.width, |
---|
1263 | diff * charHeight, |
---|
1264 | False); |
---|
1265 | |
---|
1266 | me->textDisplay.topLine = value; |
---|
1267 | |
---|
1268 | drawText(me, |
---|
1269 | value + me->textDisplay.visLines - diff, |
---|
1270 | diff, |
---|
1271 | me->core.y + me->textDisplay.internalBorder + |
---|
1272 | (me->textDisplay.visLines - diff) * charHeight); |
---|
1273 | } |
---|
1274 | else |
---|
1275 | if (diff < 0 && |
---|
1276 | (-diff) < me->textDisplay.visLines) |
---|
1277 | { |
---|
1278 | /* scroll */ |
---|
1279 | diff = -diff; |
---|
1280 | |
---|
1281 | XCopyArea(me->core.display, |
---|
1282 | me->core.window, |
---|
1283 | me->core.window, |
---|
1284 | me->textDisplay.gc, |
---|
1285 | me->core.x, |
---|
1286 | me->core.y + me->textDisplay.internalBorder, |
---|
1287 | me->core.width, |
---|
1288 | (me->textDisplay.visLines - diff) * charHeight, |
---|
1289 | me->core.x, |
---|
1290 | me->core.y + me->textDisplay.internalBorder + |
---|
1291 | diff * charHeight); |
---|
1292 | |
---|
1293 | XClearArea(me->core.display, |
---|
1294 | me->core.window, |
---|
1295 | me->core.x, |
---|
1296 | me->core.y + me->textDisplay.internalBorder, |
---|
1297 | me->core.width, |
---|
1298 | diff * charHeight, |
---|
1299 | False); |
---|
1300 | |
---|
1301 | me->textDisplay.topLine = value; |
---|
1302 | |
---|
1303 | drawText(me, |
---|
1304 | me->textDisplay.topLine, |
---|
1305 | diff, |
---|
1306 | me->core.y + me->textDisplay.internalBorder); |
---|
1307 | } |
---|
1308 | else |
---|
1309 | { /* completely new screen... */ |
---|
1310 | XClearArea(me->core.display, |
---|
1311 | me->core.window, |
---|
1312 | me->core.x, |
---|
1313 | me->core.y, |
---|
1314 | me->core.width, |
---|
1315 | me->core.height, |
---|
1316 | False); |
---|
1317 | |
---|
1318 | me->textDisplay.topLine = value; |
---|
1319 | |
---|
1320 | drawText(me, |
---|
1321 | me->textDisplay.topLine, |
---|
1322 | me->textDisplay.visLines, |
---|
1323 | me->core.y + me->textDisplay.internalBorder); |
---|
1324 | } |
---|
1325 | } |
---|
1326 | |
---|
1327 | static Boolean event_handler(me, event) |
---|
1328 | TextDisplayJet me; |
---|
1329 | XEvent *event; |
---|
1330 | { |
---|
1331 | char *w; |
---|
1332 | int line; |
---|
1333 | char *oldStart, *oldEnd; |
---|
1334 | int length; |
---|
1335 | |
---|
1336 | oldStart = me->textDisplay.startSelect; |
---|
1337 | oldEnd = me->textDisplay.endSelect; |
---|
1338 | |
---|
1339 | switch(event->type) |
---|
1340 | { |
---|
1341 | case KeyPress: |
---|
1342 | switch (XLookupKeysym(&(event->xkey), 0)) |
---|
1343 | { |
---|
1344 | case XK_Left: |
---|
1345 | case XK_Up: |
---|
1346 | if (me->textDisplay.topLine > 0) |
---|
1347 | SetLine(me, me->textDisplay.topLine - 1); |
---|
1348 | break; |
---|
1349 | |
---|
1350 | case XK_Right: |
---|
1351 | case XK_Down: |
---|
1352 | if (me->textDisplay.topLine + me->textDisplay.visLines |
---|
1353 | < me->textDisplay.numLines) |
---|
1354 | SetLine(me, me->textDisplay.topLine + 1); |
---|
1355 | break; |
---|
1356 | |
---|
1357 | case XK_R9: /* "PgUp" on the Sun kbd. */ |
---|
1358 | case XK_Prior: |
---|
1359 | { |
---|
1360 | int tmp = me->textDisplay.topLine - (me->textDisplay.visLines - 1); |
---|
1361 | SetLine(me, MAX(0, tmp)); |
---|
1362 | } |
---|
1363 | break; |
---|
1364 | |
---|
1365 | case XK_R15: /* "PgDn" on the Sun kbd. */ |
---|
1366 | case XK_Next: |
---|
1367 | if (me->textDisplay.numLines > me->textDisplay.visLines) |
---|
1368 | { |
---|
1369 | int tmp = me->textDisplay.topLine + me->textDisplay.visLines - 1; |
---|
1370 | int tmp2 = me->textDisplay.numLines - me->textDisplay.visLines; |
---|
1371 | SetLine(me, MIN(tmp, tmp2)); |
---|
1372 | } |
---|
1373 | break; |
---|
1374 | |
---|
1375 | case XK_R7: /* "Home" on the Sun kbd. */ |
---|
1376 | case XK_Home: |
---|
1377 | SetLine(me, 0); |
---|
1378 | break; |
---|
1379 | |
---|
1380 | case XK_R13: /* "End" on the Sun kbd. */ |
---|
1381 | case XK_End: |
---|
1382 | if (me->textDisplay.numLines > me->textDisplay.visLines) |
---|
1383 | SetLine(me, me->textDisplay.numLines - me->textDisplay.visLines); |
---|
1384 | break; |
---|
1385 | |
---|
1386 | default: |
---|
1387 | return False; |
---|
1388 | } |
---|
1389 | |
---|
1390 | XjCallCallbacks(me, me->textDisplay.scrollProc, NULL); |
---|
1391 | break; |
---|
1392 | |
---|
1393 | case ButtonRelease: |
---|
1394 | if (me->textDisplay.buttonDown == False) |
---|
1395 | break; |
---|
1396 | me->textDisplay.buttonDown = False; |
---|
1397 | |
---|
1398 | if (event->xbutton.button == Button1) |
---|
1399 | me->textDisplay.lastUpTime = event->xbutton.time; |
---|
1400 | |
---|
1401 | me->textDisplay.realStart = me->textDisplay.startSelect; |
---|
1402 | me->textDisplay.realEnd = me->textDisplay.endSelect; |
---|
1403 | |
---|
1404 | if (me->textDisplay.startSelect == me->textDisplay.endSelect) |
---|
1405 | break; |
---|
1406 | |
---|
1407 | if (xselGetOwnership(me->core.display, /* bug! */ |
---|
1408 | me->core.window, |
---|
1409 | event->xbutton.time)) |
---|
1410 | { |
---|
1411 | length = (int)(me->textDisplay.realEnd - |
---|
1412 | me->textDisplay.realStart); |
---|
1413 | if (me->textDisplay.selection != NULL) |
---|
1414 | XjFree(me->textDisplay.selection); |
---|
1415 | me->textDisplay.selection = (char *)XjMalloc(length + 1); |
---|
1416 | memcpy(me->textDisplay.selection, |
---|
1417 | me->textDisplay.realStart, length); |
---|
1418 | me->textDisplay.selection[length] = '\0'; |
---|
1419 | } |
---|
1420 | break; |
---|
1421 | |
---|
1422 | case ButtonPress: |
---|
1423 | if (me->textDisplay.buttonDown == True) |
---|
1424 | break; |
---|
1425 | |
---|
1426 | /* MUST compute clickTimes before calling "where" below, as the */ |
---|
1427 | /* return values from it depend on the value of clickTime... */ |
---|
1428 | |
---|
1429 | if (event->xbutton.button == Button1) |
---|
1430 | { |
---|
1431 | if (me->textDisplay.clickTimes != 0 |
---|
1432 | && event->xbutton.time - me->textDisplay.lastUpTime |
---|
1433 | < me->textDisplay.multiClickTime) |
---|
1434 | me->textDisplay.clickTimes++; |
---|
1435 | else |
---|
1436 | me->textDisplay.clickTimes = 1; |
---|
1437 | } |
---|
1438 | |
---|
1439 | /* deal with button3 being first one pushed... */ |
---|
1440 | if (event->xbutton.button == Button3 |
---|
1441 | && me->textDisplay.clickTimes == 0) |
---|
1442 | me->textDisplay.clickTimes = 1; |
---|
1443 | |
---|
1444 | me->textDisplay.buttonDown = True; |
---|
1445 | me->textDisplay.whichButton = event->xbutton.button; |
---|
1446 | w = where(me, event->xbutton.x, event->xbutton.y, &line); |
---|
1447 | |
---|
1448 | switch(me->textDisplay.whichButton) |
---|
1449 | { |
---|
1450 | case Button1: |
---|
1451 | me->textDisplay.startSelect = me->textDisplay.startPivot = |
---|
1452 | find_boundary(me, &w, line, START); |
---|
1453 | me->textDisplay.endSelect = me->textDisplay.endPivot = |
---|
1454 | find_boundary(me, &w, line, END); |
---|
1455 | break; |
---|
1456 | |
---|
1457 | case Button3: |
---|
1458 | if ((int)w < |
---|
1459 | ((int)me->textDisplay.realStart + |
---|
1460 | (int)me->textDisplay.realEnd) |
---|
1461 | / 2) |
---|
1462 | { |
---|
1463 | me->textDisplay.startEnd = 0; |
---|
1464 | me->textDisplay.startSelect = find_boundary(me, &w, line, START); |
---|
1465 | me->textDisplay.endSelect = me->textDisplay.realEnd; |
---|
1466 | } |
---|
1467 | else |
---|
1468 | { |
---|
1469 | me->textDisplay.startEnd = 1; |
---|
1470 | me->textDisplay.endSelect = find_boundary(me, &w, line, END); |
---|
1471 | me->textDisplay.startSelect = me->textDisplay.realStart; |
---|
1472 | } |
---|
1473 | break; |
---|
1474 | } |
---|
1475 | |
---|
1476 | showSelect(me, oldStart, oldEnd); |
---|
1477 | break; |
---|
1478 | |
---|
1479 | case MotionNotify: |
---|
1480 | /* sigh... Xqvss can't be trusted, due to kernel bug */ |
---|
1481 | if (me->textDisplay.buttonDown == False) |
---|
1482 | break; |
---|
1483 | |
---|
1484 | w = where(me, event->xbutton.x, event->xbutton.y, &line); |
---|
1485 | |
---|
1486 | switch(me->textDisplay.whichButton) |
---|
1487 | { |
---|
1488 | case Button1: |
---|
1489 | me->textDisplay.startSelect = me->textDisplay.startPivot; |
---|
1490 | me->textDisplay.endSelect = me->textDisplay.endPivot; |
---|
1491 | |
---|
1492 | if (w >= me->textDisplay.endPivot) |
---|
1493 | me->textDisplay.endSelect = find_boundary(me, &w, line, END); |
---|
1494 | else if (w < me->textDisplay.startPivot) |
---|
1495 | me->textDisplay.startSelect = find_boundary(me, &w, line, START); |
---|
1496 | break; |
---|
1497 | |
---|
1498 | case Button3: |
---|
1499 | if (w > me->textDisplay.realEnd) |
---|
1500 | { |
---|
1501 | me->textDisplay.startEnd = 1; |
---|
1502 | me->textDisplay.endSelect = find_boundary(me, &w, line, END); |
---|
1503 | me->textDisplay.startSelect = me->textDisplay.realStart; |
---|
1504 | } |
---|
1505 | else if (w < me->textDisplay.realStart) |
---|
1506 | { |
---|
1507 | me->textDisplay.startEnd = 0; |
---|
1508 | me->textDisplay.startSelect = find_boundary(me, &w, line, START); |
---|
1509 | me->textDisplay.endSelect = me->textDisplay.realEnd; |
---|
1510 | } |
---|
1511 | else if (me->textDisplay.startEnd == 0) |
---|
1512 | me->textDisplay.startSelect = find_boundary(me, &w, line, START); |
---|
1513 | else |
---|
1514 | me->textDisplay.endSelect = find_boundary(me, &w, line, END); |
---|
1515 | break; |
---|
1516 | } |
---|
1517 | showSelect(me, oldStart, oldEnd); |
---|
1518 | break; |
---|
1519 | |
---|
1520 | case SelectionRequest: |
---|
1521 | xselProcessSelection(me->core.display, me->core.window, event, |
---|
1522 | me->textDisplay.selection); |
---|
1523 | break; |
---|
1524 | |
---|
1525 | case SelectionClear: |
---|
1526 | xselOwnershipLost(event->xselectionclear.time); |
---|
1527 | me->textDisplay.endSelect = me->textDisplay.startSelect; |
---|
1528 | showSelect(me, oldStart, oldEnd); |
---|
1529 | if (me->textDisplay.selection != NULL) |
---|
1530 | { |
---|
1531 | XjFree(me->textDisplay.selection); |
---|
1532 | me->textDisplay.selection = NULL; |
---|
1533 | } |
---|
1534 | break; |
---|
1535 | |
---|
1536 | default: |
---|
1537 | return False; |
---|
1538 | } |
---|
1539 | |
---|
1540 | return True; |
---|
1541 | } |
---|