
/* ================================================================

      File:  DVCLIP.C

   Purpose:  Contains routines related to cutting/pasting bitmaps to/from
             the clipboard.

 Functions:  CopyHandle
             CropBitmap
             RenderFormat
             HandleCopyClipboard
             HandlePasteClipboard

  Comments:  

   History:   Date      Reason
             6/ 1/91     Created

=================================================================== */
#include	"dv.h"		// Single inclusive include
//#include <windows.h>
//#include "DvChild.h"
//#include "DvClip.h"
//#include "DvDib.h"
//#include "DvErrors.h"
//#include "DvFrame.h"
//#include "DvPal.h"
//#include "DvMem.h"
#ifdef ADD_CLIP_RESIZING // FIX20051128 - add CLIP resizing
#include <windowsx.h>
#endif // #ifdef ADD_CLIP_RESIZING

#ifdef   ADD_STATUS_BAR
#include <commctrl.h>
extern HWND ghStatusWnd;
#endif // #ifdef   ADD_STATUS_BAR

// NEW
extern   PMWL	AddToFileList4( PRDIB prd );
extern   BOOL  WriteBMPFile2( LPTSTR lpf, HGLOBAL hDIB, BOOL bShow );
extern DWORD GetTextExtent( HDC hdc, LPSTR lpS, int len );

//RECT  grcClip     = {0,0,0,0};    // Clipboard rectangle at time of copy.
//DWORD		gnPasteNum = 1;      // For window title
TCHAR gszRPFrm[] = DEF_FM_STG;   // "TEMPB%03d.BMP" - when NO FILE, use this

RECT	   rcPrevClip = { 0 };
HCURSOR  hCurWE = 0;
HCURSOR  hCurNS = 0;
HCURSOR  hCurNESW = 0;  //   / for TOPRIGHT, and BOTTLEFT
HCURSOR  hCurNWSE = 0;  //   \ for TOPLEFT, and BOTTRIGHT
HCURSOR  hCurALL = 0;
HCURSOR  hCurArrow = 0;

typedef enum {
   DV_ON_NONE = 0,
   DV_ON_LEFT,
   DV_ON_RIGHT,
   DV_ON_TOP,
   DV_ON_BOTTOM,
   DV_ON_TOPLEFT,
   DV_ON_TOPRIGHT,
   DV_ON_BOTTLEFT,
   DV_ON_BOTTRIGHT,
   DV_ON_ALL
}DV_CURPOS;

DV_CURPOS Clippos = DV_ON_NONE;

BOOL  InitCursors ( VOID )
{
   hCurWE = LoadCursor( NULL, IDC_SIZEWE ); // = <=> type
   hCurNS = LoadCursor( NULL, IDC_SIZENS );
   hCurNESW = LoadCursor( NULL, IDC_SIZENESW ); //   / for TOPRIGHT, and BOTTLEFT
   hCurNWSE = LoadCursor( NULL, IDC_SIZENWSE ); //   \ for TOPLEFT, and BOTTRIGHT
   hCurALL = LoadCursor( NULL, IDC_SIZEALL );
   hCurArrow = LoadCursor( NULL, IDC_ARROW );
   Clippos = DV_ON_NONE;

   if( !hCurWE || !hCurNS || !hCurArrow || !hCurNESW || !hCurNWSE || !hCurALL ) {
		MessageBox( NULL,
			"RESOURCE LOAD ERROR",
			"ERROR: Unable to load the one or more\r\n"
         "of the standard system cursors\r\n"
			"On OK, will abort application!",
			( MB_ICONINFORMATION | MB_OK ) );
      return FALSE;
   }

   return TRUE;
}

VOID  SetCursorType( DV_CURPOS cp )
{
   switch( cp )
   {
   case DV_ON_LEFT:
   case DV_ON_RIGHT:
      SetCursor( hCurWE );
      break;
   case DV_ON_TOP:
   case DV_ON_BOTTOM:
      SetCursor( hCurNS );
      break;
   case DV_ON_TOPLEFT:
   case DV_ON_BOTTRIGHT:
      SetCursor( hCurNWSE );
      break;
   case DV_ON_TOPRIGHT:
   case DV_ON_BOTTLEFT:
      SetCursor( hCurNESW );
      break;
   case DV_ON_ALL:
      SetCursor( hCurALL );
      break;
   default:
      SetCursor( hCurArrow );
      break;
   }
}

PTSTR GetClipPosStg( DV_CURPOS cp )
{
   PTSTR ps = "";
   switch( cp )
   {
   case DV_ON_LEFT:   ps = "ON LEFT"; break;
   case DV_ON_RIGHT:  ps = "ON RIGHT"; break;
   case DV_ON_TOP:    ps = "ON TOP"; break;
   case DV_ON_BOTTOM: ps = "ON BOTTOM"; break;
   case DV_ON_TOPLEFT: ps = "TOPLEFT"; break;
   case DV_ON_BOTTRIGHT: ps = "BOTTRIGHT"; break;
   case DV_ON_TOPRIGHT: ps = "TOPRIGHT"; break;
   case DV_ON_BOTTLEFT: ps = "BOTTLEFT"; break;
   case DV_ON_ALL:    ps = "IN CLIP"; break;
   }
   return ps;
}

VOID SetMouseMessage( INT xPos, INT yPos, DV_CURPOS cp, PRECT prcClip )
{
#ifdef   ADD_STATUS_BAR
   if( ghStatusWnd ) {
      PTSTR pd = GetStgBuf();
      sprintf( pd, "Mouse x,y = %d,%d - %s %dx%d", xPos, yPos, GetClipPosStg(cp),
         (prcClip->right - prcClip->left),
         (prcClip->bottom - prcClip->top) );
      SendMessage( ghStatusWnd, SB_SETTEXT, 0, (LPARAM) pd);
   }
#endif // #ifdef   ADD_STATUS_BAR
}

#ifdef   ADD_STATUS_BAR
#define  SSM(a)   if(ghStatusWnd) SendMessage( ghStatusWnd, SB_SETTEXT, 0, (LPARAM) a)
#else
#define  SSM(a)
#endif

VOID ChildWndMouseMove( HWND hWnd, WPARAM wParam, LPARAM lParam )
{
   LPDIBINFO   lpDIBInfo;
	HANDLE      hDIBInfo = GetWindowExtra( hWnd, WW_DIB_HINFO );
   PTSTR       pd; // = GetTmp3();
   INT         xPos; // = GET_X_LPARAM(lParam);
   INT         yPos; // = GET_Y_LPARAM(lParam);
   RECT        rcc;

   xPos = GET_X_LPARAM(lParam);
   yPos = GET_Y_LPARAM(lParam);
   pd = GetTmp3();

#ifdef   ADD_STATUS_BAR
   if( ghStatusWnd ) {
      sprintf( pd, "Mouse x,y = %d,%d ...", xPos, yPos );
   }
#endif // #ifdef   ADD_STATUS_BAR

   if( !hDIBInfo ) {
      strcat(pd, " NO hDIBInfo!");
      SSM(pd);
      //SendMessage( ghStatusWnd, SB_SETTEXT, 0, (LPARAM) pd );
		return;
   }

	lpDIBInfo = (LPDIBINFO) DVGlobalLock (hDIBInfo);
   if( !lpDIBInfo ) {
      strcat(pd, " NO lpDIBInfo!");
      SSM(pd);
		return;
   }

   if( IsRectEmpty( &lpDIBInfo->rcClip ) ) {
      // sprintf(pd, "WM_MOUSEMOVE: Position x,y %d,%d NO rcClip!"MEOR, xPos, yPos);
	   // DO(pd);
      strcat(pd, " Empty CLIP!");
      SSM(pd);
      DVGlobalUnlock( hDIBInfo );	// Unlock pointer
      return;
   }

   rcc = lpDIBInfo->rcClip; // Get the current CLIP region
   if((( rcc.left      == xPos ) || ( rcc.left + 1   == xPos ) || ( rcc.left - 1  == xPos )) &&
      (( rcc.top       == yPos ) || ( rcc.top  + 1   == yPos ) || ( rcc.top  - 1  == yPos )) ) {
         // of TOP LEFT CORNER
         Clippos = DV_ON_TOPLEFT;
   } else if((( rcc.left      == xPos ) || ( rcc.left   + 1 == xPos ) || ( rcc.left  - 1  == xPos )) &&
      (( rcc.bottom    == yPos ) || ( rcc.bottom + 1 == yPos ) || ( rcc.bottom - 1 == yPos )) ) {
         // of BOTTOM LEFT CORNER
         Clippos = DV_ON_BOTTLEFT;
   } else if((( rcc.right    == xPos ) || ( rcc.right  + 1 == xPos ) || ( rcc.right  - 1 == xPos )) &&
      (( rcc.bottom    == yPos ) || ( rcc.bottom + 1 == yPos ) || ( rcc.bottom - 1 == yPos )) ) {
         // of BOTTOM RIGHT CORNER
         Clippos = DV_ON_BOTTRIGHT;
   } else if((( rcc.right  == xPos ) || ( rcc.right + 1 == xPos ) || ( rcc.right - 1  == xPos )) &&
      (( rcc.top       == yPos ) || ( rcc.top  + 1   == yPos ) || ( rcc.top  - 1  == yPos )) ) {
         // of TOP RIGHT CORNER
         Clippos = DV_ON_TOPRIGHT;
   } else if((( rcc.left     == xPos ) || ( rcc.right      == xPos )||
       ( rcc.left + 1 == xPos ) || ( rcc.right + 1  == xPos )||
       ( rcc.left - 1 == xPos ) || ( rcc.right - 1  == xPos )) &&
       ( rcc.top + 1  <  yPos  )&& ( rcc.bottom - 1 >  yPos )) {
      // matching WE == x parameter
      if(( rcc.left     == xPos ) ||
         ( rcc.left + 1 == xPos ) ||
         ( rcc.left - 1 == xPos ))
      {
         Clippos = DV_ON_LEFT;
      } else {
         Clippos = DV_ON_RIGHT;
      }
   } else if((( rcc.top      == yPos  ) || ( rcc.bottom     == yPos )||
              ( rcc.top + 1  == yPos  ) || ( rcc.bottom + 1 == yPos )||
              ( rcc.top - 1  == yPos  ) || ( rcc.bottom - 1 == yPos )) &&
              ( rcc.left - 1 <  xPos  ) && ( rcc.right - 1  >  xPos )) {
      // matching NS == y parameter
      if(( rcc.top      == yPos  ) ||
         ( rcc.top + 1  == yPos  ) ||
         ( rcc.top - 1  == yPos  ))
      {
         Clippos = DV_ON_TOP;
      } else {
         Clippos = DV_ON_BOTTOM;
      }
   } else if( ( xPos > rcc.left   ) &&
              ( xPos < rcc.right  ) &&
              ( yPos > rcc.top    ) &&
              ( yPos < rcc.bottom ) ) {
      // could use BOOL PtInRect(CONST RECT *lprc,  // rectangle
      // POINT pt ); // point, but here in can NOT be ON top or left
      Clippos = DV_ON_ALL;
   } else {
      //SetCursor( hCurArrow );
      Clippos = DV_ON_NONE;
   }
   DVGlobalUnlock( hDIBInfo );	// Unlock pointer
   SetCursorType( Clippos );
   SetMouseMessage( xPos, yPos, Clippos, &rcc );
}

//---------------------------------------------------------------------
//
// Function:   NormalizeRect
//
// Purpose:    Insure that the upper/left corner of the rectangle is
//             kept in rect.top/rect.right.  Swaps around coordinates
//             in the rectangle to make sure this is true.
//
//             Code was stolen verbatim from ShowDIB.
//
// Parms:      lpRect == Pointer to RECT to normalize.
//
// History:   Date      Reason
//             ????     Created
//             
//---------------------------------------------------------------------
// SWAP bits
#define  SWAP_LR     1
#define  SWAP_TB     2

int NormalizeRect (LPRECT lpRect)
{
   int   ret = 0;
   if (lpRect->right < lpRect->left) {
      SWAP (lpRect->right,lpRect->left);
      ret |= SWAP_LR;
   }

   if (lpRect->bottom < lpRect->top) {
      SWAP (lpRect->bottom,lpRect->top);
      ret |= SWAP_TB;
   }
   return ret;
}

