Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / fpicker / source / aqua / ControlHelper.mm
blob08a3971a6cf9bd3417c1913034572cf5ed8621cb
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 "CFStringUtilities.hxx"
27 #include "resourceprovider.hxx"
28 #include "NSString_OOoAdditions.hxx"
29 #include "sal/log.hxx"
31 #include "ControlHelper.hxx"
33 #pragma mark DEFINES
34 #define CLASS_NAME "ControlHelper"
35 #define POPUP_WIDTH_MIN 200
36 #define POPUP_WIDTH_MAX 350
38 using namespace ::com::sun::star::ui::dialogs;
39 using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
40 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
41 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
42 using namespace ::rtl;
44 #pragma mark Constructor / Destructor
46 // Constructor / Destructor
48 ControlHelper::ControlHelper()
49 : m_pUserPane(NULL)
50 , m_pFilterControl(nil)
51 , m_bUserPaneNeeded( false )
52 , m_bIsUserPaneLaidOut(false)
53 , m_bIsFilterControlNeeded(false)
54 , m_pFilterHelper(NULL)
56     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
58     int i;
60     for( i = 0; i < TOGGLE_LAST; i++ ) {
61         m_bToggleVisibility[i] = false;
62     }
64     for( i = 0; i < LIST_LAST; i++ ) {
65         m_bListVisibility[i] = false;
66     }
68     DBG_PRINT_EXIT(CLASS_NAME, __func__);
71 ControlHelper::~ControlHelper()
73     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
75     NSAutoreleasePool *pool = [NSAutoreleasePool new];
77     if (NULL != m_pUserPane) {
78         [m_pUserPane release];
79     }
81     if (m_pFilterControl != NULL) {
82         [m_pFilterControl setTarget:nil];
83     }
85     for(std::list<NSControl *>::iterator control = m_aActiveControls.begin(); control != m_aActiveControls.end(); ++control) {
86         NSControl* pControl = (*control);
87         NSString* sLabelName = m_aMapListLabels[pControl];
88         if (sLabelName != nil) {
89             [sLabelName release];
90         }
91         if ([pControl class] == [NSPopUpButton class]) {
92             NSTextField* pField = m_aMapListLabelFields[(NSPopUpButton*)pControl];
93             if (pField != nil) {
94                 [pField release];
95             }
96         }
97         [pControl release];
98     }
100     [pool release];
102     DBG_PRINT_EXIT(CLASS_NAME, __func__);
105 #pragma mark XInitialization delegate
107 // XInitialization delegate
109 void ControlHelper::initialize( sal_Int16 nTemplateId )
111     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "templateId", nTemplateId);
113     switch( nTemplateId )
114     {
115         case FILESAVE_AUTOEXTENSION_PASSWORD:
116             m_bToggleVisibility[AUTOEXTENSION] = true;
117             m_bToggleVisibility[PASSWORD] = true;
118             break;
119         case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
120             m_bToggleVisibility[AUTOEXTENSION] = true;
121             m_bToggleVisibility[PASSWORD] = true;
122             m_bToggleVisibility[FILTEROPTIONS] = true;
123             break;
124         case FILESAVE_AUTOEXTENSION_SELECTION:
125             m_bToggleVisibility[AUTOEXTENSION] = true;
126             m_bToggleVisibility[SELECTION] = true;
127             break;
128         case FILESAVE_AUTOEXTENSION_TEMPLATE:
129             m_bToggleVisibility[AUTOEXTENSION] = true;
130             m_bListVisibility[TEMPLATE] = true;
131             break;
132         case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
133             m_bToggleVisibility[LINK] = true;
134             m_bToggleVisibility[PREVIEW] = true;
135             m_bListVisibility[IMAGE_TEMPLATE] = true;
136             break;
137         case FILEOPEN_READONLY_VERSION:
138             m_bToggleVisibility[READONLY] = true;
139             m_bListVisibility[VERSION] = true;
140             break;
141         case FILEOPEN_LINK_PREVIEW:
142             m_bToggleVisibility[LINK] = true;
143             m_bToggleVisibility[PREVIEW] = true;
144             break;
145         case FILESAVE_AUTOEXTENSION:
146             m_bToggleVisibility[AUTOEXTENSION] = true;
147             break;
148     }
150     createControls();
152     DBG_PRINT_EXIT(CLASS_NAME, __func__);
155 #pragma mark XFilePickerControlAccess delegates
157 // XFilePickerControlAccess functions
160 void ControlHelper::enableControl( const sal_Int16 nControlId, const sal_Bool bEnable ) const
162     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "enable", bEnable);
164     SolarMutexGuard aGuard;
166     if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
167         SAL_INFO("fpicker.aqua"," preview checkbox cannot be changed");
168         DBG_PRINT_EXIT(CLASS_NAME, __func__);
169         return;
170     }
172     NSControl* pControl = getControl(nControlId);
174     if( pControl != nil ) {
175         if( bEnable ) {
176             SAL_INFO("fpicker.aqua", "enable" );
177         } else {
178             SAL_INFO("fpicker.aqua", "disable" );
179         }
180         [pControl setEnabled:bEnable];
181     } else {
182         SAL_INFO("fpicker.aqua","enable unknown control " << nControlId );
183     }
185     DBG_PRINT_EXIT(CLASS_NAME, __func__);
188 OUString ControlHelper::getLabel( sal_Int16 nControlId )
190     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId);
192     SolarMutexGuard aGuard;
194     NSControl* pControl = getControl( nControlId );
196     if( pControl == nil ) {
197         SAL_INFO("fpicker.aqua","Get label for unknown control " << nControlId);
198         return OUString();
199     }
201     rtl::OUString retVal;
202     if ([pControl class] == [NSPopUpButton class]) {
203         NSString *temp = m_aMapListLabels[pControl];
204         if (temp != nil)
205             retVal = [temp OUString];
206     }
207     else {
208         NSString* sLabel = [[pControl cell] title];
209         retVal = [sLabel OUString];
210     }
212     DBG_PRINT_EXIT(CLASS_NAME, __func__, retVal);
214     return retVal;
217 void ControlHelper::setLabel( sal_Int16 nControlId, NSString* aLabel )
219     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "label", aLabel);
221     SolarMutexGuard aGuard;
223     NSAutoreleasePool *pool = [NSAutoreleasePool new];
225     NSControl* pControl = getControl(nControlId);
227     if (nil != pControl) {
228         if ([pControl class] == [NSPopUpButton class]) {
229             NSString *sOldName = m_aMapListLabels[pControl];
230             if (sOldName != NULL && sOldName != aLabel) {
231                 [sOldName release];
232             }
234             m_aMapListLabels[pControl] = [aLabel retain];
235         } else if ([pControl class] == [NSButton class]) {
236             [[pControl cell] setTitle:aLabel];
237         }
238     } else {
239         SAL_INFO("fpicker.aqua","Control not found to set label for");
240     }
242     layoutControls();
244     [pool release];
246     DBG_PRINT_EXIT(CLASS_NAME, __func__);
249 void ControlHelper::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
251     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "controlAction", nControlAction);
253     SolarMutexGuard aGuard;
255     if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
256         SAL_INFO("fpicker.aqua"," value for preview is unchangeable");
257     }
258     else {
259         NSControl* pControl = getControl( nControlId );
261         if( pControl == nil ) {
262             SAL_INFO("fpicker.aqua","enable unknown control " << nControlId);
263         } else {
264             if( [pControl class] == [NSPopUpButton class] ) {
265                 HandleSetListValue(pControl, nControlAction, rValue);
266             } else if( [pControl class] == [NSButton class] ) {
267                 sal_Bool bChecked = false;
268                 rValue >>= bChecked;
269                 SAL_INFO("fpicker.aqua"," value is a bool: " << bChecked);
270                 [(NSButton*)pControl setState:(bChecked ? NSOnState : NSOffState)];
271             } else
272             {
273                 SAL_INFO("fpicker.aqua","Can't set value on button / list " << nControlId << " " << nControlAction);
274             }
275         }
276     }
278     DBG_PRINT_EXIT(CLASS_NAME, __func__);
281 uno::Any ControlHelper::getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const
283     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "controlAction", nControlAction);
285     SolarMutexGuard aGuard;
286     uno::Any aRetval;
288     NSControl* pControl = getControl( nControlId );
290     if( pControl == nil ) {
291         SAL_INFO("fpicker.aqua","get value for unknown control " << nControlId);
292         aRetval <<= sal_True;
293     } else {
294         if( [pControl class] == [NSPopUpButton class] ) {
295             aRetval = HandleGetListValue(pControl, nControlAction);
296         } else if( [pControl class] == [NSButton class] ) {
297             //NSLog(@"control: %@", [[pControl cell] title]);
298             sal_Bool bValue = [(NSButton*)pControl state] == NSOnState ? sal_True : sal_False;
299             aRetval <<= bValue;
300             SAL_INFO("fpicker.aqua","value is a bool (checkbox): " << bValue);
301         }
302     }
304     DBG_PRINT_EXIT(CLASS_NAME, __func__);
306     return aRetval;
309 void ControlHelper::createUserPane()
311     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
313     if (m_bUserPaneNeeded == false) {
314         SAL_INFO("fpicker.aqua","no user pane needed");
315         DBG_PRINT_EXIT(CLASS_NAME, __func__);
316         return;
317     }
319     if (nil != m_pUserPane) {
320         SAL_INFO("fpicker.aqua","user pane already exists");
321         DBG_PRINT_EXIT(CLASS_NAME, __func__);
322         return;
323     }
325     if (m_bIsFilterControlNeeded == true && m_pFilterControl == nil) {
326         createFilterControl();
327     }
329     NSRect minRect = NSMakeRect(0,0,300,33);
330     m_pUserPane = [[NSView alloc] initWithFrame:minRect];
332     int currentHeight = kAquaSpaceBoxFrameViewDiffTop + kAquaSpaceBoxFrameViewDiffBottom;
333     int currentWidth = 300;
335     sal_Bool bPopupControlPresent = NO;
336     sal_Bool bButtonControlPresent = NO;
338     int nCheckboxMaxWidth = 0;
339     int nPopupMaxWidth = 0;
340     int nPopupLabelMaxWidth = 0;
342     for (::std::list<NSControl*>::iterator child = m_aActiveControls.begin(); child != m_aActiveControls.end(); child++) {
343         SAL_INFO("fpicker.aqua","currentHeight: " << currentHeight);
345         NSControl* pControl = *child;
347         //let the control calculate its size
348         [pControl sizeToFit];
350         NSRect frame = [pControl frame];
351         SAL_INFO("fpicker.aqua","frame for control " << [[pControl description] UTF8String] << " is {" << frame.origin.x << ", " << frame.origin.y << ", " << frame.size.width << ", " << frame.size.height << "}");
353         int nControlHeight = frame.size.height;
354         int nControlWidth = frame.size.width;
356         // Note: controls are grouped by kind, first all popup menus, then checkboxes
357         if ([pControl class] == [NSPopUpButton class]) {
358             if (bPopupControlPresent == YES) {
359                 //this is not the first popup
360                 currentHeight += kAquaSpaceBetweenPopupMenus;
361             }
362             else if (child != m_aActiveControls.begin()){
363                 currentHeight += kAquaSpaceBetweenControls;
364             }
366             bPopupControlPresent = YES;
368             // we have to add the label text width
369             NSString *label = m_aMapListLabels[pControl];
371             NSTextField *textField = createLabelWithString(label);
372             [textField sizeToFit];
373             m_aMapListLabelFields[(NSPopUpButton*)pControl] = textField;
374             [m_pUserPane addSubview:textField];
376             NSRect tfRect = [textField frame];
377             SAL_INFO("fpicker.aqua","frame for textfield " << [[textField description] UTF8String] << " is {" << tfRect.origin.x << ", " << tfRect.origin.y << ", " << tfRect.size.width << ", " << tfRect.size.height << "}");
379             int tfWidth = tfRect.size.width;
381             if (nPopupLabelMaxWidth < tfWidth) {
382                 nPopupLabelMaxWidth = tfWidth;
383             }
385             frame.origin.x += (kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft) + tfWidth;
387             if (nControlWidth < POPUP_WIDTH_MIN) {
388                 nControlWidth = POPUP_WIDTH_MIN;
389                 frame.size.width = nControlWidth;
390                 [pControl setFrame:frame];
391             }
393             if (nControlWidth > POPUP_WIDTH_MAX) {
394                 nControlWidth = POPUP_WIDTH_MAX;
395                 frame.size.width = nControlWidth;
396                 [pControl setFrame:frame];
397             }
399             //set the max size
400             if (nPopupMaxWidth < nControlWidth) {
401                 nPopupMaxWidth = nControlWidth;
402             }
404             nControlWidth += tfWidth + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
405             if (nControlHeight < kAquaPopupButtonDefaultHeight) {
406                 //maybe the popup has no menu item yet, so set a default height
407                 nControlHeight = kAquaPopupButtonDefaultHeight;
408             }
410             nControlHeight -= kAquaSpacePopupMenuFrameBoundsDiffV;
411         }
412         else if ([pControl class] == [NSButton class]) {
413             if (child != m_aActiveControls.begin()){
414                 currentHeight += kAquaSpaceBetweenControls;
415             }
417             if (nCheckboxMaxWidth < nControlWidth) {
418                 nCheckboxMaxWidth = nControlWidth;
419             }
421             bButtonControlPresent = YES;
422             nControlWidth -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
423             nControlHeight -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
424         }
426         // if ((nControlWidth + 2 * kAquaSpaceInsideGroupH) > currentWidth) {
427         //     currentWidth = nControlWidth + 2 * kAquaSpaceInsideGroupH;
428         // }
430         currentHeight += nControlHeight;
432         [m_pUserPane addSubview:pControl];
433     }
435     SAL_INFO("fpicker.aqua","height after adding all controls: " << currentHeight);
437     if (bPopupControlPresent && bButtonControlPresent)
438     {
439         //after a popup button (array) and before a different kind of control we need some extra space instead of the standard
440         currentHeight -= kAquaSpaceBetweenControls;
441         currentHeight += kAquaSpaceAfterPopupButtonsV;
442         SAL_INFO("fpicker.aqua","popup extra space added, currentHeight: " << currentHeight);
443     }
445     int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
447     currentWidth = nLongestPopupWidth > nCheckboxMaxWidth ? nLongestPopupWidth : nCheckboxMaxWidth;
448     SAL_INFO("fpicker.aqua","longest control width: " << currentWidth);
450     currentWidth += 2* kAquaSpaceInsideGroupH;
452     if (currentWidth < minRect.size.width)
453         currentWidth = minRect.size.width;
455     if (currentHeight < minRect.size.height)
456         currentHeight = minRect.size.height;
458     NSRect upRect = NSMakeRect(0, 0, currentWidth, currentHeight );
459     SAL_INFO("fpicker.aqua","setting user pane rect to {" << upRect.origin.x << ", " << upRect.origin.y << ", " << upRect.size.width << ", " << upRect.size.height << "}");
461     [m_pUserPane setFrame:upRect];
463     layoutControls();
465     DBG_PRINT_EXIT(CLASS_NAME, __func__);
468 #pragma mark Private / Misc
470 // Private / Misc
472 void ControlHelper::createControls()
474     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
476     CResourceProvider aResProvider;
477     for (int i = 0; i < LIST_LAST; i++) {
478         if (true == m_bListVisibility[i]) {
479             m_bUserPaneNeeded = true;
481             int elementName = getControlElementName([NSPopUpButton class], i);
482             NSString* sLabel = aResProvider.getResString(elementName);
484             m_pListControls[i] = [NSPopUpButton new];
486 #define MAP_LIST_( elem ) \
487  case elem: \
488      setLabel(ExtendedFilePickerElementIds::LISTBOX_##elem, sLabel); \
489      break
491             switch(i) {
492                 MAP_LIST_(VERSION);
493                 MAP_LIST_(TEMPLATE);
494                 MAP_LIST_(IMAGE_TEMPLATE);
495             }
497             m_aActiveControls.push_back(m_pListControls[i]);
498         } else {
499             m_pListControls[i] = nil;
500         }
501     }
503     for (int i = 0/*#i102102*/; i < TOGGLE_LAST; i++) {
504         if (true == m_bToggleVisibility[i]) {
505             m_bUserPaneNeeded = true;
507             int elementName = getControlElementName([NSButton class], i);
508             NSString* sLabel = aResProvider.getResString(elementName);
510             NSButton *button = [NSButton new];
511             [button setTitle:sLabel];
513             [button setButtonType:NSSwitchButton];
515             [button setState:NSOffState];
517             if (i == AUTOEXTENSION) {
518                 [button setTarget:m_pDelegate];
519                 [button setAction:@selector(autoextensionChanged:)];
520             }
522             m_pToggles[i] = button;
524             m_aActiveControls.push_back(m_pToggles[i]);
525         } else {
526             m_pToggles[i] = nil;
527         }
528     }
530     //preview is always on with Mac OS X
531     NSControl *pPreviewBox = m_pToggles[PREVIEW];
532     if (pPreviewBox != nil) {
533         [pPreviewBox setEnabled:NO];
534         [(NSButton*)pPreviewBox setState:NSOnState];
535     }
537     DBG_PRINT_EXIT(CLASS_NAME, __func__);
540 #define TOGGLE_ELEMENT( elem ) \
541 case elem: \
542     nReturn = CHECKBOX_##elem; \
543     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn); \
544     return nReturn
545 #define LIST_ELEMENT( elem ) \
546 case elem: \
547     nReturn = LISTBOX_##elem##_LABEL; \
548     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn); \
549     return nReturn
551 int ControlHelper::getControlElementName(const Class aClazz, const int nControlId) const
553     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aClazz", [[aClazz description] UTF8String], "controlId", nControlId);
555     int nReturn = -1;
556     if (aClazz == [NSButton class])
557     {
558         switch (nControlId) {
559             TOGGLE_ELEMENT( AUTOEXTENSION );
560             TOGGLE_ELEMENT( PASSWORD );
561             TOGGLE_ELEMENT( FILTEROPTIONS );
562             TOGGLE_ELEMENT( READONLY );
563             TOGGLE_ELEMENT( LINK );
564             TOGGLE_ELEMENT( PREVIEW );
565             TOGGLE_ELEMENT( SELECTION );
566         }
567     }
568     else if (aClazz == [NSPopUpButton class])
569     {
570         switch (nControlId) {
571             LIST_ELEMENT( VERSION );
572             LIST_ELEMENT( TEMPLATE );
573             LIST_ELEMENT( IMAGE_TEMPLATE );
574         }
575     }
577     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn);
579     return nReturn;
582 void ControlHelper::HandleSetListValue(const NSControl* pControl, const sal_Int16 nControlAction, const uno::Any& rValue)
584     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlAction", nControlAction);
586     if ([pControl class] != [NSPopUpButton class]) {
587         SAL_INFO("fpicker.aqua","not a popup menu");
588         DBG_PRINT_EXIT(CLASS_NAME, __func__);
589         return;
590     }
592     NSPopUpButton *pButton = (NSPopUpButton*)pControl;
593     NSMenu *rMenu = [pButton menu];
594     if (nil == rMenu) {
595         SAL_INFO("fpicker.aqua","button has no menu");
596         DBG_PRINT_EXIT(CLASS_NAME, __func__);
597         return;
598     }
600     switch (nControlAction)
601     {
602         case ControlActions::ADD_ITEM:
603         {
604             SAL_INFO("fpicker.aqua","ADD_ITEMS");
605             OUString sItem;
606             rValue >>= sItem;
608             NSString* sCFItem = [NSString stringWithOUString:sItem];
609             SAL_INFO("fpicker.aqua","Adding menu item: " << OUStringToOString(sItem, RTL_TEXTENCODING_UTF8).getStr());
610             [pButton addItemWithTitle:sCFItem];
611         }
612             break;
613         case ControlActions::ADD_ITEMS:
614         {
615             SAL_INFO("fpicker.aqua","ADD_ITEMS");
616             uno::Sequence< OUString > aStringList;
617             rValue >>= aStringList;
618             sal_Int32 nItemCount = aStringList.getLength();
619             for (sal_Int32 i = 0; i < nItemCount; ++i)
620             {
621                 NSString* sCFItem = [NSString stringWithOUString:aStringList[i]];
622                 SAL_INFO("fpicker.aqua","Adding menu item: " << OUStringToOString(aStringList[i], RTL_TEXTENCODING_UTF8).getStr());
623                 [pButton addItemWithTitle:sCFItem];
624             }
625         }
626             break;
627         case ControlActions::DELETE_ITEM:
628         {
629             SAL_INFO("fpicker.aqua","DELETE_ITEM");
630             sal_Int32 nPos = -1;
631             rValue >>= nPos;
632             SAL_INFO("fpicker.aqua","Deleting item at position " << (nPos));
633             [rMenu removeItemAtIndex:nPos];
634         }
635             break;
636         case ControlActions::DELETE_ITEMS:
637         {
638             SAL_INFO("fpicker.aqua","DELETE_ITEMS");
639             int nItems = [rMenu numberOfItems];
640             if (nItems == 0) {
641                 SAL_INFO("fpicker.aqua","no menu items to delete");
642                 DBG_PRINT_EXIT(CLASS_NAME, __func__);
643                 return;
644             }
645             for(sal_Int32 i = 0; i < nItems; i++) {
646                 [rMenu removeItemAtIndex:i];
647             }
648         }
649             break;
650         case ControlActions::SET_SELECT_ITEM:
651         {
652             sal_Int32 nPos = -1;
653             rValue >>= nPos;
654             SAL_INFO("fpicker.aqua","Selecting item at position " << nPos);
655             [pButton selectItemAtIndex:nPos];
656         }
657             break;
658         default:
659             SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
660             break;
661     }
663     layoutControls();
665     DBG_PRINT_EXIT(CLASS_NAME, __func__);
669 uno::Any ControlHelper::HandleGetListValue(const NSControl* pControl, const sal_Int16 nControlAction) const
671     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlAction", nControlAction);
673     uno::Any aAny;
675     if ([pControl class] != [NSPopUpButton class]) {
676         SAL_INFO("fpicker.aqua","not a popup button");
677         DBG_PRINT_EXIT(CLASS_NAME, __func__);
678         return aAny;
679     }
681     NSPopUpButton *pButton = (NSPopUpButton*)pControl;
682     NSMenu *rMenu = [pButton menu];
683     if (nil == rMenu) {
684         SAL_INFO("fpicker.aqua","button has no menu");
685         DBG_PRINT_EXIT(CLASS_NAME, __func__);
686         return aAny;
687     }
689     switch (nControlAction)
690     {
691         case ControlActions::GET_ITEMS:
692         {
693             SAL_INFO("fpicker.aqua","GET_ITEMS");
694             uno::Sequence< OUString > aItemList;
696             int nItems = [rMenu numberOfItems];
697             if (nItems > 0) {
698                 aItemList.realloc(nItems);
699             }
700             for (int i = 0; i < nItems; i++) {
701                 NSString* sCFItem = [pButton itemTitleAtIndex:i];
702                 if (nil != sCFItem) {
703                     aItemList[i] = [sCFItem OUString];
704                     SAL_INFO("fpicker.aqua","Return value[" << (i - 1) << "]: " << OUStringToOString(aItemList[i - 1], RTL_TEXTENCODING_UTF8).getStr());
705                 }
706             }
708             aAny <<= aItemList;
709         }
710             break;
711         case ControlActions::GET_SELECTED_ITEM:
712         {
713             SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM");
714             NSString* sCFItem = [pButton titleOfSelectedItem];
715             if (nil != sCFItem) {
716                 OUString sString = [sCFItem OUString];
717                 SAL_INFO("fpicker.aqua","Return value: " << OUStringToOString(sString, RTL_TEXTENCODING_UTF8).getStr());
718                 aAny <<= sString;
719             }
720         }
721             break;
722         case ControlActions::GET_SELECTED_ITEM_INDEX:
723         {
724             SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM_INDEX");
725             sal_Int32 nActive = [pButton indexOfSelectedItem];
726             SAL_INFO("fpicker.aqua","Return value: " << nActive);
727             aAny <<= nActive;
728         }
729             break;
730         default:
731             SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
732             break;
733     }
735     DBG_PRINT_EXIT(CLASS_NAME, __func__);
737     return aAny;
741 // cf. offapi/com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.idl
742 NSControl* ControlHelper::getControl( const sal_Int16 nControlId ) const
744     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId);
746     NSControl* pWidget = nil;
748 #define MAP_TOGGLE( elem ) \
749 case ExtendedFilePickerElementIds::CHECKBOX_##elem: \
750     pWidget = m_pToggles[elem]; \
751     break
753 #define MAP_LIST( elem ) \
754 case ExtendedFilePickerElementIds::LISTBOX_##elem: \
755     pWidget = m_pListControls[elem]; \
756     break
758 #define MAP_LIST_LABEL( elem ) \
759 case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \
760     pWidget = m_pListControls[elem]; \
761     break
763     switch( nControlId )
764     {
765             MAP_TOGGLE( AUTOEXTENSION );
766             MAP_TOGGLE( PASSWORD );
767             MAP_TOGGLE( FILTEROPTIONS );
768             MAP_TOGGLE( READONLY );
769             MAP_TOGGLE( LINK );
770             MAP_TOGGLE( PREVIEW );
771             MAP_TOGGLE( SELECTION );
772             //MAP_BUTTON( PLAY );
773             MAP_LIST( VERSION );
774             MAP_LIST( TEMPLATE );
775             MAP_LIST( IMAGE_TEMPLATE );
776             MAP_LIST_LABEL( VERSION );
777             MAP_LIST_LABEL( TEMPLATE );
778             MAP_LIST_LABEL( IMAGE_TEMPLATE );
779         default:
780             SAL_INFO("fpicker.aqua","Handle unknown control " << nControlId);
781             break;
782     }
783 #undef MAP
785     DBG_PRINT_EXIT(CLASS_NAME, __func__);
787     return pWidget;
790 void ControlHelper::layoutControls()
792     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
794     SolarMutexGuard aGuard;
796     if (nil == m_pUserPane) {
797         SAL_INFO("fpicker.aqua","no user pane to layout");
798         DBG_PRINT_EXIT(CLASS_NAME, __func__);
799         return;
800     }
802     if (m_bIsUserPaneLaidOut == true) {
803         SAL_INFO("fpicker.aqua","user pane already laid out");
804         DBG_PRINT_EXIT(CLASS_NAME, __func__);
805         return;
806     }
808     NSRect userPaneRect = [m_pUserPane frame];
809     SAL_INFO("fpicker.aqua","userPane frame: {" << userPaneRect.origin.x << ", " << userPaneRect.origin.y << ", " << userPaneRect.size.width << ", " << userPaneRect.size.height << "}");
811     int nUsableWidth = userPaneRect.size.width;
813     //NOTE: NSView's coordinate system starts in the lower left hand corner but we start adding controls from the top,
814     // so we subtract from the vertical position as we make our way down the pane.
815     int currenttop = userPaneRect.size.height;
816     int nCheckboxMaxWidth = 0;
817     int nPopupMaxWidth = 0;
818     int nPopupLabelMaxWidth = 0;
820     //first loop to determine max sizes
821     for (::std::list<NSControl*>::iterator child = m_aActiveControls.begin(); child != m_aActiveControls.end(); child++) {
822         NSControl* pControl = *child;
824         NSRect controlRect = [pControl frame];
825         int nControlWidth = controlRect.size.width;
827         Class aSubType = [pControl class];
828         if (aSubType == [NSPopUpButton class]) {
829             if (nPopupMaxWidth < nControlWidth) {
830                 nPopupMaxWidth = nControlWidth;
831             }
832             NSTextField *label = m_aMapListLabelFields[(NSPopUpButton*)pControl];
833             NSRect labelFrame = [label frame];
834             int nLabelWidth = labelFrame.size.width;
835             if (nPopupLabelMaxWidth < nLabelWidth) {
836                 nPopupLabelMaxWidth = nLabelWidth;
837             }
838         } else {
839             if (nCheckboxMaxWidth < nControlWidth) {
840                 nCheckboxMaxWidth = nControlWidth;
841             }
842         }
843     }
845     int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
846     SAL_INFO("fpicker.aqua","longest popup width: " << nLongestPopupWidth);
848     NSControl* previousControl = nil;
850     int nDistBetweenControls = 0;
852     for (::std::list<NSControl*>::iterator child = m_aActiveControls.begin(); child != m_aActiveControls.end(); child++) {
853         NSControl* pControl = *child;
855         //get the control's bounds
856         NSRect controlRect = [pControl frame];
857         int nControlHeight = controlRect.size.height;
858         int nControlWidth = controlRect.size.width;
860         //subtract the height from the current vertical position, because the control's bounds origin rect will be its lower left hand corner
861         currenttop -= nControlHeight;
863         Class aSubType = [pControl class];
865         //add space between the previous control and this control according to Apple's HIG
866         nDistBetweenControls = getVerticalDistance(previousControl, pControl);
867         SAL_INFO("fpicker.aqua","vertical distance: " << nDistBetweenControls);
868         currenttop -= nDistBetweenControls;
870         previousControl = pControl;
872         if (aSubType == [NSPopUpButton class]) {
873             //move vertically up some pixels to space the controls between their real (visual) bounds
874             currenttop += kAquaSpacePopupMenuFrameBoundsDiffTop;//from top
876             //get the corresponding popup label
877             NSTextField *label = m_aMapListLabelFields[(NSPopUpButton*)pControl];
878             NSRect labelFrame = [label frame];
879             int totalWidth = nPopupMaxWidth + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
880             SAL_INFO("fpicker.aqua","totalWidth: " << totalWidth);
881             //let's center popups
882             int left = (nUsableWidth + nLongestPopupWidth) / 2 - totalWidth;
883             SAL_INFO("fpicker.aqua","left: " << left);
884             labelFrame.origin.x = left;
885             labelFrame.origin.y = currenttop + kAquaSpaceLabelPopupDiffV;
886             SAL_INFO("fpicker.aqua","setting label at: {" << labelFrame.origin.x << ", " << labelFrame.origin.y << ", " << labelFrame.size.width << ", " << labelFrame.size.height << "}");
887             [label setFrame:labelFrame];
889             controlRect.origin.x = left + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
890             controlRect.origin.y = currenttop;
891             controlRect.size.width = nPopupMaxWidth;
892             SAL_INFO("fpicker.aqua","setting popup at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}");
893             [pControl setFrame:controlRect];
895             //add some space to place the vertical position right below the popup's visual bounds
896             currenttop += kAquaSpacePopupMenuFrameBoundsDiffBottom;
897         } else {
898             currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;//from top
900             nControlWidth = nCheckboxMaxWidth;
901             int left = (nUsableWidth - nCheckboxMaxWidth) / 2;
902             controlRect.origin.x = left;
903             controlRect.origin.y = currenttop;
904             controlRect.size.width = nPopupMaxWidth;
905             [pControl setFrame:controlRect];
906             SAL_INFO("fpicker.aqua","setting checkbox at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}");
908             currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;
909         }
910     }
912     m_bIsUserPaneLaidOut = true;
914     DBG_PRINT_EXIT(CLASS_NAME, __func__);
917 void ControlHelper::createFilterControl() {
918     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
920     CResourceProvider aResProvider;
921     NSString* sLabel = aResProvider.getResString(CommonFilePickerElementIds::LISTBOX_FILTER_LABEL);
923     m_pFilterControl = [NSPopUpButton new];
925     [m_pFilterControl setAction:@selector(filterSelectedAtIndex:)];
926     [m_pFilterControl setTarget:m_pDelegate];
928     NSMenu *menu = [m_pFilterControl menu];
930     for (NSStringList::iterator iter = m_pFilterHelper->getFilterNames()->begin(); iter != m_pFilterHelper->getFilterNames()->end(); iter++) {
931         NSString *filterName = *iter;
932         SAL_INFO("fpicker.aqua","adding filter name: " << [filterName UTF8String]);
933         if ([filterName isEqualToString:@"-"]) {
934             [menu addItem:[NSMenuItem separatorItem]];
935         }
936         else {
937             [m_pFilterControl addItemWithTitle:filterName];
938         }
939     }
941     // always add the filter as first item
942     m_aActiveControls.push_front(m_pFilterControl);
943     m_aMapListLabels[m_pFilterControl] = [sLabel retain];
945     DBG_PRINT_EXIT(CLASS_NAME, __func__);
948 NSTextField* ControlHelper::createLabelWithString(NSString* labelString) {
949     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "label", labelString);
951     NSTextField *textField = [NSTextField new];
952     [textField setEditable:NO];
953     [textField setSelectable:NO];
954     [textField setDrawsBackground:NO];
955     [textField setBordered:NO];
956     [[textField cell] setTitle:labelString];
958     DBG_PRINT_EXIT(CLASS_NAME, __func__);
959     return textField;
962 int ControlHelper::getVerticalDistance(const NSControl* first, const NSControl* second)
964     if (first == nil) {
965         return kAquaSpaceBoxFrameViewDiffTop;
966     }
967     else if (second == nil) {
968         return kAquaSpaceBoxFrameViewDiffBottom;
969     }
970     else {
971         Class firstClass = [first class];
972         Class secondClass = [second class];
974         if (firstClass == [NSPopUpButton class]) {
975             if (secondClass == [NSPopUpButton class]) {
976                 return kAquaSpaceBetweenPopupMenus;
977             }
978             else {
979                 return kAquaSpaceAfterPopupButtonsV;
980             }
981         }
983         return kAquaSpaceBetweenControls;
984     }
987 void ControlHelper::updateFilterUI()
989     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
991     if (m_bIsFilterControlNeeded == false || m_pFilterHelper == NULL) {
992         SAL_INFO("fpicker.aqua","no filter control needed or no filter helper present");
993         DBG_PRINT_EXIT(CLASS_NAME, __func__);
994         return;
995     }
997     int index = m_pFilterHelper->getCurrentFilterIndex();
999     if (m_pFilterControl == nil) {
1000         createFilterControl();
1001     }
1003     [m_pFilterControl selectItemAtIndex:index];
1005     DBG_PRINT_EXIT(CLASS_NAME, __func__);
1008 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */