#include <signal.h>
#include "fm.h"

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

#ifdef USE_COLOR

#define EFFECT_ANCHOR_START       effect_anchor_start()
#define EFFECT_ANCHOR_END         effect_anchor_end()
#define EFFECT_IMAGE_START        effect_image_start()
#define EFFECT_IMAGE_END          effect_image_end()
#define EFFECT_FORM_START         effect_form_start()
#define EFFECT_FORM_END           effect_form_end()
#define EFFECT_ACTIVE_START	  effect_active_start()
#define EFFECT_ACTIVE_END	  effect_active_end()
#define EFFECT_VISITED_START      effect_visited_start()
#define EFFECT_VISITED_END        effect_visited_end()
#define EFFECT_MARK_START         effect_mark_start()
#define EFFECT_MARK_END           effect_mark_end()

/* color: *     0  black *      1  red *        2  green *      3  yellow
 * *    4  blue *       5  magenta *    6  cyan *       7  white */

#define EFFECT_ANCHOR_START_C       setfcolor(anchor_color)
#define EFFECT_IMAGE_START_C        setfcolor(image_color)
#define EFFECT_FORM_START_C         setfcolor(form_color)
#define EFFECT_ACTIVE_START_C      (setfcolor(active_color), underline())
#define EFFECT_VISITED_START_C      setfcolor(visited_color)
#ifdef USE_BG_COLOR
#define EFFECT_MARK_START_C         setbcolor(mark_color)
#else
#define EFFECT_MARK_START_C         standout()
#endif

#define EFFECT_IMAGE_END_C          setfcolor(basic_color)
#define EFFECT_ANCHOR_END_C         setfcolor(basic_color)
#define EFFECT_FORM_END_C           setfcolor(basic_color)
#define EFFECT_ACTIVE_END_C        (setfcolor(basic_color), underlineend())
#define EFFECT_VISITED_END_C        setfcolor(basic_color)
#ifdef USE_BG_COLOR
#define EFFECT_MARK_END_C           setbcolor(bg_color)
#else
#define EFFECT_MARK_END_C           standend()
#endif

#define EFFECT_ANCHOR_START_M       underline()
#define EFFECT_ANCHOR_END_M         underlineend()
#define EFFECT_IMAGE_START_M        standout()
#define EFFECT_IMAGE_END_M          standend()
#define EFFECT_FORM_START_M         standout()
#define EFFECT_FORM_END_M           standend()
#define EFFECT_ACTIVE_START_NC      underline()
#define EFFECT_ACTIVE_END_NC        underlineend()
#define EFFECT_ACTIVE_START_M       bold()
#define EFFECT_ACTIVE_END_M         boldend()
#define EFFECT_VISITED_START_M      /**/
#define EFFECT_VISITED_END_M        /**/
#define EFFECT_MARK_START_M         standout()
#define EFFECT_MARK_END_M           standend()

#define define_effect(name_start,name_end,color_start,color_end,mono_start,mono_end) \
static void name_start { if (useColor) { color_start; } else { mono_start; }}\
static void name_end { if (useColor) { color_end; } else { mono_end; }}

define_effect(EFFECT_ANCHOR_START, EFFECT_ANCHOR_END, EFFECT_ANCHOR_START_C,
	      EFFECT_ANCHOR_END_C, EFFECT_ANCHOR_START_M, EFFECT_ANCHOR_END_M)
define_effect(EFFECT_IMAGE_START, EFFECT_IMAGE_END, EFFECT_IMAGE_START_C,
	      EFFECT_IMAGE_END_C, EFFECT_IMAGE_START_M, EFFECT_IMAGE_END_M)
define_effect(EFFECT_FORM_START, EFFECT_FORM_END, EFFECT_FORM_START_C,
	      EFFECT_FORM_END_C, EFFECT_FORM_START_M, EFFECT_FORM_END_M)
define_effect(EFFECT_MARK_START, EFFECT_MARK_END, EFFECT_MARK_START_C,
	      EFFECT_MARK_END_C, EFFECT_MARK_START_M, EFFECT_MARK_END_M)
static void
EFFECT_ACTIVE_START
{
    if (useColor) {
	if (useActiveColor) {
#ifdef __EMX__
	    if(!getenv("WINDOWID"))
		setfcolor(active_color);
	    else
#endif
	    {
		EFFECT_ACTIVE_START_C;
	    }
	} else {
	    EFFECT_ACTIVE_START_NC;
	}
    } else {
	EFFECT_ACTIVE_START_M;
    }
}

static void
EFFECT_ACTIVE_END
{
    if (useColor) {
	if (useActiveColor)
	    EFFECT_ACTIVE_END_C;
	else
	    EFFECT_ACTIVE_END_NC;
    }
    else
	 EFFECT_ACTIVE_END_M;
}

static void
EFFECT_VISITED_START
{
    if (useVisitedColor) {
	if (useColor) {
	    EFFECT_VISITED_START_C;
	} else {
	    EFFECT_VISITED_START_M;
	}
    }
}

static void
EFFECT_VISITED_END
{
    if (useVisitedColor) {
	if (useColor) {
	    EFFECT_VISITED_END_C;
	} else {
	    EFFECT_VISITED_END_M;
	}
    }
}
 

#else				/* not USE_COLOR */

#define EFFECT_ANCHOR_START       underline()
#define EFFECT_ANCHOR_END         underlineend()
#define EFFECT_IMAGE_START        standout()
#define EFFECT_IMAGE_END          standend()
#define EFFECT_FORM_START         standout()
#define EFFECT_FORM_END           standend()
#define EFFECT_ACTIVE_START       bold()
#define EFFECT_ACTIVE_END         boldend()
#define EFFECT_VISITED_START      /**/
#define EFFECT_VISITED_END        /**/
#define EFFECT_MARK_START         standout()
#define EFFECT_MARK_END           standend()

#endif				/* not USE_COLOR */


#ifndef KANJI_SYMBOLS
static char g_rule[] = "ntwluxkavmqajaaa";
#endif				/* not KANJI_SYMBOLS */

static int fmDontInitialize;

/* 
 * Terminate routine.
 */

void
fmTerm(int subproc_p)
{
    if (subproc_p) {
	fprintf(urgent_out, "%X T\n", urgent_fmsetup);
	fflush(urgent_out);
    }
    else if (fmInitialized) {
	move(INPUTLINE, 0);
	clrtoeolx();
	refresh();
#ifdef USE_MOUSE
	if (use_mouse)
	    mouse_end();
#endif				/* USE_MOUSE */
	reset_tty();
	fmInitialized = FALSE;
    }
    else
	++fmDontInitialize;
}


/* 
 * Initialize routine.
 */
void
fmInit(int subproc_p)
{
    if (subproc_p) {
	fprintf(urgent_out, "%X I\n", urgent_fmsetup);
	fflush(urgent_out);
    }
    else if (!fmInitialized) {
	if (fmDontInitialize) {
	    --fmDontInitialize;
	    return;
	}

	initscr();
	term_raw();
	term_noecho();
#ifdef USE_IMAGE
	initImage();
#endif
	fmInitialized = TRUE;
    }
}

/* 
 * Display some lines.
 */
static BufferView *cview;

static int ulmode = 0, somode = 0, bomode = 0;
static int anch_mode = 0, emph_mode = 0, imag_mode = 0, form_mode = 0,
    active_mode = 0, visited_mode = 0, mark_mode = 0;
#ifndef KANJI_SYMBOLS
static int graph_mode = 0;
#endif				/* not KANJI_SYMBOLS */
#ifdef USE_ANSI_COLOR
static Linecolor color_mode = 0;
#endif

#ifdef USE_BUFINFO
static Buffer *save_current_buf = NULL;
#endif

void
shrink_cat(int width, Str dst, Str s)
{
    int l;
#ifdef MANY_CHARSET
    int dwidth, swidth, owidth;
#endif

#ifdef MANY_CHARSET
    dwidth = ttyfix_width_n(dst->ptr, dst->length);
    swidth = ttyfix_width_n(s->ptr, s->length);
    owidth = ttyfix_width(RCSTR_OMITTED);
    l = width - dwidth;
#else
    l = width - dst->length;
#endif
    if (
#ifdef MANY_CHARSET
	swidth > l && l >= owidth
#else
	s->length > l && l >= 4
#endif
	) {
#ifdef MANY_CHARSET
	int cn, cw, w, half;
	size_t b, e;
	mb_wchar_t wc;

	half = (l - owidth) / 2;

	for (b = w = 0 ; b < s->length ;) {
	    if ((cn = mb_mem_to_wchar_internal(&s->ptr[b], s->length - b, wc)) < 0)
		cn = 1;

	    cw = ttyfix_wchar_width(wc);

	    if (half - w < cw)
		break;

	    w += cw;
	    b += cn;
	}

	Strcat_charp_n(dst, s->ptr, b);
	Strcat_charp(dst, RCSTR_OMITTED);

	for (w = 0, e = s->length ; e > 0 ;) {
	    b = e - 1;
	    wc = mb_mem_to_wchar(s->ptr, &b, &e);
	    cw = ttyfix_wchar_width(wc);

	    if (half - w < cw)
		break;

	    w += cw;
	    e = b;
	}

	Strcat_charp_n(dst, &s->ptr[e], s->length - e);
#else
	Strcat(dst, Strsubstr(s, 0, (l - 2) / 2));
#if LANG == JA
	Strcat_charp(dst, "¡Ä");
#else				/* LANG != JA */
	Strcat_charp(dst, "..");
#endif				/* LANG != JA */
	l = width - dst->length;
	Strcat(dst, Strsubstr(s, s->length - l, l));
#endif
    }
    else {
	Strcat(dst, s);
    }
}

static int
needDrawAnchorCursor(Buffer *buf, int n)
{
    HmarkerList *ml;
    int prevhseq = -1, stop, sbot, top, bot;
    BufferPointLink *bpl;
    Anchor *a;
    BufferView *v;

    if (!buf->topLine || !(v = buf->view))
	return FALSE;

    stop = buf->topLine->linenumber;
    sbot = stop + v->height;

    if (n < 0) {
	top = stop;
	bot = top - n;
    }
    else {
	bot = stop + v->height - 1;
	top = bot - n;
    }

    a = retrieveCurrentAnchor(buf);

    if ((ml = buf->hmarklist) && (prevhseq = ml->prevhseq) >= 0 &&
	(((bpl = &ml->marks[ml->prevhseq])->origin.line < top && bpl->corner.line >= stop) ||
	 (bpl->corner.line >= bot && bpl->origin.line < sbot)) &&
	(!a || a->hseq != prevhseq))
	return TRUE;

    if (a && a->hseq >= 0 && a->hseq != prevhseq &&
	(((bpl = &ml->marks[a->hseq])->origin.line < top && bpl->corner.line >= stop) ||
	 (bpl->corner.line >= bot && bpl->origin.line < sbot)))
	return TRUE;

    return FALSE;
}

