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;
}