
/*
 *  DISPLAY.C
 *
 *  This module handles all display windows.
 *
 */

#include <exec/types.h>
#include "defs.h"
#include <intuition/intuition.h>

Prototype int getyn(char *);
Prototype void *OpenBrowseDisplay(char *, char *, short);
Prototype void SetBrowseTitle(void *, char *);
Prototype void CloseBrowseDisplay(void *);
Prototype void ChangeBrowseDisplay(void *, char *, char *);
Prototype int WaitBrowseEvent(void **, int *, int *);
Prototype void	LoadDisplayConfig(void);
Prototype void	SaveDisplayConfig(void);
Prototype void	RefreshBrowseDisplay(void *);

Local void TabExpand(char *, short);



#define MAXCONF 3

typedef struct Window	    Window;
typedef struct NewWindow    NewWindow;
typedef struct IntuiMessage IMsg;

typedef struct Display {
    struct Display *Next;
    Window  *Win;	    /*	associated window	*/
    char    *Name;	    /*	name of file		*/
    char    *Title;	    /*	window title		*/
    FILE    *Fi;	    /*	file pointer		*/
    long    WMask;	    /*	wait mask for window	*/
    short   Id; 	    /*	config id		*/
    short   FlagRefresh;
    short   ColStart;
    long    FirstNonHdrLine;/*	first non-header line	*/

    long    TLineNo;	    /*	line # for top line	*/
    long    TPos;	    /*	position (top line)     */
    long    BPos;	    /*	position (bottom line)  */
    long    XPos;	    /*	end of file (or scan)   */
    long    WLines;	    /*	lines displayed in win	*/
    long    WMaxLines;	    /*	maximum #lines in window*/
    long    FLines;	    /*	lines in file if known	*/
} Display;

typedef struct Config {
    short   TopEdge;
    short   LeftEdge;
    short   Width;
    short   Height;
} Config;

static long    WMask;	   /*  Window mask	   */
static Display *CachDisp;  /*  last display that had an event		   */
static Display *DispBase;  /*  list of displays    */
static short   FlagRefresh;

static Config  ConfAry[MAXCONF];

Local int   HandleEvent(Display *, int *, int *);
Local void  SaveWindowConfig(Display *);
Local long  BackupFile(Display *, long, long);
Local long  ForwardFile(Display *, long, long);
Local void  SetRefresh(Display *, int);

#ifndef LATTICE
extern void *OpenWindow();
extern void *GetMsg();
#endif

void *
OpenBrowseDisplay(fileName, title, id)
char *fileName;
char *title;
short id;
{
    static NewWindow Nw = {
	0, 0, 320, 200, -1, -1,
	CLOSEWINDOW|NEWSIZE|MOUSEBUTTONS|VANILLAKEY|REFRESHWINDOW,
	WINDOWSIZING|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|
	SIMPLE_REFRESH|ACTIVATE,
	NULL, NULL, NULL, NULL, NULL,
	32, 16, -1, -1, WBENCHSCREEN
    };
    Display *disp = malloc(sizeof(Display));

    if (title == NULL)
	title = fileName;

    setmem(disp, sizeof(Display), 0);

    if (id < 0 || id >= MAXCONF)
	id = MAXCONF - 1;

    {
	Config *conf = ConfAry + id;
	Nw.LeftEdge = conf->LeftEdge;
	Nw.TopEdge  = conf->TopEdge;
	Nw.Width    = conf->Width;
	Nw.Height   = conf->Height;
	if (Nw.Width < 32) {
	    Nw.LeftEdge = 40;
	    Nw.Width = 600;
	}
	if (Nw.Height < 16)
	    Nw.Height = 150;
    }

    disp->Win = OpenWindow(&Nw);
    if (disp->Win == NULL) {
	Nw.LeftEdge = 0;
	Nw.TopEdge  = 0;
	Nw.Width    = 320;
	Nw.Height   = 100;
	disp->Win = OpenWindow(&Nw);
    }

    if (disp->Win) {
	disp->Name = strdup(fileName);
	disp->Title= strdup(title);
	disp->WMask = 1 << disp->Win->UserPort->mp_SigBit;
	disp->Id = id;

	SetAPen(disp->Win->RPort, 1);

	SetWindowTitles(disp->Win, disp->Title, (char *)-1L);

	disp->Next = DispBase;
	DispBase = disp;

	WMask |= disp->WMask;

	SetRefresh(disp, 0);
	if (disp->Fi = fopen(disp->Name, "r")) {
	    disp->FirstNonHdrLine = 0;
	    while (fgets(TmpBuf, sizeof(TmpBuf), disp->Fi) && TmpBuf[0] != '\n')
		++disp->FirstNonHdrLine;
	}
    } else {
	free(disp);
	disp = NULL;
    }
    return((void *)disp);
}

