/* HEBUFFER.C Text-buffer management for the Hugo Engine Copyright (c) 2002-2006 by Kent Tessman To use text-buffer management: 1. #define USE_TEXTBUFFER (probably in the appropriate section in heheader.h). 2. Add a call to TB_Scroll() in hugo_scrollwindowup(). 3. Add calls to TB_Clear() in hugo_clearwindow() and hugo_clearfullscreen(), as appropriate (as well as wherever else the port may clear a portion or all of the display without going through the hugo_*() functions). 4. Make sure the port manages current_text_x and (especially) current_text_y with regard to positioning and printing text. Then: 5. Call TB_Find() to get the word at a given location. NOTES: All TB_*() calls are done in individual units, i.e., in characters if the port is character-based or in pixels if the port is pixel-based. */ #include "heheader.h" #ifdef USE_TEXTBUFFER #ifndef MAX_TEXTBUFFER_COUNT #define MAX_TEXTBUFFER_COUNT 1000 #endif #define TB_NONE (-1) #define WINDOW_CELL " _win" char allow_text_selection = true; int tb_first_used; /* indexes */ int tb_first_unused; int tb_last_used; int tb_last_unused; int tb_used = 0; /* counts */ int tb_unused; typedef struct { char *data; int left, top, right, bottom; int prev, next; #ifdef TEXTBUFFER_FORMATTING int font, fcolor, bgcolor; #endif } tb_list_struct; tb_list_struct tb_list[MAX_TEXTBUFFER_COUNT]; int tb_selected; /* TB_Init() Initializes text-buffer management. */ void TB_Init() { int i; for (i=0; i= MAX_TEXTBUFFER_COUNT) { #ifdef TB_DEBUG printf("TB_AddWord(): MAX_TEXTBUFFER_COUNT reached; removing tb_first_used\n"); #endif TB_Remove(tb_first_used); } /* Get the cell we're going to add */ n = tb_first_unused; /* Try to allocate storage for the word */ if (!(tb_list[n].data = hugo_blockalloc(strlen(w)+1))) { /* If we fail, try to free older used cells */ while (tb_used) { #ifdef TB_DEBUG printf("TB_AddWord(): Initial alloc failed (tb_used = %d)\n", tb_used); #endif TB_Remove(tb_first_used); if ((tb_list[n].data = hugo_blockalloc(strlen(w)+1))) break; } if (tb_used==0) { #ifdef TB_DEBUG printf("TB_AddWord(): Unable to allocate for '%s'\n", w); #endif return TB_NONE; } } /* Take the cell out of the unused list */ if (tb_list[n].prev != TB_NONE) tb_list[tb_list[n].prev].next = tb_list[n].next; if (tb_list[n].next != TB_NONE) tb_list[tb_list[n].next].prev = tb_list[n].prev; if (n==tb_first_unused) tb_first_unused = tb_list[n].next; if (n==tb_last_unused) tb_last_unused = tb_list[n].prev; tb_unused--; /* Move it to the used list */ tb_list[n].prev = tb_last_used; if (tb_last_used != TB_NONE) tb_list[tb_last_used].next = n; tb_list[n].next = TB_NONE; if (tb_used==0) tb_first_used = n; tb_last_used = n; tb_used++; /* Set its coordinates */ /* Just using strcpy() will let font/color change codes through */ /* strcpy(tb_list[n].data, w); */ c = 0; for (i=0; i<(int)strlen(w); i++) { if ((unsigned char)w[i]>=' ') tb_list[n].data[c++] = w[i]; } tb_list[n].data[c] = '\0'; tb_list[n].left = left; tb_list[n].top = top; tb_list[n].right = right; tb_list[n].bottom = bottom; #ifdef TEXTBUFFER_FORMATTING tb_list[n].font = currentfont; tb_list[n].fcolor = fcolor; tb_list[n].bgcolor = bgcolor; #endif #ifdef TB_DEBUG printf("TB_AddWord(): Added tb_list[%d]: '%s' (%d, %d, %d, %d), tb_used=%d\n", n, w, left, top, right, bottom, tb_used); #endif return n; } /* TB_AddWin(left, top, right, bottom) Adds a window area. Generally only useful when TEXTBUFFER_FORMATTING is #defined. */ int TB_AddWin(int left, int top, int right, int bottom) { return TB_AddWord(WINDOW_CELL, left, top, right, bottom); } /* TB_InBounds(n, left, top, right, bottom) Returns true if tb_list[n] is even partially within the given boundaries. */ char TB_InBounds(int n, int left, int top, int right, int bottom) { if ((tb_list[n].left>=left && tb_list[n].left<=right && tb_list[n].top>=top && tb_list[n].top<=bottom) || (tb_list[n].right>=left && tb_list[n].right<=right && tb_list[n].bottom>=top && tb_list[n].bottom<=bottom)) { return true; } return false; } /* TB_Clear(left, top, right, bottom) Removes all cells within the given boundaries. */ void TB_Clear(int left, int top, int right, int bottom) { int i = tb_first_used; /* right may be <0 when called from Printout() with only control characters in the string */ if (right < 0) return; #ifdef TB_DEBUG printf("TB_Clear(%d, %d, %d, %d)\n", left, top, right, bottom); #endif while (i!=TB_NONE) { /* Have to find out what the next cell is before we remove this current one */ int next = tb_list[i].next; if (TB_InBounds(i, left, top, right, bottom)) { TB_Remove(i); i = next; continue; } if (tb_list[i].top < physical_windowtop || tb_list[i].bottom<0) { TB_Remove(i); } i = next; } } /* TB_Scroll(left, top, right, bottom, size) Scrolls all cells within the given window up by units. */ void TB_Scroll() { int i; i = tb_first_used; while (i!=TB_NONE) { int next = tb_list[i].next; /* Remove first the cells which will be scrolled away */ if (TB_InBounds(i, physical_windowleft, physical_windowtop, physical_windowright, physical_windowtop+lineheight/2)) { #ifdef TB_DEBUG printf("TB_Scroll(): "); #endif TB_Remove(i); } /* Then scroll up the remaining cells */ if (TB_InBounds(i, physical_windowleft, physical_windowtop+lineheight, physical_windowright, physical_windowbottom)) { #ifdef TB_DEBUG // printf("TB_Scroll(): scrolling tb_list[%d]: '%s'\n", i, tb_list[i].data); #endif tb_list[i].top -= lineheight; tb_list[i].bottom -= lineheight; /* Don't scroll areas into the invalidation zone until the bottom is invalidated */ if ((tb_list[i].data) && !strcmp(tb_list[i].data, WINDOW_CELL) && tb_list[i].top<0 && tb_list[i].bottom > tb_list[i].top) { tb_list[i].top = 0; } } i = next; } } /* TB_FindWord(x, y) Returns the (first) word found at (x, y). Returns NULL if no word is found. */ char *TB_FindWord(int x, int y) { int i; tb_selected = TB_NONE; if (!allow_text_selection) return NULL; for (i=tb_last_used; i!=TB_NONE; i=tb_list[i].prev) { if (x >= tb_list[i].left && x <=tb_list[i].right && y >= tb_list[i].top && y <= tb_list[i].bottom) { static char buf[255]; char instring = false; int n = 0, len; char *w = tb_list[i].data; char c; if (!strcmp(w, WINDOW_CELL)) return NULL; tb_selected = i; len = strlen(w); for (i=0; i='0' && c<='9') || (unsigned char)c>='A') { #ifdef USE_SMARTFORMATTING if (smartformatting) { switch ((unsigned char)c) { case 145: case 146: c = '\''; break; case 147: case 148: c = '\"'; break; case 151: buf[n++] = '-'; c = '-'; break; } } #endif buf[n++] = c; instring = true; } else if ((unsigned char)c>=' ' && instring) { buf[n++] = c; } } buf[n] = '\0'; /* Strip off any trailing punctuation */ i = strlen(buf); for (; i; i--) { if ((buf[i]>='0' && buf[i]<='9') || (unsigned char)buf[i]>='A') break; buf[i] = '\0'; } if (n==0) { tb_selected = TB_NONE; return NULL; } #ifdef TB_DEBUG printf("TB_FindWord(%d, %d) = '%s'\n", x, y, w); #endif return buf; } } #ifdef TB_DEBUG printf("TB_FindWord(%d, %d): no match\n", x, y); #endif return NULL; } #endif /* USE_TEXTBUFFER */