WindowsXP-SP1/enduser/netmeeting/nmutil/genwindow.cpp
2020-09-30 16:53:49 +02:00

1330 lines
28 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// File: GenWindow.cpp
#include "precomp.h"
#include "GenWindow.h"
#include "GenContainers.h"
#include <windowsx.h>
// We need a different tooltip window for each top level window, or the tooltip
// will get hidden behind the window
struct TT_TopWindow
{
HWND hwndTop;
HWND hwndTooltip;
} ;
class CTopWindowArray
{
private:
enum { InitSize = 4 } ;
TT_TopWindow *m_pArray;
UINT m_nArrayLen;
int FindIndex(HWND hwndTop)
{
if (NULL == m_pArray)
{
return(-1);
}
// Just a linear search
int i;
for (i=m_nArrayLen-1; i>=0; --i)
{
if (m_pArray[i].hwndTop == hwndTop)
{
break;
}
}
return(i);
}
public:
CTopWindowArray() :
m_pArray(NULL)
{
}
~CTopWindowArray()
{
delete[] m_pArray;
}
static HWND GetTopFrame(HWND hwnd)
{
HWND hwndParent;
while (NULL != (hwndParent = GetParent(hwnd)))
{
hwnd = hwndParent;
}
return(hwnd);
}
void GrowArray()
{
if (NULL == m_pArray)
{
m_nArrayLen = InitSize;
m_pArray = new TT_TopWindow[m_nArrayLen];
ZeroMemory(m_pArray, m_nArrayLen*sizeof(TT_TopWindow));
return;
}
// Grow exponentially
TT_TopWindow *pArray = new TT_TopWindow[m_nArrayLen*2];
if (NULL == pArray)
{
// very bad
return;
}
CopyMemory(pArray, m_pArray, m_nArrayLen*sizeof(TT_TopWindow));
ZeroMemory(pArray+m_nArrayLen, m_nArrayLen*sizeof(TT_TopWindow));
delete[] m_pArray;
m_pArray = pArray;
m_nArrayLen *= 2;
}
void Add(HWND hwndTop, HWND hwndTooltip)
{
hwndTop = GetTopFrame(hwndTop);
// I'm going to allow multiple adds of the same thing, but then you
// must have the corresponding number of removes
int i = FindIndex(NULL);
if (i < 0)
{
GrowArray();
i = FindIndex(NULL);
if (i < 0)
{
// Very bad
return;
}
}
m_pArray[i].hwndTop = hwndTop;
m_pArray[i].hwndTooltip = hwndTooltip;
}
void Remove(HWND hwndTop)
{
hwndTop = GetTopFrame(hwndTop);
int i = FindIndex(hwndTop);
if (i >= 0)
{
// LAZYLAZY georgep: I'm never going to shrink the array
m_pArray[i].hwndTop = NULL;
m_pArray[i].hwndTooltip = NULL;
}
}
HWND Find(HWND hwndTop)
{
hwndTop = GetTopFrame(hwndTop);
int i = FindIndex(hwndTop);
if (i >= 0)
{
return(m_pArray[i].hwndTooltip);
}
return(NULL);
}
int GetCount()
{
if (NULL == m_pArray)
{
return(0);
}
int c = 0;
for (int i=m_nArrayLen-1; i>=0; --i)
{
if (NULL != m_pArray[i].hwndTop)
{
++c;
}
}
return(c);
}
} ;
static inline BOOL TT_AddToolInfo(HWND hwnd, TOOLINFO *pti)
{
return (BOOL)(SendMessage(hwnd, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(pti)) != 0);
}
static inline void TT_DelToolInfo(HWND hwnd, TOOLINFO *pti)
{
SendMessage(hwnd, TTM_DELTOOL, 0, reinterpret_cast<LPARAM>(pti));
}
static inline BOOL TT_GetToolInfo(HWND hwnd, TOOLINFO *pti)
{
return (BOOL)(SendMessage(hwnd, TTM_GETTOOLINFO, 0, reinterpret_cast<LPARAM>(pti)) != 0);
}
static inline void TT_SetToolInfo(HWND hwnd, TOOLINFO *pti)
{
SendMessage(hwnd, TTM_SETTOOLINFO, 0, reinterpret_cast<LPARAM>(pti));
}
static inline int TT_GetToolCount(HWND hwnd)
{
return (int)(SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0));
}
CGenWindow *CGenWindow::g_pCurHot = NULL;
const DWORD IGenWindow::c_msgFromHandle = RegisterWindowMessage(_TEXT("NetMeeting::FromHandle"));
IGenWindow *IGenWindow::FromHandle(HWND hwnd)
{
return(reinterpret_cast<IGenWindow*>(SendMessage(hwnd, c_msgFromHandle, 0, 0)));
}
// HACKHACK georgep: Need to make this larger than the largest DM_ message
enum
{
GWM_LAYOUT = WM_USER + 111,
GWM_CUSTOM,
} ;
CGenWindow::CGenWindow()
: m_hwnd(NULL), m_lUserData(0)
{
// Init the ref count to 1
REFCOUNT::AddRef();
// This marks this object for deletion when the ref count goes to 0.
REFCOUNT::Delete();
}
CGenWindow::~CGenWindow()
{
// I don't think the HWND can still exist, since the window proc does an AddRef
ASSERT(!m_hwnd);
}
HRESULT STDMETHODCALLTYPE CGenWindow::QueryInterface(REFGUID riid, LPVOID *ppv)
{
HRESULT hr = S_OK;
if ((__uuidof(IGenWindow) == riid) || (IID_IUnknown == riid))
{
*ppv = dynamic_cast<IGenWindow *>(this);
}
else if (__uuidof(CGenWindow) == riid)
{
*ppv = this;
}
else
{
hr = E_NOINTERFACE;
*ppv = NULL;
}
if (S_OK == hr)
{
AddRef();
}
return hr;
}
BOOL CGenWindow::Create(
HWND hWndParent, // Window parent
LPCTSTR szWindowName, // Window name
DWORD dwStyle, // Window style
DWORD dwEXStyle, // Extended window style
int x, // Window pos: x
int y, // Window pos: y
int nWidth, // Window size: width
int nHeight, // Window size: height
HINSTANCE hInst, // The hInstance to create the window on
HMENU hmMain, // Window menu
LPCTSTR szClassName // The class name to use
)
{
if (NULL != m_hwnd)
{
// Alread created
return(FALSE);
}
if (NULL == szClassName)
{
szClassName = TEXT("NMGenWindowClass");
}
if (!InitWindowClass(szClassName, hInst))
{
// Couldn't init the window class
return(FALSE);
}
BOOL ret = (NULL != CreateWindowEx(dwEXStyle, szClassName, szWindowName, dwStyle,
x, y, nWidth, nHeight, hWndParent, hmMain,
hInst, (LPVOID)this));
#ifdef DEBUG
if (!ret)
{
GetLastError();
}
#endif // DEBUG
return(ret);
}
BOOL CGenWindow::Create(
HWND hWndParent, // Window parent
INT_PTR nId, // ID of the child window
LPCTSTR szWindowName, // Window name
DWORD dwStyle, // Window style; WS_CHILD|WS_VISIBLE will be added to this
DWORD dwEXStyle // Extended window style
)
{
ASSERT(NULL != hWndParent);
// Child windows should default to visible
return(Create(
hWndParent, // Window parent
szWindowName, // Window name
dwStyle|WS_CHILD|WS_VISIBLE, // Window style
dwEXStyle, // Extended window style
0, // Window pos: x
0, // Window pos: y
10, // Window size: width
10, // Window size: height
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(hWndParent, GWLP_HINSTANCE)),
reinterpret_cast<HMENU>(nId) // Window menu
));
}
BOOL CGenWindow::InitWindowClass(LPCTSTR szClassName, HINSTANCE hThis)
{
WNDCLASS wc;
// See if the class is already registered
if (GetClassInfo(hThis, szClassName, &wc))
{
ASSERT(RealWindowProc == wc.lpfnWndProc);
// Already registered
return(TRUE);
}
// If not, attempt to register it
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
// BUGBUG georgep: Hard-coding the background color for now
// wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
// wc.hbrBackground = CreateSolidBrush(RGB(0xA9, 0xA9, 0xA9));
wc.hbrBackground = NULL;
wc.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
wc.hIcon = NULL;
wc.hInstance = hThis;
wc.lpfnWndProc = RealWindowProc;
wc.lpszClassName = szClassName;
wc.lpszMenuName = NULL;
wc.style = CS_DBLCLKS;
return(RegisterClass(&wc));
}
LRESULT CALLBACK CGenWindow::RealWindowProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
// Handle the WM_CREATE message
if (WM_NCCREATE == message)
{
HANDLE_WM_NCCREATE(hWnd, wParam, lParam, OnNCCreate);
}
// Get the "this" pointer and call the ProcessMessage virtual method
LRESULT ret = 0;
CGenWindow* pWnd = reinterpret_cast<CGenWindow*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
// 'pWnd' won't be valid for any messages that come before WM_NCCREATE or after WM_NCDESTROY
if(NULL != pWnd)
{
// Messages after WM_NCCREATE:
ret = pWnd->ProcessMessage(hWnd, message, wParam, lParam);
}
else
{
// Messages before WM_CREATE:
ret = DefWindowProc(hWnd, message, wParam, lParam);
}
// Clean up on WM_NCDESTROY
if (WM_NCDESTROY == message && NULL != pWnd)
{
SetWindowLongPtr(hWnd, GWLP_USERDATA, 0);
pWnd->m_hwnd = NULL;
pWnd->OnMouseLeave();
pWnd->Release();
}
return(ret);
}
void CGenWindow::OnShowWindow(HWND hwnd, BOOL fShow, int fnStatus)
{
OnDesiredSizeChanged();
}
LRESULT CGenWindow::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
HANDLE_MSG(hwnd, WM_SIZE , OnSize);
HANDLE_MSG(hwnd, WM_ERASEBKGND, OnEraseBkgnd);
HANDLE_MSG(hwnd, WM_MOUSEMOVE , OnMouseMove);
HANDLE_MSG(hwnd, WM_SHOWWINDOW, OnShowWindow);
case WM_MOUSELEAVE:
OnMouseLeave();
break;
case GWM_LAYOUT:
Layout();
break;
case GWM_CUSTOM:
reinterpret_cast<InvokeProc>(lParam)(this, wParam);
break;
case WM_DESTROY:
RemoveTooltip();
break;
default:
if (c_msgFromHandle == message)
{
// Return the IGenWindow* for this object, as specified by the
// IGenWindow interface
return(reinterpret_cast<LRESULT>(dynamic_cast<IGenWindow*>(this)));
}
}
return(DefWindowProc(hwnd, message, wParam, lParam));
}
void CGenWindow::ScheduleLayout()
{
HWND hwnd = GetWindow();
MSG msg;
// I don't know why we are getting messages for windows other than our own,
// but it seems to happen for top level windows
if (PeekMessage(&msg, hwnd, GWM_LAYOUT, GWM_LAYOUT, PM_NOREMOVE|PM_NOYIELD)
&& (msg.hwnd == hwnd))
{
// Message already posted
return;
}
if (!PostMessage(hwnd, GWM_LAYOUT, 0, 0))
{
Layout();
}
}
BOOL CGenWindow::AsyncInvoke(InvokeProc proc, WPARAM wParam)
{
return(!PostMessage(GetWindow(), GWM_CUSTOM, wParam, reinterpret_cast<LPARAM>(proc)));
}
void CGenWindow::OnSize(HWND hwnd, UINT state, int cx, int cy)
{
// Call the virtual Layout, and then forward to DefWindowProc
ScheduleLayout();
// Update the Tooltip info
TOOLINFO ti;
TCHAR szTip[MAX_PATH];
BOOL bExist = InitToolInfo(&ti, szTip);
if (bExist)
{
GetClientRect(hwnd, &ti.rect);
HWND hwndTooltip = g_pTopArray->Find(hwnd);
TT_SetToolInfo(hwndTooltip, &ti);
}
FORWARD_WM_SIZE(hwnd, state, cx, cy, DefWindowProc);
}
BOOL CGenWindow::OnEraseBkgnd(HWND hwnd, HDC hdc)
{
HBRUSH hErase = GetBackgroundBrush();
if (NULL == hErase)
{
return(FORWARD_WM_ERASEBKGND(hwnd, hdc, DefWindowProc));
}
HPALETTE hOldPal = NULL;
HPALETTE hPal = GetPalette();
if (NULL != hPal)
{
hOldPal = SelectPalette(hdc, hPal, TRUE);
RealizePalette(hdc);
}
RECT rc;
GetClientRect(hwnd, &rc);
HBRUSH hOld = (HBRUSH)SelectObject(hdc, hErase);
PatBlt(hdc, 0, 0, rc.right, rc.bottom, PATCOPY);
SelectObject(hdc, hOld);
if (NULL != hOldPal)
{
SelectPalette(hdc, hOldPal, TRUE);
}
return(TRUE);
}
void CGenWindow::OnMouseLeave()
{
if (dynamic_cast<IGenWindow*>(this) == g_pCurHot)
{
SetHotControl(NULL);
}
}
void CGenWindow::OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
SetHotControl(this);
FORWARD_WM_MOUSEMOVE(hwnd, x, y, keyFlags, DefWindowProc);
}
// REVIEW georgep: Should this loop until it gets an IGenWindow?
HBRUSH CGenWindow::GetBackgroundBrush()
{
HWND parent = GetParent(GetWindow());
if (NULL == parent)
{
return(GetStandardBrush());
}
IGenWindow *pParent = FromHandle(parent);
if (pParent == NULL)
{
return(GetStandardBrush());
}
return(pParent->GetBackgroundBrush());
}
// REVIEW georgep: Should this loop until it gets an IGenWindow?
HPALETTE CGenWindow::GetPalette()
{
HWND parent = GetParent(GetWindow());
if (NULL == parent)
{
return(GetStandardPalette());
}
IGenWindow *pParent = FromHandle(parent);
if (pParent == NULL)
{
return(GetStandardPalette());
}
return(pParent->GetPalette());
}
BOOL CGenWindow::OnNCCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
// Store away the "this" pointer ahnd save off the window handle
CGenWindow* pWnd = NULL;
pWnd = (CGenWindow*) lpCreateStruct->lpCreateParams;
ASSERT(pWnd);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) pWnd);
pWnd->AddRef();
TRACE_OUT(("CGenWindow::OnNCCreate"));
ASSERT(NULL == pWnd->m_hwnd);
pWnd->m_hwnd = hwnd;
return(TRUE);
}
void CGenWindow::GetDesiredSize(SIZE *ppt)
{
HWND hwnd = GetWindow();
RECT rcTemp = { 0, 0, 0, 0 };
AdjustWindowRectEx(&rcTemp, GetWindowLong(hwnd, GWL_STYLE), FALSE,
GetWindowLong(hwnd, GWL_EXSTYLE));
ppt->cx = rcTemp.right - rcTemp.left;
ppt->cy = rcTemp.bottom - rcTemp.top;
}
void CGenWindow::OnDesiredSizeChanged()
{
HWND parent = GetParent(GetWindow());
if (NULL != parent)
{
IGenWindow *pParent = FromHandle(parent);
if (NULL != pParent)
{
pParent->OnDesiredSizeChanged();
}
}
// Do this after telling the parents about the change, so their layouts
// will happen before this one
ScheduleLayout();
}
class GWTrackMouseLeave
{
private:
enum { DefIdTimer = 100 };
enum { DefTimeout = 500 };
static HWND m_hwnd;
static UINT_PTR m_idTimer;
static DWORD m_dwWhere;
static void CALLBACK OnTimer(HWND hwnd, UINT uMsg, UINT_PTR idTimer, DWORD dwTime)
{
RECT rc;
GetWindowRect(m_hwnd, &rc);
DWORD dwPos = GetMessagePos();
// If the mouse has not moved since this timer started, then leave it hot
// This allows a reasonable keyboard-only interface
if (m_dwWhere == dwPos)
{
return;
}
POINT ptPos = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };
if (!PtInRect(&rc, ptPos))
{
PostMessage(m_hwnd, WM_MOUSELEAVE, 0, 0);
}
}
public:
GWTrackMouseLeave() {}
static void Track(HWND hwnd, BOOL bTrack)
{
if (!bTrack)
{
if (NULL != m_hwnd && hwnd == m_hwnd)
{
KillTimer(NULL, m_idTimer);
m_hwnd = NULL;
}
return;
}
// Stop any previous tracking
Track(m_hwnd, FALSE);
m_hwnd = hwnd;
m_dwWhere = GetMessagePos();
m_idTimer = SetTimer(NULL, DefIdTimer, DefTimeout, OnTimer);
}
} ;
HWND GWTrackMouseLeave::m_hwnd = NULL;
DWORD GWTrackMouseLeave::m_dwWhere = 0;
UINT_PTR GWTrackMouseLeave::m_idTimer;
static void GWTrackMouseEvent(HWND hwnd, BOOL bTrack)
{
// I need to set up a timer to handle this
GWTrackMouseLeave::Track(hwnd, bTrack);
}
// Set the global Hot control
void CGenWindow::SetHotControl(CGenWindow *pHot)
{
CGenWindow *pGenWindow = NULL;
if (NULL != pHot)
{
for (HWND hwndHot=pHot->GetWindow(); ; hwndHot=GetParent(hwndHot))
{
if (NULL == hwndHot)
{
break;
}
IGenWindow *pWindow = FromHandle(hwndHot);
if (NULL == pWindow)
{
continue;
}
if (SUCCEEDED(pWindow->QueryInterface(__uuidof(CGenWindow),
reinterpret_cast<LPVOID*>(&pGenWindow)))
&& NULL != pGenWindow)
{
pGenWindow->SetHot(TRUE);
// Not all windows may care about the hot state
BOOL bIsHot = pGenWindow->IsHot();
pGenWindow->Release();
if (bIsHot)
{
break;
}
}
pGenWindow = NULL;
}
}
if (g_pCurHot != pGenWindow)
{
if (NULL != g_pCurHot)
{
g_pCurHot->SetHot(FALSE);
GWTrackMouseEvent(g_pCurHot->GetWindow(), FALSE);
ULONG uRef = g_pCurHot->Release();
}
g_pCurHot = pGenWindow;
if (NULL!= g_pCurHot)
{
ULONG uRef = g_pCurHot->AddRef();
// Now we need to track the mouse leaving
GWTrackMouseEvent(g_pCurHot->GetWindow(), TRUE);
}
}
}
// Set this control to be hot
void CGenWindow::SetHot(BOOL bHot)
{
}
// Is this control currently hot
BOOL CGenWindow::IsHot()
{
return(FALSE);
}
LPARAM CGenWindow::GetUserData()
{
return(m_lUserData);
}
HPALETTE CGenWindow::g_hPal = NULL;
BOOL CGenWindow::g_bNeedPalette = TRUE;
HBRUSH CGenWindow::g_hBrush = NULL;
CTopWindowArray *CGenWindow::g_pTopArray = NULL;
// Not particularly robust: we give out our internal palette and trust everybody
// not to delete it
HPALETTE CGenWindow::GetStandardPalette()
{
#include "indeopal.h"
if (!g_bNeedPalette || NULL != g_hPal)
{
return(g_hPal);
}
HDC hDC = ::GetDC(NULL);
if (NULL != hDC)
{
// Use the Indeo palette
// Check out the video mode. We only care about 8 bit mode.
if (8 == ::GetDeviceCaps(hDC, BITSPIXEL) * ::GetDeviceCaps(hDC, PLANES))
{
#ifndef HALFTONE_PALETTE
LOGPALETTE_NM gIndeoPalette = gcLogPaletteIndeo;
if (SYSPAL_NOSTATIC != ::GetSystemPaletteUse(hDC))
{
// Preserve the static colors
int nStaticColors = ::GetDeviceCaps(hDC, NUMCOLORS) >> 1;
if (nStaticColors <= 128)
{
// Get the 10 first entries
::GetSystemPaletteEntries( hDC,
0,
nStaticColors,
&gIndeoPalette.aEntries[0]);
// Get the 10 last entries
::GetSystemPaletteEntries( hDC,
256 - nStaticColors,
nStaticColors,
&gIndeoPalette.aEntries[256 - nStaticColors]);
// Hammer the peFlags
for (; --nStaticColors + 1;)
{
gIndeoPalette.aEntries[nStaticColors].peFlags = 0;
gIndeoPalette.aEntries[255 - nStaticColors].peFlags = 0;
}
}
}
// Build a palette
g_hPal = ::CreatePalette((LOGPALETTE *)&gIndeoPalette);
#else // HALFTONE_PALETTE
g_hPal = ::CreateHalftonePalette(hDC);
#endif // HALFTONE_PALETTE
}
::ReleaseDC(NULL, hDC);
}
g_bNeedPalette = (NULL != g_hPal);
return(g_hPal);
}
void CGenWindow::DeleteStandardPalette()
{
if (NULL != g_hPal)
{
DeleteObject(g_hPal);
g_hPal = NULL;
}
}
// Get the standard palette for drawing
HBRUSH CGenWindow::GetStandardBrush()
{
return(GetSysColorBrush(COLOR_3DFACE));
}
// Delete the standard palette for drawing
void CGenWindow::DeleteStandardBrush()
{
}
// Returns TRUE if the TT exists
BOOL CGenWindow::InitToolInfo(TOOLINFO *pti, LPTSTR pszText)
{
TCHAR szText[MAX_PATH];
if (NULL == pszText)
{
pszText = szText;
}
HWND hwnd = GetWindow();
HWND hwndTooltip = NULL == g_pTopArray ? NULL : g_pTopArray->Find(hwnd);
TOOLINFO &ti = *pti;
ti.cbSize = sizeof(TOOLINFO);
ti.hwnd = hwnd;
ti.hinst = GetWindowInstance(hwnd);
ti.lpszText = pszText;
GetClientRect(hwnd, &ti.rect);
ti.uId = reinterpret_cast<UINT_PTR>(hwnd);
ti.uFlags = TTF_SUBCLASS;
GetSharedTooltipInfo(&ti);
// HACKHACK georgep: The flags keep getting messed up by the tooltip window
UINT uFlags = ti.uFlags;
BOOL bExist = NULL == hwndTooltip ? FALSE : TT_GetToolInfo(hwndTooltip, &ti);
ti.uFlags = uFlags;
if (ti.lpszText == szText)
{
ti.lpszText = NULL;
}
return(bExist);
}
// Set the tooltip for this window
void CGenWindow::SetTooltip(LPCTSTR pszTip)
{
HWND hwnd = GetWindow();
if (NULL == g_pTopArray)
{
g_pTopArray = new CTopWindowArray;
if (NULL == g_pTopArray)
{
return;
}
}
HWND hwndTop = CTopWindowArray::GetTopFrame(hwnd);
HWND hwndTooltip = g_pTopArray->Find(hwndTop);
if (NULL == hwndTooltip)
{
hwndTooltip = CreateWindowEx(0,
TOOLTIPS_CLASS,
NULL,
0, // styles
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
hwndTop,
(HMENU) NULL,
GetWindowInstance(hwnd),
NULL);
if (NULL == hwndTooltip)
{
// Couldn't create the tooltip window
return;
}
g_pTopArray->Add(hwndTop, hwndTooltip);
}
TOOLINFO ti;
BOOL bExist = InitToolInfo(&ti);
ti.lpszText = const_cast<LPTSTR>(pszTip);
if (bExist)
{
TT_SetToolInfo(hwndTooltip, &ti);
}
else
{
TT_AddToolInfo(hwndTooltip, &ti);
}
}
// Remove the tooltip for this window
void CGenWindow::RemoveTooltip()
{
if (NULL == g_pTopArray)
{
// Nothing to do
return;
}
HWND hwndTop = CTopWindowArray::GetTopFrame(GetWindow());
HWND hwndTooltip = g_pTopArray->Find(hwndTop);
BOOL bIsWindow = NULL != hwndTooltip && IsWindow(hwndTooltip);
TOOLINFO ti;
BOOL bExist = bIsWindow && InitToolInfo(&ti);
if (bExist)
{
TT_DelToolInfo(hwndTooltip, &ti);
}
if (NULL != hwndTooltip && (!bIsWindow || 0 == TT_GetToolCount(hwndTooltip)))
{
if (bIsWindow)
{
DestroyWindow(hwndTooltip);
}
g_pTopArray->Remove(hwndTop);
if (0 == g_pTopArray->GetCount())
{
delete g_pTopArray;
g_pTopArray = NULL;
}
}
}
// Get the info necessary for displaying a tooltip
void CGenWindow::GetSharedTooltipInfo(TOOLINFO *pti)
{
}
// Just makes the first child fill the client area
void CFillWindow::Layout()
{
HWND child = GetChild();
if (NULL != child)
{
RECT rc;
GetClientRect(GetWindow(), &rc);
SetWindowPos(child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOZORDER);
}
}
void CFillWindow::GetDesiredSize(SIZE *psize)
{
CGenWindow::GetDesiredSize(psize);
HWND child = GetChild();
if (NULL != child)
{
IGenWindow *pChild = FromHandle(child);
if (NULL != pChild)
{
SIZE sizeTemp;
pChild->GetDesiredSize(&sizeTemp);
psize->cx += sizeTemp.cx;
psize->cy += sizeTemp.cy;
}
}
}
// Get the info necessary for displaying a tooltip
void CFillWindow::GetSharedTooltipInfo(TOOLINFO *pti)
{
CGenWindow::GetSharedTooltipInfo(pti);
// Since the child covers this whole area, we need to change the HWND to
// hook
pti->hwnd = GetChild();
}
CEdgedWindow::CEdgedWindow() :
m_hMargin(0),
m_vMargin(0),
m_pHeader(NULL)
{
}
CEdgedWindow::~CEdgedWindow()
{
SetHeader(NULL);
}
BOOL CEdgedWindow::Create(HWND hwndParent)
{
return(CGenWindow::Create(
hwndParent, // Window parent
0, // ID of the child window
TEXT("NMEdgedWindow"), // Window name
WS_CLIPCHILDREN, // Window style; WS_CHILD|WS_VISIBLE will be added to this
WS_EX_CONTROLPARENT // Extended window style
));
}
HWND CEdgedWindow::GetContentWindow()
{
// If we are hosting an IGenWindow, add on its desired size
HWND child = GetFirstChild(GetWindow());
if (NULL == child)
{
return(NULL);
}
if (NULL != m_pHeader && child == m_pHeader->GetWindow())
{
child = ::GetWindow(child, GW_HWNDNEXT);
}
return(child);
}
static const int LeftIndent = 20;
// Just makes the first child fill the client area - the border
void CEdgedWindow::Layout()
{
int nBorder = GetBorderWidth();
int hBorder = m_hMargin + nBorder;
int vBorder = m_vMargin + nBorder;
HWND hwnd = GetWindow();
RECT rc;
GetClientRect(hwnd, &rc);
CGenWindow *pHeader = GetHeader();
if (NULL != pHeader)
{
SIZE sizeTemp;
pHeader->GetDesiredSize(&sizeTemp);
SetWindowPos(pHeader->GetWindow(), NULL, rc.left+LeftIndent, rc.top,
sizeTemp.cx, sizeTemp.cy, SWP_NOZORDER|SWP_NOACTIVATE);
rc.top += sizeTemp.cy;
}
HWND child = GetContentWindow();
if (NULL != child)
{
SetWindowPos(child, NULL, rc.left+hBorder, rc.top+vBorder,
rc.right-rc.left-2*hBorder, rc.bottom-rc.top-2*vBorder, SWP_NOZORDER|SWP_NOACTIVATE);
}
}
void CEdgedWindow::GetDesiredSize(SIZE *psize)
{
int nBorder = GetBorderWidth();
int hBorder = m_hMargin + nBorder;
int vBorder = m_vMargin + nBorder;
CGenWindow::GetDesiredSize(psize);
psize->cx += 2*hBorder;
psize->cy += 2*vBorder;
// If we are hosting an IGenWindow, add on its desired size
HWND child = GetContentWindow();
if (NULL == child)
{
return;
}
IGenWindow *pChild = FromHandle(child);
if (NULL == pChild)
{
return;
}
SIZE size;
pChild->GetDesiredSize(&size);
psize->cx += size.cx;
psize->cy += size.cy;
CGenWindow *pHeader = GetHeader();
if (NULL != pHeader)
{
SIZE sizeTemp;
pHeader->GetDesiredSize(&sizeTemp);
psize->cy += sizeTemp.cy;
psize->cx = max(psize->cx, sizeTemp.cx+LeftIndent+hBorder);
}
}
void CEdgedWindow::OnPaint(HWND hwnd)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
CGenWindow *pHeader = GetHeader();
if (NULL != pHeader)
{
SIZE sizeTemp;
pHeader->GetDesiredSize(&sizeTemp);
// Make the etch go through the middle of the header
rc.top += (sizeTemp.cy-GetBorderWidth()) / 2;
}
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_RECT);
EndPaint(hwnd, &ps);
}
LRESULT CEdgedWindow::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
case WM_DESTROY:
SetHeader(NULL);
break;
case WM_SIZE:
// Need to invalidate if we bacame larger to redraw the border in the
// right place
InvalidateRect(hwnd, NULL, TRUE);
break;
}
return(CGenWindow::ProcessMessage(hwnd, message, wParam, lParam));
}
void CEdgedWindow::SetHeader(CGenWindow *pHeader)
{
if (NULL != m_pHeader)
{
m_pHeader->Release();
}
m_pHeader = pHeader;
if (NULL != m_pHeader)
{
m_pHeader->AddRef();
}
}
BOOL CFrame::Create(
HWND hWndOwner, // Window owner
LPCTSTR szWindowName, // Window name
DWORD dwStyle, // Window style
DWORD dwEXStyle, // Extended window style
int x, // Window pos: x
int y, // Window pos: y
int nWidth, // Window size: width
int nHeight, // Window size: height
HINSTANCE hInst, // The hInstance to create the window on
HICON hIcon, // The icon for the window
HMENU hmMain, // Window menu
LPCTSTR szClassName // The class name to use
)
{
if (!CFillWindow::Create(hWndOwner, szWindowName, dwStyle, dwEXStyle,
x, y, nWidth, nHeight, hInst, hmMain, szClassName))
{
return(FALSE);
}
if (NULL != hIcon)
{
SendMessage(GetWindow(), WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon));
}
return(TRUE);
}
void CFrame::Resize()
{
Resize(this, 0);
}
void CFrame::Resize(CGenWindow *pThis, WPARAM wParam)
{
SIZE size;
pThis->GetDesiredSize(&size);
SetWindowPos(pThis->GetWindow(), NULL, 0, 0, size.cx, size.cy,
SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
}
void CFrame::OnDesiredSizeChanged()
{
// I should probably look at the window style and only do this if it is
// not resizable. But then that would be wrong sometimes too, so just
// override this if you want different behavior.
AsyncInvoke(Resize, 0);
}
LRESULT CFrame::ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_MSG(hwnd, WM_PALETTECHANGED , OnPaletteChanged);
HANDLE_MSG(hwnd, WM_QUERYNEWPALETTE, OnQueryNewPalette);
}
return(CFillWindow::ProcessMessage(hwnd, uMsg, wParam, lParam));
}
void CFrame::OnPaletteChanged(HWND hwnd, HWND hwndPaletteChange)
{
SelAndRealizePalette(TRUE);
::RedrawWindow(GetWindow(), NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
}
BOOL CFrame::SelAndRealizePalette(BOOL bBackground)
{
BOOL bRet = FALSE;
HPALETTE hPal = GetPalette();
if (NULL == hPal)
{
return(bRet);
}
HWND hwnd = GetWindow();
HDC hdc = ::GetDC(hwnd);
if (NULL != hdc)
{
::SelectPalette(hdc, hPal, bBackground);
bRet = (GDI_ERROR != ::RealizePalette(hdc));
::ReleaseDC(hwnd, hdc);
}
return bRet;
}
BOOL CFrame::OnQueryNewPalette(HWND hwnd)
{
return(SelAndRealizePalette(FALSE));
}
BOOL CFrame::SetForeground()
{
BOOL bRet = FALSE;
HWND hwnd = GetWindow();
if (NULL != hwnd)
{
WINDOWPLACEMENT wp;
wp.length = sizeof(wp);
if (::GetWindowPlacement(hwnd, &wp) &&
((SW_MINIMIZE == wp.showCmd) || (SW_SHOWMINIMIZED == wp.showCmd)))
{
// The window is minimized - restore it:
::ShowWindow(hwnd, SW_RESTORE);
}
else
{
::ShowWindow(hwnd, SW_SHOW);
}
// Bring it to the foreground
SetForegroundWindow(hwnd);
bRet = TRUE;
}
return bRet;
}
void CFrame::MoveEnsureVisible(int x, int y)
{
static const int MinVis = 16;
RECT rcThis;
GetWindowRect(GetWindow(), &rcThis);
// Change to width and height
rcThis.right -= rcThis.left;
rcThis.bottom -= rcThis.top;
RECT rcDesktop;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0);
if ((x+rcThis.right < rcDesktop.left+MinVis) || (x > rcDesktop.right-MinVis))
{
x = (rcDesktop.left + rcDesktop.right - rcThis.right) / 2;
}
if ((y+rcThis.bottom < rcDesktop.top+MinVis) || (y > rcDesktop.bottom-MinVis))
{
y = (rcDesktop.top + rcDesktop.bottom - rcThis.bottom) / 2;
}
SetWindowPos(GetWindow(), NULL, x, y, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
}