/******************************************************************************

    MODUL
	subs.c

    DESCRIPTION
	General subroutines.

    NOTES

    BUGS

    TODO

    EXAMPLES

    SEE ALSO

    INDEX

    HISTORY
	16. Jan 1993	ada created

******************************************************************************/

/**************************************
		Includes
**************************************/
#include "defs.h"
#include <clib/macros.h>
#include <graphics/display.h>
#define MYDEBUG     0
#include "debug.h"


/**************************************
	    Globale Variable
**************************************/
Prototype void	  makemygadget	  (struct Gadget *);
Prototype int	  firstns	  (char *);
Prototype int	  lastns	  (char *);
Prototype int	  wordlen	  (char *);
Prototype BOOL	  getpathto	  (BPTR, char *, char *);
Prototype LINE	  allocline	  (long);
Prototype int	  detab 	  (char *, char *, int);
Prototype int	  xefgets	  (FILE *, char *, int);
Prototype ED	* finded	  (char *, int);
Prototype void	  mountrequest	  (int);
Prototype FONT	* GetFont	  (char *, short);
Prototype void	  freeline	  (LINE);
Prototype void	  movetocursor	  (void);
Prototype int	  extend	  (ED *, int);
Prototype int	  makeroom	  (int);
Prototype void	  freelist	  (LINE *, int);
Prototype long	  lineflags	  (int);
Prototype void	  scroll_display  (short, short, Column, Line, Column, Line);
Prototype char	* skip_whitespace (char *);
Prototype char	  is_number	  (char *);
Prototype char	* getnextcomline  (FILE *, int *);
Prototype char	* fname 	  (char *);
Prototype BOOL	  switch_ed	  (ED *);
Prototype void	  do_makecursorvisible (void);
Prototype void	  MakeRectVisible (WIN *, USHORT, USHORT, USHORT, USHORT);
Prototype LINE	  GETLINE	  (ED *, Line);
Prototype void	  SETLINE	  (ED *, LINE, Line);
Prototype int	  LENGTH	  (LINE);
Prototype UBYTE * GETCONTENTS	  (LINE);
Prototype BOOL	  SETCONTENTS	  (LINE, UBYTE *);
Prototype int	  LINELEN	  (ED *, Line);

/* Assembler */
Prototype void swapmem (void *, void *, ULONG);


/**************************************
      Interne Defines & Strukturen
**************************************/
typedef struct FileInfoBlock FIB;


/**************************************
	    Interne Variable
**************************************/


/**************************************
	   Interne Prototypes
**************************************/


/*
 *  Create XDME's text icon.
 */