static void
drawAnchorCursor(Buffer *buf, int n, int prevhseq)
{
    Line *l;
    int pos, top, bot;
    Anchor *can, *an;
    BufferView *v;

    if (!buf->topLine || !(v = buf->view))
	return;

    if (n < 0) {
	top = buf->topLine->linenumber - n;
	bot = buf->topLine->linenumber + v->height;
    }
    else {
	top = buf->topLine->linenumber;
	bot = top + v->height - n;
    }

    if (bot > buf->lastLine->linenumber + 1)
	bot = buf->lastLine->linenumber + 1;

    pos = 0;

    if (!(an = retrieveAnchor(buf->href, top, pos)))
	an = closest_next_anchor(buf->href, NULL, pos, top);

    l = buf->topLine;
    can = retrieveCurrentAnchor(buf);

    while (an && an->start.line < bot) {
	if (an->hseq >= 0) {
	    while (l->linenumber < an->start.line)
		if (!(l = l->next))
		    return;

	    if ((prevhseq == an->hseq &&
		 !(can && can->hseq == an->hseq)) ||
		(can && can->hseq == an->hseq))
		redrawLineRegion(buf, l, l->linenumber - buf->topLine->linenumber, an->start.pos, an->end.pos);
	}

	an = closest_next_anchor(buf->href, NULL, an->start.pos, an->start.line);
    }
}

static Str
cat_indicator(Str d, char *s)
{
    if (!d)
	d = Strnew_charp("[");

    Strcat_charp(d, s);
    return d;
}

static void
cat_framepos(Str msg, Buffer *buf)
{
    BufferView *v;

    v = buf->view;
    Strcat(msg, Sprintf("(%d%%,%d%%)",
			(v->rootX * 100 + v->sup->width / 2) / v->sup->width,
			(v->rootY * 100 + v->sup->height / 2) / v->sup->height));

    while (v = v->sup, v->sup)
	Strcat(msg, Sprintf(",(%d%%,%d%%)",
			    (v->rootX * 100 + v->sup->width / 2) / v->sup->width,
			    (v->rootY * 100 + v->sup->height / 2) / v->sup->height));

    Strcat_char(msg, ' ');
}

#ifdef KANJI_SYMBOLS
#ifdef MANY_CHARSET
static int VERTICALBAR_WIDTH;
static int HORIZONTALBAR_WIDTH;
extern char *ruleB[];
#define VERTICALBAR (ruleB[1 | 4])
#define HORIZONTALBAR (ruleB[2 | 8])
#endif
#ifdef JP_CHARSET
#define VERTICALBAR "¨­"
#define VERTICALBAR_WIDTH (2)
#define HORIZONTALBAR "¨¬"
#define HORIZONTALBAR_WIDTH (2)
#endif
#else
static char *rule;
#define VERTICALBAR (rule[1 | 4])
#define VERTICALBAR_WIDTH (1)
#define HORIZONTALBAR (rule[2 | 8])
#define HORIZONTALBAR_WIDTH (1)
#endif

BufferView *
initBufferView(BufferView *v, struct frameset *fset, BufferView *sup,
	       short rootX, short rootY, short width, short height, short col, short row)
{
    BufferViewList *l;

    if (!v)
	v = New(BufferView);

    if ((v->sup = sup)) {
	l = &sup->subv[row * sup->ncols + col];
	v->root = sup->root;
    }
    else {
	l = &TopViewList;
	v->root = v;
    }

    v->up = NULL;

    if ((v->down = l->top))
	v->down->up = v;
    else
	l->bot = v;

    l->top = v;
    v->x = col;
    v->y = row;
    v->flag = 0;
    v->rootX = rootX;
    v->rootY = rootY;
    v->width = width;
    v->height = height;
    v->top = v->bot = NULL;

    if ((v->frameset = fset)) {
	int i;
	short sub_x, sub_y, sub_col, sub_row, nzcol, nzrow;

#if defined(KANJI_SYMBOLS) && defined(MANY_CHARSET)
	if (!VERTICALBAR_WIDTH || !HORIZONTALBAR_WIDTH) {
	    VERTICALBAR_WIDTH = ttyfix_width(VERTICALBAR);
	    HORIZONTALBAR_WIDTH = ttyfix_width(HORIZONTALBAR);
	}
#endif

	v->ncols = fset->col;
	v->colv = NewAtom_N(short, fset->col);
	parse_frame_colrow(width, HORIZONTALBAR_WIDTH, VERTICALBAR_WIDTH, pixel_per_char,
			   fset->width, fset->col, v->colv);
	v->nrows = fset->row;
	v->rowv = NewAtom_N(short, fset->row);
	parse_frame_colrow(height, 1, 1, pixel_per_line,
			   fset->height, fset->row, v->rowv);
	v->subv = New_N(BufferViewList, v->ncols * v->nrows);
	memset(v->subv, 0, sizeof(v->subv[0]) * v->ncols * v->nrows);
	v->cviewv = New_N(BufferView *, v->ncols * v->nrows);
	memset(v->cviewv, 0, sizeof(v->cviewv[0]) * v->ncols * v->nrows);

	for (nzrow = -1, i = 0, sub_y = rootY, sub_row = 0 ; sub_row < v->nrows ; sub_y += v->rowv[sub_row++]) {
	    if (!v->rowv[sub_row]) {
		i += v->ncols;
		continue;
	    }

	    if (nzrow >= 0)
		++sub_y;

	    for (nzcol = -1, sub_x = rootX, sub_col = 0 ; sub_col < v->ncols ; sub_x += v->colv[sub_col++], ++i) {
		if (!v->colv[sub_col])
		    continue;

		if (nzcol >= 0)
		    sub_x += HORIZONTALBAR_WIDTH;

		initBufferView(NULL,
			       (fset->frame[i].element && fset->frame[i].element->attr == F_FRAMESET) ?
			       fset->frame[i].set : NULL,
			       v, sub_x, sub_y, v->colv[sub_col], v->rowv[sub_row], sub_col, sub_row);
		nzcol = sub_col;
	    }

	    nzrow = sub_row;
	}
    }
    else {
	v->ncols = v->nrows = 1;
	v->colv = NewAtom(short);
	v->colv[0] = width;
	v->rowv = NewAtom(short);
	v->rowv[0] = height;
	v->subv = NULL;
    }

    v->flag |= BV_LINKED;
    return v;
}

BufferView *
defaultBufferView(void)
{
    return ((Currentbuf && !Currentbuf->view->root->frameset) ? Currentbuf->view->root :
	    initBufferView(NULL, NULL, NULL, 0, 0, COLS, LASTLINE, 0, 0));
}

static void
displayBufferView(BufferView *v, short goal_width, short goal_height, short sup_x, short sup_y)
{
    short col, nzcol, row, nzrow, x, y, z;
    int i, j, need_reshape = FALSE;

    if (!v)
	return;

    if (v->width != goal_width) {
	need_reshape = TRUE;

	if (v->frameset)
	    parse_frame_colrow(goal_width, HORIZONTALBAR_WIDTH, VERTICALBAR_WIDTH, pixel_per_char,
			       v->frameset->width, v->frameset->col,
			       v->colv);

	v->width = goal_width;
    }

    if (v->height != goal_height) {
	if (v->frameset)
	    parse_frame_colrow(goal_height, 1, 1, pixel_per_line,
			       v->frameset->height, v->frameset->row,
			       v->rowv);

	v->height = goal_height;
    }

    v->rootX = sup_x;
    v->rootY = sup_y;

    if (v->frameset)
	for (nzrow = -1, i = 0, y = sup_y, row = 0 ; row < v->nrows ; ++row) {
	    if (!v->rowv[row]) {
		i += v->ncols;
		continue;
	    }

	    z = nzrow >= 0 ? y + 1 : y;

	    for (nzcol = -1, x = sup_x, col = 0 ; col < v->ncols ; ++col, ++i) {
		if (!v->colv[col])
		    continue;

#ifndef KANJI_SYMBOLS
		if (!graph_mode && graph_ok) {
		    graphstart();
		    graph_mode = TRUE;
		}

		rule = graph_mode ? g_rule : alt_rule;
#endif

		if (nzrow >= 0) {
		    move(y, x);
#ifdef USE_COLOR
		    if (useColor)
			setfcolor(frame_color);
#endif

		    for (j = v->colv[col] ; j >= HORIZONTALBAR_WIDTH ; j -= HORIZONTALBAR_WIDTH) {
#ifdef KANJI_SYMBOLS
			addstr(HORIZONTALBAR);
#else
			addch(HORIZONTALBAR);
#endif
		    }

#ifdef USE_COLOR
		    if (useColor)
			setfcolor(basic_color);
#endif
		}

		if (nzcol >= 0) {
		    for (j = v->rowv[row] ; j > 0 ;) {
			--j;
			move(z + j, x);
#ifdef USE_COLOR
			if (useColor)
			    setfcolor(frame_color);
#endif
#ifdef KANJI_SYMBOLS
			addstr(VERTICALBAR);
#else
			addch(VERTICALBAR);
#endif
#ifdef USE_COLOR
			if (useColor)
			    setfcolor(basic_color);
#endif
		    }

		    x += VERTICALBAR_WIDTH;
		}

#ifndef KANJI_SYMBOLS
		if (graph_mode) {
		    graphend();
		    graph_mode = FALSE;
		}
#endif

		v->cviewv[i] = v->subv[i].top;
		displayBufferView(v->subv[i].top, v->colv[col], v->rowv[row], x, z);
		x += v->colv[col];
		nzcol = col;
	    }

	    y = z + v->rowv[row];
	    nzrow = row;
	}
    else if (v->top && v->top->bufferprop & BP_VISIBLE) {
	if (need_reshape)
	    v->top->need_reshape = need_reshape;

	v->cline = v->cbot = NULL;
	v->ccolumn = -1;
#ifdef USE_IMAGE
	v->draw_image_flag = FALSE;
#endif
	displayBuffer(v->top,
#ifdef USE_IMAGE
		      v->top->image_flag == IMG_FLAG_AUTO && v->top->img ? B_REDRAW_IMAGE :
#endif
		      B_FORCE_REDRAW);
    }
}

