#include "stdafx.h"
#include <commctrl.h>

// And remember to add comctl32.lib to project settings
#include "resource.h"
#include <stdio.h>	// for sprintf
#include <stdlib.h> // for atof

// For our support functions
#include "PNCmnControls.h"

// For SHGetFileIngo
#include <shellapi.h>

// Note: For the Progress Dialog, you must add this module, as well as the
// IDD_PROGRESSDLG dialog resource to any new projects.

// PNProgressDlgProc - Message Procedure for Progress Dialog
// Parameters:
//     hwndDlg - Window handle for dialog
//     uMsg - Message to process
//     wParam - wParam for message
//     lParam - lParam for message
// Returns: Depends on value of uMsg
LRESULT CALLBACK PNProgressDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam  )
{
	
	if( uMsg == WM_COMMAND && LOWORD(wParam)==IDCANCEL && HIWORD(wParam)==BN_CLICKED )
	{
		if( MessageBox( hwndDlg, "Are you sure you want to cancel?", "Confirmation", MB_ICONQUESTION|MB_YESNO ) == IDYES )
			SetWindowLong( hwndDlg, GWL_USERDATA, TRUE );
	}
	return( FALSE );
}

// PNProgressDlgPopup - Display and initialize the progress dialog
// Parameters:
//     hParent - Window handle for parent window
//     Min - Minimum value for Progress Bar
//     Max - Maximum value for Progress Bar
// Returns: Window handle for the progress dialog
HWND PNProgressDlgPopup( HWND hParent, int Min, int Max )
{
	// Create the progress dialog
	HWND hDlg=CreateDialog( GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_PROGRESSDLG), hParent, (DLGPROC)PNProgressDlgProc );
	if( hDlg )
	{
		// Make the progress dialog visible (modeless approach)
		ShowWindow( hDlg, SW_SHOWNORMAL );
		// Disable the parent (main) window
		EnableWindow( hParent, FALSE );
		// Set the Progress Bar min and Max values
		SendDlgItemMessage( hDlg, IDC_DLGPROGRESS, PBM_SETRANGE32, Min, Max );
	}
	return( hDlg );
}

// PNProgressDlgSetPos - Sets the Progress Bar position
// Parameters:
//     hDlg - Window handle for the progress dialog
//     Pos - New position for the Progress Bar
//     Label - Option label to display in the progress bar
// Returns: TRUE if position set, or FALSE if unable to set because user hit Cancel
BOOL PNProgressDlgSetPos( HWND hDlg, int Pos, const char* Label )
{
	MSG Msg;

	// Message Pump: Give the user the chance to hit the Cancel button
	while( PeekMessage( &Msg, hDlg, 0, 0, PM_NOREMOVE ) )
	{
		GetMessage( &Msg, hDlg, 0, 0 );
		DispatchMessage( &Msg );
	}

	// If the Cancel option was chosen, then return FALSE
	if( GetWindowLong( hDlg, GWL_USERDATA ) != 0 )
		return( FALSE );

	// Otherwise, set the progress bar position, and optionally the STATIC control text.
	HWND hProg = GetDlgItem( hDlg, IDC_DLGPROGRESS );
	SendMessage( hProg, PBM_SETPOS, Pos, 0 );
	if( Label )
		SetDlgItemText( hDlg, IDC_LABEL, Label );
	
	// And return FALSE (meaning, user didn't hit the Cancel option this time)
	return( TRUE );

}

// PNProgressDlgCanceled - Determines if user hit Cancel for Progress Dialog
// Parameters:
//     hDlg - Window handle for the progress dialog
// Returns: TRUE if user hit Cancel button, or FALSE if not
BOOL PNProgressDlgCanceled( HWND hDlg )
{
	return( GetWindowLong( hDlg, GWL_USERDATA ) ? TRUE : FALSE );
}

