/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

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

#include "Os2Clipboard.hxx"

//------------------------------------------------------------------------
// namespace directives
//------------------------------------------------------------------------

using namespace com::sun::star::datatransfer;
using namespace com::sun::star::datatransfer::clipboard;
using namespace com::sun::star::datatransfer::clipboard::RenderingCapabilities;
using namespace com::sun::star::lang;
using namespace com::sun::star::uno;
using namespace cppu;
using namespace osl;
using namespace os2;

using ::rtl::OUString;

const Type CPPUTYPE_SEQINT8	 = getCppuType( ( Sequence< sal_Int8 >* )0 );
const Type CPPUTYPE_OUSTRING = getCppuType( (OUString*)0 );

#define DTRANS_OBJ_CLASSNAME "DTRANSOBJWND"

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

inline void SetWindowPtr( HWND hWnd, Os2Clipboard* pThis )
{
    WinSetWindowULong( hWnd, QWL_USER, (ULONG)pThis );
}

inline Os2Clipboard* GetWindowPtr( HWND hWnd )
{
    return (Os2Clipboard*)WinQueryWindowULong( hWnd, QWL_USER );
}

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

MRESULT EXPENTRY DtransObjWndProc( HWND hWnd, ULONG nMsg, MPARAM nMP1, MPARAM nMP2 )
{

    switch ( nMsg )
    {
    case WM_DRAWCLIPBOARD:	// clipboard content has changed
        {
            Os2Clipboard* os2Clipboard = GetWindowPtr( hWnd);
            if (os2Clipboard)
            {
                debug_printf("WM_DRAWCLIPBOARD os2Clipboard %08x\n", os2Clipboard);
                if (os2Clipboard->m_bInSetClipboardData)
                {
                    debug_printf("WM_DRAWCLIPBOARD our change\n");
                }
                else
                {
                    // notify listener for clipboard change
                    debug_printf("WM_DRAWCLIPBOARD notify change\n");
                    os2Clipboard->notifyAllClipboardListener();
                }
            }
        }
        break;
    }

    return WinDefWindowProc( hWnd, nMsg, nMP1, nMP2 );
}

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

Os2Clipboard::Os2Clipboard() :
    m_aMutex(),
    WeakComponentImplHelper4< XClipboardEx, XClipboardNotifier, XServiceInfo, XInitialization > (m_aMutex),
    m_bInitialized(sal_False),
    m_bInSetClipboardData(sal_False)
{
    MutexGuard aGuard(m_aMutex);

    debug_printf("Os2Clipboard::Os2Clipboard\n");
    hAB = WinQueryAnchorBlock( HWND_DESKTOP );
    hText = 0;
    hBitmap = 0;

}

Os2Clipboard::~Os2Clipboard()
{
    debug_printf("Os2Clipboard::~Os2Clipboard\n");
}

void SAL_CALL Os2Clipboard::initialize( const Sequence< Any >& aArguments ) 
    throw(Exception, RuntimeException)
{
    if (!m_bInitialized)
    {
        for (sal_Int32 n = 0, nmax = aArguments.getLength(); n < nmax; n++)
            if (aArguments[n].getValueType() == getCppuType((OUString *) 0))
            {
                aArguments[0] >>= m_aName;
                break;
            }
    }
}

OUString SAL_CALL Os2Clipboard::getImplementationName() throw( RuntimeException )
{
    debug_printf("Os2Clipboard::getImplementationName\n");
    return OUString(RTL_CONSTASCII_USTRINGPARAM( OS2_CLIPBOARD_IMPL_NAME ));
}

sal_Bool SAL_CALL Os2Clipboard::supportsService( const OUString& ServiceName ) throw( RuntimeException )
{
    debug_printf("Os2Clipboard::supportsService\n");
    Sequence < OUString > SupportedServicesNames = Os2Clipboard_getSupportedServiceNames();

    for ( sal_Int32 n = SupportedServicesNames.getLength(); n--; )
        if (SupportedServicesNames[n].compareTo(ServiceName) == 0)
            return sal_True;

    return sal_False;
}

Sequence< OUString > SAL_CALL Os2Clipboard::getSupportedServiceNames() throw( RuntimeException )
{
    debug_printf("Os2Clipboard::getSupportedServiceNames\n");
    return Os2Clipboard_getSupportedServiceNames();
}

Reference< XTransferable > SAL_CALL Os2Clipboard::getContents() throw( RuntimeException )
{
    debug_printf("Os2Clipboard::getContents\n");
    MutexGuard aGuard(m_aMutex);

    // os2 can have only one viewer at time, and we don't get a notification
    // when the viewer changes. So we need to check handles of clipboard
    // data and compare with previous handles
    if (UWinOpenClipbrd(hAB)) {
        sal_Bool	fireChanged = sal_False;
        ULONG handle = UWinQueryClipbrdData( hAB, UCLIP_CF_UNICODETEXT);
        if (handle) {
            if (handle != hText) {
                hText = handle;
                fireChanged = sal_True;
            }
        }
        handle = UWinQueryClipbrdData( hAB, UCLIP_CF_BITMAP);
        if (handle) {
            if (handle != hBitmap) {
                hBitmap = handle;
                fireChanged = sal_True;
            }
        }
        UWinCloseClipbrd( hAB);
        if (fireChanged)
        {
            // notify listener for clipboard change
            debug_printf("Os2Clipboard::getContents notify change\n");
            notifyAllClipboardListener();
        }
    }

    if( ! m_aContents.is() )
        m_aContents = new Os2Transferable( static_cast< OWeakObject* >(this) );

    return m_aContents;
}