void
displayCurrentView(BufferView *v)
{
    short x, y, w, h, ch;
    int cur_x, cur_y;
    Buffer *cur;

#if defined(KANJI_SYMBOLS) && defined(MANY_CHARSET)
    if (!VERTICALBAR_WIDTH || !HORIZONTALBAR_WIDTH) {
	VERTICALBAR_WIDTH = ttyfix_width(VERTICALBAR);
	HORIZONTALBAR_WIDTH = ttyfix_width(HORIZONTALBAR);
    }
#endif

    cur = Currentbuf;

#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    if (cur->menu)
	cur = cur->menu->current;
#endif

    if (!v &&
	!(cur && (v = cur->view->root))) {
	clear_partial(0, 0, COLS,
		      dont_buffername ? LASTLINE :
		      inputLineBusy ? INPUTLINE : LINES);
	return;
    }

    if (inputLineBusy || !(Currentbuf && Currentbuf->view))
	getyx(cur_y, cur_x);
    else {
	cur_y = Currentbuf->view->rootY + Currentbuf->cursorY;
	cur_x = Currentbuf->view->rootX + Currentbuf->lmargin + Currentbuf->cursorX;
    }

    if (v->sup) {
	x = v->rootX;
	y = v->rootY;
	w = v->width;
	ch = h = v->height;
    }
    else {
	x = y = 0;
	w = COLS;
	h = LASTLINE;
	ch = dont_buffername ? LASTLINE : inputLineBusy ? INPUTLINE : LINES;
    }

#ifdef USE_IMAGE
    clearImage(v);
#endif
    clear_partial(x, y, w, ch);
    cview = v;
    displayBufferView(v, w, h, x, y);
    move(cur_y, cur_x);
    refresh();
}

static int
isThisView(BufferView *v)
{
    for (; v->sup ; v = v->sup)
	if (v != v->sup->cviewv[v->y * v->sup->ncols + v->x])
	    return FALSE;

    return v == cview;
}

void
displayBuffer(Buffer *buf, int mode)
{
    Str msg;
    Anchor *aa;
    int draw_an_cur = FALSE, n = 0, prevhseq = -1, x, y;
#ifdef USE_IMAGE
    int image_unloaded;
#endif
    Line *bot;
    BufferView *v;
    Buffer *cur;

    if (buf->firstLine == NULL && readBufferCache(buf) == 0)	/* clear_buffer */
	mode = B_FORCE_REDRAW;

    v = buf->view;

    if (
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	!buf->menu &&
#endif
	(v->root->width != COLS || v->root->height != LASTLINE || !isThisView(v))) {
	displayCurrentView(v->root);
	return;
    }

#ifdef USE_IMAGE
    image_unloaded = buf->image_unloaded;
    buf->image_unloaded = FALSE;
#endif

    if (buf->need_reshape && !buf->async_buf
#ifdef USE_IMAGE
	&& !image_unloaded
#endif
	) {
	Phase0Env p0env;

	p0env = main_p0env;
	p0env.flag &= ~(RG_PROC_MASK | RG_DUMP_MASK | RG_HALFDUMP_MASK);
#ifdef USE_IMAGE
	p0env.displayImage = buf->image_flag == IMG_FLAG_AUTO ? TRUE : FALSE;
#endif
	mode = (
#ifdef USE_IMAGE
		buf->need_reshape ? buf->redraw_mode :
		(buf->image_flag == IMG_FLAG_AUTO && buf->img) ? B_REDRAW_IMAGE :
#endif
		B_FORCE_REDRAW);
	buf->need_reshape = TRUE;
	reloadBuffer(buf, &p0env);
    }
    if (showLineNum && buf->lmargin <= 0 && !buf->async_buf
#ifdef USE_IMAGE
	&& !image_unloaded
#endif
	) {
	Line *l;

	for (l = buf->lastLine ; l && !l->real_linenumber ; l = l->prev)
	    ;

	if (l) {
	    int margin;

	    margin = (int)ceil(log10(l->real_linenumber)) + sizeof(": ") - 1;

	    if (margin < v->width - buf->rmargin - Tabstop) {
		Phase0Env p0env;

		mode = (
#ifdef USE_IMAGE
			(buf->image_flag == IMG_FLAG_AUTO && buf->img) ?
			B_REDRAW_IMAGE :
#endif
			B_FORCE_REDRAW);
		p0env = main_p0env;
		p0env.flag &= ~(RG_PROC_MASK | RG_DUMP_MASK | RG_HALFDUMP_MASK);
#ifdef USE_IMAGE
		p0env.displayImage = buf->image_flag == IMG_FLAG_AUTO ? TRUE : FALSE;
#endif
		buf->lmargin = margin;
		buf->need_reshape = TRUE;
		reloadBuffer(buf, &p0env);
	    }
	}
    }
    if (!buf->async_buf
#ifdef USE_IMAGE
	&& !image_unloaded
#endif
	) {
	if (mode < buf->redraw_mode)
	    mode = buf->redraw_mode;
	buf->redraw_mode = B_NORMAL;
    }
    if (!buf->firstLine) {
	v->cline = v->cbot = NULL;
	v->ccolumn = -1;
#ifdef USE_IMAGE
	clearImage(v);
	v->draw_image_flag = FALSE;
#endif
	x = v->rootX;
	y = v->rootY;
	clear_partial(x, y, v->width, v->height);
    }
    else {
	if (buf->topLine == NULL)
	    buf->topLine = buf->firstLine;
	bot = lineSkip(buf, buf->topLine, v->height - 1, FALSE);
	if (buf->hmarklist)
	    prevhseq = buf->hmarklist->prevhseq;
	if (inputLineBusy)
	    getyx(y, x);
	else if (Currentbuf && Currentbuf != buf) {
	    y = Currentbuf->view->rootY + Currentbuf->cursorY;
	    x = Currentbuf->view->rootX + Currentbuf->lmargin + Currentbuf->cursorX;
	}
	else {
	    y = v->rootY + buf->cursorY;
	    x = v->rootX + buf->lmargin + buf->cursorX;
	}
	if (mode == B_FORCE_REDRAW || mode == B_SCROLL ||
#ifdef USE_IMAGE
	    mode == B_REDRAW_IMAGE ||
#endif
	    v->cline != buf->topLine ||
	    v->ccolumn != buf->currentColumn) {
	    if (mode == B_SCROLL && v->cline && buf->currentColumn == v->ccolumn &&
#ifdef USE_IMAGE
		!v->draw_image_flag && !(activeImage && buf->image_flag == IMG_FLAG_AUTO && buf->img) &&
#endif
		!v->rootX && v->width == COLS) {
		n = buf->topLine->linenumber - v->cline->linenumber;
		if (n > 0 && n < v->height) {
		    scroll(n, v->rootY, v->height);
		    move(v->rootY + v->height - n, 0);
		}
		else if (n < 0 && n > -v->height) {
		    rscroll(-n, v->rootY, v->height);
		    move(v->rootY, 0);
		}
		draw_an_cur = needDrawAnchorCursor(buf, n);
		redrawNLine(buf, n);

#ifdef USE_IMAGE
		if (image_unloaded && !buf->image_unloaded
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
		    && !buf->menu
#endif
		    )
		    checkUnloadedImage(v);
#endif
	    }
	    else {
#ifdef USE_IMAGE
		if (activeImage &&
		    (mode == B_REDRAW_IMAGE ||
		     v->cline != buf->topLine || v->ccolumn != buf->currentColumn)
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
		    && !buf->menu
#endif
		    ) {
		    clearImage(v);
		    if (v->draw_image_flag || (buf->image_flag == IMG_FLAG_AUTO && buf->img))
			clear_partial(v->rootX, v->rootY, v->width, v->height);
		    v->draw_image_flag = FALSE;
		}
#endif
		redrawBuffer(buf);
	    }
	    v->cline = buf->topLine;
	    v->cbot = bot;
	    v->ccolumn = buf->currentColumn;
	}
	else if (v->cbot != bot) {
	    n = v->height - (v->cbot->linenumber - buf->topLine->linenumber);
	    draw_an_cur = needDrawAnchorCursor(buf, n);
	    redrawNLine(buf, n);
	    v->cbot = bot;

#ifdef USE_IMAGE
	    if (image_unloaded && !buf->image_unloaded
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
		    && !buf->menu
#endif
		)
		checkUnloadedImage(v);
#endif
	}
	else {
	    draw_an_cur = needDrawAnchorCursor(buf, 0);

#ifdef USE_IMAGE
	    if (image_unloaded
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
		&& !buf->menu
#endif
		)
		checkUnloadedImage(v);
#endif
	}

	if (draw_an_cur)
	    drawAnchorCursor(buf, n, prevhseq);
    }
    move(y, x);
    cur = Currentbuf;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    if (cur && cur->menu)
	cur = cur->menu->current;
#endif
    if (cur && cur->buffername
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	&& !cur->menu
#endif
	) {
	if (!dont_buffername) {
	    Str indicators = NULL;

#ifdef USE_MOUSE
	    if (!use_mouse) {
#ifdef MANY_CHARSET
		msg = Sprintf(RCSTR_VIEW_BUF, html_unquote(cur->buffername));
#else
		msg = Sprintf("Viewing <%s> ", html_unquote(cur->buffername));
#endif
	    }
	    else {
#ifdef MANY_CHARSET
		msg = Sprintf(RCSTR_VIEW_BUF_WITH_MOUSE, html_unquote(cur->buffername));
#else
#if LANG == JA
		msg = Sprintf("¢ã¢¬¢­Viewing <%s> ", html_unquote(cur->buffername));
#else				/* LANG != JA */
		msg = Sprintf("<=UpDn Viewing <%s> ", html_unquote(cur->buffername));
#endif				/* LANG != JA */
#endif
	    }
#else				/* not USE_MOUSE */
#ifdef MANY_CHARSET
	    msg = Sprintf(RCSTR_VIEW_BUF, html_unquote(cur->buffername));
#else
	    msg = Sprintf("Viewing <%s> ", html_unquote(cur->buffername));
#endif
#endif				/* not USE_MOUSE */
	    if (cur->view->sup)
		cat_framepos(msg, cur);
	    if (cur->bufferprop & BP_FOREVER)
		indicators = cat_indicator(indicators, "F");
#ifdef USE_SSL
	    if (cur->ssl_certificate)
		indicators = cat_indicator(indicators, "S");
#endif
#ifdef USE_IMAGE
	    if (image_unloaded)
		indicators = cat_indicator(indicators, "I");
#endif
	    if (indicators) {
		Strcat(msg, indicators);
		Strremovetrailingspaces(msg);
		Strcat_char(msg, ']');
		Strcat_char(msg, ' ');
	    }
	    if (cur->async_buf && cur->async_buf->pid > 0 &&
		!(cur->async_buf->flag & ASYNC_FLAG_COMPLETED))
		Strcat(msg, Sprintf("(under construction[%d]) ", cur->async_buf->pid));
	    if (displayLineInfo)
		Strcat(msg, curlno_str(1));
	    if (displayLink) {
		aa = retrieveCurrentAnchor(cur);
		if (aa) {
		    ParsedURL url;

		    parseURL2(aa->link->href->url, &url, baseURL(cur));
		    shrink_cat(COLS - 1, msg, parsedURL2Str(&url));
		}
	    }
	    standout();
	    message(msg->ptr);
	    standend();
	}
	term_title(cur->buffername);
    }
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    if (CurrentMenu && CurrentMenuPopup && !buf->menu)
	draw_all_menu(CurrentMenu);
#endif
    refresh();
#ifdef USE_IMAGE
    if (activeImage)
	drawImage();
#endif
#ifdef USE_BUFINFO
    if (cur != save_current_buf) {
	saveBufferInfo();
	save_current_buf = cur;
    }
#endif
}