// PNProgressDlgClose - Closes the progress dialog and re-enables parent
// Parameters:
//     hDlg - Window handle for the progress dialog
// Returns - TRUE if user had Canceled the dialog, FALSE if they did not.
BOOL PNProgressDlgClose( HWND hDlg )
{
	// Get the Canceled status of the dialog, for the return value
	BOOL Ret = PNProgressDlgCanceled( hDlg );
	// Reneable the parent window
	EnableWindow( GetParent( hDlg ), TRUE );
	// Destroy this progress dialog window
	DestroyWindow( hDlg );
	return( Ret );
}

// PNSetSliderRange - Sets a slider range and pagesize
// Parameters:
//     hWnd - Handle to slider window to set
//     Min - Minimum range value for slider control
//     Max - Maximum range value for slider control
//     Redraw - TRUE or FALSE, to redraw the control when set
// Note: The pagesize and tick frequency are calculated as 1/5 and 1/10th
//     if the range, automatically by this function
void PNSetSliderRange( HWND hWnd, int Min, int Max, BOOL Redraw )
{
	// Set minimum and maximum range values
	SendMessage( hWnd, TBM_SETRANGE, Redraw, MAKELONG(Min,Max) );
	
	int Step = (Max-Min) / 5;
	// Set the typical page size:
	SendMessage( hWnd, TBM_SETPAGESIZE, 0, Step );

	// If it has tick mark style, adjust tick mrk gaps as well
	if( GetWindowLong( hWnd, GWL_STYLE ) & TBS_AUTOTICKS )
		SendMessage( hWnd, TBM_SETTICFREQ, (Max-Min)/10, 0 );

}


// List View controls support

// PNListViewInsertColumn- Inserts a new column for a List View control
// Parameters:
//     hWnd - Handle to list view to add column to
//     Column - Column number to be added
//     Header - Text for column header
//     Percent - Percentage of window header should occupy
// Note: This function should only be called for a list view control that
//     has been set to the 'Report' style property.
int PNListViewInsertColumn( HWND hWnd, int Column, char* Header, int Percent )
{
	RECT Client;
	GetClientRect( hWnd, &Client );
	int Width = (int)(Client.right * (Percent/100.0));

	LVCOLUMN lvColumn;
	lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM|LVCF_WIDTH;
	lvColumn.fmt=LVCFMT_LEFT;
	lvColumn.pszText = Header;
	lvColumn.iSubItem = Column;
	lvColumn.cx = Width;

	return( ListView_InsertColumn( hWnd, Column, &lvColumn ) );

}

// PNListViewInsertItem - Inserts a new item (row) for a List View control
// Parameters:
//     hWnd - Handle to list view to add item to
//     Row - Row to be inserted at (-1 means at end of list)
//     Text - Text to be inserted (appears in first column)
//     Image - Image index into image list control for list view
// Returns: Index (row) where item was inserted
int PNListViewInsertItem( HWND hWnd, int Row, char* Text, int Image )
{
	LVITEM lvItem;

	if( Row == -1 )
		Row = ListView_GetItemCount( hWnd );

	lvItem.mask = LVIF_TEXT;
	if( Image >=0 )
		lvItem.mask |= LVIF_IMAGE;
	lvItem.iItem = Row;
	lvItem.iSubItem = 0;
	lvItem.pszText = Text;
	lvItem.iImage = Image;

	return( ListView_InsertItem( hWnd, &lvItem ) );
}

// PNListViewDeleteColumns - Delets all columns from a listview control
// Parameters:
//     hWnd - Handle to list view to remove columns from
void PNListViewDeleteColumns( HWND hWnd )
{
	HWND Header = ListView_GetHeader( hWnd );
	int Columns = Header_GetItemCount( Header );

	while( Columns-- )
		ListView_DeleteColumn( hWnd, 0 );
}

// PNListViewSetItemData - Sets Item Data for a specific item
// Parameters:
//    hWnd - Handle of the List View to set item data for
//    Index - Row or Item to set the idem data for
//    Data - 32 bit value to store
// Returns: TRUE if succesful, FALSE if not
BOOL PNListViewSetItemData( HWND hWnd, int Index, int Data )
{
	LVITEM lvi;
	lvi.mask = LVIF_PARAM;
	lvi.iItem = Index;
	lvi.lParam = Data;
	return ListView_SetItem( hWnd, &lvi );
}

