| /* |
| * GraphApp - Cross-Platform Graphics Programming Library. |
| * |
| * File: drawtext.c -- cross-platform portable drawing functions. |
| * Platform: Neutral Version: 2.30 Date: 1998/01/01 |
| * |
| * Version: 1.00 Changes: Original version by Lachlan Patrick. |
| * Version: 1.50 Changes: gprintf now has internal state. |
| * Version: 1.60 Changes: Major bugfixes! |
| * Version: 2.00 Changes: Update to version 2. |
| * Version: 2.01 Changes: Changed unnecessary longs to ints. |
| * Version: 2.20 Changes: Added underlining ability. |
| * Version: 2.30 Changes: Now uses getheight, getdescent. |
| */ |
| |
| /* Copyright (C) 1993-1998 Lachlan Patrick |
| |
| This file is part of GraphApp, a cross-platform C graphics library. |
| |
| GraphApp is free software; you can redistribute it and/or modify it |
| under the terms of the GNU Library General Public License. |
| GraphApp is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY. |
| |
| See the file COPYLIB.TXT for details. |
| */ |
| |
| #include "internal.h" |
| #include <stdarg.h> |
| |
| #define COMPRESS_WHITESPACE 0 |
| |
| static const char *get_next_line(char *line, int width, const char *s) |
| { |
| const char *t; char *k; |
| int word_width, line_width; |
| int sp, tab, nl; |
| |
| line_width = 0; |
| |
| for (t=s, k=line; *t!='\0'; line=k, s=t) |
| { |
| for (nl=tab=sp=0; (*t!='\0') && isspace(*t) && !nl; t++) |
| { |
| #if COMPRESS_WHITESPACE |
| if (*t == '\n') nl++; |
| else if (*t == '\t') tab++; |
| else sp++; |
| #else |
| if (*t == '\n') nl++; |
| else if (*t == '\t') { /* expand tabs */ |
| for (tab=4; tab; tab--) |
| *k++ = ' '; |
| } |
| else |
| *k++ = *t; |
| #endif |
| } |
| |
| s = t; |
| |
| if (nl) { *k++ = '\n'; break; } |
| else if (tab) { *k++ = ' '; *k++ = ' '; } |
| else if (sp) { *k++ = ' '; } |
| |
| /* fetch the next word to draw */ |
| |
| for (*k='\0'; (*t!='\0') && !isspace(*t); *k='\0') { |
| if (*t == '-') { |
| *k++ = *t++; |
| *k = '\0'; |
| break; |
| } |
| *k++ = *t++; |
| } |
| |
| /* determine where the word should be drawn */ |
| |
| while (((word_width = strwidth(current->fnt,line)) > width) |
| && (k>line)) |
| { |
| *(--k) = '\0'; |
| --t; |
| } |
| |
| if (word_width > (width - line_width)) { |
| k = line; |
| break; |
| } else |
| line_width += word_width; |
| } |
| |
| *k = '\0'; |
| |
| return (*s == '\0') ? NULL : s; |
| } |
| |
| int textheight(int width, const char *s) |
| { |
| int y; |
| char line[256]; |
| int height; |
| |
| height = getheight(current->fnt); |
| |
| for(y=0; s; y+=height) |
| { |
| s = get_next_line(line, width, s); |
| } |
| |
| return y; |
| } |
| |
| static const char *draw_text_left(char *line, rect r, int line_height, |
| int underline, const char *s) |
| { |
| char *k; |
| point p; |
| int width, height; |
| font f; |
| |
| f = current->fnt; |
| |
| for(p=pt(r.x,r.y); (p.y<=r.y+r.height) && (s); p.y+=line_height) |
| { |
| s = get_next_line(line, r.width, s); |
| |
| for (k=line; *k!='\0'; k++) |
| continue; |
| for (--k; (k>=line) && isspace(*k); k--) |
| *k = '\0'; |
| |
| drawstr(p, line); |
| |
| if (underline) { |
| width = strwidth(f, line); |
| height = p.y+getheight(f)-getdescent(f)+2; |
| drawline(pt(p.x+1, height), pt(p.x+width-1, height)); |
| } |
| } |
| |
| return s; |
| } |
| |
| static const char *draw_text_right(char *line, rect r, int line_height, |
| int underline, const char *s) |
| { |
| char *k; |
| int w; |
| point p; |
| int width, height; |
| font f; |
| |
| f = current->fnt; |
| |
| for(p=pt(r.x,r.y); (p.y<=r.y+r.height) && (s); p.y+=line_height) |
| { |
| s = get_next_line(line, r.width, s); |
| |
| for (k=line; *k!='\0'; k++) |
| continue; |
| for (--k; (k>=line) && isspace(*k); k--) |
| *k = '\0'; |
| for (k=line; (*k!='\0') && isspace(*k); k++) |
| continue; |
| |
| w = strwidth(current->fnt, k); |
| p.x = r.x+r.width - w; |
| |
| drawstr(p, k); |
| |
| if (underline) { |
| width = strwidth(f, k); |
| height = p.y+getheight(f)-getdescent(f)+2; |
| drawline(pt(p.x+1, height), pt(p.x+width-1, height)); |
| } |
| } |
| |
| return s; |
| } |
| |
| static const char *draw_text_centered(char *line, rect r, int line_height, |
| int underline, const char *s) |
| { |
| char *k; |
| int w; |
| point p; |
| int width, height; |
| font f; |
| |
| f = current->fnt; |
| |
| for(p=pt(r.x,r.y); (p.y<=r.y+r.height) && (s); p.y+=line_height) |
| { |
| s = get_next_line(line, r.width, s); |
| |
| for (k=line; *k!='\0'; k++) |
| continue; |
| for (--k; (k>=line) && isspace(*k); k--) |
| *k = '\0'; |
| for(k=line; (*k!='\0') && isspace(*k); k++) |
| continue; |
| |
| w = strwidth(current->fnt, k); |
| p.x = r.x + (r.width-w)/2; |
| |
| drawstr(p, k); |
| |
| if (underline) { |
| width = strwidth(f, k); |
| height = p.y+getheight(f)-getdescent(f)+2; |
| drawline(pt(p.x+1, height), pt(p.x+width-1, height)); |
| } |
| } |
| |
| return s; |
| } |
| |
| static const char *draw_text_justified(char *line, rect r, int line_height, |
| int underline, const char *s) |
| { |
| char *j, *k; |
| int w, xw, nl, sc, sw, space_width; |
| point p; |
| int width, height; |
| font f; |
| |
| space_width = strwidth(current->fnt, " "); |
| f = current->fnt; |
| |
| for(p=pt(r.x,r.y); (p.y<=r.y+r.height) && (s); p.y+=line_height) |
| { |
| s = get_next_line(line, r.width, s); |
| |
| p.x = r.x; |
| |
| for(j=line; (*j!='\0') && isspace(*j); j++) |
| p.x += space_width; |
| for (sc=0, k=j; *k!='\0'; k++) |
| if (isspace(*k)) |
| sc++; |
| for (nl=0, --k; (k>=j) && isspace(*k); k--) { |
| if (*k == '\n') |
| nl++; |
| *k = '\0'; |
| sc--; |
| } |
| |
| if ((sc==0) || nl || (! s)) { |
| drawstr(p, j); |
| width = strwidth(f, j); |
| } |
| else { |
| w = strwidth(f, j); |
| sw = space_width + (r.x+r.width-p.x-w)/sc; |
| xw = (r.x+r.width-p.x-w)%sc; |
| |
| for(j=strtok(j," "); j; j=strtok(NULL," ")) |
| { |
| drawstr(p, j); |
| p.x += sw + strwidth(f, j); |
| if (xw) { |
| p.x++; |
| xw--; |
| } |
| } |
| width = r.width; |
| } |
| if (underline) { |
| height = p.y+getheight(f)-getdescent(f)+2; |
| drawline(pt(p.x+1, height), pt(p.x+width-1, height)); |
| } |
| } |
| |
| return s; |
| } |
| |
| const char *drawtext(rect r, int alignment, const char *s) |
| { |
| int h; |
| int nlines; |
| int line_height; |
| int u = 0; |
| rect clip; |
| const char *remains; |
| char line[256]; |
| |
| initapp(0,0); |
| if (! s) return (char *) NULL; |
| if (! current->fnt) |
| current->fnt = SystemFont; |
| |
| clip = getcliprect(); |
| setcliprect(r); |
| |
| line_height = getheight(current->fnt); |
| |
| if ((alignment & VCenter) == VCenter) { |
| h = textheight(r.width, s); |
| if (h < r.height) |
| r.y += (r.height-h)/2; |
| } |
| else if ((alignment & VJustify) == VJustify) { |
| h = textheight(r.width, s); |
| if (h < r.height) { |
| nlines = h / line_height; |
| if (nlines > 1) |
| line_height += ((r.height-h) / (nlines-1)); |
| } |
| } |
| else if ((alignment & AlignBottom) == AlignBottom) { |
| h = textheight(r.width, s); |
| if (h < r.height) |
| r.y += (r.height-h); |
| } |
| |
| u = (alignment & Underline); |
| |
| if ((alignment & Center) == Center) |
| remains = draw_text_centered(line, r, line_height, u, s); |
| else if ((alignment & Justify) == Justify) |
| remains = draw_text_justified(line, r, line_height, u, s); |
| else if ((alignment & AlignRight) == AlignRight) |
| remains = draw_text_right(line, r, line_height, u, s); |
| else |
| remains = draw_text_left(line, r, line_height, u, s); |
| |
| setcliprect(clip); |
| return remains; |
| } |
| |
| int gprintf(const char *fmt, ...) |
| { |
| static point p = {0,0}; |
| int count; |
| int line_height; |
| char *s, *t; |
| va_list argptr; |
| char str[256]; |
| |
| va_start(argptr, fmt); |
| count = vsprintf(str, fmt, argptr); |
| |
| initapp(0,0); |
| if (! current->fnt) |
| current->fnt = SystemFont; |
| line_height = getheight(current->fnt); |
| |
| for (s=t=str; *s!='\0'; t++) { |
| if (current->p.y != p.y) { |
| /* typewriter ping! */ |
| p = current->p; |
| } |
| if (*t == '\n') { |
| /* print everything from s to t and move point down */ |
| *t = '\0'; |
| drawstr(p, s); |
| current->p.y += line_height; |
| /* go past the substring just printed */ |
| s = t+1; |
| } |
| else if (*t == '\0') { |
| /* print final string without newline */ |
| p.x += drawstr(p, s); |
| /* go to end of string, signal termination */ |
| s = t; |
| } |
| } |
| |
| va_end(argptr); |
| |
| return count; |
| } |