void
SetBrowseTitle(_disp, title)
void *_disp;
char *title;
{
    Display *disp = _disp;
    char *old = disp->Title;

    disp->Title = strdup(title);
    SetWindowTitles(disp->Win, disp->Title, (char *)-1L);
    free(old);
}

void
CloseBrowseDisplay(_disp)
void *_disp;
{
    Display *disp = _disp;
    Display *d;

    if (disp == CachDisp)
	CachDisp = NULL;

    if (DispBase == disp) {                     /*  unlink display  */
	DispBase = disp->Next;
    } else {
	for (d = DispBase; d; d = d->Next) {
	    if (d->Next == disp) {
		d->Next = disp->Next;
		break;
	    }
	}
    }

    SetWindowTitles(disp->Win, NULL, NULL);

    if (disp->Win) {
	SaveWindowConfig(disp);
	CloseWindow(disp->Win);
    }

    WMask &= ~disp->WMask;

    free(disp->Title);
    free(disp->Name);
    if (disp->Fi)
	fclose(disp->Fi);
    free(disp);
}

/*
 *  Change file being displayed
 */

void
ChangeBrowseDisplay(_disp, fileName, title)
void *_disp;
char *fileName;
char *title;
{
    Display *disp = _disp;
    Window *win = disp->Win;

    if (title == NULL)
	title = fileName;

    {
	char *old = disp->Title;
	disp->Title = strdup(title);
	SetWindowTitles(disp->Win, disp->Title, (char *)-1L);
	free(old);
    }

    free(disp->Name);
    disp->Name = strdup(fileName);
    if (disp->Fi) {
	fclose(disp->Fi);
	disp->Fi = NULL;
    }

    disp->TLineNo = 0;
    disp->TPos = 0;
    disp->XPos = 0;
    disp->WLines = 0;
    disp->WMaxLines = 0;
    disp->FLines = 0;

    SetRefresh(disp, 0);
}

/*
 *  Wait for an event.	While waiting finish up scanning files for which we
 *  do not know the line number extent.  Returns 0 if there are no windows
 *  left.
 */

int
WaitBrowseEvent(_pdisp, prow, pcol)
void **_pdisp;
int *prow;
int *pcol;
{
    long mask;
    Display **pdisp = _pdisp;

    if (WMask == 0)
	return(0);

    for (;;) {
	Display *disp;

	if (FlagRefresh && (SetSignal(0,0) & WMask) == 0) {
	    for (disp = DispBase; disp; disp = disp->Next) {
		if (disp->FlagRefresh)
		    RefreshBrowseDisplay(disp);
	    }
	    FlagRefresh = 0;
	}
	mask = Wait(WMask);
	if ((disp = CachDisp) && (disp->WMask & mask)) {
	    mask &= ~disp->WMask;
	    if (HandleEvent(disp, prow, pcol))
		goto event_handled;
	}
	if (mask) {
	    for (disp = DispBase; disp; disp = disp->Next) {
		if (mask & disp->WMask) {
		    mask &= ~disp->WMask;
		    if (HandleEvent(disp, prow, pcol)) {
event_handled:
			*pdisp = disp;
			if (GetHead(&disp->Win->UserPort->mp_MsgList))
			    mask |= disp->WMask;
			if (mask)
			    SetSignal(mask, mask);
			return(1);
		    }
		}
	    }
	}
    }
}