// PNListViewGetItemData - Retrieves Item Data for a specific item
// Parameters:
//    hWnd - Handle of the List View to get item data for
//    Index - Row or Item to get the item data for
// Returns: The item data value
int PNListViewGetItemData( HWND hWnd, int Index )
{
	LVITEM lvi;
	lvi.mask = LVIF_PARAM;
	lvi.iItem = Index;
	ListView_GetItem( hWnd, &lvi );
	return( (int)lvi.lParam );
}


// PNGetSysImageList - Retrieves the Image list handle used by the Window system
// Returns: The HIMAGELIST handle
// Note: The returned image list handle is shared between windows applications.  Do
//       not destroy it.
HIMAGELIST PNGetSysImageList( BOOL LargeImages )
{
	char Drives[128];
	SHFILEINFO shFileInfo;

	// Get list of drives on the system
	GetLogicalDriveStrings( sizeof(Drives), Drives );

	HIMAGELIST hImageList;

	// Call SHGetFileInfo to retrieve the system image list
	hImageList = (HIMAGELIST)SHGetFileInfo( Drives, 0, &shFileInfo, sizeof( shFileInfo ),
		SHGFI_SYSICONINDEX | (LargeImages?SHGFI_LARGEICON:SHGFI_SMALLICON) );
	
	// Return the image list handle
	return( hImageList );	
}

// PNGetPathIcons - Retrieves index into the system image list for a file, folder, or drive
// Parameters:
//     Path - Path of drive, folder, or file to retrieve image for
//     iIcon - Index of icon within the system index (returned)
//     iIconSel - Index of selected icon within the system index (returned)
//     Large - TRUE or FALSE to retrieve the large or small system icon
//     MustSlash - TRUE if the path must end in a slash, false if not
// Returns: TRUE if succesful, false if not
// Note: Files must not end in a slash, and drive letters and folders should.
BOOL PNGetPathIcons( const char* Path, int& iIcon, int& iIconSel, BOOL Large, BOOL MustSlash )
{
	SHFILEINFO shFileInfo;
	char strPath[MAX_PATH];
	int ImageSize = Large ? SHGFI_LARGEICON : SHGFI_SMALLICON;

	// Make sure path has an ending slash or doesn't
	strcpy( strPath, Path );
	if( MustSlash )
	{
		if( strPath[strlen(strPath)]!='\\' && strPath[strlen(strPath)]!='/' )
		{
			if( strchr( strPath, '/' ) )
				strcat( strPath, "/" );
			else
				strcat(strPath, "\\" );
		}
	}
	else 
	{
		if( strPath[strlen(strPath)]=='\\' || strPath[strlen(strPath)]=='/')
			strPath[strlen(strPath)-1] = '\0';
    }

	// Get icons for the path
	if( SHGetFileInfo( strPath, 0, &shFileInfo, sizeof( shFileInfo ), SHGFI_ICON | ImageSize ) == 0 )
		return( FALSE );
	iIcon = shFileInfo.iIcon;
	if( SHGetFileInfo( strPath, 0, &shFileInfo, sizeof( shFileInfo ), 
		SHGFI_ICON | SHGFI_OPENICON | ImageSize ) == 0 )
		return( FALSE );
	iIconSel = shFileInfo.iIcon;
	return( TRUE );
}

