


/****************************************************************************
    Copyright (c) 1993 DPI Services

    PROGRAM:    ACRODEMO

    FILE:       ACRODEMO.C

    PURPOSE:    Collect annotations from response documents into the Main
                document.

    DESCRIPTION: This program opens a Notes database and Adobe Acrobat.
                 It accesses the View named 'TODO' to find the main Note
                 that matches the document number and release numer passed
                 from the Notes Application.  The embedded Acrobat document
                 in the main Note is activated.  Then the response documents
                 are accessed.  Each embedded Acrobat document is activated
                 and the Annotations from the response Acrobat documents
                 are copied to the main Acrobat document.  The Consolidate
                 field is set to "Y" and all documents closed.

                 The format for the command is:

                 ACRODEMO dataset_name document# release#

                 All console and error messages are stored in the ACRODEMO.LOG
                 file.  If this program doesn't work correctly, check this file
                 first for errors.


   This version eliminates the memcpy to global storage.

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

/* Windows and C include files */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <windows.h>

/* Notes API include files */

#include <global.h>
#include <nsfdb.h>
#include <nif.h>
#include <nsfdata.h>
#include <nsferr.h>
#include <nsfnote.h>
#include <nsfsearc.h>
#include <ostime.h>
#include <osmisc.h>
#include <osfile.h>
#include <osmem.h>
#include <stdnames.h>
#include <misc.h>
#include <miscerr.h>
#include <easycd.h>
#include <editdflt.h>
#include <nsf.h>
#include <kfm.h>                        /* SECKFMGetUserName */
#include <idtable.h>                    /* IDEnumerate, etc. */
#include <nsfobjec.h>                   /* NSFDbReadObject */
#include "acroauto.h"
#include "cntrdll.h"

/* Local INCLUDE FILES */
 
#include "acrodemo.h"
 
/* global variables */

HCURSOR     hSaveCursor;
HCURSOR     hHourGlass;

static HANDLE    hInst;
static HANDLE    hModule;
static HWND      hwndMain;                     /* Handle to main window */

STATUS      status;    /* Status returned from Notes API function calls. */

static char    szDBFileNameString[MAXPATH];    /* target DB filename */
static char    szServerNameString[MAXPATH];    /* server of target DB */
static char    szDBFullPathString[2*MAXPATH];  /* full path to target DB */
static char    szDBTitleString[NSF_INFO_SIZE]; /* target DB title */
static DBHANDLE    hNotesDB = NULLHANDLE;      /* target DB handle */
static NOTEID      MainNoteID;                 /* ID of main document */
static NOTEID      nidSelectedView;            /* ID of view to process */
static HGLOBAL     hHugeBuffer;                /* Handle to hugh buffer  */
static char huge   *outbuf_ptr;                /* Pointer to Global buffer */
static char huge   *pHugeBuffer;               /* Pointer to Global buffer */

char             logfile[30] = "ACRODEMO.LOG";
char             emsg[200];          /* Buffers for error messages */
FILE             *pErrlog;           /* Error log for sprint finction */
DWORD            oleSize;            /* OLE Object Size               */
HACROPDDOC       pToPDDoc;           /* Handle to the output Acrobat Doc */
HACROAPP         hAcroApp;           /* Handle for Acrobat Application  */
char             text_key[80];       /* Key for desired document in view */
BOOL             field_found;        /* Flag if field found              */

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

    FUNCTION:    WinMain (if Windows) or main (otherwise)

    PURPOSE:     Provide the appropriate entry point: WinMain if Windows,
                 or main() otherwise.

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



int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;            /* current instance             */
HANDLE hPrevInstance;        /* previous instance            */
char *lpCmdLine;             /* command line                 */
int nCmdShow;
{
char          db_filename[60];   /* pathname of source database */
char          view_name[10];     /* View name to search         */
char          revNumber[30];   /* Revision number to process  */
char	      docNumber[50];     /* Document to be processed */
char          *formula;       /* a note selection formula */
FORMULAHANDLE    formula_handle; /* handle to compiled formula */
STATUS          error;        /* return status from API calls */
WORD            wdc;          /* a word we don't care about */
DWORD           numResponses; /* Number of responses processed  */
FARPROC         lpProc;       /* procedure instance for callback function */
HANDLE          prHandle;

HACROAUTO       hAcroAuto;


/* Externs for parameters passed to program */

extern int	__argc;
extern char	**__argv;

/* Initialize log file               */
      pErrlog = fopen(logfile,"w"); /* Open the file to clear it */
      fclose(pErrlog);
	
/* Set default values for the parameters. */

      strcpy (docNumber, "");

      sprintf(emsg,
            "Lotus Notes API.\nLaunch Acrobat version 2.0\n");
      log_msg();

 /* Get the docNumber parameter if passed to program. */

    if (__argc != 4)
    {
      sprintf(emsg,
        "Format for command is ACRODEMO <db_name> <docnumber> <revnumber>");
      log_msg();
      sprintf(emsg,
         "Number of parameters passed is %i\n",__argc);
      log_msg();
      sprintf(emsg,
         "First parameter is %s\n",__argv[1]);
      log_msg();
      return(4);
    }

    strcpy (db_filename, __argv[1]);  /* Get database name        */
    strcpy (docNumber, __argv[2]);    /* Get key value of main document */
    strcpy (revNumber, __argv[3]);     /* Get revision number  */

    /*  Build key for View retrieval                         */

    strcpy (text_key, docNumber);
    strcat (text_key, ".");
    strcat (text_key, revNumber);

      /*  Initialize Notes.  */

    error = NotesInit();

    if (error)
    {
      sprintf(emsg,"Error: unable to initialize Notes:%#4x.\n", ERR(error));
      log_msg();
      return (ERR(error));
     }
	
/* Open the Notes database               */  	

    strcpy(view_name, "TODO");

    if (error = NSFDbOpen (db_filename, &hNotesDB))
       {
        sprintf(emsg, "Notes Database %s could not be opened\n", db_filename);
        log_msg();
        return(error);
       }
    sprintf(emsg,"NSFDbOpen is succesful\n");
    log_msg();

    if (error = NIFFindView(hNotesDB, view_name, &nidSelectedView))
         return(error);

    sprintf(emsg,"Found view : %s \n", view_name);
    log_msg();

// For Ole 2.0 programs, it is recommended that the line
// SetMessageQueue(96) be places in you WinMain or InitInstance
// code.  This line will clear the current message queue, so do it early
    SetMessageQueue(96);
    hAcroAuto = AcroAutoInitialize();
    if (!hAcroAuto)
      {
       sprintf(emsg,"Could not initialize Acrobat\n");
       log_msg();
       return FALSE;
      }

    hAcroApp = AcroAppNew();
    if (!hAcroApp)
       {
        sprintf(emsg,"Could not get new Acrobat application\n");
        log_msg();
        goto error;
       }
	
//   AcroAppShow(hAcroApp);
	

    if (status = FindResponse(&numResponses))
      {
       sprintf(emsg,"Error from FindResponse()\n");
       log_msg();
      }
    NSFDbClose (hNotesDB);
    NotesTerm();
                   /* Terminate the Notes runtime system. */
    sprintf(emsg,"NotesDB Closed and Terminated\n");
    log_msg();

    AcroAppDelete(hAcroApp);
	
    AcroAutoUninitialize(hAcroAuto);

    return NOERROR;
	
error:
    sprintf(emsg, "AcroAuto Error\n");
    log_msg();

    AcroAutoUninitialize(hAcroAuto);
    return FALSE;

}

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

    FUNCTION: FindResponse

    PURPOSE:  Find responses so ACROBAT can collect Annotations
              for all documents in the selected view of the database.

    COMMENTS:
        This function opens the selected view, navigates to each
        main document in the view, and finds all the responses to
        the main document. It processes all response documents by
        calling ProcessResponses().   This loops
        through the view until it reaches the end of the view.

        If a single main document has more than about 32,000 response
        documents, this function will not process all the responses.
        This limitation arises because this function does not check for
        the SIGNAL_MORE_TO_DO bit in the SignalFlags parameter returned
        from NIFReadEntries.

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

