/*	GENERIC.C - Source for Automation Demo Code in C
 *  Copyright (C) 1991-1994 Shapeware Corporation. All rights reserved.
 */
 
#include <windows.h>
#include <ole2.h> 
#include <dispatch.h>  

#include "ivisauto.h"
#include "ivisreg.h"
#include "resource.h"

#ifdef _DLL
#define VAOCB __loadds CALLBACK
#else
#define VAOCB CALLBACK
#endif

/*
 *	Forward prototype of dialog procedure.
 */
 
BOOL VAOCB AboutDlgProc(
	HWND,
	UINT,
	WPARAM,
	LPARAM);

/**************************************************************************
 *+ RunDemo
 *
 *  This demo will drive Visio through OLE Automation in C and draws a
 *	simple orgizational chart on a Visio page.  Notice that the properties
 *	and methods are no different than those called through VB, it's just
 *	that you have the overhead of VARIANTs and pointer validation that VB
 *	does for you.
 *
 *	Comments are spread throughout the code to explain the how and why
 *	of programming Visio through OLE Automation.  Some of these comments
 *	are directed at Visual Basic programmers who are making the transition
 *	from OLE Automation under VB to C/C++.
 */

int RunDemo(void)
	{
	/* Declarations Section
	 *
	 * INTERFACES
	 *
	 *	In this example we're going to work with many different Visio
	 *	interfaces so we need a pointer to each one.  If you're coming from
	 *	the Visual Basic world you need to get used to the idea of 
	 *	releasing interfaces and verifying interface pointers before
	 *	invoking a method or property.  Visual Basic automatically does
	 *	a release for you whenever Object variables go out of scope or
	 *	whenever you do an explicit Set obj = Nothing.  However in C/C++
	 *	you must always release an interface when done with it and, when
	 *	given an interface, must verify that the pointer is != NULL before
	 *	attempting to invoke properties or methods on it.  In VB this is
	 *	equivalent to asking (obj Is Nothing).
	 *
	 *	Notice that we use the prefix ipIV for all our interface pointers.
	 *	ip stands for "Interface Pointer" and IV stands for
	 *	"Interface-Visio".  After that we put a descriptive name of the
	 *	interface we're pointing to.
	 *
	 *	An important thing to notice is that for every interface pointer
	 *	declared, there is a corresponding Release() call for it in the 
	 *	clean up (CU:) section at the bottom of RunDemo.  This is to
	 *	ensure that we release our lock upon the interface.  For more
	 *	information on the Release function see the OLE documentation.
	 *
	 * BSTRs & VARIANTs
	 *
	 *	Just like in Visual Basic, we have to pass parameters to our
	 *	properties and methods.  Because we are talking to OLE we need to 
	 *	pass strings as BSTRs.  These are allocated/deallocated using the
	 *	SysAllocString and SysFreeString functions provided by OLE.
	 *	Furthermore, properties and methods that can take more than one
	 *	data type for the same parameter will use the VARIANT type.  These
	 *	must be handled through the VariantInit, VariantClear functions and
	 *	the V_VT and V_type macros.  See the OLE documentation for more
	 *	information on manipulating both the BSTR and VARIANT data types.
	 */
	 
	LPVISIOAPPLICATION ipIVApp     = NULL;
	LPVISIODOCUMENTS   ipIVDocs    = NULL;
	LPVISIODOCUMENT    ipIVDoc     = NULL;
	LPVISIOPAGES       ipIVPages   = NULL;
	LPVISIOPAGE        ipIVPage    = NULL;
	LPVISIOSHAPE       ipIVShape   = NULL;
	LPVISIOSHAPE       ipIVShape1  = NULL;
	LPVISIOMASTERS     ipIVMasters = NULL;
	LPVISIOMASTER      ipIVMaster  = NULL;
	LPVISIODOCUMENT    ipIVStencil = NULL;
	LPVISIOCELL        ipIVCell    = NULL;
	LPVISIOCELL        ipIVCell1   = NULL;
	BSTR bstr = NULL;       
	VARIANT variant;
	HRESULT hResult;
	
	/*
	 * STARTING VISIO
	 *
	 *	A special utility file is included called IVISREG that does
	 *	the work of getting a Visio application interface.  This file
	 *	is equivalent to the VISREG.BAS file for Visual Basic.
	 *
	 *	To see how it works open the file and examine it's function calls.
	 *	We use a simple, one parameter function that gets the active
	 *	instance if one exists or creates a new one if one is not running.
	 */
	 
	if ( VAO_SUCCESS != vaoGetObject(&ipIVApp) )
		goto CU;
		
	/*
	 *	Next we start the main work of drawing the orgchart.  First
	 *	we retrieve the Documents collection and try to add a blank
	 *	document based on the ORGCHART.VST template.
	 *
	 *	To demonstrate an invoke let's look at how we retrieve the 
	 *	Documents property of the Application interface.  First off, 
	 *	every property and method returns an HRESULT and if we want
	 *	to see if the call passed or failed we use the SUCCEEDED macro to
	 *	examine the SCODE contained in the HRESULT. If the call fails then
	 *	we automatically go to clean up since we can't continue.
	 *
	 *	If the call succeeds we're still not done.  Next we have to check
	 *	that another interface pointer was returned and, if it was not,
	 *	we go to clean up once again.  This is the standard convention
	 *	that we'll use throughout the rest of the examples:
	 *
	 *		1) Make sure the call works
	 *		2) Verify an interface, if any, was returned.
	 *
	 *	Remember to check the pointers returned because if a call fails
	 *	and you try to call a method or property on a NULL pointer an
	 *	error will occur.
	 */
	 
	// Get App.Documents property
	
	hResult = ipIVApp->lpVtbl->Documents(ipIVApp, &ipIVDocs);
	
	if ( !SUCCEEDED(hResult) )
		goto CU;
		
	if ( !ipIVDocs )
		goto CU;        
    
    /*
     *	For this call notice that we have to allocate a BSTR.  We
     *	always allocate the BSTR before the call and then immediately
     *	after it.  This way we're guaranteed we don't have a memory leak
     *	However, this limits us from jumping straight to clean up as in
     *	the previous example, thus we store the HRESULT in a temporary
     *	variable and check it after deallocating the BSTR.
     */
     
    // Add a blank document with Docs.Add
    
    bstr = SysAllocString("sampler.vst");
    hResult = ipIVDocs->lpVtbl->Add(ipIVDocs, bstr, &ipIVDoc);
	SysFreeString(bstr);
	
    if ( !SUCCEEDED(hResult) || !ipIVDoc )
		goto CU;
    
    // Get blank document Pages collection using Doc.Pages
    
    hResult = ipIVDoc->lpVtbl->Pages(ipIVDoc, &ipIVPages);
    
    if ( !SUCCEEDED(hResult) || !ipIVPages )
		goto CU;
	 
	/*
	 *	For this call we need to use a variant for the Pages.Item property.
	 *	Because Pages.Item can take either a string containing the page's
	 *	name or an integer specifying the page's index we need to put pass
	 *	a VARIANT.  Notice that we first init the variant, then set the
	 *	type of it with the V_VT macro to I2, meaning 2 byte integer.  Then
	 *	we use the V_I2 macro to set the variant's value.  After that we
	 *	can make the call.  Once the call has returned, though, we need
	 *	to clear the variant.  Read up on VARIANTs in the OLE 2.0 Programmer's
	 *	Reference to see exactly how to work with them and for a listing of
	 *	their support functions and macros.
	 */
	 
	// Get first page of blank document using Pages.Item
	
	VariantInit(&variant);
	V_VT(&variant) = VT_I2;
	V_I2(&variant) = 1;
	hResult = ipIVPages->lpVtbl->Item(ipIVPages, variant, &ipIVPage);
	VariantClear(&variant);
	
	if ( !SUCCEEDED(hResult) || !ipIVPage )
		goto CU;
    
	// Get Document interface for orgchart stencil using Docs.Item
	
	VariantInit(&variant);
	V_VT(&variant) = VT_BSTR;
	V_BSTR(&variant) = SysAllocString("sampler.vss");;
	hResult = ipIVDocs->lpVtbl->Item(ipIVDocs, variant, &ipIVStencil);
	VariantClear(&variant);
	
	if ( !SUCCEEDED(hResult) || !ipIVStencil )
		goto CU;
    
    // Retrieve orgchart Masters interface using Doc.Masters
    
    hResult = ipIVStencil->lpVtbl->Masters(ipIVStencil, &ipIVMasters);
    
    if ( !SUCCEEDED(hResult) || !ipIVMasters )
		goto CU;
	
	// Get Executive master using Masters.Item
	
	VariantInit(&variant);
	V_VT(&variant) = VT_BSTR;
	V_BSTR(&variant) = SysAllocString("Executive");;
	hResult = ipIVMasters->lpVtbl->Item(ipIVMasters, variant, &ipIVMaster);
	VariantClear(&variant);
	
	if ( !SUCCEEDED(hResult) || !ipIVMaster )
		goto CU;
    
	// Drop Executive master using Page.Drop
	
	hResult = ipIVPage->lpVtbl->Drop(ipIVPage,
									 (LPUNKNOWN)ipIVMaster,
									 6.0,
									 6.0,
									 &ipIVShape );
	
	if ( !SUCCEEDED(hResult) || !ipIVShape )
		goto CU;
	    
	/*
	 *	At this point we want to re-use the one master interface we
	 *	declaraed.  To do it we must first release and then set it
	 *	to NULL.  Not setting it to NULL could cause serious problems later
	 *	so remember to do this, especially when iterating collections.
	 */
	 
	if ( ipIVMaster )
		{
		ipIVMaster->lpVtbl->Release(ipIVMaster);
		ipIVMaster = NULL;
	    }
	    
	// Get Position 1 master using Masters.Item
	
	VariantInit(&variant);
	V_VT(&variant) = VT_BSTR;
    V_BSTR(&variant) = SysAllocString("Position");;
	hResult = ipIVMasters->lpVtbl->Item(ipIVMasters, variant, &ipIVMaster);
	VariantClear(&variant);
	
	if ( !SUCCEEDED(hResult) || !ipIVMaster )
		goto CU;
    
	// Drop Position 1 master using Page.Drop
	
	hResult = ipIVPage->lpVtbl->Drop(ipIVPage,
									 (LPUNKNOWN)ipIVMaster,
									 3.0,
									 3.0,
									 &ipIVShape1 );
									 
	if ( !SUCCEEDED(hResult) || !ipIVShape1 )
		goto CU;
    
	// Get Executive's Connection point using Shape.Cells
	
	bstr = SysAllocString("Connections.X4");
	hResult = ipIVShape->lpVtbl->Cells(ipIVShape, bstr, &ipIVCell);
	SysFreeString(bstr);
	
	if ( !SUCCEEDED(hResult) || !ipIVCell )
		goto CU;
    
	// Get Position 1's Control handle using Shape.Cells
    
	bstr = SysAllocString("Controls.X1");
	hResult = ipIVShape1->lpVtbl->Cells(ipIVShape1, bstr, &ipIVCell1);
	SysFreeString(bstr);
	
	if ( !SUCCEEDED(hResult) || !ipIVCell )
		goto CU;
    
   // Glue Position 1 to Executive using Cell.Glue
   
   ipIVCell1->lpVtbl->GlueTo(ipIVCell1, ipIVCell);
	
CU:
	/*
	 * CLEANUP
	 *
	 *	The clean up section is meant to release any interface pointers
	 *	that are outstanding at this point.  Our example does not need to
	 *	keep any around for later use or to pass back to our caller so we
	 *	release every one.  However, if we were going to, say, pass one
	 *	back to a calling function as the call vaoGetObject does we would
	 *	not want to release it.
	 */
	 
	if ( ipIVCell1 )
		ipIVCell1->lpVtbl->Release(ipIVCell1);
	if ( ipIVCell )
		ipIVCell->lpVtbl->Release(ipIVCell);
	if ( ipIVStencil )
		ipIVStencil->lpVtbl->Release(ipIVStencil);
	if ( ipIVMaster )
		ipIVMaster->lpVtbl->Release(ipIVMaster);
	if ( ipIVMasters )
		ipIVMasters->lpVtbl->Release(ipIVMasters);
	if ( ipIVShape1 )
		ipIVShape1->lpVtbl->Release(ipIVShape1);
	if ( ipIVShape )
		ipIVShape->lpVtbl->Release(ipIVShape);
	if ( ipIVPage )
		ipIVPage->lpVtbl->Release(ipIVPage);
	if ( ipIVPages )
		ipIVPages->lpVtbl->Release(ipIVPages);
	if ( ipIVDoc )
		ipIVDoc->lpVtbl->Release(ipIVDoc);
	if ( ipIVDocs )
		ipIVDocs->lpVtbl->Release(ipIVDocs);
	if ( ipIVApp )
		ipIVApp->lpVtbl->Release(ipIVApp);
	
	return 0;
	}
	

/***********************************************************************
 *+ AboutDlgProc
 *
 */
#ifdef __BORLANDC__
#pragma argsused
#endif 
BOOL VAOCB AboutDlgProc(
	HWND hDlg,
	UINT msg,
	WPARAM wParam,
	LPARAM lParam)
	{
	
    switch (msg)
    	{
    	case WM_COMMAND:
        	switch (wParam)
        		{
				case IDOK:
        		case IDCANCEL:
        			EndDialog(hDlg, TRUE);
            		return (TRUE);
				}
    	}
    
    return (FALSE);
	}


/***********************************************************************
 *+ ShowAboutDialog
 *
 *	Displays the help about dialog.
 */
 
void ShowAboutDialog(HINSTANCE hInstance)
	{
	FARPROC lpProcDlg = NULL;
	
    lpProcDlg = MakeProcInstance((FARPROC)AboutDlgProc, hInstance);

	DialogBox(hInstance, "ABOUTBOX", NULL, lpProcDlg);
       	
	FreeProcInstance(lpProcDlg);
	};
	