#if !NO_SCCS_STRINGS
static char sccs_clip_c[] = "@(#)clip.c	128.1 1/20/93 19:38:37";
#endif /* NO_SCCS_STRINGS */
/***********************************************************************\

   (c) Copyright 1991 by Lotus Development Corporation
   All Rights Reserved

   Module:  clip.c

   Description:  Add-in application that supports copying and pasting
   cells to a clipboard.   This module allocates memory on demand to
   match the size of the current clipboard copy region.

\***********************************************************************/

#define  LPI_MAIN_MODULE

#define  USE_REGISTER
#define  USE_CELL
#define  USE_RANGE
#define  USE_INPUT
#define  USE_UTILITY

#include "lpixport.h"
#include "clip.h"

/* structure to hold a Cell's characterization */

struct CellCharacteristics {
   lmbcs   Contents[LPI_CL_CELL_BUFLEN];
   lbool   Protection;
   lbool   Highlight;
   lbool   ColorNegative;
   lbool   Parentheses;
   lushort LabelPrefix;
   lushort FormatType;
   lushort FormatPlaces;
   lbool   Status;
};
typedef struct CellCharacteristics CellDescription; 
CellDescription theClipboard;

lmhdl(lmhdl(CellDescription)) theCellsHandle;
int                           theStatus = CLIPBOARD_EMPTY;
lushort                       theSheets, theColumns, theRows;
lushort                       theFirstSheet, theFirstColumn, theFirstRow;
lmbcs                         theFilename[LPI_RNG_FILEPATH_BUFLEN];
lulong                        theCopySize;

/******************************************************************************\

   Utility function that sounds an error tone.  Used when user attempts to paste
   from an empty clipboard;

\*******************************************************************************/

lsshort clip_bell ()
{
   return (LpiUtBeep(BELL_FREQUENCY,BELL_DURATION));
}

/*******************************************************************************\

   Prompts the user for a clipboard range, assigns the dimensions, first cell
   location, and size of the range to global variables.

\*******************************************************************************/

lsshort ClipboardSize()
{
   lushort  apperr;
   lsshort  retval;
   LpiRangeType theRange;

   /* prompt the user for a range specification */
   retval = LpiRngInput(&theRange,COPY_RANGE_PROMPT,IS_ANCHORED,&apperr);
   switch (retval) {
      case LPI_SUCCESS:
         break;
      case LPI_K_BREAK:
      case LPI_K_ESCAPE:
			return (retval);
      default:
         return (LFALSE);
         break;
   }

   /* get range dimensions */
   if (LpiRngGetSheets(theRange,&theSheets) != LPI_SUCCESS) goto error;
   if (LpiRngGetColumns(theRange,&theColumns) != LPI_SUCCESS) goto error;
   if (LpiRngGetRows(theRange,&theRows) != LPI_SUCCESS) goto error;

   /* get range first cell location */
   if (LpiRngGetFirstCell(theRange,theFilename,&theFirstSheet,
      &theFirstColumn,&theFirstRow) != LPI_SUCCESS) goto error;

   /* get clipboard size */
   theCopySize = theSheets * theColumns * theRows;
   LpiRngDestroy(&theRange);
   return (LTRUE);

error:  
   LpiRngDestroy(&theRange);
   return (LFALSE);
}

/*******************************************************************************\

   Unmaps and frees clipboard memory.  Sets clipboard to EMPTY.

\*******************************************************************************/

lsshort ClipboardClear(LpiContext)
lptr(CONTEXT) LpiContext;
{
   lptr(lmhdl(CellDescription)) theCellsPtr;
   lmhdl(CellDescription)       theHandle;
   
   /* set clipboard state and check if anything to do */
   theStatus = CLIPBOARD_EMPTY;
   if (LpiCheckMem(theCellsHandle)) return (LTRUE);

   /* free any allocated structures */
   theCellsPtr = LpiMapMem(theCellsHandle,VMR_0);
   while (!LpiCheckMem(*theCellsPtr)) {
      if (!LpiFreeMem(*theCellsPtr++,sizeof(CellDescription))) return (LFALSE);
   }

   /* free handles array */
   LpiUnmapMem(theCellsHandle,VMR_0);
   return (LpiFreeMem(theCellsHandle,sizeof(theCellsHandle)));
}

/**************************************************************************\

   Allocates memory for an array of handles to cell description structures.
   Checks that the Lpi environment block supports allocations of the size of
   a cell description structure and of the size of the array of handles.   

\***************************************************************************/

lsshort ClipboardAllocate(LpiContext)
lptr(CONTEXT) LpiContext;
{
   lulong i, theAllocation, theCellAllocSize;
   lptr(lmhdl(CellDescription)) theCellsPtr;
   
   /* calculate allocation size and check if exceeds maximum allowed */
   theAllocation = (theCopySize+1) * sizeof(lmhdl(CellDescription));
   theCellAllocSize = sizeof(CellDescription);
   if ((LpiPEnvblk->maxsize < theCellAllocSize) && 
       (LpiPEnvblk->maxsize < theAllocation)) return (LFALSE);
   
   /* allocate array of handles */
   theCellsHandle = LpiAllocMem(CellDescription,theAllocation,VMR_0);
   if (LpiCheckMem(theCellsHandle)) return (LFALSE);

   /* return a pointer to the newly allocated memory block */
   theCellsPtr = LpiMapMem(theCellsHandle,VMR_0);

   /* initialize array of pointers to handles */
   for (i = 0; i < theCopySize+1; i++) *(theCellsPtr+i) = NULL_MEM_HANDLE;

   return (LTRUE);
}

