std::ostream Implementation for Text Output with CWnd-derived Controls

I needed this class for keeping some other parts of a larger application
portable. These other classes just require ostream & parameters for
doing message and error text output (adapted lex and yacc generated classes
actually).

The basic ideas for the implementation came from a sample implementation
for a X-Motif widget ostream by
Dietmar Kuehl,
see this URL to read more about it, and compare what i have been changing:


I added extra handling for special characters ‘n’ and ‘a’:


  • ‘n’ needs to have a ‘r’ prefixed for proper output of a CR-LF sequence
    (linebreak) in a CWnd

  • ‘a’ is commonly defined as alert character, i have implemented this as
    an ‘optical bell’ (flashing of top window title)

The implementation is intended for use with a STATIC or EDIT control window
class and actually uses the CWnd::GetWindowText() and CWnd::SetWindowText()
methods for doing the text rendering. To get a CDC * with CWnd::GetWindowDC()
and to draw directly in the windows’ client area would be more efficient
and flexible (concerning text colouring and formatting). But if you use
an EDIT control in conjunction with winostream you’ll have advantage of
all the standard control features as ‘copy & paste’ for instance.

There’s another article to be found on CodeGuru, mentioning to send
a WM_SETSEL message to append text to the actual window text (thus you
can omit the CWnd::GetWindowText() calls). But this can be realized with
EDIT control window classes only; my solution will be slower but more general.

Implementation of special stream manipulators in the same style as ‘std::endl(ostream
&)’ will reqire to reimplement std::ostream overloadings of the operator<<()
method to return a winostream & actually. Otherwise the manipulator
function will receive an ostream & when a standard operator<<()
overloading is seen at the left side.

The methods overloaded and declared with winstrbuf should represent
a sufficient abstraction for implementing an ostream/streambuf pair. Overload
or rewrite the methods winstrbuf::TranslateWinChar(), winstrbuf::put_buffer()
and winstrbuf::put_char() to adapt character rendering to your needs.


//////////////////////////////////////////////////////////////////////
// winostream.h: interface of class winostream
// author: g. makulik
// purpose: implementing std::ostream for text output on
// a MFC-CWnd. This is primarily intended to use
// with CStatic/CEdit controls.
//
//////////////////////////////////////////////////////////////////////

// … some includes etc.

class winostream : public ostream
{
public:
winostream ( CWnd * pWnd_
, int nBufSize_ = 0
);
virtual ~winostream ();

// It’s not essential to reimplement all the operator<< overloads
// of ostream. But this will be useful if you like to implement
// special manipulator methods and their global pendants in the
// same style as ‘endl’.
winostream & operator<< ( winostream & (*fmanip)(winostream &)
);
winostream & operator<< ( ostream & (*fmanip)(ostream &)
)
{ fmanip(*this); return *this; };
winostream & operator<< ( ios & (*fmanip)(ios &)
)
{ fmanip(static_cast<ios>(*this)); return *this; };
winostream & operator<< ( ios_base & (*fmanip)(ios_base &)
)
{ fmanip(static_cast<ios_base>(*this)); return *this; };
winostream & operator<< ( bool x_
)
{ ostream::operator <<(x_); return *this; };
winostream & operator<< ( short x_
)
{ ostream::operator <<(x_); return *this; };
// … put more << operator overloadings here, see ostream for
// their signature

// declare special winostream manipulators here
//====================================================================

protected:
virtual CWnd * getwnd ()
{ return m_pWinstrbuf->getwnd(); };
private:
winstrbuf * m_pWinstrbuf;
};

//////////////////////////////////////////////////////////////////////
// winstrbuf.h: interface of class winstrbuf.
// author: g. makulik
// purpose: see winostream.h
//////////////////////////////////////////////////////////////////////

// … some includes etc.