void
redrawBuffer(Buffer *buf)
{
    redrawNLine(buf, buf->view->height);
}

void
redrawNLine(Buffer *buf, int n)
{
    Line *l, *l0;
    int i;
    BufferView *v;

#ifdef USE_COLOR
    if (useColor) {
	EFFECT_ANCHOR_END_C;
#ifdef USE_BG_COLOR
	setbcolor(bg_color);
#endif				/* USE_BG_COLOR */
    }
#endif				/* USE_COLOR */

    v = buf->view;

    for (i = 0, l = buf->topLine; i < v->height; i++) {
	if (i >= v->height - n || i < -n)
	    l0 = redrawLine(buf, l, i);
	else
	    l0 = l ? l->next : NULL;

	if (l0 == NULL && l == NULL)
	    break;

	l = l0;
    }

    if (n > 0)
	clrtobotx_partial(v->rootX, v->rootY, v->width, v->height);
}

static char *
formRealText(FormItemList *fi, Line *l, int *p_n)
{
    int i;

    switch (fi->type) {
    default:
	return NULL;
    case FORM_INPUT_CHECKBOX:
    case FORM_INPUT_RADIO:
	*p_n = 1;
	return fi->checked ? "*" : " ";
#ifdef MENU_SELECT
    case FORM_SELECT:
	updateSelectOption(fi, fi->select_option);
	*p_n = fi->label->length;
	return fi->label->ptr;
#endif				/* MENU_SELECT */
    case FORM_INPUT_TEXT:
    case FORM_INPUT_FILE:
    case FORM_INPUT_PASSWORD:
	if (fi->value) {
	    *p_n = fi->value->length;
	    return fi->value->ptr;
	}
	else
	    return NULL;
    case FORM_TEXTAREA:
	if (!fi->lines || fi->fold != FoldTextarea)
	    formSplitValue(fi);

	if ((i = l->linenumber - fi->y) < 0 || i >= fi->rows || !fi->lines[i])
	    return NULL;
	else {
	    *p_n = fi->lines[i]->length;
	    return fi->lines[i]->ptr;
	}
    }
}

static int
redrawForm(Buffer *buf, Anchor *a, Line *l, int pos,
	   int rcol, int column, int ecol, int *p_ncol)
{
    FormItemList *fi;
    int i, j, n, ctype, delta, bcol, ncol, epos;
    char *p;
    Lineprop *pr, prop;
#ifdef USE_ANSI_COLOR
    Linecolor *pc;
#endif
#ifdef MANY_CHARSET
    mb_wchar_t wc;
    int cw;
#endif

    fi = a->link->fi;

    if (!(p = formRealText(fi, l, &n)))
	return -1;

    prop = l->propBuf[pos];
    i = a->start.pos;
    bcol = COLPOS(l, i);
    epos = a->end.pos;
    ncol = bcol + epos - i;
    if (ecol > ncol)
	ecol = ncol;
    else
	epos = ecol - bcol + i;
    for (j = bcol ; n > 0 && j < ecol ;) {
	if (*p == '\r' || *p == '\n')
	    while (n > 1 && (*(p + 1) == '\r' || *(p + 1) == '\n')) {
		++p;
		--n;
	    }
	ctype = get_mctype(p);
#ifdef MANY_CHARSET
	if ((delta = mb_mem_to_wchar_internal(p, n, wc)) < 0)
	    delta = 1;
	if (wc == '\t') {
	    ncol = ((j - bcol) / Tabstop + 1) * Tabstop + bcol;
	    if (ncol > ecol)
		break;
	    if (j < rcol)
		j = rcol;
	    if (j < column)
		j = column;
	    for (; j < ncol ; ++j)
		addChar(' ', prop);
	}
	else {
	    cw = ttyfix_wchar_width(wc);
	    ncol = j + cw;
	    if (ncol > ecol)
		break;
	    if (j < rcol || j < column) {
		if ((j = rcol) < column)
		    j = column;
		switch (ncol - j) {
		case 2:
		    addChar(' ', prop);
		case 1:
		    addChar(' ', prop);
		default:
		    break;
		}
	    }
	    else if (fi->type == FORM_INPUT_PASSWORD)
		switch (cw) {
		case 2:
		    addChar('*', prop);
		case 1:
		    addChar('*', prop);
		default:
		    break;
		}
	    else
		addWChar((wc == '\r' || wc == '\n') ? ' ' : wc, cw, prop);
	}
#else
	delta = 1;
	if (*p == '\t') {
	    ncol = ((j - bcol) / Tabstop + 1) * Tabstop + bcol;
	    if (ncol > ecol)
		break;
	    if (j < rcol)
		j = rcol;
	    if (j < column)
		j = column;
	    for (; j < ncol ; ++j)
		addChar(' ', prop);
	}
#ifdef JP_CHARSET
	else if (ctype == PC_KANJI) {
	    ++delta;
	    if ((ncol = j + 2) > ecol)
		break;
	    if (j < rcol || j < column) {
		if (ncol > rcol && ncol > column)
		    addChar(' ', prop);
	    }
	    else if (fi->type == FORM_INPUT_PASSWORD) {
		addChar('*', prop | PC_ASCII);
		addChar('*', prop | PC_ASCII);
	    }
	    else {
		addChar(*p, prop | PC_KANJI1);
		addChar(*(p + 1), prop | PC_KANJI2);
	    }
	}
#endif
	else {
	    if ((ncol = j + 1) > ecol)
		break;
	    if (j >= rcol && j >= column) {
		if (fi->type == FORM_INPUT_PASSWORD)
		    addChar('*', prop | PC_ASCII);
		else
		    addChar((*p == '\r' || *p == '\n') ? ' ' : *p, prop | PC_ASCII);
	    }
	}
#endif
	j = ncol;
	p += delta;
	pr += delta;
#ifdef USE_ANSI_COLOR
	pc += delta;
#endif
	n -= delta;
    }
    if (j < rcol)
	j = rcol;
    if (j < column)
	j = column;
    for (; j < ecol ; ++j)
	addChar(' ', prop);
    *p_ncol = j;
    return epos - pos;
}

#ifdef USE_IMAGE
static int
redrawImage(Buffer *buf, Anchor *a_img, Line *l, int i, int pos,
	    int rcol, int column, int ecol, int *p_ncol)
{
    Image *image;
    int delta, ncol, j;
    ImageCache *cache;
    Phase0Env p0env;
    int x, y, cy, sx, sy, iw, ih, w, h;
    BufferView *v;
#ifdef MANY_CHARSET
    mb_wchar_t wc;
#endif

    image = a_img->link->img;
    p0env = main_p0env;
    p0env.flag &= ~RG_PROC_MASK;
    p0env.cur_baseURL = New(ParsedURL);
    copyParsedURL(p0env.cur_baseURL, baseURL(buf));

    if (!(cache = getImage(image, &p0env, IMG_FLAG_AUTO))) {
	buf->redraw_mode = B_REDRAW_IMAGE;
	buf->image_unloaded = TRUE;
	return -1;
    }
    else if (cache->loaded == IMG_FLAG_ERROR ||
	     cache->loaded == IMG_FLAG_FATAL_ERROR)
	return -1;

    if (cache->loaded == IMG_FLAG_UNLOADED && !buf->reshape_failure) {
	buf->redraw_mode = B_REDRAW_IMAGE;
	buf->image_unloaded = TRUE;
    }

    image->cache = cache;
    iw = image->width;
    ih = image->height;

    if (cache->loaded == IMG_FLAG_LOADED && (iw < 0 || ih < 0) && !buf->reshape_failure) {
	buf->need_reshape = TRUE;
	buf->redraw_mode = B_REDRAW_IMAGE;
    }

    v = buf->view;
    x = (int)((rcol - column + v->rootX + buf->lmargin) * pixel_per_char);
    sx = (int)((rcol - COLPOS(l, a_img->start.pos)) * pixel_per_char);

    if (sx == 0 && x + image->xoffset >= 0)
	x += image->xoffset;
    else
	sx -= image->xoffset;

    if ((cy = i - l->linenumber + image->y) < 0) {
	sy = (int)(-cy * pixel_per_line) - image->yoffset;
	y = v->rootY * pixel_per_line;
    }
    else {
	y = cy * pixel_per_line;
	sy = 0;

	if (y + image->yoffset >= 0)
	    y += image->yoffset;

	y += v->rootY * pixel_per_line;
    }

    if (iw > 0)
	w = iw - sx;
    else
	w = (int)(8 * pixel_per_char - sx);

    if (ih > 0)
	h = ih - sy;
    else
	h = (int)(pixel_per_line - sy);

    if (w > (int)((v->rootX + buf->lmargin + v->width) * pixel_per_char - x))
	w = (int)((v->rootX + buf->lmargin + v->width) * pixel_per_char - x);
    if (h > (int)((v->rootY + v->height) * pixel_per_line - y))
	h = (int)((v->rootY + v->height) * pixel_per_line - y);

    addImage(cache, x, y, sx, sy, w, h);
    v->draw_image_flag = TRUE;
    move(buf->view->rootY + i, buf->view->rootX + buf->lmargin + rcol - column);
#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
    delta = 1;
#endif

    for (j = pos ; rcol - column < ecol && j < a_img->end.pos ; j += delta) {
#ifdef MANY_CHARSET
	if ((delta = mb_mem_to_wchar_internal(&l->lineBuf[j], l->len - j, wc)) < 0)
	    delta = 1;
#elif defined(JP_CHARSET)
	if (CharType(l->propBuf[j]) == PC_KANJI1)
	    delta = 2;
	else
	    delta = 1;
#endif

	ncol = COLPOS(l, j + delta);

	if (ncol - column < ecol) {
#ifdef MANY_CHARSET
	    addWChar(wc, ncol - rcol, l->propBuf[j]);
#else
	    addChar(l->lineBuf[j], l->propBuf[j]);

#ifdef JP_CHARSET
	    if (delta == 2)
		addChar(l->lineBuf[j + 1], l->propBuf[j + 1]);
#endif
#endif
	}
	else
	    for (; rcol - column < ecol ; ++rcol)
		addChar(' ', 0);

	rcol = ncol;
    }

    *p_ncol = rcol;
    return j - pos;
}
#endif

