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


Comments

  • Tanx

    Posted by Legacy on 01/26/2004 12:00am

    Originally posted by: Teldin Moore

    Thank you for showing me how to redirect streams to a MFC editbox.

    kind regards,

    Mr. Moore.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

  • Savvy enterprises are discovering that the cloud holds the power to transform IT processes and support business objectives. IT departments can use the cloud to redefine the continuum of development and operations—a process that is becoming known as DevOps. Download the Executive Brief DevOps: Why IT Operations Managers Should Care About the Cloud—prepared by Frost & Sullivan and sponsored by IBM—to learn how IBM SmartCloud Application services provide a robust platform that streamlines …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds