4. Hintergrund: Das Handbuch - diesmal online (von Michael Christoph)

Wurde im letzten Teil besonderes Augenmerk auf das gedruckte Handbuch und 
dessen Anforderungen gelegt, soll diesmal eine weitere Art der Anleitung 
genauer unter die Lupe genommen werden: die Online-Hilfe. Dieser Teil zeigt 
zwar im besonderen die Programmierumsetzung, liefert aber auch für jeden 
Interessierten etwas.

Unter der Online-Hilfe versteht man die Hilfe, die direkt vom Programm aus 
aufgerufen werden kann. Sie sollte ebenso Standard sein, wie ein normales 
Handbuch (egal ob gedruckt oder digital) und auch die im letzten Teil 
angesprochenen Punkte erfüllen.

Die Hilfe sollte idealerweise sofort zur Textstelle verzweigen, an der die 
Informationen zum aktuellen Kontext zu finden sind. Nur dann ist diese Art 
der Hilfe überhaupt sinnvoll. Sowohl AmigaGuide als auch HTML unterstützen 
das gezielte Anzeigen einer Passage und sollen daher nachfolgend genauer 
betrachtet werden. Mittels des HTML-Datatypes ist außerdem kein extra 
Browser notwendig (auch wenn seit AmigaOS 3.5 AWeb bereits im 
Betriebssystem vorhanden ist). Solange HTML nur nach seiner ursprünglichen 
Konzeption eingesetzt wird, ist dieser Datatype völlig ausreichend.

Man kann die normale Anleitung so gestalten, dass sie direkt im Programm 
auch als Hilfe eingebunden werden kann, oder man erstellt eine zusätzliche
Datei. Wichtig dabei ist nur, dass sofort der Text zum aktuellen Kontext
angezeigt wird. Diese Hilfe kann man beispielsweise grob auf die einzelnen 
Fenster gliedern oder sehr fein auf die einzelnen Gadgets.

Wenden wir uns daher dem Aufbau der Online-Hilfe zu.

Im AmigaGuide-Format sollte jedes Kapitel und auch die Unterkapitel in 
einem eigenen Node vorliegen. Und so wie das Inhaltsverzeichnis auf jeden 
Node verweist, kann auch von jeder anderen Stelle im Text darauf verwiesen 
werden. Der Node-Verweis kann noch mittels einer optionalen Zeilennummer 
genauer innerhalb des Kapitels platziert werden.

AmigaGuide - Vorteil : Amiga-eigenes Format, Datatype-Unterstützung
             Nachteil: keine direkte Bildereinbindung,
                       geringe Layoutmöglichkeiten

Bei HTML gliedert man allgemein nur die oberen Kapitel in einzelne Dateien. 
In diesem Fall ist es hilfreich, wenn zu Beginn der HTML-Datei die 
einzelnen Unterkapitel aufgezählt werden, so dass durch einen einfachen 
Klick dorthin gesprungen werden kann. Natürlich ist das Inhaltsverzeichnis 
mit allen Kapiteln zu verwenden, wobei die Unterkapitel direkt an die 
entsprechenden Stellen in der HTML-Datei zeigen sollten.

HTML - Vorteil : direkte Bilderunterstützung, Layoutmöglichkeiten
       Nachteil: extra Browser oder HTML-Datatype notwendig

Besonders wichtig ist auch eine Indexseite, auf der die wichtigsten 
Begriffe rund um das Programm aufgeführt sind und die direkt auf die
entsprechenden Zeilen in den Beschreibungstexten verweisen.

Da es allerdings auch Konvertierungsprogramme gibt, die AmigaGuide-
Anleitungen in eine HTML-Datei umwandeln können, spricht nichts dagegen, 
beide Arten von Online-Hilfe mitzuliefern. Dadurch kann der Leser
selbst entscheiden, welche Art der Hilfe er bevorzugt.

---------------------------------------------------------------------------

Ein Grundgerüst für eine AmigaGuide-Datei hat den folgenden Aufbau:

/@DATABASE "Anleitung.guide"
/@INDEX "INDEX"

/@NODE "MAIN" "Inhaltsverzeichnis"
/@{"Kapitel 1"     LINK "Kapitel1"}
/@{"  Kapitel 1.1" LINK "Kapitel1_1"}
/@{"  Kapitel 1.2" LINK "Kapitel1_2"}
/@ENDNODE

/@NODE "Kapitel1" "Kapitel 1"
Einleitungstext ...
/@ENDNODE

/@NODE "Kapitel1_1" "Kapitel 1.1"
Text ...
/@ENDNODE

/@NODE "Kapitel1_2" "Kapitel 1.2"
Text ...
/@ENDNODE

/@NODE "INDEX" "Index"
/@{"Schlüsselwort" LINK "Verweisziel"}
/@ENDNODE

---------------------------------------------------------------------------

Ein Grundgerüst für eine HTML-Datei hat den folgenden Aufbau:

<HTML>
<HEAD>
<TITLE>Anleitung zu ...</TITLE>
</HEAD>
<BODY>
<P>
<H1>Kapitel 1</H1>
<UL>
  <LI><A HREF="#kapitel1_1">Kapitel 1.1</A></LI>
  <LI><A HREF="#kapitel1_2">Kapitel 1.2</A></LI>
</UL>
Einleitungstext ...
</P>

<P>
<A NAME="kapitel1_1"><H1>Kapitel 1.1</H1></A>
Text ...
</P>

<P>
<A NAME="kapitel1_2"><H1>Kapitel 1.2</H1></A>
Text ...
</P>
</BODY>
</HTML>

---------------------------------------------------------------------------

Die einfachste Art, die Anleitung anzuzeigen, ist der direkte Aufruf von
MultiView. Das nachfolgende Beispiel zeigt die vollständige Funktion,
die sehr flexibel ist. So wird neben MultiView auch die ältere Ausgabe
AmigaGuide gesucht, und es wird auch überprüft, ob die Anleitung im 
aktuellen oder im System-Hilfe-Verzeichnis vorhanden ist. Im Fehlerfall 
wird ein entsprechender Text ausgegeben (z.B. mittels EasyRequest() in 
einer normalen GUI-Anwendung). Ansonsten wird der gefundene Anzeiger 
mittels Execute() (oder alternativ mittels SystemTagList()) gestartet.
Das Programm hat keine Kontrolle über die Anzeige, läuft aber parallel zum 
Anzeigefenster weiter, da es mittels "run" als Hintergrundprozess gestartet 
wird. Allerdings fehlt auch die Möglichkeit, sofort eine bestimmte Sektion 
der Datei anzuzeigen. Daher eignet sich diese Art der Hilfe eher zum 
Anzeigen des normalen Handbuches.


BOOL ZeigeAnleitung(const STRPTR guide)
{
  BOOL res = FALSE;

  UBYTE kommand[80] = "";
  UBYTE guidefile[50];
  BPTR lock;

  /* prüfen, ob die Anleitung vorhanden ist */
  strcpy(guidefile,guide);
  if((lock = Lock(guidefile,SHARED_LOCK)))
  {
    UnLock(lock);
  }
  else
  {
    sprintf(guidefile,"LOCALE:Help/%s",guide);
    if((lock = Lock(guidefile,SHARED_LOCK)))
    {
      UnLock(lock);
    }
    else guidefile[0]='\0';
  }

  if(guidefile[0])
  {
    /* prüfen, ob ein Anzeigeprogramm vorhanden ist */
    if((lock = Lock("SYS:Utilities/MultiView",SHARED_LOCK)))
    {
      UnLock(lock);
      sprintf(kommand,"RUN >NIL: SYS:Utilities/MultiView %s",guidefile);
    }
    else if((lock = Lock("SYS:Utilities/AmigaGuide",SHARED_LOCK)))
    {
      UnLock(lock);
      sprintf(kommand,"RUN >NIL: SYS:Utilities/AmigaGuide %s",guidefile);
    }
 
    if(kommand[0])
    {
      Execute(kommand,NULL,NULL);
      res = TRUE;
    }
    else printf("Fehler\n"
                "Guide-Anzeiger sind nicht vorhanden;\n"
                "SYS:Utilities/MultiView 
oder\nSYS:Utilities/AmigaGuide.\n");
  }
  else printf("Fehler\n"
              "Anleitung '%s'\n"
              "ist nicht (im aktuellen Verzeichnis) vorhanden.\n",guide);

  return( res );
}

---------------------------------------------------------------------------

Das nächste Beispiel zeigt die Benutzung der amigaguide.library, wie sie 
seit AmigaOS 2.x existiert. Dabei wird nur die einfache, blockierende 
Anwendung vorgestellt. Das heißt, die Hilfe muss erst geschlossen werden, 
damit das Programm fortgesetzt wird. Man hat allerdings genaue Kontrolle 
über die Anzeige. Über den nichtblockierenden Aufruf OpenAmigaGuideAsync()
kann das Programm ganz normal parallel zur Hilfe weiterlaufen. Es besteht 
dann jederzeit die Möglichkeit, die Hilfe wieder nach vorn zu holen oder 
eine andere Stelle anzuzeigen. Über den Eintrag nag_Node der NewAmigaGuide-
Struktur kann direkt die anzuzeigende Seite (Default Main) vorgegeben 
werden.


struct Library *AmigaGuideBase;

BOOL ZeigeAnleitung(const STRPTR guide)
{
  BOOL res = FALSE;
  struct NewAmigaGuide nag = { NULL };
  AMIGAGUIDECONTEXT handle;
  BPTR lock;

  if((AmigaGuideBase = OpenLibrary("amigaguide.library",33)))
  {
    if((lock = Lock((UBYTE*)guide,SHARED_LOCK)))
    {
      UnLock(lock);
 
      if((handle = OpenAmigaGuide(&nag,NULL)))
      {
        /* synchrone Verarbeitung; */
        /* kehrt erst nach Beenden von AmigaGuide zurück */

        CloseAmigaGuide(handle);
        res = TRUE;
      }
      else printf("Anzeige durch AmigaGuide fehlgeschlagen.\n");
    }
    else printf("Guide-Datei '%s' fehlt.\n",guide);

    CloseLibrary(AmigaGuideBase);
  }
  else printf("amigaguide.library V33 fehlt.\n");

  return( res );
}

---------------------------------------------------------------------------

Mittels NewDTObject() kann jeder Amiga-Programmierer die Hilfe direkt per 
Datatype in sein Programm einbinden. Dabei ist man vom Format her am 
flexibelsten, da lediglich ein passender Datatype vorhanden sein muss. 
Allerdings hat man auch hierbei wieder keine Möglichkeit, direkt eine 
Stelle in der Anleitung anzuzeigen. 
Das folgende Gerüst zeigt, wie der Datentyp ermittelt werden kann und die 
Datei eingelesen wird. Die Weiterverarbeitung als Gadget wird allerdings 
nicht mehr ausgeführt. Diese Art ist wieder nichtblockierend.


struct Library *DataTypesBase;

BOOL ZeigeAnleitung(const STRPTR guide)
{
  BOOL res = FALSE;
  struct DataType       *dtn;
  struct DataTypeHeader *dth;
  Object                *obj;
  BPTR                   lock;

  if((DataTypesBase = OpenLibrary("datatypes.library",39)))
  {
 
    /* existiert die angegebene Datei überhaupt */
    if((lock = Lock((UBYTE*)guide,SHARED_LOCK)))
    {
      /* Dateityp ermitteln */
      if((dtn = ObtainDataTypeA(DTST_FILE,(APTR)lock,NULL)))
      {
        dth = dtn->dtn_Header;

        if(dth->dth_GroupID == GID_DOCUMENT ||  /* = AmigaGuide */
           dth->dth_GroupID == GID_TEXT)        /* = HTML */
        {
          res = TRUE;
        }
        else printf("Falscher Dateityp der Anleitung.\n");

        ReleaseDataType(dtn);
      }


      if(res)
      {
        /* Datei einlesen */
        if((obj = NewDTObject((APTR) guide,
                               DTA_SourceType, DTST_FILE,
                               /* DTA_GroupID, GID_xxx, */
                               /* wenn ein bestimmter Typ gefordert wird */
                               TAG_DONE)))
        {
          /* ... Datatype-Objekt mittels Gadget zur Anzeige verwenden */

          DisposeDTObject(obj);
        }
        else printf("Kein passender Datatype zur Anzeige vorhanden.\n");
      }

      UnLock(lock);
    }
    else printf("Anleitungsdatei '%s' fehlt.\n",guide);

    CloseLibrary(DataTypesBase);
  }
  else printf("datatype.library V39 fehlt.\n");

  return( res );
}

---------------------------------------------------------------------------

Eine ganz andere Richtung soll noch kurz angesprochen werden.
Seit OS 3.0 (V39) unterstützen die Boopsi-Gadgets die GM_HELPTEST-Methode, 
um dem Programm mitzuteilen, zu welchem Gadget der Benutzer eine Hilfe 
wünscht. Window-bezogen sind die Tags WA_HelpGroup und WA_HelpGroupWindow
dafür zuständig, dass eine Nachricht der Art IDCMP_GADGETHELP versandt 
wird. Wie allerdings der fromme Wunsch des Benutzers nach einer Hilfe vom
Programm letztendlich umgesetzt wird, dafür hatte Commodore keine Lösung 
mehr parat. Hier drei sehr einfache Möglichkeiten:

- eine separate Zeile im Fenster reservieren, in der der Hilfetext
  angezeigt wird (z.B. mittels Text() oder besser in einem String- oder
  ReadOnly-Button-Gadget, das sich dann auch um das Clipping kümmert)

- die Screenzeile benutzen, um den Hilfetext anzuzeigen

- einen EasyRequester mit dem Hilfetext anzeigen

In allen drei Fällen kann in Verbindung mit einem Timer die Anzeige
auch zeitlich beschränkt werden (BuildEasyRequest() + FreeSysRequest()
im dritten Fall).

Diese Art eignet sich natürlich besonders gut, um eine Kurzhilfe zum 
aktuellen Gadget oder Fenster anzuzeigen. Allerdings ist es wirklich nur 
eine Kurzhilfe, da platzbedingt meist sehr wenig Raum zur Verfügung steht. 
Des weiteren muss diese Hilfe direkt im Programm eingebaut werden oder 
besser in einer externen Catalog-Datei untergebracht werden.

Alle Varianten haben ihre Vor- und Nachteile. So setzt die Datatypes-
Methode voraus, dass das Programm unter OS 3.x läuft, was allerdings heute 
wohl der Standard sein dürfte. Die Execute-Methode ist zwar sehr einfach 
und robust, erlaubt allerdings keine Kontrolle über die Anzeige und eignet 
sich eher zum Anzeigen der normalen Anleitung.

Letztendlich muss der Programmierer anhand der Zielgruppe und OS-Version 
abschätzen, welche Lösung die für seinen Fall passendste ist.

Mit diesen Worten soll die kleine dreiteilige Geschichte zum Handbuch
abgeschlossen werden.

Michael Christoph <michael@meicky-soft.de>