/* * Copyright (C) 1999 Peter Amstutz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of *the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ /* On-screen text handling */ #include #include #include #include #include "text.h" #include "gfx.h" #include "font5.xpm" ggi_visual_t txt_fntvis; Char_fnt txt_chars[256]; int txtCharWidth(int x, int y) { unsigned n, m; ggi_pixel col; for(n = 0; n < 20; n++) { for(m = 0; m < 20; m++) { ggiGetPixel(txt_fntvis, x + n, y + m, &col); if(col != 1) { break; } } if(m == 20) return n; } return n; } void txtInit() { unsigned i; txt_fntvis = gfxXPMtoMemvis(font5_xpm); memset(txt_chars, 0, sizeof(txt_chars)); for(i = 0; i < 13; i++) { txt_chars['a' + i].x = i * 20; txt_chars['a' + i].y = 0; txt_chars['a' + i].w = txtCharWidth(i * 20, 0); } for(i = 0; i < 13; i++) { txt_chars['n' + i].x = i * 20; txt_chars['n' + i].y = 20; txt_chars['n' + i].w = txtCharWidth(i * 20, 20); } for(i = 0; i < 13; i++) { txt_chars['A' + i].x = i * 20; txt_chars['A' + i].y = 40; txt_chars['A' + i].w = txtCharWidth(i * 20, 40); } for(i = 0; i < 13; i++) { txt_chars['N' + i].x = i * 20; txt_chars['N' + i].y = 60; txt_chars['N' + i].w = txtCharWidth(i * 20, 60); } for(i = 0; i < 13; i++) { txt_chars['!' + i].x = i * 20; txt_chars['!' + i].y = 80; txt_chars['!' + i].w = txtCharWidth(i * 20, 80); } for(i = 0; i < 13; i++) { txt_chars['.' + i].x = i * 20; txt_chars['.' + i].y = 100; txt_chars['.' + i].w = txtCharWidth(i * 20, 100); } for(i = 0; i < 6; i++) { txt_chars[';' + i].x = i * 20; txt_chars[';' + i].y = 120; txt_chars[';' + i].w = txtCharWidth(i * 20, 120); } for(i = 0; i < 6; i++) { txt_chars['[' + i].x = (i + 6) * 20; txt_chars['[' + i].y = 120; txt_chars['[' + i].w = txtCharWidth((i + 6) * 20, 120); } txt_chars['{'].x = 240; txt_chars['{'].y = 120; txt_chars['{'].w = txtCharWidth(240, 140); for(i = 0; i < 3; i++) { txt_chars['|' + i].x = i * 20; txt_chars['|' + i].y = 140; txt_chars['|' + i].w = txtCharWidth(i * 20, 140); } for(i = 0; i < 2; i++) /* arrow chars */ { txt_chars['\020' + i].x = (i + 3) * 20; txt_chars['\020' + i].y = 140; txt_chars['\020' + i].w = txtCharWidth((i + 3) * 20, 140); } txt_chars[' '].x = 100; txt_chars[' '].y = 140; txt_chars[' '].w = 8; } void txtPuts(int sx, int sy, char *msg) { int x, y; ggi_pixel fg; ggi_color src1; static ggi_pixel pixels[20], pixels2[20], pixels3[20]; static ggi_color colors[20], colors2[20], colors3[20]; ggiGetGCForeground(gfx_vis, &fg); ggiUnmapPixel(gfx_vis, fg, &src1); for(; *msg; msg++, sx++) { for(x = 0; x < txt_chars[(int) *msg].w; x++, sx++) { /* we've embedded an alpha channel into our data, * so we can now do nice compositing with our * text and have it look good against any * background. Formula is from * _Computer Graphics: Principals and Practice_ 2nd ed * by Foley, van Dam, et al * section 17.6.1 */ ggiGetVLine(gfx_vis, sx, sy, 20, &pixels); ggiUnpackPixels(gfx_vis, pixels, colors, 20); ggiGetVLine(txt_fntvis, txt_chars[(int) *msg].x + x, txt_chars[(int) *msg].y, 20, &pixels2); ggiUnpackPixels(txt_fntvis, pixels2, colors2, 20); for(y = 0; y < 20; y++) { if(pixels2[y] != 1) { colors3[y].r = ((src1.r - colors[y].r) * colors2[y].r) / 65536.0 + colors[y].r; colors3[y].g = ((src1.g - colors[y].g) * colors2[y].g) / 65536.0 + colors[y].g; colors3[y].b = ((src1.b - colors[y].b) * colors2[y].b) / 65536.0 + colors[y].b; } else { colors3[y].r = colors[y].r; colors3[y].g = colors[y].g; colors3[y].b = colors[y].b; } } ggiPackColors(gfx_vis, pixels3, colors3, 20); ggiPutVLine(gfx_vis, sx, sy, 20, &pixels3); } } } void txtPrintf(int sx, int sy, char *fmt, ...) { va_list ap; char buf[4096]; va_start(ap, fmt); vsprintf(buf, fmt, ap); txtPuts(sx, sy, buf); va_end(ap); } ScrollWindow_txt *txtMakeScrollWindow(int x, int y, int w, int h) { ScrollWindow_txt *ret = (ScrollWindow_txt *) malloc(sizeof(ScrollWindow_txt)); ret->x = x; ret->y = y; ret->w = w; ret->h = h; ret->rows = h / 20; ret->currow = 0; return ret; } void txtClearScrollWindow(ScrollWindow_txt * sw) { ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, sw->x, sw->y, sw->w, sw->h); sw->currow = 0; } int txtStringWidth(char *str) { unsigned len; for(len = 0; *str; str++) len += txt_chars[(int) *str].w + 1; return len; } int txtStringWidthFindOverrun(char *str, int max) { unsigned len, i; for(len = 0, i = 0; str[i]; i++) { if(len + txt_chars[(int) str[i]].w + 1 > max) break; else len += txt_chars[(int) str[i]].w + 1; } return i; } int txtStringWidthFindOverrunBackwards(char *str, int start, int max) { unsigned len, i; assert(start >= 0); for(len = 0, i = start; i > 0; i--) { if(len + txt_chars[(int) str[i]].w + 1 > max) break; else len += txt_chars[(int) str[i]].w + 1; } return i; } void txtScrollWindowPrintf(ScrollWindow_txt * sw, char *fmt, ...) { va_list ap; char buf[4096]; ggi_pixel *movebox; ggi_pixel textcol; unsigned n; ggiGetGCForeground(gfx_vis, &textcol); va_start(ap, fmt); vsprintf(buf, fmt, ap); if(txtStringWidth(buf) > sw->w) { n = txtStringWidthFindOverrun(buf, sw->w); txtScrollWindowPrintf(sw, "%.*s", n, buf); txtScrollWindowPrintf(sw, buf + n); } else { if(sw->currow < sw->rows) { txtPuts(sw->x, sw->y + 20 * sw->currow, buf); sw->currow++; } else { movebox = (ggi_pixel *) malloc(sw->w * sw->h * sizeof(ggi_pixel)); ggiGetBox(gfx_vis, sw->x, sw->y + 20, sw->w, 20 * (sw->rows - 1), movebox); ggiPutBox(gfx_vis, sw->x, sw->y, sw->w, 20 * (sw->rows - 1), movebox); free(movebox); ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, sw->x, sw->y + 20 * (sw->rows - 1), sw->w, 20); ggiSetGCForeground(gfx_vis, textcol); txtPuts(sw->x, sw->y + 20 * (sw->rows - 1), buf); } } va_end(ap); } InputBox_txt *txtMakeInputBox(int x, int y, int w, int h, int start, char *outstr, int maxlen) { InputBox_txt *ret = (InputBox_txt *) malloc(sizeof(InputBox_txt)); ret->x = x; ret->y = y; ret->w = w; ret->h = h; ret->curpos = start; ret->string = outstr; ret->maxlen = maxlen; return ret; } void txtDoInputBox(InputBox_txt * ib, ggi_event * ev) { char str[2]; ggi_pixel textcol; ggiGetGCForeground(gfx_vis, &textcol); switch (ev->any.type) { case evKeyPress: case evKeyRepeat: switch (ev->key.sym) { case GIIUC_BackSpace: case GIIUC_Delete: if(ib->curpos > 0) { ib->curpos--; ib->string[ib->curpos] = 0; ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, ib->x + txtStringWidth(ib->string), ib->y, 40, 20); ggiSetGCForeground(gfx_vis, textcol); txtPuts(ib->x + txtStringWidth(ib->string), ib->y, "_"); } break; default: if(ib->curpos < ib->maxlen && txt_chars[GII_KVAL(ev->key.sym)].w > 0 && txtStringWidth(ib->string) + txt_chars['_'].w < ib->w) { ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, ib->x + txtStringWidth(ib->string), ib->y, 20, 20); str[0] = GII_KVAL(ev->key.sym); str[1] = 0; ggiSetGCForeground(gfx_vis, textcol); txtPuts(ib->x + txtStringWidth(ib->string), ib->y, str); ib->string[ib->curpos] = GII_KVAL(ev->key.sym); ib->curpos++; ib->string[ib->curpos] = 0; txtPuts(ib->x + txtStringWidth(ib->string), ib->y, "_"); } break; } } } void txtClearInputBox(InputBox_txt * ib) { ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, ib->x, ib->y, txtStringWidth(ib->string) + txt_chars['_'].w, 20); ib->string[0] = 0; ib->curpos = 0; txtPuts(ib->x, ib->y, "_"); } ListBox_txt *txtMakeListBox(int x, int y, int w, int h, int (*activatefunc) (void *elem), int (*deactivatefunc) (void *elem), void (*redrawfunc) (int x, int y, int w, int h, void *elem)) { ListBox_txt *ret = (ListBox_txt *) malloc(sizeof(ListBox_txt)); ret->x = x; ret->y = y; ret->w = w; ret->h = h; ret->top = NULL; ret->current = NULL; ret->activatefunc = activatefunc; ret->deactivatefunc = deactivatefunc; ret->redrawfunc = redrawfunc; for(ret->sy = y + h / 2; ret->sy - 20 >= y; ret->sy -= 20) ; for(ret->sh = h / 2; ret->sh + 40 <= h; ret->sh += 20) ; ret->sh = (ret->sh / 20) * 20; return ret; } void txtAddToListBox(ListBox_txt * lb, void *elem) { if(lb->top == NULL) { lb->top = (ListBoxElements_txt *) malloc(sizeof(ListBoxElements_txt)); lb->top->next = NULL; lb->current = lb->top; } else { lb->top->prev = (ListBoxElements_txt *) malloc(sizeof(ListBoxElements_txt)); lb->top->prev->next = lb->top; lb->top = lb->top->prev; } lb->top->prev = NULL; lb->top->elem = elem; } void txtDrawListBox(ListBox_txt * lb) { int y; ListBoxElements_txt *le; if(lb->top == NULL) return; for(y = lb->y + lb->h / 2, le = lb->current; y >= lb->y && le; y -= 20, le = le->prev) { lb->redrawfunc(lb->x, y, lb->w, 20, le->elem); } for(y = lb->y + lb->h / 2 + 20, le = lb->current->next; y + 20 < lb->y + lb->h && le; y += 20, le = le->next) { lb->redrawfunc(lb->x, y, lb->w, 20, le->elem); } } void txtDoListBox(ListBox_txt * lb, ggi_event * ev) { /* ggi_pixel* movebox; */ unsigned char *movebox; int y; ListBoxElements_txt *el; switch (ev->any.type) { case evKeyPress: case evKeyRepeat: switch (ev->key.sym) { case GIIK_Up: if(lb->current->prev != NULL) { movebox = (unsigned char *) malloc(lb->w * lb->h * sizeof(ggi_pixel)); ggiGetBox(gfx_vis, lb->x, lb->sy, lb->w, lb->sh - 20, movebox); ggiPutBox(gfx_vis, lb->x, lb->sy + 20, lb->w, lb->sh - 20, movebox); free(movebox); for(y = lb->y + lb->h / 2, el = lb->current; y - 20 >= lb->sy && el; y -= 20, el = el->prev) ; ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, lb->x, lb->sy, lb->w, y - lb->sy + 20); if(el != NULL && el->prev != NULL) { lb->redrawfunc(lb->x, y, lb->w, 20, el->prev->elem); } lb->current = lb->current->prev; /* for(y=lb->y + lb->h/2, el=lb->current; y-20 > lb->y && el; y-=20, el=el->prev); if(el!=NULL) { lb->redrawfunc(lb->x, y, lb->w, 20, el->elem); } lb->current=lb->current->prev; */ } break; case GIIK_Down: if(lb->current->next != NULL) { movebox = (unsigned char *) malloc(lb->w * lb->h * sizeof(ggi_pixel)); ggiGetBox(gfx_vis, lb->x, lb->sy + 20, lb->w, lb->sh - 20, movebox); ggiPutBox(gfx_vis, lb->x, lb->sy, lb->w, lb->sh - 20, movebox); free(movebox); for(y = lb->y + lb->h / 2, el = lb->current; y + 40 <= lb->y + lb->h - 20 && el; y += 20, el = el->next) ; ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, lb->x, y, lb->w, lb->sy + lb->sh - y); if(el != NULL && el->next != NULL) { lb->redrawfunc(lb->x, y, lb->w, 20, el->next->elem); } lb->current = lb->current->next; } break; case GIIK_Right: if(lb->activatefunc(lb->current->elem)) { ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, lb->x, lb->y + lb->h / 2, lb->w, 20); lb->redrawfunc(lb->x, lb->y + lb->h / 2, lb->w, 20, lb->current->elem); } break; case GIIK_Left: if(lb->deactivatefunc(lb->current->elem)) { ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, lb->x, lb->y + lb->h / 2, lb->w, 20); lb->redrawfunc(lb->x, lb->y + lb->h / 2, lb->w, 20, lb->current->elem); } break; } } }