// Windows 3.1 Demo Program
// Copyright (c) 1992 by Borland International

// File CLIOLE.C
// An example of an Ole Client.  It demonstrates
// a simple use of Client Ole functions.

// Its main window allows the user to create a paint brush object, or
// copy an object in from the clipboard.  Note, you do need to have
// paint brush available on the system.  (It is a standard part of
// Windows 3.1).

#define STRICT

#include <windows.h>
#include <ole.h>
#include <commdlg.h>
#include <process.h>
#include <alloc.h>
#include <dir.h>
#include <dos.h>
#pragma hdrstop

#include "clioleer.h"
#include "cliole.h"
#include "cliolerc.h"

char szAppName[] = "ClientOleApp" ;

HINSTANCE hInst = 0;

void InitApplication(void)
{
	WNDCLASS wndclass;

	wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
	wndclass.lpfnWndProc   = (WNDPROC) WndProc ;
	wndclass.cbClsExtra    = 0 ;
	wndclass.cbWndExtra    = 0 ;
	wndclass.hInstance     = hInst ;
	wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
	wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
	wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wndclass.lpszMenuName  = "CLIOLEMENU" ;
	wndclass.lpszClassName = szAppName ;
	RegisterClass (&wndclass) ;
}

// These are clipboard formats that Ole
// applications use to determine the type of
// object on the clipboard.  This application does not
// make use of them.

OLECLIPFORMAT vcfLink, vcfNative, vcfOwnerLink;

void InitInstance(void)
{
	vcfLink = RegisterClipboardFormat( "ObjectLink" );
	vcfNative = RegisterClipboardFormat( "Native" );
	vcfOwnerLink = RegisterClipboardFormat( "OwnerLink" );
}

#pragma argsused
int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
					LPSTR lpszCommandLine, int nCmdShow)
	 {
	 HWND        hwnd ;
	 MSG         msg ;

     hInst = hInstance;
	 if (!hPrevInstance)
	 {
		InitApplication();
	 }

	 InitInstance();

	 hwnd = CreateWindow (szAppName,
			"Client OLE Program",
					WS_OVERLAPPEDWINDOW,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					CW_USEDEFAULT,
					NULL,
					NULL,
					hInstance,
					NULL) ;

	 ShowWindow (hwnd, nCmdShow) ;
	 UpdateWindow (hwnd) ;

	 while (GetMessage (&msg, NULL, 0, 0))
	 {
		  TranslateMessage (&msg) ;
		  DispatchMessage (&msg) ;
	 }
	 return msg.wParam ;
}



int FAR PASCAL _export StdCallBack(LPOLECLIENT lpClient,
	OLE_NOTIFICATION notification,
	LPOLEOBJECT lpObject )
{
	static TOleNot OleNotStruct;
	OleNotStruct.lpClient = lpClient;
	OleNotStruct.not      = notification;
	OleNotStruct.lpObject = lpObject;

	return (
		(int)SendMessage( (( TOleClient * )lpClient)->hwnd,
			WM_U_CALLBACK, 0, (DWORD)&OleNotStruct )
			);
}

// It should be noted that since much of the data associated with the
// main window is stored globally, only one Windows class OleAppName
// is supported.  Extending the program to support several windows of this
// class would require that you store this data on a per window basis.
// A good technique would be to use a structure which contains all
// these data members, and to store a pointer to such a structure
// in the extra bytes of the windows.

	OLESTATUS ret;   		//last OleStatus
	LHCLIENTDOC lhClientDoc;
	char lpszDocName[ MAXPATH ];
	BOOL bDefDocName;

	char lpszObjectName[ MAXPATH ];
	char lpszLastObjectName[ MAXPATH ];
	TOleClient *pOleClient;
	BOOL bObjectLoaded = FALSE;
	BOOL bUndoObjectLoaded = FALSE;
	LPOLEOBJECT lpObject = NULL;		// actual object we are housing
	LPOLEOBJECT lpUndoObject = NULL;	// last change made to object
	int nNextObjectNum = 0;


