Click here to Skip to main content
15,893,594 members
Articles / Desktop Programming / MFC

Self-Registering Windows Classes

Rate me:
Please Sign up or sign in to vote.
4.70/5 (7 votes)
24 Oct 20017 min read 88.2K   1.2K   38  
A class that automatically registers its own Window class.
// Compass.cpp : implementation file
//

#include "stdafx.h"
#include "resource.h"
#include "DoublePoint.h"
#include "Polygon.h"
#include "Compass.h"
#include "SaveDC.h"
#include "DtoR.h"
#include "msg.h"
#include "RegVars.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

DECLARE_MESSAGE(CPM_SET_ANGLE)

/////////////////////////////////////////////////////////////////////////////
// CCompass

CCompass::CCompass()
{
 // Note: for optimal performance, sort monotonically by font size
 // Note: The first entry must be the largest 
 // The largest size is 100.0, which is the base size used
 // for computing the offset circle
 display.Add(new displayinfo(0.0, _T("N"), 100.0, TRUE));
 display.Add(new displayinfo( 90.0, _T("E"), 90.0, FALSE));
 display.Add(new displayinfo(180.0, _T("S"), 90.0, FALSE));
 display.Add(new displayinfo(270.0, _T("W"), 90.0, FALSE));
 display.Add(new displayinfo( 45.0, _T("NE"), 80.0, FALSE));
 display.Add(new displayinfo(135.0, _T("SE"), 80.0, FALSE));
 display.Add(new displayinfo(225.0, _T("SW"), 80.0, FALSE));
 display.Add(new displayinfo(315.0, _T("NW"), 80.0, FALSE));

 if(!arrow.Load(_T("IDP_ARROW")))
    { /* try disk file */
     RegistryString compass(IDS_COMPASS);
     compass.load();
     if(compass.value.GetLength() == 0 || !arrow.Read(compass.value))
	arrow.Read(_T("Arrow.pln")); // use default
    } /* try disk file */

 angle = 0.0; // initialize at North
 ArrowVisible = FALSE;
}

CCompass::~CCompass()
{
}


/****************************************************************************
*			      CCompass::RegisterMe
* Result: BOOL
*       TRUE if successful
*	FALSE if error
* Effect: 
*       Registers the class. Note that this is called as a consequence of
*	initializing a static variable (sneaky C++)
****************************************************************************/
BOOL CCompass::hasclass = CCompass::RegisterMe();

BOOL CCompass::RegisterMe()
   {                       
    WNDCLASS wc;           
    wc.style = 0;          
    wc.lpfnWndProc = ::DefWindowProc; // must be this value       
    wc.cbClsExtra = 0;                                            
    wc.cbWndExtra = 0;                                            
    wc.hInstance = (HINSTANCE)GetModuleHandle(NULL);              
    wc.hIcon = NULL;     // child window has no icon              
    wc.hCursor = NULL;   // we use OnSetCursor                    
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);               
    wc.lpszMenuName = NULL;  // no menu                           
    wc.lpszClassName = COMPASS_CLASS_NAME;                       
    return AfxRegisterClass(&wc);                                 
   } // CCompass::RegisterMe                                     

BEGIN_MESSAGE_MAP(CCompass, CWnd)
	//{{AFX_MSG_MAP(CCompass)
        ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_ENABLE()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CCompass message handlers

/****************************************************************************
*                           CCompass::CreateRegion
* Inputs:
*       CRgn & rgn: Region to be initialized
* Result: CRect
*       Bounding box of the region
* Effect: 
*       Creates a circular region within the control
****************************************************************************/

CRect CCompass::CreateClipRegion(CRgn & rgn)
    {
     CRect r;
     GetClientRect(&r);
     int radius = min(r.Width() / 2, r.Height() / 2);
     CPoint center(r.Width() / 2, r.Height() / 2);
     rgn.CreateEllipticRgn(center.x - radius, center.y - radius,
			   center.x + radius, center.y + radius);
     return CRect(center.x - radius, center.y - radius,
		  center.x + radius, center.y + radius);
    } // CCompass::CreateClipRegion

/****************************************************************************
*                           CCompass::OnEraseBkgnd
* Inputs:
*       CDC * pDC: DC
* Result: BOOL
*       TRUE if erased
*	FALSE if not erased
* Effect: 
*       Erases the background outside the clipping region
****************************************************************************/

