Sunteți pe pagina 1din 37

Capitolul 10

Meniuri i taste de
accelerare
mi amintesc o schi a lui Monty Python despre un magazin de brnzeturi. Era cam aa: un tip intr ntr-un
magazin de brnzeturi i cere un anumit sortiment de brnz. Desigur, magazinul nu avea aa ceva. Tipul
cere un alt sortiment, apoi altul i altul (n total cam 40 de sortimente de brnz) i rspunsul este n
continuare: nu, nu, nu. n sfrit, povestea se termin cu nite mpucturi.
Acest incident nefericit ar fi putut fi evitat dac s-ar fi folosit meniuri. Un meniu este o list de opiuni
disponibile. Un meniu i poate spune unui tip flmnd ce poate servi buctria sau, n cazul unui program
Windows, i poate spune utilizatorului ce operaii poate s execute aplicaia respectiv.
Meniurile sunt probabil partea cea mai important a interfeei cu utilizatorul, oferit de programele
Windows, iar adugarea unui meniu la un program este o operaie relativ uoar n programarea sub
Windows. Nu trebuie dect s definii structura meniului n fiierul de resurse i s atribuii un numr de
identificare (ID) unic fiecrui articol din meniu. Specificai apoi numele meniului n structura de clas a
ferestrei. Atunci cnd utilizatorul selecteaz o opiune de meniu, Windows trimite programului un mesaj
WM_COMMAND care conine identificatorul articolului respectiv. Dar nu ne vom opri la acest exemplu
simplu. Unul dintre cele mai interesante lucruri pe care le putei face cu meniurile este s afiai imagini
bitmap n meniu n locul unor iruri de caractere, aa c vom discuta n detaliu modul n care putei s facei
acest lucru.
Tot n acest capitol vom discuta i despre tastele de accelerare". Acestea sunt combinaii de taste
folosite, n principal, pentru dublarea unor funcii de meniu.

MENIURI
Bara de meniu a unei ferestre este afiat imediat sub bara de titlu i este numit uneori meniu principal" sau
meniul de pe primul nivel". Articolele din meniul principal sunt folosite de obicei pentru afiarea unui
meniu derulant (drop-down menu" sau popup menu") numit uneori i submeniu". Putei s definii
niveluri multiple de meniuri derulante: un articol al unui meniu derulant poate s activeze un alt meniu
derulant. Uneori articolele din meniurile derulante activeaz o caset de dialog pentru afiarea unor
informaii suplimentare. (Despre casetele de dialog vom discuta n capitolul urmtor.) Majoritatea
ferestrelor principale afieaz n partea stng a barei de titlu o pictogram mic a programului. Aceast
pictogram apeleaz meniul de sistem, care este tot un meniu derulant.
Articolele din meniurile derulante pot fi validate", ceea ce nseamn c Windows deseneaz un mic marcaj
de validare n partea stng a textului corespunztor articolului de meniu. Marcajele de validare permit
utilizatorului s-i dea seama ce opiuni au fost selectate din meniu. Aceste opiuni se pot exclude
reciproc, dar acest lucru nu este obligatoriu. Articolele din meniul principal nu pot fi validate.
Articolele din meniul principal i din meniurile derulante pot fi activate", dezactivate" (uneori se
folosesc cu acelai sens termenii active" i inactive") sau gri". Articolele de meniu activate i
dezactivate arat la fel pentru utilizatori, dar textul meniurilor gri" este scris cu un ton de gri.
Din punctul de vedere al utilizatorului, articolele de meniu activate, dezactivate sau gri pot fi selectate".
Aceasta nseamn c utilizatorul poate s execute clic pe un articol de meniu dezactivat, poate s mute bara
cursor afiat n video invers pe un articol dezactivat sau poate s marcheze articolul respectiv folosind
tasta cheie corespunztoare. Totui, din perspectiva programuui, articolele de meniu activate, dezactivate
i gri funcioneaz n mod diferit. Windows trimite programului un mesaj WM_COMMAND numai pentru
meniurile activate. Meniurile dezactivate sau gri sunt folosite pentru opiunile care n momentul respectiv
nu sunt valide. Dac vrei ca utilizatorul s tie c o anumit opiune nu este valid, afiai-o cu gri.

Structura meniurilor
Atunci cnd creai sau cnd modificai meniurile unui program, este bine s v gndii la meniul principal i
la meniurile derulante, ca la meniuri diferite. Meniul principal are o variabil handle, fiecare meniu
derulant apelat de meniul principal are o variabil handle proprie i meniul de sistem (care este tot un
meniu derulant) are o alt variabil handle.
Fiecare articol de meniu este definit prin trei caracteristici. Prima caracteristic este ceea ce apare n meniu.
Aceasta poate fi un ir de caractere sau o imagine bitmap. Cea de-a doua caracteristic este fie un numr de
identificare pe care Windows l trimite programului prin mesajul WM_COMMAND, fie un meniu
derulant afiat atunci cnd utilizatorul selecteaz articolul respectiv. A treia caracteristic descrie atributele
articolului respectiv, indiferent dac acesta este activat, dezactivat, gri sau validat.

