/* * UAE - The Un*x Amiga Emulator * * ncurses frontend for a text-based user interface. * * Copyright 1996 Bernd Schmidt * If you find the routines in this file useful, you may use them in your * programs without restrictions. Essentially, it's in the public domain. * */ #include "sysconfig.h" #include "sysdeps.h" #ifdef HAVE_NCURSES_H #include #else #include #endif #include #include "options.h" #include "uae.h" #include "tui.h" #ifdef DONT_HAVE_ATTR_T typedef int attr_t; #endif static WINDOW *currwin; static WINDOW *winstack[10]; /* more than enough */ static int winnr = 0; void tui_setup(void) { int i; for (i = 0; i < 10; i++) winstack[i] = NULL; /* From the ncurses manpage... */ initscr (); start_color (); cbreak(); noecho(); nonl (); intrflush (stdscr, FALSE); keypad (stdscr, TRUE); currwin = stdscr; if (has_colors ()) { init_pair (1, COLOR_WHITE, COLOR_BLUE); init_pair (2, COLOR_BLACK, COLOR_WHITE); wattron (currwin, COLOR_PAIR (1) | A_BOLD); wbkgd (currwin, ' ' | COLOR_PAIR (1)); } winstack[0] = stdscr; winnr = 1; } int tui_lines (void) { return LINES; } int tui_cols (void) { return COLS; } void tui_shutdown (void) { endwin (); } void tui_refresh (void) { int w; for (w = 0; w < winnr; w++) { touchwin (winstack[w]); wnoutrefresh (winstack[w]); } doupdate (); } void tui_puts (const char *s) { waddstr (currwin, s); } void tui_cursoff(void) { } void tui_curson (void) { } void tui_putc(char c) { waddch (currwin, c); } void tui_cr (void) { waddch (currwin, '\r'); } char tui_getc(void) { return getch (); } void tui_gotoxy (int x, int y) { x--; y--; wmove (currwin, y, x); } void tui_selwin (int w) { currwin = winstack[w]; } void tui_clrwin (int w) { werase (winstack[w]); } void tui_drawbox(int w) { wborder (winstack[w], 0, 0, 0, 0, 0, 0, 0, 0); } void tui_hline (int x1, int y1, int x2) { wmove (currwin, y1-1, x1-1); whline (currwin, 0, x2-x1+1); } int tui_dlog(int x1, int y1, int x2, int y2) { x1--; y1--; winstack[winnr] = newwin (y2 - y1, x2 - x1, y1, x1); return winnr++; } void tui_dlogdie (int w) { if (currwin == winstack[w]) currwin = stdscr; delwin (winstack[w]); winstack[w] = NULL; while (winstack[winnr-1] == NULL) winnr--; for (w = 0; w < winnr; w++) redrawwin (winstack[w]), wrefresh (winstack[w]); } int tui_gets (char *buf, int x, int y, int n) { int i = 0; for (;;) { int c, j; buf[i] = 0; wmove (currwin, y, x); for (j = 0; j < i; j++) waddch (currwin, buf[j]); for (; j < n; j++) waddch (currwin, ' '); wmove (currwin, y, x + i); wrefresh (currwin); c = getch (); wmove (currwin, y, x + i); if (c == 13) return 1; else if (c == 27) return 0; else if (i > 0 && c == KEY_BACKSPACE) i--; else if (i + 1 < n && !iscntrl (c)) buf[i++] = c; } } int tui_wgets (char *buf, const char *title, int n) { int l = strlen (title); int ww = (l > n ? l : n) + 2; int w = tui_dlog((tui_cols ()-ww)/2, tui_lines ()/2-1, (tui_cols ()+ww)/2, tui_lines ()/2+1); int result; tui_selwin (w); tui_drawbox(w); wmove (currwin, 0, (ww-l)/2); waddstr (currwin, title); result = tui_gets (buf, 1, 1, n); tui_dlogdie (w); return result; } int tui_menubrowse (struct bstring *menu, int xoff, int yoff, int selected, int height) { int count = 0, maxsel = 0, maxw = 0; int i, j, w, s, yp, oldyp; chtype moresave[6][2]; int xpos, ypos; const char *mtitle = NULL; for (i = 0; menu[i].val != -3; i++) { int tmp; if (menu[i].val == -4) { if (maxsel < selected) selected--; continue; } if (menu[i].val != 0) { count++; if (menu[i].val != -2) maxsel++; } else mtitle = menu[i].data; if ((tmp = strlen (menu[i].data)) > maxw) maxw = tmp; } if (height > count) height = count; maxw += 3; if (strlen (mtitle ? mtitle : "") + 8 > maxw) maxw = strlen (mtitle ? mtitle : "") + 8; if (xoff > 0) xpos = xoff; else xpos = tui_cols () + xoff - maxw - 1; if (yoff > 0) ypos = yoff; else ypos = tui_lines () + yoff - height - 2; w = tui_dlog(xpos, ypos, xpos+maxw, ypos+height+1); tui_selwin (w); tui_drawbox(w); if (mtitle != NULL) { mvwaddstr (currwin, 0, 1, mtitle); } for (i = 0; i < 6; i++) { moresave[i][0] = mvwinch (currwin, 0, maxw-6+i); moresave[i][1] = mvwinch (currwin, height+1, maxw-6+i); } s = yp = 0; oldyp = -1; for (;;) { int c; int s2; while (selected < yp) yp--; while (selected >= yp + height) yp++; if (yp == 0) for (i = 0; i < 6; i++) mvwaddch (currwin, 0, maxw-6+i, moresave[i][0]); else mvwaddstr (currwin, 0, maxw-6, "(more)"); if (yp + height == count) for (i = 0; i < 6; i++) mvwaddch (currwin, height+1, maxw-6+i, moresave[i][1]); else mvwaddstr (currwin, height+1, maxw-6, "(more)"); for (i = s2 = j = 0; i < count; i++, j++) { int k, x; attr_t a = s2 == selected ? A_STANDOUT : 0; while (menu[j].val == 0 || menu[j].val == -4) j++; if (i >= yp && i < yp+height) { mvwaddch (currwin, 1+i-yp, 1, ' ' | a); for (k = x = 0; menu[j].data[k]; k++, x++) { int a2 = 0; c = menu[j].data[k]; if (c == '_') c = menu[j].data[++k], a2 = A_UNDERLINE; mvwaddch (currwin, 1+i-yp, 2+x, c | a | a2); } for (; x < maxw-2; x++) { mvwaddch (currwin, 1+i-yp, 2+x, ' ' | a); } } if (menu[j].val != -2) s2++; } tui_refresh (); c = getch (); if (c == 27) { tui_dlogdie (w); return -1; } else if (c == KEY_ENTER || c == 13 || c == ' ') { tui_dlogdie (w); for (i = s2 = j = 0; s2 <= selected; j++) { if (menu[j].val == -4) { i++; j++; continue; } while (menu[j].val == 0) j++; if (s2 == selected) return i; if (menu[j].val != -2) s2++, i++; } abort(); } else switch (c) { case KEY_UP: if (selected > 0) selected--; break; case KEY_DOWN: if (selected + 1 < count) selected++; break; case KEY_PPAGE: if (selected > height) selected -= height; else selected = 0; break; case KEY_NPAGE: if (selected + height < count) selected += height; else selected = count-1; break; default: for (j = i = 0; menu[i].val != -3; i++) if (menu[i].val == toupper (c)) { tui_dlogdie (w); return j; } else if (menu[i].val == -1 || menu[i].val == -4 || menu[i].val > 0) { j++; } break; } } return -1; /* Can't get here */ } void tui_errorbox(const char *err) { const char *hak = "Hit any key"; int n = strlen (hak); int l = strlen (err); int ww = (l > n ? l : n) + 2; int w = tui_dlog((tui_cols ()-ww)/2, tui_lines ()/2-1, (tui_cols ()+ww)/2, tui_lines ()/2+1); tui_selwin (w); tui_drawbox(w); wmove (currwin, 0, (ww-6)/2); waddstr (currwin, "Error!"); wmove (currwin, 1, (ww-l)/2); waddstr (currwin, err); wmove (currwin, 2, (ww-n)/2); waddstr (currwin, hak); wrefresh (currwin); for (;;) { int c = getch (); if (c == 13) break; } tui_dlogdie (w); } static char *pattern; static int maxlen; static void put_filename (char *s, int x, int y, attr_t a) { char buf[256]; int i; tui_gotoxy (x,y); if (strcmp (s, ".") == 0) strcpy (buf, "(none)"); else strcpy (buf, s); buf[maxlen] = 0; for (i = 0; i < strlen (buf); i++) waddch (currwin, buf[i] | a); for (; i < maxlen; i++) waddch (currwin, ' ' | a); } static char fsbuf[256]; static int selectfn (const struct dirent *de) { int l1, l2; /* l1 = strlen (pattern + 1);*/ l2 = strlen (de->d_name); if (l2 >= tui_cols ()-10) /* Restrict length of filenames so we won't mess up the display */ return 0; /* No pattern matching for now. But we don't show hidden files. */ if (strcmp (de->d_name, ".") != 0 && strcmp (de->d_name, "..") != 0 && de->d_name[0] == '.') return 0; if (l2 > maxlen) maxlen = l2; return 1; } static int my_alphasort (const void *a, const void *b) { return strcmp ((*(struct dirent **) a)->d_name, (*(struct dirent **) b)->d_name); } char *tui_filereq(char *s, char *oldfile, const char *title) { char cwd[256]; char *retval = fsbuf; char *tmp; int fin = 0; chtype moresave[6][2]; /* Save wd */ if (getcwd (cwd, 256) == NULL) return NULL; /* Change into directory of old file */ strcpy (fsbuf, oldfile); tmp = strrchr (fsbuf, '/'); if (tmp != NULL) { *tmp = 0; if (strlen (fsbuf) > 0) chdir (fsbuf); } pattern = s; if (s[0] != '*') write_log ("Can't handle wildcard %s\n", s); if (s[1] != 0 && strchr (s+1, '*') != NULL) write_log ("Can't handle wildcard %s\n", s); for (;!fin;) { struct dirent **names; int i, w, n, l, yp, oldyp, s; maxlen = 0; n = scandir (".", &names, selectfn, my_alphasort); if (n <= 0) return NULL; if (title != NULL && strlen (title) + 6 > maxlen) maxlen = strlen (title) + 6; l = n; if (l > 15) l = 15; yp = s = 0; oldyp = -1; w = tui_dlog (tui_cols () - maxlen - 8, 5, tui_cols () - 5, 5 + l + 1); tui_selwin (w); tui_drawbox (w); if (title) mvwaddstr (currwin, 0, 2, title); for (i = 0; i < 6; i++) { moresave[i][0] = mvwinch (currwin, 0, maxlen-3+i); moresave[i][1] = mvwinch (currwin, l+1, maxlen-3+i); } for (;;) { int c; char tmp[256]; while (s < yp) yp--; while (s >= yp + l) yp++; if (oldyp != yp) { oldyp = yp; for (i = 0; i < l; i++) { put_filename (names[i + yp]->d_name, 3, 2 + i, 0); } } put_filename (names[s]->d_name, 3, 2 + s - yp, A_STANDOUT); if (yp == 0) for (i = 0; i < 6; i++) mvwaddch (currwin, 0, maxlen-3+i, moresave[i][0]); else mvwaddstr (currwin, 0, maxlen-3, "(more)"); if (yp + l == n) for (i = 0; i < 6; i++) mvwaddch (currwin, l+1, maxlen-3+i, moresave[i][1]); else mvwaddstr (currwin, l+1, maxlen-3, "(more)"); tui_refresh (); c = getch (); put_filename (names[s]->d_name, 3, 2 + s - yp, 0); if (c == 27) { retval = NULL; fin = 1; break; } else if (c == KEY_ENTER || c == 13 || c == ' ') { int err; if (strcmp (names[s]->d_name, ".") == 0) { fin = 1; strcpy (fsbuf, ""); break; } err = chdir (names[s]->d_name); if (err == 0) break; else if (errno == ENOTDIR) { fin = 1; if (getcwd (fsbuf, 256) == NULL) retval = NULL; if (strlen (fsbuf) + strlen (names[s]->d_name) + 2 >= 256) retval = NULL; else { strcat(fsbuf, "/"); strcat(fsbuf, names[s]->d_name); } break; } /* else what? */ } switch (c) { case KEY_UP: if (s > 0) s--; break; case KEY_DOWN: if (s + 1 < n) s++; break; case KEY_PPAGE: if (s > l) s -= l; else s = 0; break; case KEY_NPAGE: if (s + l < n) s += l; else s = n - 1; break; default: i = 0; if (names[s]->d_name[0] == c) i = s+1; for (; i < n*2; i++) { int j = i; if (i >= n) j -= n; if (names[j]->d_name[0] == c) { s = j; break; } } } } #if 0 /* @@@ is this right? */ for (i = 0; i < n; i++) free (names[i]); free (names); #endif tui_dlogdie (w); } chdir (cwd); return retval; } int tui_backup_optionsfile (void) { char tmp[257]; strcpy (tmp, optionsfile); strcat (tmp, "~"); return rename (optionsfile, tmp); }