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.