calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / fpicker / source / aqua / ControlHelper.mm
blob88f0b655cb589c8e7f2686191ff8abcb19bb2b7e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
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"
32 #pragma mark DEFINES
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;
41 namespace {
43 uno::Any HandleGetListValue(const NSControl* pControl, const sal_Int16 nControlAction)
45     uno::Any aAny;
47     if ([pControl class] != [NSPopUpButton class]) {
48         SAL_INFO("fpicker.aqua","not a popup button");
49         return aAny;
50     }
52     NSPopUpButton *pButton = static_cast<NSPopUpButton*>(pControl);
53     NSMenu *rMenu = [pButton menu];
54     if (nil == rMenu) {
55         SAL_INFO("fpicker.aqua","button has no menu");
56         return aAny;
57     }
59     switch (nControlAction)
60     {
61         case ControlActions::GET_ITEMS:
62         {
63             SAL_INFO("fpicker.aqua","GET_ITEMS");
64             uno::Sequence< OUString > aItemList;
66             int nItems = [rMenu numberOfItems];
67             if (nItems > 0) {
68                 aItemList.realloc(nItems);
69                 OUString* pItemList = aItemList.getArray();
70                 for (int i = 0; i < nItems; i++) {
71                     NSString* sCFItem = [pButton itemTitleAtIndex:i];
72                     if (nil != sCFItem) {
73                         pItemList[i] = [sCFItem OUString];
74                         SAL_INFO("fpicker.aqua","Return value[" << (i - 1) << "]: " << aItemList[i - 1]);
75                     }
76                 }
77             }
79             aAny <<= aItemList;
80         }
81             break;
82         case ControlActions::GET_SELECTED_ITEM:
83         {
84             SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM");
85             NSString* sCFItem = [pButton titleOfSelectedItem];
86             if (nil != sCFItem) {
87                 OUString sString = [sCFItem OUString];
88                 SAL_INFO("fpicker.aqua","Return value: " << sString);
89                 aAny <<= sString;
90             }
91         }
92             break;
93         case ControlActions::GET_SELECTED_ITEM_INDEX:
94         {
95             SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM_INDEX");
96             sal_Int32 nActive = [pButton indexOfSelectedItem];
97             SAL_INFO("fpicker.aqua","Return value: " << nActive);
98             aAny <<= nActive;
99         }
100             break;
101         default:
102             SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
103             break;
104     }
106     return aAny;
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];
118     return textField;
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)
135     int i;
137     for( i = 0; i < TOGGLE_LAST; i++ ) {
138         m_bToggleVisibility[i] = false;
139     }
141     for( i = 0; i < LIST_LAST; i++ ) {
142         m_bListVisibility[i] = false;
143     }
146 ControlHelper::~ControlHelper()
148     NSAutoreleasePool *pool = [NSAutoreleasePool new];
150     if (nullptr != m_pUserPane) {
151         [m_pUserPane release];
152     }
154     if (m_pFilterControl != nullptr) {
155         [m_pFilterControl setTarget:nil];
156     }
158     for (auto const& activeControl : m_aActiveControls)
159     {
160         NSString* sLabelName = m_aMapListLabels[activeControl];
161         if (sLabelName != nil) {
162             [sLabelName release];
163         }
164         if ([activeControl class] == [NSPopUpButton class]) {
165             NSTextField* pField = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)];
166             if (pField != nil) {
167                 [pField release];
168             }
169         }
170         [activeControl release];
171     }
173     [pool release];
176 #pragma mark XInitialization delegate
178 // XInitialization delegate
180 void ControlHelper::initialize( sal_Int16 nTemplateId )
182     switch( nTemplateId )
183     {
184         case FILESAVE_AUTOEXTENSION_PASSWORD:
185             m_bToggleVisibility[AUTOEXTENSION] = true;
186             m_bToggleVisibility[PASSWORD] = true;
187             break;
188         case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
189             m_bToggleVisibility[AUTOEXTENSION] = true;
190             m_bToggleVisibility[PASSWORD] = true;
191             m_bToggleVisibility[FILTEROPTIONS] = true;
192             break;
193         case FILESAVE_AUTOEXTENSION_SELECTION:
194             m_bToggleVisibility[AUTOEXTENSION] = true;
195             m_bToggleVisibility[SELECTION] = true;
196             break;
197         case FILESAVE_AUTOEXTENSION_TEMPLATE:
198             m_bToggleVisibility[AUTOEXTENSION] = true;
199             m_bListVisibility[TEMPLATE] = true;
200             break;
201         case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
202             m_bToggleVisibility[LINK] = true;
203             m_bToggleVisibility[PREVIEW] = true;
204             m_bListVisibility[IMAGE_TEMPLATE] = true;
205             break;
206         case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
207             m_bToggleVisibility[LINK] = true;
208             m_bToggleVisibility[PREVIEW] = true;
209             m_bListVisibility[IMAGE_ANCHOR] = true;
210             break;
211         case FILEOPEN_READONLY_VERSION:
212             m_bToggleVisibility[READONLY] = true;
213             m_bListVisibility[VERSION] = true;
214             break;
215         case FILEOPEN_LINK_PREVIEW:
216             m_bToggleVisibility[LINK] = true;
217             m_bToggleVisibility[PREVIEW] = true;
218             break;
219         case FILESAVE_AUTOEXTENSION:
220             m_bToggleVisibility[AUTOEXTENSION] = true;
221             break;
222         case FILEOPEN_PREVIEW:
223             m_bToggleVisibility[PREVIEW] = true;
224             break;
225         case FILEOPEN_LINK_PLAY:
226             m_bToggleVisibility[LINK] = true;
227     }
229     createControls();
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");
243         return;
244     }
246     NSControl* pControl = getControl(nControlId);
248     if( pControl != nil ) {
249         if( bEnable ) {
250             SAL_INFO("fpicker.aqua", "enable" );
251         } else {
252             SAL_INFO("fpicker.aqua", "disable" );
253         }
254         [pControl setEnabled:bEnable];
255     } else {
256         SAL_INFO("fpicker.aqua","enable unknown control " << nControlId );
257     }
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);
268         return OUString();
269     }
271     OUString retVal;
272     if ([pControl class] == [NSPopUpButton class]) {
273         NSString *temp = m_aMapListLabels[pControl];
274         if (temp != nil)
275             retVal = [temp OUString];
276     }
277     else {
278         NSString* sLabel = [[pControl cell] title];
279         retVal = [sLabel OUString];
280     }
282     return retVal;
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) {
297                 [sOldName release];
298             }
300             m_aMapListLabels[pControl] = [aLabel retain];
301         } else if ([pControl class] == [NSButton class]) {
302             [[pControl cell] setTitle:aLabel];
303         }
304     } else {
305         SAL_INFO("fpicker.aqua","Control not found to set label for");
306     }
308     layoutControls();
310     [pool release];
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");
319     }
320     else {
321         NSControl* pControl = getControl( nControlId );
323         if( pControl == nil ) {
324             SAL_INFO("fpicker.aqua","enable unknown control " << nControlId);
325         } else {
326             if( [pControl class] == [NSPopUpButton class] ) {
327                 HandleSetListValue(pControl, nControlAction, rValue);
328             } else if( [pControl class] == [NSButton class] ) {
329                 bool bChecked = false;
330                 rValue >>= bChecked;
331                 SAL_INFO("fpicker.aqua"," value is a bool: " << bChecked);
332                 [static_cast<NSButton*>(pControl) setState:(bChecked ? NSControlStateValueOn : NSControlStateValueOff)];
333             } else
334             {
335                 SAL_INFO("fpicker.aqua","Can't set value on button / list " << nControlId << " " << nControlAction);
336             }
337         }
338     }
341 uno::Any ControlHelper::getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const
343     SolarMutexGuard aGuard;
344     uno::Any aRetval;
346     NSControl* pControl = getControl( nControlId );
348     if( pControl == nil ) {
349         SAL_INFO("fpicker.aqua","get value for unknown control " << nControlId);
350     } else {
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;
356             aRetval <<= bValue;
357             SAL_INFO("fpicker.aqua","value is a bool (checkbox): " << bValue);
358         }
359     }
361     return aRetval;
364 void ControlHelper::createUserPane()
366     if (!m_bUserPaneNeeded) {
367         SAL_INFO("fpicker.aqua","no user pane needed");
368         return;
369     }
371     if (nil != m_pUserPane) {
372         SAL_INFO("fpicker.aqua","user pane already exists");
373         return;
374     }
376     if (m_bIsFilterControlNeeded && m_pFilterControl == nil) {
377         createFilterControl();
378     }
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;
393     size_t nLoop = 0;
394     for (auto const& activeControl : m_aActiveControls)
395     {
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;
412             }
413             else if (nLoop)
414             {
415                 currentHeight += kAquaSpaceBetweenControls;
416             }
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;
435             }
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];
443             }
445             if (nControlWidth > POPUP_WIDTH_MAX) {
446                 nControlWidth = POPUP_WIDTH_MAX;
447                 frame.size.width = nControlWidth;
448                 [activeControl setFrame:frame];
449             }
451             //set the max size
452             if (nPopupMaxWidth < nControlWidth) {
453                 nPopupMaxWidth = nControlWidth;
454             }
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;
460             }
462             nControlHeight -= kAquaSpacePopupMenuFrameBoundsDiffV;
463         }
464         else if ([activeControl class] == [NSButton class]) {
465             if (nLoop)
466             {
467                 currentHeight += kAquaSpaceBetweenControls;
468             }
470             if (nCheckboxMaxWidth < nControlWidth) {
471                 nCheckboxMaxWidth = nControlWidth;
472             }
474             bButtonControlPresent = true;
475             nControlWidth -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
476             nControlHeight -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
477         }
479         // if ((nControlWidth + 2 * kAquaSpaceInsideGroupH) > currentWidth) {
480         //     currentWidth = nControlWidth + 2 * kAquaSpaceInsideGroupH;
481         // }
483         currentHeight += nControlHeight;
485         [m_pUserPane addSubview:activeControl];
486         ++nLoop;
487     }
489     SAL_INFO("fpicker.aqua","height after adding all controls: " << currentHeight);
491     if (bPopupControlPresent && bButtonControlPresent)
492     {
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);
497     }
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];
517     layoutControls();
520 #pragma mark Private / Misc
522 // 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 ) \
536  case elem: \
537      setLabel(ExtendedFilePickerElementIds::LISTBOX_##elem, sLabel); \
538      break
540             switch(i) {
541                 MAP_LIST_(VERSION);
542                 MAP_LIST_(TEMPLATE);
543                 MAP_LIST_(IMAGE_TEMPLATE);
544                 MAP_LIST_(IMAGE_ANCHOR);
545             }
547             m_aActiveControls.push_back(m_pListControls[i]);
548         } else {
549             m_pListControls[i] = nil;
550         }
551     }
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:)];
570             }
572             m_pToggles[i] = button;
574             m_aActiveControls.push_back(m_pToggles[i]);
575         } else {
576             m_pToggles[i] = nil;
577         }
578     }
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];
585     }
588 #define TOGGLE_ELEMENT( elem ) \
589 case elem: \
590     nReturn = CHECKBOX_##elem; \
591     return nReturn
592 #define LIST_ELEMENT( elem ) \
593 case elem: \
594     nReturn = LISTBOX_##elem##_LABEL; \
595     return nReturn
597 int ControlHelper::getControlElementName(const Class aClazz, const int nControlId)
599     int nReturn = -1;
600     if (aClazz == [NSButton class])
601     {
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 );
610         }
611     }
612     else if (aClazz == [NSPopUpButton class])
613     {
614         switch (nControlId) {
615             LIST_ELEMENT( VERSION );
616             LIST_ELEMENT( TEMPLATE );
617             LIST_ELEMENT( IMAGE_TEMPLATE );
618             LIST_ELEMENT( IMAGE_ANCHOR );
619         }
620     }
622     return nReturn;
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");
629         return;
630     }
632     NSPopUpButton *pButton = static_cast<NSPopUpButton*>(pControl);
633     NSMenu *rMenu = [pButton menu];
634     if (nil == rMenu) {
635         SAL_INFO("fpicker.aqua","button has no menu");
636         return;
637     }
639     switch (nControlAction)
640     {
641         case ControlActions::ADD_ITEM:
642         {
643             SAL_INFO("fpicker.aqua","ADD_ITEMS");
644             OUString sItem;
645             rValue >>= sItem;
647             NSString* sCFItem = [NSString stringWithOUString:sItem];
648             SAL_INFO("fpicker.aqua","Adding menu item: " << sItem);
649             [pButton addItemWithTitle:sCFItem];
650         }
651             break;
652         case ControlActions::ADD_ITEMS:
653         {
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)
659             {
660                 NSString* sCFItem = [NSString stringWithOUString:aStringList[i]];
661                 SAL_INFO("fpicker.aqua","Adding menu item: " << aStringList[i]);
662                 [pButton addItemWithTitle:sCFItem];
663             }
664         }
665             break;
666         case ControlActions::DELETE_ITEM:
667         {
668             SAL_INFO("fpicker.aqua","DELETE_ITEM");
669             sal_Int32 nPos = -1;
670             rValue >>= nPos;
671             SAL_INFO("fpicker.aqua","Deleting item at position " << (nPos));
672             [rMenu removeItemAtIndex:nPos];
673         }
674             break;
675         case ControlActions::DELETE_ITEMS:
676         {
677             SAL_INFO("fpicker.aqua","DELETE_ITEMS");
678             int nItems = [rMenu numberOfItems];
679             if (nItems == 0) {
680                 SAL_INFO("fpicker.aqua","no menu items to delete");
681                 return;
682             }
683             for(sal_Int32 i = 0; i < nItems; i++) {
684                 [rMenu removeItemAtIndex:i];
685             }
686         }
687             break;
688         case ControlActions::SET_SELECT_ITEM:
689         {
690             sal_Int32 nPos = -1;
691             rValue >>= nPos;
692             SAL_INFO("fpicker.aqua","Selecting item at position " << nPos);
693             [pButton selectItemAtIndex:nPos];
694         }
695             break;
696         default:
697             SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
698             break;
699     }
701     layoutControls();
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]; \
712     break
714 #define MAP_LIST( elem ) \
715 case ExtendedFilePickerElementIds::LISTBOX_##elem: \
716     pWidget = m_pListControls[elem]; \
717     break
719 #define MAP_LIST_LABEL( elem ) \
720 case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \
721     pWidget = m_pListControls[elem]; \
722     break
724     switch( nControlId )
725     {
726             MAP_TOGGLE( AUTOEXTENSION );
727             MAP_TOGGLE( PASSWORD );
728             MAP_TOGGLE( FILTEROPTIONS );
729             MAP_TOGGLE( READONLY );
730             MAP_TOGGLE( LINK );
731             MAP_TOGGLE( PREVIEW );
732             MAP_TOGGLE( SELECTION );
733             //MAP_BUTTON( PLAY );
734             MAP_LIST( VERSION );
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 );
742         default:
743             SAL_INFO("fpicker.aqua","Handle unknown control " << nControlId);
744             break;
745     }
746 #undef MAP
748     return pWidget;
751 void ControlHelper::layoutControls()
753     SolarMutexGuard aGuard;
755     if (nil == m_pUserPane) {
756         SAL_INFO("fpicker.aqua","no user pane to layout");
757         return;
758     }
760     if (m_bIsUserPaneLaidOut) {
761         SAL_INFO("fpicker.aqua","user pane already laid out");
762         return;
763     }
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)
779     {
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;
788             }
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;
794             }
795         } else {
796             if (nCheckboxMaxWidth < nControlWidth) {
797                 nCheckboxMaxWidth = nControlWidth;
798             }
799         }
800     }
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)
810     {
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;
852         } else {
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;
863         }
864     }
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())
881     {
882         SAL_INFO("fpicker.aqua","adding filter name: " << [filterName UTF8String]);
883         if ([filterName isEqualToString:@"-"]) {
884             [menu addItem:[NSMenuItem separatorItem]];
885         }
886         else {
887             [m_pFilterControl addItemWithTitle:filterName];
888         }
889     }
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)
898     if (first == nil) {
899         return kAquaSpaceBoxFrameViewDiffTop;
900     }
901     else if (second == nil) {
902         return kAquaSpaceBoxFrameViewDiffBottom;
903     }
904     else {
905         Class firstClass = [first class];
906         Class secondClass = [second class];
908         if (firstClass == [NSPopUpButton class]) {
909             if (secondClass == [NSPopUpButton class]) {
910                 return kAquaSpaceBetweenPopupMenus;
911             }
912             else {
913                 return kAquaSpaceAfterPopupButtonsV;
914             }
915         }
917         return kAquaSpaceBetweenControls;
918     }
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");
925         return;
926     }
928     int index = m_pFilterHelper->getCurrentFilterIndex();
930     if (m_pFilterControl == nil) {
931         createFilterControl();
932     }
934     [m_pFilterControl selectItemAtIndex:index];
937 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */