Alaska Software Inc. - XbpMle Question
Username: Password:
AuthorTopic: XbpMle Question
Carlos A Beling XbpMle Question
on Tue, 16 Mar 2021 18:37:40 -0300
Good night.
Please how can I find a string's position in XbpMle():editbuffer() for 
to mark it?

Fraternally
Beling
Andreas Gehrs-Pahl
Re: XbpMle Question
on Tue, 16 Mar 2021 22:17:57 -0400
Carlos,

>Please how can I find a string's position in XbpMle():editbuffer() for 
>to mark it?

The following three methods are copied from my custom MLE class "AGP_Text". 
They include calls to a lot of functions and procedures that you won't have 
or need, so you can't compile them, but the code should explain how to do 
this in principle. 

The ::Find_Text() method finds the first or next occurrence of a string in 
the MLE and optionally displays a dialog to enter a new search string.

The ::Set_Cursor() method calculates and stores the positions of the text to 
be displayed and any highlighted area.

The ::Restore_Cursor() method moves the cursor to a particular position in 
the MLE, and if necessary, scrolls the required text into view, and also 
optionally highlights a particular area. This is also used when focus is 
restored from a different dialog.

The only standard MLE methods that you really need to use are:
* ::CharFromLine()
* ::LineFromChar()
* ::QueryFirstChar()
* ::QueryMarked()
* ::SetFirstChar()
* ::SetMarked()

They are all documented in the Xbase++ online documentation.

Some longer lines of the following source code will probably wrap.

******************************
* AGP_Text: Find_Text Method *
******************************
Method AGP_Text:Find_Text(lNewSearch)
LOCAL nLines  := int(::CurrentSize()[2] / ::SetFont():Height)
LOCAL nPos    := ::Pos()
LOCAL aText   := {010, 050, 070, 320, , 'Search Text:', 'cText', 40, replicate('X', 40), {|x| Validate_Trim(x), .t.}, , , , , .f.}
LOCAL cMarked := Get_Text_From_ClipBoard()
PRIVATE cText := ::cFindText
  if empty(cText) .or. lNewSearch
    if ::CopyMarked() > 0
      cText := Get_Text_From_ClipBoard()
    else
      Save_Text_to_ClipBoard(cMarked)
    endif
    if .not. Input_Dialog('Find Text:', , {440, 100}, {aText}, '~Search', , 'Cancel Text Search?', 1, .t., ID_FIND_TEXT_DIALOG)
      cText := ''
    endif
  endif
  if .not. empty(cText)
    ::cFindText := cText
    nPos := at(upper(cText), upper(::EditBuffer()), nPos + 1)
    if nPos == 0
      nPos := at(upper(cText), upper(::EditBuffer()))
    endif
    if nPos > 0
      ::nLastPos    := nPos
      ::nLastTopPos := ::CharFromLine(max(1, ::LineFromChar(nPos) - int(nLines / 3)))
      ::Set_Cursor(nPos, nPos + len(cText))
    endif
  endif
return (Self)

*******************************
* AGP_Text: Set_Cursor Method *
*******************************
Method AGP_Text:Set_Cursor(nPos, nMark)
LOCAL nLines   := int(::CurrentSize()[2] / ::SetFont():Height)
LOCAL nMarkPos := iif(empty(nMark), nPos, nMark)
  ::nLastPos := nPos
  if (nPos < ::QueryFirstChar()) .or. (::LineFromChar(nPos) >= (nLines + ::LineFromChar(::QueryFirstChar())))
    ::nLastTopPos := nPos
  endif
  ::aLastMarked := {nPos, nMarkPos}
  ::Restore_Cursor()
return (::Pos() == nPos)

***********************************
* AGP_Text: Restore_Cursor Method *
***********************************
Method AGP_Text:Restore_Cursor()
LOCAL aMarked  := ::aLastMarked
LOCAL nCurTop  := max(1, ::nLastTopPos)
LOCAL nCurPos  := max(1, ::nLastPos)
LOCAL nMaxPos  := max(1, len(::EditBuffer()) + iif(Set(_SET_INSERT), 1, 0))
LOCAL nCols    := int(::CurrentSize()[1] / ::SetFont():Width)
LOCAL nCurCol  := nCurPos - ::CharFromLine(::LineFromChar(nCurPos))
LOCAL nOffset  := max(0, int(nCurCol - (nCols / 2)))
LOCAL nTopLine := ::LineFromChar(nCurTop)
LOCAL nCurLine := ::LineFromChar(nCurPos)
  ************************************************************************
  * Position the Cursor at the Last active Position in the Input Buffer, 
   display the same part of the Input Buffer as was shown previously,   
   and mark the same Text that was previously marked (if any).          *
  ************************************************************************
  if empty(::EditBuffer())
    aMarked := {1, 1}
  elseif nCurPos > nMaxPos
    aMarked := {nMaxPos, nMaxPos}
  endif
  if ::WordWrap .or. .not. ::HorizScroll
    ::SetFirstChar(nCurTop)
  else
    while nTopLine < nCurLine
      if abs(::CharFromLine(nTopLine) - ::CharFromLine(nTopLine + 1)) >= nOffset
        ::SetFirstChar(::CharFromLine(nTopLine) + nOffset) ; exit
      else
        nTopLine++
      endif
    enddo
    if nTopLine == nCurLine
      ::SetFirstChar(::CharFromLine(nCurLine) + nOffset)
    endif
  endif
  ::SetMarked(aMarked)
