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 "MatchObjects.h"
00026 #include "QueryHit.h"
00027 #include "Buffer.h"
00028 #include "XML.h"
00029 #include "Schema.h"
00030 #include "SchemaCache.h"
00031 #include "Library.h"
00032 #include "SHA.h"
00033
00034 #include "CoolInterface.h"
00035 #include "ImageServices.h"
00036 #include "ImageFile.h"
00037 #include "RichElement.h"
00038 #include "ShellIcons.h"
00039 #include "Emoticons.h"
00040 #include "Skin.h"
00041 #include "CtrlSearchDetailPanel.h"
00042
00043 #ifdef _DEBUG
00044 #define new DEBUG_NEW
00045 #undef THIS_FILE
00046 static char THIS_FILE[] = __FILE__;
00047 #endif
00048
00049 IMPLEMENT_DYNAMIC(CSearchDetailPanel, CWnd)
00050
00051 BEGIN_MESSAGE_MAP(CSearchDetailPanel, CWnd)
00052
00053 ON_WM_CREATE()
00054 ON_WM_DESTROY()
00055 ON_WM_SIZE()
00056 ON_WM_VSCROLL()
00057 ON_WM_PAINT()
00058 ON_WM_SETCURSOR()
00059 ON_WM_LBUTTONUP()
00060 ON_WM_LBUTTONDOWN()
00061 ON_WM_MOUSEWHEEL()
00062 ON_WM_ERASEBKGND()
00063 ON_NOTIFY(RVN_CLICK, IDC_REVIEW_VIEW, OnClickReview)
00064
00065 END_MESSAGE_MAP()
00066
00067 #define SIZE_INTERNAL 1982
00068
00069
00071
00072
00073 CSearchDetailPanel::CSearchDetailPanel()
00074 {
00075 m_pMatches = NULL;
00076 m_bValid = FALSE;
00077 m_pFile = NULL;
00078 m_hThread = NULL;
00079 m_bThread = FALSE;
00080 m_crLight = CCoolInterface::CalculateColour(
00081 CoolInterface.m_crTipBack, RGB( 255, 255, 255 ), 128 );
00082 m_nThumbSize = 0;
00083
00084
00085 if( !SystemParametersInfo ( SPI_GETWHEELSCROLLLINES, 0, &m_nScrollWheelLines, 0) )
00086 {
00087 m_nScrollWheelLines = 3;
00088 }
00089 }
00090
00091 CSearchDetailPanel::~CSearchDetailPanel()
00092 {
00093 ClearReviews();
00094 }
00095
00097
00098
00099 BOOL CSearchDetailPanel::Create(CWnd* pParentWnd)
00100 {
00101 CRect rect( 0, 0, 0, 0 );
00102 return CWnd::Create( NULL, NULL, WS_CHILD|WS_VSCROLL|WS_CLIPCHILDREN, rect, pParentWnd, IDC_DETAIL_PANEL, NULL );
00103 }
00104
00105 void CSearchDetailPanel::Update(CMatchFile* pFile)
00106 {
00107 CSingleLock pLock( &m_pSection, TRUE );
00108
00109 CancelPreview();
00110 ClearReviews();
00111
00112 if ( pFile == NULL || ( pFile->m_pBest == NULL ) )
00113 {
00114 if ( m_bValid )
00115 {
00116 m_bValid = FALSE;
00117 OnSize( SIZE_INTERNAL, 0, 0 );
00118 }
00119 return;
00120 }
00121
00122 m_pMatches = pFile->m_pList;
00123 m_bValid = TRUE;
00124 m_pFile = pFile;
00125 m_pSHA1 = pFile->m_pSHA1;
00126 m_sName = pFile->m_pBest->m_sName;
00127 m_sSize = pFile->m_sSize;
00128 m_nIcon32 = ShellIcons.Get( pFile->m_pBest->m_sName, 32 );
00129 m_nIcon48 = ShellIcons.Get( pFile->m_pBest->m_sName, 48 );
00130 m_nRating = pFile->m_nRated ? pFile->m_nRating / pFile->m_nRated : 0;
00131
00132 m_bCanPreview = FALSE;
00133 m_pSchema = NULL;
00134
00135 DWORD nSpeed = 0;
00136
00137 for ( CQueryHit* pHit = pFile->m_pHits ; pHit ; pHit = pHit->m_pNext )
00138 {
00139 if ( m_pSchema == NULL ) m_pSchema = SchemaCache.Get( pHit->m_sSchemaURI );
00140 nSpeed += pHit->m_nSpeed;
00141
00142 if ( pHit->m_bSHA1 && pHit->m_bPush == TS_FALSE )
00143 {
00144 if ( pHit->m_bPreview )
00145 {
00146 m_pPreviewURLs.AddTail( pHit->m_sPreview );
00147 m_bCanPreview = TRUE;
00148 }
00149 #ifdef _DEBUG
00150 else if ( _tcsistr( pHit->m_sName, _T(".mpg") ) ||
00151 _tcsistr( pHit->m_sName, _T(".mpeg") ) ||
00152 _tcsistr( pHit->m_sName, _T(".avi") ) ||
00153 _tcsistr( pHit->m_sName, _T(".jpg") ) ||
00154 _tcsistr( pHit->m_sName, _T(".jpeg") ) )
00155 {
00156 CString strURL;
00157 strURL.Format( _T("http://%s:%i/gnutella/preview/v1?%s"),
00158 (LPCTSTR)CString( inet_ntoa( pHit->m_pAddress ) ), pHit->m_nPort,
00159 (LPCTSTR)CSHA::HashToString( &pHit->m_pSHA1, TRUE ) );
00160 m_pPreviewURLs.AddTail( strURL );
00161 m_bCanPreview = TRUE;
00162 }
00163 #endif
00164 }
00165
00166 if ( pHit->m_nRating > 0 || pHit->m_sComments.GetLength() > 0 )
00167 {
00168 m_pReviews.AddTail( new Review( &pHit->m_pClientID,
00169 &pHit->m_pAddress, pHit->m_sNick, pHit->m_nRating, pHit->m_sComments ) );
00170 }
00171 }
00172
00173 m_pMetadata.Setup( m_pSchema );
00174
00175 if ( m_pSchema != NULL )
00176 {
00177 for ( CQueryHit* pHit = pFile->m_pHits ; pHit ; pHit = pHit->m_pNext )
00178 {
00179 if ( pHit->m_pXML != NULL && m_pSchema->CheckURI( pHit->m_sSchemaURI ) )
00180 {
00181 m_pMetadata.Combine( pHit->m_pXML );
00182 }
00183 }
00184 }
00185
00186 m_pMetadata.Vote();
00187 m_pMetadata.CreateLinks();
00188 m_pMetadata.Clean( 4096 );
00189
00190 if ( IsWindowVisible() )
00191 {
00192 CString strFormat, strPart;
00193 m_sStatus.Empty();
00194
00195 if ( pFile->m_nSources == 1 )
00196 {
00197 LoadString( strFormat, IDS_SEARCH_DETAILS_SOURCES_ONE );
00198 strPart.Format( strFormat, (LPCTSTR)Settings.SmartVolume( nSpeed, TRUE, TRUE ) );
00199 m_sStatus += strPart;
00200 }
00201 else
00202 {
00203 if ( pFile->m_nSources == 0 ) nSpeed = 0;
00204 LoadString( strFormat, IDS_SEARCH_DETAILS_SOURCES_MANY );
00205 strPart.Format( strFormat, pFile->m_nSources, (LPCTSTR)Settings.SmartVolume( nSpeed, TRUE, TRUE ) );
00206 m_sStatus += strPart;
00207 }
00208
00209 if ( m_pReviews.GetCount() > 1 )
00210 {
00211 LoadString( strFormat, IDS_SEARCH_DETAILS_REVIEWS_MANY );
00212 strPart.Format( strFormat, m_pReviews.GetCount() );
00213 m_sStatus += strPart;
00214 }
00215 else if ( m_pReviews.GetCount() == 1 )
00216 {
00217 LoadString( strPart, IDS_SEARCH_DETAILS_REVIEWS_ONE );
00218 m_sStatus += strPart;
00219 }
00220
00221 if ( pFile->m_pPreview != NULL && pFile->m_nPreview > 0 )
00222 {
00223 CImageServices pServices;
00224 CImageFile pImage( &pServices );
00225
00226 if ( pImage.LoadFromMemory( _T(".jpg"), (LPCVOID)pFile->m_pPreview, pFile->m_nPreview, FALSE, TRUE ) )
00227 {
00228 pLock.Unlock();
00229 OnPreviewLoaded( &m_pSHA1, &pImage );
00230 }
00231 }
00232
00233 OnSize( SIZE_INTERNAL, 0, 0 );
00234 }
00235 }
00236
00237 void CSearchDetailPanel::ClearReviews()
00238 {
00239 for ( POSITION pos = m_pReviews.GetHeadPosition() ; pos ; )
00240 {
00241 delete (Review*)m_pReviews.GetNext( pos );
00242 }
00243
00244 m_pReviews.RemoveAll();
00245 }
00246
00248
00249
00250 int CSearchDetailPanel::OnCreate(LPCREATESTRUCT lpCreateStruct)
00251 {
00252 if ( CWnd::OnCreate( lpCreateStruct ) == -1 ) return -1;
00253 return 0;
00254 }
00255
00256 void CSearchDetailPanel::OnDestroy()
00257 {
00258 ClearReviews();
00259
00260 m_bThread = FALSE;
00261 CancelPreview();
00262
00263 if ( m_hThread != NULL )
00264 {
00265 m_pWakeup.SetEvent();
00266
00267 int nAttempt = 10;
00268 for ( ; nAttempt > 0 ; nAttempt-- )
00269 {
00270 DWORD nCode;
00271 if ( ! GetExitCodeThread( m_hThread, &nCode ) ) break;
00272 if ( nCode != STILL_ACTIVE ) break;
00273 Sleep( 100 );
00274 }
00275
00276 if ( nAttempt == 0 )
00277 {
00278 TerminateThread( m_hThread, 0 );
00279 theApp.Message( MSG_DEBUG, _T("WARNING: Terminating CSearchDetailPanel thread.") );
00280 Sleep( 100 );
00281 }
00282
00283 m_hThread = NULL;
00284 }
00285
00286 CWnd::OnDestroy();
00287 }
00288
00289 void CSearchDetailPanel::OnSize(UINT nType, int cx, int cy)
00290 {
00291 if ( nType != SIZE_INTERNAL ) CWnd::OnSize( nType, cx, cy );
00292
00293 SCROLLINFO pInfo;
00294 CRect rc;
00295
00296 GetWindowRect( &rc );
00297 rc.OffsetRect( -rc.left, -rc.top );
00298 rc.right -= GetSystemMetrics( SM_CXVSCROLL );
00299
00300 int nThumbSize = rc.Height() - 16;
00301 nThumbSize = max( nThumbSize, 64 );
00302 nThumbSize = min( nThumbSize, 128 );
00303 rc.left += nThumbSize + 16;
00304 rc.right -= 8;
00305
00306 CClientDC dc( this );
00307 int nHeight = 54 + m_pMetadata.Layout( &dc, rc.Width() );
00308
00309 for ( POSITION pos = m_pReviews.GetHeadPosition() ; pos ; )
00310 {
00311 Review* pReview = (Review*)m_pReviews.GetNext( pos );
00312 CRect rcReview( rc.left, nHeight, rc.right, nHeight );
00313 pReview->Layout( this, &rcReview );
00314 nHeight += rcReview.Height();
00315 }
00316
00317 if ( ! m_bValid ) nHeight = 0;
00318
00319 pInfo.cbSize = sizeof(pInfo);
00320 pInfo.fMask = SIF_ALL & ~SIF_TRACKPOS;
00321 pInfo.nMin = 0;
00322 pInfo.nMax = nHeight;
00323 pInfo.nPage = rc.Height();
00324 pInfo.nPos = 0;
00325 pInfo.nPos = max( 0, min( pInfo.nPos, pInfo.nMax - (int)pInfo.nPage + 1 ) );
00326
00327 SetScrollInfo( SB_VERT, &pInfo, TRUE );
00328
00329 Invalidate();
00330 }
00331
00332 void CSearchDetailPanel::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
00333 {
00334 SCROLLINFO pScroll;
00335
00336 ZeroMemory( &pScroll, sizeof(pScroll) );
00337 pScroll.cbSize = sizeof(pScroll);
00338 pScroll.fMask = SIF_ALL;
00339
00340 GetScrollInfo( SB_VERT, &pScroll );
00341
00342 switch ( nSBCode )
00343 {
00344 case SB_TOP:
00345 pScroll.nPos = 0;
00346 break;
00347 case SB_BOTTOM:
00348 pScroll.nPos = pScroll.nMax - 1;
00349 break;
00350 case SB_LINEUP:
00351 pScroll.nPos -= 8;
00352 break;
00353 case SB_LINEDOWN:
00354 pScroll.nPos += 8;
00355 break;
00356 case SB_PAGEUP:
00357 pScroll.nPos -= pScroll.nPage;
00358 break;
00359 case SB_PAGEDOWN:
00360 pScroll.nPos += pScroll.nPage;
00361 break;
00362 case SB_THUMBPOSITION:
00363 case SB_THUMBTRACK:
00364 pScroll.nPos = nPos;
00365 break;
00366 }
00367
00368 pScroll.fMask = SIF_POS;
00369 pScroll.nPos = max( 0, min( pScroll.nPos, pScroll.nMax ) );
00370
00371 SetScrollInfo( SB_VERT, &pScroll );
00372
00373 for ( POSITION pos = m_pReviews.GetHeadPosition() ; pos ; )
00374 {
00375 Review* pReview = (Review*)m_pReviews.GetNext( pos );
00376 pReview->Reposition( pScroll.nPos );
00377 }
00378
00379 Invalidate();
00380 }
00381
00382 void CSearchDetailPanel::OnLButtonDown(UINT nFlags, CPoint point)
00383 {
00384 SetFocus();
00385 }
00386
00387 BOOL CSearchDetailPanel::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
00388 {
00389 OnVScroll( SB_THUMBPOSITION, (int)( GetScrollPos( SB_VERT ) - zDelta / WHEEL_DELTA * m_nScrollWheelLines * 8 ), NULL );
00390 return TRUE;
00391 }
00392
00393 BOOL CSearchDetailPanel::OnEraseBkgnd(CDC* pDC)
00394 {
00395 return TRUE;
00396 }
00397
00398 void CSearchDetailPanel::OnPaint()
00399 {
00400 CSingleLock pLock( &m_pSection, TRUE );
00401 CPaintDC dc( this );
00402 CRect rcClient;
00403 CString str;
00404
00405 GetClientRect( &rcClient );
00406
00407 CFont* pOldFont = dc.GetCurrentFont();
00408 dc.SetBkColor( CoolInterface.m_crWindow );
00409 dc.SetBkMode( OPAQUE );
00410 dc.SetTextColor( CoolInterface.m_crText );
00411
00412 if ( ! m_bValid )
00413 {
00414 dc.SelectObject( &CoolInterface.m_fntNormal );
00415 LoadString( str, IDS_SEARCH_DETAILS_EMPTY );
00416 CSize sz = dc.GetTextExtent( str );
00417 CPoint pt = rcClient.CenterPoint();
00418 pt.x -= sz.cx / 2; pt.y -= sz.cy / 2;
00419 dc.ExtTextOut( pt.x, pt.y, ETO_OPAQUE, &rcClient, str, NULL );
00420 dc.SelectObject( pOldFont );
00421 return;
00422 }
00423
00424 CRect rcWork( 0, 0, 0, 0 );
00425 DrawThumbnail( &dc, rcClient, rcWork );
00426
00427 dc.SetViewportOrg( 0, -GetScrollPos( SB_VERT ) );
00428
00429 dc.SetBkColor( CoolInterface.m_crWindow );
00430 dc.SetTextColor( CoolInterface.m_crText );
00431
00432 dc.SelectObject( &CoolInterface.m_fntCaption );
00433 DrawText( &dc, rcWork.left, rcWork.top, m_sName );
00434
00435 CPoint ptStar( rcWork.right - 3, rcWork.top - 2 );
00436
00437 if ( m_nRating > 1 )
00438 {
00439 for ( int nRating = m_nRating - 1 ; nRating ; nRating-- )
00440 {
00441 ptStar.x -= 16;
00442 ShellIcons.Draw( &dc, SHI_STAR, 16, ptStar.x, ptStar.y, CoolInterface.m_crWindow );
00443 }
00444 }
00445 else if ( m_nRating == 1 )
00446 {
00447 ptStar.x -= 16;
00448 ShellIcons.Draw( &dc, SHI_FAKE, 16, ptStar.x, ptStar.y, CoolInterface.m_crWindow );
00449 }
00450
00451 rcWork.top += 20;
00452
00453 dc.FillSolidRect( rcWork.left, rcWork.top, rcWork.Width(), 1, CoolInterface.m_crMargin );
00454 dc.ExcludeClipRect( rcWork.left, rcWork.top, rcWork.right, rcWork.top + 1 );
00455 dc.SetBkColor( CoolInterface.m_crWindow );
00456 rcWork.top += 4;
00457
00458 dc.SelectObject( &CoolInterface.m_fntBold );
00459 LoadString( str, IDS_TIP_SIZE );
00460 DrawText( &dc, rcWork.right - 125, rcWork.top, str + ':' );
00461 dc.SelectObject( &CoolInterface.m_fntNormal );
00462 DrawText( &dc, rcWork.right - 60, rcWork.top, m_sSize );
00463 if ( m_pReviews.GetCount() )
00464 {
00465 dc.SelectObject( &CoolInterface.m_fntUnder );
00466 dc.SetTextColor( RGB( 0, 0, 255 ) );
00467 }
00468 DrawText( &dc, rcWork.left, rcWork.top, m_sStatus, &m_rcStatus );
00469 rcWork.top += 18;
00470
00471 m_pMetadata.Paint( &dc, &rcWork );
00472
00473 dc.SetViewportOrg( 0, 0 );
00474 dc.SelectObject( &CoolInterface.m_fntCaption );
00475 dc.SetBkColor( CoolInterface.m_crWindow );
00476 dc.SetTextColor( 0 );
00477
00478 for ( POSITION pos = m_pReviews.GetHeadPosition() ; pos ; )
00479 {
00480 Review* pReview = (Review*)m_pReviews.GetNext( pos );
00481 pReview->Paint( &dc, GetScrollPos( SB_VERT ) );
00482 }
00483
00484 dc.SelectObject( pOldFont );
00485 dc.FillSolidRect( &rcClient, CoolInterface.m_crWindow );
00486 }
00487
00488 void CSearchDetailPanel::DrawText(CDC* pDC, int nX, int nY, LPCTSTR pszText, RECT* pRect)
00489 {
00490 CSize sz = pDC->GetTextExtent( pszText, _tcslen( pszText ) );
00491 CRect rc( nX - 2, nY - 2, nX + sz.cx + 2, nY + sz.cy + 2 );
00492
00493 pDC->ExtTextOut( nX, nY, ETO_CLIPPED|ETO_OPAQUE, &rc, pszText, _tcslen( pszText ), NULL );
00494 pDC->ExcludeClipRect( &rc );
00495
00496 if ( pRect != NULL ) CopyMemory( pRect, &rc, sizeof(RECT) );
00497 }
00498
00499 void CSearchDetailPanel::DrawThumbnail(CDC* pDC, CRect& rcClient, CRect& rcWork)
00500 {
00501 int nThumbSize = rcClient.Height() - 16;
00502 nThumbSize = max( nThumbSize, 64 );
00503 nThumbSize = min( nThumbSize, 128 );
00504
00505 CRect rcThumb( rcClient.left + 8, rcClient.top + 8,
00506 rcClient.left + 8 + nThumbSize, rcClient.top + 8 + nThumbSize );
00507
00508 rcWork.CopyRect( &rcThumb );
00509
00510 pDC->Draw3dRect( &rcWork, CoolInterface.m_crMargin, CoolInterface.m_crMargin );
00511 rcWork.DeflateRect( 1, 1 );
00512 m_nThumbSize = rcWork.Width();
00513
00514 DrawThumbnail( pDC, rcWork );
00515
00516 pDC->ExcludeClipRect( &rcThumb );
00517
00518 rcWork.SetRect( rcThumb.right + 8, rcThumb.top, rcClient.right - 8, rcClient.bottom );
00519 }
00520
00521 void CSearchDetailPanel::DrawThumbnail(CDC* pDC, CRect& rcThumb)
00522 {
00523 m_rcThumb = rcThumb;
00524
00525 if ( m_bmThumb.m_hObject != NULL &&
00526 m_szThumb.cx != m_nThumbSize && m_szThumb.cy != m_nThumbSize )
00527 {
00528 CSingleLock pLock( &m_pMatches->m_pSection, TRUE );
00529
00530 if ( m_pMatches->FileToItem( m_pFile ) < 0xFFFFFFFF )
00531 {
00532 if ( m_pFile->m_pPreview != NULL && m_pFile->m_nPreview > 0 )
00533 {
00534 CImageServices pServices;
00535 CImageFile pImage( &pServices );
00536
00537 if ( pImage.LoadFromMemory( _T(".jpg"), (LPCVOID)m_pFile->m_pPreview, m_pFile->m_nPreview, FALSE, TRUE ) )
00538 {
00539 pLock.Unlock();
00540 OnPreviewLoaded( &m_pSHA1, &pImage );
00541 }
00542 }
00543 }
00544 }
00545
00546 if ( m_bmThumb.m_hObject &&
00547 ( m_szThumb.cx == m_nThumbSize || m_szThumb.cy == m_nThumbSize ) )
00548 {
00549 CDC dcMem;
00550 dcMem.CreateCompatibleDC( pDC );
00551
00552 CBitmap* pOld = (CBitmap*)dcMem.SelectObject( &m_bmThumb );
00553
00554 CPoint ptImage( ( rcThumb.left + rcThumb.right ) / 2 - m_szThumb.cx / 2,
00555 ( rcThumb.top + rcThumb.bottom ) / 2 - m_szThumb.cy / 2 );
00556
00557 pDC->BitBlt( ptImage.x, ptImage.y, m_szThumb.cx, m_szThumb.cy,
00558 &dcMem, 0, 0, SRCCOPY );
00559 pDC->ExcludeClipRect( ptImage.x, ptImage.y,
00560 ptImage.x + m_szThumb.cx, ptImage.y + m_szThumb.cy );
00561
00562 dcMem.SelectObject( pOld );
00563
00564 pDC->FillSolidRect( &rcThumb, m_crLight );
00565 }
00566 else
00567 {
00568 CPoint pt( ( rcThumb.left + rcThumb.right ) / 2 - 24,
00569 ( rcThumb.top + rcThumb.bottom ) / 2 - 24 );
00570
00571 if ( m_bCanPreview )
00572 {
00573 CString str;
00574 LoadString( str, m_bIsPreviewing ? IDS_SEARCH_DETAILS_PREVIEWING : IDS_SEARCH_DETAILS_PREVIEW );
00575
00576 pDC->SetBkColor( m_crLight );
00577 pDC->SetTextColor( m_bIsPreviewing ? RGB( 255, 0, 0 ) : RGB( 0, 0, 255 ) );
00578 pDC->SelectObject( m_bIsPreviewing ? &theApp.m_gdiFontBold : &theApp.m_gdiFontLine );
00579
00580 CSize sz = pDC->GetTextExtent( str );
00581
00582 if ( sz.cx + 4 < rcThumb.Width() )
00583 {
00584 pt.y -= sz.cy / 2;
00585 CPoint ptText(
00586 ( rcThumb.left + rcThumb.right ) / 2 - sz.cx / 2,
00587 pt.y + 50 );
00588 DrawText( pDC, ptText.x, ptText.y, str );
00589 }
00590 else
00591 {
00592
00593 int nLength = str.GetLength();
00594 int nSpace = str.Find( ' ', nLength / 2 - 1 );
00595 CString strFirstHalf = str.Left( nSpace );
00596 str = str.Right( nLength - nSpace - 1 );
00597 sz = pDC->GetTextExtent( strFirstHalf );
00598
00599 if ( sz.cx + 4 < rcThumb.Width() && pt.y + 50 < rcThumb.Height() )
00600 {
00601 pt.y -= sz.cy / 2;
00602 CPoint ptText(
00603 ( rcThumb.left + rcThumb.right ) / 2 - sz.cx / 2,
00604 pt.y + 50 );
00605 DrawText( pDC, ptText.x, ptText.y, strFirstHalf );
00606 CSize sz2 = pDC->GetTextExtent( str );
00607
00608 if ( sz2.cx + 4 < rcThumb.Width() &&
00609 pt.y + sz2.cy + 57 < rcThumb.Height() )
00610 {
00611 pt.y -= sz2.cy / 2;
00612 CPoint ptText(
00613 ( rcThumb.left + rcThumb.right ) / 2 - sz2.cx / 2,
00614 pt.y + sz.cy + 57 );
00615 DrawText( pDC, ptText.x, ptText.y, str );
00616 }
00617 else
00618
00619 DrawText( pDC, ptText.x + sz.cx + 1, ptText.y, _T("\x2026") );
00620 }
00621 }
00622 }
00623
00624 if ( m_nIcon48 >= 0 )
00625 {
00626 ShellIcons.Draw( pDC, m_nIcon48, 48, pt.x, pt.y, m_crLight );
00627 }
00628 else if ( m_nIcon32 >= 0 )
00629 {
00630 pt.x += 8; pt.y += 8;
00631 ShellIcons.Draw( pDC, m_nIcon32, 32, pt.x, pt.y, m_crLight );
00632 }
00633
00634 pDC->FillSolidRect( &rcThumb, m_crLight );
00635 }
00636 }
00637
00638 BOOL CSearchDetailPanel::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
00639 {
00640 CPoint point;
00641
00642 GetCursorPos( &point );
00643 ScreenToClient( &point );
00644
00645 if ( m_bValid && m_bCanPreview && ! m_bIsPreviewing && m_rcThumb.PtInRect( point ) )
00646 {
00647 SetCursor( AfxGetApp()->LoadCursor( IDC_HAND ) );
00648 return TRUE;
00649 }
00650
00651 point.y += GetScrollPos( SB_VERT );
00652
00653 if ( m_bValid && m_pReviews.GetCount() > 0 && m_rcStatus.PtInRect( point ) )
00654 {
00655 SetCursor( AfxGetApp()->LoadCursor( IDC_HAND ) );
00656 return TRUE;
00657 }
00658
00659 if ( m_bValid && m_pMetadata.HitTest( point, TRUE ) != NULL )
00660 {
00661 SetCursor( AfxGetApp()->LoadCursor( IDC_HAND ) );
00662 return TRUE;
00663 }
00664
00665 return CWnd::OnSetCursor( pWnd, nHitTest, message );
00666 }
00667
00668 void CSearchDetailPanel::OnLButtonUp(UINT nFlags, CPoint point)
00669 {
00670 if ( m_bValid && m_bCanPreview && ! m_bIsPreviewing && m_rcThumb.PtInRect( point ) )
00671 {
00672 RequestPreview();
00673 }
00674
00675 point.y += GetScrollPos( SB_VERT );
00676
00677 if ( m_bValid && m_pReviews.GetCount() > 0 && m_rcStatus.PtInRect( point ) )
00678 {
00679 int nHeight = 54 + m_pMetadata.m_nHeight;
00680 SetScrollPos( SB_VERT, nHeight );
00681 OnVScroll( SB_THUMBPOSITION, nHeight, NULL );
00682 Invalidate();
00683 }
00684
00685 m_pMetadata.OnClick( point );
00686
00687 CWnd::OnLButtonUp( nFlags, point );
00688 }
00689
00691
00692
00693 CSearchDetailPanel::Review::Review(GGUID* pGUID, IN_ADDR* pAddress, LPCTSTR pszNick, int nRating, LPCTSTR pszComments)
00694 {
00695 m_pGUID = *pGUID;
00696 m_nRating = nRating;
00697
00698 if ( pszNick != NULL && *pszNick != 0 )
00699 {
00700 m_sNick.Format( _T("%s (%s)"), pszNick,
00701 (LPCTSTR)CString( inet_ntoa( *pAddress ) ) );
00702 }
00703 else
00704 {
00705 m_sNick = inet_ntoa( *pAddress );
00706 }
00707
00708 if ( pszComments != NULL )
00709 {
00710 m_pComments.m_szMargin = CSize( 6, 0 );
00711 Emoticons.FormatText( &m_pComments, pszComments, TRUE );
00712 }
00713 }
00714
00715 CSearchDetailPanel::Review::~Review()
00716 {
00717 if ( m_wndComments.m_hWnd != NULL ) m_wndComments.DestroyWindow();
00718 }
00719
00720 void CSearchDetailPanel::Review::Layout(CSearchDetailPanel* pParent, CRect* pRect)
00721 {
00722 pRect->bottom += 22;
00723
00724 if ( m_pComments.GetCount() )
00725 {
00726 if ( m_wndComments.m_hWnd == NULL )
00727 {
00728 m_wndComments.Create( WS_CHILD, *pRect, pParent, IDC_REVIEW_VIEW );
00729 m_wndComments.SetSelectable( TRUE );
00730 m_wndComments.SetDocument( &m_pComments );
00731 }
00732
00733 pRect->bottom += m_wndComments.FullHeightMove( pRect->left, pRect->bottom, pRect->Width(), TRUE );
00734 pRect->bottom += 4;
00735 }
00736 else
00737 {
00738 pRect->bottom += 2;
00739 }
00740
00741 m_rc.CopyRect( pRect );
00742 }
00743
00744 void CSearchDetailPanel::Review::Reposition(int nScroll)
00745 {
00746 if ( m_wndComments.m_hWnd != NULL )
00747 {
00748 m_wndComments.SetWindowPos( NULL, m_rc.left, m_rc.top - nScroll + 22, 0, 0,
00749 SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE );
00750 }
00751 }
00752
00753 void CSearchDetailPanel::Review::Paint(CDC* pDC, int nScroll)
00754 {
00755 CRect rc( &m_rc );
00756 rc.OffsetRect( 0, -nScroll );
00757
00758 pDC->FillSolidRect( rc.left, rc.top, rc.Width(), 1, CoolInterface.m_crMargin );
00759 pDC->ExcludeClipRect( rc.left, rc.top, rc.right, rc.top + 1 );
00760 rc.top += 4;
00761
00762 CString strFormat, strCaption;
00763
00764 LoadString( strFormat, m_pComments.GetCount() > 0 ? IDS_SEARCH_DETAILS_WRITES : IDS_SEARCH_DETAILS_RATES );
00765 strCaption.Format( strFormat, (LPCTSTR)m_sNick );
00766
00767 pDC->SetBkColor( CoolInterface.m_crWindow );
00768 DrawText( pDC, rc.left, rc.top, strCaption );
00769
00770 CPoint ptStar( rc.right - 3, rc.top );
00771
00772 if ( m_nRating > 1 )
00773 {
00774 for ( int nRating = m_nRating - 1 ; nRating ; nRating-- )
00775 {
00776 ptStar.x -= 16;
00777 ShellIcons.Draw( pDC, SHI_STAR, 16, ptStar.x, ptStar.y, CoolInterface.m_crWindow );
00778 }
00779 }
00780 else if ( m_nRating == 1 )
00781 {
00782 ptStar.x -= 16;
00783 ShellIcons.Draw( pDC, SHI_FAKE, 16, ptStar.x, ptStar.y, CoolInterface.m_crWindow );
00784 }
00785
00786 rc.top += 20;
00787 }
00788
00789 void CSearchDetailPanel::OnClickReview(RVN_ELEMENTEVENT* pNotify, LRESULT *pResult)
00790 {
00791 if ( CRichElement* pElement = pNotify->pElement )
00792 {
00793 theApp.InternalURI( pElement->m_sLink );
00794 }
00795 }
00796
00798
00799
00800 BOOL CSearchDetailPanel::RequestPreview()
00801 {
00802 CSingleLock pLock( &m_pSection, TRUE );
00803
00804 if ( ! m_bValid || ! m_bCanPreview || m_pPreviewURLs.IsEmpty() ) return FALSE;
00805
00806 if ( m_hThread == NULL )
00807 {
00808 m_bThread = TRUE;
00809 CWinThread* pThread = AfxBeginThread( ThreadStart, this, THREAD_PRIORITY_IDLE );
00810 m_hThread = pThread->m_hThread;
00811 }
00812
00813 m_bRunPreview = TRUE;
00814
00815 pLock.Unlock();
00816
00817 m_pWakeup.SetEvent();
00818
00819 return TRUE;
00820 }
00821
00822 void CSearchDetailPanel::CancelPreview()
00823 {
00824 CSingleLock pLock( &m_pSection, TRUE );
00825
00826 m_bRunPreview = FALSE;
00827 m_pPreviewURLs.RemoveAll();
00828
00829 if ( m_bmThumb.m_hObject != NULL )
00830 {
00831 m_bmThumb.DeleteObject();
00832 Invalidate();
00833 }
00834
00835 if ( m_bIsPreviewing )
00836 {
00837 m_bIsPreviewing = FALSE;
00838 Invalidate();
00839 }
00840
00841 m_pRequest.Cancel();
00842 }
00843
00844 UINT CSearchDetailPanel::ThreadStart(LPVOID pParam)
00845 {
00846 CSearchDetailPanel* pPanel = (CSearchDetailPanel*)pParam;
00847 pPanel->OnRun();
00848 return 0;
00849 }
00850
00851 void CSearchDetailPanel::OnRun()
00852 {
00853 CSingleLock pLock( &m_pSection );
00854 CImageServices pServices;
00855
00856 while ( m_bThread )
00857 {
00858 pLock.Lock();
00859
00860 if ( ! m_bValid || ! m_bRunPreview || m_pPreviewURLs.IsEmpty() )
00861 {
00862 if ( m_bIsPreviewing )
00863 {
00864 m_bIsPreviewing = FALSE;
00865 Invalidate();
00866 }
00867
00868 pLock.Unlock();
00869 WaitForSingleObject( m_pWakeup, INFINITE );
00870
00871 continue;
00872 }
00873
00874 CString strURL = m_pPreviewURLs.RemoveHead();
00875 SHA1 pSHA1 = m_pSHA1;
00876
00877 if ( ! m_bIsPreviewing )
00878 {
00879 m_bIsPreviewing = TRUE;
00880 Invalidate();
00881 }
00882
00883 pLock.Unlock();
00884
00885 BYTE* pBuffer;
00886 DWORD nBuffer;
00887
00888 if ( ExecuteRequest( strURL, &pBuffer, &nBuffer ) )
00889 {
00890 CImageFile pImage( &pServices );
00891
00892 if ( pImage.LoadFromMemory( _T(".jpg"), (LPCVOID)pBuffer, nBuffer, FALSE, TRUE ) )
00893 {
00894 OnPreviewLoaded( &pSHA1, &pImage );
00895 CachePreviewImage( &pSHA1, pBuffer, nBuffer );
00896 }
00897 else
00898 {
00899 theApp.Message( MSG_ERROR, IDS_SEARCH_DETAILS_PREVIEW_FAILED, (LPCTSTR)strURL );
00900 }
00901
00902 free( pBuffer );
00903 }
00904 else
00905 {
00906 theApp.Message( MSG_ERROR, IDS_SEARCH_DETAILS_PREVIEW_FAILED, (LPCTSTR)strURL );
00907 }
00908 }
00909 }
00910
00911 BOOL CSearchDetailPanel::ExecuteRequest(CString strURL, BYTE** ppBuffer, DWORD* pnBuffer)
00912 {
00913 m_pRequest.Clear();
00914 m_pRequest.SetURL( strURL );
00915 m_pRequest.AddHeader( _T("Accept"), _T("image/jpeg") );
00916 m_pRequest.LimitContentLength( Settings.Search.MaxPreviewLength );
00917
00918 if ( ! m_pRequest.Execute( FALSE ) )
00919 {
00920 theApp.Message( MSG_DEBUG, _T("Preview failed: unable to execute request.") );
00921 return FALSE;
00922 }
00923
00924 int nCode = m_pRequest.GetStatusCode();
00925
00926 if ( m_pRequest.GetStatusSuccess() == FALSE )
00927 {
00928 theApp.Message( MSG_DEBUG, _T("Preview failed: HTTP status code %i"),
00929 m_pRequest.GetStatusCode() );
00930 return FALSE;
00931 }
00932
00933 CString strURN = m_pRequest.GetHeader( _T("X-Previewed-URN") );
00934
00935 if ( strURN.GetLength() )
00936 {
00937 SHA1 pSHA1;
00938
00939 if ( CSHA::HashFromURN( strURN, &pSHA1 ) && pSHA1 != m_pSHA1 )
00940 {
00941 theApp.Message( MSG_DEBUG, _T("Preview failed: wrong URN.") );
00942 return FALSE;
00943 }
00944 }
00945
00946 CString strMIME = m_pRequest.GetHeader( _T("Content-Type") );
00947
00948 if ( strMIME.CompareNoCase( _T("image/jpeg") ) != 0 )
00949 {
00950 theApp.Message( MSG_DEBUG, _T("Preview failed: unacceptable content type.") );
00951 return FALSE;
00952 }
00953
00954 CBuffer* pBuffer = m_pRequest.GetResponseBuffer();
00955 if ( pBuffer == NULL ) return FALSE;
00956
00957 *pnBuffer = pBuffer->m_nLength;
00958 *ppBuffer = (BYTE*)malloc( *pnBuffer );
00959 CopyMemory( *ppBuffer, pBuffer->m_pBuffer, *pnBuffer );
00960
00961 return TRUE;
00962 }
00963
00964 void CSearchDetailPanel::OnPreviewLoaded(SHA1* pSHA1, CImageFile* pImage)
00965 {
00966 if ( m_nThumbSize == 0 ) return;
00967
00968 int nSize = m_nThumbSize * pImage->m_nWidth / pImage->m_nHeight;
00969
00970 if ( nSize > m_nThumbSize )
00971 {
00972 nSize = m_nThumbSize * pImage->m_nHeight / pImage->m_nWidth;
00973 pImage->Resample( m_nThumbSize, nSize );
00974 }
00975 else
00976 {
00977 pImage->Resample( nSize, m_nThumbSize );
00978 }
00979
00980 CSingleLock pLock( &m_pSection, TRUE );
00981
00982 if ( m_pSHA1 != *pSHA1 ) return;
00983
00984 m_bCanPreview = m_bRunPreview = m_bIsPreviewing = FALSE;
00985
00986 if ( m_bmThumb.m_hObject ) m_bmThumb.DeleteObject();
00987
00988 m_bmThumb.Attach( pImage->CreateBitmap() );
00989 m_szThumb.cx = pImage->m_nWidth;
00990 m_szThumb.cy = pImage->m_nHeight;
00991
00992 pLock.Unlock();
00993 Invalidate();
00994 }
00995
00996 BOOL CSearchDetailPanel::CachePreviewImage(SHA1* pSHA1, LPBYTE pBuffer, DWORD nBuffer)
00997 {
00998 CSingleLock pLock( &m_pMatches->m_pSection, TRUE );
00999
01000 if ( m_pMatches->FileToItem( m_pFile ) != 0xFFFFFFFF )
01001 {
01002 if ( m_pFile->m_pPreview != NULL ) delete [] m_pFile->m_pPreview;
01003
01004 m_pFile->m_nPreview = nBuffer;
01005 m_pFile->m_pPreview = new BYTE[ nBuffer ];
01006 CopyMemory( m_pFile->m_pPreview, pBuffer, nBuffer );
01007
01008 return TRUE;
01009 }
01010
01011 return FALSE;
01012 }
01013