/**************************************************************************\

   Advances through the globally specified range.  For each cell, allocates
   memory for the cell structure and stores the handle to that memory in a
   global array.  Uses a pointer to the memory to populate the structure
   with cell attributes.

\**************************************************************************/

lsshort ClipboardCopy (LpiContext)
lptr(CONTEXT) LpiContext;
{
   lushort s, c, r;
   lmhdl(CellDescription) theCellHandle;
   lptr(CellDescription) theCellPtr;
   lptr(lmhdl(CellDescription)) theCellsPtr;
   LpiCellType theCell;
   lsshort retval;

   theCellsPtr = LpiMapMem(theCellsHandle,VMR_0);

   for (s = 0; s < theSheets; s++) {
      for (c = 0; c < theColumns; c++) {
         for (r = 0; r < theRows; r++) {
            /* allocate memory for next struct */

            theCellHandle = LpiAllocMem(CellDescription,sizeof(CellDescription),
            VMR_1);
            if (LpiCheckMem(theCellHandle)) {
               LpiUnmapMem(theCellsHandle,VMR_0);
               return (LFALSE);
            }

            theCellPtr = LpiMapMem(theCellHandle,VMR_1);
            *theCellsPtr++ = theCellHandle;  

            /* get cell information */

            /* construct next cell */
            retval = LpiClConstructWZXY(&theCell,theFilename,theFirstSheet + s,
               theFirstColumn + c,theFirstRow + r);
            if (retval != LPI_SUCCESS) goto error_unmap;
   
            /* get cell contents */
            retval = LpiClGetContents(theCell, theCellPtr->Contents);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* get cell protection state */
            retval = LpiClGetProtection(theCell, &theCellPtr->Protection);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* get cell highlight state */
            retval = LpiClGetHighlight(theCell, &theCellPtr->Highlight);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* get cell color negative state */
            retval = LpiClGetColorNegative(theCell, &theCellPtr->ColorNegative);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* get cell parentheses state */
            retval = LpiClGetParentheses(theCell, &theCellPtr->Parentheses);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* get cell label prefix type */
            retval = LpiClGetLabelPrefix(theCell, &theCellPtr->LabelPrefix);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* get cell format type */
            retval = LpiClGetFormatPlaces(theCell, &theCellPtr->FormatType,
                                    &theCellPtr->FormatPlaces);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            LpiUnmapMem(theCellHandle,VMR_1);
            LpiClDestroy(&theCell);
         }
      }
   }
   LpiUnmapMem(theCellsHandle,VMR_0);
   return (LTRUE);

error_unmap_cell: 
   LpiClDestroy(&theCell);

error_unmap:
   LpiUnmapMem(theCellHandle,VMR_1);
   LpiUnmapMem(theCellsHandle,VMR_0);
   return (LFALSE);  
}

/******************************************************************************\

   Writes the contents of the clipboard, starting with the current cell.  For
   each cell, attains a pointer for the associated handle, stored in a global
   array.  Uses the pointer to populate the cell from structure fields.

\******************************************************************************/

lsshort ClipboardPaste (LpiContext)
lptr(CONTEXT) LpiContext;
{
   lushort s, c, r;
   lmhdl(CellDescription) theCellHandle;
   lptr(CellDescription) theCellPtr;
   lptr(lmhdl(CellDescription)) theCellsPtr;
   LpiCellType theCell;
   lsshort retval;
   lushort apperr;

   /* construct current cell and get location*/
   if (LpiClConstructCurrent(&theCell) != LPI_SUCCESS) return (LFALSE);

   if ((retval = LpiClGetWorkFileName(theCell,theFilename)) != LPI_SUCCESS)
      goto cell_destroy;
   
   retval = LpiClGetCoords(theCell,&theFirstSheet,&theFirstColumn,&theFirstRow);

cell_destroy:
   LpiClDestroy(&theCell);
   if (retval != LPI_SUCCESS) return (LFALSE);

   theCellsPtr = LpiMapMem(theCellsHandle,VMR_0);

   for (s = 0; s < theSheets; s++) {
      for (c = 0; c < theColumns; c++) {
         for (r = 0; r < theRows; r++) {

            theCellHandle = *theCellsPtr++;
            theCellPtr = LpiMapMem(theCellHandle,VMR_1);

            /* set cell information

            /* construct current cell */
            retval = LpiClConstructWZXY(&theCell,theFilename,theFirstSheet + s,
               theFirstColumn + c,theFirstRow + r);
            if (retval != LPI_SUCCESS) goto error_unmap;
   
            /* set cell contents */
            retval = LpiClSetContents(theCell, theCellPtr->Contents, &apperr);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* set cell protection state */
            retval = LpiClSetProtection(theCell, theCellPtr->Protection, &apperr);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* set cell highlight state */
            retval = LpiClSetHighlight(theCell, theCellPtr->Highlight, &apperr);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* set cell color negative state */
            retval = LpiClSetColorNegative(theCell, theCellPtr->ColorNegative, &apperr);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* set cell parentheses state */
            retval = LpiClSetParentheses(theCell, theCellPtr->Parentheses, &apperr);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* set cell label prefix type */
            retval = LpiClSetLabelPrefix(theCell, theCellPtr->LabelPrefix);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;

            /* set cell format type */
            retval = LpiClSetFormatPlaces(theCell, theCellPtr->FormatType,
                                    theCellPtr->FormatPlaces, &apperr);
            if (retval != LPI_SUCCESS) goto error_unmap_cell;
            LpiClDestroy(&theCell);
            LpiUnmapMem(theCellHandle,VMR_1);
         }
      }
   }
   LpiUnmapMem(theCellsHandle,VMR_0);
   return (LTRUE);

error_unmap_cell: 
   LpiClDestroy(&theCell);

error_unmap:      
   LpiUnmapMem(theCellHandle,VMR_1);
   LpiUnmapMem(theCellsHandle,VMR_0);
   return (LFALSE);  
}

