/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <cassert>
 
#include <vcl/outdev.hxx>
#include <vcl/virdev.hxx>
#include <vcl/window.hxx>
#include <sal/log.hxx>
 
#include <vcl/salnativewidgets.hxx>
#include <vcl/pdfextoutdevdata.hxx>
 
#include <salgdi.hxx>
 
static bool EnableNativeWidget( const OutputDevice& i_rDevice )
{
    const OutDevType eType( i_rDevice.GetOutDevType() );
    switch ( eType )
    {
 
    case OUTDEV_WINDOW:
        {
            const vcl::Window* pWindow = dynamic_cast< const vcl::Window* >( &i_rDevice );
            if (pWindow)
            {
                return pWindow->IsNativeWidgetEnabled();
            }
            else
            {
                SAL_WARN ("vcl.gdi", "Could not cast i_rDevice to Window");
                assert (pWindow);
                return false;
            }
        }
 
    case OUTDEV_VIRDEV:
    {
        const vcl::ExtOutDevData* pOutDevData( i_rDevice.GetExtOutDevData() );
        const vcl::PDFExtOutDevData* pPDFData( dynamic_cast< const vcl::PDFExtOutDevData* >( pOutDevData ) );
        return pPDFData == nullptr;
    }
 
    default:
        return false;
    }
}
 
ImplControlValue::~ImplControlValue()
{
}
 
ImplControlValue* ImplControlValue::clone() const
{
    assert( typeid( const ImplControlValue ) == typeid( *this ));
    return new ImplControlValue( *this );
}
 
ScrollbarValue::~ScrollbarValue()
{
}
 
ScrollbarValue* ScrollbarValue::clone() const
{
    assert( typeid( const ScrollbarValue ) == typeid( *this ));
    return new ScrollbarValue( *this );
}
 
SliderValue::~SliderValue()
{
}
 
SliderValue* SliderValue::clone() const
{
    assert( typeid( const SliderValue ) == typeid( *this ));
    return new SliderValue( *this );
}
 
TabitemValue::~TabitemValue()
{
}
 
TabitemValue* TabitemValue::clone() const
{
    assert( typeid( const TabitemValue ) == typeid( *this ));
    return new TabitemValue( *this );
}
 
SpinbuttonValue::~SpinbuttonValue()
{
}
 
SpinbuttonValue* SpinbuttonValue::clone() const
{
    assert( typeid( const SpinbuttonValue ) == typeid( *this ));
    return new SpinbuttonValue( *this );
}
 
ToolbarValue::~ToolbarValue()
{
}
 
ToolbarValue* ToolbarValue::clone() const
{
    assert( typeid( const ToolbarValue ) == typeid( *this ));
    return new ToolbarValue( *this );
}
 
MenubarValue::~MenubarValue()
{
}
 
MenubarValue* MenubarValue::clone() const
{
    assert( typeid( const MenubarValue ) == typeid( *this ));
    return new MenubarValue( *this );
}
 
MenupopupValue::~MenupopupValue()
{
}
 
MenupopupValue* MenupopupValue::clone() const
{
    assert( typeid( const MenupopupValue ) == typeid( *this ));
    return new MenupopupValue( *this );
}
 
PushButtonValue::~PushButtonValue()
{
}
 
PushButtonValue* PushButtonValue::clone() const
{
    assert( typeid( const PushButtonValue ) == typeid( *this ));
    return new PushButtonValue( *this );
}
 
// These functions are mainly passthrough functions that allow access to
// the SalFrame behind a Window object for native widget rendering purposes.
 
bool OutputDevice::IsNativeControlSupported( ControlType nType, ControlPart nPart ) const
{
    if( !EnableNativeWidget( *this ) )
        return false;
 
    if ( !mpGraphics )
        if ( !AcquireGraphics() )
            return false;
 
    return mpGraphics->IsNativeControlSupported(nType, nPart);
}
 
bool OutputDevice::HitTestNativeScrollbar(
                              ControlPart nPart,
                              const tools::Rectangle& rControlRegion,
                              const Point& aPos,
                              bool& rIsInside ) const
{
    if( !EnableNativeWidget( *this ) )
        return false;
 
    if ( !mpGraphics )
        if ( !AcquireGraphics() )
            return false;
 
    Point aWinOffs( mnOutOffX, mnOutOffY );
    tools::Rectangle screenRegion( rControlRegion );
    screenRegion.Move( aWinOffs.X(), aWinOffs.Y());
 
    return mpGraphics->HitTestNativeScrollbar( nPart, screenRegion, Point( aPos.X() + mnOutOffX, aPos.Y() + mnOutOffY ),
        rIsInside, this );
}
 