void makemygadget (struct Gadget *gad)
{
    const static unsigned short ga[] =
    {
     /* Plane 0 */
     0x0000,0x0000,0x0000,0x0400,0x0000,0x0000,0x0000,0x0C00,
     0x0000,0x0000,0x0000,0x0C00,0x0FFF,0xFFFF,0xFFFF,0x8C00,
     0x0C00,0x0000,0x0000,0x0C00,0x0C1F,0xFFFF,0xFFE0,0x0C00,
     0x0C17,0xFFFF,0xFFA0,0x0C00,0x0C1E,0x08BF,0xFFE0,0x0C00,
     0x0C17,0xFFFF,0xFFA0,0x0C00,0x0C1F,0x25FF,0xFFE0,0x0C00,
     0x0C17,0xFFFF,0xFFA0,0x0C00,0x0C1F,0x0BFF,0xFFE0,0x0C00,
     0x0C17,0xFFFF,0xFFA0,0x0C00,0x0C1E,0x1FFF,0xFFE0,0x0C00,
     0x0C16,0xFFFF,0xFFA0,0x0C00,0x0C1E,0x7FFF,0xFFE0,0x0C00,
     0x0C17,0xFFFF,0xFFA0,0x0C00,0x0800,0x0000,0x0000,0x0C00,
     0x0000,0x0000,0x0000,0x0C00,0x007B,0xFF9E,0x7FF8,0x0C00,
     0x0031,0x98CE,0x7318,0x0C00,0x001B,0x186F,0xF318,0x0C00,
     0x001B,0x186F,0xF360,0x0C00,0x000E,0x186D,0xB3E0,0x0C00,
     0x001B,0x186D,0xB360,0x0C00,0x001B,0x186C,0x3318,0x0C00,
     0x0031,0x98CC,0x3318,0x0C00,0x007B,0xFF9E,0x7FF8,0x0C00,
     0x0000,0x0000,0x0000,0x0C00,0x7FFF,0xFFFF,0xFFFF,0xFC00,
     /* Plane 1 */
     0xFFFF,0xFFFF,0xFFFF,0xF800,0xD555,0x5555,0x5555,0x5000,
     0xD555,0x5555,0x5555,0x5000,0xD000,0x0000,0x0000,0x5000,
     0xD000,0x0000,0x0000,0xD000,0xD000,0x0000,0x0000,0xD000,
     0xD003,0xFFFF,0xFF00,0xD000,0xD003,0xFFFF,0xFF00,0xD000,
     0xD003,0xFFFF,0xFF00,0xD000,0xD003,0xFFFF,0xFF00,0xD000,
     0xD003,0xFFFF,0xFF00,0xD000,0xD003,0xFFFF,0xFF00,0xD000,
     0xD003,0xFFFF,0xFF00,0xD000,0xD003,0xFFFF,0xFF00,0xD000,
     0xD003,0xFFFF,0xFF00,0xD000,0xD003,0xFFFF,0xFF00,0xD000,
     0xD003,0xFFFF,0xFF00,0xD000,0xD7FF,0xFFFF,0xFFFF,0xD000,
     0xD555,0x5555,0x5555,0x5000,0xD504,0x0041,0x0005,0x5000,
     0xD544,0x4511,0x0445,0x5000,0xD544,0x4510,0x0445,0x5000,
     0xD544,0x4510,0x0415,0x5000,0xD551,0x4510,0x4415,0x5000,
     0xD544,0x4510,0x4415,0x5000,0xD544,0x4511,0x4445,0x5000,
     0xD544,0x4511,0x4445,0x5000,0xD504,0x0041,0x0005,0x5000,
     0xD555,0x5555,0x5555,0x5000,0x8000,0x0000,0x0000,0x0000
    };
    const static struct Image image =
    {
     0, 0, 54, 30, 2, (unsigned short *)ga, 3, 0, NULL
    };

    clrmem (gad, sizeof(struct Gadget));

    gad->Width	      = 54;
    gad->Height       = 31; /* Blank line between Image and Text ! */
    gad->Flags	      = GADGIMAGE|GADGHCOMP;
    gad->GadgetType   = BOOLGADGET;
    gad->Activation   = RELVERIFY|GADGIMMEDIATE;
    gad->GadgetRender = (APTR)&image;
} /* makemygadget */


/*
 * return index of first non space.  Returns 0 if no spaces found.
 */

int firstns (char * str)
{
    short i;

    for (i = 0; str[i] && str[i] == ' '; i ++);

    if (str[i] == 0)
	i = 0;

    return (i);
} /* firstns */


/*
 *  Return index of last non-space, 0 if no spaces.
 */

int lastns (char * str)
{
    short i;

    /* get last char of string (i might be -1 after that !).
       now walk left until a char != ' ' is found or i == 0. */
    for (i = strlen(str) - 1; i > 0 && str[i] == ' '; i --);

    if (i < 0)  /* string empty */
	i = 0;

    return (i);
} /* lastns */


/*
 *  Return length of word under cursor
 */

int wordlen (char * str)
{
    short i;

    for (i = 0; *str && *str != ' '; ++i, ++str);

    return(i);
} /* wordlen */


/*
 *  Backtracks the program lock, 0 on failure, 1 on success.
 *  The filename is appended to the lock, if it exists.
 */

BOOL getpathto (BPTR lock, char * filename, char * buf)
{
    if (!NameFromLock (lock, buf, PATHSIZE))
	return (FALSE);

    if (filename)
	AddPart (buf, filename, PATHSIZE);

    return (TRUE);
} /* getpathto */


/*
 *  Allocation routines and other shortcuts
 */

static LINE empty_line;

__autoinit void init_empty_line (void)
{
    if (empty_line.text = AllocateArray (1,1))
	WriteElements (empty_line, "", 0, 1);
}


LINE allocline (long size)
{
    LINE line;

    if (size <= 1)
	return (empty_line);     /* no more tiny empty blocks */

    if (!(line.text = AllocateArray (1, size)) )
	return (NULL);

    return (line);
} /* allocline */


void freeline (LINE line)
{
    if (line && line != empty_line)
	FreeArray (line.text);
} /* freeline */


/*
 *  Remove tabs in a buffer
 */

int detab (char * ibuf, char * obuf, int maxlen)
{
    short i, j;

    maxlen -= 1;

    for (i = j = 0; ibuf[i] && j < maxlen; ++i)
    {
	if (ibuf[i] == 9)
	{
	    do
	    {
		obuf[j++] = ' ';
	    } while ((j & 7) && j < maxlen);
	} else
	{
	    obuf[j++] = ibuf[i];
	}
    }

    if (j && obuf[j-1] == '\n')
	--j;

    while (j && obuf[j-1] == ' ')
	--j;

    obuf[j] = 0;

    return (j);
} /* detab */


