|
|
1.1 root 1: // view.cpp : Defines the behaviors for the application and frame window.
2: //
3: // This is a part of the Microsoft Foundation Classes C++ library.
4: // Copyright (C) 1992 Microsoft Corporation
5: // All rights reserved.
6: //
7: // This source code is only intended as a supplement to the
8: // Microsoft Foundation Classes Reference and Microsoft
9: // QuickHelp documentation provided with the library.
10: // See these sources for detailed information regarding the
11: // Microsoft Foundation Classes product.
12:
13: #include <afxwin.h>
14:
15: #include "resource.h"
16: #include "database.h"
17: #include "view.h"
18:
19: #include <commdlg.h>
20:
21: #define SIZESTRING 256
22: #define SIZENAME 30
23: #define SIZEPHONE 26
24: #define PAGESIZE 8
25:
26: // a simple way to reduce size of C runtimes
27: // disables the use of getenv and argv/argc
28: extern "C" void _setargv() { }
29: extern "C" void _setenvp() { }
30:
31: /////////////////////////////////////////////////////////////////////////////
32: // Create the single global instance of the database viewer app.
33:
34: CTheApp PersonApp;
35:
36: /////////////////////////////////////////////////////////////////////////////
37: // CTheApp
38:
39: //////////////////////////////////////////////////
40: // CtheApp::InitInstance
41: // Override InitInstance function to create a CMainWindow object
42: // and display it on the screen
43: //
44: BOOL CTheApp::InitInstance()
45: {
46: m_pMainWnd = new CMainWindow();
47: m_pMainWnd->ShowWindow( m_nCmdShow );
48: m_pMainWnd->UpdateWindow();
49: return TRUE;
50: }
51:
52: /////////////////////////////////////////////////////////////////////////////
53: // CFindDialog
54:
55: //////////////////////////////////////////////////
56: // CFindDialog::OnOK
57: // When the user hits OK get the data entered and store it in this
58: // object. Then end the dialog.
59: //
60: void CFindDialog::OnOK()
61: {
62: GetDlgItemText( IDC_DATA, m_szFindName.GetBuffer( SIZESTRING ), SIZESTRING );
63: m_szFindName.ReleaseBuffer();
64: EndDialog( IDOK );
65: }
66:
67: /////////////////////////////////////////////////////////////////////////////
68: // CEditPerson
69:
70: //////////////////////////////////////////////////
71: // CEditDialog::OnInitDialog
72: // Fill in the fields witht the data placed in this object
73: // when it was created.
74: //
75: BOOL CEditDialog::OnInitDialog()
76: {
77: SetDlgItemText( IDC_LASTNAME, m_pData->GetLastName() );
78: SetDlgItemText( IDC_FIRSTNAME, m_pData->GetFirstName() );
79: SetDlgItemText( IDC_PHONE, m_pData->GetPhoneNumber() );
80: SetDlgItemText( IDC_MOD, m_pData->GetModTime().Format("%m/%d/%y %H:%M" ) );
81: SendDlgItemMessage( IDC_LASTNAME, EM_SETSEL );
82:
83: return TRUE;
84: }
85:
86: //////////////////////////////////////////////////
87: // CEditDialog::OnOK
88: // When OK is pressed set the data to what the user has entered.
89: //
90: void CEditDialog::OnOK()
91: {
92: char szTmp[SIZESTRING];
93:
94: GetDlgItemText( IDC_LASTNAME, szTmp, sizeof ( szTmp ) );
95: m_pData->SetLastName( szTmp );
96:
97: GetDlgItemText( IDC_FIRSTNAME, szTmp, sizeof ( szTmp ) );
98: m_pData->SetFirstName( szTmp );
99:
100: GetDlgItemText( IDC_PHONE, szTmp, sizeof ( szTmp ) );
101: m_pData->SetPhoneNumber( szTmp );
102:
103: EndDialog( IDOK );
104: }
105:
106: /////////////////////////////////////////////////////////////////////////////
107: // CMainWindow
108:
109: BEGIN_MESSAGE_MAP( CMainWindow, CFrameWnd )
110:
111: // File menu commands:
112: ON_COMMAND( IDM_NEW, OnNew )
113: ON_COMMAND( IDM_OPEN, OnOpen )
114: ON_COMMAND( IDM_SAVE, OnSave )
115: ON_COMMAND( IDM_SAVEAS, OnSaveAs )
116: ON_COMMAND( IDM_CLOSE, OnDBClose )
117: ON_COMMAND( IDM_PRINT, OnPrint )
118: ON_COMMAND( IDM_EXIT, OnExit )
119:
120: // Person menu commands:
121: ON_COMMAND( IDM_ADD, OnAdd )
122: ON_COMMAND( IDM_DELETE, OnDelete )
123: ON_COMMAND( IDM_EDIT, OnEdit )
124: ON_COMMAND( IDM_FIND, OnFind )
125: ON_COMMAND( IDM_FINDALL, OnFindAll )
126:
127: // Help menu commands:
128: ON_COMMAND( IDM_HELP, OnHelp )
129: ON_COMMAND( IDM_ABOUT, OnAbout )
130:
131: // Selection accelerators:
132: ON_COMMAND( VK_UP, OnUp )
133: ON_COMMAND( VK_DOWN, OnDown )
134:
135: // Other Windows messages:
136: ON_WM_CREATE()
137: ON_WM_CLOSE()
138: ON_WM_SIZE()
139: ON_WM_VSCROLL()
140: ON_WM_HSCROLL()
141: ON_WM_LBUTTONDOWN()
142: ON_WM_LBUTTONDBLCLK()
143: ON_WM_KEYDOWN()
144: ON_WM_PAINT()
145: END_MESSAGE_MAP()
146:
147:
148: //////////////////////////////////////////////////
149: // CMainWindow::CMainWindow
150: // Constructs and initializes a CMainWindow object
151: //
152: CMainWindow::CMainWindow()
153: {
154: VERIFY( LoadAccelTable( "MainAccelTable" ) );
155: VERIFY( Create( NULL, "Phone Book",
156: WS_OVERLAPPEDWINDOW, rectDefault, NULL, "MainMenu" ) );
157: m_nSelectLine = -1;
158: }
159:
160: /////////////////////////////////////////////////////////////////////////////
161: // The Following are CMainWindow Menu Items
162:
163: //////////////////////////////////////////////////
164: // CMainWindow::OnNew
165: // After checking to see if current data needs to be stored, call
166: // database New and resize/repaint the window.
167: //
168: void CMainWindow::OnNew()
169: {
170: if ( !CheckForSave( "File New", "Save file before New?" ) )
171: return;
172: m_people.New();
173: SetMenu();
174: SetWindowText( m_people.GetTitle() );
175: OnSize( 0, m_cxClient, m_cyClient );
176: }
177:
178: //////////////////////////////////////////////////
179: // CMainWindow::OnOpen
180: //
181: void CMainWindow::OnOpen()
182: {
183: if ( !CheckForSave( "File Open", "Save file before Open?" ) )
184: return;
185:
186: // Attempt to open a database file and read it.
187: // If a file or archive exception occurs, catch it and
188: // present an error message box.
189: CString szFileName, szFileTitle;
190: TRY
191: {
192: // Use CommDlg to get the file name and then call DoOpen.
193: // Set the Window title and menus. Resize/Repaint.
194: if ( FileDlg( TRUE, SIZESTRING, szFileName.GetBuffer( SIZESTRING ),
195: SIZESTRING, szFileTitle.GetBuffer( SIZESTRING ) ) )
196: {
197: szFileName.ReleaseBuffer();
198: szFileTitle.ReleaseBuffer();
199: m_people.DoOpen( szFileName );
200: m_people.SetTitle( szFileTitle );
201: SetWindowText( m_people.GetTitle() );
202: SetMenu();
203: OnSize( 0, m_cxClient, m_cyClient );
204: }
205: }
206: CATCH( CFileException, e )
207: {
208: char ErrorMsg[SIZESTRING];
209: sprintf( ErrorMsg,"Opening %s returned a 0x%lx.",
210: (const char*)szFileTitle, e->m_lOsError );
211: MessageBox( ErrorMsg, "File Open Error" );
212: }
213: AND_CATCH( CArchiveException, e )
214: {
215: char ErrorMsg[SIZESTRING];
216: sprintf( ErrorMsg,"Reading the %s archive failed.",
217: (const char*)szFileTitle );
218: MessageBox( ErrorMsg, "File Open Error" );
219: }
220: END_CATCH
221: }
222:
223: //////////////////////////////////////////////////
224: // CMainWindow::OnSave
225: //
226: void CMainWindow::OnSave()
227: {
228: Save( m_people.IsNamed() );
229: }
230:
231: //////////////////////////////////////////////////
232: // CMainWindow::OnSaveAs
233: //
234: void CMainWindow::OnSaveAs()
235: {
236: Save();
237: }
238:
239: //////////////////////////////////////////////////
240: // CMainWindow::OnDBClose
241: // Closes the current database, checking to see if it should be
242: // saved first. Reset the window title and the scroll regions.
243: // Invalidating the entire screen causes OnPaint to repaint but
244: // this time without any data.
245: //
246: void CMainWindow::OnDBClose()
247: {
248: if ( !CheckForSave( "File Close", "Save file before closing?" ) )
249: return;
250: m_people.Terminate();
251: SetWindowText( "Phone Book" );
252: SetMenu();
253: OnSize( 0, m_cxClient, m_cyClient );
254: }
255:
256: //////////////////////////////////////////////////
257: // CMainWindow::OnPrint
258: // Uses the commdlg print dialog to create a printer dc
259: // Then it uses code almost identical to the OnPaint code
260: // to write the data to the printer.
261: //
262: void CMainWindow::OnPrint()
263: {
264:
265: PRINTDLG pd;
266:
267: pd.lStructSize = sizeof( PRINTDLG );
268: pd.hwndOwner=m_hWnd;
269: pd.hDevMode=(HANDLE)NULL;
270: pd.hDevNames=(HANDLE)NULL;
271: pd.Flags=PD_RETURNDC | PD_NOSELECTION | PD_NOPAGENUMS;
272: pd.nFromPage=0;
273: pd.nToPage=0;
274: pd.nMinPage=0;
275: pd.nMaxPage=0;
276: pd.nCopies=1;
277: pd.hInstance=(HINSTANCE)NULL;
278:
279: if ( PrintDlg( &pd ) != 0 )
280: {
281: // CommDlg returned a DC so create a CDC object from it.
282: ASSERT( pd.hDC != 0 );
283: CDC * dc;
284: dc = CDC::FromHandle( pd.hDC );
285:
286: // Change to hour glass while printing
287: SetCursor( AfxGetApp()->LoadStandardCursor( IDC_WAIT ) );
288:
289: // Begin printing the document.
290: int rc;
291: char szError[SIZESTRING];
292: #ifndef _NTWIN
293: rc = dc->StartDoc( "Phone Book" );
294: #else
295: DOCINFO di;
296: di.cbSize = sizeof(di);
297: di.lpszDocName = "Phone Book";
298: di.lpszOutput = NULL;
299: rc = dc->StartDoc(&di);
300: #endif
301: if ( rc < 0 )
302: {
303: sprintf( szError, "Unable to Begin printing - Error[%d]", rc );
304: MessageBox( szError, NULL,MB_OK );
305: return;
306: }
307:
308: int x, y;
309: CPerson* pCurrent;
310: int nPerson=0;
311: CString szDisplay;
312: int nStart, nEnd;
313:
314: // Get Height and Width of large character
315: CSize extentChar = dc->GetTextExtent( "M", 1 );
316: int nCharHeight = extentChar.cy;
317: int nCharWidth = extentChar.cx;
318:
319: // Get Page size in # of full lines
320: int nExtPage = ( dc->GetDeviceCaps(VERTRES) - nCharHeight )
321: / nCharHeight;
322:
323: CString szTitle;
324: szTitle = CString( "Phone Book - " ) + m_people.GetName();
325:
326: while ( nPerson != m_people.GetCount() )
327: {
328: // Print a Page Header
329: dc->StartPage();
330: dc->SetTextAlign ( TA_LEFT | TA_TOP );
331: dc->TextOut( 0, 0, szTitle, szTitle.GetLength() );
332: dc->MoveTo( 0, nCharHeight );
333: dc->LineTo(
334: dc->GetTextExtent( szTitle, szTitle.GetLength() ).cx,
335: nCharHeight );
336:
337: // Print People from start to last person or page size minus
338: // 2 ( header size )
339: nEnd = min( m_people.GetCount() - nPerson, nExtPage-2 );
340: for ( nStart = 0; nStart < nEnd; nStart++, nPerson++ )
341: {
342: x = 0;
343: y = nCharHeight * ( nStart+2 );
344:
345: pCurrent = m_people.GetPerson( nPerson );
346: szDisplay = " " + pCurrent->GetLastName() + ", " +
347: pCurrent->GetFirstName();
348: dc->SetTextAlign( TA_LEFT | TA_TOP );
349: dc->TextOut( x, y, szDisplay, szDisplay.GetLength() );
350:
351: szDisplay = pCurrent->GetPhoneNumber();
352: dc->SetTextAlign( TA_RIGHT | TA_TOP );
353: dc->TextOut( x + SIZENAME * nCharWidth, y, szDisplay,
354: szDisplay.GetLength() );
355:
356: szDisplay = pCurrent->GetModTime().Format( "%m/%d/%y %H:%M" );
357: dc->TextOut( x + ( SIZENAME + SIZEPHONE ) * nCharWidth, y,
358: szDisplay, szDisplay.GetLength() );
359: }
360: dc->EndPage();
361: }
362: dc->EndDoc();
363: dc->DeleteDC();
364: SetCursor( AfxGetApp()->LoadStandardCursor( IDC_ARROW ) );
365: }
366: }
367:
368: //////////////////////////////////////////////////
369: // CMainWindow::OnExit
370: //
371: void CMainWindow::OnExit()
372: {
373: OnClose();
374: }
375:
376: //////////////////////////////////////////////////
377: // CMainWindow::OnAdd
378: // Using the EditDialog fill in a new person object. If the user
379: // selects OK then add the person, call OnSize to resize the scroll
380: // region, and invalidate the screen so it will be redrawn with the
381: // new person in the correct order.
382: //
383: void CMainWindow::OnAdd()
384: {
385: CPerson* person=new CPerson();
386:
387: CEditDialog dlgAdd( person, this );
388: if ( dlgAdd.DoModal() == IDOK )
389: {
390: m_people.AddPerson( person );
391: OnSize( 0, m_cxClient, m_cyClient );
392: }
393: else
394: delete person;
395: }
396:
397: //////////////////////////////////////////////////
398: // CMainWindow::OnDelete
399: // Deletes the current selection. Check to see if the selection is
400: // now past then end of the list. Also call OnSize since the list
401: // length has now changed.
402: //
403: void CMainWindow::OnDelete()
404: {
405: if ( m_nSelectLine == -1 )
406: {
407: MessageBox( "Select a person to delete first" );
408: return;
409: }
410: m_people.DeletePerson( m_nSelectLine );
411: if ( m_nSelectLine >= (int)m_people.GetCount() )
412: m_nSelectLine--;
413: OnSize( 0, m_cxClient, m_cyClient );
414: }
415:
416: ////////////////////////////////////////////////////
417: // CMainWindow::OnFind
418: // Gets information from the CFindDialog modal dialog box, then searches for
419: // matching people. Note the Add and Delete menu items are disabled after
420: // a find is made. Find All is enabled.
421: //
422: void CMainWindow::OnFind()
423: {
424: CFindDialog dlgFind( this );
425: if ( dlgFind.DoModal() == IDOK &&
426: dlgFind.GetFindString().GetLength() != 0 )
427: {
428: if ( m_people.DoFind( dlgFind.GetFindString() ) )
429: {
430: m_nSelectLine = -1;
431: CString tmp;
432: tmp = m_people.GetTitle() + " Found: "
433: + dlgFind.GetFindString();
434: SetWindowText( tmp );
435: CMenu* pMenu = GetMenu();
436: pMenu->EnableMenuItem( IDM_FINDALL, MF_ENABLED );
437: pMenu->EnableMenuItem( IDM_FIND, MF_GRAYED );
438: pMenu->EnableMenuItem( IDM_DELETE, MF_GRAYED );
439: pMenu->EnableMenuItem( IDM_ADD, MF_GRAYED );
440: OnSize( 0, m_cxClient, m_cyClient );
441: }
442: else
443: MessageBox( "No match found in list." );
444: }
445: }
446:
447: ////////////////////////////////////////////////////
448: // CMainWindow::OnFindAll
449: // Returns to view the whole database. Add, Delete are re-enabled, and
450: // Find All is again disabled. OnSize is called because the list
451: // has changed length.
452: //
453: void CMainWindow::OnFindAll()
454: {
455: m_people.DoFind();
456: SetWindowText( m_people.GetTitle() );
457: CMenu* pMenu = GetMenu();
458: pMenu->EnableMenuItem( IDM_FINDALL, MF_GRAYED );
459: pMenu->EnableMenuItem( IDM_FIND, MF_ENABLED );
460: pMenu->EnableMenuItem( IDM_DELETE, MF_ENABLED );
461: pMenu->EnableMenuItem( IDM_ADD, MF_ENABLED );
462: OnSize( 0, m_cxClient, m_cyClient );
463: }
464:
465: ////////////////////////////////////////////////////
466: // CMainWindow::OnEdit
467: // Using the member variable m_nSelectLine a CEditDialog is created
468: // and filled with the selected person. If the dialog OK button is
469: // used the dialog saves the changes into the object.
470: // over any old information.
471: //
472: void CMainWindow::OnEdit()
473: {
474: if ( m_nSelectLine == -1 )
475: {
476: MessageBox( "Select a person to edit first" );
477: return;
478: }
479:
480: // Get a pointer to the person in the list.
481: CPerson* pPerson = m_people.GetPerson( m_nSelectLine );
482: CPerson tmpPerson = *pPerson;
483:
484: //Edit the data.
485: CEditDialog dlgEdit( &tmpPerson, this );
486:
487: //if the ok button is pressed redraw the screen
488: if ( dlgEdit.DoModal() == IDOK )
489: {
490: m_people.ReplacePerson( pPerson, tmpPerson );
491: InvalidateLine();
492: }
493: }
494:
495: //////////////////////////////////////////////////
496: // CMainWindow::OnHelp
497: //
498: void CMainWindow::OnHelp()
499: {
500: if ( !m_people.IsPresent() )
501: {
502: CModalDialog dlgHelp( "NoData", this );
503: dlgHelp.DoModal();
504: return;
505: }
506:
507: if ( !m_people.IsNamed() )
508: {
509: CModalDialog dlgHelp( "NoName", this );
510: if ( dlgHelp.DoModal() == IDCANCEL )
511: return;
512: }
513:
514: CModalDialog dlgHelp( "Enter", this );
515: dlgHelp.DoModal();
516: }
517:
518: //////////////////////////////////////////////////
519: // CMainWindow::OnAbout
520: //
521: void CMainWindow::OnAbout()
522: {
523: CModalDialog dlgAbout( "AboutBox", this );
524: dlgAbout.DoModal();
525: }
526:
527: /////////////////////////////////////////////////////////////////////////////
528: // The Following are WINDOW messages
529:
530: //////////////////////////////////////////////////
531: // CMainWindow::OnCreate
532: // Queries the current text metrics to determine char size.
533: //
534: int CMainWindow::OnCreate( LPCREATESTRUCT )
535: {
536: TEXTMETRIC tm;
537:
538: // Get the text metrics.
539: CDC* dc = GetDC();
540: dc->GetTextMetrics( &tm );
541: ReleaseDC( dc );
542:
543: // Decide the statistics on how many rows, etc., we can display.
544: m_cxChar = tm.tmAveCharWidth;
545: m_cxCaps = ( (tm.tmPitchAndFamily & 1 )? 3 : 2 ) * m_cxChar / 2;
546: m_cyChar = tm.tmHeight + tm.tmExternalLeading;
547: m_nMaxWidth = ( SIZENAME + SIZEPHONE + 1 ) * m_cxCaps;
548: m_nVscrollPos = m_nHscrollPos = 0;
549:
550: return 0;
551: }
552:
553: //////////////////////////////////////////////////
554: // CMainWindow::OnClose
555: // Check to see if the current file needs to be saved. Terminate
556: // the database and destory the window.
557: //
558: void CMainWindow::OnClose()
559: {
560: if ( !CheckForSave( "File Exit", "Save file before exit?" ) )
561: return;
562: m_people.Terminate();
563: DestroyWindow();
564: }
565:
566: //////////////////////////////////////////////////
567: // CMainWindow::OnSize
568: // When resized, we need to recalculate our scrollbar ranges based on what
569: // part of the database is visible.
570: //
571: void CMainWindow::OnSize( UINT, int x, int y )
572: {
573: m_cxClient = x;
574: m_cyClient = y;
575:
576: m_nVscrollMax = max( 0,
577: (int)( m_people.GetCount() ) - m_cyClient / m_cyChar );
578: m_nVscrollPos = min( m_nVscrollPos, m_nVscrollMax );
579:
580: SetScrollRange( SB_VERT, 0, m_nVscrollMax, FALSE );
581: SetScrollPos( SB_VERT, m_nVscrollPos, TRUE );
582:
583: m_nHscrollMax = max( 0, ( m_nMaxWidth - m_cxClient ) / m_cxChar );
584: m_nHscrollPos = min( m_nHscrollPos, m_nHscrollMax );
585:
586: SetScrollRange( SB_HORZ, 0, m_nHscrollMax, FALSE );
587: SetScrollPos( SB_HORZ, m_nHscrollPos, TRUE );
588: Invalidate( TRUE );
589: }
590:
591: //////////////////////////////////////////////////
592: // CMainWindow::OnVScroll
593: // Translate scroll messages into Scroll increments and then
594: // checks the current position to determine if scrolling is possible
595: //
596: void CMainWindow::OnVScroll( UINT wParam, UINT pos, CScrollBar* )
597: {
598: short nScrollInc;
599:
600: switch ( wParam )
601: {
602: case SB_TOP:
603: nScrollInc = -m_nVscrollPos;
604: break;
605:
606: case SB_BOTTOM:
607: nScrollInc = m_nVscrollMax - m_nVscrollPos;
608: break;
609:
610: case SB_LINEUP:
611: nScrollInc = -1;
612: break;
613:
614: case SB_LINEDOWN:
615: nScrollInc = 1;
616: break;
617:
618: case SB_PAGEUP:
619: nScrollInc = min( -1, -m_cyClient / m_cyChar );
620: break;
621:
622: case SB_PAGEDOWN:
623: nScrollInc = max( 1, m_cyClient / m_cyChar );
624: break;
625:
626: case SB_THUMBTRACK:
627: nScrollInc = pos - m_nVscrollPos;
628: break;
629:
630: default:
631: nScrollInc = 0;
632: }
633:
634: if ( nScrollInc = max( -m_nVscrollPos,
635: min( nScrollInc, m_nVscrollMax - m_nVscrollPos ) ) )
636: {
637: m_nVscrollPos += nScrollInc;
638: ScrollWindow( 0, -m_cyChar * nScrollInc );
639: SetScrollPos( SB_VERT, m_nVscrollPos );
640: UpdateWindow();
641: }
642: }
643:
644: //////////////////////////////////////////////////
645: // CMainWindow::OnHScroll
646: // Translate scroll messages into Scroll increments and then
647: // checks the current position to determine if scrolling is possible
648: //
649: void CMainWindow::OnHScroll( UINT wParam, UINT pos, CScrollBar* )
650: {
651: int nScrollInc;
652: switch ( wParam )
653: {
654: case SB_LINEUP:
655: nScrollInc = -1;
656: break;
657:
658: case SB_LINEDOWN:
659: nScrollInc = 1;
660: break;
661:
662: case SB_PAGEUP:
663: nScrollInc = -PAGESIZE;
664: break;
665:
666: case SB_PAGEDOWN:
667: nScrollInc = PAGESIZE;
668: break;
669:
670: case SB_THUMBPOSITION:
671: nScrollInc = pos - m_nHscrollPos;
672: break;
673:
674: default:
675: nScrollInc = 0;
676: }
677:
678: if ( nScrollInc = max( -m_nHscrollPos,
679: min( nScrollInc, m_nHscrollMax - m_nHscrollPos ) ) )
680: {
681: m_nHscrollPos += nScrollInc;
682: ScrollWindow( -m_cxChar * nScrollInc, 0 );
683: SetScrollPos( SB_HORZ, m_nHscrollPos );
684: UpdateWindow();
685: }
686: }
687:
688: //////////////////////////////////////////////////
689: // CMainWindow::OnUp
690: // Uses Accelerator tables to link the up arrow key to this
691: // routine. Decrements the select line with checking for scrolling
692: // and wrapping off the top of the list.
693: //
694: //
695: void CMainWindow::OnUp()
696: {
697: InvalidateLine();
698:
699: if ( m_nSelectLine <= 0 )
700: {
701: m_nSelectLine = m_people.GetCount() - 1;
702: m_nVscrollPos = max( 0, m_nSelectLine + 1 - ( m_cyClient / m_cyChar ) );
703: Invalidate( TRUE );
704: }
705: else
706: {
707: m_nSelectLine--;
708: if ( m_nSelectLine - m_nVscrollPos < 0 )
709: OnVScroll( SB_LINEUP, 0, NULL );
710:
711: // Selection is off the screen
712: if ( m_nSelectLine - m_nVscrollPos > ( m_cyClient / m_cyChar ) )
713: {
714: m_nVscrollPos = m_nSelectLine + 1 - ( m_cyClient / m_cyChar );
715: SetScrollPos( SB_VERT, m_nVscrollPos, TRUE );
716: Invalidate( TRUE );
717: }
718: if ( m_nSelectLine - m_nVscrollPos < 0 )
719: {
720: m_nVscrollPos = m_nSelectLine;
721: SetScrollPos( SB_VERT, m_nVscrollPos, TRUE );
722: Invalidate( TRUE );
723: }
724: }
725:
726: InvalidateLine();
727: }
728:
729: //////////////////////////////////////////////////
730: // CMainWindow::OnDown
731: // Uses Accelerator tables to link the down arrow key to this
732: // routine. Inc the select line with checking for scrolling
733: // and wrapping off the bottom of the list.
734: //
735: void CMainWindow::OnDown()
736: {
737: InvalidateLine();
738:
739: if ( m_nSelectLine == (int)( m_people.GetCount() - 1 )
740: || m_nSelectLine == -1 )
741: {
742: m_nSelectLine = 0;
743: m_nVscrollPos = 0;
744: Invalidate( TRUE );
745: }
746: else
747: {
748: m_nSelectLine++;
749: if ( ( m_nSelectLine - m_nVscrollPos + 1 ) > ( m_cyClient / m_cyChar ) )
750: OnVScroll( SB_LINEDOWN, 0, NULL );
751:
752: // Selection is off the screen
753: if ( ( m_nSelectLine - m_nVscrollPos ) > ( m_cyClient / m_cyChar ) )
754: {
755: m_nVscrollPos = m_nSelectLine + 1 - ( m_cyClient / m_cyChar );
756: SetScrollPos( SB_VERT, m_nVscrollPos, TRUE );
757: Invalidate( TRUE );
758: }
759: if ( ( m_nSelectLine - m_nVscrollPos ) < 0 )
760: {
761: m_nVscrollPos = m_nSelectLine;
762: SetScrollPos( SB_VERT, m_nVscrollPos, TRUE );
763: Invalidate( TRUE );
764: }
765: }
766:
767: InvalidateLine();
768: }
769:
770: //////////////////////////////////////////////////
771: // CMainWindow::OnLButtonDown
772: // Turns the location of the mouse pointer into a line number
773: // and stores that information in m_nSelectLine. Uses
774: // InvalidateLine to cause OnPaint to change the screen.
775: //
776: void CMainWindow::OnLButtonDown( UINT, CPoint location )
777: {
778: InvalidateLine();
779:
780: int pos = m_nVscrollPos + location.y / m_cyChar;
781:
782: if ( ( m_nSelectLine != pos ) && ( pos < (int)m_people.GetCount() ) )
783: {
784: m_nSelectLine = pos;
785: InvalidateLine();
786: }
787: else
788: m_nSelectLine = -1;
789: }
790:
791: //////////////////////////////////////////////////
792: // CMainWindow::OnLButtonDblClk
793: // Translates mouse left button double click into edit person.
794: //
795: void CMainWindow::OnLButtonDblClk( UINT wParam, CPoint location )
796: {
797: if ( m_nSelectLine == -1 )
798: OnLButtonDown( wParam, location );
799: OnEdit();
800: }
801:
802: //////////////////////////////////////////////////
803: // CMainWindow::OnKeyDown
804: // Translates keyboard input into scroll messages
805: //
806: void CMainWindow::OnKeyDown( UINT wParam, UINT, UINT )
807: {
808: switch ( wParam )
809: {
810: case VK_HOME:
811: OnVScroll( SB_TOP, 0, NULL );
812: break;
813: case VK_END:
814: OnVScroll( SB_BOTTOM, 0, NULL );
815: break;
816: case VK_PRIOR:
817: OnVScroll( SB_PAGEUP, 0, NULL );
818: break;
819: case VK_NEXT:
820: OnVScroll( SB_PAGEDOWN, 0, NULL );
821: break;
822: case VK_LEFT:
823: OnHScroll( SB_PAGEUP, 0, NULL );
824: break;
825: case VK_RIGHT:
826: OnHScroll( SB_PAGEDOWN, 0, NULL );
827: break;
828: }
829: }
830:
831: //////////////////////////////////////////////////
832: // CMainWindow::OnPaint
833: // This routine does all the painting for the screen.
834: //
835: void CMainWindow::OnPaint()
836: {
837:
838: CPaintDC dc( this );
839:
840: // Set the Text and background colors for the DC also create a Brush
841: CBrush bBack;
842: dc.SetTextColor( GetSysColor( COLOR_WINDOWTEXT ) );
843: dc.SetBkColor( GetSysColor( COLOR_WINDOW ) );
844: bBack.CreateSolidBrush( GetSysColor( COLOR_WINDOW ) );
845:
846: // Compute the lines that need to be redrawn
847: int nStart = max( 0, m_nVscrollPos + dc.m_ps.rcPaint.top / m_cyChar - 1 );
848: int nEnd = min( (int)m_people.GetCount(),
849: m_nVscrollPos + ( dc.m_ps.rcPaint.bottom / m_cyChar+1 ) );
850:
851: // Create a rect the width of the display.
852: CRect area( 0, 0, m_cxClient, 0 );
853:
854: CString szDisplay;
855: CPerson* pCurrent;
856: int x,y;
857: for ( ;nStart < nEnd; nStart++ )
858: {
859: // if the current line is the select line then change the
860: // colors to the highlight text colors.
861: if ( m_nSelectLine == nStart )
862: {
863: bBack.DeleteObject();
864: bBack.CreateSolidBrush( GetSysColor( COLOR_HIGHLIGHT ) );
865: dc.SetTextColor( GetSysColor( COLOR_HIGHLIGHTTEXT ) );
866: dc.SetBkColor( GetSysColor( COLOR_HIGHLIGHT ) );
867: }
868:
869: // x is the number of characters horz scrolled * the width of
870: // char. y is the current line no. - number of lines scrolled
871: // times the height of a line.
872: x = m_cxChar * ( -m_nHscrollPos );
873: y = m_cyChar * ( nStart - m_nVscrollPos );
874:
875: // Set the rect to y and y + the height of the line. Fill the
876: // rect with the background color.
877: area.top = y;
878: area.bottom = y+ m_cyChar;
879: dc.FillRect( area, &bBack );
880:
881: // Get the person and build a string with his name.
882: pCurrent = m_people.GetPerson( nStart );
883: szDisplay = " " + pCurrent->GetLastName() + ", " +
884: pCurrent->GetFirstName();
885:
886: // Set the dc to write using the point as the left top of the
887: // character. Write the name.
888: dc.SetTextAlign( TA_LEFT | TA_TOP );
889: dc.TextOut ( x, y,szDisplay, szDisplay.GetLength() );
890:
891: // Write the phone number right aligned.
892: szDisplay = pCurrent->GetPhoneNumber();
893: dc.SetTextAlign ( TA_RIGHT | TA_TOP );
894: dc.TextOut ( x + SIZENAME * m_cxCaps, y, szDisplay,
895: szDisplay.GetLength() );
896:
897: // Write the time.
898: szDisplay = pCurrent->GetModTime().Format( "%m/%d/%y %H:%M" );
899: dc.TextOut ( x + ( SIZENAME + SIZEPHONE ) * m_cxCaps, y,
900: szDisplay, szDisplay.GetLength() );
901:
902: // If this is the select line then we need to reset the dc
903: // colors back to the original colors.
904: if ( m_nSelectLine == nStart )
905: {
906: bBack.DeleteObject();
907: bBack.CreateSolidBrush( GetSysColor( COLOR_WINDOW ) );
908: dc.SetTextColor( GetSysColor( COLOR_WINDOWTEXT ) );
909: dc.SetBkColor( GetSysColor( COLOR_WINDOW ) );
910: }
911: }
912: }
913:
914: /////////////////////////////////////////////////////////////////////////////
915: // The following are utility routines
916:
917:
918: //////////////////////////////////////////////////
919: // CMainWindow::FileDlg
920: // Call the commdlg routine to display File Open or File Save As
921: // dialogs. The setup is the same for either. If bOpen is TRUE
922: // then File Open is display otherwise File Save As is displayed.
923: // The File Name and File Title are stored at the string pointer
924: // passed in.
925: //
926: BOOL CMainWindow::FileDlg( BOOL bOpen, int nMaxFile, LPSTR szFile,
927: int nMaxFileTitle, LPSTR szFileTitle )
928: {
929:
930: OPENFILENAME of;
931:
932: char szDirName[SIZESTRING];
933: char szFilter[] = "Phone Book Files (*.pb)\0"
934: "*.pb\0"
935: "\0";
936:
937: szDirName[0] = '.';
938:
939: of.lStructSize = sizeof( OPENFILENAME );
940: of.hwndOwner = m_hWnd;
941: of.lpstrFilter = szFilter;
942: of.lpstrCustomFilter = NULL;
943: of.nMaxCustFilter = 0L;
944: of.nFilterIndex = 1L;
945: of.lpstrFile=szFile;
946: of.nMaxFile=nMaxFile;
947: of.lpstrFileTitle = szFileTitle;
948: of.nMaxFileTitle = nMaxFileTitle;
949: of.lpstrInitialDir = szDirName;
950: of.lpstrTitle = NULL;
951: of.nFileOffset = 0;
952: of.nFileExtension = 0;
953: of.lpstrDefExt = "pb";
954: if ( bOpen )
955: {
956: of.Flags = OFN_HIDEREADONLY;
957: return GetOpenFileName( &of );
958: }
959: else
960: {
961: of.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
962: return GetSaveFileName( &of );
963: }
964: }
965:
966: //////////////////////////////////////////////////
967: // CMainWindow::Save
968: // Handles any time a file needs to be saved to the disk.
969: // Passing in FALSE for name brings up the file save as dialog
970: // whether or not the database had a name before.
971: //
972: BOOL CMainWindow::Save( BOOL bIsNamed /* = FALSE */ )
973: {
974: CString szFileName, szFileTitle;
975: TRY
976: {
977: if ( bIsNamed )
978: m_people.DoSave();
979: else
980: {
981: szFileName = m_people.GetName();
982: if ( FileDlg( FALSE, SIZESTRING,
983: szFileName.GetBuffer( SIZESTRING ), SIZESTRING,
984: szFileTitle.GetBuffer( SIZESTRING ) ) )
985: {
986: szFileName.ReleaseBuffer();
987: m_people.DoSave( szFileName );
988: m_people.SetTitle( szFileTitle );
989: SetWindowText( m_people.GetTitle() );
990: }
991: else
992: return FALSE;
993: }
994: }
995: CATCH( CFileException, e )
996: {
997: char ErrorMsg[SIZESTRING];
998: sprintf( ErrorMsg,"Saving %s returned a 0x%lx.",
999: (const char*)szFileTitle, e->m_lOsError );
1000: MessageBox( ErrorMsg, "File Open Error" );
1001: }
1002: AND_CATCH( CArchiveException, e )
1003: {
1004: char ErrorMsg[SIZESTRING];
1005: sprintf( ErrorMsg,"Reading the %s archive failed.",
1006: (const char*)szFileTitle );
1007: MessageBox( ErrorMsg, "File Open Error" );
1008: }
1009: END_CATCH
1010: return TRUE;
1011: }
1012:
1013: //////////////////////////////////////////////////
1014: // CMainWindow::CheckForSave
1015: // Whenever a new file is opened this routine will determine if
1016: // there are unsaved changes in the current database. If so it
1017: // will query the user and determine save or not as appropriate.
1018: //
1019: BOOL CMainWindow::CheckForSave( const char* pszTitle, const char* pszMessage )
1020: {
1021: if ( m_people.IsDirty() )
1022: {
1023: UINT nButton = MessageBox( pszMessage, pszTitle, MB_YESNOCANCEL );
1024: if ( nButton == IDYES )
1025: {
1026: if ( !Save( m_people.IsNamed() ) )
1027: return FALSE;
1028: }
1029: else if ( nButton == IDCANCEL )
1030: return FALSE;
1031: }
1032: return TRUE;
1033: }
1034:
1035:
1036: //////////////////////////////////////////////////
1037: // CMainWindow::SetMenu
1038: // Whenever the existance of the DataBase is changed this
1039: // routine will reset the menus so only the possible commands
1040: // are accessible.
1041: //
1042: void CMainWindow::SetMenu()
1043: {
1044: CMenu* pMenu = GetMenu();
1045: if ( m_people.IsPresent() )
1046: {
1047: if ( m_people.IsNamed() )
1048: pMenu->EnableMenuItem( IDM_SAVE, MF_ENABLED );
1049: else
1050: pMenu->EnableMenuItem( IDM_SAVE, MF_GRAYED );
1051: pMenu->EnableMenuItem( IDM_SAVEAS, MF_ENABLED );
1052: pMenu->EnableMenuItem( IDM_CLOSE, MF_ENABLED );
1053: pMenu->EnableMenuItem( IDM_PRINT, MF_ENABLED );
1054: pMenu->EnableMenuItem( IDM_ADD, MF_ENABLED );
1055: pMenu->EnableMenuItem( IDM_DELETE, MF_ENABLED );
1056: pMenu->EnableMenuItem( IDM_FIND, MF_ENABLED );
1057: pMenu->EnableMenuItem( IDM_EDIT, MF_ENABLED );
1058: }
1059: else
1060: {
1061: pMenu->EnableMenuItem( IDM_SAVE, MF_GRAYED );
1062: pMenu->EnableMenuItem( IDM_SAVEAS, MF_GRAYED );
1063: pMenu->EnableMenuItem( IDM_CLOSE, MF_GRAYED );
1064: pMenu->EnableMenuItem( IDM_PRINT, MF_GRAYED );
1065: pMenu->EnableMenuItem( IDM_ADD, MF_GRAYED );
1066: pMenu->EnableMenuItem( IDM_DELETE, MF_GRAYED );
1067: pMenu->EnableMenuItem( IDM_FIND, MF_GRAYED );
1068: pMenu->EnableMenuItem( IDM_FINDALL, MF_GRAYED );
1069: pMenu->EnableMenuItem( IDM_EDIT, MF_GRAYED );
1070: }
1071: }
1072:
1073: //////////////////////////////////////////////////
1074: // CMainWindow::InvalidateLine
1075: // Marks the screen area of the currently selected person as
1076: // invalid causing windows to call OnPaint to redraw the area.
1077: // This is normally used when the selected line is being changed.
1078: //
1079: void CMainWindow::InvalidateLine()
1080: {
1081: CRect area( 0, ( m_nSelectLine - m_nVscrollPos ) * m_cyChar, m_cxClient,
1082: ( m_nSelectLine + 1 - m_nVscrollPos ) * m_cyChar );
1083: InvalidateRect( area );
1084: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.