home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / OWLSRC.PAK / EDIT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  24.8 KB  |  946 lines

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