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.