// // CBaseClassWindow.cpp // // (c)Copyright 2004 Steven J. Eschweiler. All Rights Reserved. // #include "cbaseclasswindow.h" #include <windows.h> #include <iostream> #include <assert.h> // Internal helper functions - Required to handle window procedure - Must be global! LRESULT CALLBACK CBaseClassWindowWndProc2(HWND hwnd, UINT mMsg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK CBaseClassWindowWndProc(HWND hwnd, UINT mMsg, WPARAM wParam, LPARAM lParam); // Internal helper variables - Required to handle window procedure - Must be global! CBaseClassWindow *g_pWindowBaseClass; // Initialize static class variables int CBaseClassWindow::m_nNumInstances=0; CBaseClassWindow::CBaseClassWindow(void) { m_hwnd = NULL; m_hwndParent = NULL; m_nNumChildren = 0; m_nControlID = 0; } CBaseClassWindow::~CBaseClassWindow(void) { } bool CBaseClassWindow::Init(DWORD dwExStyle,LPCTSTR lpClassName,LPCTSTR lpWindowName,DWORD dwStyle,int x,int y,int nWidth,int nHeight,HMENU hMenu, COLORREF clrBackground,HICON hIcon,HICON hIconSm,bool bRegisterWindowClass) { WNDCLASSEX wcx; // Make sure it's not already created assert(m_hwnd==NULL); // Force it to < 256 and test it if (lpClassName) assert(strlen(lpClassName)<254); if (lpWindowName) assert(strlen(lpWindowName)<254); // Initialize this /* if (BGRed==-1 || BGGreen==-1 || BGBlue==-1) { m_BGRed = 236; m_BGGreen = 233; m_BGBlue = 216; } else { m_BGRed = BGRed; m_BGGreen = BGGreen; m_BGBlue = BGBlue; } */ m_clrBackground = clrBackground; m_hBrushStatic = static_cast<HBRUSH>(CreateSolidBrush(clrBackground)); m_bRegisterWindowClass=bRegisterWindowClass; m_dwExStyle=dwExStyle; if (lpClassName) strcpy_s(m_lpClassName,lpClassName); else m_lpClassName[0]=0; if (lpWindowName) strcpy_s(m_lpWindowName,lpWindowName); else m_lpWindowName[0]=0; m_dwStyle=dwStyle; m_x=x; m_y=y; m_width=nWidth; m_height=nHeight; /* hMenu [in] Handle to a menu, or specifies a child-window identifier, depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used. For a child window, hMenu specifies the child-window identifier, an integer value used by a dialog box control to notify its parent about events. The application determines the child-window identifier; it must be unique for all child windows with the same parent window. */ m_hMenu=hMenu; // See: http://www.codecomments.com/archive371-2005-11-708667.html // You can avoid this warning but 64 bit windows would uses 64 bit pointers // m_nControlID = static_cast<UINT>(reinterpret_cast<intptr_t>(m_hMenu)); m_nControlID = reinterpret_cast<UINT>(hMenu); if (m_nControlID==WM_APP) { char szTmp[256]; sprintf_s(szTmp,"%d-%d-%d-%s-%s-%d-%d-%d-%d-%d{1990BE72-30C7-4bac-8DF9-06DEB04591DB}",rand(),m_dwExStyle,m_dwStyle,m_lpClassName,m_lpWindowName,m_x,m_y,m_width,m_height,m_hBrushStatic); m_nControlID = ::RegisterWindowMessage(szTmp); m_hMenu = reinterpret_cast<HMENU>(m_nControlID); } // Create a unique class name, if needed m_nNumInstances++; if (lpClassName==NULL) sprintf_s(m_lpClassName,"CBaseClassWindow%d",m_nNumInstances); // Now save an uppercase copy of class name m_szClassNameUppercase=m_lpClassName; //m_szClassNameUppercase.MakeUpper(); m_szClassNameUppercase = m_SM.ToUpper(m_szClassNameUppercase); // Register window class if (m_bRegisterWindowClass) { wcx.cbSize = sizeof(wcx); wcx.lpszClassName = m_lpClassName; wcx.hInstance = GetModuleHandle(NULL); wcx.lpfnWndProc = (WNDPROC)CBaseClassWindowWndProc; wcx.hCursor = LoadCursor(NULL, IDC_ARROW); // wcx.hCursor = LoadCursor(NULL, IDC_APPSTARTING); wcx.hIcon = hIcon; wcx.lpszMenuName = NULL; wcx.hbrBackground = m_hBrushStatic; wcx.style = NULL; wcx.cbClsExtra = 0; wcx.cbWndExtra = 0; wcx.hIconSm = hIconSm; ATOM atom = RegisterClassEx(&wcx); if (atom==0) return false; assert(atom!=0); } return true; } HWND CBaseClassWindow::Create(HWND hwndParent) { // Make sure it's not already created assert(m_hwnd==NULL); // Save parent info m_hwndParent = hwndParent; // Our WndProc solution g_pWindowBaseClass = this; // Create the window now m_hwnd = CreateWindowEx( m_dwExStyle, m_lpClassName, m_lpWindowName, m_dwStyle, m_x, m_y, m_width, m_height, m_hwndParent, m_hMenu, GetModuleHandle(NULL), NULL ); // A little debug code assert(m_hwnd!=NULL); if (!m_bRegisterWindowClass) { // Save old WndProc and assign new m_OriginalWndProc = (WNDPROC)SetWindowLongPtr(m_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(CBaseClassWindowWndProc)); } // If member OnCreate() exists, call it now... // This is a virtual function so the derived class member version // of OnCreate() will be called if it exists! // So far, our checkbox class and our TreeView class need this... OnCreate(); return m_hwnd; } // If you create (derive) this virtual member function in your derived class, you must call this base class version! LRESULT CBaseClassWindow::WndProc(HWND hwnd, UINT mMsg, WPARAM wParam, LPARAM lParam) { LRESULT lResultVal; int nChildIndexTabFrom,nChildIndexTabTo,nDefaultChildButton; bool bTabToNext=false; bool bTabToPrevious=false; bool bPressDefaultButton=false; if (m_bRegisterWindowClass) { // Handle any child WS_TABSTOP controls first for (nChildIndexTabFrom=0; nChildIndexTabFrom<m_nNumChildren; nChildIndexTabFrom++) { if (mMsg==m_cptrChildWindow[nChildIndexTabFrom]->GetControlId()) { // OK, a control has sent one of our custom messages if (lParam==WM_KEYDOWN && wParam==VK_TAB) { // It was a request to TAB to the next control if (GetKeyState(VK_SHIFT)&0x8000) bTabToPrevious=true; else bTabToNext=true; break; } if (lParam==WM_KEYDOWN && wParam==VK_RETURN) { // Press the default button... Only one button should be the // default. Note that we just simulate a press on the first // default button we find. bPressDefaultButton=true; break; } } } if (bTabToNext) { nChildIndexTabTo = (nChildIndexTabFrom+1)%m_nNumChildren; do { if (m_cptrChildWindow[nChildIndexTabTo]->m_dwStyle&WS_TABSTOP) { break; } nChildIndexTabTo++; nChildIndexTabTo%=m_nNumChildren; } while (nChildIndexTabTo!=nChildIndexTabFrom); m_cptrChildWindow[nChildIndexTabTo]->SetFocus(); //SetFocus(m_cptrChildWindow[nChildIndexTabTo]->GetHwnd()); } else if (bTabToPrevious) { nChildIndexTabTo = nChildIndexTabFrom-1; if (nChildIndexTabTo<0) nChildIndexTabTo=m_nNumChildren-1; do { if (m_cptrChildWindow[nChildIndexTabTo]->m_dwStyle&WS_TABSTOP) { break; } nChildIndexTabTo--; if (nChildIndexTabTo<0) nChildIndexTabTo=m_nNumChildren-1; } while (nChildIndexTabTo!=nChildIndexTabFrom); //SetFocus(m_cptrChildWindow[nChildIndexTabTo]->GetHwnd()); m_cptrChildWindow[nChildIndexTabTo]->SetFocus(); } else if (bPressDefaultButton) { for (nDefaultChildButton=0; nDefaultChildButton<m_nNumChildren; nDefaultChildButton++) { if ( m_cptrChildWindow[nDefaultChildButton]->m_szClassNameUppercase=="BUTTON" && (m_cptrChildWindow[nDefaultChildButton]->m_dwStyle&BS_DEFPUSHBUTTON) ) { //MessageBox(hwnd,"Hello","JK",MB_OK); PostMessage(hwnd,WM_COMMAND,MAKEWPARAM(m_cptrChildWindow[nDefaultChildButton]->GetControlId(),1),reinterpret_cast<LPARAM>(m_cptrChildWindow[nDefaultChildButton]->GetHwnd())); break; } } } // Most likely a window. Either way, this is safe! lResultVal = DefWindowProc(hwnd, mMsg, wParam, lParam); } else { // Most likely a control. Either way, this is safe! lResultVal = CallWindowProc(this->m_OriginalWndProc, hwnd, mMsg, wParam, lParam); } return lResultVal; } void CBaseClassWindow::OnCreate() { } void CBaseClassWindow::AttachChild(CBaseClassWindow *cptrChildWindow) { assert(m_nNumChildren<m_nMaxChildren); m_cptrChildWindow[m_nNumChildren] = cptrChildWindow; m_nNumChildren++; } void CBaseClassWindow::CreateChildren(HWND hwndParent) { if (m_nNumChildren>0) { for (int LoopVar=0; LoopVar<m_nNumChildren; LoopVar++) { m_cptrChildWindow[LoopVar]->Create(hwndParent); // Now save a copy of our parent window m_cptrChildWindow[LoopVar]->m_pWndParent = this; } } } LRESULT CALLBACK CBaseClassWindowWndProc(HWND hwnd, UINT mMsg, WPARAM wParam, LPARAM lParam) { // See: http://www.rpi.edu/~pudeyo/articles/wndproc/ // And: http://www.gamedev.net/reference/articles/article1901.asp /* switch (mMsg) { case WM_NCCREATE: { LPCREATESTRUCT cs; cs = reinterpret_cast<LPCREATESTRUCT>(lParam); // cs->lpCreateParams is the "this" pointer from CButtonClass::Create->CreateWindowEx() SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(cs->lpCreateParams) ); break; } case WM_GETMINMAXINFO: { // GUESS WHAT! WM_GETMINMAXINFO is the first function called when a // main window is created causing the program to crash on startup!!! // This is because WM_NCCREATE is not called first and also // cs->lpCreateParams is NOT the "this" pointer from // CButtonClass::Create->CreateWindowEx() and so we have no way of knowing // what WndProc to call!!! // So.......... // We will use an alternate method, described here: // http://www.rpi.edu/~pudeyo/articles/wndproc/ return (DefWindowProc(hwnd, mMsg, wParam, lParam)); break; } } CBaseClassWindow *pcwbc = reinterpret_cast<CBaseClassWindow *>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); return pcwbc->WndProc(hwnd, mMsg, wParam, lParam); */ // Stash global Window pointer into per-window data area SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(g_pWindowBaseClass)); // Reset the window message handler SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(CBaseClassWindowWndProc2)); //g_pWindowBaseClass->m_OriginalWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(CBaseClassWindowWndProc2)); // Dispatch first message to the member message handler return CBaseClassWindowWndProc2(hwnd, mMsg, wParam, lParam); } LRESULT CBaseClassWindow::OnCtlColorStatic(HWND hwnd, UINT mMsg, WPARAM wParam, LPARAM lParam) { // WM_CTLCOLORSTATIC // This is what our radio & checkbox buttons use for background colors!!! // You can actually process this message to change the text color. This // would be useful for highlighting our text when hovering over it... // We should create a class for this... return reinterpret_cast<LRESULT>(m_hBrushStatic); } void CBaseClassWindow::OnDestroy(HWND hwnd, UINT mMsg, WPARAM wParam, LPARAM lParam) { // WM_DESTROY DeleteObject(m_hBrushStatic); } LRESULT CALLBACK CBaseClassWindowWndProc2(HWND hwnd, UINT mMsg, WPARAM wParam, LPARAM lParam) { // Get a window pointer associated with this window CBaseClassWindow *w = reinterpret_cast<CBaseClassWindow *>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); // It should be valid, assert so assert(w); // Redirect messages to the window procedure of the associated window LRESULT lResult = w->WndProc(hwnd, mMsg, wParam, lParam); // We could put this before or after the call to the class WndProc() function if (mMsg == WM_DESTROY) w->OnDestroy(hwnd, mMsg, wParam, lParam); // This MUST come _after_ we call class WndProc (w->WndProc() above) if (mMsg == WM_CTLCOLORSTATIC) return w->OnCtlColorStatic(hwnd, mMsg, wParam, lParam); return lResult; } void CBaseClassWindow::Show(void) { ShowWindow(m_hwnd,SW_SHOW); } void CBaseClassWindow::Hide(void) { ShowWindow(m_hwnd,SW_HIDE); } void CBaseClassWindow::Minimize(void) { ShowWindow(m_hwnd,SW_MINIMIZE); } void CBaseClassWindow::Maximize(void) { ShowWindow(m_hwnd,SW_MAXIMIZE); } void CBaseClassWindow::Restore(void) { ShowWindow(m_hwnd,SW_RESTORE); } void CBaseClassWindow::Update(void) { UpdateWindow(m_hwnd); } void CBaseClassWindow::Invalidate(LPRECT lpRect) { InvalidateRect(m_hwnd,lpRect,TRUE); } int CBaseClassWindow::GetX(void) { /* RECT rc; GetClientRect(m_hwnd,&rc); MapWindowPoints(m_hwnd,m_hwndParent,reinterpret_cast<LPPOINT>(&rc),2); return rc.left; */ // This following method is preferred and is safe as long as we keep // it updated - For example, in the move functions below... // It is preferred because we can use it to layout our window even // when the window has not been created yet but only initialized. return m_x; } int CBaseClassWindow::GetY(void) { /* RECT rc; GetClientRect(m_hwnd,&rc); MapWindowPoints(m_hwnd,m_hwndParent,reinterpret_cast<LPPOINT>(&rc),2); return rc.top; */ // This following method is preferred and is safe as long as we keep // it updated - For example, in the move functions below... // It is preferred because we can use it to layout our window even // when the window has not been created yet but only initialized. return m_y; } int CBaseClassWindow::GetWidth(void) { /* RECT rc; GetClientRect(m_hwnd,&rc); return rc.right; */ // This following method is preferred and is safe as long as we keep // it updated - For example, in the move functions below... // It is preferred because we can use it to layout our window even // when the window has not been created yet but only initialized. return m_width; } int CBaseClassWindow::GetHeight(void) { /* RECT rc; GetClientRect(m_hwnd,&rc); return rc.bottom; */ // This following method is preferred and is safe as long as we keep // it updated - For example, in the move functions below... // It is preferred because we can use it to layout our window even // when the window has not been created yet but only initialized. return m_height; } void CBaseClassWindow::Move(int x,int y) { m_x = x; m_y = y; MoveWindow(m_hwnd,x,y,m_width,m_height,TRUE); } void CBaseClassWindow::Move(int x,int y,int width,int height) { m_x = x; m_y = y; m_width = width; m_height = height; MoveWindow(m_hwnd,x,y,m_width,m_height,TRUE); } void CBaseClassWindow::Move(int x,int y,int width,int height,BOOL bRepaint) { m_x = x; m_y = y; m_width = width; m_height = height; MoveWindow(m_hwnd,x,y,m_width,m_height,bRepaint); } void CBaseClassWindow::SetClassCursor(HCURSOR hCursor) { SetClassLongPtr(m_hwnd,GCLP_HCURSOR,reinterpret_cast<LONG_PTR>(hCursor)); //this->Invalidate(); } bool CBaseClassWindow::Enable(bool bEnable) { /* if (bEnable==false) { //SetClassLongPtr(m_hwnd,GCLP_HCURSOR,(LONG_PTR) //SetClassLongPtr(m_hwnd,GCLP_HCURSOR,reinterpret_cast<LONG_PTR>(LoadCursor(NULL, IDC_APPSTARTING))); SetClassLong(m_hwnd,GCL_HCURSOR,reinterpret_cast<LONG>(LoadCursor(NULL, IDC_APPSTARTING))); SetClassLongPtr(m_hwnd,GCLP_HCURSOR,reinterpret_cast<LONG_PTR>(LoadCursor(NULL, IDC_APPSTARTING))); //SetClassLong(m_hwnd,GCLP_HCURSOR,reinterpret_cast<LONG>(LoadCursor(NULL, IDC_APPSTARTING))); //SetCursor(LoadCursor(NULL, IDC_APPSTARTING)); //SetClassLongPtr(m_hwnd,GCLP_HCURSOR,NULL); } //return true; */ return static_cast<bool>(EnableWindow(m_hwnd,static_cast<BOOL>(bEnable))); } bool CBaseClassWindow::IsEnabled(void) { return static_cast<bool>(IsWindowEnabled(m_hwnd)); } HWND CBaseClassWindow::GetHwnd(void) { return m_hwnd; } HWND CBaseClassWindow::GetHwndParent(void) { return m_hwndParent; } HWND CBaseClassWindow::SetFocus() { return ::SetFocus(m_hwnd); } COLORREF CBaseClassWindow::GetBkColor() { return m_clrBackground; } void CBaseClassWindow::SetFont(HFONT hFont,bool bRedraw) { //HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); SendMessage(m_hwnd, WM_SETFONT, reinterpret_cast<WPARAM>(hFont), MAKELPARAM(static_cast<BOOL>(bRedraw), 0)); } UINT CBaseClassWindow::GetControlId() { return m_nControlID; } std::string CBaseClassWindow::GetText(bool bTrimmed,bool bCapped,int nCappedLength,bool bCapitalizeFirstLetter) { char ch; int szLength=GetWindowTextLength(m_hwnd)+1; // +1 for terminating NULL character if (bCapped) { if (szLength>nCappedLength) szLength = nCappedLength+1; } char *pszText = new char[szLength]; GetWindowText(m_hwnd,pszText,szLength); std::string strText=pszText; delete pszText; if (bTrimmed) strText = m_SM.Trim(strText); if (bCapitalizeFirstLetter) { if (strText.length() > 0) { ch = strText.at(0); if (ch>='a' && ch<='z') strText[0] = ch-('a'-'A'); } } //if (bCapitalizeAllFirstLetters) //{ //} return strText; } void CBaseClassWindow::ReadOnly(bool bReadOnly) { // this style after the control has been created, use the EM_SETREADONLY message. SendMessage(m_hwnd,EM_SETREADONLY,static_cast<WPARAM>(bReadOnly),0); }