static int
HandleEvent(disp, prow, pcol)
Display *disp;
int *prow;
int *pcol;
{
    int ret = 0;
    IMsg *im;
    Window *win = disp->Win;

    while ((ret == 0) && (im = GetMsg(win->UserPort))) {
	switch(im->Class) {
	case CLOSEWINDOW:
	    *prow = -1;
	    *pcol = -1;
	    ret = 1;
	    break;
	case VANILLAKEY:
	    switch(im->Code) {
	    case 13:
		if (disp->BPos != disp->XPos) {
		    disp->TPos = ForwardFile(disp, disp->TPos, 2);
		    ++disp->TLineNo;
		    SetRefresh(disp, 0);
		}
		break;
	    case '2':       /*  page down   */
	    case ' ':
		if (disp->BPos != disp->XPos) {
		    disp->TPos = disp->BPos;
		    disp->TLineNo += disp->WLines;
		    SetRefresh(disp, 0);
		}
		break;
	    case '8':       /*  page up   this article  */
		if (disp->TPos != 0) {
		    disp->TPos = BackupFile(disp, disp->TPos, disp->WMaxLines);
		    disp->TLineNo -= disp->WMaxLines;
		    if (disp->TLineNo < 0)
			disp->TLineNo = 0;
		    SetRefresh(disp, 0);
		}
		break;
	    case '3':
		if (disp->BPos != disp->XPos) {
		    disp->TPos = BackupFile(disp, disp->XPos, disp->WLines);
		    disp->TLineNo = disp->FLines - disp->WLines;    /* XXX */
		    if (disp->TLineNo < 0)
			disp->TLineNo = 0;
		    SetRefresh(disp, 0);
		}
		break;
	    case '9':
		if (disp->TPos != 0) {
		    disp->TPos = 0;
		    disp->TLineNo = 0;
		    SetRefresh(disp, 0);
		}
		break;
	    case '6':
		SetRefresh(disp, disp->ColStart + 40);
		break;
	    case '4':
		SetRefresh(disp, disp->ColStart - 40);
		break;
	    default:
		*prow = -1;
		*pcol = im->Code;
		ret = 1;
	    }
	    break;
	case MOUSEBUTTONS:
	    if (im->Code == SELECTDOWN) {
		*pcol = (im->MouseX - win->BorderLeft) / win->RPort->TxWidth;
		*prow = ((im->MouseY - win->BorderTop) / win->RPort->TxHeight) + disp->TLineNo;
		if (*prow >= 0)
		    ret = 1;
	    }
	    break;
	case NEWSIZE:
	    SetRefresh(disp, 0);
	    break;
	case REFRESHWINDOW:
	    BeginRefresh(win);
	    RefreshBrowseDisplay(disp);
	    EndRefresh(win, 1);
	    break;
	default:
	    break;
	}
	ReplyMsg(im);
    }
    return(ret);
}

void
RefreshBrowseDisplay(vdisp)
void *vdisp;
{
    Display *disp = (Display *)vdisp;
    long x, y;
    int maxx;
    int dispLines = 0;
    int lineNo;
    Window *win = disp->Win;
    short b;
    short isHdr;
    short rvsMode;

    disp->FlagRefresh = 0;
    SetAPen(win->RPort, 0);
    RectFill(win->RPort, win->BorderLeft, win->BorderTop, win->Width - win->BorderRight, win->Height - win->BorderBottom);
    SetAPen(win->RPort, 1);

    if (disp->Fi == NULL) {
	disp->Fi = fopen(disp->Name, "r");
	if (disp->Fi == NULL)       /*  can't refresh if can't open file */
	    return;
	fseek(disp->Fi, 0L, 2);
	disp->XPos = ftell(disp->Fi);
    }
    if (ftell(disp->Fi) != disp->TPos)
	fseek(disp->Fi, disp->TPos, 0);
    lineNo = disp->TLineNo - 1;
    x = win->BorderLeft;
    y = win->BorderTop + win->RPort->TxBaseline;
    maxx = (win->Width - win->BorderLeft - win->BorderRight) / win->RPort->TxWidth;

    b = 0;
    TmpBuf[0] = 0;

    disp->WMaxLines = (win->Height - win->BorderBottom - win->BorderTop - 2) / win->RPort->TxHeight;

    while (y < win->Height - win->BorderBottom - 2) {
	int len;

	if (TmpBuf[b] == 0) {
	    if (fgets(TmpBuf, sizeof(TmpBuf), disp->Fi) == NULL)
		break;
	    rvsMode = 0;
	    if (++lineNo < disp->FirstNonHdrLine) {
		isHdr = 1;
		if (strncmp(TmpBuf, "Subject:", 8) == 0)
		    rvsMode = 1;
	    } else {
		isHdr = 0;
	    }
	    TabExpand(TmpBuf, sizeof(TmpBuf));
	    b = 0;
	}
	len = strlen(TmpBuf + b) - disp->ColStart;
	if (len > maxx)
	    len = maxx;
	if (len > 0) {
	    if (rvsMode) {
		SetAPen(win->RPort, 0);
		SetBPen(win->RPort, 1);
	    }
	    Move(win->RPort, x, y);
	    Text(win->RPort, TmpBuf + b + disp->ColStart, len);
	    if (rvsMode) {
		SetAPen(win->RPort, 1);
		SetBPen(win->RPort, 0);
	    }
	}
	++dispLines;
	y += win->RPort->TxHeight;
	if (isHdr || disp->ColStart || len <= 0)
	    b = strlen(TmpBuf);
	else
	    b += len;
    }
    disp->BPos = ftell(disp->Fi);
    disp->WLines = dispLines;
}

void
LoadDisplayConfig()
{
    FILE *fi;

    if (fi = fopen("s:dnews.config", "r")) {
	fread(ConfAry, 1, sizeof(ConfAry), fi);
	fclose(fi);
    }
}

