/*
***************************************************************************
*
* Datei:
*    RSysAction.c
*
* Inhalt:
*    void BuildActionList(void);
*    int StartAction(int ftype, char *file, short last);
*
* Bemerkungen:
*    Actioning-Routinen von RSys. Wird ein Icon auf das AppIcon von
*    RSys gezogen, werden entsprechend des Dateityps Aktionen
*    gestartet.
*
* Erstellungsdatum:
*    07-Jan-93    Rolf Böhme
*
* Änderungen:
*    07-Jan-93    Rolf Böhme    Erstellung
*
***************************************************************************
*/

#include "RSysDebug.h"
#include "RSysFunc.h"

   /*
    * Der Zeiger auf alle Actions, die aus der Datei
    * sys.act eingelesen werden.
    */
struct _acts *Actions;

   /*
    * Die Liste aller gelesenen Aktionen.
    */
struct List ActionList,
      DisplayList;
   /*
    * Ein globaler Zähler für die eingelesenen Aktionen.
    */
int   counter;
   /*
    * Liste aller Aktionen zu einem Dateityp.
    */
struct List DL;

   /*
    * Die Intuition-Objekte für den Aufbau des
    * "Auswahl-Fensters der Aktionen".
    */
static struct Window *ActionWnd = NULL;
static struct Gadget *ActionGList = NULL;
static struct Gadget *ActionGadgets[4];
static UWORD ActionLeft = 211;
static UWORD ActionTop = 43;
static UWORD ActionWidth = 149;
static UWORD ActionHeight;
static UBYTE ActionWdt[14] = (UBYTE *) "Action";
 
   /*
    * Die Funktion OpenActionWindow() öffnet ein Fenster
    * mit einem ListView und zwei Gadgets. Im ListView
    * werden die Aktionen zu einem erkannten Dateitypen
    * angezeigt. Werden mehrere Dateien behandelt, so
    * erscheint ein weiteres Gadget, mit dem man zur
    * nächsten Datei wechseln kann.
    */
