5.5 File Listings

The code shown in listing 1 and 2 is available on the distribution disks in files VIEW.H and VIEW.CPP.

Listing 1

// view.h : Declares the interfaces to the application and frame window.

//

// This is a part of the Microsoft Foundation Classes C++ library.

// Copyright (C) 1991 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.

#ifndef __VIEW_H__

#define __VIEW_H__

/////////////////////////////////////////////////////////////////////////////

// CTheApp

// Derived from CWinApp in order to allow us to override

// the InitInstance member function to create our own window.

//

class CTheApp : public CWinApp

{

public:

virtual BOOL InitInstance();

};

/////////////////////////////////////////////////////////////////////////////

// CFindDialog

// This dialog is a one line entry field for getting a search

// string to use as a find filter for the database

//

class CFindDialog : public CModalDialog

{

private:

CString m_szFindName;

public:

CFindDialog( CWnd* pParentWnd = NULL )

: CModalDialog( "Find", pParentWnd )

{ }

virtual void OnOK();

CString& GetFindString() { return m_szFindName; }

};

/////////////////////////////////////////////////////////////////////////////

// CEditDialog

// Used to add or edit a Person object.

//

class CEditDialog : public CModalDialog

{

private:

CPerson* m_pData;

public:

CEditDialog( CPerson* person, CWnd* pParentWnd = NULL )

: CModalDialog( "EditPerson", pParentWnd )

{ m_pData = person; }

virtual BOOL OnInitDialog();

virtual void OnOK();

CPerson* GetData()

{ return m_pData; }

};

/////////////////////////////////////////////////////////////////////////////

// CMainWindow

// The window object that WinApp creates. In this program we

// only use one window class. In that sense this object does

// all the work that makes our window a CPersonList viewer.

//

class CMainWindow : public CFrameWnd

{

private:

// Variables that contain the window size, font size and scroll

// position.

int m_cxChar;

int m_cyChar;

int m_nHscrollPos;

int m_nVscrollPos;

int m_cxCaps;

int m_nMaxWidth;

int m_cxClient;

int m_cyClient;

int m_nVscrollMax;

int m_nHscrollMax;

int m_nSelectLine;

CDataBase m_people;

// Private helpers for the other routines.

void SetMenu();

BOOL Save( BOOL bNamed=FALSE );

BOOL FileDlg( BOOL bOpen, int nMaxFile, LPSTR szFile,

int nMaxFileTitle, LPSTR szFileTitle );

BOOL CheckForSave( const char* pszTitle, const char* pszMessage );

void InvalidateLine();

public:

// The CMainWindow constructor

CMainWindow();

// These routines are all overrides of CWnd. Windows messages

// cause these to be called.

afx_msg int OnCreate( LPCREATESTRUCT cs );

afx_msg void OnClose();

afx_msg void OnSize( UINT type, int x, int y );

afx_msg void OnHScroll( UINT nSBCode, UINT pos, CWnd* control );

afx_msg void OnVScroll( UINT nSBCode, UINT pos, CWnd* control );

afx_msg void OnLButtonDown( UINT wParam, CPoint location );

afx_msg void OnLButtonDblClk( UINT wParam, CPoint location );

afx_msg void OnKeyDown( UINT wParam, UINT, UINT );

afx_msg void OnPaint();

// These routines are all menu items. User action causes

// these to be called.

afx_msg void OnNew();

afx_msg void OnOpen();

afx_msg void OnSave();

afx_msg void OnSaveAs();

afx_msg void OnDBClose();

afx_msg void OnPrint();

afx_msg void OnExit();

afx_msg void OnAdd();

afx_msg void OnDelete();

afx_msg void OnFind();

afx_msg void OnFindAll();

afx_msg void OnEdit();

afx_msg void OnHelp();

afx_msg void OnAbout();

afx_msg void OnUp();

afx_msg void OnDown();

DECLARE_MESSAGE_MAP()

};

/////////////////////////////////////////////////////////////////////////////

#endif // __VIEW_H__

Listing 2

// view.cpp : Defines the behaviors for the application and frame window.

//

// This is a part of the Microsoft Foundation Classes C++ library.

// Copyright (C) 1991 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"

extern "C"

{

#include <commdlg.h>

}

#define SIZESTRING 256

#define SIZENAME 30

#define SIZEPHONE 26

#define PAGESIZE 8

// a simple way to reduce size of C run times

// 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( IDM_PDATA, m_szFindName.GetBuffer( SIZESTRING ), SIZESTRING );

m_szFindName.ReleaseBuffer();

EndDialog( IDOK );

}

/////////////////////////////////////////////////////////////////////////////

// CEditPerson

//////////////////////////////////////////////////

// CEditDialog::OnInitDialog

// Fill in the fields with 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[25];

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[25];

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=(HANDLE)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 hourglass while printing

SetCursor( AfxGetApp() -> LoadStandardCursor( IDC_WAIT ) );

// Begin printing the document.

int rc;

char szError[50];

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;

UINT 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

UINT nExtPage = ( dc -> GetDeviceCaps(VERTRES) - nCharHeight )

/ nCharHeight;

CString pszTitle;

pszTitle = 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, pszTitle, pszTitle.GetLength() );

dc -> MoveTo( 0, nCharHeight );

dc -> LineTo( dc -> GetTextExtent( pszTitle, pszTitle.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_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_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 destroy 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, CWnd* )

{

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, CWnd* )

{

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 displayed 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 = (LPSTR)szFilter;

of.lpstrCustomFilter = (LPSTR)NULL;

of.nMaxCustFilter = 0L;

of.nFilterIndex = 1L;

of.lpstrFile=szFile;

of.nMaxFile=nMaxFile;

of.lpszFileTitle = szFileTitle;

of.nMaxFileTitle = nMaxFileTitle;

of.lpstrInitialDir = szDirName;

of.lpstrTitle = (LPSTR)NULL;

of.nFileOffset = 0;

of.nFileExtension = 0;

of.lpstrDefExt = (LPSTR)"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[25];

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[25];

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 to 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 existence 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 );

}