/************************************************************************
 * This program is Copyright (C) 1986 by Jonathan Payne.  JOVE is       *
 * provided to you without charge, and with no warranty.  You may give  *
 * away copies of JOVE, including sources, provided that this notice is *
 * included in all the files.                                           *
 ************************************************************************/

#include "jove.h"

RCS("$Id: marks.c,v 14.31 1993/02/18 10:07:33 tom Exp tom $")

DEF_INT( "marks-should-float", MarksShouldFloat, V_BOOL ) = YES;

Mark *
MakeMark(line, column, type)
Line	*line;
{
	register Buffer	*cb = curbuf;
	register Mark	*newmark = (Mark *) emalloc(sizeof *newmark);

	init_mark(newmark, cb, line, column, type);
	newmark->m_alloced++;	/* say that it can be free()ed */

	return newmark;
}

int
del_mark(m, b)
register Mark	*m;
Buffer		*b;
{
	register Mark	**hp;

	for (hp = &b->b_marks;  (*hp) != m;  hp = &(*hp)->m_next)
		if ((*hp) == NULL)
			return NO;

	(*hp) = m->m_next;

	/* only release it if it was created by MakeMark. */
	if (m->m_alloced)
		free((void_*) m);

	return YES;
}

void
DelMark(m)
Mark	*m;
{
	if (!del_mark(m, curbuf))
		complain("?mark?");
}

DEF_CMD( "pop-mark", PopMark, NO )
{
	register Buffer	*cb = curbuf;
	register unsigned pmark = cb->b_themark;	/* unsigned for % */
	register Mark	*cm;

	if ((cm = cb->b_markring[pmark]) == NULL)	/* curmark */
		return;

	PtToMark();

	cb->b_themark = (pmark - 1) % NMARKS;

	if (cb->b_markring[(pmark + 1) % NMARKS] == NULL) {

		/* move the mark just popped to the end of the list,
		   and clear the slot, so that next DoSetMark creates
		   a new mark. */

		cb->b_markring[pmark] = NULL;

		do {
			pmark = (pmark - 1) % NMARKS;
		} while (cb->b_markring[pmark] != NULL);

		cb->b_markring[pmark] = cm;
	}
}

void
set_mark()
{
	register Buffer	*cb = curbuf;

	DoSetMark(cb->b_dot, cb->b_char);
}

DEF_CMD( "set-mark", SetMark, NO )
{
	if (exp_p)
		PopMark();
	else
		set_mark();
}

void
DoSetMark(l, c)
Line	*l;
{
	register Buffer	*cb = curbuf;
	register int	pmark = (unsigned)(cb->b_themark + 1) % NMARKS;
	register Mark	**mp;

	cb->b_themark = pmark;
	if (*(mp = &cb->b_markring[pmark]))
		DelMark(*mp);
	(*mp) = MakeMark(l, c, MarksShouldFloat);

	s_mess("[Point pushed]");
}

/* Move point to Mark */

void
ToMark(mark)
Mark	*mark;
{
	register Mark	*m;

	if ((m = mark) == NULL)
		return;
	DotTo(m->m_line, m->m_char);
#if NO	/* [TRH] this is now assured by DotTo */
    {	register Buffer	*cb = curbuf;
	register int	len;

	if (cb->b_char > (len = length(cb->b_dot)))
		cb->b_char = len;
    }
#endif
}

Mark *
CurMark()
{
	register Buffer	*cb = curbuf;
	register Mark	*m;

	if ((m = cb->b_markring[cb->b_themark]) == NULL)
		complain("No mark.");
#if 0	/* NO! breaks too many things! */
	if (m->m_line == cb->b_dot && m->m_char == cb->b_char)
		complain("Empty region.");
#endif
	return m;
}

void
to_mark()
{
	ToMark(CurMark());
}

DEF_CMD( "exchange-point-and-mark", PtToMark, NO )
{
	register Line	*mline;
	register int	mchar;
    {
	register Buffer	*cb = curbuf;

	mline = cb->b_dot;
	mchar = cb->b_char;
    }
    {
	register Mark	*m = CurMark();

	ToMark(m);
	MarkSet(m, mline, mchar);
    }
}

/* show mark */

DEF_CMD( "mark-flash", ShowMark, NO )
{
	register Mark	*m = CurMark();

	Blink(m->m_line, m->m_char);
}

/* Fix marks for after a deletion.  For now, even marks that don't
   float will actually float, because we can't allow marks to point
   to non-existant lines. [TRH] It is a lot simpler than you thought...
   no need for two passes since marks are moved backward while we move
   forward over the lines so we can never encounter the same mark twice */

void
DFixMarks(line1, char1, line2, char2)
Line		*line1;
register int	char1;
Line		*line2;
{
	register Mark	*m;
	register Line	*lp = line1;

	do {
		if ((m = curbuf->b_marks) == NULL)
			return;
		do {
#if NO
			if (!m->m_floater)
				continue;
#endif
			if (m->m_line != lp)
				continue;
			if (/*m->m_line*/ lp == line1 && m->m_char <= char1)
				continue;

			/* set mark to start of region. (we know it's inside
			   since lp is) Be careful about marks just outside
			   on last line. */
			if (/*m->m_line*/ lp == line2 && m->m_char > char2)
				m->m_char -= (char2 - char1);
			else
				m->m_char = char1;

			m->m_line = line1;
		} while (m = m->m_next);
	} while (lp != line2 && (lp = lp->l_next, TRUE));
}

/* Fix marks after an insertion.  Marks that don't float are ignored
   on insertion, which means PtToMark has to be careful.  Note that
   programmed FLOATERing marks that equal DOT move along with it,
   whereas user-defined marks stay put ... */

void
IFixMarks(line1, char1, line2, char2)
register Line	*line1;
register int	char1;
Line		*line2;
{
	register Mark	*m;

	if (m = curbuf->b_marks) do {
		if (!m->m_floater)
			continue;
		if (m->m_line != line1)
			continue;
		if (m->m_char < char1)
			continue;
		if (m->m_char == char1 && !(m->m_floater & FLOATER))
			continue;

		m->m_char += (char2 - char1);
		m->m_line = line2;
	} while (m = m->m_next);
}

/*======================================================================
 * $Log: marks.c,v $
 * Revision 14.31  1993/02/18  10:07:33  tom
 * remove (void) casts; lotsa random optimizations.
 *
 * Revision 14.30  1993/02/05  00:07:30  tom
 * cleanup whitespace; some random optimizations.
 *
 * Revision 14.28  1992/10/24  01:24:21  tom
 * convert to "port{ansi,defs}.h" conventions.
 *
 * Revision 14.26  1992/08/26  23:56:56  tom
 * remove AllMarkSet(); add RCS directives.
 *
 */
