/* housekeeping.c -- Generally editor fiddly stuff
   Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>

   This file is part of Jade.

   Jade 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 2, or (at your option)
   any later version.

   Jade 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 Jade; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "jade.h"
#include "jade_protos.h"

_PR void adjust_marks_add_x(TX *, long, long, long);
_PR void adjust_marks_sub_x(TX *, long, long, long);
_PR void adjust_marks_add_y(TX *, long, long);
_PR void adjust_marks_sub_y(TX *, long, long);
_PR void adjust_marks_split_y(TX *, long, long);
_PR void adjust_marks_join_y(TX *, long, long);
_PR void resync_xy(VW *);
_PR void set_start_col(VW *, long);
_PR void set_start_line(VW *, long);
_PR void reset_all_views(TX *);

/* The next few routines deal with updating the various references to
   coordinates throughout the views after chunks have been deleted and
   inserted.  */

void
adjust_marks_add_x(TX *tx, long addx, long xpos, long ypos)
{
    VW *thisvw;
    Mark *thismark;

#define UPD(x,y)				\
    do {					\
	if((y == ypos) && (x >= xpos))		\
	    x += addx;				\
    } while(0)

    for(thisvw = view_chain; thisvw; thisvw = thisvw->vw_Next)
    {
	if(thisvw->vw_Tx == tx)
	{
	    UPD(thisvw->vw_CursorPos.pos_Col, thisvw->vw_CursorPos.pos_Line);
            if(!(thisvw->vw_Flags & VWFF_RECTBLOCKS))
	    {
		UPD(thisvw->vw_BlockS.pos_Col, thisvw->vw_BlockS.pos_Line);
		UPD(thisvw->vw_BlockE.pos_Col, thisvw->vw_BlockE.pos_Line);
	    }
	}
    }
    for(thismark = tx->tx_MarkChain; thismark; thismark = thismark->mk_Next)
    {
	UPD(VPOS(thismark->mk_Pos).pos_Col, VPOS(thismark->mk_Pos).pos_Line);
    }
    UPD(tx->tx_SavedCPos.pos_Col, tx->tx_SavedCPos.pos_Line);
    UPD(tx->tx_SavedWPos.pos_Col, tx->tx_SavedWPos.pos_Line);
    UPD(tx->tx_SavedBlockPos[0].pos_Col, tx->tx_SavedBlockPos[0].pos_Line);
    UPD(tx->tx_SavedBlockPos[1].pos_Col, tx->tx_SavedBlockPos[1].pos_Line);
#if 1
    if((tx->tx_ModStart.pos_Line == ypos) && (tx->tx_ModStart.pos_Col > xpos))
	tx->tx_ModStart.pos_Col += addx;
#else
    UPD(tx->tx_ModStart.pos_Col, tx->tx_ModStart.pos_Line);
#endif
    UPD(tx->tx_ModEnd.pos_Col, tx->tx_ModEnd.pos_Line);

#undef UPD
}

