/*
   ***************************************************************************
   *
   * Datei:
   *      RSysAction.c
   *
   * Inhalt:
   *
   *      --- Globale Routinen ---
   *
   *    int StartAction ( int ftype , char *file , BOOL last );
   *    void BuildActionList ( void );
   *    void DisplayFileRequest ( void );
   *
   *      --- Lokale  Routinen ---
   *
   *    static long OpenActionWindow ( LIST *AL , BOOL last );
   *    static void BuildActionEntry ( UBYTE *line , RSYS_acts *Act );
   *    static void ShowInfo ( char *file );
   *
   * 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 "RSys.h"
#include "protos.h"


 /*
  * Die Intuition-Objekte für den Aufbau des
  * "Auswahl-Fensters der Aktionen".
  */
static WINDOW *ActionWnd = NULL;
static GADGET *ActionGList = NULL;
static GADGET *ActionGadgets[4];
static UWORD ActionLeft = 211;
static UWORD ActionTop = 43;
static UWORD ActionWidth = 149;
static UWORD ActionHeight;
static UBYTE ActionWdt[14] = "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 (LIST * AL, BOOL last)
{
  NEWGADGET ng;
  GADGET *g;
  UWORD wleft = ActionLeft, wtop = ActionTop, ww, wh;
  int gl[] =
  {GD_FileTypeTGad - GD_FileTypeTGad,
   GD_ActionLV - GD_FileTypeTGad};

  ActionHeight = (last ? 92 : 108);

  AdjustWindowDimensions (Scr, ActionLeft, ActionTop, ActionWidth, ActionHeight,
			  &wleft, &wtop, &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_Title, ActionWdt,
				    WA_PubScreenName, (UBYTE *) "Workbench",
				       TAG_DONE)))
    return 4L;

  RefreshRastPort (ActionWnd, ActionGadgets, gl, 2, FALSE, ActionGList);

  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, RSYS_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;
  int i = 0;
  UBYTE *line, *buf;

  HandleHelp (MN_ReloadActionFile);

  DPOS;

  if (!ChooseNewFile ((char *) RSysFiles.ActionFile, "Select new Action file..."))
    return;

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

  if ((line = MyAllocVec ((MAXLINESIZE + 1) * sizeof (UBYTE), MEMF_CLEAR, NO_KILL)) &&
      (actfile = Open (RSysFiles.ActionFile, 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);
	  MyFreeVec (line);
	  ErrorHandle ((char *) RSysFiles.ActionFile, FILE_ERR, WRONG_FAIL, NO_KILL);
	  return;
	}

      /*
       * Für die Liste wird entsprechend der Anzahl der
       * Einträge Speicherplatz besorgt.
       */
      Actions = MyAllocVec (counter * sizeof (RSYS_acts), 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 ((char *) RSysFiles.ActionFile, FILE_ERR, READ_FAIL, NO_KILL);
	}

      /*
       * Die Datei wird nach dem Auslesen geschlossen.
       */
      Close (actfile);

      MyFreeVec (line);

      return;
    }

  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;
  RSYS_Objid 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 (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 (filetype (file) == TYPE_EXECUTABLE)
	{
	  if (DisplayFileInfo (file))
	    PrintHunkStruct (file);
	}
      else
	DisplayFileInfo (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)
{
  INTUIMESSAGE *message;
  ULONG class, code;
  APTR object;
  ULONG mask;
  NODE *node = NULL;
  RSYS_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 (RSYS_acts), MEMF_CLEAR, 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 (RSYS_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 - 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 = (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 = ((GADGET *) object)->GadgetID;

		    HandleHelp ((enum RSysNumbers) GID);

		    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':
			HandleHelp ((enum RSysNumbers) GD_FInfo);

			ShowInfo (file);
			node = NULL;
			break;

			/*
			 * Diese Tasten entsprechen dem Anklicken des
			 * Closegadgets des Action windows..
			 */
		      case ESC:
			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 ("SetupScreen()", WINDOW_ERR, OPEN_FAIL, NO_KILL);

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

  return ret;
}

void
DisplayFileRequest (void)
{
  static char dir[MAXFULLNAME] = "RAM:", file[MAXFULLNAME] = "unknown";

  DisableSysRequest (TRUE);

  if (GetFile (SysWnd, dir, file, "#?", "Choose File for ClipBoard...", "Clip it"))
    {
      strncpy (file, (char *) FilePart ((STRPTR) _fullpath), MAXFULLNAME);
      strncpy (dir, (char *) _fullpath, (int) ((ULONG) PathPart ((STRPTR) _fullpath) - (ULONG) _fullpath));

      SaveListToClip (NULL, _fullpath);
    }

  DisableSysRequest (FALSE);

  return;
}