return (Self)

Hope that helps,

Andreas

Andreas Gehrs-Pahl
Absolute Software, LLC

phone: (989) 723-9927
email: Andreas@AbsoluteSoftwareLLC.com
web:   http://www.AbsoluteSoftwareLLC.com
[L]:   https://www.LinkedIn.com/in/AndreasGehrsPahl
[F]:   https://www.FaceBook.com/AbsoluteSoftwareLLC
Carlos A Beling Re: XbpMle Question
on Wed, 17 Mar 2021 10:10:50 -0300
HI Andreas.
God day.
Again many thanks.

Just one question:
In the code below:
 >      nPos := at(upper(cText), upper(::EditBuffer()), nPos + 1)
 >      if nPos == 0
 >        nPos := at(upper(cText), upper(::EditBuffer()))
 >      endif
I think that at(upper(cText), upper(::EditBuffer()), nPos + 1) will 
return a position considering the CRLF existing in ::EditBuffer().
If that is true, may the method ::LineFromChar(nPos) return unreal line 
number?

Fraternally
Beling


On 16/03/2021 23:17, Andreas Gehrs-Pahl wrote:
> Carlos,
> 
>> Please how can I find a string's position in XbpMle():editbuffer() for
>> to mark it?
> 
> The following three methods are copied from my custom MLE class "AGP_Text".
> They include calls to a lot of functions and procedures that you won't have
> or need, so you can't compile them, but the code should explain how to do
> this in principle.
> 
> The ::Find_Text() method finds the first or next occurrence of a string in
> the MLE and optionally displays a dialog to enter a new search string.
> 
> The ::Set_Cursor() method calculates and stores the positions of the text to
> be displayed and any highlighted area.
> 
> The ::Restore_Cursor() method moves the cursor to a particular position in
> the MLE, and if necessary, scrolls the required text into view, and also
> optionally highlights a particular area. This is also used when focus is
> restored from a different dialog.
> 
> The only standard MLE methods that you really need to use are:
> * ::CharFromLine()
> * ::LineFromChar()
> * ::QueryFirstChar()
> * ::QueryMarked()
> * ::SetFirstChar()
> * ::SetMarked()
> 
> They are all documented in the Xbase++ online documentation.
> 
> Some longer lines of the following source code will probably wrap.
> 
> ******************************
> * AGP_Text: Find_Text Method *
> ******************************
> Method AGP_Text:Find_Text(lNewSearch)
> LOCAL nLines  := int(::CurrentSize()[2] / ::SetFont():Height)
> LOCAL nPos    := ::Pos()
> LOCAL aText   := {010, 050, 070, 320, , 'Search Text:', 'cText', 40, replicate('X', 40), {|x| Validate_Trim(x), .t.}, , , , , .f.}
> LOCAL cMarked := Get_Text_From_ClipBoard()
> PRIVATE cText := ::cFindText
>    if empty(cText) .or. lNewSearch
>      if ::CopyMarked() > 0
>        cText := Get_Text_From_ClipBoard()
>      else
>        Save_Text_to_ClipBoard(cMarked)
>      endif
>      if .not. Input_Dialog('Find Text:', , {440, 100}, {aText}, '~Search', , 'Cancel Text Search?', 1, .t., ID_FIND_TEXT_DIALOG)
>        cText := ''
>      endif
>    endif
>    if .not. empty(cText)
>      ::cFindText := cText
>      nPos := at(upper(cText), upper(::EditBuffer()), nPos + 1)
>      if nPos == 0
>        nPos := at(upper(cText), upper(::EditBuffer()))
>      endif
>      if nPos > 0
>        ::nLastPos    := nPos
>        ::nLastTopPos := ::CharFromLine(max(1, ::LineFromChar(nPos) - int(nLines / 3)))
>        ::Set_Cursor(nPos, nPos + len(cText))
>      endif
>    endif
> return (Self)
> 
> *******************************
> * AGP_Text: Set_Cursor Method *
> *******************************
> Method AGP_Text:Set_Cursor(nPos, nMark)
> LOCAL nLines   := int(::CurrentSize()[2] / ::SetFont():Height)
> LOCAL nMarkPos := iif(empty(nMark), nPos, nMark)
>    ::nLastPos := nPos
>    if (nPos < ::QueryFirstChar()) .or. (::LineFromChar(nPos) >= (nLines + ::LineFromChar(::QueryFirstChar())))
>      ::nLastTopPos := nPos
>    endif
>    ::aLastMarked := {nPos, nMarkPos}
>    ::Restore_Cursor()
> return (::Pos() == nPos)
> 
> ***********************************
> * AGP_Text: Restore_Cursor Method *
> ***********************************
> Method AGP_Text:Restore_Cursor()
> LOCAL aMarked  := ::aLastMarked
> LOCAL nCurTop  := max(1, ::nLastTopPos)
> LOCAL nCurPos  := max(1, ::nLastPos)
> LOCAL nMaxPos  := max(1, len(::EditBuffer()) + iif(Set(_SET_INSERT), 1, 0))
> LOCAL nCols    := int(::CurrentSize()[1] / ::SetFont():Width)
> LOCAL nCurCol  := nCurPos - ::CharFromLine(::LineFromChar(nCurPos))
> LOCAL nOffset  := max(0, int(nCurCol - (nCols / 2)))
> LOCAL nTopLine := ::LineFromChar(nCurTop)
> LOCAL nCurLine := ::LineFromChar(nCurPos)
>    ************************************************************************
>    * Position the Cursor at the Last active Position in the Input Buffer, *
>    * display the same part of the Input Buffer as was shown previously,   *
>    * and mark the same Text that was previously marked (if any).          *
>    ************************************************************************
>    if empty(::EditBuffer())
>      aMarked := {1, 1}
>    elseif nCurPos > nMaxPos
>      aMarked := {nMaxPos, nMaxPos}
>    endif
>    if ::WordWrap .or. .not. ::HorizScroll
>      ::SetFirstChar(nCurTop)
>    else
>      while nTopLine < nCurLine
>        if abs(::CharFromLine(nTopLine) - ::CharFromLine(nTopLine + 1)) >= nOffset
>          ::SetFirstChar(::CharFromLine(nTopLine) + nOffset) ; exit
>        else
>          nTopLine++
>        endif
>      enddo
>      if nTopLine == nCurLine
>        ::SetFirstChar(::CharFromLine(nCurLine) + nOffset)
>      endif
>    endif
>    ::SetMarked(aMarked)
> return (Self)
> 
> Hope that helps,
> 
> Andreas
>
Andreas Gehrs-Pahl
Re: XbpMle Question
on Thu, 18 Mar 2021 14:04:36 -0400
Carlos,