static long
OpenActionWindow(struct List *AL, BOOL last)
{
   struct NewGadget ng;
   struct Gadget *g;
   UWORD wleft = ActionLeft,
         wtop = ActionTop,
         ww,
         wh;
   int gl[] = {GD_FileTypeTGad,GD_ActionLV};

      /*
       * Die Höhe des Fensters wird entsprechend der
       * Gadgetanzahl bestimmt. Ist last TRUE, gibt es nur
       * drei Gadgets, sonst vier.
       */
   ActionHeight = (last ? 92 : 108);

      /*
       * Der Font wird für das neue Fenster festgelegt. Ist
       * der Font zu groß, wird automatisch topaz 8
       * genommen.
       */
   ComputeFont(Scr,ActionWidth, ActionHeight);

      /*
       * Die Höhe und Weite des Fensters wird fontsensitiv
       * bestimmt.
       */
   ww = compute((UWORD) 0, FontX, (int)ActionWidth);
   wh = compute((UWORD) 0, FontY, (int)ActionHeight);

   if ((wleft + ww + OffX + Scr->WBorRight) > Scr->Width)
      wleft = Scr->Width - ww;
   if ((wtop + wh + OffY + Scr->WBorBottom) > Scr->Height)
      wtop = Scr->Height - wh;

   ww = compute((UWORD) (OffX + Scr->WBorRight), FontX, (int)ActionWidth);
   wh = compute((UWORD) (OffY + Scr->WBorBottom), FontY, (int)ActionHeight);

      /*
       * Das Fenster wird zentriert auf den aktiven Screen
       * gebracht.
       */
   CenterWindow(Scr, &wtop, &wleft, ww, wh);

      /*
       * Der Gadtools-Kontext dr Benutzeroberfläche wird
       * erzeugt.
       */
   if (NOT(g = CreateContext(&ActionGList)))
      return (1L);

      /*
       * Der Reihe nach werden alle Gadgets des Fensters
       * erzeugt und in den Kontext eingebunden. Dabei
       * werden pro Gadget nur die Werte verändert, die
       * sich gegenüber dem vorhergehenden gändert haben.
       */
   ng.ng_LeftEdge = compute(OffX, FontX, 4);
   ng.ng_TopEdge = compute(OffY, FontY, 1);
   ng.ng_Width = compute((UWORD) 0, FontX, 141);
   ng.ng_Height = compute((UWORD) 0, FontY, 15);
   ng.ng_GadgetText = NULL;
   ng.ng_TextAttr = Font;
   ng.ng_GadgetID = GD_FileTypeTGad;
   ng.ng_Flags = 0;
   ng.ng_VisualInfo = VisualInfo;

   g = CreateGadget(TEXT_KIND, g, &ng, GTTX_Text, NULL, GTTX_Border, TRUE, TAG_DONE);

   ActionGadgets[0] = g;

   ng.ng_TopEdge = compute(OffY, FontY, 17);
   ng.ng_Height = compute((UWORD) 0, FontY, 60);
   ng.ng_GadgetID = GD_ActionLV;

   g = CreateGadget(LISTVIEW_KIND, g, &ng, GTLV_Labels, AL, TAG_DONE);

   ActionGadgets[1] = g;

   ng.ng_TopEdge = compute(OffY, FontY, 78);
   ng.ng_Height = compute((UWORD) 0, FontY, 13);
   ng.ng_GadgetText = (UBYTE *) "_Info";
   ng.ng_GadgetID = GD_FInfo;
   ng.ng_Flags = PLACETEXT_IN;

   g = CreateGadget(BUTTON_KIND, g, &ng, GT_Underscore, '_', TAG_DONE);

   ActionGadgets[2] = g;

      /*
       * Falls es sich nicht um die letzte Datei handelt,
       * wird ein weiteres Gadget eingebunden.
       */
   if (NOT(last))
   {
      ng.ng_TopEdge = compute(OffY, FontY, 92);
      ng.ng_GadgetText = (UBYTE *) "Next Entry";
      ng.ng_GadgetID = GD_NextEntryGad;

      g = CreateGadget(BUTTON_KIND, g, &ng, TAG_DONE);

      ActionGadgets[3] = g;
   }

   if (NOT g)
      return (2L);

      /*
       * Das Fenster wird mit den entsprechenden
       * Attributen versehen geöffnet.
       */
   if (NOT(ActionWnd = OpenWindowTags(NULL,
                                      WA_Left, wleft,
                                      WA_Top, wtop,
                                      WA_Width, ww,
                                      WA_Height, wh,
                                      WA_IDCMP, TEXTIDCMP | LISTVIEWIDCMP |
                                      IDCMP_MOUSEBUTTONS | IDCMP_MOUSEMOVE |
                                      IDCMP_CLOSEWINDOW |
                                      IDCMP_REFRESHWINDOW | VANILLAKEY,
                                      WA_Flags, WFLG_DRAGBAR | WFLG_DEPTHGADGET |
                                      WFLG_CLOSEGADGET | WFLG_SMART_REFRESH |
                                      WFLG_RMBTRAP | WFLG_ACTIVATE,
                                      WA_Gadgets, ActionGList,
                                      WA_Title, ActionWdt,
                                      WA_PubScreenName, (UBYTE *)"Workbench",
                                      TAG_DONE)))
      return (4L);

   RefreshRastPort(ActionWnd,ActionGadgets,gl, 2);

   if(Flags.autofront)
   {
      WindowToFront(ActionWnd);
      ActivateWindow(ActionWnd);
   }


   return (NULL);
}
 
   /*
    * Diese Procedure erzeugt ein Listenelement in der
    * Aktionsliste. Dazu wird eine eingelesene Zeile mit
    * der C-Library-Funktion strtok() in einzelne Teile
    * zerlegt, die durch ein '#' getrennt sind.
    */
static void
BuildActionEntry(UBYTE * line, struct _acts *Act)
{
      /*
       * Die Token für den weiteren Duchlauf
       * initialisieren.
       */
   char *token = strtok((char *)line, ACTION_TOKEN);

      /*
       * Der Listenknoten wird gemäß der C=-Richtlinien
       * initialisiert.
       */
   Act->act_Node.ln_Pri = 0;
   Act->act_Node.ln_Type = NT_USER;
   Act->act_Node.ln_Name = Act->act_name;

      /*
       * Das  erste  Token  enthält  die Dateitypnummer des
       * entsprechenden Eintrags, die auch der intern
       * verwendeten Nummer entspricht.
       */
   Act->act_filetype = atoi(token);

      /*
       * Dieses Token enthält den Dateityp als String, der
       * in dem Textgadget über dem ListView erscheint.
       */
   token = strtok(STRINGEND, ACTION_TOKEN);
   strcpy(Act->act_listviewheader, token);

      /*
       * Dieser Token enthält die Aktionsbezeichnung, die
       * in dem ListView angezeigt wird.
       */
   token = strtok(STRINGEND, ACTION_TOKEN);
   strcpy(Act->act_name, token);

      /*
       * Dieser Token enthält die Aktion, die nach der
       * Anwahl des entsprechenden ListView-Eintrags
       * ausgeführt wird.
       */
   token = strtok(STRINGEND, ACTION_TOKEN);
   strcpy(Act->act_command, token);

      /*
       * Das Prioritätsfeld des Knotens in der
       * Actions-Struktur mißbrauchen wir als Flag, das
       * festlegt, ob nach dieser Aktion das Action-Fenster
       * geschlossen werden soll oder nicht.
       */
   token = strtok(STRINGEND, ACTION_TOKEN);
   Act->act_Node.ln_Pri = (*token == 'W') ? 1 : 0;

   return;
}

   /*
    * Die Funktion BuildActionList() holt aus der
    * Environmentvariablen SYSACTION die
    * Bezeichnung der Datei, die die Aktionen für die
    * einzelnen Dateitypen enthält und baut aus den
    * Inhalten dieser Datei eine Liste auf.
    */
void
BuildActionList(void)
{
   BPTR  actfile;
   UBYTE actfilename[MAXFULLNAME+1];
   int   i = 0;
   UBYTE line[MAXLINESIZE+1],
        *buf;

   DPOS;

      /*
       * Falls bereits zuvor die Datei gelesen und eine
       * Liste aufgebaut wurde, wird der Speicher jetzt
       * freigegeben und der Zeiger initialisiert.
       */
   MyFreeVec(Actions);
   Actions = NULL;

      /*
       * Die Environmentvariable SYSACTION wird gelesen und
       * die Datei geöffnet, falls die Variable korrekt
       * ausgelesen werden konnte.
       */
   if (GetVar((UBYTE *) "RSYSACTION", actfilename, MAXFULLNAME, GVF_GLOBAL_ONLY) > 0)
      if (actfile = Open(actfilename, MODE_OLDFILE))
      {
            /*
             * Der erste Eintrag in der Datei enthält die Anzahl
             * der eingetragenen Aktionen.
             */
         FGets(actfile, line, MAXLINESIZE);
         counter = atoi((char *)line);
 
            /*
             * Falls der Eintrag keine gültige Zahl war, lieferte
             * atoi() den Wert 0 zurück.  In diesem Falle wird
             * das Unterprogramm mit dem Schließen der Datei und
             * einer Fehlermeldung abgebrochen.
             */
         if (!counter)
         {
            Close(actfile);
            ErrorHandle(FILE_ERR, WRONG_FAIL, NO_KILL);
            return;
         }
 
            /*
             * Für die Liste wird entsprechend der Anzahl der
             * Einträge Speicherplatz besorgt.
             */
         Actions = MyAllocVec(counter * sizeof(struct _acts),
                              MEMF_ANY | MEMF_CLEAR, NO_KILL);
 
         if (Actions)
         {
               /*
                * Die Action-Liste wird aktualisiert.
                */
            NewList(&ActionList);
 
               /*
                * Die erste Zeile wird gelesen.
                */
            buf = FGets(actfile, line, MAXLINESIZE);

               /*
                * In dieser Zeile werden alle Aktionen eingelesen
                * und die Liste aufgebaut.
                */
            for (i = 0; (i < counter) && buf; i++)
            {
               BuildActionEntry(line, &Actions[i]);
 
               AddTail(&ActionList, &Actions[i].act_Node);
               buf = FGets(actfile, line, MAXLINESIZE);
            }
 
               /*
                * Falls ein Fehler beim Lesen auftrat oder weniger
                * Zeilen in der Datei waren, als counter angibt,
                * wird eine Fehlermeldung erzeugt.
                */
            if (!buf && IoErr())
               ErrorHandle(FILE_ERR, READ_FAIL, NO_KILL);
         }
 
            /*
             * Die Datei wird nach dem Auslesen geschlossen.
             */
         Close(actfile);
 
         return;
      }
      else
         ErrorHandle(FILE_ERR, FIND_FAIL, NO_KILL);
   else
      ErrorHandle(ENV_ERR, FIND_FAIL, NO_KILL);

   return;
}
 
   /*
    * Die Prozedur ShowInfo() zeigt entsprechend des
    * Dateityps Directory, Device oder File eine Info
    * in Form eines Systemrequesters.
    */
