API и FreeBasic. (Операции над текстом в RichEdit)

В этом туториале вы узнаете больше о операциях над текстом, доступных в RichEdit, например о том, как искать/заменять текст и переходить к определенной строке.

Поиск текста

В RichEdit есть несколько текстовых операций. Поиск определенного текста - одна из них. Поиск текста осуществляется с помощью сообщений EM_FINDTEXT или EM_FINDTEXTEX. Эти сообщения мало отличаются друг от друга.

EM_FINDTEXT

  wParam == опции поиска.

  Может быть комбинацией значений, приведенных ниже. Эти опции идентичны как для EM_FINDTEXT, так и для EM_FINDTEXTEX.

  • FR_DOWN - если этот флаг указан, поиск начинается с конца текущего выделенного текста и до самого конца (вперед). Этот флаг действует только в RichEdit 2.0 или выше: этот способ применяется по умолчанию в RichEdit 1.0. В RichEdit 2.0 по умолчанию поиск осуществляется от конца выделенного текста до начала (назад). Кратко говоря, если вы используете RichEdit 1.0, вы ничего не можете сделать, чтобы изменить направление поиска: он всегда ищет вперед. Если вы используете RichEdit 2.0 и хотите искать вперед, вам нужно указать этот флаг, иначе поиск будет производиться назад.
  • FR_MATCHCASE - если этот флаг указан, будет учитываться регистр.
  • FR_WHOLEWORD - если этот флаг указан, поиск будет искать место в тексте, которое будет удовлетворять указанной поисковой строке.

Вообще-то, есть еще несколько флагов, но они относятся к языкам, отличным от английского.

  lParam == указатель на структуру FINDTEXT.

Type FINDTEXTA field=4
    chrg As CHARRANGE
    lpstrText As LPSTR
End Type

 chrg - это структура CHARRANGE, которая определена следующим образом:

Type CHARRANGE field=4
    cpMin As Long
    cpMax As Long
End Type

cpMin содержит индекс первого символа в массиве символов (диапазон).

cpMax содержит индекс символа, который следует непосредственно за последним символов в массиве символов.

Фактически, чтобы найти строку текста, вам нужно указать диапазон символом, в котором нужно искать. Значение cpMin и cpMax будут зависеть от того, проводится ли поиск назад или вперед. Если поиск идет вперед, cpMin задает начальный индекс, а cpMax - конечный. Если поиск идет назад, тогда cpMin содержит конечное значение индекса, в то время как cpMax задает начальный индекс.

lpstrText - это указатель на текстовую строку, которую нужно искать.

EM_FINDTEXT возвращает индекс первого символа в заданной текстовой строке в RichEdit. Оно возвращает -1, если указанный текст не был найден.

EM_FINDTEXTEX

wParam == опции поиска. То же самое, что и EM_FINDTEXT. lParam == указатель на структуру FINDTEXTEX.

Type FINDTEXTEXA field=4
    chrg As CHARRANGE
    lpstrText As LPSTR
    chrgText As CHARRANGE
End Type

Первые два члена FINDTEXTEX идентичны соответствующим полям в структуре FINDTEXT. chrgText - это структура CHARRANGE, которая будет заполнена начальным и конечным индексами, если будут найдены какие-либо совпадения.

Возвращаемое значение EM_FINDTEXTEX - то же самое, что и у EM_FINDTEXT.

  Разница между EM_FINDTEXT и EM_FINDTEXTEXT - это то, что у структуры FINDTEXTEX есть дополнительное поле, chrgText, которое будет заполнено начальным и конечным индексами, если будут найдены совпадения. Это удобно, если мы хотим иметь возможность осуществлять больше текстовых операций над строкой.

Замещение/вставка текста

Контрол RichEdit предоставляет EM_SETTEXTEX для замещения/вставки текста. Это сообщение комбинирует функциональность WM_SETTEXT и WM_REPLACESEL. У него следующий синтаксис:

EM_SETTEXTEXT wParam == указатель на структуру SETTEXTEX.

Type SETTEXTEX
    flags As DWORD
    codepage As UINT
End Type

Поле 'flags' может быть комбинацией следующих значений:

  • ST_DEFAULT - удаляет стек совершенных операций, сбрасывает форматирование RichEdit.
  • ST_KEEPUNDO - сохраняет стек совершенных операций.
  • ST_SELECTION - замещает выделенный текст и сохраняет форматирование.

Поле 'codepage' - это константа, которая указывает кодовую страницу. Обычно мы указываем CP_ACP.

Выделение текста

Мы можем выделить текст программно с помощью сообщений EM_SETSEL или EM_EXSETSEL. Обе прекрасно работают. Выбирать, какое из сообщений необходимо использовать, зависит от доступного формата индексов символов. Если они уже сохранены в структуры CHARRANGE, проще использовать EM_EXSETSEL.

EM_EXSETSEL

wParam == не используется. Должен быть равен 0. lParam == указатель на структуру CHARRANGE, который содержит диапазон символов, который необходимо выделить.

Уведомительные события

В случае с многолинейным edit control'ом вам необходимо сабклассировать его, чтобы получить входные сообщения, такие как события мыши/клавиатуры. RichEdit предоставляет лучший способ: он будет уведомлять родительское окно о таких событиях. Чтобы указать RichEdit, какие события нужно посылать, родительское окно посылает сообщение EM_SETEVENTMASK контролу RichEdit, указывая, в каких событиях он заинтересован. У EM_SETEVENTMASK следующий синтаксис:

EM_SETEVENTMASK

wParam == не используется. Должен быть равен нулю. lParam == маска событий. Это должна быть комбинация флагов, указанных ниже.

  • ENM_CHANGE - прием уведомлений EN_CHANGE.
  • ENM_CORRECTTEXT - прием уведомлений EN_CORRECTTEXT.
  • ENM_DRAGDROPDONE - прием уведомлений EN_DRAGDROPDONE.
  • ENM_DROPFILES - прием уведомлений EN_DROPFILES.
  • ENM_KEYEVENTS - прием уведомлений EN_MSGFILTER, относящихся к событиям от клавиатуры.
  • ENM_LINK - RichEdit 2.0 и выше: прием уведомлений EN_LINK, когда курсор мыши находится над текстом, который принимает CFE_LINK и/или другие события от мыши.
  • ENM_MOUSEEVENTS - прием уведомлений EN_MSGFILTER, относящихся к событиям от мыши.
  • ENM_OBJECTPOSITIONS - прием уведомлений EN_OBJECTPOSITIONS.
  • ENM_PROTECTED - прием уведомлений EN_PROTECTED.
  • ENM_REQUESTRESIZE - прием уведомлений EN_REQUESTRESIZE.
  • ENM_SCROLL - прием уведомлений EN_HSCROLL и EN_VSCROLL.
  • ENM_SCROLLEVENTS - прием уведомлений EN_MSGFILTER от колесика мыши.
  • ENM_SELCHANGE - прием уведомлений EN_SELCHANGE.
  • ENM_UPDATE - прием уведомлений EN_UPDATE. RichEdit 2.0 и выше: этот флаг игнорирует, а сообщения EN_UPDATE отсылаются всегда. Тем не менее, если Rich Edit 3.0 эмулирует Rich Edit 1.0, вы должны указать этот флаг для того, чтобы родительское окно принимало уведомления EN_UPDATE.

Все вышеуказанные уведомления будут отсылаться через сообщение WM_NOTIFY: вы должны проверить поле 'code' структуры NMHDR, чтобы узнать, какое уведомление вы получили. Например, если вы хотите зарегистрировать сообщения от мыши (скажем, чтобы отображать контекстное меню по нажатию на правую кнопку мыши), вы должны сделать что-то вроде следующего:

SendMessage(hwndRichEdit,EM_SETEVENTMASK, _
                   0,ENM_MOUSEEVENTS)
.....
.....
Function wndproc(hwnd As HWND, uMsg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer
.....
....
        Case WM_NOTIFY
            Dim nmhdr_ As NMHDR Ptr = Cast (NMHDR Ptr,lParam)
            If nmhdr_->code=EN_MSGFILTER Then
          ....
          [ Do something here]
          ....
            Endif

ПРИМЕР

Следующий пример является обновлением IczEdit. Были добавлены возможности поиска/замещения и акселераторы. Также обрабатываются события от мыши и отображается контекстное меню.

#INCLUDE Once "windows.bi"
#INCLUDE Once "win/richedit.bi"
#INCLUDE Once "win/commdlg.bi"

Declare Function WinMain (Byval hInst As HINSTANCE, _
Byval hPrevInst As HINSTANCE, _
Byval szCmdLine As LPSTR, _
Byval iCmdShow As Integer ) As Integer
Declare Function wndproc(hwnd As HWND, msg As Uinteger,_
wparam As WPARAM, lparam As LPARAM) As Integer

Enum Iczelion
IDM_OPEN=40001
IDM_SAVE
IDM_CLOSE
IDM_SAVEAS
IDM_EXIT
IDM_COPY
IDM_CUT
IDM_PASTE
IDM_DELETE
IDM_SELECTALL
IDM_OPTION
IDM_UNDO
IDM_REDO
IDR_MAINMENU=101
IDD_OPTIONDLG =101
IDD_FINDDLG
IDD_GOTODLG
IDD_REPLACEDLG
IDR_MAINACCEL
IDC_BACKCOLORBOX =1000
IDC_FINDEDIT   =1000
IDC_TEXTCOLORBOX =1001
IDC_MATCHCASE =1001
IDC_REPLACEEDIT =1001
IDC_WHOLEWORD
IDC_DOWN
IDC_UP
IDC_LINENO
IDM_FIND =40014
IDM_FINDNEXT
IDM_REPLACE
IDM_GOTOLINE
IDM_FINDPREV
RichEditID =300
End Enum


Dim Shared As String ClassName,AppName,RichEditDLL,RichEditClass, _
NoRichEdit,ASMFilterString,OpenFileFail,WannaSave, _
ASMSection,ZeroString

ClassName = "IczEditClass"
AppName = "IczEdit version 2.0"
RichEditDLL = "riched20.dll"
RichEditClass = "RichEdit20A"
NoRichEdit = "Cannot find riched20.dll"
ASMFilterString = "Text files (*.txt, *.ini, *.doc ,*.asm)"_
+Chr(0)+"*.txt;*.ini;*.doc;*.asm"+Chr(0)
OpenFileFail = "Cannot open the file"
WannaSave = "The data in the control is modified. Want to save it?"
ASMSection = "ASSEMBLY"
ZeroString = Chr(0)

Dim Shared As Integer BackgroundColor = &hFFFFFF , _
TextColor, _
FileOpened, _
CustomColors(16) , _
uFlags
Dim Shared As FINDTEXTEX FindText_


Dim Shared As HINSTANCE hInstance,hRichEdit
Dim Shared As HACCEL hAccel
Dim Shared As HWND hwndRichEdit,hSearch
Dim Shared As String*256  _
FileName , _
AlternateFileName , _
FindBuffer , _
ReplaceBuffer

'---------------Старт----------------------
hInstance=GetModuleHandle( NULL)
hRichEdit = LoadLibrary( RichEditDLL)
If hRichEdit<>0 Then
    WinMain(hInstance,0,0, SW_SHOWDEFAULT)
    FreeLibrary(hRichEdit)
Else
    MessageBox(0,NoRichEdit,AppName,MB_OK Or MB_ICONERROR)
Endif
ExitProcess(0)

Function WinMain (Byval hInst As HINSTANCE, _
    Byval hPrevInst As HINSTANCE, _
    Byval szCmdLine As LPSTR, _
    Byval iCmdShow As Integer ) As Integer
    Dim As WNDCLASSEX wc
    Dim As MSG msg
    Dim As HWND hwnd
    wc.cbSize = Sizeof(WNDCLASSEX)
    wc.style = CS_HREDRAW Or CS_VREDRAW
    wc.lpfnWndProc = @wndproc
    wc.hInstance = hInst
    wc.hbrBackground = Cast(HBRUSH,COLOR_WINDOW+1)
    wc.lpszMenuName = Cast(LPSTR,IDR_MAINMENU)
    wc.lpszClassName = Strptr(ClassName)
    wc.hIcon = LoadIcon(NULL,IDI_APPLICATION)
    wc.hIconSm = wc.hIcon
    wc.hCursor  = LoadCursor(NULL,IDC_ARROW)
    If RegisterClassEx(@wc)=0 Then Return 0
    hwnd = CreateWindowEx(0,ClassName,AppName, _
    WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, _
    CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL, _
    hInst,NULL)
    ShowWindow(hwnd,SW_SHOWNORMAL)
    UpdateWindow(hwnd)
    hAccel = LoadAccelerators(hInstance,Cast(LPCSTR,IDR_MAINACCEL))

    While GetMessage(@msg,0,0,0)
        If IsDialogMessage(hSearch,@msg)=FALSE Then
            If TranslateAccelerator(hwnd,hAccel,@msg)=FALSE Then
                TranslateMessage(@msg)
                DispatchMessage(@msg)
            Endif
        Endif
    Wend
End Function

Function StreamInProc( hFile As Handle,pBuffer As PVOID, NumBytes As Integer, pBytesRead As Integer Ptr) As BOOl
    Dim As Integer length
    ReadFile(hFile,pBuffer,NumBytes,Cast(LPDWORD,@length),0)
    *pBytesRead = length
    If length = 0 Then
        Return 1
    Endif
End Function

Function StreamOutProc (hFile As Handle,pBuffer As PVOID, NumBytes As Integer, pBytesWritten As Integer Ptr) As bool
    Dim As Integer length
    WriteFile(hFile,pBuffer,NumBytes,Cast(LPDWORD,@length),0)
    *pBytesWritten = length
    If length = 0 Then
        Return 1
    Endif
End Function

Function CheckModifyState(hWnd As HWND) As BOOL
    If SendMessage(hwndRichEdit,EM_GETMODIFY,0,0)<>0 Then
        Select Case MessageBox(hWnd,WannaSave,AppName,MB_YESNOCANCEL)
            Case IDYES
                SendMessage(hWnd,WM_COMMAND,IDM_SAVE,0)
            Case IDCANCEL
                Return FALSE
        End Select
    Endif
    Return TRUE
End Function



Sub SetColor()
    Dim cfm As CHARFORMAT
    SendMessage(hwndRichEdit,EM_SETBKGNDCOLOR,0,BackgroundColor)
    RtlZeroMemory(@cfm,Sizeof(cfm))
    cfm.cbSize = Sizeof(cfm)
    cfm.dwMask = CFM_COLOR
    cfm.crTextColor = TextColor
    SendMessage(hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,Cast(LPARAM,@cfm))
End Sub



Function OptionProc(hWnd As HWND, uMsg As UINT, wParam As WPARAM, lParam As LPARAM) As Integer
    Dim clr As ChooseColor
    Select Case uMsg
        Case WM_INITDIALOG
        Case WM_COMMAND
            If Hiword(wParam) = BN_CLICKED Then
                Select Case Loword(wParam)
                    Case IDCANCEL
                        SendMessage(hWnd,WM_CLOSE,0,0)
                    Case IDC_BACKCOLORBOX
                        RtlZeroMemory(@clr,Sizeof(clr))
                        clr.lStructSize=SizeOf(clr)
                        clr.hwndOwner = hWnd
                        clr.hInstance = Cast(HWND,hInstance)
                        clr.rgbResult = BackgroundColor
                        clr.lpCustColors = @CustomColors(0)
                        clr.Flags = CC_ANYCOLOR Or CC_RGBINIT
                        If ChooseColor(@clr)<>0 Then
                            BackgroundColor = clr.rgbResult
                            InvalidateRect(GetDlgItem(hWnd,IDC_BACKCOLORBOX),0,TRUE)
                        Endif
                    Case IDC_TEXTCOLORBOX
                        RtlZeroMemory(@clr,Sizeof(clr))
                        clr.lStructSize = Sizeof(clr)
                        clr.hwndOwner = hWnd
                        clr.hInstance = Cast(HWND,hInstance)
                        clr.rgbResult = TextColor
                        clr.lpCustColors = @CustomColors(0)
                        clr.Flags = CC_ANYCOLOR Or CC_RGBINIT
                        If ChooseColor(@clr)<>0 Then
                            TextColor = clr.rgbResult
                            InvalidateRect(GetDlgItem(hWnd,IDC_TEXTCOLORBOX),0,TRUE)
                        Endif
                    Case IDOK
                        '==================================================================================
                        ' Save the modify state of the richedit control because changing the text color changes the
                        ' modify state of the richedit control.
                        '==================================================================================
                        Var GM = SendMessage(hwndRichEdit,EM_GETMODIFY,0,0)
                        SetColor()
                        SendMessage(hwndRichEdit,EM_SETMODIFY,GM,0)
                        EndDialog(hWnd,0)
                End Select
            Endif
        Case WM_CTLCOLORSTATIC
            If GetDlgItem(hWnd,IDC_BACKCOLORBOX) = lParam Then
                Return Cast(Integer,CreateSolidBrush(BackgroundColor))
            Else
                If GetDlgItem(hWnd,IDC_TEXTCOLORBOX)=lParam Then
                    Return Cast(Integer,CreateSolidBrush(TextColor))
                Endif
            Endif
            Return FALSE
        Case WM_CLOSE
            EndDialog(hWnd,0)
        Case Else
            Return FALSE
    End Select
    Return TRUE
End Function



Function SearchProc(hWnd As HWND, uMsg As UINT, wParam As WPARAM, lParam As LPARAM) As Integer
    Select Case  uMsg
        Case WM_INITDIALOG
            hSearch = hWnd
            '===================================================
            ' Select the default search down option
            '===================================================
            CheckRadioButton(hWnd,IDC_DOWN,IDC_UP,IDC_DOWN)
            SendDlgItemMessage(hWnd,IDC_FINDEDIT,WM_SETTEXT,0,Cast(LPARAM,@FindBuffer))
        Case WM_COMMAND
            If Hiword(wParam) = BN_CLICKED Then
                Select Case Loword(wParam)
                    Case IDOK
                        uFlags = 0
                        SendMessage(hwndRichEdit,EM_EXGETSEL,0,Cast(LPARAM,@findtext_.chrg))
                        If GetDlgItemText(hWnd,IDC_FINDEDIT,Cast(LPSTR,@FindBuffer),Sizeof(FindBuffer))<>0 Then
                            If IsDlgButtonChecked(hWnd,IDC_DOWN) = BST_CHECKED Then
                                uFlags Or=FR_DOWN
                                If findtext_.chrg.cpMin<>findtext_.chrg.cpMax Then
                                    findtext_.chrg.cpMin = findtext_.chrg.cpMax
                                Endif
                                findtext_.chrg.cpMax=-1
                            Else
                                findtext_.chrg.cpMax=0
                            Endif
                            If IsDlgButtonChecked(hWnd,IDC_MATCHCASE) = BST_CHECKED Then
                                uFlags Or=FR_MATCHCASE
                            Endif
                            If IsDlgButtonChecked(hWnd,IDC_WHOLEWORD) = BST_CHECKED Then
                                uFlags Or=FR_WHOLEWORD
                            Endif
                            findtext_.lpstrText = Strptr(FindBuffer)
                            If SendMessage(hwndRichEdit,EM_FINDTEXTEX,uFlags,Cast(Lparam,@findtext_))<>-1 Then
                                SendMessage(hwndRichEdit,EM_EXSETSEL,0,Cast(Lparam,@findtext_.chrgText))
                            Endif
                        Endif
                    Case IDCANCEL
                        SendMessage(hWnd,WM_CLOSE,0,0)
                    Case Else
                        Return FALSE
                End Select
            Endif
        Case WM_CLOSE
            hSearch=0
            EndDialog(hWnd,0)
        Case Else
            Return FALSE
    End Select
    Return TRUE
End Function

Function ReplaceProc(hWnd As HWND, uMsg As UINT, wParam As WPARAM, lParam As LPARAM) As Integer
    Dim settext As SETTEXTEX
    Select Case  uMsg
        Case WM_INITDIALOG
            hSearch = hWnd
            SetDlgItemText(hWnd,IDC_FINDEDIT,Cast(LPCTSTR,@FindBuffer))
            SetDlgItemText(hWnd,IDC_REPLACEEDIT,Cast(LPCTSTR,@ReplaceBuffer))
        Case WM_COMMAND
            If Hiword(wParam) = BN_CLICKED Then
                Select Case Loword(wParam)
                    Case IDCANCEL
                        SendMessage(hWnd,WM_CLOSE,0,0)
                    Case IDOK
                        GetDlgItemText(hWnd,IDC_FINDEDIT,Cast(LPCTSTR,@FindBuffer),Sizeof(FindBuffer))
                        GetDlgItemText(hWnd,IDC_REPLACEEDIT,Cast(LPCTSTR,@ReplaceBuffer),Sizeof(ReplaceBuffer))
                        findtext_.chrg.cpMin = 0
                        findtext_.chrg.cpMax = -1
                        findtext_.lpstrText = Strptr(FindBuffer)
                        settext.flags = ST_SELECTION
                        settext.codepage = CP_ACP
                        While TRUE
                            If  SendMessage(hwndRichEdit,EM_FINDTEXTEX,FR_DOWN,Cast(LPARAM,@FindText_)) = -1 Then
                                Exit While
                            Else
                                SendMessage(hwndRichEdit,EM_EXSETSEL,0,Cast(LPARAM,@findtext_.chrgText))
                                SendMessage(hwndRichEdit,EM_SETTEXTEX,Cast(WPARAM,@settext),Cast(LPARAM,@ReplaceBuffer))
                            Endif
                        Wend
                End Select
            Endif
        Case WM_CLOSE
            hSearch=0
            EndDialog(hWnd,0)
        Case Else
            Return FALSE
    End Select
    Return TRUE
End Function

Function GoToProc (hWnd As HWND, uMsg As UINT, wParam As WPARAM, lParam As LPARAM) As Integer
    Dim LineNo As Integer
    Dim chrg As CHARRANGE
    Select Case uMsg
        Case WM_INITDIALOG
            hSearch = hWnd
        Case WM_COMMAND
            If Hiword(wParam) = BN_CLICKED Then
                Select Case Loword(wParam)
                    Case IDCANCEL
                        SendMessage(hWnd,WM_CLOSE,0,0)
                    Case IDOK
                        LineNo = GetDlgItemInt(hWnd,IDC_LINENO,NULL,FALSE)
                        Var GM = SendMessage(hwndRichEdit,EM_GETLINECOUNT,0,0)
                        If GM > LineNo Then
                            Var GM = SendMessage(hwndRichEdit,EM_LINEINDEX,LineNo,0)
                            SendMessage(hwndRichEdit,EM_SETSEL,GM,GM)
                            SetFocus(hwndRichEdit)
                        Endif
                End Select
            Endif
        Case    WM_CLOSE
            hSearch = 0
            EndDialog(hWnd,0)
        Case Else
            Return FALSE
    End Select
    Return TRUE
End Function

Function PrepareEditMenu(hSubMenu As HMENU) As Integer
    Dim chrg As CHARRANGE
    '=============================================================================
    ' Check whether there is some text in the clipboard. If so, we enable the paste menuitem
    '=============================================================================
    If SendMessage(hwndRichEdit,EM_CANPASTE,CF_TEXT,0)=0 Then
        EnableMenuItem(hSubMenu,IDM_PASTE,MF_GRAYED)
    Else
        EnableMenuItem(hSubMenu,IDM_PASTE,MF_ENABLED)
    Endif
    '==========================================================
    ' check whether the undo queue is empty
    '==========================================================
    If SendMessage(hwndRichEdit,EM_CANUNDO,0,0) = 0 Then
        EnableMenuItem(hSubMenu,IDM_UNDO,MF_GRAYED)
    Else
        EnableMenuItem(hSubMenu,IDM_UNDO,MF_ENABLED)
    Endif
    '=========================================================
    ' check whether the redo queue is empty
    '=========================================================
    If SendMessage(hwndRichEdit,EM_CANREDO,0,0)  = 0 Then
        EnableMenuItem(hSubMenu,IDM_REDO,MF_GRAYED)
    Else
        EnableMenuItem(hSubMenu,IDM_REDO,MF_ENABLED)
    Endif
    '=========================================================
    ' check whether there is a current selection in the richedit control.
    ' If there is, we enable the cut/copy/delete menuitem
    '=========================================================
    SendMessage(hwndRichEdit,EM_EXGETSEL,0,Cast(Lparam,@chrg))
    If chrg.cpMin = chrg.cpMax Then ' no current selection
        EnableMenuItem(hSubMenu,IDM_COPY,MF_GRAYED)
        EnableMenuItem(hSubMenu,IDM_CUT,MF_GRAYED)
        EnableMenuItem(hSubMenu,IDM_DELETE,MF_GRAYED)
    Else
        EnableMenuItem(hSubMenu,IDM_COPY,MF_ENABLED)
        EnableMenuItem(hSubMenu,IDM_CUT,MF_ENABLED)
        EnableMenuItem(hSubMenu,IDM_DELETE,MF_ENABLED)
    Endif
    Return 0
End Function

Function wndproc(hwnd As HWND, uMsg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer
    Dim  ofn As OPENFILENAME
    Dim editstream As EDITSTREAM
    Dim hFile As HANDLE
    Dim hPopup As HMENU
    Dim pt As Point
    Dim chrg As CHARRANGE
    Select Case uMsg
        Case WM_CREATE
            hwndRichEdit = CreateWindowEx(WS_EX_CLIENTEDGE,RichEditClass,0,WS_CHILD Or WS_VISIBLE Or ES_MULTILINE Or WS_VSCROLL Or WS_HSCROLL Or ES_NOHIDESEL, _
            CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,hWnd,Cast(HMENU,RichEditID),hInstance,0)
            ' Set the text limit. The default is 64K
            '=============================================================
            SendMessage(hwndRichEdit,EM_LIMITTEXT,-1,0)
            '=============================================================
            ' Set the default text/background color
            '=============================================================
            SetColor()
            SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0)
            '============================================================
            ' set event mask
            '============================================================
            SendMessage(hwndRichEdit,EM_SETEVENTMASK,0,ENM_MOUSEEVENTS)
            SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0)
        Case WM_NOTIFY
            Dim nmhdr_ As NMHDR Ptr = Cast (NMHDR Ptr,lParam)
            If nmhdr_->code=EN_MSGFILTER Then
                Dim msgfilter_ As MSGFILTER Ptr = Cast (MSGFILTER Ptr,lParam)
                If msgfilter_->msg=WM_RBUTTONDOWN Then
                    hPopup = GetSubMenu(GetMenu(hWnd),1)
                    PrepareEditMenu(hPopup)
                    pt.x = Loword(msgfilter_->lParam)
                    pt.y = Hiword(msgfilter_->lParam)
                    ClientToScreen(hWnd,@pt)
                    TrackPopupMenu(hPopup,TPM_LEFTALIGN Or TPM_BOTTOMALIGN,pt.x,pt.y,NULL,hWnd,NULL)
                Endif
            Endif
        Case WM_INITMENUPOPUP
            Select Case Loword(LPARAM)
                Case 0
                    If FileOpened=TRUE  Then' a file is already opened
                        EnableMenuItem(Cast(HMENU,wParam),IDM_OPEN,MF_GRAYED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_CLOSE,MF_ENABLED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_SAVE,MF_ENABLED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_SAVEAS,MF_ENABLED)
                    Else
                        EnableMenuItem(Cast(HMENU,wParam),IDM_OPEN,MF_ENABLED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_CLOSE,MF_GRAYED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_SAVE,MF_GRAYED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_SAVEAS,MF_GRAYED)
                    Endif
                Case 1  ' edit menu
                    PrepareEditMenu(Cast(HMENU,wParam))
                Case 2      'search menu bar
                    If FileOpened=TRUE Then
                        EnableMenuItem(Cast(HMENU,wParam),IDM_FIND,MF_ENABLED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_FINDNEXT,MF_ENABLED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_FINDPREV,MF_ENABLED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_REPLACE,MF_ENABLED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_GOTOLINE,MF_ENABLED)
                    Else
                        EnableMenuItem(Cast(HMENU,wParam),IDM_FIND,MF_GRAYED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_FINDNEXT,MF_GRAYED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_FINDPREV,MF_GRAYED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_REPLACE,MF_GRAYED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_GOTOLINE,MF_GRAYED)
                    Endif
            End Select
        Case WM_COMMAND
            If lParam=0 Then    ' menu commands
                Select Case Loword(WPARAM)
                    Case IDM_OPEN
                        RtlZeroMemory(@ofn,Sizeof(ofn))
                        ofn.lStructSize = Sizeof(ofn)
                        ofn.hwndOwner = hWnd
                        ofn.hInstance = hInstance
                        ofn.lpstrFilter = Strptr(ASMFilterString)
                        ofn.lpstrFile = Strptr(FileName)
                        FileName = ""
                        ofn.nMaxFile = Sizeof(FileName)
                        ofn.Flags = OFN_FILEMUSTEXIST Or OFN_HIDEREADONLY Or OFN_PATHMUSTEXIST
                        If GetOpenFileName(@ofn)<>0 Then
                            hFile = CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)
                            If hFile <> INVALID_HANDLE_VALUE Then
                                '================================================================
                                ' stream the text into the richedit control
                                '================================================================
                                editstream.dwCookie = Cast(DWORD,hFile)
                                editstream.pfnCallback= Cast(EDITSTREAMCALLBACK,@StreamInProc)
                                SendMessage(hwndRichEdit,EM_STREAMIN,SF_TEXT,Cast(LPARAM,@editstream))
                                '==========================================================
                                ' Initialize the modify state to false
                                '==========================================================
                                SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0)
                                CloseHandle(hFile)
                                FileOpened = TRUE
                            Else
                                MessageBox(hWnd,OpenFileFail,AppName,MB_OK Or MB_ICONERROR)
                            Endif
                        Endif
                    Case IDM_CLOSE
                        If CheckModifyState(hWnd) = TRUE Then
                            SetWindowText(hwndRichEdit,"")
                            FileOpened = FALSE
                        Endif
                    Case IDM_SAVE
                        hFile = CreateFile(FileName,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
                        If hFile<>INVALID_HANDLE_VALUE Then
                            '================================================================
                            ' stream the text to the file
                            '================================================================
                            editstream.dwCookie = Cast(DWORD,hFile)
                            editstream.pfnCallback= Cast(EDITSTREAMCALLBACK,@StreamOutProc)
                            SendMessage(hwndRichEdit,EM_STREAMOUT,SF_TEXT,Cast(LPARAM,@editstream))
                            '==========================================================
                            ' Initialize the modify state to false
                            '==========================================================
                            SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0)
                            CloseHandle(hFile)
                        Else
                            MessageBox(hWnd,OpenFileFail,AppName,MB_OK Or MB_ICONERROR)
                        Endif
                    Case IDM_COPY
                        SendMessage(hwndRichEdit,WM_COPY,0,0)
                    Case IDM_CUT
                        SendMessage(hwndRichEdit,WM_CUT,0,0)
                    Case IDM_PASTE
                        SendMessage(hwndRichEdit,WM_PASTE,0,0)
                    Case IDM_DELETE
                        SendMessage(hwndRichEdit,EM_REPLACESEL,TRUE,0)
                    Case IDM_SELECTALL
                        chrg.cpMin = 0
                        chrg.cpMax = -1
                        SendMessage(hwndRichEdit,EM_EXSETSEL,0,Cast(LPARAM,@chrg))
                    Case IDM_UNDO
                        SendMessage(hwndRichEdit,EM_UNDO,0,0)
                    Case IDM_REDO
                        SendMessage(hwndRichEdit,EM_REDO,0,0)
                    Case IDM_OPTION
                        DialogBoxParam(hInstance,Cast(LPCTSTR,IDD_OPTIONDLG),hWnd,@OptionProc,0)
                    Case IDM_SAVEAS
                        RtlZeroMemory(@ofn,Sizeof(ofn))
                        ofn.lStructSize = Sizeof(ofn)
                        ofn.hwndOwner = hWnd
                        ofn.hInstance = hInstance
                        ofn.lpstrFilter = Strptr(ASMFilterString)
                        ofn.lpstrFile = Strptr(AlternateFileName)
                        AlternateFileName = ""
                        ofn.nMaxFile = Sizeof(AlternateFileName)
                        ofn.Flags = OFN_FILEMUSTEXIST Or OFN_HIDEREADONLY Or OFN_PATHMUSTEXIST
                        If GetSaveFileName(@ofn)<>0 Then
                            hFile = CreateFile(AlternateFileName,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
                            If hFile<>INVALID_HANDLE_VALUE Then
                                '================================================================
                                ' stream the text to the file
                                '================================================================
                                editstream.dwCookie = Cast(DWORD,hFile)
                                editstream.pfnCallback= Cast(EDITSTREAMCALLBACK,@StreamOutProc)
                                SendMessage(hwndRichEdit,EM_STREAMOUT,SF_TEXT,Cast(LPARAM,@editstream))
                                '==========================================================
                                ' Initialize the modify state to false
                                '==========================================================
                                SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0)
                                CloseHandle(hFile)
                            Endif
                        Endif
                    Case IDM_FIND
                        If hSearch=0 Then
                            CreateDialogParam(hInstance,Cast(LPCTSTR,IDD_FINDDLG),hWnd,@SearchProc,0)
                        Endif
                    Case IDM_REPLACE
                        If hSearch=0 Then
                            CreateDialogParam(hInstance,Cast(LPCTSTR,IDD_REPLACEDLG),hWnd,@ReplaceProc,0)
                        Endif
                    Case IDM_GOTOLINE
                        If hSearch=0 Then
                            CreateDialogParam(hInstance,Cast(LPCTSTR,IDD_GOTODLG),hWnd,@GoToProc,0)
                        Endif
                    Case IDM_FINDNEXT
                        If lstrlen(Cast(LPCTSTR, @FindBuffer))<>0 Then
                            SendMessage(hwndRichEdit,EM_EXGETSEL,0,Cast(LPARAM,@findtext_.chrg))
                            If findtext_.chrg.cpMin<>findtext_.chrg.cpMax Then
                                findtext_.chrg.cpMin = findtext_.chrg.cpMax
                            Endif
                            findtext_.chrg.cpMax = -1
                            findtext_.lpstrText = Strptr(FindBuffer)
                            If SendMessage(hwndRichEdit,EM_FINDTEXTEX,FR_DOWN,Cast(LPARAM,@FindText_))<> -1 Then
                                SendMessage(hwndRichEdit,EM_EXSETSEL,0,Cast(LPARAM,@findtext_.chrgText))
                            Endif
                        Endif
                    Case IDM_FINDPREV
                        If lstrlen(Cast(LPCTSTR,@FindBuffer))<> 0 Then
                            SendMessage(hwndRichEdit,EM_EXGETSEL,0,Cast(LPARAM,@findtext_.chrg))
                            findtext_.chrg.cpMax = 0
                            findtext_.lpstrText = Strptr(FindBuffer)
                            If SendMessage(hwndRichEdit,EM_FINDTEXTEX,0,Cast(LPARAM,@findtext_))<>-1 Then
                                SendMessage(hwndRichEdit,EM_EXSETSEL,0,Cast(LPARAM,@findtext_.chrgText))
                            Endif
                        Endif
                    Case IDM_EXIT
                        SendMessage(hWnd,WM_CLOSE,0,0)
                End Select
            Endif
        Case WM_CLOSE
            If CheckModifyState(hWnd)=TRUE Then
                DestroyWindow(hWnd)
            Endif
        Case WM_SIZE
            MoveWindow(hwndRichEdit,0,0,Loword(LPARAM),Hiword(LPARAM),TRUE)
        Case WM_DESTROY
            PostQuitMessage(NULL)
        Case Else
            Return DefWindowProc(hWnd,uMsg,wParam,lParam)
    End Select
    Return 0
End Function

Файл ресурсов:
//#include "resource.h"
#DEFINE IDC_NEXT                        1003
#DEFINE IDR_MAINMENU                    101
#DEFINE IDD_OPTIONDLG                   101
#DEFINE IDD_FINDDLG                     102
#DEFINE IDD_GOTODLG                     103
#DEFINE IDD_REPLACEDLG                  104
#DEFINE IDR_MAINACCEL                   105
#DEFINE IDC_BACKCOLORBOX                1000
#DEFINE IDC_FINDEDIT                    1000
#DEFINE IDC_TEXTCOLORBOX                1001
#DEFINE IDC_MATCHCASE                   1001
#DEFINE IDC_REPLACEEDIT                 1001
#DEFINE IDC_WHOLEWORD                   1002
#DEFINE IDC_DOWN                        1003
#DEFINE IDC_UP                          1004
#DEFINE IDC_LINENO                      1005
#DEFINE IDM_OPEN                        40001
#DEFINE IDM_SAVE                        40002
#DEFINE IDM_CLOSE                       40003
#DEFINE IDM_SAVEAS                      40004
#DEFINE IDM_EXIT                        40005
#DEFINE IDM_COPY                        40006
#DEFINE IDM_CUT                         40007
#DEFINE IDM_PASTE                       40008
#DEFINE IDM_DELETE                      40009
#DEFINE IDM_SELECTALL                   40010
#DEFINE IDM_OPTION                      40011
#DEFINE IDM_UNDO                        40012
#DEFINE IDM_REDO                        40013
#DEFINE IDM_FIND                        40014
#DEFINE IDM_FINDNEXT                    40015
#DEFINE IDM_REPLACE                     40016
#DEFINE IDM_GOTOLINE                    40017
#DEFINE IDM_FINDPREV                    40018
#DEFINE IDC_STATIC                      -1

IDR_MAINMENU MENU DISCARDABLE 
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Open",                       IDM_OPEN
        MENUITEM "&Close",                      IDM_CLOSE
        MENUITEM "&Save",                       IDM_SAVE
        MENUITEM "Save &As",                    IDM_SAVEAS
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       IDM_EXIT
    End
    POPUP "&Edit"
    BEGIN
        MENUITEM "&Undo",                       IDM_UNDO
        MENUITEM "&Redo",                       IDM_REDO
        MENUITEM "&Copy",                       IDM_COPY
        MENUITEM "C&ut",                        IDM_CUT
        MENUITEM "&Paste",                      IDM_PASTE
        MENUITEM SEPARATOR
        MENUITEM "&Delete",                     IDM_DELETE
        MENUITEM SEPARATOR
        MENUITEM "Select &All",                 IDM_SELECTALL
    End
    POPUP "&Search"
    BEGIN
        MENUITEM "&Find...\tCtrl+F",            IDM_FIND
        MENUITEM "Find &Next\tF3",              IDM_FINDNEXT
        MENUITEM "Find &Prev.\tCtrl+F3",        IDM_FINDPREV
        MENUITEM "&Replace..\tCtrl+R",          IDM_REPLACE
        MENUITEM SEPARATOR
        MENUITEM "&Go To Line\tCtrl+G",         IDM_GOTOLINE
    End
    MENUITEM "Options",                     IDM_OPTION
End

IDD_OPTIONDLG DIALOG DISCARDABLE  0, 0, 183, 54
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
    WS_SYSMENU
CAPTION "Options"
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,137,7,39,14
    PUSHBUTTON      "Cancel",IDCANCEL,137,25,39,14
    GROUPBOX        "",IDC_STATIC,5,0,124,49
    LTEXT           "Background Color:",IDC_STATIC,20,14,60,8
    LTEXT           "",IDC_BACKCOLORBOX,85,11,28,14,SS_NOTIFY | WS_BORDER
    LTEXT           "Text Color:",IDC_STATIC,20,33,35,8
    LTEXT           "",IDC_TEXTCOLORBOX,85,29,28,14,SS_NOTIFY | WS_BORDER
End

IDD_FINDDLG DIALOG DISCARDABLE  0, 0, 186, 54
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Find.."
FONT 8, "MS Sans Serif"
BEGIN
    EDITTEXT        IDC_FINDEDIT,42,3,94,12,ES_AUTOHSCROLL
    CONTROL         "Match Case",IDC_MATCHCASE,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,6,24,54,10
    CONTROL         "Whole Word",IDC_WHOLEWORD,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,6,37,56,10
    CONTROL         "Down",IDC_DOWN,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
                    83,27,35,10
    CONTROL         "Up",IDC_UP,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,83,
                    38,25,10
    DEFPUSHBUTTON   "OK",IDOK,141,3,39,12
    PUSHBUTTON      "Cancel",IDCANCEL,141,18,39,12
    LTEXT           "Find what:",IDC_STATIC,5,4,34,8
    GROUPBOX        "Direction",IDC_STATIC,70,18,64,32
End

IDD_GOTODLG DIALOGEX 0, 0, 106, 30
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_TOOLWINDOW
CAPTION "Go To Line"
FONT 8, "MS Sans Serif", 0, 0, 0x1
BEGIN
    EDITTEXT        IDC_LINENO,29,4,35,11,ES_AUTOHSCROLL | ES_NUMBER,
                    WS_EX_CLIENTEDGE
    DEFPUSHBUTTON   "OK",IDOK,70,4,31,11
    PUSHBUTTON      "Cancel",IDCANCEL,70,17,31,11
    LTEXT           "Line :",IDC_STATIC,8,5,18,8
End

IDD_REPLACEDLG DIALOG DISCARDABLE  0, 0, 186, 33
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Replace"
FONT 8, "MS Sans Serif"
BEGIN
    EDITTEXT        IDC_FINDEDIT,51,3,84,12,ES_AUTOHSCROLL
    EDITTEXT        IDC_REPLACEEDIT,51,17,84,11,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "OK",IDOK,142,3,39,11
    PUSHBUTTON      "Cancel",IDCANCEL,142,17,39,11
    LTEXT           "Find what:",IDC_STATIC,3,4,34,8
    LTEXT           "Replace with",IDC_STATIC,3,18,42,8
End

IDR_MAINACCEL ACCELERATORS DISCARDABLE 
BEGIN
    "F",            IDM_FIND,               VIRTKEY, CONTROL, NOINVERT
    "G",            IDM_GOTOLINE,           VIRTKEY, CONTROL, NOINVERT
    "R",            IDM_REPLACE,            VIRTKEY, CONTROL, NOINVERT
    VK_F3,          IDM_FINDNEXT,           VIRTKEY, NOINVERT
    VK_F3,          IDM_FINDPREV,           VIRTKEY, CONTROL, NOINVERT
End

АНАЛИЗ

Поиск текста реализуется с помощью EM_FINDTEXTEX. Когда пользователь кликает на пункте меню 'Find', отсылается сообщение IDM_FIND и отображается соответствующее диалоговое окно.

If GetDlgItemText(hWnd,IDC_FINDEDIT,Cast(LPSTR,@FindBuffer),Sizeof(FindBuffer))<>0 Then

Когда пользователь задает текст, который нужно найти, а затем нажимает кнопку 'OK', текст, который мы собираемся искать, оказывается в FindBuffer.

uFlags = 0
SendMessage(hwndRichEdit,EM_EXGETSEL,0,Cast(LPARAM,@findtext_.chrg))