Line *
redrawLine(Buffer *buf, Line *l, int i)
{
    BufferView *v;
    int j, pos, rcol, ncol, delta, nexthseq;
    Anchor *an;
#ifdef MANY_CHARSET
    mb_wchar_t wc;
#endif
    int column = buf->currentColumn;
    char *p;
    Lineprop *pr, prev_prop = 0, active_prop = 0;
#ifdef USE_ANSI_COLOR
    Linecolor *pc;
#endif
#ifdef USE_COLOR
    Anchor *a;
    ParsedURL url;
    int k, vpos = -1;
#endif
#ifdef USE_IMAGE
    Anchor *a_img;
    int use_image;

    use_image = activeImage && buf->image_flag == IMG_FLAG_AUTO && buf->img;
#endif

    if (l == NULL) {
	if (buf->async_buf && buf->async_buf->rfd >= 0 &&
	    !is_recorded_read_fd(buf->async_buf->rfd) &&
	    is_hook_recorded_read_fd(buf->async_buf->rfd))
	    record_read_fd(buf->async_buf->rfd, NULL, NULL, NULL);

	return NULL;
    }
    v = buf->view;
    move(v->rootY + i, v->rootX);
    if (buf->lmargin > 0) {
	char lno[(sizeof(int) * CHAR_BIT + 2) / 3 + sizeof(": ")];

	if (l->real_linenumber)
	    sprintf(lno, "%*ld: ", (int)(buf->lmargin - sizeof(": ") + 1), l->real_linenumber);
	else {
	    memset(lno, ' ', buf->lmargin);
	    lno[buf->lmargin] = '\0';
	}

	addstr(lno);
    }
    if (l->width < 0)
	l->width = COLPOS(l, l->len);
    if ((l->len == 0 || l->width - 1 < column) && v->rootX + buf->lmargin + v->width == COLS) {
	clrtoeolx();
	return l->next;
    }
    if ((an = retrieveCurrentAnchor(buf))) {
	nexthseq = an->hseq;
	an = NULL;
    }
    else
	nexthseq = -1;
    pos = columnPos(l, column);
    p = &(l->lineBuf[pos]);
    pr = &(l->propBuf[pos]);
#ifdef USE_ANSI_COLOR
    if (useColor && l->colorBuf)
	pc = &(l->colorBuf[pos]);
    else
	pc = NULL;
#endif
    rcol = COLPOS(l, pos);

#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
    delta = 1;
#endif
    for (j = 0; pos + j < l->len && rcol - column < v->width - buf->rmargin - buf->lmargin; j += delta) {
#ifdef USE_IMAGE
	if (use_image && pr[j] & PE_IMAGE &&
	    (a_img = retrieveAnchor(buf->img, l->linenumber, pos + j)) &&
	    a_img->link->img &&
	    (delta = redrawImage(buf, a_img, l, i, pos + j, rcol, column, v->width - buf->rmargin - buf->lmargin, &ncol)) >= 0) {
	    prev_prop = pr[j + delta - 1];
	    rcol = ncol;
#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
	    j += delta - 1;
	    delta = 1;
#endif
	    continue;
	}
#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
	else
	    delta = 1;
#endif
#endif
#ifdef USE_COLOR
	if (useVisitedColor && vpos <= pos + j && !(pr[j] & PE_VISITED)) {
	    a = retrieveAnchor(buf->href, l->linenumber, pos + j);
	    if (a) {
		parseURL2(a->link->href->url, &url, baseURL(buf));
		if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
		    for (k = a->start.pos; k < a->end.pos; k++)
			pr[k - pos] |= PE_VISITED;
		}
		vpos = a->end.pos;
	    }
	}
#endif
	if (pr[j] & ~prev_prop & PE_FORM) {
	    Anchor *fa;

	    if ((fa = retrieveAnchor(buf->formitem, l->linenumber, pos + j))) {
		if ((delta = redrawForm(buf, fa, l, pos + j, rcol, column, v->width - buf->rmargin - buf->lmargin + column, &ncol)) >= 0) {
		    prev_prop = pr[j + delta - 1];
		    rcol = ncol;
#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
		    j += delta - 1;
		    delta = 1;
#endif
		    continue;
		}
#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
		else
		    delta = 1;
#endif
	    }
	}
	if (!an || pos + j >= an->end.pos) {
	    prev_prop &= ~PE_ANCHOR;
	    an = NULL;
	}
	if (!(prev_prop & PE_ANCHOR))
	    active_prop = 0;
	if (pr[j] & ~prev_prop & PE_ANCHOR &&
	    (an = retrieveAnchor(buf->href, l->linenumber, pos + j)) &&
	    an->hseq >= 0 && an->hseq == nexthseq)
	    active_prop = PE_ACTIVE;
#ifdef JP_CHARSET
	if (CharType(pr[j]) == PC_KANJI1)
	    delta = 2;
	else
	    delta = 1;
#endif
#ifdef MANY_CHARSET
	if ((delta = mb_mem_to_wchar_internal(&l->lineBuf[pos + j], l->len - (pos + j), wc)) < 0)
	    delta = 1;
#endif
	ncol = COLPOS(l, pos + j + delta);
	if (rcol < column) {
	    for (rcol = column; rcol < ncol; rcol++)
		addChar(' ', 0);
	    continue;
	}
	if (ncol - column > v->width - buf->rmargin - buf->lmargin)
	    break;
#ifdef USE_ANSI_COLOR
	if (pc)
	    do_color(pc[j]);
#endif
#ifdef MANY_CHARSET
	if (wc == '\t') {
	    for (; rcol < ncol; rcol++)
		addChar(' ', 0);
	}
	else {
	    addWChar(wc, ncol - rcol, pr[j] | active_prop);
	    rcol = ncol;
	}
#else
	if (p[j] == '\t') {
	    for (; rcol < ncol; rcol++)
		addChar(' ', 0);
	}
	else {
	    addChar(p[j], pr[j] | active_prop);
#ifdef JP_CHARSET
	    if (delta == 2)
		addChar(p[j + 1], pr[j + 1] | active_prop);
#endif
	}
	rcol = ncol;
#endif
	prev_prop = pr[j + delta - 1];
    }
    if (somode) {
	somode = FALSE;
	standend();
    }
    if (ulmode) {
	ulmode = FALSE;
	underlineend();
    }
    if (bomode) {
	bomode = FALSE;
	boldend();
    }
    if (emph_mode) {
	emph_mode = FALSE;
	boldend();
    }

    if (anch_mode) {
	anch_mode = FALSE;
	EFFECT_ANCHOR_END;
    }
    if (imag_mode) {
	imag_mode = FALSE;
	EFFECT_IMAGE_END;
    }
    if (form_mode) {
	form_mode = FALSE;
	EFFECT_FORM_END;
    }
    if (visited_mode) {
	visited_mode = FALSE;
	EFFECT_VISITED_END;
    }
    if (active_mode) {
	active_mode = FALSE;
	EFFECT_ACTIVE_END;
    }
    if (mark_mode) {
	mark_mode = FALSE;
	EFFECT_MARK_END;
    }
#ifndef KANJI_SYMBOLS
    if (graph_mode) {
	graph_mode = FALSE;
	graphend();
    }
#endif				/* not KANJI_SYMBOLS */
#ifdef USE_ANSI_COLOR
    if (color_mode)
	do_color(0);
#endif
    if (buf->rmargin) {
	if (pos + j < l->len && LineTruncated && *LineTruncated) {
	    standout();
	    for (; rcol - column < v->width - buf->lmargin ; ++rcol)
		addch(*LineTruncated);
	    standend();
	}
	else if (l->next && !l->next->real_linenumber && LineContinued && *LineContinued) {
	    standout();
	    for (; rcol - column < v->width - buf->lmargin ; ++rcol)
		addch(*LineContinued);
	    standend();
	}
    }
    if (rcol < column)
	rcol = column;
    if (rcol - column < COLS) {
	if (v->rootX + v->width < COLS)
	    for (; rcol - column < v->width - buf->lmargin ; ++rcol)
		addch(' ');
	else
	    clrtoeolx();
    }
    if (buf->hmarklist)
	buf->hmarklist->prevhseq = nexthseq;
    return l->next;
}

int
redrawLineRegion(Buffer *buf, Line *l, int i, int bpos, int epos)
{
    int j, pos, rcol, ncol, delta, nexthseq;
    Anchor *an;
#ifdef MANY_CHARSET
    mb_wchar_t wc;
#endif
    int column = buf->currentColumn;
    char *p;
    Lineprop *pr, prev_prop, active_prop;
#ifdef USE_ANSI_COLOR
    Linecolor *pc;
#endif
    int bcol, ecol;
#ifdef USE_COLOR
    Anchor *a;
    ParsedURL url;
    int k, vpos = -1;
#endif
    BufferView *v;
#ifdef USE_IMAGE
    Anchor *a_img;
    int use_image;

    use_image = activeImage && buf->image_flag == IMG_FLAG_AUTO && buf->img;
#endif

    if (l == NULL)
	return 0;
    if ((an = retrieveCurrentAnchor(buf))) {
	nexthseq = an->hseq;
	an = NULL;
    }
    else
	nexthseq = -1;
    pos = columnPos(l, column);
    prev_prop = active_prop = 0;
    p = &(l->lineBuf[pos]);
    pr = &(l->propBuf[pos]);
#ifdef USE_ANSI_COLOR
    if (useColor && l->colorBuf)
	pc = &(l->colorBuf[pos]);
    else
	pc = NULL;
#endif
    rcol = COLPOS(l, pos);
    bcol = COLPOS(l, bpos);
    v = buf->view;
    if ((ecol = COLPOS(l, epos)) > v->width - buf->rmargin - buf->lmargin + column)
	ecol = v->width - buf->rmargin - buf->lmargin + column;

#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
    delta = 1;
#endif
    for (j = 0; rcol - column < v->width - buf->rmargin - buf->lmargin && pos + j < l->len; j += delta) {
#ifdef USE_IMAGE
	if (use_image && rcol >= bcol && rcol < ecol && pr[j] & PE_IMAGE &&
	    (a_img = retrieveAnchor(buf->img, l->linenumber, pos + j)) &&
	    a_img->link->img &&
	    (delta = redrawImage(buf, a_img, l, i, pos + j, rcol, column, ecol - column, &ncol)) >= 0) {
	    prev_prop = pr[j + delta - 1];
	    rcol = ncol;
#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
	    j += delta - 1;
	    delta = 1;
#endif
	    continue;
	}
#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
	else
	    delta = 1;
#endif
#endif
#ifdef USE_COLOR
	if (useVisitedColor && vpos <= pos + j && !(pr[j] & PE_VISITED)) {
	    a = retrieveAnchor(buf->href, l->linenumber, pos + j);
	    if (a) {
		parseURL2(a->link->href->url, &url, baseURL(buf));
		if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
		    for (k = a->start.pos; k < a->end.pos; k++)
			pr[k - pos] |= PE_VISITED;
		}
		vpos = a->end.pos;
	    }
	}
#endif
	if (rcol >= bcol && rcol < ecol && pr[j] & ~prev_prop & PE_FORM) {
	    Anchor *fa;

	    if ((fa = retrieveAnchor(buf->formitem, l->linenumber, pos + j))) {
		move(v->rootY + i, v->rootX + buf->lmargin + rcol - column);
		if ((delta = redrawForm(buf, fa, l, pos + j, rcol, column, ecol, &ncol)) >= 0) {
		    prev_prop = pr[j + delta - 1];
		    rcol = ncol;
#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
		    j += delta - 1;
		    delta = 1;
#endif
		    continue;
		}
#if !defined(JP_CHARSET) && !defined(MANY_CHARSET)
		else
		    delta = 1;
#endif
	    }
	}
	if (!an || pos + j >= an->end.pos) {
	    prev_prop &= ~PE_ANCHOR;
	    an = NULL;
	}
	if (!(prev_prop & PE_ANCHOR))
	    active_prop = 0;
	if (pr[j] & ~prev_prop & PE_ANCHOR &&
	    (an = retrieveAnchor(buf->href, l->linenumber, pos + j)) &&
	    an->hseq >= 0 && an->hseq == nexthseq)
	    active_prop = PE_ACTIVE;
#ifdef JP_CHARSET
	if (CharType(pr[j]) == PC_KANJI1)
	    delta = 2;
	else
	    delta = 1;
#endif
#ifdef MANY_CHARSET
	if ((delta = mb_mem_to_wchar_internal(&l->lineBuf[pos + j], l->len - (pos + j), wc)) < 0)
	    delta = 1;
#endif
	ncol = COLPOS(l, pos + j + delta);
	if (ncol - column > v->width - buf->rmargin - buf->lmargin)
	    break;
#ifdef USE_ANSI_COLOR
	if (pc)
	    do_color(pc[j]);
#endif
	if (rcol >= bcol && rcol < ecol) {
	    if (rcol < column) {
		move(v->rootY + i, v->rootX + buf->lmargin);
		for (rcol = column; rcol < ncol; rcol++)
		    addChar(' ', 0);
		continue;
	    }
	    move(v->rootY + i, v->rootX + buf->lmargin + rcol - column);
#ifdef MANY_CHARSET
	    if (wc == '\t') {
		for (; rcol < ncol; rcol++)
		    addChar(' ', 0);
	    }
	    else {
		addWChar(wc, ncol - rcol, pr[j] | active_prop);
	    }
#else
	    if (p[j] == '\t') {
		for (; rcol < ncol; rcol++)
		    addChar(' ', 0);
	    }
	    else {
		addChar(p[j], pr[j] | active_prop);
#ifdef JP_CHARSET
		if (delta == 2)
		    addChar(p[j + 1], pr[j + 1] | active_prop);
#endif
	    }
#endif
	    prev_prop = pr[j + delta - 1];
	}
	rcol = ncol;
    }
    if (somode) {
	somode = FALSE;
	standend();
    }
    if (ulmode) {
	ulmode = FALSE;
	underlineend();
    }
    if (bomode) {
	bomode = FALSE;
	boldend();
    }
    if (emph_mode) {
	emph_mode = FALSE;
	boldend();
    }

    if (anch_mode) {
	anch_mode = FALSE;
	EFFECT_ANCHOR_END;
    }
    if (imag_mode) {
	imag_mode = FALSE;
	EFFECT_IMAGE_END;
    }
    if (form_mode) {
	form_mode = FALSE;
	EFFECT_FORM_END;
    }
    if (visited_mode) {
	visited_mode = FALSE;
	EFFECT_VISITED_END;
    }
    if (active_mode) {
	active_mode = FALSE;
	EFFECT_ACTIVE_END;
    }
    if (mark_mode) {
	mark_mode = FALSE;
	EFFECT_MARK_END;
    }
#ifndef KANJI_SYMBOLS
    if (graph_mode) {
	graph_mode = FALSE;
	graphend();
    }
#endif				/* not KANJI_SYMBOLS */
#ifdef USE_ANSI_COLOR
    if (color_mode)
	do_color(0);
#endif
    if (buf->hmarklist)
	buf->hmarklist->prevhseq = nexthseq;
    return rcol - column;
}

#define do_effect1(effect,modeflag,action_start,action_end) \
if (m & effect) { \
    if (!modeflag) { \
	action_start; \
	modeflag = TRUE; \
    } \
}

#define do_effect2(effect,modeflag,action_start,action_end) \
if (modeflag) { \
    action_end; \
    modeflag = FALSE; \
}

void
do_effects(Lineprop m)
{
    /* effect end */
    do_effect2(PE_UNDER, ulmode, underline(), underlineend());
    do_effect2(PE_STAND, somode, standout(), standend());
    do_effect2(PE_BOLD, bomode, bold(), boldend());
    do_effect2(PE_EMPH, emph_mode, bold(), boldend());
    do_effect2(PE_ANCHOR, anch_mode, EFFECT_ANCHOR_START, EFFECT_ANCHOR_END);
    do_effect2(PE_IMAGE, imag_mode, EFFECT_IMAGE_START, EFFECT_IMAGE_END);
    do_effect2(PE_FORM, form_mode, EFFECT_FORM_START, EFFECT_FORM_END);
    do_effect2(PE_VISITED, visited_mode, EFFECT_VISITED_START,
	       EFFECT_VISITED_END);
    do_effect2(PE_ACTIVE, active_mode, EFFECT_ACTIVE_START, EFFECT_ACTIVE_END);
    do_effect2(PE_MARK, mark_mode, EFFECT_MARK_START, EFFECT_MARK_END);
#ifndef KANJI_SYMBOLS
    if (graph_mode) {
	graphend();
	graph_mode = FALSE;
    }
#endif				/* not KANJI_SYMBOLS */

    /* effect start */
    do_effect1(PE_UNDER, ulmode, underline(), underlineend());
    do_effect1(PE_STAND, somode, standout(), standend());
    do_effect1(PE_BOLD, bomode, bold(), boldend());
    do_effect1(PE_EMPH, emph_mode, bold(), boldend());
    do_effect1(PE_ANCHOR, anch_mode, EFFECT_ANCHOR_START, EFFECT_ANCHOR_END);
    do_effect1(PE_IMAGE, imag_mode, EFFECT_IMAGE_START, EFFECT_IMAGE_END);
    do_effect1(PE_FORM, form_mode, EFFECT_FORM_START, EFFECT_FORM_END);
    do_effect1(PE_VISITED, visited_mode, EFFECT_VISITED_START,
	       EFFECT_VISITED_END);
    do_effect1(PE_ACTIVE, active_mode, EFFECT_ACTIVE_START, EFFECT_ACTIVE_END);
    do_effect1(PE_MARK, mark_mode, EFFECT_MARK_START, EFFECT_MARK_END);
#ifndef KANJI_SYMBOLS
    if (m & PC_RULE) {
	if (!graph_mode && graph_ok()) {
	    graphstart();
	    graph_mode = TRUE;
	}
    }
#endif				/* not KANJI_SYMBOLS */
}

#ifdef USE_ANSI_COLOR
void
do_color(Linecolor c)
{
    if (c & 0x8)
	setfcolor(c & 0x7);
    else if (color_mode & 0x8)
	setfcolor(basic_color);
#ifdef USE_BG_COLOR
    if (c & 0x80)
	setbcolor((c >> 4) & 0x7);
    else if (color_mode & 0x80)
	setbcolor(bg_color);
#endif
    color_mode = c;
}
#endif

