/* $Id$ */
/**************************************************************************
 *   winio.c                                                              *
 *                                                                        *
 *   Copyright (C) 1999 Chris Allegretta                                  *
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
 *   the Free Software Foundation; either version 1, or (at your option)  *
 *   any later version.                                                   *
 *                                                                        *
 *   This program is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 *   GNU General Public License for more details.                         *
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include "config.h"
#include "proto.h"
#include "nano.h"

#ifndef NANO_SMALL
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif

static int statblank = 0;	/* Number of keystrokes left after
				   we call statubar() before we
				   actually blank the statusbar */

/* Local Function Prototypes for only winio.c */
inline int get_page_from_virtual(int virtual);
inline int get_page_start_virtual(int page);
inline int get_page_end_virtual(int page);

/* Window I/O */

int do_first_line(void)
{
    current = fileage;
    placewewant = 0;
    current_x = 0;
    edit_update(current, CENTER);
    return 1;
}

int do_last_line(void)
{
    current = filebot;
    placewewant = 0;
    current_x = 0;
    edit_update(current, CENTER);
    return 1;
}

/* Like xplustabs, but for a specifc index of a speficific filestruct */
int xpt(filestruct * fileptr, int index)
{
    int i, tabs = 0;

    if (fileptr == NULL || fileptr->data == NULL)
	return 0;

    for (i = 0; i < index && fileptr->data[i] != 0; i++) {
	tabs++;

	if (fileptr->data[i] == NANO_CONTROL_I) {
	    if (tabs % tabsize == 0);
	    else
		tabs += tabsize - (tabs % tabsize);
	} else if (fileptr->data[i] & 0x80)
	    /* Make 8 bit chars only 1 collumn! */
	    ;
	else if (fileptr->data[i] < 32)
	    tabs++;
    }

    return tabs;
}


/* Return the actual place on the screen of current->data[current_x], which 
   should always be > current_x */
int xplustabs(void)
{
    return xpt(current, current_x);
}


/* Return what current_x should be, given xplustabs() for the line, 
 * given a start position in the filestruct's data */
int actual_x_from_start(filestruct * fileptr, int xplus, int start)
{
    int i, tot = 1;

    if (fileptr == NULL || fileptr->data == NULL)
	return 0;

    for (i = start; tot <= xplus && fileptr->data[i] != 0; i++, tot++)
	if (fileptr->data[i] == NANO_CONTROL_I) {
	    if (tot % tabsize == 0)
		tot++;
	    else
		tot += tabsize - (tot % tabsize);
	} else if (fileptr->data[i] & 0x80)
	    tot++;		/* Make 8 bit chars only 1 column (again) */
	else if (fileptr->data[i] < 32)
	    tot += 2;

#ifdef DEBUG
    fprintf(stderr, _("actual_x_from_start for xplus=%d returned %d\n"),
	    xplus, i);
#endif
    return i - start;
}

/* Opposite of xplustabs */
inline int actual_x(filestruct * fileptr, int xplus)
{
    return actual_x_from_start(fileptr, xplus, 0);
}

/* a strlen with tabs factored in, similar to xplustabs() */
int strlenpt(char *buf)
{
    int i, tabs = 0;

    if (buf == NULL)
	return 0;

    for (i = 0; buf[i] != 0; i++) {
	tabs++;

	if (buf[i] == NANO_CONTROL_I) {
	    if (tabs % tabsize == 0);
	    else
		tabs += tabsize - (tabs % tabsize);
	} else if (buf[i] & 0x80)
	    /* Make 8 bit chars only 1 collumn! */
	    ;
	else if (buf[i] < 32)
	    tabs++;
    }

    return tabs;
}


/* resets current_y based on the position of current and puts the cursor at 
   (current_y, current_x) */
void reset_cursor(void)
{
    filestruct *ptr = edittop;
    int x;

    current_y = 0;

    while (ptr != current && ptr != editbot && ptr->next != NULL) {
	ptr = ptr->next;
	current_y++;
    }

    x = xplustabs();
    if (x <= COLS - 2)
	wmove(edit, current_y, x);
    else
	wmove(edit, current_y, x -
	      get_page_start_virtual(get_page_from_virtual(x)));

}

void blank_bottombars(void)
{
    int i = no_help()? 3 : 1;

    for (; i <= 2; i++)
	mvwaddstr(bottomwin, i, 0, hblank);

}

void blank_edit(void)
{
    int i;
    for (i = 0; i <= editwinrows - 1; i++)
	mvwaddstr(edit, i, 0, hblank);
    wrefresh(edit);
}


