755 lines
23 KiB
C++
755 lines
23 KiB
C++
// Copyright (C) Microsoft Corporation 1996, All Rights reserved.
|
|
|
|
#include "header.h"
|
|
#include "popup.h"
|
|
#include "cinput.h"
|
|
#include "hha_strtable.h"
|
|
#include "strtable.h"
|
|
#include "hhshell.h" // g_hwndApi.
|
|
|
|
#include "resource.h"
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Constants
|
|
//
|
|
static const char txtComment[] = ".comment";
|
|
static const char txtTopicID[] = ".topic";
|
|
static const char txtCrLf[] = "\r\n";
|
|
static const char txtSpace[] = " ";
|
|
static const char txtDefaultFileName[] = "/cshelp.txt" ;
|
|
|
|
const int TEXT_PADDING = 5; // padding around the text.
|
|
const int SHADOW_WIDTH = 6;
|
|
const int SHADOW_HEIGHT = 6;
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Globals
|
|
//
|
|
CPopupWindow* g_pPopupWindow;
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Constructor
|
|
//
|
|
CPopupWindow::CPopupWindow()
|
|
{
|
|
ZERO_INIT_CLASS(CPopupWindow);
|
|
m_pfsclient = NULL; // doesn't get cleared, don't know why
|
|
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Constructor
|
|
//
|
|
CPopupWindow::~CPopupWindow()
|
|
{
|
|
CleanUp();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Constructor Helper - Allows reusing window, but breaks caching.
|
|
//
|
|
void CPopupWindow::CleanUp(void)
|
|
{
|
|
if (IsValidWindow(m_hwnd))
|
|
DestroyWindow(m_hwnd);
|
|
|
|
if (m_pfsclient)
|
|
delete m_pfsclient;
|
|
if (m_pszText)
|
|
lcClearFree(&m_pszText);
|
|
if (m_hfont)
|
|
DeleteObject(m_hfont);
|
|
if (m_ptblText)
|
|
delete m_ptblText;
|
|
if (m_pszTextFile)
|
|
lcClearFree((void**) &m_pszTextFile);
|
|
|
|
m_pfsclient = NULL;
|
|
m_ptblText = NULL;
|
|
m_hfont = NULL;
|
|
}
|
|
|
|
void CPopupWindow::SetColors(COLORREF clrForeground, COLORREF clrBackground)
|
|
{
|
|
if (clrForeground != (COLORREF) -1)
|
|
m_clrForeground = clrForeground;
|
|
else
|
|
m_clrForeground = GetSysColor(COLOR_WINDOWTEXT);
|
|
|
|
if (clrBackground != (COLORREF) -1)
|
|
m_clrBackground = clrBackground;
|
|
else
|
|
m_clrBackground = RGB(255, 255, 238); // dithered yellow
|
|
|
|
// If the colors are the same, then use standard window colors
|
|
|
|
HDC hdc = GetWindowDC(m_hwndCaller);
|
|
|
|
if (GetHighContrastFlag() ||
|
|
GetNearestColor(hdc, m_clrBackground) ==
|
|
GetNearestColor(hdc, m_clrForeground)) {
|
|
m_clrForeground = GetSysColor(COLOR_WINDOWTEXT);
|
|
m_clrBackground = GetSysColor(COLOR_WINDOW);
|
|
}
|
|
|
|
ReleaseDC(m_hwndCaller, hdc);
|
|
}
|
|
|
|
// assumes text in m_pszText, result in m_rcWindow
|
|
|
|
#define DEFAULT_DT_FLAGS (DT_EXPANDTABS | DT_NOCLIP | DT_NOPREFIX | DT_WORDBREAK | DT_RTLREADING)
|
|
|
|
void CPopupWindow::CalculateRect(POINT pt)
|
|
{
|
|
RECT rc; // BUGBUG: Broken on multiple monitor systems
|
|
GetClientRect(GetDesktopWindow(), &rc); // get desktop area
|
|
int cyScreen = RECT_HEIGHT(rc);
|
|
int cxScreen = RECT_WIDTH(rc);
|
|
|
|
HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
|
|
HFONT hfontOld;
|
|
if (m_hfont)
|
|
hfontOld = (HFONT) SelectObject(hdc, m_hfont);
|
|
|
|
DrawText(hdc, m_pszText, -1, &rc, DEFAULT_DT_FLAGS | DT_CALCRECT);
|
|
|
|
// Check for an overly wide but short popup
|
|
|
|
if (rc.bottom * 12 < rc.right) {
|
|
rc.right = rc.bottom * 12;
|
|
DrawText(hdc, m_pszText, -1, &rc, DEFAULT_DT_FLAGS | DT_CALCRECT);
|
|
}
|
|
|
|
if (m_hfont)
|
|
SelectObject(hdc, hfontOld);
|
|
|
|
m_rcWindow.left = pt.x - (RECT_WIDTH(rc) / 2);
|
|
m_rcWindow.right = m_rcWindow.left + RECT_WIDTH(rc);
|
|
m_rcWindow.top = pt.y;
|
|
m_rcWindow.bottom = m_rcWindow.top + RECT_HEIGHT(rc);
|
|
|
|
m_rcWindow.left -= m_rcMargin.left;
|
|
m_rcWindow.top -= m_rcMargin.top;
|
|
m_rcWindow.right += m_rcMargin.right;
|
|
m_rcWindow.bottom += m_rcMargin.bottom;
|
|
|
|
if (m_rcWindow.left < 0)
|
|
OffsetRect(&m_rcWindow, -m_rcWindow.left, 0);
|
|
if (m_rcWindow.bottom > cyScreen)
|
|
OffsetRect(&m_rcWindow, 0, cyScreen - m_rcWindow.bottom);
|
|
}
|
|
|
|
static BOOL s_fRegistered;
|
|
const char txtPopupClass[] = "hh_popup";
|
|
|
|
HWND CPopupWindow::doPopupWindow(void)
|
|
{
|
|
if (!s_fRegistered) {
|
|
WNDCLASS wndclass;
|
|
ZeroMemory(&wndclass, sizeof(WNDCLASS));
|
|
wndclass.style = CS_VREDRAW | CS_HREDRAW;
|
|
wndclass.lpfnWndProc = PopupWndProc;
|
|
wndclass.hInstance = _Module.GetModuleInstance();
|
|
wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
|
|
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wndclass.lpszClassName = txtPopupClass;
|
|
s_fRegistered = RegisterClass(&wndclass);
|
|
}
|
|
ASSERT_COMMENT(m_clrForeground != (COLORREF) -1, "Forgot to call SetColors()");
|
|
|
|
char pszPopupTitle[128];
|
|
lstrcpyn(pszPopupTitle, m_pszText, 128);
|
|
|
|
// t-jzybur 4-3-99: Added WS_EX_TOOLWINDOW to prevent a taskbar entry for the
|
|
// popup text.
|
|
m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, txtPopupClass, pszPopupTitle, WS_POPUP,
|
|
m_rcWindow.left, m_rcWindow.top, RECT_WIDTH(m_rcWindow) + SHADOW_WIDTH,
|
|
RECT_HEIGHT(m_rcWindow) + SHADOW_HEIGHT,
|
|
m_hwndCaller, NULL, _Module.GetModuleInstance(), NULL);
|
|
|
|
if (IsValidWindow(m_hwnd)) {
|
|
SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR) this);
|
|
ShowWindow(m_hwnd, SW_SHOW);
|
|
|
|
// t-jzybur 4-3-99: Added SetForegroundWindow to activate the popup text. It could
|
|
// be inactive if the previous popup text was closed by clicking the mouse somwhere
|
|
// outside of the popup window.
|
|
SetForegroundWindow(m_hwnd);
|
|
|
|
// t-jzybur 4-3-99: Instead of capturing the focus and responding to click events,
|
|
// we'll respond to click events and deactivate messages. Its a cleaneer event model,
|
|
// and we won't have the possibility of locking in the hour glass cursor.
|
|
// SetCapture(m_hwnd);
|
|
}
|
|
|
|
return m_hwnd;
|
|
}
|
|
|
|
LRESULT CALLBACK PopupWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC hdc;
|
|
CPopupWindow* pThis;
|
|
RECT rc;
|
|
PAINTSTRUCT ps;
|
|
HFONT hfontOld;
|
|
|
|
switch (msg) {
|
|
case WM_ERASEBKGND:
|
|
hdc = (HDC) wParam;
|
|
pThis = (CPopupWindow*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
GetClipBox(hdc, &rc);
|
|
|
|
return PaintShadowBackground(hwnd, (HDC) wParam, pThis->m_clrBackground);
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
pThis = (CPopupWindow*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
GetClientRect(hwnd, &rc);
|
|
rc.left += pThis->m_rcMargin.left;
|
|
rc.top += pThis->m_rcMargin.top;
|
|
rc.right -= pThis->m_rcMargin.right;
|
|
rc.bottom -= pThis->m_rcMargin.bottom;
|
|
rc.right -= SHADOW_WIDTH;
|
|
rc.bottom -= SHADOW_HEIGHT;
|
|
if (pThis->m_hfont)
|
|
hfontOld = (HFONT) SelectObject(hdc, pThis->m_hfont);
|
|
SetTextColor(hdc, pThis->m_clrForeground);
|
|
SetBkColor(hdc, pThis->m_clrBackground);
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
DrawText(hdc, pThis->m_pszText, -1, &rc, DEFAULT_DT_FLAGS);
|
|
if (pThis->m_hfont)
|
|
SelectObject(hdc, hfontOld);
|
|
EndPaint(hwnd, &ps);
|
|
break;
|
|
|
|
// t-jzybur 4-3-99: Added WndProc handler to close popup text on
|
|
// window deactivation messages.
|
|
case WM_ACTIVATE:
|
|
if (LOWORD(wParam) != WA_INACTIVE) break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_SYSKEYDOWN:
|
|
case WM_KEYDOWN:
|
|
pThis = (CPopupWindow*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
pThis->m_hwnd = NULL;
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0);
|
|
break;
|
|
|
|
// t-jzybur 4-3-99: Removed ReleaseCapture along with SetCapture.
|
|
// case WM_DESTROY:
|
|
// ReleaseCapture();
|
|
// return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
|
|
default:
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: PaintShadowBackground
|
|
|
|
PURPOSE: Draws a border and a shadow around a window
|
|
|
|
PARAMETERS:
|
|
hwnd
|
|
hdc
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
02-Mar-1997 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
static const WORD rgwPatGray[] =
|
|
{ 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA };
|
|
const DWORD PATMERGE = 0x00A000C9;
|
|
|
|
BOOL PaintShadowBackground(HWND hwnd, HDC hdc, COLORREF clrBackground)
|
|
{
|
|
BOOL fStockBrush; // Whether hBrush is a stock object
|
|
|
|
/*
|
|
* First the background of the "fake" window is erased leaving the
|
|
* desktop where the shadow will be.
|
|
*/
|
|
|
|
RECT rcClient; // Will always be client rectangle
|
|
GetClientRect(hwnd, &rcClient);
|
|
RECT rct = rcClient;
|
|
rct.bottom = max(0, rct.bottom - SHADOW_HEIGHT);
|
|
rct.right = max(0, rct.right - SHADOW_WIDTH);
|
|
|
|
HBRUSH hBrush = CreateSolidBrush((clrBackground == (COLORREF) -1 ?
|
|
GetSysColor(COLOR_WINDOW) : clrBackground));
|
|
if (!hBrush)
|
|
return FALSE;
|
|
|
|
UnrealizeObject(hBrush);
|
|
POINT pt;
|
|
pt.x = pt.y = 0;
|
|
ClientToScreen(hwnd, &pt);
|
|
SetBrushOrgEx(hdc, pt.x, pt.y, NULL);
|
|
FillRect(hdc, &rct, hBrush);
|
|
DeleteObject(hBrush);
|
|
|
|
// Next we create the "window" border
|
|
|
|
rct = rcClient;
|
|
rct.bottom = max(0, rct.bottom - SHADOW_HEIGHT);
|
|
rct.right = max(0, rct.right - SHADOW_WIDTH);
|
|
|
|
FrameRect(hdc, &rct, (HBRUSH) GetStockObject(BLACK_BRUSH));
|
|
InflateRect(&rct, -1, -1);
|
|
FrameRect(hdc, &rct, (HBRUSH) GetStockObject(LTGRAY_BRUSH));
|
|
|
|
// Now we create the brush for the the shadow
|
|
|
|
hBrush = 0;
|
|
HBITMAP hbmGray;
|
|
if ((hbmGray = CreateBitmap(8, 8, 1, 1, rgwPatGray)) != NULL) {
|
|
hBrush = CreatePatternBrush(hbmGray);
|
|
DeleteObject(hbmGray);
|
|
fStockBrush = FALSE;
|
|
}
|
|
|
|
// If we cannot create the pattern brush, we try to use a black brush.
|
|
|
|
if (hBrush == 0) {
|
|
if (!(hBrush == GetStockObject(BLACK_BRUSH)))
|
|
return FALSE;
|
|
fStockBrush = TRUE;
|
|
}
|
|
|
|
SetROP2(hdc, R2_MASKPEN);
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
HPEN hpen;
|
|
if ((hpen = (HPEN) GetStockObject(NULL_PEN)) != 0)
|
|
SelectObject(hdc, hpen); // We do not care if this fails
|
|
HBRUSH hbrushTemp = (HBRUSH) SelectObject(hdc, hBrush); // or if this fails, since the
|
|
// paint behavior will be okay.
|
|
|
|
rct = rcClient; // Paint the right side rectangle
|
|
rct.top = rct.top + SHADOW_HEIGHT;
|
|
rct.left = max(0, rct.right - SHADOW_WIDTH);
|
|
PatBlt(hdc, rct.left, rct.top, rct.right - rct.left,
|
|
rct.bottom - rct.top, PATMERGE);
|
|
|
|
rct = rcClient; // Paint the bottom rectangle
|
|
rct.top = max(0, rct.bottom - SHADOW_HEIGHT);
|
|
rct.left = rct.left + SHADOW_WIDTH;
|
|
|
|
// Note overlap by one pixel!
|
|
|
|
rct.right = max(0, rct.right - SHADOW_WIDTH + 1);
|
|
PatBlt(hdc, rct.left, rct.top, rct.right - rct.left,
|
|
rct.bottom - rct.top, PATMERGE);
|
|
|
|
// Cleanup brush
|
|
|
|
if (hbrushTemp != NULL)
|
|
SelectObject(hdc, hbrushTemp);
|
|
if (!fStockBrush)
|
|
DeleteObject(hBrush);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CPopupWindow::ReadTextFile(PCSTR pszFile)
|
|
{
|
|
// If the string pointer is NULL or empty we have to bail.
|
|
if (!pszFile || pszFile[0] == '\0')
|
|
return FALSE ;
|
|
|
|
// Now, verify that we have a text file specified. Urg! More parsing of URL's
|
|
CStr cszFileName;
|
|
PCSTR pszSubFile = GetCompiledName(pszFile, &cszFileName) ;
|
|
if (!pszSubFile || pszSubFile[0] == '\0')
|
|
{
|
|
pszSubFile = txtDefaultFileName ;
|
|
}
|
|
cszFileName += txtDoubleColonSep ;
|
|
cszFileName += pszSubFile ;
|
|
|
|
#if 0//REVIEW:: This never works, because CleanUp resets everything. Removed for safety.
|
|
// Check to see if its cached.
|
|
if (lstrcmpi(cszFileName, m_pszTextFile) == 0)
|
|
return TRUE; // we've cached this file
|
|
#endif
|
|
|
|
CInput input;
|
|
if (!input.Open(cszFileName))
|
|
return FALSE;
|
|
if (m_ptblText)
|
|
delete m_ptblText;
|
|
// Allocate a text buffer.
|
|
CStr cszText;
|
|
if (m_pszTextFile)
|
|
lcFree(m_pszTextFile);
|
|
m_pszTextFile = lcStrDup(cszFileName);
|
|
m_ptblText = new CTable;
|
|
while (input.getline(&cszText)) {
|
|
if (!IsSamePrefix(cszText, txtComment))
|
|
m_ptblText->AddString(cszText);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CreatePopupWindow.
|
|
//
|
|
HWND CPopupWindow::CreatePopupWindow(HWND hwndCaller, PCSTR pszFile,
|
|
HH_POPUP* pPopup)
|
|
{
|
|
if (!pPopup) // TODO: Validate pPopup pointer.
|
|
return NULL ;
|
|
|
|
m_hwndCaller = hwndCaller;
|
|
|
|
//--- Getting the string to display. We can get the string to display in three ways:
|
|
// 1. From a string contained in the HH_POPUP structure.
|
|
// 2. From a string resource in a module.
|
|
// 3. From a txt file embedded in the CHM.
|
|
// This order is the order of least complicated to most complicated. We start with the
|
|
// least complicated method to save loading extra working set.
|
|
// NOTE: A future possibility would be to search in the reverse order. This would allow
|
|
// using a string in the HH_POPUP structure if one wasn't found in the embedded txt file.
|
|
bool bFoundString = false ;
|
|
if ((pPopup->idString == 0) && IsNonEmptyString(pPopup->pszText)) // 1. Get string from HH_POPUP. Only if idString is 0! See HH 3532.
|
|
{
|
|
m_pszText = lcStrDup(pPopup->pszText);
|
|
bFoundString = true ;
|
|
}
|
|
else if (pPopup->idString && pPopup->hinst) // 2. From a string resource in a module.
|
|
{
|
|
m_pszText = (PSTR) lcMalloc(MAX_STRING_RESOURCE_LEN);
|
|
char *pszText = NULL;
|
|
|
|
if ((pszText =(char *) GetStringResource(pPopup->idString, pPopup->hinst)) && *pszText )
|
|
{
|
|
strcpy(m_pszText,pszText);
|
|
bFoundString = true ;
|
|
}
|
|
}
|
|
else if (IsNonEmptyString(pszFile)) // 3. From a txt file embedded in the CHM.
|
|
{
|
|
// Try to read the text file.
|
|
if (ReadTextFile(pszFile))
|
|
{
|
|
ASSERT(m_ptblText);
|
|
for (int pos = 1; pos <= m_ptblText->CountStrings(); pos++) {
|
|
if (IsSamePrefix(m_ptblText->GetPointer(pos), txtTopicID)) {
|
|
PSTR pszNumber = FirstNonSpace(m_ptblText->GetPointer(pos) +
|
|
strlen(txtTopicID));
|
|
if (pszNumber && pPopup->idString == (UINT) Atoi(pszNumber))
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Do we have enough strings?
|
|
if (pos <= m_ptblText->CountStrings())
|
|
{
|
|
CStr cszText(txtZeroLength);
|
|
BOOL fAddSpace = FALSE;
|
|
for (++pos; pos <= m_ptblText->CountStrings(); pos++) {
|
|
PCSTR pszLine = m_ptblText->GetPointer(pos);
|
|
if (*pszLine == '.')
|
|
break;
|
|
if (!*pszLine) {
|
|
if (pos + 1 <= m_ptblText->CountStrings()) {
|
|
pszLine = m_ptblText->GetPointer(pos + 1);
|
|
if (*pszLine != '.')
|
|
cszText += txtCrLf;
|
|
}
|
|
fAddSpace = FALSE;
|
|
continue;
|
|
}
|
|
else if (fAddSpace)
|
|
cszText += txtSpace;
|
|
cszText += pszLine;
|
|
fAddSpace = TRUE;
|
|
}
|
|
cszText.TransferPointer(&m_pszText);
|
|
bFoundString = true ;
|
|
}
|
|
else
|
|
{
|
|
if (IsHelpAuthor(NULL))
|
|
{
|
|
char szMsgBuf[256];
|
|
wsprintf(szMsgBuf, pGetDllStringResource(IDS_HHA_MISSING_TP_TXT),
|
|
pPopup->idString, pszFile);
|
|
doAuthorMsg(IDS_IDH_GENERIC_STRING, szMsgBuf);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We couldn't read the text file in. Will display error popup...
|
|
doAuthorMsg(IDS_CANT_OPEN, pszFile);
|
|
}
|
|
}
|
|
|
|
// This needs to be true when displaying static strings loaded from the resource
|
|
// because the font specified by the user might not be appropriate for the
|
|
// string loaded from the resource.
|
|
//
|
|
BOOL bUseDefaultFont = FALSE;
|
|
|
|
//--- Do we have a string?
|
|
if (!bFoundString)
|
|
{
|
|
if (m_pszText)
|
|
{
|
|
lcClearFree(&m_pszText);
|
|
m_pszText = NULL ;
|
|
}
|
|
|
|
m_pszText = (PSTR) lcMalloc(MAX_STRING_RESOURCE_LEN);
|
|
|
|
char *pszText;
|
|
|
|
if ((pszText = (char *) GetStringResource(IDS_IDH_MISSING_CONTEXT)) )
|
|
{
|
|
strcpy(m_pszText,pszText);
|
|
bUseDefaultFont = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Dang it! We can't even get our own string.
|
|
CleanUp() ;
|
|
return NULL ;
|
|
}
|
|
}
|
|
|
|
//--- Okay, now we can display the string.
|
|
m_rcMargin.left = (pPopup->rcMargins.left >= 0 ?
|
|
pPopup->rcMargins.left : TEXT_PADDING);
|
|
m_rcMargin.top = (pPopup->rcMargins.top >= 0 ?
|
|
pPopup->rcMargins.top : TEXT_PADDING);
|
|
m_rcMargin.right = (pPopup->rcMargins.right >= 0 ?
|
|
pPopup->rcMargins.right : TEXT_PADDING);
|
|
m_rcMargin.bottom = (pPopup->rcMargins.bottom >= 0 ?
|
|
pPopup->rcMargins.bottom : TEXT_PADDING);
|
|
if (IsNonEmptyString(pPopup->pszFont) && !bUseDefaultFont) {
|
|
if (m_hfont)
|
|
DeleteObject(m_hfont);
|
|
m_hfont = CreateUserFont(pPopup->pszFont);
|
|
}
|
|
else if (!m_hfont)
|
|
m_hfont = CreateUserFont(GetStringResource(IDS_DEFAULT_RES_FONT));
|
|
|
|
// Get a default location to display.
|
|
POINT pt = pPopup->pt;
|
|
if (pt.x == -1 && pt.x == -1 && IsWindow(hwndCaller))
|
|
{
|
|
RECT rcWindow;
|
|
GetWindowRect(hwndCaller, &rcWindow);
|
|
pt.x = rcWindow.left + (RECT_WIDTH(rcWindow) / 2);
|
|
pt.y = rcWindow.top;
|
|
}
|
|
|
|
CalculateRect(pt);
|
|
SetColors(pPopup->clrForeground, pPopup->clrBackground);
|
|
|
|
return doPopupWindow();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Handle the HH_TP_HELP_CONTEXTMENU command. Display the What's this menu.
|
|
//
|
|
HWND
|
|
doTpHelpContextMenu(HWND hwndMain, LPCSTR pszFile, DWORD_PTR ulData)
|
|
{
|
|
/*
|
|
In WinHelp we put up a little menu for this message. In HTML Help we don't.
|
|
So we remove the menu and just handle this like HH_TP_HELP_WM_HELP.
|
|
*/
|
|
return doTpHelpWmHelp(hwndMain, pszFile, ulData) ;
|
|
/*
|
|
ASSERT(IsWindow(hwndMain)) ;
|
|
|
|
// Create the menu.
|
|
HMENU hMenu = LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_WHATSTHIS_MENU)) ;
|
|
ASSERT(hMenu) ;
|
|
|
|
// Get the Popup Menu
|
|
HMENU hPopupMenu = GetSubMenu(hMenu, 0) ;
|
|
|
|
//--- Get the location to display the menu
|
|
POINT pt ;
|
|
// Use the mouse cursor position.
|
|
GetCursorPos(&pt) ;
|
|
|
|
// Set the style of the menu.
|
|
DWORD style = TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD ;
|
|
|
|
// Display the menu.
|
|
int iCmd = TrackPopupMenuEx(hPopupMenu,
|
|
style ,
|
|
pt.x, pt.y,
|
|
g_hwndApi ? g_hwndApi : hwndMain, // We have to have a window in the current thread!
|
|
NULL) ;
|
|
#ifdef _DEBUG
|
|
DWORD err ;
|
|
if (iCmd == 0)
|
|
{
|
|
err = ::GetLastError() ;
|
|
}
|
|
#endif
|
|
|
|
// Cleanup
|
|
DestroyMenu(hMenu) ;
|
|
|
|
// Act on the item.
|
|
if (iCmd == IDM_WHATSTHIS)
|
|
{
|
|
return doTpHelpWmHelp(hwndMain, pszFile, ulData) ;
|
|
}
|
|
else
|
|
{
|
|
return NULL ;
|
|
}
|
|
*/
|
|
}
|
|
|
|
///////////////// Dialog control parsing from helpcall.c in user32 ///////
|
|
|
|
const int MAX_ATTEMPTS = 5; // maximum -1 id controls to search through
|
|
|
|
HWND doTpHelpWmHelp(HWND hwndMain, LPCSTR pszFile, DWORD_PTR ulData)
|
|
{
|
|
int id = GetDlgCtrlID(hwndMain); // get control id
|
|
int idSave = id;
|
|
|
|
DWORD* pid = (DWORD*) ulData;
|
|
|
|
if ((short) id == -1)
|
|
{ // static control?
|
|
HWND hwndCtrl = hwndMain;
|
|
int cAttempts = 0;
|
|
|
|
// For non-id controls (typically static controls), step
|
|
// through to the next tab item. Keep finding the next tab
|
|
// item until we find a valid id, or we have tried
|
|
// MAX_ATTEMPTS times.
|
|
|
|
do
|
|
{
|
|
hwndCtrl = GetNextWindow(hwndCtrl, GW_HWNDNEXT);
|
|
|
|
// hwndCtrl will be NULL if hwndMain doesn't have a parent,
|
|
// or if there are no tab stops.
|
|
|
|
if (!hwndCtrl)
|
|
{
|
|
DBWIN("GetNextDlgHelpItem failed.");
|
|
return NULL;
|
|
}
|
|
|
|
id = GetDlgCtrlID(hwndCtrl);
|
|
}
|
|
while ((id == -1) && (++cAttempts < MAX_ATTEMPTS));
|
|
}
|
|
|
|
// Find the id value in array of id/help context values
|
|
|
|
for (int i = 0; pid[i]; i += 2)
|
|
{
|
|
if ((int) pid[i] == id)
|
|
break;
|
|
}
|
|
|
|
// Create a popup structure to pass to doDisplayTextPopup.
|
|
HH_POPUP popup ;
|
|
memset(&popup, 0, sizeof(popup)) ;
|
|
|
|
// We want the default window size.
|
|
popup.pt.x = -1 ;
|
|
popup.pt.y = -1 ;
|
|
|
|
// We want the default margins.
|
|
popup.rcMargins.top =
|
|
popup.rcMargins.bottom =
|
|
popup.rcMargins.left =
|
|
popup.rcMargins.right = -1 ;
|
|
|
|
if (!pid[i])
|
|
{
|
|
popup.hinst = _Module.GetResourceInstance();
|
|
|
|
switch (id) {
|
|
case IDOK:
|
|
popup.idString = IDS_IDH_OK;
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
popup.idString = IDS_IDH_CANCEL;
|
|
break;
|
|
|
|
case IDHELP:
|
|
popup.idString = IDS_IDH_HELP;
|
|
break;
|
|
|
|
default:
|
|
if (IsHelpAuthor(NULL))
|
|
{
|
|
char szMsgBuf[256];
|
|
wsprintf(szMsgBuf,
|
|
pGetDllStringResource(IDS_HHA_MISSING_HELP_ID), idSave);
|
|
doAuthorMsg(IDS_IDH_GENERIC_STRING, szMsgBuf);
|
|
}
|
|
popup.idString = IDS_IDH_MISSING_CONTEXT;
|
|
break;
|
|
}
|
|
return doDisplayTextPopup(hwndMain, NULL, &popup) ;
|
|
}
|
|
else
|
|
{
|
|
ulData = pid[i + 1];
|
|
if (ulData == (DWORD) -1)
|
|
return NULL; // caller doesn't want help after all
|
|
if (IsHelpAuthor(NULL))
|
|
{
|
|
char szMsgBuf[256];
|
|
wsprintf(szMsgBuf, pGetDllStringResource(IDS_HHA_HELP_ID),
|
|
(int) pid[i], (int) pid[i + 1], pszFile);
|
|
SendStringToParent(szMsgBuf);
|
|
}
|
|
|
|
// Set the id of the string that we want.
|
|
popup.idString = (UINT)ulData;
|
|
|
|
return doDisplayTextPopup(hwndMain, pszFile, &popup) ;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// doDisplaytextPopup
|
|
//
|
|
HWND
|
|
doDisplayTextPopup(HWND hwndMain, LPCSTR pszFile, HH_POPUP* pPopup)
|
|
{
|
|
if (!g_pPopupWindow)
|
|
{
|
|
g_pPopupWindow = new CPopupWindow;
|
|
}
|
|
g_pPopupWindow->CleanUp();
|
|
|
|
return g_pPopupWindow->CreatePopupWindow(hwndMain, pszFile, pPopup);
|
|
}
|