// enum_view.cpp : implementation of the CEnumView class
//
// $Header$
//
// All of this source code is copyright (C) 2001 Tim Finer.
// You have permission to use this code in any way.
// My only restriction is that you must not claim you wrote this code.
//
#include "stdafx.h"
#include "enum_view.h"
#include "enum_doc.h"
#include "stl_index.h"
#include "resource.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
using namespace tfiner;
CEnumView::CEnumView() {
m_sortColumn = eNONE;
InitCache();
}
CEnumView::~CEnumView() {}
void CEnumView::InitCache() {
m_cacheRowStart = m_cacheRowFinish = -1;
m_strCache.clear();
if ( GetSafeHwnd() )
Invalidate();
}
BOOL CEnumView::PreCreateWindow(CREATESTRUCT& cs) {
cs.style |= LVS_REPORT | LVS_OWNERDATA;
return CListView::PreCreateWindow(cs);
}
void CEnumView::OnInitialUpdate() {
CListView::OnInitialUpdate();
CListCtrl& listCtrl = GetListCtrl();
listCtrl.SetExtendedStyle(
LVS_EX_GRIDLINES |
LVS_EX_FULLROWSELECT );
if ( !listCtrl.GetHeaderCtrl()->GetItemCount() ) {
CString col;
col.LoadString( IDS_NAME );
listCtrl.InsertColumn( eNAME, col );
int width = listCtrl.GetStringWidth( col );
listCtrl.SetColumnWidth( eNAME, width * 4 );
col.LoadString( IDS_VALUE );
listCtrl.InsertColumn( eVALUE, col );
width = listCtrl.GetStringWidth( col );
listCtrl.SetColumnWidth( eVALUE, width * 4 );
}
m_viewHex = 1 == AfxGetApp()->GetProfileInt( "View", "Hex", true );
m_font.CreateStockObject( ANSI_FIXED_FONT );
SetFont( &m_font );
}
BOOL CEnumView::OnPreparePrinting(CPrintInfo* pInfo) {
return DoPreparePrinting(pInfo);
}
void CEnumView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) {}
void CEnumView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) {}
void CEnumView::OnUpdate( CView* pSender, LPARAM lHint, CObject* pHint ) {
CEnumDoc* pDoc = GetDocument();
const size_t NUM_CONSTS = pDoc->GetNumConstants();
if ( pDoc->IsOpen() && 0 == NUM_CONSTS )
AfxMessageBox( IDS_NO_ENUMS_FOUND );
GetListCtrl().SetItemCountEx( NUM_CONSTS, LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL );
//default order, none
m_rowOrder.resize( NUM_CONSTS );
for ( size_t i = 0; i < NUM_CONSTS; ++i )
m_rowOrder[ i ] = i;
// parser by default returns alphabetical order for names
m_sortColumn = eNAME;
Invalidate();
}
void CEnumView::OnHex() {
m_viewHex = !m_viewHex;
AfxGetApp()->WriteProfileInt( "View", "Hex", m_viewHex );
InitCache();
}
void CEnumView::OnUpdateHex( CCmdUI* pCmdUI ) {
pCmdUI->SetCheck( m_viewHex );
}
void CEnumView::OnGetDispInfo( NMHDR* pNMHDR, LRESULT* pResult ) {
LV_DISPINFO* pDispInfo = reinterpret_cast<LV_DISPINFO*>(pNMHDR);
if ( pDispInfo->item.mask & LVIF_TEXT ) {
const int ROW = pDispInfo->item.iItem;
const int COL = pDispInfo->item.iSubItem;
// It turns out that this condition actually happens!
// It looks like cache msgs don't always get handled before disp info msgs.
if ( m_cacheRowStart > ROW || ROW > m_cacheRowFinish )
return;
const int CACHE_ROW = ROW - m_cacheRowStart;
const int CACHE_SIZE = static_cast<int>(m_strCache.size());
if ( CACHE_ROW >= CACHE_SIZE )
return;
const int CACHE_COLS = static_cast<int>(m_strCache[ CACHE_ROW ].size());
if ( COL >= CACHE_COLS )
return;
// Locking semantics not needed as this is the only routine to actually read the cache.
pDispInfo->item.pszText = m_strCache[CACHE_ROW][COL].GetBuffer( 0 );
}
*pResult = 0;
}
// Refreshes the string cache, as necessary.
void CEnumView::OnCacheHint( NMHDR* pNMHDR, LRESULT* pResult ) {
const NMLVCACHEHINT* pCacheHint = reinterpret_cast<NMLVCACHEHINT*>(pNMHDR);
const size_t NUM_ROWS = pCacheHint->iTo - pCacheHint->iFrom + 1;
CHeaderCtrl* pHead = GetListCtrl().GetHeaderCtrl();
ASSERT_VALID( pHead );
if ( !pHead )
return;
const size_t NUM_COLS = pHead->GetItemCount();
// resize necessary? (note that this routine only grows the cache, never trims).
if ( m_strCache.size() < NUM_ROWS ) {
const size_t NUM_OLD_ROWS = m_strCache.size();
m_strCache.resize( NUM_ROWS );
// if out of memory, bail
if ( m_strCache.size() < NUM_ROWS )
return;
// resize only the new rows
for( size_t i = NUM_OLD_ROWS; i != NUM_ROWS; ++i ) {
m_strCache[i].resize( NUM_COLS );
if ( m_strCache[i].size() < NUM_COLS )
return;
}
}
// new content?
if ( m_cacheRowStart != pCacheHint->iFrom ||
m_cacheRowFinish != pCacheHint->iTo ) {
m_cacheRowStart = pCacheHint->iFrom;
m_cacheRowFinish = pCacheHint->iTo;
CEnumDoc* pDoc = GetDocument();
for ( int rowIdx = m_cacheRowStart; rowIdx <= m_cacheRowFinish; rowIdx++ ) {
CString name;
int value;
const size_t REC_IDX = m_rowOrder[ rowIdx ];
if ( !pDoc->GetConstant(REC_IDX, name, value) )
break;
for ( size_t cacheCol = 0; cacheCol < NUM_COLS; cacheCol++ ) {
CString& cell = m_strCache[ rowIdx - m_cacheRowStart ][cacheCol];
// OK to call even if GetBuffer was never called.
cell.ReleaseBuffer( 0 );
// NOT OK to call if LockBuffer wasn't called - also UnlockBuffer causes a memory allocation.
// strRec.UnlockBuffer();
if ( 0 == cacheCol )
cell = name;
else if ( 1 == cacheCol )
cell.Format( m_viewHex ? "0x%08x" : "%d", value );
}
}
}
*pResult = 0;
}
void CEnumView::OnColumnClick( NMHDR* pNMHDR, LRESULT* pResult ) {
const NMLISTVIEW* pLIST_VIEW = reinterpret_cast<NMLISTVIEW*>(pNMHDR);
if ( -1 == pLIST_VIEW->iItem ) {
// simple flip, no sorting or loading needed.
if ( m_sortColumn == pLIST_VIEW->iSubItem )
std::reverse( m_rowOrder.begin(), m_rowOrder.end() );
// loading, sorting...
else {
CEnumDoc* pDoc = GetDocument();
const size_t NUM_ROWS = m_rowOrder.size();
std::vector< CString > names( NUM_ROWS );
std::vector< int > values( NUM_ROWS );
for ( size_t i = 0; i < NUM_ROWS; ++i ) {
m_rowOrder[ i ] = i;
VERIFY( pDoc->GetConstant( i, names[i], values[i] ) );
}
if ( eNAME == pLIST_VIEW->iSubItem )
create_index( names.begin(), names.end(), m_rowOrder.begin() );
else if ( eVALUE == pLIST_VIEW->iSubItem )
create_index( values.begin(), values.end(), m_rowOrder.begin() );
m_sortColumn = static_cast<eColumn>(pLIST_VIEW->iSubItem);
}
InitCache();
}
}
#ifdef _DEBUG
void CEnumView::AssertValid() const {
CListView::AssertValid();
}
void CEnumView::Dump(CDumpContext& dc) const {
CListView::Dump(dc);
}
CEnumDoc* CEnumView::GetDocument() {
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CEnumDoc)));
return (CEnumDoc*)m_pDocument;
}
#endif //_DEBUG
IMPLEMENT_DYNCREATE(CEnumView, CListView)
BEGIN_MESSAGE_MAP(CEnumView, CListView)
//{{AFX_MSG_MAP(CEnumView)
ON_COMMAND(ID_HEX, OnHex)
ON_UPDATE_COMMAND_UI(ID_HEX, OnUpdateHex)
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CListView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CListView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CListView::OnFilePrintPreview)
ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetDispInfo)
ON_NOTIFY_REFLECT(LVN_ODCACHEHINT, OnCacheHint)
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
END_MESSAGE_MAP()