/*************************************************************************
 *
 *  $RCSfile: previewadapter.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: hr $ $Date: 2003-03-25 18:05:01 $
 *
 *  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): _______________________________________
 *
 *
 ************************************************************************/

#include <tchar.h>

#ifndef _PREVIEWADAPTER_HXX_
#include "previewadapter.hxx"
#endif

#ifndef _COM_SUN_STAR_UI_DIALOG_FILEPREVIEWIMAGEFORMATS_HPP_
#include <com/sun/star/ui/dialogs/FilePreviewImageFormats.hpp>
#endif

#ifndef _DIBPREVIEW_HXX_
#include "dibpreview.hxx"
#endif

#ifndef _WINIMPLHELPER_HXX_
#include "../misc/WinImplHelper.hxx"
#endif

#include <memory>
#include <stdexcept>

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

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;

//---------------------------------------------
// An impl class to hide implementation details
// from clients
//---------------------------------------------

class CPreviewAdapterImpl
{
public:
    CPreviewAdapterImpl(HINSTANCE instance);

    virtual ~CPreviewAdapterImpl();

    virtual sal_Int32 SAL_CALL getTargetColorDepth();

    virtual sal_Int32 SAL_CALL getAvailableWidth();

    virtual sal_Int32 SAL_CALL getAvailableHeight();

    virtual void SAL_CALL setImage( sal_Int16 aImageFormat, const Any& aImage )
        throw (IllegalArgumentException,RuntimeException);

    virtual sal_Bool SAL_CALL setShowState(sal_Bool bShowState);

    virtual sal_Bool SAL_CALL getShowState();

    virtual void SAL_CALL setParent(HWND parent);

    virtual HWND SAL_CALL getParent();

    //-------------------------------------
    // parent notification handler
    //-------------------------------------

    virtual void SAL_CALL notifyParentShow(sal_Bool bShow);

    virtual void SAL_CALL notifyParentSizeChanged();

    virtual void SAL_CALL notifyParentWindowPosChanged(sal_Bool bIsVisible);

protected:
    virtual void SAL_CALL calcRightMargin();

    virtual void SAL_CALL rearrangeLayout();

    void SAL_CALL initializeActivePreview() throw(std::runtime_error);

    HWND SAL_CALL findFileListbox() const;

// member
protected:
    HINSTANCE                   m_Instance;
    std::auto_ptr<PreviewBase>  m_Preview;
    HWND                        m_FileDialog;
    int                         m_RightMargin;

//prevent copy/assignment
private:
    CPreviewAdapterImpl(const CPreviewAdapterImpl&);
    CPreviewAdapterImpl& operator=(const CPreviewAdapterImpl&);
};

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

CPreviewAdapterImpl::CPreviewAdapterImpl(HINSTANCE instance) :
    m_Instance(instance),
    m_Preview(new PreviewBase()), // create dummy preview (NULL-Object pattern)
    m_FileDialog(0),
    m_RightMargin(0)
{
}

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

CPreviewAdapterImpl::~CPreviewAdapterImpl()
{
}

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

sal_Int32 SAL_CALL CPreviewAdapterImpl::getTargetColorDepth()
{
    return m_Preview->getTargetColorDepth();
}

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

sal_Int32 SAL_CALL CPreviewAdapterImpl::getAvailableWidth()
{
    return m_Preview->getAvailableWidth();
}

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

sal_Int32 SAL_CALL CPreviewAdapterImpl::getAvailableHeight()
{
    return m_Preview->getAvailableHeight();
}

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

void SAL_CALL CPreviewAdapterImpl::setImage( sal_Int16 aImageFormat, const Any& aImage )
    throw (IllegalArgumentException,RuntimeException)
{
    m_Preview->setImage(aImageFormat,aImage);
}

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

sal_Bool SAL_CALL CPreviewAdapterImpl::setShowState( sal_Bool bShowState )
{
    sal_Bool bRet = m_Preview->setShowState(bShowState);
    rearrangeLayout();
    return bRet;
}

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

sal_Bool SAL_CALL CPreviewAdapterImpl::getShowState()
{
    return m_Preview->getShowState();
}

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

void SAL_CALL CPreviewAdapterImpl::setParent(HWND parent)
{
    OSL_PRECOND(IsWindow(parent),"Invalid FileDialog handle");

    m_FileDialog = parent;
    calcRightMargin();
}

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