ablonul meniurilor
Putei s creai un meniu n trei moduri. Cea mai obinuit (i mai simpl) cale este s definii
meniul n fiierul script de resurse sub forma unui ablon de meniu, cum ar fi:
HyMenu MENU { [lista meniului]

MyMenu este numele meniului. Acest nume este referit n structura de clas a ferestrei. De obicei,
numele meniului este acelai cu numele aplicaiei.
ntre acolade putei s folosii instruciunile MENUITEM i POPUP. Formatul instruciunii
MENUITEM este:
MENUITEM "&Text", id [, opiuni]

iar formatul instruciunii

POPUP este:
POPUP "&Text", id [, opiuni] {
[lista meniului] }

n locul acoladelor, dac dorii, putei s folosii cuvintele cheie BEGIN i END. Textul afiat pentru
fiecare meniu trebuie s fie ncadrat ntre ghilimele. Caracterul care urmeaz dup
ampersand (&) va fi afiat subliniat. Acesta este caracterul pe care l va cuta sistemul de
operare Windows atunci cnd selectai meniul cu ajutorul tastei Alt. Dac textul nu conine
caracterul ampersand, nu va fi subliniat nici un caracter, iar Windows va folosi prima liter a
textului pentru combinaiile cu tasta Alt.
Opiunile pe care putei s le folosii pentru instruciunile MENUITEM i POPUP din lista
meniurilor principale sunt:
GRAYED - Articolul respectiv este dezactivat, nu genereaz mesaje
WM_COMMAND, iar textul este afiat cu gri.
INACTIVE - Articolul respectiv este dezactivat, nu genereaz mesaje
WM_COMMAND, iar textul este afiat normal.
MENUBREAK - Articolul respectiv i cele care urmeaz sunt afiate pe o
nou linie a meniului.
HELP - Articolul respectiv i cele care urmeaz sunt aliniate la dreapta.
Opiunile pot fi combinate cu ajutorul simbolului SAU orientat pe bii din C (|), dar opiunile
GRAYED i INACTIVE nu pot fi folosite mpreun. Opiunea MENUBREAK este rareori folosit
pentru articolele din meniul principal, deoarece Windows afieaz automat acest meniu pe mai
multe linii, dac fereastra este prea ngust ca s ncap toate articolele.
Dac urmeaz dup o instruciune POPUP, acoladele (sau cuvintele cheie BEGIN i END)
ncadreaz lista articolelor in meniul derulant. Urmtoarele instruciuni pot fi folosite pentru
definirea unui meniu derulant:
MENUITEM "text", id [, opiuni]
SI
MENUITEM SEPARATOR i

POPUP "text", [, opiuni]

MENUITEM SEPARATOR deseneaz o linie orizontal n meniul derulant. Aceast linie este
deseori folosit pentru separarea grupurilor de articole nrudite.
Pentru articolele din meniurile derulante putei s folosii n irul de caractere caracterul
tab (\t). Textul care urmeaz dup acest caracter este afiat ntr-o nou coloan, destul de
departe, pentru ca n prima coloan s ncap cel mai lung ir de caractere din meniul
derulant. Vom vedea cum funcioneaz acest caracter atunci cnd vom discuta despre tastele

de accelerare, ctre sfritul acestui capitol. Dac n irul de caractere includei combinaia \a,
textul care urmeaz este aliniat la dreapta, n meniurile derulante, pentru instruciunea
MENUITEM putei s folosii urmtoarele opiuni.
CHECKED - n partea stng a textului este afiat un marcaj de validare.
GRAYED - Articolul respectiv este dezactivat, nu genereaz mesaje
WM_COMMAND, iar textul este afiat cu gri.
INACTIVE - Articolul respectiv este dezactivat, nu genereaz mesaje
WM_COMMAND, iar textul este afiat normal.
MENUBREAK - Articolul respectiv i cele care urmeaz sunt afiate pe o
nou coloan a meniului.
MENUBARBREAK - Articolul respectiv i cele care urmeaz sunt afiate
pe o nou coloan a meniului. Coloanele sunt separate de o linie vertical.
Opiunile GRAYED i INACTIVE nu pot fi folosite mpreun. Opiunile MENUBREAK i
MENUBARBREAK nu pot fi folosite mpreun. Putei s folosii opiunea MENUBREAK sau
opiunea MENUBARBREAK atunci cnd meniul derulant conine prea multe opiuni pentru a fi
afiate pe o singur coloan.
Identificatorii din instruciunile MENUITEM sunt numerele pe care Windows le \ trimite
procedurii ferestrei prin mesajele de meniu. Valorile de identificare trebuie s fie unice n cadrul
unui meniu. n locul numerelor putei s folosii identificatori definii ntr-un fiier antet. Prin
convenie, aceti identificatori ncep cu literele IDM (IDentificator de Meniu").

Referirea unui meniu n program


Multe aplicaii Windows au n fiierul script de resurse un singur meniu. Programul face referire
la acest meniu atunci cnd este definit clasa ferestrei:
wndclass.lpszMenuName = "MyMenu" ;

Programatorii folosesc deseori pentru numele meniului numele programului, astfel nct
acelai ir de caractere poate fi folosit pentru clasa ferestrei, numele pictogramei
programului i numele meniului. Totui, n locul numelui putei s folosii un numr (sau un macroidentificator). n acest caz, fiierul script de resurse va arta astfel:
45 MENU (

[definiia meniului] )

n acest caz, instruciunea de atribuire a variabilei membru IpszMenuName din structura clasei
poate fi:
wndclass.IpszMenuName = MAKEINTRESOURCE (45) ; sau
wndclass.IpszMenuName "#45" ;

Dei specificarea meniului n clasa ferestrei este calea cel mai des folosit de referire a unei
resurse de tip meniu, avei i alte posibiliti. O aplicaie Windows poate s ncarce resursa unui
meniu n memorie folosind funcia LoadMenu, asemntoare cu funciile Loadlcon i LoadCursor
descrise n Capitolul 9. Dac n fiierul script de resurse folosii un nume pentru meniu, funcia
LoadMenu returneaz o variabil handle a meniului:
hMenu = LoadMenu (hlnstance, "MyMenu") ; Dac folosii un numr, apelul funciei LoadMenu ia una
dintre formele urmtoare:
hMenu = LoadMenu (hlnstance, MAKEINTRESOURCE (45)) ;

sau
hMenu LoadMenu (hlnstance, "#45") ;

Putei apoi s specificai variabila handle returnat ca al noulea parametru al funciei


CreateWindow:

hwnd - CreateWindow ("MyClass", "Window Caption", WS_OVERLAPPEDWINDOW, CW


USEDEFAULT, CWJJSEDEFAULT, CITUSEDEFAULT, CWJSEDEFAULT, NULL, NULL,
hlnstance, NULL) ;

n acest caz, meniul specificat ca parametru al funciei CreateWindow suprascrie orice meniu
specificat n clasa ferestrei. Putei s considerai meniul din clasa ferestrei ca fiind meniul
prestabilit pentru ferestrele bazate pe clasa ferestrei n cazul n care cel de-al noulea parametru
al funciei CreateWindow are valoarea NULL. Ca urmare, putei s folosii meniuri diferite pentru
mai multe ferestre bazate pe aceeai clas.

Putei s avei un meniu NULL n clasa ferestrei i un meniu NULL la apelarea funciei
CreateWindow, i s atribuii ferestri un meniu dup creare:
SetMenu (hwnd, hMenu) ;

Aceast funcie v permite schimbarea dinamic a meniului unei ferestre. Vom vedea un astfel de
exemplu n programul NOPOPUPS, prezentat mai trziu n acest capitol.
Orice meniu ataat unei ferestre este distrus odat cu fereastra. Orice meniu care nu este ataat unei
ferestre trebuie s fie distrus explicit prin apelarea funciei DestroyMenu, nainte de nchiderea
programului.

Meniuri i mesaje
Sistemul de operare Windows trimite procedurii unei ferestre mai multe mesaje diferite atunci
cnd utilizatorul selecteaz un articol din meniu. n majoritatea cazurilor, programul poate s ignore
aceste mesaje i s le retransmit procedurii DefWindowProc. Un astfel de mesaj este
WMJNITMENU, cu urmtorii parametri:
wParam
Variabila handle a meniului principal

IParam
0

Parametrul wParam conine variabila handle a meniului principal, chiar dac utilizatorul selecteaz un articol din
meniul de sistem. n general, programele Windows ignor mesajul WM_INITMENU. Dei transmiterea acestui
mesaj v d posibilitatea s modificai meniul nainte de selectarea unui articol, bnuiesc c orice modificare n
meniul principal n acest moment ar fi foarte deconcertant pentru utilizator.
De asemenea, programul primete i mesaje WM_MENUSELECT. Programul poate primi mai multe mesaje
WM_MENUSELECT, pe msur ce utilizatorul mut cursorul sau mouse-ul pe diferite articole de meniu. Aa
cum vom vedea n Capitolul 12, acest mesaj este foarte util pentru implementarea unei bare de stare care conine o
descriere complet a opiunii din meniu. Parametrii mesajului WM_MENUSELECT sunt:
LOWORD (wParam)
Articolul selectat: identificatorul de meniu sau variabila
handle a meniului derulant

HIWORD (vParam)
Indicatorul flag de
selectare

IParam
Variabila handle a meniului care
conine articolul selectat

WM_MENUSELECT este un mesaj de urmrire a meniului. Cuvntul mai puin semnificativ al parametrului
wParam specific articolul de meniu care a fost selectat (marcat). Indicatorul flag de selectare" din cuvntul mai
semnificativ al parametrului wParam poate fi o combinaie a urmtorilor identificatori: MF_GRAYED, MF_DISABLED, MF_CHECKED, MF_BITMAP, MFJPOPUP, MFJHELP, MF_SYSMENU i MF_MOUSESELECT. Putei s
folosii mesajul WM_MENUSELECT dac dorii s modificai ceva n zona client a ferestrei n funcie de
deplasarea barei de selectare ntre articolele de meniu. Majoritatea programelor retransmit acest mesaj procedurii
DefWindowProc.

Atunci cnd este pregtit s afieze un meniu derulant, Windows trimite procedurii ferestrei un mesaj
WM_INITMENUPOPUP cu urmtorii parametri:

Capitolul 10 Meniuri i taste de accelerare


wParam

LOWORD (IParam)

HIWORD (IParam)

Variabila handle a
meniului derulant

Indexul meniului derulant

1 pentru meniul sistem, O pentru


alte meniuri

Acest mesaj este important dac vrei s activai sau s dezactivai articole dintr-un meniu derulant nainte ca acesta
s fie afiat. De exemplu, s presupunem c programul ofer utilizatorului posibilitatea de a copia text din memoria
temporar (clipboard) cu ajutorul comenzii Paste, dintr-un meniu derulant. Atunci cnd primii mesajul
WMJNITMENUPOPUP trebuie s determinai dac n clipboard exist vreun text. Dac nu exist, ar trebui s afiai
cu gri articolul de meniu Paste. Vei vedea un astfel de exemplu n programul POPPAD revizuit, prezentat ctre
sfritul acestui capitol.
Cel mai important mesaj este WM_COMMAND. Acest mesaj indic faptul c utilizatorul a selectat un articol activat
din meniul ferestrei. V amintii din Capitolul 8 c mesajul WM_COMMAND este trimis i de controale. Dac folosii
acelai identificator pentru un articol de meniu i pentru un control, putei s difereniai mesajele n funcie de
parametrul IParam, care are valoarea 0 n cazul articolelor de meniu.

Meniu:
Control:

LOWORD (wParam)

HIWORD (wParam)

Identificatorul meniului
Identificatorul controlului

0
Cod de ntiinare

IParam
0
Variabila handle a
ferestrei descendent

Mesajul WM_SYSCOMMAND este asemntor cu mesajul WM_COMMAND, cu excepia faptului c primul


semnaleaz selectarea unui articol activat din meniul de sistem:

Meniu de
sistem:

LOWORD
(wParam)

HIWORD
(wParam)

IParam

Identificatorul

0 (Dac mesajul

meniului

WM SYSCOMMAND a fost
trimis n urma executrii unui clic,
LOWORD (IParam) i HIWORD
(IParam) conin coordonatele X i
Y ale indicatorului mouse-ului.)

Identificatorul de meniu specific articolul selectat din meniul sistem. Pentru articolele de meniu predefinite,
ultimii patru bii ar trebui mascai. Valoarea rezultat poate fi: SC_SIZE, SC_MOVE, SC_MINIMIZE,
SCJMAXIMIZE, SC_NEXTWINDOW, SCJPREVWINDOW, SQCLOSE, SCJVSCROLL, SC_HSCROLL,
SC_ARRANGE, SC_RESTORE i SC_TASKLIST. n plus, cuvntul mai puin semnificativ al parametrului
xoPram poate fi SC_MOUSEMENU sau SC_KEYMENU.
Dac adugai noi articole n meniul sistem, cuvntul mai puin semnificativ al parametrului wParam va
conine numrul de identificare al articolului pe care l-ai definit. Pentru a evita conflictele cu identificatorii
articolelor predefinite, folosii valori mai mici de OxFOOO. Este foarte important ca mesajele
WM_SYSCOMMAND s fie trimise ctre procedura DefWindowProc. n caz contrar, vei dezactiva comenzile
normale din meniul sistem.
Ultimul mesaj despre care vom discuta este WM_MENUCHAR, care nu este un mesaj de meniu. Windows
trimite acest mesaj ctre procedura ferestrei n dou situaii: dac utilizatorul apas tasta Alt i o tast care nu
corespunde unui articol de meniu, sau atunci cnd este afiat un meniu derulant, dac utilizatorul apas o tast
care nu corespunde nici unui articol din meniul respectiv. Mesajul WM_MENU- CHAR are urmtorii
parametri:
LOWORD
(wParam)

HP/VORD (wParam)

IParam

Cod ASCII

Cod de selectare

Variabil handle a meniului

Codul de selectare poate avea urmtoarele valori:

0 - Nu a fost afiat nici un meniu derulant.

MF_POPUP - Este afiat un meniu derulant.

MF_SYSMENU - Este afiat meniul sistem.


De obicei, programele Windows transmit acest mesaj procedurii DefWindowProc, care returneaz sistemului
de operare Windows valoarea 0, avnd ca rezultat emiterea unui semnal sonor (beep). Vom vedea cum este
folosit mesajul WM_MENUCHAR n programul GRAFMENU prezentat mai trziu n acest capitol.

Un program de exemplificare
Haidei s vedem un mic exemplu. Programul MENUDEMO, prezentat n Figura 10-1, are patru articole n
meniul principal - File, Edit, Background, Timer i Help. Fiecare dintre aceste articole are un meniu derulant.
MENUDEMO execut cele mai simple i mai obinuite operaii de prelucrare a meniurilor, care implic
interceptarea mesa-jmlui WM_COMMAND i verificarea cuvntului mai puin semnificativ al parametrului
wParam.

MENUDEMO.MAK

#
#Fiierul de construcie MENUDEMO.MAK
#
menudemo.exe:menudemo.objmenudemo.res
$(LINKER)$(GUIFLAGS)OUT:menudemo.exemenudemo.obj\
menudemo.res$(GUILIBS)
menudemo.obj:menudemo.cmenudemo.h
$(CC)$(CFLAGS)menudemo.c
menudemo.res:menudemo.rcmenudemo.h
$(RC)$(RCVARS)menudemo.rc

MENUDEMO.C
/*
MENUDEMO.CProgramdemonstrativpentrumeniuri
*/
#include<windows.h>
#include"menudemo.h"
LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);
charszAppName[]="MenuDemo";
intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,
PSTRszCmdLine,intiCmdShow)
{
HWNDhwnd;
MSGmsg;
WNDCLASSEXwndclass;
wndclass.cbSize=sizeof(wndclass);
wndclass.style=CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc=WndProc;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hInstance=hInstance;
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName=szAppName;
wndclass.lpszClassName=szAppName;
wndclass.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
RegisterClassEx(&wndclass);
hwnd=CreateWindow(szAppName,"MenuDemonstration",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
returnmsg.wParam;
}
LRESULTCALLBACKWndProc(HWNDhwnd,UINTiMsg,WPARAMwParam,LPARAMlParam)
{
staticintiColorID[5]={WHITE_BRUSH,LTGRAY_BRUSH,GRAY_BRUSH,
DKGRAY_BRUSH,BLACK_BRUSH};

staticintiSelection=IDM_WHITE;
HMENUhMenu;
switch(iMsg)
{
caseWM_COMMAND:
hMenu=GetMenu(hwnd);
switch(LOWORD(wParam))
{
caseIDM_NEW:
caseIDM_OPEN:
caseIDM_SAVE:
caseIDM_SAVEAS:
MessageBeep(0);
return0;
caseIDM_EXIT:
SendMessage(hwnd,WM_CLOSE,0,0L);
return0;
caseIDM_UNDO:
caseIDM_CUT:
caseIDM_COPY:
caseIDM_PASTE:
caseIDM_DEL:
MessageBeep(0);
return0;
caseIDM_WHITE://Note:Logicaurm?toaresebazeaz?pepresupunereac?indicatoriicuprinintre
IDM_WHITEiIDM_BLACKsuntnumereconsecutivenordineaar?tat?aici
caseIDM_LTGRAY://
caseIDM_GRAY://
caseIDM_DKGRAY://
caseIDM_BLACK://
CheckMenuItem(hMenu,iSelection,MF_UNCHECKED);
iSelection=LOWORD(wParam);
CheckMenuItem(hMenu,iSelection,MF_CHECKED);
SetClassLong(hwnd,GCL_HBRBACKGROUND,
(LONG)GetStockObject

(iColorID[LOWORD(wParam)IDM_WHITE]));
InvalidateRect(hwnd,NULL,TRUE);
return0;
caseIDM_START:
if(SetTimer(hwnd,1,1000,NULL))
{
EnableMenuItem(hMenu,IDM_START,MF_GRAYED);
EnableMenuItem(hMenu,IDM_STOP,MF_ENABLED);
}
return0;
caseIDM_STOP:
KillTimer(hwnd,1);
EnableMenuItem(hMenu,IDM_START,MF_ENABLED);
EnableMenuItem(hMenu,IDM_STOP,MF_GRAYED);
return0;
caseIDM_HELP:
MessageBox(hwnd,"Helpnotyetimplemented!",
szAppName,MB_ICONEXCLAMATION|MB_OK);
return0;
caseIDM_ABOUT:
MessageBox(hwnd,"MenuDemonstrationProgram.",
szAppName,MB_ICONINFORMATION|MB_OK);
return0;
}
break;
caseWM_TIMER:
MessageBeep(0);
return0;
caseWM_DESTROY:
PostQuitMessage(0);
return0;
}
returnDefWindowProc(hwnd,iMsg,wParam,lParam);
}

MENUDEMO.RC
/*
MENUDEMO.RCfiierscriptderesurse
*/
#include"menudemo.h"
MenuDemoMENU
{

POPUP"&File"
{
MENUITEM"&New",IDM_NEW
MENUITEM"&Open...",IDM_OPEN
MENUITEM"&Save",IDM_SAVE
MENUITEM"Save&As...",IDM_SAVEAS
MENUITEMSEPARATOR
MENUITEM"E&xit",IDM_EXIT
}
POPUP"&Edit"
{
MENUITEM"&Undo",IDM_UNDO
MENUITEMSEPARATOR
MENUITEM"Cu&t",IDM_CUT
MENUITEM"&Copy",IDM_COPY
MENUITEM"&Paste",IDM_PASTE
MENUITEM"De&lete",IDM_DEL
}
POPUP"&Background"

321
{
MENUITEM"&White",IDM_WHITE,CHECKED
MENUITEM"&LtGray",IDM_LTGRAY
MENUITEM"&Gray",IDM_GRAY
MENUITEM"&DkGray",IDM_DKGRAY
MENUITEM"&Black",IDM_BLACK
}
POPUP"&Timer"
{
MENUITEM"&Start"IDM_START
MENUITEM"S&top"IDM_STOP,GRAYED
}
POPUP"&Help"
{
MENUITEM"&Help...",IDM_HELP
MENUITEM"&AboutMenuDemo...",IDM_ABOUT
}
}

MENUDEMO.H
/*
MENUDEMO.Hfiierantet
*/
#defineIDM_NEW1
#defineIDM_OPEN2
#defineIDM_SAVE3
#defineIDM_SAVEAS4
#defineIDM_EXIT5
#defineIDM_UNDO10
#defineIDM_CUT11
#defineIDM_COPY12
#defineIDM_PASTE13
#defineIDM_DEL14
#defineIDM_WHITE20
#defineIDM_LTGRAY21
#defineIDM_GRAY22
#defineIDM_DKGRAY23
#defineIDM_BLACK24
#defineIDM_START30
#defineIDM_STOP31
#defineIDM_HELP40
#defineIDM_ABOUT41

Fig. 10.1 Programul MENUDEMO

Identificatorii pentru fiecare meniu sunt definii n fiierul MENUDEMO.H. Acest fiier trebuie specificat (de
obicei printr-o instruciune Mnclude) att n fiierul script de resurse, ct i n codul surs C. Toi identificatorii ncep
cu literele IDM. Nu este nevoie ca numerele de identificare folosite pentru articolele dintr-un meniu s fie
consecutive. Totui, dac prelucrai aceti identificatori folosind instruciuni switch i case, compilatorul C poate
s optimizeze codul cu ajutorul unor tabele de salt (jump tables) dac folosii numere de identificare consecutive.
Pentru majoritatea articolelor din meniurile File i Edit, programul MENUDEMO nu face dect s emit un
semnal sonor atunci cnd primete mesajul WM_COM-MAND. Meniul derulant Background conine un set de
cinci pensule pe care putei s Ie folosii pentru colorarea fondului. n fiierul de resurse MENUDEMO.RC
articolul de meniu White (cu identificatorul IDM_WHITE) este marcat cu opiunea CHECKED, ceea ce are ca
efect afiarea unui marcaj de validare n dreptul acestui articol. n fiierul MENUDEMO.C, variabila iSeledion
are iniial valoarea IDM_WHITE.'
Cele cinci pensule din meniul Background se exclud reciproc. Atunci cnd primete un mesaj

WM_COMMAND n care cuvntul mai puin semnificativ al parametrului wParam conine identificatorul unuia
dintre cele cinci articole din meniul Background, programul trebuie s tearg mai nti marcajul de validare din
dreptul articolului selectat anterior i apoi s adauge altul n dreptul noii culori. Pentru aceasta, programul are
nevoie de o variabil handle a meniului:
hMenu - GetMenu (hwnd) ;

Funcia CheckMenuItem este folosit pentru tergerea marcajului de validare din dreptul articolului curent
selectat:
CheckMenuItem (hMenu, iSelection, MFJINCHECKED) ;

Variabila iSelection primete valoarea cuvntului mai puin semnificativ al parametrului wParam i noua
culoare de fond este marcat:
iSelection = LOWORD (wParam) ;
CheckMenuItem (hMenu, iSelection, MF_CHECKED) ;

n continuare culoarea de fond din clasa ferestrei este nlocuit cu noua culoare, iar zona client a ferestrei
este invalidat. Windows terge zona client folosind noua culoare de fond.
Meniul derulant Timer (Cronometru) conine dou opiuni - Start i Stop. Iniial, opiunea Stop este scris
cu gri (aa cum indic definiia meniului din fiierul script de resurse). Atunci cnd selectai opiunea Start,
programul MENUDEMO ncearc s porneasc un cronometru i, dac reuete, afieaz cu gri opiunea
Start i activeaz opiunea Stop:
EnableMenuItem (hMenu, IDMJTART, MFJRAYED) ; EnableMenuItem (hMenu, IDM_ST0P,
MF_ENABLED) ;

La primirea unui mesaj WM_COMMAND n care cuvntul mai puin semnificativ al parametrului wParam
conine identificatorul IDM_STOP, programul MENUDEMO oprete cronometrul, activeaz opiunea Start i
dezactiveaz opiunea Stop:
EnableMenuItem (hMenu, IDM START, MF_ENABLEO) ; EnableMenuItem (hMenu, IDMJTOP,
MF_GRAYED) ;

Remarcai faptul c este imposibil ca programul s primeasc un mesaj WM_COM-MAND n care cuvntul
mai puin semnificativ al parametrului wParam s conin identificatorul IDM_START dup pornirea
cronometrului. La fel, programul nu poate s primeasc un mesaj WM_COMMAND n care cuvntul mai
puin semnificativ al parametrului wParam s conin identificatorul IDM_STOP dup oprirea cronometrului.
Atunci cnd primete un mesaj WM_COMMAND n care cuvntul mai puin semnificativ al
parametrului wParam conine identificatorul IDM_ABOUT sau IDM_HELP, programul MENUDEMO
afieaz o caset de mesaje. (n capitolul urmtor vom nlocui aceast caset de mesaje cu o caset de
dialog.)
Atunci cnd primete un mesaj WM_COMMAND n care cuvntul mai puin semnificativ al
parametrului wParam conine identificatorul IDM_EXIT, programul MENUDEMO trimite un mesaj
WM_CLOSE. Acelai mesaj l trimite procedura DefWindowProc ctre procedura ferestrei atunci cnd
primete un mesaj WM_SYS-COMMAND n care cuvntul mai puin semnificativ al parametrului wParam
conine identificatorul SC_CLOSE. Vom discuta mai multe despre acest mesaj n programul POPPAD2,
prezentat ctre sfritul acestui capitol.
Capitolul 10 Meniuri i taste de accelerare

Modul de folosire a meniurilor


Formatul meniurilor Edit i File din programul MENUDEMO sunt foarte asemntoare cu aceleai meniuri
din alte programe Windows. Unul dintre obiectivele sistemului de operare Windows este asigurarea unei
interfee uor de recunoscut, astfel nct utilizatorul s nu fie nevoit de fiecare dat s nvee din nou
conceptele de baz ale fiecrui program. Cu siguran, este de mare ajutor dac meniurile File i Edit din toate
programele Windows arat la fel i folosesc aceleai combinaii de taste pentru selectare.
n afar de meniurile File i Edit, celelalte meniuri ale programelor Windows arat diferit. Atunci cnd
proiectai un meniu, ar trebui s studiai programele Windows existente i s ncercai s pstrai o
configuraie uniform. Desigur, dac vi se pare c programele respective sunt greite i cunoatei o cale mai
bun de a face aceleai lucruri, nimeni nu v oprete. De asemenea, reinei c pentru modificarea unui meniu
trebuie s modificai numai fiierul script de resurse, nu i codul surs al programului. De exemplu, putei s
schimbai ordinea articolelor din meniu fr prea multe probleme.
Dei meniul unui program poate avea instruciuni MENUITEM pe primul nivel, situaia nu este una prea
obinuit, deoarece aceste articole ar putea fi selectate din greeal. Dac facei acest lucru, adugai la sfritul
textului un semn de exclamare, care s indice faptul c articolul respectiv nu apeleaz un meniu derulant.

0 cale mai greoaie de definire a unui meniu


Definirea unui meniu n fiierul script de resurse al programului este, de obicei, cea mai simpl cale, dar nu i

singura. Putei s renunai la fiierul script de resurse i s creai ntregul meniu direct n codul surs, folosind
funciile CreateMenu i Append-Menu. Dup ce definii meniul, putei s transmitei funciei CreateWindow
variabila handle a acestuia sau s apelai funcia SetMenu ca s schimbai meniul ferestrei.
Iat cum putei s facei acest lucru. Funcia CreateMenu returneaz variabila handle a meniului:
hHenu CreateMenu () ;

Iniial meniul este gol. Prin apelarea funciei AppendMenu sunt inserate articolele de meniu. Trebuie s obinei
variabile handle diferite pentru meniul principal i pentru fiecare meniu derulant. Meniurile derulante sunt
construite separat, apoi variabilele handle ale acestora sunt inserate n meniul principal. Codul prezentat n
Figura 10-2 creeaz un meniu prin aceast metod; de fapt, acesta este acelai meniu cu cel folosit de programul
MENUDEMO.
hMenu=CreateMenu();
hMenuPopup=CreateMenu();
AppendMenu(hMenuPopup,MF_STRING,IDM_NEW,"&New");
AppendMenu(hMenuPopup,MF_STRING,IDM_OPEN,"&Open...");
AppendMenu(hMenuPopup,MF_STRING,IDM_SAVE,"&Save");
AppendMenu(hMenuPopup,MF_STRING,IDM_SAVEAS,"Save&As...");
AppendMenu(hMenuPopup,MF_SEPARATOR,0,NULL);
AppendMenu(hMenuPopup,MF_STRING,IDM_EXIT,"E&xit");
AppendMenu(hMenu,MF_POPUP,(UINT)hMenuPopup,"&File");
hMenuPopup=CreateMenu();
AppendMenu(hMenuPopup,MF_STRING,IDM_UNDO,"&Undo");
AppendMenu(hMenuPopup,MF_SEPARATOR,0,NULL);
AppendMenu(hMenuPopup,MF_STRING,IDM_CUT,"Cu&t");
AppendMenu(hMenuPopup,MF_STRING,IDM_COPY,"&Copy");
AppendMenu(hMenuPopup,MF_STRING,IDM_PASTE,"&Paste");
AppendMenu(hMenuPopup,MF_STRING,IDM_DEL,"De&lete");
AppendMenu(hMenu,MF_POPUP,(UINT)hMenuPopup,"&Edit");
hMenuPopup=CreateMenu();
AppendMenu(hMenuPopup,MF_STRING|MF_CHECKED,IDM_WHITE,"&White");
AppendMenu(hMenuPopup,MF_STRING,IDM_LTGRAY,"&LtGray");
AppendMenu(hMenuPopup,MF_STRING,IDM_GRAY,"&Gray");
AppendMenu(hMenuPopup,MF_STRING,IDM_DKGRAY,"&DkGray");
AppendMenu(hMenuPopup,MF_STRING,IDM_BLACK,"&Black");
AppendMenu(hMenu,MF_POPUP,(UINT)hMenuPopup,"&Background");
hMenuPopup=CreateMenu();
AppendMenu(hMenuPopup,MF_STRING,IDM_START,"&Start");
AppendMenu(hMenuPopup,MF_STRING|MF_GRAYED,IDM_STOP,"S&top");
AppendMenu(hMenu,MF_POPUP,(UINT)hMenuPopup,"&Timer");
hMenuPopup=CreateMenu();
AppendMenu(hMenuPopup,MF_STRING,IDM_HELP,"&Help...");
AppendMenu(hMenuPopup,MF_STRING,IDM_ABOUT,"&AboutMenuDemo...");
AppendMenu(hMenu,MF_POPUP,(UINT)hMenuPopup,"&Help");

Fig. 10.2 Codul C care creaz meniul utilizat de programul MENUDEMO, fr se foloseasc un fiier script de resurse.

Cred c suntei de acord c metoda care folosete ablonul de meniu din fiierul script de resurse este mai
simpl i mai clar. Ca urmare, nu v recomand s definii meniul j direct n codul surs, ci doar am artat c
exist i aceast cale. Desigur, putei s ( reducei substanial dimensiunea codului surs cu ajutorul unor
matrice, care s \ aonin irurile de caractere, identificatorii i indicatorii flag ai articolelor de meniu. Dar
dac facei acest lucru, putei s folosii la fel de bine cea de-a treia metod de definire a unui meniu,
prezentat n continuare.

A treia metod de definire a unui meniu


ceasta funcie este folosit n Windows pentru construirea unui meniu dup ce ablonul normal a fost
ncrcat din fiierul script de resurse. Dac suntei un programator curajos, putei s ncercai i aceast metod.

Meniuri derulante plutitoare"


Putei s folosii meniuri chiar dac nu avei o bar de meniu principal. Puteri s facei ca un meniu derulant
s apar n orice parte a ecranului. O metod des folosit este apelarea acestui tip de meniuri ca rspuns la
executarea unui clic cu butonul din dreapta al rnouse-ului. Articolele de meniu trebuie s fie selectate, ns, tot cu
ajutorul butonului din stnga al mouse-ului. Programul POPMENU, prezentat n Figura 10-3 arata cum poate fi
fcut acest lucru.

POPMENU.MAK

#
#Fiieruldeconstruc?iePOPMENU.MAK
#
popmenu.exe:popmenu.objpopmenu.res
$(LINKER)$(GUIFLAGS)OUT:popmenu.exepopmenu.obj\

popmenu.res$(GUILIBS)
popmenu.obj:popmenu.cpopmenu.h
$(CC)$(CFLAGS)popmenu.c
popmenu.res:popmenu.rcpopmenu.h
$(RC)$(RCVARS)popmenu.rc

POPMENU.C
/*
POPMENU.Cprogramdemonstrativpentrumeniurilederulante
(c)CharlesPetzold,1996
*/
#include<windows.h>
#include"popmenu.h"
LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);
charszAppName[]="PopMenu";
HINSTANCEhInst;
intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,
PSTRszCmdLine,intiCmdShow)
{
HWNDhwnd;
MSGmsg;
WNDCLASSEXwndclass;
wndclass.cbSize=sizeof(wndclass);
wndclass.style=CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc=WndProc;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hInstance=hInstance;
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName=NULL;
wndclass.lpszClassName=szAppName;

325
wndclass.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
RegisterClassEx(&wndclass);
hInst=hInstance;
hwnd=CreateWindow(szAppName,"PopupMenuDemonstration",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
returnmsg.wParam;
}
LRESULTCALLBACKWndProc(HWNDhwnd,UINTiMsg,WPARAMwParam,LPARAMlParam)
{
staticHMENUhMenu;
staticintiColorID[5]={WHITE_BRUSH,LTGRAY_BRUSH,GRAY_BRUSH,
DKGRAY_BRUSH,BLACK_BRUSH};
staticintiSelection=IDM_WHITE;
POINTpoint;
switch(iMsg)
{
caseWM_CREATE:
hMenu=LoadMenu(hInst,szAppName);
hMenu=GetSubMenu(hMenu,0);
return0;
caseWM_RBUTTONDOWN:
point.x=LOWORD(lParam);
point.y=HIWORD(lParam);
ClientToScreen(hwnd,&point);
TrackPopupMenu(hMenu,0,point.x,point.y,0,hwnd,NULL);
return0;
caseWM_COMMAND:
switch(LOWORD(wParam))
{
caseIDM_NEW:
caseIDM_OPEN:
caseIDM_SAVE:
caseIDM_SAVEAS:
caseIDM_UNDO:

caseIDM_CUT:
caseIDM_COPY:
caseIDM_PASTE:
caseIDM_DEL:
MessageBeep(0);
return0;
caseIDM_WHITE://Logicaurm?toaresebazeaz?pepresupunereac?identificatoriicuprinin
intervalulIDM_WHITEiIDM_BLACKsuntnumereconsecutivenordineaar?tat?aici
caseIDM_LTGRAY://
caseIDM_GRAY://
caseIDM_DKGRAY://
caseIDM_BLACK://
CheckMenuItem(hMenu,iSelection,MF_UNCHECKED);
iSelection=LOWORD(wParam);
CheckMenuItem(hMenu,iSelection,MF_CHECKED);
SetClassLong(hwnd,GCL_HBRBACKGROUND,
(LONG)GetStockObject
(iColorID[LOWORD(wParam)IDM_WHITE]));
InvalidateRect(hwnd,NULL,TRUE);
return0;
caseIDM_ABOUT:
MessageBox(hwnd,"PopupMenuDemonstrationProgram.",
szAppName,MB_ICONINFORMATION|MB_OK);
return0;
caseIDM_EXIT:
SendMessage(hwnd,WM_CLOSE,0,0);
return0;
caseIDM_HELP:
MessageBox(hwnd,"Helpnotyetimplemented!",
szAppName,MB_ICONEXCLAMATION|MB_OK);
return0;
}
break;
caseWM_DESTROY:
PostQuitMessage(0);
return0;
}
returnDefWindowProc(hwnd,iMsg,wParam,lParam);
}

POPMENU.RC
/*
POPMENU.RCfiierscriptderesurse
*/
#include"popmenu.h"
PopMenuMENU
{
POPUP""
{
POPUP"&File"
{
MENUITEM"&New",IDM_NEW
MENUITEM"&Open...",IDM_OPEN
MENUITEM"&Save",IDM_SAVE
MENUITEM"Save&As...",IDM_SAVEAS
MENUITEMSEPARATOR
MENUITEM"E&xit",IDM_EXIT
}
POPUP"&Edit"
{
MENUITEM"&Undo",IDM_UNDO
MENUITEMSEPARATOR
MENUITEM"Cu&t",IDM_CUT
MENUITEM"&Copy",IDM_COPY
MENUITEM"&Paste",IDM_PASTE

327
MENUITEM"De&lete",IDM_DEL
}
POPUP"&Background"
{
MENUITEM"&White",IDM_WHITE,CHECKED
MENUITEM"&LtGray",IDM_LTGRAY
MENUITEM"&Gray",IDM_GRAY
MENUITEM"&DkGray",IDM_DKGRAY
MENUITEM"&Black",IDM_BLACK
}
POPUP"&Help"
{

MENUITEM"&Help...",IDM_HELP
MENUITEM"&AboutPopMenu...",IDM_ABOUT
}
}
}

POPMENU.H
/*
POPMENU.Hfiierantet
*/
#defineIDM_NEW1
#defineIDM_OPEN2
#defineIDM_SAVE3
#defineIDM_SAVEAS4
#defineIDM_EXIT5
#defineIDM_UNDO10
#defineIDM_CUT11
#defineIDM_COPY12
#defineIDM_PASTE13
#defineIDM_DEL14
#defineIDM_WHITE20
#defineIDM_LTGRAY21
#defineIDM_GRAY22
#defineIDM_DKGRAY23
#defineIDM_BLACK24
#defineIDM_HELP30
#defineIDM_ABOUT31

Fig. 10.3 Programul POPMENU

Fiierul script de resurse POPMENU.RC definete un meniu foarte asemntor cu cel definit n fiierul
MENUDEMO.RC. Diferena const n faptul c meniul principal conine un singur articol - un meniu derulant
care conine opiunile File, Edit, Background i Help.
n timpul prelucrrii mesajului WM_CREATE n procedura WndProc, POPMENU obine o variabil handle a
acestui meniu derulant:
hMenu = LoadMenu (hlnst, szAppName) ; hMenu = GetSubMenu (hMenu, 0) ;

n timpul prelucrrii mesajului WM_RBUTTONDOWN, POPMENU obine poziia indicatorului mouse-ului,


convertete aceast poziie n coordonate de ecran i transmite coordonatele obinute funciei TraekPopupMenu:
point.x LOWORD (IParam) ; point.y = HIWORD (IParam) ; ClientToScreen (hwnd, &point) ;
TrackPopupMenu (hMenu, 0, point.x, point.y, 0, hwnd, NULL) ;

Sistemul de operare afieaz apoi meniul derulant cu articolele File, Edit, Background i Help. Selectarea
oricruia dintre aceste articole determin afiarea meniurilor derulante corespunztoare. Funciile din meniuri au
acelai rol ca i funciile din meniul normal.

Folosirea meniului sistem


Ferestrele printe create cu un stil care include identificatorul WS_SYSMENU au un meniu sistem n partea
stng a barei de titlu. Dac dorii, putei s modificai acest meniu. De exemplu, putei s adugai n meniul
sistem comenzi proprii. Dei acest lucru nu este recomandat, modificarea meniului sistem este o metod rapid,
dar mai puin elegant de adugare a unui meniu la un program, fr definirea acestuia n fiierul script de
resurse. Singura restricie este necesitatea ca numerele de identificare a comenzilor adugate n meniul sistem s
fie mai mici de OxFOOO. n caz contrar, aceste numere vor intra n conflict cu identificatorii pe care Windows
i folosete pentru comenzile normale din meniul sistem. Nu uitai c, dei mesajele WM_SYS-COMMAND
pentru noile articole de meniu sunt prelucrate n procedura ferestrei, trebuie s transmitei procedurii
DefWindowProc celelalte mesaje WM_SYSCOM-MAND. Dac nu facei acest lucru, toate opiunile normale
din meniul sistem vor fi efectiv dezactivate.
Programul POORMENU, prezentat n Figura 10-4, adaug n meniul sistem un separator i trei comenzi
suplimentare. Ultima dintre cele trei comenzi terge comenzile adugate.

POORMENU.MAK

#
#Fiieruldeconstruc?iePOORMENU.MAKmakefile
#
poormenu.exe:poormenu.obj
$(LINKER)$(GUIFLAGS)OUT:poormenu.exepoormenu.obj$(GUILIBS)
poormenu.obj:poormenu.c
$(CC)$(CFLAGS)poormenu.c

POORMENU.C
/*
POORMENU.CMeniupentrus?raci
(c)CharlesPetzold,1996

*/
#include<windows.h>
#defineIDM_ABOUT1
#defineIDM_HELP2
#defineIDM_REMOVE3
LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);
staticcharszAppName[]="PoorMenu";
intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,
PSTRszCmdLine,intiCmdShow)
{
HMENUhMenu;
HWNDhwnd;
MSGmsg;
WNDCLASSEXwndclass;
wndclass.cbSize=sizeof(wndclass);
wndclass.style=CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc=WndProc;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hInstance=hInstance;
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName=NULL;
wndclass.lpszClassName=szAppName;
wndclass.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
RegisterClassEx(&wndclass);
hwnd=CreateWindow(szAppName,"ThePoorPerson'sMenu",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
hMenu=GetSystemMenu(hwnd,FALSE);
AppendMenu(hMenu,MF_SEPARATOR,0,NULL);
AppendMenu(hMenu,MF_STRING,IDM_ABOUT,"About...");
AppendMenu(hMenu,MF_STRING,IDM_HELP,"Help...");
AppendMenu(hMenu,MF_STRING,IDM_REMOVE,"RemoveAdditions");
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
returnmsg.wParam;
}
LRESULTCALLBACKWndProc(HWNDhwnd,UINTiMsg,WPARAMwParam,LPARAMlParam)
{
switch(iMsg)
{
caseWM_SYSCOMMAND:
switch(LOWORD(wParam))
{
caseIDM_ABOUT:
MessageBox(hwnd,"APoorPerson'sMenuProgram.",
szAppName,MB_OK|MB_ICONINFORMATION);
return0;
caseIDM_HELP:
MessageBox(hwnd,"Helpnotyetimplemented!",
szAppName,MB_OK|MB_ICONEXCLAMATION);
return0;
caseIDM_REMOVE:
GetSystemMenu(hwnd,TRUE);
return0;
}
break;
caseWM_DESTROY:
PostQuitMessage(0);
return0;
}
returnDefWindowProc(hwnd,iMsg,wParam,lParam);
}

Fig. 10.4 Programul POORMENU

Cele trei numere de identificare ale meniurilor adugate sunt definite n partea de sus a fiierului
POORMENU.C:
fdefine IDM ABOUT 1 #define IDM~HELP 2 #define IDM^REMOVE 3

Dup crearea ferestrei programului, POORMENU obine o variabil handle a meniului sistem:
hMenu = GetSystemMenu (hwnd, FALSE) ;

La prima apelare a funciei GetSystemMenu trebuie s dai celui de-al doilea parametru valoarea FALSE ca s
pregtii modificarea meniului.
Meniul este modificat prin patru apeluri ale funciei AppendMenu:
AppendMenu (hMenu, MF SEPARATOR, 0,
NULL) ;
AppendMenu (hMenu, MF~STRING,
IDM ABOUT, "About...") ; AppendMenu (hMenu, MF~STRING,
AppendMenu (hMenu, MF~STRING,
IDM REMOVE, "Remove Additions") ; *

IDMJELP,
~

"Help...") ;
~

Primul apel al funciei AppendMenu adaug o bar de separare. Selectarea opiuni Remove Additions determin
eliminarea articolelor de meniu adugate, operaie efectuat prin apelarea funciei GetSystemMenu avnd al
doilea parametru cu valoarea TRUE:
GetSystemMenu (hwnd, TRUE) ;

Meniul sistem standard conine opiunile Restore, Move, Size, Minimize, Maximize, Close i Switch To.
Acestea genereaz mesaje WM_SYSCOMMAND n care parametrul wParam poate avea valorile SC_RESTORE,
SC_MOVE, SC_SIZE, SC.MINIMIZE, SC_MAXIMIZE, SC_CLOSE i SC_TASKLIST. Dei, n general, programele Windows nu fac acest lucru, putei s prelucrai aceste mesaje, n loc s le transmitei procedurii
DefWindowProc. De asemenea, putei s dezactivai sau s tergei unele dintre opiunile standard folosind
metodele descrise mai jos. Documentaia Windows include i alte opiuni standard pe care putei s le adugai
n meniul sistem. Aceste opiuni suplimentare folosesc identificatorii SC_NEXTWIN-DOW,
SQPREVWINDOW, SC_VSCROLL, SC_HSCROLL i SC_ARRANGE. Este posibil ca n unele aplicaii s dorii
s adugai i aceste comenzi la meniul sistem.

Modificarea meniului
Am vzut deja cum poate fi folosit funcia AppendMenu pentru definirea unui ntreg meniu direct ntr-un
program i pentru adugarea unor articole la meniul sistem, nainte de apariia versiunii Windows 3.0, pentru
aceste operaii trebuia s folosii funcia ChangeMenu. Funcia ChangeMenu, datorit flexibilitii ei, era una
dintre cele mai complexe funcii Windows. In Windows 95 funcia ChangeMenu poate fi nc folosit, dar
operaiile pe care le poate executa aceasta au fost mprite ntre alte cinci noi funcii:
AppendMenu - adaug un nou articol la sfritul unui meniu.
DeleteMenu - terge un articol existent dintr-un meniu i distruge articolul
respectiv.
InsertMenu - insereaz un nou articol ntr-un meniu.
ModifyMenu - modific un articol de meniu existent.
RemoveMenu - terge un articol existent dintr-un meniu.
Diferena dintre funciile RemoveMenu i DeleteMenu este important dac articolul respectiv face parte dintrun meniu derulant. DeleteMenu distruge meniul derulant, pe cnd RemoveMenu nu face acest lucru.

Alte comenzi pentru meniuri


Exist alte cteva funcii care pot fi folosite pentru meniuri.
Arunci cnd modificai un articol din meniul principal, modificarea fcut nu este vizibil pn cnd meniul
nu este redesenat. Putei s forai redesenarea meniului
astfel:
DrawMenuBar (hwnd) ;

Remarcai c parametrul fuwsei DrawMenuBar este variabila handie a ferestrei, nu a meniului.


Putei s obinei variabila handie a unui meniu derulant astfel:
hMenuPopup - GetSubMenu (hMenu, iPosition) ;

unde iPosition este indexul meniului derulant (ncepnd de la 0) n cadrul meniului principal indicat de
parametrul hMenu. Putei apoi s folosii variabila handle obinut pentru apelarea altor funcii (cum ar fi
AppendMenu).
Putei s obinei numrul curent de articole dintr-un meniu derulant astfel:
iCount = GetMenuItemCount (hMenu) ; Putei s obinei identificatorul unui articol dintr-un meniu derulant astfel:
id - GetMenuItemID (hMenuPopup, iPosition) ;

unde iPosition este poziia articolului n meniul derulant (ncepnd de la 0).


n programul MENUDEMO ai vzut cum putei s marcai sau s demarcai un articol dintr-un meniu:
CheckMenuItem (hMenu, id, iCheck) ;

n programul MENUDEMO, hMenu era variabila handle a meniului principal, id era identificatorul meniului, iar
iCheck avea una dintre valorile MF_CHECKED sau MF_UNCHECKED. Dac hMenu este variabila handle a
unui meniu derulant, atunci id poate fi un index poziional n loc de un identificator de meniu. Dac folosirea

indexului este mai convenabil, includei identificatorul MF_B\TOSITION n cel de-al treilea parametru. De
exemplu:
ChedkMenuItem (hMenu, iPosition, MF_CHECKED j MF_BYP0SITI0N) ;

Funcia EnableMenuItem lucreaz ntr-un mod asemntor cu funcia CheckMenuItem, exceptnd faptul c cel
de-al treilea parametru poate avea una dintre valorile MF_ENABLED, MF_DISABLED sau MF_GRAYED.
Dac folosii funcia EnableMenuItem pentru un articol al meniului principal care apeleaz un meniu derulant,
n cel de-al treilea parametru trebuie s includei identificatorul MF_BYPOSITION, deoarece articolul respectiv
nu are un identificator de meniu. Vom vedea un exemplu de folosire a funciei EnableMenuItem n programul
POPPAD prezentat mai trziu n acest capitol. Funcia HUiieMenuItem este asemntoare cu funciile
EnableMenuItem i CheckMenuItem, dar folosete identificatorii MF_HILITE i MFJJNHILITE. Acest marcaj
(highlighting) este modul de afiare n video invers pe care sistemul de operare Windows l folosete atunci cnd
v deplasai de la un articol de meniu la altul. n mod normal nu avei nevoie s folosii funcia HUiieMenuItem.
Ce altceva ai mai avea nevoie s facei cu un meniu? Ai uitat ce ir de caractere ai folosit ntr-un meniu?
Putei s v mprosptai memoria cu urmtorul apel:
iByteCount = GetMenuString (hMenu, id, pString, iMaxCount, iFlag) ;

iFlag poate fi MF_BYCOMMAND (caz n care id este identificatorul meniului) sau MFJBYPOSITION (iar id
este un index poziional). Funcia copiaz cel mult iMaxCount octei n pString i returneaz numrul de octei
copiai.
Sau poate c dorii s aflai care sunt indicatoarele flag stabilite pentru un articol de meniu:
iFlags = GetMenuState (hMenu, id, iFlag) ;

Din nou, iFlag poate fi MF_BYCOMMAND sau MF_BYPOSITION. Valoarea iFlags returnat este o
combinaie a tuturor indicatoarelor flag curente. Putei s stabilii care sunt acestea comparnd aceast
valoare cu identificatorii MF_DISABLED,
MF.GRAYED, MF_CHECKED, MF_MENUBREAK, MF_MENUBARBREAK i MF_SEPARATOR.
Probabil c v-ai plictisit de meniuri. n acest caz, v vei bucura s aflai c putei scpa de un meniu de care nu
mai avei nevoie cu ajutorul funciei DestroyMenu:
DestroyMenu (hMenu) ; Apelul de mai sus invalideaz variabila handle a meniului.

0 abordare mai neortodox" a meniurilor


Haidei s ne abatem puin de la crarea cunoscut. Ce spunei dac, n loc s folosii n program meniuri
derulante, creai mai multe meniuri de nivel nalt i trecei de la un meniu la altul apelnd funcia SetMenu?
Programul NOPOPUPS, prezentat n Figura 10-5, v arat cum putei s facei acest lucru. Programul conine
articolele File i Edit asemntoare cu cele din programul MENUDEMO, dar le afieaz ca meniuri de nivel
nalt alternative.
NOPOPUPS.MAK

#
#Fiieruldeconstruc?ieNOPOPUPS.MAK
#
nopopups.exe:nopopups.objnopopups.res
$(LINKER)$(GUIFLAGS)OUT:nopopups.exenopopups.obj\
nopopups.res$(GUILIBS)
nopopups.obj:nopopups.cnopopups.h
$(CC)$(CFLAGS)nopopups.c
nopopups.res:nopopups.rcnopopups.h
$(RC)$(RCVARS)nopopups.rc

332
NOPOPUPS.C
/*
NOPOPUPS.CProgramdemonstrativepentruutilizareameniurilorimbricate,nederulate
(c)CharlesPetzold,1996
*/
#include<windows.h>
#include"nopopups.h"
LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);
intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,
PSTRszCmdLine,intiCmdShow)
{
staticcharszAppName[]="NoPopUps";
HWNDhwnd;
MSGmsg;
WNDCLASSEXwndclass;
wndclass.cbSize=sizeof(wndclass);
wndclass.style=CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc=WndProc;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hInstance=hInstance;
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);

wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName=NULL;
wndclass.lpszClassName=szAppName;
wndclass.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
RegisterClassEx(&wndclass);
hwnd=CreateWindow(szAppName,"NoPopupNestedMenuDemonstration",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
returnmsg.wParam;
}
LRESULTCALLBACKWndProc(HWNDhwnd,UINTiMsg,WPARAMwParam,LPARAMlParam)
{
staticHMENUhMenuMain,hMenuEdit,hMenuFile;
HINSTANCEhInstance;
switch(iMsg)
{
caseWM_CREATE:
hInstance=(HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE);
hMenuMain=LoadMenu(hInstance,"MenuMain");
hMenuFile=LoadMenu(hInstance,"MenuFile");
hMenuEdit=LoadMenu(hInstance,"MenuEdit");
SetMenu(hwnd,hMenuMain);
return0;
caseWM_COMMAND:
switch(LOWORD(wParam))
{
caseIDM_MAIN:
SetMenu(hwnd,hMenuMain);
return0;
caseIDM_FILE:
SetMenu(hwnd,hMenuFile);
return0;
caseIDM_EDIT:
SetMenu(hwnd,hMenuEdit);
return0;
caseIDM_NEW:
caseIDM_OPEN:
caseIDM_SAVE:
caseIDM_SAVEAS:
caseIDM_UNDO:
caseIDM_CUT:
caseIDM_COPY:
caseIDM_PASTE:
caseIDM_DEL:
MessageBeep(0);
return0;
}
break;
caseWM_DESTROY:
SetMenu(hwnd,hMenuMain);
DestroyMenu(hMenuFile);
DestroyMenu(hMenuEdit);
PostQuitMessage(0);
return0;
}
returnDefWindowProc(hwnd,iMsg,wParam,lParam);
}

