Delphi-Help

Главная Статьи Заменить (ReplaceDialog) Диалоги поиска и замены текста — компоненты FindDialog и ReplaceDialog

Диалоги поиска и замены текста — компоненты FindDialog и ReplaceDialog

Оцените материал
(0 голосов)


Диалоги поиска и замены текста — компоненты FindDialog и ReplaceDialog

Компоненты FindDialog и ReplaceDialog, вызывающие диалоги поиска и замены фрагментов текста, очень похожи и имеют одинаковые свойства, кроме одного, задающего заменяющий текст в компоненте ReplaceDialog. Такое сходство не удивительно, поскольку ReplaceDialog — производный класс от FindDialog.

Компоненты имеют следующие основные свойства:

  • 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;

Авторизация



Счетчики