Files
Client/Server/ToolProject/WebInfoManager/ListViewCtrlEx.cpp
LGram16 dd97ddec92 Restructure repository to include all source folders
Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 20:17:20 +09:00

5432 lines
164 KiB
C++

/******************************************************************************
$Author$
$Modtime$
$Revision$
Description: Implementation of class "CListBase"
(list control with sort icons and colored sort column)
$Log$
******************************************************************************/
#include "stdafx.h"
#include "ListViewCtrlEx.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/*** Theme parts and states not defined in Visual Studio *********************/
#if _MSC_VER < 1500
enum NEW_LISTITEMSTATES
{
LISS_NORMAL = 1,
LISS_HOT = 2,
LISS_SELECTED = 3,
LISS_DISABLED = 4,
LISS_SELECTEDNOTFOCUS = 5,
LISS_HOTSELECTED = 6
};
#endif
/*** Definition of class "CListCtrlEx" ***************************************/
#ifndef NDEBUG
ATL::CTraceCategory ListCtrlEx(_T("ListCtrlEx"), 1);
#endif
IMPLEMENT_DYNCREATE(CListCtrlEx, CListCtrl)
/*** Protected member functions **********************************************/
/*** Label editing will be started *******************************************/
BOOL CListCtrlEx::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT*)
{
return CListBase::OnBeginLabelEdit(pNMHDR);
}
/*** Label editing will be cancelled *****************************************/
LRESULT CListCtrlEx::OnCancelEditLabel(WPARAM, LPARAM)
{
return CListBase::OnCancelEditLabel();
}
/*** A column header has been clicked ****************************************/
BOOL CListCtrlEx::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult)
{
return CListBase::OnColumnclick(pNMHDR, pResult);
}
/*** An item from a menu has been selected ***********************************/
BOOL CListCtrlEx::OnCommand(WPARAM wParam, LPARAM)
{
return CListBase::OnCommand(wParam);
}
/*** The user has right clicked the mouse ************************************/
void CListCtrlEx::OnContextMenu(CWnd* pWnd, CPoint point)
{
CListBase::OnContextMenu(pWnd, point);
}
/*** A list view (sub)item will be drawn *************************************/
void CListCtrlEx::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
CListBase::OnCustomDraw(pNMHDR, pResult);
}
/*** All items from a list view control will be removed **********************/
LRESULT CListCtrlEx::OnDeleteAllItems(WPARAM, LPARAM)
{
return CListBase::OnDeleteAllItems();
}
/*** A column in a list view control will be deleted *************************/
LRESULT CListCtrlEx::OnDeleteColumn(WPARAM wParam, LPARAM)
{
return CListBase::OnDeleteColumn(wParam);
}
/*** An item from a list view control will be removed ************************/
LRESULT CListCtrlEx::OnDeleteItem(WPARAM wParam, LPARAM)
{
return CListBase::OnDeleteItem(wParam);
}
/*** The list view control will be destroyed *********************************/
void CListCtrlEx::OnDestroy()
{
CListBase::OnDestroy();
}
/*** Label editing will be finished ******************************************/
BOOL CListCtrlEx::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT*)
{
return CListBase::OnEndLabelEdit(pNMHDR);
}
/*** The background will be erased *******************************************/
BOOL CListCtrlEx::OnEraseBkgnd(CDC* pDC)
{
return CListBase::OnEraseBkgnd(pDC);
}
/*** A specific item of a list view control will be searched *****************/
LRESULT CListCtrlEx::OnFindItem(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnFindItem(wParam, lParam);
}
/*** The attributes of a list view control's column will be retrieved ********/
LRESULT CListCtrlEx::OnGetColumn(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnGetColumn(wParam, lParam);
}
/*** The current left-to-right order of columns in a list view control *******/
/*** will be retrieved *******/
LRESULT CListCtrlEx::OnGetColumnOrderArray(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnGetColumnOrderArray(wParam, lParam);
}
/*** The width of a column in a list view control will be retrieved **********/
LRESULT CListCtrlEx::OnGetColumnWidth(WPARAM wParam, LPARAM)
{
return CListBase::OnGetColumnWidth(wParam);
}
/*** Information needed to display a list view item **************************/
BOOL CListCtrlEx::OnGetdispinfo(NMHDR* pNMHDR, LRESULT*)
{
return CListBase::OnGetdispinfo(pNMHDR);
}
/*** The extended list view style will be retrieved **************************/
LRESULT CListCtrlEx::OnGetExtendedStyle(WPARAM, LPARAM)
{
return m_dwExtendedStyle;
}
/*** Some or all of a list view item's attributes will be retrieved **********/
LRESULT CListCtrlEx::OnGetItem(WPARAM, LPARAM lParam)
{
return CListBase::OnGetItem(lParam);
}
/*** The rectangle bounding an item will be retrieved ************************/
LRESULT CListCtrlEx::OnGetItemRect(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnGetItemRect(wParam, lParam);
}
/*** The text of a list view item or subitem will be retrieved ***************/
LRESULT CListCtrlEx::OnGetItemText(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnGetItemText(wParam, lParam);
}
/*** The rectangle bounding a subitem will be retrieved **********************/
LRESULT CListCtrlEx::OnGetSubItemRect(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnGetSubItemRect(wParam, lParam);
}
/*** The list-view item at a specified position will be determined ***********/
LRESULT CListCtrlEx::OnHitTest(WPARAM, LPARAM lParam)
{
return CListBase::OnHitTest(lParam);
}
/*** List control has been scrolled horizontally *****************************/
void CListCtrlEx::OnHScroll(UINT, UINT, CScrollBar*)
{
CListBase::OnHScroll();
}
/*** A new column in a list-view control will be inserted ********************/
LRESULT CListCtrlEx::OnInsertColumn(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnInsertColumn(wParam, lParam);
}
/*** A new item in a list-view control will be inserted **********************/
LRESULT CListCtrlEx::OnInsertItem(WPARAM, LPARAM lParam)
{
return CListBase::OnInsertItem(lParam);
}
/*** A key has been pressed *************************************************/
void CListCtrlEx::OnKeyDown(UINT nChar, UINT, UINT)
{
CListBase::OnKeyDown(nChar);
}
/*** A key has been released *************************************************/
void CListCtrlEx::OnKeyUp(UINT nChar, UINT, UINT)
{
CListBase::OnKeyUp(nChar);
}
/*** List control loses input focus ******************************************/
void CListCtrlEx::OnKillFocus(CWnd*)
{
CListBase::OnKillFocus();
}
/*** The user double-clicks the left mouse button ****************************/
void CListCtrlEx::OnLButtonDblClk(UINT, CPoint point)
{
CListBase::OnLButtonDblClk(point);
}
/*** The user presses the left mouse button **********************************/
void CListCtrlEx::OnLButtonDown(UINT, CPoint point)
{
CListBase::OnLButtonDown(point);
}
/*** The user releases the left mouse button *********************************/
void CListCtrlEx::OnLButtonUp(UINT, CPoint)
{
CListBase::OnLButtonUp();
}
/*** The mouse cursor leaves the client area of this list view control *******/
LRESULT CListCtrlEx::OnMouseLeave(WPARAM, LPARAM)
{
return CListBase::OnMouseLeave();
}
/*** The mouse has been moved ************************************************/
void CListCtrlEx::OnMouseMove(UINT, CPoint point)
{
CListBase::OnMouseMove(point);
}
/*** The mouse wheel has been rotated ****************************************/
BOOL CListCtrlEx::OnMouseWheel(UINT, short, CPoint pt)
{
return CListBase::OnMouseWheel(pt);
}
/*** Divider in header control has been dragged ******************************/
BOOL CListCtrlEx::OnNotify(WPARAM, LPARAM lParam, LRESULT*)
{
return CListBase::OnNotify(lParam);
}
/*** The contents of the display area of a virtual list view control have ****/
/*** been changed ****/
BOOL CListCtrlEx::OnODCacheHint(NMHDR* pNMHDR, LRESULT*)
{
return CListBase::OnODCacheHint(pNMHDR);
}
/*** Will be called when Windows or an application makes a request to ********/
/*** repaint a portion of the client area of this list view control ********/
void CListCtrlEx::OnPaint()
{
CListBase::OnPaint();
}
/*** The background color of the list view control will be set ***************/
LRESULT CListCtrlEx::OnSetBkColor(WPARAM, LPARAM lParam)
{
return CListBase::OnSetBkColor(lParam);
}
/*** An background image will be set *****************************************/
LRESULT CListCtrlEx::OnSetBkImage(WPARAM, LPARAM)
{
return CListBase::OnSetBkImage();
}
/*** The attributes of a list view column will be set ************************/
LRESULT CListCtrlEx::OnSetColumn(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSetColumn(wParam, lParam);
}
/*** The left-to-right order of columns in a list view control will be set ***/
LRESULT CListCtrlEx::OnSetColumnOrderArray(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSetColumnOrderArray(wParam, lParam);
}
/*** The width of a column in a list view control will be changed ************/
LRESULT CListCtrlEx::OnSetColumnWidth(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSetColumnWidth(wParam, lParam);
}
/*** The extended list view style will be set ********************************/
LRESULT CListCtrlEx::OnSetExtendedStyle(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSetExtendedStyle(wParam, lParam);
}
/*** List control gains input focus ******************************************/
void CListCtrlEx::OnSetFocus(CWnd*)
{
CListBase::OnSetFocus();
}
/*** An image list will be assigned ******************************************/
LRESULT CListCtrlEx::OnSetImageList(WPARAM wParam, LPARAM)
{
return CListBase::OnSetImageList(wParam);
}
/*** Some or all of a list view item's attributes will be set ****************/
LRESULT CListCtrlEx::OnSetItem(WPARAM, LPARAM lParam)
{
return CListBase::OnSetItem(lParam);
}
/*** The text of an item or a subitem in a list view control will be changed */
LRESULT CListCtrlEx::OnSetItemText(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSetItemText(wParam, lParam);
}
/*** The items of a list view control will be sorted *************************/
LRESULT CListCtrlEx::OnSortItems(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSortItems(wParam, lParam);
}
/*** System colors have been changed *****************************************/
void CListCtrlEx::OnSysColorChange()
{
CListBase::OnSysColorChange();
}
/*** Called by the framework to allow other necessary subclassing to occur ***/
/*** before the window is subclassed ***/
void CListCtrlEx::PreSubclassWindow()
{
InitializeSubCtrls ();
CListCtrl::PreSubclassWindow();
}
/*** Table of message handlers ***********************************************/
BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
ON_MESSAGE (LVM_CANCELEDITLABEL, OnCancelEditLabel)
ON_MESSAGE (LVM_DELETEALLITEMS, OnDeleteAllItems)
ON_MESSAGE (LVM_DELETECOLUMN, OnDeleteColumn)
ON_MESSAGE (LVM_DELETEITEM, OnDeleteItem)
ON_MESSAGE (LVM_FINDITEM, OnFindItem)
ON_MESSAGE (LVM_GETCOLUMN, OnGetColumn)
ON_MESSAGE (LVM_GETCOLUMNORDERARRAY, OnGetColumnOrderArray)
ON_MESSAGE (LVM_GETCOLUMNWIDTH, OnGetColumnWidth)
ON_MESSAGE (LVM_GETEXTENDEDLISTVIEWSTYLE , OnGetExtendedStyle)
ON_MESSAGE (LVM_GETITEM, OnGetItem)
ON_MESSAGE (LVM_GETITEMRECT, OnGetItemRect)
ON_MESSAGE (LVM_GETITEMTEXT, OnGetItemText)
ON_MESSAGE (LVM_GETSUBITEMRECT, OnGetSubItemRect)
ON_MESSAGE (LVM_HITTEST, OnHitTest)
ON_MESSAGE (LVM_INSERTCOLUMN, OnInsertColumn)
ON_MESSAGE (LVM_INSERTITEM, OnInsertItem)
ON_MESSAGE (LVM_SETBKCOLOR, OnSetBkColor)
ON_MESSAGE (LVM_SETBKIMAGE, OnSetBkImage)
ON_MESSAGE (LVM_SETCOLUMN, OnSetColumn)
ON_MESSAGE (LVM_SETCOLUMNORDERARRAY, OnSetColumnOrderArray)
ON_MESSAGE (LVM_SETCOLUMNWIDTH, OnSetColumnWidth)
ON_MESSAGE (LVM_SETEXTENDEDLISTVIEWSTYLE , OnSetExtendedStyle)
ON_MESSAGE (LVM_SETIMAGELIST, OnSetImageList)
ON_MESSAGE (LVM_SETITEM, OnSetItem)
ON_MESSAGE (LVM_SETITEMTEXT, OnSetItemText)
ON_MESSAGE (LVM_SORTITEMS, OnSortItems)
ON_MESSAGE (LVM_SUBITEMHITTEST, OnHitTest)
ON_MESSAGE (WM_MOUSELEAVE, OnMouseLeave)
ON_NOTIFY_REFLECT (NM_CUSTOMDRAW, OnCustomDraw)
ON_NOTIFY_REFLECT_EX(LVN_BEGINLABELEDIT, OnBeginLabelEdit)
ON_NOTIFY_REFLECT_EX(LVN_COLUMNCLICK, OnColumnclick)
ON_NOTIFY_REFLECT_EX(LVN_ENDLABELEDIT, OnEndLabelEdit)
ON_NOTIFY_REFLECT_EX(LVN_GETDISPINFO, OnGetdispinfo)
ON_NOTIFY_REFLECT_EX(LVN_ODCACHEHINT, OnODCacheHint)
ON_WM_CONTEXTMENU ()
ON_WM_DESTROY ()
ON_WM_ERASEBKGND ()
ON_WM_HSCROLL ()
ON_WM_KEYDOWN ()
ON_WM_KEYUP ()
ON_WM_KILLFOCUS ()
ON_WM_LBUTTONDBLCLK ()
ON_WM_LBUTTONDOWN ()
ON_WM_LBUTTONUP ()
ON_WM_MOUSEMOVE ()
ON_WM_MOUSEWHEEL ()
ON_WM_PAINT ()
ON_WM_SETFOCUS ()
ON_WM_SYSCOLORCHANGE()
END_MESSAGE_MAP()
/*** Definition of class "CListViewEx" ***************************************/
IMPLEMENT_DYNCREATE(CListViewEx, CListView)
/*** Protected member functions **********************************************/
/*** Label editing will be started *******************************************/
BOOL CListViewEx::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT*)
{
return CListBase::OnBeginLabelEdit(pNMHDR);
}
/*** Label editing will be cancelled *****************************************/
LRESULT CListViewEx::OnCancelEditLabel(WPARAM, LPARAM)
{
return CListBase::OnCancelEditLabel();
}
/*** A column header has been clicked ****************************************/
BOOL CListViewEx::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult)
{
return CListBase::OnColumnclick(pNMHDR, pResult);
}
/*** An item from a menu has been selected ***********************************/
BOOL CListViewEx::OnCommand(WPARAM wParam, LPARAM)
{
return CListBase::OnCommand(wParam);
}
/*** The user has right clicked the mouse ************************************/
void CListViewEx::OnContextMenu(CWnd* pWnd, CPoint point)
{
CListBase::OnContextMenu(pWnd, point);
}
/*** A list view (sub)item will be drawn *************************************/
void CListViewEx::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
CListBase::OnCustomDraw(pNMHDR, pResult);
}
/*** All items from a list view control will be removed **********************/
LRESULT CListViewEx::OnDeleteAllItems(WPARAM, LPARAM)
{
return CListBase::OnDeleteAllItems();
}
/*** A column in a list view control will be deleted *************************/
LRESULT CListViewEx::OnDeleteColumn(WPARAM wParam, LPARAM)
{
return CListBase::OnDeleteColumn(wParam);
}
/*** An item from a list view control will be removed ************************/
LRESULT CListViewEx::OnDeleteItem(WPARAM wParam, LPARAM)
{
return CListBase::OnDeleteItem(wParam);
}
/*** The list view control will be destroyed *********************************/
void CListViewEx::OnDestroy()
{
CListBase::OnDestroy();
}
/*** Label editing will be finished ******************************************/
BOOL CListViewEx::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT*)
{
return CListBase::OnEndLabelEdit(pNMHDR);
}
/*** The background will be erased *******************************************/
BOOL CListViewEx::OnEraseBkgnd(CDC* pDC)
{
return CListBase::OnEraseBkgnd(pDC);
}
/*** A specific item of a list view control will be searched *****************/
LRESULT CListViewEx::OnFindItem(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnFindItem(wParam, lParam);
}
/*** The attributes of a list view control's column will be retrieved ********/
LRESULT CListViewEx::OnGetColumn(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnGetColumn(wParam, lParam);
}
/*** The current left-to-right order of columns in a list view control *******/
/*** will be retrieved *******/
LRESULT CListViewEx::OnGetColumnOrderArray(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnGetColumnOrderArray(wParam, lParam);
}
/*** The width of a column in a list view control will be retrieved **********/
LRESULT CListViewEx::OnGetColumnWidth(WPARAM wParam, LPARAM)
{
return CListBase::OnGetColumnWidth(wParam);
}
/*** Information needed to display a list view item **************************/
BOOL CListViewEx::OnGetdispinfo(NMHDR* pNMHDR, LRESULT*)
{
return CListBase::OnGetdispinfo(pNMHDR);
}
/*** The extended list view style will be retrieved **************************/
LRESULT CListViewEx::OnGetExtendedStyle(WPARAM, LPARAM)
{
return m_dwExtendedStyle;
}
/*** Some or all of a list view item's attributes will be retrieved **********/
LRESULT CListViewEx::OnGetItem(WPARAM, LPARAM lParam)
{
return CListBase::OnGetItem(lParam);
}
/*** The rectangle bounding an item will be retrieved ************************/
LRESULT CListViewEx::OnGetItemRect(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnGetItemRect(wParam, lParam);
}
/*** The text of a list view item or subitem will be retrieved ***************/
LRESULT CListViewEx::OnGetItemText(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnGetItemText(wParam, lParam);
}
/*** The rectangle bounding a subitem will be retrieved **********************/
LRESULT CListViewEx::OnGetSubItemRect(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnGetSubItemRect(wParam, lParam);
}
/*** The list-view item at a specified position will be determined ***********/
LRESULT CListViewEx::OnHitTest(WPARAM, LPARAM lParam)
{
return CListBase::OnHitTest(lParam);
}
/*** List control has been scrolled horizontally *****************************/
void CListViewEx::OnHScroll(UINT, UINT, CScrollBar*)
{
CListBase::OnHScroll();
}
/*** Called by the framework after the view is first attached to the *********/
/*** document, but before the view is initially displayed *********/
void CListViewEx::OnInitialUpdate()
{
CListView::OnInitialUpdate();
InitializeSubCtrls ();
}
/*** A new column in a list-view control will be inserted ********************/
LRESULT CListViewEx::OnInsertColumn(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnInsertColumn(wParam, lParam);
}
/*** A new item in a list-view control will be inserted **********************/
LRESULT CListViewEx::OnInsertItem(WPARAM, LPARAM lParam)
{
return CListBase::OnInsertItem(lParam);
}
/*** A key has been pressed *************************************************/
void CListViewEx::OnKeyDown(UINT nChar, UINT, UINT)
{
CListBase::OnKeyDown(nChar);
}
/*** A key has been released *************************************************/
void CListViewEx::OnKeyUp(UINT nChar, UINT, UINT)
{
CListBase::OnKeyUp(nChar);
}
/*** List control loses input focus ******************************************/
void CListViewEx::OnKillFocus(CWnd*)
{
CListBase::OnKillFocus();
}
/*** The user double-clicks the left mouse button ****************************/
void CListViewEx::OnLButtonDblClk(UINT, CPoint point)
{
CListBase::OnLButtonDblClk(point);
}
/*** The user presses the left mouse button **********************************/
void CListViewEx::OnLButtonDown(UINT, CPoint point)
{
CListBase::OnLButtonDown(point);
}
/*** The user releases the left mouse button *********************************/
void CListViewEx::OnLButtonUp(UINT, CPoint)
{
CListBase::OnLButtonUp();
}
/*** The mouse cursor leaves the client area of this list view control *******/
LRESULT CListViewEx::OnMouseLeave(WPARAM, LPARAM)
{
return CListBase::OnMouseLeave();
}
/*** The mouse has been moved ************************************************/
void CListViewEx::OnMouseMove(UINT, CPoint point)
{
CListBase::OnMouseMove(point);
}
/*** The mouse wheel has been rotated ****************************************/
BOOL CListViewEx::OnMouseWheel(UINT, short, CPoint pt)
{
return CListBase::OnMouseWheel(pt);
}
/*** Divider in header control has been dragged ******************************/
BOOL CListViewEx::OnNotify(WPARAM, LPARAM lParam, LRESULT*)
{
return CListBase::OnNotify(lParam);
}
/*** The contents of the display area of a virtual list view control have ****/
/*** been changed ****/
BOOL CListViewEx::OnODCacheHint(NMHDR* pNMHDR, LRESULT*)
{
return CListBase::OnODCacheHint(pNMHDR);
}
/*** Will be called when Windows or an application makes a request to ********/
/*** repaint a portion of the client area of this list view control ********/
void CListViewEx::OnPaint()
{
CListBase::OnPaint();
}
/*** The background color of the list view control will be set ***************/
LRESULT CListViewEx::OnSetBkColor(WPARAM, LPARAM lParam)
{
return CListBase::OnSetBkColor(lParam);
}
/*** The background color of the list view control will be set ***************/
LRESULT CListViewEx::OnSetBkImage(WPARAM, LPARAM)
{
return CListBase::OnSetBkImage();
}
/*** The attributes of a list view column will be set ************************/
LRESULT CListViewEx::OnSetColumn(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSetColumn(wParam, lParam);
}
/*** The left-to-right order of columns in a list view control will be set ***/
LRESULT CListViewEx::OnSetColumnOrderArray(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSetColumnOrderArray(wParam, lParam);
}
/*** The width of a column in a list view control will be changed ************/
LRESULT CListViewEx::OnSetColumnWidth(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSetColumnWidth(wParam, lParam);
}
/*** The extended list view style will be set ********************************/
LRESULT CListViewEx::OnSetExtendedStyle(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSetExtendedStyle(wParam, lParam);
}
/*** List control gains input focus ******************************************/
void CListViewEx::OnSetFocus(CWnd*)
{
CListBase::OnSetFocus();
}
/*** An image list will be assigned ******************************************/
LRESULT CListViewEx::OnSetImageList(WPARAM wParam, LPARAM)
{
return CListBase::OnSetImageList(wParam);
}
/*** Some or all of a list view item's attributes will be set ****************/
LRESULT CListViewEx::OnSetItem(WPARAM, LPARAM lParam)
{
return CListBase::OnSetItem(lParam);
}
/*** The state of an item in a list view control will be changed *************/
LRESULT CListViewEx::OnSetItemText(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSetItemText(wParam, lParam);
}
/*** The items of a list view control will be sorted *************************/
LRESULT CListViewEx::OnSortItems(WPARAM wParam, LPARAM lParam)
{
return CListBase::OnSortItems(wParam, lParam);
}
/*** System colors have been changed *****************************************/
void CListViewEx::OnSysColorChange()
{
CListBase::OnSysColorChange();
}
/*** Table of message handlers ***********************************************/
BEGIN_MESSAGE_MAP(CListViewEx, CListView)
ON_MESSAGE (LVM_CANCELEDITLABEL, OnCancelEditLabel)
ON_MESSAGE (LVM_DELETEALLITEMS, OnDeleteAllItems)
ON_MESSAGE (LVM_DELETECOLUMN, OnDeleteColumn)
ON_MESSAGE (LVM_DELETEITEM, OnDeleteItem)
ON_MESSAGE (LVM_FINDITEM, OnFindItem)
ON_MESSAGE (LVM_GETCOLUMN, OnGetColumn)
ON_MESSAGE (LVM_GETCOLUMNORDERARRAY, OnGetColumnOrderArray)
ON_MESSAGE (LVM_GETCOLUMNWIDTH, OnGetColumnWidth)
ON_MESSAGE (LVM_GETEXTENDEDLISTVIEWSTYLE , OnGetExtendedStyle)
ON_MESSAGE (LVM_GETITEM, OnGetItem)
ON_MESSAGE (LVM_GETITEMRECT, OnGetItemRect)
ON_MESSAGE (LVM_GETITEMTEXT, OnGetItemText)
ON_MESSAGE (LVM_GETSUBITEMRECT, OnGetSubItemRect)
ON_MESSAGE (LVM_HITTEST, OnHitTest)
ON_MESSAGE (LVM_INSERTCOLUMN, OnInsertColumn)
ON_MESSAGE (LVM_INSERTITEM, OnInsertItem)
ON_MESSAGE (LVM_SETBKCOLOR, OnSetBkColor)
ON_MESSAGE (LVM_SETBKIMAGE, OnSetBkImage)
ON_MESSAGE (LVM_SETCOLUMN, OnSetColumn)
ON_MESSAGE (LVM_SETCOLUMNORDERARRAY, OnSetColumnOrderArray)
ON_MESSAGE (LVM_SETCOLUMNWIDTH, OnSetColumnWidth)
ON_MESSAGE (LVM_SETEXTENDEDLISTVIEWSTYLE , OnSetExtendedStyle)
ON_MESSAGE (LVM_SETIMAGELIST, OnSetImageList)
ON_MESSAGE (LVM_SETITEM, OnSetItem)
ON_MESSAGE (LVM_SETITEMTEXT, OnSetItemText)
ON_MESSAGE (LVM_SORTITEMS, OnSortItems)
ON_MESSAGE (LVM_SUBITEMHITTEST, OnHitTest)
ON_MESSAGE (WM_MOUSELEAVE, OnMouseLeave)
ON_NOTIFY_REFLECT (NM_CUSTOMDRAW, OnCustomDraw)
ON_NOTIFY_REFLECT_EX(LVN_BEGINLABELEDIT, OnBeginLabelEdit)
ON_NOTIFY_REFLECT_EX(LVN_COLUMNCLICK, OnColumnclick)
ON_NOTIFY_REFLECT_EX(LVN_ENDLABELEDIT, OnEndLabelEdit)
ON_NOTIFY_REFLECT_EX(LVN_GETDISPINFO, OnGetdispinfo)
ON_NOTIFY_REFLECT_EX(LVN_ODCACHEHINT, OnODCacheHint)
ON_WM_CONTEXTMENU ()
ON_WM_DESTROY ()
ON_WM_ERASEBKGND ()
ON_WM_HSCROLL ()
ON_WM_KEYDOWN ()
ON_WM_KEYUP ()
ON_WM_KILLFOCUS ()
ON_WM_LBUTTONDBLCLK ()
ON_WM_LBUTTONDOWN ()
ON_WM_LBUTTONUP ()
ON_WM_MOUSEMOVE ()
ON_WM_MOUSEWHEEL ()
ON_WM_PAINT ()
ON_WM_SETFOCUS ()
ON_WM_SYSCOLORCHANGE()
END_MESSAGE_MAP()
/*** Definition of helper class "CListCtrlHelper" ****************************/
class CListCtrlHelper: public CListCtrl
{
public:
LRESULT Default ();
LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
/*** Public member functions *************************************************/
/*** Message handlers ********************************************************/
LRESULT CListCtrlHelper::Default()
{
return __super::Default();
}
LRESULT CListCtrlHelper::DefWindowProc(UINT message, WPARAM wParam,
LPARAM lParam)
{
return __super::DefWindowProc(message, wParam, lParam);
}
/*** Table of message handlers ***********************************************/
BEGIN_MESSAGE_MAP(CListCtrlHelper, CListCtrl)
END_MESSAGE_MAP()
/*** Definition of helper class "CLabelEdit" *********************************/
class CLabelEdit: public CEdit
{
public:
CLabelEdit(CListBase* pListBase): m_pListBase(pListBase) {}
virtual BOOL PreTranslateMessage(MSG* pMsg);
protected:
// Generated message map functions
afx_msg void OnWindowPosChanging(WINDOWPOS* lpwndpos);
DECLARE_MESSAGE_MAP()
private:
CListBase* m_pListBase;
};
/*** Public member functions *************************************************/
/*** Called before a new message will be dispatched **************************/
BOOL CLabelEdit::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
m_pListBase->m_bLabelEditingCancelled = true;
return FALSE;
}
/*** Protected member functions **********************************************/
/*** Called when the size, position, or Z-order is about to change ***********/
void CLabelEdit::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
__super::OnWindowPosChanging(lpwndpos);
if (m_pListBase->m_bKeepLabelLeft && m_pListBase->IndexToOrder(0) > 0)
{
// position edit control
CRect rc;
if (m_pListBase->m_pListCtrl->GetSubItemRect(
m_pListBase->m_iItemEdit, 0, LVIR_LABEL, rc)) lpwndpos->x = rc.left;
}
}
/*** Table of message handlers ***********************************************/
BEGIN_MESSAGE_MAP(CLabelEdit, CEdit)
ON_WM_WINDOWPOSCHANGING()
END_MESSAGE_MAP()
/*** Definition of helper class "CLabelTipCtrl" ******************************/
class CLabelTipCtrl: public CToolTipCtrl
{
public:
CLabelTipCtrl(CListBase* pListBase):
m_iYOff (0),
m_bSettingsChanged (false),
m_pListBase (pListBase),
m_bExpandedMargin (false),
m_iLastX (-1),
m_iLastY (-1),
m_bMouseInClientArea(false),
m_bLabelTipPoppedUp (false),
m_iCurrentItem (-1),
m_iCurrentSubItem (-1)
{}
~CLabelTipCtrl()
{
if (!m_strClassName.IsEmpty())
UnregisterClass(m_strClassName, AfxGetInstanceHandle());
}
BOOL Create(CWnd* pParentWnd);
protected:
// Generated message map functions
afx_msg LRESULT OnMouseLeave (WPARAM, LPARAM);
afx_msg void OnMouseMove (UINT nFlags, CPoint point);
afx_msg void OnTimer (UINT nIDEvent);
afx_msg void OnWindowPosChanging(WINDOWPOS* lpwndpos);
DECLARE_MESSAGE_MAP()
private:
CString m_strClassName;
RECT m_rcMargin;
int m_iYOff;
bool m_bSettingsChanged;
CListBase* m_pListBase;
bool m_bExpandedMargin;
int m_iLastX;
int m_iLastY;
bool m_bMouseInClientArea;
bool m_bLabelTipPoppedUp;
int m_iCurrentItem;
int m_iCurrentSubItem;
};
/*** Public member functions *************************************************/
/*** Creates the label tip window ********************************************/
BOOL CLabelTipCtrl::Create(CWnd* pwndParent)
{
// register window class without shadows
WNDCLASS wndClass;
if (GetClassInfo(0, TOOLTIPS_CLASS, &wndClass))
{
wndClass.style &= ~CS_DROPSHADOW;
m_strClassName =
wndClass.lpszClassName + CString(_T("_without_shadows"));
wndClass.lpszClassName = m_strClassName;
if (!RegisterClass(&wndClass) &&
GetLastError() != ERROR_CLASS_ALREADY_EXISTS) m_strClassName.Empty();
}
if (CWnd::CreateEx(
m_pListBase->m_visualStyle == CListBase::Present ?
WS_EX_TRANSPARENT : 0,
m_strClassName.IsEmpty() ? TOOLTIPS_CLASS : m_strClassName, 0,
TTS_NOANIMATE | TTS_NOFADE | TTS_NOPREFIX, CW_USEDEFAULT, 0,
CW_USEDEFAULT, 0, pwndParent->m_hWnd, 0))
{
SetOwner (pwndParent);
SetFont (pwndParent->GetFont());
GetMargin (&m_rcMargin); // remember margins
SetDelayTime(TTDT_INITIAL, 0);
SetDelayTime(TTDT_RESHOW, 0);
return TRUE;
}
else
return FALSE;
}
/*** Protected member functions **********************************************/
/*** The mouse cursor leaves the client area of this tooltip control *********/
LRESULT CLabelTipCtrl::OnMouseLeave(WPARAM, LPARAM)
{
m_bMouseInClientArea = false;
// Reset current tooltip item if mouse cursor has been moved over the
// header control
m_pListBase->m_bLabelUnfolded = FALSE;
m_pListBase->m_fCurrentFlags = 0;
m_pListBase->m_iCurrentItem = -1;
m_pListBase->m_iCurrentSubItem = -1;
return Default();
}
/*** The mouse has been moved ************************************************/
void CLabelTipCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_bMouseInClientArea)
{
m_bMouseInClientArea = true;
// Fire an event when the mouse cursor leaves the client area of this
// tooltip control
TRACKMOUSEEVENT eventTrack = {sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd};
_TrackMouseEvent(&eventTrack);
}
__super::OnMouseMove(nFlags, point);
}
/*** Called after each timer interval ****************************************/
void CLabelTipCtrl::OnTimer(UINT nIDEvent)
{
#ifndef NDEBUG
ATLTRACE2(ListCtrlEx, 3, _T("nIDEvent = %u\n"), nIDEvent);
#endif
if (!m_bLabelTipPoppedUp && nIDEvent == 3)
{
m_bLabelTipPoppedUp = true;
Update();
}
// nIDEvent == 6: Expanded label will be faded out. Updating the expanded
// label prevents any fading effects.
if (nIDEvent == 6) Update();
// nIDEvent == 4: Expanded label will be popped off. Ignoring timer event 4
// prevents hiding of the expanded label.
if (nIDEvent != 4) __super::OnTimer(nIDEvent);
}
/*** Called when the size, position, or Z-order is about to change ***********/
void CLabelTipCtrl::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
#ifndef NDEBUG
struct
{
UINT uFlags;
LPCTSTR pszFlags;
} td[] =
{
{SWP_FRAMECHANGED, _T("SWP_FRAMECHANGED") },
{SWP_HIDEWINDOW, _T("SWP_HIDEWINDOW") },
{SWP_NOACTIVATE, _T("SWP_NOACTIVATE") },
{SWP_NOCOPYBITS, _T("SWP_NOACTIVATE") },
{SWP_NOMOVE, _T("SWP_NOMOVE") },
{SWP_NOOWNERZORDER, _T("SWP_NOOWNERZORDER") },
{SWP_NOSIZE, _T("SWP_NOSIZE") },
{SWP_NOREDRAW, _T("SWP_NOREDRAW") },
{SWP_NOSENDCHANGING, _T("SWP_NOSENDCHANGING")},
{SWP_NOZORDER, _T("SWP_NOZORDER") },
{SWP_SHOWWINDOW, _T("SWP_SHOWWINDOW") }
};
CString strFlags;
for (int i = 0; i < sizeof td / sizeof td[0]; ++i)
if (lpwndpos->flags & td[i].uFlags)
{
if (!strFlags.IsEmpty()) strFlags += _T(" | ");
strFlags += td[i].pszFlags;
}
ATLTRACE2(
ListCtrlEx, 2, _T("flags = %s (0x%08X)\n"), strFlags, lpwndpos->flags);
#endif
__super::OnWindowPosChanging(lpwndpos);
// If tooltip updates are suspended, restore last remembered position to
// prevent flickering.
if (!m_pListBase->m_bUpdateToolTips && m_iLastX != -1 && m_iLastY != -1)
{
lpwndpos->x = m_iLastX;
lpwndpos->y = m_iLastY;
}
else
if (m_pListBase->m_fCurrentFlags != 0 &&
m_pListBase->m_iCurrentItem != -1 &&
m_pListBase->m_iCurrentSubItem != -1)
{
// position unfolded label
CRect rc;
if (m_pListBase->m_pListCtrl->GetSubItemRect(
m_pListBase->m_iCurrentItem, m_pListBase->m_iCurrentSubItem,
LVIR_LABEL, rc))
{
m_pListBase->m_pListCtrl->ClientToScreen(rc);
lpwndpos->x = rc.left;
lpwndpos->y = rc.top;
if ((lpwndpos->flags & (SWP_NOMOVE | SWP_NOSIZE)) == 0)
if (lpwndpos->cy > rc.Height()+1)
m_iYOff = lpwndpos->cy - (rc.Height()+1);
else
m_iYOff = 0;
lpwndpos->y -= m_iYOff;
// remember position
m_iLastX = lpwndpos->x;
m_iLastY = lpwndpos->y;
// adjust margins
if (m_pListBase->m_iCurrentSubItem > 0 &&
!m_pListBase->m_bKeepLabelLeft ||
m_pListBase->IndexToOrder(m_pListBase->m_iCurrentSubItem) > 0 &&
m_pListBase->m_bKeepLabelLeft)
{
if (!m_bExpandedMargin)
{
rc = m_rcMargin;
rc.left += m_pListBase->m_iNextColXOff - 3;
rc.right = rc.left;
SetMargin(rc);
m_bExpandedMargin = true;
}
}
else
if (m_bExpandedMargin)
{
SetMargin(&m_rcMargin);
m_bExpandedMargin = false;
}
if (lpwndpos->flags & SWP_SHOWWINDOW &&
(m_pListBase->m_iCurrentItem != m_iCurrentItem ||
m_pListBase->m_iCurrentSubItem != m_iCurrentSubItem))
{
m_bLabelTipPoppedUp = false;
m_iCurrentItem = m_pListBase->m_iCurrentItem;
m_iCurrentSubItem = m_pListBase->m_iCurrentSubItem;
}
}
}
else
if (m_iLastX != -1 && m_iLastY != -1)
{
lpwndpos->x = m_iLastX;
lpwndpos->y = m_iLastY;
}
}
/*** Table of message handlers ***********************************************/
BEGIN_MESSAGE_MAP(CLabelTipCtrl, CToolTipCtrl)
ON_MESSAGE (WM_MOUSELEAVE, OnMouseLeave)
ON_WM_MOUSEMOVE ()
ON_WM_TIMER ()
ON_WM_WINDOWPOSCHANGING()
END_MESSAGE_MAP()
/*** Definition of "workhorse" class "CListBase" *****************************/
UINT CListBase::m_winver = 0; // Windows version
CListBase::VISUAL_STYLE CListBase::m_visualStyle = Unknown; // visual style?
const int CListBase::m_iFirstColXOff = 2; // x-off of first col
const int CListBase::m_iNextColXOff = 6; // x-off of other cols
// Width of column separator (applies only to Vista explorer style)
const int CListBase::m_iColumnSeparatorWidth = 2;
/*** Public member functions *************************************************/
/*** Constructor *************************************************************/
CListBase::CListBase()
{
// Build equivalent to deprecated global "_win_ver"
if (m_winver == 0)
{
WORD wVersion = LOWORD(GetVersion());
m_winver = MAKEWORD(HIBYTE(wVersion), LOBYTE(wVersion));
}
if (m_visualStyle == Unknown)
{
m_visualStyle = NotPresent;
HINSTANCE hinstDll = LoadLibrary(_T("COMCTL32.DLL"));
if (hinstDll)
{
DLLGETVERSIONPROC pDllGetVersion =
reinterpret_cast<DLLGETVERSIONPROC>(GetProcAddress(hinstDll,
"DllGetVersion"));
// Because some DLLs might not implement this function, you must test for
// it explicitly. Depending on the particular DLL, the lack of a
// DllGetVersion function can be a useful indicator of the version.
if (pDllGetVersion)
{
DLLVERSIONINFO dvi = {sizeof(DLLVERSIONINFO)};
if (SUCCEEDED((*pDllGetVersion)(&dvi)) && dvi.dwMajorVersion >= 6)
m_visualStyle = Present;
}
FreeLibrary(hinstDll);
}
}
m_iIconXOff = 4;
m_hTheme = 0;
m_pListCtrl = 0;
m_bIsActive = false;
m_bMouseInClientArea = false;
m_bTopMost = false;
m_bFocusSet = false;
m_iColumnHidingAllowed = 0;
m_bColumnSeparators = FALSE;
m_bExplorerStyle = false;
m_bSortIconEnabled = FALSE;
m_bColorSortColumn = FALSE;
m_bBkColorKnown = false;
m_bBkImage = false;
m_iSortColumn = 0;
m_bKeepLabelLeft = FALSE;
m_bLocked = false;
m_bControl = false;
m_bIconXOffCalculated = false;
m_bFixedBkColor = false;
m_dwExtendedStyle = 0;
m_iItemUnderCursor = -1;
m_iHotItem = -1;
m_dwHotLite =
m_winver <= 0x0400 ? RGB(0, 0, 128) : GetSysColor(COLOR_HOTLIGHT);
m_hCursorCustom = 0;
m_hCursorArrow = 0;
m_hCursorHand = 0;
m_iItemLastSelected = -1;
m_iFirstCachedItem = INT_MAX;
m_iLastCachedItem = 0;
m_bAlwaysGetSmallIconRect = false;
m_bRefreshToolTips = false;
m_bSubItemTips = FALSE;
m_bUpdateToolTips = true;
m_pToolTip = 0;
m_pLabelTip = 0;
m_bToolTips = false;
m_bUnfoldLabel = false;
m_bLabelUnfolded = FALSE;
m_iItemEdit = -1;
m_bOnEndLabelEdit = false;
m_bOnGetDispinfo = false;
m_bOnPaint = false;
m_bRepost = false;
m_fCurrentFlags = 0;
m_iCurrentItem = -1;
m_iCurrentSubItem = -1;
}
/*** Destructor **************************************************************/
CListBase::~CListBase()
{
delete m_pLabelEdit;
delete m_pToolTip;
delete m_pLabelTip;
// remove remembered column data
for (INT_PTR i = m_aColumnData.GetUpperBound(); i >= 0; --i)
delete m_aColumnData[i];
if (m_winver <= 0x400 && m_hCursorHand) DestroyCursor(m_hCursorHand);
if (m_hTheme) CloseThemeData(m_hTheme);
}
/*** Enable or disable coloring of sort column *******************************/
#pragma runtime_checks("c", off) // due to a flaw in the definition of
// Get[R|G|B]Value
BOOL CListBase::ColorSortColumn(BOOL bEnable, int nSortColumn)
{
if (bEnable == m_bColorSortColumn &&
(nSortColumn == 0 || nSortColumn == m_iSortColumn)) return bEnable;
if (bEnable)
{
// 256 colors --> don't color sort column
CDC* pDC = m_pListCtrl->GetDC();
bool bColor = pDC->GetDeviceCaps(BITSPIXEL) > 8;
m_pListCtrl->ReleaseDC(pDC);
if (!bColor) return m_bColorSortColumn = FALSE;
}
if (bEnable != m_bColorSortColumn)
{
if (bEnable)
{
DWORD dwColNormalColor = m_pListCtrl->GetBkColor();
// emulate sort column coloring of Windows XP explorer
UINT nRed = GetRValue(dwColNormalColor);
UINT nGreen = GetGValue(dwColNormalColor);
UINT nBlue = GetBValue(dwColNormalColor);
if (nRed > 240 && nGreen > 240 && nBlue > 240)
{
nRed -= 8;
nGreen -= 8;
nBlue -= 8;
}
else
{
if (nRed < 232) nRed += nRed / 10; else nRed = 255;
if (nGreen < 232) nGreen += nGreen / 10; else nGreen = 255;
if (nBlue < 232) nBlue += nBlue / 10; else nBlue = 255;
}
m_dwColSortColor = RGB(nRed, nGreen, nBlue);
}
m_bColorSortColumn = bEnable;
bEnable = !bEnable;
}
if (nSortColumn != 0) m_iSortColumn = nSortColumn;
if (m_visualStyle == Present)
if (m_bColorSortColumn)
m_pListCtrl->SendMessage(
LVM_SETSELECTEDCOLUMN, GetPhysicalIndex(abs(m_iSortColumn)-1));
else
m_pListCtrl->SendMessage(LVM_SETSELECTEDCOLUMN, 0xFFFFFFFF);
m_pListCtrl->Invalidate();
return bEnable;
}
#pragma runtime_checks("c", restore)
/*** Draw small icon *********************************************************/
void CListBase::DrawSmallIcon(CDC* pDC, LVITEM* pItem, LPRECT pRect)
{
if (pItem->iImage > 0)
{
CImageList* pimglst = m_pListCtrl->GetImageList(LVSIL_SMALL);
if (pimglst)
{
IMAGEINFO imgInfo;
if (pimglst->GetImageInfo(pItem->iImage, &imgInfo))
{
pimglst->DrawIndirect(
pDC, pItem->iImage, CPoint(pRect->left, pRect->top),
CSize(
__min(
pRect->right - pRect->left + 1,
imgInfo.rcImage.right - imgInfo.rcImage.left),
__min(
pRect->bottom - pRect->top + 1,
imgInfo.rcImage.bottom - imgInfo.rcImage.top)), CPoint(0, 0),
(!m_hTheme &&
pItem->state & LVIS_SELECTED &&
m_pListCtrl->GetFocus() == m_pListCtrl ?
ILD_SELECTED : ILD_NORMAL) | pItem->state & LVIS_OVERLAYMASK,
SRCCOPY, CLR_NONE);
}
}
}
}
/*** Draw state icon *********************************************************/
void CListBase::DrawStateIcon(CDC* pDC, LVITEM* pItem, LPRECT pRect)
{
int iImage = (pItem->state & LVIS_STATEIMAGEMASK) >> 12;
if (iImage > 0)
{
CImageList* pimglst = m_pListCtrl->GetImageList(LVSIL_STATE);
if (pimglst)
{
IMAGEINFO imgInfo;
// image indices are zero-based
if (pimglst->GetImageInfo(--iImage, &imgInfo))
{
long lRectWidth = pRect->right - pRect->left + 1L;
long lRectHeight = pRect->bottom - pRect->top + 1L;
long lImageWidth = imgInfo.rcImage.right - imgInfo.rcImage.left;
long lImageHeight = imgInfo.rcImage.bottom - imgInfo.rcImage.top;
long lDiffHorz = lRectWidth - lImageWidth;
long lDiffVert = lRectHeight - lImageHeight;
// size of state icon to be drawn
SIZE sz =
{
lDiffHorz >= 0 ? lImageWidth : lRectWidth,
lDiffVert >= 0 ? lImageHeight : lRectHeight
};
// coordinates of state icon's upper left corner
POINT pt = {pRect->left, pRect->top};
if (lDiffVert > 2) pt.y += lDiffVert / 2;
pimglst->DrawIndirect(
pDC, iImage, pt, sz, CPoint(0, 0),
!m_hTheme &&
pItem->state & LVIS_SELECTED &&
m_pListCtrl->GetFocus() == m_pListCtrl ? ILD_SELECTED : ILD_NORMAL,
SRCCOPY, CLR_NONE);
}
}
}
}
/*** Draw the label of an item or subitem ************************************/
void CListBase::DrawSubItemText(
CDC* pDC, LVITEM* pItem, LVCOLUMN* pColumn, LPRECT pRect)
{
if (*pItem->pszText)
if (pRect->right - pRect->left > 0)
pDC->DrawText(
pItem->pszText, -1, pRect,
(pColumn->fmt & LVCFMT_CENTER ? DT_CENTER :
pColumn->fmt & LVCFMT_RIGHT ? DT_RIGHT : DT_LEFT) |
DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
}
/*** Enable/disable a column entirely ****************************************/
void CListBase::EnableColumn(int nColumn, BOOL bEnableIt)
{
ASSERT(nColumn >= 0 && nColumn < m_aColumnData.GetSize());
if (bEnableIt)
{
if (!m_aColumnData[nColumn]->m_bEnabled)
{
// Enable column
m_aColumnData[nColumn]->m_bEnabled = true;
if (m_aColumnData[nColumn]->m_bVisible)
{
// Column was visible before disabling --> show it again
m_aColumnData[nColumn]->m_bVisible = false;
RedisplayColumn(nColumn);
}
}
}
else
if (m_aColumnData[nColumn]->m_bEnabled)
{
// Disable column
if (m_aColumnData[nColumn]->m_bVisible) HideColumn(nColumn);
m_aColumnData[nColumn]->m_bEnabled = false;
}
}
/*** Allow or disallow the hiding of a column ********************************/
void CListBase::EnableColumnHiding(int nColumn, BOOL bEnableIt)
{
if (nColumn >= 0 && nColumn < m_aColumnData.GetSize())
if (bEnableIt)
{
if (!m_aColumnData[nColumn]->m_bHidingAllowed)
// The first column is allowed to be hidden only
// if the list view control is owner-drawn.
if (nColumn > 0 || m_pListCtrl->GetStyle() & LVS_OWNERDRAWFIXED)
{
m_aColumnData[nColumn]->m_bHidingAllowed = true;
++m_iColumnHidingAllowed;
}
}
else
if (m_aColumnData[nColumn]->m_bHidingAllowed)
{
ShowColumn(nColumn); // display column if hidden
m_aColumnData[nColumn]->m_bHidingAllowed = false;
--m_iColumnHidingAllowed;
}
ASSERT(m_iColumnHidingAllowed >= 0);
}
/*** Enable style of Windows explorer ****************************************/
void CListBase::EnableExplorerStyle()
{
if (m_winver >= 0x0600 && m_visualStyle == Present && !m_bExplorerStyle)
if (SetWindowTheme(m_pListCtrl->m_hWnd, L"Explorer", 0) == S_OK)
{
m_pListCtrl->SetBkColor(RGB(255, 255, 255));
if (m_pListCtrl->GetStyle() & LVS_OWNERDRAWFIXED)
m_hTheme = OpenThemeData(m_pListCtrl->m_hWnd, L"ListView");
m_bExplorerStyle = true;
}
}
/*** Enable or disable sort icon *********************************************/
void CListBase::EnableSortIcon(BOOL bEnable, int nSortColumn)
{
ASSERT(m_pListCtrl->GetHeaderCtrl());
m_bSortIconEnabled = bEnable;
if (nSortColumn != 0) m_iSortColumn = nSortColumn;
if (bEnable && m_visualStyle == NotPresent)
{
CreateSortIcons ();
m_pListCtrl->GetHeaderCtrl()->SetImageList(&m_imglstSortIcons);
}
SetSortIcon(); // display or hide sort icon
}
/*** Gets the position of the first checked item in the list view control ****/
POSITION CListBase::GetFirstCheckedItemPosition() const
{
int nItemCount = m_pListCtrl->GetItemCount();
for (int nItem = 0; nItem < nItemCount; ++nItem)
if (m_pListCtrl->GetCheck(nItem))
return reinterpret_cast<POSITION>(static_cast<INT_PTR>(nItem + 1));
return 0;
}
/*** Gets the index of the next checked item in the list view control ********/
int CListBase::GetNextCheckedItem(POSITION& pos) const
{
ASSERT(pos);
int nOldPos = static_cast<int>(reinterpret_cast<INT_PTR>(pos));
int nItemCount = m_pListCtrl->GetItemCount();
pos = 0;
for (int nItem = nOldPos; nItem < nItemCount; ++nItem)
if (m_pListCtrl->GetCheck(nItem))
{
pos = reinterpret_cast<POSITION>(static_cast<INT_PTR>(nItem + 1));
break;
}
return nOldPos - 1;
}
/*** Get attributes of this list view control ********************************/
BOOL CListBase::GetState(LPBYTE* ppState, LPUINT pnStateLen) const
{
int nColumnCount = static_cast<int>(m_aColumnData.GetSize());
if (nColumnCount > 0 && ppState)
{
UINT nStateLen =
2 * sizeof(int) + nColumnCount * (sizeof(bool) + 2 * sizeof(int));
*ppState = new BYTE[nStateLen];
LPBYTE p = *ppState;
*reinterpret_cast<LPINT>(p) = nColumnCount;
p += sizeof(int);
*reinterpret_cast<LPINT>(p) = m_iSortColumn;
for (int nColumn = 0; nColumn < nColumnCount; ++nColumn)
{
p += sizeof(int);
*reinterpret_cast<bool*>(p) = m_aColumnData[nColumn]->m_bHidingAllowed ?
m_aColumnData[nColumn]->m_bVisible : true;
p += sizeof(bool);
*reinterpret_cast<LPINT>(p) = m_aColumnData[nColumn]->m_bEnabled &&
m_aColumnData[nColumn]->m_bVisible ?
m_pListCtrl->GetColumnWidth(nColumn) :
m_aColumnData[nColumn]->m_nWidth,
p += sizeof(int);
*reinterpret_cast<LPINT>(p) = m_aColumnData[nColumn]->m_nOrder;
}
if (pnStateLen) *pnStateLen = nStateLen;
return TRUE;
}
else
return FALSE;
}
/*** Is the width of a given subitem sufficient to display "pszText"? ********/
BOOL CListBase::IsSubItemWidthSufficient(
int nItem, int nSubItem, LPCTSTR pszText)
{
CRect rc;
if (m_pListCtrl->GetSubItemRect(nItem, nSubItem, LVIR_LABEL, rc))
return
m_aColumnData[nSubItem]->m_bEnabled &&
m_aColumnData[nSubItem]->m_bVisible ?
m_pListCtrl->GetStringWidth(pszText) +
(
nSubItem == 0 && !m_bKeepLabelLeft ||
IndexToOrder(nSubItem) == 0 && m_bKeepLabelLeft ?
m_iFirstColXOff : m_iNextColXOff
) +
(nSubItem == 0 && (!m_bKeepLabelLeft || IndexToOrder(0) == 0) ?
m_iFirstColXOff : m_iNextColXOff) <= rc.Width() : FALSE;
else
return FALSE;
}
/*** Small icon always should be kept left ***********************************/
BOOL CListBase::KeepLabelLeft(BOOL bKeepLeft)
{
BOOL bSuccess = TRUE;
if (bKeepLeft)
if ((m_pListCtrl->GetStyle() & LVS_OWNERDRAWFIXED) == 0)
{
bKeepLeft = FALSE;
bSuccess = FALSE;
}
if (bSuccess && bKeepLeft != m_bKeepLabelLeft)
{
m_bKeepLabelLeft = bKeepLeft;
m_pListCtrl->Invalidate();
}
return bSuccess;
}
/*** Restore attributes of this list view control ****************************/
BOOL CListBase::RestoreState(LPCTSTR pszSection, LPCTSTR pszEntry)
{
BOOL bSuccess = FALSE;
if (static_cast<int>(m_aColumnData.GetSize()) > 0)
{
LPBYTE pState;
UINT nStateLen;
if (AfxGetApp()->GetProfileBinary(
pszSection, pszEntry, &pState, &nStateLen))
{
bSuccess = SetState(pState, nStateLen);
delete[] pState;
}
}
return bSuccess;
}
/*** Save attributes of this list view control *******************************/
BOOL CListBase::SaveState(LPCTSTR pszSection, LPCTSTR pszEntry) const
{
LPBYTE pState;
UINT nStateLen;
if (GetState(&pState, &nStateLen)) // get current state
{
BOOL bSuccess =
AfxGetApp()->WriteProfileBinary(pszSection, pszEntry, pState, nStateLen);
delete[] pState;
return bSuccess;
}
else
return FALSE;
}
/*** Set column to be sorted *************************************************/
void CListBase::SetSortColumn(int nColumn)
{
ASSERT(abs(nColumn) <= m_aColumnData.GetSize());
if (nColumn != m_iSortColumn)
{
bool bColumnChanged = abs(nColumn) != abs(m_iSortColumn);
m_iSortColumn = nColumn;
SetSortIcon();
if (bColumnChanged)
if (m_visualStyle == Present)
if (m_iSortColumn != 0)
m_pListCtrl->SendMessage(
LVM_SETSELECTEDCOLUMN, GetPhysicalIndex(abs(m_iSortColumn)-1));
else
{
m_pListCtrl->SendMessage(LVM_SETSELECTEDCOLUMN, 0xFFFFFFFF);
m_pListCtrl->Invalidate ();
}
else
m_pListCtrl->Invalidate();
}
}
/*** Set attributes of this list view control ********************************/
BOOL CListBase::SetState(LPBYTE pState, UINT nStateLen)
{
int nColumnCount = static_cast<int>(m_aColumnData.GetSize());
if (nColumnCount > 0 &&
nStateLen ==
2*sizeof(int) + nColumnCount * (sizeof(bool) + 2*sizeof(int)) &&
*reinterpret_cast<LPINT>(pState) == nColumnCount)
{
pState += sizeof(int);
int nColumn = *reinterpret_cast<LPINT>(pState);
if (abs(nColumn) <= nColumnCount)
SetSortColumn(nColumn);
else
return FALSE; // wrong sort column
for (nColumn = 0; nColumn < nColumnCount; ++nColumn)
{
COLUMN_DATA* pColData = m_aColumnData[nColumn];
// restore display status of column
pState += sizeof(int);
bool bVisible = *reinterpret_cast<bool*>(pState);
if (bVisible != pColData->m_bVisible)
if (pColData->m_bHidingAllowed || !pColData->m_bVisible)
ShowColumn(nColumn, bVisible);
pState += sizeof(bool);
// restore width of column
m_pListCtrl->SetColumnWidth(nColumn, *reinterpret_cast<LPINT>(pState));
pState += sizeof(int);
// restore column order
int nOrder = *reinterpret_cast<LPINT>(pState);
if (nOrder < nColumnCount)
pColData->m_nOrder = nOrder;
else
return FALSE;
}
// set column order
LPINT pnColOrder = new int[nColumnCount];
for (nColumn = 0; nColumn < nColumnCount; ++nColumn)
pnColOrder[m_aColumnData[nColumn]->m_nOrder] = nColumn;
m_pListCtrl->SetColumnOrderArray(nColumnCount, pnColOrder);
delete[] pnColOrder;
}
return TRUE;
}
/*** Show or hide an individual column ***************************************/
void CListBase::ShowColumn(int nColumn, BOOL bShowIt)
{
ASSERT(nColumn >= 0 && nColumn < m_aColumnData.GetSize());
if (m_aColumnData[nColumn]->m_bEnabled)
if (m_aColumnData[nColumn]->m_bHidingAllowed)
if (bShowIt)
{
if (!m_aColumnData[nColumn]->m_bVisible) RedisplayColumn(nColumn);
}
else
if (m_aColumnData[nColumn]->m_bVisible)
{
HideColumn(nColumn);
m_aColumnData[nColumn]->m_bVisible = false;
}
}
/*** Private member functions ************************************************/
/*** Compare function for sorting of list view control ***********************/
int CALLBACK CListBase::CompareFunc(
LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CListBase* pThis = reinterpret_cast<CListBase*>(lParamSort);
return
pThis->m_pfnLVCompare(
reinterpret_cast<ITEM_DATA*>(lParam1)->m_lParam,
reinterpret_cast<ITEM_DATA*>(lParam2)->m_lParam, pThis->m_lParamSort);
}
/*** Create image list with sort icons ***************************************/
void CListBase::CreateSortIcons()
{
if (!m_imglstSortIcons.m_hImageList)
{
COLORMAP cm = {RGB(0, 0, 0), GetSysColor(COLOR_GRAYTEXT)};
m_imglstSortIcons.Create (9, 5, ILC_COLOR24 | ILC_MASK, 2, 0);
m_bmpUpArrow.LoadMappedBitmap(IDB_HDRUP, 0, &cm, 1);
m_iUpArrow = m_imglstSortIcons.Add(&m_bmpUpArrow, RGB(255, 255, 255));
m_bmpDownArrow.LoadMappedBitmap(IDB_HDRDOWN, 0, &cm, 1);
m_iDownArrow = m_imglstSortIcons.Add(&m_bmpDownArrow, RGB(255, 255, 255));
}
}
/*** Draw the entire item (called if window has style LVS_OWNERDRAWFIXED) ****/
void CListBase::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
LVITEM* pItem = GetLVITEM(lpDrawItemStruct->itemID);
DWORD dwStyle = m_pListCtrl->GetStyle();
bool bSelected = (pItem->state & LVIS_SELECTED) != 0;
bool bAlwaysSelected = bSelected && (dwStyle & LVS_SHOWSELALWAYS) != 0;
bool bLVHasFocus = m_pListCtrl->GetFocus() == m_pListCtrl;
bool bItemHasFocus = pItem->state & LVIS_FOCUSED && bLVHasFocus;
bool bReallySelected = bSelected && bLVHasFocus;
bool bSelectionVisible = bAlwaysSelected || bReallySelected;
bool bFullRowSelected = (m_dwExtendedStyle & LVS_EX_FULLROWSELECT) != 0;
bool bItemUnderCursor = pItem->iItem == m_iItemUnderCursor;
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CBrush brushHiLite;
DWORD dwNormalText = GetSysColor(COLOR_WINDOWTEXT);
DWORD dwHiLiteBk = 0;
bool bHiLite = false;
if (!m_hTheme)
if (bReallySelected)
{
dwHiLiteBk =
pItem->iItem == m_iHotItem ?
m_dwHotLite : GetSysColor(COLOR_HIGHLIGHT);
brushHiLite.CreateSolidBrush(dwHiLiteBk);
}
else if (bAlwaysSelected)
{
dwHiLiteBk = GetSysColor(COLOR_3DFACE);
brushHiLite.CreateSolidBrush(dwHiLiteBk);
}
CFont* pfontPrev = 0;
CFont* pfontHotUnderlined = 0;
bool bHotUnderlining = false;
if (m_dwExtendedStyle & LVS_EX_TWOCLICKACTIVATE &&
(bSelected || bAlwaysSelected) ||
(m_dwExtendedStyle & (LVS_EX_UNDERLINEHOT | LVS_EX_ONECLICKACTIVATE)) ==
(LVS_EX_UNDERLINEHOT | LVS_EX_ONECLICKACTIVATE) &&
pItem->iItem == m_iHotItem)
{
CFont* pFont = m_pListCtrl->GetFont();
LOGFONT logFont;
if (pFont->GetLogFont(&logFont))
{
logFont.lfUnderline = TRUE;
pfontHotUnderlined = new CFont;
pfontHotUnderlined->CreateFontIndirect(&logFont);
if (bFullRowSelected) pfontPrev = pDC->SelectObject(pfontHotUnderlined);
bHotUnderlining = true;
}
}
RECT rcItem; // rectangle bounding complete item
m_pListCtrl->GetItemRect(pItem->iItem, &rcItem, LVIR_BOUNDS);
CRect rcLabelArea; // rectangle bounding item label
m_pListCtrl->GetItemRect(pItem->iItem, rcLabelArea, LVIR_LABEL);
CRect rcSelection; // rectangle bounding selection
if (bFullRowSelected)
{
rcSelection = rcItem;
if (m_hTheme)
{
if (bSelectionVisible || bItemUnderCursor)
{
// draw background of selection area
DrawThemeBackground(
m_hTheme, pDC->m_hDC, LVP_LISTITEM,
bItemUnderCursor && bSelectionVisible ? LISS_HOTSELECTED :
bReallySelected ? LISS_SELECTED :
bItemUnderCursor ? LISS_HOT :
LISS_SELECTEDNOTFOCUS,
rcSelection, 0);
bHiLite = true;
}
}
else
{
if (IndexToOrder(0) == 0 || m_bKeepLabelLeft)
{
rcSelection.left = rcLabelArea.left;
int nWidthOfCol0 =
static_cast<int>(
m_pListCtrl->DefWindowProc(LVM_GETCOLUMNWIDTH, OrderToIndex(0), 0));
if (rcSelection.left > nWidthOfCol0) rcSelection.left = nWidthOfCol0;
if (bSelectionVisible && rcSelection.left > rcItem.left)
{
// fill area left from selection with background color
RECT rc = rcItem;
rc.right = rcSelection.left-1;
EraseRect(pDC, &rc);
}
}
if (bSelectionVisible)
{
pDC->FillRect (rcSelection, &brushHiLite);
pDC->SetBkColor (dwHiLiteBk);
pDC->SetTextColor(
!m_hTheme && bReallySelected ? GetSysColor(COLOR_HIGHLIGHTTEXT) :
!m_hTheme && bHotUnderlining ? m_dwHotLite :
dwNormalText);
bHiLite = true;
}
}
}
LVCOLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH;
// display all subitems
int nIndex;
RECT rcSubItem = {0, 0, 0, 0}; // rectangle bounding subitem
for (int nColumn = 0;
m_pListCtrl->DefWindowProc(
LVM_GETCOLUMN, nIndex = OrderToIndex(nColumn),
reinterpret_cast<LPARAM>(&lvc));
++nColumn)
{
LVITEM* pSubItem = nIndex > 0 ? GetLVITEM(pItem->iItem, nIndex) : pItem;
RECT rcText; // output rectangle
int iAbsSortColumn = abs(m_iSortColumn) - 1;
int iLogicalIndex = GetLogicalIndex(nIndex);
// consider column margins
if (nColumn > 0)
{
// move output rectangle over next column
rcSubItem.left = rcSubItem.right;
rcSubItem.right += lvc.cx;
}
else
{
rcSubItem = rcItem;
rcSubItem.right = rcSubItem.left + lvc.cx;
}
if (nIndex == 0 && !m_bKeepLabelLeft || nColumn == 0 && m_bKeepLabelLeft)
{
if (bHotUnderlining && !bFullRowSelected)
pfontPrev = pDC->SelectObject(pfontHotUnderlined);
rcText = rcLabelArea;
// extra space is only needed if there are icons to be displayed
if (rcText.left + m_pListCtrl->GetScrollPos(SB_HORZ) > m_iFirstColXOff)
rcText.left += m_iFirstColXOff;
rcText.right -= nIndex > 0 ? m_iNextColXOff : m_iFirstColXOff;
}
else
{
if (bHotUnderlining && !bFullRowSelected && pfontPrev)
{
pDC->SelectObject(pfontPrev);
delete pfontHotUnderlined;
bHotUnderlining = false;
}
rcText = rcSubItem;
rcText.left += m_iNextColXOff;
rcText.right -= m_iNextColXOff;
// take subitem image into consideration
if (!m_bKeepLabelLeft &&
nIndex > 0 &&
m_dwExtendedStyle & LVS_EX_SUBITEMIMAGES &&
pSubItem->iImage)
{
CRect rcIcon;
if (m_pListCtrl->GetSubItemRect(
pItem->iItem, iLogicalIndex, LVIR_ICON, rcIcon))
rcText.left += rcIcon.Width();
}
}
CRect rcLabel;
if (!GetLabelRect(pItem->iItem, iLogicalIndex, rcLabel))
{
rcLabel.left = rcText.left;
rcLabel.right = rcText.right;
}
if (!bFullRowSelected)
{
bHiLite = false;
if ((bSelectionVisible || bItemHasFocus) &&
(
nIndex == 0 && !m_bKeepLabelLeft && nColumn != 0 ||
nColumn == 0 && (m_bKeepLabelLeft || nIndex == 0)
))
{
// calculate selection area
RECT rcSubItemSelection = rcSubItem;
if (!bFullRowSelected)
{
if (!m_hTheme)
{
rcSubItemSelection = rcLabelArea;
switch (nIndex == 0 && nColumn == 0 || !m_bKeepLabelLeft ?
m_iFormatOfSubItem0 : lvc.fmt & LVCFMT_JUSTIFYMASK)
{
case LVCFMT_LEFT:
rcSubItemSelection.right =
rcSubItemSelection.left + rcLabel.Width();
break;
case LVCFMT_RIGHT:
rcSubItemSelection.left =
rcSubItemSelection.right - rcLabel.Width();
break;
case LVCFMT_CENTER:
{
int nSelectionWidth = rcLabel.Width();
rcSubItemSelection.left =
rcLabelArea.left + (rcLabelArea.Width() - nSelectionWidth) / 2;
rcSubItemSelection.right =
rcSubItemSelection.left + nSelectionWidth;
break;
}
default:
ASSERT(false);
break;
}
if (rcSubItemSelection.left < rcLabelArea.left)
rcSubItemSelection.left = rcLabelArea.left;
if (rcSubItemSelection.right > rcLabelArea.right)
rcSubItemSelection.right = rcLabelArea.right;
}
rcSelection = rcSubItemSelection;
}
if (bSelectionVisible)
{
if (rcSubItemSelection.left > rcSubItem.left)
{
// fill area left from selection with background color
RECT rc = rcSubItem;
rc.right = rcSubItemSelection.left-1;
EraseRect(pDC, &rc);
}
// fill selection area with highlight color
if (m_hTheme)
DrawThemeBackground(
m_hTheme, pDC->m_hDC, LVP_LISTITEM,
bItemUnderCursor && bSelectionVisible ? LISS_HOTSELECTED :
bReallySelected ? LISS_SELECTED :
bItemUnderCursor ? LISS_HOT :
LISS_SELECTEDNOTFOCUS,
&rcSubItemSelection, 0);
else
{
pDC->FillRect (&rcSubItemSelection, &brushHiLite);
pDC->SetBkColor (dwHiLiteBk);
pDC->SetTextColor(
bReallySelected ? GetSysColor(COLOR_HIGHLIGHTTEXT) :
bHotUnderlining ? m_dwHotLite :
dwNormalText);
}
// fill area right from selection with background color
if (rcSubItemSelection.right < rcSubItem.right)
{
RECT rc = rcSubItem;
rc.left = rcSubItemSelection.right+1;
EraseRect(pDC, &rc);
}
bHiLite = true;
}
}
}
if (!bHiLite)
{
EraseRect (pDC, &rcSubItem);
pDC->SetBkColor (m_bColorSortColumn &&
m_iSortColumn != 0 &&
m_aColumnData[iAbsSortColumn]->m_bVisible &&
nIndex == GetPhysicalIndex(iAbsSortColumn) ?
m_dwColSortColor : m_pListCtrl->GetBkColor());
pDC->SetTextColor(!m_hTheme && bHotUnderlining ?
m_dwHotLite : dwNormalText);
}
if (!m_bKeepLabelLeft && nIndex == 0 ||
m_bKeepLabelLeft && nColumn == 0)
{
RECT rcIcon;
if (GetStateIconRect(pItem->iItem, &rcIcon))
DrawStateIcon(pDC, pItem, &rcIcon);
}
if (!m_bKeepLabelLeft &&
(
nIndex == 0 ||
m_dwExtendedStyle & LVS_EX_SUBITEMIMAGES && pSubItem->iImage > 0
) ||
m_bKeepLabelLeft && nColumn == 0)
{
CRect rcIcon;
if (m_pListCtrl->GetSubItemRect(
pItem->iItem, iLogicalIndex, LVIR_ICON, rcIcon))
{
LVITEM* pLogicalItem;
NMLVDISPINFO lvDispInfo;
if (dwStyle & LVS_OWNERDATA)
{
lvDispInfo.hdr.hwndFrom = m_pListCtrl->m_hWnd;
lvDispInfo.hdr.idFrom = m_pListCtrl->GetDlgCtrlID();
lvDispInfo.hdr.code = LVN_GETDISPINFO;
lvDispInfo.item.mask = LVIF_IMAGE;
lvDispInfo.item.iItem = pItem->iItem;
lvDispInfo.item.iSubItem = 0;
OnGetdispinfo(&lvDispInfo.hdr);
pLogicalItem = &lvDispInfo.item;
}
else
pLogicalItem =
reinterpret_cast<ITEM_DATA*>(pItem->lParam)->m_apLVItem[0];
// take state of the physical item!
pLogicalItem->state = pItem->state;
pLogicalItem->stateMask = pItem->stateMask;
DrawSmallIcon(pDC, pLogicalItem, rcIcon);
pLogicalItem->state = 0;
pLogicalItem->stateMask = 0;
}
}
pSubItem->iSubItem = GetLogicalIndex(pSubItem->iSubItem);
lvc.iSubItem = pSubItem->iSubItem;
DrawSubItemText(pDC, pSubItem, &lvc, &rcText);
if (nIndex > 0)
{
delete[] pSubItem->pszText;
delete pSubItem;
}
}
if (bHotUnderlining)
{
pDC->SelectObject(pfontPrev);
delete pfontHotUnderlined;
}
delete[] pItem->pszText;
delete pItem;
// If item has focus draw focus rectangle
if (bItemHasFocus)
{
pDC->SetTextColor(dwNormalText);
if (m_hTheme)
// If visual themes are enabled, draw focus rectangle inside the border
// of the selection rectangle
if (rcSelection.Width() > 1 && rcSelection.Height() > 1)
{
++rcSelection.left;
++rcSelection.top;
--rcSelection.right;
--rcSelection.bottom;
}
pDC->DrawFocusRect(&rcSelection);
}
}
/*** Duplicate column information ********************************************/
LVCOLUMN* CListBase::DupLVColumn(LVCOLUMN* pLVColumn) const
{
#pragma warning(push)
#pragma warning(disable:4616 6211)
LVCOLUMN* pLVColumn2 = new LVCOLUMN(*pLVColumn);
// mask unnecessary fields
if (!(pLVColumn2->mask & LVCF_FMT)) pLVColumn2->fmt = 0;
if (!(pLVColumn2->mask & LVCF_IMAGE)) pLVColumn2->iImage = 0;
if (!(pLVColumn2->mask & LVCF_ORDER)) pLVColumn2->iOrder = 0;
if (!(pLVColumn2->mask & LVCF_SUBITEM)) pLVColumn2->iSubItem = 0;
if (pLVColumn2->mask & LVCF_TEXT)
{
ASSERT(pLVColumn2->pszText);
pLVColumn2->pszText = new TCHAR[_tcslen(pLVColumn->pszText) + 1];
_tcscpy(pLVColumn2->pszText, pLVColumn->pszText);
}
else
pLVColumn2->pszText = 0;
if (!(pLVColumn2->mask & LVCF_WIDTH)) pLVColumn2->cx = 0;
pLVColumn2->cchTextMax = 0;
return pLVColumn2;
#pragma warning(pop)
}
/*** Duplicate item information **********************************************/
LVITEM* CListBase::DupLVItem(LVITEM* pLVItem) const
{
#pragma warning(push)
#pragma warning(disable:4616 6211)
LVITEM* pLVItem2 = new LVITEM(*pLVItem);
// mask unnecessary fields
if (!(pLVItem2->mask & LVIF_IMAGE)) pLVItem2->iImage = 0;
if (!(pLVItem2->mask & LVIF_INDENT)) pLVItem2->iIndent = 0;
if (!(pLVItem2->mask & LVIF_PARAM)) pLVItem2->lParam = 0;
if (pLVItem2->mask & LVIF_STATE)
pLVItem2->state &= pLVItem2->stateMask;
else
{
pLVItem2->state = 0;
pLVItem2->stateMask = 0;
}
if (pLVItem2->mask & LVIF_TEXT)
{
ASSERT(pLVItem2->pszText);
if (pLVItem2->pszText != LPSTR_TEXTCALLBACK)
{
pLVItem2->pszText = new TCHAR[_tcslen(pLVItem->pszText) + 1];
_tcscpy(pLVItem2->pszText, pLVItem->pszText);
}
}
else
pLVItem2->pszText = 0;
pLVItem2->iSubItem = 0;
pLVItem2->cchTextMax = 0;
return pLVItem2;
#pragma warning(pop)
}
/*** Erase rectangular area with background color ****************************/
void CListBase::EraseRect(CDC* pDC, LPRECT pRect)
{
if (pDC->SaveDC())
{
CRgn rgn;
rgn.CreateRectRgn (pRect->left, pRect->top,
pRect->right, pRect->bottom);
pDC->SelectClipRgn (&rgn);
m_pListCtrl->SendMessage(WM_ERASEBKGND,
reinterpret_cast<WPARAM>(pDC->m_hDC));
pDC->RestoreDC (-1);
}
}
/*** Retrieves the bounding rectangle of the label text of an item or ********/
/*** subitem ********/
bool CListBase::GetLabelRect(int nItem, int nSubItem, LPRECT pRect)
{
// Determine the rectangle bounding the text selection area
CRect rcLabel;
if (m_pListCtrl->GetSubItemRect(nItem, nSubItem, LVIR_LABEL, rcLabel))
{
int nWidth =
m_pListCtrl->GetStringWidth(m_pListCtrl->GetItemText(nItem, nSubItem)) +
(nSubItem == 0 && !m_bKeepLabelLeft ||
IndexToOrder(nSubItem) == 0 && m_bKeepLabelLeft ?
m_iFirstColXOff : m_iNextColXOff) +
(nSubItem == 0 && (!m_bKeepLabelLeft || IndexToOrder(0) == 0) ?
m_iFirstColXOff : m_iNextColXOff);
if (nWidth < rcLabel.Width())
switch (m_aColumnData[nSubItem]->m_pLVColumn->fmt & LVCFMT_JUSTIFYMASK)
{
case LVCFMT_LEFT:
rcLabel.right -= rcLabel.Width() - nWidth;
break;
case LVCFMT_RIGHT:
rcLabel.left += rcLabel.Width() - nWidth;
break;
case LVCFMT_CENTER:
rcLabel.left += (rcLabel.Width() - nWidth) / 2;
rcLabel.right = rcLabel.left + nWidth;
break;
}
*pRect = rcLabel;
return true;
}
return false;
}
/*** Retrieve for a given physical column number the logical column number ***/
int CListBase::GetLogicalIndex(int nPhysicalIndex) const
{
INT_PTR nColumnCount = m_aColumnData.GetSize();
for (int i = 0; i < nColumnCount; ++i)
if (m_aColumnData[i]->m_bEnabled && m_aColumnData[i]->m_bVisible)
{
if (i == nPhysicalIndex) return nPhysicalIndex;
}
else
++nPhysicalIndex;
ASSERT(false);
return -1;
}
/*** Retrieve for a given physical column order the logical column order *****/
int CListBase::GetLogicalOrder(int nPhysicalOrder) const
{
for (int i = 0; i <= nPhysicalOrder; ++i)
for (INT_PTR j = m_aColumnData.GetUpperBound(); j >= 0; --j)
if (m_aColumnData[j]->m_nOrder == i)
{
if (m_aColumnData[i]->m_bEnabled && m_aColumnData[j]->m_bVisible)
{
if (i == nPhysicalOrder) return nPhysicalOrder;
}
else
++nPhysicalOrder;
break;
}
ASSERT(false);
return -1;
}
/*** Get all attributes of a given physical item or subitem ******************/
LVITEM* CListBase::GetLVITEM(int nItem, int nSubItem) const
{
#pragma warning(push)
#pragma warning(disable:4616 6211)
LVITEM* pItem = new LVITEM;
pItem->mask = LVIF_IMAGE | LVIF_STATE | LVIF_TEXT | LVIF_PARAM;
pItem->iItem = nItem;
pItem->iSubItem = nSubItem;
pItem->stateMask = ~0U;
// enlarge text buffer gradually until it's large enough
for (int nLen = 128;; nLen += nLen)
{
LPTSTR pszText = new TCHAR[nLen];
pItem->cchTextMax = nLen;
pItem->pszText = pszText;
if (m_pListCtrl->DefWindowProc(
LVM_GETITEM, 0, reinterpret_cast<LPARAM>(pItem)))
{
if (pItem->pszText != pszText)
{
// Windows hasn't used our buffer!
_tcsncpy(pszText, pItem->pszText, nLen-1);
pszText[nLen-1] = _T('\0');
pItem->pszText = pszText;
}
}
else
{
delete[] pszText;
delete pItem;
return 0;
}
if (static_cast<int>(_tcslen(pItem->pszText)) < nLen-1) break;
delete[] pItem->pszText;
}
return pItem;
#pragma warning(pop)
}
/*** Returns the physical index of a given column index **********************/
int CListBase::GetPhysicalIndex(int nColumnIndex) const
{
int nIndex = 0;
for (int i = 0; i < nColumnIndex; ++i)
if (m_aColumnData[i]->m_bEnabled && m_aColumnData[i]->m_bVisible) ++nIndex;
return nIndex;
}
/*** Returns the physical order of a given column order **********************/
int CListBase::GetPhysicalOrder(int nColumnOrder) const
{
int nOrder = 0;
for (int i = 0; i < nColumnOrder; ++i)
for (INT_PTR j = m_aColumnData.GetUpperBound(); j >= 0; --j)
if (m_aColumnData[j]->m_nOrder == i)
{
if (m_aColumnData[i]->m_bEnabled && m_aColumnData[j]->m_bVisible)
++nOrder;
break;
}
return nOrder;
}
/*** Retrieves the bounding rectangle for the state icon of an item **********/
bool CListBase::GetStateIconRect(int nItem, LPRECT pRect)
{
bool bRet = false;
CRect rcSubItem;
if (m_pListCtrl->GetSubItemRect(
nItem, GetLogicalIndex(m_bKeepLabelLeft ? OrderToIndex(0) : 0),
LVIR_BOUNDS, rcSubItem))
{
RECT rcSmallIcon;
m_bAlwaysGetSmallIconRect = true;
if (m_pListCtrl->GetItemRect(nItem, &rcSmallIcon, LVIR_ICON))
{
*pRect = rcSubItem;
pRect->right = rcSmallIcon.left;
if (rcSmallIcon.right > rcSmallIcon.left) --pRect->right;
// calculate x-offset of state icon
if (!m_bIconXOffCalculated)
{
CImageList* pImageList = m_pListCtrl->GetImageList(LVSIL_STATE);
if (pImageList)
{
// retrieve width of state icon
IMAGEINFO ii = {0, 0, 0, 0};
if (pImageList->GetImageInfo(0, &ii))
{
int nXOff =
pRect->right - (ii.rcImage.right - ii.rcImage.left) -
pRect->left;
if (nXOff < 0)
m_iIconXOff = 0;
else if (nXOff < 4)
m_iIconXOff = nXOff;
else
m_iIconXOff = 4;
}
else
m_iIconXOff = 4;
}
else
m_iIconXOff = 4;
m_bIconXOffCalculated = true;
}
pRect->left += m_iIconXOff;
// clip at right column border
int nWidth = rcSubItem.Width();
if (pRect->right >= rcSubItem.left + nWidth)
pRect->right = pRect->left - m_iIconXOff + nWidth - 1;
bRet = true;
}
m_bAlwaysGetSmallIconRect = false;
}
return bRet;
}
/*** Hide an individual column ***********************************************/
void CListBase::HideColumn(int nColumn)
{
ASSERT(nColumn >= 0 && nColumn < m_aColumnData.GetSize());
// hide column
int nPhysicalColumn = GetPhysicalIndex(nColumn);
m_aColumnData[nColumn]->m_nWidth =
m_pListCtrl->GetColumnWidth(nColumn);
// hide column coloring, too, if this column was the selected column
if (m_visualStyle == Present &&
m_bColorSortColumn &&
nColumn == abs(m_iSortColumn)-1)
m_pListCtrl->SendMessage(LVM_SETSELECTEDCOLUMN, 0xFFFFFFFF);
m_pListCtrl->DefWindowProc(LVM_DELETECOLUMN, nPhysicalColumn, 0);
}
/*** Return the order in the header control of a subitem, based on its index */
int CListBase::IndexToOrder(int nIndex)
{
ASSERT(m_pListCtrl->GetHeaderCtrl());
HDITEM headerItem = {HDI_ORDER};
return
m_pListCtrl->GetHeaderCtrl()->GetItem(nIndex, &headerItem) ?
headerItem.iOrder : -1;
}
/*** Invalidate client area not covered by list control items ****************/
void CListBase::InvalidateNonItemArea()
{
int nSortColumn = abs(m_iSortColumn) - 1;
if (m_bColorSortColumn &&
m_iSortColumn != 0 &&
m_aColumnData[nSortColumn]->m_bEnabled &&
m_aColumnData[nSortColumn]->m_bVisible)
{
RECT rcHdrItem;
ASSERT(m_pListCtrl->GetHeaderCtrl());
if (m_pListCtrl->GetHeaderCtrl()->GetItemRect(0, &rcHdrItem))
{
RECT rcThis;
m_pListCtrl->GetClientRect(&rcThis);
RECT rcToBeErased =
{
rcThis.left, rcHdrItem.bottom, rcThis.right, rcThis.bottom
};
int iItemCount = m_pListCtrl->GetItemCount();
if (iItemCount > 0)
{
// erase area above top item
ASSERT(m_pListCtrl->GetTopIndex() >= 0);
RECT rcItem;
m_pListCtrl->GetItemRect(
m_pListCtrl->GetTopIndex(), &rcItem, LVIR_BOUNDS);
rcToBeErased.bottom = rcItem.top;
m_pListCtrl->InvalidateRect(&rcToBeErased);
// erase area below bottom item
m_pListCtrl->GetItemRect(iItemCount - 1, &rcItem, LVIR_BOUNDS);
if (rcItem.bottom < rcThis.bottom)
{
rcToBeErased.top = rcItem.bottom;
rcToBeErased.bottom = rcThis.bottom;
}
else
return;
}
m_pListCtrl->InvalidateRect(&rcToBeErased);
}
}
}
/*** Rejustify first column of listview control to enable a right- ***********/
/*** justified or centerd first column ***********/
void CListBase::JustifyFirstColumn(int nFormat)
{
m_iFormatOfSubItem0 = nFormat;
if (m_pListCtrl->GetStyle() & LVS_OWNERDRAWFIXED ||
m_dwExtendedStyle & LVS_EX_FULLROWSELECT)
{
CHeaderCtrl* pHeaderCtrl = m_pListCtrl->GetHeaderCtrl();
ASSERT(pHeaderCtrl);
if (pHeaderCtrl)
{
HDITEM hdrItem;
hdrItem.mask = HDI_FORMAT;
if (pHeaderCtrl->GetItem(0, &hdrItem))
{
hdrItem.fmt =
hdrItem.fmt & ~HDF_JUSTIFYMASK | nFormat & HDF_JUSTIFYMASK;
pHeaderCtrl->SetItem(0, &hdrItem);
}
}
}
}
/*** Label editing will be started *******************************************/
BOOL CListBase::OnBeginLabelEdit(NMHDR* pNMHDR)
{
NMLVDISPINFO* pNMLVDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
m_iItemEdit = pNMLVDispInfo->item.iItem;
m_bLabelEditingCancelled = false;
m_pLabelEdit->SubclassWindow(m_pListCtrl->GetEditControl()->m_hWnd);
return FALSE;
}
/*** Label editing will be cancelled *****************************************/
LRESULT CListBase::OnCancelEditLabel()
{
m_bLabelEditingCancelled = true;
return m_pListCtrl->Default();
}
/*** A column header has been clicked ****************************************/
BOOL CListBase::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
int iPhysicalColumn = pNMListView->iSubItem;
bool bSortColumnChanged = false;
pNMListView->iSubItem = GetLogicalIndex(iPhysicalColumn);
if (pNMListView->iSubItem == abs(m_iSortColumn)-1)
m_iSortColumn = -m_iSortColumn;
else
{
m_iSortColumn = pNMListView->iSubItem + 1;
bSortColumnChanged = true;
}
SetSortIcon();
if (bSortColumnChanged && m_visualStyle == Present)
m_pListCtrl->SendMessage(LVM_SETSELECTEDCOLUMN, iPhysicalColumn);
*pResult = 0;
return FALSE;
}
/*** An item in the column selection menu has been selected ******************/
BOOL CListBase::OnCommand(WPARAM wParam)
{
if (HIWORD(wParam) == 0)
ShowColumn(static_cast<int>(wParam), !m_aColumnData[wParam]->m_bVisible);
return TRUE;
}
/*** The user has right clicked the mouse ************************************/
void CListBase::OnContextMenu(CWnd* pWnd, CPoint point)
{
if (pWnd == m_pListCtrl->GetHeaderCtrl())
{
if (m_iColumnHidingAllowed > 0)
{
CMenu menu;
if (menu.CreatePopupMenu())
{
for (INT_PTR i = m_aColumnData.GetUpperBound(); i >= 0; --i)
if (m_aColumnData[i]->m_bEnabled)
menu.InsertMenu(0,
MF_BYPOSITION |
(m_aColumnData[i]->m_bVisible ? MF_CHECKED : MF_UNCHECKED) |
(m_aColumnData[i]->m_bHidingAllowed ? 0 : MF_GRAYED) |
MF_STRING, i, m_aColumnData[i]->m_pLVColumn->pszText);
CPoint pt(0, 0);
GetCursorPos (&pt);
menu.TrackPopupMenu(TPM_LEFTALIGN, pt.x, pt.y, m_pListCtrl, 0);
}
}
}
else if (pWnd == m_pListCtrl)
{
CWnd* pWndParent = pWnd->GetParent();
if (pWndParent)
pWndParent->SendMessage(
WM_CONTEXTMENU, reinterpret_cast<WPARAM>(pWnd->m_hWnd),
MAKELPARAM(point.x, point.y));
}
}
/*** A list view (sub)item will be drawn *************************************/
void CListBase::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
// If the list view control uses the Windows Vista Explorer style we have
// nothing to do
if (m_bExplorerStyle)
{
*pResult = CDRF_DODEFAULT;
return;
}
ASSERT(m_pListCtrl->GetHeaderCtrl());
NMLVCUSTOMDRAW* pNMLVCustomDraw = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
switch (pNMLVCustomDraw->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
*pResult =
CDRF_NOTIFYSUBITEMDRAW |
(m_bBkImage && !(m_dwExtendedStyle & LVS_EX_DOUBLEBUFFER) ?
CDRF_NOTIFYPOSTPAINT : 0);
break;
case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
{
int iSortColumn = abs(m_iSortColumn) - 1;
if (m_bColorSortColumn &&
m_iSortColumn != 0 &&
m_aColumnData[iSortColumn]->m_bEnabled &&
m_aColumnData[iSortColumn]->m_bVisible &&
pNMLVCustomDraw->iSubItem == GetPhysicalIndex(iSortColumn))
pNMLVCustomDraw->clrTextBk = m_dwColSortColor;
else
pNMLVCustomDraw->clrTextBk = m_pListCtrl->GetBkColor();
*pResult = CDRF_NEWFONT | CDRF_NOTIFYPOSTPAINT;
break;
}
case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM:
{
// special treatment for first subitem:
// fill empty area left of text label
int iItem = static_cast<int>(pNMLVCustomDraw->nmcd.dwItemSpec);
int iSubItem = pNMLVCustomDraw->iSubItem;
CRect rcItem;
m_pListCtrl->GetHeaderCtrl()->GetItemRect(iSubItem, rcItem);
int iColumnWidth = rcItem.Width();
int iLeftX = rcItem.left - m_pListCtrl->GetScrollPos(SB_HORZ);
bool bFirstColumn = rcItem.left == 0;
bool bHasFocus = m_pListCtrl->GetFocus() == m_pListCtrl;
CDC* pDC = CDC::FromHandle(pNMLVCustomDraw->nmcd.hdc);
CRect rc(rcItem);
m_bAlwaysGetSmallIconRect = true;
m_pListCtrl->GetSubItemRect(iItem, iSubItem, LVIR_ICON, rc);
m_bAlwaysGetSmallIconRect = false;
if (iSubItem > 0)
if ( m_dwExtendedStyle & LVS_EX_SUBITEMIMAGES && !bFirstColumn ||
!(m_dwExtendedStyle & LVS_EX_SUBITEMIMAGES))
rc.right += m_iIconXOff;
else if (m_dwExtendedStyle & LVS_EX_SUBITEMIMAGES &&
m_visualStyle == Present)
rc.right += m_iFirstColXOff;
CBrush brushColColor;
bool bFullRowSelect = (m_dwExtendedStyle & LVS_EX_FULLROWSELECT) != 0;
LVITEM item;
item.mask = LVIF_IMAGE | LVIF_STATE;
item.iItem = iItem;
item.iSubItem = iSubItem;
item.stateMask =
LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_OVERLAYMASK;
m_pListCtrl->GetItem(&item);
bool bEraseBkgnd = false;
if (!bFirstColumn &&
bFullRowSelect &&
item.state & LVIS_SELECTED &&
bHasFocus &&
m_dwExtendedStyle & (LVS_EX_UNDERLINEHOT |
LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE) &&
item.iItem == m_pListCtrl->GetHotItem())
// create brush with hot-tracked color
brushColColor.CreateSolidBrush(m_dwHotLite);
else if (!bFirstColumn &&
bFullRowSelect &&
item.state & LVIS_SELECTED &&
bHasFocus)
// create brush with highlight color
brushColColor.CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
else if (!bFirstColumn &&
bFullRowSelect &&
item.state & LVIS_SELECTED &&
m_pListCtrl->GetStyle() & LVS_SHOWSELALWAYS)
// create brush with highlight (nonfocus) color
brushColColor.CreateSolidBrush(GetSysColor(COLOR_3DFACE));
else
// fill empty area left of text label
// by sending a WM_ERASEBKGND message
bEraseBkgnd = true;
if (bEraseBkgnd)
{
rc.left = iLeftX;
if (iLeftX > rc.right) rc.right = iLeftX;
EraseRect(pDC, rc);
}
else
{
// select new brush and save previous brush
CBrush* pbrushPrev = pDC->SelectObject(&brushColColor);
// color area left of text label
if (iSubItem == 0 ||
m_dwExtendedStyle & LVS_EX_SUBITEMIMAGES && item.iImage > 0)
pDC->PatBlt(
iLeftX, rc.top, __max(rc.left, rc.right) - iLeftX, rc.Height(),
PATCOPY);
// restore previous brush
pDC->SelectObject(pbrushPrev);
}
if (iSubItem == 0 && iColumnWidth > m_iIconXOff)
{
// draw state icon
CImageList* pimglst = m_pListCtrl->GetImageList(LVSIL_STATE);
if (pimglst)
{
int iImage = (item.state & LVIS_STATEIMAGEMASK) >> 12;
if (iImage > 0)
{
IMAGEINFO imgInfo;
// image indices are zero-based
if (pimglst->GetImageInfo(--iImage, &imgInfo) &&
GetStateIconRect(item.iItem, rc) &&
rc.Width() > 0)
{
int iRcWidth = rc.Width();
int iRcHeight = rc.Height();
long lImgWidth = imgInfo.rcImage.right - imgInfo.rcImage.left;
long lImgHeight = imgInfo.rcImage.bottom - imgInfo.rcImage.top;
long lDiffHorz = iRcWidth - lImgWidth;
long lDiffVert = iRcHeight - lImgHeight;
// size of state icon to be drawn
SIZE sz =
{
lDiffHorz >= 0 ? lImgWidth : iRcWidth,
lDiffVert >= 0 ? lImgHeight : iRcHeight
};
// coordinates of state icon's upper left corner
POINT pt = {rc.left, rc.top};
if (lDiffVert > 2) pt.y += lDiffVert / 2;
pimglst->DrawIndirect(
pDC, iImage, pt, sz, CPoint(0, 0),
item.state & LVIS_SELECTED && bHasFocus ?
ILD_SELECTED : ILD_NORMAL,
SRCCOPY, CLR_NONE);
}
}
}
}
if (iSubItem == 0 && iColumnWidth > m_iIconXOff ||
m_dwExtendedStyle & LVS_EX_SUBITEMIMAGES &&
iSubItem > 0 && iColumnWidth > 0)
{
// draw small icon
CImageList* pimglst = m_pListCtrl->GetImageList(LVSIL_SMALL);
if (pimglst)
if (item.iImage > 0)
{
IMAGEINFO imgInfo;
if (pimglst->GetImageInfo(item.iImage, &imgInfo) &&
m_pListCtrl->GetSubItemRect(iItem, iSubItem, LVIR_ICON, rc))
{
int nIconOffset = rc.left - iLeftX;
if (iColumnWidth > nIconOffset)
pimglst->DrawIndirect(
pDC, item.iImage, rc.TopLeft(),
CSize(
__min(
iColumnWidth - nIconOffset,
imgInfo.rcImage.right - imgInfo.rcImage.left),
__min(
rc.Height(),
imgInfo.rcImage.bottom - imgInfo.rcImage.top)),
CPoint(0, 0),
(item.state & LVIS_SELECTED && bHasFocus ?
ILD_SELECTED : ILD_NORMAL) | item.state & LVIS_OVERLAYMASK,
SRCCOPY, CLR_NONE);
}
}
}
*pResult = CDRF_DODEFAULT;
break;
}
case CDDS_ITEMPOSTPAINT:
{
// Correct a Windows drawing error which occurs if a background image is
// set and the style LVS_EX_DOUBLEBUFFER is not applied.
// The correction works as follows:
// If a selected item has just been drawn, redraw all visible items which
// were selected during the previous drawing cycle and aren't selected
// anymore during this drawing cycle.
int iItemJustDrawn = static_cast<int>(pNMLVCustomDraw->nmcd.dwItemSpec);
LVITEM item;
item.mask = LVIF_STATE;
item.iItem = iItemJustDrawn;
item.iSubItem = 0;
item.stateMask = LVIS_SELECTED;
m_pListCtrl->GetItem(&item);
if (item.state & LVIS_SELECTED)
{
RECT rcClientArea;
m_pListCtrl->GetClientRect(&rcClientArea);
bool bItemJustDrawnFound = false;
for (POSITION pos =
m_lstVisibleItemsPreviouslySelected.GetHeadPosition();
pos;)
{
POSITION posCurrent = pos;
int iItem =
m_lstVisibleItemsPreviouslySelected.GetNext(pos);
if (iItem != iItemJustDrawn)
{
RECT rcItem;
m_pListCtrl->GetItemRect(iItem, &rcItem, LVIR_BOUNDS);
if (rcItem.bottom >= rcClientArea.top &&
rcItem.top <= rcClientArea.bottom)
{
// Item visible
item.iItem = iItem;
m_pListCtrl->GetItem(&item);
if (!(item.state & LVIS_SELECTED))
{
// Item not selected anymore
m_pListCtrl->InvalidateRect (&rcItem);
m_lstVisibleItemsPreviouslySelected.RemoveAt(posCurrent);
}
}
else
// Item not visible
m_lstVisibleItemsPreviouslySelected.RemoveAt(posCurrent);
}
else
bItemJustDrawnFound = true;
}
if (!bItemJustDrawnFound)
// Remember selected item just drawn
m_lstVisibleItemsPreviouslySelected.AddTail(iItemJustDrawn);
}
else
// If the item just been drawn was selected during the previous drawing
// cycle redraw it.
for (POSITION pos =
m_lstVisibleItemsPreviouslySelected.GetHeadPosition();
pos;)
{
POSITION posCurrent = pos;
int iItem =
m_lstVisibleItemsPreviouslySelected.GetNext(pos);
if (iItem == iItemJustDrawn)
{
RECT rcClientArea;
m_pListCtrl->GetClientRect(&rcClientArea);
RECT rcItem;
m_pListCtrl->GetItemRect(iItem, &rcItem, LVIR_BOUNDS);
if (rcItem.bottom >= rcClientArea.top &&
rcItem.top <= rcClientArea.bottom)
{
// Item visible
item.iItem = iItem;
m_pListCtrl->GetItem (&item);
m_pListCtrl->InvalidateRect(&rcItem);
}
m_lstVisibleItemsPreviouslySelected.RemoveAt(posCurrent);
}
}
*pResult = CDRF_DODEFAULT;
break;
}
default:
*pResult = CDRF_DODEFAULT;
break;
}
}
/*** All items will be removed ***********************************************/
LRESULT CListBase::OnDeleteAllItems()
{
BOOL bSuccess = TRUE;
if (!(m_pListCtrl->GetStyle() & LVS_OWNERDATA))
// First remove all item data ..
for (int nItem =
static_cast<int>(
m_pListCtrl->DefWindowProc(LVM_GETITEMCOUNT, 0, 0));
--nItem >= 0;)
{
LVITEM lvItem = {LVIF_PARAM, nItem, 0};
if (m_pListCtrl->DefWindowProc(LVM_GETITEM, 0,
reinterpret_cast<LPARAM>(&lvItem)))
delete reinterpret_cast<ITEM_DATA*>(lvItem.lParam);
else
{
ASSERT(false);
bSuccess = FALSE;
}
}
// ... then remove all items
m_pListCtrl->Default();
return bSuccess;
}
/*** A column will be deleted ************************************************/
LRESULT CListBase::OnDeleteColumn(WPARAM wParam)
{
int nColumn = static_cast<int>(wParam);
INT_PTR nColumnCount = m_aColumnData.GetSize();
if (nColumn < 0 || nColumn >= nColumnCount) return FALSE;
LRESULT lResult =
m_aColumnData[nColumn]->m_bEnabled && m_aColumnData[nColumn]->m_bVisible ?
m_pListCtrl->DefWindowProc(LVM_DELETECOLUMN, GetPhysicalIndex(nColumn), 0)
: TRUE;
if (lResult)
{
// adjust column order
int i;
for (i = 0; i < nColumnCount; ++i)
if (m_aColumnData[i]->m_nOrder > m_aColumnData[nColumn]->m_nOrder)
--m_aColumnData[i]->m_nOrder;
delete m_aColumnData[nColumn];
m_aColumnData.RemoveAt(nColumn);
if (m_iSortColumn)
{
// adjust index of sort column
if (m_iSortColumn < 0)
{
if (nColumn < -m_iSortColumn) ++m_iSortColumn;
}
else
{
if (nColumn < m_iSortColumn) --m_iSortColumn;
}
if (abs(m_iSortColumn) == nColumn) m_iSortColumn = 0;
}
// delete subitem structures belonging to column
for (i = static_cast<int>(m_pListCtrl->DefWindowProc(
LVM_GETITEMCOUNT, 0, 0)); --i >= 0;)
{
LVITEM lvItem = {LVIF_PARAM, i, 0};
if (m_pListCtrl->DefWindowProc(LVM_GETITEM, 0,
reinterpret_cast<LPARAM>(&lvItem)))
{
ITEM_DATA* pItemData = reinterpret_cast<ITEM_DATA*>(lvItem.lParam);
LVITEM* pLVItem = pItemData->m_apLVItem[nColumn];
pItemData->m_apLVItem.RemoveAt(nColumn);
if (pLVItem)
{
delete[] pLVItem->pszText;
delete pLVItem;
}
}
}
}
return lResult;
}
/*** An item will be removed *************************************************/
LRESULT CListBase::OnDeleteItem(WPARAM wParam)
{
int nItem = static_cast<int>(wParam);
if (nItem < 0) return FALSE;
LVITEM lvItem = {LVIF_PARAM, nItem, 0};
if (m_pListCtrl->DefWindowProc(LVM_GETITEM, 0,
reinterpret_cast<LPARAM>(&lvItem)) &&
m_pListCtrl->Default())
{
// remove administration data
delete reinterpret_cast<ITEM_DATA*>(lvItem.lParam);
return TRUE;
}
else
return FALSE;
}
/*** The list view control will be destroyed *********************************/
void CListBase::OnDestroy()
{
m_pListCtrl->SendMessage(LVM_CANCELEDITLABEL);
if (!(m_pListCtrl->GetStyle() & LVS_OWNERDATA))
// delete all items explicitly to ensure
// that all administration data will be deleted, too
m_pListCtrl->DeleteAllItems();
m_pListCtrl->Default();
}
/*** Label editing will be finished ******************************************/
BOOL CListBase::OnEndLabelEdit(NMHDR* pNMHDR)
{
if (m_bOnEndLabelEdit) return FALSE;
m_bOnEndLabelEdit = true;
// notify parent window
CWnd* pWndParent = m_pListCtrl->GetParent();
if (pWndParent)
{
LVITEM* pItem = &reinterpret_cast<NMLVDISPINFO*>(pNMHDR)->item;
if (!m_bLabelEditingCancelled)
{
m_pLabelEdit->GetWindowText(m_strEditedLabel);
int iBufLen = __max(m_strEditedLabel.GetLength() + 1, 260);
pItem->pszText = m_strEditedLabel.GetBuffer(iBufLen);
pItem->cchTextMax = iBufLen;
}
LRESULT validated =
pWndParent->SendMessage(
WM_NOTIFY, m_pListCtrl->GetDlgCtrlID(),
reinterpret_cast<LPARAM>(pNMHDR));
if (!m_bLabelEditingCancelled)
{
m_strEditedLabel.ReleaseBuffer();
if (validated)
{
pItem->mask = LVIF_TEXT;
m_pListCtrl->SetItem(pItem);
}
}
}
m_iItemEdit = -1;
m_bOnEndLabelEdit = false;
return TRUE;
}
/*** The background will be erased *******************************************/
BOOL CListBase::OnEraseBkgnd(CDC* pDC)
{
if (m_bColorSortColumn && m_iSortColumn != 0 && m_visualStyle != Present)
{
int iAbsSortColumn = abs(m_iSortColumn) - 1;
if (m_aColumnData[iAbsSortColumn]->m_bEnabled &&
m_aColumnData[iAbsSortColumn]->m_bVisible)
{
CRect rc;
if (!m_pListCtrl->GetHeaderCtrl()->GetItemRect(
GetPhysicalIndex(iAbsSortColumn), rc))
return m_pListCtrl->Default() != 0;
int nXScrlOff = m_pListCtrl->GetScrollPos(SB_HORZ);
int nLeftX = rc.left - nXScrlOff;
int nRightX = rc.right - nXScrlOff;
pDC->GetClipBox(rc); // get area to be erased
if (nLeftX < rc.right && nRightX > rc.left)
{
CBrush brushSortColor(m_dwColSortColor);
CBrush* pbrushPrev = pDC->SelectObject(&brushSortColor);
// remember clipping area in rectangular region
CRgn rgn;
rgn.CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
// erase area inside sort column with sort color
if (nLeftX > rc.left) rc.left = nLeftX;
if (nRightX < rc.right) rc.right = nRightX;
pDC->PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);
// exclude sort column from clipping area
pDC->ExcludeClipRect(rc);
// restore previous brush
pDC->SelectObject(pbrushPrev);
// erase background w/o sort column
BOOL bResult = m_pListCtrl->Default() != 0;
// restore original clipping area
pDC->SelectClipRgn(&rgn);
return bResult;
}
}
}
return m_pListCtrl->Default() != 0;
}
/*** A specific item will be searched ****************************************/
LRESULT CListBase::OnFindItem(WPARAM wParam, LPARAM lParam)
{
int iStart = static_cast<int>(wParam);
ASSERT(iStart >= -1);
const LVFINDINFO* plvfi = reinterpret_cast<const LVFINDINFO*>(lParam);
if (plvfi->flags & LVFI_PARAM)
{
int nItemCount = m_pListCtrl->GetItemCount();
for (int nItem = iStart; ++nItem < nItemCount;)
{
LVITEM lvItem = {LVIF_PARAM, nItem, 0};
if (m_pListCtrl->DefWindowProc(LVM_GETITEM, 0,
reinterpret_cast<LPARAM>(&lvItem)))
if (reinterpret_cast<ITEM_DATA*>(lvItem.lParam)->m_lParam ==
plvfi->lParam) return nItem;
}
// wrap around?
if (iStart >= 0 && plvfi->flags & LVFI_WRAP)
for (int nItem = 0; nItem <= iStart; ++nItem)
{
LVITEM lvItem = {LVIF_PARAM, nItem, 0};
if (m_pListCtrl->DefWindowProc(LVM_GETITEM, 0,
reinterpret_cast<LPARAM>(&lvItem)))
if (reinterpret_cast<ITEM_DATA*>(lvItem.lParam)->m_lParam ==
plvfi->lParam) return nItem;
}
return -1;
}
else
return m_pListCtrl->Default();
}
/*** The attributes of a column will be retrieved ****************************/
LRESULT CListBase::OnGetColumn(WPARAM wParam, LPARAM lParam)
{
int nColumn = static_cast<int>(wParam);
if (nColumn < 0 || nColumn >= m_aColumnData.GetSize()) return FALSE;
LVCOLUMN* pLVColumn = reinterpret_cast<LVCOLUMN*>(lParam);
if (m_aColumnData[nColumn]->m_bEnabled && m_aColumnData[nColumn]->m_bVisible)
{
LRESULT lResult = m_pListCtrl->DefWindowProc(LVM_GETCOLUMN,
GetPhysicalIndex(nColumn),
lParam);
if (lResult)
{
// adjust index and order
if (pLVColumn->mask & LVCF_ORDER)
pLVColumn->iOrder = m_aColumnData[nColumn]->m_nOrder;
if (pLVColumn->mask & LVCF_SUBITEM) pLVColumn->iSubItem = nColumn;
}
return lResult;
}
else
{
LVCOLUMN* pLVColumnSrc = m_aColumnData[nColumn]->m_pLVColumn;
// retrieve column attributes from remembered column data
if (pLVColumn->mask & LVCF_FMT)
if (pLVColumnSrc->mask & LVCF_FMT)
pLVColumn->fmt = pLVColumnSrc->fmt;
else
pLVColumn->fmt = LVCFMT_LEFT;
if (pLVColumn->mask & LVCF_IMAGE)
if (pLVColumnSrc->mask & LVCF_IMAGE)
pLVColumn->iImage = pLVColumnSrc->iImage;
else
pLVColumn->iImage = -1;
if (pLVColumn->mask & LVCF_ORDER)
pLVColumn->iOrder = m_aColumnData[nColumn]->m_nOrder;
if (pLVColumn->mask & LVCF_SUBITEM) pLVColumn->iSubItem = nColumn;
if (pLVColumn->mask & LVCF_TEXT &&
pLVColumn->pszText && pLVColumn->cchTextMax > 0)
if (pLVColumnSrc->mask & LVCF_TEXT)
if (_tcslen(pLVColumnSrc->pszText) >=
static_cast<size_t>(pLVColumn->cchTextMax - 1))
{
_tcsncpy(pLVColumn->pszText, pLVColumnSrc->pszText,
pLVColumn->cchTextMax - 1);
pLVColumn->pszText[pLVColumn->cchTextMax - 1] = _T('\0');
}
else
_tcscpy(pLVColumn->pszText, pLVColumnSrc->pszText);
else
*pLVColumn->pszText = _T('\0');
if (pLVColumn->mask & LVCF_WIDTH)
pLVColumn->cx = m_aColumnData[nColumn]->m_nWidth;
return TRUE;
}
}
/*** The current left-to-right order of columns will be retrieved ************/
LRESULT CListBase::OnGetColumnOrderArray(WPARAM wParam, LPARAM lParam)
{
INT_PTR nCount = wParam;
if (nCount <= 0) return 0;
INT_PTR nColumnCount = m_aColumnData.GetSize();
if (nCount > nColumnCount) nCount = nColumnCount;
LPINT pArray = reinterpret_cast<LPINT>(lParam);
for (int i = 0; i < nCount; ++i)
for (int j = 0; j < nColumnCount; ++j)
if (m_aColumnData[j]->m_nOrder == i) pArray[i] = j;
return nCount;
}
/*** The width of a column will be retrieved *********************************/
LRESULT CListBase::OnGetColumnWidth(WPARAM wParam)
{
int nColumn = static_cast<int>(wParam);
if (nColumn < 0 || nColumn >= m_aColumnData.GetSize()) return 0;
if (m_aColumnData[nColumn]->m_bEnabled && m_aColumnData[nColumn]->m_bVisible)
return m_pListCtrl->DefWindowProc(LVM_GETCOLUMNWIDTH,
GetPhysicalIndex(nColumn), 0);
else
// invisible column returns 0
return 0;
}
/*** Information needed to display an item will be requested *****************/
BOOL CListBase::OnGetdispinfo(NMHDR* pNMHDR)
{
if (m_bOnGetDispinfo) return FALSE;
m_bOnGetDispinfo = true;
LVITEM* pLVItem = &reinterpret_cast<NMLVDISPINFO*>(pNMHDR)->item;
bool bNotifyParent = true;
int iOrigSubItem = pLVItem->iSubItem;
pLVItem->iSubItem = GetLogicalIndex(iOrigSubItem);
LPARAM origLParam = pLVItem->lParam;
LVITEM* pLVItemStored =
origLParam ?
reinterpret_cast<ITEM_DATA*>(origLParam)->m_apLVItem[pLVItem->iSubItem] :
0;
if (!(m_pListCtrl->GetStyle() & LVS_OWNERDATA) && pLVItem->mask & LVIF_TEXT)
if (pLVItemStored &&
pLVItemStored->mask & LVIF_TEXT &&
pLVItemStored->pszText != LPSTR_TEXTCALLBACK)
{
pLVItem->pszText = pLVItemStored->pszText;
bNotifyParent = false;
}
if (bNotifyParent)
{
// notify parent window
CWnd* pwndParent = m_pListCtrl->GetParent();
if (pwndParent)
{
if (pLVItemStored) pLVItem->lParam = pLVItemStored->lParam;
pwndParent->SendMessage(WM_NOTIFY, m_pListCtrl->GetDlgCtrlID(),
reinterpret_cast<LPARAM>(pNMHDR));
pLVItem->lParam = origLParam; // restore original lParam
}
}
// restore original subitem number
pLVItem->iSubItem = iOrigSubItem;
m_bOnGetDispinfo = false;
return TRUE;
}
/*** Some or all of an item's attributes will be retrieved *******************/
LRESULT CListBase::OnGetItem(LPARAM lParam)
{
LVITEM* pLVItem = reinterpret_cast<LVITEM*>(lParam);
int nColumn = pLVItem->iSubItem;
if (nColumn < 0 || nColumn >= m_aColumnData.GetSize() || pLVItem->iItem < 0)
return FALSE;
bool bVisible =
m_aColumnData[nColumn]->m_bEnabled && m_aColumnData[nColumn]->m_bVisible;
UINT origMask = pLVItem->mask;
if (bVisible)
pLVItem->iSubItem = GetPhysicalIndex(nColumn);
else
{
if (pLVItem->mask & ~LVIF_STATE) pLVItem->mask |= LVIF_PARAM;
pLVItem->mask &= LVIF_STATE | LVIF_PARAM;
pLVItem->iSubItem = 0;
}
LRESULT lResult = m_pListCtrl->Default();
pLVItem->mask = origMask;
pLVItem->iSubItem = nColumn;
if (lResult && (!bVisible || pLVItem->mask != LVIF_STATE))
{
ITEM_DATA* pItemData = reinterpret_cast<ITEM_DATA*>(pLVItem->lParam);
if (pLVItem->mask & LVIF_PARAM) pLVItem->lParam = pItemData->m_lParam;
// column visible and only lParam (and state) shall be retrieved
// --> all has been done!
if (bVisible) return lResult;
LVITEM* pLVItemSrc = pItemData->m_apLVItem[nColumn];
// retrieve item attributes from remembered item data (except state)
if (pLVItem->mask & LVIF_IMAGE)
if (pLVItemSrc && pLVItemSrc->mask & LVIF_IMAGE)
pLVItem->iImage = pLVItemSrc->iImage;
else
pLVItem->iImage = 0;
if (pLVItem->mask & LVIF_INDENT)
if (pLVItemSrc && pLVItemSrc->mask & LVIF_INDENT)
pLVItem->iIndent = pLVItemSrc->iIndent;
else
pLVItem->iIndent = 0;
if (pLVItem->mask & LVIF_TEXT &&
pLVItem->pszText && pLVItem->cchTextMax > 0)
if (pLVItemSrc && pLVItemSrc->mask & LVIF_TEXT)
if (_tcslen(pLVItemSrc->pszText) >
static_cast<size_t>(pLVItem->cchTextMax - 1))
{
_tcsncpy(pLVItem->pszText, pLVItemSrc->pszText,
pLVItem->cchTextMax - 1);
pLVItem->pszText[pLVItem->cchTextMax - 1] = _T('\0');
}
else
_tcscpy(pLVItem->pszText, pLVItemSrc->pszText);
else
*pLVItem->pszText = _T('\0');
}
return lResult;
}
/*** The rectangle bounding an item will be retrieved ************************/
LRESULT CListBase::OnGetItemRect(WPARAM wParam, LPARAM lParam)
{
CRect* pRc = reinterpret_cast<CRect*>(lParam);
switch (pRc->left)
{
// rectangle bounding whole item
case LVIR_BOUNDS:
return m_pListCtrl->Default();
// rectangle bounding small icon
case LVIR_ICON:
// rectangle bounding label
case LVIR_LABEL:
if (m_bKeepLabelLeft)
return
m_pListCtrl->GetSubItemRect(
static_cast<int>(wParam), GetLogicalIndex(OrderToIndex(0)),
pRc->left, *pRc);
else
return m_pListCtrl->Default();
default:
ASSERT(false);
break;
}
return 0L;
}
/*** The text of an item or subitem will be retrieved ************************/
LRESULT CListBase::OnGetItemText(WPARAM wParam, LPARAM lParam)
{
INT_PTR nItem = wParam;
LVITEM* pLVItem = reinterpret_cast<LVITEM*>(lParam);
int nColumn = pLVItem->iSubItem;
if (nColumn < 0 || nColumn >= m_aColumnData.GetSize() || nItem < 0)
return FALSE;
if (m_aColumnData[nColumn]->m_bEnabled && m_aColumnData[nColumn]->m_bVisible)
{
pLVItem->iSubItem = GetPhysicalIndex(nColumn);
LRESULT lResult =
m_pListCtrl->DefWindowProc(LVM_GETITEMTEXT, wParam, lParam);
pLVItem->iSubItem = nColumn;
return lResult;
}
else
{
if (m_pListCtrl->GetStyle() & LVS_OWNERDATA) return 0;
LVITEM lvItem = {LVIF_PARAM, static_cast<int>(nItem), 0};
if (m_pListCtrl->DefWindowProc(
LVM_GETITEM, 0, reinterpret_cast<LPARAM>(&lvItem)))
{
LVITEM* pLVItemSrc =
reinterpret_cast<ITEM_DATA*>(lvItem.lParam)->m_apLVItem[nColumn];
// retrieve text from remembered item
if (pLVItemSrc &&
pLVItemSrc->mask & LVIF_TEXT &&
pLVItemSrc->pszText &&
pLVItem->pszText &&
pLVItem->cchTextMax > 0)
if (_tcslen(pLVItemSrc->pszText) >
static_cast<size_t>(pLVItem->cchTextMax - 1))
{
_tcsncpy(
pLVItem->pszText, pLVItemSrc->pszText, pLVItem->cchTextMax - 1);
pLVItem->pszText[pLVItem->cchTextMax - 1] = _T('\0');
return pLVItem->cchTextMax - 1;
}
else
{
_tcscpy(pLVItem->pszText, pLVItemSrc->pszText);
return _tcslen(pLVItem->pszText);
}
else
{
*pLVItem->pszText = _T('\0');
return 0;
}
}
else
return 0;
}
}
/*** Calculates the bounding rectangle of a subitem **************************/
LRESULT CListBase::OnGetSubItemRect(WPARAM wParam, LPARAM lParam)
{
CRect* pRc = reinterpret_cast<CRect*>(lParam);
int iSubItem = pRc->top;
ASSERT(iSubItem >= 0);
// An invisible subitem has no bounding rectangle
if (!m_aColumnData[iSubItem]->m_bEnabled ||
!m_aColumnData[iSubItem]->m_bVisible) return 0L;
int iItem = static_cast<int>(wParam);
int iPhysicalSubItem = GetPhysicalIndex(iSubItem);
switch (pRc->left)
{
// rectangle bounding whole subitem
case LVIR_BOUNDS:
{
pRc->top = iPhysicalSubItem;
if (m_pListCtrl->Default())
{
// adjust width of bounding rectangle because LVM_GETSUBITEMRECT
// doesn't always produce the correct result
pRc->right = pRc->left + m_pListCtrl->GetColumnWidth(iSubItem);
if (iPhysicalSubItem == 0)
{
CRect rc(LVIR_LABEL, 0, 0, 0);
if (m_pListCtrl->DefWindowProc(
LVM_GETSUBITEMRECT, wParam, reinterpret_cast<LPARAM>(&rc)))
{
pRc->right = rc.right;
int iOrder = IndexToOrder(0);
if (iOrder > 0)
{
// The left edge of subitem 0 is identical with the right edge of
// the subitem left of subitem 0.
if (m_pListCtrl->GetSubItemRect(
iItem, GetLogicalIndex(OrderToIndex(iOrder - 1)),
LVIR_BOUNDS, rc))
{
pRc->left = rc.right;
return 1L;
}
}
else
return 1L;
}
}
else
return 1L;
}
else
// in case of error restore original subitem number
pRc->top = iSubItem;
break;
}
// rectangle bounding small icon
case LVIR_ICON:
{
CRect rcIcon(*pRc);
rcIcon.top = iPhysicalSubItem;
if (m_pListCtrl->DefWindowProc(
LVM_GETSUBITEMRECT, wParam, reinterpret_cast<LPARAM>(&rcIcon)))
{
CRect rcSubItem; // rectangle bounding subitem
if (m_pListCtrl->GetSubItemRect(
iItem, iSubItem, LVIR_BOUNDS, rcSubItem))
if (m_bKeepLabelLeft &&
iSubItem > 0 &&
IndexToOrder(iPhysicalSubItem) == 0)
{
CRect rcIcon0(LVIR_ICON, 0, 0, 0);
CRect rcSubItem0;
if (m_pListCtrl->DefWindowProc (
LVM_GETSUBITEMRECT, wParam,
reinterpret_cast<LPARAM>(&rcIcon0)) &&
m_pListCtrl->GetSubItemRect(iItem, 0, LVIR_BOUNDS, rcSubItem0))
{
int nSmallIconXOff = __max(rcIcon0.left - rcSubItem0.left, 4);
rcIcon.left += nSmallIconXOff;
rcIcon.right = rcIcon.left + rcIcon0.Width();
// clip rectangle at right edge if necessary
INT_PTR nWidth =
m_pListCtrl->DefWindowProc(
LVM_GETCOLUMNWIDTH, iPhysicalSubItem, 0);
if (nSmallIconXOff + rcIcon.Width() >= nWidth)
rcIcon.right = rcIcon.left - nSmallIconXOff + nWidth - 1;
*pRc = rcIcon;
return 1L;
}
}
else if (iSubItem == 0 ||
!m_bKeepLabelLeft &&
m_dwExtendedStyle & LVS_EX_SUBITEMIMAGES)
{
// Does this subitem have a small icon being displayed?
LVITEM item = {LVIF_IMAGE, iItem, iPhysicalSubItem};
if (m_bAlwaysGetSmallIconRect ||
m_pListCtrl->DefWindowProc(
LVM_GETITEM, 0, reinterpret_cast<LPARAM>(&item)) &&
item.iImage > 0)
// whole icon or part of it visible?
if (rcIcon.left <= rcSubItem.right)
{
// clip rectangle at right edge if necessary
if (rcIcon.right > rcSubItem.right) rcIcon.right = rcSubItem.right;
*pRc = rcIcon;
return 1L;
}
}
}
break;
}
// rectangle bounding label
case LVIR_LABEL:
{
if (iSubItem == 0 && !m_bKeepLabelLeft ||
IndexToOrder(iPhysicalSubItem) == 0 && m_bKeepLabelLeft)
{
RECT rcLabel = {LVIR_LABEL, 0};// rectangle bounding label of subitem 0
if (m_pListCtrl->DefWindowProc(
LVM_GETSUBITEMRECT, wParam, reinterpret_cast<LPARAM>(&rcLabel)))
{
CRect rcSubItem0; // rectangle bounding subitem 0
if (m_pListCtrl->GetSubItemRect(iItem, 0, LVIR_BOUNDS, rcSubItem0))
if (IndexToOrder(0) > 0 && m_bKeepLabelLeft)
{
if (m_pListCtrl->GetSubItemRect(
iItem, GetLogicalIndex(OrderToIndex(0)), LVIR_BOUNDS, *pRc))
{
pRc->left += rcLabel.left - rcSubItem0.left;
return 1L;
}
}
else
{
*pRc = rcLabel;
return 1L;
}
}
}
else
if (m_pListCtrl->GetSubItemRect(iItem, iSubItem, LVIR_BOUNDS, *pRc))
{
CRect rcIcon;
if (m_dwExtendedStyle & LVS_EX_SUBITEMIMAGES &&
m_pListCtrl->GetSubItemRect(iItem, iSubItem, LVIR_ICON, rcIcon))
pRc->left += rcIcon.Width() + 2;
return 1L;
}
break;
}
default:
ASSERT(false);
break;
}
return 0L;
}
/*** Determines which list view item, if any, is at a specified position *****/
int CListBase::OnHitTest(LPARAM lParam)
{
if (m_pListCtrl->Default() != -1)
{
LPLVHITTESTINFO pHitTestInfo = reinterpret_cast<LPLVHITTESTINFO>(lParam);
if (pHitTestInfo->iItem >= 0)
if (pHitTestInfo->flags & LVHT_ONITEM)
{
CRect rc;
// Determine subitem
for (pHitTestInfo->iSubItem = GetColumnCount() - 1;
pHitTestInfo->iSubItem >= 0;
--pHitTestInfo->iSubItem)
if (m_pListCtrl->GetSubItemRect(
pHitTestInfo->iItem, pHitTestInfo->iSubItem, LVIR_BOUNDS, rc)
&&
rc.PtInRect(pHitTestInfo->pt)) break;
if (pHitTestInfo->iSubItem == -1)
pHitTestInfo->flags = LVHT_NOWHERE;
else if (GetLabelRect(pHitTestInfo->iItem, pHitTestInfo->iSubItem, rc)
&&
rc.PtInRect(pHitTestInfo->pt))
pHitTestInfo->flags = LVHT_ONITEMLABEL;
else if (m_pListCtrl->GetSubItemRect(
pHitTestInfo->iItem, pHitTestInfo->iSubItem, LVIR_ICON, rc)
&&
rc.PtInRect(pHitTestInfo->pt))
pHitTestInfo->flags = LVHT_ONITEMICON;
else if (GetStateIconRect(pHitTestInfo->iItem, rc) &&
rc.PtInRect(pHitTestInfo->pt))
pHitTestInfo->flags = LVHT_ONITEMSTATEICON;
else if (m_pListCtrl->GetItemRect(
pHitTestInfo->iItem, rc, LVIR_BOUNDS))
if (!rc.PtInRect(pHitTestInfo->pt) ||
// The small area left from state icon is LVHT_NOWHERE!!!
rc.left > -m_iIconXOff &&
pHitTestInfo->pt.x < __min(m_iIconXOff+rc.left, m_iIconXOff))
pHitTestInfo->flags = LVHT_NOWHERE;
else
pHitTestInfo->flags = LVHT_ONITEM;
else
pHitTestInfo->flags = LVHT_ONITEM;
}
return pHitTestInfo->iItem;
}
else
return -1;
}
/*** List control has been scrolled horizontally *****************************/
void CListBase::OnHScroll()
{
m_pListCtrl->Default ();
InvalidateNonItemArea();
}
/*** A new column will be inserted *******************************************/
LRESULT CListBase::OnInsertColumn(WPARAM wParam, LPARAM lParam)
{
int nColumn = static_cast<int>(wParam);
INT_PTR nColumnCount = m_aColumnData.GetSize();
if (nColumn < 0 || nColumn > nColumnCount) return -1;
LVCOLUMN* pLVColumn = DupLVColumn(reinterpret_cast<LVCOLUMN*>(lParam));
// adjust column attributes according to the real physical conditions
pLVColumn->mask &= ~LVCF_SUBITEM; // mask unnecessary subitem number
if (pLVColumn->mask & LVCF_ORDER)
{
ASSERT(pLVColumn->iOrder >= 0 && pLVColumn->iOrder <= nColumnCount);
pLVColumn->iOrder = GetPhysicalOrder(pLVColumn->iOrder);
}
LRESULT lResult =
m_pListCtrl->DefWindowProc(LVM_INSERTCOLUMN, GetPhysicalIndex(nColumn),
reinterpret_cast<LPARAM>(pLVColumn));
if (lResult != -1)
{
if (nColumn == 0)
// Rejustify first column of listview control to enable a right-justified
// or centerd first column
JustifyFirstColumn(reinterpret_cast<LVCOLUMN*>(lParam)->fmt);
// create and fill column administration structure
COLUMN_DATA* pColumnData = new COLUMN_DATA;
pLVColumn->mask |= LVCF_WIDTH;
pLVColumn->cx = 0;
pColumnData->m_pLVColumn = pLVColumn;
pColumnData->m_nOrder = pLVColumn->mask & LVCF_ORDER ?
reinterpret_cast<LVCOLUMN*>(lParam)->iOrder :
nColumn;
// adjust column order
for (int i = 0; i < nColumnCount; ++i)
if (m_aColumnData[i]->m_nOrder >= pColumnData->m_nOrder)
++m_aColumnData[i]->m_nOrder;
// store column administration structure
m_aColumnData.InsertAt(nColumn, pColumnData);
if (m_iSortColumn)
// adjust index of sort column
if (m_iSortColumn < 0)
{
if (nColumn < -m_iSortColumn) --m_iSortColumn;
}
else
if (nColumn < m_iSortColumn) ++m_iSortColumn;
// insert subitem placeholders belonging to column
for (int i =
static_cast<int>(
m_pListCtrl->DefWindowProc(LVM_GETITEMCOUNT, 0, 0));
--i >= 0;)
{
LVITEM lvItem = {LVIF_PARAM, i, 0};
if (m_pListCtrl->DefWindowProc(
LVM_GETITEM, 0, reinterpret_cast<LPARAM>(&lvItem)))
reinterpret_cast<ITEM_DATA*>(lvItem.lParam)->m_apLVItem.InsertAt(
nColumn, 0, 1);
}
}
else
{
delete[] pLVColumn->pszText;
delete pLVColumn;
}
return lResult;
}
/*** A new item will be inserted *********************************************/
LRESULT CListBase::OnInsertItem(LPARAM lParam)
{
LVITEM* pLVItemSrc = reinterpret_cast<LVITEM*>(lParam);
if (pLVItemSrc->iItem < 0) return -1;
LVITEM* pLVItem = DupLVItem(pLVItemSrc);
// save lParam in administration data
ITEM_DATA* pItemData = new ITEM_DATA;
if (pLVItemSrc->mask & LVIF_PARAM) pItemData->m_lParam = pLVItem->lParam;
// ^administration data is physical lParam
pLVItem->lParam = reinterpret_cast<LPARAM>(pItemData);
pLVItem->mask |= LVIF_PARAM;
// physical text is always LPSTR_TEXTCALLBACK
LPTSTR pszOrigText = pLVItem->pszText;
if (pLVItemSrc->mask & LVIF_TEXT &&
pLVItemSrc->pszText != LPSTR_TEXTCALLBACK &&
!(m_pListCtrl->GetStyle() & (LVS_SORTASCENDING | LVS_SORTDESCENDING)))
pLVItem->pszText = LPSTR_TEXTCALLBACK;
LRESULT lResult =
m_pListCtrl->DefWindowProc(LVM_INSERTITEM, 0,
reinterpret_cast<LPARAM>(pLVItem));
if (lResult != -1)
{
// correct item data
pLVItem->mask = pLVItemSrc->mask;
pLVItem->iItem = static_cast<int>(lResult);
pLVItem->pszText = pszOrigText;
// insert item into administration data for column 0
pItemData->m_apLVItem.InsertAt(0, pLVItem);
// insert placeholders for other columns
INT_PTR nColumnCount = m_aColumnData.GetSize();
for (int nColumn = 1; nColumn < nColumnCount; ++nColumn)
pItemData->m_apLVItem.InsertAt(nColumn, 0, 1);
RefreshToolTips();
}
else
{
if (pLVItemSrc->pszText != LPSTR_TEXTCALLBACK) delete[] pszOrigText;
delete pLVItem;
delete pItemData;
}
return lResult;
}
/*** A key has been pressed *************************************************/
void CListBase::OnKeyDown(UINT nChar)
{
switch (nChar)
{
// Space
case VK_SPACE:
{
// Space --> toggle checkbox state of all selected items
int nItem = m_pListCtrl->GetNextItem(-1, LVNI_FOCUSED);
if (m_dwExtendedStyle & LVS_EX_CHECKBOXES && nItem >= 0)
{
BOOL bChecked = !m_pListCtrl->GetCheck(nItem);
m_pListCtrl->SetCheck(nItem, bChecked);
for (POSITION pos = m_pListCtrl->GetFirstSelectedItemPosition(); pos;)
m_pListCtrl->SetCheck(
m_pListCtrl->GetNextSelectedItem(pos), bChecked);
}
else
m_pListCtrl->Default();
break;
}
// Numpad-Plus
case VK_ADD:
// Ctrl-Numpad-Plus --> set optimum width for all columns
if (m_bControl && m_bKeepLabelLeft && IndexToOrder(0) > 0)
{
LVCOLUMN lvc;
lvc.mask = LVCF_FMT;
int iIndex;
for (int nColumn = 0;
m_pListCtrl->DefWindowProc(
LVM_GETCOLUMN, iIndex = OrderToIndex(nColumn),
reinterpret_cast<LPARAM>(&lvc));
++nColumn)
{
int iLogicalIndex = GetLogicalIndex(iIndex);
int iOptWidth;
if (iIndex == 0 || nColumn == 0)
{
// calculate needed column width
iOptWidth = 0;
for (int iItem = m_pListCtrl->GetItemCount(); --iItem >= 0;)
{
int iWidth =
m_pListCtrl->GetStringWidth(
m_pListCtrl->GetItemText(iItem, iLogicalIndex));
if (iWidth > iOptWidth) iOptWidth = iWidth;
}
if (iIndex > 0)
{
// add space for state icon and small icon
CRect rcSubItem;
if (m_pListCtrl->GetSubItemRect(
0, iLogicalIndex, LVIR_BOUNDS, rcSubItem))
{
CRect rcLabel;
if (m_pListCtrl->GetSubItemRect(
0, iLogicalIndex, LVIR_LABEL, rcLabel))
iOptWidth += rcLabel.left - rcSubItem.left;
}
// add left offset
iOptWidth += m_iFirstColXOff;
}
else
// add left offset
iOptWidth += m_iNextColXOff;
// add right offset
iOptWidth += m_iNextColXOff;
}
else
iOptWidth = LVSCW_AUTOSIZE;
m_pListCtrl->SetColumnWidth(iLogicalIndex, iOptWidth);
}
}
else
m_pListCtrl->Default();
break;
// Ctrl
case VK_CONTROL:
if (m_bKeepLabelLeft && IndexToOrder(0) > 0)
m_bControl = true;
else
m_pListCtrl->Default();
break;
// All other keys
default:
m_pListCtrl->Default();
break;
}
}
/*** A key has been released *************************************************/
void CListBase::OnKeyUp(UINT nChar)
{
if (nChar == VK_CONTROL) m_bControl = false;
m_pListCtrl->Default();
}
/*** List control loses input focus ******************************************/
void CListBase::OnKillFocus()
{
m_pListCtrl->Default();
// manually remove focus state so that custom drawing will function properly
int nItem = m_pListCtrl->GetNextItem(-1, LVNI_SELECTED);
if (nItem >= 0) m_pListCtrl->SetItemState(nItem, 0, LVIS_FOCUSED);
// Reset current tooltip item
m_bLabelUnfolded = FALSE;
m_fCurrentFlags = 0;
m_iCurrentItem = -1;
m_iCurrentSubItem = -1;
}
/*** The user double-clicks the left mouse button ****************************/
void CListBase::OnLButtonDblClk(CPoint point)
{
UINT flags = 0;
int nItem = m_pListCtrl->HitTest(point, &flags);
if (nItem >= 0 &&
m_dwExtendedStyle & LVS_EX_CHECKBOXES &&
flags == LVHT_ONITEMSTATEICON)
{
BOOL bChecked = !m_pListCtrl->GetCheck(nItem);
if (m_pListCtrl->GetItemState(nItem, LVIS_SELECTED))
for (POSITION pos = m_pListCtrl->GetFirstSelectedItemPosition(); pos;)
m_pListCtrl->SetCheck(
m_pListCtrl->GetNextSelectedItem(pos), bChecked);
else
m_pListCtrl->SetCheck(nItem, bChecked);
}
else
m_pListCtrl->Default();
}
/*** The user presses the left mouse button **********************************/
void CListBase::OnLButtonDown(CPoint point)
{
if (m_hCursorCustom) SetCursor(m_hCursorCustom);
LVHITTESTINFO info = {{point.x, point.y}};
int nItem = m_pListCtrl->SubItemHitTest(&info);
if (nItem >= 0 &&
m_dwExtendedStyle & LVS_EX_CHECKBOXES &&
info.flags == LVHT_ONITEMSTATEICON)
{
BOOL bChecked = !m_pListCtrl->GetCheck(nItem);
if (m_pListCtrl->GetItemState(nItem, LVIS_SELECTED))
for (POSITION pos = m_pListCtrl->GetFirstSelectedItemPosition(); pos;)
m_pListCtrl->SetCheck(m_pListCtrl->GetNextSelectedItem(pos), bChecked);
else
m_pListCtrl->SetCheck(nItem, bChecked);
}
else
m_pListCtrl->Default();
SetHotCursorAndItem(&info);
}
/*** The user releases the left mouse button *********************************/
void CListBase::OnLButtonUp()
{
if (m_pListCtrl->GetStyle() & LVS_OWNERDRAWFIXED)
if (m_hCursorCustom) SetCursor(m_hCursorCustom);
m_pListCtrl->Default();
}
/*** The mouse cursor leaves the client area of this list view control *******/
LRESULT CListBase::OnMouseLeave()
{
m_bMouseInClientArea = false;
POINT pt;
if (GetCursorPos(&pt))
{
// Check whether the mouse cursor is hovering over the client area of this
// list view control
m_pListCtrl->ScreenToClient(&pt);
BOOL bMouseInClientRect = m_pListCtrl->HitTest(pt) != -1;
if (bMouseInClientRect)
{
// Check whether the mouse cursor is hovering over the header control
CHeaderCtrl* pHdrCtrl = m_pListCtrl->GetHeaderCtrl();
if (pHdrCtrl)
{
CRect rc;
pHdrCtrl->GetClientRect(rc);
bMouseInClientRect = !rc.PtInRect(pt);
}
}
if (!bMouseInClientRect)
{
// Reset current tooltip item if the mouse cursor has left the client
// rectangle of this list control
m_bLabelUnfolded = FALSE;
m_fCurrentFlags = 0;
m_iCurrentItem = -1;
m_iCurrentSubItem = -1;
m_pLabelTip->Activate(FALSE);
}
}
if (m_dwExtendedStyle &
(LVS_EX_UNDERLINEHOT | LVS_EX_ONECLICKACTIVATE |
LVS_EX_TWOCLICKACTIVATE))
{
int iOldHotItem;
if (m_pListCtrl->GetStyle() & LVS_OWNERDRAWFIXED)
{
iOldHotItem = m_iHotItem;
m_iHotItem = -1;
}
else
iOldHotItem = m_pListCtrl->SetHotItem(-1);
// Redraw the previous hot item because the list view control doesn't do it
// always (depending of the position where the mouse cursor has left the
// client area).
if (iOldHotItem >= 0)
{
RECT rc;
m_pListCtrl->GetItemRect (iOldHotItem, &rc, LVIR_BOUNDS);
m_pListCtrl->InvalidateRect(&rc, FALSE);
}
}
if (m_hTheme && m_pListCtrl->GetStyle() & LVS_OWNERDRAWFIXED)
{
if (m_iItemUnderCursor >= 0)
{
RECT rc;
m_pListCtrl->GetItemRect (m_iItemUnderCursor, &rc, LVIR_BOUNDS);
m_pListCtrl->InvalidateRect(&rc, FALSE);
}
m_iItemUnderCursor = -1;
}
return m_pListCtrl->Default();
}
/*** The mouse has been moved ************************************************/
void CListBase::OnMouseMove(CPoint point)
{
m_bRefreshToolTips = false;
m_pListCtrl->Default();
// Set tooltip's WS_EX_TOPMOST style
if (m_bFocusSet)
{
m_bFocusSet = false;
if ((GetWindowLong(m_pListCtrl->GetTopLevelParent()->m_hWnd, GWL_EXSTYLE) &
WS_EX_TOPMOST) != 0)
{
if (!m_bTopMost)
{
m_pLabelTip->SetWindowPos(
&CWnd::wndTopMost, 0, 0, 0, 0,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
m_pToolTip->SetWindowPos(
&CWnd::wndTopMost, 0, 0, 0, 0,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
m_bTopMost = true;
}
}
else
if (m_bTopMost)
{
m_pLabelTip->SetWindowPos(
&CWnd::wndNoTopMost, 0, 0, 0, 0,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
m_pToolTip->SetWindowPos(
&CWnd::wndNoTopMost, 0, 0, 0, 0,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
m_bTopMost = false;
}
}
CRect rc;
m_pListCtrl->GetClientRect(rc);
if (!rc.PtInRect(point))
{
#ifndef NDEBUG
ATLTRACE2(ListCtrlEx, 1, _T("Client area left\n"));
#endif
// Reset current tooltip item if the mouse cursor has left the client area
m_bLabelUnfolded = FALSE;
m_fCurrentFlags = 0;
m_iCurrentItem = -1;
m_iCurrentSubItem = -1;
m_pLabelTip->Activate(FALSE);
m_pToolTip->Activate (FALSE);
return;
}
if (!m_bMouseInClientArea)
{
m_bMouseInClientArea = true;
// Fire an event when the mouse cursor leaves the client area of this list
// control
TRACKMOUSEEVENT eventTrack =
{sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_pListCtrl->m_hWnd};
_TrackMouseEvent(&eventTrack);
}
LVHITTESTINFO info = {{point.x, point.y}};
int nItem = m_pListCtrl->SubItemHitTest(&info);
SetHotCursorAndItem(&info);
// Reset current rooltip item if this application has been deactivated
if (GetActiveWindow() != 0)
m_bIsActive = true;
else
{
if (m_bIsActive)
{
m_bIsActive = false;
m_fCurrentFlags = 0;
m_iCurrentItem = -1;
m_iCurrentSubItem = -1;
}
return;
}
if (!m_bUpdateToolTips) return; // update of tooltips suspended
if (m_bLabelUnfolded &&
info.flags != 0 &&
nItem != -1 &&
info.iSubItem != -1 &&
nItem == m_iCurrentItem)
{
// check if cursor over unfolded label
m_pLabelTip->GetWindowRect(rc);
CPoint ptScreen(point);
m_pListCtrl->ClientToScreen(&ptScreen);
if (rc.PtInRect(ptScreen)) return;
}
// valid item?
if (nItem >= 0 && (m_bSubItemTips && info.iSubItem >= 0 || !m_bSubItemTips))
{
bool bItemChanged = nItem != m_iCurrentItem;
// item or subitem changed?
if (bItemChanged ||
info.iSubItem != m_iCurrentSubItem ||
info.flags != m_fCurrentFlags)
{
#ifndef NDEBUG
ATLTRACE2(
ListCtrlEx, 1, _T("Item = %d, subitem = %d, flags = %s\n"),
nItem, info.iSubItem,
info.flags == LVHT_NOWHERE ? _T("LVHT_NOWHERE") :
info.flags == LVHT_ONITEM ? _T("LVHT_ONITEM") :
info.flags == LVHT_ONITEMICON ? _T("LVHT_ONITEMICON") :
info.flags == LVHT_ONITEMLABEL ? _T("LVHT_ONITEMLABEL") :
info.flags == LVHT_ONITEMSTATEICON ? _T("LVHT_ONITEMSTATEICON") :
_T("<unknown>"));
#endif
m_bLabelUnfolded = FALSE;
m_strCurrentLabelTip.Empty();
if (m_bToolTips)
if (m_bSubItemTips)
{
// get tooltip for current subitem
m_strCurrentToolTip =
GetToolTip(nItem, info.iSubItem, info.flags, m_bLabelUnfolded);
if (m_bLabelUnfolded)
{
m_strCurrentLabelTip = m_strCurrentToolTip;
m_strCurrentToolTip.Empty();
}
}
else if (bItemChanged)
{
// get tooltip for current item
m_strCurrentToolTip = GetToolTip(nItem);
}
else
m_strCurrentToolTip.Empty();
if (m_strCurrentLabelTip.IsEmpty() &&
m_bUnfoldLabel &&
info.flags == LVHT_ONITEMLABEL)
{
// No tooltip displayed as label for subitem
// --> check if subitem text fits
if (m_pListCtrl->GetSubItemRect(nItem, info.iSubItem, LVIR_LABEL, rc))
{
m_strCurrentLabelTip =
m_pListCtrl->GetItemText(nItem, info.iSubItem);
if (m_pListCtrl->GetStringWidth(m_strCurrentLabelTip) +
(
info.iSubItem == 0 &&
!m_bKeepLabelLeft ||
IndexToOrder(GetPhysicalIndex(info.iSubItem)) == 0 &&
m_bKeepLabelLeft ?
m_iFirstColXOff : m_iNextColXOff
) +
(
info.iSubItem == 0 &&
(!m_bKeepLabelLeft || IndexToOrder(0) == 0) ?
m_iFirstColXOff : m_iNextColXOff
) +
(m_bExplorerStyle ? m_iColumnSeparatorWidth : 0) > rc.Width())
m_bLabelUnfolded = TRUE;
else
{
m_strCurrentLabelTip.Empty();
m_bLabelUnfolded = FALSE;
}
}
}
m_iCurrentItem = info.iItem;
m_iCurrentSubItem = info.iSubItem;
m_fCurrentFlags = info.flags;
LPMSG pCurMsg = AfxGetCurrentMessage();
if (m_bLabelUnfolded)
{
m_pLabelTip->Activate (TRUE);
m_pLabelTip->UpdateTipText(m_strCurrentLabelTip, m_pListCtrl);
m_pLabelTip->RelayEvent (pCurMsg);
}
else
m_pLabelTip->Activate(FALSE);
if ((m_bSubItemTips || bItemChanged) && !m_strCurrentToolTip.IsEmpty())
{
m_iCurrentItem = nItem;
m_pToolTip->Activate(TRUE);
m_pToolTip->UpdateTipText(m_strCurrentToolTip, m_pListCtrl);
m_pToolTip->RelayEvent (pCurMsg);
if (m_bLabelUnfolded)
{
// display tool tip if cursor hovers over expanded label
m_pToolTip->UpdateTipText(m_strCurrentToolTip, m_pLabelTip);
MSG msg = *pCurMsg;
msg.hwnd = m_pLabelTip->m_hWnd;
m_pToolTip->RelayEvent(&msg);
}
}
else if (m_bSubItemTips || bItemChanged)
{
m_iCurrentItem = nItem;
m_pToolTip->Activate(FALSE);
}
}
}
else
{
if (m_bLabelUnfolded)
{
m_bLabelUnfolded = FALSE;
m_pLabelTip->Activate(FALSE);
}
else
m_pToolTip->Activate(FALSE);
m_fCurrentFlags = 0;
m_iCurrentItem = -1;
m_iCurrentSubItem = -1;
}
}
/*** The mouse wheel has been rotated ****************************************/
BOOL CListBase::OnMouseWheel(CPoint point)
{
BOOL bRet = m_pListCtrl->Default() != 0;
CRect rc;
m_pListCtrl->GetWindowRect(rc);
// Force WM_MOUSEMOVE message, if the cursor position is inside the client
// area of the list view control
if (rc.PtInRect(point))
{
m_bRefreshToolTips = true;
SetCursorPos(point.x, point.y);
}
return bRet;
}
/*** Divider in header control has been doubleclicked or dragged *************/
BOOL CListBase::OnNotify(LPARAM lParam)
{
BOOL bRet = FALSE;
LPNMHDR pNMHdr = reinterpret_cast<LPNMHDR>(lParam);
CHeaderCtrl* pHdrCtrl = m_pListCtrl->GetHeaderCtrl();
ASSERT(pHdrCtrl);
if (pHdrCtrl && pNMHdr->hwndFrom == pHdrCtrl->m_hWnd)
{
LPNMHEADER pNMHeader = reinterpret_cast<LPNMHEADER>(pNMHdr);
switch (pNMHdr->code)
{
case HDN_DIVIDERDBLCLICKW:
case HDN_DIVIDERDBLCLICKA:
if (m_bKeepLabelLeft &&
(
GetLogicalIndex(pNMHeader->iItem) > 0 &&
IndexToOrder(pNMHeader->iItem) == 0 ||
pNMHeader->iItem == 0 &&
IndexToOrder(pNMHeader->iItem) > 0
))
{
// calculate needed column width
int iFirstItem;
int iLastItem;
int iOptWidth = 0;
int iLogicalSubItem = GetLogicalIndex(pNMHeader->iItem);
if (m_pListCtrl->GetStyle() & LVS_OWNERDATA)
{
// In a virtual list view control
// at least the visible items and
// at most the cached items will be considered.
int iFirstVisibleItem = m_pListCtrl->GetTopIndex();
iFirstItem = __min(m_iFirstCachedItem, iFirstVisibleItem);
iLastItem =
__min(
__max(
m_iLastCachedItem,
iFirstVisibleItem + m_pListCtrl->GetCountPerPage()),
m_pListCtrl->GetItemCount() - 1);
}
else
{
iFirstItem = 0;
iLastItem = m_pListCtrl->GetItemCount() - 1;
}
for (int iItem = iFirstItem; iItem <= iLastItem; ++iItem)
{
int iWidth =
m_pListCtrl->GetStringWidth(
m_pListCtrl->GetItemText(iItem, iLogicalSubItem));
if (iWidth > iOptWidth) iOptWidth = iWidth;
}
if (GetLogicalIndex(pNMHeader->iItem) > 0)
{
// add space for state icon and small icon
CRect rcSubItem;
if (m_pListCtrl->GetSubItemRect(
0, iLogicalSubItem, LVIR_BOUNDS, rcSubItem))
{
CRect rcLabel;
if (m_pListCtrl->GetSubItemRect(
0, iLogicalSubItem, LVIR_LABEL, rcLabel))
iOptWidth += rcLabel.left - rcSubItem.left;
}
// add left offset
iOptWidth += m_iFirstColXOff;
}
else
// add left offset
iOptWidth += m_iNextColXOff;
// add right offset
iOptWidth += m_iNextColXOff;
m_pListCtrl->DefWindowProc(LVM_SETCOLUMNWIDTH, pNMHeader->iItem, iOptWidth);
bRet = TRUE;
}
break;
case HDN_ENDDRAG:
bRet = m_pListCtrl->Default() != 0;
if (pNMHeader->pitem->iOrder >= 0)
{
int nColumn = GetLogicalIndex(pNMHeader->iItem);
int nOrder = GetLogicalOrder(pNMHeader->pitem->iOrder);
if (nOrder < m_aColumnData[nColumn]->m_nOrder)
{
// adjust column order
for (INT_PTR i = m_aColumnData.GetUpperBound(); i >= 0; --i)
if (m_aColumnData[i]->m_nOrder >= nOrder &&
m_aColumnData[i]->m_nOrder <
m_aColumnData[nColumn]->m_nOrder)
++m_aColumnData[i]->m_nOrder;
m_aColumnData[nColumn]->m_nOrder = nOrder;
}
else if (nOrder > m_aColumnData[nColumn]->m_nOrder)
{
// adjust column order
for (INT_PTR i = m_aColumnData.GetUpperBound(); i >= 0; --i)
if (m_aColumnData[i]->m_nOrder >
m_aColumnData[nColumn]->m_nOrder &&
m_aColumnData[i]->m_nOrder <= nOrder)
--m_aColumnData[i]->m_nOrder;
m_aColumnData[nColumn]->m_nOrder = nOrder;
}
}
break;
case HDN_ENDTRACKW:
case HDN_ENDTRACKA:
case HDN_ITEMCHANGEDW:
case HDN_ITEMCHANGEDA:
bRet = m_pListCtrl->Default() != 0;
if (m_bColorSortColumn) InvalidateNonItemArea();
break;
case HDN_ITEMCLICK:
m_pListCtrl->Default();
// Suppress further processing of this message to prevent Windows from
// sending the notification message LVN_COLUMNCLICK a second time!
// For unknown reasons this is necessary in an Unicode environment
// only.
bRet = TRUE;
break;
default:
break;
}
}
return bRet;
}
/*** The contents of the display area of a virtual list view control have ****/
/*** been changed ****/
BOOL CListBase::OnODCacheHint(NMHDR* pNMHDR)
{
LPNMLVCACHEHINT pNMLVCacheHint = reinterpret_cast<LPNMLVCACHEHINT>(pNMHDR);
// remember caching area
m_iFirstCachedItem = pNMLVCacheHint->iFrom;
m_iLastCachedItem = pNMLVCacheHint->iTo;
return FALSE;
}
/*** Will be called when Windows or an application makes a request to ********/
/*** repaint a portion of the client area of this list view control ********/
void CListBase::OnPaint()
{
// first let the control do its default drawing.
m_pListCtrl->Default();
if (m_bColumnSeparators && !m_bBkImage)
{
// now get the client rect so we know the line length and when to stop
RECT rcClient;
m_pListCtrl->GetClientRect(&rcClient);
CClientDC dc(m_pListCtrl);
CPen penSeparator(PS_SOLID, 0, GetSysColor(COLOR_3DFACE));
// select new pen and save previous pen
CPen* ppenPrev = dc.SelectObject(&penSeparator);
// the border of the column is offset by the horz scroll
int iBorderX = -m_pListCtrl->GetScrollPos(SB_HORZ);
CHeaderCtrl* pHeaderCtrl = m_pListCtrl->GetHeaderCtrl();
int iItemCount = pHeaderCtrl->GetItemCount();
for (int iItem = 0; iItem < iItemCount; ++iItem)
{
CRect rcItem;
pHeaderCtrl->GetItemRect(OrderToIndex(iItem), rcItem);
// get the next border
iBorderX += rcItem.Width();
// if next border is outside client area, break out
if (iBorderX >= rcClient.right) break;
// Draw the line.
// The bottom of the header corresponds to the top of the line.
dc.MoveTo(iBorderX-1, rcItem.bottom);
dc.LineTo(iBorderX-1, rcClient.bottom);
}
// restore previous pen
dc.SelectObject(ppenPrev);
}
}
/*** The background color of the list view control will be set ***************/
LRESULT CListBase::OnSetBkColor(LPARAM lParam)
{
if (m_bBkImage)
if (lParam != CLR_NONE)
{
m_clrBkColor = static_cast<COLORREF>(lParam);
m_bBkColorKnown = true;
m_bFixedBkColor = true;
return TRUE;
}
if (m_pListCtrl->Default())
{
if (lParam != CLR_NONE)
{
m_clrBkColor = static_cast<COLORREF>(lParam);
m_bBkColorKnown = true;
if (m_bColorSortColumn)
{
// force recalculation of sort column color
m_bColorSortColumn = FALSE;
ColorSortColumn();
}
}
m_bFixedBkColor = true;
return TRUE;
}
else
return FALSE;
}
/*** An background image will be set *****************************************/
LRESULT CListBase::OnSetBkImage()
{
m_bBkImage = m_pListCtrl->Default() != 0;
if (m_bBkImage)
{
// Remember the current background color when a background image has been
// set. Then set the background color to none to correct a drawing error
// in the common control library < 6.0!
if (!m_bBkColorKnown)
{
m_clrBkColor = m_pListCtrl->GetBkColor();
m_bBkColorKnown = true;
}
bool bFixedBkColor = m_bFixedBkColor;
m_pListCtrl->SetBkColor(CLR_NONE);
m_bFixedBkColor = bFixedBkColor;
}
else
if (m_bBkColorKnown)
{
bool bFixedBkColor = m_bFixedBkColor;
m_pListCtrl->SetBkColor(m_clrBkColor);
m_bFixedBkColor = bFixedBkColor;
}
return m_bBkImage;
}
/*** The attributes of a column will be set **********************************/
LRESULT CListBase::OnSetColumn(WPARAM wParam, LPARAM lParam)
{
int nColumn = static_cast<int>(wParam);
INT_PTR nColumnCount = m_aColumnData.GetSize();
if (nColumn < 0 || nColumn >= nColumnCount) return FALSE;
LVCOLUMN* pLVColumn = DupLVColumn(reinterpret_cast<LVCOLUMN*>(lParam));
// adjust column attributes according to the real physical conditions
pLVColumn->mask &= ~LVCF_SUBITEM; // mask unnecessary subitem number
if (pLVColumn->mask & LVCF_ORDER)
{
ASSERT(pLVColumn->iOrder >= 0 && pLVColumn->iOrder < nColumnCount);
pLVColumn->iOrder = GetPhysicalOrder(pLVColumn->iOrder);
}
LRESULT lResult =
m_aColumnData[nColumn]->m_bEnabled && m_aColumnData[nColumn]->m_bVisible ?
m_pListCtrl->DefWindowProc(LVM_SETCOLUMN, GetPhysicalIndex(nColumn),
reinterpret_cast<LPARAM>(pLVColumn)) : TRUE;
if (lResult)
{
// update column data
LVCOLUMN* pLVColumnDst = m_aColumnData[nColumn]->m_pLVColumn;
if (pLVColumn->mask & LVCF_FMT) pLVColumnDst->fmt = pLVColumn->fmt;
if (pLVColumn->mask & LVCF_IMAGE) pLVColumnDst->iImage = pLVColumn->iImage;
if (pLVColumn->mask & LVCF_ORDER)
{
int nOrder = reinterpret_cast<LVCOLUMN*>(lParam)->iOrder;
if (nOrder < m_aColumnData[nColumn]->m_nOrder)
{
// adjust column order
for (int i = 0; i < nColumnCount; ++i)
if (m_aColumnData[i]->m_nOrder >= nOrder &&
m_aColumnData[i]->m_nOrder < m_aColumnData[nColumn]->m_nOrder)
++m_aColumnData[i]->m_nOrder;
m_aColumnData[nColumn]->m_nOrder = nOrder;
}
else if (nOrder > m_aColumnData[nColumn]->m_nOrder)
{
// adjust column order
for (int i = 0; i < nColumnCount; ++i)
if (m_aColumnData[i]->m_nOrder > m_aColumnData[nColumn]->m_nOrder &&
m_aColumnData[i]->m_nOrder <= nOrder)
--m_aColumnData[i]->m_nOrder;
m_aColumnData[nColumn]->m_nOrder = nOrder;
}
}
if (pLVColumn->mask & LVCF_TEXT && pLVColumn->pszText)
{
delete[] pLVColumnDst->pszText;
pLVColumnDst->pszText = new TCHAR[_tcslen(pLVColumn->pszText)+1];
_tcscpy(pLVColumnDst->pszText, pLVColumn->pszText);
}
pLVColumnDst->mask |= pLVColumn->mask;
}
delete[] pLVColumn->pszText;
delete pLVColumn;
return lResult;
}
/*** The left-to-right order of columns will be set **************************/
LRESULT CListBase::OnSetColumnOrderArray(WPARAM wParam, LPARAM lParam)
{
INT_PTR nCount = wParam;
if (nCount <= 0) return 0;
INT_PTR nColumnCount = m_aColumnData.GetSize();
if (nCount > nColumnCount) nCount = nColumnCount;
LPINT pArraySrc = reinterpret_cast<LPINT>(lParam);
LPINT pArrayDst = new int[nCount];
int n = 0;
for (int i = 0; i < nCount; ++i)
{
int nColumn = pArraySrc[i];
if (nColumn >= nColumnCount)
{
// illegal column index
delete[] pArrayDst;
return 0;
}
if (m_aColumnData[nColumn]->m_bEnabled &&
m_aColumnData[nColumn]->m_bVisible)
pArrayDst[n++] = GetPhysicalIndex(nColumn);
}
LRESULT lResult =
m_pListCtrl->DefWindowProc(
LVM_SETCOLUMNORDERARRAY, n, reinterpret_cast<LPARAM>(pArrayDst));
delete[] pArrayDst;
if (lResult)
// adjust column order
for (int i = 0; i < nCount; ++i)
{
int nColumn = pArraySrc[i];
if (i < m_aColumnData[nColumn]->m_nOrder)
{
for (int j = 0; j < nColumnCount; ++j)
if (m_aColumnData[j]->m_nOrder >= i &&
m_aColumnData[j]->m_nOrder < m_aColumnData[nColumn]->m_nOrder)
++m_aColumnData[j]->m_nOrder;
m_aColumnData[nColumn]->m_nOrder = i;
}
else if (i > m_aColumnData[nColumn]->m_nOrder)
{
for (int j = 0; j < nColumnCount; ++j)
if (m_aColumnData[j]->m_nOrder > m_aColumnData[nColumn]->m_nOrder &&
m_aColumnData[j]->m_nOrder <= i) --m_aColumnData[j]->m_nOrder;
m_aColumnData[nColumn]->m_nOrder = i;
}
}
return lResult;
}
/*** The width of a column will be changed ***********************************/
LRESULT CListBase::OnSetColumnWidth(WPARAM wParam, LPARAM lParam)
{
int nColumn = static_cast<int>(wParam);
if (nColumn < 0 || nColumn >= m_aColumnData.GetSize()) return FALSE;
if (m_aColumnData[nColumn]->m_bEnabled && m_aColumnData[nColumn]->m_bVisible)
return
m_pListCtrl->DefWindowProc(
LVM_SETCOLUMNWIDTH, GetPhysicalIndex(nColumn), lParam);
else
{
// remember new column width
m_aColumnData[nColumn]->m_nWidth = static_cast<int>(lParam);
return TRUE;
}
}
/*** The extended list view style will be set ********************************/
LRESULT CListBase::OnSetExtendedStyle(WPARAM wParam, LPARAM lParam)
{
LRESULT dwResult = m_dwExtendedStyle;
if (wParam == 0U) wParam = ~0U;
m_dwExtendedStyle = static_cast<DWORD>(wParam & lParam);
// Mask all style bits not supported by non-visual common control library
if (m_visualStyle == NotPresent)
m_dwExtendedStyle &= ~(LVS_EX_DOUBLEBUFFER | LVS_EX_SIMPLESELECT);
// Hide the bits LVS_EX_INFOTIP and LVS_EX_LABELTIP from Windows
DWORD dwExtendedStyle =
m_dwExtendedStyle & ~(LVS_EX_INFOTIP | LVS_EX_LABELTIP);
if (wParam & LVS_EX_INFOTIP)
m_bToolTips = (lParam & LVS_EX_INFOTIP) != 0;
if (wParam & LVS_EX_LABELTIP)
m_bUnfoldLabel = (lParam & LVS_EX_LABELTIP) != 0;
if (m_pListCtrl->GetStyle() & LVS_OWNERDRAWFIXED)
// Hide the bits LVS_EX_UNDERLINEHOT, LVS_EX_ONECLICKACTIVATE, and
// LVS_EX_TWOCLICKACTIVATE from Windows
if (wParam &
(LVS_EX_UNDERLINEHOT | LVS_EX_ONECLICKACTIVATE |
LVS_EX_TWOCLICKACTIVATE))
{
if (lParam &
(LVS_EX_UNDERLINEHOT | LVS_EX_ONECLICKACTIVATE |
LVS_EX_TWOCLICKACTIVATE))
PrepareHotUnderlining();
else
m_hCursorCustom = 0;
dwExtendedStyle &=
~(LVS_EX_UNDERLINEHOT | LVS_EX_ONECLICKACTIVATE |
LVS_EX_TWOCLICKACTIVATE);
}
else
m_hCursorCustom = 0;
m_pListCtrl->DefWindowProc(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwExtendedStyle);
return dwResult;
}
/*** List control gains input focus ******************************************/
void CListBase::OnSetFocus()
{
m_pListCtrl->Default();
// manually set focus state so that custom drawing will function properly
int nItem = m_pListCtrl->GetNextItem(-1, LVNI_SELECTED);
if (nItem >= 0) m_pListCtrl->SetItemState(nItem, LVIS_FOCUSED, LVIS_FOCUSED);
m_bFocusSet = true;
}
/*** Assigns an image list to a list control *********************************/
LRESULT CListBase::OnSetImageList(WPARAM wParam)
{
ASSERT(m_pListCtrl->GetHeaderCtrl());
ASSERT(wParam != LVSIL_NORMAL);
LRESULT dwResult = m_pListCtrl->Default();
if (wParam == LVSIL_SMALL)
{
CHeaderCtrl* pHeaderCtrl = m_pListCtrl->GetHeaderCtrl();
// restore image list with sort icons because default behavior is that the
// header control shares its image list with the small icon list of the
// list control
if (IsWindow(pHeaderCtrl->m_hWnd))
pHeaderCtrl->SetImageList(&m_imglstSortIcons);
}
return dwResult;
}
/*** Some or all of a list view items attributes will be set *****************/
LRESULT CListBase::OnSetItem(LPARAM lParam)
{
LVITEM* pLVItemSrc = reinterpret_cast<LVITEM*>(lParam);
int nColumn = pLVItemSrc->iSubItem;
if (nColumn < 0 || nColumn >= m_aColumnData.GetSize() ||
pLVItemSrc->iItem < 0) return FALSE;
LVITEM* pLVItem = DupLVItem(pLVItemSrc);
bool bVisible =
m_aColumnData[nColumn]->m_bEnabled && m_aColumnData[nColumn]->m_bVisible;
LRESULT lResult;
LPTSTR pszOrigText = pLVItem->pszText;
if (bVisible && pLVItemSrc->mask & ~LVIF_PARAM ||
nColumn == 0 && pLVItemSrc->mask & LVIF_STATE)
{
// column is visible and other attributes than lParam will be set
// or
// column zero and at least state will be set
// --> do it physically
pLVItem->mask &= ~LVIF_PARAM;
if (bVisible)
{
if (pLVItemSrc->mask & LVIF_TEXT &&
pLVItemSrc->pszText != LPSTR_TEXTCALLBACK &&
!(m_pListCtrl->GetStyle() &
(LVS_SORTASCENDING | LVS_SORTDESCENDING)))
pLVItem->pszText = LPSTR_TEXTCALLBACK;
}
else
pLVItem->mask &= LVIF_STATE;
if (pLVItem->mask)
{
pLVItem->iSubItem = GetPhysicalIndex(nColumn);
lResult =
m_pListCtrl->DefWindowProc(
LVM_SETITEM, 0, reinterpret_cast<LPARAM>(pLVItem));
}
else
lResult = TRUE;
}
else
lResult = TRUE;
if (lResult)
{
LVITEM lvItem = {LVIF_PARAM, pLVItemSrc->iItem, 0};
if (m_pListCtrl->DefWindowProc(
LVM_GETITEM, 0, reinterpret_cast<LPARAM>(&lvItem)))
{
ITEM_DATA* pItemData = reinterpret_cast<ITEM_DATA*>(lvItem.lParam);
// store lParam in administration data
if (pLVItemSrc->mask & LVIF_PARAM)
pItemData->m_lParam = pLVItemSrc->lParam;
LVITEM* pLVItemDst;
bool bInsertItem;
if (pItemData->m_apLVItem.GetSize() > nColumn)
{
pLVItemDst = pItemData->m_apLVItem[nColumn];
bInsertItem = false;
}
else
{
pLVItemDst = 0;
bInsertItem = true;
}
// Does the subitem already exist in the administration data?
if (pLVItemDst)
{
delete pLVItem;
// update subitem data
if (pLVItemSrc->mask & LVIF_IMAGE)
pLVItemDst->iImage = pLVItemSrc->iImage;
if (pLVItemSrc->mask & LVIF_INDENT)
pLVItemDst->iIndent = pLVItemSrc->iIndent;
if (pLVItemSrc->mask & LVIF_TEXT && pszOrigText)
{
if (pLVItemDst->mask & LVIF_TEXT &&
pLVItemDst->pszText != LPSTR_TEXTCALLBACK)
delete[] pLVItemDst->pszText;
pLVItemDst->pszText = pszOrigText;
}
pLVItemDst->mask |= pLVItemSrc->mask;
}
else
{
// store subitem administration data
pLVItem->mask = pLVItemSrc->mask;
pLVItem->iSubItem = nColumn;
pLVItem->pszText = pszOrigText;
if (bInsertItem)
pItemData->m_apLVItem.InsertAt(nColumn, pLVItem);
else
pItemData->m_apLVItem[nColumn] = pLVItem;
}
}
RefreshToolTips();
}
else
{
if (pLVItemSrc->mask & LVIF_TEXT &&
pLVItemSrc->pszText != LPSTR_TEXTCALLBACK) delete[] pszOrigText;
delete pLVItem;
}
return lResult;
}
/*** The text of an item or a subitem will be changed ************************/
LRESULT CListBase::OnSetItemText(WPARAM wParam, LPARAM lParam)
{
int nItem = static_cast<int>(wParam);
LVITEM lvItemSrc = *reinterpret_cast<LVITEM*>(lParam);
if (lvItemSrc.iSubItem < 0 ||
lvItemSrc.iSubItem >= m_aColumnData.GetSize() ||
nItem < 0) return FALSE;
lvItemSrc.mask = LVIF_TEXT;
LVITEM* pLVItem = DupLVItem(&lvItemSrc);
LRESULT lResult;
LPTSTR pszOrigText = pLVItem->pszText;
if (m_aColumnData[lvItemSrc.iSubItem]->m_bEnabled &&
m_aColumnData[lvItemSrc.iSubItem]->m_bVisible)
{
if (pszOrigText != LPSTR_TEXTCALLBACK &&
!(m_pListCtrl->GetStyle() & (LVS_SORTASCENDING | LVS_SORTDESCENDING)))
pLVItem->pszText = LPSTR_TEXTCALLBACK;
pLVItem->iSubItem = GetPhysicalIndex(lvItemSrc.iSubItem);
lResult =
m_pListCtrl->DefWindowProc(
LVM_SETITEMTEXT, wParam, reinterpret_cast<LPARAM>(pLVItem));
}
else
lResult = TRUE;
if (lResult)
{
LVITEM lvItem = {LVIF_PARAM, nItem, 0};
if (m_pListCtrl->DefWindowProc(
LVM_GETITEM, 0, reinterpret_cast<LPARAM>(&lvItem)))
{
ITEM_DATA* pItemData = reinterpret_cast<ITEM_DATA*>(lvItem.lParam);
LVITEM* pLVItemDst;
bool bInsertItem;
if (pItemData->m_apLVItem.GetSize() > lvItemSrc.iSubItem)
{
pLVItemDst = pItemData->m_apLVItem[lvItemSrc.iSubItem];
bInsertItem = false;
}
else
{
pLVItemDst = 0;
bInsertItem = true;
}
// Does the subitem already exist in the administration data?
if (pLVItemDst)
{
delete pLVItem;
// update subitem text
if (pszOrigText)
{
if (pLVItemDst->mask & LVIF_TEXT &&
pLVItemDst->pszText != LPSTR_TEXTCALLBACK)
delete[] pLVItemDst->pszText;
pLVItemDst->pszText = pszOrigText;
}
pLVItemDst->mask |= lvItemSrc.mask;
}
else
{
// store subitem administration data
pLVItem->iSubItem = lvItemSrc.iSubItem;
pLVItem->pszText = pszOrigText;
if (bInsertItem)
pItemData->m_apLVItem.InsertAt(lvItemSrc.iSubItem, pLVItem);
else
pItemData->m_apLVItem[lvItemSrc.iSubItem] = pLVItem;
}
}
RefreshToolTips();
}
else
{
if (lvItemSrc.pszText != LPSTR_TEXTCALLBACK) delete[] pszOrigText;
delete pLVItem;
}
return lResult;
}
/*** The items will be sorted ************************************************/
LRESULT CListBase::OnSortItems(WPARAM wParam, LPARAM lParam)
{
m_pfnLVCompare = reinterpret_cast<PFNLVCOMPARE>(lParam);
m_lParamSort = wParam;
m_bUpdateToolTips = false;
LRESULT lResult =
m_pListCtrl->DefWindowProc(
LVM_SORTITEMS, reinterpret_cast<WPARAM>(this),
reinterpret_cast<LPARAM>(CompareFunc));
m_bUpdateToolTips = true;
if (lResult) RefreshToolTips();
return lResult;
}
/*** System colors have been changed *****************************************/
void CListBase::OnSysColorChange()
{
ASSERT(m_pListCtrl->GetHeaderCtrl());
m_pListCtrl->Default();
// update background color
if (!m_bFixedBkColor)
{
m_pListCtrl->SetBkColor(GetSysColor(COLOR_WINDOW));
m_bFixedBkColor = false;
}
// update color of sort icons
if (m_imglstSortIcons.m_hImageList)
{
m_imglstSortIcons.DeleteImageList ();
m_bmpUpArrow.DeleteObject ();
m_bmpDownArrow.DeleteObject ();
CreateSortIcons ();
m_pListCtrl->GetHeaderCtrl()->SetImageList(&m_imglstSortIcons);
SetSortIcon ();
}
// force update of column colors
m_bColorSortColumn = !m_bColorSortColumn;
ColorSortColumn(!m_bColorSortColumn);
}
/*** Return the index of a subitem, based in its order in the header control */
int CListBase::OrderToIndex(int nOrder)
{
ASSERT(m_pListCtrl->GetHeaderCtrl());
return m_pListCtrl->GetHeaderCtrl()->OrderToIndex(nOrder);
}
/*** Prepare resources for hot underlining ***********************************/
void CListBase::PrepareHotUnderlining()
{
if (!m_hCursorArrow) m_hCursorArrow = LoadCursor(0, IDC_ARROW);
if (!m_hCursorHand)
if (m_winver <= 0x0400)
// Under Windows 95/NT we must create our own cursor
// to indicate hot items
m_hCursorHand = LoadCursor(AfxGetResourceHandle(), _T("IDC_HAND"));
else
m_hCursorHand = LoadCursor(0, IDC_HAND);
}
/*** Allow other necessary subclassing to occur before the window is *********/
/*** subclassed *********/
void CListBase::InitializeSubCtrls()
{
// add window to standard tool tip control
m_pToolTip = new CToolTipCtrl;
m_pToolTip->Create (m_pListCtrl);
m_pToolTip->AddTool(m_pListCtrl, _T(""));
// create special tool tip control for label expanding
m_pLabelTip = new CLabelTipCtrl(this);
m_pLabelTip->Create (m_pListCtrl);
m_pLabelTip->AddTool(m_pListCtrl, _T(""));
CToolInfo ti;
ti.cbSize = sizeof(TOOLINFO);
if (m_pLabelTip->GetToolInfo(ti, m_pListCtrl))
{
ti.uFlags |= TTF_TRANSPARENT;
m_pLabelTip->SetToolInfo(&ti);
}
// create special edit control for label editing
m_pLabelEdit = new CLabelEdit(this);
// fire an event when the mouse cursor leaves the client area of this list
// control
TRACKMOUSEEVENT eventTrack =
{
sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_pListCtrl->m_hWnd
};
_TrackMouseEvent(&eventTrack);
}
/*** Redisplay an individual column ******************************************/
void CListBase::RedisplayColumn(int nColumn)
{
ASSERT(nColumn >= 0 && nColumn < m_aColumnData.GetSize());
// show column
bool bNoColumnWasVisible = true;
for (INT_PTR i = m_aColumnData.GetUpperBound(); i >= 0; --i)
if (m_aColumnData[i]->m_bVisible)
{
bNoColumnWasVisible = false;
break;
}
m_aColumnData[nColumn]->m_bVisible = true;
int iPhysicalColumn = GetPhysicalIndex(nColumn);
LVCOLUMN lvColumn = *m_aColumnData[nColumn]->m_pLVColumn;
lvColumn.mask |= LVCF_ORDER;
lvColumn.iOrder = GetPhysicalOrder(m_aColumnData[nColumn]->m_nOrder);
if (m_pListCtrl->DefWindowProc(
LVM_INSERTCOLUMN, iPhysicalColumn, reinterpret_cast<LPARAM>(&lvColumn))
!= -1)
{
if (iPhysicalColumn == 0)
// rejustify first column of listview control to enable a right-
// justified or centerd first column
JustifyFirstColumn(lvColumn.fmt);
if (m_iSortColumn != 0)
{
int iSortColumn = abs(m_iSortColumn)-1;
bool bRecolorSortColumn = false;
if (iSortColumn == nColumn)
{
// restore sort icon
SetSortIcon();
bRecolorSortColumn = true;
}
else
{
iSortColumn = GetPhysicalIndex(iSortColumn);
bRecolorSortColumn = iSortColumn > iPhysicalColumn;
}
if (m_bColorSortColumn && bRecolorSortColumn && m_visualStyle == Present)
m_pListCtrl->SendMessage(LVM_SETSELECTEDCOLUMN, iSortColumn);
}
if (!(m_pListCtrl->GetStyle() & LVS_OWNERDATA))
{
if (iPhysicalColumn > 0)
for (int nItem = m_pListCtrl->GetItemCount(); --nItem >= 0;)
{
LVITEM lvItem = {LVIF_PARAM, nItem, 0};
if (m_pListCtrl->DefWindowProc(
LVM_GETITEM, 0, reinterpret_cast<LPARAM>(&lvItem)))
{
LVITEM* pLVItem =
reinterpret_cast<ITEM_DATA*>(
lvItem.lParam)->m_apLVItem[nColumn];
pLVItem->iItem = nItem;
pLVItem->iSubItem = nColumn;
m_pListCtrl->SetItem(pLVItem);
}
}
}
m_pListCtrl->SetColumnWidth(nColumn, m_aColumnData[nColumn]->m_nWidth);
}
}
/*** Refresh tooltips ********************************************************/
void CListBase::RefreshToolTips()
{
m_fCurrentFlags = 0;
m_iCurrentItem = -1;
m_iCurrentSubItem = -1;
POINT point;
if (GetCursorPos(&point))
{
m_bRefreshToolTips = true;
SetCursorPos(point.x, point.y);
}
}
/*** Set cursor if hot-tracking is enabled ***********************************/
void CListBase::SetHotCursorAndItem(LPLVHITTESTINFO pInfo)
{
if (m_dwExtendedStyle &
(LVS_EX_UNDERLINEHOT | LVS_EX_ONECLICKACTIVATE |
LVS_EX_TWOCLICKACTIVATE))
{
// determine item under mouse cursor (hot item) and
// the appropriate mouse cursor
int iOldHotItem = m_iHotItem;
m_iHotItem =
pInfo->iItem >= 0 &&
(
m_dwExtendedStyle & LVS_EX_FULLROWSELECT &&
pInfo->flags & LVHT_ONITEM & ~LVHT_ONITEMSTATEICON ||
(
m_bKeepLabelLeft && IndexToOrder(pInfo->iSubItem) == 0 ||
!m_bKeepLabelLeft && pInfo->iSubItem == 0
)
&&
(
pInfo->flags == LVHT_ONITEMLABEL ||
pInfo->flags == LVHT_ONITEMICON
)
)
&&
(
m_dwExtendedStyle & LVS_EX_TWOCLICKACTIVATE &&
m_pListCtrl->GetItemState(pInfo->iItem, LVIS_SELECTED) ==
LVIS_SELECTED ||
m_dwExtendedStyle & LVS_EX_ONECLICKACTIVATE
) ? pInfo->iItem : -1;
if (m_pListCtrl->GetStyle() & LVS_OWNERDRAWFIXED)
{
m_hCursorCustom = m_iHotItem != -1 ? m_hCursorHand : m_hCursorArrow;
if (m_iHotItem != iOldHotItem)
{
RECT rc;
if (iOldHotItem != -1)
{
m_pListCtrl->GetItemRect (iOldHotItem, &rc, LVIR_BOUNDS);
m_pListCtrl->InvalidateRect(&rc, FALSE);
}
if (m_iHotItem != -1)
{
m_pListCtrl->GetItemRect (m_iHotItem, &rc, LVIR_BOUNDS);
m_pListCtrl->InvalidateRect(&rc, FALSE);
}
}
SetCursor(m_hCursorCustom);
}
}
if (m_hTheme && m_pListCtrl->GetStyle() & LVS_OWNERDRAWFIXED)
{
// determine item under mouse cursor
int iOldItemUnderCursor = m_iItemUnderCursor;
m_iItemUnderCursor =
pInfo->iItem >= 0 &&
(
m_dwExtendedStyle & LVS_EX_FULLROWSELECT && pInfo->flags & LVHT_ONITEM
||
(
m_bKeepLabelLeft && IndexToOrder(pInfo->iSubItem) == 0 ||
!m_bKeepLabelLeft && pInfo->iSubItem == 0
)
&&
(
pInfo->flags == LVHT_ONITEMLABEL ||
pInfo->flags == LVHT_ONITEMICON ||
pInfo->flags == LVHT_ONITEMSTATEICON
)
) ? pInfo->iItem : -1;
if (m_iItemUnderCursor != iOldItemUnderCursor)
{
RECT rc;
if (iOldItemUnderCursor != -1)
{
m_pListCtrl->GetItemRect (iOldItemUnderCursor, &rc, LVIR_BOUNDS);
m_pListCtrl->InvalidateRect(&rc, FALSE);
}
if (m_iItemUnderCursor != -1)
{
m_pListCtrl->GetItemRect (m_iItemUnderCursor, &rc, LVIR_BOUNDS);
m_pListCtrl->InvalidateRect(&rc, FALSE);
}
}
}
}
/*** Display or hide sort icon on column to be sorted ************************/
void CListBase::SetSortIcon()
{
CHeaderCtrl* pHeaderCtrl = m_pListCtrl->GetHeaderCtrl();
ASSERT(pHeaderCtrl);
for (int col = static_cast<int>(m_aColumnData.GetUpperBound());
col >= 0;
--col)
if (m_aColumnData[col]->m_bEnabled && m_aColumnData[col]->m_bVisible)
{
HDITEM hdrItem;
int nPhysicalCol = GetPhysicalIndex(col);
if (m_visualStyle == Present)
{
hdrItem.mask = HDI_FORMAT;
pHeaderCtrl->GetItem(nPhysicalCol, &hdrItem);
if (m_bSortIconEnabled &&
m_iSortColumn != 0 &&
m_iSortColumn - 1 == col)
hdrItem.fmt =
hdrItem.fmt & HDF_JUSTIFYMASK | HDF_STRING | HDF_SORTUP;
else if (m_bSortIconEnabled &&
m_iSortColumn != 0 &&
-m_iSortColumn - 1 == col)
hdrItem.fmt =
hdrItem.fmt & HDF_JUSTIFYMASK | HDF_STRING | HDF_SORTDOWN;
else
hdrItem.fmt = hdrItem.fmt & HDF_JUSTIFYMASK | HDF_STRING;
}
else
{
hdrItem.mask = HDI_FORMAT | HDI_IMAGE;
pHeaderCtrl->GetItem(nPhysicalCol, &hdrItem);
if (m_bSortIconEnabled &&
m_iSortColumn != 0 &&
m_iSortColumn - 1 == col)
{
hdrItem.iImage = m_iUpArrow;
hdrItem.fmt =
hdrItem.fmt & HDF_JUSTIFYMASK |
HDF_IMAGE | HDF_STRING | HDF_BITMAP_ON_RIGHT;
}
else if (m_bSortIconEnabled &&
m_iSortColumn != 0 &&
-m_iSortColumn - 1 == col)
{
hdrItem.iImage = m_iDownArrow;
hdrItem.fmt =
hdrItem.fmt & HDF_JUSTIFYMASK |
HDF_IMAGE | HDF_STRING | HDF_BITMAP_ON_RIGHT;
}
else
hdrItem.fmt = hdrItem.fmt & HDF_JUSTIFYMASK | HDF_STRING;
}
pHeaderCtrl->SetItem(nPhysicalCol, &hdrItem);
}
}
/*** Definition of structure "CListBase::COLUMN_DATA" ************************/
/*** Destructor **************************************************************/
CListBase::COLUMN_DATA::~COLUMN_DATA()
{
if (m_pLVColumn)
{
delete[] m_pLVColumn->pszText;
delete m_pLVColumn;
}
}
/*** Definition of structure "CListBase::ITEM_DATA" **************************/
/*** Destructor **************************************************************/
CListBase::ITEM_DATA::~ITEM_DATA()
{
for (INT_PTR nColumn = m_apLVItem.GetUpperBound(); nColumn >= 0; --nColumn)
{
LVITEM* pLVItem = m_apLVItem[nColumn];
if (pLVItem)
{
delete[] pLVItem->pszText;
delete pLVItem;
}
}
}