NOPOPUPS.RC
/*
NOPOPUPS.RCfiierul script de resurse
*/
#include"nopopups.h"
MenuMainMENU
{
MENUITEM"MAIN:",0,INACTIVE
MENUITEM"&File...",IDM_FILE
MENUITEM"&Edit...",IDM_EDIT
}

MenuFileMENU
{
MENUITEM"FILE:",0,INACTIVE
MENUITEM"&New",IDM_NEW
MENUITEM"&Open...",IDM_OPEN
MENUITEM"&Save",IDM_SAVE
MENUITEM"Save&As...",IDM_SAVEAS
MENUITEM"(&Main)",IDM_MAIN
}
MenuEditMENU
{
MENUITEM"EDIT:",0,INACTIVE
MENUITEM"&Undo",IDM_UNDO
MENUITEM"Cu&t",IDM_CUT
MENUITEM"&Copy",IDM_COPY
MENUITEM"&Paste",IDM_PASTE
MENUITEM"De&lete",IDM_DEL
MENUITEM"(&Main)",IDM_MAIN
}

NOPOPUPS.H
/*
NOPOPUPS.Hfiierantet
*/
#defineIDM_NEW1
#defineIDM_OPEN2
#defineIDM_SAVE3
#defineIDM_SAVEAS4
#defineIDM_UNDO5
#defineIDM_CUT6
#defineIDM_COPY7
#defineIDM_PASTE8
#defineIDM_DEL9
#defineIDM_MAIN10
#defineIDM_EDIT11
#defineIDM_FILE12

Fig. 10.5 Programul NOPOPUPS


Fiierul script de resurse conine trei meniuri. n timpul prelucrrii mesajului

WM_CREATE, Windows ncarc n memorie resursele celor trei meniuri:


hMenuMain=LoadMenu(hInstance,"MenuMain");
hMenuFile=LoadMenu(hInstance,"MenuFile");
hMenuEdit=LoadMenu(hInstance,"MenuEdit");

Iniial, programul afieaz meniul principal:


SetHenu (hwnd, hMenuMain) ;

Meniul principal conine trei opiuni, identificate prin irurile de caractere MAIN:", File..." i Edit...".
Opiunea MAIN:" este ns dezactivat, aa c nu trimite mesaje WM_COMMAND ctre procedura ferestrei.
Meniurile File i Edit sunt identificate prin irurile de caractere FILE:" i EDIT:". Ultima opiune din
meniurile File i Edit este (Main)"; aceast opiune permite ntoarcerea la meniul principal. Trecerea de la un
meniu la altul este simpl:
case WM COMMAND :
switch (LOWORD (wParam)) { case IDH_MAIN :
SetMenu (hwnd, hMenuMain) ; return 0 ;
case IOM FILE :
SetMenu (hwnd, hMenuFile) ; return 0 ;
case IDM EDIT :
SetMenu (hwnd, hMenuEdit) ; return 0 ;

[alte linii ale programului] } break ;

n timpul prelucrrii mesajului WM_DESTROY, programul NOPOPUPS stabilete ca meniu al programului


meniul principal (Main) i distruge meniurile File i Edit apelnd funcia DestroyMenu. Meniul principal este
distrus automat odat cu fereastra.

FOLOSIREA IMAGINILOR BITMAP N MENIURI


irurile de caractere nu sunt singura cale de afiare a unui articol de meniu. Pentru acelai lucru putei s folosii
i imagini bitmap, cu condiia s v convin ideea de a folosi imagini de dosare, fiiere i couri de gunoi" n
meniuri. Totui, gndii-v ct de utile ar putea fi imaginile bitmap din meniuri pentru un program de desenare.
Gndii-v la ideea folosirii mai multor fonturi i dimensiuni de fonturi, dimensiuni de linii, modele de hauri i
culori, n meniurile programului.
Programul pe care urmeaz s l studiem se numete GRAFMENU (meniu grafic"). Meniul principal al
programului este prezentat n Figura 10-6. Literele mrite sunt stocate n fiiere ca imagini bitmap monocrome,
cu dimensiunea 40x16 pixeli. Imaginile au fost create n editorul de imagini Developer Studio i au fost salvate

ca fiiere .BMP; n locul acestora ar putea fi folosite orice imagini. Articolul Font din meniu apeleaz un meniu
derulant care conine trei opiuni: Courier New, Arial si Times New Roman. Acestea sunt fonturile TrueType
standard n Windows i numele fiecruia este afiat cu fontul respectiv (Figura 10-7). Aceste imagini au fost
create n program printr-o tehnic ce implic folosirea unui context de dispozitiv de memorie.
PARTEA A III-A FOLOSIREA RESURSELOR

||r|Bitn.pMl... !

Figura 10-6. Meniul principal al programului GRAFMENU.

Figura 10-7. Meniul derulant FONT al programului GRAFMENU.

n sfrit, dac derulai meniul sistem, vei vedea c avei acces la unele informaii de asisten soft (help),
opiunea Help fiind o imagine a disperrii unui utilizator nou (Figura 10-8). Aceast imagine bitmap
monocrom de 64x64 pixeli a fost creat n editorul de imagini Developer Studio.

Figura 10-8. Meniul sistem al programului GRAFMENU.

Programul GRAFMENU, inclusiv cele patru imagini bitmap create n editorul de


imagini, sunt prezentate n Figura 10-9.

GRAFMENU.MAK

#
#Fiierdeconstruc?ieGRAFMENU.MAK
#
grafmenu.exe:grafmenu.objgrafmenu.res
$(LINKER)$(GUIFLAGS)OUT:grafmenu.exe\
grafmenu.objgrafmenu.res$(GUILIBS)
grafmenu.obj:grafmenu.cgrafmenu.h
$(CC)$(CFLAGS)grafmenu.c
grafmenu.res:grafmenu.rcgrafmenu.h\
editlabl.bmpfilelabl.bmpfontlabl.bmpbighelp.bmp
$(RC)$(RCVARS)grafmenu.rc

GRAFMENU.C
/*
GRAFMENU.CProgramilustrativpentruarticoledemeniu
(c)CharlesPetzold,1996
*/
#include<windows.h>
#include<string.h>
#include"grafmenu.h"
LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);
HBITMAPStretchBitmap(HBITMAP);
HBITMAPGetBitmapFont(int);
charszAppName[]="GrafMenu";
intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,
PSTRszCmdLine,intiCmdShow)
{
HBITMAPhBitmapHelp,hBitmapFile,hBitmapEdit,
hBitmapFont,hBitmapPopFont[3];
HMENUhMenu,hMenuPopup;
HWNDhwnd;
inti;
MSGmsg;
WNDCLASSEXwndclass;
wndclass.cbSize=sizeof(wndclass);
wndclass.style=CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc=WndProc;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hInstance=hInstance;
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName=NULL;
wndclass.lpszClassName=szAppName;
wndclass.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
RegisterClassEx(&wndclass);
hMenu=CreateMenu();

hMenuPopup=LoadMenu(hInstance,"MenuFile");
hBitmapFile=StretchBitmap(LoadBitmap(hInstance,"BitmapFile"));
AppendMenu(hMenu,MF_BITMAP|MF_POPUP,(int)hMenuPopup,
(PSTR)(LONG)hBitmapFile);
hMenuPopup=LoadMenu(hInstance,"MenuEdit");
hBitmapEdit=StretchBitmap(LoadBitmap(hInstance,"BitmapEdit"));
AppendMenu(hMenu,MF_BITMAP|MF_POPUP,(int)hMenuPopup,
(PSTR)(LONG)hBitmapEdit);
hMenuPopup=CreateMenu();
for(i=0;i<3;i++)
{
hBitmapPopFont[i]=GetBitmapFont(i);
AppendMenu(hMenuPopup,MF_BITMAP,IDM_COUR+i,
(PSTR)(LONG)hBitmapPopFont[i]);
}
hBitmapFont=StretchBitmap(LoadBitmap(hInstance,"BitmapFont"));
AppendMenu(hMenu,MF_BITMAP|MF_POPUP,(int)hMenuPopup,
(PSTR)(LONG)hBitmapFont);
hwnd=CreateWindow(szAppName,"BitmapMenuDemonstration",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
NULL,hMenu,hInstance,NULL);
hMenu=GetSystemMenu(hwnd,FALSE);
hBitmapHelp=StretchBitmap(LoadBitmap(hInstance,"BitmapHelp"));
AppendMenu(hMenu,MF_SEPARATOR,NULL,NULL);
AppendMenu(hMenu,MF_BITMAP,IDM_HELP,(PSTR)(LONG)hBitmapHelp);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DeleteObject(hBitmapHelp);
DeleteObject(hBitmapEdit);
DeleteObject(hBitmapFile);
DeleteObject(hBitmapFont);
for(i=0;i<3;i++)
DeleteObject(hBitmapPopFont[i]);
returnmsg.wParam;
}
HBITMAPStretchBitmap(HBITMAPhBitmap1)
{
BITMAPbm1,bm2;
HBITMAPhBitmap2;
HDChdc,hdcMem1,hdcMem2;
TEXTMETRICtm;
hdc=CreateIC("DISPLAY",NULL,NULL,NULL);
GetTextMetrics(hdc,&tm);
hdcMem1=CreateCompatibleDC(hdc);
hdcMem2=CreateCompatibleDC(hdc);
DeleteDC(hdc);
GetObject(hBitmap1,sizeof(BITMAP),(PSTR)&bm1);
bm2=bm1;
bm2.bmWidth=(tm.tmAveCharWidth*bm2.bmWidth)/4;
bm2.bmHeight=(tm.tmHeight*bm2.bmHeight)/8;
bm2.bmWidthBytes=((bm2.bmWidth+15)/16)*2;
hBitmap2=CreateBitmapIndirect(&bm2);
SelectObject(hdcMem1,hBitmap1);
SelectObject(hdcMem2,hBitmap2);
StretchBlt(hdcMem2,0,0,bm2.bmWidth,bm2.bmHeight,
hdcMem1,0,0,bm1.bmWidth,bm1.bmHeight,SRCCOPY);
DeleteDC(hdcMem1);
DeleteDC(hdcMem2);
DeleteObject(hBitmap1);
returnhBitmap2;
}
HBITMAPGetBitmapFont(inti)
{
staticchar*szFaceName[3]={"CourierNew","Arial",
"TimesNewRoman"};
staticLOGFONTlf;
HBITMAPhBitmap;
HDChdc,hdcMem;
HFONThFont;
SIZEsize;
TEXTMETRICtm;

hdc=CreateIC("DISPLAY",NULL,NULL,NULL);
GetTextMetrics(hdc,&tm);
lf.lfHeight=2*tm.tmHeight;
strcpy((char*)lf.lfFaceName,szFaceName[i]);
hdcMem=CreateCompatibleDC(hdc);
hFont=(HFONT)SelectObject(hdcMem,CreateFontIndirect(&lf));
GetTextExtentPoint(hdcMem,szFaceName[i],strlen(szFaceName[i]),&size);
hBitmap=CreateBitmap(size.cx,size.cy,1,1,NULL);
SelectObject(hdcMem,hBitmap);
TextOut(hdcMem,0,0,szFaceName[i],strlen(szFaceName[i]));
DeleteObject(SelectObject(hdcMem,hFont));
DeleteDC(hdcMem);
DeleteDC(hdc);
returnhBitmap;
}
LRESULTCALLBACKWndProc(HWNDhwnd,UINTiMsg,WPARAMwParam,LPARAMlParam)
{
HMENUhMenu;
staticintiCurrentFont=IDM_COUR;
switch(iMsg)
{
caseWM_CREATE:
CheckMenuItem(GetMenu(hwnd),iCurrentFont,MF_CHECKED);
return0;
caseWM_SYSCOMMAND:
switch(LOWORD(wParam))
{
caseIDM_HELP:
MessageBox(hwnd,"Helpnotyetimplemented!",
szAppName,MB_OK|MB_ICONEXCLAMATION);
return0;
}
break;
caseWM_COMMAND:
switch(LOWORD(wParam))
{
caseIDM_NEW:
caseIDM_OPEN:
caseIDM_SAVE:
caseIDM_SAVEAS:
caseIDM_UNDO:
caseIDM_CUT:
caseIDM_COPY:
caseIDM_PASTE:
caseIDM_DEL:
MessageBeep(0);
return0;
caseIDM_COUR:
caseIDM_ARIAL:
caseIDM_TIMES:
hMenu=GetMenu(hwnd);
CheckMenuItem(hMenu,iCurrentFont,MF_UNCHECKED);
iCurrentFont=LOWORD(wParam);
CheckMenuItem(hMenu,iCurrentFont,MF_CHECKED);
return0;
}
break;
caseWM_DESTROY:
PostQuitMessage(0);
return0;
}
returnDefWindowProc(hwnd,iMsg,wParam,lParam);
}

GRAFMENU.RC
/*
GRAFMENU.RCfiierscriptderesurse
*/
#include"grafmenu.h"
BitmapEditBITMAPeditlabl.bmp
BitmapFileBITMAPfilelabl.bmp
BitmapFontBITMAPfontlabl.bmp
BitmapHelpBITMAPbighelp.bmp
MenuFileMENU
{
MENUITEM"&New",IDM_NEW
MENUITEM"&Open...",IDM_OPEN
MENUITEM"&Save",IDM_SAVE

MENUITEM"Save&As...",IDM_SAVEAS
}
MenuEditMENU
{
MENUITEM"&Undo",IDM_UNDO
MENUITEMSEPARATOR
MENUITEM"Cu&t",IDM_CUT
MENUITEM"&Copy",IDM_COPY
MENUITEM"&Paste",IDM_PASTE
MENUITEM"De&lete",IDM_DEL
}

GRAFMENU.H
/*
GRAFMENU.Hfiierantet
*/
#defineIDM_NEW1
#defineIDM_OPEN2
#defineIDM_SAVE3
#defineIDM_SAVEAS4
#defineIDM_UNDO5
#defineIDM_CUT6
#defineIDM_COPY7
#defineIDM_PASTE8
#defineIDM_DEL9
#defineIDM_COUR10
#defineIDM_ARIAL11
#defineIDM_TIMES12
#defineIDM_HELP13

Fig. 10.9 Programul GRAFMENU

EDITLABL.BMP

FILELABL.BMP

FONTLABL.BMP

BIGHELP.BMP

Dou metode de creare a imaginilor bitmap pentru meniuri


Pentru inserarea unei imagini bitmap ntr-un meniu folosii funciile AppendMenu si InsertMenu.
Dar de unde provine imaginea bitmap? Exist dou posibile surse. In primul rnd, putei s
creai o imagine bitmap folosind editorul de imagini Developer Studio i incluznd apoi fiierul
bitmap n fiierul script de resurse. n program, putei s folosii funcia LoadBitmap ca s
ncrcai resursa imaginii n memorie i apoi s folosii funciile AppendMenu i InsertMenu ca s
o ataai la meniu. Totui, acest mod de abordare prezint o problem. Imaginea bitmap creat
poate s nu fie potrivit pentru toate tipurile de rezoluie video i rate de afiare - va fi nevoie
s restrngei imaginea bitmap creat, n funcie de acestea. O a doua metod este s creai
imaginea bitmap direct n program i apoi s o ataai la meniu.
Ambele metode par mai dificile dect sunt n realitate. De fapt, nu trebuie s lucrai direct cu biii.
Sistemul de operare Windows pune la dispoziie o serie de funcii care ne permit s manipulm cu
uurin imaginile bitmap folosind contextul de dispozitiv n memorie.

Contextul de dispozitiv n memorie


Atunci cnd folosii apelurile GDI (cum ar fi TextOut) ca s scriei n zona client a ferestrei, scriei
de fapt ntr-o zon de memorie (memoria video), care este organizat foarte asemntor cu o
imagine bitmap uria. Limea i nlimea acestei imagini j sunt egale cu rezoluia ecranului.
Modul n care mai muli bii definesc culoarea! fiecrui pixel este determinat de placa video. De
fapt, Windows ar putea s considere c un bloc din memoria obinuit este memoria video, i ar
putea s scrie n acest bloc j de memorie la fel cum scrie pe ecran. n acest caz am putea folosi acest
bloc de memorie ca pe o imagine bitmap.
Exact acest lucru nseamn un context de dispozitiv de memorie. Acesta ne ajutai s
desenm i s manipulm imagini bitmap ntr-un program Windows. Iat etapele care trebuie
parcurse:
1. Creai un context de dispozitiv n memorie folosind funcia CreateCompatibleDC,
Iniial, suprafaa de afiare a contextului de dispozitiv conine un singur pi
monocrom. Putei s v gndii la acest context de dispozitiv ca avnd un pi
lime, un pixel nlime i dou culori (alb i negru).
2. Creai o imagine neiniializat folosind funciile CreateBitmap, CreateBitmapIndirec
sau CreateCompatbleBitmap. La crearea imaginii bitmap specificai nlimea, l
imea i organizarea culorilor, chiar dac pixelii imaginii nu reprezint nc nimic
Salvai variabila handle a imaginii bitmap.
3. Selectai imaginea bitmap n contextul de dispozitiv n memorie cu ajutorul func
iei SelectObject. Acum contextul de dispozitiv are o suprafa de afiare de dimeiB
siunea imaginii bitmap i cu acelai numr de culori.
4. Folosii funciile GDI ca s desenai n contextul de dispozitiv n memorie ca i cui
ai desena ntr-un context de dispozitiv obinuit. Tot ceea ce desenai pe suprafai
de afiare a contextului de dispozitiv n memorie se deseneaz de fapt n imagine
bitmap selectat n contextul de dispozitiv.

Dou metode de creare a imaginilor bitmap pentru meniuri


Pentru inserarea unei imagini bitmap ntr-un meniu folosii funciile AppendMenu si InsertMenu.
Dar de unde provine imaginea bitmap? Exist dou posibile surse. In primul rnd, putei s
creai o imagine bitmap folosind editorul de imagini Developer Studio i incluznd apoi fiierul

bitmap n fiierul script de resurse. n program, putei s folosii funcia LoadBitmap ca s


ncrcai resursa imaginii n memorie i apoi s folosii funciile AppendMenu i InsertMenu ca s
o ataai la meniu. Totui, acest mod de abordare prezint o problem. Imaginea bitmap creat
poate s nu fie potrivit pentru toate tipurile de rezoluie video i rate de afiare - va fi nevoie
s restrngei imaginea bitmap creat, n funcie de acestea. O a doua metod este s creai
imaginea bitmap direct n program i apoi s o ataai la meniu.
Ambele metode par mai dificile dect sunt n realitate. De fapt, nu trebuie s lucrai direct cu biii.
Sistemul de operare Windows pune la dispoziie o serie de funcii care ne permit s manipulm cu
uurin imaginile bitmap folosind contextul de dispozitiv n memorie.

Contextul de dispozitiv n memorie


