File:  [WindowsNT SDKs] / mstools / mfc / samples / tutorial / view.cpp
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:24:41 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntsdk-jul-1993, HEAD
Microsoft Windows NT Build 511 (SDK Final Release) 07-24-1993

// view.cpp : Defines the behaviors for the application and frame window.
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and Microsoft
// QuickHelp documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include <afxwin.h>

#include "resource.h"
#include "database.h"
#include "view.h"

#include <commdlg.h>

#define SIZESTRING 256
#define SIZENAME 30
#define SIZEPHONE 26
#define PAGESIZE 8

// a simple way to reduce size of C runtimes
// disables the use of getenv and argv/argc
extern "C" void _setargv() { }
extern "C" void _setenvp() { }

/////////////////////////////////////////////////////////////////////////////
// Create the single global instance of the database viewer app.

CTheApp PersonApp;

/////////////////////////////////////////////////////////////////////////////
// CTheApp

//////////////////////////////////////////////////
//  CtheApp::InitInstance
//  Override InitInstance function to create a CMainWindow object
//  and display it on the screen
//
BOOL CTheApp::InitInstance()
{
	m_pMainWnd = new CMainWindow();
	m_pMainWnd->ShowWindow( m_nCmdShow );
	m_pMainWnd->UpdateWindow();
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CFindDialog

//////////////////////////////////////////////////
//  CFindDialog::OnOK
//  When the user hits OK get the data entered and store it in this
//  object.  Then end the dialog.
//
void CFindDialog::OnOK()
{
	GetDlgItemText( IDC_DATA, m_szFindName.GetBuffer( SIZESTRING ), SIZESTRING );
	m_szFindName.ReleaseBuffer();
	EndDialog( IDOK );
}

/////////////////////////////////////////////////////////////////////////////
// CEditPerson

//////////////////////////////////////////////////
//  CEditDialog::OnInitDialog
//  Fill in the fields witht the data placed in this object
//  when it was created.
//
BOOL CEditDialog::OnInitDialog()
{
	SetDlgItemText( IDC_LASTNAME,  m_pData->GetLastName() );
	SetDlgItemText( IDC_FIRSTNAME, m_pData->GetFirstName() );
	SetDlgItemText( IDC_PHONE, m_pData->GetPhoneNumber() );
	SetDlgItemText( IDC_MOD, m_pData->GetModTime().Format("%m/%d/%y %H:%M" ) );
	SendDlgItemMessage( IDC_LASTNAME, EM_SETSEL );

	return TRUE;
}

//////////////////////////////////////////////////
//  CEditDialog::OnOK
//  When OK is pressed set the data to what the user has entered.
//
void CEditDialog::OnOK()
{
	char szTmp[SIZESTRING];
	
	GetDlgItemText( IDC_LASTNAME, szTmp, sizeof ( szTmp ) );
	m_pData->SetLastName( szTmp );
	
	GetDlgItemText( IDC_FIRSTNAME, szTmp, sizeof ( szTmp ) );
	m_pData->SetFirstName( szTmp );
	
	GetDlgItemText( IDC_PHONE, szTmp, sizeof ( szTmp ) );
	m_pData->SetPhoneNumber( szTmp );
	
	EndDialog( IDOK );
}

/////////////////////////////////////////////////////////////////////////////
// CMainWindow

BEGIN_MESSAGE_MAP( CMainWindow, CFrameWnd )

	// File menu commands:
	ON_COMMAND( IDM_NEW, OnNew )
	ON_COMMAND( IDM_OPEN, OnOpen )
	ON_COMMAND( IDM_SAVE, OnSave )
	ON_COMMAND( IDM_SAVEAS, OnSaveAs )
	ON_COMMAND( IDM_CLOSE, OnDBClose )
	ON_COMMAND( IDM_PRINT, OnPrint )
	ON_COMMAND( IDM_EXIT, OnExit )

	// Person menu commands:
	ON_COMMAND( IDM_ADD, OnAdd )
	ON_COMMAND( IDM_DELETE, OnDelete )
	ON_COMMAND( IDM_EDIT, OnEdit )
	ON_COMMAND( IDM_FIND, OnFind )
	ON_COMMAND( IDM_FINDALL, OnFindAll )

	// Help menu commands:
	ON_COMMAND( IDM_HELP, OnHelp )
	ON_COMMAND( IDM_ABOUT, OnAbout )

	// Selection accelerators:
	ON_COMMAND( VK_UP, OnUp )
	ON_COMMAND( VK_DOWN, OnDown )

	// Other Windows messages:
	ON_WM_CREATE()
	ON_WM_CLOSE()
	ON_WM_SIZE()
	ON_WM_VSCROLL()
	ON_WM_HSCROLL()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_KEYDOWN()
	ON_WM_PAINT()
END_MESSAGE_MAP()


//////////////////////////////////////////////////
//  CMainWindow::CMainWindow
//  Constructs and initializes a CMainWindow object
//
CMainWindow::CMainWindow()
{
	VERIFY( LoadAccelTable( "MainAccelTable" ) );
	VERIFY( Create( NULL, "Phone Book",
			WS_OVERLAPPEDWINDOW, rectDefault, NULL, "MainMenu" ) );
	m_nSelectLine = -1;
}

/////////////////////////////////////////////////////////////////////////////
//  The Following are CMainWindow Menu Items

//////////////////////////////////////////////////
//  CMainWindow::OnNew
//  After checking to see if current data needs to be stored, call
//  database New and resize/repaint the window.
//
void CMainWindow::OnNew()
{
	if ( !CheckForSave( "File New", "Save file before New?" ) )
		return;
	m_people.New();
	SetMenu();
	SetWindowText( m_people.GetTitle() );
	OnSize( 0, m_cxClient, m_cyClient );
}

//////////////////////////////////////////////////
//  CMainWindow::OnOpen
//
void CMainWindow::OnOpen()
{
	if ( !CheckForSave( "File Open", "Save file before Open?" ) )
		return;

	// Attempt to open a database file and read it.
	// If a file or archive exception occurs, catch it and
	// present an error message box.
	CString szFileName, szFileTitle;
	TRY
	{
		// Use CommDlg to get the file name and then call DoOpen.
		// Set the Window title and menus.  Resize/Repaint.
		if ( FileDlg( TRUE, SIZESTRING, szFileName.GetBuffer( SIZESTRING ),
				SIZESTRING, szFileTitle.GetBuffer( SIZESTRING ) ) )
		{
			szFileName.ReleaseBuffer();
			szFileTitle.ReleaseBuffer();
			m_people.DoOpen( szFileName );
			m_people.SetTitle( szFileTitle );
			SetWindowText( m_people.GetTitle() );
			SetMenu();
			OnSize( 0, m_cxClient, m_cyClient );
		}
	}
	CATCH( CFileException, e )
	{
		char ErrorMsg[SIZESTRING];
		sprintf( ErrorMsg,"Opening %s returned a 0x%lx.",
				(const char*)szFileTitle, e->m_lOsError );
		MessageBox( ErrorMsg, "File Open Error" );
	}
	AND_CATCH( CArchiveException, e )
	{
		char ErrorMsg[SIZESTRING];
		sprintf( ErrorMsg,"Reading the %s archive failed.",
				(const char*)szFileTitle );
		MessageBox( ErrorMsg, "File Open Error" );
	}
	END_CATCH
}

//////////////////////////////////////////////////
//  CMainWindow::OnSave
//
void CMainWindow::OnSave()
{
	Save( m_people.IsNamed() );
}

//////////////////////////////////////////////////
//  CMainWindow::OnSaveAs
//
void CMainWindow::OnSaveAs()
{
	Save();
}

//////////////////////////////////////////////////
//  CMainWindow::OnDBClose
//  Closes the current database, checking to see if it should be
//  saved first.  Reset the window title and the scroll regions.
//  Invalidating the entire screen causes OnPaint to repaint but
//  this time without any data.
//
void CMainWindow::OnDBClose()
{
	if ( !CheckForSave( "File Close", "Save file before closing?" ) )
		return;
	m_people.Terminate();
	SetWindowText( "Phone Book" );
	SetMenu();
	OnSize( 0, m_cxClient, m_cyClient );
}

//////////////////////////////////////////////////
//  CMainWindow::OnPrint
//  Uses the commdlg print dialog to create a printer dc
//  Then it uses code almost identical to the OnPaint code
//  to write the data to the printer.
//
void CMainWindow::OnPrint()
{

	PRINTDLG pd;
	
	pd.lStructSize = sizeof( PRINTDLG );
	pd.hwndOwner=m_hWnd;
	pd.hDevMode=(HANDLE)NULL;
	pd.hDevNames=(HANDLE)NULL;
	pd.Flags=PD_RETURNDC | PD_NOSELECTION | PD_NOPAGENUMS;
	pd.nFromPage=0;
	pd.nToPage=0;
	pd.nMinPage=0;
	pd.nMaxPage=0;
	pd.nCopies=1;
	pd.hInstance=(HINSTANCE)NULL;

	if ( PrintDlg( &pd ) != 0 )
	{
		// CommDlg returned a DC so create a CDC object from it.
		ASSERT( pd.hDC != 0 );
		CDC * dc;
		dc = CDC::FromHandle( pd.hDC );

		// Change to hour glass while printing
		SetCursor( AfxGetApp()->LoadStandardCursor( IDC_WAIT ) );

		// Begin printing the document.
		int rc;
		char szError[SIZESTRING];

		rc = dc->StartDoc( "Phone Book" );

		if ( rc < 0 )
		{
			sprintf( szError, "Unable to Begin printing - Error[%d]", rc );
			MessageBox( szError, NULL,MB_OK );
			return;
		}
		
		int x, y;
		CPerson* pCurrent;
		int nPerson=0;
		CString szDisplay;
		int nStart, nEnd;
		
		// Get Height and Width of large character
		CSize extentChar = dc->GetTextExtent( "M", 1 );
		int nCharHeight = extentChar.cy;
		int nCharWidth = extentChar.cx;
		
		// Get Page size in # of full lines
		int nExtPage = ( dc->GetDeviceCaps(VERTRES) - nCharHeight )
				/ nCharHeight;
		
		CString szTitle;
		szTitle = CString( "Phone Book - " ) + m_people.GetName();

		while ( nPerson != m_people.GetCount() )
		{
			// Print a Page Header
			dc->StartPage();
			dc->SetTextAlign ( TA_LEFT | TA_TOP );
			dc->TextOut( 0, 0, szTitle, szTitle.GetLength() );
			dc->MoveTo( 0, nCharHeight );
			dc->LineTo( 
				dc->GetTextExtent( szTitle, szTitle.GetLength() ).cx,
				nCharHeight );

			// Print People from start to last person or page size minus
			// 2 ( header size )
			nEnd = min( m_people.GetCount() - nPerson, nExtPage-2 );
			for ( nStart = 0; nStart < nEnd; nStart++, nPerson++ )
			{
				x = 0;
				y = nCharHeight * ( nStart+2 );

				pCurrent = m_people.GetPerson( nPerson );
				szDisplay = " " + pCurrent->GetLastName() + ", " +
						pCurrent->GetFirstName();
				dc->SetTextAlign( TA_LEFT | TA_TOP );
				dc->TextOut( x, y, szDisplay, szDisplay.GetLength() );

				szDisplay = pCurrent->GetPhoneNumber();
				dc->SetTextAlign( TA_RIGHT | TA_TOP );
				dc->TextOut( x + SIZENAME * nCharWidth, y, szDisplay,
						szDisplay.GetLength() );

				szDisplay = pCurrent->GetModTime().Format( "%m/%d/%y %H:%M" );
				dc->TextOut( x + ( SIZENAME + SIZEPHONE ) * nCharWidth, y,
					szDisplay, szDisplay.GetLength() );
			}
			dc->EndPage();
		}
		dc->EndDoc();
		dc->DeleteDC();
		SetCursor( AfxGetApp()->LoadStandardCursor( IDC_ARROW ) );
	}
}

//////////////////////////////////////////////////
//  CMainWindow::OnExit
//
void CMainWindow::OnExit()
{
	OnClose();
}

//////////////////////////////////////////////////
//  CMainWindow::OnAdd
//  Using the EditDialog fill in a new person object.  If the user
//  selects OK then add the person, call OnSize to resize the scroll
//  region, and invalidate the screen so it will be redrawn with the
//  new person in the correct order.
//
void CMainWindow::OnAdd()
{
	CPerson* person=new CPerson();
	
	CEditDialog dlgAdd( person, this );
	if ( dlgAdd.DoModal() == IDOK )
	{
		m_people.AddPerson( person );
		OnSize( 0, m_cxClient, m_cyClient );
	}
	else
		delete person;
}

//////////////////////////////////////////////////
//  CMainWindow::OnDelete
//  Deletes the current selection.  Check to see if the selection is
//  now past then end of the list.  Also call OnSize since the list
//  length has now changed.
//
void CMainWindow::OnDelete()
{
	if ( m_nSelectLine == -1 )
	{
		MessageBox( "Select a person to delete first" );
		return;
	}
	m_people.DeletePerson( m_nSelectLine );
	if ( m_nSelectLine >= (int)m_people.GetCount() )
		m_nSelectLine--;
	OnSize( 0, m_cxClient, m_cyClient );
}

////////////////////////////////////////////////////
//  CMainWindow::OnFind
//  Gets information from the CFindDialog modal dialog box, then searches for
//  matching people.  Note the Add and Delete menu items are disabled after
//  a find is made.  Find All is enabled.
//
void CMainWindow::OnFind()
{
	CFindDialog dlgFind( this );
	if ( dlgFind.DoModal() == IDOK &&
			dlgFind.GetFindString().GetLength() != 0 )
	{
		if ( m_people.DoFind( dlgFind.GetFindString() ) )
		{
			m_nSelectLine = -1;
			CString tmp;
			tmp = m_people.GetTitle() + " Found: "
					+ dlgFind.GetFindString();
			SetWindowText( tmp );
			CMenu* pMenu = GetMenu();
			pMenu->EnableMenuItem( IDM_FINDALL, MF_ENABLED );
			pMenu->EnableMenuItem( IDM_FIND, MF_GRAYED );
			pMenu->EnableMenuItem( IDM_DELETE, MF_GRAYED );
			pMenu->EnableMenuItem( IDM_ADD, MF_GRAYED );
			OnSize( 0, m_cxClient, m_cyClient );
		}
		else
			MessageBox( "No match found in list." );
	}
}

////////////////////////////////////////////////////
//  CMainWindow::OnFindAll
//  Returns to view the whole database.  Add, Delete are re-enabled, and
//  Find All is again disabled.  OnSize is called because the list
//  has changed length.
//
void CMainWindow::OnFindAll()
{
	m_people.DoFind();
	SetWindowText( m_people.GetTitle() );
	CMenu* pMenu = GetMenu();
	pMenu->EnableMenuItem( IDM_FINDALL, MF_GRAYED );
	pMenu->EnableMenuItem( IDM_FIND, MF_ENABLED );
	pMenu->EnableMenuItem( IDM_DELETE, MF_ENABLED );
	pMenu->EnableMenuItem( IDM_ADD, MF_ENABLED );
	OnSize( 0, m_cxClient, m_cyClient );
}

////////////////////////////////////////////////////
//  CMainWindow::OnEdit
//  Using the member variable m_nSelectLine a CEditDialog is created
//  and filled with the selected person.  If the dialog OK button is
//  used the dialog saves the changes into the object.
//  over any old information.
//
void CMainWindow::OnEdit()
{
	if ( m_nSelectLine == -1 )
	{
		MessageBox( "Select a person to edit first" );
		return;
	}

	// Get a pointer to the person in the list.
	CPerson* pPerson = m_people.GetPerson( m_nSelectLine );
	CPerson tmpPerson = *pPerson;
	
	//Edit the data.
	CEditDialog dlgEdit( &tmpPerson, this );
	
	//if the ok button is pressed redraw the screen
	if ( dlgEdit.DoModal() == IDOK )
	{
		m_people.ReplacePerson( pPerson, tmpPerson );
		InvalidateLine();
	}
}

//////////////////////////////////////////////////
//  CMainWindow::OnHelp
//
void CMainWindow::OnHelp()
{
	if ( !m_people.IsPresent() )
	{
		CModalDialog dlgHelp( "NoData", this );
		dlgHelp.DoModal();
		return;
	}
	
	if ( !m_people.IsNamed() )
	{
		CModalDialog dlgHelp( "NoName", this );
		if ( dlgHelp.DoModal() == IDCANCEL )
			return;
	}
	
	CModalDialog dlgHelp( "Enter", this );
	dlgHelp.DoModal();
}

//////////////////////////////////////////////////
//  CMainWindow::OnAbout
//
void CMainWindow::OnAbout()
{
	CModalDialog dlgAbout( "AboutBox", this );
	dlgAbout.DoModal();
}

/////////////////////////////////////////////////////////////////////////////
//  The Following are WINDOW messages

//////////////////////////////////////////////////
//  CMainWindow::OnCreate
//  Queries the current text metrics to determine char size.
//
int CMainWindow::OnCreate( LPCREATESTRUCT )
{
	TEXTMETRIC tm;

	// Get the text metrics.
	CDC* dc = GetDC();
	dc->GetTextMetrics( &tm );
	ReleaseDC( dc );

	// Decide the statistics on how many rows, etc., we can display.
	m_cxChar = tm.tmAveCharWidth;
	m_cxCaps = ( (tm.tmPitchAndFamily & 1 )? 3 : 2 ) * m_cxChar / 2;
	m_cyChar = tm.tmHeight + tm.tmExternalLeading;
	m_nMaxWidth = ( SIZENAME + SIZEPHONE + 1 ) * m_cxCaps;
	m_nVscrollPos = m_nHscrollPos = 0;

	return 0;
}

//////////////////////////////////////////////////
//  CMainWindow::OnClose
//  Check to see if the current file needs to be saved.  Terminate
//  the database and destory the window.
//
void CMainWindow::OnClose()
{
	if ( !CheckForSave( "File Exit", "Save file before exit?" ) )
		return;
	m_people.Terminate();
	DestroyWindow();
}

//////////////////////////////////////////////////
//  CMainWindow::OnSize
//  When resized, we need to recalculate our scrollbar ranges based on what
//  part of the database is visible.
//
void CMainWindow::OnSize( UINT, int x, int y )
{
	m_cxClient = x;
	m_cyClient = y;

	m_nVscrollMax = max( 0,
			(int)( m_people.GetCount() ) - m_cyClient / m_cyChar );
	m_nVscrollPos = min( m_nVscrollPos, m_nVscrollMax );

	SetScrollRange( SB_VERT, 0, m_nVscrollMax, FALSE );
	SetScrollPos( SB_VERT, m_nVscrollPos, TRUE );

	m_nHscrollMax = max( 0, ( m_nMaxWidth - m_cxClient ) / m_cxChar );
	m_nHscrollPos = min( m_nHscrollPos, m_nHscrollMax );

	SetScrollRange( SB_HORZ, 0, m_nHscrollMax, FALSE );
	SetScrollPos( SB_HORZ, m_nHscrollPos, TRUE );
	Invalidate( TRUE );
}

//////////////////////////////////////////////////
//  CMainWindow::OnVScroll
//  Translate scroll messages into Scroll increments and then
//  checks the current position to determine if scrolling is possible
//
void CMainWindow::OnVScroll( UINT wParam, UINT pos, CScrollBar* )
{
	short nScrollInc;
	
	switch ( wParam )
	{
		case SB_TOP:
			nScrollInc = -m_nVscrollPos;
			break;

		case SB_BOTTOM:
			nScrollInc = m_nVscrollMax - m_nVscrollPos;
			break;
			
		case SB_LINEUP:
			nScrollInc = -1;
			break;

		case SB_LINEDOWN:
			nScrollInc = 1;
			break;

		case SB_PAGEUP:
			nScrollInc = min( -1, -m_cyClient / m_cyChar );
			break;

		case SB_PAGEDOWN:
			nScrollInc = max( 1, m_cyClient / m_cyChar );
			break;

		case SB_THUMBTRACK:
			nScrollInc = pos - m_nVscrollPos;
			break;

		default:
			nScrollInc = 0;
	}

	if ( nScrollInc = max( -m_nVscrollPos,
			min( nScrollInc, m_nVscrollMax - m_nVscrollPos ) ) )
	{
		m_nVscrollPos += nScrollInc;
		ScrollWindow( 0, -m_cyChar * nScrollInc );
		SetScrollPos( SB_VERT, m_nVscrollPos );
		UpdateWindow();
	}
}

//////////////////////////////////////////////////
//  CMainWindow::OnHScroll
//  Translate scroll messages into Scroll increments and then
//  checks the current position to determine if scrolling is possible
//
void CMainWindow::OnHScroll( UINT wParam, UINT pos, CScrollBar* )
{
	int nScrollInc;
	switch ( wParam )
	{
		case SB_LINEUP:
			nScrollInc = -1;
			break;

		case SB_LINEDOWN:
			nScrollInc = 1;
			break;

		case SB_PAGEUP:
			nScrollInc = -PAGESIZE;
			break;

		case SB_PAGEDOWN:
			nScrollInc = PAGESIZE;
			break;

		case SB_THUMBPOSITION:
			nScrollInc = pos - m_nHscrollPos;
			break;

		default:
			nScrollInc = 0;
	}

	if ( nScrollInc = max( -m_nHscrollPos,
			 min( nScrollInc, m_nHscrollMax - m_nHscrollPos ) ) )
	{
		m_nHscrollPos += nScrollInc;
		ScrollWindow( -m_cxChar * nScrollInc, 0 );
		SetScrollPos( SB_HORZ, m_nHscrollPos );
		UpdateWindow();
	}
}

//////////////////////////////////////////////////
//  CMainWindow::OnUp
//  Uses Accelerator tables to link the up arrow key to this
//  routine.  Decrements the select line with checking for scrolling
//  and wrapping off the top of the list.
//
//
void CMainWindow::OnUp()
{
	InvalidateLine();
	
	if ( m_nSelectLine <= 0 )
	{
		m_nSelectLine = m_people.GetCount() - 1;
		m_nVscrollPos = max( 0, m_nSelectLine + 1 - ( m_cyClient / m_cyChar ) );
		Invalidate( TRUE );
	}
	else
	{
		m_nSelectLine--;
		if ( m_nSelectLine - m_nVscrollPos < 0 )
			OnVScroll( SB_LINEUP, 0, NULL );
		
		// Selection is off the screen
		if ( m_nSelectLine - m_nVscrollPos > ( m_cyClient / m_cyChar ) )
		{
			m_nVscrollPos = m_nSelectLine + 1 - ( m_cyClient / m_cyChar );
			SetScrollPos( SB_VERT, m_nVscrollPos, TRUE );
			Invalidate( TRUE );
		}
		if ( m_nSelectLine - m_nVscrollPos < 0 )
		{
			m_nVscrollPos = m_nSelectLine;
			SetScrollPos( SB_VERT, m_nVscrollPos, TRUE );
			Invalidate( TRUE );
		}
	}
	
	InvalidateLine();
}

//////////////////////////////////////////////////
//  CMainWindow::OnDown
//  Uses Accelerator tables to link the down arrow key to this
//  routine.  Inc the select line with checking for scrolling
//  and wrapping off the bottom of the list.
//
void CMainWindow::OnDown()
{
	InvalidateLine();
	
	if ( m_nSelectLine == (int)( m_people.GetCount() - 1 )
			|| m_nSelectLine == -1 )
	{
		m_nSelectLine = 0;
		m_nVscrollPos = 0;
		Invalidate( TRUE );
	}
	else
	{
		m_nSelectLine++;
		if ( ( m_nSelectLine - m_nVscrollPos + 1 ) > ( m_cyClient / m_cyChar ) )
			OnVScroll( SB_LINEDOWN, 0, NULL );
		
		// Selection is off the screen
		if ( ( m_nSelectLine - m_nVscrollPos ) > ( m_cyClient / m_cyChar ) )
		{
			m_nVscrollPos = m_nSelectLine + 1 - ( m_cyClient / m_cyChar );
			SetScrollPos( SB_VERT, m_nVscrollPos, TRUE );
			Invalidate( TRUE );
		}
		if ( ( m_nSelectLine - m_nVscrollPos ) < 0 )
		{
			m_nVscrollPos = m_nSelectLine;
			SetScrollPos( SB_VERT, m_nVscrollPos, TRUE );
			Invalidate( TRUE );
		}
	}

	InvalidateLine();
}

//////////////////////////////////////////////////
//  CMainWindow::OnLButtonDown
//  Turns the location of the mouse pointer into a line number
//  and stores that information in m_nSelectLine.  Uses
//  InvalidateLine to cause OnPaint to change the screen.
//
void CMainWindow::OnLButtonDown( UINT, CPoint location )
{
	InvalidateLine();
	
	int pos = m_nVscrollPos + location.y / m_cyChar;
	
	if ( ( m_nSelectLine != pos )  && ( pos < (int)m_people.GetCount() ) )
	{
		m_nSelectLine = pos;
		InvalidateLine();
	}
	else
		m_nSelectLine = -1;
}

//////////////////////////////////////////////////
//  CMainWindow::OnLButtonDblClk
//  Translates mouse left button double click into edit person.
//
void CMainWindow::OnLButtonDblClk( UINT wParam, CPoint location )
{
	if ( m_nSelectLine == -1 )
		OnLButtonDown( wParam, location );
	OnEdit();
}

//////////////////////////////////////////////////
//  CMainWindow::OnKeyDown
//  Translates keyboard input into scroll messages
//
void CMainWindow::OnKeyDown( UINT wParam, UINT, UINT )
{
	switch ( wParam )
	{
		case VK_HOME:
			OnVScroll( SB_TOP, 0, NULL );
			break;
		case VK_END:
			OnVScroll( SB_BOTTOM, 0, NULL );
			break;
		case VK_PRIOR:
			OnVScroll( SB_PAGEUP, 0, NULL );
			break;
		case VK_NEXT:
			OnVScroll( SB_PAGEDOWN, 0, NULL );
			break;
		case VK_LEFT:
			OnHScroll( SB_PAGEUP, 0, NULL );
			break;
		case VK_RIGHT:
			OnHScroll( SB_PAGEDOWN, 0, NULL );
			break;
	}
}

//////////////////////////////////////////////////
//  CMainWindow::OnPaint
//  This routine does all the painting for the screen.
//
void CMainWindow::OnPaint()
{
	
	CPaintDC dc( this );

	// Set the Text and background colors for the DC also create a Brush
	CBrush bBack;
	dc.SetTextColor( GetSysColor( COLOR_WINDOWTEXT ) );
	dc.SetBkColor( GetSysColor( COLOR_WINDOW ) );
	bBack.CreateSolidBrush( GetSysColor( COLOR_WINDOW ) );

	// Compute the lines that need to be redrawn
	int nStart = max( 0, m_nVscrollPos + dc.m_ps.rcPaint.top / m_cyChar - 1 );
	int nEnd = min( (int)m_people.GetCount(),
				m_nVscrollPos + ( dc.m_ps.rcPaint.bottom / m_cyChar+1 ) );

	// Create a rect the width of the display.
	CRect area( 0, 0, m_cxClient, 0 );
	
	CString szDisplay;
	CPerson* pCurrent;
	int x,y;
	for ( ;nStart < nEnd; nStart++ )
	{
		// if the current line is the select line then change the
		// colors to the highlight text colors.
		if ( m_nSelectLine == nStart )
		{
			bBack.DeleteObject();
			bBack.CreateSolidBrush( GetSysColor( COLOR_HIGHLIGHT ) );
			dc.SetTextColor( GetSysColor( COLOR_HIGHLIGHTTEXT ) );
			dc.SetBkColor( GetSysColor( COLOR_HIGHLIGHT ) );
		}

		// x is the number of characters horz scrolled * the width of
		// char.  y is the current line no. - number of lines scrolled
		// times the height of a line.
		x = m_cxChar * ( -m_nHscrollPos );
		y = m_cyChar * ( nStart - m_nVscrollPos );

		// Set the rect to y and y + the height of the line.  Fill the
		// rect with the background color.
		area.top = y;
		area.bottom = y+ m_cyChar;
		dc.FillRect( area, &bBack );

		// Get the person and build a string with his name.
		pCurrent = m_people.GetPerson( nStart );
		szDisplay = " " + pCurrent->GetLastName() + ", " +
				pCurrent->GetFirstName();

		// Set the dc to write using the point as the left top of the
		// character.  Write the name.
		dc.SetTextAlign( TA_LEFT | TA_TOP );
		dc.TextOut ( x, y,szDisplay, szDisplay.GetLength() );

		// Write the phone number right aligned.
		szDisplay = pCurrent->GetPhoneNumber();
		dc.SetTextAlign ( TA_RIGHT | TA_TOP );
		dc.TextOut ( x + SIZENAME * m_cxCaps, y, szDisplay,
			szDisplay.GetLength() );

		// Write the time.
		szDisplay = pCurrent->GetModTime().Format( "%m/%d/%y %H:%M" );
		dc.TextOut ( x + ( SIZENAME + SIZEPHONE ) * m_cxCaps, y,
			szDisplay, szDisplay.GetLength() );

		// If this is the select line then we need to reset the dc
		// colors back to the original colors.
		if ( m_nSelectLine == nStart )
		{
			bBack.DeleteObject();
			bBack.CreateSolidBrush( GetSysColor( COLOR_WINDOW ) );
			dc.SetTextColor( GetSysColor( COLOR_WINDOWTEXT ) );
			dc.SetBkColor( GetSysColor( COLOR_WINDOW ) );
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
//  The following are utility routines


//////////////////////////////////////////////////
//  CMainWindow::FileDlg
//  Call the commdlg routine to display File Open or File Save As
//  dialogs.  The setup is the same for either.  If bOpen is TRUE
//  then File Open is display otherwise File Save As is displayed.
//  The File Name and File Title are stored at the string pointer
//  passed in.
//
BOOL CMainWindow::FileDlg( BOOL bOpen, int nMaxFile, LPSTR szFile,
		int nMaxFileTitle, LPSTR szFileTitle )
{
	
	OPENFILENAME of;

	char szDirName[SIZESTRING];
	char szFilter[] = "Phone Book Files (*.pb)\0"
		"*.pb\0"
		"\0";

	szDirName[0] = '.';

	of.lStructSize = sizeof( OPENFILENAME );
	of.hwndOwner = m_hWnd;
	of.lpstrFilter = szFilter;
	of.lpstrCustomFilter = NULL;
	of.nMaxCustFilter = 0L;
	of.nFilterIndex = 1L;
	of.lpstrFile=szFile;
	of.nMaxFile=nMaxFile;
	of.lpstrFileTitle = szFileTitle;
	of.nMaxFileTitle = nMaxFileTitle;
	of.lpstrInitialDir = szDirName;
	of.lpstrTitle = NULL;
	of.nFileOffset = 0;
	of.nFileExtension = 0;
	of.lpstrDefExt = "pb";
	if ( bOpen )
	{
		of.Flags = OFN_HIDEREADONLY;
		return GetOpenFileName( &of );
	}
	else
	{
		of.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
		return GetSaveFileName( &of );
	}
}

//////////////////////////////////////////////////
//  CMainWindow::Save
//  Handles any time a file needs to be saved to the disk.
//  Passing in FALSE for name brings up the file save as dialog
//  whether or not the database had a name before.
//
BOOL CMainWindow::Save( BOOL bIsNamed /* = FALSE */ )
{
	CString szFileName, szFileTitle;
	TRY
	{
		if ( bIsNamed )
			m_people.DoSave();
		else
		{
			szFileName = m_people.GetName();
			if ( FileDlg( FALSE, SIZESTRING,
					szFileName.GetBuffer( SIZESTRING ), SIZESTRING,
					szFileTitle.GetBuffer( SIZESTRING ) ) )
			{
				szFileName.ReleaseBuffer();
				m_people.DoSave( szFileName );
				m_people.SetTitle( szFileTitle );
				SetWindowText( m_people.GetTitle() );
			}
			else
				return FALSE;
		}
	}
	CATCH( CFileException, e )
	{
		char ErrorMsg[SIZESTRING];
		sprintf( ErrorMsg,"Saving %s returned a 0x%lx.",
				(const char*)szFileTitle, e->m_lOsError );
		MessageBox( ErrorMsg, "File Open Error" );
	}
	AND_CATCH( CArchiveException, e )
	{
		char ErrorMsg[SIZESTRING];
		sprintf( ErrorMsg,"Reading the %s archive failed.",
				(const char*)szFileTitle );
		MessageBox( ErrorMsg, "File Open Error" );
	}
	END_CATCH
	return TRUE;
}

//////////////////////////////////////////////////
//  CMainWindow::CheckForSave
//  Whenever a new file is opened this routine will determine if
//  there are unsaved changes in the current database.  If so it
//  will query the user and determine save or not as appropriate.
//
BOOL CMainWindow::CheckForSave( const char* pszTitle, const char* pszMessage )
{
	if ( m_people.IsDirty() )
	{
		UINT nButton = MessageBox( pszMessage, pszTitle, MB_YESNOCANCEL );
		if ( nButton == IDYES )
		{
			if ( !Save( m_people.IsNamed() ) )
				return FALSE;
		}
		else if ( nButton == IDCANCEL )
			return FALSE;
	}
	return TRUE;
}


//////////////////////////////////////////////////
//  CMainWindow::SetMenu
//  Whenever the existance of the DataBase is changed this
//  routine will reset the menus so only the possible commands
//  are accessible.
//
void CMainWindow::SetMenu()
{
	CMenu* pMenu = GetMenu();
	if ( m_people.IsPresent() )
	{
		if ( m_people.IsNamed() )
			pMenu->EnableMenuItem( IDM_SAVE, MF_ENABLED );
		else
			pMenu->EnableMenuItem( IDM_SAVE, MF_GRAYED );
		pMenu->EnableMenuItem( IDM_SAVEAS, MF_ENABLED );
		pMenu->EnableMenuItem( IDM_CLOSE, MF_ENABLED );
		pMenu->EnableMenuItem( IDM_PRINT, MF_ENABLED );
		pMenu->EnableMenuItem( IDM_ADD, MF_ENABLED );
		pMenu->EnableMenuItem( IDM_DELETE, MF_ENABLED );
		pMenu->EnableMenuItem( IDM_FIND, MF_ENABLED );
		pMenu->EnableMenuItem( IDM_EDIT, MF_ENABLED );
	}
	else
	{
		pMenu->EnableMenuItem( IDM_SAVE, MF_GRAYED );
		pMenu->EnableMenuItem( IDM_SAVEAS, MF_GRAYED );
		pMenu->EnableMenuItem( IDM_CLOSE, MF_GRAYED );
		pMenu->EnableMenuItem( IDM_PRINT, MF_GRAYED );
		pMenu->EnableMenuItem( IDM_ADD, MF_GRAYED );
		pMenu->EnableMenuItem( IDM_DELETE, MF_GRAYED );
		pMenu->EnableMenuItem( IDM_FIND, MF_GRAYED );
		pMenu->EnableMenuItem( IDM_FINDALL, MF_GRAYED );
		pMenu->EnableMenuItem( IDM_EDIT, MF_GRAYED );
	}
}

//////////////////////////////////////////////////
//  CMainWindow::InvalidateLine
//  Marks the screen area of the currently selected person as
//  invalid causing windows to call OnPaint to redraw the area.
//  This is normally used when the selected line is being changed.
//
void CMainWindow::InvalidateLine()
{
	CRect area( 0, ( m_nSelectLine - m_nVscrollPos ) * m_cyChar, m_cxClient,
				( m_nSelectLine + 1 - m_nVscrollPos ) * m_cyChar );
	InvalidateRect( area );
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.