BOOL CCompass::OnEraseBkgnd(CDC* pDC) 
   {
    CRgn rgn;
    CSaveDC sdc(pDC);
    CreateClipRegion(rgn);
    pDC->SelectClipRgn(&rgn, RGN_DIFF); // remove circle from update area
    return CWnd::OnEraseBkgnd(pDC);
   }

/****************************************************************************
*                               CCompass::MapDC
* Inputs:
*       CDC & dc: DC
* Result: void
*       
* Effect: 
*       Maps the DC to the isotropic coordinates used for the compass
****************************************************************************/

void CCompass::MapDC(CDC & dc)
    {
     dc.SetMapMode(MM_ISOTROPIC);
     CRect r;
     GetClientRect(&r);
     dc.SetWindowExt(r.Width(), r.Height());
     dc.SetViewportExt(r.Width(), -r.Height());
     CPoint center(r.left + r.Width() / 2, r.top + r.Height() / 2);
     dc.SetViewportOrg(center.x, center.y);
    } // CCompass::MapDC

/****************************************************************************
*                              CCompass::OnPaint
* Result: void
*       
* Effect: 
*       Paints the compass
****************************************************************************/

void CCompass::OnPaint() 
   {
    CPaintDC dc(this); // device context for painting
    CBrush br(::GetSysColor(COLOR_INFOBK));
    CRgn rgn;
    CRect r;
    r = CreateClipRegion(rgn);
#define BORDER_WIDTH 2
    CPen border(PS_SOLID, BORDER_WIDTH, RGB(0,0,0));
    CBrush needle(RGB(255, 0, 0));

#define ENABLED_COLOR RGB(0,0,0)
#define DISABLED_COLOR RGB(128,128,128)

    CPen enabledPen(PS_SOLID, 0, ENABLED_COLOR);
    CPen disabledPen(PS_SOLID, 0, DISABLED_COLOR);
    //----------------------------------------------------------------
    // GDI resources must be declared above this line
    //----------------------------------------------------------------
    CSaveDC sdc(dc);
    dc.SelectClipRgn(&rgn); // clip to compass
    dc.FillRgn(&rgn, &br);
    // Convert the origin to the center of the circle
    CPoint center(r.left + r.Width() / 2, r.top + r.Height() / 2);
    // Renormalize the rectangle to the center of the circle
    r -= center;
    int radius = r.Width() / 2;
    dc.SetBkMode(TRANSPARENT);
    MapDC(dc);

    // Draw the borders
    { 
     CSaveDC sdc2(dc);
     dc.SelectClipRgn(NULL);
     dc.SelectStockObject(HOLLOW_BRUSH);

     dc.SelectObject(&border);
     dc.Ellipse(-radius, -radius, radius, radius);

     r.InflateRect(-BORDER_WIDTH, -BORDER_WIDTH);
     radius = r.Width() / 2;
    }
    radius = r.Width() / 2;
    
    dc.SelectObject(IsWindowEnabled() ? &enabledPen : &disabledPen);
    // Draw N-S line
    dc.MoveTo(0, radius);
    dc.LineTo(0, -radius);
    // Draw E-W line
    dc.MoveTo(-radius, 0);
    dc.LineTo(radius, 0);

    // Draw SW-NE line
    dc.MoveTo((int)(radius * sin(DegreesToRadians(GeographicToGeometric(225.0)))), 
	      (int)(radius * cos(DegreesToRadians(GeographicToGeometric(225.0)))) );
    dc.LineTo((int)(radius * sin(DegreesToRadians(GeographicToGeometric( 45.0)))),
	      (int)(radius * cos(DegreesToRadians(GeographicToGeometric( 45.0)))) );
    // Draw NW-SE line
    dc.MoveTo((int)(radius * sin(DegreesToRadians(GeographicToGeometric(315.0)))), 
	      (int)(radius * cos(DegreesToRadians(GeographicToGeometric(315.0)))) );
    dc.LineTo((int)(radius * sin(DegreesToRadians(GeographicToGeometric(135.0)))),
	      (int)(radius * cos(DegreesToRadians(GeographicToGeometric(135.0)))) );

    // Now create the font elements
    // The symbols are placed along a circle which is inscribed
    // within the compass area
    //
    // +-----------------------------+
    //         /     N     \
    //        / NW   |   NE \
    //       /       |       \
    //       |       |       |
    //       | W-----+-----E |
    //       |       |       |
    //       \       |       /
    //        \ SW   |   SE /
    //         \     S     /
    // +-----------------------------+

    double size = 0.15 * (double)r.Width();
    double CurrentFontSize = 0.0; // current font size
    CFont * f = NULL;
    dc.SetTextColor(IsWindowEnabled() ? ENABLED_COLOR : DISABLED_COLOR);

    innerRadius = radius;
    for(int i = 0; i < display.GetSize(); i++)
       { /* draw points */
	CSaveDC sdc2(dc);
	dc.SetBkMode(OPAQUE);
	dc.SetBkColor(::GetSysColor(COLOR_INFOBK));
	if(display[i]->GetSize() != CurrentFontSize)
	   { /* new font */
	    if(f != NULL)
	       delete f;
	    f = display[i]->CreateFont(size, _T("Times New Roman"));
	   } /* new font */
	dc.SelectObject(f);
	CurrentFontSize = display[i]->GetSize();
	CString text = display[i]->GetText();
	//
	//      4 | 1
	//      --+--
	//      3 | 2
	//------------------------------------------------------------------
	//  �    qdant    x     y   x-origin     y-origin        alignment
	//  ----------------------------------------------------------------
	//  0    4.1      0    >0   x-w/2        y               TOP, LEFT
	//  <90  1       >0    >0   x            y               TOP, RIGHT
	//  90   1.2     >0    0    x            y-h/2           TOP, RIGHT
	//  <180 2       >0    <0   x            y               BOTTOM, RIGHT
	//  180  2.3     0     <0   x-w/2        y               BOTTOM, RIGHT
	//  <270 3       <0    <0   x            y               BOTTOM, LEFT
	//  270  3.4     <0    0    x            y-h/2           TOP, LEFT
	//  <360 4       <0    >0   x            y               TOP, LEFT

	//text.Format(_T("%.0f"), display[i]->GetAngle());
	int x = (int)(radius * cos(DegreesToRadians(GeographicToGeometric(display[i]->GetAngle()))));
	int y = (int)(radius * sin(DegreesToRadians(GeographicToGeometric(display[i]->GetAngle()))));
        //text.Format(_T("%d,%d"), x, y);
	CSize textSize = dc.GetTextExtent(text);

	double theta = display[i]->GetAngle();
	if(theta == 0.0)
	   { /* 0 */
	    dc.SetTextAlign(TA_TOP | TA_LEFT);
	    x -= textSize.cx / 2;
	    innerRadius = radius - textSize.cy;
	   } /* 0 */
	else
	if(theta < 90.0)
	   { /* < 90 */
	    dc.SetTextAlign(TA_TOP | TA_RIGHT);
	   } /* < 90 */
	else
	if(theta == 90.0)
	   { /* 90 */
	    dc.SetTextAlign(TA_TOP | TA_RIGHT);
	    y += textSize.cy / 2;
	   } /* 90 */
	else
	if(theta < 180.0)
	   { /* < 180 */
	    dc.SetTextAlign(TA_BOTTOM | TA_RIGHT);
	   } /* < 180 */
	else
	if(theta == 180.0)
	   { /* 180 */
	    dc.SetTextAlign(TA_BOTTOM | TA_LEFT);
	    x -= textSize.cx / 2;
	   } /* 180 */
	else
	if(theta < 270.0)
	   { /* < 270 */
	    dc.SetTextAlign(TA_BOTTOM | TA_LEFT);
	   } /* < 270 */
	else
	if(theta == 270)
	   { /* 270 */
	    dc.SetTextAlign(TA_TOP | TA_LEFT);
	    y += textSize.cy / 2;
	   } /* 270 */
	else
	   { /* < 360 */
	    dc.SetTextAlign(TA_TOP | TA_LEFT);
	   } /* < 360 */
	dc.TextOut(x, y, text);
       } /* draw points */
    if(f != NULL)
       delete f;

    // Draw the arrow
    if(IsWindowEnabled() && ArrowVisible)
       { /* draw arrow */
	CRect bb = arrow.GetInputBB();
	dc.SelectObject(&needle);
	arrow.Transform(angle, (double)abs(bb.Height()) / (2.0 * (double)radius));
	arrow.Draw(dc, CDoublePoint(0.0, 0.0));
       } /* draw arrow */

    // Do not call CWnd::OnPaint() for painting messages
   }

/****************************************************************************
*                             CCompass::SetAngle
* Inputs:
*       double direction: Desired angle
* Result: void
*       
* Effect: 
*       Sets the angle of the arrow to the specified angle
****************************************************************************/

void CCompass::SetAngle(double direction)
    {
     if(angle == direction) // works only because angles are integers converted to floats
	return; 
     CRect r;
     GetClientRect(&r);
     int radius = r.Width() / 2;
     angle = direction;

     // The reason for doing all this work, instead of a simple
     // InvalidateRect(NULL), is that this reduces flicker
     CClientDC dc(this);
     MapDC(dc);
     CRgn inner;
     CRect rr(-innerRadius, -innerRadius,
	     innerRadius, innerRadius);
     dc.LPtoDP(&rr);
     rr.NormalizeRect();
     inner.CreateEllipticRgn(rr.left, rr.top, rr.right, rr.bottom);
     InvalidateRgn(&inner);
    } // CCompass::SetAngle

/****************************************************************************
*                               CCompass::SetShow
* Inputs:
*       BOOL mode: TRUE to show, FALSE to hide
* Result: void
*       
* Effect: 
*       Shows/hides the arrow
****************************************************************************/

void CCompass::SetShow(BOOL mode)
    {
     if(mode == ArrowVisible)
	return;
     ArrowVisible = mode;
     //PTR<CRgn> rgn (arrow.GetRgn());
     //InvalidateRgn(rgn);
     InvalidateRect(NULL);
    } // CCompass::SetShow

