1 /*
2  * GraphApp - Cross-Platform Graphics Programming Library.
3  *
4  * File: drawtext.c -- cross-platform portable drawing functions.
5  * Platform: Neutral  Version: 2.30  Date: 1998/01/01
6  *
7  * Version: 1.00  Changes: Original version by Lachlan Patrick.
8  * Version: 1.50  Changes: gprintf now has internal state.
9  * Version: 1.60  Changes: Major bugfixes!
10  * Version: 2.00  Changes: Update to version 2.
11  * Version: 2.01  Changes: Changed unnecessary longs to ints.
12  * Version: 2.20  Changes: Added underlining ability.
13  * Version: 2.30  Changes: Now uses getheight, getdescent.
14  */
15 
16 /* Copyright (C) 1993-1998 Lachlan Patrick
17 
18    This file is part of GraphApp, a cross-platform C graphics library.
19 
20    GraphApp is free software; you can redistribute it and/or modify it
21    under the terms of the GNU Library General Public License.
22    GraphApp is distributed in the hope that it will be useful, but
23    WITHOUT ANY WARRANTY.
24 
25    See the file COPYLIB.TXT for details.
26 */
27 
28 #include "internal.h"
29 #include <stdarg.h>
30 
31 #define COMPRESS_WHITESPACE 0
32 
get_next_line(char * line,int width,const char * s)33 static const char *get_next_line(char *line, int width, const char *s)
34 {
35     const char *t; char  *k;
36     int	word_width, line_width;
37     int	sp, tab, nl;
38 
39     line_width  = 0;
40 
41     for (t=s, k=line; *t!='\0'; line=k, s=t)
42     {
43 	for (nl=tab=sp=0; (*t!='\0') && isspace(*t) && !nl; t++)
44 	{
45 #if COMPRESS_WHITESPACE
46 	    if (*t == '\n') nl++;
47 	    else if (*t == '\t') tab++;
48 	    else sp++;
49 #else
50 	    if (*t == '\n') nl++;
51 	    else if (*t == '\t') { /* expand tabs */
52 		for (tab=4; tab; tab--)
53 		    *k++ = ' ';
54 	    }
55 	    else
56 		*k++ = *t;
57 #endif
58 	}
59 
60 	s = t;
61 
62 	if (nl)	{ *k++ = '\n'; break; }
63 	else if (tab)	{ *k++ = ' '; *k++ = ' '; }
64 	else if (sp)	{ *k++ = ' ';  }
65 
66 	/* fetch the next word to draw */
67 
68 	for (*k='\0'; (*t!='\0') && !isspace(*t); *k='\0') {
69 	    if (*t == '-') {
70 		*k++ = *t++;
71 		*k = '\0';
72 		break;
73 	    }
74 	    *k++ = *t++;
75 	}
76 
77 	/* determine where the word should be drawn */
78 
79 	while (((word_width = strwidth(current->fnt,line)) > width)
80 	       && (k>line))
81 	{
82 	    *(--k) = '\0';
83 	    --t;
84 	}
85 
86 	if (word_width > (width - line_width)) {
87 	    k = line;
88 	    break;
89 	} else
90 	    line_width += word_width;
91     }
92 
93     *k = '\0';
94 
95     return (*s == '\0') ? NULL : s;
96 }
97 
textheight(int width,const char * s)98 int textheight(int width, const char *s)
99 {
100     int y;
101     char line[256];
102     int height;
103 
104     height = getheight(current->fnt);
105 
106     for(y=0; s; y+=height)
107     {
108 	s = get_next_line(line, width, s);
109     }
110 
111     return y;
112 }
113 
draw_text_left(char * line,rect r,int line_height,int underline,const char * s)114 static const char *draw_text_left(char *line, rect r, int line_height,
115 				  int underline, const char *s)
116 {
117     char *k;
118     point p;
119     int width, height;
120     font f;
121 
122     f = current->fnt;
123 
124     for(p=pt(r.x,r.y); (p.y<=r.y+r.height) && (s); p.y+=line_height)
125     {
126 	s = get_next_line(line, r.width, s);
127 
128 	for (k=line; *k!='\0'; k++)
129 	    continue;
130 	for (--k; (k>=line) && isspace(*k); k--)
131 	    *k = '\0';
132 
133 	drawstr(p, line);
134 
135 	if (underline) {
136 	    width = strwidth(f, line);
137 	    height = p.y+getheight(f)-getdescent(f)+2;
138 	    drawline(pt(p.x+1, height), pt(p.x+width-1, height));
139 	}
140     }
141 
142     return s;
143 }
144 
draw_text_right(char * line,rect r,int line_height,int underline,const char * s)145 static const char *draw_text_right(char *line, rect r, int line_height,
146 			     int underline, const char *s)
147 {
148     char *k;
149     int w;
150     point p;
151     int width, height;
152     font f;
153 
154     f = current->fnt;
155 
156     for(p=pt(r.x,r.y); (p.y<=r.y+r.height) && (s); p.y+=line_height)
157     {
158 	s = get_next_line(line, r.width, s);
159 
160 	for (k=line; *k!='\0'; k++)
161 	    continue;
162 	for (--k; (k>=line) && isspace(*k); k--)
163 	    *k = '\0';
164 	for (k=line; (*k!='\0') && isspace(*k); k++)
165 	    continue;
166 
167 	w = strwidth(current->fnt, k);
168 	p.x = r.x+r.width - w;
169 
170 	drawstr(p, k);
171 
172 	if (underline) {
173 	    width = strwidth(f, k);
174 	    height = p.y+getheight(f)-getdescent(f)+2;
175 	    drawline(pt(p.x+1, height), pt(p.x+width-1, height));
176 	}
177     }
178 
179     return s;
180 }
181 
draw_text_centered(char * line,rect r,int line_height,int underline,const char * s)182 static const char *draw_text_centered(char *line, rect r, int line_height,
183 				      int underline, const char *s)
184 {
185     char *k;
186     int w;
187     point p;
188     int width, height;
189     font f;
190 
191     f = current->fnt;
192 
193     for(p=pt(r.x,r.y); (p.y<=r.y+r.height) && (s); p.y+=line_height)
194     {
195 	s = get_next_line(line, r.width, s);
196 
197 	for (k=line; *k!='\0'; k++)
198 	    continue;
199 	for (--k; (k>=line) && isspace(*k); k--)
200 	    *k = '\0';
201 	for(k=line; (*k!='\0') && isspace(*k); k++)
202 	    continue;
203 
204 	w = strwidth(current->fnt, k);
205 	p.x = r.x + (r.width-w)/2;
206 
207 	drawstr(p, k);
208 
209 	if (underline) {
210 	    width = strwidth(f, k);
211 	    height = p.y+getheight(f)-getdescent(f)+2;
212 	    drawline(pt(p.x+1, height), pt(p.x+width-1, height));
213 	}
214     }
215 
216     return s;
217 }
218 
draw_text_justified(char * line,rect r,int line_height,int underline,const char * s)219 static const char *draw_text_justified(char *line, rect r, int line_height,
220 				       int underline, const char *s)
221 {
222     char *j, *k;
223     int w, xw, nl, sc, sw, space_width;
224     point p;
225     int width, height;
226     font f;
227 
228     space_width = strwidth(current->fnt, " ");
229     f = current->fnt;
230 
231     for(p=pt(r.x,r.y); (p.y<=r.y+r.height) && (s); p.y+=line_height)
232     {
233 	s = get_next_line(line, r.width, s);
234 
235 	p.x = r.x;
236 
237 	for(j=line; (*j!='\0') && isspace(*j); j++)
238 	    p.x += space_width;
239 	for (sc=0, k=j; *k!='\0'; k++)
240 	    if (isspace(*k))
241 		sc++;
242 	for (nl=0, --k; (k>=j) && isspace(*k); k--) {
243 	    if (*k == '\n')
244 		nl++;
245 	    *k = '\0';
246 	    sc--;
247 	}
248 
249 	if ((sc==0) || nl || (! s)) {
250 	    drawstr(p, j);
251 	    width = strwidth(f, j);
252 	}
253 	else {
254 	    w = strwidth(f, j);
255 	    sw = space_width + (r.x+r.width-p.x-w)/sc;
256 	    xw = (r.x+r.width-p.x-w)%sc;
257 
258 	    for(j=strtok(j," "); j; j=strtok(NULL," "))
259 	    {
260 		drawstr(p, j);
261 		p.x += sw + strwidth(f, j);
262 		if (xw) {
263 		    p.x++;
264 		    xw--;
265 		}
266 	    }
267 	    width = r.width;
268 	}
269 	if (underline) {
270 	    height = p.y+getheight(f)-getdescent(f)+2;
271 	    drawline(pt(p.x+1, height), pt(p.x+width-1, height));
272 	}
273     }
274 
275     return s;
276 }
277 
drawtext(rect r,int alignment,const char * s)278 const char *drawtext(rect r, int alignment, const char *s)
279 {
280     int h;
281     int nlines;
282     int line_height;
283     int u = 0;
284     rect clip;
285     const char *remains;
286     char line[256];
287 
288     initapp(0,0);
289     if (! s) return (char *) NULL;
290     if (! current->fnt)
291 	current->fnt = SystemFont;
292 
293     clip = getcliprect();
294     setcliprect(r);
295 
296     line_height = getheight(current->fnt);
297 
298     if ((alignment & VCenter) == VCenter) {
299 	h = textheight(r.width, s);
300 	if (h < r.height)
301 	    r.y += (r.height-h)/2;
302     }
303     else if ((alignment & VJustify) == VJustify) {
304 	h = textheight(r.width, s);
305 	if (h < r.height) {
306 	    nlines = h / line_height;
307 	    if (nlines > 1)
308 		line_height += ((r.height-h) / (nlines-1));
309 	}
310     }
311     else if ((alignment & AlignBottom) == AlignBottom) {
312 	h = textheight(r.width, s);
313 	if (h < r.height)
314 	    r.y += (r.height-h);
315     }
316 
317     u = (alignment & Underline);
318 
319     if ((alignment & Center) == Center)
320 	remains = draw_text_centered(line, r, line_height, u, s);
321     else if ((alignment & Justify) == Justify)
322 	remains = draw_text_justified(line, r, line_height, u, s);
323     else if ((alignment & AlignRight) == AlignRight)
324 	remains = draw_text_right(line, r, line_height, u, s);
325     else
326 	remains = draw_text_left(line, r, line_height, u, s);
327 
328     setcliprect(clip);
329     return remains;
330 }
331 
gprintf(const char * fmt,...)332 int gprintf(const char *fmt, ...)
333 {
334     static point p = {0,0};
335     int count;
336     int line_height;
337     char *s, *t;
338     va_list argptr;
339     char str[256];
340 
341     va_start(argptr, fmt);
342     count = vsprintf(str, fmt, argptr);
343 
344     initapp(0,0);
345     if (! current->fnt)
346 	current->fnt = SystemFont;
347     line_height = getheight(current->fnt);
348 
349     for (s=t=str; *s!='\0'; t++) {
350 	if (current->p.y != p.y) {
351 	    /* typewriter ping! */
352 	    p = current->p;
353 	}
354 	if (*t == '\n') {
355 	    /* print everything from s to t and move point down */
356 	    *t = '\0';
357 	    drawstr(p, s);
358 	    current->p.y += line_height;
359 	    /* go past the substring just printed */
360 	    s = t+1;
361 	}
362 	else if (*t == '\0') {
363 	    /* print final string without newline */
364 	    p.x += drawstr(p, s);
365 	    /* go to end of string, signal termination */
366 	    s = t;
367 	}
368     }
369 
370     va_end(argptr);
371 
372     return count;
373 }
374