STATUS far PASCAL  FindResponse( DWORD * pdwResponsesProcessed )
{
    STATUS      error;
    HCOLLECTION hCollection;            /* handle to open collection */
    BOOL        fFirstTime = TRUE;      /* used to detect empty view */
    COLLECTIONPOSITION  coll_pos;       /* collection position */
    COLLECTIONPOSITION  SaveCollPos;    /* save coll_pos for comparison */
    HANDLE      hRetBuff;               /* return buffer from ReadEntries */
    DWORD       dwNotesFound;           /* number of notes read */
    int         i;
    DWORD       match_size;             /* Number of entries found      */
    BOOL        fCollPosTumblerChanged; /* used when comparing coll_pos */

    /* initialize return value to 0 */

    *pdwResponsesProcessed = (DWORD)0;

    /* open view note (collection) */

    status = NIFOpenCollection( hNotesDB, hNotesDB, nidSelectedView,
                                0, NULLHANDLE, &hCollection,
                                NULLHANDLE, NULL, NULLHANDLE,
                                NULLHANDLE );
    if (status)
    {   /* Unable to open selected database view.*/
       sprintf(emsg, "Error: unable to open view: %#4x.\n", ERR(status));
       log_msg();
       return( ERR(status) );
    }


/* Look for notes that have the given primary sort key (which must be of
type text). We get back a COLECTIONPOSITION structure describing where the
first such note is in the collection, and a count of how many such notes
there are. Check the return code for "not found" versus a real error. */

   error = NIFFindByName (
          hCollection,        /* collection to look in */
          text_key,          /* string to match on */
          FIND_CASE_INSENSITIVE, /* match rules */
          &coll_pos,         /* where match begins (return) */
          &match_size);      /* how many notes found? */

   if (ERR(error) == ERR_NOT_FOUND)
      {
      sprintf (emsg,"\nKey not found in the collection.\n");
      log_msg();
      NIFCloseCollection (hCollection);
      return (NOERROR);
      }

   if (error)
      {
      NIFCloseCollection (hCollection);
      return (ERR(error));
      }

         status = NIFReadEntries( hCollection,
                     &coll_pos,          /* where the match begins */
                     NAVIGATE_CURRENT,   /* order to skip entries */
                     0,                  /* no. of entries to skip */
                     NAVIGATE_NEXT_PEER, /* navigate at this level */
                     1,                  /* just get one note id */
                     READ_MASK_NOTEID,   /* get the note ID */
                     &hRetBuff,          /* Note ID return buffer */
                     NULL,               /* length of return buffer */
                     NULL,               /* no. entries skipped */
                     &dwNotesFound,      /* entries read */
                     NULL);              /* signal warning */
        if (status)
        {
            /* Unable to read next entry in view.*/
         sprintf(emsg,
          "Error: unable to read next entry in view: %#4x.\n", ERR(status));
         log_msg();

             if (hRetBuff != NULLHANDLE)
            {
                OSMemFree( hRetBuff );
            }
            NIFCloseCollection( hCollection );
            return( ERR(status) );
        }
        if ( (dwNotesFound == (DWORD)0) && (fFirstTime == TRUE) )
        {
            /* No documents found in selected view.*/

           sprintf(emsg, "No documents found in selected view\n");
           log_msg();
            if (hRetBuff != NULLHANDLE)
            {
                OSMemFree( hRetBuff );
            }
            NIFCloseCollection( hCollection );
            return NOERROR;
        }
        fFirstTime = FALSE;

        /* If note is a category note, skip to next main document.
           If note is a main document, this function does nothing.
         */
        if (status = SkipToMainDocument( &hRetBuff, &dwNotesFound,
                                        &hCollection, &coll_pos ))
        {
            OSMemFree( hRetBuff );
            NIFCloseCollection( hCollection );
            return( ERR(status) );
        }
                                        /* process the main doc */
        error = ProcessMainDocument( hRetBuff, dwNotesFound );
        OSMemFree( hRetBuff );
        if (error)
        {
            NIFCloseCollection( hCollection );
            return( ERR(error) );
        }
                                        /* read all response docs */
        status = NIFReadEntries( hCollection,
                        &coll_pos,          /* where the match begins */
                        NAVIGATE_CHILD,     /* skip main document */
                        1,                  /* skip 1 to responses */
                        NAVIGATE_NEXT_PEER, /* navigate at this level */
                        0xFFFFFFFF,         /* return ALL responses */
                        READ_MASK_NOTEID,   /* get the note IDs */
                        &hRetBuff,          /* Note ID return buffer */
                        NULL,               /* length of return buffer */
                        NULL,               /* no. entries skipped */
                        &dwNotesFound,      /* entries read */
                        NULL);              /* signal warning */
        if (status)
        {
           /* Unable to read response documents.*/
         sprintf(emsg,
          "Error: unable to read response documents: %#4x.\n", ERR(status));
         log_msg();

         if (hRetBuff != NULLHANDLE)
            {
                OSMemFree( hRetBuff );
            }
            NIFCloseCollection( hCollection );
            return( ERR(status) );
        }

        /* process response docs */

        error = ProcessResponses( hRetBuff, dwNotesFound );
       OSMemFree( hRetBuff );
        if (error)
        {
            NIFCloseCollection( hCollection );
            return( ERR(error) );
        }

        (*pdwResponsesProcessed) += dwNotesFound;

    NIFCloseCollection( hCollection );
    return NOERROR;
}


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

   FUNCTION:  SkipToMainDocument()

   PURPOSE:   If the note ID in the specified buffer identifies a
              category note, then skip to the next non-category note,
              adjusting collectionposition, buff_handle, and notes_found.

   RETURNS:   status

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