// PNListViewInitDrives - Populates a list view control with a list of Drive
//      names and types, and icons
// Parameters:
//      hWnd - Window handle for List View to place drive list in
// Returns: Number of rows (drives) added to list
int PNListViewInitDrives( HWND hWnd )
{
	HIMAGELIST hImageList;
	BOOL LargeSize;

	// Determine image size needed based on style of List View control
	if( GetWindowLong( hWnd, GWL_STYLE ) & (LVS_SMALLICON|LVS_REPORT) )
		LargeSize = FALSE;
	else
		LargeSize = TRUE;

	// Use Windows own image list for drive and file icons
	hImageList = PNGetSysImageList( LargeSize );
	ListView_SetImageList( hWnd, hImageList, LargeSize ? LVSIL_NORMAL : LVSIL_SMALL );


	// Setup ListView appearance
	ListView_DeleteAllItems( hWnd );
	PNListViewDeleteColumns( hWnd );
	PNListViewInsertColumn( hWnd, 0, "Drives", 100 );

	// Determine valid drive letters
	char Drives[128], *pRoot;
	char Volume[64], FileSystem[64];
	char Descr[128];
	DWORD Flags, MaxLength;
	int iIcon, iIconSel;
	int Row=0;
	
	GetLogicalDriveStrings( sizeof(Drives), Drives );

	// Iterate through drive letters, determine icons, and add to list view control
	for( pRoot=Drives; *pRoot; pRoot++ )
	{
		switch( GetDriveType( pRoot ) )
		{
			case DRIVE_UNKNOWN:
				sprintf( Descr, "%c: Unknown", *pRoot );
				break;
			case DRIVE_NO_ROOT_DIR:
				sprintf( Descr, "%c: Can't determine", *pRoot );
				break;
			case DRIVE_REMOVABLE:
				sprintf( Descr, "%c: Removable", *pRoot );
				break;
			case DRIVE_FIXED:
				sprintf( Descr, "%c: Fixed", *pRoot );
				break;
			case DRIVE_REMOTE:
				GetVolumeInformation( pRoot, Volume, sizeof(Volume),
					NULL, &MaxLength, &Flags, FileSystem, sizeof(FileSystem) );
				sprintf( Descr, "%c: Network drive %s (%s)", *pRoot, Volume, FileSystem );
				break;
			case DRIVE_CDROM:
				sprintf( Descr, "%c: CD-ROM", *pRoot );
				break;

			case DRIVE_RAMDISK:
				sprintf( Descr, "%c: RAM Disk", *pRoot );
				break;
		}
		
		// Determine system image for drive
		PNGetPathIcons( pRoot, iIcon, iIconSel, LargeSize, FALSE );
		PNListViewInsertItem( hWnd, Row++, Descr, iIcon );

		while( *pRoot )
			pRoot++;
	}
	return( Row );
}


// PNTreeViewInsertItem - Inserts new item into a tree control
// Parameters:
//     hWnd - Window handle for Tree View to insert into
//     Text - Text for new insertion
//     hParent - HTREEITEM for insertion point, or zero for root
//     hInsertAt - Determines insertion position: TVI_LAST, TVI_FIRST, or TVI_SORT
//     iImageIndex - Index into image list for item, if image list is available. -1 means no image.
//     iSelectedImageIndex - Index into image list for item when selected, if image list is available. -1 means no image.
// Returns: TRUE if succesful, false if not
HTREEITEM PNTreeViewInsertItem( HWND hWnd, LPTSTR Text, HTREEITEM hParent, HTREEITEM hInsertAt, int iImageIndex, int iSelectedImageIndex )
{
	TVINSERTSTRUCT tvInsertStruct;

	// Initialize tvInsertStruct
	tvInsertStruct.hParent = hParent;
	tvInsertStruct.hInsertAfter = hInsertAt;
	tvInsertStruct.item.mask = TVIF_TEXT;
	if( iImageIndex!=-1 )
	{
		tvInsertStruct.item.iImage = iImageIndex;
		tvInsertStruct.item.mask |= TVIF_IMAGE;
	}
	if( iSelectedImageIndex !=-1 )
	{
		tvInsertStruct.item.mask |= TVIF_SELECTEDIMAGE;
		tvInsertStruct.item.iSelectedImage = iSelectedImageIndex;
	}

	tvInsertStruct.item.pszText = Text;
	// Perform the actual insert
	HTREEITEM Ret = TreeView_InsertItem( hWnd, &tvInsertStruct );
	// Refresh window: looks nicer
	InvalidateRect(hWnd, NULL,FALSE);
	return(Ret);
}


