LibreOffice Module fpicker (master) 1
SalAquaFilePicker.mm
Go to the documentation of this file.
1/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <config_features.h>
21
22#include <sal/config.h>
23#include <sal/log.hxx>
24
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>
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>
38
39#include "resourceprovider.hxx"
40
41#include <osl/file.hxx>
44
45#include <iostream>
46
47#include "SalAquaFilePicker.hxx"
48
49#include <objc/objc-runtime.h>
50
51#pragma mark DEFINES
52
53using namespace ::com::sun::star;
54using namespace ::com::sun::star::ui::dialogs;
55using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
56using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
57using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
58using namespace ::com::sun::star::lang;
59using namespace ::com::sun::star::beans;
60using namespace ::com::sun::star::uno;
61
62namespace
63{
64 uno::Sequence<OUString> FilePicker_getSupportedServiceNames()
65 {
66 return { "com.sun.star.ui.dialogs.FilePicker",
67 "com.sun.star.ui.dialogs.SystemFilePicker",
68 "com.sun.star.ui.dialogs.AquaFilePicker" };
69 }
70}
71
72#pragma mark Constructor
73
75 : SalAquaFilePicker_Base( m_rbHelperMtx )
76 , m_pFilterHelper( nullptr )
77{
78 m_pDelegate = [[AquaFilePickerDelegate alloc] initWithFilePicker:this];
80}
81
83{
84 if (nullptr != m_pFilterHelper)
85 delete m_pFilterHelper;
86
87 [m_pDelegate release];
88}
89
90
91#pragma mark XFilePickerNotifier
92
93void SAL_CALL SalAquaFilePicker::addFilePickerListener( const uno::Reference<XFilePickerListener>& xListener )
94{
95 SolarMutexGuard aGuard;
96 m_xListener = xListener;
97}
98
99void SAL_CALL SalAquaFilePicker::removeFilePickerListener( const uno::Reference<XFilePickerListener>& )
100{
101 SolarMutexGuard aGuard;
102 m_xListener.clear();
103}
104
105#pragma mark XAsynchronousExecutableDialog
106
107void SAL_CALL SalAquaFilePicker::setTitle( const OUString& aTitle )
108{
109 SolarMutexGuard aGuard;
110 implsetTitle(aTitle);
111}
112
113sal_Int16 SAL_CALL SalAquaFilePicker::execute()
114{
115 SolarMutexGuard aGuard;
116
117 sal_Int16 retVal = 0;
118
120
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) {
125 }
126
127 if (m_pFilterHelper) {
129 }
130
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) {
147 }
148 }
149 }
150 }
151 }
152 }
153
154 //Set the delegate to be notified of certain events
155
156 [m_pDialog setDelegate:m_pDelegate];
157
158 int nStatus = runandwaitforresult();
159
160 [m_pDialog setDelegate:nil];
161
162 switch( nStatus )
163 {
164 case NSModalResponseOK:
165 retVal = ExecutableDialogResults::OK;
166 break;
167
168 case NSModalResponseCancel:
169 retVal = ExecutableDialogResults::CANCEL;
170 break;
171
172 default:
173 throw uno::RuntimeException(
174 "The dialog returned with an unknown result!",
175 static_cast<XFilePicker*>( static_cast<XFilePicker3*>( this ) ));
176 break;
177 }
178
179 return retVal;
180}
181
182
183#pragma mark XFilePicker
184
186{
187 SolarMutexGuard aGuard;
188
190 [static_cast<NSOpenPanel*>(m_pDialog) setAllowsMultipleSelection:YES];
191 }
192}
193
194void SAL_CALL SalAquaFilePicker::setDefaultName( const OUString& aName )
195{
196 SolarMutexGuard aGuard;
197
199}
200
201void SAL_CALL SalAquaFilePicker::setDisplayDirectory( const OUString& rDirectory )
202{
203 SolarMutexGuard aGuard;
204
205 implsetDisplayDirectory(rDirectory);
206}
207
209{
210 OUString retVal = implgetDisplayDirectory();
211
212 return retVal;
213}
214
215uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getFiles()
216{
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);
222
223 return aSelectedFiles;
224}
225
226uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getSelectedFiles()
227{
228 SolarMutexGuard aGuard;
229
230#if HAVE_FEATURE_MACOSX_SANDBOX
231 static NSUserDefaults *userDefaults;
232 static bool triedUserDefaults = false;
233
234 if (!triedUserDefaults)
235 {
236 userDefaults = [NSUserDefaults standardUserDefaults];
237 triedUserDefaults = true;
238 }
239#endif
240
241 NSArray *files = nil;
243 files = [static_cast<NSOpenPanel*>(m_pDialog) URLs];
244 }
246 files = [NSArray arrayWithObjects:[m_pDialog URL], nil];
247 }
248
249 NSUInteger nFiles = [files count];
250 SAL_INFO("fpicker.aqua", "# of items: " << nFiles);
251
252 uno::Sequence< OUString > aSelectedFiles(nFiles);
253 OUString* pSelectedFiles = aSelectedFiles.getArray();
254
255 for(NSUInteger nIndex = 0; nIndex < nFiles; nIndex += 1)
256 {
257 NSURL *url = [files objectAtIndex:nIndex];
258
259#if HAVE_FEATURE_MACOSX_SANDBOX
260 if (userDefaults != NULL &&
261 [url respondsToSelector:@selector(bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:)])
262 {
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
271 relativeToURL:nil
272 error:nil];
273 if (data != NULL)
274 {
275 [userDefaults setObject:data
276 forKey:[@"bookmarkFor:" stringByAppendingString:[url absoluteString]]];
277 }
278 }
279#endif
280
281 OUString sFileOrDirURL = [url OUString];
282
283 pSelectedFiles[nIndex] = sFileOrDirURL;
284 }
285
286 return aSelectedFiles;
287}
288
289#pragma mark XFilterManager
290
291void SAL_CALL SalAquaFilePicker::appendFilter( const OUString& aTitle, const OUString& aFilter )
292{
293 SolarMutexGuard aGuard;
294
296 m_pFilterHelper->appendFilter( aTitle, aFilter );
298}
299
300void SAL_CALL SalAquaFilePicker::setCurrentFilter( const OUString& aTitle )
301{
302 SolarMutexGuard aGuard;
303
307
309}
310
312{
313 SolarMutexGuard aGuard;
314
316
318}
319
320#pragma mark XFilterGroupManager
321
322void SAL_CALL SalAquaFilePicker::appendFilterGroup( const OUString&, const uno::Sequence<beans::StringPair>& aFilters )
323{
324 SolarMutexGuard aGuard;
325
329}
330
331#pragma mark XFilePickerControlAccess
332
333void SAL_CALL SalAquaFilePicker::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
334{
335 SolarMutexGuard aGuard;
336
337 m_pControlHelper->setValue(nControlId, nControlAction, rValue);
338
339 if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION && m_nDialogType == NAVIGATIONSERVICES_SAVE) {
341 }
342}
343
344uno::Any SAL_CALL SalAquaFilePicker::getValue( sal_Int16 nControlId, sal_Int16 nControlAction )
345{
346 uno::Any aValue = m_pControlHelper->getValue(nControlId, nControlAction);
347
348 return aValue;
349}
350
351void SAL_CALL SalAquaFilePicker::enableControl( sal_Int16 nControlId, sal_Bool bEnable )
352{
354}
355
356void SAL_CALL SalAquaFilePicker::setLabel( sal_Int16 nControlId, const OUString& aLabel )
357{
358 SolarMutexGuard aGuard;
359
360 NSString* sLabel = [NSString stringWithOUString:aLabel];
362}
363
364OUString SAL_CALL SalAquaFilePicker::getLabel( sal_Int16 nControlId )
365{
367}
368
369#pragma mark XInitialization
370
371void SAL_CALL SalAquaFilePicker::initialize( const uno::Sequence<uno::Any>& aArguments )
372{
373 SolarMutexGuard aGuard;
374
375 // parameter checking
376 uno::Any aAny;
377 if( 0 == aArguments.getLength() )
378 throw lang::IllegalArgumentException("no arguments",
379 static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ), 1 );
380
381 aAny = aArguments[0];
382
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 );
387
388 sal_Int16 templateId = -1;
389 aAny >>= templateId;
390
391 switch( templateId )
392 {
393 case FILEOPEN_SIMPLE:
395 break;
396 case FILESAVE_SIMPLE:
398 break;
399 case FILESAVE_AUTOEXTENSION_PASSWORD:
401 break;
402 case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
404 break;
405 case FILESAVE_AUTOEXTENSION_SELECTION:
407 break;
408 case FILESAVE_AUTOEXTENSION_TEMPLATE:
410 break;
411 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
413 break;
414 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
416 break;
417 case FILEOPEN_PLAY:
419 break;
420 case FILEOPEN_LINK_PLAY:
422 break;
423 case FILEOPEN_READONLY_VERSION:
425 break;
426 case FILEOPEN_LINK_PREVIEW:
428 break;
429 case FILESAVE_AUTOEXTENSION:
431 break;
432 case FILEOPEN_PREVIEW:
434 break;
435 default:
436 throw lang::IllegalArgumentException("Unknown template",
437 static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ),
438 1 );
439 }
440
441 m_pControlHelper->initialize(templateId);
442
444}
445
446#pragma mark XCancellable
447
449{
450 SolarMutexGuard aGuard;
451
452 if (m_pDialog != nil) {
453 [m_pDialog cancel:nil];
454 }
455}
456
457#pragma mark XEventListener
458
459void SalAquaFilePicker::disposing( const lang::EventObject& aEvent )
460{
461 SolarMutexGuard aGuard;
462
463 uno::Reference<XFilePickerListener> xFilePickerListener( aEvent.Source, css::uno::UNO_QUERY );
464
465 if( xFilePickerListener.is() )
466 removeFilePickerListener( xFilePickerListener );
467}
468
469#pragma mark XServiceInfo
470
472{
473 return "com.sun.star.ui.dialogs.SalAquaFilePicker";
474}
475
476sal_Bool SAL_CALL SalAquaFilePicker::supportsService( const OUString& sServiceName )
477{
479}
480
481uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getSupportedServiceNames()
482{
484}
485
486#pragma mark Misc/Private
487
488void SalAquaFilePicker::fileSelectionChanged( FilePickerEvent aEvent )
489{
490 if (m_xListener.is())
491 m_xListener->fileSelectionChanged( aEvent );
492}
493
494void SalAquaFilePicker::directoryChanged( FilePickerEvent aEvent )
495{
496 if (m_xListener.is())
497 m_xListener->directoryChanged( aEvent );
498}
499
500void SalAquaFilePicker::controlStateChanged( FilePickerEvent aEvent )
501{
502 if (m_xListener.is())
503 m_xListener->controlStateChanged( aEvent );
504}
505
507{
508 if (m_xListener.is())
509 m_xListener->dialogSizeChanged();
510}
511
512
513// Misc
514
516{
517 SolarMutexGuard aGuard;
518
519 if (nullptr == m_pFilterHelper) {
522 [m_pDelegate setFilterHelper:m_pFilterHelper];
523 }
524}
525
527{
529}
530
532{
534 return;
535 }
536
537 // we need to set this here again because initial setting does
538 //[m_pDialog setExtensionHidden:YES];
539
540 SolarMutexGuard aGuard;
541
543 SAL_WNODEPRECATED_DECLARATIONS_PUSH // setAllowedFileTypes (12.0)
544 [m_pDialog setAllowedFileTypes:nil];
546 [m_pDialog setAllowsOtherFileTypes:YES];
547 } else {
549
551 if( aStringList.empty()) // #i9328#
552 return;
553
554 OUString suffix = (*(aStringList.begin())).copy(1);
555 NSString *requiredFileType = [NSString stringWithOUString:suffix];
556
557 SAL_WNODEPRECATED_DECLARATIONS_PUSH // setAllowedFileTypes (12.0)
558 [m_pDialog setAllowedFileTypes:[NSArray arrayWithObjects:requiredFileType, nil]];
560
561 [m_pDialog setAllowsOtherFileTypes:NO];
562 }
563}
564
566{
567 if (m_pDialog == nil) {
568 return;
569 }
570
571 SolarMutexGuard aGuard;
572
574
575 [m_pDialog validateVisibleColumns];
576
577 FilePickerEvent evt;
578 evt.ElementId = LISTBOX_FILTER;
579 controlStateChanged( evt );
580}
581
582extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
584 css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
585{
586 return cppu::acquire(new SalAquaFilePicker());
587}
588
589
590/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
::std::list< OUString > OUStringList
sal_Int16 nControlId
::cppu::WeakComponentImplHelper< css::ui::dialogs::XFilePicker3, css::ui::dialogs::XFilePickerControlAccess, css::lang::XInitialization, css::lang::XServiceInfo > SalAquaFilePicker_Base
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * fpicker_SalAquaFilePicker_get_implementation(css::uno::XComponentContext *, css::uno::Sequence< css::uno::Any > const &)
constexpr OUStringLiteral sServiceName
AnyEventRef aEvent
uno::Any getValue(sal_Int16 nControlId, sal_Int16 nControlAction) const
void setFilterControlNeeded(bool bNeeded)
void setFilePickerDelegate(AquaFilePickerDelegate *pDelegate)
void initialize(sal_Int16 templateId)
void setLabel(sal_Int16 nControlId, NSString *aLabel)
void setFilterHelper(FilterHelper *pFilterHelper)
OUString getLabel(sal_Int16 nControlId)
void updateFilterUI()
void enableControl(sal_Int16 nControlId, bool bEnable) const
bool isAutoExtensionEnabled()
void setValue(sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any &rValue)
OUString getCurrentFilter()
void appendFilter(const OUString &aTitle, std::u16string_view aFilter)
void appendFilterGroup(const css::uno::Sequence< css::beans::StringPair > &aFilters)
OUStringList getCurrentFilterSuffixList()
void SetFilters()
void setCurrentFilter(const OUString &aTitle)
virtual void SAL_CALL appendFilter(const OUString &aTitle, const OUString &aFilter) override
AquaFilePickerDelegate * m_pDelegate
virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > &aArguments) override
virtual OUString SAL_CALL getDisplayDirectory() override
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual void SAL_CALL addFilePickerListener(const css::uno::Reference< css::ui::dialogs::XFilePickerListener > &xListener) override
virtual void disposing(const css::lang::EventObject &aEvent)
virtual void SAL_CALL setDefaultName(const OUString &aName) override
virtual void SAL_CALL removeFilePickerListener(const css::uno::Reference< css::ui::dialogs::XFilePickerListener > &xListener) override
virtual sal_Int16 SAL_CALL execute() override
virtual void SAL_CALL setMultiSelectionMode(sal_Bool bMode) override
virtual void SAL_CALL setValue(sal_Int16 nControlId, sal_Int16 nControlAction, const css::uno::Any &aValue) override
virtual css::uno::Sequence< OUString > SAL_CALL getFiles() override
virtual OUString SAL_CALL getCurrentFilter() override
void directoryChanged(css::ui::dialogs::FilePickerEvent aEvent)
virtual void SAL_CALL appendFilterGroup(const OUString &sGroupTitle, const css::uno::Sequence< css::beans::StringPair > &aFilters) override
virtual void SAL_CALL setLabel(sal_Int16 nControlId, const OUString &aLabel) override
virtual css::uno::Any SAL_CALL getValue(sal_Int16 aControlId, sal_Int16 aControlAction) override
virtual void SAL_CALL setDisplayDirectory(const OUString &aDirectory) override
virtual void SAL_CALL cancel() override
virtual OUString SAL_CALL getLabel(sal_Int16 nControlId) override
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
FilterHelper * m_pFilterHelper
css::uno::Reference< css::ui::dialogs::XFilePickerListener > m_xListener
void controlStateChanged(css::ui::dialogs::FilePickerEvent aEvent)
virtual void SAL_CALL enableControl(sal_Int16 nControlId, sal_Bool bEnable) override
virtual OUString SAL_CALL getImplementationName() override
virtual void SAL_CALL setTitle(const OUString &aTitle) override
virtual void SAL_CALL setCurrentFilter(const OUString &aTitle) override
void fileSelectionChanged(css::ui::dialogs::FilePickerEvent aEvent)
virtual css::uno::Sequence< OUString > SAL_CALL getSelectedFiles() override
virtual ~SalAquaFilePicker() override
virtual void ensureFilterHelper()
OUString const & implgetDisplayDirectory()
void implInitialize()
int runandwaitforresult()
void implsetTitle(const OUString &aTitle)
ControlHelper * m_pControlHelper
NavigationServices_DialogType m_nDialogType
NSSavePanel * m_pDialog
void implsetDisplayDirectory(const OUString &rDirectory)
Sequence< OUString > FilePicker_getSupportedServiceNames()
Sequence< PropertyValue > aArguments
sal_Int32 nIndex
OUString aName
#define SAL_INFO(area, stream)
return NULL
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
#define SAL_WNODEPRECATED_DECLARATIONS_POP
unsigned char sal_Bool
#define SAL_WNODEPRECATED_DECLARATIONS_PUSH
OUString aLabel
const std::u16string_view aStringList[]