Page 1 of 1

How to find text within a multi-line rich edit wxTextCtrl?

Posted: Wed Sep 12, 2007 2:40 am
by Kenneth Camargo
Hi everybody. I'm new to wxWidgets, please be patient...

Using mingw gcc 3.4.2, Win XP, wxWidgets 2.8.4

I tried going to the API doing this:

HWND handle = (HWND) ctrl->GetHandle();
FINDTEXT ft;
LONG retval;

ft.lpstrText = wxStringBuffer(text,text.Len());
// "text" is a wxString with the text to search for

ft.chrg.cpMin = from;
ft.chrg.cpMax = to;

options |= 1;
// "options" is the WPARAM, meant to be or'ed with one of
// the following
// FR_DOWN 1
// FR_MATCHCASE 4
// FR_WHOLEWORD 2

retval = ::SendMessage(handle, EM_FINDTEXT, options, (LPARAM)&ft);

No matter what I do, retval is set to -1, which means the text wasn't found.

The new wxRichTextCtrl isn't suitable for what I need, since being able to read rtf files is an absolute need for me...

I really need this to port an app to wxWidgets, I feel that I hit a wall, any help will be greatly appreciated.

Thanks in advance,
Ken

Posted: Wed Sep 12, 2007 6:01 pm
by protocol
It's probably best that you use wxWidget classes (wxTextCtrl & wxString) to find the text.

best regards.

Posted: Wed Sep 12, 2007 7:21 pm
by Kenneth Camargo
protocol wrote:It's probably best that you use wxWidget classes (wxTextCtrl & wxString) to find the text.

best regards.
Thanks for the answer, but that's how I got there in the first place... :) I can't find a way to locate a substring within a wxTextCtrl, I'd appreciate any suggestions in that regard.

Ken

Posted: Wed Sep 12, 2007 8:13 pm
by Kenneth Camargo
Kenneth Camargo wrote:
protocol wrote:It's probably best that you use wxWidget classes (wxTextCtrl & wxString) to find the text.

best regards.
Thanks for the answer, but that's how I got there in the first place... :) I can't find a way to locate a substring within a wxTextCtrl, I'd appreciate any suggestions in that regard.

Ken
And an addendum: I managed to extract the text from the control and place it a wxString, where I can use Find() to search for the first occurrence of the character sequence. But I really need to find repeated occurrences of that sequence within the wxString...

Still hoping from some help,
Ken

Posted: Wed Sep 19, 2007 2:52 pm
by protocol
For repeated matches, you can continuously call wxString::find('TEXT', FIND_POS), until it stops returning a valid result or until the end of the string.

http://www.wxwidgets.org/manuals/stable ... wxstringat

Code: Select all

    // All find() functions take the nStart argument which specifies the
    // position to start the search on, the default value is 0. All functions
    // return npos if there were no match.

    // find a substring
  size_t find(const wxString& str, size_t nStart = 0) const;
best regards.

Posted: Sat Sep 22, 2007 3:07 am
by Kenneth Camargo
protocol wrote:For repeated matches, you can continuously call wxString::find('TEXT', FIND_POS), until it stops returning a valid result or until the end of the string.

http://www.wxwidgets.org/manuals/stable ... wxstringat

Code: Select all

    // All find() functions take the nStart argument which specifies the
    // position to start the search on, the default value is 0. All functions
    // return npos if there were no match.

    // find a substring
  size_t find(const wxString& str, size_t nStart = 0) const;
best regards.
Hi - I still have to copy the contents of the control into a wxString, I'm afraid this might be a problem if the size of the text is too big - isn't there a way to retrieve that information without dumping all the data from the control to another buffer?

And thanks for the hint,
Ken

Posted: Mon Sep 24, 2007 3:59 am
by protocol
wxTextCtrl has no built-in 'Find' functions that I know about.

Check it out: http://www.wxwidgets.org/manuals/stable ... wxtextctrl

Code: Select all

int position = myTextCtrl.GetValue().find('this_text', start_pos)
You may just want to edit the source (of wxTextCtrl) and implement your own 'FindText' method.

best regards.

Posted: Mon Sep 24, 2007 4:18 am
by Kenneth Camargo
protocol wrote:wxTextCtrl has no built-in 'Find' functions that I know about.

Check it out: http://www.wxwidgets.org/manuals/stable ... wxtextctrl

Code: Select all

int position = myTextCtrl.GetValue().find('this_text', start_pos)
You may just want to edit the source (of wxTextCtrl) and implement your own 'FindText' method.