/****************************************************************************
*                           CCompass::NotifyParent
* Inputs:
*       CPoint point: Point of mouse
* Result: void
*       
* Effect: 
*       Notifies the parent of the mouse position
****************************************************************************/

void CCompass::NotifyParent(CPoint point)
    {
     CClientDC dc(this);
     MapDC(dc);
     dc.DPtoLP(&point);
     GetParent()->SendMessage(CPM_SET_ANGLE, (WPARAM)point.x, (LPARAM)point.y);
    } // CCompass::NotifyParent

/****************************************************************************
*                           CCompass::OnLButtonDown
* Inputs:
*       UINT nFlags:
*	CPoint point: Point in client coordinates
* Result: void
*       
* Effect: 
*       Notifies the parent of the x,y in mapped coordinates
****************************************************************************/

void CCompass::OnLButtonDown(UINT nFlags, CPoint point) 
   {
    CRgn rgn;
    CreateClipRegion(rgn);
    if(rgn.PtInRegion(point))
       { /* in region */
	SetCapture();
	NotifyParent(point);
	return;
       } /* in region */
    CWnd::OnLButtonDown(nFlags, point);
   }

/****************************************************************************
*                             CCompass::OnEnable
* Inputs:
*       BOOL bEnable: TRUE if enabling, FALSE if disabling
* Result: void
*       
* Effect: 
*       Forces update so window will be redrawn in the correct mode
****************************************************************************/

void CCompass::OnEnable(BOOL bEnable) 
   {
    CWnd::OnEnable(bEnable);
    InvalidateRect(NULL);	
   }

/****************************************************************************
*                            CCompass::OnMouseMove
* Inputs:
*       UINT nFlags: flags, ignored unless passed to superclass
*	CPoint point: Point at which mouse is clicked
* Result: void
*       
* Effect: 
*       Notifies the parent the mouse has moved if there is capture
****************************************************************************/

void CCompass::OnMouseMove(UINT nFlags, CPoint point) 
   {
    if(GetCapture() != NULL)
       { /* notify parent */
	NotifyParent(point);
	return;
       } /* notify parent */
	
    CWnd::OnMouseMove(nFlags, point);
   }

/****************************************************************************
*                            CCompass::OnLButtonUp
* Inputs:
*       UINT nFlags: ignored except when passed to superclass
*	CPoint point: Mouse position
* Result: void
*       
* Effect: 
*       Notifies the parent of the change and release capture
****************************************************************************/

void CCompass::OnLButtonUp(UINT nFlags, CPoint point) 
   {
    if(GetCapture() != NULL)
       { /* notify */
	NotifyParent(point);
	ReleaseCapture();
       } /* notify */
    CWnd::OnLButtonUp(nFlags, point);
   }

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Retired
United States United States
PhD, Computer Science, Carnegie Mellon University, 1975
Certificate in Forensic Science and the Law, Duquesne University, 2008

Co-Author, [i]Win32 Programming[/i]

Comments and Discussions