/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the Collabora Office 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 <vcl/event.hxx>
#include <vcl/layout.hxx>
#include <vcl/tabpage.hxx>
#include <vcl/bitmap.hxx>
#include <vcl/settings.hxx>
#include <vcl/toolkit/scrbar.hxx>
#include <svdata.hxx>

void TabPage::ImplInit( vcl::Window* pParent, WinBits nStyle )
{
    if ( !(nStyle & WB_NODIALOGCONTROL) )
        nStyle |= WB_DIALOGCONTROL;

    Window::ImplInit( pParent, nStyle, nullptr );

    bool bHasHoriBar = false;
    bool bHasVertBar = false;

    Link<ScrollBar*,void> aLink( LINK( this, TabPage, ScrollBarHdl ) );

    if ( nStyle & ( WB_AUTOHSCROLL | WB_AUTOVSCROLL ) )
    {
        if ( nStyle & WB_AUTOHSCROLL )
        {
            bHasHoriBar = true;
            m_pHScroll.reset(VclPtr<ScrollBar>::Create(this, (WB_HSCROLL | WB_DRAG)));
            m_pHScroll->Show();
            m_pHScroll->SetScrollHdl(aLink);
        }
        if ( nStyle &  WB_AUTOVSCROLL )
        {
            bHasVertBar = true;
            m_pVScroll.reset(VclPtr<ScrollBar>::Create(this, (WB_VSCROLL | WB_DRAG)));
            m_pVScroll->Show();
            m_pVScroll->SetScrollHdl(aLink);
        }
    }

    if ( bHasHoriBar || bHasVertBar )
    {
        SetStyle( GetStyle() | WB_CLIPCHILDREN );
    }

    mnScrWidth = Application::GetSettings().GetStyleSettings().GetScrollBarSize();

    ImplInitSettings();

    // if the tabpage is drawn (ie filled) by a native widget, make sure all controls will have transparent background
    // otherwise they will paint with a wrong background
    if( IsNativeControlSupported(ControlType::TabBody, ControlPart::Entire) && GetParent() && (GetParent()->GetType() == WindowType::TABCONTROL) )
        EnableChildTransparentMode();
}

void TabPage::ImplInitSettings()
{
    vcl::Window* pParent = GetParent();
    if (pParent && pParent->IsChildTransparentModeEnabled() && !IsControlBackground())
    {
        EnableChildTransparentMode();
        SetParentClipMode( ParentClipMode::NoClip );
        SetPaintTransparent( true );
        SetBackground();
    }
    else
    {
        EnableChildTransparentMode( false );
        SetParentClipMode();
        SetPaintTransparent( false );

        if (IsControlBackground() || !pParent)
            SetBackground( GetControlBackground() );
        else
            SetBackground( pParent->GetBackground() );
    }
}

TabPage::TabPage( vcl::Window* pParent, WinBits nStyle ) :
    Window( WindowType::TABPAGE )
{
    ImplInit( pParent, nStyle );
}

TabPage::~TabPage()
{
    disposeOnce();
}

void TabPage::dispose()
{
    m_pVScroll.disposeAndClear();
    m_pHScroll.disposeAndClear();
    vcl::Window::dispose();
}

void TabPage::StateChanged( StateChangedType nType )
{
    Window::StateChanged( nType );

    if ( nType == StateChangedType::InitShow )
    {
        if (GetSettings().GetStyleSettings().GetAutoMnemonic())
            GenerateAutoMnemonicsOnHierarchy(this);
        // FIXME: no layouting, workaround some clipping issues
        ImplAdjustNWFSizes();
    }
    else if ( nType == StateChangedType::ControlBackground )
    {
        ImplInitSettings();
        Invalidate();
    }
}

void TabPage::DataChanged( const DataChangedEvent& rDCEvt )
{
    Window::DataChanged( rDCEvt );

    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
         (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
    {
        ImplInitSettings();
        Invalidate();
    }
}

void TabPage::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
{
    // draw native tabpage only inside tabcontrols, standalone tabpages look ugly (due to bad dialog design)
    if( !(IsNativeControlSupported(ControlType::TabBody, ControlPart::Entire) && GetParent() && (GetParent()->GetType() == WindowType::TABCONTROL)) )
        return;

    const ImplControlValue aControlValue;

    ControlState nState = ControlState::ENABLED;
    if ( !IsEnabled() )
        nState &= ~ControlState::ENABLED;
    if ( HasFocus() )
        nState |= ControlState::FOCUSED;
    // pass the whole window region to NWF as the tab body might be a gradient or bitmap
    // that has to be scaled properly, clipping makes sure that we do not paint too much
    tools::Rectangle aCtrlRegion( Point(), GetOutputSizePixel() );
    rRenderContext.DrawNativeControl( ControlType::TabBody, ControlPart::Entire, aCtrlRegion, nState,
            aControlValue, OUString() );
}

void TabPage::Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags )
{
    Point aPos = pDev->LogicToPixel( rPos );
    Size aSize = GetSizePixel();

    Wallpaper aWallpaper = GetBackground();
    if ( !aWallpaper.IsBitmap() )
        ImplInitSettings();

    auto popIt = pDev->ScopedPush();
    pDev->SetMapMode();
    pDev->SetLineColor();

    if ( aWallpaper.IsBitmap() )
        pDev->DrawBitmap( aPos, aSize, aWallpaper.GetBitmap() );
    else
    {
        if( aWallpaper.GetColor() == COL_AUTO )
            pDev->SetFillColor( GetSettings().GetStyleSettings().GetDialogColor() );
        else
            pDev->SetFillColor( aWallpaper.GetColor() );
        pDev->DrawRect( tools::Rectangle( aPos, aSize ) );
    }
}

