home *** CD-ROM | disk | FTP | other *** search
- /*
- * MultiMail offline mail reader
- * ANSI image/text viewer
-
- Copyright (c) 1999 William McBrine <wmcbrine@clark.net>
-
- Distributed under the GNU General Public License.
- For details, see the file COPYING in the parent directory. */
-
- #include "interfac.h"
-
- AnsiLine::AnsiLine(int space, AnsiLine *parent)
- {
- next = 0;
- prev = parent;
- if (space) {
- text = new chtype[space];
- for (int i = 0; i < (space - 1); i++)
- text[i] = ' ' | C_ANSIBACK;
- text[space - 1] = 0;
- } else
- text = 0;
- length = 0;
- }
-
- AnsiLine::~AnsiLine()
- {
- delete[] text;
- }
-
- AnsiLine *AnsiLine::getnext(int space)
- {
- if (!next)
- if (space)
- next = new AnsiLine(space, this);
- return next;
- }
-
- AnsiLine *AnsiLine::getprev()
- {
- return prev;
- }
-
- void AnsiWindow::DestroyChain()
- {
- while (NumOfLines)
- delete linelist[--NumOfLines];
- delete[] linelist;
- }
-
- int AnsiWindow::getparm()
- {
- char *parm;
- int value;
-
- if (escparm[0]) {
- for (parm = escparm; (*parm != ';') && *parm; parm++);
-
- if (*parm == ';') { // more params after
- *parm++ = '\0';
- if (parm == escparm + 1) // empty param
- value = 1;
- else
- value = atoi(escparm);
- strcpy(escparm, parm);
- } else { // last parameter
- value = atoi(escparm);
- escparm[0] = '\0';
- }
- } else // empty last param
- value = 1;
-
- return value;
- }
-
- void AnsiWindow::colreset()
- {
- cfl = crv = cbr = 0;
- ccf = COLOR_WHITE;
- ccb = COLOR_BLACK;
- }
-
- void AnsiWindow::colorset()
- {
- static const int colortable[8] = {COLOR_BLACK, COLOR_RED,
- COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA,
- COLOR_CYAN, COLOR_WHITE};
- int tmp;
-
- while(escparm[0]) {
- tmp = getparm();
- switch (tmp) {
- case 0: // reset colors
- colreset();
- break;
- case 1: // bright
- cbr = 1;
- break;
- case 5: // flashing
- cfl = 1;
- break;
- case 7: // reverse
- crv = 1;
- break;
- default:
- if ((tmp > 29) && (tmp < 38)) // foreground
- ccf = colortable[tmp - 30];
- else if ((tmp > 39) && (tmp < 48)) // background
- ccb = colortable[tmp - 40];
- }
- }
-
- // Attribute set
-
- // Check bold and blinking:
-
- attrib = (cbr ? A_BOLD : 0) | (cfl ? A_BLINK : 0) |
-
- // If animating, check for color pair 0 (assumes COLOR_BLACK == 0),
- // and for remapped black-on-black:
-
- ((!anim || ((ccb | ccf) || !(oldcolorx | oldcolory))) ?
-
- (crv ? REVERSE(ccf, ccb) : COL(ccf, ccb)) :
-
- (crv ? REVERSE(oldcolorx, oldcolory) : COL(oldcolorx,
- oldcolory)));
-
- // If not animating, mark color pair as used:
-
- if (!anim)
- #ifdef __PDCURSES__
- if (crv)
- colorsused[(ccb << 3) + ccf] = true;
- else
- #endif
- colorsused[(ccf << 3) + ccb] = true;
- }
-
- const unsigned char *AnsiWindow::escfig(const unsigned char *escode)
- {
- char a[2];
-
- a[0] = *escode;
- a[1] = '\0';
-
- switch (a[0]) {
- case 'A': // cursor up
- cpy -= getparm();
- if (cpy < 0)
- cpy = 0;
- break;
- case 'B': // cursor down
- cpy += getparm();
- if (anim && (cpy > (LINES - 2)))
- cpy = LINES - 2;
- break;
- case 'C': // cursor right
- cpx += getparm();
- if (cpx > (COLS - 1))
- cpx = COLS - 1;
- break;
- case 'D': // cursor left
- cpx -= getparm();
- if (cpx < 0)
- cpx = 0;
- break;
- case 'J': // clear screen
- if (getparm() == 2) {
- if (anim) {
- animtext->Clear(C_ANSIBACK);
- animtext->attrib(0);
- } else {
- cpy = NumOfLines - baseline - 1;
- checkpos();
-
- if (baseline < (NumOfLines - 1))
- baseline = NumOfLines - 1;
- }
- posreset();
- }
- break;
- case 'H': // set cursor position
- case 'f':
- cpy = getparm() - 1;
- cpx = getparm() - 1;
- break;
- case 's': // save position
- spx = cpx;
- spy = cpy;
- break;
- case 'u': // restore position
- cpx = spx;
- cpy = spy;
- break;
- case 'm': // attribute set (colors, etc.)
- colorset();
- break;
-
- // I know this is strange, but I think it beats the alternatives:
-
- default:
- if ((a[0] >= '0') && (a[0] <= '9'))
-
- case '=': // for some weird mutant codes
- case '?':
-
- case ';': // parameter separator
- {
- strcat(escparm, a);
- return escfig(++escode);
- }
- }
- return escode;
- }
-
- void AnsiWindow::posreset()
- {
- cpx = cpy = lpy = spx = spy = 0;
- }
-
- void AnsiWindow::checkpos()
- {
- if (cpy > lpy) {
- for (; lpy != cpy; lpy++) // move down and
- curr = curr->getnext(COLS + 1); // allocate space
- } else
- if (cpy < lpy) {
- for (; lpy != cpy; lpy--) // move up
- curr = curr->getprev();
- }
- if ((cpy + 1) > (NumOfLines - baseline))
- NumOfLines = cpy + baseline + 1;
- }
-
- void AnsiWindow::update(unsigned char c)
- {
- static const chtype acstrans[] = {
- ACS_BOARD, ACS_BOARD, ACS_CKBOARD, ACS_VLINE, ACS_RTEE,
- ACS_RTEE, ACS_RTEE, ACS_URCORNER, ACS_URCORNER, ACS_RTEE,
- ACS_VLINE, ACS_URCORNER, ACS_LRCORNER, ACS_LRCORNER,
- ACS_LRCORNER, ACS_URCORNER, ACS_LLCORNER, ACS_BTEE,
- ACS_TTEE, ACS_LTEE, ACS_HLINE, ACS_PLUS, ACS_LTEE,
- ACS_LTEE, ACS_LLCORNER, ACS_ULCORNER, ACS_BTEE, ACS_TTEE,
- ACS_LTEE, ACS_HLINE, ACS_PLUS, ACS_BTEE, ACS_BTEE,
- ACS_TTEE, ACS_TTEE, ACS_LLCORNER, ACS_LLCORNER,
- ACS_ULCORNER, ACS_ULCORNER, ACS_PLUS, ACS_PLUS,
- ACS_LRCORNER, ACS_ULCORNER, ACS_BLOCK,
-
- // There are no good equivalents for these four:
-
- '_', '[', ']', '~'
-
- //ACS_BULLET, ACS_VLINE, ACS_VLINE, ACS_BULLET
- };
-
-
- if (!ansiAbort) {
- chtype ouch;
-
- if (isoConsole) {
- if ((c > 175) && (c < 224)) {
- ouch = acstrans[c - 176];
-
- // suppress or'ing of A_ALTCHARSET:
- c = ' ';
-
- } else {
- if (c & 0x80)
- c = (unsigned char)
- dos2isotab[c & 0x7f];
- ouch = c;
- }
- } else
- ouch = c;
-
- if ((c < ' ') || (c > 126))
- ouch |= A_ALTCHARSET;
-
- ouch |= attrib;
-
- int limit = LINES - 2;
-
- if (anim) {
- if (cpy > limit) {
- animtext->wscroll(cpy - limit);
- cpy = limit;
- }
- animtext->put(cpy, cpx++, ouch);
- animtext->update();
- ansiAbort |= (animtext->keypressed() != ERR);
- } else {
- checkpos();
- curr->text[cpx++] = ouch;
- if (cpx > (int) curr->length)
- curr->length = cpx;
- }
- if (cpx == COLS) {
- cpx = 0;
- cpy++;
- }
- }
- }
-
- void AnsiWindow::ResetChain()
- {
- head = new AnsiLine();
- curr = head;
- posreset();
- curr = curr->getnext(COLS + 1);
- NumOfLines = 1;
- baseline = 0;
- }
-
- void AnsiWindow::MakeChain(const unsigned char *message)
- {
- ansiAbort = false;
- if (!anim)
- ResetChain();
- attrib = C_ANSIBACK;
- colreset();
-
- while (*message && !ansiAbort) {
- switch (*message) {
- case 1: // hidden lines (only in pos 0)
- if (!cpx) {
- do
- message++;
- while (*message && (*message != '\n'));
- if (!*message)
- message--;
- }
- break;
- case 8: // backspace
- if (cpx)
- cpx--;
- break;
- case 10:
- cpy++;
- case 13:
- cpx = 0;
- case 7: // ^G: beep
- case 26: // ^Z: EOF for DOS
- break;
- case 12: // form feed
- if (anim) {
- animtext->Clear(C_ANSIBACK);
- animtext->attrib(0);
- posreset();
- }
- break;
- #ifndef __PDCURSES__ // unprintable control codes
- case 14: // double musical note
- update(19);
- break;
- case 15: // much like an asterisk
- update('*');
- break;
- case 155: // ESC + high bit = slash-o,
- update('o'); // except in CP 437
- break;
- #endif
- case '`':
- case 27: // ESC
- if (message[1] == '[') {
- escparm[0] = '\0';
- message = escfig(message + 2);
- } else
- update('`');
- break;
- case '\t': // TAB
- cpx = ((cpx / 8) + 1) * 8;
- while (cpx >= COLS) {
- cpx -= COLS;
- cpy++;
- }
- break;
- default:
- update(*message);
- }
- message++;
- }
-
- if (!anim && !ansiAbort) {
- linelist = new AnsiLine *[NumOfLines];
- curr = head->getnext();
- int i = 0;
- while (curr) {
- linelist[i++] = curr;
- curr = curr->getnext();
- }
- delete head;
- }
- }
-
- void AnsiWindow::statupdate(const char *intro)
- {
- static const char *helpmsg = "F1 or ? - Help ",
- *mainstat = " ANSI View";
- char format[30], *tmp = new char[COLS + 1];
-
- sprintf(format, "%%s: %%-%d.%ds %%s", COLS - 28, COLS - 28);
- sprintf(tmp, format, (intro ? intro : mainstat), title, helpmsg);
-
- statbar->cursor_on();
- statbar->put(0, 0, tmp);
- statbar->update();
- statbar->cursor_off();
-
- delete[] tmp;
- }
-
- void AnsiWindow::animate()
- {
- animtext = new Win(LINES - 1, COLS, 0, C_ANSIBACK);
- animtext->attrib(0);
- animtext->cursor_on();
-
- posreset();
- colreset();
- anim = true;
-
- statupdate(" Animating");
-
- MakeChain(source);
-
- if (!ansiAbort)
- statupdate(" Done");
-
- anim = false;
- while (!ansiAbort && (animtext->inkey() == ERR));
-
- animtext->cursor_off();
- delete animtext;
-
- header->wtouch();
- text->wtouch();
- statupdate();
- }
-
- void AnsiWindow::oneLine(int i)
- {
- text->put(i, 0, linelist[position + i]->text);
- }
-
- void AnsiWindow::lineCount()
- {
- char tmp[22];
-
- sprintf(tmp, "Lines: %6d/%-7d", position + 1, NumOfLines);
- header->put(0, COLS-23, tmp);
- header->delay_update();
- }
-
- void AnsiWindow::DrawBody()
- {
- lineCount();
-
- for (int i = 0; i < y; i++)
- if ((position + i) < NumOfLines)
- oneLine(i);
- else
- for (int j = 0; j < x; j++)
- text->put(i, j, (chtype) ' ' | C_ANSIBACK);
- }
-
- void AnsiWindow::set(const char *ansiSource, const char *winTitle)
- {
- fromFile = 0;
- source = (const unsigned char *) ansiSource;
-
- position = 0;
- sourceDel = false;
-
- title = winTitle;
- }
-
- void AnsiWindow::setFile(file_header *f, const char *winTitle)
- {
- fromFile = f;
- source = 0;
-
- position = 0;
- sourceDel = true;
-
- title = winTitle ? winTitle : f->getName();
- }
-
- void AnsiWindow::getFile()
- {
- FILE *flist;
- char *list;
-
- if ((flist = mm.workList->ftryopen(fromFile->getName(), "rb"))) {
- off_t size = fromFile->getSize();
-
- list = new char[size + 1];
- if (fread(list, 1, size, flist))
- list[size] = '\0';
- else {
- delete list;
- list = 0;
- }
- fclose(flist);
- } else
- list = 0;
-
- source = (unsigned char *) list;
- }
-
- void AnsiWindow::MakeActive()
- {
- int i;
- bool end, oldAbort;
-
- header = new Win(1, COLS, 0, C_LBOTTSTAT);
- text = new Win(LINES - 2, COLS, 1, C_ANSIBACK);
- statbar = new Win(1, COLS, LINES - 1, C_LBOTTSTAT);
-
- text->attrib(0);
-
- anim = false;
- oldAbort = ansiAbort;
-
- char *tmp = new char[COLS + 1];
- i = sprintf(tmp, " " MM_TOPHEADER, MM_NAME, MM_MAJOR, MM_MINOR);
- for (; i < COLS; i++)
- tmp[i] = ' ';
- tmp[i] = '\0';
-
- header->put(0, 0, tmp);
- header->delay_update();
- delete[] tmp;
-
- y = LINES - 2;
- x = COLS;
-
- for (i = 0; i < 64; i++)
- colorsused[i] = false;
-
- colorsused[PAIR_NUMBER(C_LBOTTSTAT)] = 1; //don't remap stat bars
-
- oldcolorx = oldcolory = 0;
-
- init_pair(((COLOR_WHITE) << 3) + (COLOR_WHITE),
- COLOR_WHITE, COLOR_WHITE);
-
- if (fromFile)
- getFile();
-
- MakeChain(source);
-
- // This deals with the unavailability of color pair 0:
-
- if (colorsused[0]) { // assumes COLOR_BLACK == 0
-
- // Find an unused color pair for black-on-black:
-
- for (end = false, i = 1; i < 64 && !end; i++)
- if (!colorsused[i]) {
- end = true;
- oldcolorx = i >> 3;
- oldcolory = i & 7;
- init_pair(i, COLOR_BLACK, COLOR_BLACK);
- }
-
- // Remap all instances of color pair 0 to the new pair:
-
- if (end) {
- for (int j = 0; j < NumOfLines; j++) {
- curr = linelist[j];
- for (i = 0; i < (int) curr->length; i++)
- if (!PAIR_NUMBER(curr->text[i])) {
- curr->text[i] &= ~A_COLOR;
- curr->text[i] |=
- COL(oldcolorx, oldcolory);
- }
- }
- }
- }
-
- DrawBody();
- text->delay_update();
-
- statupdate();
-
- ansiAbort = oldAbort;
- }
-
- void AnsiWindow::Delete()
- {
- ansiAbort = true;
-
- // Restore remapped color pairs:
-
- if (oldcolorx + oldcolory)
- init_pair((oldcolorx << 3) + oldcolory,
- oldcolorx, oldcolory);
- init_pair(((COLOR_WHITE) << 3) + (COLOR_WHITE),
- COLOR_BLACK, COLOR_BLACK);
-
- DestroyChain();
- delete header;
- delete text;
- delete statbar;
- if (sourceDel)
- delete[] source;
- }
-
- void AnsiWindow::setPos(int x)
- {
- position = x;
- }
-
- int AnsiWindow::getPos()
- {
- return position;
- }
-
- searchret AnsiWindow::search(const char *item)
- {
- chtype *ch;
- searchret found = False;
-
- unsigned char *a, *buffer = new unsigned char[COLS + 1];
-
- for (int x = position + 1; (x < NumOfLines) && (found == False);
- x++) {
-
- if (text->keypressed() == 27) {
- found = Abort;
- break;
- }
-
- a = buffer;
- ch = linelist[x]->text;
- int count = linelist[x]->length;
-
- for (int y = 0; y < count; y++)
- *a++ = (*ch++ & A_CHARTEXT);
- *a = '\0';
-
- found = searchstr((char *) buffer, item) ? True : False;
-
- if (found == True) {
- position = x;
- DrawBody();
- text->delay_update();
- }
- }
-
- delete[] buffer;
- return found;
- }
-
- void AnsiWindow::Save()
- {
- FILE *fd;
- const unsigned char *message = source;
-
- static char keepname[128];
- char filename[128], oldfname[128];
-
- if (keepname[0])
- strcpy(filename, keepname);
- else {
- sprintf(filename, "%.8s.ans", title);
- unspace(filename);
- }
-
- strcpy(oldfname, filename);
-
- if (interface->savePrompt("Save to file:", filename)) {
- mychdir(mm.resourceObject->get(SaveDir));
- if ((fd = fopen(filename, "at"))) {
- while (*message)
- fputc(*message++, fd);
- fclose(fd);
- }
- if (strcmp(filename, oldfname))
- strcpy(keepname, filename);
- }
- }
-
- void AnsiWindow::KeyHandle(int key)
- {
- switch (key) {
- case KEY_UP:
- if (position > 0) {
- position--;
- text->wscroll(-1);
- oneLine(0);
- lineCount();
- }
- break;
- case KEY_DOWN:
- if (position < NumOfLines - y) {
- position++;
- text->wscroll(1);
- oneLine(y - 1);
- lineCount();
- }
- break;
- case KEY_HOME:
- position = 0;
- DrawBody();
- break;
- case KEY_END:
- if (NumOfLines > y) {
- position = NumOfLines - y;
- DrawBody();
- }
- break;
- case 'B':
- case KEY_PPAGE:
- position -= ((y < position) ? y : position);
- DrawBody();
- break;
- case ' ':
- position += y;
- if (position < NumOfLines)
- DrawBody();
- else
- interface->setKey('\n');
- break;
- case 'F':
- case KEY_NPAGE:
- if (position < NumOfLines - y) {
- position += y;
- if (position > NumOfLines - y)
- position = NumOfLines - y;
- DrawBody();
- }
- break;
- case 'V':
- case 1:
- case 22:
- animate();
- break;
- case 'S':
- Save();
- DrawBody();
- break;
- case KEY_F(1):
- case '?':
- interface->changestate(ansi_help);
- }
- text->delay_update();
- }
-