/*************************************************************************
 *
 *  $RCSfile: PreviewCtrl.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: hr $ $Date: 2003-03-25 18:04:57 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

//------------------------------------------------------------------------
// includes
//------------------------------------------------------------------------

#include <tchar.h>

#ifndef _PREVIEWCTRL_HXX_
#include "PreviewCtrl.hxx"
#endif

#ifndef _OSL_DIAGNOSE_H_
#include <osl/diagnose.h>
#endif

#include <windows.h>
#include <ocidl.h>
#include <olectl.h>

//------------------------------------------------------------------------
// defines
//------------------------------------------------------------------------

#define PREVIEWWND_CLASS_NAME TEXT("PreviewWnd###")

#define HIMETRIC_INCH 2540

// means 3 pixel left and 3 pixel right
#define HORZ_BODER_SPACE    6

// means 3 pixel top and 3 pixel bottom
#define VERT_BORDER_SPACE   6

//---------------------------------------------------
// static member initialization
//---------------------------------------------------

CFilePreview* CFilePreview::s_FilePreviewInst = NULL;
CFilePreview::FILEPREVIEW_SINGLETON_DESTROYER_T CFilePreview::s_SingletonDestroyer;

//---------------------------------------------------
// some useful helper functions
//---------------------------------------------------

namespace // private
{
    class CPreviewException
    {
        // used when registering or creation
        // of the preview window failed
    };

    //------------------------------------------------------------
    //
    //------------------------------------------------------------

    inline
    sal_Int32 SubDiv( sal_Int32 nNumber, sal_Int32 nMinuend, sal_Int32 nDenominator )
    {
        return ( static_cast<sal_Int32>( ( nNumber - nMinuend ) / nDenominator ) );
    }

    //------------------------------------------------------------
    // convert himetric to pixel
    //------------------------------------------------------------

    inline
    sal_Int32 Himetric2Pixel( HDC hDC, sal_Int32 hmSize, sal_Int32 nIndex )
    {
        return MulDiv( hmSize, GetDeviceCaps( hDC, nIndex), HIMETRIC_INCH );
    }

    //------------------------------------------------------------
    //
    //------------------------------------------------------------

    inline
    sal_uInt32 _getWidthRect( const RECT& aRect )
    {
        return ( aRect.right - aRect.left );
    }

    //------------------------------------------------------------
    //
    //------------------------------------------------------------

    inline
    sal_uInt32 _getHeightRect( const RECT& aRect )
    {
        return ( aRect.bottom - aRect.top );
    }

    //------------------------------------------------------------
    // calc the upper left corner so that a given window will be
    // displayed centered within the given window
    //------------------------------------------------------------

    inline
    POINT _calcULCorner( HWND hwnd, const CDimension& aPicSize )
    {
        RECT rect;
        GetClientRect( hwnd, &rect );

        sal_Int32 nWidthWnd  = _getWidthRect( rect );
        sal_Int32 nHeightWnd = _getHeightRect( rect );

        POINT ulCorner;
        ulCorner.x = SubDiv( nWidthWnd,  aPicSize.m_cx, 2 );
        ulCorner.y = SubDiv( nHeightWnd, aPicSize.m_cy, 2 );

        return ulCorner;
    }

    //------------------------------------------------------------
    // test if a picture with the given dimensions fits into an
    // arbitrary window
    // we expect the width and height to be in pixel
    //------------------------------------------------------------

    inline
    sal_Bool _pictureSizeFitsWindowSize( HWND hwnd, const CDimension& aPicSize )
    {
        RECT rect;
        GetClientRect( hwnd, &rect );

        sal_Int32 nWidthWnd  = _getWidthRect( rect );
        sal_Int32 nHeightWnd = _getHeightRect( rect );

        return ( ( ( nWidthWnd  - HORZ_BODER_SPACE )  >= aPicSize.m_cx ) &&
                 ( ( nHeightWnd - VERT_BORDER_SPACE ) >= aPicSize.m_cy ) );
    }

    //------------------------------------------------------------
    // calc the dimemsions so that a given picture fits into a
    // given window, if the picture fits into the given window
    // the original CDimension will be returned
    //------------------------------------------------------------

    inline
    CDimension _scalePictureSize( HWND hwnd, const CDimension& aPicSize )
    {
        CDimension scaledPicSize = aPicSize;

        if ( !_pictureSizeFitsWindowSize( hwnd, aPicSize ) )
        {
            RECT rect;
            GetClientRect( hwnd, &rect );

            // the dimensions of the preview wnd are not equal
            // that's why we equalize it
            sal_Int32 nHeightWnd = _getHeightRect( rect ) - VERT_BORDER_SPACE;
            sal_Int32 nWidthWnd  = nHeightWnd;

            if ( aPicSize.m_cx >= aPicSize.m_cy )
            {
                scaledPicSize.m_cx = nWidthWnd;
                scaledPicSize.m_cy =
                    static_cast< sal_Int32 >(
                        aPicSize.m_cy * nWidthWnd / aPicSize.m_cx );
            }
            else
            {
                scaledPicSize.m_cx =
                    static_cast< sal_Int32 >(
                        aPicSize.m_cx * nHeightWnd / aPicSize.m_cy );
                scaledPicSize.m_cy = nHeightWnd;
            }
        }

        return scaledPicSize;
    }

} // end namespace


//---------------------------------------------------
// to ensure only one instance (singleton)
//---------------------------------------------------

CFilePreview* CFilePreview::createInstance(
    HWND aParent,
    POINT ulCorner,
    const CDimension& aSize,
    HINSTANCE hInstance,
    sal_Bool bShow,
    sal_Bool bEnabled )
{
    if ( !s_FilePreviewInst )
    {
        try
        {
            s_FilePreviewInst = new CFilePreview(
                aParent, ulCorner, aSize, hInstance, bShow, bEnabled );
            s_SingletonDestroyer.reset( s_FilePreviewInst );
        }
        catch( CPreviewException& )
        {
            OSL_ASSERT( !s_FilePreviewInst );
            OSL_ENSURE( sal_False, "Creation of the preview window failed" );
        }
        catch( CAutoOleInit::COleInitException& )
        {
            OSL_ASSERT( !s_FilePreviewInst );
            OSL_ENSURE( sal_False, "OleInitalize failed" );
        }
    }

    return s_FilePreviewInst;
}

//---------------------------------------------------
//
//---------------------------------------------------

CFilePreview::CFilePreview(
    HWND aParent,
    POINT ulCorner,
    const CDimension& aSize,
    HINSTANCE hInstance,
    sal_Bool bShow,
    sal_Bool bEnabled ) :
    m_hInstance( hInstance ),
    m_bEnabled( bEnabled )
{
    // register the preview window class
    WNDCLASSEX wndClsEx;
    ZeroMemory(&wndClsEx, sizeof(wndClsEx));

    wndClsEx.cbSize        = sizeof(wndClsEx);
    wndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
    wndClsEx.lpfnWndProc   = CFilePreview::WndProc;
    wndClsEx.hInstance     = m_hInstance;
    wndClsEx.hbrBackground = (HBRUSH)( COLOR_INACTIVEBORDER + 1 );
    wndClsEx.lpszClassName = PREVIEWWND_CLASS_NAME;

    // register the preview window class
    // !!! Win95 -   the window class will be unregistered automaticly
    //               if the dll is unloaded
    //     Win2000 - the window class must be unregistered manually
    //               if the dll is unloaded
    m_atomPrevWndClass = RegisterClassEx(&wndClsEx);
    if ( !m_atomPrevWndClass )
        throw CPreviewException( );

    // create the preview window in invisible state
    sal_uInt32 dwStyle = bShow ? (WS_CHILD | WS_VISIBLE) : WS_CHILD;
    m_hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        PREVIEWWND_CLASS_NAME,
        TEXT(""),
        dwStyle,
        ulCorner.x,
        ulCorner.y,
        aSize.m_cx,
        aSize.m_cy,
        aParent,
        (HMENU)100, // for child windows this will
                    // be used as child window identifier
        m_hInstance,
        0 );
    if (!IsWindow(m_hwnd))
        throw CPreviewException( );
}

//---------------------------------------------------
//
//---------------------------------------------------

CFilePreview::~CFilePreview( )
{
    // unregister preview window class
    sal_Bool bRet = UnregisterClass(
        (LPCTSTR)MAKELONG( m_atomPrevWndClass, 0 ),
        m_hInstance );
    OSL_POSTCOND( bRet, "Unregister preview window class failed" );
}

//---------------------------------------------------
// sets the size of the preview window
//---------------------------------------------------

sal_Bool SAL_CALL CFilePreview::setSize( const CDimension& aSize )
{
    OSL_PRECOND( IsWindow( m_hwnd ), "Preview window not initialized" );

    // resize the fileopen file listbox
    return SetWindowPos(
        m_hwnd,
        NULL,
        0,
        0,
        aSize.m_cx,
        aSize.m_cy,
        SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
}

//---------------------------------------------------
// returns the dimension of the preview
//---------------------------------------------------

sal_Bool SAL_CALL CFilePreview::getSize( CDimension& theSize ) const
{
    OSL_PRECOND( IsWindow( m_hwnd ), "Preview window not initialized" );

    RECT rect;
    sal_Bool bRet = GetWindowRect( m_hwnd, &rect );

    theSize.m_cx = _getWidthRect( rect );
    theSize.m_cy = _getHeightRect( rect );

    return bRet;
}

//---------------------------------------------------
// sets the position of the upper left corner
// of the preview window relative to the
// upper left corner of the parent window
//---------------------------------------------------

sal_Bool SAL_CALL CFilePreview::setPos( POINT ulCorner )
{
    OSL_PRECOND( IsWindow( m_hwnd ), "Preview window not initialized" );

    // resize the fileopen file listbox
    return SetWindowPos(
        m_hwnd,
        NULL,
        ulCorner.x,
        ulCorner.y,
        0,
        0,
        SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
}

//---------------------------------------------------
// returns the current position of the preview
// relative to the upper left corner of the
// parent window
//---------------------------------------------------

sal_Bool SAL_CALL CFilePreview::getPos( POINT& ulCorner ) const
{
    OSL_PRECOND( IsWindow( m_hwnd ), "Preview window not initialized" );

    POINT pt = { 0, 0 };
    RECT rect;

    sal_Bool bRet = GetWindowRect( m_hwnd, &rect );

    ulCorner.x = rect.left;
    ulCorner.y = rect.top;

    ScreenToClient( m_hwnd, &ulCorner );

    return bRet;
}

//---------------------------------------------------
//
//---------------------------------------------------

void SAL_CALL CFilePreview::enable( sal_Bool bEnable )
{
    m_bEnabled = bEnable;

    // force a redraw
    InvalidateRect( m_hwnd, NULL, TRUE );
    UpdateWindow( m_hwnd );
}

//---------------------------------------------------
// shows the preview window
// possible values see SHOW_STATE
// SS_SHOW     - make the window visible
// SS_HIDE     - hide the window
// SS_ENABLED  - enable the window
// SS_DISABLED - disable the window
//---------------------------------------------------

sal_Bool SAL_CALL CFilePreview::show( sal_Bool bShow )
{
    OSL_PRECOND( IsWindow( m_hwnd ), "Preview window not initialized" );

    sal_Int32 showState = bShow ? SW_SHOW : SW_HIDE;
    return ShowWindow( m_hwnd, showState );
}

//---------------------------------------------------
// if the preview is shown and enabled
// preview of the given file will be shown
// returns true on success or false if an error
// occured (the file in not there or not accessible etc.)
//---------------------------------------------------

sal_Bool SAL_CALL CFilePreview::update( const rtl::OUString& aFileName )
{
    OSL_PRECOND( IsWindow( m_hwnd ), "Preview window not initialized" );

    try
    {
        if ( m_bEnabled )
        {
            if ( m_IPicture )
                m_IPicture.Release( );

            loadFile( aFileName );

            // force a complete window redraw
            InvalidateRect( m_hwnd, NULL, TRUE );
            UpdateWindow( m_hwnd );
        }
    }
    catch( _com_error& )
    {
    }

    return sal_True;
}

//---------------------------------------------------
//
//---------------------------------------------------

void SAL_CALL CFilePreview::onPaint( HWND hWnd, HDC hDC )
{
    OSL_PRECOND( IsWindow( m_hwnd ), "Preview window not initialized" );

    try
    {
        if ( m_bEnabled )
        {
            // get width and height of picture
            long cxPicHIMETRIC;
            long cyPicHIMETRIC;

            m_IPicture->get_Width( &cxPicHIMETRIC );
            m_IPicture->get_Height( &cyPicHIMETRIC );

            // convert himetric to pixels
            int cxPicPIXEL = Himetric2Pixel( hDC, cxPicHIMETRIC, LOGPIXELSX );
            int cyPicPIXEL = Himetric2Pixel( hDC, cyPicHIMETRIC, LOGPIXELSY );

            // scale the picture based on the size of the preview window
            RECT rcPrevWnd;
            GetClientRect(hWnd, &rcPrevWnd);

            CDimension scaledPicSize = _scalePictureSize(
                hWnd, CDimension( cxPicPIXEL, cyPicPIXEL ) );

            // calc the upper left corner so that the picture
            // is centered within the window
            POINT ulCorner = _calcULCorner( hWnd, scaledPicSize );

            // render the picture
            HRESULT hr = m_IPicture->Render(
                hDC,
                ulCorner.x,
                ulCorner.y,
                scaledPicSize.m_cx,
                scaledPicSize.m_cy,
                0,
                cyPicHIMETRIC,
                cxPicHIMETRIC,
                -cyPicHIMETRIC,
                &rcPrevWnd );
        } // end if ( m_bEnabled )
    }
    catch( _com_error& )
    {
    }
}

//---------------------------------------------------
//
//---------------------------------------------------

sal_Bool CFilePreview::loadFile( const rtl::OUString& aFileName )
{
    HANDLE      hFile   = 0;
    HGLOBAL     hGlobal = 0;
    LPVOID      pData   = NULL;
    IStreamPtr  pIStream;
    HRESULT     hr = E_FAIL;
    sal_Bool    bRet;
    sal_uInt32  nBytesRead;
    sal_uInt32  fszExtra;
    sal_uInt32  fsize;

    hFile = CreateFile(
        aFileName.getStr( ),
        GENERIC_READ,
        0,
        NULL,
        OPEN_EXISTING,
        0,
        NULL );
    if ( INVALID_HANDLE_VALUE == hFile )
        goto CLEANUP_AND_EXIT;

    fszExtra = 0;
    fsize = GetFileSize( hFile, &fszExtra );

    // empty file, error or file to big
    if ( -1 == fsize || 0 == fsize || fszExtra )
        goto CLEANUP_AND_EXIT;

    hGlobal = GlobalAlloc( GMEM_MOVEABLE, fsize );
    if ( !hGlobal )
        goto CLEANUP_AND_EXIT;

    pData = GlobalLock( hGlobal );
    if ( !pData )
        goto CLEANUP_AND_EXIT;

    bRet = ReadFile(
        hFile, pData, fsize, &nBytesRead, NULL );

    if ( !bRet )
        goto CLEANUP_AND_EXIT;

    hr = CreateStreamOnHGlobal(
        hGlobal, FALSE, &pIStream );

    if ( SUCCEEDED( hr ) )
    {
        hr = OleLoadPicture(
            pIStream, fsize, FALSE,
            __uuidof( IPicture ), (LPVOID*)&m_IPicture );
    }

CLEANUP_AND_EXIT:
    if ( hFile )
        CloseHandle( hFile );

    if ( pData )
        GlobalUnlock( hGlobal );

    if ( hGlobal )
        GlobalFree( hGlobal );

    return ( SUCCEEDED( hr ) );
}

//---------------------------------------------------
//
//---------------------------------------------------

LRESULT CALLBACK CFilePreview::WndProc(
    HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    LRESULT lResult = 0;

    switch( uMsg )
    {
    case WM_PAINT:
    {
        OSL_PRECOND( s_FilePreviewInst, "Static member not initialized" );

        HDC         hDC;
        PAINTSTRUCT ps;

        hDC = BeginPaint( hWnd, &ps );
        s_FilePreviewInst->onPaint( hWnd, hDC );
        EndPaint( hWnd, &ps );
    }
    break;

    // under windows 95/98 the creation of the
        // hidden target request window fails if
        // we don't handle this message ourself
        // because the DefWindowProc returns 0 as
        // a result of handling WM_NCCREATE what
        // leads to a failure of CreateWindow[Ex]!!!
    case WM_NCCREATE:
        lResult = TRUE;
        break;

    default:
        return DefWindowProc( hWnd, uMsg, wParam, lParam );
    }

    return lResult;
}



