/**$F
// File: ReportCtrl.cpp
// Version: 3.0.2
//
// Author: Maarten Hoeben
// E-mail: hamster@xs4all.nl
//
// Implementation of the CReportCtrl and associated classes.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name and all copyright
// notices remains intact.
//
// An email letting me know how you are using it would be nice as well.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability for any damage/loss of business that
// this product may cause.
//
// Version history
//
// 1.0.1 - Initial release.
// 1.1.0 - Changed copyright notice.
// - Added RVN_LAYOUTCHANGED notification message.
// - Fixed SB_THUMBPOSITION and SB_THUMBTRACK problems.
// - Removed IDC_HEADERCTRL and IDC_REPORTCTRL definitions.
// Now hard coded because conflicts with some implementations.
// - Added SortAllColumns(), contributed by Roger Parkinson.
// - Added Chris Hambleton's suggestion to sort on image.
// indices when the (sub)item does not have text.
// - Fixed DeleteAllItems(), as suggested by Paul Leung.
// - Fixed DeleteAllItems() focus problem, as suggested by Dmitry ??.
// - Fixed DeleteItem(), as noted by Eugenio Ciceri.
// - Added fixes and suggestions of Serge Weinstock:
// - Fixed GetNextSelectedItem().
// - Fixed "no items to show" position.
// - Added support for WS_BORDER style.
// - Added RVN_ITEMDELETED notification.
// - Added mouse-wheel support.
// - Added extended horizontal grid style and changed regular
// horizontal grid style.
// - Fixed selection inversion using space key.
// - Fixed focus selection using ctrl key.
// - Fixed focus on deleted items.
// - Added RVS_OWNERDATA style.
// - Changed notification structure to facilitate
// RVS_OWNERDATA style.
// - Changed hit test info structure to facilitate
// RVS_FOCUSSUBITEMS style.
// - Added RVS_FOCUSSUBITEMS style to enable focus on individual
// subitems to facilitate subitem editing.
// - Added dialog control ID to WPARAM of notification messages.
// - Added subitem editing functionality.
// - Added CReportEditCtrl edit control.
// - Added CReportComboCtrl edit control.
// - Added support for RVIM_TEXT on empty strings (required for
// editing).
// - Added RVIS_READONLY state.
// - Added rect member to RVHITTESTINFO structure.
// - Added RVS_EXPANDSUBITEMS style.
// - Added CReportTipCtrl, based on code by Zafir Anjum.
// - Added background image support, based on code contributed by
// Ernest Laurentin.
// 1.1.1 - Added RVN_KEYDOWN notification.
// - Fixed DeleteItem as suggested by Thomas Freudenberg
// (Luca; this time I tested the function :-).
// - Removed use of GetBuffer() in CReportData::GetSubItem()
// to improve performance.
// - Improved sorting performance by using quick sort instead
// of bubble sort (QuickSort based on Martin Ziacek's
// QArray implementation).
// - Added item to row mapping for improved lookup performance.
// - Fixed GetItemForRow to retrieve ITEM in OWNERDATA style.
// - Added item/row manipulation functions.
// - Fixed column reordering and storage for invisible
// controls.
// - Added GetItemHeight function.
// - Added NMRVHEADER notification structure.
// - Replaced RVN_COLUMNCLICK with RVN_HEADERCLICK. This
// new notification uses the new NMRVHEADER structure.
// - Added MeasureItem function.
// - Added RVN_DIVIDERDBLCLICK notification and code that
// implements optimal column sizing.
// - Added right mouse button handling and RVN_ITEMRCLICK
// notification.
// - Added item cache to optimize lookup of ITEM structures
// in RVS_OWNERDATA style.
// - Fixed a bug related to creation of the control from
// from dialog templates.
// - Fixed double RVN_ENDITEMEDIT messages in edit controls.
// - Fixed edit cancellation on style changes.
// - Fixed control keys in edit controls.
// - Added AddItem operations.
// - Added FindItem operation.
// - Changed text string parameter of InsertItem to LPCTSTR.
// - Changed behavior of edit row and RVS_FOCUSSUBITEMS style.
// The focus on the edit row is now always on individual columns,
// while the focus on other rows is only on individual columns
// if the RVS_FOCUSSUBITEMS style is used.
// - Fixed a bug related to editing of edit row while not visible.
// - Added BeginEdit functions to edit controls.
// - Changed 'Column' handling function names in order to stop
// confusion about what is a column and what is a subitem.
// A column is a visible (or active) subitem.
// - Added GetSubItemWidth and SetSubItemWidth functions, courtesy
// of Jonathan Kotas.
// - Removed restriction to sort on columns only.
// - Added Focus manipulation functions, suggested by Attila Hajdrik.
// - Added subitem persistent style, as suggested by Attila Hajdrik.
// - Fixed assertion on column invisible deactivation.
// - Fixed several bugs related to subitem dynamic (de)activation and
// column manipulation.
// - Added RVP_SORTTOOLTIP property to allow changing "Sort by"
// tooltip.
// - Fixed pResult assignment in OnHdnItemClick.
// - Added ResortItems function.
// - Removed restriction for at least one visible subitem.
// - Added GetActiveSubItemCount().
// - Added flat style to CReportSubItemListCtrl
// (use WS_EX_STATICEDGE).
// - Added subitem disable to CReportSubItemListCtrl.
// - Added UndefineAllSubItems, as suggested by Brian Pollack.
// - Added RVCF_SUBITEM_NOFOCUS, as suggested by Attila Hajdrik.
// - Added RVP_NOTIFYMASK property for setting a mask on what
// notifications must be generated, as suggested by A. Hajdrik.
// - Made Notify function virtual.
// - Fixed OnVScroll thumb track for item count > 32767.
// - Improved SelectRows performance by using list instead of
// iteration through all items to clear the previous selection.
// - Fixed several glitches related to subitem activation.
// - Added IsItemVisible().
// - Fixed scrollbar assertion in SetItemCount().
// - Improved performance of CReportData.
// - Added PreviewHeight function to calculate the height of
// preview text.
// - Made several implementation function virtual.
// - Minor performance improvement by replacing printf with prepared
// fixed in New.
// 1.1.2 - Fixed MoveUp/MoveDown related selection bug.
// - Fixed focus on item sort as suggested by Matija Jerkovic.
// - Fixed GetProfile and improved GetItem as suggested by
// Eugenio Ciceri.
// - Several fixes to CReportView as suggested by Eugenio Ciceri.
// - Fixed ClearSelection, added InvertSelection SelectAll and
// SetSelection variants (SelectMatch and SelectLessEq) as
// contributed by Eugenio Ciceri.
// - Fixed SelectRows as suggested by Matija Jerkovic.
// - Fixed InsertItem and DeleteItem as suggested by Eugenio Ciceri.
// - Fixed DeleteAllItems.
// - Fixed grid property, thanks to Roland Kopetzky.
// - Applied fixes to edit row as suggested by Alex.
// - Added support for disabled style.
// - Fixed obstructed bottom selected row redraw problem.
// - Added GetColor, SetColor and DeleteAllColors methods.
// - Fixed (ctrl+)page-up assertion in empty reports.
// - Added UpdateWindow on strategic places to improve
// drawing on slower or heavy loaded systems.
// - Fixed assertion in HitTest on edit row.
// - Added "browse" button option to CReportEditCtrl.
// - Added RVN_BUTTONCLICK notification.
// - Added RVIS_OWNERDRAW state and RVN_ITEMDRAW notification
// to support owner drawn subitems.
// - Changed default font to default GUI font.
// - Added second PreviewHeight function to calculate the height of
// preview text from the text and an optional rectangle.
// - Fixed a selection bug in SetItemCount().
// - Fixed another selection bug in DeleteItem().
// - Fixed FindItem for LPARAM as suggested by Armin Z�rcher.
// 2.0.0 - Made scrolling with left or right cursor key dependent on
// focus subitems style.
// - Fixed resource leak in empty list, as suggested by Florent
// Odelain.
// - Fixed ResortItems and DeactivateSubItem bugs as suggested by
// Rafael Lombardi Santos.
// - Fixed HitTest on reordered columns, reported by Trevor Ash.
// - Fixed item rect returned in hitinfo structure for scrolled
// controls. This fixes incorrect positioning of tip windows.
// - Added CReportHeaderCtrl to access CFlatHeaderCtrl's protected
// members.
// - Added first column indent functions, to support hierarchy
// GUI elements.
// - Fixed HitTest to retrieve correct item data.
// - Added parameter to sort callback.
// - Removed SortAllSubItems, because implementation was not
// maintained and buggy.
// - Fixed IsItemVisible and added GetTopIndex(), PageUp() and
// PageDown() as suggested by Alina Kozlovsky.
// - Added tree control features, both in preparation of group
// view mode as well as a standalone feature.
// - Fixed subitem text drawing following uninitialized subitems.
// - Changed CompareItems callback and functions to support
// separate subitems to enable tree view and group view sorting.
// - Extended CompareItems to sort on checkboxes as suggested by
// Peter Lagerhem.
// - Fixed offset of subitem tip for items with images or checks
// and text and related hit testing issues.
// - Fixed NOHEADER style.
// - Changed selected items and tree boxes in tree view mode visuals
// to match common control look and feel.
// - Fixed default height setting for font size updates with images.
// - Fixed cosmetic bug with RVS_SHOWHGRID style, as suggested by Matrix.
// - Fixed SelectAll() for empty control as suggested by Dmitry Sazonov.
// - Added GetStyle().
// - Added RVN_HEADERRCLICK to support popup menus on header.
// - Fixed tooltip double click and ALT key relay events by removing
// mouse capture.
// 2.0.1 - Fixed bug in DeleteItem for tree control mode.
// - Fixed item expansion for single subitem hierarchy items.
// - Added GetExpandedItemText to allow expanded items to show different
// text from the subitem text.
// - Added support for radio button and disabled check marks and
// radio buttons.
// - Added RVP_ENABLEFLATCHECKMARK property, to control the visual style
// of check marks or radio buttons.
// - Added support for check mark and radio button image list.
// - Changed CurrentFocus() to GetCurrentFocus().
// - Changed OnKillFocus to recognize all child windows.
// - Extended MeasureItem function.
// - Fixed GetNextItem() for RVTI_ROOT.
// - Added support for SetRedraw suggested by Phil J Pearson.
// - Optimized InsertItem performance by skipping GetRowFromItem
// when the focus is not on an reorderable row, as suggested by
// Phil J Pearson.
// - Added disabled background function to CReportSubItemListCtrl.
// - Added pre-create style passing to CReportView.
// - Adjusted edit box position.
// - Fixed a bug related to keydown messages in unfocused state.
// - Fixed tip redraw problem and tip background color mismatch.
// - Fixed kill tip on WM_KILLFOCUS, finally fixing click and double
// click on expanded subitems.
// - Added GetSelectedItems method.
// - Fixed a selection bug in ClearSelection.
// - Made GetReportCtrlPtr virtual and changed CReportView to use
// overridden function to get a pointer to the embedded CReportCtrl.
// This allows control derived from CReportCtrl to be embedded in
// CReportView.
// - Added UpdateWindow to SetRedraw when re-enabling redrawing.
// - Removed legacy definitions from header file.
// - Added blended image support through state bits, focus and selection
// through the RVP_ENABLEIMAGEBLENDING and RVP_BLENDCOLOR properties.
// - Added support for overlay images.
// - Added Win2K tip fading to CReportTipCtrl.
// - Fixed nFormat subitem member update in OnHdnItemChanged, as suggested
// by Sven Ritter.
// - Added selective item cache flush to SetItemCount.
// - Added GetItemString() function to retrieve an item as a string.
// - Added clipboard Copy support and clipboard separator and indent
// properties.
// - Fixed OnRvnEndItemEdit to not loose lParam, as suggested by
// Paul Hurley.
// - Added GetSortSettings() to retrieve sorting settings.
// - Made SelectRows virtual to allow owner data multiple selection
// management.
// - Fixed a bug in DeleteItem for trees.
// - Fixed various mouse button, keyboard selection/focus issues.
// 3.0.0 - Compile with either VC-6 or VC-7
// - Support grouping by categories
// - Support graphical tree display
// - Fixed various scroll problems
// - Fixed auto sizing the columms
// - Fixed header match window width
// - Support time
// - Time for category displays Today, Tomorrow etc
// - Use a stable sort
// - Added Popup menu to select category
// - Shift key can be used to resize a column without modifying header size
// 3.0.1 - Support UNICODE
// 3.0.2 - Fix bug in control in dialog
*/
#include "stdafx.h"
#include "ReportCtrl.h"
#include "MemDC.h"
#include ".\reportctrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define REPORTDATA_MAX_NODATA 20
static TCHAR g_cSeparator = _T('|');
static TCHAR g_cCBSeparator = _T('\t');
static CString g_strCBIndent = _T(" ");
static TCHAR g_szNoData[REPORTDATA_MAX_NODATA];
static lpfnUpdateLayeredWindow g_lpfnUpdateLayeredWindow = NULL;
static lpfnSetLayeredWindowAttributes g_lpfnSetLayeredWindowAttributes = NULL;
// CCategoryTime
CCategoryTime::CCategoryTime(LPTSTR lptstr)
{
ParseDateTime(lptstr);
}
CString CCategoryTime::NameCategory(COleDateTime baseTime, bool b_Both)
{
if (GetStatus() != COleDateTime::valid)
return _T("");
COleDateTimeSpan oneDay(1, 0, 0, 0);
COleDateTime ct(baseTime.GetYear(), baseTime.GetMonth(), baseTime.GetDay(), 0, 0, 0);
if (*this > baseTime)
{
ct += oneDay;
if (*this < ct)
return _T("Today");
if (*this < ct + oneDay)
return _T("Tomorrow");
ct += (7 - baseTime.GetDayOfWeek()) * oneDay;
if (*this < ct)
return b_Both ? _T("Later this week") : _T("This Week");
ct += 7 * oneDay;
if (*this < ct)
return _T("Next week");
ct.SetDate(baseTime.GetYear() + (baseTime.GetMonth() == 12), baseTime.GetMonth() % 12 + 1, 1);
if (*this < ct)
return b_Both ? _T("Later this Month") : _T("This Month");
ct.SetDate(baseTime.GetYear() + (baseTime.GetMonth() >= 11), (baseTime.GetMonth() + 1) % 12 + 1, 1);
if (*this < ct)
return _T("Next Month");
ct.SetDate(baseTime.GetYear() + 1, 1, 1);
if (*this < ct)
return b_Both ? _T("Later this year") : _T("This year");
ct.SetDate(baseTime.GetYear() + 2, 1, 1);
if (*this < ct)
return _T("Next year");
return _T("Later");
}
if (ct <= *this)
return _T("Today");
if (ct - oneDay <= *this)
return _T("Yesterday");
ct -= (baseTime.GetDayOfWeek() - 1) * oneDay;
if (ct <= *this)
return b_Both ? _T("Earlier this week") : _T("This Week");
ct -= 7 * oneDay;
if (ct <= *this)
return _T("Last Week");
ct.SetDate(baseTime.GetYear(), baseTime.GetMonth(), 1);
if (ct <= *this)
return b_Both ? _T("Earlier this Month") : _T("This Month");
if (baseTime.GetMonth() == 1)
ct.SetDate(baseTime.GetYear() - 1, 12, 1);
else
ct.SetDate(baseTime.GetYear(), baseTime.GetMonth() - 1, 1);
if (ct <= *this)
return _T("Last month");
ct.SetDate(ct.GetYear() - 1, 1, 1);
if (ct <= *this)
return _T("Last Year");
return _T("Earlier");
}
BOOL CCategoryTime::SameCategoryAs(CCategoryTime other, COleDateTime baseTime, bool b_Both)
{
if (GetStatus() == COleDateTime::valid && other.GetStatus() == COleDateTime::valid)
return other.NameCategory(baseTime, b_Both) == NameCategory(baseTime, b_Both);
else
return FALSE;
}
// CReportData
CReportData::CReportData()
{
}
CReportData::~CReportData()
{
}
BOOL CReportData::New(INT iSubItems)
{
LPTSTR lpsz = GetBuffer(iSubItems * REPORTDATA_MAX_NODATA);
lpsz[0] = 0;
for (INT i = 0; i < iSubItems; i++)
lpsz = _tcscat(lpsz, g_szNoData);
ReleaseBuffer();
return TRUE;
}
BOOL CReportData::GetSubItem(INT iSubItem, LPINT lpiImage, LPINT lpiOverlay, LPINT lpiCheck, LPINT lpiColor,
LPTSTR lpszText, LPINT lpiTextMax)
{
INT i, iPos, iText;
for (i = 0, iPos = 0; i < iSubItem && iPos >= 0; i++, iPos++)
iPos = Find(g_cSeparator, iPos);
if (iPos < 0)
return FALSE;
LPCTSTR lpsz = GetBuffer(0);
lpsz = &lpsz[iPos];
VERIFY(_stscanf(lpsz, _T("(%d,%d,%d,%d,%d)"), lpiImage, lpiOverlay, lpiCheck, lpiColor, &iText));
if (iText < 0)
*lpiTextMax = -1;
if (*lpiImage == -1 && *lpiOverlay == -1 && *lpiCheck == -1 && *lpiColor == -1 && *lpiTextMax == -1)
return FALSE;
if (iText < 0)
return TRUE;
lpsz = _tcspbrk(lpsz, _T(")")) + 1;
if (lpsz && lpszText)
{
for (INT iTextSize = 0; iTextSize < (*lpiTextMax) - 1 && *lpsz != g_cSeparator; iTextSize++)
lpszText[iTextSize] = *lpsz++;
lpszText[iTextSize] = 0;
}
return TRUE;
}
BOOL CReportData::SetSubItem(INT iSubItem, INT iImage, INT iOverlay, INT iCheck, INT iColor, LPCTSTR lpszText)
{
if (!InsertSubItem(iSubItem, iImage, iOverlay, iCheck, iColor, lpszText))
return FALSE;
if (!DeleteSubItem(iSubItem + 1))
return FALSE;
return TRUE;
}
BOOL CReportData::InsertSubItem(INT iSubItem, INT iImage, INT iOverlay, INT iCheck, INT iColor, LPCTSTR lpszText)
{
INT i, iPos, iText;
for (i = 0, iPos = 0; i < iSubItem && iPos >= 0; i++, iPos++)
iPos = Find(g_cSeparator, iPos);
if (iPos < 0)
return FALSE;
if (lpszText == NULL)
{
lpszText = _T("");
iText = -1;
}
else
iText = _tcslen(lpszText);
TCHAR sz[32 + REPORTCTRL_MAX_TEXT];
_stprintf(sz, _T("(%d,%d,%d,%d,%d)%s%c"), iImage, iOverlay, iCheck, iColor, iText, lpszText, g_cSeparator);
Insert(iPos, sz);
return TRUE;
}
BOOL CReportData::DeleteSubItem(INT iSubItem)
{
INT i, iPos1, iPos2;
for (i = 0, iPos1 = 0; i < iSubItem && iPos1 >= 0; i++, iPos1++)
iPos1 = Find(g_cSeparator, iPos1);
if (iPos1 < 0)
return FALSE;
iPos2 = Find(g_cSeparator, iPos1);
if (iPos2++ < 0)
return FALSE;
Delete(iPos1, iPos2 - iPos1);
return TRUE;
}
// CReportView
IMPLEMENT_DYNCREATE(CReportView, CView)
CReportView::CReportView()
{
m_bCreated = FALSE;
m_nExtra = 0;
}
CReportView::~CReportView()
{
}
BOOL CReportView::PreCreateWindow(CREATESTRUCT &cs)
{
BOOL bResult = CView::PreCreateWindow(cs);
m_wndReportCtrl.m_dwStyle = cs.style & 0xFFFF;
return bResult;
}
void CReportView::OnInitialUpdate()
{
CView::OnInitialUpdate();
if (!m_bCreated && GetReportCtrlPtr() == &m_wndReportCtrl)
{
m_wndReportCtrl.m_dwStyle |= m_nExtra | WS_CHILD | WS_TABSTOP | WS_VISIBLE;
CRect rect;
GetClientRect(rect);
if (m_wndReportCtrl.Create(m_wndReportCtrl.m_dwStyle, rect, this, 0) == NULL)
AfxThrowMemoryException();
}
m_bCreated = TRUE;
}
BEGIN_MESSAGE_MAP(CReportView, CView)
//{{AFX_MSG_MAP(CReportView)
ON_WM_SIZE()
ON_WM_SETFOCUS()
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CReportView drawing
void CReportView::OnDraw(CDC *pDC)
{
UNREFERENCED_PARAMETER(pDC);
}
// CReportView diagnostics
#ifdef _DEBUG
void CReportView::AssertValid() const
{
CView::AssertValid();
}
void CReportView::Dump(CDumpContext &dc) const
{
CView::Dump(dc);
}
#endif // DEBUG
// CReportView attributes
CReportCtrl &CReportView::GetReportCtrl()
{
ASSERT(GetReportCtrlPtr() == &m_wndReportCtrl); // Don't use this function
// GetReportCtrlPtr is overridden.
return m_wndReportCtrl;
}
CReportCtrl *CReportView::GetReportCtrlPtr()
{
return &m_wndReportCtrl;
}
// CReportView implementation
BOOL CReportView::OnEraseBkgnd(CDC *pDC)
{
UNREFERENCED_PARAMETER(pDC);
return TRUE;
}
void CReportView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
if (m_bCreated && GetReportCtrlPtr()->GetSafeHwnd() != NULL)
{
CRect rect;
GetClientRect(rect);
GetReportCtrlPtr()->MoveWindow(rect);
}
}
void CReportView::OnSetFocus(CWnd *pOldWnd)
{
UNREFERENCED_PARAMETER(pOldWnd);
if (m_bCreated && GetReportCtrlPtr()->GetSafeHwnd() != NULL)
GetReportCtrlPtr()->SetFocus();
}
// CReportCtrl
IMPLEMENT_DYNCREATE(CReportCtrl, CWnd)
CReportCtrl::CReportCtrl()
{
WNDCLASS wndclass;
HINSTANCE hInst = AfxGetInstanceHandle();
if (!(::GetClassInfo(hInst, REPORTCTRL_CLASSNAME, &wndclass)))
{
wndclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = ::DefWindowProc;
wndclass.cbClsExtra = wndclass.cbWndExtra = 0;
wndclass.hInstance = hInst;
wndclass.hIcon = NULL;
wndclass.hCursor = LoadCursor(hInst, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) COLOR_WINDOW;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = REPORTCTRL_CLASSNAME;
if (!AfxRegisterClass(&wndclass))
AfxThrowResourceException();
}
m_iScrollWindow = 0;
m_bSubclassFromCreate = FALSE;
m_iTreeDepth = 0;
m_bDoubleBuffer = TRUE;
m_bRedrawFlag = TRUE;
m_iSpacing = 6;
m_nRowsPerWheelNotch = GetMouseScrollLines();
m_dwStyle = 0;
m_bPreview = TRUE;
m_bHorzScrollBar = TRUE;
m_font.CreateStockObject(DEFAULT_GUI_FONT);
m_pImageList = NULL;
m_pCheckList = NULL;
m_sizeImage.cx = 0;
m_sizeImage.cy = 0;
m_sizeCheck.cx = 8;
m_sizeCheck.cy = 8;
m_arrayColors.SetSize(0, 8);
m_iGridStyle = PS_SOLID;
m_nFrameControlStyle = DFCS_FLAT;
m_nBlendStyle = RVP_BLEND_NONE;
m_crBlendColor = RGB(0xff, 0xff, 0xff);
m_strNoItems = _T("There are no items to show in this view.");
m_strSortBy = _T("Sort by: %s");
_stprintf(g_szNoData, _T("(-1,-1,-1,-1,-1)%c"), g_cSeparator);
m_iDefaultWidth = 200;
m_iDefaultHeight = 10;
m_iVirtualWidth = 0;
m_iVirtualHeight = 0;
m_arraySubItems.SetSize(0, 8);
m_arrayItems.SetSize(0, 128);
m_tiRoot.bOpen = TRUE;
m_bEditValid = FALSE;
m_bUseItemCacheMap = TRUE;
for (UINT n = 0; n < REPORTCTRL_MAX_CACHE; n++)
m_aciCache[n].iItem = RVI_INVALID;
m_bProcessKey = FALSE;
m_bUpdateItemMap = FALSE;
m_bColumnsReordered = FALSE;
m_bWidthChanged = FALSE;
m_arrayColumns.SetSize(0, 8);
m_bFocus = FALSE;
m_iFocusRow = RVI_EDIT;
m_iFocusColumn = -1;
m_iSelectRow = 0;
m_arrayRows.SetSize(0, 128);
m_bIndentColumn = FALSE;
m_bIndentGrey = FALSE;
m_iIndentColumn = 0;
m_iIndentColumnPending = -1;
m_iEditItem = RVI_INVALID;
m_iEditSubItem = -1;
m_hEditWnd = NULL;
m_lprsilc = NULL;
m_lpfnrvc = NULL;
m_lParamCompare = 0;
m_uNotifyMask = RVNM_ALL;
m_bCategoryAscending = FALSE;
m_iCategoryColumn = -1;
m_iCategorySubItem = -1;
m_iCategoryImage = -1;
m_wndCategoryHdr = NULL;
m_bSorting = FALSE;
m_bFrameFocus = FALSE;
}
CReportCtrl::~CReportCtrl()
{
m_wndHeader.DestroyWindow();
if (m_wndCategoryHdr)
delete m_wndCategoryHdr;
m_wndTip.DestroyWindow();
if (m_palette.m_hObject)
m_palette.DeleteObject();
if (m_bitmap.m_hObject != NULL)
m_bitmap.DeleteObject();
if (m_font.m_hObject)
m_font.DeleteObject();
if (m_fontBold.m_hObject)
m_fontBold.DeleteObject();
}
BOOL CReportCtrl::Create()
{
CRect rect(0, 0, 0, 0);
DWORD dwStyle = HDS_HORZ | HDS_BUTTONS | HDS_FULLDRAG | HDS_DRAGDROP | CCS_TOP;
if (!m_wndHeader.Create(dwStyle, rect, this, 0))
return FALSE;
if (!m_wndTip.Create(this))
return FALSE;
CWnd *pWnd = GetParent();
if (pWnd)
{
CFont *pFont = pWnd->GetFont();
if (pFont)
{
LOGFONT lf;
pFont->GetLogFont(&lf);
m_font.DeleteObject();
m_font.CreateFontIndirect(&lf);
}
}
OnSetFont((WPARAM) ((HFONT) m_font), FALSE);
m_wndHeader.SetFont(&m_font, FALSE);
m_dwStyle = CWnd::GetStyle();
Layout();
GetSysColors();
return TRUE;
}
BOOL CReportCtrl::Create(DWORD dwStyle, const RECT &rect, CWnd *pParentWnd, UINT nID, CCreateContext *pContext)
{
m_bSubclassFromCreate = TRUE;
if (!CWnd::Create(REPORTCTRL_CLASSNAME, NULL, dwStyle, rect, pParentWnd, nID, pContext))
return FALSE;
return Create();
}
void CReportCtrl::PreSubclassWindow()
{
CWnd::PreSubclassWindow();
if (!m_bSubclassFromCreate)
if (!Create())
AfxThrowMemoryException();
}
void CReportCtrl::OnDestroy()
{
DeleteAllItems();
CWnd::OnDestroy();
}
BEGIN_MESSAGE_MAP(CReportCtrl, CWnd)
//{{AFX_MSG_MAP(CReportCtrl)
ON_WM_DESTROY()
ON_WM_SIZE()
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_SYSCOLORCHANGE()
ON_WM_SETTINGCHANGE()
ON_NOTIFY(HDN_ITEMCHANGED, 0, OnHdnItemChanged)
ON_NOTIFY(HDN_ITEMCHANGINGA, 0, OnHdnItemchanging)
ON_NOTIFY(HDN_ITEMCHANGINGW, 0, OnHdnItemchanging)
ON_NOTIFY(HDN_ITEMCLICK, 0, OnHdnItemClick)
ON_NOTIFY(HDN_BEGINDRAG, 0, OnHdnBeginDrag)
ON_NOTIFY(HDN_ENDDRAG, 0, OnHdnEndDrag)
ON_NOTIFY(HDN_DIVIDERDBLCLICK, 0, OnHdnDividerDblClick)
ON_NOTIFY(RVN_ENDITEMEDIT, 0, OnRvnEndItemEdit)
ON_WM_HSCROLL()
ON_WM_VSCROLL()
ON_WM_LBUTTONDOWN()
ON_WM_SETCURSOR()
ON_WM_KEYDOWN()
ON_WM_GETDLGCODE()
ON_WM_LBUTTONDBLCLK()
ON_WM_QUERYNEWPALETTE()
ON_WM_PALETTECHANGED()
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
ON_WM_NCCALCSIZE()
ON_WM_NCPAINT()
ON_WM_MOUSEWHEEL()
ON_WM_CHAR()
ON_WM_MOUSEMOVE()
ON_WM_RBUTTONDOWN()
ON_WM_WINDOWPOSCHANGING()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_SETFONT, OnSetFont)
ON_MESSAGE(WM_GETFONT, OnGetFont)
END_MESSAGE_MAP()
// CReportCtrl attributes
DWORD CReportCtrl::GetStyle() const
{
return (CWnd::GetStyle() & 0xFFFF0000) | m_dwStyle;
}
BOOL CReportCtrl::ModifyProperty(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case RVP_SPACING:
m_iSpacing = (INT) lParam;
break;
case RVP_CHECK:
m_sizeCheck.cx = LOWORD(lParam);
m_sizeCheck.cy = HIWORD(lParam);
break;
case RVP_NOITEMTEXT:
m_strNoItems = (LPCTSTR) lParam;
break;
case RVP_GRIDSTYLE:
switch (lParam)
{
case RVP_GRIDSTYLE_DOT:
m_iGridStyle = PS_DOT;
break;
case RVP_GRIDSTYLE_DASH:
m_iGridStyle = PS_DASH;
break;
case RVP_GRIDSTYLE_SOLID:
m_iGridStyle = PS_SOLID;
break;
default:
return FALSE;
}
break;
case RVP_SORTTOOLTIP:
m_strSortBy = (LPCTSTR) lParam;
break;
case RVP_NOTIFYMASK:
m_uNotifyMask = (ULONG) lParam;
break;
case RVP_ENABLEITEMCACHEMAP:
m_bUseItemCacheMap = (BOOL) lParam;
break;
case RVP_SEPARATOR:
g_cSeparator = (TCHAR) lParam;
_stprintf(g_szNoData, _T("(-1,-1,-1,-1)%c"), g_cSeparator);
break;
case RVP_ENABLEPREVIEW:
m_bPreview = (BOOL) lParam;
break;
case RVP_ENABLEHORZSCROLLBAR:
m_bHorzScrollBar = (BOOL) lParam;
break;
case RVP_FRAMECONTROLSTYLE:
switch (lParam)
{
case RVP_FCSTYLE_FLAT:
m_nFrameControlStyle = DFCS_FLAT;
break;
case RVP_FCSTYLE_MONO:
m_nFrameControlStyle = DFCS_MONO;
break;
case RVP_FCSTYLE_NORMAL:
m_nFrameControlStyle = 0;
break;
default:
return FALSE;
}
break;
case RVP_ENABLEIMAGEBLENDING:
m_nBlendStyle = (UINT) lParam;
break;
case RVP_BLENDCOLOR:
m_crBlendColor = (COLORREF) lParam;
break;
case RVP_CBSEPARATOR:
g_cCBSeparator = (TCHAR) lParam;
break;
case RVP_CBINDENT:
g_strCBIndent = (LPCTSTR) lParam;
break;
default:
return FALSE;
}
Layout();
return TRUE;
}
INT CReportCtrl::GetSubItemCount()
{
return m_arraySubItems.GetSize();
}
CString CReportCtrl::GetSubItemText(INT iSubItem)
{
CString str;
if (iSubItem != -1)
{
ASSERT(iSubItem >= 0);
ASSERT(iSubItem < m_arraySubItems.GetSize()); // Specify a valid subitem
SUBITEM &subitem = m_arraySubItems[iSubItem];
if (subitem.nFormat & RVCF_TEXT)
str = subitem.strText.GetBuffer(0);
}
return str;
}
void CReportCtrl::GetSubItem(INT iSubItem, LPHDITEM phdi)
{
ASSERT(iSubItem >= 0);
ASSERT(iSubItem < m_arraySubItems.GetSize()); // Specify a valid subitem
SUBITEM &subitem = m_arraySubItems[iSubItem];
phdi->mask = HDI_FORMAT | HDI_WIDTH | HDI_LPARAM;
phdi->fmt = subitem.nFormat & RVCF_MASK;
phdi->cxy = subitem.iWidth;
phdi->lParam = (LPARAM) iSubItem;
if (subitem.nFormat & RVCF_IMAGE)
{
phdi->mask |= HDI_IMAGE;
phdi->iImage = subitem.iImage;
}
if (subitem.nFormat & RVCF_TEXT)
{
phdi->mask |= HDI_TEXT;
phdi->pszText = subitem.strText.GetBuffer(0);
}
}
void CReportCtrl::GetSubItemEx(INT iSubItem, HDITEMEX *phditemex)
{
if (iSubItem != -1)
{
ASSERT(iSubItem >= 0);
ASSERT(iSubItem < m_arraySubItems.GetSize()); // Specify a valid subitem
SUBITEM &subitem = m_arraySubItems[iSubItem];
phditemex->nStyle = (subitem.nFormat & RVCF_EX_MASK) >> 16;
phditemex->iMinWidth = subitem.iMinWidth;
phditemex->iMaxWidth = subitem.iMaxWidth;
if (phditemex->nStyle & HDF_EX_TOOLTIP)
phditemex->strToolTip = subitem.strText;
}
}
INT CReportCtrl::ActivateSubItem(INT iSubItem, INT iColumn)
{
ASSERT(iSubItem >= 0);
ASSERT(iSubItem < m_arraySubItems.GetSize()); // Specify a valid subitem
SUBITEM &subitem = m_arraySubItems[iSubItem];
INT iResult = -1;
ExpandCategories(0);
if (GetColumnFromSubItem(iSubItem) < 0)
{
UnindentFirstColumn();
try
{
HDITEM hdi;
hdi.mask = HDI_FORMAT | HDI_WIDTH | HDI_LPARAM | HDI_ORDER;
hdi.fmt = subitem.nFormat & RVCF_MASK;
hdi.cxy = subitem.iWidth;
hdi.iOrder = iColumn;
hdi.lParam = (LPARAM) iSubItem;
if (subitem.nFormat & RVCF_IMAGE)
{
hdi.mask |= HDI_IMAGE;
hdi.iImage = subitem.iImage;
}
if (subitem.nFormat & RVCF_TEXT)
{
hdi.mask |= HDI_TEXT;
hdi.pszText = subitem.strText.GetBuffer(0);
}
iResult = m_wndHeader.InsertItem(iSubItem, &hdi);
if (iResult >= 0)
{
m_iVirtualWidth += subitem.iWidth;
HDITEMEX hditemex;
hditemex.nStyle = (subitem.nFormat & RVCF_EX_MASK) >> 16;
hditemex.iMinWidth = subitem.iMinWidth;
hditemex.iMaxWidth = subitem.iMaxWidth;
if (hditemex.nStyle & HDF_EX_TOOLTIP)
hditemex.strToolTip.Format(m_strSortBy, subitem.strText);
m_wndHeader.SetItemEx(iResult, &hditemex);
hdi.mask = HDI_WIDTH;
m_wndHeader.GetItem(iResult, &hdi);
subitem.iWidth = hdi.cxy;
ReorderColumns();
BOOL bAscending;
INT iSortItem = m_wndHeader.GetSortColumn(&bAscending);
if (iSubItem <= iSortItem)
m_wndHeader.SetSortColumn(1 + iSortItem, bAscending);
if (iSubItem == m_iCategorySubItem)
m_iCategorySubItem = -1;
m_bWidthChanged = TRUE;
}
IndentFirstColumn();
}
catch(CMemoryException * e)
{
e->Delete();
if (iResult >= 0)
m_wndHeader.DeleteItem(iResult);
}
}
if (m_wndCategoryHdr != NULL)
{
m_wndCategoryHdr->NewCategory();
m_wndCategoryHdr->Show();
}
if (m_dwStyle & RVS_TREEVIEW)
BuildTree();
return iResult;
}
BOOL CReportCtrl::DeactivateSubItem(INT iSubItem)
{
ASSERT(iSubItem >= 0);
ASSERT(iSubItem < m_arraySubItems.GetSize()); // Specify a valid subitem
INT iColumn = GetColumnFromSubItem(iSubItem);
if (iColumn < 0)
return FALSE;
UnindentFirstColumn();
BOOL bAscending;
INT iSortItem = m_wndHeader.GetSortColumn(&bAscending);
INT iSortColumn = -1, iColumns = m_arrayColumns.GetSize();
for (int i = 0; i < iColumns; i++)
if (iSortItem == m_arrayColumns[i])
iSortColumn = i;
HDITEM hdi;
hdi.mask = HDI_WIDTH;
m_wndHeader.GetItem(m_arrayColumns[iColumn], &hdi);
BOOL bResult = m_wndHeader.DeleteItem(m_arrayColumns[iColumn]);
m_iVirtualWidth -= hdi.cxy;
ReorderColumns();
if (iSortColumn > 0)
{
if (iSortColumn == iColumn)
m_wndHeader.SetSortColumn(-1, TRUE);
else
m_wndHeader.SetSortColumn(m_arrayColumns[iSortColumn - (iColumn <= iSortColumn)], bAscending);
}
IndentFirstColumn();
m_bWidthChanged = TRUE;
Layout();
return bResult;
}
BOOL CReportCtrl::DeactivateAllSubItems()
{
INT iSubItems = m_arraySubItems.GetSize();
for (INT iSubItem = 0; iSubItem < iSubItems; iSubItem++)
{
if (IsActiveSubItem(iSubItem))
{
if (!DeactivateSubItem(iSubItem))
return FALSE;
}
}
return TRUE;
}
BOOL CReportCtrl::IsActiveSubItem(INT iSubItem)
{
INT iColumn = GetColumnFromSubItem(iSubItem);
return iColumn < 0 ? FALSE : TRUE;
}
INT CReportCtrl::GetActiveSubItemCount()
{
return m_arrayColumns.GetSize();
}
INT CReportCtrl::GetSubItemWidth(INT iSubItem)
{
INT iSubItems = m_arraySubItems.GetSize();
ASSERT(iSubItem <= iSubItems);
try
{
INT iWidth = m_arraySubItems[iSubItem].iWidth;
if (m_bIndentColumn && GetColumnFromSubItem(iSubItem) == 0)
iWidth -= m_iIndentColumn;
return iWidth;
}
catch(CMemoryException * e)
{
e->Delete();
return -1;
}
}
BOOL CReportCtrl::SetSubItemWidth(INT iSubItem, INT iWidth)
{
BOOL bResult;
UnindentFirstColumn();
bResult = SetSubItemWidthImpl(iSubItem, iWidth);
IndentFirstColumn();
return bResult;
}
INT CReportCtrl::GetItemIndex(HTREEITEM hItem)
{
ASSERT(m_dwStyle & RVS_TREEVIEW);
return ((LPTREEITEM) hItem)->iItem;
}
HTREEITEM CReportCtrl::GetItemHandle(INT iItem)
{
ASSERT(m_dwStyle & RVS_TREEVIEW);
return iItem >= RVI_FIRST ? (HTREEITEM) m_arrayItems[iItem].lptiItem : NULL;
}
void CReportCtrl::SetTreeSubItem(HTREEITEM hItem, INT iSubItem)
{
((LPTREEITEM) hItem)->iSubItem = iSubItem;
}
HTREEITEM CReportCtrl::GetNextItem(HTREEITEM hItem, Direction nCode)
{
LPTREEITEM lpti = (LPTREEITEM) hItem;
if (hItem == RVTI_ROOT)
lpti = &m_tiRoot;
switch (nCode)
{
case RVGN_ROOT:
while (lpti->lptiParent != &m_tiRoot)
lpti = lpti->lptiParent;
break;
case RVGN_NEXT:
lpti = lpti->lptiSibling;
break;
case RVGN_PREVIOUS:
if (lpti->lptiParent->lptiChildren != lpti)
{
LPTREEITEM lptiSibling = lpti->lptiParent->lptiChildren;
ASSERT(lptiSibling != NULL);
while (lptiSibling->lptiSibling != lpti)
{
lptiSibling = lptiSibling->lptiSibling;
ASSERT(lptiSibling != NULL);
}
lpti = lptiSibling;
}
else
lpti = NULL;
break;
case RVGN_PARENT:
lpti = lpti->lptiParent != &m_tiRoot ? lpti->lptiParent : NULL;
break;
case RVGN_CHILD:
lpti = lpti->lptiChildren;
break;
case RVGN_FOCUSED:
{
INT iFocusItem = GetItemForRow(m_iFocusRow);
if (iFocusItem >= RVI_FIRST)
lpti = m_arrayItems[iFocusItem].lptiItem;
else
lpti = NULL;
}
break;
default:
lpti = NULL;
break;
}
return (HTREEITEM) lpti;
}
BOOL CReportCtrl::GetItem(LPRVITEM lprvi)
{
ASSERT(lprvi->iItem > RVI_INVALID);
ASSERT(lprvi->iItem < GetItemCount()); // Specify item
ASSERT(lprvi->iSubItem < m_arraySubItems.GetSize()); // Specify subitem
if
(
(lprvi->iItem <= RVI_INVALID)
|| (lprvi->iItem >= GetItemCount())
|| (lprvi->iSubItem >= m_arraySubItems.GetSize())
) return FALSE;
UINT nMask = lprvi->nMask;
INT iTextMax = lprvi->iTextMax;
ITEM &item = GetItemStruct(lprvi->iItem, lprvi->iSubItem, nMask);
lprvi->nMask = 0;
lprvi->iBkColor = item.iBkColor;
if (lprvi->iBkColor >= 0)
lprvi->nMask |= RVIM_BKCOLOR;
lprvi->nPreview = item.nPreview;
if (lprvi->nPreview > 0)
lprvi->nMask |= RVIM_PREVIEW;
lprvi->iIndent = item.iIndent;
if (lprvi->iIndent >= 0)
lprvi->nMask |= RVIM_INDENT;
lprvi->lpszText = nMask & RVIM_TEXT ? lprvi->lpszText : NULL;
item.rdData.GetSubItem(lprvi->iSubItem, &lprvi->iImage, &lprvi->iOverlay, &lprvi->iCheck, &lprvi->iTextColor,
lprvi->lpszText, &iTextMax);
if (lprvi->lpszText && iTextMax >= 0)
lprvi->nMask |= RVIM_TEXT;
if (lprvi->iImage >= 0)
lprvi->nMask |= RVIM_IMAGE;
if (lprvi->iOverlay >= 0)
lprvi->nMask |= RVIM_OVERLAY;
if (lprvi->iCheck >= 0)
lprvi->nMask |= RVIM_CHECK;
if (lprvi->iTextColor >= 0)
lprvi->nMask |= RVIM_TEXTCOLOR;
lprvi->nMask |= RVIM_STATE | RVIM_LPARAM;
lprvi->nState = item.nState;
lprvi->lParam = item.lParam;
return TRUE;
}
BOOL CReportCtrl::SetItem(LPRVITEM lprvi)
{
ASSERT(!(lprvi->iItem != RVI_EDIT && m_dwStyle & RVS_OWNERDATA)); // Not supported when using this style
if (m_dwStyle & RVS_OWNERDATA)
return FALSE;
m_wndTip.Hide();
TCHAR szText[REPORTCTRL_MAX_TEXT];
RVITEM rvi;
rvi.iItem = lprvi->iItem;
rvi.iSubItem = lprvi->iSubItem;
rvi.lpszText = szText;
rvi.iTextMax = REPORTCTRL_MAX_TEXT;
rvi.nMask = RVIM_TEXT | RVIM_STATE;
VERIFY(GetItem(&rvi));
if (lprvi->nMask & RVIM_TEXT)
{
if (lprvi->lpszText != NULL)
{
_tcsncpy(rvi.lpszText, lprvi->lpszText, REPORTCTRL_MAX_TEXT - 1);
rvi.lpszText[REPORTCTRL_MAX_TEXT - 1] = 0;
}
else
rvi.lpszText = NULL;
}
else if (!(rvi.nMask & RVIM_TEXT))
rvi.lpszText = NULL;
if (lprvi->nMask & RVIM_TEXTCOLOR)
rvi.iTextColor = lprvi->iTextColor;
if (lprvi->nMask & RVIM_IMAGE)
{
ASSERT(m_pImageList != NULL && lprvi->iImage < m_pImageList->GetImageCount());
rvi.iImage = lprvi->iImage;
}
if (lprvi->nMask & RVIM_OVERLAY)
{
ASSERT(m_pImageList != NULL && lprvi->iOverlay < m_pImageList->GetImageCount());
rvi.iOverlay = lprvi->iOverlay;
}
if (lprvi->nMask & RVIM_CHECK)
rvi.iCheck = lprvi->iCheck;
if (lprvi->nMask & RVIM_BKCOLOR)
rvi.iBkColor = lprvi->iBkColor;
ASSERT(!(lprvi->iItem == RVI_EDIT && lprvi->nMask & RVIM_PREVIEW)); // Preview not supported on edit row
if (lprvi->nMask & RVIM_PREVIEW)
rvi.nPreview = lprvi->nPreview;
if (lprvi->nMask & RVIM_INDENT)
rvi.iIndent = lprvi->iIndent;
// Note: focus, selection and tree-GUI elements cannot be changed through this
// function
if (lprvi->nMask & RVIM_STATE)
{
rvi.nState &= RVIS_FOCUSED | RVIS_SELECTED | RVIS_TREEMASK;
rvi.nState |= lprvi->nState &~(RVIS_FOCUSED | RVIS_SELECTED | RVIS_TREEMASK);
}
if (lprvi->nMask & RVIM_LPARAM)
rvi.lParam = lprvi->lParam;
ITEM &item = GetItemStruct(rvi.iItem, rvi.iSubItem);
VERIFY(item.rdData.InsertSubItem(rvi.iSubItem, rvi.iImage, rvi.iOverlay, rvi.iCheck, rvi.iTextColor, rvi.lpszText));
VERIFY(item.rdData.DeleteSubItem(rvi.iSubItem + 1));
item.iBkColor = rvi.iBkColor;
item.nPreview = rvi.nPreview;
item.iIndent = rvi.iIndent;
item.nState = rvi.nState;
item.lParam = rvi.lParam;
SetItemStruct(rvi.iItem, item);
ScrollWindow(SB_VERT, GetScrollPos32(SB_VERT));
if (m_bRedrawFlag == TRUE)
RedrawItems(rvi.iItem);
return TRUE;
}
INT CReportCtrl::GetItemText(INT iItem, INT iSubItem, LPTSTR lpszText, INT iLen)
{
RVITEM rvi;
rvi.nMask = RVIM_TEXT;
rvi.iItem = iItem;
rvi.iSubItem = iSubItem;
rvi.lpszText = lpszText;
rvi.iTextMax = iLen;
return GetItem(&rvi) ? _tcslen(rvi.lpszText) : 0;
}
CString CReportCtrl::GetItemText(INT iItem, INT iSubItem)
{
CString str;
TCHAR szText[REPORTCTRL_MAX_TEXT];
if (GetItemText(iItem, iSubItem, szText, REPORTCTRL_MAX_TEXT))
str = szText;
return str;
}
BOOL CReportCtrl::SetItemText(INT iItem, INT iSubItem, LPCTSTR lpszText)
{
RVITEM rvi;
rvi.nMask = RVIM_TEXT;
rvi.iItem = iItem;
rvi.iSubItem = iSubItem;
rvi.lpszText = (LPTSTR) lpszText;
return SetItem(&rvi);
}
INT CReportCtrl::GetItemImage(INT iItem, INT iSubItem)
{
RVITEM rvi;
rvi.nMask = RVIM_IMAGE;
rvi.iItem = iItem;
rvi.iSubItem = iSubItem;
return GetItem(&rvi) ? rvi.iImage : -1;
}
BOOL CReportCtrl::SetItemImage(INT iItem, INT iSubItem, INT iImage)
{
RVITEM rvi;
rvi.nMask = RVIM_IMAGE;
rvi.iItem = iItem;
rvi.iSubItem = iSubItem;
rvi.iImage = iImage;
return SetItem(&rvi);
}
INT CReportCtrl::GetItemCheck(INT iItem, INT iSubItem)
{
RVITEM rvi;
rvi.nMask = RVIM_CHECK;
rvi.iItem = iItem;
rvi.iSubItem = iSubItem;
return GetItem(&rvi) ? rvi.iCheck : -1;
}
BOOL CReportCtrl::SetItemCheck(INT iItem, INT iSubItem, INT iCheck)
{
RVITEM rvi;
rvi.nMask = RVIM_CHECK;
rvi.iItem = iItem;
rvi.iSubItem = iSubItem;
rvi.iCheck = iCheck;
return SetItem(&rvi);
}
DWORD CReportCtrl::GetItemData(INT iItem)
{
RVITEM rvi;
rvi.nMask = RVIM_LPARAM;
rvi.iItem = iItem;
return GetItem(&rvi) ? rvi.lParam : 0;
}
BOOL CReportCtrl::SetItemData(INT iItem, DWORD dwData)
{
RVITEM rvi;
rvi.nMask = RVIM_LPARAM;
rvi.iItem = iItem;
rvi.lParam = dwData;
return SetItem(&rvi);
}
BOOL CReportCtrl::GetItemRect(INT iItem, INT iSubItem, LPRECT lpRect, UINT nCode)
{
INT iRow = GetRowFromItem(iItem);
INT iColumn = GetColumnFromSubItem(iSubItem);
if (nCode == RVIR_TEXTNOCOLUMNS)
iColumn = 0;
if (!GetRowRect(iRow, iColumn, lpRect, nCode))
return FALSE;
RVITEM rvi;
rvi.iItem = iItem;
rvi.iSubItem = iSubItem;
GetItem(&rvi);
if (nCode != RVIR_BOUNDS && iColumn == 0 && rvi.nMask & RVIM_INDENT && rvi.iIndent >= 0)
{
lpRect->left += rvi.iIndent * m_iDefaultHeight;
if (lpRect->left > lpRect->right)
return FALSE;
}
switch (nCode)
{
case RVIR_BOUNDS:
return TRUE;
case RVIR_IMAGE:
if (!(rvi.nMask & RVIM_IMAGE))
return FALSE;
lpRect->left += m_iSpacing;
if (lpRect->left + m_sizeImage.cx > lpRect->right - m_iSpacing)
return FALSE;
lpRect->right = lpRect->left + m_sizeImage.cx;
return TRUE;
case RVIR_CHECK:
if (!(rvi.nMask & RVIM_CHECK))
return FALSE;
lpRect->left += m_iSpacing + (rvi.nMask & RVIM_IMAGE ? m_sizeImage.cx + m_iSpacing : 0);
if (lpRect->left + m_sizeCheck.cx > lpRect->right - m_iSpacing)
return FALSE;
lpRect->right = lpRect->left + m_sizeCheck.cx;
return TRUE;
case RVIR_TEXT:
case RVIR_TEXTNOCOLUMNS:
lpRect->left += m_iSpacing + (rvi.nMask & RVIM_IMAGE ? m_sizeImage.cx + m_iSpacing : 0) + (rvi.nMask & RVIM_CHECK ? m_sizeCheck.cx + m_iSpacing : 0);
if (lpRect->left > lpRect->right - m_iSpacing)
return FALSE;
lpRect->right -= m_iSpacing;
return TRUE;
default:
return FALSE;
}
}
BOOL CReportCtrl::MeasureItem(INT iItem, INT iSubItem, LPRECT lpRect, BOOL bTextOnly)
{
lpRect->top = 0;
lpRect->bottom = m_iDefaultHeight;
lpRect->left = 0;
lpRect->right = m_iDefaultWidth;
if (iSubItem != -1)
{
TCHAR szText[REPORTCTRL_MAX_TEXT];
RVITEM rvi;
rvi.nMask = RVIM_TEXT;
rvi.iItem = iItem;
rvi.iSubItem = iSubItem;
rvi.lpszText = szText;
rvi.iTextMax = REPORTCTRL_MAX_TEXT;
GetItem(&rvi);
lpRect->right = m_iSpacing;
if (bTextOnly == FALSE)
{
lpRect->right += (rvi.nMask & RVIM_IMAGE ? m_sizeImage.cx + m_iSpacing : 0) + (rvi.nMask & RVIM_CHECK ? m_sizeCheck.cx + m_iSpacing : 0);
}
if (rvi.nMask & RVIM_TEXT)
{
CClientDC dc(this);
CFont *pFontDC = dc.SelectObject(rvi.nState & RVIS_BOLD ? &m_fontBold : &m_font);
lpRect->right += dc.GetTextExtent(rvi.lpszText, _tcslen(rvi.lpszText)).cx + m_iSpacing;
dc.SelectObject(pFontDC);
}
}
return TRUE;
}
INT CReportCtrl::GetItemHeight()
{
return m_iDefaultHeight;
}
void CReportCtrl::SetItemHeight(INT iHeight)
{
m_iDefaultHeight = iHeight;
Layout();
}
CString CReportCtrl::GetHeadingForSortedCategory()
{
BOOL bSortAccending;
INT iSubItem = GetCategorySubItem(&bSortAccending);
HDITEMEX hditemex;
GetSubItemEx(iSubItem, &hditemex);
return (hditemex.nStyle & HDF_EX_HASDATE) ? (bSortAccending ? _T("Earliest on Top") : _T("Latest on Top")) : (bSortAccending ? _T("Lowest on Top") : _T("Highest on Top"));
}
void CReportCtrl::GetCategoryMenuItem(LPCATITEM lpCatItem)
{
INT iSubItem = lpCatItem->iSubItem;
if (iSubItem >= 0)
{
if (iSubItem == m_iCategorySubItem || GetColumnFromSubItem(iSubItem) >= 0)
{
HDITEM hdi;
lpCatItem->mask = iSubItem == 0 && m_dwStyle & RVS_TREEVIEW ? 0 : CATEGORY_ENABLED;
GetSubItem(iSubItem, &hdi);
lpCatItem->strText = GetSubItemText(iSubItem);;
if (lpCatItem->strText.GetLength() == 0)
{
HDITEMEX hditemex;
GetSubItemEx(iSubItem, &hditemex);
lpCatItem->strText += hditemex.strToolTip;
}
if (hdi.mask & HDI_IMAGE)
{
lpCatItem->iImage = hdi.iImage;
lpCatItem->mask |= CATEGORY_IMAGE;
}
}
}
else
{
lpCatItem->mask = CATEGORY_ENABLED;
lpCatItem->strText = _T("None");
}
}
CString CReportCtrl::GetItemString(INT iItem, TCHAR cSeparator)
{
CString str;
TCHAR szText[REPORTCTRL_MAX_TEXT + 1];
INT iColumn, iColumns = m_arrayColumns.GetSize();
for (iColumn = 0; iColumn < iColumns; iColumn++)
{
HDITEM hdi;
hdi.mask = HDI_WIDTH | HDI_LPARAM;
m_wndHeader.GetItem(m_arrayColumns[iColumn], &hdi);
RVITEM rvi;
rvi.iItem = iItem;
rvi.iSubItem = hdi.lParam;
rvi.nMask = RVIM_TEXT;
rvi.lpszText = szText;
rvi.iTextMax = REPORTCTRL_MAX_TEXT;
VERIFY(GetItem(&rvi));
if (rvi.nMask & RVIM_TEXT)
{
LPTSTR lpsz = rvi.lpszText;
do
{
lpsz = _tcschr(lpsz, cSeparator);
if (lpsz != NULL)
*lpsz = _T(' ');
} while (lpsz != NULL);
}
else if (rvi.nMask & RVIM_IMAGE)
{
_stprintf(szText, _T("%d"), rvi.iImage);
}
else if (rvi.nMask & RVIM_CHECK)
{
_stprintf(szText, _T("%d"), rvi.iCheck);
}
else
szText[0] = 0;
if (m_dwStyle & RVS_TREEVIEW && iColumn == 0 && rvi.nMask & RVIM_INDENT)
{
for (INT i = 1; i < rvi.iIndent; i++)
str += g_strCBIndent;
HTREEITEM hItem = GetItemHandle(iItem);
ASSERT(hItem != NULL);
if (HasChildren(hItem))
str += GetNextItem(hItem, RVGN_CHILD) != NULL ? _T('-') : _T('+');
else
str += _T(' ');
}
str += rvi.lpszText;
str += iColumn < iColumns - 1 ? cSeparator : _T('\n');
}
return str;
}
INT CReportCtrl::GetVisibleCount(BOOL bUnobstructed)
{
return GetVisibleRows(bUnobstructed);
}
INT CReportCtrl::GetTopIndex()
{
return GetScrollPos32(SB_VERT);
}
CCategoryHdr *CReportCtrl::GetCategoryHdrCtrl()
{
if (m_wndCategoryHdr && m_wndCategoryHdr->GetSafeHwnd() != NULL)
return m_wndCategoryHdr;
else
return NULL;
}
void CReportCtrl::SetAutosizeHeaders(BOOL bAutoSize)
{
GetHeaderCtrl()->ModifyProperty(FH_PROPERTY_ENABLEAUTOSIZE, bAutoSize);
if (m_wndCategoryHdr)
{
m_wndCategoryHdr->ModifyProperty(FH_PROPERTY_ENABLEAUTOSIZE, bAutoSize);
m_wndCategoryHdr->NewCategory();
}
Invalidate();
}
INT CReportCtrl::GetCategoryHdrHeight()
{
return (m_wndCategoryHdr != NULL && m_wndCategoryHdr->m_iState == SW_SHOW) ? m_wndCategoryHdr->GetHeight() : 0;
}
BOOL CReportCtrl::IsItemVisible(INT iItem, BOOL bUnobstructed)
{
INT iFirst, iLast, iRow;
iRow = GetRowFromItem(iItem);
iFirst = GetScrollPos32(SB_VERT);
GetVisibleRows(bUnobstructed, &iFirst, &iLast);
return (iRow >= iFirst && iRow <= iLast) ? TRUE : FALSE;
}
void CReportCtrl::PageUp()
{
INT iPos = GetTopIndex() - GetVisibleCount();
ScrollWindow(SB_VERT, iPos < 0 ? 0 : iPos);
}
void CReportCtrl::PageDown()
{
INT iPos = GetTopIndex() + GetVisibleCount();
ScrollWindow(SB_VERT, iPos);
}
INT CReportCtrl::GetItemCount()
{
return m_dwStyle & RVS_OWNERDATA ? m_iVirtualHeight : m_arrayItems.GetSize();
}
INT CReportCtrl::GetItemCount(HTREEITEM hParent, BOOL bRecurse)
{
INT iCount = 0;
LPTREEITEM lptiSibling = hParent == RVTI_ROOT ? m_tiRoot.lptiChildren : ((LPTREEITEM) hParent)->lptiChildren;
while (lptiSibling != NULL)
{
if (bRecurse == TRUE && lptiSibling->lptiChildren != NULL)
iCount += GetItemCount((HTREEITEM) lptiSibling, bRecurse);
lptiSibling = lptiSibling->lptiSibling;
iCount++;
}
return iCount;
}
void CReportCtrl::SetItemCount(INT iCount)
{
ASSERT(m_dwStyle & RVS_OWNERDATA); // Only supported when using RVS_OWNERDATA style
ASSERT(iCount >= 0);
INT i;
m_iVirtualHeight = iCount;
m_iFocusRow = m_iFocusRow >= m_iVirtualHeight ? m_iVirtualHeight - 1 : m_iFocusRow;
CList<INT, INT> list;
while (!m_listSelection.IsEmpty())
{
i = m_listSelection.GetHead();
if (i < iCount)
list.AddTail(i);
m_listSelection.RemoveHead();
}
m_listSelection.AddTail(&list);
for (i = 0; i < REPORTCTRL_MAX_CACHE; i++)
{
if (m_aciCache[i].iItem >= iCount)
m_aciCache[i].iItem = RVI_INVALID;
}
RedrawRows(RVI_EDIT, RVI_LAST, TRUE);
INT iPos = GetScrollPos32(SB_VERT);
ScrollWindow(SB_VERT, iPos > iCount ? iCount : iPos);
}
INT CReportCtrl::GetFirstSelectedItem()
{
return GetNextSelectedItem(RVI_INVALID);
}
INT CReportCtrl::GetNextSelectedItem(INT iItem)
{
RVITEM rvi;
rvi.nMask = RVIM_STATE;
INT iItems = GetItemCount();
for (rvi.iItem = iItem + 1; rvi.iItem < iItems; rvi.iItem++)
if (GetItem(&rvi) && rvi.nState & RVIS_SELECTED)
return rvi.iItem;
return RVI_INVALID;
}
INT CReportCtrl::GetSelectedItems(LPINT lpiItems, INT iMax)
{
INT iItem = GetFirstSelectedItem(), iItems = 0;
while (iItem != RVI_INVALID)
{
if (lpiItems != NULL && iItems < iMax)
lpiItems[iItems] = iItem;
iItem = GetNextSelectedItem(iItem);
iItems++;
}
return iItems;
}
INT CReportCtrl::GetSelectedItemCount()
{
INT iItem = GetFirstSelectedItem(), iItems = 0;
while (iItem != RVI_INVALID)
{
iItem = GetNextSelectedItem(iItem);
iItems++;
}
return iItems;
}
void CReportCtrl::ClearSelection()
{
POSITION pos = m_listSelection.GetHeadPosition();
while (pos != NULL)
{
INT iItem = m_listSelection.GetAt(pos);
INT iRow = GetRowFromItem(iItem);
SetState(iRow, GetState(iRow) &~RVIS_SELECTED, RVIS_SELECTED);
RedrawRows(iRow);
m_listSelection.GetNext(pos);
}
m_listSelection.RemoveAll();
}
void CReportCtrl::InvertSelection()
{
INT iRows = m_arrayRows.GetSize();
INT iFocusRow = m_iFocusRow;
SelectRows(RVI_FIRST, iRows - 1, TRUE, TRUE, TRUE, FALSE);
SelectRows(iFocusRow, iFocusRow, FALSE, FALSE, FALSE, FALSE);
}
void CReportCtrl::SelectAll()
{
if (m_arrayRows.GetSize())
{
ClearSelection();
InvertSelection();
}
}
void CReportCtrl::SetSelection(INT iItem, BOOL bKeepSelection, BOOL bNotify)
{
ASSERT(iItem > RVI_INVALID && iItem < GetItemCount());
INT iRow = GetRowFromItem(iItem);
SelectRows(iRow, iRow, TRUE, bKeepSelection, FALSE, bNotify);
}
void CReportCtrl::SetSelection(LPINT lpiItems, INT iCount, BOOL bKeepSelection, BOOL bNotify)
{
INT i, iRow;
for (i = 0; i < iCount; i++)
{
ASSERT(lpiItems[i] > RVI_INVALID && lpiItems[i] < GetItemCount());
iRow = GetRowFromItem(lpiItems[i]);
SelectRows(iRow, iRow, TRUE, bKeepSelection | (i != 0), FALSE, bNotify);
}
}
void CReportCtrl::SetSelection(INT iSubItem, CString &strPattern, BOOL bKeepSelection, BOOL bNotify)
{
INT iRows = m_arrayRows.GetSize();
if (bKeepSelection == FALSE)
ClearSelection();
for (INT iRow = 0; iRow < iRows; iRow++)
{
INT iItem = GetItemForRow(iRow);
if (MatchString(GetItemText(iItem, iSubItem), strPattern))
SelectRows(iRow, iRow, TRUE, TRUE, FALSE, bNotify);
}
}
void CReportCtrl::SetSelectionLessEq(INT iSubItem, INT iMatch, BOOL bKeepSelection, BOOL bNotify)
{
RVITEM rvi;
INT iRows = m_arrayRows.GetSize();
if (bKeepSelection == FALSE)
ClearSelection();
rvi.iSubItem = iSubItem;
rvi.nMask = RVIM_LPARAM;
for (INT iRow = 0; iRow < iRows; iRow++)
{
rvi.iItem = GetItemForRow(iRow);
if (GetItem(&rvi))
{
if (rvi.lParam <= iMatch)
SelectRows(iRow, iRow, TRUE, TRUE, FALSE, bNotify);
}
}
}
void CReportCtrl::SetSelection(HTREEITEM hItem, BOOL bKeepSelection, BOOL bNotify)
{
SetSelection(((LPTREEITEM) hItem)->iItem, bKeepSelection, bNotify);
}
void CReportCtrl::SetSelection(HTREEITEM *lphItems, INT iCount, BOOL bKeepSelection, BOOL bNotify)
{
INT i, iItem, iRow;
for (i = 0; i < iCount; i++)
{
iItem = ((LPTREEITEM) lphItems[i])->iItem;
ASSERT(iItem > RVI_INVALID && iItem < GetItemCount());
iRow = GetRowFromItem(iItem);
SelectRows(iRow, iRow, TRUE, bKeepSelection | (i != 0), FALSE, bNotify);
}
}
INT CReportCtrl::GetItemRow(INT iItem)
{
ASSERT(!(m_dwStyle & RVS_OWNERDATA));
return GetRowFromItem(iItem);
}
CArray<INT, INT> *CReportCtrl::GetItemRowArray()
{
ASSERT(!(m_dwStyle & RVS_OWNERDATA));
return &m_arrayRows;
}
BOOL CReportCtrl::SetItemRowArray(CArray<INT, INT> &arrayRows)
{
if (m_dwStyle & RVS_OWNERDATA)
return FALSE;
ASSERT(m_arrayRows.GetSize() == arrayRows.GetSize()); // Must match
if (m_arrayRows.GetSize() != arrayRows.GetSize())
return FALSE;
INT iFocusItem = GetItemForRow(m_iFocusRow);
m_arrayRows.Copy(arrayRows);
m_bUpdateItemMap = TRUE;
m_iFocusRow = GetRowFromItem(iFocusItem);
Invalidate();
return TRUE;
}
BOOL CReportCtrl::MoveUp(INT iRow)
{
if (m_dwStyle & RVS_OWNERDATA)
return FALSE;
if (iRow <= RVI_FIRST)
return FALSE;
ASSERT(iRow < m_arrayRows.GetSize());
INT iTemp = m_arrayRows[iRow - 1];
m_arrayRows[iRow - 1] = m_arrayRows[iRow];
m_arrayRows[iRow] = iTemp;
if (m_iFocusRow == iRow)
m_iFocusRow--;
Invalidate();
m_bUpdateItemMap = TRUE;
return TRUE;
}
BOOL CReportCtrl::MoveDown(INT iRow)
{
if (m_dwStyle & RVS_OWNERDATA)
return FALSE;
if (iRow >= m_arrayRows.GetSize() - 1)
return FALSE;
ASSERT(iRow >= RVI_FIRST);
INT iTemp = m_arrayRows[iRow + 1];
m_arrayRows[iRow + 1] = m_arrayRows[iRow];
m_arrayRows[iRow] = iTemp;
if (m_iFocusRow == iRow)
m_iFocusRow++;
Invalidate();
m_bUpdateItemMap = TRUE;
return TRUE;
}
BOOL CReportCtrl::SetImageList(CImageList *pImageList, CImageList *pCheckList)
{
BOOL bResult = TRUE;
IMAGEINFO info;
m_pImageList = pImageList;
m_wndHeader.SetImageList(pImageList);
if (pImageList != NULL)
{
if (pImageList->GetImageInfo(0, &info))
{
m_sizeImage.cx = info.rcImage.right - info.rcImage.left;
m_sizeImage.cy = info.rcImage.bottom - info.rcImage.top;
m_iDefaultHeight = m_sizeImage.cy + 1 > m_iDefaultHeight ? m_sizeImage.cy + 1 : m_iDefaultHeight;
}
else
bResult = FALSE;
}
m_pCheckList = pCheckList;
if (pCheckList != NULL)
{
if (pCheckList->GetImageInfo(0, &info))
{
m_sizeCheck.cx = info.rcImage.right - info.rcImage.left;
m_sizeCheck.cy = info.rcImage.bottom - info.rcImage.top;
m_iDefaultHeight = m_sizeCheck.cy + 1 > m_iDefaultHeight ? m_sizeCheck.cy + 1 : m_iDefaultHeight;
}
else
bResult = FALSE;
}
Invalidate();
return bResult;
}
CImageList *CReportCtrl::GetImageList(void)
{
return m_pImageList;
}
BOOL CReportCtrl::SetBkImage(UINT nIDResource)
{
return SetBkImage((LPCTSTR) nIDResource);
}
BOOL CReportCtrl::SetBkImage(LPCTSTR lpszResourceName)
{
if (m_bitmap.m_hObject != NULL)
m_bitmap.DeleteObject();
HBITMAP hBitmap = (HBITMAP)::LoadImage(AfxGetInstanceHandle(), lpszResourceName, IMAGE_BITMAP, 0, 0,
LR_CREATEDIBSECTION);
Invalidate();
if (hBitmap == NULL)
return FALSE;
m_bitmap.Attach(hBitmap);
BITMAP bitmap;
m_bitmap.GetBitmap(&bitmap);
m_sizeBitmap.cx = bitmap.bmWidth;
m_sizeBitmap.cy = bitmap.bmHeight;
BOOL bResult = CreatePalette();
Invalidate();
return bResult;
}
COLORREF CReportCtrl::GetColor(INT iIndex)
{
return m_arrayColors[iIndex];
}
BOOL CReportCtrl::SetColor(INT iIndex, COLORREF crColor)
{
if (iIndex < 0 || iIndex >= m_arrayColors.GetSize())
return FALSE;
m_arrayColors.SetAt(iIndex, crColor);
BOOL bResult = CreatePalette();
Invalidate();
return bResult;
}
BOOL CReportCtrl::HasFocus()
{
return m_bFocus;
}
INT CReportCtrl::GetCurrentFocus(LPINT lpiColumn)
{
if (lpiColumn != NULL)
*lpiColumn = m_iFocusColumn;
return m_iFocusRow;
}
BOOL CReportCtrl::AdjustFocus(INT iRow, INT iColumn)
{
INT iSubItem = GetSubItemFromColumn(iColumn);
if (iSubItem >= 0 && m_arraySubItems[iSubItem].nFormat & RVCF_SUBITEM_NOFOCUS)
return FALSE;
if (iRow < RVI_INVALID || iRow > m_iVirtualHeight - 1 || iColumn < -1 || iColumn > m_arrayColumns.GetSize() - 1)
return FALSE;
if (m_iFocusRow > RVI_INVALID && iRow != m_iFocusRow)
{
SetState(m_iFocusRow, 0, RVIS_FOCUSED);
RedrawRows(m_iFocusRow);
}
m_iFocusColumn = iColumn;
m_iFocusRow = iRow;
if (iRow > RVI_INVALID)
{
SetState(iRow, RVIS_FOCUSED, RVIS_FOCUSED);
RedrawRows(m_iFocusRow);
}
return TRUE;
}
BOOL CReportCtrl::HasChildren(HTREEITEM hItem)
{
ASSERT(m_dwStyle & RVS_TREEVIEW);
return ((LPTREEITEM) hItem)->lptiChildren != NULL ? TRUE : FALSE;
}
BOOL CReportCtrl::IsChild(HTREEITEM hParent, HTREEITEM hItem)
{
ASSERT(m_dwStyle & RVS_TREEVIEW);
return ((LPTREEITEM) hItem)->lptiParent == (LPTREEITEM) hParent ? TRUE : FALSE;
}
BOOL CReportCtrl::IsDescendent(HTREEITEM hParent, HTREEITEM hItem)
{
ASSERT(m_dwStyle & RVS_TREEVIEW);
LPTREEITEM lptiAncestor = ((LPTREEITEM) hItem)->lptiParent;
while (lptiAncestor != NULL)
{
if (lptiAncestor == (LPTREEITEM) hParent)
return TRUE;
lptiAncestor = lptiAncestor->lptiParent;
}
return FALSE;
}
CReportHeaderCtrl *CReportCtrl::GetHeaderCtrl()
{
return m_wndHeader.GetSafeHwnd() != NULL ? &m_wndHeader : NULL;
}
BOOL CReportCtrl::SetReportSubItemListCtrl(CReportSubItemListCtrl *lprsilc)
{
if (m_lprsilc != NULL)
{
m_wndHeader.ModifyProperty(FH_PROPERTY_DROPTARGET, NULL);
m_lprsilc->SetReportCtrl(NULL);
}
m_lprsilc = lprsilc;
if (m_lprsilc != NULL)
{
m_wndHeader.ModifyProperty(FH_PROPERTY_DROPTARGET, (LPARAM) m_lprsilc->m_hWnd);
m_lprsilc->SetReportCtrl(this);
}
return TRUE;
}
CReportSubItemListCtrl *CReportCtrl::GetReportSubItemListCtrl()
{
return m_lprsilc;
}
BOOL CReportCtrl::SetSortCallback(LPFNRVCOMPARE lpfnrvc, LPARAM lParam)
{
m_lpfnrvc = lpfnrvc;
m_lParamCompare = lParam;
return TRUE;
}
LPFNRVCOMPARE CReportCtrl::GetSortCallback()
{
return m_lpfnrvc;
}
BOOL CReportCtrl::GetSortSettings(INT *lpiSubItem, BOOL *lpbAscending)
{
INT iColumn = m_wndHeader.GetSortColumn(lpbAscending);
if (iColumn >= 0)
{
*lpiSubItem = GetSubItemFromColumn(iColumn);
return TRUE;
}
*lpiSubItem = -1;
return FALSE;
}
BOOL CReportCtrl::WriteProfile(LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszData)
{
CString str, strProfile;
if (lpszData)
strProfile = lpszData;
else
{
UnindentFirstColumn();
if (m_bColumnsReordered)
ReorderColumns();
INT i;
INT iSubItems = m_arraySubItems.GetSize();
INT iColumns = m_arrayColumns.GetSize();
strProfile.Format(_T("(%d,%d)"), iSubItems, iColumns);
for (i = 0; i < iSubItems; i++)
{
str.Format(_T(" %d"), m_arraySubItems[i].iWidth);
strProfile += str;
}
for (i = 0; i < iColumns; i++)
{
HDITEM hdi;
hdi.mask = HDI_LPARAM;
m_wndHeader.GetItem(m_arrayColumns[i], &hdi);
str.Format(_T(" %d"), hdi.lParam);
strProfile += str;
}
IndentFirstColumn();
}
CWinApp *pApp = AfxGetApp();
return pApp->WriteProfileString(lpszSection, lpszEntry, strProfile);
}
BOOL CReportCtrl::GetProfile(LPCTSTR lpszSection, LPCTSTR lpszEntry, LPTSTR lpszDefault)
{
CString str, strProfile;
CWinApp *pApp = AfxGetApp();
strProfile = pApp->GetProfileString(lpszSection, lpszEntry);
if (strProfile.IsEmpty())
return FALSE;
if (lpszDefault)
{
ASSERT(int(_tcslen(lpszDefault)) == strProfile.GetLength());
if (int(_tcslen(lpszDefault)) != strProfile.GetLength())
return FALSE;
_tcscpy(lpszDefault, strProfile);
}
else
{
UnindentFirstColumn();
LPTSTR lpsz = strProfile.GetBuffer(0);
INT i;
INT iSubItems;
INT iColumns;
iSubItems = _tcstol(++lpsz, &lpsz, 10);
iColumns = _tcstol(++lpsz, &lpsz, 10);
if (iSubItems != m_arraySubItems.GetSize())
return FALSE;
if (iColumns == 0)
return FALSE;
for (i = 0; i < iSubItems; i++)
m_arraySubItems[i].iWidth = _tcstol(++lpsz, &lpsz, 10);
for (i = 0; i < iColumns; i++)
ActivateSubItem(_tcstol(++lpsz, &lpsz, 10), i);
ReorderColumns();
}
return TRUE;
}
// CReportCtrl operations
BOOL CReportCtrl::ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags)
{
DWORD dwStyle = CWnd::GetStyle();
ASSERT(!(dwRemove & (RVS_OWNERDATA | RVS_TREEVIEW))); // Can't dynamically remove this styles
ASSERT(!(dwAdd & (RVS_OWNERDATA | RVS_TREEVIEW))); // Can't dynamically add this styles
if (m_hEditWnd != NULL)
EndEdit();
if (CWnd::ModifyStyle(dwRemove, dwAdd, nFlags))
{
if (!(dwStyle & RVS_SHOWHGRID) && dwAdd & RVS_SHOWHGRID)
m_iDefaultHeight++;
if (dwStyle & RVS_SHOWHGRID && dwRemove & RVS_SHOWHGRID)
m_iDefaultHeight--;
if (dwRemove & RVS_FOCUSSUBITEMS)
m_iFocusColumn = -1;
m_dwStyle = CWnd::GetStyle();
Layout();
return TRUE;
}
return FALSE;
}
INT CReportCtrl::DefineSubItem(INT iSubItem, LPRVSUBITEM lprvs, BOOL bUpdateList)
{
INT i;
INT iSubItems = m_arraySubItems.GetSize();
ASSERT(iSubItem <= iSubItems);
ASSERT(lprvs->lpszText != NULL); // Must supply (descriptive) text for subitem selector
ASSERT(_tcslen(lprvs->lpszText) < FLATHEADER_TEXT_MAX);
try
{
SUBITEM subitem;
subitem.nFormat = lprvs->nFormat;
subitem.iWidth = lprvs->iWidth < 0 ? m_iDefaultWidth : lprvs->iWidth;
subitem.iMinWidth = lprvs->iMinWidth;
subitem.iMaxWidth = lprvs->iMaxWidth;
subitem.iImage = lprvs->nFormat & RVCF_IMAGE ? lprvs->iImage : 0;
subitem.strText = lprvs->lpszText;
m_arraySubItems.InsertAt(iSubItem, subitem);
HDITEM hdi;
hdi.mask = HDI_LPARAM;
INT iHeaderItems = m_arrayColumns.GetSize();
for (i = 0; i < iHeaderItems; i++)
{
if (m_wndHeader.GetItem(m_arrayColumns[i], &hdi))
if (hdi.lParam >= iSubItem)
{
hdi.lParam++;
m_wndHeader.SetItem(m_arrayColumns[i], &hdi);
}
}
VERIFY(m_itemEdit.rdData.InsertSubItem(iSubItem, -1, -1, -1, -1, NULL));
if (!(m_dwStyle & RVS_OWNERDATA))
{
INT iItems = m_arrayItems.GetSize();
for (i = 0; i < iItems; i++)
VERIFY(m_arrayItems[i].rdData.InsertSubItem(iSubItem, -1, -1, -1, -1, NULL));
}
if (bUpdateList && m_lprsilc != NULL)
m_lprsilc->UpdateList();
return iSubItem;
}
catch(CMemoryException * e)
{
e->Delete();
return -1;
}
}
BOOL CReportCtrl::RedefineSubItem(INT iSubItem, LPRVSUBITEM lprvs, BOOL bUpdateList)
{
INT iSubItems = m_arraySubItems.GetSize();
ASSERT(iSubItem <= iSubItems);
ASSERT(lprvs->lpszText != NULL); // Must supply (descriptive) text for subitem selector
ASSERT(_tcslen(lprvs->lpszText) < FLATHEADER_TEXT_MAX);
try
{
SUBITEM subitem;
subitem.nFormat = lprvs->nFormat;
subitem.iWidth = lprvs->iWidth < 0 ? m_iDefaultWidth : lprvs->iWidth;
subitem.iMinWidth = lprvs->iMinWidth;
subitem.iMaxWidth = lprvs->iMaxWidth;
subitem.iImage = lprvs->nFormat & RVCF_IMAGE ? lprvs->iImage : 0;
subitem.strText = lprvs->lpszText;
m_arraySubItems.SetAt(iSubItem, subitem);
if (bUpdateList && m_lprsilc != NULL)
m_lprsilc->UpdateList();
return SetSubItemWidth(iSubItem, lprvs->iWidth);
}
catch(CMemoryException * e)
{
e->Delete();
return FALSE;
}
}
BOOL CReportCtrl::UndefineSubItem(INT iSubItem)
{
ASSERT(iSubItem >= 0);
ASSERT(iSubItem < m_arraySubItems.GetSize()); // Specify a valid subitem
VERIFY(!DeactivateSubItem(iSubItem));
m_arraySubItems.RemoveAt(iSubItem);
HDITEM hdi;
hdi.mask = HDI_LPARAM;
INT i;
INT iHeaderItems = m_arrayColumns.GetSize();
for (i = 0; i < iHeaderItems; i++)
{
if (m_wndHeader.GetItem(m_arrayColumns[i], &hdi))
if (hdi.lParam > iSubItem)
{
hdi.lParam--;
m_wndHeader.SetItem(m_arrayColumns[i], &hdi);
}
}
VERIFY(m_itemEdit.rdData.DeleteSubItem(iSubItem));
if (!(m_dwStyle & RVS_OWNERDATA))
{
INT iItems = m_arrayItems.GetSize();
for (i = 0; i < iItems; i++)
VERIFY(m_arrayItems[i].rdData.DeleteSubItem(iSubItem));
}
if (m_lprsilc != NULL)
m_lprsilc->UpdateList();
return TRUE;
}
void CReportCtrl::UndefineAllSubItems()
{
while (m_arraySubItems.GetSize() > 0)
UndefineSubItem(0);
}
INT CReportCtrl::AddItem(LPCTSTR lpszText, INT iImage, INT iCheck, INT iTextColor)
{
INT iItems = m_arrayItems.GetSize();
return InsertItem(iItems, lpszText, iImage, iCheck, iTextColor);
}
INT CReportCtrl::AddItem(LPRVITEM lprvi)
{
INT iItems = m_arrayItems.GetSize();
if (lprvi == NULL)
{
INT iSubItem = 0, iSubItems = m_arraySubItems.GetSize();
RVITEM rvi;
TCHAR szText[REPORTCTRL_MAX_TEXT];
rvi.iItem = RVI_EDIT;
rvi.iSubItem = 0;
rvi.nMask = RVIM_TEXT;
rvi.lpszText = szText;
rvi.iTextMax = REPORTCTRL_MAX_TEXT;
VERIFY(GetItem(&rvi));
rvi.iItem = iItems;
INT iItem = InsertItem(&rvi);
if (iItem != RVI_INVALID)
{
for (iSubItem = 1; iSubItem < iSubItems; iSubItem++)
{
rvi.iItem = RVI_EDIT;
rvi.iSubItem = iSubItem;
rvi.nMask = RVIM_TEXT;
rvi.lpszText = szText;
rvi.iTextMax = REPORTCTRL_MAX_TEXT;
VERIFY(GetItem(&rvi));
rvi.iItem = iItem;
VERIFY(SetItem(&rvi));
}
}
return iItem;
}
else
return InsertItem(lprvi);
}
INT CReportCtrl::InsertItem(INT iItem, LPCTSTR lpszText, INT iImage, INT iCheck, INT iTextColor)
{
RVITEM rvi;
rvi.nMask = RVIM_TEXT;
rvi.iItem = iItem;
rvi.iSubItem = 0;
rvi.lpszText = (LPTSTR) lpszText;
rvi.iTextColor = iTextColor;
rvi.iImage = iImage;
rvi.iCheck = iCheck;
if (iTextColor >= 0)
rvi.nMask |= RVIM_TEXTCOLOR;
if (iImage >= 0)
rvi.nMask |= RVIM_IMAGE;
if (iCheck >= 0)
rvi.nMask |= RVIM_CHECK;
return InsertItem(&rvi);
}
INT CReportCtrl::InsertItem(LPRVITEM lprvi)
{
if (m_dwStyle & RVS_OWNERDATA || m_dwStyle & RVS_TREEVIEW)
return RVI_INVALID; // Use set SetItemCount() when using OWNERDATA style
// Use tree item variants when using TREEVIEW style
INT iItem = InsertItemImpl(lprvi);
if (iItem != RVI_INVALID)
{
m_bUpdateItemMap = TRUE;
m_iVirtualHeight++;
if (m_iFocusRow >= RVI_FIRST && m_iFocusRow >= GetRowFromItem(iItem))
m_iFocusRow++;
}
return iItem;
}
BOOL CReportCtrl::DeleteItem(INT iItem)
{
if (m_dwStyle & RVS_OWNERDATA || m_dwStyle & RVS_TREEVIEW)
return RVI_INVALID; // Use set SetItemCount() when using OWNERDATA style
// Use tree item variants when using TREEVIEW style
if (iItem < RVI_FIRST)
return FALSE;
ASSERT(iItem <= m_arrayItems.GetSize());
INT iRow = GetRowFromItem(iItem);
BOOL bResult = DeleteItemImpl(iItem);
m_bUpdateItemMap = TRUE;
ASSERT(m_iVirtualHeight > 0);
m_iVirtualHeight--;
if (m_iFocusRow >= iRow)
m_iFocusRow--;
m_iFocusRow = m_iFocusRow >= m_iVirtualHeight ? m_iVirtualHeight - 1 : m_iFocusRow;
INT iRows = m_arrayRows.GetSize();
INT iFirst = GetScrollPos32(SB_VERT), iLast;
GetVisibleRows(TRUE, &iFirst, &iLast);
if (iRow <= iFirst || iLast >= iRows - 1)
GetVisibleRows(TRUE, &iFirst, &iLast, TRUE);
ScrollWindow(SB_VERT, iFirst > RVI_INVALID ? iFirst : 0);
return bResult;
}
HTREEITEM CReportCtrl::InsertItem(LPCTSTR lpszText, INT iImage, INT iCheck, INT iTextColor, HTREEITEM hParent,
HTREEITEM hInsertAfter, INT iSubItem)
{
RVITEM rvi;
rvi.nMask = RVIM_TEXT;
rvi.lpszText = (LPTSTR) lpszText;
rvi.iTextColor = iTextColor;
rvi.iImage = iImage;
rvi.iCheck = iCheck;
if (iTextColor >= 0)
rvi.nMask |= RVIM_TEXTCOLOR;
if (iImage >= 0)
rvi.nMask |= RVIM_IMAGE;
if (iCheck >= 0)
rvi.nMask |= RVIM_CHECK;
return InsertItem(&rvi, hParent, hInsertAfter, iSubItem);
}
HTREEITEM CReportCtrl::InsertItem(LPRVITEM lprvi, HTREEITEM hParent, HTREEITEM hInsertAfter, INT iSubItem)
{
if (!(m_dwStyle & RVS_TREEVIEW))
return NULL; // TREEVIEW style must be set to use this style.
LPTREEITEM lptiFocus = GetTreeFocus();
LPTREEITEM lpti = new TREEITEM;
if (lpti != NULL)
{
lprvi->iItem = m_arrayItems.GetSize();
lprvi->iSubItem = 0;
lpti->iItem = InsertItemImpl(lprvi);
lpti->iSubItem = iSubItem;
if (lpti->iItem != RVI_INVALID)
{
LPTREEITEM lptiParent = hParent == RVTI_ROOT ? &m_tiRoot : (LPTREEITEM) hParent;
InsertTree(lprvi->iSubItem, lptiParent, (LPTREEITEM) hInsertAfter, lpti, TRUE);
m_arrayItems[lpti->iItem].lptiItem = lpti;
lpti->iIndent = hParent == RVTI_ROOT ? 1 : lptiParent->iIndent + 1;
lpti->lptiParent = lptiParent;
}
else
{
delete lpti;
lpti = NULL;
}
}
BuildTree();
SetTreeFocus(lptiFocus);
return (HTREEITEM) lpti;
}
BOOL CReportCtrl::DeleteItem(HTREEITEM hItem)
{
if (!(m_dwStyle & RVS_TREEVIEW))
return FALSE; // TREEVIEW style must be set to use this style.
LPTREEITEM lptiFocus = GetTreeFocus();
if (lptiFocus != NULL && (IsDescendent(hItem, (HTREEITEM) lptiFocus) || (HTREEITEM) lptiFocus == hItem))
{
if (lptiFocus->lptiSibling != NULL)
lptiFocus = lptiFocus->lptiSibling;
else if (lptiFocus->lptiParent != &m_tiRoot)
lptiFocus = lptiFocus->lptiParent;
else
lptiFocus = NULL;
}
BOOL bResult = DeleteItemImpl((LPTREEITEM) hItem, FALSE);
BuildTree();
SetTreeFocus(lptiFocus);
return bResult;
}
BOOL CReportCtrl::DeleteAllItems()
{
BOOL bResult = TRUE;
if (m_dwStyle & RVS_OWNERDATA) // Use set SetItemCount() when using this style
return FALSE;
if (m_dwStyle & RVS_TREEVIEW)
{
bResult = DeleteItemImpl(&m_tiRoot, TRUE);
}
else
{
RVITEM rvi;
for (int iItem = 0; iItem < m_arrayItems.GetSize(); iItem++)
{
rvi.nMask = RVIM_LPARAM;
rvi.iItem = iItem;
GetItem(&rvi);
Notify(RVN_ITEMDELETED, iItem, 0, 0, rvi.lParam);
}
}
m_arrayRows.RemoveAll();
m_arrayItems.RemoveAll();
m_listSelection.RemoveAll();
m_bUpdateItemMap = TRUE;
m_iVirtualHeight = 0;
m_iFocusRow = RVI_INVALID;
ScrollWindow(SB_VERT, 0);
return bResult;
}
void CReportCtrl::RedrawItems(INT iFirst, INT iLast)
{
CRect rect;
if (iFirst == iLast || iLast == RVI_INVALID)
{
GetItemRect(iFirst, -1, rect);
if (m_bPreview)
rect.bottom += GetItemStruct(iFirst, -1, RVIM_PREVIEW).nPreview;
rect.OffsetRect(GetScrollPos32(SB_HORZ), 0);
}
else
rect = m_rectReport;
InvalidateRect(rect);
}
BOOL CReportCtrl::EnsureVisible(INT iItem, BOOL bUnobstructed)
{
INT iFirst = GetScrollPos32(SB_VERT), iLast;
INT iRow = GetRowFromItem(iItem);
if (iRow < RVI_FIRST)
return FALSE;
INT iVirtualRow = GetVirtualRow(iRow);
GetVisibleRows(bUnobstructed, &iFirst, &iLast);
if (iVirtualRow < iFirst)
ScrollWindow(SB_VERT, iRow);
if (iVirtualRow > iLast)
{
iLast = iRow;
GetVisibleRows(bUnobstructed, &iFirst, &iLast, TRUE);
ScrollWindow(SB_VERT, iFirst);
}
return TRUE;
}
BOOL CReportCtrl::EnsureVisible(HTREEITEM hItem, BOOL bUnobstructed)
{
LPTREEITEM lpti = (LPTREEITEM) hItem;
while (lpti->lptiParent != &m_tiRoot)
{
Expand((HTREEITEM) lpti->lptiParent, RVE_EXPAND);
lpti = lpti->lptiParent;
}
lpti = (LPTREEITEM) hItem;
return EnsureVisible(lpti->iItem, bUnobstructed);
}
INT CReportCtrl::InsertColor(INT iIndex, COLORREF crColor)
{
try
{
m_arrayColors.InsertAt(iIndex, crColor);
if (CreatePalette())
return iIndex;
return -1;
}
catch(CMemoryException * e)
{
e->Delete();
return -1;
}
}
BOOL CReportCtrl::DeleteColor(INT iIndex)
{
if (iIndex >= m_arrayColors.GetSize())
return FALSE;
m_arrayColors.RemoveAt(iIndex);
CreatePalette();
return TRUE;
}
void CReportCtrl::DeleteAllColors()
{
m_arrayColors.RemoveAll();
CreatePalette();
}
INT CReportCtrl::HitTest(LPRVHITTESTINFO lprvhti)
{
ASSERT(lprvhti);
lprvhti->nFlags = 0;
lprvhti->iItem = RVI_INVALID;
lprvhti->iSubItem = -1;
lprvhti->hItem = NULL;
lprvhti->iRow = RVI_INVALID;
lprvhti->iColumn = -1;
CRect rectItem = m_rectEdit;
rectItem.bottom -= GetSystemMetrics(SM_CYFIXEDFRAME) * 2;
if (rectItem.PtInRect(lprvhti->point) || m_rectReport.PtInRect(lprvhti->point))
{
INT iHPos = GetScrollPos32(SB_HORZ);
INT iFirst = GetScrollPos32(SB_VERT), iLast;
BOOL bCheckItem = FALSE;
rectItem.left = -iHPos;
rectItem.right = rectItem.left + m_iVirtualWidth;
if (rectItem.PtInRect(lprvhti->point))
{
iFirst = RVI_EDIT;
lprvhti->iItem = RVI_EDIT;
lprvhti->nFlags |= RVHT_ONITEMEDIT;
bCheckItem = TRUE;
}
else if (!bCheckItem && GetVisibleRows(FALSE, &iFirst, &iLast))
{
ITEM item;
/*$off*/
rectItem.SetRect(
m_rectReport.left-iHPos, m_rectReport.top,
m_rectReport.left-iHPos+m_iVirtualWidth, m_rectReport.top
);
/*$on*/
int iRows = m_dwStyle & RVS_OWNERDATA ? m_iVirtualHeight : m_arrayRows.GetSize();
for (INT iTop = iFirst; iFirst <= iLast && iFirst < iRows; iFirst++)
{
int iRow = GetRowFromVirtualRow(iFirst);
GetItemFromRow(iRow, item);
rectItem.bottom = rectItem.top + m_iDefaultHeight;
if (GetCategoryForRow(iRow, iFirst == iTop) >= 0)
{
if (rectItem.PtInRect(lprvhti->point))
{
lprvhti->nFlags = ((lprvhti->point.x <= (m_iCategoryImage >= 0 ? m_sizeImage.cx : m_iDefaultHeight))) ? RVHT_ONCATIMAGE : RVHT_ONCATITEM;
lprvhti->iItem = GetItemForRow(iRow);
return lprvhti->iItem;
}
if (!(m_dwStyle & RVS_OWNERDATA) && (RVIS_HIDDEN & GetState(iRow)))
{
rectItem.top = rectItem.bottom;
if (iLast + 1 < iRows)
iLast++;
continue;
}
else
{
rectItem.top = rectItem.bottom;
rectItem.bottom += m_iDefaultHeight + (m_bPreview ? item.nPreview : 0);
}
}
else
{
if ((!(m_dwStyle & RVS_OWNERDATA) && (RVIS_HIDDEN & GetState(iRow))))
{
if (iLast + 1 < iRows)
iLast++;
continue;
}
else
rectItem.bottom += (m_bPreview ? item.nPreview : 0);
}
if (rectItem.PtInRect(lprvhti->point))
break;
rectItem.top = rectItem.bottom;
}
if (iFirst <= iLast)
{
lprvhti->iItem = GetItemForRow(GetRowFromVirtualRow(iFirst));
lprvhti->iRow = GetRowFromItem(lprvhti->iItem);
if (m_dwStyle & RVS_TREEVIEW && lprvhti->iItem >= RVI_FIRST)
lprvhti->hItem = (HTREEITEM) m_arrayItems[lprvhti->iItem].lptiItem;
if (m_bPreview && item.nPreview)
{
CRect rectPreview(rectItem);
rectPreview.top += m_iDefaultHeight;
if (rectPreview.PtInRect(lprvhti->point))
{
lprvhti->nFlags |= RVHT_ONITEMPREVIEW;
return lprvhti->iItem;
}
}
bCheckItem = TRUE;
}
}
if (bCheckItem == TRUE)
{
bCheckItem = FALSE;
if (m_dwStyle & RVS_TREEVIEW && m_arrayItems[lprvhti->iItem].lptiItem->iSubItem >= 0)
{
LPTREEITEM lpti = m_arrayItems[lprvhti->iItem].lptiItem;
lprvhti->iSubItem = lpti->iSubItem;
lprvhti->iColumn = 0;
rectItem.OffsetRect(iHPos, 0);
lprvhti->rect = rectItem;
lprvhti->rect.right = lprvhti->rect.left + m_iVirtualWidth;
lprvhti->rect.bottom = rectItem.top + m_iDefaultHeight;
bCheckItem = TRUE;
}
else
{
INT iHeaderItem;
INT iHeaderItems = m_arrayColumns.GetSize();
HDITEM hdi;
hdi.mask = HDI_WIDTH | HDI_LPARAM;
rectItem.right = rectItem.left;
for (iHeaderItem = 0; iHeaderItem < iHeaderItems; iHeaderItem++)
{
CRect rect;
m_wndHeader.GetItemRect(iHeaderItem, &rect);
CPoint point = lprvhti->point;
point.y = (rect.bottom - rect.top) / 2;
point.x += iHPos;
if (point.x >= rect.left && point.x < rect.right)
{
rectItem.left = rect.left;
rectItem.right = rect.right;
break;
}
}
if (iHeaderItem < iHeaderItems)
{
m_wndHeader.GetItem(iHeaderItem, &hdi);
lprvhti->iSubItem = hdi.lParam;
lprvhti->iColumn = GetColumnFromSubItem(lprvhti->iSubItem);
bCheckItem = TRUE;
}
}
if (bCheckItem == TRUE)
{
RVITEM rvi;
rvi.iItem = GetItemForRow(GetRowFromVirtualRow(iFirst));
rvi.iSubItem = lprvhti->iSubItem;
GetItem(&rvi);
if (lprvhti->iColumn == 0 && rvi.nMask & RVIM_INDENT && rvi.iIndent >= 0)
{
rectItem.left += rvi.iIndent * m_iDefaultHeight;
if (rvi.nState & RVIS_TREEMASK)
{
if (lprvhti->point.x >= rectItem.left - m_iDefaultHeight && lprvhti->point.x < rectItem.left)
{
lprvhti->rect = rectItem;
lprvhti->rect.left -= iHPos + m_iDefaultHeight;
lprvhti->rect.right -= lprvhti->rect.left;
lprvhti->rect.bottom = rectItem.top + m_iDefaultHeight;
lprvhti->nFlags |= RVHT_ONITEMTREEBOX;
return lprvhti->iItem;
}
}
}
rectItem.OffsetRect(-iHPos, 0);
lprvhti->rect = rectItem;
lprvhti->rect.bottom = rectItem.top + m_iDefaultHeight;
rectItem.right = rectItem.left + m_iSpacing;
if (rvi.nMask & RVIM_IMAGE)
{
rectItem.right += m_sizeImage.cx + m_iSpacing;
if (lprvhti->point.x < rectItem.right)
{
lprvhti->nFlags |= RVHT_ONITEMIMAGE;
return lprvhti->iItem;
}
}
if (rvi.nMask & RVIM_CHECK)
{
rectItem.right += m_sizeCheck.cx + m_iSpacing;
if (lprvhti->point.x < rectItem.right)
{
lprvhti->nFlags |= RVHT_ONITEMCHECK;
return lprvhti->iItem;
}
}
if (lprvhti->point.x >= rectItem.right && lprvhti->point.x < lprvhti->rect.right - m_iSpacing)
{
lprvhti->nFlags |= RVHT_ONITEMTEXT;
return lprvhti->iItem;
}
lprvhti->nFlags = RVHT_NOWHERE;
}
else
lprvhti->nFlags = RVHT_NOWHERE;
}
else
lprvhti->nFlags = RVHT_NOWHERE;
}
else
{
lprvhti->nFlags |= lprvhti->point.y < m_rectReport.top ? RVHT_ABOVE : 0;
lprvhti->nFlags |= lprvhti->point.y > m_rectReport.bottom ? RVHT_BELOW : 0;
lprvhti->nFlags |= lprvhti->point.x < m_rectReport.left ? RVHT_TOLEFT : 0;
lprvhti->nFlags |= lprvhti->point.x > m_rectReport.right ? RVHT_TORIGHT : 0;
}
return lprvhti->iItem;
}
BOOL CReportCtrl::ResortItems()
{
INT iColumn;
INT iSubItem;
BOOL bAscending;
iColumn = m_wndHeader.GetSortColumn(&bAscending);
iSubItem = GetSubItemFromColumn(iColumn);
return SortItems(iSubItem, bAscending);
}
BOOL CReportCtrl::SortItems(INT iSubItem, BOOL bAscending)
{
CWaitCursor wait;
m_cBaseTime = COleDateTime::GetCurrentTime();
if (m_dwStyle & RVS_OWNERDATA)
return FALSE; // Can't sort if data is managed by owner
BOOL bCategoryAscending;
INT iCategorySubItem = GetCategorySubItem(&bCategoryAscending);
if (iSubItem < 0 && iCategorySubItem < 0)
return FALSE;
INT iColumn = GetColumnFromSubItem(iSubItem);
m_bEarlier = m_bLater = false;
if (iColumn >= 0)
m_wndHeader.SetSortColumn(m_arrayColumns[iColumn], bAscending);
m_bSorting = TRUE;
if (m_dwStyle & RVS_TREEVIEW)
{
LPTREEITEM lptiFocus = GetTreeFocus();
if (iSubItem >= 0)
TreeSort(iSubItem, &m_tiRoot, bAscending, TRUE);
BuildTree();
// TODO: extend tree sort to do both in one pass
if (iCategorySubItem >= 0)
TreeSort(iCategorySubItem, &m_tiRoot, bCategoryAscending, TRUE);
BuildTree();
SetTreeFocus(lptiFocus);
}
else
{
INT iRows = m_arrayRows.GetSize();
if (!iRows)
return FALSE;
INT iFocusItem = GetItemForRow(m_iFocusRow);
if (iSubItem >= 0)
ShellSort(iSubItem, 0, iRows - 1, bAscending);
if (iCategorySubItem >= 0)
ShellSort(iCategorySubItem, 0, iRows - 1, bCategoryAscending);
m_bUpdateItemMap = TRUE;
if (iFocusItem >= 0)
{
m_iFocusRow = GetRowFromItem(iFocusItem);
INT iFirst = m_iFocusRow, iLast;
GetVisibleRows(TRUE, &iFirst, &iLast);
GetVisibleRows(TRUE, &iFirst, &iLast, TRUE);
ScrollWindow(SB_VERT, iFirst);
}
}
m_bSorting = FALSE;
m_bUpdateItemMap = TRUE;
Invalidate();
return TRUE;
}
INT CReportCtrl::CompareItems(INT iItem1, INT iSubItem1, INT iItem2, INT iSubItem2, BOOL bCategory)
{
ASSERT(!(m_dwStyle & RVS_OWNERDATA)); // Can't sort if data is managed by owner
if (m_lpfnrvc == NULL)
{
RVITEM rvi1, rvi2;
TCHAR szText1[REPORTCTRL_MAX_TEXT], szText2[REPORTCTRL_MAX_TEXT];
rvi1.nMask = RVIM_TEXT;
rvi1.iItem = iItem1;
rvi1.iSubItem = iSubItem1;
rvi1.lpszText = szText1;
rvi1.iTextMax = REPORTCTRL_MAX_TEXT;
VERIFY(GetItem(&rvi1));
rvi2.nMask = RVIM_TEXT;
rvi2.iItem = iItem2;
rvi2.iSubItem = iSubItem2;
rvi2.lpszText = szText2;
rvi2.iTextMax = REPORTCTRL_MAX_TEXT;
VERIFY(GetItem(&rvi2));
if (!(rvi1.nMask & RVIM_TEXT || rvi2.nMask & RVIM_TEXT))
{
if ((rvi1.nMask & RVIM_CHECK || rvi2.nMask & RVIM_CHECK))
{
if (rvi1.iCheck < rvi2.iCheck)
return -1;
else if (rvi1.iCheck > rvi2.iCheck)
return 1;
else
return 0;
}
if (rvi1.iImage < rvi2.iImage)
return -1;
else if (rvi1.iImage > rvi2.iImage)
return 1;
else
return 0;
}
else
{
CString str1 = szText1;
CString str2 = szText2;
SUBITEM &subitem1 = m_arraySubItems[iSubItem1];
SUBITEM &subitem2 = m_arraySubItems[iSubItem2];
ASSERT(subitem1.nFormat == subitem2.nFormat);
// See if we're comparing dates, not simply strings
if (subitem1.nFormat & RVCF_EX_HASDATE)
{
if
(
bCategory
&& CCategoryTime(szText1).SameCategoryAs(CCategoryTime(szText2), m_cBaseTime, m_bEarlier & m_bLater)
) return 0;
COleDateTime date1;
BOOL bRes = date1.ParseDateTime(szText1);
// If the timestamp cannot be successfully parsed, consider it a text string
if (!bRes)
return _tcscmp(szText1, szText2);
COleDateTime date2;
bRes = date2.ParseDateTime(szText2);
// If the timestamp cannot be successfully parsed, consider it a text string
if (!bRes)
return _tcscmp(szText1, szText2);
// Now compare to date classes
m_bEarlier |= date1 < m_cBaseTime || date2 < m_cBaseTime;
m_bLater |= date1 > m_cBaseTime || date2 > m_cBaseTime;
if (date1 < date2)
return -1;
else if (date2 < date1)
return 1;
else
return 0;
}
int nComp;
// compare the two strings, notice: in this case, "xxxx10" comes after "xxxx2"
{
CString tmpStr1, tmpStr2;
#define numbers _T("0123456789")
int index = str1.FindOneOf(numbers);
if (index != -1)
tmpStr1 = str1.Right(str1.GetLength() - index);
index = str2.FindOneOf(numbers);
if (index != -1)
tmpStr2 = str2.Right(str2.GetLength() - index);
tmpStr1 = tmpStr1.SpanIncluding(numbers);
tmpStr2 = tmpStr2.SpanIncluding(numbers);
if ((tmpStr1 == _T("")) && (tmpStr2 == _T("")))
nComp = str1.CompareNoCase(str2);
else
{
int num1 = _ttoi(tmpStr1);
int num2 = _ttoi(tmpStr2);
tmpStr1 = str1.SpanExcluding(numbers);
tmpStr2 = str2.SpanExcluding(numbers);
if (tmpStr1 == tmpStr2)
{
if (num1 > num2)
nComp = 1;
else if (num1 < num2)
nComp = -1;
else
nComp = str1.CompareNoCase(str2);
}
else
nComp = str1.CompareNoCase(str2);
}
}
return nComp;
}
}
else
return m_lpfnrvc(iItem1, iSubItem1, iItem2, iSubItem2, m_lParamCompare);
}
INT CReportCtrl::FindItem(LPRVFINDINFO lprvfi, INT iSubItem, INT iStart)
{
if (m_dwStyle & RVS_OWNERDATA)
return FALSE; // Can't find data if data is managed by owner
INT iItems = m_arrayItems.GetSize();
INT iItem = iStart + (lprvfi->nFlags & RVFI_UP ? -1 : 1);
RVITEM rvi;
TCHAR szText[REPORTCTRL_MAX_TEXT];
rvi.lpszText = szText;
rvi.iTextMax = REPORTCTRL_MAX_TEXT;
BOOL bMatch;
while (iItem != iStart)
{
if (iItem >= iItems)
{
if (lprvfi->nFlags & RVFI_WRAP)
iItem = RVI_EDIT;
else
break;
}
if (iItem < RVI_EDIT)
{
if (lprvfi->nFlags & RVFI_WRAP)
iItem = iItems - 1;
else
break;
}
rvi.nMask = RVIM_TEXT | RVIM_IMAGE | RVIM_CHECK | RVIM_LPARAM;
rvi.iItem = iItem;
rvi.iSubItem = iSubItem;
VERIFY(GetItem(&rvi));
bMatch = TRUE;
if (lprvfi->nFlags & RVFI_TEXT && rvi.nMask & RVIM_TEXT)
{
INT iMatch;
if (lprvfi->nFlags & RVFI_PARTIAL)
iMatch = _tcsncmp(lprvfi->lpszText, rvi.lpszText, _tcslen(lprvfi->lpszText));
else
iMatch = _tcscmp(lprvfi->lpszText, rvi.lpszText);
bMatch &= !iMatch ? TRUE : FALSE;
}
else if (lprvfi->nFlags & RVFI_TEXT)
bMatch = FALSE;
if (lprvfi->nFlags & RVFI_IMAGE && rvi.nMask & RVIM_IMAGE)
bMatch &= lprvfi->iImage == rvi.iImage ? TRUE : FALSE;
else if (lprvfi->nFlags & RVFI_IMAGE)
bMatch = FALSE;
if (lprvfi->nFlags & RVFI_CHECK && rvi.nMask & RVIM_CHECK)
bMatch &= lprvfi->iCheck == rvi.iCheck ? TRUE : FALSE;
else if (lprvfi->nFlags & RVFI_CHECK)
bMatch = FALSE;
if (lprvfi->nFlags & RVFI_LPARAM && rvi.nMask & RVIM_LPARAM)
bMatch &= lprvfi->lParam == rvi.lParam ? TRUE : FALSE;
else if (lprvfi->nFlags & RVFI_LPARAM)
bMatch = FALSE;
if (bMatch)
return iItem;
iItem += lprvfi->nFlags & RVFI_UP ? -1 : 1;
}
return RVI_INVALID;
}
BOOL CReportCtrl::Expand(HTREEITEM hItem, UINT nCode)
{
if (!(m_dwStyle & RVS_TREEVIEW))
return FALSE;
LPTREEITEM lpti = (LPTREEITEM) hItem;
switch (nCode)
{
case RVE_COLLAPSE:
lpti->bOpen = FALSE;
break;
case RVE_EXPAND:
lpti->bOpen = TRUE;
break;
case RVE_TOGGLE:
lpti->bOpen = !lpti->bOpen;
break;
default:
return FALSE;
}
INT iFocusItem = RVI_INVALID;
if (m_iFocusRow != RVI_INVALID)
{
SetState(m_iFocusRow, 0, RVIS_FOCUSED);
iFocusItem = GetItemForRow(m_iFocusRow);
}
BuildTree();
if (iFocusItem >= RVI_FIRST)
{
LPTREEITEM lptiAncestor = GetVisibleAncestor(m_arrayItems[iFocusItem].lptiItem);
ASSERT(lptiAncestor != &m_tiRoot && lptiAncestor != NULL);
m_iFocusRow = GetRowFromItem(lptiAncestor->iItem);
ASSERT(m_iFocusRow != RVI_INVALID);
SetState(m_iFocusRow, RVIS_FOCUSED, RVIS_FOCUSED);
DeselectDescendents(lptiAncestor);
}
INT iFirst = GetScrollPos32(SB_VERT), iLast;
GetVisibleRows(TRUE, &iFirst, &iLast);
if (m_iFocusRow <= iFirst || iLast >= m_iFocusRow - 1)
GetVisibleRows(TRUE, &iFirst, &iLast, TRUE);
ScrollWindow(SB_VERT, iFirst > RVI_INVALID ? iFirst : 0);
return TRUE;
}
BOOL CReportCtrl::ExpandAll(HTREEITEM hItem, UINT nCode, INT iLevels)
{
if (!(m_dwStyle & RVS_TREEVIEW))
return FALSE;
LPTREEITEM lpti;
if (hItem == RVTI_ROOT)
{
lpti = &m_tiRoot;
}
else
{
Expand(hItem, nCode);
lpti = (LPTREEITEM) hItem;
}
return ExpandAllImpl(hItem, nCode, iLevels, 0);
}
void CReportCtrl::FlushCache(INT iItem)
{
if (!(m_dwStyle & RVS_OWNERDATA || m_bUseItemCacheMap))
return;
CacheDelete(iItem);
}
void CReportCtrl::Copy()
{
CString str;
if (OpenClipboard() == FALSE || ::EmptyClipboard() == FALSE)
return;
HGLOBAL hMem = NULL, h;
CArray<INT, INT> arraySelection;
SelectionToSortedArray(arraySelection);
INT i, iSize = arraySelection.GetSize();
UINT nData = 0;
for (i = 0; i < iSize; i++)
{
INT iRow = arraySelection[i];
INT iItem = GetItemForRow(iRow);
ASSERT(iItem > RVI_INVALID);
str = GetItemString(iItem, g_cCBSeparator);
ASSERT(str.GetLength() > 0);
if (hMem == NULL)
h = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, str.GetLength() * (sizeof(TCHAR) + 1));
else
h = ::GlobalReAlloc(hMem, nData + str.GetLength() * (sizeof(TCHAR) + 1), GMEM_MOVEABLE | GMEM_DDESHARE);
if (h == NULL)
break;
hMem = h;
LPBYTE lpx = (LPBYTE)::GlobalLock(hMem);
ASSERT(lpx != NULL);
memcpy(lpx + nData, (LPCTSTR) str, str.GetLength() * sizeof(TCHAR));
::GlobalUnlock(hMem);
nData += str.GetLength() * sizeof(TCHAR);
((LPTSTR) lpx)[nData / sizeof(TCHAR)] = 0;
}
if (hMem != NULL)
::SetClipboardData(CF_TEXT, hMem);
::CloseClipboard();
}
void CReportCtrl::Cut()
{
}
void CReportCtrl::Paste()
{
}
// CReportCtrl misc
INT CReportCtrl::PreviewHeight(CFont *pFont, UINT nLines)
{
INT iHeight = -1;
ASSERT(pFont != NULL);
CDC *pDC = GetDC();
if (pDC)
{
CFont *pDCFont = pDC->SelectObject(pFont);
TEXTMETRIC tm;
pDC->GetTextMetrics(&tm);
pDC->SelectObject(pDCFont);
ReleaseDC(pDC);
iHeight = (tm.tmHeight + tm.tmExternalLeading) * nLines;
}
return iHeight;
}
INT CReportCtrl::PreviewHeight(CFont *pFont, LPCTSTR lpszText, LPRECT lpRect)
{
INT iHeight = -1;
ASSERT(pFont != NULL);
CDC *pDC = GetDC();
if (pDC)
{
CRect rect(GetSubItemWidth(0) + 10, 0, m_iVirtualWidth, 0);
if (lpRect != NULL)
rect = *lpRect;
CFont *pDCFont = pDC->SelectObject(pFont);
(pDCFont);
iHeight = pDC->DrawText(lpszText, -1, rect, DT_NOPREFIX | DT_LEFT | DT_CALCRECT | DT_EXPANDTABS | DT_WORDBREAK | DT_NOCLIP);
ReleaseDC(pDC);
}
return iHeight;
}
// CReportCtrl implementation
void CReportCtrl::GetSysColors()
{
m_crBackground = ::GetSysColor(COLOR_WINDOW);
m_crBkSelected = ::GetSysColor(COLOR_HIGHLIGHT);
m_crBkSelectedNoFocus = ::GetSysColor(COLOR_BTNFACE);
m_crText = ::GetSysColor(COLOR_WINDOWTEXT);
m_crTextSelected = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
m_crTextSelectedNoFocus = ::GetSysColor(COLOR_WINDOWTEXT);
m_crGrid = ::GetSysColor(COLOR_BTNFACE);
m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
m_cr3DLight = ::GetSysColor(COLOR_3DLIGHT);
m_cr3DShadow = ::GetSysColor(COLOR_3DSHADOW);
m_cr3DHiLight = ::GetSysColor(COLOR_3DHILIGHT);
m_cr3DDkShadow = ::GetSysColor(COLOR_3DDKSHADOW);
}
UINT CReportCtrl::GetMouseScrollLines()
{
UINT nScrollLines = 3; // Reasonable default
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
{
TCHAR szData[128];
DWORD dwKeyDataType;
DWORD dwDataBufSize = sizeof(szData);
if
(RegQueryValueEx(hKey, _T("WheelScrollLines"), NULL, &dwKeyDataType, (LPBYTE) & szData, &dwDataBufSize) == ERROR_SUCCESS) nScrollLines = _tcstoul(szData, NULL, 10);
RegCloseKey(hKey);
}
return nScrollLines;
}
BOOL CReportCtrl::CreatePalette()
{
if (m_palette.m_hObject)
m_palette.DeleteObject();
INT iUserColors = m_arrayColors.GetSize();
INT iBitmapColors = 0;
DIBSECTION ds;
BITMAPINFOHEADER &bmInfo = ds.dsBmih;
if ((HBITMAP) m_bitmap != NULL)
{
m_bitmap.GetObject(sizeof(ds), &ds);
iBitmapColors = bmInfo.biClrUsed ? bmInfo.biClrUsed : 1 << bmInfo.biBitCount;
}
INT iColors = iUserColors + iBitmapColors;
if (!iColors)
return FALSE;
CClientDC cdc(NULL);
if (iColors <= 256)
{
UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * iColors);
LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize];
pLP->palVersion = 0x300;
pLP->palNumEntries = (WORD) (iColors);
INT i;
for (i = 0; i < iUserColors; i++)
{
pLP->palPalEntry[i].peRed = GetRValue(m_arrayColors[i]);
pLP->palPalEntry[i].peGreen = GetGValue(m_arrayColors[i]);
pLP->palPalEntry[i].peBlue = GetBValue(m_arrayColors[i]);
pLP->palPalEntry[i].peFlags = 0;
}
if (iBitmapColors > 0)
{
// Create the palette
RGBQUAD *pRGB = new RGBQUAD[iBitmapColors];
CDC dc;
dc.CreateCompatibleDC(&cdc);
dc.SelectObject(&m_bitmap);
::GetDIBColorTable(dc, 0, iColors, pRGB);
for (INT i = 0; i < iBitmapColors; i++)
{
pLP->palPalEntry[iUserColors + i].peRed = pRGB[i].rgbRed;
pLP->palPalEntry[iUserColors + i].peGreen = pRGB[i].rgbGreen;
pLP->palPalEntry[iUserColors + i].peBlue = pRGB[i].rgbBlue;
pLP->palPalEntry[iUserColors + i].peFlags = 0;
}
delete[] pRGB;
}
m_palette.CreatePalette(pLP);
delete[] pLP;
}
else
m_palette.CreateHalftonePalette(&cdc);
return TRUE;
}
BOOL CReportCtrl::NotifyHdr(LPNMRVHEADER lpnmrvhdr, UINT nCode, INT iHdrItem)
{
lpnmrvhdr->hdr.hwndFrom = GetSafeHwnd();
lpnmrvhdr->hdr.idFrom = GetDlgCtrlID();
lpnmrvhdr->hdr.code = nCode;
HDITEM hdi;
hdi.mask = HDI_LPARAM;
hdi.lParam = -1;
m_wndHeader.GetItem(iHdrItem, &hdi);
lpnmrvhdr->iSubItem = hdi.lParam;
return Notify((LPNMREPORTVIEW) lpnmrvhdr);
}
BOOL CReportCtrl::Notify(UINT nCode, INT iItem, INT iSubItem, UINT nState, LPARAM lParam)
{
NMREPORTVIEW nmrv;
nmrv.hdr.hwndFrom = GetSafeHwnd();
nmrv.hdr.idFrom = GetDlgCtrlID();
nmrv.hdr.code = nCode;
nmrv.iItem = iItem;
nmrv.iSubItem = iSubItem;
if (m_dwStyle & RVS_TREEVIEW && iItem >= RVI_FIRST)
nmrv.hItem = (HTREEITEM) m_arrayItems[iItem].lptiItem;
nmrv.nState = nState;
nmrv.lParam = lParam;
return Notify(&nmrv);
}
BOOL CReportCtrl::Notify(UINT nCode, UINT nKeys, LPRVHITTESTINFO lprvhti)
{
NMREPORTVIEW nmrv;
nmrv.hdr.hwndFrom = GetSafeHwnd();
nmrv.hdr.idFrom = GetDlgCtrlID();
nmrv.hdr.code = nCode;
nmrv.nKeys = nKeys;
nmrv.point = lprvhti->point;
nmrv.nFlags = lprvhti->nFlags;
nmrv.iItem = lprvhti->iItem;
nmrv.iSubItem = lprvhti->iSubItem;
if (m_dwStyle & RVS_TREEVIEW && lprvhti->iItem >= RVI_FIRST)
nmrv.hItem = (HTREEITEM) m_arrayItems[lprvhti->iItem].lptiItem;
if (lprvhti->iItem >= 0 && (!(m_dwStyle & RVS_OWNERDATA)))
nmrv.lParam = GetItemStruct(lprvhti->iItem, -1).lParam;
return Notify(&nmrv);
}
BOOL CReportCtrl::Notify(LPNMREPORTVIEW lpnmrv)
{
INT iCode = 0 - (lpnmrv->hdr.code - RVN_FIRST);
if (m_uNotifyMask & (1 << iCode))
{
CWnd *pWnd = GetParent();
if (pWnd)
return pWnd->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM) lpnmrv);
}
return FALSE;
}
BOOL CReportCtrl::PreTranslateMessage(MSG *pMsg)
{
if ((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_RETURN))
{
INT iFirst = GetScrollPos32(SB_VERT), iLast;
GetVisibleRows(TRUE, &iFirst, &iLast);
NMREPORTVIEW nmrv;
nmrv.hdr.hwndFrom = GetSafeHwnd();
nmrv.hdr.idFrom = GetDlgCtrlID();
nmrv.hdr.code = RVN_KEYDOWN;
nmrv.nKeys = VK_RETURN;
nmrv.iItem = GetItemForRow(m_iFocusRow);
nmrv.iSubItem = GetSubItemFromColumn(m_iFocusColumn);
if (nmrv.iItem != RVI_INVALID && (!(m_dwStyle & RVS_OWNERDATA)))
nmrv.lParam = GetItemStruct(nmrv.iItem, -1).lParam;
m_bProcessKey = FALSE;
if (Notify(&nmrv))
return true;
}
return CWnd::PreTranslateMessage(pMsg);
}
void CReportCtrl::Layout()
{
CRect rect;
GetClientRect(rect);
Layout(rect.Width(), rect.Height());
}
void CReportCtrl::Layout(INT cx, INT cy)
{
HDLAYOUT hdl;
WINDOWPOS wpos;
hdl.prc = &m_rectReport;
hdl.pwpos = &wpos;
m_rectReport.SetRect(0, 0, cx, cy);
if (IsWindow(m_wndHeader.GetSafeHwnd()))
{
m_wndTip.Hide();
VERIFY(m_wndHeader.SendMessage(HDM_LAYOUT, 0, (LPARAM) & hdl));
int iCategoryHdrHeight = GetCategoryHdrHeight();
if (m_dwStyle & RVS_NOHEADER)
m_rectHeader.SetRect(wpos.x, 0, wpos.x + wpos.cx, 0);
else
m_rectHeader.SetRect(wpos.x, wpos.y, wpos.x + wpos.cx, wpos.y + wpos.cy);
CCategoryHdr *ac = GetCategoryHdrCtrl();
if (ac != NULL)
VERIFY(ac->SendMessage(HDM_LAYOUT, 0, (LPARAM) & hdl));
m_rectHeader.OffsetRect(0, iCategoryHdrHeight);
m_rectReport.top = iCategoryHdrHeight + m_rectHeader.Height();
m_rectTop = m_rectHeader;
if (m_dwStyle & RVS_SHOWEDITROW)
{
UINT nFrameHeight = GetSystemMetrics(SM_CYFIXEDFRAME);
m_rectTop.bottom += m_iDefaultHeight + 2 * nFrameHeight;
m_rectReport.top += m_iDefaultHeight + 2 * nFrameHeight;
m_rectEdit = m_rectTop;
m_rectEdit.top = m_rectHeader.bottom;
}
else
m_rectEdit.SetRectEmpty();
ScrollWindow(SB_VERT, GetScrollPos32(SB_VERT));
ScrollWindow(SB_HORZ, GetScrollPos32(SB_HORZ));
}
}
CReportCtrl::LPITEM CReportCtrl::CacheLookup(INT iItem)
{
if (iItem == RVI_EDIT)
return m_bEditValid ? &m_itemEdit : NULL;
UINT nIndex = iItem % REPORTCTRL_MAX_CACHE;
if (m_aciCache[nIndex].iItem == iItem)
return &m_aciCache[nIndex].item;
return NULL;
}
CReportCtrl::LPITEM CReportCtrl::CacheAdd(INT iItem)
{
if (iItem == RVI_EDIT)
{
m_bEditValid = TRUE;
return &m_itemEdit;
}
UINT nIndex = iItem % REPORTCTRL_MAX_CACHE;
m_aciCache[nIndex].iItem = iItem;
return &m_aciCache[nIndex].item;
}
void CReportCtrl::CacheDelete(INT iItem)
{
if (iItem == RVI_INVALID)
{
for (UINT n = 0; n < REPORTCTRL_MAX_CACHE; n++)
m_aciCache[n].iItem = RVI_INVALID;
}
else if (iItem == RVI_EDIT)
{
m_bEditValid = FALSE;
}
else
{
UINT nIndex = iItem % REPORTCTRL_MAX_CACHE;
if (m_aciCache[nIndex].iItem == iItem)
m_aciCache[nIndex].iItem = RVI_INVALID;
}
}
void CReportCtrl::BuildTree()
{
if (m_dwStyle & RVS_TREEVIEW)
{
m_iTreeDepth = 0;
m_iVirtualHeight = 0;
m_arrayRows.RemoveAll();
BuildTree(&m_tiRoot);
if (!m_bSorting && m_arraySubItems[0].nFormat & RVCF_EX_AUTOWIDTH)
{
int oldWidth = GetSubItemWidth(0);
int iWidth = max(m_iDefaultHeight * m_iTreeDepth, m_arraySubItems[0].iMinWidth);
if (abs(iWidth - oldWidth) >= m_iDefaultHeight / 2)
{
SetSubItemWidth(0, iWidth);
ScrollWindow(SB_HORZ, GetScrollPos32(SB_HORZ));
Notify(RVN_INDENTCHANGED);
}
}
}
m_bUpdateItemMap = TRUE;
}
void CReportCtrl::BuildTree(LPTREEITEM lpti)
{
LPTREEITEM lptiSibling = lpti->lptiChildren;
BOOL bAsTreeColumn = AsTreeColumn();
while (lptiSibling != NULL)
{
INT iRow = m_arrayRows.Add(lptiSibling->iItem);
m_iVirtualHeight++;
ITEM &item = GetItemStruct(lptiSibling->iItem, lptiSibling->iItem, RVIM_STATE | RVIM_INDENT);
item.nState &= ~RVIS_TREEMASK;
item.iIndent = bAsTreeColumn ? lptiSibling->iIndent : 0;
if (item.iIndent > m_iTreeDepth)
m_iTreeDepth = item.iIndent;
if (lptiSibling->lptiChildren != NULL)
{
item.nState |= lptiSibling->bOpen ? RVIS_TREEOPENED : RVIS_TREECLOSED;
SetItemStruct(lptiSibling->iItem, item);
SetState(iRow, item.nState & RVIS_TREEMASK, RVIS_TREEMASK);
if (lptiSibling->bOpen)
BuildTree((LPTREEITEM) lptiSibling);
}
lptiSibling = lptiSibling->lptiSibling;
}
}
void CReportCtrl::InsertTree(INT iSubItem, LPTREEITEM lptiParent, LPTREEITEM lptiInsertAfter, LPTREEITEM lpti,
BOOL bAscending)
{
LPTREEITEM lptiSibling = lptiParent->lptiChildren;
if (lptiSibling == NULL)
{
ASSERT(lptiParent != NULL);
lptiParent->lptiChildren = lpti;
}
else
{
switch ((ULONG) lptiInsertAfter)
{
case RVTI_FIRST:
lpti->lptiSibling = lptiSibling;
lptiParent->lptiChildren = lpti;
break;
case RVTI_LAST:
while (lptiSibling->lptiSibling != NULL)
lptiSibling = lptiSibling->lptiSibling;
lptiSibling->lptiSibling = lpti;
break;
case RVTI_SORT:
{
INT iSubItem1 = lpti->iSubItem < 0 ? iSubItem : lpti->iSubItem;
INT iSubItem2 = lptiSibling->iSubItem < 0 ? iSubItem : lptiSibling->iSubItem;
if
(
(bAscending && CompareItems(lpti->iItem, iSubItem1, lptiSibling->iItem, iSubItem2) < 0)
|| (!bAscending && CompareItems(lpti->iItem, iSubItem1, lptiSibling->iItem, iSubItem2) > 0)
)
{
lpti->lptiSibling = lptiSibling;
lptiParent->lptiChildren = lpti;
break;
}
else
{
while (lptiSibling->lptiSibling != NULL)
{
iSubItem1 = lpti->iSubItem < 0 ? iSubItem : lpti->iSubItem;
iSubItem2 = lptiSibling->iSubItem < 0 ? iSubItem : lptiSibling->iSubItem;
/*$off*/
if(
(bAscending && CompareItems(lpti->iItem, iSubItem1, lptiSibling->lptiSibling->iItem, iSubItem2) < 0) ||
(!bAscending && CompareItems(lpti->iItem, iSubItem1, lptiSibling->lptiSibling->iItem, iSubItem2) > 0)
)
break;
/*$on*/
lptiSibling = lptiSibling->lptiSibling;
}
lptiInsertAfter = lptiSibling;
}
}
// Intentional
default:
lpti->lptiSibling = lptiInsertAfter->lptiSibling;
lptiInsertAfter->lptiSibling = lpti;
break;
}
}
}
BOOL CReportCtrl::ExpandAllImpl(HTREEITEM hRoot, UINT nCode, INT iLevels, INT iDeep)
{
HTREEITEM hItem = GetNextItem(hRoot, RVGN_CHILD);
iDeep++;
if (iLevels >= 0 && iDeep <= iLevels)
return TRUE;
while (hItem != NULL)
{
Expand(hItem, nCode);
if (ExpandAllImpl(hItem, nCode, iLevels, iDeep) == FALSE)
return FALSE;
hItem = GetNextItem(hItem, RVGN_NEXT);
}
return TRUE;
}
INT CReportCtrl::InsertItemImpl(LPRVITEM lprvi)
{
ASSERT(lprvi->iItem >= 0); // Use SetItem to set Edit row item
ASSERT(lprvi->iItem <= m_arrayItems.GetSize());
ASSERT(lprvi->iSubItem < m_arraySubItems.GetSize());
INT iRow = -1;
BOOL bInserted1 = FALSE, bInserted2 = FALSE;
try
{
ITEM item;
item.rdData.New(m_arraySubItems.GetSize());
m_arrayItems.InsertAt(lprvi->iItem, item);
bInserted1 = TRUE;
VERIFY(SetItem(lprvi));
if (!(m_dwStyle & RVS_TREEVIEW))
{
iRow = lprvi->iItem;
m_arrayRows.InsertAt(iRow, INT_MIN);
bInserted2 = TRUE;
INT iRows = m_arrayRows.GetSize();
for (INT i = 0; i < iRows; i++)
if (m_arrayRows[i] >= iRow)
m_arrayRows[i]++;
m_arrayRows[iRow] = lprvi->iItem;
}
ScrollWindow(SB_VERT, GetScrollPos32(SB_VERT));
CList<INT, INT> list;
POSITION pos = m_listSelection.GetHeadPosition();
while (pos != NULL)
{
INT iSelectedItem = m_listSelection.GetAt(pos);
if (iSelectedItem >= lprvi->iItem)
list.AddTail(iSelectedItem + 1);
else
list.AddTail(iSelectedItem);
m_listSelection.GetNext(pos);
}
m_listSelection.RemoveAll();
m_listSelection.AddTail(&list);
return lprvi->iItem;
}
catch(CMemoryException * e)
{
if (bInserted1)
m_arrayItems.RemoveAt(lprvi->iItem);
if (bInserted2)
m_arrayRows.RemoveAt(iRow);
e->Delete();
return RVI_INVALID;
}
}
BOOL CReportCtrl::DeleteItemImpl(INT iItem)
{
RVITEM rvi;
rvi.nMask = RVIM_LPARAM;
rvi.iItem = iItem;
GetItem(&rvi);
Notify(RVN_ITEMDELETED, iItem, 0, 0, rvi.lParam);
INT i, iItems = m_arrayItems.GetSize();
if (m_dwStyle & RVS_TREEVIEW)
{
for (i = 0; i < iItems; i++)
{
LPTREEITEM lpti = m_arrayItems[i].lptiItem;
ASSERT(i == lpti->iItem);
if (lpti->iItem > iItem)
lpti->iItem--;
}
}
m_arrayItems.RemoveAt(iItem);
if (!(m_dwStyle & RVS_TREEVIEW))
{
for (i = 0; i < m_arrayRows.GetSize(); i++)
if (m_arrayRows[i] == iItem)
m_arrayRows.RemoveAt(i);
INT iRows = m_arrayRows.GetSize();
for (i = 0; i < iRows; i++)
if (m_arrayRows[i] > iItem)
m_arrayRows[i]--;
}
POSITION pos = m_listSelection.Find(iItem);
if (pos != NULL)
m_listSelection.RemoveAt(pos);
CList<INT, INT> list;
pos = m_listSelection.GetHeadPosition();
while (pos != NULL)
{
INT iSelectedItem = m_listSelection.GetAt(pos);
if (iSelectedItem >= iItem)
list.AddTail(iSelectedItem - 1);
else
list.AddTail(iSelectedItem);
m_listSelection.GetNext(pos);
}
m_listSelection.RemoveAll();
m_listSelection.AddTail(&list);
return TRUE;
}
BOOL CReportCtrl::DeleteItemImpl(LPTREEITEM lpti, BOOL bChildrenOnly)
{
LPTREEITEM lptiSibling = lpti->lptiChildren;
while (lptiSibling != NULL)
{
if (lptiSibling->lptiChildren != NULL)
DeleteItemImpl(lptiSibling, TRUE);
DeleteItemImpl(lptiSibling->iItem);
LPTREEITEM lptiChild = lptiSibling;
lptiSibling = lptiSibling->lptiSibling;
delete lptiChild;
}
lpti->lptiChildren = NULL;
if (!bChildrenOnly)
{
DeleteItemImpl(lpti->iItem);
lptiSibling = (LPTREEITEM) GetNextItem((HTREEITEM) lpti, RVGN_PREVIOUS);
if (lptiSibling != NULL)
lptiSibling->lptiSibling = lpti->lptiSibling;
else if (lpti->lptiParent->lptiChildren == lpti)
lpti->lptiParent->lptiChildren = lpti->lptiSibling;
delete lpti;
}
return TRUE;
}
CReportCtrl::ITEM & CReportCtrl::GetItemStruct(INT iItem, INT iSubItem, UINT nMask)
{
if (m_dwStyle & RVS_OWNERDATA)
{
INT iSubItems = m_arraySubItems.GetSize();
TCHAR szText[REPORTCTRL_MAX_TEXT + 1];
memset(szText, 0, sizeof(szText));
LPITEM lpItem = CacheLookup(iItem);
if (m_bUseItemCacheMap && lpItem != NULL)
{
if (iSubItem < 0)
return *lpItem;
INT iImage, iOverlay, iCheck, iColor, iTextMax;
if (lpItem->rdData.GetSubItem(iSubItem, &iImage, &iOverlay, &iCheck, &iColor, szText, &iTextMax))
return *lpItem;
}
else
{
VERIFY((lpItem = CacheAdd(iItem)) != 0);
lpItem->rdData.New(iSubItems);
}
VERIFY(LoadItemData(lpItem, iItem, iSubItem, nMask));
return *lpItem;
}
return iItem == RVI_EDIT ? m_itemEdit : m_arrayItems[iItem];
}
void CReportCtrl::SetItemStruct(INT iItem, ITEM &item)
{
if (m_dwStyle & RVS_OWNERDATA)
return;
if (iItem == RVI_EDIT)
m_itemEdit = item;
else
m_arrayItems[iItem] = item;
}
BOOL CReportCtrl::LoadItemData(LPITEM lpItem, INT iItem, INT iSubItem, UINT nMask)
{
CWnd *pWnd = GetParent();
if (pWnd)
{
TCHAR szText[REPORTCTRL_MAX_TEXT + 1];
memset(szText, 0, sizeof(szText));
NMRVITEMCALLBACK nmrvic;
nmrvic.hdr.hwndFrom = GetSafeHwnd();
nmrvic.hdr.idFrom = GetDlgCtrlID();
nmrvic.hdr.code = RVN_ITEMCALLBACK;
nmrvic.item.iItem = iItem;
nmrvic.item.iSubItem = iSubItem;
nmrvic.item.nMask = nMask;
if (iSubItem >= 0)
{
nmrvic.item.nMask |= RVIM_TEXT;
nmrvic.item.lpszText = szText;
nmrvic.item.iTextMax = REPORTCTRL_MAX_TEXT;
}
Notify((LPNMREPORTVIEW) & nmrvic);
INT iColor = nmrvic.item.nMask & RVIM_TEXTCOLOR ? nmrvic.item.iTextColor : -1;
INT iImage = nmrvic.item.nMask & RVIM_IMAGE ? nmrvic.item.iImage : -1;
INT iOverlay = nmrvic.item.nMask & RVIM_OVERLAY ? nmrvic.item.iOverlay : -1;
INT iCheck = nmrvic.item.nMask & RVIM_CHECK ? nmrvic.item.iCheck : -1;
if (iSubItem >= 0)
lpItem->rdData.SetSubItem(iSubItem, iImage, iOverlay, iCheck, iColor, szText);
lpItem->iBkColor = nmrvic.item.nMask & RVIM_BKCOLOR ? nmrvic.item.iBkColor : -1;
lpItem->nPreview = nmrvic.item.nMask & RVIM_PREVIEW ? nmrvic.item.nPreview : 0;
lpItem->nState = nmrvic.item.nMask & RVIM_STATE ? nmrvic.item.nState : 0;
lpItem->iIndent = nmrvic.item.nMask & RVIM_INDENT ? nmrvic.item.iIndent : -1;
return TRUE;
}
return FALSE;
}
INT CReportCtrl::GetItemForRow(INT iRow)
{
if (m_dwStyle & RVS_OWNERDATA)
return iRow;
if (iRow == RVI_INVALID)
return RVI_INVALID;
return iRow == RVI_EDIT ? RVI_EDIT : m_arrayRows[iRow];
}
INT CReportCtrl::GetItemFromRow(INT iRow, ITEM &item)
{
if (iRow == RVI_INVALID)
return RVI_INVALID;
INT iItem = iRow;
if (!(m_dwStyle & RVS_OWNERDATA))
iItem = GetItemForRow(iItem);
item = GetItemStruct(iItem, -1);
return iItem;
}
INT CReportCtrl::GetRowFromItem(INT iItem)
{
if (iItem == RVI_EDIT || m_dwStyle & RVS_OWNERDATA)
return iItem;
INT iRows = m_arrayRows.GetSize(), iRow;
if (iRows > 0)
{
if (m_bUpdateItemMap)
{
for (iRow = 0; iRow < iRows; iRow++)
m_mapItemToRow[m_arrayRows[iRow]] = iRow;
m_bUpdateItemMap = FALSE;
MarkCategories();
}
if (m_mapItemToRow.Lookup(iItem, iRow))
return iRow;
}
return RVI_INVALID;
}
INT CReportCtrl::GetSubItemFromColumn(INT iColumn)
{
if (iColumn < 0 || iColumn >= m_arrayColumns.GetSize())
return -1;
HDITEM hdi;
hdi.mask = HDI_WIDTH | HDI_LPARAM;
m_wndHeader.GetItem(m_arrayColumns[iColumn], &hdi);
return hdi.lParam;
}
BOOL CReportCtrl::AsTreeColumn()
{
if (m_dwStyle & RVS_TREEVIEW && GetColumnFromSubItem(0) == 0)
return TRUE;
return FALSE;
}
INT CReportCtrl::GetColumnFromSubItem(INT iSubItem)
{
if (iSubItem < 0)
return -1;
INT iColumn, iColumns = m_arrayColumns.GetSize();
for (iColumn = 0; iColumn < iColumns; iColumn++)
{
HDITEM hdi;
hdi.mask = HDI_WIDTH | HDI_LPARAM;
m_wndHeader.GetItem(m_arrayColumns[iColumn], &hdi);
if (hdi.lParam == iSubItem)
return iColumn;
}
return -1;
}
CReportCtrl::LPTREEITEM CReportCtrl::GetVisibleAncestor(LPTREEITEM lpti)
{
BOOL bExit = FALSE;
LPTREEITEM lptiAncestor = GetVisibleAncestor(lpti, &bExit);
return bExit ? lptiAncestor : lpti;
}
CReportCtrl::LPTREEITEM CReportCtrl::GetVisibleAncestor(LPTREEITEM lpti, LPBOOL lpbExit)
{
if (lpti->lptiParent == &m_tiRoot)
return lpti;
LPTREEITEM lptiAncestor = GetVisibleAncestor(lpti->lptiParent, lpbExit);
if (*lpbExit == TRUE)
return lptiAncestor;
if (lptiAncestor->bOpen == FALSE)
{
*lpbExit = TRUE;
return lptiAncestor;
}
return lpti;
}
BOOL CReportCtrl::SetSubItemWidthImpl(INT iSubItem, INT iWidth)
{
INT iSubItems = m_arraySubItems.GetSize();
ASSERT(iSubItem <= iSubItems);
try
{
iWidth = iWidth < 0 ? m_iDefaultWidth : iWidth;
SUBITEM subitem;
subitem = m_arraySubItems.GetAt(iSubItem);
iWidth = iWidth < subitem.iMinWidth ? subitem.iMinWidth : iWidth;
iWidth = iWidth > subitem.iMaxWidth && subitem.iMaxWidth > 0 ? subitem.iMaxWidth : iWidth;
INT iOldWidth = subitem.iWidth;
subitem.iWidth = iWidth;
m_arraySubItems.SetAt(iSubItem, subitem);
INT iColumn = GetColumnFromSubItem(iSubItem);
if (iColumn < 0)
return TRUE;
HDITEM hditem;
hditem.mask = HDI_WIDTH;
if (!m_wndHeader.GetItem(m_arrayColumns[iColumn], &hditem))
return FALSE;
m_iVirtualWidth += iWidth - iOldWidth;
hditem.cxy = iWidth;
m_wndHeader.SetItem(m_arrayColumns[iColumn], &hditem);
return TRUE;
}
catch(CMemoryException * e)
{
e->Delete();
return FALSE;
}
}
void CReportCtrl::GetExpandedItemText(LPRVITEM lprvi)
{
UNREFERENCED_PARAMETER(lprvi);
}
void CReportCtrl::SetState(INT iRow, UINT nState, UINT nMask)
{
ASSERT(iRow >= RVI_EDIT);
if (iRow == RVI_EDIT)
{
m_itemEdit.nState &= ~nMask;
m_itemEdit.nState |= nState & nMask;
}
else if (!(m_dwStyle & RVS_OWNERDATA))
{
m_arrayItems[m_arrayRows[iRow]].nState &= ~nMask;
m_arrayItems[m_arrayRows[iRow]].nState |= nState & nMask;
}
FlushCache(iRow);
}
UINT CReportCtrl::GetState(INT iRow)
{
RVITEM rvi;
rvi.iItem = GetItemForRow(iRow);
rvi.nMask = RVIM_STATE;
GetItem(&rvi);
ASSERT(rvi.nMask & RVIM_STATE); // Need to return state of the item
return rvi.nState;
}
CReportCtrl::LPTREEITEM CReportCtrl::GetTreeFocus()
{
INT iFocusItem = GetItemForRow(m_iFocusRow);
if (iFocusItem >= RVI_EDIT)
SetState(m_iFocusRow, 0, RVIS_FOCUSED);
m_iFocusRow = RVI_INVALID;
return iFocusItem >= RVI_FIRST ? m_arrayItems[iFocusItem].lptiItem : NULL;
}
void CReportCtrl::SetTreeFocus(LPTREEITEM lptiFocus)
{
INT iFirst = GetScrollPos32(SB_VERT), iLast;
if (lptiFocus != NULL)
{
m_iFocusRow = GetRowFromItem(lptiFocus->iItem);
if (m_iFocusRow == RVI_INVALID)
{
ScrollWindow(SB_VERT, 0);
return;
}
}
else
m_iFocusRow = RVI_EDIT;
SetState(m_iFocusRow, RVIS_FOCUSED, RVIS_FOCUSED);
GetVisibleRows(TRUE, &iFirst, &iLast);
if (m_iFocusRow >= 0 && (m_iFocusRow <= iFirst || iLast >= m_iFocusRow - 1))
GetVisibleRows(TRUE, &iFirst, &iLast, TRUE);
ScrollWindow(SB_VERT, iFirst > RVI_INVALID ? iFirst : 0);
}
void CReportCtrl::DeselectDescendents(LPTREEITEM lptiParent)
{
LPTREEITEM lptiSibling = lptiParent->lptiChildren;
while (lptiSibling != NULL)
{
if (lptiSibling->lptiChildren != NULL)
DeselectDescendents(lptiSibling);
POSITION pos = m_listSelection.Find(lptiSibling->iItem);
if (pos != NULL)
{
m_arrayItems[lptiSibling->iItem].nState &= ~RVIS_SELECTED;
m_listSelection.RemoveAt(pos);
}
lptiSibling = lptiSibling->lptiSibling;
}
}
void CReportCtrl::ReOrder()
{
m_wndHeader.SetSortColumn(-1, FALSE);
m_wndHeader.ReOrder();
for (INT i = 0; i < m_arrayColumns.GetSize(); i++)
m_arrayColumns[i] = i;
}
void CReportCtrl::ReorderColumns()
{
LPINT lpi;
INT iColumns = m_wndHeader.GetItemCount();
if (iColumns)
{
m_iFocusColumn = m_iFocusColumn >= iColumns ? iColumns - 1 : m_iFocusColumn;
INT iSubItem = m_arrayColumns.GetSize() ? GetSubItemFromColumn(m_iFocusColumn) : -1;
m_arrayColumns.SetSize(iColumns, 8);
lpi = m_arrayColumns.GetData();
if (!m_wndHeader.GetOrderArray(lpi, iColumns))
return;
if (iSubItem > 0)
m_iFocusColumn = GetColumnFromSubItem(iSubItem);
else
m_iFocusColumn = -1;
}
else
{
m_arrayColumns.RemoveAll();
m_iFocusColumn = -1;
}
if (m_lprsilc != NULL)
m_lprsilc->UpdateList();
m_bColumnsReordered = FALSE;
}
BOOL CReportCtrl::GetRowRect(INT iRow, INT iColumn, LPRECT lpRect, UINT nCode)
{
// Select a visible sub-item
if (iColumn < 0 && nCode != RVIR_BOUNDS)
return FALSE;
INT iFirst, iLast;
if (iRow != RVI_EDIT)
{
iFirst = GetScrollPos32(SB_VERT);
GetVisibleRows(FALSE, &iFirst, &iLast);
// Select a visible item
if (iFirst > iRow || iLast < iRow)
return FALSE;
*lpRect = m_rectReport;
for (INT iTop = iFirst; iFirst < iRow; iFirst++)
{
lpRect->top += (GetCategoryForRow(iFirst, iFirst == iTop) >= 0 ? m_iDefaultHeight : 0);
if ((m_dwStyle & RVS_OWNERDATA) || !(RVIS_HIDDEN & m_arrayItems[iFirst].nState))
lpRect->top += m_iDefaultHeight + (m_bPreview ? GetItemStruct(GetItemForRow(iFirst), -1).nPreview : 0);
}
lpRect->top += (GetCategoryForRow(iFirst, iFirst == iTop) == -1 ? 0 : m_iDefaultHeight);
}
else
*lpRect = m_rectEdit;
lpRect->bottom = lpRect->top + m_iDefaultHeight;
INT iPos = GetScrollPos32(SB_HORZ);
lpRect->left -= iPos;
lpRect->right -= iPos;
INT iLeft = lpRect->left;
if (iColumn < 0)
return TRUE;
HDITEM hdi;
hdi.mask = HDI_WIDTH | HDI_LPARAM;
for (iFirst = 0; iFirst < iColumn; iFirst++)
{
m_wndHeader.GetItem(m_arrayColumns[iFirst], &hdi);
lpRect->left += hdi.cxy;
}
m_wndHeader.GetItem(m_arrayColumns[iColumn], &hdi);
if (nCode == RVIR_TEXTNOCOLUMNS)
lpRect->right = iLeft + m_iVirtualWidth;
else
lpRect->right = lpRect->left + hdi.cxy;
return TRUE;
}
INT CReportCtrl::GetVisibleRows(BOOL bUnobstructed, LPINT lpiFirst, LPINT lpiLast, BOOL bReverse)
{
INT iHeight = m_rectReport.Height();
INT iVirtualRows = 0;
if (!bReverse)
{
INT iVirtualRow;
if (lpiFirst)
iVirtualRow = *lpiFirst;
else
iVirtualRow = GetScrollPos32(SB_VERT);
for (INT iTop = iVirtualRow; iVirtualRow < m_iVirtualHeight && iHeight > 0; iVirtualRow++, iVirtualRows++)
{
INT iRow = GetRowFromVirtualRow(iVirtualRow);
INT iItem = GetItemForRow(iRow);
iHeight -= (GetCategoryForRow(iRow, iVirtualRow == iTop) == -1 ? 0 : m_iDefaultHeight);
if (m_dwStyle & RVS_OWNERDATA || !(RVIS_HIDDEN & m_arrayItems[iItem].nState))
iHeight -= m_iDefaultHeight + (m_bPreview ? GetItemStruct(iItem, -1).nPreview : 0);
if (bUnobstructed && iHeight <= 0)
break;
}
if (lpiLast)
*lpiLast = iVirtualRow - 1;
}
else
{
ASSERT(lpiFirst);
ASSERT(lpiLast);
INT iFirst = *lpiFirst;
for (*lpiFirst = *lpiLast; *lpiFirst >= 0 && iHeight > 0; *lpiFirst -= 1, iVirtualRows++)
{
INT iRow = GetRowFromVirtualRow(*lpiFirst);
INT iItem = GetItemForRow(iRow);
iHeight -= (GetCategoryForRow(iRow, *lpiFirst == iFirst) == -1 ? 0 : m_iDefaultHeight);
if (m_dwStyle & RVS_OWNERDATA || !(RVIS_HIDDEN & m_arrayItems[iItem].nState))
iHeight -= m_iDefaultHeight + (m_bPreview ? GetItemStruct(iItem, -1).nPreview : 0);
if (bUnobstructed && iHeight <= 0)
break;
}
*lpiFirst += 1;
}
return iVirtualRows;
}
void CReportCtrl::SelectRows(INT iFirst, INT iLast, BOOL bSelect, BOOL bKeepSelection, BOOL bInvert, BOOL bNotify)
{
INT i;
if (m_dwStyle & RVS_SINGLESELECT)
{
iLast = iFirst;
bKeepSelection = FALSE;
}
if (m_iFocusRow > RVI_INVALID && iFirst != m_iFocusRow)
{
SetState(m_iFocusRow, 0, RVIS_FOCUSED);
RedrawRows(m_iFocusRow);
}
m_iFocusRow = iFirst;
SetState(iFirst, RVIS_FOCUSED, RVIS_FOCUSED);
if (iFirst > iLast)
{
iLast += iFirst;
iFirst = iLast - iFirst;
iLast -= iFirst;
}
if (bSelect)
{
for (i = iFirst; i <= iLast; i++)
{
INT iItem = GetItemForRow(i);
UINT nOldState, nNewState;
nOldState = nNewState = GetState(i);
if (bInvert)
nNewState ^= RVIS_SELECTED;
else
nNewState |= RVIS_SELECTED;
if (nNewState != nOldState)
{
if (bNotify && Notify(RVN_SELECTIONCHANGING, iItem, -1, nNewState))
continue;
POSITION pos = m_listSelection.Find(iItem);
if (nNewState & RVIS_SELECTED)
{
if (pos == NULL)
m_listSelection.AddTail(iItem);
}
else
m_listSelection.RemoveAt(pos);
SetState(i, nNewState, RVIS_SELECTED);
if (bNotify)
Notify(RVN_SELECTIONCHANGED, iItem, -1, nNewState);
}
EnsureVisible(iItem);
}
RedrawRows(iFirst, iLast);
}
else
RedrawRows(iFirst);
if (!bKeepSelection && bSelect)
{
CList<INT, INT> list;
POSITION pos = m_listSelection.GetHeadPosition();
while (pos != NULL)
{
INT iItem = m_listSelection.GetAt(pos);
INT i = GetRowFromItem(iItem);
UINT nOldState, nNewState;
nOldState = nNewState = GetState(i);
if ((i < iFirst || i > iLast) && nOldState & RVIS_SELECTED)
{
nNewState &= ~RVIS_SELECTED;
if (nNewState != nOldState)
{
if (bNotify && Notify(RVN_SELECTIONCHANGING, iItem, -1, nNewState))
continue;
SetState(i, nNewState, RVIS_SELECTED);
if (bNotify)
Notify(RVN_SELECTIONCHANGED, iItem, -1, nNewState);
RedrawRows(i);
}
}
else
list.AddTail(iItem);
m_listSelection.GetNext(pos);
}
m_listSelection.RemoveAll();
m_listSelection.AddTail(&list);
}
if (bNotify)
Notify(RVN_SELECTIONDONECHANGING);
UpdateWindow();
}
INT CReportCtrl::GetScrollPos32(INT iBar, BOOL bGetTrackPos)
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
INT iMax = iBar == SB_HORZ ? m_iVirtualWidth : m_iVirtualHeight;
if (bGetTrackPos)
{
if (GetScrollInfo(iBar, &si, SIF_TRACKPOS))
return min(si.nTrackPos, iMax);
}
else
{
if (GetScrollInfo(iBar, &si, SIF_POS))
return min(si.nPos, iMax);
}
return 0;
}
BOOL CReportCtrl::SetScrollPos32(INT iBar, INT iPos, BOOL bRedraw)
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = iPos;
return SetScrollInfo(iBar, &si, bRedraw);
}
void CReportCtrl::ScrollWindow(INT iBar, INT iPos)
{
if (m_iScrollWindow > 3 || m_rectReport.Width() <= 0 || m_rectReport.Height() <= 0)
return;
m_iScrollWindow++; // Layout invokes ScrollWindow recursively
m_wndTip.Hide();
if (iBar == SB_HORZ)
{
SCROLLINFO si;
INT iWidth = m_rectReport.Width();
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
si.nPage = iWidth;
si.nMin = 0;
si.nMax = iWidth < m_iVirtualWidth ? m_iVirtualWidth - 2 : 0;
si.nPos = iPos;
SetScrollInfo(SB_HORZ, &si, TRUE);
INT x = GetScrollPos32(SB_HORZ);
INT cx = iWidth < m_iVirtualWidth ? m_iVirtualWidth : iWidth;
if (int iCategoryHdrHeight = GetCategoryHdrHeight ())
VERIFY(m_wndCategoryHdr->SetWindowPos(&wndTop, -x, 0, cx, iCategoryHdrHeight, SWP_SHOWWINDOW));
VERIFY(m_wndHeader.SetWindowPos(&wndTop, -x, m_rectHeader.top, cx, m_rectHeader.Height(),
m_dwStyle & RVS_NOHEADER ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
}
if (iBar == SB_VERT)
{
ASSERT(iPos >= 0 && iPos <= m_iVirtualHeight);
INT iFirst = iPos, iLast;
GetVisibleRows(TRUE, &iFirst, &iLast);
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
si.nPage = (iLast - iFirst + 1);
si.nMin = 0;
si.nMax = (iLast - iFirst + 1) < m_iVirtualHeight ? m_iVirtualHeight - 1 : 0;
si.nPos = iFirst;
iFirst = 0;
if (GetVisibleRows(TRUE, &iFirst, &iLast) == m_iVirtualHeight)
si.nPos = 0;
SetScrollInfo(SB_VERT, &si, TRUE);
}
if (m_dwStyle & RVS_SHOWEDITROW)
InvalidateRect(m_rectEdit);
InvalidateRect(m_rectReport);
--m_iScrollWindow;
}
void CReportCtrl::EnsureVisibleColumn(INT iColumn)
{
if (iColumn > -1)
{
INT iWidth = m_rectReport.Width();
INT iPos = GetScrollPos(SB_HORZ);
INT iOffset = 0;
HDITEM hdi;
hdi.mask = HDI_WIDTH | HDI_LPARAM;
for (INT i = 0; i < iColumn; i++)
{
m_wndHeader.GetItem(m_arrayColumns[i], &hdi);
iOffset += hdi.cxy;
}
m_wndHeader.GetItem(m_arrayColumns[i], &hdi);
if (iOffset + hdi.cxy > iPos + iWidth || iOffset < iPos)
ScrollWindow(SB_HORZ, iOffset);
}
}
void CReportCtrl::SetIndentFirstColumn(INT iIndent)
{
UnindentFirstColumn();
m_iIndentColumn = iIndent;
IndentFirstColumn();
}
void CReportCtrl::IndentFirstColumn()
{
if (m_arrayColumns.GetSize() > 0 && m_iIndentColumn > 0)
{
ASSERT(m_bIndentColumn == FALSE);
INT iSubItem = GetSubItemFromColumn(0);
ASSERT(iSubItem >= 0);
SUBITEM &subitem = m_arraySubItems[iSubItem];
subitem.iMinWidth += m_iIndentColumn;
subitem.iMaxWidth += subitem.iMaxWidth > 0 ? m_iIndentColumn : 0;
SetSubItemWidthImpl(iSubItem, subitem.iWidth + m_iIndentColumn);
m_wndHeader.SetMinMax(m_wndHeader.OrderToIndex(0), subitem.iMinWidth, subitem.iMaxWidth);
m_bIndentColumn = TRUE;
}
}
void CReportCtrl::UnindentFirstColumn()
{
if (m_bIndentColumn && m_iIndentColumn >= 0)
{
INT iSubItem = GetSubItemFromColumn(0);
ASSERT(iSubItem >= 0);
SUBITEM &subitem = m_arraySubItems[iSubItem];
subitem.iMaxWidth -= subitem.iMaxWidth > 0 ? m_iIndentColumn : 0;
subitem.iMinWidth -= m_iIndentColumn;
/*$off*/
SetSubItemWidthImpl(
iSubItem,
(subitem.iMinWidth <= 0&& subitem.iWidth-m_iIndentColumn < 20)
?
20:subitem.iWidth-m_iIndentColumn
);
m_iIndentColumnPending =
m_iIndentColumnPending >= 0
?
m_iIndentColumnPending:m_wndHeader.OrderToIndex(0);
m_wndHeader.SetMinMax(
m_iIndentColumnPending,
subitem.iMinWidth,
subitem.iMaxWidth
);
/*$on*/
}
m_bIndentColumn = FALSE;
m_iIndentColumnPending = -1;
}
void CReportCtrl::SetRedraw(BOOL bRedraw)
{
CWnd::SetRedraw(bRedraw);
m_bRedrawFlag = bRedraw;
if (bRedraw == TRUE)
UpdateWindow();
}
void CReportCtrl::RedrawRows(INT iFirst, INT iLast, BOOL bUpdate)
{
CRect rect;
if (iFirst == iLast || iLast == RVI_INVALID)
{
if (iFirst == RVI_INVALID && iLast == RVI_INVALID)
return;
GetRowRect(iFirst, -1, rect);
if (m_bPreview)
rect.bottom += GetItemStruct(GetItemForRow(iFirst), -1, RVIM_PREVIEW).nPreview;
rect.OffsetRect(GetScrollPos32(SB_HORZ), 0);
}
else
rect = m_rectReport;
InvalidateRect(rect);
if (bUpdate)
UpdateWindow();
}
void CReportCtrl::SelectionToSortedArray(CArray<INT, INT> &arrayRows)
{
INT iPos = 0, iSize;
POSITION pos = m_listSelection.GetHeadPosition();
while (pos != NULL)
{
INT iRow = GetRowFromItem(m_listSelection.GetAt(pos));
iSize = arrayRows.GetSize();
if (iSize)
{
if (arrayRows[iPos] <= iRow)
{
while (iPos < iSize && arrayRows[iPos] <= iRow)
iPos++;
}
else
{
while (iPos > 0 && arrayRows[iPos - 1] > iRow)
iPos--;
}
}
arrayRows.InsertAt(iPos, iRow);
m_listSelection.GetNext(pos);
}
}
void CReportCtrl::DrawCtrl(CDC *pDC)
{
CRect rectClip;
if (pDC->GetClipBox(&rectClip) == ERROR)
return;
INT iHPos = GetScrollPos32(SB_HORZ);
INT iVPos = GetScrollPos32(SB_VERT);
DrawBkgnd(pDC, rectClip, m_dwStyle & WS_DISABLED ? m_cr3DFace : m_crBackground);
CPen pen(m_iGridStyle, 1, m_crGrid);
CPen *pPen = pDC->SelectObject(&pen);
CFont *pFont = pDC->SelectObject(GetFont());
pDC->SetBkMode(TRANSPARENT);
CRect rect;
CRect rectRow
(
m_rectReport.left - iHPos,
m_rectReport.top,
m_rectReport.left - iHPos + m_iVirtualWidth,
m_rectReport.top
);
GetClientRect(rect);
rect.right = rect.left + m_iDefaultHeight;
rect.OffsetRect(-iHPos, 0);
if (m_iIndentColumn > 0 && m_bIndentGrey && rectClip.left < rect.right && rect.right > 0)
pDC->FillSolidRect(rect, m_cr3DFace);
TCHAR szText[REPORTCTRL_MAX_TEXT];
RVITEM rvi;
rvi.nMask = RVIM_TEXT;
rvi.lpszText = szText;
rvi.iTextMax = REPORTCTRL_MAX_TEXT;
NMRVDRAWPREVIEW nmrvdp;
nmrvdp.hdr.hwndFrom = GetSafeHwnd();
nmrvdp.hdr.idFrom = GetDlgCtrlID();
nmrvdp.hdr.code = RVN_ITEMDRAWPREVIEW;
nmrvdp.hDC = pDC->m_hDC;
INT iColumns = m_arrayColumns.GetSize();
if (m_bColumnsReordered)
{
UnindentFirstColumn();
ReorderColumns();
IndentFirstColumn();
}
if (m_dwStyle & RVS_SHOWEDITROW)
{
GetClientRect(rect);
rect.bottom = m_rectEdit.bottom;
rect.top = rect.bottom - GetSystemMetrics(SM_CYFIXEDFRAME) * 2;
pDC->FillSolidRect(rect, m_cr3DFace);
pDC->Draw3dRect(rect, m_cr3DHiLight, m_cr3DDkShadow);
rvi.iItem = RVI_EDIT;
rvi.nMask = RVIM_TEXT;
GetItem(&rvi);
rect = m_rectEdit;
rect.bottom -= GetSystemMetrics(SM_CYFIXEDFRAME) * 2;
rect.left -= iHPos;
DrawRow(pDC, rect, rectClip, RVI_EDIT, &rvi, FALSE);
if (m_bFocus && m_hEditWnd == NULL && m_iFocusRow == RVI_EDIT && m_iFocusColumn < 0)
{
if (m_dwStyle & (RVS_SHOWHGRID | RVS_SHOWVGRID))
rect.DeflateRect(1, 0, 1, 0);
pDC->SetBkColor(m_dwStyle & WS_DISABLED ? m_cr3DFace : m_crBackground);
pDC->SetTextColor(m_crText);
pDC->DrawFocusRect(rect);
}
}
if (!m_iVirtualHeight)
{
rectRow.top += 2;
rectRow.bottom = rectRow.top + m_iDefaultHeight;
GetClientRect(rect);
rect.top = rectRow.top;
rect.bottom = rectRow.bottom;
if (rectRow.Width() < rect.Width())
rect = rectRow;
if (!m_strNoItems.IsEmpty())
{
pDC->SetTextColor(m_crText);
pDC->DrawText(m_strNoItems, rect, DT_CENTER | DT_END_ELLIPSIS | DT_NOPREFIX);
}
}
else
{
for
(
INT iVirtualRow = iVPos;
iVirtualRow < m_iVirtualHeight && rectRow.top < rectClip.bottom + m_iDefaultHeight;
iVirtualRow++
)
{
int iRow = GetRowFromVirtualRow(iVirtualRow);
rvi.iItem = GetItemForRow(iRow);
rvi.nMask = RVIM_TEXT;
rvi.lpszText = szText;
VERIFY(GetItem(&rvi));
INT iCategoryHeight = (GetCategoryForRow(iRow, iVirtualRow == iVPos) == -1 ? 0 : m_iDefaultHeight);
rectRow.left = m_rectReport.left - iHPos;
CRect rectRowBefore(rectRow);
if (!(rvi.nState & RVIS_HIDDEN))
rectRow.bottom = rectRow.top + m_iDefaultHeight + (m_bPreview ? rvi.nPreview : 0);
if (rectRow.bottom + iCategoryHeight >= rectClip.top)
{
if (rvi.iIndent >= 0)
{
rectRow.left += rvi.iIndent * m_iDefaultHeight;
rectRow.right = max(rectRow.left, rectRow.right);
}
DrawRow(pDC, rectRow, rectClip, iVirtualRow, &rvi, iVirtualRow & 1);
rectRow.OffsetRect(0, iCategoryHeight);
if (rvi.nState & RVIS_HIDDEN)
{
rectRow.bottom = rectRow.top;
}
else
{
if (m_bPreview && rvi.nMask & RVIM_PREVIEW && rvi.nPreview)
{
nmrvdp.iItem = rvi.iItem;
nmrvdp.nState = rvi.nState;
nmrvdp.rect = rectRow;
nmrvdp.rect.top += m_iDefaultHeight;
nmrvdp.lParam = rvi.lParam;
Notify((LPNMREPORTVIEW) & nmrvdp);
}
}
if (m_dwStyle & RVS_SHOWHGRID)
{
if (iCategoryHeight)
{
pDC->MoveTo(rectRow.left, rectRowBefore.bottom - 1 + iCategoryHeight);
pDC->LineTo(rectRow.right, rectRowBefore.bottom - 1 + iCategoryHeight);
}
pDC->MoveTo(rectRow.left, rectRow.bottom - 1);
pDC->LineTo(rectRow.right, rectRow.bottom - 1);
}
if
(
m_bFocus
&& m_hEditWnd == NULL
&& m_iFocusRow == iVirtualRow
&& (!(m_dwStyle & RVS_FOCUSSUBITEMS) || m_iFocusColumn < 0)
)
{
if (m_dwStyle & (RVS_SHOWHGRID | RVS_SHOWVGRID))
rectRow.DeflateRect(1, 0, 1, 1);
pDC->SetBkColor(m_dwStyle & WS_DISABLED ? m_cr3DFace : m_crBackground);
pDC->SetTextColor(m_crText);
if (m_bFrameFocus)
pDC->DrawFocusRect(rectRow);
if (m_dwStyle & (RVS_SHOWHGRID | RVS_SHOWVGRID))
rectRow.InflateRect(1, 0, 1, 1);
}
}
else
rectRow.OffsetRect(0, (GetCategoryForRow(iVirtualRow, iVirtualRow == iVPos) == -1 ? 0 : m_iDefaultHeight));
rectRow.top = rectRow.bottom;
}
if (AsTreeColumn())
{
for (; iVirtualRow < m_iVirtualHeight; iVirtualRow++)
{
rvi.iItem = GetItemForRow(GetRowFromVirtualRow(iVirtualRow));
HTREEITEM thisNode = GetItemHandle(rvi.iItem);
ASSERT(thisNode);
if (GetNextItem(thisNode, RVGN_PARENT) == NULL)
break;
rvi.nMask = RVIM_TEXT;
rvi.lpszText = szText;
VERIFY(GetItem(&rvi));
rectRow.left = m_rectReport.left - iHPos;
rectRow.bottom = rectRow.top + m_iDefaultHeight + (m_bPreview ? rvi.nPreview : 0);
if (rectRow.bottom >= rectClip.top)
{
if (rvi.iIndent >= 0)
{
rectRow.left += rvi.iIndent * m_iDefaultHeight;
rectRow.right = max(rectRow.left, rectRow.right);
}
DrawRow(pDC, rectRow, rectClip, iVirtualRow, &rvi, iVirtualRow & 1);
if (m_bPreview && rvi.nMask & RVIM_PREVIEW && rvi.nPreview)
{
nmrvdp.iItem = rvi.iItem;
nmrvdp.nState = rvi.nState;
nmrvdp.rect = rectRow;
nmrvdp.rect.top += m_iDefaultHeight;
nmrvdp.lParam = rvi.lParam;
Notify((LPNMREPORTVIEW) & nmrvdp);
}
}
rectRow.top = rectRow.bottom;
}
}
rectRow.top = m_rectReport.top;
rectRow.bottom = m_rectReport.bottom < rectRow.bottom ? m_rectReport.bottom : rectRow.bottom;
if (m_dwStyle & (RVS_SHOWHGRID | RVS_SHOWVGRID))
{
if (m_dwStyle & RVS_SHOWHGRIDEX)
{
pDC->MoveTo(rectRow.left, m_rectReport.top);
pDC->LineTo(rectRow.left, m_rectReport.bottom);
pDC->MoveTo(rectRow.right - 1, m_rectReport.top);
pDC->LineTo(rectRow.right - 1, m_rectReport.bottom);
}
else
{
pDC->MoveTo(rectRow.left, rectRow.top);
pDC->LineTo(rectRow.left, rectRow.bottom);
pDC->MoveTo(rectRow.right - 1, rectRow.top);
pDC->LineTo(rectRow.right - 1, rectRow.bottom);
}
}
if (m_dwStyle & RVS_SHOWHGRID && m_dwStyle & RVS_SHOWHGRIDEX)
{
while (rectRow.bottom <= rectClip.bottom)
{
pDC->MoveTo(rectRow.left, rectRow.bottom - 1);
pDC->LineTo(rectRow.right, rectRow.bottom - 1);
rectRow.bottom += m_iDefaultHeight;
}
}
if (m_dwStyle & RVS_SHOWVGRID)
{
for (INT iColumn = 0; iColumn < iColumns && rectRow.left < rectClip.right; iColumn++)
{
HDITEM hdi;
hdi.mask = HDI_WIDTH;
m_wndHeader.GetItem(m_arrayColumns[iColumn], &hdi);
rectRow.left += hdi.cxy;
pDC->MoveTo(rectRow.left - 1, rectRow.top);
pDC->LineTo(rectRow.left - 1, rectRow.bottom);
}
}
}
pDC->SelectObject(pFont);
pDC->SelectObject(pPen);
pen.DeleteObject();
}
void CReportCtrl::DrawRow(CDC *pDC, CRect rectRow, CRect rectClip, INT iVirtualRow, LPRVITEM lprvi, BOOL bAlternate)
{
int iRow = GetRowFromVirtualRow(iVirtualRow);
INT iColumns = m_arrayColumns.GetSize();
INT iCategorySubItem = GetCategoryForRow(iRow, GetScrollPos32(SB_VERT) == iVirtualRow);
ASSERT(!(m_dwStyle & RVS_SHOWCOLORALTERNATE && !m_arrayColors.GetSize()));
ASSERT(lprvi->lpszText != NULL);
LPTSTR lpszText = lprvi->lpszText;
COLORREF crBackground = m_dwStyle & WS_DISABLED ? m_cr3DFace : m_crBackground;
CRect rectItem(rectRow.left, rectRow.top, rectRow.left, rectRow.top + m_iDefaultHeight);
CRect rectFill(rectRow);
if (lprvi->nState & RVIS_BOLD)
pDC->SelectObject(&m_fontBold);
if (iCategorySubItem >= 0)
{
lprvi->iSubItem = iCategorySubItem;
CFlatHeaderCtrl *hc = GetHeaderCtrl();
lprvi->nMask |= RVIM_CATEGORY;
rectItem.left = DrawRowBkgnd(pDC, rectItem, lprvi, GetRootRow(iRow), bAlternate);
lprvi->nMask &= ~RVIM_CATEGORY;
rectItem.right = hc->GetWidth() - rectItem.left;
rectFill.OffsetRect(0, m_iDefaultHeight);
}
for (INT iColumn = 0; iColumn < iColumns && rectItem.left < rectClip.right;)
{
int iDisplayRow = iRow;
HDITEM hdi;
hdi.mask = HDI_WIDTH | HDI_LPARAM;
if (iCategorySubItem >= 0)
{
hdi.lParam = (LPARAM) iCategorySubItem;
iDisplayRow = GetRootRow(iRow);
}
else
{
if (lprvi->nState & RVIS_HIDDEN)
break;
lprvi->iItem = GetItemForRow(iDisplayRow);
VERIFY(GetItem(lprvi));
m_wndHeader.GetItem(m_arrayColumns[iColumn], &hdi);
lprvi->iSubItem = -1;
if (iColumn == 0)
{
DrawRowBkgnd(pDC, rectFill, lprvi, iDisplayRow, bAlternate);
int itemIndex = GetItemForRow(iDisplayRow);
lastRenderedPosition.SetAtGrow(itemIndex, rectFill.top);
}
rectItem.right = rectItem.left + hdi.cxy - (iColumn == 0 && lprvi->iIndent >= 0 ? lprvi->iIndent * m_iDefaultHeight : 0);
}
lprvi->iSubItem = hdi.lParam;
if (rectItem.right > rectClip.left)
{
lprvi->nMask = RVIM_TEXT;
lprvi->lpszText = lpszText;
lprvi->iItem = GetItemForRow(iDisplayRow);
VERIFY(GetItem(lprvi));
COLORREF crText = lprvi->nMask & RVIM_TEXTCOLOR ? m_arrayColors[lprvi->iTextColor] : m_crText;
if (lprvi->nState & RVIS_SELECTED)
{
if (m_bFocus && iCategorySubItem < 0)
crText =
(
m_dwStyle & RVS_SHOWCOLORALWAYS
&& lprvi->nMask & RVIM_TEXTCOLOR
) ? crText : m_crTextSelected;
else if (m_dwStyle & RVS_SHOWSELALWAYS)
crText = lprvi->nMask & RVIM_TEXTCOLOR
&& m_dwStyle & RVS_SHOWCOLORALWAYS ? crText : m_crTextSelectedNoFocus;
}
if
(
m_iFocusRow == iDisplayRow
&& m_iFocusColumn == iColumn
&& (m_iFocusRow == RVI_EDIT || m_dwStyle & RVS_FOCUSSUBITEMS)
)
{
pDC->FillSolidRect(rectItem, crBackground);
crText = m_crText;
}
pDC->SetTextColor(crText);
if (iCategorySubItem < 0 && iColumn == 0 && lprvi->iIndent >= 0)
{
if (AsTreeColumn())
{
CRgn oldClipRegion;
GetClipRgn(pDC->GetSafeHdc(), oldClipRegion);
m_wndHeader.GetItem(m_arrayColumns[iColumn], &hdi);
pDC->IntersectClipRect(rectClip.left, rectClip.top, hdi.cxy, rectClip.bottom);
HTREEITEM thisNode = GetItemHandle(GetItemForRow(iDisplayRow));
ASSERT(thisNode);
HTREEITEM parent = GetNextItem(thisNode, RVGN_PARENT);
if (parent)
{
// We need to draw a line up to our parent. (Or to our previous sibling, if any.)
int startOfLine = lastRenderedPosition[GetItemIndex(parent)] - m_iDefaultHeight / 2;
HTREEITEM previous = GetNextItem(thisNode, RVGN_PREVIOUS);
while (previous)
{
RVITEM previousItem;
LPTSTR szText = 0;
previousItem.iItem = GetItemIndex(previous);
previousItem.nMask = RVIM_TEXT;
previousItem.lpszText = szText;
VERIFY(GetItem(&previousItem));
// Is the previous node a sibling (at our own level?)
if (lprvi->iIndent == previousItem.iIndent)
{
// We want to draw our line up to this sibling.
startOfLine = lastRenderedPosition[GetItemIndex(previous)] - m_iDefaultHeight / 2;
// If the sibling has children, then we need to draw only to the bottom of their
// box.
if (previousItem.nState & RVIS_TREEMASK)
startOfLine += 5; // Magic number! Ungh!
break;
}
previous = GetNextItem(previous, RVGN_PREVIOUS);
if (previous == parent)
previous = 0;
}
// Draw a line up to our parent (or sibling)
pDC->MoveTo(rectItem.left - m_iDefaultHeight / 2, startOfLine + m_iDefaultHeight);
pDC->LineTo(rectItem.left - m_iDefaultHeight / 2, rectItem.top + m_iDefaultHeight / 2);
pDC->LineTo(rectItem.left - m_iDefaultHeight / 2, rectItem.top + m_iDefaultHeight / 2);
// Draw a line from our node to the right. Children might attach to this line
// later.
if ((lprvi->nState & RVIS_TREEOPENED))
{
pDC->MoveTo(rectItem.left - m_iDefaultHeight / 2, // should be width!
rectItem.top + m_iDefaultHeight / 2);
pDC->LineTo(rectItem.left + m_iDefaultHeight / 2, // should be width!
rectItem.top + m_iDefaultHeight / 2);
}
else
{
pDC->MoveTo(rectItem.left - m_iDefaultHeight / 2, // should be width!
rectItem.top + m_iDefaultHeight / 2);
pDC->LineTo(rectItem.left, rectItem.top + m_iDefaultHeight / 2);
}
}
else
{
// Don't render the dash if there are no children.
if (lprvi->nState & RVIS_TREEMASK)
{
// There is no parent (this is a top-level node)
pDC->MoveTo(rectItem.left - m_iDefaultHeight / 2, // should be width!
rectItem.top + m_iDefaultHeight / 2);
pDC->LineTo(rectItem.left + m_iDefaultHeight / 2, // should be width!
rectItem.top + m_iDefaultHeight / 2);
}
}
pDC->SelectClipRgn(&oldClipRegion);
}
if (AsTreeColumn() && lprvi->nState & RVIS_TREEMASK)
{
rectItem.left -= m_iDefaultHeight;
DrawTreeBox(pDC, rectItem, lprvi->nState & RVIS_TREEOPENED);
rectItem.left += m_iDefaultHeight;
}
if (!(m_dwStyle & RVS_OWNERDATA))
{
LPTREEITEM lpti = m_arrayItems[lprvi->iItem].lptiItem;
if (lpti != NULL)
{
if (lpti->iSubItem >= 0)
{
rectItem.right = rectRow.right;
lprvi->iSubItem = lpti->iSubItem;
lprvi->nMask = RVIM_TEXT;
lprvi->lpszText = lpszText;
VERIFY(GetItem(lprvi));
rectItem.DeflateRect(m_iSpacing, 0);
DrawItem(pDC, rectItem, lprvi);
rectItem.InflateRect(m_iSpacing, 0);
break;
}
}
}
}
rectItem.DeflateRect(m_iSpacing, 0);
if (iCategorySubItem >= 0)
lprvi->nMask |= RVIM_CATEGORY;
DrawItem(pDC, rectItem, lprvi);
lprvi->nMask &= ~RVIM_CATEGORY;
rectItem.InflateRect(m_iSpacing, 0);
if
(
iCategorySubItem < 0
&& m_bFocus
&& m_hEditWnd == NULL
&& m_iFocusRow == iDisplayRow
&& m_iFocusColumn == iColumn
&& (m_iFocusRow == RVI_EDIT || m_dwStyle & RVS_FOCUSSUBITEMS)
)
{
if (m_dwStyle & (RVS_SHOWHGRID | RVS_SHOWVGRID))
rectItem.DeflateRect(1, 0, 1, 1);
pDC->SetBkColor(crBackground);
pDC->SetTextColor(m_crText);
pDC->DrawFocusRect(rectItem);
if (m_dwStyle & (RVS_SHOWHGRID | RVS_SHOWVGRID))
rectItem.InflateRect(1, 0, 1, 1);
}
}
if (iCategorySubItem >= 0)
{
RVITEM rvi;
rvi.nMask = RVIM_CATEGORY;
rvi.iSubItem = iCategorySubItem;
DrawRowFrgnd(pDC, rectItem, &rvi, iDisplayRow, bAlternate);
iCategorySubItem = -1;
rectItem.OffsetRect(0, rectItem.bottom - rectItem.top);
rectItem.left = rectRow.left;
}
else
{
rectItem.left = rectItem.right;
iColumn++;
if (!(iColumn < iColumns && rectItem.left < rectClip.right))
DrawRowFrgnd(pDC, rectItem, lprvi, iDisplayRow, bAlternate);
}
}
if (lprvi->nState & RVIS_BOLD)
pDC->SelectObject(GetFont());
}
void CReportCtrl::DrawRowFrgnd(CDC *pDC, CRect rect, LPRVITEM lprvi, INT iRow, BOOL bAlternate)
{
UNREFERENCED_PARAMETER(iRow);
UNREFERENCED_PARAMETER(bAlternate);
if (lprvi->nMask & RVIM_CATEGORY)
{
CPen *pPen = pDC->GetCurrentPen();
CPen penHighLight(PS_SOLID, 1, m_cr3DDkShadow);
pDC->SelectObject(&penHighLight);
pDC->MoveTo(0, rect.bottom - 1);
pDC->LineTo(GetHeaderCtrl()->GetWidth(), rect.bottom - 1);
pDC->SelectObject(pPen);
penHighLight.DeleteObject();
}
}
INT CReportCtrl::DrawRowBkgnd(CDC *pDC, CRect rect, LPRVITEM lprvi, INT iRow, BOOL bAlternate)
{
if (lprvi->nMask & RVIM_CATEGORY)
{
BYTE r = GetRValue(m_cr3DFace);
BYTE g = GetGValue(m_cr3DFace);
BYTE b = GetBValue(m_cr3DFace);
BYTE rb = GetRValue(m_crBackground);
BYTE gb = GetGValue(m_crBackground);
BYTE bb = GetBValue(m_crBackground);
int w = GetHeaderCtrl()->GetWidth();
--rect.top;
for (int i = 0, k = 64; i <= k; i++)
{
rect.left = max(0, int(float(w) / k * i) - 1);
rect.right = min(w, 1 + int(rect.left + float(w) / k));
#define rgb(f, b) int(float(f) + float(b - f) / (k / 2) * (i < (k / 2) ? ((k / 2) - i) : (i - (k / 2))))
pDC->FillSolidRect(rect, RGB(rgb(r, rb), rgb(g, gb), rgb(b, bb)));
#undef rgb
}
rect.left = 0;
if (m_iCategoryImage >= 0)
{
RVITEM rvi;
rvi.nMask = RVIM_IMAGE;
rvi.iImage = m_iCategoryImage + (lprvi->nState & RVIS_HIDDEN ? 1 : 0);
rect.right += m_sizeImage.cx;
rect.left += DrawImage(pDC, rect, &rvi);
}
else
{
CPen penErase(PS_SOLID, 1, m_cr3DDkShadow);
CPen *pPen = pDC->SelectObject(&penErase);
CBrush brushFace(m_cr3DFace);
CBrush *pBrush = pDC->SelectObject(&brushFace);
CRect r3dRect(CPoint(rect.left + 2, rect.top), CSize(m_iDefaultHeight, m_iDefaultHeight));
r3dRect.DeflateRect(2, 2);
pDC->RoundRect(r3dRect, CPoint(2, 2));
r3dRect.DeflateRect(1, 1);
pDC->FillSolidRect(CRect(r3dRect.left + 1, r3dRect.CenterPoint().y + 2, r3dRect.right - 3,
r3dRect.CenterPoint().y - 1), m_crBackground);
if (lprvi->nState & RVIS_HIDDEN)
{
pDC->FillSolidRect(CRect(r3dRect.CenterPoint().x - 2, r3dRect.top + 2, r3dRect.CenterPoint().x + 1,
r3dRect.bottom - 1), m_crBackground);
}
pDC->FillSolidRect(CRect(r3dRect.left + 2, r3dRect.CenterPoint().y - 1, r3dRect.right - 2,
r3dRect.CenterPoint().y + 1), m_cr3DShadow);
if (lprvi->nState & RVIS_HIDDEN)
{
pDC->FillSolidRect(CRect(r3dRect.CenterPoint().x - 1, r3dRect.top + 2, r3dRect.CenterPoint().x + 1,
r3dRect.bottom - 2), m_cr3DShadow);
}
rect.left += m_iDefaultHeight + 2;
pDC->SelectObject(pPen);
pDC->SelectObject(pBrush);
}
CFlatHeaderCtrl *hc = GetHeaderCtrl();
rect.right = rect.left + hc ? hc->GetWidth() : 0;
}
else
{
COLORREF crBackground = m_dwStyle & WS_DISABLED ? m_cr3DFace : m_crBackground;
COLORREF crItem;
if (lprvi->iBkColor >= 0)
crItem = m_arrayColors[lprvi->iBkColor];
else
crItem = (m_dwStyle & RVS_SHOWCOLORALTERNATE && bAlternate) ? m_arrayColors[0] : crBackground;
if (lprvi->nState & RVIS_SELECTED)
{
if (m_bFocus)
crItem = m_crBkSelected;
else if (m_dwStyle & RVS_SHOWSELALWAYS)
crItem = m_crBkSelectedNoFocus;
}
if (m_bitmap.m_hObject == NULL || crItem != crBackground || iRow == RVI_EDIT)
{
pDC->FillSolidRect(rect, crItem);
pDC->SetBkColor(crItem);
}
}
return rect.left;
}
void CReportCtrl::DrawItem(CDC *pDC, CRect rect, LPRVITEM lprvi)
{
BOOL bResult = FALSE;
if (lprvi->nState & RVIS_OWNERDRAW)
{
NMRVITEMDRAW nmrvid;
nmrvid.hdr.hwndFrom = GetSafeHwnd();
nmrvid.hdr.idFrom = GetDlgCtrlID();
nmrvid.hdr.code = RVN_ITEMDRAW;
nmrvid.lprvi = lprvi;
nmrvid.hDC = pDC->GetSafeHdc();
nmrvid.rect = rect;
bResult = Notify((LPNMREPORTVIEW) & nmrvid);
}
if (!bResult)
{
INT iWidth = DrawImage(pDC, rect, lprvi);
rect.left += iWidth ? iWidth + m_iSpacing : 0;
if (lprvi->nMask & RVIM_IMAGE && !iWidth)
return;
iWidth = DrawCheck(pDC, rect, lprvi);
rect.left += iWidth ? iWidth + m_iSpacing : 0;
if (lprvi->nMask & RVIM_CHECK && !iWidth)
return;
DrawText(pDC, rect, lprvi);
}
}
INT CReportCtrl::DrawTreeBox(CDC *pDC, CRect rectBox, BOOL bOpen)
{
if (rectBox.right < rectBox.left)
return rectBox.right;
rectBox.right = rectBox.left + rectBox.Height();
CPoint point = rectBox.CenterPoint();
CRect rect;
rect.SetRect(point, point);
rect.InflateRect(4, 4, 5, 5);
pDC->FillSolidRect(rect, m_cr3DShadow);
rect.DeflateRect(1, 1);
pDC->FillSolidRect(rect, m_crBackground);
CPen pen(PS_SOLID, 1, m_crText);
CPen *pPen = pDC->SelectObject(&pen);
pDC->MoveTo(point.x - 2, point.y);
pDC->LineTo(point.x + 3, point.y);
if (!bOpen)
{
pDC->MoveTo(point.x, point.y - 2);
pDC->LineTo(point.x, point.y + 3);
}
pDC->SelectObject(pPen);
return rectBox.right;
}
INT CReportCtrl::DrawImage(CDC *pDC, CRect rect, LPRVITEM lprvi)
{
CImageList *pImageList = GetImageList();
INT iWidth = 0;
if (lprvi->nMask & RVIM_IMAGE)
{
ASSERT(pImageList);
ASSERT(lprvi->iImage >= 0 && lprvi->iImage < pImageList->GetImageCount());
if (rect.Width() > 0)
{
POINT point;
UINT nStyle = ILD_NORMAL;
COLORREF crColor = 0;
point.y = rect.CenterPoint().y - (m_sizeImage.cy >> 1) + 1;
point.x = rect.left;
switch (m_nBlendStyle)
{
case RVP_BLEND_FOCUS:
nStyle = lprvi->nState & RVIS_FOCUSED ? ILD_FOCUS : ILD_NORMAL;
crColor = m_crBackground;
break;
case RVP_BLEND_SELECT:
nStyle = lprvi->nState & RVIS_SELECTED ? ILD_SELECTED : ILD_NORMAL;
crColor = pDC->GetBkColor();
break;
case RVP_BLEND_ALL:
nStyle = lprvi->nState & RVIS_FOCUSED ? ILD_FOCUS : ILD_NORMAL;
crColor = m_crBackground;
if (lprvi->nState & RVIS_SELECTED)
{
nStyle = lprvi->nState & RVIS_SELECTED ? ILD_SELECTED : ILD_NORMAL;
crColor = pDC->GetBkColor();
}
break;
default:
if (lprvi->nState & RVIS_BLENDMASK)
{
nStyle = lprvi->nState & RVIS_BLEND25 ? ILD_BLEND25 : ILD_BLEND50;
crColor = m_crBlendColor;
}
break;
}
SIZE size;
size.cx = rect.Width() < m_sizeImage.cx ? rect.Width() : m_sizeImage.cx;
size.cy = m_sizeImage.cy;
pImageList->DrawIndirect(pDC, lprvi->iImage, point, size, CPoint(0, 0), nStyle, SRCCOPY, CLR_DEFAULT,
crColor);
if (lprvi->nMask & RVIM_OVERLAY)
{
pImageList->DrawIndirect(pDC, lprvi->iOverlay, point, size, CPoint(0, 0), nStyle, SRCCOPY, CLR_DEFAULT,
crColor);
}
iWidth = m_sizeImage.cx;
}
}
else
iWidth = m_arraySubItems[lprvi->iSubItem].nFormat & RVCF_SUBITEM_IMAGE ? m_sizeImage.cx : 0;
return iWidth;
}
INT CReportCtrl::DrawCheck(CDC *pDC, CRect rect, LPRVITEM lprvi)
{
INT iWidth = 0;
if (lprvi->nMask & RVIM_CHECK)
{
if (rect.Width() > m_sizeCheck.cx)
{
if (m_pCheckList == NULL)
{
rect.top = rect.CenterPoint().y - (m_sizeCheck.cy >> 1);
rect.left = rect.left;
rect.bottom = rect.top + m_sizeCheck.cx;
rect.right = rect.left + m_sizeCheck.cy;
UINT nState = m_nFrameControlStyle;
BOOL bDisable = FALSE;
switch (lprvi->iCheck)
{
case 2:
bDisable = TRUE;
// Intentional
case 0:
nState |= DFCS_BUTTONCHECK;
break;
case 3:
bDisable = TRUE;
// Intentional
case 1:
nState |= DFCS_BUTTONCHECK | DFCS_CHECKED;
break;
case 6:
bDisable = TRUE;
// Intentional
case 4:
nState |= DFCS_BUTTONRADIO;
break;
case 7:
bDisable = TRUE;
// Intentional
case 5:
nState |= DFCS_BUTTONRADIO | DFCS_CHECKED;
break;
}
pDC->DrawFrameControl(rect, DFC_BUTTON, nState);
CPen pen(PS_SOLID, 2, RGB(0xFF, 0, 0));
CPen *pPen = pDC->SelectObject(&pen);
if (bDisable == TRUE)
{
rect.DeflateRect(1, 1);
if (lprvi->iCheck >= 4)
rect.OffsetRect(1, 0);
pDC->MoveTo(rect.left, rect.top);
pDC->LineTo(rect.right - 1, rect.bottom - 1);
pDC->MoveTo(rect.right - 1, rect.top);
pDC->LineTo(rect.left, rect.bottom - 1);
}
pDC->SelectObject(pPen);
}
else
{
POINT point;
point.y = rect.CenterPoint().y - (m_sizeCheck.cy >> 1) + 1;
point.x = rect.left;
SIZE size;
size.cx = rect.Width() < m_sizeCheck.cx ? rect.Width() : m_sizeCheck.cx;
size.cy = m_sizeCheck.cy;
m_pCheckList->DrawIndirect(pDC, lprvi->iCheck, point, size, CPoint(0, 0));
}
iWidth = m_sizeCheck.cx;
}
}
else
iWidth = m_arraySubItems[lprvi->iSubItem].nFormat & RVCF_SUBITEM_CHECK ? m_sizeCheck.cx : 0;
return iWidth;
}
INT CReportCtrl::DrawText(CDC *pDC, CRect rect, LPRVITEM lprvi)
{
CSize size;
CString str = lprvi->lpszText;
if (rect.Width() > 0 && lprvi->nMask & RVIM_TEXT)
{
INT iExtra = 0;
CFont *pCurrentFont = NULL;
if (lprvi->nMask & RVIM_CATEGORY)
{
if (m_arraySubItems[lprvi->iSubItem].nFormat & RVCF_EX_HASDATE)
{
str = CCategoryTime(lprvi->lpszText).NameCategory(m_cBaseTime, m_bEarlier & m_bLater);
if (str.IsEmpty())
str = lprvi->lpszText;
}
pCurrentFont = pDC->SelectObject(&m_fontBold);
iExtra = pDC->SetTextCharacterExtra(1);
}
size = pDC->GetTextExtent(lprvi->lpszText);
switch (m_arraySubItems[lprvi->iSubItem].nFormat & HDF_JUSTIFYMASK)
{
case HDF_LEFT:
case HDF_LEFT | HDF_RTLREADING:
pDC->DrawText(str, -1, rect, DT_LEFT | DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
break;
case HDF_CENTER:
case HDF_CENTER | HDF_RTLREADING:
pDC->DrawText(str, -1, rect, DT_CENTER | DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
break;
case HDF_RIGHT:
case HDF_RIGHT | HDF_RTLREADING:
pDC->DrawText(str, -1, rect, DT_RIGHT | DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
break;
}
if (lprvi->nMask & RVIM_CATEGORY)
{
pDC->SetTextCharacterExtra(iExtra);
pDC->SelectObject(pCurrentFont);
}
}
size.cx = rect.Width() > size.cx ? size.cx : rect.Width();
return size.cx > 0 ? size.cx : 0;
}
BOOL CReportCtrl::DrawBkgnd(CDC *pDC, CRect rect, COLORREF crBackground)
{
if (m_bitmap.m_hObject != NULL)
{
CDC dc;
CRgn rgnBitmap;
CRect rectBitmap;
dc.CreateCompatibleDC(pDC);
dc.SelectObject(&m_bitmap);
rect.IntersectRect(rect, m_rectReport);
INT iHPos = GetScrollPos32(SB_HORZ);
rectBitmap.top = rect.top - rect.top % m_sizeBitmap.cy;
rectBitmap.bottom = rect.top + m_sizeBitmap.cy;
rectBitmap.left = rect.left - (rect.left + iHPos) % m_sizeBitmap.cx;
rectBitmap.right = rect.left + m_sizeBitmap.cx;
INT x, y;
for (x = rectBitmap.left; x < rect.right; x += m_sizeBitmap.cx)
{
for (y = rectBitmap.top; y < rect.bottom; y += m_sizeBitmap.cy)
pDC->BitBlt(x, y, m_sizeBitmap.cx, m_sizeBitmap.cy, &dc, 0, 0, SRCCOPY);
}
return TRUE;
}
pDC->FillSolidRect(rect, crBackground);
return FALSE;
}
BOOL CReportCtrl::BeginEdit(INT iRow, INT iColumn, UINT nKey)
{
if (iRow == RVI_INVALID || iColumn < 0)
return FALSE;
if (iRow == RVI_EDIT && !(m_dwStyle & RVS_SHOWEDITROW))
return FALSE;
TCHAR szText[REPORTCTRL_MAX_TEXT];
m_iEditItem = GetItemForRow(iRow);
m_iEditSubItem = GetSubItemFromColumn(iColumn);
// Make sure that the item to edit actually exists
ASSERT(m_iEditItem != RVI_INVALID && m_iEditSubItem != -1);
RVITEM rvi;
rvi.iItem = m_iEditItem;
rvi.iSubItem = m_iEditSubItem;
rvi.nMask = RVIM_TEXT | RVIM_STATE | RVIM_LPARAM;
rvi.lpszText = szText;
rvi.iTextMax = REPORTCTRL_MAX_TEXT;
GetItem(&rvi);
if (rvi.nState & RVIS_READONLY)
return FALSE;
NMRVITEMEDIT nmrvie;
memset(&nmrvie, 0, sizeof(nmrvie));
nmrvie.hdr.hwndFrom = GetSafeHwnd();
nmrvie.hdr.idFrom = GetDlgCtrlID();
nmrvie.hdr.code = RVN_BEGINITEMEDIT;
nmrvie.iItem = m_iEditItem;
nmrvie.iSubItem = m_iEditSubItem;
nmrvie.hWnd = NULL;
if (!GetItemRect(m_iEditItem, m_iEditSubItem, &nmrvie.rect))
return FALSE;
nmrvie.nKey = nKey;
if (rvi.nMask & RVIM_TEXT)
nmrvie.lpszText = szText;
nmrvie.lParam = rvi.lParam;
BOOL bResult = FALSE;
if (GetParent() != NULL)
bResult = Notify((LPNMREPORTVIEW) & nmrvie);
if (!bResult)
{
if (!(rvi.nMask & RVIM_TEXT))
return FALSE;
if (!GetItemRect(m_iEditItem, m_iEditSubItem, &nmrvie.rect, RVIR_TEXT))
return FALSE;
nmrvie.rect.top += 2;
CReportEditCtrl *pWnd = new CReportEditCtrl(m_iEditItem, m_iEditSubItem, nmrvie.bButton);
if (pWnd == NULL)
return FALSE;
if (!pWnd->Create(WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, nmrvie.rect, this, 0))
{
delete pWnd;
return FALSE;
}
pWnd->SetFont(rvi.nMask & RVIM_STATE && rvi.nState & RVIS_BOLD ? &m_fontBold : &m_font);
pWnd->SetWindowText(szText);
pWnd->BeginEdit(nKey);
m_hEditWnd = pWnd->GetSafeHwnd();
}
else
{
if (nmrvie.hWnd == NULL)
return FALSE;
m_hEditWnd = nmrvie.hWnd;
}
if (m_hEditWnd != NULL)
::SetFocus(m_hEditWnd);
return TRUE;
}
void CReportCtrl::EndEdit(BOOL bUpdate, LPNMRVITEMEDIT lpnmrvie)
{
if (!(m_dwStyle & RVS_OWNERDATA) && bUpdate)
{
if (lpnmrvie == NULL)
{
TCHAR szText[REPORTCTRL_MAX_TEXT];
INT i = ::GetWindowText(m_hEditWnd, szText, REPORTCTRL_MAX_TEXT - 1);
szText[REPORTCTRL_MAX_TEXT - 1] = 0;
if (i || !::GetLastError())
SetItemText(m_iEditItem, m_iEditSubItem, szText);
}
else
SetItemText(m_iEditItem, m_iEditSubItem, lpnmrvie->lpszText);
}
if (IsWindow(m_hEditWnd))
::PostMessage(m_hEditWnd, WM_CLOSE, 0, 0);
m_hEditWnd = NULL;
CWnd *pWnd = GetFocus();
if (pWnd)
{
if (pWnd->GetSafeHwnd() != m_hWnd)
m_bFocus = FALSE;
}
RedrawRows(m_iFocusRow);
}
void CReportCtrl::ShellSort(INT iSubItem, INT iLow, INT iHigh, BOOL bAscending)
{
int g, i, j;
int iRows = iHigh - iLow + 1;
for (g = iRows / 2; g > 0; g /= 2)
{
for (i = g; i < iRows; ++i)
{
for (j = (i - g) + iLow; j >= iLow; j -= g)
{
if (bAscending)
{
if (CompareItems(m_arrayRows[j], iSubItem, m_arrayRows[j + g], iSubItem) <= 0)
continue;
}
else if (CompareItems(m_arrayRows[j], iSubItem, m_arrayRows[j + g], iSubItem) >= 0)
continue;
INT iTemp;
iTemp = m_arrayRows[j + g];
m_arrayRows[j + g] = m_arrayRows[j];
m_arrayRows[j] = iTemp;
}
}
}
}
void CReportCtrl::QuickSort(INT iSubItem, INT iLow, INT iHigh, BOOL bAscending)
{
INT i, j;
i = iHigh;
j = iLow;
INT iRow = m_arrayRows[((INT) ((iLow + iHigh) / 2))];
do
{
if (bAscending)
{
while (CompareItems(m_arrayRows[j], iSubItem, iRow, iSubItem) < 0)
j++;
while (CompareItems(m_arrayRows[i], iSubItem, iRow, iSubItem) > 0)
i--;
}
else
{
while (CompareItems(m_arrayRows[j], iSubItem, iRow, iSubItem) > 0)
j++;
while (CompareItems(m_arrayRows[i], iSubItem, iRow, iSubItem) < 0)
i--;
}
if (i >= j)
{
if (i != j)
{
INT iTemp;
iTemp = m_arrayRows[i];
m_arrayRows[i] = m_arrayRows[j];
m_arrayRows[j] = iTemp;
}
i--;
j++;
}
} while (j <= i);
if (iLow < i)
QuickSort(iSubItem, iLow, i, bAscending);
if (j < iHigh)
QuickSort(iSubItem, j, iHigh, bAscending);
}
void CReportCtrl::TreeSort(INT iSubItem, LPTREEITEM lptiParent, BOOL bAscending, BOOL bTraverse)
{
TREEITEM ti;
LPTREEITEM lpti, lptiSibling = lptiParent->lptiChildren;
while (lptiSibling != NULL)
{
lpti = lptiSibling;
lptiSibling = lptiSibling->lptiSibling;
lpti->lptiSibling = NULL;
if (bTraverse && lpti->lptiChildren != NULL)
TreeSort(iSubItem, lpti, bAscending);
InsertTree(iSubItem, &ti, (LPTREEITEM) RVTI_SORT, lpti, bAscending);
}
lptiParent->lptiChildren = ti.lptiChildren;
}
// CReportCtrl message handlers
BOOL CReportCtrl::OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message)
{
SetCursor(::LoadCursor(NULL, IDC_ARROW));
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
void CReportCtrl::OnSetFocus(CWnd *pOldWnd)
{
CWnd::OnSetFocus(pOldWnd);
m_bFocus = TRUE;
Invalidate();
}
void CReportCtrl::OnKillFocus(CWnd *pNewWnd)
{
CWnd::OnKillFocus(pNewWnd);
if (CWnd::IsChild(pNewWnd) == FALSE)
m_bFocus = FALSE;
if (m_bFocus == FALSE && pNewWnd != NULL && pNewWnd->GetSafeHwnd() != m_wndTip.GetSafeHwnd())
m_wndTip.Hide();
Invalidate();
}
void CReportCtrl::OnSysColorChange()
{
CWnd::OnSysColorChange();
GetSysColors();
}
void CReportCtrl::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
CWnd::OnSettingChange(uFlags, lpszSection);
m_nRowsPerWheelNotch = GetMouseScrollLines();
}
BOOL CReportCtrl::OnQueryNewPalette()
{
Invalidate();
return CWnd::OnQueryNewPalette();
}
void CReportCtrl::OnPaletteChanged(CWnd *pFocusWnd)
{
CWnd::OnPaletteChanged(pFocusWnd);
if (pFocusWnd->GetSafeHwnd() != GetSafeHwnd())
Invalidate();
}
void CReportCtrl::OnSize(UINT nType, int cx, int cy)
{
if (m_hEditWnd != NULL)
EndEdit();
CWnd::OnSize(nType, cx, cy);
Layout();
}
void CReportCtrl::OnWindowPosChanging(WINDOWPOS FAR *lpwndpos)
{
CWnd::OnWindowPosChanging(lpwndpos);
if (!m_bHorzScrollBar)
{
ShowScrollBar(SB_HORZ, FALSE);
ModifyStyle(WS_HSCROLL, 0, SWP_DRAWFRAME);
}
}
LRESULT CReportCtrl::OnGetFont(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
return (LRESULT) m_font.m_hObject;
}
LRESULT CReportCtrl::OnSetFont(WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = Default();
CFont *pFont = CFont::FromHandle((HFONT) wParam);
if (pFont)
{
LOGFONT lf;
pFont->GetLogFont(&lf);
m_font.DeleteObject();
m_font.CreateFontIndirect(&lf);
lf.lfWeight = FW_BOLD;
m_fontBold.DeleteObject();
m_fontBold.CreateFontIndirect(&lf);
}
CDC *pDC = GetDC();
if (pDC)
{
CFont *pFont = pDC->SelectObject(&m_font);
TEXTMETRIC tm;
pDC->GetTextMetrics(&tm);
pDC->SelectObject(pFont);
ReleaseDC(pDC);
INT iDefaultHeight;
iDefaultHeight = tm.tmHeight + tm.tmExternalLeading + 2;
iDefaultHeight += m_dwStyle & RVS_SHOWHGRID ? 1 : 0;
if (m_pImageList != NULL)
m_iDefaultHeight = max(iDefaultHeight, m_sizeImage.cy + 1);
else
m_iDefaultHeight = iDefaultHeight;
m_sizeCheck.cx = m_iDefaultHeight - 2;
m_sizeCheck.cy = m_iDefaultHeight - 2;
}
if (::IsWindow(GetSafeHwnd()) && lParam)
Layout();
return lResult;
}
BOOL CReportCtrl::OnEraseBkgnd(CDC *pDC)
{
UNREFERENCED_PARAMETER(pDC);
return TRUE;
}
void CReportCtrl::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR *lpncsp)
{
UNREFERENCED_PARAMETER(bCalcValidRects);
LONG lStyle = ::GetWindowLong(m_hWnd, GWL_STYLE);
if (lStyle & WS_BORDER)
::InflateRect(&lpncsp->rgrc[0], -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE));
Default();
}
void CReportCtrl::OnNcPaint()
{
Default();
LONG lStyle = ::GetWindowLong(m_hWnd, GWL_STYLE);
if (lStyle & WS_BORDER)
{
CWindowDC dc(this);
CRect rcBounds;
GetWindowRect(rcBounds);
ScreenToClient(rcBounds);
rcBounds.OffsetRect(-rcBounds.left, -rcBounds.top);
dc.DrawEdge(rcBounds, EDGE_SUNKEN, BF_RECT);
}
}
void CReportCtrl::OnPaint()
{
if (m_bWidthChanged)
{
Notify(RVN_NEWWIDTH, -1, NULL);
m_bWidthChanged = false;
}
if (m_wndCategoryHdr)
m_wndCategoryHdr->MatchHeaders();
CPaintDC dc(this);
CPalette *pPalette = NULL;
if (dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
{
pPalette = dc.SelectPalette(&m_palette, FALSE);
dc.RealizePalette();
}
CRect rExclude(m_rectHeader);
rExclude.right = GetHeaderCtrl()->GetWidth() - rExclude.left;
rExclude.top = 0;
dc.ExcludeClipRect(&rExclude);
if (m_bDoubleBuffer)
{
CMemDC MemDC(&dc);
DrawCtrl(&MemDC);
if (m_hEditWnd != NULL)
{
RECT rect;
::GetWindowRect(m_hEditWnd, &rect);
ScreenToClient(&rect);
dc.ExcludeClipRect(&rect);
}
}
else
DrawCtrl(&dc);
if (pPalette && dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
dc.SelectPalette(pPalette, FALSE);
}
void CReportCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
{
UNREFERENCED_PARAMETER(pScrollBar);
INT iScrollPos = GetScrollPos32(SB_HORZ);
if (m_hEditWnd != NULL)
EndEdit();
switch (nSBCode)
{
case SB_LINERIGHT:
iScrollPos += min(m_iVirtualWidth - (m_rectReport.Width() + iScrollPos), m_rectReport.Width() >> 3);
break;
case SB_LINELEFT:
iScrollPos -= min(iScrollPos, m_rectReport.Width() >> 3);
break;
case SB_PAGERIGHT:
iScrollPos = min(m_iVirtualWidth, iScrollPos + m_rectReport.Width());
break;
case SB_PAGELEFT:
iScrollPos = max(0, iScrollPos - m_rectReport.Width());
break;
case SB_THUMBPOSITION:
iScrollPos = nPos;
break;
case SB_THUMBTRACK:
iScrollPos = GetScrollPos32(SB_HORZ, true);
if (!iScrollPos)
return;
break;
case SB_RIGHT:
iScrollPos = m_iVirtualWidth;
break;
case SB_LEFT:
iScrollPos = 0;
break;
default:
return;
}
ScrollWindow(SB_HORZ, iScrollPos);
}
void CReportCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
{
UNREFERENCED_PARAMETER(pScrollBar);
UNREFERENCED_PARAMETER(nPos);
INT iFirst = GetScrollPos32(SB_VERT), iLast;
INT iItems = GetVisibleRows(TRUE, &iFirst, &iLast);
if (m_hEditWnd != NULL)
EndEdit();
switch (nSBCode)
{
case SB_LINEUP:
if (iFirst > 0)
iFirst--;
break;
case SB_LINEDOWN:
if (iFirst + iItems < m_iVirtualHeight)
iFirst++;
break;
case SB_PAGEUP:
GetVisibleRows(TRUE, &iLast, &iFirst, TRUE);
iFirst = iLast - 1;
iFirst = iFirst < 0 ? 0 : iFirst;
break;
case SB_PAGEDOWN:
iFirst += iItems;
GetVisibleRows(TRUE, &iFirst, &iLast);
GetVisibleRows(TRUE, &iFirst, &iLast, TRUE);
break;
case SB_THUMBPOSITION:
GetVisibleRows(TRUE, &iFirst, &iLast);
GetVisibleRows(TRUE, &iFirst, &iLast, TRUE);
break;
case SB_THUMBTRACK:
SetScrollPos32(SB_VERT, GetScrollPos32(SB_VERT, TRUE));
iFirst = GetScrollPos32(SB_VERT);
GetVisibleRows(TRUE, &iFirst, &iLast);
GetVisibleRows(TRUE, &iFirst, &iLast, TRUE);
break;
case SB_TOP:
iFirst = 0;
break;
case SB_BOTTOM:
iLast = m_iVirtualHeight - 1;
GetVisibleRows(TRUE, &iFirst, &iLast, TRUE);
break;
default:
return;
}
ScrollWindow(SB_VERT, iFirst);
}
void CReportCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
RVHITTESTINFO rvhti;
rvhti.point = point;
HitTest(&rvhti);
SetFocus();
if (!(m_dwStyle & RVS_OWNERDATA))
{
if (rvhti.nFlags & RVHT_ONCATITEM)
{
Notify(RVN_CATEGORYCLICK, nFlags, &rvhti);
return;
}
if (rvhti.nFlags & RVHT_ONCATIMAGE)
{
if (!Notify(RVN_CATEGORYCLICK, nFlags, &rvhti))
{
INT iNext = GetRowFromItem(rvhti.iItem);
while (iNext > 0 && !(GetState(iNext) & RVIS_CATEGORY))
--iNext;
UINT nState = RVIS_HIDDEN & (RVIS_HIDDEN ^ GetState(iNext));
SetState(iNext, nState, RVIS_HIDDEN);
MarkCategories();
ScrollWindow(SB_VERT, GetScrollPos32(SB_VERT));
Invalidate();
}
return;
}
}
if (AsTreeColumn() && rvhti.nFlags & RVHT_ONITEMTREEBOX)
{
if (!Notify(RVN_ITEMEXPANDING, rvhti.iItem, rvhti.iSubItem))
{
Expand((HTREEITEM) m_arrayItems[rvhti.iItem].lptiItem, RVE_TOGGLE);
Notify(RVN_ITEMEXPANDED, rvhti.iItem, rvhti.iSubItem);
return;
}
}
INT iFocusRow = m_iFocusRow, iFocusColumn = m_iFocusColumn;
INT iSubItem = GetSubItemFromColumn(rvhti.iColumn);
if (m_dwStyle & RVS_FOCUSSUBITEMS && iSubItem >= 0 && !(m_arraySubItems[iSubItem].nFormat & RVCF_SUBITEM_NOFOCUS))
m_iFocusColumn = rvhti.iColumn;
else
m_iFocusColumn = -1;
if (rvhti.iItem != RVI_INVALID)
{
INT iRow = GetRowFromItem(rvhti.iItem);
ASSERT(iRow != RVI_INVALID);
switch (nFlags & (MK_CONTROL | MK_SHIFT))
{
case MK_CONTROL:
SelectRows(iRow, iRow, TRUE, TRUE, TRUE);
m_iSelectRow = iRow;
break;
case MK_SHIFT:
SelectRows(iRow, m_iSelectRow, TRUE);
break;
case MK_CONTROL | MK_SHIFT:
SelectRows(iRow, m_iSelectRow, TRUE, TRUE);
m_iSelectRow = iRow;
break;
default:
SelectRows(iRow, iRow, TRUE);
m_iSelectRow = iRow;
if
(
m_iFocusRow == iFocusRow
&& m_iFocusColumn == iFocusColumn
&& (m_iFocusRow == RVI_EDIT || m_dwStyle & RVS_FOCUSSUBITEMS)
) BeginEdit(m_iFocusRow, m_iFocusColumn, VK_LBUTTON);
break;
}
}
if (Notify(RVN_ITEMCLICK, nFlags, &rvhti))
{
CWnd::OnLButtonDown(nFlags, point);
return;
}
}
void CReportCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
SetFocus();
RVHITTESTINFO rvhti;
rvhti.point = point;
HitTest(&rvhti);
if (rvhti.nFlags & (RVHT_ONCATIMAGE | RVHT_ONCATITEM))
{
Notify(RVN_CATEGORYRCLICK, nFlags, &rvhti);
return;
}
if (Notify(RVN_ITEMRCLICK, nFlags, &rvhti))
{
CWnd::OnRButtonDown(nFlags, point);
return;
}
if (rvhti.iItem != RVI_INVALID)
{
INT iRow = GetRowFromItem(rvhti.iItem);
ASSERT(iRow != RVI_INVALID);
switch (nFlags & (MK_CONTROL | MK_SHIFT))
{
case MK_CONTROL:
SelectRows(iRow, iRow, TRUE, TRUE, TRUE);
m_iSelectRow = iRow;
break;
case MK_SHIFT:
SelectRows(iRow, m_iSelectRow, TRUE);
break;
case MK_CONTROL | MK_SHIFT:
SelectRows(iRow, m_iSelectRow, TRUE, TRUE);
m_iSelectRow = iRow;
break;
default:
if (GetState(iRow) & RVIS_SELECTED)
SelectRows(iRow, iRow, TRUE, TRUE, FALSE);
else
SelectRows(iRow, iRow, TRUE, FALSE, TRUE);
m_iSelectRow = iRow;
break;
}
}
}
void CReportCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
RVHITTESTINFO rvhti;
rvhti.point = point;
HitTest(&rvhti);
if (rvhti.nFlags & (RVHT_ONCATITEM | RVHT_ONCATIMAGE))
return;
if (Notify(RVN_ITEMDBCLICK, nFlags, &rvhti))
{
CWnd::OnLButtonDblClk(nFlags, point);
return;
}
if (m_dwStyle & RVS_TREEVIEW)
{
if (rvhti.iItem >= RVI_FIRST && m_arrayItems[rvhti.iItem].lptiItem->lptiChildren != NULL)
{
Expand((HTREEITEM) m_arrayItems[rvhti.iItem].lptiItem, RVE_TOGGLE);
Notify(RVN_ITEMEXPANDED, rvhti.iItem, rvhti.iSubItem);
}
}
}
UINT CReportCtrl::OnGetDlgCode()
{
return DLGC_WANTARROWS;
}
void CReportCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
INT iFirst = GetScrollPos32(SB_VERT), iLast;
GetVisibleRows(TRUE, &iFirst, &iLast);
NMREPORTVIEW nmrv;
nmrv.hdr.hwndFrom = GetSafeHwnd();
nmrv.hdr.idFrom = GetDlgCtrlID();
nmrv.hdr.code = RVN_KEYDOWN;
nmrv.nKeys = nChar;
nmrv.nFlags = nFlags;
nmrv.iItem = GetItemForRow(m_iFocusRow);
nmrv.iSubItem = GetSubItemFromColumn(m_iFocusColumn);
if (nmrv.iItem != RVI_INVALID && (!(m_dwStyle & RVS_OWNERDATA)))
nmrv.lParam = GetItemStruct(nmrv.iItem, -1).lParam;
m_bProcessKey = FALSE;
if (!Notify(&nmrv))
{
switch (nChar)
{
case VK_SPACE:
SelectRows(m_iFocusRow, m_iFocusRow, TRUE, TRUE, IsCtrlDown() ? TRUE : FALSE);
return;
case VK_LEFT:
if (m_dwStyle & RVS_TREEVIEW && m_iFocusColumn == -1)
{
INT iFocusItem = GetItemForRow(m_iFocusRow);
if (iFocusItem >= RVI_FIRST)
{
LPTREEITEM lpti = m_arrayItems[iFocusItem].lptiItem;
if (lpti->lptiChildren != NULL && lpti->bOpen == TRUE)
{
Expand((HTREEITEM) lpti, RVE_COLLAPSE);
break;
}
else
{
if (lpti->lptiParent != &m_tiRoot)
{
lpti = lpti->lptiParent;
INT iFocusRow = GetRowFromItem(lpti->iItem);
ASSERT(iFocusRow != RVI_INVALID);
SelectRows(iFocusRow, iFocusRow);
EnsureVisible(lpti->iItem);
break;
}
else if (m_dwStyle & RVS_SHOWEDITROW)
{
SelectRows(RVI_EDIT, RVI_EDIT);
break;
}
}
}
}
if (m_dwStyle & RVS_FOCUSSUBITEMS)
{
if (m_iFocusColumn > 0)
{
m_iFocusColumn--;
while (m_iFocusColumn >= 0)
{
INT iSubItem = GetSubItemFromColumn(m_iFocusColumn);
if (iSubItem >= 0 && !(m_arraySubItems[iSubItem].nFormat & RVCF_SUBITEM_NOFOCUS))
break;
m_iFocusColumn--;
}
}
else
m_iFocusColumn = -1;
EnsureVisibleColumn(m_iFocusColumn);
iFirst = GetRowFromItem(m_iFocusRow);
RedrawRows(iFirst);
}
else
{
INT iScrollPos = GetScrollPos32(SB_HORZ);
INT iScroll = min(iScrollPos, m_rectReport.Width() >> 3);
ScrollWindow(SB_HORZ, iScrollPos - iScroll);
}
return;
case VK_RIGHT:
if (m_dwStyle & RVS_TREEVIEW && m_iFocusColumn == -1)
{
INT iFocusItem = GetItemForRow(m_iFocusRow);
if (iFocusItem >= RVI_FIRST)
{
LPTREEITEM lpti = m_arrayItems[iFocusItem].lptiItem;
if (lpti->lptiChildren != NULL)
{
if (lpti->bOpen == FALSE)
{
Expand((HTREEITEM) lpti, RVE_EXPAND);
break;
}
else
{
lpti = lpti->lptiChildren;
INT iFocusRow = GetRowFromItem(lpti->iItem);
ASSERT(iFocusRow != RVI_INVALID);
SelectRows(iFocusRow, iFocusRow);
EnsureVisible(lpti->iItem);
break;
}
}
}
}
if (m_dwStyle & RVS_FOCUSSUBITEMS)
{
if (m_iFocusColumn < m_arrayColumns.GetSize() - 1)
{
INT iFocusColumn = m_iFocusColumn;
m_iFocusColumn++;
while (m_iFocusColumn < m_arrayColumns.GetSize())
{
INT iSubItem = GetSubItemFromColumn(m_iFocusColumn);
if (iSubItem >= 0 && !(m_arraySubItems[iSubItem].nFormat & RVCF_SUBITEM_NOFOCUS))
break;
m_iFocusColumn++;
if (m_iFocusColumn == m_arrayColumns.GetSize())
{
m_iFocusColumn = iFocusColumn;
break;
}
}
}
else
m_iFocusColumn = m_arrayColumns.GetSize() - 1;
EnsureVisibleColumn(m_iFocusColumn);
iFirst = GetRowFromItem(m_iFocusRow);
RedrawRows(iFirst);
}
else
{
INT iScrollPos = GetScrollPos32(SB_HORZ);
INT iScroll = min(m_iVirtualWidth - (m_rectReport.Width() + iScrollPos), m_rectReport.Width() >> 3);
ScrollWindow(SB_HORZ, iScrollPos + iScroll);
}
return;
case VK_DOWN:
if (m_iFocusRow < m_iVirtualHeight - 1)
{
if (m_iFocusRow == RVI_INVALID && !(m_dwStyle & RVS_SHOWEDITROW) && m_iVirtualHeight > 0)
m_iFocusRow = RVI_EDIT;
if (IsCtrlDown())
{
SelectRows(m_iFocusRow + 1, m_iFocusRow + 1, FALSE);
m_iSelectRow = m_iFocusRow;
}
else if (!IsShiftDown())
{
SelectRows(m_iFocusRow + 1, m_iFocusRow + 1, TRUE);
m_iSelectRow = m_iFocusRow;
}
else
SelectRows(m_iFocusRow + 1, m_iSelectRow, TRUE);
EnsureVisible(GetItemForRow(m_iFocusRow));
}
return;
case VK_UP:
if ((m_dwStyle & RVS_SHOWEDITROW && m_iFocusRow > RVI_EDIT) || m_iFocusRow > RVI_FIRST)
{
if (IsCtrlDown())
{
SelectRows(m_iFocusRow - 1, m_iFocusRow - 1, FALSE);
m_iSelectRow = m_iFocusRow;
}
else if (!IsShiftDown())
{
SelectRows(m_iFocusRow - 1, m_iFocusRow - 1, TRUE);
m_iSelectRow = m_iFocusRow;
}
else
SelectRows(m_iFocusRow - 1, m_iSelectRow, TRUE);
EnsureVisible(GetItemForRow(m_iFocusRow));
}
return;
case VK_NEXT:
if (m_iFocusRow == iLast)
{
SendMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
iFirst = GetScrollPos32(SB_VERT);
GetVisibleRows(TRUE, &iFirst, &iLast);
}
if (IsCtrlDown())
{
SelectRows(iLast, iLast, FALSE);
m_iSelectRow = m_iFocusRow;
return;
}
else if (!IsShiftDown())
{
iFirst = iLast;
m_iSelectRow = iLast;
}
else
iFirst = m_iSelectRow;
SelectRows(iLast, iFirst, TRUE);
return;
case VK_PRIOR:
if (m_iFocusRow == iFirst)
{
SendMessage(WM_VSCROLL, SB_PAGEUP, 0);
iFirst = GetScrollPos32(SB_VERT);
}
iFirst = iLast < iFirst ? iLast : iFirst;
if (IsCtrlDown())
{
SelectRows(iFirst, iFirst, FALSE);
m_iSelectRow = m_iFocusRow;
return;
}
else if (!IsShiftDown())
{
iLast = iFirst;
m_iSelectRow = iFirst;
}
else
iLast = m_iSelectRow;
SelectRows(iFirst, iLast, TRUE);
return;
case VK_HOME:
if (m_iFocusRow > 0)
{
SendMessage(WM_VSCROLL, SB_TOP, 0);
if (!IsShiftDown())
m_iSelectRow = 0;
SelectRows(0, m_iSelectRow, IsCtrlDown() ? FALSE : TRUE);
}
break;
case VK_END:
if (m_iFocusRow < m_iVirtualHeight)
{
SendMessage(WM_VSCROLL, SB_BOTTOM, 0);
if (!IsShiftDown())
m_iSelectRow = m_iVirtualHeight - 1;
SelectRows(m_iVirtualHeight - 1, m_iSelectRow, IsCtrlDown() ? FALSE : TRUE);
}
break;
case VK_ESCAPE:
break;
default:
m_bProcessKey = !IsCtrlDown() ? TRUE : FALSE;
break;
}
}
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CReportCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (m_bProcessKey && m_iFocusColumn != -1 && (m_iFocusRow == RVI_EDIT || m_dwStyle & RVS_FOCUSSUBITEMS))
if (BeginEdit(m_iFocusRow, m_iFocusColumn, nChar))
return;
CWnd::OnChar(nChar, nRepCnt, nFlags);
}
BOOL CReportCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
int i;
// A m_nRowsPerWheelNotch value less than 0 indicates that the mouse wheel
// scrolls whole pages, not just lines.
if (m_nRowsPerWheelNotch == -1)
{
int iPagesScrolled = zDelta / 120;
if (iPagesScrolled > 0)
for (i = 0; i < iPagesScrolled; i++)
PostMessage(WM_VSCROLL, SB_PAGEUP, 0);
else
for (i = 0; i > iPagesScrolled; i--)
PostMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
}
else
{
int iRowsScrolled = (int) m_nRowsPerWheelNotch * zDelta / 120;
if (iRowsScrolled > 0)
for (i = 0; i < iRowsScrolled; i++)
PostMessage(WM_VSCROLL, SB_LINEUP, 0);
else
for (i = 0; i > iRowsScrolled; i--)
PostMessage(WM_VSCROLL, SB_LINEDOWN, 0);
}
return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}
void CReportCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
BOOL bHide = TRUE;
if (m_bFocus && !nFlags && m_dwStyle & RVS_EXPANDSUBITEMS && m_hEditWnd == NULL)
{
RVHITTESTINFO rvhti;
rvhti.point = point;
if (HitTest(&rvhti) != RVI_INVALID)
{
TCHAR szText[REPORTCTRL_MAX_TEXT];
RVITEM rvi;
rvi.iItem = rvhti.iItem;
rvi.iSubItem = rvhti.iSubItem;
rvi.lpszText = szText;
rvi.iTextMax = REPORTCTRL_MAX_TEXT;
rvi.nMask = RVIM_TEXT | RVIM_STATE;
GetItem(&rvi);
if (rvhti.nFlags & RVHT_ONITEMTEXT && rvi.nMask & RVIM_TEXT && _tcslen(rvi.lpszText))
{
CRect rect;
if (m_dwStyle & RVS_TREEVIEW && m_arrayItems[rvhti.iItem].lptiItem->iSubItem >= 0)
GetItemRect(rvhti.iItem, rvhti.iSubItem, rect, RVIR_TEXTNOCOLUMNS);
else
GetItemRect(rvhti.iItem, rvhti.iSubItem, rect, RVIR_TEXT);
GetExpandedItemText(&rvi);
bHide = !m_wndTip.Show(rect, rvi.lpszText, rvi.nState & RVIS_BOLD ? &m_fontBold : &m_font);
}
}
}
if (m_wndTip.IsWindowVisible() && bHide)
m_wndTip.Hide();
CWnd::OnMouseMove(nFlags, point);
}
void CReportCtrl::OnHdnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)
{
CFlatHeaderCtrl *hc;
if (pNMHDR->hwndFrom == (HWND) * GetHeaderCtrl())
hc = GetHeaderCtrl();
else
hc = GetCategoryHdrCtrl();
ASSERT(hc);
*pResult = hc->ItemChanging(pNMHDR);
}
void CReportCtrl::OnHdnItemChanged(NMHDR *pNMHDR, LRESULT *pResult)
{
LPHDITEM lphdi = (LPHDITEM) ((LPNMHEADER) pNMHDR)->pitem;
if (m_hEditWnd != NULL)
EndEdit();
if (pNMHDR->hwndFrom == (HWND) * GetHeaderCtrl())
{
HDITEM hdi;
hdi.mask = HDI_WIDTH | HDI_ORDER | HDI_LPARAM;
m_wndHeader.GetItem(((LPNMHEADER) pNMHDR)->iItem, &hdi);
if (lphdi->mask & HDI_FORMAT)
{
m_arraySubItems[hdi.lParam].nFormat = (m_arraySubItems[hdi.lParam].nFormat &~RVCF_JUSTIFYMASK) | (lphdi->fmt & HDF_JUSTIFYMASK);
}
if (lphdi->mask & HDI_WIDTH)
{
// AS: ref bug 14238 setting the minimal column width to 10 pixels
if (lphdi->cxy < FHDR_MIN_COL_WIDTH)
{
hdi.cxy = FHDR_MIN_COL_WIDTH;
lphdi->cxy = FHDR_MIN_COL_WIDTH;
m_wndHeader.SetItem(((LPNMHEADER) pNMHDR)->iItem, &hdi);
}
m_iVirtualWidth += lphdi->cxy - m_arraySubItems[hdi.lParam].iWidth;
ASSERT(m_iVirtualWidth >= 0);
m_arraySubItems[hdi.lParam].iWidth = lphdi->cxy;
}
if (m_wndHeader.m_bAutoSizing == FALSE)
ScrollWindow(SB_HORZ, GetScrollPos32(SB_HORZ));
Notify(RVN_LAYOUTCHANGED, RVI_INVALID, (INT) hdi.lParam);
}
else
{
CCategoryHdr *ac = GetCategoryHdrCtrl();
if (ac)
{
HDITEM hdi;
hdi.mask = HDI_WIDTH | HDI_ORDER | HDI_LPARAM;
ac->GetItem(((LPNMHEADER) pNMHDR)->iItem, &hdi);
if (lphdi->mask & HDI_WIDTH)
{
if (lphdi->cxy < FHDR_MIN_COL_WIDTH)
{
hdi.cxy = FHDR_MIN_COL_WIDTH;
lphdi->cxy = FHDR_MIN_COL_WIDTH;
ac->SetItem(((LPNMHEADER) pNMHDR)->iItem, &hdi);
}
}
}
}
m_bWidthChanged = TRUE;
*pResult = FALSE;
}
void CReportCtrl::OnHdnItemClick(NMHDR *pNMHDR, LRESULT *pResult)
{
if (m_hEditWnd != NULL)
EndEdit();
if (pNMHDR->hwndFrom == (HWND) * GetHeaderCtrl())
{
NMRVHEADER nmrvhdr;
if (!NotifyHdr(&nmrvhdr, RVN_HEADERCLICK, ((LPNMHEADER) pNMHDR)->iItem))
{
if (!(m_dwStyle & (RVS_NOSORT | RVS_OWNERDATA)))
{
BOOL bAscending;
INT iOld = m_wndHeader.GetSortColumn(&bAscending);
INT iNew = ((LPNMHEADER) pNMHDR)->iItem;
if (iNew == iOld)
bAscending = !bAscending;
else
bAscending = TRUE;
SortItems(nmrvhdr.iSubItem, bAscending);
}
}
}
*pResult = FALSE;
}
void CReportCtrl::OnHdnBeginDrag(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMHEADER pnmhdr = LPNMHEADER(pNMHDR);
if (m_hEditWnd != NULL)
EndEdit();
*pResult = pnmhdr->iItem == 0 && m_dwStyle & RVS_TREEVIEW ? TRUE : FALSE;
}
void CReportCtrl::OnHdnEndDrag(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMHEADER lpnmhdr = (LPNMHEADER) pNMHDR;
if (pNMHDR->hwndFrom == (HWND) * GetHeaderCtrl())
{
INT iDropResult = m_wndHeader.GetDropResult();
if (iDropResult == FHDR_DROPPED || iDropResult == FHDR_ONTARGET)
{
if (m_dwStyle & RVS_ALLOWCOLUMNREMOVAL)
DeactivateSubItem(lpnmhdr->pitem->lParam);
*pResult = TRUE;
}
else
{
m_bColumnsReordered = TRUE;
m_iIndentColumnPending = m_wndHeader.OrderToIndex(0);
Invalidate();
*pResult = FALSE;
}
}
}
void CReportCtrl::OnHdnDividerDblClick(NMHDR *pNMHDR, LRESULT *pResult)
{
if (m_hEditWnd != NULL)
EndEdit();
NMRVHEADER nmrvhdr;
if
(
!NotifyHdr(&nmrvhdr, RVN_DIVIDERDBLCLICK, ((LPNMHEADER) pNMHDR)->iItem)
&& !(m_dwStyle & RVS_OWNERDATA)
&& !(m_arraySubItems[nmrvhdr.iSubItem].nFormat & RVCF_EX_FIXEDWIDTH)
)
{
INT iItems = m_arrayItems.GetSize();
if (iItems > 0)
{
INT iWidth = 0;
RECT rect;
for (INT iItem = 0; iItem < iItems; iItem++)
{
if (MeasureItem(iItem, nmrvhdr.iSubItem, &rect))
{
INT cx = rect.right - rect.left;
iWidth = cx > iWidth ? cx : iWidth;
}
}
HDITEM hdi;
hdi.mask = HDI_WIDTH;
hdi.cxy = iWidth;
m_wndHeader.SetItem(((LPNMHEADER) pNMHDR)->iItem, &hdi);
}
}
*pResult = FALSE;
}
void CReportCtrl::OnRvnEndItemEdit(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMRVITEMEDIT lpnmrvie = (LPNMRVITEMEDIT) pNMHDR;
ASSERT(m_iEditItem == lpnmrvie->iItem);
ASSERT(m_iEditSubItem == lpnmrvie->iSubItem);
NMRVITEMEDIT nmrvie;
memset(&nmrvie, 0, sizeof(nmrvie));
nmrvie.hdr.hwndFrom = GetSafeHwnd();
nmrvie.hdr.idFrom = GetDlgCtrlID();
nmrvie.hdr.code = RVN_ENDITEMEDIT;
nmrvie.iItem = m_iEditItem;
nmrvie.iSubItem = m_iEditSubItem;
nmrvie.hWnd = lpnmrvie->hWnd;
nmrvie.nKey = lpnmrvie->nKey;
nmrvie.lpszText = lpnmrvie->lpszText;
nmrvie.lParam = lpnmrvie->lParam;
BOOL bResult = FALSE;
if (GetParent() != NULL)
bResult = Notify((LPNMREPORTVIEW) & nmrvie);
if (bResult)
EndEdit(FALSE);
else
EndEdit(nmrvie.nKey != VK_ESCAPE ? TRUE : FALSE, &nmrvie);
pResult = FALSE;
}
void CReportCtrl::SetCategoryHdr(CCategoryHdr *pWndCategoryHdr)
{
ASSERT(m_wndCategoryHdr == NULL);
m_wndCategoryHdr = pWndCategoryHdr == NULL ? new CCategoryHdr() : pWndCategoryHdr;
if (m_wndCategoryHdr)
{
DWORD dwStyle = HDS_HORZ | HDS_BUTTONS | HDS_FULLDRAG | CCS_TOP;
CFlatHeaderCtrl *hc = GetHeaderCtrl();
int iHeight = hc ? hc->GetHeight() : 20;
m_wndCategoryHdr->Create(dwStyle, CRect(0, 0, 100, iHeight + 2), this, 0);
m_wndCategoryHdr->OnInitialUpdate();
hc->ModifyProperty(FH_PROPERTY_ENABLEAUTOSIZE, hc->IsAutoSize());
m_wndCategoryHdr->Show();
}
}
void CReportCtrl::SetCategorySubItem(INT iSubItem, BOOL bCategoryAscending)
{
ASSERT(iSubItem < m_arraySubItems.GetSize());
ASSERT((m_dwStyle & RVS_TREEVIEW) || GetColumnFromSubItem(m_iCategorySubItem) < 0);
ASSERT(!(m_dwStyle & RVS_OWNERDATA));
if (m_dwStyle & RVS_OWNERDATA)
return;
if (m_iCategorySubItem == iSubItem && bCategoryAscending == m_bCategoryAscending)
return;
if (m_iCategorySubItem >= 0)
{
ActivateSubItem(m_iCategorySubItem, m_iCategoryColumn);
m_iCategorySubItem = -1;
m_iDefaultHeight--;
}
if (iSubItem >= 0)
{
m_iCategoryColumn = GetColumnFromSubItem(iSubItem);
ASSERT(m_iCategoryColumn >= 0);
m_iDefaultHeight++;
if (!(m_dwStyle & RVS_TREEVIEW))
DeactivateSubItem(iSubItem);
m_bCategoryAscending = bCategoryAscending;
m_iCategorySubItem = iSubItem;
if (m_wndCategoryHdr == NULL)
SetCategoryHdr();
}
if (m_wndCategoryHdr != NULL)
m_wndCategoryHdr->Show();
if (iSubItem >= 0)
ResortItems();
Layout();
Invalidate();
if (m_wndCategoryHdr != NULL)
m_wndCategoryHdr->NewCategory();
MarkCategories();
}
INT CReportCtrl::SetCategoryImage(INT image)
{
INT iImage = m_iCategoryImage;
m_iCategoryImage = image;
return iImage;
}
INT CReportCtrl::GetCategorySubItem(BOOL *pbCategoryAscending)
{
ASSERT((m_dwStyle & RVS_TREEVIEW) || GetColumnFromSubItem(m_iCategorySubItem) < 0);
if (pbCategoryAscending)
*pbCategoryAscending = m_bCategoryAscending;
return m_iCategorySubItem;
}
INT CReportCtrl::GetRootRow(INT iRow)
{
if (m_dwStyle & RVS_TREEVIEW)
{
while (iRow > 0)
{
HTREEITEM thisNode = GetItemHandle(GetItemForRow(iRow));
ASSERT(thisNode);
HTREEITEM parent = GetNextItem(thisNode, RVGN_PARENT);
if (parent == NULL)
break;
iRow--;
}
}
return iRow;
}
void CReportCtrl::ExpandCategories(UINT nState)
{
ASSERT(nState == RVIS_HIDDEN || nState == 0);
int iRows = m_arrayRows.GetSize();
for (INT iRow = 0; iRow < iRows; iRow++)
SetState(iRow, nState, RVIS_HIDDEN);
if (m_dwStyle & RVS_TREEVIEW)
BuildTree();
m_bUpdateItemMap = TRUE;
}
void CReportCtrl::MarkCategories(void)
{
if (m_iCategorySubItem < 0)
return;
INT iMaxRows = m_arrayRows.GetSize();
UINT iHide = 0;
m_iVirtualHeight = 0;
for (int iRow = 0, iCategory = 0; iRow < iMaxRows; iRow++)
{
int iState = RVIS_CATEGORY;
if (iRow != 0)
{
if (m_dwStyle & RVS_TREEVIEW)
{
HTREEITEM thisNode = GetItemHandle(GetItemForRow(iRow));
ASSERT(thisNode);
if (GetNextItem(thisNode, RVGN_PARENT) != NULL)
iState = 0;
}
}
if
(
iRow != 0
&& CompareItems(m_arrayRows[iRow], m_iCategorySubItem, m_arrayRows[iCategory], m_iCategorySubItem, TRUE) == 0
) iState = 0;
if (iState)
{
iCategory = iRow;
iHide = GetState(iRow) & RVIS_HIDDEN;
}
SetState(iRow, iState | iHide, RVIS_CATEGORY | RVIS_HIDDEN);
if (iState || !(GetState(iRow) & RVIS_HIDDEN))
++m_iVirtualHeight;
}
}
INT CReportCtrl::GetRowFromVirtualRow(INT iVirtualRow)
{
if (m_dwStyle & RVS_OWNERDATA)
return iVirtualRow;
INT iMaxRows = m_arrayRows.GetSize();
for (int iRow = 0; iVirtualRow > 0 && iRow + 1 < iMaxRows; iRow++)
{
int nState = m_arrayItems[m_arrayRows[iRow]].nState;
if (nState & RVIS_CATEGORY || 0 == (nState & RVIS_HIDDEN))
--iVirtualRow;
}
while (iRow + 1 < iMaxRows)
{
int nState = m_arrayItems[m_arrayRows[iRow]].nState;
if (nState & RVIS_CATEGORY)
break;
if (nState & RVIS_HIDDEN)
++iRow;
else
break;
}
return iRow;
}
INT CReportCtrl::GetVirtualRow(INT iRow)
{
if (m_dwStyle & RVS_OWNERDATA)
return iRow;
ASSERT(iRow < m_arrayRows.GetSize());
for (int iVirtualRow = 0, i = 0; i <= iRow; i++)
{
int nState = m_arrayItems[m_arrayRows[i]].nState;
if (nState & RVIS_CATEGORY || 0 == (nState & RVIS_HIDDEN))
++iVirtualRow;
}
return iVirtualRow;
}
INT CReportCtrl::GetCategoryForVirtualRow(INT iVirtualRow, BOOL bTop)
{
return GetCategoryForRow(GetRowFromVirtualRow(iVirtualRow), bTop);
}
INT CReportCtrl::GetCategoryForRow(INT iRow, BOOL bTop)
{
if (iRow >= 0 && m_iCategorySubItem >= 0)
{
if (bTop || iRow == 0)
return m_iCategorySubItem;
if (m_arrayItems[m_arrayRows[iRow]].nState & RVIS_CATEGORY)
return m_iCategorySubItem;
}
return -1;
}
// CReportSubItemListCtrl
CReportSubItemListCtrl::CReportSubItemListCtrl()
{
m_pReportCtrl = NULL;
m_iSubItem = -1;
m_pDragWnd = NULL;
}
CReportSubItemListCtrl::~CReportSubItemListCtrl()
{
}
BEGIN_MESSAGE_MAP(CReportSubItemListCtrl, CDragListBox)
//{{AFX_MSG_MAP(CReportSubItemListCtrl)
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CReportSubItemListCtrl attributes
BOOL CReportSubItemListCtrl::SetReportCtrl(CReportCtrl *pReportCtrl)
{
if (pReportCtrl == NULL)
return FALSE;
ASSERT_KINDOF(CReportCtrl, pReportCtrl);
m_pReportCtrl = pReportCtrl;
ResetContent();
return TRUE;
}
CReportCtrl *CReportSubItemListCtrl::GetReportCtrl()
{
return m_pReportCtrl;
}
// CReportSubItemListCtrl operations
BOOL CReportSubItemListCtrl::UpdateList()
{
INT iSubItem, iSubItems = m_pReportCtrl->m_arraySubItems.GetSize();
ResetContent();
for (iSubItem = 0; iSubItem < iSubItems; iSubItem++)
{
if (!m_pReportCtrl->IsActiveSubItem(iSubItem) && Include(iSubItem))
{
INT iItem = AddString(m_pReportCtrl->m_arraySubItems[iSubItem].strText);
SetItemData(iItem, iSubItem);
}
}
return TRUE;
}
BOOL CReportSubItemListCtrl::Include(INT iSubItem)
{
UNREFERENCED_PARAMETER(iSubItem);
return TRUE;
}
BOOL CReportSubItemListCtrl::Disable(INT iSubItem)
{
UNREFERENCED_PARAMETER(iSubItem);
return FALSE;
}
// CReportSubItemListCtrl message handlers
void CReportSubItemListCtrl::PreSubclassWindow()
{
CDragListBox::PreSubclassWindow();
SetItemHeight(0, GetItemHeight(0) + 2);
}
void CReportSubItemListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rcItem(lpDrawItemStruct->rcItem);
if (GetCount() > 0)
{
BOOL bDisable = Disable(lpDrawItemStruct->itemData);
if (GetExStyle() & WS_EX_STATICEDGE)
{
pDC->Draw3dRect(rcItem, m_pReportCtrl->m_cr3DHiLight, m_pReportCtrl->m_cr3DDkShadow);
rcItem.DeflateRect(1, 1);
pDC->FillSolidRect(rcItem, m_pReportCtrl->m_cr3DFace);
rcItem.DeflateRect(1, 1);
}
else
{
pDC->DrawFrameControl(rcItem, DFC_BUTTON, DFCS_BUTTONPUSH);
rcItem.DeflateRect(2, 2);
}
pDC->SetBkMode(TRANSPARENT);
if (lpDrawItemStruct->itemState & ODS_SELECTED)
{
pDC->FillSolidRect(rcItem, m_pReportCtrl->m_cr3DDkShadow);
pDC->SetTextColor(bDisable ? m_pReportCtrl->m_cr3DFace : m_pReportCtrl->m_cr3DHiLight);
}
else
{
if (bDisable)
{
pDC->SetTextColor(::GetSysColor(COLOR_3DHIGHLIGHT));
CRect rect = rcItem;
rect.left += 1;
rect.top += 1;
rect.right += 1;
rect.bottom += 1;
pDC->DrawText(m_pReportCtrl->m_arraySubItems[lpDrawItemStruct->itemData].strText, -1, rect,
DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | DT_VCENTER | DT_END_ELLIPSIS | DT_LEFT);
pDC->SetTextColor(::GetSysColor(COLOR_3DSHADOW));
}
else
pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
}
pDC->DrawText(m_pReportCtrl->m_arraySubItems[lpDrawItemStruct->itemData].strText, -1, rcItem,
DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | DT_VCENTER | DT_END_ELLIPSIS | DT_LEFT);
}
else
pDC->FillSolidRect(rcItem, ::GetSysColor(COLOR_WINDOW));
}
BOOL CReportSubItemListCtrl::BeginDrag(CPoint pt)
{
if (GetCount() <= 0)
return FALSE;
INT iItem = ItemFromPt(pt);
if (iItem >= 0)
{
m_iSubItem = GetItemData(iItem);
if (Disable(m_iSubItem))
{
m_iSubItem = -1;
return FALSE;
}
GetClientRect(m_rcDragWnd);
m_rcDragWnd.bottom = m_rcDragWnd.top + GetItemHeight(0);
_tcscpy(m_szSubItemText, m_pReportCtrl->m_arraySubItems[m_iSubItem].strText);
m_hdiSubItem.mask = HDI_WIDTH | HDI_TEXT | HDI_FORMAT;
m_hdiSubItem.cxy = m_rcDragWnd.Width();
m_hdiSubItem.pszText = m_szSubItemText;
m_hdiSubItem.cchTextMax = sizeof(m_szSubItemText);
m_hdiSubItem.fmt = HDF_STRING | HDF_LEFT;
m_pDragWnd = new CFHDragWnd;
if (m_pDragWnd)
m_pDragWnd->Create(m_rcDragWnd, &m_pReportCtrl->m_wndHeader, -2, &m_hdiSubItem);
GetWindowRect(m_rcDropTarget1);
m_pReportCtrl->m_wndHeader.GetWindowRect(m_rcDropTarget2);
}
m_iDropIndex = -1;
return TRUE;
}
UINT CReportSubItemListCtrl::Dragging(CPoint pt)
{
CPoint point = pt;
point.Offset(-(m_rcDragWnd.Width() >> 1), -(m_rcDragWnd.Height() >> 1));
if (m_pDragWnd != NULL)
m_pDragWnd->SetWindowPos(&wndTop, point.x, point.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
if (m_rcDropTarget1.PtInRect(pt))
return DL_MOVECURSOR;
m_iDropIndex = m_pReportCtrl->m_wndHeader.SendMessage(HDM_SETHOTDIVIDER, TRUE, MAKELONG(pt.x, pt.y));
if (m_rcDropTarget2.PtInRect(pt))
return DL_MOVECURSOR;
return DL_STOPCURSOR;
}
void CReportSubItemListCtrl::CancelDrag(CPoint pt)
{
UNREFERENCED_PARAMETER(pt);
m_pReportCtrl->m_wndHeader.SendMessage(HDM_SETHOTDIVIDER, FALSE, -1);
if (m_pDragWnd != NULL)
{
m_pDragWnd->DestroyWindow();
m_pDragWnd = NULL;
}
m_iSubItem = -1;
}
void CReportSubItemListCtrl::Dropped(INT iSrcIndex, CPoint pt)
{
UNREFERENCED_PARAMETER(pt);
UNREFERENCED_PARAMETER(iSrcIndex);
m_pReportCtrl->m_wndHeader.SendMessage(HDM_SETHOTDIVIDER, FALSE, -1);
if (m_pDragWnd != NULL)
{
m_pDragWnd->DestroyWindow();
m_pDragWnd = NULL;
}
if (m_iSubItem >= 0)
{
if (!m_pReportCtrl->GetActiveSubItemCount())
m_pReportCtrl->ActivateSubItem(m_iSubItem);
else if (m_iDropIndex >= 0)
m_pReportCtrl->ActivateSubItem(m_iSubItem, m_iDropIndex);
}
m_iSubItem = -1;
}
BOOL CReportSubItemListCtrl::OnEraseBkgnd(CDC *pDC)
{
if (!IsWindowEnabled())
{
CRect rect;
GetClientRect(rect);
pDC->FillSolidRect(rect, ::GetSysColor(COLOR_BTNFACE));
return TRUE;
}
else
return CDragListBox::OnEraseBkgnd(pDC);
}
// CReportEditCtrl
CReportEditCtrl::CReportEditCtrl(INT iItem, INT iSubItem, BOOL bButton)
{
m_bEndEdit = FALSE;
m_iItem = iItem;
m_iSubItem = iSubItem;
m_bButton = bButton;
m_nLastKey = VK_RETURN;
}
CReportEditCtrl::~CReportEditCtrl()
{
}
BOOL CReportEditCtrl::PreCreateWindow(CREATESTRUCT &cs)
{
if (m_bButton)
{
if (cs.cx > cs.cy + 10)
cs.cx -= cs.cy;
else
m_bButton = FALSE;
}
return CEdit::PreCreateWindow(cs);
}
int CReportEditCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CEdit::OnCreate(lpCreateStruct) == -1)
return -1;
if (m_bButton)
{
CRect rect
(
lpCreateStruct->x + lpCreateStruct->cx,
lpCreateStruct->y,
lpCreateStruct->x + lpCreateStruct->cx + lpCreateStruct->cy,
lpCreateStruct->y + lpCreateStruct->cy
);
CWnd *pParent = GetParent();
ASSERT(pParent != NULL);
if (!m_wndButton.Create(_T("..."), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, rect, pParent, 0))
return -1;
m_wndButton.SetFont(pParent->GetFont());
}
return 0;
}
void CReportEditCtrl::PostNcDestroy()
{
CEdit::PostNcDestroy();
delete this;
}
BEGIN_MESSAGE_MAP(CReportEditCtrl, CEdit)
//{{AFX_MSG_MAP(CReportEditCtrl)
ON_WM_KILLFOCUS()
ON_WM_GETDLGCODE()
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CReportEditCtrl operations
void CReportEditCtrl::BeginEdit(UINT nKey)
{
CString str;
GetWindowText(str);
switch (nKey)
{
case VK_LBUTTON:
case VK_RETURN:
SetSel(str.GetLength(), -1);
break;
case VK_BACK:
SetSel(str.GetLength(), -1);
SendMessage(WM_CHAR, nKey);
break;
case VK_TAB:
case VK_DOWN:
case VK_UP:
case VK_RIGHT:
case VK_LEFT:
case VK_NEXT:
case VK_PRIOR:
case VK_HOME:
case VK_SPACE:
case VK_END:
SetSel(0, -1);
break;
default:
SetSel(0, -1);
SendMessage(WM_CHAR, nKey);
break;
}
}
void CReportEditCtrl::EndEdit()
{
CString str;
if (m_bEndEdit)
return;
m_bEndEdit = TRUE;
GetWindowText(str);
NMRVITEMEDIT nmrvie;
memset(&nmrvie, 0, sizeof(nmrvie));
nmrvie.hdr.hwndFrom = GetSafeHwnd();
nmrvie.hdr.idFrom = GetDlgCtrlID();
nmrvie.hdr.code = RVN_ENDITEMEDIT;
nmrvie.iItem = m_iItem;
nmrvie.iSubItem = m_iSubItem;
nmrvie.bButton = m_bButton;
nmrvie.hWnd = nmrvie.hdr.hwndFrom;
nmrvie.nKey = m_nLastKey;
nmrvie.lpszText = (LPTSTR) (LPCTSTR) str;
CWnd *pWnd = GetOwner();
if (pWnd != NULL)
pWnd->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM) & nmrvie);
PostMessage(WM_CLOSE, 0, 0);
}
// CReportEditCtrl message handlers
void CReportEditCtrl::OnKillFocus(CWnd *pNewWnd)
{
CWnd::OnKillFocus(pNewWnd);
if (m_bButton)
{
if (pNewWnd->GetSafeHwnd() == m_wndButton.GetSafeHwnd())
{
BOOL bResult = FALSE;
CString str;
NMRVITEMEDIT nmrvie;
memset(&nmrvie, 0, sizeof(nmrvie));
CReportCtrl *pWnd = DYNAMIC_DOWNCAST(CReportCtrl, GetOwner());
if (pWnd != NULL)
{
GetWindowText(str);
nmrvie.hdr.hwndFrom = pWnd->GetSafeHwnd();
nmrvie.hdr.idFrom = pWnd->GetDlgCtrlID();
nmrvie.hdr.code = RVN_BUTTONCLICK;
nmrvie.iItem = m_iItem;
nmrvie.iSubItem = m_iSubItem;
nmrvie.bButton = m_bButton;
nmrvie.hWnd = GetSafeHwnd();
VERIFY((nmrvie.lpszText = str.GetBuffer(REPORTCTRL_MAX_TEXT)) != 0);
bResult = pWnd->Notify((LPNMREPORTVIEW) & nmrvie);
str.ReleaseBuffer();
}
if (bResult == TRUE)
SetWindowText(nmrvie.lpszText);
SetFocus();
return;
}
}
EndEdit();
}
UINT CReportEditCtrl::OnGetDlgCode()
{
return DLGC_WANTALLKEYS;
}
BOOL CReportEditCtrl::PreTranslateMessage(MSG *pMsg)
{
if
(
pMsg->message == WM_KEYDOWN
&& (pMsg->wParam == VK_TAB || pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
)
{
m_nLastKey = pMsg->wParam;
GetParent()->SetFocus();
return TRUE;
}
if (pMsg->message == WM_SYSCHAR)
return TRUE;
return CEdit::PreTranslateMessage(pMsg);
}
// CReportComboCtrl
CReportComboCtrl::CReportComboCtrl(INT iItem, INT iSubItem)
{
m_bEndEdit = FALSE;
m_iItem = iItem;
m_iSubItem = iSubItem;
m_nLastKey = VK_RETURN;
}
CReportComboCtrl::~CReportComboCtrl()
{
}
void CReportComboCtrl::PostNcDestroy()
{
CComboBox::PostNcDestroy();
delete this;
}
BEGIN_MESSAGE_MAP(CReportComboCtrl, CComboBox)
//{{AFX_MSG_MAP(CReportComboCtrl)
ON_WM_GETDLGCODE()
ON_CONTROL_REFLECT(CBN_KILLFOCUS, OnKillfocus)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CReportComboCtrl operations
void CReportComboCtrl::BeginEdit(UINT nKey)
{
CString str;
GetWindowText(str);
if (GetStyle() & CBS_DROPDOWN)
{
switch (nKey)
{
case VK_LBUTTON:
case VK_RETURN:
break;
default:
PostMessage(WM_CHAR, nKey);
break;
}
}
}
void CReportComboCtrl::EndEdit()
{
CString str;
if (m_bEndEdit)
return;
m_bEndEdit = TRUE;
GetWindowText(str);
NMRVITEMEDIT nmrvie;
memset(&nmrvie, 0, sizeof(nmrvie));
nmrvie.hdr.hwndFrom = GetSafeHwnd();
nmrvie.hdr.idFrom = GetDlgCtrlID();
nmrvie.hdr.code = RVN_ENDITEMEDIT;
nmrvie.iItem = m_iItem;
nmrvie.iSubItem = m_iSubItem;
nmrvie.hWnd = nmrvie.hdr.hwndFrom;
nmrvie.nKey = m_nLastKey;
nmrvie.lpszText = (LPTSTR) (LPCTSTR) str;
CWnd *pWnd = GetOwner();
if (pWnd != NULL)
pWnd->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM) & nmrvie);
PostMessage(WM_CLOSE, 0, 0);
}
// CReportComboCtrl message handlers
UINT CReportComboCtrl::OnGetDlgCode()
{
return DLGC_WANTALLKEYS;
}
BOOL CReportComboCtrl::PreTranslateMessage(MSG *pMsg)
{
if
(
pMsg->message == WM_KEYDOWN
&& (pMsg->wParam == VK_TAB || pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
)
{
m_nLastKey = pMsg->wParam;
GetParent()->SetFocus();
return TRUE;
}
if (pMsg->message == WM_SYSCHAR)
return TRUE;
return CComboBox::PreTranslateMessage(pMsg);
}
void CReportComboCtrl::OnKillfocus()
{
EndEdit();
}
// CReportTipCtrl
CReportTipCtrl::CReportTipCtrl()
{
WNDCLASS wndcls;
HINSTANCE hInst = AfxGetInstanceHandle();
if (!(::GetClassInfo(hInst, REPORTTIPCTRL_CLASSNAME, &wndcls)))
{
wndcls.style = CS_SAVEBITS;
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = hInst;
wndcls.hIcon = NULL;
wndcls.hCursor = LoadCursor(hInst, IDC_ARROW);
wndcls.hbrBackground = (HBRUSH) (COLOR_INFOBK + 1);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = REPORTTIPCTRL_CLASSNAME;
if (!AfxRegisterClass(&wndcls))
AfxThrowResourceException();
}
m_bLayeredWindows = InitLayeredWindows();
m_nAlpha = 0;
m_dwLastLButtonDown = ULONG_MAX;
m_dwDblClickMsecs = GetDoubleClickTime();
m_pReportCtrl = NULL;
}
CReportTipCtrl::~CReportTipCtrl()
{
}
BEGIN_MESSAGE_MAP(CReportTipCtrl, CWnd)
//{{AFX_MSG_MAP(CReportTipCtrl)
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CReportTipCtrl message handlers
BOOL CReportTipCtrl::Create(CReportCtrl *pReportCtrl)
{
ASSERT_VALID(pReportCtrl);
m_pReportCtrl = pReportCtrl;
return CreateEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW | (m_bLayeredWindows ? WS_EX_LAYERED : 0), // |WS_EX_LAYERED,
REPORTTIPCTRL_CLASSNAME, NULL, WS_BORDER | WS_POPUP, CRect(0, 0, 0, 0), pReportCtrl, 0, NULL);
}
BOOL CReportTipCtrl::Show(CRect rectText, LPCTSTR lpszText, CFont *pFont)
{
BOOL bResult = FALSE;
ASSERT(::IsWindow(GetSafeHwnd()));
if (rectText.IsRectEmpty() || GetFocus() == NULL)
{
Hide();
return FALSE;
}
if (rectText != m_rectText)
{
Invalidate();
UpdateWindow();
m_rectTip.top = -1;
m_rectTip.bottom = rectText.Height() + 1;
m_rectTip.left = -1;
m_rectTip.right = rectText.Width() + 1;
m_rectTip.InflateRect(m_pReportCtrl->m_iSpacing, 0);
m_pReportCtrl->ClientToScreen(rectText);
CClientDC dc(this);
pFont = pFont == NULL ? m_pReportCtrl->GetFont() : pFont;
CFont *pFontDC = dc.SelectObject(pFont);
CRect rectDisplay = rectText;
CSize size = dc.GetTextExtent(lpszText, _tcslen(lpszText));
rectDisplay.right = rectDisplay.left + size.cx;
if (rectDisplay.right > rectText.right)
{
rectDisplay.InflateRect(m_pReportCtrl->m_iSpacing, 0);
SetWindowPos(&wndTop, rectDisplay.left, rectDisplay.top, rectDisplay.Width(), rectDisplay.Height(),
SWP_NOOWNERZORDER | SWP_SHOWWINDOW | SWP_NOACTIVATE);
dc.SetBkMode(TRANSPARENT);
dc.TextOut(m_pReportCtrl->m_iSpacing - 1, 1, lpszText);
if (m_bLayeredWindows)
{
UpdateWindow();
if (m_nAlpha < 255)
SetTimer(1, REPORTTIPCTRL_FADETIME / (255 / REPORTTIPCTRL_FADESTEP), NULL);
g_lpfnSetLayeredWindowAttributes(GetSafeHwnd(), RGB(0xFF, 0, 0xFF), (BYTE) (m_nAlpha), ULW_ALPHA);
}
bResult = TRUE;
}
else
Hide();
m_rectText = rectText;
dc.SelectObject(pFontDC);
}
else
Hide();
return bResult;
}
void CReportTipCtrl::Hide()
{
if (!::IsWindow(GetSafeHwnd()))
return;
if (m_bLayeredWindows)
{
m_nAlpha = 255;
SetTimer(1, REPORTTIPCTRL_FADETIMEOUT, NULL);
}
if (IsWindowVisible())
ShowWindow(SW_HIDE);
}
void CReportTipCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_rectTip.PtInRect(point))
{
Hide();
ClientToScreen(&point);
CWnd *pWnd = WindowFromPoint(point);
if (pWnd == this)
pWnd = m_pReportCtrl;
INT iHitTest = (INT) pWnd->SendMessage(WM_NCHITTEST, 0, MAKELONG(point.x, point.y));
if (iHitTest == HTCLIENT)
{
pWnd->ScreenToClient(&point);
pWnd->PostMessage(WM_MOUSEMOVE, nFlags, MAKELONG(point.x, point.y));
}
else
{
pWnd->PostMessage(WM_NCMOUSEMOVE, iHitTest, MAKELONG(point.x, point.y));
}
}
}
BOOL CReportTipCtrl::PreTranslateMessage(MSG *pMsg)
{
DWORD dwTick = 0;
BOOL bDoubleClick = FALSE;
CWnd *pWnd;
INT iHitTest;
switch (pMsg->message)
{
case WM_LBUTTONDOWN:
dwTick = GetTickCount();
bDoubleClick = ((dwTick - m_dwLastLButtonDown) <= m_dwDblClickMsecs);
m_dwLastLButtonDown = dwTick;
// Notice fall-through
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
{
POINTS points = MAKEPOINTS(pMsg->lParam);
POINT point;
point.x = points.x;
point.y = points.y;
ClientToScreen(&point);
pWnd = WindowFromPoint(point);
if (pWnd == this)
pWnd = m_pReportCtrl;
iHitTest = (INT) pWnd->SendMessage(WM_NCHITTEST, 0, MAKELONG(point.x, point.y));
if (iHitTest == HTCLIENT)
{
pWnd->ScreenToClient(&point);
pMsg->lParam = MAKELONG(point.x, point.y);
}
else
{
switch (pMsg->message)
{
case WM_LBUTTONDOWN:
pMsg->message = WM_NCLBUTTONDOWN;
break;
case WM_RBUTTONDOWN:
pMsg->message = WM_NCRBUTTONDOWN;
break;
case WM_MBUTTONDOWN:
pMsg->message = WM_NCMBUTTONDOWN;
break;
}
pMsg->wParam = iHitTest;
pMsg->lParam = MAKELONG(point.x, point.y);
}
Hide();
pWnd->PostMessage(bDoubleClick ? WM_LBUTTONDBLCLK : pMsg->message, pMsg->wParam, pMsg->lParam);
return TRUE;
}
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
Hide();
m_pReportCtrl->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam);
return TRUE;
}
if (GetFocus() == NULL)
{
Hide();
return TRUE;
}
return CWnd::PreTranslateMessage(pMsg);
}
void CReportTipCtrl::OnTimer(UINT nIDEvent)
{
if (nIDEvent == 1)
{
ASSERT(m_bLayeredWindows == TRUE);
if (m_nAlpha < 255)
{
m_nAlpha += REPORTTIPCTRL_FADESTEP;
if (m_nAlpha >= 255)
{
m_nAlpha = 255;
KillTimer(1);
}
g_lpfnSetLayeredWindowAttributes(GetSafeHwnd(), RGB(0xFF, 0, 0xFF), (BYTE) (m_nAlpha), ULW_ALPHA);
}
else
{
m_nAlpha = 0;
KillTimer(1);
}
}
CWnd::OnTimer(nIDEvent);
}
// Global utilities
BOOL InitLayeredWindows()
{
if (g_lpfnUpdateLayeredWindow == NULL)
{
HMODULE hUser32 = GetModuleHandle(_T("USER32.DLL"));
g_lpfnUpdateLayeredWindow = (lpfnUpdateLayeredWindow) GetProcAddress(hUser32, "UpdateLayeredWindow");
g_lpfnSetLayeredWindowAttributes = (lpfnSetLayeredWindowAttributes) GetProcAddress(hUser32,
"SetLayeredWindowAttributes");
return g_lpfnUpdateLayeredWindow != NULL ? TRUE : FALSE;
}
return TRUE;
}
BOOL MatchString(const CString &strString, const CString &strPattern)
{
CString strStr = strString;
CString strPat = strPattern;
strStr.MakeLower();
strPat.MakeLower();
BOOL bStopSearch = FALSE;
while (!strString.IsEmpty() && !strPat.IsEmpty() && !bStopSearch)
{
if (strPat[0] == _T('*'))
{
strPat = strPat.Right(strPat.GetLength() - 1);
if (strPat.IsEmpty())
return TRUE;
CString strBuf = strPat;
INT iWild = strBuf.FindOneOf(_T("*?"));
if (iWild != -1)
strBuf = strBuf.Left(iWild);
INT iFound = strStr.Find(strBuf);
if (iFound == -1)
return FALSE;
if (iWild == -1)
return TRUE;
strStr = strStr.Right(strStr.GetLength() - iFound);
}
else
{
bStopSearch = strStr[0] != strPat[0] && strPat[0] != _T('?');
if (!bStopSearch)
{
strStr = strStr.Right(strStr.GetLength() - 1);
strPat = strPat.Right(strPat.GetLength() - 1);
}
}
}
bStopSearch = bStopSearch || !strStr.IsEmpty() && strPat.IsEmpty() || strStr.IsEmpty() && !strPat.IsEmpty();
return !bStopSearch || bStopSearch && !strPat.IsEmpty() && strPat[0] == _T('*');
}
// CReportHeaderCtrl
CReportHeaderCtrl::CReportHeaderCtrl()
{
}
CReportHeaderCtrl::~CReportHeaderCtrl()
{
}
BEGIN_MESSAGE_MAP(CReportHeaderCtrl, CFlatHeaderCtrl)
//{{AFX_MSG_MAP(CReportHeaderCtrl)
ON_WM_RBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CReportHeaderCtrl attributes
void CReportHeaderCtrl::SetMinMax(INT iPos, INT iMin, INT iMax)
{
ASSERT(iPos < m_arrayHdrItemEx.GetSize());
m_arrayHdrItemEx[iPos].iMinWidth = iMin;
m_arrayHdrItemEx[iPos].iMaxWidth = iMax;
};
// CReportHeaderCtrl messages
void CReportHeaderCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
CReportCtrl *pReportCtrl = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
if (pReportCtrl != NULL)
{
HDHITTESTINFO hdhti;
hdhti.pt = point;
INT iItem = ::SendMessage(GetSafeHwnd(), HDM_HITTEST, 0, (LPARAM) (&hdhti));
NMRVHEADER nmrvhdr;
if (pReportCtrl->NotifyHdr(&nmrvhdr, RVN_HEADERRCLICK, iItem))
return;
}
CFlatHeaderCtrl::OnRButtonDown(nFlags, point);
}
void CReportHeaderCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
CFlatHeaderCtrl::OnMouseMove(nFlags, point);
if (m_bResizing)
{
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
if (rc->GetCategoryHdrHeight() > 0)
{
CFlatHeaderCtrl *ac = rc->GetCategoryHdrCtrl();
ac->SameWidth(this);
}
}
}
void CReportHeaderCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
BOOL bResizing = m_bResizing;
CFlatHeaderCtrl::OnLButtonUp(nFlags, point);
if (bResizing)
{
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
if (rc->GetCategoryHdrHeight() > 0)
{
CFlatHeaderCtrl *ac = rc->GetCategoryHdrCtrl();
ac->SameWidth(this);
}
}
}
IMPLEMENT_DYNCREATE(CCategoryHdr, CFlatHeaderCtrl)
BEGIN_MESSAGE_MAP(CCategoryHdr, CFlatHeaderCtrl)
//{{AFX_MSG_MAP(CReportHeaderCtrl)
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CCategoryHdr::CCategoryHdr()
{
m_bDynamic = false;
m_iState = SW_HIDE;
}
void CCategoryHdr::Show()
{
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
int iState = -1;
if (m_bDynamic && rc->GetCategorySubItem() < 0)
{
if (m_iState == SW_SHOW)
iState = SW_HIDE;
}
else
{
if (m_iState == SW_HIDE)
iState = SW_SHOW;
}
if (iState != -1)
{
m_iState = iState;
ShowWindow(m_iState);
rc->Layout();
rc->ScrollWindow(SB_HORZ, rc->GetScrollPos32(SB_HORZ));
}
}
void CCategoryHdr::OnInitialUpdate(void)
{
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
CFlatHeaderCtrl *hc = rc->GetHeaderCtrl();
SetImageList(hc->GetImageList());
HDITEM hdi;
HDITEMEX hditemex;
INT item = 0;
hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
hdi.fmt = HDF_RIGHT | HDF_STRING;
hdi.pszText = _T("Arranged By:");
hdi.cxy = GetItemWidth(&hdi);
InsertItem(item, &hdi);
hditemex.nStyle = HDF_EX_NODIVIDER;
hditemex.iMinWidth = hdi.cxy;
hditemex.iMaxWidth = hdi.cxy;
SetItemEx(item++, &hditemex);
hditemex.iMaxWidth = 0;
hditemex.iMinWidth = 20;
hditemex.nStyle = 0;
hdi.fmt = HDF_LEFT | HDF_STRING;
hdi.cxy = 20;
InsertItem(item, &hdi);
SetItemEx(item++, &hditemex);
hdi.fmt = HDF_CENTER | HDF_STRING;
hdi.cxy = 10;
hdi.pszText = _T("");
hditemex.iMinWidth = 10;
InsertItem(item, &hdi);
SetItemEx(item++, &hditemex);
NewCategory();
}
void CCategoryHdr::NewCategory(BOOL bSort)
{
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
ASSERT(rc);
BOOL bSortAccending;
INT iSubItem = rc->GetCategorySubItem(&bSortAccending);
HDITEM hdiItem;
if (!bSort)
{
hdiItem.mask = HDI_WIDTH;
VERIFY(GetItem(0, &hdiItem));
CString str;
if (iSubItem >= 0)
{
rc->GetSubItem(iSubItem, &hdiItem);
hdiItem.mask &= ~HDI_WIDTH;
hdiItem.cxy = 0;
}
else
{
hdiItem.mask = HDI_TEXT | HDI_FORMAT;
hdiItem.fmt = HDF_RIGHT | HDF_STRING;
hdiItem.pszText = _T("");
}
SetItem(1, &hdiItem);
}
hdiItem.mask = HDI_WIDTH;
GetItem(2, &hdiItem);
hdiItem.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
hdiItem.fmt = (IsAutoSize() ? HDF_RIGHT : HDF_LEFT) | HDF_STRING;
CString sText = rc->GetHeadingForSortedCategory();
hdiItem.pszText = sText.GetBuffer(0);
hdiItem.cxy = max(hdiItem.cxy, GetItemWidth(&hdiItem, TRUE));
CRect rWindow;
GetWindowRect(&rWindow);
hdiItem.cxy = rWindow.Width() / 2;
if (iSubItem >= 0)
SetSortColumn(2, bSortAccending);
else
{
SetSortColumn(-1, FALSE);
hdiItem.pszText = _T("");
}
SetItem(2, &hdiItem);
}
void CCategoryHdr::MatchHeaders()
{
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
ASSERT(rc);
CFlatHeaderCtrl *hc = rc->GetHeaderCtrl();
ASSERT(hc);
SameWidth(hc);
hc->SameWidth(this, rc->GetStyle() & RVS_TREEVIEW ? 0 : -1);
}
void CCategoryHdr::OnMouseMove(UINT nFlags, CPoint point)
{
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
if (m_bResizing)
{
CFlatHeaderCtrl *hc = rc->GetHeaderCtrl();
CFlatHeaderCtrl::OnMouseMove(nFlags, point);
hc->SameWidth(this);
rc->m_bWidthChanged = TRUE;
rc->Invalidate();
}
}
void CCategoryHdr::MeasureMenuItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
CClientDC dc(this);
INT iSubItem = lpMeasureItemStruct->itemData;
CReportCtrl::CATITEM catItem;
catItem.iSubItem = iSubItem;
rc->GetCategoryMenuItem(&catItem);
ASSERT(catItem.mask & CATEGORY_ENABLED);
CFont *pFont = dc.SelectObject(GetFont());
CSize size;
size = dc.GetTextExtent(catItem.strText);
lpMeasureItemStruct->itemWidth = size.cx;
lpMeasureItemStruct->itemHeight = max(m_sizeImage.cy, size.cy) + 2;
dc.SelectObject(pFont);
}
void CCategoryHdr::DrawMenuItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
INT iSubItem = lpDrawItemStruct->itemData;
CReportCtrl::CATITEM catItem;
catItem.iSubItem = iSubItem;
rc->GetCategoryMenuItem(&catItem);
ASSERT(catItem.mask & CATEGORY_ENABLED);
CRect rcItem(lpDrawItemStruct->rcItem);
rcItem.DeflateRect(1, 1);
if (lpDrawItemStruct->itemState & ODS_SELECTED)
pDC->FillSolidRect(rcItem, m_ColorMenuHilight);
if (catItem.mask & CATEGORY_IMAGE)
{
IMAGEINFO info;
CImageList *pImageList = GetImageList();
pImageList->GetImageInfo(catItem.iImage, &info);
pImageList->DrawIndirect(pDC, catItem.iImage, rcItem.TopLeft(), CRect(info.rcImage).Size(), CPoint(0, 0));
}
rcItem.left += m_sizeImage.cx + 2;
COLORREF bkColor = pDC->GetBkColor();
COLORREF pnColor = pDC->GetTextColor();
pDC->SetBkColor(lpDrawItemStruct->itemState & ODS_SELECTED ? m_ColorMenuHilight : m_ColorMenu);
pDC->SetTextColor(m_crMenuText);
pDC->DrawText(catItem.strText, -1, rcItem, DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER);
pDC->SetTextColor(pnColor);
pDC->SetBkColor(bkColor);
rcItem.left -= m_sizeImage.cx + 2;
if (lpDrawItemStruct->itemState & ODS_SELECTED)
{
CBrush brushHighLight(RGB(0, 0, 0));
rcItem.InflateRect(1, 1);
pDC->FrameRect(rcItem, &brushHighLight);
}
}
void CCategoryHdr::OnLButtonDown(UINT nFlags, CPoint point)
{
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
CPoint p(GetSystemMetrics(SM_CXDRAG), 0);
CRect rect;
CRect rect1;
GetItemRect(0, &rect);
GetItemRect(1, &rect1);
GetDC()->DPtoLP(&p);
rect.right = rect1.right - p.x - 1;
if (PtInRect(&rect, point))
{
CCategoryMenu menu(this, m_sizeImage.cx);
menu.CreatePopupMenu();
INT iCategorySubItem = rc->GetCategorySubItem();
for (INT iSubItem = 0; iSubItem < rc->GetSubItemCount(); iSubItem++)
{
if (iSubItem == iCategorySubItem || rc->GetColumnFromSubItem(iSubItem) >= 0)
{
CReportCtrl::CATITEM catItem;
catItem.iSubItem = iSubItem;
rc->GetCategoryMenuItem(&catItem);
if (catItem.mask & CATEGORY_ENABLED)
menu.AppendMenu(MF_OWNERDRAW, (UINT_PTR) iSubItem, (LPCTSTR) iSubItem);
}
}
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_OWNERDRAW, (UINT_PTR) - 1, (LPCTSTR) - 1);
ClientToScreen(&point);
menu.TrackPopupMenu(TPM_CENTERALIGN, point.x, point.y, this);
}
else
{
if (HHT_ONDIVIDER & m_hdhtiHotItem.flags || rc->GetCategorySubItem() >= 0)
CFlatHeaderCtrl::OnLButtonDown(nFlags, point);
}
}
void CCategoryHdr::OnLButtonUp(UINT nFlags, CPoint point)
{
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
BOOL bOnDivider = HHT_ONDIVIDER & m_hdhtiHotItem.flags;
CFlatHeaderCtrl::OnLButtonUp(nFlags, point);
MatchHeaders();
rc->Layout();
CRect rect;
GetItemRect(2, &rect);
if (!bOnDivider && PtInRect(&rect, point) && rc->GetCategorySubItem() >= 0)
{
rc->m_bCategoryAscending = !rc->m_bCategoryAscending;
NewCategory(TRUE);
rc->ResortItems();
}
}
BOOL CCategoryHdr::OnCommand(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
CReportCtrl *rc = DYNAMIC_DOWNCAST(CReportCtrl, GetParent());
rc->SetCategorySubItem(wParam);
return TRUE;
}
void CCategoryHdr::SetDynamic(BOOL bDynamic)
{
m_bDynamic = bDynamic;
Show();
NewCategory();
}
BOOL CCategoryHdr::IsDynamic()
{
return m_bDynamic;
}
IMPLEMENT_DYNCREATE(CCategoryMenu, CMenu)
CCategoryMenu::CCategoryMenu()
{
ASSERT(0);
}
CCategoryMenu::CCategoryMenu(CCategoryHdr *pCCategoryHdr, int iHilite)
{
VERIFY(m_rgn.CreateRectRgn(0, 0, 0, 0));
m_iHilite = iHilite;
m_pCCategoryHdr = pCCategoryHdr;
m_ColorMenu = ::GetSysColor(COLOR_MENU);
if (GetSysColorBrush(COLOR_MENUHILIGHT) != NULL)
m_ColorMenuHilight = ::GetSysColor(COLOR_MENUHILIGHT);
else
m_ColorMenuHilight = ::GetSysColor(COLOR_3DFACE);
}
void CCategoryMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rcItem(lpDrawItemStruct->rcItem);
pDC->FillSolidRect(rcItem, m_ColorMenu);
rcItem.right = m_iHilite + 1;
CRect rcHilite(rcItem);
rcHilite.right = m_iHilite;
pDC->FillSolidRect(rcItem, m_ColorMenuHilight);
m_pCCategoryHdr->DrawMenuItem(lpDrawItemStruct);
CRgn rgn;
CRect box;
rgn.CreateRectRgnIndirect(&rcItem);
m_rgn.CombineRgn(&m_rgn, &rgn, RGN_OR);
m_rgn.GetRgnBox(&box);
rgn.SetRectRgn(&box);
rgn.CombineRgn(&rgn, &m_rgn, RGN_DIFF);
CBrush brushFace(m_ColorMenuHilight);
CBrush *pBrush = pDC->SelectObject(&brushFace);
pDC->PaintRgn(&rgn);
pDC->SelectObject(&pBrush);
}
void CCategoryMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
ASSERT(m_pCCategoryHdr);
m_pCCategoryHdr->MeasureMenuItem(lpMeasureItemStruct);
}