TRichEdit & TMemo itp...
Wyszukiwanie tekstu w RichEdit.
Obiekt RichEdit posiada wszystkie funkcje potrzebne do wyszukiwania w nim tekstu. Przedstawię najpierw sposób na wyszukiwanie tekstu bez rozróżniania wielkości liter. Dowyszukiwania posłużę się obiektem Edit1 w którym będzie wpisywany poszukiwany tekst i przyciskiem Button1 inicjującym wyszukiwanie.
// Plik źródłowy np. Unit1.cpp. //--------------------------------
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TSearchTypes st;
String fText = Edit1->Text.Trim().LowerCase();
if(RichEdit1->SelLength)
RichEdit1->SelStart += 1;
int fPos = RichEdit1->FindText(fText, RichEdit1->SelStart,
RichEdit1->Text.LowerCase().Length(),
st);
RichEdit1->SelStart = fPos;
RichEdit1->SelLength = fText.Length();
RichEdit1->SetFocus();
if(fPos != -1)
{
RichEdit1->SelStart = fPos;
RichEdit1->SelLength = fText.Length();
}
else
{
MessageBox(NULL, "Przeszukano
cały dokument", "Zakończono
wyszukiwanie", MB_OK);
RichEdit1->SelStart = 0;
}
}
Kod jest banalnie prosty i w zasadzie opiera się na funkcjach zawartych w obiekcie RichEdit. Teraz przedstawię kod pozwalający na wyszukiwanie tekstu z uwzględnieniem wielkości liter i z rozróżnianiem tylko dla całych wyrazów:
// Plik źródłowy np. Unit1.cpp. //--------------------------------
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TSearchTypes st;
st << stMatchCase << stWholeWord;
String fText = Edit1->Text.Trim();
if(RichEdit1->SelLength)
RichEdit1->SelStart += 1;
int fPos = RichEdit1->FindText(fText, RichEdit1->SelStart,
RichEdit1->Text.Length(), st);
RichEdit1->SelStart = fPos;
RichEdit1->SelLength = fText.Length();
RichEdit1->SetFocus();
if(fPos != -1)
{
RichEdit1->SelStart = fPos;
RichEdit1->SelLength = fText.Length();
}
else
{
MessageBox(NULL, "Przeszukano
cały dokument", "Zakończono
wyszukiwanie", MB_OK);
RichEdit1->SelStart = 0;
}
}
Jak
widać kody różnią się między sobą tylko nieznacznie. Do ustawiania
wyszukiwania z rozróżnieniem wielkości liter służy parametr stMatchCase,
a do wyszukiwania tylko całych wyrazów parametr stWholeWord. W
przykładzie pokazany jest kod wprowadzania tych parametrów, wyprowadzanie
ich, czyli wyłączanie przebiega podobnie z tą różnicą, że używa się innego
operatora:
st >> stMatchCase >> stWholeWord;
Można oczywiście stosować kombinacje i np. jeden parametr wprowadzać, a
drugi wyprowadzać.
Odczyt i zmiana zawartości pojedynczej linii obiektu RichEdit.
Odczyt:
// Plik źródłowy np. Unit1.cpp. //--------------------------------
Zmiana:
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String tekst = RichEdit1->Lines->Strings[numer_linii_liczony_od_0];
}
// Plik źródłowy np. Unit1.cpp. //--------------------------------
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
RichEdit1->Lines->Strings[numer_linii]
= "tekst";
}
Odczyt numeru linii wskazywanej myszą w RichEdit.
Posłużę się zdarzeniem OnMouseDown dla RichEdit, ale równie dobrze można wykorzystać zdarzenia OnMouseUp lub OnMouseMove:
// Plik źródłowy np. Unit1.cpp.
{ //-------------------------------- |
Zaznaczanie linii wskazywanej myszą.
W celu zaznaczenia całej linii wykorzystam zdarzenie OnMouseDown dla obiektu RichEdit. Cała linia zostanie zaznaczona po kliknięciu na niej:
// Plik źródłowy np. Unit1.cpp. void __fastcall TForm1::RichEdit1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{ //-------------------------------- |
Zmiana wartości lewego i prawego marginesu.
// Plik źródłowy np. Unit1.cpp. //-------------------------------- |
Wczytywanie plików większych niż 64Kb do RichEdit.
Standardowo obiekt RichEdit nie wczytuje plików większych niż 64Kb, ale można to zmienić za pomocą prostego kodu:
// Plik źródłowy np. Unit1.cpp.
void __fastcall TForm1::FormCreate(TObject *Sender) //-------------------------------- |
Sposób zaznaczania tekstu za pomocą myszki każdy zna, dlatego pokażę jak można zaznaczyć tekst w dowolnym obiekcie, w którym jest to możliwe za pomocą kodu. Do zaznaczania tekstu można wykorzystać funkcję Perform przekazując jej jako argumenty komunikat o rodzaju wykonywanej akcji, oraz początkową i końcową wartość zaznaczenia. Dla lepszego zobrazowania tego zadania proponuję umieścić na formularzu obiekty Memo1, CSpinEdit1 i CSpinEdit2. Do obiektu Memo1 wstawiamy jakiś tekst, obiekt CSpinEdit1 będzie podawał początkową wartość zaznaczenia, natomiast obiekt CSpinEdit2 końcową, reszta jest banalnie prosta, dla przejrzystości do zaznaczania stworzę własną funkcję, którą nazwę SelectText:
// Plik źródłowy np.
Unit1.cpp. |
Jak widać na przykładzie zmiana zaznaczenia odbywa się poprzez zmianę wartości Value dla obiektów CSpinEdit1 i CSpinEdit2, myślę, że nie wymaga to dalszych wyjaśnień.
W powyższym przykładzie widać, że z funkcji SelectText może skorzystać tylko obiekt typu TMemo, ponieważ jako jeden z argumentów funkcja pobiera właśnie odwołanie do tego typu obiektu, ale można to zmienić. Wykorzystując funkcję ogólną możemy zdefiniować odwołanie do bliżej nieokreślonego typu obiektu, oczywiście ten bliżej niezdefiniowany obiekt musi obsługiwać funkcję Perform. Opis funkcji ogólnych został zamieszczony w dziale
. Realizacja całego zadania jest banalnie prosta:
// Plik źródłowy np.
Unit1.cpp. |
W ten oto sposób możemy przekazać do funkcji jako pierwszy argument dowolny typ obiektu obsługujący funkcję Perform. Można by to oczywiście zrealizować jeszcze za pomocą rzutowanie jednego typu obiektu na drugi co jeszcze upraszcza zadanie:
// Plik źródłowy np.
Unit1.cpp. |
Wstawka korektora to tak zwany kursor piszący z angielskiego carret. Kursor w oknie edycji dowolnego obiektu typu TMemo, TRichEdit, TEdit itp. można przesuwać np. za pomocą klawiszy strzałek, ale można to też robić za pomocą kodu. Zadanie w realizacji jest bardzo podobne do porady zaznaczanie tekstu za pomocą kodu. Posłużymy się tutaj obiektem Memo1 i SCpinEdit1:
// Plik źródłowy np.
Unit1.cpp. |
Jak widać w poradzie wykorzystałem funkcję ogólną ze względu na jej uniwersalny charakter, oraz funkcję Perform przekazując jej jako argumentu komunikat o zaznaczaniu tekstu, chociaż w rzeczywistości tekst nie jest zaznaczany a to dlatego, że pozostałe dwa argumenty określające początek zaznaczenia i jego koniec mają taką samą wartość.
Skok do wybranej linii obiektu RichEdit.
W obiektach takich jak np. ListBox skok do wybranej lini nie stanowi większego problemu ponieważ można posłużyć się funkcją ItemIndex, niestety obiekty typu TRichEdit i TMemoEdit nie posiadają tej właściwości, dlatego żeby wykonać skok do wybranej linii z zaznaczeniem całej linii trzeba stworzyć własną funkcję. Do realizacji tego zadania stworzę funkcję ogólną by można ją było bez problemu wykorzystać zarówno dla obiektu typu TRichEdit jak i TMemo. Do zmiany numeru linii posłużę się obiektem CSpinEdit1 z zakładki Samples:
// Plik źródłowy np.
Unit1.cpp. |
Działanie kodu jest bardzo proste. Funkcja SelStart pobiera początek linii i to właśnie tej funkcji przekazujemy numer linii do której chcemy skoczyć. Funkcja SelLength pobiera długość linii do zaznaczenia, w tym przypadku jest to po prostu długość całej wybranej linii, funkcja Perform przesuwa wstawkę korektora na koniec linii zaznaczając przy okazji tekst. Jeżeli chcemy skoczyć do wybranej linii bez zaznaczania tekstu należy przekazać funkcji SelLength wartość 0 (Obiekt->SelLength = 0;). Przedstawiona funkcja ma jeszcze jedną zaletę powoduje przewijanie okna z tekstem.
Zapisywanie zawartości Memo do pliku *.ini
W tej poradzie chcę pokazać jak można zapisać zawartość obiektu Memo1 do pliku *.ini, może się to przydać w sytuacji gdy chcemy zbudować w prosty sposób bazę danych zawierającą wszelakiego rodzaju opisy, ładowane np. do obiektu Memo1 przy odwołaniu się do wybranego rekordu, możliwości jest wiele. Wykorzystanie pliku typu *.ini ma tą zaletę, że łatwo jest indeksować jego zawartość poprzez tworzenie kluczy i podkluczy. W przykładzie zawartość Memo1 będzie właśnie indeksowana w wybranym podkluczu klucza głównego. Istnieje jednak pewne ograniczenie co do wielkości zapisywanego łańcucha znaków, otóż klasa TIniFile nie obsłuży łańcucha dłuższego niż 2047 + kilkanaście znaków własnych, oznacza to że do każdego podklucza możemy wpisać maksymalnie 2047 znaków, dlatego dobrze jest ustawić właściwość MaxLength obiektu Memo1 na 2047, co zapobiegnie wpisywaniu do niego zbyt dużej ilości tekstu, czyli takiej, która nie zmieści się w podkluczu. Ktoś może sobie pomyśleć co to za filozofia wpisać łańcuch znaków do pliku ini, nawet z obiektu Memo, skoro jest to typ AnsiString, a klasa TIniFiles posiada funkcje WriteString i ReadString. To prawda nie stanowi to większego problemu, ja jednak przedstawię dwie funkcje, które będą zapisywały zawartość Memo1 do pliku *.ini z zachowaniem końca linii i z łamaniem tekstu, co oznacza że po wczytaniu Memo1 zachowa swój pierwotny układ tekstu:
// Plik źródłowy np.
Unit1.cpp. |
Przeciąganie i upuszczanie plików na RichEdit.
Do ładowaniu plików do obiektów typu TRichEdit, TMemoEdit, TListBox itp. służy funkcja LoadFromFile, o czym zapewne wszyscy wiedzą. Tym razem chcę pokazać na przykładzie obiektu RichEdit jak załadować plik metodą przeciągnij - upuść. Zadanie polega na przeciągnięciu pliku np. z pulpitu, eksploratora lub innego dowolnego menadżera plików do obiektu RichEdit i wyświetleniu w nim jego zawartości. Zadanie jest trochę złożone, na początek w pliku nagłówkowym (np. Unit1.h) umieszczamy mapę komunikatów odpowiedzialną za przechwycenie komunikatu o przeciągnięciu pliku, a w sekcji private umieszczamy deklarację funkcji przechwytującej:
// Plik nagłówkowy np.
Unit1.h. |
Umieszczamy na formularzu obiekt RichEdit1, a następnie tworzymy definicję funkcji przechwytującej, funkcja ta o przechwyceniu komunikatu o przeciągnięciu wykonuje zlecone działanie, może to być dowolne zadanie, ja jednak umieszczę instrukcję ładującą plik do RichEdit. Konieczne jest również umieszczenie w zdarzeniu OnCreate dla formularza funkcji DragAcceptFile akceptującej przeciąganie, Funkcja pobiera dwa argumenty, pierwszy to uchwyt do okna, które ma obsługiwać przechwytywanie, drugi parametr to zmienna typu bool włączająca lub wyłączająca przechwytywanie. W zdarzeniu OnCLoseQuery umieszczamy funkcję wyłączającą akceptowanie przeciąganych plików.
// Plik źródłowy np.
Unit1.cpp. |
Wykorzystując metodę WindowProc możemy w obiekcie typu TMemo umieścić jako tło grafikę w formacie *.bmp. Przedstawiony sposób działa prawidłowo, jednak nie jest doskonały, wadą jest np. brak płynnego przesuwania "pasków przewijania", ma to związek z blokadą odświeżania, poza tym w komputerach ze słabą kartą graficzną tło może migać podczas przewijania. Pomimo podobieństwa Memo do RichEdit, to w tym drugim nie będzie działać to prawidłowo, tło się pojawi, ale nie będzie tekstu.
W pliku nagłówkowym umieszczamy deklaracje:
// Plik nagłówkowy np.
Unit1.h |
Definicje w pliku źródłowym:
// Plik nagłówkowy np.
Unit1.h |
Jak wcześniej wspomniałem, kod uniemożliwia płynne przesuwanie pasków przewijania, trzeba cały czas klikać żeby się przesuwały. Można ten błąd wyeliminować usuwając z funkcji NewMemoWP funkcje LockWindowUpdate(Memo1->Handle) i LockWindowUpdate(0). Wspomniane funkcje blokują odświeżanie obiektu Memo1, ale tylko w czasie odrysowywania tła, niemniej jednak ma to wpływ na paski przewijania. Funkcja Memo1->DoubleBuffered zapobiega pojawiania się artefaktów w postaci powielonych linii tekstu w Memo1, jednak nie zawsze może prawidłowo działać. No i największa wada to, że funkcja działa prawidłowo tylko w systemach Windows NT/2000/XP, w Win95/95 wystąpią problemy z wyświetlaniem tekstu.
Przepisywanie zawartości z jednego RichEdit do drugiego z zachowaniem formatowania.
Zawartość jednego RichEdit do
drugiego można bez problemu przepisać poprzez jego właściwość Text lub
Lines, jednak przy takim przepisywaniu okaże się że tekst co prawda się
przeniósł, ale formatowanie tekstu już nie.
Do przeniesienia tekstu wraz z formatowaniem potrzebny jest obiekt typu
TMemoryStream do któego pierwszy RichEdit przepisze swoją zawartość i z
którego drugi RichEdit tą zawartość pobierze:
// Plik źródłowy np.
Unit1.cpp. |
Wczytywanie plików binarnych do obiektu Memo - wg. pomysłu kinio.
Komponenty takie jak Memo, RichEdit itp. nie wyświetlają do końca całego pliku ponieważ taka jest natura typu char. Jeżeli masz łańcuch tekstu jak tablice typu char* to zostanie on wyświetlony do momentu kiedy zostanie napotkany znak NULL. W poniższym kodzie jak widać jest on zamieniony na spacje. Możesz go dowolnie zmienić, lub nawet usunać.
// Plik źródłowy np.
Unit1.cpp. |