08 Obsługa zdarzeń wykonywanych myszą



Rozdział 8
Obsługa zdarzeń wywoływanych myszą
Obsługa zdarzeń naciśnięcia i zwolnienia klawisza myszy Wykorzystanie
własnych funkcji do obsługi mchów myszy
Wykorzystywanie informacji o współrzędnych wskaźnika myszy oraz
obszarów testowania
Obsługa powiadomień klawiszy myszy
Częstokroć zachodzi potrzeba sprawdzenia, czy użytkownik nacisnął bądź zwolnił
klawisz myszy i miejsca, w którym to nastąpiło bez użycia klawiszy kontrolnych. Naj-
lepszym przykładem takiej sytuacji może być aplikacja graficzna, w której ekran służy
użytkownikowi jako tablica do rysowania, a kuknięcia myszą powodują określone reakcje w
postaci rysowania kresek lub punktów. Jak już wiemy, wszelkie akcje podjęte przez
użytkownika docierają do aplikacji w formie określonych komunikatów (lub zdarzeń).
Komunikaty pochodzące od klawiszy myszy nie są wyjątkiem od tej reguły. Aktualna pozycja
wskaźnika zostaje połączona z komunikatami klawiszy, co sprawia, że są one bardzo łatwe do
wykorzystania w połączeniu z funkcjami graficznymi systemu Windows.
Kontrolowanie długości czasu naciśnięcia klawisza myszy
Komunikaty Windows przekazują informacje o tym, jak długo użytkownik naciskał klawisz myszy.
Informacja ta może zostać pobrana ze składowej zmiennej struktury MSG poprzez wymuszenie
wykonania wirtualnej funkcji PreTranslate-
Message ().
Funkcja ta wywoływana jest po otrzymaniu komunikatu przez aplikację, a przed przeprowadzeniem
wszelkich operacji korzystających z mapy komunikatów.


186 Poznaj Visual C++ 6
Obsługa zdarzeń naciśnięcia i zwolnienia klawisza myszy
System Windows posiada mechanizmy obsługi trzech klawiszy, w jakie wyposażona
może być mysz. Część myszy jednak posiada tylko dwa klawisze; w takim przypadku mamy
do czynienia z wydarzeniami związanymi z lewym bądź prawym klawiszem, lecz w pewnych
okolicznościach wskazana jest obsługa również środkowego klawisza. System Windows
wysyła do aplikacji dwa rodzaje informacji o zdarzeniach: gdy użytkownik naciśnie klawisz
myszy oraz go zwolni. Czasami obsługa tych zdarzeń określana jest terminem catching
(łapanie), ponieważ zdarzenia są "łapane" czy przechwytywane i wywoływany jest
odpowiedni kod przy przekazywaniu zdarzeń klasie bazowej okna.
Aby poznać ten proces w praktyce, utworzymy za pomocą AppWizard nową aplikację
okna dialogowego i nazwijmy jÄ… MouseMsg.
Po utworzeniu okna dialogowego aplikacji IDD_MOUSEMSG_DIALOG, powiększmy w
edytorze zasobów jego rozmiary do około 300 pikseli szerokości i 200 wysokości. Da nam to
wystarczająco dużą powierzchnię do przeprowadzenia kontroli zwracanych przez mysz
współrzędnych. Dokonując edycji okna dialogowego aplikacji, musimy także usunąć
widoczny pośrodku napis TODO :. . ., pozostawiając w obrębie okna jedynie przyciski OK oraz
Cancel.
Za pomocą poniższej procedury ustanowimy funkcję obsługi przechwyconego komu-
nikatu, informującego o naciśnięciu przez użytkownika lewego klawisza myszy.
Dodanie funkcji obsługi komunikatu lewego klawisza myszy
1. Kliknij kartÄ™ CIassView w panelu Project Workspace.
2. Kliknij symbol +, by rozwinąć widok użytych w aplikacji klas.
3. Prawym klawiszem kliknij pozycję klasy CMouseMsgDlg, po czym wyświetlone zostanie
menu skrótów.
4. Otwórz okno dialogowe New Windows Message and Event Handlers, klikając pozycję
menu Add Windows Message Handler.
5. Z listy komunikatów i zdarzeń (messages/events) wybierz WM_LBUTTOMDOHN.
6. Kliknij przycisk Add and Edit, co umożliwi edycję zawartości nowej funkcji.
Otworzyliśmy zatem funkcję obsługi OnLButtonDown () dla zdarzenia związanego z
lewym klawiszem myszy. Funkcja ta będzie wywoływana za każdym razem, gdy użytkownik
naciśnie ten klawisz. Edytując kod funkcji, możemy nakazać wykonywanie określonych
czynności po wywołaniu tej funkcji. Przykładowo, dodając kod zapisany na listingu 8.1,
spowodujemy, że po każdym naciśnięciu lewego klawisza myszy wyświetlana będzie ikona z
wykrzyknikiem.