void
adjust_marks_sub_x(TX *tx, long subx, long xpos, long ypos)
{
    VW *thisvw;
    Mark *thismark;

#define UPD(x,y)				\
    do {					\
	if((y == ypos) && (x >= xpos))		\
	{					\
	    if((x -= subx) < xpos)		\
		x = xpos;			\
	}					\
    } while(0)

    for(thisvw = view_chain; thisvw; thisvw = thisvw->vw_Next)
    {
	if(thisvw->vw_Tx == tx)
	{
	    UPD(thisvw->vw_CursorPos.pos_Col, thisvw->vw_CursorPos.pos_Line);
            if(!(thisvw->vw_Flags & VWFF_RECTBLOCKS))
	    {
		UPD(thisvw->vw_BlockS.pos_Col, thisvw->vw_BlockS.pos_Line);
		UPD(thisvw->vw_BlockE.pos_Col, thisvw->vw_BlockE.pos_Line);
	    }
	}
    }
    for(thismark = tx->tx_MarkChain; thismark; thismark = thismark->mk_Next)
    {
	UPD(VPOS(thismark->mk_Pos).pos_Col, VPOS(thismark->mk_Pos).pos_Line);
    }
    UPD(tx->tx_SavedCPos.pos_Col, tx->tx_SavedCPos.pos_Line);
    UPD(tx->tx_SavedWPos.pos_Col, tx->tx_SavedWPos.pos_Line);
    UPD(tx->tx_SavedBlockPos[0].pos_Col, tx->tx_SavedBlockPos[0].pos_Line);
    UPD(tx->tx_SavedBlockPos[1].pos_Col, tx->tx_SavedBlockPos[1].pos_Line);

    UPD(tx->tx_ModStart.pos_Col, tx->tx_ModStart.pos_Line);
#if 1
    if((tx->tx_ModEnd.pos_Line == ypos) && (tx->tx_ModEnd.pos_Col > xpos))
    {
	if((tx->tx_ModEnd.pos_Col -= subx) < xpos)
	    tx->tx_ModEnd.pos_Col = xpos;
    }
#else
    UPD(tx->tx_ModEnd.pos_Col, tx->tx_ModEnd.pos_Line);
#endif

#undef UPD
}

/*
 * Whole lines only please
 */
void
adjust_marks_add_y(TX *tx, long addy, long ypos)
{
    VW *thisvw;
    Mark *thismark;

#define UPD(y)					\
    do {					\
	if(y >= ypos)				\
	    y += addy;				\
    } while(0)

    for(thisvw = view_chain; thisvw; thisvw = thisvw->vw_Next)
    {
	if(thisvw->vw_Tx == tx)
	{
	    UPD(thisvw->vw_CursorPos.pos_Line);
	    UPD(thisvw->vw_BlockS.pos_Line);
	    UPD(thisvw->vw_BlockE.pos_Line);
	    if(thisvw != curr_vw)
		UPD(thisvw->vw_StartLine);
	}
    }
    for(thismark = tx->tx_MarkChain; thismark; thismark = thismark->mk_Next)
    {
	UPD(VPOS(thismark->mk_Pos).pos_Line);
    }
    UPD(tx->tx_SavedCPos.pos_Line);
    UPD(tx->tx_SavedWPos.pos_Line);
    UPD(tx->tx_SavedBlockPos[0].pos_Line);
    UPD(tx->tx_SavedBlockPos[1].pos_Line);

#if 0
#if 1
    if(tx->tx_ModStart.pos_Line > ypos)
	tx->tx_ModStart.pos_Line += addy;
#else
    UPD(tx->tx_ModStart.pos_Line);
#endif
    UPD(tx->tx_ModEnd.pos_Line);
#endif

#undef UPD
}

/*
 * Whole lines only please
 */
