home *** CD-ROM | disk | FTP | other *** search
/ Computer Select (Limited Edition) / Computer Select.iso / dobbs / v17n02 / dflt10.exe / TEXTBOX.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-13  |  26.9 KB  |  865 lines

  1. /* ------------- textbox.c ------------ */
  2.  
  3. #include "dflat.h"
  4.  
  5. static void ComputeWindowTop(WINDOW);
  6. static void ComputeWindowLeft(WINDOW);
  7. static int ComputeVScrollBox(WINDOW);
  8. static int ComputeHScrollBox(WINDOW);
  9. static void MoveScrollBox(WINDOW, int);
  10. static char *GetTextLine(WINDOW, int);
  11.  
  12. int VSliding;
  13. int HSliding;
  14.  
  15. /* ------------ ADDTEXT Message -------------- */
  16. static int AddTextMsg(WINDOW wnd, PARAM p1)
  17. {
  18.     /* --- append text to the textbox's buffer --- */
  19.     unsigned adln = strlen((char *)p1);
  20.     if (adln > (unsigned)0xfff0)
  21.         return FALSE;
  22.     if (wnd->text != NULL)    {
  23.         /* ---- appending to existing text ---- */
  24.         unsigned txln = strlen(wnd->text);
  25.         if ((long)txln+adln > (unsigned) 0xfff0)
  26.             return FALSE;
  27.         if (txln+adln > wnd->textlen)    {
  28.             wnd->text = realloc(wnd->text, txln+adln+3);
  29.             wnd->textlen = txln+adln+1;
  30.         }
  31.     }
  32.     else    {
  33.         /* ------ 1st text appended ------ */
  34.         wnd->text = calloc(1, adln+3);
  35.         wnd->textlen = adln+1;
  36.     }
  37.     if (wnd->text != NULL)    {
  38.         /* ---- append the text ---- */
  39.         strcat(wnd->text, (char*) p1);
  40.         strcat(wnd->text, "\n");
  41.         BuildTextPointers(wnd);
  42.         return TRUE;
  43.     }
  44.     return FALSE;
  45. }
  46.  
  47. /* ------------ SETTEXT Message -------------- */
  48. static int SetTextMsg(WINDOW wnd, PARAM p1)
  49. {
  50.     /* -- assign new text value to textbox buffer -- */
  51.     char *cp;
  52.     unsigned int len;
  53.     cp = (void *) p1;
  54.     len = strlen(cp)+1;
  55.     if (wnd->text == NULL || wnd->textlen < len)    {
  56.         wnd->textlen = len;
  57.         if ((wnd->text=realloc(wnd->text, len+1)) == NULL)
  58.             return FALSE;
  59.         wnd->text[len] = '\0';
  60.     }
  61.     strcpy(wnd->text, cp);
  62.     BuildTextPointers(wnd);
  63.     wnd->wtop = wnd->wleft = 0;
  64.     return TRUE;
  65. }
  66.  
  67. /* ------------ CLEARTEXT Message -------------- */
  68. static void ClearTextMsg(WINDOW wnd)
  69. {
  70.     /* ----- clear text from textbox ----- */
  71.     if (wnd->text != NULL)
  72.         free(wnd->text);
  73.     wnd->text = NULL;
  74.     wnd->textlen = 0;
  75.     wnd->wlines = 0;
  76.     wnd->textwidth = 0;
  77.     wnd->wtop = wnd->wleft = 0;
  78.     ClearTextBlock(wnd);
  79.     ClearTextPointers(wnd);
  80. }
  81.  
  82. /* ------------ KEYBOARD Message -------------- */
  83. static int KeyboardMsg(WINDOW wnd, PARAM p1)
  84. {
  85.     switch ((int) p1)    {
  86.         case UP:
  87.             return SendMessage(wnd,SCROLL,FALSE,0);
  88.         case DN:
  89.             return SendMessage(wnd,SCROLL,TRUE,0);
  90.         case FWD:
  91.             return SendMessage(wnd,HORIZSCROLL,TRUE,0);
  92.         case BS:
  93.             return SendMessage(wnd,HORIZSCROLL,FALSE,0);
  94.         case PGUP:
  95.             return SendMessage(wnd,SCROLLPAGE,FALSE,0);
  96.         case PGDN:
  97.             return SendMessage(wnd,SCROLLPAGE,TRUE,0);
  98.         case CTRL_PGUP:
  99.             return SendMessage(wnd,HORIZPAGE,FALSE,0);
  100.         case CTRL_PGDN:
  101.             return SendMessage(wnd,HORIZPAGE,TRUE,0);
  102.         case HOME:
  103.             return SendMessage(wnd,SCROLLDOC,TRUE,0);
  104.         case END:
  105.             return SendMessage(wnd,SCROLLDOC,FALSE,0);
  106.         default:
  107.             break;
  108.     }
  109.     return FALSE;
  110. }
  111.  
  112. /* ------------ LEFT_BUTTON Message -------------- */
  113. static int LeftButtonMsg(WINDOW wnd, PARAM p1, PARAM p2)
  114. {
  115.     int mx = (int) p1 - GetLeft(wnd);
  116.     int my = (int) p2 - GetTop(wnd);
  117.     if (TestAttribute(wnd, VSCROLLBAR) &&
  118.                         mx == WindowWidth(wnd)-1)    {
  119.         /* -------- in the right border ------- */
  120.         if (my == 0 || my == ClientHeight(wnd)+1)
  121.             /* --- above or below the scroll bar --- */
  122.             return FALSE;
  123.         if (my == 1)
  124.             /* -------- top scroll button --------- */
  125.             return SendMessage(wnd, SCROLL, FALSE, 0);
  126.         if (my == ClientHeight(wnd))
  127.             /* -------- bottom scroll button --------- */
  128.             return SendMessage(wnd, SCROLL, TRUE, 0);
  129.         /* ---------- in the scroll bar ----------- */
  130.         if (!VSliding && my-1 == wnd->VScrollBox)    {
  131.             RECT rc;
  132.             VSliding = TRUE;
  133.             rc.lf = rc.rt = GetRight(wnd);
  134.             rc.tp = GetTop(wnd)+2;
  135.             rc.bt = GetBottom(wnd)-2;
  136.             return SendMessage(NULL, MOUSE_TRAVEL,
  137.                 (PARAM) &rc, 0);
  138.         }
  139.         if (my-1 < wnd->VScrollBox)
  140.             return SendMessage(wnd,SCROLLPAGE,FALSE,0);
  141.         if (my-1 > wnd->VScrollBox)
  142.             return SendMessage(wnd,SCROLLPAGE,TRUE,0);
  143.     }
  144.     if (TestAttribute(wnd, HSCROLLBAR) &&
  145.                         my == WindowHeight(wnd)-1) {
  146.         /* -------- in the bottom border ------- */
  147.         if (mx == 0 || my == ClientWidth(wnd)+1)
  148.             /* ------  outside the scroll bar ---- */
  149.             return FALSE;
  150.         if (mx == 1)
  151.             return SendMessage(wnd, HORIZSCROLL,FALSE,0);
  152.         if (mx == WindowWidth(wnd)-2)
  153.             return SendMessage(wnd, HORIZSCROLL,TRUE,0);
  154.         if (!HSliding && mx-1 == wnd->HScrollBox)    {
  155.             /* --- hit the scroll box --- */
  156.             RECT rc;
  157.             rc.lf = GetLeft(wnd)+2;
  158.             rc.rt = GetRight(wnd)-2;
  159.             rc.tp = rc.bt = GetBottom(wnd);
  160.             /* - keep the mouse in the scroll bar - */
  161.             SendMessage(NULL,MOUSE_TRAVEL,(PARAM)&rc,0);
  162.             HSliding = TRUE;
  163.             return TRUE;
  164.         }
  165.         if (mx-1 < wnd->HScrollBox)
  166.             return SendMessage(wnd,HORIZPAGE,FALSE,0);
  167.         if (mx-1 > wnd->HScrollBox)
  168.             return SendMessage(wnd,HORIZPAGE,TRUE,0);
  169.     }
  170.     return FALSE;
  171. }
  172.  
  173. /* ------------ MOUSE_MOVED Message -------------- */
  174. static int MouseMovedMsg(WINDOW wnd, PARAM p1, PARAM p2)
  175. {
  176.     int mx = (int) p1 - GetLeft(wnd);
  177.     int my = (int) p2 - GetTop(wnd);
  178.     if (VSliding)    {
  179.         /* ---- dragging the vertical scroll box --- */
  180.         if (my-1 != wnd->VScrollBox)    {
  181.             foreground = FrameForeground(wnd);
  182.             background = FrameBackground(wnd);
  183.             wputch(wnd, SCROLLBARCHAR, WindowWidth(wnd)-1,
  184.                     wnd->VScrollBox+1);
  185.             wnd->VScrollBox = my-1;
  186.             wputch(wnd, SCROLLBOXCHAR, WindowWidth(wnd)-1,
  187.                     my);
  188.         }
  189.         return TRUE;
  190.     }
  191.     if (HSliding)    {
  192.         /* --- dragging the horizontal scroll box --- */
  193.         if (mx-1 != wnd->HScrollBox)    {
  194.             foreground = FrameForeground(wnd);
  195.             background = FrameBackground(wnd);
  196.             wputch(wnd, SCROLLBARCHAR, wnd->HScrollBox+1,
  197.                     WindowHeight(wnd)-1);
  198.             wnd->HScrollBox = mx-1;
  199.             wputch(wnd, SCROLLBOXCHAR, mx, WindowHeight(wnd)-1);
  200.         }
  201.         return TRUE;
  202.     }
  203.     return FALSE;
  204. }
  205.  
  206. /* ------------ BUTTON_RELEASED Message -------------- */
  207. static void ButtonReleasedMsg(WINDOW wnd)
  208. {
  209.     if (HSliding || VSliding)    {
  210.         /* release the mouse ouside the scroll bar */
  211.         SendMessage(NULL, MOUSE_TRAVEL, 0, 0);
  212.         VSliding ? ComputeWindowTop(wnd):ComputeWindowLeft(wnd);
  213.         SendMessage(wnd, PAINT, 0, 0);
  214.         SendMessage(wnd, KEYBOARD_CURSOR, 0, 0);
  215.         VSliding = HSliding = FALSE;
  216.     }
  217. }
  218.  
  219. /* ------------ SCROLL Message -------------- */
  220. static int ScrollMsg(WINDOW wnd, PARAM p1)
  221. {
  222.     /* ---- vertical scroll one line ---- */
  223.     if (p1)    {
  224.         /* ----- scroll one line up ----- */
  225.         if (wnd->wtop+ClientHeight(wnd) >= wnd->wlines)
  226.             return FALSE;
  227.         wnd->wtop++;
  228.     }
  229.     else    {
  230.         /* ----- scroll one line down ----- */
  231.         if (wnd->wtop == 0)
  232.             return FALSE;
  233.         --wnd->wtop;
  234.     }
  235.     if (isVisible(wnd))    {
  236.         RECT rc;
  237.         rc = ClipRectangle(wnd, ClientRect(wnd));
  238.         if (ValidRect(rc))    {
  239.             /* ---- scroll the window ----- */
  240.             if (wnd != inFocus)
  241.                 SendMessage(wnd, PAINT, 0, 0);
  242.             else    {
  243.                 scroll_window(wnd, rc, (int)p1);
  244.                 if (!(int)p1)
  245.                     /* -- write top line (down) -- */
  246.                     WriteTextLine(wnd,NULL,wnd->wtop,FALSE);
  247.                 else    {
  248.                     /* -- write bottom line (up) -- */
  249.                     int y=RectBottom(rc)-GetClientTop(wnd);
  250.                     WriteTextLine(wnd, NULL,
  251.                         wnd->wtop+y, FALSE);
  252.                 }
  253.             }
  254.         }
  255.         /* ---- reset the scroll box ---- */
  256.         if (TestAttribute(wnd, VSCROLLBAR))    {
  257.             int vscrollbox = ComputeVScrollBox(wnd);
  258.             if (vscrollbox != wnd->VScrollBox)
  259.                 MoveScrollBox(wnd, vscrollbox);
  260.         }
  261.         return TRUE;
  262.     }
  263.     return FALSE;
  264. }
  265.  
  266. /* ------------ HORIZSCROLL Message -------------- */
  267. static int HorizScrollMsg(WINDOW wnd, PARAM p1)
  268. {
  269.     /* --- horizontal scroll one column --- */
  270.     if (p1)    {
  271.         /* --- scroll left --- */
  272.         if (wnd->wleft + ClientWidth(wnd)-1 >= wnd->textwidth)
  273.             return FALSE;
  274.         wnd->wleft++;
  275.     }
  276.     else    {
  277.         /* --- scroll right --- */
  278.         if (wnd->wleft == 0)
  279.             return FALSE;
  280.         --wnd->wleft;
  281.     }
  282.     SendMessage(wnd, PAINT, 0, 0);
  283.     return TRUE;
  284. }
  285.  
  286. /* ------------  SCROLLPAGE Message -------------- */
  287. static void ScrollPageMsg(WINDOW wnd, PARAM p1)
  288. {
  289.     /* --- vertical scroll one page --- */
  290.     if ((int) p1 == FALSE)    {
  291.         /* ---- page up ---- */
  292.         if (wnd->wtop)
  293.             wnd->wtop -= ClientHeight(wnd);
  294.     }
  295.     else     {
  296.         /* ---- page down ---- */
  297.         if (wnd->wtop+ClientHeight(wnd) < wnd->wlines) {
  298.             wnd->wtop += ClientHeight(wnd);
  299.             if (wnd->wtop>wnd->wlines-ClientHeight(wnd))
  300.                 wnd->wtop=wnd->wlines-ClientHeight(wnd);
  301.         }
  302.     }
  303.     if (wnd->wtop < 0)
  304.         wnd->wtop = 0;
  305.     SendMessage(wnd, PAINT, 0, 0);
  306. }
  307.  
  308. /* ------------ HORIZSCROLLPAGE Message -------------- */
  309. static void HorizScrollPageMsg(WINDOW wnd, PARAM p1)
  310. {
  311.     /* --- horizontal scroll one page --- */
  312.     if ((int) p1 == FALSE)
  313.         /* ---- page left ----- */
  314.         wnd->wleft -= ClientWidth(wnd);
  315.     else    {
  316.         /* ---- page right ----- */
  317.         wnd->wleft += ClientWidth(wnd);
  318.         if (wnd->wleft > wnd->textwidth-ClientWidth(wnd))
  319.             wnd->wleft = wnd->textwidth-ClientWidth(wnd);
  320.     }
  321.     if (wnd->wleft < 0)
  322.         wnd->wleft = 0;
  323.     SendMessage(wnd, PAINT, 0, 0);
  324. }
  325.  
  326. /* ------------ SCROLLDOC Message -------------- */
  327. static void ScrollDocMsg(WINDOW wnd, PARAM p1)
  328. {
  329.     /* --- scroll to beginning or end of document --- */
  330.     if ((int) p1)
  331.         wnd->wtop = wnd->wleft = 0;
  332.     else if (wnd->wtop+ClientHeight(wnd) < wnd->wlines){
  333.         wnd->wtop = wnd->wlines-ClientHeight(wnd);
  334.         wnd->wleft = 0;
  335.     }
  336.     SendMessage(wnd, PAINT, 0, 0);
  337. }
  338.  
  339. /* ------------ PAINT Message -------------- */
  340. static int PaintMsg(WINDOW wnd, PARAM p1, PARAM p2)
  341. {
  342.     /* ------ paint the client area ----- */
  343.     RECT rc, rcc;
  344.     int y;
  345.     char blankline[201];
  346.  
  347.     /* ----- build the rectangle to paint ----- */
  348.     if ((RECT *)p1 == NULL)
  349.         rc=RelativeWindowRect(wnd, WindowRect(wnd));
  350.     else
  351.         rc= *(RECT *)p1;
  352.     if (TestAttribute(wnd, HASBORDER) &&
  353.             RectRight(rc) >= WindowWidth(wnd)-1) {
  354.         if (RectLeft(rc) >= WindowWidth(wnd)-1)
  355.             return FALSE;
  356.         RectRight(rc) = WindowWidth(wnd)-2;
  357.     }
  358.     rcc = AdjustRectangle(wnd, rc);
  359.  
  360.     if (!p2 && wnd != inFocus)
  361.         ClipString++;
  362.  
  363.     /* ----- blank line for padding ----- */
  364.     memset(blankline, ' ', SCREENWIDTH);
  365.     blankline[RectRight(rcc)+1] = '\0';
  366.  
  367.     /* ------- each line within rectangle ------ */
  368.     for (y = RectTop(rc); y <= RectBottom(rc); y++){
  369.         int yy;
  370.         /* ---- test outside of Client area ---- */
  371.         if (TestAttribute(wnd,
  372.                     HASBORDER | HASTITLEBAR))    {
  373.             if (y < TopBorderAdj(wnd))
  374.                 continue;
  375.             if (y > WindowHeight(wnd)-2)
  376.                 continue;
  377.         }
  378.         yy = y-TopBorderAdj(wnd);
  379.         if (yy < wnd->wlines-wnd->wtop)
  380.             /* ---- paint a text line ---- */
  381.             WriteTextLine(wnd, &rc,
  382.                         yy+wnd->wtop, FALSE);
  383.         else    {
  384.             /* ---- paint a blank line ---- */
  385.             SetStandardColor(wnd);
  386.             writeline(wnd, blankline+RectLeft(rcc),
  387.                     RectLeft(rcc)+1, y, FALSE);
  388.         }
  389.     }
  390.     /* ------- position the scroll box ------- */
  391.     if (TestAttribute(wnd, VSCROLLBAR|HSCROLLBAR)) {
  392.         int hscrollbox = ComputeHScrollBox(wnd);
  393.         int vscrollbox = ComputeVScrollBox(wnd);
  394.         if (hscrollbox != wnd->HScrollBox ||
  395.                 vscrollbox != wnd->VScrollBox)    {
  396.             wnd->HScrollBox = hscrollbox;
  397.             wnd->VScrollBox = vscrollbox;
  398.             SendMessage(wnd, BORDER, p1, 0);
  399.         }
  400.     }
  401.     if (!p2 && wnd != inFocus)
  402.         --ClipString;
  403.     return TRUE;
  404. }
  405.  
  406. /* ------------ CLOSE_WINDOW Message -------------- */
  407. static void CloseWindowMsg(WINDOW wnd)
  408. {
  409.     SendMessage(wnd, CLEARTEXT, 0, 0);
  410.     if (wnd->TextPointers != NULL)    {
  411.         free(wnd->TextPointers);
  412.         wnd->TextPointers = NULL;
  413.     }
  414. }
  415.  
  416. /* ----------- TEXTBOX Message-processing Module ----------- */
  417. int TextBoxProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
  418. {
  419.     switch (msg)    {
  420.         case CREATE_WINDOW:
  421.             wnd->HScrollBox = wnd->VScrollBox = 1;
  422.             ClearTextPointers(wnd);
  423.             break;
  424.         case ADDTEXT:
  425.             return AddTextMsg(wnd, p1);
  426.         case SETTEXT:
  427.             return SetTextMsg(wnd, p1);
  428.         case CLEARTEXT:
  429.             ClearTextMsg(wnd);
  430.             break;
  431.         case KEYBOARD:
  432.             if (WindowMoving || WindowSizing)
  433.                 break;
  434.             if (KeyboardMsg(wnd, p1))
  435.                 return TRUE;
  436.             break;
  437.         case LEFT_BUTTON:
  438.             if (WindowSizing || WindowMoving)
  439.                 return FALSE;
  440.             if (LeftButtonMsg(wnd, p1, p2))
  441.                 return TRUE;
  442.             break;
  443.         case MOUSE_MOVED:
  444.             if (MouseMovedMsg(wnd, p1, p2))
  445.                 return TRUE;
  446.             break;
  447.         case BUTTON_RELEASED:
  448.             ButtonReleasedMsg(wnd);
  449.             break;
  450.         case SCROLL:
  451.             return ScrollMsg(wnd, p1);
  452.         case HORIZSCROLL:
  453.             return HorizScrollMsg(wnd, p1);
  454.         case SCROLLPAGE:
  455.             ScrollPageMsg(wnd, p1);
  456.             return TRUE;
  457.         case HORIZPAGE:
  458.             HorizScrollPageMsg(wnd, p1);
  459.             return TRUE;
  460.         case SCROLLDOC:
  461.             ScrollDocMsg(wnd, p1);
  462.             return TRUE;
  463.         case PAINT:
  464.             if (isVisible(wnd) && wnd->wlines)    {
  465.                 PaintMsg(wnd, p1, p2);
  466.                 return FALSE;
  467.             }
  468.             break;
  469.         case CLOSE_WINDOW:
  470.             CloseWindowMsg(wnd);
  471.             break;
  472.         default:
  473.             break;
  474.     }
  475.     return BaseWndProc(TEXTBOX, wnd, msg, p1, p2);
  476. }
  477.  
  478. /* ------ compute the vertical scroll box position from
  479.                    the text pointers --------- */
  480. static int ComputeVScrollBox(WINDOW wnd)
  481. {
  482.     int pagelen = wnd->wlines - ClientHeight(wnd);
  483.     int barlen = ClientHeight(wnd)-2;
  484.     int lines_tick;
  485.     int vscrollbox;
  486.  
  487.     if (pagelen < 1 || barlen < 1)
  488.         vscrollbox = 1;
  489.     else    {
  490.         if (pagelen > barlen)
  491.             lines_tick = pagelen / barlen;
  492.         else
  493.             lines_tick = barlen / pagelen;
  494.         vscrollbox = 1 + (wnd->wtop / lines_tick);
  495.         if (vscrollbox > ClientHeight(wnd)-2 ||
  496.                 wnd->wtop + ClientHeight(wnd) >= wnd->wlines)
  497.             vscrollbox = ClientHeight(wnd)-2;
  498.     }
  499.     return vscrollbox;
  500. }
  501.  
  502. /* ---- compute top text line from scroll box position ---- */
  503. static void ComputeWindowTop(WINDOW wnd)
  504. {
  505.     int pagelen = wnd->wlines - ClientHeight(wnd);
  506.     if (wnd->VScrollBox == 0)
  507.         wnd->wtop = 0;
  508.     else if (wnd->VScrollBox == ClientHeight(wnd)-2)
  509.         wnd->wtop = pagelen;
  510.     else    {
  511.         int barlen = ClientHeight(wnd)-2;
  512.         int lines_tick;
  513.  
  514.         if (pagelen > barlen)
  515.             lines_tick = pagelen / barlen;
  516.         else
  517.             lines_tick = barlen / pagelen;
  518.         wnd->wtop = (wnd->VScrollBox-1) * lines_tick;
  519.         if (wnd->wtop + ClientHeight(wnd) > wnd->wlines)
  520.             wnd->wtop = pagelen;
  521.     }
  522.     if (wnd->wtop < 0)
  523.         wnd->wtop = 0;
  524. }
  525.  
  526. /* ------ compute the horizontal scroll box position from
  527.                    the text pointers --------- */
  528. static int ComputeHScrollBox(WINDOW wnd)
  529. {
  530.     int pagewidth = wnd->textwidth - ClientWidth(wnd);
  531.     int barlen = ClientWidth(wnd)-2;
  532.     int chars_tick;
  533.     int hscrollbox;
  534.  
  535.     if (pagewidth < 1 || barlen < 1)
  536.         hscrollbox = 1;
  537.     else     {
  538.         if (pagewidth > barlen)
  539.             chars_tick = pagewidth / barlen;
  540.         else
  541.             chars_tick = barlen / pagewidth;
  542.         hscrollbox = 1 + (wnd->wleft / chars_tick);
  543.         if (hscrollbox > ClientWidth(wnd)-2 ||
  544.                 wnd->wleft + ClientWidth(wnd) >= wnd->textwidth)
  545.             hscrollbox = ClientWidth(wnd)-2;
  546.     }
  547.     return hscrollbox;
  548. }
  549.  
  550. /* ---- compute left column from scroll box position ---- */
  551. static void ComputeWindowLeft(WINDOW wnd)
  552. {
  553.     int pagewidth = wnd->textwidth - ClientWidth(wnd);
  554.  
  555.     if (wnd->HScrollBox == 0)
  556.         wnd->wleft = 0;
  557.     else if (wnd->HScrollBox == ClientWidth(wnd)-2)
  558.         wnd->wleft = pagewidth;
  559.     else    {
  560.         int barlen = ClientWidth(wnd)-2;
  561.         int chars_tick;
  562.  
  563.         if (pagewidth > barlen)
  564.             chars_tick = pagewidth / barlen;
  565.         else
  566.             chars_tick = barlen / pagewidth;
  567.         wnd->wleft = (wnd->HScrollBox-1) * chars_tick;
  568.         if (wnd->wleft + ClientWidth(wnd) > wnd->textwidth)
  569.             wnd->wleft = pagewidth;
  570.     }
  571.     if (wnd->wleft < 0)
  572.         wnd->wleft = 0;
  573. }
  574.  
  575. /* ----- get the text to a specified line ----- */
  576. static char *GetTextLine(WINDOW wnd, int selection)
  577. {
  578.     char *line;
  579.     int len = 0;
  580.     char *cp, *cp1;
  581.     cp = cp1 = TextLine(wnd, selection);
  582.     while (*cp && *cp != '\n')    {
  583.         len++;
  584.         cp++;
  585.     }
  586.     line = malloc(len+6);
  587.     if (line != NULL)    {
  588.         memmove(line, cp1, len);
  589.         line[len] = '\0';
  590.     }
  591.     return line;
  592. }
  593.  
  594. /* ------- write a line of text to a textbox window ------- */
  595. void WriteTextLine(WINDOW wnd, RECT *rcc, int y, int reverse)
  596. {
  597.     int len = 0;
  598.     int dif = 0;
  599.     unsigned char *line;
  600.     RECT rc;
  601.     unsigned char *lp, *svlp;
  602.     int lnlen;
  603.     int i;
  604.     int trunc = FALSE;
  605.  
  606.     /* ------ make sure y is inside the window ----- */
  607.     if (y < wnd->wtop || y >= wnd->wtop+ClientHeight(wnd))
  608.         return;
  609.  
  610.     /* ---- build the retangle within which can write ---- */
  611.     if (rcc == NULL)    {
  612.         rc = RelativeWindowRect(wnd, WindowRect(wnd));
  613.         if (TestAttribute(wnd, HASBORDER) &&
  614.                 RectRight(rc) >= WindowWidth(wnd)-1)
  615.             RectRight(rc) = WindowWidth(wnd)-2;
  616.     }
  617.     else
  618.         rc = *rcc;
  619.  
  620.     /* ----- make sure rectangle is within window ------ */
  621.     if (RectLeft(rc) >= WindowWidth(wnd)-1)
  622.         return;
  623.     if (RectRight(rc) == 0)
  624.         return;
  625.     rc = AdjustRectangle(wnd, rc);
  626.     if (y-wnd->wtop<RectTop(rc) || y-wnd->wtop>RectBottom(rc))
  627.         return;
  628.  
  629.     /* --- get the text and length of the text line --- */
  630.     lp = svlp = GetTextLine(wnd, y);
  631.     if (svlp == NULL)
  632.         return;
  633.     lnlen = LineLength(lp);
  634.  
  635.     /* -------- insert block color change controls ------- */
  636.     if (TextBlockMarked(wnd))    {
  637.         int bbl = wnd->BlkBegLine;
  638.         int bel = wnd->BlkEndLine;
  639.         int bbc = wnd->BlkBegCol;
  640.         int bec = wnd->BlkEndCol;
  641.         int by = y;
  642.  
  643.         /* ----- put lowest marker first ----- */
  644.         if (bbl > bel)    {
  645.             swap(bbl, bel);
  646.             swap(bbc, bec);
  647.         }
  648.         if (bbl == bel && bbc > bec)
  649.             swap(bbc, bec);
  650.  
  651.         if (by >= bbl && by <= bel)    {
  652.             /* ------ the block includes this line ----- */
  653.             int blkbeg = 0;
  654.             int blkend = lnlen;
  655.             if (!(by > bbl && by < bel))    {
  656.                 /* --- the entire line is not in the block -- */
  657.                 if (by == bbl)
  658.                     /* ---- the block begins on this line --- */
  659.                     blkbeg = bbc;
  660.                 if (by == bel)
  661.                     /* ---- the block ends on this line ---- */
  662.                     blkend = bec;
  663.             }
  664.             /* ----- insert the reset color token ----- */
  665.             memmove(lp+blkend+1,lp+blkend,strlen(lp+blkend)+1);
  666.             lp[blkend] = RESETCOLOR;
  667.             /* ----- insert the change color token ----- */
  668.             memmove(lp+blkbeg+3,lp+blkbeg,strlen(lp+blkbeg)+1);
  669.             lp[blkbeg] = CHANGECOLOR;
  670.             /* ----- insert the color tokens ----- */
  671.             SetReverseColor(wnd);
  672.             lp[blkbeg+1] = foreground | 0x80;
  673.             lp[blkbeg+2] = background | 0x80;
  674.             lnlen += 4;
  675.         }
  676.     }
  677.     /* - make sure left margin doesn't overlap color change - */
  678.     for (i = 0; i < wnd->wleft+3; i++)    {
  679.         if (*(lp+i) == '\0')
  680.             break;
  681.         if (*(unsigned char *)(lp + i) == RESETCOLOR)
  682.             break;
  683.     }
  684.     if (*(lp+i) && i < wnd->wleft+3)    {
  685.         if (wnd->wleft+4 > lnlen)
  686.             trunc = TRUE;
  687.         else 
  688.             lp += 4;
  689.     }
  690.     else     {
  691.         /* --- it does, shift the color change over --- */
  692.         for (i = 0; i < wnd->wleft; i++)    {
  693.             if (*(lp+i) == '\0')
  694.                 break;
  695.             if (*(unsigned char *)(lp + i) == CHANGECOLOR)    {
  696.                 *(lp+wnd->wleft+2) = *(lp+i+2);
  697.                 *(lp+wnd->wleft+1) = *(lp+i+1);
  698.                 *(lp+wnd->wleft) = *(lp+i);
  699.                 break;
  700.             }
  701.         }
  702.     }
  703.     /* ------ build the line to display -------- */
  704.     if ((line = malloc(200)) != NULL)    {
  705.         if (!trunc)    {
  706.             if (lnlen < wnd->wleft)
  707.                 lnlen = 0;
  708.             else
  709.                 lp += wnd->wleft;
  710.             if (lnlen > RectLeft(rc))    {
  711.                 /* ---- the line exceeds the rectangle ---- */
  712.                 int ct = RectLeft(rc);
  713.                 char *initlp = lp;
  714.                 /* --- point to end of clipped line --- */
  715.                 while (ct)    {
  716.                     if (*(unsigned char *)lp == CHANGECOLOR)
  717.                         lp += 3;
  718.                     else if (*(unsigned char *)lp == RESETCOLOR)
  719.                         lp++;
  720.                     else
  721.                         lp++, --ct;
  722.                 }
  723.                 if (RectLeft(rc))    {
  724.                     char *lpp = lp;
  725.                     while (*lpp)    {
  726.                         if (*(unsigned char*)lpp==CHANGECOLOR)
  727.                             break;
  728.                         if (*(unsigned char*)lpp==RESETCOLOR) {
  729.                             lpp = lp;
  730.                             while (lpp >= initlp)    {
  731.                                 if (*(unsigned char *)lpp ==
  732.                                                 CHANGECOLOR) {
  733.                                     lp -= 3;
  734.                                     memmove(lp,lpp,3);
  735.                                     break;
  736.                                 }
  737.                                 --lpp;
  738.                             }
  739.                             break;
  740.                         }
  741.                         lpp++;
  742.                     }
  743.                 }
  744.                 lnlen = LineLength(lp);
  745.                 len = min(lnlen, RectWidth(rc));
  746.                 dif = strlen(lp) - lnlen;
  747.                 len += dif;
  748.                 if (len > 0)
  749.                     strncpy(line, lp, len);
  750.             }
  751.         }
  752.         /* -------- pad the line --------- */
  753.         while (len < RectWidth(rc)+dif)
  754.             line[len++] = ' ';
  755.         line[len] = '\0';
  756.         dif = 0;
  757.         /* ------ establish the line's main color ----- */
  758.         if (reverse)    {
  759.             char *cp = line;
  760.             SetReverseColor(wnd);
  761.             while ((cp = strchr(cp, CHANGECOLOR)) != NULL)    {
  762.                 cp += 2;
  763.                 *cp++ = background | 0x80;
  764.             }
  765.             if (*(unsigned char *)line == CHANGECOLOR)
  766.                 dif = 3;
  767.         }
  768.         else
  769.             SetStandardColor(wnd);
  770.         /* ------- display the line -------- */
  771.         writeline(wnd, line+dif,
  772.                     RectLeft(rc)+BorderAdj(wnd),
  773.                         y-wnd->wtop+TopBorderAdj(wnd), FALSE);
  774.         free(line);
  775.     }
  776.     free(svlp);
  777. }
  778.  
  779. /* ----- set anchor point for marking text block ----- */
  780. void SetAnchor(WINDOW wnd, int mx, int my)
  781. {
  782.     if (TextBlockMarked(wnd))    {
  783.         ClearTextBlock(wnd);
  784.         SendMessage(wnd, PAINT, 0, 0);
  785.     }
  786.     /* ------ set the anchor ------ */
  787.     wnd->BlkBegLine = wnd->BlkEndLine = my;
  788.     wnd->BlkBegCol = wnd->BlkEndCol = mx;
  789. }
  790.  
  791. void MarkTextBlock(WINDOW wnd, int BegLine, int BegCol,
  792.                                int EndLine, int EndCol)
  793. {
  794.     wnd->BlkBegLine = BegLine;
  795.     wnd->BlkEndLine = EndLine;
  796.     wnd->BlkBegCol = BegCol;
  797.     wnd->BlkEndCol = EndCol;
  798. }
  799.  
  800. /* ----- clear and initialize text line pointer array ----- */
  801. void ClearTextPointers(WINDOW wnd)
  802. {
  803.     wnd->TextPointers = realloc(wnd->TextPointers, sizeof(int));
  804.     if (wnd->TextPointers != NULL)
  805.         *(wnd->TextPointers) = 0;
  806. }
  807.  
  808. #define INITLINES 100
  809.  
  810. /* ---- build array of pointers to text lines ---- */
  811. void BuildTextPointers(WINDOW wnd)
  812. {
  813.     char *cp = wnd->text, *cp1;
  814.     int incrs = INITLINES;
  815.     unsigned int off;
  816.     wnd->textwidth = wnd->wlines = 0;
  817.     while (*cp)    {
  818.         if (incrs == INITLINES)    {
  819.             incrs = 0;
  820.             wnd->TextPointers = realloc(wnd->TextPointers,
  821.                     (wnd->wlines + INITLINES) * sizeof(int));
  822.             if (wnd->TextPointers == NULL)
  823.                 break;
  824.         }
  825.         off = (unsigned int) (cp - wnd->text);
  826.         *((wnd->TextPointers) + wnd->wlines) = off;
  827.         wnd->wlines++;
  828.         incrs++;
  829.         cp1 = cp;
  830.         while (*cp && *cp != '\n')
  831.             cp++;
  832.         wnd->textwidth = max(wnd->textwidth,
  833.                         (unsigned int) (cp - cp1));
  834.         if (*cp)
  835.             cp++;
  836.     }
  837. }
  838.  
  839. static void MoveScrollBox(WINDOW wnd, int vscrollbox)
  840. {
  841.     foreground = FrameForeground(wnd);
  842.     background = FrameBackground(wnd);
  843.     wputch(wnd, SCROLLBARCHAR, WindowWidth(wnd)-1,
  844.             wnd->VScrollBox+1);
  845.     wputch(wnd, SCROLLBOXCHAR, WindowWidth(wnd)-1,
  846.             vscrollbox+1);
  847.     wnd->VScrollBox = vscrollbox;
  848. }
  849.  
  850. int TextLineNumber(WINDOW wnd, char *lp)
  851. {
  852.     int lineno;
  853.     char *cp;
  854.     for (lineno = 0; lineno < wnd->wlines; lineno++)    {
  855.         cp = wnd->text + *((wnd->TextPointers) + lineno);
  856.         if (cp == lp)
  857.             return lineno;
  858.         if (cp > lp)
  859.             break;
  860.     }
  861.     return lineno-1;
  862. }
  863.  
  864.  
  865.