diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /fpicker/source/office | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
36 files changed, 12336 insertions, 0 deletions
diff --git a/fpicker/source/office/OfficeControlAccess.cxx b/fpicker/source/office/OfficeControlAccess.cxx new file mode 100644 index 000000000..3d06627da --- /dev/null +++ b/fpicker/source/office/OfficeControlAccess.cxx @@ -0,0 +1,770 @@ +/* -*- 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); + } + }; + + + void lcl_throwIllegalArgumentException( ) + { + throw IllegalArgumentException(); + // TODO: error message in the exception + } + } + + 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 UTF8 encoded and escaped + OString sID( OUStringToOString( sHelpID, RTL_TEXTENCODING_UTF8 ) ); + if (IsFileViewWidget(pControl)) + { + // the file view "overrides" the SetHelpId + m_pFileView->set_help_id(sID); + } + else + pControl->set_help_id(sID); + } + + OUString OControlAccess::getHelpURL(weld::Widget const * pControl) const + { + OString aHelpId = pControl->get_help_id(); + if (IsFileViewWidget(pControl)) + { + // the file view "overrides" the SetHelpId + aHelpId = m_pFileView->get_help_id(); + } + + OUString sHelpURL; + OUString aTmp( OStringToOUString( aHelpId, RTL_TEXTENCODING_UTF8 ) ); + INetURLObject aHID( aTmp ); + if ( aHID.GetProtocol() == INetProtocol::NotValid ) + sHelpURL = INET_HID_SCHEME; + sHelpURL += aTmp; + 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 + lcl_throwIllegalArgumentException(); + + if ( !( nPropertyMask & aPropDesc->nPropertyId ) ) + // it's a property which is known, but not allowed for this control + lcl_throwIllegalArgumentException(); + + 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 ) + lcl_throwIllegalArgumentException(); + + // 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 ) + lcl_throwIllegalArgumentException(); + + // 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 ) + { + lcl_throwIllegalArgumentException(); + } + } + break; + + case PropFlags::Enabled: + { + bool bEnabled = false; + if ( rValue >>= bEnabled ) + { + m_pFilePickerController->enableControl( nControlId, bEnabled ); + } + else if ( !_bIgnoreIllegalArgument ) + { + lcl_throwIllegalArgumentException(); + } + } + break; + + case PropFlags::Visible: + { + bool bVisible = false; + if ( rValue >>= bVisible ) + { + pControl->set_visible( bVisible ); + } + else if ( !_bIgnoreIllegalArgument ) + { + lcl_throwIllegalArgumentException(); + } + } + break; + + case PropFlags::HelpUrl: + { + OUString sHelpURL; + if ( rValue >>= sHelpURL ) + { + setHelpURL(pControl, sHelpURL); + } + else if ( !_bIgnoreIllegalArgument ) + { + lcl_throwIllegalArgumentException(); + } + } + 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 ) + { + lcl_throwIllegalArgumentException(); + } + } + 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 ) + { + lcl_throwIllegalArgumentException(); + } + } + 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 ) + { + lcl_throwIllegalArgumentException(); + } + } + 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 ) + { + lcl_throwIllegalArgumentException(); + } + } + 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 000000000..859ff15f2 --- /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 000000000..7410e0c96 --- /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(); + + // 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", + static_cast< OWeakObject * >(this), 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 000000000..b75a3f636 --- /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 000000000..c941d6cb6 --- /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 000000000..7b98fb5bf --- /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 000000000..86bd50517 --- /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 000000000..ed3a0798e --- /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 OString& 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 000000000..81bc4ad10 --- /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 000000000..4536690f9 --- /dev/null +++ b/fpicker/source/office/RemoteFilesDialog.cxx @@ -0,0 +1,1201 @@ +/* -*- 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(OStringToOUString(m_xDialog->get_window_state(WindowStateMask::All), RTL_TEXTENCODING_UTF8)); + + 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::Ftp: + return "FTP"; + 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(OUStringToOString(aDlgOpt.GetWindowState(), RTL_TEXTENCODING_UTF8)); + + 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 OString&, rIdent, void ) +{ + OString 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 000000000..3dfb4e1dc --- /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 OString&, 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 000000000..349d586b8 --- /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 000000000..e2bac1208 --- /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 000000000..89a2d0b0c --- /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 000000000..3eb79eb14 --- /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 000000000..0b27367d8 --- /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 000000000..5f476010c --- /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 000000000..29ae5d08b --- /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 \"" + + OString(sSettingName.getStr(), sSettingName.getLength(), 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 000000000..1c24d8eb9 --- /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 000000000..5b74719a3 --- /dev/null +++ b/fpicker/source/office/contentenumeration.cxx @@ -0,0 +1,326 @@ +/* -*- 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 <tools/diagnose_ex.h> +#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 ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::document::DocumentProperties; + using ::ucbhelper::ResultSetInclude; + 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() + { + ::osl::MutexGuard 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 ) + { + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_aFolder = _rFolder; + m_pResultHandler = nullptr; + m_rDenyList = rDenyList; + } + return enumerateFolderContent(); + } + + + void FileViewContentEnumerator::enumerateFolderContent( + const FolderDescriptor& _rFolder, IEnumerationResultHandler* _pResultHandler ) + { + ::osl::MutexGuard 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; + { + ::osl::MutexGuard aGuard( m_aMutex ); + aFolder = m_aFolder; + xEnvironment = m_xCommandEnv; + } + if ( !aFolder.aContent.get().is() ) + { + aFolder.aContent = ::ucbhelper::Content( aFolder.sURL, xEnvironment, comphelper::getProcessComponentContext() ); + { + ::osl::MutexGuard 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 + { + ::osl::MutexGuard 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 ) + { + SolarMutexGuard aGuard; + ::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) ); + } + } + + { + ::osl::MutexGuard 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; + { + ::osl::MutexGuard 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 000000000..b1c07afcf --- /dev/null +++ b/fpicker/source/office/contentenumeration.hxx @@ -0,0 +1,237 @@ +/* -*- 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 <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 ::osl::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 000000000..7e78f773e --- /dev/null +++ b/fpicker/source/office/fileview.cxx @@ -0,0 +1,1816 @@ +/* -*- 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 OString& rHelpId) { mxTreeView->set_help_id(rHelpId); } + OString 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::string_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( const OUString &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]) ); + aSizeStr += 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); + OString sCommand(xContextMenu->popup_at_rect(mxTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)))); + ExecuteContextMenuAction(sCommand); + } + + return true; +} + +void ViewTabListBox_Impl::ExecuteContextMenuAction(std::string_view rSelectedPopupEntry) +{ + if (rSelectedPopupEntry == "delete") + DeleteEntries(); + else if (rSelectedPopupEntry == "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; +} + +OString SvtFileView::get_help_id() const +{ + return mpImpl->mxView->get_help_id(); +} + +void SvtFileView::set_help_id(const OString& 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( const OUString &rFilter ) +{ + if ( rFilter.isEmpty() || ( 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 + maContent.erase(std::remove_if(maContent.begin(), maContent.end(), + [&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)); + }), + maContent.end()); +} + +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) +{ + OUString const aTab( "\t" ); + OUString const aTabString( "%09" ); + + sal_Int32 iPos; + OUString aValue(rValue); + while ( ( iPos = aValue.indexOf( aTab ) ) >= 0 ) + aValue = aValue.replaceAt( iPos, 1, aTabString ); + return aValue; +} + +void SvtFileView_Impl::CreateDisplayText_Impl() +{ + ::osl::MutexGuard aGuard( maMutex ); + + OUString const aDateSep( ", " ); + + 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 ) + + aDateSep + + 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 000000000..e2f132029 --- /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 OString& rHelpId); + OString 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 000000000..9b704d6c4 --- /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 000000000..e1ba25699 --- /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 000000000..20ab36297 --- /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 OUStringLiteral FILEDIALOG_FILTER_ALL = u"*.*"; + +// SvtFileDialog_Base + +class SvtFileDialog_Base : public weld::GenericDialogController, public ::svt::IFilePickerController +{ +public: + SvtFileDialog_Base(weld::Window* pParent, const OUString& rUIXMLDescription, const OString& 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 000000000..9d3591d75 --- /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 000000000..84f4a43d6 --- /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 000000000..cdf5f8247 --- /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 000000000..3f9cad855 --- /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 <tools/diagnose_ex.h> + + +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_pContent.reset(); + m_eState = INVALID; // default to INVALID + m_sURL = _rURL; + + if ( !m_sURL.isEmpty() ) + { + try + { + m_pContent.reset( new ::ucbhelper::Content( _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_pContent && "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_pContent->isFolder(); + else + bIs = m_pContent->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_pContent->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_pContent->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_pContent->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_pContent->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_pContent->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_pContent->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 000000000..5f9d4e32f --- /dev/null +++ b/fpicker/source/office/fpsmartcontent.hxx @@ -0,0 +1,195 @@ +/* -*- 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> + + +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::unique_ptr<::ucbhelper::Content> m_pContent; + 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_pContent ? m_pContent->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 000000000..a7addbb10 --- /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 <tools/diagnose_ex.h> +#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 const OUStringLiteral sHomeDirPropertyName( u"HomeDirectory" ); + 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(OStringToOUString(m_xDialog->get_window_state(WindowStateMask::All), RTL_TEXTENCODING_UTF8)); + 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(OUStringToOString(aDlgOpt.GetWindowState(), RTL_TEXTENCODING_UTF8)); + + 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 000000000..9de9261af --- /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 000000000..e9e8e0049 --- /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 OString&, 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 OString&, 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 000000000..caac91793 --- /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 OString& 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 OString&, void); + DECL_LINK(ClickHdl, const OString&, 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 000000000..dcc82c083 --- /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: */ |