void blank_statusbar(void)
{
    mvwaddstr(bottomwin, 0, 0, hblank);
}

void blank_statusbar_refresh(void)
{
    blank_statusbar();
    wrefresh(bottomwin);
}

void check_statblank(void)
{

    if (statblank > 1)
	statblank--;
    else if (statblank == 1 && !ISSET(CONSTUPDATE)) {
	statblank--;
	blank_statusbar_refresh();
    }
}

/* Get the input from the kb, this should only be called from statusq */
int nanogetstr(char *buf, char *def, shortcut s[], int slen, int start_x)
{
    int kbinput = 0, j = 0, x = 0, xend;
    int x_left = 0;
    char inputstr[132], inputbuf[132] = "";

    blank_statusbar();
    mvwaddstr(bottomwin, 0, 0, buf);
    if (strlen(def) > 0)
	waddstr(bottomwin, def);
    wrefresh(bottomwin);

    x_left = strlen(buf);
    x = strlen(def) + x_left;

    /* Get the input! */
    if (strlen(def) > 0) {
	strcpy(answer, def);
	strcpy(inputbuf, def);
    }

    while ((kbinput = wgetch(bottomwin)) != 13) {
	for (j = 0; j <= slen - 1; j++) {
	    if (kbinput == s[j].val) {
		strcpy(answer, "");
		return s[j].val;
	    }
	}
	xend = strlen(buf) + strlen(inputbuf);

	switch (kbinput) {
	case KEY_HOME:
	    x = x_left;
	    blank_statusbar();
	    mvwaddstr(bottomwin, 0, 0, buf);
	    waddstr(bottomwin, inputbuf);
	    wmove(bottomwin, 0, x);
	    break;
	case KEY_END:
	    x = x_left + strlen(inputbuf);
	    blank_statusbar();
	    mvwaddstr(bottomwin, 0, 0, buf);
	    waddstr(bottomwin, inputbuf);
	    wmove(bottomwin, 0, x);
	    break;
	case KEY_RIGHT:

	    if (x < xend)
		x++;
	    wmove(bottomwin, 0, x);
	    break;
	case NANO_CONTROL_D:
	    if (strlen(inputbuf) > 0 && (x - x_left) != strlen(inputbuf)) {
		memmove(inputbuf + (x - x_left),
			inputbuf + (x - x_left) + 1,
			strlen(inputbuf) - (x - x_left) - 1);
		inputbuf[strlen(inputbuf) - 1] = 0;
	    }
	    blank_statusbar();
	    mvwaddstr(bottomwin, 0, 0, buf);
	    waddstr(bottomwin, inputbuf);
	    wmove(bottomwin, 0, x);
	    break;
	case NANO_CONTROL_K:
	case NANO_CONTROL_U:
	    *inputbuf = 0;
	    x = x_left;
	    blank_statusbar();
	    mvwaddstr(bottomwin, 0, 0, buf);
	    waddstr(bottomwin, inputbuf);
	    wmove(bottomwin, 0, x);
	    break;
	case KEY_BACKSPACE:
	case KEY_DC:
	case 127:
	case NANO_CONTROL_H:
	    if (strlen(inputbuf) > 0) {
		if (x == (x_left + strlen(inputbuf)))
		    inputbuf[strlen(inputbuf) - 1] = 0;
		else if (x - x_left) {
		    memmove(inputbuf + (x - x_left) - 1,
			    inputbuf + (x - x_left),
			    strlen(inputbuf) - (x - x_left));
		    inputbuf[strlen(inputbuf) - 1] = 0;
		}
	    }
	    blank_statusbar();
	    mvwaddstr(bottomwin, 0, 0, buf);
	    waddstr(bottomwin, inputbuf);
	case KEY_LEFT:
	    if (x > strlen(buf))
		x--;
	    wmove(bottomwin, 0, x);
	    break;
	case KEY_UP:
	case KEY_DOWN:
	    break;

	case 27:
	    switch (kbinput = wgetch(edit)) {
	    case 79:
		switch (kbinput = wgetch(edit)) {
		case 70:
		    x = x_left + strlen(inputbuf);
		    blank_statusbar();
		    mvwaddstr(bottomwin, 0, 0, buf);
		    waddstr(bottomwin, inputbuf);
		    wmove(bottomwin, 0, x);
		    break;
		case 72:
		    x = x_left;
		    blank_statusbar();
		    mvwaddstr(bottomwin, 0, 0, buf);
		    waddstr(bottomwin, inputbuf);
		    wmove(bottomwin, 0, x);
		    break;
		}
		break;
	    case 91:
		switch (kbinput = wgetch(edit)) {
		case 'C':
		    if (x < xend)
			x++;
		    wmove(bottomwin, 0, x);
		    break;
		case 'D':
		    if (x > strlen(buf))
			x--;
		    wmove(bottomwin, 0, x);
		    break;
		case 49:
		    x = x_left;
		    blank_statusbar();
		    mvwaddstr(bottomwin, 0, 0, buf);
		    waddstr(bottomwin, inputbuf);
		    wmove(bottomwin, 0, x);
		    goto skip_126;
		case 51:
		    if (strlen(inputbuf) > 0
			&& (x - x_left) != strlen(inputbuf)) {
			memmove(inputbuf + (x - x_left),
				inputbuf + (x - x_left) + 1,
				strlen(inputbuf) - (x - x_left) - 1);
			inputbuf[strlen(inputbuf) - 1] = 0;
		    }
		    blank_statusbar();
		    mvwaddstr(bottomwin, 0, 0, buf);
		    waddstr(bottomwin, inputbuf);
		    wmove(bottomwin, 0, x);
		    goto skip_126;
		case 52:
		    x = x_left + strlen(inputbuf);
		    blank_statusbar();
		    mvwaddstr(bottomwin, 0, 0, buf);
		    waddstr(bottomwin, inputbuf);
		    wmove(bottomwin, 0, x);
		    goto skip_126;
		  skip_126:
		    nodelay(edit, TRUE);
		    kbinput = wgetch(edit);
		    if (kbinput == 126 || kbinput == ERR)
			kbinput = -1;
		    nodelay(edit, FALSE);
		    break;
		}
	    }
	    blank_statusbar();
	    mvwaddstr(bottomwin, 0, 0, buf);
	    waddstr(bottomwin, inputbuf);
	    wmove(bottomwin, 0, x);
	    break;


	default:
	    if (kbinput < 32)
		break;
	    strcpy(inputstr, inputbuf);
	    inputstr[x - strlen(buf)] = kbinput;
	    strcpy(&inputstr[x - strlen(buf) + 1],
		   &inputbuf[x - strlen(buf)]);
	    strcpy(inputbuf, inputstr);
	    x++;

	    mvwaddstr(bottomwin, 0, 0, buf);
	    waddstr(bottomwin, inputbuf);
	    wmove(bottomwin, 0, x);

#ifdef DEBUG
	    fprintf(stderr, _("input \'%c\' (%d)\n"), kbinput, kbinput);
#endif
	}
	wrefresh(bottomwin);
    }

    strncpy(answer, inputbuf, 132);

    if (!strcmp(answer, ""))
	return -2;
    else
	return 0;
}