STATUS far PASCAL   SkipToMainDocument( HANDLE *phRetBuff,
                                        DWORD * pdwNotesFound,
                                        HCOLLECTION *phCollection,
                                        COLLECTIONPOSITION *pCollPos )
{
    while (IsCategory( *phRetBuff, *pdwNotesFound ))
    {
        /* Is a category note. Calling NIFReadEntries to skip 1  */
        OSMemFree( *phRetBuff );
        status = NIFReadEntries( *phCollection,
                       pCollPos,           /* where the match begins */
                       NAVIGATE_NEXT,      /* order to skip entries */
                       1,                  /* no. of entries to skip */
                       NAVIGATE_NEXT_PEER, /* navigate at this level */
                       1,                  /* just get one note id */
                       READ_MASK_NOTEID,   /* get the note ID */
                       phRetBuff,          /* Note ID return buffer */
                       NULL,               /* length of return buffer */
                       NULL,               /* no. entries skipped */
                       pdwNotesFound,      /* entries read */
                       NULL );             /* signal warning */
        if (status)
        {
            /* Unable to skip category note to main document. */
            sprintf(emsg, "Error: unable to skip category note to main document: %#4x.\n", ERR(status));
           log_msg();
            if (*phRetBuff != NULLHANDLE)
            {
                OSMemFree( *phRetBuff );
            }
            return( ERR(status) ) ;
        }
    }
    return NOERROR;
}

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

   FUNCTION:  IsCategory()

   RETURNS:   TRUE if Note ID in buffer specifies a category note,
              FALSE otherwise.

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

BOOL far PASCAL IsCategory( HANDLE BuffHdl, DWORD dwNotesFound )
{
    NOTEID *pNoteIDData = NULL;
    BOOL    fRetval = FALSE;

    pNoteIDData = (NOTEID *)OSLockObject( BuffHdl );

    if (NOTEID_CATEGORY & *pNoteIDData)
    {
        fRetval = TRUE;
    }
    OSUnlockObject( BuffHdl );
    return( fRetval );
}

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

   FUNCTION:  ProcessMainDocument()

   PURPOSE:   Just store the Note ID of main document in global variable
              MainNoteID for use when processing response documents to it.

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

STATUS far PASCAL ProcessMainDocument( HANDLE hBuff, DWORD dwNotesFound )
{
    char   *pBuffer = NULL;

    if (dwNotesFound <= (DWORD)0)        /* need to handle empty buffers */
        return NOERROR;

    pBuffer = OSLockObject( hBuff );

    MainNoteID = *((NOTEID *)pBuffer);
    sprintf(emsg, "Processing Main Document\n");
    log_msg();

    OSUnlockObject( hBuff );

    return NOERROR;
}

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

   FUNCTION:  ProcessResponses()

   PURPOSE:   Process all responses to one main document.

   INPUTS:    hBuff - a buffer full of response Note IDs.
              dwNotesFound - the number of response note IDs

   RETURNS:   status

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