void
adjust_marks_sub_y(TX *tx, long suby, long ypos)
{
    VW *thisvw;
    Mark *thismark;

#define UPD(y)					\
    do {					\
	if(y > ypos)				\
	{					\
	    if((y -= suby) < ypos)		\
		y = ypos;			\
	}					\
    } while(0)

#define UPD2(x,y)				\
    if(y >= ypos)				\
    {						\
	if((y -= suby) < ypos)			\
	{					\
	    y = ypos;				\
	    x = 0;				\
	}					\
    } while(0)

    for(thisvw = view_chain; thisvw; thisvw = thisvw->vw_Next)
    {
	if(thisvw->vw_Tx == tx)
	{
	    UPD2(thisvw->vw_CursorPos.pos_Col, thisvw->vw_CursorPos.pos_Line);
            if(thisvw->vw_Flags & VWFF_RECTBLOCKS)
	    {
		UPD(thisvw->vw_BlockS.pos_Line);
		UPD(thisvw->vw_BlockE.pos_Line);
	    }
            else
	    {
                UPD2(thisvw->vw_BlockS.pos_Col, thisvw->vw_BlockS.pos_Line);
                UPD2(thisvw->vw_BlockE.pos_Col, thisvw->vw_BlockE.pos_Line);
	    }
	    if(thisvw != curr_vw)
		UPD(thisvw->vw_StartLine);
	}
    }
    for(thismark = tx->tx_MarkChain; thismark; thismark = thismark->mk_Next)
    {
	UPD2(VPOS(thismark->mk_Pos).pos_Col, VPOS(thismark->mk_Pos).pos_Line);
    }
    UPD2(tx->tx_SavedCPos.pos_Col, tx->tx_SavedCPos.pos_Line);
    UPD2(tx->tx_SavedWPos.pos_Col, tx->tx_SavedWPos.pos_Line);
    UPD2(tx->tx_SavedBlockPos[0].pos_Col, tx->tx_SavedBlockPos[0].pos_Line);
    UPD2(tx->tx_SavedBlockPos[1].pos_Col, tx->tx_SavedBlockPos[1].pos_Line);

    UPD2(tx->tx_ModStart.pos_Col, tx->tx_ModStart.pos_Line);
#if 1
    if(tx->tx_ModEnd.pos_Line > ypos)
    {
	if((tx->tx_ModEnd.pos_Line -= suby) < ypos)
	{
	    tx->tx_ModEnd.pos_Line = ypos;
	    tx->tx_ModEnd.pos_Col = 0;
	}
    }
#else
    UPD2(tx->tx_ModEnd.pos_Col, tx->tx_ModEnd.pos_Line);
#endif

#undef UPD
#undef UPD2
}

/*
 * Use when splitting a line into 2, cursor should be at position of split
 */
void
adjust_marks_split_y(TX *tx, long xpos, long ypos)
{
    VW *thisvw;
    Mark *thismark;

#define UPD(y)					\
    do {					\
	if(y > ypos)				\
	    y++;				\
    } while(0)

#define UPD2(x,y)				\
    do {					\
	if((y == ypos) && (x >= xpos))		\
	{					\
	    x -= xpos;				\
	    y++;				\
	}					\
	else if(y > ypos)			\
	    y++;				\
    } while(0)

    for(thisvw = view_chain; thisvw; thisvw = thisvw->vw_Next)
    {
	if(thisvw->vw_Tx == tx)
	{
	    UPD2(thisvw->vw_CursorPos.pos_Col, thisvw->vw_CursorPos.pos_Line);
            if(thisvw->vw_Flags & VWFF_RECTBLOCKS)
	    {
		UPD(thisvw->vw_BlockS.pos_Line);
		UPD(thisvw->vw_BlockE.pos_Line);
	    }
            else
	    {
                UPD2(thisvw->vw_BlockS.pos_Col, thisvw->vw_BlockS.pos_Line);
                UPD2(thisvw->vw_BlockE.pos_Col, thisvw->vw_BlockE.pos_Line);
	    }
	    if(thisvw != curr_vw)
		UPD(thisvw->vw_StartLine);
	}
    }
    for(thismark = tx->tx_MarkChain; thismark; thismark = thismark->mk_Next)
    {
	UPD2(VPOS(thismark->mk_Pos).pos_Col, VPOS(thismark->mk_Pos).pos_Line);
    }
    UPD2(tx->tx_SavedCPos.pos_Col, tx->tx_SavedCPos.pos_Line);
    UPD2(tx->tx_SavedWPos.pos_Col, tx->tx_SavedWPos.pos_Line);
    UPD2(tx->tx_SavedBlockPos[0].pos_Col, tx->tx_SavedBlockPos[0].pos_Line);
    UPD2(tx->tx_SavedBlockPos[1].pos_Col, tx->tx_SavedBlockPos[1].pos_Line);

#if 1
    if((tx->tx_ModStart.pos_Line == ypos) && (tx->tx_ModStart.pos_Col > xpos))
    {
	tx->tx_ModStart.pos_Col -= xpos;
	tx->tx_ModStart.pos_Line++;
    }
    else if(tx->tx_ModStart.pos_Line > ypos)
	tx->tx_ModStart.pos_Line++;
#else
    UPD2(tx->tx_ModStart.pos_Col, tx->tx_ModStart.pos_Line);
#endif
    UPD2(tx->tx_ModEnd.pos_Col, tx->tx_ModEnd.pos_Line);

#undef UPD
#undef UPD2
}

