//+------------------------------------------------------------------------- // // 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: 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