HWND SAL_CALL CPreviewAdapterImpl::getParent()
{
    return m_FileDialog;
}

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

void SAL_CALL CPreviewAdapterImpl::calcRightMargin()
{
    // Calculate the right reference margin
    //
    // Assumtions:
    // 1. This method will be called before the dialog becomes
    //    visible
    // 2. There exist a FileListbox with the id lst1 even
    //    if it is not visible like under Win2000/XP
    // 3. Initially this FileListbox has the appropriate size
    //    to fit within the FileListbox
    // 4. The margin between the right edge of the FileListbox
    //    and the right edge of the FileDialog will be constant
    //    even if the size of the dialog changes

    HWND flb = GetDlgItem(m_FileDialog,lst1);

    OSL_ENSURE(IsWindow(flb),"Filelistbox not found");

    RECT rcFlb;
    GetWindowRect(flb,&rcFlb);

    RECT rcFileDlg;
    GetWindowRect(m_FileDialog,&rcFileDlg);

    m_RightMargin = rcFileDlg.right - rcFlb.right;
}

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

void SAL_CALL CPreviewAdapterImpl::notifyParentShow(sal_Bool bShow)
{
}

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

void SAL_CALL CPreviewAdapterImpl::notifyParentSizeChanged()
{
    rearrangeLayout();
}

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

void SAL_CALL CPreviewAdapterImpl::notifyParentWindowPosChanged(sal_Bool bIsVisible)
{
}

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