/*
 * Use when compacting 2 adjacent lines into one
 */
void
adjust_marks_join_y(TX *tx, long xpos, long ypos)
{
    VW *thisvw;
    Mark *thismark;

#define UPD(y)					\
    do {					\
	if(y > ypos)				\
	    y--;				\
    } while(0)

#define UPD2(x,y)				\
    do {					\
	if(y > ypos)				\
	{					\
	    if(y == ypos + 1)			\
	    {					\
		x += xpos;			\
		y--;				\
	    }					\
	}					\
    } while(0)

    for(thisvw = view_chain; thisvw; thisvw = thisvw->vw_Next)
    {
	if(thisvw->vw_Tx == tx)
	{
	    UPD2(thisvw->vw_CursorPos.pos_Col, thisvw->vw_CursorPos.pos_Line);
            if(thisvw->vw_Flags & VWFF_RECTBLOCKS)
	    {
		UPD(thisvw->vw_BlockS.pos_Line);
		UPD(thisvw->vw_BlockE.pos_Line);
	    }
            else
	    {
                UPD2(thisvw->vw_BlockS.pos_Col, thisvw->vw_BlockS.pos_Line);
                UPD2(thisvw->vw_BlockE.pos_Col, thisvw->vw_BlockE.pos_Line);
	    }
	    if(thisvw != curr_vw)
		UPD(thisvw->vw_StartLine);
	}
    }
    for(thismark = tx->tx_MarkChain; thismark; thismark = thismark->mk_Next)
    {
	UPD2(VPOS(thismark->mk_Pos).pos_Col, VPOS(thismark->mk_Pos).pos_Line);
    }
    UPD2(tx->tx_SavedCPos.pos_Col, tx->tx_SavedCPos.pos_Line);
    UPD2(tx->tx_SavedWPos.pos_Col, tx->tx_SavedWPos.pos_Line);
    UPD2(tx->tx_SavedBlockPos[0].pos_Col, tx->tx_SavedBlockPos[0].pos_Line);
    UPD2(tx->tx_SavedBlockPos[1].pos_Col, tx->tx_SavedBlockPos[1].pos_Line);

    UPD2(tx->tx_ModStart.pos_Col, tx->tx_ModStart.pos_Line);
    UPD2(tx->tx_ModEnd.pos_Col, tx->tx_ModEnd.pos_Line);

#undef UPD
#undef UPD2
}

/* These routines are called to recalculate the cursor's position on the
   screen...  */
static void
resync_x(VW *vw)
{
    long offset;
    calc_cursor_offset(vw);
    offset = vw->vw_LastCursorOffset;
    while((offset - vw->vw_StartCol) >= vw->vw_MaxX)
    {
	vw->vw_StartCol += vw->vw_XStep;
	vw->vw_Flags |= VWFF_FORCE_REFRESH;
    }
    while(offset < vw->vw_StartCol)
    {
	vw->vw_StartCol -= vw->vw_XStep;
	if(vw->vw_StartCol < 0)
	    vw->vw_StartCol = 0;
	vw->vw_Flags |= VWFF_FORCE_REFRESH;
    }
}

static void
resync_y(VW *vw)
{
    TX *tx = vw->vw_Tx;
    long y = vw->vw_CursorPos.pos_Line - vw->vw_StartLine;
    if(y < 0)
    {
	if(-y > vw->vw_YStep)
	    vw->vw_StartLine = vw->vw_CursorPos.pos_Line - (vw->vw_MaxY / 2);
	else
	    vw->vw_StartLine -= vw->vw_YStep;
	if(vw->vw_StartLine < 0)
	    vw->vw_StartLine = 0;
	else if(vw->vw_StartLine >= tx->tx_NumLines)
	    vw->vw_StartLine = tx->tx_NumLines - 1;
    }
    else if(y >= vw->vw_MaxY)
    {
	if((vw->vw_MaxY + vw->vw_YStep) <= y)
	    vw->vw_StartLine = vw->vw_CursorPos.pos_Line - (vw->vw_MaxY / 2);
	else
	    vw->vw_StartLine += vw->vw_YStep;
	if(vw->vw_StartLine < 0)
	    vw->vw_StartLine = 0;
	else if(vw->vw_StartLine >= tx->tx_NumLines)
	    vw->vw_StartLine = tx->tx_NumLines - 1;
	/* Check for a `gap' at the bottom of the display */
	if((tx->tx_NumLines >= vw->vw_MaxY)
	   && ((tx->tx_NumLines - vw->vw_StartLine) < vw->vw_MaxY))
	{
	    vw->vw_StartLine = tx->tx_NumLines - vw->vw_MaxY;
	}
    }
}

void
resync_xy(VW *vw)
{
    if(vw->vw_Tx != vw->vw_LastRefTx)
	/* kludge */
	vw->vw_LastDisplayOrigin = vw->vw_DisplayOrigin;
    resync_x(vw);
    resync_y(vw);
}

void
set_start_col(VW *vw, long col)
{
    if(vw->vw_StartCol != col)
    {
	vw->vw_Flags |= VWFF_FORCE_REFRESH;
	vw->vw_StartCol = col;
    }
}

void
set_start_line(VW *vw, long line)
{
    long cline = vw->vw_StartLine;
    if(line != cline)
    {
	long yord = vw->vw_CursorPos.pos_Line - cline;
	vw->vw_StartLine = line;
#if 0
	/* This is done in vert_scroll() now; doing it here as
	   well means it's done twice!  */
	if(!(vw->vw_Flags & VWFF_FORCE_REFRESH))
	{
	    long diff = line - cline;
	    if((diff > 0) && (diff <= vw->vw_MaxScroll))
	    {
		long ypos = vw->vw_MaxY - 1;
		sys_scroll_vw(vw, diff);
		redraw_lines(vw, cline + ypos, line + ypos + 1);
	    }
	    else if((diff < 0) && (diff >= -vw->vw_MaxScroll))
	    {
		sys_scroll_vw(vw, diff);
		redraw_lines(vw, line, cline);
	    }
	    else
		vw->vw_Flags |= VWFF_FORCE_REFRESH;
	}
	else
	    vw->vw_Flags |= VWFF_FORCE_REFRESH;
#endif
	vw->vw_CursorPos.pos_Line = line + yord;
	if(vw->vw_CursorPos.pos_Line >= vw->vw_Tx->tx_NumLines)
	    vw->vw_CursorPos.pos_Line = vw->vw_Tx->tx_NumLines - 1;
    }
}

/*
 * This makes all views of this file have their cursor at the top of the
 * file, it also refreshes each view.
 */
void
reset_all_views(TX *tx)
{
    VW *thisvw;
    for(thisvw = view_chain; thisvw; thisvw = thisvw->vw_Next)
    {
	if(thisvw->vw_Tx == tx)
	{
	    thisvw->vw_CursorPos.pos_Col = 0;
	    thisvw->vw_CursorPos.pos_Line = 0;
	    thisvw->vw_StartCol = 0;
	    thisvw->vw_StartLine = 0;
	    thisvw->vw_BlockStatus = -1;
	    thisvw->vw_Flags |= VWFF_FORCE_REFRESH;
	    thisvw->vw_Flags &= ~VWFF_MESSAGE;
	    std_message(thisvw);
	}
	tx->tx_SavedCPos.pos_Col = 0;
	tx->tx_SavedCPos.pos_Line = 0;
	tx->tx_SavedWPos.pos_Col = 0;
	tx->tx_SavedWPos.pos_Line = 0;
	tx->tx_SavedBlockStatus = -1;
    }
}