best regards.
Thanks once again. That still copies the text, I think, but that'll have to do for the moment, I'm too much of a newbie to dare tweaking the internals...

Regards,
Ken

Posted: Mon Sep 24, 2007 7:20 pm
by protocol
You're welcome. If this closes the question please 'Accept' the respective answer/post.

best regards.

Posted: Mon Sep 24, 2007 7:56 pm
by Kenneth Camargo
protocol wrote:You're welcome. If this closes the question please 'Accept' the respective answer/post.

best regards.
Just did, sorry for the oversight. That's what lack of sleep does to people. :)

Ken

Posted: Tue Mar 10, 2009 5:24 am
by closer
Hi all,

I'm trying to do similar function (search in multiline wxTextCtrl) recently, and I encountered the "\n" vs "\r\n" problem. Though I have a solution, but I don't think it's a perfect one. Now I'll list what I have known, and if you have any good ideas, please kindly share your comments.

(The environment I'm using: WinXP, VC++ Express 2008, wxWidgets 2.8.9)

The goal is:
  1. Search for a phrase from the insertion point to the end of a wxTextCtrl object.
  2. If found, move the insertion point to the phrase and select it.
  3. If not found, show message in status bar.
And my original program looked like this:

Code: Select all

        // Search text from the insertion point to the end.
        start = m_textCtrl->GetInsertionPoint();
        result = m_textCtrl->GetRange( start, m_textCtrl->GetLastPosition()).find( strTarget.c_str(), 0);

        if ( result != wxNOT_FOUND)
        {
            result += start;			// 'result' is an offset from 'start', so 'start' have to be added back
            m_textCtrl->SetFocus();
            m_textCtrl->SetInsertionPoint( result);
            m_textCtrl->SetSelection( result, result + strTarget.Length());
        }
        else
        {
            m_statusBar1->SetStatusText( wxT("Not found."));
        }
Then I found that this doesn't work. The selected text is a few chars ahead the actual target if new-line is encountered.

I looked up the manual and realized that it's the \r\n problem. Then I did some tests to check how wxTextCtrl member functions deal with the new line characters:
  • Get/SetInsertionPoint() : Treats new-line as \r\n.
  • GetLastPosition() : Treats new-line as \r\n.
  • GetRange() : Treats new-line as \r\n when calculating the start/end position, but the wxString returned contains \n only.
  • SetSelection() : Treats new-line as \r\n.
The problem occurs because I use the index returned from wxString::find() to feed into SetInsertionPoint() and SetSelection(). Since the text format is different, the index cannot be used by each other.

I found a work-around after digging into the implementation of wxTextCtrl::GetRange():

Code: Select all

        start = m_textCtrl->GetInsertionPoint();

		// manually translate the wxString object returned from GetRange() into DOS format
        wxString tmp = wxTextFile::Translate( m_textCtrl->GetRange( start, m_textCtrl->GetLastPosition()), wxTextFileType_Dos);

        result = tmp.find( strTarget.c_str(), 0);

		// skipped...
It's workable, but I don't think it's a good solution because:
  1. The wxTextFile::Translate() is undocumented.
  2. It's not portable.
  3. The string has to be copied/translated redaundantly.
    (There is already a translation from DOS to Unix inside GetRange(), but I have to translate it back....)
IMHO, the best way to solve this problem is to modify the member functions of wxTextCtrl. My suggestions are:
  1. Create a new member function wxTextCtrl::Find() who returns index that can be used by other member functions,
  2. wxTextCtrl::GetRange() should return text with \r\n so that all member functions uses the same index system, or
  3. All member functions should treats new-line as \n since the internal data is so. (the most portable way)
If you have better ideas, please kindly share with every body.

Posted: Sun Mar 15, 2009 4:28 am
by protocol
closer: I use multi-line text searching in QuRegExmm. Check out the source code on sourceforge.

Posted: Mon Mar 16, 2009 10:14 am
by closer
protocol wrote:closer: I use multi-line text searching in QuRegExmm. Check out the source code on sourceforge.
protocol,

I downloaded QuRegExmm and looked into the sources. I found that you used wxRegEx in QuRegExmm, so I tried the similar way in my code. Unfortunately, the result is still the same.

However, when I tried to implement the highlight function by turning on the wxTE_RICH flag, suddenly it worked -- whether wxRegEx or wxString::find() was used. In this case, all functions I described in the last post treats newline as \n, no more \n vs \r\n problems now!

I haven't traced into wxTextCtrl again yet, but it seems that different methods are applied when simple text control or rich-edit is used... Well, it's a little confusing to me. (Is it a bug or a feature?)

Thanks for your sources anyway.