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>
42 #include "CFStringUtilities.hxx"
44 #include "NSURL_OOoAdditions.hxx"
45 
46 #include <iostream>
47 
48 #include "SalAquaFilePicker.hxx"
49 
50 #include <objc/objc-runtime.h>
51 
52 #pragma mark DEFINES
53 
54 using namespace ::com::sun::star;
55 using namespace ::com::sun::star::ui::dialogs;
56 using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
57 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
58 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
59 using namespace ::com::sun::star::lang;
60 using namespace ::com::sun::star::beans;
61 using namespace ::com::sun::star::uno;
62 
63 namespace
64 {
65  uno::Sequence<OUString> FilePicker_getSupportedServiceNames()
66  {
67  return { "com.sun.star.ui.dialogs.FilePicker",
68  "com.sun.star.ui.dialogs.SystemFilePicker",
69  "com.sun.star.ui.dialogs.AquaFilePicker" };
70  }
71 }
72 
73 #pragma mark Constructor
74 
76  : SalAquaFilePicker_Base( m_rbHelperMtx )
77  , m_pFilterHelper( nullptr )
78 {
79  m_pDelegate = [[AquaFilePickerDelegate alloc] initWithFilePicker:this];
81 }
82 
84 {
85  if (nullptr != m_pFilterHelper)
86  delete m_pFilterHelper;
87 
88  [m_pDelegate release];
89 }
90 
91 
92 #pragma mark XFilePickerNotifier
93 
94 void SAL_CALL SalAquaFilePicker::addFilePickerListener( const uno::Reference<XFilePickerListener>& xListener )
95 {
96  SolarMutexGuard aGuard;
97  m_xListener = xListener;
98 }
99 
100 void SAL_CALL SalAquaFilePicker::removeFilePickerListener( const uno::Reference<XFilePickerListener>& )
101 {
102  SolarMutexGuard aGuard;
103  m_xListener.clear();
104 }
105 
106 #pragma mark XAsynchronousExecutableDialog
107 
108 void SAL_CALL SalAquaFilePicker::setTitle( const OUString& aTitle )
109 {
110  SolarMutexGuard aGuard;
111  implsetTitle(aTitle);
112 }
113 
114 sal_Int16 SAL_CALL SalAquaFilePicker::execute()
115 {
116  SolarMutexGuard aGuard;
117 
118  sal_Int16 retVal = 0;
119 
120  implInitialize();
121 
122  // if m_pDialog is nil after initialization, something must have gone wrong before
123  // or there was no initialization (see issue https://bz.apache.org/ooo/show_bug.cgi?id=100214)
124  if (m_pDialog == nil) {
126  }
127 
128  if (m_pFilterHelper) {
130  }
131 
133  if (m_sSaveFileName.getLength() == 0) {
134  //if no filename is set, NavigationServices will set the name to "untitled". We don't want this!
135  //So let's try to get the window title to get the real untitled name
136  NSWindow *frontWindow = [NSApp keyWindow];
137  if (nullptr != frontWindow) {
138  NSString *windowTitle = [frontWindow title];
139  if (windowTitle != nil) {
140  OUString ouName = [windowTitle OUString];
141  //a window title will typically be something like "Untitled1 - OpenOffice.org Writer"
142  //but we only want the "Untitled1" part of it
143  sal_Int32 indexOfDash = ouName.indexOf(" - ");
144  if (indexOfDash > -1) {
145  m_sSaveFileName = ouName.copy(0,indexOfDash);
146  if (m_sSaveFileName.getLength() > 0) {
148  }
149  }
150  }
151  }
152  }
153  }
154 
155  //Set the delegate to be notified of certain events
156 
157  // I don't know why, but with gcc 4.2.1, this line results in the warning:
158  // class 'AquaFilePickerDelegate' does not implement the 'NSOpenSavePanelDelegate' protocol
159  // So instead of:
160  // [m_pDialog setDelegate:m_pDelegate];
161  // do:
162  reinterpret_cast<id (*)(id, SEL, ...)>(objc_msgSend)(
163  m_pDialog, @selector(setDelegate:), m_pDelegate);
164 
165  int nStatus = runandwaitforresult();
166 
167  [m_pDialog setDelegate:nil];
168 
169  switch( nStatus )
170  {
171  case NSModalResponseOK:
172  retVal = ExecutableDialogResults::OK;
173  break;
174 
175  case NSModalResponseCancel:
176  retVal = ExecutableDialogResults::CANCEL;
177  break;
178 
179  default:
180  throw uno::RuntimeException(
181  "The dialog returned with an unknown result!",
182  static_cast<XFilePicker*>( static_cast<XFilePicker3*>( this ) ));
183  break;
184  }
185 
186  return retVal;
187 }
188 
189 
190 #pragma mark XFilePicker
191 
192 void SAL_CALL SalAquaFilePicker::setMultiSelectionMode( sal_Bool /* bMode */ )
193 {
194  SolarMutexGuard aGuard;
195 
197  [static_cast<NSOpenPanel*>(m_pDialog) setAllowsMultipleSelection:YES];
198  }
199 }
200 
201 void SAL_CALL SalAquaFilePicker::setDefaultName( const OUString& aName )
202 {
203  SolarMutexGuard aGuard;
204 
205  m_sSaveFileName = aName;
206 }
207 
208 void SAL_CALL SalAquaFilePicker::setDisplayDirectory( const OUString& rDirectory )
209 {
210  SolarMutexGuard aGuard;
211 
212  implsetDisplayDirectory(rDirectory);
213 }
214 
216 {
217  OUString retVal = implgetDisplayDirectory();
218 
219  return retVal;
220 }
221 
222 uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getFiles()
223 {
224  uno::Sequence< OUString > aSelectedFiles = getSelectedFiles();
225  // multiselection doesn't really work with getFiles
226  // so just retrieve the first url
227  if (aSelectedFiles.getLength() > 1)
228  aSelectedFiles.realloc(1);
229 
230  return aSelectedFiles;
231 }
232 
233 uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getSelectedFiles()
234 {
235  SolarMutexGuard aGuard;
236 
237 #if HAVE_FEATURE_MACOSX_SANDBOX
238  static NSUserDefaults *userDefaults;
239  static bool triedUserDefaults = false;
240 
241  if (!triedUserDefaults)
242  {
243  userDefaults = [NSUserDefaults standardUserDefaults];
244  triedUserDefaults = true;
245  }
246 #endif
247 
248  NSArray *files = nil;
250  files = [static_cast<NSOpenPanel*>(m_pDialog) URLs];
251  }
253  files = [NSArray arrayWithObjects:[m_pDialog URL], nil];
254  }
255 
256  NSUInteger nFiles = [files count];
257  SAL_INFO("fpicker.aqua", "# of items: " << nFiles);
258 
259  uno::Sequence< OUString > aSelectedFiles(nFiles);
260 
261  for(NSUInteger nIndex = 0; nIndex < nFiles; nIndex += 1)
262  {
263  NSURL *url = [files objectAtIndex:nIndex];
264 
265 #if HAVE_FEATURE_MACOSX_SANDBOX
266  if (userDefaults != NULL &&
267  [url respondsToSelector:@selector(bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:)])
268  {
269  // In the case of "Save As" when the user has input a new
270  // file name, this call will return nil, as bookmarks can
271  // (naturally) only be created for existing file system
272  // objects. In that case, code at a much lower level, in
273  // sal, takes care of creating a bookmark when a new file
274  // has been created outside the sandbox.
275  NSData *data = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
276  includingResourceValuesForKeys:nil
277  relativeToURL:nil
278  error:nil];
279  if (data != NULL)
280  {
281  [userDefaults setObject:data
282  forKey:[@"bookmarkFor:" stringByAppendingString:[url absoluteString]]];
283  }
284  }
285 #endif
286 
287  OUString sFileOrDirURL = [url OUStringForInfo:FULLPATH];
288 
289  aSelectedFiles[nIndex] = sFileOrDirURL;
290  }
291 
292  return aSelectedFiles;
293 }
294 
295 #pragma mark XFilterManager
296 
297 void SAL_CALL SalAquaFilePicker::appendFilter( const OUString& aTitle, const OUString& aFilter )
298 {
299  SolarMutexGuard aGuard;
300 
302  m_pFilterHelper->appendFilter( aTitle, aFilter );
304 }
305 
306 void SAL_CALL SalAquaFilePicker::setCurrentFilter( const OUString& aTitle )
307 {
308  SolarMutexGuard aGuard;
309 
312  updateFilterUI();
313 
315 }
316 
318 {
319  SolarMutexGuard aGuard;
320 
322 
324 }
325 
326 #pragma mark XFilterGroupManager
327 
328 void SAL_CALL SalAquaFilePicker::appendFilterGroup( const OUString& sGroupTitle, const uno::Sequence<beans::StringPair>& aFilters )
329 {
330  SolarMutexGuard aGuard;
331 
333  m_pFilterHelper->appendFilterGroup(sGroupTitle, aFilters);
335 }
336 
337 #pragma mark XFilePickerControlAccess
338 
339 void SAL_CALL SalAquaFilePicker::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
340 {
341  SolarMutexGuard aGuard;
342 
343  m_pControlHelper->setValue(nControlId, nControlAction, rValue);
344 
345  if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION && m_nDialogType == NAVIGATIONSERVICES_SAVE) {
347  }
348 }
349 
350 uno::Any SAL_CALL SalAquaFilePicker::getValue( sal_Int16 nControlId, sal_Int16 nControlAction )
351 {
352  uno::Any aValue = m_pControlHelper->getValue(nControlId, nControlAction);
353 
354  return aValue;
355 }
356 
357 void SAL_CALL SalAquaFilePicker::enableControl( sal_Int16 nControlId, sal_Bool bEnable )
358 {
359  m_pControlHelper->enableControl(nControlId, bEnable);
360 }
361 
362 void SAL_CALL SalAquaFilePicker::setLabel( sal_Int16 nControlId, const OUString& aLabel )
363 {
364  SolarMutexGuard aGuard;
365 
366  NSString* sLabel = [NSString stringWithOUString:aLabel];
367  m_pControlHelper->setLabel( nControlId, sLabel ) ;
368 }
369 
370 OUString SAL_CALL SalAquaFilePicker::getLabel( sal_Int16 nControlId )
371 {
372  return m_pControlHelper->getLabel(nControlId);
373 }
374 
375 #pragma mark XInitialization
376 
377 void SAL_CALL SalAquaFilePicker::initialize( const uno::Sequence<uno::Any>& aArguments )
378 {
379  SolarMutexGuard aGuard;
380 
381  // parameter checking
382  uno::Any aAny;
383  if( 0 == aArguments.getLength() )
384  throw lang::IllegalArgumentException("no arguments",
385  static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ), 1 );
386 
387  aAny = aArguments[0];
388 
389  if( ( aAny.getValueType() != ::cppu::UnoType<sal_Int16>::get() ) &&
390  (aAny.getValueType() != ::cppu::UnoType<sal_Int8>::get() ) )
391  throw lang::IllegalArgumentException("invalid argument type",
392  static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ), 1 );
393 
394  sal_Int16 templateId = -1;
395  aAny >>= templateId;
396 
397  switch( templateId )
398  {
399  case FILEOPEN_SIMPLE:
401  break;
402  case FILESAVE_SIMPLE:
404  break;
405  case FILESAVE_AUTOEXTENSION_PASSWORD:
407  break;
408  case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
410  break;
411  case FILESAVE_AUTOEXTENSION_SELECTION:
413  break;
414  case FILESAVE_AUTOEXTENSION_TEMPLATE:
416  break;
417  case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
419  break;
420  case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
422  break;
423  case FILEOPEN_PLAY:
425  break;
426  case FILEOPEN_LINK_PLAY:
428  break;
429  case FILEOPEN_READONLY_VERSION:
431  break;
432  case FILEOPEN_LINK_PREVIEW:
434  break;
435  case FILESAVE_AUTOEXTENSION:
437  break;
438  case FILEOPEN_PREVIEW:
440  break;
441  default:
442  throw lang::IllegalArgumentException("Unknown template",
443  static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ),
444  1 );
445  }
446 
447  m_pControlHelper->initialize(templateId);
448 
449  implInitialize();
450 }
451 
452 #pragma mark XCancellable
453 
455 {
456  SolarMutexGuard aGuard;
457 
458  if (m_pDialog != nil) {
459  [m_pDialog cancel:nil];
460  }
461 }
462 
463 #pragma mark XEventListener
464 
465 void SalAquaFilePicker::disposing( const lang::EventObject& aEvent )
466 {
467  SolarMutexGuard aGuard;
468 
469  uno::Reference<XFilePickerListener> xFilePickerListener( aEvent.Source, css::uno::UNO_QUERY );
470 
471  if( xFilePickerListener.is() )
472  removeFilePickerListener( xFilePickerListener );
473 }
474 
475 #pragma mark XServiceInfo
476 
478 {
479  return "com.sun.star.ui.dialogs.SalAquaFilePicker";
480 }
481 
482 sal_Bool SAL_CALL SalAquaFilePicker::supportsService( const OUString& sServiceName )
483 {
484  return cppu::supportsService(this, sServiceName);
485 }
486 
487 uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getSupportedServiceNames()
488 {
490 }
491 
492 #pragma mark Misc/Private
493 
494 void SalAquaFilePicker::fileSelectionChanged( FilePickerEvent aEvent )
495 {
496  if (m_xListener.is())
497  m_xListener->fileSelectionChanged( aEvent );
498 }
499 
500 void SalAquaFilePicker::directoryChanged( FilePickerEvent aEvent )
501 {
502  if (m_xListener.is())
503  m_xListener->directoryChanged( aEvent );
504 }
505 
506 void SalAquaFilePicker::controlStateChanged( FilePickerEvent aEvent )
507 {
508  if (m_xListener.is())
509  m_xListener->controlStateChanged( aEvent );
510 }
511 
513 {
514  if (m_xListener.is())
515  m_xListener->dialogSizeChanged();
516 }
517 
518 
519 // Misc
520 
522 {
523  SolarMutexGuard aGuard;
524 
525  if (nullptr == m_pFilterHelper) {
528  [m_pDelegate setFilterHelper:m_pFilterHelper];
529  }
530 }
531 
533 {
535 }
536 
538 {
540  return;
541  }
542 
543  // we need to set this here again because initial setting does
544  //[m_pDialog setExtensionHidden:YES];
545 
546  SolarMutexGuard aGuard;
547 
549  [m_pDialog setAllowedFileTypes:nil];
550  [m_pDialog setAllowsOtherFileTypes:YES];
551  } else {
553 
555  if( aStringList.empty()) // #i9328#
556  return;
557 
558  OUString suffix = (*(aStringList.begin())).copy(1);
559  NSString *requiredFileType = [NSString stringWithOUString:suffix];
560 
561  [m_pDialog setAllowedFileTypes:[NSArray arrayWithObjects:requiredFileType, nil]];
562 
563  [m_pDialog setAllowsOtherFileTypes:NO];
564  }
565 }
566 
568 {
569  if (m_pDialog == nil) {
570  return;
571  }
572 
573  SolarMutexGuard aGuard;
574 
576 
577  [m_pDialog validateVisibleColumns];
578 
579  FilePickerEvent evt;
580  evt.ElementId = LISTBOX_FILTER;
581  controlStateChanged( evt );
582 }
583 
584 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
586  css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
587 {
588  return cppu::acquire(new SalAquaFilePicker());
589 }
590 
591 
592 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 nIndex
virtual void SAL_CALL setTitle(const OUString &aTitle) override
virtual void SAL_CALL appendFilter(const OUString &aTitle, const OUString &aFilter) override
void directoryChanged(css::ui::dialogs::FilePickerEvent aEvent)
OUString const & implgetDisplayDirectory()
virtual void SAL_CALL setMultiSelectionMode(sal_Bool bMode) override
virtual void SAL_CALL removeFilePickerListener(const css::uno::Reference< css::ui::dialogs::XFilePickerListener > &xListener) override
void controlStateChanged(css::ui::dialogs::FilePickerEvent aEvent)
void appendFilterGroup(const OUString &sGroupTitle, const css::uno::Sequence< css::beans::StringPair > &aFilters)
void setFilterHelper:(FilterHelper *filterHelper)
OUString getLabel(sal_Int16 nControlId)
void setCurrentFilter(const OUString &aTitle)
virtual void SAL_CALL enableControl(sal_Int16 nControlId, sal_Bool bEnable) override
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * fpicker_SalAquaFilePicker_get_implementation(css::uno::XComponentContext *, css::uno::Sequence< css::uno::Any > const &)
int runandwaitforresult()
virtual OUString SAL_CALL getCurrentFilter() override
void implsetDisplayDirectory(const OUString &rDirectory)
void implInitialize()
virtual OUString SAL_CALL getDisplayDirectory() override
uno::Any getValue(sal_Int16 nControlId, sal_Int16 nControlAction) const
::cppu::WeakComponentImplHelper< css::ui::dialogs::XFilePicker3, css::ui::dialogs::XFilePickerControlAccess, css::lang::XInitialization, css::lang::XServiceInfo > SalAquaFilePicker_Base
ControlHelper * m_pControlHelper
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual void SAL_CALL cancel() override
return NULL
virtual sal_Int16 SAL_CALL execute() override
OUString getCurrentFilter()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
virtual void SAL_CALL addFilePickerListener(const css::uno::Reference< css::ui::dialogs::XFilePickerListener > &xListener) override
virtual void SAL_CALL appendFilterGroup(const OUString &sGroupTitle, const css::uno::Sequence< css::beans::StringPair > &aFilters) override
virtual OUString SAL_CALL getImplementationName() override
::std::list< OUString > OUStringList
virtual css::uno::Sequence< OUString > SAL_CALL getSelectedFiles() override
virtual void ensureFilterHelper()
void initialize(sal_Int16 templateId)
void updateFilterUI()
virtual void SAL_CALL setLabel(sal_Int16 nControlId, const OUString &aLabel) override
void implsetTitle(const OUString &aTitle)
bool isAutoExtensionEnabled()
virtual void disposing(const css::lang::EventObject &aEvent)
void fileSelectionChanged(css::ui::dialogs::FilePickerEvent aEvent)
virtual void SAL_CALL setDisplayDirectory(const OUString &aDirectory) override
virtual ~SalAquaFilePicker() override
void setFilePickerDelegate(AquaFilePickerDelegate *pDelegate)
sal_Int16 nControlId
void setFilterControlNeeded(bool bNeeded)
void SetFilters()
virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > &aArguments) override
unsigned char sal_Bool
virtual css::uno::Sequence< OUString > SAL_CALL getFiles() override
css::uno::Type const & get()
virtual OUString SAL_CALL getLabel(sal_Int16 nControlId) override
virtual void SAL_CALL setValue(sal_Int16 nControlId, sal_Int16 nControlAction, const css::uno::Any &aValue) override
virtual void SAL_CALL setDefaultName(const OUString &aName) override
exports com.sun.star.chart2. data
FilterHelper * m_pFilterHelper
void setLabel(sal_Int16 nControlId, NSString *aLabel)
NSSavePanel * m_pDialog
OUStringList getCurrentFilterSuffixList()
void appendFilter(const OUString &aTitle, const OUString &aFilter)
#define SAL_INFO(area, stream)
css::uno::Reference< css::ui::dialogs::XFilePickerListener > m_xListener
const std::u16string_view aStringList[]
void setValue(sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any &rValue)
AquaFilePickerDelegate * m_pDelegate
NavigationServices_DialogType m_nDialogType
Sequence< OUString > FilePicker_getSupportedServiceNames()
void enableControl(sal_Int16 nControlId, bool bEnable) const
virtual void SAL_CALL setCurrentFilter(const OUString &aTitle) override
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
virtual css::uno::Any SAL_CALL getValue(sal_Int16 aControlId, sal_Int16 aControlAction) override
void setFilterHelper(FilterHelper *pFilterHelper)