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 >
C/C++ Source or Header  |  1999-08-17  |  14KB  |  750 lines

  1. /*
  2.  * MultiMail offline mail reader
  3.  * ANSI image/text viewer
  4.  
  5.  Copyright (c) 1999 William McBrine <wmcbrine@clark.net>
  6.  
  7.  Distributed under the GNU General Public License.
  8.  For details, see the file COPYING in the parent directory. */
  9.  
  10. #include "interfac.h"
  11.  
  12. AnsiLine::AnsiLine(int space, AnsiLine *parent)
  13. {
  14.     next = 0;
  15.     prev = parent;
  16.     if (space) {
  17.         text = new chtype[space];
  18.         for (int i = 0; i < (space - 1); i++)
  19.             text[i] = ' ' | C_ANSIBACK;
  20.         text[space - 1] = 0;
  21.     } else
  22.         text = 0;
  23.     length = 0;
  24. }
  25.  
  26. AnsiLine::~AnsiLine()
  27. {
  28.     delete[] text;
  29. }
  30.  
  31. AnsiLine *AnsiLine::getnext(int space)
  32. {
  33.     if (!next)
  34.         if (space)
  35.             next = new AnsiLine(space, this);
  36.     return next;
  37. }
  38.  
  39. AnsiLine *AnsiLine::getprev()
  40. {
  41.     return prev;
  42. }
  43.  
  44. void AnsiWindow::DestroyChain()
  45. {
  46.     while (NumOfLines)
  47.         delete linelist[--NumOfLines];
  48.     delete[] linelist;
  49. }
  50.  
  51. int AnsiWindow::getparm()
  52. {
  53.     char *parm;
  54.     int value;
  55.  
  56.     if (escparm[0]) { 
  57.         for (parm = escparm; (*parm != ';') && *parm; parm++);
  58.  
  59.         if (*parm == ';') {            // more params after
  60.             *parm++ = '\0';
  61.             if (parm == escparm + 1)    // empty param
  62.                 value = 1;
  63.             else
  64.                 value = atoi(escparm);
  65.             strcpy(escparm, parm);
  66.         } else {                // last parameter
  67.             value = atoi(escparm);
  68.             escparm[0] = '\0';
  69.         }
  70.     } else                        // empty last param
  71.         value = 1;
  72.  
  73.     return value;
  74. }
  75.  
  76. void AnsiWindow::colreset()
  77. {
  78.     cfl = crv = cbr = 0;
  79.     ccf = COLOR_WHITE;
  80.     ccb = COLOR_BLACK;
  81. }
  82.  
  83. void AnsiWindow::colorset()
  84. {
  85.     static const int colortable[8] = {COLOR_BLACK, COLOR_RED,
  86.         COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA,
  87.         COLOR_CYAN, COLOR_WHITE};
  88.     int tmp;
  89.  
  90.     while(escparm[0]) {
  91.         tmp = getparm();
  92.         switch (tmp) {
  93.         case 0:            // reset colors
  94.             colreset();
  95.             break;
  96.         case 1:            // bright
  97.             cbr = 1;
  98.             break;
  99.         case 5:            // flashing
  100.             cfl = 1;
  101.             break;
  102.         case 7:            // reverse
  103.             crv = 1;
  104.             break;
  105.         default:
  106.             if ((tmp > 29) && (tmp < 38))        // foreground
  107.                 ccf = colortable[tmp - 30];
  108.             else if ((tmp > 39) && (tmp < 48))    // background
  109.                 ccb = colortable[tmp - 40];
  110.         }
  111.     }
  112.  
  113.     // Attribute set
  114.  
  115.     // Check bold and blinking:
  116.  
  117.     attrib = (cbr ? A_BOLD : 0) | (cfl ? A_BLINK : 0) |
  118.  
  119.     // If animating, check for color pair 0 (assumes COLOR_BLACK == 0),
  120.     // and for remapped black-on-black:
  121.  
  122.         ((!anim || ((ccb | ccf) || !(oldcolorx | oldcolory))) ?
  123.  
  124.         (crv ? REVERSE(ccf, ccb) : COL(ccf, ccb)) :
  125.  
  126.         (crv ? REVERSE(oldcolorx, oldcolory) : COL(oldcolorx,
  127.         oldcolory)));
  128.  
  129.     // If not animating, mark color pair as used:
  130.  
  131.     if (!anim)
  132. #ifdef __PDCURSES__
  133.         if (crv)
  134.             colorsused[(ccb << 3) + ccf] = true;
  135.         else
  136. #endif
  137.             colorsused[(ccf << 3) + ccb] = true;
  138. }
  139.  
  140. const unsigned char *AnsiWindow::escfig(const unsigned char *escode)
  141. {
  142.     char a[2];
  143.  
  144.     a[0] = *escode;
  145.     a[1] = '\0';
  146.  
  147.     switch (a[0]) {
  148.     case 'A':            // cursor up
  149.         cpy -= getparm();
  150.         if (cpy < 0)
  151.             cpy = 0;
  152.         break;
  153.     case 'B':            // cursor down
  154.         cpy += getparm();
  155.         if (anim && (cpy > (LINES - 2)))
  156.             cpy = LINES - 2;
  157.         break;
  158.     case 'C':            // cursor right
  159.         cpx += getparm();
  160.         if (cpx > (COLS - 1))
  161.             cpx = COLS - 1;
  162.         break;
  163.     case 'D':            // cursor left
  164.         cpx -= getparm();
  165.         if (cpx < 0)
  166.             cpx = 0;
  167.         break;
  168.     case 'J':            // clear screen
  169.         if (getparm() == 2) {
  170.             if (anim) {
  171.                 animtext->Clear(C_ANSIBACK);
  172.                 animtext->attrib(0);
  173.             } else {
  174.                 cpy = NumOfLines - baseline - 1;
  175.                 checkpos();
  176.  
  177.                 if (baseline < (NumOfLines - 1))
  178.                     baseline = NumOfLines - 1;
  179.             }
  180.             posreset();
  181.         }
  182.         break;
  183.     case 'H':            // set cursor position
  184.     case 'f':
  185.         cpy = getparm() - 1;
  186.         cpx = getparm() - 1;
  187.         break;
  188.     case 's':            // save position
  189.         spx = cpx;
  190.         spy = cpy;
  191.         break;
  192.     case 'u':            // restore position
  193.         cpx = spx;
  194.         cpy = spy;
  195.         break;
  196.     case 'm':            // attribute set (colors, etc.)
  197.         colorset();
  198.         break;
  199.  
  200.     // I know this is strange, but I think it beats the alternatives:
  201.  
  202.     default:
  203.         if ((a[0] >= '0') && (a[0] <= '9'))
  204.  
  205.     case '=':            // for some weird mutant codes
  206.     case '?':
  207.  
  208.     case ';':            // parameter separator
  209.         {
  210.             strcat(escparm, a);
  211.             return escfig(++escode);
  212.         }
  213.     }
  214.     return escode;
  215. }
  216.  
  217. void AnsiWindow::posreset()
  218. {
  219.     cpx = cpy = lpy = spx = spy = 0;
  220. }
  221.  
  222. void AnsiWindow::checkpos()
  223. {
  224.     if (cpy > lpy) {
  225.         for (; lpy != cpy; lpy++)         // move down and
  226.             curr = curr->getnext(COLS + 1);  // allocate space
  227.     } else
  228.         if (cpy < lpy) {
  229.             for (; lpy != cpy; lpy--)     // move up
  230.                 curr = curr->getprev();
  231.         }
  232.     if ((cpy + 1) > (NumOfLines - baseline))
  233.         NumOfLines = cpy + baseline + 1;
  234. }
  235.  
  236. void AnsiWindow::update(unsigned char c)
  237. {
  238.     static const chtype acstrans[] = {
  239.         ACS_BOARD, ACS_BOARD, ACS_CKBOARD, ACS_VLINE, ACS_RTEE,
  240.         ACS_RTEE, ACS_RTEE, ACS_URCORNER, ACS_URCORNER, ACS_RTEE,
  241.         ACS_VLINE, ACS_URCORNER, ACS_LRCORNER, ACS_LRCORNER,
  242.         ACS_LRCORNER, ACS_URCORNER, ACS_LLCORNER, ACS_BTEE,
  243.         ACS_TTEE, ACS_LTEE, ACS_HLINE, ACS_PLUS, ACS_LTEE,
  244.         ACS_LTEE, ACS_LLCORNER, ACS_ULCORNER, ACS_BTEE, ACS_TTEE,
  245.         ACS_LTEE, ACS_HLINE, ACS_PLUS, ACS_BTEE, ACS_BTEE,
  246.         ACS_TTEE, ACS_TTEE, ACS_LLCORNER, ACS_LLCORNER,
  247.         ACS_ULCORNER, ACS_ULCORNER, ACS_PLUS, ACS_PLUS,
  248.         ACS_LRCORNER, ACS_ULCORNER, ACS_BLOCK,
  249.  
  250.         // There are no good equivalents for these four:
  251.  
  252.         '_', '[', ']', '~'
  253.  
  254.         //ACS_BULLET, ACS_VLINE, ACS_VLINE, ACS_BULLET
  255.     };
  256.  
  257.  
  258.     if (!ansiAbort) {
  259.         chtype ouch;
  260.  
  261.         if (isoConsole) {
  262.             if ((c > 175) && (c < 224)) {
  263.                 ouch = acstrans[c - 176];
  264.  
  265.                 // suppress or'ing of A_ALTCHARSET:
  266.                 c = ' ';
  267.  
  268.             } else {
  269.                 if (c & 0x80)
  270.                     c = (unsigned char)
  271.                         dos2isotab[c & 0x7f];
  272.                 ouch = c;
  273.             }
  274.         } else
  275.             ouch = c;
  276.  
  277.         if ((c < ' ') || (c > 126))
  278.             ouch |= A_ALTCHARSET;
  279.  
  280.         ouch |= attrib;
  281.  
  282.         int limit = LINES - 2;
  283.  
  284.         if (anim) {
  285.             if (cpy > limit) {
  286.                 animtext->wscroll(cpy - limit);
  287.                 cpy = limit;
  288.             }
  289.             animtext->put(cpy, cpx++, ouch);
  290.             animtext->update();
  291.             ansiAbort |= (animtext->keypressed() != ERR);
  292.         } else {
  293.             checkpos();
  294.                 curr->text[cpx++] = ouch;
  295.             if (cpx > (int) curr->length)
  296.                     curr->length = cpx;
  297.         }
  298.         if (cpx == COLS) {
  299.             cpx = 0;
  300.             cpy++;
  301.         }
  302.     }
  303. }
  304.  
  305. void AnsiWindow::ResetChain()
  306. {
  307.     head = new AnsiLine();
  308.     curr = head;
  309.     posreset();
  310.     curr = curr->getnext(COLS + 1);
  311.     NumOfLines = 1;
  312.     baseline = 0;
  313. }
  314.  
  315. void AnsiWindow::MakeChain(const unsigned char *message)
  316. {
  317.     ansiAbort = false;
  318.     if (!anim)
  319.         ResetChain();
  320.     attrib = C_ANSIBACK;
  321.     colreset();
  322.  
  323.     while (*message && !ansiAbort) {
  324.         switch (*message) {
  325.         case 1:            // hidden lines (only in pos 0)
  326.             if (!cpx) {
  327.                                 do
  328.                                         message++;
  329.                                 while (*message && (*message != '\n'));
  330.                                 if (!*message)
  331.                                         message--;
  332.                         }
  333.             break;
  334.         case 8:            // backspace
  335.             if (cpx)
  336.                 cpx--;
  337.             break;
  338.         case 10:
  339.             cpy++;
  340.         case 13:
  341.             cpx = 0;
  342.         case 7:            // ^G: beep
  343.         case 26:        // ^Z: EOF for DOS
  344.             break;
  345.         case 12:        // form feed
  346.             if (anim) {
  347.                 animtext->Clear(C_ANSIBACK);
  348.                 animtext->attrib(0);
  349.                 posreset();
  350.             }
  351.             break;
  352. #ifndef __PDCURSES__            // unprintable control codes
  353.         case 14:        // double musical note
  354.             update(19);
  355.             break;
  356.         case 15:        // much like an asterisk
  357.             update('*');
  358.             break;
  359.         case 155:        // ESC + high bit = slash-o,
  360.             update('o');    // except in CP 437
  361.             break;
  362. #endif
  363.         case '`':
  364.         case 27:        // ESC
  365.             if (message[1] == '[') {
  366.                 escparm[0] = '\0';
  367.                 message = escfig(message + 2);
  368.             } else
  369.                 update('`');
  370.             break;
  371.         case '\t':        // TAB
  372.             cpx = ((cpx / 8) + 1) * 8;
  373.             while (cpx >= COLS) {
  374.                 cpx -= COLS;
  375.                 cpy++;
  376.             }
  377.             break;
  378.         default:
  379.             update(*message);
  380.         }
  381.         message++;
  382.     }
  383.  
  384.     if (!anim && !ansiAbort) {
  385.         linelist = new AnsiLine *[NumOfLines];
  386.         curr = head->getnext();
  387.         int i = 0;
  388.         while (curr) {
  389.             linelist[i++] = curr;
  390.             curr = curr->getnext();
  391.         }
  392.         delete head;
  393.     }
  394. }
  395.  
  396. void AnsiWindow::statupdate(const char *intro)
  397. {
  398.     static const char *helpmsg = "F1 or ? - Help ",
  399.         *mainstat = " ANSI View";
  400.     char format[30], *tmp = new char[COLS + 1];
  401.  
  402.     sprintf(format, "%%s: %%-%d.%ds %%s", COLS - 28, COLS - 28);
  403.     sprintf(tmp, format, (intro ? intro : mainstat), title, helpmsg);
  404.  
  405.     statbar->cursor_on();
  406.     statbar->put(0, 0, tmp);
  407.     statbar->update();
  408.     statbar->cursor_off();
  409.  
  410.     delete[] tmp;
  411. }
  412.  
  413. void AnsiWindow::animate()
  414. {
  415.     animtext = new Win(LINES - 1, COLS, 0, C_ANSIBACK);
  416.     animtext->attrib(0);
  417.     animtext->cursor_on();
  418.  
  419.     posreset();
  420.     colreset();
  421.     anim = true;
  422.  
  423.     statupdate(" Animating");
  424.  
  425.     MakeChain(source);
  426.  
  427.     if (!ansiAbort)
  428.         statupdate("      Done");
  429.  
  430.     anim = false;
  431.     while (!ansiAbort && (animtext->inkey() == ERR));
  432.  
  433.     animtext->cursor_off();
  434.     delete animtext;
  435.  
  436.     header->wtouch();
  437.     text->wtouch();
  438.     statupdate();
  439. }
  440.  
  441. void AnsiWindow::oneLine(int i)
  442. {
  443.     text->put(i, 0, linelist[position + i]->text);
  444. }
  445.  
  446. void AnsiWindow::lineCount()
  447. {
  448.     char tmp[22];
  449.  
  450.     sprintf(tmp, "Lines: %6d/%-7d", position + 1, NumOfLines);
  451.     header->put(0, COLS-23, tmp);
  452.     header->delay_update();
  453. }
  454.  
  455. void AnsiWindow::DrawBody()
  456. {
  457.     lineCount();
  458.  
  459.     for (int i = 0; i < y; i++)
  460.         if ((position + i) < NumOfLines)
  461.             oneLine(i);
  462.         else
  463.             for (int j = 0; j < x; j++)
  464.                 text->put(i, j, (chtype) ' ' | C_ANSIBACK);
  465. }
  466.  
  467. void AnsiWindow::set(const char *ansiSource, const char *winTitle)
  468. {
  469.     fromFile = 0;
  470.     source = (const unsigned char *) ansiSource;
  471.  
  472.     position = 0;
  473.     sourceDel = false;
  474.  
  475.     title = winTitle;
  476. }
  477.  
  478. void AnsiWindow::setFile(file_header *f, const char *winTitle)
  479. {
  480.     fromFile = f;
  481.     source = 0;
  482.  
  483.     position = 0;
  484.     sourceDel = true;
  485.     
  486.     title = winTitle ? winTitle : f->getName();
  487. }
  488.  
  489. void AnsiWindow::getFile()
  490. {
  491.     FILE *flist;
  492.     char *list;
  493.  
  494.     if ((flist = mm.workList->ftryopen(fromFile->getName(), "rb"))) {
  495.         off_t size = fromFile->getSize();
  496.  
  497.         list = new char[size + 1];
  498.         if (fread(list, 1, size, flist))
  499.             list[size] = '\0';
  500.         else {
  501.             delete list;
  502.             list = 0;
  503.         }
  504.         fclose(flist);
  505.     } else
  506.         list = 0;
  507.     
  508.     source = (unsigned char *) list;
  509. }
  510.  
  511. void AnsiWindow::MakeActive()
  512. {
  513.     int i;
  514.     bool end, oldAbort;
  515.  
  516.     header = new Win(1, COLS, 0, C_LBOTTSTAT);
  517.     text = new Win(LINES - 2, COLS, 1, C_ANSIBACK);
  518.     statbar = new Win(1, COLS, LINES - 1, C_LBOTTSTAT);
  519.  
  520.     text->attrib(0);
  521.  
  522.     anim = false;
  523.     oldAbort = ansiAbort;
  524.  
  525.     char *tmp = new char[COLS + 1];
  526.     i = sprintf(tmp, " " MM_TOPHEADER, MM_NAME, MM_MAJOR, MM_MINOR);
  527.     for (; i < COLS; i++)
  528.         tmp[i] = ' ';
  529.     tmp[i] = '\0';
  530.  
  531.     header->put(0, 0, tmp);
  532.     header->delay_update();
  533.     delete[] tmp;
  534.  
  535.     y = LINES - 2;
  536.     x = COLS;
  537.  
  538.     for (i = 0; i < 64; i++)
  539.         colorsused[i] = false;
  540.  
  541.     colorsused[PAIR_NUMBER(C_LBOTTSTAT)] = 1;  //don't remap stat bars
  542.  
  543.     oldcolorx = oldcolory = 0;
  544.  
  545.     init_pair(((COLOR_WHITE) << 3) + (COLOR_WHITE),
  546.         COLOR_WHITE, COLOR_WHITE);
  547.  
  548.     if (fromFile)
  549.         getFile();
  550.  
  551.     MakeChain(source);
  552.  
  553.     // This deals with the unavailability of color pair 0:
  554.  
  555.     if (colorsused[0]) {    // assumes COLOR_BLACK == 0
  556.  
  557.         // Find an unused color pair for black-on-black:
  558.  
  559.         for (end = false, i = 1; i < 64 && !end; i++)
  560.             if (!colorsused[i]) {
  561.                 end = true;
  562.                 oldcolorx = i >> 3;
  563.                 oldcolory = i & 7;
  564.                 init_pair(i, COLOR_BLACK, COLOR_BLACK);
  565.             }
  566.  
  567.         // Remap all instances of color pair 0 to the new pair:
  568.  
  569.         if (end) {
  570.             for (int j = 0; j < NumOfLines; j++) {
  571.                 curr = linelist[j];
  572.                 for (i = 0; i < (int) curr->length; i++)
  573.                     if (!PAIR_NUMBER(curr->text[i])) {
  574.                         curr->text[i] &= ~A_COLOR;
  575.                         curr->text[i] |=
  576.                           COL(oldcolorx, oldcolory);
  577.                     }
  578.             }
  579.         }
  580.     }
  581.  
  582.     DrawBody();
  583.     text->delay_update();
  584.  
  585.     statupdate();
  586.  
  587.     ansiAbort = oldAbort;
  588. }
  589.  
  590. void AnsiWindow::Delete()
  591. {
  592.     ansiAbort = true;
  593.  
  594.     // Restore remapped color pairs:
  595.  
  596.     if (oldcolorx + oldcolory)
  597.         init_pair((oldcolorx << 3) + oldcolory,
  598.             oldcolorx, oldcolory);
  599.     init_pair(((COLOR_WHITE) << 3) + (COLOR_WHITE),
  600.         COLOR_BLACK, COLOR_BLACK);
  601.  
  602.     DestroyChain();
  603.     delete header;
  604.     delete text;
  605.     delete statbar;
  606.     if (sourceDel)
  607.         delete[] source;
  608. }
  609.  
  610. void AnsiWindow::setPos(int x)
  611. {
  612.     position = x;
  613. }
  614.  
  615. int AnsiWindow::getPos()
  616. {
  617.     return position;
  618. }
  619.  
  620. searchret AnsiWindow::search(const char *item)
  621. {
  622.     chtype *ch;
  623.     searchret found = False;
  624.  
  625.     unsigned char *a, *buffer = new unsigned char[COLS + 1];
  626.  
  627.     for (int x = position + 1; (x < NumOfLines) && (found == False);
  628.         x++) {
  629.  
  630.         if (text->keypressed() == 27) {
  631.             found = Abort;
  632.             break;
  633.         }
  634.  
  635.         a = buffer;
  636.         ch = linelist[x]->text;
  637.         int count = linelist[x]->length;
  638.  
  639.         for (int y = 0; y < count; y++)
  640.             *a++ = (*ch++ & A_CHARTEXT);
  641.         *a = '\0';
  642.  
  643.         found = searchstr((char *) buffer, item) ? True : False;
  644.  
  645.         if (found == True) {
  646.             position = x;
  647.             DrawBody();
  648.             text->delay_update();
  649.         }
  650.     }
  651.  
  652.     delete[] buffer;
  653.     return found;
  654. }
  655.  
  656. void AnsiWindow::Save()
  657. {
  658.     FILE *fd;
  659.     const unsigned char *message = source;
  660.  
  661.     static char keepname[128];
  662.     char filename[128], oldfname[128];
  663.  
  664.     if (keepname[0])
  665.         strcpy(filename, keepname);
  666.     else {
  667.         sprintf(filename, "%.8s.ans", title);
  668.         unspace(filename);
  669.     }
  670.  
  671.     strcpy(oldfname, filename);
  672.  
  673.     if (interface->savePrompt("Save to file:", filename)) {
  674.         mychdir(mm.resourceObject->get(SaveDir));
  675.         if ((fd = fopen(filename, "at"))) {
  676.             while (*message)
  677.                 fputc(*message++, fd);
  678.             fclose(fd);
  679.         }
  680.         if (strcmp(filename, oldfname))
  681.             strcpy(keepname, filename);
  682.     }
  683. }
  684.  
  685. void AnsiWindow::KeyHandle(int key)
  686. {
  687.     switch (key) {
  688.     case KEY_UP:
  689.         if (position > 0) {
  690.             position--;
  691.             text->wscroll(-1);
  692.             oneLine(0);
  693.             lineCount();
  694.         }
  695.         break;
  696.     case KEY_DOWN:
  697.         if (position < NumOfLines - y) {
  698.             position++;
  699.             text->wscroll(1);
  700.             oneLine(y - 1);
  701.             lineCount();
  702.         }
  703.         break;
  704.     case KEY_HOME:
  705.         position = 0;
  706.         DrawBody();
  707.         break;
  708.     case KEY_END:
  709.         if (NumOfLines > y) {
  710.             position = NumOfLines - y;
  711.             DrawBody();
  712.         }
  713.         break;
  714.     case 'B':
  715.     case KEY_PPAGE:
  716.         position -= ((y < position) ? y : position);
  717.         DrawBody();
  718.         break;
  719.     case ' ':
  720.         position += y;
  721.         if (position < NumOfLines)
  722.             DrawBody();
  723.         else
  724.             interface->setKey('\n');
  725.         break;
  726.     case 'F':
  727.     case KEY_NPAGE:
  728.         if (position < NumOfLines - y) {
  729.             position += y;
  730.             if (position > NumOfLines - y)
  731.                 position = NumOfLines - y;
  732.             DrawBody();
  733.         }
  734.         break;
  735.     case 'V':
  736.     case 1:
  737.     case 22:
  738.         animate();
  739.         break;
  740.     case 'S':
  741.         Save();
  742.         DrawBody();
  743.         break;
  744.     case KEY_F(1):
  745.     case '?':
  746.         interface->changestate(ansi_help);
  747.     }
  748.     text->delay_update();
  749. }
  750.