#include <windows.h>
#include <stdlib.h>


#define LINES 27
static char *abc[] = {
       "anteater",
       "bear",
       "cougar",
       "dingo",
   "elephant",
   "frog",
   "gazelle",
   "hyena",
   "iguana",
   "jackal",
   "kangaroo",
   "llama",
   "moose",
   "newt",
   "octopus",
   "penguin",
   "quail",
   "rat",
   "squid",
   "tortoise",
   "urus",
   "vole",
   "walrus",
   "xylophone",
   "yak",
   "zebra",
   "This line contains many words, but no character. Go figure."
};


LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
       HDC hdc;
       PAINTSTRUCT ps;
       TEXTMETRIC tm;
       SCROLLINFO si;

/* These variables are required to display text. */

       static int xClient;     /* width of client area                  */
       static int yClient;     /* height of client area                 */
       static int xClientMax;  /* maximum width of client area          */

       static int xChar;       /* horizontal scrolling unit             */
       static int yChar;       /* vertical scrolling unit               */

       static int xUpper;      /* average width of uppercase letters    */

       static int xPos;        /* current horizontal scrolling position */
       static int yPos;        /* current vertical scrolling position   */

       static int xMax;        /* maximum horiz. scrolling position     */
       static int yMax;        /* maximum vert. scrolling position      */

       int xInc;               /* horizontal scrolling increment        */
       int yInc;               /* vertical scrolling increment          */

       int i;                  /* loop counter                          */
       int x, y;               /* horiz. and vert. printing coords      */

       int FirstLine;          /* first line in the invalidated area    */
       int LastLine;           /* last line in the invalidated area     */

       RECT rect;

       switch (msg) {
               case WM_CREATE :

                       /* Get the handle of the client area's device context. */

                       hdc = GetDC (hwnd);

                       /* Extract font dimensions from the text metrics. */

                       GetTextMetrics (hdc, &tm);

                       xChar = tm.tmAveCharWidth;
                       xUpper = (tm.tmPitchAndFamily & 1 ? 3 : 2) * xChar/2;
                       yChar = tm.tmHeight + tm.tmExternalLeading;

                       /* Free the device context. */

                       ReleaseDC (hwnd, hdc);

                       /*
                        * Set an arbitrary maximum width for client area.
                        * (xClientMax is the sum of the widths of 48 average
                        * lowercase letters and 12 uppercase letters.)
                        */

                       xClientMax = 48 * xChar + 12 * xUpper;

                       return 0;

               case WM_SIZE:

                       /* Retrieve the dimensions of the client area. */

                       yClient = HIWORD (lParam);
                       xClient = LOWORD (lParam);

                       /*
                        * Determine the maximum vertical scrolling position.
                        * The two is added for extra space below the lines
                        * of text.
                        */

                       yMax = max (0, LINES + 2 - yClient/yChar);

                       /*
                        * Make sure the current vertical scrolling position
                        * does not exceed the maximum.
                        */

                       yPos = min(yPos, yMax);

                       /*
                        * Adjust the vertical scrolling range and scroll box
                        * position to reflect the new yMax and yPos values.
                        */

                       si.cbSize = sizeof(si);
                       si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
                       si.nMin   = 0;
                       si.nMax   = yMax;
                       si.nPage  = yClient / yChar;
                       si.nPos   = yPos;
                       SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

                       /*
                        * Determine the maximum horizontal scrolling position.
                        * The two is added for extra space to the right of the
                        * lines of text.
                        */

                       xMax = max (0, 2 + (xClientMax - xClient)/xChar);

                       /*
                        * Make sure the current horizontal scrolling position
                        * does not exceed the maximum.
                        */

                       xPos = min (xPos, xMax);

                       /*
                        * Adjust the horizontal scrolling range and scroll box
                        * position to reflect the new xMax and xPos values.
                        */

                       si.cbSize = sizeof(si);
                       si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
                       si.nMin   = 0;
                       si.nMax   = xMax;
                       si.nPage  = xClient / xChar;
                       si.nPos   = xPos;
                       SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

                       return 0;

               case WM_PAINT:

                       /* Prepare the window for painting. */

                       hdc = BeginPaint(hwnd, &ps);

                       (void) GetClientRect(hwnd, &rect);
                       FillRect(hdc, &rect, GetStockObject(LTGRAY_BRUSH));

                       /*
                        * Use the current vertical scrolling position and
                        * coordinates of the invalid rectangle to determine
                        * the range of new lines that should be drawn in the
                        * client area.
                        */

                       FirstLine = max (0, yPos + ps.rcPaint.top/yChar - 1);
                       LastLine = min (LINES, yPos + ps.rcPaint.bottom/yChar);

                       /* Display these lines. */

                       for (i = FirstLine;i < LastLine;i++) {
                               x = xChar * (1 - xPos);
                               y = yChar * (1 - yPos + i);
                               TextOut (hdc, x, y, abc[i], lstrlen(abc[i]));
                       }

                       /* Indicate that painting is finished. */

                       EndPaint(hwnd, &ps);
                       break;

               case WM_HSCROLL:
                       switch(LOWORD (wParam)) {

                               /* User clicked shaft left of the scroll box. */

                               case SB_PAGEUP:
                                       xInc = -8;
                                       break;

                               /* User clicked shaft right of the scroll box. */

                               case SB_PAGEDOWN:
                                       xInc = 8;
                                       break;

                               /* User clicked the left arrow. */

                               case SB_LINEUP:
                                       xInc = -1;
                                       break;

                               /* User clicked the right arrow. */

                               case SB_LINEDOWN:
                                       xInc = 1;
                                       break;

                               /* User dragged the scroll box. */

                               case SB_THUMBTRACK:
                                       xInc = HIWORD(wParam) - xPos;
                                       break;

                               default:
                                       xInc = 0;
                       }

                       /*
                        * If applying the horizontal scrolling increment does not
                        * take the scrolling position out of the scrolling range,
                        * increment the scrolling position, adjust the position
                        * of the scroll box, and update the window.
                        */

                       if (xInc = max (-xPos, min (xInc, xMax - xPos))) {
                               xPos += xInc;

                               ScrollWindowEx (hwnd, -xChar * xInc, 0,
                                       (CONST RECT *) NULL, (CONST RECT *) NULL,
                                       (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE);
                               si.cbSize = sizeof(si);
                               si.fMask  = SIF_POS;
                               si.nPos   = xPos;
                               SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
                               UpdateWindow (hwnd);
                       }

                       return 0;

               case WM_VSCROLL:
                       switch(LOWORD (wParam)) {

                               /* User clicked the shaft above the scroll box. */

                               case SB_PAGEUP:
                                       yInc = min(-1, -yClient / yChar);
                                       break;

                               /* User clicked the shaft below the scroll box. */

                               case SB_PAGEDOWN:
                                       yInc = max(1, yClient / yChar);
                                       break;

                               /* User clicked the top arrow. */

                               case SB_LINEUP:
                                       yInc = -1;
                                       break;

                               /* User clicked the bottom arrow. */

                               case SB_LINEDOWN:
                                       yInc = 1;
                                       break;

                               /* User dragged the scroll box. */

                               case SB_THUMBTRACK:
                                       yInc = HIWORD(wParam) - yPos;
                                       break;

                               default:
                                       yInc = 0;
                       }

                       /*
                        * If applying the vertical scrolling increment does not
                        * take the scrolling position out of the scrolling range,
                        * increment the scrolling position, adjust the position
                        * of the scroll box, and update the window. UpdateWindow
                        * sends the WM_PAINT message.
                        */
                       if (yInc = max(-yPos, min(yInc, yMax - yPos))) {
                               yPos += yInc;
                               ScrollWindowEx(hwnd, 0, -yChar * yInc,
                                       (CONST RECT *) NULL, (CONST RECT *) NULL,
                                       (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE);
                               si.cbSize = sizeof(si);
                               si.fMask  = SIF_POS;
                               si.nPos   = yPos;

                               SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
                               UpdateWindow (hwnd);
                       }

                       return 0;

               case WM_DESTROY:
                       PostQuitMessage( 0 );
                       break;

               default:
                       return(DefWindowProc(hwnd, msg, wParam, lParam));
       }

       return 0;
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
       WNDCLASS wc;
       MSG msg;
       HWND hwnd;

       if(! hPrevInstance) {
               memset(&wc,0,sizeof(WNDCLASS));
               wc.lpszClassName = "MyAppClass";
               wc.lpfnWndProc = MainWndProc;
               wc.style = CS_VREDRAW | CS_HREDRAW;
               wc.hInstance = hInstance;
               wc.hIcon = LoadIcon(NULL, IDI_APPLICATION );
               wc.hCursor = LoadCursor(NULL, IDC_ARROW );
               wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
               wc.lpszMenuName = NULL;
               wc.cbClsExtra = 0;
               wc.cbWndExtra = 0;
               RegisterClass(&wc);
       }

       hwnd = CreateWindowEx(
               0L,
               "MyAppClass",
               "Scroll Bar Application",
               WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
               CW_USEDEFAULT,
               CW_USEDEFAULT,
               CW_USEDEFAULT,
               CW_USEDEFAULT,
               (HWND)NULL,
               (HMENU)NULL,
               hInstance,
               (LPVOID)NULL
       );

       ShowWindow(hwnd, nCmdShow);

       while (GetMessage(&msg, NULL, 0, 0 )) {
               TranslateMessage(&msg);
               DispatchMessage(&msg);
       }

       return msg.wParam;
}