|
|
1.1 root 1: /* $Header: Xtextlib.c,v 10.4 86/02/01 15:42:32 tony Rel $ */
2: /* Library of routines for creating a simple text output window.
3: *
4: * Routines in the library are:
5: *
6: * TextCreate Creates a new instance of a text window
7: * TextDestroy Destroys the window
8: * TextClear Clears a text window
9: * TextRedisplay Redisplays the window
10: * TextEvent Handles exposure and unmapping events
11: * TextPutString Displays a string in a text window
12: * TextPutChar Displays a character in a text window
13: * TextPrintf Does a printf in a text window
14: *
15: * All these routines pass around a pointer to a TextWindow data structure:
16: *
17: * typedef struct _TextWindow {
18: * Window w; Window to use
19: * FontInfo *font; Font to use for text
20: * short num_lines; Number of lines in the window
21: * short num_chars; The length of each line
22: * short mapped; Whether or not the window is mapped
23: * short height; Height of window in pixels
24: * short width; Width of window in pixels
25: * short first_line; The index of the first line
26: * char **lines; Ptr to array of text lines
27: * short *line_length; Ptr to array of line lengths (in pixels)
28: * short *line_chars; Ptr to array of line lengths in chars
29: * short last_line; Which line is the last
30: * short last_char; Length of the last line
31: * short next_x; X-coord for next character
32: * short next_y; Y-coord for next character
33: * unsigned int eventmask; List of events we're interested in
34: * char *scroll_history; Ptr to list of scroll amounts
35: * short scroll_count; Number of outstanding scrolls
36: * short scroll_start; Where in the history the history starts
37: * short old_scrolls; Number of ignorable outstanding scrolls
38: * short fastscroll; Whether or not to use fast scrolling
39: * } TextWindow;
40: *
41: * Applications should not modify anything in this data structure, obviously!
42: * They may, however, have reason to get information out of it. (Such as the
43: * window id for mapping).
44: *
45: * Information about the first line of the window is stored in the array
46: * entries subscripted by [first_line]; the arrays wrap back up at the end.
47: * Last_char should always be the same as line_chars[last_line].
48: * Similarly, next_x should always be the same as line_length[last_line];
49: *
50: * The only complicated thing about these procedures is the way they keep
51: * track of scrolling. When a scroll is done, X sends ExposeRegions for
52: * every region that needs to be patched up and then an ExposeCopy event.
53: * The ExposeCopy comes even if there were no regions. The only problem
54: * is that more scrolls may have been done in the meantime. So we keep a
55: * history of how much cumulative scrolling has been done in the
56: * scroll_history list. scroll_start tells which one to start with, and
57: * scroll_count tells how many there are (they wrap around). The list is
58: * num_lines long since anything that's scrolled away longer ago than that
59: * has scrolled off the screen. The old_scrolls field gets set whenever the
60: * screen is fully updated for some reason or other; it means that that
61: * many ExposeCopy events can be completely ignored since the screen has
62: * been fully updated.
63: */
64:
65: #include <stdio.h>
66: #include "Xlib.h"
67: #include "Xtext.h"
68:
69: #ifndef TRUE
70: #define TRUE 1
71: #define FALSE 0
72: #endif
73:
74: /* Define the width of the left margin */
75:
76: #define mar_width 2
77:
78: char *calloc(), *malloc(), *realloc();
79:
80: /* The following variable is sometimes set by TextPutString to temporarily
81: disable screen updating. */
82:
83: static int dont_update = FALSE;
84:
85: /* TextCreate creates a new window which will use the
86: * specified font. The window is height lines high and width
87: * characters wide. Note that since a variable-width font may be
88: * used, the width is calculated using the average width of the font.
89: * Colors are used as specified.
90: */
91:
92: TextWindow *TextCreate (width, height, x, y, parent, fontname,
93: bwidth, fgpixel, bgpixel, bordercolor, fastscroll)
94: int height, width, x, y, bwidth, fastscroll;
95: Window parent;
96: char *fontname;
97: int fgpixel, bgpixel;
98: Pixmap bordercolor;
99: {
100: register TextWindow *t;
101: register int i;
102: register FontInfo *f;
103: Window XCreateWindow();
104: Pixmap bgpixmap;
105:
106: if ((t = (TextWindow *) malloc(sizeof(TextWindow))) ==
107: NULL) return NULL;
108:
109: if ((f = t->font = XOpenFont(fontname)) == NULL) {
110: TextDestroy(t);
111: return NULL;
112: }
113:
114: t->fgpixel = fgpixel;
115: t->bgpixel = bgpixel;
116:
117: if ((bgpixmap = XMakeTile(bgpixel)) == NULL) {
118: TextDestroy(t);
119: return NULL;
120: }
121:
122: t->width = width * f->width + mar_width;
123: t->height = height * f->height;
124:
125: t->w = XCreateWindow (parent, x, y, t->width, t->height,
126: bwidth, bordercolor, bgpixmap);
127: if (t->w == NULL) {
128: TextDestroy(t);
129: XFreePixmap(bgpixmap);
130: return NULL;
131: }
132:
133: XFreePixmap(bgpixmap);
134:
135: t->eventmask = ExposeRegion | ExposeCopy | UnmapWindow;
136: /* (ExposeRegion automatically selects ExposeWindow) */
137:
138: XSelectInput (t->w, t->eventmask);
139:
140: XSetResizeHint (t->w, mar_width, 0, f->width, f->height);
141: t->fastscroll = fastscroll;
142: t->mapped = FALSE;
143: t->num_lines = height;
144: t->num_chars = width;
145:
146: t->first_line = 0;
147:
148: if ((t->lines = (char **)
149: calloc (height, sizeof (char *))) == NULL) {
150: TextDestroy(t);
151: return NULL;
152: }
153:
154: if ((t->line_length = (short *)
155: calloc (height, sizeof (short))) == NULL) {
156: TextDestroy(t);
157: return NULL;
158: }
159:
160: if ((t->line_chars = (short *)
161: calloc (height, sizeof (short))) == NULL) {
162: TextDestroy(t);
163: return NULL;
164: }
165:
166: for (i = 0; i < height; i++) {
167: if ((t->lines[i] = (char *)
168: calloc (width+1, sizeof (char))) == NULL) {
169: TextDestroy(t);
170: return NULL;
171: }
172: }
173:
174: if ((t->scroll_history = calloc(height, sizeof (char))) == NULL) {
175: TextDestroy(t);
176: return NULL;
177: }
178:
179: t->scroll_count = t->scroll_start = t->old_scrolls = 0;
180: TextClear(t);
181: return t;
182: }
183:
184: /* Free all the storage associated with a textwindow */
185:
186: TextDestroy(t)
187: register TextWindow *t;
188: {
189: register int i;
190:
191: /* Free things in the order we allocated them. If something doesn't
192: exist, don't free it!) */
193:
194: if (t->font) {
195: if (t->font->fixedwidth == 0) free(t->font->widths);
196: free(t->font);
197: }
198:
199: if (t->w) XDestroyWindow(t->w);
200:
201: if (t->lines) {
202: for (i = 0; i < t->num_lines; i++) {
203: if (t->lines[i]) free(t->lines[i]);
204: }
205: free(t->lines);
206: }
207:
208: if (t->line_length) free (t->line_length);
209: if (t->line_chars) free (t->line_chars);
210: if (t->scroll_history) free (t->scroll_history);
211:
212: /* And finally the data structure itself! */
213:
214: free (t);
215: }
216:
217: /* Clear out a text window and redisplay */
218:
219: TextClear(t)
220: register TextWindow *t;
221: {
222: register int i;
223:
224: for (i = 0; i < t->num_lines; i++) {
225: t->lines[i][0] = '\0';
226: t->line_chars[i] = 0;
227: t->line_length[i] = mar_width; /* Allow a left margin */
228: }
229: t->last_line = 0;
230: t->last_char = 0;
231: t->next_x = mar_width; /* Allow a left margin */
232: t->next_y = 0;
233: t->first_line = 0;
234:
235: TextRedisplay(t);
236: }
237:
238: /* Redisplays a text window */
239:
240: TextRedisplay (t)
241: register TextWindow *t;
242: {
243: if (!t->mapped) return;
244:
245: /* Clear the border area */
246:
247: XPixSet(t->w, 0, 0, mar_width, t->height, t->bgpixel);
248:
249: Redisplay_lines(t, 0, t->num_lines - 1);
250:
251: /* Any outstanding copies from scrolls can now be ignored */
252:
253: t->old_scrolls = t->scroll_count;
254: t->scroll_count = t->scroll_start = 0;
255: }
256:
257: Redisplay_lines(t, start, finish)
258: register TextWindow *t;
259: int start, finish;
260: {
261: register int i, j, y, height = t->font->height, x, width;
262:
263: if (finish < 0) return;
264: if (start < 0) start = 0;
265:
266: y = start * height;
267: j = start + t->first_line;
268:
269: for (i = start; i <= finish; i++) {
270: if (j >= t->num_lines) j = 0;
271:
272: if (t->line_chars[j]) {
273: XText (t->w, mar_width, y, t->lines[j], t->line_chars[j],
274: t->font->id, t->fgpixel, t->bgpixel);
275: }
276:
277: x = t->line_length[j];
278: width = t->width - x;
279:
280: if (width > 0) XPixSet(t->w, x, y, width, height, t->bgpixel);
281: y += height;
282: j++;
283: }
284: }
285:
286: /* Handles an event. If it's not an event it knows how to deal with,
287: returns TRUE, otherwise FALSE. */
288:
289: int TextEvent(t, e)
290: register TextWindow *t;
291: XEvent *e;
292: {
293: XExposeEvent *ee = (XExposeEvent *) e;
294: int offset;
295:
296: switch (e->type) {
297: case ExposeWindow:
298: if (ee->height != t->height || ee->width != t->width) {
299: Change_text_window_size(t, ee->height / t->font->height,
300: ee->width / t->font->width);
301: }
302: t->mapped = TRUE;
303: TextRedisplay(t);
304: break;
305:
306: case ExposeRegion:
307: /* If there have been more scrolls than there are lines,
308: this stuff has already scrolled off! */
309:
310: if (t->scroll_count > t->num_lines) return FALSE;
311:
312: /* If this is for an old scroll, ignore it */
313:
314: if (ee->detail == ExposeCopy && t->old_scrolls) return FALSE;
315:
316: if (t->scroll_count > 0) {
317: offset = t->scroll_history[t->scroll_start];
318: } else offset = 0;
319: Redisplay_lines(t, ee->y / t->font->height - offset,
320: (ee->y + ee->height - 1) / t->font->height - offset);
321: break;
322:
323: case UnmapWindow:
324: t->mapped = FALSE;
325: break;
326:
327: case ExposeCopy: /* We've finished the events for one scroll */
328: /* If there are old scrolls, just decrement the count and
329: return */
330:
331: if (t->old_scrolls) {
332: t->old_scrolls--;
333: return FALSE;
334: }
335: t->scroll_count--;
336: if (t->scroll_count < t->num_lines) {
337: t->scroll_start++;
338: if (t->scroll_start >= t->num_lines) t->scroll_start = 0;
339: }
340: break;
341:
342: default:
343: return TRUE;
344: }
345: return FALSE;
346: }
347:
348: Change_text_window_size (t, new_h, new_w)
349: register TextWindow *t;
350: register int new_h, new_w;
351: {
352: register int i;
353: register char *curline;
354:
355: Normalize(t); /* Rearrange lines so that first_line = 0 */
356:
357: /* First free up any now extraneous lines */
358:
359: for (i = new_h; i < t->num_lines; i++) free(t->lines[i]);
360:
361: if ((t->lines = (char **)
362: realloc(t->lines, new_h * sizeof (char *))) == NULL) {
363: return;
364: }
365:
366: if ((t->line_length = (short *)
367: realloc(t->line_length, new_h * sizeof (short))) == NULL) {
368: return;
369: }
370:
371: if ((t->line_chars = (short *)
372: realloc(t->line_chars, new_h * sizeof (short))) == NULL) {
373: return;
374: }
375:
376: if ((t->scroll_history = realloc(t->scroll_history, new_h)) == NULL) {
377: return;
378: }
379:
380: for (i = 0; i < new_h; i++) {
381: if (i < t->num_lines) {
382: if ((curline = t->lines[i] =
383: realloc(t->lines[i], new_w + 1)) == NULL) {
384: return;
385: }
386:
387: if (t->line_chars[i] > new_w) {
388: t->line_chars[i] = new_w;
389: curline[new_w] = '\0'; /* Truncate the line */
390: t->line_length[i] = mar_width +
391: XStringWidth (curline, t->font, 0, 0);
392: }
393: } else {
394: if ((t->lines[i] = malloc(new_w+1)) == NULL) {
395: return;
396: }
397: t->lines[i][0] = '\0';
398: t->line_chars[i] = 0;
399: t->line_length[i] = mar_width;
400: }
401: }
402:
403: if (t->last_line >= new_h) {
404: t->last_line = new_h - 1;
405: t->last_char = t->line_chars[t->last_line];
406: t->next_x = t->line_length[t->last_line];
407: t->next_y = t->last_line * t->font->height;
408:
409: } else if (t->last_char > new_w) {
410: t->last_char = t->line_chars[t->last_line];
411: t->next_x = t->line_length[t->last_line];
412: }
413:
414: t->num_lines = new_h;
415: t->num_chars = new_w;
416: t->height = new_h * t->font->height;
417: t->width = new_w * t->font->width + mar_width;
418: }
419:
420: /* Routine to re-arrange the lines in a window structure so that first_line
421: is equal to 0. */
422:
423: Normalize(t)
424: register TextWindow *t;
425: {
426: if (t->first_line == 0) return;
427:
428: t->last_line -= t->first_line;
429: if (t->last_line < 0) t->last_line += t->num_lines;
430:
431: Spin_lines(t, 0, t->num_lines-1, t->first_line);
432:
433: t->first_line = 0;
434: }
435:
436: /* Spin lines rotates the m through n lines of the arrays
437: forward offset places. For example, 012345 spun forward 2 is 234501.
438: It's straightforward to spin the first part of the arrays; and we
439: call Spin_lines recursively to do the last offset elements */
440:
441: /* Actually, it's tail-recursive, so I just use a loop. But I can
442: pretend, can't I? */
443:
444: Spin_lines(t, m, n, offset)
445: register TextWindow *t;
446: int m, n;
447: register int offset;
448: {
449: register int i;
450: register int temp; /* Temporaries */
451: register char *tempc;
452:
453: while (1) {
454: if (offset == 0 || offset > n-m) return;
455:
456: for (i = m; i <= n-offset; i++) {
457: temp = t->line_length[i];
458: t->line_length[i] = t->line_length[offset+i];
459: t->line_length[offset+i] = temp;
460:
461: temp = t->line_chars[i];
462: t->line_chars[i] = t->line_chars[offset+i];
463: t->line_chars[offset+i] = temp;
464:
465: tempc = t->lines[i];
466: t->lines[i] = t->lines[offset+i];
467: t->lines[offset+i] = tempc;
468: }
469:
470: /* Spin_lines(t, n-offset+1, n, offset - ((n-m+1) % offset)); */
471:
472: temp = m;
473: m = n - offset + 1;
474: offset -= (n - temp + 1) % offset;
475: }
476: }
477:
478: /* Routine to put a string in a text window. If fastscroll is
479: set in the TextWindow structure, a single block scroll is done instead
480: of scrolling at each newline. */
481:
482: #define verybig 10000 /* Amount to scroll if we should refresh instead */
483:
484: TextPutString (t, str)
485: register TextWindow *t;
486: register char *str;
487: {
488: register char *ch = str;
489: register char oldch;
490: int jump = t->fastscroll; /* Whether to do jump scrolling */
491: int newlines, scroll;
492:
493: if (jump) jump = Count_lines (t, str, &newlines, &scroll);
494:
495: while (1) {
496: while (*ch != '\0' && *ch != '\n') ch++;
497: if (ch != str) {
498: oldch = *ch;
499: *ch = '\0';
500: Do_text_string (t, str);
501: *ch = oldch;
502: }
503: if (*ch == '\0') break;
504: if (jump && newlines == scroll) {
505: Clear_lines (t, newlines);
506: dont_update = TRUE; /* Stop updating now */
507: }
508: newlines--;
509: TextPutChar (t, *ch);
510: str = ++ch;
511: }
512: if (t->mapped && jump) {
513: if (scroll != verybig) Scroll_text_window (t, scroll);
514: else TextRedisplay (t);
515: }
516: dont_update = FALSE;
517: }
518:
519: /* Count the number of lines in str, calculate how much scrolling
520: will be needed, and return whether this amount is positive */
521:
522: int Count_lines (t, str, newlines, scroll)
523: register TextWindow *t;
524: register char *str;
525: int *newlines, *scroll;
526: {
527: register int num_lines = 0;
528: register int lines_left, height = t->num_lines;
529:
530: *scroll = 0;
531:
532: while (*str) {
533: if (*str++ == '\n') num_lines++;
534: }
535:
536: *newlines = num_lines;
537:
538: if (num_lines <= 1) return FALSE; /* Don't bother jump scrolling */
539:
540: /* Would this fill the screen? */
541:
542: if (num_lines >= height) {
543: *scroll = verybig;
544: return TRUE;
545: }
546:
547: /* Calculate the number of lines left in the window */
548:
549: lines_left = height - (t->last_line - t->first_line + 1);
550: if (lines_left >= height) lines_left -= height;
551:
552: /* Figure out how many lines to scroll */
553:
554: num_lines -= lines_left;
555:
556: if (num_lines <= 0) return FALSE; /* Enough room already */
557:
558: *scroll = num_lines;
559: return TRUE;
560: }
561:
562: /* Clear a number of lines in the window data structure */
563:
564: Clear_lines (t, scroll)
565: register TextWindow *t;
566: register int scroll;
567: {
568: register int i, start = t->first_line;
569: register int height = t->num_lines;
570:
571: /* If this would fill the screen, clear it instead */
572:
573: if (scroll >= t->height ) {
574: TextClear (t);
575: return;
576: }
577:
578: /* Shift the contents */
579:
580: t->first_line += scroll;
581: if (t->first_line >= height) t->first_line -= height;
582:
583: /* Now clear the blank lines */
584:
585: for (i = 0; i < scroll; i++) {
586: t->lines[start][0] = '\0';
587: t->line_chars[start] = 0;
588: t->line_length[start] = mar_width; /* Allow a left margin */
589: start++;
590: if (start >= height) start = 0;
591: }
592: }
593:
594: /* Store the characters of a string in the window and update the screen,
595: but only if dont_update isn't set */
596:
597: Do_text_string (t, str)
598: register TextWindow *t;
599: char *str;
600: {
601: register char *ch = str;
602: register char *curline = t->lines[t->last_line];
603: register int curchar = t->last_char;
604: register int x = t->next_x;
605: register FontInfo *f = t->font;
606: int start_x = t->next_x, start = curchar,
607: minch = f->firstchar, maxch = f->lastchar;
608:
609: /* First store the characters in the line */
610:
611: while (*ch != '\0' && curchar < t->num_chars) {
612: curline[curchar] = *ch;
613: if (*ch >= minch && *ch <= maxch) {
614: x += f->fixedwidth ? f->width : f->widths[*ch - minch];
615: }
616: curchar++;
617: ch++;
618: }
619:
620: curline[curchar] = '\0';
621: t->line_chars[t->last_line] = t->last_char = curchar;
622: t->line_length[t->last_line] = t->next_x = x;
623:
624: if (dont_update || !t->mapped) return;
625:
626: /* And then update the screen */
627:
628: if (start < t->num_chars) {
629: XText (t->w, start_x, t->next_y, str, curchar-start,
630: f->id, t->fgpixel, t->bgpixel);
631: }
632: }
633:
634: /* Textputchar displays a character in the text window. It
635: * responds to \n as a special character and just displays anything else.
636: */
637:
638: TextPutChar (t, ch)
639: register TextWindow *t;
640: char ch;
641: {
642: register int i, height = t->num_lines;
643: register char *curline = t->lines[t->last_line];
644: register FontInfo *f = t->font;
645:
646: switch (ch) {
647: case '\0': /* NULL */
648: break;
649:
650: case '\n': /* newline */
651: if (t->last_line == t->first_line - 1 ||
652: (t->last_line == height - 1 && t->first_line == 0)) {
653:
654: /* The screen is full...clear out the first line */
655:
656: t->lines[t->first_line][0] = '\0';
657: t->line_chars[t->first_line] = 0;
658: t->line_length[t->first_line] = mar_width;
659:
660: t->first_line++; /* And advance it */
661: if (t->first_line == height) t->first_line = 0;
662:
663: if (!dont_update && t->mapped) Scroll_text_window (t, 1);
664:
665: } else if (!dont_update) t->next_y += f->height;
666:
667: t->last_line++;
668: if (t->last_line == height) t->last_line = 0;
669:
670: t->last_char = 0;
671: t->next_x = mar_width;
672: break;
673:
674: default: /* Just insert the character */
675: t->last_char++;
676: t->line_chars[t->last_line]++;
677: if (t->last_char > t->num_chars) break;
678:
679: curline[t->last_char] = ch;
680: curline[t->last_char+1] = '\0';
681:
682: if (!dont_update && t->mapped) {
683: XText(t->w, t->next_x, t->next_y, &ch, 1,
684: f->id, t->fgpixel, t->bgpixel);
685: }
686: if (ch <= f->firstchar && ch >= f->lastchar) {
687: t->line_length[t->last_line] = t->next_x +=
688: (f->fixedwidth ? f->width :
689: f->widths[ch - f->lastchar]);
690: }
691: break;
692: }
693: }
694:
695: /* This procedure moves the contents of a text window up n lines.
696: */
697:
698: Scroll_text_window (t, n)
699: register TextWindow *t;
700: register int n;
701: {
702: register int i, y, x, width, j;
703: int height = t->font->height;
704: int scrollsize = n * height;
705:
706: /* First shift up the contents */
707:
708: XMoveArea(t->w, 0, scrollsize, 0, 0, t->width, t->height-scrollsize);
709:
710: /* Now redisplay the bottom n lines */
711:
712: y = height * (t->num_lines - n);
713: i = t->first_line - n;
714: if (i < 0) i += t->num_lines;
715:
716: for (j = 0; j < n; j++) {
717: if (t->line_chars[i]) {
718: XText (t->w, mar_width, y, t->lines[i], t->line_chars[i],
719: t->font->id, t->fgpixel, t->bgpixel);
720: }
721: x = t->line_length[i];
722: width = t->width - x;
723:
724: if (width > 0) XPixSet(t->w, x, y, width, height, t->bgpixel);
725: y += height;
726: i++;
727: if (i == t->num_lines) i = 0;
728: }
729:
730: /* Add the current scroll to all values in the scroll history,
731: then add a new entry at the end (the history wraps!) */
732:
733: i = t->scroll_start;
734:
735: for (j = 0; j < t->scroll_count; j++) {
736: t->scroll_history[i] += n;
737: i++;
738: if (i >= t->num_lines) i = 0;
739: }
740: t->scroll_count++;
741: t->scroll_history[i] = n;
742:
743: if (t->scroll_count > t->num_lines) t->scroll_start++; /* trash one */
744: }
745:
746: #define TEXT_BUFSIZE 2048
747:
748: TextPrintf(t, format, args)
749: TextWindow *t;
750: char *format;
751: {
752: char buffer[TEXT_BUFSIZE+1];
753: struct _iobuf _strbuf;
754:
755: _strbuf._flag = _IOWRT+_IOSTRG;
756: _strbuf._ptr = buffer;
757: _strbuf._cnt = TEXT_BUFSIZE;
758: _doprnt(format, &args, &_strbuf);
759: _strbuf._cnt++; /* Be sure there's room for the \0 */
760: putc('\0', &_strbuf);
761: TextPutString(t, buffer);
762: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.