//---------------------------------------------------------------------
//
// Function:   DrawSelect ( HDC hDc, LPRECT lpClip )
//
// Purpose:    Draw the rubberbanding rectangle with the specified
//             dimensions on the specified DC.  Rectangle includes
//             a string with its dimensions centered within it.
//             ie paintclip
//             Code was stolen almost verbatim from ShowDIB.
//
// Parms:      hDC      == DC to draw into.
//             lprcClip == Pointer to Rectangle to draw.
//
// History:   Date      Reason
//             ????     Created
//			1998 May 4	There appears to be a PROBLEM with the CLIP
//						when the image is SCROLLED!
//			1998 Dec 27 Changed to POINTER to RECT to draw
//
//---------------------------------------------------------------------

void DrawSelect( HDC hDC, LPRECT lprcClip )
{
	char	szStr[80];
	DWORD	dwExt;
	int		x, y, nLen, dx, dy;
	HDC		hDCBits;
	HBITMAP	hBitmap, hOldmap;

	// Don't have anything to do if the rectangle is empty.
	if( ( hDC == 0 ) ||
		( lprcClip == 0 ) ||
		( IsRectEmpty( lprcClip ) ) )
		return;

	// Draw rectangular clip region
	PatBlt( hDC,
		lprcClip->left,
		lprcClip->top,
		lprcClip->right - lprcClip->left,
		1,
		DSTINVERT );

	PatBlt( hDC,
		lprcClip->left,
		lprcClip->bottom,
		1,
		-(lprcClip->bottom - lprcClip->top),
		DSTINVERT );

	PatBlt( hDC,
		lprcClip->right - 1,
		lprcClip->top,
		1,
		lprcClip->bottom - lprcClip->top,
		DSTINVERT );

	PatBlt( hDC,
		lprcClip->right,
		lprcClip->bottom - 1,
		-(lprcClip->right - lprcClip->left),
		1,
		DSTINVERT );

	// Format the dimensions string
	wsprintf( szStr,
		"%dx%d",
		(lprcClip->right - lprcClip->left),
		(lprcClip->bottom - lprcClip->top) );

	nLen = lstrlen( szStr );

	// and center it in the rectangle
	dwExt   = GetTextExtent( hDC, szStr, nLen );
#ifdef	DIAGTE
	if( dwExt != GetTE( hDC, szStr, nLen ) )
		chkchk();
#endif	// DIAGTE
	dx      = LOWORD (dwExt);
	dy      = HIWORD (dwExt);
	x       = (lprcClip->right  + lprcClip->left - dx) / 2;
	y       = (lprcClip->bottom + lprcClip->top  - dy) / 2;

	hDCBits = CreateCompatibleDC( hDC );

	// Output the text to the DC
	SetTextColor( hDCBits, RGB(255, 255, 255) );
	SetBkColor(   hDCBits, RGB(  0,   0,   0) );

	if( hBitmap = CreateBitmap( dx, dy, 1, 1, NULL ) )
	{
		hOldmap = SelectObject( hDCBits, hBitmap );
		ExtTextOut( hDCBits, 0, 0, 0, NULL, szStr, nLen, NULL );
		BitBlt( hDC, x, y, dx, dy, hDCBits, 0, 0, SRCINVERT );
		hOldmap = SelectObject( hDCBits, hOldmap );
		DeleteObject( hBitmap );
	}

	DeleteDC( hDCBits );

#if	(defined( DIAGSCROLL ) && defined( SHOWEACLIP ))
	DiagDrawSel( lprcClip, NULL );
#endif	// DIAGSCROLL nad SHOWEACLIP

	rcPrevClip.left   = lprcClip->left;
	rcPrevClip.top    = lprcClip->top;
	rcPrevClip.right  = lprcClip->right;
	rcPrevClip.bottom = lprcClip->bottom;

}


//---------------------------------------------------------------------
//
// Function:   TrackMouse
//
// Purpose:    This routine is called when the left mouse button is
//             held down.  It will continuously draw a rectangle
//             showing where the user has selected for cutting to the
//             clipboard.  When this routine is called, lpClipRect's
//             top/left should point at the point in the client area
//             where the left mouse button was hit.  It will return the
//             full sized rectangle the user selected.  Never allow the
//             rubber band to extend beyond the DIB's margins.
//
//             Code was stolen almost verbatim from ShowDIB.
//
// Parms:      hWnd       == Handle to this MDI child window.
//             lpClipRect == Rectangle enclosed by tracking box.
//             cxDIB      == Width of DIB.  Won't allow tracking box
//                             to go beyond the width.
//             cyDIB      == Height of DIB.  Won't allow tracking box
//                             to go beyond the height.
//
// Caller:	void ChildWndLeftButton( HWND hWnd, int x, int y)
//			Caller: WM_LBUTTONDOWN case in CHILDWNDPROC
//
// History:   Date      Reason
//             ????     Created
//             9/1/91   Added cxDIB/cyDIB to not allow garbage
//                        to be pasted to the clipboard.
//             
//---------------------------------------------------------------------

void TrackMouse( HWND hWnd, LPRECT lpClipRect, int cxDIB, int cyDIB)
{

	HDC   hDC;
	MSG   msg;
	POINT ptOrigin, ptStart;
	RECT  rcClient, rcLast;

	hDC = GetDC (hWnd);
	if( !hDC )
		return;

	SetCapture( hWnd );

	GetClientRect( hWnd, &rcClient );

	// Get mouse coordinates relative to origin of DIB.  Then
	//  setup the clip rectangle accordingly (it already should
	//  contain the starting point in its top/left).
	ptOrigin.x         = GetScrollPos( hWnd, SB_HORZ );
	ptOrigin.y         = GetScrollPos( hWnd, SB_VERT );
	// FIX981228 - This looks WRONG
	//lpClipRect->top   += ptOrigin.x;
	//lpClipRect->left  += ptOrigin.y;
	// Lets SWITCH IT and SEE
	lpClipRect->left  += ptOrigin.x;
	lpClipRect->top   += ptOrigin.y;

	lpClipRect->right  = lpClipRect->left;
	lpClipRect->bottom = lpClipRect->top;

	ptStart.x          = lpClipRect->left;    // Need to remember the
	ptStart.y          = lpClipRect->top;     //  starting point.

   // Display the starting coordinates.
#ifdef	WIN32
	SetWindowOrgEx( hDC, ptOrigin.x, ptOrigin.y, NULL );
#else	// !WIN32
	SetWindowOrg( hDC, ptOrigin.x, ptOrigin.y );
#endif	// WIN32 y/n

	// ADD THE FIRST NEW CLIP
   // OR REMOVE the OLD clip region
	//DrawSelect( hDC, *lpClipRect );
   DrawSelect( hDC, lpClipRect );

#ifdef	SHOWCLIP
	ShowBegin( lpClipRect, &ptOrigin, &ptStart, &rcClient, cxDIB, cyDIB );
#endif	// SHOWCLIP
	rcLast = *lpClipRect;
	// Eat mouse messages until a WM_LBUTTONUP is encountered.
	// Meanwhile continue to draw a rubberbanding rectangle
	// and display it's dimensions
	for( ;; )
	{

		// The WaitMessage function yields control to other threads
		// when a thread has no other messages in its message queue.
		// The WaitMessage function suspends the thread and does not
		// return until a new message is placed in the thread's
		// message queue. 
		WaitMessage();

		// PeekMessage Return Values
		// If a message is available, the return value is nonzero.
		if( PeekMessage( &msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE ) )
		{
			// Erase the OLD rectangle
         DrawSelect( hDC, lpClipRect );

			rcLast = *lpClipRect;

			// Determine new coordinates.
			lpClipRect->left   = ptStart.x;
			lpClipRect->top    = ptStart.y;

			lpClipRect->right  = LOWORD (msg.lParam) + ptOrigin.x;
			lpClipRect->bottom = HIWORD (msg.lParam) + ptOrigin.y;

			NormalizeRect( lpClipRect );

			// Keep the rectangle within the bounds of the DIB.
			lpClipRect->left   = MAX(lpClipRect->left,   0);
			lpClipRect->top    = MAX(lpClipRect->top,    0);
			lpClipRect->right  = MAX(lpClipRect->right,  0);
			lpClipRect->bottom = MAX(lpClipRect->bottom, 0);

			lpClipRect->left   = MIN(lpClipRect->left,   cxDIB);
			lpClipRect->top    = MIN(lpClipRect->top,    cyDIB);
			lpClipRect->right  = MIN(lpClipRect->right,  cxDIB);
			lpClipRect->bottom = MIN(lpClipRect->bottom, cyDIB);

			// Draw the NEW rectangle.
			DrawSelect( hDC, lpClipRect );

#if	(defined( SHOWWM ) || defined( DIAGWM2 ))
//typedef struct tagMSG {     // msg
//	HWND   hwnd;
//	UINT   message; 
//  WPARAM wParam;
//	LPARAM lParam;
//	DWORD  time;
//	POINT  pt;
//} MSG;
			DiagChildMsg( msg.hwnd,		// HWND hWnd,
				msg.message,			// UINT message,
				msg.wParam,				// WPARAM wParam,
				msg.lParam );			// LPARAM lParam
#endif	// SHOWWM or DIAGWM2

			// If the button is released, quit.
			if( msg.message == WM_LBUTTONUP )
			{
#ifdef	SHOWCLIP
				ShowFinal( lpClipRect,
					&ptOrigin, &ptStart,
					&rcClient, cxDIB, cyDIB );
#endif	// SHOWCLIP
				// we have completed tracking
				break;
			}
			else
			{
#ifdef	SHOWCLIP
//				if( ( rcLast.left != lpClipRect->left ) ||
				DiagDrawSel( lpClipRect, &ptStart );
#endif	// SHOWCLIP
			}
		}
		else
			continue;

	}	// FOREVER

	// Clean up.
	ReleaseCapture();

	ReleaseDC( hWnd, hDC );

} // end - void TrackMouse (HWND hWnd, LPRECT lpClipRect, int cxDIB, int cyDIB)

void TrackMouse2 (HWND hWnd, LPRECT lpClipRect, int cxDIB, int cyDIB,
                  LPDIBINFO	lpDIBInfo )
{

	HDC   hDC;
	MSG   msg;
	POINT ptOrigin, ptStart, ptBgn;
	RECT  rcClient, rcLast;
   INT   x, y, swap;
   DV_CURPOS  cp = Clippos;
   INT   xd,yd;

	hDC = GetDC (hWnd);

	if( !hDC )
		return;

   // extract current mouse position,
   // and this is also where the FINAL Clip is returned
   x = lpClipRect->left;
   y = lpClipRect->top;
   ptBgn.x = x;
   ptBgn.y = y;

   rcLast = *lpClipRect; // extract the ORIGINAL x,y mouse position

	SetCapture( hWnd );
   SetCursorType( cp );

	GetClientRect( hWnd, &rcClient );

	// Get mouse coordinates relative to origin of DIB.  Then
	//  setup the clip rectangle accordingly
	ptOrigin.x         = GetScrollPos( hWnd, SB_HORZ );
	ptOrigin.y         = GetScrollPos( hWnd, SB_VERT );
	//lpClipRect->left  += ptOrigin.x;
	//lpClipRect->top   += ptOrigin.y;
	//lpClipRect->right  = lpClipRect->left;
	//lpClipRect->bottom = lpClipRect->top;
   *lpClipRect = lpDIBInfo->rcClip; // establish the ORIGINAL clip

	ptStart.x          = lpClipRect->left;    // Need to remember the
	ptStart.y          = lpClipRect->top;     //  starting point.

   // Display the starting coordinates.
	SetWindowOrgEx( hDC, ptOrigin.x, ptOrigin.y, NULL );

   // REMOVE the OLD clip region
   DrawSelect( hDC, lpClipRect );
#ifdef	SHOWCLIP
	ShowBegin( lpClipRect, &ptOrigin, &ptStart, &rcClient, cxDIB, cyDIB );
#endif	// SHOWCLIP
   // make NEW clip region ... will probably be the same +/- 1 pixel
   switch( cp ) // = Clippos
   {
   case DV_ON_LEFT:
      lpClipRect->left = x + ptOrigin.x;
      break;
   case DV_ON_RIGHT:
      lpClipRect->right = x + ptOrigin.x;
      break;
   case DV_ON_TOP:
      lpClipRect->top = y + ptOrigin.y;
      break;
   case DV_ON_BOTTOM:
      lpClipRect->bottom = y + ptOrigin.y;
      break;
   case DV_ON_TOPLEFT:
      lpClipRect->left = x + ptOrigin.x;
      lpClipRect->top = y + ptOrigin.y;
      break;
   case DV_ON_TOPRIGHT:
      lpClipRect->top = y + ptOrigin.y;
      lpClipRect->right = x + ptOrigin.x;
      break;
   case DV_ON_BOTTLEFT:
      lpClipRect->bottom = y + ptOrigin.y;
      lpClipRect->left = x + ptOrigin.x;
      break;
   case DV_ON_BOTTRIGHT:
      lpClipRect->bottom = y + ptOrigin.y;
      lpClipRect->right = x + ptOrigin.x;
      break;
   case DV_ON_ALL:
      break;
   }
   // DRAW the NEW clip region
   DrawSelect( hDC, lpClipRect );

	rcLast = *lpClipRect;
	// Eat mouse messages until a WM_LBUTTONUP is encountered.
	// Meanwhile continue to draw a rubberbanding rectangle
	// and display it's dimensions
	for( ;; )
	{
		// The WaitMessage function yields control to other threads
		// when a thread has no other messages in its message queue.
		// The WaitMessage function suspends the thread and does not
		// return until a new message is placed in the thread's
		// message queue. 
		WaitMessage();

		// PeekMessage Return Values
		// If a message is available, the return value is nonzero.
		if( PeekMessage( &msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE ) )
		{
			// Erase the OLD rectangle
         DrawSelect( hDC, lpClipRect );

         x = LOWORD (msg.lParam);
         y = HIWORD (msg.lParam);
         xd = ptBgn.x - x;
         yd = ptBgn.y - y;

			rcLast = *lpClipRect;

			// Determine new coordinates.
         // there is a PROBLEM when crossing over
         // fix = cancel NormalizeRect( lpClipRect );
         // and do normailisation case by case
         switch( cp ) // = Clippos
         {
         case DV_ON_LEFT:
            lpClipRect->left = x + ptOrigin.x;
            if( lpClipRect->right < lpClipRect->left ) {
               SWAP (lpClipRect->right,lpClipRect->left);
               cp = DV_ON_RIGHT; // switch sides
            }
            break;
         case DV_ON_RIGHT:
            lpClipRect->right = x + ptOrigin.x;
            if( lpClipRect->right < lpClipRect->left ) {
               SWAP (lpClipRect->right,lpClipRect->left);
               cp = DV_ON_LEFT;
            }
            break;
         case DV_ON_TOP:
            lpClipRect->top = y + ptOrigin.y;
            if (lpClipRect->bottom < lpClipRect->top) {
               SWAP (lpClipRect->bottom,lpClipRect->top);
               cp = DV_ON_BOTTOM;
            }
            break;
         case DV_ON_BOTTOM:
            lpClipRect->bottom = y + ptOrigin.y;
            if (lpClipRect->bottom < lpClipRect->top) {
               SWAP (lpClipRect->bottom,lpClipRect->top);
               cp = DV_ON_TOP;
            }
            break;
         case DV_ON_TOPLEFT:
            lpClipRect->left = x + ptOrigin.x;
            lpClipRect->top = y + ptOrigin.y;
            swap = NormalizeRect(lpClipRect);
            if( swap ) {
               if( swap == (SWAP_LR & SWAP_TB) ) {
                  cp = DV_ON_BOTTRIGHT; // swapped BOTH
               } else if ( swap & SWAP_LR ) {
                  cp = DV_ON_TOPRIGHT;
               } else if ( swap & SWAP_TB ) {
                  cp = DV_ON_BOTTLEFT;
               }
            }
            break;
         case DV_ON_TOPRIGHT:
            lpClipRect->top = y + ptOrigin.y;
            lpClipRect->right = x + ptOrigin.x;
            swap = NormalizeRect(lpClipRect);
            if( swap ) {
               if(swap == (SWAP_LR & SWAP_TB)) {
                  cp = DV_ON_BOTTLEFT; // swapped BOTH
               } else if( swap & SWAP_LR ) {
                  cp = DV_ON_TOPLEFT;
               } else if( swap & SWAP_TB ) {
                  cp = DV_ON_BOTTRIGHT;
               }
            }
            break;
         case DV_ON_BOTTLEFT:
            lpClipRect->bottom = y + ptOrigin.y;
            lpClipRect->left = x + ptOrigin.x;
            swap = NormalizeRect(lpClipRect);
            if( swap ) {
               if(swap == (SWAP_LR & SWAP_TB)) {
                  cp = DV_ON_TOPRIGHT; // swapped BOTH
               } else if( swap & SWAP_LR ) {
                  cp = DV_ON_BOTTRIGHT;
               } else if( swap & SWAP_TB ) {
                  cp = DV_ON_TOPLEFT;
               }
            }
            break;
         case DV_ON_BOTTRIGHT:
            lpClipRect->bottom = y + ptOrigin.y;
            lpClipRect->right = x + ptOrigin.x;
            swap = NormalizeRect(lpClipRect);
            if( swap ) {
               if(swap == (SWAP_LR & SWAP_TB)) {
                  cp = DV_ON_TOPLEFT; // swapped BOTH
               } else if( swap & SWAP_LR ) {
                  cp = DV_ON_BOTTLEFT;
               } else if( swap & SWAP_TB ) {
                  cp = DV_ON_TOPRIGHT;
               }
            }
            break;
         case DV_ON_ALL:
            // move the whole thing, potentially
            {
               RECT rcNew, rcCopy;
               rcNew = *lpClipRect; // get current
               rcNew.left   -= xd;
               rcNew.right  -= xd;
               rcNew.top    -= yd;
               rcNew.bottom -= yd;
               rcCopy = rcNew;   // get COPY
               // max limit fixes
			      rcNew.left   = MAX(rcNew.left,   0);
               rcNew.top    = MAX(rcNew.top,    0);
			      rcNew.right  = MAX(rcNew.right,  0);
			      rcNew.bottom = MAX(rcNew.bottom, 0);
               rcNew.left   = MIN(rcNew.left,   cxDIB);
               rcNew.top    = MIN(rcNew.top,    cyDIB);
               rcNew.right  = MIN(rcNew.right,  cxDIB);
               rcNew.bottom = MIN(rcNew.bottom, cyDIB);
               if(( rcCopy.left   == rcNew.left   ) &&
                  ( rcCopy.bottom == rcNew.bottom ) &&
                  ( rcCopy.top    == rcNew.top    ) &&
                  ( rcCopy.right  == rcNew.right  ) ) {
                     // only UPDATE if NOT going OUT-OF-LIMITS
                     lpClipRect->left   -= xd;
                     lpClipRect->right  -= xd;
                     lpClipRect->top    -= yd;
                     lpClipRect->bottom -= yd;
               }
            }
            break;
         }

			// Keep the rectangle within the bounds of the DIB.
			lpClipRect->left   = MAX(lpClipRect->left,   0);
			lpClipRect->top    = MAX(lpClipRect->top,    0);
			lpClipRect->right  = MAX(lpClipRect->right,  0);
			lpClipRect->bottom = MAX(lpClipRect->bottom, 0);

			lpClipRect->left   = MIN(lpClipRect->left,   cxDIB);
			lpClipRect->top    = MIN(lpClipRect->top,    cyDIB);
			lpClipRect->right  = MIN(lpClipRect->right,  cxDIB);
			lpClipRect->bottom = MIN(lpClipRect->bottom, cyDIB);

			// Draw the NEW rectangle.
			DrawSelect( hDC, lpClipRect );
         // update start of move point
         ptBgn.x = x;
         ptBgn.y = y;
         SetMouseMessage( x, y, cp, lpClipRect );
			// If the button is released, quit.
			if( msg.message == WM_LBUTTONUP ) {
#ifdef	SHOWCLIP
				ShowFinal( lpClipRect,
					&ptOrigin, &ptStart,
					&rcClient, cxDIB, cyDIB );
#endif	// SHOWCLIP
				// we have completed tracking
            SetCursorType( DV_ON_NONE );
				break;   // EXIT forever
			}
			else
			{
#ifdef	SHOWCLIP
//				if( ( rcLast.left != lpClipRect->left ) ||
				DiagDrawSel( lpClipRect, &ptStart );
#endif	// SHOWCLIP
            SetCursorType( cp );
			}
		}
		else
			continue;

	}	// FOREVER

	// Clean up.
	ReleaseCapture();

	ReleaseDC( hWnd, hDC );

} // end - void TrackMouse2 (HWND hWnd, LPRECT lpClipRect, int cxDIB, int cyDIB,
  //                        LPDIBINFO	lpDIBInfo)


