463 lines
10 KiB
C++
463 lines
10 KiB
C++
|
//============================================================================
|
||
|
// Copyright (C) Microsoft Corporation, 1996 - 1999
|
||
|
//
|
||
|
// File: lcx.cpp
|
||
|
//
|
||
|
// History:
|
||
|
// 07/13/96 Abolade Gbadegesin Created, based on C code by Steve Cobb
|
||
|
//
|
||
|
// Implements an enhanced list-control.
|
||
|
//============================================================================
|
||
|
#include "stdafx.h"
|
||
|
#include "resource.h"
|
||
|
#include "util.h"
|
||
|
#include "listctrl.h"
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
#define new DEBUG_NEW
|
||
|
#undef THIS_FILE
|
||
|
static char THIS_FILE[] = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
IMPLEMENT_DYNAMIC(CListCtrlEx, CListCtrl)
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
|
||
|
//{{AFX_MSG_MAP(CListCtrlEx)
|
||
|
ON_WM_LBUTTONDOWN()
|
||
|
ON_WM_CHAR()
|
||
|
ON_WM_KEYDOWN()
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: CListCtrlEx::~CListCtrlEx
|
||
|
//
|
||
|
// Destructor. Deletes the image list, if any, and unloads row-information.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
CListCtrlEx::~CListCtrlEx(
|
||
|
) {
|
||
|
|
||
|
delete m_pimlChecks;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: CListCtrlEx::GetColumnCount
|
||
|
//
|
||
|
// Called to retrieve the number of columns in any list-control.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
INT
|
||
|
CListCtrlEx::GetColumnCount(
|
||
|
) {
|
||
|
|
||
|
return Header_GetItemCount(ListView_GetHeader(m_hWnd));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: CListCtrlEx::SetColumnText
|
||
|
//
|
||
|
// Sets the text in the header of the column in position 'iCol'.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL
|
||
|
CListCtrlEx::SetColumnText(
|
||
|
INT iCol,
|
||
|
LPCTSTR pszText,
|
||
|
INT fmt
|
||
|
) {
|
||
|
|
||
|
LV_COLUMN lvc;
|
||
|
|
||
|
lvc.mask = LVCF_FMT | LVCF_TEXT;
|
||
|
lvc.pszText = (LPTSTR)pszText;
|
||
|
lvc.fmt = fmt;
|
||
|
|
||
|
return SetColumn(iCol, &lvc);
|
||
|
}
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: CListCtrlEx::InstallChecks
|
||
|
//
|
||
|
// Installs check-box handling for the list-control.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL
|
||
|
CListCtrlEx::InstallChecks(
|
||
|
) {
|
||
|
|
||
|
HICON hIcon;
|
||
|
|
||
|
//
|
||
|
// Make sure the list-control is in report-mode
|
||
|
//
|
||
|
|
||
|
if (!(GetStyle() & LVS_REPORT)) { return FALSE; }
|
||
|
|
||
|
|
||
|
//
|
||
|
// Allocate a new image-list.
|
||
|
//
|
||
|
|
||
|
m_pimlChecks = new CImageList;
|
||
|
|
||
|
if (!m_pimlChecks) { return FALSE; }
|
||
|
|
||
|
do {
|
||
|
|
||
|
//
|
||
|
// Initialize the image-list
|
||
|
//
|
||
|
|
||
|
if (!m_pimlChecks->Create(
|
||
|
::GetSystemMetrics(SM_CXSMICON),
|
||
|
::GetSystemMetrics(SM_CYSMICON),
|
||
|
ILC_MASK, 2, 2
|
||
|
)) {
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Add the icons for the checked and unchecked images
|
||
|
//
|
||
|
|
||
|
hIcon = ::LoadIcon(
|
||
|
AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_COMMON_UNCHECK)
|
||
|
);
|
||
|
|
||
|
if (!hIcon) { break; }
|
||
|
|
||
|
m_pimlChecks->Add(hIcon); ::DeleteObject(hIcon);
|
||
|
|
||
|
|
||
|
hIcon = ::LoadIcon(
|
||
|
AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_COMMON_CHECK)
|
||
|
);
|
||
|
|
||
|
if (!hIcon) { break; }
|
||
|
|
||
|
m_pimlChecks->Add(hIcon); ::DeleteObject(hIcon);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Replace the list-control's current image-list with the new one
|
||
|
//
|
||
|
|
||
|
m_pimlOldState = SetImageList(m_pimlChecks, LVSIL_STATE);
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
} while(FALSE);
|
||
|
|
||
|
|
||
|
//
|
||
|
// If we arrive here, an error occurred, so clean up and fail
|
||
|
//
|
||
|
|
||
|
delete m_pimlChecks; m_pimlChecks = NULL;
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: CListCtrlEx::UninstallChecks
|
||
|
//
|
||
|
// Uninstalls checkbox-handling for the list-control.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
CListCtrlEx::UninstallChecks(
|
||
|
) {
|
||
|
|
||
|
if (!m_pimlChecks) { return; }
|
||
|
|
||
|
if (m_pimlOldState) { SetImageList(m_pimlOldState, LVSIL_STATE); }
|
||
|
|
||
|
delete m_pimlChecks; m_pimlChecks = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: CListCtrlEx::GetCheck
|
||
|
//
|
||
|
// Returns TRUE if the specified item is checked, FALSE otherwise.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL
|
||
|
CListCtrlEx::GetCheck(
|
||
|
INT iItem
|
||
|
) {
|
||
|
|
||
|
return !!(GetItemState(iItem, LVIS_STATEIMAGEMASK) &
|
||
|
INDEXTOSTATEIMAGEMASK(LCXI_CHECKED));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: CListCtrlEx::SetCheck
|
||
|
//
|
||
|
// If 'fCheck' is non-zero, checks 'iItem', otherwise clears 'iItem'.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
CListCtrlEx::SetCheck(
|
||
|
INT iItem,
|
||
|
BOOL fCheck
|
||
|
) {
|
||
|
|
||
|
SetItemState(
|
||
|
iItem,
|
||
|
INDEXTOSTATEIMAGEMASK(fCheck ? LCXI_CHECKED : LCXI_UNCHECKED),
|
||
|
LVIS_STATEIMAGEMASK
|
||
|
);
|
||
|
|
||
|
if (GetParent()) {
|
||
|
|
||
|
NMHDR nmh;
|
||
|
|
||
|
nmh.code = LVXN_SETCHECK;
|
||
|
nmh.hwndFrom = m_hWnd;
|
||
|
|
||
|
::SendMessage(
|
||
|
GetParent()->m_hWnd, WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmh
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: CListCtrlEx::OnChar
|
||
|
//
|
||
|
// Handles the 'WM_CHAR' message for the list-control.
|
||
|
// This allows users to change items' checked-states using the keyboard.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
CListCtrlEx::OnChar(
|
||
|
UINT nChar,
|
||
|
UINT nRepCnt,
|
||
|
UINT nFlags
|
||
|
) {
|
||
|
|
||
|
BOOL fSet = FALSE;
|
||
|
BOOL fClear = FALSE;
|
||
|
BOOL fToggle = FALSE;
|
||
|
INT iItem;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Handle characters with special meaning for us
|
||
|
//
|
||
|
|
||
|
switch (nChar) {
|
||
|
|
||
|
case TEXT(' '): { fToggle = TRUE; break; }
|
||
|
|
||
|
case TEXT('+'):
|
||
|
case TEXT('='): { fSet = TRUE; break; }
|
||
|
|
||
|
case TEXT('-'): { fClear = TRUE; break; }
|
||
|
}
|
||
|
|
||
|
if (!fToggle && !fSet && !fClear) {
|
||
|
|
||
|
CListCtrl::OnChar(nChar, nRepCnt, nFlags);
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
//
|
||
|
// Change the state of all the selected items
|
||
|
//
|
||
|
|
||
|
for (iItem = GetNextItem(-1, LVNI_SELECTED);
|
||
|
iItem != -1;
|
||
|
iItem = GetNextItem(iItem, LVNI_SELECTED)) {
|
||
|
|
||
|
if (fToggle) {
|
||
|
|
||
|
SetCheck(iItem, !GetCheck(iItem));
|
||
|
}
|
||
|
else
|
||
|
if (fSet) {
|
||
|
|
||
|
if (!GetCheck(iItem)) { SetCheck(iItem, TRUE); }
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
if (GetCheck(iItem)) { SetCheck(iItem, FALSE); }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: CListCtrlEx::OnKeyDown
|
||
|
//
|
||
|
// Handles the 'WM_KEYDOWN' message for the list-control.
|
||
|
// This allows users to change items' checked-states using the keyboard.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
CListCtrlEx::OnKeyDown(
|
||
|
UINT nChar,
|
||
|
UINT nRepCnt,
|
||
|
UINT nFlags
|
||
|
) {
|
||
|
|
||
|
//
|
||
|
// We want the left-arrow treated as an up-arrow
|
||
|
// and the right-arrow treated as a down-arrow.
|
||
|
//
|
||
|
|
||
|
if (nChar == VK_LEFT) {
|
||
|
|
||
|
CListCtrl::OnKeyDown(VK_UP, nRepCnt, nFlags); return;
|
||
|
}
|
||
|
else
|
||
|
if (nChar == VK_RIGHT) {
|
||
|
|
||
|
CListCtrl::OnKeyDown(VK_DOWN, nRepCnt, nFlags); return;
|
||
|
}
|
||
|
|
||
|
CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: CListCtrlEx::OnLButtonDown
|
||
|
//
|
||
|
// Handles the 'WM_LBUTTONDOWN' message, changing an item's checked state
|
||
|
// when the user clicks the item's check-image.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
CListCtrlEx::OnLButtonDown(
|
||
|
UINT nFlags,
|
||
|
CPoint pt
|
||
|
) {
|
||
|
|
||
|
INT iItem;
|
||
|
UINT uiFlags;
|
||
|
|
||
|
|
||
|
//
|
||
|
// If the left-mouse button is over the check-box icon,
|
||
|
// we treat it as a toggle on the check-box.
|
||
|
//
|
||
|
|
||
|
uiFlags = 0;
|
||
|
|
||
|
iItem = HitTest(pt, &uiFlags);
|
||
|
|
||
|
|
||
|
if (iItem != -1 && (uiFlags & LVHT_ONITEMSTATEICON)) {
|
||
|
|
||
|
SetCheck(iItem, !GetCheck(iItem));
|
||
|
|
||
|
// Redraw this item
|
||
|
RedrawItems(iItem, iItem);
|
||
|
}
|
||
|
|
||
|
CListCtrl::OnLButtonDown(nFlags, pt);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
// Function: AdjustColumnWidth
|
||
|
//
|
||
|
// Called to adjust the width of column 'iCol' so that the string 'pszContent'
|
||
|
// can be displayed in the column without truncation.
|
||
|
//
|
||
|
// If 'NULL' is specified for 'pszContent', the function adjusts the column
|
||
|
// so that the first string in the column is displayed without truncation.
|
||
|
//
|
||
|
// Returns the new width of the column.
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
INT
|
||
|
AdjustColumnWidth(
|
||
|
IN CListCtrl& listCtrl,
|
||
|
IN INT iCol,
|
||
|
IN LPCTSTR pszContent
|
||
|
) {
|
||
|
|
||
|
INT iWidth, iOldWidth;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Compute the minimum width the column needs to be
|
||
|
//
|
||
|
|
||
|
if (pszContent) {
|
||
|
|
||
|
iWidth = listCtrl.GetStringWidth(pszContent);
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
iWidth = listCtrl.GetStringWidth(listCtrl.GetItemText(0, iCol));
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Adjust 'iWidth' to leave some breathing space
|
||
|
//
|
||
|
|
||
|
iWidth += ::GetSystemMetrics(SM_CXSMICON) +
|
||
|
::GetSystemMetrics(SM_CXEDGE) * 2;
|
||
|
|
||
|
|
||
|
//
|
||
|
// If the column is narrower than 'iWidth', enlarge it.
|
||
|
//
|
||
|
|
||
|
iOldWidth = listCtrl.GetColumnWidth(iCol);
|
||
|
|
||
|
if (iOldWidth < iWidth) {
|
||
|
|
||
|
listCtrl.SetColumnWidth(iCol, iWidth);
|
||
|
|
||
|
iOldWidth = iWidth;
|
||
|
}
|
||
|
|
||
|
return iOldWidth;
|
||
|
}
|
||
|
|
||
|
|
||
|
INT
|
||
|
AdjustColumnWidth(
|
||
|
IN CListCtrl& listCtrl,
|
||
|
IN INT iCol,
|
||
|
IN UINT idsContent
|
||
|
) {
|
||
|
|
||
|
// Needed for Loadstring
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
CString sCol;
|
||
|
|
||
|
sCol.LoadString(idsContent);
|
||
|
|
||
|
return AdjustColumnWidth(listCtrl, iCol, sCol);
|
||
|
}
|
||
|
|