Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: 'Złośliwiec'
Biblioteki C++

Okna dialogowe, cz. 4

[lekcja]

Dialog wyboru koloru


Więc chodź, pomaluj mój świat na żółto i na niebieeeesko! No dobra, ale najpierw przydałoby się wybrać te kolory, bo inaczej pomalujemy świat na czarno, a przecież nie jesteśmy żadnymi wrednymi satanistami (bez względu na to, co sugeruje nazwa tej strony ;-)) i kolorki lubimy. Dialog, jaki sobie tym razem wyczarujemy, wygląda następująco:

Kolorowo, aż się niedobrze robi ;-) (Windows 98)
Kolorowo, aż się niedobrze robi ;-) (Windows 98)


Czarowanie rozpoczniemy, jak się pewnie domyślasz, od przedstawienia nowej struktury. Będzie to struktura typu CHOOSECOLOR:

C/C++
typedef struct
{ // cc
   
DWORD lStructSize;
   
HWND hwndOwner;
   
HWND hInstance;
   
COLORREF rgbResult;
   
COLORREF * lpCustColors;
   
DWORD Flags;
   
LPARAM lCustData;
   
LPCCHOOKPROC lpfnHook;
   
LPCTSTR lpTemplateName;
} CHOOSECOLOR;

Jak widzisz, w porównaniu z OPENFILENAME nie ma się czego bać :-). Zresztą znów możemy bezczelnie pominąć kilka pól, które na razie nam się nie przydadzą. Dwa pierwsze pola już znasz, takie same były w strukturze OPENFILENAME. Znaczenia czwartego i piątego się pewnie domyślasz - tak, tu będzie przechowywany rezultat dialogu.
 
Zwróć uwagę na pole lpCustColors. Powinien on wskazywać na tablicę 16 kolorów. Musimy ją sobie sami stworzyć. Jak widać, na dialogu wyboru koloru mamy 48 predefiniowanych (standardowych) kolorów - tych zmieniać nie możemy, można je tylko wybrać, oraz 16 kolorów niestandardowych, definiowanych przez użytkownika. To właśnie na nie jest ta tablica. Jeśli jej nie utworzymy, dialog nie będzie miał gdzie przechowywać danych o tych kolorach.
 
Tablicę, wskazywaną przez lpCustColors dobrze jest zainicjalizować, albo przynajmniej wyzerować funkcją ZeroMemory. Jeśli tego nie zrobimy, to nic złego się nie stanie, po prostu dialog będzie wyświetlał 16 przypadkowych kolorów.
 
Nie pozostaje nic innego, jak tylko wypełnić strukturę i wywołać dialog na scenę:

C/C++
CHOOSECOLOR ccl;
COLORREF TabKol[ 16 ];
BOOL bResult;

ZeroMemory( & ccl, sizeof( CHOOSECOLOR ) );
ccl.lStructSize = sizeof( CHOOSECOLOR );
ccl.hwndOwner = hwnd;
ccl.lpCustColors = TabKol;
ccl.Flags = CC_ANYCOLOR;
bResult = ChooseColor( & ccl );

Jeśli wszystko pójdzie zgodnie z planem, to funkcja ChooseColor zwróci TRUE. W przeciwnym wypadku będziemy musieli wywołać specjalną funkcję CommDlgExtendedError w celu sprawdzenia, jaki konkretnie błąd wystąpił. Jeśli ChooseColor zwróci FALSE, ale CommDlgExtendedError zwróci 0, będzie to oznaczało, że żadnego błędu nie było, tylko użytkownik wcisnął przycisk Anuluj:

C/C++
DWORD dwErr;
bResult = ChooseColor( & ccl );

if( bResult )
   
 MessageBox( hwnd, "Wybrano nowy kolor", "test", MB_ICONINFORMATION );
else
{
   
dwErr = CommDlgExtendedError();
   
if( !dwErr )
       
 MessageBox( hwnd, "Anulowano wybór koloru", "test", MB_ICONEXCLAMATION );
   
else
       
 MessageBox( hwnd, "Wystąpił jakowyś błąd", "test", MB_ICONSTOP );
   
}

Ten przykład ilustruje tylko samo wywołanie dialogu, nie robi nic z wybranym kolorem (który zwracany jest w polu rgbResult, jak nietrudno się domyślić) ani nawet nie sprawdza, czy użytkownik wybrał w ogóle jakiś kolor. To wszystko pozostawiam tobie do samodzielnych eksperymentów.

Dialog wyboru czcionki


Z wyborem czcionek sprawa ma się podobnie. Tym razem struktura nazywa się CHOOSEFONT, a funkcja otwierająca dialog - ChooseFont. Mamy też i flagi - w większości oznaczają one rodzaje czcionek, jakie mają być dostępne do wyboru:

C/C++
typedef struct { // cf
   
DWORD lStructSize;
   
HWND hwndOwner;
   
HDC hDC;
   
LPLOGFONT lpLogFont;
   
INT iPointSize;
   
DWORD Flags;
   
DWORD rgbColors;
   
LPARAM lCustData;
   
LPCFHOOKPROC lpfnHook;
   
LPCTSTR lpTemplateName;
   
HINSTANCE hInstance;
   
LPTSTR lpszStyle;
   
WORD nFontType;
   
WORD ___MISSING_ALIGNMENT__;
   
INT nSizeMin;
   
INT nSizeMax;
} CHOOSEFONT;

Trochę tego więcej niż ostatnio, ale nie marudzimy i zabieramy się do roboty:

C/C++
CHOOSEFONT cfnt;
LOGFONT lf;

ZeroMemory( & cfnt, sizeof( CHOOSEFONT ) );
cfnt.lStructSize = sizeof( CHOOSEFONT );
cfnt.hwndOwner = hwnd;
cfnt.Flags = CF_EFFECTS | CF_FORCEFONTEXIST | CF_SCREENFONTS;
cfnt.lpLogFont = & lf;

Podobnie jak w przypadku dialogu z kolorkami, tutaj również potrzebujemy własnej zmiennej, tym razem typu LOGFONT. Wskaźnik do niej musimy przekazać w polu lpLogFont (ostatnia linijka przykładu). Jest to bardzo ważne, gdyż inaczej dialog nie będzie miał gdzie zapamiętać wybranej czcionki. Gdybyśmy dorzucili do flag CF_INITTOLOGFONTSTRUCT, a zmienna lf zawierałaby już prawidłowy opis czcionki, to dialog przyjąłby tę czcionkę za domyślną.

Dialog wyboru czcionki (Windows 98)
Dialog wyboru czcionki (Windows 98)

 
Użyliśmy trzech flag, jak widać. Pierwsza, CF_EFFECTS, oznacza że dialog powinien umożliwiać wybór efektów dla czcionki. Te efekty to: podkreślenie, przekreślenie oraz kolor. Nie są to zbyt potrzebne rzeczy, ale domyślnie są ukryte, co paskudnie wygląda.
 
Flaga CF_FORCEFONTEXIST oznacza, że użytkownik musi wybrać istniejącą czcionkę. Jeśli menda wpisze złośliwie coś dziwnego w pole z nazwą czcionki, a ta flaga jest ustawiona, to system ochrzani użytkownika za pisanie głupot ;-).
 
No i wreszcie CF_SCREENFONTS mówi systemowi, że ma nam wyświetlić tylko czcionki ekranowe. Gdyby była taka potrzeba, moglibyśmy wyświetlić tylko czcionki drukarki (flaga CF_PRINTERFONTS) albo i jedne, i drugie (flaga CF_BOTH). Jeśli natomiast nie damy żadnej z wymienionych w tym akapicie trzech flag, to dostaniemy uroczy (i absurdalny) komunikat, że nie zainstalowano żadnych czcionek w systemie, a następnie funkcja ChooseFont zwróci FALSE, co oznacza błąd.
 