void horizbar(WINDOW * win, int y)
{
    wattron(win, A_REVERSE);
    mvwaddstr(win, 0, 0, hblank);
    wattroff(win, A_REVERSE);
}

void titlebar(void)
{
    int namelen, space;

    horizbar(topwin, 0);
    wattron(topwin, A_REVERSE);
    mvwaddstr(topwin, 0, 3, VERMSG);

    space = COLS - strlen(VERMSG) - strlen(VERSION) - 21;

    namelen = strlen(filename);

    if (!strcmp(filename, ""))
	mvwaddstr(topwin, 0, center_x - 6, _("New Buffer"));
    else {
	if (namelen > space) {
	    waddstr(topwin, _("  File: ..."));
	    waddstr(topwin, &filename[namelen - space]);
	} else {
	    mvwaddstr(topwin, 0, center_x - (namelen / 2 + 1), "File: ");
	    waddstr(topwin, filename);
	}
    }
    if (ISSET(MODIFIED))
	mvwaddstr(topwin, 0, COLS - 10, _("Modified"));
    wattroff(topwin, A_REVERSE);
    wrefresh(topwin);
    reset_cursor();
}

void onekey(char *keystroke, char *desc)
{
    char description[80];

    snprintf(description, 13, " %-11s", desc);
    wattron(bottomwin, A_REVERSE);
    waddstr(bottomwin, keystroke);
    wattroff(bottomwin, A_REVERSE);
    waddstr(bottomwin, description);
}

void clear_bottomwin(void)
{
    if (ISSET(NO_HELP))
	return;

    mvwaddstr(bottomwin, 1, 0, hblank);
    mvwaddstr(bottomwin, 2, 0, hblank);
}

