//----------------------------------------------------------------------------- // File: flexwnd.cpp // // Desc: CFlexWnd is a generic class that encapsulates the functionalities // of a window. All other window classes are derived from CFlexWnd. // // Child classes can have different behavior by overriding the // overridable message handlers (OnXXX members). // // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved. //----------------------------------------------------------------------------- #include "common.hpp" #include "typeinfo.h" BOOL CFlexWnd::sm_bWndClassRegistered = FALSE; WNDCLASSEX CFlexWnd::sm_WndClass; LPCTSTR CFlexWnd::sm_tszWndClassName = _T("Microsoft.CFlexWnd.WndClassName"); HINSTANCE CFlexWnd::sm_hInstance = NULL; CFlexToolTip CFlexWnd::s_ToolTip; // Shared tooltip window object DWORD CFlexWnd::s_dwLastMouseMove; // Last GetTickCount() that we have a WM_MOUSEMOVE HWND CFlexWnd::s_hWndLastMouseMove; // Last window handle of WM_MOUSEMOVE LPARAM CFlexWnd::s_PointLastMouseMove; // Last point of WM_MOUSEMOVE HWND CFlexWnd::s_CurrPageHwnd; // For unhighlighting callouts when a click is made outside of a callout int NewID() { static int i = 0; return ++i; } CFlexWnd::CFlexWnd() : m_nID(NewID()), m_hWnd(m_privhWnd), m_privhWnd(NULL), m_hRenderInto(NULL), m_bIsDialog(FALSE), m_bRender(FALSE), m_bReadOnly(FALSE) { } CFlexWnd::~CFlexWnd() { Destroy(); } void CFlexWnd::Destroy() { if (m_hWnd != NULL) DestroyWindow(m_hWnd); assert(m_privhWnd == NULL); } BOOL CFlexWnd::IsDialog() { return HasWnd() && m_bIsDialog; } void CFlexWnd::OnRender(BOOL bInternalCall) { // if parent is flexwnd and both are in render mode, pass to parent if (!m_hWnd) return; HWND hParent = GetParent(m_hWnd); if (!hParent) return; CFlexWnd *pParent = GetFlexWnd(hParent); if (!pParent) return; if (pParent->InRenderMode() && InRenderMode()) pParent->OnRender(TRUE); } BOOL CFlexWnd::OnEraseBkgnd(HDC hDC) { if (InRenderMode()) return TRUE; /* if (IsDialog()) return FALSE;*/ return TRUE; } struct GETFLEXWNDSTRUCT { int cbSize; BOOL bFlexWnd; CFlexWnd *pFlexWnd; }; // This function takes a HWND and returns a pointer to CFlexWnd if the HWND is a window // created by the UI. CFlexWnd *CFlexWnd::GetFlexWnd(HWND hWnd) { if (hWnd == NULL) return NULL; GETFLEXWNDSTRUCT gfws; gfws.cbSize = sizeof(gfws); gfws.bFlexWnd = FALSE; gfws.pFlexWnd = NULL; SendMessage(hWnd, WM_GETFLEXWND, 0, (LPARAM)(LPVOID)(FAR GETFLEXWNDSTRUCT *)&gfws); if (gfws.bFlexWnd) return gfws.pFlexWnd; else return NULL; } // Basic window proc. It simply forward interesting messages to the appropriate handlers (OnXXX). // If child class defines this function, it should pass unhandled messages to CFlexWnd. LRESULT CFlexWnd::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_GETFLEXWND: { if ((LPVOID)lParam == NULL) break; GETFLEXWNDSTRUCT &gfws = *((FAR GETFLEXWNDSTRUCT *)(LPVOID)lParam); switch (gfws.cbSize) { case sizeof(GETFLEXWNDSTRUCT): gfws.bFlexWnd = TRUE; gfws.pFlexWnd = this; return 0; default: assert(0); break; } break; } case WM_CREATE: { LPCREATESTRUCT lpCreateStruct = (LPCREATESTRUCT)lParam; LRESULT lr = OnCreate(lpCreateStruct); if (lr != -1) OnInit(); return lr; } case WM_INITDIALOG: { BOOL b = OnInitDialog(); OnInit(); return b; } case WM_TIMER: OnTimer((UINT)wParam); return 0; case WM_ERASEBKGND: return OnEraseBkgnd((HDC)wParam); case WM_PAINT: { // Check the update rectangle. If we don't have it, exit immediately. if (typeid(*this) == typeid(CDeviceView) && !GetUpdateRect(m_hWnd, NULL, FALSE)) return 0; PAINTSTRUCT ps; HDC hDC = BeginPaint(hWnd, &ps); if (InRenderMode()) OnRender(TRUE); else DoOnPaint(hDC); EndPaint(hWnd, &ps); return 0; } case WM_COMMAND: { WORD wNotifyCode = HIWORD(wParam); WORD wID = LOWORD(wParam); HWND hWnd = (HWND)lParam; return OnCommand(wNotifyCode, wID, hWnd); } case WM_NOTIFY: return OnNotify(wParam, lParam); case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_MOUSEWHEEL: { POINT point = {int(LOWORD(lParam)), int(HIWORD(lParam))}; switch (msg) { case WM_MOUSEMOVE: OnMouseOver(point, wParam); break; case WM_LBUTTONDOWN: OnClick(point, wParam, TRUE); break; case WM_RBUTTONDOWN: OnClick(point, wParam, FALSE); break; case WM_LBUTTONDBLCLK: OnDoubleClick(point, wParam, TRUE); break; case WM_MOUSEWHEEL: { // Send wheel msg to the window beneath the cursor HWND hWnd = WindowFromPoint(point); CFlexWnd *pWnd = NULL; if (hWnd) { pWnd = GetFlexWnd(hWnd); if (pWnd) pWnd->OnWheel(point, wParam); else return DefWindowProc(hWnd, msg, wParam, lParam); } break; } } return 0; } case WM_DESTROY: OnDestroy(); m_privhWnd = NULL; return 0; } if (!m_bIsDialog) return DefWindowProc(hWnd, msg, wParam, lParam); else return 0; } //@@BEGIN_MSINTERNAL // TODO: better control id thingy //@@END_MSINTERNAL static HMENU windex = 0; BOOL CFlexWnd::EndDialog(int n) { if (!m_bIsDialog || m_hWnd == NULL) { assert(0); return FALSE; } return ::EndDialog(m_hWnd, n); } int CFlexWnd::DoModal(HWND hParent, int nTemplate, HINSTANCE hInst) { return DoModal(hParent, MAKEINTRESOURCE(nTemplate), hInst); } HWND CFlexWnd::DoModeless(HWND hParent, int nTemplate, HINSTANCE hInst) { return DoModeless(hParent, MAKEINTRESOURCE(nTemplate), hInst); } int CFlexWnd::DoModal(HWND hParent, LPCTSTR lpTemplate, HINSTANCE hInst) { if (m_hWnd != NULL) { assert(0); return -1; } if (hInst == NULL) hInst = CFlexWnd::sm_hInstance; return (int)DialogBoxParam(hInst, lpTemplate, hParent, (DLGPROC)__BaseFlexWndDialogProc, (LPARAM)(void *)this); } HWND CFlexWnd::DoModeless(HWND hParent, LPCTSTR lpTemplate, HINSTANCE hInst) { if (m_hWnd != NULL) { assert(0); return NULL; } if (hInst == NULL) hInst = CFlexWnd::sm_hInstance; return CreateDialogParam(hInst, lpTemplate, hParent, (DLGPROC)__BaseFlexWndDialogProc, (LPARAM)(void *)this); } HWND CFlexWnd::Create(HWND hParent, const RECT &rect, BOOL bVisible) { ++(*(LPBYTE*)&windex); return Create(hParent, _T("(unnamed)"), 0, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_EX_NOPARENTNOTIFY | (bVisible ? WS_VISIBLE : 0), rect, windex); } HWND CFlexWnd::Create(HWND hParent, LPCTSTR tszName, DWORD dwExStyle, DWORD dwStyle, const RECT &rect, HMENU hMenu) { HWND hWnd = NULL; if (m_hWnd != NULL) { assert(0); return hWnd; } if (hMenu == NULL && (dwStyle & WS_CHILD)) { ++(*(LPBYTE*)&windex); hMenu = windex; } hWnd = CreateWindowEx( dwExStyle, CFlexWnd::sm_tszWndClassName, tszName, dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hParent, hMenu, CFlexWnd::sm_hInstance, (void *)this); assert(m_hWnd == hWnd); return hWnd; } void CFlexWnd::SetHWND(HWND hWnd) { assert(m_hWnd == NULL && hWnd != NULL); m_privhWnd = hWnd; assert(m_hWnd == m_privhWnd); InitFlexWnd(); } void CFlexWnd::InitFlexWnd() { if (!HasWnd()) return; HWND hParent = GetParent(m_hWnd); CFlexWnd *pParent = GetFlexWnd(hParent); if (pParent && pParent->InRenderMode()) SetRenderMode(); } TCHAR sg_tszFlexWndPointerProp[] = _T("CFlexWnd *"); LRESULT CALLBACK __BaseFlexWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { CFlexWnd *pThis = (CFlexWnd *)GetProp(hWnd, sg_tszFlexWndPointerProp); if ((msg == WM_MOUSEMOVE || msg == WM_MOUSEWHEEL) && hWnd != CFlexWnd::s_ToolTip.m_hWnd) { // Filter out the message with same window handle and point. // Windows sometimes seems to send us WM_MOUSEMOVE message even though the mouse is not moved. if (CFlexWnd::s_hWndLastMouseMove != hWnd || CFlexWnd::s_PointLastMouseMove != lParam) { CFlexWnd::s_hWndLastMouseMove = hWnd; CFlexWnd::s_PointLastMouseMove = lParam; CFlexWnd::s_dwLastMouseMove = GetTickCount(); // Get timestamp CFlexWnd::s_ToolTip.SetEnable(FALSE); CFlexWnd::s_ToolTip.SetToolTipParent(NULL); } } switch (msg) { case WM_CREATE: { LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam; if (lpcs == NULL) break; pThis = (CFlexWnd *)(void *)(lpcs->lpCreateParams); assert(sizeof(HANDLE) == sizeof(CFlexWnd *)); SetProp(hWnd, sg_tszFlexWndPointerProp, (HANDLE)pThis); if (pThis != NULL) { pThis->m_bIsDialog = FALSE; pThis->SetHWND(hWnd); } break; } } if (pThis != NULL) return pThis->WndProc(hWnd, msg, wParam, lParam); else return DefWindowProc(hWnd, msg, wParam, lParam); } LRESULT CALLBACK __BaseFlexWndDialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { CFlexWnd *pThis = (CFlexWnd *)GetProp(hWnd, sg_tszFlexWndPointerProp); switch (msg) { case WM_INITDIALOG: pThis = (CFlexWnd *)(void *)lParam; assert(sizeof(HANDLE) == sizeof(CFlexWnd *)); SetProp(hWnd, sg_tszFlexWndPointerProp, (HANDLE)pThis); if (pThis != NULL) { pThis->m_bIsDialog = TRUE; pThis->SetHWND(hWnd); } break; } if (pThis != NULL) return (BOOL)pThis->WndProc(hWnd, msg, wParam, lParam); else return FALSE; } void CFlexWnd::Invalidate() { if (m_hWnd != NULL) InvalidateRect(m_hWnd, NULL, TRUE); } SIZE CFlexWnd::GetClientSize() const { RECT rect = {0, 0, 0, 0}; if (m_hWnd != NULL) ::GetClientRect(m_hWnd, &rect); SIZE size = { rect.right - rect.left, rect.bottom - rect.top}; return size; } void CFlexWnd::FillWndClass(HINSTANCE hInst) { sm_WndClass.cbSize = sizeof(WNDCLASSEX); sm_WndClass.style = CS_DBLCLKS; sm_WndClass.lpfnWndProc = __BaseFlexWndProc; sm_WndClass.cbClsExtra = 0; sm_WndClass.cbWndExtra = sizeof(CFlexWnd *); sm_WndClass.hInstance = sm_hInstance = hInst; sm_WndClass.hIcon = NULL; sm_WndClass.hCursor = NULL; sm_WndClass.hbrBackground = NULL; sm_WndClass.lpszMenuName = NULL; sm_WndClass.lpszClassName = sm_tszWndClassName; sm_WndClass.hIconSm = NULL; } void CFlexWnd::RegisterWndClass(HINSTANCE hInst) { if (hInst == NULL) { assert(0); return; } FillWndClass(hInst); RegisterClassEx(&sm_WndClass); sm_bWndClassRegistered = TRUE; } void CFlexWnd::UnregisterWndClass(HINSTANCE hInst) { if (hInst == NULL) return; UnregisterClass(sm_tszWndClassName, hInst); sm_bWndClassRegistered = FALSE; } void CFlexWnd::GetClientRect(LPRECT lprect) const { if (lprect == NULL || m_hWnd == NULL) return; ::GetClientRect(m_hWnd, lprect); } LPCTSTR CFlexWnd::GetDefaultClassName() { return CFlexWnd::sm_tszWndClassName; } void CFlexWnd::SetRenderMode(BOOL bRender) { if (bRender == m_bRender) return; m_bRender = bRender; Invalidate(); } BOOL CFlexWnd::InRenderMode() { return m_bRender; } void EnumChildWindowsZDown(HWND hParent, WNDENUMPROC proc, LPARAM lParam) { if (hParent == NULL || proc == NULL) return; HWND hWnd = GetWindow(hParent, GW_CHILD); while (hWnd != NULL) { if (!proc(hWnd, lParam)) break; hWnd = GetWindow(hWnd, GW_HWNDNEXT); } } void EnumSiblingsAbove(HWND hParent, WNDENUMPROC proc, LPARAM lParam) { if (hParent == NULL || proc == NULL) return; HWND hWnd = hParent; while (1) { hWnd = GetWindow(hWnd, GW_HWNDPREV); if (hWnd == NULL) break; if (!proc(hWnd, lParam)) break; } } static BOOL CALLBACK RenderIntoClipChild(HWND hWnd, LPARAM lParam) { CFlexWnd *pThis = (CFlexWnd *)(LPVOID)lParam; return pThis->RenderIntoClipChild(hWnd); } static BOOL CALLBACK RenderIntoRenderChild(HWND hWnd, LPARAM lParam) { CFlexWnd *pThis = (CFlexWnd *)(LPVOID)lParam; // Check if this is the immediate child. Do nothing if it's not immediate. HWND hParent = GetParent(hWnd); if (hParent != pThis->m_hWnd) return TRUE; return pThis->RenderIntoRenderChild(hWnd); } BOOL CFlexWnd::RenderIntoClipChild(HWND hChild) { if (m_hRenderInto != NULL && HasWnd() && hChild && IsWindowVisible(hChild)) { RECT rect; GetWindowRect(hChild, &rect); POINT ul = {rect.left, rect.top}, lr = {rect.right, rect.bottom}; ScreenToClient(m_hWnd, &ul); ScreenToClient(m_hWnd, &lr); ExcludeClipRect(m_hRenderInto, ul.x, ul.y, lr.x, lr.y); } return TRUE; } BOOL CFlexWnd::RenderIntoRenderChild(HWND hChild) { CFlexWnd *pChild = GetFlexWnd(hChild); if (m_hRenderInto != NULL && HasWnd() && pChild != NULL && IsWindowVisible(hChild)) { RECT rect; GetWindowRect(hChild, &rect); POINT ul = {rect.left, rect.top}; ScreenToClient(m_hWnd, &ul); pChild->RenderInto(m_hRenderInto, ul.x, ul.y); } return TRUE; } void CFlexWnd::RenderInto(HDC hDC, int x, int y) { if (hDC == NULL) return; int sdc = SaveDC(hDC); { OffsetViewportOrgEx(hDC, x, y, NULL); SIZE size = GetClientSize(); IntersectClipRect(hDC, 0, 0, size.cx, size.cy); m_hRenderInto = hDC; int sdc2 = SaveDC(hDC); { EnumChildWindows/*ZDown*/(m_hWnd, ::RenderIntoClipChild, (LPARAM)(PVOID)this); EnumSiblingsAbove(m_hWnd, ::RenderIntoClipChild, (LPARAM)(PVOID)this); DoOnPaint(hDC); } if (sdc2) RestoreDC(hDC, sdc2); EnumChildWindows/*ZDown*/(m_hWnd, ::RenderIntoRenderChild, (LPARAM)(PVOID)this); m_hRenderInto = NULL; } if (sdc) RestoreDC(hDC, sdc); } void CFlexWnd::SetCapture() { ::SetCapture(m_hWnd); } void CFlexWnd::ReleaseCapture() { ::ReleaseCapture(); } void CFlexWnd::DoOnPaint(HDC hDC) { OnPaint(hDC); }