Компоненты FindDialog и ReplaceDialog, вызывающие диалоги поиска и замены фрагментов текста (рис. 8.12 и 8.13), очень похожи и имеют одинаковые свойства, кроме одного, задающего заменяющий текст в компоненте ReplaceDialog. Такое сходство не удивительно, поскольку ReplaceDialog — производный класс от FindDialog.
| Рис. 8.12 Диалоговое окно поиска фрагмента текста | ![]() |
| Рис. 8.13 Диалоговое окно замены фрагмента текста | ![]() |
Компоненты имеют следующие основные свойства:
| FindText | Текст, заданный пользователем для поиска или замены. Программно может быть установлен как начальное значение, предлагаемое пользователю |
| ReplaceText | Только в компоненте ReplaceDialog — текст, который должен заменять FindText |
| Position | Позиция левого верхнего угла диалогового окна, заданная типом TPoint — записью, содержащей поля X (экранная координата по горизонтали) и Y (экранная координата по вертикали) |
| Left | Координата левого края диалогового окна, то же, что Position.X |
| Top | Координата верхнего края диалогового окна, то же, что Position.Y |
| Options | Множество опций |
Последний параметр Options — может содержать следующие свойства:
| frDisableMatchCase | Делает недоступным индикатор С учетом регистрав диалоговом окне |
| frDisableUpDown | Делает недоступными в диалоговом окне кнопки Вверхи Внизгруппы Направление, определяющие направление поиска |
| frDisableWholeWord | Делает недоступным индикатор Только слово целикомв диалоговом окне |
| frDown | Выбирает кнопку Внизгруппы Направлениепри открытии диалогового окна. Если эта опция не установлена, то выбирается кнопка Вверх |
| frFindNext | Эта опция включается автоматически, когда пользователь в диалоговом окне щелкает на кнопке Найти далее, и выключается при закрытии диалога |
| frHideMatchCase | Удаляет индикатор С учетом регистраиз диалогового окна |
| frHideWholeWord | Удаляет индикатор Только слово целикомиз диалогового окна |
| frHideUpDown | Удаляет кнопки Вверхи Внизиз диалогового окна |
| frMatchCase | Этот флаг включается и выключается, если пользователь включает и выключает опцию С учетом регистрав диалоговом окне. Можно установить эту опцию по умолчанию во время проектирования, чтобы при открытии диалога она была включена |
| frReplace | Применяется только для ReplaceDialog. Этот флаг устанавливается системой, чтобы показать, что текущее (и только текущее) найденное значение FindText должно быть заменено значением ReplaceText |
| frReplaceAll | Применяется только для ReplaceDialog. Этот флаг устанавливается системой, чтобы показать, что все найденные значения FindText должны быть заменены значениями ReplaceText |
| frShowHelp | Задает отображение кнопки Справкав диалоговом окне |
| frWholeWord | Этот флаг включается и выключается, если пользователь включает и выключает опцию Только слово целикомв диалоговом окне. Можно установить эту опцию по умолчанию во время проектирования, чтобы при открытии диалога она была включена |
Сами по себе компоненты FindDialog и ReplaceDialog не осуществляют ни поиска, ни замены. Они только обеспечивают интерфейс с пользователем. А поиск и замену надо осуществлять программно. Для этого можно пользоваться событием OnFind, происходящим, когда пользователь нажал в диалоге кнопку Найти далее
, и событием OnReplace, возникающим, если пользователь нажал кнопку Заменить
или Заменить все
. В событии OnReplace узнать, какую именно кнопку нажал пользователь, можно но значениям флагов frReplace и frReplaceAll.
Поиск заданного фрагмента легко проводить, пользуясь функцией Object Pascal Pos, которая определена в модуле System следующим образом:
function Pos(Substr: string; S: string): Byte;где S — строка, в которой ищется фрагмент текста, a Substr — искомый фрагмент. Функция возвращает позицию первого символа первого вхождения искомого фрагмента в строку. Если Substr в S не найден, возвращается 0.
Для организации поиска нам потребуется еще две функции: Сору и AnsiLowerCase. Первая из них определена как:
function Copy(S: string; Index, Count: Integer): string;
Она возвращает фрагмент строки S, начинающийся с позиции Index и содержащий число символов, не превышающее Count. Функция AnsiLowerCase, определенная как
function AnsiLowerCase(const S: string): string;возвращает строку символов S, переведенную в нижний регистр.
Теперь мы можем рассмотреть пример организации поиска. Пусть в вашем приложении имеется компонент Memo1 и при выборе раздела меню MFind вы хотите организовать поиск в тексте, содержащемся в Memo1. Для упрощения задачи исключим опцию поиска только целых слов и опцию поиска вверх от положения курсора.
Программа, реализующая поиск, может иметь следующий вид:
var SPos: integer;
procedure TForm1.MFindClick(Sender: TObject);
begin
{запоминание позиции курсора}
SPos := Memo1.SelStart;
with FindDialog1 do
begin
{начальное значение текста поиска —
текст, выделенный в Memo1}
FindText := Memo1.SelText;
{позиционирование окна диалога внизу Memo1}
Position := Point(Form1.Left, Form1.Top +
Memo1.Top + Memo1.Height);
{удаление из диалога кнопок «Вверх», «Вниз»,
«Только слово целиком»}
Options := Options + [frHideUpDown, frHideWholeWord];
{выполнение}
Execute;
end;
end;
procedure TForm1.FindDialog1Find(Sender: TObject);
begin
with FindDialog1 do
begin
if frMatchCase in Options
{поиск с учетом регистра}
then Memo1.SelStart := Pos(FindText,
Copy(Memo1.Lines.Text, SPos + 1,
Length(Memo1.Lines.Text))) + Spos — 1
{поиск без учета регистра}
else Memo1.SelStart := Pos(AnsiLowerCase(FindText),
AnsiLowerCase(Copy(Memo1.Lines.Text, SPos + 1,
Length(Memo1.Lines.Text)))) + Spos — 1;
if Memo1.SelStart >= Spos
then
begin
{выделение найденного текста}
Memo1.SelLength := Length(FindText);
{изменение начальной позиции поиска}
SPos := Memo1.SelStart + Memo1.SelLength + 1;
end
else if MessageDlg(
'Текст "'+FindText+'" не найден. Продолжать диалог?',
mtConfirmation, mbYesNoCancel, 0) <> mrYes
then CloseDialog;
end;
Memo1.SetFocus;
end;
В программе вводится переменная SPos, сохраняющая позицию, начиная с которой надо проводить поиск.
Процедура MFindClick вызывает диалог, процедура FindDialog1Find обеспечивает поиск с учетом или без учета регистра в зависимости от флага frMatchCase. После нахождения очередного вхождения искомого текста этот текст выделяется в окне Memo1 и управление передается этому окну редактирования. Затем при нажатии пользователем в диалоговом окне кнопки Найти далее
, поиск продолжается в оставшейся части текста. Если искомый текст не найден, делается запрос пользователю о продолжении диалога. Если пользователь не ответил на этот запрос положительно, то диалог закрывается методом CloseDialog.
В дополнение к приведенному тексту полезно в обработчики событий OnClick и OnKeyUp компонента Memo1 ввести операторы
SPos := Memo1.SelStart;
Это позволяет пользователю во время диалога изменить положение курсора в окне Memo1. Это новое положение сохранится в переменной SPos и будет использовано при продолжении поиска.
При реализации команды Заменить
приведенные выше процедуры можно оставить теми же самыми, заменив в них FindDialog1 на ReplaceDialog1. Дополнительно можно написать процедуру обработки события OnReplace компонента ReplaceDialog1:
procedure TForm1.ReplaceDialog1Replace(Sender: TObject); begin if Memo1.SelText <> '' then Memo1.SelText := ReplaceDialog1.ReplaceText; if frReplaceAll in ReplaceDialog1.Options then ReplaceDialog1Find(Self); end;
Этот код производит замену выделенного текста и, если пользователь нажал кнопку Заменить все
, то продолжается поиск вызовом уже имеющейся процедуры поиска ReplaceDialog1Find*. Если же пользователь нажал кнопку Заменить
, то производится только одна замена и для продолжения поиска пользователь должен нажать кнопку Найти далее
.
Заменить всезаменяет только одно значение и находит следующее. На наш взгляд такия действия более логично было бы задать кнопке
Заменить, а для
Заменить всеорганизовать цикл. Причем такой цикл проще осуществить в процедуре ReplaceDialog1Find. В приведенном ниже коде кроме того введена локальная переменная ss, так как свойству SelStart нельзя присваивать отрицательные значения.
procedure TForm1.ReplaceDialog1Find(Sender: TObject);
var ss: integer;
last: Boolean;
st: string;
begin
with ReplaceDialog1 do begin
if (frFindNext in Options) then
{изменение начальной позиции поиска}
SPos := Memo1.SelStart + Memo1.SelLength + 1;
last := not (frReplaceAll in Options);
repeat
if frMatchCase in Options
{поиск с учетом регистра}
then ss := Pos(FindText,
Copy(Memo1.Lines.Text, SPos + 1,
Length(Memo1.Lines.Text))) + Spos — 1
{поиск без учета регистра}
else ss := Pos(AnsiLowerCase(FindText),
AnsiLowerCase(Copy(Memo1.Lines.Text, SPos + 1,
Length(Memo1.Lines.Text)))) + Spos — 1;
if ss >= Spos then
begin
{выделение найденного текста}
Memo1.SelStart := ss;
Memo1.SelLength := Length(FindText);
if (frReplaceAll in Options) then begin
{замена}
Memo1.SelText := ReplaceDialog1.ReplaceText;
{изменение начальной позиции поиска}
SPos := Memo1.SelStart + Memo1.SelLength + 1;
end;
end
else
begin
if (frReplaceAll in Options) or (frReplace in Options) then
st := 'Замена "' + FindText + '" на "' + ReplaceText + '" закончена'
else st := 'Текст "' + FindText + '" не найден';
if MessageDlg(st + '. Продолжать диалог?',
mtConfirmation, mbYesNoCancel, 0) <> mrYes
then CloseDialog;
last:=true;
end;
until last;
end;
end;
procedure TForm1.ReplaceDialog1Replace(Sender: TObject);
begin
if (frReplace in ReplaceDialog1.Options) and (Memo1.SelText <> '')
then Memo1.SelText := ReplaceDialog1.ReplaceText;
ReplaceDialog1Find(Self);
end;
— Примечание разработчика электронной версии.