Listing 2 The CStrWnd class

//  CStrWnd.cpp

//  See CStrWnd.h for a definition of what the
//  class CStrWnd does

#include   "stdhdr.h"
#include   "test_r.h"
#include   "cstrwnd.h"
#include   "strdetab.h"

CStrWnd::CStrWnd (const char * window_name)
{
VERIFY (LoadAccelTable ("CStrWndAccelTable"));
VERIFY (Create (NULL, window_name,
             WS_CHILD | WS_VSCROLL, rectDefault,
             NULL));
SetFocus ();
current_first_row = 0; current_first_col = 0;
h_scroll_flag = 0;
length_of_longest_line = 0;
}

BEGIN_MESSAGE_MAP (CStrWnd, CMDIChildWnd)
   ON_WM_PAINT ()
   ON_WM_VSCROLL ()
   ON_WM_HSCROLL ()
   ON_WM_KEYDOWN ()
   
   ON_COMMAND (CSW_DOC_START, OnDocStart)
   ON_COMMAND (CSW_DOC_END, OnDocEnd)
   ON_COMMAND (CSW_LINE_RIGHT, OnLineRight)
   ON_COMMAND (CSW_LINE_LEFT, OnLineLeft)
END_MESSAGE_MAP ()

afx_msg void CStrWnd::OnPaint ()
{
CPaintDC dc (this);
// Use a fixed-pitch font for better presentation
VERIFY (dc.SelectStockObject(OEM_FIXED_FONT) != NULL);

// First find out number of lines in the window
TEXTMETRIC tm;
VERIFY (dc.GetTextMetrics (&tm));
const int char_height = tm.tmHeight
                    + tm.tmExternalLeading;
const int char_width = tm.tmAveCharWidth;
CRect rect;
GetClientRect (rect);
no_of_lines : rect.Height () / char_height;
no_of_chars_in_line = rect.Width () / char_width;

// Set up the tab stop positions
const int tab_interval = 4;
const int no_of_tabs = 120 / tab_interval;
int char_tab_stops [no_of_tabs];
for (int i = 0; i < no_of_tabs; i++)

   char_tab_stops [i] = (i + 1) * tab_interval;

//  Write a window_full of text

for (int lc = 0;
    lc < no_of_lines
    && current_first_row + lc
      < text_buffer.GetSize ();
    lc++)
   {
   const int max_line_length = 256;
   char out_buf [max_line_length + 1];
   int line_length;
   //  Convert tabs to spaces
   strdetab (out_buf, max_line_length,
           text_buffer [current_first_row + lc],
           no_of_tabs, char_tab_stops,
           &line_length);
   //  Get pointer to first char in line for output
   const char * first_char = out_buf
                         + current_first_col;
   //  Update record of longest line length
   if (length_of_longest_line < line_length)
      length_of_longest_line = line_length;
   //  Call up horizontal scroll bar if needed
   if (line_length > no_of_chars_in_line)
      h_scroll_flag = 1;
   dc.TextOut (0, lc * char_height, first_char,
             line_length - current_first_col);
   }
if (h_scroll_flag) ShowScrollBar (SB_HORZ, TRUE);
}

afx_msg void CStrWnd::OnVScroll (UINT nSBCode,
                           UINT npos,
                           CScrollBar * pBar)
{
int min_scroll_pos, max_scroll_pos,
   max index = text_buffer.GetSize () - 1,
   scrolled = 1;
if (max_index < 0) max_index = 0;
   //   Cater for no lines in text_buffer
switch (nSBCode)
   {
   case SB_LINEDOWN:
      current_first_row++; break;
   case SB_PAGEDOWN:
      current_first_row += no_of_lines - 1; break;
   case SB_LINEUP:
      current_first_row--; break;
   case SB_PAGEUP:
      current_first_row -= no_of_lines - 1; break;
   case SB_THUMBPOSITION:
      GetScrollRange (SB_VERT, &min_scroll_pos,
                   &max_scroll_pos);
      current_first_row =
         (max_index * (npos - min_scroll_pos))
          / (max_scroll_pos - min_scroll_pos);
      break;
   default:
   //  We did nothing, so do not update the window!
      scrolled = 0; break;
   }

if (scrolled)
   {
   //   Now make sure that the current row is in range
   if (current_first_row < 0) current_first_row = 0;
   if (current_first_row > max_index)
      current_first_row = max_index;
   Invalidate (TRUE);
   SetVerticalScroll ();
   UpdateWindow ();
   }
}

