home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC-Online 1999 November
/
PCOnline_11_1999.iso
/
filesbbs
/
OS2
/
MMSRC029.ZIP
/
mmail-0.29
/
interfac
/
ansiview.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1999-08-17
|
14KB
|
750 lines
/*
* 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();
}