// ObjectWindows - (C) Copyright 1992 by Borland International
//
// oleclnt.cpp

// An example of an Ole Client program using OWL.  It demonstrates
// a simple use of Client Ole functions, and using C++ classes to
// wrap around the Ole structures.

// 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.0 or 3.1).

#include <owl.h>
#include <listbox.h>
#include <string.h>
#include <edit.h>
#include <bwcc.h>
#include <filedial.h>
#include <ole.h>
#include <shellapi.h>
#pragma hdrstop

#include "oleclnte.h"
#include "oleclntr.h"
#include "oleclnt.h"

// static class data members.
LPOLECLIENTVTBL TOwlClient::lpClientVtbl = NULL;
int             TOleDocWindow::nNextObjectNum = 0;

void TOleApp::InitInstance()
{
	TApplication::InitInstance();

	vcfLink = RegisterClipboardFormat( "ObjectLink" );
	vcfNative = RegisterClipboardFormat( "Native" );
	vcfOwnerLink = RegisterClipboardFormat( "OwnerLink" );
	// comments in owlole.h mention these ole clipboard formats
}


// passes the notification on to the OWL Object which
// is keeping track of this Ole Object

int FAR PASCAL _export StdCallBack(LPOLECLIENT lpClient,
	OLE_NOTIFICATION notification,
	LPOLEOBJECT lpObject )
{
	return (( PTOwlClient )lpClient)->TOleDocWindowThis->
		CallBack( lpClient ,
		notification,
		lpObject );
}

// if no vtbl has been constructed, make one.  Then initialize
// the vtbl class instance member to point to it.  When smart
// call backs are used for the compiler entry/exit code,
// MakeProcInstance is not needed.  This code will not call
// MakeProcInstance when the default value of hInst is used (it
// defaults to being 0).  If you want to use Procedure instances
// for the callbacks, construct a TOwlClient with a valid hInst.

TOwlClient::TOwlClient( PTOleDocWindow owner , HINSTANCE hInst )
{
	TOleDocWindowThis = owner;
	if ( !lpClientVtbl )
	{
		lpClientVtbl = new OLECLIENTVTBL;
		if (  hInst == 0 ) {
			lpClientVtbl->CallBack = StdCallBack;
		} else {
			lpClientVtbl->CallBack = (TCallBack)MakeProcInstance( (FARPROC)StdCallBack,
					hInst );
		}
	}
	lpvtbl = lpClientVtbl;
}

void TOleDocWindow::WMURedraw( RTMessage )
{
	bObjectLoaded = TRUE;
	InvalidateRect( HWindow, NULL, TRUE );
	UpdateWindow( HWindow );
}