STATUS far PASCAL ProcessResponses( HANDLE hBuff, DWORD dwNotesFound )
{
    NOTEHANDLE         hMainDoc;               /* main doc handle */
    char               *pBuffer = NULL;
    NOTEID             *pNoteIDData = NULL;
    int                i;
    int unsigned       uinta;
    STATUS             error;
    HANDLE             hCompound;      /* handle to Compound Text context */
    COMPOUNDSTYLE      Style;
    DWORD              dwStyleID;
    OBJECT_DESCRIPTOR  toOLEObject;    /* Object descriptor of OLE object */
    HANDLE             htoOLEBuffer;   /* Actual object                   */
    char               *pDump = NULL;
    void*              m_pToContainer;
    HACROAVDOC         pToAVDoc;
    BOOL               acro_good;
    char               *ptoOLEBuffer;
    HGLOBAL            hGlobal;
    void *             lptoOLEObject;
    HACRORECT		   hAcroRectOld;

    /* An empty buffer means there are no responses to this main document */
    if (dwNotesFound <= (DWORD)0)
    {
        return NOERROR;
    }
    /* Open main document. */
    if (status = NSFNoteOpen( hNotesDB, MainNoteID, 0, &hMainDoc ))
    {
        /* Unable to open main document in view */
       sprintf(emsg,
               "Error: unable to open main document in view: %#4x.\n",
                ERR(status));
        log_msg();
        return( ERR(status) );
    }
    sprintf(emsg,
            "Processing Main Document in ProcessResponses()\n");
    log_msg();


   /* Activate Main Acrobat Document            */
#if 0    /* KJB */
     if (!ExecuteNotesMenuItem("Select &All"))
         {
          sprintf(emsg,
           "Select All: Menu Item not found.\n");
          log_msg();
          return FALSE;
	 }
#endif
     if (!ExecuteNotesMenuItem("Edit Object:"))
         {
          sprintf(emsg,
           "Edit Object: Menu Item not found or returned an error.\n");
          log_msg();
          return FALSE;
	 }

     pToAVDoc = NULL;
     pToPDDoc = NULL;
     m_pToContainer = NULL;

     pToAVDoc = AcroAppGetActiveDoc(hAcroApp);
     if (pToAVDoc == NULL)
       {
        sprintf(emsg, "Error: Could not open Main AV Document\n");
        log_msg();
        return (8);
       }

	 // save current frame rectangle     
     AcroAVDocMaximize(pToAVDoc,FALSE);
     hAcroRectOld = AcroAVDocGetFrame(pToAVDoc);
     {
     	HACRORECT hAcroRect;
     	hAcroRect = AcroRectNew();
     	AcroRectSetLeft(hAcroRect,-100);
     	AcroRectSetRight(hAcroRect,-50);
     	AcroRectSetTop(hAcroRect,-100);
     	AcroRectSetBottom(hAcroRect,-50);
     	
     	// hide old frame rectangle
     	AcroAVDocSetFrame(pToAVDoc,hAcroRect);
     	AcroRectDelete(hAcroRect);
     }

     pToPDDoc = AcroAVDocGetPDDoc(pToAVDoc);

     if (pToPDDoc == NULL)
       {
        sprintf(emsg, "Error: Could not open Main PD Document\n");
        log_msg();
        return (8);
       }

   /* Now loop over each Note ID in the buffer. The buffer contains
      the Note IDs of all the responses to this main document. For
      each response, assimilate that response note into the compound
      text context.
    */

    pBuffer = OSLockObject( hBuff );

    pNoteIDData = (NOTEID *) pBuffer;

    for( i=0, error = NOERROR;
        (i<(int)dwNotesFound) && (error == NOERROR);
        i++)
     {
        error = ProcessOneResponse( *(pNoteIDData+i), hCompound, dwStyleID);
     }
    OSUnlockObject( hBuff );

    sprintf(emsg, "All responses processed\n");
    log_msg();

   /* If error, clean up and return. ProcessOneResponse has already
      displayed an error message.
    */
    if (error)
    {
         NSFNoteClose( hMainDoc );
         AcroPDDocDelete(pToPDDoc);
         AcroAVDocDelete(pToAVDoc);
        return( ERR(error) );
    }

    /* leave main document maximized */
    AcroAVDocSetFrame(pToAVDoc,hAcroRectOld);
    AcroAVDocMaximize(pToAVDoc,TRUE);

    /* Delete Acrobat objects   */

    AcroPDDocDelete(pToPDDoc);
    AcroAVDocDelete(pToAVDoc);


    status = NSFNoteUpdate( hMainDoc, 0 );
    NSFNoteClose( hMainDoc );

    sprintf(emsg, "Main document updated and closed\n");
    log_msg();

    if (status)
    {
        /* Unable to update main document with new Acrobat object. */
        sprintf(emsg,
           "Error: unable to embed ACROBAT document : %#4x.\n",
           ERR(status));
        log_msg();
        return( ERR(status) );
    }



    return NOERROR;    /* Return as processing is done   */
 }

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

   FUNCTION:  ProcessOneResponse

   PURPOSE:   Process one Note. Called once for each response note in
              the buffer.

   RETURNS:   status

   COMMENTS:
        ProcessOneResponse().  Processes a response taking off the
            annotations and placing them in the main document.
*****************************************************************************/