//---------------------------------------------------------------------
//
// Function:   ChildWndLeftButton
//
// Purpose:    Called by ChildWndProc() on WM_LBUTTONDOWN.
//
//             If the user presses the left button, erase the currently
//             selected rectangle.  Then, start drawing a
//             rectangle (for the area of the DIB to be put in the
//             clipboard on an Edit/Paste operation).
//
//
// Parms:      hWnd == Handle to window getting WM_LBUTTONDOWN.
//
// History:   Date      Reason
//
//            10/15/91  Cut code out from WM_LBUTTONDOWN case.
//             
//---------------------------------------------------------------------
void ChildWndLeftButton( HWND hWnd, int x, int y )
{
	HANDLE		hDIBInfo;
	LPDIBINFO	lpDIBInfo;
	HDC			hDC;
	RECT		   rcClip;
	int			cxDIB, cyDIB;

	// Start a new clip rectangle.  Track the rubber band. Rubber
	//  band won't be allowed to extend past the extents of the
	//  DIB.
	rcClip.top  = y;
	rcClip.left = x;

	// Find the old clip rectangle and erase it.
	hDIBInfo = GetWindowExtra( hWnd, WW_DIB_HINFO );
	if( !hDIBInfo )
		return;

	lpDIBInfo = (LPDIBINFO) DVGlobalLock (hDIBInfo);
	if( !lpDIBInfo )
		return;

	// Determine the DIB's extents.  This is different than the
	//  DIB's height/width when the DIB's stretched.
	if( lpDIBInfo->Options.bStretch2W ) {
		RECT rcClient;
		GetClientRect( hWnd, &rcClient );
		cxDIB = rcClient.right;
		cyDIB = rcClient.bottom;
	} else {
		cxDIB = lpDIBInfo->di_dwDIBWidth;
		cyDIB = lpDIBInfo->di_dwDIBHeight;
	}

   if( Clippos == DV_ON_NONE ) {

	   hDC = GetDC( hWnd );
      if( !hDC ) {
         DVGlobalUnlock( hDIBInfo );
		   return;
      }

   #ifdef	WIN32
	   SetWindowOrgEx( hDC,
		   GetScrollPos( hWnd, SB_HORZ ),
		   GetScrollPos( hWnd, SB_VERT ),
		   NULL );
   #else	// !WIN32
	   SetWindowOrg( hDC,
		   GetScrollPos (hWnd, SB_HORZ),
		   GetScrollPos (hWnd, SB_VERT) );
   #endif	// WIN32 y/n

	   // REMOVE ANY OLD CLIP
	   DrawSelect( hDC, &lpDIBInfo->rcClip );
   #ifdef	SHOWCLIP
	   ShowRemove( &lpDIBInfo->rcClip );
   #endif	// SHOWCLIP

	   ReleaseDC( hWnd, hDC );

      TrackMouse( hWnd, &rcClip, cxDIB, cyDIB );

   } else {

      TrackMouse2( hWnd, &rcClip, cxDIB, cyDIB, lpDIBInfo );

   }

	// Store the new clipboard coordinates.
	lpDIBInfo->rcClip = rcClip;

	DVGlobalUnlock( hDIBInfo );

}

//---------------------------------------------------------------------
// Function:   CopyHandle
// Purpose:    Makes a copy of the given global memory block.  Returns
//             a handle to the new memory block (NULL on error).
//             Routine stolen verbatim out of ShowDIB.
// Parms:      h == Handle to global memory to duplicate.
// Returns:    Handle to new global memory block.
// History:   Date      Reason
//             ???      Created
//---------------------------------------------------------------------
HANDLE CopyHandle( HANDLE h )
{
	PINT8	lpCopy;
	PINT8	lp;
	HANDLE     hCopy;
	DWORD      dwLen;

	hCopy = 0;
	if( ( h ) &&
		( dwLen = GlobalSize( h ) ) )
	{
		//if( hCopy = DVGlobalAlloc( GHND, dwLen ) )
		if( hCopy = DVGAlloc( "CopyHand", GHND, dwLen ) )
		{
			if( lpCopy = (PINT8)DVGlobalLock( hCopy ) )
         {
			   // NOTE: Could be a handle from CLIPBOARD
			   if( lp = (PINT8)GlobalLock( h ) )
            {
#ifdef	WIN32
			      memcpy( lpCopy, lp, dwLen );
#else	// !WIN32
			      while( dwLen-- )
				      *lpCopy++ = *lp++;	// Slow BYTE by BYTE copy
#endif	// WIn32 y/n
      			GlobalUnlock( h );	// Note: A Clipboard Handle, for ex.
            }

            DVGlobalUnlock( hCopy );
         }
		}
	}
	return( hCopy );
}