long FAR PASCAL _export WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
{

   HDC           hdc ;
	PAINTSTRUCT   ps ;
	RECT	 	     rect ;
   TOleNot far * ons ;

	switch (message)
	{
		case WM_U_REDRAW:
			WMURedraw( hwnd );
			return 0;

		case WM_COMMAND:
			switch ( wParam )
			{
				case CM_PBRUSH:
					CMPBrush();
					break;

				case CM_UNDO:
					CMUndo(hwnd);
					break;

				case CM_ACTIVATE:
					CMActivate( hwnd );
					break;

				case CM_CLEAR:
					CMClear( hwnd );
					break;

				case CM_CUT:
					CMCut(hwnd);
					break;

				case CM_COPY:
					CMCopy(hwnd);
					break;

				case CM_PASTE:
					CMPaste(hwnd);
					break;

				case CM_ABOUT:
					CMAbout( hwnd );
					break;

				case CM_EXIT:
					DestroyWindow( hwnd );
					break;
			}
			return 0;

		case WM_CREATE:
			WMCreate( hwnd );
			return 0;

		case WM_INITMENU:
			WMInitMenu( hwnd , wParam );
			return 0;

		case WM_U_CALLBACK:
			(TOleNot far *)ons = (TOleNot far *)lParam;
			WMUCallBack( ons->lpClient, ons->not, ons->lpObject );
			return 0;

		case WM_PAINT:
			 hdc = BeginPaint (hwnd, &ps) ;
			 GetClientRect (hwnd, &rect) ;

			 Paint( hwnd , hdc );

			 EndPaint (hwnd, &ps) ;
			 return 0 ;

		case WM_DESTROY:
			PostQuitMessage (0) ;
			return 0 ;
	}
	return DefWindowProc (hwnd, message, wParam, lParam) ;
}


// Comments on MakeTOleClient:
// If no vtbl has been constructed, make one.  Then initialize
// the vtbl member of the struct to point to it.
// To be more robust, one should always check that memory is allocated,
// and MakeProcInstance succeeds.

LPOLECLIENTVTBL lpClientVtbl = NULL;

void MakeTOleClient( TOleClient *OleClient, HWND owner , HINSTANCE hInst )
{
	OleClient->hwnd = owner;
	if ( !lpClientVtbl )
	{
		lpClientVtbl = (LPOLECLIENTVTBL) malloc( sizeof ( OLECLIENTVTBL ));
		lpClientVtbl->CallBack = (TCallBack)MakeProcInstance( (FARPROC)StdCallBack,
					hInst );
	}
	OleClient->oleClient.lpvtbl = lpClientVtbl;
}

void WMURedraw(HWND hwnd )
{
	bObjectLoaded = TRUE;
	InvalidateRect( hwnd, NULL, TRUE );
	UpdateWindow( hwnd );
}

#pragma argsused
int WMUCallBack( LPOLECLIENT lpOleClient ,
				 OLE_NOTIFICATION oleNot,
				 LPOLEOBJECT lpOleObject )
{
	switch ( oleNot ) {
		case OLE_CHANGED:
		case OLE_SAVED:
			PostMessage( (( TOleClient *)lpOleClient)->hwnd ,
				WM_U_REDRAW, 0, 0L );
			break;

		case OLE_CLOSED:
			break;

		case OLE_QUERY_PAINT:
			break;

		case OLE_RELEASE:
			break;

		case OLE_RENAMED:
			break;

		default:
			break;
	}
	return TRUE;
}

// create a new paint brush
void CMPBrush(void)
{
	BackupObject();
	bObjectLoaded = FALSE;

	lstrcpy( lpszObjectName, GetNextObjectName() );

	ret = OleCreate( "StdFileEditing",
			(LPOLECLIENT)pOleClient,
			"PBRUSH",
			lhClientDoc,
			szAppName,
			&lpObject,
			olerender_draw,
			0 );

   // Creating an Ole Object is an asynchronous operation.  An
	// interesting experiment is to use TDW to step into
	// WaitOleNotBusy (which the following wait macro takes you
	// to) and look at the user screen between message
	// dispatching.  You should see pbrush gradually paint
	// itself as it processes the messages which which Ole
   // generates for it.  In general, if an Ole Server does not
	// behave properly when creating an object, a likely cause is a
	// problem with the message dispatch loop.

	wait( ret , lpObject );

	// OleSetHostNames sets the name in the server app.  If this
	// was not called, pbrush would display a string with a bunch
   // of % signs in it.

	ret = OleSetHostNames( lpObject, szAppName, lpszObjectName );
	wait( ret , lpObject );
}

