|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999.
//
// File: lview.cxx
//
// Contents:
//
// History: 15 Aug 1996 DLee Created
//
//--------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
//
// Window procedure for ListView
//
LRESULT WINAPI ListViewWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { CListView *pControl = (CListView *) GetWindowLongPtr(hwnd, 0); LRESULT lRet = 0;
switch (msg) { case WM_CREATE : pControl = new CListView; pControl->Create (GetParent(hwnd), hwnd); SetWindowLongPtr (hwnd, 0, (LONG_PTR) pControl); break; case WM_DESTROY : delete pControl; lRet = DefWindowProc(hwnd, msg, wParam, lParam); break; case WM_SETFONT: pControl->SetFont ((HFONT)wParam); break; case WM_SETFOCUS: pControl->SetFocus(); lRet = DefWindowProc(hwnd, msg, wParam, lParam); break; case wmInsertItem: pControl->InsertItem ((int)lParam); break; case wmDeleteItem: pControl->DeleteItem ((int)lParam); break; case wmUpdateItem: pControl->InvalidateItem ((int)lParam); break;
case wmSetCountBefore: pControl->SetCountBefore ((int)lParam); break; case wmSetCount: pControl->SetTotalCount ((int)lParam); break; case wmResetContents: pControl->ResetContents(); break;
case WM_SIZE: pControl->Size (wParam, LOWORD(lParam), HIWORD(lParam)); break; case WM_PAINT: { PAINTSTRUCT paint; BeginPaint ( hwnd, &paint ); pControl->Paint (paint); EndPaint(hwnd, &paint ); } break; case WM_LBUTTONUP: pControl->ButtonUp(HIWORD(lParam)); break; case WM_LBUTTONDOWN: pControl->ButtonDown(HIWORD(lParam)); break; case WM_LBUTTONDBLCLK: SendMessage (pControl->Parent(), WM_COMMAND, MAKEWPARAM(idListChild, LBN_DBLCLK), (LPARAM) hwnd); break; case WM_KEYDOWN: pControl->KeyDown ((int)wParam); break; case WM_VSCROLL: pControl->Vscroll ((int)LOWORD(wParam), (int)HIWORD(wParam)); break; case WM_MOUSEWHEEL : lRet = pControl->MouseWheel( hwnd, wParam, lParam ); break; case wmContextMenuHitTest: lRet = pControl->ContextMenuHitTest( wParam, lParam ); break; default : lRet = DefWindowProc(hwnd, msg, wParam, lParam); break; }
return lRet; } //ListViewWndProc
CListView::CListView () : _hwndParent(0), _hwnd(0), _cBefore(0), _cTotal (0), _cx(0), _cy(0), _cyLine(1), _cLines(0), _hfont(0), _iWheelRemainder(0) {}
LRESULT CListView::MouseWheel( HWND hwnd, WPARAM wParam, LPARAM lParam ) { // forward what we don't process
if ( wParam & ( MK_SHIFT | MK_CONTROL ) ) return DefWindowProc( hwnd, WM_MOUSEWHEEL, wParam, lParam );
// add the current scroll to the remainder from last time
int iDelta = (int) (short) HIWORD( wParam ); iDelta += _iWheelRemainder;
// if there isn't enough to process this time, just return
if ( abs( iDelta ) < WHEEL_DELTA ) { _iWheelRemainder = iDelta; return 0; }
// compute the remainder and amount to scroll
_iWheelRemainder = ( iDelta % WHEEL_DELTA ); iDelta /= WHEEL_DELTA;
BOOL fDown; if ( iDelta < 0 ) { fDown = TRUE; iDelta = -iDelta; } else fDown = FALSE;
// get the # of lines to scroll per WHEEL_DELTA
int cLines; SystemParametersInfo( SPI_GETWHEELSCROLLLINES, 0, &cLines, 0 ); if ( 0 == cLines ) return 0;
int cVisibleLines = _cLines;
// if scrolling a page, do so. don't scroll more than one page
if ( WHEEL_PAGESCROLL == cLines ) iDelta = __max( 1, (cVisibleLines - 1) ); else { iDelta *= cLines; if ( iDelta >= cVisibleLines ) iDelta = __max( 1, (cVisibleLines - 1) ); }
// phew. do the scroll
if ( 0 != iDelta ) { if ( fDown ) _GoDown( iDelta ); else _GoUp( iDelta ); }
return iDelta; } //MouseWheel
LRESULT CListView::ContextMenuHitTest( WPARAM wParam, LPARAM lParam ) { POINT pt;
// cast required to sign extend [multimon bug]
pt.x = (LONG)(short)LOWORD( lParam ); pt.y = (LONG)(short)HIWORD( lParam );
RECT rc; GetWindowRect( _hwnd, &rc );
// did they click in the window?
if ( !PtInRect( &rc, pt ) ) return -1;
// convert y to window view coordinates
int vy = pt.y - rc.top;
// did they click on a line in the window?
int line = vy / _cyLine; int newLine = line; if ( line >= _cLines || line >= _cTotal ) return -1;
// make this line the current selection
ButtonDown( vy );
return line; } //ContextMenuHitTest
//
// Create
//
void CListView::Create (HWND hwndParent, HWND hwnd) { _hwndParent = hwndParent; _hwnd = hwnd; MEASUREITEMSTRUCT measure; measure.CtlType = odtListView; //
// Owner: Measure item!
//
SendMessage (_hwndParent, wmMeasureItem, 0, (LPARAM) &measure); _cyLine = measure.itemHeight; } //Create
//
// Key Down
//
void CListView::KeyDown (int nKey) { switch (nKey) { case ' ' : ButtonDown( 0 ); break; case 11: case 13: // treat ENTER as a double-click
//
// Owner: Double click!
//
SendMessage (_hwndParent, WM_COMMAND, MAKEWPARAM(idListChild, LBN_DBLCLK), (LPARAM) _hwnd); break; //
// Translate keystrokes into scrolling actions
//
case VK_HOME: SendMessage (_hwnd, WM_VSCROLL, SB_TOP, 0L); break; case VK_END: SendMessage (_hwnd, WM_VSCROLL, SB_BOTTOM, 0L); break; case VK_PRIOR: SendMessage (_hwnd, WM_VSCROLL, SB_PAGEUP, 0L); break; case VK_NEXT: SendMessage (_hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L); break; case VK_UP: SelectUp (); break; case VK_DOWN: SelectDown (); break; } } //KeyDown
void CListView::UpdateHighlight( int oldLine, int newLine ) { // unhighlight
if ( -1 != oldLine ) RefreshRow( oldLine );
// highlight
if ( oldLine != newLine ) RefreshRow( newLine );
UpdateWindow (_hwnd); } //UpdateHighlight
void CListView::SelectUp () { int newLine;
if ( SendMessage( _hwndParent, wmListNotify, listSelectUp, (LPARAM)&newLine )) UpdateHighlight( newLine + 1, newLine ); } //SelectUp
void CListView::SelectDown () { int newLine;
if ( SendMessage( _hwndParent, wmListNotify, listSelectDown, (LPARAM)&newLine )) UpdateHighlight( newLine - 1, newLine ); } //SelectDown
//
// Button up (select)
//
void CListView::ButtonUp (int y) { }
void CListView::ButtonDown (int y) { int line = y / _cyLine; int newLine = line; if (line >= _cLines) return; //
// Owner: Selection made!
//
if (SendMessage (_hwndParent, wmListNotify, listSelect, (LPARAM)&line )) UpdateHighlight( line, newLine );
::SetFocus (_hwnd); } //ButtonDown
void CListView::SetFocus() { //
// Owner: Focus!
//
SendMessage (_hwndParent, WM_COMMAND, MAKEWPARAM(idListChild, LBN_SETFOCUS), (LPARAM) _hwnd); } //SetFocus
//
// Size
//
void CListView::Size (WPARAM flags, int cx, int cy) { int cxOld = _cx; int cyOld = _cy; _cx = cx; _cy = cy;
BOOL fInvalidate = FALSE;
if (cy != cyOld) { _cLines = cy / _cyLine; //
// Owner: Size!
//
long cRows = _cLines; fInvalidate = (BOOL)SendMessage(_hwndParent, wmListNotify, listSize, (LPARAM) &cRows); }
// Don't repaint the common area
RECT rect; rect.top = 0; rect.left = 0; rect.bottom = min (cy, cyOld); rect.right = min (cx, cxOld);
// no need -- user does this for free, and it causes repaint bugs
// ValidateRect (_hwnd, &rect );
if (cy != cyOld) { if ( fInvalidate ) InvalidateAndUpdateScroll(); else UpdateScroll(); } } //Size
//
// Paint
//
void CListView::Paint (PAINTSTRUCT& paint) { RECT& rect = paint.rcPaint; int lineStart = rect.top / _cyLine; int lineEnd = (rect.bottom + _cyLine - 1) / _cyLine; DRAWITEMSTRUCT draw; draw.hwndItem = _hwnd; draw.itemAction = ODA_DRAWENTIRE; HDC hdc = paint.hdc; draw.hDC = hdc; HFONT hfontOld = (HFONT) SelectObject (hdc, _hfont);
for (int i = lineStart; i < lineEnd; i++) { draw.itemState = 0;
if ( GetFocus() == _hwnd ) draw.itemState |= ODS_FOCUS;
draw.itemID = i; draw.rcItem.top = 0; draw.rcItem.left = 0; draw.rcItem.bottom = _cyLine; draw.rcItem.right = _cx;
SetViewportOrgEx( hdc, 0, i * _cyLine, 0 ); //
// Owner: Draw item!
//
SendMessage (_hwndParent, wmDrawItem, 0, (LPARAM)&draw); } SelectObject (hdc, hfontOld); } //Paint
//
// Set Font
//
void CListView::SetFont (HFONT hfontNew) { _hfont = hfontNew; MEASUREITEMSTRUCT measure; measure.CtlType = odtListView; //
// Owner: Measure item
//
SendMessage (_hwndParent, wmMeasureItem, 0, (LPARAM) &measure); _cyLine = measure.itemHeight; long cRows = (_cy + _cyLine - 1) / _cyLine; _cLines = cRows; //
// Owner: Size
//
SendMessage(_hwndParent, wmListNotify, listSize, (LPARAM) &cRows); InvalidateAndUpdateScroll(); } //SetFont
//
// Scrolling
//
void CListView::Vscroll ( int action, int nPos) { switch (action) { case SB_LINEUP: LineUp (); break; case SB_LINEDOWN: LineDown (); break; case SB_THUMBTRACK: // don't refresh when thumb dragging
// over many hits (too expensive)
break;
case SB_THUMBPOSITION: if (_cTotal > 65535) { SCROLLINFO si; si.cbSize = sizeof si; si.fMask = SIF_TRACKPOS; GetScrollInfo (_hwnd, SB_VERT, &si); nPos = si.nTrackPos; } ScrollPos (nPos); break; case SB_PAGEDOWN: PageDown (); break; case SB_PAGEUP: PageUp (); break; case SB_TOP: Top (); break; case SB_BOTTOM: Bottom (); break;
} } //VScroll
void CListView::LineUp () { long cLine = 1; //
// Owner: Line up!
//
SendMessage(_hwndParent, wmListNotify, listScrollLineUp, (LPARAM) &cLine); if (cLine == 1) { if (_cBefore != 0) _cBefore--;
// Force scroll and redraw
RECT rect; GetClientRect (_hwnd, &rect); MyScrollWindow (_hwnd, 0, _cyLine, &rect, &rect); UpdateScroll(); } } //LineUp
void CListView::LineDown () { long cLine = 1; //
// Owner: Line down!
//
SendMessage(_hwndParent, wmListNotify, listScrollLineDn, (LPARAM) &cLine); if (cLine == 1) { RECT rect; GetClientRect (_hwnd, &rect); MyScrollWindow (_hwnd, 0, -_cyLine, &rect, &rect); _cBefore++; UpdateScroll(); } } //LineDown
void CListView::_GoUp( long cToGo ) { CWaitCursor wait;
long count = cToGo;
count = __min( count, _cBefore );
//
// Owner: Page up!
//
SendMessage(_hwndParent, wmListNotify, listScrollPageUp, (LPARAM) &count);
// _cBefore is approximate; don't give up if it is too big
if ( 0 == count ) { if ( _cBefore > 0 ) count = _cBefore - 1; else count = 1; // worst case; scroll up one line
SendMessage( _hwndParent, wmListNotify, listScrollPageUp, (LPARAM) &count ); }
// gee, we're having a bad hair day
if ( 0 == count ) { count = 1; // worst case; scroll up one line
SendMessage( _hwndParent, wmListNotify, listScrollPageUp, (LPARAM) &count ); }
if ( 0 != count ) { // count == number of lines open at the top
_cBefore -= count; if (_cBefore < 0) _cBefore = 0; InvalidateAndUpdateScroll(); } } //_GoUp
void CListView::PageUp () { _GoUp( _cLines - 1 ); } //PageUp
void CListView::_GoDown( long cToGo ) { CWaitCursor wait;
long count = cToGo; //
// Owner: Page Down!
//
SendMessage(_hwndParent, wmListNotify, listScrollPageDn, (LPARAM) &count); // count == number of lines open at the bottom
if ( 0 != count ) { _cBefore += count; if (_cBefore >= ( _cTotal - _cLines ) ) _cBefore = ( _cTotal - _cLines ); InvalidateAndUpdateScroll(); } } //_GoDown
void CListView::PageDown () { _GoDown( _cLines - 1 ); } //PageDown
void CListView::Top () { long count = _cLines; //
// Owner: Top!
//
SendMessage(_hwndParent, wmListNotify, listScrollTop, (LPARAM) &count ); _cBefore = 0; InvalidateAndUpdateScroll(); } //Top
void CListView::Bottom () { long count = _cLines; //
// Owner: Bottom!
//
SendMessage(_hwndParent, wmListNotify, listScrollBottom, (LPARAM) &count); // count == number of lines visible
_cBefore = _cTotal - count; if (_cBefore < 0) _cBefore = 0; InvalidateAndUpdateScroll(); } //Bottom
void CListView::ScrollPos (int pos) { long iRow = pos; //
// Owner: Scroll Position!
//
SendMessage(_hwndParent, wmListNotify, listScrollPos, (LPARAM) &iRow); if (iRow != -1) { _cBefore = iRow; InvalidateAndUpdateScroll(); } } //ScrollPos
//
// Message: Reset Contents
//
void CListView::ResetContents() { _cBefore = 0; _cTotal = 0; UpdateScroll();
RECT rect; GetClientRect (_hwnd, &rect); InvalidateRect (_hwnd, &rect, TRUE ); UpdateWindow (_hwnd); } //ResetContents
void CListView::InvalidateAndUpdateScroll() { RECT rect; GetClientRect (_hwnd, &rect); InvalidateRect (_hwnd, &rect, TRUE );
UpdateScroll(); } //InvalidateAndUpdateScroll
//
// Message: Insert item after iRow
//
void CListView::InsertItem (int iRow) { Win4Assert (iRow < _cLines ); RECT rect; GetClientRect (_hwnd, &rect); rect.top = (iRow + 1) * _cyLine; MyScrollWindow( _hwnd, 0, _cyLine, &rect, &rect, FALSE ); _cTotal++; UpdateWindow (_hwnd); UpdateScroll(); } //InsertItem
//
// Message: Delete item
//
void CListView::DeleteItem (int iRow) { Win4Assert (iRow < _cLines ); RECT rect;
GetClientRect (_hwnd, &rect); rect.top = (iRow + 1) * _cyLine; MyScrollWindow( _hwnd, 0, -_cyLine, &rect, &rect, FALSE );
_cTotal--; if (_cTotal < 0) _cTotal = 0;
// Invalidate the area which was
// scrolled up (the last row before scrolling), if visible
if ( _cTotal && _cTotal < _cLines ) { RefreshRow( _cTotal ); }
UpdateScroll(); } //DeleteItem
//
// Message: Invalidate item
//
void CListView::InvalidateItem (int iRow) { Win4Assert (iRow <= _cLines ); RefreshRow (iRow); UpdateWindow (_hwnd); } //InvalidateItem
//
// Message: Set count before
//
void CListView::SetCountBefore (int cBefore) { _cBefore = cBefore; SetScrollPos (_hwnd, SB_VERT, _cBefore, TRUE); } //SetCountBefore
//
// Message: Set total count
//
void CListView::SetTotalCount (int cTotal) { _cTotal = cTotal; UpdateScroll (); } //SetTotalCount
//
// Internal methods
//
void CListView::RefreshRow (int iRow) { Win4Assert ( iRow < _cLines ); RECT rect; rect.top = iRow * _cyLine; rect.left = 0; rect.bottom = rect.top + _cyLine; rect.right = _cx; InvalidateRect (_hwnd, &rect, TRUE ); } //RefreshRow
void CListView::UpdateScroll() { if (_cTotal - _cLines >= 0) { ShowScrollBar( _hwnd, SB_VERT, TRUE ); SetScrollRange (_hwnd, SB_VERT, 0, _cTotal - 1, FALSE); SetScrollPos (_hwnd, SB_VERT, _cBefore, TRUE);
// proportional scroll box
SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_PAGE; si.nPage = _cLines; SetScrollInfo( _hwnd, SB_VERT, &si, TRUE );
EnableScrollBar (_hwnd, SB_VERT, ESB_ENABLE_BOTH ); } else { _cBefore = 0;
ShowScrollBar( _hwnd, SB_VERT, FALSE ); EnableScrollBar (_hwnd, SB_VERT, ESB_DISABLE_BOTH ); } } //UpdateScroll
|