void
SaveDisplayConfig()
{
    FILE *fi;

    if (fi = fopen("s:dnews.config", "w")) {
	fwrite(ConfAry, 1, sizeof(ConfAry), fi);
	fclose(fi);
    }
}

static void
SaveWindowConfig(disp)
Display *disp;
{
    Config *conf = ConfAry + disp->Id;
    Window *win = disp->Win;

    conf->TopEdge   = win->TopEdge;
    conf->LeftEdge  = win->LeftEdge;
    conf->Width     = win->Width;
    conf->Height    = win->Height;
}

/*
 *  Count lines forwards
 */

static long
ForwardFile(disp, pos, lines)
Display *disp;
long pos;
long lines;
{
    long n;
    long i;

    if (disp->Fi == NULL) {
	disp->Fi = fopen(disp->Name, "r");
	if (disp->Fi == NULL)
	    return(0);
    }
    fseek(disp->Fi, pos, 0);
    while (lines) {
	if (fgets(TmpBuf, sizeof(TmpBuf), disp->Fi) == NULL)
	    break;
	--lines;
    }
    return(ftell(disp->Fi));
}

/*
 *  Count lines backwards
 */

static long
BackupFile(disp, pos, rlines)
Display *disp;
long pos;
long rlines;
{
    long n;
    long i;

    if (disp->Fi == NULL) {
	disp->Fi = fopen(disp->Name, "r");
	if (disp->Fi == NULL)
	    return(0);
    }
    ++rlines;	/*  think of the case where rlines is 1 in the below code   */

    while (rlines && pos) {
	n = sizeof(TmpBuf);
	if (pos < n)
	    n = pos;
	fseek(disp->Fi, pos - n, 0);
	fread(TmpBuf, 1, n, disp->Fi);
	for (i = n - 1; i >= 0; --i) {
	    if (TmpBuf[i] == '\n') {
		if (--rlines == 0)
		    break;
	    }
	}
	++i;
	pos = pos - n + i;
    }
    return(pos);
}

/*
 *  Flag that the display should be refreshed without doing so immediately.
 *  This allows the user to skip around articles without having to wait
 *  for the display to catch up.
 */

static void
SetRefresh(disp, lrc)
Display *disp;
int lrc;
{
    FlagRefresh = 1;

    if (lrc < 0)
	lrc = 0;

    if (lrc > TMPBSIZE - 1)
	lrc = TMPBSIZE - 1;

    disp->FlagRefresh = 1;
    disp->ColStart = lrc;
}


/*
 *			    YES OR NO
 */

typedef struct IntuiText    ITEXT;
typedef unsigned char	ubyte;

int
getyn(text)
char *text;
{
    int result;
    ITEXT *body, *pos, *neg;

    if (DispBase == NULL)
	return(0);

    body = (ITEXT *)AllocMem(sizeof(ITEXT), 0);
    pos  = (ITEXT *)AllocMem(sizeof(ITEXT), 0);
    neg  = (ITEXT *)AllocMem(sizeof(ITEXT), 0);
    setmem(body, sizeof(ITEXT), 0);
    setmem(pos , sizeof(ITEXT), 0);
    setmem(neg , sizeof(ITEXT), 0);
    body->BackPen = pos->BackPen = neg->BackPen = 1;
    body->DrawMode= pos->DrawMode= neg->DrawMode= AUTODRAWMODE;
    body->LeftEdge = 10;
    body->TopEdge  = 12;
    body->IText    = (ubyte *)text;
    pos->LeftEdge = AUTOLEFTEDGE;
    pos->TopEdge = AUTOTOPEDGE;
    pos->IText = (ubyte *)"OK";
    neg->LeftEdge = AUTOLEFTEDGE;
    neg->TopEdge = AUTOTOPEDGE;
    neg->IText = (ubyte *)"CANCEL";
    result = AutoRequest(DispBase->Win, body, pos, neg, 0, 0, 320, 58);
    FreeMem(body, sizeof(ITEXT));
    FreeMem(pos , sizeof(ITEXT));
    FreeMem(neg , sizeof(ITEXT));
    return(result);
}

void
TabExpand(buf, max)
char *buf;
short max;
{
    short i;
    short len = strlen(buf);
    char *ptr;

    max -= 9;
    for (i = 0, ptr = buf; *ptr; ++ptr, ++i) {
	if (*ptr == '\n') {
	    *ptr = 0;
	    break;
	}
	if (*ptr == 9 && len < max) {
	    int n = ~i & 7;
	    movmem(ptr, ptr + n, len - i + 1);
	    setmem(ptr, n + 1, ' ');
	    len += n;
	}
    }
}

