/* 
 * mxSelect.c --
 *
 *	This file contains routines used by Mx and Tx for manipulating
 *	the selection.
 *
 * Copyright (C) 1986, 1987, 1988 Regents of the University of California
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

#ifndef lint
static char rcsid[] = "$Header: /sprite/src/lib/mx/RCS/mxSelect.c,v 1.9 89/09/25 17:43:04 ouster Exp $ SPRITE (Berkeley)";
#endif not lint


#include <X11/Xlib.h>
#include <stdio.h>
#include <strings.h>

#include "mxInt.h"

/*
 * Information about the selection.  Since there's only one selection
 * at a time, this can be global rather than window-specific.
 */

MxFileInfo *MxSelectedFile = NULL;	/* File containing selection.  NULL
					 * means no selection here. */
MxHighlight *MxSelection;		/* Describes selection. */

/*
 * Forward references to procedures defined later in this file:
 */

static void		MxSelChange();
static int		MxSelFetch();

/*
 *----------------------------------------------------------------------
 *
 * MxSelectionSet --
 *
 *	Make the selection be a given range in a given window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The selection is changed to correspond to the range between
 *	first and last (inclusive) in mxwPtr.
 *
 *----------------------------------------------------------------------
 */

void
MxSelectionSet(mxwPtr, first, last)
    MxWindow *mxwPtr;		/* Window in which to make selection. */
    Mx_Position first, last;	/* Range for new selection. */
{
    if (mxwPtr->fileInfoPtr != MxSelectedFile) {
	Sx_SelectionSet(mxwPtr->display, MxSelFetch, MxSelChange,
		(ClientData) NULL);
	MxSelectedFile = mxwPtr->fileInfoPtr;
	MxSelection = MxHighlightCreate(mxwPtr, first, last, MX_REVERSE);
    } else {
	MxHighlightSetRange(MxSelection, first, last);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * MxSelectionClear --
 *
 *	If the selection belongs to mxwPtr, then clear it.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The selection may get cleared.
 *
 *----------------------------------------------------------------------
 */

void
MxSelectionClear(mxwPtr)
    MxWindow *mxwPtr;		/* Window in which to clear selection.  If
				 * NULL, then clear selection regardless
				 * of which window it's in (assuming that
				 * we've got it, of course). */
{
    if (MxSelection == NULL) {
	return;
    }
    if ((mxwPtr != NULL) && (mxwPtr->fileInfoPtr != MxSelectedFile)) {
	return;
    }
    MxHighlightDelete(MxSelection);
    MxSelection = NULL;
    MxSelectedFile = NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * MxSelFetch --
 *
 *	Called by the Sx selection package when someone wants to know
 *	what's selected.
 *
 * Results:
 *	See the documentation for Sx_SelectionGet.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

    /* ARGSUSED */
static int
MxSelFetch(clientData, desiredFormat, firstByte, numBytes, selectionPtr,
	formatPtr)
    ClientData clientData;		/* Not used. */
    char *desiredFormat;		/* Three formats are allowed:
					 * "line" -- wants current file and
					 * 1-origin line number of selection,
					 * separated by a space.
					 * "string" or "text" -- wants the
					 * selection as a string of characters.
					 */
    int firstByte;			/* Index of first desired byte. */
    int numBytes;			/* Max no. of bytes to return. */
    char *selectionPtr;			/* Store bytes of selection here. */
    char *formatPtr;			/* Store type of selection here. */
{
    Mx_Position first, last;
    Mx_File file;
    char *line;
    int count, bytesThisLine, lineLength;

    if (MxSelectedFile == NULL) {
	return -1;
    }
    file = MxSelectedFile->file;
    MxHighlightGetRange(MxSelection, &first, &last);
    if (MX_POS_LESS(last, first)) {
	return -1;
    }
    if (strcmp(desiredFormat, "line") == 0) {
	/*
	 * Requestor wants the current file and line number as
	 * <file> <line>.
	 * The line number should be 1-origin.
	 */
	file = MxSelectedFile->file;
	strcpy(formatPtr, "line");
	if (MxSelectedFile->name == (char *)NULL) {
	    /*
	     * Selected file has no name -- this is an error.
	     */
	    return(-1);
	}
	(void)sprintf(selectionPtr, "%s %d", MxSelectedFile->name,
		      first.lineIndex + 1);
	return(strlen(selectionPtr));
    } else if ((strcmp(desiredFormat, "text") == 0) ||
	       (strcmp(desiredFormat, "string") == 0))
    {
	strcpy(formatPtr, "text");
	for (count = 0, first = Mx_Offset(file, first, firstByte);
	     (count < numBytes) && MX_POS_LEQ(first, last); ) {
		 line = Mx_GetLine(file, first.lineIndex, &lineLength);
		 if (first.lineIndex == last.lineIndex) {
		     bytesThisLine = last.charIndex + 1 - first.charIndex;
		 } else {
		     bytesThisLine = lineLength - first.charIndex;
		 }
		 if (count + bytesThisLine > numBytes) {
		     bytesThisLine = numBytes - count;
		 }
		 strncpy(selectionPtr, &line[first.charIndex], bytesThisLine);
		 selectionPtr += bytesThisLine;
		 count += bytesThisLine;
		 first.lineIndex++;
		 first.charIndex = 0;
	}
	return count;
    } else {
	return(-1);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * MxSelChange --
 *
 *	This procedure is called by the Sx selection module whenever
 *	the selection changes out from underneath us.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The selection highlight is deleted and we remember that we
 *	no longer have the selection.
 *
 *----------------------------------------------------------------------
 */

static void
MxSelChange()
{
    MxSelectedFile = NULL;
    if (MxSelection != NULL) {
	MxHighlightDelete(MxSelection);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * MxGetSelRange --
 *
 *	If the selection is in this window's file, return its range.
 *	Otherwise return an error.
 *
 * Results:
 *	TCL_OK is returned if the selection is in mxwPtr's file.  In
 *	addition, *firstPtr and *lastPtr get filled in with the selection's
 *	range.  Otherwise, TCL_ERROR is returned and a pointer to an
 *	error message is left in mxwPtr->interp->result.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
MxGetSelRange(mxwPtr, firstPtr, lastPtr)
    register MxWindow *mxwPtr;		/* Window of interest. */
    Mx_Position *firstPtr;		/* To be filled in with location of
					 * first selected character. */
    Mx_Position *lastPtr;		/* To be filled in with location of
					 * last selected character. */
{
    if ((MxSelectedFile != mxwPtr->fileInfoPtr)) {
	goto noSelection;
    }
    MxHighlightGetRange(MxSelection, firstPtr, lastPtr);
    if (MX_POS_LESS(*lastPtr, *firstPtr)) {
	goto noSelection;
    }
    return TCL_OK;

    noSelection:
    mxwPtr->interp->result = "nothing is selected in this file";
    return TCL_ERROR;
}
