C dla każdego (cz. 17.)

Gadżety (5)

Omówimy teraz pokrótce przedstawiony w poprzedniej części listing.

Zacznijmy od funkcji obsluzokno().

Widzimy w niej klasyczną pętlę interakcyjnej komunikacji z użytkownikiem. Większość klas wiadomości już znamy, pojawia się jednak jedna, z którą dotychczas się nie zetknęliśmy:

IDCMP_REFRESHWINDOW -- wiadomości tej klasy są generowane, gdy niezbędne jest "odrysowanie" jakiegoś fragmentu okna. Standardowe okna są typu WFLG_SMARTREFRESH. Oznacza to, że konieczność ich "odrysowania" zachodzi tylko po zmianie rozmiaru. Jeżeli przy otwieraniu okna podamy flagę WFLG_SIMPLEREFRESH, to otrzymamy okno, które nie będzie "pamiętało" swych zasłoniętych w danym momencie przez inne okna fragmentów -- ma ono mniejsze wymagania pamięciowe. W naszym wypadku wiadomoci te przybędą po użyciu gadżetu skokowej zmiany wielkości. Obsługa polega na wywołaniu po sobie dwóch funkcji biblioteki GadTools:

void GT_BeginRefresh( struct Window *win );
void GT_EndRefresh( struct Window *win, long complete );

Jako "win" podajemy adres okna, jako "complete" po prostu TRUE. Funkcje te powodują "odrysowanie" gadżetów. Gdybymy mieli w oknie jakie własne obrazki czy napisy, to należałoby je "odrysować" pomiędzy GT_BeginRefresh() i GT_EndRefresh() -- funkcje te powodują chwilowe zwiększenie prędkoci operacji graficznych.

Czas wreszcie omówić strukturę "ObslugaGadzetu", odniesienia do której pojawiały się we wcześniejszych fragmentach listingu. Struktura ta upraszcza obsługę gadżetów. Definiujemy tablicę takich struktur -- "tabobgad" -- po jednej dla każdego gadżetu w oknie. W pole "og_Func" wstawiamy adres funkcji, która ma zostać wywołana, gdy przybędzie wiadomoć od danego gadżetu. Pola "og_Obecnie" i "og_Maksimum" są używane tylko w gadżetach przechowujących jaką informację liczbową, np. numer zaznaczonej pozycji w gadżecie-liście. W "og_Obecnie" odnotowana jest bieżąca wartość tej liczby, wartość ta może należeć do przedziału od 0 do "og_Maksimum".

Pewnie już zapomnieliście (nie zdziwilibyśmy się...), że adres struktury "ObslugaGadzetu" dla każdego gadżetu odnotowaliśmy w funkcji stworzgadzety() w polu "ng_UserData" struktur "NewGadget", a więc tym samym w polu "UserData" struktury "Gadget".

Gdy w funkcji obsluzokno() otrzymujemy wiadomoć od jakiego gadżetu, w polu "IAddress" struktury "IntuiMessage" jest odnotowany adres tego gadżetu. Zaglądając dalej w pole "UserData" struktury "Gadget" otrzymujemy informację o adresie struktury "ObslugaGadzetu" dla danego gadżetu. Mając tę informację, w polu "og_Obecnie" odnotowujemy wartość pola "Code" przekazanej struktury "IntuiMessage". To właśnie w polu "Code" system przekazuje nam nową "wartość liczbową" gadżetu, np. numer pozycji zaznaczonej w gadżecie-liście, numer wybranej pozycji w gadżecie "MX_KIND" itd. Zwróćcie uwagę, że np. dla gadżetu "BUTTON_KIND" pole "Code" nie zawiera żadnej informacji, jako że ten gadżet nie odwzorowuje żadnej wartoci numerycznej -- jest to zwykły przycisk. Dla takich gadżetów wykonanie opisanej powyżej operacji kopiowania jest więc zbędne, ale ponieważ nie jest szkodliwe, to nie zawracamy sobie tym głowy. Ostatnią operacją wykonywaną przy obsłudze gadżetu jest wywołanie specyficznej dla każdego gadżetu funkcji, odnotowanej w polu "og_Func".

Przed dalszym zagłębieniem się w listing musimy omówić jeszcze jedną funkcję biblioteki GadTools:

void GT_SetGadgetAttrsA( struct Gadget *gad, struct Window *win, struct Requester *req, struct TagItem *taglist );
void GT_SetGadgetAttrs( struct Gadget *gad, struct Window *win, struct Requester *req, Tag tag1, ... );

Funkcja ta służy do zmiany atrybutów już utworzonego, wyświetlanego w oknie, gadżetu. Jako "gad" podajemy adres gadżetu, którego atrybut chcemy zmienić (wstawiamy wartoć zwróconą przez CreateGadgetA() -- odnotowalimy ją w tablicy "tabgad"). Jako "win" podajemy adres okna, zawierającego gadżet, jako "req" podajemy 0, a następnie tagi i nowe atrybuty. Używamy tych samych tagów, co w CreateGadgetA(), tyle że nie wszystkie są dozwolone. Z wymienionych w poprzednich częściach tagów używać można: GA_Disabled, GTCB_Checked, GTST_String, GTIN_Number, GTTX_Text, GTNM_Number, GTLV_Labels, GTLV_Selected, GTLV_Top, GTLV_MakeVisible, GTCY_Labels, GTCY_Active, GTMX_Active, GTPA_Color, GTSC_Total, GTSC_Visible, GTSC_Top, GTSL_Min, GTSL_Max, GTSL_Level i GTSL_LevelFormat (uff...).

