C dla każdego (cz. 24.)
Pomocy! (2)
Dziś zajmiemy się najważniejszym mechanizmem pomocy:
biblioteką "amigaguide.library". Dokończymy też
rozpoczęty przed miesiącem listing.
Biblioteka "amigaguide.library" pojawiła się pierwszy
raz w wersji 3.0 (39) systemu operacyjnego. Udostępniona
została jednak również wersja 34 tej biblioteki,
pracująca pod systemami 1.3 i 2.04. Biblioteka przeszła
również poważną metamorfozę w wersji 3.1 (40) systemu
operacyjnego.
Można by się więc cieszyć, że biblioteka jest dostępna
pod wszystkimi systemami i że była aktywnie rozwijana.
Problem jednak w tym, Że każda z tych wersji jest inna!
Najprymitywniejsza z nich- wersja 34 - nie oferuje
żadnych specjalnych wodotrysków. Ot - tekst i gadżety. W
wersji 39 dodano możliwość zmiany atrybutów tekstu -
czcionek, rozmiaru, koloru itd. Pojawiły się również
pewne mechanizmy automatycznego formatowania paragrafów.
Wersja 40, najbardziej rozbudowana, umożliwia przede
wszystkim jeszcze lepszą kontrolę nad automatycznym
formatowaniem tekstu. W wersji tej zmieniono jednak
znaczenie znaku "\". W poprzednich wersjach znak ten
miał specjalne znaczenie tylko przed niektórymi znakami,
jak np. '@'. W wersji 40 zmieniono to i np. aby na
ekranie zostało wyświetlone "\n", w dokumentacji trzeba
napisać "\\n". To z kolei spowoduje, że w wersjach 34 i
39 na ekranie pojawi się właśnie "\\n". Co więc robić?
Dwie (a może i trzy) osobne wersje dokumentacji? A może
jakieś automatyczne konwersje podczas instalacji?
Horror!
Żeby na tym jednak problemy się kończyły... Pomoc tego
typu powinna być dostępna na żądanie i asynchronicznie,
a więc zarówno biblioteka, jak i dokumentacja powinny
być otwierane dopiero przy pierwszym naciśnięciu
przycisku "Help", a otwarte okno z pomocą nie powinno
blokować aplikacji. Tymczasem w dokumentacji systemu
znajdziemy przykłady, które blokują aplikację lub ładują
ją zaraz po uruchomieniu programu. Najbardziej oczywista
implementacja została po prostu przemilczana. Skandal!
No, ale skończmy biadolenie i weźmy się do pracy. W
naszym listingu pomoc AmigaGuide jest wywoływana, gdy
użytkownik zażąda pomocy dla menu (IDCMP_MENUHELP), dla
aktywnego gadżetu tekstowego (IDCMP_GADGETUP z "Code"
równym Ox5F) oraz gdy naciśnie przycisk "Help" w
dowolnej innej sytuacji (IDCMP_RAWKEY z "Code" równym
Ox5F). W tym ostatnim przypadku korzystamy z informacji
udzielonych nam wcześniej przez IDCMP_GADGETHELP i
zapamiętanych w zmiennej "ostatnigad".
We wszystkich tych przypadkach wywoływana jest funkcja
"duzapomoc()", której parametrem jest liczbowy
identyfikator rozdziału w dokumentacji.
Funkcja ta najpierw inicjuje pomoc AmigaGuide. Polega to
na otwarciu biblioteki "amigaguide.library", a następnie
wywołaniu funkcji:
APTR OpenAmigaGuideAsyncA( struct NewAmigaGuide *nag,
sfcruct Tagitem *atbrs ) ;
APTR OpenAmigaGuideAsync (struct NewAmigaGuide *nag, Tag
Tag1, ... ) ;
Funkcja otwiera dokument AmigaGuide, zwracając "czarną
skrzynkę" - adres na prywatną strukturę, lub NULL w
przypadku niepowodzenia. Tagów nie podajemy zwykle
żadnych, bo nie ma specjalnie z czego wybierać. Parametr
"nag" to wskaźnik na zainicjowaną strukturę
"NewAmigaGuide", która jest zdefiniowana w pliku
"libraries/amigaguide.h". Zawiera ona wiele pól, z
których najważniejsze to:
- nag_Name: nazwa dokumentu z pomocą AmigaGuide.
- nag_Screen i nag_PubScreen: odpowiednio: adres ekranu
lub jego nazwa - na nim zostanie otwarte okno pomocy.
- nag_Context: jest to jeden ze sposobów sterowania
pomocą. W tym wypadku, podajemy tablicę wskaźników na
napisy będące nazwami rozdziałów dokumentacji. Dzięki
temu nie trzeba póˇniej podawać napisów, a tylko indeksy
w tej tablicy.
Po zakończonym sukcesem wywołaniu funkcji, należy pobrać
informację o porcie, przez który pomoc komunikuje się z
naszą aplikacją:
ULONG AmigaGuideSignal( APTR cl );
Parametrem jest wartość zwrócona przez OpenAmigaGuideAO,
a funkcja zwraca maskę bitową sygnału portu.
Teraz następuje "gwóźdź programu", a więc to, czego
nigdzie nie opisano. Niby pomoc mamy już otwartą, ale
okazuje się, że nie można się z nią jeszcze komunikować,
bo nie została zakończona jej inicjalizacja. Przy pomocy
funkcji Wait() czekamy więc, aż w porcie pojawią się
jakieś wiadomości. Następnie wywołujemy funkcję
"obsluzagmsg()", która wiadomości obsługuje.
Wiadomości od AmigaGuide pobieramy i zwracamy przy
użyciu funkcji:
struct AmigaGuideMsg *GetAmigaGuideMsg( APTR cl ) ;
void RepiyAmigaGuideMsg ( struci AmigaGuideMsg *amsg ) ;
Parametrem pierwszej z nich jest wartość zwrócona przez
OpenAmigaGuideAsyncAO. Struktura "AmigaGuideMsg" jest
zdefiniowana w pliku "libraries/amigaguide. h" i jest
rozszerzoną wersją struktury "Message". Najważniejszym
polem tej struktury jest "agm_Type", które identyfikuje
typ przybyłej wiadomości. Interesujące nas typy to:
- ActiveToolID: oznacza, że inicjalizacja pomocy
zakończyła się i można się już z nią komunikować.
- ToolCmdRepłylD, ToolStatusID, ShutdownMsgID: gdy
przybędzie któryś z nich, sprawdzamy czy nie wystąpił
błąd, co poznajemy po niezerowej wartości pola
"agm_Pri_Ret". Słowny opis błędu uzyskujemy wywołując
funkcję:
STRPTR GetAmigaGuideString( long id );
Jako parametr podajemy wartość pola "agm_Sec_Ret". Po
tych wszystkich machinacjach możemy wreszcie
poinformować pomoc, który rozdział nas interesuje:
LONG SefcAmigaGuideContextA( APTR cl, unsigned long id,
struct TagItem *attrs );
LONG SetAmigaGuideContext( APTR cl, unsigned long id,
Tag tag1, ... );
Jako "cl" podajemy wartość zwróconą
przez OpenAmigaGuideAsyncA(), "id" to identyfikator
rozdziału, a tagów póki co żadnych nie ma. Funkcja
zwraca TRUE, jeżeli operacja się powiodła.
Wywołanie powyższej funkcji nie powoduje jednak otwarcia
okna na ekranie ani ustawienia właściwego rozdziału. Aby
to osiągnąć, musimy jeszcze wywołać funkcję:
LONG SendAmigaGuideContextA( APTR cl, struct TagItem
*attrs );
LONG SendAmigaGuideContext ( APTR cl, Tag tag1, ... );
Jako pierwszy parametr podajemy wartość zwróconą przez
OpenAmigaGuideAsyncA(), tagów nie ma na razie żadnych.
Jak zwykle, przy wychodzeniu z programu należy jeszcze
pomoc zamknąć. Służy do tego funkcja:
void CloseAmigaGuide( APTR cl );
Nie chce nam się kolejny raz powtarzać, co jest jej
parametrem...
I to w zasadzie wszystko, jeżeli chodzi o typowe
wykorzystanie "amigaguide.library". Potrzebna jest nam
jeszcze oczywiście sama dokumentacja w tym standardzie.
Ze względów oszczędnościowych zamieszczamy tylko jej
makietę. Temat tworzenia dokumentacji AmigaGuide był już
omawiany w Magazynie AMIGA 11/94 przez Wojciecha Wyparta
- "Przewodnik po AmigaGuide".
Fragmenty zaprezentowanego sposobu obsługi systemu
AmigaGuide bazują na znajdującym się na Aminecie w
katalogu dev/c archiwum "simpleguidel.fha", autorstwa
Pettera Nilsena. Pragniemy Jemu w tym momencie
podziękować za zgodę na ich wykorzystanie.
@database "Pomoc.guide"
@node MAIN
Główne okno:
@("Wpisz" link BUTTON_GAD]
@ ["Gadżet tekstowy" link STRING.GAD}
@("Ramka z tekstem" link TEXT_GAD}
@ ( "Górne menu" link MENU}
@endnode
@node BUTTON_GAD
Tu miejsce na opis.
@endnode
@node STRING_GAD
@endnode
@node TEXT_GAD
@endnode
@node MENU
Górne menu:
@{"Menu Projekt" link PROJEKT_MENU}
@endnode
@node PROJEKT.MENU
Menu Projekt:
@{"Otwórz..." link OTWORZ_ITEM}
@{"Zapisz" link ZAPISZ_ITEM}
@{"Zapisz jako..." link ZAPISZ.JAKO_ITEM}
@endnode
@node OTWORZ_ITEM
@endnode
@node ZAPISZ_ITEM
@endnode
@node ZAPISZ_JAKO_ITEM
@endnode
/* Wstaw poniższe linie w MIEJSCE l (zawartość funkcji
obsluzagmsg()). */
int bylblad=0 ;
struct AmigaGuideMsg *msg;
while (msg-GetAmigaGuideMsg(aginfo))
{
switch (msg->agm_Type)
{
case ToolCmdReplyID:
case ToolStatusID:
case ShutdownMsgID:
if (msg->agm_Pri_Ret)
{
printf("Blad obslugi Online help!\n%sl\n", GetAmigaGuideString(msg->agm_Sec_Ret));
bylblad=l;
}
break
case ActiveToolID:
if (aktywny)
*aktywny=1 ;
}
ReplyAmigaGuideMsg(msg) ;
}
return bylblad;
/* Wstaw ponizsze linie w MIEJSCE 2 (miedzy obsluzagmsg
() i main () ) */
void duzapomoc(int node} {
static const char* nazwy[]=
{
"MAIN", "BUTTON_GAD". "STRING_GAD", "TEXT_GAD". "MENU" ,
"PROJEKT_MENU", "OTWORZ_ITEM", "ZAPISZ_ITEM",
"ZAPISZ_JAKO_ITEM"
);
if (!aginfo)
{
int aktywny=0;
static struct NewAmigaGuide nag;
if (!AmigaGuideBase)
if (!(AmigaGuideBase=OpenLibrary("amigaguide.library", v34+!\n");
return
{
printf("Online help wymaga biblioteki "amigaguide.library\" v34+'\n") ;
return
}
nag.nag_Name="Pomoc.guide" ;
nag.nag_Screen=pubscr;
nag.nag_Context=(STRPTR*)nazwy;
if (!(aginfo=OpenAmigaGuideAsyncA(&nag, 0)))
{
printf("Blad podczas inicjowania online help!\n");
return;
}
agsig=AmigaGuideSignal(aginfo) ;
Wait (agsig);
while (!aktywny)
if (obsluzagmsg(&aktywny)}
{
CloseAmigaGuide(aginfo) ;
aginfo=0 ;
return;
}
}
SetAmigaGuideContext(aginfo, node, NULL) ;
SendAmigaGuideContext(aginfo, NULL) ;
}
/* Wstaw ponizsze linie w MIEJSCE 3 (w instrukcji
switch, po case IDCMP_GADGETHELP). */
Case IDCMP_MENUHELP:
(
int node=POMOC_MENU;
struct Menultem *item=ItemAddress(menu, kopia_msg.Code);
if (item && GTMENUITEM_USERDATA(item))
node=(int)GTMENUITEM_USERDATA(item) ;
else
if (MENUNUM(kopia_msg.Code)==0)
node=POMOC_PROJEKT;
duzapomoc(node) ;
break;
}
case IDCMP_RAWKEY:
if (kopia_msg.Code==Ox5F) /* Help */
duzapomoc(ostatni gad ? ( (struct
PomocDoGadzetu*)ostatnigad->UserData)->pdg"Node : POMOC_MAIN) ;
break;
case IDCMP_GADGETUP:
if (kopia.msg.IAddress==tabgad[STRING_ID]&&kopia_msg.Code==Ox5F)
duzapomoc(((struct
PomocDoGadzetu*)tabgad[STRING_ID]->UserData)->pdg_Node);
break;