Po krátké přestávce se opět vracím k pokračování seriálu o Win32 API. Posledně jsme se naučili nahrávat ikony a vytvářet menu programu, dnes se naučíme s tímto menu pracovat. Budeme se zaměřovat na zprávu WM_COMMAND.
Vytvoření a nahrání menu je jedna věc, to už umíme, ale samozřejmě potřebujeme s tímto menu pracovat a dnes si právě ukážeme, jak na to. Vrátíme se tedy ke kódu, který jsme vytvořili minule.
Máme vytvořené menu a jak tedy na něj reagovat? Jak říci Windows, že když klepnu na tu nebo tu položku v menu, že se má vykonat ta nebo ta operace? No není to zas tak těžké, jak by se zdálo a jistě zjistíte, že to je vlastně naprosto „Easy“ :-)
Nabídky a zprávy
Když vybereme položku nabídky, Windows obvykle zasílají proceduře okna několik různých zpráv. Ve většině případů program může spoustu těchto zpráv ignorovat a prostě je předat funkci DefWindowProc, jedna z těchto zpráv je zpráva WM_INITMENU, která má následující parametry:
wParam - handle hlavní nabídky
lParam - 0
Hodnota wParam je handle naší nabídky, dokonce i když se vybíra položka systémové nabídky. Zprávu WM_INITMENU programy Windows obvykle ignorují. Ale tato zpráva existuje, a to z důvodu změny nabídky ještě před tím, než je položka vybrána, ale to může uživatele jen mást, a tak tuto zprávu budeme prozatím také ignorovat.
Další zpráva, kterou bude program dostávat, bude zpráva M_MENUSELECT podle toho, jak uživatel přesouvá kurzor. Program může dostávat mnoho zpráv WM_MENUSELECT podle toho, jak uživatel přesouvá kurzor mezi položkami nabídky. Toto je velice užitečné při vytvoření stavového řádku, který může obsahovat plný popis možností nabídky. Parametry zprávy WM_MENUSELECT jsou:
LOWORD(wParam) - vybraná položka: ID nabídky nebo index rozbalovací nabídky
HIWORD((wParam) - příznaky výběru
lParam - handle nabídky, která obsahuje vybranou položku
Zpráva WM_MENUSELECT je zprávou pohybu po nabídce. Hodnota wParam říká, která nabídka je zrovna vybrána. Příznaky výběru v horním slově wParam mohou být kombinací těchto příznaků:
MF_GRAYED
MF_DISABLE
MF_CHECKED
MF_BITMAP
MF_POPUP
MF_HELP
MF_SYSMENU
MF_MPUSESELECT.
Nejdůležitější zprávou nabídek a pro nás nejpoužívanější je zpráva WM_COMMAND. Tato zpráva říká, že uživatel klepnul na nějakou přístupnou položku nabídky. S touto zprávou se setkáme i při používáni dceřiných prvku, protože z nich vychází. Pokud se stane, že požijeme stejné ID pro nabídky a dceřiné ovládací prvky, můžeme mezi nimi rozlišovat pomocí testování hodnot lParam, která bude u položky nabídky 0.
LOWORD(wParam) - ID nabídky ...... ID ovládacího prvku
HIWORD(wParam) - 0 ....... oznamovací kod
lParam - 0 ....... handle dceřiného okna
Zpráva WM_SYSCOMMAND je podobná zprávě WM_COMMAND až na to, že zpráva WM_SYSCOMMAND signalizuje, že uživatel klepnul na nějakou přístupnou položku ze systémové nabídky:
wParam - ID nabídky
lParam - 0
U této zprávy ale budou hodnoty LOWORD (lParam) a HIWORD (lParam) obsahovat obrazovkové souradnice x,y myši. U zprávy WM_SYSCOMMAND určuje ID nabídky, která položka systémové nabídky byla vybrána. U předdefinovaných položek systémové nabídky by měly být spodní čtyři bity odfiltrovány operací AND s hodnotou 0xFFF0. Výsledná hodnota bude jedna z následujících:
SC_SIZE
SC_MOVE
SC_MINIMIZE
SC_MAXIMIZE
SC_NEXTWINDOW
SC_PREVWINDOW
SC_CLOSE
SC_VSCROLL
SC_HSCROLL
SC_ARRANGE
SC_RESTORE
SC_TASKLIST
Navíc parametr wParam může být SC_MOUSEMENU, nebo SC_KEYMENU.
Když přidáme do systémové nabídky některé položky, spodní slovo wParam bude ID nabídky, kterou jsme zadali. Aby se předešlo kolizi s předdefinovanými ID v nabídce, používají se hodnoty menší než 0xF000. Je to důležité, protože normálně předáváme zprávy WM_SYSCOMMAND dále funkci DefWindowProc. Pokud se toto nedodrží, zablokují se prakticky normální příkazy systémové nabídky.
Poslední zpráva, kterou si popíšeme, je zpráva WM_MENUCHAR, která v podstatě není zpráva nabídky. Windows tuto zprávu odesílají proceduře okna v jednom ze dvou případů:
Pokud uživatel stiskne Alt a znak, který neodpovídá položce nabídky, nebo když je zobrazena rozbalovací nabídka a uživatel stiskne klávesu znaku, který neodpovídá žádné položce v této nabídce. Parametry, které doprovázejí zprávu WM_MENUCHAR jsou tyto:
LOWORD(wParam) - kód znaku (ASCII nebo UNICODE)
HIWORD(wParam) - kód vyberu
lParam - handle nabídky
a kód vyběru je:
0 - žádná nabídka není vybrána
MF_POPUP - rozbalovací nabídka
MF_SYSMENU - systémová nabídka je zobrazena
Programy Windows obvykle předávají zprávy funkci DefWindowProc, která normálně vrací do Windows 0. To způsobí, že Windows pípnou.
Zpráva WM_COMMAND
Pro ovládání nabídky budeme používat zprávu WM_COMMAND. Tak tedy trochu blíže ke kódu. Jelikož jde vlastně o zprávy, budeme se tedy zajímat o proceduru okna.
První věc, co musíme udělat, je získat hale naší nabídky, abychom s ní mohli pracovat, takže si nadefinujeme proměnnou hMenu typu HMENU. Nejlépe na začátek zpracování zpráv si umistíme zprávu WM_COMMAND a jelikož se jedná o blok zpráv, které budou reagovat na různé položky, umístíme zprávy položek do přepínače switch. Na zprávy se reaguje jako na jakékoliv jiné zprávy, procedura okna by tedy mohla vypadat nějak takto:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HMENU hMenu;
switch(message)
{
case WM_COMMAND:
hMenu=GetMenu(hWnd);
switch(LOWORD(wParam))
{
case IDM_ABOUT:
MessageBox(hWnd,TEXT("Já jsem úžasná zpráva"),TEXT("Systém"),
MB_OK | MB_ICONINFORMATION);
return 0;
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
A to je základ, podle kterého budete jistě schopni doplnit obsluhu i ostatních položek nabídky.
Ošetřili jsme položku „O aplikaci“, kde vytváříme jednoduchý dialog pomocí funkce MessageBox(). To se v praxi samozřejmě dělá pomocí dialogových oken, ale jelikož na ty je ještě brzy, tak se prozatím spokojíme s tímto řešením. Dialogová okna si probereme, až zvládneme dceřiné prvky.
Slíbil jsem, že nakousnu i vykreslení textu do klientské oblasti okna. A taky že ano. Tím otevřu delší sérii kapitol o GDI, o kterém si řekneme více v příštím díle.
Funkce DrawText()
Pro výpis textu na obrazovku se dá použít funkceDrawText().
DrawText(hdc, psText, -1, iFlags);
DrawText nakreslí text do klientské oblasti okna. psText je ukazatel na řetězec znaků. -1 říká, že řetězec je ukončen znakem nula. iFlags jsou bitové příznaky, jak má být text zobrazen. Parametr hdc se nazývá „handle kontextu zařízení“, to je pro nás něco zcela nového, ale budeme to používat hodně často. hdc je důležitou součástí GDI. Téměř každá funkce GDI vyžaduje tento handle jako svůj parametr. V příštím díle se k tomuto vrátíme a blíže vysvětlíme.
Abychom mohli kreslit do klientské oblasti, musíme získat kontext zařízení (hdc). Ten získáme voláním funkce BeginPaint ve zprávě WM_PAINT, poté použijeme kreslicí funkce a zavoláme funkci EndPaint, tím uvolníme kontext. Každý kontext, který jsme vytvořili, bychom měli po jeho použití uvolnit!
Volání BeginPaint vyžaduje manipulátor okna a adresu struktury PAINTSTRUCT, což je struktura, která v sobě uchovává důležité hodnoty pro kreslení. I o ní blíže až příště. Takže obsluha zprávy WM_PAINT by mohla vypadat takto:
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
DrawText(hDC, TEXT("Sluníčko je milé a roztomilé :-)"), -1, &rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hWnd, &ps);
return 0;
Tímto voláním se vykreslí text do středu klientské oblasti. Jak a kde je text zobrazen nám určují příznaky, což jsou vlastně číselné konstanty. Říkají, že text má být zobrazen na jeden řádek (DT_SINGLELINE), vystředěn vodorovně a na střed (DT_VCENTER a DT_CENTER). V příštím díle se k tomuto vrátíme.
A teď by procedura okna mohla vypadat takto:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HMENU hMenu;
HDC hDC;
PAINTSTRUCT ps;
RECT rect;
switch(message)
{
case WM_COMMAND:
hMenu=GetMenu(hWnd);
switch(LOWORD(wParam))
{
case IDM_ABOUT:
MessageBox(hWnd,TEXT("Jsem informační zpráva"),TEXT("Systém"),
MB_OK|MB_ICONINFORMATION);
return 0;
}
return 0;
case WM_PAINT:
GetClientRect(hWnd, &rect);
hDC=BeginPaint(hWnd, &ps);
DrawText(hDC, TEXT("Sluníčko je milé a roztomilé :-)"), -1, &rect,
DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
Pro dnešek vše. V příštím díle si povíme něco o GDI, doladíme detaily okolo textu, odhalíme funkci TextOut() a začneme něco málo kreslit :-)
Zatím :-)