home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 November / PCONLINE_11_99.ISO / filesbbs / OS2 / MMSRC029.ZIP / mmail-0.29 / interfac / basic.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1999-08-22  |  11.6 KB  |  673 lines

  1. /*
  2.  * MultiMail offline mail reader
  3.  * Interface, ShadowedWin, ListWindow
  4.  
  5.  Copyright (c) 1996 Kolossvary Tamas <thomas@tvnet.hu>
  6.  Copyright (c) 1997 John Zero <john@graphisoft.hu>
  7.  Copyright (c) 1999 William McBrine <wmcbrine@clark.net>
  8.  
  9.  Distributed under the GNU General Public License.
  10.  For details, see the file COPYING in the parent directory. */
  11.  
  12. #include "interfac.h"
  13.  
  14. Win::Win(int height, int width, int topline, chtype backg)
  15. {
  16.     // All windows are centered horizontally.
  17.  
  18.     win = newwin(height, width, topline, (COLS - width) / 2);
  19.     Clear(backg);
  20.  
  21.     keypad(win, TRUE);
  22.     cursor_off();
  23. }
  24.  
  25. Win::~Win()
  26. {
  27.     delwin(win);
  28. }
  29.  
  30. void Win::Clear(chtype backg)
  31. {
  32.     wbkgdset(win, backg | ' ');
  33.     werase(win);
  34.     wbkgdset(win, ' ');
  35.     wattrset(win, backg);
  36. }
  37.  
  38. void Win::put(int y, int x, chtype z)
  39. {
  40.     mvwaddch(win, y, x, z);
  41. }
  42.  
  43. void Win::put(int y, int x, char z)
  44. {
  45.     mvwaddch(win, y, x, (unsigned char) z);
  46. }
  47.  
  48. void Win::put(int y, int x, const chtype *z, int len)
  49. {
  50.     // The cast is to suppress warnings with certain implementations
  51.     // of curses that omit "const" from the prototype.
  52.  
  53.     if (!len)
  54.         len = COLS;
  55.  
  56.     mvwaddchnstr(win, y, x, (chtype *) z, len);
  57. }
  58.  
  59. int Win::put(int y, int x, const char *z)
  60. {
  61.     // Formerly just mvwaddstr() -- this ensures printing of (most)
  62.     // characters outside the ASCII range, instead of control codes.
  63.  
  64.     chtype z2;
  65.     int counter = 0;
  66.  
  67.     wmove(win, y, x);
  68.  
  69.     for (; *z; z++) {
  70.         z2 = ((unsigned char) *z);
  71.         switch (z2) {
  72. #ifndef __PDCURSES__            // unprintable control codes
  73.         case 14:        // double musical note
  74.             z2 = 19;
  75.             break;
  76.         case 15:        // much like an asterisk
  77.             z2 = '*';
  78.             break;
  79.         case 155:        // ESC + high bit = slash-o,
  80.             z2 = 'o';    // except in CP 437
  81.             break;
  82.         case 8:            // backspace
  83.         case 12:        // form feed
  84.             z2 = '#';
  85.             break;
  86. #endif
  87.         case 27:        // ESC
  88.             z2 = '`';
  89.             break;
  90.         case '\t':        // TAB
  91.             z2 = ' ';
  92.             int i = 7 - (counter % 8);
  93.             counter += i;
  94.             while (i--)
  95.                 waddch(win, z2);
  96.         }
  97.         if ((z2 < ' ') || (z2 > 126))
  98.             z2 |= A_ALTCHARSET;
  99.         waddch(win, z2);
  100.         counter++;
  101.     }
  102.     return counter;
  103. }
  104.  
  105. void Win::attrib(chtype z)
  106. {
  107.     wattrset(win, z);
  108. }
  109.  
  110. void Win::horizline(int y, int len)
  111. {
  112.     wmove(win, y, 1);
  113.     whline(win, ACS_HLINE, len);
  114. }
  115.  
  116. void Win::update()
  117. {
  118.     wrefresh(win);
  119. }
  120.  
  121. void Win::delay_update()
  122. {
  123.     wnoutrefresh(win);
  124. }
  125.  
  126. void Win::wtouch()
  127. {
  128.     touchwin(win);
  129.     wnoutrefresh(win);
  130. }
  131.  
  132. void Win::wscroll(int i)
  133. {
  134.     scrollok(win, TRUE);
  135.     wscrl(win, i);
  136.     scrollok(win, FALSE);
  137. }
  138.  
  139. /* The cursor toggling routines for PDCurses are ugly, I know; especially
  140.    in the mismatched use of the standard curs_set() to disable, and a
  141.    private PDCurses function to enable. Unfortunately, this seems to be
  142.    the only combination which works correctly on all platforms. (OS/2 Full
  143.    Screen sessions are a particular problem.)
  144. */
  145. void Win::cursor_on()
  146. {
  147.     leaveok(win, FALSE);
  148. #if defined (__PDCURSES__) && !defined(XCURSES)
  149.     PDC_set_cursor_mode(curs_start, curs_end);
  150. #else
  151.     curs_set(1);
  152. #endif
  153. }
  154.  
  155. void Win::cursor_off()
  156. {
  157.     leaveok(win, TRUE);
  158.     curs_set(0);
  159. }
  160.  
  161. int Win::keypressed()
  162. {
  163.     // Return key status immediately (non-blocking)
  164.  
  165.     nodelay(win, TRUE);
  166.     return wgetch(win);
  167. }
  168.  
  169. int Win::inkey()
  170. {
  171.     // Wait until key pressed, OR signal received (e.g., SIGWINCH)
  172.  
  173.     nodelay(win, FALSE);
  174.     return wgetch(win);
  175. }
  176.  
  177. void Win::boxtitle(chtype backg, const char *title, chtype titleAttrib)
  178. {
  179.     wattrset(win, backg);
  180.     box(win, 0, 0);
  181.  
  182.     if (title) {
  183.         put(0, 2, ACS_RTEE);
  184.         put(0, 3 + strlen(title), ACS_LTEE);
  185.         wattrset(win, titleAttrib);
  186.         put(0, 3, title);
  187.         wattrset(win, backg);
  188.     }
  189. }
  190.  
  191. ShadowedWin::ShadowedWin(int height, int width, int topline, chtype backg,
  192.     const char *title, chtype titleAttrib) : Win(height, width,
  193.     topline, backg)
  194. {
  195.     // The text to be in "shadow" is taken from different windows,
  196.     // depending on the curses implementation. Note that the default,
  197.     // "stdscr", just makes the shadowed area solid black; only with
  198.     // ncurses and PDCurses does it draw proper shadows.
  199.  
  200. #ifdef USE_SHADOWS
  201.     int i, j;
  202.     chtype *right, *lower;
  203. # ifndef NCURSES_VERSION
  204. #  ifdef __PDCURSES__
  205.     WINDOW *&newscr = curscr;
  206. #  else
  207.     WINDOW *&newscr = stdscr;
  208. #  endif
  209. # endif
  210.     int xlimit = 2 + (interface->active() != letter);
  211.     int firstcol = (COLS - width) / 2;
  212.  
  213.     while ((width + firstcol) > (COLS - xlimit))
  214.         width--;
  215.     while ((height + topline) > (LINES - 1))
  216.         height--;
  217.  
  218.     right = new chtype[(height - 1) << 1];
  219.     lower = new chtype[width + 1];
  220.  
  221.     // Gather the old text and attribute info:
  222.  
  223.     for (i = 0; i < (height - 1); i++)
  224.         for (j = 0; j < 2; j++)
  225.             right[(i << 1) + j] = (mvwinch(newscr,
  226.                 (topline + i + 1), (firstcol + width + j)) &
  227.                     (A_CHARTEXT | A_ALTCHARSET));
  228.  
  229.     mvwinchnstr(newscr, (topline + height), (firstcol + 2), lower, width);
  230.  
  231.     // Redraw it in darkened form:
  232.  
  233.     shadow = newwin(height, width, topline + 1, firstcol + 2);
  234.     leaveok(shadow, TRUE);
  235.  
  236.     for (i = 0; i < (height - 1); i++)
  237.         for (j = 0; j < 2; j++)
  238.             mvwaddch(shadow, i, width - 2 + j,
  239.                 (right[(i << 1) + j] | C_SHADOW));
  240.     for (i = 0; i < width; i++)
  241.         mvwaddch(shadow, height - 1, i, (lower[i] & (A_CHARTEXT |
  242.             A_ALTCHARSET)) | C_SHADOW);
  243.  
  244.     wnoutrefresh(shadow);
  245. #endif
  246.     boxtitle(backg, title, titleAttrib);
  247.  
  248. #ifdef USE_SHADOWS
  249.     delete[] lower;
  250.     delete[] right;
  251. #endif
  252. }
  253.  
  254. ShadowedWin::~ShadowedWin()
  255. {
  256. #ifdef USE_SHADOWS
  257.     delwin(shadow);
  258. #endif
  259. }
  260.  
  261. void ShadowedWin::touch()
  262. {
  263. #ifdef USE_SHADOWS
  264.     touchwin(shadow);
  265.     wnoutrefresh(shadow);
  266. #endif
  267.     Win::wtouch();
  268. }
  269.  
  270. int ShadowedWin::getstring(int y, int x, char *string, int maxlen,
  271.         chtype bg_color, chtype fg_color)
  272. {
  273.     int i, j, end, c;
  274.     char *tmp = new char[maxlen];
  275.  
  276.     cropesp(string);
  277.  
  278.     wattrset(win, fg_color);
  279.     for (i = 0; i < maxlen; i++) {
  280.         put(y, x + i, ACS_BOARD);
  281.         tmp[i] = '\0';
  282.     }
  283.     maxlen--;
  284.  
  285.     put(y, x, string);    // Hmm...
  286.  
  287.     cursor_on();
  288.     update();
  289.  
  290.     i = end = 0;
  291.     bool first_key = true;
  292.  
  293.     while (!end) {
  294.         c = inkey();
  295.  
  296.         // these keys make the string "accepted" and editable:
  297.         if (first_key) {
  298.             first_key = false;
  299.  
  300.             switch (c) {
  301.             case KEY_LEFT:
  302.             case KEY_RIGHT:
  303.             case 8:
  304.             case KEY_BACKSPACE:
  305.             case KEY_HOME:
  306.             case KEY_END:
  307.                 strncpy(tmp, string, maxlen);
  308.                 i = strlen(tmp);
  309.             }
  310.         }
  311.  
  312.         switch (c) {
  313.         case KEY_DOWN:
  314.             end++;
  315.         case KEY_UP:
  316.             end++;
  317.         case MM_ENTER:
  318.             end++;
  319.         case '\033':        // Escape
  320.             end++;
  321.             break;
  322.         case KEY_LEFT:
  323.             if (i > 0)
  324.                 i--;
  325.             break;
  326.         case KEY_RIGHT:
  327.             if ((i < maxlen) && tmp[i])
  328.                 i++;
  329.             break;
  330.         case 127:
  331.         case KEY_DC:        // Delete key 
  332.             strncpy(&tmp[i], &tmp[i + 1], maxlen - i);
  333.             tmp[maxlen] = '\0';
  334.             break;
  335.         case 8:            // Ctrl-H
  336.         case KEY_BACKSPACE:
  337.             if (i > 0) {
  338.                 strncpy(&tmp[i - 1], &tmp[i], maxlen - i);
  339.                 tmp[maxlen] = '\0';
  340.                 i--;
  341.             }
  342.             break;
  343.         case KEY_HOME:
  344.             i = 0;
  345.             break;
  346.         case KEY_END:
  347.             i = strlen(tmp);
  348.             break;
  349.         default:
  350.             for (j = maxlen; j > i; j--)
  351.                 tmp[j] = tmp[j - 1];
  352.             tmp[i] = c;
  353.             if (i < maxlen)
  354.                 i++;
  355.         }
  356.         for (j = 0; j <= maxlen; j++)
  357.             put(y, x + j, (tmp[j] ? (unsigned char) tmp[j] :
  358.                 ACS_BOARD));
  359.         wmove(win, y, x + i);
  360.         update();
  361.     }
  362.  
  363.     if (tmp[0])
  364.         strcpy(string, tmp);
  365.  
  366.     wattrset(win, bg_color);
  367.  
  368.     j = strlen(string);
  369.     for (i = 0; i <= maxlen; i++)
  370.         put(y, x + i, ((i < j) ? string[i] : ' '));
  371.  
  372.     update();
  373.     cursor_off();
  374.  
  375.     delete[] tmp;
  376.  
  377.     return end - 1;
  378. }
  379.  
  380. InfoWin::InfoWin(int height, int width, int topline, chtype backg,
  381.     const char *title, chtype titleAttrib, int bottx, int topx) :
  382.     ShadowedWin(height, width, topline, backg, title, titleAttrib)
  383. {
  384.     lineBuf = new char[COLS];
  385.     info = new Win(height - bottx, width - 2, topline + topx, backg);
  386. }
  387.  
  388. InfoWin::~InfoWin()
  389. {
  390.     delete info;
  391.     delete[] lineBuf;
  392. }
  393.  
  394. void InfoWin::irefresh()
  395. {
  396.     info->delay_update();
  397. }
  398.  
  399. void InfoWin::touch()
  400. {
  401.     ShadowedWin::touch();
  402.     info->wtouch();
  403. }
  404.  
  405. void InfoWin::oneline(int i, chtype ch)
  406. {
  407.     info->attrib(ch);
  408.     info->put(i, 0, lineBuf);
  409. }
  410.  
  411. void InfoWin::iscrl(int i)
  412. {
  413.     info->wscroll(i);
  414. }
  415.  
  416. ListWindow::ListWindow()
  417. {
  418.     position = active = 0;
  419.     oldPos = oldActive = oldHigh = -100;
  420.     lynxNav = mm.resourceObject->getInt(UseLynxNav);
  421.     useScrollBars = mm.resourceObject->getInt(UseScrollBars);
  422. }
  423.  
  424. ListWindow::~ListWindow()
  425. {
  426. }
  427.  
  428. void ListWindow::checkPos(int limit)
  429. {
  430.     if (active >= limit)
  431.         active = limit - 1;
  432.  
  433.     if (active < 0)
  434.         active = 0;
  435.  
  436.     if (active < position)
  437.         position = active;
  438.     else
  439.         if (active - position >= list_max_y)
  440.             position = active - list_max_y + 1;
  441.  
  442.     if (limit > list_max_y) {
  443.         if (position < 0)
  444.             position = 0;
  445.         else
  446.             if (position > limit - list_max_y)
  447.                 position = limit - list_max_y;
  448.     } else
  449.         position = 0;
  450. }
  451.  
  452. chtype ListWindow::setHighlight(chtype ch)
  453. {
  454.     chtype backg = PAIR_NUMBER(ch) & 7;
  455.  
  456.     switch (backg) {
  457.     case COLOR_BLACK:
  458.     case COLOR_RED:
  459.     case COLOR_BLUE:
  460.         ch = REVERSE(COLOR_WHITE, COLOR_BLACK);
  461.         break;
  462.     default:
  463.         ch = REVERSE(COLOR_BLACK, COLOR_WHITE);
  464.     }
  465.     return ch;
  466. }
  467.  
  468. void ListWindow::Draw()
  469. {
  470.     const chtype current = ACS_DIAMOND | A_ALTCHARSET;
  471.     const chtype old =
  472. #ifdef __PDCURSES__
  473.         ACS_BOARD;
  474. #else
  475.         ACS_CKBOARD;
  476. #endif
  477.     int i, j, limit = NumOfItems();
  478.  
  479.     checkPos(limit);
  480.  
  481.     // Highlight bar:
  482.  
  483.     if (useScrollBars && (limit > list_max_y)) {
  484.         if (oldHigh == -100) {
  485.             list->attrib(borderCol);
  486.             for (i = top_offset; i < (top_offset +
  487.                 list_max_y); i++)
  488.                 list->put(i, list_max_x + 1, old);
  489.         }
  490.  
  491.         i = active * list_max_y / limit;
  492.  
  493.         if (i != oldHigh) {
  494.             list->attrib(borderCol);
  495.             list->put(top_offset + oldHigh, list_max_x + 1, old);
  496.             list->put(top_offset + i, list_max_x + 1, current);
  497.             list->delay_update();
  498.  
  499.             oldHigh = i;
  500.         }
  501.     }
  502.  
  503.     // Scroll or redraw:
  504.  
  505.     i = position - oldPos;
  506.     switch (i) {
  507.     case -1:
  508.     case 1:
  509.         list->iscrl(i);
  510.     case 0:
  511.         if (active != oldActive) {
  512.             j = oldActive - position;
  513.             if ((j >= 0) && (j < list_max_y))
  514.                 oneLine(j);
  515.         }
  516.         oneLine(active - position);
  517.         list->irefresh();
  518.         break;
  519.     default:
  520.         for (j = 0; j < list_max_y; j++) {
  521.             oneLine(j);
  522.             if (position + j == limit - 1)
  523.                 j = list_max_y;
  524.         }
  525.         list->touch();
  526.     }
  527.     oldPos = position;
  528.     oldActive = active;
  529. }
  530.  
  531. void ListWindow::DrawOne(int i, chtype ch)
  532. {
  533.     // Highlight, if drawing the active bar
  534.  
  535.     if (position + i == active)
  536.         ch = setHighlight(ch);
  537.  
  538.     list->oneline(i, ch);
  539. }
  540.  
  541. void ListWindow::DrawAll()
  542. {
  543.     oldPos = oldActive = oldHigh = -100;
  544.     Draw();
  545. }
  546.  
  547. void ListWindow::Move(direction dir)
  548. {
  549.     int limit = NumOfItems();
  550.  
  551.     switch (dir) {
  552.     case UP:
  553.         active--;
  554.         break;
  555.     case DOWN:
  556.         active++;
  557.         break;
  558.     case PGUP:
  559.         position -= list_max_y;
  560.         active -= list_max_y;
  561.         break;
  562.     case PGDN:
  563.         position += list_max_y;
  564.         active += list_max_y;
  565.         break;
  566.     case HOME:
  567.         active = 0;
  568.         break;
  569.     case END:
  570.         active = limit - 1;
  571.         break;
  572.     }
  573.     checkPos(limit);
  574. }
  575.  
  576. void ListWindow::setActive(int x)
  577. {
  578.     active = x;
  579. }
  580.  
  581. int ListWindow::getActive()
  582. {
  583.     return active;
  584. }
  585.  
  586. searchret ListWindow::search(const char *item, int mode)
  587. {
  588.     int limit = NumOfItems();
  589.     searchret found = False;
  590.  
  591.     statetype orgstate = interface->active();
  592.     for (int x = active + 1; (x < limit) && (found == False); x++) {
  593.         if (list->keypressed() == 27) {
  594.             found = Abort;
  595.             break;
  596.         }
  597.         found = oneSearch(x, item, mode);
  598.         if (found == True) {
  599.             active = x;
  600.             if (interface->active() == orgstate)
  601.                 Draw();
  602.         }
  603.     }
  604.  
  605.     checkPos(limit);
  606.     return found;
  607. }
  608.  
  609. void ListWindow::Prev()
  610. {
  611.     Move(UP);
  612. }
  613.  
  614. void ListWindow::Next()
  615. {
  616.     Move(DOWN);
  617. }
  618.  
  619. bool ListWindow::KeyHandle(int key)
  620. {
  621.     bool draw = true, end = false;
  622.  
  623.     switch (key) {
  624.     case KEY_LEFT:
  625.         if (lynxNav) {
  626.             draw = false;
  627.             end = interface->back();
  628.             break;
  629.         }
  630.     case MM_MINUS:
  631.         Prev();
  632.         break;
  633.     case KEY_RIGHT:
  634.         if (lynxNav) {
  635.             draw = false;
  636.             end = interface->select();
  637.             break;
  638.         }
  639.     case '\t':
  640.     case MM_PLUS:
  641.         Next();
  642.         break;
  643.     case KEY_DOWN:
  644.         Move(DOWN);
  645.         break;
  646.     case KEY_UP:
  647.         Move(UP);
  648.         break;
  649.     case KEY_HOME:
  650.         Move(HOME);
  651.         break;
  652.     case KEY_END:
  653.         Move(END);
  654.         break;
  655.     case 'B':
  656.     case KEY_PPAGE:
  657.         Move(PGUP);
  658.         break;
  659.     case ' ':
  660.     case 'F':
  661.     case KEY_NPAGE:
  662.         Move(PGDN);
  663.         break;
  664.     default:
  665.         draw = false;
  666.         end = extrakeys(key);
  667.     }
  668.     if (draw)
  669.         Draw();
  670.  
  671.     return end;
  672. }
  673.