void SAL_CALL CPreviewAdapterImpl::rearrangeLayout()
{
    // try to get a handle to the filelistbox
    // if there is no new-style filelistbox like
    // in Win2000/XP there should be at least the
    // old listbox, so we take this one
    // lst1 - identifies the old-style filelistbox
    // lst2 - identifies the new-style filelistbox
    // see dlgs.h
    HWND flb_new = findFileListbox();

    // under Windows NT 4.0 the size of the old
    // filelistbox will be used as reference for
    // sizing the new filelistbox, so we have
    // to change the size of it too
    HWND flb_old = GetDlgItem(m_FileDialog,lst1);

    RECT rcFlbNew;
    GetWindowRect(flb_new,&rcFlbNew);

    RECT rcFileDlg;
    GetWindowRect(m_FileDialog,&rcFileDlg);
    rcFileDlg.right -= m_RightMargin;

    // the available area for the filelistbox should be
    // the left edge of the filelistbox and the right
    // edge of the OK button, we take this as reference
    int height = rcFlbNew.bottom - rcFlbNew.top;
    int width  = rcFileDlg.right - rcFlbNew.left;

    HWND prvwnd = m_Preview->getWindowHandle();

    // we use GetWindowLong to ask for the visibility
    // of the preview window because IsWindowVisible
    // only returns true the specified window including
    // its parent windows are visible
    // this is not the case when we are called in response
    // to the WM_SHOWWINDOW message, somehow the WS_VISIBLE
    // style bit of the FileOpen dialog must be set after that
    // message
    LONG lStyle = GetWindowLong(prvwnd,GWL_STYLE);
    BOOL bIsVisible = (BOOL)(lStyle & WS_VISIBLE);

    int cx = 0;

    if (IsWindow(prvwnd) && bIsVisible)
    {
        cx = width/2;

        // resize the filelistbox to the half of the
        // available space
        BOOL bRet = SetWindowPos(flb_new,
            NULL, 0, 0, cx, height,
            SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

        bRet = SetWindowPos(flb_old,
            NULL, 0, 0, cx, height,
            SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

        // get the new dimensions of the filelistbox after
        // resizing and take the right,top corner as starting
        // point for the preview window
        GetWindowRect(flb_new,&rcFlbNew);
        POINT pt = { rcFlbNew.right, rcFlbNew.top };
        ScreenToClient(m_FileDialog,&pt);

        // resize the preview window to fit within
        // the available space and set the window
        // to the top of the z-order else it will
        // be invisible
        SetWindowPos(prvwnd,
            HWND_TOP, pt.x, pt.y, cx, height, SWP_NOACTIVATE);
    }
    else
    {
        // resize the filelistbox to the maximum available
        // space
        cx = rcFileDlg.right - rcFlbNew.left;

        // resize the old filelistbox
        SetWindowPos(flb_old,
            NULL, 0, 0, cx, height,
            SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

        // resize the new filelistbox
        SetWindowPos(flb_new,
            NULL, 0, 0, cx, height,
            SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
    }
}

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

void SAL_CALL CPreviewAdapterImpl::initializeActivePreview() throw(std::runtime_error)
{
    sal_Bool bShowState = m_Preview->getImaginaryShowState();

    sal_Int16 aImgFrmt;
    Any aImg;
    m_Preview->getImage(aImgFrmt,aImg);

    HWND flb = findFileListbox();

    PreviewBase* prv = new CDIBPreview(
        m_Instance, GetParent(flb), bShowState);

    m_Preview.reset(prv);

    m_Preview->setImage(aImgFrmt,aImg);
}

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

HWND SAL_CALL CPreviewAdapterImpl::findFileListbox() const
{
    // try to get a handle to the filelistbox
    // if there is no new-style filelistbox like
    // in Win2000/XP there should be at least the
    // old listbox, so we take this one
    // lst1 - identifies the old-style filelistbox
    // lst2 - identifies the new-style filelistbox
    // see dlgs.h
    HWND flb = GetDlgItem(m_FileDialog,lst2);
    if (!IsWindow(flb))
        flb = GetDlgItem(m_FileDialog,lst1);

    return flb;
}


//##############################################################


//-----------------------------------------
// Special implementation for Win98
// because:
//
//-----------------------------------------

class CWin98PreviewAdapterImpl : public CPreviewAdapterImpl
{
public:
    CWin98PreviewAdapterImpl(HINSTANCE instance);

    virtual void SAL_CALL notifyParentWindowPosChanged(sal_Bool bIsVisible);

protected:
    virtual void SAL_CALL rearrangeLayout();

    bool isValidToolbarDimension() const;

private:
    sal_Bool    m_PreviewActive;
    int         m_ToolbarPosX;
    int         m_ToolbarPosY;
    int         m_ToolbarWidth;
    int         m_ToolbarHeight;
};

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

CWin98PreviewAdapterImpl::CWin98PreviewAdapterImpl(HINSTANCE instance) :
    CPreviewAdapterImpl(instance),
    m_PreviewActive(sal_False),
    m_ToolbarPosX(0),
    m_ToolbarPosY(0),
    m_ToolbarWidth(0),
    m_ToolbarHeight(0)
{
}

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

void SAL_CALL CWin98PreviewAdapterImpl::notifyParentWindowPosChanged(sal_Bool bIsVisible)
{
    try
    {
        // the reason for this condition is
        // Windows 98
        // Under Windows 98 the message WM_SHOWWINDOW
        // will be sent only the first time the
        // GetOpenFileName function is called within
        // the same process
        // so we must use another message to initialize
        // the preview window
        if (IsWindow(m_FileDialog) && !m_PreviewActive)
        {
            initializeActivePreview();
            m_PreviewActive = sal_True;
            rearrangeLayout();
        }

        if (IsWindow(m_FileDialog) && !isValidToolbarDimension())
        {
            RECT rcStc1;
            GetWindowRect(GetDlgItem(m_FileDialog,stc1),&rcStc1);

            RECT rcCmb2;
            GetWindowRect(GetDlgItem(m_FileDialog,cmb2),&rcCmb2);

            // Assumption:
            // the toolbar position is only valid
            // if the left edge is greater or equal
            // than the right edge of the drives listbox
            // the stc1 static text is invisible at runtime
            // but will be used as reference for the position
            // and dimension of the toolbar
            if (rcStc1.left >= rcCmb2.right)
            {
                // important: save the upper left corner in
                // client coordinates
                POINT pt = {rcStc1.left,rcStc1.top};
                ScreenToClient(m_FileDialog,&pt);

                m_ToolbarPosX   = pt.x;
                m_ToolbarPosY   = pt.y;
                m_ToolbarWidth  = rcStc1.right - rcStc1.left;
                m_ToolbarHeight = rcStc1.bottom - rcStc1.top;
            }
        }
    }
    catch(std::runtime_error&)
    {
    }
}

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

void SAL_CALL CWin98PreviewAdapterImpl::rearrangeLayout()
{
    CPreviewAdapterImpl::rearrangeLayout();

    // fix the position of the upper toolbar
    // because the FileDialog moves all windows
    // that are to the right of the FileListbox
    // so if we have changed the size of the
    // FileListbox we would run into trouble else
    if (isValidToolbarDimension())
    {
        HWND hwndTlb = FindWindowEx(
            m_FileDialog,NULL,TEXT("ToolbarWindow32"),NULL);

        SetWindowPos(hwndTlb,
            HWND_TOP,
            m_ToolbarPosX,
            m_ToolbarPosY,
            m_ToolbarWidth,
            m_ToolbarHeight,
            SWP_NOACTIVATE);
    }
}

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

bool CWin98PreviewAdapterImpl::isValidToolbarDimension() const
{
    return (m_ToolbarPosX   > 0 &&
            m_ToolbarPosY   > 0 &&
            m_ToolbarWidth  > 0 &&
            m_ToolbarHeight > 0);
}

//##############################################################


//--------------------------------------------
// Implementation for Windows 95/NT/ME/2000/XP
// because:
//
//--------------------------------------------

class CWin95NTPreviewAdapterImpl : public CPreviewAdapterImpl
{
public:
    CWin95NTPreviewAdapterImpl(HINSTANCE instance);

    virtual void SAL_CALL notifyParentShow(sal_Bool bShow);
};

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

CWin95NTPreviewAdapterImpl::CWin95NTPreviewAdapterImpl(HINSTANCE instance) :
    CPreviewAdapterImpl(instance)
{
}

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

void SAL_CALL CWin95NTPreviewAdapterImpl::notifyParentShow(sal_Bool bShow)
{
    try
    {
        if (bShow)
        {
            initializeActivePreview();
            rearrangeLayout();
        }
    }
    catch(std::runtime_error&)
    {
    }
}


//##############################################################


//-------------------------------
// ctor
//-------------------------------

CPreviewAdapter::CPreviewAdapter(HINSTANCE instance)
{
    if (!IsWindows98())
        m_pImpl.reset(new CWin95NTPreviewAdapterImpl(instance));
    else
        m_pImpl.reset(new CWin98PreviewAdapterImpl(instance));
}

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

CPreviewAdapter::~CPreviewAdapter()
{
}

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

Sequence<sal_Int16> SAL_CALL CPreviewAdapter::getSupportedImageFormats()
{
    com::sun::star::uno::Sequence<sal_Int16> imgFormats(1);
    imgFormats[0] = ::com::sun::star::ui::dialogs::FilePreviewImageFormats::BITMAP;
    return imgFormats;
}

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

sal_Int32 SAL_CALL CPreviewAdapter::getTargetColorDepth()
{
    return m_pImpl->getTargetColorDepth();
}

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

sal_Int32 SAL_CALL CPreviewAdapter::getAvailableWidth()
{
    return m_pImpl->getAvailableWidth();
}

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

sal_Int32 SAL_CALL CPreviewAdapter::getAvailableHeight()
{
    return m_pImpl->getAvailableHeight();
}

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

void SAL_CALL CPreviewAdapter::setImage( sal_Int16 aImageFormat, const Any& aImage )
    throw (IllegalArgumentException, RuntimeException)
{
    m_pImpl->setImage(aImageFormat,aImage);
}

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

sal_Bool SAL_CALL CPreviewAdapter::setShowState( sal_Bool bShowState )
{
    return m_pImpl->setShowState(bShowState);
}

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

sal_Bool SAL_CALL CPreviewAdapter::getShowState()
{
    return m_pImpl->getShowState();
}

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

void SAL_CALL CPreviewAdapter::setParent(HWND parent)
{
    m_pImpl->setParent(parent);
}

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

void SAL_CALL CPreviewAdapter::notifyParentShow(sal_Bool bShow)
{
    m_pImpl->notifyParentShow(bShow);
}

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

void SAL_CALL CPreviewAdapter::notifyParentSizeChanged()
{
    m_pImpl->notifyParentSizeChanged();
}

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

void SAL_CALL CPreviewAdapter::notifyParentWindowPosChanged(sal_Bool bIsVisible)
{
    m_pImpl->notifyParentWindowPosChanged(bIsVisible);
}
