home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1991 / 06 / dflat3 / editbox.c < prev    next >
Text File  |  1991-05-19  |  27KB  |  1,087 lines

  1. /* ------------- editbox.c ------------ */
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <stdlib.h>
  7. #include "dflat.h"
  8.  
  9. #define EDITBUFFERLENGTH  4096
  10. #define ENTRYBUFFERLENGTH 1024
  11. #define GROWLENGTH        1024
  12.  
  13.  
  14. #define EditBufLen(wnd) (isMultiLine(wnd) ? EDITBUFFERLENGTH : ENTRYBUFFERLENGTH)
  15. #define WndCol (wnd->CurrCol-wnd->wleft)
  16.  
  17. #define CurrChar (TextLine(wnd, wnd->CurrLine)+wnd->CurrCol)
  18.  
  19. static void PasteText(WINDOW, char *, int);
  20. static void SaveDeletedText(WINDOW, char *, int);
  21. static void Forward(WINDOW);
  22. static void Backward(WINDOW);
  23. static void End(WINDOW);
  24. static void Home(WINDOW);
  25. static void Downward(WINDOW);
  26. static void Upward(WINDOW);
  27. static void StickEnd(WINDOW);
  28. static void UpLine(WINDOW);
  29. static void DownLine(WINDOW);
  30. static void NextWord(WINDOW);
  31. static void PrevWord(WINDOW);
  32. static void ResetEditBox(WINDOW);
  33. static void AddTextPointers(WINDOW, int, int);
  34. static int TextLineNumber(WINDOW, char *);
  35.  
  36. #ifdef INCLUDE_MULTILINE
  37. #define SetLinePointer(wnd, ln)    (wnd->CurrLine = ln)
  38. #else
  39. #define SetLinePointer(wnd, ln) (wnd->CurrLine = 0)
  40. #endif
  41.  
  42. static int KeyBoardMarking, ButtonDown;
  43. int TextMarking;
  44. static int bx, by;
  45.  
  46. char *Clipboard;
  47. int ClipboardLength;
  48.  
  49. int EditBoxProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
  50. {
  51.     int rtn;
  52.     static int py = -1;
  53.     int kx = (int) p1 - GetLeft(wnd);
  54.     int ky = (int) p2 - GetTop(wnd);
  55.     int c;
  56.     RECT rc;
  57.     char *lp;
  58.     int len;
  59.     int    mx;
  60.     int    my;
  61.     char *currchar = CurrChar;
  62.  
  63.     rc = ClientRect(wnd);
  64.     switch (msg)    {
  65.         case CREATE_WINDOW:
  66.             rtn = BaseWndProc(EDITBOX, wnd, msg, p1, p2);
  67.             wnd->text = calloc(1, EditBufLen(wnd)+1);
  68.             wnd->textlen = EditBufLen(wnd);
  69.             ResetEditBox(wnd);
  70.             return rtn;
  71.         case ADDTEXT:
  72.             rtn = BaseWndProc(EDITBOX, wnd, msg, p1, p2);
  73.             currchar = CurrChar;
  74.             if (!isMultiLine(wnd))    {
  75.                 wnd->CurrLine = 0;
  76.                 wnd->CurrCol = strlen((char *)p1);
  77.                 if (wnd->CurrCol >= ClientWidth(wnd))    {
  78.                     wnd->wleft = wnd->CurrCol - ClientWidth(wnd);
  79.                     wnd->CurrCol -= wnd->wleft;
  80.                 }
  81.                 wnd->BlkEndCol = wnd->CurrCol;
  82.                 PostMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  83.             }
  84.             return rtn;
  85.         case SETTEXT:
  86.             rtn = BaseWndProc(EDITBOX, wnd, msg, p1, p2);
  87.             wnd->CurrLine = 0;
  88.             return rtn;
  89.         case CLEARTEXT:
  90.             ResetEditBox(wnd);
  91.             ClearTextPointers(wnd);
  92.             break;
  93.         case EB_GETTEXT:    {
  94.             char *cp1 = (char *)p1;
  95.             char *cp2 = wnd->text;
  96.             while (p2-- && *cp2 && *cp2 != '\n')
  97.                 *cp1++ = *cp2++;
  98.             *cp1 = '\0';
  99.             return TRUE;
  100.         }
  101.         case EB_PUTTEXT:
  102.             SendMessage(wnd, CLEARTEXT, 0, 0);
  103.             SendMessage(wnd, ADDTEXT, p1, p2);
  104.             BuildTextPointers(wnd);
  105.             return TRUE;
  106.         case SETFOCUS:
  107.             rtn = BaseWndProc(EDITBOX, wnd, msg, p1, p2);
  108.             if (p1)    {
  109.                 SendMessage(NULLWND, SHOW_CURSOR, GetCommandToggle(MainMenu, ID_INSERT), 0);
  110.                 SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  111.             }
  112.             else
  113.                 SendMessage(NULLWND, HIDE_CURSOR, 0, 0);
  114.             return rtn;
  115. #ifdef INCLUDE_MULTILINE
  116.         case SHIFT_CHANGED:
  117.             if (!(p1 & (LEFTSHIFT | RIGHTSHIFT)) && KeyBoardMarking)    {
  118.                 SendMessage(wnd, BUTTON_RELEASED, 0, 0);
  119.                 KeyBoardMarking = FALSE;
  120.             }
  121.             break;
  122.         case DOUBLE_CLICK:
  123.             if (KeyBoardMarking)
  124.                 return TRUE;
  125.             break;
  126. #endif
  127.         case LEFT_BUTTON:
  128. #ifdef INCLUDE_MULTILINE
  129.             if (KeyBoardMarking || TextMarking)
  130.                 return TRUE;
  131. #endif
  132. #ifdef INCLUDE_SYSTEM_MENUS
  133.             if (WindowMoving || WindowSizing)
  134.                 break;
  135. #endif
  136.             if (!InsideRect(p1, p2, rc))
  137.                 break;
  138.  
  139.             mx = (int) p1 - GetClientLeft(wnd);
  140.             my = (int) p2 - GetClientTop(wnd);
  141.  
  142. #ifdef INCLUDE_MULTILINE
  143.             if (isMultiLine(wnd))    {
  144.  
  145.                 if (BlockMarked(wnd))    {
  146.                     ClearBlock(wnd);
  147.                     SendMessage(wnd, PAINT, 0, 0);
  148.                 }
  149.  
  150.                 if (wnd->wlines)    {
  151.                     if (my > wnd->wlines-1)
  152.                         break;
  153.                     lp = TextLine(wnd, my+wnd->wtop);
  154.                     len = (int) (strchr(lp, '\n') - lp);
  155.                     mx = min(mx, len);
  156.                     if (mx < wnd->wleft)    {
  157.                         mx = 0;
  158.                         SendMessage(wnd, KEYBOARD, HOME, 0);
  159.                     }
  160.                     ButtonDown = TRUE;
  161.                     bx = mx;
  162.                     by = my;
  163.                 }
  164.                 else
  165.                     mx = my = 0;
  166.  
  167.                 wnd->WndRow = my;
  168.                 SetLinePointer(wnd, my+wnd->wtop);
  169.             }
  170. #endif
  171.             if (isMultiLine(wnd) || !BlockMarked(wnd))
  172.                 wnd->CurrCol = mx+wnd->wleft;
  173.             PostMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  174.             return TRUE;
  175. #ifdef INCLUDE_MULTILINE
  176.         case MOUSE_MOVED:
  177.             mx = (int) p1 - GetClientLeft(wnd);
  178.             my = (int) p2 - GetClientTop(wnd);
  179.             if (my > wnd->wlines-1)
  180.                 break;
  181.  
  182.             if (ButtonDown)    {
  183.                 SetAnchor(wnd, bx+wnd->wleft, by+wnd->wtop);
  184.                 TextMarking = TRUE;
  185.                 SendMessage(wnd, CAPTURE_MOUSE, TRUE, 0);
  186.                 ButtonDown = FALSE;
  187.             }
  188.  
  189.             if (TextMarking
  190. #ifdef INCLUDE_SYSTEM_MENUS
  191.                     && !(WindowMoving || WindowSizing)
  192. #endif
  193.                             )    {
  194.                 int ptop;
  195.                 int pbot;
  196.                 char *lp;
  197.                 int len;
  198.                 int y;
  199.                 int bbl, bel;
  200.                 RECT rc;
  201.                 rc = ClientRect(wnd);
  202.  
  203.                 if (!InsideRect(p1, p2, rc))      {
  204.                     int hs = -1, sc = -1;
  205.                     if (p1 < GetClientLeft(wnd))    {
  206.                         hs = FALSE;
  207.                         p1 = GetClientLeft(wnd);
  208.                     }
  209.                     if (p1 > GetClientRight(wnd))    {
  210.                         hs = TRUE;
  211.                         p1 = GetClientRight(wnd);
  212.                     }
  213.                     if (p2 < GetClientTop(wnd))    {
  214.                         sc = FALSE;
  215.                         p2 = GetClientTop(wnd);
  216.                     }
  217.                     if (p2 > GetClientBottom(wnd))    {
  218.                         sc = TRUE;
  219.                         p2 = GetClientBottom(wnd);
  220.                     }
  221.                     SendMessage(NULLWND, MOUSE_CURSOR, p1, p2);
  222.                     if (sc != -1)
  223.                         SendMessage(wnd, SCROLL, sc, 0);
  224.                     if (hs != -1)
  225.                         SendMessage(wnd, HORIZSCROLL, hs, 0);
  226.                     mx = (int) p1 - GetClientLeft(wnd);
  227.                     my = (int) p2 - GetClientTop(wnd);
  228.                 }
  229.  
  230.                 ptop = min(wnd->BlkBegLine, wnd->BlkEndLine);
  231.                 pbot = max(wnd->BlkBegLine, wnd->BlkEndLine);
  232.  
  233.                 lp = TextLine(wnd, wnd->wtop+my);
  234.                 len = (int) (strchr(lp, '\n') - lp);
  235.                 mx = min(mx, len-wnd->wleft);
  236.  
  237.                 wnd->BlkEndCol = mx+wnd->wleft;
  238.                 wnd->BlkEndLine = my+wnd->wtop;
  239.  
  240.                 bbl = min(wnd->BlkBegLine, wnd->BlkEndLine);
  241.                 bel = max(wnd->BlkBegLine, wnd->BlkEndLine);
  242.  
  243.                 while (ptop < bbl)    {
  244.                     WriteTextLine(wnd, NULL, ptop, FALSE);
  245.                     ptop++;
  246.                 }
  247.                 for (y = bbl; y <= bel; y++)
  248.                     WriteTextLine(wnd, NULL, y, FALSE);
  249.                 while (pbot > bel)    {
  250.                     WriteTextLine(wnd, NULL, pbot, FALSE);
  251.                     --pbot;
  252.                 }
  253.                 return TRUE;
  254.             }
  255.             break;
  256.         case BUTTON_RELEASED:
  257.             if (!isMultiLine(wnd))
  258.                 break;
  259.             ButtonDown = FALSE;
  260.             if (TextMarking
  261. #ifdef INCLUDE_SYSTEM_MENUS
  262.                     && !(WindowMoving || WindowSizing)
  263. #endif
  264.                             )    {
  265.                 PostMessage(wnd, RELEASE_MOUSE, 0, 0);
  266.                 TextMarking = FALSE;
  267.                 if (wnd->BlkBegLine > wnd->BlkEndLine)    {
  268.                     swap(wnd->BlkBegLine, wnd->BlkEndLine);
  269.                     swap(wnd->BlkBegCol, wnd->BlkEndCol);
  270.                 }
  271.                 if (wnd->BlkBegLine == wnd->BlkEndLine &&
  272.                         wnd->BlkBegCol > wnd->BlkEndCol)
  273.                     swap(wnd->BlkBegCol, wnd->BlkEndCol);
  274.                 return TRUE;
  275.             }
  276.             else
  277.                 py = -1;
  278.             break;
  279.         case SCROLL:
  280.             if (!isMultiLine(wnd))
  281.                 break;
  282.             if ((rtn = BaseWndProc(EDITBOX, wnd, msg, p1, p2)) != FALSE)    {
  283.                 if (p1)    {
  284.                     /* -------- scrolling up --------- */
  285.                     if (wnd->WndRow == 0)    {
  286.                         DownLine(wnd);
  287.                         StickEnd(wnd);
  288.                     }
  289.                     else
  290.                         --wnd->WndRow;
  291.                 }
  292.                 else    {
  293.                     /* -------- scrolling down --------- */
  294.                     if (wnd->WndRow == ClientHeight(wnd)-1)    {
  295.                         UpLine(wnd);
  296.                         StickEnd(wnd);
  297.                     }
  298.                     else
  299.                         wnd->WndRow++;
  300.                 }
  301.                 SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  302.             }
  303.             return rtn;
  304. #endif
  305.         case HORIZSCROLL:
  306.             if (p1 && wnd->CurrCol == wnd->wleft &&
  307.                     *currchar == '\n')
  308.                 return FALSE;
  309.             if ((rtn = BaseWndProc(EDITBOX, wnd, msg, p1, p2)) != FALSE)    {
  310.                 if (wnd->CurrCol < wnd->wleft)
  311.                     wnd->CurrCol++;
  312.                 else if (WndCol == ClientWidth(wnd))
  313.                     --wnd->CurrCol;
  314.                 SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  315.             }
  316.             return rtn;
  317. #ifdef INCLUDE_SYSTEM_MENUS
  318.         case MOVE:
  319.             rtn = BaseWndProc(EDITBOX, wnd, msg, p1, p2);
  320.             SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  321.             return rtn;
  322.         case SIZE:
  323.             rtn = BaseWndProc(EDITBOX, wnd, msg, p1, p2);
  324.             if (WndCol > ClientWidth(wnd)-1)
  325.                 wnd->CurrCol = ClientWidth(wnd)-1 + wnd->wleft;
  326.             if (wnd->WndRow > ClientHeight(wnd)-1)    {
  327.                 wnd->WndRow = ClientHeight(wnd)-1;
  328.                 SetLinePointer(wnd, wnd->WndRow+wnd->wtop);
  329.             }
  330.             SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  331.             return rtn;
  332. #endif
  333.         case KEYBOARD:
  334. #ifdef INCLUDE_SYSTEM_MENUS
  335.             if (WindowMoving || WindowSizing)
  336.                 break;
  337. #endif
  338.             c = (int) p1;
  339.             if (c == CTRL_FIVE)
  340.                 break;
  341.             if ((p2 & ALTKEY) || 
  342.                     c == SHIFT_INS || c == SHIFT_DEL)
  343.                 break;
  344.             if ((p2 & CTRLKEY) && c != CTRL_FWD  &&
  345.                                   c != CTRL_BS   &&
  346.                                   c != CTRL_HOME &&
  347.                                   c != CTRL_END)
  348.                 break;
  349.             if (c == ESC)
  350.                 break;
  351. #ifdef INCLUDE_MULTILINE
  352.             if (isMultiLine(wnd))    {
  353.                 if (p2 & (LEFTSHIFT | RIGHTSHIFT))    {
  354.                     SendMessage(NULLWND, CURRENT_KEYBOARD_CURSOR,
  355.                         LPARAM(&kx), LPARAM(&ky));
  356.  
  357.                     kx -= GetClientLeft(wnd);
  358.                     ky -= GetClientTop(wnd);
  359.  
  360.                     switch (c)    {
  361.                         case HOME:
  362.                         case END:
  363.                         case PGUP:
  364.                         case PGDN:
  365.                         case UP:
  366.                         case DN:
  367.                         case FWD:
  368.                         case BS:
  369.                         case CTRL_FWD:
  370.                         case CTRL_BS:
  371.                             if (!KeyBoardMarking)    {
  372.                                 if (BlockMarked(wnd))    {
  373.                                     ClearBlock(wnd);
  374.                                     SendMessage(wnd, PAINT, 0, 0);
  375.                                 }
  376.                                 KeyBoardMarking = TextMarking = TRUE;
  377.                                 SetAnchor(wnd, kx+wnd->wleft, ky+wnd->wtop);
  378.                             }
  379.                             break;
  380.                         default:
  381.                             break;
  382.                     }
  383.                 }
  384.                 else if (((c != DEL && c != RUBOUT) ||
  385.                         !isMultiLine(wnd)) && BlockMarked(wnd))    {
  386.                     ClearBlock(wnd);
  387.                     SendMessage(wnd, PAINT, 0, 0);
  388.                 }
  389.             }
  390. #endif
  391.             switch (c)    {
  392. #ifdef INCLUDE_MULTILINE
  393.                 case PGUP:
  394.                 case PGDN:
  395.                     if (!isMultiLine(wnd))
  396.                         break;
  397.                     BaseWndProc(EDITBOX, wnd, msg, p1, p2);
  398.                     SetLinePointer(wnd, wnd->wtop+wnd->WndRow);
  399.                     StickEnd(wnd);
  400.                     SendMessage(wnd, KEYBOARD_CURSOR,WndCol, wnd->WndRow);
  401.                     break;
  402. #endif
  403.                 case HOME:
  404.                     Home(wnd);
  405.                     break;
  406.                 case END:
  407.                     End(wnd);
  408.                     break;
  409.                 case CTRL_FWD:
  410.                     NextWord(wnd);
  411.                     break;
  412.                 case CTRL_BS:
  413.                     PrevWord(wnd);
  414.                     break;
  415.                 case CTRL_HOME:
  416.                     if (!isMultiLine(wnd))    {
  417.                         Home(wnd);
  418.                         break;
  419.                     }
  420. #ifdef INCLUDE_MULTILINE
  421.                     rtn = BaseWndProc(EDITBOX, wnd, msg, HOME, p2);
  422.                     Home(wnd);
  423.                     wnd->CurrLine = 0;
  424.                     wnd->WndRow = 0;
  425.                     SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  426.                     return rtn;
  427. #endif
  428.                 case CTRL_END:
  429.                     if (!isMultiLine(wnd))    {
  430.                         End(wnd);
  431.                         break;
  432.                     }
  433. #ifdef INCLUDE_MULTILINE
  434.                     Home(wnd);
  435.                     rtn = BaseWndProc(EDITBOX, wnd, msg, END, p2);
  436.                     SetLinePointer(wnd, wnd->wlines-1);
  437.                     wnd->WndRow = min(ClientHeight(wnd)-1, wnd->wlines-1);
  438.                     End(wnd);
  439.                     SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  440.                     return rtn;
  441. #endif
  442. #ifdef INCLUDE_MULTILINE
  443.                 case UP:
  444.                     if (!isMultiLine(wnd))
  445.                         break;
  446.                     Upward(wnd);
  447.                     break;
  448.                 case DN:
  449.                     if (!isMultiLine(wnd))
  450.                         break;
  451.                     Downward(wnd);
  452.                     break;
  453. #endif
  454.                 case FWD:
  455.                     Forward(wnd);
  456.                     break;
  457.                 case BS:
  458.                     Backward(wnd);
  459.                     break;
  460.             }
  461. #ifdef INCLUDE_MULTILINE
  462.             if (KeyBoardMarking)    {
  463.                 SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  464.                 SendMessage(NULLWND, CURRENT_KEYBOARD_CURSOR,
  465.                     LPARAM(&kx), LPARAM(&ky));
  466.                 SendMessage(wnd, MOUSE_MOVED, kx, ky);
  467.                 return TRUE;
  468.             }
  469. #endif
  470.             if (!TestAttribute(wnd, READONLY))    {
  471.                 switch (c)    {
  472.                     case HOME:
  473.                     case END:
  474.                     case PGUP:
  475.                     case PGDN:
  476.                     case UP:
  477.                     case DN:
  478.                     case FWD:
  479.                     case BS:
  480.                     case CTRL_FWD:
  481.                     case CTRL_BS:
  482.                     case CTRL_HOME:
  483.                     case CTRL_END:
  484.                     case RUBOUT:
  485.                         if (!isMultiLine(wnd) && BlockMarked(wnd))    {
  486.                             ClearBlock(wnd);
  487.                             SendMessage(wnd, PAINT, 0, 0);
  488.                         }
  489.                         if (c != RUBOUT)
  490.                             break;
  491.                         if (wnd->CurrLine == 0 && wnd->CurrCol == 0)
  492.                             break;
  493.                         Backward(wnd);
  494.                         currchar = CurrChar;
  495.                         SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  496.                     case DEL:
  497.                         if (BlockMarked(wnd))    {
  498.                             SendMessage(wnd, COMMAND, ID_DELETETEXT, 0);
  499.                             return TRUE;
  500.                         }
  501.                         if (*(currchar+1) == '\0')
  502.                             return TRUE;
  503. #ifdef INCLUDE_MULTILINE
  504.                         if (isMultiLine(wnd) &&
  505.                                 *currchar == '\n')
  506.                             --wnd->wlines;
  507. #endif
  508.                         if (*(currchar+1))    {
  509.                             int repaint = *currchar == '\n';
  510.                             strcpy(currchar, currchar+1);
  511.                             AddTextPointers(wnd, wnd->CurrLine+1, -1);
  512.                             if (repaint)
  513.                                 SendMessage(wnd, PAINT, 0, 0);
  514.                             else
  515.                                 WriteTextLine(wnd, NULL, wnd->WndRow+wnd->wtop, FALSE);
  516.                         }
  517.                         wnd->TextChanged = TRUE;
  518.                         break;
  519.                     case INS:
  520.                         InvertCommandToggle(MainMenu, ID_INSERT);
  521.                         SendMessage(NULLWND, SHOW_CURSOR, GetCommandToggle(MainMenu, ID_INSERT), 0);
  522.                         break;
  523.                     case '\r':
  524. #ifdef INCLUDE_MULTILINE
  525.                         if (isMultiLine(wnd))
  526.                             c = '\n';
  527. #endif
  528.                     default:
  529.                         if (c == '\t')    {
  530.                             int insmd = GetCommandToggle(MainMenu, ID_INSERT);
  531.                             if (!isMultiLine(wnd))
  532.                                 PostMessage(GetParent(wnd), msg, p1, p2);
  533. #ifdef INCLUDE_MULTILINE
  534.                             else do    {
  535.                                 if (!insmd && *(CurrChar+1) == '\0')
  536.                                     break;
  537.                                 SendMessage(wnd, KEYBOARD,
  538.                                     insmd ? ' ' : FWD, 0);
  539.                             } while (wnd->CurrCol % cfg.Tabs);
  540. #endif
  541.                             return TRUE;
  542.                         }
  543.                         if ((c != '\n' && c < ' ') || (c & 0x1000))
  544.                             /* ---- not recognized by editor --- */
  545.                             break;
  546. #ifdef INCLUDE_MULTILINE
  547.                         if (!isMultiLine(wnd) && BlockMarked(wnd))    {
  548.                             ResetEditBox(wnd);
  549.                             ClearBlock(wnd);
  550.                         }
  551. #endif
  552.                         if (*currchar == '\0')    {
  553.                             *currchar = '\n';
  554.                             *(currchar+1) = '\0';
  555.                             wnd->wlines++;
  556.                         }
  557.                         /* --- displayable char or newline --- */
  558.                         if (c == '\n' ||
  559.                                 GetCommandToggle(MainMenu, ID_INSERT) ||
  560.                                     *currchar == '\n')    {
  561.                             if (wnd->text[wnd->textlen-1] != '\0')    {
  562.                                 wnd->textlen += GROWLENGTH;
  563.                                 wnd->text = realloc(wnd->text, wnd->textlen+1);
  564.                                 wnd->text[wnd->textlen-1] = '\0';
  565.                                 currchar = CurrChar;
  566.                             }
  567.                             wnd->textwidth = max(wnd->textwidth,
  568.                                 (int) (TextLine(wnd, wnd->CurrLine+1)-
  569.                                 TextLine(wnd, wnd->CurrLine)));
  570.                             /* ------ insert mode ------ */
  571.                             memmove(currchar+1, currchar, strlen(currchar)+1);
  572.                             AddTextPointers(wnd, wnd->CurrLine+1, 1);
  573.                             WriteTextLine(wnd, NULL,
  574.                                 wnd->WndRow, FALSE);
  575.                         }
  576.                         /* ----- put the char in the buffer ----- */
  577.                         *currchar = c;
  578.                         wnd->TextChanged = TRUE;
  579.                         if (c == '\n')    {
  580.                             wnd->wleft = 0;
  581.                             wnd->wlines++;
  582.                             BuildTextPointers(wnd);
  583.                             End(wnd);
  584.                             Forward(wnd);
  585.                             SendMessage(wnd, PAINT, 0, 0);
  586.                             break;
  587.                         }
  588.                         /* ---------- test end of window --------- */
  589.                         if (WndCol == ClientWidth(wnd)-1)    {
  590.                             int dif;
  591.                             char *cp = currchar;
  592.                             while (*cp != ' ' && cp != TextLine(wnd, wnd->CurrLine))
  593.                                 --cp;
  594. #ifdef INCLUDE_MULTILINE
  595.                             if (!isMultiLine(wnd) || cp == TextLine(wnd, wnd->CurrLine) ||
  596.                                     !GetCommandToggle(MainMenu, ID_WRAP))
  597. #endif
  598.                                 SendMessage(wnd, HORIZSCROLL, TRUE, 0);
  599. #ifdef INCLUDE_MULTILINE
  600.                             else    {
  601.                                 dif = 0;
  602.                                 if (c != ' ')    {
  603.                                     dif = (int) (currchar - cp);
  604.                                     wnd->CurrCol -= dif;
  605.                                     SendMessage(wnd, KEYBOARD, DEL, 0);
  606.                                     --dif;
  607.                                 }
  608.                                 SendMessage(wnd, KEYBOARD, '\r', 0);
  609.                                 currchar = CurrChar;
  610.                                 wnd->CurrCol = dif;
  611.                                 if (c == ' ')
  612.                                     break;
  613.                             }
  614. #endif
  615.                         }
  616.                         /* ------ display the character ------ */
  617.                         SetStandardColor(wnd);
  618.                         PutWindowChar(wnd, WndCol+BorderAdj(wnd),
  619.                             wnd->WndRow+TopBorderAdj(wnd), c);
  620.                         /* ----- advance the pointers ------ */
  621.                         wnd->CurrCol++;
  622.                         break;
  623.                 }
  624.             }
  625.             SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  626.             return TRUE;
  627. #ifdef INCLUDE_MULTILINE
  628.         case COMMAND:    {
  629.             char *bbl, *bel, *bb;
  630.             int len;
  631.  
  632.             if (BlockMarked(wnd))    {
  633.                 bbl = TextLine(wnd, wnd->BlkBegLine) + wnd->BlkBegCol;
  634.                 bel = TextLine(wnd, wnd->BlkEndLine) + wnd->BlkEndCol;
  635.                 len = (int) (bel - bbl);
  636.             }
  637.             switch ((int)p1)    {
  638. #ifdef INCLUDE_CLIPBOARD
  639.                 case ID_CUT:
  640.                 case ID_COPY:
  641.                     ClipboardLength = len;
  642.                     Clipboard = realloc(Clipboard, ClipboardLength);
  643.                     if (Clipboard != NULL)
  644.                         memmove(Clipboard, bbl, ClipboardLength);
  645. #endif
  646.                 case ID_DELETETEXT:
  647.                     if (p1 != ID_COPY)    {
  648.                         if (p1 != ID_CUT)
  649.                             SaveDeletedText(wnd, bbl, len);
  650.                         wnd->TextChanged = TRUE;
  651.                         strcpy(bbl, bel);
  652.                         wnd->CurrLine = TextLineNumber(wnd, bbl - wnd->BlkBegCol);
  653.                         wnd->CurrCol = wnd->BlkBegCol;
  654.                         wnd->WndRow = wnd->BlkBegLine - wnd->wtop;
  655.                         if (wnd->WndRow < 0)    {
  656.                             wnd->wtop = wnd->BlkBegLine;
  657.                             wnd->WndRow = 0;
  658.                         }
  659.                         wnd->wlines -= wnd->BlkEndLine - wnd-> BlkBegLine;
  660.                         SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  661.                     }
  662.                     ClearBlock(wnd);
  663.                     BuildTextPointers(wnd);
  664.                     SendMessage(wnd, PAINT, 0, 0);
  665.                     return TRUE;
  666. #ifdef INCLUDE_CLIPBOARD
  667.                 case ID_CLEAR:
  668.                     SaveDeletedText(wnd, bbl, len);
  669.                     wnd->CurrLine = TextLineNumber(wnd, bbl);
  670.                     wnd->CurrCol = wnd->BlkBegCol;
  671.                     wnd->WndRow = wnd->BlkBegLine - wnd->wtop;
  672.                     if (wnd->WndRow < 0)    {
  673.                         wnd->WndRow = 0;
  674.                         wnd->wtop = wnd->BlkBegLine;
  675.                     }
  676.                     while (bbl < bel)    {
  677.                         char *cp = strchr(bbl, '\n');
  678.                         if (cp > bel)
  679.                             cp = bel;
  680.                         strcpy(bbl, cp);
  681.                         bel -= (int) (cp - bbl);
  682.                         bbl++;
  683.                     }
  684.                     ClearBlock(wnd);
  685.                     BuildTextPointers(wnd);
  686.                     SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  687.                     SendMessage(wnd, PAINT, 0, 0);
  688.                     wnd->TextChanged = TRUE;
  689.                     return TRUE;
  690.                 case ID_PASTE:
  691.                     if (Clipboard != NULL)    {
  692.                         PasteText(wnd, Clipboard, ClipboardLength);
  693.                         wnd->TextChanged = TRUE;
  694.                     }
  695.                     return TRUE;
  696. #endif
  697.                 case ID_UNDO:
  698.                     if (wnd->DeletedText != NULL)    {
  699.                         PasteText(wnd, wnd->DeletedText, wnd->DeletedLength);
  700.                         free(wnd->DeletedText);
  701.                         wnd->DeletedText = NULL;
  702.                     }
  703.                     return TRUE;
  704.                 case ID_PARAGRAPH:    {
  705.                     char *bl;
  706.                     int bc, ec;
  707.                     int fl, el;
  708.                     int Blocked;
  709.  
  710.                     el = wnd->BlkEndLine;
  711.                     ec = wnd->BlkEndCol;
  712.                     if (!BlockMarked(wnd))    {
  713.                         Blocked = FALSE;
  714.                         /* ---- forming paragraph from cursor position --- */
  715.                         fl = wnd->wtop + wnd->WndRow;
  716.                         bl = TextLine(wnd, wnd->CurrLine);
  717.                         bc = wnd->CurrCol;
  718.                         Home(wnd);
  719.                         bbl = bel = bl;
  720.                         if (bc >= ClientWidth(wnd))
  721.                             bc = 0;
  722.                         /* ---- locate the end of the paragraph ---- */
  723.                         while (*bel)    {
  724.                             int blank = TRUE;
  725.                             char *bll = bel;
  726.                             /* --- blank line marks end of paragraph --- */
  727.                             while (*bel && *bel != '\n')    {
  728.                                 if (*bel != ' ')
  729.                                     blank = FALSE;
  730.                                 bel++;
  731.                             }
  732.                             if (blank)    {
  733.                                 bel = bll;
  734.                                 break;
  735.                             }
  736.                             if (*bel)
  737.                                 bel++;
  738.                         }
  739.                         if (bel == bbl)    {
  740.                             SendMessage(wnd, KEYBOARD, DN, 0);
  741.                             return TRUE;
  742.                         }
  743.                         if (*bel == '\0')
  744.                             --bel;
  745.                         if (*bel == '\n')
  746.                             --bel;
  747.                     }
  748.                     else    {
  749.                         Blocked = TRUE;
  750.                         /* ---- forming paragraph from marked block --- */
  751.                         fl = wnd->BlkBegLine;
  752.                         bc = wnd->CurrCol = wnd->BlkBegCol;
  753.                         wnd->CurrLine = fl;
  754.                         if (fl < wnd->wtop)
  755.                             wnd->wtop = fl;
  756.                         wnd->WndRow = fl - wnd->wtop;
  757.                         SendMessage(wnd, KEYBOARD, '\r', 0);
  758.                         el++, fl++;
  759.                         if (bc != 0)    {
  760.                             SendMessage(wnd, KEYBOARD, '\r', 0);
  761.                             el++, fl ++;
  762.                         }
  763.                         bc = 0;
  764.                         bl = TextLine(wnd, fl);
  765.                         wnd->CurrLine = fl;
  766.                         bbl = bl + bc;
  767.                         bel = TextLine(wnd, el) + ec;
  768.                     }
  769.  
  770.                     /* --- change all newlines in block to spaces --- */
  771.                     while (CurrChar < bel)    {
  772.                         if (*CurrChar == '\n')    {
  773.                             *CurrChar = ' ';
  774.                             wnd->CurrLine++;
  775.                             wnd->CurrCol = 0;
  776.                             --wnd->wlines;
  777.                         }
  778.                         else
  779.                             wnd->CurrCol++;
  780.                     }
  781.  
  782.                     /* ---- insert newlines at new margin boundaries ---- */
  783.                     bb = bbl;
  784.                     while (bbl < bel)    {
  785.                         bbl++;
  786.                         if ((int)(bbl - bb) == ClientWidth(wnd)-1)    {
  787.                             while (*bbl != ' ' && bbl > bb)
  788.                                 --bbl;
  789.                             if (*bbl != ' ')    {
  790.                                 bbl = strchr(bbl, ' ');
  791.                                 if (bbl == NULL || bbl >= bel)
  792.                                     break;
  793.                             }
  794.                             *bbl = '\n';
  795.                             wnd->textwidth = max(wnd->textwidth,
  796.                                 (int) (bbl-bb));
  797.                             wnd->wlines++;
  798.                             bb = bbl+1;
  799.                         }
  800.                     }
  801.                     ec = (int)(bel - bb);
  802.                     BuildTextPointers(wnd);
  803.  
  804.                     if (Blocked)    {
  805.                         /* ---- position cursor at end of new paragraph ---- */
  806.                         if (el < wnd->wtop ||
  807.                                 wnd->wtop + ClientHeight(wnd) < el)
  808.                             wnd->wtop = el-ClientHeight(wnd);
  809.                         if (wnd->wtop < 0)
  810.                             wnd->wtop = 0;
  811.                         wnd->WndRow = el - wnd->wtop;
  812.                         wnd->CurrLine = el;
  813.                         wnd->CurrCol = ec;
  814.                         SendMessage(wnd, KEYBOARD, '\r', 0);
  815.                         SendMessage(wnd, KEYBOARD, '\r', 0);
  816.                     }
  817.                     else    {
  818.                         /* --- put cursor back at beginning --- */
  819.                         wnd->CurrLine = TextLineNumber(wnd, bl);
  820.                         wnd->CurrCol = bc;
  821.                         if (fl < wnd->wtop)
  822.                             wnd->wtop = fl;
  823.                         wnd->WndRow = fl - wnd->wtop;
  824.                     }
  825.                     SendMessage(wnd, PAINT, 0, 0);
  826.                     SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  827.                     wnd->TextChanged = TRUE;
  828.                     BuildTextPointers(wnd);
  829.                     return TRUE;
  830.                 }
  831.                 default:
  832.                     break;
  833.             }
  834.             break;
  835.         }
  836. #endif
  837.         case CLOSE_WINDOW:
  838.             SendMessage(NULLWND, HIDE_CURSOR, 0, 0);
  839.             if (wnd->DeletedText != NULL)
  840.                 free(wnd->DeletedText);
  841.             break;
  842.         default:
  843.             break;
  844.     }
  845.     return BaseWndProc(EDITBOX, wnd, msg, p1, p2);
  846. }
  847.  
  848. #ifdef INCLUDE_MULTILINE
  849. static void PasteText(WINDOW wnd, char *SaveTo, int len)
  850. {
  851.     int plen = strlen(wnd->text) + len;
  852.     char *bl, *el;
  853.     int width = 0;
  854.  
  855.     if (plen > wnd->textlen)    {
  856.         wnd->text = realloc(wnd->text, plen+1);
  857.         wnd->textlen = plen;
  858.     }
  859.     bl = CurrChar;
  860.     el = bl+len;
  861.     memmove(el,    bl,    strlen(bl)+1);
  862.     memmove(bl, SaveTo, len);
  863.     while (bl < el)    {
  864.         if (*bl++ == '\n')    {
  865.             wnd->textwidth = max(width, wnd->textwidth);
  866.             wnd->wlines++;
  867.             width = 0;
  868.         }
  869.         width++;
  870.     }
  871.     BuildTextPointers(wnd);
  872.     SendMessage(wnd, PAINT, 0, 0);
  873. }
  874.  
  875. static void SaveDeletedText(WINDOW wnd, char *bbl, int len)
  876. {
  877.     wnd->DeletedLength = len;
  878.     if ((wnd->DeletedText = realloc(wnd->DeletedText, len)) != NULL)
  879.         memmove(wnd->DeletedText, bbl, len);
  880. }
  881.  
  882. #endif
  883.  
  884. static void Forward(WINDOW wnd)
  885. {
  886.     if (*(CurrChar+1) == '\0')
  887.         return;
  888. #ifdef INCLUDE_MULTILINE
  889.     if (*CurrChar == '\n')    {
  890.         Home(wnd);
  891.         Downward(wnd);
  892.     }
  893. #endif
  894.     else    {
  895.         wnd->CurrCol++;
  896.         if (WndCol == ClientWidth(wnd))    {
  897.             wnd->wleft++;
  898.             SendMessage(wnd, PAINT, 0, 0);
  899.         }
  900.     }
  901. }
  902.  
  903. static void StickEnd(WINDOW wnd)
  904. {
  905.     char *cp = TextLine(wnd, wnd->CurrLine);
  906.     int len = (int) (strchr(cp, '\n') - cp);
  907.  
  908.     wnd->CurrCol = min(len, wnd->CurrCol);
  909.     if (wnd->wleft > wnd->CurrCol)    {
  910.         wnd->wleft = max(0, wnd->CurrCol - 4);
  911.         SendMessage(wnd, PAINT, 0, 0);
  912.     }
  913.     else if (wnd->CurrCol-wnd->wleft >= ClientWidth(wnd))    {
  914.         wnd->wleft = wnd->CurrCol - (ClientWidth(wnd)-1);
  915.         SendMessage(wnd, PAINT, 0, 0);
  916.     }
  917. }
  918.  
  919. #ifdef INCLUDE_MULTILINE
  920. static void Downward(WINDOW wnd)
  921. {
  922.     if (isMultiLine(wnd) && wnd->WndRow+wnd->wtop+1 < wnd->wlines)    {
  923.         DownLine(wnd);
  924.         if (wnd->WndRow == ClientHeight(wnd)-1)    {
  925.             if (wnd->wtop+ClientHeight(wnd) <= wnd->wlines)    {
  926.                 wnd->wtop++;
  927.                 SendMessage(wnd, PAINT, 0, 0);
  928.             }
  929.         }
  930.         else
  931.             wnd->WndRow++;
  932.         StickEnd(wnd);
  933.     }
  934. }
  935.  
  936. static void DownLine(WINDOW wnd)
  937. {
  938.     wnd->CurrLine++;
  939. }
  940.  
  941. static void UpLine(WINDOW wnd)
  942. {
  943.     if (wnd->CurrLine > 0)
  944.         --wnd->CurrLine;
  945. }
  946.  
  947. static void Upward(WINDOW wnd)
  948. {
  949.     if (isMultiLine(wnd) && wnd->CurrLine != 0)    {
  950.         UpLine(wnd);
  951.         if (wnd->WndRow == 0)    {
  952.             if (wnd->wtop)    {
  953.                 --wnd->wtop;
  954.                 SendMessage(wnd, PAINT, 0, 0);
  955.             }
  956.         }
  957.         else
  958.             --wnd->WndRow;
  959.         StickEnd(wnd);
  960.     }
  961. }
  962. #endif
  963.  
  964. static void Backward(WINDOW wnd)
  965. {
  966.     if (wnd->CurrCol)    {
  967.         if (wnd->CurrCol-- <= wnd->wleft)    {
  968.             if (wnd->wleft != 0)    {
  969.                 --wnd->wleft;
  970.                 SendMessage(wnd, PAINT, 0, 0);
  971.                 return;
  972.             }
  973.         }
  974.         else
  975.             return ;
  976.     }
  977. #ifdef INCLUDE_MULTILINE
  978.     if (isMultiLine(wnd) && wnd->CurrLine != 0)    {
  979.         Upward(wnd);
  980.         End(wnd);
  981.     }
  982. #endif
  983. }
  984.  
  985. static void End(WINDOW wnd)
  986. {
  987.     while (*CurrChar != '\n')
  988.         ++wnd->CurrCol;
  989.     if (WndCol >= ClientWidth(wnd))    {
  990.         wnd->wleft = wnd->CurrCol - (ClientWidth(wnd)-1);
  991.         SendMessage(wnd, PAINT, 0, 0);
  992.     }
  993. }
  994.  
  995. static void Home(WINDOW wnd)
  996. {
  997.     wnd->CurrCol = 0;
  998.     if (wnd->wleft != 0)    {
  999.         wnd->wleft = 0;
  1000.         SendMessage(wnd, PAINT, 0, 0);
  1001.     }
  1002. }
  1003.  
  1004. #define isWhite(c)     ((c) == ' ' || (c) == '\n')
  1005.  
  1006. static void NextWord(WINDOW wnd)
  1007. {
  1008.     int savetop = wnd->wtop;
  1009.     int saveleft = wnd->wleft;
  1010.     ClearVisible(wnd);
  1011.     while (!isWhite(*CurrChar))    {
  1012.         if (*(CurrChar+1) == '\0')
  1013.             break;
  1014.         Forward(wnd);
  1015.     }
  1016.     while (isWhite(*CurrChar))    {
  1017.         if (*(CurrChar+1) == '\0')
  1018.             break;
  1019.         Forward(wnd);
  1020.     }
  1021.     SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  1022.     SetVisible(wnd);
  1023.     if (wnd->wtop != savetop || wnd->wleft != saveleft)
  1024.         SendMessage(wnd, PAINT, 0, 0);
  1025. }
  1026.  
  1027. static void PrevWord(WINDOW wnd)
  1028. {
  1029.     int savetop = wnd->wtop;
  1030.     int saveleft = wnd->wleft;
  1031.     ClearVisible(wnd);
  1032.     Backward(wnd);
  1033.     while (isWhite(*CurrChar))    {
  1034.         if (wnd->CurrLine == 0 && wnd->CurrCol == 0)
  1035.             break;
  1036.         Backward(wnd);
  1037.     }
  1038.     while (!isWhite(*CurrChar))    {
  1039.         if (wnd->CurrLine == 0 && wnd->CurrCol == 0)
  1040.             break;
  1041.         Backward(wnd);
  1042.     }
  1043.     if (isWhite(*CurrChar))
  1044.         Forward(wnd);
  1045.     if (wnd->wleft != saveleft)
  1046.         if (wnd->CurrCol >= saveleft)
  1047.             if (wnd->CurrCol - saveleft < ClientWidth(wnd))
  1048.                 wnd->wleft = saveleft;
  1049.     SendMessage(wnd, KEYBOARD_CURSOR, WndCol, wnd->WndRow);
  1050.     SetVisible(wnd);
  1051.     if (wnd->wtop != savetop || wnd->wleft != saveleft)
  1052.         SendMessage(wnd, PAINT, 0, 0);
  1053. }
  1054.  
  1055. static void ResetEditBox(WINDOW wnd)
  1056. {
  1057.     *wnd->text = '\0';
  1058.     wnd->wlines = 0;
  1059.     wnd->CurrLine = 0;
  1060.     wnd->CurrCol = 0;
  1061.     wnd->WndRow = 0;
  1062.     wnd->TextChanged = FALSE;
  1063.     wnd->wleft = 0;
  1064. }
  1065.  
  1066. static void AddTextPointers(WINDOW wnd, int lineno, int ct)
  1067. {
  1068.     while (lineno < wnd->wlines)
  1069.         *((int *)(wnd->extension) + lineno++) += ct;
  1070. }
  1071.  
  1072. static int TextLineNumber(WINDOW wnd, char *lp)
  1073. {
  1074.     int lineno;
  1075.     char *cp;
  1076.     for (lineno = 0; lineno < wnd->wlines; lineno++)    {
  1077.         cp = wnd->text + *((int *)(wnd->extension) + lineno);
  1078.         if (cp == lp)
  1079.             return lineno;
  1080.         if (lineno && cp > lp)
  1081.             return lineno - 1;
  1082.     }
  1083.     return 0;
  1084. }
  1085.  
  1086.  
  1087.