class winstrbuf : public streambuf
{
public:
winstrbuf ( CWnd * pWnd_
, int nBufSize_ = 0
);
virtual ~winstrbuf ();

CWnd * getwnd ()
{ return m_pWnd; };

protected:
virtual int overflow ( int c_
);
virtual int sync ();
virtual CString TranslateWinChar( int c_
);
virtual CString TranslateWinChar( CString const & cs_
);
virtual void PutAlertChar ();
private:
CWnd * m_pWnd;
int m_nBufSize;

// helper class for representing installed
// WIN32 timer ressources
struct CFlashTimer {
CFlashTimer ( UINT nId_
, CWnd * pWnd_
, UINT nTimes_
)
: m_nId(nId_)
, m_pWnd(pWnd_)

// Assure to have an odd value for m_nTimes, to
// get the same window state as before, when the
// flashing is over
, m_nTimes((nTimes_%2)?nTimes_+1:nTimes_)
, m_nCount(0)
{};
UINT m_nId;
CWnd * m_pWnd;
UINT m_nTimes;
UINT m_nCount;
BOOL m_bNextFlash;

static void CALLBACK EXPORT
TimerProc ( HWND hWnd_
, UINT nMsg_
, UINT nId_
, DWORD dwTime_
);
};
friend struct CFlashTimer;

CFlashTimer * InstallTimer ( CWnd * pWnd_
, DWORD dwFreq_
, UINT nTimes_
);
static CFlashTimer * GetTimer ( UINT nId_
);
static BOOL KillTimer ( UINT nId_
);

static UINT sm_nextTimerId;
static map<UINT,CFlashTimer*> sm_mapFlshTimers;

void put_buffer ();
void put_char ( int c_
);

};

//////////////////////////////////////////////////////////////////////
// winstrbuf.cpp: implementation of class winstrbuf.
// author: g. makulik
//
//////////////////////////////////////////////////////////////////////

// … some includes etc.

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

winstrbuf::winstrbuf ( CWnd * pWnd_
, int nBufSize_
)
: streambuf()
, m_pWnd(pWnd_)
, m_nBufSize(nBufSize_)
{
ASSERT_VALID(pWnd_);
if (m_nBufSize)
{
// if the requested buffer size is greater than 0,
// allocate the buffer memory and tell streambuf
// where it is allocated
char * ptr = new char[m_nBufSize];
setp(ptr, ptr + m_nBufSize);
}
else
setp(0, 0);

setg(0, 0, 0);
}

winstrbuf::~winstrbuf ()
{
sync();
if (m_nBufSize)
{
// free any buffer memory which was
// allocated on construction
delete [] pbase();
}
}

// overflow is called when the buffer is full
int winstrbuf::overflow ( int c_
)
{
// render textoutput and clear the buffer
put_buffer();

// check for end of file character
if (c_ != EOF)
if (pbase() == epptr())
// if there’s no space left in the buffer
// send the character directly to the device
put_char(c_);
else
// otherwise append it to the buffer
sputc(c_);

return 0;
}

// called to syncronize contents of buffer and device
int winstrbuf::sync ()
{
put_buffer();
return 0;
}

// called to render a single character on the device
void winstrbuf::put_char( int c_
)
{
// extract actual window text
ASSERT_VALID(m_pWnd);
CString csBuf;
m_pWnd->GetWindowText(csBuf);

// append character to extracted text
csBuf += TranslateWinChar(c_);

// write back text to the window (render)
m_pWnd->SetWindowText(csBuf);
}

// called to render the actual content of the buffer on the device
void winstrbuf::put_buffer()
{
if (pbase() != pptr())
{
int nBufLength = (pptr() – pbase());

// extract actual window text
ASSERT_VALID(m_pWnd);
CString csBuf;
m_pWnd->GetWindowText(csBuf);

// append buffer to extracted text
CString csHelp(pbase(),nBufLength);
csBuf += TranslateWinChar(csHelp);

// write back text to the window (render)
m_pWnd->SetWindowText(csBuf);

setp(pbase(), epptr());
}
}