Obsługa zdarzeń wywoływanych myszą 187
Listing 8.1. LST8_1.CPP - przypisanie funkcji OnLButtonDown () reakcji w postaci wyświetlania ikony
z wykrzyknikiem, w miejscu naciśnięcia lewego klawisza myszy
void CMouseMsgDlg::OnLButtonDown(UINT nFlags, CPoint point) {
// TODO: Dodaj tu kod kod obsługi komunikatu lub/i
II ** Wyświetl komunikat w pasku tytułu okna CString strMessage;
strMessage.Format("Left Button Pressed at (%d,%d)", O point.x,point.y) ;
SetWindowText(strMessage) ;
// ** Wybór ikony w zależności od użycia klawisza Ctrl char* pszlcon;
if (nFlags & MK_CONTROL) @
pszlcon = IDI_EXCLAMATION;
el-se
pszlcon = IDI_HAND;
// ** Wyświetlenie ikony z wykrzyknikiem w miejscu naciśnięcia II ** klawisz myszy CDC* pDC
= GetDCO ;
pDC->Draw!con(point,
AfxGetApp()->LoadStandardIcon(pszlcon)); ® ReleaseDC(pDC) ;
CDialog::OnLButtonDown(nFlags, point) ;
O Współrzędne wskaźnika myszy zostają sformatowane w łańcuch zmiennych i wyświetlone w pasku
tytułowym okna dialogowego.
@ Jeśli użytkownik jednocześnie z naciśnięciem lewego klawisza myszy użyje klawisza Ctrl, wybrana
zostanie inna spośród standardowych ikon.
© Wybrana ikona zostaje wyÅ›wietlona w miejscu, w którym nastÄ…piÅ‚o naciÅ›niÄ™cie klawisza myszy.
Zauważmy, iż w powyższym listingu funkcji OnLButtonDown () zostają przekazane dwa parametry.
Pierwszy z nich, nFlags, informuje o tym, który z klawiszy myszy został


188_____________________________________Poznaj Visual C++ 6 i
naciśnięty oraz czy jednocześnie nastąpiło użycie klawisza Ctrl lub Shift (wartości tego parametru są
przedstawione w tabeli 8.1). Są to znaczniki bitowe i każda ich kombinacja może zostać wykorzystana, w
każdym momencie. Dla sprawdzenia działania poszczególnych znaczników można posłużyć się
operatorem logicznym AND (w języku C++ operator &), jak w linii 13. Zostaje tutaj sprawdzona wartość
znacznika MK_CONTROL, którą będzie TRUE, jeśli wraz z lewym klawiszem myszy zostanie naciśnięty
kalwisz Ctrl.
Wskaźnik pszlcon jest wykorzystany do wskazania w linii 14 ikony IDI_EXCLA-MATION lub IDI_HAND, w
zależności od wyniku sprawdzenia wartości nFlags pod kątem użycia klawisza Ctri. Ikony te są standardowymi
ikonami systemowymi i zostają użyte w liniach 20 i 21 przez funkcję kontekstu urządzenia DrawiconO (nie
będziemy tutaj rozprawiać o kontekście urządzenia; zagadnienie to zostanie opisane w rozdziale 15). Funkcja
klasy aplikacji LoadStandardlcon () pobiera wskaźnik pszlcon w celu załado- i wania określonej ikony
systemowej (linia 21). Inne tego rodzaju ikony wyszczególnione są w tabeli 8.2.
Drugim parametrem przekazywanym funkcji OnLButtonDown () jest obiekt point klasy c Point. Jest
to klasa przechowująca współrzędne jako składowe x oraz y. W linii 7 zostaje sformułowany na ich
podstawie łańcuch znakowy, który następnie jest wyświetlany przez funkcję SetWindowText () na pasku
tytułowym okna dialogowego (linia 9). Punkt o podanych współrzędnych służy następnie umiejscowieniu
wyświetlanej ikony.
Tabela 8.1. Znaczniki przekazywane funkcji OnLButtonDown () jako parametr nFlags Postać
znacznika Opis
MK_LBUTTON Informuje o naciśnięciu lewego klawisza myszy
MK_MBUTTON Informuje o naciśnięciu środkowego klawisza
MK_RBUTTON Informuje o naciśnięciu klawisza prawego
MK_CONTROL Informuje o naciśnięciu klawisza Ctrl
MK_SHIFT Informuje o naciśnięciu klawisza Shift
Tabela 8.2. Standardowe ikony systemowe dostępne poprzez funkcję LoadStandardlcon ()
Znacznik ikony Opis
IDI_EXCLAMATION Znak wykrzyknika wewnątrz trójkąta
IDI_HAND Znak X wewnątrz czerwonego kółka
IDI_APPLICATION Bieżąca ikona przypisana aplikacji
I DI_ASTERI SK Litera i oznaczajÄ…ca informacjÄ™
IDI_QUESTION Znak zapytania


Obsługa zdarzeń wywoływanych myszą 189
Po dodaniu do funkcji obsługi kodu umieszczonego na listingu 8.1, skompilowaniu i
uruchomieniu programu, otrzymamy w rezultacie najprostszy na świecie program do
rysowania. Po naciśnięciu lewego klawisza myszy powinny ukazywać się ikony z
wykrzyknikiem, a gdy dodatkowo naciśniemy klawisz Ctrl, rysować będziemy ikony typu
IDI_HAND, jak to zostało pokazane na rysunku 8.1.
Standardowe ikony Windows 95 i NT 4.0
Używając standardowych ikon systemowych Windows 95 i NT 4.0, zauważyć można, iż nie
zawsze wizerunek, który przedstawiają odpowiada ich nazwie. Przykładowo, ikona IDI_HAND (ręka)
przedstawia biały krzyżyk na czerwonym tle. Nazwy te, to pozostałość po systemach Windows 3.11
oraz NT 3.51. Zmienione zostały elementy graficzne, nazwy jednak zachowano.

Rysunek 8.1. Rysowanie ikon poprzez naciskanie lewego klawisza myszy
O Ikona z krzyżykiem rysowana jest po kliknięciu lewym klawiszem myszy, z jednoczesnym użyciem
klawisza Ctrl.
@ Ikona z wykrzyknikiem rysowana jest na skutek naciśnięcia lewego klawisza myszy.
Zauważmy, iż na pasku tytułowym okna wyświetlane są koordynaty punktu, w którym
nastąpiło ostatnie kliknięcie. Gdy klikniemy w pobliżu lewego górnego rogu, wyświetlane
współrzędne będą się odnosiły do lewego górnego rogu okna dialogowego, a nie ekranu.
Badanie współrzędnych ekranowych możemy wykonać poprzez wywołanie


190_____________________________________Poznaj Visual C++ 6
funkcji GetCursorPos (), która może zostać wywołana w każdym momencie dla określenia położenia
wskaźnika myszy. Funkcja ta wykorzystuje tylko jeden parametr. Jest nim wskaźnik obiektu cpoint. Aby
sprawdzić jej działanie, dodajmy do funkcji obsługi OnL-ButtonDown () bezpośrednio za linią 3
następujący wpis:
GetCursorPos(&point) ;
Po ponownym skompilowaniu i uruchomieniu programu zauważymy, że obecnie wyświetlane w
pasku tytułowym okna współrzędne są koordynatami ekranowymi, a nie okna dialogowego. Ikony
również będą wyświetlane według tych współrzędnych. Jednakże funkcja rysująca oczekuje koordynat
leżących w obrębie okna, należy zatem usunąć powyższą linię po sprawdzeniu jej działania.
Komunikaty o zwolnieniu klawisza myszy mogą być w taki sam sposób przechwyty-wane, jak
komunikaty o jego naciśnięciu. Funkcję obsługi takiego komunikatu dodajemy więc w taki sam sposób
jak poprzednio, z tą różnicą, iż wybranym komunikatem będzie w tym przypadku WM_LBUTTONUP
zamiast WM_LBUTTONDOWN. Po przeprowadzeniu stosownej procedury otrzymamy nowÄ… funkcjÄ™
OnLButtonUp (). Możemy umieścić w niej kod sprawiający wyświetlenie informacji w pasku tytułowym
okna, mówiącej o zwolnieniu klawisza:
SetWindowText( "Left Button Released") ;
Po umieszczeniu tej linii wewnÄ…trz kodu funkcji OnLButtonUp (), skompilowaniu i uruchomieniu
programu stwierdzimy, iż nowa funkcja wywoływana jest za każdym razem, gdy wciśnięty klawisz
myszy (lewy) zostanie zwolniony. Takie zachowanie pozwala nam na tworzenie aplikacji, w których
naciśnięcie klawisza myszy będzie wywoływało określony tryb działania, zwolnienie klawisza natomiast
powodować będzie wyłączenie danego trybu. Najlepszym przykładem może być czynność przesuwania
okna na pulpicie Windows. Naciśnięcie lewego klawisza myszy, gdy jej wskaźnik znajduje się ponad
paskiem tytułowym okna, wywołuje tryb przesuwania. Po zwolnieniu klawisza ustalona zostaje nowa
pozycja okna, a jego normalne funkcje powracają do działania.
Podobne funkcje istnieją dla klawiszy prawego oraz środkowego. Tabela 8.3 przedstawia wszystkie
możliwe zdarzenia związane z użyciem tych klawiszy. Funkcje obsługi dodajemy w taki sam sposób, jak
w przypadku lewego klawisza.
Funkcje obsługi zdarzeń środkowego klawisza myszy
Umieszczanie tych funkcji wymaga nieco zachodu, gdyż Microsoft nie umieścił w narzędziach Visual
C++ mechanizmu ich tworzenia. Nie oznacza to oczywiście, że nie można tych funkcji umieścić,
należy jednak najpierw utworzyć własnoręcznie mapę komunikatów oraz definicję funkcji. Powoduje
to niechęć programistów do pisania aplikacji wykorzystujących środkowy klawisz myszy.


Obsługa zdarzeń wywoływanych myszą 191 Tabela 8.3. Komunikaty Windows
dotyczÄ…ce klawiszy myszy
Komunikat Funkcja obsługi Opis w AppWizard
Naciśnięto lewy klawisz Lewy klawisz został zwolniony Naciśnięto środkowy klawisz Zwolniono
środkowy klawisz Naciśnięto prawy klawisz Zwolniono prawy klawisz
WM_LBUTTONDOWN OnLButtonDown()
WM_LBUTTONUP OnLButtonUp()
WM_MBUTTONDOWN Brak
WM_MBUTTONUP Brak
WM_RBUTTONDOWN OnRButtonDown()
WM RBUTTONUP OnRButtonUp()
PATRZ TAKŻE
• WiÄ™cej informacji o rysowaniu w kontekÅ›cie urzÄ…dzenia znajduje siÄ™ w rozdziale 15.
* O projektowaniu i edytowaniu okien dialogowych mówimy w rozdziale 3.
Przechwytywanie zdarzeń dwukrotnego kliknięcia
Podwójne kliknięcie posiada w aplikacjach Windows specjalne znaczenie. Często jest
wykorzystywane jako skrót przy wybieraniu pozycji z listy. Gdy użytkownik naciśnie lewy
klawisz myszy dwukrotnie w krótkim odstępie czasu, następuje wysłanie do aplikacji
komunikatu o zdarzeniu podwójnego klinięcia. System Windows dokonuje oceny odstępu
pomiędzy obydwoma kliknięciami i jeśli był to wystarczający okres, wysyła komunikat
WM_LBUTTONDBLCLK. Równoznaczne komunikaty obowiązują w przypadku klawiszy pra-
wego i środkowego: WM_RBUTTONDBLCLK oraz WM_MBUTTONDBLCLK. Funkcje obsługujące
te zdarzenia umieszcza się w kodzie programu w taki sam sposób jak dla lewego klawisza,
czyli według procedury w punkcie "Dodanie funkcji obsługi komunikatu lewego klawisza
myszy". Spróbujemy teraz zapisać funkcję obsługi zdarzenia podwójnego kliknięcia lewym
klawiszem myszy i skojarzyć je z otwarciem okna komunikatu:
void CMouseMsgDlg::OnLButtonDblClk(UINT nFlags, CPoint point) (
AfxMessageBox("Left Button Double Clicked");
CDialog::OnLButtonDblClk(nFlags, point) ;
)
Po skompilowaniu i uruchomieniu aplikacji, po każdym dwukrotnym kliknięciu lewym
klawiszem myszy wyświetlone zostanie okno zawierające komunikat o zaistnieniu tego
zdarzenia. Zauważmy, że w przypadkach pozostałych klawiszy wykorzystywane są te same
parametry nFlags oraz point, co pozwala na ustalanie wielu różnych kombinacji klawiszy
myszy z klawiszami kontrolnymi na klawiaturze.


192 Poznaj Visual C++ 6
Ustalanie prędkości dwukrotnego kliknięcia
Odstęp czasu pomiędzy dwoma kliknięciami, który powoduje ich interpretację jako kliknięcie
podwójne ustala się za pomocą apletu Mysz w Panelu Sterowania. Wyświetlony jest tam suwak
regulujący odstęp oraz pole testowe, służące sprawdzeniu ustawienia.
Śledzenie ruchów i położenia wskaźnika myszy
Badanie aktualnego położenia wskaźnika myszy jest bardzo przydatne, jednak istnieją sytuacje, gdy
należy prześledzić trajektorię jego ruchów wykonywanych myszą przez użytkownika. Program
rysunkowy wymaga istnienia takiej procedury, by umożliwić użytkownikowi wykonywanie rysunków "z
wolnej ręki". System Windows wychodzi tej potrzebie naprzeciw, wysyłając komunikaty po każdym
wykonanym myszą ruchu i przekazujące aktualne położenie wskaźnika.
Ograniczenie przetwarzania informacji o ruchach myszy
Twórcy oprogramowania zwykle starają się ograniczać przetwarzanie informacji o ruchach myszy.
Dzieje się tak dlatego, że ogromna liczba tych komunikatów, generowana podczas wykonywania przez
użytkownika wielu szybkich ruchów, powodowałaby obniżenie sprawności działania systemu i
rozbieżności pomiędzy aktualnym położeniem wskaźnika myszy a zadaniami wykonywanymi na
ekranie.
Obsługa zdarzenia przesunięcia wskaźnika myszy
Podobnie jak informacje o użyciu klawiszy myszy, informacje o przesunięciu wskaźnika wysyłane
są do aplikacji za pomocą odpowiedniego komunikatu: WM_MOUSEMOVE. Funkcję, która obsługiwać
będzie ten komunikat, umieszczamy w programie w taki sam sposób jak funkcje dla komunikatów
klawiszy. Wykonajmy więc procedurę z punktu "Dodanie funkcji obsługi komunikatu lewego klawisza
myszy" opisaną wcześniej, jako komunikat docelowy wybierając WM_MOUSEMOVE. CIassWizard
wygeneruje kod nowej funkcji obsługi OnMouseMove (), wypisanej na listingu 8.2.
Listing 8.2. LST8_2.CPP - wykorzystanie funkcji OnMouseMove () do wyświetlenia i przechowania
pozycji wskaźnika myszy po wykonaniu jej przesunięcia
1 void CMouseMsgDlg::OnMouseMove(UINT nFlags, CPoint point)
2 {
3 // TODO: Dodaj tu kod obsługi komunikatu lub/i


4
5 // ** Wyświetl aktualne współrzędne wskaźnika myszy
6 CString strMessage;
7 strMessage.Format("Mouse Position = (%d,%d)",
8 point.x,point.y);
9 SetWindowText(strMessage) ;
10
11 // ** PrzecAowaj współrzędne punktu l odśwież okno
12 m ptMouse = point;
13 Invalidate();0
14
15 CDialog::OnMouseMove(nFlags, point);
16 }
O Pozycja wskaźnika myszy zostaje zachowana w składowej zmiennej, do późniejszego
wykorzystania w funkcji OnPaint ().
Należy zauważyć, iż parametry przekazywane funkcji OnMouseMove () są tymi samymi
parametrami, co w przypadku funkcji obsługi komunikatów o naciśnięciu klawiszy myszy.
Wartość wskaźnika nFlags można tu porównać ze wartościami wymienionymi w tabeli 8.1, w
ten sam sposób, jak czyni to funkcja obsługi na listingu 8.1. Drugim parametrem jest point, w
którym zapisane są aktualne koordynaty wskaźnika myszy; są one wyświetlane w pasku
tytułowym okna dialogowego, co następuje w liniach 6-9 listingu 8.2. Funkcja lnvalidate (),
wywołana w linii 13, wymusza odświeżenie widoku okna.
Pozycję wskaźnika myszy przechować można w zmiennej składowej CMouseMsgDlg,
jak to zostało wykonane w linii 12 listingu 8.2. Po utworzeniu właściwej funkcji musimy
jeszcze umieścić w definicji klasy okna dialogowego zmienną składową m_ptMouse. Jest to
proste zadanie, które wykonamy według poniższej procedury.
Dodanie składowej zmiennej do klasy okna dialogowego
1. Kliknij kartÄ™ ClassView w panelu Project Workspace.
2. Kliknij znak + widoczny przy jedynej wyświetlonej pozycji, po czym wyświetlone zostaną
klasy użyte w bieżącej aplikacji.
3. W celu otwarcia menu skrótów, kliknij prawym klawiszem myszy pozycję CMouse-
MsgDlg.
4. Wybierz opcję Add Member Variable, co wywoła okno dialogowe o tej samej nazwie.
5. W polu Variable Type wprowadź CPoint.


194_____________________________________Poznaj Visual C++ 6
6. Wpisz nazwÄ™ zmiennej: m_ptMouse w oknie Variable Declaration. PozycjÄ™ Access Type pozostaw
niezmienioną, czyli Public Access (umożliwi to dostęp do zmiennej wszystkim obiektom). Gdyby
wybrana została pozycja Protected bądź Private, dostęp do nowej zmiennej miałyby tylko obiekty
klasy CMouseMsgDIg.
7. Kliknij OK, co zakończy działanie CIassWizard i doda nową zmienną.
Dodana właśnie zmienna m_ptMouse może zostać użyta przez funkcję OnPaint () do odświeżenia
widoku okna z nowymi współrzędnymi wskaźnika myszy. Dopiszemy kod zamieszczony na listingu 8.3,
by umieścić w oknie dialogowym parę oczu, które będą "obserwowały" ruchy wskaźnika myszy na
ekranie. Aby dodać ten kod, musimy dwukrotnie kliknąć pozycję Onpaint () na karcie CIassYiew w
panelu Project Workspace. Być może konieczne będzie również kliknięcie znaku + widocznego obok
pozycji CMo-useMsgDlg, by uzyskać dostęp do funkcji tej klasy. Funkcja Onpaint () zawiera już kod
rysujący ikonę aplikacji, gdy funkcja lslconic() zwraca wartość TRUE (okno aplikacji jest
zminimalizowane). Powinniśmy to jednak zignorować i dodać własny kod wewnątrz instrukcji else
pomiędzy klamrami, tuż za komentarzem // TODO :.
Listing 8.3. LST8_3.CPP - rysowanie pary oczu śledzących ruchy wskaźnika myszy





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
else (
// ** Utwórz kontekst urządzenia
CPaintDC dc(this); O
// ** Określ wymiary okna dialogowego CRect
rcDIg;
GetCIientRect(SrcDIg) ;
// ** Wykonaj pętlę osobno dla każdego oka
for(int i=0;i<2;i++)
( •
// ** Określ pozycję środka oka CPoint
ptEye = rcDIg.CenterPoint() ;
// ** Ustaw pozycjÄ™ obu oczu ptEye.x +=
i==0 ? -80 : +80; @
// ** MÄ…kÄ™ an eye rectangle CRect
rcEye(ptEye,ptEye);
rcEye.InflateRect(40,60) ;
// ** Wypełnij oko białym kolorem
dc.SelectStockObject(WHITE_BRUSH) ;
dc.Ellipse(rcEye); ©


Obsługa zdarzeń wywoływanych myszą 195





26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
rcEye.DeflateRect(20,40) ;
// ** Pobierz pozycję wskaźnika myszy w celu zmiany
II ** położenia źrenicy
CPoint ptRel = m_ptMouse - rcEye.CenterPoint ();
double dX = (double)ptRel.x *
(rcEye.Width() / (double)rcDIg.Width());
double dY = (double)ptRel.y *
(rcEye.HeightO / (double)rcDIg.Height());©
// ** Przesuń źrenicę 2 narysuj ją
rcEye.OffsetRect(CPoint((int)dX,(int)dY)) ;
dc.SelectStockObject(BLACK_BRUSH) ;
dc.Ellipse(rcEye); ©
CDialog::OnPaint () ;
}





O Zainicjalizowany zostaje kontekst urzÄ…dzenia rysujÄ…cego.
@ Początkowa pozycja każdego oka zostaje określona na 80 pikseli od pionowej osi symetrii okna
dialogowego.
© Funkcja Ellipse () wykreÅ›la biaÅ‚e elipsy oczu.
O Bieżąca pozycja wskaźnika myszy zostaje użyta do obliczenia przesunięcia źrenic.
© Źrenice zostajÄ… odrysowane w oparciu o pozycjÄ™ wskaźnika myszy.
Na powyższym listingu w linii 4 spotykamy się z obiektem kontekstu urządzenia cpa-intDC.
Zagadnienia związane z kontekstem urządzenia zostaną omówione w rozdziale 15. Wywołana w linii 8
funkcja GetdientRect () służy do określenia wymiarów okna dialogowego, które zostają zapisane w
obiekcie rcDig zadeklarowanym w Unii7, a należącym do CRect. CRect to klasa przechowująca dwa
obiekty CPoint, jeden dla koordynat górnej i lewej, drugi natomiast dla dolnej i prawej strony. Dostarcza
również kilku przydatnych funkcji służących do określania punktu środkowego, szerokości i wysokości
oraz zmniejszania i zwiększania wymiarów okna.
W linii 11 rozpoczęta zostaje pętla, wykonywana dwukrotnie, osobno dla każdego oka. Określa ona
rozmiary prostokąta mieszczącego każde oko (w linii 14) i umieszcza je po obu stronach osi symetrii
okna dialogowego (linia 17). W linii 20 z kolei określony jest ostateczny rozmiar, a w liniach 24-25
narysowane są białe elipsy. W linii 26 określone zostają wymiary źrenicy.


196 _Poznaj Visual C++ 6
Bardzo interesująca jest linia 29. W linii tej bowiem aktualne położenie wskaźnika myszy
użyte zostaje do wyliczenia pozycji źrenicy każdego z oczu. Przesunięcie ich zostaje obliczone
w liniach 30-36, a linie 37-38 powodują narysowanie źrenic w nowym położeniu.
Nie zajmowaliśmy się tutaj funkcjami graficznymi, gdyż omówione zostaną one w
rozdziale 16.
Po skompilowaniu i uruchomieniu aplikacji w oknie dialogowym widoczne będzie dwoje
oczu ze źrenicami przesuwającymi się wraz ze wskaźnikiem myszy (rysunek 8.2).

Rysunek 8.2. Dwoje oczu podąża za wskaźnikiem myszy podczas przesuwania go wewnątrz okna
dialogowego
PATRZ TAKŻE
• WiÄ™cej informacji o dziaÅ‚aniu funkcji OnPain t () znajduje siÄ™ w rozdziale 15. ^ Jak
rysować za pomocą piór i pędzli mówimy w rozdziale 16.
Przechwytywanie wskaźnika myszy
Po uruchomieniu aplikacji zawierającej kod z listingu 8.3 zorientujemy się, iż pojawia się
pewien problem. Wszystko pracuje świetnie do momentu, gdy wskaźnik myszy znajdzie się
poza obszarem okna dialogowego aplikacji. Dzieje się tak, ponieważ komunikaty o wykonaniu
ruchu myszą wysyłane są tylko wtedy, gdy jej wskaźnik pozostaje w obrębie okna. Kiedy
jednak wskaźnik wykroczy poza ten obszar, komunikaty odbierane będą przez okno innej
aplikacji. Istnieje pewna technika umożliwiająca w takiej sytuacji kierowanie komunikatów do
właściwej aplikacji, zwana (ang. mouse capturing) przechwytywaniem myszy.
Przechwytywanie owo realizuje siÄ™ bardzo prosto, za pomocÄ… funkcji SetCapture () z okna
aplikacji, które ma przechwycenie wykonać. Jednakże ten proces jest "aspołeczny"


Obsługa zdarzeń wywoływanych myszą 197
w stosunku do innych aplikacji, które w normalnych warunkach przechwytywałyby mysz. W związku z
tym, należy za każdym razem wywoływać funkcję ReleaseCapture (), która powoduje powrót do
normalnej sytuacji.
Zasady rzÄ…dzÄ…ce przechwytywaniem myszy
Tylko okno znajdujące się na najwyższym poziomie może przechwytywać mysz. Jeśli któreś z okien
niższego poziomu próbuje przechwycenia, otrzyma informacje dotyczące tylko widocznej jego części,
nieprzesłoniętej przez inne okna. Tylko jedna aplikacja może w danym momencie wykorzystywać ten
mechanizm.
Najwłaściwszymi miejscami na umieszczenie obu funkcji są odpowiednio funkcje obsługi
zdarzeń naciśnięcia klawisza myszy oraz jego zwolnienia. Do listingu 8.3 musimy więc
wprowadzić pewne zmiany. Do funkcji OnLButtonDown () dopiszemy taki kod:
void CMouseMsgDlg::OnLButtonDown(UINT nFlags, CPoint point) ( SetCaptureO ;
CDialog::OnLButtonDown(nFlags, point) ;
}
Po wprowadzeniu tej poprawki mysz będzie przechwytywana przez okno dialogowe aplikacji za
każdym razem, gdy naciśnięty zostanie lewy klawisz. Odwołanie przechwy-tywania następować będzie z
kolei po wywołaniu funkcji ReleaseCapture (), umieszczonej w funkcji obsługi OnLButtonUpO . To
również wymaga wprowadzenia dodatkowego kodu:
void CMouseMsgDlg::OnLButtonUp(UINT nFlags, CPoint point) {
ReleaseCapture () ;
CDialog::OnLButtonUp(nFlags, point) ;
}
Można już skompilować i uruchomić program. Kiedy naciśniemy lewy klawisz myszy, gdy jej
wskaźnik znajduje się w obszarze okna dialogowego, możemy tenże wskaźnik przesuwać poza obszar
okna, po całym ekranie. Oczy będą cały czas podążały za wskaźnikiem. Oznacza to, iż okno dialogowe
wciąż odbiera komunikaty WM_MOUSEMOVE, nawet gdy wskaźnik myszy znajduje się poza jego
obrębem. Zauważmy również, że koordynaty wskaźnika będą wyświetlane jako ujemne, gdy znajdzie
kursor się po lewej stronie lub powyżej okna dialogowego. Dzieje się tak dlatego, ponieważ pozycja
wskaźnika jest cały czas obliczana w odniesieniu do lewego, górnego rogu okna dialogowego aplikacji.
Po zwolnieniu klawisza wywołana zostaje funkcja ReleaseCapture (), a oczy powracają do zwykłego
trybu pracy, co oznacza, iż przechwytywanie myszy zostało odwołane.


198_____________________________________Poznaj Visual C++ 6
Ustalanie obszaru testowania
Jednym z normalnych warunków swobodnego operowania myszą jest umożliwienie sprawdzenia,
czy w ustalonym obszarze nastąpiło kliknięcie. Przykładowo, aby spowodować kuknięciem zmianę
koloru wypełniającego owal oka w bieżącym przykładzie, musimy ustanowić procedurę sprawdzającą.
Punkt wskazujÄ…cy kursora
Tylko jeden piksel spośród składających się na obraz kursora (wskaźnika myszy) reprezentuje aktualne
jego położenie. Jest to piksel stanowiący wierzchołek grota strzałki. Punktem tym może być każdy
inny piksel ustanowiony podczas tworzenia kursora. Gdy piksel ten znajdzie siÄ™ w obszarze
testowania, procedura sprawdzajÄ…ca zwraca TRUE.
Procedura ta nie jest niczym więcej, jak tylko sprawdzeniem, czy punkt, w którym nastąpiło
kliknięcie leży wewnątrz określonego obszaru. Po pierwsze, zachowane muszą zostać współrzędne
punktu, w którym użytkownik ostatnio kliknął. Jest to przypadek przypisania parametru point,
przekazanemu funkcji obsługi OnLButtonDown (), do zmiennej składowej:
void CMouseMsgDlg::OnLButtonDown(UINT nFlags, CPoint point) ( SetCapture() ;
m ptButton = point; // **Przechowanie przekazanych współrzędnych }
Musimy dodać zmienną m_ptButton do klasy CMouseMsgDlg w taki sam sposób, jak w przypadku
określania położenia wskaźnika myszy, wykonując procedurę dodawania zmiennej składowej do klasy
okna dialogowego, nadajÄ…c zmiennej nazwÄ™ m_ptButton (typ zmiennej to CPoint).
Mając zachowane współrzędne ostatniego kliknięcia, możemy sprawdzić, czy m_pt-Button leży
wewnątrz jednego z oczu, zmieniając zapis Onpaint () zaraz za linią komentarza // ** Wypełnij oko
białym kolorem w podany poniżej sposób:
if (rcEye.PtInRect(m_ptButton))
dc.SelectStockObject(GRAY_BRUSH) ;
else
dc.SelectStockObject(WHITE_BRUSH) ;
Należąca do CRect funkcja składowa ptlnRect () zwraca wartość TRUE, gdy punkt kliknięcia (w
obecnym przypadku zapisany w m_ptButton) wypada wewnątrz sprawdzanego prostokąta rcEye. Jeśli
warunek ten jest spełniony, zostaje użyty obiekt GRAY_BRUSH w miejsce WHITE_BRUSH i oko wypełnione
zostaje szarym kolorem.


Obsługa zdarzeń wywoływanych myszą 199
Gdy skompilujemy i uruchomimy program, kliknięcie wewnątrz któregoś z oczu po-
wodować będzie zmianę koloru jego wypełnienia na szary, jak zostało to pokazane na rysunku
8.3. Jeśli klikniemy wewnątrz drugiego oka, stanie się ono szare, a pierwsze powróci do
koloru pierwotnego, gdyż punkt kliknięcia wypada wewnątrz innego obszaru. Z kolei
kliknięcie gdziekolwiek poza obszarem oczu spowoduje ponowne wypełnienie ich białym
kolorem, gdyż w tym wypadku punkt kliknięcia znajduje się poza ich obszarami.

Rysunek 8.3. Kliknięcie wewnątrz obszaru oka powoduje zmianę koloru wypełnienia z białego na szary
PATRZ TAKŻE
• O stosowaniu pÄ™dzli (brush) mówimy wiÄ™cej w rozdziale 16.
Stosowanie klasy CRectTracker
W wielu aplikacjach do zaznaczania większej liczby elementów posłużyć się możemy
tzw. "gumową taśmą". Klasą, która umożliwia nam taką operację, jest CRectTracker. Używa
ona kodu, dzięki któremu sama przeprowadza przechwytywanie myszy, pozwalając
użytkownikowi na oznaczanie dowolnych obszarów, a aplikacji na sprawdzenie, czy punkty
kontrolne znajdujÄ… siÄ™ wewnÄ…trz tego obszaru.
Wykorzystamy obiekt tej funkcji w naszym przykładowym programie, w którym posłuży
ona do zakreślania za pomocą gumowej taśmy obszaru wokół oczu, następnie do sprawdzenia,
które oko znajduje się wewnątrz wykreślonego prostokąta.
Aby dodać zmienną składową CRectTracker, o nazwie m_RectTracker, do klasy
CMouseMsgDlg, musimy wykonać poznaną już procedurę dodawania nowej zmiennej do
klasy okna dialogowego.


200 Poznaj Visual C++ 6
Pierwszą rzeczą, którą musimy wykonać, jest inicjalizacja obiektu rect, z parametrami
początkowymi wykreślanego prostokąta oraz stylu. Właściwy kod umieścimy w miejscu inicjalizacji
klasy CMouseMsgDlg, w konstruktorze funkcji:
CMouseMsgDlg::CMouseMsgDlg():
CMouseMsgDlg::CMouseMsgDlg(CWnd* pParent /*=NULL*/ ) , :
CDialog(CMouseMsgDlg::IDD, pParent) , m_RectTracker(CRect(O,0,0,0) , // **ProstokÄ…t
poczÄ…tkowy
CRectTracker::hatchedBorder + // * * Krę skowanie obwódki
CRectTracker::resizeOutside) // **Uchwyty wymiarowania na zewnÄ…trz obszaru
Dostęp do tej funkcji uzyskamy klikając dwukrotnie pozycję CMouseMsgDlg (), należącą do klasy
CMouseMsgDlg widocznej na karcie ClassView, w panelu Project Workspace.
Dodatkowe linie komentarza pokazują, jak zostaje zainicjalizowana zmienna składowa
m_RectTracker z początkowymi współrzędnymi i wymiarami prostokąta równymi zero, w związku z
czym nic nie jest zaznaczone. Drugim parametrem konstruktora CRectTracker jest wyznacznik stylu,
który może być kombinacją znaczników wymienionych w tabeli 8.4. Styl CRectTracker: :hatchedBorder
oznacza wykreślanie grubej, kreskowanej obwódki i jest połączony z CRectTracker::resizeOutside.
Tabela 8.4. Znaczniki stylu CRectTracker Znacznik
stylu Opis
CRectTracker: : solidLine Podczas zaznaczania obszaru za pomocą "gumowej taśmy" wykreśla
prostokąt linią ciągłą
CRectTracker: : dottedLine Jak wyżej, jednak z użyciem linii kropkowanej CRectTracker: :
hatchedBorder Rysuje grubą kreskowaną linię CRectTracker: : hatchinside Wypełnia
kreskowaniem zaznaczony obszar
CRectTracker:: resizeinside Uchwyty wymiarowania umieszczone zostają wewnątrz wykreślonego
prostokÄ…ta
CRectTracker: : resizeOutside Uchwyty wymiarowania umieszczone zostajÄ… na zewnÄ…trz
wykreślonego prostokąta
Operacja zaznaczania za pomocą "gumowej taśmy" rozpoczynana jest zwykle od naciśnięcia przez
użytkownika lewego klawisza myszy, zatem funkcja obsługi OnLButton-Down () jest właściwym
miejscem rozpoczęcia tego procesu. Cały proces użycia "gumowej taśmy" obsługiwany jest przez funkcję
składową klasy CRectTracker, Trac-kRubberBand (). Dodajmy więc poniższy kod do treści funkcji
OnLButtonDown ():


Obsługa zdarzeń wywoływanych myszą 201
void CMouseMsgDlg::OnLButtonDown(UINT nFlags, CPoint point) (
m_RectTracker.TrackRubberBand(this, point, TRUE);
CDialog::OnLButtonDown(nFlags, point);
)
Jak widzimy, funkcja TrackRubberBand () wymaga podania trzech parametrów. Pierwszy z nich jest
wskaźnikiem okna, w którym użyty zostanie mechanizm "gumowej taśmy". Ponieważ oknem tym będzie
okno dialogowe naszej przykładowej aplikacji, należy przekazać funkcji specjalny wskaźnik C++, this,
by wskazać obiekt MouseMsgDlg (wyprowadzony z klasy cwnd, będący w związku z tym obiektem
okna).
Drugim parametrem jest lewy górny punkt prostokąta wykreślanego za pomocą "gumowej taśmy"
jako punkt początkowy. W tym przypadku przypisać możemy punkt kliknięcia klawiszem myszy.
Trzeci parametr jest opcjonalny. Jeśli jego wartość wynosi TRUE, za pomocą "gumowej taśmy"
można zaznaczać również obszary leżące po lewej stronie i powyżej punktu początkowego. W
przeciwnym wypadku zaznaczać można obszary leżące jedynie po prawej stronie i poniżej punktu
poczÄ…tkowego.
Wykorzystanie "gumowej taśmy" do własnych zadań
Normalne operacje wykonywane za pomocą klasy CRectTracker można zamienić na inne poprzez
wyprowadzenie z niej własnej, zmienionej wersji tej klasy. Istnieje kilka funkcji wirtualnych, takich
jak DrawTrackerRect (), powodujących określony sposób obrysowania oznaczanego obszaru, które
można wbudować we własną klasę.
Poprzednio umieszczaliśmy wewnątrz funkcji OnLButtonDown () funkcję przechwy-tywania myszy
setCapture (), którą musimy w tej chwili usunąć. Obiekt rect bowiem ustanawia własny mechanizm
przechwytywania, który pozostawałby w konflikcie z poprzednim. Musimy usunąć również powiązany
wpis w funkcji OnLButtonUp (), czyli ReleaseCapture().
void CMouseMsgDlg::OnLButtonUp(UINT nFlags, CPoint point)
( CDialog::OnLButtonUp(nFlags, point) ;
}
Na koniec należy jeszcze zmodyfikować w podany poniżej sposób treść funkcji OnPa-int (), by
mogła sprawdzać wartość składowej obiektu rect. Klasa CRectTracker posiada własną funkcję HitTestO,
która może zostać wywołana poprzez przekazanie obiektu CPoint do sprawdzenia:


202 Poznaj Visual C++ 6
// ** Wypełnij oko białym kolorem if
(m_RectTracker.HitTest(rcEye.CenterPoint() ) !=CRectTracker::hitNothi ng)/
dc.SelectStockObject(GRAY_BRUSH) ;
else dc.SelectStockObject(WHITE_BRUSH) ;
Poprzez przekazanie rcEye. CenterPoint () do funkcji HitTestO dokonujemy sprawdzenia, czy punkt
środkowy oka wypada wewnątrz wykreślonego prostokąta. Funkcja HitTest () zwróci CRectTracker:
:hitNothing (-1), jeśli prostokąt nie obejmuje żadnego z tych punktów. W przeciwnym wypadku wartość
zwrócona może przyjąć jedną z postaci wymienionych w tabeli 8.5, identyfikując położenie
sprawdzanego punktu w wykreślonym prostokącie. Normalnie jednak używane są dwie z poniższych
wartości:
CRectTracker: :hitMiddle, gdy prostokąt obejmie swym zasięgiem punkt środkowy oka oraz
CRectTracker:: hitNothing, kiedy tak nie jest.
Tabela 8.5. Wartości zwracane przez funkcję HitTest () Zwracana
wartość Wartość Znaczenie





CRectTracker::hitNothing -
l
CRectTracker::hitTopLeft Q
CRectTracker;:hitTopRight l
CRectTracker::hitBottomRight 2
CRectTracker::hitBottomLeft 3
CRectTracker::hitTop 4
CRectTracker::hitRight 5
CRectTracker::hitBottom 6
CRectTracker::hitLeft 7
CRectTracker::hitMiddle g


Punkt nie został objęty zasięgiem prostokąta
Punkt wypada w lewym górnym rogu
prostokÄ…ta
Punkt wypada w prawym górnym rogu
prostokÄ…ta
Punkt wypada w prawym dolnym rogu
prostokÄ…ta
Punkt wypada w lewym dolnym rogu
prostokÄ…ta
Punkt wypada u góry prostokąta
Punkt wypada po prawej stronie prostokÄ…ta
Punkt wypada u dołu prostokąta
Punkt wypada po lewej stronie prostokÄ…ta
Punkt wypada pośrodku prostokąta





Po skompilowaniu i uruchomieniu programu z wprowadzonymi zmianami można wy-
kreślać prostokąt za pomocą "gumowej taśmy", przy użyciu myszy z wciśniętym lewym
klawiszem. Prostokąt ten, służący do selekcji obiektów wewnątrz jego obszaru, możemy


Obsługa zdarzeń wywoływanych myszą 203
wymiarować do żądanych rozmiarów przytrzymując klawisz myszy. Po jego zwolnieniu rozmiary
prostokąta zostaną zatwierdzone, a obiekty wewnątrz prostokąta zaznaczone. W bieżącym przykładzie,
gdy punkty środkowe oczu lub jednego z nich, znajdą się wewnątrz wykreślonego prostokąta, zostaną
wyselekcjonowane, a kolor ich wypełnienia zmieni się na szary, jak na rysunku 8.4.

Rysunek 8.4. Użycie CRectTracker do zaznaczania obszarów za pomocą "gumowej taśmy"


Wyszukiwarka

Podobne podstrony:
obsluga zdarzen 9
713[08] Z5 02 Wykonywanie zabezpieczeń przed korozją biologiczną i działaniem ognia
713[08] Z1 02 Wykonywanie podstawowych robót zbrojarskich i betoniarskich
713[08] Z4 03 Wykonywanie izolacji przeciwdrganiowych maszyn i urządzeń przemysłowych
37 pm 08 obsługa i konserwacja szlifierek
Cw 7 Obsługa zdarzeń Materiały dodatkowe
Cw 7 Obsługa zdarzeń Materiały dodatkowe
713[08] Z5 03 Wykonywanie powłok chemoodpornych
713[08] Z3 06 Wykonywanie izolacji zimnochronnych rurociągów i komór chłodniczych
713[08] Z4 04 Wykonywanie izolacji akustycznych obiektów o podwyższonych wymaganiach akustycznych
713[08] Z3 05 Wykonywanie izolacji termicznych kotłów, turbin i pieców przemysłowych
15 Wykonywanie obsługi i konserwacji układów automatyki

więcej podobnych podstron