Superclassed Edit Control
//=============================================================================
//SUPERCLASSED EDIT CONTROL - Copyright © 2003,2005 Ken Fitlike
//=============================================================================
//API functions used: CallWindowProc,ChooseFont,CreateFontIndirect,
//CreateWindowEx,DefWindowProc,DeleteObject,DispatchMessage,FreeLibrary,
//GetMessage,GetClassInfoEx,GetProcAddress,GetStockObject,GetSystemMetrics,
//LoadImage,LoadLibrary,MessageBox,PostQuitMessage,RegisterClassEx,SendMessage,
//SetFocus,SetWindowTheme,ShowWindow,UpdateWindow,TranslateMessage,WinMain.
//=============================================================================
//Demonstrates window superclassing. An edit control is superclassed to give
//extra functionality in the form of an additional button that enables font
//changes to be made. 
//
//MINGW    - Use of the -fomit-frame-pointer compiler switch will generate
//           an exe that will crash under winxp.
//MSVC2003 - Compile with multi-threaded libs (/MT switch)
//=============================================================================
#include <windows.h>  //include all the basics
#include <tchar.h>    //string and other mapping macros
#include <commdlg.h>  //for common dialogs
#include <string>

//define an unicode string type alias
typedef std::basic_string<TCHAR> ustring;
//=============================================================================
//main window message processsing function declarations
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int OnCreate(const HWND,CREATESTRUCT*);
void OnParentNotify(const HWND,const UINT,const HWND);

//superclassed edit control message processing function declarations
LRESULT CALLBACK EditProc(HWND,UINT,WPARAM,LPARAM);
void OnCommandEdit(const HWND,int,int,const HWND);
int OnCreateEdit(const HWND,CREATESTRUCT*);

//non-message function declarations
HFONT ChangeFont(const HWND);
HWND CreateEdit(const HWND,const HINSTANCE,DWORD,const RECT&,const int,
                const ustring&);                
inline int ErrMsg(const ustring&);
void SetFormattingRect(const HWND);

//setup some edit control id's
enum {
  IDC_SUPERCLASSED_EDIT=200
};

namespace {
  WNDPROC wpOldProc;
}
//=============================================================================
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int nCmd)
{
ustring classname=_T("SIMPLEWND");
WNDCLASSEX wcx={0};  //used for storing information about the wnd 'class'

wcx.cbSize         = sizeof(WNDCLASSEX);           
wcx.lpfnWndProc    = WndProc;             //wnd Procedure pointer
wcx.hInstance      = hInst;               //app instance
//use 'LoadImage' to load wnd class icon and cursor as it supersedes the 
//obsolete functions 'LoadIcon' and 'LoadCursor', although these functions will 
//still work. Because the icon and cursor are loaded from system resources ie 
//they are shared, it is not necessary to free the image resources with either 
//'DestroyIcon' or 'DestroyCursor'.
wcx.hIcon         = reinterpret_cast<HICON>(LoadImage(0,IDI_APPLICATION,
                                            IMAGE_ICON,0,0,LR_SHARED));
wcx.hCursor       = reinterpret_cast<HCURSOR>(LoadImage(0,IDC_ARROW,
                                              IMAGE_CURSOR,0,0,LR_SHARED));
wcx.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BTNFACE+1);   
wcx.lpszClassName = classname.c_str(); 
//the window 'class' (not c++ class) has to be registered with the system
//before windows of that 'class' can be created
if (!RegisterClassEx(&wcx))
  {
  ErrMsg(_T("Failed to register wnd class"));
  return -1;
  }

int desktopwidth=GetSystemMetrics(SM_CXSCREEN);
int desktopheight=GetSystemMetrics(SM_CYSCREEN);

HWND hwnd=CreateWindowEx(0,                     //extended styles
                         classname.c_str(),     //name: wnd 'class'
                         _T("Superclassed Window - Edit"),     //wnd title
                         WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,  //wnd style
                         desktopwidth/4,        //position:left
                         desktopheight/4,       //position: top
                         desktopwidth/2,        //width
                         desktopheight/2,       //height
                         0,                     //parent wnd handle
                         0,                     //menu handle/wnd id
                         hInst,                 //app instance
                         0);                    //user defined info
