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

    MODUL
	text.c

    DESCRIPTION

    NOTES

    BUGS

    TODO

    EXAMPLES

    SEE ALSO

    INDEX

    HISTORY
	04. Oct 1992	ada created

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

/**************************************
		Includes
**************************************/
#include <defs.h>


/**************************************
	    Globale Variable
**************************************/


/**************************************
      Interne Defines & Strukturen
**************************************/


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


/**************************************
	   Interne Prototypes
**************************************/
Prototype ED	* uninit_init		 (ED *);
Prototype int	  setpen		 (int);
Prototype short   inversemode		 (int);
Prototype void	  text_cursor		 (int);
Prototype int	  text_init		 (ED *, WIN *, struct NewWindow *);
Prototype int	  text_switch		 (struct Window *);
Prototype int	  text_sync		 (void);
Prototype void	  text_load		 (void);
Prototype int	  text_colno		 (void);
Prototype int	  text_lineno		 (void);
Prototype int	  text_lines		 (void);
Prototype int	  text_cols		 (void);
Prototype int	  text_imode		 (void);
Prototype int	  text_tabsize		 (void);
Prototype ubyte * text_name		 (void);
Prototype void	  text_uninit		 (void);
Prototype void	  text_position 	 (int, int);
Prototype void	  text_redisplaycurrline (void);
Prototype void	  text_redisplay	 (void);
Prototype void	  text_write		 (ubyte *);
Prototype void	  text_displayseg	 (int, int);


#ifndef offsetof
#define offsetof(sname,fname)  ((long)((sname *)0)->fname)
#endif


ED * uninit_init (ED * ep)
{
    FONT	  * font;
    BPTR	    lock   = DupLock ((BPTR)ep->dirlock);
    WIN 	  * win;
    struct Gadget * gadg;
    int 	    bufsiz = offsetof (ED, EndConfig) - offsetof (ED, BeginConfig);
    char	  * buf    = malloc(bufsiz);

    if (!buf) {
	memoryfail = 1;
	warn ("Out of memory !");
	return (NULL);
    }

    movmem (&ep->BeginConfig, buf, bufsiz);

    win      = ep->Win;
    font     = ep->Font;
    gadg     = ep->PropGad;
    ep->Font = NULL;

    text_uninit ();
    text_init (Ep, NULL, NULL);

    ep = Ep;

    if (ep->Font)
	CloseFont (ep->Font);

    ep->Win  = win;

    if (ep->Font = font) {
	SetFont (ep->Win->RPort, font);
	set_window_params ();
    }

    ep->PropGad = gadg;

    movmem (buf, &ep->BeginConfig, bufsiz);

    ep->Modified = 0;
    ep->Line	 = ep->Topline = 0;

    UnLock ((BPTR)ep->dirlock);
    ep->dirlock = (long)lock;

    free (buf);

    return (ep);
} /* uninit_init */


/* Return: Same as is_inblock() ! */
int setpen (int line)
{
    ED	* ep   = Ep;
    RP	* rp   = ep->Win->RPort;
    short test;

    if (line == ep->Line)
	test = is_inblock (line, ep->Column);
    else
	test = is_inblock (line, -1);

    if (test & BP_INSIDE) {
	SetAPen (rp,  BLOCK_FPEN);
	SetBPen (rp,  BLOCK_BPEN);
	SetWrMsk (rp, BLOCK_MASK);
    } else {
	SetAPen (rp,  TEXT_FPEN);
	SetBPen (rp,  TEXT_BPEN);
	SetWrMsk (rp, TEXT_MASK);
    }

    return (test);
} /* setpen */


short inversemode (int n)
{
    RP	* rp = Ep->Win->RPort;
    short test;

    test = setpen (Ep->Line);

    if (n) {
	SetAPen (rp, ~rp->BgPen);
	SetDrMd (rp, JAM2|INVERSVID);
    } else {
	SetDrMd (rp, JAM2);
    }

    return (test);
} /* inversemode */


int text_init (ED * oldep, WIN * win, struct NewWindow * nw)
{
    ED * ep;

    if (!(ep = (ED *)allocb (sizeof(ED))) )
	return(0);

    setmem (ep, sizeof(ED), 0);
    ep->Win = win;

    if (oldep) {
	ep->dirlock = (long)DupLock ((BPTR)oldep->dirlock);

	movmem (&oldep->BeginConfig, &ep->BeginConfig,
		(char *)&ep->EndConfig - (char *)&ep->BeginConfig);

	if (oldep->Font) {
	    ep->Font = oldep->Font;
	    ep->Font->tf_Accessors ++;

	    if (win)
		SetFont (win->RPort, ep->Font);
	}

	/* change oldep to "last editor" to have the iconified window
	   positioned right */
	oldep = GetTail ((LIST *)&DBase);

	if (oldep->iconmode) {
	    ep->IWinx = oldep->Win->LeftEdge;
	    ep->IWiny = oldep->Win->TopEdge;
	} else {
	    ep->IWinx = oldep->IWinx;
	    ep->IWiny = oldep->IWiny;
	}

	ep->IWinx += TextLength (oldep->Win->RPort, oldep->Name,
			    strlen (oldep->Name)) + 20 + 5*8;

	if (ep->IWinx > oldep->Win->WScreen->Width) {
	    ep->IWinx = 0;

	    if(Ep->Win->WScreen->Font != NULL)
		ep->IWiny += oldep->Win->WScreen->Font->ta_YSize + 3;	/* height */
	    else
		ep->IWiny += GfxBase->DefaultFont->tf_YSize + 3;
	}
    } else {
	PROC *proc = (PROC *)FindTask (NULL);
	ep->dirlock = (long)DupLock ((BPTR)proc->pr_CurrentDir);

	ep->Insertmode = 1;
	ep->Tabstop    = 4;
	ep->WWCol      = -1;
	ep->Margin     = 75;
	ep->FGPen      = 1;
	ep->BGPen      = 0;
	ep->HGPen      = 2;
	ep->ASLleft    = 40;
	ep->ASLtop     = 20;
	ep->ASLwidth   = 400;
	ep->ASLheight  = 230;

	ep->IWiny = 11;
	ep->IWinx = 0;

	loadconfig (ep);
    }

    ep->Lines	 = 1;
    ep->Maxlines = 32;
    ep->List	 = (ubyte **)alloclptr (ep->Maxlines);
    ep->List[0]  = allocline (1);

    Current[0] = Clen = 0;

    AddTail ((LIST *)&DBase, (NODE *)ep);
    strcpy ((char *)ep->Name, "unnamed");
    Ep = ep;

    if (nw) {
	if (!ep->iconmode && ep->Win) {
	    nw->LeftEdge = ep->Win->LeftEdge;
	    nw->TopEdge  = ep->Win->TopEdge;
	    nw->Width	 = ep->Win->Width;
	    nw->Height	 = ep->Win->Height;
	} else {
	    if (ep->Winwidth && ep->Winheight) {
		nw->LeftEdge = ep->Winx;
		    nw->TopEdge  = ep->Winy;
		    nw->Width	 = ep->Winwidth;
		    nw->Height	 = ep->Winheight;
	    } else {
		nw->LeftEdge = 0;
		nw->TopEdge  = 0;
		nw->Width    = 640;
		nw->Height   = 200;
	    }
	}

	nw->DetailPen = TEXT_BPEN;
	nw->BlockPen  = TEXT_FPEN;
    }

    return (1);
} /* text_init */


int text_switch (WIN * win)
{
    ED * ep;

    if (win) {
	text_sync ();

	for (ep = (ED *)GetHead (&DBase); ep; ep = (ED *)GetSucc ((struct Node *)ep)) {
	    if (ep->Win == win) {
		Ep = ep;

		text_load ();

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

		return(1);
	    } /* if */
	} /* for */
    } /* if (win) */

    return(0);
} /* text_switch */


     /* copy current line to list of lines and say, if screen-update
	is neccessary. Furthermore, Current is filled with spaces upto
	the current column. */

int text_sync (void)
{
    ED	  * ep	   = Ep;
    char    redraw = 0;
    short   len;
    ubyte * ptr;
    ubyte * line;

    /* remove trailing spaces */
    len = strlen ((char *)Current)-1;
    ptr = Current + len;

    while (len && *ptr == ' ') {
	len --;
	ptr --;
    }

    if (*ptr == ' ')
	*ptr = 0;
    else {
	ptr[1] = 0;
	len ++;
    }

    /* Line length */
    Clen = len;

    if (!Comlinemode) {
	short len_in_blocks, clen_in_blocks;

	line = ep->List[ep->Line];
	len = strlen ((char *)line);

	/* Find out how many blocks (*8) we use and how many we need */
	len_in_blocks  =  (len + 8) & ~7;
	clen_in_blocks = (Clen + 8) & ~7;

	/* This was highly wasting memory. OS is always padding Alloc's
	   to 8 Bytes. If the length of the line is still within one
	   block but has changed we freed and allocated new memory.
	   This guranteed to fragment memory.

	   Just use the old version and edit some large text. Use avail,
	   save & reload text and "avail" again. The you'll see what I mean.
	   This is fixed now. */

	/* I must not use strcpy() if the line was empty since empty
	   lines are handled differently, i.e. they use static memory.
	   Only allocate new, if the line was empty or if the size in
	   blocks has changed, i.e. we need one more or less block of
	   memory (see AllocMem() in Amiga-ROM-Kernel-Ref.-Manual about
	   blocks). */
	if ((!len && Clen) || len_in_blocks != clen_in_blocks) {
	    if (ptr = allocline(Clen+1)) {

#ifdef DEBUG_MEM_H
    printf ("Allocated at %08x\n", ptr);
#endif

		ep->Modified = 1;
		Overide = 0;

		freeline (line);
		ep->List[ep->Line] = ptr;

		strcpy ((char *)ptr, (char *)Current);
	    } else {
		nomemory ();
		strcpy ((char *)Current, (char *)line);
	    }
	} else { /* Within one block ... */
	    ubyte * ptr1, * ptr2;

	    ptr1 = line;
	    ptr2 = Current;

	    /* We need not to copy stuff that's equal ! */
	    while (*ptr1 == *ptr2) {
		if (!*ptr1)
		    break;

		ptr1 ++;
		ptr2 ++;
	    }

	    if (*ptr1 != *ptr2) {
		ep->Modified = 1;
		Overide = 0;

/*printf ("Lines \nOld `%s'\nNew `%s'\ndiffer at\n%s\n%s", line, Current, ptr1, ptr2); */

		strcpy ((char *)ptr1, (char *)ptr2);
/*printf ("New Line `%s'\n", line);*/
	    } /* Any changes ? */
	} /* Old block length != new block length */
    } /* if (!Comlinemode) */

error:
    if (!Nsu) {
	if (ep->Column - ep->Topcolumn >= Columns || ep->Column < ep->Topcolumn) {
	    redraw = 1;
	    ep->Topcolumn = ep->Column - (Columns>>1);
	    if (ep->Topcolumn < 0)
		ep->Topcolumn = 0;
	}

	if (ep->Line - ep->Topline >= Rows || ep->Line < ep->Topline) {
	    redraw = 1;
	    ep->Topline = ep->Line - (Rows>>1);
	    if (ep->Topline < 0)
		ep->Topline = 0;
	}
    }

    /* pad line with spaces upto cursor */
    while (ep->Column > Clen)
	Current[Clen++] = ' ';
    Current[Clen] = '\0';

    if (redraw)
	text_redisplay ();

    return ((int)redraw);
} /* text_sync */


     /* Load current line into Current */

void text_load (void)
{
    if (Comlinemode)
	return;

    strcpy ((char *)Current, (char *)Ep->List[Ep->Line]);

    Clen = strlen ((char *)Current);

    while (Ep->Column > Clen)
	Current[Clen++] = ' ';

    Current[Clen] = '\0';
} /* text_load */