// PNTreeSetItemData - Stores an extra 32-bit value with a tree view item
// Parameters:
//     hWnd - Window handle for Tree View to store data to
//     dwData - Value to be stored
//     hNode - Node within tree to store to (0 means to currently selected node)
// Returns: TRUE if succesful, false if not
BOOL PNTreeViewSetItemData( HWND hWnd, DWORD dwData, HTREEITEM hNode )
{
	if( hNode == 0 )
		hNode = TreeView_GetSelection( hWnd );
	if( hNode == 0 )
		return( FALSE );

	TVITEM tvItem;
	tvItem.hItem = hNode;
	tvItem.mask = TVIF_PARAM;
	tvItem.lParam = dwData;

	return( TreeView_SetItem( hWnd, &tvItem ) );

}

// PNTreeGetItemData - Retrieves extra 32-bit value for a node
// Parameters:
//     hWnd - Window handle for Tree View to get data from
//     hNode - Node within tree to retrieve (0 means from currently selected node)
// Returns: Value retrieved from node
DWORD PNTreeViewGetItemData( HWND hWnd, HTREEITEM hNode )
{
	if( hNode == 0 )
		hNode = TreeView_GetSelection( hWnd );
	if( hNode == 0 )
		return( FALSE );

	TVITEM tvItem;
	tvItem.hItem = hNode;
	tvItem.mask = TVIF_PARAM;
	TreeView_GetItem( hWnd, &tvItem );
	return( tvItem.lParam );
}

// PNTreeGetItemText - Retrieves text for specific node
// Parameters:
//     hWnd - Window handle for Tree View to get text from
//     pDest - Where to store text retrieved
//     iMax - Maximum size of text to retrieve, without null terminator
//     hNode - Node within tree to retrieve (0 means from currently selected node)
// Returns: TRUE if succesful, false if not
BOOL PNTreeViewGetItemText( HWND hWnd, LPTSTR pDest, int iMax, HTREEITEM hNode )
{
	if( hNode == 0 )
		hNode = TreeView_GetSelection( hWnd );
	if( hNode == 0 )
		return( FALSE );

	TVITEM tvItem;
	tvItem.hItem = hNode;
	tvItem.mask = TVIF_TEXT;
	tvItem.pszText = pDest;
	tvItem.cchTextMax = iMax;

	return( TreeView_GetItem( hWnd, &tvItem ) );
}

// PNTreeSetItemText - Retrieves text for specific node
// Parameters:
//     hWnd - Window handle for Tree View to get text from
//     pSrc - Text to set in node
//     hNode - Node within tree to set (0 means from currently selected node)
// Returns: TRUE if succesful, false if not
BOOL PNTreeViewSetItemText( HWND hWnd, LPTSTR pSrc, HTREEITEM hNode )
{
	if( hNode == 0 )
		hNode = TreeView_GetSelection( hWnd );
	if( hNode == 0 )
		return( FALSE );

	TVITEM tvItem;
	tvItem.hItem = hNode;
	tvItem.mask = TVIF_TEXT;
	tvItem.pszText = pSrc;

	return( TreeView_SetItem( hWnd, &tvItem ) );
}

// PNTreeViewFindItemText - Searchs nodes for an item text
// Parameters:
//     hWnd - Window handle for Tree View to search
//     pItemText - Text to search for (case sensitive)
//     hItem - Node to start on, or zero to start on root
// Returns: HTREEITEM for found node, or 0 if not found
HTREEITEM PNTreeViewFindItemText( HWND hWnd, LPCTSTR pItemText, HTREEITEM hItem )
{
	// Default to Root Node
	if (hItem == 0 )
		hItem = TreeView_GetRoot( hWnd );
    
	// Check all siblings
	while( hItem != NULL )
	{
		char szBuffer[128];
		HTREEITEM hChildItem;

		// Is this the item?
		PNTreeViewGetItemText( hWnd, szBuffer, sizeof(szBuffer), hItem );
		if (strcmp(szBuffer, pItemText) == 0)
			return hItem;

		// Look for children items
		hChildItem = TreeView_GetNextItem( hWnd, hItem, TVGN_CHILD );
		if( hChildItem )
		{
			// Recursive call: Check children node(s)
			HTREEITEM hItemFound = PNTreeViewFindItemText(hWnd, pItemText, hChildItem);

			// Did child call find it?
			if (hItemFound != NULL)
				return hItemFound;
		}

		// Go to next sibling item.
		hItem = TreeView_GetNextItem( hWnd, hItem, TVGN_NEXT );
	}

	// Didn't find it
	return NULL;
} 