int xefgets (FILE * fi, char * buf, int max)
{
    char ebuf[MAXLINELEN];

    if (fgets (ebuf, max, fi))
	return (detab (ebuf, buf, max));

    return (-1);
} /* xefgets */


ED * finded (char * str, int doff)

{
    ED *ed;

    for (ed = (ED *)GetHead(&DBase); ed; ed=(ED *)GetSucc((struct Node *)ed))
    {
	if (strlen (ed->name) >= doff && stricmp (str, ed->name+doff) == 0)
	    return (ed);
    }

    return (NULL);
} /* finded */


void mountrequest (int bool)
{
    static APTR     original_pr_WindowPtr = NULL;
    register PROC * proc;

    proc = (PROC *)FindTask (0);

    if (!bool && proc->pr_WindowPtr != (APTR)-1)
    {
	original_pr_WindowPtr = proc->pr_WindowPtr;
	proc->pr_WindowPtr    = (APTR)-1;
    }

    if (bool && proc->pr_WindowPtr == (APTR)-1)
	proc->pr_WindowPtr = original_pr_WindowPtr;
} /* mountrequest */


/*
 *  GETFONT()
 *
 *  This function properly searches resident and disk fonts for the
 *  font.
 */

struct Library * DiskfontBase;

FONT * GetFont (char * name, short size)
{
    FONT * font1;
    TA	   Ta;
    USHORT pos;

    strcpy (tmp_buffer, name);
    pos = strlen (tmp_buffer);

    if (pos < 6 || (pos > 5 && stricmp (tmp_buffer + pos - 5, ".font")))
	strcpy (tmp_buffer + pos, ".font");

    Ta.ta_Name	= (UBYTE *)tmp_buffer;
    Ta.ta_YSize = size;
    Ta.ta_Style = 0;
    Ta.ta_Flags = 0;

    font1 = OpenFont (&Ta);

    if (!font1 || font1->tf_YSize != Ta.ta_YSize)
    {
	FONT * font2;

	if (DiskfontBase = OpenLibrary ("diskfont.library", 0L))
	{
	    if (font2 = OpenDiskFont (&Ta))
	    {
		if (font1)
		    CloseFont (font1);

		font1 = font2;

		CloseLibrary (DiskfontBase);
	    }
	}
    } /* if !font1 || Wrong_Font_Size */

    return (font1);
} /* GetFont */


void movetocursor (void)
{
    Move (Ep->win->RPort, COLT(Ep->column - Ep->topcolumn),
	  ROWT(Ep->line - Ep->topline));
} /* movetocursor */


int extend (ED * ep, int lines)
{
    long   extra = ep->maxlines - ep->lines;
    LINE * list;

    if (lines > extra)
    {
	lines += ep->lines;

	if (list = (LINE *)alloclptr(lines))
	{
	    bmovl (ep->list, list, ep->lines);
	    FreeMem (ep->list, sizeof(LINE) * ep->maxlines);

	    ep->maxlines = lines;
	    ep->list	 = list;

	    return (1);
	}

	nomemory ();
	return (0);
    }

    return (1);
} /* extend */


int makeroom (int n)
{
    ED * ep = Ep;

    if (ep->lines >= ep->maxlines)
	return (extend (ep, n));

    return (1);
} /* makeroom */


void freelist (LINE * list, int n)
{
    while (n)
    {
	freeline (list[0]);
	list ++;
	n --;
    }
} /* freelist */


void do_undo (void)
{
    text_load ();
    text_redisplaycurrline ();
} /* do_undo */


long lineflags (int line)
{
    long flags;

    if (line < Ep->topline)
	flags = LINE_ABOVE;
    else if (line < Ep->topline + Lines)
	flags = LINE_VISIBLE;
    else
	flags = LINE_BELOW;

    return (flags);
} /* lineflags */