int text_colno (void)
{
    return((int)Ep->Column);
} /* text_colno */


int text_lineno (void)
{
    return((int)Ep->Line+1);
} /* text_lineno */


int text_lines (void)
{
    return((int)Ep->Lines);
} /* text_lines */


int text_cols (void)
{
    return((int)Clen);
} /* text_cols */


int text_imode (void)
{
    return((int)Ep->Insertmode);
} /* text_imode */


int text_tabsize (void)
{
    return((int)Ep->Tabstop);
} /* text_tabsize */


ubyte * text_name (void)
{
    return(Ep->Name);
} /* text_name */


void text_uninit (void)
{
    ED *ep = Ep;

    markerkill (ep);
    check_stack (ep);

    freelist (ep->List, ep->Lines);
    FreeMem (ep->List, ep->Maxlines * sizeof(LINE));

    if (ActualBlock.ep == ep) {
	ActualBlock.type = BT_NONE;
	ActualBlock.ep = NULL;
    }

    Remove ((NODE *)ep);

    if (ep->Font) {
	SetFont (ep->Win->RPort, ep->Win->WScreen->RastPort.Font);
	CloseFont (ep->Font);
    }

    UnLock ((BPTR)ep->dirlock);
    FreeMem (ep, sizeof(ED));

    if (Ep = (ED *)GetHead (&DBase)) {
	text_load ();
    }
} /* text_uninit */


void text_cursor (int n)
{
    ED	* ep   = Ep;
    RP	* rp   = ep->Win->RPort;

    movetocursor ();
    if (!inversemode (n))
	SetWrMsk (rp, -1);

    if (Current[ep->Column]) {
	Text (rp, Current+ep->Column, 1);
    } else {
	Text (rp, (STRPTR)" ", 1);
    }

    inversemode (0);

    SetBPen (rp, TEXT_BPEN);
    SetWrMsk (rp, -1);
} /* text_cursor */


void text_position (int col, int row)
{
    ED	* ep	 = Ep;
    short column,
	  line;

    text_sync ();

    column = ep->Topcolumn + col;

    if (column > MAXLINELEN-1)
	column = MAXLINELEN-1;

    if (column < 0)
	column = 0;

    line = ep->Topline + row;

    if (line >= ep->Lines)
	line = ep->Lines - 1;

    if (line < 0)
	line = 0;

    ep->Column = column;
    ep->Line = line;

    text_load ();
    text_sync ();
} /* text_position */


     /* This routine cried for beeing rewritten. It is now MUCH more straight-
	forward. Especially the for-loop has been improved. */

void text_displayseg (int start, int n)
{
    short    i,
	     c,
	     x,
	     y,
	     end,
	     top,
	     topcol,
	     test;
    ubyte ** ptr,
	   * hptr;
    ED	   * ep    = Ep;
    RP	   * rp    = ep->Win->RPort;

    if (Nsu)
	return;

    topcol = ep->Topcolumn;

    if (Comlinemode) {
	/* Clear back */
	SetAPen (rp, TEXT_BPEN);
	RectFill (rp, COL(0), ROWT(ep->Line - ep->Topline), Xpixs, Ypixs);

	/* Write text */
	SetAPen (rp, TEXT_FPEN);

	c = Clen - topcol;

	if (c > 0) {
	    Move (rp, COLT(0), ROWT(ep->Line - ep->Topline));
	    Text (rp, Current + topcol, (c > Columns) ? Columns : c);
	}
    } else {
	n += start;
	if (n > Rows) n = Rows;
	if (n > (ep->Lines - ep->Topline)) n = ep->Lines - ep->Topline;
	n -= start;
	if (n <= 0) return;

	top = ep->Topline + start;
	end = top + n - 1;
	ptr = ep->List + top;

	SetDrMd (rp, JAM1);

	if (ActualBlock.ep == ep && ((ActualBlock.start_line >= top && ActualBlock.start_line <= end) ||
		    (ActualBlock.end_line >= top && ActualBlock.end_line <= end) ||
		    (ActualBlock.start_line <  top && ActualBlock.end_line >  end)) ) {
	    x = ActualBlock.start_line - ep->Topline;

	    if (x < start)
		x = start;

	    end = ActualBlock.end_line - top;

	    test = 0;

	    if (end >= n) {
		if (end == n)
		    test = 1;

		end = start+n;
	    } else
		end += start+1;

	    if (x < end) {
		SetAPen (rp, BLOCK_BPEN);
		SetWrMsk (rp, BLOCK_MASK);

		switch (ActualBlock.type) {
		    case BT_LINE:
			RectFill (rp, Xbase, ROW(x), Xpixs, ROW(end)-1);
			break;

#ifdef NOTDEF
		    case BT_NORMAL: {
			short x1, x2;

			x1 = ActualBlock.start_column - ep->Topcolumn;
			x2 = ActualBlock.end_column - ep->Topcolumn;

			if (x == start) {
			    if (test) { /* Block is only one line */
				if (x1 < 0)
				    x1 = 0;

				if (x1 < Rows && x2 > 0)
				    RectFill (rp, COL(x1), ROW(x), COL(x2),
					    ROW(end)-1);
			    } else { /* Block only starts here */
				if (x1 < 0) { /* Block startes to the left of
						    the current window */
				    RectFill (rp, Xbase, ROW(x), Xpixs,
					    ROW(end)-1);
				} else { /* maybe we don't need to draw the 1. line */
				    if (x1 < Rows) /* No, 1. line is visible */
					RectFill (rp, COL(x1), ROW(x),
						Xpixs, ROW(x+1)-1);

				    x ++; /* next line */

				    if (x < end) /* show rest of block (if possible) */
					RectFill (rp, Xbase, ROW(x), Xpixs,
						ROW(end)-1);
				}
			    } /* if (test) */
			} else {
			    if (test) { /* Block ends here */
				if (x2 >= Rows) { /* Block startes to the left of
						    the current window */
				    RectFill (rp, Xbase, ROW(x), Xpixs,
					    ROW(end)-1);
				} else { /* maybe we don't need to draw the 1. line */
				    if (x2 > 0) /* No, 1. line is visible */
					RectFill (rp, Xbase, ROW(x),
						COL(x2), ROW(x+1)-1);

				    x ++; /* next line */

				    if (x < end) /* show rest of block (if possible) */
					RectFill (rp, Xbase, ROW(x), Xpixs,
						ROW(end)-1);
				}
			    } else { /* mid of block */
				RectFill (rp, Xbase, ROW(x), Xpixs,
					ROW(end)-1);
			    } /* if (test) */
			} /* if (x == start) */

			break; } /* BT_NORMAL */

		    case BT_VERTICAL: {
			short x1, x2;

			x1 = ActualBlock.start_column - ep->Topcolumn;
			x2 = ActualBlock.end_column - ep->Topcolumn;

			if (x1 < Rows && x2 > 0) { /* is block visible ? */
			    if (x1 < 0)
				x1 = 0;

			    if (x2 >= Rows)
				x2 = Rows-1;

			    RectFill (rp, COL(x1), ROW(x), COL(x2), ROW(end)-1);
			}

			break; } /* BT_VERTICAL */
#endif
		} /* switch (ActualBlock.type) */
	    } /* if Can_I_draw */
	} /* if in_block */

	y = ROWT(start);

	for (i=0; i < n; i++, ptr ++, y += Ysize) {
	    x = XTbase;

	    setpen (top);

	    top ++;

	    hptr = *ptr;

	    c = strlen ((char *)hptr) - topcol;

	    if (c > 0) {
		hptr += topcol;

		if (c > Columns)
		    c = Columns;

		while (*hptr == ' ' && c) {
		    hptr ++;
		    c --;
		    x += Xsize;
		}

		if (c) {
		    Move (rp, x, y);
		    Text (rp, hptr, c);
		} /* if (c) */
	    } /* if (c > 0) */
	} /* for (...) */

	prop_adj ();
    } /* !Comlinemode */

    SetBPen (rp, TEXT_BPEN);
    SetWrMsk (rp, -1);
} /* text_displayseg */


