00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "StdAfx.h"
00023 #include "Shareaza.h"
00024 #include "Settings.h"
00025 #include "CtrlMatch.h"
00026 #include "MatchObjects.h"
00027 #include "QueryHit.h"
00028 #include "ShellIcons.h"
00029 #include "CoolInterface.h"
00030 #include "VendorCache.h"
00031 #include "Network.h"
00032 #include "Schema.h"
00033 #include "Skin.h"
00034 #include "WndBaseMatch.h"
00035
00036 #ifdef _DEBUG
00037 #define new DEBUG_NEW
00038 #undef THIS_FILE
00039 static char THIS_FILE[] = __FILE__;
00040 #endif
00041
00042 BEGIN_MESSAGE_MAP(CMatchCtrl, CWnd)
00043 ON_WM_CREATE()
00044 ON_WM_DESTROY()
00045 ON_WM_SIZE()
00046 ON_WM_ERASEBKGND()
00047 ON_WM_PAINT()
00048 ON_WM_VSCROLL()
00049 ON_WM_LBUTTONDOWN()
00050 ON_WM_MOUSEMOVE()
00051 ON_WM_LBUTTONUP()
00052 ON_WM_LBUTTONDBLCLK()
00053 ON_WM_KEYDOWN()
00054 ON_WM_RBUTTONDOWN()
00055 ON_WM_RBUTTONUP()
00056 ON_WM_SETCURSOR()
00057 ON_WM_MOUSEWHEEL()
00058 ON_WM_HSCROLL()
00059 ON_WM_TIMER()
00060 ON_NOTIFY(HDN_ITEMCHANGEDW, IDC_MATCH_HEADER, OnChangeHeader)
00061 ON_NOTIFY(HDN_ITEMCHANGEDA, IDC_MATCH_HEADER, OnChangeHeader)
00062 ON_NOTIFY(HDN_ENDDRAG, IDC_MATCH_HEADER, OnChangeHeader)
00063 ON_NOTIFY(HDN_ITEMCLICKW, IDC_MATCH_HEADER, OnClickHeader)
00064 ON_NOTIFY(HDN_ITEMCLICKA, IDC_MATCH_HEADER, OnClickHeader)
00065 ON_WM_SETFOCUS()
00066 ON_WM_KILLFOCUS()
00067 END_MESSAGE_MAP()
00068
00069 #define HEADER_HEIGHT 20
00070 #define ITEM_HEIGHT 17
00071
00072
00074
00075
00076 CMatchCtrl::CMatchCtrl()
00077 {
00078 m_pMatches = NULL;
00079 m_sType = _T("Search");
00080
00081 m_pSchema = NULL;
00082 m_nTopIndex = 0;
00083 m_nHitIndex = 0;
00084 m_nBottomIndex = 0xFFFFFFFF;
00085 m_nFocus = 0xFFFFFFFF;
00086 m_nPageCount = 1;
00087 m_nCurrentWidth = 0;
00088 m_nMessage = 0;
00089 m_bSearchLink = FALSE;
00090 m_bTips = TRUE;
00091
00092
00093 if( !SystemParametersInfo ( SPI_GETWHEELSCROLLLINES, 0, &m_nScrollWheelLines, 0) )
00094 {
00095 m_nScrollWheelLines = 3;
00096 }
00097 }
00098
00099 CMatchCtrl::~CMatchCtrl()
00100 {
00101 }
00102
00104
00105
00106 BOOL CMatchCtrl::Create(CMatchList* pMatches, CWnd* pParentWnd)
00107 {
00108 CRect rect( 0, 0, 0, 0 );
00109 m_pMatches = pMatches;
00110 DWORD dwStyle = WS_CHILD|WS_VSCROLL|WS_TABSTOP|WS_VISIBLE;
00111 return CWnd::Create( NULL, NULL, dwStyle, rect, pParentWnd, IDC_MATCHES, NULL );
00112 }
00113
00114 int CMatchCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
00115 {
00116 if ( CWnd::OnCreate( lpCreateStruct ) == -1 ) return -1;
00117
00118 CRect rc;
00119
00120 if ( ! m_wndHeader.Create( WS_CHILD|WS_VISIBLE|HDS_BUTTONS|HDS_DRAGDROP|HDS_HOTTRACK|HDS_FULLDRAG,
00121 rc, this, IDC_MATCH_HEADER ) ) return -1;
00122 m_wndHeader.SetFont( &theApp.m_gdiFont );
00123
00124 if ( ! m_wndTip.Create( this ) ) return -1;
00125
00126 EnableToolTips( TRUE );
00127
00128 InsertColumn( MATCH_COL_NAME, _T("File"), HDF_LEFT, 200 );
00129 InsertColumn( MATCH_COL_TYPE, _T("Extension"), HDF_CENTER, 40 );
00130 InsertColumn( MATCH_COL_SIZE, _T("Size"), HDF_CENTER, 60 );
00131 InsertColumn( MATCH_COL_RATING, _T("Rating"), HDF_CENTER, 12*5 );
00132 InsertColumn( MATCH_COL_STATUS, _T("Status"), HDF_CENTER, 16*3 );
00133 InsertColumn( MATCH_COL_COUNT, _T("Host/Count"), HDF_CENTER, 120 );
00134 InsertColumn( MATCH_COL_SPEED, _T("Speed"), HDF_CENTER, 60 );
00135 InsertColumn( MATCH_COL_CLIENT, _T("Client"), HDF_CENTER, 80 );
00136
00137 CBitmap bmStar;
00138 bmStar.LoadBitmap( IDB_SMALL_STAR );
00139 if ( ! m_pStars.Create( 12, 12, ILC_COLOR16|ILC_MASK, 7, 0 ) )
00140 m_pStars.Create( 12, 12, ILC_COLOR24|ILC_MASK, 7, 0 );
00141 m_pStars.Add( &bmStar, RGB( 0, 255, 0 ) );
00142
00143 LoadColumnState();
00144
00145 UpdateScroll();
00146
00147 return 0;
00148 }
00149
00150 void CMatchCtrl::OnDestroy()
00151 {
00152 m_wndTip.DestroyWindow();
00153
00154 SaveColumnState();
00155 CWnd::OnDestroy();
00156 }
00157
00158 void CMatchCtrl::OnSize(UINT nType, int cx, int cy)
00159 {
00160 CWnd::OnSize( nType, cx, cy );
00161
00162 m_nCurrentWidth = cx;
00163
00164 m_nPageCount = ( cy - HEADER_HEIGHT ) / ITEM_HEIGHT;
00165 if ( m_nPageCount < 1 ) m_nPageCount = 1;
00166 m_nBottomIndex = 0xFFFFFFFF;
00167
00168 UpdateScroll();
00169 }
00170
00172
00173
00174 void CMatchCtrl::Update()
00175 {
00176 CSingleLock pLock( &m_pMatches->m_pSection, TRUE );
00177
00178 if ( ! m_pMatches->m_bUpdated ) return;
00179
00180 m_nCacheItems = m_pMatches->m_nItems;
00181 ScrollTo( GetScrollPos( SB_VERT ) );
00182
00183 if ( m_pMatches->m_nUpdateMax >= m_nTopIndex &&
00184 m_pMatches->m_nUpdateMin <= m_nBottomIndex )
00185 {
00186 Invalidate();
00187 }
00188
00189 m_pMatches->ClearUpdated();
00190 }
00191
00192 void CMatchCtrl::DestructiveUpdate()
00193 {
00194 m_wndTip.Hide();
00195 }
00196
00197 void CMatchCtrl::SelectSchema(CSchema* pSchema, CPtrList* pColumns)
00198 {
00199 SaveColumnState();
00200
00201 m_pSchema = pSchema;
00202 m_pColumns.RemoveAll();
00203
00204 while ( m_wndHeader.DeleteItem( MATCH_COL_MAX ) );
00205 int nColumn = MATCH_COL_MAX;
00206
00207 if ( pSchema && pColumns )
00208 {
00209 m_pColumns.AddTail( pColumns );
00210
00211 for ( POSITION pos = m_pColumns.GetHeadPosition() ; pos ; nColumn++ )
00212 {
00213 CSchemaMember* pMember = (CSchemaMember*)m_pColumns.GetNext( pos );
00214 InsertColumn( nColumn, pMember->m_sTitle, pMember->m_nColumnAlign, pMember->m_nColumnWidth );
00215 }
00216 }
00217
00218 m_pMatches->SelectSchema( pSchema, pColumns );
00219
00220 LoadColumnState();
00221
00222 Update();
00223 }
00224
00225 void CMatchCtrl::SetBrowseMode()
00226 {
00227 SaveColumnState();
00228 m_sType = _T("Browse");
00229 HDITEM pZero = { HDI_WIDTH, 0 };
00230 m_wndHeader.SetItem( MATCH_COL_STATUS, &pZero );
00231 m_wndHeader.SetItem( MATCH_COL_COUNT, &pZero );
00232 m_wndHeader.SetItem( MATCH_COL_SPEED, &pZero );
00233 m_wndHeader.SetItem( MATCH_COL_CLIENT, &pZero );
00234 LoadColumnState();
00235 }
00236
00237 BOOL CMatchCtrl::HitTestHeader(const CPoint& point)
00238 {
00239 CRect rc;
00240 m_wndHeader.GetWindowRect( &rc );
00241 return rc.PtInRect( point );
00242 }
00243
00244 void CMatchCtrl::SetSortColumn(int nColumn, BOOL bDirection)
00245 {
00246 CSingleLock pLock( &m_pMatches->m_pSection, TRUE );
00247 CWaitCursor pCursor;
00248
00249 m_pMatches->SetSortColumn( nColumn, bDirection );
00250 Update();
00251
00252 if ( m_bmSortAsc.m_hObject == NULL )
00253 {
00254 m_bmSortAsc.LoadMappedBitmap( IDB_SORT_ASC );
00255 m_bmSortDesc.LoadMappedBitmap( IDB_SORT_DESC );
00256 }
00257
00258 HDITEM pColumn = { HDI_BITMAP|HDI_FORMAT };
00259
00260 for ( int nCol = 0 ; m_wndHeader.GetItem( nCol, &pColumn ) ; nCol++ )
00261 {
00262 if ( nCol == nColumn )
00263 {
00264 pColumn.fmt |= HDF_BITMAP|HDF_BITMAP_ON_RIGHT;
00265 pColumn.hbm = (HBITMAP)( bDirection ? m_bmSortAsc.GetSafeHandle() : m_bmSortDesc.GetSafeHandle() );
00266 }
00267 else
00268 {
00269 pColumn.fmt &= ~HDF_BITMAP;
00270 pColumn.hbm = NULL;
00271 }
00272
00273 m_wndHeader.SetItem( nCol, &pColumn );
00274 }
00275 }
00276
00277 void CMatchCtrl::SetMessage(UINT nMessageID, BOOL bLink)
00278 {
00279 CString strCurrentText;
00280 Skin.LoadString( strCurrentText, m_nMessage );
00281
00282 if ( nMessageID == m_nMessage && m_bSearchLink == bLink && strCurrentText == m_sMessage ) return;
00283
00284 m_bSearchLink = bLink;
00285
00286 m_nMessage = nMessageID;
00287 m_sMessage = strCurrentText;
00288
00289 if ( m_nCacheItems == 0 ) Invalidate();
00290 }
00291
00292 void CMatchCtrl::SetMessage(LPCTSTR pszMessage, BOOL bLink)
00293 {
00294 if ( m_sMessage == pszMessage && m_bSearchLink == bLink ) return;
00295
00296 m_bSearchLink = bLink;
00297 m_nMessage = 0;
00298 m_sMessage = pszMessage;
00299
00300 if ( m_nCacheItems == 0 ) Invalidate();
00301 }
00302
00303 void CMatchCtrl::EnableTips(BOOL bTips)
00304 {
00305 m_bTips = bTips;
00306 }
00307
00309
00310
00311 void CMatchCtrl::InsertColumn(int nColumn, LPCTSTR pszCaption, int nFormat, int nWidth)
00312 {
00313 HDITEM pItem = { HDI_TEXT|HDI_FORMAT|HDI_WIDTH };
00314
00315 pItem.pszText = (LPTSTR)pszCaption;
00316 pItem.cchTextMax = _tcslen( pszCaption );
00317 pItem.fmt = nFormat;
00318 pItem.cxy = nWidth;
00319
00320 m_wndHeader.InsertItem( nColumn, &pItem );
00321 }
00322
00323 void CMatchCtrl::SaveColumnState()
00324 {
00325 HDITEM pItem = { HDI_WIDTH|HDI_ORDER };
00326
00327 CString strOrdering, strWidths, strItem;
00328
00329 for ( int nColumns = 0 ; m_wndHeader.GetItem( nColumns, &pItem ) ; nColumns++ )
00330 {
00331 m_wndHeader.GetItem( nColumns, &pItem );
00332
00333 strItem.Format( _T("%.2x"), pItem.iOrder );
00334 strOrdering += strItem;
00335
00336 strItem.Format( _T("%.4x"), pItem.cxy );
00337 strWidths += strItem;
00338 }
00339
00340 int nSort = m_pMatches->m_nSortColumn >= 0 ?
00341 ( m_pMatches->m_nSortColumn + 1 ) * m_pMatches->m_bSortDir : 0;
00342
00343 LPCTSTR pszName = _T("Null");
00344 if ( m_pSchema ) pszName = m_pSchema->m_sSingular;
00345
00346 strItem.Format( _T("CMatchCtrl.%s.%s.Ordering"), m_sType, pszName );
00347 theApp.WriteProfileString( _T("ListStates"), strItem, strOrdering );
00348 strItem.Format( _T("CMatchCtrl.%s.%s.Widths"), m_sType, pszName );
00349 theApp.WriteProfileString( _T("ListStates"), strItem, strWidths );
00350 strItem.Format( _T("CMatchCtrl.%s.%s.Sort"), m_sType, pszName );
00351 theApp.WriteProfileInt( _T("ListStates"), strItem, nSort );
00352 }
00353
00354 BOOL CMatchCtrl::LoadColumnState()
00355 {
00356 CString strOrdering, strWidths, strItem;
00357
00358 LPCTSTR pszName = _T("Null");
00359 if ( m_pSchema ) pszName = m_pSchema->m_sSingular;
00360
00361 strItem.Format( _T("CMatchCtrl.%s.%s.Ordering"), m_sType, pszName );
00362 strOrdering = theApp.GetProfileString( _T("ListStates"), strItem, _T("") );
00363 strItem.Format( _T("CMatchCtrl.%s.%s.Widths"), m_sType, pszName );
00364 strWidths = theApp.GetProfileString( _T("ListStates"), strItem, _T("") );
00365 strItem.Format( _T("CMatchCtrl.%s.%s.Sort"), m_sType, pszName );
00366 int nSort = theApp.GetProfileInt( _T("ListStates"), strItem, - MATCH_COL_COUNT - 1 );
00367
00368 HDITEM pItem = { HDI_WIDTH|HDI_ORDER };
00369
00370 if ( _tcsncmp( strWidths, _T("0000"), 4 ) == 0 &&
00371 _tcsncmp( strOrdering, _T("00"), 2 ) == 0 )
00372 {
00373 strWidths = strWidths.Mid( 4 );
00374 strOrdering = strOrdering.Mid( 2 );
00375 }
00376
00377 for ( int nColumns = 0 ; m_wndHeader.GetItem( nColumns, &pItem ) ; nColumns++ )
00378 {
00379 if ( strWidths.GetLength() < 4 || strOrdering.GetLength() < 2 ) return FALSE;
00380
00381 _stscanf( strWidths.Left( 4 ), _T("%x"), &pItem.cxy );
00382 _stscanf( strOrdering.Left( 2 ), _T("%x"), &pItem.iOrder );
00383
00384 strWidths = strWidths.Mid( 4 );
00385 strOrdering = strOrdering.Mid( 2 );
00386
00387 m_wndHeader.SetItem( nColumns, &pItem );
00388 }
00389
00390 SetSortColumn( abs( nSort ) - 1, nSort < 0 );
00391
00392 return TRUE;
00393 }
00394
00396
00397
00398 void CMatchCtrl::UpdateScroll(DWORD nScroll)
00399 {
00400 SCROLLINFO pInfo;
00401
00402 pInfo.cbSize = sizeof(pInfo);
00403 pInfo.fMask = SIF_ALL & ~SIF_TRACKPOS;
00404 pInfo.nMin = 0;
00405 pInfo.nMax = m_pMatches->m_nItems - 1;
00406 pInfo.nPage = m_nPageCount;
00407 pInfo.nPos = nScroll < 0xFFFFFFFF ? nScroll : GetScrollPos( SB_VERT );
00408 pInfo.nPos = max( 0, min( pInfo.nPos, pInfo.nMax - (int)pInfo.nPage + 1 ) );
00409
00410 SetScrollInfo( SB_VERT, &pInfo, TRUE );
00411
00412 int nColumnWidth = 0;
00413
00414 for ( int nColumn = m_wndHeader.GetItemCount() - 1 ; nColumn >= 0 ; nColumn-- )
00415 {
00416 CRect rcCol;
00417 Header_GetItemRect( m_wndHeader.GetSafeHwnd(), nColumn, &rcCol );
00418 nColumnWidth = max( nColumnWidth, int(rcCol.right) );
00419 }
00420
00421 pInfo.fMask = SIF_ALL & ~SIF_TRACKPOS;
00422 pInfo.nMin = 0;
00423 pInfo.nMax = nColumnWidth - 1;
00424 pInfo.nPage = m_nCurrentWidth;
00425 pInfo.nPos = GetScrollPos( SB_HORZ );
00426 pInfo.nPos = max( 0, min( pInfo.nPos, pInfo.nMax - (int)pInfo.nPage + 1 ) );
00427
00428 SetScrollInfo( SB_HORZ, &pInfo, TRUE );
00429
00430 CRect rc;
00431 m_wndHeader.GetWindowRect( &rc );
00432 ScreenToClient( &rc );
00433
00434 if ( rc.left != -pInfo.nPos || rc.Width() != max( m_nCurrentWidth, pInfo.nMax ) )
00435 {
00436 m_wndHeader.SetWindowPos( NULL, -pInfo.nPos, 0,
00437 max( m_nCurrentWidth, pInfo.nMax ), HEADER_HEIGHT, SWP_NOZORDER );
00438 }
00439 }
00440
00441 void CMatchCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
00442 {
00443 switch ( nSBCode )
00444 {
00445 case SB_BOTTOM:
00446 ScrollTo( 0xFFFFFFFF );
00447 break;
00448 case SB_LINEDOWN:
00449 ScrollBy( 1 );
00450 break;
00451 case SB_LINEUP:
00452 ScrollBy( -1 );
00453 break;
00454 case SB_PAGEDOWN:
00455 ScrollBy( m_nPageCount );
00456 break;
00457 case SB_PAGEUP:
00458 ScrollBy( -m_nPageCount );
00459 break;
00460 case SB_THUMBPOSITION:
00461 case SB_THUMBTRACK:
00462 ScrollTo( nPos );
00463 break;
00464 case SB_TOP:
00465 ScrollTo( 0 );
00466 break;
00467 }
00468 }
00469
00470 void CMatchCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
00471 {
00472 SCROLLINFO pInfo;
00473
00474 pInfo.cbSize = sizeof(pInfo);
00475 pInfo.fMask = SIF_ALL & ~SIF_TRACKPOS;
00476
00477 GetScrollInfo( SB_HORZ, &pInfo );
00478 int nDelta = pInfo.nPos;
00479
00480 switch ( nSBCode )
00481 {
00482 case SB_BOTTOM:
00483 pInfo.nPos = pInfo.nMax - pInfo.nPage;
00484 break;
00485 case SB_LINEDOWN:
00486 pInfo.nPos ++;
00487 break;
00488 case SB_LINEUP:
00489 pInfo.nPos --;
00490 break;
00491 case SB_PAGEDOWN:
00492 pInfo.nPos += pInfo.nPage;
00493 break;
00494 case SB_PAGEUP:
00495 pInfo.nPos -= pInfo.nPage;
00496 break;
00497 case SB_THUMBPOSITION:
00498 case SB_THUMBTRACK:
00499 pInfo.nPos = nPos;
00500 break;
00501 case SB_TOP:
00502 pInfo.nPos = 0;
00503 break;
00504 }
00505
00506 pInfo.nPos = max( 0, min( pInfo.nPos, pInfo.nMax - (int)pInfo.nPage + 1 ) );
00507 if ( pInfo.nPos == nDelta ) return;
00508
00509 SetScrollInfo( SB_HORZ, &pInfo, TRUE );
00510
00511 m_wndHeader.SetWindowPos( NULL, -pInfo.nPos, 0,
00512 max( m_nCurrentWidth, pInfo.nMax ), HEADER_HEIGHT, SWP_NOZORDER );
00513
00514 RedrawWindow( NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW );
00515 }
00516
00517 BOOL CMatchCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
00518 {
00519 ScrollBy( zDelta / WHEEL_DELTA * -m_nScrollWheelLines );
00520
00521 return TRUE;
00522 }
00523
00524 void CMatchCtrl::ScrollBy(int nDelta)
00525 {
00526 int nIndex = GetScrollPos( SB_VERT ) + nDelta;
00527 nIndex = max( 0, nIndex );
00528 ScrollTo( nIndex );
00529 }
00530
00531 void CMatchCtrl::ScrollTo(DWORD nIndex)
00532 {
00533 DWORD nLimit = m_pMatches->m_nItems;
00534 if ( nLimit > (DWORD)m_nPageCount ) nLimit -= m_nPageCount;
00535 else nLimit = 0;
00536 nIndex = min( nIndex, nLimit );
00537
00538 DWORD nScroll = 0;
00539
00540 m_nTopIndex = 0;
00541 m_nHitIndex = 0;
00542 m_nBottomIndex = 0xFFFFFFFF;
00543
00544 CMatchFile** ppFile = m_pMatches->m_pFiles;
00545
00546 for ( DWORD nFiles = 0 ; nFiles < m_pMatches->m_nFiles ; nFiles++, ppFile++ )
00547 {
00548 DWORD nCount = (*ppFile)->GetItemCount();
00549 if ( ! nCount ) continue;
00550
00551 m_nTopIndex = nFiles;
00552
00553 if ( nIndex < nCount )
00554 {
00555 m_nHitIndex = nIndex;
00556 nScroll += nIndex;
00557 break;
00558 }
00559
00560 nIndex -= nCount;
00561 nScroll += nCount;
00562 }
00563
00564 UpdateScroll( nScroll );
00565
00566 CRect rc;
00567 GetClientRect( &rc );
00568 rc.top += HEADER_HEIGHT;
00569
00570
00571 RedrawWindow( &rc, NULL, RDW_INVALIDATE );
00572 }
00573
00575
00576
00577 BOOL CMatchCtrl::OnEraseBkgnd(CDC* pDC)
00578 {
00579 return TRUE;
00580 }
00581
00582 void CMatchCtrl::OnPaint()
00583 {
00584 CSingleLock pLock( &m_pMatches->m_pSection );
00585
00586 if ( ! pLock.Lock( 80 ) )
00587 {
00588 PostMessage( WM_TIMER, 1 );
00589 return;
00590 }
00591
00592 CRect rcClient, rcItem;
00593 CPaintDC dc( this );
00594 if ( theApp.m_bRTL ) dc.SetTextAlign( TA_RTLREADING );
00595
00596 GetClientRect( &rcClient );
00597 rcClient.top += HEADER_HEIGHT;
00598
00599 dc.SetViewportOrg( -GetScrollPos( SB_HORZ ), 0 );
00600
00601 INT nZeroInt, nColWidth;
00602 GetScrollRange( SB_HORZ, &nZeroInt, &nColWidth );
00603 rcClient.right = max( rcClient.right, LONG(nColWidth) );
00604
00605 CFont* pOldFont = (CFont*)dc.SelectObject( &CoolInterface.m_fntNormal );
00606
00607 m_nTrailWidth = dc.GetTextExtent( _T('\x2026') ).cx;
00608
00609 rcItem.SetRect( rcClient.left, rcClient.top, rcClient.right, 0 );
00610 rcItem.top -= m_nHitIndex * ITEM_HEIGHT;
00611 rcItem.bottom = rcItem.top + ITEM_HEIGHT;
00612
00613 CMatchFile** ppFile = m_pMatches->m_pFiles + m_nTopIndex;
00614 BOOL bFocus = ( GetFocus() == this );
00615
00616 DWORD nIndex = m_nTopIndex;
00617 for ( ;
00618 nIndex < m_pMatches->m_nFiles && rcItem.top < rcClient.bottom ;
00619 nIndex++, ppFile++ )
00620 {
00621 CMatchFile* pFile = *ppFile;
00622 int nCount = pFile->GetFilteredCount();
00623
00624 if ( ! nCount ) continue;
00625
00626 if ( rcItem.top >= rcClient.top && dc.RectVisible( &rcItem ) )
00627 {
00628 DrawItem( dc, rcItem, pFile, NULL, bFocus && ( nIndex == m_nFocus ) );
00629 }
00630
00631 rcItem.top += ITEM_HEIGHT;
00632 rcItem.bottom += ITEM_HEIGHT;
00633
00634 if ( nCount > 1 && pFile->m_bExpanded )
00635 {
00636 for ( CQueryHit* pHit = pFile->m_pHits ; pHit ; pHit = pHit->m_pNext )
00637 {
00638 if ( ! pHit->m_bFiltered ) continue;
00639
00640 if ( rcItem.top >= rcClient.top && dc.RectVisible( &rcItem ) )
00641 {
00642 DrawItem( dc, rcItem, pFile, pHit, FALSE );
00643 }
00644
00645 rcItem.top += ITEM_HEIGHT;
00646 rcItem.bottom += ITEM_HEIGHT;
00647
00648 if ( rcItem.top >= rcClient.bottom ) break;
00649 }
00650 }
00651 }
00652
00653 m_nBottomIndex = nIndex + 1;
00654
00655 if ( m_pMatches->m_nFilteredFiles == 0 && m_sMessage.GetLength() )
00656 {
00657 dc.SetViewportOrg( 0, 0 );
00658 GetClientRect( &rcClient );
00659 rcClient.top += HEADER_HEIGHT;
00660 DrawEmptyMessage( dc, rcClient );
00661 }
00662
00663 dc.SelectObject( pOldFont );
00664
00665 rcItem.bottom = rcClient.bottom;
00666
00667 if ( dc.RectVisible( &rcItem ) )
00668 {
00669 dc.FillSolidRect( &rcItem, CoolInterface.m_crWindow );
00670 }
00671 }
00672
00673 void CMatchCtrl::DrawItem(CDC& dc, CRect& rcRow, CMatchFile* pFile, CQueryHit* pHit, BOOL bFocus)
00674 {
00675 static TCHAR szBuffer[64];
00676
00677 int nColumns = m_wndHeader.GetItemCount();
00678 int nHits = pHit ? 0 : pFile->GetFilteredCount();
00679
00680 LPCTSTR pszName = pHit ? pHit->m_sName : pFile->m_pBest->m_sName;
00681 LPCTSTR pszType = _tcsrchr( pszName, '.' );
00682 int nNameLen = pszType ? pszType - pszName : _tcslen( pszName );
00683
00684 BOOL bSelected = pHit ? pHit->m_bSelected : pFile->m_bSelected;
00685 BOOL bGrayed = FALSE;
00686 COLORREF crWnd = CoolInterface.m_crWindow;
00687 COLORREF crText = bSelected ? CoolInterface.m_crHighlight : CoolInterface.m_crText ;
00688 COLORREF crBack = crWnd;
00689
00690 if ( pFile->m_bCollection )
00691 {
00692 crWnd = crBack = CCoolInterface::CalculateColour( crBack, RGB( 0, 0, 255 ), 25 );
00693 }
00694
00695 if ( pFile->m_bExisting == 1 )
00696 {
00697 crText = pHit ? RGB( 0, 64, 0 ) : RGB( 0, 127, 0 );
00698 }
00699 else if ( pFile->m_bDownload || ( pHit && pHit->m_bDownload ) )
00700 {
00701 crText = pHit ? RGB( 0, 0, 100 ) : RGB( 0, 0, 160 );
00702 }
00703
00704 if ( bSelected )
00705 {
00706 crBack = CoolInterface.m_crBackSel;
00707 }
00708 else if ( ( pHit && ( pHit->m_bBogus || pHit->m_sURL.IsEmpty() || ! pHit->m_bMatched ) ) ||
00709 pFile->m_bExisting == 2 || ( ! pHit && ! pFile->m_bOneValid ) ||
00710 ( pHit && pHit->m_bPush == TS_TRUE && Network.IsStable() == FALSE ) )
00711 {
00712 crText = GetSysColor( COLOR_3DSHADOW );
00713 bGrayed = TRUE;
00714 }
00715
00716 dc.SetBkMode( OPAQUE );
00717 dc.SetBkColor( crBack );
00718
00719 dc.SelectObject( Settings.Search.HighlightNew && ( pHit ? pHit->m_bNew : pFile->m_bNew )
00720 ? &theApp.m_gdiFontBold : &theApp.m_gdiFont );
00721
00722 for ( int nColumn = 0 ; nColumn < nColumns ; nColumn++ )
00723 {
00724 HDITEM pColumn = { HDI_FORMAT|HDI_WIDTH|HDI_ORDER };
00725 CRect rcCol;
00726
00727 Header_GetItem( m_wndHeader.GetSafeHwnd(), nColumn, &pColumn );
00728 Header_GetItemRect( m_wndHeader.GetSafeHwnd(), nColumn, &rcCol );
00729
00730 int nLeft = rcCol.left;
00731 rcCol.top = rcRow.top;
00732 rcCol.bottom = rcRow.bottom;
00733
00734 LPCTSTR pszText = _T("");
00735 UINT nIconStyle = 0;
00736 int nText = -1;
00737 int nPosition;
00738
00739 dc.SetTextColor( crText );
00740
00741 switch ( nColumn )
00742 {
00743 case MATCH_COL_NAME:
00744 if ( rcCol.Width() < 32 ) break;
00745
00746 pszText = pszName;
00747 nText = nNameLen;
00748
00749 nIconStyle = bSelected ? ILD_SELECTED : ( bGrayed ? ILD_BLEND50 : ILD_NORMAL );
00750 if ( pFile->m_bDRM ) nIconStyle |= INDEXTOOVERLAYMASK( SHI_O_COMMERCIAL );
00751
00752 if ( ! pHit && nHits > 1 )
00753 {
00754 ImageList_DrawEx( ShellIcons.GetHandle( 16 ),
00755 pFile->m_bExpanded ? SHI_MINUS : SHI_PLUS,
00756 dc.GetSafeHdc(), rcCol.left, rcCol.top, 16, 16,
00757 crWnd, CLR_NONE, ILD_NORMAL );
00758
00759 ImageList_DrawEx( ShellIcons.GetHandle( 16 ),
00760 pFile->m_nShellIndex, dc.GetSafeHdc(), rcCol.left + 16, rcCol.top, 16, 16,
00761 crWnd, bSelected ? CLR_DEFAULT : crText,
00762 nIconStyle );
00763
00764 dc.FillSolidRect( rcCol.left, rcCol.top + 16, 32, ITEM_HEIGHT - 16, crWnd );
00765
00766 rcCol.left += 32;
00767 }
00768 else
00769 {
00770 dc.FillSolidRect( rcCol.left, rcCol.top, ( pHit ? 24 : 16 ),
00771 ITEM_HEIGHT, crWnd );
00772 rcCol.left += ( pHit ? 24 : 16 );
00773
00774 if ( ! pFile->m_bDRM )
00775 {
00776 if ( pHit && pHit->m_nPartial )
00777 nIconStyle |= INDEXTOOVERLAYMASK( SHI_O_PARTIAL );
00778 else if ( nHits == 1 && pFile->m_pBest->m_nPartial )
00779 nIconStyle |= INDEXTOOVERLAYMASK( SHI_O_PARTIAL );
00780 }
00781
00782 ImageList_DrawEx( ShellIcons.GetHandle( 16 ),
00783 pFile->m_nShellIndex, dc.GetSafeHdc(), rcCol.left, rcCol.top, 16, 16,
00784 crWnd, bSelected ? CLR_DEFAULT : crText,
00785 nIconStyle );
00786
00787 dc.FillSolidRect( rcCol.left, rcCol.top + 16, 16, ITEM_HEIGHT - 16, crWnd );
00788
00789 rcCol.left += 16;
00790 }
00791
00792 dc.FillSolidRect( rcCol.left, rcCol.top, 1, ITEM_HEIGHT, crWnd );
00793 rcCol.left += 1;
00794
00795 if ( bSelected && bFocus )
00796 {
00797 CRect rcFocus( &rcRow );
00798 rcFocus.left = rcCol.left;
00799 dc.Draw3dRect( &rcFocus, CoolInterface.m_crBorder, CoolInterface.m_crBorder );
00800 dc.ExcludeClipRect( rcFocus.left, rcFocus.top, rcFocus.right, rcFocus.top + 1 );
00801 dc.ExcludeClipRect( rcFocus.left, rcFocus.bottom - 1, rcFocus.right, rcFocus.bottom );
00802 dc.ExcludeClipRect( rcFocus.left, rcFocus.top + 1, rcFocus.left + 1, rcFocus.bottom - 1 );
00803 dc.ExcludeClipRect( rcFocus.right - 1, rcFocus.top + 1, rcFocus.right, rcFocus.bottom - 1 );
00804 }
00805
00806 break;
00807
00808 case MATCH_COL_TYPE:
00809 if ( pszType ) pszText = pszType + 1;
00810 break;
00811
00812 case MATCH_COL_SIZE:
00813 pszText = pFile->m_sSize;
00814 break;
00815
00816 case MATCH_COL_STATUS:
00817 if ( pHit )
00818 {
00819 DrawStatus( dc, rcCol, pFile, pHit, bSelected, crBack );
00820 }
00821 else if ( nHits == 1 )
00822 {
00823 DrawStatus( dc, rcCol, pFile, pFile->m_pBest, bSelected, crBack );
00824 }
00825 else
00826 {
00827 DrawStatus( dc, rcCol, pFile, NULL, bSelected, crBack );
00828 }
00829 break;
00830
00831 case MATCH_COL_RATING:
00832 if ( pHit )
00833 {
00834 DrawRating( dc, rcCol, pHit->m_nRating, bSelected, crBack );
00835 }
00836 else if ( nHits == 1 )
00837 {
00838 DrawRating( dc, rcCol, pFile->m_pBest->m_nRating, bSelected, crBack );
00839 }
00840 else
00841 {
00842 DrawRating( dc, rcCol,
00843 pFile->m_nRated ? pFile->m_nRating / pFile->m_nRated : 0,
00844 bSelected, crBack );
00845 }
00846 break;
00847
00848 case MATCH_COL_COUNT:
00849 if ( nHits == 1 || pHit != NULL )
00850 {
00851 CQueryHit* ppHit = ( nHits == 1 || pHit == NULL ) ? pFile->m_pBest : pHit;
00852
00853 if ( Settings.Search.ShowNames && ppHit->m_sNick.GetLength() )
00854 {
00855 if ( ppHit->m_nSources > 1 )
00856 {
00857 _sntprintf( szBuffer, sizeof( szBuffer ) / sizeof( TCHAR ), _T("%s+%u"),
00858 (LPCTSTR)ppHit->m_sNick,
00859 ppHit->m_nSources - 1 );
00860 szBuffer[ sizeof( szBuffer ) / sizeof( TCHAR ) - 1 ] = 0;
00861 pszText = szBuffer;
00862 }
00863 else
00864 {
00865 pszText = ppHit->m_sNick;
00866 }
00867 }
00868 else if( ( ppHit->m_nProtocol == PROTOCOL_ED2K ) && ( ppHit->m_bPush == TS_TRUE ) )
00869 {
00870 if ( ppHit->m_nSources > 1 )
00871 {
00872
00873
00874
00875 _sntprintf( szBuffer, sizeof( szBuffer ) / sizeof( TCHAR ), _T("(%s)+%u"),
00876 (LPCTSTR)CString( inet_ntoa( (IN_ADDR&)ppHit->m_pClientID.w[0] ) ), ppHit->m_nSources - 1 );
00877 szBuffer[ sizeof( szBuffer ) / sizeof( TCHAR ) - 1 ] = 0;
00878 }
00879 else
00880 {
00881
00882
00883 _sntprintf( szBuffer, sizeof( szBuffer ) / sizeof( TCHAR ), _T("(%s)"),
00884 (LPCTSTR)CString( inet_ntoa( (IN_ADDR&)ppHit->m_pClientID.w[0] ) ) );
00885 szBuffer[ sizeof( szBuffer ) / sizeof( TCHAR ) - 1 ] = 0;
00886 }
00887 pszText = szBuffer;
00888
00889 }
00890 else if ( ppHit->m_pAddress.S_un.S_addr )
00891 {
00892 if ( ppHit->m_nSources > 1 )
00893 {
00894 _sntprintf( szBuffer, sizeof( szBuffer ) / sizeof( TCHAR ), _T("%s+%u"),
00895 (LPCTSTR)CString( inet_ntoa( ppHit->m_pAddress ) ),
00896 ppHit->m_nSources - 1 );
00897 szBuffer[ sizeof( szBuffer ) / sizeof( TCHAR ) - 1 ] = 0;
00898 pszText = szBuffer;
00899 }
00900 else
00901 {
00902 MultiByteToWideChar( CP_ACP, 0, inet_ntoa( ppHit->m_pAddress ), -1, szBuffer, 64 );
00903 pszText = szBuffer;
00904 }
00905 }
00906 else
00907 {
00908 if ( ppHit->m_nSources )
00909 {
00910 CString strSource, strText;
00911 LoadSourcesString( strSource, pFile->m_nSources );
00912 strText.Format( _T("(%u %s)"), pFile->m_nSources, strSource );
00913 _sntprintf( szBuffer, sizeof( szBuffer ) / sizeof( TCHAR ), strText, pFile->m_nSources );
00914 szBuffer[ sizeof( szBuffer ) / sizeof( TCHAR ) - 1 ] = 0;
00915 }
00916 else
00917 {
00918
00919 pszText = _T("(Firewalled)");
00920 }
00921 }
00922 }
00923 else
00924 {
00925 CString strSource, strText;
00926 LoadSourcesString( strSource, pFile->m_nSources );
00927 strText.Format( _T("(%u %s)"), pFile->m_nSources, strSource );
00928 _sntprintf( szBuffer, sizeof( szBuffer ) / sizeof( TCHAR ), strText, pFile->m_nSources );
00929 szBuffer[ sizeof( szBuffer ) / sizeof( TCHAR ) - 1 ] = 0;
00930 pszText = szBuffer;
00931 }
00932 break;
00933
00934 case MATCH_COL_SPEED:
00935 if ( pHit )
00936 {
00937 if ( ! bSelected && pHit->m_bMeasured == TS_TRUE ) dc.SetTextColor( RGB( 0, 127, 0 ) );
00938 pszText = pHit->m_sSpeed;
00939 }
00940 else
00941 {
00942 if ( ! bSelected && pFile->m_pBest->m_bMeasured == TS_TRUE ) dc.SetTextColor( RGB( 0, 127, 0 ) );
00943 pszText = pFile->m_sSpeed;
00944 }
00945 break;
00946
00947 case MATCH_COL_CLIENT:
00948 if ( pHit )
00949 {
00950 if ( ! bSelected && pHit->m_bBrowseHost ) dc.SetTextColor( RGB( 0, 127, 0 ) );
00951 pszText = pHit->m_pVendor->m_sName;
00952 }
00953 else if ( nHits == 1 )
00954 {
00955 if ( ! bSelected && pFile->m_pBest->m_bBrowseHost ) dc.SetTextColor( RGB( 0, 127, 0 ) );
00956 pszText = pFile->m_pBest->m_pVendor->m_sName;
00957 }
00958 break;
00959
00960 default:
00961 if ( pFile->m_pColumns == NULL ) break;
00962 pszText = pFile->m_pColumns[ nColumn - MATCH_COL_MAX ];
00963 nText = _tcslen( pszText );
00964 nText = min( nText, 128 );
00965 break;
00966
00967 }
00968
00969 if ( nText < 0 ) nText = _tcslen( pszText );
00970 int nWidth = 0;
00971 int nTrail = 0;
00972
00973 while ( nText )
00974 {
00975 nWidth = dc.GetTextExtent( pszText, nText ).cx + nTrail;
00976 if ( nWidth <= rcCol.Width() - 4 ) break;
00977 nTrail = m_nTrailWidth;
00978 nText--;
00979 }
00980
00981 switch ( pColumn.fmt & HDF_JUSTIFYMASK )
00982 {
00983 default:
00984 nPosition = rcCol.left + 4;
00985 break;
00986 case HDF_CENTER:
00987 nPosition = ( rcCol.left + rcCol.right ) / 2 - nWidth / 2;
00988 break;
00989 case HDF_RIGHT:
00990 nPosition = rcCol.right - 4 - nWidth;
00991 break;
00992 }
00993
00994 dc.SetBkColor( crBack );
00995
00996 if ( nTrail )
00997 {
00998 CString strTrail;
00999 LPTSTR pszTrail = strTrail.GetBuffer( nText + 1 );
01000 CopyMemory( pszTrail, pszText, nText * sizeof(TCHAR) );
01001 pszTrail[ nText ] = _T('\x2026');
01002 strTrail.ReleaseBuffer( nText + 1 );
01003 dc.ExtTextOut( nPosition, rcCol.top + 2, ETO_CLIPPED|ETO_OPAQUE,
01004 &rcCol, strTrail, nText + 1, NULL );
01005 }
01006 else
01007 {
01008 dc.ExtTextOut( nPosition, rcCol.top + 2, ETO_CLIPPED|ETO_OPAQUE,
01009 &rcCol, pszText, nText, NULL );
01010 }
01011
01012 dc.ExcludeClipRect( nLeft, rcCol.top, rcCol.right, rcCol.bottom );
01013 }
01014
01015 dc.FillSolidRect( &rcRow, crBack );
01016 }
01017
01018 void CMatchCtrl::DrawStatus(CDC& dc, CRect& rcCol, CMatchFile* pFile, CQueryHit* pHit, BOOL bSelected, COLORREF crBack)
01019 {
01020 if ( rcCol.Width() < 16 * 3 ) return;
01021
01022 int nLeft = rcCol.left;
01023
01024 if ( rcCol.Width() > 16 * 6 )
01025 nLeft = ( rcCol.left + rcCol.right ) / 2 - ( 16 * 6 ) / 2;
01026
01027 int nPos = nLeft;
01028 TRISTATE bState;
01029
01030 if ( bState = pHit ? pHit->m_bBusy : pFile->m_bBusy )
01031 {
01032 ImageList_DrawEx( ShellIcons.GetHandle( 16 ),
01033 bState == TS_TRUE ? SHI_BUSY : SHI_TICK, dc.GetSafeHdc(), nPos,
01034 rcCol.top, 16, 16, crBack, crBack, bSelected ? ILD_BLEND50 : ILD_NORMAL );
01035 }
01036 else
01037 {
01038 dc.FillSolidRect( nPos, rcCol.top, 16, 16, crBack );
01039 }
01040
01041 nPos += 16;
01042
01043 if ( bState = pHit ? pHit->m_bPush : pFile->m_bPush )
01044 {
01045 ImageList_DrawEx( ShellIcons.GetHandle( 16 ),
01046 bState == TS_TRUE ? SHI_FIREWALL : SHI_TICK, dc.GetSafeHdc(), nPos,
01047 rcCol.top, 16, 16, crBack, crBack, bSelected ? ILD_BLEND50 : ILD_NORMAL );
01048 }
01049 else
01050 {
01051 dc.FillSolidRect( nPos, rcCol.top, 16, 16, crBack );
01052 }
01053
01054 nPos += 16;
01055
01056 if ( bState = pHit ? pHit->m_bStable : pFile->m_bStable )
01057 {
01058 ImageList_DrawEx( ShellIcons.GetHandle( 16 ),
01059 bState == TS_TRUE ? SHI_TICK : SHI_UNSTABLE, dc.GetSafeHdc(), nPos,
01060 rcCol.top, 16, 16, crBack, crBack, bSelected ? ILD_BLEND50 : ILD_NORMAL );
01061 }
01062 else
01063 {
01064 dc.FillSolidRect( nPos, rcCol.top, 16, 16, crBack );
01065 }
01066
01067 nPos += 16;
01068
01069 if ( nPos + 16 < rcCol.right )
01070 {
01071 if ( pHit ? pHit->m_bPreview : pFile->m_bPreview )
01072 {
01073 ImageList_DrawEx( ShellIcons.GetHandle( 16 ),
01074 SHI_PREVIEW, dc.GetSafeHdc(), nPos,
01075 rcCol.top, 16, 16, crBack, crBack, bSelected ? ILD_BLEND50 : ILD_NORMAL );
01076 }
01077 else
01078 {
01079 dc.FillSolidRect( nPos, rcCol.top, 16, 16, crBack );
01080 }
01081
01082 nPos += 16;
01083 }
01084
01085 if ( nPos + 16 < rcCol.right && pHit )
01086 {
01087 if ( pHit->m_bBrowseHost )
01088 {
01089 ImageList_DrawEx( ShellIcons.GetHandle( 16 ),
01090 SHI_BROWSE, dc.GetSafeHdc(), nPos,
01091 rcCol.top, 16, 16, crBack, crBack, bSelected ? ILD_BLEND50 : ILD_NORMAL );
01092 }
01093 else
01094 {
01095 dc.FillSolidRect( nPos, rcCol.top, 16, 16, crBack );
01096 }
01097
01098 nPos += 16;
01099 }
01100
01101 if ( nPos + 16 < rcCol.right && pHit )
01102 {
01103 if ( pHit->m_bChat )
01104 {
01105 ImageList_DrawEx( ShellIcons.GetHandle( 16 ),
01106 SHI_CHAT, dc.GetSafeHdc(), nPos,
01107 rcCol.top, 16, 16, crBack, crBack, bSelected ? ILD_BLEND50 : ILD_NORMAL );
01108 }
01109 else
01110 {
01111 dc.FillSolidRect( nPos, rcCol.top, 16, 16, crBack );
01112 }
01113
01114 nPos += 16;
01115 }
01116
01117 dc.ExcludeClipRect( nLeft, rcCol.top, nPos, rcCol.top + 16 );
01118 }
01119
01120 void CMatchCtrl::DrawRating(CDC& dc, CRect& rcCol, int nRating, BOOL bSelected, COLORREF crBack)
01121 {
01122 if ( nRating > 1 && nRating <= 6 )
01123 {
01124 CPoint pt( rcCol.left, rcCol.top + 2 );
01125
01126 if ( rcCol.Width() >= 12 * 5 )
01127 {
01128 pt.x += rcCol.Width() / 2;
01129 pt.x -= 6 * ( --nRating );
01130 }
01131 else
01132 {
01133 nRating = min( nRating - 1, rcCol.Width() / 12 );
01134 }
01135
01136 while ( nRating-- )
01137 {
01138 ImageList_DrawEx( m_pStars, 0, dc, pt.x, pt.y, 12, 12, crBack,
01139 crBack, bSelected ? ILD_BLEND50 : ILD_NORMAL );
01140 dc.ExcludeClipRect( pt.x, pt.y, pt.x + 12, pt.y + 12 );
01141 pt.x += 12;
01142 }
01143 }
01144 else if ( nRating == 1 && rcCol.Width() > 12 )
01145 {
01146 CPoint pt( ( rcCol.left + rcCol.right ) / 2 - 6, rcCol.top + 2 );
01147 ImageList_DrawEx( m_pStars, 6, dc, pt.x, pt.y, 12, 12, crBack,
01148 crBack, bSelected ? ILD_BLEND50 : ILD_NORMAL );
01149 dc.ExcludeClipRect( pt.x, pt.y, pt.x + 12, pt.y + 12 );
01150 }
01151
01152 dc.FillSolidRect( &rcCol, crBack );
01153 }
01154
01155 void CMatchCtrl::DrawEmptyMessage(CDC& dc, CRect& rcClient)
01156 {
01157 CPoint ptText;
01158 CRect rcText;
01159 CSize szText;
01160
01161 rcText.SetRect( rcClient.left, 16, rcClient.right, 0 );
01162 rcText.bottom = ( rcClient.top + rcClient.bottom ) / 2;
01163 rcText.top = rcText.bottom - rcText.top;
01164
01165 if ( ! m_bSearchLink ) rcText.OffsetRect( 0, rcText.Height() / 2 );
01166
01167 dc.SetBkMode( TRANSPARENT );
01168 dc.SetBkColor( CoolInterface.m_crWindow );
01169 dc.SetTextColor( CoolInterface.m_crText );
01170 dc.SelectObject( &theApp.m_gdiFont );
01171
01172 szText = dc.GetTextExtent( m_sMessage );
01173 ptText.x = ( rcText.left + rcText.right ) / 2 - szText.cx / 2;
01174 ptText.y = ( rcText.top + rcText.bottom ) / 2 - szText.cy / 2;
01175
01176 dc.ExtTextOut( ptText.x, ptText.y, ETO_CLIPPED|ETO_OPAQUE, &rcText, m_sMessage, NULL );
01177 dc.ExcludeClipRect( &rcText );
01178
01179 if ( m_bSearchLink )
01180 {
01181 CString strText;
01182 Skin.LoadString( strText, IDS_SEARCH_AGAIN );
01183
01184 rcText.OffsetRect( 0, rcText.Height() );
01185
01186 dc.SelectObject( &theApp.m_gdiFontLine );
01187 dc.SetTextColor( RGB( 0, 0, 255 ) );
01188
01189 szText = dc.GetTextExtent( strText );
01190 ptText.x = ( rcText.left + rcText.right ) / 2 - szText.cx / 2;
01191 ptText.y = ( rcText.top + rcText.bottom ) / 2 - szText.cy / 2;
01192
01193 dc.ExtTextOut( ptText.x, ptText.y, ETO_CLIPPED|ETO_OPAQUE, &rcText, strText, NULL );
01194 dc.ExcludeClipRect( &rcText );
01195 }
01196 }
01197
01199
01200
01201 BOOL CMatchCtrl::HitTest(const CPoint& point, CMatchFile** poFile, CQueryHit** poHit, DWORD* pnIndex, CRect* pRect)
01202 {
01203 CSingleLock pLock( &m_pMatches->m_pSection );
01204 CRect rcClient, rcItem;
01205
01206 if ( poFile ) *poFile = NULL;
01207 if ( poHit ) *poHit = NULL;
01208 if ( pnIndex ) *pnIndex = 0xFFFFFFFF;
01209
01210 if ( ! pLock.Lock( 10 ) ) return FALSE;
01211
01212 GetClientRect( &rcClient );
01213 rcClient.top += HEADER_HEIGHT;
01214
01215 rcItem.SetRect( rcClient.left, rcClient.top, rcClient.right, 0 );
01216 rcItem.top -= m_nHitIndex * ITEM_HEIGHT;
01217 rcItem.bottom = rcItem.top + ITEM_HEIGHT;
01218
01219 CMatchFile** ppFile = m_pMatches->m_pFiles + m_nTopIndex;
01220
01221 for ( DWORD nIndex = m_nTopIndex ;
01222 nIndex < m_pMatches->m_nFiles && rcItem.top < rcClient.bottom ;
01223 nIndex++, ppFile++ )
01224 {
01225 CMatchFile* pFile = *ppFile;
01226 int nCount = pFile->GetFilteredCount();
01227
01228 if ( ! nCount ) continue;
01229
01230 if ( rcItem.top >= rcClient.top && rcItem.PtInRect( point ) )
01231 {
01232 *poFile = pFile;
01233 if ( pnIndex ) *pnIndex = nIndex;
01234 if ( pRect ) *pRect = rcItem;
01235 return TRUE;
01236 }
01237
01238 rcItem.top += ITEM_HEIGHT;
01239 rcItem.bottom += ITEM_HEIGHT;
01240
01241 if ( nCount > 1 && pFile->m_bExpanded )
01242 {
01243 for ( CQueryHit* pHit = pFile->m_pHits ; pHit ; pHit = pHit->m_pNext )
01244 {
01245 if ( ! pHit->m_bFiltered ) continue;
01246
01247 if ( rcItem.top >= rcClient.top && rcItem.PtInRect( point ) )
01248 {
01249 *poFile = pFile;
01250 *poHit = pHit;
01251 if ( pnIndex ) *pnIndex = nIndex;
01252 if ( pRect ) *pRect = rcItem;
01253 return TRUE;
01254 }
01255
01256 rcItem.top += ITEM_HEIGHT;
01257 rcItem.bottom += ITEM_HEIGHT;
01258
01259 if ( rcItem.top >= rcClient.bottom ) break;
01260 }
01261 }
01262 }
01263
01264 return FALSE;
01265 }
01266
01267 BOOL CMatchCtrl::GetItemRect(CMatchFile* pFindFile, CQueryHit* pFindHit, CRect* pRect)
01268 {
01269 CSingleLock pLock( &m_pMatches->m_pSection );
01270 CRect rcClient, rcItem;
01271
01272 if ( ! pLock.Lock( 10 ) ) return FALSE;
01273
01274 GetClientRect( &rcClient );
01275 rcClient.top += HEADER_HEIGHT;
01276
01277 rcItem.SetRect( rcClient.left, rcClient.top, rcClient.right, rcClient.top + ITEM_HEIGHT );
01278 rcItem.top -= m_nHitIndex * ITEM_HEIGHT;
01279 rcItem.bottom = rcItem.top + ITEM_HEIGHT;
01280
01281 if ( m_nTopIndex > 0 )
01282 {
01283 CMatchFile** ppFile = m_pMatches->m_pFiles + m_nTopIndex - 1;
01284
01285 for ( DWORD nIndex = m_nTopIndex ; nIndex ; nIndex--, ppFile-- )
01286 {
01287 CMatchFile* pFile = *ppFile;
01288 int nCount = pFile->GetFilteredCount();
01289
01290 if ( ! nCount ) continue;
01291
01292 rcItem.top -= ITEM_HEIGHT;
01293 rcItem.bottom -= ITEM_HEIGHT;
01294
01295 if ( nCount > 1 && pFile->m_bExpanded )
01296 {
01297 for ( CQueryHit* pHit = pFile->m_pHits ; pHit ; pHit = pHit->m_pNext )
01298 {
01299 if ( ! pHit->m_bFiltered ) continue;
01300
01301 rcItem.top -= ITEM_HEIGHT;
01302 rcItem.bottom -= ITEM_HEIGHT;
01303 }
01304 }
01305 }
01306 }
01307
01308 CMatchFile** ppFile = ppFile = m_pMatches->m_pFiles;
01309
01310 for ( DWORD nIndex = m_pMatches->m_nFiles ; nIndex ; nIndex--, ppFile++ )
01311 {
01312 CMatchFile* pFile = *ppFile;
01313 int nCount = pFile->GetFilteredCount();
01314
01315 if ( ! nCount ) continue;
01316
01317 if ( pFile == pFindFile )
01318 {
01319 *pRect = rcItem;
01320 return TRUE;
01321 }
01322
01323 rcItem.top += ITEM_HEIGHT;
01324 rcItem.bottom += ITEM_HEIGHT;
01325
01326 if ( nCount > 1 && pFile->m_bExpanded )
01327 {
01328 for ( CQueryHit* pHit = pFile->m_pHits ; pHit ; pHit = pHit->m_pNext )
01329 {
01330 if ( ! pHit->m_bFiltered ) continue;
01331
01332 if ( pHit == pFindHit )
01333 {
01334 *pRect = rcItem;
01335 return TRUE;
01336 }
01337
01338 rcItem.top += ITEM_HEIGHT;
01339 rcItem.bottom += ITEM_HEIGHT;
01340 }
01341 }
01342 }
01343
01344 return FALSE;
01345 }
01346
01347 void CMatchCtrl::OnLButtonDown(UINT nFlags, CPoint point)
01348 {
01349 CSingleLock pLock( &m_pMatches->m_pSection, TRUE );
01350 CMatchFile* pFile;
01351 CQueryHit* pHit;
01352 DWORD nIndex;
01353 CRect rcItem;
01354
01355 SetFocus();
01356 SetCapture();
01357 m_wndTip.Hide();
01358
01359 HitTest( point, &pFile, &pHit, &nIndex, &rcItem );
01360
01361 if ( pFile != NULL && pHit == NULL && pFile->GetFilteredCount() > 1 )
01362 {
01363 CRect rcHeader;
01364 Header_GetItemRect( m_wndHeader.GetSafeHwnd(), 0, &rcHeader );
01365
01366 point.x += GetScrollPos( SB_HORZ );
01367
01368 if ( point.x >= rcHeader.left && point.x <= rcHeader.left + 16 )
01369 {
01370 pFile->Expand( ! pFile->m_bExpanded );
01371 NotifySelection();
01372 Update();
01373 return;
01374 }
01375
01376 point.x -= GetScrollPos( SB_HORZ );
01377 }
01378
01379 BOOL bChanged = FALSE;
01380
01381 if ( ( nFlags & MK_SHIFT ) == 0 && ( nFlags & MK_CONTROL ) == 0 &&
01382 ( nFlags & MK_RBUTTON ) == 0 )
01383 {
01384 bChanged |= m_pMatches->ClearSelection();
01385 m_nFocus = nIndex;
01386 }
01387
01388 if ( pFile != NULL )
01389 {
01390 BOOL bSelected = ( pHit != NULL ) ? pHit->m_bSelected : pFile->m_bSelected;
01391
01392 if ( nFlags & MK_RBUTTON )
01393 {
01394 if ( ! bSelected )
01395 {
01396 m_pMatches->ClearSelection();
01397 m_pMatches->Select( pFile, pHit, TRUE );
01398 m_nFocus = nIndex;
01399 }
01400 }
01401 else
01402 {
01403 bChanged |= m_pMatches->Select( pFile, pHit, ! bSelected );
01404 }
01405 }
01406
01407 if ( bChanged ) NotifySelection();
01408 Update();
01409 }
01410
01411 void CMatchCtrl::OnMouseMove(UINT nFlags, CPoint point)
01412 {
01413 CRect rcCol;
01414
01415 GetClientRect( &rcCol );
01416 rcCol.top += HEADER_HEIGHT;
01417
01418 if ( m_bTips && rcCol.PtInRect( point ) && point.x >= rcCol.left + 16 )
01419 {
01420 CSingleLock pLock( &m_pMatches->m_pSection );
01421 if ( ! pLock.Lock( 100 ) ) return;
01422
01423 CMatchFile* pFile;
01424 CQueryHit* pHit;
01425
01426 if ( HitTest( point, &pFile, &pHit ) && PixelTest( point ) )
01427 {
01428 m_wndTip.Show( pFile, pHit );
01429 return;
01430 }
01431 }
01432
01433 m_wndTip.Hide();
01434 }
01435
01436 BOOL CMatchCtrl::PixelTest(const CPoint& point)
01437 {
01438 POINT pNESW[4] = { { 0, -1 }, { 1, 0 }, { 0, 1 }, { -1, 0 } };
01439 CClientDC dc( this );
01440 COLORREF crEmpty;
01441 CRect rc;
01442
01443 crEmpty = CoolInterface.m_crWindow;
01444 if ( dc.GetPixel( point ) != crEmpty ) return TRUE;
01445 GetClientRect( &rc );
01446
01447 for ( int nDirection = 0 ; nDirection < 4 ; nDirection++ )
01448 {
01449 CPoint pt( point );
01450
01451 for ( int nLength = 16 ; nLength ; nLength-- )
01452 {
01453 pt += pNESW[ nDirection ];
01454 if ( ! rc.PtInRect( pt ) ) break;
01455 if ( dc.GetPixel( pt ) != crEmpty ) return TRUE;
01456 }
01457 }
01458
01459 return FALSE;
01460 }
01461
01462 void CMatchCtrl::OnLButtonUp(UINT nFlags, CPoint point)
01463 {
01464 ReleaseCapture();
01465
01466 if ( m_pMatches->m_nFilteredFiles == 0 && m_bSearchLink )
01467 {
01468 CRect rc;
01469
01470 GetClientRect( &rc );
01471 rc.top += HEADER_HEIGHT;
01472
01473 rc.left = ( rc.left + rc.right ) / 2 - 64;
01474 rc.right = rc.left + 128;
01475 rc.top = ( rc.top + rc.bottom ) / 2;
01476 rc.bottom = rc.top + 16;
01477
01478 if ( rc.PtInRect( point ) )
01479 {
01480 GetOwner()->PostMessage( WM_COMMAND, ID_SEARCH_SEARCH );
01481 return;
01482 }
01483 }
01484 }
01485
01486 void CMatchCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
01487 {
01488 if ( point.x < 16 )
01489 {
01490 OnLButtonDown( nFlags, point );
01491 }
01492 else
01493 {
01494 CMatchFile* pFile = NULL;
01495 CQueryHit* pHit = NULL;
01496 CRect rcItem;
01497
01498 if ( HitTest( point, &pFile, &pHit, NULL, &rcItem ) )
01499 {
01500
01501 }
01502
01503 GetOwner()->PostMessage( WM_COMMAND, ID_SEARCH_DOWNLOAD );
01504 }
01505 }
01506
01507 void CMatchCtrl::OnRButtonDown(UINT nFlags, CPoint point)
01508 {
01509 OnLButtonDown( nFlags, point );
01510 CWnd::OnRButtonDown( nFlags, point );
01511 }
01512
01513 void CMatchCtrl::OnRButtonUp(UINT nFlags, CPoint point)
01514 {
01515 OnLButtonUp( nFlags, point );
01516 CWnd::OnRButtonUp( nFlags, point );
01517 }
01518
01519 BOOL CMatchCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
01520 {
01521 if ( m_pMatches->m_nFilteredFiles == 0 && m_bSearchLink )
01522 {
01523 CPoint point;
01524 CRect rc;
01525
01526 GetClientRect( &rc );
01527 rc.top += HEADER_HEIGHT;
01528
01529 rc.left = ( rc.left + rc.right ) / 2 - 64;
01530 rc.right = rc.left + 128;
01531 rc.top = ( rc.top + rc.bottom ) / 2;
01532 rc.bottom = rc.top + 16;
01533 ClientToScreen( &rc );
01534
01535 GetCursorPos( &point );
01536
01537 if ( rc.PtInRect( point ) )
01538 {
01539 SetCursor( theApp.LoadCursor( IDC_HAND ) );
01540 return TRUE;
01541 }
01542 }
01543
01544 return CWnd::OnSetCursor( pWnd, nHitTest, message );
01545 }
01546
01548
01549
01550 void CMatchCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
01551 {
01552 BOOL bShift = ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) == 0x8000;
01553
01554 m_wndTip.Hide();
01555
01556 switch ( nChar )
01557 {
01558 case VK_ESCAPE:
01559 if ( m_pMatches->ClearSelection() ) NotifySelection();
01560 Update();
01561 return;
01562 case VK_HOME:
01563 MoveFocus( -(int)m_pMatches->m_nItems, bShift );
01564 return;
01565 case VK_END:
01566 MoveFocus( m_pMatches->m_nItems, bShift );
01567 return;
01568 case VK_PRIOR:
01569 MoveFocus( -m_nPageCount, bShift );
01570 return;
01571 case VK_NEXT:
01572 MoveFocus( m_nPageCount, bShift );
01573 return;
01574 case VK_UP:
01575 MoveFocus( -1, bShift );
01576 return;
01577 case VK_DOWN:
01578 MoveFocus( 1, bShift );
01579 return;
01580 case VK_RETURN:
01581 GetOwner()->PostMessage( WM_COMMAND, ID_SEARCH_DOWNLOAD );
01582 return;
01583 case VK_DELETE:
01584 DoDelete();
01585 return;
01586 case VK_LEFT:
01587 case VK_SUBTRACT:
01588 DoExpand( FALSE );
01589 break;
01590 case VK_RIGHT:
01591 case VK_ADD:
01592 DoExpand( TRUE );
01593 break;
01594 case VK_TAB:
01595 if ( CBaseMatchWnd* pOwner = (CBaseMatchWnd*)GetOwner() )
01596 {
01597 if ( pOwner->IsKindOf( RUNTIME_CLASS(CBaseMatchWnd) ) )
01598 {
01599 pOwner->m_wndFilter.SetFocus();
01600 }
01601 }
01602 break;
01603 }
01604
01605 CWnd::OnKeyDown( nChar, nRepCnt, nFlags );
01606 }
01607
01608 void CMatchCtrl::MoveFocus(int nDelta, BOOL bShift)
01609 {
01610 CSingleLock pLock( &m_pMatches->m_pSection, TRUE );
01611
01612 if ( m_pMatches->m_nFiles == 0 || nDelta == 0 ) return;
01613
01614 if ( m_nFocus >= m_pMatches->m_nFiles )
01615 {
01616 m_nFocus = nDelta > 0 ? 0 : m_pMatches->m_nFiles - 1;
01617 }
01618
01619 CMatchFile** ppFile = m_pMatches->m_pFiles + m_nFocus;
01620 int nSign = ( nDelta > 0 ) ? 1 : -1;
01621
01622 for ( ; m_nFocus < m_pMatches->m_nFiles
01623 ; m_nFocus += nSign, ppFile += nSign )
01624 {
01625 CMatchFile* pFile = *ppFile;
01626 if ( pFile->GetItemCount() ) break;
01627 }
01628
01629 if ( m_nFocus >= m_pMatches->m_nFiles )
01630 {
01631 m_nFocus = nDelta > 0 ? m_pMatches->m_nFiles - 1 : 0;
01632 return;
01633 }
01634
01635 CMatchFile* pFocus = NULL;
01636 nDelta += nSign;
01637
01638 for ( DWORD nPosition = m_nFocus ;
01639 nPosition < m_pMatches->m_nFiles && nDelta != 0 ;
01640 nPosition += nSign, ppFile += nSign )
01641 {
01642 CMatchFile* pFile = *ppFile;
01643
01644 if ( pFile->GetItemCount() )
01645 {
01646 m_nFocus = nPosition;
01647 pFocus = pFile;
01648 nDelta -= nSign;
01649 }
01650 }
01651
01652 if ( pFocus != NULL )
01653 {
01654 CRect rcItem, rcClient;
01655 BOOL bChanged = FALSE;
01656
01657 if ( ! bShift ) bChanged |= m_pMatches->ClearSelection();
01658 bChanged |= m_pMatches->Select( pFocus, NULL, TRUE );
01659
01660 if ( GetItemRect( pFocus, NULL, &rcItem ) )
01661 {
01662 GetClientRect( &rcClient );
01663 rcClient.top += HEADER_HEIGHT;
01664
01665 if ( rcItem.top < rcClient.top )
01666 {
01667 ScrollBy( ( rcItem.top - rcClient.top - ITEM_HEIGHT + 1 ) / ITEM_HEIGHT );
01668 }
01669 else if ( rcItem.bottom > rcClient.bottom )
01670 {
01671 ScrollBy( ( rcItem.bottom - rcClient.bottom + ITEM_HEIGHT - 1 ) / ITEM_HEIGHT );
01672 }
01673 }
01674
01675 if ( bChanged ) NotifySelection();
01676 Update();
01677 }
01678 }
01679
01680 void CMatchCtrl::DoDelete()
01681 {
01682 CSingleLock pLock( &m_pMatches->m_pSection, TRUE );
01683 BOOL bChanged = FALSE;
01684
01685 for ( POSITION pos = m_pMatches->m_pSelectedFiles.GetHeadPosition() ; pos ; )
01686 {
01687 CMatchFile* pFile = (CMatchFile*)m_pMatches->m_pSelectedFiles.GetNext( pos );
01688 bChanged |= m_pMatches->Select( pFile, NULL, FALSE );
01689
01690 for ( CQueryHit* pHit = pFile->m_pHits ; pHit ; pHit = pHit->m_pNext )
01691 {
01692 pHit->m_bBogus = TRUE;
01693 }
01694 }
01695
01696 for ( POSITION pos = m_pMatches->m_pSelectedHits.GetHeadPosition() ; pos ; )
01697 {
01698 CQueryHit* pHit = (CQueryHit*)m_pMatches->m_pSelectedHits.GetNext( pos );
01699 m_pMatches->Select( NULL, pHit, FALSE );
01700 pHit->m_bBogus = TRUE;
01701 }
01702
01703 m_pMatches->Filter();
01704
01705 m_wndTip.Hide();
01706 if ( bChanged ) NotifySelection();
01707 Update();
01708 }
01709
01710 void CMatchCtrl::DoExpand(BOOL bExpand)
01711 {
01712 CSingleLock pLock( &m_pMatches->m_pSection, TRUE );
01713 BOOL bChanged = FALSE;
01714
01715 for ( POSITION pos = m_pMatches->m_pSelectedFiles.GetHeadPosition() ; pos ; )
01716 {
01717 CMatchFile* pFile = (CMatchFile*)m_pMatches->m_pSelectedFiles.GetNext( pos );
01718
01719 bChanged |= pFile->Expand( bExpand );
01720 }
01721
01722 m_wndTip.Hide();
01723 if ( bChanged ) NotifySelection();
01724 Update();
01725 }
01726
01727 void CMatchCtrl::NotifySelection()
01728 {
01729 GetOwner()->PostMessage( WM_COMMAND, MAKELONG( GetDlgCtrlID(), LBN_SELCHANGE ), (LPARAM)GetSafeHwnd() );
01730 }
01731
01733
01734
01735 void CMatchCtrl::OnClickHeader(NMHDR* pNotifyStruct, LRESULT* pResult)
01736 {
01737 HD_NOTIFY* pNotify = (HD_NOTIFY*)pNotifyStruct;
01738
01739 if ( m_pMatches->m_nSortColumn == pNotify->iItem )
01740 {
01741 if ( m_pMatches->m_bSortDir == 1 )
01742 {
01743 SetSortColumn( -1 );
01744 }
01745 else
01746 {
01747 SetSortColumn( pNotify->iItem, FALSE );
01748 }
01749 }
01750 else
01751 {
01752 SetSortColumn( pNotify->iItem, TRUE );
01753 }
01754 }
01755
01756 void CMatchCtrl::OnChangeHeader(NMHDR* pNotifyStruct, LRESULT* pResult)
01757 {
01758 Invalidate();
01759 }
01760
01761 void CMatchCtrl::OnTimer(UINT nIDEvent)
01762 {
01763 Invalidate();
01764 }
01765
01766 void CMatchCtrl::OnSetFocus(CWnd* pOldWnd)
01767 {
01768 CWnd::OnSetFocus( pOldWnd );
01769 Invalidate();
01770 }
01771
01772 void CMatchCtrl::OnKillFocus(CWnd* pNewWnd)
01773 {
01774 CWnd::OnKillFocus( pNewWnd );
01775 Invalidate();
01776 }