static std::shared_ptr< ImplControlValue > TransformControlValue( const ImplControlValue& rVal, const OutputDevice& rDev )
{
    std::shared_ptr< ImplControlValue > aResult;
    switch( rVal.getType() )
    {
    case ControlType::Slider:
        {
            const SliderValue* pSlVal = static_cast<const SliderValue*>(&rVal);
            SliderValue* pNew = new SliderValue( *pSlVal );
            aResult.reset( pNew );
            pNew->maThumbRect = rDev.ImplLogicToDevicePixel( pSlVal->maThumbRect );
        }
        break;
    case ControlType::Scrollbar:
        {
            const ScrollbarValue* pScVal = static_cast<const ScrollbarValue*>(&rVal);
            ScrollbarValue* pNew = new ScrollbarValue( *pScVal );
            aResult.reset( pNew );
            pNew->maThumbRect = rDev.ImplLogicToDevicePixel( pScVal->maThumbRect );
            pNew->maButton1Rect = rDev.ImplLogicToDevicePixel( pScVal->maButton1Rect );
            pNew->maButton2Rect = rDev.ImplLogicToDevicePixel( pScVal->maButton2Rect );
        }
        break;
    case ControlType::SpinButtons:
        {
            const SpinbuttonValue* pSpVal = static_cast<const SpinbuttonValue*>(&rVal);
            SpinbuttonValue* pNew = new SpinbuttonValue( *pSpVal );
            aResult.reset( pNew );
            pNew->maUpperRect = rDev.ImplLogicToDevicePixel( pSpVal->maUpperRect );
            pNew->maLowerRect = rDev.ImplLogicToDevicePixel( pSpVal->maLowerRect );
        }
        break;
    case ControlType::Toolbar:
        {
            const ToolbarValue* pTVal = static_cast<const ToolbarValue*>(&rVal);
            ToolbarValue* pNew = new ToolbarValue( *pTVal );
            aResult.reset( pNew );
            pNew->maGripRect = rDev.ImplLogicToDevicePixel( pTVal->maGripRect );
        }
        break;
    case ControlType::TabItem:
        {
            const TabitemValue* pTIVal = static_cast<const TabitemValue*>(&rVal);
            TabitemValue* pNew = new TabitemValue( *pTIVal );
            pNew->maContentRect = rDev.ImplLogicToDevicePixel(pTIVal->maContentRect);
            aResult.reset( pNew );
        }
        break;
    case ControlType::Menubar:
        {
            const MenubarValue* pMVal = static_cast<const MenubarValue*>(&rVal);
            MenubarValue* pNew = new MenubarValue( *pMVal );
            aResult.reset( pNew );
        }
        break;
    case ControlType::Pushbutton:
        {
            const PushButtonValue* pBVal = static_cast<const PushButtonValue*>(&rVal);
            PushButtonValue* pNew = new PushButtonValue( *pBVal );
            aResult.reset( pNew );
        }
        break;
    case ControlType::Generic:
            aResult.reset( new ImplControlValue( rVal ) );
            break;
    case ControlType::MenuPopup:
        {
            const MenupopupValue* pMVal = static_cast<const MenupopupValue*>(&rVal);
            MenupopupValue* pNew = new MenupopupValue( *pMVal );
            pNew->maItemRect = rDev.ImplLogicToDevicePixel( pMVal->maItemRect );
            aResult.reset( pNew );
        }
        break;
    default:
        OSL_FAIL( "unknown ImplControlValue type !" );
        break;
    }
    return aResult;
}
bool OutputDevice::DrawNativeControl( ControlType nType,
                            ControlPart nPart,
                            const tools::Rectangle& rControlRegion,
                            ControlState nState,
                            const ImplControlValue& aValue,
                            const OUString& aCaption )
{
    assert(!is_double_buffered_window());
 
    if( !EnableNativeWidget( *this ) )
        return false;
 
    // make sure the current clip region is initialized correctly
    if ( !mpGraphics )
        if ( !AcquireGraphics() )
            return false;
 
    if ( mbInitClipRegion )
        InitClipRegion();
    if ( mbOutputClipped )
        return true;
 
    if ( mbInitLineColor )
        InitLineColor();
    if ( mbInitFillColor )
        InitFillColor();
 
    // Convert the coordinates from relative to Window-absolute, so we draw
    // in the correct place in platform code
    std::shared_ptr< ImplControlValue > aScreenCtrlValue( TransformControlValue( aValue, *this ) );
    tools::Rectangle screenRegion( ImplLogicToDevicePixel( rControlRegion ) );
 
    vcl::Region aTestRegion( GetActiveClipRegion() );
    aTestRegion.Intersect( rControlRegion );
    if (aTestRegion == vcl::Region(rControlRegion))
        nState |= ControlState::CACHING_ALLOWED;   // control is not clipped, caching allowed
 
    if (dynamic_cast<VirtualDevice*>(this))
        nState |= ControlState::DOUBLEBUFFERING;
 
    bool bRet = mpGraphics->DrawNativeControl(nType, nPart, screenRegion, nState, *aScreenCtrlValue, aCaption, this );
 
    return bRet;
}
 
bool OutputDevice::GetNativeControlRegion(  ControlType nType,
                                ControlPart nPart,
                                const tools::Rectangle& rControlRegion,
                                ControlState nState,
                                const ImplControlValue& aValue,
                                tools::Rectangle &rNativeBoundingRegion,
                                tools::Rectangle &rNativeContentRegion ) const
{
    if( !EnableNativeWidget( *this ) )
        return false;
 
    if ( !mpGraphics )
        if ( !AcquireGraphics() )
            return false;
 
    // Convert the coordinates from relative to Window-absolute, so we draw
    // in the correct place in platform code
    std::shared_ptr< ImplControlValue > aScreenCtrlValue( TransformControlValue( aValue, *this ) );
    tools::Rectangle screenRegion( ImplLogicToDevicePixel( rControlRegion ) );
 
    bool bRet = mpGraphics->GetNativeControlRegion(nType, nPart, screenRegion, nState, *aScreenCtrlValue,
                                rNativeBoundingRegion,
                                rNativeContentRegion, this );
    if( bRet )
    {
        // transform back native regions
        rNativeBoundingRegion = ImplDevicePixelToLogic( rNativeBoundingRegion );
        rNativeContentRegion = ImplDevicePixelToLogic( rNativeContentRegion );
    }
 
    return bRet;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1004 The 'mpGraphics' pointer was used unsafely after it was verified against nullptr. Check lines: 163, 167.

V1004 The 'mpGraphics' pointer was used unsafely after it was verified against nullptr. Check lines: 179, 187.