Win32 API. Урок 33. RichEdit Control: основы

Дата публикации 3 июн 2002

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. Однако, есть рабочий пример, который хорошо работает, я покажу Вам позже.

Код (Text):
  1.  
  2. .data
  3.   RichEditDLL db "RichEd20.dll", 0
  4.       .....
  5. .data?
  6.     HRichEditDLL dd?
  7. .code
  8.     invoke LoadLibrary, addr RichEditDLL
  9.     Mov hRichEditDLL, eax
  10.       ......
  11.     invoke FreeLibrary, hRichEditDLL
  12.  

Когда 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.

Код (Text):
  1.  
  2. .const
  3.     RichEditID equ 300
  4. .data
  5.     RichEditDLL db "RichEd20.dll", 0
  6.     RichEditClass db "RichEdit20A", 0
  7.       .....
  8. .data?
  9.     HRichEditDLL dd ?
  10.     HwndRichEdit dd ?
  11. .code
  12.    .....
  13.  invoke LoadLibrary, addr RichEditDLL
  14.  Mov hRichEditDLL, eax
  15.  invoke CreateWindowEx, 0, addr RichEditClass,\
  16.    WS_VISIBLE or ES_MULTILINE or WS_CHILD or WS_VSCROLL or WS_HSCROLL, \
  17.    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, \
  18.    hWnd, RichEditID, hInstance, 0
  19.  Mov hwndRichEdit, eax
  20.  

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

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

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

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

Код (Text):
  1.  
  2. invoke SendMessage, hwndRichEdit, EM_SETBKGNDCOLOR, 0,0FF0000h
  3.  

Устанавливая цвет текста, 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 особенности вам не нужны.

Код (Text):
  1.  
  2. CHARFORMATA STRUCT
  3.     CbSize DWORD ?
  4.     DwMask DWORD ?
  5.     DwEffects DWORD ?
  6.     YHeight DWORD ?
  7.     YOffset DWORD ?
  8.     CrTextColor COLORREF ?
  9.     BCharSet BYTE ?
  10.     BPitchAndFamily BYTE ?
  11.     SzFaceName BYTE LF_FACESIZE dup(?)
  12.     _wPad2 WORD ?
  13. CHARFORMATA ENDS
  14.  
Имя поляОписание
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 следующим образом:

Код (Text):
  1.  
  2. .data?
  3.   Cf CHARFORMAT < >
  4. ..
  5. .code
  6.   Mov cf.cbSize, sizeof cf
  7.   Mov cf.dwMask, CFM_COLOR
  8.   Mov cf.crTextColor, 0FF0000h
  9.   invoke SendMessage, hwndRichEdit, EM_SETCHARFORMAT, \
  10.   SCF_ALL, addr cf</b>
  11.  

Этот фрагмент кода устанавливает цвет текста 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, которая имеет следующее определение:

Код (Text):
  1.  
  2.  
  3. EDITSTREAM STRUCT
  4.   DwCookie DWORD ?
  5.   DwError DWORD ?
  6.   PfnCallback DWORD ?
  7. EDITSTREAM ENDS
  8.  

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

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

Код (Text):
  1.  
  2. EditStreamCallback proto dwCookie:DWORD,
  3.   PBuffer:DWORD,
  4.   NumBytes:DWORD,
  5.   PBytesTransferred:DWORD
  6.  

Вы должны создать функцию с вышеупомянутым прототипом в вашей программе. И затем передайте ее адрес с помощью 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 или выше.

Код (Text):
  1.  
  2. .386.model flat,stdcall
  3. option casemap:none
  4. include \masm32\include\windows.inc
  5. include \masm32\include\user32.inc
  6. include \masm32\include\comdlg32.inc
  7. include \masm32\include\gdi32.inc
  8. include \masm32\include\kernel32.inc
  9. includelib \masm32\lib\gdi32.lib
  10. includelib \masm32\lib\comdlg32.lib
  11. includelib \masm32\lib\user32.lib
  12. includelib \masm32\lib\kernel32.lib
  13.  
  14. WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
  15.  
  16. .const
  17. IDR_MAINMENU      equ 101
  18. IDM_OPEN          equ 40001
  19. IDM_SAVE          equ 40002
  20. IDM_CLOSE         equ 40003
  21. IDM_SAV           equ 40004
  22. IDM_EXIT          equ 40005
  23. IDM_COPY          equ 40006
  24. IDM_CUT           equ 40007
  25. IDM_PASTE         equ 40008
  26. IDM_DELETE        equ 40009
  27. IDM_SELECTALL     equ 40010
  28. IDM_OPTION        equ 40011
  29. IDM_UNDO          equ 40012
  30. IDM_REDO          equ 40013
  31. IDD_OPTIONDLG     equ 101
  32. IDC_BACKCOLORBOX  equ 1000
  33. IDC_TEXTCOLORBOX  equ 1001
  34.  
  35. RichEditID        equ 300
  36.  
  37. .data
  38. ClassName db "IczEditClass",0
  39. AppName db "IczEdit версия 1.0",0
  40. RichEditDLL db "riched20.dll",0
  41. RichEditClass db "RichEdit20A",0
  42. NoRichEdit db "
  43. ASMFilterString db "ASM Исходники (*.asm)",0,"*.asm",0
  44.                 db "Все файлы (*.*)",0,"*.*",0,0
  45. OpenFileFail db "Не могу окрыть файл",0
  46. WannaSave db "Данные были изменены. Вы хотите сохранить изменения?",0
  47. FileOpened dd FALSE
  48. BackgroundColor dd 0FFFFFFh ; по умолчанию - белый
  49. TextColor dd 0              ; по умолчанию - черный
  50.  
  51. .data?
  52. hInstance dd ?
  53. hRichEdit dd ?
  54. hwndRichEdit dd ?
  55. FileName db 256 dup(?)
  56. AlternateFileName db 256 dup(?)
  57. CustomColors dd 16 dup(?)
  58.  
  59. .code
  60. start:
  61.   invoke GetModuleHandle, NULL
  62.   mov    hInstance,eax
  63.   invoke LoadLibrary,addr RichEditDLL
  64.   .if eax!=0
  65.     mov hRichEdit,eax
  66.     invoke WinMain, hInstance,0,0, SW_SHOWDEFAULT
  67.     invoke FreeLibrary,hRichEdit
  68.   .else
  69.     invoke MessageBox,0,addr NoRichEdit,addr AppName, \
  70.            MB_OK or MB_ICONERROR
  71.   .endif
  72.   invoke ExitProcess,eax
  73.  
  74. WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD
  75.   LOCAL wc:WNDCLASSEX
  76.   LOCAL msg:MSG
  77.   LOCAL hwnd:DWORD
  78.   mov   wc.cbSize,SIZEOF WNDCLASSEX
  79.   mov   wc.style, CS_HREDRAW or CS_VREDRAW
  80.   mov   wc.lpfnWndProc, OFFSET WndProc
  81.   mov   wc.cbClsExtra,NULL
  82.   mov   wc.cbWndExtra,NULL
  83.   push  hInst
  84.   pop   wc.hInstance
  85.   mov   wc.hbrBackground,COLOR_WINDOW+1
  86.   mov   wc.lpszMenuName,IDR_MAINMENU
  87.   mov   wc.lpszClassName,OFFSET ClassName
  88.   invoke LoadIcon,NULL,IDI_APPLICATION
  89.   mov   wc.hIcon,eax
  90.   mov   wc.hIconSm,eax
  91.   invoke LoadCursor,NULL,IDC_ARROW
  92.   mov   wc.hCursor,eax
  93.   invoke RegisterClassEx, addr wc
  94.   INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
  95.          WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
  96.          CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
  97.          hInst,NULL
  98.   mov   hwnd,eax
  99.   invoke ShowWindow, hwnd,SW_SHOWNORMAL
  100.   invoke UpdateWindow, hwnd
  101.   .while TRUE
  102.     invoke GetMessage, ADDR msg,0,0,0
  103.     .break .if (!eax)
  104.     invoke TranslateMessage, ADDR msg
  105.     invoke DispatchMessage, ADDR msg
  106.   .endw
  107.   mov   eax,msg.wParam
  108.   ret
  109. WinMain endp
  110.  
  111. StreamInProc proc hFile:DWORD,pBuffer:DWORD, \
  112.                   NumBytes:DWORD, pBytesRead:DWORD
  113.   invoke ReadFile,hFile,pBuffer,NumBytes,pBytesRead,0
  114.   xor eax,1
  115.   ret
  116. StreamInProc endp
  117.  
  118. StreamOutProc proc hFile:DWORD,pBuffer:DWORD, \
  119.                    NumBytes:DWORD, pBytesWritten:DWORD
  120.   invoke WriteFile,hFile,pBuffer,NumBytes,pBytesWritten,0
  121.   xor eax,1
  122.   ret
  123. StreamOutProc endp
  124.  
  125. CheckModifyState proc hWnd:DWORD
  126.   invoke SendMessage,hwndRichEdit,EM_GETMODIFY,0,0
  127.   .if eax!=0
  128.     invoke MessageBox,hWnd,addr WannaSave, \
  129.            addr AppName,MB_YESNOCANCEL
  130.     .if eax==IDYES
  131.       invoke SendMessage,hWnd,WM_COMMAND,IDM_SAVE,0
  132.     .elseif eax==IDCANCEL
  133.       mov eax,FALSE
  134.       ret
  135.     .endif
  136.   .endif
  137.   mov eax,TRUE
  138.   ret
  139. CheckModifyState endp
  140.  
  141. SetColor proc
  142.   LOCAL cfm:CHARFORMAT
  143.   invoke SendMessage,hwndRichEdit,EM_SETBKGNDCOLOR, \
  144.          0,BackgroundColor
  145.   invoke RtlZeroMemory,addr cfm,sizeof cfm
  146.   mov cfm.cbSize,sizeof cfm
  147.   mov cfm.dwMask,CFM_COLOR
  148.   push TextColor
  149.   pop cfm.crTextColor
  150.   invoke SendMessage,hwndRichEdit,EM_SETCHARFORMAT, \
  151.          SCF_ALL,addr cfm
  152.   ret
  153. SetColor endp
  154.  
  155. OptionProc proc hWnd:DWORD, uMsg:DWORD, \
  156.                 wParam:DWORD, lParam:DWORD
  157.   LOCAL clr:CHOOSECOLOR
  158.   .if uMsg==WM_INITDIALOG
  159.   .elseif uMsg==WM_COMMAND
  160.     mov eax,wParam
  161.     shr eax,16
  162.     .if ax==BN_CLICKED
  163.       mov eax,wParam
  164.       .if ax==IDCANCEL
  165.         invoke SendMessage,hWnd,WM_CLOSE,0,0
  166.       .elseif ax==IDC_BACKCOLORBOX
  167.         invoke RtlZeroMemory,addr clr,sizeof clr
  168.         mov clr.lStructSize,sizeof clr
  169.         push hWnd
  170.         pop clr.hwndOwner
  171.         push hInstance
  172.         pop clr.hInstance
  173.         push BackgroundColor
  174.         pop clr.rgbResult
  175.         mov clr.lpCustColors,offset CustomColors
  176.         mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT
  177.         invoke ChooseColor,addr clr
  178.         .if eax!=0
  179.           push clr.rgbResult
  180.           pop BackgroundColor
  181.           invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
  182.           invoke InvalidateRect,eax,0,TRUE
  183.         .endif
  184.       .elseif ax==IDC_TEXTCOLORBOX
  185.         invoke RtlZeroMemory,addr clr,sizeof clr
  186.         mov clr.lStructSize,sizeof clr
  187.         push hWnd
  188.         pop clr.hwndOwner
  189.         push hInstance
  190.         pop clr.hInstance
  191.         push TextColor
  192.         pop clr.rgbResult
  193.         mov clr.lpCustColors,offset CustomColors
  194.         mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT
  195.         invoke ChooseColor,addr clr
  196.         .if eax!=0
  197.           push clr.rgbResult
  198.           pop TextColor
  199.           invoke GetDlgItem,hWnd,IDC_TEXTCOLORBOX
  200.           invoke InvalidateRect,eax,0,TRUE
  201.         .endif
  202.       .elseif ax==IDOK
  203.         ;====================================================
  204.         ; Сохраните состояние richedit контрола потому,
  205.         ; что изменение цвета текста изменяет это состояние.
  206.         ;====================================================
  207.         invoke SendMessage,hwndRichEdit,EM_GETMODIFY,0,0
  208.         push eax
  209.         invoke SetColor
  210.         pop eax
  211.         invoke SendMessage,hwndRichEdit,EM_SETMODIFY,eax,0
  212.         invoke EndDialog,hWnd,0
  213.       .endif
  214.     .endif
  215.   .elseif uMsg==WM_CTLCOLORSTATIC
  216.     invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
  217.     .if eax==lParam
  218.       invoke CreateSolidBrush,BackgroundColor      
  219.       ret
  220.     .else
  221.       invoke GetDlgItem,hWnd,IDC_TEXTCOLORBOX
  222.       .if eax==lParam
  223.         invoke CreateSolidBrush,TextColor
  224.         ret
  225.       .endif
  226.     .endif
  227.     mov eax,FALSE
  228.     ret
  229.   .elseif uMsg==WM_CLOSE
  230.     invoke EndDialog,hWnd,0
  231.   .else
  232.     mov eax,FALSE
  233.     ret
  234.   .endif
  235.   mov eax,TRUE
  236.   ret
  237. OptionProc endp
  238.  
  239. WndProc proc hWnd:DWORD, uMsg:DWORD, \
  240.              wParam:DWORD, lParam:DWORD
  241.   LOCAL chrg:CHARRANGE
  242.   LOCAL ofn:OPENFILENAME
  243.   LOCAL buffer[256]:BYTE
  244.   LOCAL editstream:EDITSTREAM
  245.   LOCAL hFile:DWORD
  246.   .if uMsg==WM_CREATE
  247.    invoke CreateWindowEx,WS_EX_CLIENTEDGE,addr RichEditClass,0,\
  248.           WS_CHILD or WS_VISIBLE or ES_MULTILINE or\
  249.           WS_VSCROLL or WS_HSCROLL or ES_NOHIDESEL,\
  250.           CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,\
  251.           CW_USEDEFAULT,hWnd,RichEditID,hInstance,0
  252.     mov hwndRichEdit,eax
  253.     ;==================================================
  254.     ; Установка предела текста. По умолчанию - 64K
  255.     ;==================================================
  256.     invoke SendMessage,hwndRichEdit,EM_LIMITTEXT,-1,0
  257.     ;==================================================
  258.     ; установка цвета текста/фона
  259.     ;==================================================
  260.     invoke SetColor
  261.     invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
  262.     invoke SendMessage,hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0
  263.   .elseif uMsg==WM_INITMENUPOPUP
  264.     mov eax,lParam
  265.     .if ax==0    ; меню Файл      
  266.       .if FileOpened==TRUE  ; a file is already opened
  267.         invoke EnableMenuItem,wParam,IDM_OPEN,MF_GRAYED
  268.         invoke EnableMenuItem,wParam,IDM_CLOSE,MF_ENABLED
  269.         invoke EnableMenuItem,wParam,IDM_SAVE,MF_ENABLED
  270.         invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_ENABLED
  271.       .else
  272.         invoke EnableMenuItem,wParam,IDM_OPEN,MF_ENABLED
  273.         invoke EnableMenuItem,wParam,IDM_CLOSE,MF_GRAYED
  274.         invoke EnableMenuItem,wParam,IDM_SAVE,MF_GRAYED
  275.         invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_GRAYED
  276.       .endif
  277.     .elseif ax==1  ; edit menu
  278.       ;=====================================================
  279.       ; проверьте есть ли текст в буфере обмена, если есть
  280.       ; мы активизируем в меню пункт "Вставить".
  281.       ;=====================================================
  282.       invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0
  283.       .if eax==0    ; no text in the clipboard
  284.         invoke EnableMenuItem,wParam,IDM_PASTE,MF_GRAYED
  285.       .else
  286.         invoke EnableMenuItem,wParam,IDM_PASTE,MF_ENABLED
  287.       .endif
  288.       ;=====================================================
  289.       ; Проверьте, является ли очередь отмены пустой
  290.       ;=====================================================
  291.       invoke SendMessage,hwndRichEdit,EM_CANUNDO,0,0
  292.       .if eax==0
  293.         invoke EnableMenuItem,wParam,IDM_UNDO,MF_GRAYED
  294.       .else
  295.         invoke EnableMenuItem,wParam,IDM_UNDO,MF_ENABLED
  296.       .endif
  297.       ;=====================================================
  298.       ; Проверьте, является ли очередь повтора пустой
  299.       ;=====================================================
  300.       invoke SendMessage,hwndRichEdit,EM_CANREDO,0,0
  301.       .if eax==0
  302.         invoke EnableMenuItem,wParam,IDM_REDO,MF_GRAYED
  303.       .else
  304.         invoke EnableMenuItem,wParam,IDM_REDO,MF_ENABLED
  305.       .endif
  306.       ;=====================================================
  307.       ; Проверьте, выделено ли что-нибудь в richedit контроле.
  308.       ; Если да, мы активизируем пункты меню вырезать/
  309.       ; копировать/удалить
  310.       ;======================================================
  311.       invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg
  312.       mov eax,chrg.cpMin
  313.       .if eax==chrg.cpMax    ; no current selection
  314.         invoke EnableMenuItem,wParam,IDM_COPY,MF_GRAYED
  315.         invoke EnableMenuItem,wParam,IDM_CUT,MF_GRAYED
  316.         invoke EnableMenuItem,wParam,IDM_DELETE,MF_GRAYED
  317.       .else
  318.         invoke EnableMenuItem,wParam,IDM_COPY,MF_ENABLED
  319.         invoke EnableMenuItem,wParam,IDM_CUT,MF_ENABLED
  320.         invoke EnableMenuItem,wParam,IDM_DELETE,MF_ENABLED
  321.       .endif
  322.     .endif
  323.   .elseif uMsg==WM_COMMAND
  324.     .if lParam==0    ; menu commands
  325.       mov eax,wParam
  326.       .if ax==IDM_OPEN
  327.         invoke RtlZeroMemory,addr ofn,sizeof ofn
  328.         mov ofn.lStructSize,sizeof ofn
  329.         push hWnd
  330.         pop ofn.hwndOwner
  331.         push hInstance
  332.         pop ofn.hInstance
  333.         mov ofn.lpstrFilter,offset ASMFilterString
  334.         mov ofn.lpstrFile,offset FileName
  335.         mov byte ptr [FileName],0
  336.         mov ofn.nMaxFile,sizeof FileName
  337.         mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST
  338.         invoke GetOpenFileName,addr ofn
  339.         .if eax!=0
  340.           invoke CreateFile,addr FileName,GENERIC_READ,\
  341.               FILE_SHARE_READ,NULL,OPEN_EXISTING,\
  342.               FILE_ATTRIBUTE_NORMAL,0
  343.           .if eax!=INVALID_HANDLE_VALUE
  344.             mov hFile,eax
  345.             ;==============================================
  346.             ; stream the text into the richedit control
  347.             ;==============================================
  348.             mov editstream.dwCookie,eax
  349.             mov editstream.pfnCallback,offset StreamInProc
  350.             invoke SendMessage,hwndRichEdit,EM_STREAMIN,\
  351.                  SF_TEXT,addr editstream
  352.             ;==============================================
  353.             ; Initialize the modify state to false
  354.             ;==============================================
  355.             invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
  356.             invoke CloseHandle,hFile
  357.             mov FileOpened,TRUE
  358.           .else
  359.             invoke MessageBox,hWnd,addr OpenFileFail,\
  360.                 addr AppName,MB_OK or MB_ICONERROR
  361.           .endif
  362.         .endif
  363.       .elseif ax==IDM_CLOSE
  364.         invoke CheckModifyState,hWnd
  365.         .if eax==TRUE
  366.           invoke SetWindowText,hwndRichEdit,0
  367.           mov FileOpened,FALSE
  368.         .endif
  369.       .elseif ax==IDM_SAVE
  370.         invoke CreateFile,addr FileName,GENERIC_WRITE, \
  371.             FILE_SHARE_READ,NULL,\
  372.             CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
  373.         .if eax!=INVALID_HANDLE_VALUE
  374. @@:        
  375.           mov hFile,eax
  376.           ;=========================================
  377.           ; stream the text to the file
  378.           ;=========================================  
  379.           mov editstream.dwCookie,eax
  380.           mov editstream.pfnCallback,offset StreamOutProc
  381.           invoke SendMessage,hwndRichEdit,EM_STREAMOUT, \
  382.                  SF_TEXT, addr editstream
  383.           ;=========================================
  384.           ; Initialize the modify state to false
  385.           ;=========================================
  386.           invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
  387.           invoke CloseHandle,hFile
  388.         .else
  389.           invoke MessageBox,hWnd,addr OpenFileFail,addr AppName,\
  390.               MB_OK or MB_ICONERROR
  391.         .endif
  392.       .elseif ax==IDM_COPY
  393.         invoke SendMessage,hwndRichEdit,WM_COPY,0,0
  394.       .elseif ax==IDM_CUT
  395.         invoke SendMessage,hwndRichEdit,WM_CUT,0,0
  396.       .elseif ax==IDM_PASTE
  397.         invoke SendMessage,hwndRichEdit,WM_PASTE,0,0
  398.       .elseif ax==IDM_DELETE
  399.         invoke SendMessage,hwndRichEdit,EM_REPLACESEL,TRUE,0
  400.       .elseif ax==IDM_SELECTALL
  401.         mov chrg.cpMin,0
  402.         mov chrg.cpMax,-1
  403.         invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg
  404.       .elseif ax==IDM_UNDO
  405.         invoke SendMessage,hwndRichEdit,EM_UNDO,0,0
  406.       .elseif ax==IDM_REDO
  407.         invoke SendMessage,hwndRichEdit,EM_REDO,0,0
  408.       .elseif ax==IDM_OPTION
  409.         invoke DialogBoxParam,hInstance,IDD_OPTIONDLG,hWnd,addr OptionProc,0
  410.       .elseif ax==IDM_SAVEAS
  411.         invoke RtlZeroMemory,addr ofn,sizeof ofn
  412.         mov ofn.lStructSize,sizeof ofn
  413.         push hWnd
  414.         pop ofn.hwndOwner
  415.         push hInstance
  416.         pop ofn.hInstance
  417.         mov ofn.lpstrFilter,offset ASMFilterString
  418.         mov ofn.lpstrFile,offset AlternateFileName
  419.         mov byte ptr [AlternateFileName],0
  420.         mov ofn.nMaxFile,sizeof AlternateFileName
  421.         mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY
  422.             or OFN_PATHMUSTEXIST
  423.         invoke GetSaveFileName,addr ofn
  424.         .if eax!=0
  425.           invoke CreateFile,addr AlternateFileName,GENERIC_WRITE,\
  426.               FILE_SHARE_READ,NULL,CREATE_ALWAYS,\
  427.               FILE_ATTRIBUTE_NORMAL,0
  428.           .if eax!=INVALID_HANDLE_VALUE
  429.             jmp @B
  430.           .endif
  431.         .endif
  432.       .elseif ax==IDM_EXIT
  433.         invoke SendMessage,hWnd,WM_CLOSE,0,0
  434.       .endif
  435.     .endif
  436.   .elseif uMsg==WM_CLOSE
  437.     invoke CheckModifyState,hWnd
  438.     .if eax==TRUE
  439.       invoke DestroyWindow,hWnd
  440.     .endif
  441.   .elseif uMsg==WM_SIZE
  442.     mov eax,lParam
  443.     mov edx,eax
  444.     and eax,0FFFFh
  445.     shr edx,16
  446.     invoke MoveWindow,hwndRichEdit,0,0,eax,edx,TRUE    
  447.   .elseif uMsg==WM_DESTROY
  448.     invoke PostQuitMessage,NULL
  449.   .else
  450.     invoke DefWindowProc,hWnd,uMsg,wParam,lParam    
  451.     ret
  452.   .endif
  453.   xor eax,eax
  454.   ret
  455. WndProc endp
  456. end start
  457.  
  458. ;=============================================
  459. ; Файл ресурсов
  460. ;=============================================
  461. #include "resource.h"
  462. #define IDR_MAINMENU                    101
  463. #define IDD_OPTIONDLG                   101
  464. #define IDC_BACKCOLORBOX                1000
  465. #define IDC_TEXTCOLORBOX                1001
  466. #define IDM_OPEN                        40001
  467. #define IDM_SAVE                        40002
  468. #define IDM_CLOSE                       40003
  469. #define IDM_SAVEAS                      40004
  470. #define IDM_EXIT                        40005
  471. #define IDM_COPY                        40006
  472. #define IDM_CUT                         40007
  473. #define IDM_PASTE                       40008
  474. #define IDM_DELETE                      40009
  475. #define IDM_SELECTALL                   40010
  476. #define IDM_OPTION                      40011
  477. #define IDM_UNDO                        40012
  478. #define IDM_REDO                        40013
  479.  
  480. IDR_MAINMENU MENU DISCARDABLE
  481. BEGIN
  482.     POPUP "&Файл"
  483.     BEGIN
  484.         MENUITEM "&Открыть...",        IDM_OPEN
  485.         MENUITEM "&Закрыть",           IDM_CLOSE
  486.         MENUITEM "&Сохранить",         IDM_SAVE
  487.         MENUITEM "Сохранить &как...",  IDM_SAVEAS
  488.         MENUITEM SEPARATOR
  489.         MENUITEM "В&ыход",             IDM_EXIT
  490.     END
  491.     POPUP "&Правка"
  492.     BEGIN
  493.         MENUITEM "&Отменить",          IDM_UNDO
  494.         MENUITEM "&Повторить",         IDM_REDO
  495.         MENUITEM "&Копировать",        IDM_COPY
  496.         MENUITEM "&Вырезать",          IDM_CUT
  497.         MENUITEM "Вст&авить",          IDM_PASTE
  498.         MENUITEM SEPARATOR
  499.         MENUITEM "&Удалить",           IDM_DELETE
  500.         MENUITEM SEPARATOR
  501.         MENUITEM "В&ыделить все",      IDM_SELECTALL
  502.     END
  503.     MENUITEM "П&араметры",             IDM_OPTION
  504. END
  505.  
  506. IDD_OPTIONDLG DIALOG DISCARDABLE  0, 0, 183, 54
  507. STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION
  508.     | WS_SYSMENU | DS_CENTER
  509. CAPTION "Options"
  510. FONT 8, "MS Sans Serif"
  511. BEGIN
  512.     DEFPUSHBUTTON   "OK",IDOK,137,7,39,14
  513.     PUSHBUTTON      "Отмена",IDCANCEL,137,25,39,14
  514.     GROUPBOX        "",IDC_STATIC,5,0,124,49
  515.     LTEXT           "Цвет фона:",IDC_STATIC,20,14,60,8
  516.     LTEXT  "",IDC_BACKCOLORBOX,85,11,28,14,SS_NOTIFY | WS_BORDER
  517.     LTEXT  "Цвет текста:",IDC_STATIC,20,33,35,8
  518.     LTEXT  "",IDC_TEXTCOLORBOX,85,29,28,14,SS_NOTIFY | WS_BORDER
  519. END
  520.  

АНАЛИЗ

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

Код (Text):
  1.  
  2. invoke LoadLibrary, addr RichEditDLL
  3. .if eax!=0
  4.   Mov hRichEdit, eax
  5.   invoke WinMain, hInstance, 0,0, SW_SHOWDEFAULT
  6.   invoke FreeLibrary, hRichEdit
  7. .else
  8.   invoke MessageBox, 0, addr NoRichEdit, addr AppName, \
  9.          MB_OK or MB_ICONERROR
  10. .endif
  11.   invoke ExitProcess, eax
  12.  

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

Код (Text):
  1.  
  2. invoke CreateWindowEx, WS_EX_CLIENTEDGE, addr RichEditClass, 0,\
  3.   WS_CHILD or WS_VISIBLE or ES_MULTILINE or WS_VSCROLL or
  4.   WS_HSCROLL or ES_NOHIDESEL,\
  5.   CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  6.   CW_USEDEFAULT, hWnd, RichEditID,\
  7.   hInstance, 0
  8. Mov hwndRichEdit, eax
  9.  

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

Код (Text):
  1.  
  2. invoke SendMessage, hwndRichEdit, EM_LIMITTEXT,-1,0
  3.  

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

Код (Text):
  1.  
  2. invoke SetColor
  3.  

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

Код (Text):
  1.  
  2. SetColor proc
  3.   LOCAL cfm:CHARFORMAT
  4.   invoke SendMessage, hwndRichEdit, EM_SETBKGNDCOLOR, \
  5.          0, BackgroundColor
  6.  

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

Код (Text):
  1.  
  2. invoke RtlZeroMemory, addr cfm, sizeof cfm
  3. Mov cfm.cbSize, sizeof cfm
  4. Mov cfm.dwMask, CFM_COLOR
  5. push TextColor
  6. pop cfm.crTextColor</b>
  7.  

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

Код (Text):
  1.  
  2. invoke SendMessage, hwndRichEdit, EM_SETCHARFORMAT, \
  3. SCF_ALL, addr cfm
  4. Ret
  5. SetColor endp
  6.  

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

Код (Text):
  1.  
  2. invoke SendMessage, hwndRichEdit, EM_EMPTYUNDOBUFFER, 0,0
  3.  

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

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

Код (Text):
  1.  
  2. .elseif uMsg== WM_SIZE
  3.   Mov eax, lParam
  4.   Mov edx, eax
  5.   and eax, 0FFFFh
  6.   Shr edx, 16
  7.   invoke MoveWindow, hwndRichEdit, 0,0, eax, edx, TRUE
  8.  

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

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

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

Код (Text):
  1.  
  2. .elseif uMsg==WM_INITMENUPOPUP
  3.   mov eax,lParam
  4.   .if ax==0    ; меню Файл      
  5.     .if FileOpened==TRUE   ; файл уже открыт
  6.       invoke EnableMenuItem,wParam,IDM_OPEN,MF_GRAYED
  7.       invoke EnableMenuItem,wParam,IDM_CLOSE,MF_ENABLED
  8.       invoke EnableMenuItem,wParam,IDM_SAVE,MF_ENABLED
  9.       invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_ENABLED
  10.     .else
  11.       invoke EnableMenuItem,wParam,IDM_OPEN,MF_ENABLED
  12.       invoke EnableMenuItem,wParam,IDM_CLOSE,MF_GRAYED
  13.       invoke EnableMenuItem,wParam,IDM_SAVE,MF_GRAYED
  14.       invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_GRAYED
  15.     .endif
  16.  

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

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

Код (Text):
  1.  
  2. invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0
  3. .if eax==0    ; нет текста в буфере обмена
  4.   invoke EnableMenuItem,wParam,IDM_PASTE,MF_GRAYED
  5. .else
  6.   invoke EnableMenuItem,wParam,IDM_PASTE,MF_ENABLED
  7. .endif
  8.  

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

Код (Text):
  1.  
  2. invoke SendMessage,hwndRichEdit,EM_CANUNDO,0,0
  3. .if eax==0
  4.   invoke EnableMenuItem,wParam,IDM_UNDO,MF_GRAYED
  5. .else
  6.   invoke EnableMenuItem,wParam,IDM_UNDO,MF_ENABLED
  7. .endif
  8.  

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

Код (Text):
  1.  
  2. invoke SendMessage,hwndRichEdit,EM_CANREDO,0,0
  3. .if eax==0
  4.   invoke EnableMenuItem,wParam,IDM_REDO,MF_GRAYED
  5. .else
  6.   invoke EnableMenuItem,wParam,IDM_REDO,MF_ENABLED
  7. .endif
  8.  

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

Код (Text):
  1.  
  2. invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg
  3. mov eax,chrg.cpMin
  4. .if eax==chrg.cpMax    ; ничего не выделенно
  5.   invoke EnableMenuItem,wParam,IDM_COPY,MF_GRAYED
  6.   invoke EnableMenuItem,wParam,IDM_CUT,MF_GRAYED
  7.   invoke EnableMenuItem,wParam,IDM_DELETE,MF_GRAYED
  8. .else
  9.   invoke EnableMenuItem,wParam,IDM_COPY,MF_ENABLED
  10.   invoke EnableMenuItem,wParam,IDM_CUT,MF_ENABLED
  11.   invoke EnableMenuItem,wParam,IDM_DELETE,MF_ENABLED
  12. .endif
  13.  

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

Код (Text):
  1.  
  2. CHARRANGE STRUCT
  3.     cpMin DWORD ?
  4.     CpMax DWORD ?
  5. CHARRANGE ENDS
  6.  

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

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

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

Код (Text):
  1.  
  2. invoke CreateFile,addr FileName,GENERIC_READ,
  3.        FILE_SHARE_READ,NULL,OPEN_EXISTING,
  4.        FILE_ATTRIBUTE_NORMAL,0
  5. .if eax!=INVALID_HANDLE_VALUE
  6.   mov hFile,eax
  7.   mov editstream.dwCookie,eax
  8.   mov editstream.pfnCallback,offset StreamInProc
  9.   invoke SendMessage,hwndRichEdit,EM_STREAMIN,
  10.          SF_TEXT,addr editstream
  11.  

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

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

Код (Text):
  1.  
  2. StreamInProc proc hFile:DWORD, pBuffer:DWORD,
  3.              NumBytes:DWORD, pBytesRead:DWORD
  4.   invoke ReadFile, hFile, pBuffer, NumBytes, pBytesRead, 0
  5.   Xor eax, 1
  6.   Ret
  7. StreamInProc endp
  8.  

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

Код (Text):
  1.  
  2. invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
  3. invoke CloseHandle,hFile
  4. mov FileOpened,TRUE
  5.  

После возврата 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 сделаны следующим образом:

Код (Text):
  1.  
  2. .elseif ax==IDM_DELETE
  3.   invoke SendMessage,hwndRichEdit,EM_REPLACESEL,TRUE,0
  4. .elseif ax==IDM_SELECTALL
  5.   mov chrg.cpMin,0
  6.   mov chrg.cpMax,-1
  7.   invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg
  8.  

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

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

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

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

Код (Text):
  1.  
  2. .elseif ax==IDC_BACKCOLORBOX
  3.   invoke RtlZeroMemory,addr clr,sizeof clr
  4.   mov clr.lStructSize,sizeof clr
  5.   push hWnd
  6.   pop clr.hwndOwner
  7.   push hInstance
  8.   pop clr.hInstance
  9.   push BackgroundColor
  10.   pop clr.rgbResult
  11.   mov clr.lpCustColors,offset CustomColors
  12.   mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT
  13.   invoke ChooseColor,addr clr
  14. .if eax!=0
  15.   push clr.rgbResult
  16.   pop BackgroundColor
  17.   invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
  18.   invoke InvalidateRect,eax,0,TRUE
  19. .endif
  20.  

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

Код (Text):
  1.  
  2.  invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
  3.  .if eax==lParam
  4.    invoke CreateSolidBrush,BackgroundColor
  5.    ret
  6.  

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


0 4.326
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532