summaryrefslogtreecommitdiffstats
path: root/fpicker/source/office
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /fpicker/source/office
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fpicker/source/office')
-rw-r--r--fpicker/source/office/OfficeControlAccess.cxx761
-rw-r--r--fpicker/source/office/OfficeControlAccess.hxx134
-rw-r--r--fpicker/source/office/OfficeFilePicker.cxx1108
-rw-r--r--fpicker/source/office/OfficeFilePicker.hxx239
-rw-r--r--fpicker/source/office/OfficeFolderPicker.cxx175
-rw-r--r--fpicker/source/office/OfficeFolderPicker.hxx89
-rw-r--r--fpicker/source/office/PlacesListBox.cxx158
-rw-r--r--fpicker/source/office/PlacesListBox.hxx66
-rw-r--r--fpicker/source/office/QueryFolderName.hxx39
-rw-r--r--fpicker/source/office/RemoteFilesDialog.cxx1199
-rw-r--r--fpicker/source/office/RemoteFilesDialog.hxx186
-rw-r--r--fpicker/source/office/asyncfilepicker.cxx183
-rw-r--r--fpicker/source/office/asyncfilepicker.hxx93
-rw-r--r--fpicker/source/office/autocmpledit.cxx104
-rw-r--r--fpicker/source/office/autocmpledit.hxx53
-rw-r--r--fpicker/source/office/breadcrumb.cxx246
-rw-r--r--fpicker/source/office/breadcrumb.hxx70
-rw-r--r--fpicker/source/office/commonpicker.cxx476
-rw-r--r--fpicker/source/office/commonpicker.hxx190
-rw-r--r--fpicker/source/office/contentenumeration.cxx322
-rw-r--r--fpicker/source/office/contentenumeration.hxx238
-rw-r--r--fpicker/source/office/fileview.cxx1807
-rw-r--r--fpicker/source/office/fileview.hxx184
-rw-r--r--fpicker/source/office/foldertree.cxx178
-rw-r--r--fpicker/source/office/foldertree.hxx48
-rw-r--r--fpicker/source/office/fpdialogbase.hxx112
-rw-r--r--fpicker/source/office/fpinteraction.cxx142
-rw-r--r--fpicker/source/office/fpinteraction.hxx80
-rw-r--r--fpicker/source/office/fps_office.component34
-rw-r--r--fpicker/source/office/fpsmartcontent.cxx325
-rw-r--r--fpicker/source/office/fpsmartcontent.hxx196
-rw-r--r--fpicker/source/office/iodlg.cxx2331
-rw-r--r--fpicker/source/office/iodlg.hxx302
-rw-r--r--fpicker/source/office/iodlgimp.cxx190
-rw-r--r--fpicker/source/office/iodlgimp.hxx205
-rw-r--r--fpicker/source/office/pickercallbacks.hxx51
36 files changed, 12314 insertions, 0 deletions
diff --git a/fpicker/source/office/OfficeControlAccess.cxx b/fpicker/source/office/OfficeControlAccess.cxx
new file mode 100644
index 0000000000..4b3cb59427
--- /dev/null
+++ b/fpicker/source/office/OfficeControlAccess.cxx
@@ -0,0 +1,761 @@
+/* -*- 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 <sal/macros.h>
+#include "OfficeControlAccess.hxx"
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ControlActions.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+
+#include <algorithm>
+#include <utility>
+
+
+namespace svt
+{
+
+
+ // helper -------------------------------------------------------------
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::ui::dialogs;
+
+ using namespace ExtendedFilePickerElementIds;
+ using namespace CommonFilePickerElementIds;
+ using namespace InternalFilePickerElementIds;
+
+
+ namespace
+ {
+
+ struct ControlDescription
+ {
+ const char* pControlName;
+ sal_Int16 nControlId;
+ PropFlags nPropertyFlags;
+ };
+
+
+ typedef const ControlDescription* ControlDescIterator;
+
+
+ #define PROPERTY_FLAGS_COMMON ( PropFlags::Enabled | PropFlags::Visible | PropFlags::HelpUrl )
+ #define PROPERTY_FLAGS_LISTBOX ( PropFlags::ListItems | PropFlags::SelectedItem | PropFlags::SelectedItemIndex )
+ #define PROPERTY_FLAGS_CHECKBOX ( PropFlags::Checked | PropFlags::Text )
+
+ // Note: this array MUST be sorted by name!
+ const ControlDescription aDescriptions[] = {
+ { "AutoExtensionBox", CHECKBOX_AUTOEXTENSION, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "CancelButton", PUSHBUTTON_CANCEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "CurrentFolderText", FIXEDTEXT_CURRENTFOLDER, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "FileURLEdit", EDIT_FILEURL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "FileURLEditLabel", EDIT_FILEURL_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "FileView", CONTROL_FILEVIEW, PROPERTY_FLAGS_COMMON },
+ { "FilterList", LISTBOX_FILTER, PROPERTY_FLAGS_COMMON },
+ { "FilterListLabel", LISTBOX_FILTER_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "FilterOptionsBox", CHECKBOX_FILTEROPTIONS, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "GpgPassword", CHECKBOX_GPGENCRYPTION, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "HelpButton", PUSHBUTTON_HELP, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "ImageAnchorList", LISTBOX_IMAGE_ANCHOR, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_LISTBOX },
+ { "ImageAnchorListLabel", LISTBOX_IMAGE_ANCHOR_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "ImageTemplateList", LISTBOX_IMAGE_TEMPLATE, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_LISTBOX },
+ { "ImageTemplateListLabel", LISTBOX_IMAGE_TEMPLATE_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "LevelUpButton", TOOLBOXBUTTON_LEVEL_UP, PROPERTY_FLAGS_COMMON },
+ { "LinkBox", CHECKBOX_LINK, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "NewFolderButton", TOOLBOXBUTTON_NEW_FOLDER, PROPERTY_FLAGS_COMMON },
+ { "OkButton", PUSHBUTTON_OK , PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "PasswordBox", CHECKBOX_PASSWORD, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "PlayButton", PUSHBUTTON_PLAY, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "PreviewBox", CHECKBOX_PREVIEW, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "ReadOnlyBox", CHECKBOX_READONLY, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "SelectionBox", CHECKBOX_SELECTION, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "TemplateList", LISTBOX_TEMPLATE, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_LISTBOX },
+ { "TemplateListLabel", LISTBOX_TEMPLATE_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "VersionList", LISTBOX_VERSION, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_LISTBOX },
+ { "VersionListLabel", LISTBOX_VERSION_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text }
+ };
+
+ const sal_Int32 s_nControlCount = SAL_N_ELEMENTS( aDescriptions );
+
+ ControlDescIterator s_pControls = aDescriptions;
+ ControlDescIterator s_pControlsEnd = aDescriptions + s_nControlCount;
+
+ struct ControlDescriptionLookup
+ {
+ bool operator()( const ControlDescription& rDesc1, const ControlDescription& rDesc2 )
+ {
+ return strcmp(rDesc1.pControlName, rDesc2.pControlName) < 0;
+ }
+ };
+
+ struct ControlProperty
+ {
+ const char* pPropertyName;
+ PropFlags nPropertyId;
+ };
+
+ typedef const ControlProperty* ControlPropertyIterator;
+
+ const ControlProperty aProperties[] = {
+ { "Text", PropFlags::Text },
+ { "Enabled", PropFlags::Enabled },
+ { "Visible", PropFlags::Visible },
+ { "HelpURL", PropFlags::HelpUrl },
+ { "ListItems", PropFlags::ListItems },
+ { "SelectedItem", PropFlags::SelectedItem },
+ { "SelectedItemIndex", PropFlags::SelectedItemIndex },
+ { "Checked", PropFlags::Checked }
+ };
+
+ const int s_nPropertyCount = SAL_N_ELEMENTS( aProperties );
+
+ ControlPropertyIterator s_pProperties = aProperties;
+ ControlPropertyIterator s_pPropertiesEnd = aProperties + s_nPropertyCount;
+
+
+ struct ControlPropertyLookup
+ {
+ OUString m_sLookup;
+ explicit ControlPropertyLookup(OUString aLookup)
+ : m_sLookup(std::move(aLookup))
+ {
+ }
+
+ bool operator()(const ControlProperty& rProp)
+ {
+ return m_sLookup.equalsAscii(rProp.pPropertyName);
+ }
+ };
+ }
+
+ OControlAccess::OControlAccess(IFilePickerController* pController, SvtFileView* pFileView)
+ : m_pFilePickerController(pController)
+ , m_pFileView(pFileView)
+ {
+ DBG_ASSERT( m_pFilePickerController, "OControlAccess::OControlAccess: invalid control locator!" );
+ }
+
+ bool OControlAccess::IsFileViewWidget(weld::Widget const * pControl) const
+ {
+ if (!pControl)
+ return false;
+ if (!m_pFileView)
+ return false;
+ return pControl == m_pFileView->identifier();
+ }
+
+ void OControlAccess::setHelpURL(weld::Widget* pControl, const OUString& sHelpURL)
+ {
+ OUString sHelpID( sHelpURL );
+ INetURLObject aHID( sHelpURL );
+ if (aHID.GetProtocol() == INetProtocol::Hid)
+ sHelpID = aHID.GetURLPath();
+
+ // URLs should always be escaped
+ if (IsFileViewWidget(pControl))
+ {
+ // the file view "overrides" the SetHelpId
+ m_pFileView->set_help_id(sHelpID);
+ }
+ else
+ pControl->set_help_id(sHelpID);
+ }
+
+ OUString OControlAccess::getHelpURL(weld::Widget const * pControl) const
+ {
+ OUString aHelpId = pControl->get_help_id();
+ if (IsFileViewWidget(pControl))
+ {
+ // the file view "overrides" the SetHelpId
+ aHelpId = m_pFileView->get_help_id();
+ }
+
+ OUString sHelpURL;
+ INetURLObject aHID(aHelpId);
+ if ( aHID.GetProtocol() == INetProtocol::NotValid )
+ sHelpURL = INET_HID_SCHEME;
+ sHelpURL += aHelpId;
+ return sHelpURL;
+ }
+
+ Any OControlAccess::getControlProperty( std::u16string_view rControlName, const OUString& rControlProperty )
+ {
+ // look up the control
+ sal_Int16 nControlId = -1;
+ PropFlags nPropertyMask = PropFlags::NONE;
+ weld::Widget* pControl = implGetControl( rControlName, &nControlId, &nPropertyMask );
+ // will throw an IllegalArgumentException if the name is not valid
+
+ // look up the property
+ ControlPropertyIterator aPropDesc = ::std::find_if( s_pProperties, s_pPropertiesEnd, ControlPropertyLookup( rControlProperty ) );
+ if ( aPropDesc == s_pPropertiesEnd )
+ // it's a completely unknown property
+ throw IllegalArgumentException();
+
+ if ( !( nPropertyMask & aPropDesc->nPropertyId ) )
+ // it's a property which is known, but not allowed for this control
+ throw IllegalArgumentException();
+
+ return implGetControlProperty( pControl, aPropDesc->nPropertyId );
+ }
+
+ weld::Widget* OControlAccess::implGetControl( std::u16string_view rControlName, sal_Int16* _pId, PropFlags* _pPropertyMask ) const
+ {
+ weld::Widget* pControl = nullptr;
+ ControlDescription tmpDesc;
+ OString aControlName = OUStringToOString( rControlName, RTL_TEXTENCODING_UTF8 );
+ tmpDesc.pControlName = aControlName.getStr();
+
+ // translate the name into an id
+ auto aFoundRange = ::std::equal_range( s_pControls, s_pControlsEnd, tmpDesc, ControlDescriptionLookup() );
+ if ( aFoundRange.first != aFoundRange.second )
+ {
+ // get the VCL control determined by this id
+ pControl = m_pFilePickerController->getControl( aFoundRange.first->nControlId );
+ }
+
+ // if not found 'til here, the name is invalid, or we do not have the control in the current mode
+ if ( !pControl )
+ throw IllegalArgumentException();
+
+ // out parameters and outta here
+ if ( _pId )
+ *_pId = aFoundRange.first->nControlId;
+ if ( _pPropertyMask )
+ *_pPropertyMask = aFoundRange.first->nPropertyFlags;
+
+ return pControl;
+ }
+
+ void OControlAccess::setControlProperty( std::u16string_view rControlName, const OUString& rControlProperty, const css::uno::Any& rValue )
+ {
+ // look up the control
+ sal_Int16 nControlId = -1;
+ weld::Widget* pControl = implGetControl( rControlName, &nControlId );
+ // will throw an IllegalArgumentException if the name is not valid
+
+ // look up the property
+ ControlPropertyIterator aPropDesc = ::std::find_if( s_pProperties, s_pPropertiesEnd, ControlPropertyLookup( rControlProperty ) );
+ if ( aPropDesc == s_pPropertiesEnd )
+ throw IllegalArgumentException();
+
+ // set the property
+ implSetControlProperty( nControlId, pControl, aPropDesc->nPropertyId, rValue, false );
+ }
+
+ Sequence< OUString > OControlAccess::getSupportedControls( ) const
+ {
+ Sequence< OUString > aControls( s_nControlCount );
+ OUString* pControls = aControls.getArray();
+
+ // collect the names of all _actually_existent_ controls
+ for ( ControlDescIterator aControl = s_pControls; aControl != s_pControlsEnd; ++aControl )
+ {
+ if ( m_pFilePickerController->getControl( aControl->nControlId ) )
+ *pControls++ = OUString::createFromAscii( aControl->pControlName );
+ }
+
+ aControls.realloc( pControls - aControls.getArray() );
+ return aControls;
+ }
+
+ Sequence< OUString > OControlAccess::getSupportedControlProperties( std::u16string_view rControlName )
+ {
+ sal_Int16 nControlId = -1;
+ PropFlags nPropertyMask = PropFlags::NONE;
+ implGetControl( rControlName, &nControlId, &nPropertyMask );
+ // will throw an IllegalArgumentException if the name is not valid
+
+ // fill in the property names
+ Sequence< OUString > aProps( s_nPropertyCount );
+ OUString* pProperty = aProps.getArray();
+
+ for ( ControlPropertyIterator aProp = s_pProperties; aProp != s_pPropertiesEnd; ++aProp )
+ if ( nPropertyMask & aProp->nPropertyId )
+ *pProperty++ = OUString::createFromAscii( aProp->pPropertyName );
+
+ aProps.realloc( pProperty - aProps.getArray() );
+ return aProps;
+ }
+
+ bool OControlAccess::isControlSupported( std::u16string_view rControlName )
+ {
+ ControlDescription tmpDesc;
+ OString aControlName = OUStringToOString(rControlName, RTL_TEXTENCODING_UTF8);
+ tmpDesc.pControlName = aControlName.getStr();
+ return ::std::binary_search( s_pControls, s_pControlsEnd, tmpDesc, ControlDescriptionLookup() );
+ }
+
+ bool OControlAccess::isControlPropertySupported( std::u16string_view rControlName, const OUString& rControlProperty )
+ {
+ // look up the control
+ sal_Int16 nControlId = -1;
+ PropFlags nPropertyMask = PropFlags::NONE;
+ implGetControl( rControlName, &nControlId, &nPropertyMask );
+ // will throw an IllegalArgumentException if the name is not valid
+
+ // look up the property
+ ControlPropertyIterator aPropDesc = ::std::find_if( s_pProperties, s_pPropertiesEnd, ControlPropertyLookup( rControlProperty ) );
+ if ( aPropDesc == s_pPropertiesEnd )
+ // it's a property which is completely unknown
+ return false;
+
+ return bool( aPropDesc->nPropertyId & nPropertyMask );
+ }
+
+ void OControlAccess::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const Any& rValue )
+ {
+ weld::Widget* pControl = m_pFilePickerController->getControl( nControlId );
+ DBG_ASSERT( pControl, "OControlAccess::SetValue: don't have this control in the current mode!" );
+ if ( !pControl )
+ return;
+
+ PropFlags nPropertyId = PropFlags::Unknown;
+ if ( ControlActions::SET_HELP_URL == nControlAction )
+ {
+ nPropertyId = PropFlags::HelpUrl;
+ }
+ else
+ {
+ switch ( nControlId )
+ {
+ case CHECKBOX_AUTOEXTENSION:
+ case CHECKBOX_PASSWORD:
+ case CHECKBOX_FILTEROPTIONS:
+ case CHECKBOX_READONLY:
+ case CHECKBOX_LINK:
+ case CHECKBOX_PREVIEW:
+ case CHECKBOX_SELECTION:
+ nPropertyId = PropFlags::Checked;
+ break;
+
+ case LISTBOX_FILTER:
+ SAL_WARN( "fpicker.office", "Use the XFilterManager to access the filter listbox" );
+ break;
+
+ case LISTBOX_VERSION:
+ case LISTBOX_TEMPLATE:
+ case LISTBOX_IMAGE_TEMPLATE:
+ case LISTBOX_IMAGE_ANCHOR:
+ if ( ControlActions::SET_SELECT_ITEM == nControlAction )
+ {
+ nPropertyId = PropFlags::SelectedItemIndex;
+ }
+ else
+ {
+ weld::ComboBox* pComboBox = dynamic_cast<weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::SetValue: implGetControl returned nonsense!");
+ implDoListboxAction(pComboBox, nControlAction, rValue);
+ }
+ break;
+ }
+ }
+
+ if ( PropFlags::Unknown != nPropertyId )
+ implSetControlProperty( nControlId, pControl, nPropertyId, rValue );
+ }
+
+ Any OControlAccess::getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const
+ {
+ Any aRet;
+
+ weld::Widget* pControl = m_pFilePickerController->getControl( nControlId );
+ DBG_ASSERT( pControl, "OControlAccess::GetValue: don't have this control in the current mode!" );
+ if ( pControl )
+ {
+ PropFlags nPropertyId = PropFlags::Unknown;
+ if ( ControlActions::SET_HELP_URL == nControlAction )
+ {
+ nPropertyId = PropFlags::HelpUrl;
+ }
+ else
+ {
+ switch ( nControlId )
+ {
+ case CHECKBOX_AUTOEXTENSION:
+ case CHECKBOX_PASSWORD:
+ case CHECKBOX_GPGENCRYPTION:
+ case CHECKBOX_FILTEROPTIONS:
+ case CHECKBOX_READONLY:
+ case CHECKBOX_LINK:
+ case CHECKBOX_PREVIEW:
+ case CHECKBOX_SELECTION:
+ nPropertyId = PropFlags::Checked;
+ break;
+
+ case LISTBOX_FILTER:
+ if ( ControlActions::GET_SELECTED_ITEM == nControlAction )
+ {
+ aRet <<= m_pFilePickerController->getCurFilter();
+ }
+ else
+ {
+ SAL_WARN( "fpicker.office", "Use the XFilterManager to access the filter listbox" );
+ }
+ break;
+
+ case LISTBOX_VERSION:
+ case LISTBOX_TEMPLATE:
+ case LISTBOX_IMAGE_TEMPLATE:
+ case LISTBOX_IMAGE_ANCHOR:
+ switch ( nControlAction )
+ {
+ case ControlActions::GET_SELECTED_ITEM:
+ nPropertyId = PropFlags::SelectedItem;
+ break;
+ case ControlActions::GET_SELECTED_ITEM_INDEX:
+ nPropertyId = PropFlags::SelectedItemIndex;
+ break;
+ case ControlActions::GET_ITEMS:
+ nPropertyId = PropFlags::ListItems;
+ break;
+ default:
+ SAL_WARN( "fpicker.office", "OControlAccess::GetValue: invalid control action for the listbox!" );
+ break;
+ }
+ break;
+ }
+ }
+
+ if ( PropFlags::Unknown != nPropertyId )
+ aRet = implGetControlProperty( pControl, nPropertyId );
+ }
+
+ return aRet;
+ }
+
+ void OControlAccess::setLabel( sal_Int16 nId, const OUString &rLabel )
+ {
+ weld::Widget* pControl = m_pFilePickerController->getControl(nId, true);
+ if (weld::Label* pLabel = dynamic_cast<weld::Label*>(pControl))
+ {
+ pLabel->set_label(rLabel);
+ return;
+ }
+ if (weld::Button* pButton = dynamic_cast<weld::Button*>(pControl))
+ {
+ pButton->set_label(rLabel);
+ return;
+ }
+ assert(false && "OControlAccess::GetValue: don't have this control in the current mode!");
+ }
+
+ OUString OControlAccess::getLabel( sal_Int16 nId ) const
+ {
+ weld::Widget* pControl = m_pFilePickerController->getControl(nId, true);
+ if (weld::Label* pLabel = dynamic_cast<weld::Label*>(pControl))
+ return pLabel->get_label();
+ if (weld::Button* pButton = dynamic_cast<weld::Button*>(pControl))
+ return pButton->get_label();
+ assert(false && "OControlAccess::GetValue: don't have this control in the current mode!");
+ return OUString();
+ }
+
+ void OControlAccess::enableControl(sal_Int16 nId, bool bEnable)
+ {
+ m_pFilePickerController->enableControl(nId, bEnable);
+ }
+
+ void OControlAccess::implDoListboxAction(weld::ComboBox* pListbox, sal_Int16 nControlAction, const Any& rValue)
+ {
+ switch ( nControlAction )
+ {
+ case ControlActions::ADD_ITEM:
+ {
+ OUString aEntry;
+ rValue >>= aEntry;
+ if ( !aEntry.isEmpty() )
+ pListbox->append_text( aEntry );
+ }
+ break;
+
+ case ControlActions::ADD_ITEMS:
+ {
+ Sequence < OUString > aTemplateList;
+ rValue >>= aTemplateList;
+
+ if ( aTemplateList.hasElements() )
+ {
+ for ( const OUString& s : std::as_const(aTemplateList) )
+ pListbox->append_text( s );
+ }
+ }
+ break;
+
+ case ControlActions::DELETE_ITEM:
+ {
+ sal_Int32 nPos = 0;
+ if ( rValue >>= nPos )
+ pListbox->remove( nPos );
+ }
+ break;
+
+ case ControlActions::DELETE_ITEMS:
+ pListbox->clear();
+ break;
+
+ default:
+ SAL_WARN( "fpicker.office", "Wrong ControlAction for implDoListboxAction()" );
+ }
+ }
+
+ void OControlAccess::implSetControlProperty( sal_Int16 nControlId, weld::Widget* pControl, PropFlags _nProperty, const Any& rValue, bool _bIgnoreIllegalArgument )
+ {
+ if ( !pControl )
+ pControl = m_pFilePickerController->getControl( nControlId );
+ DBG_ASSERT( pControl, "OControlAccess::implSetControlProperty: invalid argument, this will crash!" );
+ if ( !pControl )
+ return;
+
+ DBG_ASSERT( pControl == m_pFilePickerController->getControl( nControlId ),
+ "OControlAccess::implSetControlProperty: inconsistent parameters!" );
+
+ switch ( _nProperty )
+ {
+ case PropFlags::Text:
+ {
+ OUString sText;
+ if (rValue >>= sText)
+ {
+ weld::Label* pLabel = dynamic_cast<weld::Label*>(pControl);
+ assert(pLabel);
+ pLabel->set_label(sText);
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::Enabled:
+ {
+ bool bEnabled = false;
+ if ( rValue >>= bEnabled )
+ {
+ m_pFilePickerController->enableControl( nControlId, bEnabled );
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::Visible:
+ {
+ bool bVisible = false;
+ if ( rValue >>= bVisible )
+ {
+ pControl->set_visible( bVisible );
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::HelpUrl:
+ {
+ OUString sHelpURL;
+ if ( rValue >>= sHelpURL )
+ {
+ setHelpURL(pControl, sHelpURL);
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::ListItems:
+ {
+ weld::ComboBox* pComboBox = dynamic_cast<weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implSetControlProperty: invalid control/property combination!");
+
+ Sequence< OUString > aItems;
+ if ( rValue >>= aItems )
+ {
+ // remove all previous items
+ pComboBox->clear();
+
+ // add the new ones
+ for (auto const & item : std::as_const(aItems))
+ {
+ pComboBox->append_text(item);
+ }
+
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::SelectedItem:
+ {
+ weld::ComboBox* pComboBox = dynamic_cast<weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implSetControlProperty: invalid control/property combination!");
+
+ OUString sSelected;
+ if ( rValue >>= sSelected )
+ {
+ pComboBox->set_active_text(sSelected);
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::SelectedItemIndex:
+ {
+ weld::ComboBox* pComboBox = dynamic_cast<weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implSetControlProperty: invalid control/property combination!");
+
+ sal_Int32 nPos = 0;
+ if ( rValue >>= nPos )
+ {
+ pComboBox->set_active(nPos);
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::Checked:
+ {
+ weld::Toggleable* pToggleButton = dynamic_cast<weld::Toggleable*>(pControl);
+ assert(pToggleButton && "OControlAccess::implSetControlProperty: invalid control/property combination!");
+
+ bool bChecked = false;
+ if ( rValue >>= bChecked )
+ {
+ pToggleButton->set_active(bChecked);
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "OControlAccess::implSetControlProperty: invalid property id!" );
+ }
+ }
+
+ Any OControlAccess::implGetControlProperty( weld::Widget const * pControl, PropFlags _nProperty ) const
+ {
+ assert(pControl && "OControlAccess::implGetControlProperty: invalid argument, this will crash!");
+
+ Any aReturn;
+ switch ( _nProperty )
+ {
+ case PropFlags::Text:
+ {
+ const weld::Label* pLabel = dynamic_cast<const weld::Label*>(pControl);
+ assert(pLabel);
+ aReturn <<= pLabel->get_label();
+ break;
+ }
+ case PropFlags::Enabled:
+ aReturn <<= pControl->get_sensitive();
+ break;
+
+ case PropFlags::Visible:
+ aReturn <<= pControl->get_visible();
+ break;
+
+ case PropFlags::HelpUrl:
+ aReturn <<= getHelpURL(pControl);
+ break;
+
+ case PropFlags::ListItems:
+ {
+ const weld::ComboBox* pComboBox = dynamic_cast<const weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implGetControlProperty: invalid control/property combination!");
+
+ Sequence< OUString > aItems(pComboBox->get_count());
+ OUString* pItems = aItems.getArray();
+ for (sal_Int32 i = 0; i < pComboBox->get_count(); ++i)
+ *pItems++ = pComboBox->get_text(i);
+
+ aReturn <<= aItems;
+ break;
+ }
+
+ case PropFlags::SelectedItem:
+ {
+ const weld::ComboBox* pComboBox = dynamic_cast<const weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implGetControlProperty: invalid control/property combination!");
+
+ sal_Int32 nSelected = pComboBox->get_active();
+ OUString sSelected;
+ if (nSelected != -1)
+ sSelected = pComboBox->get_active_text();
+ aReturn <<= sSelected;
+ break;
+ }
+
+ case PropFlags::SelectedItemIndex:
+ {
+ const weld::ComboBox* pComboBox = dynamic_cast<const weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implGetControlProperty: invalid control/property combination!");
+
+ sal_Int32 nSelected = pComboBox->get_active();
+ if (nSelected != -1)
+ aReturn <<= nSelected;
+ else
+ aReturn <<= sal_Int32(-1);
+ break;
+ }
+
+ case PropFlags::Checked:
+ {
+ const weld::Toggleable* pToggleButton = dynamic_cast<const weld::Toggleable*>(pControl);
+ assert(pToggleButton && "OControlAccess::implGetControlProperty: invalid control/property combination!");
+
+ aReturn <<= pToggleButton->get_active();
+ break;
+ }
+
+ default:
+ OSL_FAIL( "OControlAccess::implGetControlProperty: invalid property id!" );
+ }
+ return aReturn;
+ }
+
+} // namespace svt
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeControlAccess.hxx b/fpicker/source/office/OfficeControlAccess.hxx
new file mode 100644
index 0000000000..859ff15f27
--- /dev/null
+++ b/fpicker/source/office/OfficeControlAccess.hxx
@@ -0,0 +1,134 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "fileview.hxx"
+#include "pickercallbacks.hxx"
+#include <o3tl/typed_flags_set.hxx>
+
+enum class PropFlags {
+ Unknown = -1, // used as an error sentinel
+ NONE = 0x0000,
+ Text = 0x0001,
+ Enabled = 0x0002,
+ Visible = 0x0004,
+ HelpUrl = 0x0008,
+ ListItems = 0x0010,
+ SelectedItem = 0x0020,
+ SelectedItemIndex = 0x0040,
+ Checked = 0x0080,
+};
+namespace o3tl {
+ template<> struct typed_flags<PropFlags> : is_typed_flags<PropFlags, 0x00ff> {};
+}
+
+
+namespace svt
+{
+
+
+ namespace InternalFilePickerElementIds
+ {
+ const sal_Int16 PUSHBUTTON_HELP = sal_Int16(0x1000);
+ const sal_Int16 TOOLBOXBUTTON_LEVEL_UP = sal_Int16(0x1002);
+ const sal_Int16 TOOLBOXBUTTON_NEW_FOLDER = sal_Int16(0x1003);
+ const sal_Int16 FIXEDTEXT_CURRENTFOLDER = sal_Int16(0x1004);
+ }
+
+
+ /** implements the XControlAccess, XControlInformation and XFilePickerControlAccess for the file picker
+ */
+ class OControlAccess
+ {
+ IFilePickerController* m_pFilePickerController;
+ SvtFileView* m_pFileView;
+
+ public:
+ OControlAccess( IFilePickerController* pController, SvtFileView* pFileView );
+
+ // XControlAccess implementation
+ void setControlProperty( std::u16string_view rControlName, const OUString& rControlProperty, const css::uno::Any& rValue );
+ css::uno::Any getControlProperty( std::u16string_view rControlName, const OUString& rControlProperty );
+
+ // XControlInformation implementation
+ css::uno::Sequence< OUString > getSupportedControls( ) const;
+ css::uno::Sequence< OUString > getSupportedControlProperties( std::u16string_view rControlName );
+ static bool isControlSupported( std::u16string_view rControlName );
+ bool isControlPropertySupported( std::u16string_view rControlName, const OUString& rControlProperty );
+
+ // XFilePickerControlAccess
+ void setValue( sal_Int16 nId, sal_Int16 nCtrlAction, const css::uno::Any& rValue );
+ css::uno::Any getValue( sal_Int16 nId, sal_Int16 nCtrlAction ) const;
+ void setLabel( sal_Int16 nId, const OUString& rValue );
+ OUString getLabel( sal_Int16 nId ) const;
+ void enableControl( sal_Int16 nId, bool bEnable );
+
+ void setHelpURL(weld::Widget* pControl, const OUString& rURL);
+ OUString getHelpURL(weld::Widget const* pControl) const;
+
+ private:
+ /** implements the various methods for setting properties on controls
+
+ @param nControlId
+ the id of the control
+ @param pControl
+ the affected control. Must be the same as referred by <arg>nControlId</arg>, or NULL.
+ @param nProperty
+ the property to set
+ See PropFlags::*
+ @param rValue
+ the value to set
+ @param bIgnoreIllegalArgument
+ if <FALSE/>, an exception will be thrown if the given value is of improper type
+ */
+ void implSetControlProperty(
+ sal_Int16 nControlId,
+ weld::Widget* pControl, PropFlags nProperty, const css::uno::Any& rValue,
+ bool bIgnoreIllegalArgument = true );
+
+ weld::Widget* implGetControl( std::u16string_view rControlName, sal_Int16* pId, PropFlags* pPropertyMask = nullptr ) const;
+
+ /** implements the various methods for retrieving properties from controls
+
+ @param pControl
+ the affected control
+ @PRECOND not <NULL/>
+ @param nProperty
+ the property to retrieve
+ See PropFlags::*
+ @return
+ */
+ css::uno::Any implGetControlProperty( weld::Widget const * pControl, PropFlags nProperty ) const;
+
+ bool IsFileViewWidget(weld::Widget const * pControl) const;
+
+ static void implDoListboxAction(weld::ComboBox* pListbox, sal_Int16 nCtrlAction, const css::uno::Any& rValue);
+
+ };
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeFilePicker.cxx b/fpicker/source/office/OfficeFilePicker.cxx
new file mode 100644
index 0000000000..723a8a2799
--- /dev/null
+++ b/fpicker/source/office/OfficeFilePicker.cxx
@@ -0,0 +1,1108 @@
+/* -*- 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 "OfficeControlAccess.hxx"
+#include "OfficeFilePicker.hxx"
+#include "iodlg.hxx"
+#include "RemoteFilesDialog.hxx"
+
+#include <utility>
+#include <vector>
+#include <algorithm>
+#include <sal/log.hxx>
+#include <tools/urlobj.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/ui/dialogs/FilePickerEvent.hpp>
+#include <com/sun/star/ui/dialogs/FilePreviewImageFormats.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <unotools/pathoptions.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/make_shared.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::awt;
+using namespace ::utl;
+
+
+struct FilterEntry
+{
+protected:
+ OUString m_sTitle;
+ OUString m_sFilter;
+
+ UnoFilterList m_aSubFilters;
+
+public:
+ FilterEntry( OUString _aTitle, OUString _aFilter )
+ :m_sTitle(std::move( _aTitle ))
+ ,m_sFilter(std::move( _aFilter ))
+ {
+ }
+
+ FilterEntry( OUString _aTitle, const UnoFilterList& _rSubFilters );
+
+ const OUString& getTitle() const { return m_sTitle; }
+ const OUString& getFilter() const { return m_sFilter; }
+
+ /// determines if the filter has sub filter (i.e., the filter is a filter group in real)
+ bool hasSubFilters( ) const;
+
+ /** retrieves the filters belonging to the entry
+ */
+ void getSubFilters( UnoFilterList& _rSubFilterList );
+
+ // helpers for iterating the sub filters
+ const UnoFilterEntry* beginSubFilters() const { return m_aSubFilters.getConstArray(); }
+ const UnoFilterEntry* endSubFilters() const { return m_aSubFilters.getConstArray() + m_aSubFilters.getLength(); }
+};
+
+
+FilterEntry::FilterEntry( OUString _aTitle, const UnoFilterList& _rSubFilters )
+ :m_sTitle(std::move( _aTitle ))
+ ,m_aSubFilters( _rSubFilters )
+{
+}
+
+
+bool FilterEntry::hasSubFilters( ) const
+{
+ return m_aSubFilters.hasElements();
+}
+
+
+void FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
+{
+ _rSubFilterList = m_aSubFilters;
+}
+
+struct ElementEntry_Impl
+{
+ sal_Int16 m_nElementID;
+ sal_Int16 m_nControlAction;
+ Any m_aValue;
+ OUString m_aLabel;
+ bool m_bEnabled : 1;
+
+ bool m_bHasValue : 1;
+ bool m_bHasLabel : 1;
+ bool m_bHasEnabled : 1;
+
+ explicit ElementEntry_Impl( sal_Int16 nId );
+
+ void setValue( const Any& rVal ) { m_aValue = rVal; m_bHasValue = true; }
+ void setAction( sal_Int16 nAction ) { m_nControlAction = nAction; }
+ void setLabel( const OUString& rVal ) { m_aLabel = rVal; m_bHasLabel = true; }
+ void setEnabled( bool bEnabled ) { m_bEnabled = bEnabled; m_bHasEnabled = true; }
+};
+
+ElementEntry_Impl::ElementEntry_Impl( sal_Int16 nId )
+ : m_nElementID( nId )
+ , m_nControlAction( 0 )
+ , m_bEnabled( false )
+ , m_bHasValue( false )
+ , m_bHasLabel( false )
+ , m_bHasEnabled( false )
+{}
+
+
+void SvtFilePicker::prepareExecute()
+{
+ // set the default directory
+ // --**-- doesn't match the spec yet
+ if ( !m_aDisplayDirectory.isEmpty() || !m_aDefaultName.isEmpty() )
+ {
+ bool isFileSet = false;
+ if ( !m_aDisplayDirectory.isEmpty() )
+ {
+
+ INetURLObject aPath;
+ INetURLObject givenPath( m_aDisplayDirectory );
+ if (!givenPath.HasError())
+ aPath = givenPath;
+ else
+ {
+ aPath = INetURLObject( SvtPathOptions().GetWorkPath() );
+ }
+ if ( !m_aDefaultName.isEmpty() )
+ {
+ aPath.insertName( m_aDefaultName );
+ m_xDlg->SetHasFilename( true );
+ }
+ m_xDlg->SetPath( aPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ isFileSet = true;
+ }
+ if ( !isFileSet && !m_aDefaultName.isEmpty() )
+ {
+ m_xDlg->SetPath( m_aDefaultName );
+ m_xDlg->SetHasFilename( true );
+ }
+ }
+ else
+ {
+ // set the default standard dir
+ INetURLObject aStdDirObj( SvtPathOptions().GetWorkPath() );
+ m_xDlg->SetPath( aStdDirObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+
+ // set the control values and set the control labels, too
+ if ( m_pElemList && !m_pElemList->empty() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+
+ for (auto const& elem : *m_pElemList)
+ {
+ if ( elem.m_bHasValue )
+ aAccess.setValue( elem.m_nElementID, elem.m_nControlAction, elem.m_aValue );
+ if ( elem.m_bHasLabel )
+ aAccess.setLabel( elem.m_nElementID, elem.m_aLabel );
+ if ( elem.m_bHasEnabled )
+ aAccess.enableControl( elem.m_nElementID, elem.m_bEnabled );
+ }
+
+ }
+
+ if ( m_pFilterList )
+ {
+ for (auto & elem : *m_pFilterList)
+ {
+ if ( elem.hasSubFilters() )
+ { // it's a filter group
+ UnoFilterList aSubFilters;
+ elem.getSubFilters( aSubFilters );
+
+ m_xDlg->AddFilterGroup( elem.getTitle(), aSubFilters );
+ }
+ else
+ {
+ // it's a single filter
+ m_xDlg->AddFilter( elem.getTitle(), elem.getFilter() );
+ }
+ }
+ }
+
+ // set the default filter
+ if ( !m_aCurrentFilter.isEmpty() )
+ m_xDlg->SetCurFilter( m_aCurrentFilter );
+
+}
+
+void SvtFilePicker::DialogClosedHdl(sal_Int32 nResult)
+{
+ if ( m_xDlgClosedListener.is() )
+ {
+ sal_Int16 nRet = static_cast< sal_Int16 >(nResult);
+ css::ui::dialogs::DialogClosedEvent aEvent( *this, nRet );
+ m_xDlgClosedListener->dialogClosed( aEvent );
+ m_xDlgClosedListener.clear();
+ }
+}
+
+// SvtFilePicker
+PickerFlags SvtFilePicker::getPickerFlags() const
+{
+ // set the winbits for creating the filedialog
+ PickerFlags nBits = PickerFlags::NONE;
+
+ // set the standard bits according to the service name
+ if ( m_nServiceType == TemplateDescription::FILEOPEN_SIMPLE )
+ {
+ nBits = PickerFlags::Open;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_SIMPLE )
+ {
+ nBits = PickerFlags::SaveAs;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_AUTOEXTENSION )
+ {
+ nBits = PickerFlags::SaveAs | PickerFlags::AutoExtension;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD )
+ {
+ nBits = PickerFlags::SaveAs | PickerFlags::Password | PickerFlags::AutoExtension;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS )
+ {
+ nBits = PickerFlags::SaveAs | PickerFlags::Password | PickerFlags::AutoExtension | PickerFlags::FilterOptions;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_AUTOEXTENSION_TEMPLATE )
+ {
+ nBits = PickerFlags::SaveAs | PickerFlags::AutoExtension | PickerFlags::Templates;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_AUTOEXTENSION_SELECTION )
+ {
+ nBits = PickerFlags::SaveAs | PickerFlags::AutoExtension | PickerFlags::Selection;
+ }
+
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE )
+ {
+ nBits = PickerFlags::Open | PickerFlags::InsertAsLink | PickerFlags::ShowPreview | PickerFlags::ImageTemplate;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_PLAY )
+ {
+ nBits = PickerFlags::Open | PickerFlags::PlayButton;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_LINK_PLAY )
+ {
+ nBits = PickerFlags::Open | PickerFlags::InsertAsLink | PickerFlags::PlayButton;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_READONLY_VERSION )
+ {
+ nBits = PickerFlags::Open | PickerFlags::ReadOnly | PickerFlags::ShowVersions;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_LINK_PREVIEW )
+ {
+ nBits = PickerFlags::Open | PickerFlags::InsertAsLink | PickerFlags::ShowPreview;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR )
+ {
+ nBits = PickerFlags::Open | PickerFlags::InsertAsLink | PickerFlags::ShowPreview | PickerFlags::ImageAnchor;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_PREVIEW )
+ {
+ nBits = PickerFlags::Open | PickerFlags::ShowPreview;
+ }
+ if ( m_bMultiSelection && ( nBits & PickerFlags::Open ) )
+ nBits |= PickerFlags::MultiSelection;
+
+ return nBits;
+}
+
+
+void SvtFilePicker::notify( sal_Int16 _nEventId, sal_Int16 _nControlId )
+{
+ if ( !m_xListener.is() )
+ return;
+
+ FilePickerEvent aEvent( *this, _nControlId );
+
+ switch ( _nEventId )
+ {
+ case FILE_SELECTION_CHANGED:
+ m_xListener->fileSelectionChanged( aEvent );
+ break;
+ case DIRECTORY_CHANGED:
+ m_xListener->directoryChanged( aEvent );
+ break;
+ case CTRL_STATE_CHANGED:
+ m_xListener->controlStateChanged( aEvent );
+ break;
+ case DIALOG_SIZE_CHANGED:
+ m_xListener->dialogSizeChanged();
+ break;
+ default:
+ SAL_WARN( "fpicker.office", "SvtFilePicker::notify(): Unknown event id!" );
+ break;
+ }
+}
+
+
+namespace {
+
+ struct FilterTitleMatch
+ {
+ protected:
+ const OUString& rTitle;
+
+ public:
+ explicit FilterTitleMatch( const OUString& _rTitle ) : rTitle( _rTitle ) { }
+
+
+ bool operator () ( const FilterEntry& _rEntry )
+ {
+ bool bMatch;
+ if ( !_rEntry.hasSubFilters() )
+ // a real filter
+ bMatch = ( _rEntry.getTitle() == rTitle );
+ else
+ // a filter group -> search the sub filters
+ bMatch =
+ ::std::any_of(
+ _rEntry.beginSubFilters(),
+ _rEntry.endSubFilters(),
+ *this
+ );
+
+ return bMatch;
+ }
+ bool operator () ( const UnoFilterEntry& _rEntry )
+ {
+ return _rEntry.First == rTitle;
+ }
+ };
+}
+
+
+bool SvtFilePicker::FilterNameExists( const OUString& rTitle )
+{
+ bool bRet = false;
+
+ if ( m_pFilterList )
+ bRet =
+ ::std::any_of(
+ m_pFilterList->begin(),
+ m_pFilterList->end(),
+ FilterTitleMatch( rTitle )
+ );
+
+ return bRet;
+}
+
+
+bool SvtFilePicker::FilterNameExists( const UnoFilterList& _rGroupedFilters )
+{
+ bool bRet = false;
+
+ if ( m_pFilterList )
+ {
+ const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
+ const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
+ for ( ; pStart != pEnd; ++pStart )
+ if ( ::std::any_of( m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch( pStart->First ) ) )
+ break;
+
+ bRet = pStart != pEnd;
+ }
+
+ return bRet;
+}
+
+
+void SvtFilePicker::ensureFilterList( const OUString& _rInitialCurrentFilter )
+{
+ if ( !m_pFilterList )
+ {
+ m_pFilterList.reset( new FilterList );
+
+ // set the first filter to the current filter
+ if ( m_aCurrentFilter.isEmpty() )
+ m_aCurrentFilter = _rInitialCurrentFilter;
+ }
+}
+
+
+
+SvtFilePicker::SvtFilePicker()
+ :m_bMultiSelection ( false )
+ ,m_nServiceType ( TemplateDescription::FILEOPEN_SIMPLE )
+{
+}
+
+SvtFilePicker::~SvtFilePicker()
+{
+}
+
+sal_Int16 SvtFilePicker::implExecutePicker( )
+{
+ m_xDlg->SetFileCallback( this );
+
+ prepareExecute();
+
+ m_xDlg->EnableAutocompletion();
+ // now we are ready to execute the dialog
+ sal_Int16 nRet = m_xDlg->run();
+
+ // coverity[check_after_deref] - the execution of the dialog yields, so it is possible the at this point the window or the dialog is closed
+ if (m_xDlg)
+ m_xDlg->SetFileCallback( nullptr );
+
+ return nRet;
+}
+
+std::shared_ptr<SvtFileDialog_Base> SvtFilePicker::implCreateDialog( weld::Window* pParent )
+{
+ PickerFlags nBits = getPickerFlags();
+
+ auto dialog = o3tl::make_shared<SvtFileDialog>(pParent, nBits);
+
+ // Set StandardDir if present
+ if ( !m_aStandardDir.isEmpty())
+ {
+ OUString sStandardDir = m_aStandardDir;
+ dialog->SetStandardDir( sStandardDir );
+ dialog->SetDenyList( m_aDenyList );
+ }
+
+ return dialog;
+}
+
+
+// disambiguate XInterface
+
+IMPLEMENT_FORWARD_XINTERFACE2( SvtFilePicker, OCommonPicker, SvtFilePicker_Base )
+
+
+// disambiguate XTypeProvider
+
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvtFilePicker, OCommonPicker, SvtFilePicker_Base )
+
+IMPLEMENT_FORWARD_XINTERFACE3( SvtRemoteFilePicker, SvtFilePicker, OCommonPicker, SvtFilePicker_Base )
+
+
+// disambiguate XTypeProvider
+
+css::uno::Sequence< css::uno::Type > SAL_CALL SvtRemoteFilePicker::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ SvtFilePicker::getTypes(),
+ OCommonPicker::getTypes(),
+ SvtFilePicker_Base::getTypes()
+ );
+}
+IMPLEMENT_GET_IMPLEMENTATION_ID( SvtRemoteFilePicker )
+
+
+// XExecutableDialog functions
+
+
+void SAL_CALL SvtFilePicker::setTitle( const OUString& _rTitle )
+{
+ OCommonPicker::setTitle( _rTitle );
+}
+
+sal_Int16 SAL_CALL SvtFilePicker::execute( )
+{
+ return OCommonPicker::execute();
+}
+
+// XAsynchronousExecutableDialog functions
+void SAL_CALL SvtFilePicker::setDialogTitle( const OUString& _rTitle )
+{
+ setTitle( _rTitle );
+}
+
+void SAL_CALL SvtFilePicker::startExecuteModal( const Reference< css::ui::dialogs::XDialogClosedListener >& xListener )
+{
+ m_xDlgClosedListener = xListener;
+ prepareDialog();
+ prepareExecute();
+ m_xDlg->EnableAutocompletion();
+ if (!m_xDlg->PrepareExecute())
+ return;
+ weld::DialogController::runAsync(m_xDlg, [this](sal_Int32 nResult){
+ DialogClosedHdl(nResult);
+ });
+}
+
+// XFilePicker functions
+void SAL_CALL SvtFilePicker::setMultiSelectionMode( sal_Bool bMode )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ m_bMultiSelection = bMode;
+}
+
+void SAL_CALL SvtFilePicker::setDefaultName( const OUString& aName )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ m_aDefaultName = aName;
+}
+
+void SAL_CALL SvtFilePicker::setDisplayDirectory( const OUString& aDirectory )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ m_aDisplayDirectory = aDirectory;
+}
+
+OUString SAL_CALL SvtFilePicker::getDisplayDirectory()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (m_xDlg)
+ {
+ OUString aPath = m_xDlg->GetPath();
+
+ if( m_aOldHideDirectory == aPath )
+ return m_aOldDisplayDirectory;
+ m_aOldHideDirectory = aPath;
+
+ if( !m_xDlg->ContentIsFolder( aPath ) )
+ {
+ INetURLObject aFolder( aPath );
+ aFolder.CutLastName();
+ aPath = aFolder.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ m_aOldDisplayDirectory = aPath;
+ return aPath;
+ }
+ else
+ return m_aDisplayDirectory;
+}
+
+Sequence< OUString > SAL_CALL SvtFilePicker::getSelectedFiles()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (!m_xDlg)
+ {
+ Sequence< OUString > aEmpty;
+ return aEmpty;
+ }
+
+ return comphelper::containerToSequence(m_xDlg->GetPathList());
+}
+
+Sequence< OUString > SAL_CALL SvtFilePicker::getFiles()
+{
+ Sequence< OUString > aFiles = getSelectedFiles();
+ if (aFiles.getLength() > 1)
+ aFiles.realloc(1);
+ return aFiles;
+}
+
+
+// XFilePickerControlAccess functions
+
+
+void SAL_CALL SvtFilePicker::setValue( sal_Int16 nElementID,
+ sal_Int16 nControlAction,
+ const Any& rValue )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (m_xDlg)
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ aAccess.setValue( nElementID, nControlAction, rValue );
+ }
+ else
+ {
+ if ( !m_pElemList )
+ m_pElemList.reset( new ElementList );
+
+ bool bFound = false;
+
+ for (auto & elem : *m_pElemList)
+ {
+ if ( ( elem.m_nElementID == nElementID ) &&
+ ( !elem.m_bHasValue || ( elem.m_nControlAction == nControlAction ) ) )
+ {
+ elem.setAction( nControlAction );
+ elem.setValue( rValue );
+ bFound = true;
+ }
+ }
+
+ if ( !bFound )
+ {
+ ElementEntry_Impl aNew( nElementID );
+ aNew.setAction( nControlAction );
+ aNew.setValue( rValue );
+ m_pElemList->insert( m_pElemList->end(), aNew );
+ }
+ }
+}
+
+
+Any SAL_CALL SvtFilePicker::getValue( sal_Int16 nElementID, sal_Int16 nControlAction )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ Any aAny;
+
+ // execute() called?
+ if (m_xDlg)
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ aAny = aAccess.getValue( nElementID, nControlAction );
+ }
+ else if ( m_pElemList )
+ {
+ for (auto const& elem : *m_pElemList)
+ {
+ if ( ( elem.m_nElementID == nElementID ) &&
+ ( elem.m_bHasValue ) &&
+ ( elem.m_nControlAction == nControlAction ) )
+ {
+ aAny = elem.m_aValue;
+ break;
+ }
+ }
+ }
+
+ return aAny;
+}
+
+
+void SAL_CALL SvtFilePicker::setLabel( sal_Int16 nLabelID, const OUString& rValue )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (m_xDlg)
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ aAccess.setLabel( nLabelID, rValue );
+ }
+ else
+ {
+ if ( !m_pElemList )
+ m_pElemList.reset( new ElementList );
+
+ bool bFound = false;
+
+ for (auto & elem : *m_pElemList)
+ {
+ if ( elem.m_nElementID == nLabelID )
+ {
+ elem.setLabel( rValue );
+ bFound = true;
+ }
+ }
+
+ if ( !bFound )
+ {
+ ElementEntry_Impl aNew( nLabelID );
+ aNew.setLabel( rValue );
+ m_pElemList->insert( m_pElemList->end(), aNew );
+ }
+ }
+}
+
+
+OUString SAL_CALL SvtFilePicker::getLabel( sal_Int16 nLabelID )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ OUString aLabel;
+
+ if (m_xDlg)
+ {
+ ::svt::OControlAccess aAccess(m_xDlg.get(), m_xDlg->GetView());
+ aLabel = aAccess.getLabel( nLabelID );
+ }
+ else if ( m_pElemList )
+ {
+ for (auto const& elem : *m_pElemList)
+ {
+ if ( elem.m_nElementID == nLabelID )
+ {
+ if ( elem.m_bHasLabel )
+ aLabel = elem.m_aLabel;
+ break;
+ }
+ }
+ }
+
+ return aLabel;
+}
+
+
+void SAL_CALL SvtFilePicker::enableControl( sal_Int16 nElementID, sal_Bool bEnable )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (m_xDlg)
+ {
+ ::svt::OControlAccess aAccess(m_xDlg.get(), m_xDlg->GetView());
+ aAccess.enableControl( nElementID, bEnable );
+ }
+ else
+ {
+ if ( !m_pElemList )
+ m_pElemList.reset( new ElementList );
+
+ bool bFound = false;
+
+ for (auto & elem : *m_pElemList)
+ {
+ if ( elem.m_nElementID == nElementID )
+ {
+ elem.setEnabled( bEnable );
+ bFound = true;
+ }
+ }
+
+ if ( !bFound )
+ {
+ ElementEntry_Impl aNew( nElementID );
+ aNew.setEnabled( bEnable );
+ m_pElemList->insert( m_pElemList->end(), aNew );
+ }
+ }
+}
+
+
+// XFilePickerNotifier functions
+
+
+void SAL_CALL SvtFilePicker::addFilePickerListener( const Reference< XFilePickerListener >& xListener )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ m_xListener = xListener;
+}
+
+
+void SAL_CALL SvtFilePicker::removeFilePickerListener( const Reference< XFilePickerListener >& )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ m_xListener.clear();
+}
+
+// XFilePreview functions
+Sequence< sal_Int16 > SAL_CALL SvtFilePicker::getSupportedImageFormats()
+{
+ checkAlive();
+
+ return { FilePreviewImageFormats::BITMAP };
+}
+
+sal_Int32 SAL_CALL SvtFilePicker::getTargetColorDepth()
+{
+ return 0;
+}
+
+sal_Int32 SAL_CALL SvtFilePicker::getAvailableWidth()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ sal_Int32 nWidth = 0;
+
+ if (m_xDlg)
+ nWidth = m_xDlg->getAvailableWidth();
+
+ return nWidth;
+}
+
+sal_Int32 SAL_CALL SvtFilePicker::getAvailableHeight()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ sal_Int32 nHeight = 0;
+
+ if (m_xDlg)
+ nHeight = m_xDlg->getAvailableHeight();
+
+ return nHeight;
+}
+
+void SAL_CALL SvtFilePicker::setImage(sal_Int16 /*aImageFormat*/, const Any& rImage)
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (m_xDlg)
+ m_xDlg->setImage(rImage);
+}
+
+sal_Bool SAL_CALL SvtFilePicker::setShowState( sal_Bool )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ bool bRet = false;
+
+ if (m_xDlg)
+ {
+ // #97633 for the system filedialog it's
+ // useful to make the preview switchable
+ // because the preview occupies
+ // half of the size of the file listbox
+ // which is not the case here,
+ // so we (TRA/FS) decided not to make
+ // the preview window switchable because
+ // else we would have to change the layout
+ // of the file dialog dynamically
+ // support for set/getShowState is optionally
+ // see css::ui::dialogs::XFilePreview
+
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+
+sal_Bool SAL_CALL SvtFilePicker::getShowState()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ bool bRet = false;
+
+ if (m_xDlg)
+ bRet = m_xDlg->getShowState();
+
+ return bRet;
+}
+
+
+// XFilterGroupManager functions
+
+
+void SAL_CALL SvtFilePicker::appendFilterGroup( const OUString& sGroupTitle,
+ const Sequence< StringPair >& aFilters )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+
+ // check the names
+ if ( FilterNameExists( aFilters ) )
+ throw IllegalArgumentException(
+ "filter name exists",
+ getXWeak(), 1);
+
+ // ensure that we have a filter list
+ OUString sInitialCurrentFilter;
+ if ( aFilters.hasElements() )
+ sInitialCurrentFilter = aFilters[0].First;
+ ensureFilterList( sInitialCurrentFilter );
+
+ // append the filter
+ m_pFilterList->insert( m_pFilterList->end(), FilterEntry( sGroupTitle, aFilters ) );
+}
+
+
+// XFilterManager functions
+
+
+void SAL_CALL SvtFilePicker::appendFilter( const OUString& aTitle,
+ const OUString& aFilter )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ // check the name
+ if ( FilterNameExists( aTitle ) )
+ // TODO: a more precise exception message
+ throw IllegalArgumentException();
+
+ // ensure that we have a filter list
+ ensureFilterList( aTitle );
+
+ // append the filter
+ m_pFilterList->insert( m_pFilterList->end(), FilterEntry( aTitle, aFilter ) );
+}
+
+
+void SAL_CALL SvtFilePicker::setCurrentFilter( const OUString& aTitle )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( ! FilterNameExists( aTitle ) )
+ throw IllegalArgumentException();
+
+ m_aCurrentFilter = aTitle;
+
+ if (m_xDlg)
+ m_xDlg->SetCurFilter( aTitle );
+}
+
+
+OUString SAL_CALL SvtFilePicker::getCurrentFilter()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ OUString aFilter = m_xDlg ? m_xDlg->GetCurFilter() :
+ m_aCurrentFilter;
+ return aFilter;
+}
+
+
+// XInitialization functions
+
+
+void SAL_CALL SvtFilePicker::initialize( const Sequence< Any >& _rArguments )
+{
+ checkAlive();
+
+ Sequence< Any > aArguments( _rArguments.getLength());
+
+ m_nServiceType = TemplateDescription::FILEOPEN_SIMPLE;
+
+ if ( _rArguments.hasElements() )
+ {
+ // compatibility: one argument, type sal_Int16 , specifies the service type
+ int index = 0;
+ auto pArguments = aArguments.getArray();
+ if (_rArguments[0] >>= m_nServiceType)
+ {
+ // skip the first entry if it was the ServiceType, because it's not needed in OCommonPicker::initialize and it's not a NamedValue
+ NamedValue emptyNamedValue;
+ pArguments[0] <<= emptyNamedValue;
+ index = 1;
+
+ }
+ for ( int i = index; i < _rArguments.getLength(); i++)
+ {
+ NamedValue namedValue;
+ pArguments[i] = _rArguments[i];
+
+ if (aArguments[i] >>= namedValue )
+ {
+
+ if ( namedValue.Name == "StandardDir" )
+ {
+ OUString sStandardDir;
+
+ namedValue.Value >>= sStandardDir;
+
+ // Set the directory for the "back to the default dir" button
+ if ( !sStandardDir.isEmpty() )
+ {
+ m_aStandardDir = sStandardDir;
+ }
+ }
+ else if ( namedValue.Name == "DenyList" )
+ {
+ namedValue.Value >>= m_aDenyList;
+ }
+ }
+ }
+ }
+
+ // let the base class analyze the sequence (will call into implHandleInitializationArgument)
+ OCommonPicker::initialize( aArguments );
+}
+
+
+bool SvtFilePicker::implHandleInitializationArgument( const OUString& _rName, const Any& _rValue )
+{
+ if ( _rName == "TemplateDescription" )
+ {
+ m_nServiceType = TemplateDescription::FILEOPEN_SIMPLE;
+ OSL_VERIFY( _rValue >>= m_nServiceType );
+ return true;
+ }
+ if ( _rName == "StandardDir" )
+ {
+ OSL_VERIFY( _rValue >>= m_aStandardDir );
+ return true;
+ }
+
+ if ( _rName == "DenyList" )
+ {
+ OSL_VERIFY( _rValue >>= m_aDenyList );
+ return true;
+ }
+
+
+ return OCommonPicker::implHandleInitializationArgument( _rName, _rValue );
+}
+
+
+// XServiceInfo
+
+
+/* XServiceInfo */
+OUString SAL_CALL SvtFilePicker::getImplementationName()
+{
+ return "com.sun.star.svtools.OfficeFilePicker";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SvtFilePicker::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SvtFilePicker::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.dialogs.OfficeFilePicker" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_SvtFilePicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SvtFilePicker());
+}
+
+
+// SvtRemoteFilePicker
+
+SvtRemoteFilePicker::SvtRemoteFilePicker()
+{
+}
+
+std::shared_ptr<SvtFileDialog_Base> SvtRemoteFilePicker::implCreateDialog(weld::Window* pParent)
+{
+ PickerFlags nBits = getPickerFlags();
+
+ auto dialog = std::make_shared<RemoteFilesDialog>(pParent, nBits);
+
+ // Set StandardDir if present
+ if ( !m_aStandardDir.isEmpty())
+ {
+ OUString sStandardDir = m_aStandardDir;
+ dialog->SetStandardDir( sStandardDir );
+ dialog->SetDenyList( m_aDenyList );
+ }
+
+ return dialog;
+}
+
+// XServiceInfo
+
+
+/* XServiceInfo */
+OUString SAL_CALL SvtRemoteFilePicker::getImplementationName()
+{
+ return "com.sun.star.svtools.RemoteFilePicker";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SvtRemoteFilePicker::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SvtRemoteFilePicker::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.dialogs.RemoteFilePicker" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_SvtRemoteFilePicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SvtRemoteFilePicker());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeFilePicker.hxx b/fpicker/source/office/OfficeFilePicker.hxx
new file mode 100644
index 0000000000..b75a3f6368
--- /dev/null
+++ b/fpicker/source/office/OfficeFilePicker.hxx
@@ -0,0 +1,239 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <cppuhelper/implbase5.hxx>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/ui/dialogs/XFilePreview.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerListener.hpp>
+#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+
+
+#include "commonpicker.hxx"
+#include "pickercallbacks.hxx"
+
+#include <vector>
+
+class Dialog;
+struct FilterEntry;
+struct ElementEntry_Impl;
+enum class PickerFlags;
+
+typedef ::std::vector< FilterEntry > FilterList; // can be maintained more effectively
+typedef ::std::vector< ElementEntry_Impl > ElementList;
+
+typedef css::beans::StringPair UnoFilterEntry;
+typedef css::uno::Sequence< UnoFilterEntry > UnoFilterList; // can be transported more effectively
+
+// class SvtFilePicker ---------------------------------------------------
+
+typedef ::cppu::ImplHelper5 < css::ui::dialogs::XFilePicker3
+ , css::ui::dialogs::XFilePickerControlAccess
+ , css::ui::dialogs::XFilePreview
+ , css::lang::XServiceInfo
+ , css::ui::dialogs::XAsynchronousExecutableDialog
+ > SvtFilePicker_Base;
+
+class SvtFilePicker :public SvtFilePicker_Base
+ ,public ::svt::OCommonPicker
+ ,public ::svt::IFilePickerListener
+{
+protected:
+ std::unique_ptr<FilterList>
+ m_pFilterList;
+ std::unique_ptr<ElementList>
+ m_pElemList;
+
+ bool m_bMultiSelection;
+ sal_Int16 m_nServiceType;
+ OUString m_aDefaultName;
+ OUString m_aCurrentFilter;
+
+ OUString m_aOldDisplayDirectory;
+ OUString m_aOldHideDirectory;
+
+ OUString m_aStandardDir;
+ css::uno::Sequence< OUString >
+ m_aDenyList;
+
+ css::uno::Reference< css::ui::dialogs::XFilePickerListener >
+ m_xListener;
+ css::uno::Reference< css::ui::dialogs::XDialogClosedListener >
+ m_xDlgClosedListener;
+
+public:
+ SvtFilePicker();
+ virtual ~SvtFilePicker() override;
+
+
+ // disambiguate XInterface
+
+ DECLARE_XINTERFACE( )
+
+
+ // disambiguate XTypeProvider
+
+ DECLARE_XTYPEPROVIDER( )
+
+
+ // XExecutableDialog functions
+
+ virtual void SAL_CALL setTitle( const OUString& _rTitle ) override;
+ virtual sal_Int16 SAL_CALL execute( ) override;
+
+
+ // XAsynchronousExecutableDialog functions
+
+ virtual void SAL_CALL setDialogTitle( const OUString& _rTitle ) override;
+ virtual void SAL_CALL startExecuteModal( const css::uno::Reference< css::ui::dialogs::XDialogClosedListener >& xListener ) override;
+
+
+ // XFilePicker functions
+
+
+ virtual void SAL_CALL setMultiSelectionMode( sal_Bool bMode ) override;
+ virtual void SAL_CALL setDefaultName( const OUString& aName ) override;
+ virtual void SAL_CALL setDisplayDirectory( const OUString& aDirectory ) override;
+ virtual OUString SAL_CALL getDisplayDirectory() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getFiles() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSelectedFiles() override;
+
+
+ // XFilePickerControlAccess functions
+
+
+ virtual void SAL_CALL setValue( sal_Int16 ElementID, sal_Int16 ControlAction, const css::uno::Any& value ) override;
+ virtual css::uno::Any SAL_CALL getValue( sal_Int16 ElementID, sal_Int16 ControlAction ) override;
+ virtual void SAL_CALL setLabel( sal_Int16 ElementID, const OUString& aValue ) override;
+ virtual OUString SAL_CALL getLabel( sal_Int16 ElementID ) override;
+ virtual void SAL_CALL enableControl( sal_Int16 ElementID, sal_Bool bEnable ) override;
+
+
+ // XFilePickerNotifier functions
+
+
+ virtual void SAL_CALL addFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener ) override;
+ virtual void SAL_CALL removeFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener ) override;
+
+
+ // XFilePreview functions
+
+
+ virtual css::uno::Sequence< sal_Int16 > SAL_CALL getSupportedImageFormats() override;
+ virtual sal_Int32 SAL_CALL getTargetColorDepth() override;
+ virtual sal_Int32 SAL_CALL getAvailableWidth() override;
+ virtual sal_Int32 SAL_CALL getAvailableHeight() override;
+ virtual void SAL_CALL setImage( sal_Int16 aImageFormat, const css::uno::Any& aImage ) override;
+ virtual sal_Bool SAL_CALL setShowState( sal_Bool bShowState ) override;
+ virtual sal_Bool SAL_CALL getShowState() override;
+
+
+ // XFilterManager functions
+
+
+ virtual void SAL_CALL appendFilter( const OUString& aTitle, const OUString& aFilter ) override;
+ virtual void SAL_CALL setCurrentFilter( const OUString& aTitle ) override;
+ virtual OUString SAL_CALL getCurrentFilter() override;
+
+
+ // XFilterGroupManager functions
+
+ virtual void SAL_CALL appendFilterGroup( const OUString& sGroupTitle, const css::uno::Sequence< css::beans::StringPair >& aFilters ) override;
+
+
+ // these methods are here because they're ambiguous
+
+ virtual void SAL_CALL cancel() override
+ { ::svt::OCommonPicker::cancel(); }
+ virtual void SAL_CALL dispose() override
+ { ::svt::OCommonPicker::dispose(); }
+ virtual void SAL_CALL addEventListener(const css::uno::Reference<css::lang::XEventListener>& l) override
+ { ::svt::OCommonPicker::addEventListener(l); }
+ virtual void SAL_CALL removeEventListener(const css::uno::Reference<css::lang::XEventListener>& l) override
+ { ::svt::OCommonPicker::removeEventListener(l); }
+
+
+ // XInitialization functions
+
+
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+
+ // XServiceInfo functions
+
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+protected:
+
+ // OCommonPicker overridables
+
+ virtual std::shared_ptr<SvtFileDialog_Base> implCreateDialog( weld::Window* pParent ) override;
+ virtual sal_Int16 implExecutePicker( ) override;
+ virtual bool implHandleInitializationArgument(
+ const OUString& _rName,
+ const css::uno::Any& _rValue
+ ) override;
+
+protected:
+ PickerFlags getPickerFlags() const;
+ virtual void notify( sal_Int16 _nEventId, sal_Int16 _nControlId ) override;
+
+ bool FilterNameExists( const OUString& rTitle );
+ bool FilterNameExists( const UnoFilterList& _rGroupedFilters );
+
+ void ensureFilterList( const OUString& _rInitialCurrentFilter );
+
+ void prepareExecute( );
+
+ void DialogClosedHdl(sal_Int32 nResult);
+};
+
+// SvtRemoteFilePicker
+
+class SvtRemoteFilePicker : public SvtFilePicker
+{
+public:
+ SvtRemoteFilePicker();
+
+ virtual std::shared_ptr<SvtFileDialog_Base> implCreateDialog( weld::Window* pParent ) override;
+
+ // disambiguate XInterface
+
+ DECLARE_XINTERFACE( )
+
+ // disambiguate XTypeProvider
+
+ DECLARE_XTYPEPROVIDER( )
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeFolderPicker.cxx b/fpicker/source/office/OfficeFolderPicker.cxx
new file mode 100644
index 0000000000..c941d6cb63
--- /dev/null
+++ b/fpicker/source/office/OfficeFolderPicker.cxx
@@ -0,0 +1,175 @@
+/* -*- 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 "OfficeFolderPicker.hxx"
+
+#include "iodlg.hxx"
+
+#include <vector>
+#include <tools/urlobj.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/make_shared.hxx>
+#include <unotools/pathoptions.hxx>
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+SvtFolderPicker::SvtFolderPicker()
+{
+}
+
+SvtFolderPicker::~SvtFolderPicker()
+{
+}
+
+void SAL_CALL SvtFolderPicker::setTitle( const OUString& _rTitle )
+{
+ OCommonPicker::setTitle( _rTitle );
+}
+
+sal_Int16 SAL_CALL SvtFolderPicker::execute( )
+{
+ return OCommonPicker::execute();
+}
+
+void SAL_CALL SvtFolderPicker::setDialogTitle( const OUString& _rTitle)
+{
+ setTitle( _rTitle );
+}
+
+void SAL_CALL SvtFolderPicker::startExecuteModal( const Reference< css::ui::dialogs::XDialogClosedListener >& xListener )
+{
+ m_xListener = xListener;
+ prepareDialog();
+ prepareExecute();
+
+ m_xDlg->EnableAutocompletion();
+ if (!m_xDlg->PrepareExecute())
+ return;
+ weld::DialogController::runAsync(m_xDlg, [this](sal_Int32 nResult){
+ DialogClosedHdl(nResult);
+ });
+}
+
+std::shared_ptr<SvtFileDialog_Base> SvtFolderPicker::implCreateDialog( weld::Window* pParent )
+{
+ return o3tl::make_shared<SvtFileDialog>(pParent, PickerFlags::PathDialog);
+}
+
+sal_Int16 SvtFolderPicker::implExecutePicker( )
+{
+ prepareExecute();
+
+ // now we are ready to execute the dialog
+ m_xDlg->EnableAutocompletion( false );
+ return m_xDlg->run();
+}
+
+void SvtFolderPicker::prepareExecute()
+{
+ // set the default directory
+ if ( !m_aDisplayDirectory.isEmpty() )
+ m_xDlg->SetPath( m_aDisplayDirectory );
+ else
+ {
+ // set the default standard dir
+ INetURLObject aStdDirObj( SvtPathOptions().GetWorkPath() );
+ m_xDlg->SetPath( aStdDirObj.GetMainURL( INetURLObject::DecodeMechanism::NONE) );
+ }
+}
+
+void SvtFolderPicker::DialogClosedHdl(sal_Int32 nResult)
+{
+ if ( m_xListener.is() )
+ {
+ sal_Int16 nRet = static_cast<sal_Int16>(nResult);
+ css::ui::dialogs::DialogClosedEvent aEvent( *this, nRet );
+ m_xListener->dialogClosed( aEvent );
+ m_xListener.clear();
+ }
+}
+
+void SAL_CALL SvtFolderPicker::setDisplayDirectory( const OUString& aDirectory )
+{
+ m_aDisplayDirectory = aDirectory;
+}
+
+OUString SAL_CALL SvtFolderPicker::getDisplayDirectory()
+{
+ if (!m_xDlg)
+ return m_aDisplayDirectory;
+
+ std::vector<OUString> aPathList(m_xDlg->GetPathList());
+
+ if(!aPathList.empty())
+ return aPathList[0];
+
+ return OUString();
+}
+
+OUString SAL_CALL SvtFolderPicker::getDirectory()
+{
+ if (!m_xDlg)
+ return m_aDisplayDirectory;
+
+ std::vector<OUString> aPathList(m_xDlg->GetPathList());
+
+ if(!aPathList.empty())
+ return aPathList[0];
+
+ return OUString();
+}
+
+void SAL_CALL SvtFolderPicker::setDescription( const OUString& )
+{
+}
+
+void SvtFolderPicker::cancel()
+{
+ OCommonPicker::cancel();
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SvtFolderPicker::getImplementationName()
+{
+ return "com.sun.star.svtools.OfficeFolderPicker";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SvtFolderPicker::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SvtFolderPicker::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.dialogs.OfficeFolderPicker" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_SvtFolderPicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SvtFolderPicker());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeFolderPicker.hxx b/fpicker/source/office/OfficeFolderPicker.hxx
new file mode 100644
index 0000000000..7b98fb5bf9
--- /dev/null
+++ b/fpicker/source/office/OfficeFolderPicker.hxx
@@ -0,0 +1,89 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
+#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include "commonpicker.hxx"
+
+class Dialog;
+
+typedef
+ cppu::ImplInheritanceHelper<
+ svt::OCommonPicker, css::ui::dialogs::XFolderPicker2,
+ css::ui::dialogs::XAsynchronousExecutableDialog,
+ css::lang::XServiceInfo >
+ SvtFolderPicker_Base;
+
+class SvtFolderPicker: public SvtFolderPicker_Base
+{
+private:
+ css::uno::Reference< css::ui::dialogs::XDialogClosedListener >
+ m_xListener;
+
+ void prepareExecute();
+ void DialogClosedHdl(sal_Int32 nResult);
+
+public:
+ SvtFolderPicker();
+ virtual ~SvtFolderPicker() override;
+
+
+ // XFolderPicker2 functions
+
+ virtual void SAL_CALL setDisplayDirectory( const OUString& aDirectory ) override;
+ virtual OUString SAL_CALL getDisplayDirectory() override;
+ virtual OUString SAL_CALL getDirectory() override;
+ virtual void SAL_CALL setDescription( const OUString& aDescription ) override;
+
+ virtual void SAL_CALL cancel() override;
+
+
+ // XExecutableDialog functions
+
+ virtual void SAL_CALL setTitle( const OUString& _rTitle ) override;
+ virtual sal_Int16 SAL_CALL execute( ) override;
+
+
+ // XAsynchronousExecutableDialog functions
+
+ virtual void SAL_CALL setDialogTitle( const OUString& _rTitle ) override;
+ virtual void SAL_CALL startExecuteModal( const css::uno::Reference< css::ui::dialogs::XDialogClosedListener >& xListener ) override;
+
+
+ // XServiceInfo functions
+
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+protected:
+
+ // OCommonPicker overridables
+
+ virtual std::shared_ptr<SvtFileDialog_Base> implCreateDialog( weld::Window* pParent ) override;
+ virtual sal_Int16 implExecutePicker( ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/PlacesListBox.cxx b/fpicker/source/office/PlacesListBox.cxx
new file mode 100644
index 0000000000..86bd505179
--- /dev/null
+++ b/fpicker/source/office/PlacesListBox.cxx
@@ -0,0 +1,158 @@
+/* -*- 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 "PlacesListBox.hxx"
+#include <svtools/PlaceEditDialog.hxx>
+
+#include <bitmaps.hlst>
+
+PlacesListBox::PlacesListBox(std::unique_ptr<weld::TreeView> xControl,
+ std::unique_ptr<weld::Button> xAdd,
+ std::unique_ptr<weld::Button> xDel,
+ SvtFileDialog* pFileDlg)
+ : mpDlg(pFileDlg)
+ , mxImpl(std::move(xControl))
+ , mxAddBtn(std::move(xAdd))
+ , mxDelBtn(std::move(xDel))
+ , mnNbEditables(0)
+ , mbUpdated( false )
+{
+ Size aSize(mxImpl->get_approximate_digit_width() * 18,
+ mxImpl->get_height_rows(9));
+ mxImpl->set_size_request(aSize.Width(), aSize.Height());
+
+ mxImpl->connect_changed( LINK( this, PlacesListBox, Selection ) );
+ mxImpl->connect_row_activated( LINK( this, PlacesListBox, DoubleClick ) ) ;
+ mxImpl->connect_query_tooltip(LINK(this, PlacesListBox, QueryTooltipHdl));
+}
+
+PlacesListBox::~PlacesListBox( )
+{
+}
+
+void PlacesListBox::AppendPlace( const PlacePtr& pPlace )
+{
+ maPlaces.push_back( pPlace );
+ mxImpl->append_text(pPlace->GetName());
+ mxImpl->set_image(maPlaces.size() - 1, getEntryIcon(pPlace));
+
+ if(pPlace->IsEditable()) {
+ ++mnNbEditables;
+ mbUpdated = true;
+ }
+}
+
+bool PlacesListBox::IsUpdated() {
+ if(mbUpdated) {
+ mbUpdated = false;
+ return true;
+ }
+ return false;
+}
+
+void PlacesListBox::RemovePlace( sal_uInt16 nPos )
+{
+ if ( nPos < maPlaces.size() )
+ {
+ if(maPlaces[nPos]->IsEditable()) {
+ --mnNbEditables;
+ mbUpdated = true;
+ }
+ maPlaces.erase( maPlaces.begin() + nPos );
+ mxImpl->remove(nPos);
+ }
+}
+
+void PlacesListBox::RemoveSelectedPlace() {
+ RemovePlace(mxImpl->get_cursor_index());
+}
+
+void PlacesListBox::SetAddHdl( const Link<weld::Button&,void>& rHdl )
+{
+ mxAddBtn->connect_clicked( rHdl );
+}
+
+void PlacesListBox::SetDelHdl( const Link<weld::Button&,void>& rHdl )
+{
+ mxDelBtn->connect_clicked( rHdl );
+}
+
+void PlacesListBox::SetDelEnabled( bool enabled )
+{
+ mxDelBtn->set_sensitive( enabled );
+}
+
+OUString PlacesListBox::getEntryIcon( const PlacePtr& pPlace )
+{
+ OUString theImage = BMP_FILEDLG_PLACE_LOCAL;
+ if ( !pPlace->IsLocal( ) )
+ theImage = BMP_FILEDLG_PLACE_REMOTE;
+ return theImage;
+}
+
+IMPL_LINK_NOARG( PlacesListBox, Selection, weld::TreeView&, void )
+{
+ sal_uInt32 nSelected = mxImpl->get_cursor_index();
+ PlacePtr pPlace = maPlaces[nSelected];
+
+ if (pPlace->IsEditable())
+ mpDlg->RemovablePlaceSelected();
+ else
+ mpDlg->RemovablePlaceSelected(false);
+
+ updateView();
+}
+
+IMPL_LINK_NOARG( PlacesListBox, DoubleClick, weld::TreeView&, bool )
+{
+ sal_uInt16 nSelected = mxImpl->get_cursor_index();
+ PlacePtr pPlace = maPlaces[nSelected];
+ if ( !pPlace->IsEditable() || pPlace->IsLocal( ) )
+ return true;
+ PlaceEditDialog aDlg(mpDlg->getDialog(), pPlace);
+ short aRetCode = aDlg.run();
+ switch (aRetCode)
+ {
+ case RET_OK :
+ {
+ pPlace->SetName ( aDlg.GetServerName() );
+ pPlace->SetUrl( aDlg.GetServerUrl() );
+ mbUpdated = true;
+ break;
+ }
+ case RET_NO :
+ {
+ RemovePlace(nSelected);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+}
+
+IMPL_LINK(PlacesListBox, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString)
+{
+ const OUString sText = mxImpl->get_text(rIter);
+ for (const auto& pPlace : maPlaces)
+ {
+ if (pPlace->GetName() == sText)
+ return pPlace->GetUrlObject().GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);
+ }
+ return OUString();
+}
+
+void PlacesListBox::updateView( )
+{
+ sal_uInt32 nSelected = mxImpl->get_cursor_index();
+ PlacePtr pPlace = maPlaces[nSelected];
+ mpDlg->OpenURL_Impl( pPlace->GetUrl( ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/PlacesListBox.hxx b/fpicker/source/office/PlacesListBox.hxx
new file mode 100644
index 0000000000..934126072d
--- /dev/null
+++ b/fpicker/source/office/PlacesListBox.hxx
@@ -0,0 +1,66 @@
+/* -*- 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/.
+ */
+#pragma once
+
+#include "iodlg.hxx"
+
+#include <svtools/place.hxx>
+#include <vcl/weld.hxx>
+
+#include <memory>
+#include <vector>
+
+typedef std::shared_ptr<Place> PlacePtr;
+
+class PlacesListBox;
+
+/** ListBox to handle Places.
+ */
+class PlacesListBox
+{
+private:
+ std::vector<PlacePtr> maPlaces;
+ SvtFileDialog* mpDlg;
+ std::unique_ptr<weld::TreeView> mxImpl;
+ std::unique_ptr<weld::Button> mxAddBtn;
+ std::unique_ptr<weld::Button> mxDelBtn;
+ sal_Int32 mnNbEditables;
+ bool mbUpdated;
+
+public:
+ PlacesListBox(std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::Button> xAddBtn,
+ std::unique_ptr<weld::Button> xDelBtn,
+ SvtFileDialog* pFileDlg);
+ ~PlacesListBox();
+
+ void AppendPlace( const PlacePtr& pPlace );
+ void RemovePlace( sal_uInt16 nPos );
+ void RemoveSelectedPlace();
+ sal_Int32 GetNbEditablePlaces() const { return mnNbEditables;}
+ bool IsUpdated();
+ const std::vector<PlacePtr>& GetPlaces() const { return maPlaces;}
+
+ void SetAddHdl( const Link<weld::Button&,void>& rHdl );
+ void SetDelHdl( const Link<weld::Button&,void>& rHdl );
+ void SetDelEnabled( bool enabled );
+ void updateView( );
+
+ void set_help_id(const OUString& rHelpId) { mxImpl->set_help_id(rHelpId); }
+
+private:
+
+ static OUString getEntryIcon(const PlacePtr& pPlace);
+
+ DECL_LINK( Selection, weld::TreeView&, void );
+ DECL_LINK( DoubleClick, weld::TreeView&, bool );
+ DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/QueryFolderName.hxx b/fpicker/source/office/QueryFolderName.hxx
new file mode 100644
index 0000000000..81bc4ad105
--- /dev/null
+++ b/fpicker/source/office/QueryFolderName.hxx
@@ -0,0 +1,39 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class QueryFolderNameDialog : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::Entry> m_xNameEdit;
+ std::unique_ptr<weld::Button> m_xOKBtn;
+
+ DECL_LINK(OKHdl, weld::Button&, void);
+ DECL_LINK(NameHdl, weld::Entry&, void);
+
+public:
+ QueryFolderNameDialog(weld::Window* _pParent, const OUString& rTitle,
+ const OUString& rDefaultText);
+ virtual ~QueryFolderNameDialog() override;
+ OUString GetName() const { return m_xNameEdit->get_text(); }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/RemoteFilesDialog.cxx b/fpicker/source/office/RemoteFilesDialog.cxx
new file mode 100644
index 0000000000..2b7dbd3d46
--- /dev/null
+++ b/fpicker/source/office/RemoteFilesDialog.cxx
@@ -0,0 +1,1199 @@
+/* -*- 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, "fps/ui/remotefilesdialog.ui", "RemoteFilesDialog" )
+ , m_xContext( comphelper::getProcessComponentContext() )
+ , m_xMasterPasswd( PasswordContainer::create( m_xContext ) )
+ , m_bIsInExecute( false )
+ , m_xOk_btn(m_xBuilder->weld_button("ok"))
+ , m_xCancel_btn(m_xBuilder->weld_button("cancel"))
+ , m_xManageServices(m_xBuilder->weld_menu_button("add_service_btn"))
+ , m_xServices_lb(m_xBuilder->weld_combo_box("services_lb"))
+ , m_xPathContainer(m_xBuilder->weld_container("breadcrumb_container"))
+ , m_xNewFolder(m_xBuilder->weld_button("new_folder"))
+ , m_xListView_btn(m_xBuilder->weld_toggle_button("list_view"))
+ , m_xIconView_btn(m_xBuilder->weld_toggle_button("icon_view"))
+ , m_xFilter_lb(m_xBuilder->weld_combo_box("filter_lb"))
+ , m_xName_ed(new AutocompleteEdit(m_xBuilder->weld_entry("filename")))
+{
+ 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("container");
+ 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("fileview"),
+ m_xBuilder->weld_icon_view("iconview"),
+ 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("foldertree"), 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( "UserData",
+ 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("change_password", bEnable);
+ m_xManageServices->set_item_visible("edit_service", bEnable);
+ m_xManageServices->set_item_visible("delete_service", bEnable);
+ m_xManageServices->set_item_visible("change_password", 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 "Google Drive";
+ else if( sHost.startsWith( ALFRESCO_CLOUD_BASE_URL ) )
+ return "Alfresco Cloud";
+ else if( sHost.startsWith( ONEDRIVE_BASE_URL ) )
+ return "OneDrive";
+
+ return "CMIS";
+ }
+ case INetProtocol::Smb:
+ return "Windows Share";
+ case INetProtocol::File:
+ return "SSH";
+ case INetProtocol::Http:
+ return "WebDAV";
+ case INetProtocol::Https:
+ return "WebDAV";
+ case INetProtocol::Generic:
+ return "SSH";
+ 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( "UserData" );
+ 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 )
+{
+ OUString sName = rFilter;
+
+ m_aFilters.emplace_back( rFilter, rType );
+ if (rType.isEmpty())
+ m_xFilter_lb->append_separator("");
+ else
+ m_xFilter_lb->append_text(sName);
+
+ 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( "" );
+
+ 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("change_password", 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("change_password", 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() )
+ {
+ OUString aNewPass = pPasswordRequest->getPassword();
+ Sequence<OUString> aPasswd { aNewPass };
+
+ 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( "" );
+ }
+ }
+
+ 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 >& rFolders = m_xFileView->GetContent();
+ ::std::vector< std::pair< OUString, OUString > > aFolders;
+
+ m_xName_ed->ClearEntries();
+
+ for(const auto & rFolder : rFolders)
+ {
+ //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: */
diff --git a/fpicker/source/office/RemoteFilesDialog.hxx b/fpicker/source/office/RemoteFilesDialog.hxx
new file mode 100644
index 0000000000..8c4fa0a4cb
--- /dev/null
+++ b/fpicker/source/office/RemoteFilesDialog.hxx
@@ -0,0 +1,186 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include "autocmpledit.hxx"
+#include <svtools/place.hxx>
+
+#include <unotools/viewoptions.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/task/XPasswordContainer2.hpp>
+
+#include <vector>
+
+#include "asyncfilepicker.hxx"
+#include "fpdialogbase.hxx"
+#include "breadcrumb.hxx"
+#include "fileview.hxx"
+#include "foldertree.hxx"
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ui::dialogs;
+
+enum SvtRemoteDlgMode
+{
+ REMOTEDLG_MODE_OPEN = 0,
+ REMOTEDLG_MODE_SAVE = 1
+};
+
+enum SvtRemoteDlgType
+{
+ REMOTEDLG_TYPE_FILEDLG = 0,
+ REMOTEDLG_TYPE_PATHDLG = 1
+};
+
+typedef std::shared_ptr< Place > ServicePtr;
+
+class RemoteFilesDialog : public SvtFileDialog_Base
+{
+public:
+ RemoteFilesDialog( weld::Window* pParent, PickerFlags nBits );
+ virtual ~RemoteFilesDialog() override;
+
+ virtual short run() override;
+
+ // SvtFileDialog_Base
+
+ virtual SvtFileView* GetView() override;
+
+ virtual void SetHasFilename( bool ) override;
+ virtual void SetDenyList( const css::uno::Sequence< OUString >& rDenyList ) override;
+ virtual const css::uno::Sequence< OUString >& GetDenyList() const override;
+ virtual void SetStandardDir( const OUString& rStdDir ) override;
+ virtual const OUString& GetStandardDir() const override;
+ virtual void SetPath( const OUString& rNewURL ) override;
+ virtual const OUString& GetPath() override;
+ virtual std::vector<OUString> GetPathList() const override;
+ virtual bool ContentIsFolder( const OUString& rURL ) override;
+ bool ContentIsDocument(const OUString& rURL);
+
+ virtual OUString getCurrentFileText() const override;
+ virtual void setCurrentFileText( const OUString& rText, bool bSelectAll = false ) override;
+
+ virtual void AddFilter( const OUString& rFilter, const OUString& rType ) override;
+ virtual void AddFilterGroup( const OUString& _rFilter,
+ const css::uno::Sequence< css::beans::StringPair >& rFilters ) override;
+ virtual OUString GetCurFilter() const override;
+ virtual void SetCurFilter( const OUString& rFilter ) override;
+ virtual void FilterSelect() override;
+
+ virtual void SetFileCallback( ::svt::IFilePickerListener *pNotifier ) override;
+ virtual void onAsyncOperationStarted() override;
+ virtual void onAsyncOperationFinished() override;
+ virtual void UpdateControls( const OUString& rURL ) override;
+
+ virtual void EnableAutocompletion( bool = true) override;
+
+ virtual sal_Int32 getAvailableWidth() override;
+ virtual sal_Int32 getAvailableHeight() override;
+
+ virtual void setImage( const css::uno::Any& rImage ) override;
+
+ virtual bool getShowState() override;
+
+ virtual weld::Widget* getControl( sal_Int16 nControlId, bool bLabelControl = false ) const override;
+ virtual void enableControl( sal_Int16 nControlId, bool bEnable ) override;
+ virtual OUString getCurFilter( ) const override;
+
+private:
+ Reference< XComponentContext > m_xContext;
+ Reference< XPasswordContainer2 > m_xMasterPasswd;
+
+ SvtRemoteDlgMode m_eMode;
+ SvtRemoteDlgType m_eType;
+ bool m_bIsUpdated;
+ bool m_bIsConnected;
+ bool m_bServiceChanged;
+
+ OUString m_sIniKey;
+
+ bool m_bIsInExecute;
+
+ OUString m_sPath;
+ OUString m_sStdDir;
+ OUString m_sRootLabel;
+ OUString m_sLastServiceUrl;
+ int m_nCurrentFilter;
+
+ ::rtl::Reference< ::svt::AsyncPickerAction > m_pCurrentAsyncAction;
+
+ css::uno::Sequence< OUString > m_aDenyList;
+
+ std::unique_ptr<weld::Button> m_xOk_btn;
+ std::unique_ptr<weld::Button> m_xCancel_btn;
+ std::unique_ptr<weld::MenuButton> m_xManageServices;
+ std::unique_ptr<weld::ComboBox> m_xServices_lb;
+ std::unique_ptr<weld::Container> m_xPathContainer;
+ std::unique_ptr<Breadcrumb> m_xPath;
+ std::unique_ptr<weld::Button> m_xNewFolder;
+ std::unique_ptr<weld::ToggleButton> m_xListView_btn;
+ std::unique_ptr<weld::ToggleButton> m_xIconView_btn;
+ std::unique_ptr<FolderTree> m_xTreeView;
+ std::unique_ptr<SvtFileView> m_xFileView;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<weld::ComboBox> m_xFilter_lb;
+ std::unique_ptr<AutocompleteEdit> m_xName_ed;
+
+ std::vector< ServicePtr > m_aServices;
+ std::vector< std::pair< OUString, OUString > > m_aFilters;
+
+ void InitSize();
+
+ void FillServicesListbox();
+
+ /* If failure returns < 0 */
+ int GetSelectedServicePos();
+
+ void OpenURL( OUString const & sURL );
+
+ OUString AddFileExtension(const OUString& rFileName);
+
+ void EnableExtraMenuItems(bool bEnable);
+ void EnableControls();
+ void DisableControls();
+
+ void SavePassword(const OUString& rURL, const OUString& rUser,
+ const OUString& rPassword, bool bPersistent);
+
+ void AddService();
+
+ DECL_LINK ( SelectServiceHdl, weld::ComboBox&, void );
+ DECL_LINK ( EditServiceMenuHdl, const OUString&, void );
+
+ DECL_LINK( DoubleClickHdl, SvtFileView*, bool );
+ DECL_LINK( SelectHdl, SvtFileView*, void );
+
+ DECL_LINK( FileNameGetFocusHdl, weld::Widget&, void );
+ DECL_LINK( FileNameModifyHdl, weld::Entry&, void );
+
+ DECL_LINK( SelectFilterHdl, weld::ComboBox&, void );
+
+ DECL_LINK( TreeSelectHdl, weld::TreeView&, void );
+
+ DECL_LINK( SelectBreadcrumbHdl, Breadcrumb*, bool );
+
+ DECL_LINK( NewFolderHdl, weld::Button&, void );
+ DECL_LINK( IconViewHdl, weld::Button&, void );
+ DECL_LINK( ListViewHdl, weld::Button&, void );
+
+ DECL_LINK( OkHdl, weld::Button&, void );
+ DECL_LINK( CancelHdl, weld::Button&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/asyncfilepicker.cxx b/fpicker/source/office/asyncfilepicker.cxx
new file mode 100644
index 0000000000..349d586b89
--- /dev/null
+++ b/fpicker/source/office/asyncfilepicker.cxx
@@ -0,0 +1,183 @@
+/* -*- 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 "asyncfilepicker.hxx"
+#include "fileview.hxx"
+#include "iodlg.hxx"
+#include <tools/debug.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+
+
+namespace svt
+{
+ AsyncPickerAction::AsyncPickerAction( SvtFileDialog_Base* _pDialog, SvtFileView* _pView, const Action _eAction )
+ :m_eAction ( _eAction )
+ ,m_pView ( _pView )
+ ,m_pDialog ( _pDialog )
+ ,m_bRunning ( false )
+ {
+ assert( m_pDialog && "AsyncPickerAction::AsyncPickerAction: invalid dialog!" );
+ assert( m_pView && "AsyncPickerAction::AsyncPickerAction: invalid view!" );
+ }
+
+
+ AsyncPickerAction::~AsyncPickerAction()
+ {
+ }
+
+
+ void AsyncPickerAction::cancel()
+ {
+ DBG_TESTSOLARMUTEX();
+ // if this asserts, we'd need to have an own mutex per instance
+
+ OSL_ENSURE( m_bRunning, "AsyncPickerAction::cancel: not running" );
+ if ( m_pView )
+ m_pView->CancelRunningAsyncAction();
+ }
+
+
+ void AsyncPickerAction::execute(
+ const OUString& _rURL,
+ const OUString& _rFilter,
+ sal_Int32 _nMinTimeout,
+ sal_Int32 _nMaxTimeout,
+ const css::uno::Sequence< OUString >& rDenyList )
+ {
+ DBG_TESTSOLARMUTEX();
+ // if this asserts, we'd need to have an own mutex per instance
+
+ sal_Int32 nMinTimeout = _nMinTimeout;
+ sal_Int32 nMaxTimeout = _nMaxTimeout;
+ // normalizations
+ if ( nMinTimeout < 0 )
+ // if negative, this is considered as "do it synchronously"
+ nMinTimeout = 0;
+ else if ( nMinTimeout < 1000 )
+ nMinTimeout = 1000;
+ if ( nMaxTimeout <= nMinTimeout )
+ nMaxTimeout = nMinTimeout + 30000;
+
+ std::unique_ptr< FileViewAsyncAction > pActionDescriptor;
+ if ( nMinTimeout )
+ {
+ pActionDescriptor.reset( new FileViewAsyncAction );
+ pActionDescriptor->nMinTimeout = nMinTimeout;
+ pActionDescriptor->nMaxTimeout = nMaxTimeout;
+ pActionDescriptor->aFinishHandler = LINK( this, AsyncPickerAction, OnActionDone );
+ }
+
+ FileViewResult eResult = eFailure;
+ m_sURL = _rURL;
+ switch ( m_eAction )
+ {
+ case ePrevLevel:
+ eResult = m_pView->PreviousLevel( pActionDescriptor.get() );
+ break;
+
+ case eOpenURL:
+ eResult = m_pView->Initialize( _rURL, _rFilter, pActionDescriptor.get(), rDenyList );
+ break;
+
+ case eExecuteFilter:
+ // preserve the filename (FS: why?)
+ m_sFileName = m_pDialog->getCurrentFileText();
+ // execute the new filter
+ eResult = m_pView->ExecuteFilter( _rFilter, pActionDescriptor.get() );
+ break;
+
+ default:
+ OSL_FAIL( "AsyncPickerAction::execute: unknown action!" );
+ break;
+ }
+
+ acquire();
+ if ( ( eResult == eSuccess ) || ( eResult == eFailure ) )
+ {
+ // the handler is only called if the action could not be finished within
+ // the given minimum time period. In case of success, we need to call it
+ // explicitly
+ OnActionDone( reinterpret_cast< void* >( eResult ) );
+ }
+ else if ( eResult == eStillRunning )
+ {
+ m_bRunning = true;
+ m_pDialog->onAsyncOperationStarted();
+ }
+ }
+
+
+ IMPL_LINK( AsyncPickerAction, OnActionDone, void*, pEmptyArg, void )
+ {
+ DBG_TESTSOLARMUTEX();
+ // if this asserts, we'd need to have an own mutex per instance
+
+ FileViewResult eResult = static_cast< FileViewResult >( reinterpret_cast< sal_IntPtr >( pEmptyArg ) );
+ OSL_ENSURE( eStillRunning != eResult, "AsyncPickerAction::OnActionDone: invalid result!" );
+
+ // release once (since we acquired in |execute|), but keep alive until the
+ // end of the method
+ ::rtl::Reference< AsyncPickerAction > xKeepAlive( this );
+ release();
+
+ m_pDialog->onAsyncOperationFinished();
+ m_bRunning = true;
+
+ if ( eFailure == eResult )
+ // TODO: do we need some kind of cleanup here?
+ return;
+
+ if ( eTimeout == eResult )
+ {
+ SvtFileDialog::displayIOException( m_sURL, css::ucb::IOErrorCode_CANT_READ );
+ return;
+ }
+
+ OSL_ENSURE( eSuccess == eResult, "AsyncPickerAction::OnActionDone: what else valid results are there?" );
+
+ switch ( m_eAction )
+ {
+ case ePrevLevel:
+ case eOpenURL:
+ m_pDialog->UpdateControls( m_pView->GetViewURL() );
+ break;
+
+ case eExecuteFilter:
+ // restore the filename
+ m_pView->SetNoSelection();
+ m_pDialog->setCurrentFileText( m_sFileName, true );
+
+ // notify listeners
+ m_pDialog->FilterSelect();
+ break;
+
+ default:
+ OSL_FAIL( "AsyncPickerAction::OnActionDone: unknown action!" );
+ break;
+ }
+ }
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/asyncfilepicker.hxx b/fpicker/source/office/asyncfilepicker.hxx
new file mode 100644
index 0000000000..e2bac1208f
--- /dev/null
+++ b/fpicker/source/office/asyncfilepicker.hxx
@@ -0,0 +1,93 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <tools/link.hxx>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <salhelper/simplereferenceobject.hxx>
+
+class SvtFileView;
+class SvtFileDialog_Base;
+
+namespace svt
+{
+
+
+ //= AsyncPickerAction
+
+ class AsyncPickerAction : public salhelper::SimpleReferenceObject
+ {
+ public:
+ enum Action
+ {
+ ePrevLevel,
+ eOpenURL,
+ eExecuteFilter
+ };
+
+ private:
+ Action m_eAction;
+ SvtFileView* m_pView;
+ SvtFileDialog_Base* m_pDialog;
+ OUString m_sURL;
+ OUString m_sFileName;
+ bool m_bRunning;
+
+ public:
+ AsyncPickerAction( SvtFileDialog_Base* _pDialog, SvtFileView* _pView, const Action _eAction );
+
+ /** executes the action
+
+ @param _nMinTimeout
+ the minimum timeout to wait, in milliseconds. If negative, the action will we done
+ synchronously. If between 0 and 999, it will be corrected to 1000, means the
+ smallest valid value is 1000 (which equals one second).
+ @param _nMaxTimeout
+ The maximum time to wait for a result, in milliseconds. If there's no result of
+ the action within the given time frame, the action will be cancelled.
+ If smaller than or equal to <arg>_nMinTimeout</arg>, it will be corrected to
+ <arg>_nMinTimeout</arg> + 30000.
+ */
+ void execute(
+ const OUString& _rURL,
+ const OUString& _rFilter,
+ sal_Int32 _nMinTimeout,
+ sal_Int32 _nMaxTimeout,
+ const css::uno::Sequence< OUString >& rDenyList );
+
+ /// cancels the running action
+ void cancel();
+
+ protected:
+ virtual ~AsyncPickerAction() override;
+
+ private:
+ DECL_LINK( OnActionDone, void*, void );
+
+ AsyncPickerAction( const AsyncPickerAction& ) = delete;
+ AsyncPickerAction& operator=( const AsyncPickerAction& ) = delete;
+ };
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/autocmpledit.cxx b/fpicker/source/office/autocmpledit.cxx
new file mode 100644
index 0000000000..89a2d0b0c2
--- /dev/null
+++ b/fpicker/source/office/autocmpledit.cxx
@@ -0,0 +1,104 @@
+/* -*- 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 <vcl/event.hxx>
+#include "autocmpledit.hxx"
+
+AutocompleteEdit::AutocompleteEdit(std::unique_ptr<weld::Entry> xEntry)
+ : m_xEntry(std::move(xEntry))
+ , m_aChangedIdle("fpicker::AutocompleteEdit m_aChangedIdle")
+ , m_nLastCharCode(0)
+{
+ m_xEntry->connect_changed(LINK(this, AutocompleteEdit, ChangedHdl));
+ m_xEntry->connect_key_press(LINK(this, AutocompleteEdit, KeyInputHdl));
+
+ m_aChangedIdle.SetInvokeHandler(LINK(this, AutocompleteEdit, TryAutoComplete));
+}
+
+IMPL_LINK(AutocompleteEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ m_nLastCharCode = rKEvt.GetKeyCode().GetCode();
+ return false;
+}
+
+IMPL_LINK_NOARG(AutocompleteEdit, ChangedHdl, weld::Entry&, void)
+{
+ m_aChangeHdl.Call(*m_xEntry);
+
+ switch (m_nLastCharCode)
+ {
+ case css::awt::Key::DELETE_WORD_BACKWARD:
+ case css::awt::Key::DELETE_WORD_FORWARD:
+ case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
+ case css::awt::Key::DELETE_TO_END_OF_LINE:
+ case KEY_BACKSPACE:
+ case KEY_DELETE:
+ m_aChangedIdle.Stop();
+ break;
+ default:
+ m_aChangedIdle.Start(); //launch this to happen on idle after cursor position will have been set
+ break;
+ }
+}
+
+void AutocompleteEdit::AddEntry( const OUString& rEntry )
+{
+ m_aEntries.push_back( rEntry );
+}
+
+void AutocompleteEdit::ClearEntries()
+{
+ m_aEntries.clear();
+ m_aMatching.clear();
+}
+
+IMPL_LINK_NOARG(AutocompleteEdit, TryAutoComplete, Timer *, void)
+{
+ OUString aCurText = m_xEntry->get_text();
+
+ int nStartPos, nEndPos;
+ m_xEntry->get_selection_bounds(nStartPos, nEndPos);
+ if (std::max(nStartPos, nEndPos) != aCurText.getLength())
+ return;
+
+ auto nLen = std::min(nStartPos, nEndPos);
+ aCurText = aCurText.copy( 0, nLen );
+ if( aCurText.isEmpty() )
+ return;
+
+ if( !m_aEntries.empty() )
+ {
+ if( Match( aCurText ) )
+ {
+ m_xEntry->set_text(m_aMatching[0]);
+ auto nNewLen = m_aMatching[0].getLength();
+ m_xEntry->select_region(nLen, nNewLen);
+ }
+ }
+}
+
+bool AutocompleteEdit::Match( std::u16string_view rText )
+{
+ bool bRet = false;
+
+ m_aMatching.clear();
+
+ for(const OUString & rEntry : m_aEntries)
+ {
+ if( rEntry.startsWithIgnoreAsciiCase( rText ) )
+ {
+ m_aMatching.push_back( rEntry );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/autocmpledit.hxx b/fpicker/source/office/autocmpledit.hxx
new file mode 100644
index 0000000000..3eb79eb14a
--- /dev/null
+++ b/fpicker/source/office/autocmpledit.hxx
@@ -0,0 +1,53 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <vcl/idle.hxx>
+#include <vcl/weld.hxx>
+#include <vector>
+
+class AutocompleteEdit
+{
+private:
+ std::unique_ptr<weld::Entry> m_xEntry;
+
+ std::vector<OUString> m_aEntries;
+ std::vector<OUString> m_aMatching;
+ Idle m_aChangedIdle;
+ Link<weld::Entry&, void> m_aChangeHdl;
+ sal_uInt16 m_nLastCharCode;
+
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(ChangedHdl, weld::Entry&, void);
+ DECL_LINK(TryAutoComplete, Timer*, void);
+
+ bool Match(std::u16string_view rText);
+
+public:
+ AutocompleteEdit(std::unique_ptr<weld::Entry> xEntry);
+
+ void show() { m_xEntry->show(); }
+ void set_sensitive(bool bSensitive) { m_xEntry->set_sensitive(bSensitive); }
+ OUString get_text() const { return m_xEntry->get_text(); }
+ void set_text(const OUString& rText) { m_xEntry->set_text(rText); }
+ void grab_focus() { m_xEntry->grab_focus(); }
+ void select_region(int nStartPos, int nEndPos) { m_xEntry->select_region(nStartPos, nEndPos); }
+
+ void connect_changed(const Link<weld::Entry&, void>& rLink) { m_aChangeHdl = rLink; }
+ void connect_focus_in(const Link<weld::Widget&, void>& rLink)
+ {
+ m_xEntry->connect_focus_in(rLink);
+ }
+
+ void AddEntry(const OUString& rEntry);
+ void ClearEntries();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/breadcrumb.cxx b/fpicker/source/office/breadcrumb.cxx
new file mode 100644
index 0000000000..0b27367d8a
--- /dev/null
+++ b/fpicker/source/office/breadcrumb.cxx
@@ -0,0 +1,246 @@
+/* -*- 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 <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include "breadcrumb.hxx"
+
+Breadcrumb::Breadcrumb(weld::Container* pParent)
+ : m_pParent(pParent)
+ , m_nMaxWidth(m_pParent->get_preferred_size().Width())
+{
+ m_pParent->connect_size_allocate(LINK(this, Breadcrumb, SizeAllocHdl));
+ m_eMode = SvtBreadcrumbMode::ONLY_CURRENT_PATH;
+ appendField(); // root
+}
+
+IMPL_LINK(Breadcrumb, SizeAllocHdl, const Size&, rSize, void)
+{
+ m_nMaxWidth = rSize.Width();
+}
+
+Breadcrumb::~Breadcrumb()
+{
+ m_pParent->connect_size_allocate(Link<const Size&, void>());
+}
+
+void Breadcrumb::EnableFields( bool bEnable )
+{
+ if( bEnable )
+ {
+ INetURLObject aURL( m_aCurrentURL );
+ int nSegments = aURL.getSegmentCount();
+ m_aSegments[nSegments]->m_xLink->set_sensitive(false);
+ }
+}
+
+void Breadcrumb::connect_clicked( const Link<Breadcrumb*,bool>& rLink )
+{
+ m_aClickHdl = rLink;
+}
+
+const OUString& Breadcrumb::GetHdlURL() const
+{
+ return m_sClickedURL;
+}
+
+void Breadcrumb::SetRootName( const OUString& rURL )
+{
+ m_sRootName = rURL;
+
+ // we changed root - clear all fields
+ for (size_t i = 1; i < m_aSegments.size(); ++i)
+ {
+ m_aSegments[i]->m_xLink->set_label("");
+
+ m_aSegments[i]->m_xLink->hide();
+ m_aSegments[i]->m_xSeparator->hide();
+ m_aSegments[i]->m_xLink->set_sensitive(true);
+ }
+}
+
+void Breadcrumb::SetURL( const OUString& rURL )
+{
+ m_aCurrentURL = rURL;
+ INetURLObject aURL(rURL);
+ aURL.setFinalSlash();
+
+ bool bClear = m_eMode == SvtBreadcrumbMode::ONLY_CURRENT_PATH;
+
+ int nSegments = aURL.getSegmentCount();
+
+ size_t nVecSizeRequired = nSegments + 1;
+
+ while (m_aSegments.size() < nVecSizeRequired)
+ appendField();
+
+ // fill the fields under root
+ for (int i = nSegments; i; --i)
+ {
+ OUString sLabel = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset);
+ OUString sLink = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ if (m_eMode == SvtBreadcrumbMode::ALL_VISITED)
+ {
+ if( m_aSegments[i]->m_xLink->get_label() != sLabel )
+ bClear = true;
+ }
+
+ m_aSegments[i]->m_xLink->hide();
+ m_aSegments[i]->m_xLink->set_label(sLabel);
+ m_aSegments[i]->m_xLink->set_sensitive(true);
+ m_aSegments[i]->m_xLink->set_uri(sLink);
+ m_aUris[m_aSegments[i]->m_xLink.get()] = sLink;
+
+ m_aSegments[i]->m_xSeparator->hide();
+
+ aURL.removeSegment();
+ }
+
+ OUString sRootPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::WithCharset);
+
+ // root field
+ m_aSegments[0]->m_xLink->set_label( m_sRootName );
+ m_aSegments[0]->m_xLink->set_sensitive(true);
+ m_aSegments[0]->m_xLink->set_uri(sRootPath);
+ m_aUris[m_aSegments[0]->m_xLink.get()] = sRootPath;
+
+ // clear unused fields
+ for (size_t i = nSegments + 1; i < m_aSegments.size(); i++ )
+ {
+ if( bClear )
+ m_aSegments[i]->m_xLink->set_label( "" );
+
+ m_aSegments[i]->m_xLink->hide();
+ m_aSegments[i]->m_xSeparator->hide();
+ m_aSegments[i]->m_xLink->set_sensitive(true);
+ }
+
+ // show fields
+ unsigned int nSeparatorWidth = m_aSegments[0]->m_xSeparator->get_preferred_size().Width();
+ unsigned int nCurrentWidth = 0;
+ unsigned int nLastVisible = nSegments;
+
+ bool bRight = ( m_eMode == SvtBreadcrumbMode::ALL_VISITED );
+ bool bLeft = true;
+
+ int i = 0;
+
+ while( bLeft || bRight )
+ {
+ if( nSegments - i == -1 )
+ bLeft = false;
+
+ if( bLeft )
+ {
+ unsigned int nIndex = nSegments - i;
+
+ if( showField( nIndex, m_nMaxWidth - nCurrentWidth ) )
+ {
+ nCurrentWidth += m_aSegments[nIndex]->m_xLink->get_preferred_size().Width()
+ + nSeparatorWidth + 2*SPACING;
+ }
+ else
+ {
+ // label is too long
+ if( nSegments != 0 )
+ {
+ m_aSegments[0]->m_xLink->set_label("...");
+ m_aSegments[0]->m_xLink->set_sensitive(false);
+ }
+ bLeft = false;
+ }
+ }
+
+ if( nSegments + i == static_cast<int>(m_aSegments.size()) )
+ bRight = false;
+
+ if( i != 0 && bRight )
+ {
+ unsigned int nIndex = nSegments + i;
+
+ if( m_aSegments[nIndex]->m_xLink->get_label().isEmpty() )
+ {
+ bRight = false;
+ }
+ else if( showField( nIndex, m_nMaxWidth - nCurrentWidth ) )
+ {
+ nCurrentWidth += m_aSegments[nIndex]->m_xLink->get_preferred_size().Width()
+ + nSeparatorWidth + 3*SPACING;
+ nLastVisible = nIndex;
+ }
+ else
+ {
+ bRight = false;
+ }
+ }
+
+ i++;
+ }
+
+ // current dir should be inactive
+ m_aSegments[nSegments]->m_xLink->set_sensitive(false);
+
+ // hide last separator
+ m_aSegments[nLastVisible]->m_xSeparator->hide();
+}
+
+void Breadcrumb::SetMode( SvtBreadcrumbMode eMode )
+{
+ m_eMode = eMode;
+}
+
+void Breadcrumb::appendField()
+{
+ m_aSegments.emplace_back(std::make_unique<BreadcrumbPath>(m_pParent));
+ size_t nIndex = m_aSegments.size() - 1;
+ m_aSegments[nIndex]->m_xLink->hide();
+ m_aSegments[nIndex]->m_xLink->connect_activate_link(LINK(this, Breadcrumb, ClickLinkHdl));
+ m_aSegments[nIndex]->m_xSeparator->set_label( ">" );
+ m_aSegments[nIndex]->m_xSeparator->hide();
+}
+
+bool Breadcrumb::showField( unsigned int nIndex, unsigned int nWidthMax )
+{
+ m_aSegments[nIndex]->m_xLink->show();
+ m_aSegments[nIndex]->m_xSeparator->show();
+
+ unsigned int nSeparatorWidth = m_aSegments[0]->m_xSeparator->get_preferred_size().Width();
+ unsigned int nWidth = m_aSegments[nIndex]->m_xLink->get_preferred_size().Width()
+ + nSeparatorWidth + 3*SPACING;
+
+ if( nWidth > nWidthMax )
+ {
+ if( nIndex != 0 )
+ {
+ m_aSegments[nIndex]->m_xLink->hide();
+ m_aSegments[nIndex]->m_xSeparator->hide();
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+IMPL_LINK(Breadcrumb, ClickLinkHdl, weld::LinkButton&, rLink, bool)
+{
+ m_sClickedURL = m_aUris[&rLink];
+ return m_aClickHdl.Call(this);
+}
+
+BreadcrumbPath::BreadcrumbPath(weld::Container* pContainer)
+ : m_xBuilder(Application::CreateBuilder(pContainer, "fps/ui/breadcrumb.ui"))
+ , m_xContainer(m_xBuilder->weld_container("container"))
+ , m_xLink(m_xBuilder->weld_link_button("link"))
+ , m_xSeparator(m_xBuilder->weld_label("label"))
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/breadcrumb.hxx b/fpicker/source/office/breadcrumb.hxx
new file mode 100644
index 0000000000..5f476010c9
--- /dev/null
+++ b/fpicker/source/office/breadcrumb.hxx
@@ -0,0 +1,70 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <map>
+#include <vector>
+
+#define SPACING 6
+
+enum SvtBreadcrumbMode
+{
+ ONLY_CURRENT_PATH = 0,
+ ALL_VISITED = 1
+};
+
+struct BreadcrumbPath
+{
+ BreadcrumbPath(weld::Container* pParent);
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<weld::LinkButton> m_xLink;
+ std::unique_ptr<weld::Label> m_xSeparator;
+};
+
+class Breadcrumb
+{
+private:
+ weld::Container* m_pParent;
+ int m_nMaxWidth;
+
+ std::vector<std::unique_ptr<BreadcrumbPath>> m_aSegments;
+ std::map<weld::LinkButton*, OUString> m_aUris;
+
+ OUString m_sRootName;
+ OUString m_sClickedURL;
+ OUString m_aCurrentURL;
+
+ SvtBreadcrumbMode m_eMode;
+
+ Link<Breadcrumb*, bool> m_aClickHdl;
+
+ void appendField();
+ bool showField(unsigned int nIndex, unsigned int nWidthMax);
+
+ DECL_LINK(SizeAllocHdl, const Size&, void);
+ DECL_LINK(ClickLinkHdl, weld::LinkButton&, bool);
+
+public:
+ Breadcrumb(weld::Container* pParent);
+ ~Breadcrumb();
+
+ void EnableFields(bool bEnable);
+
+ void connect_clicked(const Link<Breadcrumb*, bool>& rLink);
+ const OUString& GetHdlURL() const;
+
+ void SetRootName(const OUString& rURL);
+ void SetURL(const OUString& rURL);
+ void SetMode(SvtBreadcrumbMode eMode);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/commonpicker.cxx b/fpicker/source/office/commonpicker.cxx
new file mode 100644
index 0000000000..8b7f2827df
--- /dev/null
+++ b/fpicker/source/office/commonpicker.cxx
@@ -0,0 +1,476 @@
+/* -*- 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 "commonpicker.hxx"
+#include "fpdialogbase.hxx"
+#include "OfficeControlAccess.hxx"
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/weakeventlistener.hxx>
+#include <comphelper/types.hxx>
+
+
+namespace svt
+{
+
+
+#define PROPERTY_ID_HELPURL 1
+#define PROPERTY_ID_WINDOW 2
+
+ // using --------------------------------------------------------------
+
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::ui::dialogs;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::comphelper;
+
+
+ OCommonPicker::OCommonPicker()
+ :OCommonPicker_Base( m_aMutex )
+ ,OPropertyContainer( GetBroadcastHelper() )
+ ,m_nCancelEvent( nullptr )
+ ,m_bExecuting( false )
+ {
+ // the two properties we have
+ registerProperty(
+ "HelpURL", PROPERTY_ID_HELPURL,
+ PropertyAttribute::TRANSIENT,
+ &m_sHelpURL, cppu::UnoType<decltype(m_sHelpURL)>::get()
+ );
+
+ registerProperty(
+ "Window", PROPERTY_ID_WINDOW,
+ PropertyAttribute::TRANSIENT | PropertyAttribute::READONLY,
+ &m_xWindow, cppu::UnoType<decltype(m_xWindow)>::get()
+ );
+ }
+
+
+ OCommonPicker::~OCommonPicker()
+ {
+ if ( !GetBroadcastHelper().bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ // disambiguate XInterface
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OCommonPicker, OCommonPicker_Base, OPropertyContainer )
+
+
+ // disambiguate XTypeProvider
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCommonPicker, OCommonPicker_Base, OPropertyContainer )
+
+
+ // XComponent related methods
+
+ void OCommonPicker::checkAlive() const
+ {
+ if ( GetBroadcastHelper().bInDispose || GetBroadcastHelper().bDisposed )
+ throw DisposedException();
+ }
+
+ void OCommonPicker::prepareDialog()
+ {
+ if(createPicker())
+ {
+ // set the title
+ if ( !m_aTitle.isEmpty() )
+ m_xDlg->set_title(m_aTitle);
+ }
+ }
+
+
+ void SAL_CALL OCommonPicker::disposing()
+ {
+ SolarMutexGuard aGuard;
+
+ stopWindowListening();
+
+ if ( m_nCancelEvent )
+ Application::RemoveUserEvent( m_nCancelEvent );
+
+ {
+ ::osl::MutexGuard aOwnGuard( m_aMutex );
+ if ( m_bExecuting && m_xDlg )
+ m_xDlg->response(RET_CANCEL);
+ }
+
+ m_xDlg.reset();
+ m_xWindow = nullptr;
+ m_xDialogParent = nullptr;
+ }
+
+
+ void OCommonPicker::stopWindowListening()
+ {
+ disposeComponent( m_xWindowListenerAdapter );
+ disposeComponent( m_xParentListenerAdapter );
+ }
+
+ // XEventListener
+ void SAL_CALL OCommonPicker::disposing( const EventObject& _rSource )
+ {
+ SolarMutexGuard aGuard;
+ bool bDialogDying = _rSource.Source == m_xWindow;
+ bool bParentDying = _rSource.Source == m_xDialogParent;
+
+ if ( bDialogDying || bParentDying )
+ {
+ stopWindowListening();
+
+ SAL_WARN_IF(bDialogDying && m_bExecuting, "fpicker.office", "unexpected disposing before response" );
+
+ // it's the parent which is dying -> delete the dialog
+ {
+ ::osl::MutexGuard aOwnGuard(m_aMutex);
+ if (m_bExecuting && m_xDlg)
+ m_xDlg->response(RET_CANCEL);
+ }
+
+ m_xDlg.reset();
+ m_xWindow = nullptr;
+ m_xDialogParent = nullptr;
+ }
+ else
+ {
+ OSL_FAIL( "OCommonPicker::disposing: where did this come from?" );
+ }
+ }
+
+ // property set related methods
+ ::cppu::IPropertyArrayHelper* OCommonPicker::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps;
+ describeProperties( aProps );
+ return new cppu::OPropertyArrayHelper( aProps );
+ }
+
+ ::cppu::IPropertyArrayHelper& SAL_CALL OCommonPicker::getInfoHelper()
+ {
+ return *getArrayHelper();
+ }
+
+ Reference< XPropertySetInfo > SAL_CALL OCommonPicker::getPropertySetInfo( )
+ {
+ return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
+ }
+
+ void SAL_CALL OCommonPicker::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue)
+ {
+ OPropertyContainer::setFastPropertyValue_NoBroadcast(nHandle, rValue);
+
+ // if the HelpURL changed, forward this to the dialog
+ if (PROPERTY_ID_HELPURL == nHandle && m_xDlg)
+ {
+ ::svt::OControlAccess aAccess(m_xDlg.get(), m_xDlg->GetView());
+ aAccess.setHelpURL(m_xDlg->getDialog(), m_sHelpURL);
+ }
+ }
+
+ bool OCommonPicker::createPicker()
+ {
+ if ( !m_xDlg )
+ {
+ m_xDlg = implCreateDialog(Application::GetFrameWeld(m_xDialogParent));
+ SAL_WARN_IF( !m_xDlg, "fpicker.office", "OCommonPicker::createPicker: invalid dialog returned!" );
+
+ if ( m_xDlg )
+ {
+ weld::Dialog* pDlg = m_xDlg->getDialog();
+
+ ::svt::OControlAccess aAccess(m_xDlg.get(), m_xDlg->GetView());
+ // synchronize the help id of the dialog without help URL property
+ if ( !m_sHelpURL.isEmpty() )
+ { // somebody already set the help URL while we had no dialog yet
+ aAccess.setHelpURL(pDlg, m_sHelpURL);
+ }
+ else
+ {
+ m_sHelpURL = aAccess.getHelpURL(pDlg);
+ }
+
+ m_xWindow = pDlg->GetXWindow();
+
+ // add as event listener to the window
+ OSL_ENSURE( m_xWindow.is(), "OCommonPicker::createFileDialog: invalid window component!" );
+ if ( m_xWindow.is() )
+ {
+ m_xWindowListenerAdapter = new OWeakEventListenerAdapter( this, m_xWindow );
+ // the adapter will add itself as listener, and forward notifications
+ }
+
+ VclPtr<vcl::Window> xVclDialog(VCLUnoHelper::GetWindow(m_xWindow));
+ if (xVclDialog) // this block is quite possibly unnecessary by now
+ {
+ // _and_ add as event listener to the parent - in case the parent is destroyed
+ // before we are disposed, our disposal would access dead VCL windows then...
+ m_xDialogParent = VCLUnoHelper::GetInterface(xVclDialog->GetParent());
+ OSL_ENSURE(m_xDialogParent.is() || !xVclDialog->GetParent(), "OCommonPicker::createFileDialog: invalid window component (the parent this time)!");
+ }
+ if ( m_xDialogParent.is() )
+ {
+ m_xParentListenerAdapter = new OWeakEventListenerAdapter( this, m_xDialogParent );
+ // the adapter will add itself as listener, and forward notifications
+ }
+ }
+ }
+
+ return nullptr != m_xDlg;
+ }
+
+ // XControlAccess functions
+ void SAL_CALL OCommonPicker::setControlProperty( const OUString& aControlName, const OUString& aControlProperty, const Any& aValue )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ aAccess.setControlProperty( aControlName, aControlProperty, aValue );
+ }
+ }
+
+ Any SAL_CALL OCommonPicker::getControlProperty( const OUString& aControlName, const OUString& aControlProperty )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ return aAccess.getControlProperty( aControlName, aControlProperty );
+ }
+
+ return Any();
+ }
+
+ // XControlInformation functions
+ Sequence< OUString > SAL_CALL OCommonPicker::getSupportedControls( )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ return aAccess.getSupportedControls( );
+ }
+
+ return Sequence< OUString >();
+ }
+
+ sal_Bool SAL_CALL OCommonPicker::isControlSupported( const OUString& aControlName )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ return svt::OControlAccess::isControlSupported( aControlName );
+ }
+
+ return false;
+ }
+
+ Sequence< OUString > SAL_CALL OCommonPicker::getSupportedControlProperties( const OUString& aControlName )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ return aAccess.getSupportedControlProperties( aControlName );
+ }
+
+ return Sequence< OUString >();
+ }
+
+ sal_Bool SAL_CALL OCommonPicker::isControlPropertySupported( const OUString& aControlName, const OUString& aControlProperty )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ return aAccess.isControlPropertySupported( aControlName, aControlProperty );
+ }
+
+ return false;
+ }
+
+
+ // XExecutableDialog functions
+
+ void OCommonPicker::setTitle( const OUString& _rTitle )
+ {
+ SolarMutexGuard aGuard;
+ m_aTitle = _rTitle;
+ }
+
+
+ sal_Int16 OCommonPicker::execute()
+ {
+ SolarMutexGuard aGuard;
+
+ prepareDialog();
+
+ {
+ ::osl::MutexGuard aOwnGuard( m_aMutex );
+ m_bExecuting = true;
+ }
+ sal_Int16 nResult = implExecutePicker();
+ {
+ ::osl::MutexGuard aOwnGuard( m_aMutex );
+ m_bExecuting = false;
+ }
+
+ return nResult;
+ }
+
+
+ // XCancellable functions
+
+ void SAL_CALL OCommonPicker::cancel( )
+ {
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_nCancelEvent )
+ // nothing to do - the event for cancelling the dialog is already on the way
+ return;
+ }
+
+ // The thread which executes our dialog has locked the solar mutex for
+ // sure. Cancelling the dialog should be done with a locked solar mutex, too.
+ // Thus we post ourself a message for cancelling the dialog. This way, the message
+ // is either handled in the thread which opened the dialog (which may even be
+ // this thread here), or, if no dialog is open, in the thread doing scheduling
+ // currently. Both is okay for us...
+
+ // Note that we could do check if we are really executing the dialog currently.
+ // but the information would be potentially obsolete at the moment our event
+ // arrives, so we need to check it there, anyway...
+ m_nCancelEvent = Application::PostUserEvent( LINK( this, OCommonPicker, OnCancelPicker ) );
+ }
+
+ IMPL_LINK_NOARG(OCommonPicker, OnCancelPicker, void*, void)
+ {
+ // By definition, the solar mutex is locked when we arrive here. Note that this
+ // is important, as for instance the consistency of m_xDlg depends on this mutex.
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nCancelEvent = nullptr;
+
+ if ( !m_bExecuting )
+ // nothing to do. This may be because the dialog was canceled after our cancel method
+ // posted this async event, or because somebody called cancel without the dialog
+ // being executed at this time.
+ return;
+
+ OSL_ENSURE( m_xDlg, "OCommonPicker::OnCancelPicker: executing, but no dialog!" );
+ if (m_xDlg)
+ m_xDlg->response(RET_CANCEL);
+ }
+
+ // XInitialization functions
+ void SAL_CALL OCommonPicker::initialize( const Sequence< Any >& _rArguments )
+ {
+ checkAlive();
+
+ OUString sSettingName;
+ Any aSettingValue;
+
+ PropertyValue aPropArg;
+ NamedValue aPairArg;
+
+
+ const Any* pArguments = _rArguments.getConstArray();
+ const Any* pArgumentsEnd = _rArguments.getConstArray() + _rArguments.getLength();
+ for ( const Any* pArgument = pArguments;
+ pArgument != pArgumentsEnd;
+ ++pArgument
+ )
+ {
+ if ( *pArgument >>= aPropArg )
+ {
+ if ( aPropArg.Name.isEmpty())
+ continue;
+
+ sSettingName = aPropArg.Name;
+ aSettingValue = aPropArg.Value;
+ }
+ else if ( *pArgument >>= aPairArg )
+ {
+ if ( aPairArg.Name.isEmpty())
+ continue;
+
+ sSettingName = aPairArg.Name;
+ aSettingValue = aPairArg.Value;
+
+
+ }
+ else
+ {
+ SAL_WARN( "fpicker", "OCommonPicker::initialize: unknown argument type at position "
+ << (pArguments - _rArguments.getConstArray()));
+ continue;
+ }
+
+ bool bKnownSetting =
+ implHandleInitializationArgument( sSettingName, aSettingValue );
+ DBG_ASSERT( bKnownSetting,
+ OString(
+ "OCommonPicker::initialize: unknown argument \""
+ + OUStringToOString(sSettingName, osl_getThreadTextEncoding())
+ + "\"!").getStr() );
+ }
+ }
+
+ bool OCommonPicker::implHandleInitializationArgument( const OUString& _rName, const Any& _rValue )
+ {
+ bool bKnown = true;
+ if ( _rName == "ParentWindow" )
+ {
+ m_xDialogParent.clear();
+ OSL_VERIFY( _rValue >>= m_xDialogParent );
+ OSL_ENSURE( m_xDialogParent.is(), "OCommonPicker::implHandleInitializationArgument: invalid parent window given!" );
+ }
+ else
+ bKnown = false;
+ return bKnown;
+ }
+
+} // namespace svt
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/commonpicker.hxx b/fpicker/source/office/commonpicker.hxx
new file mode 100644
index 0000000000..1c24d8eb9e
--- /dev/null
+++ b/fpicker/source/office/commonpicker.hxx
@@ -0,0 +1,190 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <com/sun/star/ui/dialogs/XControlInformation.hpp>
+#include <com/sun/star/ui/dialogs/XControlAccess.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/util/XCancellable.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <comphelper/propertycontainer.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/uno3.hxx>
+#include <tools/link.hxx>
+
+class SvtFileDialog_Base;
+namespace weld { class Window; }
+struct ImplSVEvent;
+
+namespace svt
+{
+
+
+ typedef ::cppu::WeakComponentImplHelper < css::ui::dialogs::XControlAccess
+ , css::ui::dialogs::XControlInformation
+ , css::lang::XEventListener
+ , css::util::XCancellable
+ , css::lang::XInitialization
+ > OCommonPicker_Base;
+ /** implements common functionality for the 2 UNO picker components
+ */
+ class OCommonPicker
+ :public ::cppu::BaseMutex
+ ,public OCommonPicker_Base
+ ,public ::comphelper::OPropertyContainer
+ ,public ::comphelper::OPropertyArrayUsageHelper< OCommonPicker >
+ {
+ private:
+ // <properties>
+ OUString m_sHelpURL;
+ css::uno::Reference< css::awt::XWindow > m_xWindow;
+ // </properties>
+
+ ImplSVEvent * m_nCancelEvent;
+ bool m_bExecuting;
+
+ css::uno::Reference< css::awt::XWindow > m_xDialogParent;
+
+ css::uno::Reference< css::lang::XComponent > m_xWindowListenerAdapter;
+ css::uno::Reference< css::lang::XComponent > m_xParentListenerAdapter;
+
+ protected:
+ OUString m_aTitle;
+ OUString m_aDisplayDirectory;
+
+ protected:
+ std::shared_ptr<SvtFileDialog_Base> m_xDlg;
+
+ const ::cppu::OBroadcastHelper& GetBroadcastHelper() const { return OCommonPicker_Base::rBHelper; }
+ ::cppu::OBroadcastHelper& GetBroadcastHelper() { return OCommonPicker_Base::rBHelper; }
+
+ public:
+ OCommonPicker();
+
+ protected:
+ virtual ~OCommonPicker() override;
+
+ // overridables
+
+ // will be called with locked SolarMutex
+ virtual std::shared_ptr<SvtFileDialog_Base> implCreateDialog( weld::Window* pParent ) = 0;
+ virtual sal_Int16 implExecutePicker( ) = 0;
+ // do NOT override XExecutableDialog::execute! We need to do some stuff there ourself ...
+
+ protected:
+
+ // disambiguate XInterface
+
+ DECLARE_XINTERFACE( )
+
+
+ // disambiguate XTypeProvider
+
+ DECLARE_XTYPEPROVIDER( )
+
+
+ // ComponentHelper/XComponent
+
+ virtual void SAL_CALL disposing() override;
+
+
+ // XEventListener
+
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+
+ // property set related methods
+
+
+ // XPropertySet pure methods
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ // OPropertySetHelper pure methods
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ // OPropertyArrayUsageHelper pure methods
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ // OPropertySetHelper overridden methods
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(
+ sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+
+
+ // XExecutableDialog functions
+
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setTitle( const OUString& _rTitle );
+ /// @throws css::uno::RuntimeException
+ virtual sal_Int16 SAL_CALL execute();
+
+
+ // XControlAccess functions
+
+ virtual void SAL_CALL setControlProperty( const OUString& aControlName, const OUString& aControlProperty, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getControlProperty( const OUString& aControlName, const OUString& aControlProperty ) override;
+
+
+ // XControlInformation functions
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedControls( ) override;
+ virtual sal_Bool SAL_CALL isControlSupported( const OUString& aControlName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedControlProperties( const OUString& aControlName ) override;
+ virtual sal_Bool SAL_CALL isControlPropertySupported( const OUString& aControlName, const OUString& aControlProperty ) override;
+
+
+ // XCancellable functions
+
+ virtual void SAL_CALL cancel( ) override;
+
+
+ // XInitialization functions
+
+
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+
+ // misc
+
+ void checkAlive() const;
+
+ void prepareDialog();
+
+ protected:
+ bool createPicker();
+
+ /** handle a single argument from the XInitialization::initialize method
+
+ @return <TRUE/> if the argument could be handled
+ */
+ virtual bool implHandleInitializationArgument(
+ const OUString& _rName,
+ const css::uno::Any& _rValue
+ );
+
+ private:
+ void stopWindowListening();
+
+ DECL_LINK( OnCancelPicker, void*, void );
+ };
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/contentenumeration.cxx b/fpicker/source/office/contentenumeration.cxx
new file mode 100644
index 0000000000..2a5b015648
--- /dev/null
+++ b/fpicker/source/office/contentenumeration.cxx
@@ -0,0 +1,322 @@
+/* -*- 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 "contentenumeration.hxx"
+#include <svtools/imagemgr.hxx>
+
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/XDynamicResultSet.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/document/DocumentProperties.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/urlobj.hxx>
+
+namespace svt
+{
+
+
+#define ROW_TITLE 1
+#define ROW_SIZE 2
+#define ROW_DATE_MOD 3
+#define ROW_DATE_CREATE 4
+#define ROW_IS_FOLDER 5
+#define ROW_TARGET_URL 6
+#define ROW_IS_HIDDEN 7
+#define ROW_IS_VOLUME 8
+#define ROW_IS_REMOTE 9
+#define ROW_IS_REMOVABLE 10
+#define ROW_IS_FLOPPY 11
+#define ROW_IS_COMPACTDISC 12
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::util::DateTime;
+ using ::com::sun::star::sdbc::XResultSet;
+ using ::com::sun::star::sdbc::XRow;
+ using ::com::sun::star::ucb::XDynamicResultSet;
+ using ::com::sun::star::ucb::CommandAbortedException;
+ using ::com::sun::star::ucb::XContentAccess;
+ using ::com::sun::star::ucb::XCommandEnvironment;
+ using ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS;
+
+
+ //= FileViewContentEnumerator
+
+
+ FileViewContentEnumerator::FileViewContentEnumerator(
+ const Reference< XCommandEnvironment >& _rxCommandEnv,
+ ContentData& _rContentToFill, ::osl::Mutex& _rContentMutex )
+ :Thread ( "FileViewContentEnumerator" )
+ ,m_rContent ( _rContentToFill )
+ ,m_rContentMutex ( _rContentMutex )
+ ,m_xCommandEnv ( _rxCommandEnv )
+ ,m_pResultHandler ( nullptr )
+ ,m_bCancelled ( false )
+ {
+ }
+
+
+ FileViewContentEnumerator::~FileViewContentEnumerator()
+ {
+ }
+
+
+ void FileViewContentEnumerator::cancel()
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_bCancelled = true;
+ m_pResultHandler = nullptr;
+ m_aFolder.aContent = ::ucbhelper::Content();
+ m_aFolder.sURL.clear();
+ }
+
+
+ EnumerationResult FileViewContentEnumerator::enumerateFolderContentSync(
+ const FolderDescriptor& _rFolder,
+ const css::uno::Sequence< OUString >& rDenyList )
+ {
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_aFolder = _rFolder;
+ m_pResultHandler = nullptr;
+ m_rDenyList = rDenyList;
+ }
+ return enumerateFolderContent();
+ }
+
+
+ void FileViewContentEnumerator::enumerateFolderContent(
+ const FolderDescriptor& _rFolder, IEnumerationResultHandler* _pResultHandler )
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_aFolder = _rFolder;
+ m_pResultHandler = _pResultHandler;
+
+ OSL_ENSURE( m_aFolder.aContent.get().is() || !m_aFolder.sURL.isEmpty(),
+ "FileViewContentEnumerator::enumerateFolderContent: invalid folder descriptor!" );
+
+ launch();
+ //TODO: a protocol is missing how to join with the launched thread
+ // before exit(3), to ensure the thread is no longer relying on any
+ // infrastructure while that infrastructure is being shut down in
+ // atexit handlers
+ }
+
+
+ EnumerationResult FileViewContentEnumerator::enumerateFolderContent()
+ {
+ EnumerationResult eResult = EnumerationResult::ERROR;
+ try
+ {
+
+ Reference< XResultSet > xResultSet;
+ Sequence< OUString > aProps{ "Title",
+ "Size",
+ "DateModified",
+ "DateCreated",
+ "IsFolder",
+ "TargetURL",
+ "IsHidden",
+ "IsVolume",
+ "IsRemote",
+ "IsRemoveable",
+ "IsFloppy",
+ "IsCompactDisc" };
+
+ Reference< XCommandEnvironment > xEnvironment;
+ try
+ {
+ FolderDescriptor aFolder;
+ {
+ std::unique_lock aGuard( m_aMutex );
+ aFolder = m_aFolder;
+ xEnvironment = m_xCommandEnv;
+ }
+ if ( !aFolder.aContent.get().is() )
+ {
+ aFolder.aContent = ::ucbhelper::Content( aFolder.sURL, xEnvironment, comphelper::getProcessComponentContext() );
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_aFolder.aContent = aFolder.aContent;
+ }
+ }
+
+ Reference< XDynamicResultSet > xDynResultSet = aFolder.aContent.createDynamicCursor( aProps, INCLUDE_FOLDERS_AND_DOCUMENTS );
+
+ if ( xDynResultSet.is() )
+ xResultSet = xDynResultSet->getStaticResultSet();
+ }
+ catch( CommandAbortedException& )
+ {
+ TOOLS_WARN_EXCEPTION( "svtools.contnr", "");
+ }
+ catch( Exception& )
+ {
+ }
+
+ if ( xResultSet.is() )
+ {
+ Reference< XRow > xRow( xResultSet, UNO_QUERY );
+ Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
+
+ try
+ {
+ DateTime aDT;
+
+ bool bCancelled = false;
+ while ( !bCancelled && xResultSet->next() )
+ {
+ bool bIsHidden = xRow->getBoolean( ROW_IS_HIDDEN );
+ // don't show hidden files
+ if ( !bIsHidden || xRow->wasNull() )
+ {
+ aDT = xRow->getTimestamp( ROW_DATE_MOD );
+ bool bContainsDate = !xRow->wasNull();
+ if ( !bContainsDate )
+ {
+ aDT = xRow->getTimestamp( ROW_DATE_CREATE );
+ bContainsDate = !xRow->wasNull();
+ }
+
+ OUString aContentURL = xContentAccess->queryContentIdentifierString();
+ OUString aTargetURL = xRow->getString( ROW_TARGET_URL );
+ bool bHasTargetURL = !xRow->wasNull() && !aTargetURL.isEmpty();
+
+ OUString sRealURL = bHasTargetURL ? aTargetURL : aContentURL;
+
+ // check for restrictions
+ {
+ std::unique_lock aGuard( m_aMutex );
+ if ( /* m_rDenyList.hasElements() && */ URLOnDenyList ( sRealURL ) )
+ continue;
+ }
+
+ std::unique_ptr<SortingData_Impl> pData(new SortingData_Impl);
+ pData->maTargetURL = sRealURL;
+
+ pData->mbIsFolder = xRow->getBoolean( ROW_IS_FOLDER ) && !xRow->wasNull();
+ pData->mbIsVolume = xRow->getBoolean( ROW_IS_VOLUME ) && !xRow->wasNull();
+ pData->mbIsRemote = xRow->getBoolean( ROW_IS_REMOTE ) && !xRow->wasNull();
+ pData->mbIsRemoveable = xRow->getBoolean( ROW_IS_REMOVABLE ) && !xRow->wasNull();
+ pData->mbIsFloppy = xRow->getBoolean( ROW_IS_FLOPPY ) && !xRow->wasNull();
+ pData->mbIsCompactDisc = xRow->getBoolean( ROW_IS_COMPACTDISC ) && !xRow->wasNull();
+ pData->SetNewTitle( xRow->getString( ROW_TITLE ) );
+ pData->maSize = xRow->getLong( ROW_SIZE );
+
+ if ( bHasTargetURL &&
+ INetURLObject( aContentURL ).GetProtocol() == INetProtocol::VndSunStarHier )
+ {
+ ::ucbhelper::Content aCnt( aTargetURL, xEnvironment, comphelper::getProcessComponentContext() );
+ try
+ {
+ aCnt.getPropertyValue("Size") >>= pData->maSize;
+ aCnt.getPropertyValue("DateModified") >>= aDT;
+ }
+ catch (...) {}
+ }
+
+ if ( bContainsDate )
+ {
+ pData->maModDate = ::DateTime( aDT );
+ }
+
+ if ( pData->mbIsFolder )
+ {
+ ::svtools::VolumeInfo aVolInfo( pData->mbIsVolume, pData->mbIsRemote,
+ pData->mbIsRemoveable, pData->mbIsFloppy,
+ pData->mbIsCompactDisc );
+ pData->maType = SvFileInformationManager::GetFolderDescription( aVolInfo );
+ }
+ else
+ pData->maType = SvFileInformationManager::GetFileDescription(
+ INetURLObject( pData->maTargetURL ) );
+
+ {
+ ::osl::MutexGuard aGuard( m_rContentMutex );
+ m_rContent.push_back( std::move(pData) );
+ }
+ }
+
+ {
+ std::unique_lock aGuard( m_aMutex );
+ bCancelled = m_bCancelled;
+ }
+ }
+ eResult = EnumerationResult::SUCCESS;
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svtools.contnr", "FileViewContentEnumerator::enumerateFolderContent: caught an exception while enumerating");
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svtools.contnr", "FileViewContentEnumerator::enumerateFolderContent" );
+ }
+
+ IEnumerationResultHandler* pHandler = nullptr;
+ {
+ std::unique_lock aGuard( m_aMutex );
+ pHandler = m_pResultHandler;
+ if ( m_bCancelled )
+ return EnumerationResult::ERROR;
+ }
+
+ {
+ ::osl::MutexGuard aGuard( m_rContentMutex );
+ if ( eResult != EnumerationResult::SUCCESS )
+ // clear any "intermediate" and unfinished result
+ m_rContent.clear();
+ }
+
+ if ( pHandler )
+ pHandler->enumerationDone( eResult );
+ return eResult;
+ }
+
+
+ bool FileViewContentEnumerator::URLOnDenyList ( std::u16string_view sRealURL )
+ {
+ std::u16string_view entryName = sRealURL.substr( sRealURL.rfind( '/' ) + 1 );
+
+ return comphelper::findValue(m_rDenyList, entryName) != -1;
+ }
+
+
+ void FileViewContentEnumerator::execute()
+ {
+ enumerateFolderContent();
+ }
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/contentenumeration.hxx b/fpicker/source/office/contentenumeration.hxx
new file mode 100644
index 0000000000..d15db4d7a5
--- /dev/null
+++ b/fpicker/source/office/contentenumeration.hxx
@@ -0,0 +1,238 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+#include <mutex>
+
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <salhelper/thread.hxx>
+#include <ucbhelper/content.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/datetime.hxx>
+#include <utility>
+
+namespace svt
+{
+
+
+ //= SortingData_Impl
+
+ struct SortingData_Impl
+ {
+ private:
+ OUString maFilename; // only filename in upper case - for compare purposes
+ OUString maTitle; // -> be careful when changing maTitle to update maFilename only when new
+ OUString maLowerTitle;
+
+
+ public:
+ OUString maType;
+ OUString maTargetURL;
+ OUString maDisplayName;
+ OUString maDisplaySize;
+ OUString maDisplayDate;
+ DateTime maModDate;
+ OUString maImage;
+ sal_Int64 maSize;
+ bool mbIsFolder;
+ bool mbIsVolume;
+ bool mbIsRemote;
+ bool mbIsRemoveable;
+ bool mbIsFloppy;
+ bool mbIsCompactDisc;
+
+ inline SortingData_Impl();
+ inline const OUString& GetTitle() const;
+ inline const OUString& GetLowerTitle() const;
+ inline const OUString& GetFileName() const;
+ inline void SetNewTitle( const OUString& rNewTitle ); // new maTitle is set -> maFilename is set to same!
+
+ private:
+ inline void SetTitles( const OUString& rNewTitle );
+ };
+
+ inline SortingData_Impl::SortingData_Impl() :
+ maModDate ( DateTime::EMPTY ),
+ maSize ( 0 ),
+ mbIsFolder ( false ),
+ mbIsVolume ( false ),
+ mbIsRemote ( false ),
+ mbIsRemoveable ( false ),
+ mbIsFloppy ( false ),
+ mbIsCompactDisc ( false )
+ {
+ }
+
+ inline const OUString& SortingData_Impl::GetTitle() const
+ {
+ return maTitle;
+ }
+
+ inline const OUString& SortingData_Impl::GetLowerTitle() const
+ {
+ return maLowerTitle;
+ }
+
+ inline const OUString& SortingData_Impl::GetFileName() const
+ {
+ return maFilename;
+ }
+
+ inline void SortingData_Impl::SetNewTitle( const OUString& rNewTitle )
+ {
+ SetTitles( rNewTitle );
+ maFilename = rNewTitle.toAsciiUpperCase();
+ }
+
+ inline void SortingData_Impl::SetTitles( const OUString& rNewTitle )
+ {
+ maTitle = rNewTitle;
+ maLowerTitle = rNewTitle.toAsciiLowerCase();
+ }
+
+
+ //= EnumerationResult
+
+ enum class EnumerationResult
+ {
+ SUCCESS, /// the enumeration was successful
+ ERROR, /// the enumeration was unsuccessful
+ };
+
+
+ //= FolderDescriptor
+
+ struct FolderDescriptor
+ {
+ /** a content object describing the folder. Can be <NULL/>, in this case <member>sURL</member>
+ is relevant.
+ */
+ ::ucbhelper::Content aContent;
+ /** the URL of a folder. Will be ignored if <member>aContent</member> is not <NULL/>.
+ */
+ OUString sURL;
+
+ FolderDescriptor() { }
+
+ explicit FolderDescriptor( OUString _aURL )
+ :sURL(std::move( _aURL ))
+ {
+ }
+ };
+
+
+ //= IEnumerationResultHandler
+
+ class IEnumerationResultHandler
+ {
+ public:
+ virtual void enumerationDone( EnumerationResult _eResult ) = 0;
+
+ protected:
+ ~IEnumerationResultHandler() {}
+ };
+
+
+ //= FileViewContentEnumerator
+
+ class FileViewContentEnumerator: public salhelper::Thread
+ {
+ public:
+ typedef ::std::vector< std::unique_ptr<SortingData_Impl> > ContentData;
+
+ private:
+ ContentData& m_rContent;
+ ::osl::Mutex& m_rContentMutex;
+
+ mutable std::mutex m_aMutex;
+
+ FolderDescriptor m_aFolder;
+ css::uno::Reference< css::ucb::XCommandEnvironment >
+ m_xCommandEnv;
+ IEnumerationResultHandler* m_pResultHandler;
+ bool m_bCancelled;
+
+ css::uno::Sequence< OUString > m_rDenyList;
+
+ bool URLOnDenyList ( std::u16string_view sRealURL );
+
+ public:
+ /** constructs an enumerator instance
+
+ @param _rContentToFill
+ the structure which is to be filled with the found content
+ @param _rContentMutex
+ the mutex which protects the access to <arg>_rContentToFill</arg>
+ @param _pTranslator
+ an instance which should be used to translate content titles. May be <NULL/>
+ */
+ FileViewContentEnumerator(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& _rxCommandEnv,
+ ContentData& _rContentToFill,
+ ::osl::Mutex& _rContentMutex
+ );
+
+ /** enumerates the content of a given folder
+
+ @param _rFolder
+ the folder whose content is to be enumerated
+ @param _pFilter
+ a filter to apply to the found contents
+ @param _pResultHandler
+ an instance which should handle the results of the enumeration
+ */
+ void enumerateFolderContent(
+ const FolderDescriptor& _rFolder,
+ IEnumerationResultHandler* _pResultHandler
+ );
+
+ /** enumerates the content of a given folder synchronously
+ */
+ EnumerationResult enumerateFolderContentSync(
+ const FolderDescriptor& _rFolder,
+ const css::uno::Sequence< OUString >& rDenyList
+ );
+
+ /** cancels the running operation.
+
+ Note that "cancel" may mean that the operation is running, but its result
+ is simply disregarded later on.
+ */
+ void cancel();
+
+ protected:
+ virtual ~FileViewContentEnumerator() override;
+
+ private:
+ EnumerationResult enumerateFolderContent();
+
+ // Thread overridables
+ virtual void execute() override;
+
+ };
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fileview.cxx b/fpicker/source/office/fileview.cxx
new file mode 100644
index 0000000000..1fa1c4dafc
--- /dev/null
+++ b/fpicker/source/office/fileview.cxx
@@ -0,0 +1,1807 @@
+/* -*- 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 <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svtools/svtresid.hxx>
+#include <svtools/imagemgr.hxx>
+#include <svtools/querydelete.hxx>
+#include <svtools/strings.hrc>
+#include <bitmaps.hlst>
+#include "contentenumeration.hxx"
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <algorithm>
+#include <string_view>
+#include <vector>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <rtl/math.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/mutex.hxx>
+#include <osl/conditn.hxx>
+#include <salhelper/timer.hxx>
+#include <svtools/urlfilter.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/timer.hxx>
+#include <memory>
+#include "fileview.hxx"
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::beans;
+using namespace ::comphelper;
+using ::svt::SortingData_Impl;
+using ::svt::FolderDescriptor;
+
+constexpr OUStringLiteral ALL_FILES_FILTER = u"*.*";
+
+#define COLUMN_TITLE 1
+#define COLUMN_TYPE 2
+#define COLUMN_SIZE 3
+#define COLUMN_DATE 4
+
+#define QUICK_SEARCH_TIMEOUT 1500 // time in mSec before the quicksearch string will be reset
+
+namespace {
+
+enum class FileViewFlags
+{
+ NONE = 0x00,
+ MULTISELECTION = 0x02,
+ SHOW_TYPE = 0x04,
+ SHOW_NONE = 0x20,
+};
+
+}
+
+namespace o3tl
+{
+ template<> struct typed_flags<FileViewFlags> : is_typed_flags<FileViewFlags, 0x26> {};
+}
+
+namespace
+{
+
+ //= CallbackTimer
+
+ class CallbackTimer : public ::salhelper::Timer
+ {
+ protected:
+ SvtFileView_Impl* const m_pTimeoutHandler;
+
+ public:
+ explicit CallbackTimer( SvtFileView_Impl* _pHandler ) : m_pTimeoutHandler( _pHandler ) { }
+
+ protected:
+ virtual void SAL_CALL onShot() override;
+ };
+
+class ViewTabListBox_Impl
+{
+private:
+ Reference< XCommandEnvironment > mxCmdEnv;
+ std::unique_ptr<weld::TreeView> mxTreeView;
+ std::unique_ptr<weld::TreeIter> mxScratchIter;
+
+ ::osl::Mutex maMutex;
+ SvtFileView_Impl* mpParent;
+ Timer maResetQuickSearch { "fpicker SvtFileView_Impl maResetQuickSearch" };
+ OUString maQuickSearchText;
+ sal_uInt32 mnSearchIndex;
+ bool mbEnableDelete;
+ bool mbEditing;
+ bool const mbShowType;
+
+ void DeleteEntries();
+ void DoQuickSearch( sal_Unicode rChar );
+ bool Kill( const OUString& rURL );
+
+public:
+ ViewTabListBox_Impl(std::unique_ptr<weld::TreeView> xTreeView, weld::Window* pTopLevel, SvtFileView_Impl* pParent, FileViewFlags nFlags);
+
+ std::unique_ptr<weld::TreeIter> make_iterator() const { return mxTreeView->make_iterator(); }
+ void insert(const OUString &rEntry, const OUString& rId, const OUString& rImage, weld::TreeIter& rIter)
+ {
+ mxTreeView->insert(nullptr, -1, &rEntry, &rId, nullptr, nullptr, false, &rIter);
+ mxTreeView->set_image(rIter, rImage);
+ }
+ void append(const OUString& rId, const OUString& rStr, const OUString& rType, const OUString& rSize, const OUString& rDate, const OUString& rImage)
+ {
+ mxTreeView->insert(nullptr, -1, &rStr, &rId, nullptr, nullptr, false, mxScratchIter.get());
+ mxTreeView->set_image(*mxScratchIter, rImage);
+ int nCol = 1;
+ if (mbShowType)
+ mxTreeView->set_text(*mxScratchIter, rType, nCol++);
+ mxTreeView->set_text(*mxScratchIter, rSize, nCol++);
+ mxTreeView->set_text(*mxScratchIter, rDate, nCol++);
+ }
+
+ void scroll_to_row(const weld::TreeIter& rIter) { mxTreeView->scroll_to_row(rIter); }
+ void set_cursor(int nPos) { mxTreeView->set_cursor(nPos); }
+ void set_cursor(const weld::TreeIter& rIter) { mxTreeView->set_cursor(rIter); }
+ bool get_cursor(weld::TreeIter* pIter) const { return mxTreeView->get_cursor(pIter); }
+ bool get_iter_first(weld::TreeIter& rIter) const { return mxTreeView->get_iter_first(rIter); }
+ bool get_selected(weld::TreeIter* pIter) const { return mxTreeView->get_selected(pIter); }
+
+ OUString get_selected_text() const
+ {
+ // tdf#131898 only care about column 0
+ int nIndex = mxTreeView->get_selected_index();
+ return nIndex != -1 ? mxTreeView->get_text(nIndex, 0) : OUString();
+ }
+
+ void unselect_all() { mxTreeView->unselect_all(); }
+
+ OUString get_id(const weld::TreeIter& rIter) { return mxTreeView->get_id(rIter); }
+
+ void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) { mxTreeView->connect_row_activated(rLink); }
+ void connect_changed(const Link<weld::TreeView&, void>& rLink) { mxTreeView->connect_changed(rLink); }
+
+ int n_children() const { return mxTreeView->n_children(); }
+
+ void freeze() { mxTreeView->freeze(); }
+ void thaw() { mxTreeView->thaw(); }
+
+ void show() { mxTreeView->show(); }
+ void hide() { mxTreeView->hide(); }
+ bool get_visible() const { return mxTreeView->get_visible(); }
+
+ int count_selected_rows() const { return mxTreeView->count_selected_rows(); }
+
+ void grab_focus() { mxTreeView->grab_focus(); }
+ bool has_focus() const { return mxTreeView->has_focus(); }
+
+ void set_help_id(const OUString& rHelpId) { mxTreeView->set_help_id(rHelpId); }
+ OUString get_help_id() const { return mxTreeView->get_help_id(); }
+
+ bool IsEditingActive() const { return mbEditing; }
+
+ void end_editing()
+ {
+ mxTreeView->end_editing();
+ mxTreeView->connect_editing(Link<const weld::TreeIter&, bool>(), Link<const IterString&, bool>());
+ mbEditing = false;
+ }
+
+ void selected_foreach(const std::function<bool(weld::TreeIter&)>& func)
+ {
+ mxTreeView->selected_foreach(func);
+ }
+
+ weld::TreeView* getWidget() const
+ {
+ return mxTreeView.get();
+ }
+
+ void clear() { mxTreeView->clear(); }
+
+ void EnableDelete( bool bEnable ) { mbEnableDelete = bEnable; }
+ bool TypeColumnVisible() const { return mbShowType; }
+
+ const Reference< XCommandEnvironment >& GetCommandEnvironment() const { return mxCmdEnv; }
+
+ DECL_LINK(ResetQuickSearch_Impl, Timer *, void);
+ DECL_LINK(CommandHdl, const CommandEvent&, bool);
+ DECL_LINK(EditingEntryHdl, const weld::TreeIter&, bool);
+ typedef std::pair<const weld::TreeIter&, OUString> IterString;
+ DECL_LINK(EditedEntryHdl, const IterString&, bool);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+
+ void ExecuteContextMenuAction(std::u16string_view rSelectedPopentry);
+};
+
+}
+
+//= SvtFileView_Impl
+class SvtFileView_Impl :public ::svt::IEnumerationResultHandler
+{
+protected:
+ SvtFileView* m_pAntiImpl;
+ Link<SvtFileView*,void> m_aSelectHandler;
+
+ ::rtl::Reference< ::svt::FileViewContentEnumerator >
+ m_xContentEnumerator;
+ Link<void*,void> m_aCurrentAsyncActionHandler;
+ ::osl::Condition m_aAsyncActionFinished;
+ ::rtl::Reference< ::salhelper::Timer > m_xCancelAsyncTimer;
+ ::svt::EnumerationResult m_eAsyncActionResult;
+ bool m_bRunningAsyncAction;
+ bool m_bAsyncActionCancelled;
+
+public:
+
+ ::std::vector<std::unique_ptr<SortingData_Impl>> maContent;
+ ::std::vector<std::unique_ptr<SvtContentEntry>> maEntries;
+ ::osl::Mutex maMutex;
+
+ weld::Window* m_pTopLevel;
+ std::unique_ptr<ViewTabListBox_Impl> mxView;
+ std::unique_ptr<weld::IconView> mxIconView;
+ sal_uInt16 mnSortColumn;
+ bool mbAscending : 1;
+ bool const mbOnlyFolder : 1;
+ sal_Int16 mnSuspendSelectCallback : 1;
+ bool mbIsFirstResort : 1;
+
+ IntlWrapper const aIntlWrapper;
+
+ OUString maViewURL;
+ OUString maCurrentFilter;
+ OUString maFolderImage;
+ Link<SvtFileView*,void> maOpenDoneLink;
+ Link<SvtFileView*,bool> maDoubleClickHandler;
+
+ Reference< XCommandEnvironment > mxCmdEnv;
+
+ SvtFileView_Impl(SvtFileView* pAntiImpl, weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ Reference < XCommandEnvironment > const & xEnv,
+ FileViewFlags nFlags,
+ bool bOnlyFolder);
+
+ virtual ~SvtFileView_Impl();
+
+ void Clear();
+
+ FileViewResult GetFolderContent_Impl(
+ std::u16string_view rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList );
+
+ FileViewResult GetFolderContent_Impl(
+ const FolderDescriptor& _rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList );
+ void FilterFolderContent_Impl( std::u16string_view rFilter );
+ void CancelRunningAsyncAction();
+
+ void OpenFolder_Impl();
+ static OUString ReplaceTabWithString(const OUString& rValue);
+ void CreateDisplayText_Impl();
+ void SortFolderContent_Impl();
+
+ void EntryRemoved( std::u16string_view rURL );
+ void EntryRenamed( OUString& rURL,
+ const OUString& rName );
+ const SortingData_Impl& FolderInserted( const OUString& rURL,
+ const OUString& rTitle );
+
+ int GetEntryPos( std::u16string_view rURL );
+
+ void SetViewMode( FileViewMode eMode );
+
+ inline void EnableDelete( bool bEnable );
+
+ void Resort_Impl( sal_Int16 nColumn, bool bAscending );
+ bool SearchNextEntry( sal_uInt32 &nIndex,
+ std::u16string_view rTitle,
+ bool bWrapAround );
+
+ void SetSelectHandler( const Link<SvtFileView*,void>& rHdl );
+ void SetDoubleClickHandler(const Link<SvtFileView*,bool>& rHdl);
+
+ void ResetCursor();
+
+ void EndEditing()
+ {
+ if (mxView->IsEditingActive())
+ mxView->end_editing();
+ }
+
+ void onTimeout();
+
+ void grab_focus()
+ {
+ if (mxView->get_visible())
+ mxView->grab_focus();
+ else
+ mxIconView->grab_focus();
+ }
+
+ bool has_focus() const
+ {
+ return mxView->has_focus() || mxIconView->has_focus();
+ }
+
+ int GetSortColumn() const
+ {
+ sal_uInt16 nOldSortID = mnSortColumn;
+ // skip "TYPE"
+ if (!mxView->TypeColumnVisible() && nOldSortID != COLUMN_TITLE)
+ --nOldSortID;
+ return nOldSortID - 1;
+ }
+
+protected:
+ DECL_LINK(ChangedHdl, weld::TreeView&, void);
+ DECL_LINK(SelectionChangedHdl, weld::IconView&, void);
+ DECL_LINK(RowActivatedHdl, weld::TreeView&, bool);
+ DECL_LINK(ItemActivatedHdl, weld::IconView&, bool);
+
+ // IEnumerationResultHandler overridables
+ virtual void enumerationDone( ::svt::EnumerationResult eResult ) override;
+ void implEnumerationSuccess();
+};
+
+inline void SvtFileView_Impl::EnableDelete( bool bEnable )
+{
+ mxView->EnableDelete( bEnable );
+}
+
+namespace
+{
+ // functions -------------------------------------------------------------
+
+ OUString CreateExactSizeText( sal_Int64 nSize )
+ {
+ double fSize( static_cast<double>(nSize) );
+ int nDec;
+
+ tools::Long nMega = 1024 * 1024;
+ tools::Long nGiga = nMega * 1024;
+
+ OUString aUnitStr(' ');
+
+ if ( nSize < 10000 )
+ {
+ aUnitStr += SvtResId(STR_SVT_BYTES );
+ nDec = 0;
+ }
+ else if ( nSize < nMega )
+ {
+ fSize /= 1024;
+ aUnitStr += SvtResId(STR_SVT_KB);
+ nDec = 1;
+ }
+ else if ( nSize < nGiga )
+ {
+ fSize /= nMega;
+ aUnitStr += SvtResId(STR_SVT_MB);
+ nDec = 2;
+ }
+ else
+ {
+ fSize /= nGiga;
+ aUnitStr += SvtResId(STR_SVT_GB);
+ nDec = 3;
+ }
+
+ OUString aSizeStr =
+ ::rtl::math::doubleToUString( fSize,
+ rtl_math_StringFormat_F, nDec,
+ SvtSysLocale().GetLocaleData().getNumDecimalSep()[0]) +
+ aUnitStr;
+
+ return aSizeStr;
+ }
+}
+
+ViewTabListBox_Impl::ViewTabListBox_Impl(std::unique_ptr<weld::TreeView> xTreeView,
+ weld::Window* pTopLevel,
+ SvtFileView_Impl* pParent,
+ FileViewFlags nFlags)
+ : mxTreeView(std::move(xTreeView))
+ , mxScratchIter(mxTreeView->make_iterator())
+ , mpParent( pParent )
+ , mnSearchIndex( 0 )
+ , mbEnableDelete( false )
+ , mbEditing( false )
+ , mbShowType(nFlags & FileViewFlags::SHOW_TYPE)
+{
+ std::vector<int> aWidths { 180 };
+ if (nFlags & FileViewFlags::SHOW_TYPE)
+ aWidths.push_back(140);
+ aWidths.push_back(80);
+ mxTreeView->set_column_fixed_widths(aWidths);
+
+ if (nFlags & FileViewFlags::MULTISELECTION)
+ mxTreeView->set_selection_mode(SelectionMode::Multiple);
+
+ maResetQuickSearch.SetTimeout( QUICK_SEARCH_TIMEOUT );
+ maResetQuickSearch.SetInvokeHandler( LINK( this, ViewTabListBox_Impl, ResetQuickSearch_Impl ) );
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xInteractionHandler(
+ InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
+
+ mxCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
+
+ mxTreeView->connect_popup_menu(LINK(this, ViewTabListBox_Impl, CommandHdl));
+ mxTreeView->connect_key_press(LINK(this, ViewTabListBox_Impl, KeyInputHdl));
+}
+
+IMPL_LINK_NOARG(ViewTabListBox_Impl, EditingEntryHdl, const weld::TreeIter&, bool)
+{
+ return mbEditing;
+}
+
+IMPL_LINK_NOARG(ViewTabListBox_Impl, ResetQuickSearch_Impl, Timer *, void)
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maQuickSearchText.clear();
+ mnSearchIndex = 0;
+}
+
+IMPL_LINK(ViewTabListBox_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ if (mbEditing)
+ return false;
+
+ bool bHandled = false;
+
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ if ( 0 == rKeyCode.GetModifier() )
+ {
+ if ( ( rKeyCode.GetCode() == KEY_DELETE ) &&
+ mbEnableDelete )
+ {
+ ResetQuickSearch_Impl( nullptr );
+ DeleteEntries();
+ bHandled = true;
+ }
+ else if ( ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM ) ||
+ ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA ) )
+ {
+ DoQuickSearch( rKEvt.GetCharCode() );
+ bHandled = true;
+ }
+ }
+
+ if (!bHandled)
+ ResetQuickSearch_Impl( nullptr );
+ return bHandled;
+}
+
+IMPL_LINK(ViewTabListBox_Impl, CommandHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ bool bEnableDelete = mbEnableDelete;
+ bool bEnableRename = true;
+
+ int nCount = 0;
+ mxTreeView->selected_foreach([this, &nCount, &bEnableDelete, &bEnableRename](weld::TreeIter& rEntry){
+ ++nCount;
+
+ ::ucbhelper::Content aCnt;
+ try
+ {
+ OUString aURL(weld::fromId<SvtContentEntry*>(
+ mxTreeView->get_id(rEntry))->maURL);
+ aCnt = ::ucbhelper::Content( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
+ }
+ catch( Exception const & )
+ {
+ bEnableDelete = bEnableRename = false;
+ }
+
+ if ( bEnableDelete )
+ {
+ try
+ {
+ Reference< XCommandInfo > aCommands = aCnt.getCommands();
+ if ( aCommands.is() )
+ bEnableDelete = aCommands->hasCommandByName( "delete" );
+ else
+ bEnableDelete = false;
+ }
+ catch( Exception const & )
+ {
+ bEnableDelete = false;
+ }
+ }
+
+ if ( bEnableRename )
+ {
+ try
+ {
+ Reference< XPropertySetInfo > aProps = aCnt.getProperties();
+ if ( aProps.is() )
+ {
+ Property aProp = aProps->getPropertyByName("Title");
+ bEnableRename
+ = !( aProp.Attributes & PropertyAttribute::READONLY );
+ }
+ else
+ bEnableRename = false;
+ }
+ catch( Exception const & )
+ {
+ bEnableRename = false;
+ }
+ }
+
+ bool bStop = !bEnableDelete && !bEnableRename;
+ return bStop;
+ });
+
+ if (nCount == 0)
+ bEnableDelete = false;
+ if (nCount != 1)
+ bEnableRename = false;
+
+ if (bEnableDelete || bEnableRename)
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(mxTreeView.get(), "svt/ui/fileviewmenu.ui"));
+ auto xContextMenu = xBuilder->weld_menu("menu");
+ xContextMenu->set_visible("delete", bEnableDelete);
+ xContextMenu->set_visible("rename", bEnableRename);
+ OUString sCommand(xContextMenu->popup_at_rect(mxTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
+ ExecuteContextMenuAction(sCommand);
+ }
+
+ return true;
+}
+
+void ViewTabListBox_Impl::ExecuteContextMenuAction(std::u16string_view rSelectedPopupEntry)
+{
+ if (rSelectedPopupEntry == u"delete")
+ DeleteEntries();
+ else if (rSelectedPopupEntry == u"rename")
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator();
+ if (mxTreeView->get_selected(xEntry.get()))
+ {
+ mbEditing = true;
+
+ mxTreeView->connect_editing(LINK(this, ViewTabListBox_Impl, EditingEntryHdl),
+ LINK(this, ViewTabListBox_Impl, EditedEntryHdl));
+
+ mxTreeView->start_editing(*xEntry);
+ }
+ }
+}
+
+void ViewTabListBox_Impl::DeleteEntries()
+{
+ short eResult = svtools::QUERYDELETE_YES;
+
+ mxTreeView->selected_foreach([this, &eResult](weld::TreeIter& rCurEntry){
+ OUString aURL;
+ if (!mxTreeView->get_id(rCurEntry).isEmpty())
+ aURL = weld::fromId<SvtContentEntry*>(mxTreeView->get_id(rCurEntry))->maURL;
+ if (aURL.isEmpty())
+ {
+ mxTreeView->unselect(rCurEntry);
+ return false;
+ }
+
+ bool canDelete = true;
+ try
+ {
+ ::ucbhelper::Content aCnt( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
+ Reference< XCommandInfo > aCommands = aCnt.getCommands();
+ if ( aCommands.is() )
+ canDelete = aCommands->hasCommandByName( "delete" );
+ else
+ canDelete = false;
+ }
+ catch( Exception const & )
+ {
+ canDelete = false;
+ }
+
+ if (!canDelete)
+ {
+ mxTreeView->unselect(rCurEntry);
+ return false; // process next entry
+ }
+
+ if ( eResult != svtools::QUERYDELETE_ALL )
+ {
+ INetURLObject aObj( aURL );
+ svtools::QueryDeleteDlg_Impl aDlg(
+ mxTreeView.get(), aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset));
+
+ if (mxTreeView->count_selected_rows() > 1)
+ aDlg.EnableAllButton();
+
+ eResult = aDlg.run();
+ }
+
+ bool bDeleted = false;
+
+ if (eResult == svtools::QUERYDELETE_ALL || eResult == svtools::QUERYDELETE_YES)
+ {
+ if ( Kill( aURL ) )
+ {
+ mpParent->EntryRemoved( aURL );
+ bDeleted = true;
+ }
+ }
+
+ if (!bDeleted)
+ mxTreeView->unselect(rCurEntry);
+
+ return false;
+ });
+
+ mxTreeView->remove_selection();
+}
+
+IMPL_LINK(ViewTabListBox_Impl, EditedEntryHdl, const IterString&, rIterString, bool)
+{
+ mbEditing = false;
+
+ mxTreeView->connect_editing(Link<const weld::TreeIter&, bool>(), Link<const IterString&, bool>());
+
+ const weld::TreeIter& rEntry = rIterString.first;
+ OUString sNewText = rIterString.second;
+
+ if (sNewText.isEmpty())
+ return false;
+
+ bool bRet = false;
+
+ OUString aURL;
+ SvtContentEntry* pData = weld::fromId<SvtContentEntry*>(mxTreeView->get_id(rEntry));
+
+ if ( pData )
+ aURL = pData->maURL;
+
+ if ( aURL.isEmpty() )
+ return bRet;
+
+ try
+ {
+ OUString aPropName( "Title" );
+ bool canRename = true;
+ ::ucbhelper::Content aContent( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
+
+ try
+ {
+ Reference< XPropertySetInfo > aProps = aContent.getProperties();
+ if ( aProps.is() )
+ {
+ Property aProp = aProps->getPropertyByName( aPropName );
+ canRename = !( aProp.Attributes & PropertyAttribute::READONLY );
+ }
+ else
+ {
+ canRename = false;
+ }
+ }
+ catch ( Exception const & )
+ {
+ canRename = false;
+ }
+
+ if ( canRename )
+ {
+ Any aValue;
+ aValue <<= sNewText;
+ aContent.setPropertyValue( aPropName, aValue );
+ mpParent->EntryRenamed(aURL, sNewText);
+
+ if (pData)
+ pData->maURL = aURL;
+
+ mxTreeView->set_id(rEntry, weld::toId(pData));
+
+ bRet = true;
+ }
+ }
+ catch( Exception const & )
+ {
+ }
+
+ return bRet;
+}
+
+void ViewTabListBox_Impl::DoQuickSearch( sal_Unicode rChar )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maResetQuickSearch.Stop();
+
+ OUString aLastText = maQuickSearchText;
+ sal_uInt32 aLastPos = mnSearchIndex;
+
+ maQuickSearchText += OUString(rChar).toAsciiLowerCase();
+
+ bool bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, false );
+
+ if ( !bFound && ( aLastText.getLength() == 1 ) &&
+ ( aLastText == OUStringChar(rChar) ) )
+ {
+ mnSearchIndex = aLastPos + 1;
+ maQuickSearchText = aLastText;
+ bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, true );
+ }
+
+ if (bFound)
+ {
+ mxTreeView->unselect_all();
+ mxTreeView->select(mnSearchIndex);
+ mxTreeView->set_cursor(mnSearchIndex);
+ mxTreeView->scroll_to_row(mnSearchIndex);
+ }
+
+ maResetQuickSearch.Start();
+}
+
+bool ViewTabListBox_Impl::Kill( const OUString& rContent )
+{
+ bool bRet = true;
+
+ try
+ {
+ ::ucbhelper::Content aCnt( rContent, mxCmdEnv, comphelper::getProcessComponentContext() );
+ aCnt.executeCommand( "delete", Any( true ) );
+ }
+ catch( css::ucb::CommandAbortedException const & )
+ {
+ SAL_INFO( "svtools.contnr", "CommandAbortedException" );
+ bRet = false;
+ }
+ catch( Exception const & )
+ {
+ SAL_INFO( "svtools.contnr", "Any other exception" );
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+SvtFileView::SvtFileView(weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ bool bOnlyFolder, bool bMultiSelection, bool bShowType )
+{
+ FileViewFlags nFlags = FileViewFlags::NONE;
+ if ( bMultiSelection )
+ nFlags |= FileViewFlags::MULTISELECTION;
+ if ( bShowType )
+ nFlags |= FileViewFlags::SHOW_TYPE;
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xInteractionHandler(
+ InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
+ Reference < XCommandEnvironment > xCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
+
+ mpImpl.reset(new SvtFileView_Impl(this, pTopLevel, std::move(xTreeView), std::move(xIconView), xCmdEnv, nFlags, bOnlyFolder));
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ pView->connect_column_clicked(LINK(this, SvtFileView, HeaderSelect_Impl));
+}
+
+void SvtFileView::grab_focus()
+{
+ mpImpl->grab_focus();
+}
+
+bool SvtFileView::has_focus() const
+{
+ return mpImpl->has_focus();
+}
+
+SvtFileView::~SvtFileView()
+{
+}
+
+void SvtFileView::SetViewMode( FileViewMode eMode )
+{
+ mpImpl->SetViewMode( eMode );
+}
+
+OUString SvtFileView::GetURL(const weld::TreeIter& rEntry) const
+{
+ SvtContentEntry* pEntry;
+ if (mpImpl->mxView->get_visible())
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(rEntry));
+ else
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(rEntry));
+ if (pEntry)
+ return pEntry->maURL;
+ return OUString();
+}
+
+OUString SvtFileView::GetCurrentURL() const
+{
+ SvtContentEntry* pEntry = nullptr;
+ OUString aURL;
+ if (mpImpl->mxView->get_visible())
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
+ if (mpImpl->mxView->get_selected(xEntry.get()))
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(*xEntry));
+ }
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxIconView->make_iterator();
+ if (mpImpl->mxIconView->get_selected(xEntry.get()))
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(*xEntry));
+ }
+ if (pEntry)
+ aURL = pEntry->maURL;
+ return aURL;
+}
+
+void SvtFileView::CreatedFolder( const OUString& rUrl, const OUString& rNewFolder )
+{
+ const SortingData_Impl& rEntry = mpImpl->FolderInserted( rUrl, rNewFolder );
+
+ mpImpl->maEntries.emplace_back(std::make_unique<SvtContentEntry>(rUrl, true));
+ OUString sId(weld::toId(mpImpl->maEntries.back().get()));
+
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
+ mpImpl->mxView->insert(rEntry.maDisplayName, sId, mpImpl->maFolderImage, *xEntry);
+ mpImpl->mxView->scroll_to_row(*xEntry);
+
+ std::unique_ptr<weld::TreeIter> xIconEntry = mpImpl->mxIconView->make_iterator();
+ mpImpl->mxIconView->insert(-1, &rEntry.maDisplayName, &sId, &mpImpl->maFolderImage, xIconEntry.get());
+ mpImpl->mxIconView->scroll_to_item(*xIconEntry);
+}
+
+FileViewResult SvtFileView::PreviousLevel( const FileViewAsyncAction* pAsyncDescriptor )
+{
+ FileViewResult eResult = eFailure;
+
+ OUString sParentURL;
+ if ( GetParentURL( sParentURL ) )
+ eResult = Initialize( sParentURL, mpImpl->maCurrentFilter, pAsyncDescriptor, maDenyList );
+
+ return eResult;
+}
+
+bool SvtFileView::GetParentURL( OUString& rParentURL ) const
+{
+ bool bRet = false;
+ try
+ {
+ ::ucbhelper::Content aCnt( mpImpl->maViewURL, mpImpl->mxCmdEnv, comphelper::getProcessComponentContext() );
+ Reference< XContent > xContent( aCnt.get() );
+ Reference< css::container::XChild > xChild( xContent, UNO_QUERY );
+ if ( xChild.is() )
+ {
+ Reference< XContent > xParent( xChild->getParent(), UNO_QUERY );
+ if ( xParent.is() )
+ {
+ rParentURL = xParent->getIdentifier()->getContentIdentifier();
+ bRet = !rParentURL.isEmpty() && rParentURL != mpImpl->maViewURL;
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ // perhaps an unknown url protocol (e.g. "private:newdoc")
+ }
+
+ return bRet;
+}
+
+OUString SvtFileView::get_help_id() const
+{
+ return mpImpl->mxView->get_help_id();
+}
+
+void SvtFileView::set_help_id(const OUString& rHelpId)
+{
+ mpImpl->mxView->set_help_id(rHelpId);
+}
+
+OUString SvtFileView::get_selected_text() const
+{
+ if (mpImpl->mxView->get_visible())
+ return mpImpl->mxView->get_selected_text();
+ return mpImpl->mxIconView->get_selected_text();
+}
+
+FileViewResult SvtFileView::Initialize(
+ const OUString& rURL,
+ const OUString& rFilter,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList )
+{
+ weld::WaitObject aWaitCursor(mpImpl->m_pTopLevel);
+ maDenyList = rDenyList;
+
+ OUString sPushURL( mpImpl->maViewURL );
+
+ mpImpl->maViewURL = rURL;
+ FileViewResult eResult = ExecuteFilter( rFilter, pAsyncDescriptor );
+ switch ( eResult )
+ {
+ case eFailure:
+ case eTimeout:
+ mpImpl->maViewURL = sPushURL;
+ return eResult;
+
+ case eStillRunning:
+ OSL_ENSURE( pAsyncDescriptor, "SvtFileView::Initialize: we told it to read synchronously!" );
+ [[fallthrough]];
+ case eSuccess:
+ return eResult;
+ }
+
+ OSL_FAIL( "SvtFileView::Initialize: unreachable!" );
+ return eFailure;
+}
+
+FileViewResult SvtFileView::ExecuteFilter( const OUString& rFilter, const FileViewAsyncAction* pAsyncDescriptor )
+{
+ mpImpl->maCurrentFilter = rFilter.toAsciiLowerCase();
+
+ mpImpl->Clear();
+ FileViewResult eResult = mpImpl->GetFolderContent_Impl(mpImpl->maViewURL, pAsyncDescriptor, maDenyList);
+ OSL_ENSURE( ( eResult != eStillRunning ) || pAsyncDescriptor, "SvtFileView::ExecuteFilter: we told it to read synchronously!" );
+ return eResult;
+}
+
+void SvtFileView::CancelRunningAsyncAction()
+{
+ mpImpl->CancelRunningAsyncAction();
+}
+
+void SvtFileView::SetNoSelection()
+{
+ mpImpl->mxView->unselect_all();
+ mpImpl->mxIconView->unselect_all();
+}
+
+void SvtFileView::SetSelectHdl(const Link<SvtFileView*,void>& rHdl)
+{
+ mpImpl->SetSelectHandler(rHdl);
+}
+
+void SvtFileView::SetDoubleClickHdl(const Link<SvtFileView*,bool>& rHdl)
+{
+ mpImpl->SetDoubleClickHandler(rHdl);
+}
+
+sal_uInt32 SvtFileView::GetSelectionCount() const
+{
+ if (mpImpl->mxView->get_visible())
+ return mpImpl->mxView->count_selected_rows();
+ return mpImpl->mxIconView->count_selected_items();
+}
+
+SvtContentEntry* SvtFileView::FirstSelected() const
+{
+ if (mpImpl->mxView->get_visible())
+ {
+ SvtContentEntry* pRet = nullptr;
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
+ if (mpImpl->mxView->get_selected(xEntry.get()))
+ pRet = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(*xEntry));
+ return pRet;
+ }
+
+ SvtContentEntry* pRet = nullptr;
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxIconView->make_iterator();
+ if (mpImpl->mxIconView->get_selected(xEntry.get()))
+ pRet = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(*xEntry));
+ return pRet;
+}
+
+const OUString& SvtFileView::GetViewURL() const
+{
+ return mpImpl->maViewURL;
+}
+
+void SvtFileView::SetOpenDoneHdl( const Link<SvtFileView*,void>& rHdl )
+{
+ mpImpl->maOpenDoneLink = rHdl;
+}
+
+void SvtFileView::EnableDelete( bool bEnable )
+{
+ mpImpl->EnableDelete( bEnable );
+}
+
+void SvtFileView::EndInplaceEditing()
+{
+ return mpImpl->EndEditing();
+}
+
+IMPL_LINK(SvtFileView, HeaderSelect_Impl, int, nColumn, void)
+{
+ sal_uInt16 nItemID = nColumn + 1;
+ // skip "TYPE"
+ if (!mpImpl->mxView->TypeColumnVisible() && nItemID != COLUMN_TITLE)
+ ++nItemID;
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ bool bSortAtoZ = mpImpl->mbAscending;
+
+ //set new arrow positions in headerbar
+ if (nItemID != mpImpl->mnSortColumn)
+ {
+ // remove old indicator, new will be created in OpenFolder_Impl
+ pView->set_sort_indicator(TRISTATE_INDET, mpImpl->GetSortColumn());
+ }
+ else
+ bSortAtoZ = !bSortAtoZ;
+
+ mpImpl->Resort_Impl(nItemID, bSortAtoZ);
+}
+
+OUString SvtFileView::GetConfigString() const
+{
+ // sort order
+ OUString sRet = OUString::number( mpImpl->mnSortColumn ) + ";";
+
+ bool bUp = mpImpl->mbAscending;
+ sRet += OUString::Concat(bUp ? std::u16string_view(u"1") : std::u16string_view(u"0")) + ";";
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ sal_uInt16 nCount = mpImpl->mxView->TypeColumnVisible() ? 4 : 3;
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ sal_uInt16 nId = i + 1;
+ // skip "TYPE"
+ if (!mpImpl->mxView->TypeColumnVisible() && nId != COLUMN_TITLE)
+ ++nId;
+
+ sRet += OUString::number( nId )
+ + ";"
+ + OUString::number(pView->get_column_width(i))
+ + ";";
+ }
+
+ return comphelper::string::stripEnd(sRet, ';');
+}
+
+::std::vector< SvtContentEntry > SvtFileView::GetContent()
+{
+ ::std::vector< SvtContentEntry > aContent;
+
+ for(auto const& elem : mpImpl->maContent)
+ {
+ SvtContentEntry aEntry( elem->maTargetURL, elem->mbIsFolder );
+ aContent.push_back( aEntry );
+ }
+
+ return aContent;
+}
+
+void SvtFileView::SetConfigString(std::u16string_view rCfgStr)
+{
+ sal_Int32 nIdx = 0;
+ sal_uInt16 nSortColumn = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx )));
+ bool bAscending = static_cast<bool>(static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ))));
+
+ std::vector<int> aWidths(mpImpl->mxView->TypeColumnVisible() ? 4 : 3, -1);
+
+ while ( nIdx != -1 )
+ {
+ sal_uInt16 nItemId = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx )));
+
+ int nWidth = o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ));
+
+ // skip "TYPE"
+ if (!mpImpl->mxView->TypeColumnVisible() && nItemId != COLUMN_TITLE)
+ --nItemId;
+ int nColumn = nItemId - 1;
+
+ if (nColumn >= 0 && o3tl::make_unsigned(nColumn) < aWidths.size())
+ aWidths[nColumn] = nWidth;
+ }
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ pView->set_column_fixed_widths(aWidths);
+ if (mpImpl->mnSortColumn != nSortColumn)
+ pView->set_sort_indicator(TRISTATE_INDET, mpImpl->GetSortColumn());
+ mpImpl->Resort_Impl(nSortColumn, bAscending);
+}
+
+SvtFileView_Impl::SvtFileView_Impl(SvtFileView* pAntiImpl, weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ Reference < XCommandEnvironment > const & xEnv,
+ FileViewFlags nFlags, bool bOnlyFolder)
+ : m_pAntiImpl ( pAntiImpl )
+ , m_eAsyncActionResult ( ::svt::EnumerationResult::ERROR )
+ , m_bRunningAsyncAction ( false )
+ , m_bAsyncActionCancelled ( false )
+ , m_pTopLevel ( pTopLevel )
+ , mxView(new ViewTabListBox_Impl(std::move(xTreeView), pTopLevel, this, nFlags))
+ , mxIconView(std::move(xIconView))
+ , mnSortColumn ( COLUMN_TITLE )
+ , mbAscending ( true )
+ , mbOnlyFolder ( bOnlyFolder )
+ , mnSuspendSelectCallback ( 0 )
+ , mbIsFirstResort ( true )
+ , aIntlWrapper ( Application::GetSettings().GetLanguageTag() )
+ , maFolderImage (RID_BMP_FOLDER)
+ , mxCmdEnv ( xEnv )
+{
+ weld::TreeView* pWidget = mxView->getWidget();
+
+ // set the width to something small so it's the parent that decides the final
+ // width
+ Size aSize(42, pWidget->get_height_rows(7));
+ pWidget->set_size_request(aSize.Width(), aSize.Height());
+ mxIconView->set_size_request(aSize.Width(), aSize.Height());
+}
+
+SvtFileView_Impl::~SvtFileView_Impl()
+{
+ Clear();
+}
+
+void SvtFileView_Impl::Clear()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maContent.clear();
+}
+
+FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
+ std::u16string_view rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList )
+{
+ ::osl::ClearableMutexGuard aGuard( maMutex );
+ INetURLObject aFolderObj( rFolder );
+ DBG_ASSERT( aFolderObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
+
+ FolderDescriptor aFolder( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ aGuard.clear();
+ return GetFolderContent_Impl( aFolder, pAsyncDescriptor, rDenyList );
+}
+
+FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
+ const FolderDescriptor& _rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList )
+{
+ DBG_TESTSOLARMUTEX();
+ ::osl::ClearableMutexGuard aGuard( maMutex );
+
+ OSL_ENSURE( !m_xContentEnumerator.is(), "SvtFileView_Impl::GetFolderContent_Impl: still running another enumeration!" );
+ m_xContentEnumerator.set(new ::svt::FileViewContentEnumerator(
+ mxView->GetCommandEnvironment(), maContent, maMutex));
+ // TODO: should we cache and re-use this thread?
+
+ if ( !pAsyncDescriptor )
+ {
+ ::svt::EnumerationResult eResult = m_xContentEnumerator->enumerateFolderContentSync( _rFolder, rDenyList );
+ if ( ::svt::EnumerationResult::SUCCESS == eResult )
+ {
+ implEnumerationSuccess();
+ m_xContentEnumerator.clear();
+ return eSuccess;
+ }
+ m_xContentEnumerator.clear();
+ return eFailure;
+ }
+
+ m_bRunningAsyncAction = true;
+ m_bAsyncActionCancelled = false;
+ m_eAsyncActionResult = ::svt::EnumerationResult::ERROR;
+ m_aAsyncActionFinished.reset();
+
+ // don't (yet) set m_aCurrentAsyncActionHandler to pTimeout->aFinishHandler.
+ // By definition, this handler *only* gets called when the result cannot be obtained
+ // during the minimum wait time, so it is only set below, when needed.
+ m_aCurrentAsyncActionHandler = Link<void*,void>();
+
+ // minimum time to wait
+ TimeValue aTimeout;
+ sal_Int32 nMinTimeout = pAsyncDescriptor->nMinTimeout;
+ OSL_ENSURE( nMinTimeout > 0, "SvtFileView_Impl::GetFolderContent_Impl: invalid minimum timeout!" );
+ if ( nMinTimeout <= 0 )
+ nMinTimeout = sal_Int32( 1000 );
+ aTimeout.Seconds = nMinTimeout / 1000;
+ aTimeout.Nanosec = ( nMinTimeout % 1000 ) * 1000000;
+
+ m_xContentEnumerator->enumerateFolderContent( _rFolder, this );
+
+ // wait until the enumeration is finished
+ // for this, release our own mutex (which is used by the enumerator thread)
+ aGuard.clear();
+
+ ::osl::Condition::Result eResult = ::osl::Condition::result_ok;
+ {
+ // also release the SolarMutex. Not all code which is needed during the enumeration
+ // is Solar-Thread-Safe, in particular there is some code which needs to access
+ // string resources (and our resource system relies on the SolarMutex :()
+ SolarMutexReleaser aSolarRelease;
+
+ // now wait. Note that if we didn't get a pAsyncDescriptor, then this is an infinite wait.
+ eResult = m_aAsyncActionFinished.wait( &aTimeout );
+ }
+
+ ::osl::MutexGuard aGuard2( maMutex );
+ if ( ::osl::Condition::result_timeout == eResult )
+ {
+ // maximum time to wait
+ OSL_ENSURE(!m_xCancelAsyncTimer,
+ "SvtFileView_Impl::GetFolderContent_Impl: there's still a previous timer!");
+ m_xCancelAsyncTimer.set(new CallbackTimer(this));
+ sal_Int32 nMaxTimeout = pAsyncDescriptor->nMaxTimeout;
+ OSL_ENSURE( nMaxTimeout > nMinTimeout,
+ "SvtFileView_Impl::GetFolderContent_Impl: invalid maximum timeout!" );
+ if ( nMaxTimeout <= nMinTimeout )
+ nMaxTimeout = nMinTimeout + 5000;
+ m_xCancelAsyncTimer->setRemainingTime( salhelper::TTimeValue( nMaxTimeout - nMinTimeout ) );
+ // we already waited for nMinTimeout milliseconds, so take this into account
+ m_xCancelAsyncTimer->start();
+
+ m_aCurrentAsyncActionHandler = pAsyncDescriptor->aFinishHandler;
+ DBG_ASSERT( m_aCurrentAsyncActionHandler.IsSet(), "SvtFileView_Impl::GetFolderContent_Impl: nobody interested when it's finished?" );
+ maEntries.clear();
+ mxView->clear();
+ mxIconView->clear();
+ return eStillRunning;
+ }
+
+ m_bRunningAsyncAction = false;
+ switch ( m_eAsyncActionResult )
+ {
+ case ::svt::EnumerationResult::SUCCESS:
+ return eSuccess;
+
+ case ::svt::EnumerationResult::ERROR:
+ return eFailure;
+ }
+
+ SAL_WARN( "svtools.contnr", "SvtFileView_Impl::GetFolderContent_Impl: unreachable!" );
+ return eFailure;
+}
+
+void SvtFileView_Impl::FilterFolderContent_Impl( std::u16string_view rFilter )
+{
+ if ( rFilter.empty() || ( rFilter == ALL_FILES_FILTER ) )
+ // when replacing names, there is always something to filter (no view of ".nametranslation.table")
+ return;
+
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if ( maContent.empty() )
+ return;
+
+ // collect the filter tokens
+ ::std::vector< WildCard > aFilters;
+ FilterMatch::createWildCardFilterList(rFilter,aFilters);
+
+
+ // do the filtering
+ std::erase_if(maContent,
+ [&aFilters](const std::unique_ptr<SortingData_Impl>& rxContent) {
+ if (rxContent->mbIsFolder)
+ return false;
+ // normalize the content title (we always match case-insensitive)
+ // 91872 - 11.09.2001 - frank.schoenheit@sun.com
+ OUString sCompareString = rxContent->GetFileName(); // filter works on file name, not on title!
+ return std::none_of(aFilters.begin(), aFilters.end(), FilterMatch(sCompareString));
+ });
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, ChangedHdl, weld::TreeView&, void)
+{
+ if (!mnSuspendSelectCallback)
+ m_aSelectHandler.Call(m_pAntiImpl);
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, SelectionChangedHdl, weld::IconView&, void)
+{
+ if (!mnSuspendSelectCallback)
+ m_aSelectHandler.Call(m_pAntiImpl);
+}
+
+void SvtFileView_Impl::SetSelectHandler(const Link<SvtFileView*,void>& rHdl)
+{
+ m_aSelectHandler = rHdl;
+
+ mxView->connect_changed(LINK(this, SvtFileView_Impl, ChangedHdl));
+ mxIconView->connect_selection_changed(LINK(this, SvtFileView_Impl, SelectionChangedHdl));
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, RowActivatedHdl, weld::TreeView&, bool)
+{
+ return maDoubleClickHandler.Call(m_pAntiImpl);
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, ItemActivatedHdl, weld::IconView&, bool)
+{
+ return maDoubleClickHandler.Call(m_pAntiImpl);
+}
+
+void SvtFileView_Impl::SetDoubleClickHandler(const Link<SvtFileView*,bool>& rHdl)
+{
+ maDoubleClickHandler = rHdl;
+
+ mxView->connect_row_activated(LINK(this, SvtFileView_Impl, RowActivatedHdl));
+ mxIconView->connect_item_activated(LINK(this, SvtFileView_Impl, ItemActivatedHdl));
+}
+
+void SvtFileView_Impl::OpenFolder_Impl()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ mxView->freeze();
+ mxIconView->freeze();
+ maEntries.clear();
+ mxView->clear();
+ mxIconView->clear();
+
+ for (auto const& elem : maContent)
+ {
+ if (mbOnlyFolder && !elem->mbIsFolder)
+ continue;
+
+ // insert entry and set user data
+ maEntries.emplace_back(std::make_unique<SvtContentEntry>(elem->maTargetURL, elem->mbIsFolder));
+ OUString sId(weld::toId(maEntries.back().get()));
+ mxView->append(sId, elem->maDisplayName, elem->maType, elem->maDisplaySize, elem->maDisplayDate, elem->maImage);
+ mxIconView->append(sId, elem->maDisplayName, elem->maImage);
+ }
+
+ ++mnSuspendSelectCallback;
+ mxView->thaw();
+
+ //set sort indicator
+ weld::TreeView* pView = mxView->getWidget();
+ pView->set_sort_indicator(mbAscending ? TRISTATE_TRUE : TRISTATE_FALSE, GetSortColumn());
+
+ mxIconView->thaw();
+ --mnSuspendSelectCallback;
+
+ ResetCursor();
+}
+
+void SvtFileView_Impl::ResetCursor()
+{
+ if (mxView->get_visible())
+ {
+ std::unique_ptr<weld::TreeIter> xFirst = mxView->make_iterator();
+ if (mxView->get_iter_first(*xFirst))
+ {
+ // set cursor to the first entry
+ mxView->set_cursor(*xFirst);
+ }
+ // deselect
+ mxView->unselect_all();
+ }
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xFirst = mxIconView->make_iterator();
+ if (mxIconView->get_iter_first(*xFirst))
+ {
+ // set cursor to the first entry
+ mxIconView->set_cursor(*xFirst);
+ }
+ // deselect
+ mxIconView->unselect_all();
+ }
+}
+
+void SvtFileView_Impl::CancelRunningAsyncAction()
+{
+ DBG_TESTSOLARMUTEX();
+ ::osl::MutexGuard aGuard( maMutex );
+ if ( !m_xContentEnumerator.is() )
+ return;
+
+ m_bAsyncActionCancelled = true;
+ m_xContentEnumerator->cancel();
+ m_bRunningAsyncAction = false;
+
+ m_xContentEnumerator.clear();
+ if ( m_xCancelAsyncTimer.is() && m_xCancelAsyncTimer->isTicking() )
+ m_xCancelAsyncTimer->stop();
+ m_xCancelAsyncTimer.clear();
+}
+
+
+void SvtFileView_Impl::onTimeout()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( maMutex );
+ if ( !m_bRunningAsyncAction )
+ // there might have been a race condition while we waited for the mutex
+ return;
+
+ CancelRunningAsyncAction();
+
+ if ( m_aCurrentAsyncActionHandler.IsSet() )
+ {
+ Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( eTimeout ) );
+ m_aCurrentAsyncActionHandler = Link<void*,void>();
+ }
+}
+
+
+void SvtFileView_Impl::enumerationDone( ::svt::EnumerationResult eResult )
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( maMutex );
+
+ m_xContentEnumerator.clear();
+ if ( m_xCancelAsyncTimer.is() && m_xCancelAsyncTimer->isTicking() )
+ m_xCancelAsyncTimer->stop();
+ m_xCancelAsyncTimer.clear();
+
+ if ( m_bAsyncActionCancelled )
+ // this is to prevent race conditions
+ return;
+
+ m_eAsyncActionResult = eResult;
+ m_bRunningAsyncAction = false;
+
+ m_aAsyncActionFinished.set();
+
+ if ( svt::EnumerationResult::SUCCESS == eResult )
+ implEnumerationSuccess();
+
+ if ( m_aCurrentAsyncActionHandler.IsSet() )
+ {
+ Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( m_eAsyncActionResult ) );
+ m_aCurrentAsyncActionHandler = Link<void*,void>();
+ }
+}
+
+
+void SvtFileView_Impl::implEnumerationSuccess()
+{
+ FilterFolderContent_Impl( maCurrentFilter );
+ SortFolderContent_Impl();
+ CreateDisplayText_Impl();
+ OpenFolder_Impl();
+ maOpenDoneLink.Call( m_pAntiImpl );
+}
+
+OUString SvtFileView_Impl::ReplaceTabWithString(const OUString& rValue)
+{
+ return rValue.replaceAll(u"\t", u"%09");
+}
+
+void SvtFileView_Impl::CreateDisplayText_Impl()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ for (auto const& elem : maContent)
+ {
+ // title, type, size, date
+ elem->maDisplayName = ReplaceTabWithString(elem->GetTitle());
+ // folders don't have a size
+ if ( ! elem->mbIsFolder )
+ elem->maDisplaySize = CreateExactSizeText( elem->maSize );
+ // set the date, but volumes have no date
+ if ( ! elem->mbIsFolder || ! elem->mbIsVolume )
+ {
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
+ elem->maDisplayDate = rLocaleData.getDate( elem->maModDate )
+ + ", "
+ + rLocaleData.getTime( elem->maModDate, false );
+ }
+
+ // detect image
+ if ( elem->mbIsFolder )
+ {
+ ::svtools::VolumeInfo aVolInfo( elem->mbIsVolume, elem->mbIsRemote,
+ elem->mbIsRemoveable, elem->mbIsFloppy,
+ elem->mbIsCompactDisc );
+ elem->maImage = SvFileInformationManager::GetFolderImageId(aVolInfo);
+ }
+ else
+ elem->maImage = SvFileInformationManager::GetFileImageId(INetURLObject(elem->maTargetURL));
+ }
+}
+
+void SvtFileView_Impl::Resort_Impl( sal_Int16 nColumn, bool bAscending )
+{
+ // TODO: IconView ()
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if ( ( nColumn == mnSortColumn ) &&
+ ( bAscending == mbAscending ) )
+ return;
+
+ // reset the quick search index
+ mxView->ResetQuickSearch_Impl( nullptr );
+
+ std::unique_ptr<weld::TreeIter> xEntry(mxView->make_iterator());
+ bool bEntry = mxView->get_cursor(xEntry.get());
+
+ OUString aEntryURL;
+ if (bEntry && !mxView->get_id(*xEntry).isEmpty())
+ aEntryURL = weld::fromId<SvtContentEntry*>(mxView->get_id(*xEntry))->maURL;
+
+ mnSortColumn = nColumn;
+ mbAscending = bAscending;
+
+ SortFolderContent_Impl();
+ OpenFolder_Impl();
+
+ if ( !mbIsFirstResort )
+ {
+ int nPos = GetEntryPos( aEntryURL );
+ if (nPos != -1 && nPos < mxView->n_children())
+ {
+ ++mnSuspendSelectCallback; // #i15668#
+ mxView->set_cursor(nPos);
+ --mnSuspendSelectCallback;
+ }
+ }
+ else
+ mbIsFirstResort = false;
+}
+
+static bool gbAscending = true;
+static sal_Int16 gnColumn = COLUMN_TITLE;
+static const CollatorWrapper* pCollatorWrapper = nullptr;
+
+/* this function returns true, if aOne is less than aTwo
+*/
+static bool CompareSortingData_Impl( std::unique_ptr<SortingData_Impl> const & aOne, std::unique_ptr<SortingData_Impl> const & aTwo )
+{
+ DBG_ASSERT( pCollatorWrapper, "*CompareSortingData_Impl(): Can't work this way!" );
+
+ sal_Int32 nComp;
+ bool bRet = false;
+ bool bEqual = false;
+
+ if ( aOne->mbIsFolder != aTwo->mbIsFolder )
+ {
+ bRet = aOne->mbIsFolder;
+
+ // !!! pb: #100376# folder always on top
+ if ( !gbAscending )
+ bRet = !bRet;
+ }
+ else
+ {
+ switch ( gnColumn )
+ {
+ case COLUMN_TITLE:
+ // compare case insensitive first
+ nComp = pCollatorWrapper->compareString( aOne->GetLowerTitle(), aTwo->GetLowerTitle() );
+
+ if ( nComp == 0 )
+ nComp = pCollatorWrapper->compareString( aOne->GetTitle(), aTwo->GetTitle() );
+
+ if ( nComp < 0 )
+ bRet = true;
+ else if ( nComp > 0 )
+ bRet = false;
+ else
+ bEqual = true;
+ break;
+ case COLUMN_TYPE:
+ nComp = pCollatorWrapper->compareString( aOne->maType, aTwo->maType );
+ if ( nComp < 0 )
+ bRet = true;
+ else if ( nComp > 0 )
+ bRet = false;
+ else
+ bEqual = true;
+ break;
+ case COLUMN_SIZE:
+ if ( aOne->maSize < aTwo->maSize )
+ bRet = true;
+ else if ( aOne->maSize > aTwo->maSize )
+ bRet = false;
+ else
+ bEqual = true;
+ break;
+ case COLUMN_DATE:
+ if ( aOne->maModDate < aTwo->maModDate )
+ bRet = true;
+ else if ( aOne->maModDate > aTwo->maModDate )
+ bRet = false;
+ else
+ bEqual = true;
+ break;
+ default:
+ SAL_INFO( "svtools.contnr", "CompareSortingData_Impl: Compare unknown type!" );
+ bRet = false;
+ }
+ }
+
+ // when the two elements are equal, we must not return sal_True (which would
+ // happen if we just return ! ( a < b ) when not sorting ascending )
+ if ( bEqual )
+ return false;
+
+ return gbAscending ? bRet : !bRet;
+}
+
+
+void SvtFileView_Impl::SortFolderContent_Impl()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if ( maContent.size() > 1 )
+ {
+ gbAscending = mbAscending;
+ gnColumn = mnSortColumn;
+ pCollatorWrapper = aIntlWrapper.getCaseCollator();
+
+ std::stable_sort( maContent.begin(), maContent.end(), CompareSortingData_Impl );
+
+ pCollatorWrapper = nullptr;
+ }
+}
+
+
+void SvtFileView_Impl::EntryRemoved( std::u16string_view rURL )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maContent.erase(std::find_if(maContent.begin(), maContent.end(),
+ [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; }));
+}
+
+
+void SvtFileView_Impl::EntryRenamed( OUString& rURL,
+ const OUString& rTitle )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ auto aFoundElem = std::find_if(maContent.begin(), maContent.end(),
+ [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; });
+ if (aFoundElem != maContent.end())
+ {
+ (*aFoundElem)->SetNewTitle( rTitle );
+ (*aFoundElem)->maDisplayName = ReplaceTabWithString(rTitle);
+
+ INetURLObject aURLObj( rURL );
+ aURLObj.setName( rTitle, INetURLObject::EncodeMechanism::All );
+
+ rURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ (*aFoundElem)->maTargetURL = rURL;
+ }
+}
+
+const SortingData_Impl& SvtFileView_Impl::FolderInserted( const OUString& rURL, const OUString& rTitle )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ std::unique_ptr<SortingData_Impl> pData(new SortingData_Impl);
+
+ pData->SetNewTitle( rTitle );
+ pData->maSize = 0;
+ pData->mbIsFolder = true;
+ pData->maTargetURL = rURL;
+
+ ::svtools::VolumeInfo aVolInfo;
+ pData->maType = SvFileInformationManager::GetFolderDescription( aVolInfo );
+ pData->maImage = SvFileInformationManager::GetFolderImageId( aVolInfo );
+
+ // title, type, size, date
+ pData->maDisplayName = ReplaceTabWithString(pData->GetTitle());
+ // set the date
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
+ pData->maDisplayDate = rLocaleData.getDate( pData->maModDate )
+ + ", "
+ + rLocaleData.getTime( pData->maModDate );
+
+ maContent.push_back( std::move(pData) );
+
+ return *maContent.back();
+}
+
+int SvtFileView_Impl::GetEntryPos(std::u16string_view rURL)
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ auto aFoundElem = std::find_if(maContent.begin(), maContent.end(),
+ [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; });
+ return aFoundElem != maContent.end() ? std::distance(maContent.begin(), aFoundElem) : -1;
+}
+
+void SvtFileView_Impl::SetViewMode( FileViewMode eMode )
+{
+ switch ( eMode )
+ {
+ case eDetailedList:
+ mxView->show();
+ mxIconView->hide();
+ break;
+
+ case eIcon:
+ mxView->hide();
+ mxIconView->show();
+ break;
+
+ default:
+ mxView->show();
+ mxIconView->hide();
+ };
+}
+
+bool SvtFileView_Impl::SearchNextEntry( sal_uInt32& nIndex, std::u16string_view rTitle, bool bWrapAround )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ sal_uInt32 nEnd = maContent.size();
+ sal_uInt32 nStart = nIndex;
+ while ( nIndex < nEnd )
+ {
+ SortingData_Impl* pData = maContent[ nIndex ].get();
+ if ( pData->GetLowerTitle().startsWith( rTitle ) )
+ return true;
+ ++nIndex;
+ }
+
+ if ( bWrapAround )
+ {
+ nIndex = 0;
+ while ( nIndex < nEnd && nIndex <= nStart )
+ {
+ SortingData_Impl* pData = maContent[ nIndex ].get();
+ if ( pData->GetLowerTitle().startsWith( rTitle ) )
+ return true;
+ ++nIndex;
+ }
+ }
+
+ return false;
+}
+
+namespace {
+ void SAL_CALL CallbackTimer::onShot()
+ {
+ OSL_ENSURE( m_pTimeoutHandler, "CallbackTimer::onShot: nobody interested in?" );
+ SvtFileView_Impl* pHandler( m_pTimeoutHandler );
+ if ( pHandler )
+ pHandler->onTimeout();
+ }
+}
+
+void SvtFileView::selected_foreach(const std::function<bool(weld::TreeIter&)>& func)
+{
+ if (mpImpl->mxView->get_visible())
+ mpImpl->mxView->selected_foreach(func);
+ else
+ mpImpl->mxIconView->selected_foreach(func);
+}
+
+weld::Widget* SvtFileView::identifier() const
+{
+ return mpImpl->mxView->getWidget();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fileview.hxx b/fpicker/source/office/fileview.hxx
new file mode 100644
index 0000000000..a19c209550
--- /dev/null
+++ b/fpicker/source/office/fileview.hxx
@@ -0,0 +1,184 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <memory>
+#include <com/sun/star/uno/Sequence.h>
+#include <utility>
+#include <vcl/weld.hxx>
+#include <rtl/ustring.hxx>
+
+namespace com :: sun :: star :: ucb { class XContent; }
+
+// class SvtFileView -----------------------------------------------------
+
+class SvtFileView_Impl;
+struct SvtContentEntry;
+
+/// the result of an action in the FileView
+enum FileViewResult
+{
+ eSuccess,
+ eFailure,
+ eTimeout,
+ eStillRunning
+};
+
+enum FileViewMode
+{
+ eDetailedList,
+ eIcon
+};
+
+/// describes parameters for doing an action on the FileView asynchronously
+struct FileViewAsyncAction
+{
+ sal_uInt32 nMinTimeout; /// minimum time to wait for a result, in milliseconds
+ sal_uInt32 nMaxTimeout; /// maximum time to wait for a result, in milliseconds, until eTimeout is returned
+ Link<void*,void> aFinishHandler; /// the handler to be called when the action is finished. Called in every case, no matter of the result
+
+ FileViewAsyncAction() : nMinTimeout(0), nMaxTimeout (0)
+ {
+ }
+};
+
+class SvtFileView
+{
+private:
+ std::unique_ptr<SvtFileView_Impl> mpImpl;
+ css::uno::Sequence<OUString> maDenyList;
+
+ DECL_LINK(HeaderSelect_Impl, int, void);
+
+public:
+ SvtFileView(weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ bool bOnlyFolder, bool bMultiSelection, bool bShowType = true);
+ ~SvtFileView();
+
+ void SetViewMode( FileViewMode eMode );
+
+ const OUString& GetViewURL() const;
+ OUString GetURL(const weld::TreeIter& rEntry) const;
+ OUString GetCurrentURL() const;
+
+ bool GetParentURL( OUString& _rParentURL ) const;
+ void CreatedFolder( const OUString& rUrl, const OUString& rNewFolder );
+
+ void set_help_id(const OUString& rHelpId);
+ OUString get_help_id() const;
+
+ void grab_focus();
+ bool has_focus() const;
+
+ OUString get_selected_text() const;
+
+ weld::Widget* identifier() const; // just to uniquely identify this widget
+
+ /** initialize the view with the content of a folder given by URL, and apply an immediate filter
+
+ @param rFolderURL
+ the URL of the folder whose content is to be read
+ @param rFilter
+ the initial filter to be applied
+ @param pAsyncDescriptor
+ If not <NULL/>, this struct describes the parameters for doing the
+ action asynchronously.
+ */
+ FileViewResult Initialize(
+ const OUString& rFolderURL,
+ const OUString& rFilter,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList
+ );
+
+ /** reads the current content of the current folder again, and applies the given filter to it
+
+ Note 1: The folder is really read a second time. This implies that any new elements (which were
+ not present when you called Initialize the last time) are now displayed.
+
+ Note 2: This method must not be called when you previously initialized the view from a sequence
+ of strings, or a UNO content object.
+
+ @param rFilter
+ the filter to be applied
+ @param pAsyncDescriptor
+ If not <NULL/>, this struct describes the parameters for doing the
+ action asynchronously.
+ */
+ FileViewResult ExecuteFilter(
+ const OUString& rFilter,
+ const FileViewAsyncAction* pAsyncDescriptor
+ );
+
+ /** cancels a running async action (if any)
+
+ @seealso Initialize
+ @seealso ExecuteFilter
+ @seealso FileViewAsyncAction
+ */
+ void CancelRunningAsyncAction();
+
+ /** initializes the view with the parent folder of the current folder
+
+ @param rNewURL
+ the URL of the folder which we just navigated to
+ @param pAsyncDescriptor
+ If not <NULL/>, this struct describes the parameters for doing the
+ action asynchronously.
+ */
+ FileViewResult PreviousLevel(
+ const FileViewAsyncAction* pAsyncDescriptor
+ );
+
+ void SetNoSelection();
+
+ void SetSelectHdl( const Link<SvtFileView*,void>& rHdl );
+ void SetDoubleClickHdl( const Link<SvtFileView*,bool>& rHdl );
+ void SetOpenDoneHdl( const Link<SvtFileView*,void>& rHdl );
+
+ sal_uInt32 GetSelectionCount() const;
+ SvtContentEntry* FirstSelected() const;
+
+ void selected_foreach(const std::function<bool(weld::TreeIter&)>& func);
+
+ void EnableDelete( bool bEnable );
+
+ // save and load column size and sort order
+ OUString GetConfigString() const;
+ void SetConfigString( std::u16string_view rCfgStr );
+
+ void EndInplaceEditing();
+
+ ::std::vector< SvtContentEntry > GetContent();
+};
+
+// struct SvtContentEntry ------------------------------------------------
+
+struct SvtContentEntry
+{
+ bool mbIsFolder;
+ OUString maURL;
+
+ SvtContentEntry( OUString aURL, bool bIsFolder ) :
+ mbIsFolder( bIsFolder ), maURL(std::move( aURL )) {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/foldertree.cxx b/fpicker/source/office/foldertree.cxx
new file mode 100644
index 0000000000..9b704d6c41
--- /dev/null
+++ b/fpicker/source/office/foldertree.cxx
@@ -0,0 +1,178 @@
+/* -*- 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 <comphelper/processfactory.hxx>
+#include <tools/urlobj.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include "contentenumeration.hxx"
+#include "foldertree.hxx"
+#include <bitmaps.hlst>
+
+using namespace ::com::sun::star::task;
+
+using namespace ::svt;
+
+FolderTree::FolderTree(std::unique_ptr<weld::TreeView> xTreeView, weld::Window* pTopLevel)
+ : m_xTreeView(std::move(xTreeView))
+ , m_xScratchIter(m_xTreeView->make_iterator())
+ , m_pTopLevel(pTopLevel)
+{
+ m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 24,
+ m_xTreeView->get_height_rows(7));
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xInteractionHandler(
+ InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
+ m_xEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
+
+ m_xTreeView->connect_expanding(LINK(this, FolderTree, RequestingChildrenHdl));
+}
+
+IMPL_LINK(FolderTree, RequestingChildrenHdl, const weld::TreeIter&, rEntry, bool)
+{
+ weld::WaitObject aWait(m_pTopLevel);
+
+ FillTreeEntry(rEntry);
+
+ return true;
+}
+
+void FolderTree::InsertRootEntry(const OUString& rId, const OUString& rRootLabel)
+{
+ m_xTreeView->insert(nullptr, -1, &rRootLabel, &rId, nullptr, nullptr,
+ true, m_xScratchIter.get());
+ m_xTreeView->set_image(*m_xScratchIter, RID_BMP_FOLDER);
+ m_xTreeView->set_cursor(*m_xScratchIter);
+}
+
+void FolderTree::FillTreeEntry(const weld::TreeIter& rEntry)
+{
+ OUString sURL = m_xTreeView->get_id(rEntry);
+ OUString sFolderImage(RID_BMP_FOLDER);
+
+ if (m_sLastUpdatedDir != sURL)
+ {
+ while (m_xTreeView->iter_has_child(rEntry))
+ {
+ std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rEntry));
+ (void)m_xTreeView->iter_children(*xChild);
+ m_xTreeView->remove(*xChild);
+ }
+
+ ::std::vector< std::unique_ptr<SortingData_Impl> > aContent;
+
+ ::rtl::Reference< ::svt::FileViewContentEnumerator >
+ xContentEnumerator(new FileViewContentEnumerator(
+ m_xEnv, aContent, m_aMutex));
+
+ FolderDescriptor aFolder(sURL);
+
+ EnumerationResult eResult =
+ xContentEnumerator->enumerateFolderContentSync( aFolder, m_aDenyList );
+
+ if (EnumerationResult::SUCCESS == eResult)
+ {
+ for(const auto & i : aContent)
+ {
+ if (!i->mbIsFolder)
+ continue;
+ m_xTreeView->insert(&rEntry, -1, &i->GetTitle(), &i->maTargetURL,
+ nullptr, nullptr, true, m_xScratchIter.get());
+ m_xTreeView->set_image(*m_xScratchIter, sFolderImage);
+ }
+ }
+ }
+ else
+ {
+ // this dir was updated recently
+ // next time read this remote folder
+ m_sLastUpdatedDir.clear();
+ }
+}
+
+void FolderTree::FillTreeEntry( const OUString & rUrl, const ::std::vector< std::pair< OUString, OUString > >& rFolders )
+{
+ SetTreePath(rUrl);
+
+ std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator());
+ bool bParent = m_xTreeView->get_cursor(xParent.get());
+
+ if (!bParent || m_xTreeView->get_row_expanded(*xParent))
+ return;
+
+ OUString sFolderImage(RID_BMP_FOLDER);
+ while (m_xTreeView->iter_has_child(*xParent))
+ {
+ std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xParent.get()));
+ (void)m_xTreeView->iter_children(*xChild);
+ m_xTreeView->remove(*xChild);
+ }
+
+ for (auto const& folder : rFolders)
+ {
+ m_xTreeView->insert(xParent.get(), -1, &folder.first, &folder.second,
+ nullptr, nullptr, true, m_xScratchIter.get());
+ m_xTreeView->set_image(*m_xScratchIter, sFolderImage);
+ }
+
+ m_sLastUpdatedDir = rUrl;
+ m_xTreeView->expand_row(*xParent);
+}
+
+void FolderTree::SetTreePath( std::u16string_view sUrl )
+{
+ INetURLObject aUrl( sUrl );
+ aUrl.setFinalSlash();
+
+ OUString sPath = aUrl.GetURLPath( INetURLObject::DecodeMechanism::WithCharset );
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
+ bool bEntry = m_xTreeView->get_iter_first(*xEntry);
+ bool bEnd = false;
+
+ while (bEntry && !bEnd)
+ {
+ if (!m_xTreeView->get_id(*xEntry).isEmpty())
+ {
+ OUString sNodeUrl = m_xTreeView->get_id(*xEntry);
+
+ INetURLObject aUrlObj( sNodeUrl );
+ aUrlObj.setFinalSlash();
+
+ sNodeUrl = aUrlObj.GetURLPath( INetURLObject::DecodeMechanism::WithCharset );
+
+ if( sPath == sNodeUrl )
+ {
+ m_xTreeView->select(*xEntry);
+ bEnd = true;
+ }
+ else if( sPath.startsWith( sNodeUrl ) )
+ {
+ if (!m_xTreeView->get_row_expanded(*xEntry))
+ m_xTreeView->expand_row(*xEntry);
+
+ bEntry = m_xTreeView->iter_children(*xEntry);
+ }
+ else
+ {
+ bEntry = m_xTreeView->iter_next_sibling(*xEntry);
+ }
+ }
+ else
+ break;
+ }
+}
+
+void FolderTree::SetDenyList( const css::uno::Sequence< OUString >& rDenyList )
+{
+ m_aDenyList = rDenyList;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/foldertree.hxx b/fpicker/source/office/foldertree.hxx
new file mode 100644
index 0000000000..e1ba25699c
--- /dev/null
+++ b/fpicker/source/office/foldertree.hxx
@@ -0,0 +1,48 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <vcl/weld.hxx>
+
+namespace com :: sun :: star :: ucb { class XCommandEnvironment; }
+
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::uno;
+
+class FolderTree
+{
+private:
+ std::unique_ptr<weld::TreeView> m_xTreeView;
+ std::unique_ptr<weld::TreeIter> m_xScratchIter;
+ weld::Window* m_pTopLevel;
+ Reference< XCommandEnvironment > m_xEnv;
+ ::osl::Mutex m_aMutex;
+ Sequence< OUString > m_aDenyList;
+
+ OUString m_sLastUpdatedDir;
+
+ DECL_LINK(RequestingChildrenHdl, const weld::TreeIter&, bool);
+
+public:
+ FolderTree(std::unique_ptr<weld::TreeView> xTreeView, weld::Window* pTopLevel);
+
+ void clear() { m_xTreeView->clear(); }
+
+ void connect_changed(const Link<weld::TreeView&, void>& rLink) { m_xTreeView->connect_changed(rLink); }
+
+ void InsertRootEntry(const OUString& rId, const OUString& rRootLabel);
+ void FillTreeEntry(const weld::TreeIter& rEntry);
+ void FillTreeEntry(const OUString & rUrl, const ::std::vector< std::pair< OUString, OUString > >& rFolders);
+ void SetTreePath(std::u16string_view sUrl);
+ void SetDenyList(const css::uno::Sequence< OUString >& rDenyList);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fpdialogbase.hxx b/fpicker/source/office/fpdialogbase.hxx
new file mode 100644
index 0000000000..60205695b7
--- /dev/null
+++ b/fpicker/source/office/fpdialogbase.hxx
@@ -0,0 +1,112 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include "pickercallbacks.hxx"
+
+class SvTabListBox;
+class SvtFileView;
+class SvtFileDialogFilter_Impl;
+
+
+enum class PickerFlags {
+ NONE = 0x000000,
+ AutoExtension = 0x000001,
+ FilterOptions = 0x000002,
+ ShowVersions = 0x000004,
+ InsertAsLink = 0x000008,
+ ShowPreview = 0x000010,
+ Templates = 0x000020,
+ PlayButton = 0x000040,
+ Selection = 0x000080,
+ ImageTemplate = 0x000100,
+ PathDialog = 0x000200,
+ Open = 0x000400,
+ SaveAs = 0x000800,
+ Password = 0x001000,
+ ReadOnly = 0x002000,
+ MultiSelection = 0x004000,
+ ImageAnchor = 0x008000,
+};
+namespace o3tl {
+ template<> struct typed_flags<PickerFlags> : is_typed_flags<PickerFlags, 0x00ffff> {};
+}
+
+inline constexpr OUString FILEDIALOG_FILTER_ALL = u"*.*"_ustr;
+
+// SvtFileDialog_Base
+
+class SvtFileDialog_Base : public weld::GenericDialogController, public ::svt::IFilePickerController
+{
+public:
+ SvtFileDialog_Base(weld::Window* pParent, const OUString& rUIXMLDescription, const OUString& rID)
+ : weld::GenericDialogController(pParent, rUIXMLDescription, rID)
+ {
+ }
+
+ virtual bool PrepareExecute() { return true ; }
+
+ virtual SvtFileView* GetView() = 0;
+
+ virtual void SetHasFilename( bool bHasFilename ) = 0;
+ virtual void SetDenyList( const css::uno::Sequence< OUString >& rDenyList ) = 0;
+ virtual const css::uno::Sequence< OUString >& GetDenyList() const = 0;
+ virtual void SetStandardDir( const OUString& rStdDir ) = 0;
+ virtual const OUString& GetStandardDir() const = 0;
+ virtual void SetPath( const OUString& rNewURL ) = 0;
+ virtual const OUString& GetPath() = 0;
+ virtual std::vector<OUString> GetPathList() const = 0;
+ virtual bool ContentIsFolder( const OUString& rURL ) = 0;
+
+ virtual OUString getCurrentFileText() const = 0;
+ virtual void setCurrentFileText( const OUString& rText, bool bSelectAll = false ) = 0;
+
+ virtual void AddFilter( const OUString& rFilter, const OUString& rType ) = 0;
+ virtual void AddFilterGroup( const OUString& _rFilter,
+ const css::uno::Sequence< css::beans::StringPair >& rFilters ) = 0;
+ virtual OUString GetCurFilter() const = 0;
+ virtual void SetCurFilter( const OUString& rFilter ) = 0;
+ virtual void FilterSelect() = 0;
+
+ virtual void SetFileCallback( ::svt::IFilePickerListener *pNotifier ) = 0;
+ virtual void onAsyncOperationStarted() = 0;
+ virtual void onAsyncOperationFinished() = 0;
+ virtual void UpdateControls( const OUString& rURL ) = 0;
+
+ virtual void EnableAutocompletion( bool _bEnable = true ) = 0;
+
+ virtual sal_Int32 getAvailableWidth() = 0;
+ virtual sal_Int32 getAvailableHeight() = 0;
+
+ virtual void setImage( const css::uno::Any& rImage ) = 0;
+
+ virtual bool getShowState() = 0;
+};
+
+#define FILE_SELECTION_CHANGED 1
+#define DIRECTORY_CHANGED 2
+#define CTRL_STATE_CHANGED 4
+#define DIALOG_SIZE_CHANGED 5
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fpinteraction.cxx b/fpicker/source/office/fpinteraction.cxx
new file mode 100644
index 0000000000..9d3591d758
--- /dev/null
+++ b/fpicker/source/office/fpinteraction.cxx
@@ -0,0 +1,142 @@
+/* -*- 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 "fpinteraction.hxx"
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/task/XInteractionDisapprove.hpp>
+#include <com/sun/star/task/XInteractionRetry.hpp>
+
+#include <sal/log.hxx>
+#include <utility>
+
+
+namespace svt
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::task;
+ using namespace ::com::sun::star::ucb;
+
+ OFilePickerInteractionHandler::OFilePickerInteractionHandler( css::uno::Reference< css::task::XInteractionHandler > _xMaster )
+ :m_xMaster(std::move( _xMaster ))
+ ,m_bUsed( false )
+ ,m_eInterceptions( OFilePickerInteractionHandler::E_NOINTERCEPTION )
+ {
+ SAL_WARN_IF( !m_xMaster.is(), "fpicker.office", "OFilePickerInteractionHandler::OFilePickerInteractionHandler: invalid master handler!" );
+ }
+
+
+ OFilePickerInteractionHandler::~OFilePickerInteractionHandler( )
+ {
+ }
+
+
+ void SAL_CALL OFilePickerInteractionHandler::handle( const Reference< XInteractionRequest >& _rxRequest )
+ {
+ if (!_rxRequest.is())
+ return;
+
+ m_bUsed = true;
+
+ // extract some generic continuations ... might we need it later
+ // if something goes wrong.
+ Reference< XInteractionAbort > xAbort;
+ Reference< XInteractionApprove > xApprove;
+ Reference< XInteractionDisapprove > xDisapprove;
+ Reference< XInteractionRetry > xRetry;
+
+ const Sequence< Reference< XInteractionContinuation > > lConts = _rxRequest->getContinuations();
+ for (const Reference< XInteractionContinuation >& rCont : lConts)
+ {
+ if (!xAbort.is())
+ xAbort.set(rCont, UNO_QUERY);
+ if (!xApprove.is())
+ xApprove.set(rCont, UNO_QUERY);
+ if (!xDisapprove.is())
+ xDisapprove.set(rCont, UNO_QUERY);
+ if (!xRetry.is())
+ xRetry.set(rCont, UNO_QUERY);
+ }
+
+ // safe the original request for later analyzing!
+ m_aException = _rxRequest->getRequest();
+
+ // intercept some interesting interactions
+
+ // The "does not exist" interaction will be suppressed here completely.
+ if (m_eInterceptions & OFilePickerInteractionHandler::E_DOESNOTEXIST)
+ {
+ InteractiveIOException aIoException;
+ if (
+ (m_aException >>= aIoException ) &&
+ (IOErrorCode_NOT_EXISTING == aIoException.Code)
+ )
+ {
+ if (xAbort.is())
+ xAbort->select();
+ return;
+ }
+ }
+
+ // no master => abort this operation ...
+ if (!m_xMaster.is())
+ {
+ if (xAbort.is())
+ xAbort->select();
+ return;
+ }
+
+ // forward it to our master - so he can handle all
+ // not interesting interactions :-)
+ m_xMaster->handle(_rxRequest);
+ }
+
+
+ void OFilePickerInteractionHandler::enableInterceptions( EInterceptedInteractions eInterceptions )
+ {
+ m_eInterceptions = eInterceptions;
+ }
+
+
+ void OFilePickerInteractionHandler::resetUseState()
+ {
+ m_bUsed = false;
+ }
+
+
+ void OFilePickerInteractionHandler::forgetRequest()
+ {
+ m_aException = Any();
+ }
+
+
+ bool OFilePickerInteractionHandler::wasAccessDenied() const
+ {
+ InteractiveIOException aIoException;
+ return (m_aException >>= aIoException ) &&
+ (IOErrorCode_ACCESS_DENIED == aIoException.Code);
+ }
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fpinteraction.hxx b/fpicker/source/office/fpinteraction.hxx
new file mode 100644
index 0000000000..84f4a43d60
--- /dev/null
+++ b/fpicker/source/office/fpinteraction.hxx
@@ -0,0 +1,80 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+
+
+namespace svt
+{
+
+
+ //= OFilePickerInteractionHandler
+
+ typedef ::cppu::WeakImplHelper < css::task::XInteractionHandler
+ > OFilePickerInteractionHandler_Base;
+
+ /** an InteractionHandler implementation which extends another handler with some customizability
+ */
+ class OFilePickerInteractionHandler final : public OFilePickerInteractionHandler_Base
+ {
+ public:
+ /** flags, which indicates special handled interactions
+ These values will be used combined as flags - so they must
+ in range [2^n]!
+ */
+ enum EInterceptedInteractions
+ {
+ E_NOINTERCEPTION = 0,
+ E_DOESNOTEXIST = 1
+ // next values [2,4,8,16 ...]!
+ };
+
+ private:
+ css::uno::Reference< css::task::XInteractionHandler > m_xMaster; // our master handler
+ css::uno::Any m_aException; // the last handled request
+ bool m_bUsed; // indicates using of this interaction handler instance
+ EInterceptedInteractions m_eInterceptions; // enable/disable interception of some special interactions
+
+ public:
+ explicit OFilePickerInteractionHandler( css::uno::Reference< css::task::XInteractionHandler > _xMaster );
+
+ // some generic functions
+ void enableInterceptions( EInterceptedInteractions eInterceptions );
+ bool wasUsed () const { return m_bUsed; }
+ void resetUseState ();
+ void forgetRequest ();
+
+ // functions to analyze last cached request
+ bool wasAccessDenied() const;
+
+ private:
+ // XInteractionHandler
+ virtual void SAL_CALL handle( const css::uno::Reference< css::task::XInteractionRequest >& _rxRequest ) override;
+
+ virtual ~OFilePickerInteractionHandler() override;
+ };
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fps_office.component b/fpicker/source/office/fps_office.component
new file mode 100644
index 0000000000..cdf5f82479
--- /dev/null
+++ b/fpicker/source/office/fps_office.component
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.svtools.OfficeFilePicker"
+ constructor="fpicker_SvtFilePicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.OfficeFilePicker"/>
+ </implementation>
+ <implementation name="com.sun.star.svtools.RemoteFilePicker"
+ constructor="fpicker_SvtRemoteFilePicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.RemoteFilePicker"/>
+ </implementation>
+ <implementation name="com.sun.star.svtools.OfficeFolderPicker"
+ constructor="fpicker_SvtFolderPicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.OfficeFolderPicker"/>
+ </implementation>
+</component>
diff --git a/fpicker/source/office/fpsmartcontent.cxx b/fpicker/source/office/fpsmartcontent.cxx
new file mode 100644
index 0000000000..4cc504ddaa
--- /dev/null
+++ b/fpicker/source/office/fpsmartcontent.cxx
@@ -0,0 +1,325 @@
+/* -*- 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 "fpsmartcontent.hxx"
+
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/ContentInfo.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+
+namespace svt
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::task;
+ using namespace ::com::sun::star::ucb;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::container;
+
+
+ //= SmartContent
+
+
+ SmartContent::SmartContent()
+ :m_eState( NOT_BOUND )
+ {
+ }
+
+
+ SmartContent::SmartContent( const OUString& _rInitialURL )
+ :m_eState( NOT_BOUND )
+ {
+ bindTo( _rInitialURL );
+ }
+
+
+ SmartContent::~SmartContent()
+ {
+ /* This destructor originally contained the following blurb: "Do
+ not delete the content. Because the content will be used by
+ the cache." This is just plain silly, because it relies on
+ the provider caching created contents (which is done by
+ ucbhelper::ContentProviderImplHelper, but we do not actually
+ expect all providers to use that, right?) Otherwise we are
+ just leaking memory.
+
+ TODO: If there is real need for caching the content, it must
+ be done here.
+ */
+ }
+
+
+ void SmartContent::enableOwnInteractionHandler(::svt::OFilePickerInteractionHandler::EInterceptedInteractions eInterceptions)
+ {
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xGlobalInteractionHandler(
+ InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW );
+
+ m_xOwnInteraction = new ::svt::OFilePickerInteractionHandler(xGlobalInteractionHandler);
+ m_xOwnInteraction->enableInterceptions(eInterceptions);
+
+ m_xCmdEnv = new ::ucbhelper::CommandEnvironment( m_xOwnInteraction, Reference< XProgressHandler >() );
+ }
+
+
+ void SmartContent::enableDefaultInteractionHandler()
+ {
+ m_xOwnInteraction.clear();
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xGlobalInteractionHandler(
+ InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW );
+ m_xCmdEnv = new ucbhelper::CommandEnvironment( xGlobalInteractionHandler, Reference< XProgressHandler >() );
+ }
+
+
+ ::svt::OFilePickerInteractionHandler* SmartContent::getOwnInteractionHandler() const
+ {
+ return m_xOwnInteraction.get();
+ }
+
+
+ SmartContent::InteractionHandlerType SmartContent::queryCurrentInteractionHandler() const
+ {
+ if (m_xOwnInteraction.is())
+ return IHT_OWN;
+
+ if (!m_xCmdEnv.is())
+ return IHT_NONE;
+
+ return IHT_DEFAULT;
+ }
+
+
+ void SmartContent::disableInteractionHandler()
+ {
+ m_xOwnInteraction.clear();
+ m_xCmdEnv.clear();
+ }
+
+
+ void SmartContent::bindTo( const OUString& _rURL )
+ {
+ if ( getURL() == _rURL )
+ // nothing to do, regardless of the state
+ return;
+
+ m_oContent.reset();
+ m_eState = INVALID; // default to INVALID
+ m_sURL = _rURL;
+
+ if ( !m_sURL.isEmpty() )
+ {
+ try
+ {
+ m_oContent.emplace( _rURL, m_xCmdEnv, comphelper::getProcessComponentContext() );
+ m_eState = UNKNOWN;
+ // from now on, the state is unknown -> we cannot know for sure if the content
+ // is really valid (some UCP's only tell this when asking for properties, not upon
+ // creation)
+ }
+ catch( const ContentCreationException& )
+ {
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "fpicker", "SmartContent::bindTo: unexpected exception caught!" );
+ }
+ }
+ else
+ {
+ m_eState = NOT_BOUND;
+ }
+
+
+ // don't forget to reset the may internal used interaction handler ...
+ // But do it only for our own specialized interaction helper!
+ ::svt::OFilePickerInteractionHandler* pHandler = getOwnInteractionHandler();
+ if (pHandler)
+ {
+ pHandler->resetUseState();
+ pHandler->forgetRequest();
+ }
+ }
+
+
+ bool SmartContent::implIs( const OUString& _rURL, Type _eType )
+ {
+ // bind to this content
+ bindTo( _rURL );
+
+ // did we survive this?
+ if ( isInvalid() || !isBound() )
+ return false;
+
+ assert( m_oContent && "SmartContent::implIs: inconsistence!" );
+ // if, after a bindTo, we don't have a content, then we should be INVALID, or at least
+ // NOT_BOUND (the latter happens, for example, if somebody tries to ask for an empty URL)
+
+ bool bIs = false;
+ try
+ {
+ if ( Folder == _eType )
+ bIs = m_oContent->isFolder();
+ else
+ bIs = m_oContent->isDocument();
+
+ // from here on, we definitely know that the content is valid
+ m_eState = VALID;
+ }
+ catch( const Exception& )
+ {
+ // now we're definitely invalid
+ m_eState = INVALID;
+ }
+ return bIs;
+ }
+
+
+ void SmartContent::getTitle( OUString& /* [out] */ _rTitle )
+ {
+ if ( !isBound() || isInvalid() )
+ return;
+
+ try
+ {
+ OUString sTitle;
+ m_oContent->getPropertyValue("Title") >>= sTitle;
+ _rTitle = sTitle;
+
+ // from here on, we definitely know that the content is valid
+ m_eState = VALID;
+ }
+ catch( const css::uno::Exception& )
+ {
+ // now we're definitely invalid
+ m_eState = INVALID;
+ }
+ }
+
+
+ bool SmartContent::hasParentFolder( )
+ {
+ if ( !isBound() || isInvalid() )
+ return false;
+
+ bool bRet = false;
+ try
+ {
+ Reference< XChild > xChild( m_oContent->get(), UNO_QUERY );
+ if ( xChild.is() )
+ {
+ Reference< XContent > xParent( xChild->getParent(), UNO_QUERY );
+ if ( xParent.is() )
+ {
+ const OUString aParentURL( xParent->getIdentifier()->getContentIdentifier() );
+ bRet = ( !aParentURL.isEmpty() && aParentURL != m_oContent->getURL() );
+
+ // now we're definitely valid
+ m_eState = VALID;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ // now we're definitely invalid
+ m_eState = INVALID;
+ }
+ return bRet;
+ }
+
+
+ bool SmartContent::canCreateFolder( )
+ {
+ if ( !isBound() || isInvalid() )
+ return false;
+
+ bool bRet = false;
+ try
+ {
+ const css::uno::Sequence<css::ucb::ContentInfo> aContentsInfo = m_oContent->queryCreatableContentsInfo();
+ for ( auto const& rInfo : aContentsInfo )
+ {
+ // Simply look for the first KIND_FOLDER...
+ if ( rInfo.Attributes & ContentInfoAttribute::KIND_FOLDER )
+ {
+ bRet = true;
+ break;
+ }
+ }
+
+ // now we're definitely valid
+ m_eState = VALID;
+ }
+ catch( const Exception& )
+ {
+ // now we're definitely invalid
+ m_eState = INVALID;
+ }
+ return bRet;
+ }
+
+ OUString SmartContent::createFolder( const OUString& _rTitle )
+ {
+ OUString aCreatedUrl;
+ try
+ {
+ OUString sFolderType;
+
+ const css::uno::Sequence<css::ucb::ContentInfo> aContentsInfo = m_oContent->queryCreatableContentsInfo();
+ for ( auto const& rInfo : aContentsInfo )
+ {
+ // Simply look for the first KIND_FOLDER...
+ if ( rInfo.Attributes & ContentInfoAttribute::KIND_FOLDER )
+ {
+ sFolderType = rInfo.Type;
+ break;
+ }
+ }
+
+ if ( !sFolderType.isEmpty() )
+ {
+ ucbhelper::Content aCreated;
+ Sequence< OUString > aNames { "Title" };
+ Sequence< Any > aValues { Any(_rTitle) };
+ m_oContent->insertNewContent( sFolderType, aNames, aValues, aCreated );
+
+ aCreatedUrl = aCreated.getURL();
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+ return aCreatedUrl;
+ }
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fpsmartcontent.hxx b/fpicker/source/office/fpsmartcontent.hxx
new file mode 100644
index 0000000000..ef3329320b
--- /dev/null
+++ b/fpicker/source/office/fpsmartcontent.hxx
@@ -0,0 +1,196 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include "fpinteraction.hxx"
+
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <ucbhelper/content.hxx>
+#include <rtl/ref.hxx>
+#include <memory>
+#include <optional>
+
+
+namespace svt
+{
+
+
+ //= SmartContent
+
+ /** a "smart content" which basically wraps a UCB content, but caches some information
+ so that repeatedly recreating it may be faster
+ */
+ class SmartContent
+ {
+ public:
+ enum State
+ {
+ NOT_BOUND, // never bound
+ UNKNOWN, // bound, but validity is unknown
+ VALID, // bound to a URL, and valid
+ INVALID // bound to a URL, and invalid
+ };
+
+ private:
+ OUString m_sURL;
+ std::optional<::ucbhelper::Content> m_oContent;
+ State m_eState;
+ css::uno::Reference < css::ucb::XCommandEnvironment > m_xCmdEnv;
+ rtl::Reference<::svt::OFilePickerInteractionHandler> m_xOwnInteraction;
+
+ private:
+ enum Type { Folder, Document };
+ /// checks if the currently bound content is a folder or document
+ bool implIs( const OUString& _rURL, Type _eType );
+
+ SmartContent( const SmartContent& _rSource ) = delete;
+ SmartContent& operator=( const SmartContent& _rSource ) = delete;
+
+ public:
+ SmartContent();
+ explicit SmartContent( const OUString& _rInitialURL );
+ ~SmartContent();
+
+ public:
+
+ /** create and set a specialized interaction handler at the internal used command environment.
+
+ @param eInterceptions
+ will be directly forwarded to OFilePickerInteractionHandler::enableInterceptions()
+ */
+ void enableOwnInteractionHandler(::svt::OFilePickerInteractionHandler::EInterceptedInteractions eInterceptions);
+
+ /** disable the specialized interaction handler and use the global UI interaction handler only.
+ */
+ void enableDefaultInteractionHandler();
+
+ /** return the internal used interaction handler object ...
+ Because this pointer will be valid only, if the uno object is hold
+ alive by its uno reference (and this reference is set on the
+ command environment) we must return NULL, in case this environment does
+ not exist!
+ */
+ ::svt::OFilePickerInteractionHandler* getOwnInteractionHandler() const;
+
+ /** describes different types of interaction handlers
+ */
+ enum InteractionHandlerType
+ {
+ IHT_NONE,
+ IHT_OWN,
+ IHT_DEFAULT
+ };
+
+ /** return the type of the internal used interaction handler object ...
+
+ @seealso InteractionHandlerType
+ */
+ InteractionHandlerType queryCurrentInteractionHandler() const;
+
+ /** disable internal used interaction handler object ...
+ */
+ void disableInteractionHandler();
+
+ /** returns the current state of the content
+
+ @seealso State
+ */
+ State getState( ) const { return m_eState; }
+
+ /** checks if the content is valid
+ <p>Note that "not (is valid)" is not the same as "is invalid"</p>
+ */
+ bool isValid( ) const { return VALID == getState(); }
+
+ /** checks if the content is valid
+ <p>Note that "not (is invalid)" is not the same as "is valid"</p>
+ */
+ bool isInvalid( ) const { return INVALID == getState(); }
+
+ /** checks if the content is bound
+ */
+ bool isBound( ) const { return NOT_BOUND != getState(); }
+
+ /** returns the URL of the content
+ */
+ OUString const & getURL() const { return m_oContent ? m_oContent->getURL() : m_sURL; }
+
+ /** (re)creates the content for the given URL
+
+ <p>Note that getState will return either UNKNOWN or INVALID after the call returns,
+ but never VALID. The reason is that there are content providers which allow to construct
+ content objects, even if the respective contents are not accessible. They tell about this
+ only upon working with the content object (e.g. when asking for the IsFolder).</p>
+
+ @postcond
+ <member>getState</member> does not return NOT_BOUND after the call returns
+ */
+ void bindTo( const OUString& _rURL );
+
+ /** retrieves the title of the content
+ @precond
+ the content is bound and not invalid
+ */
+ void getTitle( OUString& /* [out] */ _rTitle );
+
+ /** checks if the content has a parent folder
+ @precond
+ the content is bound and not invalid
+ */
+ bool hasParentFolder( );
+
+ /** checks if sub folders below the content can be created
+ @precond
+ the content is bound and not invalid
+ */
+ bool canCreateFolder( );
+
+ /** creates a new folder with the given title and return the corresponding URL.
+
+ @return
+ the URL of the created folder or an empty string
+ */
+ OUString createFolder( const OUString& _rTitle );
+
+ /** binds to the given URL, checks whether or not it refers to a folder
+
+ @postcond
+ the content is not in the state UNKNOWN
+ */
+ bool isFolder( const OUString& _rURL )
+ {
+ return implIs( _rURL, Folder );
+ }
+
+ /** checks if the content is existent (it is if and only if it is a document or a folder)
+ */
+ bool is( const OUString& _rURL )
+ {
+ return implIs( _rURL, Folder ) || implIs( _rURL, Document );
+ }
+
+ bool isFolder( ) { return isFolder( getURL() ); }
+ };
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/iodlg.cxx b/fpicker/source/office/iodlg.cxx
new file mode 100644
index 0000000000..b3e279f6b4
--- /dev/null
+++ b/fpicker/source/office/iodlg.cxx
@@ -0,0 +1,2331 @@
+/* -*- 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 <sal/config.h>
+
+#include <sal/log.hxx>
+#include "fileview.hxx"
+#include "iodlg.hxx"
+#include <svtools/PlaceEditDialog.hxx>
+#include "OfficeControlAccess.hxx"
+#include "PlacesListBox.hxx"
+#include <fpicker/fpsofficeResMgr.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/errinf.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/viewoptions.hxx>
+#include <svtools/sfxecode.hxx>
+
+#include <fpicker/strings.hrc>
+#include <svtools/helpids.h>
+#include <strings.hrc>
+#include "asyncfilepicker.hxx"
+#include "iodlgimp.hxx"
+#include <svtools/inettbc.hxx>
+#include "QueryFolderName.hxx"
+#include <rtl/ustring.hxx>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <comphelper/interaction.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+
+#include <osl/file.hxx>
+#include <vcl/dibtools.hxx>
+
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include "fpinteraction.hxx"
+#include <osl/process.h>
+#include <o3tl/string_view.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::sdbc;
+using namespace ::utl;
+using namespace ::svt;
+
+using namespace ExtendedFilePickerElementIds;
+using namespace CommonFilePickerElementIds;
+using namespace InternalFilePickerElementIds;
+
+// functions -------------------------------------------------------------
+
+namespace
+{
+ OUString getMostCurrentFilter( std::unique_ptr<SvtExpFileDlg_Impl> const & pImpl )
+ {
+ assert( pImpl && "invalid impl pointer" );
+ const SvtFileDialogFilter_Impl* pFilter = pImpl->m_xUserFilter.get();
+
+ if ( !pFilter )
+ pFilter = pImpl->GetCurFilter();
+
+ if ( !pFilter )
+ return OUString();
+
+ return pFilter->GetType();
+ }
+
+ void restoreCurrentFilter( std::unique_ptr<SvtExpFileDlg_Impl> const & pImpl )
+ {
+ SAL_WARN_IF( !pImpl->GetCurFilter(), "fpicker.office", "restoreCurrentFilter: no current filter!" );
+ SAL_WARN_IF( pImpl->GetCurFilterDisplayName().isEmpty(), "fpicker.office", "restoreCurrentFilter: no current filter (no display name)!" );
+
+ pImpl->SelectFilterListEntry( pImpl->GetCurFilterDisplayName() );
+
+#ifdef DBG_UTIL
+ OUString sSelectedDisplayName;
+ DBG_ASSERT( ( pImpl->GetSelectedFilterEntry( sSelectedDisplayName ) == pImpl->GetCurFilter() )
+ && ( sSelectedDisplayName == pImpl->GetCurFilterDisplayName() ),
+ "restoreCurrentFilter: inconsistence!" );
+#endif
+ }
+
+
+ OUString GetFsysExtension_Impl( std::u16string_view rFile, const OUString& rLastFilterExt )
+ {
+ size_t nDotPos = rFile.rfind( '.' );
+ if ( nDotPos != std::u16string_view::npos )
+ {
+ if ( !rLastFilterExt.isEmpty() )
+ {
+ if ( o3tl::equalsIgnoreAsciiCase(rFile.substr( nDotPos + 1 ), rLastFilterExt ) )
+ return rLastFilterExt;
+ }
+ else
+ return OUString(rFile.substr( nDotPos ));
+ }
+ return OUString();
+ }
+
+
+ void SetFsysExtension_Impl( OUString& rFile, std::u16string_view rExtension )
+ {
+ const sal_Int32 nDotPos{ rFile.lastIndexOf('.') };
+ if (nDotPos>=0)
+ {
+ if (!rExtension.empty())
+ rFile = OUString::Concat(rFile.subView(0, nDotPos)) + rExtension; // replace old extension with new (not empty) one
+ else if (nDotPos)
+ rFile = rFile.copy(0, nDotPos-1); // truncate extension (new one is empty)
+ else
+ rFile.clear(); // Filename was just an extension
+ }
+ else if (!rExtension.empty())
+ rFile += OUString::Concat(".") + rExtension;
+ // no extension was present, append new one if not empty
+ }
+
+ void lcl_autoUpdateFileExtension( SvtFileDialog* _pDialog, const OUString& _rLastFilterExt )
+ {
+ // if auto extension is enabled...
+ if ( !_pDialog->isAutoExtensionEnabled() )
+ return;
+
+ // automatically switch to the extension of the (maybe just newly selected) extension
+ OUString aNewFile = _pDialog->getCurrentFileText( );
+ OUString aExt = GetFsysExtension_Impl( aNewFile, _rLastFilterExt );
+
+ // but only if there already is an extension
+ if ( aExt.isEmpty() )
+ return;
+
+ // check if it is a real file extension, and not only the "post-dot" part in
+ // a directory name
+ bool bRealExtensions = true;
+ if ( -1 != aExt.indexOf( '/' ) )
+ bRealExtensions = false;
+ else if ( -1 != aExt.indexOf( '\\' ) )
+ bRealExtensions = false;
+ else
+ {
+ // no easy way to tell, because the part containing the dot already is the last
+ // segment of the complete file name
+ // So we have to check if the file name denotes a folder or a file.
+ // For performance reasons, we do this for file urls only
+ INetURLObject aURL( aNewFile );
+ if ( INetProtocol::NotValid == aURL.GetProtocol() )
+ {
+ OUString sURL;
+ if ( osl::FileBase::getFileURLFromSystemPath( aNewFile, sURL )
+ == osl::FileBase::E_None )
+ aURL = INetURLObject( sURL );
+ }
+ if ( INetProtocol::File == aURL.GetProtocol() )
+ {
+ try
+ {
+ bRealExtensions = !_pDialog->ContentIsFolder( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+ catch( const css::uno::Exception& )
+ {
+ SAL_INFO( "fpicker.office", "Exception in lcl_autoUpdateFileExtension" );
+ }
+ }
+ }
+
+ if ( bRealExtensions )
+ {
+ SetFsysExtension_Impl( aNewFile, _pDialog->GetDefaultExt() );
+ _pDialog->setCurrentFileText( aNewFile );
+ }
+ }
+
+#if defined( UNX )
+ bool lcl_getHomeDirectory( const OUString& _rForURL, OUString& /* [out] */ _rHomeDir )
+ {
+ _rHomeDir.clear();
+
+ // now ask the content broker for a provider for this scheme
+
+ try
+ {
+ // get the provider for the current scheme
+ Reference< XContentProvider > xProvider(
+ UniversalContentBroker::create(
+ comphelper::getProcessComponentContext() )->
+ queryContentProvider( _rForURL ) );
+
+ SAL_WARN_IF( !xProvider.is(), "fpicker.office", "lcl_getHomeDirectory: could not find a (valid) content provider for the current URL!" );
+ Reference< XPropertySet > xProviderProps( xProvider, UNO_QUERY );
+ if ( xProviderProps.is() )
+ {
+ Reference< XPropertySetInfo > xPropInfo = xProviderProps->getPropertySetInfo();
+ static constexpr OUString sHomeDirPropertyName( u"HomeDirectory"_ustr );
+ if ( !xPropInfo.is() || xPropInfo->hasPropertyByName( sHomeDirPropertyName ) )
+ {
+ OUString sHomeDirectory;
+ xProviderProps->getPropertyValue( sHomeDirPropertyName ) >>= sHomeDirectory;
+ _rHomeDir = sHomeDirectory;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "fpicker", "lcl_getHomeDirectory" );
+ }
+ return !_rHomeDir.isEmpty();
+ }
+#endif
+
+ OUString lcl_ensureFinalSlash( std::u16string_view _rDir )
+ {
+ INetURLObject aWorkPathObj( _rDir, INetProtocol::File );
+ aWorkPathObj.setFinalSlash();
+ return aWorkPathObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+
+
+ /** retrieves the value of an environment variable
+ @return <TRUE/> if and only if the retrieved string value is not empty
+ */
+ bool getEnvironmentValue( const char* _pAsciiEnvName, OUString& _rValue )
+ {
+ _rValue.clear();
+ OUString sEnvName = OUString::createFromAscii( _pAsciiEnvName );
+ osl_getEnvironment( sEnvName.pData, &_rValue.pData );
+ return !_rValue.isEmpty();
+ }
+}
+
+// SvtFileDialog
+SvtFileDialog::SvtFileDialog(weld::Window* pParent, PickerFlags nStyle)
+ : SvtFileDialog_Base(pParent, "fps/ui/explorerfiledialog.ui", "ExplorerFileDialog")
+ , m_xCbReadOnly(m_xBuilder->weld_check_button("readonly"))
+ , m_xCbLinkBox(m_xBuilder->weld_check_button("link"))
+ , m_xCbPreviewBox(m_xBuilder->weld_check_button("cb_preview"))
+ , m_xCbSelection(m_xBuilder->weld_check_button("selection"))
+ , m_xPbPlay(m_xBuilder->weld_button("play"))
+ , m_xPreviewFrame(m_xBuilder->weld_widget("previewframe"))
+ , m_xPrevBmp(m_xBuilder->weld_image("preview"))
+ , m_pFileNotifier(nullptr)
+ , m_xImpl(new SvtExpFileDlg_Impl)
+ , m_nPickerFlags(nStyle)
+ , m_bIsInExecute(false)
+ , m_bInExecuteAsync(false)
+ , m_bHasFilename(false)
+{
+ m_xImpl->m_xCbOptions = m_xBuilder->weld_check_button("options");
+ m_xImpl->m_xFtFileName = m_xBuilder->weld_label("file_name_label");
+ m_xImpl->m_xEdFileName.reset(new SvtURLBox(m_xBuilder->weld_combo_box("file_name")));
+ m_xImpl->m_xFtFileType = m_xBuilder->weld_label("file_type_label");
+ m_xImpl->m_xLbFilter = m_xBuilder->weld_combo_box("file_type");
+ m_xImpl->m_xEdCurrentPath.reset(new SvtURLBox(m_xBuilder->weld_combo_box("current_path")));
+ m_xImpl->m_xBtnFileOpen = m_xBuilder->weld_button("open");
+ m_xImpl->m_xBtnCancel = m_xBuilder->weld_button("cancel");
+ m_xImpl->m_xBtnHelp = m_xBuilder->weld_button("help");
+ m_xImpl->m_xBtnConnectToServer = m_xBuilder->weld_button("connect_to_server");
+ m_xImpl->m_xBtnNewFolder = m_xBuilder->weld_button("new_folder");
+ m_xImpl->m_xCbPassword = m_xBuilder->weld_check_button("password");
+ m_xImpl->m_xCbGPGEncrypt = m_xBuilder->weld_check_button("gpgencrypt");
+ m_xImpl->m_xCbAutoExtension = m_xBuilder->weld_check_button("extension");
+ m_xImpl->m_xSharedLabel = m_xBuilder->weld_label("shared_label");
+ m_xImpl->m_xSharedListBox = m_xBuilder->weld_combo_box("shared");
+
+ // because the "<All Formats> (*.bmp,*...)" entry is too wide,
+ // we need to disable the auto width feature of the filter box
+ int nWidth = m_xImpl->m_xLbFilter->get_approximate_digit_width() * 60;
+ m_xImpl->m_xSharedListBox->set_size_request(nWidth, -1);
+ m_xImpl->m_xLbFilter->set_size_request(nWidth, -1);
+
+ m_xImpl->m_xBtnUp.reset(new SvtUpButton_Impl(m_xBuilder->weld_toolbar("up_bar"),
+ m_xBuilder->weld_menu("up_menu"),
+ this));
+ m_xImpl->m_xBtnUp->set_help_id(HID_FILEOPEN_LEVELUP);
+ m_xImpl->m_xBtnUp->show();
+
+ m_xImpl->m_nStyle = nStyle;
+ m_xImpl->m_eMode = ( nStyle & PickerFlags::SaveAs ) ? FILEDLG_MODE_SAVE : FILEDLG_MODE_OPEN;
+ m_xImpl->m_eDlgType = FILEDLG_TYPE_FILEDLG;
+
+ if (nStyle & PickerFlags::PathDialog)
+ m_xImpl->m_eDlgType = FILEDLG_TYPE_PATHDLG;
+
+ // Set the directory for the "back to the default dir" button
+ INetURLObject aStdDirObj( SvtPathOptions().GetWorkPath() );
+ SetStandardDir( aStdDirObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ // Create control element, the order defines the tab control.
+ m_xImpl->m_xEdFileName->connect_changed( LINK( this, SvtFileDialog, EntrySelectHdl_Impl ) );
+ m_xImpl->m_xEdFileName->connect_entry_activate( LINK( this, SvtFileDialog, OpenUrlHdl_Impl ) );
+
+ // in folder picker mode, only auto-complete directories (no files)
+ bool bIsFolderPicker = m_xImpl->m_eDlgType == FILEDLG_TYPE_PATHDLG;
+ m_xImpl->m_xEdFileName->SetOnlyDirectories( bIsFolderPicker );
+
+ // in save mode, don't use the autocompletion as selection in the edit part
+ bool bSaveMode = FILEDLG_MODE_SAVE == m_xImpl->m_eMode;
+ m_xImpl->m_xEdFileName->SetNoURLSelection( bSaveMode );
+
+ if (nStyle & PickerFlags::MultiSelection)
+ m_xImpl->m_bMultiSelection = true;
+
+ m_xContainer = m_xBuilder->weld_container("container");
+ m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * 95, -1);
+
+ m_xFileView.reset(new SvtFileView(m_xDialog.get(),
+ m_xBuilder->weld_tree_view("fileview"),
+ m_xBuilder->weld_icon_view("iconview"),
+ FILEDLG_TYPE_PATHDLG == m_xImpl->m_eDlgType,
+ m_xImpl->m_bMultiSelection));
+ m_xFileView->set_help_id( HID_FILEDLG_STANDARD );
+
+ if ( nStyle & PickerFlags::ReadOnly )
+ {
+ m_xCbReadOnly->set_help_id( HID_FILEOPEN_READONLY );
+ m_xCbReadOnly->set_label( FpsResId( STR_SVT_FILEPICKER_READONLY ) );
+ m_xCbReadOnly->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xCbReadOnly->show();
+ }
+
+ if ( nStyle & PickerFlags::Password )
+ {
+ m_xImpl->m_xCbPassword->set_label( FpsResId( STR_SVT_FILEPICKER_PASSWORD ) );
+ m_xImpl->m_xCbPassword->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xImpl->m_xCbPassword->show();
+ m_xImpl->m_xCbGPGEncrypt->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xImpl->m_xCbGPGEncrypt->show();
+ }
+
+ // set the ini file for extracting the size
+ m_xImpl->m_aIniKey = "FileDialog";
+
+ AddControls_Impl( );
+
+ // adjust the labels to the mode
+ TranslateId pResId = STR_EXPLORERFILE_OPEN;
+ TranslateId pButtonResId;
+
+ if ( nStyle & PickerFlags::SaveAs )
+ {
+ pResId = STR_EXPLORERFILE_SAVE;
+ pButtonResId = STR_EXPLORERFILE_BUTTONSAVE;
+ }
+
+ if ( nStyle & PickerFlags::PathDialog )
+ {
+ m_xImpl->m_xFtFileName->set_label( FpsResId( STR_PATHNAME ) );
+ pResId = STR_PATHSELECT;
+ pButtonResId = STR_BUTTONSELECT;
+ }
+
+ m_xDialog->set_title(FpsResId(pResId));
+
+ if ( pButtonResId )
+ m_xImpl->m_xBtnFileOpen->set_label( FpsResId( pButtonResId ) );
+
+ if ( FILEDLG_TYPE_FILEDLG != m_xImpl->m_eDlgType )
+ {
+ m_xImpl->m_xFtFileType->hide();
+ m_xImpl->GetFilterListControl()->hide();
+ }
+
+ // Setting preferences of the control elements.
+ m_xImpl->m_xBtnNewFolder->connect_clicked( LINK( this, SvtFileDialog, NewFolderHdl_Impl ) );
+ m_xImpl->m_xBtnFileOpen->connect_clicked( LINK( this, SvtFileDialog, OpenClickHdl_Impl ) );
+ m_xImpl->m_xBtnCancel->connect_clicked( LINK( this, SvtFileDialog, CancelHdl_Impl ) );
+ m_xImpl->SetFilterListSelectHdl( LINK( this, SvtFileDialog, FilterSelectHdl_Impl ) );
+ m_xImpl->m_xEdFileName->connect_focus_in( LINK( this, SvtFileDialog, FileNameGetFocusHdl_Impl ) );
+ m_xImpl->m_xEdFileName->connect_changed( LINK( this, SvtFileDialog, FileNameModifiedHdl_Impl ) );
+ m_xImpl->m_xEdCurrentPath->connect_entry_activate( LINK( this, SvtFileDialog, URLBoxModifiedHdl_Impl ) );
+ m_xImpl->m_xBtnConnectToServer->connect_clicked( LINK ( this, SvtFileDialog, ConnectToServerPressed_Hdl ) );
+
+ m_xFileView->SetSelectHdl( LINK( this, SvtFileDialog, SelectHdl_Impl ) );
+ m_xFileView->SetDoubleClickHdl( LINK( this, SvtFileDialog, DblClickHdl_Impl ) );
+ m_xFileView->SetOpenDoneHdl( LINK( this, SvtFileDialog, OpenDoneHdl_Impl ) );
+
+ // set timer for the filterbox travel
+ m_xImpl->m_aFilterIdle.SetPriority(TaskPriority::LOWEST);
+ m_xImpl->m_aFilterIdle.SetInvokeHandler( LINK( this, SvtFileDialog, FilterSelectTimerHdl_Impl ) );
+
+ if ( PickerFlags::SaveAs & nStyle )
+ {
+ // different help ids if in save-as mode
+ m_xDialog->set_help_id( HID_FILESAVE_DIALOG );
+
+ m_xImpl->m_xEdFileName->set_help_id( HID_FILESAVE_FILEURL );
+ m_xImpl->m_xBtnFileOpen->set_help_id( HID_FILESAVE_DOSAVE );
+ m_xImpl->m_xBtnNewFolder->set_help_id( HID_FILESAVE_CREATEDIRECTORY );
+ m_xImpl->m_xBtnUp->set_help_id( HID_FILESAVE_LEVELUP );
+ m_xImpl->GetFilterListControl()->set_help_id( HID_FILESAVE_FILETYPE );
+ m_xFileView->set_help_id( HID_FILESAVE_FILEVIEW );
+
+ // formerly, there was only _pLbFileVersion, which was used for 3 different
+ // use cases. For reasons of maintainability, I introduced extra members (_pLbTemplates, _pLbImageTemplates)
+ // for the extra use cases, and separated _pLbFileVersion
+ // I did not find out in which cases the help ID is really needed HID_FILESAVE_TEMPLATE - all
+ // tests I made lead to a dialog where _no_ of the three list boxes was present.
+ if (m_xImpl->m_xSharedListBox)
+ m_xImpl->m_xSharedListBox->set_help_id( HID_FILESAVE_TEMPLATE );
+
+ if ( m_xImpl->m_xCbPassword ) m_xImpl->m_xCbPassword->set_help_id( HID_FILESAVE_SAVEWITHPASSWORD );
+ if ( m_xImpl->m_xCbAutoExtension ) m_xImpl->m_xCbAutoExtension->set_help_id( HID_FILESAVE_AUTOEXTENSION );
+ if ( m_xImpl->m_xCbOptions ) m_xImpl->m_xCbOptions->set_help_id( HID_FILESAVE_CUSTOMIZEFILTER );
+ if ( m_xCbSelection ) m_xCbSelection->set_help_id( HID_FILESAVE_SELECTION );
+ }
+
+ /// read our settings from the configuration
+ m_aConfiguration = OConfigurationTreeRoot::createWithComponentContext(
+ ::comphelper::getProcessComponentContext(),
+ "/org.openoffice.Office.UI/FilePicker"
+ );
+
+ m_xDialog->connect_size_allocate(LINK(this, SvtFileDialog, SizeAllocHdl));
+ SizeAllocHdl(Size());
+
+ m_xImpl->m_xEdFileName->grab_focus();
+}
+
+SvtFileDialog::~SvtFileDialog()
+{
+ if (!m_xImpl->m_aIniKey.isEmpty())
+ {
+ // save window state
+ SvtViewOptions aDlgOpt( EViewType::Dialog, m_xImpl->m_aIniKey );
+ aDlgOpt.SetWindowState(m_xDialog->get_window_state(vcl::WindowDataMask::All));
+ OUString sUserData = m_xFileView->GetConfigString();
+ aDlgOpt.SetUserItem( "UserData",
+ Any( sUserData ) );
+ }
+
+ m_xFileView->SetSelectHdl(Link<SvtFileView*,void>());
+
+ // Save bookmarked places
+ if (!m_xImpl->m_xPlaces->IsUpdated())
+ return;
+
+ const std::vector<PlacePtr> aPlaces = m_xImpl->m_xPlaces->GetPlaces();
+ Sequence< OUString > placesUrlsList(m_xImpl->m_xPlaces->GetNbEditablePlaces());
+ auto placesUrlsListRange = asNonConstRange(placesUrlsList);
+ Sequence< OUString > placesNamesList(m_xImpl->m_xPlaces->GetNbEditablePlaces());
+ auto placesNamesListRange = asNonConstRange(placesNamesList);
+ int i(0);
+ for (auto const& place : aPlaces)
+ {
+ if(place->IsEditable()) {
+ placesUrlsListRange[i] = place->GetUrl();
+ placesNamesListRange[i] = place->GetName();
+ ++i;
+ }
+ }
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Misc::FilePickerPlacesUrls::set(placesUrlsList, batch);
+ officecfg::Office::Common::Misc::FilePickerPlacesNames::set(placesNamesList, batch);
+ batch->commit();
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, NewFolderHdl_Impl, weld::Button&, void)
+{
+ m_xFileView->EndInplaceEditing();
+
+ SmartContent aContent( m_xFileView->GetViewURL( ) );
+ 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;
+ }
+}
+
+void SvtFileDialog::createNewUserFilter( const OUString& _rNewFilter )
+{
+ // delete the old user filter and create a new one
+ m_xImpl->m_xUserFilter.reset( new SvtFileDialogFilter_Impl( _rNewFilter, _rNewFilter ) );
+
+ // remember the extension
+ bool bIsAllFiles = _rNewFilter == FILEDIALOG_FILTER_ALL;
+ if ( bIsAllFiles )
+ EraseDefaultExt();
+ else
+ SetDefaultExt( _rNewFilter.copy( 2 ) );
+ // TODO: this is nonsense. In the whole file there are a lot of places where we assume that a user filter
+ // is always "*.<something>". But changing this would take some more time than I have now...
+
+ // now, the default extension is set to the one of the user filter (or empty)
+ if ( m_xImpl->GetCurFilter( ) )
+ SetDefaultExt( m_xImpl->GetCurFilter( )->GetExtension() );
+ else
+ EraseDefaultExt();
+}
+
+
+AdjustFilterFlags SvtFileDialog::adjustFilter( const OUString& rFilter )
+{
+ AdjustFilterFlags nReturn = AdjustFilterFlags::NONE;
+
+ const bool bNonEmpty = !rFilter.isEmpty();
+ if ( bNonEmpty )
+ {
+ nReturn |= AdjustFilterFlags::NonEmpty;
+
+ bool bFilterChanged = true;
+
+ // search for a corresponding filter
+ SvtFileDialogFilter_Impl* pFilter = FindFilter_Impl( rFilter, false, bFilterChanged );
+
+ // look for multi-ext filters if necessary
+ if ( !pFilter )
+ pFilter = FindFilter_Impl( rFilter, true, bFilterChanged );
+
+ if ( bFilterChanged )
+ nReturn |= AdjustFilterFlags::Changed;
+
+ if ( !pFilter )
+ {
+ nReturn |= AdjustFilterFlags::UserFilter;
+ // no filter found : use it as user defined filter
+ createNewUserFilter( rFilter );
+ }
+ }
+
+ return nReturn;
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, CancelHdl_Impl, weld::Button&, void)
+{
+ if ( m_pCurrentAsyncAction.is() )
+ {
+ m_pCurrentAsyncAction->cancel();
+ onAsyncOperationFinished();
+ }
+ else
+ {
+ m_xDialog->response(RET_CANCEL);
+ }
+}
+
+IMPL_LINK( SvtFileDialog, OpenClickHdl_Impl, weld::Button&, rVoid, void )
+{
+ OpenHdl_Impl(&rVoid);
+}
+
+IMPL_LINK( SvtFileDialog, OpenUrlHdl_Impl, weld::ComboBox&, rVoid, bool )
+{
+ OpenHdl_Impl(&rVoid);
+ return true;
+}
+
+void SvtFileDialog::OpenHdl_Impl(void const * pVoid)
+{
+ if ( m_xImpl->m_bMultiSelection && m_xFileView->GetSelectionCount() > 1 )
+ {
+ // special open in case of multiselection
+ OpenMultiSelection_Impl();
+ return;
+ }
+
+ OUString aFileName;
+ OUString aOldPath(m_xFileView->GetViewURL());
+ if ( m_xImpl->m_bDoubleClick || m_xFileView->has_focus() )
+ {
+ // Selection done by doubleclicking in the view, get filename from the view
+ aFileName = m_xFileView->GetCurrentURL();
+ }
+
+ if ( aFileName.isEmpty() )
+ {
+ // if an entry is selected in the view...
+ if ( m_xFileView->GetSelectionCount() )
+ { // -> use this one. This will allow us to step down this folder
+ aFileName = m_xFileView->GetCurrentURL();
+ }
+ }
+
+ if ( aFileName.isEmpty() )
+ {
+ // get the URL from the edit field ( if not empty )
+ if ( !m_xImpl->m_xEdFileName->get_active_text().isEmpty() )
+ {
+ OUString aText = m_xImpl->m_xEdFileName->get_active_text();
+
+ // did we reach the root?
+ if ( !INetURLObject( aOldPath ).getSegmentCount() )
+ {
+ if ( ( aText.getLength() == 2 && aText == ".." ) ||
+ ( aText.getLength() == 3 && ( aText == "..\\" || aText == "../" ) ) )
+ // don't go higher than the root
+ return;
+ }
+
+#if defined( UNX )
+ if ( ( 1 == aText.getLength() ) && ( '~' == aText[0] ) )
+ {
+ // go to the home directory
+ if ( lcl_getHomeDirectory( m_xFileView->GetViewURL(), aFileName ) )
+ // in case we got a home dir, reset the text of the edit
+ m_xImpl->m_xEdFileName->set_entry_text( OUString() );
+ }
+ if ( aFileName.isEmpty() )
+#endif
+ {
+ // get url from autocomplete edit
+ aFileName = m_xImpl->m_xEdFileName->GetURL();
+ }
+ }
+ else if ( pVoid == m_xImpl->m_xBtnFileOpen.get() )
+ // OpenHdl was called for the "Open" Button; if edit field is empty, use selected element in the view
+ aFileName = m_xFileView->GetCurrentURL();
+ }
+
+ // MBA->PB: ?!
+ if ( aFileName.isEmpty() && pVoid == m_xImpl->m_xEdFileName.get() && m_xImpl->m_xUserFilter )
+ {
+ m_xImpl->m_xUserFilter.reset();
+ return;
+ }
+
+ sal_Int32 nLen = aFileName.getLength();
+ if ( !nLen )
+ {
+ // if the dialog was opened to select a folder, the last selected folder should be selected
+ if( m_xImpl->m_eDlgType == FILEDLG_TYPE_PATHDLG )
+ {
+ aFileName = m_xImpl->m_xEdCurrentPath->get_active_text();
+ nLen = aFileName.getLength();
+ }
+ else
+ // no file selected !
+ return;
+ }
+
+ // mark input as selected
+ m_xImpl->m_xEdFileName->select_entry_region(0, nLen);
+
+ // if a path with wildcards is given, divide the string into path and wildcards
+ OUString aFilter;
+ if ( !SvtFileDialog::IsolateFilterFromPath_Impl( aFileName, aFilter ) )
+ return;
+
+ // if a filter was retrieved, there were wildcards !
+ AdjustFilterFlags nNewFilterFlags = adjustFilter( aFilter );
+ if ( nNewFilterFlags & AdjustFilterFlags::Changed )
+ {
+ // cut off all text before wildcard in edit and select wildcard
+ m_xImpl->m_xEdFileName->set_entry_text( aFilter );
+ m_xImpl->m_xEdFileName->select_entry_region(0, -1);
+ }
+
+ {
+ INetURLObject aFileObject( aFileName );
+ if ( ( aFileObject.GetProtocol() == INetProtocol::NotValid ) && !aFileName.isEmpty() )
+ {
+ OUString sCompleted = SvtURLBox::ParseSmart( aFileName, m_xFileView->GetViewURL() );
+ if ( !sCompleted.isEmpty() )
+ aFileName = sCompleted;
+ }
+ }
+
+ // check if it is a folder
+ bool bIsFolder = false;
+
+ // first thing before doing anything with the content: Reset it. When the user presses "open" (or "save" or "export",
+ // for that matter), s/he wants the complete handling, including all possible error messages, even if s/he
+ // does the same thing for the same content twice, s/he wants both fails to be displayed.
+ // Without the reset, it could be that the content cached all relevant information, and will not display any
+ // error messages for the same content a second time...
+ m_aContent.bindTo( OUString( ) );
+
+ if ( !aFileName.isEmpty() )
+ {
+ // Make sure we have own Interaction Handler in place. We do not need
+ // to intercept interactions here, but to record the fact that there
+ // was an interaction.
+ SmartContent::InteractionHandlerType eInterActionHandlerType
+ = m_aContent.queryCurrentInteractionHandler();
+ if ( ( eInterActionHandlerType == SmartContent::IHT_NONE ) ||
+ ( eInterActionHandlerType == SmartContent::IHT_DEFAULT ) )
+ m_aContent.enableOwnInteractionHandler(
+ OFilePickerInteractionHandler::E_NOINTERCEPTION );
+
+ bIsFolder = m_aContent.isFolder( aFileName );
+
+ // access denied to the given resource - and interaction was already
+ // used => break following operations
+ OFilePickerInteractionHandler* pHandler
+ = m_aContent.getOwnInteractionHandler();
+
+ OSL_ENSURE( pHandler, "Got no Interaction Handler!!!" );
+
+ if ( pHandler->wasAccessDenied() )
+ return;
+
+ if ( m_aContent.isInvalid() &&
+ ( m_xImpl->m_eMode == FILEDLG_MODE_OPEN ) )
+ {
+ if ( !pHandler->wasUsed() )
+ ErrorHandler::HandleError( ERRCODE_IO_NOTEXISTS );
+
+ return;
+ }
+
+ // restore previous Interaction Handler
+ if ( eInterActionHandlerType == SmartContent::IHT_NONE )
+ m_aContent.disableInteractionHandler();
+ else if ( eInterActionHandlerType == SmartContent::IHT_DEFAULT )
+ m_aContent.enableDefaultInteractionHandler();
+ }
+
+ if ( !bIsFolder // no existent folder
+ && m_xImpl->m_xCbAutoExtension // auto extension is enabled in general
+ && m_xImpl->m_xCbAutoExtension->get_active()// auto extension is really to be used
+ && !GetDefaultExt().isEmpty() // there is a default extension
+ && GetDefaultExt() != "*" // the default extension is not "all"
+ && !( FILEDLG_MODE_SAVE == m_xImpl->m_eMode // we're saving a file
+ && m_xFileView->GetSelectionCount() // there is a selected file in the file view -> it will later on
+ ) // (in SvtFileDialog::GetPathList) be taken as file to save to
+
+ && FILEDLG_MODE_OPEN != m_xImpl->m_eMode // #i83408# don't append extension on open
+ )
+ {
+ // check extension and append the default extension if necessary
+ appendDefaultExtension(aFileName,
+ GetDefaultExt(),
+ m_xImpl->GetCurFilter()->GetType());
+ }
+
+ bool bOpenFolder = ( FILEDLG_TYPE_PATHDLG == m_xImpl->m_eDlgType ) &&
+ !m_xImpl->m_bDoubleClick && pVoid != m_xImpl->m_xEdFileName.get();
+ if ( bIsFolder )
+ {
+ if ( bOpenFolder )
+ {
+ m_aPath = aFileName;
+ }
+ else
+ {
+ if ( aFileName != m_xFileView->GetViewURL() )
+ {
+ OpenURL_Impl( aFileName );
+ }
+ else
+ {
+ if ( nNewFilterFlags & AdjustFilterFlags::Changed )
+ ExecuteFilter();
+ }
+
+ return;
+ }
+ }
+ else if ( !( nNewFilterFlags & AdjustFilterFlags::NonEmpty ) )
+ {
+ // if applicable save URL
+ m_aPath = aFileName;
+ }
+ else
+ {
+ // if applicable filter again
+ if ( nNewFilterFlags & AdjustFilterFlags::Changed )
+ ExecuteFilter();
+ return;
+ }
+
+ INetURLObject aFileObj( aFileName );
+ if ( aFileObj.HasError() )
+ {
+ ErrorHandler::HandleError( ERRCODE_IO_GENERAL );
+ return;
+ }
+
+ switch (m_xImpl->m_eMode)
+ {
+ case FILEDLG_MODE_SAVE:
+ {
+ if ( ::utl::UCBContentHelper::Exists( aFileObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
+ {
+ OUString aMsg = FpsResId(STR_SVT_ALREADYEXISTOVERWRITE);
+ aMsg = aMsg.replaceFirst(
+ "$filename$",
+ aFileObj.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset)
+ );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo, aMsg));
+ if (xBox->run() != RET_YES)
+ return;
+ }
+ else
+ {
+ OUString aCurPath;
+ if (osl::FileBase::getSystemPathFromFileURL(aFileName, aCurPath) == osl::FileBase::E_None)
+ {
+ // if content does not exist: at least its path must exist
+ INetURLObject aPathObj = aFileObj;
+ aPathObj.removeSegment();
+ bool bFolder = m_aContent.isFolder( aPathObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ if ( !bFolder )
+ {
+ ErrorHandler::HandleError( ERRCODE_IO_NOTEXISTSPATH );
+ return;
+ }
+ }
+ }
+ }
+ break;
+
+ case FILEDLG_MODE_OPEN:
+ {
+ // do an existence check herein, again
+
+ if ( INetProtocol::File == aFileObj.GetProtocol( ) )
+ {
+ bool bExists = m_aContent.is( aFileObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ if ( !bExists )
+ {
+ OUString sError(FpsResId(RID_FILEOPEN_NOTEXISTENTFILE));
+
+ OUString sInvalidFile( aFileObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) );
+ if ( INetProtocol::File == aFileObj.GetProtocol() )
+ { // if it's a file URL, transform the URL into system notation
+ OUString sURL( sInvalidFile );
+ OUString sSystem;
+ osl_getSystemPathFromFileURL( sURL.pData, &sSystem.pData );
+ sInvalidFile = sSystem;
+ }
+ sError = sError.replaceFirst( "$name$", sInvalidFile );
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok, sError));
+ xBox->run();
+ return;
+ }
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL("SvtFileDialog, OpenHdl_Impl: invalid mode!");
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+void SvtFileDialog::EnableAutocompletion(bool bEnable)
+{
+ m_xImpl->m_xEdFileName->EnableAutocomplete(bEnable);
+}
+
+IMPL_LINK_NOARG( SvtFileDialog, FilterSelectHdl_Impl, weld::ComboBox&, void )
+{
+ OUString sSelectedFilterDisplayName;
+ SvtFileDialogFilter_Impl* pSelectedFilter = m_xImpl->GetSelectedFilterEntry( sSelectedFilterDisplayName );
+ if ( !pSelectedFilter )
+ { // there is no current selection. This happens if for instance the user selects a group separator using
+ // the keyboard, and then presses enter: When the selection happens, we immediately deselect the entry,
+ // so in this situation there is no current selection.
+ restoreCurrentFilter( m_xImpl );
+ }
+ else
+ {
+ if ( ( pSelectedFilter != m_xImpl->GetCurFilter() )
+ || m_xImpl->m_xUserFilter
+ )
+ {
+ // Store the old filter for the auto extension handling
+ OUString sLastFilterExt = m_xImpl->GetCurFilter()->GetExtension();
+ m_xImpl->m_xUserFilter.reset();
+
+ // if applicable remove filter of the user
+ m_xImpl->SetCurFilter( pSelectedFilter, sSelectedFilterDisplayName );
+
+ // if applicable show extension
+ SetDefaultExt( pSelectedFilter->GetExtension() );
+ sal_Int32 nSepPos = GetDefaultExt().indexOf( FILEDIALOG_DEF_EXTSEP );
+
+ if ( nSepPos != -1 )
+ EraseDefaultExt( nSepPos );
+
+ // update the extension of the current file if necessary
+ lcl_autoUpdateFileExtension( this, sLastFilterExt );
+
+ // if the user is traveling fast through the filterbox
+ // do not filter instantly
+ // FilterSelectHdl_Impl should be started again at idle
+ m_xImpl->m_aFilterIdle.Start();
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, FilterSelectTimerHdl_Impl, Timer*, void)
+{
+ // filter the view again
+ ExecuteFilter();
+}
+
+IMPL_LINK_NOARG( SvtFileDialog, FileNameGetFocusHdl_Impl, weld::Widget&, void )
+{
+ m_xFileView->SetNoSelection();
+}
+
+IMPL_LINK( SvtFileDialog, FileNameModifiedHdl_Impl, weld::ComboBox&, rComboBox, void )
+{
+ FileNameGetFocusHdl_Impl(rComboBox);
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, URLBoxModifiedHdl_Impl, weld::ComboBox&, bool)
+{
+ OUString aPath = m_xImpl->m_xEdCurrentPath->GetURL();
+ OpenURL_Impl(aPath);
+ return true;
+}
+
+IMPL_LINK_NOARG( SvtFileDialog, ConnectToServerPressed_Hdl, weld::Button&, void )
+{
+ m_xFileView->EndInplaceEditing();
+
+ PlaceEditDialog aDlg(m_xDialog.get());
+ short aRetCode = aDlg.run();
+
+ switch (aRetCode) {
+ case RET_OK :
+ {
+ PlacePtr newPlace = aDlg.GetPlace();
+ m_xImpl->m_xPlaces->AppendPlace(newPlace);
+
+ break;
+ }
+ case RET_CANCEL :
+ default :
+ // Do Nothing
+ break;
+ }
+}
+
+IMPL_LINK_NOARG ( SvtFileDialog, AddPlacePressed_Hdl, weld::Button&, void )
+{
+ // Maybe open the PlacesDialog would have been a better idea
+ // there is an ux choice to make we did not make...
+ INetURLObject aURLObj( m_xFileView->GetViewURL() );
+ PlacePtr newPlace =
+ std::make_shared<Place>( aURLObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset),
+ m_xFileView->GetViewURL(), true);
+ m_xImpl->m_xPlaces->AppendPlace(newPlace);
+}
+
+IMPL_LINK_NOARG ( SvtFileDialog, RemovePlacePressed_Hdl, weld::Button&, void )
+{
+ m_xImpl->m_xPlaces->RemoveSelectedPlace();
+}
+
+SvtFileDialogFilter_Impl* SvtFileDialog::FindFilter_Impl
+(
+ const OUString& rFilter,
+ bool bMultiExt,/* TRUE - regard filter with several extensions
+ FALSE - do not ...
+ */
+ bool& rFilterChanged
+)
+
+/* [Description]
+
+ This method looks for the specified extension in the included filters.
+*/
+
+{
+ SvtFileDialogFilter_Impl* pFoundFilter = nullptr;
+ SvtFileDialogFilterList_Impl& rList = m_xImpl->m_aFilter;
+ sal_uInt16 nFilter = rList.size();
+
+ while ( nFilter-- )
+ {
+ SvtFileDialogFilter_Impl* pFilter = rList[ nFilter ].get();
+ const OUString& rType = pFilter->GetType();
+
+ if ( bMultiExt )
+ {
+ sal_Int32 nIdx = 0;
+ while ( !pFoundFilter && nIdx != -1 )
+ {
+ const OUString aSingleType = rType.getToken( 0, FILEDIALOG_DEF_EXTSEP, nIdx );
+#ifdef UNX
+ if ( aSingleType == rFilter )
+#else
+ if ( aSingleType.equalsIgnoreAsciiCase( rFilter ) )
+#endif
+ pFoundFilter = pFilter;
+ }
+ }
+#ifdef UNX
+ else if ( rType == rFilter )
+#else
+ else if ( rType.equalsIgnoreAsciiCase( rFilter ) )
+#endif
+ pFoundFilter = pFilter;
+
+ if ( pFoundFilter )
+ {
+ // activate filter
+ rFilterChanged = m_xImpl->m_xUserFilter || ( m_xImpl->GetCurFilter() != pFilter );
+
+ createNewUserFilter( rFilter );
+
+ break;
+ }
+ }
+ return pFoundFilter;
+}
+
+
+void SvtFileDialog::ExecuteFilter()
+{
+ executeAsync( AsyncPickerAction::eExecuteFilter, OUString(), getMostCurrentFilter(m_xImpl) );
+}
+
+/* [Description]
+
+ OpenHandler for MultiSelection
+*/
+void SvtFileDialog::OpenMultiSelection_Impl()
+{
+ SvtContentEntry* pEntry = m_xFileView->FirstSelected();
+
+ if (pEntry)
+ m_aPath = pEntry->maURL;
+
+ m_xDialog->response(RET_OK);
+}
+
+void SvtFileDialog::UpdateControls( const OUString& rURL )
+{
+ m_xImpl->m_xEdFileName->SetBaseURL( rURL );
+
+ INetURLObject aObj( rURL );
+
+ {
+ OUString sText;
+ SAL_WARN_IF( INetProtocol::NotValid == aObj.GetProtocol(), "fpicker.office", "SvtFileDialog::UpdateControls: Invalid URL!" );
+
+ if ( aObj.getSegmentCount() )
+ {
+ osl::FileBase::getSystemPathFromFileURL(rURL, sText);
+ if ( !sText.isEmpty() )
+ {
+ // no Fsys path for server file system ( only UCB has mountpoints! )
+ if ( INetProtocol::File != aObj.GetProtocol() )
+ sText = rURL.copy( INetURLObject::GetScheme( aObj.GetProtocol() ).getLength() );
+ }
+
+ if ( sText.isEmpty() && aObj.getSegmentCount() )
+ sText = rURL;
+ }
+
+ // path mode ?
+ if ( FILEDLG_TYPE_PATHDLG == m_xImpl->m_eDlgType )
+ // -> set new path in the edit field
+ m_xImpl->m_xEdFileName->set_entry_text( sText );
+
+ // in the "current path" field, truncate the trailing slash
+ if ( aObj.hasFinalSlash() )
+ {
+ aObj.removeFinalSlash();
+ OUString sURL( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ if (osl::FileBase::getSystemPathFromFileURL(sURL, sText) != osl::FileBase::E_None)
+ sText = sURL;
+ }
+
+ if ( sText.isEmpty() && !rURL.isEmpty() )
+ // happens, for instance, for URLs which the INetURLObject does not know to belong to a hierarchical scheme
+ sText = rURL;
+ m_xImpl->m_xEdCurrentPath->set_entry_text(sText);
+ }
+
+ m_aPath = rURL;
+
+ m_xImpl->m_xBtnUp->FillURLMenu();
+
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify( DIRECTORY_CHANGED, 0 );
+}
+
+IMPL_LINK( SvtFileDialog, SelectHdl_Impl, SvtFileView*, pBox, void )
+{
+ SvtContentEntry* pUserData = pBox->FirstSelected();
+ if (pUserData)
+ {
+ INetURLObject aObj( pUserData->maURL );
+ if ( FILEDLG_TYPE_PATHDLG == m_xImpl->m_eDlgType )
+ {
+ if ( aObj.GetProtocol() == INetProtocol::File )
+ {
+ if ( !pUserData->mbIsFolder )
+ aObj.removeSegment();
+ OUString aName = aObj.getFSysPath( static_cast<FSysStyle>(FSysStyle::Detect & ~FSysStyle::Vos) );
+ m_xImpl->m_xEdFileName->set_entry_text( aName );
+ m_xImpl->m_xEdFileName->select_entry_region(0, -1);
+ m_aPath = pUserData->maURL;
+ }
+ else if ( !pUserData->mbIsFolder )
+ {
+ m_xImpl->m_xEdFileName->set_entry_text( pUserData->maURL );
+ m_xImpl->m_xEdFileName->select_entry_region(0, -1);
+ m_aPath = pUserData->maURL;
+ }
+ else
+ m_xImpl->m_xEdFileName->set_entry_text( OUString() );
+ }
+ else
+ {
+ if ( !pUserData->mbIsFolder )
+ {
+ OUString aName = pBox->get_selected_text();
+ m_xImpl->m_xEdFileName->set_entry_text( aName );
+ m_xImpl->m_xEdFileName->select_entry_region(0, -1);
+ m_aPath = pUserData->maURL;
+ }
+ }
+ }
+
+ if ( m_xImpl->m_bMultiSelection && m_xFileView->GetSelectionCount() > 1 )
+ {
+ // clear the file edit for multiselection
+ m_xImpl->m_xEdFileName->set_entry_text( OUString() );
+ }
+
+ FileSelect();
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, DblClickHdl_Impl, SvtFileView*, bool)
+{
+ m_xImpl->m_bDoubleClick = true;
+ OpenHdl_Impl( nullptr );
+ m_xImpl->m_bDoubleClick = false;
+ return true;
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, EntrySelectHdl_Impl, weld::ComboBox&, void)
+{
+ FileSelect();
+}
+
+IMPL_LINK( SvtFileDialog, OpenDoneHdl_Impl, SvtFileView*, pView, void )
+{
+ const OUString& sCurrentFolder( pView->GetViewURL() );
+ // check if we can create new folders
+ EnableControl( m_xImpl->m_xBtnNewFolder.get(), ContentCanMakeFolder( sCurrentFolder ) );
+
+ // check if we can travel one level up
+ bool bCanTravelUp = ContentHasParentFolder( pView->GetViewURL() );
+ if ( bCanTravelUp )
+ {
+ // additional check: the parent folder should not be prohibited
+ INetURLObject aCurrentFolder( sCurrentFolder );
+ SAL_WARN_IF( INetProtocol::NotValid == aCurrentFolder.GetProtocol(),
+ "fpicker.office", "SvtFileDialog::OpenDoneHdl_Impl: invalid current URL!" );
+
+ aCurrentFolder.removeSegment();
+ }
+ EnableControl( m_xImpl->m_xBtnUp->getWidget(), bCanTravelUp );
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, AutoExtensionHdl_Impl, weld::Toggleable&, void)
+{
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify(CTRL_STATE_CHANGED, CHECKBOX_AUTOEXTENSION);
+
+ // update the extension of the current file if necessary
+ lcl_autoUpdateFileExtension( this, m_xImpl->GetCurFilter()->GetExtension() );
+}
+
+IMPL_LINK( SvtFileDialog, ClickHdl_Impl, weld::Toggleable&, rCheckBox, void )
+{
+ if (!m_pFileNotifier)
+ return;
+
+ sal_Int16 nId = -1;
+
+ if ( &rCheckBox == m_xImpl->m_xCbOptions.get() )
+ nId = CHECKBOX_FILTEROPTIONS;
+ else if ( &rCheckBox == m_xCbSelection.get() )
+ nId = CHECKBOX_SELECTION;
+ else if ( &rCheckBox == m_xCbReadOnly.get() )
+ nId = CHECKBOX_READONLY;
+ else if ( &rCheckBox == m_xImpl->m_xCbPassword.get() )
+ nId = CHECKBOX_PASSWORD;
+ else if ( &rCheckBox == m_xImpl->m_xCbGPGEncrypt.get() )
+ nId = CHECKBOX_GPGENCRYPTION;
+ else if ( &rCheckBox == m_xCbLinkBox.get() )
+ nId = CHECKBOX_LINK;
+ else if ( &rCheckBox == m_xCbPreviewBox.get() )
+ nId = CHECKBOX_PREVIEW;
+
+ if ( nId != -1 )
+ m_pFileNotifier->notify( CTRL_STATE_CHANGED, nId );
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, PlayButtonHdl_Impl, weld::Button&, void)
+{
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify(CTRL_STATE_CHANGED, PUSHBUTTON_PLAY);
+}
+
+namespace
+{
+
+bool implIsInvalid( const OUString & rURL )
+{
+ SmartContent aContent( rURL );
+ aContent.enableOwnInteractionHandler( ::svt::OFilePickerInteractionHandler::E_DOESNOTEXIST );
+ aContent.isFolder(); // do this _before_ asking isInvalid! Otherwise result might be wrong.
+ return aContent.isInvalid();
+}
+
+}
+
+
+OUString SvtFileDialog::implGetInitialURL( const OUString& _rPath, std::u16string_view _rFallback )
+{
+ // a URL parser for the fallback
+ INetURLObject aURLParser;
+
+ // set the path
+ bool bWasAbsolute = false;
+ aURLParser = aURLParser.smartRel2Abs( _rPath, bWasAbsolute );
+
+ // is it a valid folder?
+ m_aContent.bindTo( aURLParser.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ bool bIsFolder = m_aContent.isFolder( ); // do this _before_ asking isInvalid!
+ bool bIsInvalid = m_aContent.isInvalid();
+
+ if ( bIsInvalid && m_bHasFilename && !aURLParser.hasFinalSlash() )
+ { // check if the parent folder exists
+ INetURLObject aParent( aURLParser );
+ aParent.removeSegment( );
+ aParent.setFinalSlash( );
+ bIsInvalid = implIsInvalid( aParent.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+
+ if ( bIsInvalid )
+ {
+ INetURLObject aFallback( _rFallback );
+ bIsInvalid = implIsInvalid( aFallback.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ if ( !bIsInvalid )
+ aURLParser = aFallback;
+ }
+
+ if ( bIsInvalid )
+ {
+ INetURLObject aParent( aURLParser );
+ while ( bIsInvalid && aParent.removeSegment() )
+ {
+ aParent.setFinalSlash( );
+ bIsInvalid = implIsInvalid( aParent.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+
+ if ( !bIsInvalid )
+ aURLParser = aParent;
+ }
+
+ if ( !bIsInvalid && bIsFolder )
+ {
+ aURLParser.setFinalSlash();
+ }
+ return aURLParser.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+}
+
+
+short SvtFileDialog::run()
+{
+ if ( !PrepareExecute() )
+ return 0;
+
+ // start the dialog
+ m_bIsInExecute = true;
+ short nResult = GenericDialogController::run();
+ m_bIsInExecute = false;
+
+ SAL_WARN_IF( m_pCurrentAsyncAction.is(), "fpicker.office", "SvtFilePicker::run: still running an async action!" );
+ // the dialog should not be cancellable while an async action is running - first, the action
+ // needs to be cancelled
+
+ // remember last directory
+ if ( RET_OK == nResult )
+ {
+ INetURLObject aURL( m_aPath );
+ if ( aURL.GetProtocol() == INetProtocol::File )
+ {
+ // remember the selected directory only for file URLs not for virtual folders
+ sal_Int32 nLevel = aURL.getSegmentCount();
+ bool bDir = m_aContent.isFolder( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ if ( nLevel > 1 && ( FILEDLG_TYPE_FILEDLG == m_xImpl->m_eDlgType || !bDir ) )
+ aURL.removeSegment();
+ }
+ }
+
+ return nResult;
+}
+
+void SvtFileDialog::onAsyncOperationStarted()
+{
+ EnableUI( false );
+ // the cancel button must be always enabled
+ m_xImpl->m_xBtnCancel->set_sensitive(true);
+ m_xImpl->m_xBtnCancel->grab_focus();
+}
+
+void SvtFileDialog::onAsyncOperationFinished()
+{
+ EnableUI( true );
+ m_pCurrentAsyncAction = nullptr;
+ if ( !m_bInExecuteAsync )
+ m_xImpl->m_xEdFileName->grab_focus();
+ // (if m_bInExecuteAsync is true, then the operation was finished within the minimum wait time,
+ // and to the user, the operation appears to be synchronous)
+}
+
+void SvtFileDialog::RemovablePlaceSelected(bool enable)
+{
+ m_xImpl->m_xPlaces->SetDelEnabled( enable );
+}
+
+void SvtFileDialog::displayIOException( const OUString& _rURL, IOErrorCode _eCode )
+{
+ try
+ {
+ // create make a human-readable string from the URL
+ OUString sDisplayPath;
+ if (osl::FileBase::getSystemPathFromFileURL(_rURL, sDisplayPath)
+ == osl::FileBase::E_None)
+ {
+ sDisplayPath = _rURL;
+ }
+
+ // build an own exception which tells "access denied"
+ InteractiveAugmentedIOException aException;
+ aException.Arguments =
+ { css::uno::Any(sDisplayPath),
+ css::uno::Any(PropertyValue(
+ "Uri",
+ -1, aException.Arguments[ 0 ], PropertyState_DIRECT_VALUE
+ )) };
+ // (formerly, it was sufficient to put the URL first parameter. Nowadays,
+ // the services expects the URL in a PropertyValue named "Uri" ...)
+ aException.Code = _eCode;
+ aException.Classification = InteractionClassification_ERROR;
+
+ // let and interaction handler handle this exception
+ rtl::Reference<::comphelper::OInteractionRequest> pRequest =
+ new ::comphelper::OInteractionRequest( Any( aException ) );
+ pRequest->addContinuation( new ::comphelper::OInteractionAbort( ) );
+
+ Reference< XInteractionHandler2 > xHandler(
+ InteractionHandler::createWithParent( ::comphelper::getProcessComponentContext(), nullptr ) );
+ xHandler->handle( pRequest );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "fpicker", "iodlg::displayIOException" );
+ }
+}
+
+void SvtFileDialog::EnableUI(bool bEnable)
+{
+ m_xDialog->set_sensitive(bEnable);
+
+ if (bEnable)
+ {
+ for (auto& rxControl : m_aDisabledControls)
+ {
+ rxControl->set_sensitive(false);
+ }
+ }
+}
+
+void SvtFileDialog::EnableControl(weld::Widget* pControl, bool bEnable)
+{
+ if (!pControl)
+ {
+ SAL_WARN( "fpicker.office", "SvtFileDialog::EnableControl: invalid control!" );
+ return;
+ }
+
+ pControl->set_sensitive(bEnable);
+
+ if (bEnable)
+ {
+ auto aPos = m_aDisabledControls.find( pControl );
+ if ( m_aDisabledControls.end() != aPos )
+ m_aDisabledControls.erase( aPos );
+ }
+ else
+ m_aDisabledControls.insert( pControl );
+}
+
+bool SvtFileDialog::PrepareExecute()
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return false;
+
+ OUString aEnvValue;
+ if ( getEnvironmentValue( "WorkDirMustContainRemovableMedia", aEnvValue ) && aEnvValue == "1" )
+ {
+ try
+ {
+ INetURLObject aStdDir( GetStandardDir() );
+ ::ucbhelper::Content aCnt( aStdDir.GetMainURL(
+ INetURLObject::DecodeMechanism::NONE ),
+ Reference< XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ Sequence< OUString > aProps { "IsVolume", "IsRemoveable" };
+
+ Reference< XResultSet > xResultSet
+ = aCnt.createCursor( aProps, ::ucbhelper::INCLUDE_FOLDERS_ONLY );
+ if ( xResultSet.is() && !xResultSet->next() )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ FpsResId(STR_SVT_NOREMOVABLEDEVICE)));
+ xBox->run();
+ return false;
+ }
+ }
+ catch ( ContentCreationException const & )
+ {
+ }
+ catch ( CommandAbortedException const & )
+ {
+ }
+ }
+
+ if ( ( m_xImpl->m_nStyle & PickerFlags::SaveAs ) && m_bHasFilename )
+ // when doing a save-as, we do not want the handler to handle "this file does not exist" messages
+ // - finally we're going to save that file, aren't we?
+ m_aContent.enableOwnInteractionHandler(::svt::OFilePickerInteractionHandler::E_DOESNOTEXIST);
+ else
+ m_aContent.enableDefaultInteractionHandler();
+
+ // possibly just a filename without a path
+ OUString aFileNameOnly;
+ if( !m_aPath.isEmpty() && (m_xImpl->m_eMode == FILEDLG_MODE_SAVE)
+ && (m_aPath.indexOf(':') == -1)
+ && (m_aPath.indexOf('\\') == -1)
+ && (m_aPath.indexOf('/') == -1))
+ {
+ aFileNameOnly = m_aPath;
+ m_aPath.clear();
+ }
+
+ // no starting path specified?
+ if ( m_aPath.isEmpty() )
+ {
+ // then use the standard directory
+ m_aPath = lcl_ensureFinalSlash( m_xImpl->GetStandardDir() );
+
+ // attach given filename to path
+ if ( !aFileNameOnly.isEmpty() )
+ m_aPath += aFileNameOnly;
+ }
+
+
+ m_aPath = implGetInitialURL( m_aPath, GetStandardDir() );
+
+ if ( m_xImpl->m_nStyle & PickerFlags::SaveAs && !m_bHasFilename )
+ // when doing a save-as, we do not want the handler to handle "this file does not exist" messages
+ // - finally we're going to save that file, aren't we?
+ m_aContent.enableOwnInteractionHandler(::svt::OFilePickerInteractionHandler::E_DOESNOTEXIST);
+
+ // if applicable show filter
+ m_xImpl->InitFilterList();
+
+ // set up initial filter
+ sal_uInt16 nFilterCount = GetFilterCount();
+ OUString aAll = FpsResId( STR_FILTERNAME_ALL );
+ bool bHasAll = m_xImpl->HasFilterListEntry( aAll );
+ if ( m_xImpl->GetCurFilter() || nFilterCount == 1 || ( nFilterCount == 2 && bHasAll ) )
+ {
+ // if applicable set the only filter or the only filter that
+ // does not refer to all files, as the current one
+ if ( !m_xImpl->GetCurFilter() )
+ {
+ sal_uInt16 nPos = 0;
+ if ( 2 == nFilterCount && bHasAll )
+ {
+ nPos = nFilterCount;
+ while ( nPos-- )
+ {
+ if ( aAll != GetFilterName( nPos ) )
+ break;
+ }
+ }
+ SvtFileDialogFilter_Impl* pNewCurFilter = m_xImpl->m_aFilter[ nPos ].get();
+ assert( pNewCurFilter && "SvtFileDialog::run: invalid filter pos!" );
+ m_xImpl->SetCurFilter( pNewCurFilter, pNewCurFilter->GetName() );
+ }
+
+ // adjust view
+ m_xImpl->SelectFilterListEntry( m_xImpl->GetCurFilter()->GetName() );
+ SetDefaultExt( m_xImpl->GetCurFilter()->GetExtension() );
+ sal_Int32 nSepPos = GetDefaultExt().indexOf( FILEDIALOG_DEF_EXTSEP );
+ if ( nSepPos != -1 )
+ EraseDefaultExt( nSepPos );
+ }
+ else
+ {
+ // if applicable set respectively create filter for all files
+ if ( !bHasAll )
+ {
+ SvtFileDialogFilter_Impl* pAllFilter = implAddFilter( aAll, FILEDIALOG_FILTER_ALL );
+ m_xImpl->InsertFilterListEntry( pAllFilter );
+ m_xImpl->SetCurFilter( pAllFilter, aAll );
+ }
+ m_xImpl->SelectFilterListEntry( aAll );
+ }
+
+ // if applicable isolate filter
+ OUString aFilter;
+
+ if ( !IsolateFilterFromPath_Impl( m_aPath, aFilter ) )
+ return false;
+
+ AdjustFilterFlags nNewFilterFlags = adjustFilter( aFilter );
+ if ( nNewFilterFlags & ( AdjustFilterFlags::NonEmpty | AdjustFilterFlags::UserFilter ) )
+ {
+ m_xImpl->m_xEdFileName->set_entry_text( aFilter );
+ }
+
+ // create and show instance for set path
+ INetURLObject aFolderURL( m_aPath );
+ OUString aFileName( aFolderURL.getName( INetURLObject::LAST_SEGMENT, false ) );
+ sal_Int32 nFileNameLen = aFileName.getLength();
+ bool bFileToSelect = nFileNameLen != 0;
+ if ( bFileToSelect && aFileName[ nFileNameLen - 1 ] != '/' )
+ {
+ OUString aDecodedName = aFolderURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+ m_xImpl->m_xEdFileName->set_entry_text( aDecodedName );
+ aFolderURL.removeSegment();
+ }
+
+ INetURLObject aObj = aFolderURL;
+ if ( aObj.GetProtocol() == INetProtocol::File )
+ {
+ // set folder as current directory
+ aObj.setFinalSlash();
+ }
+
+ UpdateControls( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ // Somebody might want to enable some controls according to the current filter
+ FilterSelect();
+
+ OpenURL_Impl( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ // if applicable read and set size from ini
+ InitSize();
+
+ return true;
+}
+
+void SvtFileDialog::executeAsync( ::svt::AsyncPickerAction::Action eAction,
+ const OUString& rURL, const OUString& rFilter )
+{
+ SAL_WARN_IF( m_pCurrentAsyncAction.is(), "fpicker.office", "SvtFileDialog::executeAsync: previous async action not yet finished!" );
+
+ m_pCurrentAsyncAction = new AsyncPickerAction( this, m_xFileView.get(), eAction );
+
+ bool bReallyAsync = true;
+ m_aConfiguration.getNodeValue( OUString( "FillAsynchronously" ) ) >>= bReallyAsync;
+
+ sal_Int32 nMinTimeout = 0;
+ m_aConfiguration.getNodeValue( OUString( "Timeout/Min" ) ) >>= nMinTimeout;
+ sal_Int32 nMaxTimeout = 0;
+ m_aConfiguration.getNodeValue( OUString( "Timeout/Max" ) ) >>= nMaxTimeout;
+
+ m_bInExecuteAsync = true;
+ m_pCurrentAsyncAction->execute(rURL, rFilter, bReallyAsync ? nMinTimeout : -1, nMaxTimeout, GetDenyList());
+ m_bInExecuteAsync = false;
+}
+
+
+void SvtFileDialog::FileSelect()
+{
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify( FILE_SELECTION_CHANGED, 0 );
+}
+
+
+void SvtFileDialog::FilterSelect()
+{
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify( CTRL_STATE_CHANGED,
+ LISTBOX_FILTER );
+}
+
+
+/* [Description]
+
+ This method sets the path for the default button.
+*/
+void SvtFileDialog::SetStandardDir( const OUString& rStdDir )
+{
+ INetURLObject aObj( rStdDir );
+ SAL_WARN_IF( aObj.GetProtocol() == INetProtocol::NotValid, "fpicker.office", "Invalid protocol!" );
+ aObj.setFinalSlash();
+ m_xImpl->SetStandardDir( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+}
+
+void SvtFileDialog::SetDenyList( const css::uno::Sequence< OUString >& rDenyList )
+{
+ m_xImpl->SetDenyList( rDenyList );
+}
+
+
+const css::uno::Sequence< OUString >& SvtFileDialog::GetDenyList() const
+{
+ return m_xImpl->GetDenyList();
+}
+
+
+/* [Description]
+
+ This method returns the standard path.
+*/
+const OUString& SvtFileDialog::GetStandardDir() const
+{
+ return m_xImpl->GetStandardDir();
+}
+
+
+void SvtFileDialog::PrevLevel_Impl()
+{
+ m_xFileView->EndInplaceEditing();
+
+ OUString sDummy;
+ executeAsync( AsyncPickerAction::ePrevLevel, sDummy, sDummy );
+}
+
+void SvtFileDialog::OpenURL_Impl( const OUString& _rURL )
+{
+ m_xFileView->EndInplaceEditing();
+
+ executeAsync( AsyncPickerAction::eOpenURL, _rURL, getMostCurrentFilter( m_xImpl ) );
+}
+
+SvtFileDialogFilter_Impl* SvtFileDialog::implAddFilter( const OUString& rFilter, const OUString& _rType )
+{
+ SvtFileDialogFilter_Impl* pNewFilter = new SvtFileDialogFilter_Impl( rFilter, _rType );
+ m_xImpl->m_aFilter.push_front( std::unique_ptr<SvtFileDialogFilter_Impl>( pNewFilter ) );
+
+ if ( !m_xImpl->GetCurFilter() )
+ m_xImpl->SetCurFilter( pNewFilter, rFilter );
+
+ return pNewFilter;
+}
+
+void SvtFileDialog::AddFilter( const OUString& rFilter, const OUString& _rType )
+{
+ SAL_WARN_IF( m_bIsInExecute, "fpicker.office", "SvtFileDialog::AddFilter: currently executing!" );
+ implAddFilter ( rFilter, _rType );
+}
+
+
+void SvtFileDialog::AddFilterGroup( const OUString& rFilter, const Sequence< StringPair >& rFilters )
+{
+ SAL_WARN_IF( m_bIsInExecute, "fpicker.office", "SvtFileDialog::AddFilter: currently executing!" );
+
+ implAddFilter( rFilter, OUString() );
+ const StringPair* pSubFilters = rFilters.getConstArray();
+ const StringPair* pSubFiltersEnd = pSubFilters + rFilters.getLength();
+ for ( ; pSubFilters != pSubFiltersEnd; ++pSubFilters )
+ implAddFilter( pSubFilters->First, pSubFilters->Second );
+}
+
+
+void SvtFileDialog::SetCurFilter( const OUString& rFilter )
+{
+ SAL_WARN_IF( m_bIsInExecute, "fpicker.office", "SvtFileDialog::SetCurFilter: currently executing!" );
+
+ // look for corresponding filter
+ sal_uInt16 nPos = m_xImpl->m_aFilter.size();
+
+ while ( nPos-- )
+ {
+ SvtFileDialogFilter_Impl* pFilter = m_xImpl->m_aFilter[ nPos ].get();
+ if ( pFilter->GetName() == rFilter )
+ {
+ m_xImpl->SetCurFilter( pFilter, rFilter );
+ break;
+ }
+ }
+}
+
+OUString SvtFileDialog::GetCurFilter() const
+{
+ OUString aFilter;
+
+ const SvtFileDialogFilter_Impl* pCurrentFilter = m_xImpl->GetCurFilter();
+ if ( pCurrentFilter )
+ aFilter = pCurrentFilter->GetName();
+
+ return aFilter;
+}
+
+OUString SvtFileDialog::getCurFilter( ) const
+{
+ return GetCurFilter();
+}
+
+sal_uInt16 SvtFileDialog::GetFilterCount() const
+{
+ return m_xImpl->m_aFilter.size();
+}
+
+const OUString& SvtFileDialog::GetFilterName( sal_uInt16 nPos ) const
+{
+ assert( nPos < GetFilterCount() && "invalid index" );
+ return m_xImpl->m_aFilter[ nPos ]->GetName();
+}
+
+void SvtFileDialog::InitSize()
+{
+ if (m_xImpl->m_aIniKey.isEmpty())
+ return;
+
+ // initialize from config
+ SvtViewOptions aDlgOpt( EViewType::Dialog, m_xImpl->m_aIniKey );
+
+ if ( aDlgOpt.Exists() )
+ {
+ m_xDialog->set_window_state(aDlgOpt.GetWindowState());
+
+ Any aUserData = aDlgOpt.GetUserItem( "UserData");
+ OUString sCfgStr;
+ if ( aUserData >>= sCfgStr )
+ m_xFileView->SetConfigString( sCfgStr );
+ }
+}
+
+std::vector<OUString> SvtFileDialog::GetPathList() const
+{
+ std::vector<OUString> aList;
+
+ m_xFileView->selected_foreach([this, &aList](weld::TreeIter& rCurEntry){
+ aList.push_back(m_xFileView->GetURL(rCurEntry));
+ return false;
+ });
+
+ if (aList.empty())
+ {
+ if ( !m_xImpl->m_xEdFileName->get_active_text().isEmpty() && m_bIsInExecute )
+ aList.push_back(m_xImpl->m_xEdFileName->GetURL());
+ else
+ aList.push_back(m_aPath);
+ }
+
+ return aList;
+}
+
+bool SvtFileDialog::IsolateFilterFromPath_Impl( OUString& rPath, OUString& rFilter )
+{
+ OUString aReversePath = comphelper::string::reverseString(rPath);
+ sal_Int32 nQuestionMarkPos = rPath.indexOf( '?' );
+ sal_Int32 nWildCardPos = rPath.indexOf( FILEDIALOG_DEF_WILDCARD );
+
+ if ( nQuestionMarkPos != -1 )
+ {
+ // use question mark as wildcard only for files
+ INetProtocol eProt = INetURLObject::CompareProtocolScheme( rPath );
+
+ if ( INetProtocol::NotValid != eProt && INetProtocol::File != eProt )
+ nQuestionMarkPos = -1;
+
+ nWildCardPos = std::min( nWildCardPos, nQuestionMarkPos );
+ }
+
+ rFilter.clear();
+
+ if ( nWildCardPos == -1 )
+ return true;
+
+ sal_Int32 nPathTokenPos = aReversePath.indexOf( '/' );
+
+ if ( nPathTokenPos == -1 )
+ {
+ OUString aDelim(
+#if defined(_WIN32)
+ '\\'
+#else
+ '/'
+#endif
+ );
+
+ nPathTokenPos = aReversePath.indexOf( aDelim );
+#if !defined( UNX )
+ if ( nPathTokenPos == -1 )
+ {
+ nPathTokenPos = aReversePath.indexOf( ':' );
+ }
+#endif
+ }
+
+ // check syntax
+ if ( nPathTokenPos != -1 )
+ {
+ if ( nPathTokenPos < (rPath.getLength() - nWildCardPos - 1) )
+ {
+ ErrorHandler::HandleError( ERRCODE_SFX_INVALIDSYNTAX );
+ return false;
+ }
+
+ // cut off filter
+ rFilter = aReversePath.copy( 0, nPathTokenPos );
+ rFilter = comphelper::string::reverseString(rFilter);
+
+ // determine folder
+ rPath = aReversePath.copy( nPathTokenPos );
+ rPath = comphelper::string::reverseString(rPath);
+ }
+ else
+ {
+ rFilter = rPath;
+ rPath.clear();
+ }
+
+ return true;
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, SizeAllocHdl, const Size&, void)
+{
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify(DIALOG_SIZE_CHANGED, 0);
+}
+
+weld::Widget* SvtFileDialog::getControl( sal_Int16 nControlId, bool bLabelControl ) const
+{
+ weld::Widget* pReturn = nullptr;
+
+ switch ( nControlId )
+ {
+ case CONTROL_FILEVIEW:
+ pReturn = bLabelControl ? nullptr : m_xFileView->identifier();
+ break;
+
+ case EDIT_FILEURL:
+ pReturn = bLabelControl
+ ? static_cast<weld::Widget*>(m_xImpl->m_xFtFileName.get())
+ : static_cast<weld::Widget*>(m_xImpl->m_xEdFileName->getWidget());
+ break;
+
+ case EDIT_FILEURL_LABEL:
+ pReturn = m_xImpl->m_xFtFileName.get();
+ break;
+
+ case CHECKBOX_AUTOEXTENSION:
+ pReturn = m_xImpl->m_xCbAutoExtension.get();
+ break;
+
+ case CHECKBOX_PASSWORD:
+ pReturn = m_xImpl->m_xCbPassword.get();
+ break;
+
+ case CHECKBOX_GPGENCRYPTION:
+ pReturn = m_xImpl->m_xCbGPGEncrypt.get();
+ break;
+
+ case CHECKBOX_FILTEROPTIONS:
+ pReturn = m_xImpl->m_xCbOptions.get();
+ break;
+
+ case CHECKBOX_READONLY:
+ pReturn = m_xCbReadOnly.get();
+ break;
+
+ case CHECKBOX_LINK:
+ pReturn = m_xCbLinkBox.get();
+ break;
+
+ case CHECKBOX_PREVIEW:
+ pReturn = m_xCbPreviewBox.get();
+ break;
+
+ case CHECKBOX_SELECTION:
+ pReturn = m_xCbSelection.get();
+ break;
+
+ case LISTBOX_FILTER:
+ pReturn = bLabelControl ? m_xImpl->m_xFtFileType.get() : m_xImpl->GetFilterListControl();
+ break;
+
+ case LISTBOX_FILTER_LABEL:
+ pReturn = m_xImpl->m_xFtFileType.get();
+ break;
+
+ case FIXEDTEXT_CURRENTFOLDER:
+ pReturn = m_xImpl->m_xEdCurrentPath->getWidget();
+ break;
+
+ case LISTBOX_VERSION:
+ pReturn = bLabelControl
+ ? static_cast<weld::Widget*>(m_xImpl->m_xSharedLabel.get())
+ : static_cast<weld::Widget*>(m_xImpl->m_xSharedListBox.get());
+ break;
+
+ case LISTBOX_TEMPLATE:
+ pReturn = bLabelControl
+ ? static_cast<weld::Widget*>(m_xImpl->m_xSharedLabel.get())
+ : static_cast<weld::Widget*>(m_xImpl->m_xSharedListBox.get());
+ break;
+
+ case LISTBOX_IMAGE_TEMPLATE:
+ pReturn = bLabelControl
+ ? static_cast<weld::Widget*>(m_xImpl->m_xSharedLabel.get())
+ : static_cast<weld::Widget*>(m_xImpl->m_xSharedListBox.get());
+ break;
+
+ case LISTBOX_IMAGE_ANCHOR:
+ pReturn = bLabelControl
+ ? static_cast<weld::Widget*>(m_xImpl->m_xSharedLabel.get())
+ : static_cast<weld::Widget*>(m_xImpl->m_xSharedListBox.get());
+ break;
+
+ case LISTBOX_VERSION_LABEL:
+ pReturn = m_xImpl->m_xSharedLabel.get();
+ break;
+
+ case LISTBOX_TEMPLATE_LABEL:
+ pReturn = m_xImpl->m_xSharedLabel.get();
+ break;
+
+ case LISTBOX_IMAGE_TEMPLATE_LABEL:
+ pReturn = m_xImpl->m_xSharedLabel.get();
+ break;
+
+ case LISTBOX_IMAGE_ANCHOR_LABEL:
+ pReturn = m_xImpl->m_xSharedLabel.get();
+ break;
+
+ case PUSHBUTTON_OK:
+ pReturn = m_xImpl->m_xBtnFileOpen.get();
+ break;
+
+ case PUSHBUTTON_CANCEL:
+ pReturn = m_xImpl->m_xBtnCancel.get();
+ break;
+
+ case PUSHBUTTON_PLAY:
+ pReturn = m_xPbPlay.get();
+ break;
+
+ case PUSHBUTTON_HELP:
+ pReturn = m_xImpl->m_xBtnHelp.get();
+ break;
+
+ case TOOLBOXBUTTON_LEVEL_UP:
+ pReturn = m_xImpl->m_xBtnUp->getWidget();
+ break;
+
+ case TOOLBOXBUTTON_NEW_FOLDER:
+ pReturn = m_xImpl->m_xBtnNewFolder.get();
+ break;
+
+ case LISTBOX_FILTER_SELECTOR:
+ // only exists on SalGtkFilePicker
+ break;
+
+ default:
+ SAL_WARN( "fpicker.office", "SvtFileDialog::getControl: invalid id!" );
+ }
+ return pReturn;
+}
+
+void SvtFileDialog::enableControl(sal_Int16 nControlId, bool bEnable)
+{
+ weld::Widget* pControl = getControl(nControlId);
+ if (pControl)
+ EnableControl(pControl, bEnable);
+ weld::Widget* pLabel = getControl(nControlId, true);
+ if (pLabel)
+ EnableControl(pLabel, bEnable);
+}
+
+void SvtFileDialog::AddControls_Impl( )
+{
+ // create the "insert as link" checkbox, if needed
+ if ( m_nPickerFlags & PickerFlags::InsertAsLink )
+ {
+ m_xCbLinkBox->set_label( FpsResId( STR_SVT_FILEPICKER_INSERT_AS_LINK ) );
+ m_xCbLinkBox->set_help_id( HID_FILEDLG_LINK_CB );
+ m_xCbLinkBox->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xCbLinkBox->show();
+ }
+
+ // create the "show preview" checkbox ( and the preview window, too ), if needed
+ if ( m_nPickerFlags & PickerFlags::ShowPreview )
+ {
+ m_xImpl->m_aIniKey = "ImportGraphicDialog";
+
+ // "preview"
+ m_xCbPreviewBox->set_label( FpsResId( STR_SVT_FILEPICKER_SHOW_PREVIEW ) );
+ m_xCbPreviewBox->set_help_id( HID_FILEDLG_PREVIEW_CB );
+ m_xCbPreviewBox->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xCbPreviewBox->show();
+
+ // generate preview window just here
+ m_aPreviewSize = Size(200, 300);
+ m_xPrevBmp->set_size_request(m_aPreviewSize.Width(), m_aPreviewSize.Height());
+ m_xPrevBmp->connect_size_allocate(LINK(this, SvtFileDialog, PreviewSizeAllocHdl));
+ m_xPreviewFrame->show();
+ m_xPrevBmp->set_accessible_name(FpsResId(STR_PREVIEW));
+ }
+
+ if ( m_nPickerFlags & PickerFlags::AutoExtension )
+ {
+ m_xImpl->m_xCbAutoExtension->set_label( FpsResId( STR_SVT_FILEPICKER_AUTO_EXTENSION ) );
+ m_xImpl->m_xCbAutoExtension->set_active(true);
+ m_xImpl->m_xCbAutoExtension->connect_toggled( LINK( this, SvtFileDialog, AutoExtensionHdl_Impl ) );
+ m_xImpl->m_xCbAutoExtension->show();
+ }
+
+ if ( m_nPickerFlags & PickerFlags::FilterOptions )
+ {
+ m_xImpl->m_xCbOptions->set_label( FpsResId( STR_SVT_FILEPICKER_FILTER_OPTIONS ) );
+ m_xImpl->m_xCbOptions->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xImpl->m_xCbOptions->show();
+ }
+
+ if ( m_nPickerFlags & PickerFlags::Selection )
+ {
+ m_xCbSelection->set_label( FpsResId( STR_SVT_FILEPICKER_SELECTION ) );
+ m_xCbSelection->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xCbSelection->show();
+ }
+
+ if ( m_nPickerFlags & PickerFlags::PlayButton )
+ {
+ m_xPbPlay->set_label( FpsResId( STR_SVT_FILEPICKER_PLAY ) );
+ m_xPbPlay->set_help_id( HID_FILESAVE_DOPLAY );
+ m_xPbPlay->connect_clicked( LINK( this, SvtFileDialog, PlayButtonHdl_Impl ) );
+ m_xPbPlay->show();
+ }
+
+ if ( m_nPickerFlags & PickerFlags::ShowVersions )
+ {
+ m_xImpl->m_xSharedLabel->set_label( FpsResId( STR_SVT_FILEPICKER_VERSION ) );
+ m_xImpl->m_xSharedLabel->show();
+
+ m_xImpl->m_xSharedListBox->set_help_id( HID_FILEOPEN_VERSION );
+ m_xImpl->m_xSharedListBox->show();
+ }
+ else if ( m_nPickerFlags & PickerFlags::Templates )
+ {
+ m_xImpl->m_xSharedLabel->set_label( FpsResId( STR_SVT_FILEPICKER_TEMPLATES ) );
+ m_xImpl->m_xSharedLabel->show();
+
+ m_xImpl->m_xSharedListBox->set_help_id( HID_FILEOPEN_VERSION );
+ m_xImpl->m_xSharedListBox->show();
+ // This is strange. During the re-factoring during 96930, I discovered that this help id
+ // is set in the "Templates mode". This was hidden in the previous implementation.
+ // Shouldn't this be a more meaningful help id.
+ }
+ else if ( m_nPickerFlags & PickerFlags::ImageTemplate )
+ {
+ m_xImpl->m_xSharedLabel->set_label( FpsResId( STR_SVT_FILEPICKER_IMAGE_TEMPLATE ) );
+ m_xImpl->m_xSharedLabel->show();
+
+ m_xImpl->m_xSharedListBox->set_help_id( HID_FILEOPEN_IMAGE_TEMPLATE );
+ m_xImpl->m_xSharedListBox->show();
+ }
+ else if ( m_nPickerFlags & PickerFlags::ImageAnchor )
+ {
+ m_xImpl->m_xSharedLabel->set_label( FpsResId( STR_SVT_FILEPICKER_IMAGE_ANCHOR ) );
+ m_xImpl->m_xSharedLabel->show();
+
+ m_xImpl->m_xSharedListBox->set_help_id( HID_FILEOPEN_IMAGE_ANCHOR );
+ m_xImpl->m_xSharedListBox->show();
+ }
+
+ m_xImpl->m_xPlaces.reset(new PlacesListBox(m_xBuilder->weld_tree_view("places"),
+ m_xBuilder->weld_button("add"),
+ m_xBuilder->weld_button("del"),
+ this));
+ m_xImpl->m_xPlaces->set_help_id("SVT_HID_FILESAVE_PLACES_LISTBOX");
+ m_xImpl->m_xPlaces->SetAddHdl( LINK ( this, SvtFileDialog, AddPlacePressed_Hdl ) );
+ m_xImpl->m_xPlaces->SetDelHdl( LINK ( this, SvtFileDialog, RemovePlacePressed_Hdl ) );
+
+ initDefaultPlaces();
+}
+
+IMPL_LINK(SvtFileDialog, PreviewSizeAllocHdl, const Size&, rSize, void)
+{
+ m_aPreviewSize = rSize;
+}
+
+sal_Int32 SvtFileDialog::getAvailableWidth()
+{
+ if (m_xPrevBmp)
+ return m_aPreviewSize.Width();
+ else
+ return 0;
+}
+
+sal_Int32 SvtFileDialog::getAvailableHeight()
+{
+ if (m_xPrevBmp)
+ return m_aPreviewSize.Height();
+ else
+ return 0;
+}
+
+void SvtFileDialog::setImage(const Any& rImage)
+{
+ if (!m_xPrevBmp || !m_xPreviewFrame->get_visible())
+ return;
+
+ Sequence < sal_Int8 > aBmpSequence;
+
+ if ( rImage >>= aBmpSequence )
+ {
+ BitmapEx aBmp;
+ SvMemoryStream aData( aBmpSequence.getArray(),
+ aBmpSequence.getLength(),
+ StreamMode::READ );
+ ReadDIBBitmapEx(aBmp, aData);
+
+ m_xPrevBmp->set_image(Graphic(aBmp).GetXGraphic());
+ }
+ else
+ {
+ m_xPrevBmp->set_image(nullptr);
+ }
+}
+
+OUString SvtFileDialog::getCurrentFileText( ) const
+{
+ OUString sReturn;
+ if (m_xImpl && m_xImpl->m_xEdFileName)
+ sReturn = m_xImpl->m_xEdFileName->get_active_text();
+ return sReturn;
+}
+
+void SvtFileDialog::setCurrentFileText( const OUString& _rText, bool m_bSelectAll )
+{
+ if (m_xImpl && m_xImpl->m_xEdFileName)
+ {
+ m_xImpl->m_xEdFileName->set_entry_text( _rText );
+ if ( m_bSelectAll )
+ m_xImpl->m_xEdFileName->select_entry_region(0, -1);
+ }
+}
+
+bool SvtFileDialog::isAutoExtensionEnabled() const
+{
+ return m_xImpl->m_xCbAutoExtension && m_xImpl->m_xCbAutoExtension->get_active();
+}
+
+bool SvtFileDialog::getShowState()
+{
+ if (m_xPreviewFrame)
+ return m_xPreviewFrame->get_visible();
+ else
+ return false;
+}
+
+bool SvtFileDialog::ContentHasParentFolder( const OUString& rURL )
+{
+ m_aContent.bindTo( rURL );
+
+ if ( m_aContent.isInvalid() )
+ return false;
+
+ return m_aContent.hasParentFolder( ) && m_aContent.isValid();
+}
+
+bool SvtFileDialog::ContentCanMakeFolder( const OUString& rURL )
+{
+ m_aContent.bindTo( rURL );
+
+ if ( m_aContent.isInvalid() )
+ return false;
+
+ return m_aContent.canCreateFolder( ) && m_aContent.isValid();
+}
+
+bool SvtFileDialog::ContentGetTitle( const OUString& rURL, OUString& rTitle )
+{
+ m_aContent.bindTo( rURL );
+
+ if ( m_aContent.isInvalid() )
+ return false;
+
+ OUString sTitle;
+ m_aContent.getTitle( sTitle );
+ rTitle = sTitle;
+
+ return m_aContent.isValid();
+}
+
+void SvtFileDialog::appendDefaultExtension(OUString& rFileName,
+ std::u16string_view rFilterDefaultExtension,
+ const OUString& rFilterExtensions)
+{
+ const OUString aType(rFilterExtensions.toAsciiLowerCase());
+
+ if ( aType == FILEDIALOG_FILTER_ALL )
+ return;
+
+ const OUString aTemp(rFileName.toAsciiLowerCase());
+ sal_Int32 nPos = 0;
+
+ do
+ {
+ if (nPos+1<aType.getLength() && aType[nPos]=='*') // take care of a leading *
+ ++nPos;
+ const std::u16string_view aExt(o3tl::getToken(aType, 0, FILEDIALOG_DEF_EXTSEP, nPos ));
+ if (aExt.empty())
+ continue;
+ if (o3tl::ends_with(aTemp, aExt))
+ return;
+ }
+ while (nPos>=0);
+
+ rFileName += OUString::Concat(".") + rFilterDefaultExtension;
+}
+
+void SvtFileDialog::initDefaultPlaces( )
+{
+ PlacePtr pRootPlace = std::make_shared<Place>( FpsResId(STR_DEFAULT_DIRECTORY), GetStandardDir() );
+ m_xImpl->m_xPlaces->AppendPlace( pRootPlace );
+
+ // Load from user settings
+ Sequence< OUString > placesUrlsList(officecfg::Office::Common::Misc::FilePickerPlacesUrls::get());
+ Sequence< OUString > placesNamesList(officecfg::Office::Common::Misc::FilePickerPlacesNames::get());
+
+ for(sal_Int32 nPlace = 0; nPlace < placesUrlsList.getLength() && nPlace < placesNamesList.getLength(); ++nPlace)
+ {
+ PlacePtr pPlace = std::make_shared<Place>(placesNamesList[nPlace], placesUrlsList[nPlace], true);
+ m_xImpl->m_xPlaces->AppendPlace(pPlace);
+ }
+
+ // Reset the placesList "updated" state
+ m_xImpl->m_xPlaces->IsUpdated();
+}
+
+QueryFolderNameDialog::QueryFolderNameDialog(weld::Window* _pParent,
+ const OUString& rTitle, const OUString& rDefaultText)
+ : GenericDialogController(_pParent, "fps/ui/foldernamedialog.ui", "FolderNameDialog")
+ , m_xNameEdit(m_xBuilder->weld_entry("entry"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+{
+ m_xDialog->set_title(rTitle);
+ m_xNameEdit->set_text(rDefaultText);
+ m_xNameEdit->select_region(0, -1);
+ m_xOKBtn->connect_clicked(LINK(this, QueryFolderNameDialog, OKHdl));
+ m_xNameEdit->connect_changed(LINK(this, QueryFolderNameDialog, NameHdl));
+};
+
+QueryFolderNameDialog::~QueryFolderNameDialog()
+{
+}
+
+IMPL_LINK_NOARG(QueryFolderNameDialog, OKHdl, weld::Button&, void)
+{
+ // trim the strings
+ m_xNameEdit->set_text(comphelper::string::strip(m_xNameEdit->get_text(), ' '));
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(QueryFolderNameDialog, NameHdl, weld::Entry&, void)
+{
+ // trim the strings
+ OUString aName = comphelper::string::strip(m_xNameEdit->get_text(), ' ');
+ m_xOKBtn->set_sensitive(!aName.isEmpty());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/iodlg.hxx b/fpicker/source/office/iodlg.hxx
new file mode 100644
index 0000000000..9de9261af2
--- /dev/null
+++ b/fpicker/source/office/iodlg.hxx
@@ -0,0 +1,302 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <memory>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/ucb/IOErrorCode.hpp>
+#include <unotools/confignode.hxx>
+#include "asyncfilepicker.hxx"
+#include "fpsmartcontent.hxx"
+#include "fpdialogbase.hxx"
+#include <o3tl/typed_flags_set.hxx>
+#include <vcl/timer.hxx>
+
+#include <set>
+#include <string_view>
+
+class SvtFileView;
+class SvtFileDialogFilter_Impl;
+class SvtExpFileDlg_Impl;
+class SvtURLBox;
+
+enum class AdjustFilterFlags {
+ NONE = 0x0000,
+ NonEmpty = 0x0001,
+ Changed = 0x0002,
+ UserFilter = 0x0004,
+};
+namespace o3tl {
+ template<> struct typed_flags<AdjustFilterFlags> : is_typed_flags<AdjustFilterFlags, 0x0007> {};
+}
+
+
+class SvtFileDialog final : public SvtFileDialog_Base
+{
+private:
+ std::unique_ptr<weld::CheckButton> m_xCbReadOnly;
+ std::unique_ptr<weld::CheckButton> m_xCbLinkBox;
+ std::unique_ptr<weld::CheckButton> m_xCbPreviewBox;
+ std::unique_ptr<weld::CheckButton> m_xCbSelection;
+ std::unique_ptr<weld::Button> m_xPbPlay;
+ std::unique_ptr<weld::Widget> m_xPreviewFrame;
+ std::unique_ptr<weld::Image> m_xPrevBmp;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<SvtFileView> m_xFileView;
+ ::svt::IFilePickerListener* m_pFileNotifier;
+ std::unique_ptr<SvtExpFileDlg_Impl> m_xImpl;
+ Size m_aPreviewSize;
+ PickerFlags m_nPickerFlags;
+ bool m_bIsInExecute : 1;
+
+ ::svt::SmartContent m_aContent;
+
+ ::std::set<weld::Widget*> m_aDisabledControls;
+
+ ::utl::OConfigurationNode m_aConfiguration;
+ ::rtl::Reference< ::svt::AsyncPickerAction >
+ m_pCurrentAsyncAction;
+ bool m_bInExecuteAsync;
+ bool m_bHasFilename;
+
+ DECL_LINK( FilterSelectHdl_Impl, weld::ComboBox&, void );
+ DECL_LINK( FilterSelectTimerHdl_Impl, Timer*, void );
+ DECL_LINK( NewFolderHdl_Impl, weld::Button&, void );
+ DECL_LINK( OpenUrlHdl_Impl, weld::ComboBox&, bool );
+ DECL_LINK( OpenClickHdl_Impl, weld::Button&, void );
+ DECL_LINK( CancelHdl_Impl, weld::Button&, void );
+ DECL_LINK( FileNameGetFocusHdl_Impl, weld::Widget&, void );
+ DECL_LINK( FileNameModifiedHdl_Impl, weld::ComboBox&, void );
+
+ DECL_LINK( URLBoxModifiedHdl_Impl, weld::ComboBox&, bool );
+ DECL_LINK( ConnectToServerPressed_Hdl, weld::Button&, void );
+
+ DECL_LINK( AddPlacePressed_Hdl, weld::Button&, void );
+ DECL_LINK( RemovePlacePressed_Hdl, weld::Button&, void );
+ DECL_LINK( PreviewSizeAllocHdl, const Size&, void);
+
+ void OpenHdl_Impl(void const * pVoid);
+
+ /** find a filter with the given wildcard
+ @param _rFilter
+ the wildcard pattern to look for in the filter list
+ @param _bMultiExt
+ allow for filters with more than one extension pattern
+ @param _rFilterChanged
+ set to <TRUE/> if the filter changed
+ @return
+ the filter which has been found
+ */
+ SvtFileDialogFilter_Impl* FindFilter_Impl( const OUString& _rFilter,
+ bool _bMultiExt,
+ bool& _rFilterChanged
+ );
+ void ExecuteFilter();
+ void OpenMultiSelection_Impl();
+ void AddControls_Impl( );
+
+ DECL_LINK(SelectHdl_Impl, SvtFileView*, void);
+ DECL_LINK(DblClickHdl_Impl, SvtFileView*, bool);
+ DECL_LINK(EntrySelectHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK(OpenDoneHdl_Impl, SvtFileView*, void);
+ DECL_LINK(AutoExtensionHdl_Impl, weld::Toggleable&, void);
+ DECL_LINK(ClickHdl_Impl, weld::Toggleable&, void);
+ DECL_LINK(PlayButtonHdl_Impl, weld::Button&, void);
+ DECL_LINK(SizeAllocHdl, const Size&, void);
+
+ // removes a filter with wildcards from the path and returns it
+ static bool IsolateFilterFromPath_Impl( OUString& rPath, OUString& rFilter );
+
+ OUString m_aPath;
+ OUString m_aDefExt;
+
+ /** enables or disables the complete UI of the file picker, with only offering a
+ cancel button
+
+ This method preserves the "enabled" state of its controls in the following sense:
+ If you disable a certain control, then disable the dialog UI, then enable the dialog
+ UI, the control will still be disabled.
+ This is under the assumption that you'll use EnableControl. Direct access to the control
+ (such as pControl->Enable()) will break this.
+ */
+ void EnableUI( bool _bEnable );
+
+ /** enables or disables a control
+
+ You are strongly encouraged to prefer this method over pControl->Enable( bEnable ). See
+ <member>EnableUI</member> for details.
+ */
+ void EnableControl(weld::Widget* pControl, bool bEnable);
+ virtual bool PrepareExecute() override;
+
+public:
+ SvtFileDialog( weld::Window* pParent, PickerFlags nBits );
+ virtual ~SvtFileDialog() override;
+
+ virtual short run() override;
+
+ void FileSelect();
+ void FilterSelect() override;
+
+ void SetDenyList( const css::uno::Sequence< OUString >& rDenyList ) override;
+ const css::uno::Sequence< OUString >& GetDenyList() const override;
+ void SetStandardDir( const OUString& rStdDir ) override;
+ const OUString& GetStandardDir() const override;
+ std::vector<OUString> GetPathList() const override; // for MultiSelection
+
+ void AddFilter( const OUString& rFilter,
+ const OUString& rType ) override;
+
+ void AddFilterGroup(
+ const OUString& _rFilter,
+ const css::uno::Sequence< css::beans::StringPair >& rFilters ) override;
+
+ void SetCurFilter( const OUString& rFilter ) override;
+ OUString GetCurFilter() const override;
+ sal_uInt16 GetFilterCount() const;
+ const OUString& GetFilterName( sal_uInt16 nPos ) const;
+
+ void PrevLevel_Impl();
+ void OpenURL_Impl( const OUString& rURL );
+
+ SvtFileView* GetView() override;
+
+ void InitSize();
+ void UpdateControls( const OUString& rURL ) override;
+ void EnableAutocompletion( bool _bEnable = true ) override;
+
+ void SetFileCallback( ::svt::IFilePickerListener *pNotifier ) override { m_pFileNotifier = pNotifier; }
+
+ sal_Int32 getAvailableWidth() override;
+ sal_Int32 getAvailableHeight() override;
+ void setImage( const css::uno::Any& rImage ) override;
+ bool getShowState() override;
+ bool isAutoExtensionEnabled() const;
+
+ OUString getCurrentFileText( ) const override;
+ void setCurrentFileText( const OUString& _rText, bool _bSelectAll = false ) override;
+
+ void onAsyncOperationStarted() override;
+ void onAsyncOperationFinished() override;
+
+ void RemovablePlaceSelected(bool enable = true);
+
+ static void displayIOException( const OUString& _rURL, css::ucb::IOErrorCode _eCode );
+
+ // inline
+ inline void SetPath( const OUString& rNewURL ) override;
+ inline void SetHasFilename( bool bHasFilename ) override;
+ inline const OUString& GetPath() override;
+ inline void SetDefaultExt( const OUString& rExt );
+ inline void EraseDefaultExt( sal_Int32 _nIndex = 0 );
+ inline const OUString& GetDefaultExt() const;
+
+ bool ContentIsFolder( const OUString& rURL ) override { return m_aContent.isFolder( rURL ) && m_aContent.isValid(); }
+ bool ContentHasParentFolder( const OUString& rURL );
+ bool ContentCanMakeFolder( const OUString& rURL );
+ bool ContentGetTitle( const OUString& rURL, OUString& rTitle );
+
+private:
+ SvtFileDialogFilter_Impl* implAddFilter( const OUString& _rFilter, const OUString& _rType );
+
+ /** updates m_xUserFilter with a new filter
+ <p>No checks for necessity are made.</p>
+ */
+ void createNewUserFilter( const OUString& _rNewFilter );
+
+ AdjustFilterFlags adjustFilter( const OUString& _rFilter );
+
+ // IFilePickerController, needed by OControlAccess
+ virtual weld::Widget* getControl( sal_Int16 nControlId, bool bLabelControl = false ) const override;
+ virtual void enableControl( sal_Int16 _nControlId, bool _bEnable ) override;
+ virtual OUString getCurFilter( ) const override;
+
+ OUString implGetInitialURL( const OUString& _rPath, std::u16string_view _rFallback );
+
+ /// executes a certain FileView action asynchronously
+ void executeAsync(
+ ::svt::AsyncPickerAction::Action _eAction,
+ const OUString& _rURL,
+ const OUString& _rFilter
+ );
+
+ /** helper function to check and append the default filter extension if
+ necessary.
+ The function checks if the specified filename already contains one of
+ the valid extensions of the specified filter. If not the filter default
+ extension is appended to the filename.
+
+ @param _rFileName the filename which is checked and extended if necessary.
+ @param _rFilterDefaultExtension the default extension of the used filter.
+ @param _rFilterExtensions a list of one or more valid filter extensions
+ of the used filter.
+
+ */
+ static void appendDefaultExtension(
+ OUString& _rFileName,
+ std::u16string_view _rFilterDefaultExtension,
+ const OUString& _rFilterExtensions);
+
+ void initDefaultPlaces( );
+};
+
+
+inline void SvtFileDialog::SetPath( const OUString& rNewURL )
+{
+ m_aPath = rNewURL;
+}
+
+
+inline void SvtFileDialog::SetHasFilename( bool bHasFilename )
+{
+ m_bHasFilename = bHasFilename;
+}
+
+
+inline const OUString& SvtFileDialog::GetPath()
+{
+ return m_aPath;
+}
+
+
+inline void SvtFileDialog::SetDefaultExt( const OUString& rExt )
+{
+ m_aDefExt = rExt;
+}
+
+inline void SvtFileDialog::EraseDefaultExt( sal_Int32 _nIndex )
+{
+ m_aDefExt = m_aDefExt.copy( 0, _nIndex );
+}
+
+inline const OUString& SvtFileDialog::GetDefaultExt() const
+{
+ return m_aDefExt;
+}
+
+
+inline SvtFileView* SvtFileDialog::GetView()
+{
+ return m_xFileView.get();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/iodlgimp.cxx b/fpicker/source/office/iodlgimp.cxx
new file mode 100644
index 0000000000..5b2a67e38d
--- /dev/null
+++ b/fpicker/source/office/iodlgimp.cxx
@@ -0,0 +1,190 @@
+/* -*- 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 <sal/config.h>
+
+#include <string_view>
+
+#include "fileview.hxx"
+#include "iodlgimp.hxx"
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <svtools/inettbc.hxx>
+#include "iodlg.hxx"
+#include <svtools/imagemgr.hxx>
+#include <svl/svlresid.hxx>
+#include <svl/svl.hrc>
+#include <utility>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::utl;
+
+SvtFileDialogFilter_Impl::SvtFileDialogFilter_Impl( OUString aName, OUString aType )
+ : m_aName(std::move( aName ))
+ , m_aType(std::move( aType ))
+{
+ m_aType = m_aType.toAsciiLowerCase();
+}
+
+SvtFileDialogFilter_Impl::~SvtFileDialogFilter_Impl()
+{
+}
+
+//= SvtUpButton_Impl
+SvtUpButton_Impl::SvtUpButton_Impl(std::unique_ptr<weld::Toolbar> xToolbar,
+ std::unique_ptr<weld::Menu> xMenu,
+ SvtFileDialog* pDlg)
+ : m_xToolbar(std::move(xToolbar))
+ , m_xMenu(std::move(xMenu))
+ , m_pDlg(pDlg)
+{
+ m_xToolbar->set_item_menu("up_btn", m_xMenu.get());
+ m_xToolbar->connect_clicked(LINK(this, SvtUpButton_Impl, ClickHdl));
+ m_xMenu->connect_activate(LINK(this, SvtUpButton_Impl, SelectHdl));
+}
+
+void SvtUpButton_Impl::FillURLMenu()
+{
+ SvtFileView* pBox = m_pDlg->GetView();
+
+ sal_uInt16 nItemId = 1;
+
+ aURLs.clear();
+ m_xMenu->clear();
+
+ // determine parent levels
+ INetURLObject aObject( pBox->GetViewURL() );
+ sal_Int32 nCount = aObject.getSegmentCount();
+
+ ::svtools::VolumeInfo aVolInfo( true /* volume */, false /* remote */,
+ false /* removable */, false /* floppy */,
+ false /* compact disk */ );
+ OUString aVolumeImage( SvFileInformationManager::GetFolderImageId( aVolInfo ) );
+
+ while ( nCount >= 1 )
+ {
+ aObject.removeSegment();
+ OUString aParentURL(aObject.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+
+ OUString aTitle;
+
+ if (nCount == 1) // adjust the title of the top level entry (the workspace)
+ aTitle = SvlResId(STR_SVT_MIMETYPE_CNT_FSYSBOX);
+ else if (!m_pDlg->ContentGetTitle(aParentURL, aTitle) || aTitle.isEmpty())
+ aTitle = aObject.getName();
+
+ OUString aImage = ( nCount > 1 ) // if nCount == 1 means workplace, which detects the wrong image
+ ? SvFileInformationManager::GetImageId( aObject ) : aVolumeImage;
+
+ m_xMenu->append(OUString::number(nItemId), aTitle, aImage);
+ aURLs.push_back(aParentURL);
+
+ ++nItemId;
+ --nCount;
+ }
+}
+
+IMPL_LINK(SvtUpButton_Impl, SelectHdl, const OUString&, rId, void)
+{
+ sal_uInt32 nId = rId.toUInt32();
+ if (nId)
+ {
+ --nId;
+ assert( nId <= aURLs.size() && "SvtUpButton_Impl: wrong index" );
+
+ m_pDlg->OpenURL_Impl(aURLs[nId]);
+ }
+}
+
+IMPL_LINK_NOARG(SvtUpButton_Impl, ClickHdl, const OUString&, void)
+{
+ m_pDlg->PrevLevel_Impl();
+}
+
+// SvtExpFileDlg_Impl
+SvtExpFileDlg_Impl::SvtExpFileDlg_Impl()
+ : m_pCurFilter( nullptr )
+ , m_eMode( FILEDLG_MODE_OPEN )
+ , m_eDlgType( FILEDLG_TYPE_FILEDLG )
+ , m_nStyle( PickerFlags::NONE )
+ , m_aFilterIdle("fpicker SvtExpFileDlg_Impl m_aFilterIdle")
+ , m_bDoubleClick( false )
+ , m_bMultiSelection( false )
+{
+}
+
+SvtExpFileDlg_Impl::~SvtExpFileDlg_Impl()
+{
+}
+
+void SvtExpFileDlg_Impl::SetStandardDir( const OUString& _rDir )
+{
+ m_aStdDir = _rDir;
+ if (m_aStdDir.isEmpty())
+ m_aStdDir = "file:///";
+}
+
+namespace {
+ OUString lcl_DecoratedFilter( std::u16string_view _rOriginalFilter )
+ {
+ return "<" + OUString::Concat(_rOriginalFilter) + ">";
+ }
+}
+
+void SvtExpFileDlg_Impl::SetCurFilter( SvtFileDialogFilter_Impl const * pFilter, const OUString& rDisplayName )
+{
+ DBG_ASSERT( pFilter, "SvtExpFileDlg_Impl::SetCurFilter: invalid filter!" );
+ DBG_ASSERT( ( rDisplayName == pFilter->GetName() )
+ || ( rDisplayName == lcl_DecoratedFilter( pFilter->GetName() ) ),
+ "SvtExpFileDlg_Impl::SetCurFilter: arguments are inconsistent!" );
+
+ m_pCurFilter = pFilter;
+ m_sCurrentFilterDisplayName = rDisplayName;
+}
+
+void SvtExpFileDlg_Impl::InsertFilterListEntry(const SvtFileDialogFilter_Impl* pFilterDesc)
+{
+ // insert and set user data
+ OUString sId(weld::toId(pFilterDesc));
+ OUString sName = pFilterDesc->GetName();
+ if (pFilterDesc->isGroupSeparator())
+ m_xLbFilter->append_separator(sId);
+ else
+ m_xLbFilter->append(sId, sName);
+}
+
+void SvtExpFileDlg_Impl::InitFilterList( )
+{
+ // clear the current list
+ m_xLbFilter->clear();
+
+ // reinit it
+ sal_uInt16 nPos = m_aFilter.size();
+
+ // search for the first entry which is no group separator
+ while ( nPos-- && m_aFilter[ nPos ]->isGroupSeparator() )
+ ;
+
+ // add all following entries
+ while ( static_cast<sal_Int16>(nPos) >= 0 )
+ InsertFilterListEntry( m_aFilter[ nPos-- ].get() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/iodlgimp.hxx b/fpicker/source/office/iodlgimp.hxx
new file mode 100644
index 0000000000..3a02dbbee6
--- /dev/null
+++ b/fpicker/source/office/iodlgimp.hxx
@@ -0,0 +1,205 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include "PlacesListBox.hxx"
+
+#include <vcl/idle.hxx>
+
+#include <deque>
+#include <memory>
+#include <vector>
+
+class CheckBox;
+class SvtFileDialog;
+
+#define FILEDIALOG_DEF_EXTSEP ';'
+#define FILEDIALOG_DEF_WILDCARD '*'
+
+
+// SvtFileDialogFilter_Impl
+
+
+/* [Description]
+
+ Instances of this class represent a filter.
+*/
+
+class SvtFileDialogFilter_Impl
+{
+private:
+ OUString m_aName; // name of the entry
+ OUString m_aType; // filter wildcard - if empty, the entry marks a group
+
+public:
+ SvtFileDialogFilter_Impl( OUString aName, OUString aType );
+ ~SvtFileDialogFilter_Impl();
+
+ const OUString& GetName() const { return m_aName; }
+ const OUString& GetType() const { return m_aType; }
+ OUString GetExtension() const { return m_aType.getLength() > 2 ? m_aType.copy( 2 ) : OUString(); }
+
+ bool isGroupSeparator() const { return m_aType.isEmpty(); }
+};
+
+typedef std::deque<std::unique_ptr<SvtFileDialogFilter_Impl>> SvtFileDialogFilterList_Impl;
+
+enum SvtFileDlgMode
+{
+ FILEDLG_MODE_OPEN = 0,
+ FILEDLG_MODE_SAVE = 1
+};
+
+enum SvtFileDlgType
+{
+ FILEDLG_TYPE_FILEDLG = 0,
+ FILEDLG_TYPE_PATHDLG
+};
+
+class SvtUpButton_Impl
+{
+private:
+ std::unique_ptr<weld::Toolbar> m_xToolbar;
+ std::unique_ptr<weld::Menu> m_xMenu;
+ SvtFileDialog* m_pDlg;
+
+ std::vector<OUString> aURLs;
+
+public:
+ SvtUpButton_Impl(std::unique_ptr<weld::Toolbar> xToolbar,
+ std::unique_ptr<weld::Menu> xMenu,
+ SvtFileDialog* pDlg);
+
+ void set_help_id(const OUString& rHelpId) { m_xToolbar->set_help_id(rHelpId); }
+ void show() { m_xToolbar->show(); }
+
+ void FillURLMenu();
+
+ weld::Widget* getWidget() { return m_xToolbar.get(); }
+
+private:
+
+ DECL_LINK(SelectHdl, const OUString&, void);
+ DECL_LINK(ClickHdl, const OUString&, void);
+};
+
+class SvtURLBox;
+class SvtExpFileDlg_Impl
+{
+private:
+ const SvtFileDialogFilter_Impl* m_pCurFilter;
+ OUString m_sCurrentFilterDisplayName; // may differ from m_pCurFilter->GetName in case it is a cached entry
+
+ css::uno::Sequence< OUString > m_aDenyList;
+
+public:
+ SvtFileDialogFilterList_Impl m_aFilter;
+ std::unique_ptr<SvtFileDialogFilter_Impl> m_xUserFilter;
+
+ std::unique_ptr<weld::Label> m_xFtFileName;
+ std::unique_ptr<SvtURLBox> m_xEdFileName;
+
+ std::unique_ptr<weld::Label> m_xSharedLabel;
+ std::unique_ptr<weld::ComboBox> m_xSharedListBox;
+
+ std::unique_ptr<weld::Label> m_xFtFileType;
+ std::unique_ptr<weld::ComboBox> m_xLbFilter;
+ std::unique_ptr<weld::Button> m_xBtnFileOpen;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+ std::unique_ptr<weld::Button> m_xBtnHelp;
+ std::unique_ptr<SvtUpButton_Impl> m_xBtnUp;
+ std::unique_ptr<weld::Button> m_xBtnNewFolder;
+ std::unique_ptr<weld::CheckButton> m_xCbPassword;
+ std::unique_ptr<weld::CheckButton> m_xCbGPGEncrypt;
+ std::unique_ptr<SvtURLBox> m_xEdCurrentPath;
+ std::unique_ptr<weld::CheckButton> m_xCbAutoExtension;
+ std::unique_ptr<weld::CheckButton> m_xCbOptions;
+
+ std::unique_ptr<PlacesListBox> m_xPlaces;
+ std::unique_ptr<weld::Button> m_xBtnConnectToServer;
+
+ SvtFileDlgMode m_eMode;
+ SvtFileDlgType m_eDlgType;
+ PickerFlags m_nStyle;
+
+ OUString m_aStdDir;
+
+ // delay filter when traveling the filterbox
+ Idle m_aFilterIdle;
+
+ // shows OpenHdl_Imp() if the open was triggered by a double click
+ bool m_bDoubleClick;
+
+ // MultiSelection?
+ bool m_bMultiSelection;
+
+ // remember sizes
+ OUString m_aIniKey;
+
+ explicit SvtExpFileDlg_Impl();
+ ~SvtExpFileDlg_Impl();
+
+ void SetDenyList( const css::uno::Sequence< OUString >& rDenyList ) { m_aDenyList = rDenyList; }
+ const css::uno::Sequence< OUString >& GetDenyList() const { return m_aDenyList; }
+ void SetStandardDir( const OUString& rDir );
+ const OUString& GetStandardDir() const { return m_aStdDir; }
+
+ // access to the filter listbox only as weld::Widget* - we want to maintain the entries/userdata ourself
+ weld::Widget* GetFilterListControl() { return m_xLbFilter.get(); }
+ const weld::Widget* GetFilterListControl() const { return m_xLbFilter.get(); }
+ void SetFilterListSelectHdl(const Link<weld::ComboBox&, void>& rHandler)
+ {
+ m_xLbFilter->connect_changed(rHandler);
+ }
+
+ // inits the listbox for the filters from the filter list (_pFilter)
+ void InitFilterList( );
+ bool HasFilterListEntry( const OUString& rFilterName )
+ {
+ return m_xLbFilter->find_text(rFilterName) != -1;
+ }
+
+ void SelectFilterListEntry( const OUString& rFilterName )
+ {
+ m_xLbFilter->set_active_text(rFilterName);
+ }
+
+ void InsertFilterListEntry( const SvtFileDialogFilter_Impl* _pFilterDesc );
+ // _pFilterDesc must already have been added to _pFilter
+ SvtFileDialogFilter_Impl* GetSelectedFilterEntry( OUString& rDisplayName ) const
+ {
+ rDisplayName = m_xLbFilter->get_active_text();
+ return weld::fromId<SvtFileDialogFilter_Impl*>(m_xLbFilter->get_active_id());
+ }
+
+ // access to the current filter via methods only - need to care for consistency between m_pCurFilter and m_sCurrentFilterDisplayName
+ const SvtFileDialogFilter_Impl* GetCurFilter( ) const
+ {
+ return m_pCurFilter;
+ }
+
+ const OUString& GetCurFilterDisplayName() const
+ {
+ return m_sCurrentFilterDisplayName;
+ }
+
+ void SetCurFilter( SvtFileDialogFilter_Impl const * _pFilter, const OUString& rDisplayName );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/pickercallbacks.hxx b/fpicker/source/office/pickercallbacks.hxx
new file mode 100644
index 0000000000..dcc82c0839
--- /dev/null
+++ b/fpicker/source/office/pickercallbacks.hxx
@@ -0,0 +1,51 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+namespace weld { class Widget; }
+
+namespace svt
+{
+ class IFilePickerController
+ {
+ public:
+ virtual weld::Widget* getControl( sal_Int16 nControlId, bool bLabelControl = false ) const = 0;
+ virtual void enableControl( sal_Int16 nControlId, bool bEnable ) = 0;
+ virtual OUString getCurFilter( ) const = 0;
+
+ protected:
+ ~IFilePickerController() {}
+ };
+
+ class IFilePickerListener
+ {
+ public:
+ virtual void notify( sal_Int16 nEventId, sal_Int16 nControlId ) = 0;
+
+ protected:
+ ~IFilePickerListener() {}
+ };
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */