/* * WINDOW.C * * Written by John Dennis and released to the public domain on 10-Jul-94. * * This module contains routines that maintain text windows on the * screen. Output routines are integrated in relation to the window * coordinates on the screen. All coordinates have a 0 based origin! * Please note this! * * This is the second level of abstraction from the physical device; it * should not be necessary to modify this module when porting to other * operating systems/devices. * * History: * * 10-Nov-91 JD Started. * 25-Jul-92 JD Whole package (of which this is a module) was finished * to a useable degree. * 13-Aug-92 JD Functions added during the Msged port to this system. * Should increase the packages' useability. */ #include #include #include #include #include "specch.h" #include "memextra.h" #include "keys.h" #include "winsys.h" #include "mcompile.h" #define XMOD(w) ((w->flags & INSBDR) ? 3 : ((w->flags & NBDR) ? 0 : 1)) #define YMOD(w) ((w->flags & INSBDR) ? 2 : ((w->flags & NBDR) ? 0 : 1)) /* double and single border chars */ /* unsigned char Dbdr[6] = {SC1, SC2, SC3, SC4, SC5, SC6}; unsigned char Sbdr[6] = {SC7, SC8, SC9, SC10, SC11, SC12}; */ #define Dbdr(x) SC(((x)+1)) #define Sbdr(x) SC(((x)+7)) #define Bdr(y,x) (((y) != SBDR) ? Dbdr((x)) : Sbdr((x))) int wnd_bs_127 = 0; /* Is ASCII 127 backspace on ANSI console ? */ int wnd_suppress_shadows = 0; /* do not suppress window shadows */ #ifdef UNIX int wnd_force_monochrome = 1; #else int wnd_force_monochrome = 0; /* do not enforce monochrome output */ #endif unsigned long wndid = 20; /* unique window ID */ WND *CW = NULL; /* current window */ static void WDrwBox(int x1, int y1, int x2, int y2, int Bdrt, int Battr, int ins); static char line[255]; int CheckMousePos(int x1, int y1, int x2, int y2) { int x, y; GetMouInfo(&x, &y); if (x >= x1 && x <= x2) { if (y >= y1 && y <= y2) { return 1; } } return 0; } /* * WndOpen; Creates and opens a window returning the handle to * the user, using 0 based coordinates on the screen. */ WND *WndOpen(int x1, int y1, int x2, int y2, int Bdr, int BAttr, int Attr) { WND *w; unsigned long ch; int i, k = 0; static int first_win = 1; w = xmalloc(sizeof *w); if (w == NULL) { return NULL; } if (wnd_suppress_shadows) { Bdr = Bdr & (~SHADOW); } /* sanity checks*/ if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if (x2 < x1) x2 = x1; if (y2 < y1) y2 = y1; w->wid = ++wndid; w->x1 = (unsigned char)x1; w->x2 = (unsigned char)x2; w->y1 = (unsigned char)y1; w->y2 = (unsigned char)y2; w->wattr = (unsigned char)Attr; w->battr = (unsigned char)BAttr; w->flags = (unsigned char)Bdr; w->title = NULL; if (Bdr & SHADOW) { x2 += 2; y2++; } MouseOFF(); /* get a copy of the background */ if ((!(Bdr & NOSAVE)) || (Bdr & SHADOW)) { w->buffer = xmalloc(sizeof(unsigned long *) * ((y2 - y1) + 2)); if (!w->buffer) { return NULL; } for (i = y1; i <= y2; i++) { w->buffer[k] = xmalloc(sizeof(unsigned long) * ((x2 - x1) + 2)); if (!w->buffer[k]) { return NULL; } TTReadStr(w->buffer[k], x2 - x1 + 1, i, x1); k++; } } else { w->buffer = NULL; } TTBeginOutput(); /* put out shadow */ if (Bdr & SHADOW) { k = 1; for (i = y1 + 1; i <= y2; i++) { ch = (unsigned long)w->buffer[k][x2 - x1]; ch &= 0xFF00FFFFUL; ch |= ((unsigned long)(DGREY | _BLACK) << 16); TTWriteStr(&ch, 1, i, x2); ch = (unsigned long)w->buffer[k][x2 - x1 - 1]; ch &= 0xFF00FFFFUL; ch |= ((unsigned long)(DGREY | _BLACK) << 16); TTWriteStr(&ch, 1, i, x2 - 1); k++; } k = y2 - y1; for (i = 2; i <= (x2 - x1); i++) { ch = (unsigned long)w->buffer[k][i]; ch &= 0xFF00FFFFUL; ch |= ((unsigned long)(DGREY | _BLACK) << 16); TTWriteStr(&ch, 1, y2, i + x1); } } TTScolor(Attr); if (!first_win) { TTClear(w->x1, w->y1, w->x2, w->y2); } else { first_win = 0; } if (!(Bdr & NBDR)) { if (Bdr & SBDR) { WDrwBox(w->x1, w->y1, w->x2, w->y2, SBDR, BAttr, Bdr & INSBDR ? 1 : 0); } else { WDrwBox(w->x1, w->y1, w->x2, w->y2, DBDR, BAttr, Bdr & INSBDR ? 1 : 0); } } MouseON(); TTEndOutput(); CW = w; return CW; } /* * WndPopUp; Opens up a window of the passed size in the middle of the * screen. */ WND *WndPopUp(int wid, int dep, int Bdr, int BAttr, int NAttr) { int x1, x2, y1, y2; x1 = max((term.NCol / 2) - (wid / 2) - 1, 0); y1 = max((term.NRow / 2) - (dep / 2) - 1, 0); x2 = min(x1 + wid, term.NCol - 1); y2 = min(y1 + dep, term.NRow - 1); return WndOpen(x1, y1, x2, y2, Bdr, BAttr, NAttr); } /* * WndClose; Closes the passed window and restores the screen * behind it. * * Caveats: Since no track is kept of the windows on the screen, it * is the caller's responsibility to ensure that windows are closed * in the right order. */ void WndClose(WND * w) { WND *wnd; int i, k = 0; int x2, y2; /* restore screen buffer */ if (w == NULL) { wnd = CW; } else { wnd = w; } if (wnd == NULL) { return; } x2 = wnd->x2; y2 = wnd->y2; if (wnd->flags & SHADOW) { x2 += 2; y2++; } TTBeginOutput(); MouseOFF(); if (!(wnd->flags & NOSAVE)) { for (i = wnd->y1; i <= y2; i++) { TTWriteStr(wnd->buffer[k], x2 - wnd->x1 + 1, i, wnd->x1); xfree(wnd->buffer[k++]); } xfree(wnd->buffer); } xfree(wnd); MouseON(); TTEndOutput(); } /* * WndDrwBox; Draws a box at the specified position. Internal function. */ static void WDrwBox(int x1, int y1, int x2, int y2, int Bdrt, int Battr, int ins) { unsigned int i, width; unsigned long cell, *pcell, *p; int xmod = ins ? 2 : 0; int ymod = ins ? 1 : 0; TTBeginOutput(); MouseOFF(); /* write corner chars */ cell = MAKECELL(Bdr(Bdrt,2), Battr | F_ALTERNATE); TTWriteStr(&cell, 1, y1 + ymod, x1 + xmod); cell = MAKECELL(Bdr(Bdrt,3), Battr | F_ALTERNATE); TTWriteStr(&cell, 1, y1 + ymod, x2 - xmod); cell = MAKECELL(Bdr(Bdrt,4), Battr | F_ALTERNATE); TTWriteStr(&cell, 1, y2 - ymod, x1 + xmod); cell = MAKECELL(Bdr(Bdrt,5), Battr | F_ALTERNATE); TTWriteStr(&cell, 1, y2 - ymod, x2 - xmod); /* write top & bottom horiziontal border chars */ width = (x2 - xmod) - (x1 + 1 + xmod); pcell = xmalloc(width * sizeof *pcell); p = pcell; for (i = 0; i < width; i++) { *p = MAKECELL(Bdr(Bdrt,1), Battr | F_ALTERNATE); p++; } TTWriteStr(pcell, width, y1 + ymod, x1 + 1 + xmod); TTWriteStr(pcell, width, y2 - ymod, x1 + 1 + xmod); xfree(pcell); /* write left & right vertical border chars */ cell = MAKECELL(Bdr(Bdrt,0), Battr | F_ALTERNATE); for (i = y1 + 1 + ymod; i < y2 - ymod; i++) { TTWriteStr(&cell, 1, i, x1 + xmod); TTWriteStr(&cell, 1, i, x2 - xmod); } TTEndOutput(); MouseON(); } /* * WndBox; Draws a box in the window. */ void WndBox(int x1, int y1, int x2, int y2, int Attr, int type) { int xmod, ymod; int Bdrt = (type & DBDR) ? DBDR : SBDR; if (CW == NULL) { return; } ymod = YMOD(CW); xmod = XMOD(CW); if (y1 < 0 || x1 < 0 || CW->x1 + x2 + xmod > CW->x2 || CW->y1 + y2 + ymod > CW->y2) { return; } WDrwBox(x1 + CW->x1 + xmod, y1 + CW->y1 + ymod, x2 + CW->x1 + xmod, y2 + CW->y1 + ymod, Bdrt, Attr, 0); } /* * WndGetRel; Converts absolute coordinates to coordinates relative * to the current window. */ void WndGetRel(int x, int y, int *wx, int *wy) { int xmod, ymod; if (CW == NULL) { return; } ymod = YMOD(CW); xmod = XMOD(CW); *wx = x - (CW->x1 + xmod); *wy = y - (CW->y1 + ymod); } int WndWidth(void) { if (CW == NULL) { return 0; } return CW->x2 - CW->x1 - (XMOD(CW) * 2); } /* * WndTitle; Writes a title to centre of the *current* window, * clearing whatever was there initially. * * Caveats: Doesn't check to see if there is a border, only for the * type of border. */ void WndTitle(const char *title, int Attr) { int cntr; int pos, i, len; unsigned long ch; int ymod; int Bdrt = SBDR; int m = 0; if (CW == NULL) { return; } if (title == NULL) { title = "<-- null pointer passed -->"; } cntr = (CW->x2 - CW->x1 + 1) / 2; len = strlen(title); /* 'cause were writing on the border */ if (CW->flags & INSBDR) { ymod = 1; } else { ymod = 0; } if (CheckMousePos(CW->x1, CW->y1, CW->x2, CW->y2)) { m = 1; MouseOFF(); } TTBeginOutput(); if (CW->title) { if (!(CW->flags & NBDR)) { if (CW->flags & SBDR) { Bdrt = SBDR; } else { Bdrt = DBDR; } } ch = MAKECELL(Bdr(Bdrt,1), CW->battr | F_ALTERNATE); for (i = CW->x1 + 1; i < CW->x2; i++) { TTWriteStr(&ch, 1, CW->y1 + ymod, i); } xfree(CW->title); } pos = (cntr - (len / 2)) + CW->x1; CW->title = xstrdup(title); TTScolor(Attr); TTStrWr((unsigned char *)title, CW->y1 + ymod, pos, -1); if (m) { MouseON(); } TTEndOutput(); } /* * WndWriteStr; Writes a string to the (current) window, using a * zero-based origin. * * Caveats: If string would write past end of line, it won't write * the string. */ void WndWriteStr(int x, int y, int Attr, char *Str) { int row, col, len; int m = 0; char *PrintStr = Str; if (Str == NULL) { return; } len = strlen(Str); if (CW == NULL) { return; } /* row and col must be zero-based */ row = CW->y1 + y; col = CW->x1 + x; if (x < 0 || y < 0) { return; } if (!(CW->flags & NBDR)) { /* check end-of-window overstep */ row += YMOD(CW); col += XMOD(CW); if (row >= CW->y2 || col > CW->x2 - XMOD(CW)) { return; } } else { if (row > CW->y2 || col > CW->x2) { return; } } if (CheckMousePos(col, row, col + len - 1, row)) { m = 1; MouseOFF(); } if ((col + len - 1) > CW->x2 - XMOD(CW)) { len = ((CW->x2 - XMOD(CW)) - col + 1); PrintStr = xmalloc(len + 1); memcpy(PrintStr, Str, len); PrintStr[len] = '\0'; } TTBeginOutput(); TTScolor(Attr); TTStrWr((unsigned char *)PrintStr, row, col, len); if (PrintStr != Str) { xfree(PrintStr); } TTEndOutput(); if (m) { MouseON(); } } /* * WndPutsCen; Puts a string in the center of the window at the passed * line. */ void WndPutsCen(int y, int attr, char *str) { int cntr; int col, len; int mod; if (CW == NULL) { return; } if (str == NULL) { return; } if (y < 0) { return; } cntr = (CW->x2 - CW->x1 + 1) / 2; mod = XMOD(CW); len = strlen(str); if (len > WndWidth()) { col = 0; } else { col = cntr - (len / 2) - mod; } WndWriteStr(col, y, attr, str); } /* * WndPrintf; Operates the same as printf(), except it does it to the * current window, using the passed parameters. */ int WndPrintf(int x, int y, int attr, char *str, ...) { int rc; va_list params; va_start(params, str); rc = vsprintf(line, str, params); WndWriteStr(x, y, attr, line); return rc; } /* * WndPutsn; Puts len of str onto the current window. */ void WndPutsn(int x, int y, int len, int attr, char *str) { char *s = str, *c = line; int i = 0; if (x < 0 || y < 0 || len < 1) { return; } if (len > 254) { len = 254; } if (s != NULL) { i = strlen(s); if (i > len) { i = len; } memcpy(c, s, i); } if (i < len) { memset(c + i, ' ', len - i); } c[len] = '\0'; WndWriteStr(x, y, attr, line); } /* * WndScroll; Scrolls a window at the specified cooridiates using a * zero-based origin. * */ void WndScroll(int x1, int y1, int x2, int y2, int dir) { int xmod, ymod; int m = 0; if (CW == NULL) { return; } if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if (x2 < x1 || y2 < y1) return; xmod = XMOD(CW); ymod = YMOD(CW); if (CheckMousePos(CW->x1 + x1 + xmod, CW->y1 + y1 + ymod, CW->x2 + x2 + xmod, CW->y2 + y2 + ymod)) { m = 1; MouseOFF(); } TTBeginOutput(); TTScolor(CW->wattr); TTScroll(CW->x1 + x1 + xmod, CW->y1 + y1 + ymod, CW->x1 + x2 + xmod, CW->y1 + y2 + ymod, 1, dir); TTEndOutput(); if (m) { MouseON(); } } /* * WndCurr; Makes the passed window the current window. * * Caveats: The windowing system doesn't keep track of the windows * that exist - the caller must do that. */ void WndCurr(WND * hWnd) { if (CW == hWnd || hWnd == NULL) { return; } CW = hWnd; } /* * WndTop; Returns the current window recognized by the windowing system. */ WND *WndTop(void) { if (CW != NULL) { return CW; } else { return NULL; } } /* * WndPutc; Puts a char anywhere on the window, ignoring borders, etc. * Puts it at the current cursor position (virtual or real). * * Caveats: The cursor may not even be on the window, but it doesn't * care. :-) */ void WndPutc(int Ch, int attr) { if (CW == NULL) { return; } MouseOFF(); TTScolor(attr); TTPutChr(Ch); MouseON(); } /* * WndGotoXY; Goes to the passed coordinates, zero-based, starting * from any borders on the window. * * Caveats: No checks for validity of arguments. */ void WndGotoXY(int x, int y) { int xmod, ymod; if (CW == NULL) { return; } xmod = XMOD(CW); ymod = YMOD(CW); TTgotoxy(CW->y1 + y + ymod, CW->x1 + x + xmod); } /* * WndClear; Clears the passed coordinates with the passed attribute * in the current window. * * Caveats: No checks for validity of arguments. */ void WndClear(int x1, int y1, int x2, int y2, int attr) { int xmod, ymod, m = 0; if (CW == NULL) { return; } xmod = XMOD(CW); ymod = YMOD(CW); if (CheckMousePos(CW->x1 + x1 + xmod, CW->y1 + y1 + ymod, CW->x2 + x2 + xmod, CW->y2 + y2 + ymod)) { m = 1; MouseOFF(); } TTScolor(attr); TTClear(CW->x1 + x1 + xmod, CW->y1 + y1 + ymod, CW->x1 + x2 + xmod, CW->y1 + y2 + ymod); if (m) { MouseON(); } } /* * WndClearLine; Clears the passed line in the current window. */ void WndClearLine(int y, int Att) { if (CW == NULL) { return; } WndClear(0, y, CW->x2 - CW->x1 - (XMOD(CW) * 2), y, Att); } /* * WndFillField; Fills in an edit-field region with char. */ void WndFillField(int x, int y, int len, unsigned char ch, int Attr) { if (len > sizeof(line) - 1) { len = sizeof(line) - 1; } memset(line, ch, len); *(line + len - 1) = '\0'; WndWriteStr(x, y, Attr, line); } /* * WndGetLine; Gets a string from the user on the current window at * the passed coordinates. Passes mouse events not on itself back to * the caller via the extra parameters. */ int WndGetLine(int x, int y, int len, char *buf, int Attr, int *pos, int nokeys, int fil, int disp, EVT * ev) { EVT event; int done = 0; int row = y, col = x; int xmod, ymod, i; unsigned int ch = 0; unsigned char fill; fill= (fil) ? SC13 : (unsigned char) ' '; if (CW == NULL) { return 0; } if (x < 0) x = 0; if (y < 0) y = 0; if (disp) { TTBeginOutput(); WndFillField(col, row, len + 1, fill, Attr | F_ALTERNATE); WndPutsn(col, row, len, Attr, buf); TTEndOutput(); } xmod = XMOD(CW); ymod = YMOD(CW); i = *pos; TTCurSet(1); while (!done) { WndGotoXY(i + col, row); ch = MnuGetMsg(&event, 0); switch (event.msgtype) { case WND_WM_MOUSE: case WND_WM_COMMAND: { int p1 = event.x, p2 = event.y; if (p1 < CW->x1 + xmod + x || p1 > CW->x1 + xmod + x + len - 1 || p2 != CW->y1 + ymod + y) { done = TRUE; break; } } break; case WND_WM_CHAR: switch (ch) { case Key_Up: case Key_Dwn: done = TRUE; break; case Key_Lft: if (i > 0) { i--; } break; case Key_Rgt: if (i < strlen(buf)) { i++; } break; case Key_A_X: memset(buf, 0, len); i = 0; break; case Key_BS: if (i > 0) { TTBeginOutput(); if (i < strlen(buf)) { memmove(buf + i - 1, buf + i, strlen(buf + i) + 1); i--; WndWriteStr(col + i, row, Attr, buf + i); WndPrintf(col + strlen(buf), row, Attr | F_ALTERNATE, "%c", fill); } else { i--; *(buf + i) = '\0'; WndGotoXY(i + col, row); WndPutc(fill, Attr | F_ALTERNATE); } TTEndOutput(); } break; case Key_Del: if (i < strlen(buf)) { TTBeginOutput(); memmove(buf + i, buf + i + 1, strlen(buf + i + 1) + 1); WndWriteStr(col + i, row, Attr, buf + i); WndPrintf(col + strlen(buf), row, Attr | F_ALTERNATE, "%c", fill); TTEndOutput(); } break; case Key_End: i = strlen(buf); break; case Key_Home: i = 0; break; case Key_Ent: done = 1; break; case Key_Esc: done = 2; break; default: if (ch > 0 && ch < 256 && i < len && strlen(buf) < len) { TTBeginOutput(); if (nokeys) { strcpy(buf, ""); WndFillField(col, row, len + 1, fill, Attr | F_ALTERNATE); i = 0; WndGotoXY(i + col, row); } if (i < strlen(buf)) { memmove(buf + i + 1, buf + i, strlen(buf + i) + 1); *(buf + i) = (char)ch; WndWriteStr(col + i, row, Attr, buf + i); i++; } else { *(buf + i) = (char)ch; *(buf + i + 1) = '\0'; WndPutc((unsigned char)ch, Attr); i++; } TTEndOutput(); } else { done = 1; } break; } break; } nokeys = 0; } TTCurSet(0); if (ev != NULL) { *ev = event; } *pos = i; return (int)ch; }