// PNTreeViewInitDrives - Populates a Tree view control with a list of Drive
//      names and types, and icons
// Parameters:
//      hWnd - Window handle for Tree control to initialize
// Returns: Number of rows (drives) added to list
// Note: Call this function to setup a drive list, but also call the PNTreeViewExpandPath
//       function when a folder in the Tree View control is expanded (TVN_EXPANDING)
int PNTreeViewInitDrives( HWND hWnd )
{
	HIMAGELIST hImageList;
	HTREEITEM hTreeItem;
	BOOL LargeSize=FALSE;


	// Use Windows own image list for drive and file icons
	hImageList = PNGetSysImageList( FALSE );
	TreeView_SetImageList( hWnd, hImageList, TVSIL_NORMAL );


	// Setup TreeView appearance
	TreeView_DeleteAllItems( hWnd );

	// Determine valid drive letters
	char Drives[128], *pRoot;
	char Descr[128];
	int iIcon, iIconSel;
	int Row=0;
	
	GetLogicalDriveStrings( sizeof(Drives), Drives );

	// Iterate through drive letters, determine icons, and add to list view control
	for( pRoot=Drives; *pRoot; pRoot++ )
	{
		sprintf( Descr, "%c:", *pRoot );
		
		// Determine system image for drive
		PNGetPathIcons( pRoot, iIcon, iIconSel, LargeSize, FALSE );
		hTreeItem = PNTreeViewInsertItem( hWnd, Descr, TVI_ROOT, TVI_SORT, iIcon, iIconSel );
		PNTreeViewInsertItem( hWnd, "", hTreeItem );

		while( *pRoot )
			pRoot++;
	}
	return( Row );
}

// PNTreeViewGetDrivePath - Retrieves full path from a node in a DrivePath tree view
// Parameters:
//      hWnd - Window handle for Tree control to retrieve path from
//      Path - Where to store the path retrieved
//      hItem - Node item to retrieve path for
// Returns: Number of rows (drives) added to list
// Note: This is a recurdsive function, that builds up a string from all the parent nodes.
void PNTreeViewGetDrivePath( HWND hWnd, char* Path, HTREEITEM hItem )
{
	static int Recurse=0;
	
	if(Recurse == 0 )
		*Path='\0';
	Recurse++;

	if( hItem )
	{
		PNTreeViewGetDrivePath( hWnd, Path, TreeView_GetParent( hWnd, hItem ) );
		PNTreeViewGetItemText( hWnd, Path+strlen(Path), MAX_PATH-strlen(Path), hItem );
		if( *Path )
			strcat(Path, "\\" );
	}
	Recurse--;
}

// PNTreeViewExpandPath - Used with Drive Path tree views, to populate a tree
//      node with a list of folders within a path.
// Parameters:
//      hWnd - Window handle for Tree control with drive path information
//      lParam - lParam parameter for LVN_ITEMEXPANDING message (an NMTREEVIEW pointer)
// Returns: TRUE if folders add, FALSE if not
// Note: This function is used to expand folders for a tree control that has had drive
//       letters added to it by calling the PNTreeViewInitDrives function
BOOL PNTreeViewExpandPath( HWND hWnd, LPARAM lParam )
{
	char Path[MAX_PATH]="";
	int Icon, IconSel;
	HTREEITEM hTreeItem;
	NMTREEVIEW* pNMTreeView = (NMTREEVIEW*) lParam;
	BOOL Ret=FALSE;

	if( pNMTreeView->action == TVE_EXPAND )
	{
		// Remove current listing
		while( (hTreeItem=TreeView_GetChild( hWnd, pNMTreeView->itemNew.hItem ) ) != 0 )
			TreeView_DeleteItem( hWnd, hTreeItem );
		
		// Get Path string for the node
		PNTreeViewGetDrivePath( hWnd, Path, pNMTreeView->itemNew.hItem );

		// List Folders in the Path
		strcat( Path, "*.*" );
		WIN32_FIND_DATA FindFileData;
		HANDLE hFind;

		hFind = FindFirstFile( Path, &FindFileData);
		if (hFind != INVALID_HANDLE_VALUE)
		{
			do {
				if( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
					FindFileData.cFileName[0]!='.' && 
					!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) )
				{
					PNGetPathIcons( Path, Icon, IconSel, FALSE, TRUE );
					hTreeItem = PNTreeViewInsertItem( hWnd, FindFileData.cFileName, pNMTreeView->itemNew.hItem, TVI_SORT, Icon, IconSel );
					PNTreeViewInsertItem( hWnd, "", hTreeItem );
					Ret = TRUE;
				}
			}while( FindNextFile( hFind, &FindFileData ) );
		}
	    FindClose(hFind);
	}

	return( Ret );
}