if (!hwnd)
  {
  ErrMsg(_T("Failed to create wnd"));
  return -1;
  }

ShowWindow(hwnd,nCmd); 
UpdateWindow(hwnd);
//start message loop - windows applications are 'event driven' waiting on user,
//application or system signals to determine what action, if any, to take. Note 
//that an error may cause GetMessage to return a negative value so, ideally,  
//this result should be tested for and appropriate action taken to deal with 
//it(the approach taken here is to simply quit the application).
MSG msg;
while (GetMessage(&msg,0,0,0)>0)
  {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
  }
return static_cast<int>(msg.wParam);
}
//=============================================================================
//main window message processing functions
//=============================================================================
LRESULT CALLBACK WndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
switch (uMsg)
  {
  case WM_PARENTNOTIFY:
    {
    OnParentNotify(hwnd,LOWORD(wParam),reinterpret_cast<HWND>(lParam));
    return 0;
    }
  case WM_CREATE:
    return OnCreate(hwnd,reinterpret_cast<CREATESTRUCT*>(lParam));
  case WM_DESTROY:
    PostQuitMessage(0);    //signal end of application
    return 0;
  default:
    //let system deal with msg
    return DefWindowProc(hwnd,uMsg,wParam,lParam);  
  }
}
//=============================================================================
int OnCreate(const HWND hwnd,CREATESTRUCT *cs)
{
//handles the WM_CREATE message of the main, parent window; return -1 to fail
//window creation
RECT rc={60,10,200,70};
//the various edit control types are created by simply varying the style bits
CreateEdit(hwnd,cs->hInstance,ES_MULTILINE|WS_VSCROLL|WS_CLIPCHILDREN,rc,
           IDC_SUPERCLASSED_EDIT,_T("Superclassed Edit"));

return 0;
}
//=============================================================================
void OnParentNotify(const HWND hwnd,const UINT uMsg,const HWND hChild)
{
//handles parent window's WM_PARENTNOTIFY message
if (uMsg==WM_CREATE)
  {
  //attempting to set the formatting rectangle in the superclassed edit
  //control's own WM_CREATE handler fails so do it here instead. Note that the 
  //superclassed edit control's WM_CREATE message is issued prior to the 
  //parent's WM_PARENTNOTIFY message by the system.
  SetFormattingRect(hChild);
  }
}
//=============================================================================
//superclassed edit control message functions
//=============================================================================
LRESULT CALLBACK EditProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
switch (uMsg)
  {
  case WM_COMMAND:
    CallWindowProc(wpOldProc,hwnd,uMsg,wParam,lParam);
    OnCommandEdit(hwnd,LOWORD(wParam),HIWORD(wParam),
                  reinterpret_cast<HWND>(lParam));
    return 0;
  case WM_CREATE:
    //get default creation first
    CallWindowProc(wpOldProc,hwnd,uMsg,wParam,lParam);
    return OnCreateEdit(hwnd,reinterpret_cast<CREATESTRUCT*>(lParam));
  default:
    //pass the message to system handler
    return CallWindowProc(wpOldProc,hwnd,uMsg,wParam,lParam);
  }
}
//=============================================================================
void OnCommandEdit(const HWND hwnd,int id,int nNotify,const HWND hChild)
{
//handles WM_COMMAND message of the superclassed edit control
if (hChild && nNotify==BN_CLICKED)
  {
  HFONT hFont=ChangeFont(hwnd);
  if (hFont)
    {
    //change the superclassed edit control font and destroy the existing one
    DeleteObject(reinterpret_cast<HFONT>(SendMessage(hwnd,WM_SETFONT,
                   reinterpret_cast<WPARAM>(hFont),0)));
    SetFormattingRect(hwnd);
    }
  //restore focus to parent superclassed edit control
  SetFocus(hwnd);
  }
}
//=============================================================================
int OnCreateEdit(const HWND hwnd,CREATESTRUCT *cs)
{
//handles the WM_CREATE message of the superclassed edit control; return -1 to 
//fail window creation

//change to gui font
SendMessage(hwnd,WM_SETFONT,
            reinterpret_cast<WPARAM>(GetStockObject(DEFAULT_GUI_FONT)),0);
//create a small button and place it in top-left corner of edit control
HWND hBtn=CreateWindowEx(0,_T("button"),0,
                         WS_CHILD|WS_VISIBLE,
                         0,0,10,10,
                         hwnd,
                         0,
                         cs->hInstance,0);
                         
//if winxp themes are used the button will be obscured so turn off themes for
//button control but use dynamic linking so that code still works with 
//pre-winxp systems                          
HINSTANCE hLib=LoadLibrary(_T("UxTheme.dll"));
if (hLib)
  {
  typedef HRESULT (__stdcall *dllSetWindowTheme)(HWND,LPCWSTR,LPCWSTR);
  dllSetWindowTheme SetWindowTheme=(dllSetWindowTheme)GetProcAddress(hLib,
    "SetWindowTheme");
  SetWindowTheme(hBtn,L" ",L" ");
  FreeLibrary(hLib);
  }

//select all the text in the edit control
SendMessage(hwnd,EM_SETSEL,0,static_cast<LPARAM>(-1));
SetFocus(hwnd);
                        
return 0;
}
//=============================================================================
//non-message functions
//=============================================================================
HFONT ChangeFont(const HWND hwnd)
{
//display font common dialog and return any created font based on user 
//selection
CHOOSEFONT cf={0};
LOGFONT lf={0};

cf.lStructSize=sizeof(CHOOSEFONT);
cf.hwndOwner=hwnd;
cf.lpLogFont=&lf;
cf.Flags=CF_SCREENFONTS;
//display the font common dialog box
if (ChooseFont(&cf))
  {
  return CreateFontIndirect(cf.lpLogFont);
  }
return 0;
}
//=============================================================================
HWND CreateEdit(const HWND hParent,const HINSTANCE hInst,DWORD dwStyle,
                const RECT& rc,const int id,const ustring& caption)
{
//superclass the edit control, register the new edit control class and then
//create a control of that control class.
WNDCLASSEX wcx={0};
wcx.cbSize=sizeof(wcx);
//fill out the WNDCLASSEX with system class info for edit control
GetClassInfoEx(0,_T("edit"),&wcx);
//save important information
wpOldProc=wcx.lpfnWndProc;    //save original wndproc
//now change information to suit requirements
ustring classname=_T("superclassed_edit");
wcx.lpszClassName=classname.c_str();  //unique wnd class name
wcx.lpfnWndProc=EditProc;             //new edit wndproc
wcx.hInstance=hInst;
//and register the new class with the system
if (!RegisterClassEx(&wcx))
  {
  ErrMsg(_T("Failed to register edit control superclass"));
  return 0;
  }
  
dwStyle|=WS_CHILD|WS_VISIBLE;
return CreateWindowEx(WS_EX_CLIENTEDGE,             //extended styles
                      classname.c_str(),            //control 'class' name
                      caption.c_str(),              //control caption
                      dwStyle,                      //control style 
                      rc.left,                      //position: left
                      rc.top,                       //position: top
                      rc.right,                     //width
                      rc.bottom,                    //height
                      hParent,                      //parent window handle
                      //control's ID
                      reinterpret_cast<HMENU>(static_cast<INT_PTR>(id)),
                      hInst,                        //application instance
                      0);                           //user defined info
}
//=============================================================================
inline int ErrMsg(const ustring& s)
{
return MessageBox(0,s.c_str(),_T("ERROR"),MB_OK|MB_ICONEXCLAMATION);
}
//=============================================================================
void SetFormattingRect(const HWND hwnd)
{
//sets the formatting rectangle for the superclassed edit control ie the
//dimensions used to display text.
const int MARGIN_X=10;
const int MARGIN_Y=10;

RECT rc={0};
SendMessage(hwnd,EM_GETRECT,0,reinterpret_cast<LPARAM>(&rc));
rc.left+=MARGIN_X;
rc.top+=MARGIN_Y;
SendMessage(hwnd,EM_SETRECT,0,reinterpret_cast<LPARAM>(&rc));
}
//=============================================================================