C dla każdego (cz. 10.)
Górne menu
Dziś zajmiemy się ważnym elementem Graficznego
Interfejsu Użytkownika, a mianowicie górnym menu.
"Mechanizm" górnego menu jest dość prosty. System
umożliwia posiadanie menu, składającego się z 31
głównych menu (opisanych Strukturą "Menu"), z
których każde może zawierać 63 pozycje (opisane
strukturą "Menultem"), a każda pozycja 31
podpozycji (tzw. subitems - opisanych również
strukturą "MenuItem"). To chyba sporo...
Programując w OS 1.3, należało utworzyć wspomniane
powyżej struktury i połączyć je ze sobą za pomocą
odpowiednich pól. Nie było to może szczególnie
trudne, ale wymagało tworzenia wielu struktur,
"ręcznego" rozplanowywania poszczególnych pozycji
itp. W OS 2.0 uproszczono to wręcz do granic
możliwości - w nowej bibliotece "gadtools.library"
pojawiła się bardzo miła funkcja:
struct Menu *CreateMenusA( struct NewMenu
*newmenu, struct Tagitem otaglist ) ;
struct Menu *CreateMenus ( struct.NęwMenu
*newmenu, Tag tagi, ... );
Funkcja ta, na podstawie podanych parametrów
tworzy struktury opisujące górne menu. Pierwszym
parametrem jest wskaźnik na tablicę struktur
"NewMenu", za pomocą których bardzo łatwo
definiuje się górne menu (tablica ta jest używana
tylko w momencie tworzenia menu - potem jest już
zbędna):
struct NewMenu
{
UBYTE nm_Type;
STRPTR nm_Label;
STRPTR nm_CommKey;
UWORD nm_Flags;
LONG nm_MutualExclude;
APTR nm_UserData;
}
Znaczenie pól jest następujące:
nm_Type - ustala, co ma zostać utworzone.
Najczęściej używane typy to NM_TITLE (tytuł menu -
odpowiednik struktury "Menu"), NMJTEM (pozycja w
menu) l NMJ3UB (podpozycja). Ważnym typem jest
NM_END, który oznacza koniec tablicy (to taki
odpowiednik taga TAGLDONE). Tworząc tablicę
struktur "NewMenu", w pierwszym jej elemencie pole
"nm_Type" powinno być równe NM_T1TLE - element ten
będzie oznaczał tytuł pierwszego menu z lewej.
Kolejne elementy tablicy powinny zawierać pozycje,
które mają się znaleˇć w tym menu (NMJTEM); za
każdą pozycją mogą wystąpić jej podpozycję
(NM_SUB). Kolejne menu tworzy się podając w
kolejnym elemencie w "nm_Type" znowu NM_TITLE itd.
nm_Label - napis, który ukaże się w danym
menu/pozycji/podpozycji. Dla pozycji i podpozycji
można tu podać NM_BARLABEL, co spowoduje, że
zamiast napisu pojawi się pozioma "belka" - jest
to dość elegancki sposób grupowania pozycji w
menu, wpływający korzystnie na przejrzystość. Mała
uwaga natury stylistycznej: zaleca się, aby
pozycje, których wybranie powoduje Otwarcie
jakiegoś okienka, miały napis zakończony trzema
kropkami (zobacz choćby menu Workbencha) - to
ułatwia użytkownikowi orientację. Napisy, których
adresy są odnotowane w tym polu, nie są przez
funkcja CreateMenusA() kopiowane do jakichś
przydzielonych buforów - funkcja kopiuje do
stworzonych przez siebie struktur tylko adres
napisu. Oznacza to, że napis ten musi istnieć
przynajmniej tak długo, jak długo istnieje
utworzone menu, a więc Zwykle nie może być np.
lokalną tablicą znakową (czas życia używanych
zwykle w menu sta- łych napisowych otoczonych
cudzysłowami jest równy czasowi życia całego
programu, więc zwykle nie ma problemów).
nm_CommKey - Jednoznakowy napis, ustalający skrót
z klawiatury, uzyskiwany, jak wiadomo, przez
naciśnięcie prawego klawisza "Amiga" i danej
litery.
nm_Flags - Flagi:
- NM_MENUDISABLED i NM_ITEMDISABLED - informuje,
że dane menu bądˇ pozycja/podpozycja ma być
wyłączona - "za mgiełką".
- CHECKIT - powoduje, że dana pozycja/podpozycja
będzie miała po lewej stronie miejsce na tzw.
checkmark - "ptaszek" czy "fajeczkę". Jeżeli
użytkownik wybierze daną pozycja, to pojawi się
przy niej "fajka". Ale przy ponownym wybraniu
danej pozycji "fajka" NIE zniknie! Flagi tej używa
się zwykle do pozycji wzajemnie się
wykluczających, o których za chwilę.
- CHECKED - używane w połączeniu z powyższym, gdy
flaga ta jest ustawiona, to "fajka" będzie od razu
wyświetlana przy danej pozycji.
- MENUTOGGLE - używany w połączeniu z dwoma
powyższymi - służy do tworzenia
pozycji-przełączników - przy pierwszym wybraniu
zachowuje się tak, jak sam CHECKIT, przy drugim
zaś "fajka" znika.
nm_MutualExclude - służy do tworzenia pozycji
wzajemnie wykluczających się (ang. mutually
exclusive). Pozycje, te tworzy się zwykle jako
CHECKIT, przy czym wybranie jednej z nich powoduje
pojawienie się przy niej "fajki" ORAZ zniknięcie
"fajki" przy poprzednio zaznaczonej pozycji. To
pole służy do ustalania, przy których pozycjach ma
zostać wyczyszczona "fajeczka", gdy ta pozycja
zostanie wybrana. Robi się to, podając maskę
bitową- należy ustawić bity o numerach
odpowiadających pozycjom, które chcemy
"odfajkowywać" (patrz listing). Wszystkie
wzajemnie wykluczające się pozycje muszą należeć
do jednego menu (lub, w wypadku podpozycji, do
jednego podmenu), numeruje się je od 0.
nm_UserData - To pole jest zostawione dla nas, do
wykorzystania tak, jak nam się żywnie podoba.
Drugim parametrem funkcji CreateMenusA() jest
znany nam już wskaźnik na tablicę tagów. Wśród
dostępnych tagów ale ma jednak żadnych istotnych.
Funkcja zwraca wskaźnik do struktury "Menu",
zawierającej opis pierwszego menu od lewej.
Pozostałe utworzone struktury są z nią połączone.
Jak zwykle funkcja może zwrócić NULL przy braku
pamięci (bądź gdy zrobisz jakiś błąd w tablicy
struktur "NewMenu").
Utworzone w ten sposób kompletne menu nie jest
jeszcze gotowe do dołączenia do okna - należy
ustalić rozmiar i współrzędne poszczególnych
pozycji. Zrobi to za nas funkcja z GadTools:
BOOL LayoutMenusA(struct Menu *firstmenu, APTR vi,
struct Tagitem *tagliat );
BOOL LayoutMenus(struct Menu *firstmenu, APTR vi,
Tag tagi, ... );
Znaczenie parametrów jest następujące:
firstmenu - podaje się wartość zwróconą przez
funkcję CreateMa- nusA().
vi - Wyjaśnimy za chwilę.
taglist - Lista tagów. Ważny jest dla nas tylko
tag:
- GTMN_NewLookMenus - pojawił się w OS 3.0 i
powoduje, że przy uruchomieniu programu pod tym
systemem menu będzie miało "nowy wygląd", tzn.
czarne litery na białym tle, a nie, jak w 0S 2.0,
szare na czarnym.
Funkcja zwraca TRUE, gdy Jej działanie zakończyło
się sukcesem, co zdarza się właściwie zawsze.
Co zaś należy podać jako parametr "vi"? Podaje się
wartość zwróconą przez funkcję:
APTR GetVisualInfoA( struct Screen *screen, struct
Tagitem *taglist ) ;
APTR GetVisualInfo( struct Screen *screen. Tag
tag1,... );
Parametry są chyba oczywiste - adres ekranu l
lista tagów (na razie nie ma żadnych tagów, więc
podaje ale po prostu NULL).
Mniej oczywista Jest zwracana wartość - nie można
właściwie powiedzieć, co to jest. Jest to, jak
twierdzą autorzy systemu, "wskaźnik na prywatne
dane". Oznacza to, że my - programiści - nie mamy
nic z nimi kombinować, tylko po prostu podawać
wskaźnik innym funkcjom, które go wymagają. Jest
to więc taka "czarna skrzynka" - spotkamy się z
nimi jeszcze nie raz, przy okazji innych funkcji i
bibliotek. Funkcja, jak zwykle, może zwrócić NULL
w wypadku niepowodzenia. Zwracany typ "APTR" jest
równoznaczny typowi "void*". Gdy wartość te nie
jest nam już potrzebna (np. po zamknięciu okna),
należy użyć funkcji: .
void FreevisualInfo( APTR vi );
Parametrem Jest wartość zwrócona przez
GetVisualInfoA(). Gdy górne menu nie jest nam Już
dłużej potrzebne, należy użyć funkcji:
void FreeMenus ( struct Menu *menu );
Parametrem jest wartość zwrócona przez
CreateMenusA(). Mamy już więc gotowe menu. Jak je
dołączyć do okna? Robi się to za pomocą funkcji
Intuition:
BOOL SetMenuScript struct Window *window, struct
Menu *menu );
Znaczenie parametrów jest chyba oczywiste: adres
okna, do które- go ma zostać dołączone menu i samo
menu.
Funkcja zwraca zawsze TRUE.
Przed zamknięciem okna należy ZAWSZE usunąć z
niego menu, używając funkcji:
void ClearMenuStrip(struct Window *window );
Parametrem jest adres okna, z którego ma zostać
usunięte menu.
Przy otwieraniu okna za pomocą OpenWIndowTagList()
warto podać tag:
(WA_NewLookMenus, TRUE) -
zapewni to poprawny wygląd menu w 0S 3.0+.
Należy też podać flagę IDCMP:
IDCMP_MENUPICK - Powoduje ona, że do UserPortu
naszego okna będą napływać informacje tej klasy po
tym, gdy użytkownik wybierze jakąś pozycję z menu
(lub użyje jej "skrótu z klawiatury"). Wiadomość
ta jest generowana tylko wtedy, gdy zostanie
poprawnie wybrana pozycja - np. samo naciśnięcie
prawego przycisku nie wystarczy. Identyfikator
pozycji zostanie odnotowany w polu "Code"
struktury "IntuiMessage". Można go stamtąd
wyłuskać przynajmniej na dwa sposoby:
MENUNUM(n)
ITEMNUM(n)
SUBNUM(n)
Są to makra preprocesora, zdefiniowane w pliku
"intuition/Intuition.h" - parametrem jest "Code",
zwracają numer menu, pozycji i podpozycji, lub
NOMENU, NOITEM, NOSUB, gdy nie ma którejś z nich.
Jeżeli więc np. użytkownik wybierze drugą pozycję
z trzeciego menu, przy czym pozycja ta nie ma
żadnych podpozycji, to MENUNUM() zwróci 2 (bo
pierwsze menu, pozycja S podpozycja mają numer 0),
1TEMNUM() zwróci 1, a SUBNUM() NOSUB.
Drugim sposobem jest użycie funkcji Intuition:
struct Menultem *ItemAddress(struct Menu
omenuStrip, unsigned long menuNumber );
Podaje się adres struktury menu (ten zwrócony
przez CreateMenusA()) oraz wartość pola "Code"
struktury "IntuiMessage".
Funkcja zwraca adres struktury "MenuItem",
odpowiadającej danemu kodowi.
Ciag dalszy za miesiąc.