void
addChar(char c, Lineprop mode)
{
    Lineprop m = CharEffect(mode);

#ifdef JP_CHARSET
    if (CharType(mode) != PC_KANJI2)
#endif				/* JP_CHARSET */
    do_effects(m);
#ifndef MANY_CHARSET
#ifndef KANJI_SYMBOLS
    if (m & PC_RULE) {
	int i;

	i = (((unsigned char)c < RULEC_CODE_BEG || (unsigned char)c > RULEC_CODE_END) ?
	     RULEC_INVALID : rule_translate[(unsigned char)c - RULEC_CODE_BEG]);
	if (graph_mode)
	    addch(g_rule[i]);
	else
	    addch(alt_rule[i]);
    } else
#endif				/* not KANJI_SYMBOLS */
    if (IS_UNPRINTABLE_ASCII(c, mode)) {
	addstr(Sprintf("\\%3o", (unsigned char)c)->ptr);
    }
    else
#endif
    if (c == '\t') {
	addch(c);
    }
    else if (c == DEL_CODE)
	addstr("^?");
    else if (IS_UNPRINTABLE_CONTROL(c, mode)) {	/* Control code */
	addch('^');
	addch(c + '@');
    }
    else if (c != '\n')
	addch(c);
    else	/* \n */
	addch(' ');
}

#ifdef MANY_CHARSET
void
addWChar(mb_wchar_t wc, size_t len, Lineprop mode)
{
    if (wc >= 0x21) {
	Lineprop m = CharEffect(mode);

	do_effects(m);
#ifndef KANJI_SYMBOLS
	if (m & PC_RULE) {
	    int i;

	    i = ((wc < MB_CTL_ENC(RULEC_CODE_BEG) || wc > MB_CTL_ENC(RULEC_CODE_END)) ?
		 RULEC_INVALID : rule_translate[wc - MB_CTL_ENC(RULEC_CODE_BEG)]);
	    if (graph_mode)
		addch(g_rule[i]);
	    else
		addch(alt_rule[i]);
	}
	else
#endif				/* not KANJI_SYMBOLS */
	    add_wch(wc, len);
    }
    else
	addChar(wc, mode);
}
#endif

GeneralList *message_list = NULL;

void
record_err_message(char *s)
{
    if (fmInitialized) {
	if (!message_list)
	    message_list = newGeneralList();
	if (message_list->nitem >= LINES)
	    popValue(message_list);
	pushValue(message_list, allocStr(s, -1));
    }
    else if (w3m_backend >= BACKEND_VERY_VERBOSE)
	backend_message(s, 2);
}

/* 
 * List of error messages
 */
Buffer *
message_list_panel(void)
{
    Str tmp = Strnew_size(LINES * COLS);
    ListItem *p;

    Strcat_charp(tmp,
		 "<html><head>"
#ifdef MANY_CHARSET
		 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=x-moe-internal\">\n"
#endif
		 "<title>List of error messages</title></head><body>"
		 "<h1>List of error messages</h1><table cellpadding=0>\n");
    if (message_list)
      for (p = message_list->last ; p ; p = p->prev)
	Strcat_m_charp(tmp, "<tr><td><pre>", html_quote(p->ptr), "</pre></td></tr>\n", NULL);
    else
      Strcat_charp(tmp, "<tr><td>(no message recorded)</td></tr>\n");
    Strcat_charp(tmp, "</table></body></html>");
    return loadHTMLString(tmp, NULL, &main_p0env);
}

void
message(char *s)
{
    int x, y;

    if (!fmInitialized)
	return;
    if (inputLineBusy || !(Currentbuf && Currentbuf->view))
	getyx(y, x);
    else {
	y = Currentbuf->view->rootY + Currentbuf->cursorY;
	x = Currentbuf->view->rootX + Currentbuf->lmargin + Currentbuf->cursorX;
    }
    move(LASTLINE, 0);
    addnstr(s, COLS - 1);
    clrtoeolx();
    move(y, x);
    dont_buffername = FALSE;
}

void
disp_message_nsec(char *s, int redraw_current, int sec, int purge, int mouse)
{
#ifdef USE_MOUSE
    int mouseActiveP;
#endif
    if (!fmInitialized) {
	if (w3m_backend)
	    backend_message(conv_to_system(s), w3m_backend >= BACKEND_VERY_VERBOSE);
	else
	    fprintf(stderr, "%s\n", conv_to_system(s));
	return;
    }
    message(s);
    refresh();
    dont_buffername = TRUE;

    if (sec > 0) {
#ifdef USE_MOUSE
	if (!(mouseActiveP = mouseActive) &&
	    !inputLineBusy && mouse && use_mouse)
	    mouse_active();
#endif
	sleep_till_anykey(sec, purge);
#ifdef USE_MOUSE
	if (!mouseActiveP && mouse && use_mouse)
	    mouse_inactive();
#endif
    }

    if (Currentbuf != NULL && redraw_current)
	displayBuffer(Currentbuf, B_NORMAL);
}

void
disp_message(char *s, int redraw_current)
{
    disp_message_nsec(s, redraw_current, 0, FALSE, TRUE);
}

#ifdef USE_MOUSE
void
disp_message_nomouse(char *s, int redraw_current)
{
    disp_message_nsec(s, redraw_current, 10, FALSE, FALSE);
}
#endif

void
cursorUp(Buffer *buf, int n)
{
    if (n <= 0 || buf->firstLine == NULL)
	return;
    if (buf->cursorY >= n)
	cursorUpDown(buf, -n);
    else {
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	Line *cl;

	cl = buf->currentLine;
#endif
	buf->topLine = lineSkip(buf, buf->topLine,
				n * ((scroll_amount > 0 && scroll_amount < buf->view->height - 1) ?
				     -scroll_amount : -buf->view->height / 2),
				0);
	if (buf->currentLine->prev != NULL)
	    buf->currentLine = buf->currentLine->prev;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	select_menu_line(buf, cl);
#endif
	arrangeLine(buf);
    }
}

void
cursorDown(Buffer *buf, int n)
{
    if (n <= 0 || buf->firstLine == NULL)
	return;
    if (buf->cursorY < buf->view->height - n)
	cursorUpDown(buf, n);
    else {
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	Line *cl;

	cl = buf->currentLine;
#endif
	buf->topLine = lineSkip(buf, buf->topLine,
				n * ((scroll_amount > 0 && scroll_amount < buf->view->height - 1) ?
				     scroll_amount : buf->view->height / 2),
				0);
	if (buf->currentLine->next != NULL)
	    buf->currentLine = buf->currentLine->next;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	select_menu_line(buf, cl);
#endif
	arrangeLine(buf);
    }
}

void
cursorUpDown(Buffer *buf, int n)
{
    Line *cl;

    if (buf->firstLine == NULL)
	return;
    cl = buf->currentLine;
    if ((buf->currentLine = currentLineSkip(buf, cl, n, 0)) == cl)
	return;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    select_menu_line(buf, cl);
#endif
    arrangeLine(buf);
}

int
formRealPos(Buffer *buf, Line *l, int pos, int *epos)
{
    Anchor *a;
    FormItemList *fi;
    char *p;
    int n;

    if ((a = retrieveAnchor(buf->formitem, l->linenumber, pos)) &&
	(fi = a->link->fi) &&
	(p = formRealText(fi, l, &n))) {
	int col, bcol, ecol, rcol, ncol, delta;
#ifdef MANY_CHARSET
	mb_wchar_t wc;
#endif

	bcol = COLPOS(l, a->start.pos);
	ecol = COLPOS(l, a->end.pos);
	col = COLPOS(l, pos);
#if !defined(JP_CHARSET) || !defined(MANY_CHARSET)
	delta = 1;
#endif

	for (rcol = bcol ; n > 0 ; p += delta, n -= delta) {
#ifdef MANY_CHARSET
	    if ((delta = mb_mem_to_wchar_internal(p, n, wc)) < 0)
		delta = 1;

	    ncol = wc == '\t' ? (rcol - bcol / Tabstop + 1) * Tabstop + bcol : rcol + ttyfix_wchar_width(wc);
#else
#ifdef JP_CHARSET
	    if (IS_KANJI1(*p) && n > 1)
		delta = 2;
	    else
		delta = 1;
#endif

	    ncol = *p == '\t' ? (rcol - bcol / Tabstop + 1) * Tabstop + bcol : rcol + delta;
#endif

	    if (ncol > col) {
		if (epos) {
		    if (ncol > ecol)
			ncol = ecol;

		    *epos = columnPos(l, ncol);
		}

		return columnPos(l, rcol);
	    }

	    bcol = rcol;
	    rcol = ncol;
	}

	if (rcol < col) {
	    bcol = col;
	    rcol = bcol + 1;
	}

	if (epos)
	    *epos = columnPos(l, rcol);

	return columnPos(l, bcol);
    }

    return -1;
}

void
cursorRight(Buffer *buf, int n)
{
#ifdef MANY_CHARSET
    int cpos, vpos2;
    size_t cb, ce;
#else
    int i, delta = 1, cpos, vpos2;
#endif
    Line *l = buf->currentLine;
#ifndef MANY_CHARSET
    Lineprop *p;
#endif
    int dont_next_line;

    if (!n || buf->firstLine == NULL)
	return;

    if (n > 0)
	dont_next_line = FALSE;
    else {
	n = -n;
	dont_next_line = TRUE;
    }

    do {
#ifdef MANY_CHARSET
	cb = buf->pos;
	ce = l->len;
	mb_mem_to_wchar(l->lineBuf, &cb, &ce);

	if (ce < l->len)
	    buf->pos = cb = ce;
	else if (l->next) {
	    if (dont_next_line)
		break;

	    cursorDown(buf, 1);

	    if (l == buf->currentLine)
		break;

	    l = buf->currentLine;
	    buf->pos = cb = 0;
	}
	else
	    buf->pos = cb;
#else
	i = buf->pos;
	p = l->propBuf;

#ifdef JP_CHARSET
	if (CharType(p[i]) == PC_KANJI1)
	    delta = 2;
#endif				/* JP_CHARSET */

	if (i + delta < l->len) {
	    buf->pos = i + delta;
	}
	else if (l->next) {
	    if (dont_next_line)
		break;

	    cursorDown(buf, 1);

	    if (l == buf->currentLine)
		break;

	    l = buf->currentLine;
	    buf->pos = 0;
	}
	else if (l->len == 0) {
	    buf->pos = 0;
	}
	else {
	    buf->pos = l->len -1;

#ifdef JP_CHARSET
	    if (CharType(p[buf->pos]) == PC_KANJI2)
		buf->pos--;
#endif                          /* JP_CHARSET */
	}
#endif
    } while (--n > 0 && buf->pos < l->len);

    if (l->propBuf[buf->pos] & PE_FORM) {
	int bpos, epos;

	if ((bpos = formRealPos(buf, l, buf->pos, &epos)) >= 0 &&
	    bpos < buf->pos)
	    buf->pos = epos;
    }

    cpos = COLPOS(l, buf->pos);
    buf->visualpos = cpos - buf->currentColumn;
#ifdef MANY_CHARSET
    ce = l->len;
    mb_mem_to_wchar(l->lineBuf, &cb, &ce);
    vpos2 = COLPOS(l, ce) - buf->currentColumn - 1;
#else
    delta = 1;

#ifdef JP_CHARSET
    if (CharType(p[buf->pos]) == PC_KANJI1)
        delta = 2;
#endif                          /* JP_CHARSET */

    vpos2 = COLPOS(l, buf->pos + delta) - buf->currentColumn - 1;
#endif

    if (buf->visualpos < 0 || vpos2 >= buf->view->width - buf->rmargin - buf->lmargin) {
	int x;

	x = ((scroll_amount > 0 && scroll_amount < buf->view->width - buf->rmargin - buf->lmargin) ?
	     scroll_amount : (buf->view->width - buf->rmargin - buf->lmargin) / 2);
	columnSkip(buf,
		   x
		   + (vpos2 - buf->view->width + buf->rmargin + buf->lmargin)
		   - (vpos2 - buf->view->width + buf->rmargin + buf->lmargin) % x);
	buf->visualpos = cpos - buf->currentColumn;
    }

    buf->cursorX = buf->visualpos;
}

