3867 lines
110 KiB
C++
3867 lines
110 KiB
C++
// CListCtrl - A WTL list control with Windows Vista style item selection
|
|
// Revision: 1.5
|
|
// Last modified: 2nd November 2016
|
|
|
|
#pragma once
|
|
|
|
#include <set>
|
|
#include <algorithm>
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4838) // warning C4838: conversion from 'int' to 'UINT' requires a narrowing conversion
|
|
#include <wtl/atlctrlx.h>
|
|
#include <wtl/atlframe.h>
|
|
#include <wtl/atlmisc.h>
|
|
#include <wtl/atlgdi.h>
|
|
#pragma warning(pop)
|
|
|
|
#include "DragDrop.h"
|
|
#include "DropArrows.h"
|
|
#include "TitleTip.h"
|
|
#include "ListEdit.h"
|
|
#include "ListCombo.h"
|
|
#include "ListDate.h"
|
|
|
|
struct CListColumn
|
|
{
|
|
stdstr m_strText;
|
|
int m_nWidth;
|
|
BOOL m_bFixed;
|
|
UINT m_nFormat;
|
|
UINT m_nFlags;
|
|
int m_nImage;
|
|
int m_nIndex;
|
|
CListArray < stdstr > m_aComboList;
|
|
};
|
|
|
|
template < class T >
|
|
class CListImpl : public CWindowImpl< CListImpl< T > >,
|
|
public CDoubleBufferImpl< CListImpl< T > >
|
|
{
|
|
public:
|
|
CListImpl()
|
|
{
|
|
m_bSortEnabled = TRUE; // Added by Rowan 05/12/2006
|
|
m_bRightClickSelect = FALSE; // Shygoo 11/02/2016
|
|
m_bShowHeader = TRUE;
|
|
m_bSortAscending = TRUE;
|
|
m_bButtonDown = FALSE;
|
|
m_bMouseOver = FALSE;
|
|
m_bColumnSizing = FALSE;
|
|
m_bBeginSelect = FALSE;
|
|
m_bSingleSelect = FALSE;
|
|
m_bFocusSubItem = FALSE;
|
|
m_bGroupSelect = FALSE;
|
|
m_bEnableHorizScroll = FALSE;
|
|
m_bEnableVertScroll = FALSE;
|
|
m_bShowHorizScroll = TRUE;
|
|
m_bShowVertScroll = TRUE;
|
|
m_bShowSort = TRUE;
|
|
m_bResizeTimer = FALSE;
|
|
m_bDragDrop = FALSE;
|
|
m_bSmoothScroll = TRUE;
|
|
m_bEditItem = FALSE;
|
|
m_bScrolling = FALSE;
|
|
m_bScrollDown = FALSE;
|
|
m_bTileBackground = FALSE;
|
|
m_nMouseWheelScroll = 3;
|
|
m_nTotalWidth = 0;
|
|
m_nHeaderHeight = 0;
|
|
m_nItemHeight = 0;
|
|
m_nFirstSelected = NULL_ITEM;
|
|
m_nFocusItem = NULL_ITEM;
|
|
m_nFocusSubItem = NULL_SUBITEM;
|
|
m_nHotItem = NULL_ITEM;
|
|
m_nHotSubItem = NULL_SUBITEM;
|
|
m_nTitleTipItem = NULL_ITEM;
|
|
m_nTitleTipSubItem = NULL_SUBITEM;
|
|
m_nSortColumn = NULL_COLUMN;
|
|
m_nHighlightColumn = NULL_COLUMN;
|
|
m_nDragColumn = NULL_COLUMN;
|
|
m_nHotColumn = NULL_COLUMN;
|
|
m_nHotDivider = NULL_COLUMN;
|
|
m_nColumnSizing = NULL_COLUMN;
|
|
m_nScrollOffset = 0;
|
|
m_nScrollDelta = 0;
|
|
m_nScrollUnit = 0;
|
|
m_nStartScrollPos = 0;
|
|
m_nStartSize = 0;
|
|
m_nStartPos = 0;
|
|
m_ptDownPoint = 0;
|
|
m_ptSelectPoint = 0;
|
|
m_rcGroupSelect = 0;
|
|
m_dwSearchTick = 0;
|
|
m_dwScrollTick = 0;
|
|
m_strSearchString = _T( "" );
|
|
}
|
|
|
|
~CListImpl()
|
|
{
|
|
if (m_wndItemEdit.IsWindow())
|
|
{
|
|
// Patch memory window crash
|
|
m_wndItemEdit.UnsubclassWindow();
|
|
}
|
|
}
|
|
|
|
protected:
|
|
BOOL m_bSortEnabled; // Added by Rowan 05/12/2006 to disable sorting
|
|
BOOL m_bRightClickSelect; // Shygoo 11/02/2016
|
|
BOOL m_bShowHeader;
|
|
BOOL m_bShowSort;
|
|
BOOL m_bSortAscending;
|
|
BOOL m_bButtonDown;
|
|
BOOL m_bMouseOver;
|
|
BOOL m_bColumnSizing;
|
|
BOOL m_bBeginSelect;
|
|
BOOL m_bSingleSelect;
|
|
BOOL m_bFocusSubItem;
|
|
BOOL m_bGroupSelect;
|
|
BOOL m_bShowHorizScroll;
|
|
BOOL m_bShowVertScroll;
|
|
BOOL m_bEnableHorizScroll;
|
|
BOOL m_bEnableVertScroll;
|
|
BOOL m_bResizeTimer;
|
|
BOOL m_bDragDrop;
|
|
BOOL m_bSmoothScroll;
|
|
BOOL m_bEditItem;
|
|
BOOL m_bScrolling;
|
|
BOOL m_bScrollDown;
|
|
BOOL m_bTileBackground;
|
|
CPoint m_ptDownPoint;
|
|
CPoint m_ptSelectPoint;
|
|
CRect m_rcGroupSelect;
|
|
int m_nItemHeight;
|
|
int m_nHeaderHeight;
|
|
int m_nFirstSelected;
|
|
int m_nFocusItem;
|
|
int m_nFocusSubItem;
|
|
int m_nHotItem;
|
|
int m_nHotSubItem;
|
|
int m_nTitleTipItem;
|
|
int m_nTitleTipSubItem;
|
|
int m_nMouseWheelScroll;
|
|
int m_nTotalWidth;
|
|
int m_nSortColumn;
|
|
int m_nDragColumn;
|
|
int m_nHighlightColumn;
|
|
int m_nHotColumn;
|
|
int m_nHotDivider;
|
|
int m_nColumnSizing;
|
|
int m_nScrollOffset;
|
|
int m_nScrollDelta;
|
|
int m_nScrollUnit;
|
|
int m_nStartScrollPos;
|
|
int m_nStartSize;
|
|
int m_nStartPos;
|
|
DWORD m_dwSearchTick;
|
|
DWORD m_dwScrollTick;
|
|
stdstr m_strSearchString;
|
|
CBitmap m_bmpScrollList;
|
|
CBitmap m_bmpBackground;
|
|
|
|
CLIPFORMAT m_nHeaderClipboardFormat;
|
|
|
|
COLORREF m_rgbBackground;
|
|
COLORREF m_rgbHeaderBackground;
|
|
COLORREF m_rgbHeaderBorder;
|
|
COLORREF m_rgbHeaderShadow;
|
|
COLORREF m_rgbHeaderText;
|
|
COLORREF m_rgbHeaderHighlight;
|
|
COLORREF m_rgbSelectedItem;
|
|
COLORREF m_rgbSelectedText;
|
|
COLORREF m_rgbItemText;
|
|
COLORREF m_rgbSelectOuter;
|
|
COLORREF m_rgbSelectInner;
|
|
COLORREF m_rgbSelectTop;
|
|
COLORREF m_rgbSelectBottom;
|
|
COLORREF m_rgbNoFocusTop;
|
|
COLORREF m_rgbNoFocusBottom;
|
|
COLORREF m_rgbNoFocusOuter;
|
|
COLORREF m_rgbNoFocusInner;
|
|
COLORREF m_rgbFocusTop;
|
|
COLORREF m_rgbFocusBottom;
|
|
COLORREF m_rgbProgressTop;
|
|
COLORREF m_rgbProgressBottom;
|
|
COLORREF m_rgbItemFocus;
|
|
COLORREF m_rgbHyperLink;
|
|
|
|
CCursor m_curDivider;
|
|
CCursor m_curHyperLink;
|
|
CFont m_fntListFont;
|
|
CFont m_fntUnderlineFont;
|
|
CImageList m_ilListItems;
|
|
CImageList m_ilItemImages;
|
|
CDragDrop < CListImpl > m_oleDragDrop;
|
|
CToolTipCtrl m_ttToolTip;
|
|
CDropArrows m_wndDropArrows;
|
|
CTitleTip m_wndTitleTip;
|
|
CListEdit m_wndItemEdit;
|
|
CListCombo m_wndItemCombo;
|
|
CListDate m_wndItemDate;
|
|
|
|
CListArray < CListColumn > m_aColumns;
|
|
set < int > m_setSelectedItems;
|
|
public:
|
|
BOOL SubclassWindow( HWND hWnd )
|
|
{
|
|
T* pT;
|
|
pT = static_cast<T*>(this);
|
|
return CWindowImpl< CListImpl >::SubclassWindow( hWnd ) ? pT->Initialise() : FALSE;
|
|
}
|
|
|
|
void RegisterClass()
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
pT = pT;
|
|
pT->GetWndClassInfo().m_wc.lpfnWndProc = m_pfnSuperWindowProc;
|
|
pT->GetWndClassInfo().Register( &m_pfnSuperWindowProc );
|
|
}
|
|
|
|
BOOL Initialise()
|
|
{
|
|
// Load list images
|
|
if ( !m_ilListItems.CreateFromImage( IDB_LISTITEMS, 16, 0, RGB( 255, 0, 255 ), IMAGE_BITMAP, LR_CREATEDIBSECTION ) )
|
|
return FALSE;
|
|
|
|
if ( m_curDivider.LoadCursor( IDC_DIVIDER ) == nullptr )
|
|
return FALSE;
|
|
if ( m_curHyperLink.LoadCursor( IDC_HYPERLINK ) == nullptr )
|
|
return FALSE;
|
|
|
|
// Load interface settings
|
|
if ( !LoadSettings() )
|
|
return FALSE;
|
|
|
|
// Give control a static border
|
|
ModifyStyle( WS_BORDER, WS_CLIPCHILDREN );
|
|
ModifyStyleEx( WS_EX_CLIENTEDGE, WS_EX_STATICEDGE, SWP_FRAMECHANGED );
|
|
|
|
// Register drag drop
|
|
m_oleDragDrop.Register( this );
|
|
m_oleDragDrop.AddTargetFormat( m_nHeaderClipboardFormat );
|
|
m_oleDragDrop.AddSourceFormat( m_nHeaderClipboardFormat );
|
|
|
|
// Create the tooltip
|
|
if ( !m_ttToolTip.Create( m_hWnd ) )
|
|
return FALSE;
|
|
m_ttToolTip.SetMaxTipWidth( SHRT_MAX );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL LoadSettings()
|
|
{
|
|
m_rgbBackground = GetSysColor( COLOR_WINDOW );
|
|
m_rgbHeaderBackground = GetSysColor( COLOR_BTNFACE );
|
|
m_rgbHeaderBorder = GetSysColor( COLOR_3DHIGHLIGHT );
|
|
m_rgbHeaderShadow = GetSysColor( COLOR_3DSHADOW );
|
|
m_rgbHeaderText = GetSysColor( COLOR_WINDOWTEXT );
|
|
m_rgbHeaderHighlight = RGB( 130, 140, 180 );
|
|
m_rgbSelectedItem = GetSysColor( COLOR_HIGHLIGHT );
|
|
m_rgbSelectedText = GetSysColor( COLOR_HIGHLIGHTTEXT );
|
|
m_rgbItemText = GetSysColor( COLOR_WINDOWTEXT );
|
|
m_rgbSelectOuter = RGB( 170, 200, 245 );
|
|
m_rgbSelectInner = RGB( 230, 250, 250 );
|
|
m_rgbSelectTop = RGB( 210, 240, 250 );
|
|
m_rgbSelectBottom = RGB( 185, 215, 250 );
|
|
m_rgbNoFocusTop = RGB( 250, 250, 250 );
|
|
m_rgbNoFocusBottom = RGB( 235, 235, 235 );
|
|
m_rgbNoFocusOuter = RGB( 220, 220, 220 );
|
|
m_rgbNoFocusInner = RGB( 245, 245, 245 );
|
|
m_rgbFocusTop = RGB( 235, 245, 245 );
|
|
m_rgbFocusBottom = RGB( 225, 235, 245 );
|
|
m_rgbProgressTop = RGB( 170, 240, 170 );
|
|
m_rgbProgressBottom = RGB( 45, 210, 50 );
|
|
m_rgbItemFocus = RGB( 180, 190, 210 );
|
|
m_rgbHyperLink = RGB( 0, 0, 200 );
|
|
|
|
m_nHeaderClipboardFormat = (CLIPFORMAT)RegisterClipboardFormat( _T( "HEADERCLIPBOARDFORMAT" ) );
|
|
|
|
// Get number of lines to scroll
|
|
#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
|
|
SystemParametersInfo( SPI_GETWHEELSCROLLLINES, 0, &m_nMouseWheelScroll, 0 );
|
|
#endif
|
|
|
|
// Get system message font
|
|
CLogFont logFont;
|
|
logFont.SetMessageBoxFont();
|
|
if ( !m_fntListFont.IsNull() )
|
|
m_fntListFont.DeleteObject();
|
|
if ( m_fntListFont.CreateFontIndirect( &logFont ) == nullptr )
|
|
return FALSE;
|
|
|
|
// Get system underline font
|
|
logFont.lfUnderline = BYTE(TRUE);
|
|
if ( !m_fntUnderlineFont.IsNull() )
|
|
m_fntUnderlineFont.DeleteObject();
|
|
if ( m_fntUnderlineFont.CreateFontIndirect( &logFont ) == nullptr )
|
|
return FALSE;
|
|
|
|
CClientDC dcClient( m_hWnd );
|
|
|
|
HFONT hOldFont = dcClient.SelectFont( m_fntListFont );
|
|
|
|
CSize sizeExtent;
|
|
if ( !dcClient.GetTextExtent( _T( "Height" ), -1, &sizeExtent ) )
|
|
return FALSE;
|
|
|
|
dcClient.SelectFont( hOldFont );
|
|
|
|
// Has system font changed?
|
|
if ( m_nItemHeight != sizeExtent.cy + ITEM_HEIGHT_MARGIN )
|
|
{
|
|
m_nItemHeight = sizeExtent.cy + ITEM_HEIGHT_MARGIN;
|
|
m_nHeaderHeight = m_nItemHeight;
|
|
|
|
// Create drop arrows window
|
|
if ( m_wndDropArrows.IsWindow() )
|
|
m_wndDropArrows.DestroyWindow();
|
|
if ( !m_wndDropArrows.Create( m_hWnd, m_nHeaderHeight, TRUE ) )
|
|
return FALSE;
|
|
}
|
|
|
|
// Create title tip window
|
|
if ( m_wndTitleTip.IsWindow() )
|
|
m_wndTitleTip.DestroyWindow();
|
|
if ( !m_wndTitleTip.Create( m_hWnd ) )
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Added by Rowan 05/12/2006
|
|
void SetSortEnabled(BOOL bSortEnabled)
|
|
{
|
|
m_bSortEnabled = bSortEnabled;
|
|
}
|
|
|
|
// Shygoo 11/02/2016
|
|
void SetRightClickSelect( BOOL bRightClickSelect = TRUE)
|
|
{
|
|
m_bRightClickSelect = bRightClickSelect;
|
|
}
|
|
|
|
void ShowHeader( BOOL bShowHeader = TRUE )
|
|
{
|
|
m_bShowHeader = bShowHeader;
|
|
ResetScrollBars();
|
|
Invalidate();
|
|
}
|
|
|
|
void ShowHeaderSort( BOOL bShowSort = TRUE )
|
|
{
|
|
m_bShowSort = bShowSort;
|
|
Invalidate();
|
|
}
|
|
|
|
void SetSingleSelect( BOOL bSingleSelect = TRUE )
|
|
{
|
|
m_bSingleSelect = bSingleSelect;
|
|
Invalidate();
|
|
}
|
|
|
|
void SetFocusSubItem( BOOL bFocusSubItem = TRUE )
|
|
{
|
|
m_bFocusSubItem = bFocusSubItem;
|
|
Invalidate();
|
|
}
|
|
|
|
void SetDragDrop( BOOL bDragDrop = TRUE )
|
|
{
|
|
m_bDragDrop = bDragDrop;
|
|
}
|
|
|
|
void SetSmoothScroll( BOOL bSmoothScroll = TRUE )
|
|
{
|
|
m_bSmoothScroll = bSmoothScroll;
|
|
}
|
|
|
|
void SetBackgroundImage( HBITMAP hBackgroundImage, BOOL bTileImage = FALSE )
|
|
{
|
|
m_bmpBackground = hBackgroundImage;
|
|
m_bTileBackground = bTileImage;
|
|
}
|
|
|
|
void SetImageList( CImageList& ilItemImages )
|
|
{
|
|
m_ilItemImages = ilItemImages;
|
|
}
|
|
|
|
UINT ValidateFlags( UINT nFlags )
|
|
{
|
|
if ( nFlags & ITEM_FLAGS_CENTRE )
|
|
nFlags &= ~( ITEM_FLAGS_LEFT | ITEM_FLAGS_RIGHT );
|
|
if ( nFlags & ITEM_FLAGS_RIGHT )
|
|
nFlags &= ~ITEM_FLAGS_LEFT;
|
|
if ( nFlags & ITEM_FLAGS_DATE_ONLY )
|
|
nFlags &= ~ITEM_FLAGS_TIME_ONLY;
|
|
if ( nFlags & ( ITEM_FLAGS_EDIT_NUMBER | ITEM_FLAGS_EDIT_FLOAT ) )
|
|
nFlags &= ~ITEM_FLAGS_EDIT_UPPER;
|
|
if ( !( nFlags & ( ITEM_FLAGS_EDIT_NUMBER | ITEM_FLAGS_EDIT_FLOAT ) ) )
|
|
nFlags &= ~( ITEM_FLAGS_EDIT_NEGATIVE | ITEM_FLAGS_EDIT_OPERATOR );
|
|
if ( nFlags & ITEM_FLAGS_COMBO_EDIT )
|
|
nFlags &= ~( ITEM_FLAGS_DATE_ONLY | ITEM_FLAGS_TIME_ONLY | ITEM_FLAGS_DATETIME_NONE );
|
|
return nFlags;
|
|
}
|
|
|
|
void AddColumn( CListColumn& listColumn )
|
|
{
|
|
// Minimum column width
|
|
if ( listColumn.m_strText.empty() && listColumn.m_nImage != ITEM_IMAGE_NONE )
|
|
{
|
|
CSize sizeIcon;
|
|
m_ilListItems.GetIconSize( sizeIcon );
|
|
listColumn.m_nWidth = sizeIcon.cx + 5;
|
|
listColumn.m_nFlags |= ITEM_FLAGS_CENTRE;
|
|
}
|
|
|
|
// Correct incompatible flag mask values
|
|
listColumn.m_nFlags = ValidateFlags( listColumn.m_nFlags );
|
|
|
|
// Initial data index
|
|
listColumn.m_nIndex = GetColumnCount();
|
|
|
|
m_aColumns.Add( listColumn );
|
|
|
|
ResetScrollBars();
|
|
Invalidate();
|
|
}
|
|
|
|
void AddColumn( LPCTSTR lpszText, int nWidth = 0, int nImage = ITEM_IMAGE_NONE, BOOL bFixed = FALSE, UINT nFormat = ITEM_FORMAT_NONE, UINT nFlags = ITEM_FLAGS_NONE )
|
|
{
|
|
CListColumn listColumn;
|
|
listColumn.m_strText = lpszText;
|
|
listColumn.m_nWidth = nWidth;
|
|
listColumn.m_bFixed = bFixed;
|
|
listColumn.m_nFormat = nFormat;
|
|
listColumn.m_nFlags = nFlags;
|
|
listColumn.m_nImage = nImage;
|
|
AddColumn( listColumn );
|
|
}
|
|
|
|
void RemoveAllCoumns ( void )
|
|
{
|
|
m_aColumns.RemoveAll();
|
|
ResetScrollBars();
|
|
Invalidate();
|
|
}
|
|
|
|
BOOL GetHasEditItem ()
|
|
{
|
|
return m_bEditItem;
|
|
}
|
|
|
|
int GetColumnCount()
|
|
{
|
|
return m_aColumns.GetSize();
|
|
}
|
|
|
|
BOOL GetColumn( int nColumn, CListColumn& listColumn )
|
|
{
|
|
if ( nColumn < 0 || nColumn >= GetColumnCount() )
|
|
return FALSE;
|
|
listColumn = m_aColumns[ nColumn ];
|
|
return TRUE;
|
|
}
|
|
|
|
int GetTotalWidth( BOOL bRecalc = FALSE )
|
|
{
|
|
if ( bRecalc )
|
|
{
|
|
m_nTotalWidth = 0;
|
|
for ( int nColumn = 0; nColumn < GetColumnCount(); nColumn++ )
|
|
m_nTotalWidth += GetColumnWidth( nColumn );
|
|
}
|
|
return m_nTotalWidth - 1;
|
|
}
|
|
|
|
int GetTotalHeight()
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
return max( ( pT->GetItemCount() * m_nItemHeight ) + ( m_bShowHeader ? m_nHeaderHeight : 0 ), 1 );
|
|
}
|
|
|
|
BOOL SetColumnWidth( int nColumn, int nWidth )
|
|
{
|
|
if ( nColumn < 0 || nColumn >= GetColumnCount() )
|
|
return FALSE;
|
|
|
|
// Set new column size if not fixed
|
|
if ( !m_aColumns[ nColumn ].m_bFixed )
|
|
{
|
|
m_aColumns[ nColumn ].m_nWidth = nWidth;
|
|
|
|
ResetScrollBars();
|
|
Invalidate();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int GetColumnWidth( int nColumn )
|
|
{
|
|
CListColumn listColumn;
|
|
return GetColumn( nColumn, listColumn ) ? listColumn.m_nWidth : 0;
|
|
}
|
|
|
|
int GetColumnIndex( int nColumn )
|
|
{
|
|
CListColumn listColumn;
|
|
return GetColumn( nColumn, listColumn ) ? listColumn.m_nIndex : 0;
|
|
}
|
|
|
|
int IndexToOrder( int nIndex )
|
|
{
|
|
for ( int nColumn = 0; nColumn < GetColumnCount(); nColumn++ )
|
|
{
|
|
if ( GetColumnIndex( nColumn ) == nIndex )
|
|
return nColumn;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
BOOL SetColumnFormat( int nColumn, UINT nFormat, UINT nFlags = ITEM_FLAGS_NONE )
|
|
{
|
|
if ( nColumn < 0 || nColumn >= GetColumnCount() )
|
|
return FALSE;
|
|
m_aColumns[ nColumn ].m_nFormat = nFormat;
|
|
m_aColumns[ nColumn ].m_nFlags = ValidateFlags( nFlags );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetColumnFormat( int nColumn, UINT nFormat, UINT nFlags, CListArray < stdstr >& aComboList )
|
|
{
|
|
if ( nColumn < 0 || nColumn >= GetColumnCount() )
|
|
return FALSE;
|
|
m_aColumns[ nColumn ].m_nFormat = nFormat;
|
|
m_aColumns[ nColumn ].m_nFlags = ValidateFlags( nFlags );
|
|
m_aColumns[ nColumn ].m_aComboList = aComboList;
|
|
return TRUE;
|
|
}
|
|
|
|
UINT GetColumnFormat( int nColumn )
|
|
{
|
|
CListColumn listColumn;
|
|
return GetColumn( nColumn, listColumn ) ? listColumn.m_nFormat : ITEM_FORMAT_NONE;
|
|
}
|
|
|
|
UINT GetColumnFlags( int nColumn )
|
|
{
|
|
CListColumn listColumn;
|
|
return GetColumn( nColumn, listColumn ) ? listColumn.m_nFlags : ITEM_FLAGS_NONE;
|
|
}
|
|
|
|
BOOL GetColumnComboList( int nColumn, CListArray < stdstr >& aComboList )
|
|
{
|
|
CListColumn listColumn;
|
|
if ( !GetColumn( nColumn, listColumn ) )
|
|
return FALSE;
|
|
aComboList = listColumn.m_aComboList;
|
|
return !aComboList.IsEmpty();
|
|
}
|
|
|
|
BOOL GetColumnRect( int nColumn, CRect& rcColumn )
|
|
{
|
|
if ( nColumn < 0 || nColumn >= GetColumnCount() )
|
|
return FALSE;
|
|
|
|
GetClientRect( rcColumn );
|
|
rcColumn.bottom = m_nHeaderHeight;
|
|
|
|
for ( int nColumnOrder = 0; nColumnOrder < GetColumnCount(); nColumnOrder++ )
|
|
{
|
|
int nWidth = GetColumnWidth( nColumnOrder );
|
|
|
|
if ( nColumn == nColumnOrder )
|
|
{
|
|
rcColumn.right = rcColumn.left + nWidth;
|
|
break;
|
|
}
|
|
|
|
rcColumn.left += nWidth;
|
|
}
|
|
|
|
// Offset column by scroll position
|
|
rcColumn.OffsetRect( -GetScrollPos( SB_HORZ ), 0 );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL AddItem()
|
|
{
|
|
ResetScrollBars();
|
|
return Invalidate();
|
|
}
|
|
|
|
BOOL DeleteItem( int nItem )
|
|
{
|
|
m_setSelectedItems.erase( nItem );
|
|
ResetScrollBars();
|
|
return Invalidate();
|
|
}
|
|
|
|
BOOL DeleteAllItems()
|
|
{
|
|
m_setSelectedItems.clear();
|
|
ResetScrollBars();
|
|
return Invalidate();
|
|
}
|
|
|
|
int GetItemCount()
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
return 0;
|
|
}
|
|
|
|
stdstr GetItemText( int nItem, int nSubItem )
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
return _T( "" );
|
|
}
|
|
|
|
BOOL GetItemDate( int nItem, int nSubItem, SYSTEMTIME& stItemDate )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
ZeroMemory( &stItemDate, sizeof( SYSTEMTIME ) );
|
|
|
|
stdstr strItemText = pT->GetItemText( nItem, nSubItem );
|
|
if ( strItemText.empty() )
|
|
return FALSE;
|
|
|
|
// Get date and time from item text: yyyymmddhhmmss
|
|
stItemDate.wYear = (WORD)_ttoi( strItemText.substr(0, 4 ).c_str() );
|
|
stItemDate.wMonth = (WORD)_ttoi( strItemText.substr( 4, 2 ).c_str() );
|
|
stItemDate.wDay = (WORD)_ttoi( strItemText.substr( 6, 2 ).c_str() );
|
|
stItemDate.wHour = (WORD)_ttoi( strItemText.substr( 8, 2 ).c_str() );
|
|
stItemDate.wMinute = (WORD)_ttoi( strItemText.substr( 10, 2 ).c_str() );
|
|
stItemDate.wSecond = (WORD)_ttoi( strItemText.substr( 12, 2 ).c_str() );
|
|
stItemDate.wMilliseconds = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int GetItemImage( int nItem, int nSubItem )
|
|
{
|
|
return ITEM_IMAGE_NONE; // May be implemented in a derived class
|
|
}
|
|
|
|
UINT GetItemFormat( int nItem, int nSubItem )
|
|
{
|
|
return GetColumnFormat( IndexToOrder( nSubItem ) ); // May be implemented in a derived class
|
|
}
|
|
|
|
UINT GetItemFlags( int nItem, int nSubItem )
|
|
{
|
|
return GetColumnFlags( IndexToOrder( nSubItem ) ); // May be implemented in a derived class
|
|
}
|
|
|
|
BOOL GetItemComboList( int nItem, int nSubItem, CListArray < stdstr >& aComboList )
|
|
{
|
|
return GetColumnComboList( IndexToOrder( nSubItem ), aComboList ); // May be implemented in a derived class
|
|
}
|
|
|
|
HFONT GetItemFont( int /*nItem*/, int /*nSubItem*/ )
|
|
{
|
|
return m_fntListFont; // May be implemented in a derived class
|
|
}
|
|
|
|
BOOL GetItemColours( int nItem, int nSubItem, COLORREF& rgbBackground, COLORREF& rgbText )
|
|
{
|
|
rgbBackground = m_rgbBackground;
|
|
rgbText = m_rgbItemText;
|
|
return TRUE;
|
|
}
|
|
|
|
stdstr virtual GetItemToolTip( int /*nItem*/, int /*nSubItem*/ )
|
|
{
|
|
return _T( "" ); // May be implemented in a derived class
|
|
}
|
|
stdstr virtual GetHeaderToolTip(int /*column*/)
|
|
{
|
|
return _T(""); // Implemented by child class
|
|
}
|
|
|
|
|
|
BOOL SetItemText( int nItem, int nSubItem, LPCTSTR lpszText )
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SetItemComboIndex( int nItem, int nSubItem, int nIndex )
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SetItemDate( int nItem, int nSubItem, SYSTEMTIME& stItemDate )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
// Set date and time in format (yyyymmddhhmmss)
|
|
stdstr strFormatDate;
|
|
strFormatDate.Format( _T( "%04d%02d%02d%02d%02d%02d" ), stItemDate.wYear, stItemDate.wMonth, stItemDate.wDay, stItemDate.wHour, stItemDate.wMinute, stItemDate.wSecond );
|
|
|
|
return pT->SetItemText( nItem, nSubItem, strFormatDate.c_str() );
|
|
}
|
|
|
|
BOOL SetItemCheck( int nItem, int nSubItem, int nCheckValue )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
switch ( pT->GetItemFormat( nItem, nSubItem ) )
|
|
{
|
|
case ITEM_FORMAT_CHECKBOX: return pT->SetItemText( nItem, nSubItem, nCheckValue > 0 ? _T( "1" ) : _T( "0" ) );
|
|
case ITEM_FORMAT_CHECKBOX_3STATE: if ( nCheckValue < 0 )
|
|
return pT->SetItemText( nItem, nSubItem, _T( "-1" ) );
|
|
if ( nCheckValue > 0 )
|
|
return pT->SetItemText( nItem, nSubItem, _T( "1" ) );
|
|
return pT->SetItemText( nItem, nSubItem, _T( "0" ) );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SetItemImage( int nItem, int nSubItem, int nImage )
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SetItemFormat( int nItem, int nSubItem, UINT nFormat, UINT nFlags = ITEM_FLAGS_NONE )
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SetItemFormat( int nItem, int nSubItem, UINT nFormat, UINT nFlags, CListArray < stdstr >& aComboList )
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SetItemFont( int nItem, int nSubItem, HFONT hFont )
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SetItemColours( int nItem, int nSubItem, COLORREF rgbBackground, COLORREF rgbText )
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
return FALSE;
|
|
}
|
|
|
|
void ReverseItems()
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
}
|
|
|
|
void SortItems( int nColumn, BOOL bAscending )
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
}
|
|
|
|
BOOL GetItemRect( int nItem, int nSubItem, CRect& rcItem )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
int nTopItem = GetTopItem();
|
|
if ( nItem < nTopItem || nItem >= pT->GetItemCount() || nItem >= nTopItem + GetCountPerPage() )
|
|
return FALSE;
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
|
|
// Calculate item rect based on scroll position
|
|
rcItem = rcClient;
|
|
rcItem.top = ( m_bShowHeader ? m_nHeaderHeight : 0 ) + ( ( nItem - nTopItem ) * m_nItemHeight );
|
|
rcItem.bottom = rcItem.top + m_nItemHeight;
|
|
rcItem.right = min( rcClient.right, GetTotalWidth() );
|
|
|
|
if ( nSubItem != NULL_SUBITEM )
|
|
{
|
|
CRect rcColumn;
|
|
if ( !GetColumnRect( nSubItem, rcColumn ) )
|
|
return FALSE;
|
|
|
|
rcItem.left = rcColumn.left;
|
|
rcItem.right = rcColumn.right;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GetItemRect( int nItem, CRect& rcItem )
|
|
{
|
|
return GetItemRect( nItem, NULL_SUBITEM, rcItem );
|
|
}
|
|
|
|
BOOL InvalidateItem( int nItem, int nSubItem = NULL_SUBITEM )
|
|
{
|
|
CRect rcItem;
|
|
return GetItemRect( nItem, nSubItem, rcItem ) ? InvalidateRect( rcItem ) : FALSE;
|
|
}
|
|
|
|
BOOL InvalidateHeader()
|
|
{
|
|
if ( !m_bShowHeader )
|
|
return TRUE;
|
|
CRect rcHeader;
|
|
if ( !GetClientRect( rcHeader ) )
|
|
return FALSE;
|
|
rcHeader.bottom = m_nHeaderHeight;
|
|
return InvalidateRect( rcHeader );
|
|
}
|
|
|
|
int GetTopItem()
|
|
{
|
|
return (int)( GetScrollPos( SB_VERT ) / m_nItemHeight );
|
|
}
|
|
|
|
int GetCountPerPage( BOOL bPartial = TRUE )
|
|
{
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
rcClient.top = ( m_bShowHeader ? m_nHeaderHeight : 0 );
|
|
|
|
// Calculate number of items per control height (include partial item)
|
|
div_t divHeight = div( rcClient.Height(), m_nItemHeight );
|
|
|
|
// Round up to nearest item count
|
|
return max( bPartial && divHeight.rem > 0 ? divHeight.quot + 1 : divHeight.quot, 1 );
|
|
}
|
|
|
|
BOOL IsItemVisible( int nItem, int nSubItem = NULL_SUBITEM, BOOL bPartial = TRUE )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
int nTopItem = GetTopItem();
|
|
if ( nItem < nTopItem || nItem >= pT->GetItemCount() )
|
|
return FALSE;
|
|
|
|
// Check whether item is visible
|
|
if ( nItem < nTopItem || nItem >= nTopItem + GetCountPerPage( bPartial ) )
|
|
return FALSE;
|
|
|
|
// Check whether subitem is visible
|
|
if ( m_bFocusSubItem && nSubItem != NULL_SUBITEM )
|
|
{
|
|
CRect rcColumn;
|
|
if ( !GetColumnRect( nSubItem, rcColumn ) )
|
|
return FALSE;
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
|
|
if ( rcColumn.left < rcClient.left || rcColumn.right > rcClient.right )
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL EnsureItemVisible( int nItem, int nSubItem = NULL_SUBITEM )
|
|
{
|
|
if ( IsItemVisible( nItem, nSubItem, FALSE ) )
|
|
return TRUE;
|
|
|
|
HideTitleTip();
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
rcClient.top = ( m_bShowHeader ? m_nHeaderHeight : 0 );
|
|
|
|
CRect rcItem;
|
|
rcItem.top = ( m_bShowHeader ? m_nHeaderHeight : 0 ) + ( ( nItem - GetTopItem() ) * m_nItemHeight );
|
|
rcItem.bottom = rcItem.top + m_nItemHeight;
|
|
|
|
if ( rcItem.top < rcClient.top || rcItem.bottom > rcClient.bottom )
|
|
{
|
|
int nScrollItem = NULL_ITEM;
|
|
|
|
// Scroll list up/down to include item
|
|
if ( rcItem.top < rcClient.top || rcItem.Height() > rcClient.Height() )
|
|
nScrollItem = nItem;
|
|
else if ( rcItem.bottom > rcClient.bottom )
|
|
nScrollItem = nItem - ( GetCountPerPage( FALSE ) - 1 );
|
|
|
|
if ( nScrollItem != NULL_ITEM )
|
|
SetScrollPos( SB_VERT, nScrollItem * m_nItemHeight );
|
|
}
|
|
|
|
if ( m_bFocusSubItem && nSubItem != NULL_SUBITEM )
|
|
{
|
|
CRect rcColumn;
|
|
if ( !GetColumnRect( nSubItem, rcColumn ) )
|
|
return FALSE;
|
|
|
|
GetClientRect( rcClient );
|
|
|
|
int nScrollPos = 0;
|
|
|
|
// Scroll list left/right to include subitem
|
|
if ( rcColumn.Width() > rcClient.Width() || rcColumn.left < 0 )
|
|
nScrollPos = rcColumn.left;
|
|
else if ( rcColumn.right > rcClient.right )
|
|
nScrollPos = rcColumn.right - rcClient.right;
|
|
|
|
if ( nScrollPos != 0 )
|
|
SetScrollPos( SB_HORZ, GetScrollPos( SB_HORZ ) + nScrollPos );
|
|
}
|
|
|
|
return Invalidate();
|
|
}
|
|
|
|
void ShowScrollBar( int nScrollBar, BOOL bShow = TRUE )
|
|
{
|
|
switch ( nScrollBar )
|
|
{
|
|
case SB_HORZ: m_bShowHorizScroll = bShow;
|
|
break;
|
|
case SB_VERT: m_bShowVertScroll = bShow;
|
|
break;
|
|
case SB_BOTH: m_bShowHorizScroll = bShow;
|
|
m_bShowVertScroll = bShow;
|
|
break;
|
|
}
|
|
|
|
ResetScrollBars();
|
|
Invalidate();
|
|
}
|
|
|
|
void ResetScrollBars( int nScrollBar = SB_BOTH, int nScrollPos = -1, BOOL bRecalc = TRUE )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
|
|
SCROLLINFO infoScroll;
|
|
infoScroll.cbSize = sizeof( SCROLLINFO );
|
|
infoScroll.fMask = nScrollPos < 0 ? SIF_PAGE | SIF_RANGE : SIF_PAGE | SIF_RANGE | SIF_POS;
|
|
infoScroll.nPos = nScrollPos;
|
|
infoScroll.nMin = 0;
|
|
|
|
if ( ( nScrollBar == SB_BOTH || nScrollBar == SB_VERT ) && m_bShowVertScroll )
|
|
{
|
|
infoScroll.nMax = ( pT->GetItemCount() * m_nItemHeight ) + ( m_bShowHeader ? m_nHeaderHeight : 0 );
|
|
infoScroll.nPage = rcClient.Height() - ( m_bShowHeader ? m_nHeaderHeight : 0 );
|
|
|
|
// Are we within client range?
|
|
if ( (UINT)infoScroll.nMax <= infoScroll.nPage + ( m_bShowHeader ? m_nHeaderHeight : 0 ) )
|
|
infoScroll.nMax = 0;
|
|
|
|
// Set vertical scroll bar
|
|
m_bEnableVertScroll = SetScrollInfo( SB_VERT, &infoScroll, TRUE ) ? ( infoScroll.nMax > 0 ) : FALSE;
|
|
}
|
|
|
|
if ( ( nScrollBar == SB_BOTH || nScrollBar == SB_HORZ ) && m_bShowHorizScroll )
|
|
{
|
|
infoScroll.nMax = GetTotalWidth( bRecalc );
|
|
infoScroll.nPage = rcClient.Width();
|
|
|
|
// Are we within client range?
|
|
if ( infoScroll.nPage >= (UINT)infoScroll.nMax )
|
|
infoScroll.nMax = 0;
|
|
|
|
// Set horizontal scroll bar
|
|
m_bEnableHorizScroll = SetScrollInfo( SB_HORZ, &infoScroll, TRUE ) ? ( infoScroll.nMax > (int)infoScroll.nPage ) : FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL IsScrollBarVisible( int nScrollBar )
|
|
{
|
|
switch ( nScrollBar )
|
|
{
|
|
case SB_HORZ: return m_bEnableHorizScroll;
|
|
case SB_VERT: return m_bEnableVertScroll;
|
|
case SB_BOTH: return ( m_bEnableHorizScroll && m_bEnableVertScroll );
|
|
default: return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL ResetSelected()
|
|
{
|
|
m_setSelectedItems.clear();
|
|
m_nFocusItem = NULL_ITEM;
|
|
m_nFocusSubItem = NULL_SUBITEM;
|
|
m_nFirstSelected = NULL_ITEM;
|
|
return Invalidate();
|
|
}
|
|
|
|
BOOL SelectItem( int nItem, int nSubItem = NULL_SUBITEM, UINT nFlags = 0 )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
if ( nItem < 0 || nItem >= pT->GetItemCount() )
|
|
return FALSE;
|
|
|
|
BOOL bSelectItem = TRUE;
|
|
BOOL bSelectRange = !m_bSingleSelect && ( nFlags & MK_SHIFT );
|
|
BOOL bNewSelect = !( bSelectRange || ( nFlags & MK_CONTROL ) );
|
|
BOOL bEnsureVisible = FALSE;
|
|
|
|
// Are we starting a new select sequence?
|
|
if ( bNewSelect || bSelectRange )
|
|
{
|
|
// Are we simply reselecting the same item?
|
|
if ( m_setSelectedItems.size() == 1 && *m_setSelectedItems.begin() == nItem )
|
|
{
|
|
bSelectItem = FALSE;
|
|
m_nFirstSelected = nItem;
|
|
m_nFocusItem = nItem;
|
|
m_nFocusSubItem = nSubItem;
|
|
}
|
|
else
|
|
m_setSelectedItems.clear();
|
|
}
|
|
else // We adding to or removing from select sequence
|
|
{
|
|
if ( m_bSingleSelect )
|
|
m_setSelectedItems.clear();
|
|
|
|
set < int >::iterator posSelectedItem = m_setSelectedItems.find( nItem );
|
|
|
|
// Is this item already selected?
|
|
if ( posSelectedItem != m_setSelectedItems.end() )
|
|
{
|
|
bSelectItem = FALSE;
|
|
m_setSelectedItems.erase( posSelectedItem );
|
|
m_nFirstSelected = nItem;
|
|
m_nFocusItem = nItem;
|
|
m_nFocusSubItem = m_setSelectedItems.size() > 1 ? NULL_SUBITEM : nSubItem;
|
|
}
|
|
}
|
|
|
|
// Are we adding this item to the select sequence?
|
|
if ( bSelectItem )
|
|
{
|
|
bEnsureVisible = TRUE;
|
|
|
|
if ( bSelectRange )
|
|
{
|
|
if ( m_nFirstSelected == NULL_ITEM )
|
|
m_nFirstSelected = nItem;
|
|
|
|
for ( int nSelectedItem = min( m_nFirstSelected, nItem ); nSelectedItem <= max( m_nFirstSelected, nItem ); nSelectedItem++ )
|
|
m_setSelectedItems.insert( nSelectedItem );
|
|
}
|
|
else
|
|
{
|
|
m_nFirstSelected = nItem;
|
|
m_setSelectedItems.insert( nItem );
|
|
}
|
|
|
|
m_nFocusItem = nItem;
|
|
m_nFocusSubItem = m_setSelectedItems.size() > 1 ? NULL_SUBITEM : nSubItem;
|
|
|
|
// Notify parent of selected item
|
|
NotifyParent( m_nFocusItem, m_nFocusSubItem, LCN_SELECTED );
|
|
}
|
|
|
|
// Start visible timer (scrolls list to partially hidden item)
|
|
if ( !IsItemVisible( nItem, m_setSelectedItems.size() > 1 ? NULL_SUBITEM : nSubItem, FALSE ) )
|
|
SetTimer( ITEM_VISIBLE_TIMER, ITEM_VISIBLE_PERIOD );
|
|
else if ( m_nFocusItem != NULL_ITEM && m_nFocusSubItem != NULL_SUBITEM )
|
|
EditItem( m_nFocusItem, m_nFocusSubItem );
|
|
|
|
return Invalidate();
|
|
}
|
|
|
|
BOOL IsSelected( int nItem )
|
|
{
|
|
set < int >::iterator posSelectedItem = m_setSelectedItems.find( nItem );
|
|
return ( posSelectedItem != m_setSelectedItems.end() );
|
|
}
|
|
|
|
BOOL GetSelectedItems( CListArray < int >& aSelectedItems )
|
|
{
|
|
aSelectedItems.RemoveAll();
|
|
for ( set < int >::iterator posSelectedItem = m_setSelectedItems.begin(); posSelectedItem != m_setSelectedItems.end(); ++posSelectedItem )
|
|
aSelectedItems.Add( *posSelectedItem );
|
|
return !aSelectedItems.IsEmpty();
|
|
}
|
|
|
|
BOOL SetFocusItem( int nItem, int nSubItem = NULL_SUBITEM )
|
|
{
|
|
m_nFocusItem = nItem;
|
|
m_nFocusSubItem = nSubItem;
|
|
return EnsureItemVisible( m_nFocusItem, m_nFocusSubItem );
|
|
}
|
|
|
|
BOOL GetFocusItem( int& nItem, int& nSubItem )
|
|
{
|
|
nItem = IsSelected( m_nFocusItem ) ? m_nFocusItem : ( m_setSelectedItems.empty() ? NULL_ITEM : *m_setSelectedItems.begin() );
|
|
nSubItem = !m_bFocusSubItem || nItem == NULL_ITEM ? NULL_SUBITEM : m_nFocusSubItem;
|
|
return ( nItem != NULL_ITEM );
|
|
}
|
|
|
|
int GetFocusItem()
|
|
{
|
|
return IsSelected( m_nFocusItem ) ? m_nFocusItem : ( m_setSelectedItems.empty() ? NULL_ITEM : *m_setSelectedItems.begin() );
|
|
}
|
|
|
|
BOOL HitTestHeader( CPoint point, int& nColumn, UINT& nFlags )
|
|
{
|
|
// Reset hit test flags
|
|
nFlags = HITTEST_FLAG_NONE;
|
|
|
|
if ( !m_bShowHeader )
|
|
return FALSE;
|
|
|
|
CRect rcClient;
|
|
if ( !GetClientRect( rcClient ) )
|
|
return FALSE;
|
|
|
|
// Are we over the header?
|
|
if ( point.y < rcClient.top || point.y > m_nHeaderHeight )
|
|
return FALSE;
|
|
|
|
int nDividerPos = 0;
|
|
int nColumnCount = GetColumnCount();
|
|
|
|
// Get hit test subitem
|
|
for ( nColumn = 0; nColumn < nColumnCount; nColumn++ )
|
|
{
|
|
int nColumnWidth = GetColumnWidth( nColumn );
|
|
nDividerPos += nColumnWidth;
|
|
|
|
// Offset divider position with current scroll position
|
|
int nRelativePos = nDividerPos - GetScrollPos( SB_HORZ );
|
|
|
|
// Are we over the divider zone?
|
|
if ( point.x >= nRelativePos - DRAG_HEADER_OFFSET - 1 && point.x <= nRelativePos + DRAG_HEADER_OFFSET )
|
|
{
|
|
nFlags |= HITTEST_FLAG_HEADER_DIVIDER;
|
|
|
|
// Are we to the left of the divider (or over last column divider)?
|
|
if ( ( point.x >= nRelativePos - DRAG_HEADER_OFFSET - 1 && point.x < nRelativePos ) || nColumn + 1 >= nColumnCount - 1 )
|
|
{
|
|
nFlags |= HITTEST_FLAG_HEADER_LEFT;
|
|
return TRUE;
|
|
}
|
|
|
|
// Find last zero-length column after this column
|
|
for ( int nNextColumn = nColumn + 1; nNextColumn < nColumnCount; nNextColumn++ )
|
|
{
|
|
if ( GetColumnWidth( nNextColumn ) > 0 )
|
|
break;
|
|
nColumn = nNextColumn;
|
|
}
|
|
|
|
nFlags |= HITTEST_FLAG_HEADER_RIGHT;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Are we over a column?
|
|
if ( point.x > nRelativePos - nColumnWidth && point.x < nRelativePos )
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL HitTest( CPoint point, int& nItem, int& nSubItem )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
// Are we over the header?
|
|
if ( point.y < ( m_bShowHeader ? m_nHeaderHeight : 0 ) )
|
|
return FALSE;
|
|
|
|
// Calculate hit test item
|
|
nItem = GetTopItem() + (int)( ( point.y - ( m_bShowHeader ? m_nHeaderHeight : 0 ) ) / m_nItemHeight );
|
|
|
|
if ( nItem < 0 || nItem >= pT->GetItemCount() )
|
|
return FALSE;
|
|
|
|
int nTotalWidth = 0;
|
|
int nColumnCount = GetColumnCount();
|
|
|
|
// Get hit test subitem
|
|
for ( nSubItem = 0; nSubItem < nColumnCount; nSubItem++ )
|
|
{
|
|
int nColumnWidth = GetColumnWidth( nSubItem );
|
|
nTotalWidth += nColumnWidth;
|
|
|
|
// Offset position with current scroll position
|
|
int nRelativePos = nTotalWidth - GetScrollPos( SB_HORZ );
|
|
|
|
// Are we over a subitem?
|
|
if ( point.x > nRelativePos - nColumnWidth && point.x < nRelativePos )
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL AutoSizeColumn( int nColumn )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
CListColumn listColumn;
|
|
if ( !GetColumn( nColumn, listColumn ) || listColumn.m_bFixed )
|
|
return FALSE;
|
|
|
|
CClientDC dcClient( m_hWnd );
|
|
HFONT hOldFont = dcClient.SelectFont( m_fntListFont );
|
|
|
|
// Set to column text width if zero-length
|
|
CSize sizeExtent;
|
|
if ( !dcClient.GetTextExtent( listColumn.m_strText.c_str(), -1, &sizeExtent ) )
|
|
return FALSE;
|
|
|
|
int nMaxWidth = sizeExtent.cx + ITEM_WIDTH_MARGIN;
|
|
|
|
CSize sizeIcon = 0;
|
|
if ( !m_ilItemImages.IsNull() )
|
|
m_ilItemImages.GetIconSize( sizeIcon );
|
|
|
|
// Calculate maximum column width required
|
|
for ( int nItem = 0; nItem < pT->GetItemCount(); nItem++ )
|
|
{
|
|
if ( !dcClient.GetTextExtent( pT->GetItemText( nItem, listColumn.m_nIndex ), -1, &sizeExtent ) )
|
|
return FALSE;
|
|
|
|
if ( !m_ilItemImages.IsNull() && pT->GetItemImage( nItem, listColumn.m_nIndex ) != ITEM_IMAGE_NONE )
|
|
sizeExtent.cx += sizeIcon.cx;
|
|
|
|
nMaxWidth = max( nMaxWidth, (int)sizeExtent.cx + ITEM_WIDTH_MARGIN );
|
|
}
|
|
|
|
dcClient.SelectFont( hOldFont );
|
|
|
|
return SetColumnWidth( nColumn, nMaxWidth );
|
|
}
|
|
|
|
void ResizeColumn( BOOL bColumnScroll = FALSE )
|
|
{
|
|
HideTitleTip();
|
|
|
|
int nCurrentPos = GET_X_LPARAM( GetMessagePos() );
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
int nScrollLimit = GetTotalWidth() - rcClient.Width();
|
|
|
|
if ( bColumnScroll )
|
|
{
|
|
// Have we finished scrolling list to accommodate new column size?
|
|
if ( !m_bColumnSizing || !m_bEnableHorizScroll || nCurrentPos - m_nStartScrollPos > 0 )
|
|
{
|
|
KillTimer( RESIZE_COLUMN_TIMER );
|
|
|
|
// Reset resize start point
|
|
m_nStartPos = nCurrentPos;
|
|
m_bResizeTimer = FALSE;
|
|
}
|
|
else if ( nCurrentPos < m_nStartPos && GetScrollPos( SB_HORZ ) >= nScrollLimit )
|
|
{
|
|
// Reset start column size
|
|
m_nStartSize = max( GetColumnWidth( m_nColumnSizing ) + ( nCurrentPos - m_nStartScrollPos ), 0 );
|
|
|
|
// Resize column
|
|
SetColumnWidth( m_nColumnSizing, m_nStartSize );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int nColumnSize = max( m_nStartSize + ( nCurrentPos - m_nStartPos ), 0 );
|
|
|
|
// Are we scrolled fully to the right and wanting to reduce the size of a column?
|
|
if ( m_bEnableHorizScroll && GetScrollPos( SB_HORZ ) >= nScrollLimit && nColumnSize < GetColumnWidth( m_nColumnSizing ) )
|
|
{
|
|
if ( !m_bResizeTimer )
|
|
{
|
|
// Only start the scroll timer once
|
|
m_bResizeTimer = TRUE;
|
|
|
|
// Set new start scroll position
|
|
m_nStartScrollPos = nCurrentPos;
|
|
|
|
// Start column resize / scroll timer
|
|
SetTimer( RESIZE_COLUMN_TIMER, RESIZE_COLUMN_PERIOD );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Resizing is done in scroll timer (if started)
|
|
if ( !m_bResizeTimer )
|
|
SetColumnWidth( m_nColumnSizing, nColumnSize );
|
|
}
|
|
}
|
|
}
|
|
|
|
void DragColumn()
|
|
{
|
|
HideTitleTip();
|
|
|
|
CRect rcColumn;
|
|
if ( !GetColumnRect( m_nHighlightColumn, rcColumn ) )
|
|
return;
|
|
|
|
CRect rcHeaderItem( rcColumn );
|
|
rcHeaderItem.MoveToXY( 0, 0 );
|
|
|
|
CListColumn listColumn;
|
|
if ( !GetColumn( m_nHighlightColumn, listColumn ) )
|
|
return;
|
|
|
|
// Store drag column
|
|
m_nDragColumn = m_nHighlightColumn;
|
|
|
|
CClientDC dcClient( m_hWnd );
|
|
|
|
CDC dcHeader;
|
|
dcHeader.CreateCompatibleDC( dcClient );
|
|
|
|
int nContextState = dcHeader.SaveDC();
|
|
|
|
// Create drag header bitmap
|
|
CBitmapHandle bmpHeader;
|
|
bmpHeader.CreateCompatibleBitmap( dcClient, rcHeaderItem.Width(), rcHeaderItem.Height() );
|
|
dcHeader.SelectBitmap( bmpHeader );
|
|
|
|
dcHeader.SetBkColor( m_rgbHeaderBackground );
|
|
dcHeader.ExtTextOut( rcHeaderItem.left, rcHeaderItem.top, ETO_OPAQUE, rcHeaderItem, _T( "" ), 0, nullptr );
|
|
dcHeader.Draw3dRect( rcHeaderItem, m_rgbHeaderBorder, m_rgbHeaderShadow );
|
|
|
|
CRect rcHeaderText( rcHeaderItem );
|
|
rcHeaderText.left += m_nHighlightColumn == 0 ? 4 : 3;
|
|
rcHeaderText.OffsetRect( 0, 1 );
|
|
|
|
// Margin header text
|
|
rcHeaderText.DeflateRect( 4, 0, 5, 0 );
|
|
|
|
// Has this header item an associated image?
|
|
if ( listColumn.m_nImage != ITEM_IMAGE_NONE )
|
|
{
|
|
CSize sizeIcon;
|
|
m_ilListItems.GetIconSize( sizeIcon );
|
|
|
|
CRect rcHeaderImage;
|
|
rcHeaderImage.left = listColumn.m_strText.empty() ? ( ( rcHeaderText.left + rcHeaderText.right ) / 2 ) - ( sizeIcon.cx / 2 ) - ( 0 ) : rcHeaderText.left;
|
|
rcHeaderImage.right = min( rcHeaderImage.left + sizeIcon.cx, rcHeaderItem.right );
|
|
rcHeaderImage.top = ( ( rcHeaderItem.top + rcHeaderItem.bottom ) / 2 ) - ( sizeIcon.cy / 2 );
|
|
rcHeaderImage.bottom = min( rcHeaderImage.top + sizeIcon.cy, rcHeaderItem.bottom );
|
|
|
|
m_ilListItems.DrawEx( listColumn.m_nImage, dcHeader, rcHeaderImage, CLR_DEFAULT, CLR_DEFAULT, ILD_TRANSPARENT );
|
|
|
|
// Offset header text (for image)
|
|
rcHeaderText.left += sizeIcon.cx + 4;
|
|
}
|
|
|
|
dcHeader.SelectFont( m_fntListFont );
|
|
dcHeader.SetTextColor( m_rgbHeaderText );
|
|
dcHeader.SetBkMode( TRANSPARENT );
|
|
|
|
UINT nFormat = DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS;
|
|
|
|
if ( listColumn.m_nFlags & ITEM_FLAGS_CENTRE )
|
|
nFormat |= DT_CENTER;
|
|
else if ( listColumn.m_nFlags & ITEM_FLAGS_RIGHT )
|
|
nFormat |= DT_RIGHT;
|
|
else
|
|
nFormat |= DT_LEFT;
|
|
|
|
// Draw header text
|
|
if ( !listColumn.m_strText.empty() )
|
|
dcHeader.DrawText( listColumn.m_strText.c_str(), (int)listColumn.m_strText.length(), rcHeaderText, nFormat );
|
|
|
|
dcHeader.RestoreDC( nContextState );
|
|
|
|
SHDRAGIMAGE shDragImage;
|
|
ZeroMemory( &shDragImage, sizeof( SHDRAGIMAGE ) );
|
|
|
|
shDragImage.sizeDragImage.cx = rcHeaderItem.Width();
|
|
shDragImage.sizeDragImage.cy = rcHeaderItem.Height();
|
|
shDragImage.ptOffset.x = rcColumn.Width() / 2;
|
|
shDragImage.ptOffset.y = rcColumn.Height() / 2;
|
|
shDragImage.hbmpDragImage = bmpHeader;
|
|
shDragImage.crColorKey = m_rgbBackground;
|
|
|
|
// Start header drag operation
|
|
m_oleDragDrop.DoDragDrop( &shDragImage, DROPEFFECT_MOVE );
|
|
|
|
// Hide drop arrows after moving column
|
|
m_wndDropArrows.Hide();
|
|
|
|
if ( m_bButtonDown )
|
|
{
|
|
ReleaseCapture();
|
|
m_bButtonDown = FALSE;
|
|
m_bBeginSelect = FALSE;
|
|
m_ptDownPoint = 0;
|
|
m_ptSelectPoint = 0;
|
|
}
|
|
|
|
// Finish moving a column
|
|
if ( m_nHighlightColumn != NULL_COLUMN )
|
|
{
|
|
m_nHighlightColumn = NULL_COLUMN;
|
|
InvalidateHeader();
|
|
}
|
|
|
|
m_nDragColumn = NULL_COLUMN;
|
|
}
|
|
|
|
BOOL DropColumn( CPoint point )
|
|
{
|
|
if ( !m_bShowHeader )
|
|
return FALSE;
|
|
|
|
m_nHotDivider = NULL_COLUMN;
|
|
m_nHotColumn = NULL_COLUMN;
|
|
UINT nHeaderFlags = HITTEST_FLAG_NONE;
|
|
|
|
// Are we over the header?
|
|
if ( HitTestHeader( point, m_nHotColumn, nHeaderFlags ) )
|
|
{
|
|
CRect rcColumn;
|
|
if ( !GetColumnRect( m_nHotColumn, rcColumn ) )
|
|
return FALSE;
|
|
m_nHotDivider = point.x < ( ( rcColumn.left + rcColumn.right ) / 2 ) ? m_nHotColumn : m_nHotColumn + 1;
|
|
|
|
if ( m_nHotDivider == m_nDragColumn || m_nHotDivider == m_nDragColumn + 1 )
|
|
m_nHotDivider = NULL_COLUMN;
|
|
}
|
|
|
|
if ( m_nHotDivider != NULL_COLUMN )
|
|
{
|
|
CRect rcHeader;
|
|
GetClientRect( rcHeader );
|
|
rcHeader.bottom = m_nHeaderHeight;
|
|
|
|
CPoint ptDivider( 0, rcHeader.Height() / 2 );
|
|
|
|
CRect rcColumn;
|
|
int nColumnCount = GetColumnCount();
|
|
|
|
// Set closest divider position
|
|
if ( GetColumnRect( m_nHotDivider < nColumnCount ? m_nHotDivider : nColumnCount - 1, rcColumn ) )
|
|
ptDivider.x = m_nHotDivider < nColumnCount ? rcColumn.left : rcColumn.right;
|
|
|
|
ClientToScreen( &ptDivider );
|
|
|
|
// Track drop window
|
|
m_wndDropArrows.Show( ptDivider );
|
|
return TRUE;
|
|
}
|
|
|
|
m_wndDropArrows.Hide();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SortColumn( int nColumn )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
if ( !m_bShowHeader || !m_bShowSort )
|
|
return FALSE;
|
|
|
|
int nSortIndex = GetColumnIndex( nColumn );
|
|
|
|
CWaitCursor curWait;
|
|
|
|
if ( nSortIndex != m_nSortColumn )
|
|
{
|
|
// Sort by new column
|
|
m_bSortAscending = TRUE;
|
|
m_nSortColumn = nSortIndex;
|
|
pT->SortItems( m_nSortColumn, m_bSortAscending );
|
|
}
|
|
else
|
|
{
|
|
// Toggle sort order if sorting same column
|
|
m_bSortAscending = !m_bSortAscending;
|
|
pT->ReverseItems();
|
|
}
|
|
|
|
return ResetSelected();
|
|
}
|
|
|
|
BOOL GetSortColumn( int& nColumn, BOOL& bAscending )
|
|
{
|
|
if ( !m_bShowHeader || !m_bShowSort || m_nSortColumn == NULL_COLUMN )
|
|
return FALSE;
|
|
nColumn = m_nSortColumn;
|
|
bAscending = m_bSortAscending;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL DragItem()
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL GroupSelect( CPoint point )
|
|
{
|
|
HideTitleTip();
|
|
|
|
int nHorzScroll = GetScrollPos( SB_HORZ );
|
|
int nVertScroll = GetScrollPos( SB_VERT );
|
|
|
|
m_rcGroupSelect.left = min( m_ptSelectPoint.x, point.x + nHorzScroll );
|
|
m_rcGroupSelect.right = max( m_ptSelectPoint.x, point.x + nHorzScroll );
|
|
m_rcGroupSelect.top = min( m_ptSelectPoint.y, point.y + nVertScroll );
|
|
m_rcGroupSelect.bottom = max( m_ptSelectPoint.y, point.y + nVertScroll );
|
|
|
|
if ( m_rcGroupSelect.IsRectEmpty() )
|
|
return FALSE;
|
|
|
|
// Select items in group
|
|
AutoSelect( point );
|
|
|
|
// Start auto scroll timer
|
|
SetTimer( ITEM_AUTOSCROLL_TIMER, ITEM_SCROLL_PERIOD );
|
|
|
|
DWORD dwCurrentTick = GetTickCount();
|
|
|
|
// Timer messages are a low priority, therefore we need to simulate the timer when moving the mouse
|
|
if ( ( dwCurrentTick - m_dwScrollTick ) > ITEM_SCROLL_PERIOD - 10 )
|
|
{
|
|
if ( AutoScroll( point ) )
|
|
m_dwScrollTick = dwCurrentTick;
|
|
}
|
|
|
|
// Redraw list immediately
|
|
return RedrawWindow();
|
|
}
|
|
|
|
void AutoSelect( CPoint /*point*/ )
|
|
{
|
|
m_setSelectedItems.clear();
|
|
|
|
if ( m_rcGroupSelect.left < GetTotalWidth() )
|
|
{
|
|
int nHorzScroll = GetScrollPos( SB_HORZ );
|
|
int nVertScroll = GetScrollPos( SB_VERT );
|
|
|
|
CRect rcGroupSelect( m_rcGroupSelect );
|
|
rcGroupSelect.OffsetRect( -nHorzScroll, -nVertScroll );
|
|
|
|
int nTopItem = GetTopItem();
|
|
int nLastItem = nTopItem + ( ( rcGroupSelect.bottom - ( m_bShowHeader ? m_nHeaderHeight : 0 ) ) / m_nItemHeight );
|
|
nTopItem += ( ( rcGroupSelect.top - ( m_bShowHeader ? m_nHeaderHeight : 0 ) ) / m_nItemHeight ) - ( ( rcGroupSelect.top < 0 ) ? 1 : 0 );
|
|
|
|
for ( int nItem = nTopItem; nItem <= nLastItem; nItem++ )
|
|
{
|
|
if ( m_setSelectedItems.empty() )
|
|
m_nFirstSelected = nItem;
|
|
m_setSelectedItems.insert( nItem );
|
|
|
|
m_nFocusItem = nItem;
|
|
m_nFocusSubItem = NULL_SUBITEM;
|
|
}
|
|
}
|
|
|
|
if ( m_setSelectedItems.empty() )
|
|
m_nFirstSelected = NULL_ITEM;
|
|
}
|
|
|
|
BOOL AutoScroll( CPoint point )
|
|
{
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
rcClient.top = ( m_bShowHeader ? m_nHeaderHeight : 0 );
|
|
|
|
int nHorzScroll = GetScrollPos( SB_HORZ );
|
|
int nVertScroll = GetScrollPos( SB_VERT );
|
|
|
|
BOOL bAutoScroll = FALSE;
|
|
|
|
if ( point.y < rcClient.top )
|
|
{
|
|
SendMessage( WM_VSCROLL, MAKEWPARAM( SB_LINEUP, 0 ) );
|
|
int nAutoScroll = GetScrollPos( SB_VERT );
|
|
if ( nVertScroll != nAutoScroll )
|
|
{
|
|
m_rcGroupSelect.top = rcClient.top + nAutoScroll - 1;
|
|
m_rcGroupSelect.bottom = max( m_ptSelectPoint.y, point.y + nAutoScroll - 1 );
|
|
bAutoScroll = TRUE;
|
|
}
|
|
}
|
|
if ( point.y > rcClient.bottom )
|
|
{
|
|
SendMessage( WM_VSCROLL, MAKEWPARAM( SB_LINEDOWN, 0 ) );
|
|
int nAutoScroll = GetScrollPos( SB_VERT );
|
|
if ( nVertScroll != nAutoScroll )
|
|
{
|
|
m_rcGroupSelect.top = min( m_ptSelectPoint.y, point.y + nAutoScroll + 1 );
|
|
m_rcGroupSelect.bottom = rcClient.bottom + nAutoScroll + 1;
|
|
bAutoScroll = TRUE;
|
|
}
|
|
}
|
|
if ( point.x < rcClient.left )
|
|
{
|
|
SendMessage( WM_HSCROLL, MAKEWPARAM( SB_LINELEFT, 0 ) );
|
|
int nAutoScroll = GetScrollPos( SB_HORZ );
|
|
if ( nHorzScroll != nAutoScroll )
|
|
{
|
|
m_rcGroupSelect.left = rcClient.left + nAutoScroll - 1;
|
|
m_rcGroupSelect.right = max( m_ptSelectPoint.x, point.x + nAutoScroll - 1 );
|
|
bAutoScroll = TRUE;
|
|
}
|
|
}
|
|
if ( point.x > rcClient.right )
|
|
{
|
|
SendMessage( WM_HSCROLL, MAKEWPARAM( SB_LINERIGHT, 0 ) );
|
|
int nAutoScroll = GetScrollPos( SB_HORZ );
|
|
if ( nHorzScroll != nAutoScroll )
|
|
{
|
|
m_rcGroupSelect.left = min( m_ptSelectPoint.x, point.x + nAutoScroll + 1 );
|
|
m_rcGroupSelect.right = rcClient.right + nAutoScroll + 1;
|
|
bAutoScroll = TRUE;
|
|
}
|
|
}
|
|
|
|
// Was the scrolling performed?
|
|
return bAutoScroll;
|
|
}
|
|
|
|
BOOL BeginScroll( int nBeginItem, int nEndItem )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
// Any scroll required?
|
|
if ( nBeginItem == nEndItem )
|
|
return FALSE;
|
|
|
|
// Calculate scroll offset
|
|
m_nScrollOffset = abs( nEndItem - nBeginItem ) * m_nItemHeight;
|
|
m_nScrollUnit = min( max( m_nScrollOffset / m_nItemHeight, ITEM_SCROLL_UNIT_MIN ), ITEM_SCROLL_UNIT_MAX );
|
|
m_nScrollDelta = ( m_nScrollOffset - m_nScrollUnit ) / m_nScrollUnit;
|
|
m_bScrollDown = ( nBeginItem < nEndItem );
|
|
|
|
CClientDC dcClient( m_hWnd );
|
|
|
|
CDC dcScrollList;
|
|
dcScrollList.CreateCompatibleDC( dcClient );
|
|
|
|
int nContextState = dcScrollList.SaveDC();
|
|
|
|
CRect rcScrollList;
|
|
GetClientRect( rcScrollList );
|
|
rcScrollList.bottom = ( GetCountPerPage() + abs( nEndItem - nBeginItem ) ) * m_nItemHeight;
|
|
|
|
if ( !m_bmpScrollList.IsNull() )
|
|
m_bmpScrollList.DeleteObject();
|
|
m_bmpScrollList.CreateCompatibleBitmap( dcClient, rcScrollList.Width(), rcScrollList.Height() );
|
|
dcScrollList.SelectBitmap( m_bmpScrollList );
|
|
|
|
pT->DrawBkgnd( dcScrollList.m_hDC );
|
|
|
|
CRect rcItem;
|
|
rcItem.left = -GetScrollPos( SB_HORZ );
|
|
rcItem.right = GetTotalWidth();
|
|
rcItem.top = 0;
|
|
rcItem.bottom = rcItem.top;
|
|
|
|
// Draw all visible items into bitmap
|
|
for ( int nItem = min( nBeginItem, nEndItem ); nItem < pT->GetItemCount(); rcItem.top = rcItem.bottom, nItem++ )
|
|
{
|
|
rcItem.bottom = rcItem.top + m_nItemHeight;
|
|
|
|
if ( rcItem.top > rcScrollList.bottom )
|
|
break;
|
|
|
|
// May be implemented in a derived class
|
|
pT->DrawItem( dcScrollList.m_hDC, nItem, rcItem );
|
|
}
|
|
|
|
dcScrollList.RestoreDC( nContextState );
|
|
|
|
ScrollList();
|
|
|
|
// Start scrolling timer
|
|
SetTimer( ITEM_SCROLL_TIMER, ITEM_SCROLL_PERIOD );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL EndScroll()
|
|
{
|
|
KillTimer( ITEM_SCROLL_TIMER );
|
|
if ( !m_bmpScrollList.IsNull() )
|
|
m_bmpScrollList.DeleteObject();
|
|
return Invalidate();
|
|
}
|
|
|
|
BOOL ScrollList()
|
|
{
|
|
if ( m_nScrollOffset <= m_nScrollUnit )
|
|
m_nScrollOffset--;
|
|
else
|
|
{
|
|
m_nScrollOffset -= m_nScrollDelta;
|
|
if ( m_nScrollOffset < m_nScrollDelta )
|
|
m_nScrollOffset = m_nScrollUnit;
|
|
}
|
|
|
|
if ( m_bmpScrollList.IsNull() || m_nScrollOffset < 0 )
|
|
return FALSE;
|
|
|
|
CClientDC dcClient( m_hWnd );
|
|
|
|
CDC dcScrollList;
|
|
dcScrollList.CreateCompatibleDC( dcClient );
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
rcClient.top = ( m_bShowHeader ? m_nHeaderHeight : 0 );
|
|
|
|
HBITMAP hOldBitmap = dcScrollList.SelectBitmap( m_bmpScrollList );
|
|
|
|
CSize sizScrollBitmap;
|
|
m_bmpScrollList.GetSize( sizScrollBitmap );
|
|
|
|
// Draw scrolled list
|
|
dcClient.BitBlt( 0, rcClient.top, rcClient.Width(), rcClient.Height(), dcScrollList, 0, m_bScrollDown ? ( sizScrollBitmap.cy - ( GetCountPerPage() * m_nItemHeight ) - m_nScrollOffset ) : m_nScrollOffset, SRCCOPY );
|
|
|
|
dcScrollList.SelectBitmap( hOldBitmap );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL EditItem( int nItem, int nSubItem = NULL_SUBITEM )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
if ( !EnsureItemVisible( nItem, nSubItem ) )
|
|
return FALSE;
|
|
|
|
if ( GetFocus() != m_hWnd )
|
|
return FALSE;
|
|
|
|
CRect rcSubItem;
|
|
if ( !GetItemRect( nItem, nSubItem, rcSubItem ) )
|
|
return FALSE;
|
|
|
|
int nIndex = GetColumnIndex( nSubItem );
|
|
if ( pT->GetItemFlags( nItem, nIndex ) & ITEM_FLAGS_READ_ONLY )
|
|
return TRUE;
|
|
|
|
switch ( pT->GetItemFormat( nItem, nIndex ) )
|
|
{
|
|
case ITEM_FORMAT_EDIT: m_bEditItem = TRUE;
|
|
if ( !RedrawWindow() )
|
|
return FALSE;
|
|
if ( !m_wndItemEdit.Create( m_hWnd, nItem, nSubItem, rcSubItem, pT->GetItemFlags( nItem, nIndex ), pT->GetItemText( nItem, nIndex ), pT->GetItemMaxEditLen(nItem, nIndex ) ) )
|
|
return FALSE;
|
|
m_wndItemEdit.SetFont(GetItemFont(nItem, nIndex));
|
|
break;
|
|
case ITEM_FORMAT_DATETIME: {
|
|
m_bEditItem = TRUE;
|
|
if ( !RedrawWindow() )
|
|
return FALSE;
|
|
SYSTEMTIME stItemDate;
|
|
GetItemDate( nItem, nIndex, stItemDate );
|
|
if ( !m_wndItemDate.Create( m_hWnd, nItem, nSubItem, rcSubItem, pT->GetItemFlags( nItem, nIndex ), stItemDate ) )
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case ITEM_FORMAT_COMBO: {
|
|
m_bEditItem = TRUE;
|
|
if ( !RedrawWindow() )
|
|
return FALSE;
|
|
CListArray < stdstr > aComboList;
|
|
if ( !pT->GetItemComboList( nItem, nIndex, aComboList ) )
|
|
return FALSE;
|
|
if ( !m_wndItemCombo.Create( m_hWnd, nItem, nSubItem, rcSubItem, pT->GetItemFlags( nItem, nIndex ), pT->GetItemText( nItem, nIndex ), aComboList ) )
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
stdstr FormatDate( SYSTEMTIME& stFormatDate )
|
|
{
|
|
if ( stFormatDate.wYear == 0 )
|
|
return _T( "" );
|
|
|
|
// Format date to local format
|
|
TCHAR szDateFormat[ DATE_STRING ];
|
|
return GetDateFormat( LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stFormatDate, nullptr, szDateFormat, DATE_STRING ) == 0 ? _T( "" ) : szDateFormat;
|
|
}
|
|
|
|
stdstr FormatTime( SYSTEMTIME& stFormatDate )
|
|
{
|
|
SYSTEMTIME stFormatTime = stFormatDate;
|
|
stFormatTime.wYear = 0;
|
|
stFormatTime.wMonth = 0;
|
|
stFormatTime.wDay = 0;
|
|
|
|
// Format time to local format
|
|
TCHAR szTimeFormat[ DATE_STRING ];
|
|
return GetTimeFormat( LOCALE_USER_DEFAULT, 0, &stFormatTime, nullptr, szTimeFormat, DATE_STRING ) == 0 ? _T( "" ) : szTimeFormat;
|
|
}
|
|
|
|
void NotifyParent( int nItem, int nSubItem, int nMessage )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
CListNotify listNotify;
|
|
listNotify.m_hdrNotify.hwndFrom = pT->m_hWnd;
|
|
listNotify.m_hdrNotify.idFrom = pT->GetDlgCtrlID();
|
|
listNotify.m_hdrNotify.code = nMessage;
|
|
listNotify.m_nItem = nItem;
|
|
listNotify.m_nSubItem = GetColumnIndex( nSubItem );
|
|
listNotify.m_nExitChar = 0;
|
|
listNotify.m_lpszItemText = nullptr;
|
|
listNotify.m_lpItemDate = nullptr;
|
|
|
|
// Forward notification to parent
|
|
FORWARD_WM_NOTIFY( pT->GetParent(), listNotify.m_hdrNotify.idFrom, &listNotify.m_hdrNotify, ::SendMessage );
|
|
}
|
|
|
|
BOOL ShowTitleTip( CPoint point, int nItem, int nSubItem )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
// Do not show title tip if editing
|
|
if ( m_bEditItem )
|
|
return FALSE;
|
|
|
|
// Is title tip already shown for this item?
|
|
if ( nItem == m_nTitleTipItem && nSubItem == m_nTitleTipSubItem )
|
|
return FALSE;
|
|
|
|
CRect rcSubItem;
|
|
if ( !GetItemRect( nItem, nSubItem, rcSubItem ) )
|
|
{
|
|
HideTitleTip();
|
|
return FALSE;
|
|
}
|
|
|
|
int nIndex = GetColumnIndex( nSubItem );
|
|
CRect rcItemText( rcSubItem );
|
|
|
|
// margin item text
|
|
// rcItemText.left += nSubItem == 0 ? 4 : 3;
|
|
// rcItemText.DeflateRect( 4, 0 );
|
|
|
|
// Offset item text (for image)
|
|
if ( !m_ilItemImages.IsNull() && pT->GetItemImage( nItem, nIndex ) != ITEM_IMAGE_NONE )
|
|
{
|
|
CSize sizeIcon;
|
|
m_ilItemImages.GetIconSize( sizeIcon );
|
|
rcItemText.left += sizeIcon.cx + 4;
|
|
}
|
|
|
|
// Is current cursor position over item text (not over an icon)?
|
|
if ( !rcItemText.PtInRect( point ) )
|
|
return FALSE;
|
|
|
|
stdstr strItemText;
|
|
|
|
switch ( pT->GetItemFormat( nItem, nIndex ) )
|
|
{
|
|
case ITEM_FORMAT_CHECKBOX:
|
|
case ITEM_FORMAT_CHECKBOX_3STATE:
|
|
case ITEM_FORMAT_PROGRESS: break; // No title tip for checkboxes or progress
|
|
case ITEM_FORMAT_DATETIME: {
|
|
SYSTEMTIME stItemDate;
|
|
if ( !GetItemDate( nItem, nIndex, stItemDate ) )
|
|
break;
|
|
|
|
UINT nItemFlags = pT->GetItemFlags( nItem, nIndex );
|
|
if ( nItemFlags & ITEM_FLAGS_DATE_ONLY )
|
|
strItemText = FormatDate( stItemDate );
|
|
else if ( nItemFlags & ITEM_FLAGS_TIME_ONLY )
|
|
strItemText = FormatTime( stItemDate );
|
|
else
|
|
strItemText = FormatDate( stItemDate ) + _T( " " ) + FormatTime( stItemDate );
|
|
}
|
|
break;
|
|
default: strItemText = pT->GetItemText( nItem, nIndex );
|
|
break;
|
|
}
|
|
|
|
if ( strItemText.empty() )
|
|
{
|
|
HideTitleTip();
|
|
return FALSE;
|
|
}
|
|
|
|
ClientToScreen( rcItemText );
|
|
if ( !m_wndTitleTip.Show( rcItemText, strItemText.c_str(), pT->GetItemToolTip( nItem, nIndex ).c_str() ) )
|
|
{
|
|
HideTitleTip();
|
|
return FALSE;
|
|
}
|
|
|
|
m_nTitleTipItem = nItem;
|
|
m_nTitleTipSubItem = nSubItem;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL HideTitleTip( BOOL bResetItem = TRUE )
|
|
{
|
|
if ( bResetItem )
|
|
{
|
|
m_nTitleTipItem = NULL_ITEM;
|
|
m_nTitleTipSubItem = NULL_SUBITEM;
|
|
}
|
|
return m_wndTitleTip.Hide();
|
|
}
|
|
|
|
BEGIN_MSG_MAP_EX(CListImpl)
|
|
MSG_WM_CREATE(OnCreate)
|
|
MSG_WM_DESTROY(OnDestroy)
|
|
MSG_WM_SETFOCUS(OnSetFocus)
|
|
MSG_WM_KILLFOCUS(OnKillFocus)
|
|
MSG_WM_GETDLGCODE(OnGetDlgCode)
|
|
MSG_WM_SIZE(OnSize)
|
|
MSG_WM_HSCROLL(OnHScroll)
|
|
MSG_WM_VSCROLL(OnVScroll)
|
|
MSG_WM_CANCELMODE(OnCancelMode)
|
|
MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST,WM_MOUSELAST,OnMouseRange)
|
|
MSG_WM_LBUTTONDOWN(OnLButtonDown)
|
|
MSG_WM_LBUTTONUP(OnLButtonUp)
|
|
MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk)
|
|
MSG_WM_RBUTTONDOWN(OnRButtonDown)
|
|
MSG_WM_RBUTTONUP(OnRButtonUp)
|
|
MSG_WM_MOUSEMOVE(OnMouseMove)
|
|
#if(_WIN32_WINNT >= 0x0400)
|
|
MSG_WM_MOUSELEAVE(OnMouseLeave)
|
|
MSG_WM_MOUSEWHEEL(OnMouseWheel)
|
|
#endif
|
|
MSG_WM_TIMER(OnTimer)
|
|
MSG_WM_KEYDOWN(OnKeyDown)
|
|
MSG_WM_SYSKEYDOWN(OnSysKeyDown)
|
|
MSG_WM_SETTINGCHANGE(OnSettingsChange)
|
|
MSG_WM_SYSCOLORCHANGE(OnSettingsChange)
|
|
MSG_WM_FONTCHANGE(OnSettingsChange)
|
|
//MSG_WM_THEMECHANGED(OnSettingsChange)
|
|
NOTIFY_CODE_HANDLER_EX(LCN_ENDEDIT,OnEndEdit)
|
|
MESSAGE_HANDLER(WM_CTLCOLORLISTBOX, OnCtlColorListBox)
|
|
MESSAGE_HANDLER(WM_CTLCOLOREDIT, OnCtlColorListBox)
|
|
CHAIN_MSG_MAP(CDoubleBufferImpl< CListImpl >)
|
|
REFLECT_NOTIFICATIONS()
|
|
END_MSG_MAP()
|
|
|
|
int OnCreate( LPCREATESTRUCT /*lpCreateStruct*/ )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
return pT->Initialise() ? 0 : -1;
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
m_oleDragDrop.Revoke();
|
|
|
|
if ( m_wndDropArrows.IsWindow() )
|
|
m_wndDropArrows.DestroyWindow();
|
|
|
|
if ( m_wndTitleTip.IsWindow() )
|
|
m_wndTitleTip.DestroyWindow();
|
|
|
|
if ( m_wndItemEdit.IsWindow() )
|
|
m_wndItemEdit.DestroyWindow();
|
|
|
|
if ( m_wndItemCombo.IsWindow() )
|
|
m_wndItemCombo.DestroyWindow();
|
|
|
|
if ( m_wndItemDate.IsWindow() )
|
|
m_wndItemDate.DestroyWindow();
|
|
|
|
if ( m_ttToolTip.IsWindow() )
|
|
m_ttToolTip.DestroyWindow();
|
|
}
|
|
|
|
void OnSetFocus( HWND /*hOldWnd*/ )
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
void OnKillFocus( HWND /*hNewWnd*/ )
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
UINT OnGetDlgCode( LPMSG /*lpMessage*/ )
|
|
{
|
|
return DLGC_WANTARROWS | DLGC_WANTTAB | DLGC_WANTCHARS;
|
|
}
|
|
|
|
void OnSize( UINT /*nType*/, CSize /*size*/ )
|
|
{
|
|
// Stop any pending scroll
|
|
EndScroll();
|
|
|
|
// End any pending edit
|
|
if ( m_bEditItem )
|
|
SetFocus();
|
|
|
|
ResetScrollBars( SB_BOTH, -1, FALSE );
|
|
Invalidate();
|
|
}
|
|
|
|
void OnHScroll( int nSBCode, short /*nPos*/, HWND /*hScrollBar*/ )
|
|
{
|
|
// Stop any pending scroll
|
|
EndScroll();
|
|
|
|
// End any pending edit
|
|
if ( m_bEditItem )
|
|
SetFocus();
|
|
|
|
HideTitleTip();
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
|
|
int nScrollPos = GetScrollPos( SB_HORZ );
|
|
|
|
switch ( nSBCode )
|
|
{
|
|
case SB_LEFT: nScrollPos = 0;
|
|
break;
|
|
case SB_LINELEFT: nScrollPos = max( nScrollPos - ITEM_SCROLL_OFFSET, 0 );
|
|
break;
|
|
case SB_PAGELEFT: nScrollPos = max( nScrollPos - rcClient.Width(), 0 );
|
|
break;
|
|
case SB_RIGHT: nScrollPos = rcClient.Width();
|
|
break;
|
|
case SB_LINERIGHT: nScrollPos = min( nScrollPos + ITEM_SCROLL_OFFSET, GetTotalWidth() );
|
|
break;
|
|
case SB_PAGERIGHT: nScrollPos = min( nScrollPos + rcClient.Width(), GetTotalWidth() );
|
|
break;
|
|
case SB_THUMBPOSITION:
|
|
case SB_THUMBTRACK: {
|
|
SCROLLINFO infoScroll;
|
|
ZeroMemory( &infoScroll, sizeof( SCROLLINFO ) );
|
|
infoScroll.cbSize = sizeof( SCROLLINFO );
|
|
infoScroll.fMask = SIF_TRACKPOS;
|
|
|
|
// Get 32-bit scroll position
|
|
if ( !GetScrollInfo( SB_HORZ, &infoScroll ) )
|
|
return;
|
|
|
|
// Has scroll position changed?
|
|
if ( infoScroll.nTrackPos == nScrollPos )
|
|
return;
|
|
|
|
nScrollPos = infoScroll.nTrackPos;
|
|
}
|
|
break;
|
|
default: return;
|
|
}
|
|
|
|
ResetScrollBars( SB_HORZ, nScrollPos, FALSE );
|
|
Invalidate();
|
|
}
|
|
|
|
void OnVScroll( int nSBCode, short /*nPos*/, HWND /*hScrollBar*/ )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
// End any pending edit
|
|
if ( m_bEditItem )
|
|
SetFocus();
|
|
|
|
HideTitleTip();
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
rcClient.top = ( m_bShowHeader ? m_nHeaderHeight : 0 );
|
|
|
|
int nScrollPos = GetScrollPos( SB_VERT );
|
|
BOOL bScrollList = m_bSmoothScroll;
|
|
|
|
switch ( nSBCode )
|
|
{
|
|
case SB_TOP: nScrollPos = 0;
|
|
bScrollList = FALSE;
|
|
break;
|
|
case SB_LINEUP: nScrollPos = max( nScrollPos - m_nItemHeight, 0 );
|
|
break;
|
|
case SB_PAGEUP: nScrollPos = max( nScrollPos - rcClient.Height(), 0 );
|
|
break;
|
|
case SB_BOTTOM: nScrollPos = pT->GetItemCount() * m_nItemHeight;
|
|
bScrollList = FALSE;
|
|
break;
|
|
case SB_LINEDOWN: nScrollPos += m_nItemHeight;
|
|
break;
|
|
case SB_PAGEDOWN: nScrollPos += rcClient.Height();
|
|
break;
|
|
case SB_THUMBTRACK:
|
|
case SB_THUMBPOSITION: {
|
|
SCROLLINFO infoScroll;
|
|
ZeroMemory( &infoScroll, sizeof( SCROLLINFO ) );
|
|
infoScroll.cbSize = sizeof( SCROLLINFO );
|
|
infoScroll.fMask = SIF_TRACKPOS;
|
|
|
|
// Get 32-bit scroll position
|
|
if ( !GetScrollInfo( SB_VERT, &infoScroll ) )
|
|
return;
|
|
|
|
// Has scroll position changed?
|
|
if ( infoScroll.nTrackPos == nScrollPos )
|
|
return;
|
|
|
|
nScrollPos = infoScroll.nTrackPos;
|
|
bScrollList = FALSE;
|
|
}
|
|
break;
|
|
case SB_ENDSCROLL: m_bScrolling = FALSE;
|
|
default: return;
|
|
}
|
|
|
|
// Store original top item before scrolling
|
|
int nTopItem = GetTopItem();
|
|
ResetScrollBars( SB_VERT, nScrollPos, FALSE );
|
|
|
|
if ( bScrollList && !m_bScrolling )
|
|
m_bScrolling = BeginScroll( nTopItem, GetTopItem() );
|
|
else
|
|
EndScroll();
|
|
}
|
|
|
|
void OnCancelMode()
|
|
{
|
|
if ( m_bButtonDown )
|
|
ReleaseCapture();
|
|
|
|
HideTitleTip();
|
|
m_wndDropArrows.Hide();
|
|
m_nDragColumn = NULL_COLUMN;
|
|
m_nHighlightColumn = NULL_COLUMN;
|
|
}
|
|
|
|
LRESULT OnMouseRange( UINT nMessage, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if ( m_ttToolTip.IsWindow() )
|
|
{
|
|
MSG msgRelay = { m_hWnd, nMessage, wParam, lParam };
|
|
m_ttToolTip.RelayEvent( &msgRelay );
|
|
}
|
|
SetMsgHandled( FALSE );
|
|
return 0;
|
|
}
|
|
|
|
void OnLButtonDown( UINT nFlags, CPoint point )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
// TODO: Fix?
|
|
// We have a bug here with setcapture() and the tooltip notifying the parent with mouse messages.
|
|
// Hard to explain, but what I think happens is that this click is sent by the tooltip, which then
|
|
// releases capture, so it gets no more mouse events, thus not receiving the actual double click
|
|
// on the tool tip, and what results is two single clicks for this parent control.
|
|
// Not sure how to fix - Rowan
|
|
|
|
// Explorer doesn't actually hide the tip when clicked on, so we can remove this code and it shouldn't really matter
|
|
//HideTitleTip(FALSE);
|
|
|
|
m_bButtonDown = TRUE;
|
|
m_ptDownPoint = point;
|
|
m_ptSelectPoint = CPoint( point.x + GetScrollPos( SB_HORZ ), point.y + GetScrollPos( SB_VERT ) );
|
|
|
|
// Stop any pending scroll
|
|
EndScroll();
|
|
|
|
SetFocus();
|
|
|
|
// Capture all mouse input
|
|
SetCapture();
|
|
|
|
int nColumn = NULL_COLUMN;
|
|
UINT nHeaderFlags = HITTEST_FLAG_NONE;
|
|
|
|
// Are we over the header?
|
|
if ( HitTestHeader( point, nColumn, nHeaderFlags ) )
|
|
{
|
|
CListColumn listColumn;
|
|
if ( !GetColumn( nColumn, listColumn ) )
|
|
return;
|
|
|
|
if ( !listColumn.m_bFixed && ( nHeaderFlags & HITTEST_FLAG_HEADER_DIVIDER ) )
|
|
{
|
|
SetCursor( m_curDivider );
|
|
|
|
// Begin column resizing
|
|
m_bColumnSizing = TRUE;
|
|
m_nColumnSizing = nColumn;
|
|
m_nStartSize = listColumn.m_nWidth;
|
|
m_nStartPos = GET_X_LPARAM( GetMessagePos() );
|
|
}
|
|
else if (m_bSortEnabled) // Added by Rowan 05/12/2006
|
|
{
|
|
m_nHighlightColumn = nColumn;
|
|
InvalidateHeader();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int nItem = NULL_ITEM;
|
|
int nSubItem = NULL_SUBITEM;
|
|
|
|
if ( !HitTest( point, nItem, nSubItem ) )
|
|
{
|
|
m_nFirstSelected = NULL_ITEM;
|
|
m_bBeginSelect = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Do not begin group select from first columns
|
|
if ( !( nFlags & MK_SHIFT ) && !( nFlags & MK_CONTROL ) && nSubItem != 0 )
|
|
{
|
|
m_bBeginSelect = TRUE;
|
|
m_nFirstSelected = nItem;
|
|
}
|
|
|
|
// Only select item if not already selected
|
|
if ( ( nFlags & MK_SHIFT ) || ( nFlags & MK_CONTROL ) || !IsSelected( nItem ) || m_setSelectedItems.size() <= 1 )
|
|
SelectItem( nItem, nSubItem, nFlags );
|
|
|
|
int nIndex = GetColumnIndex( nSubItem );
|
|
if ( !( pT->GetItemFlags( nItem, nIndex ) & ITEM_FLAGS_READ_ONLY ) )
|
|
{
|
|
switch ( pT->GetItemFormat( nItem, nIndex ) )
|
|
{
|
|
case ITEM_FORMAT_CHECKBOX: m_bBeginSelect = FALSE;
|
|
pT->SetItemText( nItem, nIndex, _ttoi( pT->GetItemText( nItem, nIndex ) ) > 0 ? _T( "0" ) : _T( "1" ) );
|
|
NotifyParent( nItem, nSubItem, LCN_MODIFIED );
|
|
InvalidateItem( nItem );
|
|
break;
|
|
case ITEM_FORMAT_CHECKBOX_3STATE: {
|
|
m_bBeginSelect = FALSE;
|
|
|
|
int nCheckImage = _ttoi( pT->GetItemText( nItem, nIndex ) );
|
|
if ( nCheckImage < 0 )
|
|
pT->SetItemText( nItem, nIndex, _T( "0" ) );
|
|
else if ( nCheckImage > 0 )
|
|
pT->SetItemText( nItem, nIndex, _T( "-1" ) );
|
|
else
|
|
pT->SetItemText( nItem, nIndex, _T( "1" ) );
|
|
|
|
NotifyParent( nItem, nSubItem, LCN_MODIFIED );
|
|
InvalidateItem( nItem );
|
|
}
|
|
break;
|
|
case ITEM_FORMAT_HYPERLINK: m_bBeginSelect = FALSE;
|
|
SetCursor( m_curHyperLink );
|
|
NotifyParent( nItem, nSubItem, LCN_HYPERLINK );
|
|
break;
|
|
}
|
|
|
|
if (( pT->GetItemFlags( nItem, nIndex ) & ITEM_FLAGS_HIT_NOTIFY ) != 0)
|
|
{
|
|
NotifyParent( nItem, nSubItem, LCN_HITTEST );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnLButtonUp( UINT nFlags, CPoint point )
|
|
{
|
|
if ( m_bButtonDown )
|
|
ReleaseCapture();
|
|
|
|
// Finish resizing or selecting a column
|
|
if ( m_bColumnSizing || m_nHighlightColumn != NULL_COLUMN )
|
|
{
|
|
// Are we changing the sort order?
|
|
if ( !m_bColumnSizing && m_nHighlightColumn != NULL_COLUMN && m_bSortEnabled) // Changed by Rowan 05/12/2006
|
|
//if ( !m_bColumnSizing && m_nHighlightColumn != NULL_COLUMN)
|
|
SortColumn( m_nHighlightColumn );
|
|
|
|
m_bColumnSizing = FALSE;
|
|
m_nColumnSizing = NULL_COLUMN;
|
|
m_nHighlightColumn = NULL_COLUMN;
|
|
m_nStartSize = 0;
|
|
m_nStartPos = 0;
|
|
|
|
InvalidateHeader();
|
|
}
|
|
|
|
m_bBeginSelect = FALSE;
|
|
m_bButtonDown = FALSE;
|
|
m_ptDownPoint = 0;
|
|
m_ptSelectPoint = 0;
|
|
|
|
// Have we finished a group select?
|
|
if ( m_bGroupSelect )
|
|
{
|
|
m_bGroupSelect = FALSE;
|
|
Invalidate();
|
|
}
|
|
else
|
|
{
|
|
int nItem = NULL_ITEM;
|
|
int nSubItem = NULL_SUBITEM;
|
|
|
|
// Deselect item if current item is selected
|
|
if ( HitTest( point, nItem, nSubItem ) && IsSelected( nItem ) && m_setSelectedItems.size() > 1 && !( nFlags & MK_SHIFT ) && !( nFlags & MK_CONTROL ) )
|
|
SelectItem( nItem, nSubItem, nFlags );
|
|
|
|
// Notify parent of left-click item
|
|
NotifyParent( nItem, nSubItem, LCN_LEFTCLICK );
|
|
}
|
|
}
|
|
|
|
void OnLButtonDblClk( UINT /*nFlags*/, CPoint point )
|
|
{
|
|
|
|
HideTitleTip( FALSE );
|
|
|
|
// Handle double-clicks (for drawing)
|
|
SendMessage( WM_LBUTTONDOWN, 0, MAKELPARAM( point.x, point.y ) );
|
|
|
|
int nColumn = NULL_COLUMN;
|
|
UINT nHeaderFlags = HITTEST_FLAG_NONE;
|
|
|
|
// Resize column if double-click on a divider
|
|
if ( HitTestHeader( point, nColumn, nHeaderFlags ) && ( nHeaderFlags & HITTEST_FLAG_HEADER_DIVIDER ) )
|
|
AutoSizeColumn( nColumn );
|
|
|
|
int nItem = NULL_ITEM;
|
|
int nSubItem = NULL_SUBITEM;
|
|
|
|
HitTest( point, nItem, nSubItem );
|
|
|
|
//WriteTraceF(TraceInfo, "List Ctrl Double Click, Item: %d", nItem);
|
|
|
|
// Notify parent of double-clicked item
|
|
NotifyParent( nItem, nSubItem, LCN_DBLCLICK );
|
|
}
|
|
|
|
void OnRButtonDown( UINT nFlags, CPoint point )
|
|
{
|
|
// Stop any pending scroll
|
|
EndScroll();
|
|
|
|
SetFocus();
|
|
|
|
HideTitleTip( FALSE );
|
|
|
|
int nItem = NULL_ITEM;
|
|
int nSubItem = NULL_SUBITEM;
|
|
|
|
if (m_bRightClickSelect)
|
|
{
|
|
// Only select item if not already selected (deselect in OnLButtonUp)
|
|
if (HitTest(point, nItem, nSubItem) && !IsSelected(nItem))
|
|
SelectItem(nItem, nSubItem, nFlags);
|
|
}
|
|
}
|
|
|
|
void OnRButtonUp( UINT /*nFlags*/, CPoint point )
|
|
{
|
|
int nItem = NULL_ITEM;
|
|
int nSubItem = NULL_SUBITEM;
|
|
|
|
if ( !HitTest( point, nItem, nSubItem ) )
|
|
ResetSelected();
|
|
|
|
// Notify parent of right-click item
|
|
NotifyParent( nItem, nSubItem, LCN_RIGHTCLICK );
|
|
}
|
|
|
|
void OnMouseMove( UINT nFlags, CPoint point )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
if ( !( nFlags & MK_LBUTTON ) )
|
|
{
|
|
if ( m_bButtonDown )
|
|
ReleaseCapture();
|
|
|
|
m_bButtonDown = FALSE;
|
|
}
|
|
|
|
if ( !m_bMouseOver )
|
|
{
|
|
m_bMouseOver = TRUE;
|
|
|
|
TRACKMOUSEEVENT trkMouse;
|
|
trkMouse.cbSize = sizeof( TRACKMOUSEEVENT );
|
|
trkMouse.dwFlags = TME_LEAVE;
|
|
trkMouse.hwndTrack = m_hWnd;
|
|
|
|
// Notify when the mouse leaves button
|
|
_TrackMouseEvent( &trkMouse );
|
|
}
|
|
|
|
if ( m_bButtonDown )
|
|
{
|
|
// Are we resizing a column?
|
|
if ( m_bColumnSizing )
|
|
{
|
|
ResizeColumn();
|
|
return;
|
|
}
|
|
|
|
// Are we beginning to drag a column?
|
|
if ( m_nHighlightColumn != NULL_COLUMN && ( point.x < m_ptDownPoint.x - DRAG_HEADER_OFFSET || point.x > m_ptDownPoint.x + DRAG_HEADER_OFFSET || point.y < m_ptDownPoint.y - DRAG_HEADER_OFFSET || point.y > m_ptDownPoint.y + DRAG_HEADER_OFFSET ) )
|
|
{
|
|
DragColumn();
|
|
return;
|
|
}
|
|
|
|
// Are we beginning a group select or dragging an item?
|
|
if ( point.x < m_ptDownPoint.x - DRAG_ITEM_OFFSET || point.x > m_ptDownPoint.x + DRAG_ITEM_OFFSET || point.y < m_ptDownPoint.y - DRAG_ITEM_OFFSET || point.y > m_ptDownPoint.y + DRAG_ITEM_OFFSET )
|
|
{
|
|
if ( m_bBeginSelect || !m_bDragDrop )
|
|
m_bGroupSelect = ( !m_bSingleSelect && !m_bEditItem );
|
|
else
|
|
{
|
|
int nItem = NULL_ITEM;
|
|
int nSubItem = NULL_SUBITEM;
|
|
|
|
if ( HitTest( point, nItem, nSubItem ) )
|
|
{
|
|
// Select the drag item (if not already selected)
|
|
if ( !IsSelected( nItem ) )
|
|
SelectItem( nItem, nSubItem, nFlags );
|
|
|
|
// Begin drag item operation
|
|
pT->DragItem();
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( m_bGroupSelect )
|
|
{
|
|
GroupSelect( point );
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int nColumn = NULL_COLUMN;
|
|
UINT nHeaderFlags = HITTEST_FLAG_NONE;
|
|
|
|
// Are we over the header?
|
|
BOOL bHitTestHeader = HitTestHeader( point, nColumn, nHeaderFlags );
|
|
|
|
if ( bHitTestHeader )
|
|
{
|
|
HideTitleTip();
|
|
CListColumn listColumn;
|
|
if ( GetColumn( nColumn, listColumn ) && !listColumn.m_bFixed && ( nHeaderFlags & HITTEST_FLAG_HEADER_DIVIDER ) )
|
|
SetCursor( m_curDivider );
|
|
else
|
|
{
|
|
// Get tooltip for this item
|
|
stdstr strToolTip = pT->GetHeaderToolTip(nColumn);
|
|
if(!strToolTip.empty())
|
|
{
|
|
CRect rcColumn;
|
|
if(!GetColumnRect(nColumn, rcColumn))
|
|
return;
|
|
rcColumn.bottom = m_nHeaderHeight;
|
|
m_ttToolTip.Activate( TRUE );
|
|
m_ttToolTip.AddTool( m_hWnd, (LPCTSTR)strToolTip.c_str(), rcColumn,TOOLTIP_TOOL_ID);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
int nItem = NULL_ITEM;
|
|
int nSubItem = NULL_SUBITEM;
|
|
|
|
if ( !HitTest( point, nItem, nSubItem ) )
|
|
{
|
|
if ( m_nHotItem != NULL_ITEM && m_nHotSubItem != NULL_SUBITEM )
|
|
{
|
|
// Redraw old hot item
|
|
int nIndex = GetColumnIndex( m_nHotSubItem );
|
|
if ( pT->GetItemFormat( m_nHotItem, nIndex ) == ITEM_FORMAT_HYPERLINK && !( pT->GetItemFlags( m_nHotItem, nIndex ) & ITEM_FLAGS_READ_ONLY ) )
|
|
InvalidateItem( m_nHotItem, m_nHotSubItem );
|
|
}
|
|
|
|
m_ttToolTip.Activate( FALSE );
|
|
m_ttToolTip.DelTool( m_hWnd, TOOLTIP_TOOL_ID );
|
|
|
|
m_nHotItem = NULL_ITEM;
|
|
m_nHotSubItem = NULL_SUBITEM;
|
|
HideTitleTip();
|
|
}
|
|
else
|
|
{
|
|
// Has the hot item changed?
|
|
if ( nItem != m_nHotItem || nSubItem != m_nHotSubItem )
|
|
{
|
|
// Redraw old hot item
|
|
int nIndex = GetColumnIndex( m_nHotSubItem );
|
|
if ( pT->GetItemFormat( m_nHotItem, nIndex ) == ITEM_FORMAT_HYPERLINK && !( pT->GetItemFlags( m_nHotItem, nIndex ) & ITEM_FLAGS_READ_ONLY ) )
|
|
InvalidateItem( m_nHotItem, m_nHotSubItem );
|
|
|
|
m_nHotItem = nItem;
|
|
m_nHotSubItem = nSubItem;
|
|
|
|
NotifyParent(nItem, nSubItem, LCN_HOTITEMCHANGED);
|
|
}
|
|
|
|
int nIndex = GetColumnIndex( m_nHotSubItem );
|
|
UINT nItemFormat = pT->GetItemFormat( m_nHotItem, nIndex );
|
|
UINT nItemFlags = pT->GetItemFlags( m_nHotItem, nIndex );
|
|
|
|
// Draw new hot hyperlink item
|
|
if ( nItemFormat == ITEM_FORMAT_HYPERLINK && !( nItemFlags & ITEM_FLAGS_READ_ONLY ) )
|
|
{
|
|
InvalidateItem( m_nHotItem, m_nHotSubItem );
|
|
SetCursor( m_curHyperLink );
|
|
}
|
|
|
|
// Get tooltip for this item
|
|
stdstr strToolTip = pT->GetItemToolTip( m_nHotItem, nIndex );
|
|
|
|
CRect rcSubItem;
|
|
if ( !strToolTip.empty() && GetItemRect( m_nHotItem, rcSubItem ) )
|
|
{
|
|
m_ttToolTip.Activate( TRUE );
|
|
m_ttToolTip.AddTool( m_hWnd, (LPCTSTR)strToolTip.substr(0, SHRT_MAX ).c_str(), rcSubItem, TOOLTIP_TOOL_ID );
|
|
}
|
|
else
|
|
{
|
|
m_ttToolTip.Activate( FALSE );
|
|
m_ttToolTip.DelTool( m_hWnd, TOOLTIP_TOOL_ID );
|
|
}
|
|
|
|
// Show title tips for this item
|
|
ShowTitleTip( point, m_nHotItem, m_nHotSubItem );
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnMouseLeave()
|
|
{
|
|
m_bMouseOver = FALSE;
|
|
|
|
if ( m_nHotColumn != NULL_COLUMN )
|
|
{
|
|
m_nHotColumn = NULL_COLUMN;
|
|
InvalidateHeader();
|
|
}
|
|
|
|
if ( m_nHotItem != NULL_ITEM || m_nHotSubItem != NULL_SUBITEM )
|
|
{
|
|
m_nHotItem = NULL_ITEM;
|
|
m_nHotSubItem = NULL_SUBITEM;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
BOOL OnMouseWheel( UINT /*nFlags*/, short nDelta, CPoint /*point*/ )
|
|
{
|
|
HideTitleTip();
|
|
|
|
// End any pending edit
|
|
if ( m_bEditItem )
|
|
SetFocus();
|
|
|
|
int nRowsScrolled = m_nMouseWheelScroll * nDelta / 120;
|
|
int nScrollPos = GetScrollPos( SB_VERT );
|
|
|
|
if ( nRowsScrolled > 0 )
|
|
nScrollPos = max( nScrollPos - ( nRowsScrolled * m_nItemHeight ), 0 );
|
|
else
|
|
nScrollPos += ( -nRowsScrolled * m_nItemHeight );
|
|
|
|
ResetScrollBars( SB_VERT, nScrollPos, FALSE );
|
|
Invalidate();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void OnTimer(UINT_PTR nIDEvent )
|
|
{
|
|
switch ( nIDEvent )
|
|
{
|
|
case RESIZE_COLUMN_TIMER: ResizeColumn( TRUE );
|
|
break;
|
|
case ITEM_VISIBLE_TIMER: {
|
|
KillTimer( ITEM_VISIBLE_TIMER );
|
|
|
|
int nFocusItem = NULL_ITEM;
|
|
int nFocusSubItem = NULL_SUBITEM;
|
|
|
|
// Get current focus item
|
|
if ( !GetFocusItem( nFocusItem, nFocusSubItem ) )
|
|
break;
|
|
|
|
// Make sure current focus item is visible before editing
|
|
if ( !EditItem( nFocusItem, nFocusSubItem ) )
|
|
break;
|
|
}
|
|
break;
|
|
case ITEM_AUTOSCROLL_TIMER: if ( !m_bGroupSelect )
|
|
KillTimer( ITEM_AUTOSCROLL_TIMER );
|
|
else
|
|
{
|
|
DWORD dwPoint = GetMessagePos();
|
|
CPoint ptMouse( GET_X_LPARAM( dwPoint ), GET_Y_LPARAM( dwPoint ) );
|
|
ScreenToClient( &ptMouse );
|
|
|
|
// Automatically scroll when group selecting
|
|
AutoScroll( ptMouse );
|
|
AutoSelect( ptMouse );
|
|
}
|
|
break;
|
|
case ITEM_SCROLL_TIMER: if ( !ScrollList() )
|
|
EndScroll();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void OnKeyDown( TCHAR nChar, UINT /*nRepCnt*/, UINT /*nFlags*/ )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
// Stop any pending scroll
|
|
EndScroll();
|
|
|
|
BOOL bCtrlKey = ( ( GetKeyState( VK_CONTROL ) & 0x8000 ) != 0 );
|
|
BOOL bShiftKey = ( ( GetKeyState( VK_SHIFT ) & 0x8000 ) != 0 );
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
|
|
int nFocusItem = NULL_ITEM;
|
|
int nFocusSubItem = NULL_SUBITEM;
|
|
GetFocusItem( nFocusItem, nFocusSubItem );
|
|
|
|
switch ( nChar )
|
|
{
|
|
case VK_DOWN: SetFocusItem( min( nFocusItem + 1, pT->GetItemCount() - 1 ), nFocusSubItem );
|
|
break;
|
|
case VK_UP: SetFocusItem( max( nFocusItem - 1, 0 ), nFocusSubItem );
|
|
break;
|
|
case VK_NEXT: SetFocusItem( min( nFocusItem + GetCountPerPage( FALSE ) - 1, pT->GetItemCount() - 1 ), nFocusSubItem );
|
|
break;
|
|
case VK_PRIOR: SetFocusItem( max( nFocusItem - GetCountPerPage( FALSE ) + 1, 0 ), nFocusSubItem );
|
|
break;
|
|
case VK_HOME: SetFocusItem( 0, nFocusSubItem );
|
|
break;
|
|
case VK_END: SetFocusItem( pT->GetItemCount() - 1, nFocusSubItem );
|
|
break;
|
|
case VK_LEFT: if ( m_bFocusSubItem )
|
|
SetFocusItem( nFocusItem, max( nFocusSubItem - 1, 0 ) );
|
|
else
|
|
SetScrollPos( SB_HORZ, max( GetScrollPos( SB_HORZ ) - ( bCtrlKey ? ITEM_SCROLL_OFFSET * 10 : ITEM_SCROLL_OFFSET ), 0 ) );
|
|
break;
|
|
case VK_RIGHT: if ( m_bFocusSubItem )
|
|
SetFocusItem( nFocusItem, min( nFocusSubItem + 1, GetColumnCount() - 1 ) );
|
|
else
|
|
SetScrollPos( SB_HORZ, min( GetScrollPos( SB_HORZ ) + ( bCtrlKey ? ITEM_SCROLL_OFFSET * 10 : ITEM_SCROLL_OFFSET ), rcClient.Width() ) );
|
|
break;
|
|
case VK_TAB: if ( !bCtrlKey && m_bFocusSubItem )
|
|
SetFocusItem( nFocusItem, bShiftKey ? max( nFocusSubItem - 1, 0 ) : min( nFocusSubItem + 1, GetColumnCount() - 1 ) );
|
|
break;
|
|
default: if ( nChar == VK_SPACE )
|
|
{
|
|
int nIndex = GetColumnIndex( nFocusSubItem );
|
|
if ( !( pT->GetItemFlags( nFocusItem, nIndex ) & ITEM_FLAGS_READ_ONLY ) )
|
|
{
|
|
switch ( pT->GetItemFormat( nFocusItem, nIndex ) )
|
|
{
|
|
case ITEM_FORMAT_CHECKBOX: pT->SetItemText( nFocusItem, nIndex, _ttoi( pT->GetItemText( nFocusItem, nIndex ) ) > 0 ? _T( "0" ) : _T( "1" ) );
|
|
NotifyParent( nFocusItem, nFocusSubItem, LCN_MODIFIED );
|
|
InvalidateItem( nFocusItem );
|
|
return;
|
|
case ITEM_FORMAT_CHECKBOX_3STATE: {
|
|
int nCheckImage = _ttoi( pT->GetItemText( nFocusItem, nIndex ) );
|
|
if ( nCheckImage < 0 )
|
|
pT->SetItemText( nFocusItem, nIndex, _T( "0" ) );
|
|
else if ( nCheckImage > 0 )
|
|
pT->SetItemText( nFocusItem, nIndex, _T( "-1" ) );
|
|
else
|
|
pT->SetItemText( nFocusItem, nIndex, _T( "1" ) );
|
|
|
|
NotifyParent( nFocusItem, nFocusSubItem, LCN_MODIFIED );
|
|
InvalidateItem( nFocusItem );
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bCtrlKey && nChar == _T( 'A' ) && !m_bSingleSelect )
|
|
{
|
|
m_setSelectedItems.clear();
|
|
for ( int nItem = 0; nItem < pT->GetItemCount(); nItem++ )
|
|
m_setSelectedItems.insert( nItem );
|
|
Invalidate();
|
|
return;
|
|
}
|
|
|
|
if ( !bCtrlKey && iswprint( nChar ) && iswupper( nChar ) )
|
|
{
|
|
int nSortIndex = GetColumnIndex( m_nSortColumn );
|
|
int nStartItem = nFocusItem + 1;
|
|
DWORD dwCurrentTick = GetTickCount();
|
|
|
|
stdstr strStart;
|
|
strStart += nChar;
|
|
|
|
// Has there been another keypress since last search period?
|
|
if ( ( dwCurrentTick - m_dwSearchTick ) < SEARCH_PERIOD )
|
|
{
|
|
if ( m_strSearchString.substr(0, 1 ) != strStart )
|
|
m_strSearchString += nChar;
|
|
|
|
stdstr strFocusText = pT->GetItemText( nFocusItem, nSortIndex );
|
|
|
|
// Are we continuing to type characters under current focus item?
|
|
if ( m_strSearchString.length() > 1 && _tcsicmp(m_strSearchString.c_str(),strFocusText.substr(0, m_strSearchString.length() ).c_str() ) == 0 )
|
|
{
|
|
m_dwSearchTick = GetTickCount();
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( m_strSearchString.substr(0, 1 ) != strStart )
|
|
nStartItem = 0;
|
|
m_strSearchString = strStart;
|
|
}
|
|
|
|
m_dwSearchTick = GetTickCount();
|
|
|
|
// Scan for next search string
|
|
for ( int nFirst = nStartItem; nFirst < pT->GetItemCount(); nFirst++ )
|
|
{
|
|
stdstr strItemText = pT->GetItemText( nFirst, nSortIndex );
|
|
|
|
if ( _tcsicmp(m_strSearchString.c_str(), strItemText.substr(0, m_strSearchString.length() ).c_str() ) == 0 )
|
|
{
|
|
SelectItem( nFirst, nFocusSubItem, TRUE );
|
|
EnsureItemVisible( nFirst, nFocusSubItem );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Rescan from top if not found search string
|
|
for ( int nSecond = 0; nSecond < pT->GetItemCount(); nSecond++ )
|
|
{
|
|
stdstr strItemText = pT->GetItemText( nSecond, nSortIndex );
|
|
|
|
if ( _tcsicmp(m_strSearchString.c_str(), strItemText.substr(0, m_strSearchString.length() ).c_str() ) == 0 )
|
|
{
|
|
SelectItem( nSecond, nFocusSubItem, TRUE );
|
|
EnsureItemVisible( nSecond, nFocusSubItem );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ( !bCtrlKey )
|
|
SelectItem( m_nFocusItem, m_nFocusSubItem, bShiftKey ? MK_SHIFT : 0 );
|
|
}
|
|
|
|
void OnSysKeyDown( TCHAR /*nChar*/, UINT /*nRepCnt*/, UINT /*nFlags*/ )
|
|
{
|
|
HideTitleTip( FALSE );
|
|
SetMsgHandled( FALSE );
|
|
}
|
|
|
|
void OnSettingsChange( UINT /*nFlags*/, LPCTSTR /*lpszSection*/ )
|
|
{
|
|
OnSettingsChange();
|
|
}
|
|
|
|
void OnSettingsChange()
|
|
{
|
|
LoadSettings();
|
|
ResetScrollBars();
|
|
Invalidate();
|
|
}
|
|
|
|
LRESULT OnCtlColorListBox( UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/ )
|
|
{
|
|
return DefWindowProc( nMsg, wParam, lParam );
|
|
}
|
|
|
|
LRESULT OnEndEdit( LPNMHDR lpNMHDR )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
CListNotify *pListNotify = reinterpret_cast<CListNotify *>( lpNMHDR );
|
|
|
|
m_bEditItem = FALSE;
|
|
int nIndex = GetColumnIndex( pListNotify->m_nSubItem );
|
|
|
|
switch ( pListNotify->m_nExitChar )
|
|
{
|
|
case VK_ESCAPE: break; // Do nothing
|
|
case VK_DELETE: pT->SetItemText( pListNotify->m_nItem, nIndex, _T( "" ) );
|
|
NotifyParent( pListNotify->m_nItem, pListNotify->m_nSubItem, LCN_MODIFIED );
|
|
break;
|
|
default: if ( pListNotify->m_lpItemDate == nullptr )
|
|
pT->SetItemText( pListNotify->m_nItem, nIndex, pListNotify->m_lpszItemText );
|
|
else
|
|
{
|
|
if ( _ttoi( pListNotify->m_lpszItemText ) == 0 )
|
|
pT->SetItemText( pListNotify->m_nItem, nIndex, _T( "" ) );
|
|
else
|
|
pT->SetItemDate( pListNotify->m_nItem, nIndex, *pListNotify->m_lpItemDate );
|
|
}
|
|
if ( pListNotify->m_nExitChar == VK_TAB )
|
|
PostMessage( WM_KEYDOWN, (WPARAM)VK_TAB );
|
|
NotifyParent( pListNotify->m_nItem, pListNotify->m_nSubItem, LCN_MODIFIED );
|
|
break;
|
|
}
|
|
|
|
InvalidateItem( pListNotify->m_nItem );
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD OnDragEnter( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD /*dwKeyState*/, CPoint point )
|
|
{
|
|
DWORD dwEffect = DROPEFFECT_NONE;
|
|
|
|
if ( FormatEtc.cfFormat == m_nHeaderClipboardFormat )
|
|
{
|
|
LPBYTE lpDragHeader = (LPBYTE)GlobalLock( StgMedium.hGlobal );
|
|
if ( lpDragHeader == nullptr )
|
|
return DROPEFFECT_NONE;
|
|
|
|
// Dragged column must originate from this control
|
|
if ( *( (HWND*)lpDragHeader ) == m_hWnd )
|
|
dwEffect = DropColumn( point ) ? DROPEFFECT_MOVE : DROPEFFECT_NONE;
|
|
|
|
GlobalUnlock( StgMedium.hGlobal );
|
|
}
|
|
|
|
return dwEffect;
|
|
}
|
|
|
|
DWORD OnDragOver( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD /*dwKeyState*/, CPoint point )
|
|
{
|
|
DWORD dwEffect = DROPEFFECT_NONE;
|
|
|
|
if ( FormatEtc.cfFormat == m_nHeaderClipboardFormat )
|
|
{
|
|
LPBYTE lpDragHeader = (LPBYTE)GlobalLock( StgMedium.hGlobal );
|
|
if ( lpDragHeader == nullptr )
|
|
return DROPEFFECT_NONE;
|
|
|
|
// Dragged column must originate from this control
|
|
if ( *( (HWND*)lpDragHeader ) == m_hWnd )
|
|
dwEffect = DropColumn( point ) ? DROPEFFECT_MOVE : DROPEFFECT_NONE;
|
|
|
|
GlobalUnlock( StgMedium.hGlobal );
|
|
}
|
|
|
|
return dwEffect;
|
|
}
|
|
|
|
BOOL OnDrop( FORMATETC& FormatEtc, STGMEDIUM& /*StgMedium*/, DWORD /*dwEffect*/, CPoint /*point*/ )
|
|
{
|
|
if ( FormatEtc.cfFormat == m_nHeaderClipboardFormat )
|
|
{
|
|
if ( m_nDragColumn != NULL_COLUMN && m_nHotDivider != NULL_COLUMN )
|
|
{
|
|
CListColumn listColumn;
|
|
if ( !GetColumn( m_nDragColumn, listColumn ) )
|
|
return FALSE;
|
|
|
|
// Move column to new position
|
|
m_aColumns.RemoveAt( m_nDragColumn );
|
|
m_aColumns.InsertAt( ( m_nDragColumn < m_nHotColumn ? ( m_nHotDivider == 0 ? 0 : m_nHotDivider - 1 ) : m_nHotDivider ), listColumn );
|
|
Invalidate();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Not supported
|
|
return FALSE;
|
|
}
|
|
|
|
void OnDragLeave()
|
|
{
|
|
}
|
|
|
|
BOOL OnRenderData( FORMATETC& FormatEtc, STGMEDIUM *pStgMedium, BOOL /*bDropComplete*/ )
|
|
{
|
|
if ( FormatEtc.cfFormat == m_nHeaderClipboardFormat )
|
|
{
|
|
pStgMedium->tymed = TYMED_HGLOBAL;
|
|
pStgMedium->hGlobal = GlobalAlloc( GMEM_MOVEABLE, sizeof( HWND ) );
|
|
if ( pStgMedium->hGlobal == nullptr )
|
|
return FALSE;
|
|
|
|
LPBYTE lpDragHeader = (LPBYTE)GlobalLock( pStgMedium->hGlobal );
|
|
if ( lpDragHeader == nullptr )
|
|
return FALSE;
|
|
|
|
// Store this window handle
|
|
*( (HWND*)lpDragHeader ) = m_hWnd;
|
|
|
|
GlobalUnlock( pStgMedium->hGlobal );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void DoPaint( CDCHandle dcPaint )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
int nContextState = dcPaint.SaveDC();
|
|
|
|
pT->DrawBkgnd( dcPaint );
|
|
pT->DrawList( dcPaint );
|
|
pT->DrawSelect( dcPaint );
|
|
pT->DrawHeader( dcPaint );
|
|
|
|
dcPaint.RestoreDC( nContextState );
|
|
}
|
|
|
|
void DrawBkgnd( CDCHandle dcPaint )
|
|
{
|
|
CRect rcClip;
|
|
if ( dcPaint.GetClipBox( rcClip ) == ERROR )
|
|
return;
|
|
|
|
dcPaint.SetBkColor( m_rgbBackground );
|
|
dcPaint.ExtTextOut( rcClip.left, rcClip.top, ETO_OPAQUE, rcClip, _T( "" ), 0, nullptr );
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
rcClient.top = ( m_bShowHeader ? m_nHeaderHeight : 0 );
|
|
|
|
if ( !m_bmpBackground.IsNull() && rcClip.bottom > rcClient.top )
|
|
{
|
|
CSize sizBackground;
|
|
m_bmpBackground.GetSize( sizBackground );
|
|
|
|
CDC dcBackgroundImage;
|
|
dcBackgroundImage.CreateCompatibleDC( dcPaint );
|
|
|
|
HBITMAP hOldBitmap = dcBackgroundImage.SelectBitmap( m_bmpBackground );
|
|
|
|
if ( m_bTileBackground )
|
|
{
|
|
// Calculate tile image maximum rows and columns
|
|
div_t divRows = div( (int)rcClient.Height(), (int)sizBackground.cy );
|
|
int nTileRows = divRows.rem > 0 ? divRows.quot + 1 : divRows.quot;
|
|
div_t divColumns = div( (int)rcClient.Width(), (int)sizBackground.cx );
|
|
int nTileColumns = divColumns.rem > 0 ? divColumns.quot + 1 : divColumns.quot;
|
|
|
|
// Draw tiled background image
|
|
for ( int nRow = 0; nRow <= nTileRows; nRow++ )
|
|
{
|
|
for ( int nColumn = 0; nColumn <= nTileColumns; nColumn++ )
|
|
dcPaint.BitBlt( nColumn * sizBackground.cx, nRow * sizBackground.cy, sizBackground.cx, sizBackground.cy, dcBackgroundImage, 0, 0, SRCCOPY );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CRect rcCentreImage( rcClient );
|
|
|
|
// Horizontally center image if smaller than the client width
|
|
if ( sizBackground.cx < rcClient.Width() )
|
|
{
|
|
rcCentreImage.left = ( rcClient.Width() / 2 ) - (int)( sizBackground.cx / 2 );
|
|
rcCentreImage.right = rcCentreImage.left + sizBackground.cx;
|
|
}
|
|
|
|
// Vertically center image if smaller than the client height
|
|
if ( sizBackground.cy + 16 < rcClient.Height() )
|
|
{
|
|
rcCentreImage.top = ( rcClient.Height() / 2 ) - (int)( ( sizBackground.cy + 16 ) / 2 );
|
|
rcCentreImage.bottom = rcCentreImage.top + sizBackground.cy;
|
|
}
|
|
|
|
// Draw centered background image
|
|
dcPaint.BitBlt( rcCentreImage.left, rcCentreImage.top, rcCentreImage.Width(), rcCentreImage.Height(), dcBackgroundImage, 0, 0, SRCCOPY );
|
|
}
|
|
|
|
dcBackgroundImage.SelectBitmap( hOldBitmap );
|
|
}
|
|
}
|
|
|
|
void DrawHeader( CDCHandle dcPaint )
|
|
{
|
|
if ( !m_bShowHeader )
|
|
return;
|
|
|
|
CRect rcClip;
|
|
if ( dcPaint.GetClipBox( rcClip ) == ERROR )
|
|
return;
|
|
|
|
CRect rcHeader;
|
|
GetClientRect( rcHeader );
|
|
rcHeader.bottom = m_nHeaderHeight;
|
|
|
|
if ( rcClip.top > rcHeader.bottom )
|
|
return;
|
|
|
|
dcPaint.SetBkColor( m_rgbHeaderBackground );
|
|
dcPaint.ExtTextOut( rcHeader.left, rcHeader.top, ETO_OPAQUE, rcHeader, _T( "" ), 0, nullptr );
|
|
|
|
CPen penHighlight;
|
|
penHighlight.CreatePen( PS_SOLID, 1, m_rgbHeaderBorder );
|
|
CPen penShadow;
|
|
penShadow.CreatePen( PS_SOLID, 1, m_rgbHeaderShadow );
|
|
|
|
CRect rcHeaderItem( rcHeader );
|
|
rcHeaderItem.OffsetRect( -GetScrollPos( SB_HORZ ), 0 );
|
|
|
|
int nHeaderWidth = 0;
|
|
|
|
for ( int nColumn = 0, nColumnCount = GetColumnCount(); nColumn < nColumnCount; rcHeaderItem.left = rcHeaderItem.right, nColumn++ )
|
|
{
|
|
CListColumn listColumn;
|
|
if ( !GetColumn( nColumn, listColumn ) )
|
|
break;
|
|
|
|
rcHeaderItem.right = rcHeaderItem.left + listColumn.m_nWidth;
|
|
nHeaderWidth += rcHeaderItem.Width();
|
|
|
|
if ( rcHeaderItem.right < rcClip.left )
|
|
continue;
|
|
if ( rcHeaderItem.left > rcClip.right )
|
|
break;
|
|
|
|
// Draw header and divider
|
|
if ( nColumn == m_nHighlightColumn )
|
|
{
|
|
dcPaint.SetBkColor( m_rgbHeaderHighlight );
|
|
dcPaint.ExtTextOut( rcHeaderItem.left, rcHeaderItem.top, ETO_OPAQUE, rcHeaderItem, _T( "" ), 0, nullptr );
|
|
}
|
|
|
|
|
|
dcPaint.SelectPen( penShadow );
|
|
dcPaint.MoveTo( rcHeaderItem.right - 1, rcHeaderItem.top + 1 );
|
|
dcPaint.LineTo( rcHeaderItem.right - 1, m_nHeaderHeight - 1 );
|
|
|
|
dcPaint.SelectPen( penHighlight );
|
|
dcPaint.MoveTo( rcHeaderItem.right, rcHeaderItem.top + 1 );
|
|
dcPaint.LineTo( rcHeaderItem.right, m_nHeaderHeight - 1 );
|
|
|
|
|
|
CRect rcHeaderText( rcHeaderItem );
|
|
rcHeaderText.left += nColumn == 0 ? 4 : 3;
|
|
rcHeaderText.OffsetRect( 0, 1 );
|
|
|
|
BOOL bShowArrow = m_bShowSort && ( rcHeaderItem.Width() > 15 );
|
|
|
|
|
|
if(listColumn.m_nImage == ITEM_IMAGE_NONE )
|
|
{
|
|
// Offset text bounding rectangle to account for sorting arrow
|
|
if ( bShowArrow && !listColumn.m_bFixed && listColumn.m_nIndex == m_nSortColumn )
|
|
rcHeaderText.right -= 15;
|
|
}
|
|
|
|
// Margin header text
|
|
rcHeaderText.DeflateRect( 4, 0, 5, 0 );
|
|
|
|
// Has this header item an associated image?
|
|
if ( listColumn.m_nImage != ITEM_IMAGE_NONE )
|
|
{
|
|
CSize sizeIcon;
|
|
m_ilListItems.GetIconSize( sizeIcon );
|
|
|
|
CRect rcHeaderImage;
|
|
rcHeaderImage.left = listColumn.m_strText.empty() ? ( ( rcHeaderText.left + rcHeaderText.right ) / 2 ) - ( sizeIcon.cx / 2 ) - ( 0 ) : rcHeaderText.left;
|
|
rcHeaderImage.right = min( rcHeaderImage.left + sizeIcon.cx, rcHeaderItem.right - 2 );
|
|
rcHeaderImage.top = ( ( rcHeaderItem.top + rcHeaderItem.bottom ) / 2 ) - ( sizeIcon.cy / 2 );
|
|
rcHeaderImage.bottom = min( rcHeaderImage.top + sizeIcon.cy, rcHeaderItem.bottom );
|
|
|
|
if(listColumn.m_nIndex == m_nSortColumn)
|
|
m_ilListItems.DrawEx( listColumn.m_nImage, dcPaint, rcHeaderImage, CLR_DEFAULT, CLR_DEFAULT, ILD_TRANSPARENT | ILD_SELECTED );
|
|
else
|
|
m_ilListItems.DrawEx( listColumn.m_nImage, dcPaint, rcHeaderImage, CLR_DEFAULT, CLR_DEFAULT, ILD_TRANSPARENT );
|
|
|
|
// Offset header text (for image)
|
|
rcHeaderText.left += sizeIcon.cx + 4;
|
|
}
|
|
|
|
dcPaint.SelectFont( m_fntListFont );
|
|
dcPaint.SetTextColor( m_rgbHeaderText );
|
|
dcPaint.SetBkMode( TRANSPARENT );
|
|
|
|
UINT nFormat = DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS;
|
|
|
|
if ( listColumn.m_nFlags & ITEM_FLAGS_CENTRE )
|
|
nFormat |= DT_CENTER;
|
|
else if ( listColumn.m_nFlags & ITEM_FLAGS_RIGHT )
|
|
nFormat |= DT_RIGHT;
|
|
else
|
|
nFormat |= DT_LEFT;
|
|
|
|
// Draw header text
|
|
if ( !rcHeaderText.IsRectEmpty() && !listColumn.m_strText.empty() )
|
|
dcPaint.DrawText( listColumn.m_strText.c_str(), (int)listColumn.m_strText.length(), rcHeaderText, nFormat );
|
|
|
|
// Draw sorting arrow
|
|
if ( bShowArrow && !listColumn.m_bFixed && listColumn.m_nIndex == m_nSortColumn )
|
|
{
|
|
CSize sizeIcon;
|
|
m_ilListItems.GetIconSize( sizeIcon );
|
|
|
|
CRect rcSortArrow;
|
|
rcSortArrow.left = rcHeaderText.right + 4;
|
|
rcSortArrow.right = min( rcSortArrow.left + sizeIcon.cx, rcHeaderItem.right );
|
|
rcSortArrow.top = rcHeaderItem.Height() / 2 - 3;
|
|
rcSortArrow.bottom = min( rcSortArrow.top + sizeIcon.cy, rcHeaderItem.bottom );
|
|
|
|
m_ilListItems.DrawEx( m_bSortAscending ? ITEM_IMAGE_UP : ITEM_IMAGE_DOWN, dcPaint, rcSortArrow, CLR_DEFAULT, CLR_DEFAULT, ILD_TRANSPARENT );
|
|
}
|
|
}
|
|
|
|
// Draw a frame around all header columns
|
|
|
|
if ( nHeaderWidth > 0 )
|
|
dcPaint.Draw3dRect( CRect( rcHeader.left, rcHeader.top, rcHeader.right + 2, rcHeader.bottom ), m_rgbHeaderBorder, m_rgbHeaderShadow );
|
|
|
|
}
|
|
|
|
void DrawRoundRect( CDCHandle dcPaint, CRect& rcRect, COLORREF rgbOuter, COLORREF rgbInner )
|
|
{
|
|
CRect rcRoundRect( rcRect );
|
|
|
|
CPen penBorder;
|
|
penBorder.CreatePen( PS_SOLID, 1, rgbOuter );
|
|
CBrush bshInterior;
|
|
bshInterior.CreateSolidBrush( m_rgbBackground );
|
|
|
|
dcPaint.SelectPen( penBorder );
|
|
dcPaint.SelectBrush( bshInterior );
|
|
|
|
dcPaint.RoundRect( rcRoundRect, CPoint( 5, 5 ) );
|
|
rcRoundRect.DeflateRect( 1, 1 );
|
|
|
|
CPen penInnerBorder;
|
|
penInnerBorder.CreatePen( PS_SOLID, 1, rgbInner );
|
|
dcPaint.SelectPen( penInnerBorder );
|
|
|
|
dcPaint.RoundRect( rcRoundRect, CPoint( 2, 2 ) );
|
|
}
|
|
|
|
void DrawGradient( CDCHandle dcPaint, CRect& rcRect, COLORREF rgbTop, COLORREF rgbBottom )
|
|
{
|
|
GRADIENT_RECT grdRect = { 0, 1 };
|
|
TRIVERTEX triVertext[ 2 ] = {
|
|
rcRect.left,
|
|
rcRect.top,
|
|
(COLOR16)(GetRValue( rgbTop ) << 8),
|
|
(COLOR16)(GetGValue( rgbTop ) << 8),
|
|
(COLOR16)(GetBValue( rgbTop ) << 8),
|
|
(COLOR16)(0x0000),
|
|
rcRect.right,
|
|
rcRect.bottom,
|
|
(COLOR16)(GetRValue( rgbBottom ) << 8),
|
|
(COLOR16)(GetGValue( rgbBottom ) << 8),
|
|
(COLOR16)(GetBValue( rgbBottom ) << 8),
|
|
(COLOR16)(0x0000)
|
|
};
|
|
|
|
dcPaint.GradientFill( triVertext, 2, &grdRect, 1, GRADIENT_FILL_RECT_V );
|
|
}
|
|
|
|
void DrawList( CDCHandle dcPaint )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
CRect rcClip;
|
|
if ( dcPaint.GetClipBox( rcClip ) == ERROR )
|
|
return;
|
|
|
|
CRect rcItem;
|
|
rcItem.left = -GetScrollPos( SB_HORZ );
|
|
rcItem.right = GetTotalWidth();
|
|
rcItem.top = ( m_bShowHeader ? m_nHeaderHeight : 0 );
|
|
rcItem.bottom = rcItem.top;
|
|
|
|
// Draw all visible items
|
|
for ( int nItem = GetTopItem(); nItem < pT->GetItemCount(); rcItem.top = rcItem.bottom, nItem++ )
|
|
{
|
|
rcItem.bottom = rcItem.top + m_nItemHeight;
|
|
|
|
if ( rcItem.bottom < rcClip.top || rcItem.right < rcClip.left )
|
|
continue;
|
|
if ( rcItem.top > rcClip.bottom || rcItem.left > rcClip.right )
|
|
break;
|
|
|
|
// May be implemented in a derived class
|
|
pT->DrawItem( dcPaint, nItem, rcItem );
|
|
}
|
|
}
|
|
|
|
void DrawItem( CDCHandle dcPaint, int nItem, CRect& rcItem )
|
|
{
|
|
T* pT = static_cast<T*>(this);
|
|
|
|
CRect rcClip;
|
|
if ( dcPaint.GetClipBox( rcClip ) == ERROR )
|
|
return;
|
|
|
|
int nFocusItem = NULL_ITEM;
|
|
int nFocusSubItem = NULL_SUBITEM;
|
|
GetFocusItem( nFocusItem, nFocusSubItem );
|
|
|
|
BOOL bSelectedItem = IsSelected( nItem );
|
|
//BOOL bControlFocus = ( GetFocus() == m_hWnd || m_bEditItem );
|
|
|
|
// Draw selected background
|
|
if ( bSelectedItem )
|
|
{
|
|
dcPaint.SetBkColor( m_rgbSelectedItem );
|
|
dcPaint.ExtTextOut( rcItem.left, rcItem.top, ETO_OPAQUE, rcItem, _T( "" ), 0, nullptr );
|
|
}
|
|
|
|
CRect rcSubItem( rcItem );
|
|
rcSubItem.right = rcSubItem.left;
|
|
|
|
for ( int nSubItem = 0, nColumnCount = GetColumnCount(); nSubItem < nColumnCount; rcSubItem.left = rcSubItem.right + 1, nSubItem++ )
|
|
{
|
|
CListColumn listColumn;
|
|
if ( !GetColumn( nSubItem, listColumn ) )
|
|
break;
|
|
|
|
rcSubItem.right = rcSubItem.left + listColumn.m_nWidth - 1;
|
|
|
|
if ( rcSubItem.right < rcClip.left || rcSubItem.Width() == 0 )
|
|
continue;
|
|
if ( rcSubItem.left > rcClip.right )
|
|
break;
|
|
|
|
LPCTSTR strItemText = pT->GetItemText( nItem, listColumn.m_nIndex );
|
|
int nItemImage = pT->GetItemImage( nItem, listColumn.m_nIndex );
|
|
UINT nItemFormat = pT->GetItemFormat( nItem, listColumn.m_nIndex );
|
|
UINT nItemFlags = pT->GetItemFlags( nItem, listColumn.m_nIndex );
|
|
|
|
// Custom draw subitem format
|
|
if ( nItemFormat == ITEM_FORMAT_CUSTOM )
|
|
{
|
|
pT->DrawCustomItem( dcPaint, nItem, nSubItem, rcSubItem );
|
|
return;
|
|
}
|
|
|
|
BOOL bFocusSubItem = ( m_bFocusSubItem && nFocusItem == nItem && nFocusSubItem == nSubItem );
|
|
|
|
COLORREF rgbBackground = m_rgbBackground;
|
|
COLORREF rgbText = m_rgbItemText;
|
|
|
|
if ( bFocusSubItem )
|
|
{
|
|
dcPaint.SetBkColor( m_bEditItem ? m_rgbBackground : m_rgbItemFocus );
|
|
dcPaint.ExtTextOut( rcSubItem.left, rcSubItem.top, ETO_OPAQUE, rcSubItem, _T( "" ), 0, nullptr );
|
|
|
|
if ( m_bEditItem )
|
|
{
|
|
CBrush bshSelectFrame;
|
|
bshSelectFrame.CreateSolidBrush( m_rgbItemFocus );
|
|
dcPaint.FrameRect( rcSubItem, bshSelectFrame );
|
|
}
|
|
}
|
|
else if ( pT->GetItemColours( nItem, nSubItem, rgbBackground, rgbText ) && rgbBackground != m_rgbBackground )
|
|
{
|
|
CPen penBorder;
|
|
penBorder.CreatePen( PS_SOLID, 1, rgbBackground );
|
|
CBrush bshInterior;
|
|
bshInterior.CreateSolidBrush( rgbBackground );
|
|
|
|
dcPaint.SelectPen( penBorder );
|
|
dcPaint.SelectBrush( bshInterior );
|
|
|
|
dcPaint.RoundRect( rcSubItem, CPoint( 3, 3 ) );
|
|
}
|
|
|
|
CRect rcItemText( rcSubItem );
|
|
|
|
// margin item text
|
|
//rcItemText.left += nSubItem == 0 ? 4 : 3;
|
|
//rcItemText.DeflateRect( 4, 0 );
|
|
|
|
// Draw subitem image if supplied
|
|
if ( !m_ilItemImages.IsNull() && nItemImage != ITEM_IMAGE_NONE && ( !m_bEditItem || ( m_bEditItem && !bFocusSubItem ) ) )
|
|
{
|
|
CSize sizeIcon;
|
|
m_ilItemImages.GetIconSize( sizeIcon );
|
|
|
|
CRect rcItemImage;
|
|
rcItemImage.left = (strItemText[0] == 0) ? ( ( rcItemText.left + rcItemText.right ) / 2 ) - ( sizeIcon.cx / 2 ) - ( 0 ) : rcItemText.left;
|
|
rcItemImage.right = min( rcItemImage.left + sizeIcon.cx, rcSubItem.right );
|
|
rcItemImage.top = ( ( rcSubItem.top + rcSubItem.bottom ) / 2 ) - ( sizeIcon.cy / 2 );
|
|
rcItemImage.bottom = min( rcItemImage.top + sizeIcon.cy, rcSubItem.bottom );
|
|
|
|
m_ilItemImages.DrawEx( nItemImage, dcPaint, rcItemImage, CLR_DEFAULT, CLR_DEFAULT, ILD_TRANSPARENT );
|
|
|
|
// Offset item text (for image)
|
|
rcItemText.left += sizeIcon.cx + 4;
|
|
}
|
|
|
|
if ( rcItemText.IsRectEmpty() )
|
|
continue;
|
|
|
|
COLORREF rgbSelectedText = m_rgbSelectedText;
|
|
pT->GetItemSelectedColours( nItem, nSubItem, rgbSelectedText );
|
|
|
|
dcPaint.SelectFont( pT->GetItemFont( nItem, nSubItem ) );
|
|
dcPaint.SetTextColor( ( bSelectedItem && !bFocusSubItem ) ? rgbSelectedText : rgbText );
|
|
dcPaint.SetBkMode( TRANSPARENT );
|
|
|
|
UINT nFormat = DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS;
|
|
|
|
if ( nItemFlags & ITEM_FLAGS_CENTRE )
|
|
nFormat |= DT_CENTER;
|
|
else if ( nItemFlags & ITEM_FLAGS_RIGHT )
|
|
nFormat |= DT_RIGHT;
|
|
else
|
|
nFormat |= DT_LEFT;
|
|
|
|
switch ( nItemFormat )
|
|
{
|
|
case ITEM_FORMAT_DATETIME: if ( strItemText[0] != 0 )
|
|
{
|
|
SYSTEMTIME stItemDate;
|
|
if ( !GetItemDate( nItem, listColumn.m_nIndex, stItemDate ) )
|
|
break;
|
|
|
|
stdstr strItemDate;
|
|
if ( nItemFlags & ITEM_FLAGS_DATE_ONLY )
|
|
strItemDate = FormatDate( stItemDate );
|
|
else if ( nItemFlags & ITEM_FLAGS_TIME_ONLY )
|
|
strItemDate = FormatTime( stItemDate );
|
|
else
|
|
strItemDate = FormatDate( stItemDate ) + _T( " " ) + FormatTime( stItemDate );
|
|
dcPaint.DrawText( strItemDate.c_str(), (int)strItemDate.length(), rcItemText, nFormat );
|
|
}
|
|
break;
|
|
case ITEM_FORMAT_CHECKBOX:
|
|
case ITEM_FORMAT_CHECKBOX_3STATE: {
|
|
CSize sizeIcon;
|
|
m_ilListItems.GetIconSize( sizeIcon );
|
|
|
|
CRect rcCheckBox;
|
|
rcCheckBox.left = ( ( rcItemText.left + rcItemText.right ) / 2 ) - ( sizeIcon.cx / 2 ) - 1;
|
|
rcCheckBox.right = min( rcCheckBox.left + sizeIcon.cx, rcSubItem.right );
|
|
rcCheckBox.top = ( ( rcSubItem.top + rcSubItem.bottom ) / 2 ) - ( sizeIcon.cy / 2 );
|
|
rcCheckBox.bottom = min( rcCheckBox.top + sizeIcon.cy, rcSubItem.bottom );
|
|
|
|
int nCheckValue = _ttoi( strItemText );
|
|
|
|
if ( nItemFormat == ITEM_FORMAT_CHECKBOX )
|
|
m_ilListItems.DrawEx( nCheckValue > 0 ? ITEM_IMAGE_CHECK_ON : ITEM_IMAGE_CHECK_OFF, dcPaint, rcCheckBox, CLR_DEFAULT, CLR_DEFAULT, ILD_TRANSPARENT );
|
|
else
|
|
{
|
|
int nCheckImage = ITEM_IMAGE_3STATE_UNDEF;
|
|
if ( nCheckValue < 0 )
|
|
nCheckImage = ITEM_IMAGE_3STATE_OFF;
|
|
else if ( nCheckValue > 0 )
|
|
nCheckImage = ITEM_IMAGE_3STATE_ON;
|
|
m_ilListItems.DrawEx( nCheckImage, dcPaint, rcCheckBox, CLR_DEFAULT, CLR_DEFAULT, ILD_TRANSPARENT );
|
|
}
|
|
}
|
|
break;
|
|
case ITEM_FORMAT_PROGRESS: {
|
|
CRect rcProgress( rcSubItem );
|
|
rcProgress.DeflateRect( 3, 2 );
|
|
|
|
// Draw progress border
|
|
DrawRoundRect( dcPaint, rcProgress, m_rgbHeaderShadow, m_rgbHeaderBackground );
|
|
|
|
// Fill progress bar area
|
|
rcProgress.DeflateRect( 3, 3 );
|
|
rcProgress.right = rcProgress.left + (int)( (double)rcProgress.Width() * ( ( max( min( atof( strItemText ), 100 ), 0 ) ) / 100.0 ) );
|
|
DrawGradient( dcPaint, rcProgress, m_rgbProgressTop, m_rgbProgressBottom );
|
|
}
|
|
break;
|
|
case ITEM_FORMAT_HYPERLINK: if ( nItem == m_nHotItem && nSubItem == m_nHotSubItem && !( nItemFlags & ITEM_FLAGS_READ_ONLY ) )
|
|
{
|
|
dcPaint.SelectFont( m_fntUnderlineFont );
|
|
dcPaint.SetTextColor( m_rgbHyperLink );
|
|
}
|
|
default: // Draw item text
|
|
{
|
|
size_t len = strlen(strItemText);
|
|
if ( len > 0 )
|
|
dcPaint.DrawText( strItemText, (int)len, rcItemText, nFormat );
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawSelect( CDCHandle dcPaint )
|
|
{
|
|
if ( !m_bGroupSelect )
|
|
return;
|
|
|
|
int nHorzScroll = GetScrollPos( SB_HORZ );
|
|
int nVertScroll = GetScrollPos( SB_VERT );
|
|
|
|
CRect rcGroupSelect( m_rcGroupSelect );
|
|
rcGroupSelect.OffsetRect( -nHorzScroll, -nVertScroll );
|
|
|
|
CRect rcClient;
|
|
GetClientRect( rcClient );
|
|
rcClient.top = ( m_bShowHeader ? m_nHeaderHeight : 0 );
|
|
|
|
// Limit box to list client area if scrolled to limits
|
|
if ( nHorzScroll > ( GetTotalWidth() - rcClient.Width() ) )
|
|
rcGroupSelect.right = min( rcClient.right, rcGroupSelect.right );
|
|
if ( nHorzScroll == 0 )
|
|
rcGroupSelect.left = max( rcClient.left, rcGroupSelect.left );
|
|
if ( nVertScroll > ( GetTotalHeight() - rcClient.Height() ) )
|
|
rcGroupSelect.bottom = min( rcClient.bottom, rcGroupSelect.bottom );
|
|
if ( nVertScroll == 0 )
|
|
rcGroupSelect.top = max( rcClient.top, rcGroupSelect.top );
|
|
|
|
// Limit bitmap to client area
|
|
CRect rcSelectArea( rcGroupSelect );
|
|
rcSelectArea.IntersectRect( rcSelectArea, rcClient );
|
|
|
|
CDC dcBackground;
|
|
dcBackground.CreateCompatibleDC( dcPaint );
|
|
|
|
int nBackgroundContext = dcBackground.SaveDC();
|
|
|
|
CBitmap bmpBackground;
|
|
bmpBackground.CreateCompatibleBitmap( dcPaint, rcSelectArea.Width(), rcSelectArea.Height() );
|
|
dcBackground.SelectBitmap( bmpBackground );
|
|
|
|
// Take a copy of existing background
|
|
dcBackground.BitBlt( 0, 0, rcSelectArea.Width(), rcSelectArea.Height(), dcPaint, rcSelectArea.left, rcSelectArea.top, SRCCOPY );
|
|
|
|
CDC dcGroupSelect;
|
|
dcGroupSelect.CreateCompatibleDC( dcPaint );
|
|
|
|
int nGroupSelectContext = dcGroupSelect.SaveDC();
|
|
|
|
CBitmap bmpGroupSelect;
|
|
bmpGroupSelect.CreateCompatibleBitmap( dcPaint, rcSelectArea.Width(), rcSelectArea.Height() );
|
|
dcGroupSelect.SelectBitmap( bmpGroupSelect );
|
|
|
|
// Draw group select box
|
|
dcGroupSelect.SetBkColor( m_rgbItemFocus );
|
|
dcGroupSelect.ExtTextOut( 0, 0, ETO_OPAQUE, CRect( CPoint( 0 ), rcSelectArea.Size() ), _T( "" ), 0, nullptr );
|
|
|
|
BLENDFUNCTION blendFunction;
|
|
blendFunction.BlendOp = AC_SRC_OVER;
|
|
blendFunction.BlendFlags = 0;
|
|
blendFunction.SourceConstantAlpha = 180;
|
|
blendFunction.AlphaFormat = 0;
|
|
|
|
// Blend existing background with selection box
|
|
dcGroupSelect.AlphaBlend( 0, 0, rcSelectArea.Width(), rcSelectArea.Height(), dcBackground, 0, 0, rcSelectArea.Width(), rcSelectArea.Height(), blendFunction );
|
|
|
|
// Draw blended selection box
|
|
dcPaint.BitBlt( rcSelectArea.left, rcSelectArea.top, rcSelectArea.Width(), rcSelectArea.Height(), dcGroupSelect, 0, 0, SRCCOPY );
|
|
|
|
// Draw selection box frame
|
|
CBrush bshSelectFrame;
|
|
bshSelectFrame.CreateSolidBrush( m_rgbItemText );
|
|
dcPaint.FrameRect( rcGroupSelect, bshSelectFrame );
|
|
|
|
dcBackground.RestoreDC( nBackgroundContext );
|
|
dcGroupSelect.RestoreDC( nGroupSelectContext );
|
|
}
|
|
|
|
void DrawCustomItem( CDCHandle dcPaint, int /*nItem*/, int /*nSubItem*/, CRect& /*rcSubItem*/ )
|
|
{
|
|
ATLASSERT( FALSE ); // Must be implemented in a derived class
|
|
}
|
|
};
|
|
|
|
struct CSubItem
|
|
{
|
|
stdstr m_strText;
|
|
int m_nImage;
|
|
UINT m_nFormat;
|
|
UINT m_nFlags;
|
|
UINT m_nMaxEditLen;
|
|
CListArray < stdstr > m_aComboList;
|
|
HFONT m_hFont;
|
|
COLORREF m_rgbBackground;
|
|
COLORREF m_rgbText;
|
|
COLORREF m_rgbSelectedText;
|
|
};
|
|
|
|
template < class TData = DWORD >
|
|
struct CListItem
|
|
{
|
|
CListArray < CSubItem > m_aSubItems;
|
|
stdstr m_strToolTip;
|
|
TData m_tData;
|
|
};
|
|
|
|
template < class TData >
|
|
class CListCtrlData : public CListImpl< CListCtrlData< TData > >
|
|
{
|
|
public:
|
|
DECLARE_WND_CLASS( _T( "ListCtrl" ) )
|
|
|
|
protected:
|
|
CListArray < CListItem< TData > > m_aItems;
|
|
|
|
public:
|
|
int AddItem( CListItem< TData >& listItem )
|
|
{
|
|
if ( !m_aItems.Add( listItem ) )
|
|
return -1;
|
|
return CListImpl< CListCtrlData >::AddItem() ? GetItemCount() - 1 : -1;
|
|
}
|
|
|
|
int AddItemAt( CListItem< TData >& listItem, int Index )
|
|
{
|
|
if (Index < 0 )
|
|
{
|
|
Index = 0;
|
|
}
|
|
if (Index > GetItemCount())
|
|
{
|
|
Index = GetItemCount();
|
|
}
|
|
if ( !m_aItems.AddAt( listItem, Index ) )
|
|
return -1;
|
|
return CListImpl< CListCtrlData >::AddItem() ? Index : -1;
|
|
}
|
|
|
|
int AddItem( LPCTSTR lpszText, int nImage = ITEM_IMAGE_NONE, UINT nFormat = ITEM_FORMAT_NONE, UINT nFlags = ITEM_FLAGS_NONE )
|
|
{
|
|
CSubItem listSubItem;
|
|
listSubItem.m_nImage = ITEM_IMAGE_NONE;
|
|
listSubItem.m_nFormat = nFormat;
|
|
listSubItem.m_nFlags = ValidateFlags( nFlags );
|
|
listSubItem.m_hFont = nullptr;
|
|
listSubItem.m_rgbBackground = m_rgbBackground;
|
|
listSubItem.m_rgbText = m_rgbItemText;
|
|
listSubItem.m_rgbSelectedText = m_rgbSelectedText;
|
|
listSubItem.m_nMaxEditLen = -1;
|
|
|
|
CListItem< TData > listItem;
|
|
for ( int nSubItem = 0; nSubItem < GetColumnCount(); nSubItem++ )
|
|
listItem.m_aSubItems.Add( listSubItem );
|
|
|
|
// Set item details for first subitem
|
|
listItem.m_aSubItems[ 0 ].m_strText = lpszText;
|
|
listItem.m_aSubItems[ 0 ].m_nImage = nImage;
|
|
|
|
return AddItem( listItem );
|
|
}
|
|
|
|
int AddItemAt(int Index, LPCTSTR lpszText, int nImage = ITEM_IMAGE_NONE, UINT nFormat = ITEM_FORMAT_NONE, UINT nFlags = ITEM_FLAGS_NONE )
|
|
{
|
|
CSubItem listSubItem;
|
|
listSubItem.m_nImage = ITEM_IMAGE_NONE;
|
|
listSubItem.m_nFormat = nFormat;
|
|
listSubItem.m_nFlags = ValidateFlags( nFlags );
|
|
listSubItem.m_hFont = nullptr;
|
|
listSubItem.m_rgbBackground = m_rgbBackground;
|
|
listSubItem.m_rgbText = m_rgbItemText;
|
|
listSubItem.m_rgbSelectedText = m_rgbSelectedText;
|
|
listSubItem.m_nMaxEditLen = (UINT)-1;
|
|
|
|
CListItem< TData > listItem;
|
|
for ( int nSubItem = 0; nSubItem < GetColumnCount(); nSubItem++ )
|
|
listItem.m_aSubItems.Add( listSubItem );
|
|
|
|
// Set item details for first subitem
|
|
listItem.m_aSubItems[ 0 ].m_strText = lpszText;
|
|
listItem.m_aSubItems[ 0 ].m_nImage = nImage;
|
|
|
|
return AddItemAt( listItem, Index );
|
|
}
|
|
|
|
BOOL DeleteItem( int nItem )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
return m_aItems.RemoveAt( nItem ) ? CListImpl< CListCtrlData >::DeleteItem( nItem ) : FALSE;
|
|
}
|
|
|
|
BOOL DeleteAllItems()
|
|
{
|
|
m_aItems.RemoveAll();
|
|
return CListImpl< CListCtrlData >::DeleteAllItems();
|
|
}
|
|
|
|
int GetItemCount()
|
|
{
|
|
return m_aItems.GetSize();
|
|
}
|
|
|
|
BOOL GetItem( int nItem, CListItem< TData >& listItem )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
listItem = m_aItems[ nItem ];
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GetItem( int nItem, CListItem< TData >*& listItem )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
{
|
|
listItem = nullptr;
|
|
return FALSE;
|
|
}
|
|
listItem = &m_aItems[ nItem ];
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GetSubItem( int nItem, int nSubItem, CSubItem& listSubItem )
|
|
{
|
|
CListItem< TData > * listItem;
|
|
if ( !GetItem( nItem, listItem ) )
|
|
return FALSE;
|
|
if ( nSubItem < 0 || nSubItem >= (int)listItem->m_aSubItems.GetSize() )
|
|
return FALSE;
|
|
listSubItem = listItem->m_aSubItems[ nSubItem ];
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GetSubItem( int nItem, int nSubItem, CSubItem *& listSubItem )
|
|
{
|
|
CListItem< TData > * listItem;
|
|
if ( !GetItem( nItem, listItem ) )
|
|
{
|
|
listSubItem = nullptr;
|
|
return FALSE;
|
|
}
|
|
if ( nSubItem < 0 || nSubItem >= (int)listItem->m_aSubItems.GetSize() )
|
|
{
|
|
listSubItem = nullptr;
|
|
return FALSE;
|
|
}
|
|
listSubItem = &listItem->m_aSubItems[ nSubItem ];
|
|
return TRUE;
|
|
}
|
|
|
|
LPCTSTR GetItemText( int nItem, int nSubItem )
|
|
{
|
|
CSubItem * listSubItem;
|
|
return GetSubItem( nItem, nSubItem, listSubItem ) ? listSubItem->m_strText.c_str() : _T( "" );
|
|
}
|
|
|
|
UINT GetItemMaxEditLen( int nItem, int nSubItem )
|
|
{
|
|
CSubItem * listSubItem;
|
|
return GetSubItem( nItem, nSubItem, listSubItem ) ? listSubItem->m_nMaxEditLen : 0;
|
|
}
|
|
|
|
int GetItemImage( int nItem, int nSubItem )
|
|
{
|
|
CSubItem *listSubItem;
|
|
return GetSubItem( nItem, nSubItem, listSubItem ) ? listSubItem->m_nImage : ITEM_IMAGE_NONE;
|
|
}
|
|
|
|
UINT GetItemFormat( int nItem, int nSubItem )
|
|
{
|
|
CSubItem * listSubItem;
|
|
if ( !GetSubItem( nItem, nSubItem, listSubItem ) )
|
|
return FALSE;
|
|
return listSubItem->m_nFormat == ITEM_FORMAT_NONE ? GetColumnFormat( IndexToOrder( nSubItem ) ) : listSubItem->m_nFormat;
|
|
}
|
|
|
|
UINT GetItemFlags( int nItem, int nSubItem )
|
|
{
|
|
CSubItem *listSubItem;
|
|
if ( !GetSubItem( nItem, nSubItem, listSubItem ) )
|
|
return FALSE;
|
|
return listSubItem->m_nFlags == ITEM_FLAGS_NONE ? GetColumnFlags( IndexToOrder( nSubItem ) ) : listSubItem->m_nFlags;
|
|
}
|
|
|
|
BOOL GetItemComboList( int nItem, int nSubItem, CListArray < stdstr >& aComboList )
|
|
{
|
|
CSubItem listSubItem;
|
|
if ( !GetSubItem( nItem, nSubItem, listSubItem ) )
|
|
return FALSE;
|
|
aComboList = listSubItem.m_aComboList;
|
|
return aComboList.IsEmpty() ? GetColumnComboList( IndexToOrder( nSubItem ), aComboList ) : !aComboList.IsEmpty();
|
|
}
|
|
|
|
HFONT GetItemFont( int nItem, int nSubItem )
|
|
{
|
|
CSubItem * listSubItem;
|
|
if ( !GetSubItem( nItem, nSubItem, listSubItem ) )
|
|
return FALSE;
|
|
return listSubItem->m_hFont == nullptr ? CListImpl< CListCtrlData >::GetItemFont( nItem, nSubItem ) : listSubItem->m_hFont;
|
|
}
|
|
|
|
BOOL GetItemColours( int nItem, int nSubItem, COLORREF& rgbBackground, COLORREF& rgbText )
|
|
{
|
|
CSubItem *listSubItem;
|
|
if ( !GetSubItem( nItem, nSubItem, listSubItem ) )
|
|
return FALSE;
|
|
rgbBackground = listSubItem->m_rgbBackground;
|
|
rgbText = listSubItem->m_rgbText;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GetItemSelectedColours( int nItem, int nSubItem, COLORREF& rgbSelectedText )
|
|
{
|
|
CSubItem *listSubItem;
|
|
if ( !GetSubItem( nItem, nSubItem, listSubItem ) )
|
|
return FALSE;
|
|
rgbSelectedText = listSubItem->m_rgbSelectedText;
|
|
return TRUE;
|
|
}
|
|
|
|
stdstr GetItemToolTip( int nItem, int /*nSubItem*/ )
|
|
{
|
|
CListItem< TData > listItem;
|
|
return GetItem( nItem, listItem ) ? listItem.m_strToolTip : _T( "" );
|
|
}
|
|
|
|
BOOL GetItemData( int nItem, TData& tData )
|
|
{
|
|
CListItem< TData > listItem;
|
|
if ( !GetItem( nItem, listItem ) )
|
|
return FALSE;
|
|
tData = listItem.m_tData;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetItemText( int nItem, int nSubItem, LPCTSTR lpszText, bool bInvalidateItem = true)
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
if ( nSubItem < 0 || nSubItem >= (int)m_aItems[ nItem ].m_aSubItems.GetSize() )
|
|
return FALSE;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_strText = lpszText;
|
|
|
|
if (bInvalidateItem)
|
|
InvalidateItem(nItem, nSubItem);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetItemComboIndex( int nItem, int nSubItem, int nIndex )
|
|
{
|
|
CListArray < stdstr > aComboList;
|
|
if ( !GetItemComboList( nItem, nSubItem, aComboList ) )
|
|
return FALSE;
|
|
return SetItemText( nItem, nSubItem, nIndex < 0 || nIndex >= aComboList.GetSize() ? _T( "" ) : aComboList[ nIndex ] );
|
|
}
|
|
|
|
BOOL SetItemImage( int nItem, int nSubItem, int nImage )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
if ( nSubItem < 0 || nSubItem >= (int)m_aItems[ nItem ].m_aSubItems.GetSize() )
|
|
return FALSE;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_nImage = nImage;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetItemFormat( int nItem, int nSubItem, UINT nFormat, UINT nFlags = ITEM_FLAGS_NONE )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
if ( nSubItem < 0 || nSubItem >= (int)m_aItems[ nItem ].m_aSubItems.GetSize() )
|
|
return FALSE;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_nFormat = nFormat;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_nFlags = nFlags;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetItemFormat( int nItem, int nSubItem, UINT nFormat, UINT nFlags, CListArray < stdstr >& aComboList )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
if ( nSubItem < 0 || nSubItem >= (int)m_aItems[ nItem ].m_aSubItems.GetSize() )
|
|
return FALSE;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_nFormat = nFormat;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_nFlags = nFlags;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_aComboList = aComboList;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetItemMaxEditLen( int nItem, int nSubItem, UINT nMaxEditLen )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
if ( nSubItem < 0 || nSubItem >= (int)m_aItems[ nItem ].m_aSubItems.GetSize() )
|
|
return FALSE;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_nMaxEditLen = nMaxEditLen;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetItemFont( int nItem, int nSubItem, HFONT hFont )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
if ( nSubItem < 0 || nSubItem >= (int)m_aItems[ nItem ].m_aSubItems.GetSize() )
|
|
return FALSE;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_hFont = hFont;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetItemColours( int nItem, int nSubItem, COLORREF rgbBackground, COLORREF rgbText )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
if ( nSubItem < 0 || nSubItem >= (int)m_aItems[ nItem ].m_aSubItems.GetSize() )
|
|
return FALSE;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_rgbBackground = rgbBackground;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_rgbText = rgbText;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetItemHighlightColours( int nItem, int nSubItem, COLORREF rgbSelectedText )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
if ( nSubItem < 0 || nSubItem >= (int)m_aItems[ nItem ].m_aSubItems.GetSize() )
|
|
return FALSE;
|
|
m_aItems[ nItem ].m_aSubItems[ nSubItem ].m_rgbSelectedText = rgbSelectedText;
|
|
return TRUE;
|
|
}
|
|
|
|
void ReverseItems()
|
|
{
|
|
m_aItems.Reverse();
|
|
}
|
|
|
|
class CompareItem
|
|
{
|
|
public:
|
|
CompareItem( int nColumn ) : m_nColumn( nColumn ) {}
|
|
inline bool operator() ( const CListItem< TData >& listItem1, const CListItem< TData >& listItem2 )
|
|
{
|
|
return ( _tcscmp(listItem1.m_aSubItems[ m_nColumn ].m_strText.c_str(), listItem2.m_aSubItems[ m_nColumn ].m_strText.c_str() ) < 0 );
|
|
}
|
|
|
|
protected:
|
|
int m_nColumn;
|
|
};
|
|
|
|
void SortItems( int nColumn, BOOL /*bAscending*/ )
|
|
{
|
|
m_aItems.Sort( CompareItem( nColumn ) );
|
|
}
|
|
|
|
BOOL SetItemToolTip( int nItem, LPCTSTR lpszToolTip )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
m_aItems[ nItem ].m_strToolTip = lpszToolTip;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetItemData( int nItem, TData& tData )
|
|
{
|
|
if ( nItem < 0 || nItem >= GetItemCount() )
|
|
return FALSE;
|
|
m_aItems[ nItem ].m_tData = tData;
|
|
return TRUE;
|
|
}
|
|
};
|
|
|
|
typedef CListCtrlData< DWORD > CListCtrl;
|