void bottombars(shortcut s[], int slen)
{
    int i, j, k;
    char keystr[10];

    if (ISSET(NO_HELP))
	return;

    /* Determine how many extra spaces are needed to fill the bottom of the screen */
    k = COLS / 6 - 13;

    clear_bottomwin();
    wmove(bottomwin, 1, 0);
    for (i = 0; i <= slen - 1; i += 2) {
	snprintf(keystr, 10, "^%c", s[i].val + 64);
	onekey(keystr, s[i].desc);

	for (j = 0; j < k; j++)
	    waddch(bottomwin, ' ');
    }

    wmove(bottomwin, 2, 0);
    for (i = 1; i <= slen - 1; i += 2) {
	snprintf(keystr, 10, "^%c", s[i].val + 64);
	onekey(keystr, s[i].desc);

	for (j = 0; j < k; j++)
	    waddch(bottomwin, ' ');
    }

    wrefresh(bottomwin);

}

/* If modified is not already set, set it and update titlebar */
void set_modified(void)
{
    if (!ISSET(MODIFIED)) {
	SET(MODIFIED);
	titlebar();
	wrefresh(topwin);
    }
}

/* And so start the display update routines */
/* Given a column, this returns the "page" it is on  */
/* "page" in the case of the display columns, means which set of 80 */
/* characters is viewable (ie: page 1 shows from 1 to COLS) */
inline int get_page_from_virtual(int virtual)
{
    int page = 2;

    if (virtual <= COLS - 2)
	return 1;
    virtual -= (COLS - 2);

    while (virtual > COLS - 2 - 7) {
	virtual -= (COLS - 2 - 7);
	page++;
    }

    return page;
}

/* The inverse of the above function */
inline int get_page_start_virtual(int page)
{
    int virtual;
    virtual = --page * (COLS - 7);
    if (page)
	virtual -= 2 * page - 1;
    return virtual;
}

inline int get_page_end_virtual(int page)
{
    return get_page_start_virtual(page) + COLS - 1;
}

#ifndef NANO_SMALL
/* This takes care of the case where there is a mark that covers only */
/* the current line. */

/* It expects a line with no tab characers (ie: the type that edit_add */
/* deals with */
void add_marked_sameline(int begin, int end, filestruct * fileptr, int y,
			 int virt_cur_x, int this_page)
{
    /*
     * The general idea is to break the line up into 3 sections: before
     * the mark, the mark, and after the mark.  We then paint each in
     * turn (for those that are currently visible, of course
     *
     * 3 start points: 0 -> begin, begin->end, end->strlen(data)
     *    in data    :    pre          sel           post        
     */
    int this_page_start = get_page_start_virtual(this_page),
	this_page_end = get_page_end_virtual(this_page);

    /* likewise, 3 data lengths */
    int pre_data_len = begin, sel_data_len = end - begin, post_data_len = 0;	/* Determined from the other two */

    /* now fix the start locations & lengths according to the cursor's 
     * position (ie: our page) */
    if (pre_data_len < this_page_start)
	pre_data_len = 0;
    else
	pre_data_len -= this_page_start;

    if (begin < this_page_start)
	begin = this_page_start;

    if (end < this_page_start)
	end = this_page_start;

    if (begin > this_page_end)
	begin = this_page_end;

    if (end > this_page_end)
	end = this_page_end;

    /* Now calculate the lengths */
    sel_data_len = end - begin;
    post_data_len = this_page_end - end;

    /* Paint this line! */
    mvwaddnstr(edit, y, 0, &fileptr->data[this_page_start], pre_data_len);
    wattron(edit, A_REVERSE);
    mvwaddnstr(edit, y, begin - this_page_start,
	       &fileptr->data[begin], sel_data_len);
    wattroff(edit, A_REVERSE);
    mvwaddnstr(edit, y, end - this_page_start,
	       &fileptr->data[end], post_data_len);
}
#endif

/* edit_add takes care of the job of actually painting a line into the
 * edit window.
 * 
 * Called only from update_line.  Expects a converted-to-not-have-tabs
 * line */