>I think that at(upper(cText), upper(::EditBuffer()), nPos + 1) will 
>return a position considering the CRLF existing in ::EditBuffer().
>If that is true, may the method ::LineFromChar(nPos) return unreal line 
>number?

This line will check for the NEXT occurrence of the search string "cText" in 
the :EditBuffer() of the MLE, starting one character "+ 1" after the current 
(last found) position "nPos". If there is no (further) match, the next three 
lines will check for the FIRST occurrence of the search string "cText" in 
the :EditBuffer() of the MLE.

After that, "nPos" will either be 0 (Zero), if no match was found, or the 
number of the first character position of "cText" in the :EditBuffer() of 
the MLE.

If there are any CRLF characters in the :EditBuffer() or not, shouldn't make 
any difference. Also, CRLFs don't determine the actual line numbers, unless 
the ::WordWrap iVar (property) is set to FALSE and ::HorizScroll is set to 
TRUE -- or all lines are short enough to be displayed in a separate line in 
the MLE.

For more details, see the documentation for the "At()" function as well as 
for the "XbpMLE()" class.

Hope that helps,

Andreas

Andreas Gehrs-Pahl
Absolute Software, LLC

phone: (989) 723-9927
email: Andreas@AbsoluteSoftwareLLC.com
web:   http://www.AbsoluteSoftwareLLC.com
[L]:   https://www.LinkedIn.com/in/AndreasGehrsPahl
[F]:   https://www.FaceBook.com/AbsoluteSoftwareLLC
Carlos A Beling Re: XbpMle Question
on Thu, 18 Mar 2021 15:52:17 -0300
Hi Andreas:
Good afternoon.
Many thanks again.

Fraternally
Beling



On 18/03/2021 15:04, Andreas Gehrs-Pahl wrote:
> Carlos,
> 
>> I think that at(upper(cText), upper(::EditBuffer()), nPos + 1) will
>> return a position considering the CRLF existing in ::EditBuffer().
>> If that is true, may the method ::LineFromChar(nPos) return unreal line
>> number?
> 
> This line will check for the NEXT occurrence of the search string "cText" in
> the :EditBuffer() of the MLE, starting one character "+ 1" after the current
> (last found) position "nPos". If there is no (further) match, the next three
> lines will check for the FIRST occurrence of the search string "cText" in
> the :EditBuffer() of the MLE.
> 
> After that, "nPos" will either be 0 (Zero), if no match was found, or the
> number of the first character position of "cText" in the :EditBuffer() of
> the MLE.
> 
> If there are any CRLF characters in the :EditBuffer() or not, shouldn't make
> any difference. Also, CRLFs don't determine the actual line numbers, unless
> the ::WordWrap iVar (property) is set to FALSE and ::HorizScroll is set to
> TRUE -- or all lines are short enough to be displayed in a separate line in
> the MLE.
> 
> For more details, see the documentation for the "At()" function as well as
> for the "XbpMLE()" class.
> 
> Hope that helps,
> 
> Andreas
>