The sample program shown in Figure 12-8
Figure 12-8. The MyWord window.
MyWord uses two
The status bar at the bottom of MyWord's frame window displays help text for menu items and toolbar controls. It also includes Caps Lock and Num Lock indicators and a line number display that's continually updated as the caret moves through the document. The Caps Lock and Num Lock indicators were added using MFC's predefined ID_INDICATOR_CAPS and ID_INDICATOR_NUM IDs. The line number indicator is updated by an ON_UPDATE_COMMAND_UI handler that, when called, retrieves the current line number from the
CRichEditView
, formulates a text string containing the line number, and updates the status bar display with
CCmdUI::SetText
. The line number pane is
I used AppWizard to begin MyWord. I checked the Docking Toolbar and Initial Status Bar options in AppWizard's Step 4 dialog box, and in Step 6, I selected
CRichEditView
as the base class for the view. I next derived a class named
CStyleBar
to represent the style bar, added a
CStyleBar
data member to the frame window class, and modified the frame window's
OnCreate
function to create the style bar. (I used ClassWizard to perform the class derivation, but because ClassWizard doesn't support
CToolBar
as a base class, I derived
CStyleBar
from
CCmdTarget
and then manually patched up the code to change the base class to
CToolBar
.) I used Visual C++'s Insert-Resource command to create the toolbar resource from which the style bar is created, and I added buttons in the toolbar editor. Finishing MyWord was a matter of writing the command handlers, update handlers, and ordinary class member functions that form the
The source code for MyWord's frame window, document, view, and style bar classes is listed in Figure 12-9. Take a moment to look it over to see how the toolbars and status bar are handled. Then go to "The Main Toolbar" to read about pertinent
Figure 12-9. The MyWord application.
MainFrm.h
// MainFrm.h : interface of the CMainFrame class
//
///////////////////////////////////////////////////////////////////////////
#if !defined(
AFX_MAINFRM_H__C85C9089_A154_11D2_8E53_006008A82731__INCLUDED_)
#define AFX_MAINFRM_H__C85C9089_A154_11D2_8E53_006008A82731__INCLUDED_
#include "StyleBar.h" // Added by ClassView
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CMainFrame : public CFrameWnd
{
protected: // create from serialization only
CMainFrame();
DECLARE_DYNCREATE(CMainFrame)
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMainFrame)
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CMainFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected: // control bar embedded members
CStyleBar m_wndStyleBar;
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
// Generated message map functions
protected:
BOOL CreateToolBar ();
BOOL CreateStyleBar ();
BOOL CreateStatusBar ();
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnClose();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
///////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations
// immediately before the previous line.
#endif
// !defined(AFX_MAINFRM_H__C85C9089_A154_11D2_8E53_006008A82731__INCLUDED_)
|
MainFrm.cpp
// MainFrm.cpp : implementation of the CMainFrame class
//
#include "stdafx.h"
#include "MyWord.h"
#include "MainFrm.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////////////////////////////////////////////
// CMainFrame
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_CLOSE()
//}}AFX_MSG_MAP
ON_COMMAND_EX (IDW_STYLE_BAR, OnBarCheck)
ON_UPDATE_COMMAND_UI (IDW_STYLE_BAR, OnUpdateControlBarMenu)
END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction
CMainFrame::CMainFrame()
{
}
CMainFrame::~CMainFrame()
{
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
//
// Tell the frame window to permit docking.
//
EnableDocking (CBRS_ALIGN_ANY);
//
// Create the toolbar, style bar, and status bar.
//
if (!CreateToolBar ()
!CreateStyleBar ()
!CreateStatusBar ())
return -1;
//
// Load the saved bar state (if any).
//
LoadBarState (_T ("MainBarState"));
return 0;
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const
{
CFrameWnd::Dump(dc);
}
#endif //_DEBUG
///////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers
void CMainFrame::OnClose()
{
SaveBarState (_T ("MainBarState"));
CFrameWnd::OnClose();
}
BOOL CMainFrame::CreateToolBar()
{
if (!m_wndToolBar.Create (this)
!m_wndToolBar.LoadToolBar (IDR_MAINFRAME))
return FALSE;
m_wndToolBar.SetBarStyle (m_wndToolBar.GetBarStyle ()
CBRS_TOOLTIPS CBRS_FLYBY CBRS_SIZE_DYNAMIC);
m_wndToolBar.SetWindowText (_T ("Main"));
m_wndToolBar.EnableDocking (CBRS_ALIGN_ANY);
DockControlBar (&m_wndToolBar);
return TRUE;
}
BOOL CMainFrame::CreateStyleBar()
{
if (!m_wndStyleBar.Create (this, WS_CHILD WS_VISIBLE CBRS_TOP
CBRS_TOOLTIPS CBRS_FLYBY CBRS_SIZE_DYNAMIC, IDW_STYLE_BAR))
return FALSE;
m_wndStyleBar.SetWindowText (_T ("Styles"));
m_wndStyleBar.EnableDocking (CBRS_ALIGN_TOP CBRS_ALIGN_BOTTOM);
DockControlBar (&m_wndStyleBar);
return TRUE;
}
BOOL CMainFrame::CreateStatusBar()
{
static UINT nIndicators[] = {
ID_SEPARATOR,
ID_INDICATOR_LINE,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM
};
if (!m_wndStatusBar.Create (this))
return FALSE;
m_wndStatusBar.SetIndicators (nIndicators, 4);
return TRUE;
}
|
MyWordDoc.h
// MyWordDoc.h : interface of the CMyWordDoc class
//
///////////////////////////////////////////////////////////////////////////
#if !defined(
AFX_MYWORDDOC_H__C85C908B_A154_11D2_8E53_006008A82731__INCLUDED_)
#define AFX_MYWORDDOC_H__C85C908B_A154_11D2_8E53_006008A82731__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CMyWordDoc : public CRichEditDoc
{
protected: // create from serialization only
CMyWordDoc();
DECLARE_DYNCREATE(CMyWordDoc)
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMyWordDoc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
//}}AFX_VIRTUAL
virtual CRichEditCntrItem* CreateClientItem(REOBJECT* preo) const;
// Implementation
public:
virtual ~CMyWordDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CMyWordDoc)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
///////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations
// immediately before the previous line.
#endif
// !defined(
// AFX_MYWORDDOC_H__C85C908B_A154_11D2_8E53_006008A82731__INCLUDED_)
|
MyWordDoc.cpp
// MyWordDoc.cpp : implementation of the CMyWordDoc class
//
#include "stdafx.h"
#include "MyWord.h"
#include "MyWordDoc.h"
#include "CntrItem.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////////////////////////////////////////////
// CMyWordDoc
IMPLEMENT_DYNCREATE(CMyWordDoc, CRichEditDoc)
BEGIN_MESSAGE_MAP(CMyWordDoc, CRichEditDoc)
//{{AFX_MSG_MAP(CMyWordDoc)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// Enable default OLE container implementation
ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_LINKS,
CRichEditDoc::OnUpdateEditLinksMenu)
ON_COMMAND(ID_OLE_EDIT_LINKS, CRichEditDoc::OnEditLinks)
ON_UPDATE_COMMAND_UI_RANGE(ID_OLE_VERB_FIRST,
ID_OLE_VERB_LAST, CRichEditDoc::OnUpdateObjectVerbMenu)
END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////
// CMyWordDoc construction/destruction
CMyWordDoc::CMyWordDoc()
{
}
CMyWordDoc::~CMyWordDoc()
{
}
BOOL CMyWordDoc::OnNewDocument()
{
if (!CRichEditDoc::OnNewDocument())
return FALSE;
return TRUE;
}
CRichEditCntrItem* CMyWordDoc::CreateClientItem(REOBJECT* preo) const
{
return new CMyWordCntrItem(preo, (CMyWordDoc*) this);
}
///////////////////////////////////////////////////////////////////////////
// CMyWordDoc serialization
void CMyWordDoc::Serialize(CArchive& ar)
{
CRichEditDoc::Serialize(ar);
}
///////////////////////////////////////////////////////////////////////////
// CMyWordDoc diagnostics
#ifdef _DEBUG
void CMyWordDoc::AssertValid() const
{
CRichEditDoc::AssertValid();
}
void CMyWordDoc::Dump(CDumpContext& dc) const
{
CRichEditDoc::Dump(dc);
}
#endif //_DEBUG
///////////////////////////////////////////////////////////////////////////
// CMyWordDoc commands
|
MyWordView.h
// MyWordView.h : interface of the CMyWordView class
//
///////////////////////////////////////////////////////////////////////////
#if !defined(
AFX_MYWORDVIEW_H__C85C908D_A154_11D2_8E53_006008A82731__INCLUDED_)
#define AFX_MYWORDVIEW_H__C85C908D_A154_11D2_8E53_006008A82731__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CMyWordCntrItem;
class CMyWordView : public CRichEditView
{
protected: // create from serialization only
CMyWordView();
DECLARE_DYNCREATE(CMyWordView)
// Attributes
public:
CMyWordDoc* GetDocument();
// Operations
public:
void GetFontInfo (LPTSTR pszFaceName, int& nSize);
void ChangeFont (LPCTSTR pszFaceName);
void ChangeFontSize (int nSize);
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMyWordView)
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual void OnInitialUpdate(); // called first time after construct
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CMyWordView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CMyWordView)
afx_msg void OnDestroy();
afx_msg void OnCharBold();
afx_msg void OnCharItalic();
afx_msg void OnCharUnderline();
afx_msg void OnParaLeft();
afx_msg void OnParaCenter();
afx_msg void OnParaRight();
afx_msg void OnUpdateCharBold(CCmdUI* pCmdUI);
afx_msg void OnUpdateCharItalic(CCmdUI* pCmdUI);
afx_msg void OnUpdateCharUnderline(CCmdUI* pCmdUI);
afx_msg void OnUpdateParaLeft(CCmdUI* pCmdUI);
afx_msg void OnUpdateParaCenter(CCmdUI* pCmdUI);
afx_msg void OnUpdateParaRight(CCmdUI* pCmdUI);
//}}AFX_MSG
afx_msg void OnUpdateLineNumber (CCmdUI* pCmdUI);
DECLARE_MESSAGE_MAP()
};
#ifndef _DEBUG // debug version in MyWordView.cpp
inline CMyWordDoc* CMyWordView::GetDocument()
{ return (CMyWordDoc*)m_pDocument; }
#endif
///////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations
// immediately before the previous line.
#endif
// !defined(
// AFX_MYWORDVIEW_H__C85C908D_A154_11D2_8E53_006008A82731__INCLUDED_)
|
MyWordView.cpp
// MyWordView.cpp : implementation of the CMyWordView class
//
#include "stdafx.h"
#include "MyWord.h"
#include "MyWordDoc.h"
#include "CntrItem.h"
#include "MyWordView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////////////////////////////////////////////
// CMyWordView
IMPLEMENT_DYNCREATE(CMyWordView, CRichEditView)
BEGIN_MESSAGE_MAP(CMyWordView, CRichEditView)
//{{AFX_MSG_MAP(CMyWordView)
ON_WM_DESTROY()
ON_COMMAND(ID_CHAR_BOLD, OnCharBold)
ON_COMMAND(ID_CHAR_ITALIC, OnCharItalic)
ON_COMMAND(ID_CHAR_UNDERLINE, OnCharUnderline)
ON_COMMAND(ID_PARA_LEFT, OnParaLeft)
ON_COMMAND(ID_PARA_CENTER, OnParaCenter)
ON_COMMAND(ID_PARA_RIGHT, OnParaRight)
ON_UPDATE_COMMAND_UI(ID_CHAR_BOLD, OnUpdateCharBold)
ON_UPDATE_COMMAND_UI(ID_CHAR_ITALIC, OnUpdateCharItalic)
ON_UPDATE_COMMAND_UI(ID_CHAR_UNDERLINE, OnUpdateCharUnderline)
ON_UPDATE_COMMAND_UI(ID_PARA_LEFT, OnUpdateParaLeft)
ON_UPDATE_COMMAND_UI(ID_PARA_CENTER, OnUpdateParaCenter)
ON_UPDATE_COMMAND_UI(ID_PARA_RIGHT, OnUpdateParaRight)
//}}AFX_MSG_MAP
ON_UPDATE_COMMAND_UI(ID_INDICATOR_LINE, OnUpdateLineNumber)
END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////
// CMyWordView construction/destruction
CMyWordView::CMyWordView()
{
}
CMyWordView::~CMyWordView()
{
}
BOOL CMyWordView::PreCreateWindow(CREATESTRUCT& cs)
{
return CRichEditView::PreCreateWindow(cs);
}
void CMyWordView::OnInitialUpdate()
{
CRichEditView::OnInitialUpdate();
CHARFORMAT cf;
cf.cbSize = sizeof (CHARFORMAT);
cf.dwMask = CFM_BOLD CFM_ITALIC CFM_UNDERLINE
CFM_PROTECTED CFM_STRIKEOUT CFM_FACE CFM_SIZE;
cf.dwEffects = 0;
cf.yHeight = 240; // 240 twips == 12 points
::lstrcpy (cf.szFaceName, _T ("Times New Roman"));
SetCharFormat (cf);
}
void CMyWordView::OnDestroy()
{
// Deactivate the item on destruction; this is important
// when a splitter view is being used.
CRichEditView::OnDestroy();
COleClientItem* pActiveItem = GetDocument()->GetInPlaceActiveItem(this);
if (pActiveItem != NULL && pActiveItem->GetActiveView() == this)
{
pActiveItem->Deactivate();
ASSERT(GetDocument()->GetInPlaceActiveItem(this) == NULL);
}
}
///////////////////////////////////////////////////////////////////////////
// CMyWordView diagnostics
#ifdef _DEBUG
void CMyWordView::AssertValid() const
{
CRichEditView::AssertValid();
}
void CMyWordView::Dump(CDumpContext& dc) const
{
CRichEditView::Dump(dc);
}
CMyWordDoc* CMyWordView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMyWordDoc)));
return (CMyWordDoc*)m_pDocument;
}
#endif //_DEBUG
///////////////////////////////////////////////////////////////////////////
// CMyWordView message handlers
void CMyWordView::OnCharBold()
{
CHARFORMAT cf;
cf = GetCharFormatSelection ();
if (!(cf.dwMask & CFM_BOLD) !(cf.dwEffects & CFE_BOLD))
cf.dwEffects = CFE_BOLD;
else
cf.dwEffects = 0;
cf.dwMask = CFM_BOLD;
SetCharFormat (cf);
}
void CMyWordView::OnCharItalic()
{
CHARFORMAT cf;
cf = GetCharFormatSelection ();
if (!(cf.dwMask & CFM_ITALIC) !(cf.dwEffects & CFE_ITALIC))
cf.dwEffects = CFE_ITALIC;
else
cf.dwEffects = 0;
cf.dwMask = CFM_ITALIC;
SetCharFormat (cf);
}
void CMyWordView::OnCharUnderline()
{
CHARFORMAT cf;
cf = GetCharFormatSelection ();
if (!(cf.dwMask & CFM_UNDERLINE) !(cf.dwEffects & CFE_UNDERLINE))
cf.dwEffects = CFE_UNDERLINE;
else
cf.dwEffects = 0;
cf.dwMask = CFM_UNDERLINE;
SetCharFormat (cf);
}
void CMyWordView::OnParaLeft()
{
OnParaAlign (PFA_LEFT);
}
void CMyWordView::OnParaCenter()
{
OnParaAlign (PFA_CENTER);
}
void CMyWordView::OnParaRight()
{
OnParaAlign (PFA_RIGHT);
}
void CMyWordView::OnUpdateCharBold(CCmdUI* pCmdUI)
{
OnUpdateCharEffect (pCmdUI, CFM_BOLD, CFE_BOLD);
}
void CMyWordView::OnUpdateCharItalic(CCmdUI* pCmdUI)
{
OnUpdateCharEffect (pCmdUI, CFM_ITALIC, CFE_ITALIC);
}
void CMyWordView::OnUpdateCharUnderline(CCmdUI* pCmdUI)
{
OnUpdateCharEffect (pCmdUI, CFM_UNDERLINE, CFE_UNDERLINE);
}
void CMyWordView::OnUpdateParaLeft(CCmdUI* pCmdUI)
{
OnUpdateParaAlign (pCmdUI, PFA_LEFT);
}
void CMyWordView::OnUpdateParaCenter(CCmdUI* pCmdUI)
{
OnUpdateParaAlign (pCmdUI, PFA_CENTER);
}
void CMyWordView::OnUpdateParaRight(CCmdUI* pCmdUI)
{
OnUpdateParaAlign (pCmdUI, PFA_RIGHT);
}
void CMyWordView::OnUpdateLineNumber(CCmdUI* pCmdUI)
{
int nLine = GetRichEditCtrl ().LineFromChar (-1) + 1;
CString string;
string.Format (_T ("Line %d"), nLine);
pCmdUI->Enable (TRUE);
pCmdUI->SetText (string);
}
void CMyWordView::ChangeFont(LPCTSTR pszFaceName)
{
CHARFORMAT cf;
cf.cbSize = sizeof (CHARFORMAT);
cf.dwMask = CFM_FACE;
::lstrcpy (cf.szFaceName, pszFaceName);
SetCharFormat (cf);
}
void CMyWordView::ChangeFontSize(int nSize)
{
CHARFORMAT cf;
cf.cbSize = sizeof (CHARFORMAT);
cf.dwMask = CFM_SIZE;
cf.yHeight = nSize;
SetCharFormat (cf);
}
void CMyWordView::GetFontInfo(LPTSTR pszFaceName, int& nSize)
{
CHARFORMAT cf = GetCharFormatSelection ();
::lstrcpy (pszFaceName,
cf.dwMask & CFM_FACE ? cf.szFaceName : _T (""));
nSize = cf.dwMask & CFM_SIZE ? cf.yHeight : -1;
}
|
StyleBar.h
#if !defined(
AFX_STYLEBAR_H__C85C9099_A154_11D2_8E53_006008A82731__INCLUDED_)
#define AFX_STYLEBAR_H__C85C9099_A154_11D2_8E53_006008A82731__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// StyleBar.h : header file
//
///////////////////////////////////////////////////////////////////////////
// CStyleBar command target
class CStyleBar : public CToolBar
{
// Attributes
public:
// Operations
public:
static int CALLBACK EnumFontNameProc (ENUMLOGFONT* lpelf,
NEWTEXTMETRIC* lpntm, int nFontType, LPARAM lParam);
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CStyleBar)
//}}AFX_VIRTUAL
virtual void OnUpdateCmdUI (CFrameWnd* pTarget,
BOOL bDisableIfNoHndler);
// Implementation
protected:
void InitTypefaceList (CDC* pDC);
CFont m_font;
CComboBox m_wndFontNames;
CComboBox m_wndFontSizes;
// Generated message map functions
//{{AFX_MSG(CStyleBar)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG
afx_msg void OnSelectFont ();
afx_msg void OnSelectSize ();
afx_msg void OnCloseUp ();
DECLARE_MESSAGE_MAP()
};
///////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations
// immediately before the previous line.
#endif
// !defined(
// AFX_STYLEBAR_H__C85C9099_A154_11D2_8E53_006008A82731__INCLUDED_)
|
StyleBar.cpp
// StyleBar.cpp : implementation file
//
#include "stdafx.h"
#include "MyWord.h"
#include "MyWordDoc.h"
#include "MyWordView.h"
#include "StyleBar.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////////////////////////////////////////////
// CStyleBar
BEGIN_MESSAGE_MAP(CStyleBar, CToolBar)
//{{AFX_MSG_MAP(CStyleBar)
ON_WM_CREATE()
//}}AFX_MSG_MAP
ON_CBN_SELENDOK (IDC_FONTNAMES, OnSelectFont)
ON_CBN_SELENDOK (IDC_FONTSIZES, OnSelectSize)
ON_CBN_CLOSEUP (IDC_FONTNAMES, OnCloseUp)
ON_CBN_CLOSEUP (IDC_FONTSIZES, OnCloseUp)
END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////
// CStyleBar message handlers
int CStyleBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
static int nFontSizes[] = {
8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 32, 36, 48, 72
};
if (CToolBar::OnCreate(lpCreateStruct) == -1)
return -1;
//
// Load the toolbar.
//
if (!LoadToolBar (IDR_STYLE_BAR))
return -1;
//
// Create an 8-point MS Sans Serif font for the combo boxes.
//
CClientDC dc (this);
m_font.CreatePointFont (80, _T ("MS Sans Serif"));
CFont* pOldFont = dc.SelectObject (&m_font);
TEXTMETRIC tm;
dc.GetTextMetrics (&tm);
int cxChar = tm.tmAveCharWidth;
int cyChar = tm.tmHeight + tm.tmExternalLeading;
dc.SelectObject (pOldFont);
//
// Add the font name combo box to the toolbar.
//
SetButtonInfo (8, IDC_FONTNAMES, TBBS_SEPARATOR, cxChar * 32);
CRect rect;
GetItemRect (8, &rect);
rect.bottom = rect.top + (cyChar * 16);
if (!m_wndFontNames.Create (WS_CHILD WS_VISIBLE WS_VSCROLL
CBS_DROPDOWNLIST CBS_SORT, rect, this, IDC_FONTNAMES))
return -1;
m_wndFontNames.SetFont (&m_font);
InitTypefaceList (&dc);
//
// Add the font size combo box to the toolbar.
//
SetButtonInfo (10, IDC_FONTSIZES, TBBS_SEPARATOR, cxChar * 12);
GetItemRect (10, &rect);
rect.bottom = rect.top + (cyChar * 14);
if (!m_wndFontSizes.Create (WS_CHILD WS_VISIBLE WS_VSCROLL
CBS_DROPDOWNLIST, rect, this, IDC_FONTSIZES))
return -1;
m_wndFontSizes.SetFont (&m_font);
CString string;
int nCount = sizeof (nFontSizes) / sizeof (int);
for (int i=0; i<nCount; i++) {
string.Format (_T ("%d"), nFontSizes[i]);
m_wndFontSizes.AddString (string);
}
return 0;
}
void CStyleBar::OnSelectFont ()
{
TCHAR szFaceName[LF_FACESIZE];
int nIndex = m_wndFontNames.GetCurSel ();
m_wndFontNames.GetLBText (nIndex, szFaceName);
CMyWordView* pView =
(CMyWordView*) ((CFrameWnd*) AfxGetMainWnd ())->GetActiveView ();
pView->ChangeFont (szFaceName);
}
void CStyleBar::OnSelectSize ()
{
TCHAR szSize[8];
int nIndex = m_wndFontSizes.GetCurSel ();
m_wndFontSizes.GetLBText (nIndex, szSize);
int nSize = atoi (szSize) * 20; // Need twips
CMyWordView* pView =
(CMyWordView*) ((CFrameWnd*) AfxGetMainWnd ())->GetActiveView ();
pView->ChangeFontSize (nSize);
}
void CStyleBar::OnCloseUp ()
{
((CFrameWnd*) AfxGetMainWnd ())->GetActiveView ()->SetFocus ();
}
void CStyleBar::InitTypefaceList (CDC* pDC)
{
::EnumFontFamilies (pDC->m_hDC, NULL,
(FONTENUMPROC) EnumFontNameProc, (LPARAM) this);
}
int CALLBACK CStyleBar::EnumFontNameProc (ENUMLOGFONT* lpelf,
NEWTEXTMETRIC* lpntm, int nFontType, LPARAM lParam)
{
CStyleBar* pWnd = (CStyleBar*) lParam;
if (nFontType & TRUETYPE_FONTTYPE)
pWnd->m_wndFontNames.AddString (lpelf->elfLogFont.lfFaceName);
return 1;
}
void CStyleBar::OnUpdateCmdUI (CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
{
CToolBar::OnUpdateCmdUI (pTarget, bDisableIfNoHndler);
CWnd* pWnd = GetFocus ();
if ((pWnd == &m_wndFontNames) (pWnd == &m_wndFontSizes))
return;
//
// Get the font name and size.
//
int nTwips;
TCHAR szFaceName[LF_FACESIZE];
CMyWordView* pView =
(CMyWordView*) ((CFrameWnd*) AfxGetMainWnd ())->GetActiveView ();
pView->GetFontInfo (szFaceName, nTwips);
//
// Update the font name combo box.
//
TCHAR szSelection[LF_FACESIZE];
m_wndFontNames.GetWindowText (szSelection,
sizeof (szSelection) / sizeof (TCHAR));
if (::lstrcmp (szFaceName, szSelection) != 0) {
if (szFaceName[0] == 0)
m_wndFontNames.SetCurSel (-1);
else {
if (m_wndFontNames.SelectString (-1, szFaceName) == CB_ERR)
m_wndFontNames.SetCurSel (-1);
}
}
//
// Update the font size combo box.
//
TCHAR szSize[4];
m_wndFontSizes.GetWindowText (szSize,
sizeof (szSize) / sizeof (TCHAR));
int nSizeFromComboBox = atoi (szSize);
int nSizeFromView = nTwips / 20;
if (nSizeFromComboBox != nSizeFromView) {
if (nTwips == -1)
m_wndFontSizes.SetCurSel (-1);
else {
CString string;
string.Format (_T ("%d"), nSizeFromView);
if (m_wndFontSizes.SelectString (-1, string) == CB_ERR)
m_wndFontSizes.SetCurSel (-1);
}
}
}
|
MyWord's main toolbar is a standard
CToolBar
that's created along with the style bar and status bar in
CMainFrame::OnCreate
. After the main toolbar is created, the styles CBRS_TOOLTIPS, CBRS_FLYBY, and CBRS_SIZE_DYNAMIC are added and
CToolBar::EnableDocking
is called with a CBRS_ALIGN_ANY parameter so that the toolbar can be docked to any side of the frame window.
DockControlBar
is called to dock the toolbar in its default location at the top of the window so that it can be detached and floated. The call to
LoadBarState
in
CMainFrame::OnCreate
Handlers for all the buttons on the main toolbar—and for all the items in MyWord's
ON_UPDATE_COMMAND_UI (ID_EDIT_CUT, OnUpdateNeedSel) ON_UPDATE_COMMAND_UI (ID_EDIT_COPY, OnUpdateNeedSel) |
Scan the CRichEditView message map in the MFC source code file Viewrich.cpp to see the full range of commands for which CRichEditView provides default command and update handlers.
MyWord's style bar is an instance of the
CToolBar
-derived class
CStyleBar
. The style bar is constructed when the frame window is
MyWord's view class provides command and update handlers for the buttons in the style bar. Clicking the Bold button, for example, activates
CMyWordView::OnCharBold
, which is implemented as
void CMyWordView::OnCharBold ()
{
CHARFORMAT cf;
cf = GetCharFormatSelection ();
if (!(cf.dwMask & CFM_BOLD) ¦¦ !(cf.dwEffects & CFE_BOLD))
cf.dwEffects = CFE_BOLD;
else
cf.dwEffects = 0;
cf.dwMask = CFM_BOLD;
SetCharFormat (cf);
}
|
GetCharFormatSelection is a CRichEditView function that returns a CHARFORMAT structure containing information about the text that is currently selected in the view or, if there is no selection, about the default character format. SetCharFormat is another CRichEditView function that applies the text attributes described in a CHARFORMAT structure to the selected text. If no text is currently selected, SetCharFormat sets the view's default character format.
Boldface text is toggled on or off by setting the CFM_BOLD bit in the dwMask field of the CHARFORMAT structure passed to SetCharFormat and either setting or clearing the CFE_BOLD bit in the structure's dwEffects field. To determine the proper setting for the CFE_BOLD flag, OnCharBold inspects both the CFM_BOLD and CFE_BOLD flags in the CHARFORMAT structure returned by GetCharFormatSelection . The CFM_BOLD flag is clear if the current selection includes a mix of bold and nonbold text. If CFM_BOLD is set, either the selection consists entirely of bold or nonbold text or no text is currently selected. In either case, the CFE_BOLD flag indicates whether the selected (or default) text is bold or nonbold. OnCharBold can be called in five possible scenarios. The following table describes each set of circumstances and documents the result. The view's OnCharItalic and OnCharUnderline handlers use similar logic to toggle italic and underline on and off.
| Circumstances Under Which OnCharBold Is Called | dwMask & CFM_BOLD | dwEffects &CFE_BOLD | Action Taken by OnCharBold |
|---|---|---|---|
| One or more characters are selected; the selection contains a mix of bold and nonbold text. | Undefined | Makes all characters in the selection bold. | |
| One or more characters are selected; the selection consists entirely of bold text. | Nonzero | Nonzero | Makes all characters in the selection nonbold. |
| One or more characters are selected; the selection consists entirely of nonbold text. | Nonzero | Makes all characters in the selection bold. | |
| No text is selected; the default character format is bold. | Nonzero | Nonzero | Sets the default character format to nonbold. |
| No text is selected; the default character format is nonbold. | Nonzero | Sets the default character format to bold. |
The handlers for the paragraph alignment buttons are simpler because their actions don't depend on the current paragraph alignment.
CRichEditView
provides a
OnParaAlign (PFA_LEFT); |
in OnParaLeft selects left-aligned text. If no text is selected in the view, OnParaAlign reformats the paragraph that contains the caret. If text is selected, all paragraphs touched by the selection are transformed so that the text in them is left aligned.
Each button in the style bar is mapped to an update handler that calls either CRichEditView::OnUpdateCharEffect or CRichEditView::OnUpdateParaAlign . In addition to checking and unchecking the buttons as appropriate, these CRichEditView functions also set a button to the indeterminate state when a selection includes a mix of character formats or paragraph alignments. For a simple demonstration, try this test. First enter some text if you haven't already. Then highlight some characters, click the Italic button to italicize the selection, and select a range of characters that includes both italicized and nonitalicized text. Because OnUpdateCharItalic calls OnUpdateCharEffect , the Italic button will become half-grayed, indicating that the selection contains a mix of character formats. And because each style bar button is assigned an update handler, the buttons behave like check push buttons and radio push buttons even though none is assigned the TBBS_CHECKBOX or TBBS_CHECKGROUP style.
When a font name or a font size is selected from the combo boxes, the style bar retrieves the font name or font size and calls a public member function of the view class to implement the change. Selecting a font name activates
CStyleBar::OnSelectFont
, which
void CMyWordView::ChangeFont (LPCTSTR pszFaceName)
{
CHARFORMAT cf;
cf.cbSize = sizeof (CHARFORMAT);
cf.dwMask = CFM_FACE;
::lstrcpy (cf.szFaceName, pszFaceName);
SetCharFormat (cf);
}
|
CStyleBar::OnSelectSize
uses a similar procedure to change the font size through the view's
ChangeFontSize
member function. Font sizes passed to
CRichEditView
s are
Which
So that the items selected in the combo boxes will match the character format in the view as the caret is moved through the document and selections are made,
CStyleBar
overrides the
OnUpdateCmdUI
function it inherits from
CToolBar
and updates the combo boxes based on information obtained from the view. After verifying that neither of the combo boxes has the input focus so that the combo boxes won't flicker if
OnUpdateCmdUI
is called while a drop-down list box is displayed,
OnUpdateCmdUI
calls
CMyWordView::GetFontInfo
to get the current font name and size. If the font name obtained from the view doesn't match the font name selected in the font name combo box,
OnUpdateCmdUI
changes the combo box selection. Similarly, the selection is updated in the font size combo box if the size shown in the combo box doesn't match the size
One thing
CStyleBar
doesn't do is update the list of typefaces in the font name combo box if the pool of installed fonts changes while MyWord is running. When fonts are added or deleted, Windows sends all top-level windows a WM_FONTCHANGE message
To simplify the logic for updating the selection in the font size combo box, MyWord's style bar lists TrueType fonts only. If the font name combo box included raster fonts as well, the font size combo box would need to be reinitialized each time the selection changed in the font name combo box because raster fonts come in a limited number of sizes. Limiting the user's choice of fonts to TrueType only makes the point sizes listed in the font size combo box independent of the typeface selected in the font name combo box because TrueType fonts can be accurately rendered in any point size from 1 through 999.
Most of MyWord's functionality comes from CRichEditView , which is built around the powerful rich text edit control provided in the common controls library. MFC's CRichEditView class doesn't act alone in encapsulating the features of a rich text edit control; help comes from CRichEditDoc and CRichEditCntrItem . CRichEditDoc represents the data stored in a CRichEditView , which can include linked and embedded OLE objects, and CRichEditCntrItem represents OLE objects contained in a CRichEditView .
When you derive a view class from CRichEditView , you must also derive a document class from CRichEditDoc and override CRichEditDoc::CreateClientItem , which is pure virtual. MyWord's CMyWordDoc document class implements CreateClientItem by creating a CRichEditCntrItem object and returning a pointer:
CRichEditCntrItem* CMyWordDoc::CreateClientItem (REOBJECT* preo) const
{
return new CMyWordCntrItem (preo, (CMyWordDoc*) this);
}
|
This simple override enables the Paste and Paste Special commands in the Edit menu to paste OLE items into the document. For a demonstration, copy a picture created with the Windows Paint applet to the clipboard and paste it into a MyWord document. Then double-click the embedded image in MyWord, and Paint will merge its menus and toolbars with MyWord's menus and toolbars so that you can edit the picture in place. If the document is saved, the embedded Paint object is saved, too, so that it will come back up just as you left it when you reload the document.
In case you hadn't noticed, MyWord is fully capable of saving the documents you create and loading them back in. It can even read RTF files created by other word processors and serialize OLE objects. Yet CMyWordDoc::Serialize contains just one statement:
CRichEditDoc::Serialize(ar); |
You won't find any other serialization code in
CMyWordDoc
because
CRichEditDoc
can handle serialization on its own.
CRichEditDoc::Serialize
streams data to and from a
CRichEditView
by calling the view's
Serialize
function, which in turn relies on the streaming capabilities built into a rich text edit control. (For more information, see the documentation for the EM_STREAMIN and EM_STREAMOUT messages that can be sent to a rich text edit control and the equivalent
StreamIn
and
StreamOut
function members of MFC's
CRichEditCtrl
class.) It's relatively easy to write an SDK application that saves and loads documents in a rich text edit control, but it's downright simple to do it in MFC because
CRichEditDoc
and
CRichEditView
work together with other
By default, CRichEditDoc serializes documents in rich text format. You can instruct a CRichEditDoc to write text files that lack formatting information and OLE objects by setting the CRichEditDoc data member m_bRTF equal to FALSE before storing a document. By the same token, you can read files in plain text format by setting m_bRTF to FALSE before dearchiving a document. It wouldn't be hard to give MyWord the ability to read and write text files as well as rich text format files, but you'd have to add some logic to the front end of the deserialization process to identify the type of file that's about to be read. CRichEditDoc won't load a text file if m_bRTF is TRUE, and if it reads a rich text format document with m_bRTF equal to FALSE, it converts RTF formatting commands to ordinary text. A full treatment of CRichEditDoc serialization options is beyond the scope of this book, but if you're interested in learning more, a good place to start is the Wordpad source code provided with MFC.