// EnhancedListCtrl.cpp : implementation file
//
#include "stdafx.h"
#include "EnhancedListCtrl.h"
#include <afxdlgs.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define USE_FLAT
#if !defined(WM_OUTPUT_MSG)
#define WM_OUTPUT_MSG (WM_USER + 1005)
#endif
HHOOK ghHook = NULL;
HWND ghChild = NULL;
#define ID_LISTCTRL_SELECT_ALL 0x4141
/////////////////////////////////////////////////////////////////////////////
// CEnhancedListCtrl
CEnhancedListCtrl::CEnhancedListCtrl() :
m_bSilent(false),
m_bEnhanceHeader(false),
m_bComboItems(false),
m_bAllowClick(false),
m_bPermanentDropArrow(false),
m_bFlat(false),
m_bChildActive(false),
m_nLastSortRow(-1),
m_bLastSortOrder(false),
m_ilHeader(NULL),
m_csMessage(_T("<< No items to display >>")),
m_pwndChild(NULL),
m_ilRowHeight(NULL)
{
}
CEnhancedListCtrl::~CEnhancedListCtrl()
{
if(m_ilHeader)
delete m_ilHeader;
if(m_ilRowHeight)
delete m_ilRowHeight;
}
BEGIN_MESSAGE_MAP(CEnhancedListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CEnhancedListCtrl)
ON_WM_PAINT()
ON_WM_SETFOCUS()
ON_WM_CREATE()
ON_NOTIFY(HDN_CHECKCLICK, 0, OnItemdblclick)
ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDOWN()
ON_WM_SYSKEYDOWN()
ON_WM_KILLFOCUS()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CEnhancedListCtrl message handlers
void CEnhancedListCtrl::OnPaint()
{
if ( GetItemCount() == 0 && (m_bSilent == false))
{
CPaintDC dc( this );
int nSavedDC = dc.SaveDC();
CRect rc;
GetWindowRect( &rc );
ScreenToClient( &rc );
CHeaderCtrl* pHC;
pHC = GetHeaderCtrl();
if (pHC != NULL)
{
CRect rcH;
pHC->GetItemRect( 0, &rcH );
rc.top += rcH.bottom;
}
rc.top += 10;
dc.SetBkMode(OPAQUE);
if(IsWindowEnabled())
{
dc.SetTextColor(::GetSysColor(COLOR_GRAYTEXT));
dc.SetBkColor(::GetSysColor(COLOR_WINDOW));
}
else
{
dc.SetTextColor(::GetSysColor(COLOR_3DSHADOW));
dc.SetBkColor(::GetSysColor(COLOR_BTNFACE));
}
dc.SelectStockObject( ANSI_VAR_FONT );
dc.DrawText( m_csMessage, -1,
rc,
DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_NOCLIP );
dc.RestoreDC(nSavedDC);
}
else
{
Default();
}
}
void CEnhancedListCtrl::SetFlat(BOOL bFlat)
{
m_bFlat = bFlat;
if(IsWindow(m_hWnd))
{
Invalidate();
UpdateWindow();
}
}
//
// default focus on first item to gove feedback to the user.
//
void CEnhancedListCtrl::OnSetFocus(CWnd* pOldWnd)
{
CListCtrl::OnSetFocus(pOldWnd);
if(pOldWnd != this)
{
if((GetSelectedCount() == 0) && (GetItemCount() > 0))
{
EnsureVisible(0, TRUE);
SetSelectionMark(0);
SetItemState(0, LVIS_FOCUSED, LVIS_FOCUSED);
}
}
}
//
// handle kill focus to check if we gove focus to a child (combo)
// if so, we just ignore it and let the status of the items unchanged
//
void CEnhancedListCtrl::OnKillFocus(CWnd* pNewWnd)
{
if(pNewWnd && pNewWnd->GetParent() != this)
CListCtrl::OnKillFocus(pNewWnd);
}
void CEnhancedListCtrl::IndicateSortOrder(int nRow, BOOL bSortAscending)
{
if(m_ilHeader)
{
HD_ITEM Item;
CHeaderCtrl* HdrCtrl= GetHeaderCtrl();
Item.mask = HDI_IMAGE | HDI_FORMAT;
if(m_nLastSortRow != -1)
{
HdrCtrl->GetItem(m_nLastSortRow, &Item);
Item.iImage = -1; // remove it
HdrCtrl->SetItem(m_nLastSortRow, &Item);
}
if( nRow != -1 )
{
HdrCtrl->GetItem(nRow, &Item);
Item.iImage = (bSortAscending ? 0 : 1);
Item.fmt |= (HDF_IMAGE | HDF_BITMAP_ON_RIGHT);
HdrCtrl->SetItem(nRow, &Item);
}
m_nLastSortRow = nRow;
m_bLastSortOrder = bSortAscending;
}
}
void CEnhancedListCtrl::SetSortable()
{
HICON h1 = AfxGetApp()->LoadIcon(100); // add this resource to your app: icon pointing up
HICON h2 = AfxGetApp()->LoadIcon(101); // add this resource to your app: icon pointing down
if(h1 && h2)
{
m_ilHeader = new CImageList();
m_ilHeader->Create(16, 16, ILC_MASK, 2 , 2 );
m_ilHeader->Add(h1);
m_ilHeader->Add(h2);
CHeaderCtrl *HdrCtrl= GetHeaderCtrl();
HdrCtrl->SetImageList( m_ilHeader );
}
else
{
OutputDebugString(_T("Warning: Up/Down icons for sort indicator not found. Check icon resource 100/101.\r\n"));
}
}
int CEnhancedListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CListCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
if((m_bEnhanceHeader && !IsWindow(m_cHeader.m_hWnd)))
{
m_cHeader.SubclassDlgItem(0, this);
}
return 0;
}
CHeaderCtrl* CEnhancedListCtrl::GetHeaderCtrl()
{
if(m_bEnhanceHeader)
return (CHeaderCtrl*)&m_cHeader;
else
return CListCtrl::GetHeaderCtrl();
}
static CImageList dummyImageList;
void CEnhancedListCtrl::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
if((m_bEnhanceHeader && !IsWindow(m_cHeader.m_hWnd)))
{
m_cHeader.SubclassDlgItem(0, this);
}
if(m_bComboItems)
{
if(CListCtrl::GetImageList(LVSIL_SMALL) == NULL)
{
dummyImageList.DeleteImageList();
dummyImageList.Create( 1, 16, ILC_COLOR4, 10, 10 );
CListCtrl::SetImageList( &dummyImageList, LVSIL_SMALL );
}
}
CListCtrl::PreSubclassWindow();
}
int CEnhancedListCtrl::InsertColumn (int nCol, const LVCOLUMN* pColumn)
{
int nRetCol = CListCtrl::InsertColumn(nCol, pColumn);
if(m_bEnhanceHeader && (-1 != nRetCol))
{
HDITEM hdi = {0};
hdi.mask = HDI_FORMAT|HDI_LPARAM;
m_cHeader.GetItem(nRetCol, &hdi);
hdi.fmt |= HDF_OWNERDRAW;
if(pColumn->fmt & LVCFMT_NOSORT)
hdi.fmt |= HDF_NOSORT;
hdi.lParam = NULL;
m_cHeader.SetItem(nRetCol, &hdi);
}
return nRetCol;
}
int CEnhancedListCtrl::InsertColumn (int nCol, LPCTSTR lpszColumnHeading, int nFormat, int nWidth, int nSubItem)
{
int nRetCol = CListCtrl::InsertColumn(nCol, lpszColumnHeading, nFormat, nWidth, nSubItem);
if(m_bEnhanceHeader && (-1 != nRetCol))
{
HDITEM hdi = {0};
hdi.mask = HDI_FORMAT|HDI_LPARAM;
m_cHeader.GetItem(nRetCol, &hdi);
hdi.fmt |= HDF_OWNERDRAW;
if(nFormat & LVCFMT_NOSORT)
hdi.fmt |= HDF_NOSORT;
hdi.lParam = NULL;
m_cHeader.SetItem(nRetCol, &hdi);
}
return nRetCol;
}
BOOL CEnhancedListCtrl::SetColumnCheck(int nCol, int nCheck)
{
if(m_bEnhanceHeader)
{
return m_cHeader.SetCheck(nCol, nCheck);
}
return FALSE;
}
int CEnhancedListCtrl::GetColumnCheck(int nCol)
{
if(m_bEnhanceHeader)
{
return m_cHeader.GetCheck(nCol);
}
return FALSE;
}
BOOL CEnhancedListCtrl::SetColumnData(int nCol, int nData)
{
if(m_bEnhanceHeader)
{
return m_cHeader.SetItemData(nCol, nData);
}
HDITEM hdi = {0};
hdi.mask = HDI_LPARAM;
GetHeaderCtrl()->GetItem(nCol, &hdi);
hdi.lParam = nData;
return GetHeaderCtrl()->SetItem(nCol, &hdi);
}
int CEnhancedListCtrl::GetColumnData(int nCol)
{
if(m_bEnhanceHeader)
{
return m_cHeader.GetItemData(nCol);
}
HDITEM hdi = {0};
hdi.mask = HDI_LPARAM;
GetHeaderCtrl()->GetItem(nCol, &hdi);
return hdi.lParam;
}
void CEnhancedListCtrl::OnItemdblclick(NMHDR* pNMHDR, LRESULT* pResult)
{
HD_NOTIFY *phdn = (HD_NOTIFY *) pNMHDR;
phdn->hdr.hwndFrom = this->m_hWnd;
GetParent()->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)pNMHDR);
*pResult = 0;
}
BOOL CEnhancedListCtrl::PrepareControlSpace(const int nItem, const int nSubItem, CRect& rect, BOOL bList)
{
int offset = 0;
//
// Make sure that the item is visible
//
if(!EnsureVisible(nItem, TRUE))
{
return FALSE;
}
GetSubItemRect(nItem, bList ? 0 : nSubItem, LVIR_BOUNDS, rect);
if(bList) rect.DeflateRect(rect.Height(), 0, 0, 0);
// Now scroll if we need to expose the column
CRect rcClient;
GetClientRect(rcClient);
if( offset + rect.left < 0 || offset + rect.left > rcClient.right )
{
CSize size(offset + rect.left,0);
Scroll(size);
rect.left -= size.cx;
}
if(bList)
{
rect.left += offset;
rect.right = rect.left;
for(int i = 0; i < nSubItem; i++)
rect.right += GetColumnWidth(i);
rect.right += GetColumnWidth(nSubItem) + 1;
}
else
{
rect.left += offset;
rect.right = rect.left + GetColumnWidth(nSubItem) + 1;
}
if(rect.right > rcClient.right)
rect.right = rcClient.right;
return TRUE;
}
void CEnhancedListCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
if(m_bAllowClick)
{
CListCtrl::OnLButtonUp(nFlags, point);
}
else
{
NMLISTVIEW lvn;
lvn.hdr.code = NM_CLICK;
lvn.hdr.hwndFrom = this->m_hWnd;
lvn.hdr.idFrom = GetDlgCtrlID();
lvn.iItem = m_nCurrentItem;
lvn.iSubItem = m_nCurrentSubItem;
lvn.uNewState = NULL;
lvn.uOldState = NULL;
lvn.uChanged = NULL;
lvn.ptAction.x = 0;
lvn.ptAction.y = 0;
lvn.lParam = NULL;
SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn);
m_bAllowClick = true;
}
}
void CEnhancedListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
m_bAllowClick = TRUE;
{
LVHITTESTINFO hti = {0};
GetCursorPos(&hti.pt);
ScreenToClient(&hti.pt);
//
// find where the click hit the item or subitem
//
CListCtrl::SubItemHitTest(&hti);
if((hti.flags & LVHT_ONITEM) == 0)
return;
int nState = GetItemState(hti.iItem, 0x0f);
if(nState & LVIS_SELECTED)
{
if((hti.iSubItem == 0) && (hti.flags & LVHT_ONITEMICON) && !(hti.flags & LVHT_ONITEMLABEL))
{
}
else
{
CRect rc;
GetSubItemRect(hti.iItem, hti.iSubItem, LVIR_BOUNDS, rc);
rc.left = rc.right - rc.Height();
if(rc.PtInRect(hti.pt))
{
//
// it hit a subitem, then do what the subitem requires
//
NMLISTVIEW lvn;
lvn.hdr.code = LVN_QUERYCOMBO;
lvn.hdr.hwndFrom = this->m_hWnd;
lvn.hdr.idFrom = GetDlgCtrlID();
lvn.iItem = hti.iItem;
lvn.iSubItem = hti.iSubItem;
lvn.uNewState = NULL;
lvn.uOldState = NULL;
lvn.uChanged = NULL;
lvn.ptAction.x = 0;
lvn.ptAction.y = 0;
lvn.lParam = NULL;
if(0 != GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn))
{
m_bAllowClick = false;
m_nCurrentItem = hti.iItem;
m_nCurrentSubItem = hti.iSubItem;
}
}
}
}
}
if(m_bAllowClick)
CListCtrl::OnLButtonDown(nFlags, point);
}
void CEnhancedListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
{
// TODO: Add your control notification handler code here
*pResult = 0;
if(!m_bComboItems)
return;
if( GetFocus() != this)
SetFocus();
//
// do we have a valid item ??
//
NMLISTVIEW* pNMListView = (NMLISTVIEW*)pNMHDR;
if(pNMListView->iItem == -1)
return;
{
LVHITTESTINFO hti = {0};
GetCursorPos(&hti.pt);
ScreenToClient(&hti.pt);
//
// find where the click hit the item or subitem
//
CListCtrl::SubItemHitTest(&hti);
if((hti.flags & LVHT_ONITEM) == 0)
return;
if((hti.iSubItem == 0) && (hti.flags & LVHT_ONITEMICON) && !(hti.flags & LVHT_ONITEMLABEL))
{
}
else
{
CRect rc;
GetSubItemRect(hti.iItem, hti.iSubItem, LVIR_BOUNDS, rc);
rc.left = rc.right - rc.Height();
if(rc.PtInRect(hti.pt))
{
//
// it hit a subitem, then do what the subitem requires
//
NMLISTVIEW lvn;
lvn.hdr.code = LVN_QUERYCOMBO;
lvn.hdr.hwndFrom = this->m_hWnd;
lvn.hdr.idFrom = GetDlgCtrlID();
lvn.iItem = hti.iItem;
lvn.iSubItem = hti.iSubItem;
lvn.uNewState = NULL;
lvn.uOldState = NULL;
lvn.uChanged = NULL;
lvn.ptAction.x = 0;
lvn.ptAction.y = 0;
lvn.lParam = NULL;
switch(GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn))
{
case LV_NONE:
// do nothing
break;
case LV_LISTCTRL:
DoListCtrl(hti.iItem, hti.iSubItem);
break;
default:
DoCombo(hti.iItem, hti.iSubItem);
break;
}
}
}
}
}
CEnhancedListCtrl::OLComboCtrl* CEnhancedListCtrl::DoCombo(int nItem, int nSubItem)
{
CString strFind = GetItemText(nItem, nSubItem);
CRect rect;
if(!PrepareControlSpace(nItem, nSubItem, rect))
return NULL;
rect.top--;
//basic code end
rect.bottom += 6 * rect.Height();//dropdown area
DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_DROPDOWNLIST;
OLComboCtrl *pCtrl = new OLComboCtrl(nItem, nSubItem);
pCtrl->Create(dwStyle, rect, this, 1);
pCtrl->ModifyStyleEx(0,WS_EX_STATICEDGE);//can we tell at all
NMLISTVIEW lvn;
lvn.hdr.code = LVN_COMBODROPDOWN;
lvn.hdr.hwndFrom = this->m_hWnd;
lvn.hdr.idFrom = GetDlgCtrlID();
lvn.iItem = nItem;
lvn.iSubItem = nSubItem;
lvn.uNewState = NULL;
lvn.uOldState = NULL;
lvn.uChanged = NULL;
lvn.ptAction.x = 0;
lvn.ptAction.y = 0;
lvn.lParam = (LPARAM)pCtrl->GetSafeHwnd();
GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn);
pCtrl->ShowDropDown();
pCtrl->SelectString(-1, strFind.GetBuffer(1));
// The returned pointer should not be saved
return pCtrl;
}
LRESULT CALLBACK ListCtrlMonitor(int code, // hook code
WPARAM wParam, // removal option
LPARAM lParam) // message
{
MOUSEHOOKSTRUCT* pmsg = (MOUSEHOOKSTRUCT*) lParam;
switch(wParam)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
if(pmsg->hwnd != ghChild && ::GetParent(pmsg->hwnd) != ghChild)
{
::SendMessage(ghChild, WM_CANCELMODE, 0, 0);
}
break;
}
return CallNextHookEx(ghHook, code, wParam, lParam);
}
CEnhancedListCtrl::OLListCtrl* CEnhancedListCtrl::DoListCtrl(int nItem, int nSubItem)
{
CString strFind = GetItemText(nItem, nSubItem);
CRect rect;
if(!PrepareControlSpace(nItem, nSubItem, rect, FALSE))
return NULL;
rect.OffsetRect(0, rect.Height());
rect.bottom += 8 * rect.Height();//dropdown area
CRect rcThis;
GetClientRect(&rcThis);
CRect calc;
calc.UnionRect(rcThis, rect); // should return rcThis, then the list is completly inside
if(calc != rcThis)
{
Scroll(CSize(0, -(rcThis.Height() - calc.Height())));
rect.OffsetRect(0, rcThis.Height() - calc.Height());
}
// ClientToScreen(&rect);
DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_HSCROLL|WS_VSCROLL | (LVS_ALIGNLEFT|LVS_NOSORTHEADER|LVS_REPORT|LVS_SHOWSELALWAYS|LVS_SINGLESEL);
// setup the hook for mouse messages
ghHook = ::SetWindowsHookEx(WH_MOUSE, ListCtrlMonitor, AfxGetInstanceHandle(), NULL);
// create the control
OLListCtrl *pCtrl = new OLListCtrl(nItem, nSubItem);
pCtrl->Create(dwStyle, rect, this, 1);
// pCtrl->CreateEx(WS_EX_TOPMOST|WS_EX_CLIENTEDGE|WS_EX_TOOLWINDOW, WC_LISTVIEW, _T("DROPLIST"), dwStyle, rect, GetDesktopWindow(), 1);
ghChild = pCtrl->m_hWnd;
pCtrl->SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_TRACKSELECT);
pCtrl->SetHoverTime(1);
NMLISTVIEW lvn;
lvn.hdr.code = LVN_COMBODROPDOWN;
lvn.hdr.hwndFrom = this->m_hWnd;
lvn.hdr.idFrom = GetDlgCtrlID();
lvn.iItem = nItem;
lvn.iSubItem = nSubItem;
lvn.uNewState = NULL;
lvn.uOldState = NULL;
lvn.uChanged = NULL;
lvn.ptAction.x = 0;
lvn.ptAction.y = 0;
lvn.lParam = (LPARAM)pCtrl->GetSafeHwnd();
GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn);
{
CWinApp* pApp = AfxGetApp();
MSG msg;
while(::IsWindow(pCtrl->m_hWnd))
{
if(PeekMessage(&msg, NULL, NULL, NULL, FALSE))
pApp->PumpMessage();
/*
if (!::GetMessage(&msg, NULL, NULL, NULL))
{
break;
}
//if(msg.hwnd != NULL)
// msg.hwnd = pCtrl->m_hWnd;
if(msg.hwnd == pCtrl->m_hWnd || ::GetParent(msg.hwnd) == pCtrl->m_hWnd)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
*/
}
}
UnhookWindowsHookEx(ghHook);
ghChild = NULL;
ghHook = NULL;
// The returned pointer should not be saved
return pCtrl;
}
void CEnhancedListCtrl::OnCustomDraw( NMHDR* pNMHDR, LRESULT* pResult )
{
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
// Take the default processing unless we set this to something else below.
*pResult = CDRF_DODEFAULT;
if(!m_bComboItems)
return;
// First thing - check the draw stage. If it's the control's prepaint
// stage, then tell Windows we want messages for every item.
if(CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage)
{
*pResult = CDRF_NOTIFYITEMDRAW|CDRF_NOTIFYSUBITEMDRAW;
}
else if((CDDS_ITEMPREPAINT/*|CDDS_SUBITEM*/) == pLVCD->nmcd.dwDrawStage)
{
// This is the prepaint stage for an item. Here's where we set the
// item's text color. Our return value will tell Windows to draw the
// item itself.
//int nState = GetItemState(pLVCD->nmcd.dwItemSpec, 0x0f);
// Tell Windows to paint the control itself.
*pResult = /*CDRF_NEWFONT|CDRF_DODEFAULT|*/CDRF_NOTIFYPOSTPAINT|CDRF_NOTIFYSUBITEMDRAW;
//*pResult = CDRF_NEWFONT|CDRF_DODEFAULT|CDRF_NOTIFYPOSTPAINT;
}
else if((CDDS_POSTPAINT|CDDS_SUBITEM) & pLVCD->nmcd.dwDrawStage)
{
*pResult = CDRF_DODEFAULT;
// if(pLVCD->iSubItem != 0)
{
//
// get the rect of the subitem
//
int nState = GetItemState(pLVCD->nmcd.dwItemSpec, 0x0f);
if(m_bPermanentDropArrow || (nState & LVIS_SELECTED))
{
CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
CRect rc;
GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rc);
NMLISTVIEW lvn;
lvn.hdr.code = LVN_QUERYCOMBO;
lvn.hdr.hwndFrom = this->m_hWnd;
lvn.hdr.idFrom = GetDlgCtrlID();
lvn.iItem = pLVCD->nmcd.dwItemSpec;
lvn.iSubItem = pLVCD->iSubItem;
lvn.uNewState = NULL;
lvn.uOldState = NULL;
lvn.uChanged = NULL;
lvn.ptAction.x = 0;
lvn.ptAction.y = 0;
lvn.lParam = NULL;
if(0 != GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn))
{
rc.left = rc.right - rc.Height();
pDC->DrawFrameControl(&rc, DFC_SCROLL, m_bFlat ? DFCS_SCROLLCOMBOBOX|DFCS_FLAT : DFCS_SCROLLCOMBOBOX);
pLVCD->nmcd.rc = rc;
*pResult = CDRF_NOTIFYPOSTPAINT; //CDRF_SKIPDEFAULT;
}
}
}
}
}
/////////////////////////////////////////////////////////////////////////////
// OLComboCtrl
CEnhancedListCtrl::OLComboCtrl::OLComboCtrl( int nItem, int nSubItem)
{
m_nItem = nItem;
m_nSubItem = nSubItem;
m_bVK_ESCAPE = FALSE;
}
CEnhancedListCtrl::OLComboCtrl::~OLComboCtrl()
{
}
typedef CEnhancedListCtrl::OLComboCtrl CEnhancedListCtrlComboItem;
BEGIN_MESSAGE_MAP(CEnhancedListCtrlComboItem, CComboBox)
//{{AFX_MSG_MAP(OLComboCtrl)
ON_WM_NCDESTROY()
ON_WM_CHAR()
ON_WM_KILLFOCUS()
ON_CONTROL_REFLECT(CBN_CLOSEUP, OnCloseup)
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// OLComboCtrl message handlers
BOOL CEnhancedListCtrl::OLComboCtrl::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if( pMsg->message == WM_KEYDOWN )
{
if(pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
{
::TranslateMessage(pMsg);
::DispatchMessage(pMsg);
return 1;
}
}
return CComboBox::PreTranslateMessage(pMsg);
}
void CEnhancedListCtrl::OLComboCtrl::OnNcDestroy()
{
CComboBox::OnNcDestroy();
delete this;
}
void CEnhancedListCtrl::OLComboCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if(nChar == VK_ESCAPE || nChar == VK_RETURN)
{
if( nChar == VK_ESCAPE)
m_bVK_ESCAPE = 1;
GetParent()->SetFocus();
return;
}
CComboBox::OnChar(nChar, nRepCnt, nFlags);
}
void CEnhancedListCtrl::OLComboCtrl::OnKillFocus(CWnd* pNewWnd)
{
int nIndex = GetCurSel();
CComboBox::OnKillFocus(pNewWnd);
CString str;
GetWindowText(str);
// Send Notification to parent of ListView ctrl
LV_DISPINFO lvDispinfo;
lvDispinfo.hdr.hwndFrom = GetParent()->m_hWnd;
lvDispinfo.hdr.idFrom = GetDlgCtrlID(); //that's us
lvDispinfo.hdr.code = LVN_ENDLABELEDIT;
lvDispinfo.item.mask = LVIF_TEXT | LVIF_PARAM;
lvDispinfo.item.iItem = m_nItem;
lvDispinfo.item.iSubItem = m_nSubItem;
lvDispinfo.item.pszText = m_bVK_ESCAPE ? NULL : LPTSTR((LPCTSTR)str);
lvDispinfo.item.cchTextMax = str.GetLength();
lvDispinfo.item.lParam = GetItemData(GetCurSel());
if(nIndex!=CB_ERR)
GetParent()->GetParent()->SendMessage(WM_NOTIFY, GetParent()->GetDlgCtrlID(), (LPARAM)&lvDispinfo);
PostMessage(WM_CLOSE);
}
void CEnhancedListCtrl::OLComboCtrl::OnCloseup()
{
GetParent()->SetFocus();
}
int CEnhancedListCtrl::OLComboCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CComboBox::OnCreate(lpCreateStruct) == -1)
return -1;
CFont* font = GetParent()->GetFont();
SetFont(font);
SetFocus();
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// OLComboCtrl
CEnhancedListCtrl::OLListCtrl::OLListCtrl( int nItem, int nSubItem) : m_pParentWnd(NULL)
{
m_nItem = nItem;
m_nSubItem = nSubItem;
m_bVK_ESCAPE = FALSE;
}
CEnhancedListCtrl::OLListCtrl::~OLListCtrl()
{
}
typedef CEnhancedListCtrl::OLListCtrl CEnhancedListCtrlListCtrlItem;
#pragma warning(disable:4310) // cast truncates constant value
BEGIN_MESSAGE_MAP(CEnhancedListCtrlListCtrlItem, CListCtrl)
//{{AFX_MSG_MAP(OLListCtrl)
ON_WM_NCDESTROY()
ON_WM_NCPAINT()
ON_WM_NCHITTEST()
ON_WM_CHAR()
ON_WM_KILLFOCUS()
ON_WM_SETFOCUS()
// ON_CONTROL_REFLECT(LVN_ITEMACTIVATE, OnCloseup)
// ON_CONTROL_REFLECT(LVN_ITEMCHANGED, OnItemChange)
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
ON_WM_LBUTTONDOWN()
ON_WM_RBUTTONDOWN()
ON_WM_LBUTTONDBLCLK()
ON_WM_RBUTTONDBLCLK()
ON_WM_NCLBUTTONDOWN()
ON_WM_CREATE()
ON_WM_CANCELMODE()
ON_WM_PAINT()
ON_WM_SIZE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// OLListCtrl message handlers
BOOL CEnhancedListCtrl::OLListCtrl::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if( pMsg->message == WM_KEYDOWN )
{
if(pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
{
::TranslateMessage(pMsg);
::DispatchMessage(pMsg);
return 1;
}
}
return CListCtrl::PreTranslateMessage(pMsg);
}
void CEnhancedListCtrl::OLListCtrl::OnNcDestroy()
{
CListCtrl::OnNcDestroy();
delete this;
}
void CEnhancedListCtrl::OLListCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if(nChar == VK_ESCAPE || nChar == VK_RETURN)
{
if( nChar == VK_ESCAPE)
m_bVK_ESCAPE = 1;
GetParent()->SetFocus();
return;
}
CListCtrl::OnChar(nChar, nRepCnt, nFlags);
}
void CEnhancedListCtrl::OLListCtrl::OnKillFocus(CWnd* pNewWnd)
{
POSITION pos = CListCtrl::GetFirstSelectedItemPosition();
int item = -1;
if(pos)
item = CListCtrl::GetNextSelectedItem(pos);
CListCtrl::OnKillFocus(pNewWnd);
if(pNewWnd && pNewWnd->GetParent()->m_hWnd == this->m_hWnd) // its the header control
{
return;
}
// ReleaseCapture();
if(!m_bVK_ESCAPE)
{
CString str = GetItemText(item, 0);
// Send Notification to parent of ListView ctrl
LV_DISPINFO lvDispinfo;
lvDispinfo.hdr.hwndFrom = GetParent()->m_hWnd;
lvDispinfo.hdr.idFrom = GetDlgCtrlID(); //that's us
lvDispinfo.hdr.code = LVN_ENDLABELEDIT;
lvDispinfo.item.mask = LVIF_TEXT | LVIF_PARAM;
lvDispinfo.item.iItem = m_nItem;
lvDispinfo.item.iSubItem = m_nSubItem;
lvDispinfo.item.pszText = m_bVK_ESCAPE ? NULL : LPTSTR((LPCTSTR)str);
lvDispinfo.item.cchTextMax = str.GetLength();
if(item != -1)
{
lvDispinfo.item.lParam = CListCtrl::GetItemData(item);
GetParent()->GetParent()->SendMessage(WM_NOTIFY, GetParent()->GetDlgCtrlID(), (LPARAM)&lvDispinfo);
}
}
PostMessage(WM_CLOSE);
}
void CEnhancedListCtrl::OLListCtrl::OnCloseup()
{
GetParent()->SetFocus();
}
void CEnhancedListCtrl::OLListCtrl::OnItemChange(NMHDR* pNMHDR, LRESULT* pResult)
{
NMLISTVIEW *phdn = (NMLISTVIEW *) pNMHDR;
long l = GetItemData(phdn->iItem);
if(!(phdn->uOldState & LVIS_SELECTED) && (phdn->uNewState & LVIS_SELECTED))
{
int i = phdn->iItem;
while(GetItemData(i) == l)
{
SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
i--;
}
i = phdn->iItem;
while(GetItemData(i) == l)
{
SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
i++;
}
}
else if((phdn->uOldState & LVIS_SELECTED) && !(phdn->uNewState & LVIS_SELECTED))
{
int i = phdn->iItem;
while(GetItemData(i) == l)
{
SetItemState(i, 0, LVIS_SELECTED);
i--;
}
i = phdn->iItem;
while(GetItemData(i) == l)
{
SetItemState(i, 0, LVIS_SELECTED);
i++;
}
}
*pResult = 0;
}
void CEnhancedListCtrl::OLListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
CListCtrl::OnLButtonDown(nFlags, point);
if(HitTest(point) == -1)
m_bVK_ESCAPE = TRUE;
GetParent()->SetFocus();
}
void CEnhancedListCtrl::OLListCtrl::OnCancelMode()
{
// ReleaseCapture();
m_bVK_ESCAPE = TRUE;
PostMessage(WM_CLOSE);
}
void CEnhancedListCtrl::OLListCtrl::OnNcLButtonDown(UINT nHitTest, CPoint point)
{
if(nHitTest == HTNOWHERE)
{
// ReleaseCapture();
m_bVK_ESCAPE = TRUE;
PostMessage(WM_CLOSE);
}
CListCtrl::OnNcLButtonDown(nHitTest, point);
}
int CEnhancedListCtrl::OLListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CListCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
CFont* font = GetParent()->GetFont();
SetFont(font);
SetFocus();
// SetCapture();
return 0;
}
void CEnhancedListCtrl::OLListCtrl::OnSetFocus(CWnd* wnd)
{
CListCtrl::OnSetFocus(wnd);
}
void CEnhancedListCtrl::OLListCtrl::OnPaint()
{
CListCtrl::OnPaint();
}
void CEnhancedListCtrl::OLListCtrl::OnNcPaint()
{
CListCtrl::OnNcPaint();
/*
CWindowDC dc(this);
CRect rc;
GetWindowRect(&rc);
ScreenToClient(&rc);
rc.top = rc.bottom - GetSystemMetrics(SM_CYHSCROLL);
rc.left = rc.right - GetSystemMetrics(SM_CXVSCROLL);
InvalidateRect(rc);
DrawFrameControl(dc, &rc, DFC_SCROLL,DFCS_SCROLLSIZEGRIP);
*/
}
UINT CEnhancedListCtrl::OLListCtrl::OnNcHitTest(CPoint point)
{
CRect rc;
GetWindowRect(&rc);
if(rc.PtInRect(point))
{
rc.top = rc.bottom - GetSystemMetrics(SM_CYHSCROLL);
rc.left = rc.right - GetSystemMetrics(SM_CXVSCROLL);
if(rc.PtInRect(point))
{
return HTBOTTOMRIGHT;
}
return CListCtrl::OnNcHitTest(point);
}
return HTNOWHERE;
}
void CEnhancedListCtrl::OLListCtrl::OnSize(UINT nType, int cx, int cy)
{
CListCtrl::OnSize(nType, cx, cy);
}
void CEnhancedListCtrl::OLListCtrl::OnCustomDraw( NMHDR* pNMHDR, LRESULT* pResult )
{
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
// Take the default processing unless we set this to something else below.
*pResult = CDRF_DODEFAULT;
// First thing - check the draw stage. If it's the control's prepaint
// stage, then tell Windows we want messages for every item.
if(CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage)
{
*pResult = CDRF_NOTIFYITEMDRAW|CDRF_NOTIFYSUBITEMDRAW;
}
else if((CDDS_ITEMPREPAINT/*|CDDS_SUBITEM*/) == pLVCD->nmcd.dwDrawStage)
{
// This is the prepaint stage for an item. Here's where we set the
// item's text color. Our return value will tell Windows to draw the
// item itself.
//int nState = GetItemState(pLVCD->nmcd.dwItemSpec, 0x0f);
// Tell Windows to paint the control itself.
*pResult = /*CDRF_NEWFONT|CDRF_DODEFAULT|*/CDRF_NOTIFYPOSTPAINT|CDRF_NOTIFYSUBITEMDRAW;
//*pResult = CDRF_NEWFONT|CDRF_DODEFAULT|CDRF_NOTIFYPOSTPAINT;
}
else if((CDDS_POSTPAINT|CDDS_SUBITEM) & pLVCD->nmcd.dwDrawStage)
{
*pResult = CDRF_DODEFAULT;
// if is separation element
if(m_separator.find(pLVCD->nmcd.dwItemSpec) != m_separator.end())
{
CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
CRect rc;
GetItemRect(pLVCD->nmcd.dwItemSpec, rc, LVIR_BOUNDS);
CPen p;
p.CreatePen(PS_DOT, 1, GetSysColor(COLOR_3DSHADOW));
CPen* pold = pDC->SelectObject(&p);
pDC->MoveTo(rc.left, rc.top);
pDC->LineTo(rc.right, rc.top);
pDC->SelectObject(pold);
}
}
}
/////////////////////////////////////////////////////////////////////////////
// CEnhancedHeaderCtrl
CEnhancedHeaderCtrl::CEnhancedHeaderCtrl() : m_nLastClickCol(-1)
{
m_cxCheck = GetSystemMetrics(SM_CXMENUCHECK); // width of the checkbox
m_cxSpacing = 2; // spacing between checkbox/text/bitmap
m_cxOffsetLeft = 6; // offset of the checkbox from left
}
CEnhancedHeaderCtrl::~CEnhancedHeaderCtrl()
{
}
BEGIN_MESSAGE_MAP(CEnhancedHeaderCtrl, CHeaderCtrl)
//{{AFX_MSG_MAP(CEnhancedHeaderCtrl)
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDOWN()
ON_WM_NCLBUTTONUP()
ON_WM_CANCELMODE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CEnhancedHeaderCtrl message handlers
void CEnhancedHeaderCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// This code only works with header controls.
ASSERT(lpDrawItemStruct->CtlType == ODT_HEADER);
HDITEM hdi = {0};
TCHAR lpBuffer[256];
hdi.mask = HDI_TEXT|HDI_LPARAM|HDI_FORMAT|HDI_IMAGE;
hdi.pszText = lpBuffer;
hdi.cchTextMax = 256;
GetItem( lpDrawItemStruct->itemID, &hdi);
// Draw the button frame.
if(hdi.fmt & HDF_NOSORT)
DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH|DFCS_FLAT);
else
DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH);
lpDrawItemStruct->rcItem.left += m_cxOffsetLeft;
if(LOWORD(hdi.lParam))
{
// Draw the button frame.
int button = 0;
if(LOWORD(hdi.lParam) == 1)
button |= DFCS_BUTTONCHECK;
else if(LOWORD(hdi.lParam) == 2)
button |= DFCS_BUTTONCHECK|DFCS_CHECKED;
else if(LOWORD(hdi.lParam) == 3)
button |= DFCS_BUTTON3STATE;
if(!IsWindowEnabled() || !GetParent()->IsWindowEnabled())
button |= DFCS_INACTIVE;
if(m_nLastClickCol == lpDrawItemStruct->itemID )
button |= DFCS_PUSHED;
CRect rc = lpDrawItemStruct->rcItem;
rc.right = rc.left + m_cxCheck;
DrawFrameControl(lpDrawItemStruct->hDC, &rc, DFC_BUTTON, button);
lpDrawItemStruct->rcItem.left += rc.Width() + m_cxSpacing;
lpDrawItemStruct->rcItem.left += m_cxOffsetLeft;
lpDrawItemStruct->rcItem.right -= 2*m_cxSpacing;
}
int fmt = DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS;
switch(hdi.fmt)
{
case HDF_LEFT: fmt |= DT_LEFT; break;
case HDF_CENTER: fmt |= DT_CENTER; break;
case HDF_RIGHT: fmt |= DT_RIGHT; break;
}
if((hdi.fmt & (HDF_IMAGE|HDF_BITMAP|HDF_BITMAP_ON_RIGHT)) && (hdi.iImage != -1))
{
CRect rc = lpDrawItemStruct->rcItem;
::DrawText(lpDrawItemStruct->hDC, lpBuffer, _tcslen(lpBuffer), rc, DT_CALCRECT|fmt);
if(lpDrawItemStruct->rcItem.right - rc.right > 16)
{
CImageList* pImageList = GetImageList();
CPoint pt;
if(hdi.fmt & HDF_BITMAP_ON_RIGHT)
{
// pt.x = lpDrawItemStruct->rcItem.right - 16 - m_cxSpacing; // 16=image 4=spacing
pt.x = rc.right + m_cxSpacing; // 16=image 4=spacing
}
else
{
pt.x = lpDrawItemStruct->rcItem.left + m_cxOffsetLeft; // 16=image 4=spacing
lpDrawItemStruct->rcItem.left += m_cxSpacing;
}
pt.y = lpDrawItemStruct->rcItem.top;
pImageList->Draw(CDC::FromHandle(lpDrawItemStruct->hDC), hdi.iImage, pt ,ILD_NORMAL);
}
}
::DrawText(lpDrawItemStruct->hDC, lpBuffer, _tcslen(lpBuffer), &lpDrawItemStruct->rcItem, fmt);
}
int CEnhancedHeaderCtrl::InsertItem( int nPos, HDITEM* phdi )
{
ASSERT(phdi);
phdi->fmt |= HDF_OWNERDRAW;
phdi->lParam = 0;
return CHeaderCtrl::InsertItem(nPos, phdi);
}
BOOL CEnhancedHeaderCtrl::SubclassDlgItem(UINT nID, CWnd* pParent)
{
return CWnd::SubclassDlgItem(nID, pParent);
}
BOOL CEnhancedHeaderCtrl::SetCheck(int nCol, int nCheck)
{
HDITEM hdi = {0};
hdi.mask = HDI_LPARAM;
CHeaderCtrl::GetItem(nCol, &hdi);
hdi.lParam = MAKELONG(1+nCheck, HIWORD(hdi.lParam));
return CHeaderCtrl::SetItem(nCol, &hdi);
}
int CEnhancedHeaderCtrl::GetCheck(int nCol)
{
HDITEM hdi = {0};
hdi.mask = HDI_LPARAM;
CHeaderCtrl::GetItem(nCol, &hdi);
return LOWORD(hdi.lParam) - 1;
}
BOOL CEnhancedHeaderCtrl::SetItemData(int nCol, WORD wData)
{
HDITEM hdi = {0};
hdi.mask = HDI_LPARAM;
CHeaderCtrl::GetItem(nCol, &hdi);
hdi.lParam = MAKELONG(LOWORD(hdi.lParam), wData);
return CHeaderCtrl::SetItem(nCol, &hdi);
}
WORD CEnhancedHeaderCtrl::GetItemData(int nCol)
{
HDITEM hdi = {0};
hdi.mask = HDI_LPARAM;
CHeaderCtrl::GetItem(nCol, &hdi);
return HIWORD(hdi.lParam);
}
BOOL CEnhancedHeaderCtrl::PtInCheckbox(int nCol, CPoint& pt)
{
CRect rc;
CHeaderCtrl::GetItemRect(nCol, &rc);
if(rc.Width() > m_cxCheck + m_cxOffsetLeft)
{
rc.left += m_cxOffsetLeft;
rc.right = rc.left + m_cxCheck;
if(PtInRect(&rc, pt))
{
return TRUE;
}
}
return FALSE;
}
BOOL CEnhancedHeaderCtrl::PtInItem(int nCol, CPoint& pt)
{
CRect rc;
CHeaderCtrl::GetItemRect(nCol, &rc);
return PtInRect(&rc, pt);
}
void CEnhancedHeaderCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
int nLastCol = m_nLastClickCol;
if(m_nLastClickCol != -1)
{
ReleaseCapture();
InvalidateColumn(m_nLastClickCol);
m_nLastClickCol = -1;
}
CRect rc;
int nCount = CHeaderCtrl::GetItemCount();
for(int i = 0; i < nCount; ++i)
{
HDITEM hdi = {0};
hdi.mask = HDI_LPARAM|HDI_FORMAT;
CHeaderCtrl::GetItem(i, &hdi);
if(PtInCheckbox(i, point) && (i == nLastCol))
{
if(LOWORD(hdi.lParam))
{
if(LOWORD(hdi.lParam) == 1)
hdi.lParam = MAKELONG(2, HIWORD(hdi.lParam));
else
hdi.lParam = MAKELONG(1, HIWORD(hdi.lParam));
NMHEADER hdn;
hdn.hdr.code = HDN_CHECKCLICK;
hdn.hdr.hwndFrom = this->m_hWnd;
hdn.hdr.idFrom = GetDlgCtrlID();
hdn.iItem = nLastCol;
hdn.iButton = 0;
hdn.pitem = &hdi;
GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&hdn);
CHeaderCtrl::SetItem(i, &hdi);
return;
}
}
}
CHeaderCtrl::OnLButtonUp(nFlags, point);
}
void CEnhancedHeaderCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
int nCount = CHeaderCtrl::GetItemCount();
for(int i = 0; i < nCount; ++i)
{
HDITEM hdi = {0};
hdi.mask = HDI_LPARAM;
CHeaderCtrl::GetItem(i, &hdi);
if(PtInCheckbox(i, point))
{
if(LOWORD(hdi.lParam))
{
m_nLastClickCol = i;
InvalidateColumn(i);
::SetCapture(m_hWnd);
return;
}
}
}
CHeaderCtrl::OnLButtonDown(nFlags, point);
}
void CEnhancedHeaderCtrl::OnNcLButtonUp(UINT nHitTest, CPoint point)
{
if(m_nLastClickCol != -1)
{
ReleaseCapture();
}
CHeaderCtrl::OnNcLButtonUp(nHitTest, point);
}
void CEnhancedHeaderCtrl::OnCancelMode()
{
CHeaderCtrl::OnCancelMode();
if(m_nLastClickCol != -1)
{
ReleaseCapture();
}
}
void CEnhancedHeaderCtrl::InvalidateColumn(int nCol)
{
CRect rc;
CHeaderCtrl::GetItemRect(nCol, &rc);
CHeaderCtrl::InvalidateRect(&rc);
}
BOOL CEnhancedListCtrl::PreTranslateMessage(MSG* pMsg)
{
if( pMsg->message == WM_SYSKEYDOWN )
{
if(pMsg->wParam == VK_DOWN || pMsg->wParam == VK_INSERT)
{
::TranslateMessage(pMsg);
::DispatchMessage(pMsg);
return 1;
}
}
else if( pMsg->message == WM_KEYDOWN )
{
if((pMsg->wParam == 'A') && (GetKeyState(VK_CONTROL) & ~1))
{
OnSelectAll();
}
}
return CListCtrl::PreTranslateMessage(pMsg);
}
void CEnhancedListCtrl::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if(nChar == VK_DOWN)
{
int nItem = GetItemFocus();
if(GetItemState(nItem, 0xf) & LVIS_FOCUSED)
{
NMLISTVIEW lvn;
lvn.hdr.code = LVN_QUERYCOMBO;
lvn.hdr.hwndFrom = this->m_hWnd;
lvn.hdr.idFrom = GetDlgCtrlID();
lvn.iItem = nItem;
lvn.uNewState = NULL;
lvn.uOldState = NULL;
lvn.uChanged = NULL;
lvn.ptAction.x = 0;
lvn.ptAction.y = 0;
lvn.lParam = NULL;
int nCount = GetHeaderCtrl()->GetItemCount();
for(int i = 0; i < nCount; ++i)
{
lvn.iSubItem = i;
switch(GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn))
{
case LV_NONE:
// do nothing
break;
case LV_LISTCTRL:
DoListCtrl(nItem, i);
return;
default:
DoCombo(nItem, i);
return;
}
}
}
}
else if(nChar == VK_INSERT)
{
CString cs;
cs.Format(_T("List contains %i items (%i selected)"), GetItemCount(), GetSelectedCount());
AfxGetMainWnd()->SendMessage(WM_OUTPUT_MSG, (WPARAM)-1, (LPARAM)(LPCTSTR)cs);
}
else
{
CListCtrl::OnSysKeyDown(nChar, nRepCnt, nFlags);
}
}
void CEnhancedListCtrl::OnSelectAll()
{
int nCount = CListCtrl::GetItemCount();
for(int i = 0; i < nCount; i++)
{
CListCtrl::SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
}
}
BOOL CEnhancedListCtrl::ExportCSV(LPCTSTR pstrFilename, BOOL bSelectedOnly)
{
CString csFilename;
ASSERT(pstrFilename != NULL);
//
// construct a filename like SCAN_YYYYMMDD.CSV
//
csFilename = pstrFilename;
csFilename += _T("_");
csFilename += CTime::GetCurrentTime().FormatGmt(_T("%Y%m%d"));
csFilename += _T(".CSV");
{
static TCHAR szFilter[] = _T("Comma Separated Files (*.csv)|*.csv|All Files (*.*)|*.*||");
CFileDialog* pcBrowse; //Need a file open dialog
pcBrowse = (CFileDialog*)new CFileDialog(FALSE, NULL, csFilename,
OFN_PATHMUSTEXIST|OFN_EXPLORER|OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
szFilter, NULL);
if(IDOK == pcBrowse->DoModal())
{
POSITION p = pcBrowse->GetStartPosition();
if(p)
{
csFilename = pcBrowse->GetNextPathName(p);
pstrFilename = LPCTSTR(csFilename);
}
}
else
{
return FALSE;
}
delete pcBrowse;
}
CWaitCursor cWait;
CStdioFile file;
CFileException e;
if(file.Open(pstrFilename, CFile::modeCreate|CFile::modeWrite, &e))
{
CHeaderCtrl* pHeader = GetHeaderCtrl();
int nRows = bSelectedOnly ? GetSelectedCount() : GetItemCount();
int nCols = GetHeaderCtrl()->GetItemCount();
int x, y;
HDITEM hdi;
TCHAR lpBuffer[256];
TCHAR lpSep[128];
int nSep = ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SLIST, lpSep, 127);
CString csSep = _T("\"");
csSep += lpSep;
csSep += _T("\"");
nSep = _tcslen(lpSep) + 1;// increment the separator length by one (")
//
// write the header
//
CString cs;
CString csSubstring;
hdi.mask = HDI_TEXT;
hdi.pszText = lpBuffer;
hdi.cchTextMax = 256;
cs = _T("\"");
for(x = 0; x < nCols; ++x)
{
pHeader->GetItem(x, &hdi);
cs += hdi.pszText;
cs += csSep;
}
cs = cs.Left(cs.GetLength()-nSep); // remove the trailing ,"
file.WriteString(cs);
file.WriteString(_T("\n"));
if(bSelectedOnly)
{
POSITION n = GetFirstSelectedItemPosition();
while(n)
{
y = GetNextSelectedItem(n);
cs = _T("\"");
for(x = 0; x < nCols; ++x)
{
csSubstring = GetItemText(y, x);
csSubstring.Replace(_T("\""), _T("\"\""));
cs += csSubstring;
cs += csSep;
}
cs = cs.Left(cs.GetLength()-nSep); // remove the trailing ,"
file.WriteString(cs);
file.WriteString(_T("\n"));
}
}
else
{
for(y = 0; y < nRows; ++y)
{
cs = _T("\"");
for(x = 0; x < nCols; ++x)
{
csSubstring = GetItemText(y, x);
csSubstring.Replace(_T("\""), _T("\"\""));
cs += csSubstring;
cs += csSep;
}
cs = cs.Left(cs.GetLength()-nSep); // remove the trailing ,"
file.WriteString(cs);
file.WriteString(_T("\n"));
}
}
file.Close();
return TRUE;
}
else
{
TCHAR szError[1024];
e.GetErrorMessage(szError, 1024);
AfxMessageBox(szError, MB_OK|MB_ICONSTOP);
}
return FALSE;
}
void CEnhancedListCtrl::SetMessage(LPCTSTR pstrMsg)
{
// dont update if the string is unchanged
if(m_csMessage.Compare(pstrMsg) != 0)
{
m_csMessage = pstrMsg;
Invalidate(TRUE); // force repaint of the background
UpdateWindow();
}
}
BOOL CEnhancedListCtrl::InsertColumns(LPENHANCEDCOLUMNINFO pInfo, int nColumns)
{
VERIFY_ISREADDATA(pInfo, sizeof(ENHANCEDCOLUMNINFO) * nColumns);
CString strTemp;
LV_COLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.iSubItem = 0;
for(int i = 0; i < nColumns; ++i)
{
if(pInfo->dwFlags & EHC_VISIBLE)
{
// if the string pointer is not a valid string, we assume that it is a resource id
if(::IsBadReadPtr(pInfo->pstrTitle, 1))
{
strTemp.LoadString((int)(pInfo->pstrTitle));
lvc.pszText = (TCHAR *)(LPCTSTR)strTemp;
}
else
{
lvc.pszText = (TCHAR *)(LPCTSTR)pInfo->pstrTitle;
}
lvc.cx = pInfo->nWidth;
lvc.fmt = pInfo->dwFlags & ~EHC_MASK;
int nCol = InsertColumn( i, &lvc );
SetColumnData(nCol, pInfo->nColumnID);
if(pInfo->dwFlags & EHC_CHECKABLE)
SetColumnCheck(nCol, 0);
++lvc.iSubItem;
}
++pInfo;
}
return TRUE;
}
BOOL CEnhancedListCtrl::UpdateColumns(LPENHANCEDCOLUMNINFO pInfo, int nColumns)
{
//
// save necessary states
//
int nOldSortID = GetColumnID(GetSortIndicator());
//
// delete all existing columns
//
while(DeleteColumn(0)) ;
//
// insert the new columns
//
BOOL bRet = InsertColumns(pInfo, nColumns);
//
// set all states back
//
if(bRet && IsColumnVisible(nOldSortID))
{
IndicateSortOrder(GetColumnIndex(nOldSortID), m_bLastSortOrder);
}
else
{
IndicateSortOrder(-1, m_bLastSortOrder);
/*
int nItems = GetHeaderCtrl()->GetItemCount();
for(int i = 0; i < nItems; i++)
{
// if(IsSortable(i))
{
IndicateSortOrder(i, m_bLastSortOrder);
break;
}
}
*/
}
return bRet;
}
BOOL CEnhancedListCtrl::LoadColumnState(LPCTSTR pstrName, LPENHANCEDCOLUMNINFO pInfo, int nColumns)
{
VERIFY_ISREADDATA(pInfo, sizeof(ENHANCEDCOLUMNINFO) * nColumns);
// copy the column info, we will modify it
LPENHANCEDCOLUMNINFO pInfo2 = new ENHANCEDCOLUMNINFO[nColumns];
int nCols = AfxGetApp()->GetProfileInt(pstrName, _T("Columns"), 0);
if(nCols)
{
int* nSaveInfo0 = NULL;
int* nSaveInfo1 = NULL;
int* nOrder = new int[nCols];
int o = 0;
UINT nBytes = nCols * sizeof(int);
TRACE(_T("Loading %i columns: "), nCols);
AfxGetApp()->GetProfileBinary(pstrName, _T("ColWidth"), (LPBYTE*)&nSaveInfo0, &nBytes);
AfxGetApp()->GetProfileBinary(pstrName, _T("ColID"), (LPBYTE*)&nSaveInfo1, &nBytes);
for(int i = 0; i < nCols; ++i)
{
for(int n = 0; n < nColumns; ++n)
{
if(pInfo[n].nColumnID == nSaveInfo1[i])
{
pInfo2[o] = pInfo[n];
TRACE(_T("%i(%ipx) "), pInfo2[o].nColumnID, nSaveInfo0[i]);
pInfo2[o].nWidth = nSaveInfo0[i];
pInfo2[o].dwFlags |= EHC_VISIBLE;
o++;
break;
}
}
}
delete[] nSaveInfo1;
delete[] nSaveInfo0;
// remove all existing columns
while(CListCtrl::DeleteColumn(0))
;
for(; o < nColumns; ++o)
{
pInfo2[o] = pInfo[o];
pInfo2[o].dwFlags &= ~EHC_VISIBLE;
}
InsertColumns(pInfo2, nColumns);
// GetHeaderCtrl()->SetOrderArray(nCols, &nOrder[0]);
delete[] nOrder;
}
delete[] pInfo2;
return TRUE;
}
BOOL CEnhancedListCtrl::SaveColumnState(LPCTSTR pstrName, LPENHANCEDCOLUMNINFO, int)
{
// VERIFY_ISREADDATA(pInfo, sizeof(ENHANCEDCOLUMNINFO) * nColumns);
int nCols = GetHeaderCtrl()->GetItemCount();
int* nSaveInfo0 = new int[nCols];
int* nSaveInfo1 = new int[nCols];
TRACE(_T("Saving %i columns : "), nCols);
for(int i = 0; i < nCols; ++i)
{
HDITEM hdi = {0};
hdi.mask = HDI_WIDTH;
//
// using OrderToIndex because we want the real order from 0 to X
//
GetHeaderCtrl()->GetItem(GetHeaderCtrl()->OrderToIndex(i), &hdi);
// GetHeaderCtrl()->GetItem(i, &hdi);
nSaveInfo0[i] = hdi.cxy;
nSaveInfo1[i] = GetColumnID(GetHeaderCtrl()->OrderToIndex(i));
// nSaveInfo1[i] = GetColumnID(i);
TRACE(_T("%i(%ipx) "), nSaveInfo1[i], nSaveInfo0[i]);
}
CString cs = pstrName;
AfxGetApp()->WriteProfileInt(pstrName, _T("Columns"), nCols);
AfxGetApp()->WriteProfileBinary(pstrName, _T("ColWidth"), (LPBYTE)&nSaveInfo0[0], sizeof(int)*nCols);
AfxGetApp()->WriteProfileBinary(pstrName, _T("ColID"), (LPBYTE)&nSaveInfo1[0], sizeof(int)*nCols);
TRACE(_T("\r\n"));
delete[] nSaveInfo1;
delete[] nSaveInfo0;
return TRUE;
}
LPENHANCEDCOLUMNINFO CEnhancedListCtrl::GetColumnInfo(const LPENHANCEDCOLUMNINFO pInfo, int nColumns)
{
VERIFY_ISREADDATA(pInfo, sizeof(ENHANCEDCOLUMNINFO) * nColumns);
int nCols = GetHeaderCtrl()->GetItemCount();
LPENHANCEDCOLUMNINFO pInfo2 = new ENHANCEDCOLUMNINFO[nColumns];
ZeroMemory(pInfo2, sizeof(ENHANCEDCOLUMNINFO) * nColumns);
for(int i = 0; i < nCols; ++i)
{
HDITEM hdi = {0};
hdi.mask = HDI_WIDTH;
int n = GetColumnID(GetHeaderCtrl()->OrderToIndex(i));
for(int t = 0; t < nColumns; ++t)
if(pInfo[t].nColumnID == n)
break;
CopyMemory(&pInfo2[i], &pInfo[t], sizeof(ENHANCEDCOLUMNINFO));
GetHeaderCtrl()->GetItem(GetHeaderCtrl()->OrderToIndex(i), &hdi);
pInfo2[i].nWidth = hdi.cxy;
pInfo2[i].dwFlags |= EHC_VISIBLE;
}
if(nColumns > nCols)
{
for(int i = 0; i < nColumns; ++i)
{
bool bFound = false;
for(int n = 0; n < nCols; ++n)
{
if(pInfo[i].nColumnID == pInfo2[n].nColumnID)
{
bFound = true;
break;
}
}
if(!bFound)
{
CopyMemory(&pInfo2[nCols], &pInfo[i], sizeof(ENHANCEDCOLUMNINFO));
pInfo2[nCols].dwFlags &= ~EHC_VISIBLE;
nCols++;
}
}
}
return pInfo2;
}
void CEnhancedListCtrl::SetMinRowHeight(int n)
{
if((CListCtrl::GetImageList(LVSIL_SMALL) == NULL) || (CListCtrl::GetImageList(LVSIL_SMALL) == m_ilRowHeight))
{
if(m_ilRowHeight)
{
m_ilRowHeight->DeleteImageList();
delete m_ilRowHeight;
}
m_ilRowHeight = NULL;
if(n > 0)
{
m_ilRowHeight = new CImageList();
m_ilRowHeight->Create(1, n, ILC_COLOR, 1, 0);
CListCtrl::SetImageList(m_ilRowHeight, LVSIL_SMALL);
}
}
}
int CEnhancedListCtrl::GetMinRowHeight()
{
if(GetItemCount() == 0)
{
LOGFONT font;
GetFont()->GetLogFont(&font);
return (-font.lfHeight)+2;
}
CRect rc;
CListCtrl::GetItemRect(0, &rc, LVIR_BOUNDS);
return rc.Height();
}