|
Мои программные модули Online - MemRich.pas
Описание: Содержит
примеры функций, использующих сообщения типа EM_ для работы с TMemo и TRichEdit. (Скачать).
unit MemRich;
{
Сообщения типа EM_ (например EM_CHARFROMPOS) для TMmemo и TRichEdit используются одинаковые,
но зачастую отсылаются по-разному и возвращают результат в разных форматах. Это важно помнить!!!!
Все приведённые здесь функции, использующие индексы строк и символов, базируются на индексации с 0, то есть индекс
первого символа 0, индекс первой строки 0.
Параметр iHandle:HWND должен содержать Memo.Handle или RichEdit.Handle
весь этот юнит - фактически просто пример использования в TMemo и TRichEdit следующих сообщений:
EM_CHARFROMPOS, EM_LINEFROMCHAR, EM_LINEINDEX, EM_POSFROMCHAR, EM_GETTHUM,
EM_SCROLLCARET, EM_LINESCROLL, WM_VSCROLL, EM_GETFIRSTVISIBLELINE
}
interface
uses Windows,Messages,SysUtils,StdCtrls,ComCtrls;
//Возвращает индексы (символа и строки) символа в мемо с указанными координатами в пикселях
//Координата 0,0 пикселей соответствует левому верхнему углу текстового поля
//Входные параметры: ptPos.Lo - координата X (пикселей), ptPos.Hi - Y
//Если в ptPos записать координаты, полученные так: Memo1.ScreenToClient(Mouse.CursorPos);
//То получим индекс-коодинаты символа, над которым сейчас находится курсор
//Выходные параметры: -1, если определить не удалось, иначе результата есть тоже LongRec,
//LongRec(result).Lo = Индекс символа от начала текста (0 - индекс первого)
//LongRec(result).Hi = Индекс строки (0 - первая)
//Если координаты лежат правее последнего символа строки, считается, они указывают на последний символ строки (символы перевода каретки при этом принадлежат следующей строке как бы)
//Если координаты лежат ниже последней строки, они указывают на последнюю строку и последний символ в ней (последними символами будут символы возврата каретки, если они присутствуют в последней строке)
//Так как LongRec содержит значения не более 65535, то при превышении этого значения оно начинается сначала, то есть символ 65536 будет показан как символ с Lo, равным 0, 65537 равным 1 и т.д.
//То же и со строками. НЕ проверял, но определённо строка 65536 будет иметь значение 0 в hi.
//Данная функция удобна в том числе для определения, на какой строке находится сейчас текст, если он был скроллирован
//Посредством RichMemoLineToChar (см. ниже) можно обойти ограничение Lo в 65535 символов, если использовать отсюда значение Hi и определить положение нужного символа в строке Hi от начала строки
function MemoCharPosFromPos(const Memo:Tmemo; ptPos:LongRec):integer;
//То же, что и MemoCharPosFromPos, но для TRichEdit. Однако возвращаемый результат содержит
//только индекс символа, но не строки, зато индекс возвращается в формате Integer, поэтому
//не происходит переполнения возвращаемого значения при индексах более 65535
//(Индекс линии же не проблема получить посредством RichMemoLineFromPos, и тоже без ограничения до 65535)
//Ещё одно из отличий - пиксельные координаты задаются в TPoint, а не в LongRec
function RichCharPosFromPos(const Rich:TRichEdit; ptPos: TPoint): Integer;
//Работает одинаково для TMemo и TRichEdit
//Возвращает индекс линии (первая = 0), которой принадлежит символ с указанным индексом
//Таким образом с её помощью можно получить и для TMemo строки с индексом большим чем 65535,
//При условии, что CharInd содержит правильный индекс этого символа TMemo(полученный иначе, чем через EM_CHARFROMPOS)
//Если CharInd = -1, возвращается линия, соответствующая SelStart (на которой находится курсор или с которой начинается выделенный текст
function RichMemoLineFromPos(const iHandle:HWND; CharInd:integer): Integer;
//Работает одинаково для TMemo и TRichEdit
//Возвращает индекс от начала текста первого символа линии с индексом iLine
//Если iLine = -1, возвращается индекс символа линии, соответствующей SelStart (на которой находится курсор или с которой начинается выделенный текст
Function RichMemoLineToChar(const iHandle:HWND; iLine:integer): Integer;
//Возвращает координаты в пикселях символа Tmemo с указанным индексом
//Координаты соответствуют левому верхнему углу области вывода символа на экране относительно
//врехнего левого угла текстового поля (не путать область вывода с самим символом - символ может быть и точкой
//но область экрана, отведённая под него, по высоте будет не меньше, чем у других символов в этой строке, координаты
//левого верхнего угла этой области и будут возвращены)
//Координаты могут быть и отрицательными, если текстовое окно скроллировано
//Возвращаемые значения:
//Smallint(LoWord(DWORD(result))) = Координата X символа от левого верхнего угла текстового окна TMemo
//Smallint(HiWord(DWORD(result))) = Координата Y символа от левого верхнего угла текстового окна TMemo
//Если координаты определить не удалось, возвращает -1 (так будет, если символа с указанным индексом не существует)
//Формат Smallint налагает ограничения на величины хранимых в нём значений, ограничивая их диапазоном -32768..32767, то есть если у TMemo
//(Lines.Count*высоту символа) > 32767 может происходить переполнение возвращаемых значений, т.е. возвращаемые координаты не всегда будут правильными
//То же, если имеются слишком длинные строки и отключен перенос слов (если суммарная ширина символов в какой-либо линии превышает > 32767)
Function MemoPosFromCharPos(const Memo:Tmemo; const CharInd:integer):integer;
//То же, что и MemoPosFromCharPos, но для TRichEdit. Однако это процедура, а не функция,
//результат она возвращает в структуре TPoint, результат не может быть не определён, поэтому если
//символа с указанным индексом не существует, указанным индексом будет считаться индекс последнего символа TRichEdit
//TPoint хранит данные в формате Integer, так что здесь проблем с переполнением не будет
Procedure RichPosFromCharPos(const Rich:TRichEdit; var ptPos: TPoint;const CharInd:integer);
//Возвращает смещение вертикального скроллинга в пикселях для TRichEdit
//(Для TMemo EM_GETTHUMB судя по всему не работает - пробовал по всякому, результата нулевой)
function RichGetVSroll(Rich:TRichEdit):integer;
//Скроллирует окно так, чтобы текущее положение каретки (курсора в тексте) стало видимым
//Если курсор находится ниже видимого окна, текст будет скроллирован так, что строка, на которой
//находится курсор, станет последней из полностью видимых, если курсор выше, строка где он находится,
//станет первой (верхней) из видимых, примерно то же и для горизонтального скроллинга - если курсор
//справа от видимого текста, текст будет скроллирован так, чтобы его позиция стала последней из
//видимых справа, если же курсор слева видимого текста, занимаемая им позиция станет Не крайней слева
//из видимых, а такой, чтобы оказаться приблизительно на расстоянии третьей части ширины текстового окна
//от левого края видимого текста.
//Возвращаемое значение: для TMemo = 1, для TRichEdit = 0
//Возвращаемое значение не зависит от того, был ли осуществлён скроллинг
function RichMemoScrollCaret(const iHandle:HWND):integer;
//Скорллирует текстовое окно на указанное в iChar число символов по горизонтали и указанное
//iLine число линий по вертикали. Работает по разному с TMemo и TRichEdit. У TRichEdit
//параметр iChar игнорируется и не оказывает никакого действия. У Tmemo смещение в точности
//на указанное в iChar число символов произойдёт только при условии, что используется шрифт
//с одинаковой шириной всех символов, иначе смещение произойдёт приблизительно на такое число символов
//Вертикальное скроллирование тоже отличается у TMemo и TRichEdit. У Tmemo верхняя строка
//всегда совпадает верхней частью с верхом текстового окна (т.е. верхняя строка по высоте всегда
//видна целиком), у TRichEdit такого не наблюдается, поэтому у TMemo вертикальный скроллинг
//произойдёт в точности на число строк, заданное в iLine. У TRichEdit можно сказать, произойдёт
//то же самое, но после скроллинга будет выполнено позиционирование верхней строки, так чтобы она совпала
//верней частью с верхом текстового окна (при условии, что они не совпадают, иначе ничего не произойдёт).
//Таким образом, если верхняя видимая срока после скроллинга видна лишь частично, не зависимо от того,
//какая её часть видна (пусть хоть всего 1 пиксель) произойдёт дополнительный скроллинг вверх, так чтобы
//она вся стала видимой по высоте. Причём указанное позиционирование произойдёт даже если
//iLine = 0. Не произойдёт его, только если оно не требуется (строка уже совпадает верхом с текстовым окном)
//или если текстовое окно скроллировано до конца вниз.
function RichMemeoLineScroll(const iHandle:HWND;
const iChar,iLine:integer):integer;
//Работает по разному с Tmemo и TRichEdit
//Вертикальный скроллинг на указанную Y-позицию. Позиция скроллинга будет установлена
//точно на заданную позицию (т.е. эта позиция совпадёт с верхом текстового окна), если это
//возможно, иначе на ближайшую из возможных
//У TMemo позиция YPos задаётся в строках, а у TRichEdit в пикселях
//Так как максимальным значением Word является 65535, скроллинг не может быть осуществлён
//далее чем на строку 65535 у TMemo или пиксель 65535 у TRichEdit
//Позиция YPos является абсолютной, т.е. это координата от начала (от 0), а не от текущей позиции
function RichMemoVScrollYPosAbs(const iHandle:HWND;const YPos:Word):integer;
//Вертикальный скроллинг Tmemo на линию (строку) с заданным индексом
//Не имеет ограничения в 65535 линий
//Позиция скроллинга будет установлена точно на заданную позицию т.е. эта позиция
//совпадёт с верхом текстового окна), если это возможно, иначе на ближайшую из возможных
//Возвращает индекс строки, на которую в действительности перешёл скроллинг (которая
//является верхней из отображаемых)
function MemoVScrollYPosAbsEx(Memo:TMemo;iLine:integer):integer;
//Возвращает True, если линия LineInd в TMemo является видимой
Function IsLineVisible(Memo:TMemo;const LineInd:integer):Boolean;
implementation
//Возвращает индексы (символа и строки) символа в мемо с указанными координатами в пикселях
//Координата 0,0 пикселей соответствует левому верхнему углу текстового поля
//Входные параметры: ptPos.Lo - координата X (пикселей), ptPos.Hi - Y
//Если в ptPos записать координаты, полученные так: Memo1.ScreenToClient(Mouse.CursorPos);
//То получим индекс-коодинаты символа, над которым сейчас находится курсор
//Выходные параметры: -1, если определить не удалось, иначе результата есть тоже LongRec,
//LongRec(result).Lo = Индекс символа от начала текста (0 - индекс первого)
//LongRec(result).Hi = Индекс строки (0 - первая)
//Если координаты лежат правее последнего символа строки, считается, они указывают на последний символ строки (символы перевода каретки при этом принадлежат следующей строке как бы)
//Если координаты лежат ниже последней строки, они указывают на последнюю строку и последний символ в ней (последними символами будут символы возврата каретки, если они присутствуют в последней строке)
//Так как LongRec содержит значения не более 65535, то при превышении этого значения оно начинается сначала, то есть символ 65536 будет показан как символ с Lo, равным 0, 65537 равным 1 и т.д.
//То же и со строками. НЕ проверял, но определённо строка 65536 будет иметь значение 0 в hi.
//Данная функция удобна в том числе для определения, на какой строке находится сейчас текст, если он был скроллирован
//Посредством RichMemoLineToChar (см. ниже) можно обойти ограничение Lo в 65535 символов, если использовать отсюда значение Hi и определить положение нужного символа в строке Hi от начала строки
function MemoCharPosFromPos(const Memo:Tmemo; ptPos:LongRec):integer;
begin
Result := SendMessage(Memo.Handle, EM_CHARFROMPOS,0, LPARAM(ptPos));
end;
//То же, что и MemoCharPosFromPos, но для TRichEdit. Однако возвращаемый результат содержит
//только индекс символа, но не строки, зато индекс возвращается в формате Integer, поэтому
//не происходит переполнения возвращаемого значения при индексах более 65535
//(Индекс линии же не проблема получить посредством RichLineFromPos, и тоже без ограничения до 65535)
//Ещё одно из отличий - пиксельные координаты задаются в TPoint, а не в LongRec
function RichCharPosFromPos(const Rich:TRichEdit; ptPos: TPoint): Integer;
begin
Result := SendMessage(Rich.Handle, EM_CHARFROMPOS, 0, LPARAM(@ptPos));
end;
//Работает одинаково для TMemo и TRichEdit
//Возвращает индекс линии (первая = 0), которой принадлежит символ с указанным индексом
//Таким образом с её помощью можно получить и для TMemo строки с индексом большим чем 65535,
//При условии, что CharInd содержит правильный индекс этого символа TMemo (полученный иначе, чем через EM_CHARFROMPOS)
//Если CharInd = -1, возвращается линия, соответствующая SelStart (на которой находится курсор или с которой начинается выделенный текст
function RichMemoLineFromPos(const iHandle:HWND; CharInd:integer): Integer;
begin
Result:= SendMessage(iHandle, EM_LINEFROMCHAR, WPARAM(CharInd), 0);
end;
//Работает одинаково для TMemo и TRichEdit
//Возвращает индекс от начала текста первого символа линии с индексом iLine
//Если iLine = -1, возвращается индекс символа линии, соответствующей SelStart (на которой находится курсор или с которой начинается выделенный текст
Function RichMemoLineToChar(const iHandle:HWND; iLine:integer): Integer;
begin
result:= SendMessage(iHandle,EM_LINEINDEX,WPARAM(iLine),0);
end;
//Возвращает координаты в пикселях символа Tmemo с указанным индексом
//Координаты соответствуют левому верхнему углу области вывода символа на экране относительно
//верхнего левого угла текстового поля (не путать область вывода с самим символом - символ может быть и точкой
//но область экрана, отведённая под него, по высоте будет не меньше, чем у других символов в этой строке, координаты
//левого верхнего угла этой области и будут возвращены)
//Координаты могут быть и отрицательными, если текстовое окно скроллировано
//Возвращаемые значения:
//Smallint(LoWord(DWORD(result))) = Координата X символа от левого верхнего угла текстового окна TMemo
//Smallint(HiWord(DWORD(result))) = Координата Y символа от левого верхнего угла текстового окна TMemo
//Если координаты определить не удалось, возвращает -1 (так будет, если символа с указанным индексом не существует)
//Формат Smallint налагает ограничения на величины хранимых в нём значений, ограничивая их диапазоном -32768..32767, то есть если у TMemo
//(Lines.Count*высоту символа) > 32767 может происходить переполнение возвращаемых значений, т.е. возвращаемые координаты не всегда будут правильными
//То же, если имеются слишком длинные строки и отключен перенос слов (если суммарная ширина символов в какой-либо линии превышает > 32767)
Function MemoPosFromCharPos(const Memo:Tmemo; const CharInd:integer):integer;
begin
result:= SendMessage(Memo.Handle, EM_POSFROMCHAR,CharInd,0);
end;
//То же, что и MemoPosFromCharPos, но для TRichEdit. Однако это процедура, а не функция,
//результат она возвращает в структуре TPoint, результат не может быть не определён, поэтому если
//символа с указанным индексом не существует, указанным индексом будет считаться индекс последнего символа TRichEdit
//TPoint хранит данные в формате Integer, так что здесь проблем с переполнением не будет
Procedure RichPosFromCharPos(const Rich:TRichEdit; var ptPos: TPoint;const CharInd:integer);
begin
SendMessage(Rich.Handle, EM_POSFROMCHAR,WPARAM(@ptPos),CharInd);
end;
//Возвращает смещение вертикального скроллинга в пикселях для TRichEdit
//(Для TMemo EM_GETTHUMB судя по всему не работает - пробовал по всякому, результата нулевой)
function RichGetVSroll(Rich:TRichEdit):integer;
begin
result:= SendMessage(Rich.Handle, EM_GETTHUMB,0,0);
end;
//Скроллирует окно так, чтобы текущее положение каретки (курсора в тексте) стало видимым
//Если курсор находится ниже видимого окна, текст будет скроллирован так, что строка, на которой
//находится курсор, станет последней из полностью видимых, если курсор выше, строка где он находится,
//станет первой (верхней) из видимых, примерно то же и для горизонтального скроллинга - если курсор
//справа от видимого текста, текст будет скроллирован так, чтобы его позиция стала последней из
//видимых справа, если же курсор слева видимого текста, занимаемая им позиция станет Не крайней слева
//из видимых, а такой, чтобы оказаться приблизительно на расстоянии третьей части ширины текстового окна
//от левого края видимого текста.
//Возвращаемое значение: для TMemo = 1, для TRichEdit = 0
//Возвращаемое значение не зависит от того, был ли осуществлён скроллинг
function RichMemoScrollCaret(const iHandle:HWND):integer;
begin
result:= SendMessage(iHandle,EM_SCROLLCARET,0,0);
end;
//Скорллирует текстовое окно на указанное в iChar число символов по горизонтали и указанное
//iLine число линий по вертикали. Работает по разному с TMemo и TRichEdit. У TRichEdit
//параметр iChar игнорируется и не оказывает никакого действия. У Tmemo смещение в точности
//на указанное в iChar число символов произойдёт только при условии, что используется шрифт
//с одинаковой шириной всех символов, иначе смещение произойдёт приблизительно на такое число символов
//Вертикальное скроллирование тоже отличается у TMemo и TRichEdit. У Tmemo верхняя строка
//всегда совпадает верхней частью с верхом текстового окна (т.е. верхняя строка по высоте всегда
//видна целиком), у TRichEdit такого не наблюдается, поэтому у TMemo вертикальный скроллинг
//произойдёт в точности на число строк, заданное в iLine. У TRichEdit можно сказать, произойдёт
//то же самое, но после скроллинга будет выполнено позиционирование верхней строки, так чтобы она совпала
//верней частью с верхом текстового окна (при условии, что они не совпадают, иначе ничего не произойдёт).
//Таким образом, если верхняя видимая срока после скроллинга видна лишь частично, не зависимо от того,
//какая её часть видна (пусть хоть всего 1 пиксель) произойдёт дополнительный скроллинг вверх, так чтобы
//она вся стала видимой по высоте. Причём указанное позиционирование произойдёт даже если
//iLine = 0. Не произойдёт его, только если оно не требуется (строка уже совпадает верхом с текстовым окном)
//или если текстовое окно скроллировано до конца вниз.
function RichMemeoLineScroll(const iHandle:HWND; const iChar,iLine:integer):integer;
begin
result:= SendMessage(iHandle, EM_LINESCROLL,IChar,iLine);
end;
//Работает по разному с Tmemo и TRichEdit
//Вертикальный скроллинг на указанную Y-позицию. Позиция скроллинга будет установлена
//точно на заданную позицию (т.е. эта позиция совпадёт с верхом текстового окна), если это
//возможно, иначе на ближайшую из возможных
//У TMemo позиция YPos задаётся в строках, а у TRichEdit в пикселях
//Так как максимальным значением Word является 65535, скроллинг не может быть осуществлён
//далее чем на строку 65535 у TMemo или пиксель 65535 у TRichEdit
//Позиция YPos является абсолютной, т.е. это координата от начала (от 0), а не от текущей позиции
function RichMemoVScrollYPosAbs(const iHandle:HWND;const YPos:Word):integer;
begin
result:= SendMessage(iHandle,
WM_VSCROLL,MakeWParam(SB_THUMBPOSITION, YPos), 0);
end;
//Вертикальный скроллинг Tmemo на линию (строку) с заданным индексом
//Не имеет ограничения в 65535 линий
//Позиция скроллинга будет установлена точно на заданную позицию т.е. эта позиция
//совпадёт с верхом текстового окна), если это возможно, иначе на ближайшую из возможных
//Возвращает индекс строки, на которую в действительности перешёл скроллинг (которая
//является верхней из отображаемых)
function MemoVScrollYPosAbsEx(Memo:TMemo;iLine:integer):integer;
begin
result:= SendMessage(Memo.Handle, EM_GETFIRSTVISIBLELINE,0, 0);
if result = iLine then exit;
result:= iLine - result;
SendMessage(Memo.Handle, EM_LINESCROLL,0,result);
result:= SendMessage(Memo.Handle, EM_GETFIRSTVISIBLELINE,0, 0);
end;
//Возвращает True, если линия LineInd в TMemo является видимой
Function IsLineVisible(Memo:TMemo;const LineInd:integer):Boolean;
var Lvis:Integer;
i:integer;
begin
result:= False;
LVis:= SendMessage(Memo.Handle, EM_GETFIRSTVISIBLELINE,0, 0);
if LVis > LineInd then exit
else if LVis < LineInd then begin
i:= SendMessage(Memo.Handle, EM_LINEINDEX,WPARAM(LVis+1),0);
i:= SendMessage(Memo.Handle, EM_POSFROMCHAR,i,0);
if i < 0 then exit
//Если размер окна мемо составляет всего 1 линию
else begin
i:= Smallint(HiWord(DWORD(i)));//Height строки текста
result:= ((Memo.ClientHeight div i) >= (LineInd-LVis));
end;
end else result:= True;
end;
end.
|
|