void CMUndo( HWND hwnd )
{
	if ( lpUndoObject )		// we have a previous object
		if ( lpUndoObject != lpObject )	// it is different then the current one
		{
			LPOLEOBJECT lpObjectToDelete = lpObject;
			lpObject = lpUndoObject;
			ret = OleDelete( lpObjectToDelete );
			wait( ret , lpObjectToDelete );
			bObjectLoaded = bUndoObjectLoaded;

			InvalidateRect( hwnd, NULL, TRUE );
         UpdateWindow( hwnd );
		}
}

void CMCut(HWND hwnd )
{
	CMCopy(hwnd);
	CloseCurrentOle(hwnd);
}

void CMCopy( HWND hwnd )
{
	if ( OpenClipboard( hwnd ) && EmptyClipboard() )
	{
		ret = OleCopyToClipboard( lpObject );
		check( ret );
		CloseClipboard();
	}
}

void BackupObject(void)
{
	if ( lpObject )
   {
		ret = OleClone( lpObject, (LPOLECLIENT)pOleClient, lhClientDoc, szAppName, &lpUndoObject );
		wait( ret, lpObject );
		lstrcpy( lpszLastObjectName, lpszObjectName );
		lstrcpy( lpszObjectName , GetNextObjectName() );
		bUndoObjectLoaded = bObjectLoaded;
	}
}


void CMPaste( HWND hwnd )
{
	if ( OpenClipboard( hwnd ) )
	{
		BackupObject();

		ret = OleCreateFromClip( "StdFileEditing",
			(LPOLECLIENT)pOleClient,
			lhClientDoc,
			lpszObjectName,
			&lpObject,
			olerender_draw,
			0 );
		check( ret );

		bObjectLoaded = TRUE;

		CloseClipboard();
		PostMessage( hwnd , WM_U_REDRAW, 0, 0L );
	}
}

LPSTR GetNextObjectName(void)
{
	static char buffer[ MAXPATH ];
	wsprintf( buffer, "object #%03d", nNextObjectNum++ );
	return buffer;
}

void Paint( HWND hwnd, HDC hdc )
{
	LPOLEOBJECT lpObjectToDraw = NULL;

	if ( bObjectLoaded )
		lpObjectToDraw = lpObject;
	else if ( lpUndoObject )
		lpObjectToDraw = lpUndoObject;

	if ( lpObjectToDraw ) {
		RECT rect;
		GetClientRect( hwnd, &rect );

		// Tips for OleDraw:
		// OleDraw will return OLE_ERROR_OBJECT if the
      // object to be drawn is invalid.  A case such as this,
      // could happen if the Paint function does not get
		// informed about the object being deleted, or not having
		// yet been created.
		// You can also get OLE_ERROR_BLANK if you draw an object
		// which has not been stored.  A time when this could happen
      // is when trying use OleDraw on the object after using OleCreate
		// to make it, but not yet using the Servers update command
		// to save the object with Ole.
		ret = OleDraw( lpObjectToDraw , hdc, &rect , NULL, 0 );
		wait( ret, lpObjectToDraw );
	}
}

void WMCreate( HWND hwnd )
{
	ret = OLE_OK;
	lhClientDoc = 0;

	bObjectLoaded = FALSE;
	bUndoObjectLoaded = FALSE;

	pOleClient = NULL;
	lpObject = NULL;
	lpUndoObject = NULL;

	lstrcpy( lpszDocName , "noname.ole" );
	*lpszLastObjectName = 0;
	*lpszObjectName = 0;
	bDefDocName = TRUE;
	RegisterClientDoc();
	pOleClient = (TOleClient *) malloc( sizeof( TOleClient ));
	if ( pOleClient )
		MakeTOleClient( pOleClient, hwnd , hInst );
	else memerror(  );
}


