1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
21 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
22 #include <com/sun/star/ui/dialogs/ControlActions.hpp>
23 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
24 #include <osl/mutex.hxx>
25 #include <vcl/svapp.hxx>
26 #include "resourceprovider.hxx"
27 #include "NSString_OOoAdditions.hxx"
28 #include <sal/log.hxx>
30 #include "ControlHelper.hxx"
33 #define POPUP_WIDTH_MIN 200
34 #define POPUP_WIDTH_MAX 350
36 using namespace ::com::sun::star::ui::dialogs;
37 using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
38 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
39 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
43 uno::Any HandleGetListValue(const NSControl* pControl, const sal_Int16 nControlAction)
47 if ([pControl class] != [NSPopUpButton class]) {
48 SAL_INFO("fpicker.aqua","not a popup button");
52 NSPopUpButton *pButton = static_cast<NSPopUpButton*>(pControl);
53 NSMenu *rMenu = [pButton menu];
55 SAL_INFO("fpicker.aqua","button has no menu");
59 switch (nControlAction)
61 case ControlActions::GET_ITEMS:
63 SAL_INFO("fpicker.aqua","GET_ITEMS");
64 uno::Sequence< OUString > aItemList;
66 int nItems = [rMenu numberOfItems];
68 aItemList.realloc(nItems);
69 OUString* pItemList = aItemList.getArray();
70 for (int i = 0; i < nItems; i++) {
71 NSString* sCFItem = [pButton itemTitleAtIndex:i];
73 pItemList[i] = [sCFItem OUString];
74 SAL_INFO("fpicker.aqua","Return value[" << (i - 1) << "]: " << aItemList[i - 1]);
82 case ControlActions::GET_SELECTED_ITEM:
84 SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM");
85 NSString* sCFItem = [pButton titleOfSelectedItem];
87 OUString sString = [sCFItem OUString];
88 SAL_INFO("fpicker.aqua","Return value: " << sString);
93 case ControlActions::GET_SELECTED_ITEM_INDEX:
95 SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM_INDEX");
96 sal_Int32 nActive = [pButton indexOfSelectedItem];
97 SAL_INFO("fpicker.aqua","Return value: " << nActive);
102 SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
109 NSTextField* createLabelWithString(NSString* labelString)
111 NSTextField *textField = [NSTextField new];
112 [textField setEditable:NO];
113 [textField setSelectable:NO];
114 [textField setDrawsBackground:NO];
115 [textField setBordered:NO];
116 [[textField cell] setTitle:labelString];
123 #pragma mark Constructor / Destructor
125 // Constructor / Destructor
127 ControlHelper::ControlHelper()
128 : m_pUserPane(nullptr)
129 , m_pFilterControl(nil)
130 , m_bUserPaneNeeded( false )
131 , m_bIsUserPaneLaidOut(false)
132 , m_bIsFilterControlNeeded(false)
133 , m_pFilterHelper(nullptr)
137 for( i = 0; i < TOGGLE_LAST; i++ ) {
138 m_bToggleVisibility[i] = false;
141 for( i = 0; i < LIST_LAST; i++ ) {
142 m_bListVisibility[i] = false;
146 ControlHelper::~ControlHelper()
148 NSAutoreleasePool *pool = [NSAutoreleasePool new];
150 if (nullptr != m_pUserPane) {
151 [m_pUserPane release];
154 if (m_pFilterControl != nullptr) {
155 [m_pFilterControl setTarget:nil];
158 for (auto const& activeControl : m_aActiveControls)
160 NSString* sLabelName = m_aMapListLabels[activeControl];
161 if (sLabelName != nil) {
162 [sLabelName release];
164 if ([activeControl class] == [NSPopUpButton class]) {
165 NSTextField* pField = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)];
170 [activeControl release];
176 #pragma mark XInitialization delegate
178 // XInitialization delegate
180 void ControlHelper::initialize( sal_Int16 nTemplateId )
182 switch( nTemplateId )
184 case FILESAVE_AUTOEXTENSION_PASSWORD:
185 m_bToggleVisibility[AUTOEXTENSION] = true;
186 m_bToggleVisibility[PASSWORD] = true;
188 case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
189 m_bToggleVisibility[AUTOEXTENSION] = true;
190 m_bToggleVisibility[PASSWORD] = true;
191 m_bToggleVisibility[FILTEROPTIONS] = true;
193 case FILESAVE_AUTOEXTENSION_SELECTION:
194 m_bToggleVisibility[AUTOEXTENSION] = true;
195 m_bToggleVisibility[SELECTION] = true;
197 case FILESAVE_AUTOEXTENSION_TEMPLATE:
198 m_bToggleVisibility[AUTOEXTENSION] = true;
199 m_bListVisibility[TEMPLATE] = true;
201 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
202 m_bToggleVisibility[LINK] = true;
203 m_bToggleVisibility[PREVIEW] = true;
204 m_bListVisibility[IMAGE_TEMPLATE] = true;
206 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
207 m_bToggleVisibility[LINK] = true;
208 m_bToggleVisibility[PREVIEW] = true;
209 m_bListVisibility[IMAGE_ANCHOR] = true;
211 case FILEOPEN_READONLY_VERSION:
212 m_bToggleVisibility[READONLY] = true;
213 m_bListVisibility[VERSION] = true;
215 case FILEOPEN_LINK_PREVIEW:
216 m_bToggleVisibility[LINK] = true;
217 m_bToggleVisibility[PREVIEW] = true;
219 case FILESAVE_AUTOEXTENSION:
220 m_bToggleVisibility[AUTOEXTENSION] = true;
222 case FILEOPEN_PREVIEW:
223 m_bToggleVisibility[PREVIEW] = true;
225 case FILEOPEN_LINK_PLAY:
226 m_bToggleVisibility[LINK] = true;
232 #pragma mark XFilePickerControlAccess delegates
234 // XFilePickerControlAccess functions
237 void ControlHelper::enableControl( const sal_Int16 nControlId, const bool bEnable ) const
239 SolarMutexGuard aGuard;
241 if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
242 SAL_INFO("fpicker.aqua"," preview checkbox cannot be changed");
246 NSControl* pControl = getControl(nControlId);
248 if( pControl != nil ) {
250 SAL_INFO("fpicker.aqua", "enable" );
252 SAL_INFO("fpicker.aqua", "disable" );
254 [pControl setEnabled:bEnable];
256 SAL_INFO("fpicker.aqua","enable unknown control " << nControlId );
260 OUString ControlHelper::getLabel( sal_Int16 nControlId )
262 SolarMutexGuard aGuard;
264 NSControl* pControl = getControl( nControlId );
266 if( pControl == nil ) {
267 SAL_INFO("fpicker.aqua","Get label for unknown control " << nControlId);
272 if ([pControl class] == [NSPopUpButton class]) {
273 NSString *temp = m_aMapListLabels[pControl];
275 retVal = [temp OUString];
278 NSString* sLabel = [[pControl cell] title];
279 retVal = [sLabel OUString];
285 void ControlHelper::setLabel( sal_Int16 nControlId, NSString* aLabel )
287 SolarMutexGuard aGuard;
289 NSAutoreleasePool *pool = [NSAutoreleasePool new];
291 NSControl* pControl = getControl(nControlId);
293 if (nil != pControl) {
294 if ([pControl class] == [NSPopUpButton class]) {
295 NSString *sOldName = m_aMapListLabels[pControl];
296 if (sOldName != nullptr && sOldName != aLabel) {
300 m_aMapListLabels[pControl] = [aLabel retain];
301 } else if ([pControl class] == [NSButton class]) {
302 [[pControl cell] setTitle:aLabel];
305 SAL_INFO("fpicker.aqua","Control not found to set label for");
313 void ControlHelper::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
315 SolarMutexGuard aGuard;
317 if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
318 SAL_INFO("fpicker.aqua"," value for preview is unchangeable");
321 NSControl* pControl = getControl( nControlId );
323 if( pControl == nil ) {
324 SAL_INFO("fpicker.aqua","enable unknown control " << nControlId);
326 if( [pControl class] == [NSPopUpButton class] ) {
327 HandleSetListValue(pControl, nControlAction, rValue);
328 } else if( [pControl class] == [NSButton class] ) {
329 bool bChecked = false;
331 SAL_INFO("fpicker.aqua"," value is a bool: " << bChecked);
332 [static_cast<NSButton*>(pControl) setState:(bChecked ? NSControlStateValueOn : NSControlStateValueOff)];
335 SAL_INFO("fpicker.aqua","Can't set value on button / list " << nControlId << " " << nControlAction);
341 uno::Any ControlHelper::getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const
343 SolarMutexGuard aGuard;
346 NSControl* pControl = getControl( nControlId );
348 if( pControl == nil ) {
349 SAL_INFO("fpicker.aqua","get value for unknown control " << nControlId);
351 if( [pControl class] == [NSPopUpButton class] ) {
352 aRetval = HandleGetListValue(pControl, nControlAction);
353 } else if( [pControl class] == [NSButton class] ) {
354 //NSLog(@"control: %@", [[pControl cell] title]);
355 bool bValue = [static_cast<NSButton*>(pControl) state] == NSControlStateValueOn;
357 SAL_INFO("fpicker.aqua","value is a bool (checkbox): " << bValue);
364 void ControlHelper::createUserPane()
366 if (!m_bUserPaneNeeded) {
367 SAL_INFO("fpicker.aqua","no user pane needed");
371 if (nil != m_pUserPane) {
372 SAL_INFO("fpicker.aqua","user pane already exists");
376 if (m_bIsFilterControlNeeded && m_pFilterControl == nil) {
377 createFilterControl();
380 NSRect minRect = NSMakeRect(0,0,300,33);
381 m_pUserPane = [[NSView alloc] initWithFrame:minRect];
383 int currentHeight = kAquaSpaceBoxFrameViewDiffTop + kAquaSpaceBoxFrameViewDiffBottom;
384 int currentWidth = 300;
386 bool bPopupControlPresent = false;
387 bool bButtonControlPresent = false;
389 int nCheckboxMaxWidth = 0;
390 int nPopupMaxWidth = 0;
391 int nPopupLabelMaxWidth = 0;
394 for (auto const& activeControl : m_aActiveControls)
396 SAL_INFO("fpicker.aqua","currentHeight: " << currentHeight);
398 //let the control calculate its size
399 [activeControl sizeToFit];
401 NSRect frame = [activeControl frame];
402 SAL_INFO("fpicker.aqua","frame for control " << [[activeControl description] UTF8String] << " is {" << frame.origin.x << ", " << frame.origin.y << ", " << frame.size.width << ", " << frame.size.height << "}");
404 int nControlHeight = frame.size.height;
405 int nControlWidth = frame.size.width;
407 // Note: controls are grouped by kind, first all popup menus, then checkboxes
408 if ([activeControl class] == [NSPopUpButton class]) {
409 if (bPopupControlPresent) {
410 //this is not the first popup
411 currentHeight += kAquaSpaceBetweenPopupMenus;
415 currentHeight += kAquaSpaceBetweenControls;
418 bPopupControlPresent = true;
420 // we have to add the label text width
421 NSString *label = m_aMapListLabels[activeControl];
423 NSTextField *textField = createLabelWithString(label);
424 [textField sizeToFit];
425 m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)] = textField;
426 [m_pUserPane addSubview:textField];
428 NSRect tfRect = [textField frame];
429 SAL_INFO("fpicker.aqua","frame for textfield " << [[textField description] UTF8String] << " is {" << tfRect.origin.x << ", " << tfRect.origin.y << ", " << tfRect.size.width << ", " << tfRect.size.height << "}");
431 int tfWidth = tfRect.size.width;
433 if (nPopupLabelMaxWidth < tfWidth) {
434 nPopupLabelMaxWidth = tfWidth;
437 frame.origin.x += (kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft) + tfWidth;
439 if (nControlWidth < POPUP_WIDTH_MIN) {
440 nControlWidth = POPUP_WIDTH_MIN;
441 frame.size.width = nControlWidth;
442 [activeControl setFrame:frame];
445 if (nControlWidth > POPUP_WIDTH_MAX) {
446 nControlWidth = POPUP_WIDTH_MAX;
447 frame.size.width = nControlWidth;
448 [activeControl setFrame:frame];
452 if (nPopupMaxWidth < nControlWidth) {
453 nPopupMaxWidth = nControlWidth;
456 nControlWidth += tfWidth + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
457 if (nControlHeight < kAquaPopupButtonDefaultHeight) {
458 //maybe the popup has no menu item yet, so set a default height
459 nControlHeight = kAquaPopupButtonDefaultHeight;
462 nControlHeight -= kAquaSpacePopupMenuFrameBoundsDiffV;
464 else if ([activeControl class] == [NSButton class]) {
467 currentHeight += kAquaSpaceBetweenControls;
470 if (nCheckboxMaxWidth < nControlWidth) {
471 nCheckboxMaxWidth = nControlWidth;
474 bButtonControlPresent = true;
475 nControlWidth -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
476 nControlHeight -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
479 // if ((nControlWidth + 2 * kAquaSpaceInsideGroupH) > currentWidth) {
480 // currentWidth = nControlWidth + 2 * kAquaSpaceInsideGroupH;
483 currentHeight += nControlHeight;
485 [m_pUserPane addSubview:activeControl];
489 SAL_INFO("fpicker.aqua","height after adding all controls: " << currentHeight);
491 if (bPopupControlPresent && bButtonControlPresent)
493 //after a popup button (array) and before a different kind of control we need some extra space instead of the standard
494 currentHeight -= kAquaSpaceBetweenControls;
495 currentHeight += kAquaSpaceAfterPopupButtonsV;
496 SAL_INFO("fpicker.aqua","popup extra space added, currentHeight: " << currentHeight);
499 int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
501 currentWidth = nLongestPopupWidth > nCheckboxMaxWidth ? nLongestPopupWidth : nCheckboxMaxWidth;
502 SAL_INFO("fpicker.aqua","longest control width: " << currentWidth);
504 currentWidth += 2* kAquaSpaceInsideGroupH;
506 if (currentWidth < minRect.size.width)
507 currentWidth = minRect.size.width;
509 if (currentHeight < minRect.size.height)
510 currentHeight = minRect.size.height;
512 NSRect upRect = NSMakeRect(0, 0, currentWidth, currentHeight );
513 SAL_INFO("fpicker.aqua","setting user pane rect to {" << upRect.origin.x << ", " << upRect.origin.y << ", " << upRect.size.width << ", " << upRect.size.height << "}");
515 [m_pUserPane setFrame:upRect];
520 #pragma mark Private / Misc
524 void ControlHelper::createControls()
526 for (int i = 0; i < LIST_LAST; i++) {
527 if (m_bListVisibility[i]) {
528 m_bUserPaneNeeded = true;
530 int elementName = getControlElementName([NSPopUpButton class], i);
531 NSString* sLabel = CResourceProvider::getResString(elementName);
533 m_pListControls[i] = [NSPopUpButton new];
535 #define MAP_LIST_( elem ) \
537 setLabel(ExtendedFilePickerElementIds::LISTBOX_##elem, sLabel); \
543 MAP_LIST_(IMAGE_TEMPLATE);
544 MAP_LIST_(IMAGE_ANCHOR);
547 m_aActiveControls.push_back(m_pListControls[i]);
549 m_pListControls[i] = nil;
553 for (int i = 0/*#i102102*/; i < TOGGLE_LAST; i++) {
554 if (m_bToggleVisibility[i]) {
555 m_bUserPaneNeeded = true;
557 int elementName = getControlElementName([NSButton class], i);
558 NSString* sLabel = CResourceProvider::getResString(elementName);
560 NSButton *button = [NSButton new];
561 [button setTitle:sLabel];
563 [button setButtonType:NSButtonTypeSwitch];
565 [button setState:NSControlStateValueOff];
567 if (i == AUTOEXTENSION) {
568 [button setTarget:m_pDelegate];
569 [button setAction:@selector(autoextensionChanged:)];
572 m_pToggles[i] = button;
574 m_aActiveControls.push_back(m_pToggles[i]);
580 //preview is always on with macOS
581 NSControl *pPreviewBox = m_pToggles[PREVIEW];
582 if (pPreviewBox != nil) {
583 [pPreviewBox setEnabled:NO];
584 [static_cast<NSButton*>(pPreviewBox) setState:NSControlStateValueOn];
588 #define TOGGLE_ELEMENT( elem ) \
590 nReturn = CHECKBOX_##elem; \
592 #define LIST_ELEMENT( elem ) \
594 nReturn = LISTBOX_##elem##_LABEL; \
597 int ControlHelper::getControlElementName(const Class aClazz, const int nControlId)
600 if (aClazz == [NSButton class])
602 switch (nControlId) {
603 TOGGLE_ELEMENT( AUTOEXTENSION );
604 TOGGLE_ELEMENT( PASSWORD );
605 TOGGLE_ELEMENT( FILTEROPTIONS );
606 TOGGLE_ELEMENT( READONLY );
607 TOGGLE_ELEMENT( LINK );
608 TOGGLE_ELEMENT( PREVIEW );
609 TOGGLE_ELEMENT( SELECTION );
612 else if (aClazz == [NSPopUpButton class])
614 switch (nControlId) {
615 LIST_ELEMENT( VERSION );
616 LIST_ELEMENT( TEMPLATE );
617 LIST_ELEMENT( IMAGE_TEMPLATE );
618 LIST_ELEMENT( IMAGE_ANCHOR );
625 void ControlHelper::HandleSetListValue(const NSControl* pControl, const sal_Int16 nControlAction, const uno::Any& rValue)
627 if ([pControl class] != [NSPopUpButton class]) {
628 SAL_INFO("fpicker.aqua","not a popup menu");
632 NSPopUpButton *pButton = static_cast<NSPopUpButton*>(pControl);
633 NSMenu *rMenu = [pButton menu];
635 SAL_INFO("fpicker.aqua","button has no menu");
639 switch (nControlAction)
641 case ControlActions::ADD_ITEM:
643 SAL_INFO("fpicker.aqua","ADD_ITEMS");
647 NSString* sCFItem = [NSString stringWithOUString:sItem];
648 SAL_INFO("fpicker.aqua","Adding menu item: " << sItem);
649 [pButton addItemWithTitle:sCFItem];
652 case ControlActions::ADD_ITEMS:
654 SAL_INFO("fpicker.aqua","ADD_ITEMS");
655 uno::Sequence< OUString > aStringList;
656 rValue >>= aStringList;
657 sal_Int32 nItemCount = aStringList.getLength();
658 for (sal_Int32 i = 0; i < nItemCount; ++i)
660 NSString* sCFItem = [NSString stringWithOUString:aStringList[i]];
661 SAL_INFO("fpicker.aqua","Adding menu item: " << aStringList[i]);
662 [pButton addItemWithTitle:sCFItem];
666 case ControlActions::DELETE_ITEM:
668 SAL_INFO("fpicker.aqua","DELETE_ITEM");
671 SAL_INFO("fpicker.aqua","Deleting item at position " << (nPos));
672 [rMenu removeItemAtIndex:nPos];
675 case ControlActions::DELETE_ITEMS:
677 SAL_INFO("fpicker.aqua","DELETE_ITEMS");
678 int nItems = [rMenu numberOfItems];
680 SAL_INFO("fpicker.aqua","no menu items to delete");
683 for(sal_Int32 i = 0; i < nItems; i++) {
684 [rMenu removeItemAtIndex:i];
688 case ControlActions::SET_SELECT_ITEM:
692 SAL_INFO("fpicker.aqua","Selecting item at position " << nPos);
693 [pButton selectItemAtIndex:nPos];
697 SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
704 // cf. offapi/com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.idl
705 NSControl* ControlHelper::getControl( const sal_Int16 nControlId ) const
707 NSControl* pWidget = nil;
709 #define MAP_TOGGLE( elem ) \
710 case ExtendedFilePickerElementIds::CHECKBOX_##elem: \
711 pWidget = m_pToggles[elem]; \
714 #define MAP_LIST( elem ) \
715 case ExtendedFilePickerElementIds::LISTBOX_##elem: \
716 pWidget = m_pListControls[elem]; \
719 #define MAP_LIST_LABEL( elem ) \
720 case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \
721 pWidget = m_pListControls[elem]; \
726 MAP_TOGGLE( AUTOEXTENSION );
727 MAP_TOGGLE( PASSWORD );
728 MAP_TOGGLE( FILTEROPTIONS );
729 MAP_TOGGLE( READONLY );
731 MAP_TOGGLE( PREVIEW );
732 MAP_TOGGLE( SELECTION );
733 //MAP_BUTTON( PLAY );
735 MAP_LIST( TEMPLATE );
736 MAP_LIST( IMAGE_TEMPLATE );
737 MAP_LIST( IMAGE_ANCHOR );
738 MAP_LIST_LABEL( VERSION );
739 MAP_LIST_LABEL( TEMPLATE );
740 MAP_LIST_LABEL( IMAGE_TEMPLATE );
741 MAP_LIST_LABEL( IMAGE_ANCHOR );
743 SAL_INFO("fpicker.aqua","Handle unknown control " << nControlId);
751 void ControlHelper::layoutControls()
753 SolarMutexGuard aGuard;
755 if (nil == m_pUserPane) {
756 SAL_INFO("fpicker.aqua","no user pane to layout");
760 if (m_bIsUserPaneLaidOut) {
761 SAL_INFO("fpicker.aqua","user pane already laid out");
765 NSRect userPaneRect = [m_pUserPane frame];
766 SAL_INFO("fpicker.aqua","userPane frame: {" << userPaneRect.origin.x << ", " << userPaneRect.origin.y << ", " << userPaneRect.size.width << ", " << userPaneRect.size.height << "}");
768 int nUsableWidth = userPaneRect.size.width;
770 //NOTE: NSView's coordinate system starts in the lower left hand corner but we start adding controls from the top,
771 // so we subtract from the vertical position as we make our way down the pane.
772 int currenttop = userPaneRect.size.height;
773 int nCheckboxMaxWidth = 0;
774 int nPopupMaxWidth = 0;
775 int nPopupLabelMaxWidth = 0;
777 //first loop to determine max sizes
778 for (auto const& activeControl : m_aActiveControls)
781 NSRect controlRect = [activeControl frame];
782 int nControlWidth = controlRect.size.width;
784 Class aSubType = [activeControl class];
785 if (aSubType == [NSPopUpButton class]) {
786 if (nPopupMaxWidth < nControlWidth) {
787 nPopupMaxWidth = nControlWidth;
789 NSTextField *label = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)];
790 NSRect labelFrame = [label frame];
791 int nLabelWidth = labelFrame.size.width;
792 if (nPopupLabelMaxWidth < nLabelWidth) {
793 nPopupLabelMaxWidth = nLabelWidth;
796 if (nCheckboxMaxWidth < nControlWidth) {
797 nCheckboxMaxWidth = nControlWidth;
802 int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
803 SAL_INFO("fpicker.aqua","longest popup width: " << nLongestPopupWidth);
805 NSControl* previousControl = nil;
807 int nDistBetweenControls = 0;
809 for (auto const& activeControl : m_aActiveControls)
811 //get the control's bounds
812 NSRect controlRect = [activeControl frame];
813 int nControlHeight = controlRect.size.height;
815 //subtract the height from the current vertical position, because the control's bounds origin rect will be its lower left hand corner
816 currenttop -= nControlHeight;
818 Class aSubType = [activeControl class];
820 //add space between the previous control and this control according to Apple's HIG
821 nDistBetweenControls = getVerticalDistance(previousControl, activeControl);
822 SAL_INFO("fpicker.aqua","vertical distance: " << nDistBetweenControls);
823 currenttop -= nDistBetweenControls;
825 previousControl = activeControl;
827 if (aSubType == [NSPopUpButton class]) {
828 //move vertically up some pixels to space the controls between their real (visual) bounds
829 currenttop += kAquaSpacePopupMenuFrameBoundsDiffTop;//from top
831 //get the corresponding popup label
832 NSTextField *label = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)];
833 NSRect labelFrame = [label frame];
834 int totalWidth = nPopupMaxWidth + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
835 SAL_INFO("fpicker.aqua","totalWidth: " << totalWidth);
836 //let's center popups
837 int left = (nUsableWidth + nLongestPopupWidth) / 2 - totalWidth;
838 SAL_INFO("fpicker.aqua","left: " << left);
839 labelFrame.origin.x = left;
840 labelFrame.origin.y = currenttop + kAquaSpaceLabelPopupDiffV;
841 SAL_INFO("fpicker.aqua","setting label at: {" << labelFrame.origin.x << ", " << labelFrame.origin.y << ", " << labelFrame.size.width << ", " << labelFrame.size.height << "}");
842 [label setFrame:labelFrame];
844 controlRect.origin.x = left + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
845 controlRect.origin.y = currenttop;
846 controlRect.size.width = nPopupMaxWidth;
847 SAL_INFO("fpicker.aqua","setting popup at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}");
848 [activeControl setFrame:controlRect];
850 //add some space to place the vertical position right below the popup's visual bounds
851 currenttop += kAquaSpacePopupMenuFrameBoundsDiffBottom;
853 currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;//from top
855 int left = (nUsableWidth - nCheckboxMaxWidth) / 2;
856 controlRect.origin.x = left;
857 controlRect.origin.y = currenttop;
858 controlRect.size.width = nPopupMaxWidth;
859 [activeControl setFrame:controlRect];
860 SAL_INFO("fpicker.aqua","setting checkbox at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}");
862 currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;
866 m_bIsUserPaneLaidOut = true;
869 void ControlHelper::createFilterControl()
871 NSString* sLabel = CResourceProvider::getResString(CommonFilePickerElementIds::LISTBOX_FILTER_LABEL);
873 m_pFilterControl = [NSPopUpButton new];
875 [m_pFilterControl setAction:@selector(filterSelectedAtIndex:)];
876 [m_pFilterControl setTarget:m_pDelegate];
878 NSMenu *menu = [m_pFilterControl menu];
880 for (auto const& filterName : *m_pFilterHelper->getFilterNames())
882 SAL_INFO("fpicker.aqua","adding filter name: " << [filterName UTF8String]);
883 if ([filterName isEqualToString:@"-"]) {
884 [menu addItem:[NSMenuItem separatorItem]];
887 [m_pFilterControl addItemWithTitle:filterName];
891 // always add the filter as first item
892 m_aActiveControls.push_front(m_pFilterControl);
893 m_aMapListLabels[m_pFilterControl] = [sLabel retain];
896 int ControlHelper::getVerticalDistance(const NSControl* first, const NSControl* second)
899 return kAquaSpaceBoxFrameViewDiffTop;
901 else if (second == nil) {
902 return kAquaSpaceBoxFrameViewDiffBottom;
905 Class firstClass = [first class];
906 Class secondClass = [second class];
908 if (firstClass == [NSPopUpButton class]) {
909 if (secondClass == [NSPopUpButton class]) {
910 return kAquaSpaceBetweenPopupMenus;
913 return kAquaSpaceAfterPopupButtonsV;
917 return kAquaSpaceBetweenControls;
921 void ControlHelper::updateFilterUI()
923 if (!m_bIsFilterControlNeeded || m_pFilterHelper == nullptr) {
924 SAL_INFO("fpicker.aqua","no filter control needed or no filter helper present");
928 int index = m_pFilterHelper->getCurrentFilterIndex();
930 if (m_pFilterControl == nil) {
931 createFilterControl();
934 [m_pFilterControl selectItemAtIndex:index];
937 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */