/* -*- 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 .
 */

#undef SC_DLLIMPLEMENTATION

#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <svtools/collatorres.hxx>
#include <unotools/collatorwrapper.hxx>
#include <comphelper/processfactory.hxx>
#include <osl/diagnose.h>

#include <scitems.hxx>
#include <uiitems.hxx>
#include <viewdata.hxx>
#include <document.hxx>
#include <global.hxx>
#include <dbdata.hxx>
#include <userlist.hxx>
#include <rangeutl.hxx>
#include <scresid.hxx>
#include <sc.hrc>
#include <strings.hrc>
#include <globstr.hrc>

#include <sortkeydlg.hxx>

#include <sortdlg.hxx>

#include <tpsort.hxx>

using namespace com::sun::star;

/*
 * Since the settings on the second Tab Page (Options) effects
 * the first Tab Page, there must be a way for it to communicate with the
 * other Page.
 *
 * At the moment this problem is solved through using two data members of the
 * Tab Pages. If a page is enabled / disabled, it compares this data member
 * with its own state (-> Activate() / Deactivate()).
 *
 * In the meantime the class SfxTabPage offers the following method:
 *
 * virtual sal_Bool HasExchangeSupport() const; -> return sal_True;
 * virtual void ActivatePage(const SfxItemSet &);
 * virtual int  DeactivatePage(SfxItemSet * = 0);
 *
 * This still needs to be changed!
 */

// Sort Criteria Tab page

ScTabPageSortFields::ScTabPageSortFields(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
    : SfxTabPage(pPage, pController, u"modules/scalc/ui/sortcriteriapage.ui"_ustr, u"SortCriteriaPage"_ustr, &rArgSet)
    ,

        m_aIdle("ScTabPageSortFields Scroll To End Idle"),
        aStrUndefined   ( ScResId( SCSTR_UNDEFINED ) ),
        aStrColumn      ( ScResId( SCSTR_COLUMN ) ),
        aStrRow         ( ScResId( SCSTR_ROW ) ),
        aStrRowLabel    ( ScResId( SCSTR_ROW_LABEL ) ),
        aStrColLabel    ( ScResId( SCSTR_COL_LABEL ) ),

        nWhichSort      ( rArgSet.GetPool()->GetWhichIDFromSlotID( SID_SORT ) ),
        pViewData       ( nullptr ),
        aSortData       ( rArgSet.Get( nWhichSort ).GetSortData() ),
        nFieldCount     ( 0 ),
        // show actual size of the sorting keys without limiting them to the default size
        nSortKeyCount(std::max(aSortData.GetSortKeyCount(), static_cast<sal_uInt16>(DEFSORT)))

    , m_xTop(m_xBuilder->weld_container(u"TopWindow"_ustr))
    , m_xBtnHeader(m_xBuilder->weld_check_button(u"cbHeader"_ustr))
    , m_xBtnTopDown(m_xBuilder->weld_radio_button(u"rbTopDown"_ustr))
    , m_xBtnLeftRight(m_xBuilder->weld_radio_button(u"rbLeftRight"_ustr))
    , m_xScrolledWindow(m_xBuilder->weld_scrolled_window(u"SortCriteriaPage"_ustr))
    , m_xBox(m_xBuilder->weld_box(u"SortKeyWindow"_ustr))
    , m_aSortWin(m_xBox.get())
{
    // tdf#147722 set some nominal small default height so the height adapts
    // to all the other contents and the natural height of this widget isn't
    // an input into the overall size
    m_xScrolledWindow->set_size_request(-1, 42);

    Init();

    m_aIdle.SetInvokeHandler(LINK(this, ScTabPageSortFields, ScrollToEndHdl));

    SetExchangeSupport();
}

ScTabPageSortFields::~ScTabPageSortFields()
{
    m_aSortWin.m_aSortKeyItems.clear();
    m_xBox.reset();
    m_xScrolledWindow.reset();
}

void ScTabPageSortFields::Init()
{
    // Check whether the field that is passed on is a database field:
    ScDocument* pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
    if ( pDoc )
    {
        ScDBCollection* pDBColl = pDoc->GetDBCollection();
        const SCTAB     nCurTab = pViewData->CurrentTabForData();
        if ( pDBColl )
        {
            ScDBData* pDBData
                    = pDBColl->GetDBAtArea( nCurTab,
                                            aSortData.nCol1, aSortData.nRow1,
                                            aSortData.nCol2, aSortData.nRow2 );
            if ( pDBData )
            {
                m_xBtnHeader->set_active(pDBData->HasHeader());
            }
        }
    }
    m_xBtnHeader->set_label(aStrColLabel);

    Link<weld::Toggleable&,void> aLink = LINK(this, ScTabPageSortFields, SortDirHdl );
    m_xBtnTopDown->connect_toggled( aLink );
    m_xBtnLeftRight->connect_toggled( aLink );
    m_xBtnHeader->connect_toggled( aLink );

    const ScSortItem& rSortItem = GetItemSet().Get( nWhichSort );

    pViewData = rSortItem.GetViewData();
    OSL_ENSURE( pViewData, "ViewData not found!" );

    nFieldArr.push_back( 0 );

    // Create three sort key dialogs by default
    for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
    {
        AddSortKey(i+1);
        m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->connect_changed(LINK(this, ScTabPageSortFields, SelectHdl));
    }
}

std::unique_ptr<SfxTabPage> ScTabPageSortFields::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* pArgSet)
{
    return std::make_unique<ScTabPageSortFields>(pPage, pController, *pArgSet);
}

void ScTabPageSortFields::Reset( const SfxItemSet* /* rArgSet */ )
{
    m_xBtnHeader->set_active( aSortData.bHasHeader );
    m_xBtnTopDown->set_active( aSortData.bByRow );
    m_xBtnLeftRight->set_active( !aSortData.bByRow );

    if (m_aSortWin.m_aSortKeyItems[0]->m_xLbSort->get_count() == 0)
        FillFieldLists(0);

    // ListBox selection:
    if (!aSortData.maKeyState.empty() && aSortData.maKeyState[0].bDoSort)
    {
        // Make sure that the all sort keys are reset
        for ( sal_uInt16 i=nSortKeyCount; i<aSortData.GetSortKeyCount(); i++ )
        {
            AddSortKey(i+1);
            m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->connect_changed( LINK( this,
                                 ScTabPageSortFields, SelectHdl ) );
        }
        nSortKeyCount = aSortData.GetSortKeyCount();
        FillFieldLists(0);

        for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
        {
            if (aSortData.maKeyState[i].bDoSort )
            {
                m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active( GetFieldSelPos(
                                    aSortData.maKeyState[i].nField ) );
                (aSortData.maKeyState[i].bAscending)
                    ? m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->set_active(true)
                    : m_aSortWin.m_aSortKeyItems[i]->m_xBtnDown->set_active(true);
            }
            else
            {
                m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active(0); // Select none
                m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->set_active(true);
            }
        }

        // Enable or disable field depending on preceding Listbox selection
        m_aSortWin.m_aSortKeyItems[0]->EnableField();
        for ( sal_uInt16 i=1; i<nSortKeyCount; i++ )
            if ( m_aSortWin.m_aSortKeyItems[i - 1]->m_xLbSort->get_active() == 0 )
                m_aSortWin.m_aSortKeyItems[i]->DisableField();
            else
                m_aSortWin.m_aSortKeyItems[i]->EnableField();
    }
    else
    {
        SCCOL  nCol = pViewData->GetCurX();

        if( nCol < aSortData.nCol1 )
            nCol = aSortData.nCol1;
        else if( nCol > aSortData.nCol2 )
            nCol = aSortData.nCol2;

        sal_uInt16  nSort1Pos = nCol - aSortData.nCol1+1;

        m_aSortWin.m_aSortKeyItems[0]->m_xLbSort->set_active(nSort1Pos);
        for ( sal_uInt16 i=1; i<nSortKeyCount; i++ )
            m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active(0);

        for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
            m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->set_active(true);

        m_aSortWin.m_aSortKeyItems[0]->EnableField();
        m_aSortWin.m_aSortKeyItems[1]->EnableField();
        for ( sal_uInt16 i=2; i<nSortKeyCount; i++ )
            m_aSortWin.m_aSortKeyItems[i]->DisableField();
    }

    // Make sure that there is always a last undefined sort key
    if (m_aSortWin.m_aSortKeyItems[nSortKeyCount - 1]->m_xLbSort->get_active() > 0)
        SetLastSortKey( nSortKeyCount );
}

bool ScTabPageSortFields::FillItemSet( SfxItemSet* rArgSet )
{
    ScSortParam aNewSortData = aSortData;

    const SfxItemSet* pExample = GetDialogExampleSet();
    if (pExample)
    {
        if (const ScSortItem* pItem = pExample->GetItemIfSet(nWhichSort))
        {
            ScSortParam aTempData = pItem->GetSortData();
            aTempData.maKeyState = aNewSortData.maKeyState;
            aNewSortData = aTempData;
        }
    }
    aNewSortData.bByRow        = m_xBtnTopDown->get_active();
    aNewSortData.bHasHeader    = m_xBtnHeader->get_active();

    std::vector<sal_Int32>  nSortPos;

    for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
    {
        nSortPos.push_back(m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->get_active());
        if (nSortPos[i] == -1) nSortPos[i] = 0;
    }

    if( nSortKeyCount >= aNewSortData.GetSortKeyCount() )
        aNewSortData.maKeyState.resize(nSortKeyCount);

    if ( nSortPos[0] > 0 )
    {
        for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
        {
            aNewSortData.maKeyState[i].bDoSort = (nSortPos[i] > 0);
            aNewSortData.maKeyState[i].nField = nFieldArr[nSortPos[i]];
            aNewSortData.maKeyState[i].bAscending = m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->get_active();
        }
    }
    else
    {
        for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
            aNewSortData.maKeyState[i].bDoSort = false;
    }

    rArgSet->Put( ScSortItem( SCITEM_SORTDATA, nullptr, &aNewSortData ) );

    return true;
}

// for data exchange without dialogue detour:
void ScTabPageSortFields::ActivatePage( const SfxItemSet& rSet )
{
    // Refresh local copy with shared data
    aSortData = rSet.Get( SCITEM_SORTDATA ).GetSortData();

    m_xBtnHeader->set_active( aSortData.bHasHeader );
    m_xBtnTopDown->set_active( aSortData.bByRow );
    m_xBtnLeftRight->set_active( !aSortData.bByRow );
}

DeactivateRC ScTabPageSortFields::DeactivatePage( SfxItemSet* pSetP )
{
    if ( pSetP )
        FillItemSet( pSetP );

    return DeactivateRC::LeavePage;
}

void ScTabPageSortFields::FillFieldLists( sal_uInt16 nStartField )
{
    if ( !pViewData )
        return;

    ScDocument& rDoc = pViewData->GetDocument();

    for (sal_uInt16 j = nStartField; j < nSortKeyCount; ++j)
    {
        m_aSortWin.m_aSortKeyItems[j]->m_xLabel->set_label(aSortData.bByRow ? aStrColumn : aStrRow);
        m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->clear();
        m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->freeze();
        m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->append_text(aStrUndefined);
    }

    SCCOL   nFirstSortCol   = aSortData.nCol1;
    SCROW   nFirstSortRow   = aSortData.nRow1;
    SCTAB   nTab        = pViewData->CurrentTabForData();
    sal_uInt16  i           = 1;
    nFieldArr.clear();
    nFieldArr.push_back(0);

    if ( aSortData.bByRow )
    {
        OUString  aFieldName;
        SCCOL   nMaxCol = rDoc.ClampToAllocatedColumns(nTab, aSortData.nCol2);
        SCCOL   col;

        for ( col=nFirstSortCol; col<=nMaxCol && i<SC_MAXFIELDS(rDoc.GetSheetLimits()); col++ )
        {
            aFieldName = rDoc.GetString(col, nFirstSortRow, nTab);
            if ( !aSortData.bHasHeader || aFieldName.isEmpty() )
                aFieldName = ScColToAlpha( col );
            nFieldArr.push_back( col );

            for ( sal_uInt16 j=nStartField; j<nSortKeyCount; j++ )
                m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->insert_text(i, aFieldName);

            i++;
        }
    }
    else
    {
        OUString  aFieldName;
        SCROW   nMaxRow = aSortData.nRow2;
        SCROW   row;

        for ( row=nFirstSortRow; row<=nMaxRow && i<SC_MAXFIELDS(rDoc.GetSheetLimits()); row++ )
        {
            aFieldName = rDoc.GetString(nFirstSortCol, row, nTab);
            if ( !aSortData.bHasHeader || aFieldName.isEmpty() )
                aFieldName = OUString::number( row+1);
            nFieldArr.push_back( row );

            for ( sal_uInt16 j=nStartField; j<nSortKeyCount; j++ )
                m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->insert_text(i, aFieldName);

            i++;
        }
    }

    for (sal_uInt16 j=nStartField; j < nSortKeyCount; ++j)
    {
        m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->thaw();
    }

    nFieldCount = i;
}

sal_uInt16 ScTabPageSortFields::GetFieldSelPos( SCCOLROW nField )
{
    sal_uInt16  nFieldPos   = 0;
    bool    bFound      = false;

    for ( sal_uInt16 n=1; n<nFieldCount && !bFound; n++ )
    {
        if ( nFieldArr[n] == nField )
        {
            nFieldPos = n;
            bFound = true;
        }
    }

    return nFieldPos;
}

void ScTabPageSortFields::SetLastSortKey( sal_uInt16 nItem )
{
    // Extend local SortParam copy
    const ScSortKeyState atempKeyState = { 0, false, true, ScColorSortMode::None, Color() };
    aSortData.maKeyState.push_back( atempKeyState );

    // Add Sort Key Item
    ++nSortKeyCount;
    AddSortKey( nSortKeyCount );
    m_aSortWin.m_aSortKeyItems[nItem]->m_xLbSort->connect_changed(
                     LINK( this, ScTabPageSortFields, SelectHdl ) );

    FillFieldLists( nItem );

    // Set Status
    m_aSortWin.m_aSortKeyItems[nItem]->m_xBtnUp->set_active(true);
    m_aSortWin.m_aSortKeyItems[nItem]->m_xLbSort->set_active(0);
}

// Handler:

IMPL_LINK_NOARG(ScTabPageSortFields, SortDirHdl, weld::Toggleable&, void)
{
    if ( (m_xBtnTopDown->get_active() != aSortData.bByRow) || (m_xBtnHeader->get_active() != aSortData.bHasHeader))
    {
        if (m_xBtnTopDown->get_active())
            m_xBtnHeader->set_label(aStrColLabel);
        else
            m_xBtnHeader->set_label(aStrRowLabel);

        aSortData.bByRow     = m_xBtnTopDown->get_active();
        aSortData.bHasHeader = m_xBtnHeader->get_active();

        // remember selection
        std::vector<sal_uInt16> nCurSel;
        for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
            nCurSel.push_back( m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->get_active() );

        FillFieldLists(0);

        for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
            m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active(nCurSel[i]);
    }
}

IMPL_LINK( ScTabPageSortFields, SelectHdl, weld::ComboBox&, rLb, void )
{
    OUString aSelEntry = rLb.get_active_text();
    ScSortKeyItems::iterator pIter;

    // If last listbox is enabled add one item
    if (m_aSortWin.m_aSortKeyItems.back()->m_xLbSort.get() == &rLb)
    {
        if (rLb.get_active() >= 0 && aSelEntry != aStrUndefined)
        {
            SetLastSortKey( nSortKeyCount );
            return;
        }
    }

    // Find selected listbox
    pIter = std::find_if(m_aSortWin.m_aSortKeyItems.begin(), m_aSortWin.m_aSortKeyItems.end(),
        [&rLb](const ScSortKeyItems::value_type& rItem) { return rItem->m_xLbSort.get() == &rLb; });

    if (pIter == m_aSortWin.m_aSortKeyItems.end())
        return;

    // If not selecting the last Listbox, modify the succeeding ones
    ++pIter;
    if ( std::distance(m_aSortWin.m_aSortKeyItems.begin(), pIter) >= nSortKeyCount )
        return;

    if ( aSelEntry == aStrUndefined )
    {
        for ( ; pIter != m_aSortWin.m_aSortKeyItems.end(); ++pIter )
        {
            (*pIter)->m_xLbSort->set_active(0);

            (*pIter)->DisableField();
        }
    }
    else
    {
        (*pIter)->EnableField();
    }
}

IMPL_LINK_NOARG(ScTabPageSortFields, ScrollToEndHdl, Timer*, void)
{
    m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_upper());
}

void ScTabPageSortFields::AddSortKey( sal_uInt16 nItem )
{
    m_aSortWin.AddSortKey(nItem);
    m_aIdle.Start();
}

// Sort option Tab Page:

ScTabPageSortOptions::ScTabPageSortOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
    : SfxTabPage(pPage, pController, u"modules/scalc/ui/sortoptionspage.ui"_ustr, u"SortOptionsPage"_ustr, &rArgSet)
    , aStrUndefined(ScResId(SCSTR_UNDEFINED))
    , aStrCommentsRowLabel(ScResId(SCSTR_NOTES_ROW_LABEL))
    , aStrCommentsColLabel(ScResId(SCSTR_NOTES_COL_LABEL))
    , aStrImgRowLabel(ScResId(SCSTR_IMAGES_ROW_LABEL))
    , aStrImgColLabel(ScResId(SCSTR_IMAGES_COL_LABEL))
    , nWhichSort(rArgSet.GetPool()->GetWhichIDFromSlotID(SID_SORT))
    , aSortData(rArgSet.Get(nWhichSort).GetSortData())
    , pViewData(nullptr)
    , pDoc(nullptr)
    , m_xBtnCase(m_xBuilder->weld_check_button(u"case"_ustr))
    , m_xBtnFormats(m_xBuilder->weld_check_button(u"formats"_ustr))
    , m_xBtnNaturalSort(m_xBuilder->weld_check_button(u"naturalsort"_ustr))
    , m_xBtnCopyResult(m_xBuilder->weld_check_button(u"copyresult"_ustr))
    , m_xLbOutPos(m_xBuilder->weld_combo_box(u"outarealb"_ustr))
    , m_xEdOutPos(m_xBuilder->weld_entry(u"outareaed"_ustr))
    , m_xBtnSortUser(m_xBuilder->weld_check_button(u"sortuser"_ustr))
    , m_xLbSortUser(m_xBuilder->weld_combo_box(u"sortuserlb"_ustr))
    , m_xLbLanguage(new SvxLanguageBox(m_xBuilder->weld_combo_box(u"language"_ustr)))
    , m_xFtAlgorithm(m_xBuilder->weld_label(u"algorithmft"_ustr))
    , m_xLbAlgorithm(m_xBuilder->weld_combo_box(u"algorithmlb"_ustr))
    , m_xBtnIncComments(m_xBuilder->weld_check_button(u"includenotes"_ustr))
    , m_xBtnIncImages(m_xBuilder->weld_check_button(u"includeimages"_ustr))
    , m_xRBDoubleNaturalSort(m_xBuilder->weld_radio_button(u"doublenaturalsortrb"_ustr))
    , m_xRBIntegerNaturalSort(m_xBuilder->weld_radio_button(u"integernaturalsortrb"_ustr))
{
    m_xLbSortUser->set_size_request(m_xLbSortUser->get_approximate_digit_width() * 50, -1);
    m_xLbSortUser->set_accessible_description(ScResId(STR_A11Y_DESC_SORTUSER));
    Init();
    SetExchangeSupport();
}

void ScTabPageSortOptions::Init()
{
    //  CollatorResource has user-visible names for sort algorithms
    m_xColRes.reset(new CollatorResource);

    //! use CollatorWrapper from document?
    m_oColWrap.emplace(comphelper::getProcessComponentContext());

    const ScSortItem& rSortItem = GetItemSet().Get( nWhichSort );

    m_xLbOutPos->connect_changed( LINK( this, ScTabPageSortOptions, SelOutPosHdl ) );
    m_xBtnCopyResult->connect_toggled( LINK( this, ScTabPageSortOptions, EnableHdl ) );
    m_xBtnSortUser->connect_toggled( LINK( this, ScTabPageSortOptions, EnableHdl ) );
    m_xBtnNaturalSort->connect_toggled( LINK( this, ScTabPageSortOptions, EnableHdl ) );
    m_xLbLanguage->connect_changed( LINK( this, ScTabPageSortOptions, FillAlgorHdl ) );

    pViewData = rSortItem.GetViewData();
    pDoc      = pViewData ? &pViewData->GetDocument() : nullptr;

    OSL_ENSURE( pViewData, "ViewData not found! :-/" );

    if ( pViewData && pDoc )
    {
        const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();

        m_xLbOutPos->clear();
        m_xLbOutPos->append_text(aStrUndefined);
        m_xLbOutPos->set_sensitive(false);

        ScAreaNameIterator aIter( *pDoc );
        OUString aName;
        ScRange aRange;
        while ( aIter.Next( aName, aRange ) )
        {
            OUString aRefStr(aRange.aStart.Format(ScRefFlags::ADDR_ABS_3D, pDoc, eConv));
            m_xLbOutPos->append(aRefStr, aName);
        }

        m_xLbOutPos->set_active(0);
        m_xEdOutPos->set_text(OUString());
    }

    m_xBtnIncComments->set_label(aStrCommentsColLabel);
    m_xBtnIncImages->set_label(aStrImgColLabel);

    FillUserSortListBox();

    //  get available languages

    m_xLbLanguage->SetLanguageList( SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false );
    m_xLbLanguage->InsertLanguage( LANGUAGE_SYSTEM );
}

std::unique_ptr<SfxTabPage> ScTabPageSortOptions::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet)
{
    return std::make_unique<ScTabPageSortOptions>(pPage, pController, *rArgSet);
}

void ScTabPageSortOptions::Reset( const SfxItemSet* /* rArgSet */ )
{
    if ( aSortData.bUserDef )
    {
        m_xBtnSortUser->set_active(true);
        m_xLbSortUser->set_sensitive(true);
        m_xLbSortUser->set_active(aSortData.nUserIndex);
    }
    else
    {
        m_xBtnSortUser->set_active(false);
        m_xLbSortUser->set_sensitive(false);
        m_xLbSortUser->set_active(0);
    }

    m_xBtnCase->set_active( aSortData.bCaseSens );
    m_xBtnFormats->set_active( aSortData.aDataAreaExtras.mbCellFormats );
    m_xBtnIncComments->set_active( aSortData.aDataAreaExtras.mbCellNotes );
    m_xBtnIncImages->set_active( aSortData.aDataAreaExtras.mbCellDrawObjects );

    LanguageType eLang = LanguageTag::convertToLanguageType( aSortData.aCollatorLocale, false);
    if ( eLang == LANGUAGE_DONTKNOW )
        eLang = LANGUAGE_SYSTEM;
    m_xLbLanguage->set_active_id(eLang);
    FillAlgor();               // get algorithms, select default
    if ( !aSortData.aCollatorAlgorithm.isEmpty() )
        m_xLbAlgorithm->set_active_text(m_xColRes->GetTranslation(aSortData.aCollatorAlgorithm));

    if ( pDoc && !aSortData.bInplace )
    {
        ScRefFlags nFormat = (aSortData.nDestTab != pViewData->CurrentTabForData())
                            ? ScRefFlags::RANGE_ABS_3D
                            : ScRefFlags::RANGE_ABS;

        theOutPos.Set( aSortData.nDestCol,
                       aSortData.nDestRow,
                       aSortData.nDestTab );

        OUString aStr(theOutPos.Format(nFormat, pDoc, pDoc->GetAddressConvention()));
        m_xBtnCopyResult->set_active(true);
        m_xLbOutPos->set_sensitive(true);
        m_xEdOutPos->set_sensitive(true);
        m_xEdOutPos->set_text( aStr );
        EdOutPosModHdl();
        m_xEdOutPos->grab_focus();
        m_xEdOutPos->select_region(0, -1);
    }
    else
    {
        m_xBtnCopyResult->set_active( false );
        m_xLbOutPos->set_sensitive(false);
        m_xEdOutPos->set_sensitive(false);
        m_xEdOutPos->set_text( OUString() );
    }

    m_xBtnNaturalSort->set_sensitive(true);
    if (aSortData.eSortNumberBehavior == ScSortNumberBehavior::ALPHA_NUMERIC)
    {
        m_xBtnNaturalSort->set_active(false);
        m_xRBDoubleNaturalSort->set_sensitive(false);
        m_xRBIntegerNaturalSort->set_sensitive(false);
    }
    else
    {
        m_xBtnNaturalSort->set_active(true);
        m_xRBDoubleNaturalSort->set_sensitive(true);
        m_xRBIntegerNaturalSort->set_sensitive(true);
        if (aSortData.eSortNumberBehavior == ScSortNumberBehavior::DOUBLE)
        {
            m_xRBDoubleNaturalSort->set_active(true);
            m_xRBIntegerNaturalSort->set_active(false);
        }
        else
        {
            m_xRBDoubleNaturalSort->set_active(false);
            m_xRBIntegerNaturalSort->set_active(true);
        }
    }
}

bool ScTabPageSortOptions::FillItemSet( SfxItemSet* rArgSet )
{
    // Create local copy of ScParam
    ScSortParam aNewSortData = aSortData;

    const SfxItemSet* pExample = GetDialogExampleSet();
    if (pExample)
    {
        if (const ScSortItem* pSortItem = pExample->GetItemIfSet(nWhichSort))
            aNewSortData = pSortItem->GetSortData();
    }
    aNewSortData.bCaseSens       = m_xBtnCase->get_active();
    aNewSortData.aDataAreaExtras.mbCellNotes = m_xBtnIncComments->get_active();
    aNewSortData.aDataAreaExtras.mbCellDrawObjects = m_xBtnIncImages->get_active();
    aNewSortData.aDataAreaExtras.mbCellFormats = m_xBtnFormats->get_active();
    aNewSortData.bInplace        = !m_xBtnCopyResult->get_active();
    aNewSortData.nDestCol        = theOutPos.Col();
    aNewSortData.nDestRow        = theOutPos.Row();
    aNewSortData.nDestTab        = theOutPos.Tab();
    aNewSortData.bUserDef        = m_xBtnSortUser->get_active();
    aNewSortData.nUserIndex      = (m_xBtnSortUser->get_active())
                                   ? m_xLbSortUser->get_active()
                                   : 0;

    // get locale
    LanguageType eLang = m_xLbLanguage->get_active_id();
    aNewSortData.aCollatorLocale = LanguageTag::convertToLocale( eLang, false);

    // get algorithm
    OUString sAlg;
    if ( eLang != LANGUAGE_SYSTEM )
    {
        uno::Sequence<OUString> aAlgos = m_oColWrap->listCollatorAlgorithms(
                aNewSortData.aCollatorLocale );
        const int nSel = m_xLbAlgorithm->get_active();
        if ( nSel < aAlgos.getLength() )
            sAlg = aAlgos[nSel];
    }
    aNewSortData.aCollatorAlgorithm = sAlg;

    if (m_xBtnNaturalSort->get_active())
        aNewSortData.eSortNumberBehavior = m_xRBDoubleNaturalSort->get_active()
            ? ScSortNumberBehavior::DOUBLE : ScSortNumberBehavior::INTEGER;
    else
        aNewSortData.eSortNumberBehavior = ScSortNumberBehavior::ALPHA_NUMERIC;

    rArgSet->Put( ScSortItem( SCITEM_SORTDATA, pViewData, &aNewSortData ) );

    return true;
}

// for data exchange without dialogue detour:
void ScTabPageSortOptions::ActivatePage( const SfxItemSet& rSet )
{
    // Refresh local copy with shared data
    aSortData = rSet.Get( SCITEM_SORTDATA ).GetSortData();
    ScSortDlg* pDlg = static_cast<ScSortDlg*>(GetDialogController());
    if (!pDlg)
        return;

    //tdf#168905 If sort by row, then include affects boundary columns.
    if (aSortData.bByRow)
    {
        m_xBtnIncComments->set_label(aStrCommentsColLabel);
        m_xBtnIncImages->set_label(aStrImgColLabel);
    }
    else
    {
        m_xBtnIncComments->set_label(aStrCommentsRowLabel);
        m_xBtnIncImages->set_label(aStrImgRowLabel);
    }
}

DeactivateRC ScTabPageSortOptions::DeactivatePage( SfxItemSet* pSetP )
{
    bool bPosInputOk = true;

    if ( m_xBtnCopyResult->get_active() )
    {
        OUString    thePosStr = m_xEdOutPos->get_text();
        ScAddress   thePos;
        sal_Int32   nColonPos = thePosStr.indexOf( ':' );

        if ( -1 != nColonPos )
            thePosStr = thePosStr.copy( 0, nColonPos );

        if ( pViewData )
        {
            //  visible table is default for input without table
            //  must be changed to GetRefTabNo when sorting has RefInput!
            thePos.SetTab( pViewData->CurrentTabForData() );
        }

        ScRefFlags nResult = thePos.Parse( thePosStr, *pDoc, pDoc->GetAddressConvention() );

        bPosInputOk = (nResult & ScRefFlags::VALID) == ScRefFlags::VALID;

        if ( !bPosInputOk )
        {
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                      VclMessageType::Warning, VclButtonsType::Ok,
                                                      ScResId(STR_INVALID_TABREF)));
            xBox->run();
            m_xEdOutPos->grab_focus();
            m_xEdOutPos->select_region(0, -1);
            theOutPos.Set(0,0,0);
        }
        else
        {
            m_xEdOutPos->set_text(thePosStr);
            theOutPos = thePos;
        }
    }

    if ( pSetP && bPosInputOk )
        FillItemSet( pSetP );

    return bPosInputOk ? DeactivateRC::LeavePage : DeactivateRC::KeepPage;
}

void ScTabPageSortOptions::FillUserSortListBox()
{
    ScUserList& rUserLists = ScGlobal::GetUserList();

    m_xLbSortUser->clear();
    size_t nCount = rUserLists.size();
    for (size_t i=0; i<nCount; ++i)
        m_xLbSortUser->append_text(rUserLists[i].GetString());
}

// Handler:

IMPL_LINK( ScTabPageSortOptions, EnableHdl, weld::Toggleable&, rButton, void )
{
    if (&rButton == m_xBtnCopyResult.get())
    {
        if (rButton.get_active())
        {
            m_xLbOutPos->set_sensitive(true);
            m_xEdOutPos->set_sensitive(true);
            m_xEdOutPos->grab_focus();
        }
        else
        {
            m_xLbOutPos->set_sensitive(false);
            m_xEdOutPos->set_sensitive(false);
        }
    }
    else if (&rButton == m_xBtnSortUser.get())
    {
        if (rButton.get_active())
        {
            m_xLbSortUser->set_sensitive(true);
            m_xLbSortUser->grab_focus();
        }
        else
            m_xLbSortUser->set_sensitive(false);
    }
    else if (&rButton == m_xBtnNaturalSort.get())
    {
        m_xRBDoubleNaturalSort->set_sensitive(rButton.get_active());
        m_xRBIntegerNaturalSort->set_sensitive(rButton.get_active());
    }
}

IMPL_LINK(ScTabPageSortOptions, SelOutPosHdl, weld::ComboBox&, rLb, void)
{
    if (&rLb == m_xLbOutPos.get())
    {
        OUString  aString;
        const int nSelPos = m_xLbOutPos->get_active();

        if (nSelPos > 0)
            aString = m_xLbOutPos->get_id(nSelPos);

        m_xEdOutPos->set_text(aString);
    }
}

void ScTabPageSortOptions::EdOutPosModHdl()
{
    OUString  theCurPosStr = m_xEdOutPos->get_text();
    ScRefFlags  nResult = ScAddress().Parse( theCurPosStr, *pDoc, pDoc->GetAddressConvention() );

    if ( (nResult & ScRefFlags::VALID) != ScRefFlags::VALID )
        return;

    bool    bFound  = false;
    sal_Int32 i = 0;
    const int nCount = m_xLbOutPos->get_count();

    for ( i=2; i<nCount && !bFound; i++ )
    {
        OUString aStr = m_xLbOutPos->get_id(i);
        bFound = (theCurPosStr == aStr);
    }

    if ( bFound )
        m_xLbOutPos->set_active(--i);
    else
        m_xLbOutPos->set_active(0);
}

void ScTabPageSortOptions::FillAlgor()
{
    tools::Long nCount = 0;

    m_xLbAlgorithm->freeze();
    m_xLbAlgorithm->clear();

    LanguageType eLang = m_xLbLanguage->get_active_id();
    if ( eLang == LANGUAGE_SYSTEM )
    {
        //  for LANGUAGE_SYSTEM no algorithm can be selected because
        //  it wouldn't necessarily exist for other languages
        //  -> leave list box empty if LANGUAGE_SYSTEM is selected
        m_xFtAlgorithm->set_sensitive( false );           // nothing to select
        m_xLbAlgorithm->set_sensitive( false );           // nothing to select
    }
    else
    {
        lang::Locale aLocale( LanguageTag::convertToLocale( eLang ));
        const uno::Sequence<OUString> aAlgos = m_oColWrap->listCollatorAlgorithms( aLocale );

        nCount = aAlgos.getLength();
        for (const OUString& sAlg : aAlgos)
        {
            OUString sUser = m_xColRes->GetTranslation( sAlg );
            m_xLbAlgorithm->append_text(sUser);
        }
    }

    m_xLbAlgorithm->thaw();

    m_xLbAlgorithm->set_active(nCount ? 0 : -1);    // first entry is default
    m_xFtAlgorithm->set_sensitive(nCount > 1);      // enable only if there is a choice
    m_xLbAlgorithm->set_sensitive(nCount > 1);      // enable only if there is a choice
}

IMPL_LINK_NOARG(ScTabPageSortOptions, FillAlgorHdl, weld::ComboBox&, void)
{
    FillAlgor();
}

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