/* -*- 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/.
 */
 
#include <config_oauth2.h>
 
#include "fpsmartcontent.hxx"
#include "QueryFolderName.hxx"
#include "RemoteFilesDialog.hxx"
#include <fpicker/fpsofficeResMgr.hxx>
#include <fpicker/strings.hrc>
#include <strings.hrc>
#include <comphelper/docpasswordrequest.hxx>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/task/PasswordContainer.hpp>
#include <svtools/PlaceEditDialog.hxx>
#include <tools/debug.hxx>
#include <unotools/ucbhelper.hxx>
#include <vcl/errinf.hxx>
#include <officecfg/Office/Common.hxx>
 
using namespace ::svt;
 
RemoteFilesDialog::RemoteFilesDialog( weld::Window* pParent, PickerFlags nBits )
    : SvtFileDialog_Base( pParent, u"fps/ui/remotefilesdialog.ui"_ustr, u"RemoteFilesDialog"_ustr )
    , m_xContext( comphelper::getProcessComponentContext() )
    , m_xMasterPasswd( PasswordContainer::create( m_xContext ) )
    , m_bIsInExecute( false )
    , m_xOk_btn(m_xBuilder->weld_button(u"ok"_ustr))
    , m_xCancel_btn(m_xBuilder->weld_button(u"cancel"_ustr))
    , m_xManageServices(m_xBuilder->weld_menu_button(u"add_service_btn"_ustr))
    , m_xServices_lb(m_xBuilder->weld_combo_box(u"services_lb"_ustr))
    , m_xPathContainer(m_xBuilder->weld_container(u"breadcrumb_container"_ustr))
    , m_xNewFolder(m_xBuilder->weld_button(u"new_folder"_ustr))
    , m_xListView_btn(m_xBuilder->weld_toggle_button(u"list_view"_ustr))
    , m_xIconView_btn(m_xBuilder->weld_toggle_button(u"icon_view"_ustr))
    , m_xFilter_lb(m_xBuilder->weld_combo_box(u"filter_lb"_ustr))
    , m_xName_ed(new AutocompleteEdit(m_xBuilder->weld_entry(u"filename"_ustr)))
{
    m_eMode = ( nBits & PickerFlags::SaveAs ) ? REMOTEDLG_MODE_SAVE : REMOTEDLG_MODE_OPEN;
    m_eType = ( nBits & PickerFlags::PathDialog ) ? REMOTEDLG_TYPE_PATHDLG : REMOTEDLG_TYPE_FILEDLG;
    bool bMultiselection = bool( nBits & PickerFlags::MultiSelection );
    m_bIsUpdated = false;
    m_bIsConnected = false;
    m_bServiceChanged = false;
    m_nCurrentFilter = -1;
 
    m_xName_ed->show();
 
    // limit width due to super wide strings that may end up here
    m_xFilter_lb->set_size_request(m_xFilter_lb->get_approximate_digit_width() * 60, -1);
 
    m_xFilter_lb->set_sensitive(false);
    m_xName_ed->set_sensitive(false);
    m_xNewFolder->set_sensitive(false);
 
    if( m_eMode == REMOTEDLG_MODE_OPEN )
    {
        m_xNewFolder->hide();
    }
    else
    {
        m_xOk_btn->set_label(FpsResId(STR_EXPLORERFILE_BUTTONSAVE));
        m_xNewFolder->connect_clicked( LINK( this, RemoteFilesDialog, NewFolderHdl ) );
    }
 
    m_xListView_btn->set_active(true);
    m_xIconView_btn->connect_clicked( LINK( this, RemoteFilesDialog, IconViewHdl ) );
    m_xListView_btn->connect_clicked( LINK( this, RemoteFilesDialog, ListViewHdl ) );
 
    m_xOk_btn->set_sensitive(false);
 
    m_xOk_btn->connect_clicked( LINK( this, RemoteFilesDialog, OkHdl ) );
    m_xCancel_btn->connect_clicked( LINK( this, RemoteFilesDialog, CancelHdl ) );
 
    m_sRootLabel = FpsResId( STR_SVT_ROOTLABEL );
    m_xPath.reset(new Breadcrumb(m_xPathContainer.get()));
    m_xPath->connect_clicked( LINK( this, RemoteFilesDialog, SelectBreadcrumbHdl ) );
    m_xPath->SetMode( SvtBreadcrumbMode::ALL_VISITED );
 
    m_xContainer = m_xBuilder->weld_container(u"container"_ustr);
    m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * 82, -1);
 
    m_xFileView.reset(new SvtFileView(m_xDialog.get(),
                                      m_xBuilder->weld_tree_view(u"fileview"_ustr),
                                      m_xBuilder->weld_icon_view(u"iconview"_ustr),
                                      REMOTEDLG_TYPE_PATHDLG == m_eType,
                                      bMultiselection, false));
 
    m_xFileView->SetDoubleClickHdl( LINK( this, RemoteFilesDialog, DoubleClickHdl ) );
    m_xFileView->SetSelectHdl( LINK( this, RemoteFilesDialog, SelectHdl ) );
    m_xFileView->EnableDelete( true );
 
    m_xTreeView.reset(new FolderTree(m_xBuilder->weld_tree_view(u"foldertree"_ustr), m_xDialog.get()));
    m_xTreeView->connect_changed(LINK(this, RemoteFilesDialog, TreeSelectHdl));
 
    m_xContainer->set_sensitive(false);
 
    m_sIniKey = "RemoteFilesDialog";
    InitSize();
 
    m_xName_ed->connect_focus_in(LINK(this, RemoteFilesDialog, FileNameGetFocusHdl));
    m_xName_ed->connect_changed(LINK(this, RemoteFilesDialog, FileNameModifyHdl));
 
    m_xManageServices->connect_selected(LINK(this, RemoteFilesDialog, EditServiceMenuHdl));
 
    FillServicesListbox();
 
    m_xServices_lb->connect_changed( LINK( this, RemoteFilesDialog, SelectServiceHdl ) );
 
    m_xFilter_lb->connect_changed( LINK( this, RemoteFilesDialog, SelectFilterHdl ) );
}
 
RemoteFilesDialog::~RemoteFilesDialog()
{
    m_xFileView->SetSelectHdl(Link<SvtFileView*,void>());
 
    // save window state
    if( !m_sIniKey.isEmpty() )
    {
        SvtViewOptions aDlgOpt( EViewType::Dialog, m_sIniKey );
        aDlgOpt.SetWindowState(m_xDialog->get_window_state(vcl::WindowDataMask::All));
 
        Size aSize(m_xDialog->get_size());
 
        OUString sSize = OUString::number( aSize.Width() ) + "|";
        sSize = sSize + OUString::number( aSize.Height() ) + "|";
 
        OUString sUserData = m_xFileView->GetConfigString();
        aDlgOpt.SetUserItem( u"UserData"_ustr,
                             Any( sSize + sUserData ) );
    }
 
    // save services
    std::shared_ptr< comphelper::ConfigurationChanges > batch( comphelper::ConfigurationChanges::create() );
 
    officecfg::Office::Common::Misc::FilePickerLastService::set( m_sLastServiceUrl, batch );
 
    if( m_bIsUpdated )
    {
        Sequence< OUString > placesUrlsList( m_aServices.size() );
        auto placesUrlsListRange = asNonConstRange(placesUrlsList);
        Sequence< OUString > placesNamesList( m_aServices.size() );
        auto placesNamesListRange = asNonConstRange(placesNamesList);
 
        int i = 0;
        for (auto const& service : m_aServices)
        {
            placesUrlsListRange[i] = service->GetUrl();
            placesNamesListRange[i] = service->GetName();
            ++i;
        }
 
        officecfg::Office::Common::Misc::FilePickerPlacesUrls::set( placesUrlsList, batch );
        officecfg::Office::Common::Misc::FilePickerPlacesNames::set( placesNamesList, batch );
    }
 
    batch->commit();
}
 
void RemoteFilesDialog::EnableExtraMenuItems(bool bEnable)
{
    m_xManageServices->set_item_visible(u"change_password"_ustr, bEnable);
    m_xManageServices->set_item_visible(u"edit_service"_ustr, bEnable);
    m_xManageServices->set_item_visible(u"delete_service"_ustr, bEnable);
    m_xManageServices->set_item_visible(u"change_password"_ustr, bEnable);
}
 
short RemoteFilesDialog::run()
{
    if (m_xServices_lb->get_count() > 0)
    {
        m_xDialog->show();
        SelectServiceHdl(*m_xServices_lb);
    }
    if (!m_bIsConnected)
    {
        m_xServices_lb->set_active(-1);
        EnableExtraMenuItems(false);
    }
 
    m_bIsInExecute = true;
    short nRet = SvtFileDialog_Base::run();
    m_bIsInExecute = false;
    return nRet;
}
 
static OUString lcl_GetServiceType( const ServicePtr& pService )
{
    INetProtocol aProtocol = pService->GetUrlObject().GetProtocol();
    switch( aProtocol )
    {
        case INetProtocol::Cmis:
        {
            OUString sHost = pService->GetUrlObject().GetHost( INetURLObject::DecodeMechanism::WithCharset );
 
            if( sHost.startsWith( GDRIVE_BASE_URL ) )
                return u"Google Drive"_ustr;
            else if( sHost.startsWith( ALFRESCO_CLOUD_BASE_URL ) )
                return u"Alfresco Cloud"_ustr;
            else if( sHost.startsWith( ONEDRIVE_BASE_URL ) )
                return u"OneDrive"_ustr;
 
            return u"CMIS"_ustr;
        }
        case INetProtocol::Smb:
            return u"Windows Share"_ustr;
        case INetProtocol::File:
            return u"SSH"_ustr;
        case INetProtocol::Http:
            return u"WebDAV"_ustr;
        case INetProtocol::Https:
            return u"WebDAV"_ustr;
        case INetProtocol::Generic:
            return u"SSH"_ustr;
        default:
            return OUString();
    }
}
 
void RemoteFilesDialog::InitSize()
{
    if( m_sIniKey.isEmpty() )
        return;
 
    // initialize from config
    SvtViewOptions aDlgOpt( EViewType::Dialog, m_sIniKey );
 
    if( !aDlgOpt.Exists() )
        return;
 
    m_xDialog->set_window_state(aDlgOpt.GetWindowState());
 
    Any aUserData = aDlgOpt.GetUserItem( u"UserData"_ustr );
    OUString sCfgStr;
    if( aUserData >>= sCfgStr )
    {
        sal_Int32 nPos1{ sCfgStr.indexOf('|') };
        if (nPos1<0)
            return;
        sal_Int32 nPos2{ sCfgStr.indexOf('|', nPos1+1 ) };
        if (nPos2<0)
            return;
        m_xFileView->SetConfigString( sCfgStr.subView(nPos2+1) );
    }
}
 
void RemoteFilesDialog::FillServicesListbox()
{
    m_xServices_lb->clear();
    m_aServices.clear();
 
    // Load from user settings
    Sequence< OUString > placesUrlsList( officecfg::Office::Common::Misc::FilePickerPlacesUrls::get() );
    Sequence< OUString > placesNamesList( officecfg::Office::Common::Misc::FilePickerPlacesNames::get() );
 
    unsigned int nPos = 0;
    unsigned int i = 0;
 
    m_sLastServiceUrl = officecfg::Office::Common::Misc::FilePickerLastService::get();
 
    for( sal_Int32 nPlace = 0; nPlace < placesUrlsList.getLength() && nPlace < placesNamesList.getLength(); ++nPlace )
    {
        ServicePtr pService = std::make_shared<Place>( placesNamesList[nPlace], placesUrlsList[nPlace], true );
        m_aServices.push_back( pService );
 
        // Add to the listbox only remote services, not local bookmarks
        if( !pService->IsLocal() )
        {
            OUString sPrefix = lcl_GetServiceType( pService );
 
            if( !sPrefix.isEmpty() )
                 sPrefix += ": ";
 
            if( placesUrlsList[nPlace] == m_sLastServiceUrl )
                nPos = i;
 
            m_xServices_lb->append_text(sPrefix + placesNamesList[nPlace]);
 
            i++;
        }
    }
 
    if (m_xServices_lb->get_count() > 0)
    {
        m_xServices_lb->set_active(nPos);
        EnableExtraMenuItems(true);
    }
    else
        EnableExtraMenuItems(false);
 
    EnableControls();
}
 
int RemoteFilesDialog::GetSelectedServicePos()
{
    if( m_aServices.empty() )
        return -1;
 
    int nPos = 0;
    int i = -1;
 
    int nSelected = m_xServices_lb->get_active();
 
    int nServices = static_cast<int>(m_aServices.size());
    while( nPos < nServices )
    {
        while( (nPos < nServices) && m_aServices[nPos]->IsLocal() )
            nPos++;
        i++;
        if( i == nSelected )
            break;
        nPos++;
    }
 
    return nPos;
}
 
void RemoteFilesDialog::AddFilter( const OUString& rFilter, const OUString& rType )
{
    m_aFilters.emplace_back( rFilter, rType );
    if (rType.isEmpty())
        m_xFilter_lb->append_separator(u""_ustr);
    else
        m_xFilter_lb->append_text(rFilter);
 
    if (m_xFilter_lb->get_active() == -1)
        m_xFilter_lb->set_active(0);
}
 
void RemoteFilesDialog::OpenURL( OUString const & sURL )
{
    if( !m_xFileView )
        return;
 
    DisableControls();
 
    auto xWait = std::make_unique<weld::WaitObject>(m_xDialog.get());
 
    if( !sURL.isEmpty() )
    {
        OUString sFilter = FILEDIALOG_FILTER_ALL;
 
        if( m_nCurrentFilter != -1)
        {
            sFilter = m_aFilters[m_nCurrentFilter].second;
        }
 
        m_xFileView->EndInplaceEditing();
 
        DBG_ASSERT( !m_pCurrentAsyncAction.is(), "SvtFileDialog::executeAsync: previous async action not yet finished!" );
 
        m_pCurrentAsyncAction = new AsyncPickerAction( this, m_xFileView.get(), AsyncPickerAction::Action::eOpenURL );
 
        // -1 timeout - sync
        m_pCurrentAsyncAction->execute( sURL, sFilter, -1, -1, GetDenyList() );
 
        if( m_eMode != REMOTEDLG_MODE_SAVE )
            m_xName_ed->set_text( u""_ustr );
 
        m_xFileView->grab_focus();
    }
    else
    {
        xWait.reset();
 
        // content doesn't exist
        ErrorHandler::HandleError( ERRCODE_IO_NOTEXISTS );
 
        EnableControls();
    }
}
 
OUString RemoteFilesDialog::AddFileExtension(const OUString& rFileName)
{
    if (m_nCurrentFilter == -1)
        return rFileName;
 
    OUString sExt = m_aFilters[m_nCurrentFilter].second;
    sal_Int32 nDotPos = rFileName.lastIndexOf( '.' );
 
    if (nDotPos == -1)
        return rFileName + sExt.subView( 1 ); // without '*'
 
    return rFileName;
}
 
void RemoteFilesDialog::EnableControls()
{
    if (m_xServices_lb->get_count() > 0)
    {
        m_xServices_lb->set_sensitive(true);
 
        if (m_xServices_lb->get_active() != -1)
        {
            m_xManageServices->set_item_sensitive(u"change_password"_ustr, false);
 
            try
            {
                if( m_xMasterPasswd->isPersistentStoringAllowed() )
                {
                    int nPos = GetSelectedServicePos();
 
                    if( nPos >= 0 )
                    {
                        OUString sUrl( m_aServices[nPos]->GetUrl() );
 
                        UrlRecord aURLEntries = m_xMasterPasswd->find( sUrl, Reference< XInteractionHandler>() );
 
                        if( aURLEntries.UserList.hasElements() )
                        {
                            m_xManageServices->set_item_sensitive(u"change_password"_ustr, true);
                        }
                    }
                }
            }
            catch( const Exception& )
            {}
        }
    }
    else
        m_xServices_lb->set_sensitive(false);
 
    if( m_bIsConnected )
    {
        m_xFilter_lb->set_sensitive(true);
        m_xName_ed->set_sensitive(true);
        m_xContainer->set_sensitive(true);
        m_xNewFolder->set_sensitive(true);
 
        if (!m_xName_ed->get_text().isEmpty())
            m_xOk_btn->set_sensitive(true);
        else
            m_xOk_btn->set_sensitive(false);
    }
    else
    {
        m_xFilter_lb->set_sensitive(false);
        m_xName_ed->set_sensitive(false);
        m_xContainer->set_sensitive(false);
        m_xNewFolder->set_sensitive(false);
        m_xOk_btn->set_sensitive(false);
    }
 
    m_xPath->EnableFields( true );
    m_xManageServices->set_sensitive(true);
}
 
void RemoteFilesDialog::DisableControls()
{
    m_xServices_lb->set_sensitive(false);
    m_xFilter_lb->set_sensitive(false);
    m_xManageServices->set_sensitive(false);
    m_xName_ed->set_sensitive(false);
    m_xContainer->set_sensitive(false);
    m_xOk_btn->set_sensitive(false);
    m_xPath->EnableFields( false );
 
    m_xCancel_btn->set_sensitive(true);
}
 
void RemoteFilesDialog::SavePassword(const OUString& rURL, const OUString& rUser,
                                     const OUString& rPassword, bool bPersistent)
{
    if( rURL.isEmpty() || rUser.isEmpty() || rPassword.isEmpty() )
        return;
 
    try
    {
        if( !bPersistent ||
            ( m_xMasterPasswd->isPersistentStoringAllowed()
            && m_xMasterPasswd->authorizateWithMasterPassword( Reference< XInteractionHandler>() ) )
        )
        {
            Reference< XInteractionHandler > xInteractionHandler =
                InteractionHandler::createWithParent( m_xContext, nullptr );
 
            Sequence<OUString> aPasswd { rPassword };
 
            if( bPersistent )
                m_xMasterPasswd->addPersistent(
                    rURL, rUser, aPasswd, xInteractionHandler );
            else
                m_xMasterPasswd->add( rURL, rUser, aPasswd, xInteractionHandler );
        }
    }
    catch( const Exception& )
    {}
}
 
IMPL_LINK_NOARG ( RemoteFilesDialog, IconViewHdl, weld::Button&, void )
{
    m_xListView_btn->set_active(false);
    m_xFileView->SetViewMode( eIcon );
}
 
IMPL_LINK_NOARG ( RemoteFilesDialog, ListViewHdl, weld::Button&, void )
{
    m_xIconView_btn->set_active(false);
    m_xFileView->SetViewMode( eDetailedList );
}
 
void RemoteFilesDialog::AddService()
{
    PlaceEditDialog aDlg(m_xDialog.get());
    aDlg.ShowPasswordControl();
    short aRetCode = aDlg.run();
 
    switch( aRetCode )
    {
        case RET_OK :
        {
            ServicePtr newService = aDlg.GetPlace();
            m_aServices.push_back( newService );
 
            OUString sPassword = aDlg.GetPassword();
            OUString sUser = aDlg.GetUser();
            if( !sUser.isEmpty() && !sPassword.isEmpty() )
            {
                bool bPersistent = aDlg.IsRememberChecked();
                SavePassword( newService->GetUrl(), sUser, sPassword, bPersistent );
            }
 
            OUString sPrefix = lcl_GetServiceType( newService );
 
            if(!sPrefix.isEmpty())
                 sPrefix += ": ";
 
            m_xServices_lb->append_text( sPrefix + newService->GetName() );
            m_xServices_lb->set_active( m_xServices_lb->get_count() - 1 );
            EnableExtraMenuItems(true);
            SelectServiceHdl( *m_xServices_lb );
 
            m_bIsUpdated = true;
 
            EnableControls();
            break;
        }
        case RET_CANCEL :
        default :
            // Do Nothing
            break;
    }
}
 
