nss: upgrade to release 3.73
[LibreOffice.git] / sfx2 / source / dialog / filedlghelper.cxx
blobbfc4c893b739383859b43564babe2a93a29aa63c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <string_view>
23 #include <sfx2/filedlghelper.hxx>
24 #include <sal/types.h>
25 #include <com/sun/star/lang/XInitialization.hpp>
26 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
27 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
28 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
29 #include <com/sun/star/ui/dialogs/FilePreviewImageFormats.hpp>
30 #include <com/sun/star/ui/dialogs/ControlActions.hpp>
31 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
32 #include <com/sun/star/ui/dialogs/XControlInformation.hpp>
33 #include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
34 #include <com/sun/star/ui/dialogs/XFilePreview.hpp>
35 #include <com/sun/star/ui/dialogs/XFilterManager.hpp>
36 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
37 #include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
38 #include <com/sun/star/lang/XServiceInfo.hpp>
39 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
40 #include <com/sun/star/beans/PropertyValue.hpp>
41 #include <com/sun/star/beans/NamedValue.hpp>
42 #include <com/sun/star/embed/ElementModes.hpp>
43 #include <com/sun/star/container/XEnumeration.hpp>
44 #include <com/sun/star/container/XContainerQuery.hpp>
45 #include <com/sun/star/task/InteractionHandler.hpp>
46 #include <com/sun/star/task/XInteractionRequest.hpp>
47 #include <com/sun/star/util/RevisionTag.hpp>
48 #include <comphelper/fileurl.hxx>
49 #include <comphelper/processfactory.hxx>
50 #include <comphelper/sequenceashashmap.hxx>
51 #include <comphelper/string.hxx>
52 #include <comphelper/types.hxx>
53 #include <tools/urlobj.hxx>
54 #include <vcl/help.hxx>
55 #include <vcl/weld.hxx>
56 #include <unotools/ucbstreamhelper.hxx>
57 #include <unotools/ucbhelper.hxx>
58 #include <osl/file.hxx>
59 #include <osl/security.hxx>
60 #include <vcl/mnemonic.hxx>
61 #include <vcl/svapp.hxx>
62 #include <unotools/pathoptions.hxx>
63 #include <unotools/saveopt.hxx>
64 #include <unotools/securityoptions.hxx>
65 #include <svl/itemset.hxx>
66 #include <svl/eitem.hxx>
67 #include <svl/intitem.hxx>
68 #include <vcl/dibtools.hxx>
69 #include <vcl/graphicfilter.hxx>
70 #include <unotools/viewoptions.hxx>
71 #include <svtools/helpids.h>
72 #include <comphelper/docpasswordrequest.hxx>
73 #include <comphelper/docpasswordhelper.hxx>
74 #include <ucbhelper/content.hxx>
75 #include <comphelper/storagehelper.hxx>
76 #include <sfx2/app.hxx>
77 #include <sfx2/frame.hxx>
78 #include <sfx2/docfile.hxx>
79 #include <sfx2/docfilt.hxx>
80 #include <sfx2/objsh.hxx>
81 #include <sfx2/sfxresid.hxx>
82 #include <sfx2/sfxsids.hrc>
83 #include "filtergrouping.hxx"
84 #include "filedlgimpl.hxx"
85 #include <sfx2/strings.hrc>
86 #include <sal/log.hxx>
87 #include <comphelper/sequence.hxx>
88 #include <tools/diagnose_ex.h>
90 #ifdef UNX
91 #include <errno.h>
92 #include <sys/stat.h>
93 #endif
95 using namespace ::com::sun::star;
96 using namespace ::com::sun::star::container;
97 using namespace ::com::sun::star::lang;
98 using namespace ::com::sun::star::ui::dialogs;
99 using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
100 using namespace ::com::sun::star::uno;
101 using namespace ::com::sun::star::beans;
102 using namespace ::cppu;
104 #define IODLG_CONFIGNAME "FilePicker_Save"
105 #define IMPGRF_CONFIGNAME "FilePicker_Graph"
106 #define USERITEM_NAME "UserItem"
108 namespace sfx2
111 namespace
113 bool lclSupportsOOXMLEncryption(const OUString& aFilterName)
115 return aFilterName == "Calc MS Excel 2007 XML"
116 || aFilterName == "MS Word 2007 XML"
117 || aFilterName == "Impress MS PowerPoint 2007 XML"
118 || aFilterName == "Impress MS PowerPoint 2007 XML AutoPlay"
119 || aFilterName == "Calc Office Open XML"
120 || aFilterName == "Impress Office Open XML"
121 || aFilterName == "Impress Office Open XML AutoPlay"
122 || aFilterName == "Office Open XML Text";
126 static const std::u16string_view* GetLastFilterConfigId( FileDialogHelper::Context _eContext )
128 static const std::u16string_view aSD_EXPORT_IDENTIFIER(u"SdExportLastFilter");
129 static const std::u16string_view aSI_EXPORT_IDENTIFIER(u"SiExportLastFilter");
130 static const std::u16string_view aSW_EXPORT_IDENTIFIER(u"SwExportLastFilter");
132 const std::u16string_view* pRet = nullptr;
134 switch( _eContext )
136 case FileDialogHelper::SD_EXPORT: pRet = &aSD_EXPORT_IDENTIFIER; break;
137 case FileDialogHelper::SI_EXPORT: pRet = &aSI_EXPORT_IDENTIFIER; break;
138 case FileDialogHelper::SW_EXPORT: pRet = &aSW_EXPORT_IDENTIFIER; break;
139 default: break;
142 return pRet;
145 static OUString EncodeSpaces_Impl( const OUString& rSource );
146 static OUString DecodeSpaces_Impl( const OUString& rSource );
148 // FileDialogHelper_Impl
150 // XFilePickerListener Methods
151 void SAL_CALL FileDialogHelper_Impl::fileSelectionChanged( const FilePickerEvent& )
153 SolarMutexGuard aGuard;
154 mpAntiImpl->FileSelectionChanged();
157 void SAL_CALL FileDialogHelper_Impl::directoryChanged( const FilePickerEvent& )
159 SolarMutexGuard aGuard;
160 mpAntiImpl->DirectoryChanged();
163 OUString SAL_CALL FileDialogHelper_Impl::helpRequested( const FilePickerEvent& aEvent )
165 SolarMutexGuard aGuard;
166 return sfx2::FileDialogHelper::HelpRequested( aEvent );
169 void SAL_CALL FileDialogHelper_Impl::controlStateChanged( const FilePickerEvent& aEvent )
171 SolarMutexGuard aGuard;
172 mpAntiImpl->ControlStateChanged( aEvent );
175 void SAL_CALL FileDialogHelper_Impl::dialogSizeChanged()
177 SolarMutexGuard aGuard;
178 mpAntiImpl->DialogSizeChanged();
181 // XDialogClosedListener Methods
182 void SAL_CALL FileDialogHelper_Impl::dialogClosed( const DialogClosedEvent& _rEvent )
184 SolarMutexGuard aGuard;
185 mpAntiImpl->DialogClosed( _rEvent );
186 postExecute( _rEvent.DialogResult );
189 // handle XFilePickerListener events
190 void FileDialogHelper_Impl::handleFileSelectionChanged()
192 if ( mbHasVersions )
193 updateVersions();
195 if ( mbShowPreview )
196 maPreviewIdle.Start();
199 void FileDialogHelper_Impl::handleDirectoryChanged()
201 if ( mbShowPreview )
202 TimeOutHdl_Impl( nullptr );
205 OUString FileDialogHelper_Impl::handleHelpRequested( const FilePickerEvent& aEvent )
207 //!!! todo: cache the help strings (here or TRA)
209 OString sHelpId;
210 // mapping from element id -> help id
211 switch ( aEvent.ElementId )
213 case ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION :
214 sHelpId = HID_FILESAVE_AUTOEXTENSION;
215 break;
217 case ExtendedFilePickerElementIds::CHECKBOX_PASSWORD :
218 sHelpId = HID_FILESAVE_SAVEWITHPASSWORD;
219 break;
221 case ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS :
222 sHelpId = HID_FILESAVE_CUSTOMIZEFILTER;
223 break;
225 case ExtendedFilePickerElementIds::CHECKBOX_READONLY :
226 sHelpId = HID_FILEOPEN_READONLY;
227 break;
229 case ExtendedFilePickerElementIds::CHECKBOX_LINK :
230 sHelpId = HID_FILEDLG_LINK_CB;
231 break;
233 case ExtendedFilePickerElementIds::CHECKBOX_PREVIEW :
234 sHelpId = HID_FILEDLG_PREVIEW_CB;
235 break;
237 case ExtendedFilePickerElementIds::PUSHBUTTON_PLAY :
238 sHelpId = HID_FILESAVE_DOPLAY;
239 break;
241 case ExtendedFilePickerElementIds::LISTBOX_VERSION_LABEL :
242 case ExtendedFilePickerElementIds::LISTBOX_VERSION :
243 sHelpId = HID_FILEOPEN_VERSION;
244 break;
246 case ExtendedFilePickerElementIds::LISTBOX_TEMPLATE_LABEL :
247 case ExtendedFilePickerElementIds::LISTBOX_TEMPLATE :
248 sHelpId = HID_FILESAVE_TEMPLATE;
249 break;
251 case ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE_LABEL :
252 case ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE :
253 sHelpId = HID_FILEOPEN_IMAGE_TEMPLATE;
254 break;
256 case ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR_LABEL :
257 case ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR :
258 sHelpId = HID_FILEOPEN_IMAGE_ANCHOR;
259 break;
261 case ExtendedFilePickerElementIds::CHECKBOX_SELECTION :
262 sHelpId = HID_FILESAVE_SELECTION;
263 break;
265 default:
266 SAL_WARN( "sfx.dialog", "invalid element id" );
269 OUString aHelpText;
270 Help* pHelp = Application::GetHelp();
271 if ( pHelp )
272 aHelpText = pHelp->GetHelpText(OStringToOUString(sHelpId, RTL_TEXTENCODING_UTF8), static_cast<weld::Widget*>(nullptr));
273 return aHelpText;
276 void FileDialogHelper_Impl::handleControlStateChanged( const FilePickerEvent& aEvent )
278 switch ( aEvent.ElementId )
280 case CommonFilePickerElementIds::LISTBOX_FILTER:
281 updateFilterOptionsBox();
282 enablePasswordBox( false );
283 updateSelectionBox();
284 // only use it for export and with our own dialog
285 if ( mbExport && !mbSystemPicker )
286 updateExportButton();
287 break;
289 case ExtendedFilePickerElementIds::CHECKBOX_PREVIEW:
290 updatePreviewState(true);
291 break;
295 void FileDialogHelper_Impl::handleDialogSizeChanged()
297 if ( mbShowPreview )
298 TimeOutHdl_Impl( nullptr );
301 // XEventListener Methods
302 void SAL_CALL FileDialogHelper_Impl::disposing( const EventObject& )
304 SolarMutexGuard aGuard;
305 dispose();
308 void FileDialogHelper_Impl::dispose()
310 if ( mxFileDlg.is() )
312 // remove the event listener
313 mxFileDlg->removeFilePickerListener( this );
315 ::comphelper::disposeComponent( mxFileDlg );
316 mxFileDlg.clear();
320 OUString FileDialogHelper_Impl::getCurrentFilterUIName() const
322 OUString aFilterName;
324 if( mxFileDlg.is() )
326 aFilterName = mxFileDlg->getCurrentFilter();
328 if ( !aFilterName.isEmpty() && isShowFilterExtensionEnabled() )
329 aFilterName = getFilterName( aFilterName );
332 return aFilterName;
335 void FileDialogHelper_Impl::LoadLastUsedFilter( const OUString& _rContextIdentifier )
337 SvtViewOptions aDlgOpt( EViewType::Dialog, IODLG_CONFIGNAME );
339 if( aDlgOpt.Exists() )
341 OUString aLastFilter;
342 if( aDlgOpt.GetUserItem( _rContextIdentifier ) >>= aLastFilter )
343 setFilter( aLastFilter );
347 void FileDialogHelper_Impl::SaveLastUsedFilter()
349 const std::u16string_view* pConfigId = GetLastFilterConfigId( meContext );
350 if( pConfigId )
351 SvtViewOptions( EViewType::Dialog, IODLG_CONFIGNAME ).SetUserItem( *pConfigId,
352 makeAny( getFilterWithExtension( getFilter() ) ) );
355 std::shared_ptr<const SfxFilter> FileDialogHelper_Impl::getCurentSfxFilter()
357 OUString aFilterName = getCurrentFilterUIName();
359 if ( mpMatcher && !aFilterName.isEmpty() )
360 return mpMatcher->GetFilter4UIName( aFilterName, m_nMustFlags, m_nDontFlags );
362 return nullptr;
365 bool FileDialogHelper_Impl::updateExtendedControl( sal_Int16 _nExtendedControlId, bool _bEnable )
367 bool bIsEnabled = false;
369 uno::Reference < XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
370 if ( xCtrlAccess.is() )
374 xCtrlAccess->enableControl( _nExtendedControlId, _bEnable );
375 bIsEnabled = _bEnable;
377 catch( const IllegalArgumentException& )
379 TOOLS_WARN_EXCEPTION( "sfx", "FileDialogHelper_Impl::updateExtendedControl" );
382 return bIsEnabled;
385 bool FileDialogHelper_Impl::CheckFilterOptionsCapability( const std::shared_ptr<const SfxFilter>& _pFilter )
387 bool bResult = false;
389 if( mxFilterCFG.is() && _pFilter )
393 Sequence < PropertyValue > aProps;
394 Any aAny = mxFilterCFG->getByName( _pFilter->GetName() );
395 if ( aAny >>= aProps )
397 OUString aServiceName;
398 for( const auto& rProp : std::as_const(aProps) )
400 if( rProp.Name == "UIComponent" )
402 rProp.Value >>= aServiceName;
403 if( !aServiceName.isEmpty() )
404 bResult = true;
409 catch( const Exception& )
414 return bResult;
417 bool FileDialogHelper_Impl::isInOpenMode() const
419 bool bRet = false;
421 switch ( m_nDialogType )
423 case FILEOPEN_SIMPLE:
424 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
425 case FILEOPEN_PLAY:
426 case FILEOPEN_LINK_PLAY:
427 case FILEOPEN_READONLY_VERSION:
428 case FILEOPEN_LINK_PREVIEW:
429 case FILEOPEN_PREVIEW:
430 bRet = true;
433 return bRet;
436 void FileDialogHelper_Impl::updateFilterOptionsBox()
438 if ( !m_bHaveFilterOptions )
439 return;
441 updateExtendedControl(
442 ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS,
443 CheckFilterOptionsCapability( getCurentSfxFilter() )
447 void FileDialogHelper_Impl::updateExportButton()
449 uno::Reference < XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
450 if ( !xCtrlAccess.is() )
451 return;
453 OUString sOldLabel( xCtrlAccess->getLabel( CommonFilePickerElementIds::PUSHBUTTON_OK ) );
455 // initialize button label; we need the label with the mnemonic char
456 if ( maButtonLabel.isEmpty() || maButtonLabel.indexOf( MNEMONIC_CHAR ) == -1 )
458 // cut the ellipses, if necessary
459 sal_Int32 nIndex = sOldLabel.indexOf( "..." );
460 if ( -1 == nIndex )
461 nIndex = sOldLabel.getLength();
462 maButtonLabel = sOldLabel.copy( 0, nIndex );
465 OUString sLabel = maButtonLabel;
466 // filter with options -> append ellipses on export button label
467 if ( CheckFilterOptionsCapability( getCurentSfxFilter() ) )
468 sLabel += "...";
470 if ( sOldLabel != sLabel )
474 xCtrlAccess->setLabel( CommonFilePickerElementIds::PUSHBUTTON_OK, sLabel );
476 catch( const IllegalArgumentException& )
478 TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::updateExportButton" );
483 void FileDialogHelper_Impl::updateSelectionBox()
485 if ( !mbHasSelectionBox )
486 return;
488 // Does the selection box exist?
489 bool bSelectionBoxFound = false;
490 uno::Reference< XControlInformation > xCtrlInfo( mxFileDlg, UNO_QUERY );
491 if ( xCtrlInfo.is() )
493 Sequence< OUString > aCtrlList = xCtrlInfo->getSupportedControls();
494 bSelectionBoxFound = comphelper::findValue(aCtrlList, "SelectionBox") != -1;
497 if ( bSelectionBoxFound )
499 std::shared_ptr<const SfxFilter> pFilter = getCurentSfxFilter();
500 mbSelectionFltrEnabled = updateExtendedControl(
501 ExtendedFilePickerElementIds::CHECKBOX_SELECTION,
502 ( mbSelectionEnabled && pFilter && ( pFilter->GetFilterFlags() & SfxFilterFlags::SUPPORTSSELECTION ) ) );
503 uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
504 xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0, makeAny( mbSelection ) );
508 void FileDialogHelper_Impl::enablePasswordBox( bool bInit )
510 if ( ! mbHasPassword )
511 return;
513 bool bWasEnabled = mbIsPwdEnabled;
515 std::shared_ptr<const SfxFilter> pCurrentFilter = getCurentSfxFilter();
516 mbIsPwdEnabled = updateExtendedControl(
517 ExtendedFilePickerElementIds::CHECKBOX_PASSWORD,
518 pCurrentFilter && ( pCurrentFilter->GetFilterFlags() & SfxFilterFlags::ENCRYPTION )
521 if( bInit )
523 // in case of initialization previous state is not interesting
524 if( mbIsPwdEnabled )
526 uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
527 if( mbPwdCheckBoxState )
528 xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0, makeAny( true ) );
531 else if( !bWasEnabled && mbIsPwdEnabled )
533 uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
534 if( mbPwdCheckBoxState )
535 xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0, makeAny( true ) );
537 else if( bWasEnabled && !mbIsPwdEnabled )
539 // remember user settings until checkbox is enabled
540 uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
541 Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0 );
542 bool bPassWord = false;
543 mbPwdCheckBoxState = ( aValue >>= bPassWord ) && bPassWord;
544 xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0, makeAny( false ) );
548 void FileDialogHelper_Impl::updatePreviewState( bool _bUpdatePreviewWindow )
550 if ( !mbHasPreview )
551 return;
553 uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
555 // check, whether or not we have to display a preview
556 if ( !xCtrlAccess.is() )
557 return;
561 Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0 );
562 bool bShowPreview = false;
564 if ( aValue >>= bShowPreview )
566 mbShowPreview = bShowPreview;
568 // setShowState has currently no effect for the
569 // OpenOffice FilePicker (see svtools/source/filepicker/iodlg.cxx)
570 uno::Reference< XFilePreview > xFilePreview( mxFileDlg, UNO_QUERY );
571 if ( xFilePreview.is() )
572 xFilePreview->setShowState( mbShowPreview );
574 if ( _bUpdatePreviewWindow )
575 TimeOutHdl_Impl( nullptr );
578 catch( const Exception& )
580 TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::updatePreviewState" );
584 void FileDialogHelper_Impl::updateVersions()
586 Sequence < OUString > aEntries;
587 Sequence < OUString > aPathSeq = mxFileDlg->getFiles();
589 if ( aPathSeq.getLength() == 1 )
591 INetURLObject aObj( aPathSeq[0] );
593 if ( ( aObj.GetProtocol() == INetProtocol::File ) &&
594 ( utl::UCBContentHelper::IsDocument( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ) )
598 uno::Reference< embed::XStorage > xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(
599 aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
600 embed::ElementModes::READ );
602 DBG_ASSERT( xStorage.is(), "The method must return the storage or throw exception!" );
603 if ( !xStorage.is() )
604 throw uno::RuntimeException();
606 uno::Sequence < util::RevisionTag > xVersions = SfxMedium::GetVersionList( xStorage );
608 aEntries.realloc( xVersions.getLength() + 1 );
609 aEntries[0] = SfxResId( STR_SFX_FILEDLG_ACTUALVERSION );
611 std::transform(xVersions.begin(), xVersions.end(), std::next(aEntries.begin()),
612 [](const util::RevisionTag& rVersion) -> OUString { return rVersion.Identifier; });
614 catch( const uno::Exception& )
620 uno::Reference < XFilePickerControlAccess > xDlg( mxFileDlg, UNO_QUERY );
621 Any aValue;
625 xDlg->setValue( ExtendedFilePickerElementIds::LISTBOX_VERSION,
626 ControlActions::DELETE_ITEMS, aValue );
628 catch( const IllegalArgumentException& ){}
630 if ( !aEntries.hasElements() )
631 return;
635 aValue <<= aEntries;
636 xDlg->setValue( ExtendedFilePickerElementIds::LISTBOX_VERSION,
637 ControlActions::ADD_ITEMS, aValue );
639 Any aPos;
640 aPos <<= sal_Int32(0);
641 xDlg->setValue( ExtendedFilePickerElementIds::LISTBOX_VERSION,
642 ControlActions::SET_SELECT_ITEM, aPos );
644 catch( const IllegalArgumentException& ){}
647 IMPL_LINK_NOARG(FileDialogHelper_Impl, TimeOutHdl_Impl, Timer *, void)
649 if ( !mbHasPreview )
650 return;
652 maGraphic.Clear();
654 Any aAny;
655 uno::Reference < XFilePreview > xFilePicker( mxFileDlg, UNO_QUERY );
657 if ( ! xFilePicker.is() )
658 return;
660 Sequence < OUString > aPathSeq = mxFileDlg->getFiles();
662 if ( mbShowPreview && ( aPathSeq.getLength() == 1 ) )
664 OUString aURL = aPathSeq[0];
666 if ( ERRCODE_NONE == getGraphic( aURL, maGraphic ) )
668 // changed the code slightly;
669 // before: the bitmap was scaled and
670 // surrounded a white frame
671 // now: the bitmap will only be scaled
672 // and the filepicker implementation
673 // is responsible for placing it at its
674 // proper position and painting a frame
676 BitmapEx aBmp = maGraphic.GetBitmapEx();
677 if ( !aBmp.IsEmpty() )
679 // scale the bitmap to the correct size
680 sal_Int32 nOutWidth = xFilePicker->getAvailableWidth();
681 sal_Int32 nOutHeight = xFilePicker->getAvailableHeight();
682 sal_Int32 nBmpWidth = aBmp.GetSizePixel().Width();
683 sal_Int32 nBmpHeight = aBmp.GetSizePixel().Height();
685 double nXRatio = static_cast<double>(nOutWidth) / nBmpWidth;
686 double nYRatio = static_cast<double>(nOutHeight) / nBmpHeight;
688 if ( nXRatio < nYRatio )
689 aBmp.Scale( nXRatio, nXRatio );
690 else
691 aBmp.Scale( nYRatio, nYRatio );
693 // Convert to true color, to allow CopyPixel
694 aBmp.Convert( BmpConversion::N24Bit );
696 // and copy it into the Any
697 SvMemoryStream aData;
699 WriteDIB(aBmp, aData, false);
701 const Sequence < sal_Int8 > aBuffer(
702 static_cast< const sal_Int8* >(aData.GetData()),
703 aData.GetEndOfData() );
705 aAny <<= aBuffer;
712 SolarMutexReleaser aReleaseForCallback;
713 // clear the preview window
714 xFilePicker->setImage( FilePreviewImageFormats::BITMAP, aAny );
716 catch( const IllegalArgumentException& )
721 ErrCode FileDialogHelper_Impl::getGraphic( const OUString& rURL,
722 Graphic& rGraphic ) const
724 if ( utl::UCBContentHelper::IsFolder( rURL ) )
725 return ERRCODE_IO_NOTAFILE;
727 if ( !mpGraphicFilter )
728 return ERRCODE_IO_NOTSUPPORTED;
730 // select graphic filter from dialog filter selection
731 OUString aCurFilter( getFilter() );
733 sal_uInt16 nFilter = !aCurFilter.isEmpty() && mpGraphicFilter->GetImportFormatCount()
734 ? mpGraphicFilter->GetImportFormatNumber( aCurFilter )
735 : GRFILTER_FORMAT_DONTKNOW;
737 INetURLObject aURLObj( rURL );
739 if ( aURLObj.HasError() || INetProtocol::NotValid == aURLObj.GetProtocol() )
741 aURLObj.SetSmartProtocol( INetProtocol::File );
742 aURLObj.SetSmartURL( rURL );
745 ErrCode nRet = ERRCODE_NONE;
747 GraphicFilterImportFlags nFilterImportFlags = GraphicFilterImportFlags::SetLogsizeForJpeg;
748 // non-local?
749 if ( INetProtocol::File != aURLObj.GetProtocol() )
751 std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( rURL, StreamMode::READ );
753 if( pStream )
754 nRet = mpGraphicFilter->ImportGraphic( rGraphic, rURL, *pStream, nFilter, nullptr, nFilterImportFlags );
755 else
756 nRet = mpGraphicFilter->ImportGraphic( rGraphic, aURLObj, nFilter, nullptr, nFilterImportFlags );
758 else
760 nRet = mpGraphicFilter->ImportGraphic( rGraphic, aURLObj, nFilter, nullptr, nFilterImportFlags );
763 return nRet;
766 ErrCode FileDialogHelper_Impl::getGraphic( Graphic& rGraphic ) const
768 ErrCode nRet = ERRCODE_NONE;
770 // rhbz#1079672 do not return maGraphic, it needs not to be the selected file
772 OUString aPath;
773 Sequence<OUString> aPathSeq = mxFileDlg->getFiles();
775 if (aPathSeq.getLength() == 1)
777 aPath = aPathSeq[0];
780 if (!aPath.isEmpty())
781 nRet = getGraphic(aPath, rGraphic);
782 else
783 nRet = ERRCODE_IO_GENERAL;
785 return nRet;
788 static bool lcl_isSystemFilePicker( const uno::Reference< XFilePicker3 >& _rxFP )
792 uno::Reference< XServiceInfo > xSI( _rxFP, UNO_QUERY );
793 if ( !xSI.is() )
794 return true;
795 return xSI->supportsService( "com.sun.star.ui.dialogs.SystemFilePicker" );
797 catch( const Exception& )
800 return false;
803 namespace {
805 enum open_or_save_t {OPEN, SAVE, UNDEFINED};
809 static open_or_save_t lcl_OpenOrSave(sal_Int16 const nDialogType)
811 switch (nDialogType)
813 case FILEOPEN_SIMPLE:
814 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
815 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
816 case FILEOPEN_PLAY:
817 case FILEOPEN_LINK_PLAY:
818 case FILEOPEN_READONLY_VERSION:
819 case FILEOPEN_LINK_PREVIEW:
820 case FILEOPEN_PREVIEW:
821 return OPEN;
822 case FILESAVE_SIMPLE:
823 case FILESAVE_AUTOEXTENSION_PASSWORD:
824 case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
825 case FILESAVE_AUTOEXTENSION_SELECTION:
826 case FILESAVE_AUTOEXTENSION_TEMPLATE:
827 case FILESAVE_AUTOEXTENSION:
828 return SAVE;
829 default:
830 assert(false); // invalid dialog type
832 return UNDEFINED;
835 // FileDialogHelper_Impl
837 css::uno::Reference<css::awt::XWindow> FileDialogHelper_Impl::GetFrameInterface()
839 if (mpFrameWeld)
840 return mpFrameWeld->GetXWindow();
841 return css::uno::Reference<css::awt::XWindow>();
844 FileDialogHelper_Impl::FileDialogHelper_Impl(
845 FileDialogHelper* _pAntiImpl,
846 sal_Int16 nDialogType,
847 FileDialogFlags nFlags,
848 sal_Int16 nDialog,
849 weld::Window* pFrameWeld,
850 const OUString& sStandardDir,
851 const css::uno::Sequence< OUString >& rDenyList
853 :m_nDialogType ( nDialogType )
854 ,meContext ( FileDialogHelper::UNKNOWN_CONTEXT )
856 const char* pServiceName=nullptr;
857 switch (nDialog)
859 case SFX2_IMPL_DIALOG_SYSTEM:
860 case SFX2_IMPL_DIALOG_OOO:
861 pServiceName = "com.sun.star.ui.dialogs.OfficeFilePicker";
862 break;
863 case SFX2_IMPL_DIALOG_REMOTE:
864 pServiceName = "com.sun.star.ui.dialogs.RemoteFilePicker";
865 break;
866 default:
867 pServiceName = "com.sun.star.ui.dialogs.FilePicker";
868 break;
871 OUString aService = OUString::createFromAscii( pServiceName );
873 uno::Reference< XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
875 // create the file open dialog
876 // the flags can be SFXWB_INSERT or SFXWB_MULTISELECTION
878 mpFrameWeld = pFrameWeld;
879 mpAntiImpl = _pAntiImpl;
880 mbHasAutoExt = false;
881 mbHasPassword = false;
882 m_bHaveFilterOptions = false;
883 mbIsPwdEnabled = true;
884 mbHasVersions = false;
885 mbHasPreview = false;
886 mbShowPreview = false;
887 mbDeleteMatcher = false;
888 mbInsert = bool(nFlags & (FileDialogFlags::Insert|
889 FileDialogFlags::InsertCompare|
890 FileDialogFlags::InsertMerge));
891 mbExport = bool(nFlags & FileDialogFlags::Export);
892 mbIsSaveDlg = false;
893 mbPwdCheckBoxState = false;
894 mbSelection = false;
895 mbSelectionEnabled = true;
896 mbHasSelectionBox = false;
897 mbSelectionFltrEnabled = false;
899 // default settings
900 m_nDontFlags = SFX_FILTER_NOTINSTALLED | SfxFilterFlags::INTERNAL | SfxFilterFlags::NOTINFILEDLG;
901 if (OPEN == lcl_OpenOrSave(m_nDialogType))
902 m_nMustFlags = SfxFilterFlags::IMPORT;
903 else
904 m_nMustFlags = SfxFilterFlags::EXPORT;
907 mpMatcher = nullptr;
908 mpGraphicFilter = nullptr;
909 mnPostUserEventId = nullptr;
911 // create the picker component
912 mxFileDlg.set(xFactory->createInstance( aService ), css::uno::UNO_QUERY);
913 mbSystemPicker = lcl_isSystemFilePicker( mxFileDlg );
915 uno::Reference< XInitialization > xInit( mxFileDlg, UNO_QUERY );
917 if ( ! mxFileDlg.is() )
919 return;
923 if ( xInit.is() )
925 sal_Int16 nTemplateDescription = TemplateDescription::FILEOPEN_SIMPLE;
927 switch ( m_nDialogType )
929 case FILEOPEN_SIMPLE:
930 nTemplateDescription = TemplateDescription::FILEOPEN_SIMPLE;
931 break;
933 case FILESAVE_SIMPLE:
934 nTemplateDescription = TemplateDescription::FILESAVE_SIMPLE;
935 mbIsSaveDlg = true;
936 break;
938 case FILESAVE_AUTOEXTENSION_PASSWORD:
939 nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD;
940 mbHasPassword = true;
941 mbHasAutoExt = true;
942 mbIsSaveDlg = true;
943 break;
945 case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
946 nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS;
947 mbHasPassword = true;
949 m_bHaveFilterOptions = true;
950 if( xFactory.is() )
952 mxFilterCFG.set(
953 xFactory->createInstance( "com.sun.star.document.FilterFactory" ),
954 UNO_QUERY );
957 mbHasAutoExt = true;
958 mbIsSaveDlg = true;
959 break;
961 case FILESAVE_AUTOEXTENSION_SELECTION:
962 nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_SELECTION;
963 mbHasAutoExt = true;
964 mbIsSaveDlg = true;
965 mbHasSelectionBox = true;
966 if ( mbExport && !mxFilterCFG.is() && xFactory.is() )
968 mxFilterCFG.set(
969 xFactory->createInstance( "com.sun.star.document.FilterFactory" ),
970 UNO_QUERY );
972 break;
974 case FILESAVE_AUTOEXTENSION_TEMPLATE:
975 nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_TEMPLATE;
976 mbHasAutoExt = true;
977 mbIsSaveDlg = true;
978 break;
980 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
981 nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE;
982 mbHasPreview = true;
983 break;
985 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
986 nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR;
987 mbHasPreview = true;
988 break;
990 case FILEOPEN_PLAY:
991 nTemplateDescription = TemplateDescription::FILEOPEN_PLAY;
992 break;
994 case FILEOPEN_LINK_PLAY:
995 nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PLAY;
996 break;
998 case FILEOPEN_READONLY_VERSION:
999 nTemplateDescription = TemplateDescription::FILEOPEN_READONLY_VERSION;
1000 mbHasVersions = true;
1001 break;
1003 case FILEOPEN_LINK_PREVIEW:
1004 nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PREVIEW;
1005 mbHasPreview = true;
1006 break;
1008 case FILESAVE_AUTOEXTENSION:
1009 nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION;
1010 mbHasAutoExt = true;
1011 mbIsSaveDlg = true;
1012 break;
1014 case FILEOPEN_PREVIEW:
1015 nTemplateDescription = TemplateDescription::FILEOPEN_PREVIEW;
1016 mbHasPreview = true;
1017 break;
1019 default:
1020 SAL_WARN( "sfx.dialog", "FileDialogHelper::ctor with unknown type" );
1021 break;
1024 if (mbHasPreview)
1026 maPreviewIdle.SetPriority( TaskPriority::LOWEST );
1027 maPreviewIdle.SetInvokeHandler( LINK( this, FileDialogHelper_Impl, TimeOutHdl_Impl ) );
1030 auto xWindow = GetFrameInterface();
1032 Sequence < Any > aInitArguments(!xWindow.is() ? 3 : 4);
1034 // This is a hack. We currently know that the internal file picker implementation
1035 // supports the extended arguments as specified below.
1036 // TODO:
1037 // a) adjust the service description so that it includes the TemplateDescription and ParentWindow args
1038 // b) adjust the implementation of the system file picker to that it recognizes it
1039 if ( mbSystemPicker )
1041 aInitArguments[0] <<= nTemplateDescription;
1042 if (xWindow.is())
1043 aInitArguments[1] <<= xWindow;
1045 else
1047 aInitArguments[0] <<= NamedValue(
1048 "TemplateDescription",
1049 makeAny( nTemplateDescription )
1052 aInitArguments[1] <<= NamedValue(
1053 "StandardDir",
1054 makeAny( sStandardDir )
1057 aInitArguments[2] <<= NamedValue(
1058 "DenyList",
1059 makeAny( rDenyList )
1063 if (xWindow.is())
1064 aInitArguments[3] <<= NamedValue("ParentWindow", makeAny(xWindow));
1069 xInit->initialize( aInitArguments );
1071 catch( const Exception& )
1073 OSL_FAIL( "FileDialogHelper_Impl::FileDialogHelper_Impl: could not initialize the picker!" );
1078 // set multiselection mode
1079 if ( nFlags & FileDialogFlags::MultiSelection )
1080 mxFileDlg->setMultiSelectionMode( true );
1082 if ( nFlags & FileDialogFlags::Graphic ) // generate graphic filter only on demand
1084 addGraphicFilter();
1087 // Export dialog
1088 if ( mbExport )
1090 mxFileDlg->setTitle( SfxResId( STR_SFX_EXPLORERFILE_EXPORT ) );
1091 try {
1092 css::uno::Reference < XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY_THROW );
1093 xCtrlAccess->enableControl( ExtendedFilePickerElementIds::LISTBOX_FILTER_SELECTOR, true );
1095 catch( const Exception & ) { }
1098 // Save a copy dialog
1099 if ( nFlags & FileDialogFlags::SaveACopy )
1101 mxFileDlg->setTitle( SfxResId( STR_PB_SAVEACOPY ) );
1104 // the "insert file" dialog needs another title
1105 if ( mbInsert )
1107 if ( nFlags & FileDialogFlags::InsertCompare )
1109 mxFileDlg->setTitle( SfxResId( STR_PB_COMPAREDOC ) );
1111 else if ( nFlags & FileDialogFlags::InsertMerge )
1113 mxFileDlg->setTitle( SfxResId( STR_PB_MERGEDOC ) );
1115 else
1117 mxFileDlg->setTitle( SfxResId( STR_SFX_EXPLORERFILE_INSERT ) );
1119 uno::Reference < XFilePickerControlAccess > xExtDlg( mxFileDlg, UNO_QUERY );
1120 if ( xExtDlg.is() )
1124 xExtDlg->setLabel( CommonFilePickerElementIds::PUSHBUTTON_OK,
1125 SfxResId( STR_SFX_EXPLORERFILE_BUTTONINSERT ) );
1127 catch( const IllegalArgumentException& ){}
1131 // add the event listener
1132 mxFileDlg->addFilePickerListener( this );
1135 FileDialogHelper_Impl::~FileDialogHelper_Impl()
1137 // Remove user event if we haven't received it yet
1138 if ( mnPostUserEventId )
1139 Application::RemoveUserEvent( mnPostUserEventId );
1140 mnPostUserEventId = nullptr;
1142 mpGraphicFilter.reset();
1144 if ( mbDeleteMatcher )
1145 delete mpMatcher;
1147 maPreviewIdle.ClearInvokeHandler();
1149 ::comphelper::disposeComponent( mxFileDlg );
1152 void FileDialogHelper_Impl::setControlHelpIds( const sal_Int16* _pControlId, const char** _pHelpId )
1154 DBG_ASSERT( _pControlId && _pHelpId, "FileDialogHelper_Impl::setControlHelpIds: invalid array pointers!" );
1155 if ( !_pControlId || !_pHelpId )
1156 return;
1158 // forward these ids to the file picker
1161 const OUString sHelpIdPrefix( INET_HID_SCHEME );
1162 // the ids for the single controls
1163 uno::Reference< XFilePickerControlAccess > xControlAccess( mxFileDlg, UNO_QUERY );
1164 if ( xControlAccess.is() )
1166 while ( *_pControlId )
1168 DBG_ASSERT( INetURLObject( OStringToOUString( *_pHelpId, RTL_TEXTENCODING_UTF8 ) ).GetProtocol() == INetProtocol::NotValid, "Wrong HelpId!" );
1169 OUString sId = sHelpIdPrefix +
1170 OUString( *_pHelpId, strlen( *_pHelpId ), RTL_TEXTENCODING_UTF8 );
1171 xControlAccess->setValue( *_pControlId, ControlActions::SET_HELP_URL, makeAny( sId ) );
1173 ++_pControlId; ++_pHelpId;
1177 catch( const Exception& )
1179 OSL_FAIL( "FileDialogHelper_Impl::setControlHelpIds: caught an exception while setting the help ids!" );
1183 IMPL_LINK_NOARG( FileDialogHelper_Impl, InitControls, void*, void )
1185 mnPostUserEventId = nullptr;
1186 enablePasswordBox( true );
1187 updateFilterOptionsBox( );
1188 updateSelectionBox( );
1191 void FileDialogHelper_Impl::preExecute()
1193 loadConfig( );
1194 setDefaultValues( );
1195 updatePreviewState( false );
1197 implInitializeFileName( );
1199 #if !(defined(MACOSX) && defined(MACOSX)) && !defined(_WIN32)
1200 // allow for dialog implementations which need to be executed before they return valid values for
1201 // current filter and such
1203 // On Vista (at least SP1) it's the same as on MacOSX, the modal dialog won't let message pass
1204 // through before it returns from execution
1205 mnPostUserEventId = Application::PostUserEvent( LINK( this, FileDialogHelper_Impl, InitControls ) );
1206 #else
1207 // However, the macOS implementation's pickers run modally in execute and so the event doesn't
1208 // get through in time... so we call the methods directly
1209 enablePasswordBox( true );
1210 updateFilterOptionsBox( );
1211 updateSelectionBox( );
1212 #endif
1215 void FileDialogHelper_Impl::postExecute( sal_Int16 _nResult )
1217 if ( ExecutableDialogResults::CANCEL != _nResult )
1218 saveConfig();
1221 void FileDialogHelper_Impl::implInitializeFileName( )
1223 if ( maFileName.isEmpty() )
1224 return;
1226 INetURLObject aObj( maPath );
1227 aObj.Append( maFileName );
1229 // in case we're operating as save dialog, and "auto extension" is checked,
1230 // cut the extension from the name
1231 if ( !(mbIsSaveDlg && mbHasAutoExt) )
1232 return;
1236 bool bAutoExtChecked = false;
1238 uno::Reference < XFilePickerControlAccess > xControlAccess( mxFileDlg, UNO_QUERY );
1239 if ( xControlAccess.is()
1240 && ( xControlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0 )
1241 >>= bAutoExtChecked
1245 if ( bAutoExtChecked )
1246 { // cut the extension
1247 aObj.removeExtension( );
1248 mxFileDlg->setDefaultName(
1249 aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset));
1253 catch( const Exception& )
1255 OSL_FAIL( "FileDialogHelper_Impl::implInitializeFileName: could not ask for the auto-extension current-value!" );
1259 sal_Int16 FileDialogHelper_Impl::implDoExecute()
1261 preExecute();
1263 sal_Int16 nRet = ExecutableDialogResults::CANCEL;
1265 //On MacOSX the native file picker has to run in the primordial thread because of drawing issues
1266 //On Linux the native gtk file picker, when backed by gnome-vfs2, needs to be run in the same
1267 //primordial thread as the ucb gnome-vfs2 provider was initialized in.
1272 #ifdef _WIN32
1273 if ( mbSystemPicker )
1275 SolarMutexReleaser aSolarMutex;
1276 nRet = mxFileDlg->execute();
1278 else
1279 #endif
1280 nRet = mxFileDlg->execute();
1282 catch( const Exception& )
1284 TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::implDoExecute" );
1288 postExecute( nRet );
1290 return nRet;
1293 void FileDialogHelper_Impl::implStartExecute()
1295 DBG_ASSERT( mxFileDlg.is(), "invalid file dialog" );
1297 preExecute();
1299 if ( mbSystemPicker )
1302 else
1306 uno::Reference< XAsynchronousExecutableDialog > xAsyncDlg( mxFileDlg, UNO_QUERY );
1307 if ( xAsyncDlg.is() )
1308 xAsyncDlg->startExecuteModal( this );
1310 catch( const Exception& )
1312 TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::implDoExecute" );
1317 static void lcl_saveLastURLs(std::vector<OUString>& rpURLList,
1318 ::std::vector< OUString >& lLastURLs )
1320 lLastURLs.clear();
1321 for (auto const& url : rpURLList)
1322 lLastURLs.push_back(url);
1325 void FileDialogHelper_Impl::implGetAndCacheFiles(const uno::Reference< XInterface >& xPicker, std::vector<OUString>& rpURLList)
1327 rpURLList.clear();
1329 // a) the new way (optional!)
1330 uno::Reference< XFilePicker3 > xPickNew(xPicker, UNO_QUERY);
1331 if (xPickNew.is())
1333 Sequence< OUString > lFiles = xPickNew->getSelectedFiles();
1334 comphelper::sequenceToContainer(rpURLList, lFiles);
1337 // b) the olde way ... non optional.
1338 else
1340 uno::Reference< XFilePicker3 > xPickOld(xPicker, UNO_QUERY_THROW);
1341 Sequence< OUString > lFiles = xPickOld->getFiles();
1342 ::sal_Int32 nFiles = lFiles.getLength();
1343 if ( nFiles == 1 )
1345 rpURLList.push_back(lFiles[0]);
1347 else if ( nFiles > 1 )
1349 INetURLObject aPath( lFiles[0] );
1350 aPath.setFinalSlash();
1352 for (::sal_Int32 i = 1; i < nFiles; i++)
1354 if (i == 1)
1355 aPath.Append( lFiles[i] );
1356 else
1357 aPath.setName( lFiles[i] );
1359 rpURLList.push_back(aPath.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1364 lcl_saveLastURLs(rpURLList, mlLastURLs);
1367 ErrCode FileDialogHelper_Impl::execute( std::vector<OUString>& rpURLList,
1368 std::unique_ptr<SfxItemSet>& rpSet,
1369 OUString& rFilter )
1371 // rFilter is a pure output parameter, it shouldn't be used for anything else
1372 // changing this would surely break code
1373 // rpSet is in/out parameter, usually just a media-descriptor that can be changed by dialog
1375 uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
1377 // retrieves parameters from rpSet
1378 // for now only Password is used
1379 if ( rpSet )
1381 // check password checkbox if the document had password before
1382 if( mbHasPassword )
1384 const SfxBoolItem* pPassItem = SfxItemSet::GetItem<SfxBoolItem>(rpSet.get(), SID_PASSWORDINTERACTION, false);
1385 mbPwdCheckBoxState = ( pPassItem != nullptr && pPassItem->GetValue() );
1387 // in case the document has password to modify, the dialog should be shown
1388 const SfxUnoAnyItem* pPassToModifyItem = SfxItemSet::GetItem<SfxUnoAnyItem>(rpSet.get(), SID_MODIFYPASSWORDINFO, false);
1389 mbPwdCheckBoxState |= ( pPassToModifyItem && pPassToModifyItem->GetValue().hasValue() );
1392 const SfxBoolItem* pSelectItem = SfxItemSet::GetItem<SfxBoolItem>(rpSet.get(), SID_SELECTION, false);
1393 if ( pSelectItem )
1394 mbSelection = pSelectItem->GetValue();
1395 else
1396 mbSelectionEnabled = false;
1398 // the password will be set in case user decide so
1399 rpSet->ClearItem( SID_PASSWORDINTERACTION );
1400 if (rpSet->HasItem( SID_PASSWORD ))
1402 // As the SID_ENCRYPTIONDATA and SID_PASSWORD are using for setting password together, we need to clear them both.
1403 // Note: Do not remove SID_ENCRYPTIONDATA without SID_PASSWORD
1404 rpSet->ClearItem( SID_PASSWORD );
1405 rpSet->ClearItem( SID_ENCRYPTIONDATA );
1407 rpSet->ClearItem( SID_RECOMMENDREADONLY );
1408 rpSet->ClearItem( SID_MODIFYPASSWORDINFO );
1412 if ( mbHasPassword && !mbPwdCheckBoxState )
1414 SvtSecurityOptions aSecOpt;
1415 mbPwdCheckBoxState = (
1416 aSecOpt.IsOptionSet( SvtSecurityOptions::EOption::DocWarnRecommendPassword ) );
1419 rpURLList.clear();
1421 if ( ! mxFileDlg.is() )
1422 return ERRCODE_ABORT;
1424 if ( ExecutableDialogResults::CANCEL != implDoExecute() )
1426 // create an itemset if there is no
1427 if( !rpSet )
1428 rpSet.reset(new SfxAllItemSet( SfxGetpApp()->GetPool() ));
1430 // the item should remain only if it was set by the dialog
1431 rpSet->ClearItem( SID_SELECTION );
1433 if( mbExport && mbHasSelectionBox )
1437 Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0 );
1438 bool bSelection = false;
1439 if ( aValue >>= bSelection )
1440 rpSet->Put( SfxBoolItem( SID_SELECTION, bSelection ) );
1442 catch( const IllegalArgumentException& )
1444 OSL_FAIL( "FileDialogHelper_Impl::execute: caught an IllegalArgumentException!" );
1449 // set the read-only flag. When inserting a file, this flag is always set
1450 if ( mbInsert )
1451 rpSet->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1452 else
1454 if ( ( FILEOPEN_READONLY_VERSION == m_nDialogType ) && xCtrlAccess.is() )
1458 Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_READONLY, 0 );
1459 bool bReadOnly = false;
1460 if ( ( aValue >>= bReadOnly ) && bReadOnly )
1461 rpSet->Put( SfxBoolItem( SID_DOC_READONLY, bReadOnly ) );
1463 catch( const IllegalArgumentException& )
1465 OSL_FAIL( "FileDialogHelper_Impl::execute: caught an IllegalArgumentException!" );
1469 if ( mbHasVersions && xCtrlAccess.is() )
1473 Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::LISTBOX_VERSION,
1474 ControlActions::GET_SELECTED_ITEM_INDEX );
1475 sal_Int32 nVersion = 0;
1476 if ( ( aValue >>= nVersion ) && nVersion > 0 )
1477 // open a special version; 0 == current version
1478 rpSet->Put( SfxInt16Item( SID_VERSION, static_cast<short>(nVersion) ) );
1480 catch( const IllegalArgumentException& ){}
1483 // set the filter
1484 getRealFilter( rFilter );
1486 std::shared_ptr<const SfxFilter> pCurrentFilter = getCurentSfxFilter();
1488 // fill the rpURLList
1489 implGetAndCacheFiles( mxFileDlg, rpURLList );
1490 if ( rpURLList.empty() )
1491 return ERRCODE_ABORT;
1493 // check, whether or not we have to display a password box
1494 if ( pCurrentFilter && mbHasPassword && mbIsPwdEnabled && xCtrlAccess.is() )
1498 Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0 );
1499 bool bPassWord = false;
1500 if ( ( aValue >>= bPassWord ) && bPassWord )
1502 // ask for a password
1503 OUString aDocName(rpURLList[0]);
1504 ErrCode errCode = RequestPassword(pCurrentFilter, aDocName, rpSet.get(), GetFrameInterface());
1505 if (errCode != ERRCODE_NONE)
1506 return errCode;
1509 catch( const IllegalArgumentException& ){}
1511 // check, whether or not we have to display a key selection box
1512 if ( pCurrentFilter && mbHasPassword && xCtrlAccess.is() )
1516 Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION, 0 );
1517 bool bGpg = false;
1518 if ( ( aValue >>= bGpg ) && bGpg )
1520 uno::Sequence< beans::NamedValue > aEncryptionData;
1521 while(true)
1525 // ask for keys
1526 aEncryptionData = ::comphelper::OStorageHelper::CreateGpgPackageEncryptionData();
1527 break; // user cancelled or we've some keys now
1529 catch( const IllegalArgumentException& )
1531 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(mpFrameWeld,
1532 VclMessageType::Warning, VclButtonsType::Ok,
1533 SfxResId(RID_SVXSTR_GPG_ENCRYPT_FAILURE)));
1534 xBox->run();
1538 if ( aEncryptionData.hasElements() )
1539 rpSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData) ) );
1542 catch( const IllegalArgumentException& ){}
1545 SaveLastUsedFilter();
1546 return ERRCODE_NONE;
1548 else
1549 return ERRCODE_ABORT;
1552 ErrCode FileDialogHelper_Impl::execute()
1554 if ( ! mxFileDlg.is() )
1555 return ERRCODE_ABORT;
1557 sal_Int16 nRet = implDoExecute();
1559 maPath = mxFileDlg->getDisplayDirectory();
1561 if ( ExecutableDialogResults::CANCEL == nRet )
1562 return ERRCODE_ABORT;
1563 else
1565 return ERRCODE_NONE;
1569 OUString FileDialogHelper_Impl::getPath() const
1571 OUString aPath;
1573 if ( mxFileDlg.is() )
1574 aPath = mxFileDlg->getDisplayDirectory();
1576 if ( aPath.isEmpty() )
1577 aPath = maPath;
1579 return aPath;
1582 OUString FileDialogHelper_Impl::getFilter() const
1584 OUString aFilter = getCurrentFilterUIName();
1586 if( aFilter.isEmpty() )
1587 aFilter = maCurFilter;
1589 return aFilter;
1592 void FileDialogHelper_Impl::getRealFilter( OUString& _rFilter ) const
1594 _rFilter = getCurrentFilterUIName();
1596 if ( _rFilter.isEmpty() )
1597 _rFilter = maCurFilter;
1599 if ( !_rFilter.isEmpty() && mpMatcher )
1601 std::shared_ptr<const SfxFilter> pFilter =
1602 mpMatcher->GetFilter4UIName( _rFilter, m_nMustFlags, m_nDontFlags );
1603 _rFilter = pFilter ? pFilter->GetFilterName() : OUString();
1607 void FileDialogHelper_Impl::verifyPath()
1609 #ifdef UNX
1610 // lp#905355, fdo#43895
1611 // Check that the file has read only permission and is in /tmp -- this is
1612 // the case if we have opened the file from the web with firefox only.
1613 if (maFileName.isEmpty()) {
1614 return;
1616 INetURLObject url(maPath);
1617 if (url.GetProtocol() != INetProtocol::File
1618 || url.getName(0, true, INetURLObject::DecodeMechanism::WithCharset) != "tmp")
1620 return;
1622 if (maFileName.indexOf('/') != -1) {
1623 SAL_WARN("sfx.dialog", maFileName << " contains /");
1624 return;
1626 url.insertName(
1627 maFileName, false, INetURLObject::LAST_SEGMENT,
1628 INetURLObject::EncodeMechanism::All);
1629 OUString sysPathU;
1630 osl::FileBase::RC e = osl::FileBase::getSystemPathFromFileURL(
1631 url.GetMainURL(INetURLObject::DecodeMechanism::NONE), sysPathU);
1632 if (e != osl::FileBase::E_None) {
1633 SAL_WARN(
1634 "sfx.dialog",
1635 "getSystemPathFromFileURL("
1636 << url.GetMainURL(INetURLObject::DecodeMechanism::NONE) << ") failed with "
1637 << +e);
1638 return;
1640 OString sysPathC;
1641 if (!sysPathU.convertToString(
1642 &sysPathC, osl_getThreadTextEncoding(),
1643 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
1644 | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
1646 SAL_WARN(
1647 "sfx.dialog",
1648 "convertToString(" << sysPathU << ") failed for encoding "
1649 << +osl_getThreadTextEncoding());
1650 return;
1652 struct stat aFileStat;
1653 if (stat(sysPathC.getStr(), &aFileStat) == -1) {
1654 SAL_WARN( "sfx.dialog", "stat(" << sysPathC << ") failed with errno " << errno);
1655 return;
1657 if ((aFileStat.st_mode & (S_IRWXO | S_IRWXG | S_IRWXU)) == S_IRUSR) {
1658 maPath = SvtPathOptions().GetWorkPath();
1659 mxFileDlg->setDisplayDirectory( maPath );
1661 #else
1662 (void) this;
1663 #endif
1666 void FileDialogHelper_Impl::displayFolder( const OUString& _rPath )
1668 if ( _rPath.isEmpty() )
1669 // nothing to do
1670 return;
1672 maPath = _rPath;
1673 if ( mxFileDlg.is() )
1677 mxFileDlg->setDisplayDirectory( maPath );
1678 verifyPath();
1680 catch( const IllegalArgumentException& )
1682 TOOLS_WARN_EXCEPTION( "sfx", "FileDialogHelper_Impl::displayFolder" );
1687 void FileDialogHelper_Impl::setFileName( const OUString& _rFile )
1689 maFileName = _rFile;
1690 if ( mxFileDlg.is() )
1694 mxFileDlg->setDefaultName( maFileName );
1695 verifyPath();
1697 catch( const IllegalArgumentException& )
1699 TOOLS_WARN_EXCEPTION( "sfx", "FileDialogHelper_Impl::setFileName" );
1704 void FileDialogHelper_Impl::setFilter( const OUString& rFilter )
1706 DBG_ASSERT( rFilter.indexOf(':') == -1, "Old filter name used!");
1708 maCurFilter = rFilter;
1710 if ( !rFilter.isEmpty() && mpMatcher )
1712 std::shared_ptr<const SfxFilter> pFilter = mpMatcher->GetFilter4FilterName(
1713 rFilter, m_nMustFlags, m_nDontFlags );
1714 if ( pFilter )
1715 maCurFilter = pFilter->GetUIName();
1718 if ( !maCurFilter.isEmpty() && mxFileDlg.is() )
1722 mxFileDlg->setCurrentFilter( maCurFilter );
1724 catch( const IllegalArgumentException& ){}
1728 void FileDialogHelper_Impl::createMatcher( const OUString& rFactory )
1730 if (mbDeleteMatcher)
1731 delete mpMatcher;
1733 mpMatcher = new SfxFilterMatcher( SfxObjectShell::GetServiceNameFromFactory(rFactory) );
1734 mbDeleteMatcher = true;
1737 void FileDialogHelper_Impl::addFilters( const OUString& rFactory,
1738 SfxFilterFlags nMust,
1739 SfxFilterFlags nDont )
1741 if ( ! mxFileDlg.is() )
1742 return;
1744 if (mbDeleteMatcher)
1745 delete mpMatcher;
1747 // we still need a matcher to convert UI names to filter names
1748 if ( rFactory.isEmpty() )
1750 SfxApplication *pSfxApp = SfxGetpApp();
1751 mpMatcher = &pSfxApp->GetFilterMatcher();
1752 mbDeleteMatcher = false;
1754 else
1756 mpMatcher = new SfxFilterMatcher( rFactory );
1757 mbDeleteMatcher = true;
1760 uno::Reference< XMultiServiceFactory > xSMGR = ::comphelper::getProcessServiceFactory();
1761 uno::Reference< XContainerQuery > xFilterCont(
1762 xSMGR->createInstance("com.sun.star.document.FilterFactory"),
1763 UNO_QUERY);
1764 if ( ! xFilterCont.is() )
1765 return;
1767 m_nMustFlags |= nMust;
1768 m_nDontFlags |= nDont;
1770 // create the list of filters
1771 OUString sQuery =
1772 "getSortedFilterList()"
1773 ":module=" +
1774 rFactory + // use long name here !
1775 ":iflags=" +
1776 OUString::number(static_cast<sal_Int32>(m_nMustFlags)) +
1777 ":eflags=" +
1778 OUString::number(static_cast<sal_Int32>(m_nDontFlags));
1780 uno::Reference< XEnumeration > xResult;
1783 xResult = xFilterCont->createSubSetEnumerationByQuery(sQuery);
1785 catch( const uno::Exception& )
1787 SAL_WARN( "sfx.dialog", "Could not get filters from the configuration!" );
1790 TSortedFilterList aIter (xResult);
1792 // append the filters
1793 OUString sFirstFilter;
1794 if (OPEN == lcl_OpenOrSave(m_nDialogType))
1795 ::sfx2::appendFiltersForOpen( aIter, mxFileDlg, sFirstFilter, *this );
1796 else if ( mbExport )
1797 ::sfx2::appendExportFilters( aIter, mxFileDlg, sFirstFilter, *this );
1798 else
1799 ::sfx2::appendFiltersForSave( aIter, mxFileDlg, sFirstFilter, *this, rFactory );
1801 // set our initial selected filter (if we do not already have one)
1802 if ( maSelectFilter.isEmpty() )
1803 maSelectFilter = sFirstFilter;
1806 void FileDialogHelper_Impl::addFilter( const OUString& rFilterName,
1807 const OUString& rExtension )
1809 if ( ! mxFileDlg.is() )
1810 return;
1814 mxFileDlg->appendFilter( rFilterName, rExtension );
1816 if ( maSelectFilter.isEmpty() )
1817 maSelectFilter = rFilterName;
1819 catch( const IllegalArgumentException& )
1821 SAL_WARN( "sfx.dialog", "Could not append Filter" << rFilterName );
1825 void FileDialogHelper_Impl::addGraphicFilter()
1827 if ( ! mxFileDlg.is() )
1828 return;
1830 // create the list of filters
1831 mpGraphicFilter.reset( new GraphicFilter );
1832 sal_uInt16 i, j, nCount = mpGraphicFilter->GetImportFormatCount();
1834 // compute the extension string for all known import filters
1835 OUString aExtensions;
1837 for ( i = 0; i < nCount; i++ )
1839 j = 0;
1840 while( true )
1842 OUString sWildcard = mpGraphicFilter->GetImportWildcard( i, j++ );
1843 if ( sWildcard.isEmpty() )
1844 break;
1845 if ( aExtensions.indexOf( sWildcard ) == -1 )
1847 if ( !aExtensions.isEmpty() )
1848 aExtensions += ";";
1849 aExtensions += sWildcard;
1854 #if defined(_WIN32)
1855 if ( aExtensions.getLength() > 240 )
1856 aExtensions = FILEDIALOG_FILTER_ALL;
1857 #endif
1858 bool bIsInOpenMode = isInOpenMode();
1862 // if the extension is not "All files", insert "All images"
1863 if (aExtensions != FILEDIALOG_FILTER_ALL)
1865 OUString aAllFilterName = SfxResId(STR_SFX_IMPORT_ALL_IMAGES);
1866 aAllFilterName = ::sfx2::addExtension( aAllFilterName, aExtensions, bIsInOpenMode, *this );
1867 mxFileDlg->appendFilter( aAllFilterName, aExtensions );
1868 maSelectFilter = aAllFilterName; // and make it the default
1871 // rhbz#1715109 always include All files *.* or *
1872 OUString aAllFilesName = SfxResId( STR_SFX_FILTERNAME_ALL );
1873 aAllFilesName = ::sfx2::addExtension( aAllFilesName, FILEDIALOG_FILTER_ALL, bIsInOpenMode, *this );
1874 mxFileDlg->appendFilter( aAllFilesName, FILEDIALOG_FILTER_ALL );
1876 // if the extension is "All files", make that the default
1877 if (aExtensions == FILEDIALOG_FILTER_ALL)
1878 maSelectFilter = aAllFilesName;
1880 catch( const IllegalArgumentException& )
1882 SAL_WARN( "sfx.dialog", "Could not append Filter" );
1885 // Now add the filter
1886 for ( i = 0; i < nCount; i++ )
1888 OUString aName = mpGraphicFilter->GetImportFormatName( i );
1889 OUString aExt;
1890 j = 0;
1891 while( true )
1893 OUString sWildcard = mpGraphicFilter->GetImportWildcard( i, j++ );
1894 if ( sWildcard.isEmpty() )
1895 break;
1896 if ( aExt.indexOf( sWildcard ) == -1 )
1898 if ( !aExt.isEmpty() )
1899 aExt += ";";
1900 aExt += sWildcard;
1903 aName = ::sfx2::addExtension( aName, aExt, bIsInOpenMode, *this );
1906 mxFileDlg->appendFilter( aName, aExt );
1908 catch( const IllegalArgumentException& )
1910 SAL_WARN( "sfx.dialog", "Could not append Filter" );
1915 #define GRF_CONFIG_STR " "
1916 #define STD_CONFIG_STR "1 "
1918 static void SetToken( OUString& rOrigStr, sal_Int32 nToken, sal_Unicode cTok, const OUString& rStr)
1920 const sal_Unicode* pStr = rOrigStr.getStr();
1921 sal_Int32 nLen = rOrigStr.getLength();
1922 sal_Int32 nTok = 0;
1923 sal_Int32 nFirstChar = 0;
1924 sal_Int32 i = nFirstChar;
1926 // Determine token position and length
1927 pStr += i;
1928 while ( i < nLen )
1930 // Increase token count if match
1931 if ( *pStr == cTok )
1933 ++nTok;
1935 if ( nTok == nToken )
1936 nFirstChar = i+1;
1937 else
1939 if ( nTok > nToken )
1940 break;
1944 ++pStr;
1945 ++i;
1948 if ( nTok >= nToken )
1949 rOrigStr = rOrigStr.replaceAt( nFirstChar, i-nFirstChar, rStr );
1953 void FileDialogHelper_Impl::saveConfig()
1955 uno::Reference < XFilePickerControlAccess > xDlg( mxFileDlg, UNO_QUERY );
1956 Any aValue;
1958 if ( ! xDlg.is() )
1959 return;
1961 if ( mbHasPreview )
1963 SvtViewOptions aDlgOpt( EViewType::Dialog, IMPGRF_CONFIGNAME );
1967 aValue = xDlg->getValue( ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0 );
1968 bool bValue = false;
1969 aValue >>= bValue;
1970 OUString aUserData(GRF_CONFIG_STR);
1971 SetToken( aUserData, 1, ' ', OUString::number( static_cast<sal_Int32>(bValue) ) );
1973 INetURLObject aObj( getPath() );
1975 if ( aObj.GetProtocol() == INetProtocol::File )
1976 SetToken( aUserData, 2, ' ', aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1978 OUString aFilter = getFilter();
1979 aFilter = EncodeSpaces_Impl( aFilter );
1980 SetToken( aUserData, 3, ' ', aFilter );
1982 aDlgOpt.SetUserItem( USERITEM_NAME, makeAny( aUserData ) );
1984 catch( const IllegalArgumentException& ){}
1986 else
1988 bool bWriteConfig = false;
1989 SvtViewOptions aDlgOpt( EViewType::Dialog, IODLG_CONFIGNAME );
1990 OUString aUserData(STD_CONFIG_STR);
1992 if ( aDlgOpt.Exists() )
1994 Any aUserItem = aDlgOpt.GetUserItem( USERITEM_NAME );
1995 OUString aTemp;
1996 if ( aUserItem >>= aTemp )
1997 aUserData = aTemp;
2000 if ( mbHasAutoExt )
2004 aValue = xDlg->getValue( ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0 );
2005 bool bAutoExt = true;
2006 aValue >>= bAutoExt;
2007 SetToken( aUserData, 0, ' ', OUString::number( static_cast<sal_Int32>(bAutoExt) ) );
2008 bWriteConfig = true;
2010 catch( const IllegalArgumentException& ){}
2013 if ( ! mbIsSaveDlg )
2015 OUString aPath = getPath();
2016 if ( comphelper::isFileUrl( aPath ) )
2018 SetToken( aUserData, 1, ' ', aPath );
2019 bWriteConfig = true;
2023 if( mbHasSelectionBox && mbSelectionFltrEnabled )
2027 aValue = xDlg->getValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0 );
2028 bool bSelection = true;
2029 aValue >>= bSelection;
2030 if ( comphelper::string::getTokenCount(aUserData, ' ') < 3 )
2031 aUserData += " ";
2032 SetToken( aUserData, 2, ' ', OUString::number( static_cast<sal_Int32>(bSelection) ) );
2033 bWriteConfig = true;
2035 catch( const IllegalArgumentException& ){}
2038 if ( bWriteConfig )
2039 aDlgOpt.SetUserItem( USERITEM_NAME, makeAny( aUserData ) );
2042 SfxApplication *pSfxApp = SfxGetpApp();
2043 pSfxApp->SetLastDir_Impl( getPath() );
2046 namespace
2048 OUString getInitPath( const OUString& _rFallback, const sal_Int32 _nFallbackToken )
2050 SfxApplication *pSfxApp = SfxGetpApp();
2051 OUString sPath = pSfxApp->GetLastDir_Impl();
2053 if ( sPath.isEmpty() )
2054 sPath = _rFallback.getToken( _nFallbackToken, ' ' );
2056 // check if the path points to a valid (accessible) directory
2057 bool bValid = false;
2058 if ( !sPath.isEmpty() )
2060 OUString sPathCheck( sPath );
2061 if ( sPathCheck[ sPathCheck.getLength() - 1 ] != '/' )
2062 sPathCheck += "/";
2063 sPathCheck += ".";
2066 ::ucbhelper::Content aContent( sPathCheck,
2067 utl::UCBContentHelper::getDefaultCommandEnvironment(),
2068 comphelper::getProcessComponentContext() );
2069 bValid = aContent.isFolder();
2071 catch( const Exception& ) {}
2074 if ( !bValid )
2075 sPath.clear();
2077 return sPath;
2081 void FileDialogHelper_Impl::loadConfig()
2083 uno::Reference < XFilePickerControlAccess > xDlg( mxFileDlg, UNO_QUERY );
2084 Any aValue;
2086 if ( ! xDlg.is() )
2087 return;
2089 if ( mbHasPreview )
2091 SvtViewOptions aViewOpt( EViewType::Dialog, IMPGRF_CONFIGNAME );
2092 OUString aUserData;
2094 if ( aViewOpt.Exists() )
2096 Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
2097 OUString aTemp;
2098 if ( aUserItem >>= aTemp )
2099 aUserData = aTemp;
2102 if ( !aUserData.isEmpty() )
2106 // respect the last "insert as link" state
2107 bool bLink = aUserData.getToken( 0, ' ' ).toInt32();
2108 aValue <<= bLink;
2109 xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, aValue );
2111 // respect the last "show preview" state
2112 bool bShowPreview = aUserData.getToken( 1, ' ' ).toInt32();
2113 aValue <<= bShowPreview;
2114 xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0, aValue );
2116 if ( maPath.isEmpty() )
2117 displayFolder( getInitPath( aUserData, 2 ) );
2119 if ( maCurFilter.isEmpty() )
2121 OUString aFilter = aUserData.getToken( 3, ' ' );
2122 aFilter = DecodeSpaces_Impl( aFilter );
2123 setFilter( aFilter );
2126 // set the member so we know that we have to show the preview
2127 mbShowPreview = bShowPreview;
2129 catch( const IllegalArgumentException& ){}
2132 if ( maPath.isEmpty() )
2133 displayFolder( SvtPathOptions().GetGraphicPath() );
2135 else
2137 SvtViewOptions aViewOpt( EViewType::Dialog, IODLG_CONFIGNAME );
2138 OUString aUserData;
2140 if ( aViewOpt.Exists() )
2142 Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
2143 OUString aTemp;
2144 if ( aUserItem >>= aTemp )
2145 aUserData = aTemp;
2148 if ( aUserData.isEmpty() )
2149 aUserData = STD_CONFIG_STR;
2151 if ( maPath.isEmpty() )
2152 displayFolder( getInitPath( aUserData, 1 ) );
2154 if ( mbHasAutoExt )
2156 sal_Int32 nFlag = aUserData.getToken( 0, ' ' ).toInt32();
2157 aValue <<= static_cast<bool>(nFlag);
2160 xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0, aValue );
2162 catch( const IllegalArgumentException& ){}
2165 if( mbHasSelectionBox )
2167 sal_Int32 nFlag = aUserData.getToken( 2, ' ' ).toInt32();
2168 aValue <<= static_cast<bool>(nFlag);
2171 xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0, aValue );
2173 catch( const IllegalArgumentException& ){}
2176 if ( maPath.isEmpty() )
2177 displayFolder( SvtPathOptions().GetWorkPath() );
2181 void FileDialogHelper_Impl::setDefaultValues()
2183 // when no filter is set, we set the currentFilter to <all>
2184 if ( maCurFilter.isEmpty() && !maSelectFilter.isEmpty() )
2188 mxFileDlg->setCurrentFilter( maSelectFilter );
2190 catch( const IllegalArgumentException& )
2194 // when no path is set, we use the standard 'work' folder
2195 if ( maPath.isEmpty() )
2197 OUString aWorkFolder = SvtPathOptions().GetWorkPath();
2200 mxFileDlg->setDisplayDirectory( aWorkFolder );
2202 catch( const Exception& )
2204 OSL_FAIL( "FileDialogHelper_Impl::setDefaultValues: caught an exception while setting the display directory!" );
2209 bool FileDialogHelper_Impl::isShowFilterExtensionEnabled() const
2211 return !maFilters.empty();
2214 void FileDialogHelper_Impl::addFilterPair( const OUString& rFilter,
2215 const OUString& rFilterWithExtension )
2217 maFilters.emplace_back( rFilter, rFilterWithExtension );
2221 OUString FileDialogHelper_Impl::getFilterName( const OUString& rFilterWithExtension ) const
2223 OUString sRet;
2224 for (auto const& filter : maFilters)
2226 if (filter.Second == rFilterWithExtension)
2228 sRet = filter.First;
2229 break;
2232 return sRet;
2235 OUString FileDialogHelper_Impl::getFilterWithExtension( const OUString& rFilter ) const
2237 OUString sRet;
2238 for (auto const& filter : maFilters)
2240 if ( filter.First == rFilter )
2242 sRet = filter.Second;
2243 break;
2246 return sRet;
2249 void FileDialogHelper_Impl::SetContext( FileDialogHelper::Context _eNewContext )
2251 meContext = _eNewContext;
2253 const std::u16string_view* pConfigId = GetLastFilterConfigId( _eNewContext );
2254 if( pConfigId )
2255 LoadLastUsedFilter( *pConfigId );
2258 // FileDialogHelper
2260 FileDialogHelper::FileDialogHelper(
2261 sal_Int16 nDialogType,
2262 FileDialogFlags nFlags,
2263 const OUString& rFact,
2264 SfxFilterFlags nMust,
2265 SfxFilterFlags nDont,
2266 weld::Window* pPreferredParent)
2267 : m_nError(0),
2268 mpImpl(new FileDialogHelper_Impl(this, nDialogType, nFlags, SFX2_IMPL_DIALOG_CONFIG, pPreferredParent))
2271 // create the list of filters
2272 mpImpl->addFilters(
2273 SfxObjectShell::GetServiceNameFromFactory(rFact), nMust, nDont );
2276 FileDialogHelper::FileDialogHelper(
2277 sal_Int16 nDialogType,
2278 FileDialogFlags nFlags,
2279 const OUString& rFact,
2280 sal_Int16 nDialog,
2281 SfxFilterFlags nMust,
2282 SfxFilterFlags nDont,
2283 const OUString& rStandardDir,
2284 const css::uno::Sequence< OUString >& rDenyList,
2285 weld::Window* pPreferredParent)
2286 : m_nError(0),
2287 mpImpl( new FileDialogHelper_Impl( this, nDialogType, nFlags, nDialog, pPreferredParent, rStandardDir, rDenyList ) )
2289 // create the list of filters
2290 mpImpl->addFilters(
2291 SfxObjectShell::GetServiceNameFromFactory(rFact), nMust, nDont );
2294 FileDialogHelper::FileDialogHelper(sal_Int16 nDialogType, FileDialogFlags nFlags, weld::Window* pPreferredParent)
2295 : m_nError(0),
2296 mpImpl( new FileDialogHelper_Impl( this, nDialogType, nFlags, SFX2_IMPL_DIALOG_CONFIG, pPreferredParent ) )
2300 FileDialogHelper::FileDialogHelper(
2301 sal_Int16 nDialogType,
2302 FileDialogFlags nFlags,
2303 const OUString& aFilterUIName,
2304 const OUString& aExtName,
2305 const OUString& rStandardDir,
2306 const css::uno::Sequence< OUString >& rDenyList,
2307 weld::Window* pPreferredParent )
2308 : m_nError(0),
2309 mpImpl( new FileDialogHelper_Impl( this, nDialogType, nFlags, SFX2_IMPL_DIALOG_CONFIG, pPreferredParent, rStandardDir, rDenyList ) )
2311 // the wildcard here is expected in form "*.extension"
2312 OUString aWildcard;
2313 if ( aExtName.indexOf( '*' ) != 0 )
2315 if ( !aExtName.isEmpty() && aExtName.indexOf( '.' ) != 0 )
2316 aWildcard = "*.";
2317 else
2318 aWildcard = "*";
2321 aWildcard += aExtName;
2323 OUString const aUIString = ::sfx2::addExtension(
2324 aFilterUIName, aWildcard, (OPEN == lcl_OpenOrSave(mpImpl->m_nDialogType)), *mpImpl);
2325 AddFilter( aUIString, aWildcard );
2328 FileDialogHelper::~FileDialogHelper()
2330 mpImpl->dispose();
2333 void FileDialogHelper::CreateMatcher( const OUString& rFactory )
2335 mpImpl->createMatcher( SfxObjectShell::GetServiceNameFromFactory(rFactory) );
2338 void FileDialogHelper::SetControlHelpIds( const sal_Int16* _pControlId, const char** _pHelpId )
2340 mpImpl->setControlHelpIds( _pControlId, _pHelpId );
2343 void FileDialogHelper::SetContext( Context _eNewContext )
2345 mpImpl->SetContext( _eNewContext );
2348 IMPL_LINK_NOARG(FileDialogHelper, ExecuteSystemFilePicker, void*, void)
2350 m_nError = mpImpl->execute();
2351 m_aDialogClosedLink.Call( this );
2354 // rDirPath has to be a directory
2355 ErrCode FileDialogHelper::Execute( std::vector<OUString>& rpURLList,
2356 std::unique_ptr<SfxItemSet>& rpSet,
2357 OUString& rFilter,
2358 const OUString& rDirPath )
2360 SetDisplayFolder( rDirPath );
2361 return mpImpl->execute( rpURLList, rpSet, rFilter );
2365 ErrCode FileDialogHelper::Execute()
2367 return mpImpl->execute();
2370 ErrCode FileDialogHelper::Execute( std::unique_ptr<SfxItemSet>& rpSet,
2371 OUString& rFilter )
2373 ErrCode nRet;
2374 std::vector<OUString> rURLList;
2375 nRet = mpImpl->execute(rURLList, rpSet, rFilter);
2376 return nRet;
2379 void FileDialogHelper::StartExecuteModal( const Link<FileDialogHelper*,void>& rEndDialogHdl )
2381 m_aDialogClosedLink = rEndDialogHdl;
2382 m_nError = ERRCODE_NONE;
2383 if ( mpImpl->isSystemFilePicker() )
2384 Application::PostUserEvent( LINK( this, FileDialogHelper, ExecuteSystemFilePicker ) );
2385 else
2386 mpImpl->implStartExecute();
2389 sal_Int16 FileDialogHelper::GetDialogType() const { return mpImpl ? mpImpl->m_nDialogType : 0; }
2391 bool FileDialogHelper::IsPasswordEnabled() const
2393 return mpImpl && mpImpl->isPasswordEnabled();
2396 OUString FileDialogHelper::GetRealFilter() const
2398 OUString sFilter;
2399 if (mpImpl)
2400 mpImpl->getRealFilter( sFilter );
2401 return sFilter;
2404 void FileDialogHelper::SetTitle( const OUString& rNewTitle )
2406 if ( mpImpl->mxFileDlg.is() )
2407 mpImpl->mxFileDlg->setTitle( rNewTitle );
2410 OUString FileDialogHelper::GetPath() const
2412 OUString aPath;
2414 if ( !mpImpl->mlLastURLs.empty())
2415 return mpImpl->mlLastURLs[0];
2417 if ( mpImpl->mxFileDlg.is() )
2419 Sequence < OUString > aPathSeq = mpImpl->mxFileDlg->getFiles();
2421 if ( aPathSeq.getLength() == 1 )
2423 aPath = aPathSeq[0];
2427 return aPath;
2430 Sequence < OUString > FileDialogHelper::GetMPath() const
2432 if ( !mpImpl->mlLastURLs.empty())
2433 return comphelper::containerToSequence(mpImpl->mlLastURLs);
2435 if ( mpImpl->mxFileDlg.is() )
2436 return mpImpl->mxFileDlg->getFiles();
2437 else
2439 Sequence < OUString > aEmpty;
2440 return aEmpty;
2444 Sequence< OUString > FileDialogHelper::GetSelectedFiles() const
2446 // a) the new way (optional!)
2447 uno::Sequence< OUString > aResultSeq;
2448 if (mpImpl->mxFileDlg.is())
2450 aResultSeq = mpImpl->mxFileDlg->getSelectedFiles();
2452 // b) the olde way ... non optional.
2453 else
2455 uno::Reference< XFilePicker > xPickOld(mpImpl->mxFileDlg, UNO_QUERY_THROW);
2456 Sequence< OUString > lFiles = xPickOld->getFiles();
2457 ::sal_Int32 nFiles = lFiles.getLength();
2458 if ( nFiles > 1 )
2460 aResultSeq = Sequence< OUString >( nFiles-1 );
2462 INetURLObject aPath( lFiles[0] );
2463 aPath.setFinalSlash();
2465 for (::sal_Int32 i = 1; i < nFiles; i++)
2467 if (i == 1)
2468 aPath.Append( lFiles[i] );
2469 else
2470 aPath.setName( lFiles[i] );
2472 aResultSeq[i-1] = aPath.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2475 else
2476 aResultSeq = lFiles;
2479 return aResultSeq;
2482 OUString FileDialogHelper::GetDisplayDirectory() const
2484 return mpImpl->getPath();
2487 OUString FileDialogHelper::GetCurrentFilter() const
2489 return mpImpl->getFilter();
2492 ErrCode FileDialogHelper::GetGraphic( Graphic& rGraphic ) const
2494 return mpImpl->getGraphic( rGraphic );
2497 static int impl_isFolder( const OUString& rPath )
2501 ::ucbhelper::Content aContent(
2502 rPath, uno::Reference< ucb::XCommandEnvironment > (),
2503 comphelper::getProcessComponentContext() );
2504 if ( aContent.isFolder() )
2505 return 1;
2507 return 0;
2509 catch ( const Exception & )
2513 return -1;
2516 void FileDialogHelper::SetDisplayDirectory( const OUString& _rPath )
2518 if ( _rPath.isEmpty() )
2519 return;
2521 // if the given path isn't a folder, we cut off the last part
2522 // and take it as filename and the rest of the path should be
2523 // the folder
2525 INetURLObject aObj( _rPath );
2527 OUString sFileName = aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
2528 aObj.removeSegment();
2529 OUString sPath = aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2531 int nIsFolder = impl_isFolder( _rPath );
2532 if ( nIsFolder == 0 ||
2533 ( nIsFolder == -1 && impl_isFolder( sPath ) == 1 ) )
2535 mpImpl->setFileName( sFileName );
2536 mpImpl->displayFolder( sPath );
2538 else
2540 INetURLObject aObjPathName( _rPath );
2541 OUString sFolder( aObjPathName.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2542 if ( sFolder.isEmpty() )
2544 // _rPath is not a valid path -> fallback to home directory
2545 osl::Security aSecurity;
2546 aSecurity.getHomeDir( sFolder );
2548 mpImpl->displayFolder( sFolder );
2552 void FileDialogHelper::SetDisplayFolder( const OUString& _rURL )
2554 mpImpl->displayFolder( _rURL );
2557 void FileDialogHelper::SetFileName( const OUString& _rFileName )
2559 mpImpl->setFileName( _rFileName );
2562 void FileDialogHelper::AddFilter( const OUString& rFilterName,
2563 const OUString& rExtension )
2565 mpImpl->addFilter( rFilterName, rExtension );
2568 void FileDialogHelper::SetCurrentFilter( const OUString& rFilter )
2570 OUString sFilter( rFilter );
2571 if ( mpImpl->isShowFilterExtensionEnabled() )
2572 sFilter = mpImpl->getFilterWithExtension( rFilter );
2573 mpImpl->setFilter( sFilter );
2576 const uno::Reference < XFilePicker3 >& FileDialogHelper::GetFilePicker() const
2578 return mpImpl->mxFileDlg;
2581 // XFilePickerListener Methods
2582 void FileDialogHelper::FileSelectionChanged()
2584 mpImpl->handleFileSelectionChanged();
2587 void FileDialogHelper::DirectoryChanged()
2589 mpImpl->handleDirectoryChanged();
2592 OUString FileDialogHelper::HelpRequested( const FilePickerEvent& aEvent )
2594 return sfx2::FileDialogHelper_Impl::handleHelpRequested( aEvent );
2597 void FileDialogHelper::ControlStateChanged( const FilePickerEvent& aEvent )
2599 mpImpl->handleControlStateChanged( aEvent );
2602 void FileDialogHelper::DialogSizeChanged()
2604 mpImpl->handleDialogSizeChanged();
2607 void FileDialogHelper::DialogClosed( const DialogClosedEvent& _rEvent )
2609 m_nError = ( RET_OK == _rEvent.DialogResult ) ? ERRCODE_NONE : ERRCODE_ABORT;
2610 m_aDialogClosedLink.Call( this );
2613 ErrCode FileOpenDialog_Impl( weld::Window* pParent,
2614 sal_Int16 nDialogType,
2615 FileDialogFlags nFlags,
2616 std::vector<OUString>& rpURLList,
2617 OUString& rFilter,
2618 std::unique_ptr<SfxItemSet>& rpSet,
2619 const OUString* pPath,
2620 sal_Int16 nDialog,
2621 const OUString& rStandardDir,
2622 const css::uno::Sequence< OUString >& rDenyList )
2624 ErrCode nRet;
2625 std::unique_ptr<FileDialogHelper> pDialog;
2626 // Sign existing PDF: only works with PDF files and they are opened
2627 // read-only to discourage editing (which would invalidate existing
2628 // signatures).
2629 if (nFlags & FileDialogFlags::SignPDF)
2630 pDialog.reset(new FileDialogHelper(nDialogType, nFlags, SfxResId(STR_SFX_FILTERNAME_PDF), "pdf", rStandardDir, rDenyList, pParent));
2631 else
2632 pDialog.reset(new FileDialogHelper(nDialogType, nFlags, OUString(), nDialog, SfxFilterFlags::NONE, SfxFilterFlags::NONE, rStandardDir, rDenyList, pParent));
2634 OUString aPath;
2635 if ( pPath )
2636 aPath = *pPath;
2638 nRet = pDialog->Execute(rpURLList, rpSet, rFilter, aPath);
2639 DBG_ASSERT( rFilter.indexOf(": ") == -1, "Old filter name used!");
2641 if (rpSet && nFlags & FileDialogFlags::SignPDF)
2642 rpSet->Put(SfxBoolItem(SID_DOC_READONLY, true));
2643 return nRet;
2646 ErrCode RequestPassword(const std::shared_ptr<const SfxFilter>& pCurrentFilter, OUString const & aURL, SfxItemSet* pSet, const css::uno::Reference<css::awt::XWindow>& rParent)
2648 uno::Reference<task::XInteractionHandler2> xInteractionHandler = task::InteractionHandler::createWithParent(::comphelper::getProcessComponentContext(), rParent);
2649 // TODO: need a save way to distinguish MS filters from other filters
2650 // for now MS-filters are the only alien filters that support encryption
2651 const bool bMSType = !pCurrentFilter->IsOwnFormat();
2652 // For OOXML we can use the standard password ("unlimited" characters)
2653 // request, otherwise the MS limited password request is needed.
2654 const bool bOOXML = bMSType && lclSupportsOOXMLEncryption( pCurrentFilter->GetFilterName());
2655 const ::comphelper::DocPasswordRequestType eType = bMSType && !bOOXML ?
2656 ::comphelper::DocPasswordRequestType::MS :
2657 ::comphelper::DocPasswordRequestType::Standard;
2659 ::rtl::Reference< ::comphelper::DocPasswordRequest > pPasswordRequest( new ::comphelper::DocPasswordRequest( eType, css::task::PasswordRequestMode_PASSWORD_CREATE, aURL, bool( pCurrentFilter->GetFilterFlags() & SfxFilterFlags::PASSWORDTOMODIFY ) ) );
2661 uno::Reference< css::task::XInteractionRequest > rRequest( pPasswordRequest.get() );
2664 xInteractionHandler->handle( rRequest );
2665 if (!pPasswordRequest->isPassword() || bMSType)
2667 break;
2669 OString const utf8Pwd(OUStringToOString(pPasswordRequest->getPassword(), RTL_TEXTENCODING_UTF8));
2670 OString const utf8Ptm(OUStringToOString(pPasswordRequest->getPasswordToModify(), RTL_TEXTENCODING_UTF8));
2671 if (!(52 <= utf8Pwd.getLength() && utf8Pwd.getLength() <= 55
2672 && SvtSaveOptions().GetODFSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012)
2673 && (52 > utf8Ptm.getLength() || utf8Ptm.getLength() > 55))
2675 break;
2677 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(Application::GetFrameWeld(rParent), VclMessageType::Warning,
2678 VclButtonsType::Ok, SfxResId(STR_PASSWORD_LEN)));
2679 xBox->set_secondary_text(SfxResId(STR_PASSWORD_WARNING));
2680 xBox->run();
2682 while (true);
2683 if ( pPasswordRequest->isPassword() )
2685 if ( pPasswordRequest->getPassword().getLength() )
2687 css::uno::Sequence< css::beans::NamedValue > aEncryptionData;
2689 // TODO/LATER: The filters should show the password dialog themself in future
2690 if ( bMSType )
2692 if (bOOXML)
2694 ::comphelper::SequenceAsHashMap aHashData;
2695 aHashData[ OUString( "OOXPassword" ) ] <<= pPasswordRequest->getPassword();
2696 aHashData[ OUString( "CryptoType" ) ] <<= OUString( "Standard" );
2697 aEncryptionData = aHashData.getAsConstNamedValueList();
2699 else
2701 uno::Sequence< sal_Int8 > aUniqueID = ::comphelper::DocPasswordHelper::GenerateRandomByteSequence( 16 );
2702 uno::Sequence< sal_Int8 > aEncryptionKey = ::comphelper::DocPasswordHelper::GenerateStd97Key( pPasswordRequest->getPassword(), aUniqueID );
2704 if ( aEncryptionKey.hasElements() )
2706 ::comphelper::SequenceAsHashMap aHashData;
2707 aHashData[ OUString( "STD97EncryptionKey" ) ] <<= aEncryptionKey;
2708 aHashData[ OUString( "STD97UniqueID" ) ] <<= aUniqueID;
2710 aEncryptionData = aHashData.getAsConstNamedValueList();
2712 else
2714 return ERRCODE_IO_NOTSUPPORTED;
2719 // tdf#118639: We need ODF encryption data for autorecovery where password will already
2720 // be unavailable, even for non-ODF documents, so append it here unconditionally
2721 pSet->Put(SfxUnoAnyItem(
2722 SID_ENCRYPTIONDATA,
2723 uno::makeAny(comphelper::concatSequences(
2724 aEncryptionData, comphelper::OStorageHelper::CreatePackageEncryptionData(
2725 pPasswordRequest->getPassword())))));
2728 if ( pPasswordRequest->getRecommendReadOnly() )
2729 pSet->Put( SfxBoolItem( SID_RECOMMENDREADONLY, true ) );
2731 if ( bMSType )
2733 // the empty password has 0 as Hash
2734 sal_Int32 nHash = SfxMedium::CreatePasswordToModifyHash( pPasswordRequest->getPasswordToModify(),
2735 pCurrentFilter->GetServiceName() == "com.sun.star.text.TextDocument" );
2736 if ( nHash )
2737 pSet->Put( SfxUnoAnyItem( SID_MODIFYPASSWORDINFO, uno::makeAny( nHash ) ) );
2739 else
2741 uno::Sequence< beans::PropertyValue > aModifyPasswordInfo = ::comphelper::DocPasswordHelper::GenerateNewModifyPasswordInfo( pPasswordRequest->getPasswordToModify() );
2742 if ( aModifyPasswordInfo.hasElements() )
2743 pSet->Put( SfxUnoAnyItem( SID_MODIFYPASSWORDINFO, uno::makeAny( aModifyPasswordInfo ) ) );
2746 else
2747 return ERRCODE_ABORT;
2748 return ERRCODE_NONE;
2751 OUString EncodeSpaces_Impl( const OUString& rSource )
2753 OUString sRet = rSource.replaceAll( " ", "%20" );
2754 return sRet;
2757 OUString DecodeSpaces_Impl( const OUString& rSource )
2759 OUString sRet = rSource.replaceAll( "%20", " " );
2760 return sRet;
2763 } // end of namespace sfx2
2765 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */