/*
***************************************************************************
*
* 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 ( struct List *AL , BOOL last );
*    static void BuildActionEntry ( UBYTE *line , struct _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"

	/*
	 * 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_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, 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;
	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(struct _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;
	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 (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)
{
	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, 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 - 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;

                        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";
   extern char *_file, *_dir;

   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;
}
