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