The code shown in listing 1 and 2 is available on the distribution disks in files VIEW.H and VIEW.CPP.
// 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__
// 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" );
}
}
//////////////////////////////////////////////////
// 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" );
}
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 );
}