Atunci cnd folosii apelurile GDI (cum ar fi TextOut) ca s scriei n zona client a ferestrei, scriei
de fapt ntr-o zon de memorie (memoria video), care este organizat foarte asemntor cu o
imagine bitmap uria. Limea i nlimea acestei imagini j sunt egale cu rezoluia ecranului.
Modul n care mai muli bii definesc culoarea! fiecrui pixel este determinat de placa video. De
fapt, Windows ar putea s considere c un bloc din memoria obinuit este memoria video, i ar
putea s scrie n acest bloc j de memorie la fel cum scrie pe ecran. n acest caz am putea folosi acest
bloc de memorie ca pe o imagine bitmap.
Exact acest lucru nseamn un context de dispozitiv de memorie. Acesta ne ajutai s
desenm i s manipulm imagini bitmap ntr-un program Windows. Iat etapele care trebuie
parcurse:
1. Creai un context de dispozitiv n memorie folosind funcia CreateCompatibleDC,
Iniial, suprafaa de afiare a contextului de dispozitiv conine un singur pi
monocrom. Putei s v gndii la acest context de dispozitiv ca avnd un pi
lime, un pixel nlime i dou culori (alb i negru).
2. Creai o imagine neiniializat folosind funciile CreateBitmap, CreateBitmapIndirec
sau CreateCompatbleBitmap. La crearea imaginii bitmap specificai nlimea, l
imea i organizarea culorilor, chiar dac pixelii imaginii nu reprezint nc nimic
Salvai variabila handle a imaginii bitmap.
3. Selectai imaginea bitmap n contextul de dispozitiv n memorie cu ajutorul func
iei SelectObject. Acum contextul de dispozitiv are o suprafa de afiare de dimeiB
siunea imaginii bitmap i cu acelai numr de culori.
4. Folosii funciile GDI ca s desenai n contextul de dispozitiv n memorie ca i cui
ai desena ntr-un context de dispozitiv obinuit. Tot ceea ce desenai pe suprafai
de afiare a contextului de dispozitiv n memorie se deseneaz de fapt n imagine
bitmap selectat n contextul de dispozitiv.
5. tergei contextul de dispozitiv n memorie. Ai rmas cu o variabil handle a unei imagini bitmap care
conine o reprezentare n pixeli a ceea ce ai desenat n contextul de dispozitiv n memorie.

Crearea unei imagini bitmap cu text


Funcia GetBitmapFont din programul GRAFMENU primete parametrii 0,1 sau 2 i returneaz o variabil
handle a imaginii bitmap. Imaginea bitmap conine irurile de caractere Courier New", Arial" sau
Times New Roman" scrise cu fontul corespunztor i aproximativ de dou ori mai mari dect fontul
sistem normal. S vedem cum face funcia GetBitmapFont acest lucru. (Codul care urmeaz nu este identic
cu cel din fiierul GRAFMENU.C. Pentru mai mult claritate am nlocuit referirile la variabila
szFaceName cu valorile corespunztoare fontului Arial.)
Mai nti trebuie s determinm dimensiunea fontului sistem folosind structura TEXTMETRIC:
hdc - CreatelC ("DISPLAY", NULL, NULL, NULL) ; GetTextMet ri cs (hdc, & tm ) ;

Unele cmpuri din structura TEXTMETRIC trebuie s fie modificate pentru a descrie un font Arial mai mare
ntr-o structur logic a fontului:
lf.lfHeight = 2 * tm.tmHeight ;
strepy ((char *) lf.lfFaceName, "Arial") ;

Urmtorul pas este s obinem un context de dispozitiv pentru ecran i s crem un context de
dispozitiv n memorie compatibil cu ecranul:
hdcMem CreateCompatibleDC (hdc) ;

Variabila handle a contextului de dispozitiv n memorie este hdcMem. n continuare, crem un font pe baza
structurii modificate //i selectm fontul respectiv n contextul de dispozitiv n memorie:
hFont = (HFONT) SelectObject (hdcMem, CreateFontlndirect (&lf)) ;

Din acest moment, dac scriem un text n contextul de dispozitiv n memorie, Windows va folosi fontul
TrueType Arial, selectat n contextul de dispozitiv.
Dar contextul de dispozitiv nu are dect o suprafa de afiare monocrom de un pixel. Trebuie s crem
o imagine bitmap destul de mare pentru afiarea textului dorit. Putei s obinei dimensiunile textului cu
ajutorul funciei GetTextExtentPoint i s creai o imagine bitmap pe baza acestor dimensiuni folosind
funcia CreateBitmap:
GetTextExtentPoint (hdcMem, "Arial", 5, &size) ; hBitmap = CreateBitmap (size.cx,
size.cy, 1, 1, NULL) ; SelectObject (hdcMem, hBitmap) ;

Contextul de dispozitiv are acum o suprafa de afiare de dimensiunile textului. Tot ce mai trebuie s
facem este s scriem textul. Ai mai vzut aceast funcie:
Text O u t ( h d c Me m , 0 , 0 , " A r i a l " , 5 ) ;

Mai avem de fcut doar operaiile de curenie. Pentru aceasta, selectm din nou fontul sistem (cu
variabila handle hFont) n contextul de dispozitiv folosind funcia
SelectObject i tergem variabila handle a fontului returnat anterior de funcia Se-lectObject, respectiv
variabila handle a fontului Arial:
DeleteObject {SelectObject (hdcMem, hFont)) ;

Acum putem s tergem i cele dou contexte de dispozitiv:


DeleteOC (hdcMem) ; DeleteDC (hdc) ;

Am rmas cu o imagine bitmap care conine textul Arial" scris cu fontul Arial.

Scalarea imaginilor bitmap


Contextele de dispozitiv n memorie ne vin n ajutor i atunci cnd trebuie s scalm fonturile pentru
diferite rezoluii i rate de afiare (aspect ratio). Cele patru imagini bitmap folosite de programul
GRAFMENU au fost create astfel nct s aib dimensiunile corecte pentru un ecran pentru care fontul
sistem are nlimea de 8 pixeli i limea de patru pixeli. Pentru alte dimensiuni ale fontului sistem,
imaginile bitmap trebuie s fie micorate. n programul GRAFMENU, aceast operaie este executat de
funcia StretchBitmap.
Mai nti trebuie s obinem contextul de dispozitiv al ecranului, dimensiunile fontului sistem, i
apoi s crem dou contexte de dispozitiv n memorie:
hdc CreatelC ("DISPLAY", NULL, NULL, NULL) ; GetTextMetrics (hdc,
&tm) ; hdcMeml CreateCompatibleDC (hdc) ; hdcMem2 CreateCompatibleDC (hdc) ; DeleteDC (hdc) ;

Variabila handle a imaginii bitmap transmis funciei este hBitmapl. Programul poate obine dimensiunile
acestei imagini cu ajutorul funciei GetObject:
GetObject (hBitmapl, sizeof (BITMAP), (PSTR) &bml) ;

Apelul de mai sus copiaz dimensiunile n structura bml, de tip BITMAP. Structura bml primete
aceleai valori ca i bml, apoi unele cmpuri sunt modificate pe baza dimensiunilor fontului sistem:
bm2 = bml ;
bm2.bmWidth
= (tm.tmAveCharWidth * bm2.bmWidth) / 4 ;
bm2.bmHeight
- (tm.tmHeight
* bm2.bmHeight) / 8 ;
bm2.bmWidthBytes = ((bm2.bmWidth + 15) / 16) * 2 ;

Apoi poate fi creat o nou imagine bitmap cu variabila handle hBiimapl, pe baza dimensiunilor
modificate:
hBitmap2 = CreateBitmapIndirect (&bm2) ;

Putei apoi s selectai cele dou imagini bitmap n contextele de dispozitiv create:
SelectObject (hdcMeml, hBitmapl) ; SelectObject (hdcMem2,
hBitmap2) ;

Dorim s copiem prima imagine bitmap n cea de-a doua i s o micorm n timpul acestei operaii.
Pentru aceasta folosim funcia StretchBH:
StretchBlt (hdcMem2, O, O, bm2.bmW idth, bm2.bmHeight,
hdcMeml, O, O, bml.bmWidth, bml.bmHeight, SRCCOPY) ;

Acum, cea de-a doua imagine bitmap este scalat corespunztor i putem s o folosim n meniu.
Operaiile de curenie sunt simple:
DeleteDC (hdcMeml) ; DeleteDC (hdcMem2) ; DeleteObject
(hBitmapl) ;

Compunerea meniului

Funcia WinMain din programul GRAFMENU folosete funciile StretchBitmap i GetBitmapFont n


timpul construirii meniului. GRAFMENU are deja dou meniuri definite n fiierul script de resurse.
Acestea vor fi folosite ca meniuri derulante pentru opiunile File i Edit.
Programul GRAFMENU obine mai nti o variabil handle a unui meniu gol:
hMenu CreateMenu {) ;

Meniul derulant pentru opiunea File (coninnd articolele New, Open, Save i Save As) este ncrcat din
fiierul script de resurse:
hMenuPopup = LoadMenu (hlnstance, "MenuFile") ;

Imaginea bitmap care conine cuvntul FILE" este ncrcat tot din fiierul script de resurse i adus la
dimensiunile corespunztoare cu ajutorul funciei StretchBitmap:
hBitmapFile StretchBitmap (LoadBitmap (hlnstance, "BitmapFile")) ;

Variabilele handle ale imaginii bitmap i meniului derulant sunt folosite ca parametri pentru apelarea funciei
AppendMenu:
AppendMenu (hMenu, MF_BITMAP \ MF POPUP, (int) hMenuPopup, (PSTR) (LONG)
hBitmapFile) ;

Aceeai procedur este urmat i pentru meniul Edit:


hMenuPopup LoadMenu (hlnstance, "MenuEdit") ; hBitmapEdi t = StretchB itmap (LoadBi tmap
(h ln stan ce , "Bitm apE dit")) ; AppendMenu (hMenu, MF BITMAP 1 MF_POPUP, (int) hMenuPopup, (PSTR)
(LONG) hBitmapEdit) ;