#pragma argsused
int TOleDocWindow::CallBack( LPOLECLIENT      lpOleClient ,
							 OLE_NOTIFICATION oleNot,
							 LPOLEOBJECT lpOleObject )
{
	switch ( oleNot ) {
		case OLE_CHANGED:
		case OLE_SAVED:
			PostMessage( HWindow , 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;
}

void TOleDocWindow::CMAbout( RTMessage )
{

	MessageBox( HWindow , "OLE Client Program\nWritten using ObjectWindows\nCopyright (c) 1992 Borland",
			GetApplication()->Name, MB_OK );
}

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

	lstrcpy( lpszObjectName, GetNextObjectName() );

	ret = OleCreate( "StdFileEditing",
			(LPOLECLIENT)pOwlClient,
			"PBRUSH",
			lhClientDoc,
			GetApplication()->Name,
			&lpObject,
			olerender_draw,
			0 );

	// Creating an Ole Object is a 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 a 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 % sings in it.

	ret = OleSetHostNames( lpObject, GetApplication()->Name, lpszObjectName );
	wait( ret , lpObject );
}

void TOleDocWindow::CMUndo( RTMessage msg)
{
	if ( lpUndoObject )		// we have a previous object
		if ( lpUndoObject != lpObject )	// it is different then the current one
		{
			LPOLEOBJECT lpObjectToDelete = lpObject;
			lpObject = lpUndoObject;
			lpUndoObject = NULL;
			ret = OleDelete( lpObjectToDelete );
			wait( ret , lpObjectToDelete );
			bObjectLoaded = bUndoObjectLoaded;
			WMURedraw( msg );
		}

}

void TOleDocWindow::CMCut( RTMessage msg)
{
	CMCopy( msg );
	CloseCurrentOle();
}

void TOleDocWindow::CMCopy( RTMessage )
{
	if ( OpenClipboard( HWindow ) && EmptyClipboard() )
	{
		ret = OleCopyToClipboard( lpObject );
		check( ret );
		CloseClipboard();
	}
}

void TOleDocWindow::BackupObject()
{
	if ( lpObject )
	{
		ret = OleClone( lpObject, (LPOLECLIENT)pOwlClient,
				lhClientDoc, GetApplication()->Name, &lpUndoObject );
		wait( ret, lpObject );
		lstrcpy( lpszLastObjectName, lpszObjectName );
		lstrcpy( lpszObjectName , GetNextObjectName() );
		bUndoObjectLoaded = bObjectLoaded;
	}
}


void TOleDocWindow::CMPaste( RTMessage )
{
	if ( OpenClipboard( HWindow ) )
	{
		BackupObject();

		lstrcpy( lpszObjectName, GetNextObjectName() );

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

		ret = OleSetHostNames( lpObject, GetApplication()->Name, lpszObjectName );
		wait( ret , lpObject );

		bObjectLoaded = TRUE;

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

}

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

void TOleDocWindow::Paint( HDC hdc, PAINTSTRUCT _FAR &)
{
	LPOLEOBJECT lpObjectToDraw = NULL;

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

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

		// Tips for OleDraw:
		// OleDraw will return OLE_ERROR_OBJECT if the
		// object to be drawn is invalid.  In a case like this,
		// that 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 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 );
	}
}

TOleDocWindow::TOleDocWindow( PTWindowsObject parent, LPSTR title )
	: TWindow( parent, title )
{
	ret = OLE_OK;
	lhClientDoc = 0;

	bObjectLoaded = FALSE;
	bUndoObjectLoaded = FALSE;
	bUndoObjectLoaded = FALSE;

	pOwlClient = NULL;
	lpObject = NULL;
	lpUndoObject = NULL;

	strcpy( lpszDocName , "noname.ole" );
	*lpszLastObjectName = 0;
	*lpszObjectName = 0;
	bDefDocName = TRUE;
}

void TOleDocWindow::SetupWindow() {
	TWindow::SetupWindow();
	RegisterClientDoc();
	pOwlClient = new TOwlClient( this );

}


void TOleDocWindow::RegisterClientDoc() {
	// 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(
			GetApplication()->Name,
			lpszDocName,
			0,
			&lhClientDoc );
	check( ret );
}


void TOleDocWindow::ShutDownWindow()
{
	CloseCurrentOle();
	if ( pOwlClient ) delete pOwlClient;
	TWindow::ShutDownWindow();
}


void TOleDocWindow::RegFileName( LPSTR FileName )
{
	lstrcpy( lpszDocName , FileName );
	ret = OleRegisterClientDoc( GetApplication()->Name,
			lpszDocName ,
			0,
			&lhClientDoc );
	check ( ret );
}

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

void TOleDocWindow::WMInitMenu( RTMessage msg )
{
	HMENU hMenu = (HMENU)msg.WParam;
	WORD wEnableUndo;

	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( HWindow );
}

LPSTR TOleDocWindow::GetClassName() { return "OLEDOCWINDOW"; }

void TOleDocWindow::GetWindowClass(WNDCLASS _FAR &wc )
{
	TWindow::GetWindowClass( wc );
	wc.lpszMenuName = "MENU_DOCWINDOW";
}

void TOleDocWindow::CMClear( RTMessage )
{
	CloseCurrentOle();
}

void TOleDocWindow::CloseCurrentOle()
{
	// assume saved
	if ( lpObject ) {
		ret = OleDelete( lpObject );
		wait( ret , lpObject );
	}
	if ( lpUndoObject ) {
		ret = OleDelete( lpUndoObject );
		wait( ret , lpObject );
	}
	lpObject = lpUndoObject = NULL;

	bObjectLoaded = FALSE;

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


void TOleApp::InitMainWindow()
{
  MainWindow = new TOleDocWindow(NULL, "OWL OLE Application" );
}



int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
		   LPSTR lpCmd, int nCmdShow)
{
	TOleApp OleApp ("OleApp", hInstance, hPrevInstance,
		lpCmd, nCmdShow);
	OleApp.Run();
	return (OleApp.Status);
}

// 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, whats 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 appropiate.