void
cursorLeft(Buffer *buf, int n)
{
#ifdef MANY_CHARSET
    int cpos;
    size_t cb, ce;
#else
    int i, delta = 1, cpos;
#endif
    Line *l = buf->currentLine;
#ifndef MANY_CHARSET
    Lineprop *p;
#endif
    int dont_prev_line;

    if (!n || buf->firstLine == NULL)
	return;

    if (n > 0)
	dont_prev_line = FALSE;
    else {
	n = -n;
	dont_prev_line = TRUE;
    }

    do {
	if (!buf->pos && l->prev) {
	    if (dont_prev_line)
		break;

	    do {
		cursorUp(buf, 1);

		if (l == buf->currentLine)
		    break;

		l = buf->currentLine;
		buf->pos = l->len;
	    } while (!buf->pos && l->prev);
	}

#ifdef MANY_CHARSET
	if (buf->pos > 0) {
	    ce = buf->pos;
	    cb = ce - 1;
	    mb_mem_to_wchar(l->lineBuf, &cb, &ce);
	    buf->pos = cb;
	}
#else
	i = buf->pos;
	p = l->propBuf;
#ifdef JP_CHARSET
	if (i >= 2 && CharType(p[i - 1]) == PC_KANJI2)
	    delta = 2;
#endif				/* JP_CHARSET */
	if (i > delta)
	    buf->pos = i - delta;
	else
	    buf->pos = 0;
#endif
    } while (--n > 0 && buf->pos > 0);

    if (l->propBuf[buf->pos] & PE_FORM) {
	int k;

	if ((k = formRealPos(buf, l, buf->pos, NULL)) >= 0)
	    buf->pos = k;
    }

    cpos = COLPOS(l, buf->pos);
    buf->visualpos = cpos - buf->currentColumn;

    if (buf->visualpos < 0 || buf->visualpos >= buf->view->width - buf->rmargin - buf->lmargin) {
	int x;

	x = ((scroll_amount > 0 && scroll_amount < buf->view->width - buf->rmargin - buf->lmargin) ?
	     scroll_amount : (buf->view->width - buf->rmargin - buf->lmargin) / 2);
	columnSkip(buf, -x + buf->visualpos - buf->visualpos % x);
	buf->visualpos = cpos - buf->currentColumn;
    }

    buf->cursorX = buf->visualpos;
}

void
cursorHome(Buffer *buf)
{
    buf->visualpos = 0;
    buf->cursorX = buf->cursorY = 0;
}


/* 
 * Arrange line,column and cursor position according to current line and
 * current position.
 */
void
arrangeCursor(Buffer *buf)
{
    int col,col2;
#ifdef MANY_CHARSET
    size_t cb, ce;
#else
    int delta = 1;
#endif
    if (buf == NULL || buf->currentLine == NULL)
	return;
    /* Arrange line */
    if (buf->currentLine->linenumber - buf->topLine->linenumber >= buf->view->height ||
	buf->currentLine->linenumber < buf->topLine->linenumber) {
	buf->topLine = buf->currentLine;
    }
    /* Arrange column */
    if (buf->currentLine->len == 0)
	buf->pos = 0;
    else if (buf->pos >= buf->currentLine->len)
	buf->pos = buf->currentLine->len - 1;
#ifdef MANY_CHARSET
    cb = buf->pos;
    ce = buf->currentLine->len;
    mb_mem_to_wchar(buf->currentLine->lineBuf, &cb, &ce);
    buf->pos = cb;
#else
#ifdef JP_CHARSET
    if (CharType(buf->currentLine->propBuf[buf->pos]) == PC_KANJI2)
	buf->pos--;
#endif				/* JP_CHARSET */
#endif
    col = COLPOS(buf->currentLine, buf->pos);
#ifdef MANY_CHARSET
    cb = ce;
    ce = buf->currentLine->len;
    mb_mem_to_wchar(buf->currentLine->lineBuf, &cb, &ce);
    col2 = COLPOS(buf->currentLine, ce);
#else
#ifdef JP_CHARSET
    if (CharType(buf->currentLine->propBuf[buf->pos]) == PC_KANJI1)
        delta = 2;
#endif                         /* JP_CHARSET */
    col2 = COLPOS(buf->currentLine, buf->pos + delta);
#endif
    if (col < buf->currentColumn || col2 > buf->view->width - buf->rmargin - buf->lmargin + buf->currentColumn) {
	buf->currentColumn = 0;
        if (col2 > buf->view->width - buf->rmargin - buf->lmargin)
	    columnSkip(buf, col);
    }
    /* Arrange cursor */
    buf->cursorY = buf->currentLine->linenumber - buf->topLine->linenumber;
    buf->visualpos = buf->cursorX = COLPOS(buf->currentLine, buf->pos) - buf->currentColumn;
#ifdef DISPLAY_DEBUG
    fprintf(stderr, "arrangeCursor: column=%d, cursorX=%d, visualpos=%d, pos=%d, len=%d\n",
	    buf->currentColumn, buf->cursorX, buf->visualpos,
	    buf->pos, buf->currentLine->len);
#endif
}

void
arrangeLine(Buffer *buf)
{
    int i, cpos;
    Line *l;

    if (buf->firstLine == NULL)
	return;
    l = buf->currentLine;
    buf->cursorY = l->linenumber - buf->topLine->linenumber;
    i = columnPos(l, buf->currentColumn + buf->visualpos);
    cpos = COLPOS(l, i) - buf->currentColumn;
    if (cpos >= 0) {
	buf->cursorX = cpos;
	buf->pos = i;
    }
    else if (buf->currentLine->len > i) {
	int delta = 1;
#ifdef JP_CHARSET
	if (l->len > i + 1 &&
	    CharType(l->propBuf[i + 1]) == PC_KANJI2)
	    delta = 2;
#endif
	buf->cursorX = 0;
	buf->pos = i;
	if (COLPOS(l, i + delta) <= buf->currentColumn)
	    buf->pos += delta;
    }
    else {
	buf->cursorX = 0;
	buf->pos = 0;
    }
    if (l->propBuf[buf->pos] & PE_FORM) {
	int k;

	k = formRealPos(buf, l, buf->pos, NULL);

	if (k >= 0 && k != buf->pos) {
	    buf->pos = k;
	    arrangeCursor(buf);
	}
    }
#ifdef DISPLAY_DEBUG
    fprintf(stderr, "arrangeLine: column=%d, cursorX=%d, visualpos=%d, pos=%d, len=%d\n",
	    buf->currentColumn, buf->cursorX, buf->visualpos,
	    buf->pos, buf->currentLine->len);
#endif
}

void
cursorXY(Buffer *buf, int x, int y)
{
    int oldpos;

    cursorUpDown(buf, y - buf->cursorY);

    if (buf->cursorX > x) {
	while (buf->pos > 0 && buf->cursorX > x)
	    cursorLeft(buf, -1);
    }
    else if (buf->cursorX < x) {
	while (buf->cursorX < x && buf->pos < buf->currentLine->len) {
	    oldpos = buf->pos;
	    cursorRight(buf, -1);

	    if (buf->pos == oldpos)
		break;
	}

	if (buf->cursorX > x && buf->pos > 0)
	    cursorLeft(buf, 1);
    }
}

void
restorePosition(Buffer *buf, Buffer *orig)
{
    long curlno;

    buf->topLine = lineSkip(buf, buf->firstLine, TOP_LINENUMBER(orig) - 1,
			    FALSE);
    curlno = CUR_LINENUMBER(orig);

#ifdef USE_IMAGE
    if (buf->img && orig->img) {
	SeqMap *map;
	Anchor *a;
	AnchorLink *dv;
	int dn;
	Image *simg, *dimg = NULL;

	map = &buf->iseqmap;
	dv = buf->img->alv;
	dn = buf->img->nlink;

	if (!(a = retrieveCurrentImg(orig)))
	    a = closest_prev_anchor(orig->img, NULL, orig->pos, curlno);

	while (a &&
	       (!(simg = a->link->img) ||
		a->hseq < 0 || a->hseq >= map->n ||
		map->v[a->hseq] < 0 || map->v[a->hseq] >= buf->img->nlink ||
		!(dimg = dv[map->v[a->hseq]].img) ||
		strcmp((dimg->url ? dimg->url : ""), (simg->url ? simg->url : ""))))
	    a = closest_prev_anchor(orig->img, NULL, a->start.pos, a->start.line);

	if (a)
	    curlno += -(simg->y + simg->rows) + dimg->y + dimg->rows;
    }
#endif

    gotoLine(buf, curlno);
    buf->pos = orig->pos;
    buf->currentColumn = orig->currentColumn;

    if (!w3m_backend)
	arrangeCursor(buf);
}

/* Local Variables:    */
/* c-basic-offset: 4   */
/* tab-width: 8        */
/* End:                */