void RegisterClientDoc(void)
{
	// Tip for using OleRegisterClientDoc:
   // If the lpszDoc parameter passed to
	// OleRegisterClientDoc is not valid, (lpszPatnName in this case),
	// one can get an OLE_ERROR_ADDRESS error, and the ClientDoc
	// handle will not be valid.

	ret = OleRegisterClientDoc(
			szAppName,
			lpszDocName,
			0,
			&lhClientDoc );
	check( ret );
}

void RegFileName( LPSTR FileName )
{
	lstrcpy( lpszDocName , FileName );
	ret = OleRegisterClientDoc( szAppName,
			lpszDocName ,
			0,
			&lhClientDoc );
	check ( ret );
}

void CMActivate( HWND hwnd )
{
	RECT rect;
	BackupObject();
	GetClientRect( hwnd, &rect );
	ret = OleActivate( lpObject , OLEVERB_PRIMARY, TRUE, TRUE ,
			hwnd , &rect );
	wait ( ret, lpObject );
	PostMessage( hwnd , WM_U_REDRAW, 0, 0L );
}

void WMInitMenu( HWND hwnd , WORD wParam )
{
	WORD wEnableUndo;
	HMENU hMenu = (HMENU)wParam;

	if ( (lpObject != lpUndoObject) && ( lpUndoObject != NULL ))
		wEnableUndo = MF_ENABLED;
	else wEnableUndo = MF_GRAYED;
	EnableMenuItem( hMenu, CM_UNDO     , wEnableUndo );
	EnableMenuItem( hMenu, CM_COPY     , ( bObjectLoaded ? MF_ENABLED : MF_GRAYED ));
	EnableMenuItem( hMenu, CM_CUT      , ( bObjectLoaded ? MF_ENABLED : MF_GRAYED ));
	ret = OleQueryCreateFromClip( "StdFileEditing", olerender_draw, 0 );
	EnableMenuItem( hMenu, CM_PASTE    , (( ret == OLE_OK ) ? MF_ENABLED : MF_GRAYED ));
	EnableMenuItem( hMenu, CM_ACTIVATE , ( bObjectLoaded ? MF_ENABLED : MF_GRAYED ));
	EnableMenuItem( hMenu, CM_CLEAR    , ( bObjectLoaded ? MF_ENABLED : MF_GRAYED ));

	DrawMenuBar( hwnd );
}

void CMClear( HWND hwnd )
{
	CloseCurrentOle(hwnd);
}

void CMAbout( HWND hwnd )
{
    MessageBox( hwnd , "OLE Client Example\nCopyright (c) 1992 Borland International, Inc.", szAppName, MB_OK );
}

void CloseCurrentOle( HWND hwnd )
{
	bObjectLoaded = FALSE;

	if ( lpObject ) {
		ret = OleDelete( lpObject );
		wait( ret , lpObject );
	}
	if ( lpUndoObject ) {
		ret = OleDelete( lpUndoObject );
		wait( ret , lpObject );
	}
	lpObject = lpUndoObject = NULL;

	InvalidateRect( hwnd , NULL, TRUE );
	UpdateWindow( hwnd );
}

void memerror(void)
{
	MessageBox( GetFocus() , "error allocating memory", szAppName, MB_OK );
	exit(1);
}

// General Help:
// Question: Right after closing down the server, the only part of the
//   object repainted in my application is that portion of the window that
//   was covered with the server program, what's going on?
// Answer: When the server closes, Windows sends you a paint message to
//	 update that portion of your window which the server covered up.  It
//	 has a clipping region defined which keeps all the previous visible
//	 portions of your client window.  To repaint the whole window after
//	 a server closes, respond to the OLE_CHANGED notification in your
//	 callback, and force the whole window to be updated there.  Calling
//	 InvalidateRect would be appropriate.

// _lopen will not create a new file.  Use OpenFile( .. , OF_CREATE ) or
// _lcreate for this.