void scroll_display (short dx, short dy, Column lc, Line tl, Column rc, Line bl)
{
    USHORT width, height;
    Line lines, rest;
    USHORT x1, y1, x2, y2, t;
    Line bottom;
    Column right;
    RP * rp;

    /* don't do anything if we are not allowed */
    if (Ep->iconmode || Nsu || (!dx && !dy)) return;

    /* clip coords */
    if (tl < Ep->topline)
	tl = Ep->topline;

    if (bl >= Ep->topline+Lines)
	bl = Ep->topline + Lines - 1;

    if (lc < Ep->topcolumn)
	lc = Ep->topcolumn;

    if (rc >= Ep->topcolumn+Columns)
	rc = Ep->topcolumn + Columns - 1;

    /* check if area is visible */
    if (bl < tl || rc < lc) return;

    /* is there something below the text */
    if (bl > Ep->lines)
    {
	bottom = bl - Ep->lines + 1;
    } else
    {
	bottom = 0;
    }

    /* is there something to the right */
    if (rc > MAXLINELEN)
    {
	right = rc - MAXLINELEN + 1;
    } else
    {
	right = 0;
    }

    /* get size of real text */
    width  = rc - lc + 1 - right;
    height = bl - tl + 1 - bottom;

    if (dx)
    {
	lines = ABS(dx);    /* number of columns to draw */

	if (lines >= width)
	     rest = 0;
	else rest = width - lines;

	dx *= Xsize;
    } else
    {
	lines = ABS(dy);    /* number of lines to draw */

	/* if the number of new lines is bigger than the height of the
	   remaining text, we have to erase the empty lines. Otherwise
	   we store the first line of the area that is to update in rest. */

	if (lines >= height)
	     rest = 0;
	else rest = height - lines;

	dy *= Ysize;
    }

    x1 = lc - Ep->topcolumn;
    x2 = rc - Ep->topcolumn;
    y1 = tl - Ep->topline;
    y2 = bl - Ep->topline;

    t = y1;

    x1 = COL(x1);
    x2 = COL(x2+1)-1;
    y1 = ROW(y1);
    y2 = ROW(y2+1)-1;

    rp = Ep->win->RPort;

#ifdef MYDEBUG
    D(bug("scroll_display: rest %ld  height %ld  lines %ld  t %ld (%ld,%ld)-(%ld,%ld)\n",
	  rest, height, lines, t, x1, y1, x2, y2));
#endif

    SetWrMsk (rp, -1);
    SetBPen (rp, TEXT_BPEN);

    if (rest)
    {
	ScrollRaster (rp, dx, dy, x1, y1, x2, y2);

	if (dx)
	{
	    Line   line;
	    Column col;

	    line = tl;
	    col  = lc;

	    if (dx > 0)
		col += rest;

	    redraw_block (TRUE, line, col, line + height, col + lines - 1);
	} else
	{
	    if (dy < 0)
	    {
		text_displayseg (t, lines);
	    } else
	    {
		text_displayseg (t+rest, lines);
	    }
	}
    } else
    {
	SetAPen (rp, TEXT_BPEN);
	RectFill (rp, x1, y1, x2, y2);

	text_displayseg (t, height);
    }
} /* scroll_display */


char * skip_whitespace (char * ptr)
{
    while (isspace (*ptr)) ptr ++;

    return (ptr);
} /* skip_whitespace */


/*
 *  IS_NUMBER
 *  tests if a string represents a valid number, i.e.
 *  if there are only numbers in it ( what about spaces around,
 *  and + before a number, do they disturb atol??? )
 */

char is_number (char * str)           /* represents a string a number? */
{
    while (*str==' ')           /* ignore leading spaces */
    {
	str++;
    } /* while */

    if ((*str=='-')||(*str=='+'))  /* dont worry about +/- */
    {
	str++;
    } /* if */

    while (isdigit(*str))     /* if there are only numbers in str: ok */
    {
	str++;
    } /* while */

    while (*str==' ')          /* ignore ending spaces */
    {
	str++;
    } /* while */

    return (!*str);
} /* is_number */


char * getnextcomline (FILE* fi, int * lineno)
{
    /* skipps comments and emptylines */
    char* buf;
    char* ptr;
    do
    {
	if (lineno)
	{
	    (*lineno)++; /* Welches hat Priortaet?? */
	} /* if */

	buf = tmp_buffer;
	if ((xefgets(fi, tmp_buffer, LINE_LENGTH))<0)
	/* buf = fgets(tmp_buffer, LINE_LENGTH, fi); */
	if (!buf)
	{
	    error ("%s:\nUnexpected End Of File Encountered", av[0]);
	    return(NULL);
	} /* if */
	buf[255] = 0; /* marker for eol */
	while (*buf<33 && *buf)
	{
	    buf++;
	} /* while */
    } while (buf[0] == 0 || buf[0] == '#');
    for (ptr = buf; *ptr;ptr++)
    {
	if (*ptr == 10)
	{
	    *ptr = 0;
	} /* if */
    } /* for */
    return(buf);
} /* getnextcomline */