IMPL_LINK_NOARG( RemoteFilesDialog, SelectServiceHdl, weld::ComboBox&, void )
{
    int nPos = GetSelectedServicePos();
 
    if( nPos >= 0 )
    {
        OUString sURL = m_aServices[nPos]->GetUrl();
        EnableExtraMenuItems(true);
 
        m_bServiceChanged = true;
        OpenURL( sURL );
    }
}
 
IMPL_LINK ( RemoteFilesDialog, EditServiceMenuHdl, const OUString&, rIdent, void )
{
    OUString sIdent(rIdent);
    if( sIdent == "edit_service"  && m_xServices_lb->get_count() > 0 )
    {
        int nSelected = m_xServices_lb->get_active();
        int nPos = GetSelectedServicePos();
 
        if( nPos >= 0 )
        {
            PlaceEditDialog aDlg(m_xDialog.get(), m_aServices[nPos]);
            short aRetCode = aDlg.run();
 
            switch( aRetCode )
            {
                case RET_OK :
                {
                    ServicePtr pEditedService = aDlg.GetPlace();
 
                    m_aServices[nPos] = pEditedService;
                    m_xServices_lb->remove( nSelected );
 
                    OUString sPrefix = lcl_GetServiceType( pEditedService );
 
                    if(!sPrefix.isEmpty())
                        sPrefix += ": ";
 
                    m_xServices_lb->insert_text(nSelected, sPrefix + pEditedService->GetName());
                    m_xServices_lb->set_active( nSelected );
 
                    m_bIsUpdated = true;
                    break;
                }
                case RET_NO:
                    sIdent = "delete_service";
                    break;
                case RET_CANCEL :
                default :
                    // Do Nothing
                    break;
            }
        }
    }
    if( sIdent == "delete_service"  && m_xServices_lb->get_count() > 0 )
    {
        int nSelected = m_xServices_lb->get_active();
        int nPos = GetSelectedServicePos();
 
        if( nPos >= 0 )
        {
            OUString sMsg = FpsResId( STR_SVT_DELETESERVICE );
            sMsg = sMsg.replaceFirst( "$servicename$", m_xServices_lb->get_active_text() );
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
                                                      VclMessageType::Question, VclButtonsType::YesNo, sMsg));
            if (xBox->run() == RET_YES)
            {
                // remove password
                try
                {
                    if( m_xMasterPasswd->isPersistentStoringAllowed() )
                    {
                        OUString sUrl( m_aServices[nPos]->GetUrl() );
 
                        Reference< XInteractionHandler > xInteractionHandler =
                            InteractionHandler::createWithParent( m_xContext, nullptr );
 
                        UrlRecord aURLEntries = m_xMasterPasswd->find( sUrl, xInteractionHandler );
 
                        if( aURLEntries.Url == sUrl && aURLEntries.UserList.hasElements() )
                        {
                            OUString sUserName = aURLEntries.UserList[0].UserName;
 
                            m_xMasterPasswd->removePersistent( sUrl, sUserName );
                        }
                    }
                }
                catch( const Exception& )
                {}
 
                m_aServices.erase( m_aServices.begin() + nPos );
                m_xServices_lb->remove( nSelected );
 
                m_xServices_lb->set_active(-1);
                EnableExtraMenuItems(false);
 
                m_bIsUpdated = true;
 
                m_bIsConnected = false;
                EnableControls();
            }
        }
    }
    else if( sIdent == "change_password" )
    {
        try
        {
            if( m_xMasterPasswd->isPersistentStoringAllowed() && m_xMasterPasswd->authorizateWithMasterPassword( Reference< XInteractionHandler>() ) )
            {
                int nPos = GetSelectedServicePos();
 
                if( nPos >= 0 )
                {
                    OUString sUrl( m_aServices[nPos]->GetUrl() );
 
                    Reference< XInteractionHandler > xInteractionHandler =
                        InteractionHandler::createWithParent( m_xContext, nullptr );
 
                    UrlRecord aURLEntries = m_xMasterPasswd->find( sUrl, xInteractionHandler );
 
                    if( aURLEntries.Url == sUrl && aURLEntries.UserList.hasElements() )
                    {
                        OUString sUserName = aURLEntries.UserList[0].UserName;
 
                        rtl::Reference<::comphelper::SimplePasswordRequest> pPasswordRequest
                            = new ::comphelper::SimplePasswordRequest;
 
                        xInteractionHandler->handle( pPasswordRequest );
 
                        if ( pPasswordRequest->isPassword() )
                        {
                            Sequence<OUString> aPasswd { pPasswordRequest->getPassword() };
 
                            m_xMasterPasswd->addPersistent(
                                sUrl, sUserName, aPasswd, xInteractionHandler );
                        }
                    }
                }
            }
        }
        catch( const Exception& )
        {}
    }
    else if( sIdent == "add_service" )
        AddService();
 
    EnableControls();
}
 
