home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
drgrnd.zip
/
drag.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-28
|
59KB
|
1,257 lines
/*********************************************************************
* *
* MODULE NAME : drag.c AUTHOR: Rick Fishman *
* DATE WRITTEN: 07-16-93 *
* *
* MODULE DESCRIPTION: *
* *
* Part of the 'DRGRENDR' drag/drop sample program. *
* *
* This module handles all the Drag/Drop processing for the *
* DRGRENDR.EXE sample program. *
* *
* NOTES: *
* *
* We use the DRM_FISHMAN rendering mechanism. This is a fairly *
* simple protocol that requires rendering on the drop. *
* *
* FUNCTIONS AVALABLE TO OTHER MODULES: *
* *
* dragInit *
* dragOver *
* dragDrop *
* *
* *
* HISTORY: *
* *
* 07-16-93 - Program coded. *
* *
* Rick Fishman *
* Code Blazers, Inc. *
* 4113 Apricot *
* Irvine, CA. 92720 *
* CIS ID: 72251,750 *
* *
*********************************************************************/
#pragma strings(readonly) // used for debug version of memory mgmt routines
/*********************************************************************/
/*------- Include relevant sections of the OS/2 header files --------*/
/*********************************************************************/
#define INCL_DOSERRORS
#define INCL_DOSPROCESS
#define INCL_SHLERRORS
#define INCL_WINDIALOGS
#define INCL_WINERRORS
#define INCL_WINFRAMEMGR
#define INCL_WININPUT
#define INCL_WINSTDCNR
#define INCL_WINSTDDRAG
#define INCL_WINWINDOWMGR
/**********************************************************************/
/*----------------------------- INCLUDES -----------------------------*/
/**********************************************************************/
#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "drgrendr.h"
/*********************************************************************/
/*------------------- APPLICATION DEFINITIONS -----------------------*/
/*********************************************************************/
#define DRAG_RMF "<DRM_FISHMAN,DRF_UNKNOWN>" // Rendering Mechanism/Format
/**********************************************************************/
/*---------------------------- STRUCTURES ----------------------------*/
/**********************************************************************/
/**********************************************************************/
/*----------------------- FUNCTION PROTOTYPES ------------------------*/
/**********************************************************************/
int CountSelectedRecs ( HWND hwndFrame, PCNRREC pCnrRecUnderMouse,
PBOOL pfUseSelectedRecs );
void SetSelectedDragItems( HWND hwndFrame, int cRecs, PDRAGINFO pDragInfo,
PDRAGIMAGE pDragImage );
void SetOneDragItem ( HWND hwndFrame, PCNRREC pCnrRecUnderMouse,
PDRAGINFO pDragInfo, PDRAGIMAGE pDragImage,
int iOffset );
void ProcessDroppedItem ( HWND hwndFrame, PDRAGINFO pDragInfo,
PDRAGITEM pDragItem, PDRAGTRANSFER pDragXfer );
void FindTempFile ( PSZ szTempFileName );
MRESULT Render ( HWND hwndFrame, PDRAGTRANSFER pDragXfer );
void DoTheRendering ( PDRAGTRANSFER pDragXfer );
MRESULT RenderComplete ( HWND hwndFrame, PDRAGTRANSFER pDragXfer,
USHORT fsRender );
MRESULT EndConversation ( HWND hwndFrame );
void RemoveSourceEmphasis( HWND hwndFrame );
void TargetCleanup ( HWND hwndFrame );
void SourceCleanup ( HWND hwndFrame );
FNWP wpSource, wpTarget;
/**********************************************************************/
/*------------------------ GLOBAL VARIABLES --------------------------*/
/**********************************************************************/
char szDroppedOnCnrTitle[] = "Open a table by double-clicking on it!";
/**********************************************************************/
/*--------------------------- dragMessage ----------------------------*/
/* */
/* A DM_ MESSAGE WAS RECEIVED BY THE FRAME WINDOW PROCEDURE. */
/* */
/* PARMS: frame window handle, */
/* message id, */
/* mp1 of the message, */
/* mp2 of the message */
/* */
/* NOTES: */
/* */
/* RETURNS: return code of message */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
MRESULT dragMessage( HWND hwndFrame, ULONG msg, MPARAM mp1, MPARAM mp2 )
{
switch( msg )
{
// We only want to respond yes or no here so as not to hold up the
// target window. All rendering will be done in the UM_DO_THE_RENDERING
// message.
case DM_RENDER: // The SOURCE window gets this message
{
MRESULT mr = Render( hwndFrame, (PDRAGTRANSFER) mp1 );
if( mr )
WinPostMsg( hwndFrame, UM_DO_THE_RENDERING, mp1, NULL );
return mr;
}
case UM_DO_THE_RENDERING:
DoTheRendering( (PDRAGTRANSFER) mp1 );
return 0;
case DM_ENDCONVERSATION: // The SOURCE window gets this message
return EndConversation( hwndFrame );
case DM_RENDERCOMPLETE: // The TARGET window gets this message
{
PDRAGTRANSFER pDragXfer = (PDRAGTRANSFER) mp1;
// If the source gets this message it means that it requested a
// a retry and the target said no. So complete the loop... Note that
// pDragXfer->pditem->hwndItem is the source window.
if( hwndFrame == pDragXfer->pditem->hwndItem )
DrgPostTransferMsg( pDragXfer->hwndClient, DM_RENDERCOMPLETE,
pDragXfer, DMFL_RENDERFAIL, 0, FALSE );
else
return RenderComplete( hwndFrame, (PDRAGTRANSFER) mp1,
SHORT1FROMMP( mp2 ) );
return 0;
}
}
return 0;
}
/**********************************************************************/
/*----------------------------- dragInit -----------------------------*/
/* */
/* PROCESS CN_INITDRAG NOTIFY MESSAGE. */
/* */
/* PARMS: frame window handle, */
/* pointer to the CNRDRAGINIT structure */
/* */
/* NOTES: the CN_INITDRAG message is equivalent to the WM_BEGINDRAG */
/* message. The reason I mention this is because if the source*/
/* window in the drag is not a container you will get the */
/* WM_BEGINDRAG message. */
/* */
/* WM_BEGINDRAG is sent to windows to allow them to know that */
/* the user is requesting a drag from their window. This */
/* message contains only the x,y coordinates of the mouse at */
/* the time of WM_BEGINDRAG. */
/* */
/* The reason for CN_INITDRAG is first so that the container */
/* can notify its owner when it gets a WM_BEGINDRAG message */
/* and second so that the container can give its owner more */
/* info, like what container record the mouse pointer is over.*/
/* To accomplish this, the container packages up this informa-*/
/* tion in a CNRDRAGINIT structure and sends that structure */
/* along with the CN_INITDRAG message. */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void dragInit( HWND hwndFrame, PCNRDRAGINIT pcdi )
{
PCNRREC pCnrRecUnderMouse = (PCNRREC) pcdi->pRecord;
PDRAGIMAGE pDragImage = NULL;
PDRAGINFO pDragInfo = NULL;
BOOL fUseSelectedRecs;
PINSTANCE pi = INSTDATA( hwndFrame );
int cRecs;
if( !pi )
{
Msg( "dragInit cant get Inst data RC(%X)", HWNDERR( hwndFrame ) );
return;
}
// pSavedDragInfo is used during drop processing. When the drop is complete
// (the source gets a DM_ENDCONVERSATION message from the target), the
// source will free the DRAGINFO structure and set this field to NULL.
// So pSavedDragInfo is non-NULL if a drop has not yet completed. In this
// case we make it easy on ourselves by not allowing another drag. The
// reason for this is that if we allow another drag to take place we will
// need to overwrite this pSavedDragInfo field in which case the drop
// processing would not free the right DRAGINFO structure. Obviously in a
// commercial app you'd want to use a different mechanism for storing the
// DRAGINFO structure, like a linked list, so you could start another drag
// while the drop is in progress.
if( pi->pSavedDragInfo )
{
WinAlarm( HWND_DESKTOP, WA_WARNING );
return;
}
// Count the records that have CRA_SELECTED emphasis. Also return whether
// or not we should process the CRA_SELECTED records. If the container
// record under the mouse does not have this emphasis, we shouldn't. In that
// case we would just process the record under the mouse.
cRecs = CountSelectedRecs( hwndFrame, pCnrRecUnderMouse, &fUseSelectedRecs);
if( cRecs )
{
int iDragImageArraySize = cRecs * sizeof( DRAGIMAGE );
// Allocate an array of DRAGIMAGE structures. Each structure contains
// info about an image that will be under the mouse pointer during the
// drag. This image will represent a container record being dragged.
pDragImage = (PDRAGIMAGE) malloc( iDragImageArraySize );
if( pDragImage )
{
memset( pDragImage, 0, iDragImageArraySize );
// Let PM allocate enough memory for a DRAGINFO structure as well
// as a DRAGITEM structure for each record being dragged. It will
// allocate shared memory so other processes can participate in the
// drag/drop.
pDragInfo = DrgAllocDraginfo( cRecs );
if( pDragInfo )
{
pi->pSavedDragInfo = pDragInfo;
pi->cDragItems = cRecs;
}
else
Msg( "DrgAllocDraginfo failed. RC(%X)", HWNDERR( hwndFrame ) );
}
else
Msg( "Out of memory in dragInit" );
}
if( cRecs && pDragInfo && pDragImage )
{
// Set the data from the container records into the DRAGITEM and
// DRAGIMAGE structures. If we are to process CRA_SELECTED container
// records, do them all in one function. If not, pass a pointer to the
// container record under the mouse to a different function that will
// fill in just one DRAGITEM / DRAGIMAGE structure.
if( fUseSelectedRecs )
SetSelectedDragItems( hwndFrame, cRecs, pDragInfo, pDragImage );
else
SetOneDragItem( hwndFrame, pCnrRecUnderMouse, pDragInfo,
pDragImage, 0 );
// If DrgDrag returns NULLHANDLE, that means the user hit Esc or F1
// while the drag was going on so the target didn't have a chance to
// delete the string handles. So it is up to the source window to do
// it. Unfortunately there doesn't seem to be a way to determine
// whether the NULLHANDLE means Esc was pressed as opposed to there
// being an error in the drag operation. So we don't attempt to figure
// that out. To us, a NULLHANDLE means Esc was pressed...
if( !DrgDrag( hwndFrame, pDragInfo, pDragImage, cRecs, VK_ENDDRAG,
NULL ) )
{
if( !DrgDeleteDraginfoStrHandles( pDragInfo ) )
Msg( "dragInit DrgDeleteDraginfoStrHandles RC(%X)",
HWNDERR( hwndFrame ) );
if( !DrgFreeDraginfo( pDragInfo ) )
Msg( "dragInit DrgFreeDraginfo RC(%X)", HWNDERR( hwndFrame ) );
pi->pSavedDragInfo = NULL;
}
// Take off source emphasis from the records that were dragged
RemoveSourceEmphasis( hwndFrame );
}
if( pDragImage )
free( pDragImage );
}
/**********************************************************************/
/*----------------------------- dragOver -----------------------------*/
/* */
/* PROCESS CN_DRAGOVER NOTIFY MESSAGE. */
/* */
/* PARMS: frame window handle, */
/* pointer to the CNRDRAGINFO structure */
/* */
/* NOTES: The container sends the CN_DRAGOVER message to its owner */
/* when it gets a DM_DRAGOVER message. The container takes */
/* the info it gets on the DM_DRAGOVER message and combines */
/* it with other container-specific dragover info into a */
/* CNRDRAGINFO structure, then passes a pointer to that */
/* structure to its owner in the CN_DRAGOVER message. */
/* */
/* In this program, all we care about is that the other */
/* window can handle the DRM_FISHMAN protocol and that the */
/* window is not trying to drop on itself. Strangely enough, */
/* the DRM_FISHMAN protocol is not in widespread use <g>. */
/* */
/* RETURNS: return value from CN_DRAGOVER processing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
MRESULT dragOver( HWND hwndFrame, PCNRDRAGINFO pcdi )
{
USHORT usDrop, usDefaultOp;
PDRAGINFO pDragInfo = pcdi->pDragInfo;
PDRAGITEM pDragItem;
if( !DrgAccessDraginfo( pDragInfo ) )
{
Msg( "dragOver DrgAccessDraginfo RC(%X)", HWNDERR( hwndFrame ) );
return MRFROM2SHORT( DOR_NEVERDROP, 0 );
}
// Don't allow a window to drop on itself
if( pDragInfo->hwndSource == hwndFrame )
return MRFROM2SHORT( DOR_NEVERDROP, 0 );
// We're only concerned with the first item being dragged. We "assume" that
// if the first item is using the DRM_FISHMAN protocol then all others are.
// In a real-world program you would want to check all dragitems.
pDragItem = DrgQueryDragitemPtr( pDragInfo, 0 );
if( pDragItem )
{
// We will only allow DRM_FISHMAN items to be dropped on us
if( DrgVerifyRMF( pDragItem, "DRM_FISHMAN", NULL ) )
{
usDrop = DOR_DROP;
// We only allow a logical 'copy' operation to take place, so this
// will change the pointer to be a 'copy' (halftoned) pointer
// during the drag when the pointer is over our window.
usDefaultOp = DO_COPY;
}
else
{
usDrop = DOR_NEVERDROP;
usDefaultOp = 0;
}
}
else
Msg( "dragOver DrgQueryDragitemPtr RC(%X)", HWNDERR( hwndFrame ) );
// Free our handle to the shared memory if the source window is not in our
// process.
if( !DrgFreeDraginfo( pDragInfo ) &&
PMERR_SOURCE_SAME_AS_TARGET != HWNDERR( hwndFrame ) )
Msg( "dragOver DrgFreeDraginfo RC(%X)", HWNDERR( hwndFrame ) );
return MRFROM2SHORT( usDrop, usDefaultOp );
}
/**********************************************************************/
/*---------------------------- dragDrop ------------------------------*/
/* */
/* PROCESS CN_DROP NOTIFY MESSAGE. */
/* */
/* PARMS: frame window handle, */
/* pointer to the CNRDRAGINFO structure */
/* */
/* NOTES: The container sends the CN_DROP message to its owner when */
/* it gets a DM_DROP message. The container takes the info it */
/* gets on the DM_DROP message and combines it with other */
/* container-specific dragover info into a CNRDRAGINFO */
/* structure, then passes a pointer to that structure to its */
/* owner in the CN_DROP message. */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void dragDrop( HWND hwndFrame, PCNRDRAGINFO pcdi )
{
PDRAGINFO pDragInfo = pcdi->pDragInfo;
PINSTANCE pi = INSTDATA( hwndFrame );
PDRAGITEM pDragItem;
PDRAGTRANSFER pDragXfer;
int i, cItemsToRender, cItemsRendered;
if( !pi )
{
Msg( "dragDrop cant get Inst data RC(%X)", HWNDERR( hwndFrame ) );
return;
}
// Save this info so it can be used on the last DM_RENDERCOMPLETE message.
pi->pSavedDragInfo = pDragInfo;
pi->cDragItems = pDragInfo->cditem;
if( !DrgAccessDraginfo( pDragInfo ) )
{
Msg( "dragDrop DrgAccessDraginfo RC(%X)", HWNDERR( hwndFrame ) );
return;
}
cItemsToRender = cItemsRendered = pDragInfo->cditem;
// We allocate an array of DRAGTRANSFER structures. One by one each will
// be passed on a DM_RENDER message. On DM_RENDERCOMPLETE messages we will
// free each one. PM takes care of freeing the whole array after it gets
// back as many DrgFreeDragTransfer's as were allocated here.
pDragXfer = DrgAllocDragtransfer( pDragInfo->cditem );
if( !pDragXfer )
{
Msg( "dragDrop DrgAllocDragtransfer RC(%X)", HWNDERR( hwndFrame ) );
return;
}
// Process each DragItem. First get a pointer to it, then call a function
// that starts the rendering happening.
for( i = 0; i < pDragInfo->cditem; i++ )
{
pDragItem = DrgQueryDragitemPtr( pDragInfo, i );
if( pDragItem )
ProcessDroppedItem( hwndFrame, pDragInfo, pDragItem,pDragXfer + i );
else
{
Msg( "dragDrop DrgQueryDragitemPtr RC(%X)", HWNDERR( hwndFrame ) );
--cItemsRendered;
}
}
if( !cItemsRendered )
{
// Clean up after ourselves if we weren't able to do
// any rendering. Normally this cleanup would be done
// under the last DM_RENDERCOMPLETE message but we don't
// get those messages if we aren't rendering.
TargetCleanup( hwndFrame );
}
else if( cItemsToRender != cItemsRendered )
{
// Only *some* items weren't rendered. If the rendering for all items
// that got sent DM_RENDER messages is complete, the target resources
// wouldn't have been freed because the counter wouldn't have been zero,
// so we need to free them in this case.
if( pi->cDragItems )
pi->cDragItems -= (cItemsToRender - cItemsRendered);
else
TargetCleanup( hwndFrame );
}
}
/**********************************************************************/
/*------------------------ CountSelectedRecs -------------------------*/
/* */
/* COUNT THE NUMBER OF RECORDS THAT ARE CURRENTLY SELECTED. */
/* */
/* PARMS: frame window handle, */
/* pointer to the record that was under the pointer, */
/* address of BOOL - should we process selected records? */
/* */
/* NOTES: */
/* */
/* RETURNS: number of records to process */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
int CountSelectedRecs( HWND hwndFrame, PCNRREC pCnrRecUnderMouse,
PBOOL pfUseSelectedRecs )
{
int cRecs = 0;
*pfUseSelectedRecs = FALSE;
// If the record under the mouse is NULL, we must be over whitespace, in
// which case we don't want to drag any records.
if( pCnrRecUnderMouse )
{
PCNRREC pCnrRec = (PCNRREC) CMA_FIRST;
// Count the records with 'selection' emphasis. These are the records
// we want to drag, unless the container record under the mouse does
// not have selection emphasis. If that is the case, we only want to
// process that one.
while( pCnrRec )
{
pCnrRec = (PCNRREC) WinSendDlgItemMsg( hwndFrame, FID_CLIENT,
CM_QUERYRECORDEMPHASIS,
MPFROMP( pCnrRec ),
MPFROMSHORT( CRA_SELECTED ));
if( pCnrRec == (PCNRREC) -1 )
Msg( "CountSelectedRecs..CM_QUERYRECORDEMPHASIS RC(%X)",
HWNDERR( hwndFrame ) );
else if( pCnrRec )
{
if( pCnrRec == pCnrRecUnderMouse )
*pfUseSelectedRecs = TRUE;
cRecs++;
}
}
if( !(*pfUseSelectedRecs) )
cRecs = 1;
}
return cRecs;
}
/**********************************************************************/
/*----------------------- SetSelectedDragItems -----------------------*/
/* */
/* FILL THE DRAGINFO STRUCT WITH DRAGITEM STRUCTS FOR SELECTED RECS. */
/* */
/* PARMS: frame window handle, */
/* count of selected records, */
/* pointer to allocated DRAGINFO struct, */
/* pointer to allocated DRAGIMAGE array */
/* */
/* NOTES: */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void SetSelectedDragItems( HWND hwndFrame, int cRecs, PDRAGINFO pDragInfo,
PDRAGIMAGE pDragImage )
{
PCNRREC pCnrRec = (PCNRREC) CMA_FIRST;
int i;
for( i = 0; i < cRecs; i++, pDragImage++ )
{
pCnrRec = (PCNRREC) WinSendDlgItemMsg( hwndFrame, FID_CLIENT,
CM_QUERYRECORDEMPHASIS,
MPFROMP( pCnrRec ),
MPFROMSHORT( CRA_SELECTED ) );
if( pCnrRec == (PCNRREC) -1 )
Msg( "SetSelectedDragItems..CM_QUERYRECORDEMPHASIS RC(%X)",
HWNDERR( hwndFrame ) );
else
SetOneDragItem( hwndFrame, pCnrRec, pDragInfo, pDragImage, i );
}
}
/**********************************************************************/
/*-------------------------- SetOneDragItem --------------------------*/
/* */
/* SET ONE DRAGITEM STRUCT INTO A DRAGINFO STRUCT. */
/* */
/* PARMS: frame window handle, */
/* pointer to CNRREC that contains current container record, */
/* pointer to allocated DRAGINFO struct, */
/* pointer to allocated DRAGIMAGE array, */
/* record offset into DRAGINFO struct to place DRAGITEM */
/* */
/* NOTES: Fill in a DRAGITEM struct and 'set' it into the DRAGINFO */
/* shared memory. Also fill in a DRAGIMAGE structure so PM */
/* knows what image to use in representing this item. */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void SetOneDragItem( HWND hwndFrame, PCNRREC pCnrRec, PDRAGINFO pDragInfo,
PDRAGIMAGE pDragImage, int iOffset )
{
DRAGITEM DragItem;
PCH pchColon = NULL;
memset( &DragItem, 0, sizeof DragItem );
// hwndDragItem is the window that will get the DM_RENDER and
// DM_RENDERCOMPLETE messages.
DragItem.hwndItem = hwndFrame;
// ulItemID is used to store information that can be used at drop time. Here
// we store the container record pointer.
DragItem.ulItemID = (ULONG) pCnrRec;
// hstrType identifies 'types' when it is necessary to differentiate. A
// good example is if you are dragging file names (DRM_OS2FILE) and need to
// pass the file type to the target (i.e. DRT_BITMAP would mean the file
// contained a bitmap, DRT_TEXT is an ascii file, etc. )
DragItem.hstrType = DrgAddStrHandle( DRT_UNKNOWN );
// This is the rendering mechanism/format. This establishes the protocol
// that we will be using during the Drag/Drop operation. In this program we
// only use DRM_FISHMAN which means rendering will take place after the
// drop using a predefined and agreed-upon mechanism. Any target objects
// that OK this type of protocol must know how to handle themselves after
// the drop.
DragItem.hstrRMF = DrgAddStrHandle( DRAG_RMF );
// This will contain our 'database file' name. We have multiple 'database'
// files, each with their own set of 'tables'. This field identifies the
// 'database' name. The text for each container record has been set (in the
// InsertRecords function in drgrendr.c) to "database:table". So we
// temporarily set the colon to a null-terminator for the database name so
// we can create a stringhandle for the database name.
pchColon = strchr( pCnrRec->szTableName, ':' );
if( pchColon )
*pchColon = 0;
else // This should never happen! Play it safe though.
pchColon = pCnrRec->szTableName;
DragItem.hstrContainerName = DrgAddStrHandle( pCnrRec->szTableName );
// This will contain the table name (minus the database name)
DragItem.hstrSourceName = DrgAddStrHandle( pchColon + 1 );
// Replace the colon that we overwrote with a 0 earlier
if( pchColon != pCnrRec->szTableName )
*pchColon = ':';
// Suggested target name is the same as the source name. We *could* ask the
// target to call it something else but we don't need that functionality
// in this program.
DragItem.hstrTargetName = DragItem.hstrSourceName;
// Allow the user to only 'copy' this item. 'Move doesn't make sense since
// the records will always stay in the source container.
DragItem.fsSupportedOps = DO_COPYABLE;
// Set the DRAGITEM struct into the memory allocated by
// DrgAllocDraginfo()
DrgSetDragitem( pDragInfo, &DragItem, sizeof DragItem, iOffset );
// Fill in the DRAGIMAGE structure
pDragImage->cb = sizeof( DRAGIMAGE );
pDragImage->hImage = pCnrRec->mrc.hptrIcon; // DragImage under mouse
pDragImage->fl = DRG_ICON; // hImage is an HPOINTER
pDragImage->cxOffset = 5 * iOffset; // Image offset from mouse ptr
pDragImage->cyOffset = 5 * iOffset; // Image offset from mouse ptr
// Set source emphasis for this container record
if( !WinSendDlgItemMsg( hwndFrame, FID_CLIENT, CM_SETRECORDEMPHASIS,
MPFROMP( pCnrRec ), MPFROM2SHORT( TRUE, CRA_SOURCE ) ) )
Msg( "SetOneDragItem..CM_SETRECORDEMPHASIS RC(%X)",
HWNDERR( hwndFrame ) );
}
/**********************************************************************/
/*------------------------ ProcessDroppedItem ------------------------*/
/* */
/* PROCESS A DRAGITEM THAT HAS BEEN DROPPED ON US */
/* */
/* PARMS: frame window handle, */
/* pointer to the DRAGINFO structure, */
/* pointer to DRAGITEM structure, */
/* pointer to DRAGTRANSFER structure to use on DM_RENDER msg */
/* */
/* NOTES: */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void ProcessDroppedItem( HWND hwndFrame, PDRAGINFO pDragInfo,
PDRAGITEM pDragItem, PDRAGTRANSFER pDragXfer )
{
PSZ pszString, pszLoc;
ULONG cbString;
MRESULT mr;
char szTempFileName[ CCHMAXPATH ];
// We will ask the source to 'render' into a temporary file. This next
// function gets a temporary file name to use.
FindTempFile( szTempFileName );
// Form a 'database:table' string by combining the database found in
// hstrContainerName with the table found in hstrSourceName.
cbString = DrgQueryStrNameLen( pDragItem->hstrContainerName ) +
DrgQueryStrNameLen( pDragItem->hstrSourceName ) +
2; // Null terminator plus colon
pszString = pszLoc = _alloca( cbString );
DrgQueryStrName( pDragItem->hstrContainerName, cbString, pszLoc );
strcat( pszString, ":" );
pszLoc += strlen( pszString );
cbString -= strlen( pszString );
DrgQueryStrName( pDragItem->hstrSourceName, cbString, pszLoc );
// Fill in the DRAGTRANSFER structure. This structure is used throughout
// the rendering process and is exchanged between the source and the
// target.
memset( pDragXfer, 0, sizeof( DRAGTRANSFER ) );
pDragXfer->cb = sizeof( DRAGTRANSFER );
// hwndClient identifies the target window
pDragXfer->hwndClient = hwndFrame;
pDragXfer->pditem = pDragItem;
// hstrSelectedRMF is the Rendering Mechanism/Format used during the
// rendering. Remember that the hstrRMF field of the DRAGITEM structure
// can contain multiple mechanisms and formats. When we get down to the
// rendering stage we can use only one. In this program we only ever
// offered one so we use the same one that was placed in DRAGITEM.hstrRMF.
// In the real world we would first put the most native mechanism/format
// combination into hstrSelectedRMF and send the source a DM_RENDER. If the
// source responded to the DM_RENDER by returning DMFL_RENDERRETRY, we would
// then use the 'next best' RMF and try again by sending another DM_RENDER
// with the new hstrSelectedRMF. We'd do this until one was agreed upon.
pDragXfer->hstrSelectedRMF = DrgAddStrHandle( DRAG_RMF );
// hstrRenderToName is the file name that we are asking the source to
// render into. In this program the source will copy the rows from the
// requested database:table into this file.
pDragXfer->hstrRenderToName = DrgAddStrHandle( szTempFileName );
// usOperation is where we'd indicate 'copy' or 'move'. In this program we
// will only do a logical copy operation so this field isn't even looked at.
// Typically this will stay the same as the usOperation field in the
// DRAGINFO field because that contains the last operation selected by the
// user.
pDragXfer->usOperation = pDragInfo->usOperation;
// ulTargetInfo is a convenient place to store something between render
// messages.
pDragXfer->ulTargetInfo = 0;
// Initialize this to 0. The source will place something in here if it
// returns FALSE from DM_RENDER.
pDragXfer->fsReply = 0;
// If the source wanted to be notified before a render is to be sent,
// do it. This program doesn't use this mechanism but the multi-threaded
// version of this program does.
if( pDragItem->fsControl & DC_PREPARE )
DrgSendTransferMsg( pDragItem->hwndItem, DM_RENDERPREPARE,
pDragXfer, NULL );
// Tell the source to render
mr = DrgSendTransferMsg( pDragItem->hwndItem, DM_RENDER, pDragXfer, NULL );
// MRESULT will be NULL if the source could not render
if( !mr )
{
// If the source wants us to retry with another mechanism, tell it
// 'no' because all we can handle is DRM_FISHMAN.
if( pDragXfer->fsReply & DMFL_RENDERRETRY )
DrgPostTransferMsg( pDragItem->hwndItem, DM_RENDERCOMPLETE,
pDragXfer, DMFL_RENDERFAIL, 0, FALSE );
// If the source wants us to render, tell it 'no way'. It is the one
// that requested rendering in the first place.
if( pDragXfer->fsReply & DMFL_NATIVERENDER )
DrgSendTransferMsg( pDragItem->hwndItem, DM_ENDCONVERSATION,
MPFROMLONG( pDragItem->ulItemID ),
MPFROMLONG( DMFL_TARGETFAIL ) );
}
}
/**********************************************************************/
/*-------------------------- FindTempFile ----------------------------*/
/* */
/* FIND AN AVAILABLE TEMPORARY FILE NAME. */
/* */
/* PARMS: pointer to buffer to hold filename */
/* */
/* NOTES: This is a rudimentary method for getting a temporary file */
/* name. Usually you'd want to check the TEMP or TMP */
/* environment variables first and use that as the directory */
/* in which to place the temporary files. For a sample */
/* program, this will do fine. Basically we just get use */
/* the current path and a base filename and figure out an */
/* extension by finding the first available one. */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void FindTempFile( PSZ pszTempFileName )
{
PSZ pszFileExt;
FILESTATUS3 fs;
int i;
APIRET rc;
FILE *fhTemp;
strcpy( pszTempFileName, szCurrentPath );
strcat( pszTempFileName, BASE_TEMPFILE_NAME );
strcat( pszTempFileName, "." );
pszFileExt = pszTempFileName + strlen( pszTempFileName );
for( i = 0; i < 1000; i++ )
{
_itoa( i, pszFileExt, 10 );
rc = DosQueryPathInfo( pszTempFileName, FIL_STANDARD, &fs, sizeof fs );
if( rc == ERROR_FILE_NOT_FOUND || rc == ERROR_PATH_NOT_FOUND )
{
// Create the file so it exists and we won't try and use it for the
// next temporary file.
fhTemp = fopen( pszTempFileName, "w" );
fclose( fhTemp );
break;
}
}
if( i >= 1000 )
Msg( "Can't get a temporary file name!!!" );
}
/**********************************************************************/
/*----------------------------- Render -------------------------------*/
/* */
/* PROCESS A DM_RENDER MESSAGE */
/* */
/* PARMS: frame window handle, */
/* pointer to a DRAGTRANSFER structure */
/* */
/* NOTES: This is a SOURCE window message */
/* */
/* RETURNS: MRESULT explaining the rendering availability */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
MRESULT Render( HWND hwndFrame, PDRAGTRANSFER pDragXfer )
{
hwndFrame=hwndFrame; // Keep the compiler happy
pDragXfer=pDragXfer;
// Normally here we would check some things and return TRUE if everything
// looked OK or we would return FALSE and set pDragXfer->fsReply to the
// reason for the FALSE. A typical reason to return FALSE would be if
// pDragXfer->hstrSelectedRMF was something we couldn't support or
// pDragXfer->usOperation was something we didn't want to do. In that case
// we'd return FALSE and set fsReply to DMFL_RENDERRETRY. It would then be
// up to the target to change its parameters and send us another DM_RENDER.
return (MRESULT) TRUE;
}
/**********************************************************************/
/*------------------------- DoTheRendering ---------------------------*/
/* */
/* DO THE RENDERING. */
/* */
/* PARMS: pointer to a DRAGTRANSFER structure */
/* */
/* NOTES: This is a SOURCE window message */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void DoTheRendering( PDRAGTRANSFER pDragXfer )
{
PSZ pszTableName, pszFileName, pszLoc;
ULONG cbString, flStatus = DMFL_RENDEROK;
// Form the 'database:table' string by combining the database name which
// is stored in hstrContainerName with the table name which is stored in
// hstrSourceName.
cbString = DrgQueryStrNameLen( pDragXfer->pditem->hstrContainerName ) +
DrgQueryStrNameLen( pDragXfer->pditem->hstrSourceName ) +
2; // Null terminator plus colon
pszTableName = pszLoc = _alloca( cbString );
DrgQueryStrName( pDragXfer->pditem->hstrContainerName, cbString, pszLoc );
strcat( pszTableName, ":" );
pszLoc += strlen( pszTableName );
cbString -= strlen( pszTableName );
DrgQueryStrName( pDragXfer->pditem->hstrSourceName, cbString, pszLoc );
// Get the file name that we (the source) should be rendering to. This
// name was decided by the target before it sent us the DM_RENDER message.
cbString = DrgQueryStrNameLen( pDragXfer->hstrRenderToName ) + 1;
pszFileName = _alloca( cbString );
DrgQueryStrName( pDragXfer->hstrRenderToName, cbString, pszFileName );
// Call this function which will copy the database data into the file.
if( !dbRenderToFile( pszTableName, pszFileName ) )
flStatus = DMFL_RENDERFAIL;
// Tell the target that we're done.
DrgPostTransferMsg( pDragXfer->hwndClient, DM_RENDERCOMPLETE, pDragXfer,
flStatus, 0, FALSE );
// Bot the target and the source must free this. Since we're done at the
// source, it is time to free it.
DrgFreeDragtransfer( pDragXfer );
}
/**********************************************************************/
/*------------------------- RenderComplete ---------------------------*/
/* */
/* PROCESS A DM_RENDERCOMPLETE MESSAGE */
/* */
/* PARMS: frame window handle, */
/* pointer to a DRAGTRANSFER structure, */
/* flags describing the render operation */
/* */
/* NOTES: This is a TARGET window message sent by the source when it */
/* has completed the rendering. */
/* */
/* RETURNS: zero */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
MRESULT RenderComplete( HWND hwndFrame, PDRAGTRANSFER pDragXfer,
USHORT fsRender )
{
BOOL fSuccess = TRUE;
ULONG flStatus = DMFL_TARGETSUCCESSFUL;
PINSTANCE pi = INSTDATA( hwndFrame );
// If the source responded with RENDEROK, that means we can count on the
// hstrRenderToFile file containing the data from the table in the database.
// In that case we insert a record into the 'drop' container that will
// represent that rendered file. The user can double-click on that icon and
// bring up a listbox with the file dumped into it.
if( fsRender & DMFL_RENDEROK )
{
PSZ pszLoc;
RECORDINSERT ri;
PCNRREC pCnrRec;
memset( &ri, 0, sizeof( RECORDINSERT ) );
ri.cb = sizeof( RECORDINSERT );
ri.pRecordOrder = (PRECORDCORE) CMA_END;
ri.pRecordParent = (PRECORDCORE) NULL;
ri.zOrder = (USHORT) CMA_TOP;
ri.cRecordsInsert = 1;
ri.fInvalidateRecord = TRUE;
pCnrRec = WinSendDlgItemMsg( hwndFrame, FID_CLIENT, CM_ALLOCRECORD,
MPFROMLONG( EXTRA_BYTES ),
MPFROMLONG( 1 ) );
if( pCnrRec )
{
DrgQueryStrName( pDragXfer->pditem->hstrContainerName,
sizeof( pCnrRec->szTableName ), pCnrRec->szTableName );
strcat( pCnrRec->szTableName, ":" );
pszLoc = pCnrRec->szTableName + strlen( pCnrRec->szTableName );
DrgQueryStrName( pDragXfer->pditem->hstrSourceName,
sizeof( pCnrRec->szTableName ) -
(pszLoc - pCnrRec->szTableName), pszLoc );
DrgQueryStrName( pDragXfer->hstrRenderToName,
sizeof( pCnrRec->szRenderedFileName ),
pCnrRec->szRenderedFileName );
pCnrRec->flAttr = RECATTR_OPENABLE;
pCnrRec->mrc.pszIcon = (PSZ) &pCnrRec->szTableName;
pCnrRec->mrc.hptrIcon = hptrOpenMe;
if( !WinSendDlgItemMsg( hwndFrame, FID_CLIENT, CM_INSERTRECORD,
MPFROMP( pCnrRec ), MPFROMP( &ri ) ) )
{
fSuccess = FALSE;
Msg( "RenderComplete CM_INSERTRECORD RC(%X)",
HWNDERR( hwndFrame ) );
}
}
else
{
fSuccess = FALSE;
Msg( "RenderComplete CM_ALLOCRECORD RC(%X)", HWNDERR( hwndFrame ) );
}
}
else
fSuccess = FALSE;
if( fSuccess )
{
CNRINFO cnri;
cnri.cb = sizeof( CNRINFO );
cnri.pszCnrTitle = szDroppedOnCnrTitle;
WinSendDlgItemMsg( hwndFrame, FID_CLIENT, CM_SETCNRINFO,
MPFROMP( &cnri ), MPFROMLONG( CMA_CNRTITLE ) );
}
else
{
// Delete the temporary file if the render failed.
char szRenderToName[ CCHMAXPATH ];
DrgQueryStrName( pDragXfer->hstrRenderToName, sizeof szRenderToName,
szRenderToName );
DosDelete( szRenderToName );
flStatus = DMFL_TARGETFAIL;
}
// Tell the source that we're all done here. At this point the Drag/Drop
// is finally done.
DrgSendTransferMsg( pDragXfer->pditem->hwndItem, DM_ENDCONVERSATION,
MPFROMLONG( pDragXfer->pditem->ulItemID ),
MPFROMLONG( flStatus ) );
// It is the target's responsibility to delete the string handles.
DrgDeleteStrHandle( pDragXfer->hstrSelectedRMF );
DrgDeleteStrHandle( pDragXfer->hstrRenderToName );
// Both the source and target must free the DRAGTRANSFER structure. The
// source will have done this before it sent us the DM_RENDERCOMPLETE
// message.
DrgFreeDragtransfer( pDragXfer );
// We need to keep a running total to know when all items in the drop have
// been processed. When that happens, it is time to free the resources that
// were allocated to the drop as a whole rather than to an indidvidual item.
if( --pi->cDragItems == 0 )
TargetCleanup( hwndFrame );
return 0;
}
/**********************************************************************/
/*-------------------------- EndConversation -------------------------*/
/* */
/* FREE THE RESOURCES USED BY DRAG/DROP PROCESSING. ONLY DO THIS IF */
/* THIS IS THE LAST ITEM (there is one end-conversation message sent */
/* to us for each item dropped). */
/* */
/* PARMS: frame window handle */
/* */
/* NOTES: This is a SOURCE window message that the target sends after*/
/* it is done processing the DM_RENDERCOMPLETE message that */
/* the source sent it. */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
MRESULT EndConversation( HWND hwndFrame )
{
PINSTANCE pi = INSTDATA( hwndFrame );
if( !pi )
{
Msg( "EndConversation cant get Inst data RC(%X)", HWNDERR(hwndFrame) );
return 0;
}
// We need to keep a running total to know when all items in the drop have
// been processed. When that happens, it is time to free the resources that
// were allocated to the drag as a whole rather than to an indidvidual item.
if( --pi->cDragItems == 0 )
SourceCleanup( hwndFrame );
return 0;
}
/**********************************************************************/
/*----------------------- RemoveSourceEmphasis -----------------------*/
/* */
/* REMOVE SOURCE EMPHASIS FROM THE DRAGGED RECORDS. */
/* */
/* PARMS: frame window handle */
/* */
/* NOTES: */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void RemoveSourceEmphasis( HWND hwndFrame )
{
PCNRREC pCnrRec = (PCNRREC) CMA_FIRST;
// For every record with source emphasis, remove it.
while( pCnrRec )
{
pCnrRec = (PCNRREC) WinSendDlgItemMsg( hwndFrame, FID_CLIENT,
CM_QUERYRECORDEMPHASIS,
MPFROMP( pCnrRec ),
MPFROMSHORT( CRA_SOURCE ) );
if( pCnrRec == (PCNRREC) -1 )
Msg( "RemoveSourceEmphasis..CM_QUERYRECORDEMPHASIS RC(%X)",
HWNDERR( hwndFrame ) );
else if( pCnrRec )
if( !WinSendDlgItemMsg( hwndFrame, FID_CLIENT,
CM_SETRECORDEMPHASIS, MPFROMP( pCnrRec ),
MPFROM2SHORT( FALSE, CRA_SOURCE ) ) )
Msg( "RemoveSourceEmphasis..CM_SETRECORDEMPHASIS RC(%X)",
HWNDERR( hwndFrame ) );
}
}
/**********************************************************************/
/*--------------------------- TargetCleanup --------------------------*/
/* */
/* FREE THE RESOURCES USED BY DRAG/DROP PROCESSING FOR THE TARGET */
/* */
/* PARMS: frame window handle */
/* */
/* NOTES: */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void TargetCleanup( HWND hwndFrame )
{
PINSTANCE pi = INSTDATA( hwndFrame );
if( !pi )
{
Msg( "TargetCleanup cant get Inst data RC(%X)", HWNDERR(hwndFrame) );
return;
}
// It is the target's responsibility to delete the string resources. This
// one API frees all the strings in all DRAGITEM structures.
if( !DrgDeleteDraginfoStrHandles( pi->pSavedDragInfo ) )
Msg( "TargetCleanup DrgDeleteDraginfoStrHandles RC(%X)",
HWNDERR( hwndFrame ) );
// Both the source and target must free the DRAGINFO structure.
if( !DrgFreeDraginfo( pi->pSavedDragInfo ) &&
PMERR_SOURCE_SAME_AS_TARGET != HWNDERR( hwndFrame ) )
Msg( "TargetCleanup DrgFreeDraginfo RC(%X)", HWNDERR( hwndFrame ) );
pi->pSavedDragInfo = NULL;
pi->cDragItems = 0;
}
/**********************************************************************/
/*--------------------------- SourceCleanup --------------------------*/
/* */
/* FREE THE RESOURCES USED BY DRAG/DROP PROCESSING FOR THE SOURCE */
/* */
/* PARMS: frame window handle */
/* */
/* NOTES: */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void SourceCleanup( HWND hwndFrame )
{
PINSTANCE pi = INSTDATA( hwndFrame );
if( !pi )
{
Msg( "SourceCleanup cant get Inst data RC(%X)", HWNDERR(hwndFrame) );
return;
}
// Free the shared memory we got access to using DrgAccessDragInfo. If
// the source process is the same as the target process, we get the
// PMERR_SOURCE_SAME_AS_TARGET message. It's ok to get that - it just
// means that we don't need to free the structure because the target
// process already freed it.
if( !DrgFreeDraginfo( pi->pSavedDragInfo ) &&
PMERR_SOURCE_SAME_AS_TARGET != HWNDERR( hwndFrame ) )
Msg( "SourceCleanup DrgFreeDraginfo RC(%X)", HWNDERR( hwndFrame ) );
// This is important because the NULL-ness of pSavedDragInfo lets the
// dragInit function know that another drag is now possible.
pi->pSavedDragInfo = NULL;
pi->cDragItems = 0;
}
/*************************************************************************
* E N D O F S O U R C E *
*************************************************************************/