Size TabPage::GetOptimalSize() const
{
    if (isLayoutEnabled(this))
        return VclContainer::getLayoutRequisition(*GetWindow(GetWindowType::FirstChild));
    return getLegacyBestSizeForChildren(*this);
}

void TabPage::SetPosSizePixel(const Point& rAllocPos, const Size& rAllocation)
{
    Window::SetPosSizePixel(rAllocPos, rAllocation);
    if (isLayoutEnabled(this) && rAllocation.Width() && rAllocation.Height())
        VclContainer::setLayoutAllocation(*GetWindow(GetWindowType::FirstChild), Point(0, 0), rAllocation);
}

void TabPage::SetSizePixel(const Size& rAllocation)
{
    Window::SetSizePixel(rAllocation);
    if (isLayoutEnabled(this) && rAllocation.Width() && rAllocation.Height())
        VclContainer::setLayoutAllocation(*GetWindow(GetWindowType::FirstChild), Point(0, 0), rAllocation);
}

void TabPage::SetPosPixel(const Point& rAllocPos)
{
    Window::SetPosPixel(rAllocPos);
    Size aAllocation(GetOutputSizePixel());
    if (isLayoutEnabled(this) && aAllocation.Width() && aAllocation.Height())
    {
        VclContainer::setLayoutAllocation(*GetWindow(GetWindowType::FirstChild), Point(0, 0), aAllocation);
    }
}

void TabPage::lcl_Scroll( tools::Long nX, tools::Long nY )
{
    tools::Long nXScroll = mnScrollPos.X() - nX;
    tools::Long nYScroll = mnScrollPos.Y() - nY;
    mnScrollPos = Point( nX, nY );

    tools::Rectangle aScrollableArea( 0, 0, maScrollArea.Width(), maScrollArea.Height() );
    Scroll(nXScroll, nYScroll, aScrollableArea );
    // Manually scroll all children ( except the scrollbars )
    for ( int index = 0; index < GetChildCount(); ++index )
    {
        vcl::Window* pChild = GetChild( index );
        if ( pChild && pChild != m_pVScroll.get() && pChild != m_pHScroll.get() )
        {
            Point aPos = pChild->GetPosPixel();
            aPos += Point( nXScroll, nYScroll );
            pChild->SetPosPixel( aPos );
        }
    }
}

IMPL_LINK( TabPage, ScrollBarHdl, ScrollBar*, pSB, void )
{
    sal_uInt16 nPos = static_cast<sal_uInt16>(pSB->GetThumbPos());
    if( pSB == m_pVScroll.get() )
        lcl_Scroll(mnScrollPos.X(), nPos );
    else if( pSB == m_pHScroll.get() )
        lcl_Scroll(nPos, mnScrollPos.Y() );
}

void TabPage::SetScrollTop( tools::Long nTop )
{
    Point aOld = mnScrollPos;
    lcl_Scroll( mnScrollPos.X() , mnScrollPos.Y() - nTop );
    if( m_pHScroll )
        m_pHScroll->SetThumbPos( 0 );
    // new pos is 0,0
    mnScrollPos = aOld;
}
void TabPage::SetScrollLeft( tools::Long nLeft )
{
    Point aOld = mnScrollPos;
    lcl_Scroll( mnScrollPos.X() - nLeft , mnScrollPos.Y() );
    if( m_pVScroll )
        m_pVScroll->SetThumbPos( 0 );
    // new pos is 0,0
    mnScrollPos = aOld;
}

void TabPage::SetScrollWidth( tools::Long nWidth )
{
    maScrollArea.setWidth( nWidth );
    ResetScrollBars();
}

void TabPage::SetScrollHeight( tools::Long nHeight )
{
    maScrollArea.setHeight( nHeight );
    ResetScrollBars();
}

void TabPage::Resize()
{
    ResetScrollBars();
}

void TabPage::ResetScrollBars()
{
    Size aOutSz = GetOutputSizePixel();

    Point aVPos( aOutSz.Width() - mnScrWidth, 0 );
    Point aHPos( 0, aOutSz.Height() - mnScrWidth );

    if( m_pVScroll )
    {
        m_pVScroll->SetPosSizePixel( aVPos, Size( mnScrWidth,  GetSizePixel().Height() - mnScrWidth ) );
        m_pVScroll->SetRangeMax( maScrollArea.Height() + mnScrWidth );
        m_pVScroll->SetVisibleSize( GetSizePixel().Height() );
    }

    if( m_pHScroll )
    {
        m_pHScroll->SetPosSizePixel( aHPos, Size(  GetSizePixel().Width() - mnScrWidth, mnScrWidth ) );
        m_pHScroll->SetRangeMax( maScrollArea.Width() + mnScrWidth  );
        m_pHScroll->SetVisibleSize( GetSizePixel().Width() );
    }
}

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