void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x,
	      int virt_mark_beginx, int this_page)
{
#ifndef NANO_SMALL
    /* There are quite a few cases that could take place, we'll deal
     * with them each in turn */
    if (ISSET(MARK_ISSET)    
	&& !((fileptr->lineno > mark_beginbuf->lineno
	     && fileptr->lineno > current->lineno)
	    || (fileptr->lineno < mark_beginbuf->lineno
		&& fileptr->lineno < current->lineno)))
    {
	    /* If we get here we are on a line that is atleast
	     * partially selected.  The lineno checks above determined
	     * that */
	    if (fileptr != mark_beginbuf && fileptr != current) {
		/* We are on a completely marked line, paint it all
		 * inverse */
		wattron(edit, A_REVERSE);
		mvwaddnstr(edit, yval, 0, fileptr->data, COLS);
		wattroff(edit, A_REVERSE);
	    } else if (fileptr == mark_beginbuf && fileptr == current) {
		/* Special case, we're still on the same line we started
		 * marking -- so we call our helper function */
		if (virt_cur_x < virt_mark_beginx) {
		    /* To the right of us is marked */
		    add_marked_sameline(virt_cur_x, virt_mark_beginx,
					fileptr, yval, virt_cur_x,
					this_page);
		} else {
		    /* To the left of us is marked */
		    add_marked_sameline(virt_mark_beginx, virt_cur_x,
					fileptr, yval, virt_cur_x,
					this_page);
		}
	    } else if (fileptr == mark_beginbuf) {
		/*
		 * we're updating the line that was first marked
		 * but we're not currently on it.  So we want to
		 * figur out which half to invert based on our
		 * relative line numbers.
		 *
		 * i.e. If we're above the "beginbuf" line, we want to
		 * mark the left side.  Otherwise we're below, so we
		 * mark the right
		 */
		int target;

		if (mark_beginbuf->lineno > current->lineno)
		    wattron(edit, A_REVERSE);

		target =
		    (virt_mark_beginx <
		     COLS - 1) ? virt_mark_beginx : COLS - 1;

		mvwaddnstr(edit, yval, 0, fileptr->data, target);

		if (mark_beginbuf->lineno < current->lineno)
		    wattron(edit, A_REVERSE);
		else
		    wattroff(edit, A_REVERSE);

		target = (COLS - 1) - virt_mark_beginx;
		if (target < 0)
		    target = 0;

		mvwaddnstr(edit, yval, virt_mark_beginx,
			   &fileptr->data[virt_mark_beginx], target);

		if (mark_beginbuf->lineno < current->lineno)
		    wattroff(edit, A_REVERSE);

	    } else if (fileptr == current) {
		/* we're on the cursors line, but it's not the first
		 * one we marked.  Similar to the previous logic. */
		int this_page_start = get_page_start_virtual(this_page),
		    this_page_end = get_page_end_virtual(this_page);

		if (mark_beginbuf->lineno < current->lineno)
		    wattron(edit, A_REVERSE);

		if (virt_cur_x > COLS - 2) {
		    mvwaddnstr(edit, yval, 0,
			       &fileptr->data[this_page_start],
			       virt_cur_x - this_page_start);
		} else {
		    mvwaddnstr(edit, yval, 0, fileptr->data, virt_cur_x);
		}

		if (mark_beginbuf->lineno > current->lineno)
		    wattron(edit, A_REVERSE);
		else
		    wattroff(edit, A_REVERSE);

		if (virt_cur_x > COLS - 2)
		    mvwaddnstr(edit, yval, virt_cur_x - this_page_start,
			       &fileptr->data[virt_cur_x],
			       this_page_end - virt_cur_x);
		else
		    mvwaddnstr(edit, yval, virt_cur_x,
			       &fileptr->data[virt_cur_x],
			       COLS - virt_cur_x);

		if (mark_beginbuf->lineno > current->lineno)
		    wattroff(edit, A_REVERSE);
	}

    } else
#endif
    /* Just paint the string (no mark on this line) */
	mvwaddnstr(edit, yval, 0, &fileptr->data[start],
		   get_page_end_virtual(this_page) - start);
}

/*
 * Just update one line in the edit buffer.  Basically a wrapper for
 * edit_add
 *
 * index gives is a place in the string to update starting from.
 * Likely args are current_x or 0.
 */
void update_line(filestruct * fileptr, int index)
{
    filestruct *filetmp;
    int line = 0, col = 0;
    int virt_cur_x = current_x, virt_mark_beginx = mark_beginx;
    char *realdata, *tmp;
    int i, pos, len, page;

    if (!fileptr)
	return;

    /* First, blank out the line (at a minimum) */
    for (filetmp = edittop; filetmp != fileptr && filetmp != editbot;
	 filetmp = filetmp->next)
	line++;

    mvwaddstr(edit, line, 0, hblank);

    /* Next, convert all the tabs to spaces so everything else is easy */
    index = xpt(fileptr, index);

    realdata = fileptr->data;
    len = strlen(realdata);
    fileptr->data = nmalloc(xpt(fileptr, len) + 1);

    pos = 0;
    for (i = 0; i < len; i++) {
	if (realdata[i] == '\t') {
	    do {
		fileptr->data[pos++] = ' ';
		if (i < current_x)
		    virt_cur_x++;
		if (i < mark_beginx)
		    virt_mark_beginx++;
	    } while (pos % tabsize);
	    /* must decrement once to account for tab-is-one-character */
	    if (i < current_x)
		virt_cur_x--;
	    if (i < mark_beginx)
		virt_mark_beginx--;
	} 
	else if (realdata[i] >= 1 && realdata[i] <= 26) {
	/* Treat control characters as ^letter */
	    fileptr->data[pos++] = '^';
	    fileptr->data[pos++] = realdata[i] + 64;
	} else {
	    fileptr->data[pos++] = realdata[i];
	}
    }

    fileptr->data[pos] = '\0';

    /* Now, Paint the line */
    if (current == fileptr && index > COLS - 2) {
	/* This handles when the current line is beyond COLS */
	/* It requires figureing out what page we're at      */
	page = get_page_from_virtual(index);
	col = get_page_start_virtual(page);

	edit_add(filetmp, line, col, virt_cur_x, virt_mark_beginx, page);
	mvwaddch(edit, line, 0, '$');

	if (strlenpt(fileptr->data) > get_page_end_virtual(page))
	    mvwaddch(edit, line, COLS - 1, '$');
    } else {
	/* It's not the current line means that it's at x=0 and page=1 */
	/* If it is the current line, then we're in the same boat      */
	edit_add(filetmp, line, 0, virt_cur_x, virt_mark_beginx, 1);

	if (strlenpt(&filetmp->data[col]) > COLS)
	    mvwaddch(edit, line, COLS - 1, '$');
    }

    /* Clean up our mess */
    tmp = fileptr->data;
    fileptr->data = realdata;
    free(tmp);
}

void center_cursor(void)
{
    current_y = editwinrows / 2;
    wmove(edit, current_y, current_x);
}

/* Refresh the screen without changing the position of lines */
void edit_refresh(void)
{
    static int noloop = 0;
    int lines = 0, i = 0, currentcheck = 0;
    filestruct *temp, *hold = current;

    if (current == NULL)
	return;

    temp = edittop;

    while (lines <= editwinrows - 1 && lines <= totlines && temp != NULL) {
	hold = temp;
	update_line(temp, current_x);
	if (temp == current)
	    currentcheck = 1;

	temp = temp->next;
	lines++;
    }
    /* If noloop == 1, then we already did an edit_update without finishing
       this function.  So we don't run edit_update again */
    if (!currentcheck && !noloop) /* Then current has run off the screen... */ 
    {
	edit_update(current, CENTER);
	noloop = 1;
    }
    else if (noloop)
	noloop = 0;

    if (lines <= editwinrows - 1)
	while (lines <= editwinrows - 1) {
	    mvwaddstr(edit, lines, i, hblank);
	    lines++;
	}
    if (temp == NULL)
	editbot = hold;
    else
	editbot = temp;
}

/*
 * Same as above, but touch the window first so everything is redrawn.
 */
void edit_refresh_clearok(void)
{
    clearok(edit, TRUE);
    edit_refresh();
    clearok(edit, FALSE);
}

/*
 * Nice generic routine to update the edit buffer given a pointer to the
 * file struct =) 
 */
void edit_update(filestruct * fileptr, int topmidbot)
{
    int i = 0;
    filestruct *temp;

    if (fileptr == NULL)
	return;

    temp = fileptr;
    if (topmidbot == 2)
	;
    else if (topmidbot == 0)
	for (i = 0; i <= editwinrows - 1 && temp->prev != NULL; i++)
	    temp = temp->prev;
    else
	for (i = 0; i <= editwinrows / 2 && temp->prev != NULL; i++)
	    temp = temp->prev;

    edittop = temp;
    fix_editbot();

    edit_refresh();
}

/* This function updates current based on where current_y is, reset_cursor 
   does the opposite */
void update_cursor(void)
{
    int i = 0;

#ifdef DEBUG
    fprintf(stderr, _("Moved to (%d, %d) in edit buffer\n"), current_y,
	    current_x);
#endif

    current = edittop;
    while (i <= current_y - 1 && current->next != NULL) {
	current = current->next;
	i++;
    }

#ifdef DEBUG
    fprintf(stderr, _("current->data = \"%s\"\n"), current->data);
#endif

}

/*
 * Ask a question on the statusbar.  Answer will be stored in answer
 * global.  Returns -1 on aborted enter, -2 on a blank string, and 0
 * otherwise, the valid shortcut key caught, Def is any editable text we
 * want to put up by default.
 */