static void
ShowInfo(char *file)
{
   APTR  req;
   struct objectid obj;
 
      /*
       * Das Actionwindow wird mit einem unsichtbaren
       * Requester gegen Eingaben geschützt, während die
       * Anzeige aktiv ist.
       */
   req = LockWindow(ActionWnd);

      /*
       * Entsprechend des letzten Zeichens der Datei wird
       * entschieden, ob es sich um ein Device, ein
       * Verzeichnis oder eine Datei handelt.
       */
   switch (file[strlen(file) - 1])
   {
         /*
          * Die Datei ist eine Disk. Auf Wunsch wird eine
          * Verzeichnisstruktur dieser Disk gezeigt.
          */
      case ':':
         if (NOT(DisplayDiskInfo(file, TRUE)))
         {
            strcpy(obj.fullname, file);
            PrintTree(&obj);
         }
         break;
 
         /*
          * Die Datei ist ein Verzeichnis.  Auf Wunsch wird
          * eine Verzeichnisstruktur diese Verzeichnisses
          * gezeigt.
          */
      case '/':
         DisplayDirectoryInfo(file);
         break;
 
         /*
          * Ist jedem anderen Fall wird eine allgemeine
          * Fileinfo angezeigt. Die Routine PrintHunkStruct()
          * entscheidet außerdem, ob es ein Executable ist und
          * bietet entsprechend die Möglichkeit an, ein
          * Hunklisting zu erzeugen.
          */
      default:
         if (NOT(DisplayFileInfo(file)))
            PrintHunkStruct(file);
         break;
   }
 
      /*
       * Das Actionwindow wird wieder freigegeben.
       */
   UnlockWindow(req);

   return;
}
 
   /*
    * Die Funktion StartAction() baut eine Teilliste für
    * den entsprechenden Dateitypen auf und präsentiert
    * ein Fenster mit drei oder vier Gadgets. Wenn last
    * TRUE ist, handelt es sich um die letzte oder nur
    * eine Datei. Andernfalls wurden mehrere Gadgets
    * über das AppIcon von RSys gezogen und es erscheint
    * ein viertes Gadget "Next".
    */
