summaryrefslogtreecommitdiffstats
path: root/fpicker/source/aqua
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /fpicker/source/aqua
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fpicker/source/aqua')
-rw-r--r--fpicker/source/aqua/AquaFilePickerDelegate.hxx48
-rw-r--r--fpicker/source/aqua/AquaFilePickerDelegate.mm115
-rw-r--r--fpicker/source/aqua/ControlHelper.hxx184
-rw-r--r--fpicker/source/aqua/ControlHelper.mm937
-rw-r--r--fpicker/source/aqua/FilterHelper.hxx124
-rw-r--r--fpicker/source/aqua/FilterHelper.mm443
-rw-r--r--fpicker/source/aqua/NSString_OOoAdditions.hxx33
-rw-r--r--fpicker/source/aqua/NSString_OOoAdditions.mm47
-rw-r--r--fpicker/source/aqua/NSURL_OOoAdditions.hxx38
-rw-r--r--fpicker/source/aqua/NSURL_OOoAdditions.mm80
-rw-r--r--fpicker/source/aqua/SalAquaConstants.h54
-rw-r--r--fpicker/source/aqua/SalAquaFilePicker.hxx160
-rw-r--r--fpicker/source/aqua/SalAquaFilePicker.mm590
-rw-r--r--fpicker/source/aqua/SalAquaFolderPicker.hxx95
-rw-r--r--fpicker/source/aqua/SalAquaFolderPicker.mm179
-rw-r--r--fpicker/source/aqua/SalAquaPicker.hxx81
-rw-r--r--fpicker/source/aqua/SalAquaPicker.mm209
-rw-r--r--fpicker/source/aqua/fps_aqua.component30
-rw-r--r--fpicker/source/aqua/resourceprovider.hxx46
-rw-r--r--fpicker/source/aqua/resourceprovider.mm116
20 files changed, 3609 insertions, 0 deletions
diff --git a/fpicker/source/aqua/AquaFilePickerDelegate.hxx b/fpicker/source/aqua/AquaFilePickerDelegate.hxx
new file mode 100644
index 000000000..51c34b71b
--- /dev/null
+++ b/fpicker/source/aqua/AquaFilePickerDelegate.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <premac.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+class SalAquaFilePicker;
+class FilterHelper;
+
+@interface AquaFilePickerDelegate : NSObject <NSOpenSavePanelDelegate>
+{
+ SalAquaFilePicker* filePicker;
+ FilterHelper* filterHelper;
+}
+
+- (id)initWithFilePicker:(SalAquaFilePicker*)fPicker;
+
+- (void)setFilterHelper:(FilterHelper*)filterHelper;
+
+- (BOOL)panel:(id)sender shouldShowFilename:(NSString*)filename;
+- (void)panelSelectionDidChange:(id)sender;
+- (void)panel:(id)sender directoryDidChange:(NSString*)path;
+
+- (void)filterSelectedAtIndex:(id)sender;
+- (void)autoextensionChanged:(id)sender;
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/AquaFilePickerDelegate.mm b/fpicker/source/aqua/AquaFilePickerDelegate.mm
new file mode 100644
index 000000000..d9506c2c7
--- /dev/null
+++ b/fpicker/source/aqua/AquaFilePickerDelegate.mm
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/uno/Any.hxx>
+
+#include "SalAquaFilePicker.hxx"
+#include "FilterHelper.hxx"
+#include "AquaFilePickerDelegate.hxx"
+
+@implementation AquaFilePickerDelegate
+
+- (id)initWithFilePicker:(SalAquaFilePicker*)fPicker
+{
+ if ((self = [super init])) {
+ filePicker = fPicker;
+ filterHelper = nullptr;
+ return self;
+ }
+ return nil;
+}
+
+- (void)setFilterHelper:(FilterHelper*)helper
+{
+ filterHelper = helper;
+}
+
+#pragma mark NSSavePanel delegate methods
+
+- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
+{
+ (void)sender;
+ if( filterHelper == nullptr )
+ return true;
+ if( filename == nil )
+ return false;
+ return filterHelper->filenameMatchesFilter(filename);
+}
+
+- (void)panelSelectionDidChange:(id)sender
+{
+ (void)sender;
+ if (filePicker != nullptr) {
+ css::ui::dialogs::FilePickerEvent evt;
+ filePicker->fileSelectionChanged(evt);
+ }
+}
+
+- (void)panel:(id)sender directoryDidChange:(NSString *)path
+{
+ (void)sender;
+ (void)path;
+ if (filePicker != nullptr) {
+ css::ui::dialogs::FilePickerEvent evt;
+ filePicker->directoryChanged(evt);
+ }
+}
+
+
+#pragma mark UIActions
+- (void)filterSelectedAtIndex:(id)sender
+{
+ if (sender == nil) {
+ return;
+ }
+
+ if ([sender class] != [NSPopUpButton class]) {
+ return;
+ }
+
+ if (filterHelper == nullptr) {
+ return;
+ }
+
+ NSPopUpButton *popup = static_cast<NSPopUpButton*>(sender);
+ unsigned int selectedIndex = [popup indexOfSelectedItem];
+
+ filterHelper->SetFilterAtIndex(selectedIndex);
+
+ filePicker->filterControlChanged();
+}
+
+- (void)autoextensionChanged:(id)sender
+{
+ if (sender == nil) {
+ return;
+ }
+
+ if ([sender class] != [NSButton class]) {
+ return;
+ }
+ uno::Any aValue;
+ aValue <<= ([static_cast<NSButton*>(sender) state] == NSControlStateValueOn);
+
+ filePicker->setValue(css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0, aValue);
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/ControlHelper.hxx b/fpicker/source/aqua/ControlHelper.hxx
new file mode 100644
index 000000000..5da540df6
--- /dev/null
+++ b/fpicker/source/aqua/ControlHelper.hxx
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+#include <list>
+#include <map>
+
+#include <premac.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+#include "SalAquaConstants.h"
+#include "FilterHelper.hxx"
+#include "AquaFilePickerDelegate.hxx"
+
+using namespace com::sun::star;
+
+class ControlHelper {
+
+public:
+
+
+ // Constructor / Destructor
+
+ ControlHelper();
+ virtual ~ControlHelper();
+
+
+ // XInitialization delegate
+
+ void initialize( sal_Int16 templateId );
+
+
+ // XFilePickerControlAccess function delegates
+
+ void setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue );
+ uno::Any getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const;
+ void enableControl( sal_Int16 nControlId, bool bEnable ) const;
+ OUString getLabel( sal_Int16 nControlId );
+ void setLabel( sal_Int16 nControlId, NSString* aLabel );
+
+
+ // other stuff
+
+ void updateFilterUI();
+
+
+ // Type definitions
+
+ enum ToggleType {
+ AUTOEXTENSION, //but autoextension is handled differently on MacOSX
+ PASSWORD,
+ FILTEROPTIONS,
+ READONLY,
+ LINK,
+ PREVIEW,
+ SELECTION,
+ TOGGLE_LAST
+ };
+
+ enum ListType {
+ VERSION,
+ TEMPLATE,
+ IMAGE_TEMPLATE,
+ IMAGE_ANCHOR,
+ LIST_LAST
+ };
+
+
+ // inline functions
+
+ NSView* getUserPane() {
+ if (!m_bIsUserPaneLaidOut) {
+ createUserPane();
+ }
+ return m_pUserPane;
+ }
+
+ bool getVisibility(ToggleType tToggle) {
+ return m_bToggleVisibility[tToggle];
+ }
+
+ void setFilterControlNeeded(bool bNeeded) {
+ m_bIsFilterControlNeeded = bNeeded;
+ if (bNeeded) {
+ m_bUserPaneNeeded = true;
+ }
+ }
+
+ void setFilterHelper(FilterHelper* pFilterHelper) {
+ m_pFilterHelper = pFilterHelper;
+ }
+
+ void setFilePickerDelegate(AquaFilePickerDelegate* pDelegate) {
+ m_pDelegate = pDelegate;
+ }
+
+ bool isAutoExtensionEnabled() {
+ return ([static_cast<NSButton*>(m_pToggles[AUTOEXTENSION]) state] == NSControlStateValueOn);
+ }
+
+private:
+
+ // private member variables
+
+
+ /** the native view object */
+ NSView* m_pUserPane;
+
+ /** the checkbox controls */
+ NSControl* m_pToggles[ TOGGLE_LAST ];
+
+ /** the visibility flags for the checkboxes */
+ bool m_bToggleVisibility[TOGGLE_LAST];
+
+ /** the special filter control */
+ NSPopUpButton *m_pFilterControl;
+
+ /** the popup menu controls (except for the filter control) */
+ NSControl* m_pListControls[ LIST_LAST ];
+
+ /** a map to store a control's label text */
+ ::std::map<NSControl *, NSString *> m_aMapListLabels;
+
+ /** a map to store a popup menu's label text field */
+ ::std::map<NSPopUpButton *, NSTextField *> m_aMapListLabelFields;
+
+ /** the visibility flags for the popup menus */
+ bool m_bListVisibility[ LIST_LAST ];
+
+ /** indicates if a user pane is needed */
+ bool m_bUserPaneNeeded;
+
+ /** indicates if the user pane was laid out already */
+ bool m_bIsUserPaneLaidOut;
+
+ /** indicates if a filter control is needed */
+ bool m_bIsFilterControlNeeded;
+
+ /** a list with all actively used controls */
+ ::std::list<NSControl*> m_aActiveControls;
+
+ /** the filter helper */
+ FilterHelper *m_pFilterHelper;
+
+ /** the save or open panel's delegate */
+ AquaFilePickerDelegate *m_pDelegate;
+
+
+ // private methods
+
+ void HandleSetListValue(const NSControl* pControl, const sal_Int16 nControlAction, const uno::Any& rValue);
+
+ void createControls();
+ void createFilterControl();
+ void createUserPane();
+
+ static int getControlElementName(const Class clazz, const int nControlId);
+ NSControl* getControl( const sal_Int16 nControlId ) const;
+ static int getVerticalDistance(const NSControl* first, const NSControl* second);
+
+ void layoutControls();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/ControlHelper.mm b/fpicker/source/aqua/ControlHelper.mm
new file mode 100644
index 000000000..88f0b655c
--- /dev/null
+++ b/fpicker/source/aqua/ControlHelper.mm
@@ -0,0 +1,937 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ControlActions.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+#include "resourceprovider.hxx"
+#include "NSString_OOoAdditions.hxx"
+#include <sal/log.hxx>
+
+#include "ControlHelper.hxx"
+
+#pragma mark DEFINES
+#define POPUP_WIDTH_MIN 200
+#define POPUP_WIDTH_MAX 350
+
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
+using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
+using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
+
+namespace {
+
+uno::Any HandleGetListValue(const NSControl* pControl, const sal_Int16 nControlAction)
+{
+ uno::Any aAny;
+
+ if ([pControl class] != [NSPopUpButton class]) {
+ SAL_INFO("fpicker.aqua","not a popup button");
+ return aAny;
+ }
+
+ NSPopUpButton *pButton = static_cast<NSPopUpButton*>(pControl);
+ NSMenu *rMenu = [pButton menu];
+ if (nil == rMenu) {
+ SAL_INFO("fpicker.aqua","button has no menu");
+ return aAny;
+ }
+
+ switch (nControlAction)
+ {
+ case ControlActions::GET_ITEMS:
+ {
+ SAL_INFO("fpicker.aqua","GET_ITEMS");
+ uno::Sequence< OUString > aItemList;
+
+ int nItems = [rMenu numberOfItems];
+ if (nItems > 0) {
+ aItemList.realloc(nItems);
+ OUString* pItemList = aItemList.getArray();
+ for (int i = 0; i < nItems; i++) {
+ NSString* sCFItem = [pButton itemTitleAtIndex:i];
+ if (nil != sCFItem) {
+ pItemList[i] = [sCFItem OUString];
+ SAL_INFO("fpicker.aqua","Return value[" << (i - 1) << "]: " << aItemList[i - 1]);
+ }
+ }
+ }
+
+ aAny <<= aItemList;
+ }
+ break;
+ case ControlActions::GET_SELECTED_ITEM:
+ {
+ SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM");
+ NSString* sCFItem = [pButton titleOfSelectedItem];
+ if (nil != sCFItem) {
+ OUString sString = [sCFItem OUString];
+ SAL_INFO("fpicker.aqua","Return value: " << sString);
+ aAny <<= sString;
+ }
+ }
+ break;
+ case ControlActions::GET_SELECTED_ITEM_INDEX:
+ {
+ SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM_INDEX");
+ sal_Int32 nActive = [pButton indexOfSelectedItem];
+ SAL_INFO("fpicker.aqua","Return value: " << nActive);
+ aAny <<= nActive;
+ }
+ break;
+ default:
+ SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
+ break;
+ }
+
+ return aAny;
+}
+
+NSTextField* createLabelWithString(NSString* labelString)
+{
+ NSTextField *textField = [NSTextField new];
+ [textField setEditable:NO];
+ [textField setSelectable:NO];
+ [textField setDrawsBackground:NO];
+ [textField setBordered:NO];
+ [[textField cell] setTitle:labelString];
+
+ return textField;
+}
+
+}
+
+#pragma mark Constructor / Destructor
+
+// Constructor / Destructor
+
+ControlHelper::ControlHelper()
+: m_pUserPane(nullptr)
+, m_pFilterControl(nil)
+, m_bUserPaneNeeded( false )
+, m_bIsUserPaneLaidOut(false)
+, m_bIsFilterControlNeeded(false)
+, m_pFilterHelper(nullptr)
+{
+ int i;
+
+ for( i = 0; i < TOGGLE_LAST; i++ ) {
+ m_bToggleVisibility[i] = false;
+ }
+
+ for( i = 0; i < LIST_LAST; i++ ) {
+ m_bListVisibility[i] = false;
+ }
+}
+
+ControlHelper::~ControlHelper()
+{
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ if (nullptr != m_pUserPane) {
+ [m_pUserPane release];
+ }
+
+ if (m_pFilterControl != nullptr) {
+ [m_pFilterControl setTarget:nil];
+ }
+
+ for (auto const& activeControl : m_aActiveControls)
+ {
+ NSString* sLabelName = m_aMapListLabels[activeControl];
+ if (sLabelName != nil) {
+ [sLabelName release];
+ }
+ if ([activeControl class] == [NSPopUpButton class]) {
+ NSTextField* pField = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)];
+ if (pField != nil) {
+ [pField release];
+ }
+ }
+ [activeControl release];
+ }
+
+ [pool release];
+}
+
+#pragma mark XInitialization delegate
+
+// XInitialization delegate
+
+void ControlHelper::initialize( sal_Int16 nTemplateId )
+{
+ switch( nTemplateId )
+ {
+ case FILESAVE_AUTOEXTENSION_PASSWORD:
+ m_bToggleVisibility[AUTOEXTENSION] = true;
+ m_bToggleVisibility[PASSWORD] = true;
+ break;
+ case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
+ m_bToggleVisibility[AUTOEXTENSION] = true;
+ m_bToggleVisibility[PASSWORD] = true;
+ m_bToggleVisibility[FILTEROPTIONS] = true;
+ break;
+ case FILESAVE_AUTOEXTENSION_SELECTION:
+ m_bToggleVisibility[AUTOEXTENSION] = true;
+ m_bToggleVisibility[SELECTION] = true;
+ break;
+ case FILESAVE_AUTOEXTENSION_TEMPLATE:
+ m_bToggleVisibility[AUTOEXTENSION] = true;
+ m_bListVisibility[TEMPLATE] = true;
+ break;
+ case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
+ m_bToggleVisibility[LINK] = true;
+ m_bToggleVisibility[PREVIEW] = true;
+ m_bListVisibility[IMAGE_TEMPLATE] = true;
+ break;
+ case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
+ m_bToggleVisibility[LINK] = true;
+ m_bToggleVisibility[PREVIEW] = true;
+ m_bListVisibility[IMAGE_ANCHOR] = true;
+ break;
+ case FILEOPEN_READONLY_VERSION:
+ m_bToggleVisibility[READONLY] = true;
+ m_bListVisibility[VERSION] = true;
+ break;
+ case FILEOPEN_LINK_PREVIEW:
+ m_bToggleVisibility[LINK] = true;
+ m_bToggleVisibility[PREVIEW] = true;
+ break;
+ case FILESAVE_AUTOEXTENSION:
+ m_bToggleVisibility[AUTOEXTENSION] = true;
+ break;
+ case FILEOPEN_PREVIEW:
+ m_bToggleVisibility[PREVIEW] = true;
+ break;
+ case FILEOPEN_LINK_PLAY:
+ m_bToggleVisibility[LINK] = true;
+ }
+
+ createControls();
+}
+
+#pragma mark XFilePickerControlAccess delegates
+
+// XFilePickerControlAccess functions
+
+
+void ControlHelper::enableControl( const sal_Int16 nControlId, const bool bEnable ) const
+{
+ SolarMutexGuard aGuard;
+
+ if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
+ SAL_INFO("fpicker.aqua"," preview checkbox cannot be changed");
+ return;
+ }
+
+ NSControl* pControl = getControl(nControlId);
+
+ if( pControl != nil ) {
+ if( bEnable ) {
+ SAL_INFO("fpicker.aqua", "enable" );
+ } else {
+ SAL_INFO("fpicker.aqua", "disable" );
+ }
+ [pControl setEnabled:bEnable];
+ } else {
+ SAL_INFO("fpicker.aqua","enable unknown control " << nControlId );
+ }
+}
+
+OUString ControlHelper::getLabel( sal_Int16 nControlId )
+{
+ SolarMutexGuard aGuard;
+
+ NSControl* pControl = getControl( nControlId );
+
+ if( pControl == nil ) {
+ SAL_INFO("fpicker.aqua","Get label for unknown control " << nControlId);
+ return OUString();
+ }
+
+ OUString retVal;
+ if ([pControl class] == [NSPopUpButton class]) {
+ NSString *temp = m_aMapListLabels[pControl];
+ if (temp != nil)
+ retVal = [temp OUString];
+ }
+ else {
+ NSString* sLabel = [[pControl cell] title];
+ retVal = [sLabel OUString];
+ }
+
+ return retVal;
+}
+
+void ControlHelper::setLabel( sal_Int16 nControlId, NSString* aLabel )
+{
+ SolarMutexGuard aGuard;
+
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ NSControl* pControl = getControl(nControlId);
+
+ if (nil != pControl) {
+ if ([pControl class] == [NSPopUpButton class]) {
+ NSString *sOldName = m_aMapListLabels[pControl];
+ if (sOldName != nullptr && sOldName != aLabel) {
+ [sOldName release];
+ }
+
+ m_aMapListLabels[pControl] = [aLabel retain];
+ } else if ([pControl class] == [NSButton class]) {
+ [[pControl cell] setTitle:aLabel];
+ }
+ } else {
+ SAL_INFO("fpicker.aqua","Control not found to set label for");
+ }
+
+ layoutControls();
+
+ [pool release];
+}
+
+void ControlHelper::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
+{
+ SolarMutexGuard aGuard;
+
+ if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
+ SAL_INFO("fpicker.aqua"," value for preview is unchangeable");
+ }
+ else {
+ NSControl* pControl = getControl( nControlId );
+
+ if( pControl == nil ) {
+ SAL_INFO("fpicker.aqua","enable unknown control " << nControlId);
+ } else {
+ if( [pControl class] == [NSPopUpButton class] ) {
+ HandleSetListValue(pControl, nControlAction, rValue);
+ } else if( [pControl class] == [NSButton class] ) {
+ bool bChecked = false;
+ rValue >>= bChecked;
+ SAL_INFO("fpicker.aqua"," value is a bool: " << bChecked);
+ [static_cast<NSButton*>(pControl) setState:(bChecked ? NSControlStateValueOn : NSControlStateValueOff)];
+ } else
+ {
+ SAL_INFO("fpicker.aqua","Can't set value on button / list " << nControlId << " " << nControlAction);
+ }
+ }
+ }
+}
+
+uno::Any ControlHelper::getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRetval;
+
+ NSControl* pControl = getControl( nControlId );
+
+ if( pControl == nil ) {
+ SAL_INFO("fpicker.aqua","get value for unknown control " << nControlId);
+ } else {
+ if( [pControl class] == [NSPopUpButton class] ) {
+ aRetval = HandleGetListValue(pControl, nControlAction);
+ } else if( [pControl class] == [NSButton class] ) {
+ //NSLog(@"control: %@", [[pControl cell] title]);
+ bool bValue = [static_cast<NSButton*>(pControl) state] == NSControlStateValueOn;
+ aRetval <<= bValue;
+ SAL_INFO("fpicker.aqua","value is a bool (checkbox): " << bValue);
+ }
+ }
+
+ return aRetval;
+}
+
+void ControlHelper::createUserPane()
+{
+ if (!m_bUserPaneNeeded) {
+ SAL_INFO("fpicker.aqua","no user pane needed");
+ return;
+ }
+
+ if (nil != m_pUserPane) {
+ SAL_INFO("fpicker.aqua","user pane already exists");
+ return;
+ }
+
+ if (m_bIsFilterControlNeeded && m_pFilterControl == nil) {
+ createFilterControl();
+ }
+
+ NSRect minRect = NSMakeRect(0,0,300,33);
+ m_pUserPane = [[NSView alloc] initWithFrame:minRect];
+
+ int currentHeight = kAquaSpaceBoxFrameViewDiffTop + kAquaSpaceBoxFrameViewDiffBottom;
+ int currentWidth = 300;
+
+ bool bPopupControlPresent = false;
+ bool bButtonControlPresent = false;
+
+ int nCheckboxMaxWidth = 0;
+ int nPopupMaxWidth = 0;
+ int nPopupLabelMaxWidth = 0;
+
+ size_t nLoop = 0;
+ for (auto const& activeControl : m_aActiveControls)
+ {
+ SAL_INFO("fpicker.aqua","currentHeight: " << currentHeight);
+
+ //let the control calculate its size
+ [activeControl sizeToFit];
+
+ NSRect frame = [activeControl frame];
+ SAL_INFO("fpicker.aqua","frame for control " << [[activeControl description] UTF8String] << " is {" << frame.origin.x << ", " << frame.origin.y << ", " << frame.size.width << ", " << frame.size.height << "}");
+
+ int nControlHeight = frame.size.height;
+ int nControlWidth = frame.size.width;
+
+ // Note: controls are grouped by kind, first all popup menus, then checkboxes
+ if ([activeControl class] == [NSPopUpButton class]) {
+ if (bPopupControlPresent) {
+ //this is not the first popup
+ currentHeight += kAquaSpaceBetweenPopupMenus;
+ }
+ else if (nLoop)
+ {
+ currentHeight += kAquaSpaceBetweenControls;
+ }
+
+ bPopupControlPresent = true;
+
+ // we have to add the label text width
+ NSString *label = m_aMapListLabels[activeControl];
+
+ NSTextField *textField = createLabelWithString(label);
+ [textField sizeToFit];
+ m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)] = textField;
+ [m_pUserPane addSubview:textField];
+
+ NSRect tfRect = [textField frame];
+ SAL_INFO("fpicker.aqua","frame for textfield " << [[textField description] UTF8String] << " is {" << tfRect.origin.x << ", " << tfRect.origin.y << ", " << tfRect.size.width << ", " << tfRect.size.height << "}");
+
+ int tfWidth = tfRect.size.width;
+
+ if (nPopupLabelMaxWidth < tfWidth) {
+ nPopupLabelMaxWidth = tfWidth;
+ }
+
+ frame.origin.x += (kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft) + tfWidth;
+
+ if (nControlWidth < POPUP_WIDTH_MIN) {
+ nControlWidth = POPUP_WIDTH_MIN;
+ frame.size.width = nControlWidth;
+ [activeControl setFrame:frame];
+ }
+
+ if (nControlWidth > POPUP_WIDTH_MAX) {
+ nControlWidth = POPUP_WIDTH_MAX;
+ frame.size.width = nControlWidth;
+ [activeControl setFrame:frame];
+ }
+
+ //set the max size
+ if (nPopupMaxWidth < nControlWidth) {
+ nPopupMaxWidth = nControlWidth;
+ }
+
+ nControlWidth += tfWidth + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
+ if (nControlHeight < kAquaPopupButtonDefaultHeight) {
+ //maybe the popup has no menu item yet, so set a default height
+ nControlHeight = kAquaPopupButtonDefaultHeight;
+ }
+
+ nControlHeight -= kAquaSpacePopupMenuFrameBoundsDiffV;
+ }
+ else if ([activeControl class] == [NSButton class]) {
+ if (nLoop)
+ {
+ currentHeight += kAquaSpaceBetweenControls;
+ }
+
+ if (nCheckboxMaxWidth < nControlWidth) {
+ nCheckboxMaxWidth = nControlWidth;
+ }
+
+ bButtonControlPresent = true;
+ nControlWidth -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
+ nControlHeight -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
+ }
+
+ // if ((nControlWidth + 2 * kAquaSpaceInsideGroupH) > currentWidth) {
+ // currentWidth = nControlWidth + 2 * kAquaSpaceInsideGroupH;
+ // }
+
+ currentHeight += nControlHeight;
+
+ [m_pUserPane addSubview:activeControl];
+ ++nLoop;
+ }
+
+ SAL_INFO("fpicker.aqua","height after adding all controls: " << currentHeight);
+
+ if (bPopupControlPresent && bButtonControlPresent)
+ {
+ //after a popup button (array) and before a different kind of control we need some extra space instead of the standard
+ currentHeight -= kAquaSpaceBetweenControls;
+ currentHeight += kAquaSpaceAfterPopupButtonsV;
+ SAL_INFO("fpicker.aqua","popup extra space added, currentHeight: " << currentHeight);
+ }
+
+ int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
+
+ currentWidth = nLongestPopupWidth > nCheckboxMaxWidth ? nLongestPopupWidth : nCheckboxMaxWidth;
+ SAL_INFO("fpicker.aqua","longest control width: " << currentWidth);
+
+ currentWidth += 2* kAquaSpaceInsideGroupH;
+
+ if (currentWidth < minRect.size.width)
+ currentWidth = minRect.size.width;
+
+ if (currentHeight < minRect.size.height)
+ currentHeight = minRect.size.height;
+
+ NSRect upRect = NSMakeRect(0, 0, currentWidth, currentHeight );
+ SAL_INFO("fpicker.aqua","setting user pane rect to {" << upRect.origin.x << ", " << upRect.origin.y << ", " << upRect.size.width << ", " << upRect.size.height << "}");
+
+ [m_pUserPane setFrame:upRect];
+
+ layoutControls();
+}
+
+#pragma mark Private / Misc
+
+// Private / Misc
+
+void ControlHelper::createControls()
+{
+ for (int i = 0; i < LIST_LAST; i++) {
+ if (m_bListVisibility[i]) {
+ m_bUserPaneNeeded = true;
+
+ int elementName = getControlElementName([NSPopUpButton class], i);
+ NSString* sLabel = CResourceProvider::getResString(elementName);
+
+ m_pListControls[i] = [NSPopUpButton new];
+
+#define MAP_LIST_( elem ) \
+ case elem: \
+ setLabel(ExtendedFilePickerElementIds::LISTBOX_##elem, sLabel); \
+ break
+
+ switch(i) {
+ MAP_LIST_(VERSION);
+ MAP_LIST_(TEMPLATE);
+ MAP_LIST_(IMAGE_TEMPLATE);
+ MAP_LIST_(IMAGE_ANCHOR);
+ }
+
+ m_aActiveControls.push_back(m_pListControls[i]);
+ } else {
+ m_pListControls[i] = nil;
+ }
+ }
+
+ for (int i = 0/*#i102102*/; i < TOGGLE_LAST; i++) {
+ if (m_bToggleVisibility[i]) {
+ m_bUserPaneNeeded = true;
+
+ int elementName = getControlElementName([NSButton class], i);
+ NSString* sLabel = CResourceProvider::getResString(elementName);
+
+ NSButton *button = [NSButton new];
+ [button setTitle:sLabel];
+
+ [button setButtonType:NSButtonTypeSwitch];
+
+ [button setState:NSControlStateValueOff];
+
+ if (i == AUTOEXTENSION) {
+ [button setTarget:m_pDelegate];
+ [button setAction:@selector(autoextensionChanged:)];
+ }
+
+ m_pToggles[i] = button;
+
+ m_aActiveControls.push_back(m_pToggles[i]);
+ } else {
+ m_pToggles[i] = nil;
+ }
+ }
+
+ //preview is always on with macOS
+ NSControl *pPreviewBox = m_pToggles[PREVIEW];
+ if (pPreviewBox != nil) {
+ [pPreviewBox setEnabled:NO];
+ [static_cast<NSButton*>(pPreviewBox) setState:NSControlStateValueOn];
+ }
+}
+
+#define TOGGLE_ELEMENT( elem ) \
+case elem: \
+ nReturn = CHECKBOX_##elem; \
+ return nReturn
+#define LIST_ELEMENT( elem ) \
+case elem: \
+ nReturn = LISTBOX_##elem##_LABEL; \
+ return nReturn
+
+int ControlHelper::getControlElementName(const Class aClazz, const int nControlId)
+{
+ int nReturn = -1;
+ if (aClazz == [NSButton class])
+ {
+ switch (nControlId) {
+ TOGGLE_ELEMENT( AUTOEXTENSION );
+ TOGGLE_ELEMENT( PASSWORD );
+ TOGGLE_ELEMENT( FILTEROPTIONS );
+ TOGGLE_ELEMENT( READONLY );
+ TOGGLE_ELEMENT( LINK );
+ TOGGLE_ELEMENT( PREVIEW );
+ TOGGLE_ELEMENT( SELECTION );
+ }
+ }
+ else if (aClazz == [NSPopUpButton class])
+ {
+ switch (nControlId) {
+ LIST_ELEMENT( VERSION );
+ LIST_ELEMENT( TEMPLATE );
+ LIST_ELEMENT( IMAGE_TEMPLATE );
+ LIST_ELEMENT( IMAGE_ANCHOR );
+ }
+ }
+
+ return nReturn;
+}
+
+void ControlHelper::HandleSetListValue(const NSControl* pControl, const sal_Int16 nControlAction, const uno::Any& rValue)
+{
+ if ([pControl class] != [NSPopUpButton class]) {
+ SAL_INFO("fpicker.aqua","not a popup menu");
+ return;
+ }
+
+ NSPopUpButton *pButton = static_cast<NSPopUpButton*>(pControl);
+ NSMenu *rMenu = [pButton menu];
+ if (nil == rMenu) {
+ SAL_INFO("fpicker.aqua","button has no menu");
+ return;
+ }
+
+ switch (nControlAction)
+ {
+ case ControlActions::ADD_ITEM:
+ {
+ SAL_INFO("fpicker.aqua","ADD_ITEMS");
+ OUString sItem;
+ rValue >>= sItem;
+
+ NSString* sCFItem = [NSString stringWithOUString:sItem];
+ SAL_INFO("fpicker.aqua","Adding menu item: " << sItem);
+ [pButton addItemWithTitle:sCFItem];
+ }
+ break;
+ case ControlActions::ADD_ITEMS:
+ {
+ SAL_INFO("fpicker.aqua","ADD_ITEMS");
+ uno::Sequence< OUString > aStringList;
+ rValue >>= aStringList;
+ sal_Int32 nItemCount = aStringList.getLength();
+ for (sal_Int32 i = 0; i < nItemCount; ++i)
+ {
+ NSString* sCFItem = [NSString stringWithOUString:aStringList[i]];
+ SAL_INFO("fpicker.aqua","Adding menu item: " << aStringList[i]);
+ [pButton addItemWithTitle:sCFItem];
+ }
+ }
+ break;
+ case ControlActions::DELETE_ITEM:
+ {
+ SAL_INFO("fpicker.aqua","DELETE_ITEM");
+ sal_Int32 nPos = -1;
+ rValue >>= nPos;
+ SAL_INFO("fpicker.aqua","Deleting item at position " << (nPos));
+ [rMenu removeItemAtIndex:nPos];
+ }
+ break;
+ case ControlActions::DELETE_ITEMS:
+ {
+ SAL_INFO("fpicker.aqua","DELETE_ITEMS");
+ int nItems = [rMenu numberOfItems];
+ if (nItems == 0) {
+ SAL_INFO("fpicker.aqua","no menu items to delete");
+ return;
+ }
+ for(sal_Int32 i = 0; i < nItems; i++) {
+ [rMenu removeItemAtIndex:i];
+ }
+ }
+ break;
+ case ControlActions::SET_SELECT_ITEM:
+ {
+ sal_Int32 nPos = -1;
+ rValue >>= nPos;
+ SAL_INFO("fpicker.aqua","Selecting item at position " << nPos);
+ [pButton selectItemAtIndex:nPos];
+ }
+ break;
+ default:
+ SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
+ break;
+ }
+
+ layoutControls();
+}
+
+// cf. offapi/com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.idl
+NSControl* ControlHelper::getControl( const sal_Int16 nControlId ) const
+{
+ NSControl* pWidget = nil;
+
+#define MAP_TOGGLE( elem ) \
+case ExtendedFilePickerElementIds::CHECKBOX_##elem: \
+ pWidget = m_pToggles[elem]; \
+ break
+
+#define MAP_LIST( elem ) \
+case ExtendedFilePickerElementIds::LISTBOX_##elem: \
+ pWidget = m_pListControls[elem]; \
+ break
+
+#define MAP_LIST_LABEL( elem ) \
+case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \
+ pWidget = m_pListControls[elem]; \
+ break
+
+ switch( nControlId )
+ {
+ MAP_TOGGLE( AUTOEXTENSION );
+ MAP_TOGGLE( PASSWORD );
+ MAP_TOGGLE( FILTEROPTIONS );
+ MAP_TOGGLE( READONLY );
+ MAP_TOGGLE( LINK );
+ MAP_TOGGLE( PREVIEW );
+ MAP_TOGGLE( SELECTION );
+ //MAP_BUTTON( PLAY );
+ MAP_LIST( VERSION );
+ MAP_LIST( TEMPLATE );
+ MAP_LIST( IMAGE_TEMPLATE );
+ MAP_LIST( IMAGE_ANCHOR );
+ MAP_LIST_LABEL( VERSION );
+ MAP_LIST_LABEL( TEMPLATE );
+ MAP_LIST_LABEL( IMAGE_TEMPLATE );
+ MAP_LIST_LABEL( IMAGE_ANCHOR );
+ default:
+ SAL_INFO("fpicker.aqua","Handle unknown control " << nControlId);
+ break;
+ }
+#undef MAP
+
+ return pWidget;
+}
+
+void ControlHelper::layoutControls()
+{
+ SolarMutexGuard aGuard;
+
+ if (nil == m_pUserPane) {
+ SAL_INFO("fpicker.aqua","no user pane to layout");
+ return;
+ }
+
+ if (m_bIsUserPaneLaidOut) {
+ SAL_INFO("fpicker.aqua","user pane already laid out");
+ return;
+ }
+
+ NSRect userPaneRect = [m_pUserPane frame];
+ SAL_INFO("fpicker.aqua","userPane frame: {" << userPaneRect.origin.x << ", " << userPaneRect.origin.y << ", " << userPaneRect.size.width << ", " << userPaneRect.size.height << "}");
+
+ int nUsableWidth = userPaneRect.size.width;
+
+ //NOTE: NSView's coordinate system starts in the lower left hand corner but we start adding controls from the top,
+ // so we subtract from the vertical position as we make our way down the pane.
+ int currenttop = userPaneRect.size.height;
+ int nCheckboxMaxWidth = 0;
+ int nPopupMaxWidth = 0;
+ int nPopupLabelMaxWidth = 0;
+
+ //first loop to determine max sizes
+ for (auto const& activeControl : m_aActiveControls)
+ {
+
+ NSRect controlRect = [activeControl frame];
+ int nControlWidth = controlRect.size.width;
+
+ Class aSubType = [activeControl class];
+ if (aSubType == [NSPopUpButton class]) {
+ if (nPopupMaxWidth < nControlWidth) {
+ nPopupMaxWidth = nControlWidth;
+ }
+ NSTextField *label = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)];
+ NSRect labelFrame = [label frame];
+ int nLabelWidth = labelFrame.size.width;
+ if (nPopupLabelMaxWidth < nLabelWidth) {
+ nPopupLabelMaxWidth = nLabelWidth;
+ }
+ } else {
+ if (nCheckboxMaxWidth < nControlWidth) {
+ nCheckboxMaxWidth = nControlWidth;
+ }
+ }
+ }
+
+ int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
+ SAL_INFO("fpicker.aqua","longest popup width: " << nLongestPopupWidth);
+
+ NSControl* previousControl = nil;
+
+ int nDistBetweenControls = 0;
+
+ for (auto const& activeControl : m_aActiveControls)
+ {
+ //get the control's bounds
+ NSRect controlRect = [activeControl frame];
+ int nControlHeight = controlRect.size.height;
+
+ //subtract the height from the current vertical position, because the control's bounds origin rect will be its lower left hand corner
+ currenttop -= nControlHeight;
+
+ Class aSubType = [activeControl class];
+
+ //add space between the previous control and this control according to Apple's HIG
+ nDistBetweenControls = getVerticalDistance(previousControl, activeControl);
+ SAL_INFO("fpicker.aqua","vertical distance: " << nDistBetweenControls);
+ currenttop -= nDistBetweenControls;
+
+ previousControl = activeControl;
+
+ if (aSubType == [NSPopUpButton class]) {
+ //move vertically up some pixels to space the controls between their real (visual) bounds
+ currenttop += kAquaSpacePopupMenuFrameBoundsDiffTop;//from top
+
+ //get the corresponding popup label
+ NSTextField *label = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)];
+ NSRect labelFrame = [label frame];
+ int totalWidth = nPopupMaxWidth + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
+ SAL_INFO("fpicker.aqua","totalWidth: " << totalWidth);
+ //let's center popups
+ int left = (nUsableWidth + nLongestPopupWidth) / 2 - totalWidth;
+ SAL_INFO("fpicker.aqua","left: " << left);
+ labelFrame.origin.x = left;
+ labelFrame.origin.y = currenttop + kAquaSpaceLabelPopupDiffV;
+ SAL_INFO("fpicker.aqua","setting label at: {" << labelFrame.origin.x << ", " << labelFrame.origin.y << ", " << labelFrame.size.width << ", " << labelFrame.size.height << "}");
+ [label setFrame:labelFrame];
+
+ controlRect.origin.x = left + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
+ controlRect.origin.y = currenttop;
+ controlRect.size.width = nPopupMaxWidth;
+ SAL_INFO("fpicker.aqua","setting popup at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}");
+ [activeControl setFrame:controlRect];
+
+ //add some space to place the vertical position right below the popup's visual bounds
+ currenttop += kAquaSpacePopupMenuFrameBoundsDiffBottom;
+ } else {
+ currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;//from top
+
+ int left = (nUsableWidth - nCheckboxMaxWidth) / 2;
+ controlRect.origin.x = left;
+ controlRect.origin.y = currenttop;
+ controlRect.size.width = nPopupMaxWidth;
+ [activeControl setFrame:controlRect];
+ SAL_INFO("fpicker.aqua","setting checkbox at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}");
+
+ currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;
+ }
+ }
+
+ m_bIsUserPaneLaidOut = true;
+}
+
+void ControlHelper::createFilterControl()
+{
+ NSString* sLabel = CResourceProvider::getResString(CommonFilePickerElementIds::LISTBOX_FILTER_LABEL);
+
+ m_pFilterControl = [NSPopUpButton new];
+
+ [m_pFilterControl setAction:@selector(filterSelectedAtIndex:)];
+ [m_pFilterControl setTarget:m_pDelegate];
+
+ NSMenu *menu = [m_pFilterControl menu];
+
+ for (auto const& filterName : *m_pFilterHelper->getFilterNames())
+ {
+ SAL_INFO("fpicker.aqua","adding filter name: " << [filterName UTF8String]);
+ if ([filterName isEqualToString:@"-"]) {
+ [menu addItem:[NSMenuItem separatorItem]];
+ }
+ else {
+ [m_pFilterControl addItemWithTitle:filterName];
+ }
+ }
+
+ // always add the filter as first item
+ m_aActiveControls.push_front(m_pFilterControl);
+ m_aMapListLabels[m_pFilterControl] = [sLabel retain];
+}
+
+int ControlHelper::getVerticalDistance(const NSControl* first, const NSControl* second)
+{
+ if (first == nil) {
+ return kAquaSpaceBoxFrameViewDiffTop;
+ }
+ else if (second == nil) {
+ return kAquaSpaceBoxFrameViewDiffBottom;
+ }
+ else {
+ Class firstClass = [first class];
+ Class secondClass = [second class];
+
+ if (firstClass == [NSPopUpButton class]) {
+ if (secondClass == [NSPopUpButton class]) {
+ return kAquaSpaceBetweenPopupMenus;
+ }
+ else {
+ return kAquaSpaceAfterPopupButtonsV;
+ }
+ }
+
+ return kAquaSpaceBetweenControls;
+ }
+}
+
+void ControlHelper::updateFilterUI()
+{
+ if (!m_bIsFilterControlNeeded || m_pFilterHelper == nullptr) {
+ SAL_INFO("fpicker.aqua","no filter control needed or no filter helper present");
+ return;
+ }
+
+ int index = m_pFilterHelper->getCurrentFilterIndex();
+
+ if (m_pFilterControl == nil) {
+ createFilterControl();
+ }
+
+ [m_pFilterControl selectItemAtIndex:index];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/FilterHelper.hxx b/fpicker/source/aqua/FilterHelper.hxx
new file mode 100644
index 000000000..21cb9c4b4
--- /dev/null
+++ b/fpicker/source/aqua/FilterHelper.hxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <com/sun/star/uno/RuntimeException.hpp>
+
+#include <list>
+#include <string_view>
+#include <vector>
+
+#include <premac.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+typedef css::beans::StringPair UnoFilterEntry;
+typedef css::uno::Sequence< UnoFilterEntry > UnoFilterList; // can be transported more effectively
+typedef ::std::list<NSString *> NSStringList;
+typedef ::std::list<OUString> OUStringList;
+
+struct FilterEntry
+{
+protected:
+ OUString m_sTitle;
+ OUStringList m_sFilterSuffixList;
+ UnoFilterList m_aSubFilters;
+
+public:
+ FilterEntry( const OUString& _rTitle, const OUStringList _rFilter )
+ : m_sTitle( _rTitle )
+ , m_sFilterSuffixList( _rFilter )
+ {
+ }
+
+ FilterEntry( const OUString& _rTitle, const UnoFilterList& _rSubFilters );
+
+ OUString const & getTitle() const { return m_sTitle; }
+ OUStringList const & getFilterSuffixList() const { return m_sFilterSuffixList; }
+
+ /// determines if the filter has sub filter (i.e., the filter is a filter group in real)
+ bool hasSubFilters( ) const;
+
+ /** retrieves the filters belonging to the entry
+ @return
+ the number of sub filters
+ */
+ sal_Int32 getSubFilters( UnoFilterList& _rSubFilterList );
+
+ // helpers for iterating the sub filters
+ const UnoFilterEntry* beginSubFilters() const { return m_aSubFilters.getConstArray(); }
+ const UnoFilterEntry* endSubFilters() const { return m_aSubFilters.getConstArray() + m_aSubFilters.getLength(); }
+};
+
+typedef ::std::vector < FilterEntry > FilterList;
+
+class FilterHelper {
+
+public:
+ FilterHelper();
+ virtual ~FilterHelper();
+
+ //XFilterManager delegates
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void appendFilter( const OUString& aTitle, std::u16string_view aFilter );
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void setCurrentFilter( const OUString& aTitle );
+
+ /// @throws css::uno::RuntimeException
+ OUString getCurrentFilter( );
+
+ //XFilterGroupManager delegates
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void appendFilterGroup( const css::uno::Sequence< css::beans::StringPair >& aFilters );
+
+
+ //accessor
+ FilterList* getFilterList();
+ NSStringList* getFilterNames();
+
+ //misc
+ void SetCurFilter( const OUString& rFilter );
+ void SetFilterAtIndex(unsigned index);
+ OUStringList getCurrentFilterSuffixList();
+ int getCurrentFilterIndex();
+ void SetFilters();
+ bool filenameMatchesFilter(NSString * sFilename);
+
+private:
+ FilterList *m_pFilterList;
+ OUString m_aCurrentFilter;
+ NSStringList *m_pFilterNames;
+
+ bool FilterNameExists( const OUString& rTitle );
+ bool FilterNameExists( const UnoFilterList& _rGroupedFilters );
+
+ void ensureFilterList( const OUString& _rInitialCurrentFilter );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/FilterHelper.mm b/fpicker/source/aqua/FilterHelper.mm
new file mode 100644
index 000000000..f11d8447d
--- /dev/null
+++ b/fpicker/source/aqua/FilterHelper.mm
@@ -0,0 +1,443 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <cstddef>
+#include <string_view>
+#include <o3tl/string_view.hxx>
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+
+#include "NSString_OOoAdditions.hxx"
+#include "NSURL_OOoAdditions.hxx"
+
+#include "FilterHelper.hxx"
+
+namespace {
+
+void fillSuffixList(OUStringList& aSuffixList, std::u16string_view suffixString) {
+ std::size_t nIndex = 0;
+ do {
+ std::u16string_view aToken = o3tl::getToken( suffixString, u';', nIndex );
+ aSuffixList.push_back(OUString(aToken.substr(1)));
+ } while ( nIndex != std::u16string_view::npos );
+}
+
+}
+
+#pragma mark DEFINES
+
+#pragma mark FilterEntry
+
+FilterEntry::FilterEntry( const OUString& _rTitle, const UnoFilterList& _rSubFilters )
+:m_sTitle( _rTitle )
+,m_aSubFilters( _rSubFilters )
+{
+}
+
+
+bool FilterEntry::hasSubFilters() const
+{
+ bool bReturn = ( 0 < m_aSubFilters.getLength() );
+
+ return bReturn;
+}
+
+
+sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
+{
+ _rSubFilterList = m_aSubFilters;
+ sal_Int32 nReturn = m_aSubFilters.getLength();
+
+ return nReturn;
+}
+
+#pragma mark statics
+static bool
+isFilterString( std::u16string_view rFilterString, std::u16string_view pMatch )
+{
+ std::size_t nIndex = 0;
+ std::u16string_view aToken;
+ bool bIsFilter = true;
+
+ do
+ {
+ aToken = o3tl::getToken( rFilterString, u';', nIndex );
+ if( !o3tl::starts_with( aToken, pMatch ) )
+ {
+ bIsFilter = false;
+ break;
+ }
+ }
+ while( nIndex != std::u16string_view::npos );
+
+ return bIsFilter;
+}
+
+
+
+static OUString
+shrinkFilterName( const OUString& aFilterName, bool bAllowNoStar = false )
+{
+ sal_Int32 nBracketEnd = -1;
+ OUString aRealName(aFilterName);
+
+ for( sal_Int32 i = aRealName.getLength() - 1; i > 0; i-- )
+ {
+ if( aFilterName[i] == ')' )
+ nBracketEnd = i;
+ else if( aFilterName[i] == '(' )
+ {
+ sal_Int32 nBracketLen = nBracketEnd - i;
+ if( nBracketEnd <= 0 )
+ continue;
+ if( isFilterString( aFilterName.subView( i + 1, nBracketLen - 1 ), u"*." ) )
+ aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
+ else if (bAllowNoStar)
+ {
+ if( isFilterString( aFilterName.subView( i + 1, nBracketLen - 1 ), u".") )
+ aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
+ }
+ }
+ }
+
+ return aRealName;
+}
+
+
+namespace {
+
+ struct FilterTitleMatch
+ {
+protected:
+ const OUString rTitle;
+
+public:
+ FilterTitleMatch( const OUString& _rTitle ) : rTitle( _rTitle ) { }
+
+
+ bool operator () ( const FilterEntry& _rEntry )
+ {
+ bool bMatch;
+ if( !_rEntry.hasSubFilters() ) {
+ //first try the complete filter name
+ OUString title = _rEntry.getTitle();
+ bMatch = title.equals(rTitle);
+ if (!bMatch) {
+ //we didn't find a match using the full name, let's give it another
+ //try using the shrunk version
+ OUString aShrunkName = shrinkFilterName( _rEntry.getTitle() ).trim();
+ bMatch = aShrunkName.equals(rTitle);
+ }
+ }
+ else
+ // a filter group -> search the sub filters
+ bMatch =
+ ::std::any_of(_rEntry.beginSubFilters(),
+ _rEntry.endSubFilters(),
+ *this);
+
+ return bMatch;
+ }
+
+ bool operator () ( const UnoFilterEntry& _rEntry )
+ {
+ OUString aShrunkName = shrinkFilterName( _rEntry.First );
+ bool retVal = aShrunkName.equals(rTitle);
+ return retVal;
+ }
+ };
+}
+
+FilterHelper::FilterHelper()
+: m_pFilterList(nullptr)
+, m_pFilterNames(nullptr)
+{
+}
+
+FilterHelper::~FilterHelper()
+{
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ if (nullptr != m_pFilterList) {
+ delete m_pFilterList;
+ }
+
+ if (nullptr != m_pFilterNames) {
+ //we called retain when we added the strings to the list, so we should release them now
+ for (NSStringList::iterator iter = m_pFilterNames->begin(); iter != m_pFilterNames->end(); ++iter) {
+ [*iter release];
+ }
+ delete m_pFilterNames;
+ }
+
+ [pool release];
+}
+
+
+bool FilterHelper::FilterNameExists( const OUString& rTitle )
+{
+ bool bRet = false;
+
+ if( m_pFilterList )
+ bRet =
+ ::std::any_of(m_pFilterList->begin(),
+ m_pFilterList->end(),
+ FilterTitleMatch( rTitle ));
+
+ return bRet;
+}
+
+
+bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
+{
+ bool bRet = false;
+
+ if( m_pFilterList )
+ {
+ const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
+ const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
+ for( ; pStart != pEnd; ++pStart )
+ if( ::std::any_of(m_pFilterList->begin(),
+ m_pFilterList->end(),
+ FilterTitleMatch( pStart->First ) ) )
+ break;
+
+ bRet = (pStart != pEnd);
+ }
+
+ return bRet;
+}
+
+
+void FilterHelper::ensureFilterList( const OUString& _rInitialCurrentFilter )
+{
+ if( nullptr == m_pFilterList )
+ {
+ m_pFilterList = new FilterList;
+
+ // set the first filter to the current filter
+ m_aCurrentFilter = _rInitialCurrentFilter;
+ }
+}
+
+void FilterHelper::SetCurFilter( const OUString& rFilter )
+{
+ SolarMutexGuard aGuard;
+
+ if(!m_aCurrentFilter.equals(rFilter))
+ {
+ m_aCurrentFilter = rFilter;
+ }
+
+}
+
+void FilterHelper::SetFilters()
+{
+ // set the default filter
+ if( m_aCurrentFilter.getLength() > 0 )
+ {
+ SetCurFilter( m_aCurrentFilter );
+ }
+}
+
+void FilterHelper::appendFilter(const OUString& aTitle, std::u16string_view aFilterString)
+{
+ SolarMutexGuard aGuard;
+
+ if( FilterNameExists( aTitle ) ) {
+ throw css::lang::IllegalArgumentException();
+ }
+
+ // ensure that we have a filter list
+ ensureFilterList( aTitle );
+
+ // append the filter
+ OUStringList suffixList;
+ fillSuffixList(suffixList, aFilterString);
+ m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
+}
+
+void FilterHelper::setCurrentFilter( const OUString& aTitle )
+{
+ SetCurFilter(aTitle);
+}
+
+OUString FilterHelper::getCurrentFilter( )
+{
+ OUString sReturn = m_aCurrentFilter;
+
+ return sReturn;
+}
+
+void FilterHelper::appendFilterGroup( const css::uno::Sequence< css::beans::StringPair >& aFilters )
+{
+ SolarMutexGuard aGuard;
+
+ //add a separator if this is not the first group to be added
+ bool bPrependSeparator = m_pFilterList != nullptr;
+
+ // ensure that we have a filter list
+ OUString sInitialCurrentFilter;
+ if( aFilters.getLength() > 0)
+ sInitialCurrentFilter = aFilters[0].First;
+ ensureFilterList( sInitialCurrentFilter );
+
+ // append the filter
+ if (bPrependSeparator) {
+ OUStringList emptyList;
+ m_pFilterList->push_back(FilterEntry("-", emptyList));
+ }
+
+ const css::beans::StringPair* pSubFilters = aFilters.getConstArray();
+ const css::beans::StringPair* pSubFiltersEnd = pSubFilters + aFilters.getLength();
+ for( ; pSubFilters != pSubFiltersEnd; ++pSubFilters ) {
+ appendFilter(pSubFilters->First, pSubFilters->Second);
+ }
+}
+
+bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
+{
+ if (m_aCurrentFilter.isEmpty()) {
+ SAL_WARN("fpicker", "filter name is empty");
+ return true;
+ }
+
+ NSFileManager *manager = [NSFileManager defaultManager];
+ NSDictionary* pAttribs = [manager attributesOfItemAtPath: sFilename error: nil];
+ if( pAttribs )
+ {
+ NSObject* pType = [pAttribs objectForKey: NSFileType];
+ if( pType && [pType isKindOfClass: [NSString class]] )
+ {
+ NSString* pT = static_cast<NSString*>(pType);
+ if( [pT isEqualToString: NSFileTypeDirectory] ||
+ [pT isEqualToString: NSFileTypeSymbolicLink] )
+ return true;
+ }
+ }
+
+ FilterList::iterator filter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
+ if (filter == m_pFilterList->end()) {
+ SAL_WARN("fpicker", "filter not found in list");
+ return true;
+ }
+
+ OUStringList suffixList = filter->getFilterSuffixList();
+
+ {
+ OUString aName = [sFilename OUString];
+ for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); ++iter) {
+ if (*iter == ".*" || aName.endsWithIgnoreAsciiCase(*iter)) {
+ return true;
+ }
+ }
+ }
+
+ // might be an alias
+ NSString* pResolved = resolveAlias( sFilename );
+ if( pResolved )
+ {
+ bool bResult = filenameMatchesFilter( pResolved );
+ [pResolved autorelease];
+ if( bResult )
+ return true;
+ }
+
+ return false;
+}
+
+FilterList* FilterHelper::getFilterList()
+{
+ return m_pFilterList;
+}
+
+NSStringList* FilterHelper::getFilterNames()
+{
+ if (nullptr == m_pFilterList)
+ return nullptr;
+ if (nullptr == m_pFilterNames) {
+ //build filter names list
+ m_pFilterNames = new NSStringList;
+ for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter) {
+ m_pFilterNames->push_back([[NSString stringWithOUString:iter->getTitle()] retain]);
+ }
+ }
+
+ return m_pFilterNames;
+}
+
+void FilterHelper::SetFilterAtIndex(unsigned index)
+{
+ if (m_pFilterList->size() <= index) {
+ index = 0;
+ }
+ FilterEntry entry = m_pFilterList->at(index);
+ SetCurFilter(entry.getTitle());
+}
+
+int FilterHelper::getCurrentFilterIndex()
+{
+ int result = 0;//default to first filter
+ if (m_aCurrentFilter.getLength() > 0) {
+ int i = 0;
+ for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter, ++i) {
+ OUString aTitle = iter->getTitle();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ result = i;
+ break;
+ } else {
+ aTitle = shrinkFilterName(aTitle).trim();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ result = i;
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+OUStringList FilterHelper::getCurrentFilterSuffixList()
+{
+ OUStringList retVal;
+ if (m_aCurrentFilter.getLength() > 0) {
+ for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter) {
+ OUString aTitle = iter->getTitle();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ retVal = iter->getFilterSuffixList();
+ break;
+ } else {
+ aTitle = shrinkFilterName(aTitle).trim();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ retVal = iter->getFilterSuffixList();
+ break;
+ }
+ }
+ }
+ }
+
+ return retVal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/NSString_OOoAdditions.hxx b/fpicker/source/aqua/NSString_OOoAdditions.hxx
new file mode 100644
index 000000000..eac18d46f
--- /dev/null
+++ b/fpicker/source/aqua/NSString_OOoAdditions.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#include <postmac.h>
+#include <rtl/ustring.hxx>
+
+//for Cocoa types
+@interface NSString (OOoAdditions)
++ (id)stringWithOUString:(const OUString&)ouString;
+- (OUString)OUString;
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/NSString_OOoAdditions.mm b/fpicker/source/aqua/NSString_OOoAdditions.mm
new file mode 100644
index 000000000..23ae6bc5c
--- /dev/null
+++ b/fpicker/source/aqua/NSString_OOoAdditions.mm
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "NSString_OOoAdditions.hxx"
+
+@implementation NSString (OOoAdditions)
+
++ (id) stringWithOUString:(const OUString&)ouString
+{
+ NSString *string = [[NSString alloc] initWithCharacters:reinterpret_cast<unichar const *>(ouString.getStr()) length:ouString.getLength()];
+
+ return [string autorelease];
+}
+
+- (OUString) OUString
+{
+ unsigned int nFileNameLength = [self length];
+
+ UniChar unichars[nFileNameLength+1];
+
+ //'close' the string buffer correctly
+ unichars[nFileNameLength] = '\0';
+
+ [self getCharacters:unichars];
+
+ return OUString(reinterpret_cast<sal_Unicode *>(unichars));
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/NSURL_OOoAdditions.hxx b/fpicker/source/aqua/NSURL_OOoAdditions.hxx
new file mode 100644
index 000000000..f63ccfdcc
--- /dev/null
+++ b/fpicker/source/aqua/NSURL_OOoAdditions.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <premac.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <postmac.h>
+#include <rtl/ustring.hxx>
+
+@interface NSURL (OOoAdditions)
+- (OUString)OUString;
+@end
+
+/*
+ returns the resolved string if there was an alias
+ if there was no alias, nil is returned
+*/
+
+NSString* resolveAlias(NSString* i_pSystemPath);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/NSURL_OOoAdditions.mm b/fpicker/source/aqua/NSURL_OOoAdditions.mm
new file mode 100644
index 000000000..5a3737e9b
--- /dev/null
+++ b/fpicker/source/aqua/NSURL_OOoAdditions.mm
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "NSString_OOoAdditions.hxx"
+#include "NSURL_OOoAdditions.hxx"
+#include <sal/log.hxx>
+
+@implementation NSURL (OOoAdditions)
+- (OUString) OUString
+{
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ NSString *sURLString = nil;
+
+ SAL_INFO("fpicker.aqua","Extracting the full path of an item");
+ sURLString = [self absoluteString];
+ [sURLString retain];
+
+ OUString sResult = [sURLString OUString];
+ [sURLString release];
+
+ [pool release];
+
+ return sResult;
+}
+@end
+
+NSString* resolveAlias( NSString* i_pSystemPath )
+{
+ NSString* pResolvedPath = nil;
+ CFURLRef rUrl = CFURLCreateWithFileSystemPath( kCFAllocatorDefault,
+ reinterpret_cast<CFStringRef>(i_pSystemPath),
+ kCFURLPOSIXPathStyle, false);
+ if( rUrl != nullptr )
+ {
+ CFErrorRef rError;
+ CFDataRef rBookmark = CFURLCreateBookmarkDataFromFile( nullptr, rUrl, &rError );
+ CFRelease( rUrl );
+ if( rBookmark == nullptr )
+ {
+ CFRelease( rError );
+ }
+ else
+ {
+ Boolean bIsStale;
+ CFURLRef rResolvedUrl = CFURLCreateByResolvingBookmarkData( kCFAllocatorDefault, rBookmark, kCFBookmarkResolutionWithoutUIMask,
+ nullptr, nullptr, &bIsStale, &rError );
+ CFRelease( rBookmark );
+ if( rResolvedUrl == nullptr )
+ {
+ CFRelease( rError );
+ }
+ else
+ {
+ pResolvedPath = const_cast<NSString*>(reinterpret_cast<NSString const *>(CFURLCopyFileSystemPath( rResolvedUrl, kCFURLPOSIXPathStyle )));
+ CFRelease( rResolvedUrl );
+ }
+ }
+ }
+
+ return pResolvedPath;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaConstants.h b/fpicker/source/aqua/SalAquaConstants.h
new file mode 100644
index 000000000..3fd14d536
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaConstants.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#define kAppFourCharCode 'LibO'
+#define kControlPropertyTracking 'Trck'
+#define kControlPropertyLastPartCode 'LsPc'
+#define kControlPropertySubType 'SuTy'
+#define kPopupControlPropertyTitleWidth 'PoTW'
+
+#define kAquaSpaceBetweenControls (8)
+#define kAquaSpaceBetweenPopupMenus (10)
+
+#define kAquaSpaceInsideGroupH (16)
+#define kAquaSpaceInsideGroupV (11)
+
+#define kAquaSpaceBoxFrameViewDiffTop (7)
+#define kAquaSpaceBoxFrameViewDiffLeft (7)
+#define kAquaSpaceBoxFrameViewDiffBottom (9)
+#define kAquaSpaceBoxFrameViewDiffRight (7)
+
+#define kAquaSpaceButtonFrameBoundsDiff (6)
+#define kAquaSpaceSwitchButtonFrameBoundsDiff (2)
+
+#define kAquaSpacePopupMenuFrameBoundsDiffTop (2)
+#define kAquaSpacePopupMenuFrameBoundsDiffBottom (4)
+#define kAquaSpacePopupMenuFrameBoundsDiffV \
+ (kAquaSpacePopupMenuFrameBoundsDiffTop + kAquaSpacePopupMenuFrameBoundsDiffBottom)
+#define kAquaSpacePopupMenuFrameBoundsDiffLeft (3)
+
+#define kAquaSpaceLabelFrameBoundsDiffH (3)
+#define kAquaSpaceLabelPopupDiffV (6)
+#define kAquaSpaceAfterPopupButtonsV (20)
+
+#define kAquaPopupButtonDefaultHeight (26)
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaFilePicker.hxx b/fpicker/source/aqua/SalAquaFilePicker.hxx
new file mode 100644
index 000000000..3cf13fedb
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaFilePicker.hxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+
+#include "SalAquaPicker.hxx"
+
+#include <rtl/ustring.hxx>
+#include "FilterHelper.hxx"
+#include "AquaFilePickerDelegate.hxx"
+
+// Implementation class for the XFilePicker Interface
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::ui::dialogs::XFilePicker3,
+ css::ui::dialogs::XFilePickerControlAccess,
+ css::lang::XInitialization,
+ css::lang::XServiceInfo > SalAquaFilePicker_Base;
+
+class SalAquaFilePicker :
+ public SalAquaPicker,
+ public SalAquaFilePicker_Base
+{
+public:
+
+ // constructor
+ SalAquaFilePicker();
+
+ // XFilePickerNotifier
+
+ virtual void SAL_CALL addFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener ) override;
+ virtual void SAL_CALL removeFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener ) override;
+
+ // XExecutableDialog functions
+
+ virtual void SAL_CALL setTitle( const OUString& aTitle ) override;
+
+ virtual sal_Int16 SAL_CALL execute( ) override;
+
+ // XFilePicker functions
+
+ virtual void SAL_CALL setMultiSelectionMode( sal_Bool bMode ) override;
+
+ virtual void SAL_CALL setDefaultName( const OUString& aName ) override;
+
+ virtual void SAL_CALL setDisplayDirectory( const OUString& aDirectory ) override;
+
+ virtual OUString SAL_CALL getDisplayDirectory( ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getFiles( ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSelectedFiles( ) override;
+
+ // XFilterManager functions
+
+ virtual void SAL_CALL appendFilter( const OUString& aTitle, const OUString& aFilter ) override;
+
+ virtual void SAL_CALL setCurrentFilter( const OUString& aTitle ) override;
+
+ virtual OUString SAL_CALL getCurrentFilter( ) override;
+
+ // XFilterGroupManager functions
+
+ virtual void SAL_CALL appendFilterGroup( const OUString& sGroupTitle, const css::uno::Sequence< css::beans::StringPair >& aFilters ) override;
+
+ // XFilePickerControlAccess functions
+
+ virtual void SAL_CALL setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const css::uno::Any& aValue ) override;
+
+ virtual css::uno::Any SAL_CALL getValue( sal_Int16 aControlId, sal_Int16 aControlAction ) override;
+
+ virtual void SAL_CALL enableControl( sal_Int16 nControlId, sal_Bool bEnable ) override;
+
+ virtual void SAL_CALL setLabel( sal_Int16 nControlId, const OUString& aLabel ) override;
+
+ virtual OUString SAL_CALL getLabel( sal_Int16 nControlId ) override;
+
+ // XInitialization
+
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XCancellable
+
+ virtual void SAL_CALL cancel( ) override;
+
+ // XEventListener
+
+ using cppu::WeakComponentImplHelperBase::disposing;
+ /// @throws css::uno::RuntimeException
+ virtual void disposing( const css::lang::EventObject& aEvent );
+
+ // XServiceInfo
+
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // FilePicker Event functions
+
+ void fileSelectionChanged( css::ui::dialogs::FilePickerEvent aEvent );
+ void directoryChanged( css::ui::dialogs::FilePickerEvent aEvent );
+ // OUString SAL_CALL helpRequested( css::ui::dialogs::FilePickerEvent aEvent ) const;
+ void controlStateChanged( css::ui::dialogs::FilePickerEvent aEvent );
+ void dialogSizeChanged( );
+
+ AquaFilePickerDelegate * getDelegate() {
+ return m_pDelegate;
+ }
+
+ OUString const & getSaveFileName() {
+ return m_sSaveFileName;
+ }
+
+private:
+ SalAquaFilePicker( const SalAquaFilePicker& ) = delete;
+ SalAquaFilePicker& operator=( const SalAquaFilePicker& ) = delete;
+
+ virtual void ensureFilterHelper();
+
+ css::uno::Reference< css::ui::dialogs::XFilePickerListener > m_xListener;
+ FilterHelper *m_pFilterHelper;
+ OUString m_sSaveFileName;
+ AquaFilePickerDelegate *m_pDelegate;
+
+ void updateFilterUI();
+ void updateSaveFileNameExtension();
+
+public:
+
+ virtual ~SalAquaFilePicker() override;
+
+ void filterControlChanged();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaFilePicker.mm b/fpicker/source/aqua/SalAquaFilePicker.mm
new file mode 100644
index 000000000..01e6f80e9
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaFilePicker.mm
@@ -0,0 +1,590 @@
+/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <cppuhelper/interfacecontainer.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/ControlActions.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+
+#include "resourceprovider.hxx"
+
+#include <osl/file.hxx>
+#include "NSString_OOoAdditions.hxx"
+#include "NSURL_OOoAdditions.hxx"
+
+#include <iostream>
+
+#include "SalAquaFilePicker.hxx"
+
+#include <objc/objc-runtime.h>
+
+#pragma mark DEFINES
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
+using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
+using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+
+namespace
+{
+ uno::Sequence<OUString> FilePicker_getSupportedServiceNames()
+ {
+ return { "com.sun.star.ui.dialogs.FilePicker",
+ "com.sun.star.ui.dialogs.SystemFilePicker",
+ "com.sun.star.ui.dialogs.AquaFilePicker" };
+ }
+}
+
+#pragma mark Constructor
+
+SalAquaFilePicker::SalAquaFilePicker()
+ : SalAquaFilePicker_Base( m_rbHelperMtx )
+ , m_pFilterHelper( nullptr )
+{
+ m_pDelegate = [[AquaFilePickerDelegate alloc] initWithFilePicker:this];
+ m_pControlHelper->setFilePickerDelegate(m_pDelegate);
+}
+
+SalAquaFilePicker::~SalAquaFilePicker()
+{
+ if (nullptr != m_pFilterHelper)
+ delete m_pFilterHelper;
+
+ [m_pDelegate release];
+}
+
+
+#pragma mark XFilePickerNotifier
+
+void SAL_CALL SalAquaFilePicker::addFilePickerListener( const uno::Reference<XFilePickerListener>& xListener )
+{
+ SolarMutexGuard aGuard;
+ m_xListener = xListener;
+}
+
+void SAL_CALL SalAquaFilePicker::removeFilePickerListener( const uno::Reference<XFilePickerListener>& )
+{
+ SolarMutexGuard aGuard;
+ m_xListener.clear();
+}
+
+#pragma mark XAsynchronousExecutableDialog
+
+void SAL_CALL SalAquaFilePicker::setTitle( const OUString& aTitle )
+{
+ SolarMutexGuard aGuard;
+ implsetTitle(aTitle);
+}
+
+sal_Int16 SAL_CALL SalAquaFilePicker::execute()
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int16 retVal = 0;
+
+ implInitialize();
+
+ // if m_pDialog is nil after initialization, something must have gone wrong before
+ // or there was no initialization (see issue https://bz.apache.org/ooo/show_bug.cgi?id=100214)
+ if (m_pDialog == nil) {
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ }
+
+ if (m_pFilterHelper) {
+ m_pFilterHelper->SetFilters();
+ }
+
+ if (m_nDialogType == NAVIGATIONSERVICES_SAVE) {
+ if (m_sSaveFileName.getLength() == 0) {
+ //if no filename is set, NavigationServices will set the name to "untitled". We don't want this!
+ //So let's try to get the window title to get the real untitled name
+ NSWindow *frontWindow = [NSApp keyWindow];
+ if (nullptr != frontWindow) {
+ NSString *windowTitle = [frontWindow title];
+ if (windowTitle != nil) {
+ OUString ouName = [windowTitle OUString];
+ //a window title will typically be something like "Untitled1 - OpenOffice.org Writer"
+ //but we only want the "Untitled1" part of it
+ sal_Int32 indexOfDash = ouName.indexOf(" - ");
+ if (indexOfDash > -1) {
+ m_sSaveFileName = ouName.copy(0,indexOfDash);
+ if (m_sSaveFileName.getLength() > 0) {
+ setDefaultName(m_sSaveFileName);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //Set the delegate to be notified of certain events
+
+ [m_pDialog setDelegate:m_pDelegate];
+
+ int nStatus = runandwaitforresult();
+
+ [m_pDialog setDelegate:nil];
+
+ switch( nStatus )
+ {
+ case NSModalResponseOK:
+ retVal = ExecutableDialogResults::OK;
+ break;
+
+ case NSModalResponseCancel:
+ retVal = ExecutableDialogResults::CANCEL;
+ break;
+
+ default:
+ throw uno::RuntimeException(
+ "The dialog returned with an unknown result!",
+ static_cast<XFilePicker*>( static_cast<XFilePicker3*>( this ) ));
+ break;
+ }
+
+ return retVal;
+}
+
+
+#pragma mark XFilePicker
+
+void SAL_CALL SalAquaFilePicker::setMultiSelectionMode( sal_Bool /* bMode */ )
+{
+ SolarMutexGuard aGuard;
+
+ if (m_nDialogType == NAVIGATIONSERVICES_OPEN) {
+ [static_cast<NSOpenPanel*>(m_pDialog) setAllowsMultipleSelection:YES];
+ }
+}
+
+void SAL_CALL SalAquaFilePicker::setDefaultName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ m_sSaveFileName = aName;
+}
+
+void SAL_CALL SalAquaFilePicker::setDisplayDirectory( const OUString& rDirectory )
+{
+ SolarMutexGuard aGuard;
+
+ implsetDisplayDirectory(rDirectory);
+}
+
+OUString SAL_CALL SalAquaFilePicker::getDisplayDirectory()
+{
+ OUString retVal = implgetDisplayDirectory();
+
+ return retVal;
+}
+
+uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getFiles()
+{
+ uno::Sequence< OUString > aSelectedFiles = getSelectedFiles();
+ // multiselection doesn't really work with getFiles
+ // so just retrieve the first url
+ if (aSelectedFiles.getLength() > 1)
+ aSelectedFiles.realloc(1);
+
+ return aSelectedFiles;
+}
+
+uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getSelectedFiles()
+{
+ SolarMutexGuard aGuard;
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ static NSUserDefaults *userDefaults;
+ static bool triedUserDefaults = false;
+
+ if (!triedUserDefaults)
+ {
+ userDefaults = [NSUserDefaults standardUserDefaults];
+ triedUserDefaults = true;
+ }
+#endif
+
+ NSArray *files = nil;
+ if (m_nDialogType == NAVIGATIONSERVICES_OPEN) {
+ files = [static_cast<NSOpenPanel*>(m_pDialog) URLs];
+ }
+ else if (m_nDialogType == NAVIGATIONSERVICES_SAVE) {
+ files = [NSArray arrayWithObjects:[m_pDialog URL], nil];
+ }
+
+ NSUInteger nFiles = [files count];
+ SAL_INFO("fpicker.aqua", "# of items: " << nFiles);
+
+ uno::Sequence< OUString > aSelectedFiles(nFiles);
+ OUString* pSelectedFiles = aSelectedFiles.getArray();
+
+ for(NSUInteger nIndex = 0; nIndex < nFiles; nIndex += 1)
+ {
+ NSURL *url = [files objectAtIndex:nIndex];
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ if (userDefaults != NULL &&
+ [url respondsToSelector:@selector(bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:)])
+ {
+ // In the case of "Save As" when the user has input a new
+ // file name, this call will return nil, as bookmarks can
+ // (naturally) only be created for existing file system
+ // objects. In that case, code at a much lower level, in
+ // sal, takes care of creating a bookmark when a new file
+ // has been created outside the sandbox.
+ NSData *data = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
+ includingResourceValuesForKeys:nil
+ relativeToURL:nil
+ error:nil];
+ if (data != NULL)
+ {
+ [userDefaults setObject:data
+ forKey:[@"bookmarkFor:" stringByAppendingString:[url absoluteString]]];
+ }
+ }
+#endif
+
+ OUString sFileOrDirURL = [url OUString];
+
+ pSelectedFiles[nIndex] = sFileOrDirURL;
+ }
+
+ return aSelectedFiles;
+}
+
+#pragma mark XFilterManager
+
+void SAL_CALL SalAquaFilePicker::appendFilter( const OUString& aTitle, const OUString& aFilter )
+{
+ SolarMutexGuard aGuard;
+
+ ensureFilterHelper();
+ m_pFilterHelper->appendFilter( aTitle, aFilter );
+ m_pControlHelper->setFilterControlNeeded(true);
+}
+
+void SAL_CALL SalAquaFilePicker::setCurrentFilter( const OUString& aTitle )
+{
+ SolarMutexGuard aGuard;
+
+ ensureFilterHelper();
+ m_pFilterHelper->setCurrentFilter(aTitle);
+ updateFilterUI();
+
+ updateSaveFileNameExtension();
+}
+
+OUString SAL_CALL SalAquaFilePicker::getCurrentFilter()
+{
+ SolarMutexGuard aGuard;
+
+ ensureFilterHelper();
+
+ return m_pFilterHelper->getCurrentFilter();
+}
+
+#pragma mark XFilterGroupManager
+
+void SAL_CALL SalAquaFilePicker::appendFilterGroup( const OUString&, const uno::Sequence<beans::StringPair>& aFilters )
+{
+ SolarMutexGuard aGuard;
+
+ ensureFilterHelper();
+ m_pFilterHelper->appendFilterGroup(aFilters);
+ m_pControlHelper->setFilterControlNeeded(true);
+}
+
+#pragma mark XFilePickerControlAccess
+
+void SAL_CALL SalAquaFilePicker::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
+{
+ SolarMutexGuard aGuard;
+
+ m_pControlHelper->setValue(nControlId, nControlAction, rValue);
+
+ if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION && m_nDialogType == NAVIGATIONSERVICES_SAVE) {
+ updateSaveFileNameExtension();
+ }
+}
+
+uno::Any SAL_CALL SalAquaFilePicker::getValue( sal_Int16 nControlId, sal_Int16 nControlAction )
+{
+ uno::Any aValue = m_pControlHelper->getValue(nControlId, nControlAction);
+
+ return aValue;
+}
+
+void SAL_CALL SalAquaFilePicker::enableControl( sal_Int16 nControlId, sal_Bool bEnable )
+{
+ m_pControlHelper->enableControl(nControlId, bEnable);
+}
+
+void SAL_CALL SalAquaFilePicker::setLabel( sal_Int16 nControlId, const OUString& aLabel )
+{
+ SolarMutexGuard aGuard;
+
+ NSString* sLabel = [NSString stringWithOUString:aLabel];
+ m_pControlHelper->setLabel( nControlId, sLabel ) ;
+}
+
+OUString SAL_CALL SalAquaFilePicker::getLabel( sal_Int16 nControlId )
+{
+ return m_pControlHelper->getLabel(nControlId);
+}
+
+#pragma mark XInitialization
+
+void SAL_CALL SalAquaFilePicker::initialize( const uno::Sequence<uno::Any>& aArguments )
+{
+ SolarMutexGuard aGuard;
+
+ // parameter checking
+ uno::Any aAny;
+ if( 0 == aArguments.getLength() )
+ throw lang::IllegalArgumentException("no arguments",
+ static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ), 1 );
+
+ aAny = aArguments[0];
+
+ if( ( aAny.getValueType() != ::cppu::UnoType<sal_Int16>::get() ) &&
+ (aAny.getValueType() != ::cppu::UnoType<sal_Int8>::get() ) )
+ throw lang::IllegalArgumentException("invalid argument type",
+ static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ), 1 );
+
+ sal_Int16 templateId = -1;
+ aAny >>= templateId;
+
+ switch( templateId )
+ {
+ case FILEOPEN_SIMPLE:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILESAVE_SIMPLE:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILESAVE_AUTOEXTENSION_PASSWORD:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILESAVE_AUTOEXTENSION_SELECTION:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILESAVE_AUTOEXTENSION_TEMPLATE:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILEOPEN_PLAY:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILEOPEN_LINK_PLAY:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILEOPEN_READONLY_VERSION:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILEOPEN_LINK_PREVIEW:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILESAVE_AUTOEXTENSION:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILEOPEN_PREVIEW:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ default:
+ throw lang::IllegalArgumentException("Unknown template",
+ static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ),
+ 1 );
+ }
+
+ m_pControlHelper->initialize(templateId);
+
+ implInitialize();
+}
+
+#pragma mark XCancellable
+
+void SAL_CALL SalAquaFilePicker::cancel()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pDialog != nil) {
+ [m_pDialog cancel:nil];
+ }
+}
+
+#pragma mark XEventListener
+
+void SalAquaFilePicker::disposing( const lang::EventObject& aEvent )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<XFilePickerListener> xFilePickerListener( aEvent.Source, css::uno::UNO_QUERY );
+
+ if( xFilePickerListener.is() )
+ removeFilePickerListener( xFilePickerListener );
+}
+
+#pragma mark XServiceInfo
+
+OUString SAL_CALL SalAquaFilePicker::getImplementationName()
+{
+ return "com.sun.star.ui.dialogs.SalAquaFilePicker";
+}
+
+sal_Bool SAL_CALL SalAquaFilePicker::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getSupportedServiceNames()
+{
+ return FilePicker_getSupportedServiceNames();
+}
+
+#pragma mark Misc/Private
+
+void SalAquaFilePicker::fileSelectionChanged( FilePickerEvent aEvent )
+{
+ if (m_xListener.is())
+ m_xListener->fileSelectionChanged( aEvent );
+}
+
+void SalAquaFilePicker::directoryChanged( FilePickerEvent aEvent )
+{
+ if (m_xListener.is())
+ m_xListener->directoryChanged( aEvent );
+}
+
+void SalAquaFilePicker::controlStateChanged( FilePickerEvent aEvent )
+{
+ if (m_xListener.is())
+ m_xListener->controlStateChanged( aEvent );
+}
+
+void SalAquaFilePicker::dialogSizeChanged()
+{
+ if (m_xListener.is())
+ m_xListener->dialogSizeChanged();
+}
+
+
+// Misc
+
+void SalAquaFilePicker::ensureFilterHelper()
+{
+ SolarMutexGuard aGuard;
+
+ if (nullptr == m_pFilterHelper) {
+ m_pFilterHelper = new FilterHelper;
+ m_pControlHelper->setFilterHelper(m_pFilterHelper);
+ [m_pDelegate setFilterHelper:m_pFilterHelper];
+ }
+}
+
+void SalAquaFilePicker::updateFilterUI()
+{
+ m_pControlHelper->updateFilterUI();
+}
+
+void SalAquaFilePicker::updateSaveFileNameExtension()
+{
+ if (m_nDialogType != NAVIGATIONSERVICES_SAVE) {
+ return;
+ }
+
+ // we need to set this here again because initial setting does
+ //[m_pDialog setExtensionHidden:YES];
+
+ SolarMutexGuard aGuard;
+
+ if (!m_pControlHelper->isAutoExtensionEnabled()) {
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH // setAllowedFileTypes (12.0)
+ [m_pDialog setAllowedFileTypes:nil];
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+ [m_pDialog setAllowsOtherFileTypes:YES];
+ } else {
+ ensureFilterHelper();
+
+ OUStringList aStringList = m_pFilterHelper->getCurrentFilterSuffixList();
+ if( aStringList.empty()) // #i9328#
+ return;
+
+ OUString suffix = (*(aStringList.begin())).copy(1);
+ NSString *requiredFileType = [NSString stringWithOUString:suffix];
+
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH // setAllowedFileTypes (12.0)
+ [m_pDialog setAllowedFileTypes:[NSArray arrayWithObjects:requiredFileType, nil]];
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+
+ [m_pDialog setAllowsOtherFileTypes:NO];
+ }
+}
+
+void SalAquaFilePicker::filterControlChanged()
+{
+ if (m_pDialog == nil) {
+ return;
+ }
+
+ SolarMutexGuard aGuard;
+
+ updateSaveFileNameExtension();
+
+ [m_pDialog validateVisibleColumns];
+
+ FilePickerEvent evt;
+ evt.ElementId = LISTBOX_FILTER;
+ controlStateChanged( evt );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_SalAquaFilePicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SalAquaFilePicker());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaFolderPicker.hxx b/fpicker/source/aqua/SalAquaFolderPicker.hxx
new file mode 100644
index 000000000..f563b1983
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaFolderPicker.hxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/util/XCancellable.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
+
+#include "SalAquaPicker.hxx"
+
+#include <rtl/ustring.hxx>
+
+
+
+
+class SalAquaFolderPicker :
+ public SalAquaPicker,
+ public cppu::WeakImplHelper<
+ css::ui::dialogs::XFolderPicker2,
+ css::lang::XServiceInfo,
+ css::lang::XEventListener >
+{
+public:
+
+ // constructor
+ SalAquaFolderPicker();
+
+
+ // XExecutableDialog functions
+
+
+ virtual void SAL_CALL setTitle( const OUString& aTitle ) override;
+
+ virtual sal_Int16 SAL_CALL execute( ) override;
+
+
+ // XFolderPicker functions
+
+
+ virtual void SAL_CALL setDisplayDirectory( const OUString& rDirectory ) override;
+
+ virtual OUString SAL_CALL getDisplayDirectory( ) override;
+
+ virtual OUString SAL_CALL getDirectory( ) override;
+
+ virtual void SAL_CALL setDescription( const OUString& rDescription ) override;
+
+
+ // XServiceInfo
+
+
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+
+ // XCancellable
+
+
+ virtual void SAL_CALL cancel( ) override;
+
+
+ // XEventListener
+
+
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
+
+private:
+ SalAquaFolderPicker( const SalAquaFolderPicker& ) = delete;
+ SalAquaFolderPicker& operator=( const SalAquaFolderPicker& ) = delete;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaFolderPicker.mm b/fpicker/source/aqua/SalAquaFolderPicker.mm
new file mode 100644
index 000000000..3dabf488a
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaFolderPicker.mm
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <cppuhelper/interfacecontainer.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include "SalAquaFolderPicker.hxx"
+
+#include <iostream>
+
+#include "resourceprovider.hxx"
+
+#include <osl/file.hxx>
+#include "NSString_OOoAdditions.hxx"
+#include "NSURL_OOoAdditions.hxx"
+
+#pragma mark DEFINES
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+SalAquaFolderPicker::SalAquaFolderPicker()
+{
+ m_nDialogType = NAVIGATIONSERVICES_DIRECTORY;
+}
+
+// XExecutableDialog
+
+void SAL_CALL SalAquaFolderPicker::setTitle( const OUString& aTitle )
+{
+ SolarMutexGuard aGuard;
+
+ implsetTitle(aTitle);
+}
+
+sal_Int16 SAL_CALL SalAquaFolderPicker::execute()
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int16 retVal = 0;
+
+ int nResult = runandwaitforresult();
+
+ switch( nResult )
+ {
+ case NSModalResponseOK:
+ retVal = ExecutableDialogResults::OK;
+ break;
+
+ case NSModalResponseCancel:
+ retVal = ExecutableDialogResults::CANCEL;
+ break;
+
+ default:
+ throw uno::RuntimeException("The dialog returned with an unknown result!", static_cast< cppu::OWeakObject * >( this ));
+ break;
+ }
+
+ return retVal;
+}
+
+// XFolderPicker
+
+void SAL_CALL SalAquaFolderPicker::setDisplayDirectory( const OUString& aDirectory )
+{
+ SolarMutexGuard aGuard;
+
+ implsetDisplayDirectory(aDirectory);
+}
+
+OUString SAL_CALL SalAquaFolderPicker::getDisplayDirectory()
+{
+ SolarMutexGuard aGuard;
+
+ OUString aDirectory = implgetDisplayDirectory();
+
+ return aDirectory;
+}
+
+OUString SAL_CALL SalAquaFolderPicker::getDirectory()
+{
+ SolarMutexGuard aGuard;
+
+ NSArray *files = nil;
+ if (m_nDialogType == NAVIGATIONSERVICES_DIRECTORY) {
+ files = [static_cast<NSOpenPanel*>(m_pDialog) URLs];
+ }
+
+ NSUInteger nFiles = [files count];
+ SAL_INFO("fpicker.aqua", "# of items: " << nFiles);
+
+ if (nFiles < 1) {
+ throw uno::RuntimeException("no directory selected", static_cast< cppu::OWeakObject * >( this ));
+ }
+
+ OUString aDirectory;
+
+ NSURL *url = [files objectAtIndex:0];
+
+ aDirectory = [url OUString];
+
+ implsetDisplayDirectory(aDirectory);
+
+ return aDirectory;
+}
+
+void SAL_CALL SalAquaFolderPicker::setDescription( const OUString& rDescription )
+{
+ [m_pDialog setMessage:[NSString stringWithOUString:rDescription]];
+}
+
+// XServiceInfo
+
+OUString SAL_CALL SalAquaFolderPicker::getImplementationName()
+{
+ return "com.sun.star.ui.dialogs.SalAquaFolderPicker";
+}
+
+sal_Bool SAL_CALL SalAquaFolderPicker::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL SalAquaFolderPicker::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.dialogs.SystemFolderPicker", "com.sun.star.ui.dialogs.AquaFolderPicker" };
+}
+
+// XCancellable
+
+void SAL_CALL SalAquaFolderPicker::cancel()
+{
+ SolarMutexGuard aGuard;
+
+ [m_pDialog cancel:nil];
+}
+
+// XEventListener
+
+void SAL_CALL SalAquaFolderPicker::disposing( const lang::EventObject& )
+{
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_SalAquaFolderPicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SalAquaFolderPicker());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaPicker.hxx b/fpicker/source/aqua/SalAquaPicker.hxx
new file mode 100644
index 000000000..4eab78c52
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaPicker.hxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <osl/mutex.hxx>
+
+#include <rtl/ustring.hxx>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include "ControlHelper.hxx"
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+class SalAquaPicker
+{
+public:
+ // constructor
+ SalAquaPicker();
+ virtual ~SalAquaPicker();
+
+ int run();
+ int runandwaitforresult();
+
+ OUString const& getDisplayDirectory() { return m_sDisplayDirectory; }
+
+ ControlHelper* getControlHelper() const { return m_pControlHelper; }
+
+protected:
+ OUString m_sDisplayDirectory;
+
+ NSSavePanel* m_pDialog;
+
+ ControlHelper* m_pControlHelper;
+
+ osl::Mutex m_rbHelperMtx;
+
+ // The type of dialog
+ enum NavigationServices_DialogType
+ {
+ NAVIGATIONSERVICES_OPEN,
+ NAVIGATIONSERVICES_SAVE,
+ NAVIGATIONSERVICES_DIRECTORY
+ };
+
+ NavigationServices_DialogType m_nDialogType;
+
+ /// @throws css::uno::RuntimeException
+ void implsetTitle(const OUString& aTitle);
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void implsetDisplayDirectory(const OUString& rDirectory);
+
+ /// @throws css::uno::RuntimeException
+ OUString const& implgetDisplayDirectory();
+
+ void implInitialize();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaPicker.mm b/fpicker/source/aqua/SalAquaPicker.mm
new file mode 100644
index 000000000..c2cf497f5
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaPicker.mm
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <cppuhelper/interfacecontainer.h>
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+#include "SalAquaPicker.hxx"
+#include <osl/file.hxx>
+#include "NSString_OOoAdditions.hxx"
+
+#include "NSURL_OOoAdditions.hxx"
+
+#include "SalAquaFilePicker.hxx"
+
+#include <stdio.h>
+
+#pragma mark DEFINES
+#define kSetHideExtensionStateKey @"NSNavLastUserSetHideExtensionButtonState"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+SalAquaPicker::SalAquaPicker()
+: m_pDialog(nullptr)
+, m_pControlHelper(new ControlHelper())
+{
+}
+
+SalAquaPicker::~SalAquaPicker()
+{
+ SolarMutexGuard aGuard;
+
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ if (nullptr != m_pControlHelper)
+ delete m_pControlHelper;
+
+ if (nullptr != m_pDialog)
+ [m_pDialog release];
+
+ [pool release];
+}
+
+void SalAquaPicker::implInitialize()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pDialog != nil) {
+ return;
+ }
+
+ switch (m_nDialogType)
+ {
+ case NAVIGATIONSERVICES_OPEN:
+ m_pDialog = [NSOpenPanel openPanel];
+ [static_cast<NSOpenPanel*>(m_pDialog) setCanChooseDirectories:NO];
+ [static_cast<NSOpenPanel*>(m_pDialog) setCanChooseFiles:YES];
+ break;
+
+ case NAVIGATIONSERVICES_SAVE:
+ m_pDialog = [NSSavePanel savePanel];
+ [m_pDialog setCanSelectHiddenExtension:NO]; //changed for issue #102102
+ /* I would have loved to use
+ * [(NSSavePanel*)m_pDialog setExtensionHidden:YES];
+ * here but unfortunately this
+ * a) only works when the dialog is already displayed because it seems to act on the corresponding checkbox (that we don't show but that doesn't matter)
+ * b) macOS saves this setting on an application-based level which means that the last state is always being restored again when the app runs for the next time
+ *
+ * So the only reliable way seems to be using the NSUserDefaults object because that is where that value is stored and
+ * to just overwrite it if it has the wrong value.
+ */
+ {
+ NSUserDefaults *pDefaults = [NSUserDefaults standardUserDefaults];
+ NSNumber *pExtn = [pDefaults objectForKey:kSetHideExtensionStateKey];
+ if(pExtn == nil || [pExtn boolValue] == NO) {
+ [pDefaults setBool:YES forKey:kSetHideExtensionStateKey];
+ }
+ }
+ break;
+
+ case NAVIGATIONSERVICES_DIRECTORY:
+ m_pDialog = [NSOpenPanel openPanel];
+ [static_cast<NSOpenPanel*>(m_pDialog) setCanChooseDirectories:YES];
+ [static_cast<NSOpenPanel*>(m_pDialog) setCanChooseFiles:NO];
+ break;
+
+ default:
+ break;
+ }
+
+ if (m_pDialog != nil) {
+ [static_cast<NSOpenPanel*>(m_pDialog) setCanCreateDirectories:YES];
+ //Retain the dialog instance or it will go away immediately
+ [m_pDialog retain];
+ }
+}
+
+int SalAquaPicker::run()
+{
+ SolarMutexGuard aGuard;
+
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ if (m_pDialog == nullptr) {
+ //this is the case e.g. for the folder picker at this stage
+ implInitialize();
+ }
+
+ NSView *userPane = m_pControlHelper->getUserPane();
+ if (userPane != nullptr) {
+ [m_pDialog setAccessoryView:userPane];
+ }
+
+ int retVal = 0;
+
+ NSURL *startDirectory;
+ if (m_sDisplayDirectory.getLength() > 0) {
+ NSString *temp = [NSString stringWithOUString:m_sDisplayDirectory];
+ startDirectory = [NSURL URLWithString:temp];
+
+ SAL_INFO("fpicker.aqua", "start dir: " << [startDirectory path]);
+ }
+ else {
+ startDirectory = [NSURL fileURLWithPath:NSHomeDirectory() isDirectory:YES];
+ }
+
+ switch(m_nDialogType) {
+ case NAVIGATIONSERVICES_DIRECTORY:
+ case NAVIGATIONSERVICES_OPEN:
+ [m_pDialog setDirectoryURL:startDirectory];
+ retVal = [static_cast<NSOpenPanel*>(m_pDialog) runModal];
+ break;
+ case NAVIGATIONSERVICES_SAVE:
+ [m_pDialog setDirectoryURL:startDirectory];
+ [m_pDialog setNameFieldStringValue:[NSString stringWithOUString:static_cast<SalAquaFilePicker*>(this)->getSaveFileName()]];
+ retVal = [m_pDialog runModal];
+ break;
+ default:
+ break;
+ }
+
+ if (retVal == NSModalResponseOK) {
+ NSURL* pDir = [m_pDialog directoryURL];
+ if (pDir) {
+ implsetDisplayDirectory([pDir OUString]);
+ }
+ }
+
+ [pool release];
+
+ return retVal;
+}
+
+int SalAquaPicker::runandwaitforresult()
+{
+ SolarMutexGuard aGuard;
+
+ int status = run();
+
+ return status;
+}
+
+void SalAquaPicker::implsetDisplayDirectory( const OUString& aDirectory )
+{
+ SolarMutexGuard aGuard;
+
+ if (aDirectory != m_sDisplayDirectory) {
+ m_sDisplayDirectory = aDirectory;
+ }
+}
+
+OUString const & SalAquaPicker::implgetDisplayDirectory()
+{
+ return m_sDisplayDirectory;
+}
+
+void SalAquaPicker::implsetTitle( const OUString& aTitle )
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pDialog != nil) {
+ [m_pDialog setTitle:[NSString stringWithOUString:aTitle]];
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/fps_aqua.component b/fpicker/source/aqua/fps_aqua.component
new file mode 100644
index 000000000..bf143911a
--- /dev/null
+++ b/fpicker/source/aqua/fps_aqua.component
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.ui.dialogs.SalAquaFilePicker"
+ constructor="fpicker_SalAquaFilePicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.AquaFilePicker"/>
+ </implementation>
+ <implementation name="com.sun.star.ui.dialogs.SalAquaFolderPicker"
+ constructor="fpicker_SalAquaFolderPicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.AquaFolderPicker"/>
+ </implementation>
+</component>
diff --git a/fpicker/source/aqua/resourceprovider.hxx b/fpicker/source/aqua/resourceprovider.hxx
new file mode 100644
index 000000000..ba581389f
--- /dev/null
+++ b/fpicker/source/aqua/resourceprovider.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <sal/types.h>
+
+#include <premac.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+#define FOLDERPICKER_TITLE 500
+#define FOLDER_PICKER_DEF_DESCRIPTION 501
+#define FILE_PICKER_TITLE_OPEN 502
+#define FILE_PICKER_TITLE_SAVE 503
+#define FILE_PICKER_FILE_TYPE 504
+#define FILE_PICKER_OVERWRITE 505
+
+namespace CResourceProvider {
+
+NSString* getResString( sal_Int32 aId );
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/resourceprovider.mm b/fpicker/source/aqua/resourceprovider.mm
new file mode 100644
index 000000000..951833ae2
--- /dev/null
+++ b/fpicker/source/aqua/resourceprovider.mm
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <osl/mutex.hxx>
+#include <fpicker/strings.hrc>
+#include <vcl/svapp.hxx>
+#include <unotools/resmgr.hxx>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+
+#include "NSString_OOoAdditions.hxx"
+#include <fpicker/fpsofficeResMgr.hxx>
+#include "resourceprovider.hxx"
+
+using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
+using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
+
+// we have to translate control ids to resource ids
+
+namespace {
+
+struct Entry
+{
+ sal_Int32 ctrlId;
+ TranslateId resId;
+};
+
+}
+
+Entry const CtrlIdToResIdTable[] = {
+ { CHECKBOX_AUTOEXTENSION, STR_SVT_FILEPICKER_AUTO_EXTENSION },
+ { CHECKBOX_PASSWORD, STR_SVT_FILEPICKER_PASSWORD },
+ { CHECKBOX_FILTEROPTIONS, STR_SVT_FILEPICKER_FILTER_OPTIONS },
+ { CHECKBOX_READONLY, STR_SVT_FILEPICKER_READONLY },
+ { CHECKBOX_LINK, STR_SVT_FILEPICKER_INSERT_AS_LINK },
+ { CHECKBOX_PREVIEW, STR_SVT_FILEPICKER_SHOW_PREVIEW },
+ { PUSHBUTTON_PLAY, STR_SVT_FILEPICKER_PLAY },
+ { LISTBOX_VERSION_LABEL, STR_SVT_FILEPICKER_VERSION },
+ { LISTBOX_TEMPLATE_LABEL, STR_SVT_FILEPICKER_TEMPLATES },
+ { LISTBOX_IMAGE_TEMPLATE_LABEL, STR_SVT_FILEPICKER_IMAGE_TEMPLATE },
+ { LISTBOX_IMAGE_ANCHOR_LABEL, STR_SVT_FILEPICKER_IMAGE_ANCHOR },
+ { CHECKBOX_SELECTION, STR_SVT_FILEPICKER_SELECTION },
+ { FOLDERPICKER_TITLE, STR_SVT_FOLDERPICKER_DEFAULT_TITLE },
+ { FOLDER_PICKER_DEF_DESCRIPTION, STR_SVT_FOLDERPICKER_DEFAULT_DESCRIPTION },
+ { FILE_PICKER_OVERWRITE, STR_SVT_ALREADYEXISTOVERWRITE },
+ { LISTBOX_FILTER_LABEL, STR_SVT_FILEPICKER_FILTER_TITLE},
+ { FILE_PICKER_TITLE_OPEN, STR_FILEDLG_OPEN },
+ { FILE_PICKER_TITLE_SAVE, STR_FILEDLG_SAVE },
+ { FILE_PICKER_FILE_TYPE, STR_FILEDLG_TYPE }
+};
+
+const sal_Int32 SIZE_TABLE = SAL_N_ELEMENTS( CtrlIdToResIdTable );
+
+static TranslateId CtrlIdToResId(sal_Int32 aControlId)
+{
+ TranslateId pResId;
+
+ for ( sal_Int32 i = 0; i < SIZE_TABLE; i++ )
+ {
+ if ( CtrlIdToResIdTable[i].ctrlId == aControlId )
+ {
+ pResId = CtrlIdToResIdTable[i].resId;
+ break;
+ }
+ }
+
+ return pResId;
+}
+
+namespace CResourceProvider_Impl
+{
+ static NSString* getResString(sal_Int16 aId)
+ {
+ OUString aResString;
+
+ // translate the control id to a resource id
+ TranslateId pResId = CtrlIdToResId(aId);
+ if (pResId)
+ aResString = FpsResId(pResId);
+
+ return [NSString stringWithOUString:aResString];
+ }
+};
+
+NSString* CResourceProvider::getResString( sal_Int32 aId )
+{
+ NSString* sImmutable = CResourceProvider_Impl::getResString(aId);
+ NSMutableString *sMutableString = [NSMutableString stringWithString:sImmutable];
+ [sMutableString replaceOccurrencesOfString:@"~" withString:@"" options:0 range:NSMakeRange(0, [sMutableString length])];
+
+ NSString *result = [NSString stringWithString:sMutableString];
+
+ return result;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */