Компоненты 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 и frRepIaceAll.
Поиск заданного фрагмента в компоненте RichEdit легко проводить, используя его метод FindText, объявленный следующим образом:
int __fastcall FindText(const System::AnsiString SearchStr, int StartPos, int Length, TSearchTypes Options);
Этот метод ищет в тексте RichEdit фрагмент, заданный параметром SearchStr. Поиск производится, начиная с позиции StartPos (позиция первого символа текста считается нулевой), на протяжении Length символов. Параметр Options является множеством, которое может содержать элементы stWhoIeWord (поиск только целого слова) и stMatchCase (поиск с учетом регистра). Метод возвращает позицию найденного вхождения. Если заданный фрагмент не найден, возвращается -1.
Ниже приведен код, осуществляющий поиск заданного фрагмента в тексте компонента RichEdit1. Вызвать диалог поиска можно операторами:
/* начальное значение текста поиска - текст, выделенный в RichEdit1 */
FindDialog1->FindText = RichEdit1->SelText;
FindDialog1->Execute();
Они задают в качестве начального значения для поиска текст, выделенный в окне RichEdit1, и затем вызывают диалог поиска FindDialog1. Обработчик события OnFind компонента FindDialog1 может иметь вид:
void __fastcall TForm1::FindDialog1Find(TObject *Sender)
{
int FoundAt, StartPos, ToEnd;
TSearchTypes Option;
/* если было выделение, то поиск идет, начиная с его последнего символа, иначе с позиции курсора */
StartPos = RichEdit1->SelStart;
if(RichEdit1->SelLength)
StartPos += RichEdit1->SelLength;
/* ToEnd - длина текста, начиная с первой позиции поиска и до конца */
ToEnd = RichEdit1->Text.Length() - StartPos;
/* поиск целого слова или нет в зависимости от установки пользователя */
if(FindDialog1->Options.Contains(frWholeWord))
Option << stWholeWord;
else Option >> stWholeWord;
/* поиск с учетом или без учета регистра в зависимости от установки пользователя */
if(FindDialog1->Options.Contains(frMatchCase))
Option << stMatchCase;
else Option >> stMatchCase;
FoundAt = RichEdit1->FindText(FindDialog1->FindText, StartPos, ToEnd, Option);
if(FoundAt != -1) // если найдено
{
RichEdit1->SetFocus();
RichEdit1->SelStart = FoundAt;
RichEdit1->SelLength = FindDialog1->FinaText.Length();
}
else ShowMessage("Текст '" + FindDialog1->FindText + "' не найден");
}
Функция FindDialog1Find срабатывает, когда пользователь нажал в диалоге кнопку Найти далее. Комментарии в тексте этой функции поясняют этапы поиска. Сначала производится установка области текста (переменные StartPos и ToEnd), в которой проводится поиск. Затем устанавливаются атрибуты поиска формируется множество Option в зависимости от установленных пользователем опций. Затем методом FindText проводится сам поиск. Если нового вхождения искомого текста не найдено (метод FindText вернул -1), то пользователю выдается сообщение об этом с помощью функции ShowMessage.
Приведенный пример относился к поиску в компоненте RichEdit. Для организации поиска в тексте компонента Memo удобно использовать метод Pos класса AnsiString, который объявлен следующим образом:
int __fastcall Pos(const AnsiString& subStr) const;
Метод возвращает индекс первого символа первого вхождения подстроки subStr в строку, к которой применяется этот метод. Индексы начинаются с 1. Если subStr не содержится в строке, то возвращается 0.
Для организации поиска потребуются еще две функции класса AnsiString: SubString и LowerCase. Первая из них определена как:
AnsiString __fastcall SubString(int index, int count) const;
Она возвращает подстроку, начинающуюся с символа в позиции index и содержащую count символов.
Функция LowerCase, определенная как:
AnsiString __fastcall LowerCase() const;
возвращает строку символов S, переведенную в нижний регистр.
Теперь можно рассмотреть пример организации поиска. Пусть в приложении имеется компонент Memo1 и при выборе раздела меню MFind нужно организовать поиск в тексте, содержащемся в Memo1. Для упрощения задачи исключим опцию поиска только целых слов и опцию поиска вверх от положения курсора. Программа, реализующая поиск, может иметь следующий вид:
void __fastcall TForm1::MFindClick(TObject *Sender)
{
/* начальное значение текста поиска - текст, выделенный в Memo1 */
FindDialog1->FindText = Memo1->SelText;
FindDialog1->Execute();
}
//-----------------------------
void __fastcall TForm1::FindDialog1Find(TObject *Sender)
{
int FoundAt, StartPos, ToEnd;
/* если было выделение, то поиск идет, начиная с его последнего символа, иначе с позиции курсора */
StartPos = Memo1->SelStart;
if(Memo1->SelLength)
StartPos += Memo1->SelLength;
/* ToEnd - длина текста, начиная с первой позиции поиска и до конца */
ToEnd = Memo1->Text.Length() - StartPos;
/* поиск с учетом или без учета регистра в зависимости от установки пользователя */
if(FindDialog1->Options.Contains(frMatchCase))
FoundAt = StartPos + Memo1->Text.SubString(StartPos+1, ToEnd).Pos(FindDialog1->FindText);
else
FoundAt = StartPos + Memo1->Text.SubString(StartPos+1, ToEnd).LowerCase().Pos(FindDialog1->FindIext.LowerCase());
if(FoundAt != StartPos) // если найдено
{
Memo1->SetFocus();
Memo1->SelStart = FoundAt-1;
Memo1->SelLength = FindDialog1->FindText.Length();
}
else ShowMessage("Текст '" + FindDialog1->FindText + "' не найден");
}
Программа аналогична приведенной ранее для компонента RichEdit и отличается только несколькими операторами, осуществляющими непосредственно поиск.
При реализации команды Заменить приведенные выше процедуры можно оставить теми же самыми, заменив в них FindDialog1 на ReplaceDialog1. Дополнительно можно написать процедуру обработки события OnReplace компонента ReplaceDialog1. Кроме того желательно обеспечить, чтобы при нажатии пользователем в диалоге клавиши Заменить все программа просматривала бы весь текст и проводила все замены без дополнительных вопросов пользователю. Для этого можно в конце обработчика события OnFind вставить оператор, который в случае, если пользователь нажал в диалоге клавишу Заменить все, вызывал бы обработчик события OnReplace. В итоге текст, обеспечивающий замену в компоненте RichEdit, может иметь вид:
void __fastcall TForm1::ReplaceDialog1Find(TObject *Sender)
{
// Если нажата кнопка "Заменить все", то уход на замену
if(ReplaceDialog1->Options.Contains(frReplaceAll))
ReplaceDialog1Replace(Sender);
}
//-----------------------------
void __fastcall TForm1::ReplaceDialog1Replace(TObject *Sender)
{
if(RichEdit1->SelText != "") // Если есть выделенный текст Замена выделенного текста
RichEdit1->SelText = ReplaceDialog1->ReplaceText;
else if(ReplaceDialog1->Options.Contains(frReplace))
{
ShowMessage("Текст '" + ReplaceDialog1->FindText + "' не найден");
return;
}
// Если нажата кнопка "Заменить все", то уход на поиск
if(ReplaceDialog1->Options.Contains(frReplaceAll))
ReplaceDialog1Find(Sender);
}
В функции ReplaceDialog1Find в конце поиска очередного вхождения искомого фрагмента в текст проверяется нажатие пользователем кнопки Заменить все. Если она нажата, то происходит обращение к функции ReplaceDialog1Replace, в которой осуществляется замена текста, после чего сразу автоматически опять вызывается функция ReplaceDialog1Find. Таким образом без остановок просматриваются и заменяются все вхождения искомого фрагмента в текст. Если же кнопка Заменить все не нажата, то программа останавливается и ждет, пока пользователь нажмет в диалоге кнопку Найти далее или Заменить. Если нажимается кнопка Найти далее, то производится следующий вызов ReplaceDialog1Find. А при нажатии кнопки Заменить вызывается функция ReplaceDialog1Replace, которая заменяет выделенный текст.
Приведенные коды рассчитаны на компонент RichEdit. Для компонента Memo в них просто надо заменить RichEdit1 на Memo1.