Omówmy pokrótce funkcje zdefiniowane dla poszczególnych gadżetów.

Kliknięcie na gadżecie-przycisku powoduje po prostu wypisanie informacji o tym w oknie Shella.

Gadżet-fajeczka ustala, czy gadżet-przycisk jest włączony, czy wyłączony (za mgiełką). To, czy gadżet-fajeczka jest "zafajeczkowany" poznajemy po obecności flagi "GFLG_SELECTED" w polu "Flags" struktury "Gadget".

Gadżety do wprowadzania napisu i liczby powodują przekopiowanie napisu do gadżetów wyświetlających napis i liczbę (TEXT_KIND, NUMBER_KIND). Informacja o obecnym stanie tych gadżetów jest zapisana w strukturze "StringInfo", której adres jest zapisany w polu "SpecialInfo" struktury "Gadget". Dla gadżetu liczbowego obecna wartoć jest zapisana w polu "LongInt" struktury "StringInfo", dla gadżetu tekstowego w polu "Buffer" zapisany jest adres bufora zawierającego napis. Zauważ, że dla tego ostatniego gadżetu najpierw kopiujemy tekst do pomocniczego bufora i dopiero adres tego bufora podajemy funkcji GT_SetGadgetAttrs(). Jest to niezbędne, gdyż funkcja GT_SetGadgetAttrs() nie powoduje przekopiowania tekstu do jakiego prywatnego bufora w gadżecie -- przekazywany jest tylko wskaźnik na obszar pamięci zawierający tekst, który nie może się zmienić.

Tymczasem bufor z pola "Buffer" struktury "StringInfo" zmienia się w czasie edycji tekstu w gadżecie. Nasz pomocniczy bufor zadeklarowaliśmy jako "static", aby nie zniknął po wyjściu z funkcji stringfunc() -- jego czas życia jest taki, jak obiektów globalnych, jest jednak widoczny tylko lokalnie. Zwróć również uwagę, że wiadomość IDCMP_GADGETUP od gadżetów tekstowych i liczbowych przybywa po ZAKOŃCZENIU edycji, a więc po naciśnięciu klawisza RETURN bądź TAB. Wiadomoć NIE przybędzie, gdy uaktywnimy gadżet, co w nim zmienimy, a następnie np. klikniemy na innym gadżecie.

Gadżety "kręciołek" i "wzajemnie wykluczający" sprzęgnęliśmy ze sobą -- zmiana w jednym z nich powoduje automatyczną zmianę w drugim. W wypadku "kręciołka" zakładanie, że po każdym kliknięciu wyświetlana jest następna pozycja w tablicy, jest NIELEGALNE. Po pierwsze klikając z wciśniętym klawiszem [Shift] powodujemy "cofanie" się gadżetu, po drugie z użyciem programu "CycleToMenu" autorstwa Federigo Gianniciego (gorąco polecam) użytkownik może wybrać od razu dowolną pozycję.

Kliknięcie na gadżecie-liście powoduje wypisanie w oknie Shella wybranej pozycji.

Bardziej skomplikowane operacje wykonujemy, gdy użytkownik nacinie RETURN/TAB w gadżecie tekstowym pod gadżetem-listą. Naszym celem jest zmiana obecnie zaznaczonej pozycji. W tym celu musimy najpierw odłączyć listę od gadżetu, podając jako wartoć tagu "GTLV_Labels" -1. Następnie przekopiowujemy tekst z bufora gadżetu tekstowego do obecnie wybranej pozycji w liście, po czym z powrotem przyłączamy listę do gadżetu, co spowoduje narysowanie zmienionej pozycji.

Gadżet-paleta powoduje ustawienie gadżetu-suwaka na bieżącą wartoć składowej "Red" wybranego koloru. Korzystamy w tym celu z omówionej wcześniej funkcji getrgb().

Zmiana pozycji gadżetu-suwaka powoduje natychmiastową zmianę nasycenia składowej "Red" koloru wybranego w gadżecie-palecie. W tym celu najpierw pobieramy informację o bieżącym nasyceniu składowych Green i Blue, a następnie wywołujemy omówione w części 5. funkcje SetRGB4/SetRGB32 z nową wartością Red i nie zmienionymi Green i Blue. Nawiasem mówiąc, to taka "bezczelna" zmiana nasycenia koloru na publicznym ekranie nie jest zupełnie legalna...

Niestety, wbrew wcześniejszym przewidywaniom nie udało nam się dziś zakończyć obsługi gadżetów. Za miesiąc omówimy dzisiejszy, ostatni już, fragment listingu oraz zabierzemy się do czegoś zupełnie nowego.