PageRenderTime 156ms CodeModel.GetById 21ms app.highlight 122ms RepoModel.GetById 1ms app.codeStats 1ms

/reporting/crashsender/FilePreviewCtrl.cpp

http://crashrpt.googlecode.com/
C++ | 2082 lines | 1554 code | 390 blank | 138 comment | 209 complexity | 355c48d945f55446df805f4aff8fc950 MD5 | raw file
   1/************************************************************************************* 
   2This file is a part of CrashRpt library.
   3Copyright (c) 2003-2013 The CrashRpt project authors. All Rights Reserved.
   4
   5Use of this source code is governed by a BSD-style license
   6that can be found in the License.txt file in the root of the source
   7tree. All contributing project authors may
   8be found in the Authors.txt file in the root of the source tree.
   9***************************************************************************************/
  10
  11#include "stdafx.h"
  12#include "FilePreviewCtrl.h"
  13#include "png.h"
  14#include "jpeglib.h"
  15#include "strconv.h"
  16
  17#pragma warning(disable:4611)
  18// DIBSIZE calculates the number of bytes required by an image
  19
  20#define WIDTHBYTES(bits) ((DWORD)(((bits)+31) & (~31)) / 8)
  21#define DIBWIDTHBYTES(bi) (DWORD)WIDTHBYTES((DWORD)(bi).biWidth * (DWORD)(bi).biBitCount)
  22#define _DIBSIZE(bi) (DIBWIDTHBYTES(bi) * (DWORD)(bi).biHeight)
  23#define DIBSIZE(bi) ((bi).biHeight < 0 ? (-1)*(_DIBSIZE(bi)) : _DIBSIZE(bi))
  24
  25static inline 
  26unsigned char CLAMP(int x)
  27{
  28   return  (unsigned char)((x > 255) ? 255 : (x < 0) ? 0 : x);
  29}
  30
  31//-----------------------------------------------------------------------------
  32// CFileMemoryMapping implementation
  33//-----------------------------------------------------------------------------
  34
  35CFileMemoryMapping::CFileMemoryMapping()  
  36{
  37    // Set member vars to the default values
  38    m_hFile = INVALID_HANDLE_VALUE;
  39    m_uFileLength = 0;
  40    m_hFileMapping = NULL;  
  41
  42    SYSTEM_INFO si;  
  43    GetSystemInfo(&si);
  44    m_dwAllocGranularity = si.dwAllocationGranularity;  
  45}
  46
  47CFileMemoryMapping::~CFileMemoryMapping()
  48{
  49    Destroy();
  50}
  51
  52
  53BOOL CFileMemoryMapping::Init(LPCTSTR szFileName)
  54{
  55    if(m_hFile!=INVALID_HANDLE_VALUE)
  56    {
  57        // If a file mapping already created, destroy it
  58        Destroy();    
  59    }
  60
  61    // Open file handle
  62    m_hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
  63    if(m_hFile == INVALID_HANDLE_VALUE)
  64        return FALSE;
  65
  66    // Create file mapping
  67    m_hFileMapping = CreateFileMapping(m_hFile, 0, PAGE_READONLY, 0, 0, 0);
  68    LARGE_INTEGER size;
  69    GetFileSizeEx(m_hFile, &size);	
  70    m_uFileLength = size.QuadPart; 
  71
  72    return TRUE;
  73}
  74
  75BOOL CFileMemoryMapping::Destroy()
  76{
  77    // Unmap all views
  78    std::map<DWORD, LPBYTE>::iterator it;
  79    for(it=m_aViewStartPtrs.begin(); it!=m_aViewStartPtrs.end(); it++)
  80    {
  81        if(it->second != NULL)
  82            UnmapViewOfFile(it->second);    
  83    }
  84    m_aViewStartPtrs.clear();
  85
  86    // Close file mapping handle
  87    if(m_hFileMapping!=NULL)
  88    {
  89        CloseHandle(m_hFileMapping);    
  90    }
  91
  92    // Close file handle
  93    if(m_hFile!=INVALID_HANDLE_VALUE)
  94    {
  95        CloseHandle(m_hFile);    
  96    }
  97
  98    m_hFileMapping = NULL;
  99    m_hFile = INVALID_HANDLE_VALUE;		
 100    m_uFileLength = 0;  
 101
 102    return TRUE;
 103}
 104
 105ULONG64 CFileMemoryMapping::GetSize()
 106{
 107    return m_uFileLength;
 108}
 109
 110LPBYTE CFileMemoryMapping::CreateView(DWORD dwOffset, DWORD dwLength)
 111{
 112    DWORD dwThreadId = GetCurrentThreadId();
 113    DWORD dwBaseOffs = dwOffset-dwOffset%m_dwAllocGranularity;
 114    DWORD dwDiff = dwOffset-dwBaseOffs;
 115    LPBYTE pPtr = NULL;
 116
 117    CAutoLock lock(&m_csLock);
 118
 119    std::map<DWORD, LPBYTE>::iterator it = m_aViewStartPtrs.find(dwThreadId);
 120    if(it!=m_aViewStartPtrs.end())
 121    {
 122        UnmapViewOfFile(it->second);
 123    }
 124
 125    pPtr = (LPBYTE)MapViewOfFile(m_hFileMapping, FILE_MAP_READ, 0, dwBaseOffs, dwLength+dwDiff);
 126    if(it!=m_aViewStartPtrs.end())
 127    {
 128        it->second = pPtr;
 129    }
 130    else
 131    {
 132        m_aViewStartPtrs[dwThreadId] = pPtr;
 133    }
 134
 135    return (pPtr+dwDiff);
 136}
 137
 138CImage::CImage()
 139{
 140    m_hBitmap = NULL;
 141    m_hPalette = NULL;
 142    m_bLoadCancelled = FALSE;
 143}
 144
 145CImage::~CImage()
 146{
 147    Destroy();
 148}
 149
 150BOOL CImage::IsBitmap(FILE* f)
 151{
 152    rewind(f);
 153
 154    BITMAPFILEHEADER bfh;
 155    size_t n = fread(&bfh, sizeof(bfh), 1, f);
 156    if(n!=1)
 157        return FALSE;
 158
 159    if(memcmp(&bfh.bfType, "BM", 2)!=0)
 160        return FALSE;
 161
 162    return TRUE;  
 163}
 164
 165BOOL CImage::IsPNG(FILE* f)
 166{
 167    png_byte header[8];
 168    rewind(f);
 169
 170    fread(header, 1, 8, f);
 171    if(png_sig_cmp(header, 0, 8))  
 172        return FALSE;
 173
 174    return TRUE;
 175}
 176
 177BOOL CImage::IsJPEG(FILE* f)
 178{
 179    rewind(f);
 180
 181    // Read first two bytes (SOI marker).
 182    // Each JPEG file begins with SOI marker (0xD8FF).
 183
 184    WORD wSOIMarker;
 185    int n = (int)fread(&wSOIMarker, 1, 2, f);
 186    if(n!=2)
 187        return FALSE;
 188
 189    if(wSOIMarker==0xD8FF)
 190        return TRUE; // This is a JPEG file
 191
 192    return FALSE;
 193}
 194
 195BOOL CImage::IsImageFile(CString sFileName)
 196{
 197    FILE* f = NULL;
 198    _TFOPEN_S(f, sFileName.GetBuffer(0), _T("rb"));
 199    if(f==NULL)
 200        return FALSE;
 201
 202    if(IsBitmap(f) || IsPNG(f) || IsJPEG(f))
 203    {
 204        fclose(f);
 205        return TRUE;
 206    }
 207
 208    fclose(f);
 209    return FALSE;
 210}
 211
 212void CImage::Destroy()
 213{
 214    CAutoLock lock(&m_csLock);
 215
 216    if(m_hBitmap)
 217    {
 218        DeleteObject(m_hBitmap);
 219        m_hBitmap = NULL;
 220    }
 221
 222    if(m_hPalette)
 223    {
 224        DeleteObject(m_hPalette);
 225        m_hPalette = NULL;
 226    }
 227
 228    m_bLoadCancelled = FALSE;
 229}
 230
 231BOOL CImage::IsValid()
 232{
 233    CAutoLock lock(&m_csLock);
 234    return m_hBitmap!=NULL;
 235}
 236
 237BOOL CImage::Load(CString sFileName)
 238{
 239    Destroy();
 240
 241    FILE* f = NULL;
 242    _TFOPEN_S(f, sFileName.GetBuffer(0), _T("rb"));
 243    if(f==NULL)
 244        return FALSE;
 245    if(IsBitmap(f))
 246    {
 247        fclose(f);
 248        return LoadBitmapFromBMPFile(sFileName.GetBuffer(0));
 249    }
 250    else if(IsPNG(f))
 251    {
 252        fclose(f);
 253        return LoadBitmapFromPNGFile(sFileName.GetBuffer(0));
 254    }
 255    else if(IsJPEG(f))
 256    {
 257        fclose(f);
 258        return LoadBitmapFromJPEGFile(sFileName.GetBuffer(0));
 259    }
 260
 261    fclose(f);
 262    return FALSE;  
 263}
 264
 265void CImage::Cancel()
 266{
 267    CAutoLock lock(&m_csLock);
 268    m_bLoadCancelled = TRUE;
 269}
 270
 271// The following code was taken from http://support.microsoft.com/kb/158898
 272BOOL CImage::LoadBitmapFromBMPFile(LPTSTR szFileName)
 273{
 274    CAutoLock lock(&m_csLock);
 275
 276    BITMAP  bm;
 277
 278    // Use LoadImage() to get the image loaded into a DIBSection
 279    m_hBitmap = (HBITMAP)LoadImage( NULL, szFileName, IMAGE_BITMAP, 0, 0,
 280        LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE );
 281    if( m_hBitmap == NULL )
 282        return FALSE;
 283
 284    // Get the color depth of the DIBSection
 285    GetObject(m_hBitmap, sizeof(BITMAP), &bm );
 286    // If the DIBSection is 256 color or less, it has a color table
 287    if( ( bm.bmBitsPixel * bm.bmPlanes ) <= 8 )
 288    {
 289        HDC           hMemDC = NULL;
 290        HBITMAP       hOldBitmap = NULL;
 291        RGBQUAD       rgb[256];
 292        LPLOGPALETTE  pLogPal = NULL;
 293        WORD          i = 0;
 294
 295        // Create a memory DC and select the DIBSection into it
 296        hMemDC = CreateCompatibleDC( NULL );
 297        hOldBitmap = (HBITMAP)SelectObject( hMemDC, m_hBitmap );
 298
 299        // Get the DIBSection's color table
 300        GetDIBColorTable( hMemDC, 0, 256, rgb );
 301
 302        // Create a palette from the color tabl
 303        pLogPal = (LOGPALETTE *)malloc( sizeof(LOGPALETTE) + (256*sizeof(PALETTEENTRY)) );
 304        pLogPal->palVersion = 0x300;
 305        pLogPal->palNumEntries = 256;
 306
 307        for(i=0;i<256;i++)
 308        {
 309            pLogPal->palPalEntry[i].peRed = rgb[i].rgbRed;
 310            pLogPal->palPalEntry[i].peGreen = rgb[i].rgbGreen;
 311            pLogPal->palPalEntry[i].peBlue = rgb[i].rgbBlue;
 312            pLogPal->palPalEntry[i].peFlags = 0;
 313        }
 314
 315        m_hPalette = CreatePalette( pLogPal );
 316
 317        // Clean up
 318        free( pLogPal );
 319        SelectObject( hMemDC, hOldBitmap );
 320        DeleteDC( hMemDC );
 321    }
 322    else   // It has no color table, so use a halftone palette
 323    {
 324        HDC    hRefDC = NULL;
 325
 326        hRefDC = GetDC( NULL );
 327        m_hPalette = CreateHalftonePalette( hRefDC );
 328        ReleaseDC( NULL, hRefDC );
 329    }
 330    return TRUE;
 331}
 332
 333BOOL CImage::LoadBitmapFromPNGFile(LPTSTR szFileName)
 334{
 335    BOOL bStatus = FALSE;
 336
 337    FILE *fp = NULL;
 338    const int number = 8;
 339    png_byte header[number];
 340    png_structp png_ptr = NULL;
 341    png_infop info_ptr = NULL;
 342    png_infop end_info = NULL;
 343    png_uint_32 rowbytes = 0;
 344    png_uint_32 width = 0;
 345    png_uint_32 height = 0;
 346    png_bytep row = NULL;
 347    int y = 0;
 348    BITMAPINFO* pBMI = NULL;
 349    HDC hDC = NULL;
 350
 351    _TFOPEN_S(fp, szFileName, _T("rb"));
 352    if (!fp)
 353    {
 354        return FALSE;
 355    }
 356
 357    fread(header, 1, number, fp);
 358    if(png_sig_cmp(header, 0, number))  
 359    {
 360        goto cleanup;
 361    }  
 362
 363    png_ptr = png_create_read_struct
 364        (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 365    if (!png_ptr)
 366        goto cleanup;
 367
 368    if (setjmp(png_ptr->jmpbuf)) 
 369        goto cleanup;
 370
 371    info_ptr = png_create_info_struct(png_ptr);
 372    if (!info_ptr)
 373        goto cleanup;
 374
 375    end_info = png_create_info_struct(png_ptr);
 376    if (!end_info)
 377        goto cleanup;
 378
 379    png_init_io(png_ptr, fp);
 380    png_set_sig_bytes(png_ptr, number);
 381
 382    // Read PNG information
 383    png_read_info(png_ptr, info_ptr);
 384
 385    // Get count of bytes per row
 386    rowbytes = png_get_rowbytes(png_ptr, info_ptr);
 387    row = new png_byte[rowbytes];
 388
 389    width = png_get_image_width(png_ptr, info_ptr);
 390    height = png_get_image_height(png_ptr, info_ptr);
 391
 392    if(info_ptr->channels==3)
 393    {
 394        png_set_strip_16(png_ptr);
 395        png_set_packing(png_ptr); 
 396        png_set_bgr(png_ptr);
 397    }
 398
 399    hDC = GetDC(NULL);
 400
 401    {
 402        CAutoLock lock(&m_csLock);
 403        if(m_bLoadCancelled)
 404            goto cleanup;
 405        m_hBitmap = CreateCompatibleBitmap(hDC, width, height);  
 406    }
 407
 408    pBMI = (BITMAPINFO*)new BYTE[sizeof(BITMAPINFO)+256*4];
 409    memset(pBMI, 0, sizeof(BITMAPINFO)+256*4);  
 410    pBMI->bmiHeader.biSize = sizeof(BITMAPINFO);
 411    pBMI->bmiHeader.biBitCount = 8*info_ptr->channels;
 412    pBMI->bmiHeader.biWidth = width;
 413    pBMI->bmiHeader.biHeight = height;
 414    pBMI->bmiHeader.biPlanes = 1;
 415    pBMI->bmiHeader.biCompression = BI_RGB;
 416    pBMI->bmiHeader.biSizeImage = rowbytes*height;
 417
 418    if( info_ptr->channels == 1 )
 419    {
 420        RGBQUAD* palette = pBMI->bmiColors;
 421
 422        int i;
 423        for( i = 0; i < 256; i++ )
 424        {
 425            palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i;
 426            palette[i].rgbReserved = 0;
 427        }
 428
 429        palette[256].rgbBlue = palette[256].rgbGreen = palette[256].rgbRed = 255;
 430    }
 431
 432    for(y=height-1; y>=0; y--)
 433    {
 434        png_read_rows(png_ptr, &row, png_bytepp_NULL, 1); 
 435
 436        {
 437            CAutoLock lock(&m_csLock);
 438            int n = SetDIBits(hDC, m_hBitmap, y, 1, row, pBMI, DIB_RGB_COLORS);
 439            if(n==0)
 440                goto cleanup;
 441        }
 442    }
 443
 444    /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */
 445    png_read_end(png_ptr, info_ptr);
 446
 447    bStatus = TRUE;
 448
 449cleanup:
 450
 451    if(fp!=NULL)
 452    {
 453        fclose(fp);
 454    }
 455
 456    if(png_ptr)
 457    {
 458        png_destroy_read_struct(&png_ptr,
 459            (png_infopp)&info_ptr, (png_infopp)&end_info);
 460    }
 461
 462    if(row)
 463    {
 464        delete [] row;
 465    }
 466
 467    if(pBMI)
 468    {
 469        delete [] pBMI;
 470    }
 471
 472    if(!bStatus)
 473    {
 474        Destroy();
 475    }
 476
 477    return bStatus;
 478}
 479
 480BOOL CImage::LoadBitmapFromJPEGFile(LPTSTR szFileName)
 481{
 482    BOOL bStatus = false;
 483    struct jpeg_decompress_struct cinfo;
 484    struct jpeg_error_mgr jerr;
 485    FILE* fp = NULL;  
 486    JSAMPROW row = NULL;
 487    BITMAPINFO bmi;
 488    HDC hDC = NULL;
 489
 490    cinfo.err = jpeg_std_error(&jerr);
 491    jpeg_create_decompress(&cinfo);
 492
 493    _TFOPEN_S(fp, szFileName, _T("rb"));
 494    if (!fp)
 495    {
 496        goto cleanup;
 497    }
 498
 499    jpeg_stdio_src(&cinfo, fp);
 500
 501    jpeg_read_header(&cinfo, TRUE);
 502
 503    jpeg_start_decompress(&cinfo);
 504
 505    row = new BYTE[cinfo.output_width*3+10];
 506
 507    hDC = GetDC(NULL);
 508
 509    {
 510        CAutoLock lock(&m_csLock);
 511        if(m_bLoadCancelled)
 512            goto cleanup;
 513        m_hBitmap = CreateCompatibleBitmap(hDC, cinfo.output_width, cinfo.output_height);  
 514    }
 515
 516    memset(&bmi, 0, sizeof(bmi));  
 517    bmi.bmiHeader.biSize = sizeof(bmi);
 518    bmi.bmiHeader.biBitCount = 24;
 519    bmi.bmiHeader.biWidth = cinfo.output_width;
 520    bmi.bmiHeader.biHeight = cinfo.output_height;
 521    bmi.bmiHeader.biPlanes = 1;
 522    bmi.bmiHeader.biCompression = BI_RGB;
 523    bmi.bmiHeader.biSizeImage = cinfo.output_width*cinfo.output_height*3;
 524
 525    while (cinfo.output_scanline < cinfo.output_height)
 526    {    
 527        jpeg_read_scanlines(&cinfo, &row, 1); 
 528
 529        if(cinfo.out_color_components==3)
 530        {
 531            // Convert RGB to BGR
 532            UINT i;
 533            for(i=0; i<cinfo.output_width; i++)
 534            {
 535                BYTE tmp = row[i*3+0];
 536                row[i*3+0] = row[i*3+2];
 537                row[i*3+2] = tmp;
 538            }
 539        }
 540        else
 541        {
 542            // Convert grayscale to BGR
 543            int i;
 544            for(i=cinfo.output_width-1; i>=0; i--)
 545            {
 546                row[i*3+0] = row[i];
 547                row[i*3+1] = row[i];
 548                row[i*3+2] = row[i];
 549            }
 550        }
 551
 552        {
 553            CAutoLock lock(&m_csLock);
 554            int n = SetDIBits(hDC, m_hBitmap, cinfo.output_height-cinfo.output_scanline, 1, row, &bmi, DIB_RGB_COLORS);
 555            if(n==0)
 556                goto cleanup;
 557        }
 558    }
 559
 560    jpeg_finish_decompress(&cinfo);
 561    jpeg_destroy_decompress(&cinfo);
 562
 563    bStatus = true;
 564
 565cleanup:
 566
 567    if(row)
 568        delete [] row;
 569
 570    if(!bStatus)
 571    {
 572        Destroy();
 573    }
 574
 575	if(fp!=NULL)
 576	{
 577		fclose(fp);
 578		fp = NULL;
 579	}
 580
 581    return bStatus;
 582}
 583
 584void CImage::Draw(HDC hDC, LPRECT prcDraw)
 585{
 586    CAutoLock lock(&m_csLock);
 587    HPALETTE hOldPalette = NULL;
 588
 589    CRect rcDraw = prcDraw;
 590    BITMAP        bm;
 591    GetObject( m_hBitmap, sizeof(BITMAP), &bm );
 592
 593    HDC hMemDC = CreateCompatibleDC( hDC );
 594    HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, m_hBitmap );
 595    if(m_hPalette)
 596    {
 597        hOldPalette = SelectPalette(hDC, m_hPalette, FALSE );
 598        RealizePalette( hDC );
 599    }
 600
 601    if((float)rcDraw.Width()/(float)bm.bmWidth <
 602        (float)rcDraw.Height()/(float)bm.bmHeight)
 603    {    
 604        int nDstMid = rcDraw.top + rcDraw.Height()/2;
 605        int nDstHeight = (int)( rcDraw.Width()*(float)bm.bmHeight/bm.bmWidth ); 
 606        rcDraw.top = nDstMid - nDstHeight/2; 
 607        rcDraw.bottom = nDstMid + nDstHeight/2; 
 608    }
 609    else
 610    {
 611        int nDstMid = rcDraw.left + rcDraw.Width()/2;
 612        int nDstWidth = (int)( rcDraw.Height()*(float)bm.bmWidth/bm.bmHeight ); 
 613        rcDraw.left = nDstMid - nDstWidth/2; 
 614        rcDraw.right = nDstMid + nDstWidth/2; 
 615    }
 616
 617    int nOldMode = SetStretchBltMode(hDC, HALFTONE);
 618    StretchBlt(hDC, rcDraw.left, rcDraw.top, rcDraw.Width(), rcDraw.Height(), 
 619        hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY );
 620
 621    SetStretchBltMode(hDC, nOldMode);
 622    SelectObject( hMemDC, hOldBitmap );  
 623    if(m_hPalette)
 624    {
 625        SelectPalette( hDC, hOldPalette, FALSE ); 
 626    }
 627}
 628
 629//-----------------------------------------------------------------------------
 630// CVideo implementation
 631//-----------------------------------------------------------------------------
 632
 633CVideo::CVideo()
 634{
 635	m_pf = NULL;
 636	m_hbmpFrame = NULL;
 637	m_pFrameBits = NULL;
 638	m_pDIB = NULL;	
 639	m_hDC = NULL;
 640	m_hOldBitmap = NULL;	
 641	m_buf = NULL;
 642	m_psetup = NULL;
 643	m_pctx = NULL;	
 644	m_pos = 0;	
 645	m_nFrameWidth = 0;
 646	m_nFrameHeight = 0;
 647	m_nFrameInterval = 0;
 648	ogg_sync_init(&m_state);
 649	ogg_stream_init(&m_stream, 0);
 650	memset(&m_packet, 0, sizeof(m_packet));	
 651	th_info_init(&m_info);
 652	th_comment_init(&m_comment);
 653}
 654
 655CVideo::~CVideo()
 656{
 657	Destroy();
 658}
 659
 660BOOL CVideo::IsVideoFile(LPCTSTR szFileName)
 661{
 662	FILE* f = NULL;
 663    _TFOPEN_S(f, szFileName, _T("rb"));
 664    if(f==NULL)
 665        return FALSE;
 666
 667    if(IsOGG(f))
 668    {
 669        fclose(f);
 670        return TRUE;
 671    }
 672
 673    fclose(f);
 674    return FALSE;
 675}
 676
 677BOOL CVideo::IsOGG(FILE* f)
 678{
 679	rewind(f);
 680
 681    // Read first three bytes (Ogg).
 682    
 683    BYTE uchBytes[4];
 684    int n = (int)fread(uchBytes, 1, 3, f);
 685    if(n!=3)
 686        return FALSE;
 687
 688    if(memcmp(uchBytes, "Ogg", 3)==0)
 689        return TRUE; // This is an OGG file
 690
 691    return FALSE;
 692}
 693
 694// Loads video from file.
 695BOOL CVideo::Load(LPCTSTR szFileName)
 696{
 697	// Destroy if previously loaded
 698	if(!m_sFileName.IsEmpty())
 699		Destroy();
 700
 701	// Open file
 702    FILE* f = NULL;
 703    _TFOPEN_S(f, szFileName, _T("rb"));
 704    if(f==NULL)
 705        return FALSE;
 706
 707	// Check OGG signature
 708    if(IsOGG(f))
 709    {
 710        fclose(f);
 711		// Load OGG video
 712        return LoadOggFile(szFileName);
 713    }
 714    
 715	// Close file
 716    fclose(f);
 717    return FALSE; 
 718}
 719
 720void CVideo::Destroy()
 721{
 722	m_sFileName.Empty();
 723
 724	if(m_pf)
 725	{
 726		fclose(m_pf);
 727		m_pf = NULL;
 728	}
 729
 730	//ogg_packet_clear(&m_packet);
 731
 732	ogg_stream_clear(&m_stream);
 733
 734	// Call th_decode_free() to release all decoder memory.
 735	if(m_pctx)
 736	{
 737		th_decode_free(m_pctx);
 738		m_pctx = NULL;
 739	}
 740
 741	ogg_sync_clear(&m_state);
 742	th_info_clear(&m_info);
 743	th_comment_clear(&m_comment);
 744
 745	if(m_hbmpFrame!=NULL)
 746	{
 747		DeleteObject(m_hbmpFrame);
 748		m_hbmpFrame = NULL;
 749	}
 750
 751	if(m_pDIB)
 752	{
 753		delete [] m_pDIB;
 754		m_pDIB = NULL;
 755	}
 756		
 757	m_pFrameBits = NULL;
 758
 759	if(m_hDC!=NULL)
 760	{
 761		DeleteDC(m_hDC);	
 762		m_hDC = NULL;
 763	}
 764
 765	m_hOldBitmap = NULL;
 766}
 767
 768BOOL CVideo::LoadOggFile(LPCTSTR szFileName)
 769{
 770	bool bStatus = false;		
 771	int ret = -1;
 772
 773	// Open OGG file
 774	_TFOPEN_S(m_pf, szFileName, _T("rb"));
 775    if(m_pf==NULL)
 776        goto cleanup; // Error opening file
 777    
 778	// Init theora decoder structures
 779	th_info_init(&m_info);
 780    th_comment_init(&m_comment);
 781
 782	// The first thing we need to do when reading an Ogg file is find 
 783	// the first page of data. We use a ogg_sync_state structure to keep 
 784	// track of search for the page data. This needs to be initialized 
 785	// with ogg_sync_init and later cleaned up with ogg_sync_clear:
 786	if(0!=ogg_sync_init(&m_state))
 787		goto cleanup; // Error initializing sync state
 788				
 789	// Parse the header packets by repeatedly calling th_decode_headerin()
 790	while(ReadOGGPacket())
 791	{
 792		ret = th_decode_headerin(&m_info, &m_comment, &m_psetup, &m_packet);
 793		if(ret==0)
 794		{
 795			// Video data encountered
 796			break;
 797		}
 798		else if(ret==TH_EFAULT)
 799		{
 800			// Some parameters are incorrect
 801			goto cleanup;
 802		}
 803		else if(ret==TH_EBADHEADER)
 804		{
 805			//goto cleanup;
 806		}			
 807		else if(ret==TH_EVERSION)
 808		{
 809			//goto cleanup;
 810		}
 811		else if(ret==TH_ENOTFORMAT )
 812		{
 813			//goto cleanup;
 814		}			
 815	}
 816
 817	/*Allocate YV12 image */	
 818	m_nFrameWidth = m_info.frame_width;
 819	m_nFrameHeight = m_info.frame_height;
 820	m_nFrameInterval = (int)((float)m_info.fps_denominator/(float)m_info.fps_numerator*1000);
 821		
 822	if(m_hbmpFrame==NULL)
 823	{
 824		// Calculate frame size
 825
 826		CreateFrameDIB(m_nFrameWidth, m_nFrameHeight, 24);
 827	}
 828
 829	// Allocate a th_dec_ctx handle with th_decode_alloc().
 830	m_pctx = th_decode_alloc(&m_info, m_psetup);
 831
 832	// Call th_setup_free() to free any memory used for codec setup information.
 833	th_setup_free(m_psetup);
 834	m_psetup = NULL;
 835
 836	// Perform any additional decoder configuration with th_decode_ctl().
 837	
 838	m_sFileName = szFileName;
 839
 840	// Done
 841	bStatus = true;
 842
 843cleanup:
 844
 845	return bStatus;
 846}
 847
 848BOOL CVideo::ReadOGGPage()
 849{
 850	BOOL bStatus = FALSE;
 851	size_t nBytes = 0;
 852
 853	// Read an entire page from OGG file
 854
 855	while(ogg_sync_pageout(&m_state, &m_page) != 1) 
 856	{
 857		// Allocate buffer
 858		m_buf = ogg_sync_buffer(&m_state, 4096);
 859		if(m_buf==NULL)
 860			goto cleanup;
 861
 862		// Read a portion of data from file
 863		nBytes = fread(m_buf, 1, 4096, m_pf);
 864		if(nBytes==0)
 865			goto cleanup; // End of file
 866				
 867		if(0!=ogg_sync_wrote(&m_state, (long)nBytes))
 868			goto cleanup; // Failed
 869	}
 870
 871	bStatus = true;
 872
 873cleanup:
 874
 875	return bStatus;
 876}
 877
 878BOOL CVideo::ReadOGGPacket()
 879{
 880	BOOL bStatus = FALSE;
 881	int ret = -1;
 882
 883	while(1)
 884	{	
 885		// Call ogg_stream_packetout. This will return a value indicating if 
 886		// a packet of data is available in the stream. If it is not then we 
 887		// need to read another page (following the same steps previously) and 
 888		// add it to the stream, calling ogg_stream_packetout again until it 
 889		// tells us a packet is available. The packet&#x2019;s data is stored in an 
 890		// ogg_packet object.
 891		ret = ogg_stream_packetout(&m_stream, &m_packet);    
 892		if (ret == 0) 
 893		{
 894			// Need more data to be able to complete the packet
 895
 896			// New page
 897			if(!ReadOGGPage())
 898				goto cleanup;
 899
 900			// If this page is the beginning of the logical stream...
 901			if (ogg_page_bos(&m_page))
 902			{	
 903				// Get page's serial number
 904				int serial = ogg_page_serialno(&m_page);
 905
 906				// Init OGG stream
 907				ret = ogg_stream_init(&m_stream, serial);
 908				if(ret != 0)
 909					goto cleanup;  // Failed to init stream
 910			}
 911	
 912			// Pass the page data to stream
 913			ret = ogg_stream_pagein(&m_stream, &m_page);
 914			if(ret!=0)
 915				goto cleanup;
 916
 917			continue;
 918		}
 919		else if (ret == -1) 
 920		{
 921			// We are out of sync and there is a gap in the data.
 922			// We lost a page somewhere.
 923			break;
 924		}
 925		
 926		// A packet is available, this is what we pass to the 
 927		// theora library to decode.
 928		bStatus = true;
 929		break;
 930	}
 931
 932cleanup:
 933
 934	return bStatus;
 935}
 936
 937// Decodes next video frame and returns pointer to bitmap.
 938HBITMAP CVideo::DecodeFrame(BOOL bFirstFrame, CSize& FrameSize, int& nDuration)
 939{
 940	FrameSize.cx = m_nFrameWidth;
 941	FrameSize.cy = m_nFrameHeight;
 942	nDuration = m_nFrameInterval;
 943		
 944	// For the first frame, we use the packet that was read previously
 945	// For next frames, we need to read new packets
 946	if(!bFirstFrame)
 947	{
 948		// Read packet
 949		if(!ReadOGGPacket())
 950			return NULL; // No more packets
 951	}
 952
 953	// Feed the packet to decoder
 954	int ret = th_decode_packetin(m_pctx, &m_packet, &m_pos);
 955	if(ret!=0 && ret!=TH_DUPFRAME)
 956		return NULL; // Decoding error
 957
 958	if(ret!=TH_DUPFRAME)
 959	{
 960		// Retrieve the uncompressed video data via th_decode_ycbcr_out().	
 961		th_decode_ycbcr_out(m_pctx, &m_raw[0]);
 962		
 963		CAutoLock lock(&m_csLock);
 964
 965		// Convert YV12 to RGB
 966		YV12_To_RGB((unsigned char*)m_pFrameBits, 
 967			m_nFrameWidth, m_nFrameHeight, 
 968			m_nFrameWidth*3+(m_nFrameWidth*3)%4, 
 969			&m_raw[0]);
 970	}
 971	
 972	// Return bitmap
 973	return m_hbmpFrame;
 974}
 975
 976void CVideo::DrawFrame(HDC hDC, LPRECT prcDraw)
 977{
 978    CAutoLock lock(&m_csLock);
 979    
 980    CRect rcDraw = prcDraw;
 981    BITMAP        bm;
 982    GetObject(m_hbmpFrame, sizeof(BITMAP), &bm );
 983	    
 984    if((float)rcDraw.Width()/(float)bm.bmWidth <
 985        (float)rcDraw.Height()/(float)bm.bmHeight)
 986    {    
 987        int nDstMid = rcDraw.top + rcDraw.Height()/2;
 988        int nDstHeight = (int)( rcDraw.Width()*(float)bm.bmHeight/bm.bmWidth ); 
 989        rcDraw.top = nDstMid - nDstHeight/2; 
 990        rcDraw.bottom = nDstMid + nDstHeight/2; 
 991    }
 992    else
 993    {
 994        int nDstMid = rcDraw.left + rcDraw.Width()/2;
 995        int nDstWidth = (int)( rcDraw.Height()*(float)bm.bmWidth/bm.bmHeight ); 
 996        rcDraw.left = nDstMid - nDstWidth/2; 
 997        rcDraw.right = nDstMid + nDstWidth/2; 
 998    }
 999
1000    int nOldMode = SetStretchBltMode(hDC, HALFTONE);
1001    StretchBlt(hDC, rcDraw.left, rcDraw.top, rcDraw.Width(), rcDraw.Height(), 
1002        m_hDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY );
1003
1004    SetStretchBltMode(hDC, nOldMode);    
1005}
1006
1007void CVideo::Reset()
1008{
1009	
1010}
1011
1012// Returns TRUE if video is valid, otherwise returns FALSE
1013BOOL CVideo::IsValid()
1014{
1015	return m_sFileName.IsEmpty()?FALSE:TRUE;
1016}
1017
1018// Converts an YV12 image to RGB24 image.
1019void CVideo::YV12_To_RGB(unsigned char *pRGBData, int nFrameWidth, 
1020			int nFrameHeight, int nRGBStride, th_ycbcr_buffer raw)
1021{
1022
1023	int x, y;
1024	for(y=0; y<nFrameHeight;y++)
1025	{
1026		for (x=0; x < nFrameWidth; x ++)
1027		{
1028			int nRGBOffs = y*nRGBStride+x*3;
1029
1030			float Y = raw[0].data[y*raw[0].stride+x];
1031			float U = raw[1].data[y/2*raw[1].stride+x/2];
1032			float V = raw[2].data[y/2*raw[2].stride+x/2];
1033
1034			pRGBData[nRGBOffs+0] = CLAMP((int)(1.164*(Y - 16) + 2.018*(U - 128)));
1035			pRGBData[nRGBOffs+1] = CLAMP((int)(1.164*(Y - 16) - 0.813*(V - 128) - 0.391*(U - 128)));
1036			pRGBData[nRGBOffs+2] = CLAMP((int)(1.164*(Y - 16) + 1.596*(V - 128)));
1037		}
1038	}	
1039}
1040
1041BOOL CVideo::CreateFrameDIB(DWORD dwWidth, DWORD dwHeight, int nBits)
1042{
1043	if (m_pDIB) 
1044		return FALSE;
1045
1046	const DWORD dwcBihSize = sizeof(BITMAPINFOHEADER);
1047
1048	// Calculate the memory required for the DIB
1049	DWORD dwSize = dwcBihSize +
1050		(2>>nBits) * sizeof(RGBQUAD) +
1051		((nBits * dwWidth) * dwHeight);
1052
1053	m_pDIB = (LPBITMAPINFO)new BYTE[dwSize];
1054	if (!m_pDIB) 
1055		return FALSE;
1056
1057
1058	m_pDIB->bmiHeader.biSize = dwcBihSize;
1059	m_pDIB->bmiHeader.biWidth = dwWidth;
1060	m_pDIB->bmiHeader.biHeight = -(LONG)dwHeight;
1061	m_pDIB->bmiHeader.biBitCount = (WORD)nBits;
1062	m_pDIB->bmiHeader.biPlanes = 1;
1063	m_pDIB->bmiHeader.biCompression = BI_RGB;
1064	m_pDIB->bmiHeader.biXPelsPerMeter = 1000;
1065	m_pDIB->bmiHeader.biYPelsPerMeter = 1000;
1066	m_pDIB->bmiHeader.biClrUsed = 0;
1067	m_pDIB->bmiHeader.biClrImportant = 0;
1068
1069	LPRGBQUAD lpColors =
1070		(LPRGBQUAD)(m_pDIB+m_pDIB->bmiHeader.biSize);
1071	int nColors=2>>m_pDIB->bmiHeader.biBitCount;
1072	for(int i=0;i<nColors;i++)
1073	{
1074		lpColors[i].rgbRed=0;
1075		lpColors[i].rgbBlue=0;
1076		lpColors[i].rgbGreen=0;
1077		lpColors[i].rgbReserved=0;
1078	}
1079
1080	m_hDC = CreateCompatibleDC(GetDC(NULL));
1081
1082	m_hbmpFrame = CreateDIBSection(m_hDC, m_pDIB, DIB_RGB_COLORS, &m_pFrameBits,
1083		NULL, 0);
1084
1085	m_hOldBitmap = (HBITMAP)SelectObject(m_hDC, m_hbmpFrame);
1086
1087	return TRUE;
1088}
1089
1090
1091//-----------------------------------------------------------------------------
1092// CFilePreviewCtrl implementation
1093//-----------------------------------------------------------------------------
1094
1095CFilePreviewCtrl::CFilePreviewCtrl()
1096{   
1097    m_xChar = 10;
1098    m_yChar = 10;
1099    m_nHScrollPos = 0;
1100    m_nHScrollMax = 0;
1101    m_nMaxColsPerPage = 0;
1102    m_nMaxLinesPerPage = 0;
1103    m_nMaxDisplayWidth = 0;   
1104    m_uNumLines = 0;
1105    m_nVScrollPos = 0;
1106    m_nVScrollMax = 0;
1107    m_nBytesPerLine = 16; 
1108    m_cchTabLength = 4;
1109    m_sEmptyMsg = _T("No data to display");
1110    m_hFont = CreateFont(14, 7, 0, 0, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 
1111        ANTIALIASED_QUALITY, FIXED_PITCH, _T("Courier"));
1112
1113    m_hWorkerThread = NULL;
1114    m_bCancelled = FALSE;
1115    m_PreviewMode = PREVIEW_HEX;
1116    m_TextEncoding = ENC_ASCII;
1117    m_nEncSignatureLen = 0;
1118}
1119
1120CFilePreviewCtrl::~CFilePreviewCtrl()
1121{
1122	m_bmp.Destroy();
1123	m_fm.Destroy();
1124
1125    DeleteObject(m_hFont);  	
1126}
1127
1128LPCTSTR CFilePreviewCtrl::GetFile()
1129{
1130    if(m_sFileName.IsEmpty())
1131        return NULL;
1132    return m_sFileName;
1133}
1134
1135BOOL CFilePreviewCtrl::SetFile(LPCTSTR szFileName, PreviewMode mode, TextEncoding enc)
1136{
1137    // If we are currently processing some file in background,
1138    // stop the worker thread
1139    if(m_hWorkerThread!=NULL)
1140    {
1141        m_bCancelled = TRUE;
1142        m_bmp.Cancel();
1143        WaitForSingleObject(m_hWorkerThread, INFINITE);
1144        m_hWorkerThread = NULL;
1145    }
1146
1147    CAutoLock lock(&m_csLock);
1148
1149    m_sFileName = szFileName;
1150
1151    if(mode==PREVIEW_AUTO)
1152        m_PreviewMode = DetectPreviewMode(m_sFileName);
1153    else
1154        m_PreviewMode = mode;
1155
1156    if(szFileName==NULL)
1157    {
1158        m_fm.Destroy();
1159    }
1160    else
1161    {
1162        if(!m_fm.Init(m_sFileName))
1163        {
1164            m_sFileName.Empty();
1165            return FALSE;
1166        }
1167    }
1168
1169    CRect rcClient;
1170    GetClientRect(&rcClient);
1171
1172    HDC hDC = GetDC();
1173    HFONT hOldFont = (HFONT)SelectObject(hDC, m_hFont);
1174
1175    LOGFONT lf;
1176    ZeroMemory(&lf, sizeof(LOGFONT));
1177    GetObject(m_hFont, sizeof(LOGFONT), &lf);			
1178    m_xChar = lf.lfWidth;
1179    m_yChar = lf.lfHeight;
1180
1181    SelectObject(hDC, hOldFont);
1182
1183    m_nVScrollPos = 0; 
1184    m_nVScrollMax = 0;
1185    m_nHScrollPos = 0;
1186    m_nHScrollMax = 0;
1187    m_aTextLines.clear();
1188    m_uNumLines = 0;
1189    m_nMaxDisplayWidth = 0;
1190    m_bmp.Destroy();
1191	m_video.Destroy();
1192
1193    if(m_PreviewMode==PREVIEW_HEX)
1194    {
1195        if(m_fm.GetSize()!=0)
1196        {
1197            m_nMaxDisplayWidth = 
1198                8 +				//adress
1199                2 +				//padding
1200                m_nBytesPerLine * 3 +	//hex column
1201                1 +				//padding
1202                m_nBytesPerLine;	//ascii column
1203        }
1204
1205        m_uNumLines = m_fm.GetSize() / m_nBytesPerLine;
1206
1207        if(m_fm.GetSize() % m_nBytesPerLine)
1208            m_uNumLines++;
1209    }
1210    else if(m_PreviewMode==PREVIEW_TEXT)
1211    {       
1212        if(enc==ENC_AUTO)
1213            m_TextEncoding = DetectTextEncoding(m_sFileName, m_nEncSignatureLen);
1214        else
1215        {
1216            m_TextEncoding = enc;
1217            // Determine the length of the signature.
1218            int nSignatureLen = 0;
1219            TextEncoding enc2 = DetectTextEncoding(m_sFileName, nSignatureLen);
1220            if(enc==enc2)
1221                m_nEncSignatureLen = nSignatureLen;
1222        }
1223
1224        m_bCancelled = FALSE;
1225        m_hWorkerThread = CreateThread(NULL, 0, WorkerThread, this, 0, NULL);
1226        ::SetTimer(m_hWnd, 0, 250, NULL);
1227    }
1228    else if(m_PreviewMode==PREVIEW_IMAGE)
1229    {
1230        m_bCancelled = FALSE;
1231        m_hWorkerThread = CreateThread(NULL, 0, WorkerThread, this, 0, NULL);    
1232        ::SetTimer(m_hWnd, 0, 250, NULL);
1233    }
1234	else if(m_PreviewMode==PREVIEW_VIDEO)
1235    {
1236        m_bCancelled = FALSE;
1237        m_hWorkerThread = CreateThread(NULL, 0, WorkerThread, this, 0, NULL);    
1238        //::SetTimer(m_hWnd, 0, 250, NULL);
1239    }
1240
1241    SetupScrollbars();
1242    InvalidateRect(NULL, FALSE);
1243    UpdateWindow();
1244
1245    return TRUE;
1246}
1247
1248PreviewMode CFilePreviewCtrl::GetPreviewMode()
1249{
1250    return m_PreviewMode;
1251}
1252
1253void CFilePreviewCtrl::SetPreviewMode(PreviewMode mode)
1254{
1255    SetFile(m_sFileName, mode);
1256}
1257
1258TextEncoding CFilePreviewCtrl::GetTextEncoding()
1259{
1260    return m_TextEncoding;
1261}
1262
1263void CFilePreviewCtrl::SetTextEncoding(TextEncoding enc)
1264{
1265    SetFile(m_sFileName, m_PreviewMode, enc);
1266}
1267
1268PreviewMode CFilePreviewCtrl::DetectPreviewMode(LPCTSTR szFileName)
1269{
1270    PreviewMode mode = PREVIEW_HEX;
1271    CString sFileName;
1272    std::set<CString>::iterator it;
1273    std::set<CString> aTextFileExtensions;
1274    CString sExtension;
1275
1276    sFileName = szFileName;
1277
1278    if(CImage::IsImageFile(sFileName))
1279    {
1280        mode = PREVIEW_IMAGE;
1281        goto cleanup;
1282    }
1283	else if(CVideo::IsVideoFile(sFileName))
1284    {
1285        mode = PREVIEW_VIDEO;
1286        goto cleanup;
1287    }
1288
1289    int backslash_pos = sFileName.ReverseFind('\\');
1290    if(backslash_pos>=0)
1291        sFileName = sFileName.Mid(backslash_pos+1);  
1292    int dot_pos = sFileName.ReverseFind('.');
1293    if(dot_pos>0)
1294        sExtension = sFileName.Mid(dot_pos+1);
1295    sExtension.MakeUpper();
1296
1297    aTextFileExtensions.insert(_T("TXT"));
1298    aTextFileExtensions.insert(_T("INI"));
1299    aTextFileExtensions.insert(_T("LOG"));
1300    aTextFileExtensions.insert(_T("XML"));
1301    aTextFileExtensions.insert(_T("HTM"));
1302    aTextFileExtensions.insert(_T("HTML"));
1303    aTextFileExtensions.insert(_T("JS"));
1304    aTextFileExtensions.insert(_T("C"));
1305    aTextFileExtensions.insert(_T("H"));
1306    aTextFileExtensions.insert(_T("CPP"));
1307    aTextFileExtensions.insert(_T("HPP"));
1308
1309    it = aTextFileExtensions.find(sExtension);
1310    if(it!=aTextFileExtensions.end())
1311    {
1312        mode = PREVIEW_TEXT;
1313    }
1314
1315cleanup:
1316
1317    return mode;
1318}
1319
1320TextEncoding CFilePreviewCtrl::DetectTextEncoding(LPCTSTR szFileName, int& nSignatureLen)
1321{
1322    TextEncoding enc = ENC_ASCII;
1323    nSignatureLen = 0;
1324
1325    FILE* f = NULL;
1326
1327#if _MSC_VER<1400
1328    f = _tfopen(szFileName, _T("rb"));
1329#else
1330    _tfopen_s(&f, szFileName, _T("rb"));
1331#endif
1332
1333    if(f==NULL)
1334        goto cleanup;   
1335
1336    BYTE signature2[2];
1337    size_t nRead = fread(signature2, 1, 2, f);
1338    if(nRead!=2)
1339        goto cleanup;
1340
1341    // Compare with UTF-16 LE signature
1342    if(signature2[0]==0xFF && 
1343        signature2[1]==0xFE 
1344        )
1345    {
1346        enc = ENC_UTF16_LE;
1347        nSignatureLen = 2;
1348        goto cleanup;
1349    }
1350
1351    // Compare with UTF-16 BE signature
1352    if(signature2[0]==0xFE && 
1353        signature2[1]==0xFF 
1354        )
1355    {
1356        enc = ENC_UTF16_BE;
1357        nSignatureLen = 2;
1358        goto cleanup;
1359    }
1360
1361    rewind(f);
1362
1363    BYTE signature3[3];
1364    nRead = fread(signature3, 1, 3, f);
1365    if(nRead!=3)
1366        goto cleanup;
1367
1368    // Compare with UTF-8 signature
1369    if(signature3[0]==0xEF && 
1370        signature3[1]==0xBB && 
1371        signature3[2]==0xBF 
1372        )
1373    {
1374        enc = ENC_UTF8;
1375        nSignatureLen = 3;
1376        goto cleanup;
1377    }
1378
1379cleanup:
1380
1381    fclose(f);
1382
1383    return enc;
1384}
1385
1386DWORD WINAPI CFilePreviewCtrl::WorkerThread(LPVOID lpParam)
1387{
1388    CFilePreviewCtrl* pCtrl = (CFilePreviewCtrl*)lpParam;
1389    pCtrl->DoInWorkerThread(); 
1390    return 0;
1391}
1392
1393void CFilePreviewCtrl::DoInWorkerThread()
1394{
1395    if(m_PreviewMode==PREVIEW_TEXT)
1396        ParseText();
1397    else if(m_PreviewMode==PREVIEW_IMAGE)
1398        LoadBitmap();
1399	if(m_PreviewMode==PREVIEW_VIDEO)
1400        LoadVideo();
1401}
1402
1403WCHAR swap_bytes(WCHAR src_char)
1404{
1405    return (WCHAR)MAKEWORD((src_char>>8), (src_char&0xFF));
1406}
1407
1408void CFilePreviewCtrl::ParseText()
1409{
1410    DWORD dwFileSize = (DWORD)m_fm.GetSize();
1411    DWORD dwOffset = 0;
1412    DWORD dwPrevOffset = 0;
1413    int nTabs = 0;  
1414
1415    if(dwFileSize!=0)
1416    {
1417        CAutoLock lock(&m_csLock);
1418        if(m_PreviewMode==PREVIEW_TEXT)
1419        { 
1420            dwOffset+=m_nEncSignatureLen;
1421            m_aTextLines.push_back(dwOffset);
1422        }
1423        else
1424        {
1425            m_aTextLines.push_back(0);
1426        }
1427
1428        m_uNumLines++;
1429    }
1430
1431    for(;;)
1432    {
1433        {
1434            CAutoLock lock(&m_csLock);        
1435            if(m_bCancelled)
1436                break;
1437        }
1438
1439        DWORD dwLength = 4096;
1440        if(dwOffset+dwLength>=dwFileSize)
1441            dwLength = dwFileSize-dwOffset;
1442
1443        if(dwLength==0)
1444            break;
1445
1446        LPBYTE ptr = m_fm.CreateView(dwOffset, dwLength);
1447
1448        UINT i;
1449        for(i=0; i<dwLength; )
1450        {
1451            {
1452                CAutoLock lock(&m_csLock);        
1453                if(m_bCancelled)
1454                    break;
1455            }
1456
1457            if(m_TextEncoding==ENC_UTF16_LE || m_TextEncoding==ENC_UTF16_BE)
1458            {
1459                WCHAR src_char = ((WCHAR*)ptr)[i/2];
1460
1461                WCHAR c = m_TextEncoding==ENC_UTF16_LE?src_char:swap_bytes(src_char);
1462
1463                if(c=='\t')
1464                {
1465                    nTabs++;
1466                }
1467                else if(c=='\n')
1468                {
1469                    CAutoLock lock(&m_csLock);        
1470                    m_aTextLines.push_back(dwOffset+i+2);
1471                    int cchLineLength = dwOffset+i+2-dwPrevOffset;
1472                    if(nTabs!=0)
1473                        cchLineLength += nTabs*(m_cchTabLength-1);
1474
1475                    m_nMaxDisplayWidth = max(m_nMaxDisplayWidth, cchLineLength);
1476                    m_uNumLines++;
1477                    dwPrevOffset = dwOffset+i+2;        
1478                    nTabs = 0;
1479                }
1480
1481                i+=2;
1482            }
1483            else
1484            {
1485                char c = ((char*)ptr)[i];      
1486
1487                if(c=='\t')
1488                {
1489                    nTabs++;
1490                }
1491                else if(c=='\n')
1492                {
1493                    CAutoLock lock(&m_csLock);        
1494                    m_aTextLines.push_back(dwOffset+i+1);
1495                    int cchLineLength = dwOffset+i+1-dwPrevOffset;
1496                    if(nTabs!=0)
1497                        cchLineLength += nTabs*(m_cchTabLength-1);
1498
1499                    m_nMaxDisplayWidth = max(m_nMaxDisplayWidth, cchLineLength);
1500                    m_uNumLines++;
1501                    dwPrevOffset = dwOffset+i+1;        
1502                    nTabs = 0;
1503                }
1504
1505                i++;
1506            }
1507        }
1508
1509        dwOffset += dwLength;        
1510    }
1511
1512    PostMessage(WM_FPC_COMPLETE);
1513}
1514
1515void CFilePreviewCtrl::LoadBitmap()
1516{
1517    m_bmp.Load(m_sFileName);
1518    PostMessage(WM_FPC_COMPLETE);
1519}
1520
1521void CFilePreviewCtrl::LoadVideo()
1522{
1523	int nFrame = 0;
1524	CSize FrameSize;
1525	int nFrameInterval;	
1526    if(m_video.Load(m_sFileName))
1527	{		
1528		while(m_video.DecodeFrame(nFrame==0?TRUE:FALSE, FrameSize, nFrameInterval))
1529		{
1530			if(m_bCancelled)
1531				break;
1532
1533			nFrame++;		
1534			InvalidateRect(NULL);
1535		
1536			Sleep(nFrameInterval);
1537		}
1538
1539	}
1540
1541    PostMessage(WM_FPC_COMPLETE);
1542}
1543
1544void CFilePreviewCtrl::SetEmptyMessage(CString sText)
1545{
1546    m_sEmptyMsg = sText;
1547}
1548
1549BOOL CFilePreviewCtrl::SetBytesPerLine(int nBytesPerLine)
1550{
1551    if(nBytesPerLine<0)
1552        return FALSE;
1553
1554    m_nBytesPerLine = nBytesPerLine;
1555    return TRUE;
1556}
1557
1558void CFilePreviewCtrl::SetupScrollbars()
1559{
1560    CAutoLock lock(&m_csLock);
1561    CRect rcClient;
1562    GetClientRect(&rcClient);
1563
1564    SCROLLINFO sInfo;
1565
1566    //	Vertical scrollbar
1567
1568    m_nMaxLinesPerPage = (int)min(m_uNumLines, rcClient.Height() / m_yChar);
1569    m_nVScrollMax = (int)max(0, m_uNumLines-1);
1570    m_nVScrollPos = (int)min(m_nVScrollPos, m_nVScrollMax-m_nMaxLinesPerPage+1);
1571
1572    sInfo.cbSize = sizeof(SCROLLINFO);
1573    sInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
1574    sInfo.nMin	= 0;
1575    sInfo.nMax	= m_nVScrollMax;
1576    sInfo.nPos	= m_nVScrollPos;
1577    sInfo.nPage	= min(m_nMaxLinesPerPage, m_nVScrollMax+1);
1578    SetScrollInfo (SB_VERT, &sInfo, TRUE);
1579
1580    //	Horizontal scrollbar 
1581
1582    m_nMaxColsPerPage = min(m_nMaxDisplayWidth+1, rcClient.Width() / m_xChar);	
1583    m_nHScrollMax = max(0, m_nMaxDisplayWidth-1);
1584    m_nHScrollPos = min(m_nHScrollPos, m_nHScrollMax-m_nMaxColsPerPage+1);
1585
1586    sInfo.cbSize = sizeof(SCROLLINFO);
1587    sInfo.fMask = SIF_POS | SIF_PAGE | SIF_RANGE;
1588    sInfo.nMin	= 0;
1589    sInfo.nMax	= m_nHScrollMax;
1590    sInfo.nPos	= m_nHScrollPos;
1591    sInfo.nPage	= min(m_nMaxColsPerPage, m_nHScrollMax+1);
1592
1593    SetScrollInfo (SB_HORZ, &sInfo, TRUE);
1594
1595}	
1596
1597//
1598//	Create 1 line of a hex-dump, given a buffer of BYTES
1599//
1600CString CFilePreviewCtrl::FormatHexLine(LPBYTE pData, int nBytesInLine, ULONG64 uLineOffset)
1601{
1602    CString sResult;
1603    CString str;
1604    int i;
1605
1606    //print the hex address
1607    str.Format(_T("%08X  "), uLineOffset);
1608    sResult += str;
1609
1610    //print hex data
1611    for(i = 0; i < nBytesInLine; i++)
1612    {
1613        str.Format(_T("%02X "), pData[i]);
1614        sResult += str;
1615    }
1616
1617    //print some blanks if this isn't a full line
1618    for(; i < m_nBytesPerLine; i++)
1619    {
1620        str.Format(_T("   "));
1621        sResult += str;
1622    }
1623
1624    //print a gap between the hex and ascii
1625    sResult += _T(" ");
1626
1627    //print the ascii
1628    for(i = 0; i < nBytesInLine; i++)
1629    {
1630        BYTE c = pData[i];
1631        if(c < 32 || c > 128) c = '.';
1632        str.Format( _T("%c"), c);
1633        sResult += str;
1634    }
1635
1636    //print some blanks if this isn't a full line
1637    for(; i < m_nBytesPerLine; i++)
1638    {
1639        sResult += _T(" ");
1640    }
1641
1642    return sResult;
1643}
1644
1645//
1646//	Draw 1 line to the display
1647//
1648void CFilePreviewCtrl::DrawHexLine(HDC hdc, DWORD nLineNo)
1649{	
1650    int nBytesPerLine = m_nBytesPerLine;
1651
1652    if(m_fm.GetSize() - nLineNo * m_nBytesPerLine < (UINT)m_nBytesPerLine)
1653        nBytesPerLine= (DWORD)m_fm.GetSize() - nLineNo * m_nBytesPerLine;
1654
1655    //get data from our file mapping
1656    LPBYTE ptr = m_fm.CreateView(nLineNo * m_nBytesPerLine, nBytesPerLine);
1657
1658    //convert the data into a one-line hex-dump
1659    CString str = FormatHexLine(ptr, nBytesPerLine, nLineNo*m_nBytesPerLine );
1660
1661    //draw this line to the screen
1662    TextOut(hdc, -(int)(m_nHScrollPos * m_xChar), 
1663        (nLineNo - m_nVScrollPos) * (m_yChar-1) , str, str.GetLength());
1664}
1665
1666void CFilePreviewCtrl::DrawTextLine(HDC hdc, DWORD nLineNo)
1667{
1668    CRect rcClient;
1669    GetClientRect(&rcClient);
1670
1671    DWORD dwOffset = 0;
1672    DWORD dwLength = 0;
1673    {
1674        CAutoLock lock(&m_csLock);
1675        dwOffset = m_aTextLines[nLineNo];
1676        if(nLineNo==m_uNumLines-1)
1677            dwLength = (DWORD)m_fm.GetSize() - dwOffset;
1678        else
1679            dwLength = m_aTextLines[nLineNo+1]-dwOffset-1;
1680    }
1681
1682    if(dwLength==0)
1683        return;
1684
1685    //get data from our file mapping
1686    LPBYTE ptr = m_fm.CreateView(dwOffset, dwLength);
1687
1688    //draw this line to the screen
1689    CRect rcText;
1690    rcText.left = -(int)(m_nHScrollPos * m_xChar);
1691    rcText.top = (nLineNo - m_nVScrollPos) * m_yChar;
1692    rcText.right = rcClient.right;
1693    rcText.bottom = rcText.top + m_yChar;
1694    DRAWTEXTPARAMS params;
1695    memset(&params, 0, sizeof(DRAWTEXTPARAMS));
1696    params.cbSize = sizeof(DRAWTEXTPARAMS);
1697    params.iTabLength = m_xChar*m_cchTabLength;
1698
1699    DWORD dwFlags = DT_LEFT|DT_TOP|DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS;
1700
1701    if(m_TextEncoding==ENC_UTF8)
1702    {
1703        // Decode line
1704        strconv_t strconv;
1705        LPCWSTR szLine = strconv.utf82w((char*)ptr, dwLength-1);
1706        DrawTextExW(hdc, (LPWSTR)szLine, -1,  &rcText, dwFlags, &params);
1707    }
1708    else if(m_TextEncoding==ENC_UTF16_LE)
1709    {
1710        DrawTextExW(hdc, (WCHAR*)ptr, dwLength/2-1,  &rcText, 
1711            dwFlags, &params);   
1712    }
1713    else if(m_TextEncoding==ENC_UTF16_BE)
1714    {
1715        // Decode line
1716        strconv_t strconv;
1717        LPCWSTR szLine = strconv.w2w_be((WCHAR*)ptr, dwLength/2-1);
1718        DrawTextExW(hdc, (LPWSTR)szLine, -1,  &rcText, dwFlags, &params);
1719    }
1720    else // ASCII
1721    {
1722        DrawTextExA(hdc, (char*)ptr, dwLength-1,  &rcText, 
1723            dwFlags, &params);
1724    }
1725}
1726
1727void CFilePreviewCtrl::DoPaintEmpty(HDC hDC)
1728{
1729    RECT rcClient;
1730    GetClientRect(&rcClient);
1731
1732    HFONT hOldFont = (HFONT)SelectObject(hDC, m_hFont);
1733
1734    FillRect(hDC, &rcClient, (HBRUSH)GetStockObject(WHITE_BRUSH));
1735
1736    CRect rcText;
1737    DrawTextEx(hDC, m_sEmptyMsg.GetBuffer(0), -1, &rcText, DT_CALCRECT, NULL);
1738
1739    rcText.MoveToX(rcClient.right/2-rcText.right/2);
1740    DrawTextEx(hDC, m_sEmptyMsg.GetBuffer(0), -1, &rcText, DT_LEFT, NULL);
1741
1742    SelectObject(hDC, hOldFont);
1743}
1744
1745void CFilePreviewCtrl::DoPaintText(HDC hDC)
1746{
1747    HFONT hOldFont = (HFONT)SelectObject(hDC, m_hFont);
1748
1749    RECT rcClient;
1750    GetClientRect(&rcClient);
1751
1752    HRGN hRgn = CreateRectRgn(0, 0, rcClient.right, rcClient.bottom);
1753    SelectClipRgn(hDC, hRgn);
1754
1755    FillRect(hDC, &rcClient, (HBRUSH)GetStockObject(WHITE_BRUSH));
1756
1757    int iPaintBeg = max(0, m_nVScrollPos);			//only update the lines that 
1758    int iPaintEnd = (int)min(m_uNumLines, m_nVScrollPos + rcClient.bottom / m_yChar);		//need updating!!!!!!!!!!!!!
1759
1760    if(rcClient.bottom % m_yChar) iPaintEnd++;
1761    if(iPaintEnd > m_uNumLines) iPaintEnd--;	
1762
1763    //
1764    //	Only paint what needs to be!
1765    //
1766    int i;
1767    for(i = iPaintBeg; i < iPaintEnd; i++)
1768    {
1769        if(m_PreviewMode==PREVIEW_HEX)
1770            DrawHexLine(hDC, i);
1771        else
1772            DrawTextLine(hDC, i);				
1773    }
1774
1775    SelectObject(hDC, hOldFont);
1776}
1777
1778void CFilePreviewCtrl::DoPaintBitmap(HDC hDC)
1779{
1780    RECT rcClient;
1781    GetClientRect(&rcClient);
1782
1783    HRGN hRgn = CreateRectRgn(0, 0, rcClient.right, rcClient.bottom);
1784    SelectClipRgn(hDC, hRgn);
1785
1786    FillRect(hDC, &rcClient, (HBRUSH)GetStockObject(WHITE_BRUSH));
1787
1788    if(m_bmp.IsValid())
1789    {
1790        m_bmp.Draw(hDC, &rcClient);
1791    }
1792    else
1793    {
1794        DoPaintEmpty(hDC);
1795    }
1796}
1797
1798void CFilePreviewCtrl::DoPaintVideo(HDC hDC)
1799{
1800    RECT rcClient;
1801    GetClientRect(&rcClient);
1802
1803    HRGN hRgn = CreateRectRgn(0, 0, rcClient.right, rcClient.bottom);
1804    SelectClipRgn(hDC, hRgn);
1805
1806    FillRect(hDC, &rcClient, (HBRUSH)GetStockObject(WHITE_BRUSH));
1807
1808    if(m_video.IsValid())
1809    {
1810        m_video.DrawFrame(hDC, &rcClient);
1811    }
1812    else
1813    {
1814        DoPaintEmpty(hDC);
1815    }
1816}
1817
1818void CFilePreviewCtrl::DoPaint(HDC hDC)
1819{
1820    if(m_PreviewMode==PREVIEW_TEXT ||
1821        m_PreviewMode==PREVIEW_HEX)
1822    {
1823        DoPaintText(hDC);
1824    }
1825    else if(m_PreviewMode==PREVIEW_IMAGE)
1826    {
1827        DoPaintBitmap(hDC);
1828    }	
1829	else if(m_PreviewMode==PREVIEW_VIDEO)
1830	{
1831		DoPaintVideo(hDC);
1832	}
1833}
1834
1835LRESULT CFilePreviewCtrl::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1836{
1837    m_fm.Destroy();
1838    bHandled = FALSE;  
1839    return 0;
1840}
1841
1842LRESULT CFilePreviewCtrl::OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1843{
1844    SetupScrollbars();
1845
1846    InvalidateRect(NULL, FALSE);
1847    UpdateWindow();
1848
1849    return 0;
1850}
1851
1852LRESULT CFilePreviewCtrl::OnHScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1853{
1854    SCROLLINFO info;
1855    int nHScrollInc = 0;
1856    int  nOldHScrollPos = m_nHScrollPos;
1857
1858    switch (LOWORD(wParam))
1859    {
1860    case SB_LEFT:
1861        m_nHScrollPos = 0;
1862        break;
1863
1864    case SB_RIGHT:
1865        m_nHScrollPos = m_nHScrollMax + 1;
1866        break;
1867
1868    case SB_LINELEFT:
1869        if(m_nHScrollPos > 0) --m_nHScrollPos;
1870        break;
1871
1872    case SB_LINERIGHT:
1873        m_nHScrollPos++;
1874        break;
1875
1876    case SB_PAGELEFT:
1877        m_nHScrollPos -= m_nMaxColsPerPage;
1878        if(m_nHScrollPos > nOldHScrollPos) 
1879            m_nHScrollPos = 0;
1880        break;
1881
1882    case SB_PAGERIGHT:
1883        m_nHScrollPos += m_nMaxColsPerPage;
1884        break;
1885
1886    case SB_THUMBPOSITION:
1887        info.cbSize = sizeof(SCROLLINFO);
1888        info.fMask = SIF_TRACKPOS;
1889        GetScrollInfo(SB_HORZ, &info);
1890        m_nHScrollPos = info.nTrackPos;
1891        break;
1892
1893    case SB_THUMBTRACK:
1894        info.cbSize = sizeof(SCROLLINFO);
1895        info.fMask = SIF_TRACKPOS;
1896        GetScrollInfo(SB_HORZ, &info);
1897        m_nHScrollPos = info.nTrackPos;
1898        break;
1899
1900    default:
1901        nHScrollInc = 0;
1902    }
1903
1904    //keep scroll position in range
1905    if(m_nHScrollPos  > m_nHScrollMax - m_nMaxColsPerPage + 1)
1906        m_nHScrollPos = m_nHScrollMax - m_nMaxColsPerPage + 1;
1907
1908    nHScrollInc = m_nHScrollPos - nOldHScrollPos;
1909
1910    if (nHScrollInc)
1911    {	
1912
1913        //finally setup the actual scrollbar!
1914        info.cbSize = sizeof(SCROLLINFO);
1915        info.fMask = SIF_POS;
1916        info.nPos = m_nHScrollPos;
1917        SetScrollInfo(SB_HORZ, &info, TRUE);
1918
1919        InvalidateRect(NULL);
1920    }
1921
1922    return 0;
1923}
1924
1925LRESULT CFilePreviewCtrl::OnVScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1926{
1927    // React to the various vertical scroll related actions.
1928    // CAUTION:
1929    // All sizes are in unsigned values, so be carefull
1930    // when testing for < 0 etc
1931
1932    SCROLLINFO info;
1933    int nVScrollInc;
1934    int  nOldVScrollPos = m_nVScrollPos;
1935
1936    switch (LOWORD(wParam))
1937    {
1938    case SB_TOP:
1939        m_nVScrollPos = 0;
1940        break;
1941
1942    case SB_BOTTOM:
1943        m_nVScrollPos = m_nVScrollMax - m_nMaxLinesPerPage + 1;
1944        break;
1945
1946    case SB_LINEUP:
1947        if(m_nVScrollPos > 0) --m_nVScrollPos;
1948        break;
1949
1950    case SB_LINEDOWN:
1951        m_nVScrollPos++;
1952        break;
1953
1954    case SB_PAGEUP:
1955        m_nVScrollPos -= max(1, m_nMaxLinesPerPage);
1956        if(m_nVScrollPos > nOldVScrollPos) m_nVScrollPos = 0;
1957        break;
1958
1959    case SB_PAGEDOWN:
1960        m_nVScrollPos += max(1, m_nMaxLinesPerPage);
1961        break;
1962
1963    case SB_THUMBPOSITION:
1964        info.cbSize = sizeof(SCROLLINFO);
1965        info.fMask = SIF_TRACKPOS;
1966        GetScrollInfo(SB_VERT, &info);
1967        m_nVScrollPos = info.nTrackPos;
1968        break;
1969
1970    case SB_THUMBTRACK:
1971        info.cbSize = sizeof(SCROLLINFO);
1972        info.fMask = SIF_TRACKPOS;
1973        GetScrollInfo(SB_VERT, &info);
1974        m_nVScrollPos = info.nTrackPos;
1975        break;
1976
1977    default:
1978        nVScrollInc = 0;
1979    }
1980
1981    //keep scroll position in range
1982    if(m_nVScrollPos > m_nVScrollMax - m_nMaxLinesPerPage+1)
1983        m_nVScrollPos = m_nVScrollMax - m_nMaxLinesPerPage+1;
1984
1985    if(m_nVScrollPos<0)
1986        m_nVScrollPos = 0;
1987
1988    nVScrollInc = m_nVScrollPos - nOldVScrollPos;
1989
1990    if (nVScrollInc)
1991    {	
1992
1993        //finally setup the actual scrollbar!
1994        info.cbSize = sizeof(SCROLLINFO);
1995        info.fMask = SIF_POS;
1996        info.nPos = m_nVScrollPos;
1997        SetScrollInfo(SB_VERT, &info, TRUE);
1998
1999        InvalidateRect(NULL);
2000    }
2001
2002    return 0;
2003}
2004
2005LRESULT CFilePreviewCtrl::OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2006{
2007    // Do nothing
2008    return 0;   
2009}
2010
2011LRESULT CFilePreviewCtrl::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2012{ 
2013    PAINTSTRUCT ps;
2014    HDC hDC = BeginPaint(&ps);
2015
2016    {
2017        CMemoryDC memDC(hDC, ps.rcPaint);
2018
2019        if(m_fm.GetSize()==0)
2020            DoPaintEmpty(memDC);
2021        else
2022            DoPaint(memDC);
2023    }
2024
2025    EndPaint(&ps);
2026
2027    return 0;
2028}
2029
2030LRESULT CFilePreviewCtrl::OnTimer(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2031{
2032    SetupScrollbars();
2033    InvalidateRect(NULL);
2034    return 0;
2035}
2036
2037LRESULT CFilePreviewCtrl::OnComplete(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2038{
2039    KillTimer(0);
2040    SetupScrollbars();
2041    InvalidateRect(NULL);
2042    return 0;
2043}
2044
2045LRESULT CFilePreviewCtrl::OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2046{
2047    SetFocus();
2048    return 0;
2049}
2050
2051LRESULT CFilePreviewCtrl::OnRButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2052{
2053    NMHDR nmhdr;
2054    nmhdr.hwndFrom = m_hWnd;
2055    nmhdr.code = NM_RCLICK;
2056    nmhdr.idFrom = GetWindowLong(GWL_ID);
2057
2058    HWND hWndParent = GetParent();
2059    ::SendMessage(hWndParent, WM_NOTIFY, 0, (LPARAM)&nmhdr);
2060    return 0;
2061}
2062
2063LRESULT CFilePreviewCtrl::OnMouseWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2064{
2065    if(m_PreviewMode!=PREVIEW_TEXT && 
2066        m_PreviewMode!=PREVIEW_HEX)
2067        return 0;
2068
2069    int nDistance =  GET_WHEEL_DELTA_WPARAM(wParam)/WHEEL_DELTA;
2070    int nLinesPerDelta = m_nMaxLinesPerPage!=0?m_nVScrollMax/m_nMaxLinesPerPage:0;
2071
2072    SCROLLINFO info;
2073    memset(&info, 0, sizeof(SCROLLINFO));
2074    info.cbSize = sizeof(SCROLLINFO);
2075    info.fMask = SIF_ALL;
2076    GetScrollInfo(SB_VERT, &info);	
2077    info.nPos -=nDistance*nLinesPerDelta;
2078    SetScrollInfo(SB_VERT, &info, TRUE);
2079
2080    SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, info.nPos), 0);
2081    return 0;
2082}