//---------------------------------------------------------------------
//
// Function:   CropBitmap
//
// Purpose:    Crops a bitmap to a new size specified by the lpRect
//             parameter.  The lpptSize parameter is used to determine
//             how much to stretch/compress the bitmap.  Returns a
//             handle to a new bitmap.  If lpRect is empty, copies the
//             bitmap to a new one.
//
//             Stolen almost verbatim out of ShowDIB.
//
// Parms:      hbm      == Handle to device dependent bitmap to crop.
//             hPal     == Palette to use in cropping (NULL for default pal.)
//             lpRect   == New bitmap's size (size we're cropping to).
//             lpptSize == A scaling factor scale by the proportion:
//                              Bitmap Width / lpptSize->x horizontally,
//                              Bitmap Height / lpptSize->y horizontally.
//                           Note that if lpptSize is set to the bitmap's
//                           dimensions, no scaling occurs.
//             
//
// History:   Date      Reason
//            6/15/91   Stolen from ShowDIB
//             
//---------------------------------------------------------------------

HBITMAP CropBitmap (HBITMAP hbm, 
                   HPALETTE hPal, 
                     LPRECT lpRect, 
                    LPPOINT lpptSize)
{
	HDC      hMemDCsrc;
	HDC      hMemDCdst;
	BITMAP   bm;
	int      dxDst,dyDst, dxSrc, dySrc;
	double   cxScale, cyScale;
	HPALETTE hOldPal1 = NULL;
	HPALETTE hOldPal2 = NULL;
	HBITMAP  hNewBm = NULL;

	if( !hbm )		// Oops, no BITMAP!!!
		return( hNewBm );


	GetObject( hbm, sizeof(BITMAP), (LPSTR)&bm );


	hMemDCsrc = CreateCompatibleDC( NULL );
	hMemDCdst = CreateCompatibleDC( NULL );


	if( hPal )
	{
		hOldPal1 = SelectPalette( hMemDCsrc, hPal, FALSE );
		hOldPal2 = SelectPalette( hMemDCdst, hPal, FALSE );
		RealizePalette( hMemDCdst );
	}


	dxDst     = lpRect->right  - lpRect->left;
	dyDst     = lpRect->bottom - lpRect->top;
	cxScale   = (double) bm.bmWidth  / lpptSize->x;
	cyScale   = (double) bm.bmHeight / lpptSize->y;
	dxSrc     = (int) ((lpRect->right - lpRect->left) * cxScale);
	dySrc     = (int) ((lpRect->bottom - lpRect->top) * cyScale);


	if( (dxDst == 0) || (dyDst == 0) )
	{
		dxDst = bm.bmWidth;
		dyDst = bm.bmHeight;
	}


	if( dxSrc == 0 )
		dxSrc = 1;

	if( dySrc == 0 )
		dySrc = 1;


	hNewBm = CreateBitmap( dxDst, dyDst, bm.bmPlanes, bm.bmBitsPixel, NULL );

	if( hNewBm )
	{
		HBITMAP hOldBitmap1, hOldBitmap2;

		hOldBitmap1 = SelectObject( hMemDCsrc, hbm );
		hOldBitmap2 = SelectObject( hMemDCdst, hNewBm );

		StretchBlt( hMemDCdst,
			0,
			0,
			dxDst,
			dyDst,
			hMemDCsrc,
			(int) (lpRect->left * cxScale),
			(int) (lpRect->top  * cyScale),
			dxSrc,
			dySrc,
			SRCCOPY );

		SelectObject( hMemDCsrc, hOldBitmap1 );
		SelectObject( hMemDCdst, hOldBitmap2 );
	}

	if( hOldPal1 )
		SelectPalette( hMemDCsrc, hOldPal1, FALSE );

	if( hOldPal2 )
		SelectPalette( hMemDCdst, hOldPal1, FALSE );

	DeleteDC( hMemDCsrc );
	DeleteDC( hMemDCdst );

	return( hNewBm );
}	// End - HBITMAP CropBitmap(HBITMAP, HPALETTE, LPRECT, LPPOINT )



//---------------------------------------------------------------------
//
// Function:   RenderFormat
//
// Purpose:    Renders an object for the clipboard.  The format is
//             specified in the "cf" variable (either CF_BITMAP,
//             CF_DIB, or CF_PALETTE).
//
//             This happens after a HandleCopyClipboard ...
//
//             Stolen almost verbatim out of ShowDIB.
//
// Parms:      hWndClip == Window clipboard belongs to, and where our
//                         image is stored).
//             cf       == Format to render (CF_BITMAP, CF_DIB, CF_PALETTE)
//                         Is actually the wParam of the WM_ message.
//             ptDIBSize== Size of the DIB in the given window.
//
// History:   Date      Reason
//             ???      Created
//             
//---------------------------------------------------------------------

HANDLE RenderFormat( HWND hWndClip, int cf, POINT ptDIBSize )
{
	HBITMAP   hBitmap;
//	HANDLE    hDIB;
	HANDLE    hDIBInfo;
	LPDIBINFO lpDIBInfo;
	HPALETTE  hPalette;        // Handle to the bitmap's palette.
	HANDLE    h = NULL;		// Start with NOTHING

	if( !hWndClip )		// Oops, NO ACTIVE WINDOW!!!
		return( h );

	hDIBInfo = GetWindowExtra( hWndClip, WW_DIB_HINFO );
	if( !hDIBInfo )		// Double Oops - NO INFORMATION!!!
		return( h );

	lpDIBInfo    = (LPDIBINFO) DVGlobalLock (hDIBInfo);
   if( !lpDIBInfo )     // triple oooops - NO LOCK HANDLE
      return( h );

//	hDIB         = lpDIBInfo->hDIB;
	hPalette     = lpDIBInfo->hPal;
	hBitmap      = lpDIBInfo->hBitmap;
	DVGlobalUnlock (hDIBInfo);
	switch( cf )
	{
	case CF_BITMAP:
		h = CropBitmap( hBitmap, hPalette, &grcClip, &ptDIBSize );
		break;

	case CF_DIB:
		{
			HBITMAP hbm;
         // NOTE:  For simplicity, we use the display device to crop
			//        the bitmap.  This means that we may lose color
			//        precision (if the display device has less color
			//        precision than the DIB).  This isn't usually a
			//        problem, as users shouldn't really be editting
			//        images on devices that can't display them.
			hbm = RenderFormat( hWndClip, CF_BITMAP, ptDIBSize );
			if( hbm )
			{
				h = DVBitmapToDIB( hbm, hPalette );
				DeleteObject( hbm );
			}
			break;
		}

	case CF_PALETTE:
		if( hPalette )
			h = CopyPaletteChangingFlags( hPalette, 0 );
		break;

	}

	return h;

}	// End - HANDLE RenderFormat( HWND hWndClip, int cf, POINT ptDIBSize )


