1 /* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <config_features.h>
22 #include <sal/config.h>
23 #include <sal/log.hxx>
25 #include <com/sun/star/lang/DisposedException.hpp>
26 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
27 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
28 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
29 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
30 #include <cppuhelper/interfacecontainer.h>
31 #include <cppuhelper/supportsservice.hxx>
32 #include <osl/diagnose.h>
33 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
34 #include <com/sun/star/ui/dialogs/ControlActions.hpp>
35 #include <com/sun/star/uno/Any.hxx>
36 #include <osl/mutex.hxx>
37 #include <vcl/svapp.hxx>
39 #include "resourceprovider.hxx"
41 #include <osl/file.hxx>
42 #include "NSString_OOoAdditions.hxx"
43 #include "NSURL_OOoAdditions.hxx"
47 #include "SalAquaFilePicker.hxx"
49 #include <objc/objc-runtime.h>
53 using namespace ::com::sun::star;
54 using namespace ::com::sun::star::ui::dialogs;
55 using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
56 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
57 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
58 using namespace ::com::sun::star::lang;
59 using namespace ::com::sun::star::beans;
60 using namespace ::com::sun::star::uno;
64 uno::Sequence<OUString> FilePicker_getSupportedServiceNames()
66 return { "com.sun.star.ui.dialogs.FilePicker",
67 "com.sun.star.ui.dialogs.SystemFilePicker",
68 "com.sun.star.ui.dialogs.AquaFilePicker" };
72 #pragma mark Constructor
74 SalAquaFilePicker::SalAquaFilePicker()
75 : SalAquaFilePicker_Base( m_rbHelperMtx )
76 , m_pFilterHelper( nullptr )
78 m_pDelegate = [[AquaFilePickerDelegate alloc] initWithFilePicker:this];
79 m_pControlHelper->setFilePickerDelegate(m_pDelegate);
82 SalAquaFilePicker::~SalAquaFilePicker()
84 if (nullptr != m_pFilterHelper)
85 delete m_pFilterHelper;
87 [m_pDelegate release];
91 #pragma mark XFilePickerNotifier
93 void SAL_CALL SalAquaFilePicker::addFilePickerListener( const uno::Reference<XFilePickerListener>& xListener )
95 SolarMutexGuard aGuard;
96 m_xListener = xListener;
99 void SAL_CALL SalAquaFilePicker::removeFilePickerListener( const uno::Reference<XFilePickerListener>& )
101 SolarMutexGuard aGuard;
105 #pragma mark XAsynchronousExecutableDialog
107 void SAL_CALL SalAquaFilePicker::setTitle( const OUString& aTitle )
109 SolarMutexGuard aGuard;
110 implsetTitle(aTitle);
113 sal_Int16 SAL_CALL SalAquaFilePicker::execute()
115 SolarMutexGuard aGuard;
117 sal_Int16 retVal = 0;
121 // if m_pDialog is nil after initialization, something must have gone wrong before
122 // or there was no initialization (see issue https://bz.apache.org/ooo/show_bug.cgi?id=100214)
123 if (m_pDialog == nil) {
124 m_nDialogType = NAVIGATIONSERVICES_OPEN;
127 if (m_pFilterHelper) {
128 m_pFilterHelper->SetFilters();
131 if (m_nDialogType == NAVIGATIONSERVICES_SAVE) {
132 if (m_sSaveFileName.getLength() == 0) {
133 //if no filename is set, NavigationServices will set the name to "untitled". We don't want this!
134 //So let's try to get the window title to get the real untitled name
135 NSWindow *frontWindow = [NSApp keyWindow];
136 if (nullptr != frontWindow) {
137 NSString *windowTitle = [frontWindow title];
138 if (windowTitle != nil) {
139 OUString ouName = [windowTitle OUString];
140 //a window title will typically be something like "Untitled1 - OpenOffice.org Writer"
141 //but we only want the "Untitled1" part of it
142 sal_Int32 indexOfDash = ouName.indexOf(" - ");
143 if (indexOfDash > -1) {
144 m_sSaveFileName = ouName.copy(0,indexOfDash);
145 if (m_sSaveFileName.getLength() > 0) {
146 setDefaultName(m_sSaveFileName);
154 //Set the delegate to be notified of certain events
156 [m_pDialog setDelegate:m_pDelegate];
158 int nStatus = runandwaitforresult();
160 [m_pDialog setDelegate:nil];
164 case NSModalResponseOK:
165 retVal = ExecutableDialogResults::OK;
168 case NSModalResponseCancel:
169 retVal = ExecutableDialogResults::CANCEL;
173 throw uno::RuntimeException(
174 "The dialog returned with an unknown result!",
175 static_cast<XFilePicker*>( static_cast<XFilePicker3*>( this ) ));
183 #pragma mark XFilePicker
185 void SAL_CALL SalAquaFilePicker::setMultiSelectionMode( sal_Bool /* bMode */ )
187 SolarMutexGuard aGuard;
189 if (m_nDialogType == NAVIGATIONSERVICES_OPEN) {
190 [static_cast<NSOpenPanel*>(m_pDialog) setAllowsMultipleSelection:YES];
194 void SAL_CALL SalAquaFilePicker::setDefaultName( const OUString& aName )
196 SolarMutexGuard aGuard;
198 m_sSaveFileName = aName;
201 void SAL_CALL SalAquaFilePicker::setDisplayDirectory( const OUString& rDirectory )
203 SolarMutexGuard aGuard;
205 implsetDisplayDirectory(rDirectory);
208 OUString SAL_CALL SalAquaFilePicker::getDisplayDirectory()
210 OUString retVal = implgetDisplayDirectory();
215 uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getFiles()
217 uno::Sequence< OUString > aSelectedFiles = getSelectedFiles();
218 // multiselection doesn't really work with getFiles
219 // so just retrieve the first url
220 if (aSelectedFiles.getLength() > 1)
221 aSelectedFiles.realloc(1);
223 return aSelectedFiles;
226 uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getSelectedFiles()
228 SolarMutexGuard aGuard;
230 #if HAVE_FEATURE_MACOSX_SANDBOX
231 static NSUserDefaults *userDefaults;
232 static bool triedUserDefaults = false;
234 if (!triedUserDefaults)
236 userDefaults = [NSUserDefaults standardUserDefaults];
237 triedUserDefaults = true;
241 NSArray *files = nil;
242 if (m_nDialogType == NAVIGATIONSERVICES_OPEN) {
243 files = [static_cast<NSOpenPanel*>(m_pDialog) URLs];
245 else if (m_nDialogType == NAVIGATIONSERVICES_SAVE) {
246 files = [NSArray arrayWithObjects:[m_pDialog URL], nil];
249 NSUInteger nFiles = [files count];
250 SAL_INFO("fpicker.aqua", "# of items: " << nFiles);
252 uno::Sequence< OUString > aSelectedFiles(nFiles);
253 OUString* pSelectedFiles = aSelectedFiles.getArray();
255 for(NSUInteger nIndex = 0; nIndex < nFiles; nIndex += 1)
257 NSURL *url = [files objectAtIndex:nIndex];
259 #if HAVE_FEATURE_MACOSX_SANDBOX
260 if (userDefaults != NULL &&
261 [url respondsToSelector:@selector(bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:)])
263 // In the case of "Save As" when the user has input a new
264 // file name, this call will return nil, as bookmarks can
265 // (naturally) only be created for existing file system
266 // objects. In that case, code at a much lower level, in
267 // sal, takes care of creating a bookmark when a new file
268 // has been created outside the sandbox.
269 NSData *data = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
270 includingResourceValuesForKeys:nil
275 [userDefaults setObject:data
276 forKey:[@"bookmarkFor:" stringByAppendingString:[url absoluteString]]];
281 OUString sFileOrDirURL = [url OUString];
283 pSelectedFiles[nIndex] = sFileOrDirURL;
286 return aSelectedFiles;
289 #pragma mark XFilterManager
291 void SAL_CALL SalAquaFilePicker::appendFilter( const OUString& aTitle, const OUString& aFilter )
293 SolarMutexGuard aGuard;
295 ensureFilterHelper();
296 m_pFilterHelper->appendFilter( aTitle, aFilter );
297 m_pControlHelper->setFilterControlNeeded(true);
300 void SAL_CALL SalAquaFilePicker::setCurrentFilter( const OUString& aTitle )
302 SolarMutexGuard aGuard;
304 ensureFilterHelper();
305 m_pFilterHelper->setCurrentFilter(aTitle);
308 updateSaveFileNameExtension();
311 OUString SAL_CALL SalAquaFilePicker::getCurrentFilter()
313 SolarMutexGuard aGuard;
315 ensureFilterHelper();
317 return m_pFilterHelper->getCurrentFilter();
320 #pragma mark XFilterGroupManager
322 void SAL_CALL SalAquaFilePicker::appendFilterGroup( const OUString&, const uno::Sequence<beans::StringPair>& aFilters )
324 SolarMutexGuard aGuard;
326 ensureFilterHelper();
327 m_pFilterHelper->appendFilterGroup(aFilters);
328 m_pControlHelper->setFilterControlNeeded(true);
331 #pragma mark XFilePickerControlAccess
333 void SAL_CALL SalAquaFilePicker::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
335 SolarMutexGuard aGuard;
337 m_pControlHelper->setValue(nControlId, nControlAction, rValue);
339 if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION && m_nDialogType == NAVIGATIONSERVICES_SAVE) {
340 updateSaveFileNameExtension();
344 uno::Any SAL_CALL SalAquaFilePicker::getValue( sal_Int16 nControlId, sal_Int16 nControlAction )
346 uno::Any aValue = m_pControlHelper->getValue(nControlId, nControlAction);
351 void SAL_CALL SalAquaFilePicker::enableControl( sal_Int16 nControlId, sal_Bool bEnable )
353 m_pControlHelper->enableControl(nControlId, bEnable);
356 void SAL_CALL SalAquaFilePicker::setLabel( sal_Int16 nControlId, const OUString& aLabel )
358 SolarMutexGuard aGuard;
360 NSString* sLabel = [NSString stringWithOUString:aLabel];
361 m_pControlHelper->setLabel( nControlId, sLabel ) ;
364 OUString SAL_CALL SalAquaFilePicker::getLabel( sal_Int16 nControlId )
366 return m_pControlHelper->getLabel(nControlId);
369 #pragma mark XInitialization
371 void SAL_CALL SalAquaFilePicker::initialize( const uno::Sequence<uno::Any>& aArguments )
373 SolarMutexGuard aGuard;
375 // parameter checking
377 if( 0 == aArguments.getLength() )
378 throw lang::IllegalArgumentException("no arguments",
379 static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ), 1 );
381 aAny = aArguments[0];
383 if( ( aAny.getValueType() != ::cppu::UnoType<sal_Int16>::get() ) &&
384 (aAny.getValueType() != ::cppu::UnoType<sal_Int8>::get() ) )
385 throw lang::IllegalArgumentException("invalid argument type",
386 static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ), 1 );
388 sal_Int16 templateId = -1;
393 case FILEOPEN_SIMPLE:
394 m_nDialogType = NAVIGATIONSERVICES_OPEN;
396 case FILESAVE_SIMPLE:
397 m_nDialogType = NAVIGATIONSERVICES_SAVE;
399 case FILESAVE_AUTOEXTENSION_PASSWORD:
400 m_nDialogType = NAVIGATIONSERVICES_SAVE;
402 case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
403 m_nDialogType = NAVIGATIONSERVICES_SAVE;
405 case FILESAVE_AUTOEXTENSION_SELECTION:
406 m_nDialogType = NAVIGATIONSERVICES_SAVE;
408 case FILESAVE_AUTOEXTENSION_TEMPLATE:
409 m_nDialogType = NAVIGATIONSERVICES_SAVE;
411 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
412 m_nDialogType = NAVIGATIONSERVICES_OPEN;
414 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
415 m_nDialogType = NAVIGATIONSERVICES_OPEN;
418 m_nDialogType = NAVIGATIONSERVICES_OPEN;
420 case FILEOPEN_LINK_PLAY:
421 m_nDialogType = NAVIGATIONSERVICES_OPEN;
423 case FILEOPEN_READONLY_VERSION:
424 m_nDialogType = NAVIGATIONSERVICES_OPEN;
426 case FILEOPEN_LINK_PREVIEW:
427 m_nDialogType = NAVIGATIONSERVICES_OPEN;
429 case FILESAVE_AUTOEXTENSION:
430 m_nDialogType = NAVIGATIONSERVICES_SAVE;
432 case FILEOPEN_PREVIEW:
433 m_nDialogType = NAVIGATIONSERVICES_OPEN;
436 throw lang::IllegalArgumentException("Unknown template",
437 static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ),
441 m_pControlHelper->initialize(templateId);
446 #pragma mark XCancellable
448 void SAL_CALL SalAquaFilePicker::cancel()
450 SolarMutexGuard aGuard;
452 if (m_pDialog != nil) {
453 [m_pDialog cancel:nil];
457 #pragma mark XEventListener
459 void SalAquaFilePicker::disposing( const lang::EventObject& aEvent )
461 SolarMutexGuard aGuard;
463 uno::Reference<XFilePickerListener> xFilePickerListener( aEvent.Source, css::uno::UNO_QUERY );
465 if( xFilePickerListener.is() )
466 removeFilePickerListener( xFilePickerListener );
469 #pragma mark XServiceInfo
471 OUString SAL_CALL SalAquaFilePicker::getImplementationName()
473 return "com.sun.star.ui.dialogs.SalAquaFilePicker";
476 sal_Bool SAL_CALL SalAquaFilePicker::supportsService( const OUString& sServiceName )
478 return cppu::supportsService(this, sServiceName);
481 uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getSupportedServiceNames()
483 return FilePicker_getSupportedServiceNames();
486 #pragma mark Misc/Private
488 void SalAquaFilePicker::fileSelectionChanged( FilePickerEvent aEvent )
490 if (m_xListener.is())
491 m_xListener->fileSelectionChanged( aEvent );
494 void SalAquaFilePicker::directoryChanged( FilePickerEvent aEvent )
496 if (m_xListener.is())
497 m_xListener->directoryChanged( aEvent );
500 void SalAquaFilePicker::controlStateChanged( FilePickerEvent aEvent )
502 if (m_xListener.is())
503 m_xListener->controlStateChanged( aEvent );
506 void SalAquaFilePicker::dialogSizeChanged()
508 if (m_xListener.is())
509 m_xListener->dialogSizeChanged();
515 void SalAquaFilePicker::ensureFilterHelper()
517 SolarMutexGuard aGuard;
519 if (nullptr == m_pFilterHelper) {
520 m_pFilterHelper = new FilterHelper;
521 m_pControlHelper->setFilterHelper(m_pFilterHelper);
522 [m_pDelegate setFilterHelper:m_pFilterHelper];
526 void SalAquaFilePicker::updateFilterUI()
528 m_pControlHelper->updateFilterUI();
531 void SalAquaFilePicker::updateSaveFileNameExtension()
533 if (m_nDialogType != NAVIGATIONSERVICES_SAVE) {
537 // we need to set this here again because initial setting does
538 //[m_pDialog setExtensionHidden:YES];
540 SolarMutexGuard aGuard;
542 if (!m_pControlHelper->isAutoExtensionEnabled()) {
543 SAL_WNODEPRECATED_DECLARATIONS_PUSH // setAllowedFileTypes (12.0)
544 [m_pDialog setAllowedFileTypes:nil];
545 SAL_WNODEPRECATED_DECLARATIONS_POP
546 [m_pDialog setAllowsOtherFileTypes:YES];
548 ensureFilterHelper();
550 OUStringList aStringList = m_pFilterHelper->getCurrentFilterSuffixList();
551 if( aStringList.empty()) // #i9328#
554 OUString suffix = (*(aStringList.begin())).copy(1);
555 NSString *requiredFileType = [NSString stringWithOUString:suffix];
557 SAL_WNODEPRECATED_DECLARATIONS_PUSH // setAllowedFileTypes (12.0)
558 [m_pDialog setAllowedFileTypes:[NSArray arrayWithObjects:requiredFileType, nil]];
559 SAL_WNODEPRECATED_DECLARATIONS_POP
561 [m_pDialog setAllowsOtherFileTypes:NO];
565 void SalAquaFilePicker::filterControlChanged()
567 if (m_pDialog == nil) {
571 SolarMutexGuard aGuard;
573 updateSaveFileNameExtension();
575 [m_pDialog validateVisibleColumns];
578 evt.ElementId = LISTBOX_FILTER;
579 controlStateChanged( evt );
582 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
583 fpicker_SalAquaFilePicker_get_implementation(
584 css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
586 return cppu::acquire(new SalAquaFilePicker());
590 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */