home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
drgthr.zip
/
trgrendr.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-28
|
31KB
|
701 lines
/*********************************************************************
* *
* MODULE NAME : drag.c AUTHOR: Rick Fishman *
* DATE WRITTEN: 07-16-93 *
* *
* MODULE DESCRIPTION: *
* *
* Part of the 'DRGTHRND' drag/drop sample program. *
* *
* This module handles all the rendering for the target window. *
* *
* NOTES: *
* *
* FUNCTIONS AVALABLE TO OTHER MODULES: *
* *
* targCreateWindow *
* *
* *
* 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_WINDIALOGS
#define INCL_WINERRORS
#define INCL_WINFRAMEMGR
#define INCL_WINMENUS
#define INCL_WINSTDCNR
#define INCL_WINSTDDRAG
#define INCL_WINWINDOWMGR
/**********************************************************************/
/*----------------------------- INCLUDES -----------------------------*/
/**********************************************************************/
#include <os2.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "drgthrnd.h"
/*********************************************************************/
/*------------------- APPLICATION DEFINITIONS -----------------------*/
/*********************************************************************/
#define STACKSIZE 0xE000 // Stacksize for secondary threads
/**********************************************************************/
/*---------------------------- STRUCTURES ----------------------------*/
/**********************************************************************/
typedef struct _WINSTARTUP
{
ULONG cb;
HWND hwndFrame;
} WINSTARTUP, *PWINSTARTUP;
/**********************************************************************/
/*----------------------- FUNCTION PROTOTYPES ------------------------*/
/**********************************************************************/
void TargetThread ( void *pvHwndFrame );
void DoTheDrop ( HWND hwndRender, HWND hwndFrame,
PDRAGINFO pDragInfo );
BOOL ProcessDroppedItem( HWND hwndRender, PDRAGINFO pDragInfo,
PDRAGITEM pDragItem, PDRAGTRANSFER pDragXfer );
void FindTempFile ( PSZ szTempFileName );
MRESULT RenderComplete ( HWND hwndFrame, PDRAGTRANSFER pDragXfer,
USHORT fsRender );
FNWP wpTarget;
/**********************************************************************/
/*------------------------ GLOBAL VARIABLES --------------------------*/
/**********************************************************************/
char szDroppedOnCnrTitle[] = "Open a table by double-clicking on it!";
/**********************************************************************/
/*------------------------ targCreateWindow --------------------------*/
/* */
/* CREATE A WINDOW IN ANOTHER THREAD THAT WILL PROCESS THE RENDERING */
/* FOR THE TARGET. */
/* */
/* PARMS: frame window handle */
/* */
/* NOTES: */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
HWND targCreateWindow( HWND hwndFrame )
{
HWND hwndRender = NULLHANDLE;
TID tid;
// Start the thread. Then wait for the thread's window to be created, at
// which point it will post a UM_WINDOW_CREATED message to this thread.
// We take over the message queue not just to catch this message but so
// that the message queue will not be tied up while this message is being
// created. It does act as a cheap semaphore mechanism though...
tid = _beginthread( TargetThread, NULL, STACKSIZE, (void *) hwndFrame );
if( (int) tid != -1 )
{
HAB hab = ANCHOR( hwndFrame );
QMSG qmsg;
while( WinGetMsg( hab, &qmsg, NULLHANDLE, 0, 0 ) )
if( qmsg.msg == UM_WINDOW_CREATED )
{
hwndRender = (HWND) qmsg.mp1;
break;
}
else
WinDispatchMsg( hab, &qmsg );
}
else
Msg( "_beginthread for target window failed!" );
return hwndRender;
}
/**********************************************************************/
/*-------------------------- TargetThread ----------------------------*/
/* */
/* THREAD THAT HANDLES THE RENDERING ON THE TARGET WINDOW SIDE. */
/* */
/* PARMS: frame window handle (this is passes as a void pointer to */
/* satisfy the requirements of _beginthread()) */
/* */
/* NOTES: The UM_WINDOW_CREATED message is our way of letting the */
/* creator of this thread know the window handle of the render*/
/* object window. It is our way of 'returning' that window */
/* handle. We place it in mp1 of that message. If anything */
/* happens to hurt the creation of that window we will pass */
/* back a NULL in mp1. */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
#define TARGET_CLASS "ThisIsTheTargetClass"
void TargetThread( void *pvHwndFrame )
{
HAB hab = WinInitialize( 0 );
HMQ hmq;
HWND hwndFrame = (HWND) pvHwndFrame;
if( hab )
hmq = WinCreateMsgQueue( hab, 0 );
else
{
WinPostMsg( hwndFrame, UM_WINDOW_CREATED, NULL, NULL );
DosBeep( 1000, 100 );
fprintf( stderr, "WinInitialize failed!" );
}
if( hmq )
{
WINSTARTUP WinStartup;
HWND hwndObj;
// Pass the frame window handle to the render window - it will need that
// handle to get at the frame window's window word. Remember that when
// passing a pointer to a structure via the pCtlData parameter, the
// first USHORT must be the size of the structure.
WinStartup.cb = sizeof WinStartup;
WinStartup.hwndFrame = hwndFrame;
WinRegisterClass( hab, TARGET_CLASS, wpTarget, 0, sizeof( void * ) );
hwndObj = WinCreateWindow( HWND_OBJECT, TARGET_CLASS, NULL, 0, 0, 0, 0,
0, NULLHANDLE, HWND_TOP, 1, &WinStartup,
NULL );
if( hwndObj )
{
QMSG qmsg;
WinPostMsg( hwndFrame, UM_WINDOW_CREATED, MPFROMHWND( hwndObj ),
NULL );
while( WinGetMsg( hab, &qmsg, NULLHANDLE, 0, 0 ) )
WinDispatchMsg( hab, &qmsg );
WinDestroyWindow( hwndObj );
}
else
{
WinPostMsg( hwndFrame, UM_WINDOW_CREATED, NULL, NULL );
Msg( "WinCreateWindow( hwndObj ) RC(%X)", HABERR( hab ) );
}
}
else if( hab )
{
WinPostMsg( hwndFrame, UM_WINDOW_CREATED, NULL, NULL );
Msg( "WinCreateMsgQueue RC(%X)", HABERR( hab ) );
}
if( hmq )
WinDestroyMsgQueue( hmq );
if( hab )
WinTerminate( hab );
_endthread();
}
/**********************************************************************/
/*---------------------------- wpTarget ------------------------------*/
/* */
/* WINDOW PROCEDURE FOR THE TARGET RENDERING OBJECT WINDOW. */
/* */
/* PARMS: standard window procedure parameters */
/* */
/* NOTES: */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
MRESULT EXPENTRY wpTarget( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
{
HWND hwndFrame = (HWND) WinQueryWindowULong( hwnd, 0 );
switch( msg )
{
case WM_CREATE:
{
WinSetWindowULong( hwnd, 0, ((PWINSTARTUP) mp1)->hwndFrame );
break;
}
// The main thread sends this to use after it knows we are created. It
// is our signal to start the drop processing.
case UM_DO_THE_DROP:
DoTheDrop( hwnd, hwndFrame, (PDRAGINFO) mp1 );
return 0;
// The source posts this to us when it is done rendering.
case DM_RENDERCOMPLETE:
return RenderComplete( hwndFrame, (PDRAGTRANSFER) mp1,
SHORT1FROMMP( mp2 ) );
}
return WinDefWindowProc( hwnd, msg, mp1, mp2 );
}
/**********************************************************************/
/*---------------------------- DoTheDrop -----------------------------*/
/* */
/* DO WHAT IS NECESSARY TO HANDLE THE DROP. */
/* */
/* PARMS: rendering window's handle, */
/* frame window handle, */
/* pointer to the DRAGINFO structure */
/* */
/* NOTES: */
/* */
/* RETURNS: nothing */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
void DoTheDrop( HWND hwndRender, HWND hwndFrame, PDRAGINFO pDragInfo )
{
PDRAGITEM pDragItem;
PDRAGTRANSFER pDragXfer;
PINSTANCE pi = INSTDATA( hwndFrame );
int i, cItemsToRender, cItemsRendered;
if( !DrgAccessDraginfo( pDragInfo ) )
{
Msg( "DoTheDrop DrgAccessDraginfo RC(%X)", HWNDERR( hwndRender ) );
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( "DoTheDrop DrgAllocDragtransfer RC(%X)", HWNDERR( hwndRender ) );
return;
}
// Disable the Close option of the system menu while the rendering is
// going on. Otherwise the background threads could get hosed.
WinSendDlgItemMsg( hwndFrame, FID_SYSMENU, MM_SETITEMATTR,
MPFROM2SHORT( SC_CLOSE, TRUE ),
MPFROM2SHORT( MIA_DISABLED, MIA_DISABLED ) );
// 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 )
{
if( !ProcessDroppedItem( hwndRender, pDragInfo, pDragItem,
pDragXfer + i ) )
--cItemsRendered;
}
else
{
Msg( "DoTheDrop DrgQueryDragitemPtr RC(%X)", HWNDERR(hwndRender) );
--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.
dragTargetCleanup( hwndFrame );
}
else if( cItemsToRender != cItemsRendered )
{
// Only *some* items were'nt rendered. Since we don't want any race
// conditions with the cDragItems counter, disable thread switching
// while we check/change it. 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.
DosEnterCritSec();
if( pi->cDragItems )
{
pi->cDragItems -= (cItemsToRender - cItemsRendered);
DosExitCritSec();
}
else
{
DosExitCritSec();
dragTargetCleanup( hwndFrame );
}
}
}
/**********************************************************************/
/*------------------------ ProcessDroppedItem ------------------------*/
/* */
/* PROCESS A DRAGITEM THAT HAS BEEN DROPPED ON US */
/* */
/* PARMS: rendering window handle, */
/* pointer to the DRAGINFO structure, */
/* pointer to DRAGITEM structure, */
/* pointer to DRAGTRANSFER structure to use on DM_RENDER msg */
/* */
/* NOTES: */
/* */
/* RETURNS: TRUE or FALSE if item was sent a DM_RENDER message */
/* */
/*--------------------------------------------------------------------*/
/**********************************************************************/
BOOL ProcessDroppedItem( HWND hwndRender, PDRAGINFO pDragInfo,
PDRAGITEM pDragItem, PDRAGTRANSFER pDragXfer )
{
PSZ pszString, pszLoc;
ULONG cbString;
BOOL fRenderSent = TRUE;
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 = hwndRender;
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. The source in this program uses this message to create an object
// window in another thread to do the rendering. It then places the object
// window handle in pDragItem->hwndItem, thus overwriting what is currently
// there - the source frame window handle.
if( !(pDragItem->fsControl & DC_PREPARE) ||
(pDragItem->fsControl & DC_PREPARE &&
DrgSendTransferMsg( pDragItem->hwndItem, DM_RENDERPREPARE,
pDragXfer, NULL )) )
{
// Tell the source to render
MRESULT 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 ) );
}
}
else
{
// If we don't send a DM_RENDER message, we'll never get a
// DM_RENDERCOMPLETE. Since cleanup for these resources usually gets
// done under that message, we'll need to do it here in this case.
DrgDeleteStrHandle( pDragXfer->hstrSelectedRMF );
DrgDeleteStrHandle( pDragXfer->hstrRenderToName );
DrgFreeDragtransfer( pDragXfer );
fRenderSent = FALSE;
}
return fRenderSent;
}
/**********************************************************************/
/*-------------------------- 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!!!" );
}
/**********************************************************************/
/*------------------------- 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
{
char szRenderToName[ CCHMAXPATH ];
// Delete the temporary file if the render failed.
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 )
dragTargetCleanup( hwndFrame );
return 0;
}
/*************************************************************************
* E N D O F S O U R C E *
*************************************************************************/