Win32 API. Урок 33. RichEdit Control: основы — Архив WASM.RU

Все статьи

Win32 API. Урок 33. RichEdit Control: основы — Архив WASM.RU

Загрузите пример.

ТЕОРИЯ

О richedit контроле можно думать, как о функционально-расширенном средстве редактирования. Он обеспечивает множество полезных особенностей, которых нет в простых средствах редактирования, например, возможность использовать множество видов и размеров шрифта, глубокий уровень отмены/восстановления, операцией поиска по тексту, встроенные OLE-объекты, поддержка редактирования методом перетаскивания (drag-and-drop), и т.д. Так как richedit контрол имеет так много особенностей, он сохранен в отдельной DLL-библиотеке. Это также означает что, чтобы его использовать, вам недостаточно просто вызывать InitCommonControls, как в других Common-контролах. Вы должны вызвать LoadLibrary, чтобы загрузить richedit DLL.

Проблема состоит в том, что в настоящее время есть уже три версии richedit контрола. Версия 1,2, и 3. В таблице ниже показаны имена DLL для каждой версии.

имя DLLверсия RichEdit'аИмя класса Richedit'а
Riched32.dll1.0RICHEDIT
RichEd20.dll2.0RICHEDIT20A
RichEd20.dll3.0RICHEDIT20A

Обратите внимание, что richedit версия 2 и 3 использует то же самое имя DLL. Они также используют одно и то же имя класса! Это может вызвать проблему, если Вы захотите использовать определенные особенности richedit'а версии 3.0. До сих пор, я не нашел официального метода различия между версиями 2.0 и 3.0. Однако, есть рабочий пример, который хорошо работает, я покажу Вам позже.

.data
  RichEditDLL db "RichEd20.dll", 0
      .....
.data?
    HRichEditDLL dd?
.code
    invoke LoadLibrary, addr RichEditDLL
    Mov hRichEditDLL, eax
      ......
    invoke FreeLibrary, hRichEditDLL

Когда richedit dll загружена, она регистрирует класс окна RichEdit. Следовательно вам необходимо загрузить DLL прежде, чем Вы создадите контрол. Имена классов richedit контрола также различны. Теперь Вы можете задать вопрос: а какую версию richedit контрола мне следует использовать? Использование самой последней версии не всегда подходит, если Вы не требуете дополнительных особенностей. В таблице ниже, показаны особенности каждой версии richedit контрола.

ОсобенностьВерсия 1.0Версия 2.0Версия 3.0
Выделение областиxxx
Редактирование уникода xx
Форматирование символа/абзацаxxx
Поиск по текстуВпередВперед/назадВперед/назад
Внедрение OLExxx
Редактирование перетащить и отпустить(drag-and-drop)xxx
Отменить/ПовторитьОдноуровневыйМногоуровневыйМногоуровневый
Автоматическое распознавание URL xx
Поддержка клавиш быстрого доступа(hot key) xx
операция уменьшения окна xx
Конец строкиCRLFтолько CRтолько CR(может подражать версии 1.0)
Увеличение  x
Нумерация абзацев  x
Простая таблица  x
Нормальный и заголовочный стили  x
Цветное подчеркивание  x
Скрытый текст  x
связывание шрифта  x

Вышеуказанная таблица ни в коем случае не полная: я только перечислил важные особенности.

Создание richedit контрола

После загрузки richedit dll, Вы можете вызывать CreateWindowEx, для создания контрола. Вы можете использовать стили средств редактирования и common windows стили в CreateWindowEx кроме ES_LOWERCASE, ES_UPPERCASE и ES_OEMCONVERT.

.const
    RichEditID equ 300
.data
    RichEditDLL db "RichEd20.dll", 0
    RichEditClass db "RichEdit20A", 0 
      ..... 
.data?
    HRichEditDLL dd ?
    HwndRichEdit dd ? 
.code
   .....
 invoke LoadLibrary, addr RichEditDLL
 Mov hRichEditDLL, eax
 invoke CreateWindowEx, 0, addr RichEditClass,\
   WS_VISIBLE or ES_MULTILINE or WS_CHILD or WS_VSCROLL or WS_HSCROLL, \
   CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, \
   hWnd, RichEditID, hInstance, 0
 Mov hwndRichEdit, eax

Установка по умолчанию цвета текста и фона

У вас может возникнуть проблема с установкой цвета текста и фона в средствах редактирования. Но эта проблема была исправлена в richedit контроле. Чтобы установить цвет фона richedit контрола, вам нужно послать ему EM_SETBKGNDCOLOR. Это сообщение имеет следующий синтаксис:

WParam == цвет. Значение 0 в этом параметре означает, что Windows использует значение цвета из lParam как цвет фона. Если это значение отличное от нуля, Windows использует цвет фона системы Windows. Так как мы посылаем это сообщение, чтобы изменить цвет фона, мы должны поместить 0 в wParam.
LParam == определяет структуру COLORREF цвета, который Вы хотите установить, если wParam - 0.

Например, если бы я захотел установить цвет фона в синий, я бы поместил этот код:

invoke SendMessage, hwndRichEdit, EM_SETBKGNDCOLOR, 0,0FF0000h

Устанавливая цвет текста, richedit контрол создает новое сообщение, EM_SETCHARFORMAT. Это сообщение управляет форматированием текста в диапазоне от символа в выделении до всего текста. Это сообщение имеет следующий синтаксис:

WParam == опции форматирования:

SCF_ALLОперация затрагивает весь текст в контроле.
SCF_SELECTIONОперация затрагивает только выделенный текст
SCF_WORD или SCF_SELECTIONЗатрагивает слово в выделении. Если ничего не выделенно, то операция затронет то слово на котором находится курсор. Флаг SCF_WORD должен использоваться с SCF_SELECTION.

LParam == указатель на структуру CHARFORMAT ИЛИ CHARFORMAT2, которая определяет форматирование текста, которое нужно применить. CHARFORMAT2 доступен только для richedit 2.0 и выше. Это не подразумевает, что Вы должны использовать CHARFORMAT2 с RichEdit 2.0 и выше. Вы все еще можете использовать CHARFORMAT, если добавленные в CHARFORMAT2 особенности вам не нужны.