IMPL_LINK_NOARG( RemoteFilesDialog, DoubleClickHdl, SvtFileView*, bool )
{
    SvtContentEntry* pData = m_xFileView->FirstSelected();
    if (pData)
    {
        if (!pData->mbIsFolder)
            OkHdl(*m_xOk_btn);
        else
            OpenURL(pData->maURL);
    }
    return true;
}
 
IMPL_LINK_NOARG( RemoteFilesDialog, SelectHdl, SvtFileView*, void )
{
    SvtContentEntry* pData = m_xFileView->FirstSelected();
    if (!pData)
        return;
 
    if( ( pData->mbIsFolder && ( m_eType == REMOTEDLG_TYPE_PATHDLG ) )
        || ( !pData->mbIsFolder && ( m_eType == REMOTEDLG_TYPE_FILEDLG ) ) )
    {
        // url must contain user info, because we need this info in recent files entry
        // (to fill user field in login box by default)
        INetURLObject aURL( pData->maURL );
        INetURLObject aCurrentURL( m_sLastServiceUrl );
        aURL.SetUser( aCurrentURL.GetUser() );
 
        m_sPath = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
 
        m_xName_ed->set_text( aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset) );
    }
    else
    {
        if( m_eMode == REMOTEDLG_MODE_OPEN )
        {
            m_sPath.clear();
            m_xName_ed->set_text( u""_ustr );
        }
    }
 
    EnableControls();
}
 
IMPL_LINK_NOARG(RemoteFilesDialog, FileNameGetFocusHdl, weld::Widget&, void)
{
    m_xFileView->SetNoSelection();
}
 
IMPL_LINK_NOARG(RemoteFilesDialog, FileNameModifyHdl, weld::Entry&, void)
{
    m_xFileView->SetNoSelection();
    if (!m_xOk_btn->get_sensitive())
        EnableControls();
}
 
IMPL_LINK_NOARG( RemoteFilesDialog, SelectFilterHdl, weld::ComboBox&, void )
{
    int nPos = m_xFilter_lb->get_active();
 
    if( nPos != -1 && !m_aFilters[nPos].second.isEmpty() )
    {
        m_nCurrentFilter = nPos;
 
        OUString sCurrentURL = m_xFileView->GetViewURL();
 
        if( !sCurrentURL.isEmpty() && m_bIsConnected )
            OpenURL( sCurrentURL );
    }
}
 
IMPL_LINK(RemoteFilesDialog, TreeSelectHdl, weld::TreeView&, rBox, void)
{
    OpenURL(rBox.get_selected_id());
    m_xFileView->grab_focus();
}
 
IMPL_LINK(RemoteFilesDialog, SelectBreadcrumbHdl, Breadcrumb*, pPtr, bool)
{
    OpenURL( pPtr->GetHdlURL() );
    return true;
}
 
IMPL_LINK_NOARG ( RemoteFilesDialog, NewFolderHdl, weld::Button&, void )
{
    m_xFileView->EndInplaceEditing();
 
    // will be bound after InteractionHandler is enabled
    SmartContent aContent;
    aContent.enableDefaultInteractionHandler();
    // now it can be bound
    aContent.bindTo( m_xFileView->GetViewURL() );
    if( !aContent.canCreateFolder() )
        return;
 
    OUString aTitle;
    aContent.getTitle( aTitle );
    QueryFolderNameDialog aDlg(m_xDialog.get(), aTitle, FpsResId(STR_SVT_NEW_FOLDER));
    bool bHandled = false;
 
    while( !bHandled )
    {
        if (aDlg.run() == RET_OK)
        {
            OUString aUrl = aContent.createFolder(aDlg.GetName());
            if( !aUrl.isEmpty() )
            {
                m_xFileView->CreatedFolder(aUrl, aDlg.GetName());
                bHandled = true;
            }
        }
        else
            bHandled = true;
    }
}
 
