ref: a378d0288a77ef8efff439c5e250e82b03e2c502
dir: /textscreen/txt_gui.c/
// // Copyright(C) 2005-2014 Simon Howard // // 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. // #include <stdlib.h> #include <string.h> #include "txt_gui.h" #include "txt_io.h" #include "txt_main.h" #include "txt_utf8.h" typedef struct txt_cliparea_s txt_cliparea_t; struct txt_cliparea_s { int x1, x2; int y1, y2; txt_cliparea_t *next; }; // Array of border characters for drawing windows. The array looks like this: // // +-++ // | || // +-++ // +-++ static const int borders[4][4] = { {0xda, 0xc4, 0xc2, 0xbf}, {0xb3, ' ', 0xb3, 0xb3}, {0xc3, 0xc4, 0xc5, 0xb4}, {0xc0, 0xc4, 0xc1, 0xd9}, }; static txt_cliparea_t *cliparea = NULL; #define VALID_X(x) ((x) >= cliparea->x1 && (x) < cliparea->x2) #define VALID_Y(y) ((y) >= cliparea->y1 && (y) < cliparea->y2) void TXT_DrawDesktopBackground(const char *title) { int i; unsigned char *screendata; unsigned char *p; screendata = TXT_GetScreenData(); // Fill the screen with gradient characters p = screendata; for (i=0; i<TXT_SCREEN_W * TXT_SCREEN_H; ++i) { *p++ = 0xb1; *p++ = TXT_COLOR_GREY | (TXT_COLOR_BLUE << 4); } // Draw the top and bottom banners p = screendata; for (i=0; i<TXT_SCREEN_W; ++i) { *p++ = ' '; *p++ = TXT_COLOR_BLACK | (TXT_COLOR_GREY << 4); } p = screendata + (TXT_SCREEN_H - 1) * TXT_SCREEN_W * 2; for (i=0; i<TXT_SCREEN_W; ++i) { *p++ = ' '; *p++ = TXT_COLOR_BLACK | (TXT_COLOR_GREY << 4); } // Print the title TXT_GotoXY(0, 0); TXT_FGColor(TXT_COLOR_BLACK); TXT_BGColor(TXT_COLOR_GREY, 0); TXT_DrawString(" "); TXT_DrawString(title); } void TXT_DrawShadow(int x, int y, int w, int h) { unsigned char *screendata; unsigned char *p; int x1, y1; screendata = TXT_GetScreenData(); for (y1=y; y1<y+h; ++y1) { p = screendata + (y1 * TXT_SCREEN_W + x) * 2; for (x1=x; x1<x+w; ++x1) { if (VALID_X(x1) && VALID_Y(y1)) { p[1] = TXT_COLOR_DARK_GREY; } p += 2; } } } void TXT_DrawWindowFrame(const char *title, int x, int y, int w, int h) { txt_saved_colors_t colors; int x1, y1; int bx, by; TXT_SaveColors(&colors); TXT_FGColor(TXT_COLOR_BRIGHT_CYAN); for (y1=y; y1<y+h; ++y1) { // Select the appropriate row and column in the borders // array to pick the appropriate character to draw at // this location. // // Draw a horizontal line on the third line down, so we // draw a box around the title. by = y1 == y ? 0 : y1 == y + 2 && title != NULL ? 2 : y1 == y + h - 1 ? 3 : 1; for (x1=x; x1<x+w; ++x1) { bx = x1 == x ? 0 : x1 == x + w - 1 ? 3 : 1; if (VALID_X(x1) && VALID_Y(y1)) { TXT_GotoXY(x1, y1); TXT_PutChar(borders[by][bx]); } } } // Draw the title if (title != NULL) { TXT_GotoXY(x + 1, y + 1); TXT_BGColor(TXT_COLOR_GREY, 0); TXT_FGColor(TXT_COLOR_BLUE); for (x1=0; x1<w-2; ++x1) { TXT_DrawString(" "); } TXT_GotoXY(x + (w - TXT_UTF8_Strlen(title)) / 2, y + 1); TXT_DrawString(title); } // Draw the window's shadow. TXT_DrawShadow(x + 2, y + h, w, 1); TXT_DrawShadow(x + w, y + 1, 2, h); TXT_RestoreColors(&colors); } void TXT_DrawSeparator(int x, int y, int w) { txt_saved_colors_t colors; unsigned char *data; int x1; int b; data = TXT_GetScreenData(); TXT_SaveColors(&colors); TXT_FGColor(TXT_COLOR_BRIGHT_CYAN); if (!VALID_Y(y)) { return; } data += (y * TXT_SCREEN_W + x) * 2; for (x1=x; x1<x+w; ++x1) { TXT_GotoXY(x1, y); b = x1 == x ? 0 : x1 == x + w - 1 ? 3 : 1; if (VALID_X(x1)) { // Read the current value from the screen // Check that it matches what the window should look like if // there is no separator, then apply the separator if (*data == borders[1][b]) { TXT_PutChar(borders[2][b]); } } data += 2; } TXT_RestoreColors(&colors); } // Alternative to TXT_DrawString() where the argument is a "code page // string" - characters are in native code page format and not UTF-8. void TXT_DrawCodePageString(const char *s) { int x, y; int x1; const char *p; TXT_GetXY(&x, &y); if (VALID_Y(y)) { x1 = x; for (p = s; *p != '\0'; ++p) { if (VALID_X(x1)) { TXT_GotoXY(x1, y); TXT_PutChar(*p); } x1 += 1; } } TXT_GotoXY(x + strlen(s), y); } static void PutUnicodeChar(unsigned int c) { int d; // Treat control characters specially. if (c == '\n' || c == '\b') { TXT_PutChar(c); return; } // Map Unicode character into the symbol used to represent it in this // code page. For unrepresentable characters, print a fallback instead. // Note that we use TXT_PutSymbol() here because we just want to do a // raw write into the screen buffer. d = TXT_UnicodeCharacter(c); if (d >= 0) { TXT_PutSymbol(d); } else { TXT_PutSymbol('\xa8'); } } void TXT_DrawString(const char *s) { int x, y; int x1; const char *p; unsigned int c; TXT_GetXY(&x, &y); if (VALID_Y(y)) { x1 = x; for (p = s; *p != '\0'; ) { c = TXT_DecodeUTF8(&p); if (c == 0) { break; } if (VALID_X(x1)) { TXT_GotoXY(x1, y); PutUnicodeChar(c); } x1 += 1; } } TXT_GotoXY(x + TXT_UTF8_Strlen(s), y); } void TXT_DrawHorizScrollbar(int x, int y, int w, int cursor, int range) { txt_saved_colors_t colors; int x1; int cursor_x; if (!VALID_Y(y)) { return; } TXT_SaveColors(&colors); TXT_FGColor(TXT_COLOR_BLACK); TXT_BGColor(TXT_COLOR_GREY, 0); TXT_GotoXY(x, y); TXT_PutChar('\x1b'); cursor_x = x + 1; if (range > 0) { cursor_x += (cursor * (w - 3)) / range; } if (cursor_x > x + w - 2) { cursor_x = x + w - 2; } for (x1=x+1; x1<x+w-1; ++x1) { if (VALID_X(x1)) { if (x1 == cursor_x) { TXT_PutChar('\xdb'); } else { TXT_PutChar('\xb1'); } } } TXT_PutChar('\x1a'); TXT_RestoreColors(&colors); } void TXT_DrawVertScrollbar(int x, int y, int h, int cursor, int range) { txt_saved_colors_t colors; int y1; int cursor_y; if (!VALID_X(x)) { return; } TXT_SaveColors(&colors); TXT_FGColor(TXT_COLOR_BLACK); TXT_BGColor(TXT_COLOR_GREY, 0); TXT_GotoXY(x, y); TXT_PutChar('\x18'); cursor_y = y + 1; if (cursor_y > y + h - 2) { cursor_y = y + h - 2; } if (range > 0) { cursor_y += (cursor * (h - 3)) / range; } for (y1=y+1; y1<y+h-1; ++y1) { if (VALID_Y(y1)) { TXT_GotoXY(x, y1); if (y1 == cursor_y) { TXT_PutChar('\xdb'); } else { TXT_PutChar('\xb1'); } } } TXT_GotoXY(x, y + h - 1); TXT_PutChar('\x19'); TXT_RestoreColors(&colors); } void TXT_InitClipArea(void) { if (cliparea == NULL) { cliparea = malloc(sizeof(txt_cliparea_t)); cliparea->x1 = 0; cliparea->x2 = TXT_SCREEN_W; cliparea->y1 = 0; cliparea->y2 = TXT_SCREEN_H; cliparea->next = NULL; } } void TXT_PushClipArea(int x1, int x2, int y1, int y2) { txt_cliparea_t *newarea; newarea = malloc(sizeof(txt_cliparea_t)); // Set the new clip area to the intersection of the old // area and the new one. newarea->x1 = cliparea->x1; newarea->x2 = cliparea->x2; newarea->y1 = cliparea->y1; newarea->y2 = cliparea->y2; if (x1 > newarea->x1) newarea->x1 = x1; if (x2 < newarea->x2) newarea->x2 = x2; if (y1 > newarea->y1) newarea->y1 = y1; if (y2 < newarea->y2) newarea->y2 = y2; #if 0 printf("New scrollable area: %i,%i-%i,%i\n", x1, y1, x2, y2); #endif // Hook into the list newarea->next = cliparea; cliparea = newarea; } void TXT_PopClipArea(void) { txt_cliparea_t *next_cliparea; // Never pop the last entry if (cliparea->next == NULL) return; // Unlink the last entry and delete next_cliparea = cliparea->next; free(cliparea); cliparea = next_cliparea; }