// PNTextBox support structure
struct PNTEXTBOXDEST {
	LPTSTR pDest;
	LPCTSTR pCaption, pPrompt;
	int iMax;
};

// PNTextBoxProc - Dialog Procedure for PNTextBox function
LRESULT CALLBACK PNTextBoxProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam  )
{
	int wID;
	int wNotification;
	HWND hChild;
	HFONT hFont;
	static PNTEXTBOXDEST* pDest;

	switch( uMsg )
	{
		case WM_INITDIALOG:
			pDest = (PNTEXTBOXDEST*)lParam; // Save destination string
			hFont = (HFONT)SendMessage( GetParent(hwndDlg), WM_GETFONT, 0, 0 );
			SetWindowText( hwndDlg, pDest->pCaption );
			
			// Create controls - Easier than DLGTEMPLATE

			hChild = CreateWindow( "STATIC", pDest->pPrompt, WS_VISIBLE|WS_CHILD, 8, 8, 250, 24, hwndDlg, (HMENU)1000, 0, 0 );
			SendMessage( hChild, WM_SETFONT, (WPARAM)hFont, 0 );

			hChild = CreateWindowEx( WS_EX_CLIENTEDGE, "EDIT", pDest->pDest, WS_TABSTOP|WS_VISIBLE|WS_CHILD, 8, 36, 250, 22, hwndDlg, (HMENU)1001, 0, 0 );
			SendMessage( hChild, EM_LIMITTEXT, pDest->iMax, 0 );
			SendMessage( hChild, WM_SETFONT, (WPARAM)hFont, 0 );
			SetFocus( hChild );
			
			hChild=CreateWindow( "BUTTON", "&OK", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON, 268, 8, 50, 24, hwndDlg, (HMENU)IDOK, 0, 0 );
			SendMessage( hChild, WM_SETFONT, (WPARAM)hFont, 0 );

			hChild=CreateWindow( "BUTTON", "&Cancel", WS_TABSTOP|WS_VISIBLE|WS_CHILD, 268, 36, 50, 24, hwndDlg, (HMENU)IDCANCEL, 0, 0 );
			SendMessage( hChild, WM_SETFONT, (WPARAM)hFont, 0 );
			
			return(TRUE);

		case WM_CLOSE:
			EndDialog( hwndDlg, 0 ); // Close the dialog
			break;
		case WM_COMMAND: // A message from a control or menu item
			// Parse out WM_COMMAND parameters to be more readable
			wID = LOWORD(wParam);
			wNotification = HIWORD(wParam);
			hChild = (HWND) lParam;

			// Tree control demonstrations
			if( (wID == IDOK || wID == IDCANCEL) && wNotification == BN_CLICKED )
			{
				if( wID == IDOK )
				{
					GetDlgItemText( hwndDlg, 1001, pDest->pDest, pDest->iMax );
				}
				EndDialog( hwndDlg, wID );
				break;
			}
	}
	return( FALSE );
}

