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

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds