Link to home
Start Free TrialLog in
Avatar of Tom Knowlton
Tom KnowltonFlag for United States of America

asked on

Drawing program - refreshing objects without redrawing the ENTIRE screen

My questions are in the 2nd comment below this question:

Consider the following code:

#include <windows.h>
#include "resource.h"
#include "ObjectsDrawn.h"
#define ID_TIMER 1

int xCoordStart,
      yCoordStart,
      xCoordEnd,
      yCoordEnd,
      CollectionIndex;

bool FirstClick;

ObjectsDrawn Collection[100];



LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

TCHAR szAppName[] = TEXT ("MenuDemo") ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;
     
     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = szAppName ;
     wndclass.lpszClassName = szAppName ;
     
     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName, TEXT ("Menu Demonstration"),
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static int idColor [5] = { WHITE_BRUSH,  LTGRAY_BRUSH, GRAY_BRUSH,
                                DKGRAY_BRUSH, BLACK_BRUSH } ;
     static int iSelection = IDM_BKGND_WHITE ;
     HMENU      hMenu ;
     HDC hdc;
     switch (message)
     {
     case WM_COMMAND:
          hMenu = GetMenu (hwnd) ;
         
          switch (LOWORD (wParam))
          {
          case IDM_APP_EXIT:
               SendMessage (hwnd, WM_CLOSE, 0, 0) ;
               return 0 ;
             
          }
          break ;

              case WM_CREATE:
                    FirstClick = false;
                    CollectionIndex = 0;

              case WM_LBUTTONDOWN:
                    if (!FirstClick)
                    {
                          xCoordStart = LOWORD(lParam);
                          yCoordStart = HIWORD(lParam);                  
                          Collection[CollectionIndex].SetLineStart(xCoordStart,yCoordStart);
                          hdc = GetDC(hwnd);
                          MoveToEx(hdc,xCoordStart,yCoordStart,NULL);
                          ReleaseDC(hwnd, hdc);
                          FirstClick = true;
                    }
                    else if (FirstClick)
                    {                     
                          //InvalidateRect(hwnd,NULL,TRUE);                        
                          xCoordEnd = LOWORD(lParam);
                          yCoordEnd = HIWORD(lParam);
                          Collection[CollectionIndex].SetLineEnd(xCoordEnd,yCoordEnd);                        
                          Collection[CollectionIndex].DrawObject(hwnd);                        
                          FirstClick = false;
                          CollectionIndex++;
                    }
                   return 0;
              case WM_MOUSEMOVE:
                    if (FirstClick)
                    {
                          int counter;
                          xCoordEnd = LOWORD(lParam);
                          yCoordEnd = HIWORD(lParam);
                          Collection[CollectionIndex].SetLineEnd(xCoordEnd,yCoordEnd);
                          Collection[CollectionIndex].DrawObject(hwnd);                        
                          Sleep(100);
                          InvalidateRect(hwnd,NULL,TRUE);                        
                          for(counter = 0; counter < CollectionIndex; counter++)
                                Collection[counter].DrawObject(hwnd);
                    }
                    else if (!FirstClick)
                    {
                          int counter;
                                                    InvalidateRect(hwnd,NULL,TRUE);                        
                          for(counter = 0; counter < CollectionIndex; counter++)
                                Collection[counter].DrawObject(hwnd);
                    }

      case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}
Avatar of Tom Knowlton
Tom Knowlton
Flag of United States of America image

ASKER

Here is the code for ObjectsDrawn.cpp:

#include "ObjectsDrawn.h"

ObjectsDrawn::ObjectsDrawn()
{
      type = 1;
      
}      

void ObjectsDrawn::SetType(int temptype)
{
      type = temptype;
      
}      

int ObjectsDrawn::GetType()
{
      return type;
}      


void ObjectsDrawn::SetLineStart(int tempX,int tempY)
{
      xCoordStart = tempX;
      yCoordStart = tempY;
}      

void ObjectsDrawn::SetLineEnd(int tempX, int tempY)
{
      xCoordEnd = tempX;
      yCoordEnd = tempY;
}

int ObjectsDrawn::GetLineXStart()
{
  return xCoordStart;
}      

int ObjectsDrawn::GetLineYStart()
{
  return yCoordStart;
}      

int ObjectsDrawn::GetLineXEnd()
{
  return xCoordEnd;
}      

int ObjectsDrawn::GetLineYEnd()
{
  return yCoordEnd;
}      

void ObjectsDrawn::DrawLine(HWND &hwnd)
{
      HDC hdc;
      hdc = GetDC(hwnd);
      MoveToEx(hdc, xCoordEnd, yCoordEnd, NULL);
      LineTo(hdc, xCoordStart, yCoordStart);
      ReleaseDC(hwnd, hdc);
}      

void ObjectsDrawn::DrawObject(HWND &hwnd)
{
      if(type == 1)
            DrawLine(hwnd);
}

Now my questions:

This is how I want the program to function:

1)  Program starts.
2)  User left clicks somewhere in the client area, setting the starting endpoint of the line.
3)  As the user moves the mouse around on the screen, the LINE "rubber-bands" to each new mouse location, showing the user where the line will be drawn to if the mouse is clicked a second time.
4)  When the user clicks a second time, the line is drawn permanently and the information is stored in an array of "Objects".
5)  More lines can be drawn and the old line will still be preserved.

I hope this gives you an idea of what I am trying to do.

Now, as I have run the program, I can tell that the line information for each object is being preserved and all of the lines I have drawn previously are being stored and reproduced on demand during this for loop:

for(counter = 0; counter < CollectionIndex; counter++)
  Collection[counter].DrawObject(hwnd);

But I am refreshing the ENTIRE screen each time I draw.  I want a more elegant solution to all of this but I don't know what to do.

I have thought about passing in an OBJECT COLOR each time I draw the lines on the screen.  I could pass in the COLOR white to "erase" lines and the color black to drawn the lines, but this also seems like a hack for a better way.

I would appreciate any help someone could give me.

Avatar of Wyn
Wyn

Add a "if" condition to lines in WM_MOUSEMOVE:
like:
if(firsttime){  
InvalidateRect(hwnd,NULL,TRUE);
firsttime=false;
}

But better.You need do all the drawing into a mem DC and bitblt() into your window'DC.This will prevent the refreshing.
Regards
W.Yinan
Wyn:

I am rejecting your answer because I want to leave the question open to other comments.  I am sorry I should have explained that I just wanted comments for the time-being.

I understand what you mean about bitblt and the memDC, although I haven't actually tried this in Win32 I have done this type of thing in Delphi.  And yes, it does work quite well.

My one concern is how can I acheive the "rubberband" effect while I am drawing moving the mouse around prior to the second mouse-click?

Thanks!

Tom
Can I still show the line "rubber-banding" while using the memDC and bitblt techniques?

ASKER CERTIFIED SOLUTION
Avatar of Wyn
Wyn

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
How about this?
I will try your suggestions.

This may take me a few hours.  I am still learning this stuff.

Tom
Wyn:

I appreciate your help, BUT I still don't get it.

What I need to do is draw and erase the line rapidly until the use clicks the left mouse button again.

I have made some changes to my code in an effort to use the memDc and BitBlit:

Here is my code as it stands now:

/*-----------------------------------------
   MENUDEMO.C -- Menu Demonstration
                 (c) Charles Petzold, 1998
  -----------------------------------------*/

#include <windows.h>
#include "resource.h"
#include "ObjectsDrawn.h"

int xCoordStart,
      yCoordStart,
      xCoordEnd,
      yCoordEnd,
      CollectionIndex,
      object_type;

const int INVALID = 0;
const int LINE = 1;
const int SQUARE = 2;
const int ELLIPSE = 3;

bool FirstClick;

ObjectsDrawn Collection[100];


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

TCHAR szAppName[] = TEXT ("MenuDemo") ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;
     
     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = szAppName ;
     wndclass.lpszClassName = szAppName ;
     
     if (!RegisterClass (&wndclass))
     {
            MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                                    szAppName, MB_ICONERROR) ;
            return 0 ;
     }
     
     hwnd = CreateWindow (szAppName, TEXT ("Menu Demonstration"),
                                    WS_OVERLAPPEDWINDOW,
                                    CW_USEDEFAULT, CW_USEDEFAULT,
                                    CW_USEDEFAULT, CW_USEDEFAULT,
                                    NULL, NULL, hInstance, NULL) ;

       if (hwnd == NULL)
             {
                  MessageBox(NULL, TEXT("Not enough memory to create bitmap!"),
                        szAppName, MB_ICONERROR);
                  return 0;
             }

     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
            TranslateMessage (&msg) ;
            DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}


void GetLargestDisplayMode (int * pcxBitmap, int * pcyBitmap)
{
     DEVMODE devmode ;
     int     iModeNum = 0 ;

     * pcxBitmap = * pcyBitmap = 0 ;

     ZeroMemory (&devmode, sizeof (DEVMODE)) ;
     devmode.dmSize = sizeof (DEVMODE) ;
     
     while (EnumDisplaySettings (NULL, iModeNum++, &devmode))
     {
          * pcxBitmap = max (* pcxBitmap, (int) devmode.dmPelsWidth) ;
          * pcyBitmap = max (* pcyBitmap, (int) devmode.dmPelsHeight) ;
     }
}


LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static BOOL      fLeftButtonDown, fRightButtonDown;
       static HBITMAP      hBitmap;
       static HDC            hdcMem;
       static int            cxBitmap, cyBitmap, cxClient, cyClient, xMouse, yMouse;
       HMENU                  hMenu ;
     HDC                  hdc;
       PAINTSTRUCT ps;

     switch (message)
     {
            case WM_COMMAND:
                  hMenu = GetMenu (hwnd) ;
                  switch (LOWORD (wParam))
                        {
                              case IDM_CLEAR:
                                    return 0 ;              
                              case IDM_APP_EXIT:
                                    SendMessage (hwnd, WM_CLOSE, 0, 0) ;
                                    return 0 ;
                              case IDM_EDIT_DRAWLINE:
                                    int iFlag;
                                    iFlag = GetMenuState(hMenu,IDM_EDIT_DRAWLINE,MF_BYCOMMAND);
                                    if(iFlag == 0)
                                          {
                                                CheckMenuItem(hMenu,IDM_EDIT_DRAWLINE,MF_CHECKED);
                                                CheckMenuItem(hMenu,IDM_EDIT_DRAWCIRCLE,MF_UNCHECKED);
                                                CheckMenuItem(hMenu,IDM_EDIT_DRAWRECT,MF_UNCHECKED);
                                                object_type = LINE;
                                          }
                                    return 0;
                              case IDM_EDIT_DRAWCIRCLE:
                                    iFlag = GetMenuState(hMenu,IDM_EDIT_DRAWCIRCLE,MF_BYCOMMAND);
                                    if(iFlag == 0)
                                          {
                                                CheckMenuItem(hMenu,IDM_EDIT_DRAWCIRCLE,MF_CHECKED);
                                                CheckMenuItem(hMenu,IDM_EDIT_DRAWLINE,MF_UNCHECKED);
                                                CheckMenuItem(hMenu,IDM_EDIT_DRAWRECT,MF_UNCHECKED);
                                                object_type = LINE;
                                          }
                                    return 0;
                              case IDM_EDIT_DRAWRECT:
                                    iFlag = GetMenuState(hMenu,IDM_EDIT_DRAWRECT,MF_BYCOMMAND);
                                    if(iFlag == 0)
                                          {
                                                CheckMenuItem(hMenu,IDM_EDIT_DRAWRECT,MF_CHECKED);
                                                CheckMenuItem(hMenu,IDM_EDIT_DRAWLINE,MF_UNCHECKED);
                                                CheckMenuItem(hMenu,IDM_EDIT_DRAWCIRCLE,MF_UNCHECKED);
                                                object_type = SQUARE;
                                          }
                                    return 0;

                              case IDM_APP_HELP:
                                    MessageBox (hwnd, TEXT ("Help not yet implemented!"),
                                          szAppName, MB_ICONEXCLAMATION | MB_OK) ;
                                    return 0 ;
               
                              case IDM_APP_ABOUT:
                                    MessageBox (hwnd, TEXT ("Menu Demonstration Program\n")
                                          TEXT ("(c) Charles Petzold, 1998"),
                                          szAppName, MB_ICONINFORMATION | MB_OK) ;
                                    return 0 ;
                        }
                  break ;

                  case WM_CREATE:
                        object_type = LINE;
                        GetLargestDisplayMode (&cxBitmap, &cyBitmap);
                        hdc = GetDC(hwnd);
                        hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap);
                        hdcMem = CreateCompatibleDC(hdc);
                        ReleaseDC(hwnd, hdc);

                        if (!hBitmap)
                              {
                              DeleteDC(hdcMem);
                              return - 1;
                              }
                        SelectObject(hdcMem, hBitmap);
                        PatBlt(hdcMem, 0, 0, cxBitmap, cyBitmap, WHITENESS);
                        return 0;

                  case WM_SIZE:
                        cxClient = LOWORD(lParam);
                        cyClient = HIWORD(lParam);
                        return 0;


                  case WM_LBUTTONDOWN:
                        if(!fRightButtonDown)
                              SetCapture(hwnd);
                        if(!Collection[CollectionIndex].GetFirstCoordsStatus())
                              {
                                    xMouse = LOWORD (lParam);
                                    yMouse = HIWORD (lParam);
                                    Collection[CollectionIndex].SetLineStart(xMouse,yMouse);
                                    Collection[CollectionIndex].SetFirstCoordsStatus(true);
                                    MoveToEx(hdc,xMouse,yMouse,NULL);
                                    MoveToEx(hdcMem,xMouse,yMouse,NULL);                                    
                              }
                        else
                              {
                                    xMouse = LOWORD (lParam);
                                    yMouse = HIWORD (lParam);
                                    MoveToEx(hdc,xMouse,yMouse,NULL);
                                    MoveToEx(hdcMem,xMouse,yMouse,NULL);                                    
                                    Collection[CollectionIndex].SetLineEnd(xMouse,yMouse);
                                    Collection[CollectionIndex].SetFirstCoordsStatus(false);
                                    Collection[CollectionIndex].DrawObject(hwnd);
                                    CollectionIndex++;
                              }

                        return 0;

                  case WM_LBUTTONUP:
                        if (fLeftButtonDown)
                              SetCapture(NULL);
                        fLeftButtonDown = FALSE;
                        return 0;

                  case WM_RBUTTONDOWN:
                        if (!fLeftButtonDown)
                              SetCapture(hwnd);
                        xMouse = LOWORD(lParam);
                        yMouse = HIWORD(lParam);
                        fRightButtonDown = TRUE;
                        return 0;

                  case WM_RBUTTONUP:
                        if(fRightButtonDown)
                              SetCapture(NULL);
                        fRightButtonDown = FALSE;
                        return 0;

                  case WM_MOUSEMOVE:
                        if(Collection[CollectionIndex].GetFirstCoordsStatus())
                              {
                                    hdc = GetDC(hwnd);
                                    SelectObject(hdc,GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
                                    SelectObject(hdcMem,GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
                                    xMouse = (short) LOWORD(lParam);
                                    yMouse = (short) HIWORD(lParam);
                                    Collection[CollectionIndex].SetLineEnd(xMouse,yMouse);
                                    Collection[CollectionIndex].DrawObject(hwnd);
                                    ReleaseDC(hwnd, hdc);
                              }
                        if(!fLeftButtonDown && !fRightButtonDown)
                              return 0;
                        
                        return 0;

                  case WM_PAINT:
                        hdc = BeginPaint(hwnd, &ps);
                        int result;
                        //PatBlt(hdc, 0, 0, cxBitmap, cyBitmap, WHITENESS);
                        BitBlt(hdc, 0, 0, cxClient, cyClient, hdcMem, 0,0, SRCCOPY);
                        EndPaint(hwnd, &ps);
                        return 0;

                  case WM_DESTROY:
                        DeleteDC(hdcMem);
                        DeleteObject(hBitmap);
                        PostQuitMessage (0) ;
                        return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}
Wyn:

Here is my new ObjectsDrawn class:

#include "ObjectsDrawn.h"

ObjectsDrawn::ObjectsDrawn()
{
      type = 1;
      
}      

void ObjectsDrawn::SetType(int temptype)
{
      type = temptype;
      StartCoordsSet = false;
      
}      

int ObjectsDrawn::GetType()
{
      return type;
}      

bool ObjectsDrawn::GetFirstCoordsStatus()
{
      return StartCoordsSet;
}      

void ObjectsDrawn::SetFirstCoordsStatus(bool tempstatus)
{
      StartCoordsSet = tempstatus;
}      

void ObjectsDrawn::SetLineStart(int tempX,int tempY)
{
      xCoordStart = tempX;
      yCoordStart = tempY;
}      

void ObjectsDrawn::SetLineEnd(int tempX, int tempY)
{
      xCoordEnd = tempX;
      yCoordEnd = tempY;
}

int ObjectsDrawn::GetLineXStart()
{
  return xCoordStart;
}      

int ObjectsDrawn::GetLineYStart()
{
  return yCoordStart;
}      

int ObjectsDrawn::GetLineXEnd()
{
  return xCoordEnd;
}      

int ObjectsDrawn::GetLineYEnd()
{
  return yCoordEnd;
}      

void ObjectsDrawn::DrawLine(HWND &hwnd)
{
      HDC hdc;
      hdc = GetDC(hwnd);
      MoveToEx(hdc, xCoordEnd, yCoordEnd, NULL);
      LineTo(hdc, xCoordStart, yCoordStart);
      ReleaseDC(hwnd, hdc);
}      

void ObjectsDrawn::DrawObject(HWND &hwnd)
{
      if(type == 1)
            DrawLine(hwnd);
}
Okay.

Now what my program does is this:

I left click with my mouse.  This sets the START coordiates of the line.

As I move my mouse around the screen AFTER this first mouse click, a line is continuously REDRAWN from the START to the new mouse location.

When I click the mouse a second time, the final line is drawn and the entire process is reset.  The only PROBLEM is ALL of the previous lines are still there on the screen!  I want those extra lines to disappear as I move the mouse around until finally I left click again and the final (and ONLY) line is then drawn on the screen.

Can you explain your BitBlit code in more detail.  Explain each line to me, please:


YOU WROTE PREVIOUSLY:

change the code around DrawObject,for example:
HDC memdc;
RECT rect;
HBITMAP  bitmap;
  case WM_MOUSEMOVE:
  if (FirstClick)
  {
memdc=CreateCompatibleDC(0);
GetWindowRect(hwnd,&rect) ;
bitmap=CreateCompatibleBitmap(memdc,rect.rigth-rect.left,rect.bottom-rect.top);
HBITMAP oldbitmap=SelectObject(memdc,bitmap);
  for(counter = 0; counter < CollectionIndex; counter++)
  Collection[counter].DrawObject(memdc);
BltBit(....);
DeleteObject(SelectObjet(memdc,oldbitmap));
DeleteDC(memdc);
}
//.........


You should rewrite the DrawLine function,pass the memdc into it:
void ObjectsDrawn::DrawLine(HDC hdc)
{
MoveToEx(hdc, xCoordEnd, yCoordEnd, NULL);
LineTo(hdc, xCoordStart, yCoordStart);
}

============
How does this code allow me to "rubberband" the line around the screen as i move the mouse?  Do you understand what I mean by rubberbanding?

Thanks for your help!  I think with a little more explanation I can get this!

Tom
Wyn:

I think I finally figured it out!

Here is my current WM_MOUSEMOVE:

case WM_MOUSEMOVE:
                        if(Collection[CollectionIndex].GetFirstCoordsStatus())
                              {
                                    int counter;
                                    hdc = GetDC(hwnd);
                                    SelectObject(hdc,GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
                                    SelectObject(hdcMem,GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
                                    xMouse = (short) LOWORD(lParam);
                                    yMouse = (short) HIWORD(lParam);
                                    Collection[CollectionIndex].SetLineEnd(xMouse,yMouse);
                                    PatBlt(hdc, 0, 0, cxBitmap, cyBitmap, WHITENESS);
                                    for(counter = 0; counter < CollectionIndex; counter++)
                                          Collection[counter].DrawObject(hwnd);
                                    Collection[CollectionIndex].DrawObject(hwnd);
                                    ReleaseDC(hwnd, hdc);
                              }
                        if(!fLeftButtonDown && !fRightButtonDown)
                              return 0;
                        
                        return 0;


There is a tiny bit of flicker, but the line rubber bands now!  

Thanks for your help.  You get the points!

Why you give me the points?
Is my comments helpful?
However,thank you.
Best Regards
W.Yinan

Btw:what's rubberband exactly meaning?
(You told me and ,then,I can rehash the my code and try to explain it to you)

Thank for the pts ,i blush to get:)
Rubberbanding is like when you take a rubberband and stretch it between your fingers.  You can make it bigger or smaller but the basic object is still a rubberband.

When drawing a rectangle in a drawing program, rubberbanding refers to clicking with the mouse at a location, then dragging the mouse around the screen.  The rectangle grows bigger as you move the cursor away from the origin point, and smaller as you approach it, but the rectangle does not get permanently drawn until you click the mouse again.  Make sense?

Don't worry about the points.  You actually were quite helpful with your bitblt idea.