xref: /AOO41X/main/fpicker/source/aqua/ControlHelper.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
29 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
30 #include <com/sun/star/ui/dialogs/ControlActions.hpp>
31 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
32 #include <vos/mutex.hxx>
33 #include <vcl/svapp.hxx>
34 #include "CFStringUtilities.hxx"
35 #include "resourceprovider.hxx"
36 #include "NSString_OOoAdditions.hxx"
37 
38 #include "ControlHelper.hxx"
39 
40 #pragma mark DEFINES
41 #define CLASS_NAME "ControlHelper"
42 #define POPUP_WIDTH_MIN 200
43 #define POPUP_WIDTH_MAX 350
44 
45 using namespace ::com::sun::star::ui::dialogs;
46 using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
47 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
48 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
49 using namespace ::rtl;
50 
51 #pragma mark Constructor / Destructor
52 //------------------------------------------------------------------------------------
53 // Constructor / Destructor
54 //------------------------------------------------------------------------------------
55 ControlHelper::ControlHelper()
56 : m_pUserPane(NULL)
57 , m_pFilterControl(nil)
58 , m_bUserPaneNeeded( false )
59 , m_bIsUserPaneLaidOut(false)
60 , m_bIsFilterControlNeeded(false)
61 , m_pFilterHelper(NULL)
62 {
63     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
64 
65     int i;
66 
67     for( i = 0; i < TOGGLE_LAST; i++ ) {
68         m_bToggleVisibility[i] = false;
69     }
70 
71     for( i = 0; i < LIST_LAST; i++ ) {
72         m_bListVisibility[i] = false;
73     }
74 
75     DBG_PRINT_EXIT(CLASS_NAME, __func__);
76 }
77 
78 ControlHelper::~ControlHelper()
79 {
80     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
81 
82     NSAutoreleasePool *pool = [NSAutoreleasePool new];
83 
84     if (NULL != m_pUserPane) {
85         [m_pUserPane release];
86     }
87 
88     for(std::list<NSControl *>::iterator control = m_aActiveControls.begin(); control != m_aActiveControls.end(); control++) {
89         NSControl* pControl = (*control);
90         NSString* sLabelName = m_aMapListLabels[pControl];
91         if (sLabelName != nil) {
92             [sLabelName release];
93         }
94         if ([pControl class] == [NSPopUpButton class]) {
95             NSTextField* pField = m_aMapListLabelFields[(NSPopUpButton*)pControl];
96             if (pField != nil) {
97                 [pField release];
98             }
99         }
100         [pControl release];
101     }
102 
103     if (m_pFilterControl != NULL) {
104         [m_pFilterControl setTarget:nil];
105     }
106 
107     [pool release];
108 
109     DBG_PRINT_EXIT(CLASS_NAME, __func__);
110 }
111 
112 #pragma mark XInitialization delegate
113 //------------------------------------------------
114 // XInitialization delegate
115 //------------------------------------------------
116 void ControlHelper::initialize( sal_Int16 nTemplateId )
117 {
118     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "templateId", nTemplateId);
119 
120     switch( nTemplateId )
121     {
122         case FILESAVE_AUTOEXTENSION_PASSWORD:
123             m_bToggleVisibility[AUTOEXTENSION] = true;
124             m_bToggleVisibility[PASSWORD] = true;
125             break;
126         case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
127             m_bToggleVisibility[AUTOEXTENSION] = true;
128             m_bToggleVisibility[PASSWORD] = true;
129             m_bToggleVisibility[FILTEROPTIONS] = true;
130             break;
131         case FILESAVE_AUTOEXTENSION_SELECTION:
132             m_bToggleVisibility[AUTOEXTENSION] = true;
133             m_bToggleVisibility[SELECTION] = true;
134             break;
135         case FILESAVE_AUTOEXTENSION_TEMPLATE:
136             m_bToggleVisibility[AUTOEXTENSION] = true;
137             m_bListVisibility[TEMPLATE] = true;
138             break;
139         case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
140             m_bToggleVisibility[LINK] = true;
141             m_bToggleVisibility[PREVIEW] = true;
142             m_bListVisibility[IMAGE_TEMPLATE] = true;
143             break;
144         case FILEOPEN_READONLY_VERSION:
145             m_bToggleVisibility[READONLY] = true;
146             m_bListVisibility[VERSION] = true;
147             break;
148         case FILEOPEN_LINK_PREVIEW:
149             m_bToggleVisibility[LINK] = true;
150             m_bToggleVisibility[PREVIEW] = true;
151             break;
152         case FILESAVE_AUTOEXTENSION:
153             m_bToggleVisibility[AUTOEXTENSION] = true;
154             break;
155     }
156 
157     createControls();
158 
159     DBG_PRINT_EXIT(CLASS_NAME, __func__);
160 }
161 
162 #pragma mark XFilePickerControlAccess delegates
163 //------------------------------------------------------------------------------------
164 // XFilePickerControlAccess functions
165 //------------------------------------------------------------------------------------
166 
167 void ControlHelper::enableControl( const sal_Int16 nControlId, const sal_Bool bEnable ) const
168 {
169     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "enable", bEnable);
170 
171     ::vos::OGuard aGuard( Application::GetSolarMutex() );
172 
173     if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
174         OSL_TRACE(" preview checkbox cannot be changed");
175         DBG_PRINT_EXIT(CLASS_NAME, __func__);
176         return;
177     }
178 
179     NSControl* pControl = getControl(nControlId);
180 
181     if( pControl != nil ) {
182         if( bEnable ) {
183             OSL_TRACE( "enable" );
184         } else {
185             OSL_TRACE( "disable" );
186         }
187         [pControl setEnabled:bEnable];
188     } else {
189         OSL_TRACE("enable unknown control %d", nControlId );
190     }
191 
192     DBG_PRINT_EXIT(CLASS_NAME, __func__);
193 }
194 
195 OUString ControlHelper::getLabel( sal_Int16 nControlId )
196 {
197     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId);
198 
199     ::vos::OGuard aGuard( Application::GetSolarMutex() );
200 
201     NSControl* pControl = getControl( nControlId );
202 
203     if( pControl == nil ) {
204         OSL_TRACE("Get label for unknown control %d", nControlId);
205         return OUString();
206     }
207 
208     rtl::OUString retVal;
209     if ([pControl class] == [NSPopUpButton class]) {
210         NSString *temp = m_aMapListLabels[pControl];
211         if (temp != nil)
212             retVal = [temp OUString];
213     }
214     else {
215         NSString* sLabel = [[pControl cell] title];
216         retVal = [sLabel OUString];
217     }
218 
219     DBG_PRINT_EXIT(CLASS_NAME, __func__, retVal);
220 
221     return retVal;
222 }
223 
224 void ControlHelper::setLabel( sal_Int16 nControlId, const NSString* aLabel )
225 {
226     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "label", aLabel);
227 
228     ::vos::OGuard aGuard( Application::GetSolarMutex() );
229 
230     NSAutoreleasePool *pool = [NSAutoreleasePool new];
231 
232     NSControl* pControl = getControl(nControlId);
233 
234     if (nil != pControl) {
235         if ([pControl class] == [NSPopUpButton class]) {
236             NSString *sOldName = m_aMapListLabels[pControl];
237             if (sOldName != NULL && sOldName != aLabel) {
238                 [sOldName release];
239             }
240 
241             m_aMapListLabels[pControl] = [aLabel retain];
242         } else if ([pControl class] == [NSButton class]) {
243             [[pControl cell] setTitle:aLabel];
244         }
245     } else {
246         OSL_TRACE("Control not found to set label for");
247     }
248 
249     layoutControls();
250 
251     [pool release];
252 
253     DBG_PRINT_EXIT(CLASS_NAME, __func__);
254 }
255 
256 void ControlHelper::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
257 {
258     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "controlAction", nControlAction);
259 
260     ::vos::OGuard aGuard( Application::GetSolarMutex() );
261 
262     if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
263         OSL_TRACE(" value for preview is unchangeable");
264     }
265     else {
266         NSControl* pControl = getControl( nControlId );
267 
268         if( pControl == nil ) {
269             OSL_TRACE("enable unknown control %d", nControlId);
270         } else {
271             if( [pControl class] == [NSPopUpButton class] ) {
272                 HandleSetListValue(pControl, nControlAction, rValue);
273             } else if( [pControl class] == [NSButton class] ) {
274                 sal_Bool bChecked = false;
275                 rValue >>= bChecked;
276                 OSL_TRACE(" value is a bool: %d", bChecked);
277                 [(NSButton*)pControl setState:(bChecked ? NSOnState : NSOffState)];
278             } else
279             {
280                 OSL_TRACE("Can't set value on button / list %d %d",
281                           nControlId, nControlAction);
282             }
283         }
284     }
285 
286     DBG_PRINT_EXIT(CLASS_NAME, __func__);
287 }
288 
289 uno::Any ControlHelper::getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const
290 {
291     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "controlAction", nControlAction);
292 
293     ::vos::OGuard aGuard( Application::GetSolarMutex() );
294     uno::Any aRetval;
295 
296     NSControl* pControl = getControl( nControlId );
297 
298     if( pControl == nil ) {
299         OSL_TRACE("get value for unknown control %d", nControlId);
300         aRetval <<= sal_True;
301     } else {
302         if( [pControl class] == [NSPopUpButton class] ) {
303             aRetval = HandleGetListValue(pControl, nControlAction);
304         } else if( [pControl class] == [NSButton class] ) {
305             //NSLog(@"control: %@", [[pControl cell] title]);
306             sal_Bool bValue = [(NSButton*)pControl state] == NSOnState ? sal_True : sal_False;
307             aRetval <<= bValue;
308             OSL_TRACE("value is a bool (checkbox): %d", bValue);
309         }
310     }
311 
312     DBG_PRINT_EXIT(CLASS_NAME, __func__);
313 
314     return aRetval;
315 }
316 
317 void ControlHelper::createUserPane()
318 {
319     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
320 
321     if (m_bUserPaneNeeded == false) {
322         OSL_TRACE("no user pane needed");
323         DBG_PRINT_EXIT(CLASS_NAME, __func__);
324         return;
325     }
326 
327     if (nil != m_pUserPane) {
328         OSL_TRACE("user pane already exists");
329         DBG_PRINT_EXIT(CLASS_NAME, __func__);
330         return;
331     }
332 
333     if (m_bIsFilterControlNeeded == true && m_pFilterControl == nil) {
334         createFilterControl();
335     }
336 
337     NSRect minRect = NSMakeRect(0,0,300,33);
338     m_pUserPane = [[NSView alloc] initWithFrame:minRect];
339 
340     int currentHeight = kAquaSpaceBoxFrameViewDiffTop + kAquaSpaceBoxFrameViewDiffBottom;
341     int currentWidth = 300;
342 
343     sal_Bool bPopupControlPresent = NO;
344     sal_Bool bButtonControlPresent = NO;
345 
346     int nCheckboxMaxWidth = 0;
347     int nPopupMaxWidth = 0;
348     int nPopupLabelMaxWidth = 0;
349 
350     for (::std::list<NSControl*>::iterator child = m_aActiveControls.begin(); child != m_aActiveControls.end(); child++) {
351         OSL_TRACE("currentHeight: %d", currentHeight);
352 
353         NSControl* pControl = *child;
354 
355         //let the control calculate its size
356         [pControl sizeToFit];
357 
358         NSRect frame = [pControl frame];
359         OSL_TRACE("frame for control %s is {%f, %f, %f, %f}", [[pControl description] UTF8String], frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
360 
361         int nControlHeight = frame.size.height;
362         int nControlWidth = frame.size.width;
363 
364         // Note: controls are grouped by kind, first all popup menus, then checkboxes
365         if ([pControl class] == [NSPopUpButton class]) {
366             if (bPopupControlPresent == YES) {
367                 //this is not the first popup
368                 currentHeight += kAquaSpaceBetweenPopupMenus;
369             }
370             else if (child != m_aActiveControls.begin()){
371                 currentHeight += kAquaSpaceBetweenControls;
372             }
373 
374             bPopupControlPresent = YES;
375 
376             // we have to add the label text width
377             NSString *label = m_aMapListLabels[pControl];
378 
379             NSTextField *textField = createLabelWithString(label);
380             [textField sizeToFit];
381             m_aMapListLabelFields[(NSPopUpButton*)pControl] = textField;
382             [m_pUserPane addSubview:textField];
383 
384             NSRect tfRect = [textField frame];
385             OSL_TRACE("frame for textfield %s is {%f, %f, %f, %f}", [[textField description] UTF8String], tfRect.origin.x, tfRect.origin.y, tfRect.size.width, tfRect.size.height);
386 
387             int tfWidth = tfRect.size.width;
388 
389             if (nPopupLabelMaxWidth < tfWidth) {
390                 nPopupLabelMaxWidth = tfWidth;
391             }
392 
393             frame.origin.x += (kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft) + tfWidth;
394 
395             if (nControlWidth < POPUP_WIDTH_MIN) {
396                 nControlWidth = POPUP_WIDTH_MIN;
397                 frame.size.width = nControlWidth;
398                 [pControl setFrame:frame];
399             }
400 
401             if (nControlWidth > POPUP_WIDTH_MAX) {
402                 nControlWidth = POPUP_WIDTH_MAX;
403                 frame.size.width = nControlWidth;
404                 [pControl setFrame:frame];
405             }
406 
407             //set the max size
408             if (nPopupMaxWidth < nControlWidth) {
409                 nPopupMaxWidth = nControlWidth;
410             }
411 
412             nControlWidth += tfWidth + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
413             if (nControlHeight < kAquaPopupButtonDefaultHeight) {
414                 //maybe the popup has no menu item yet, so set a default height
415                 nControlHeight = kAquaPopupButtonDefaultHeight;
416             }
417 
418             nControlHeight -= kAquaSpacePopupMenuFrameBoundsDiffV;
419         }
420         else if ([pControl class] == [NSButton class]) {
421             if (child != m_aActiveControls.begin()){
422                 currentHeight += kAquaSpaceBetweenControls;
423             }
424 
425             if (nCheckboxMaxWidth < nControlWidth) {
426                 nCheckboxMaxWidth = nControlWidth;
427             }
428 
429             bButtonControlPresent = YES;
430             nControlWidth -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
431             nControlHeight -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
432         }
433 
434         // if ((nControlWidth + 2 * kAquaSpaceInsideGroupH) > currentWidth) {
435         //     currentWidth = nControlWidth + 2 * kAquaSpaceInsideGroupH;
436         // }
437 
438         currentHeight += nControlHeight;
439 
440         [m_pUserPane addSubview:pControl];
441     }
442 
443     OSL_TRACE("height after adding all controls: %d", currentHeight);
444 
445     if (bPopupControlPresent && bButtonControlPresent)
446     {
447         //after a popup button (array) and before a different kind of control we need some extra space instead of the standard
448         currentHeight -= kAquaSpaceBetweenControls;
449         currentHeight += kAquaSpaceAfterPopupButtonsV;
450         OSL_TRACE("popup extra space added, currentHeight: %d", currentHeight);
451     }
452 
453     int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
454 
455     currentWidth = nLongestPopupWidth > nCheckboxMaxWidth ? nLongestPopupWidth : nCheckboxMaxWidth;
456     OSL_TRACE("longest control width: %d", currentWidth);
457 
458     currentWidth += 2* kAquaSpaceInsideGroupH;
459 
460     if (currentWidth < minRect.size.width)
461         currentWidth = minRect.size.width;
462 
463     if (currentHeight < minRect.size.height)
464         currentHeight = minRect.size.height;
465 
466     NSRect upRect = NSMakeRect(0, 0, currentWidth, currentHeight );
467     OSL_TRACE("setting user pane rect to {%f, %f, %f, %f}",upRect.origin.x, upRect.origin.y, upRect.size.width, upRect.size.height);
468 
469     [m_pUserPane setFrame:upRect];
470 
471     layoutControls();
472 
473     DBG_PRINT_EXIT(CLASS_NAME, __func__);
474 }
475 
476 #pragma mark Private / Misc
477 //------------------------------------------------------------------------------------
478 // Private / Misc
479 //------------------------------------------------------------------------------------
480 void ControlHelper::createControls()
481 {
482     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
483 
484     CResourceProvider aResProvider;
485     for (int i = 0; i < LIST_LAST; i++) {
486         if (true == m_bListVisibility[i]) {
487             m_bUserPaneNeeded = true;
488 
489             int elementName = getControlElementName([NSPopUpButton class], i);
490             NSString* sLabel = aResProvider.getResString(elementName);
491 
492             m_pListControls[i] = [NSPopUpButton new];
493 
494 #define MAP_LIST_( elem ) \
495  case elem: \
496      setLabel(ExtendedFilePickerElementIds::LISTBOX_##elem, sLabel); \
497      break
498 
499             switch(i) {
500                 MAP_LIST_(VERSION);
501                 MAP_LIST_(TEMPLATE);
502                 MAP_LIST_(IMAGE_TEMPLATE);
503             }
504 
505             m_aActiveControls.push_back(m_pListControls[i]);
506         } else {
507             m_pListControls[i] = nil;
508         }
509     }
510 
511     for (int i = 0/*#i102102*/; i < TOGGLE_LAST; i++) {
512         if (true == m_bToggleVisibility[i]) {
513             m_bUserPaneNeeded = true;
514 
515             int elementName = getControlElementName([NSButton class], i);
516             NSString* sLabel = aResProvider.getResString(elementName);
517 
518             NSButton *button = [NSButton new];
519             [button setTitle:sLabel];
520 
521             [button setButtonType:NSSwitchButton];
522 
523             [button setState:NSOffState];
524 
525             if (i == AUTOEXTENSION) {
526                 [button setTarget:m_pDelegate];
527                 [button setAction:@selector(autoextensionChanged:)];
528             }
529 
530             m_pToggles[i] = button;
531 
532             m_aActiveControls.push_back(m_pToggles[i]);
533         } else {
534             m_pToggles[i] = nil;
535         }
536     }
537 
538     //preview is always on with Mac OS X
539     NSControl *pPreviewBox = m_pToggles[PREVIEW];
540     if (pPreviewBox != nil) {
541         [pPreviewBox setEnabled:NO];
542         [(NSButton*)pPreviewBox setState:NSOnState];
543     }
544 
545     DBG_PRINT_EXIT(CLASS_NAME, __func__);
546 }
547 
548 #define TOGGLE_ELEMENT( elem ) \
549 case elem: \
550     nReturn = CHECKBOX_##elem; \
551     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn); \
552     return nReturn
553 #define LIST_ELEMENT( elem ) \
554 case elem: \
555     nReturn = LISTBOX_##elem##_LABEL; \
556     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn); \
557     return nReturn
558 
559 int ControlHelper::getControlElementName(const Class aClazz, const int nControlId) const
560 {
561     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aClazz", [[aClazz description] UTF8String], "controlId", nControlId);
562 
563     int nReturn = -1;
564     if (aClazz == [NSButton class])
565     {
566         switch (nControlId) {
567             TOGGLE_ELEMENT( AUTOEXTENSION );
568             TOGGLE_ELEMENT( PASSWORD );
569             TOGGLE_ELEMENT( FILTEROPTIONS );
570             TOGGLE_ELEMENT( READONLY );
571             TOGGLE_ELEMENT( LINK );
572             TOGGLE_ELEMENT( PREVIEW );
573             TOGGLE_ELEMENT( SELECTION );
574         }
575     }
576     else if (aClazz == [NSPopUpButton class])
577     {
578         switch (nControlId) {
579             LIST_ELEMENT( VERSION );
580             LIST_ELEMENT( TEMPLATE );
581             LIST_ELEMENT( IMAGE_TEMPLATE );
582         }
583     }
584 
585     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn);
586 
587     return nReturn;
588 }
589 
590 void ControlHelper::HandleSetListValue(const NSControl* pControl, const sal_Int16 nControlAction, const uno::Any& rValue)
591 {
592     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlAction", nControlAction);
593 
594     if ([pControl class] != [NSPopUpButton class]) {
595         OSL_TRACE("not a popup menu");
596         DBG_PRINT_EXIT(CLASS_NAME, __func__);
597         return;
598     }
599 
600     NSPopUpButton *pButton = (NSPopUpButton*)pControl;
601     NSMenu *rMenu = [pButton menu];
602     if (nil == rMenu) {
603         OSL_TRACE("button has no menu");
604         DBG_PRINT_EXIT(CLASS_NAME, __func__);
605         return;
606     }
607 
608     switch (nControlAction)
609     {
610         case ControlActions::ADD_ITEM:
611         {
612             OSL_TRACE("ADD_ITEMS");
613             OUString sItem;
614             rValue >>= sItem;
615 
616             NSString* sCFItem = [NSString stringWithOUString:sItem];
617             OSL_TRACE("Adding menu item: %s", OUStringToOString(sItem, RTL_TEXTENCODING_UTF8).getStr());
618             [pButton addItemWithTitle:sCFItem];
619         }
620             break;
621         case ControlActions::ADD_ITEMS:
622         {
623             OSL_TRACE("ADD_ITEMS");
624             uno::Sequence< OUString > aStringList;
625             rValue >>= aStringList;
626             sal_Int32 nItemCount = aStringList.getLength();
627             for (sal_Int32 i = 0; i < nItemCount; ++i)
628             {
629                 NSString* sCFItem = [NSString stringWithOUString:aStringList[i]];
630                 OSL_TRACE("Adding menu item: %s", OUStringToOString(aStringList[i], RTL_TEXTENCODING_UTF8).getStr());
631                 [pButton addItemWithTitle:sCFItem];
632             }
633         }
634             break;
635         case ControlActions::DELETE_ITEM:
636         {
637             OSL_TRACE("DELETE_ITEM");
638             sal_Int32 nPos = -1;
639             rValue >>= nPos;
640             OSL_TRACE("Deleting item at position %d", (nPos));
641             [rMenu removeItemAtIndex:nPos];
642         }
643             break;
644         case ControlActions::DELETE_ITEMS:
645         {
646             OSL_TRACE("DELETE_ITEMS");
647             int nItems = [rMenu numberOfItems];
648             if (nItems == 0) {
649                 OSL_TRACE("no menu items to delete");
650                 DBG_PRINT_EXIT(CLASS_NAME, __func__);
651                 return;
652             }
653             for(sal_Int32 i = 0; i < nItems; i++) {
654                 [rMenu removeItemAtIndex:i];
655             }
656         }
657             break;
658         case ControlActions::SET_SELECT_ITEM:
659         {
660             sal_Int32 nPos = -1;
661             rValue >>= nPos;
662             OSL_TRACE("Selecting item at position %d", nPos);
663             [pButton selectItemAtIndex:nPos];
664         }
665             break;
666         default:
667             OSL_TRACE("undocumented/unimplemented ControlAction for a list");
668             break;
669     }
670 
671     layoutControls();
672 
673     DBG_PRINT_EXIT(CLASS_NAME, __func__);
674 }
675 
676 
677 uno::Any ControlHelper::HandleGetListValue(const NSControl* pControl, const sal_Int16 nControlAction) const
678 {
679     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlAction", nControlAction);
680 
681     uno::Any aAny;
682 
683     if ([pControl class] != [NSPopUpButton class]) {
684         OSL_TRACE("not a popup button");
685         DBG_PRINT_EXIT(CLASS_NAME, __func__);
686         return aAny;
687     }
688 
689     NSPopUpButton *pButton = (NSPopUpButton*)pControl;
690     NSMenu *rMenu = [pButton menu];
691     if (nil == rMenu) {
692         OSL_TRACE("button has no menu");
693         DBG_PRINT_EXIT(CLASS_NAME, __func__);
694         return aAny;
695     }
696 
697     switch (nControlAction)
698     {
699         case ControlActions::GET_ITEMS:
700         {
701             OSL_TRACE("GET_ITEMS");
702             uno::Sequence< OUString > aItemList;
703 
704             int nItems = [rMenu numberOfItems];
705             if (nItems > 0) {
706                 aItemList.realloc(nItems);
707             }
708             for (int i = 0; i < nItems; i++) {
709                 NSString* sCFItem = [pButton itemTitleAtIndex:i];
710                 if (nil != sCFItem) {
711                     aItemList[i] = [sCFItem OUString];
712                     OSL_TRACE("Return value[%d]: %s", (i - 1), OUStringToOString(aItemList[i - 1], RTL_TEXTENCODING_UTF8).getStr());
713                 }
714             }
715 
716             aAny <<= aItemList;
717         }
718             break;
719         case ControlActions::GET_SELECTED_ITEM:
720         {
721             OSL_TRACE("GET_SELECTED_ITEM");
722             NSString* sCFItem = [pButton titleOfSelectedItem];
723             if (nil != sCFItem) {
724                 OUString sString = [sCFItem OUString];
725                 OSL_TRACE("Return value: %s", OUStringToOString(sString, RTL_TEXTENCODING_UTF8).getStr());
726                 aAny <<= sString;
727             }
728         }
729             break;
730         case ControlActions::GET_SELECTED_ITEM_INDEX:
731         {
732             OSL_TRACE("GET_SELECTED_ITEM_INDEX");
733             sal_Int32 nActive = [pButton indexOfSelectedItem];
734             OSL_TRACE("Return value: %d", nActive);
735             aAny <<= nActive;
736         }
737             break;
738         default:
739             OSL_TRACE("undocumented/unimplemented ControlAction for a list");
740             break;
741     }
742 
743     DBG_PRINT_EXIT(CLASS_NAME, __func__);
744 
745     return aAny;
746 }
747 
748 
749 // cf. offapi/com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.idl
750 NSControl* ControlHelper::getControl( const sal_Int16 nControlId ) const
751 {
752     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId);
753 
754     NSControl* pWidget = nil;
755 
756 #define MAP_TOGGLE( elem ) \
757 case ExtendedFilePickerElementIds::CHECKBOX_##elem: \
758     pWidget = m_pToggles[elem]; \
759     break
760 
761 #define MAP_BUTTON( elem ) \
762 case ExtendedFilePickerElementIds::PUSHBUTTON_##elem: \
763     pWidget = m_pButtons[elem]; \
764     break
765 
766 #define MAP_LIST( elem ) \
767 case ExtendedFilePickerElementIds::LISTBOX_##elem: \
768     pWidget = m_pListControls[elem]; \
769     break
770 
771 #define MAP_LIST_LABEL( elem ) \
772 case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \
773     pWidget = m_pListControls[elem]; \
774     break
775 
776     switch( nControlId )
777     {
778             MAP_TOGGLE( AUTOEXTENSION );
779             MAP_TOGGLE( PASSWORD );
780             MAP_TOGGLE( FILTEROPTIONS );
781             MAP_TOGGLE( READONLY );
782             MAP_TOGGLE( LINK );
783             MAP_TOGGLE( PREVIEW );
784             MAP_TOGGLE( SELECTION );
785             //MAP_BUTTON( PLAY );
786             MAP_LIST( VERSION );
787             MAP_LIST( TEMPLATE );
788             MAP_LIST( IMAGE_TEMPLATE );
789             MAP_LIST_LABEL( VERSION );
790             MAP_LIST_LABEL( TEMPLATE );
791             MAP_LIST_LABEL( IMAGE_TEMPLATE );
792         default:
793             OSL_TRACE("Handle unknown control %d", nControlId);
794             break;
795     }
796 #undef MAP
797 
798     DBG_PRINT_EXIT(CLASS_NAME, __func__);
799 
800     return pWidget;
801 }
802 
803 void ControlHelper::layoutControls()
804 {
805     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
806 
807     ::vos::OGuard aGuard( Application::GetSolarMutex() );
808 
809     if (nil == m_pUserPane) {
810         OSL_TRACE("no user pane to layout");
811         DBG_PRINT_EXIT(CLASS_NAME, __func__);
812         return;
813     }
814 
815     if (m_bIsUserPaneLaidOut == true) {
816         OSL_TRACE("user pane already laid out");
817         DBG_PRINT_EXIT(CLASS_NAME, __func__);
818         return;
819     }
820 
821     NSRect userPaneRect = [m_pUserPane frame];
822     OSL_TRACE("userPane frame: {%f, %f, %f, %f}",userPaneRect.origin.x, userPaneRect.origin.y, userPaneRect.size.width, userPaneRect.size.height);
823 
824     int nUsableWidth = userPaneRect.size.width;
825 
826     //NOTE: NSView's coordinate system starts in the lower left hand corner but we start adding controls from the top,
827     // so we subtract from the vertical position as we make our way down the pane.
828     int currenttop = userPaneRect.size.height;
829     int nCheckboxMaxWidth = 0;
830     int nPopupMaxWidth = 0;
831     int nPopupLabelMaxWidth = 0;
832 
833     //first loop to determine max sizes
834     for (::std::list<NSControl*>::iterator child = m_aActiveControls.begin(); child != m_aActiveControls.end(); child++) {
835         NSControl* pControl = *child;
836 
837         NSRect controlRect = [pControl frame];
838         int nControlWidth = controlRect.size.width;
839 
840         Class aSubType = [pControl class];
841         if (aSubType == [NSPopUpButton class]) {
842             if (nPopupMaxWidth < nControlWidth) {
843                 nPopupMaxWidth = nControlWidth;
844             }
845             NSTextField *label = m_aMapListLabelFields[(NSPopUpButton*)pControl];
846             NSRect labelFrame = [label frame];
847             int nLabelWidth = labelFrame.size.width;
848             if (nPopupLabelMaxWidth < nLabelWidth) {
849                 nPopupLabelMaxWidth = nLabelWidth;
850             }
851         } else {
852             if (nCheckboxMaxWidth < nControlWidth) {
853                 nCheckboxMaxWidth = nControlWidth;
854             }
855         }
856     }
857 
858     int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
859     OSL_TRACE("longest popup width: %d", nLongestPopupWidth);
860 
861     NSControl* previousControl = nil;
862 
863     int nDistBetweenControls = 0;
864 
865     for (::std::list<NSControl*>::iterator child = m_aActiveControls.begin(); child != m_aActiveControls.end(); child++) {
866         NSControl* pControl = *child;
867 
868         //get the control's bounds
869         NSRect controlRect = [pControl frame];
870         int nControlHeight = controlRect.size.height;
871         int nControlWidth = controlRect.size.width;
872 
873         //subtract the height from the current vertical position, because the control's bounds origin rect will be its lower left hand corner
874         currenttop -= nControlHeight;
875 
876         Class aSubType = [pControl class];
877 
878         //add space between the previous control and this control according to Apple's HIG
879         nDistBetweenControls = getVerticalDistance(previousControl, pControl);
880         OSL_TRACE("vertical distance: %d", nDistBetweenControls);
881         currenttop -= nDistBetweenControls;
882 
883         previousControl = pControl;
884 
885         if (aSubType == [NSPopUpButton class]) {
886             //move vertically up some pixels to space the controls between their real (visual) bounds
887             currenttop += kAquaSpacePopupMenuFrameBoundsDiffTop;//from top
888 
889             //get the corresponding popup label
890             NSTextField *label = m_aMapListLabelFields[(NSPopUpButton*)pControl];
891             NSRect labelFrame = [label frame];
892             int totalWidth = nPopupMaxWidth + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
893             OSL_TRACE("totalWidth: %d", totalWidth);
894             //let's center popups
895             int left = (nUsableWidth + nLongestPopupWidth) / 2 - totalWidth;
896             OSL_TRACE("left: %d", left);
897             labelFrame.origin.x = left;
898             labelFrame.origin.y = currenttop + kAquaSpaceLabelPopupDiffV;
899             OSL_TRACE("setting label at: {%f, %f, %f, %f}",labelFrame.origin.x, labelFrame.origin.y, labelFrame.size.width, labelFrame.size.height);
900             [label setFrame:labelFrame];
901 
902             controlRect.origin.x = left + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
903             controlRect.origin.y = currenttop;
904             controlRect.size.width = nPopupMaxWidth;
905             OSL_TRACE("setting popup at: {%f, %f, %f, %f}",controlRect.origin.x, controlRect.origin.y, controlRect.size.width, controlRect.size.height);
906             [pControl setFrame:controlRect];
907 
908             //add some space to place the vertical position right below the popup's visual bounds
909             currenttop += kAquaSpacePopupMenuFrameBoundsDiffBottom;
910         } else {
911             currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;//from top
912 
913             nControlWidth = nCheckboxMaxWidth;
914             int left = (nUsableWidth - nCheckboxMaxWidth) / 2;
915             controlRect.origin.x = left;
916             controlRect.origin.y = currenttop;
917             controlRect.size.width = nPopupMaxWidth;
918             [pControl setFrame:controlRect];
919             OSL_TRACE("setting checkbox at: {%f, %f, %f, %f}",controlRect.origin.x, controlRect.origin.y, controlRect.size.width, controlRect.size.height);
920 
921             currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;
922         }
923     }
924 
925     m_bIsUserPaneLaidOut = true;
926 
927     DBG_PRINT_EXIT(CLASS_NAME, __func__);
928 }
929 
930 void ControlHelper::createFilterControl() {
931     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
932 
933     CResourceProvider aResProvider;
934     NSString* sLabel = aResProvider.getResString(CommonFilePickerElementIds::LISTBOX_FILTER_LABEL);
935 
936     m_pFilterControl = [NSPopUpButton new];
937 
938     [m_pFilterControl setAction:@selector(filterSelectedAtIndex:)];
939     [m_pFilterControl setTarget:m_pDelegate];
940 
941     NSMenu *menu = [m_pFilterControl menu];
942 
943     for (NSStringList::iterator iter = m_pFilterHelper->getFilterNames()->begin(); iter != m_pFilterHelper->getFilterNames()->end(); iter++) {
944         NSString *filterName = *iter;
945         OSL_TRACE("adding filter name: %s", [filterName UTF8String]);
946         if ([filterName isEqualToString:@"-"]) {
947             [menu addItem:[NSMenuItem separatorItem]];
948         }
949         else {
950             [m_pFilterControl addItemWithTitle:filterName];
951         }
952     }
953 
954     // always add the filter as first item
955     m_aActiveControls.push_front(m_pFilterControl);
956     m_aMapListLabels[m_pFilterControl] = [sLabel retain];
957 
958     DBG_PRINT_EXIT(CLASS_NAME, __func__);
959 }
960 
961 NSTextField* ControlHelper::createLabelWithString(const NSString* labelString) {
962     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "label", labelString);
963 
964     NSTextField *textField = [NSTextField new];
965     [textField setEditable:NO];
966     [textField setSelectable:NO];
967     [textField setDrawsBackground:NO];
968     [textField setBordered:NO];
969     [[textField cell] setTitle:labelString];
970 
971     DBG_PRINT_EXIT(CLASS_NAME, __func__);
972     return textField;
973 }
974 
975 int ControlHelper::getVerticalDistance(const NSControl* first, const NSControl* second)
976 {
977     if (first == nil) {
978         return kAquaSpaceBoxFrameViewDiffTop;
979     }
980     else if (second == nil) {
981         return kAquaSpaceBoxFrameViewDiffBottom;
982     }
983     else {
984         Class firstClass = [first class];
985         Class secondClass = [second class];
986 
987         if (firstClass == [NSPopUpButton class]) {
988             if (secondClass == [NSPopUpButton class]) {
989                 return kAquaSpaceBetweenPopupMenus;
990             }
991             else {
992                 return kAquaSpaceAfterPopupButtonsV;
993             }
994         }
995 
996         return kAquaSpaceBetweenControls;
997     }
998 }
999 
1000 void ControlHelper::updateFilterUI()
1001 {
1002     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
1003 
1004     if (m_bIsFilterControlNeeded == false || m_pFilterHelper == NULL) {
1005         OSL_TRACE("no filter control needed or no filter helper present");
1006         DBG_PRINT_EXIT(CLASS_NAME, __func__);
1007         return;
1008     }
1009 
1010     int index = m_pFilterHelper->getCurrentFilterIndex();
1011 
1012     if (m_pFilterControl == nil) {
1013         createFilterControl();
1014     }
1015 
1016     [m_pFilterControl selectItemAtIndex:index];
1017 
1018     DBG_PRINT_EXIT(CLASS_NAME, __func__);
1019 }
1020