IMPL_LINK_NOARG ( RemoteFilesDialog, OkHdl, weld::Button&, void )
{
    OUString sUserSelectedPath;
 
    // check if file/path exists
    OUString sCurrentPath = m_xFileView->GetViewURL();
    OUString sSelectedItem = m_xFileView->GetCurrentURL();
    OUString sUserTypedName = m_xName_ed->get_text();
    OUString sFileName;
    // auto extension
    if( m_eMode == REMOTEDLG_MODE_SAVE )
        sFileName = AddFileExtension(sUserTypedName);
    else
        sFileName = sUserTypedName;
 
    bool bFileDlg = ( m_eType == REMOTEDLG_TYPE_FILEDLG );
    bool bSelected = ( m_xFileView->GetSelectionCount() > 0 );
 
    if( !sCurrentPath.endsWith("/") )
        sCurrentPath += "/";
 
    if( !bSelected )
    {
        m_sPath = sCurrentPath + INetURLObject::encode(sFileName, INetURLObject::PART_FPATH, INetURLObject::EncodeMechanism::All);
        sUserSelectedPath = sCurrentPath + INetURLObject::encode(sUserTypedName, INetURLObject::PART_FPATH, INetURLObject::EncodeMechanism::All);
    }
    else
    {
        if( m_eType == REMOTEDLG_TYPE_PATHDLG )
            m_sPath = sCurrentPath;
        else
            m_sPath = sSelectedItem;
 
        // url must contain user info, because we need this info in recent files entry
        // (to fill user field in login box by default)
        INetURLObject aURL( m_sPath );
        INetURLObject aCurrentURL( m_sLastServiceUrl );
        aURL.SetUser( aCurrentURL.GetUser() );
 
        m_sPath = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
        sUserSelectedPath = m_sPath;
    }
 
    bool bExists = false;
 
    if( bFileDlg )
        bExists = ContentIsDocument( m_sPath );
    else
        bExists = ContentIsFolder( m_sPath );
 
    if( bExists )
    {
        if( m_eMode == REMOTEDLG_MODE_SAVE )
        {
            OUString sMsg = FpsResId( STR_SVT_ALREADYEXISTOVERWRITE );
            sMsg = sMsg.replaceFirst("$filename$", sFileName);
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
                                                      VclMessageType::Question, VclButtonsType::YesNo, sMsg));
            if (xBox->run() != RET_YES)
                return;
        }
    }
    else
    {
        if (ContentIsFolder(sUserSelectedPath))
        {
            OpenURL(sUserSelectedPath);
 
            if (!bSelected)
                m_xName_ed->grab_focus();
 
            return;
        }
 
        if( m_eMode == REMOTEDLG_MODE_OPEN )
            return;
    }
 
    m_xDialog->response(RET_OK);
}
 
IMPL_LINK_NOARG ( RemoteFilesDialog, CancelHdl, weld::Button&, void )
{
    if( m_pCurrentAsyncAction.is() )
    {
        m_pCurrentAsyncAction->cancel();
        onAsyncOperationFinished();
    }
    else
    {
        m_xDialog->response(RET_CANCEL);
    }
}
 
// SvtFileDialog_Base
SvtFileView* RemoteFilesDialog::GetView()
{
    return m_xFileView.get();
}
 
void RemoteFilesDialog::SetHasFilename( bool )
{
}
 
void RemoteFilesDialog::SetDenyList( const css::uno::Sequence< OUString >& rDenyList )
{
    m_aDenyList = rDenyList;
    m_xTreeView->SetDenyList( rDenyList );
}
 
const css::uno::Sequence< OUString >& RemoteFilesDialog::GetDenyList() const
{
    return m_aDenyList;
}
 
void RemoteFilesDialog::SetStandardDir( const OUString& rStdDir )
{
    m_sStdDir = rStdDir;
}
 
const OUString& RemoteFilesDialog::GetStandardDir() const
{
    return m_sStdDir;
}
 
void RemoteFilesDialog::SetPath( const OUString& rNewURL )
{
    m_sPath = rNewURL;
 
    if( m_eMode == REMOTEDLG_MODE_SAVE )
    {
        INetURLObject aUrl( m_sPath );
        OUString sFileName = aUrl.GetLastName( INetURLObject::DecodeMechanism::WithCharset );
 
        m_xName_ed->set_text( sFileName );
    }
}
 
OUString RemoteFilesDialog::getCurrentFileText() const
{
    OUString sReturn;
    if( m_xName_ed )
        sReturn = m_xName_ed->get_text();
    return sReturn;
}
 
void RemoteFilesDialog::setCurrentFileText( const OUString& rText, bool bSelectAll )
{
    if (m_xName_ed)
    {
        m_xName_ed->set_text(rText);
        if( bSelectAll )
            m_xName_ed->select_region(0, -1);
    }
}
 
void RemoteFilesDialog::AddFilterGroup(
                                  const OUString& rFilter,
                                  const css::uno::Sequence< css::beans::StringPair >& rFilters )
{
    AddFilter( rFilter, OUString() );
    const StringPair* pSubFilters       =               rFilters.getConstArray();
    const StringPair* pSubFiltersEnd    = pSubFilters + rFilters.getLength();
    for ( ; pSubFilters != pSubFiltersEnd; ++pSubFilters )
        AddFilter( pSubFilters->First, pSubFilters->Second );
}
 
OUString RemoteFilesDialog::GetCurFilter() const
{
    OUString sFilter;
 
    if (m_nCurrentFilter != -1)
    {
        sFilter = m_aFilters[m_nCurrentFilter].first;
    }
 
    return sFilter;
}
 
OUString RemoteFilesDialog::getCurFilter( ) const
{
    return GetCurFilter();
}
 
void RemoteFilesDialog::SetCurFilter( const OUString& rFilter )
{
    DBG_ASSERT( !m_bIsInExecute, "SvtFileDialog::SetCurFilter: currently executing!" );
 
    // look for corresponding filter
    sal_uInt16 nPos = m_aFilters.size();
 
    while ( nPos-- )
    {
        if ( m_aFilters[nPos].first == rFilter )
        {
            m_nCurrentFilter = nPos;
            m_xFilter_lb->set_active( m_nCurrentFilter );
            break;
        }
    }
}
 
void RemoteFilesDialog::FilterSelect()
{
}
 
void RemoteFilesDialog::SetFileCallback( ::svt::IFilePickerListener * )
{
}
 
void RemoteFilesDialog::onAsyncOperationStarted()
{
    DisableControls();
}
 
void RemoteFilesDialog::onAsyncOperationFinished()
{
    m_pCurrentAsyncAction = nullptr;
    EnableControls();
}
 
void RemoteFilesDialog::UpdateControls( const OUString& rURL )
{
    int nPos = GetSelectedServicePos();
 
    if( nPos >= 0 && m_bServiceChanged && rURL == m_aServices[nPos]->GetUrl() )
    {
        OUString sURL = m_aServices[nPos]->GetUrl();
 
        m_xPath->SetRootName( m_sRootLabel );
        m_xTreeView->clear();
 
        m_xTreeView->InsertRootEntry(rURL, m_sRootLabel);
 
        m_xName_ed->grab_focus();
 
        m_sLastServiceUrl = sURL;
 
        m_bServiceChanged = false;
    }
 
    m_xPath->SetURL( rURL );
 
    m_xTreeView->connect_changed(Link<weld::TreeView&,void>());
 
    // read cached data for this url and fill the tree
    const ::std::vector< SvtContentEntry > aContentFolders = m_xFileView->GetContent();
    ::std::vector< std::pair< OUString, OUString > > aFolders;
 
    m_xName_ed->ClearEntries();
 
    for(const auto & rFolder : aContentFolders)
    {
        //WebDAV folders path ends in '/', so strip it
        OUString aFolderName = rFolder.maURL;
        if( rFolder.mbIsFolder && ( ( aFolderName.lastIndexOf( '/' ) + 1 ) == aFolderName.getLength() ) )
            aFolderName = aFolderName.copy( 0, aFolderName.getLength() - 1 );
 
        int nTitleStart = aFolderName.lastIndexOf( '/' );
        if( nTitleStart != -1 )
        {
            OUString sTitle( INetURLObject::decode(
                                aFolderName.subView( nTitleStart + 1 ),
                                INetURLObject::DecodeMechanism::WithCharset ) );
 
            if( rFolder.mbIsFolder )
            {
                aFolders.emplace_back( sTitle, aFolderName );
            }
 
            // add entries to the autocompletion mechanism
            m_xName_ed->AddEntry( sTitle );
        }
    }
 
    m_xTreeView->FillTreeEntry( rURL, aFolders );
 
    m_xTreeView->connect_changed( LINK( this, RemoteFilesDialog, TreeSelectHdl ) );
 
    m_bIsConnected = true;
    EnableControls();
}
 
void RemoteFilesDialog::EnableAutocompletion( bool )
{
    // This dialog contains Breadcrumb, not Edit
}
 
const OUString& RemoteFilesDialog::GetPath()
{
    return m_sPath;
}
 
std::vector<OUString> RemoteFilesDialog::GetPathList() const
{
    std::vector<OUString> aList;
 
    m_xFileView->selected_foreach([this, &aList](weld::TreeIter& rCurEntry){
        // url must contain user info, because we need this info in recent files entry
        // (to fill user field in login box by default)
        INetURLObject aURL(m_xFileView->GetURL(rCurEntry));
        INetURLObject aCurrentURL( m_sLastServiceUrl );
        aURL.SetUser( aCurrentURL.GetUser() );
 
        aList.push_back( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
 
        return false;
    });
 
    if( aList.empty() && !m_sPath.isEmpty() )
        aList.push_back( m_sPath );
 
    return aList;
}
 
bool RemoteFilesDialog::ContentIsFolder( const OUString& rURL )
{
    try
    {
        ::ucbhelper::Content content(rURL,
            ::utl::UCBContentHelper::getDefaultCommandEnvironment(),
            m_xContext);
        return content.isFolder();
    }
    catch (css::uno::Exception const&)
    {
        return false;
    }
}
 
bool RemoteFilesDialog::ContentIsDocument( const OUString& rURL )
{
    try
    {
        ::ucbhelper::Content content(rURL,
            ::utl::UCBContentHelper::getDefaultCommandEnvironment(),
            m_xContext);
        return content.isDocument();
    }
    catch (css::uno::Exception const&)
    {
        return false;
    }
}
 
sal_Int32 RemoteFilesDialog::getAvailableWidth()
{
    // This dialog doesn't contain preview
    return 0;
}
 
sal_Int32 RemoteFilesDialog::getAvailableHeight()
{
    // This dialog doesn't contain preview
    return 0;
}
 
void RemoteFilesDialog::setImage( const css::uno::Any& )
{
    // This dialog doesn't contain preview
}
 
bool RemoteFilesDialog::getShowState()
{
    // This dialog doesn't contain preview
    return false;
}
 
weld::Widget* RemoteFilesDialog::getControl( sal_Int16, bool) const
{
    return nullptr;
}
 
void RemoteFilesDialog::enableControl( sal_Int16, bool )
{
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.