afx_msg void CStrWnd::OnHScroll (UINT nSBCode,
                           UINT npos,
                           CScrollBar * pBar)
{
int min_scroll_pos, max_scroll_pos,
   scrolled = 1;
GetScrollRange (SB_VERT,&min_scroll_pos,
             &max_scroll_pos);
switch (nSBCode)
   {
   case SB_LINERIGHT:
      current_first_col++; break;
   case SB_PAGERIGHT:
      current_first_col += 10; break;
   case SB_LINELEFT:
      current_first_col--; break;
   case SB_PAGELEFT:
      current_first_col -= 10; break;
   case SB_LEFT: 
      current_first_col = 0; break;
   case SB_RIGHT:
      current_first_col = length_of_longest_line - 1;
      break;
   case SB_THUMBPOSITION:
      current_first_col =
         ((length_of_longest_line - 1)
          * (npos - min_scroll_pos))
          / (max_scroll_pos - min_scroll_pos);
      break;
   default:
      //   We did nothing, so do not update window!
      scrolled = 0; break;
   }

if (scrolled)
   {
   //  Now ensure that we are within limits
   if (current_first_col < 0) current_first_col = 0;
   if (current_first_col >= length_of_longest_line)
      current_first_col >= length_of_longest_line - 1;
   Invalidate (TRUE);
   if (h_scroll_flag)
      SetScrollPos (SB_HORZ,
              ((max_scroll_pos - min_scroll_pos)
              * current_first_col)
              / (length_of_longest_line -1)
              + min_scroll_pos,
              TRUE);
   UpdateWindow ();
   }
}

afx_msg void CStrWnd::OnKeyDown (UINT nChar,
                           UINT nRepCnt,
                           UINT nFlags)
{
switch (nChar)
   {
   case VK_DOWN:
      OnVScroll (SB_LINEDOWN, 0, NULL); break;
   case VK_NEXT:                   // Page down
      OnVScroll (SB_PAGEDOWN, 0, NULL); break;
   case VK_UP:
      OnVScroll (SB_LINEUP, 0, NULL); break;
   case VK_PRIOR:                  // Page up
      OnVScroll (SB_PAGEUP, 0, NULL); break;
   case VK_LEFT:
      OnHScroll (SB_LINELEFT, 0, NULL); break;
   case VK_HOME:
      OnHScroll (SB_LEFT, 0, NULL); break;
   case VK_RIGHT:
      OnHScroll (SB_LINERIGHT, 0, NULL); break;
   case VK_END:
      OnHScroll (SB_RIGHT, 0, NULL); break;
   }
}

afx_msg void CStrWnd::OnDocStart ()
{
int min_scroll_pos, max_scroll_pos;
GetScrollRange (SB_VERT, &min_scroll_pos,
             &max_scroll_pos);
Invalidate (TRUE);
current_first_row = 0;
SetScrollPos (SB_VERT, min_scroll_pos, TRUE);
UpdateWindow ();
}

afx_msg void CStrWnd::OnDocEnd ()
{
int min_scroll_pos, max_scroll_pos;
GetScrollRange (SB_VERT, &min_scroll_pos,
             &max_scroll_pos);
Invalidate (TRUE);
current_first_row = text_buffer.GetUpperBound () - 4;
//  Leave 4 lines on the screen
if (current_first_row < 0) current_first_row = 0;
SetScrollPos (SB_VERT, max_scroll_pos, TRUE);
UpdateWindow ();
}

afx_msg void CStrWnd::OnLineRight ()
{OnHScroll (SB_PAGERIGHT, 0, NULL);}

afx_msg void CStrWnd::OnLineLeft ()
{OnHScroll (SB_PAGELEFT, 0, NULL);}

void CStrWnd::UpdateScreen ()
{
current_first_row = text_buffer.GetUpperBound ()
                 - no_of_lines;
if (current_first_row < 0) current_first_row = 0;
SetVerticalScroll ();
Invalidate (TRUE);
UpdateWindow ();
}

void CStrWnd::SetVerticalScroll ()
{
int min_scroll_pos, max_scroll_pos,
   max_index = text_buffer.GetUpperBound ();
GetScrollRange (SB_VERT, &min_scroll_pos,
             &max_scroll_pos);
SetScrollPos (SB_VERT,
            ((max_scroll_pos - min_scroll_pos )
              * current_first_row)
            / ((max_index > 0) ? max_index : 1)
            + min_scroll_pos,
            TRUE);
}

// End of File