/*
 * File: textwindow.c
 *
 * This file implements a very simple textwindow facility based on the 
 * SUNVIEW subwindow type Canvas. It was originally written on a SUN 
 * running SUNOS 3.4 because using the Text subwindow type which is 
 * predefined in SUNVIEW increased the executable file about 150K! 
 * 
 * I chose to keep it despite the shared libraries of SUNOS 4.0 
 * because it will be very easy to isolate all calls to SUNVIEW 
 * functions and move them to sunview.c. The corresponding calls
 * for e.g. X11 could be placed in a file x11.c and the textwindows
 * could be used as a common denominator between the interfaces and
 * thus make porting easier.
 */

#include <suntool/sunview.h>
#include <suntool/canvas.h>
#include <stdio.h>
#include <string.h>
#ifdef __GNUC__
#include "clib.h"
#endif
#include "textwindow.h"


#define MAX(a, b)  ((a) < (b) ? (b) : (a))
#define MIN(a, b)  ((a) > (b) ? (b) : (a))


/* External declarations. These should be declared in <suntool/sunview.h> */
extern void   pw_text();
extern void   pw_char();



/*
 * Create a textwindow using the text font FONT and the drawing 
 * canvas CANVAS. Each character of FONT is CHARWIDTH pixels wide
 * and CHARHEIGHT pixels hide. Make the window (WIDTH, HEIGHT)
 * characters wide and high.
 */

Textwindow *
textwindow_create(canvas, font,
		  charwidth, charheight,
		  width, height)
Canvas     canvas;
Pixfont  * font;
int        charwidth;
int        charheight;
int        width;
int        height;
{
    Textwindow  * win;
    int           i;

    /* Allocate memory for the Textwindow structure. */
    win = (Textwindow *) malloc(sizeof(Textwindow));
    if (win == NULL) {
	fprintf(stderr, "Couldn't allocate space for a textwindow.");
	exit(1);
    }

    /* Fill in the fields of the struct. The cursor starts at (0, 0). */
    win->canvas = canvas;
    win->font   = font;
    win->charheight = charheight;
    win->charwidth = charwidth;
    win->height = height;
    win->width = width;
    win->cur_row = 0;
    win->cur_col = 0;

    /* Now allocate a buffer for holding the characters of the window, */
    /* and reset them all to holding lines of WIDTH spaces. Each line */
    /* is its own string. */
    win->buffer = malloc((height + 1) * (width + 1));
    for (i = 0; i < (height + 1) * (width + 1); ++i)
	*(win->buffer + i) =  (i % (width + 1) != width)  ? ' ' : '\0';

    /* Set the default scroll region to be the whole window. */
    win->scroll_begin = 0;
    win->scroll_end = height - 1;

    return win;
}




/*
 * Scroll a textwindow. The scroll region of the window determines
 * which lines are to be scrolled.
 */

void
textwindow_scroll(win)
Textwindow  * win;
{
    int   row;
    int   i;

    /* Copy the strings to the correct lines and write them to the window. */
    for (row = win->scroll_begin; row < win->scroll_end; ++row) {
	strncpy(win->buffer + (win->width + 1) * row,
		win->buffer + (win->width + 1) * (row + 1),
		win->width);
	pw_text(canvas_pixwin(win->canvas),
		0, -1 + (row + 1) * win->charheight, PIX_SRC,
		win->font, win->buffer + (win->width + 1) * row);
    }

    /* Empty the last line of the scroll region and write it. */
    for (i = 0; i < win->width; ++i)
	*(win->buffer + win->scroll_end * (win->width + 1) + i) = ' ';
    pw_text(canvas_pixwin(win->canvas),
	    0, -1 + (win->scroll_end + 1) * win->charheight,
	    PIX_SRC, win->font,
	    win->buffer + win->scroll_end * (win->width + 1));
}



/*
 * Print the string STR in textwindow WIN. The string will be printed
 * at the current cursor position and the cursor position will be
 * updated to point at the end of the newly written string. If the
 * string goes beyond the scroll region, the window will scroll.
 *
 * A thought: There seems to be a bug here. What happens if the 
 * cursor is set to point somewhere after the scroll region?
 */

void
textwindow_print(win, str)
Textwindow  * win;
char        * str;
{
    /* Write out the characters one by one and scroll if necesary. */
    /* If there is a newline in the string, the next character is */
    /* printed at the beginning of the next line. */
    while (*str) {

	/* Take care of newlines. */
	if (*str == '\n') {
	    win->cur_col = 0;
	    if (win->cur_row++ == win->scroll_end) {
		--win->cur_row;
		textwindow_scroll(win);
	    }
	    ++str;
	    
	} else if (*str == '\r') {

	    /* Carriage Return => goto beginning of line. */
	    win->cur_col = 0;

	} else {
	    *(win->buffer + win->cur_row * (win->width + 1) + win->cur_col)
		= *str;
	    pw_char(canvas_pixwin(win->canvas),
		    win->cur_col++ * win->charwidth,
		    -1 + (win->cur_row + 1) * win->charheight,
		    PIX_SRC, win->font, *str++);
	    
	    /* If we are at the end of a line, then wrap around to the */
	    /* next line. */
	    if (win->cur_col == win->width) {
		win->cur_col = 0;
		if (win->cur_row++ == win->scroll_end) {
		    --win->cur_row;
		    textwindow_scroll(win);
		}
	    }
	}
    }
}



/*
 * Place the cursor at (ROW, COL) in the textwindow WIN. The coordinates
 * are bounded to the limits of the window.
 */

void
textwindow_putcur(win, row, col)
Textwindow  * win;
int           row;
int           col;
{
    row = MAX(row, 0);
    col = MAX(col, 0);
    win->cur_row = MIN(row, win->height - 1);
    win->cur_col = MIN(col, win->width - 1);
}



/*
 * Define a new scroll region for the window WIN. BEGIN is the
 * first line of the new region and END is the last one. The line
 * numbering starts at 0.
 */

void
textwindow_set_scrollregion(win, begin, end)
Textwindow  * win;
int           begin;
int           end;
{
    int   temp;

    /* Place begin and end within the window. */
    begin = MAX(begin, 0);
    end = MAX(end, 0);
    begin= MIN(begin, win->height - 1);
    end = MIN(end, win->height - 1);
    
    if (begin > end) {
	temp = begin;
	begin = end;
	end = temp;
    }

    win->scroll_begin = begin;
    win->scroll_end = end;
}



/*
 * Return the current row for the cursor in TW.
 */

int
textwindow_get_currow(tw)
Textwindow  * tw;
{
    return tw->cur_row;
}



/*
 * Return the current column for the cursor in TW.
 */

int
textwindow_get_curcol(tw)
Textwindow  * tw;
{
    return tw->cur_col;
}