// used to implement rendering of special characters
// on the specific device (CWnd* actually)
CString winstrbuf::TranslateWinChar ( int c_
)
{
CString csRes;

csRes = “”;
switch((char)c_)
{
// prefix ‘LF’ with ‘CR’
case ‘n’:
csRes += ‘r’;
break;
// implement alert character as ‘optical bell’. Just override if
// you want to do anything other.
case ‘a’:
PutAlertChar();
return csRes;
break;
}

csRes += (char)c_;
return csRes;
}

// respects rendering of special characters
// on the specific device for a sequence of
// characters
CString winstrbuf::TranslateWinChar ( CString const & cs_
)
{
CString csRes;

csRes = “”;
for(int i = 0;
i < cs_.GetLength();
i++)
{
csRes += TranslateWinChar(cs_[i]);
}

return csRes;
}

// The following functions implement an ‘optical bell’ for
// a MFC standard window
void winstrbuf::PutAlertChar()
{
ASSERT(m_pWnd);
// flashing is always done with the top level parent window
// (the one with the title bar usually)
CWnd * pFlashWin = m_pWnd->GetTopLevelParent();

// Install a timer activated every 100 ms and
// killed after 5th activation
InstallTimer(pFlashWin,100,5);

}

// the static timer callback function, it is called when the
// timer is activated. Note since this is a static method, a
// pseudo-this pointer is externalized in a static map of
// this class. It can be uniquely identified with the associated
// timer’s ID. Any access to the static map will be thread critical;
// use critical sections for map access, to make this class thread
// safe.
void CALLBACK EXPORT
winstrbuf::CFlashTimer::TimerProc ( HWND hWnd_
, UINT nMsg_
, UINT nId_
, DWORD dwTime_
)
{
CFlashTimer * pThis;
// Thread critical
//———————————————————
// get pseudo-this pointer
if((pThis = winstrbuf::GetTimer(nId_)) != NULL)
{
// Thread critical end
//———————————————————

// increment call counter first
if(++pThis->m_nCount <= pThis->m_nTimes)
{
// change flash state of the window to former state
pThis->m_bNextFlash = ::FlashWindow(hWnd_,pThis->m_bNextFlash);
}
else
{
// kill the timer if the specified number
// of activations was done
winstrbuf::KillTimer(nId_);
}
}
}

// initialize static members
UINT winstrbuf::sm_nextTimerId = 0;
map<UINT,CFlashTimer*> winstrbuf::sm_mapFlshTimers;

// this method installs a timer. The this-pointer of a CFlashTimer
// is stored in the global timer map
winstrbuf::CFlashTimer *
winstrbuf::InstallTimer ( CWnd * pWnd_
, DWORD dwFreq_
, UINT nTimes_
)
{
CFlashTimer * pRes = NULL;
// Thread critical from here to return
//———————————————————
if(pWnd_->SetTimer(++sm_nextTimerId,dwFreq_,CFlashTimer::TimerProc)
!= sm_nextTimerId)
{
return NULL;
}
pRes = new CFlashTimer(sm_nextTimerId,pWnd_,nTimes_);
sm_mapFlshTimers[sm_nextTimerId] = pRes;
return pRes;
}

// finds a specific CFlashTimer-pointer identified by
// an associated timer ID
winstrbuf::CFlashTimer * winstrbuf::GetTimer ( UINT nId_
)
{
CFlashTimer * pRes = NULL;
// Thread critical from here to return
//———————————————————
if(sm_mapFlshTimers.find(nId_) != sm_mapFlshTimers.end())
{
pRes = sm_mapFlshTimers[nId_];
}
return pRes;
}

// releases the WIN32 timer resource and removes the associated
// CFlashTimer object from the global timer map
BOOL winstrbuf::KillTimer ( UINT nId_
)
{
BOOL bRes = FALSE;
// Thread critical from here to return
//———————————————————
CFlashTimer * pFlashTimer = GetTimer(nId_);
if(pFlashTimer)
{
bRes = pFlashTimer->m_pWnd->KillTimer(pFlashTimer->m_nId);
delete pFlashTimer;
sm_mapFlshTimers.erase(nId_);
}
return bRes;
}

Downloads


Download demo project – 24 Kb
Download source – 4 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read