void text_redisplay (void)
{
    ED * ep = Ep;
    RP * rp = ep->Win->RPort;

    if (Nsu)
	return;

    SetAPen (rp, TEXT_BPEN);

    if (Comlinemode)
	RectFill (rp, COL(0), ROW(Rows-1), Xpixs, Ypixs);
    else
	RectFill (rp, Xbase, Ybase, Xpixs, Ypixs);

    text_displayseg (0,Rows);
} /* text_redisplay */


void text_redisplaycurrline (void)
{
    ED * ep  = Ep;
    RP * rp  = ep->Win->RPort;
    int  row = ep->Line - ep->Topline;

    if (Nsu)
	return;

    SetAPen (rp, TEXT_BPEN);
    RectFill (rp, COL(0), ROW(row), Xpixs, ROW(row+1)-1);

    text_displayseg (row, 1);
} /* text_redisplaycurrline */


void text_write (ubyte * str)
{
    short len = strlen ((char *)str);
    short i;
    ED	* ep  = Ep;
    RP	* rp  = ep->Win->RPort;

    if (Clen + len >= MAXLINELEN-1) {
	text_sync ();
	text_load ();
    }

    if (ep->Insertmode == 0) {
	if (ep->Column + len >= MAXLINELEN-1)
	    goto fail;

	movmem (str, Current + ep->Column, len);

	if (ep->Column + len >= Clen)
	    Clen = ep->Column + len;

	Current[Clen] = 0;
    } else {
	if (Clen + len >= MAXLINELEN-1)
	    goto fail;

	movmem (Current + ep->Column, Current + ep->Column + len,
		Clen+1-ep->Column);
	movmem (str, Current + ep->Column, len);
	Clen += len;

	if (len < Columns - (ep->Column - ep->Topcolumn)) {
	    i = ep->Line - ep->Topline;

	    ScrollRaster (rp, -len * Xsize, 0 ,
		COL(ep->Column - ep->Topcolumn),
		ROW(i),
		Xpixs,
		ROW(i + 1) - 1);
	}
    }

    i = ep->Column - ep->Topcolumn;
    i = ((i + len) > Columns) ? Columns - i : len;

    setpen (ep->Line);
    movetocursor ();
    Text (rp, str, i);
    SetWrMsk (rp, -1);

    ep->Column += len;

    if (ep->Column - ep->Topcolumn >= Columns)
	text_sync ();

fail:
    if (Comlinemode == 0) {
	if (ep->Wordwrap)
	    do_reformat(0);
	else if (ep->Autosplit) {
	    short thislen;	    /* Length of this line */
	    short nextlen;	    /* Length of next line */
	    short wordlen;	    /* Length of word */
	    char * ptr;

	    /* Check if we have to break */
	    thislen = strlen ((char *)Current);

	    if (thislen > ep->Margin && ep->Column == thislen) {
		/* Well, lets start.
		   This algorithm is very simple for now. It does work only,
		   when the cursor is the end of the line and copies the last
		   word in the next line.
		*/
		wordlen = thislen - 1;
		ptr = (char *)Current + wordlen;

		while (*ptr != ' ' && wordlen) {
		    ptr --;
		    wordlen --;
		}

		/* If we found a space, we are 1 char wrong */
		if (wordlen) {
		    ptr ++;
		    wordlen ++;
		}

		/* Calc. length of last word */
		wordlen = thislen - wordlen;

		/* Do nothing if this word won't fit in the next line */
		if (wordlen < ep->Margin) {
		    /* Set cursor at the beginning of the word,
		       split line there, go down and go to last
		       char in this line. */
		    ep->Column -= wordlen;
		    do_split ();
		    do_down ();
		    do_lastcolumn ();
		} /* if does word fit */
	    } /* if "Do we have to split ?" */
	} /* if Autosplit */
    } /* if !Comlinemode */
} /* text_write */


/******************************************************************************
*****  ENDE text.c
******************************************************************************/