void SAL_CALL Os2Clipboard::setContents( const Reference< XTransferable >& xTrans, const Reference< XClipboardOwner >& xClipboardOwner ) throw( RuntimeException )
{
    debug_printf("Os2Clipboard::setContents\n");
    // remember old values for callbacks before setting the new ones.
    ClearableMutexGuard aGuard(m_aMutex);

    Reference< XClipboardOwner > oldOwner(m_aOwner);
    m_aOwner = xClipboardOwner;

    Reference< XTransferable > oldContents(m_aContents);
    m_aContents = xTrans;

    aGuard.clear();

    // notify old owner on loss of ownership
    if( oldOwner.is() )
        oldOwner->lostOwnership(static_cast < XClipboard * > (this), oldContents);

    // notify all listeners on content changes
    OInterfaceContainerHelper *pContainer = 
        rBHelper.aLC.getContainer(getCppuType( (Reference < XClipboardListener > *) 0));
    if (pContainer)
    {
        ClipboardEvent aEvent(static_cast < XClipboard * > (this), m_aContents);
        OInterfaceIteratorHelper aIterator(*pContainer);

        while (aIterator.hasMoreElements())
        {
            Reference < XClipboardListener > xListener(aIterator.next(), UNO_QUERY);
            if (xListener.is())
                xListener->changedContents(aEvent);
        }
    }
    
#if OSL_DEBUG_LEVEL > 0
    // dump list of available mimetypes
    Sequence< DataFlavor > aFlavors( m_aContents->getTransferDataFlavors() );
    for( int i = 0; i < aFlavors.getLength(); i++ )
        debug_printf("Os2Clipboard::setContents available mimetype: %d %s\n", 
            i, CHAR_POINTER(aFlavors.getConstArray()[i].MimeType));
#endif

    // we can only export text or bitmap
    DataFlavor nFlavorText( OUString(RTL_CONSTASCII_USTRINGPARAM("text/plain;charset=utf-16")),
                        OUString(RTL_CONSTASCII_USTRINGPARAM("Unicode-Text")), CPPUTYPE_OUSTRING);
    DataFlavor nFlavorBitmap( OUString(RTL_CONSTASCII_USTRINGPARAM( "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"" )),
                        OUString(RTL_CONSTASCII_USTRINGPARAM("Bitmap")), CPPUTYPE_DEFAULT);
    
    // try text transfer data (if any)
    PSZ pSharedText = NULL;
    HBITMAP hbm = NULL;
    try
    {
        Any aAny = m_aContents->getTransferData( nFlavorText );
        if (aAny.hasValue())
        {
            APIRET rc;
            // copy unicode text to clipboard
            OUString aString; 
            aAny >>= aString;
            // share text
            rc = DosAllocSharedMem( (PPVOID) &pSharedText, NULL, 
                aString.getLength() * 2 + 2, 
                PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE | OBJ_ANY);
            if (!rc)
                memcpy( pSharedText, aString.getStr(), aString.getLength() * 2 + 2 );
            else
                pSharedText = NULL;
            debug_printf("Os2Clipboard::setContents SetClipbrdData text done\n");
        }
    } catch ( UnsupportedFlavorException&) {
        debug_printf("Os2Clipboard::setContents UnsupportedFlavorException (no text)\n");
    }

    // try bitmap transfer data (if any)
    try
    {
        Any aAnyB = m_aContents->getTransferData( nFlavorBitmap );
        if (aAnyB.hasValue())
        {
            hbm = OOoBmpToOS2Handle( aAnyB);
            debug_printf("Os2Clipboard::setContents SetClipbrdData bitmap done\n");
        }
    } catch ( UnsupportedFlavorException&) {
        debug_printf("Os2Clipboard::setContents UnsupportedFlavorException (no bitmap)\n");
    }

    // copy to clipboard
    if ( UWinOpenClipbrd( hAB) && (pSharedText || hbm))
    {
        // set the flag, so we will ignore the next WM_DRAWCLIPBOARD
        // since we generate it with following code.
        m_bInSetClipboardData = sal_True;
        UWinEmptyClipbrd( hAB);
        // give pointer to clipboard (it will become owner of pSharedText!)
        if (pSharedText) {
            UWinSetClipbrdData( hAB, (ULONG) pSharedText, UCLIP_CF_UNICODETEXT, CFI_POINTER);
            // update internal handle to avoid detection of this text as new data
            hText = (ULONG)pSharedText;
        }
        // give bitmap to clipboard 
        if (hbm) {
            UWinSetClipbrdData( hAB, (ULONG) hbm, UCLIP_CF_BITMAP, CFI_HANDLE);
            // update internal handle to avoid detection of this bitmap as new data
            hBitmap = hbm;
        }
        // reset the flag, so we will not ignore next WM_DRAWCLIPBOARD
        m_bInSetClipboardData = sal_False;
        UWinCloseClipbrd( hAB);
    }

}

OUString SAL_CALL Os2Clipboard::getName() throw( RuntimeException )
{
    debug_printf("Os2Clipboard::getName\n");
    return m_aName;
}

sal_Int8 SAL_CALL Os2Clipboard::getRenderingCapabilities() throw( RuntimeException )
{
    debug_printf("Os2Clipboard::getRenderingCapabilities\n");
    return Delayed;
}

//========================================================================
// XClipboardNotifier 
//========================================================================

void SAL_CALL Os2Clipboard::addClipboardListener( const Reference< XClipboardListener >& listener ) throw( RuntimeException )
{
    debug_printf("Os2Clipboard::addClipboardListener\n");
    MutexGuard aGuard( rBHelper.rMutex );
    OSL_ENSURE( !rBHelper.bInDispose, "do not add listeners in the dispose call" );
    OSL_ENSURE( !rBHelper.bDisposed, "object is disposed" );
    if (!rBHelper.bInDispose && !rBHelper.bDisposed)
        rBHelper.aLC.addInterface( getCppuType( (const ::com::sun::star::uno::Reference< XClipboardListener > *) 0), listener );
}

void SAL_CALL Os2Clipboard::removeClipboardListener( const Reference< XClipboardListener >& listener ) throw( RuntimeException )
{
    debug_printf("Os2Clipboard::removeClipboardListener\n");
    MutexGuard aGuard( rBHelper.rMutex );
    OSL_ENSURE( !rBHelper.bDisposed, "object is disposed" );
    if (!rBHelper.bInDispose && !rBHelper.bDisposed)
        rBHelper.aLC.removeInterface( getCppuType( (const Reference< XClipboardListener > *) 0 ), listener ); \
}

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

void SAL_CALL Os2Clipboard::notifyAllClipboardListener( )
{
    if ( !rBHelper.bDisposed )
    {
        ClearableMutexGuard aGuard( rBHelper.rMutex );
        if ( !rBHelper.bDisposed )
        {
            aGuard.clear( );

            ClearableMutexGuard aGuard(m_aMutex);
            // copy member references on stack so they can be called
            // without having the mutex
            Reference< XClipboardOwner > xOwner( m_aOwner );
            Reference< XTransferable > xTrans( m_aContents );
            // clear members
            m_aOwner.clear();
            m_aContents.clear();
            // release the mutex
            aGuard.clear();
        
            // inform previous owner of lost ownership
            if ( xOwner.is() )
                xOwner->lostOwnership(static_cast < XClipboard * > (this), m_aContents);

            OInterfaceContainerHelper* pICHelper = rBHelper.aLC.getContainer( 
                getCppuType( ( Reference< XClipboardListener > * ) 0 ) );

            if ( pICHelper )
            {				
                try
                {
                    OInterfaceIteratorHelper iter(*pICHelper);
                    m_aContents = 0;
                    m_aContents = new Os2Transferable( static_cast< OWeakObject* >(this) );
                    ClipboardEvent aClipbEvent(static_cast<XClipboard*>(this), m_aContents);

                    while(iter.hasMoreElements())
                    {
                        try
                        {
                            Reference<XClipboardListener> xCBListener(iter.next(), UNO_QUERY);
                            if (xCBListener.is())
                                xCBListener->changedContents(aClipbEvent);
                        }
                        catch(RuntimeException&)
                        {
                            OSL_FAIL( "RuntimeException caught" );
                            debug_printf( "RuntimeException caught" );
                        }					
                    } 
                }
                catch(const ::com::sun::star::lang::DisposedException&)
                {					
                    OSL_FAIL("Service Manager disposed");
                    debug_printf( "Service Manager disposed");
                    
                    // no further clipboard changed notifications
                    //m_pImpl->unregisterClipboardViewer();
                }
                
            } // end if
        } // end if
    } // end if
}

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

Sequence< OUString > SAL_CALL Os2Clipboard_getSupportedServiceNames()
{
    Sequence< OUString > aRet(1);
    aRet[0] = OUString(RTL_CONSTASCII_USTRINGPARAM( OS2_CLIPBOARD_SERVICE_NAME ));
    return aRet;
}

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

Reference< XInterface > SAL_CALL Os2Clipboard_createInstance( 
    const Reference< XMultiServiceFactory > & xMultiServiceFactory)
{
    return Reference < XInterface >( ( OWeakObject * ) new Os2Clipboard());
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