//---------------------------------------------------------------------
//
// Function:   HandleCopyClipboard
//
// Purpose:    User wants to copy the current DIB to the clipboard.
//             Tell the clipboard we can render a DIB, DDB, and a
//             palette (defer rendering until we get a WM_RENDERFORMAT
//             in our MDI child window procedure in CHILD.C).
//             Handle IDM_COPY from MENU
//
// Parms:      None
//             
//
// History:   Date      Reason
//            6/1/91    Created
//           11/4/91    Init grcClip to full DIB size if
//                      it is currently empty.
//             
//---------------------------------------------------------------------

HWND  HandleCopyClipboard( void )
{
	HWND    hDIBWnd = GetCurrentMDIWnd();
	if( hDIBWnd )
   {
		// Clean clipboard of contents, and tell it we can render
		//  a DIB, a DDB, and/or a palette.
		if( OpenClipboard( hDIBWnd ) )
		{
			EmptyClipboard();
			SetClipboardData( CF_DIB,     NULL );
			SetClipboardData( CF_BITMAP,  NULL );
			SetClipboardData( CF_PALETTE, NULL );
			CloseClipboard ();

			// Set our globals to tell our app which child window
			//  owns the clipboard, and the clipping rectangle at
			//  the time of the copy.  If the clipping rectangle is
			//  empty, then use the entire DIB window.
			hWndClip   = hDIBWnd;
			grcClip    = GetCurrentClipRect( hWndClip );
			ptClipSize = GetCurrentDIBSize( hWndClip );
			if( IsRectEmpty( &grcClip ) )
			{
				grcClip.left   = 0;
				grcClip.top    = 0;
				grcClip.right  = ptClipSize.x;
				grcClip.bottom = ptClipSize.y;
			}
		}
		else
		{
			DIBError( ERR_CLIPBUSY );
         hDIBWnd = 0;
		}
   }
   else
	{
		DIBError( ERR_NOCLIPWINDOW );
	}

   return hDIBWnd;

}


//---------------------------------------------------------------------
//
// Function:   HandlePasteClipboard
//
// Purpose:    User wants to paste the clipboard's contents to our
//             app.  Open a new DIB window with the bitmap in the
//             clipboard.
//
// Parms:      None
//             
// History:   Date      Reason
//            6/1/91    Created
//             
//---------------------------------------------------------------------
HWND  HandlePasteClipboard( VOID )
{
   HWND        hWnd = 0;
	HANDLE      hDIB;
	HBITMAP     hBitmap;
	HPALETTE    hPal;
	PRDIB		   prd = (PRDIB)MALLOC( sizeof(RDIB) );
   if(!prd)
      chkme( "C:ERROR: No memory!!!"MEOR );
	NULPRDIB( prd );

   hDIB = 0;   // no DIB (device independant bitmap) yet

	// Open up the clipboard.  This routine assumes our app has
	//  the focus (which it should, as the user just picked the
	//  paste operation off the menu, and Windows is a non-preemptive
	//  system.  First we try for a DIB; if that's not available go
	//  for a bitmap (and a palette if one can be had).  Whatever
	//  format's available, we have to copy immediately (since the
	//  handle returned by GetClipboardData() belongs to the clipboard.
	//  Finally, go create the new MDI child window.
	if( OpenClipboard( GetFocus() ) )
	{
		if( IsClipboardFormatAvailable( CF_DIB ) )
		{
			hDIB = CopyHandle( GetClipboardData( CF_DIB ) );
		}
		else if( IsClipboardFormatAvailable( CF_BITMAP ) )
		{
			// HANDLE GetClipboardData(
			//    UINT uFormat );	// clipboard format  
			hBitmap = GetClipboardData( CF_BITMAP );

			if( IsClipboardFormatAvailable( CF_PALETTE ) )
				hPal = GetClipboardData( CF_PALETTE );
			else
				hPal = GetStockObject( DEFAULT_PALETTE );

			hDIB = DVBitmapToDIB( hBitmap, hPal );
		}
		else
		{
			DIBError( ERR_NOCLIPFORMATS );
		}

		CloseClipboard();

      if( !hDIB )
         goto Exit_Paste;

		prd->rd_pTitle = gszRPTit;     // set TITLE file name
      prd->rd_pPath  = gszRPNam;     // but keep the FULL NAME
		// The window title is of the form: "Clipboard1".  The
		//  number in the title is changed for each paste operation.
      // Get Next Name( &rd, &gszRPFrm[0], gnPasteNum++ );
      DVNextRDName2( prd, &gszRPFrm[0], &gnPasteNum, &gbChgPaste );   // = "TEMPB%03d.BMP"

		// Perform the actual window opening.
		prd->rd_hDIB    = hDIB;
		prd->rd_Caller  = df_CLIPBRD;

		//OpenDIBWindow( hDIB, szTitle, df_CLIPBRD );
		hWnd = OpenDIBWindow2( prd ); 
		if(hWnd)
      {
         if( WriteBMPFile2( gszRPNam, hDIB, TRUE ) )
         {
#ifdef	CHGADDTO
            //AddToFileList( szTitle );
            ADD2LIST( prd );
#endif	// CHGADDTO
         }
      }
	}
	else
	{
		DIBError (ERR_CLIPBUSY);
	}

Exit_Paste:

   MFREE(prd);

   return hWnd;

}	// End - void HandlePasteClipboard (void)

// eof
