Figure 1 SCBMOD3
ScribDoc.h
// Forward declaration of data structure class
class CStroke;
class CScribbleDoc : public CDocument {
protected:
CScribbleDoc();
DECLARE_DYNCREATE(CScribbleDoc)
// The document keeps track of the current pen width on
// behalf of all views. We'd like the user interface of
// Scribble to be such that if the user chooses the Draw
// Thick Line command, it will apply to all views, not just
// the view that currently has the focus.
UINT m_nPenWidth; // current user-selected pen width
CPen m_penCur; // pen created according to
// user-selected pen style (width)
public:
CTypedPtrList<CObList,CStroke*> m_strokeList;
CPen* GetCurrentPen() { return &m_penCur; }
CStroke* NewStroke();
//{{AFX_VIRTUAL(CScribbleDoc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
virtual void DeleteContents();
virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
//}}AFX_VIRTUAL
virtual ~CScribbleDoc();
protected:
void InitDocument();
//{{AFX_MSG(CScribbleDoc)
// **MOD** new title bar handlers
afx_msg void OnUpdateMod(CCmdUI* pCmdUI);
afx_msg void OnUpdateNumStrokes(CCmdUI* pCmdUI);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
// class CStroke
//
// A stroke is a series of connected points in the scribble drawing.
// A scribble document may have multiple strokes.
class CStroke : public CObject {
public:
CStroke(UINT nPenWidth);
CArray<CPoint,CPoint> m_pointArray; // series of connected points
BOOL DrawStroke(CDC* pDC);
virtual void Serialize(CArchive& ar);
protected:
CStroke();
DECLARE_SERIAL(CStroke)
UINT m_nPenWidth; // one pen width applies to entire stroke
};
ScribDoc.cpp
#include "stdafx.h"
#include "Scribble.h"
#include "ScribDoc.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CScribbleDoc, CDocument)
BEGIN_MESSAGE_MAP(CScribbleDoc, CDocument)
//{{AFX_MSG_MAP(CScribbleDoc)
ON_UPDATE_COMMAND_UI(ID_TITLE_MOD, OnUpdateMod)
ON_UPDATE_COMMAND_UI(ID_TITLE_NUMSTROKES, OnUpdateNumStrokes)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CScribbleDoc::CScribbleDoc()
{
}
CScribbleDoc::~CScribbleDoc()
{
}
BOOL CScribbleDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
InitDocument();
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CScribbleDoc serialization
void CScribbleDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring()) {
} else {
}
m_strokeList.Serialize(ar);
}
BOOL CScribbleDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
if (!CDocument::OnOpenDocument(lpszPathName))
return FALSE;
InitDocument();
return TRUE;
}
void CScribbleDoc::DeleteContents()
{
while (!m_strokeList.IsEmpty()) {
delete m_strokeList.RemoveHead();
}
CDocument::DeleteContents();
}
void CScribbleDoc::InitDocument()
{
m_nPenWidth = 2; // default 2 pixel pen width
m_penCur.CreatePen(PS_SOLID, m_nPenWidth, RGB(0,0,0)); // solid, black pen
}
CStroke* CScribbleDoc::NewStroke()
{
CStroke* pStrokeItem = new CStroke(m_nPenWidth);
m_strokeList.AddTail(pStrokeItem);
SetModifiedFlag(); // Mark the document as having been modified, for
// purposes of confirming File Close.
return pStrokeItem;
}
void CScribbleDoc::OnUpdateMod(CCmdUI* pCmdUI)
{
pCmdUI->SetText(IsModified() ? "*" : "");
pCmdUI->m_bContinueRouting = TRUE;
}
void CScribbleDoc::OnUpdateNumStrokes(CCmdUI* pCmdUI)
{
CString s;
s.Format(" [S:%d]", m_strokeList.GetCount());
pCmdUI->SetText(s);
pCmdUI->m_bContinueRouting = TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CStroke
IMPLEMENT_SERIAL(CStroke, CObject, 1)
CStroke::CStroke()
{
}
CStroke::CStroke(UINT nPenWidth)
{
m_nPenWidth = nPenWidth;
}
void CStroke::Serialize(CArchive& ar)
{
if (ar.IsStoring()) {
ar << (WORD)m_nPenWidth;
m_pointArray.Serialize(ar);
} else {
WORD w;
ar >> w;
m_nPenWidth = w;
m_pointArray.Serialize(ar);
}
}
BOOL CStroke::DrawStroke(CDC* pDC)
{
CPen penStroke;
if (!penStroke.CreatePen(PS_SOLID, m_nPenWidth, RGB(0,0,0)))
return FALSE;
CPen* pOldPen = pDC->SelectObject(&penStroke);
pDC->MoveTo(m_pointArray[0]);
for (int i=1; i < m_pointArray.GetSize(); i++) {
pDC->LineTo(m_pointArray[i]);
}
pDC->SelectObject(pOldPen);
return TRUE;
}
TitleBar.h
struct TITLEPANE; // fwd ref
//////////////////
// A titlebar is not really a window, just an object that manages title
// bar "inidcators", which are like panes in the status bar.
//
class CTitleBar : public CCmdTarget {
DECLARE_DYNAMIC(CTitleBar)
TITLEPANE* m_arPanes; // array of "panes" (inidicators)
UINT m_nCount; // #panes
CString m_sPrevTitle; // previous title
CString m_sTitle; // current title
public:
CTitleBar();
~CTitleBar();
BOOL SetIndicators(const UINT* lpIDArray, int nIDCount);
void OnIdleUpdate(CFrameWnd* pFrame, BOOL bDisableIfNoHndler);
};
TitleBar.cpp
////////////////////////////////////////////////////////////////
// Implementation for title bar
//
#include "stdafx.h"
#include "titlebar.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNAMIC(CTitleBar, CCmdTarget)
//////////////////
// One of these for each inidicator
//
struct TITLEPANE {
CString m_sText; // current text
UINT m_nID; // indicator ID
BOOL m_bEnabled; // enabled?
};
CTitleBar::CTitleBar()
{
m_nCount = 0;
m_arPanes = NULL;
}
CTitleBar::~CTitleBar()
{
delete [] m_arPanes; // OK if NULL
}
//////////////////
// Call this when your frame is constructed, i.e., from OnCreate, to
// create the indicators.
//
BOOL CTitleBar::SetIndicators(const UINT* lpIDs, int nCount)
{
// Allocate indicator array
ASSERT(nCount>0);
m_arPanes = new TITLEPANE[nCount];
ASSERT(m_arPanes);
// Initialize panes
for (int i=0; i<nCount; i++) {
TITLEPANE& pane = m_arPanes[i];
pane.m_nID = lpIDs[i];
pane.m_bEnabled = TRUE;
}
m_nCount = nCount;
return TRUE;
}
//////////////////
// This class is private to this file.
//
class CTitleCmdUI : public CCmdUI {
public:
TITLEPANE* m_pPane; // ptr to associate title pane
// re-implementations:
virtual void Enable(BOOL bOn);
virtual void SetCheck(int nCheck);
virtual void SetText(LPCTSTR lpszText);
};
void CTitleCmdUI::Enable(BOOL bOn)
{
m_bEnableChanged = TRUE; // used by MFC
m_pPane->m_bEnabled = bOn;
}
void CTitleCmdUI::SetCheck(int nCheck)
{
// no-op
}
void CTitleCmdUI::SetText(LPCTSTR lpszText)
{
m_pPane->m_sText = lpszText;
}
//////////////////
// Do idle update of all title bar indicators
// You must handle WM_IDLEUPDATEUI from your frame window and
// call this method from your handler.
//
void CTitleBar::OnIdleUpdate(CFrameWnd* pFrame, BOOL bDisableIfNoHndler)
{
m_sTitle.Empty();
CTitleCmdUI cmdui;
cmdui.m_nIndexMax = m_nCount;
for (UINT i=0; i<cmdui.m_nIndexMax; i++) {
TITLEPANE& pane = m_arPanes[i];
cmdui.m_nIndex = i;
cmdui.m_pPane = &pane;
cmdui.m_nID = pane.m_nID;
cmdui.DoUpdate(pFrame, FALSE);
if (pane.m_bEnabled)
m_sTitle += pane.m_sText;
}
if (m_sPrevTitle != m_sTitle) {
pFrame->OnUpdateFrameTitle(TRUE);
CString title;
pFrame->GetWindowText(title);
title += m_sTitle;
pFrame->SetWindowText(title);
m_sPrevTitle = m_sTitle;
}
}
ChildFrm.h
#include "TitleBar.h"
class CChildFrame : public CMDIChildWnd {
DECLARE_DYNCREATE(CChildFrame)
CTitleBar m_titleBar; // **MOD** title bar info
BOOL m_bViewTitleInfo; // **MOD** whether to display pane info
public:
CChildFrame();
virtual ~CChildFrame();
//{{AFX_VIRTUAL(CChildFrame)
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//}}AFX_VIRTUAL
protected:
//{{AFX_MSG(CChildFrame)
// **MOD** new functions
afx_msg LRESULT OnIdleUpdateCmdUI(WPARAM wParam, LPARAM);
afx_msg void OnViewTitleInfo();
afx_msg void OnUpdateUIViewTitleInfo(CCmdUI* pCmdUI);
afx_msg void OnUpdateUINumStrokes(CCmdUI* pCmdUI);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
ChildFrm.cpp
#include "stdafx.h"
#include "Scribble.h"
#include "ChildFrm.h"
#include <afxpriv.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWnd)
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
//{{AFX_MSG_MAP(CChildFrame)
ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
ON_COMMAND(ID_VIEW_TITLE_INFO, OnViewTitleInfo)
ON_UPDATE_COMMAND_UI(ID_VIEW_TITLE_INFO, OnUpdateUIViewTitleInfo)
ON_UPDATE_COMMAND_UI(ID_TITLE_NUMSTROKES, OnUpdateUINumStrokes)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CChildFrame::CChildFrame()
{
// By default, don't display title pane info
m_bViewTitleInfo = FALSE;
// Create title bar indicators
static UINT indicators[] = { ID_TITLE_MOD, ID_TITLE_NUMSTROKES };
m_titleBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(indicators[0]));
}
CChildFrame::~CChildFrame()
{
}
BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
return CMDIChildWnd::PreCreateWindow(cs);
}
//////////////////
// **MOD** Handle idle command update
//
LRESULT CChildFrame::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)
{
m_titleBar.OnIdleUpdate(this, (BOOL)wParam); // pass to title bar
CMDIChildWnd::OnIdleUpdateCmdUI(); // pass to default handler
return 0L;
}
//////////////////
// **MOD** command and command update handlers
//
void CChildFrame::OnViewTitleInfo()
{
m_bViewTitleInfo = !m_bViewTitleInfo;
}
void CChildFrame::OnUpdateUIViewTitleInfo(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_bViewTitleInfo);
}
void CChildFrame::OnUpdateUINumStrokes(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bViewTitleInfo);
}
Since title bar panes are not really panes at all (they're not windows), I need a little structure to describe each pane. I need an ID, a place to hold the text, and a flag that tells whether the pane is enabled.
struct TITLEPANE {
CString m_sText; // current text
UINT m_nID; // indicator ID
BOOL m_bEnabled; // enabled?
};
Figure 3 DLGIDLE.CPP
////////////////////////////////////////////////////////////////
// DLGIDLE 1996 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// DLGIDLE illustrates how to implement a dialog-based app that
// does OnIdle processing. Compiles with VC++ 4.0 or greater.
#include "stdafx.h"
#include "resource.h"
#include "tracewin.h"
#include <afxpriv.h> // For WM_KICKIDLE
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
////////////////////////////////////////////////////////////////
// Dialog class
//
class CMyDlg : public CDialog {
public:
CMyDlg() : CDialog(IDD_ABOUTBOX) { }
afx_msg LRESULT OnKickIdle(WPARAM, LPARAM);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
END_MESSAGE_MAP()
//////////////////
// Handle MFC private message WM_KICKIDLE. LPARAM is the idle count.
// Return TRUE/FALSE continue/discontinue idle processing.
//
LRESULT CMyDlg::OnKickIdle(WPARAM, LPARAM lCount)
{
// Do idle processing here, just like CWinApp::OnIdle
//
TRACE("CMyDlg::OnKickIdle, do priority %d stuff\n", lCount);
return (lCount <= 2);
}
////////////////////////////////////////////////////////////////
// Application class with OnIdle function.
//
class CMyApp : public CWinApp {
public:
CMyApp();
virtual BOOL InitInstance();
DECLARE_MESSAGE_MAP()
};
CMyApp NEAR theApp;
BEGIN_MESSAGE_MAP(CMyApp, CWinApp)
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()
CMyApp::CMyApp()
{
}
BOOL CMyApp::InitInstance()
{
MfxTraceInit();
CMyDlg dlg;
m_pMainWnd = &dlg;
dlg.DoModal();
return FALSE;
}