|
|
/*++
Copyright (c) 1994-1999 Microsoft Corporation
Module Name :
odlbox.cpp
Abstract:
Owner draw listbox/combobox base classes
Author:
Ronald Meijer (ronaldm)
Project:
Internet Services Manager (cluster edition)
Revision History:
--*/
//
// Include Files
//
#include "stdafx.h"
#include "common.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__; #endif
#define new DEBUG_NEW
#define BMP_LEFT_OFFSET (1) // Space allotted to the left of bitmap
#define BMP_RIGHT_OFFSET (3) // Space allotted to the right of bitmap
//
// Ellipses are shown when column text doesn't fit in the display
//
const TCHAR g_szEllipses[] = _T("..."); int g_nLenEllipses = (sizeof(g_szEllipses) / sizeof(g_szEllipses[0])) - 1;
//
// Registry value for columns
//
const TCHAR g_szRegColumns[] = _T("Columns");
//
// Column Value Separator
//
const TCHAR g_szColValueSep[] = _T(" ");
void GetDlgCtlRect( IN HWND hWndParent, IN HWND hWndControl, OUT LPRECT lprcControl ) /*++
Routine Description:
Get the control rectangle coordinates relative to its parent. This can then be used in e.g. SetWindowPos()
Arguments:
HWND hWndParent : Parent window handle HWND hWndControl : Control window handle LPRECT lprcControl : Control rectangle to be filled in
Return Value:
None
--*/ { #define MapWindowRect(hwndFrom, hwndTo, lprc)\
MapWindowPoints((hwndFrom), (hwndTo), (POINT *)(lprc), 2)
::GetWindowRect(hWndControl, lprcControl); ::MapWindowRect(NULL, hWndParent, lprcControl); }
void FitPathToControl( IN CWnd & wndControl, IN LPCTSTR lpstrString, IN BOOL bIsFilePath ) /*++
Routine Description:
Display the given path in the given control, using ellipses to ensure that the path fits within the control.
Arguments:
CWnd & wndControl : Control to display on LPCTSTR lpstrString : Path
Return Value:
None
--*/ { CString strDisplay(lpstrString); UINT uLength = strDisplay.GetLength() + 4; // Account for ell.
LPTSTR lp = strDisplay.GetBuffer(uLength);
if (lp) { CDC * pDC = wndControl.GetDC(); ASSERT_PTR(pDC);
if (pDC != NULL) { CRect rc; wndControl.GetClientRect(&rc); if (bIsFilePath) { pDC->DrawText(lp, uLength, &rc, DT_PATH_ELLIPSIS | DT_MODIFYSTRING); } else { pDC->DrawText(lp, uLength, &rc, DT_END_ELLIPSIS | DT_MODIFYSTRING); } wndControl.ReleaseDC(pDC); }
strDisplay.ReleaseBuffer(); wndControl.SetWindowText(strDisplay); } }
void ActivateControl( IN CWnd & wndControl, IN BOOL fShow ) /*++
Routine Description:
Show/hide _AND_ enable/disable control window
Arguments:
CWnd & wndControl : Window in question BOOL fShow : TRUE to show/enable, FALSE to hide/disable
Return Value:
None
Notes:
Merely hiding a window does not disable it. Use this function instead of ShowWindow() to do that.
--*/ { wndControl.ShowWindow(fShow ? SW_SHOW : SW_HIDE); wndControl.EnableWindow(fShow); }
BOOL VerifyState() /*++
Routine Description:
Verify keyboard state
Arguments:
None
Return Value:
TRUE if keyboard is in specified state FALSE otherwise.
--*/ { SHORT s1, s2; s1 = GetKeyState(VK_SHIFT); s2 = GetKeyState(VK_CONTROL);
return (s1 & 0x8000) && (s2 & 0x8000); }
BOOL CMappedBitmapButton::LoadMappedBitmaps( IN UINT nIDBitmapResource, IN UINT nIDBitmapResourceSel, IN UINT nIDBitmapResourceFocus, IN UINT nIDBitmapResourceDisabled ) /*++
Routine Description:
LoadBitmaps will load one, two, three or all four bitmaps returns TRUE if all specified images are loaded. This will map the buttons to the default colours
Arguments:
UINT nIDBitmapResource : Standard button UINT nIDBitmapResourceSel : Selected button UINT nIDBitmapResourceFocus : Button with focus UINT nIDBitmapResourceDisabled : Disabled button
--*/ { //
// delete old bitmaps (if present)
//
m_bitmap.DeleteObject(); m_bitmapSel.DeleteObject(); m_bitmapFocus.DeleteObject(); m_bitmapDisabled.DeleteObject();
if (!m_bitmap.LoadMappedBitmap(nIDBitmapResource)) { TRACEEOLID("Failed to load bitmap for normal image.");
return FALSE; // need this one image
}
BOOL bAllLoaded = TRUE; if (nIDBitmapResourceSel != 0) { if (!m_bitmapSel.LoadMappedBitmap(nIDBitmapResourceSel)) { TRACEEOLID("Failed to load bitmap for selected image."); bAllLoaded = FALSE; } } if (nIDBitmapResourceFocus != 0) { if (!m_bitmapFocus.LoadMappedBitmap(nIDBitmapResourceFocus)) { bAllLoaded = FALSE; } }
if (nIDBitmapResourceDisabled != 0) { if (!m_bitmapDisabled.LoadMappedBitmap(nIDBitmapResourceDisabled)) { bAllLoaded = FALSE; } }
return bAllLoaded; }
CRMCListBoxResources::CRMCListBoxResources( IN int bmId, IN int nBitmaps, IN COLORREF rgbBackground ) /*++
Routine Description:
Constructor
Arguments:
int bmId : Bitmap resource ID int nBitmaps : Number of bitmaps COLORREF rgbBackground : Background colour to mask out
Return Value:
N/A
--*/ : m_idBitmap(bmId), m_rgbColorTransparent(rgbBackground), m_nBitmaps(nBitmaps), m_nBitmapHeight(0), m_nBitmapWidth(-1), // Set Later
m_fInitialized(FALSE) { ASSERT(m_nBitmaps > 0); GetSysColors(); PrepareBitmaps(); }
CRMCListBoxResources::~CRMCListBoxResources() /*++
Routine Description:
Destructor
Arguments:
N/A
Return Value:
N/A
--*/ { UnprepareBitmaps(); }
void CRMCListBoxResources::UnprepareBitmaps() /*++
Routine Description:
Free up bitmap resources
Arguments:
N/A
Return Value:
N/A
--*/ { ASSERT(m_fInitialized);
if (m_fInitialized) { CBitmap * pBmp = (CBitmap *)CGdiObject::FromHandle(m_hOldBitmap); ASSERT_READ_PTR(pBmp);
VERIFY(m_dcFinal.SelectObject(pBmp)); VERIFY(m_dcFinal.DeleteDC()); VERIFY(m_bmpScreen.DeleteObject());
m_fInitialized = FALSE; } }
void CRMCListBoxResources::PrepareBitmaps() /*++
Routine Description:
Prepare 2 rows of bitmaps. One with the selection colour background, and one with the ordinary listbox background.
Arguments:
None
Return Value:
None
--*/ { ASSERT(m_idBitmap);
//
// Clean up if we were already initialised
//
if (m_fInitialized) { UnprepareBitmaps(); }
//
// create device contexts compatible with screen
//
CDC dcImage; CDC dcMasks;
VERIFY(dcImage.CreateCompatibleDC(0)); VERIFY(dcMasks.CreateCompatibleDC(0));
VERIFY(m_dcFinal.CreateCompatibleDC(0));
CBitmap bitmap; VERIFY(bitmap.LoadBitmap(m_idBitmap));
BITMAP bm; VERIFY(bitmap.GetObject(sizeof(BITMAP), &bm));
//
// Each bitmap is assumed to be the same size.
//
m_nBitmapWidth = bm.bmWidth / m_nBitmaps; ASSERT(m_nBitmapWidth > 0);
const int bmWidth = bm.bmWidth; const int bmHeight = bm.bmHeight; m_nBitmapHeight = bmHeight;
CBitmap * pOldImageBmp = dcImage.SelectObject(&bitmap); ASSERT_PTR(pOldImageBmp);
CBitmap bmpMasks; VERIFY(bmpMasks.CreateBitmap(bmWidth, bmHeight * 2, 1, 1, NULL));
CBitmap * pOldMaskBmp = (CBitmap *)dcMasks.SelectObject(&bmpMasks); ASSERT_PTR(pOldMaskBmp);
//
// create the foreground and object masks
//
COLORREF crOldBk = dcImage.SetBkColor(m_rgbColorTransparent); dcMasks.BitBlt(0, 0, bmWidth, bmHeight, &dcImage, 0, 0, SRCCOPY); dcMasks.BitBlt(0, 0, bmWidth, bmHeight, &dcImage, 0, bmHeight, SRCAND); dcImage.SetBkColor(crOldBk); dcMasks.BitBlt(0, bmHeight, bmWidth, bmHeight, &dcMasks, 0, 0, NOTSRCCOPY);
//
// create DC to hold final image
//
VERIFY(m_bmpScreen.CreateCompatibleBitmap(&dcImage, bmWidth, bmHeight * 2)); CBitmap * pOldBmp = (CBitmap*)m_dcFinal.SelectObject(&m_bmpScreen); ASSERT_PTR(pOldBmp); m_hOldBitmap = pOldBmp->m_hObject;
CBrush b1, b2; VERIFY(b1.CreateSolidBrush(m_rgbColorHighlight)); VERIFY(b2.CreateSolidBrush(m_rgbColorWindow));
m_dcFinal.FillRect(CRect(0, 0, bmWidth, bmHeight), &b1); m_dcFinal.FillRect(CRect(0, bmHeight, bmWidth, bmHeight * 2), &b2);
//
// mask out the object pixels in the destination
//
m_dcFinal.BitBlt(0, 0, bmWidth, bmHeight, &dcMasks, 0, 0, SRCAND);
//
// mask out the background pixels in the image
//
dcImage.BitBlt(0, 0, bmWidth, bmHeight, &dcMasks, 0, bmHeight, SRCAND);
//
// XOR the revised image into the destination
//
m_dcFinal.BitBlt(0, 0, bmWidth, bmHeight, &dcImage, 0, 0, SRCPAINT);
//
// mask out the object pixels in the destination
//
m_dcFinal.BitBlt(0, bmHeight, bmWidth, bmHeight, &dcMasks, 0, 0, SRCAND);
//
// XOR the revised image into the destination
//
m_dcFinal.BitBlt(0, bmHeight, bmWidth, bmHeight, &dcImage, 0, 0, SRCPAINT);
VERIFY(dcMasks.SelectObject(pOldMaskBmp)); VERIFY(dcImage.SelectObject(pOldImageBmp));
//
// The result of all of this mucking about is a bitmap identical with the
// one loaded from the resources but with the lower row of bitmaps having
// their background changed from transparent1 to the window background
// and the upper row having their background changed from transparent2 to
// the highlight colour. A derived CRMCListBox can BitBlt the relevant part
// of the image into an item's device context for a transparent bitmap
// effect which does not take any extra time over a normal BitBlt.
//
m_fInitialized = TRUE; }
void CRMCListBoxResources::SysColorChanged() /*++
Routine Description:
Respond to change in system colours by rebuilding the resources
Arguments:
None
Return Value:
None
--*/ { //
// Reinitialise bitmaps and syscolors. This should be called from
// the parent of the CRMCListBoxResources object from
// the OnSysColorChange() function.
//
GetSysColors(); PrepareBitmaps(); }
void CRMCListBoxResources::GetSysColors() /*++
Routine Description:
Get sytem colours
Arguments:
None
Return Value:
None
--*/ { m_rgbColorWindow = ::GetSysColor(COLOR_WINDOW); m_rgbColorHighlight = ::GetSysColor(COLOR_HIGHLIGHT); m_rgbColorWindowText = ::GetSysColor(COLOR_WINDOWTEXT); m_rgbColorHighlightText = ::GetSysColor(COLOR_HIGHLIGHTTEXT); }
CRMCListBoxDrawStruct::CRMCListBoxDrawStruct( IN CDC * pDC, IN RECT * pRect, IN BOOL sel, IN DWORD_PTR item, IN int itemIndex, IN const CRMCListBoxResources * pres ) /*++
Routine Description:
Constructor
Arguments:
CDC * pdc : Device context RECT * pRect : Rectange to paint into BOOL sel : TRUE if selected DWORD item : item int itemIndex : item index const CRMCListBoxResources * pres : Pointer to resources
Return Value:
N/A
--*/ : m_pDC(pDC), m_Sel(sel), m_ItemData(item), m_ItemIndex(itemIndex), m_pResources(pres) { m_Rect.CopyRect(pRect); }
CODLBox::CODLBox() /*++
Routine Description:
Constructor for CODLBox -- abstract base class for both CRMCComboBox, and CRMCListBox
Arguments:
None
Return Value:
N/A
--*/ : m_lfHeight(0), m_pResources(NULL), m_auTabs(), m_pWnd(NULL) { }
CODLBox::~CODLBox() /*++
Routine Description:
Destructor
Arguments:
N/A
Return Value:
N/A
--*/ { }
/* virtual */ BOOL CODLBox::Initialize() /*++
Routine Description:
Listbox/combobox is being created
Arguments:
None
Return Value:
TRUE for success, FALSE for failure
--*/ { //
// Derived control must be attached at this point
//
ASSERT_PTR(m_pWnd);
//
// GetFont returns non NULL when the control is in a dialog box
//
CFont * pFont = m_pWnd->GetFont();
if(pFont == NULL) { LOGFONT lf; CFont fontTmp;
::GetObject(::GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), &lf); fontTmp.CreateFontIndirect(&lf);
CalculateTextHeight(&fontTmp); } else { CalculateTextHeight(pFont); }
return TRUE; }
BOOL CODLBox::ChangeFont( IN CFont * pFont ) /*++
Routine Description:
Change the control font the specified font
Arguments:
CFont * pFont : Pointer to the new font to be used
Return Value:
TRUE for success, FALSE for failure
--*/ { ASSERT_PTR(m_pResources); ASSERT_PTR(m_pWnd);
if( pFont == NULL || m_pResources == NULL || m_pWnd == NULL || m_pWnd->m_hWnd == NULL ) { TRACEEOLID("Invalid state of the control. Can't change font"); return FALSE; }
//
// Don't reflect changes immediately
//
m_pWnd->SetRedraw(FALSE);
m_pWnd->SetFont(pFont, TRUE); CalculateTextHeight(pFont);
int nItems = __GetCount(); int bmHeight = m_pResources->BitmapHeight(); int nHeight = bmHeight > m_lfHeight ? bmHeight : m_lfHeight;
for(int i = 0; i < nItems; ++i) { __SetItemHeight(i, nHeight); }
//
// Now reflect the change visually
//
m_pWnd->SetRedraw(TRUE); m_pWnd->Invalidate();
return TRUE; }
void CODLBox::AttachResources( IN const CRMCListBoxResources * pRes ) /*++
Routine Description:
Attach the bitmaps
Arguments:
const CRMCListBoxResources * pRes : pointer to resources to be attached
Return Value:
None
--*/ { if(pRes != m_pResources) { ASSERT_READ_PTR(pRes); m_pResources = pRes;
if(m_pWnd != NULL && m_pWnd->m_hWnd != NULL) { //
// if window was created already, redraw everything.
//
m_pWnd->Invalidate(); } } }
/* static */ int CODLBox::GetRequiredWidth( IN CDC * pDC, IN const CRect & rc, IN LPCTSTR lpstr, IN int nLength ) /*++
Routine Description:
Determine required display width of the string
Arguments:
CDC * pDC : Pointer to device context to use const CRect & rc : Starting rectangle LPCTSTR lpstr : String whose width is to be displayed int nLength : Length (in characters of the string
Return Value:
The display width that the string would need to be displayed on the given device context
--*/ { #ifdef _DEBUG
pDC->AssertValid();
#endif // _DEBUG
CRect rcTmp(rc);
pDC->DrawText( lpstr, nLength, &rcTmp, DT_CALCRECT | DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER );
return rcTmp.Width(); }
/* static */ BOOL CODLBox::ColumnText( IN CDC * pDC, IN int nLeft, IN int nTop, IN int nRight, IN int nBottom, IN LPCTSTR lpstr ) /*++
Routine Description:
Display text limited by a rectangle. Use ellipses if the text is too wide to fit inside the given dimensions.
Arguments:
CDC * pDC : Pointer to display context to use int nLeft : Left coordinate int nTop : Top coordinate int nRight : Right coordinate int nBottom : Bottom coordinate LPCTSTR lpstr : String to be displayed
Return Value:
TRUE for success, FALSE for failure
--*/ { BOOL fSuccess = TRUE;
#ifdef _DEBUG
pDC->AssertValid();
#endif // _DEBUG
CString str; CRect rc(nLeft, nTop, nRight, nBottom);
int nAvailWidth = rc.Width(); int nLength = ::lstrlen(lpstr);
try { if (GetRequiredWidth(pDC, rc, lpstr, nLength) <= nAvailWidth) { //
// Sufficient space, display as is.
//
str = lpstr; } else { //
// Build a string with ellipses until it
// fits
//
LPTSTR lpTmp = str.GetBuffer(nLength + g_nLenEllipses); while (nLength) { ::lstrcpyn(lpTmp, lpstr, nLength); ::lstrcpy(lpTmp + nLength - 1, g_szEllipses);
if (GetRequiredWidth(pDC, rc, lpTmp, nLength + g_nLenEllipses) <= nAvailWidth) { break; }
--nLength; }
str = lpTmp; }
pDC->DrawText( str, &rc, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER );
} catch(CMemoryException * e) { //
// Mem failure
//
fSuccess = FALSE; e->ReportError(); e->Delete(); }
return fSuccess; }
void CODLBox::ComputeMargins( IN CRMCListBoxDrawStruct & ds, IN int nCol, OUT int & nLeft, OUT int & nRight ) /*++
Routine Description:
Compute the left and right margins of the given column.
Arguments:
CRMCListBoxDrawStruct & ds : Drawing structure int nCol : Column whose margins we're interested in int & nLeft : Left column int & nRight : Right column
Return Value:
None
--*/ { nLeft = ds.m_Rect.left; nRight = ds.m_Rect.right;
//
// Find tab value associated with column index (0-based),
// and adjust left and right
//
ASSERT(nCol <= NumTabs());
if (nCol > 0) { if (nCol <= NumTabs()) { nLeft += m_auTabs[nCol-1]; } } if (nCol < NumTabs()) { nRight = m_auTabs[nCol]; } }
BOOL CODLBox::DrawBitmap( IN CRMCListBoxDrawStruct & ds, IN int nCol, IN int nID ) /*++
Routine Description:
Draw a bitmap in the given column. Bitmap are always placed on the leftmost side of the column if there is sufficient space.
Arguments:
CRMCListBoxDrawStruct & ds : Drawing structure int nCol : Column to place bitmap in int nID : Bitmap ID (offset within the bitmap resources)
Return Value:
None
--*/ { CDC * pBmpDC = (CDC *)&ds.m_pResources->dcBitMap();
#ifdef _DEBUG
pBmpDC->AssertValid();
#endif // _DEBUG
//
// Select the bitmap with either a selection or
// a regular background
//
int bm_h = ds.m_Sel ? 0 : ds.m_pResources->BitmapHeight(); int bm_w = ds.m_pResources->BitmapWidth() * nID;
int nLeft, nRight; ComputeMargins(ds, nCol, nLeft, nRight); nLeft += BMP_LEFT_OFFSET;
//
// Check to make sure there's enough room before
// drawing the bitmap.
//
if (nRight - nLeft >= ds.m_pResources->BitmapWidth()) { ds.m_pDC->BitBlt( nLeft, ds.m_Rect.top, ds.m_pResources->BitmapWidth(), ds.m_pResources->BitmapHeight(), pBmpDC, bm_w, bm_h, SRCCOPY ); }
return TRUE; }
BOOL CODLBox::ColumnText( IN CRMCListBoxDrawStruct & ds, IN int nCol, IN BOOL fSkipBitmap, IN LPCTSTR lpstr ) /*++
Routine Description:
Draw column text.
Arguments:
CRMCListBoxDrawStruct & ds : Drawing structure int nCol : Column to place bitmap in BOOL fSkipBitmap : If TRUE, increment lefthand column by the width of a bitmap LPCTSTR lpstr : String to be displayed. May be truncated as necessary
Return Value:
TRUE for success, FALSE for failure
--*/ { int nLeft, nRight;
ComputeMargins(ds, nCol, nLeft, nRight);
//
// Optionally adjust for bitmap
//
if (fSkipBitmap) { nLeft += (ds.m_pResources->BitmapWidth() + BMP_RIGHT_OFFSET); }
return CODLBox::ColumnText( ds.m_pDC, nLeft, ds.m_Rect.top, nRight, ds.m_Rect.bottom, lpstr ); }
void CODLBox::CalculateTextHeight( IN CFont * pFont ) /*++
Routine Description:
Calculate and set the text height of font
Arguments:
CFont * pFont : Pointer to the font to be used.
Return Value:
None
--*/ { ASSERT_PTR(m_pWnd);
CClientDC dc(m_pWnd); CFont * pOldFont = dc.SelectObject(pFont);
TEXTMETRIC tm; dc.GetTextMetrics(&tm); m_lfHeight = tm.tmHeight;
dc.SelectObject(pOldFont); }
int CODLBox::AddTab( IN UINT uTab ) /*++
Routine Description:
Add a tab to the end of the list (e.g the right side of the header)
Arguments:
UINT uTab : Tab value to set
Return Value:
The index of the new tab
--*/ { return (int)m_auTabs.Add(uTab); }
int CODLBox::AddTabFromHeaders( IN CWnd & wndLeft, IN CWnd & wndRight ) /*++
Routine Description:
Add a tab to the end of the list (e.g the right side of the header), but compute the tab by taking the difference in left-hand coordinat of two window controls (usually static header text)
Arguments:
CWnd & wndLeft : Left window CWnd & wndRight : Right window
Return Value:
The index of the new tab
--*/ { CRect rcLeft, rcRight;
wndLeft.GetWindowRect(&rcLeft); wndRight.GetWindowRect(&rcRight);
ASSERT(rcRight.left > rcLeft.left);
return AddTab(rcRight.left - rcLeft.left - 1); }
int CODLBox::AddTabFromHeaders( IN UINT idLeft, IN UINT idRight ) /*++
Routine Description:
Similar to the function above, but use the control IDs. The parent window is assumed to be the same as the parent window of the listbox
Arguments:
UINT idLeft : ID of the left control UINT idRight : ID of the right control
Return Value:
The index of the new tab or -1 in case of failure
--*/ { ASSERT_PTR(m_pWnd);
if (m_pWnd == NULL) { //
// Should have associated window handle by now
//
return -1; }
CWnd * pLeft = m_pWnd->GetParent()->GetDlgItem(idLeft); CWnd * pRight = m_pWnd->GetParent()->GetDlgItem(idRight);
ASSERT_READ_PTR(pLeft); ASSERT_READ_PTR(pRight);
if (!pLeft || !pRight) { //
// One or both control IDs were not valid
//
return -1; }
return AddTabFromHeaders(*pLeft, *pRight); }
void CODLBox::InsertTab( IN int nIndex, IN UINT uTab ) /*++
Routine Description:
Insert a tab at the given index
Arguments:
int nIndex : Column index at which the tab is to be inserted UINT uTab : Tab value to set
Return Value:
None
--*/ { m_auTabs.InsertAt(nIndex, uTab); }
void CODLBox::RemoveTab( IN int nIndex, IN int nCount ) /*++
Routine Description:
Remove one or more tabs
Arguments:
int nIndex : Column index at which to start removing tabs int nCount : Number of tabs to be removed
Return Value:
None
--*/ { m_auTabs.RemoveAt(nIndex, nCount); }
void CODLBox::RemoveAllTabs() /*++
Routine Description:
Remove all tabs
Arguments:
None
Return Value:
None
--*/ { m_auTabs.RemoveAll(); }
void CODLBox::__DrawItem( IN LPDRAWITEMSTRUCT lpDIS ) /*++
Routine Description:
Draw an item. This will draw the focus and selection state, and then call out to the derived class to draw the item.
Arguments:
LPDRAWITEMSTRUCT lpDIS : The drawitem structure
Return Value:
None
--*/ { //
// Need to attach resources before creation/adding items
//
ASSERT_PTR(m_pResources);
CDC * pDC = CDC::FromHandle(lpDIS->hDC);
#ifdef _DEBUG
pDC->AssertValid();
#endif // _DEBUG
//
// Draw focus rectangle when no items in listbox
//
if(lpDIS->itemID == (UINT)-1) { if(lpDIS->itemAction & ODA_FOCUS) { //
// rcItem.bottom seems to be 0 for variable height list boxes
//
lpDIS->rcItem.bottom = m_lfHeight; pDC->DrawFocusRect(&lpDIS->rcItem); }
return; } else { BOOL fSelChange = (lpDIS->itemAction & ODA_SELECT) != 0; BOOL fFocusChange = (lpDIS->itemAction & ODA_FOCUS) != 0; BOOL fDrawEntire = (lpDIS->itemAction & ODA_DRAWENTIRE) != 0;
if(fSelChange || fDrawEntire) { BOOL fSel = (lpDIS->itemState & ODS_SELECTED) != 0;
COLORREF hlite = (fSel ? (m_pResources->ColorHighlight()) : (m_pResources->ColorWindow())); COLORREF textcol = (fSel ? (m_pResources->ColorHighlightText()) : (m_pResources->ColorWindowText())); pDC->SetBkColor(hlite); pDC->SetTextColor(textcol);
//
// fill the rectangle with the background colour.
//
pDC->ExtTextOut(0, 0, ETO_OPAQUE, &lpDIS->rcItem, NULL, 0, NULL);
CRMCListBoxDrawStruct ds(pDC, (RECT *)&lpDIS->rcItem, fSel, (DWORD_PTR)lpDIS->itemData, lpDIS->itemID, m_pResources );
//
// Now call the draw function of the derived class
//
DrawItemEx(ds); }
if (fFocusChange || (fDrawEntire && (lpDIS->itemState & ODS_FOCUS))) { pDC->DrawFocusRect(&lpDIS->rcItem); } } }
void CODLBox::__MeasureItem( IN OUT LPMEASUREITEMSTRUCT lpMIS ) /*++
Routine Description:
Provide dimensions of given item
Arguments:
LPMEASUREITEMSTRUCT lpMIS : Measure item structure
Return Value:
None
--*/ { ASSERT_PTR(m_pResources);
// int h = lpMIS->itemHeight;
int ch = TextHeight(); int bmHeight = m_pResources->BitmapHeight();
lpMIS->itemHeight = ch < bmHeight ? bmHeight : ch; }
CRMCListBoxHeader::CRMCListBoxHeader( IN DWORD dwStyle ) /*++
Routine Description:
Constructor.
Arguments:
DWORD dwStyle : Style bits
Return Value:
N/A
--*/ : m_pHCtrl(NULL), m_pListBox(NULL), m_dwStyle(dwStyle), m_fRespondToColumnWidthChanges(TRUE) { m_pHCtrl = new CHeaderCtrl; }
CRMCListBoxHeader::~CRMCListBoxHeader() /*++
Routine Description:
Destructor.
Arguments:
N/A
Return Value:
N/A
--*/ { //
// Kill the header control and the
// font
//
if (m_pHCtrl) { delete m_pHCtrl; }
//
// Leave the listbox pointer alone, as we don't
// own it, but are merely associated with it.
//
}
//
// Message Map
//
BEGIN_MESSAGE_MAP(CRMCListBoxHeader, CStatic) //{{AFX_MSG_MAP(CRMCListBoxHeader)
ON_WM_DESTROY() ON_WM_SETFOCUS() //}}AFX_MSG_MAP
ON_NOTIFY_RANGE(HDN_ENDTRACK, 0, 0xFFFF, OnHeaderEndTrack) ON_NOTIFY_RANGE(HDN_ITEMCHANGED, 0, 0xFFFF, OnHeaderItemChanged) ON_NOTIFY_RANGE(HDN_ITEMCLICK, 0, 0xFFFF, OnHeaderItemClick)
END_MESSAGE_MAP()
void CRMCListBoxHeader::OnSetFocus(CWnd * pWnd) { m_pListBox->SetFocus(); }
void CRMCListBoxHeader::OnDestroy() /*++
Routine Description:
WM_DESTROY message handler. When the control is being destroyed, also destroy the invisible static control.
Arguments:
None
Return Value:
None
--*/ { //
// Destroy optional header control
//
if (m_pHCtrl) { m_pHCtrl->DestroyWindow(); }
CStatic::OnDestroy(); }
BOOL CRMCListBoxHeader::Create( IN DWORD dwStyle, IN const RECT & rect, IN CWnd * pParentWnd, IN CHeaderListBox * pListBox, IN UINT nID ) /*++
Routine Description:
Create the control. This will first create an invisible static window, which is to take up the entire area of the listbox. This static window then will be the parent to the listbox as well as this header control.
Arguments:
DWORD dwStyle : Creation style bits const RECT & rect : Rectangle in which the header is to be created CWnd * pParentWnd : Parent window CHeaderListBox * pListBox : Associated listbox UINT nID : Control ID of the header
Return Value:
TRUE for success, FALSE for failure
--*/ { //
// Make sure the real header control exists by now
//
if (m_pHCtrl == NULL) { return FALSE; }
//
// Make sure there's an associated listbox
//
m_pListBox = pListBox; if (m_pListBox == NULL) { return FALSE; }
//
// Create the controlling static window as do-nothing window
//
if (!CStatic::Create(NULL, WS_VISIBLE | WS_TABSTOP | SS_BITMAP | WS_CHILD, rect, pParentWnd, 0xFFFF)) { return FALSE; }
//
// Now create the header control. Its parent
// window is this static control we just created
//
CRect rc(0, 0, 0 ,0); dwStyle |= (UseButtons() ? HDS_BUTTONS : 0L); VERIFY(m_pHCtrl->Create(dwStyle, rc, this, nID)); m_pHCtrl->ModifyStyle(HDS_DRAGDROP, 0);
//
// Place header control as per style bits,
// compute the desired layout, and move it
//
HD_LAYOUT hdl; WINDOWPOS wp;
GetClientRect(&rc); hdl.prc = &rc; hdl.pwpos = ℘
m_pHCtrl->Layout(&hdl); m_pHCtrl->SetWindowPos(m_pListBox, wp.x, wp.y, wp.cx, wp.cy, wp.flags | SWP_SHOWWINDOW);
//
// And move our associated listbox just below it
//
::GetDlgCtlRect(GetParent()->m_hWnd, m_pListBox->m_hWnd, &rc); rc.top += wp.cy - 1;
//
// Adjust if header is bigger than the entire listbox
//
if (rc.top > rc.bottom) { rc.top = rc.bottom; } // Fix for theme support. Make listbox and header children of the same static control
m_pListBox->SetParent(this); GetParent()->ClientToScreen(&rc); ScreenToClient(&rc); m_pListBox->MoveWindow(rc.left, rc.top, rc.Width(), rc.Height());
//
// Make sure the header uses the right font
//
m_pHCtrl->SetFont( CFont::FromHandle((HFONT)::GetStockObject(DEFAULT_GUI_FONT)), FALSE );
return TRUE; }
void CRMCListBoxHeader::OnHeaderEndTrack( IN UINT nId, IN NMHDR * pnmh, OUT LRESULT * pResult ) /*++
Routine Description:
User has finished dragging the column divider. If we're supposed to ensure that the last column is a stretch column, turn off the redraw now -- it will get turned back on after the column width changes have all been completed. This will reduce the flash effect.
Arguments:
UINT nId : Control ID NMHDR * pnmh : Notification header structure LRESULT * pResult : Result. Will be set to 0 if the message was handled
Return Value:
None (handled in pResult)
--*/ { pnmh; nId;
if (DoesRespondToColumnWidthChanges() && UseStretch()) { //
// This will get turned back on in OnHeaderItemChanged
//
SetRedraw(FALSE); }
*pResult = 0; }
void CRMCListBoxHeader::SetColumnWidth( IN int nCol, IN int nWidth ) /*++
Routine Description:
Set the given column to the given width
Arguments:
int nCol : Column number int nWidth : New width
Return Value:
None
--*/ { ASSERT(nCol < QueryNumColumns());
if (nCol >= QueryNumColumns()) { return; }
TRACEEOLID("Setting width of column " << nCol << " to " << nWidth);
HD_ITEM hdItem;
hdItem.mask = HDI_WIDTH; hdItem.cxy = nWidth; VERIFY(SetItem(nCol, &hdItem)); }
void CRMCListBoxHeader::OnHeaderItemChanged( IN UINT nId, IN NMHDR *pnmh, OUT LRESULT *pResult ) /*++
Routine Description:
Handle change in header column width. Note: we're actually tracking the HDN_ITEMCHANGED notification, not the HDN_ENDDRAG one, because the latter is sent out before the column widths in the structure have changed.
Arguments:
UINT nId : Control ID NMHDR * pnmh : Notification header structure LRESULT * pResult : Result. Will be set to 0 if the message was handled
Return Value:
None (handled in pResult)
--*/ { nId; //
// Adjust tabs in associate listbox if
// column widths have changed
//
HD_NOTIFY * pNotify = (HD_NOTIFY *)pnmh; if (DoesRespondToColumnWidthChanges() && pNotify->pitem->mask & HDI_WIDTH) { ASSERT_PTR(m_pListBox);
//
// Stretch the last column
//
if (UseStretch()) { //
// Turn this off, as we don't want
// to get in an infinite loop
//
RespondToColumnWidthChanges(FALSE);
//
// Compute available space
//
CRect rc; GetClientRect(&rc);
//
// See how much is taken up by preceding
// columns
//
int nTotalWidth = 0; int cColumns = QueryNumColumns(); int nLastCol = cColumns - 1; ASSERT(nLastCol >= 0);
for (int nCol = 0; nCol < nLastCol; ++nCol) { int nWidth = GetColumnWidth(nCol);
//
// Each column must be at least one pixel wide
//
int nMaxWidth = rc.Width() - nTotalWidth - (nLastCol - nCol); if (nWidth > nMaxWidth) { nWidth = nMaxWidth; SetColumnWidth(nCol, nWidth); }
nTotalWidth += nWidth; }
//
// Make sure the last column takes up the rest
//
if (rc.Width() > nTotalWidth) { SetColumnWidth(nLastCol, rc.Width() - nTotalWidth); }
//
// Turn this back on again
//
RespondToColumnWidthChanges(TRUE);
//
// Redraw will have been turned off in
// OnHeaderEndTrack, now that all column
// movement has completed, turn it back
// on to draw the control in its current
// state.
//
SetRedraw(TRUE); Invalidate(); }
//
// Recompute tabs on associate listbox,
// and force redraw on it.
//
m_pListBox->SetRedraw(FALSE); SetTabsFromHeader(); m_pListBox->SetRedraw(TRUE); m_pListBox->Invalidate(); }
*pResult = 0; }
void CRMCListBoxHeader::OnHeaderItemClick( IN UINT nId, IN NMHDR *pnmh, OUT LRESULT *pResult ) /*++
Routine Description:
A button has been clicked in the header control. Pass it on to the real parent window.
Arguments:
UINT nId : Control ID NMHDR * pnmh : Notification header structure LRESULT * pResult : Result. Will be set to 0 if the message was handled
Return Value:
None (handled in pResult)
--*/ { //
// Pass notification on to parent
//
ASSERT(GetParent()); GetParent()->SendMessage(WM_NOTIFY, (WPARAM)nId, (LPARAM)pnmh); *pResult = 0; }
void CRMCListBoxHeader::SetTabsFromHeader() /*++
Routine Description:
Set the tabs (which are cumulative) from the header control columns (which are not)
Arguments:
None
Return Value:
None
--*/ { //
// Must have the same number of tabs
// as header columns
//
ASSERT_PTR(m_pListBox); ASSERT(GetItemCount() == m_pListBox->NumTabs());
int nTab = 0; for (int n = 0; n < m_pListBox->NumTabs(); ++n) { m_pListBox->SetTab(n, nTab += GetColumnWidth(n)); } }
int CRMCListBoxHeader::GetItemCount() const /*++
Routine Description:
Get the number of items in the header
Arguments:
None
Return Value:
The number of items in the header (e.g. the number of columns)
--*/ { ASSERT_PTR(m_pHCtrl); return m_pHCtrl->GetItemCount(); }
BOOL CRMCListBoxHeader::GetItem( IN int nPos, OUT HD_ITEM * pHeaderItem ) const /*++
Routine Description:
Get information on specific position (column index)
Arguments:
int nPos : Column index HD_ITEM * pHeaderItem : Header item information
Return Value:
TRUE for success, FALSE for failure (bad column index)
--*/ { ASSERT_PTR(m_pHCtrl); return m_pHCtrl->GetItem(nPos, pHeaderItem); }
int CRMCListBoxHeader::GetColumnWidth( IN int nPos ) const /*++
Routine Description:
Get column width of a specific column
Arguments:
int nPos : Column index
Return Value:
The column width of the given colum, or -1 in case of failure (bad column index)
--*/ { HD_ITEM hi;
hi.mask = HDI_WIDTH; if (GetItem(nPos, &hi)) { return hi.cxy; }
return -1; }
BOOL CRMCListBoxHeader::SetItem( IN int nPos, IN HD_ITEM * pHeaderItem ) /*++***
Routine Description:
Set information on specific position (column index)
Arguments:
int nPos : Column index HD_ITEM * pHeaderItem : Header item information
Return Value:
TRUE for success, FALSE for failure (bad column index)
--*/ { ASSERT_PTR(m_pHCtrl); ASSERT_PTR(m_pListBox);
if (!m_pHCtrl->SetItem(nPos, pHeaderItem)) { return FALSE; }
if (pHeaderItem->mask & HDI_WIDTH) { SetTabsFromHeader(); }
return TRUE; }
int CRMCListBoxHeader::InsertItem( IN int nPos, IN HD_ITEM * pHeaderItem ) /*++
Routine Description:
insert information in specific position (column index)
Arguments:
int nPos : Column index HD_ITEM * pHeaderItem : Header item information
Return Value:
The new index, or -1 in case of failure.
--*/ { ASSERT_PTR(m_pHCtrl); ASSERT_PTR(m_pListBox);
int nCol = m_pHCtrl->InsertItem(nPos, pHeaderItem); if (nCol != -1) { //
// Set 0-width tab, as tabs get recomputed anyway
//
m_pListBox->InsertTab(nPos, 0); SetTabsFromHeader(); }
return nCol; }
BOOL CRMCListBoxHeader::DeleteItem( IN int nPos ) /*++
Routine Description:
Delete the given item (i.e. column)
Arguments:
int nPos : Column index
Return Value:
TRUE for success, FALSE for failure (bad column index)
--*/ { ASSERT_PTR(m_pHCtrl); ASSERT_PTR(m_pListBox);
if (!m_pHCtrl->DeleteItem(nPos)) { return FALSE; }
m_pListBox->RemoveTab(nPos, 1);
return TRUE; }
IMPLEMENT_DYNAMIC(CRMCListBoxHeader, CStatic);
CRMCListBox::CRMCListBox() /*++
Routine Description:
Constructor
Arguments:
None
Return Value:
N/A
--*/ : m_fInitialized(FALSE), m_fMultiSelect(FALSE) { }
CRMCListBox::~CRMCListBox() /*++
Routine Description:
Destructor
Arguments:
N/A
Return Value:
N/A
--*/ { }
//
// Message Map
//
BEGIN_MESSAGE_MAP(CRMCListBox, CListBox) //{{AFX_MSG_MAP(CRMCListBox)
ON_WM_CREATE() ON_WM_DESTROY() //}}AFX_MSG_MAP
END_MESSAGE_MAP()
/* virtual */ BOOL CRMCListBox::Initialize() /*++
Routine Description:
This function should be called directly when subclassing an existing listbox, otherwise OnCreate will take care of it.
Arguments:
None
Return Value:
TRUE for success, FALSE for failure
--*/ { //
// Make sure we're only initialized once
//
if (m_fInitialized) { return TRUE; }
//
// Ensure the base class knows our window
// handle
//
AttachWindow(this);
if (!CODLBox::Initialize()) { return FALSE; }
m_fInitialized = TRUE;
DWORD dwStyle = GetStyle(); m_fMultiSelect = (dwStyle & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)) != 0;
return m_fInitialized; }
void CRMCListBox::MeasureItem( IN LPMEASUREITEMSTRUCT lpMIS ) /*++
Routine Description:
CListBox override to ODL base class
Arguments:
LPMEASUREITEMSTRUCT lpMIS : Measure item structure
Return Value:
None
--*/ { CODLBox::__MeasureItem(lpMIS); }
void CRMCListBox::DrawItem( IN LPDRAWITEMSTRUCT lpDIS ) /*++
Routine Description:
CListBox override to ODL base class
Arguments:
LPDRAWITEMSTRUCT lpDIS : Drawing item structure
Return Value:
None
--*/ { CODLBox::__DrawItem(lpDIS); }
/* virtual */ void CRMCListBox::DrawItemEx( IN CRMCListBoxDrawStruct & dw ) /*++
Routine Description:
Do-nothing extended draw function, which should be provided by the derived class. This one will ASSERT, and should never be called.
Arguments:
CRMCListBoxDrawStruct & dw : Draw Structure
Return Value:
None
--*/ { dw; ASSERT_MSG("Derived class did not provide DrawItemEx"); }
/* virtual */ int CRMCListBox::__GetCount() const /*++
Routine Description:
Provide GetCount() to ODL base class
Arguments:
None
Return Value:
Count of items in the listbox
--*/ { return GetCount(); }
/* virtual */ int CRMCListBox::__SetItemHeight( IN int nIndex, IN UINT cyItemHeight ) /*++
Routine Description:
Provide SetItemHeight() to ODL base class
Arguments:
None
Return Value:
LB_ERR if the index or height is invalid.
--*/ { return SetItemHeight(nIndex, cyItemHeight); }
int CRMCListBox::OnCreate( IN LPCREATESTRUCT lpCreateStruct ) /*++
Routine Description:
Listbox is being created
Arguments:
LPCREATESTRUCT lpCreateStruct : Creation structure
Return Value:
-1 for failure, 0 for success
--*/ { if (CListBox::OnCreate(lpCreateStruct) == -1) { return -1; }
Initialize();
return 0; }
int CRMCListBox::GetCurSel() const /*++
Routine Description:
Get the index of the current selected item
Arguments:
None
Return Value:
On multi-selection listbox, it will return the index of an item, iff that is the only item selected.
On single-selection listbox, it behaves as normal.
--*/ { if (IsMultiSelect()) { //
// We only like it if one item is selected
//
int nCurSel = LB_ERR;
if (CListBox::GetSelCount() == 1) { if (CListBox::GetSelItems(1, &nCurSel) != 1) { nCurSel = LB_ERR; } }
return nCurSel; }
//
// Single select listbox
//
return CListBox::GetCurSel(); }
int CRMCListBox::SetCurSel( IN int nSelect ) /*++
Routine Description:
Select an item. On a multi-select listbox, this will deselect everything except the given item.
Arguments:
int nSelect : Index of the item to be selected, or -1 to reset all selections.
Return Value:
LB_ERR in case of error.
--*/ { if (IsMultiSelect()) { //
// Reset all selections
//
int nReturn = SelItemRange(FALSE, 0, GetCount() - 1);
if (nSelect >= 0) { //
// Ensure item is visible
//
nReturn = CListBox::SetSel(nSelect, TRUE); CListBox::SetCaretIndex(nSelect, 0); }
return nReturn; }
return CListBox::SetCurSel(nSelect); }
int CRMCListBox::GetSel( IN int nSel ) const /*++
Routine Description:
Determine if the given item is selected or not Works for both single and multi-select listboxes
Arguments:
int nSel : Item whose state to check
Return Value:
LB_ERR in case of error, 0 if the item in question is not selected, a positive number if it is.
--*/ { if (IsMultiSelect()) { return CListBox::GetSel(nSel); }
//
// Some magic for single select
//
if (nSel < 0 || nSel >= CListBox::GetCount()) { return LB_ERR; }
return nSel == CListBox::GetCurSel() ? TRUE : FALSE; }
int CRMCListBox::GetSelCount() const /*++
Routine Description:
Return count of selected items. Works for both single and multi select (in the former case, it will return zero or one only)
Arguments:
None
Return Value:
Count of selected items
--*/ { if (IsMultiSelect()) { return CListBox::GetSelCount(); }
return GetCurSel() != LB_ERR ? 1 : 0; }
void * CRMCListBox::GetSelectedListItem( OUT int * pnSel OPTIONAL ) /*++
Routine Description:
Return the single selected item in the list or NULL
Arguments:
int * pnSel : Optionally returns the selected index
Returns:
The currently selected (single) item, or NULL if 0 or more than one items is selected. Works for both multi-select and single select.
--*/ { void * pItem = NULL;
int nCurSel = GetCurSel(); if (nCurSel >= 0) { //
// Get item properties
//
pItem = GetItemDataPtr(nCurSel); if (pnSel) { *pnSel = nCurSel; } }
return pItem; }
void * CRMCListBox::GetNextSelectedItem( IN OUT int * pnStartingIndex ) /*++
Routine Description:
Return the next selected item starting at a specific index.
Arguments:
int *pnStartingIndex : Starting index (>= 0)
Return Value:
Pointer to next selected item, or NULL if there are none left.
The starting index will be updated to reflect the current index, LB_ERR if no more selected items remain.
--*/ { ASSERT_READ_WRITE_PTR(pnStartingIndex);
if (!pnStartingIndex) { return NULL; }
ASSERT(*pnStartingIndex >= 0);
if (*pnStartingIndex < 0) { *pnStartingIndex = 0; }
if (IsMultiSelect()) { //
// Multi-select -- loop through
// until found
//
BOOL fFoundItem = FALSE;
while (*pnStartingIndex < GetCount()) { if (CListBox::GetSel(*pnStartingIndex) > 0) { ++fFoundItem; break; }
++(*pnStartingIndex); }
if (!fFoundItem) { *pnStartingIndex = LB_ERR; } } else { //
// Single select listbox, so there's no
// looping through -- either the selected item
// (if any) is in range or it isn't.
//
int nCurSel = CListBox::GetCurSel(); *pnStartingIndex = (nCurSel >= *pnStartingIndex) ? nCurSel : LB_ERR; }
return (*pnStartingIndex != LB_ERR) ? GetItemDataPtr(*pnStartingIndex) : NULL; }
BOOL CRMCListBox::SelectItem( IN void * pItemData ) /*++
Routine Description:
Select the listbox item with the given data pointer
Arguments:
void * pItemData : Item to search for
Return Value:
TRUE if the item was found and selected, FALSE otherwise
Notes:
On a multi-select listbox, this will unselect all other items in the listbox.
--*/ { if (pItemData != NULL) { for (int n = 0; n < GetCount(); ++n) { if (pItemData == GetItemDataPtr(n)) { SetCurSel(n);
return TRUE; } } }
if (!IsMultiSelect()) { //
// Set no selection
//
SetCurSel(-1); }
return FALSE; }
void CRMCListBox::InvalidateSelection( IN int nSel ) /*++
Routine Description:
Force a repaint of the given selection
Arguments:
int nSel : Index of the item to be repainted
Return Value:
None
--*/ { CRect rc;
if (GetItemRect(nSel, &rc) != LB_ERR) { InvalidateRect(&rc, TRUE); } }
IMPLEMENT_DYNAMIC(CRMCListBox,CListBox);
CHeaderListBox::CHeaderListBox( IN DWORD dwStyle, IN LPCTSTR lpRegKey OPTIONAL ) /*++
Routine Description:
Constructor
Arguments:
DWORD dwStyle : Style bits (see HLS_*) LPCTSTR lpRegKey : If specified, the registry key where the column sizes will be stored.
Return Value:
None
--*/ : m_strRegKey(), m_fInitialized(FALSE) { m_pHeader = new CRMCListBoxHeader(dwStyle); if (lpRegKey) { GenerateRegistryKey(m_strRegKey, lpRegKey); } }
CHeaderListBox::~CHeaderListBox() /*++
Routine Description:
Destructor
Arguments:
N/A
Return Value:
N/A
--*/ { //
// Clean up header control
//
ASSERT_PTR(m_pHeader);
if (m_pHeader != NULL) { delete m_pHeader; } }
//
// Message map
//
BEGIN_MESSAGE_MAP(CHeaderListBox, CRMCListBox) //{{AFX_MSG_MAP(CHeaderListBox)
ON_WM_CREATE() ON_WM_DESTROY() ON_WM_SETFOCUS() //}}AFX_MSG_MAP
END_MESSAGE_MAP()
/* virtual */ BOOL CHeaderListBox::Initialize() /*++
Routine Description:
This function should be called directly when subclassing an existing listbox, otherwise OnCreate will take care of it, and this function should not be called
Arguments:
None
Return Value:
TRUE for success, FALSE for failure
--*/ { //
// Make sure we're only initialized once
//
if (m_fInitialized) { return TRUE; }
if (!CRMCListBox::Initialize()) { return FALSE; }
//
// Create header control
//
ASSERT_PTR(m_pHeader);
if (m_pHeader) { TRACEEOLID("Creating Header");
//
// Create it in our location exactly
//
CRect rc; ::GetDlgCtlRect(GetParent()->m_hWnd, m_hWnd, &rc);
//
// Make sure the header control shares the same parent
// as we do,
//
ASSERT(GetParent());
#ifndef CCS_NOHILITE
#define CCS_NOHILITE 0x00000010L
#endif
DWORD dwStyle = WS_VISIBLE | WS_TABSTOP | CCS_TOP | CCS_NODIVIDER | WS_BORDER | HDS_HORZ;
if (!m_pHeader->Create(dwStyle, rc, GetParent(), this, 0xFFFF)) { return FALSE; } }
m_fInitialized = TRUE;
return TRUE; }
int CHeaderListBox::QueryColumnWidth( IN int nCol ) const /*++
Routine Description:
Get the width of the specified column
Arguments:
int nCol : The column
Return Value:
The width of the column, or -1 if the column index was out of range
--*/ { ASSERT(nCol < QueryNumColumns());
if (nCol >= QueryNumColumns()) { return -1; }
HD_ITEM hdItem;
hdItem.mask = HDI_WIDTH; VERIFY(GetHeaderItem(nCol, &hdItem));
return hdItem.cxy; }
BOOL CHeaderListBox::SetColumnWidth( IN int nCol, IN int nWidth ) /*++
Routine Description:
Set the width of the specified column
Arguments:
int nCol : The column int nWidth : New width
Return Value:
TRUE for success, FALSE for failure
--*/ { ASSERT(nCol < QueryNumColumns());
if (nCol >= QueryNumColumns()) { return FALSE; }
TRACEEOLID("Setting width of column " << nCol << " to " << nWidth);
HD_ITEM hdItem; hdItem.mask = HDI_WIDTH; hdItem.cxy = nWidth; VERIFY(SetHeaderItem(nCol, &hdItem));
return TRUE; }
BOOL CHeaderListBox::SetWidthsFromReg() /*++
Routine Description:
Attempt to set the column widths from the registry value we were initialized with.
Arguments:
None
Return Value:
TRUE if the column widths were succesfully set from the registry, FALSE otherwise
--*/ { if (m_strRegKey.IsEmpty()) { //
// No reg key specified
//
return FALSE; }
//
// Try to read the current column sizes from the registry
//
CRegKey rkUser; if (ERROR_SUCCESS != rkUser.Create(HKEY_CURRENT_USER, m_strRegKey)) { //
// Path doesn't exist -- no problem.
//
return FALSE; }
//
// Don't auto adjust
//
m_pHeader->RespondToColumnWidthChanges(FALSE);
CRect rc; m_pHeader->GetClientRect(&rc);
CError err;
try { TCHAR buf[MAX_PATH]; DWORD count = MAX_PATH; int nTotalWidth = 0;
err = rkUser.QueryValue(buf, g_szRegColumns, &count);
if (err.Succeeded() && lstrlen(buf) > 0) { LPTSTR lpstrValue = buf; LPTSTR lpWidth = _tcstok(lpstrValue, g_szColValueSep);
for (int n = 0; n < QueryNumColumns(); ++n) { ASSERT_PTR(lpWidth);
if (lpWidth == NULL) { err = ERROR_INVALID_PARAMETER; break; }
//
// Sanity check
//
int nWidth = _ttoi(lpWidth); if (nWidth <= 0 || (nTotalWidth + nWidth > rc.Width())) { ASSERT_MSG("column width invalid");
return FALSE; }
nTotalWidth += nWidth;
VERIFY(SetColumnWidth(n, nWidth));
lpWidth = _tcstok(NULL, g_szColValueSep); } } } catch(CMemoryException * e) { err = ERROR_NOT_ENOUGH_MEMORY; e->Delete(); } //
// Turn auto-adjust back on
//
m_pHeader->RespondToColumnWidthChanges(TRUE);
// if (err.Win32Error() == ERROR_FILE_NOT_FOUND)
// {
// // No problem, it is first run. We will set defaults.
// return err;
// }
return err; }
void CHeaderListBox::DistributeColumns() /*++
Routine Description:
Proportion the column widths of over the entire width of the header control while maintaining relative proportions.
Arguments:
None
Return Value:
None
--*/ { //
// Obtain available width
//
ASSERT_PTR(m_pHeader);
CRect rc; m_pHeader->GetClientRect(&rc);
//
// Get current total width
//
int nTotalWeight = 0; int nCol; for (nCol = 0; nCol < QueryNumColumns(); ++nCol) { nTotalWeight += QueryColumnWidth(nCol); }
//
// And spread out the width, maintaining the same
// proportions
//
//
// Temporarily ignore changes
//
m_pHeader->RespondToColumnWidthChanges(FALSE); int cColumns = QueryNumColumns();
for (nCol = 0; nCol < cColumns; ++nCol) { int nWidth = QueryColumnWidth(nCol); nWidth = rc.Width() * nWidth / nTotalWeight; VERIFY(SetColumnWidth(nCol, nWidth)); }
//
// Turn changes back on
//
m_pHeader->RespondToColumnWidthChanges(TRUE); }
int CHeaderListBox::InsertColumn( IN int nCol, IN int nWeight, IN UINT nStringID, IN HINSTANCE hResInst ) /*++
Routine Description:
Insert column. The width of the column is actually a relative "weight" of the column which needs to be adjusted later. The return value is the column number or -1 if the column is not inserted.
Arguments:
int nCol : Column number int nWeight : Relative weight of column UINT nStringID : Resource string ID
Return Value:
Index of the column, or -1 in case of failure
--*/ { CString strColName; HD_ITEM hdItem;
HINSTANCE hInst = AfxGetResourceHandle(); AfxSetResourceHandle(hResInst); VERIFY(strColName.LoadString(nStringID)); AfxSetResourceHandle(hInst);
hdItem.mask = HDI_FORMAT | HDI_WIDTH | HDI_TEXT; hdItem.fmt = HDF_STRING | HDF_LEFT; hdItem.pszText = (LPTSTR)(LPCTSTR)strColName; hdItem.cchTextMax = strColName.GetLength(); hdItem.cxy = nWeight;
return InsertHeaderItem(nCol, &hdItem); }
int CHeaderListBox::OnCreate( IN LPCREATESTRUCT lpCreateStruct ) /*++
Routine Description:
Listbox is being created
Arguments:
LPCREATESTRUCT lpCreateStruct : Creation structure
Return Value:
0 for success, -1 for failure
--*/ { if (CRMCListBox::OnCreate(lpCreateStruct) == -1) { return -1; }
Initialize();
return 0; }
BOOL CHeaderListBox::EnableWindow( IN BOOL bEnable ) /*++
Routine Description:
Enable/disable the control.
Arguments:
BOOL bEnable : TRUE to enable the control, FALSE to disable
Return Value:
Indicates the state before the EnableWindow member function was called. The return value is nonzero if the window was previously disabled. The return value is 0 if the window was previously enabled or an error occurred.
--*/ { if (m_pHeader) { m_pHeader->EnableWindow(bEnable); }
return CRMCListBox::EnableWindow(bEnable); }
BOOL CHeaderListBox::ShowWindow( IN int nCmdShow ) /*++
Routine Description:
Show/hide the window
Arguments:
int nCmdShow : SW_ flag such as SW_SHOW or SW_HIDE
Return Value:
If the window was previously visible, the return value is TRUE. If the window was previously hidden, the return value is FALSE.
--*/ { if (m_pHeader) { m_pHeader->ShowWindow(nCmdShow); }
return CRMCListBox::ShowWindow(nCmdShow); }
void CHeaderListBox::OnDestroy() /*++
Routine Description:
Handle destruction of the control
Arguments:
None
Return Value:
None
--*/ { //
// Destroy optional header control
//
ASSERT_PTR(m_pHeader);
if (m_pHeader) { if (!m_strRegKey.IsEmpty()) { //
// Try to write the current column sizes to the registry
//
CError err;
CRegKey rkUser; rkUser.Create(HKEY_CURRENT_USER, m_strRegKey);
int nWidth; TCHAR szValue[32]; CString strValue;
try { for (int n = 0; n < GetHeaderItemCount(); ++n) { if (n > 0) { //
// Put in field separator
//
strValue += g_szColValueSep; }
nWidth = m_pHeader->GetColumnWidth(n); strValue += ::_itot(nWidth, szValue, 10); }
err = rkUser.SetValue(strValue, g_szRegColumns); } catch(CMemoryException * e) { err = ERROR_NOT_ENOUGH_MEMORY; e->Delete(); }
err.MessageBoxOnFailure(m_hWnd); }
m_pHeader->DestroyWindow(); }
CRMCListBox::OnDestroy(); }
IMPLEMENT_DYNAMIC(CHeaderListBox, CRMCListBox);
CRMCComboBox::CRMCComboBox() /*++
Routine Description:
Constructor
Arguments:
None
Return Value:
N/A
--*/ : m_fInitialized(FALSE) { }
CRMCComboBox::~CRMCComboBox() /*++
Routine Description:
Destructor
Arguments:
N/A
Return Value:
N/A
--*/ { }
//
// Message Map
//
BEGIN_MESSAGE_MAP(CRMCComboBox, CComboBox) //{{AFX_MSG_MAP(CRMCComboBox)
ON_WM_CREATE() //}}AFX_MSG_MAP
END_MESSAGE_MAP()
/* virtual */ BOOL CRMCComboBox::Initialize() /*++
Routine Description:
This function should be called directly when subclassing an existing combobox, otherwise OnCreate will take care of it.
Arguments:
None
Return Value:
TRUE for success, FALSE for failure
--*/ { //
// Make sure we're only initialized once
//
if (m_fInitialized) { return TRUE; }
//
// Ensure the base class knows our window
// handle
//
AttachWindow(this);
if (!CODLBox::Initialize()) { return FALSE; }
m_fInitialized = TRUE;
return TRUE; }
void CRMCComboBox::MeasureItem( IN LPMEASUREITEMSTRUCT lpMIS ) /*++
Routine Description:
CComboBox override to ODL base class
Arguments:
LPMEASUREITEMSTRUCT lpMIS : Measure item structure
Return Value:
None
--*/ { CODLBox::__MeasureItem(lpMIS); }
void CRMCComboBox::DrawItem( IN LPDRAWITEMSTRUCT lpDIS ) /*++
Routine Description:
CListBox override to ODL base class
Arguments:
LPDRAWITEMSTRUCT lpDIS : Drawing item structure
Return Value:
None
--*/ { CODLBox::__DrawItem(lpDIS); }
/* virtual */ void CRMCComboBox::DrawItemEx( IN CRMCListBoxDrawStruct & dw ) /*++
Routine Description:
Do-nothing extended draw function, which should be provided by the derived class. This one will ASSERT, and should never be called.
Arguments:
CRMCListBoxDrawStruct & dw : Draw Structure
Return Value:
None
--*/ { dw; ASSERT_MSG("Derived class did not provide DrawItemEx"); }
int CRMCComboBox::OnCreate( IN LPCREATESTRUCT lpCreateStruct ) /*++
Routine Description:
Combo box is being created
Arguments:
LPCREATESTRUCT lpCreateStruct : Creation structure
Return Value:
-1 for failure, 0 for success
--*/ { if (CComboBox::OnCreate(lpCreateStruct) == -1) { return -1; }
Initialize();
return 0; }
/* virtual */ int CRMCComboBox::__GetCount() const /*++
Routine Description:
Provide CComboBox::GetCount() functionality to base class
Arguments:
None
Return Value:
Get the count of items in the combo box
--*/ { return GetCount(); }
/* virtual */ int CRMCComboBox::__SetItemHeight( IN int nIndex, IN UINT cyItemHeight ) /*++
Routine Description:
Provide CListBox::SetItemHeight() functionality to base class.
Arguments:
int nIndex : Index of the item UINT cyItemHeight : Height of the item
Return Value:
SetItemHeight return value.
--*/ { return SetItemHeight(nIndex, cyItemHeight); }
IMPLEMENT_DYNAMIC(CRMCComboBox,CComboBox);
|