/*
**	ProgressWin.c
**
**	Copyright (C) 1993,94,95,96 Bernardo Innocenti
**
**	Parts of this code are:
**
**	Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
**
**	Report status information for an operation in progress.
*/

#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <libraries/asl.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/graphics.h>
#include <proto/layers.h>

#include "XModulePriv.h"
#include "Gui.h"



/* Gadgets IDs */
enum
{
	GD_ProgressGroup0,
		GD_Action,
		GD_Percent,
		GD_ProgressAbort,

	Progress_CNT
};

enum
{
	GD_LogGroup0,
		GD_LogList,

	Log_CNT
};


/* Local functions prototypes */

static void ShowStats				(struct Gadget *Gadget, LONG Value, LONG Max);
static void ProgressAbortClicked	(void);
static void LogPostClose			(void);
static LONG ProgressPreOpen			(struct WinUserData *wud);



static UWORD			 LogLines = 0;
static UWORD			 ProgressOpenCount = 0;
struct List				 LogList;
static struct IntuiText	 ProgressIT = { 0 };
static BOOL				 ProgressAborting = FALSE;



static LONG ProgressArgs[] =
{
	TEXT_KIND,		NULL,						20,							GTTX_Clipped,	TRUE, TAG_DONE,
	TEXT_KIND,		NULL,						20,							GTTX_Border,	TRUE, TAG_DONE,
	BUTTON_KIND,	(LONG)ProgressAbortClicked,	MSG_UNDERSCORE_ABORT_GAD,	TAG_DONE,
	ENDGROUP_KIND
};



static LONG LogArgs[] =
{
	LISTVIEW_KIND,	NULL,	NULL, (LONG)&LogList,	GTLV_ReadOnly, TRUE, TAG_DONE,
	ENDGROUP_KIND
};




LONG ProgressWinTags[] =
{
	XMWIN_LayoutArgs,		(LONG)ProgressArgs,
	XMWIN_GCount,			Progress_CNT,
	XMWIN_Title,			MSG_PROGRESS_TITLE,
	XMWIN_IDCMPFlags,		BUTTONIDCMP|TEXTIDCMP|IDCMP_REFRESHWINDOW,
	XMWIN_PostCloseFunc,	(LONG)LogPostClose,
	TAG_DONE
};



LONG LogWinTags[] =
{
	XMWIN_LayoutArgs,		(LONG)LogArgs,
	XMWIN_GCount,			Log_CNT,
	XMWIN_Title,			MSG_LOG_TITLE,
	XMWIN_WindowFlags,		WFLG_CLOSEGADGET,
	XMWIN_IDCMPFlags,		LISTVIEWIDCMP|IDCMP_REFRESHWINDOW|IDCMP_CLOSEWINDOW,
	XMWIN_PreOpenFunc,		(LONG)ProgressPreOpen,
	TAG_DONE
};



GLOBALCALL void OpenProgressWindow (void)
{
	ProgressOpenCount++;
	if (ProgressOpenCount > 1) return;

	LockWindows();

	NewWindow (WID_PROGRESS);
}



GLOBALCALL void CloseProgressWindow (void)
{
	ProgressOpenCount--;
	if (ProgressOpenCount) return;

	UnlockWindows ();

	MyCloseWindow (WDescr[WID_PROGRESS].Wud);
}



static LONG ProgressPreOpen (struct WinUserData *wud)
{
	ProgressIT.FrontPen	= DrawInfo->dri_Pens[FILLTEXTPEN] ? DrawInfo->dri_Pens[FILLTEXTPEN] : DrawInfo->dri_Pens[FILLPEN];

	ProgressIT.DrawMode = (DrawInfo->dri_Pens[FILLPEN] == DrawInfo->dri_Pens[FILLTEXTPEN] || !DrawInfo->dri_Pens[FILLTEXTPEN]) ?
		(JAM1 | COMPLEMENT) : (JAM1);

	ProgressIT.ITextFont = wud->Attr;

	return RETURN_OK;
}



static void LogPostClose (void)
{
	/* Free ListView nodes */
	while (!IsListEmpty(&LogList))
		RemListViewNode (LogList.lh_Head);
	LogLines = 0;
}



GLOBALCALL void DisplayAction (ULONG msg)

/* Tell user what is happening in the Progress window. */
{
	DisplayActionStr (STR(msg));
}



GLOBALCALL void DisplayActionStr (CONST_STRPTR str)

/* Tell user what is happening in the Progress window. */
{
	struct WinUserData *wud;

	if ((wud = WDescr[WID_PROGRESS].Wud) && wud->Win)
		GT_SetGadgetAttrs (wud->Gadgets[GD_Action], wud->Win, NULL,
			GTTX_Text, str,
			TAG_DONE);
}



/* Tell user how are things going.  Also check for abort */
GLOBALCALL LONG DisplayProgress (LONG Num, LONG Max)
{
	struct WinUserData *wud;

	if ((wud = WDescr[WID_PROGRESS].Wud) && wud->Win)
	{
		/* Check for CTRL-C Break */
		if (SetSignal (0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
			return ERROR_BREAK;


		/* Check abort and resizing */
		HandleIDCMP();

		if (ProgressAborting)
		{
			ProgressAborting = FALSE;
			return ERROR_BREAK;
		}

		if (wud->Win)
		{
			struct Layer_Info	*layerinfo;
			UBYTE buf[48];

			/* Attempt to lock the LayerInfo associated with the
			 * screen where the Progress window resides in.
			 *
			 * We do this to prevent blocking the operation in progress
			 * when someone else (e.g. Intuition) is keeping the lock.
			 *
			 * Note that there is no AttemptLockLayerInfo() function
			 * and LockLayerInfo() will wait if the layer is already
			 * locked, which is exactly what we are trying to avoid.
			 *
			 * Our workaround is checking the LockLayersCount before
			 * locking the LayerInfo.
			 *
			 * TODO: Experiment with AttemptLockLayerRom().
			 */

			/* Perhaps using Scr->LayerInfo would be the same */
			layerinfo = wud->Win->RPort->Layer->LayerInfo;

			Forbid();
			if (!layerinfo->LockLayersCount)
			{
				struct Gadget *g;

				LockLayerInfo (layerinfo);
				Permit();

				g = wud->Gadgets[GD_Percent];

				/* Update Stats */
				ShowStats (g, Num, Max);

				/* Display progress string */

				SPrintf (buf, STR(MSG_PERCENT_DONE), Num, Max, (Num * 100) / Max);

				ProgressIT.IText = buf;

				PrintIText (wud->Win->RPort, &ProgressIT,
					g->LeftEdge + 2 + (g->Width - 4 - IntuiTextLength (&ProgressIT)) / 2,
					g->TopEdge + 1 + (g->Height - 2 - wud->Win->RPort->TxHeight) / 2);

				UnlockLayerInfo (layerinfo);
			}
			else Permit();
		}
	}

	return FALSE;
}



static void ShowStats (struct Gadget *Gadget, LONG Value, LONG Max)

/* Show the percentage bars. */
{
	struct RastPort	*RPort = WDescr[WID_PROGRESS].Wud->Win->RPort;
	LONG	MaxWidth = Gadget->Width - 4,
			Width;


	if (Max < 1)		Max = 0;
	if (Value > Max)	Value = Max;


	if((Width = (MaxWidth * Value) / Max) > 0)
	{
		if(Width != MaxWidth)
		{
			SetAPen (RPort,0);
			RectFill (RPort, Gadget->LeftEdge + 2 + Width - 1, Gadget->TopEdge + 1,
				Gadget->LeftEdge + Gadget->Width - 3, Gadget->TopEdge + Gadget->Height - 2);
		}

		SetAPen (RPort, DrawInfo->dri_Pens[FILLPEN]);
		RectFill (RPort,Gadget->LeftEdge + 2,Gadget->TopEdge + 1,
			Gadget->LeftEdge + Width + 1, Gadget->TopEdge + Gadget->Height - 2);
	}
	else
	{
		SetAPen (RPort, 0);
		RectFill (RPort, Gadget->LeftEdge + 2, Gadget->TopEdge + 1,
			Gadget->LeftEdge + Gadget->Width - 3, Gadget->TopEdge + Gadget->Height - 2);
	}
}




GLOBALCALL void ShowMessage (ULONG msg, ...)

/* Localized interface to ShowString(). */
{
	ShowString (STR(msg), (LONG *)(&msg+1));
}


GLOBALCALL void ShowString (CONST_STRPTR s, LONG *args)

/* Formats a string and shows it to the user in the Log Window.
 * If the Log Window can't be opened, this function will fall
 * to ShowRequest() or to Printf().
 */
{
	struct WinUserData *wud;

	if (!IntuitionBase)
	{
		if (StdOut) VPrintf ((STRPTR)s, args);
		return;
	}

	wud = WDescr[WID_LOG].Wud;

	if (!wud || !wud->Win)
	{
		NewWindow (WID_LOG);
		wud = WDescr[WID_LOG].Wud;
	}

	if (wud && wud->Win)
	{
		GT_SetGadgetAttrs (wud->Gadgets[GD_LogList], wud->Win, NULL,
			GTLV_Labels, ~0,
			TAG_DONE);

		if (LogLines > 30)
		{
			RemListViewNode (LogList.lh_Head);
			LogLines--;
		}

		if (AddListViewNodeA (&LogList, s, args))
			LogLines++;
		else
			DisplayBeep(Scr);

		GT_SetGadgetAttrs (wud->Gadgets[GD_LogList], wud->Win, NULL,
			GTLV_Labels, &LogList,
			GTLV_Top, 30,
			TAG_DONE);
	}
	else
		ShowRequestStr (s, NULL, args);
}



static void ProgressAbortClicked (void)
{
	ProgressAborting = TRUE;
}

