home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c083 / 11.ddi / OWLSRC.PAK / EDIT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-02  |  21.7 KB  |  848 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows - (C) Copyright 1991, 1993 by Borland International
  3. //   source\owl\edit.cpp
  4. //   Implementation of class TEdit.  This defines the basic behavior
  5. //   of all edit controls.
  6. //----------------------------------------------------------------------------
  7. #pragma hdrignore SECTION
  8. #include <owl\owlpch.h>
  9. #include <owl\edit.h>
  10. #include <owl\validate.h>
  11. #include <dos.h>
  12. #include <string.h>
  13. #include <ctype.h>
  14.  
  15. #if !defined(SECTION) || SECTION == 1
  16.  
  17. TEdit* TEdit::ValidatorReFocus = 0;
  18.  
  19. DEFINE_RESPONSE_TABLE1(TEdit, TStatic)
  20.   EV_COMMAND(CM_EDITCUT, CmEditCut),
  21.   EV_COMMAND(CM_EDITCOPY, CmEditCopy),
  22.   EV_COMMAND(CM_EDITPASTE, CmEditPaste),
  23.   EV_COMMAND(CM_EDITDELETE, CmEditDelete),
  24.   EV_COMMAND(CM_EDITCLEAR, CmEditClear),
  25.   EV_COMMAND(CM_EDITUNDO, CmEditUndo),
  26.   EV_COMMAND_ENABLE(CM_EDITCUT, CmSelectEnable),
  27.   EV_COMMAND_ENABLE(CM_EDITCOPY, CmSelectEnable),
  28.   EV_COMMAND_ENABLE(CM_EDITDELETE, CmSelectEnable),
  29.   EV_COMMAND_ENABLE(CM_EDITPASTE, CmPasteEnable),
  30.   EV_COMMAND_ENABLE(CM_EDITCLEAR, CmCharsEnable),
  31.   EV_COMMAND_ENABLE(CM_EDITUNDO, CmModEnable),
  32.   EV_NOTIFY_AT_CHILD(EN_ERRSPACE, ENErrSpace),
  33.   EV_WM_CHAR,
  34.   EV_WM_KEYDOWN,
  35.   EV_WM_GETDLGCODE,
  36.   EV_WM_SETFOCUS,
  37.   EV_WM_KILLFOCUS,
  38.   EV_WM_CHILDINVALID,
  39. END_RESPONSE_TABLE;
  40.  
  41. //
  42. // constructor for a TEdit object
  43. //
  44. // by default, edit control has a border and its text is left-justified
  45. //
  46. // multiline edit control has horizontal vertical scroll bars
  47. //
  48. TEdit::TEdit(TWindow*        parent,
  49.              int             id,
  50.              const char far* text,
  51.              int x, int y, int w, int h,
  52.              UINT            textLen,
  53.              BOOL            multiline,
  54.              TModule*        module)
  55.   : TStatic(parent, id, text, x, y, w, h, textLen, module)
  56. {
  57.   //
  58.   // undo the styles set by TStatic, & addin edit styles
  59.   //
  60.   Attr.Style &= ~SS_LEFT;
  61.   Attr.Style |= ES_LEFT | ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP;
  62.  
  63.   if (multiline)
  64.     Attr.Style |= ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | WS_HSCROLL;
  65.   Validator = 0;
  66. }
  67.  
  68. //
  69. // constructor for TEdit associated with a MS-Windows interface element
  70. // created by MS-Windows from a resource definition
  71. //
  72. // by default, data transfer is enabled
  73. //
  74. TEdit::TEdit(TWindow*   parent,
  75.              int        resourceId,
  76.              UINT       textLen,
  77.              TModule*   module)
  78.   : TStatic(parent, resourceId, textLen, module)
  79. {
  80.   EnableTransfer();
  81.   Validator = 0;
  82. }
  83.  
  84. TEdit::~TEdit()
  85. {
  86.   delete (TStreamableBase*)Validator;
  87. }
  88.  
  89. //
  90. // beeps when edit control runs out of space
  91. //
  92. void
  93. TEdit::ENErrSpace()
  94. {
  95.   MessageBeep(0);
  96.  
  97.   DefaultProcessing();  // give parent a chance to process
  98. }
  99.  
  100. //
  101. // Responds to the GetDlgCode query according to the
  102. // current state of the control.  If the edit control
  103. // contains valid input, then TABs are allowed for
  104. // changing focus.  Otherwise, requests that TABs be
  105. // sent to Self, where we will generate the Invalid
  106. // message (See WMKeyDown below).
  107. //
  108. UINT
  109. TEdit::EvGetDlgCode(MSG far* msg)
  110. {
  111.   UINT retVal = (UINT)TStatic::EvGetDlgCode(msg);
  112.   if (!IsValid(FALSE))
  113.     retVal |= DLGC_WANTTAB;
  114.   return retVal;
  115. }
  116.  
  117. //
  118. // Validates Self whenever a character is entered.  Allows
  119. // the character entry to be processed normally, then validates
  120. // the result and restores Self's text to its original state
  121. // if there is an incorrect entry.
  122. //
  123. // By default, the SupressFill parameter of the IsValidInput
  124. // method call to the Validator is set to False, so that it
  125. // is free to modify the string, if it is so configured.
  126. //
  127. void
  128. TEdit::EvChar(UINT key, UINT repeatCount, UINT flags)
  129. {
  130.   if (Validator && key != VK_BACK) {
  131.     int oldBuffLen = GetTextLen();
  132.     char far* oldBuff = new char[oldBuffLen+1];
  133.     GetText(oldBuff, oldBuffLen+1);
  134.     
  135.     UINT   startSel, endSel;
  136.     GetSelection(startSel, endSel);
  137.     BOOL wasAppending = endSel == oldBuffLen;
  138.  
  139.     TStatic::EvChar(key, repeatCount, flags);      // Process the new char...
  140.  
  141.     GetSelection(startSel, endSel);
  142.     int buffLen = GetTextLen();
  143.     char far* buff = (oldBuffLen > buffLen) ? LockBuffer(oldBuffLen+1) :
  144.                                               LockBuffer();
  145.  
  146.     //
  147.     // Run the result of the edit through the validator.  If incorrect,
  148.     // then restore the original text.  Otherwise, set the (possibly)
  149.     // modified result of the validation back into the edit control,
  150.     // so the results of the auto-fill (if any) can be viewed.
  151.     //
  152.     if (!Validator->HasOption(voOnAppend) || wasAppending && endSel == buffLen) {
  153.       if (!Validator->IsValidInput(buff, FALSE)) {
  154.         strcpy(buff, oldBuff);
  155.       } else {
  156.         if (startSel >= oldBuffLen && buffLen > oldBuffLen)
  157.           startSel = buffLen;
  158.         if (endSel >= oldBuffLen && buffLen > oldBuffLen)
  159.           endSel = buffLen;
  160.       }
  161.       UnlockBuffer(buff, TRUE);
  162.       SetSelection(startSel, endSel);
  163.  
  164.     } else {
  165.       if (endSel == buffLen && !Validator->IsValidInput(buff, FALSE))
  166.         Validator->Error();
  167.       UnlockBuffer(buff);
  168.     }
  169.     delete oldBuff;
  170.  
  171.   } else
  172.     TStatic::EvChar(key, repeatCount, flags);
  173. }
  174.  
  175. //
  176. // If the TAB key is sent to the Edit Control, check
  177. // the validity before allowing the focus to change.
  178. // The control will only get a TAB if WMGetDlgCode(above)
  179. // allows it, which is done when the control contains
  180. // invalid input (we re-validate here just for completeness,
  181. // in case descendants redefine any of this behavior).
  182. //
  183. // We need to validate on TAB focus-changes because there
  184. // is a case not handled by WMKillFocus: when focus is
  185. // lost to an OK or CANCEL button by tabbing.
  186. //
  187. void
  188. TEdit::EvKeyDown(UINT key, UINT /*repeatCount*/, UINT /*flags*/)
  189. {
  190.   if (key == VK_TAB && !IsValid(TRUE))
  191.     return;
  192.   if (Validator && Validator->HasOption(voOnAppend)) {
  193.     UINT  startSel, endSel;
  194.     GetSelection(startSel, endSel);
  195.     
  196.     int buffLen = GetTextLen(); // get new length of buffer
  197.     BOOL  wasAppending = endSel == buffLen;
  198.  
  199.     DefaultProcessing();
  200.  
  201.     if (!wasAppending) {
  202.       GetSelection(startSel, endSel);
  203.       char far* buff = LockBuffer();
  204.       if (endSel == strlen(buff) && !Validator->IsValidInput(buff, FALSE))
  205.         Validator->Error();
  206.       UnlockBuffer(buff);
  207.     }
  208.  
  209.   } else
  210.     DefaultProcessing();
  211. }
  212.  
  213. //
  214. // Validates this whenever the focus is about to be lost.
  215. // Holds onto the focus if this is not valid.  Checks first
  216. // to make sure that the focus is not being taken by either
  217. // (a) another app, or (b) a Cancel button, or (c) an OK
  218. // button (in which case CanClose will validate); in each case,
  219. // we don't want to validate.
  220. //
  221. void
  222. TEdit::EvKillFocus(HWND hWndGetFocus)
  223. {
  224.   //
  225.   // If another validator is attempting to regain focus, then let it
  226.   //
  227.   if (Validator && !ValidatorReFocus) {
  228.     // The window belongs to us if any of the window handles has an object
  229.     // attached
  230.     //
  231.     HWND hWnd = hWndGetFocus;
  232.     while (hWnd && !GetWindowPtr(hWnd))
  233.       hWnd = ::GetParent(hWnd);
  234.  
  235.     if (hWnd) {
  236.       int btnId = ::GetDlgCtrlID(hWndGetFocus);
  237.  
  238.       // Note that we do not allow IsValid to post the message
  239.       // box, since the change of focus resulting from that message
  240.       // will interfere with the change we are in the process of
  241.       // completing.  Instead, post a message to the Parent informing
  242.       // it of the validation failure, and providing it with a handle
  243.       // to us.
  244.       //
  245.       if (btnId != IDCANCEL && btnId != IDOK && !IsValid(FALSE)) {
  246.         ValidatorReFocus = this;
  247.         Parent->PostMessage(WM_CHILDINVALID, WPARAM(HWindow));
  248.       }
  249.     }
  250.   }
  251.   TControl::EvKillFocus(hWndGetFocus);
  252. }
  253.  
  254. //
  255. // We need to make sure the anti-oscillation flag is cleared
  256. //
  257. void
  258. TEdit::EvSetFocus(HWND hWndLostFocus)
  259. {
  260.   // If we're getting focus back because of invalid input, then clear the
  261.   // anti-oscillation flag
  262.   //
  263.   if (ValidatorReFocus == this)
  264.     ValidatorReFocus = 0;
  265.  
  266.   TControl::EvSetFocus(hWndLostFocus);
  267. }
  268.  
  269.  
  270. //
  271. // handler for input validation message sent by parent
  272. //
  273. void
  274. TEdit::EvChildInvalid(HWND)
  275. {
  276.   ValidatorError();
  277. }
  278.  
  279.  
  280. void
  281. TEdit::ValidatorError()
  282. {
  283.   if (Validator) {
  284.     SetFocus();
  285.     Validator->Error();
  286.   }
  287. }
  288.  
  289. void
  290. TEdit::Clear()
  291. {
  292.   DeleteSubText(0, UINT(-1));
  293. }
  294.  
  295. BOOL
  296. TEdit::CanClose()
  297. {
  298.   BOOL okToClose = TStatic::CanClose();
  299.   if (okToClose)
  300.     if (IsWindowEnabled() && !IsValid(TRUE)) {
  301.       ValidatorReFocus = this;
  302.       SetFocus();
  303.       return FALSE;
  304.     }
  305.   return okToClose;
  306. }
  307.  
  308. //
  309. // This function is called for Cut/Copy/Delete menu items to determine
  310. // whether or not the item is enabled.
  311. //
  312. void
  313. TEdit::CmSelectEnable(TCommandEnabler& commandHandler)
  314. {
  315.   UINT sPos, ePos;
  316.  
  317.   GetSelection(sPos, ePos);
  318.   commandHandler.Enable(sPos != ePos);
  319. }    
  320.  
  321. //
  322. // This function is called for the Paste menu item to determine whether or
  323. // not the item is enabled.
  324. //
  325. void
  326. TEdit::CmPasteEnable(TCommandEnabler& commandHandler)
  327. {
  328.   TClipboard& clipboard = OpenClipboard();
  329.   commandHandler.Enable(clipboard.IsClipboardFormatAvailable(CF_TEXT));
  330.     clipboard.CloseClipboard();
  331. }
  332.  
  333. //
  334. // This function is called for the Clear menu item to determine whether or
  335. // not the item is enabled.
  336. //
  337. void
  338. TEdit::CmCharsEnable(TCommandEnabler& commandHandler)
  339. {
  340.   commandHandler.Enable(!(GetNumLines() == 1 && GetLineLength(0) == 0));
  341. }
  342.  
  343. //
  344. // This function is called for the Undo menu item to determine whether or
  345. // not the item is enabled.
  346. //
  347. void
  348. TEdit::CmModEnable(TCommandEnabler& commandHandler)
  349. {
  350.   commandHandler.Enable(IsModified());
  351. }
  352.  
  353. //
  354. // returns the length of line number "LineNumber"
  355. //
  356. // if -1 is passed as the line number, the following applies:
  357. //   - returns the length of the line upon which the caret is positioned
  358. //   - if text is selected on the line, returns the line length minus the
  359. //     number of selected characters
  360. //   - if selected text spans more than one line, returns the length minus
  361. //     the number of selected characters
  362. //
  363. int
  364. TEdit::GetLineLength(int lineNumber) const
  365. {
  366.   return (int)CONST_CAST(TEdit*,this)->HandleMessage(EM_LINELENGTH,
  367.                                          (lineNumber > -1) ? 
  368.                                             GetLineIndex(lineNumber) : -1);
  369. }
  370.  
  371. //
  372. // returns the text of line number "lineNumber" (0-terminated)
  373. // returns FALSE if an error occurs or if the text doesn't fit in "TextString"
  374. //
  375. BOOL
  376. TEdit::GetLine(char far* textString, int strSize, int lineNumber) const
  377. {
  378.   if (strSize <= 0)
  379.     return FALSE;
  380.  
  381.   BOOL success = strSize >= GetLineLength(lineNumber) + 1;
  382.  
  383.   if (strSize < sizeof(int)) {
  384.     textString[0] = 0;
  385.     return success;
  386.   }
  387.   ((int far*)textString)[0] = strSize;
  388.  
  389.   int bytesCopied = (int)CONST_CAST(TEdit*,this)->
  390.                       HandleMessage(EM_GETLINE, lineNumber, (LPARAM)textString);
  391.  
  392.   textString[bytesCopied] = '\0'; // Windows returns non-0 terminated string
  393.  
  394.   if (bytesCopied != (int)CONST_CAST(TEdit*,this)->HandleMessage(EM_LINELENGTH,GetLineIndex(lineNumber)))
  395.     return FALSE;
  396.   return success;
  397. }
  398.  
  399. //
  400. // Lock the edit control's buffer, optionally resizing it first, and return
  401. // a far pointer to the beginning of it, or 0 if failure.
  402. // Must call UnlockBuffer when finished with buffer.
  403. //
  404. char far*
  405. TEdit::LockBuffer(UINT newSize)
  406. {
  407.   // make a copy if can't get handle. Single line edit controls or Win32s will
  408.   // fail the GetHandle(), so do it the hard way.
  409.   //
  410.   if (!(GetWindowLong(GWL_STYLE)&ES_MULTILINE)
  411. #if defined(__WIN32__)
  412.        || (HIWORD(::GetVersion())&0x8000)
  413. #endif
  414.   ) {
  415.     if (!newSize)
  416.       newSize = GetTextLen()+1;
  417.     char* buffer = new char[newSize];
  418.     GetText(buffer, newSize);
  419.     return buffer;
  420.   }
  421.  
  422.   HLOCAL  hBuffer = GetHandle();
  423.  
  424.   #if defined(__WIN32__)
  425.     if (newSize) {
  426.       hBuffer = LocalReAlloc(hBuffer, newSize, LHND);
  427.       if (!hBuffer)
  428.         return 0;
  429.     }
  430.     return (char*)LocalLock(hBuffer);
  431.   #else
  432.     WORD editDS = FP_SEG(GlobalLock((HINSTANCE)GetWindowWord(GWW_HINSTANCE)));
  433.     asm push DS;
  434.     _DS = editDS;
  435.  
  436.     if (newSize) {
  437.       hBuffer = LocalReAlloc(hBuffer, newSize, LHND);
  438.       if (!hBuffer) {
  439.         asm pop DS;
  440.         return 0;
  441.       }
  442.     }
  443.     char far* returnValue = (char far*)MK_FP(_DS,LocalLock(hBuffer));
  444.     asm pop DS;
  445.     return returnValue;
  446.   #endif
  447. }
  448.  
  449. //
  450. // Unlock the edit control's buffer locked by LockBuffer.
  451. // Also informs control of new handle if indicated. Should update handle
  452. // if buffer is resized or written to.
  453. // Ignores call if buffer is 0
  454. //
  455. void
  456. TEdit::UnlockBuffer(const char far* buffer, BOOL updateHandle)
  457. {
  458.   if (!buffer)
  459.     return;
  460.  
  461.   // if a copy was made on lock, copy it back if requested & free buffer
  462.   //
  463.   if (!(GetWindowLong(GWL_STYLE)&ES_MULTILINE)
  464. #if defined(__WIN32__)
  465.        || (HIWORD(::GetVersion())&0x8000)
  466. #endif
  467.    ) {
  468.     if (updateHandle)
  469.       SetText(buffer);
  470.     delete(char*)buffer;
  471.     return;
  472.   }
  473.  
  474.   #if defined(__WIN32__)
  475.     HLOCAL  hBuffer = LocalHandle((LPVOID)buffer);
  476.     LocalUnlock(hBuffer);
  477.   #else
  478.     WORD editDS = FP_SEG(buffer);
  479.     asm push DS;
  480.     _DS = editDS;
  481.  
  482.     HLOCAL  hBuffer = LocalHandle((void near*)FP_OFF(buffer));
  483.     LocalUnlock(hBuffer);
  484.  
  485.     asm pop DS;
  486.     GlobalUnlock((HGLOBAL)GlobalHandle(editDS));   // unlock LockBuffer's lock
  487.   #endif
  488.  
  489.   //
  490.   // handle may have moved or buffer contents written on
  491.   //
  492.   if (updateHandle)
  493.     SetHandle(hBuffer);
  494. }
  495.  
  496. //
  497. // Similar to strstr(), but is case sensitive or insensitive, uses Windows
  498. // string functions to work with different language drivers
  499. //
  500. static const char far*
  501. strstrcd(const char far* str1, const char far* str2, BOOL caseSens)
  502. {
  503.   PRECONDITION(str1 && str2);
  504.   int len2 = strlen(str2);
  505.   char far* p = (char far*)str1;
  506.   const char far* endp = str1 + strlen(str1) - len2 + 1;
  507.  
  508.   if (caseSens)
  509.     while (p < endp) {
  510.       char c = p[len2];            // must term p to match str2 len
  511.       p[len2] = 0;                 // for strcmp to work.
  512.       if (strcmp(p, str2) == 0) {
  513.         p[len2] = c;
  514.         return p;
  515.       }
  516.       p[len2] = c;
  517.       p++;
  518.     }
  519.   else
  520.     while (p < endp) {
  521.       char c = p[len2];
  522.       p[len2] = 0;
  523.       if (strcmpi(p, str2) == 0) {
  524.         p[len2] = c;
  525.         return p;
  526.       }
  527.       p[len2] = c;
  528.       p++;
  529.     }
  530.   return 0;
  531. }
  532.  
  533. //
  534. // Similar to strstrcd(), but searches backwards. Needs the length of str1
  535. // to know where to start.
  536. //
  537. static const char far*
  538. strrstrcd(const char far* str1, UINT len1, const char far* str2,
  539.            BOOL caseSens)
  540. {
  541.   PRECONDITION(str1 && str2);
  542.   int len2 = strlen(str2);
  543.   char far* p = (char far*)(str1 + len1 - len2);
  544.  
  545.   if (caseSens)
  546.     while (p >= str1) {
  547.       char c = p[len2];            // must term p to match str2 len
  548.       p[len2] = 0;                 // for strcmp to work.
  549.       if (strcmp(p, str2) == 0) {
  550.         p[len2] = c;
  551.         return p;
  552.       }
  553.       p[len2] = c;
  554.       p--;
  555.     }
  556.   else
  557.     while (p >= str1) {
  558.       char c = p[len2];
  559.       p[len2] = 0;
  560.       if (strcmpi(p, str2) == 0) {
  561.         p[len2] = c;
  562.         return p;
  563.       }
  564.       p[len2] = c;
  565.       p--;
  566.     }
  567.   return 0;
  568. }
  569.  
  570. //
  571. // searchs for and selects the given text and returns the offset of the text
  572. // or -1 if the text is not found
  573. //
  574. // if "startPos" = -1 then it is assumed that the start pos is the
  575. // end/beginning (depending on direction) of the current selection
  576. //
  577. int
  578. TEdit::Search(UINT startPos, const char far* text, BOOL caseSensitive,
  579.               BOOL wholeWord, BOOL up)
  580. {
  581.   if (!text || !text[0])
  582.     return -1;
  583.  
  584.   if (startPos == (UINT)-1) {
  585.     UINT sBeg, sEnd;
  586.     GetSelection(sBeg, sEnd);
  587.     startPos = up ? sBeg : sEnd;
  588.   }
  589.   int textLen = strlen(text);
  590.  
  591.   //
  592.   // Lock the text buffer to get the pointer, and search thru it for the text
  593.   //
  594.   const char far* buffer = LockBuffer();
  595.   const char far* pos;
  596.   for (;;) {
  597.     if (up)
  598.       pos = strrstrcd(buffer, startPos, text, caseSensitive);
  599.     else
  600.       pos = strstrcd(buffer+startPos, text, caseSensitive);
  601.  
  602.     //
  603.     // If whole-word matching is enabled and we have a match so far, then make
  604.     // sure the match is on word boundaries.
  605.     //
  606.     if (wholeWord && pos) {
  607.       if (pos > buffer && isalnum(pos[-1]) || // Match is in preceding word
  608.           textLen < strlen(pos) && isalnum(pos[textLen])) {
  609.         startPos = (UINT)(pos-buffer) + !up;
  610.         continue;  // Skip this match and keep searching
  611.       }
  612.     }
  613.     break;  // Out of for loop
  614.   }
  615.  
  616.   // If we've got a match, select that text, cleanup & return.
  617.   //
  618.   if (pos) {
  619.     UINT sBeg = (UINT)(pos - buffer);
  620.     UnlockBuffer(buffer);
  621.     SetSelection(sBeg, sBeg + textLen);
  622. #if defined(__WIN32__)
  623.     HandleMessage(WM_KEYDOWN, VK_RIGHT);
  624.     SetSelection(sBeg, sBeg + textLen);
  625. #endif
  626.  
  627.     return sBeg;
  628.   }
  629.   UnlockBuffer(buffer);
  630.   return -1;
  631. }
  632.  
  633.  
  634. //
  635. // deletes the selected text; returns FALSE if no text is selected
  636. //
  637. BOOL
  638. TEdit::DeleteSelection()
  639. {
  640.   UINT  startPos, endPos;
  641.  
  642.   GetSelection(startPos, endPos);
  643.  
  644.   if (startPos != endPos) {
  645.     HandleMessage(WM_CLEAR);
  646.     return TRUE;
  647.   }
  648.   return FALSE;
  649. }
  650.  
  651. //
  652. // deletes text in the range "startPos .. endPos"
  653. //
  654. // returns FALSE if an error occurs
  655. //
  656. BOOL
  657. TEdit::DeleteSubText(UINT startPos, UINT endPos)
  658. {
  659.   if (SetSelection(startPos, endPos))
  660.     return DeleteSelection();
  661.  
  662.   else
  663.     return FALSE;
  664. }
  665.  
  666. //
  667. // deletes the text at line number "lineNumber" (deletes the line containing
  668. // the caret if "lineNumber" is -1
  669. //
  670. // returns FALSE if "lineNumber" is greater than the number of lines
  671. //
  672. BOOL
  673. TEdit::DeleteLine(int lineNumber)
  674. {
  675.   if (lineNumber == -1)
  676.     lineNumber = GetLineFromPos(GetLineIndex(-1));
  677.  
  678.   int firstPos = GetLineIndex(lineNumber);
  679.  
  680.   if (firstPos != -1) {
  681.     int  lastPos = GetLineIndex(lineNumber + 1);
  682.  
  683.     if (lastPos == -1)
  684.       lastPos = firstPos + GetLineLength(lineNumber);
  685.  
  686.     if (firstPos == 0  && firstPos == lastPos) {
  687.       SetText("");
  688.       return TRUE;
  689.  
  690.     } else {
  691.       return DeleteSubText(firstPos, lastPos);
  692.     }
  693.   }
  694.  
  695.   return FALSE;
  696. }
  697.  
  698. //
  699. // retrieves the text of the associated edit control between the passed
  700. // positions
  701. //
  702. void
  703. TEdit::GetSubText(char far* textString, UINT startPos, UINT endPos) const
  704. {
  705.   int  tempIndex = 0;
  706.  
  707.   if (endPos >= startPos) {
  708.     BOOL  okToContinue = TRUE;
  709.     int   startLine = GetLineFromPos(startPos);
  710.     int   endLine = GetLineFromPos(endPos);
  711.     int   startChar = startPos - GetLineIndex(startLine);
  712.     int   endChar = endPos - GetLineIndex(endLine);
  713.  
  714.     for (int tempLine = startLine; tempLine <= endLine && okToContinue; tempLine++) {
  715.       int    tempLineLength = GetLineLength(tempLine);
  716.       char*  line = new char [tempLineLength+1];
  717.       int    tempStart = tempLine == startLine ? startChar : 0;
  718.       int    tempEnd;
  719.  
  720.       if (tempLine == endLine)
  721.         tempEnd = (endChar > tempLineLength) ? tempLineLength : endChar;
  722.  
  723.       else
  724.         tempEnd = tempLineLength;
  725.  
  726.       int  tempSize = tempEnd - tempStart;
  727.  
  728.       if (GetLine(line, tempLineLength + 1, tempLine)) {
  729.         //
  730.         // can happen if we're indexing the CR/LF
  731.         //
  732.         if (tempSize > 0) {
  733.           memcpy(&textString[tempIndex], &line[tempStart], tempSize);
  734.           tempIndex += tempSize;
  735.         }
  736.  
  737.         if (tempLine != endLine) {
  738.           textString[tempIndex++] = 0x0d;  // CR
  739.           textString[tempIndex++] = 0x0a;  // LF
  740.         }
  741.  
  742.       } else
  743.         okToContinue = FALSE;
  744.  
  745.       delete line;
  746.     }
  747.   }
  748.   textString[tempIndex] = '\0';
  749. }
  750.  
  751. //
  752. // Return name of predefined Windows edit class
  753. //
  754. char far*
  755. TEdit::GetClassName()
  756. {
  757.   return "EDIT";
  758. }
  759.  
  760. //
  761. // limits the amount of text that an edit control can have to the value of
  762. // TextLen
  763. //
  764. void
  765. TEdit::SetupWindow()
  766. {
  767.   TStatic::SetupWindow();
  768.  
  769.   if (TextLen != 0)
  770.     HandleMessage(EM_LIMITTEXT, TextLen - 1);
  771. }
  772.  
  773. BOOL
  774. TEdit::IsValid(BOOL reportError)
  775. {
  776.   if (Validator) {
  777.     char far* buffer = LockBuffer();
  778.     BOOL valid = reportError ? Validator->Valid(buffer) : 
  779.                                Validator->IsValid(buffer);
  780.     UnlockBuffer(buffer);
  781.     return valid;
  782.   }
  783.   return TRUE;
  784. }
  785.  
  786. void
  787. TEdit::SetValidator(TValidator* validator)
  788. {
  789.   delete Validator;
  790.   Validator = validator;
  791. }
  792.  
  793. //
  794. // transfers state information for TEdit controls
  795. //
  796. // delegates to the Validator if there is one & it has the transfer option set,
  797. // allowing the Validator to convert the text to/from the appropriate type.
  798. // else passes to base, TStatic.
  799. //
  800. // the return value is the size (in bytes) of the transfer data
  801. //
  802. UINT
  803. TEdit::Transfer(void* buffer, TTransferDirection direction)
  804. {
  805.   if (Validator && Validator->HasOption(voTransfer) && GetNumLines() <= 1) {
  806.     CHECK(GetWindowTextLength() < TextLen);
  807.     char* text = new char[TextLen];
  808.     GetText(text, TextLen);
  809.     UINT result = Validator->Transfer(text, buffer, direction);
  810.     if (result == 0)
  811.       result = TStatic::Transfer(buffer, direction);
  812.     else if (direction == tdSetData)
  813.       SetText(text);
  814.     delete text;
  815.     return result;
  816.   }
  817.   return TStatic::Transfer(buffer, direction);
  818. }
  819.  
  820. #endif
  821.  
  822. #if !defined(SECTION) || SECTION == 2
  823.  
  824. IMPLEMENT_STREAMABLE1(TEdit, TStatic);
  825.  
  826. //
  827. // reads an instance of TEdit from the passed ipstream
  828. //
  829. void*
  830. TEdit::Streamer::Read(ipstream& is, uint32 /*version*/) const
  831. {
  832.   ReadBaseObject((TStatic*)GetObject(), is);
  833.   is >> GetObject()->Validator;
  834.   return GetObject();
  835. }
  836.  
  837. //
  838. // writes the TEdit to the passed opstream
  839. //
  840. void
  841. TEdit::Streamer::Write(opstream& os) const
  842. {
  843.   WriteBaseObject((TStatic*)GetObject(), os);
  844.   os << GetObject()->Validator;
  845. }
  846.  
  847. #endif
  848.