int statusq(shortcut s[], int slen, char *def, char *msg, ...)
{
    va_list ap;
    char foo[133];
    int ret;

    bottombars(s, slen);

    va_start(ap, msg);
    vsnprintf(foo, 132, msg, ap);
    va_end(ap);
    strncat(foo, ": ", 132);

    wattron(bottomwin, A_REVERSE);
    ret = nanogetstr(foo, def, s, slen, (strlen(foo) + 3));
    wattroff(bottomwin, A_REVERSE);

    switch (ret) {

    case NANO_FIRSTLINE_KEY:
	do_first_line();
	break;
    case NANO_LASTLINE_KEY:
	do_last_line();
	break;
    case NANO_CANCEL_KEY:
	return -1;
    default:
	blank_statusbar_refresh();
    }

#ifdef DEBUG
    fprintf(stderr, _("I got \"%s\"\n"), answer);
#endif

    return ret;
}

/*
 * Ask a simple yes/no question on the statusbar.  Returns 1 for Y, 0 for
 * N, 2 for All (if all is non-zero when passed in) and -1 for abort (^C)
 */
int do_yesno(int all, int leavecursor, char *msg, ...)
{
    va_list ap;
    char foo[133];
    int kbinput, ok = -1;

    /* Write the bottom of the screen */
    clear_bottomwin();
    wattron(bottomwin, A_REVERSE);
    blank_statusbar_refresh();
    wattroff(bottomwin, A_REVERSE);

    /* Remove gettext call for keybindings until we clear the thing up */
    if (!ISSET(NO_HELP)) {
	wmove(bottomwin, 1, 0);
	onekey(" Y", _("Yes"));
	if (all)
	    onekey(" A", _("All"));
	wmove(bottomwin, 2, 0);
	onekey(" N", _("No"));
	onekey("^C", _("Cancel"));
    }
    va_start(ap, msg);
    vsnprintf(foo, 132, msg, ap);
    va_end(ap);
    wattron(bottomwin, A_REVERSE);
    mvwaddstr(bottomwin, 0, 0, foo);
    wattroff(bottomwin, A_REVERSE);
    wrefresh(bottomwin);

    if (leavecursor == 1)
	reset_cursor();

    while (ok == -1) {
	kbinput = wgetch(edit);

	switch (kbinput) {
	case 'Y':
	case 'y':
	    ok = 1;
	    break;
	case 'N':
	case 'n':
	    ok = 0;
	    break;
	case 'A':
	case 'a':
	    if (all)
		ok = 2;
	    break;
	case NANO_CONTROL_C:
	    ok = -2;
	    break;
	}
    }

    /* Then blank the screen */
    blank_statusbar_refresh();

    if (ok == -2)
	return -1;
    else
	return ok;
}

void statusbar(char *msg, ...)
{
    va_list ap;
    char foo[133];
    int start_x = 0;

    va_start(ap, msg);
    vsnprintf(foo, 132, msg, ap);
    va_end(ap);

    start_x = center_x - strlen(foo) / 2 - 1;

    /* Blank out line */
    blank_statusbar();

    wmove(bottomwin, 0, start_x);

    wattron(bottomwin, A_REVERSE);

    waddstr(bottomwin, "[ ");
    waddstr(bottomwin, foo);
    waddstr(bottomwin, " ]");
    wattroff(bottomwin, A_REVERSE);
    wrefresh(bottomwin);

    if (ISSET(CONSTUPDATE))
	statblank = 1;
    else
	statblank = 25;
}

void display_main_list(void)
{
    bottombars(main_list, MAIN_VISIBLE);
}

int total_refresh(void)
{
    clearok(edit, TRUE);
    clearok(topwin, TRUE);
    clearok(bottomwin, TRUE);
    wnoutrefresh(edit);
    wnoutrefresh(topwin);
    wnoutrefresh(bottomwin);
    doupdate();
    clearok(edit, FALSE);
    clearok(topwin, FALSE);
    clearok(bottomwin, FALSE);
    edit_refresh();
    return 1;
}

void previous_line(void)
{
    if (current_y > 0)
	current_y--;
}

int do_cursorpos(void)
{
    filestruct *fileptr;
    float linepct, bytepct;
    int i, tot = 0;

    if (current == NULL || fileage == NULL)
	return 0;

    for (fileptr = fileage; fileptr != current && fileptr != NULL;
	 fileptr = fileptr->next)
	tot += strlen(fileptr->data) + 1;

    if (fileptr == NULL)
	return -1;

    i = tot + current_x;;

    for (fileptr = current->next; fileptr != NULL; fileptr = fileptr->next)
	tot += strlen(fileptr->data) + 1;

    if (totlines > 0)
	linepct = 100 * current->lineno / totlines;
    else
	linepct = 0;

    if (totsize > 0)
	bytepct = 100 * i / totsize;
    else
	bytepct = 0;

#ifdef DEBUG
    fprintf(stderr, _("do_cursorpos: linepct = %f, bytepct = %f\n"),
	    linepct, bytepct);
#endif

    statusbar(_("line %d of %d (%.0f%%), character %d of %d (%.0f%%)"),
	      current->lineno, totlines, linepct, i, totsize, bytepct);
    reset_cursor();
    return 1;
}