Jeśli jednak do takiej niewesołej sytuacji nie doszło, to ChooseFont zwraca TRUE i wówczas możemy skorzystać z kilku następnych pól naszej struktury CHOOSEFONT, ażeby uzyskać informacje o wybranej przez użytkownika czcionce. Pole iPointSize będzie wówczas zawierało wybrany rozmiar czcionki, rgbColors - jej kolor, nFontType - typ czcionki (zawiera informację o pogrubieniu / pochyleniu czcionki, o tym czy jest to czcionka ekranowa czy też czcionka drukarki itp.). No i najważniejsze pole - lpLogFont będzie zawierało wskaźnik do struktury LOGFONT, opisującej wybraną czcionkę.
 
Pozostaje pytanie, jak wykorzystać informacje zawarte w LOGFONT... Wiemy bowiem, że aby wybrać czcionkę dla danego kontekstu, podajemu uchwyt do niej (typu HFONT). Na szczęście uzyskanie uchwytu na podstawie struktury LOGFONT nie jest to zbyt skomplikowane. Służy do tego funkcja CreateFontIndirect:

C/C++
HFONT hfnMyFont;
hfnMyFont = CreateFontIndirect( cfnt.lpLogFont );
if( hfnMyFont == NULL )
   
 MessageBox( hwnd, "Wredna czciona nie chce się utworzyć...", NULL, MB_ICONEXCLAMATION );

Pora na krótki przykład zastosowania. Załóżmy, że mamy EDIT-a o globalnym uchwycie g_hEdit, przycisk (o uchwycie g_hButton) do otwierania dialogu wyboru czcionki oraz globalny uchwyt g_hfnNowy typu HFONT (domyślnie ustawiony na NULL):

C/C++
case WM_COMMAND:
{
   
if(( HWND ) lParam != g_hButton ) break;
   
   
CHOOSEFONT cfnt;
   
LOGFONT lf;
   
ZeroMemory( & cfnt, sizeof( CHOOSEFONT ) );
   
cfnt.lStructSize = sizeof( CHOOSEFONT );
   
cfnt.hwndOwner = hwnd;
   
cfnt.Flags = CF_EFFECTS | CF_FORCEFONTEXIST | CF_SCREENFONTS;
   
cfnt.lpLogFont = & lf;
   
   
if( !ChooseFont( & cfnt ) )
   
{
       
DWORD dwErr = CommDlgExtendedError();
       
if( dwErr ) MessageBox( hwnd, "Straszliwy błąd dialogu!", NULL, MB_ICONEXCLAMATION );
       
   
}
   
else
   
{
       
if( g_hfnNowy != NULL )
           
 DeleteObject( g_hfnNowy );
       
       
g_hfnNowy = CreateFontIndirect( cfnt.lpLogFont );
       
if( hfnNowy != NULL )
           
 SendMessage( g_hEdit, WM_SETFONT,( WPARAM ) hfnNowy, MAKELPARAM( TRUE, 0 ) );
       
   
}
}
break;

case WM_DESTROY:
{
   
if( g_hfnNowy != NULL ) DeleteObject( g_hfnNowy );
   
   
PostQuitMessage( 0 );
}
break;

Jak łatwo się przekonać (a dla niektórych wystarczy nawet spojrzeć ;-)), powyższy kod sprawia, że naciśnięcie przycisku otwiera dialog wyboru czcionki, a w przypadku gdyby użytkownik był na tyle miły i wybrał jakąś, to czcionka ta zostanie ustawiona dla pola tekstowego.
 
Do ustawienia czcionki dla okna (tutaj dla pola tekstowego) posłużyliśmy się komunikatem WM_SETFONT. Jego parametry to: uchwyt czcionki oraz flaga oznaczająca, czy po zmianie czcionki okno (kontrolka) ma być odświeżone (u nas jak najbardziej TRUE).
Poprzedni dokument Następny dokument
Okna dialogowe, cz. 3 Okna dialogowe, cz. 5