static char rcsid[] = "$Id: region.c,v 1.2 1992/12/29 00:15:46 mike Exp $";

/* $Log: region.c,v $
 * Revision 1.2  1992/12/29  00:15:46  mike
 * - Drop a note to the user if `copyregion' succeeded.
 *
 * Revision 1.1  1992/09/05  01:13:32  mike
 * Initial revision
 *
 */

/*
 * 
 *	REGION.C MODULE
 *
 * The routines in this file deal with the region, that magic space
 *   between two marks (usually dot and mark 0).
 * Some functions are commands, some functions are just for internal use.
 */

/* Copyright 1990, 1991, 1992 Craig Durland
 *   Distributed under the terms of the GNU General Public License.
 *   Distributed "as is", without warranties of any kind, but comments,
 *     suggestions and bug reports are welcome.
 */

#include <stdio.h>
#include <char.h>
#include "me2.h"

static int tankregion();

getregion(rp) Region *rp;
   { return get_region(id_to_mark(THE_DOT), id_to_mark(THE_MARK), rp); }

/*
 * Cut the region and save it in the cut buffer.
 * Bound to "C-w".
 */
cutregion(f,n) { return tankregion(TRUE); }

/*
 * Delete the region and don't save it.
 * Same as cutregion except the region is not saved.
 * It would save code to make this part of cutregion() (^U^W => don't save)
 *  but that would make typos a real pain.
 */
delregion(f,n) { return tankregion(FALSE); }

/*
 * Delete the region. Ask getregion() to figure out the bounds of the region.
 * Move dot to the start, and delete the characters.
 * If save == TRUE, do a cut:  save region then delete it
 *    If last command was a cut command add region to cut buffer
 *    else clear cut_buffer then add region.
 */
static int tankregion(save)
{
  Region region;

  if (getregion(&region) == 0) return FALSE;
  if (save)
  {
    if ((lastflag & CFCUT) == 0) clear_bag(cut_buffer);
    thisflag |= CFCUT;		/* this is a cut command. */
  }
  *the_dot = region.mark;
  return ldelete(region.r_size,save);
}

/*
 * Copy all of the characters in the region to the cut buffer.  Don't move
 * dot at all.  This is a bit like a cut region followed by a yank.
 * Bound to "M-w".
 */
copyregion(f,n)
{
  extern int    MMask_pgm;
         Region region;
         int    status;

  if (getregion(&region) == 0) return FALSE;
  if ((lastflag & CFCUT) == 0) clear_bag(cut_buffer);
  thisflag |= CFCUT;
  status = copy_region_to_bag(&region.mark, region.r_size, cut_buffer);
  if (status == TRUE && !MMask_pgm)
    mlwrite("[Region copied]");
  return status;
}

/* ******************************************************************** */
/* ******************************************************************** */
/* ******************************************************************** */

    /* Append text from the current buffer to a bag.
     * Input:
     *   mark:  pointer to a mark in the current buffer.  It marks the start
     *     of the text to be copied.
     *   size:  number of characters to copy.
     *   bag:  where to put the text.
     * Returns:
     *   TRUE:  Region copied to bag or hit EoB before copied all text.
     *   FALSE: Bag overflowed, copy not completed.
     */
copy_region_to_bag(mark,size, bag) Mark *mark; int32 size; Bag *bag;
{
  register Line *lp;
  register int chunk, offset;
  int s;

  lp = mark->line; offset = mark->offset;
  chunk = llength(lp) -offset;
  s = TRUE;
  while (size > 0 && s)
  {
    if (lp == BUFFER_LAST_LINE(curbp)) break;	   /* Hit end of buffer */
    if (chunk == 0)		/* end of the line == newline */
    {
      s = bag_append(bag,"\n",1);
      size--; offset = 0;
      lp = lforw(lp); chunk = llength(lp);
    }
    else
    {
      if (chunk > size) chunk = size;
      s = bag_append(bag,&lp->l_text[offset],chunk);
      size -= chunk; chunk = 0;
    }
  }
  return s;
}

/*
 * Get the stats about the region between mark1 and mark2 and put the
 *   results in rp.
 * Because the marks are usually very close together, we scan outward from
 *   mark1 looking for mark2.  This should save time.
 * Input:
 *   mark1, mark2:  Pointers to live marks in the current buffer.  They
 *     don't have to be valid (see mark.c for terms).
 *   rp: Pointer to a region to be filled in with the stats about the region
 *     between mark1 and mark2.
 * Returns:
 *	0 (No mark), 1 (mark1 and mark2 on same line), 2 (mark1 above
 *	mark2), 3 (mark1 below mark2)
 * WARNING:
 *   If mark1 or mark2 live in a different buffer, this routine will loop
 *     forever.
 */
get_region(mark1,mark2,rp) Mark *mark1, *mark2; Region *rp;
{
  register Line *flp, *blp,
    *first_line = BUFFER_FIRST_LINE(curbp),
    *last_line  = BUFFER_LAST_LINE(curbp);
  register int32 fsize, bsize;
  int hf, hb;

  if (!mark1->line || !mark2->line) { mlwrite("No mark."); return 0; }

  if (mark1->line == mark2->line)
  {
    rp->mark.line = mark1->line;
    rp->mark.offset = imin(mark1->offset, mark2->offset);
    rp->r_size = iabs(mark1->offset -mark2->offset);
    rp->height = 1;
    return 1;
  }
  blp = flp = mark1->line; hf = hb = 1;
  bsize = mark1->offset; fsize = llength(flp) -bsize +1;
  while (TRUE)
  {
    if (flp != last_line)
    {
      flp = lforw(flp); hf++;
      if (flp == mark2->line)
      {
	rp->mark = *mark1; rp->r_size = fsize +mark2->offset; rp->height = hf;
        return 2;
      }
      fsize += llength(flp) +1;
    }
    if (blp != first_line)
    {
      blp = lback(blp); bsize += llength(blp) +1; hb++;
      if (blp == mark2->line)
      {
	rp->mark = *mark2; rp->r_size = bsize -mark2->offset; rp->height = hb;
        return 3;
      }
    }
  }
  /* never gets here */
}

   /* Get rectangle info about the region between mark1 and mark2.  These 2
    *   marks locate the upper left and lower right of the rectangle.
    * WARNING!  mark1 and mark2 MUST be live marks in the current buffer.
    * Returns: same as get_region().
    */
get_rectangle(mark1,mark2, rp) Mark *mark1, *mark2; register Region *rp;
{
  register int x, y, s;

  if ((s = get_region(mark1,mark2, rp)) == 0) return 0;
  x = getcol(mark1->line,mark1->offset); y = getcol(mark2->line,mark2->offset);
  rp->ulcol = imin(x,y);
  rp->width = iabs(x-y);
  return s;
}



#if 0
    /* Convert a region to upper or lower case.  Use the region code to set
     *   the limits.  Scan the buffer doing the changes.  Call lchange() to
     *   ensure that redisplay is done in all buffers.
     */
caseregion(lower)
  int lower;	/* TRUE if convert region to lowercase, else uppercase */
{
  register Line *linep;
  register int loffs;
  register unsigned char c;
  Region region;

  if (getregion(&region) == 0) return FALSE;
  lchange(WFHARD);
  linep = region.mark.line; loffs = region.mark.offset;
  while (region.r_size--)
  {
    if (loffs == llength(linep)) { linep = lforw(linep); loffs = 0; }
    else
    {
      c = lgetc(linep,loffs);
      c = lower ? tolower(c) : toupper(c);
      lputc(linep,loffs, c);
      ++loffs;
    }
  }
  return TRUE;
}

/*
 * Lower case region.  Zap all of the upper case characters in the region to
 * lower case.
 * Bound to  "C-x C-l".
 */
lowerregion(f,n) { return caseregion(TRUE); }

/*
 * Upper case region.  Zap all of the lower case characters in the region to
 * upper case.
 * Bound to "C-x C-u".
 */
upperregion(f,n) { return caseregion(FALSE); }
#endif