// PNTextBox - Simple text-input dialog box
// Parameters:
//     hParent - Handle to parent window that invokes the input dialog
//     pDest - Pointer to location to store input string
//     iMax - Maximum number of characters to input (not counting terminator)
//     pPrompt - Text to display for input.  If NULL then "Text:" is displayed
//     pCaption - Caption of dialog.  If NULL, then "Input" is displayed
//     bClearFirst - If TRUE, text is cleared first, if FALSE, text is displayed as is.
// Returns: TRUE if user hit OK, FALSE if Cancel.
// Note: This function relies on the PNTextBoxProc function to work
BOOL PNTextBox( HWND hParent, LPTSTR pDest, int iMax, LPCSTR pPrompt, LPTSTR pCaption, BOOL bClearFirst )
{
	struct _LocalDialog {
		DLGTEMPLATE TheTemplate;
		WORD Misc[4];
	} LocalDialog = {
	{DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU|DS_CENTER,
		0, 0, 0, 0, 166, 36}, 0 };

	if( bClearFirst )
		*pDest = '\0';
	
	PNTEXTBOXDEST PNTextBoxDest;
	PNTextBoxDest.pDest = pDest;
	PNTextBoxDest.iMax = iMax;
	PNTextBoxDest.pPrompt = pPrompt ? pPrompt : "Text:";
	PNTextBoxDest.pCaption = pCaption ? pCaption : "Input";

	int Ret = DialogBoxIndirectParam( GetModuleHandle(NULL), (DLGTEMPLATE*)&LocalDialog, hParent, 
		(DLGPROC)PNTextBoxProc, (DWORD)&PNTextBoxDest );

	return( Ret == IDOK ? TRUE : FALSE );
}

// PNTreeViewSetCheckState - Sets the check state for an item in a tree view
// Parameters:
//     hWnd - Tree View control to set state for
//     hItem - Item within the tree to set check for
//     bState - TRUE to set check, or FALSE to clear it
// Returns: TRUE succesful, FALSE if failed.
// Note: Tree View must have the Check style applied
BOOL PNTreeViewSetCheckState( HWND hWnd, HTREEITEM hItem, BOOL bState )
{
	// Note: This code is from the SDK online help.  Because of the
	// newness of the TVS_CHECKBOXES style, early versions of VC++
	// will not have the ListView+GetCheckState macro

    TVITEM tvItem;

    tvItem.mask = TVIF_HANDLE | TVIF_STATE;
    tvItem.hItem = hItem;
    tvItem.stateMask = TVIS_STATEIMAGEMASK;

    /*Image 1 in the tree view check box image list is the
    unchecked box. Image 2 is the checked box.*/
    
    tvItem.state = INDEXTOSTATEIMAGEMASK((bState ? 2 : 1));

    BOOL Ret = TreeView_SetItem(hWnd, &tvItem);
	InvalidateRect( hWnd, 0, TRUE );
	return( Ret );

}

// PNTreeViewGetCheckState - Gets the check state for an item in a tree view
// Parameters:
//     hWnd - Tree View control to get state for
//     hItem - Item within the tree to get check for
// Returns: TRUE item is checked, false if not
// Note: Tree View must have the Check style applied
BOOL PNTreeViewGetCheckState( HWND hWnd, HTREEITEM hItem )
{
	// Note: This code is from the SDK online help.  Because of the
	// newness of the TVS_CHECKBOXES style, early versions of VC++
	// will not have the ListView+GetCheckState macro
	
    TVITEM tvItem;

    // Prepare to receive the desired information.
    tvItem.mask = TVIF_HANDLE | TVIF_STATE;
    tvItem.hItem = hItem;
    tvItem.stateMask = TVIS_STATEIMAGEMASK;

    // Request the information.
    TreeView_GetItem(hWnd, &tvItem);

    // Return zero if it's not checked, or nonzero otherwise.
    return ((BOOL)(tvItem.state >> 12) -1);

}

// PNTabControlInsertItem - Inserts new tab in a tab control
// Parameters:
//     hWnd - Tab control window to add to
//     Item - Position to insert new tab
//     pItemText - Text for the new tab control
// Returns: TRUE if succesful, FALSE otherwise
BOOL PNTabControlInsertItem( HWND hWnd, int Item, LPCTSTR pItemText )
{
	TCITEM tcItem;

	tcItem.mask = TCIF_TEXT;
	tcItem.pszText = (LPTSTR)pItemText;
	return( TabCtrl_InsertItem( hWnd, Item, &tcItem ) );
}