int
StartAction(int ftype, char *file, BOOL last)
{
   register struct IntuiMessage *message;
   ULONG class,
         code;
   APTR  object;
   ULONG mask;
   struct Node *node = NULL;
   struct _acts *act = NULL;
   int   GID,
         cnt = 0,
         j,
         ret = TRUE,
         new;
   UBYTE *basename = FilePart((UBYTE *) file);
   UBYTE *dirname = PathPart((UBYTE *) file);

   DPOS;

   Flags.quit_action_flag = 0;
      /*
       * Initialisierung der Anzeigeliste.
       */
   NewList(&DL);

      /*
       * Die Actions, die dem Filetypen entsprechen, werden
       * gezählt.
       */
   for (j = 0; j < counter; j++)
      if (Actions[j].act_filetype == ftype)
         cnt++;

      /*
       * Falls Actionen gefunden wurden...
       */
   if(cnt)
   {
         /*
          * ...wird entsprechend Speicher besorgt.
          */
      act = MyAllocVec((cnt+1) * sizeof(struct _acts),
                       MEMF_CLEAR | MEMF_ANY, NO_KILL);

         /*
          * Das Unterprogramm wird verlassen, falls nicht
          * genügend Speicher reserviert werden konnte.
          */
      if (NOT(act))
         return (FALSE);

         /*
          * Der Zähler für die Teilliste wird initialisiert.
          */
      new = 0;

         /*
          * Die neue Displayliste wird aufgebaut und die
          * Einträge gezählt.
          */
      for (j = 0; (j < counter) && (new < cnt); j++)
         if (Actions[j].act_filetype == ftype)
         {
            CopyMem((APTR) & Actions[j], (APTR) & act[new], sizeof(struct _acts));
            AddTail(&DL, &act[new++].act_Node);
         }
   }

         /*
       * Falls die Datei kein Verzeichnis ist, wird für den
       * Windowtitle der Basisname der Datei gebildet, also
       * Filename ohne Pfad.
       */
   if (ftype != TYPE_DIR)
      strncpy((char *)ActionWdt, (char *)basename, 13);
   else
   {
         /*
          * Falls es ein Verzeichnis ist, werden maximal 13
          * Zeichen des Strings für die Titelzeile kopiert.
          */
      int   dirlen = MIN(13, ((char *)dirname - (char *)file));

      strncpy((char *)ActionWdt, (char *)file, dirlen);

      ActionWdt[dirlen] = STRINGEND;
   }

      /*
       * Die Bildschirmattribute werden bestimmt und das
       * Actionwindow geöffnet.
       */
   if (!SetupScreen() && !OpenActionWindow(&DL, last))
   {
         /*
          * Der Dateityp wird in das Textgadget eingetragen.
          */
      GT_SetGadgetAttrs(ActionGadgets[GD_FileTypeTGad], ActionWnd,
                        NULL,
                        GTTX_Text, mess[ftype],
                        TAG_DONE);

      do
      {
            /*
             * Es wird auf ein ^C oder ein Signal vom
             * Actionwindow gewartet.
             */
         mask = Wait(SIGBREAKF_CTRL_C |
                     (1L << ActionWnd->UserPort->mp_SigBit));

            /*
             * Falls ein ^C erkannt wurde, wird das Actioning
             * beendet.
             */
         if (mask & SIGBREAKF_CTRL_C)
            Flags.quit_action_flag = 1;

            /*
             * Es ist eine Nachricht am Userport des
             * Actionwindows eingetroffen.
             */
         if (mask & (1L << ActionWnd->UserPort->mp_SigBit))
            while ((message = (struct IntuiMessage *)
                    GT_GetIMsg(ActionWnd->UserPort)) != NULL)
            {
                  /*
                   * Die notwendigen Daten werden aus der
                   * Message-Struktur kopiert.
                   */
               object = message->IAddress;
               class = message->Class;
               code = message->Code;

                  /*
                   * Damit Intuition nicht wartet, wird die Nachricht
                   * sofort beantwortet.
                   */
               GT_ReplyIMsg(message);

               switch(class)
               {
                     /*
                      * Falls ein angeklicktes Gadget losgelassen wurde,
                      * wird entsprechend der GadgetID eine Aktion
                      * gestartet.
                      */
                  case IDCMP_GADGETUP:
                        GID = ((struct Gadget *)object)->GadgetID;
                        switch(GID)
                        {
                           /*
                            * Falls es sich nicht um die letzte Datei handelt,
                            * wird das Unterprogramm beendet, wenn das
                            * "Next"-Gadget geklickt wird.
                            */
                        case GD_NextEntryGad:
                           if (NOT(last))
                           {
                              Flags.quit_action_flag = 1;
                              node = NULL;
                           }
                           break;

                           /*
                            * Falls das "Info"-Gadget geklickt wurde, wird eine
                            * Info in Form eines Systemrequesters angezeigt.
                            * Falls es zu diesem Dateityp keine Aktionen gibt,
                            * die Liste DL also leer ist, wird das Unterprogramm
                            * beendet. Zuvor wird dann der MessagePort des
                            * ActionWindows geleert.
                            */
                        case GD_FInfo:
                           ShowInfo(file);
                           node = NULL;

                           if(IsListEmpty(&DL))
                           {
                              ClearIntuiMsgPort(ActionWnd);
                              Flags.quit_action_flag = 1;
                           }
                           break;

                           /*
                            * Wenn das ListView angeklickt wurde, wird der
                            * angeklickte Eintrag bestimmt, das assozierte
                            * Action command ausgeführt und das Unterprogramm
                            * zum Beenden vorbereitet, falls nach Aktion das
                            * Fenster geschlossen werden soll.
                            */
                        case GD_ActionLV:
                           if (node = GetNode(&DL, code))
                              Command(node, file);

                           if(node->ln_Pri == 0)
                           {
                              ClearIntuiMsgPort(ActionWnd);
                              Flags.quit_action_flag = 1;
                           }
                           else
                              node = NULL;
                           break;
                     }
                     break;

                     /*
                      * Falls eine Taste betätigt wurde, wird diese
                      * ermittelt und verarbeitet.
                      */
                  case IDCMP_VANILLAKEY:
                     switch(code)
                     {
                           /*
                            * Diese Tasten entsprechen dem Anklicken des
                            * "Info"-Gadgets.
                            */
                        case 'i':
                        case 'I':
                           ShowInfo(file);
                           node = NULL;
                           break;

                           /*
                            * Diese Tasten entsprechen dem Anklicken des
                            * Closegadgets des Action windows..
                            */
                        case '\33':
                           Flags.quit_action_flag = 1;
                           ret = FALSE;
                           node = NULL;
                           break;
                     }
                     break;

                     /*
                      * Das Fenster wird zum Beenden des Unterprogrammes
                      * vorbereitet.
                      */
                  case IDCMP_CLOSEWINDOW:
                     Flags.quit_action_flag = 1;
                     ret = FALSE;
                     node = NULL;
                     break;
               }
            }
      }
      while (NOT(Flags.quit_action_flag));

         /*
          * Das Action window wird geschlossen und der
          * PublicScreen freigegeben.
          */
      CloseASysWindow(&ActionWnd, &ActionGList, NULL);
      CloseDownScreen();
   }
   else
      ErrorHandle(WINDOW_ERR, OPEN_FAIL, NO_KILL);

      /*
       * Der Speicher, der für die Liste der Aktionen
       * angelegt wurde, wird freigegeben.
       */
   MyFreeVec(act);

   return (ret);
}

