/* vi:ts=4:sw=4
 *
 * VIM - Vi IMproved
 *
 * Code Contributions By:	Atsushi  Nakamura		anaka@mrit.mei.co.jp
 */

/*
 * track.c: functions for draw tracks.
 */

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "param.h"
#ifdef JP
#include "jp.h"
#endif

extern u_char *get_inserted();

/*
 *	directions where a line exists.
 *	1 : up
 *	2 : down
 *	4 : left
 *	8 : right
 */

#define TK_U	1
#define TK_D	2
#define TK_L	4
#define TK_R	8

struct tracktab
{
	char	*name;
	int		vw;
	char	**ch;
};

#ifdef JP

#define JP_TRACKTAB
#include "jptab.h"
#undef JP_TRACKTAB

#endif

char *
tracktab_as[] =
{
	" ",  "V",  "A",  "|",  ">",  "+",  "+",  "+",
	"<",  "+",  "+",  "+",  "-",  "+",  "+",  "+"
};

static struct tracktab
tracktabs[] =
{
	{ "as",    1, tracktab_as    },
#ifdef JP
	{ "jp",    2, tracktab_jp    },
	{ "bj",    2, tracktab_bj    },
	{ "hj",    2, tracktab_hj    },
	{ "vj",    2, tracktab_vj    },
# ifdef DOSGEN
	{ "pc98",  2, tracktab_pc98  },
	{ "pc98b", 2, tracktab_pc98b },
# endif
#endif
	{ NULL, 0, NULL }
},
*tracktab = &tracktabs[0];

	static void
tracktab_sw(tname)
	char *tname;
{
	struct tracktab *tp;

	if (tname && !strcmp(tracktab->name, tname))
		return;

	for(tp = &tracktabs[0]; tp->name; tp++)
		if (!strcmp(tp->name, tname))
		{
			tracktab = tp;
			break;
		}

	if (!(tp->name))
		smsg("%s unknown track character set.", tname);
}

	char *
tracktab_next(tset)
	char *tset;
{
	struct tracktab *tp;

	for(tp = &tracktabs[0]; tp->name; tp++)
		if (!strcmp(tp->name, tset))
		{
			tp++;
			break;
		}
	if (!tp->name)
		tp = &tracktabs[0];

	tracktab = tp;
	return tp->name;
}

	char *
tracktab_prev(tset)
	char *tset;
{
	struct tracktab *tp;

	tp = &tracktabs[0];
	if (strcmp(tp->name, tset))
		for(tp++; tp->name; tp++)
			if (!strcmp(tp->name, tset))
				return (tp-1)->name;

	for(tp = &tracktabs[0]; tp->name; tp++);
	return (tp-1)->name;
}

#define TV_JUST		1
#define TV_NEXT		2
#define TV_PREV		3
#define TV_FPAD		-1
#define TV_BPAD		-2

	static char *
track_vcol(line, cvcol, mode)
	char *line;
	int cvcol, mode;
{
	int  vcol, pcol;
	char *ctop = line;

	vcol = pcol = 0;
	while(*line && vcol < cvcol)
	{
		pcol = vcol;
		ctop = line;
#ifdef JP
		if (IsKanji(*line))
		{
			vcol += 2;
			line += 2;
		}
		else
#endif
		vcol += chartabsize(* line++, vcol);
	}

	switch(mode)
	{
		case TV_JUST:
			if (vcol != cvcol)
				return NULL;

		case TV_NEXT:
			return line;

		case TV_PREV:		/* tail byte of the previous char */
			return vcol == cvcol ? line: ctop;

		case TV_FPAD:
			if (!*line)
				return (char *)(long)(cvcol - vcol);

			return (char *)(long)((vcol == cvcol ? 0 : cvcol - pcol));

		case TV_BPAD:
			return (char *)(long)((vcol - cvcol));

		default: /* error */
			return NULL;
	}
}

	static int
track_has_arc(ptr, tc, dir)
	char *ptr, *tc[];
	int  dir;
{
	int i;

	for(i = 1; i < 16; i++)
		if (i & dir && !strncmp(ptr, tc[i], strlen(tc[i])))
			return TRUE;
	return FALSE;
}

	static int
track_code(move, vstart, vend)
	int move, vstart, vend;
{
	int		len;
	long	i;
	char	*line, *ptr;
	char	**tc;
	int		code;

	line = nr2ptr(Curpos.lnum);
	tc   = tracktab->ch ;
	len  = tracktab->vw;
	code = move;


	/* left char. */
	if (!(code & TK_L) && (ptr = track_vcol(line, vstart, TV_PREV)))
	{
		if (p_tt)
			while(ptr - line >= len && strchr(" \t", *(ptr - 1)))
				ptr --;

		if (ptr - line >= len && track_has_arc(ptr - len, tc, TK_R))
			code |= TK_L;
	}

	/* right char. */
	if (!(code & TK_R) && (ptr = track_vcol(line, vend, TV_NEXT)))
	{
		if (p_tt)
			while(*ptr && strchr(" \t", *ptr))
				ptr ++;

		if (track_has_arc(ptr, tc, TK_L))
			code |= TK_R;
	}

	/* up char. */
	if (!(code & TK_U))
		for(i = Curpos.lnum - 1; i > 0; i --)
		{
			ptr = track_vcol(nr2ptr(i), vstart, TV_JUST);

			if (p_tt && (!ptr || (*ptr && strchr(" \t", *ptr))))
				continue;

			if (track_has_arc(ptr, tc, TK_D))
				code |= TK_U;

			break;
		}

	/* down char. */
	if (!(code & TK_D))
		for(i = Curpos.lnum + 1; i <= line_count; i ++)
		{
			ptr = track_vcol(nr2ptr(i), vstart, TV_JUST);

			if (p_tt && (!ptr || (*ptr && strchr(" \t", *ptr))))
				continue;

			if (track_has_arc(ptr, tc, TK_U))
				code |= TK_D;

			break;
		}

	return code;
}

	static void 