/******************************************************************************\

   Main function: posts the clipboard copy and paste menu.  If the user selects
   COPY, prompts the user for a range, clears the current clipboard, allocates
   an array of handles that will hold clipboard structure references, and fills
   the clipboard.  If the user selects PASTE, check to see if the clipboard
   contains data, and if so, copies the clipboards contents starting with the
   current cell.

\******************************************************************************/

LpiMain(clip_m)
{
   lsshort      retval, theMenuItem;
   LpiMenuType  theMenu;

   /* construct clipboard menu */
   retval = LpiMnuConstruct(&theMenu,clip_theMenuArray,MENU_ITEMS);
   if (retval != LPI_SUCCESS) return;

	for (;;) {

	   /* post clipboard menu */
   	retval = LpiMnuInput(theMenu,&theMenuItem);
	   if (retval != LPI_SUCCESS) {
  		    LpiMnuDestroy(&theMenu);
    		return;
	   }

	   /* process menu selection */
   	switch (theMenuItem) {

      	case MENU_COPY:
      	   /* prompt the user for a range, check if backed out */
         	retval = ClipboardSize(LpiContext);
				if (retval == LFALSE) {
					goto error;
				}
				else if (retval != LTRUE) {
					break; 
				}

         	/* clear clipboard contents, if any*/
         	if (!ClipboardClear(LpiContext)) goto error;

         	/* allocate clipboard memory */
         	if (!ClipboardAllocate(LpiContext)) goto error;

         	/* fill the clipboard */
         	if (!ClipboardCopy(LpiContext)) goto error;

         	/* set clipboard flag to filled */  
         	theStatus = CLIPBOARD_FILLED;
      
         	goto error;
				break;

      	case MENU_PASTE:

         	/* test if anything on the clipboard */
         	if (theStatus == CLIPBOARD_FILLED) {

   	         /* paste from clipboard starting at current cell */
      	      if (!ClipboardPaste(LpiContext)) goto error;
	      	   }
	         else {
   	         /* clipboard empty */
      	      clip_bell();
					break;
         	}
	         goto error;
				break;

	      default:

				goto error;	
   	      break;
   	}
	}
   /* destroy menu */
error:
   LpiMnuDestroy(&theMenu);
   return;
}

/************************************************************************\

   The following function, LpiBind, is called when 1-2-3 loads our add-in.
   Use this function to perform any setup up, such as allocating memory,
   opening files, etc.

\************************************************************************/

LpiBind(clip_b)
{
   /* Start loading pointers to used modules */

   LPI_SETUP_LOAD;

   if (LPI_LOAD_INPUT   != LPI_SUCCESS) LpiSignal (LPI_FAILURE);
   if (LPI_LOAD_CELL    != LPI_SUCCESS) LpiSignal (LPI_FAILURE);
   if (LPI_LOAD_RANGE   != LPI_SUCCESS) LpiSignal (LPI_FAILURE);
   if (LPI_LOAD_UTILITY != LPI_SUCCESS) LpiSignal (LPI_FAILURE);

   theCellsHandle = NULL_MEM_HANDLE;
   LpiSignal (LPI_SUCCESS);
}

/************************************************************************\

   The following function, LpiUnbind, is called when 1-2-3 unloads our
   add-in.  This function simply executes the macro LPI_UNBIND and signals
   no errors.  Use this function to perform any clean up such, as
   deallocating memory you allocated in LpiBind, closing opened files, etc.

/************************************************************************/

LpiUnbind(clip_u)
{
   ClipboardClear;
   LPI_UNLOAD;                                    /* macro for clean-up */
   LpiSignal (LPI_SUCCESS);                       /* indicate no errors */
}