Если текстовая строка не равняется NULL, мы устанавливаем значение переменной uFlags равной 0. Эта переменная используется для хранения флагов поиска, отсылаемых вместе с сообщением EM_FINDTEXTEX. После этого мы получаем текущий выделенный текст с помощью EM_EXGETSEL, потому что нам нужно знать, откуда будет производиться поиск.

If IsDlgButtonChecked(hWnd,IDC_DOWN) = BST_CHECKED Then
    uFlags Or=FR_DOWN
    If findtext_.chrg.cpMin<>findtext_.chrg.cpMax Then
        findtext_.chrg.cpMin = findtext_.chrg.cpMax
    Endif
    findtext_.chrg.cpMax=-1
Else
    findtext_.chrg.cpMax=0
Endif

Следующая часть несколько сложнее. Мы проверяем значение кнопки, задающей направление поиска. Если указан поиск вперед, мы задаем флаг FR_DOWN в uFlags. После этого мы проверяем, выделен ли сейчас какой-нибудь текст, проверяя значения cpMin и cpMax. Если оба значения не равны, это означает, что выделение существует, и мы должны продолжать поиск от конца текущего выделения до конца текста в контроле. Поэтому нам требуется заменить значение cpMax на значение cpMin, а cpMax приравнять к -1 (&hFFFFFFFF). если выделения нет, поиск будет производиться от текущей позиции курсора до конца текста.

Если пользователь выберет поиск назад, мы используем диапазон от начала выделения до начала текста в контроле. Поэтому на нужно только изменить значение cpMax на 0. В случае с поиском назад cpMin содержит индекс последнего символа в диапазоне поиска, а cpMax - индекс первого символа. Это инверсия поиска вперед.

If IsDlgButtonChecked(hWnd,IDC_MATCHCASE) = BST_CHECKED Then
    uFlags Or=FR_MATCHCASE
Endif
If IsDlgButtonChecked(hWnd,IDC_WHOLEWORD) = BST_CHECKED Then
    uFlags Or=FR_WHOLEWORD
Endif
findtext_.lpstrText = Strptr(FindBuffer)

Мы продолжаем проверку checkbox'ов, чтобы задать значения FR_MATCHCASE и FR_WHOLEWORD. Наконец, мы помещаем смещение текста, который нужно найти в поле lpstrText.

If SendMessage(hwndRichEdit,EM_FINDTEXTEX,uFlags,Cast(Lparam,@findtext_))<>-1 Then
    SendMessage(hwndRichEdit,EM_EXSETSEL,0,Cast(Lparam,@findtext_.chrgText))
Endif

Мы сейчас готовы послать сообщение EM_FINDTEXTEX. После этого мы проверяем результаты поиска, возвращенные SendMessage. Если возвращаемое значение равно -1, значит никаких совпадений не было найдено. В противном случае поле chrgText структуры FINDTEXTEX заполняется индексами совпавшего текста. Мы выделяем найденный текст с помощью EM_EXSETSEL.

Операция замещения делается подобным же образом.

GetDlgItemText(hWnd,IDC_FINDEDIT,Cast(LPCTSTR,@FindBuffer),Sizeof(FindBuffer))
GetDlgItemText(hWnd,IDC_REPLACEEDIT,Cast(LPCTSTR,@ReplaceBuffer),Sizeof(ReplaceBuffer))

Мы получаем текст, который нужно найти, и текст, который нужно заменить.

findtext_.chrg.cpMin = 0
findtext_.chrg.cpMax = -1
findtext_.lpstrText = Strptr(FindBuffer)

Чтобы сделать проще, операция замещения действует на весь текст в контроле. Поэтому начальный индекс равен 0, а конечный - -1.

settext.flags = ST_SELECTION
settext.codepage = CP_ACP

Мы инициализируем структуру SETTEXTEX, чтобы задать то, что мы хотим заменить текущее выделение и использовать системную страницу кодировки по умолчанию.

While TRUE
    If  SendMessage(hwndRichEdit,EM_FINDTEXTEX,FR_DOWN,Cast(LPARAM,@FindText_)) = -1 Then
        Exit While
    Else
        SendMessage(hwndRichEdit,EM_EXSETSEL,0,Cast(LPARAM,@findtext_.chrgText))
        SendMessage(hwndRichEdit,EM_SETTEXTEX,Cast(WPARAM,@settext),Cast(LPARAM,@ReplaceBuffer))
    Endif
Wend

Мы входим в бесконечный цикл и ищем совпадающий текст. Если он был найден, мы выбираем его с помощью EM_EXSETSEL и заменяем его EM_SETTEXTEX. Если больше совпадений не было найдено, мы выходим из цикла.

'Find Next' и 'Find Prev.' используют сообщение EM_FINDTEXTEX похожим образом.

Теперь мы рассмотрим 'Go to Line'. Когда пользователь кликает по этому пункту меню, мы отображаем диалоговое окно.

Когда пользователь набирает номер линии и нажимет кнопку 'Ok', мы начинаем операцию.

LineNo = GetDlgItemInt(hWnd,IDC_LINENO,NULL,FALSE)

Получаем номер линии из edit control'а.

Var GM = SendMessage(hwndRichEdit,EM_GETLINECOUNT,0,0)
If GM > LineNo Then

Получаем количество линий в контроле. Проверяем, не указал ли пользователь номер линии, который выходит за рамки допустимых значений.

Var GM = SendMessage(hwndRichEdit,EM_LINEINDEX,LineNo,0)

Если номер линии верен, мы передвигает курсор к первому символу в этой строке. Поэтому мы посылаем сообщение EM_LINEINDEX контролу richedit. Это сообщение возвращает индекс первого символа в заданной строке. Мы посылаем номер строки через wParam, а получаем индекс символа.

SendMessage(hwndRichEdit,EM_SETSEL,GM,GM)

Чтобы установить текущее выделение, в этот раз мы используем EM_SETSEL, потому что индексы символов уже не находятся в структуре CHARRANGE, что сохраняет нам две инструкции (помещение этих индексов в структуру CHARRANGE).

SetFocus(hwndRichEdit)

Курсор не будет отображен, пока контрол RichEdit не получит фокус. Поэтому мы вызываем SetFocus.

Перевод на русский с оригинала: Aquila, адаптация материалов под FreeBasic: Станислав Будинов

содержание | назад | вперед