CHARFORMATA STRUCT
    CbSize DWORD ? 
    DwMask DWORD ?
    DwEffects DWORD ?
    YHeight DWORD ?
    YOffset DWORD ?
    CrTextColor COLORREF ?
    BCharSet BYTE ?
    BPitchAndFamily BYTE ?
    SzFaceName BYTE LF_FACESIZE dup(?)
    _wPad2 WORD ?
CHARFORMATA ENDS
Имя поляОписание
CbSizeРазмер структуры. RichEdit контрол использует это поле, чтобы определить версию структуры, является ли это CHARFORMAT или CHARFORMAT2
DwMaskРазряды флагов, которые определяют, какие из следующих членов являются правильными.
CFM_BOLDCFE_BOLD член dwEffects правильный
CFM_CHARSET член BCharSet- правильный.
CFM_COLOR член CrTextColor и значение CFE_AUTOCOLOR члена dwEffects - правильные
CFM_FACE член SzFaceName правильный.
CFM_ITALICЗначение CFE_ITALIC члена dwEffects правильное
CFM_OFFSET член YOffset правильный
CFM_PROTECTEDЗначение CFE_PROTECTED члена dwEffects правильное
CFM_SIZE член YHeight правильный
CFM_STRIKEOUTЗначение CFE_STRIKEOUT члена dwEffects правильное.
CFM_UNDERLINEЗначение CFE_UNDERLINE члена dwEffects правильное
DwEffects

Символьные эффекты. Может быть комбинация следующих значений

CFE_AUTOCOLORИспользует системный цвет текста
CFE_BOLDПолужирный
CFE_ITALICКурсивный
CFE_STRIKEOUTПеречеркнутый.
CFE_UNDERLINEПодчеркнутый.
CFE_PROTECTEDСимволы защищены; попытка изменять их вызовет уведомительное сообщение EN_PROTECTED.
YHeightВысота символов, в twips (1/1440 дюйма или 1/20 точки принтера).
YOffsetСмещение Символа, в twips (1/1440 дюйма или 1/20 точки принтера), от основной линии. Если значение этого члена положительное, символ - верхний индекс; если - отрицательное - нижний индекс.
CrTextColor Цвет текста. Этот член игнорируется, если эффект символа CFE_AUTOCOLOR определен.
BCharSetНабор символов
BPitchAndFamilyСемейство Шрифта и шаг.
SzFaceName Символьный массив с нулевым символом в конце, определяющий имя шрифта
_wPad2Дополнение

Из исследования структуры, Вы увидите, что мы можем изменять эффекты текста (полужирный, курсивный, зачеркнутый, подчеркнутый), цвет текста (crTextColor) и шрифт (вид/размер/набор символов). Текст с флагом CFE_RPOTECTED, отмечен как защищенный, это означает, что, когда пользователь попытается изменить его, то родительскому окну будет послано уведомительное сообщение EN_PROTECTED. И Вы можете либо разрешить, либо запретить изменения.

CHARFORMAT2 добавляет большее количество текстовых стилей, таких как weight шрифта, интервала, цвета фона текста, кернинга, и т.д. Если Вы не нуждаетесь в этих дополнительных особенностях, просто используйте CHARFORMAT.

Чтобы установить формат текста, Вы должны указать диапазон текста, к которому хотите применить формат. Richedit контрол присваивает каждому символу свой номер (идентификатор), начинающийся с 0: первый символ имеет идентификатор 0, второй - 1 и так далее. Чтобы указать диапазон текста, вы должны дать richedit контролу, два числа: ИДЕНТИФИКАТОРЫ первого и последнего символа диапазона. Чтобы применить форматирование к тексту с EM_SETCHARFORMAT, у вас есть 3 выбора:

  1. Применить ко всему тексту (SCF_ALL)
  2. Применить к выделенному тексту (SCF_SELECTION)
  3. Применить к целому слову в выделенной области (SCF_WORD or SCF_SELECTION)

Первый и второй выборы прямые, последний требует некоторых объяснений. Если текущее выделение охватывает только один или большее количество символов в слове, но не, целое слово, определяя флаг SCF_WORD+SCF_SELECTION, применяется форматирование текста к целому слову. Даже если ничего не выделено, форматирование применяется к целому слову, над которым находится курсор вставки.

Чтобы использовать EM_SETCHARFORMAT, Вы должны заполнить некоторые члены структуры CHARFORMAT (или CHARFORMAT2). Например, если мы хотим установить цвет текста, мы заполним структуру CHARFORMAT следующим образом:

.data?
  Cf CHARFORMAT < >
..
.code
  Mov cf.cbSize, sizeof cf
  Mov cf.dwMask, CFM_COLOR
  Mov cf.crTextColor, 0FF0000h
  invoke SendMessage, hwndRichEdit, EM_SETCHARFORMAT, \
  SCF_ALL, addr cf

Этот фрагмент кода устанавливает цвет текста richedit контрола в синий. Обратите внимание, что, если в richedit контроле нет никакого текста, когда мы посылаем сообщение EM_SETCHARFORMAT, текст, введенный в richedit контроле после сообщения будет использовать формат текста, указанный с сообщением EM_SETCHARFORMAT.

Установка текста/сохранение текста

Те, кто уже использовали Edit контролы, наверняка знакомы с WM_GETTEXT/WM_SETTEXT, для установки/получения текста в\из контрола. Этот метод работает и с richedit контролом, но не может работать с большими файлами. Edit контрол ограничивает текст, который может быть введен в него 64КБ, но richedit контрол может принимать текст намного больше. Это было бы не правильное решение, выделить очень большой блок памяти (типа 10 мб) чтобы получить текст с помощью WM_GETTEXT. Richedit контрол предлагает новый подход к этому методу, такой как текстовый поток.

Зделать это просто, вы передаете адрес функции richedit контролу. И richedit контрол вызовет эту функцию, передавая ей адрес буфера, когда он готов. Функция заполнит буфер данными, которые требуется послать контролу или считает данные из буфера и будет ждать следующего запроса, пока операция не закончена. Эта парадигма используется для обоих операций: потоковый ввод (установка текста) и потоковый вывод (получение текста из контрола). Вы увидите, что этот метод более эффективен: буфер обеспечивается richedit контролом непосредственно, так что данные разделены на куски. Операции включают два сообщения: EM_STREAMIN и EM_STREAMOUT

Оба сообщения EM_STREAMIN и EM_STREAMOUT используют одинаковый синтаксис:

WParam == опции.

SF_RTFДанные в формате RTF (rich-text format)
SF_TEXTДанные в формате открытого текста
SFF_PLAINRTFТолько ключевые слова, общие ко всем языкам в потоке.
SFF_SELECTIONЕсли цель операции - выделенный текст. Если вводите текст, он заменяет текущее выделение. Если вы выводите текст, то будет выведен только выделенный в настоящее время текст. Если этот флаг не определен, операция работает со всем текстом в контроле.
SF_UNICODE( Доступна для RichEdit 2.0 и выше) Определяют текст уникода.

LParam == указывают на структуру EDITSTREAM, которая имеет следующее определение:


EDITSTREAM STRUCT
  DwCookie DWORD ?
  DwError DWORD ?
  PfnCallback DWORD ?
EDITSTREAM ENDS

DwCookieОпределенное приложением значение, которое будет передаваться к функции определенной в члене pfnCallback ниже. Мы обычно передаем функции некоторые важные значения, такие как хэндл файла, для использования в stream-in/out процедуре.
DwErrorОтображает результат операции stream-in (чтения) или stream-out (записи). Значение 0 - нет ошибон. Значение отличное от нуля может быть результатом функции EditStreamCallback или кода, указывающего, что произошла ошибка.
PfnCallbackУказатель на функцию EditStreamCallback, которая является определенной приложением функцией, которая управляет передачей данных. Управление вызывает функцию неоднократно, передавая часть данных с каждым запросом

Editstream функция имеет следующее определение:

EditStreamCallback proto dwCookie:DWORD,
  PBuffer:DWORD,
  NumBytes:DWORD,
  PBytesTransferred:DWORD

Вы должны создать функцию с вышеупомянутым прототипом в вашей программе. И затем передайте ее адрес с помощью EM_STREAMIN или EM_STREAMOUT через структуру EDITSTREAM.

Для операции stream-in (загрузка текста в richedit контрол):

DwCookie: определенное приложением значение вы передаете с EM_STREAMIN через структуру EDITSTREAM. Мы почти всегда передаем хэндл файла, содержание которого мы хотим установить в контрол.
PBuffer: указывает на буфер, переданный richedit контролом, который получит текст от вашей функции.
NumBytes: максимальное количество байт, которые вы может записать в буфер (pBuffer) в этом запросе. Вы всегда ДОЛЖНЫ придерживаться этого предела, т.е., вы можете посылать меньшее количество данных чем значение в NumBytes, и не должны послать большее количество данных чем это значение. Вы можете считать это значение, как размер буфера pBuffer.
pBytesTransferred: указывает на переменную (dword), указывающюю число байтов, которые вы фактически передали в буфер. Это значение обычно идентично значению в NumBytes.Исключение когда данных послано меньше, чем размер буфера, обычно когда достигнут конец файла.

Для операции stream-out (получение текста из richedit контрола):

DwCookie: Такой же, как в операции stream-in. Мы обычно передаем хэндл файла, в который мы хотим записать данные.
PBuffer: указывает на буфер, обеспеченный richedit контролом, который заполнен данными из richedit контрола. Чтобы получить его размер, Вы должны посмотреть значение NumBytes.
NumBytes: размер данных в буфере, указанном в pBuffer.
PBytesTransferred: указывает на переменную (dword), в которую вы должны установить значение, индицирующее число байт, которые Вы фактически считали из буфера.

Функция возвращает 0, если не было ошибок, и richedit контрол продолжит вызывать функцию, если данные все еще есть. Если произошла ошибка в течение процесса, и вы хотите остановить операцию, верните ненулевое значение, и richedit контрол откажется от данных, указанных в pBuffer. Значение ошибки/успеха будет заполнено в поле dwError структуры EDITSTREAM, так что вы можете проверить результат операции после возврата изSendMessage.

ПРИМЕР

Пример ниже это - простой редактор, которым Вы можете открывать файлы исходного текста ассемблера, редактировать, и сохранять их. Он использует RichEdit версии 2.0 или выше.

.386.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\gdi32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

.const
IDR_MAINMENU      equ 101
IDM_OPEN          equ 40001
IDM_SAVE          equ 40002
IDM_CLOSE         equ 40003
IDM_SAV           equ 40004
IDM_EXIT          equ 40005
IDM_COPY          equ 40006
IDM_CUT           equ 40007
IDM_PASTE         equ 40008
IDM_DELETE        equ 40009
IDM_SELECTALL     equ 40010
IDM_OPTION        equ 40011
IDM_UNDO          equ 40012
IDM_REDO          equ 40013
IDD_OPTIONDLG     equ 101
IDC_BACKCOLORBOX  equ 1000
IDC_TEXTCOLORBOX  equ 1001

RichEditID        equ 300

.data
ClassName db "IczEditClass",0
AppName db "IczEdit версия 1.0",0
RichEditDLL db "riched20.dll",0
RichEditClass db "RichEdit20A",0
NoRichEdit db "
ASMFilterString db "ASM Исходники (*.asm)",0,"*.asm",0
                db "Все файлы (*.*)",0,"*.*",0,0
OpenFileFail db "Не могу окрыть файл",0
WannaSave db "Данные были изменены. Вы хотите сохранить изменения?",0
FileOpened dd FALSE
BackgroundColor dd 0FFFFFFh ; по умолчанию - белый
TextColor dd 0              ; по умолчанию - черный

.data?
hInstance dd ?
hRichEdit dd ?
hwndRichEdit dd ?
FileName db 256 dup(?)
AlternateFileName db 256 dup(?)
CustomColors dd 16 dup(?)

.code
start:
  invoke GetModuleHandle, NULL
  mov    hInstance,eax
  invoke LoadLibrary,addr RichEditDLL
  .if eax!=0
    mov hRichEdit,eax
    invoke WinMain, hInstance,0,0, SW_SHOWDEFAULT
    invoke FreeLibrary,hRichEdit
  .else
    invoke MessageBox,0,addr NoRichEdit,addr AppName, \
           MB_OK or MB_ICONERROR
  .endif
  invoke ExitProcess,eax
  
WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD
  LOCAL wc:WNDCLASSEX
  LOCAL msg:MSG
  LOCAL hwnd:DWORD
  mov   wc.cbSize,SIZEOF WNDCLASSEX
  mov   wc.style, CS_HREDRAW or CS_VREDRAW
  mov   wc.lpfnWndProc, OFFSET WndProc
  mov   wc.cbClsExtra,NULL
  mov   wc.cbWndExtra,NULL
  push  hInst
  pop   wc.hInstance
  mov   wc.hbrBackground,COLOR_WINDOW+1
  mov   wc.lpszMenuName,IDR_MAINMENU
  mov   wc.lpszClassName,OFFSET ClassName
  invoke LoadIcon,NULL,IDI_APPLICATION
  mov   wc.hIcon,eax
  mov   wc.hIconSm,eax
  invoke LoadCursor,NULL,IDC_ARROW
  mov   wc.hCursor,eax
  invoke RegisterClassEx, addr wc
  INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
         WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
         CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
         hInst,NULL
  mov   hwnd,eax
  invoke ShowWindow, hwnd,SW_SHOWNORMAL
  invoke UpdateWindow, hwnd
  .while TRUE
    invoke GetMessage, ADDR msg,0,0,0
    .break .if (!eax)
    invoke TranslateMessage, ADDR msg
    invoke DispatchMessage, ADDR msg
  .endw
  mov   eax,msg.wParam
  ret
WinMain endp

StreamInProc proc hFile:DWORD,pBuffer:DWORD, \
                  NumBytes:DWORD, pBytesRead:DWORD
  invoke ReadFile,hFile,pBuffer,NumBytes,pBytesRead,0
  xor eax,1
  ret
StreamInProc endp

StreamOutProc proc hFile:DWORD,pBuffer:DWORD, \
                   NumBytes:DWORD, pBytesWritten:DWORD
  invoke WriteFile,hFile,pBuffer,NumBytes,pBytesWritten,0
  xor eax,1
  ret
StreamOutProc endp

CheckModifyState proc hWnd:DWORD
  invoke SendMessage,hwndRichEdit,EM_GETMODIFY,0,0
  .if eax!=0
    invoke MessageBox,hWnd,addr WannaSave, \
           addr AppName,MB_YESNOCANCEL
    .if eax==IDYES
      invoke SendMessage,hWnd,WM_COMMAND,IDM_SAVE,0
    .elseif eax==IDCANCEL
      mov eax,FALSE
      ret
    .endif
  .endif
  mov eax,TRUE
  ret
CheckModifyState endp

SetColor proc
  LOCAL cfm:CHARFORMAT
  invoke SendMessage,hwndRichEdit,EM_SETBKGNDCOLOR, \
         0,BackgroundColor
  invoke RtlZeroMemory,addr cfm,sizeof cfm
  mov cfm.cbSize,sizeof cfm
  mov cfm.dwMask,CFM_COLOR
  push TextColor
  pop cfm.crTextColor
  invoke SendMessage,hwndRichEdit,EM_SETCHARFORMAT, \
         SCF_ALL,addr cfm
  ret
SetColor endp

OptionProc proc hWnd:DWORD, uMsg:DWORD, \
                wParam:DWORD, lParam:DWORD
  LOCAL clr:CHOOSECOLOR
  .if uMsg==WM_INITDIALOG
  .elseif uMsg==WM_COMMAND
    mov eax,wParam
    shr eax,16
    .if ax==BN_CLICKED
      mov eax,wParam
      .if ax==IDCANCEL
        invoke SendMessage,hWnd,WM_CLOSE,0,0
      .elseif ax==IDC_BACKCOLORBOX
        invoke RtlZeroMemory,addr clr,sizeof clr
        mov clr.lStructSize,sizeof clr
        push hWnd
        pop clr.hwndOwner
        push hInstance
        pop clr.hInstance
        push BackgroundColor
        pop clr.rgbResult
        mov clr.lpCustColors,offset CustomColors
        mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT
        invoke ChooseColor,addr clr
        .if eax!=0
          push clr.rgbResult
          pop BackgroundColor
          invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
          invoke InvalidateRect,eax,0,TRUE
        .endif
      .elseif ax==IDC_TEXTCOLORBOX
        invoke RtlZeroMemory,addr clr,sizeof clr
        mov clr.lStructSize,sizeof clr
        push hWnd
        pop clr.hwndOwner
        push hInstance
        pop clr.hInstance
        push TextColor
        pop clr.rgbResult
        mov clr.lpCustColors,offset CustomColors
        mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT
        invoke ChooseColor,addr clr
        .if eax!=0
          push clr.rgbResult
          pop TextColor
          invoke GetDlgItem,hWnd,IDC_TEXTCOLORBOX
          invoke InvalidateRect,eax,0,TRUE
        .endif
      .elseif ax==IDOK
        ;====================================================
        ; Сохраните состояние richedit контрола потому,
        ; что изменение цвета текста изменяет это состояние.
        ;====================================================
        invoke SendMessage,hwndRichEdit,EM_GETMODIFY,0,0
        push eax
        invoke SetColor
        pop eax
        invoke SendMessage,hwndRichEdit,EM_SETMODIFY,eax,0
        invoke EndDialog,hWnd,0
      .endif
    .endif
  .elseif uMsg==WM_CTLCOLORSTATIC
    invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
    .if eax==lParam
      invoke CreateSolidBrush,BackgroundColor      
      ret
    .else
      invoke GetDlgItem,hWnd,IDC_TEXTCOLORBOX
      .if eax==lParam
        invoke CreateSolidBrush,TextColor
        ret
      .endif
    .endif
    mov eax,FALSE
    ret
  .elseif uMsg==WM_CLOSE
    invoke EndDialog,hWnd,0
  .else
    mov eax,FALSE
    ret
  .endif
  mov eax,TRUE
  ret
OptionProc endp

WndProc proc hWnd:DWORD, uMsg:DWORD, \
             wParam:DWORD, lParam:DWORD
  LOCAL chrg:CHARRANGE
  LOCAL ofn:OPENFILENAME
  LOCAL buffer[256]:BYTE
  LOCAL editstream:EDITSTREAM
  LOCAL hFile:DWORD
  .if uMsg==WM_CREATE
   invoke CreateWindowEx,WS_EX_CLIENTEDGE,addr 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,RichEditID,hInstance,0
    mov hwndRichEdit,eax
    ;==================================================
    ; Установка предела текста. По умолчанию - 64K
    ;==================================================
    invoke SendMessage,hwndRichEdit,EM_LIMITTEXT,-1,0
    ;==================================================
    ; установка цвета текста/фона
    ;==================================================
    invoke SetColor
    invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
    invoke SendMessage,hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0
  .elseif uMsg==WM_INITMENUPOPUP
    mov eax,lParam
    .if ax==0    ; меню Файл      
      .if FileOpened==TRUE  ; a file is already opened
        invoke EnableMenuItem,wParam,IDM_OPEN,MF_GRAYED
        invoke EnableMenuItem,wParam,IDM_CLOSE,MF_ENABLED
        invoke EnableMenuItem,wParam,IDM_SAVE,MF_ENABLED
        invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_ENABLED
      .else
        invoke EnableMenuItem,wParam,IDM_OPEN,MF_ENABLED
        invoke EnableMenuItem,wParam,IDM_CLOSE,MF_GRAYED
        invoke EnableMenuItem,wParam,IDM_SAVE,MF_GRAYED
        invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_GRAYED
      .endif
    .elseif ax==1  ; edit menu
      ;=====================================================
      ; проверьте есть ли текст в буфере обмена, если есть
      ; мы активизируем в меню пункт "Вставить".
      ;=====================================================
      invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0
      .if eax==0    ; no text in the clipboard
        invoke EnableMenuItem,wParam,IDM_PASTE,MF_GRAYED
      .else
        invoke EnableMenuItem,wParam,IDM_PASTE,MF_ENABLED
      .endif
      ;=====================================================
      ; Проверьте, является ли очередь отмены пустой
      ;=====================================================
      invoke SendMessage,hwndRichEdit,EM_CANUNDO,0,0
      .if eax==0
        invoke EnableMenuItem,wParam,IDM_UNDO,MF_GRAYED
      .else
        invoke EnableMenuItem,wParam,IDM_UNDO,MF_ENABLED
      .endif
      ;=====================================================
      ; Проверьте, является ли очередь повтора пустой
      ;=====================================================
      invoke SendMessage,hwndRichEdit,EM_CANREDO,0,0
      .if eax==0
        invoke EnableMenuItem,wParam,IDM_REDO,MF_GRAYED
      .else
        invoke EnableMenuItem,wParam,IDM_REDO,MF_ENABLED
      .endif
      ;=====================================================
      ; Проверьте, выделено ли что-нибудь в richedit контроле. 
      ; Если да, мы активизируем пункты меню вырезать/
      ; копировать/удалить
      ;======================================================
      invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg
      mov eax,chrg.cpMin
      .if eax==chrg.cpMax    ; no current selection
        invoke EnableMenuItem,wParam,IDM_COPY,MF_GRAYED
        invoke EnableMenuItem,wParam,IDM_CUT,MF_GRAYED
        invoke EnableMenuItem,wParam,IDM_DELETE,MF_GRAYED
      .else
        invoke EnableMenuItem,wParam,IDM_COPY,MF_ENABLED
        invoke EnableMenuItem,wParam,IDM_CUT,MF_ENABLED
        invoke EnableMenuItem,wParam,IDM_DELETE,MF_ENABLED
      .endif
    .endif
  .elseif uMsg==WM_COMMAND
    .if lParam==0    ; menu commands
      mov eax,wParam
      .if ax==IDM_OPEN
        invoke RtlZeroMemory,addr ofn,sizeof ofn
        mov ofn.lStructSize,sizeof ofn
        push hWnd
        pop ofn.hwndOwner
        push hInstance
        pop ofn.hInstance
        mov ofn.lpstrFilter,offset ASMFilterString
        mov ofn.lpstrFile,offset FileName
        mov byte ptr [FileName],0
        mov ofn.nMaxFile,sizeof FileName
        mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST
        invoke GetOpenFileName,addr ofn
        .if eax!=0
          invoke CreateFile,addr FileName,GENERIC_READ,\
              FILE_SHARE_READ,NULL,OPEN_EXISTING,\
              FILE_ATTRIBUTE_NORMAL,0
          .if eax!=INVALID_HANDLE_VALUE
            mov hFile,eax
            ;==============================================
            ; stream the text into the richedit control
            ;==============================================
            mov editstream.dwCookie,eax
            mov editstream.pfnCallback,offset StreamInProc
            invoke SendMessage,hwndRichEdit,EM_STREAMIN,\
                 SF_TEXT,addr editstream
            ;==============================================
            ; Initialize the modify state to false
            ;==============================================
            invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
            invoke CloseHandle,hFile
            mov FileOpened,TRUE
          .else
            invoke MessageBox,hWnd,addr OpenFileFail,\
                addr AppName,MB_OK or MB_ICONERROR
          .endif
        .endif
      .elseif ax==IDM_CLOSE
        invoke CheckModifyState,hWnd
        .if eax==TRUE
          invoke SetWindowText,hwndRichEdit,0
          mov FileOpened,FALSE
        .endif
      .elseif ax==IDM_SAVE
        invoke CreateFile,addr FileName,GENERIC_WRITE, \
            FILE_SHARE_READ,NULL,\
            CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
        .if eax!=INVALID_HANDLE_VALUE
@@:        
          mov hFile,eax
          ;=========================================
          ; stream the text to the file
          ;=========================================  
          mov editstream.dwCookie,eax
          mov editstream.pfnCallback,offset StreamOutProc
          invoke SendMessage,hwndRichEdit,EM_STREAMOUT, \
                 SF_TEXT, addr editstream
          ;=========================================
          ; Initialize the modify state to false
          ;=========================================
          invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
          invoke CloseHandle,hFile
        .else
          invoke MessageBox,hWnd,addr OpenFileFail,addr AppName,\
              MB_OK or MB_ICONERROR
        .endif
      .elseif ax==IDM_COPY
        invoke SendMessage,hwndRichEdit,WM_COPY,0,0
      .elseif ax==IDM_CUT
        invoke SendMessage,hwndRichEdit,WM_CUT,0,0
      .elseif ax==IDM_PASTE
        invoke SendMessage,hwndRichEdit,WM_PASTE,0,0
      .elseif ax==IDM_DELETE
        invoke SendMessage,hwndRichEdit,EM_REPLACESEL,TRUE,0
      .elseif ax==IDM_SELECTALL
        mov chrg.cpMin,0
        mov chrg.cpMax,-1
        invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg
      .elseif ax==IDM_UNDO
        invoke SendMessage,hwndRichEdit,EM_UNDO,0,0
      .elseif ax==IDM_REDO
        invoke SendMessage,hwndRichEdit,EM_REDO,0,0
      .elseif ax==IDM_OPTION
        invoke DialogBoxParam,hInstance,IDD_OPTIONDLG,hWnd,addr OptionProc,0
      .elseif ax==IDM_SAVEAS
        invoke RtlZeroMemory,addr ofn,sizeof ofn
        mov ofn.lStructSize,sizeof ofn
        push hWnd
        pop ofn.hwndOwner
        push hInstance
        pop ofn.hInstance
        mov ofn.lpstrFilter,offset ASMFilterString
        mov ofn.lpstrFile,offset AlternateFileName
        mov byte ptr [AlternateFileName],0
        mov ofn.nMaxFile,sizeof AlternateFileName
        mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY 
            or OFN_PATHMUSTEXIST
        invoke GetSaveFileName,addr ofn
        .if eax!=0
          invoke CreateFile,addr AlternateFileName,GENERIC_WRITE,\
              FILE_SHARE_READ,NULL,CREATE_ALWAYS,\
              FILE_ATTRIBUTE_NORMAL,0
          .if eax!=INVALID_HANDLE_VALUE
            jmp @B
          .endif
        .endif
      .elseif ax==IDM_EXIT
        invoke SendMessage,hWnd,WM_CLOSE,0,0
      .endif
    .endif
  .elseif uMsg==WM_CLOSE
    invoke CheckModifyState,hWnd
    .if eax==TRUE
      invoke DestroyWindow,hWnd
    .endif
  .elseif uMsg==WM_SIZE
    mov eax,lParam
    mov edx,eax
    and eax,0FFFFh
    shr edx,16
    invoke MoveWindow,hwndRichEdit,0,0,eax,edx,TRUE    
  .elseif uMsg==WM_DESTROY
    invoke PostQuitMessage,NULL
  .else
    invoke DefWindowProc,hWnd,uMsg,wParam,lParam    
    ret
  .endif
  xor eax,eax
  ret
WndProc endp
end start

;=============================================
; Файл ресурсов
;=============================================
#include "resource.h"
#define IDR_MAINMENU                    101
#define IDD_OPTIONDLG                   101
#define IDC_BACKCOLORBOX                1000
#define IDC_TEXTCOLORBOX                1001
#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

IDR_MAINMENU MENU DISCARDABLE 
BEGIN
    POPUP "&Файл"
    BEGIN
        MENUITEM "&Открыть...",        IDM_OPEN
        MENUITEM "&Закрыть",           IDM_CLOSE
        MENUITEM "&Сохранить",         IDM_SAVE
        MENUITEM "Сохранить &как...",  IDM_SAVEAS
        MENUITEM SEPARATOR
        MENUITEM "В&ыход",             IDM_EXIT
    END
    POPUP "&Правка"
    BEGIN
        MENUITEM "&Отменить",          IDM_UNDO
        MENUITEM "&Повторить",         IDM_REDO
        MENUITEM "&Копировать",        IDM_COPY
        MENUITEM "&Вырезать",          IDM_CUT
        MENUITEM "Вст&авить",          IDM_PASTE
        MENUITEM SEPARATOR
        MENUITEM "&Удалить",           IDM_DELETE
        MENUITEM SEPARATOR
        MENUITEM "В&ыделить все",      IDM_SELECTALL
    END
    MENUITEM "П&араметры",             IDM_OPTION
END

IDD_OPTIONDLG DIALOG DISCARDABLE  0, 0, 183, 54
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION 
    | WS_SYSMENU | DS_CENTER
CAPTION "Options"
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,137,7,39,14
    PUSHBUTTON      "Отмена",IDCANCEL,137,25,39,14
    GROUPBOX        "",IDC_STATIC,5,0,124,49
    LTEXT           "Цвет фона:",IDC_STATIC,20,14,60,8
    LTEXT  "",IDC_BACKCOLORBOX,85,11,28,14,SS_NOTIFY | WS_BORDER
    LTEXT  "Цвет текста:",IDC_STATIC,20,33,35,8
    LTEXT  "",IDC_TEXTCOLORBOX,85,29,28,14,SS_NOTIFY | WS_BORDER
END

АНАЛИЗ

Программа сначала загружает richedit dll, в нашем случае riched20.dll. Если dll не может быть загружена, то выходим из программы.

invoke LoadLibrary, addr RichEditDLL 
.if eax!=0
  Mov hRichEdit, eax
  invoke WinMain, hInstance, 0,0, SW_SHOWDEFAULT
  invoke FreeLibrary, hRichEdit
.else
  invoke MessageBox, 0, addr NoRichEdit, addr AppName, \
         MB_OK or MB_ICONERROR
.endif
  invoke ExitProcess, eax

После того, как dll успешно загружена, мы переходим к созданию нормального окна, которое будет родительским richedit контрола. Внутри обработчика WM_CREATE, мы создаем richedit контрол:

invoke CreateWindowEx, WS_EX_CLIENTEDGE, addr 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, RichEditID,\
  hInstance, 0
Mov hwndRichEdit, eax

Обратите внимание, что мы определяем стиль ES_MULTILINE, иначе контрол будет одиночно-выровненный.

invoke SendMessage, hwndRichEdit, EM_LIMITTEXT,-1,0

После того, как richedit контрол создан, мы должны установить в нем новый текстовый предел. По умолчанию, richedit контрол имеет предел текста 64КБ, такой же как в простых многострочных Edit контролах. Мы должны расширить этот предел, чтобы оперировать с большими файлами. В вышеупомянутой строке, я определяю -1, которая составляет 0FFFFFFFFh, очень большое значение.

invoke SetColor

Затем, мы устанавливаем цвет текста и фона. Так как эта операция может быть вызвана и из другой части программы, я поместил код в функцию SetColor.

SetColor proc
  LOCAL cfm:CHARFORMAT
  invoke SendMessage, hwndRichEdit, EM_SETBKGNDCOLOR, \
         0, BackgroundColor

Установка цвета фона richedit контрола это прямая операция: просто пошлите сообщение EM_SETBKGNDCOLOR richedit контролу. (Если Вы используете многострочные Edit контролы, Вы должны обрабатывать WM_CTLCOLOREDIT). Заданный по умолчанию цвет фона белый.

invoke RtlZeroMemory, addr cfm, sizeof cfm
Mov cfm.cbSize, sizeof cfm
Mov cfm.dwMask, CFM_COLOR
push TextColor
pop cfm.crTextColor

После того, как цвет фона установлен, мы заполняем члены структуры CHARFORMAT, чтобы установить цвет текста. Обратите внимание, что мы заполняем cbSize размером структуры, так что richedit контрол знает, что мы посылаем ему CHARFORMAT, а не CHARFORMAT2. DwMask имеет только один флаг, CFM_COLOR, потому что мы только хотим установить цвет текста, и crTextColor заполнен значением желаемого цвета текста.

invoke SendMessage, hwndRichEdit, EM_SETCHARFORMAT, \
SCF_ALL, addr cfm
Ret
SetColor endp

После установки цвета, Вы должны освободить буфер отмены, потому что действие изменения текста/цвета фона возможно отменить. Мы посылаем сообщение EM_EMPTYUNDOBUFFER, чтобы сделать этого.

invoke SendMessage, hwndRichEdit, EM_EMPTYUNDOBUFFER, 0,0

После заполнения структуры CHARFORMAT, мы посылаем EM_SETCHARFORMAT richedit контролу, определяя SCF_ALL флаг в wParam, чтобы указать, что мы хотим, чтобы форматирование текста применилось ко всему тексту.

Обратите внимание, что, когда мы создавали richedit контрол, мы не определили его размер и позицию. Дело в том, что мы хотим, чтобы он закрыл всю клиентскую область родительского окна. Мы изменяем его размеры всякий раз, когда изменяется размер родительского окна.

.elseif uMsg== WM_SIZE
  Mov eax, lParam
  Mov edx, eax
  and eax, 0FFFFh
  Shr edx, 16
  invoke MoveWindow, hwndRichEdit, 0,0, eax, edx, TRUE

В вышеупомянутом фрагменте кода, мы используем новые размеры клиентской области в lParam, чтобы изменить размеры richedit контрола с помощью MoveWindow.

Когда пользователь кликает на строке меню Файл/Правка, мы обрабатываем сообщение WM_INITPOPUPMENU так, чтобы мы могли установить состояние некоторых пунктов в подменю перед отображением эго пользователю. Например, если файл уже открыт в richedit контроле, мы хотим отключить пункт открыть в подменю и включить пункт сохранить, сохранить как... и т.д.

В случае сострокой меню Файл, мы используем переменную FileOpened как флаг, чтобы определить, открыт ли уже файл. Если значение в этой переменной TRUE, то мы знаем, что файл уже открыт.

.elseif uMsg==WM_INITMENUPOPUP
  mov eax,lParam
  .if ax==0    ; меню Файл      
    .if FileOpened==TRUE   ; файл уже открыт
      invoke EnableMenuItem,wParam,IDM_OPEN,MF_GRAYED
      invoke EnableMenuItem,wParam,IDM_CLOSE,MF_ENABLED
      invoke EnableMenuItem,wParam,IDM_SAVE,MF_ENABLED
      invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_ENABLED
    .else
      invoke EnableMenuItem,wParam,IDM_OPEN,MF_ENABLED
      invoke EnableMenuItem,wParam,IDM_CLOSE,MF_GRAYED
      invoke EnableMenuItem,wParam,IDM_SAVE,MF_GRAYED
      invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_GRAYED
    .endif

Как вы можете заметить, если файл уже открыт, мы запрещаем пункты меню открытие и разрешаем пункты меню сохранение, и наоборот если FileOpened - FALSE.

В случае со строкой меню правка, мы сначала должны проверить состояние richedit контрола и буфера обмена.

invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0
.if eax==0    ; нет текста в буфере обмена
  invoke EnableMenuItem,wParam,IDM_PASTE,MF_GRAYED
.else
  invoke EnableMenuItem,wParam,IDM_PASTE,MF_ENABLED
.endif

Мы сначала проверяем, является ли текст в буфере обмена доступным, посылая сообщение EM_CANPASTE. Если текст доступен, SendMessage возвращает TRUE, и мы разрешаем пункт меню вставка, а если FALSE, то запрещаем.

invoke SendMessage,hwndRichEdit,EM_CANUNDO,0,0
.if eax==0
  invoke EnableMenuItem,wParam,IDM_UNDO,MF_GRAYED
.else
  invoke EnableMenuItem,wParam,IDM_UNDO,MF_ENABLED
.endif

Затем, мы проверяем, является ли буфер отмены пустым, посылая сообщение EM_CANUNDO. Если - не пустой, SendMessage возвращает TRUE, и мы разрешаем пункт меню отмена.

invoke SendMessage,hwndRichEdit,EM_CANREDO,0,0
.if eax==0
  invoke EnableMenuItem,wParam,IDM_REDO,MF_GRAYED
.else
  invoke EnableMenuItem,wParam,IDM_REDO,MF_ENABLED
.endif

Мы проверяем буфер Повтора, посылая richedit контролу сообщение EM_CANREDO. Если - не пустой, SendMessage возвращает TRUE, и мы разрешаем пункт меню повторить.

invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg
mov eax,chrg.cpMin
.if eax==chrg.cpMax    ; ничего не выделенно
  invoke EnableMenuItem,wParam,IDM_COPY,MF_GRAYED
  invoke EnableMenuItem,wParam,IDM_CUT,MF_GRAYED
  invoke EnableMenuItem,wParam,IDM_DELETE,MF_GRAYED
.else
  invoke EnableMenuItem,wParam,IDM_COPY,MF_ENABLED
  invoke EnableMenuItem,wParam,IDM_CUT,MF_ENABLED
  invoke EnableMenuItem,wParam,IDM_DELETE,MF_ENABLED
.endif

Наконец, мы проверяем, выделенно ли что-нибудь в richedit контроле, посылая сообщение EM_EXGETSEL. Это сообщение использует структуру CHARRANGE, которая определена следующим образом:

CHARRANGE STRUCT
    cpMin DWORD ?
    CpMax DWORD ?
CHARRANGE ENDS

CpMin содержит индекс позиции символа предшествующего первому символу в диапазоне.
CpMax содержит индекс позиции символа идущего после последнего символа в диапазоне.

После возвращения EM_EXGETSEL, структура CHARRANGE заполнена индексами позиции стартовой и конечной метками выделенного диапазона. Если ничего не выделенно, cpMin и cpMax идентичны и мы, запрещаем пункты меню вырезать/копировать/удалить.

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

invoke CreateFile,addr FileName,GENERIC_READ,
       FILE_SHARE_READ,NULL,OPEN_EXISTING,
       FILE_ATTRIBUTE_NORMAL,0
.if eax!=INVALID_HANDLE_VALUE
  mov hFile,eax
  mov editstream.dwCookie,eax
  mov editstream.pfnCallback,offset StreamInProc
  invoke SendMessage,hwndRichEdit,EM_STREAMIN,
         SF_TEXT,addr editstream

После того, как файл успешно открыт с CreateFile, мы заполняем структуру EDITSTREAM, подготавливаем к сообщению EM_STREAMIN. Мы выбираем послать хэндл открытого файла через член dwCookie и передать адрес потоковой функции в pfnCallback.

Потоковая процедура сама по себе простая.

StreamInProc proc hFile:DWORD, pBuffer:DWORD, 
             NumBytes:DWORD, pBytesRead:DWORD
  invoke ReadFile, hFile, pBuffer, NumBytes, pBytesRead, 0
  Xor eax, 1 
  Ret
StreamInProc endp

Вы можете заметить, что все параметры потоковой процедуры совершенно соответствуют ReadFile. И возвращаемое значение ReadFile - xor с 1 для, того чтобы, если она возвратит 1 (в случае успеха), фактическое значение, возвращенное в eax - 0 и наоборот.

invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
invoke CloseHandle,hFile
mov FileOpened,TRUE

После возврата EM_STREAMIN, это означает, что потоковая операция завершена. В действительности, мы должны проверить значение dwError члена структуры EDITSTREAM.

Richedit (и Edit) контролы поддерживают флаг, чтобы указать, изменялось ли их содержание. Мы можем получить значение этого флага, посылая им сообщение EM_GETMODIFY. SendMessage возвращает TRUE, если содержание изменялось. Так как загрузка текста в контрол, это - своего рода модификация. Мы должны установить флаг модификации в FALSE, посылая контролу сообщение EM_SETMODIFY с wParam == FALSE после того, как операция stream-in завершится. Мы немедленно закрываем файл и устанавливаем FileOpened в TRUE чтобы указывать, что файл был открыт.

Когда пользователь кликнет на пункт меню сохранить/сохранить как..., мы используем сообщение EM_STREAMOUT, чтобы вывести содержимое richedit контрола в файл. Как и функция stream-in, функции stream-out - простота сама по себе. Это совершенно соответствует WriteFile.

Текстовые операции такие как вырезать/копировать/вставить/восстановить/отменить, легко осуществимы, посылая richedit контролу сообщение WM_CUT/WM_COPY/WM_PASTE/WM_REDO/WM_UNDO соответственно.

Операции удаление/выделить всe сделаны следующим образом:

.elseif ax==IDM_DELETE
  invoke SendMessage,hwndRichEdit,EM_REPLACESEL,TRUE,0
.elseif ax==IDM_SELECTALL
  mov chrg.cpMin,0
  mov chrg.cpMax,-1
  invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg

Операция удаление работает с выделением. Я посылаю сообщение EM_REPLACESEL с NULL строкой, чтобы richedit контрол заменил выделенный текст пустой строкой.

Операция выделить всё сделана, посылая сообщение EM_EXSETSEL, установив cpMin == 0 и cpMax ==-1, что равносильно выделению всего текста.

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

Когда пользователь кликает на одной из палитры цветов, вызывается диалоговое окно выбора цвета. "Палитра цветов" - фактически статический элемент управления с флагом WS_BORDER и SS_NOTIFY. Статический элемент управления с флагом SS_NOTIFY уведомит его родительское окно с действиями мыши на нем, типа BN_CLICKED (STN_CLICKED). Это - уловка.

.elseif ax==IDC_BACKCOLORBOX
  invoke RtlZeroMemory,addr clr,sizeof clr
  mov clr.lStructSize,sizeof clr
  push hWnd
  pop clr.hwndOwner
  push hInstance
  pop clr.hInstance
  push BackgroundColor
  pop clr.rgbResult
  mov clr.lpCustColors,offset CustomColors
  mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT
  invoke ChooseColor,addr clr
.if eax!=0
  push clr.rgbResult
  pop BackgroundColor
  invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
  invoke InvalidateRect,eax,0,TRUE
.endif

Когда пользователь кликает на одной из палитры цветов, мы заполняем члены структуры CHOOSECOLOR и вызываем диалоговое окно выбора цвета ChooseColor. Если пользователь выбирает цвет, то это значение colorref возвращается в члене rgbResult, и мы сохраняем это значение в переменной BackgroundColor. После этого, мы вынуждаем перекрашивание на палитре цветов, вызывая InvalidateRect на хэндл палитры цветов. Палитра цветов посылает WM_CTLCOLORSTATIC сообщение своему родительскому окну.

 invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
 .if eax==lParam
   invoke CreateSolidBrush,BackgroundColor
   ret

Внутри обработчика WM_CTLCOLORSTATIC, мы сравниваем, хэндл статического элемента управления переданного в lParam> с обоими палитрами цветов. Если значение соответствует, мы создаем новую кисть, используя цвет из переменной и немедленно возвращаемся. Статический элемент управления будет использовать недавно созданную кисть, чтобы окрасить ее фон.

2002-2013 (c) wasm.ru