home *** CD-ROM | disk | FTP | other *** search
/ C Programming Starter Kit 2.0 / SamsPublishing-CProgrammingStarterKit-v2.0-Win31.iso / bc45 / owlsrc.pak / EDIT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-24  |  22.2 KB  |  853 lines

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