Akceleratory to inaczej skróty klawiszowe do elementów menu. Jak być może już zauważyłeś, w pliku *.rc menu możemy sobie dopisać jakiś skrót, używając sekwencji ucieczki
\t (wstawiającej tabulację), np.:
MENUITEM "&Nowy\tCtrl+N", 100
Tyle tylko, że ten skrót nie będzie sobie ot tak po prostu działał. Tutaj właśnie potrzebujemy akceleratorów.
Edycja pliku zasobów
Podobnie jak samo menu, tabele akceleratorów umieszcza się w zasobach (*.rc). Możemy to zrobić ręcznie albo skorzystać z odpowiedniego programu, np.
ResEdit). Wygląda to mniej więcej tak:
IDR_ACCELERATORS ACCELERATORS
{
VK_F2, ID_ACC_F2, VIRTKEY
}
Ta deklaracja ma podobną formę, jak wszystkie inne rodzaje zasobów (najpierw identyfikator, u nas
IDR_ACCELERATORS (chyba nie muszę dodawać, że identyfikator należy wcześniej zdefiniować za pomocą
#define, najlepiej w pliku nagłówkowym), potem typ zasobu, czyli
ACCELERATORS, a następnie nawiasy klamrowe oznaczające blok z konkretnymi akceleratorami. Pojedynczy akcelerator składa się z: klawisza, identyfikatora oraz dodatkowych flag, które za chwilę omówimy (tutaj jest tylko
VIRTKEY).
Klawisz, z którym skojarzony jest akcelerator, może być podany w formie kodu ASCII albo jako kod wirtualnego klawisza. Kod ASCII jest najczęściej używany dla literek, natomiast specjalne klawisze (np. F2) podajemy jako kody VK, czyli tak jak powyżej. Gdybyśmy chcieli dodać jeszcze akcelerator dla klawisza N, tabela wyglądałaby następująco:
IDR_ACCELERATORS ACCELERATORS
{
VK_F2, ID_ACC_F2, VIRTKEY
"n", ID_ACC_N, ASCII
}
Dodatkowo możemy zdefiniować modyfikatory Shift, Alt i Ctrl. W przypadku kodów ASCII dodanie klawisza Shift jest bardzo proste, wystarczy umieścić w deklaracji akceleratora dużą literę zamiast małej. Można też zrobić następująco:
IDR_ACCELERATORS ACCELERATORS
{
VK_F2, ID_ACC_F2, SHIFT, VIRTKEY
"n", ID_ACC_N, CONTROL, VIRTKEY
}
Pierwszy akcelerator z powyższego przykładu zareaguje na kombinację Shift+F2, a drugi – Ctrl+N.
Zauważ, że ten drugi akcelerator ma flagę VIRTKEY zamiast ASCII. Dodanie modyfikatora CONTROL lub SHIFT (ale nie ALT!) wymaga bowiem VIRTKEY. Kod klawisza "n" na szczęście jest i tak rozpoznawany prawidłowo.
|
Ładowanie i użycie akceleratorów
Skoro wiemy już, jak tworzyć tabelę akceleratorów, możemy spróbować użyć jej w programie. Najpierw trzeba oczywiście załadować tabelę z zasobów:
HACCEL hAccel = LoadAccelerators( hInstance, MAKEINTRESOURCE( IDR_ACCELERATORS ) );
if( !hAccel )
{
MessageBox( hWnd, "Nie można załadować akceleratorów.", NULL, MB_ICONEXCLAMATION );
}
Kolejna sprawa to wywołanie funkcji
TranslateAccelerator, co musimy zrobić w pętli komunikatów. Właśnie ta funkcja zajmuje się sprawdzaniem, czy wciśnięta kombinacja klawiszy figuruje w tabeli akceleratorów i (jeśli jest) jaki identyfikator jest jej przypisany. Szukamy więc naszej pętli, która prawdopodobnie wygląda jakoś tak:
MSG msg;
while( GetMessage( & msg, NULL, 0, 0 ) )
{
TranslateMessage( & msg );
DispatchMessage( & msg );
}
Funkcja
TranslateAccelerator dostaje uchwyt naszego głównego okna, uchwyt tabeli akceleratorów, który dostaliśmy od funkcji
LoadAccelerators oraz wskaźnik na komunikat (u nas
&msg), po czym zwraca
true jeśli komunikat pochodzi od akceleratora. Wówczas nie powinniśmy już dalej przetwarzać komunikatu. W przeciwnym wypadku wołamy normalnie
TranslateMessage i
DispatchMessage. Tak więc teraz nasza pętla będzie następująca:
MSG msg;
while( GetMessage( & msg, NULL, 0, 0 ) )
{
if( !TranslateAccelerator( hWnd, hAccel, & msg ) )
{
TranslateMessage( & msg );
DispatchMessage( & msg );
}
}
Akceleratory powinny już działać, co jednak nie zmienia nic w naszym programie, ponieważ ich działanie sprowadza się tylko do wysłania komunikatu
WM_COMMAND, którego nie obsługujemy. Możemy to jednak zrobić:
case WM_COMMAND:
{
if( LOWORD( wParam ) == ID_ACC_F2 )
{
MessageBox( hWnd, "Wcisnąłeś F2.", "Wow.", MB_ICONINFORMATION );
}
}
break;
Nie ma tu nic nadzwyczajnego, wygląda to niemal identycznie, jak obsługa poleceń menu... no właśnie, przecież akceleratory miały nam służyć do ułatwienia obsługi menu, a tymczasem po prostu obsłużyliśmy sobie klawisz. Gdy używamy akceleratorów, prościej jest wprowadzać kombinacje klawiszy, ale nie w tym rzecz (a raczej nie tylko w tym). Chodzi głównie o to, żeby wprowadzanie każdego nowego klawisza nie wymagało pisania dodatkowego kodu, tylko dodania tego klawisza do zasobów, czyli zwykle poklikania w edytorze.
Jeśli więc zależy nam tylko na dodaniu skrótu klawiszowego do określonej pozycji w menu, to nie dajemy akceleratorowi nowego identyfikatora, tylko przypisujemy mu ten sam identyfikator, co owej pozycji menu, np.:
IDR_ACCELERATORS ACCELERATORS
{
VK_F2, ID_MENU_FILE_NEW, VIRTKEY
}
Jeśli już wcześniej obsłużyliśmy kliknięcie
ID_MENU_FILE_NEW w menu, to taki akcelerator zadziała automatycznie – nie będziemy musieli nic dopisywać do obsługi
WM_COMMAND. W razie jednak gdybyś kiedyś zapragnął sprawdzić, czy dany komunikat
WM_COMMAND pochodzi od akceleratora, czy od menu, wystarczy zbadać wyrażenie
HIWORD(wParam); jeśli jest równe 0, to komunikat przyszedł od menu, natomiast akceleratory wpisują tam 1.
Tworzenie akceleratorów w run-time
Akceleratory można tworzyć w zasobach, ale można je tworzyć również już podczas działania programu. Zazwyczaj użytkownik uznaje nagłe zmiany działania kombinacji klawiszy w programie za głupi kawał, ale zapewne istnieją też bardziej konstruktywne zastosowania tej techniki ;-). Żeby daleko nie szukać, wyobraźmy sobie dialog, w którym użytkownik może sobie sam zdefiniować skróty klawiszowe. Aby taki zrobić, musimy zrezygnować z wstawiania akceleratorów do zasobów i stworzyć je właśnie w fazie ''run-time''.
Aby stworzyć nową tabelę akceleratorów zamiast ładować ją z zasobów, używamy funkcji
CreateAcceleratorTable. Jej głównym parametrem jest wskaźnik na strukturę
ACCEL. Jest to bardzo prosta strukturka:
struct ACCEL
{
BYTE fVirt;
WORD key;
WORD cmd;
};
Ponieważ widzieliśmy już, jak wyglądają deklaracje akceleratorów w pliku *.rc, więc domyślamy się, jak wypełnić tę strukturę. Pole
key ma zawierać kod klawisza (ASCII lub VK),
cmd – identyfikator akceleratora (czyli kod polecenia w menu), zaś
fVirt to kombinacja następujących flag:
Znaczenia pierwszych trzech domyślamy się z łatwością, natomiast
FVIRTKEY jest odpowiednikiem
VIRTKEY w pliku zasobów; tę ostatnią flagę dajemy wtedy, gdy podany kod klawisza to VK. Natomiast jeśli podajemy kod ASCII, po prostu pomijamy flagę
FVIRTKEY.
Zwykle tworzymy więcej niż jeden akcelerator w tabeli, więc wygodnie jest wpakować strukturę
ACCEL do tablicy:
ACCEL acc[ 2 ];
acc[ 0 ].key = VK_F2;
acc[ 0 ].cmd = ID_MENU_FILE_NEW;
acc[ 0 ].fVirt = FVIRTKEY;
acc[ 1 ].key = "a";
acc[ 1 ].cmd = ID_MENU_FILE_APPEND:
acc[ 1 ].fVirt = 0;
Teraz możemy już wywołać
CreateAcceleratorTable, podając adres powyższej tablicy, a także liczbę elementów w tablicy:
HACCEL hAccel = CreateAcceleratorTable( acc, 2 );
Z otrzymanym uchwytem postępujemy podobnie, jak w przypadku akceleratorów ładowanych z zasobów, czyli wołamy
TranslateAccelerator. Tym razem jednak musimy skasować tabelę akceleratorów, gdy przestanie już być używana:
DestroyAcceleratorTable( hAccel );