/* Our broken, non-shortcut list compliant help function.
   But hey, it's better than nothing, and it's dynamic! */
int do_help(void)
{
#ifndef NANO_SMALL
    char *ptr = help_text, *end;
    int i, j, row = 0, page = 1, kbinput = 0, no_more = 0;
    int no_help_flag = 0;

    blank_edit();
    curs_set(0);
    blank_statusbar();

    if (ISSET(NO_HELP)) {

	no_help_flag = 1;
	delwin(bottomwin);
	bottomwin = newwin(3, COLS, LINES - 3, 0);
	keypad(bottomwin, TRUE);

	editwinrows -= no_help();
	UNSET(NO_HELP);
	bottombars(help_list, HELP_LIST_LEN);
    } else
	bottombars(help_list, HELP_LIST_LEN);

    do {
	ptr = help_text;
	switch (kbinput) {
	case NANO_NEXTPAGE_KEY:
	case NANO_NEXTPAGE_FKEY:
	case KEY_NPAGE:
	    if (!no_more) {
		blank_edit();
		page++;
	    }
	    break;
	case NANO_PREVPAGE_KEY:
	case NANO_PREVPAGE_FKEY:
	case KEY_PPAGE:
	    if (page > 1) {
		no_more = 0;
		blank_edit();
		page--;
	    }
	    break;
	}

	/* Calculate where in the text we should be based on the page */
	for (i = 1; i < page; i++) {
	    row = 0;
	    j = 0;
	    while (row < editwinrows && *ptr != '\0') {
		if (*ptr == '\n' || j == COLS - 5) {
		    j = 0;
		    row++;
		}
		ptr++;
		j++;
	    }
	}

	i = 0;
	j = 0;
	while (i < editwinrows && *ptr != '\0') {
	    end = ptr;
	    while (*end != '\n' && *end != '\0' && j != COLS - 5) {
		end++;
		j++;
	    }
	    if (j == COLS - 5) {

		/* Don't print half a word if we've run of of space */
		while (*end != ' ' && *end != '\0') {
		    end--;
		    j--;
		}
	    }
	    mvwaddnstr(edit, i, 0, ptr, j);
	    j = 0;
	    i++;
	    if (*end == '\n')
		end++;
	    ptr = end;
	}
	if (*ptr == '\0') {
	    no_more = 1;
	    continue;
	}
    } while ((kbinput = wgetch(edit)) != NANO_EXIT_KEY);

    if (no_help_flag) {
	werase(bottomwin);
	wrefresh(bottomwin);
	delwin(bottomwin);
	SET(NO_HELP);
	bottomwin = newwin(3 - no_help(), COLS, LINES - 3 + no_help(), 0);
	keypad(bottomwin, TRUE);
	editwinrows += no_help();
    } else
	display_main_list();

    curs_set(1);
    edit_refresh();
#else
    nano_small_msg();
#endif

    return 1;
}

/* Dump the current file structure to stderr */
void dump_buffer(filestruct * inptr)
{
#ifdef DEBUG
    filestruct *fileptr;

    if (inptr == fileage)
	fprintf(stderr, _("Dumping file buffer to stderr...\n"));
    else if (inptr == cutbuffer)
	fprintf(stderr, _("Dumping cutbuffer to stderr...\n"));
    else
	fprintf(stderr, _("Dumping a buffer to stderr...\n"));

    fileptr = inptr;
    while (fileptr != NULL) {
	fprintf(stderr, "(%ld) %s\n", fileptr->lineno, fileptr->data);
	fflush(stderr);
	fileptr = fileptr->next;
    }
#endif				/* DEBUG */
}

void dump_buffer_reverse(filestruct * inptr)
{
#ifdef DEBUG
    filestruct *fileptr;

    fileptr = filebot;
    while (fileptr != NULL) {
	fprintf(stderr, "(%ld) %s\n", fileptr->lineno, fileptr->data);
	fflush(stderr);
	fileptr = fileptr->prev;
    }
#endif				/* DEBUG */
}

/* Fix editbot based on the assumption that edittop is correct */
void fix_editbot(void) {
    int i;
    editbot = edittop;
    for(i = 0; (i <= editwinrows - 1) && (editbot->next != NULL)
	&& (editbot != filebot); i++, editbot = editbot->next);
}