STATUS far PASCAL ProcessOneResponse( NOTEID nidResp,
                                      HANDLE hCompound,
                                      DWORD dwStyleID )
{

  OBJECT_DESCRIPTOR  fromOLEObject; /* Object descriptor of OLE object  */
  NOTEHANDLE         hRespNote;     /* Response note handle  */
  HANDLE             hfromOLEBuffer;
  DWORD              fromOLESize;
  long               numPages;
  long               J;
  long               K;
  long               numToAnnots;
  long               numFromAnnots;
  LPCSTR             fromAnnotType;
  HACROAVDOC         pFromAVDoc;
  HACROPDDOC         pFromPDDoc;
  HACROPDANNOT       fromAnnot;
  HACROPDANNOT       toAnnot;
  HACRORECT          fromRect;
  HACROPDPAGE        toPage;
  HACROPDPAGE        fromPage;
  HACROTIME          hAcroTime;
  void*              m_pFromContainer;
  void FAR*          lpOleObject;

  void*              m_pToContainer;
  BOOL               acro_good;
  char             *pfromOLEBuffer;
  void*             lpfromOLEObject;


  if (status = NSFNoteOpen( hNotesDB, nidResp, 0, &hRespNote ))
    {
       /* Unable to open response document */
        sprintf(emsg,
         
       "Error: Unable to open response document: %#4x.\n",
                ERR(status));
        log_msg();
        return ( ERR(status) );
    }
    sprintf(emsg, "Processing Response Document\n");
    log_msg();

    if (status = GetOLEObject(hRespNote,&fromOLEObject,&hfromOLEBuffer))
     {
        sprintf(emsg,
                "Error: Error in response document: %#4x.\n",
                ERR(status));
        log_msg();
        return ( ERR(status) );
     }
    fromOLESize = oleSize;

  /* Open response  Acrobat Document            */

  
    pHugeBuffer = (char huge *) GlobalLock (hHugeBuffer);


     m_pFromContainer = CntrDLLContainerObjectNew();
     if (m_pFromContainer == NULL)
       {
        sprintf(emsg, "Error in CntrDLLContainerObjectNew\n");
        log_msg();
        return (8);
       }

//KJB acro_good = CntrDLLContainerObjectActivate(m_pFromContainer,hGlobal);
     acro_good = CntrDLLContainerObjectActivate(m_pFromContainer,
                                                pHugeBuffer,
                                                fromOLESize);
     if (!acro_good)
       {
        sprintf(emsg, "Error: Could not activate response object\n");
        log_msg();
        return (8);
       }
       
     GlobalUnlock (hHugeBuffer);  /* Unlock Acrobat Object  */
     
     pFromAVDoc = AcroAppGetActiveDoc(hAcroApp);
     if (pFromAVDoc == NULL)
       {
        sprintf(emsg, "Error: Could not open Response AV Document\n");
        log_msg();
        return (8);
       }

     pFromPDDoc = AcroAVDocGetPDDoc(pFromAVDoc);
     if (pFromPDDoc == NULL)
       {
        sprintf(emsg, "Error: Could not open Response PD Document\n");
        log_msg();
        return (8);
       }


     numPages = AcroPDDocGetNumPages(pFromPDDoc);
     for (J=0; J < numPages; J++)
      {
        toPage = AcroPDDocAcquirePage(pToPDDoc,J);
        fromPage = AcroPDDocAcquirePage(pFromPDDoc,J);
        numFromAnnots = AcroPDPageGetNumAnnots(fromPage);

        for (K=0;  K < numFromAnnots; K++)
          {
            fromAnnot = AcroPDPageGetAnnot(fromPage,K);
            fromAnnotType = AcroPDAnnotGetSubtype(fromAnnot);
            if ((_stricmp(fromAnnotType,"Text"))== 0)
              {
                numToAnnots = AcroPDPageGetNumAnnots(toPage);
                fromRect = AcroPDAnnotGetRect (fromAnnot);
                toAnnot = AcroPDPageAddNewAnnot(
                           toPage,
                           (numToAnnots-1),
                           "Text",
                           fromRect);

             /* Copy Annotation attributes to consolidated document */

                AcroPDAnnotSetColor(toAnnot,
                    (long) (AcroPDAnnotGetColor(fromAnnot)));
                AcroPDAnnotSetTitle(toAnnot,
                     (LPCSTR) (AcroPDAnnotGetTitle(fromAnnot)));
                AcroPDAnnotSetContents(toAnnot,
                     (LPCSTR) (AcroPDAnnotGetContents(fromAnnot)));
                AcroPDAnnotSetOpen(toAnnot,
                     (BOOL) (AcroPDAnnotIsOpen(fromAnnot)));
		        hAcroTime =
                     (HACROTIME) AcroPDAnnotGetDate(fromAnnot);
                AcroPDAnnotSetDate(toAnnot, hAcroTime);
		        AcroTimeDelete(hAcroTime);
	        	AcroPDAnnotDelete(toAnnot);
		        AcroRectDelete(fromRect);
              }
             AcroPDAnnotDelete(fromAnnot);
           }
          AcroPDPageDelete(toPage);
          AcroPDPageDelete(fromPage);
        }
     AcroPDDocDelete(pFromPDDoc);
     AcroAVDocDelete(pFromAVDoc);

	 // KJB - I moved this here...
	 // KJB Note: if an error occurs all the AcroPDDoc, etc objects should be deleted
	 // DID NOT WORK
//   
//KJB
// The cntrdll.dll code actually requires us to free the handle passed in:
//	 {
//	 	HGLOBAL pRet = GlobalFree(hfromOLEBuffer);
//	 	if (pRet) {
//	 		MessageBox(NULL,"HELLo","ERROR",MB_OK);
//	 	}
//	 }
      GlobalFree(hHugeBuffer);
     sprintf(emsg, "Acrobat Processing Complete for One Response\n");
     log_msg();

   if (m_pFromContainer)
          {
            CntrDLLContainerObjectDelete(m_pFromContainer);
          }

  /* Change the consolidation flag to 'Y'  */

     if (status = NSFItemSetText ( hRespNote,
                "Consolidation",
                "Y",
                MAXWORD))
       {
         NSFNoteClose (hRespNote);
         return (ERR(status));
       }

   if (status = NSFNoteUpdate (hRespNote, 0))
    {
        NSFNoteClose (hRespNote);
        return (ERR(status));
    }


    /* Close the note         */
     if (status = NSFNoteClose (hRespNote))
        return (ERR(status));

   return NOERROR;
}
/**************************************************************************/
/*


    FUNCTION:   GetOleObject

    PURPOSE:    Read OLE Object into storage


    DESCRIPTION:

    Obtain the Embedded object from a note and return to caller
    the OBJECT_DESCRIPTOR and the handle to the storage where the object is.


    INPUTS:
        NOTEHANDLE  hNote   handle of open note

    OUTPUTS:
        OBJECT_DESCRIPTOR * pObject     gets the OBJECT_DESCRIPTOR structure
        HGLOBAL             *phOLEBuffer  gets the handle to the OLE object


    RETURNS:
        NOERROR if successfully returned all the information re: $LeftToDo
        ERR_ITEM_NOT_FOUND - if the $LeftToDo item was not present in macro
        (other nonzero values) - error codes from lower level API functions

.

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

STATUS far PASCAL GetOLEObject
            (NOTEHANDLE            hNote,
             OBJECT_DESCRIPTOR     *pObject,
             HANDLE                *phOLEBuffer)
{
/* Local data declarations. */


    char               field_text[500];
    BLOCKID          field_block;
    DWORD            field_length, text_length;
    WORD             field_type;
    HANDLE          text_buffer;
    char              *text_ptr;
    STATUS          error;
    WORD            olePriv;
    WORD            oleClass;
    
    
    DWORD           bufOffset;
    int unsigned    copy_size;
    
    char            *pOLEBuffer;
    DWORD           max_block;
    
/* Look for the $File field in document. This function tells us
whether the field is there, and if present, its location (BLOCKID)
within Notes' memory. Check the return code for "field not found" versus
a real error. */

    error = NSFItemInfo (
            hNote,                     /* note handle */
            ITEM_NAME_ATTACHMENT,      /* $FILE field  */
            sizeof(ITEM_NAME_ATTACHMENT) -1,
            NULL,                      /* full field (return) */
            &field_type,               /* field type (return) */
            &field_block,              /* field contents (return) */
            &field_length);            /* field length (return) */

    field_found = TRUE;

    if (ERR(error) == ERR_ITEM_NOT_FOUND)
      {
        field_found = FALSE;
        error = NOERROR;
      }

    if (error)
      {
        NSFNoteClose (hNote);
        return (ERR(error));
      }
    if (field_found)
     {
         sprintf(emsg, "$File field found\n");
         log_msg();

      *pObject =
        *((OBJECT_DESCRIPTOR*)(OSLockBlock(char,field_block)+sizeof(WORD)));

       if (pObject->ObjectType != OBJECT_FILE)
       {
         sprintf(emsg, "Error: object '%s' unknown type.\n",
              ITEM_NAME_ATTACHMENT);
         log_msg();

         OSUnlockBlock(field_block);
        return (4);
       }
       
      error = NSFDbGetObjectSize(hNotesDB,
                          pObject->RRV,
                          pObject->ObjectType,
                          &oleSize,
                          &oleClass,
                          &olePriv);
      if (error)
       {
        sprintf(emsg, "Error: unable to get object size '%s' .\n",
                ITEM_NAME_ATTACHMENT);
           log_msg();
           OSUnlockBlock(field_block);
           return (error);
      }
      
      max_block = 61440;
            
      hHugeBuffer = GlobalAlloc(GMEM_MOVEABLE,oleSize);  /* Get storage for object */
      pHugeBuffer = (char huge *) GlobalLock(hHugeBuffer); /* Get pointer to storage */
      outbuf_ptr = pHugeBuffer;
  
      for (bufOffset = 0; bufOffset < oleSize; )
       {
         if ((max_block + bufOffset) > oleSize)
           max_block = oleSize - bufOffset;
              
         error = NSFDbReadObject(hNotesDB,
                             pObject->RRV,
                             bufOffset,
                             max_block,
                             phOLEBuffer);
          if (error)
          {
           sprintf(emsg,
                 "Error: unable to read object '%s'; API Error = %d .\n",
                 ITEM_NAME_ATTACHMENT,error);
           log_msg();
           return (error);
          }
      
         pOLEBuffer = OSLockObject (*phOLEBuffer);          /* Get pointer to object */
         copy_size = max_block;  
         memcpy (outbuf_ptr, pOLEBuffer, copy_size); 
         outbuf_ptr = outbuf_ptr + copy_size;
         bufOffset = bufOffset + copy_size;
         
         OSUnlockObject (*phOLEBuffer);
         OSMemFree (*phOLEBuffer);      
       }                                       /* End of For Loop  */
       
      OSUnlockBlock(field_block);
      GlobalUnlock(hHugeBuffer);
      sprintf(emsg, "Leaving getOLEObject\n");
           log_msg();
   }

     return NOERROR;

 }