Meniul derulant pentru cele trei fonturi este construit prin apelarea funciei GetBitmapFont:
CreateMenu ()
hMenuPopu
i < 3 ; 7++)
p for (i =
0 {
hBi tm apP opF ont[i] = GetB itmapFont (i ) ;
AppendMenu (hMenuPopup, MFJITMAP, IDM_C0UR + i,
(PSTR) (LONG) hMenuPopupFont[i]) ;

Apoi meniul derulant creat este ataat la meniul principal:


hBitmapFont = StretchBitmap (LoadBitmap (hlnstance, "BitmapFont")) AppendMenu (hMenu, MF_BITMAP | MF_POPUP, (int)
hMenuPopup, (PSTR) (LONG) hBitmapFont) ;

Acum meniul ferestrei este complet. Putei include variabila handle hMenu n apelul funciei
CreateWvndow:
hwnd = CreateWindow (szAppName, "Bitmap Menu Demonstraion", WS_OVERLAPPEDWINDOW,
CWJJSEDEFAULT, CW_USEDEFAULT, CWJJSEDEFAULT, CWJSEDEFAULT, NULL,
hMenu, hlnstance, NULL) ;

Dup obinerea variabilei hwnd, programul GRAFMENU poate modifica meniul sistem. Pentru aceasta
este nevoie de o variabil handle a meniului sistem:
hMenu = GetSystemMenu {hwnd, FALSE);

Instruciunea urmtoare ncarc imaginea bitmap Help" i o aduce la dimensiunile corespunztoare:


hBitmapHelp = StretchBitmap (LoadBitmap {hlnstance, "BitmapHelp")) ;

Instruciunile urmtoare adaug o bar de separare i imaginea bitmap redimen-sionat:


AppendMenu (hMenu, MF_SEPARATOR, NULL,
NULL) ;
AppendMenu (hMenu, MF_BITMAP,
IDM_HELP, (PSTR) (LONG) hBitmapHelp) ;

Nu uitai c imaginile bitmap sunt obiecte GDI i trebuie s fie explicit terse nainte de nchiderea
programului. Operaiile de tergere sunt executate dup ce GRAFMENU iese din bucla de prelucrare a
mesajelor:
DeleteObject
DeleteObject
DeleteObject
DeleteObject

(hBitmapHelp)
(hBitmapEdit)
(hBitmapFile)
(hBitmapFont)

;
;
;
;

for (i = 0 ; i < 3
DeleteObject (hBitmapPopFont[i]) ;

Vom ncheia aceast seciune cu cteva note suplimentare:

n meniurile de pe primul nivel, Windows ajusteaz nlimea barei de


meniu astfel nct s ncap cea mai nalt imagine bitmap. Celelalte imagini
(sau iruri de caractere) sunt aliniate la partea de sus a barei de meniu.
Dimensiunea barei de meniu obinut cu instruciunea

GetSystemMetrics (SM_CYMENU) ; nu

mai este valabil dac includei imagini bitmap n

bara de meniu.

Dup cum putei s v dai seama dac v jucai puin cu programul


GRAFMENU, putei s folosii marcaj de validare pentru articolele de
meniu afiate prin imagini bitmap, dar marcajele au dimensiuni normale.
* Dac acest lucru v deranjeaz, putei s creai un marcaj de validare propriu i s l folosii
cu ajutorul funciei SetMenuItemBitmaps.

O alt metod de folosire a articolelor de meniu non-text (sau a textelor


scrise cu alte fonturi dect fontul sistem) este s folosii meniuri desenate
de utilizator".

Adugarea unei interfee cu tastatura


Acum avem o alt problem. Atunci cnd un articol de meniu conine text, Windows i adaug automat o
interfa cu tastatura. Putei s selectai un articol de meniu folosind tasta Alt n combinaie cu o liter din
irul de caractere. Dac includei n meniu o imagine bitmap, eliminai interfaa cu tastatura. Chiar dac
imaginea bitmap conine un text, Windows nu tie acest lucru.
Acum devine util mesajul WM_MENUCHAR. Windows trimite mesajul WM_MENUCHAR ctre
procedura ferestrei atunci cnd utilizatorul apas tasta Alt n combinaie cu un caracter care nu corespunde
unui articol de meniu. Trebuie s interceptai mesajele WM_MENUCHAR i s verificai cuvntul mai
puin semnificativ al parametrului wParam (codul ASCII al tastei apsate). Dac acest caracter corespunde
unui articol de meniu returnai sistemului de operare un numr care s fie ntreg lung i n care cuvntul
mai semnificativ are valoarea 2, iar cuvntul mai puin semnificativ conine indexul articolului de meniu pe
care vrei s l asociai tastei apsate. Windows va face restul.

TASTE DE ACCELERARE
Descrise foarte simplu, tastele de accelerare sunt combinaii de taste care genereaz mesaje
WM_COMMAND (sau WM_SYSCOMMAND). De cele mai multe ori, programele folosesc tastele de
accelerare pentru duplicarea aciunilor unor opiuni de meniu folosite mai des. (Tastele de accelerare pot
declana ns i executarea unor aciuni care nu sunt funcii de meniu.) De exemplu, unele programe
Windows au meniuri Edit n care este inclus opiunea Delete; n mod convenional, aceste programe
stabilesc tasta Del ca accelerator pentru aceast opiune. Utilizatorul poate s selecteze opiunea Delete din
meniu apsnd o combinaie Alt+tast sau poate s apese direct tasta Del. Atunci cnd procedura ferestrei
recepioneaz mesajul WM_COMMAND, nu trebuie s determine dac a fost folosit meniul sau tasta de
accelerare.

De ce avei nevoie de taste de accelerare


S-ar putea s v ntrebai: de ce trebuie s folosesc taste de accelerare? Nu a putea s interceptez mesajele
WM_KEYDOWN sau WM_CHAR i s dublez chiar eu funciile de meniu? Care este avantajul? n cazul
aplicaiilor cu fereastr unic este destul de simplu s interceptai mesajele de la tastatur, dar folosirea
tastelor de accelerare v ofer totui un avantaj: nu este nevoie s duplicai logica de interpretare a mesajelor
de la meniu i tastatur.
n cazul aplicaiilor cu ferestre i proceduri de fereastr multiple, tastele de accelerare devin foarte
importante. Aa cum am vzut, Windows trimite mesajele de la tastatur procedurii corespunztoare
ferestrei care deine cursorul de intrare. Totui, mesajele WM_COMMAND generate de tastele de
accelerare sunt trimise procedurii de fereastr a crei variabil handle este specificat n funcia Windows
Translate Accelerator. n general, aceasta este fereastra principal, adic fereastra creia i aparine i meniul,
ceea ce nseamn c logica de tratare a tastelor de accelerare nu trebuie s fie coninut n fiecare procedur
de fereastr.
Acest avantaj devine i mai important dac folosii casete de dialog nemodale (despre care vom
discuta n capitolul urmtor) sau ferestre descendent, n zona client a ferestrei principale. Dac definii o
tast de accelerare pentru deplasarea ntre ferestre, atunci o singur procedur de fereastr trebuie s
includ logica de tratare a acesteia. Ferestrele descendent nu primesc mesaje WM_COMMAND de la
tastele de accelerare.

Unele reguli privind atribuirea tastelor de accelerare


Teoretic, putei s definii un accelerator pentru aproape orice tast n combinaie cu tastele Shift, Alt sau
Ctrl. Totui, pentru consecven i pentru a evita ncurcturile, nu folosii tastele Tab, Enter, Esc i bara de
spaiu pentru acceleratori, deoarece acestea sunt deseori folosite pentru funcii de sistem.
De cele mai multe ori, tastele de accelerare sunt folosite pentru articolele din meniurile Edit ale

programelor. Tastele de accelerare recomandate pentru aceste articole s-au schimbat de la Windows 3.0
la Windows 3.1, aa c nu este ceva neobinuit s gsii aplicaii care accept ambele stiluri, prezentate n
tabelul urmtor:
Funcie
Undo (Anulare)
Cut (Decupare)
Copy (Copiere)
Paste (Lipire)
Delete (tergere)

Accelerator
vechi

Alt+Backspace
Shift+Del
Ctrl+Ins
Shift+Ins
Del

Accelerator
nou

Ctrl+Z
Ctrl+X
Ctrl+C
Ctrl+V
Del

O alt tast de accelerare deseori folosit este FI, pentru apelarea sistemului de asisten soft (help).
Evitai folosirea tastelor F4, F5 i F6 deoarece acestea sunt deseori folosite pentru funcii speciale n
programele care implementeaz interfaa multidocu-ment (MDI - Multiple Document Interface), despre care
vom discuta n Capitolul 18.

Tabelul de acceleratori
Tabelul tastelor de accelerare este definit n fiierul script de resurse. Forma general a acestuia este:
MyAccelerators ACCELERATORS {

[definirea acceleratorilor] )

Numele tabelului este MyAccelerators. Tabelul ACCELERATORS nu conine opiuni de ncrcare sau
opiuni legate de folosirea memoriei. Fiierul script de resurse poate conine mai multe tabele
ACCELERATORS.
Fiecare tast de accelerare trebuie definit pe o linie separat. Exist patru tipuri de definiii de
acceleratori:
"char",
"Achar",
nCode,
nCode,

id
[.SHIFT] [.CONTROL] [,ALT]
id
[,SHIFT] [.CONTROL] [,ALT]
id, ASCII
[,SHIFT] [.CONTROL] [.ALT]
id, VIRTKEY
[.SHIFT] [.CONTROL] [,ALT]

n acest exemplu, char" nseamn un singur caracter ncadrat de ghilimele, Achar" nseamn caracterulA
urmat de un singur caracter, ncadrate de ghilimele, id este un numr care are acelai rol ca i identificatorul din
definiia unui meniu. Aceasta este valoare pe care Windows o trimite procedurii de fereastr prin mesajul
WM_COM-MAND pentru identificarea acceleratorului. De obicei, aceti identificatori sunt definii n fiierul
antet. Aproape ntotdeauna, tastele de accelerare selecteaz opiuni din meniurile derulante. Atunci cnd
tasta de accelerare dubleaz o comand de meniu, folosii acelai identificator att pentru meniu, ct i pentru
tasta de accelerare. Atunci cnd tasta de accelerare nu dubleaz o comand de meniu, folosii un identificator
unic.
Pentru prima definiie, tasta de accelerare este caracterul inclus ntre ghilimele, fcndu-se diferena ntre
literele mari i cele mici:
"char", id

[,SHIFT] [.CONTROL] [,ALT]

Dac vrei s definii un accelerator pentru o combinaie ntre caracterul ncadrat de ghilimele i tastele Shif t,
Ctrl i Alt, adugai cuvintele cheie SHIFT, CONTROL i/sau ALT.
Pentru a doua definiie, acceleratorul este o combinaie ntre caracterul specificat si tasta Ctrl:
" Achar", id

[,SHIFT] [.CONTROL] [,ALT]

Acest tip de definiie este identic cu primul tip dac se folosete numai identificatorul
CONTROL.
Ultimele dou tipuri folosesc un numr n locul caracterului dintre ghilimele:
nCode, id, ASCII
nCode, id, VIRTKEY

[,SHIFT] [.CONTROL] [,ALT]


[,SHIFT] [.CONTROL] [.ALT]

Acest numr este interpretat fie ca un cod ASCII, fcndu-se diferena ntre caracterele mari i cele mici, fie ca un cod
virtual de tast, n funcie de cuvntul cheie folosit (ASCII sau VIRTKEY).
Tipurile de acceleratori cel mai des folosite sunt al doilea i al patrulea. Al doilea tip este folosit pentru combinaiile
cu tasta Ctrl. De exemplu, linia urmtoare definete un accelerator pentru combinaia Ctrl+A:
"AA\ id

Al patrulea tip de definiie este folosit pentru codurile de taste virtuale, cum ar fi tastele funcionale. De exemplu,
linia urmtoare definete un accelerator pentru combinaia Ctrl+F9:
VKJ9, wid, VIRTKEY, CONTROL

Identificatorul VK_F9 este definit n fiierele antet Windows ca fiind codul virtual pentru tasta F9, aa c trebuie s
includei acest fiier n fiierul script de resurse:
#include <windows.h>

Primul i al treilea tip de acceleratori sunt rareori folosite. Dac vrei s le folos totui, acordai o atenie deosebit
diferenierii majusculelor de minuscule. Windo1 face verificrile de coresponden cu "char" sau nCode n funcie de
caracterul aps Dac adugai cuvntul cheie SHIFT, Windows verific dac a fost apsat tasta Sh Aceast situaie
produce uneori rezultate neateptate. De exemplu, dac "char" e "A", acceleratorul este apelat atunci cnd apsai
tasta A i este apsat tasta Sh sau este activ Caps Lock, dar nu ambele deodat. Dac folosii "A" cu SHIFT, trebi s
apsai tastele A i Shift simultan, dar acceleratorul nu poate fi apelat n nici un dac este activ Caps Lock. La fel,
acceleratorul "a" este apelat dac tasta A este aps fr tasta Shift, sau dac tasta A este apsat mpreun cu tasta
Shift, iar Caps L< este activ. Acceleratorul "a" cu SHIFT este apelat numai dac tasta A este aps mpreun cu
tasta Shift, iar Caps Lock este activ.
Atunci cnd definii un accelerator pentru un articol de meniu, este bine includei combinaia de taste n
textul articolului. Caracterul tab (\t) separ textul accelerator, astfel nct combinaia de taste este aliniat la a
doua coloan. Pen notaia combinaiilor de taste n meniu, folosii textul Ctrl, Shift sau Alt, urmat semnul + i de
caracterul corespunztor. De exemplu:

F6

Shift+F6

Ctrl+F6

ncrcarea tabelului de acceleratori


n cadrul programului folosii funcia LoadAccelerators ca s ncrcai n mem< tabelul cu acceleratori i ca s
obinei o variabil handle a acestuia. Instruciu LoadAccelerators este asemntoare cu instruciunile Loadlcon,
LoadCursor, LoadBih i LoadMenu.

Mai nti, definii o variabil handle de tipul HACCEL pentru tabelul de celeratori:
HACCEL hAccel ; Apoi ncrcai tabelul de acceleratori:
hAccel = LoadAccelerators (hlnstance, "MyAccelerators") ;

Ca i n cazul pictogramelor, cursoarelor, imaginilor bitmap i al meniurilor, n fur LoadAccelerators, n locul


numelui tabelului de acceleratori putei s folosii un nur convertit cu ajutorul macroinstruciunii
MAKEINTRESOURCE sau precedat de racterul # i ncadrat de ghilimele.

Convertirea mesajelor de la tastatur


Vom discuta acum despre trei linii de cod folosite aproape n toate prograr Windows pe care le-am creat pn
acum n aceast carte. Secvena de cod de ma reprezint ciclul standard de tratare a mesajelor:
while (GetMessage (&msg, NULL, O, 0)) {
TranslateHessage (&msg) ; DispatchMessage (&msg) ; }

Iat cum se modific acest cod dac folosii un tabel de acceleratori:


while (GetMessage (&msg, NULL, 0, 0)) { if (ITranslateAccelerator (hwnd, hAccel, &msg))

TranslateMessage (&msg) ;
DispatchMessage (&msg) ;

Funcia TranslateAccelerator determin dac mesajul stocat n structura msg este un mesaj de la tastatur.
Dac da, funcia caut n tabelul de acceleratori cu variabila handie hAccel un accelerator care s
corespund combinaiei de taste. Dac gsete un astfel de accelerator, apeleaz procedura ferestrei cu
variabila handie hwnd. Dac identificatorul acceleratorului este acelai cu identificatorul unui articol din
meniul sistem, mesajul transmis ferestrei este WM_SYSCOMMAND. n caz contrar, mesajul transmis este
WM_COMMAND.
Funcia TranslateAccelerator returneaz o valoare diferit de zero dac mesajul a fost convertit (i deja
transmis ctre procedura ferestrei) i 0 n caz contrar. Dac funcia TranslateAccelerator returneaz o
valoare diferit de zero, nu mai este nevoie s apelai funciile TranslateMessage i DispatchMessage, ci trebuie
s revenii la apelul funciei GetMessage.
Parametrul hwnd din apelul funciei TranslateAccelerator poate prea puin ciudat, deoarece nu apare n
celelalte funcii din ciclul de tratare a mesajelor. Mai mult, chiar structura mesajului (variabila msg) are un
membru numit hwnd care este tot o variabil handie a unei ferestre. Iat de ce TranslateAccelerator
funcioneaz puin altfel dect celelalte:
Cmpurile structurii msg sunt completate de funcia GetMessage. Dac al doilea

parametru
este
NULL,
funcia
GetMessage
intercepteaz
mesajele
tuturor
ferestrelor
care aparin aplicaiei. La ieirea din funcia GetMessage, membrul hwnd al structurii
msg conine variabila handie a ferestrei care va primi mesajul. Totui, atunci cnd
convertete un mesaj de la tastatur ntr-un mesaj WM_COMMAND sau WM_S"YSCOMMAND,
funcia
TranslateAccelerator
nlocuiete
coninutul
cmpului
msg.hwnd
cu variabila handie a ferestrei specificate ca prim parametru. Acesta este motivul
pentru care Windows transmite toate mesajele privind tastele de accelerare ctre
fereastra principal, chiar dac o alt fereastr a aplicaiei deine cursorul de intrare.
Funcia
TranslateAccelerator
nu
convertete
mesajele
de
la
tastatur
atunci
cnd
cursorul de intrare este deinut de o caset de dialog modal sau de o caset de mesaje,
deoarece mesajele pentru aceste ferestre nu sunt prelucrate n ciclul de mesaje al
programului.
S-ar putea ca n anumite cazuri n care cursorul de intrare este deinut de o alt fereastr a
programului (cum ar fi o caset de dialog nemodal) s nu dorii ca mesajele privind tastele de accelerare
s fie convertite. n capitolul urmtor vei vedea cum putei s tratai o astfel de situaie.

Recepionarea mesajelor de accelerare


Atunci cnd un accelerator corespunde unui articol din meniul sistem, funcia Trans-lateAccelerator transmite ctre
procedura ferestrei un mesaj WM_SYSCOMMAND. n restul cazurilor, funcia TranslateAccelerator transmite ctre
procedura ferestrei un mesaj WM_COMMAND. Tabelul urmtor prezint tipurile de mesaje pe care putei s Ie
recepionai pentru acceleratori, comenzi de meniu i controale de tip fereastr descendent:
LOWORD (wParam)

HIWORD (wParam)

IParam

Accelerator:

Identificatorul
acceleratorului

Meniu:

Identificatorul
meniului
Identificatorul
controlului

Cod de ntiinare

Variabila handle a
ferestrei descendent

Control:

Dac acceleratorul corespunde unui articol de meniu, procedura ferestrei primete i mesajele WMJNITMENU,
WMJNITMENUPOPUP i WM_MENUSELECT, la fel ca i n cazul selectrii unui articol de meniu. Programele
activeaz sau dezactiveaz articolele dintr-un meniu derulant n timpul prelucrrii mesajului WMJNITMENUPOPUP, aa c avei la dispoziie aceast posibilitate chiar i n cazul folosirii acceleratorilor. Dac
acceleratorul corespunde unui articol de meniu gri sau dezactivat, funcia TranslateAccelerator nu mai transmite
ctre procedura ferestrei mesajul WM_COMMAND sau mesajul WM_SYSCOMMAND.
Dac fereastra activ este minimizat, funcia TranslateAccelerator transmite ctre procedura ferestrei numai
mesajele WM_SYSCOMMAND - nu i mesajele WM_COMMAND - pentru acceleratorii corespunztori
articolelor activate din meniul sistem. De asemenea, funcia TranslateAccelerator trimite ctre procedura ferestrei
mesajele WM_COMMAND pentru acceleratorii care nu corespund nici unui articol de meniu.

Programul POPPAD cu meniu i acceleratori


n Capitolul 8 am creat un program numit POPPAD1 care folosea un control de edit de tip fereastr descendent
pentru implementarea unui editor de texte rudimentar, n acest capitol vom aduga la program meniurile File i
Edit i vom numi noul program POPPAD2. Toate articolele din meniul Edit vor fi funcionale; funciile meniului
File vor fi implementate n Capitolul 11, iar funcia Prin n Capitolul 12. Programul POPPAD2 este prezentat n
Figura 10-10.

POPPAD2.MAK

#
#Fiierul de construcie POPPAD2.MAK
#
poppad2.exe:poppad2.objpoppad2.res
$(LINKER)$(GUIFLAGS)OUT:poppad2.exepoppad2.objpoppad2.res$(GUILIBS)
poppad2.obj:poppad2.cpoppad2.h
$(CC)$(CFLAGS)poppad2.c
poppad2.res:poppad2.rcpoppad2.hpoppad2.ico
$(RC)$(RCVARS)poppad2.rc

POPPAD2.C
/*
POPPAD2.CProgramderulantdeeditare(includemeniuri)
(c)CharlesPetzold,1996
*/
#include<windows.h>
#include"poppad2.h"
LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);

charszAppName[]="PopPad2";
intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,
PSTRszCmdLine,intiCmdShow)
{
HACCELhAccel;
HWNDhwnd;
MSGmsg;
WNDCLASSEXwndclass;
wndclass.cbSize=sizeof(wndclass);
wndclass.style=CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc=WndProc;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hInstance=hInstance;
wndclass.hIcon=LoadIcon(hInstance,szAppName);
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName=szAppName;
wndclass.lpszClassName=szAppName;
wndclass.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
RegisterClassEx(&wndclass);

349
hwnd=CreateWindow(szAppName,szAppName,
WS_OVERLAPPEDWINDOW,
GetSystemMetrics(SM_CXSCREEN)/4,
GetSystemMetrics(SM_CYSCREEN)/4,
GetSystemMetrics(SM_CXSCREEN)/2,
GetSystemMetrics(SM_CYSCREEN)/2,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
hAccel=LoadAccelerators(hInstance,szAppName);
while(GetMessage(&msg,NULL,0,0))
{
if(!TranslateAccelerator(hwnd,hAccel,&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
returnmsg.wParam;
}
AskConfirmation(HWNDhwnd)
{
returnMessageBox(hwnd,"ReallywanttoclosePopPad2?",
szAppName,MB_YESNO|MB_ICONQUESTION);
}
LRESULTCALLBACKWndProc(HWNDhwnd,UINTiMsg,WPARAMwParam,LPARAMlParam)
{
staticHWNDhwndEdit;
intiSelect,iEnable;
switch(iMsg)
{
caseWM_CREATE:
hwndEdit=CreateWindow("edit",NULL,
WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|
WS_BORDER|ES_LEFT|ES_MULTILINE|
ES_AUTOHSCROLL|ES_AUTOVSCROLL,
0,0,0,0,
hwnd,(HMENU)1,
((LPCREATESTRUCT)lParam)>hInstance,NULL);
return0;
caseWM_SETFOCUS:
SetFocus(hwndEdit);
return0;
caseWM_SIZE:
MoveWindow(hwndEdit,0,0,LOWORD(lParam),
HIWORD(lParam),TRUE);
return0;
caseWM_INITMENUPOPUP:
if(lParam==1)
{
EnableMenuItem((HMENU)wParam,IDM_UNDO,
SendMessage(hwndEdit,EM_CANUNDO,0,0)?
MF_ENABLED:MF_GRAYED);

350
EnableMenuItem((HMENU)wParam,IDM_PASTE,

IsClipboardFormatAvailable(CF_TEXT)?
MF_ENABLED:MF_GRAYED);
iSelect=SendMessage(hwndEdit,EM_GETSEL,0,0);
if(HIWORD(iSelect)==LOWORD(iSelect))
iEnable=MF_GRAYED;
else
iEnable=MF_ENABLED;
EnableMenuItem((HMENU)wParam,IDM_CUT,iEnable);
EnableMenuItem((HMENU)wParam,IDM_COPY,iEnable);
EnableMenuItem((HMENU)wParam,IDM_DEL,iEnable);
return0;
}
break;
caseWM_COMMAND:
if(lParam)
{
if(LOWORD(lParam)==1&&
(HIWORD(wParam)==EN_ERRSPACE||
HIWORD(wParam)==EN_MAXTEXT))
MessageBox(hwnd,"Editcontroloutofspace.",
szAppName,MB_OK|MB_ICONSTOP);
return0;
}
elseswitch(LOWORD(wParam))
{
caseIDM_NEW:
caseIDM_OPEN:
caseIDM_SAVE:
caseIDM_SAVEAS:
caseIDM_PRINT:
MessageBeep(0);
return0;
caseIDM_EXIT:
SendMessage(hwnd,WM_CLOSE,0,0);
return0;
caseIDM_UNDO:
SendMessage(hwndEdit,WM_UNDO,0,0);
return0;
caseIDM_CUT:
SendMessage(hwndEdit,WM_CUT,0,0);
return0;
caseIDM_COPY:
SendMessage(hwndEdit,WM_COPY,0,0);
return0;
caseIDM_PASTE:
SendMessage(hwndEdit,WM_PASTE,0,0);
return0;
caseIDM_DEL:
SendMessage(hwndEdit,WM_CLEAR,0,0);
return0;

351
caseIDM_SELALL:
SendMessage(hwndEdit,EM_SETSEL,0,1);
return0;
caseIDM_HELP:
MessageBox(hwnd,"Helpnotyetimplemented!",
szAppName,MB_OK|MB_ICONEXCLAMATION);
return0;
caseIDM_ABOUT:
MessageBox(hwnd,
"POPPAD2(c)CharlesPetzold,1996",
szAppName,MB_OK|MB_ICONINFORMATION);
return0;
}
break;
caseWM_CLOSE:
if(IDYES==AskConfirmation(hwnd))
DestroyWindow(hwnd);
return0;
caseWM_QUERYENDSESSION:
if(IDYES==AskConfirmation(hwnd))
return1;
else
return0;
caseWM_DESTROY:
PostQuitMessage(0);
return0;

}
returnDefWindowProc(hwnd,iMsg,wParam,lParam);
}

POPPAD2.RC
/*
POPPAD2.RCfiier script de resurse
*/
#include<windows.h>
#include"poppad2.h"
PopPad2ICONpoppad2.ico
PopPad2MENU
{
POPUP"&File"
{
MENUITEM"&New",IDM_NEW
MENUITEM"&Open...",IDM_OPEN
MENUITEM"&Save",IDM_SAVE
MENUITEM"Save&As...",IDM_SAVEAS
MENUITEMSEPARATOR
MENUITEM"&Print",IDM_PRINT
MENUITEMSEPARATOR
MENUITEM"E&xit",IDM_EXIT
}
POPUP"&Edit"
{
MENUITEM"&Undo\tCtrl+Z",IDM_UNDO
MENUITEMSEPARATOR
MENUITEM"Cu&t\tCtrl+X",IDM_CUT
MENUITEM"&Copy\tCtrl+C",IDM_COPY

352
MENUITEM"&Paste\tCtrl+V",IDM_PASTE
MENUITEM"De&lete\tDel",IDM_DEL
MENUITEMSEPARATOR
MENUITEM"&SelectAll",IDM_SELALL
}
POPUP"&Help"
{
MENUITEM"&Help...",IDM_HELP
MENUITEM"&AboutPopPad2...",IDM_ABOUT
}
}
PopPad2ACCELERATORS
{
"^Z",IDM_UNDO
VK_BACK,IDM_UNDO,VIRTKEY,ALT
"^X",IDM_CUT
VK_DELETE,IDM_CUT,VIRTKEY,SHIFT
"^C",IDM_COPY
VK_INSERT,IDM_COPY,VIRTKEY,CONTROL
"^V",IDM_PASTE
VK_INSERT,IDM_PASTE,VIRTKEY,SHIFT
VK_DELETE,IDM_DEL,VIRTKEY
VK_F1,IDM_HELP,VIRTKEY
}

POPPAD2.H
/*
POPPAD2.Hfiierantet
*/
#defineIDM_NEW1
#defineIDM_OPEN2
#defineIDM_SAVE3
#defineIDM_SAVEAS4
#defineIDM_PRINT5
#defineIDM_EXIT6
#defineIDM_UNDO10
#defineIDM_CUT11
#defineIDM_COPY12
#defineIDM_PASTE13
#defineIDM_DEL14
#defineIDM_SELALL15
#defineIDM_HELP20
#defineIDM_ABOUT22

POPPAD2.ICO
Fig. 10.10 Programul POPPAD2

POPPAD2.ICO

Fiierul de resurse POPPAD2.RC conine meniul i tabelul de acceleratori. Vei


observa c toi acceleratorii sunt indicai n irurile de caractere din meniul derulant
Edit, dup caracterul tab (\t).

Activarea articolelor de meniu


Una dintre sarcinile majore ale procedurii ferestrei este activarea i dezactivarea opiunilor din meniul
Edit, executat prin prelucrarea mesajului WMJNITME-NUPOPUP. Mai nti, programul verific dac
urmeaz s fie afiat meniul derulant Edit. Deoarece indexul poziional al meniului Edit este 1 (indexarea
ncepe de la 0, corespunztor meniului File), parametrul IParam are valoarea 1 atunci cnd urmeaz s
fie afiat meniul Edit.
Pentru a determina dac opiunea Undo poate fi activat, programul POPPAD2 trimite ctre controlul
de editare mesajul EM_CANUNDO. Funcia SendMessage returneaz o valoare diferit de 0 pentru
cazul n care controlul de editare poate executa operaia Undo (Anulare), cnd opiunea este activat; n
caz contrar, opiunea Undo este afiat cu gri (dezactivat):
EnableMenuItem ((HMENU) wParam, IDMJJNDO,
SendMessage (hwndEdit, EM_CANUNDO, 0, 0) ? MFJNABLED : MF_GRAYED) ;

Opiunea Paste trebuie s fie activat numai dac memoria temporar (clipboard) conine text. Putem
determina acest lucru prin apelarea funciei IsClipboardFormat-AvaUable cu identificatorul CFJTEXT:
EnableMenuItem ((HMENU) wParam, IDM_PASTE, IsClipboardFormatAvailable (CF TEXT) ?
MF_ENABLED : MF_6RAYED) ; "

Opiunile Cut, Copy i Delete trebuie s fie activate numai dac n controlul de editare a fost selectat text.
Aflm acest lucru trimind controlului mesajul EM_GETSEL. Controlul returneaz un numr ntreg:
iSelect = SendMessage (hwndEdit, EM_GETSEL, 0, 0) ;

Cuvntul mai puin semnificativ al variabilei iSelect este poziia primului caracter selectat. Cuvntul mai
semnificativ al variabilei iSelect este poziia ultimului caracter selectat. Dac cele dou cuvinte sunt egale,
nseamn c nu a fost selectat nici un text:
if (HIWORD (iSelect) " LOWORD (iSelect))
iEnable = MFJRAYED ; else
iEnable = MFJNABLED ;

Valoarea variabilei iEnable este folosit apoi pentru opiunile Cut, Copy i Delete:
EnableMenuItem ((HMENU) wParam, IDM_CUT, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_C0PY,
iEnable) ; EnableMenuItem ((HMENU) wParam, IDM DEL,
iEnable) ;

Prelucrarea opiunilor de meniu


Desigur, dac n programai PQPPAD2<nu am fi folosit un control de editare de tip fereastr descendent, ar
fi teribuit s implementm acum opiunile Undo, Cut, Copy, Delete i Select AII din menial Edit. Controlul de
editare uureaz aceast sarcin, deoarece nu mai trebuie dect s trimitem controlului cte un mesaj
pentru fiecare dintre aceste opiuni:
case IDMJJNDO :
SendMessage (hwndEdit, WMJJNDO, 0, 0) ; return 0 ;
case IDH_CUT :
SendMessage (hwndEdit, WM_CUT, 0, 0) ; return 0 ;
case IDM_COPY :
SendHessage (hwndEdit, WM_COPY, 0, 0) ;
case IDM_PASTE :
SendMessage ihvmdEdit, VM_P&S1E, 0, 0) ; r e t ur n 0 ;
case IDM_DEL :

SendMessage (hwndEdit, WH_CLEAR, 0, 0) ; r e t u r n 0 ;


case IDM_SELALL :
SendMessage (hwndEdit, EMJETSEL, 0, -1) ; r e t u r n 0 ;

Remarcai c secvena de instruciuni de mai sus ar fi putut fi simplificat i mai mult dac valorile
identificatorilor de meniu IDMJUNDO, IDM_CUT i aa mai departe ar fi fost egale cu valorile
corespunztoare mesajelor de fereastr WMJJNDO, WM_CUT i aa mai departe.
Opiunea About din meniul Help apeleaz o simpl caset de mesaje:
case IDM_AB0UT :
MessageBox (hwnd,
"P0PPAD2 (c) Charles Petzold, 1996", szAppName, MB_0K | MBJCONINFORMATION) ;
return 0 ;

n Capitolul 11 vom transforma aceast caset de mesaje ntr-o caset de dialog. Tot o caset de mesaje este
apelat i atunci cnd selectai opiunea Help din acest meniu, sau atunci cnd apsai tasta de accelerare FI.
Opiunea Exit trimite ctre procedura ferestrei un mesaj WM_CLOSE:
case IDMJXIT :

SendMessage (hwnd, WM_CLOSE, 0, 0) ; return 0 ;

Acelai lucru l face procedura DefWindowProc atunci cnd primete un mesaj WM_SYSCOMMAND n care
parametrul wParam are valoarea SC_CLOSE.
n programele anterioare nu am prelucrat mesajele WM_CLOSE n procedura ferestrei, ci le-am transmis
procedurii DefWindowProc. Procedura DefWindowProc prelucreaz foarte simplu acest mesaj: apeleaz funcia
DestroyWindow. n loc s trimit mesajul WM_CLOSE ctre procedura DefWindowProc, programul POPPAD2 l
prelucreaz. Acest lucru nu este att de important acum, dar va deveni n Capitolul 11, cnd programul POPPAD2
va putea s editeze fiiere:
case WM_CLOSE : if
(IDYES --

AskConfirmation (hwnd))

DestroyWindow (hwnd) return 0 ;

AskConfirmation este o funcie din programul POPPAD2 care afieaz o caset de mesaje, cernd
utilizatorului s confirme nchiderea programului:
AskConfirmation (HWND hwnd) { return MessageBox (hwnd, "Reall y want to close Poppad2?",
szAppName, MB_YESNO | MBJCONQUESTION) ; }

Caseta de mesaje (ca i funcia AskConfirmation) returneaz valoarea IDYES dac a fost selectat butonul Yes.
Doar n acest caz programul POPPAD2 apeleaz funcia DestroyWindow. n caz contrar, execuia
programului continu.
Dac dorii s cerei confirmarea utilizatorului nainte de terminarea unui pro gram, trebuie s prelucrai
i mesajul WM_QUERYENDSESSION. La nchiderea unei sesiuni de lucru Windows, sistemul de operare
ncepe s trimit mesaje WM_QUERY-ENDSESSION fiecrei proceduri de fereastr. Dac oricare dintre
ferestrele interogate rspunde cu valoarea 0, sesiunea Windows nu este ncheiat. Iat cum prelucrm
mesajul WM_QUERYENDSESSION:
case WM QUERYENDSESSION :
if~(IDYES == AskConfirmation (hwnd))

return 1 ; else
return 0 ;

Mesajele WM_CLOSE i WM_QUERYENDSESSION sunt singurele mesaje pe care trebuie s le


prelucrai dac vrei s cerei confirmarea utilizatorului nainte de nchiderea unui program. Din aceast
cauz, am fcut ca opiunea Exit din programul POPPAD2 s trimit ctre procedura ferestrei un mesaj
WM_CLOSE. n acest fel am evitat cererea unei confirmri de ieire n al treilea punct.
Dac prelucrai mesajele WM_QUERYENDSESSION s-ar putea s v intereseze i mesajele
WM_ENDSESSION. Windows trimite acest mesaj ctre toate procedurile de fereastr care au primit anterior
un mesaj WM_QUERYENDSESSION. Parametrul wParam are valoarea 0 dac sesiunea de lucru nu s-a
ncheiat deoarece un alt program a returnat valoarea 0 n urma primirii mesajului WM_QUERYENDSESSION.
Mesajul WM_ENDSESSION rspunde de fapt la ntrebarea: I-am spus sistemului de operare c poate s-mi
ntrerup execuia, dar a fcut-o?
Dei n meniul File al programului POPPAD2 am inclus opiunile normale New, Open, Save i Save As,
acestea nu sunt nc funcionale. Pentru prelucrarea acestor comenzi trebuie s folosim casete de dialog.
Suntem pregtii s spunem cte ceva i despre acestea.

S-ar putea să vă placă și