track_ins(dir)
	int dir;
{
	FPOS cpos;
	char *line, *ins, *nextp, *prevp;
	int  oldState;
	int  ndel, fpad, bpad;

	int  vstart, vend;
	int  ralign;

	int  rvstart, rvend;
	int  rcode, lcode;

	if (Curswant == MAXCOL)
		Curswant = Cursvcol;

	if (!u_save(Curpos.lnum - 1, Curpos.lnum + 1))
		return;

	tracktab_sw(p_trs);

	line = nr2ptr(Curpos.lnum);

	vstart = vend = Curswant;
	vend += tracktab->vw;

	rvstart = rvend = Curswant + 1;
	rvstart -= tracktab->vw;

	if (rvstart < 0)
	{
		rvstart = 0;
		rvend = tracktab->vw;
	}

	/* determine alignment */
		/* vertical matching (matched lines.) */
	rcode = track_code(0, rvstart, rvend) & (TK_U | TK_D);
	lcode = track_code(0,  vstart,  vend) & (TK_U | TK_D);

	if      ((lcode && !rcode) || (lcode & (TK_U | TK_D)) == (TK_U | TK_D))
		ralign = FALSE;
	else if ((!lcode && rcode) || (rcode & (TK_U | TK_D)) == (TK_U | TK_D))
		ralign = TRUE;

	else if (lcode & dir)
		ralign = FALSE;
	else if (rcode & dir)
		ralign = TRUE;

	else
	{	/* holizontal matching (padding chars.) */
		lcode = rcode = 0;
		if (track_vcol(line,  vstart, TV_FPAD))
			lcode |= TK_L;
		if (track_vcol(line,  vend, TV_BPAD))
			lcode |= TK_R;
		if (track_vcol(line, rvstart, TV_FPAD))
			rcode |= TK_L;
		if (track_vcol(line, rvend, TV_BPAD))
			rcode |= TK_R;

		if      (!rcode && lcode)
			ralign = TRUE;
		else if (rcode && !lcode)
			ralign = FALSE;
		else
		{
			rcode &= ~dir;
			lcode &= ~dir;
			if      (!rcode && lcode)
				ralign = TRUE;
			else if (rcode && !lcode)
				ralign = FALSE;
			else if (dir & TK_L)
				ralign = TRUE;
			else if (dir & TK_R)
				ralign = FALSE;
			else
				ralign = FALSE;
		}
	}
	/*	*/

	if (ralign)
	{
		vstart = rvstart;
		vend   = rvend;
	}

	oldState = State;
	if (State == NORMAL)
		State = INSERT;

	/* find suitable track character */
	ins = tracktab->ch[track_code(dir, vstart, vend)];

	nextp = track_vcol(line, vend,   TV_NEXT);
	prevp = track_vcol(line, vstart, TV_PREV);

	ndel = nextp - prevp;
	fpad = (long) track_vcol(line, vstart, TV_FPAD);
	bpad = (long) track_vcol(line, vend,   TV_BPAD);

	if (ralign && (Curpos.col = prevp - line) <= 0)
		Curpos.col = 0;

	if (- bpad > tracktab->vw)
	{
		Curpos.col = strlen(line);
		ndel = 0;
	}

	/* remove old characters */
	for(;ndel > 0; ndel--)
		delchar(FALSE);

	/* padd preceeding space */
	for(; fpad > 0; fpad--)
		inschar(' ', NUL);

	/* insert track */
	cpos = Curpos;
#ifdef JP
	for(; *ins; ins += IsKanji(*ins)? 2 : 1)
		inschar(*ins, *(ins + 1));
#else
	for(; *ins; ins ++)
		inschar(*ins, 0);
#endif

	/* padd tailing space */
	for(; bpad > 0; bpad--)
		inschar(' ', NUL);

	if (dir == TK_R)
	{
		line = Curpos2ptr();
		if (!*line)
			inschar('X', NUL);
	}

	/* recover saved states */
	Curpos = cpos;
	updateline();
	State = oldState;
}

	void
track_right()
{
	track_ins(TK_R);
}

	void
track_left()
{
	track_ins(TK_L);
}

	void
track_up()
{
	track_ins(TK_U);
}

	void
track_down()
{
	track_ins(TK_D);
}

	void
showtrack()
{
	if (!Track)
		return;

	gotocmdline(TRUE, NUL);
	outstrn("[TRACK]");
	outstrn(p_trs);
	setcursor();
}



syntax highlighted by Code2HTML, v. 0.9.1