/**************************************************************************/
/*                                                                        */
/*        Write a message to a file. Message will contain a date stamp    */
/*        and contents. If file is not present one will be created.       */
/*                                                                        */
/**************************************************************************/

STATUS far PASCAL log_msg ()
{
            pErrlog = fopen(logfile,"a"); /* Open the file for appending */
            fputs(emsg,pErrlog);          /* Write the message buffer */
            fclose(pErrlog);              /* Close the log file */
            return (NOERROR);
}
/**************************************************************************/
/*                                                                        */
/*        Find Notes Menu Item and invoke it as if menu item had been     */
/*        invoked by a user in Notes.  The menu item can only be found if */
/*        Notes is in the state where the menu item would be visable from */
/*        the user interface.                                             */
/*                                                                        */
/**************************************************************************/

BOOL ExecuteNotesMenuItem(LPCSTR szMenuString)
{
	HWND hWnd;
	WORD nMenuId;
	HMENU hMenu;
	
	// find the directory where notes resides (since it is not in the path)
	// Search for a windows class "NOTES" and get the module which will have
    // a path.  Do a CD to this path so we will be able to load ok.
	HWND hWndDesktop = GetDesktopWindow();
	hWnd = GetWindow(hWndDesktop,GW_CHILD);
	// there must be at least one window, our window
	do {
		char szClassName[21];
		GetClassName(hWnd,szClassName,20);
		if (!lstrcmp(szClassName,"NOTES")) {
			// found the notes window
			break;
		}
		hWnd = GetWindow(hWnd,GW_HWNDNEXT);
	} while (hWnd);

	// no Lotus Notes Window was found
	if (!hWnd) {
		return FALSE;
	}

	// get the menu
	hMenu = GetMenu(hWnd);
	// allow the application to change its menus
	SendMessage(hWnd,WM_INITMENU,(WPARAM)hMenu,0L);
	
	nMenuId = FindMenuId(hWnd, hMenu, szMenuString);
	if (!nMenuId) return FALSE;

	// send the WM_COMMAND message
	// Perhaps: try post message in case when this is executed
	// we are already inside a menu item of the program.
	SendMessage(hWnd,WM_COMMAND,(WPARAM)nMenuId,0L);
	
	return TRUE;	
}

/**************************************************************************/
/*                                                                        */
/*        Compare Menu Item desired to Menu Item found.                   */
/*        Return TRUE if good compare                                     */
/*                                                                        */
/**************************************************************************/

BOOL MenuStringCompare(LPCSTR szString0, LPCSTR szString1)
{
	const char* pChar0 = szString0;
	const char* pChar1 = szString1;
	
	// compare up to the lenght of string0 only
	while (*pChar0) {
		// skip all '&' signs these are special menu things
		while (*pChar0 && *pChar0 == '&') pChar0++;
		while (*pChar1 && *pChar1 == '&') pChar1++;
		
		if (*pChar0 != *pChar1) return FALSE;
		pChar0++;
		pChar1++;
	}
	
	// a match has been found
	return TRUE;
}


/**************************************************************************/
/*                                                                        */
/*        Search the active Notes Menu Tree to find the desired menu item.*/
/*                                                                        */
/**************************************************************************/
WORD FindMenuId(HWND hWnd, HMENU hMenu, LPCSTR szMenuString)
{
	int i;
	int nMenuId;

	for (i = 0;i < GetMenuItemCount(hMenu); i++) {
		HMENU hSubMenu = GetSubMenu(hMenu,i);

		// was this a submenu?  if so, then recursively search it
		if (hSubMenu) {
			// allow the application to update the menu items in the
			// pop up.  Note: we lie about whether or not this submenu
			// is a system menu.  Hope this does not matter.
			SendMessage(hWnd,WM_INITMENUPOPUP,(WPARAM)hSubMenu,0);
			nMenuId = FindMenuId(hWnd, hSubMenu,szMenuString);
			if (nMenuId) return nMenuId;
		} else {

			// is it active?
			if (!(GetMenuState(hMenu,i,MF_BYPOSITION) && MF_DISABLED) ) {
				char szTemp[256];

				// see if this menu item matches our string,
				GetMenuString(hMenu,i,szTemp,256,MF_BYPOSITION);

				// compare the first part of the string to see if a match
				if (MenuStringCompare(szMenuString,szTemp)) {
					nMenuId = GetMenuItemID(hMenu,i);
					return nMenuId;
				}
			}
		}
	}

	// no match was found

	return 0;
}
