windows-nt/Source/XPSP1/NT/enduser/windows.com/wuau/wuauclt/customlb.cpp

1031 lines
29 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "pch.h"
#pragma hdrstop
void SetMYLBAcc(HWND hListWin);
void DrawMYLBFocus(HWND hListWin, LPDRAWITEMSTRUCT lpdis, MYLBFOCUS enCurFocus, INT nCurFocusId);
void DrawItem(LPDRAWITEMSTRUCT lpdis, BOOL fSelectionDisabled);
void DrawTitle(HDC hDC, LBITEM * plbi, RECT rc);
void DrawRTF(HDC hDC, LBITEM * plbi, const RECT & rc /*, BOOL bHit*/);
void DrawDescription(HDC hDC, LBITEM * plbi, RECT & rc);
void DrawBitmap(HDC hDC, LBITEM * plbi, const RECT & rc, BOOL fSel, BOOL fSelectionDisabled);
void CalcTitleFocusRect(const RECT &rcIn, RECT & rcOut);
void CalcRTFFocusRect(const RECT &rcIn, RECT & rcOut);
int CalcDescHeight(HDC hDC, LBITEM * plbi, int cx);
int CalcRTFHeight(HDC hDC, LBITEM * plbi);
int CalcRTFWidth(HDC hDC, LBITEM * plbi);
int CalcTitleHeight(HDC hDC, LBITEM * plbi, int cx);
void CalcItemLocation(HDC hDC, LBITEM * plbi, const RECT & rc);
void ToggleSelection(HWND hDlg, HWND hListWin, LBITEM *pItem);
void AddItem(LPTSTR tszTitle, LPTSTR tszDesc, LPTSTR tszRTF, int index, BOOL fSelected, BOOL fRTF);
BOOL CurItemHasRTF(HWND hListWin);
void RedrawMYLB(HWND hwndLB);
void LaunchRTF(HWND hListWin);
HBITMAP ghBmpGrayOut; //=NULL;
HBITMAP ghBmpCheck; // = NULL;
HBITMAP ghBmpClear; // = NULL;
HFONT ghFontUnderline; // = NULL;
HFONT ghFontBold; // = NULL;
HFONT ghFontNormal; // = NULL;
HWND ghWndList; //=NULL;
MYLBFOCUS gFocus;
INT gFocusItemId;
TCHAR gtszRTFShortcut[MAX_RTFSHORTCUTDESC_LENGTH];
void LaunchRTF(HWND hListWin)
{
HWND hDlg = GetParent(hListWin);
int i = (LONG)SendMessage(ghWndList, LB_GETCURSEL, 0, 0);
if(i != LB_ERR)
{
LBITEM* pItem = (LBITEM*)SendMessage(hListWin, LB_GETITEMDATA, i, 0);
if (pItem && pItem->bRTF)
{
DEBUGMSG("MYLB show RTF for item %S", pItem->szTitle);
PostMessage(GetParent(hDlg), AUMSG_SHOW_RTF, LOWORD(pItem->m_index), 0);
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Overwrite hListWin's accessibility behavior using dynamic annotation server
////////////////////////////////////////////////////////////////////////////////
void SetMYLBAcc(HWND hListWin)
{
IAccPropServices * pAccPropSvc = NULL;
HRESULT hr = CoCreateInstance(CLSID_AccPropServices,
NULL,
CLSCTX_INPROC_SERVER,
IID_IAccPropServices,
(void **) &pAccPropSvc);
if( hr == S_OK && pAccPropSvc )
{
MYLBAccPropServer* pMYLBPropSrv = new MYLBAccPropServer( pAccPropSvc );
if( pMYLBPropSrv )
{
MSAAPROPID propids[4];
propids[0] = PROPID_ACC_NAME;
propids[1] = PROPID_ACC_STATE;
propids[2] = PROPID_ACC_ROLE;
propids[3] = PROPID_ACC_DESCRIPTION;
pAccPropSvc->SetHwndPropServer( hListWin, OBJID_CLIENT, 0, propids, 4, pMYLBPropSrv, ANNO_CONTAINER);
pMYLBPropSrv->Release();
}
pAccPropSvc->Release();
}
else
{
DEBUGMSG("WANRING: WUAUCLT Fail to create object AccPropServices with error %#lx", hr);
}
// Mark the listbox so that the server can tell if it alive
SetProp(hListWin, MYLBALIVEPROP, (HANDLE)TRUE);
}
/*void DumpRect(LPCTSTR tszName, RECT rc)
{
DEBUGMSG("DumpRect %S at (%d, %d, %d, %d)", tszName, rc.left, rc.top, rc.right, rc.bottom);
}
*/
void DrawItem(LPDRAWITEMSTRUCT lpdis, BOOL fSelectionDisabled)
{
LRESULT lResult = SendMessage(lpdis->hwndItem, LB_GETITEMDATA, lpdis->itemID, 0);
if (LB_ERR == lResult)
{
return;
}
LBITEM * plbi = (LBITEM*) lResult;
CalcItemLocation(lpdis->hDC, plbi, lpdis->rcItem);
// Draw the title of the item
DrawTitle(lpdis->hDC, plbi, plbi->rcTitle);
// Draw the text of the item
DrawDescription(lpdis->hDC, plbi, plbi->rcText);
// Draw the bitmap
DrawBitmap(lpdis->hDC, plbi, plbi->rcBitmap, plbi->bSelect, fSelectionDisabled);
// draw the Read this First
DrawRTF(lpdis->hDC, plbi, plbi->rcRTF);
}
BOOL CurItemHasRTF(HWND hListWin)
{
int i = (LONG)SendMessage(hListWin, LB_GETCURSEL, 0, 0);
if (LB_ERR == i)
{
return FALSE;
}
LBITEM *pItem = (LBITEM*)SendMessage(hListWin, LB_GETITEMDATA, (WPARAM)i, 0);
return pItem->bRTF;
}
BOOL fDisableSelection(void)
{
AUOPTION auopt;
if (SUCCEEDED(gInternals->m_getServiceOption(&auopt))
&& auopt.fDomainPolicy && AUOPTION_SCHEDULED == auopt.dwOption)
{
return TRUE;
}
return FALSE;
}
void ToggleSelection(HWND hDlg, HWND hListWin, LBITEM *pItem)
{
//DEBUGMSG("ToggleSelection()");
if (NULL == hDlg || NULL == hListWin || NULL == pItem || pItem->m_index >= gInternals->m_ItemList.Count())
{
AUASSERT(FALSE); //should never reach here.
return;
}
HDC hDC = GetDC(hListWin);
if (NULL == hDC)
{
return;
}
pItem->bSelect = !pItem->bSelect;
DrawBitmap(hDC, pItem, pItem->rcBitmap, pItem->bSelect, FALSE); //obviously selection is allowed
#ifndef TESTUI
gInternals->m_ItemList[pItem->m_index].SetStatus(pItem->bSelect ? AUCATITEM_SELECTED : AUCATITEM_UNSELECTED);
#endif
PostMessage(GetParent(hDlg), AUMSG_SELECTION_CHANGED, 0, 0);
ReleaseDC(hListWin, hDC);
}
void RedrawMYLB(HWND hwndLB)
{
//DEBUGMSG("REDRAW MYLB ");
InvalidateRect(ghWndList, NULL, TRUE);
UpdateWindow(ghWndList);
}
void CalcTitleFocusRect(const RECT &rcIn, RECT & rcOut)
{
rcOut = rcIn;
rcOut.right -= TITLE_MARGIN * 2/3;
rcOut.top += SECTION_SPACING * 2/3;
rcOut.bottom -= SECTION_SPACING*2/3 ;
}
void CalcRTFFocusRect(const RECT &rcIn, RECT & rcOut)
{
rcOut = rcIn;
rcOut.left -=3;
rcOut.right +=3;
rcOut.top -= 2;
rcOut.bottom += 2;
}
void DrawMYLBFocus(HWND hListWin, LPDRAWITEMSTRUCT lpdis, MYLBFOCUS enCurFocus, INT nCurFocusId)
{
LBITEM * pItem;
LRESULT lResult;
//DEBUGMSG("DrawMYLBFocus for current focus %d with Item %d", enCurFocus, nCurFocusId);
RECT rcNew;
if (nCurFocusId != lpdis->itemID)
{
return;
}
if (GetFocus() != ghWndList )
{
// DEBUGMSG("CustomLB doesn't have focus");
return;
}
lResult = SendMessage(hListWin, LB_GETITEMDATA, lpdis->itemID, 0);
if (LB_ERR == lResult)
{
DEBUGMSG("DrawMYLBFocus() fail to get item data");
goto done;
}
pItem = (LBITEM*) lResult;
if (!EqualRect(&lpdis->rcItem, &pItem->rcItem))
{
CalcItemLocation(lpdis->hDC, pItem, lpdis->rcItem);
}
if (enCurFocus == MYLB_FOCUS_RTF)
{
CalcRTFFocusRect(pItem->rcRTF, rcNew);
}
if (enCurFocus == MYLB_FOCUS_TITLE)
{
CalcTitleFocusRect(pItem->rcTitle, rcNew);
}
DrawFocusRect(lpdis->hDC, &rcNew); //set new focus rect
done:
return;
}
LRESULT CallDefLBWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static WNDPROC s_defLBWndProc = NULL;
if (NULL == s_defLBWndProc)
{
s_defLBWndProc = (WNDPROC) GetWindowLongPtr(hWnd, GWLP_USERDATA);
}
return CallWindowProc(s_defLBWndProc, hWnd, message, wParam, lParam);
}
LRESULT CALLBACK newLBWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_GETDLGCODE:
return DLGC_WANTALLKEYS;
case WM_KEYDOWN:
switch(wParam)
{
case VK_RIGHT:
case VK_LEFT:
if (MYLB_FOCUS_RTF == gFocus)
{
DEBUGMSG("LB change focus to Title");
gFocus = MYLB_FOCUS_TITLE;
RedrawMYLB(ghWndList);
}
else if (MYLB_FOCUS_TITLE == gFocus && CurItemHasRTF(hWnd))
{
DEBUGMSG("LB change focus to RTF");
gFocus = MYLB_FOCUS_RTF;
RedrawMYLB(ghWndList);
}
break;
case VK_F1:
if (GetKeyState(VK_SHIFT)<0)
{//SHIFT down
LaunchRTF(hWnd);
return 0;
}
break;
case VK_RETURN:
if (MYLB_FOCUS_RTF == gFocus)
{
DEBUGMSG("MYLB show RTF ");
LaunchRTF(hWnd);
}
break;
case VK_TAB:
PostMessage(GetParent(hWnd), WM_NEXTDLGCTL, 0, 0L);
break;
default:
return CallDefLBWndProc(hWnd, message, wParam, lParam);
}
return 0;
case WM_KEYUP:
switch(wParam)
{
case VK_RIGHT:
case VK_LEFT:
break;
case VK_RETURN:
break;
default:
return CallDefLBWndProc(hWnd, message, wParam, lParam);
}
return 0;
default:
break;
}
return CallDefLBWndProc(hWnd, message, wParam, lParam);
}
// Message handler for Custom List box.
LRESULT CALLBACK CustomLBWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static int lenx;
int clb;
LBITEM *item;
static BOOL s_fSelectionDisabled;
switch (message)
{
case WM_CREATE:
{
RECT rcLst, rcDlg;
LOGFONT lf;
HFONT parentFont;
TCHAR tszRTF[MAX_RTF_LENGTH] = _T("");
GetClientRect(hDlg, &rcDlg);
rcDlg.top += 2;
rcDlg.bottom -= 3;
rcDlg.left += 2;
rcDlg.right -= 2;
s_fSelectionDisabled = fDisableSelection();
ghWndList = CreateWindow(_T("listbox"), NULL,
WS_CHILD | WS_VISIBLE | LBS_NOTIFY | LBS_OWNERDRAWVARIABLE |
LBS_NOINTEGRALHEIGHT | LBS_HASSTRINGS | LBS_WANTKEYBOARDINPUT |
WS_VSCROLL | WS_HSCROLL | WS_TABSTOP,
rcDlg.left, rcDlg.top, rcDlg.right - rcDlg.left, rcDlg.bottom - rcDlg.top,
hDlg, NULL, ghInstance, NULL);
if (NULL == ghWndList)
{
return -1;
}
WNDPROC defLBWndProc = (WNDPROC) GetWindowLongPtr(ghWndList, GWLP_WNDPROC);
SetWindowLongPtr(ghWndList, GWLP_USERDATA, (LONG_PTR) defLBWndProc);
SetWindowLongPtr(ghWndList, GWLP_WNDPROC, (LONG_PTR) newLBWndProc);
HDC hDC = GetDC(ghWndList);
GetWindowRect(hDlg, &rcDlg);
GetWindowRect(ghWndList, &rcLst);
lenx = rcLst.right - rcLst.left;
// Load read this first text from resource file
LoadString(ghInstance, IDS_READTHISFIRST, tszRTF, MAX_RTF_LENGTH);
// load keyboard shortcut description for Read this First
LoadString(ghInstance, IDS_RTFSHORTCUT, gtszRTFShortcut, MAX_RTFSHORTCUTDESC_LENGTH);
// Load the bitmaps
ghBmpClear = (HBITMAP)LoadImage(ghInstance, MAKEINTRESOURCE(IDB_CLEAR), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS | LR_CREATEDIBSECTION);
ghBmpCheck = (HBITMAP)LoadImage(ghInstance, MAKEINTRESOURCE(IDB_CHECK), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS | LR_CREATEDIBSECTION);
ghBmpGrayOut = (HBITMAP)LoadImage(ghInstance, MAKEINTRESOURCE(IDB_GRAYOUT), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS | LR_CREATEDIBSECTION);
// Create BOLD and Italic fonts
ZeroMemory(&lf, sizeof(lf));
//fixcode: check return value of GetCurrentObject()
GetObject(GetCurrentObject(hDC, OBJ_FONT), sizeof(lf), &lf);
//fixcode: check return value of GetParent()
parentFont = (HFONT)SendMessage(GetParent(hDlg), WM_GETFONT, 0, 0);
SendMessage(hDlg, WM_SETFONT, (WPARAM)parentFont, FALSE);
SelectObject(hDC, parentFont);
//fixcode: check return value of GetCurrentObject()
GetObject(GetCurrentObject(hDC, OBJ_FONT), sizeof(lf), &lf);
lf.lfUnderline = TRUE;
lf.lfWeight = FW_NORMAL;
//fixcode: check return value of CreateFontIndirect()
ghFontUnderline = CreateFontIndirect(&lf);
lf.lfUnderline = FALSE;
lf.lfWeight = FW_NORMAL;
//fixcode: check return value of CreateFontIndirect()
ghFontNormal = CreateFontIndirect(&lf);
lf.lfUnderline = FALSE;
lf.lfWeight = FW_HEAVY;
//fixcode: check return value of CreateFontIndirect()
ghFontBold = CreateFontIndirect(&lf);
ReleaseDC(ghWndList, hDC);
#ifdef TESTUI
{
AddItem(_T("Test 1 Very long title Test 1 Very long title Test 1 Very long title Test 1 Very long title "),
_T("Description"), tszRTF, 0, TRUE, TRUE);
AddItem(_T("Test 2"), _T("Another description. No RTF"), tszRTF,0, TRUE, FALSE);
}
#else
{
for (UINT i = 0; i < gInternals->m_ItemList.Count(); i++)
{
DEBUGMSG("selected[%d] = %lu", i, gInternals->m_ItemList[i].dwStatus());
if ( !gInternals->m_ItemList[i].fHidden() )
{
AddItem(gInternals->m_ItemList[i].bstrTitle(),
gInternals->m_ItemList[i].bstrDescription(),
tszRTF,
i,
gInternals->m_ItemList[i].fSelected(),
IsRTFDownloaded(gInternals->m_ItemList[i].bstrRTFPath(), GetSystemDefaultLangID()));
}
}
}
#endif
SendMessage(ghWndList, LB_SETCURSEL, 0, 0);
gFocus = MYLB_FOCUS_TITLE;
gFocusItemId = 0;
SetMYLBAcc(ghWndList);
return 0;
}
case WM_MOVE:
{
RECT rcList;
GetWindowRect(ghWndList, &rcList); // need this to force LB to realize it got moved
return(TRUE);
}
case WM_SETCURSOR:
{
if (ghWndList == (HWND)wParam && LOWORD(lParam) == HTCLIENT && HIWORD(lParam) == WM_MOUSEMOVE)
{
POINT pt;
RECT rc;
GetCursorPos(&pt);
if (0 == MapWindowPoints(NULL, ghWndList, &pt, 1))
{
DEBUGMSG("MYLBWndProc MapWindowPoints failed");
return FALSE;
}
DWORD dwPos;
dwPos = MAKELONG( pt.x, pt.y);
DWORD dwItem = (LONG)SendMessage(ghWndList, LB_ITEMFROMPOINT, 0, dwPos);
if (LOWORD(dwItem) == -1)
return(FALSE);
item = (LBITEM*)SendMessage(ghWndList, LB_GETITEMDATA, LOWORD(dwItem), 0);
SendMessage(ghWndList, LB_GETITEMRECT, LOWORD(dwItem), (LPARAM)&rc);
if (!EqualRect(&rc, &item->rcItem))
{
HDC hDC = GetDC(ghWndList);
CalcItemLocation(hDC, item, rc);
ReleaseDC(ghWndList, hDC);
}
if (item->bRTF && PtInRect(&item->rcRTF, pt))
{
// DEBUGMSG("Change Cursor to hand in MOUSEMOVE");
SetCursor(ghCursorHand);
return TRUE;
}
return FALSE;
}
else
if (ghWndList == (HWND)wParam && LOWORD(lParam) == HTCLIENT && HIWORD(lParam) == WM_LBUTTONDOWN)
{
POINT pt;
RECT rc;
GetCursorPos(&pt);
if (0 == MapWindowPoints(NULL, ghWndList, &pt, 1))
{
DEBUGMSG("MYLBWndProc MapWindowPoints failed");
return FALSE;
}
DWORD dwPos;
dwPos = MAKELONG( pt.x, pt.y);
DWORD dwItem = (LONG)SendMessage(ghWndList, LB_ITEMFROMPOINT, 0, dwPos);
if (LOWORD(dwItem) == -1)
return(FALSE);
item = (LBITEM*)SendMessage(ghWndList, LB_GETITEMDATA, LOWORD(dwItem), 0);
SendMessage(ghWndList, LB_GETITEMRECT, LOWORD(dwItem), (LPARAM)&rc);
if (!EqualRect(&rc, &item->rcItem))
{
HDC hDC = GetDC(ghWndList);
CalcItemLocation(hDC, item, rc);
ReleaseDC(ghWndList, hDC);
}
// Are we clicking on the Title?
if (PtInRect(&item->rcBitmap, pt))
{
if (!s_fSelectionDisabled)
{
ToggleSelection(hDlg, ghWndList, item);
}
// DEBUGMSG("WM_SETCURSOR change gFocus to TITLE");
gFocus = MYLB_FOCUS_TITLE;
gFocusItemId = dwItem;
RedrawMYLB(ghWndList);
return TRUE;
}
// or are we clicking on the RTF?
if (item->bRTF && PtInRect(&item->rcRTF, pt))
{
PostMessage(GetParent(hDlg), AUMSG_SHOW_RTF, LOWORD(item->m_index), 0);
SetCursor(ghCursorHand);
//DEBUGMSG("WM_SETCURSOR change gFocus to RTF");
gFocus = MYLB_FOCUS_RTF;
gFocusItemId = dwItem;
RedrawMYLB(ghWndList);
return TRUE;
}
return FALSE;
}
return FALSE;
}
case WM_MEASUREITEM:
{
LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam ;
HDC hdc = GetDC(ghWndList);
LBITEM * plbi = (LBITEM*)lpmis->itemData;
lpmis->itemHeight = CalcTitleHeight(hdc, plbi, lenx - XBITMAP - 2* TITLE_MARGIN)
+ CalcDescHeight(hdc, plbi, lenx - XBITMAP )
+ CalcRTFHeight(hdc, plbi) + 4 * SECTION_SPACING;
ReleaseDC(ghWndList, hdc);
return TRUE;
}
case WM_PAINT:
PAINTSTRUCT ps;
RECT borderRect;
BeginPaint(hDlg, &ps);
GetClientRect(hDlg, &borderRect);
DrawEdge(ps.hdc, &borderRect, EDGE_ETCHED, BF_RECT);
EndPaint(hDlg, &ps);
break;
case WM_NEXTDLGCTL:
PostMessage(GetParent(hDlg), WM_NEXTDLGCTL, 0, 0L);
return 0;
case WM_KEYUP:
//DEBUGMSG("MYLB got KEYUP key %d", wParam);
switch(wParam)
{
case VK_TAB:
case VK_DOWN:
case VK_UP:
SetFocus(ghWndList);
return 0;
default:
break;
}
break;
case WM_VKEYTOITEM:
{
//DEBUGMSG("WM_VKEYTOITEM got char %d", LOWORD(wParam));
if (LOWORD(wParam) != VK_SPACE)
{
return -1;
}
if (MYLB_FOCUS_TITLE == gFocus)
{
int i = (LONG)SendMessage(ghWndList, LB_GETCURSEL, 0, 0);
if (LB_ERR == i)
{
return -2;
}
item = (LBITEM*)SendMessage(ghWndList, LB_GETITEMDATA, i, 0);
if (!s_fSelectionDisabled)
{
ToggleSelection(hDlg, ghWndList, item);
}
return -2;
}
if (MYLB_FOCUS_RTF == gFocus)
{
LaunchRTF(ghWndList);
}
return -2;
}
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) lParam;
// If there are no list box items, skip this message.
if (lpdis->itemID == -1)
{
break;
}
// Draw the bitmap and text for the list box item. Draw a
// rectangle around the bitmap if it is selected.
switch (lpdis->itemAction)
{
case ODA_SELECT:
case ODA_DRAWENTIRE:
//DEBUGMSG("MYLB WM_DRAWITEM ODA_DRAWENTIRE for %d", lpdis->itemID);
DrawItem(lpdis, s_fSelectionDisabled);
DrawMYLBFocus(ghWndList, lpdis, gFocus, gFocusItemId);
break;
case ODA_FOCUS:
if (lpdis->itemID != gFocusItemId)
{
gFocusItemId = lpdis->itemID;
gFocus = MYLB_FOCUS_TITLE;
}
//DEBUGMSG("MYLB ODA_FOCUS change focus to %d", gFocusItemId);
DrawItem(lpdis, s_fSelectionDisabled);
DrawMYLBFocus(ghWndList, lpdis, gFocus, gFocusItemId);
break;
}
return TRUE;
}
case WM_DESTROY:
// need to cleanup the fonts
if (ghFontBold)
DeleteObject(ghFontBold);
if (ghFontUnderline)
DeleteObject(ghFontUnderline);
if (ghFontNormal)
DeleteObject(ghFontNormal);
if (ghBmpCheck)
DeleteObject(ghBmpCheck);
if (ghBmpGrayOut)
DeleteObject(ghBmpGrayOut);
if (ghBmpClear)
DeleteObject(ghBmpClear);
ghFontNormal = NULL;
ghFontBold = NULL;
ghFontUnderline = NULL;
ghBmpCheck = NULL;
ghBmpGrayOut = NULL;
ghBmpClear = NULL;
EnterCriticalSection(&gcsClient);
RemoveProp( ghWndList, MYLBALIVEPROP );
clb = (LONG)SendMessage(ghWndList, LB_GETCOUNT, 0, 0);
for(int i = 0; i < clb; i++)
{
item = (LBITEM*)SendMessage(ghWndList, LB_GETITEMDATA, i, 0);
delete(item);
}
LeaveCriticalSection(&gcsClient);
return 0;
}
return DefWindowProc(hDlg, message, wParam, lParam);
}
void DrawTitle(HDC hDC, LBITEM * plbi, RECT rc)
{
// we want the bitmap to be on the same background as the title, let's do this here since we
// already have all the measures.
RECT rcTop = rc;
rcTop.left = 0;
// draw menu background rectangle for the title and bitmap
HBRUSH hBrush;
if (! (hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU))))
{
DEBUGMSG("WUAUCLT CreateSolidBrush failure in DrawTitle, GetLastError=%lu", GetLastError());
return;
}
FillRect(hDC, (LPRECT)&rcTop, hBrush);
if (NULL != hBrush)
{
DeleteObject(hBrush);
}
// draw 3d look
DrawEdge(hDC, &rcTop, EDGE_ETCHED, BF_RECT);
// change text and back ground color of list box's selection
DWORD dwOldTextColor = SetTextColor(hDC, GetSysColor(COLOR_MENUTEXT)); // black text color
DWORD dwOldBkColor = SetBkColor(hDC, GetSysColor(COLOR_MENU)); // text cell light gray background
HFONT hFontPrev = (HFONT)SelectObject(hDC, ghFontBold);
rc.left += TITLE_MARGIN;
rc.top += SECTION_SPACING;
rc.right -= TITLE_MARGIN;
rc.bottom -= SECTION_SPACING;
DrawText(hDC, (LPTSTR)plbi->szTitle, -1,
&rc, DT_WORDBREAK);
// restore text and back ground color of list box's selection
SetTextColor(hDC, dwOldTextColor);
SetBkColor(hDC, dwOldBkColor);
SelectObject(hDC, hFontPrev);
return;
}
void DrawRTF(HDC hDC, LBITEM * plbi, const RECT & rc /*,BOOL fHit*/)
{
if (!plbi->bRTF)
return;
//draw RTF background
RECT rcBackGround;
CalcRTFFocusRect(rc, rcBackGround);
HBRUSH hBrush;
if (!(hBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW))))
{
DEBUGMSG("WUAUCLT CreateSolidBrush failure in DrawRTF, GetLastError=%lu", GetLastError());
return;
}
if (!FillRect(hDC, (LPRECT)&rcBackGround, hBrush))
{
DEBUGMSG("Fail to erase RTF background");
}
if (NULL != hBrush)
{
DeleteObject(hBrush);
}
HFONT hFontPrev = (HFONT) SelectObject(hDC, ghFontUnderline);
DWORD dwOldTextColor = SetTextColor(hDC, GetSysColor(ATTENTION_COLOR));
SetBkMode(hDC, TRANSPARENT);
// add the read this first
TextOut(hDC, (int)(rc.left), (int)(rc.top),
(LPTSTR)plbi->szRTF, lstrlen(plbi->szRTF));
// restore text and back ground color of list box's selection
SetTextColor(hDC, dwOldTextColor);
SelectObject(hDC, hFontPrev);
return;
}
void DrawDescription(HDC hDC, LBITEM * plbi, RECT & rc)
{
HFONT hFontPrev = (HFONT)SelectObject(hDC, ghFontNormal);
DrawText(hDC, (LPTSTR)plbi->pszDescription, -1,
&rc, DT_BOTTOM | DT_EXPANDTABS | DT_WORDBREAK | DT_EDITCONTROL);
SelectObject(hDC, hFontPrev);
return;
}
void DrawBitmap(HDC hDC, LBITEM * plbi, const RECT & rc, BOOL fSel, BOOL fSelectionDisabled)
{
HDC hdcMem;
plbi->bSelect = fSel;
if (hdcMem = CreateCompatibleDC(hDC))
{
HGDIOBJ hBmp;
if (fSelectionDisabled)
{
hBmp = ghBmpGrayOut;
// DEBUGMSG("Set bitmap to grayout");
}
else
{
// DEBUGMSG("Set bitmap to selectable");
hBmp = (plbi->bSelect ? ghBmpCheck : ghBmpClear);
}
HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, hBmp);
BitBlt(hDC,
rc.left + 3, rc.top + SECTION_SPACING,
rc.right - rc.left,
rc.bottom - rc.top,
hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
}
}
BOOL GetBmpSize(HANDLE hBmp, SIZE *psz)
{
if (NULL == hBmp || NULL == psz)
{
DEBUGMSG("Error: GetBmpSize() invalid parameter");
return FALSE;
}
BITMAP bm;
ZeroMemory(&bm, sizeof(bm));
if (0 == GetObject(hBmp, sizeof(bm), &bm))
{
return FALSE;
}
psz->cx = bm.bmWidth;
psz->cy = bm.bmHeight;
return TRUE;
}
//fixcode: should return error code
void AddItem(LPTSTR tszTitle, LPTSTR tszDesc, LPTSTR tszRTF, int index, BOOL fSelected, BOOL fRTF)
{
LBITEM *newItem = new(LBITEM);
if (! newItem)
{
DEBUGMSG("WUAUCLT new() failed in AddItem, GetLastError=%lu", GetLastError());
goto Failed;
}
DWORD dwDescLen = max(lstrlen(tszDesc), MAX_DESC_LENGTH);
newItem->pszDescription = (LPTSTR) malloc((dwDescLen+1) * sizeof(TCHAR));
if (NULL == newItem->pszDescription)
{
DEBUGMSG("AddItem() fail to alloc memory for description");
goto Failed;
}
(void)StringCchCopyEx(newItem->szTitle, ARRAYSIZE(newItem->szTitle), tszTitle, NULL, NULL, MISTSAFE_STRING_FLAGS);
(void)StringCchCopyEx(newItem->pszDescription, dwDescLen+1, tszDesc, NULL, NULL, MISTSAFE_STRING_FLAGS);
(void)StringCchCopyEx(newItem->szRTF, ARRAYSIZE(newItem->szRTF), tszRTF, NULL, NULL, MISTSAFE_STRING_FLAGS);
newItem->m_index = index;
newItem->bSelect = fSelected;
newItem->bRTF = fRTF;
LRESULT i = SendMessage(ghWndList, LB_GETCOUNT, 0, 0);
if (LB_ERR == i ||
LB_ERR == (i = SendMessage(ghWndList, LB_INSERTSTRING, (WPARAM) i, (LPARAM) newItem->szTitle)) ||
LB_ERRSPACE == i ||
LB_ERR == SendMessage(ghWndList, LB_SETITEMDATA, (WPARAM) i, (LPARAM) newItem))
{
DEBUGMSG("WUAUCLT AddItem() fail to add item to listbox");
goto Failed;
}
return;
Failed:
SafeDelete(newItem);
QuitNRemind(TIMEOUT_INX_TOMORROW);
}
////////////////////////////////////////////////////
// utility function
// calculate the height of a paragraph in current
// device context
////////////////////////////////////////////////////
UINT GetParagraphHeight(HDC hDC, LPTSTR tszPara, UINT uLineWidth)
{
UINT y = 0;
TCHAR* pEOL;
SIZE sz;
if (0 == uLineWidth)
{
return 0;
}
TCHAR* pLast = tszPara;
while(pEOL = _tcschr(pLast, _T('\n')))
{
*pEOL = _T('\0');
//fixcode: check return value of GetTextExtentPoint32()
GetTextExtentPoint32(hDC, pLast, lstrlen(pLast), &sz);
*pEOL = _T('\n');
pLast = _tcsinc(pEOL);
y += sz.cy * ((sz.cx / (uLineWidth)) + 1);
}
//fixcode: check return value of GetTextExtentPoint32()
GetTextExtentPoint32(hDC, pLast, lstrlen(pLast), &sz);
y += sz.cy * ((sz.cx / (uLineWidth)) + 1);
return y;
}
int CalcDescHeight(HDC hDC, LBITEM * plbi, int cx)
{
TCHAR* pEOL = NULL;
TCHAR* pLast = NULL;
int y = 0;
HFONT hPrevFont = NULL;
if (NULL == plbi)
{
DEBUGMSG("CalcDescHeight() invalid parameter");
return 0;
}
hPrevFont = (HFONT) SelectObject(hDC, ghFontNormal);
y = GetParagraphHeight(hDC, plbi->pszDescription, cx);
SelectObject(hDC, hPrevFont);
return y;
}
int CalcRTFHeight(HDC hDC, LBITEM * plbi)
{
SIZE sz ;
if (NULL == plbi)
{
DEBUGMSG("CalcRTFHeight() invalid parameter");
return 0;
}
ZeroMemory(&sz, sizeof(sz));
HFONT hPrevFont = (HFONT) SelectObject(hDC, ghFontUnderline);
//fixcode: check return value of GetTextExtentPoint32()
GetTextExtentPoint32(hDC, plbi->szRTF, lstrlen(plbi->szRTF), &sz);
SelectObject(hDC, hPrevFont);
return sz.cy;
}
int CalcRTFWidth(HDC hDC, LBITEM * plbi)
{
if (NULL == plbi)
{
DEBUGMSG("CalcRTFWidth() invalid parameter");
return 0;
}
SIZE sz;
HFONT hPrevFont = (HFONT) SelectObject(hDC, ghFontUnderline);
//fixcode: check return value of GetTextExtentPoint32()
GetTextExtentPoint32(hDC, plbi->szRTF, lstrlen(plbi->szRTF), &sz);
SelectObject(hDC, hPrevFont);
return sz.cx;
}
int CalcTitleHeight(HDC hDC, LBITEM *plbi, int cx)
{
INT y = 0;
INT iBmpHeight = 0;
if (NULL == plbi)
{
DEBUGMSG("CalcTitleHeight() invalid parameter");
goto done;
}
HFONT hPrevFont = (HFONT) SelectObject(hDC, ghFontBold);
y = GetParagraphHeight(hDC, plbi->szTitle, cx);
SelectObject(hDC, hPrevFont);
// get checkbox size
if (NULL != ghBmpCheck && NULL != ghBmpClear && NULL != ghBmpGrayOut)
{
SIZE sz1 ;
SIZE sz2 ;
SIZE sz3 ;
sz1.cy = sz2.cy = sz3.cy = DEF_CHECK_HEIGHT;
GetBmpSize(ghBmpCheck, &sz1);
GetBmpSize(ghBmpClear, &sz2);
GetBmpSize(ghBmpGrayOut, &sz3);
iBmpHeight = max(sz1.cy, sz2.cy);
iBmpHeight = max(iBmpHeight, sz3.cy);
}
done:
return max(y, iBmpHeight); //make title height a little bigger for clearer focus rect
}
////////////////////////////////////////////////////////////
/// Layout of listbox item:
/// spacing
/// bitmap margin TITLE margin
/// spacing
/// DESCRIPTION
/// spacing
/// RTF rtf_margin
/// spacing
///////////////////////////////////////////////////////////
void CalcItemLocation(HDC hDC, LBITEM * plbi, const RECT & rc)
{
// Calculate the positon of each element
plbi->rcItem = rc;
plbi->rcTitle = rc;
plbi->rcTitle.left += XBITMAP ;
plbi->rcTitle.bottom = plbi->rcTitle.top + CalcTitleHeight(hDC, plbi, plbi->rcTitle.right - plbi->rcTitle.left - 2* TITLE_MARGIN)
+ 2 * SECTION_SPACING;
plbi->rcText = rc;
plbi->rcText.left = plbi->rcTitle.left;
plbi->rcText.right = plbi->rcTitle.right;
plbi->rcText.top = plbi->rcTitle.bottom;
plbi->rcText.bottom -= CalcRTFHeight(hDC, plbi) + SECTION_SPACING; //
plbi->rcRTF = plbi->rcText;
plbi->rcRTF.top = plbi->rcText.bottom;
plbi->rcRTF.bottom = plbi->rcRTF.top + CalcRTFHeight(hDC, plbi);
plbi->rcRTF.right = plbi->rcText.right - RTF_MARGIN;
plbi->rcRTF.left = plbi->rcRTF.right - CalcRTFWidth(hDC, plbi);
plbi->rcBitmap = rc;
plbi->rcBitmap.bottom = plbi->rcTitle.bottom;
return;
}