/*
 *  fname
 *
 *  instead of a macro: sets a ptr to the filename of a full path&filename
 *  (returniert einen Zeiger auf den namensteil eines pfades)
 *
 * SHOULD BE PUT INTO SUBS.C
 */

char * fname (char * fullpath)
{
    return (FilePart (fullpath));
} /* fname */


BOOL switch_ed (ED * newed)
{
    BOOL success;

    if (newed)
    {
	if (Ep)
	    text_sync ();

	Ep = newed;

	text_load ();

	if (!Ep->iconmode)
	{
	    set_window_params ();
	    window_title ();
	}

	text_cursor (1);

	success = TRUE;
    } else
	success = FALSE;

    return (success);
} /* switch_ed */


void do_makecursorvisible (void)
{
    if ((Ep->win->Flags & WFLG_WINDOWACTIVE) && !Ep->iconmode)
    {
	USHORT x1 = COL(Ep->column - Ep->topcolumn) + Ep->win->LeftEdge;
	USHORT y1 = ROW(Ep->line   - Ep->topline)   + Ep->win->TopEdge;
	USHORT x2, y2;

	x2 = x1 + (Xsize << 2) + Xsize;
	y2 = y1 + (Ysize << 2) + Ysize;

	if (x1 < (Xsize << 2))
	    x1 = 0;
	else
	    x1 -= Xsize << 2;

	if (y1 < (Ysize << 2))
	    y1 = 0;
	else
	    y1 -= Ysize << 2;

	MakeRectVisible (Ep->win, x1, y1, x2, y2);
    }
} /* do_makecursorvisible */


void MakeRectVisible (WIN * win, USHORT minx, USHORT miny, USHORT maxx,
								   USHORT maxy)
{
    struct DimensionInfo diminfo;
    struct Screen      * screen        = win->WScreen;
    ULONG		 Width, Height;
    WORD		 dx, dy;

    GetDisplayInfoData (NULL,
			(UBYTE *)&diminfo,
			sizeof(diminfo),
			DTAG_DIMS,
			GetVPModeID (&screen->ViewPort)
    );

    Width  = diminfo.TxtOScan.MaxX - diminfo.TxtOScan.MinX;
    Height = diminfo.TxtOScan.MaxY - diminfo.TxtOScan.MinY;

    if (Width > screen->Width)
	Width = screen->Width;

    if (Height > screen->Height)
	Height = screen->Height;

    if (minx < win->LeftEdge)
	minx = win->LeftEdge;

    if (miny < win->TopEdge)
	miny = win->TopEdge;

    if (maxx > win->LeftEdge + win->Width)
	maxx = win->LeftEdge + win->Width;

    if (maxy > win->TopEdge + win->Height)
	maxy = win->TopEdge + win->Height;

    dx = (Width - screen->LeftEdge) - maxx + 1;

    if (dx > 0)
    {
	dx = minx + screen->LeftEdge;

	if (dx > 0)
	    dx = 0;
	else
	    dx = -dx;
    }

    dy = (Height - screen->TopEdge) - maxy + 1;

    if (dy > 0)
    {
	dy = miny + screen->TopEdge;

	if (dy >= 0)
	    dy = 0;
	else
	    dy = -dy;
    }

    MoveScreen (screen, dx, dy);
} /* MakeRectVisible */


LINE GETLINE (ED * ep, Line nr)
{
    LINE line;

    ReadElements (ep->linearray, &line, nr, 1);

    return (line);
} /* GETLINE */


void SETLINE (ED * ep, LINE line, Line nr)
{
    WriteElements (ep->linearray, &line, nr, 1);
} /* SETLINE */


int LENGTH (LINE line)
{
    return (SizeofArray (line.text));
} /* LENGTH */


UBYTE * GETCONTENTS (LINE line)
{
    return (AddressOfElement (line.text, 0));
} /* GETCONTENTS */


BOOL SETCONTENTS (LINE line, UBYTE * new_contents)
{
    /* Empty old array */
    SetArrayTags (line.text, AR_NumElements, 0);

    return (WriteElements (line.text, new_contents, 0, ~1));
} /* SETCONTENTS */


int Clen (void)
{
    int     len = SizeofArray (Current.text);
    UBYTE * ptr;

    ptr = AddressOfElement (Current.text, len);
} /* Clen */


int LINELEN (ED * ep, Line nr)
{
    if (nr == ep->line)
    {
	return (Clen ());
    }

    return (LENGTH (GETLINE(ep,nr)));
} /* LINELEN */


/******************************************************************************
*****  ENDE subs.c
******************************************************************************/
