update emoji autocorrect entries from po-files
[LibreOffice.git] / fpicker / source / aqua / ControlHelper.mm
blob8e0c294c46d5bb6a789989901a98b888991b4ef0
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;
43 namespace {
45 uno::Any HandleGetListValue(const NSControl* pControl, const sal_Int16 nControlAction)
47     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlAction", nControlAction);
49     uno::Any aAny;
51     if ([pControl class] != [NSPopUpButton class]) {
52         SAL_INFO("fpicker.aqua","not a popup button");
53         DBG_PRINT_EXIT(CLASS_NAME, __func__);
54         return aAny;
55     }
57     NSPopUpButton *pButton = (NSPopUpButton*)pControl;
58     NSMenu *rMenu = [pButton menu];
59     if (nil == rMenu) {
60         SAL_INFO("fpicker.aqua","button has no menu");
61         DBG_PRINT_EXIT(CLASS_NAME, __func__);
62         return aAny;
63     }
65     switch (nControlAction)
66     {
67         case ControlActions::GET_ITEMS:
68         {
69             SAL_INFO("fpicker.aqua","GET_ITEMS");
70             uno::Sequence< OUString > aItemList;
72             int nItems = [rMenu numberOfItems];
73             if (nItems > 0) {
74                 aItemList.realloc(nItems);
75             }
76             for (int i = 0; i < nItems; i++) {
77                 NSString* sCFItem = [pButton itemTitleAtIndex:i];
78                 if (nil != sCFItem) {
79                     aItemList[i] = [sCFItem OUString];
80                     SAL_INFO("fpicker.aqua","Return value[" << (i - 1) << "]: " << OUStringToOString(aItemList[i - 1], RTL_TEXTENCODING_UTF8).getStr());
81                 }
82             }
84             aAny <<= aItemList;
85         }
86             break;
87         case ControlActions::GET_SELECTED_ITEM:
88         {
89             SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM");
90             NSString* sCFItem = [pButton titleOfSelectedItem];
91             if (nil != sCFItem) {
92                 OUString sString = [sCFItem OUString];
93                 SAL_INFO("fpicker.aqua","Return value: " << OUStringToOString(sString, RTL_TEXTENCODING_UTF8).getStr());
94                 aAny <<= sString;
95             }
96         }
97             break;
98         case ControlActions::GET_SELECTED_ITEM_INDEX:
99         {
100             SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM_INDEX");
101             sal_Int32 nActive = [pButton indexOfSelectedItem];
102             SAL_INFO("fpicker.aqua","Return value: " << nActive);
103             aAny <<= nActive;
104         }
105             break;
106         default:
107             SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
108             break;
109     }
111     DBG_PRINT_EXIT(CLASS_NAME, __func__);
113     return aAny;
116 NSTextField* createLabelWithString(NSString* labelString) {
117     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "label", labelString);
119     NSTextField *textField = [NSTextField new];
120     [textField setEditable:NO];
121     [textField setSelectable:NO];
122     [textField setDrawsBackground:NO];
123     [textField setBordered:NO];
124     SAL_WNODEPRECATED_DECLARATIONS_PUSH //TODO: 10.9 setTitle
125     [[textField cell] setTitle:labelString];
126     SAL_WNODEPRECATED_DECLARATIONS_POP
128     DBG_PRINT_EXIT(CLASS_NAME, __func__);
129     return textField;
134 #pragma mark Constructor / Destructor
136 // Constructor / Destructor
138 ControlHelper::ControlHelper()
139 : m_pUserPane(NULL)
140 , m_pFilterControl(nil)
141 , m_bUserPaneNeeded( false )
142 , m_bIsUserPaneLaidOut(false)
143 , m_bIsFilterControlNeeded(false)
144 , m_pFilterHelper(NULL)
146     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
148     int i;
150     for( i = 0; i < TOGGLE_LAST; i++ ) {
151         m_bToggleVisibility[i] = false;
152     }
154     for( i = 0; i < LIST_LAST; i++ ) {
155         m_bListVisibility[i] = false;
156     }
158     DBG_PRINT_EXIT(CLASS_NAME, __func__);
161 ControlHelper::~ControlHelper()
163     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
165     NSAutoreleasePool *pool = [NSAutoreleasePool new];
167     if (NULL != m_pUserPane) {
168         [m_pUserPane release];
169     }
171     if (m_pFilterControl != NULL) {
172         [m_pFilterControl setTarget:nil];
173     }
175     for(std::list<NSControl *>::iterator control = m_aActiveControls.begin(); control != m_aActiveControls.end(); ++control) {
176         NSControl* pControl = (*control);
177         NSString* sLabelName = m_aMapListLabels[pControl];
178         if (sLabelName != nil) {
179             [sLabelName release];
180         }
181         if ([pControl class] == [NSPopUpButton class]) {
182             NSTextField* pField = m_aMapListLabelFields[(NSPopUpButton*)pControl];
183             if (pField != nil) {
184                 [pField release];
185             }
186         }
187         [pControl release];
188     }
190     [pool release];
192     DBG_PRINT_EXIT(CLASS_NAME, __func__);
195 #pragma mark XInitialization delegate
197 // XInitialization delegate
199 void ControlHelper::initialize( sal_Int16 nTemplateId )
201     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "templateId", nTemplateId);
203     switch( nTemplateId )
204     {
205         case FILESAVE_AUTOEXTENSION_PASSWORD:
206             m_bToggleVisibility[AUTOEXTENSION] = true;
207             m_bToggleVisibility[PASSWORD] = true;
208             break;
209         case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
210             m_bToggleVisibility[AUTOEXTENSION] = true;
211             m_bToggleVisibility[PASSWORD] = true;
212             m_bToggleVisibility[FILTEROPTIONS] = true;
213             break;
214         case FILESAVE_AUTOEXTENSION_SELECTION:
215             m_bToggleVisibility[AUTOEXTENSION] = true;
216             m_bToggleVisibility[SELECTION] = true;
217             break;
218         case FILESAVE_AUTOEXTENSION_TEMPLATE:
219             m_bToggleVisibility[AUTOEXTENSION] = true;
220             m_bListVisibility[TEMPLATE] = true;
221             break;
222         case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
223             m_bToggleVisibility[LINK] = true;
224             m_bToggleVisibility[PREVIEW] = true;
225             m_bListVisibility[IMAGE_TEMPLATE] = true;
226             break;
227         case FILEOPEN_READONLY_VERSION:
228             m_bToggleVisibility[READONLY] = true;
229             m_bListVisibility[VERSION] = true;
230             break;
231         case FILEOPEN_LINK_PREVIEW:
232             m_bToggleVisibility[LINK] = true;
233             m_bToggleVisibility[PREVIEW] = true;
234             break;
235         case FILESAVE_AUTOEXTENSION:
236             m_bToggleVisibility[AUTOEXTENSION] = true;
237             break;
238     }
240     createControls();
242     DBG_PRINT_EXIT(CLASS_NAME, __func__);
245 #pragma mark XFilePickerControlAccess delegates
247 // XFilePickerControlAccess functions
250 void ControlHelper::enableControl( const sal_Int16 nControlId, const bool bEnable ) const
252     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "enable", int(bEnable));
254     SolarMutexGuard aGuard;
256     if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
257         SAL_INFO("fpicker.aqua"," preview checkbox cannot be changed");
258         DBG_PRINT_EXIT(CLASS_NAME, __func__);
259         return;
260     }
262     NSControl* pControl = getControl(nControlId);
264     if( pControl != nil ) {
265         if( bEnable ) {
266             SAL_INFO("fpicker.aqua", "enable" );
267         } else {
268             SAL_INFO("fpicker.aqua", "disable" );
269         }
270         [pControl setEnabled:bEnable];
271     } else {
272         SAL_INFO("fpicker.aqua","enable unknown control " << nControlId );
273     }
275     DBG_PRINT_EXIT(CLASS_NAME, __func__);
278 OUString ControlHelper::getLabel( sal_Int16 nControlId )
280     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId);
282     SolarMutexGuard aGuard;
284     NSControl* pControl = getControl( nControlId );
286     if( pControl == nil ) {
287         SAL_INFO("fpicker.aqua","Get label for unknown control " << nControlId);
288         return OUString();
289     }
291     rtl::OUString retVal;
292     if ([pControl class] == [NSPopUpButton class]) {
293         NSString *temp = m_aMapListLabels[pControl];
294         if (temp != nil)
295             retVal = [temp OUString];
296     }
297     else {
298         SAL_WNODEPRECATED_DECLARATIONS_PUSH //TODO: 10.9 title
299         NSString* sLabel = [[pControl cell] title];
300         SAL_WNODEPRECATED_DECLARATIONS_POP
301         retVal = [sLabel OUString];
302     }
304     DBG_PRINT_EXIT(CLASS_NAME, __func__, retVal);
306     return retVal;
309 void ControlHelper::setLabel( sal_Int16 nControlId, NSString* aLabel )
311     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "label", aLabel);
313     SolarMutexGuard aGuard;
315     NSAutoreleasePool *pool = [NSAutoreleasePool new];
317     NSControl* pControl = getControl(nControlId);
319     if (nil != pControl) {
320         if ([pControl class] == [NSPopUpButton class]) {
321             NSString *sOldName = m_aMapListLabels[pControl];
322             if (sOldName != NULL && sOldName != aLabel) {
323                 [sOldName release];
324             }
326             m_aMapListLabels[pControl] = [aLabel retain];
327         } else if ([pControl class] == [NSButton class]) {
328             SAL_WNODEPRECATED_DECLARATIONS_PUSH //TODO: 10.9 setTitle
329             [[pControl cell] setTitle:aLabel];
330             SAL_WNODEPRECATED_DECLARATIONS_POP
331         }
332     } else {
333         SAL_INFO("fpicker.aqua","Control not found to set label for");
334     }
336     layoutControls();
338     [pool release];
340     DBG_PRINT_EXIT(CLASS_NAME, __func__);
343 void ControlHelper::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
345     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "controlAction", nControlAction);
347     SolarMutexGuard aGuard;
349     if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
350         SAL_INFO("fpicker.aqua"," value for preview is unchangeable");
351     }
352     else {
353         NSControl* pControl = getControl( nControlId );
355         if( pControl == nil ) {
356             SAL_INFO("fpicker.aqua","enable unknown control " << nControlId);
357         } else {
358             if( [pControl class] == [NSPopUpButton class] ) {
359                 HandleSetListValue(pControl, nControlAction, rValue);
360             } else if( [pControl class] == [NSButton class] ) {
361                 bool bChecked = false;
362                 rValue >>= bChecked;
363                 SAL_INFO("fpicker.aqua"," value is a bool: " << bChecked);
364                 [(NSButton*)pControl setState:(bChecked ? NSOnState : NSOffState)];
365             } else
366             {
367                 SAL_INFO("fpicker.aqua","Can't set value on button / list " << nControlId << " " << nControlAction);
368             }
369         }
370     }
372     DBG_PRINT_EXIT(CLASS_NAME, __func__);
375 uno::Any ControlHelper::getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const
377     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId, "controlAction", nControlAction);
379     SolarMutexGuard aGuard;
380     uno::Any aRetval;
382     NSControl* pControl = getControl( nControlId );
384     if( pControl == nil ) {
385         SAL_INFO("fpicker.aqua","get value for unknown control " << nControlId);
386         aRetval <<= true;
387     } else {
388         if( [pControl class] == [NSPopUpButton class] ) {
389             aRetval = HandleGetListValue(pControl, nControlAction);
390         } else if( [pControl class] == [NSButton class] ) {
391             //NSLog(@"control: %@", [[pControl cell] title]);
392             bool bValue = [(NSButton*)pControl state] == NSOnState;
393             aRetval <<= bValue;
394             SAL_INFO("fpicker.aqua","value is a bool (checkbox): " << bValue);
395         }
396     }
398     DBG_PRINT_EXIT(CLASS_NAME, __func__);
400     return aRetval;
403 void ControlHelper::createUserPane()
405     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
407     if (!m_bUserPaneNeeded) {
408         SAL_INFO("fpicker.aqua","no user pane needed");
409         DBG_PRINT_EXIT(CLASS_NAME, __func__);
410         return;
411     }
413     if (nil != m_pUserPane) {
414         SAL_INFO("fpicker.aqua","user pane already exists");
415         DBG_PRINT_EXIT(CLASS_NAME, __func__);
416         return;
417     }
419     if (m_bIsFilterControlNeeded && m_pFilterControl == nil) {
420         createFilterControl();
421     }
423     NSRect minRect = NSMakeRect(0,0,300,33);
424     m_pUserPane = [[NSView alloc] initWithFrame:minRect];
426     int currentHeight = kAquaSpaceBoxFrameViewDiffTop + kAquaSpaceBoxFrameViewDiffBottom;
427     int currentWidth = 300;
429     bool bPopupControlPresent = false;
430     bool bButtonControlPresent = false;
432     int nCheckboxMaxWidth = 0;
433     int nPopupMaxWidth = 0;
434     int nPopupLabelMaxWidth = 0;
436     for (::std::list<NSControl*>::iterator child = m_aActiveControls.begin(); child != m_aActiveControls.end(); child++) {
437         SAL_INFO("fpicker.aqua","currentHeight: " << currentHeight);
439         NSControl* pControl = *child;
441         //let the control calculate its size
442         [pControl sizeToFit];
444         NSRect frame = [pControl frame];
445         SAL_INFO("fpicker.aqua","frame for control " << [[pControl description] UTF8String] << " is {" << frame.origin.x << ", " << frame.origin.y << ", " << frame.size.width << ", " << frame.size.height << "}");
447         int nControlHeight = frame.size.height;
448         int nControlWidth = frame.size.width;
450         // Note: controls are grouped by kind, first all popup menus, then checkboxes
451         if ([pControl class] == [NSPopUpButton class]) {
452             if (bPopupControlPresent) {
453                 //this is not the first popup
454                 currentHeight += kAquaSpaceBetweenPopupMenus;
455             }
456             else if (child != m_aActiveControls.begin()){
457                 currentHeight += kAquaSpaceBetweenControls;
458             }
460             bPopupControlPresent = true;
462             // we have to add the label text width
463             NSString *label = m_aMapListLabels[pControl];
465             NSTextField *textField = createLabelWithString(label);
466             [textField sizeToFit];
467             m_aMapListLabelFields[(NSPopUpButton*)pControl] = textField;
468             [m_pUserPane addSubview:textField];
470             NSRect tfRect = [textField frame];
471             SAL_INFO("fpicker.aqua","frame for textfield " << [[textField description] UTF8String] << " is {" << tfRect.origin.x << ", " << tfRect.origin.y << ", " << tfRect.size.width << ", " << tfRect.size.height << "}");
473             int tfWidth = tfRect.size.width;
475             if (nPopupLabelMaxWidth < tfWidth) {
476                 nPopupLabelMaxWidth = tfWidth;
477             }
479             frame.origin.x += (kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft) + tfWidth;
481             if (nControlWidth < POPUP_WIDTH_MIN) {
482                 nControlWidth = POPUP_WIDTH_MIN;
483                 frame.size.width = nControlWidth;
484                 [pControl setFrame:frame];
485             }
487             if (nControlWidth > POPUP_WIDTH_MAX) {
488                 nControlWidth = POPUP_WIDTH_MAX;
489                 frame.size.width = nControlWidth;
490                 [pControl setFrame:frame];
491             }
493             //set the max size
494             if (nPopupMaxWidth < nControlWidth) {
495                 nPopupMaxWidth = nControlWidth;
496             }
498             nControlWidth += tfWidth + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
499             if (nControlHeight < kAquaPopupButtonDefaultHeight) {
500                 //maybe the popup has no menu item yet, so set a default height
501                 nControlHeight = kAquaPopupButtonDefaultHeight;
502             }
504             nControlHeight -= kAquaSpacePopupMenuFrameBoundsDiffV;
505         }
506         else if ([pControl class] == [NSButton class]) {
507             if (child != m_aActiveControls.begin()){
508                 currentHeight += kAquaSpaceBetweenControls;
509             }
511             if (nCheckboxMaxWidth < nControlWidth) {
512                 nCheckboxMaxWidth = nControlWidth;
513             }
515             bButtonControlPresent = YES;
516             nControlWidth -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
517             nControlHeight -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
518         }
520         // if ((nControlWidth + 2 * kAquaSpaceInsideGroupH) > currentWidth) {
521         //     currentWidth = nControlWidth + 2 * kAquaSpaceInsideGroupH;
522         // }
524         currentHeight += nControlHeight;
526         [m_pUserPane addSubview:pControl];
527     }
529     SAL_INFO("fpicker.aqua","height after adding all controls: " << currentHeight);
531     if (bPopupControlPresent && bButtonControlPresent)
532     {
533         //after a popup button (array) and before a different kind of control we need some extra space instead of the standard
534         currentHeight -= kAquaSpaceBetweenControls;
535         currentHeight += kAquaSpaceAfterPopupButtonsV;
536         SAL_INFO("fpicker.aqua","popup extra space added, currentHeight: " << currentHeight);
537     }
539     int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
541     currentWidth = nLongestPopupWidth > nCheckboxMaxWidth ? nLongestPopupWidth : nCheckboxMaxWidth;
542     SAL_INFO("fpicker.aqua","longest control width: " << currentWidth);
544     currentWidth += 2* kAquaSpaceInsideGroupH;
546     if (currentWidth < minRect.size.width)
547         currentWidth = minRect.size.width;
549     if (currentHeight < minRect.size.height)
550         currentHeight = minRect.size.height;
552     NSRect upRect = NSMakeRect(0, 0, currentWidth, currentHeight );
553     SAL_INFO("fpicker.aqua","setting user pane rect to {" << upRect.origin.x << ", " << upRect.origin.y << ", " << upRect.size.width << ", " << upRect.size.height << "}");
555     [m_pUserPane setFrame:upRect];
557     layoutControls();
559     DBG_PRINT_EXIT(CLASS_NAME, __func__);
562 #pragma mark Private / Misc
564 // Private / Misc
566 void ControlHelper::createControls()
568     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
570     CResourceProvider aResProvider;
571     for (int i = 0; i < LIST_LAST; i++) {
572         if (m_bListVisibility[i]) {
573             m_bUserPaneNeeded = true;
575             int elementName = getControlElementName([NSPopUpButton class], i);
576             NSString* sLabel = aResProvider.getResString(elementName);
578             m_pListControls[i] = [NSPopUpButton new];
580 #define MAP_LIST_( elem ) \
581  case elem: \
582      setLabel(ExtendedFilePickerElementIds::LISTBOX_##elem, sLabel); \
583      break
585             switch(i) {
586                 MAP_LIST_(VERSION);
587                 MAP_LIST_(TEMPLATE);
588                 MAP_LIST_(IMAGE_TEMPLATE);
589             }
591             m_aActiveControls.push_back(m_pListControls[i]);
592         } else {
593             m_pListControls[i] = nil;
594         }
595     }
597     for (int i = 0/*#i102102*/; i < TOGGLE_LAST; i++) {
598         if (m_bToggleVisibility[i]) {
599             m_bUserPaneNeeded = true;
601             int elementName = getControlElementName([NSButton class], i);
602             NSString* sLabel = aResProvider.getResString(elementName);
604             NSButton *button = [NSButton new];
605             [button setTitle:sLabel];
607             [button setButtonType:NSSwitchButton];
609             [button setState:NSOffState];
611             if (i == AUTOEXTENSION) {
612                 [button setTarget:m_pDelegate];
613                 [button setAction:@selector(autoextensionChanged:)];
614             }
616             m_pToggles[i] = button;
618             m_aActiveControls.push_back(m_pToggles[i]);
619         } else {
620             m_pToggles[i] = nil;
621         }
622     }
624     //preview is always on with Mac OS X
625     NSControl *pPreviewBox = m_pToggles[PREVIEW];
626     if (pPreviewBox != nil) {
627         [pPreviewBox setEnabled:NO];
628         [(NSButton*)pPreviewBox setState:NSOnState];
629     }
631     DBG_PRINT_EXIT(CLASS_NAME, __func__);
634 #define TOGGLE_ELEMENT( elem ) \
635 case elem: \
636     nReturn = CHECKBOX_##elem; \
637     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn); \
638     return nReturn
639 #define LIST_ELEMENT( elem ) \
640 case elem: \
641     nReturn = LISTBOX_##elem##_LABEL; \
642     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn); \
643     return nReturn
645 int ControlHelper::getControlElementName(const Class aClazz, const int nControlId)
647     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aClazz", [[aClazz description] UTF8String], "controlId", nControlId);
649     int nReturn = -1;
650     if (aClazz == [NSButton class])
651     {
652         switch (nControlId) {
653             TOGGLE_ELEMENT( AUTOEXTENSION );
654             TOGGLE_ELEMENT( PASSWORD );
655             TOGGLE_ELEMENT( FILTEROPTIONS );
656             TOGGLE_ELEMENT( READONLY );
657             TOGGLE_ELEMENT( LINK );
658             TOGGLE_ELEMENT( PREVIEW );
659             TOGGLE_ELEMENT( SELECTION );
660         }
661     }
662     else if (aClazz == [NSPopUpButton class])
663     {
664         switch (nControlId) {
665             LIST_ELEMENT( VERSION );
666             LIST_ELEMENT( TEMPLATE );
667             LIST_ELEMENT( IMAGE_TEMPLATE );
668         }
669     }
671     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn);
673     return nReturn;
676 void ControlHelper::HandleSetListValue(const NSControl* pControl, const sal_Int16 nControlAction, const uno::Any& rValue)
678     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlAction", nControlAction);
680     if ([pControl class] != [NSPopUpButton class]) {
681         SAL_INFO("fpicker.aqua","not a popup menu");
682         DBG_PRINT_EXIT(CLASS_NAME, __func__);
683         return;
684     }
686     NSPopUpButton *pButton = (NSPopUpButton*)pControl;
687     NSMenu *rMenu = [pButton menu];
688     if (nil == rMenu) {
689         SAL_INFO("fpicker.aqua","button has no menu");
690         DBG_PRINT_EXIT(CLASS_NAME, __func__);
691         return;
692     }
694     switch (nControlAction)
695     {
696         case ControlActions::ADD_ITEM:
697         {
698             SAL_INFO("fpicker.aqua","ADD_ITEMS");
699             OUString sItem;
700             rValue >>= sItem;
702             NSString* sCFItem = [NSString stringWithOUString:sItem];
703             SAL_INFO("fpicker.aqua","Adding menu item: " << OUStringToOString(sItem, RTL_TEXTENCODING_UTF8).getStr());
704             [pButton addItemWithTitle:sCFItem];
705         }
706             break;
707         case ControlActions::ADD_ITEMS:
708         {
709             SAL_INFO("fpicker.aqua","ADD_ITEMS");
710             uno::Sequence< OUString > aStringList;
711             rValue >>= aStringList;
712             sal_Int32 nItemCount = aStringList.getLength();
713             for (sal_Int32 i = 0; i < nItemCount; ++i)
714             {
715                 NSString* sCFItem = [NSString stringWithOUString:aStringList[i]];
716                 SAL_INFO("fpicker.aqua","Adding menu item: " << OUStringToOString(aStringList[i], RTL_TEXTENCODING_UTF8).getStr());
717                 [pButton addItemWithTitle:sCFItem];
718             }
719         }
720             break;
721         case ControlActions::DELETE_ITEM:
722         {
723             SAL_INFO("fpicker.aqua","DELETE_ITEM");
724             sal_Int32 nPos = -1;
725             rValue >>= nPos;
726             SAL_INFO("fpicker.aqua","Deleting item at position " << (nPos));
727             [rMenu removeItemAtIndex:nPos];
728         }
729             break;
730         case ControlActions::DELETE_ITEMS:
731         {
732             SAL_INFO("fpicker.aqua","DELETE_ITEMS");
733             int nItems = [rMenu numberOfItems];
734             if (nItems == 0) {
735                 SAL_INFO("fpicker.aqua","no menu items to delete");
736                 DBG_PRINT_EXIT(CLASS_NAME, __func__);
737                 return;
738             }
739             for(sal_Int32 i = 0; i < nItems; i++) {
740                 [rMenu removeItemAtIndex:i];
741             }
742         }
743             break;
744         case ControlActions::SET_SELECT_ITEM:
745         {
746             sal_Int32 nPos = -1;
747             rValue >>= nPos;
748             SAL_INFO("fpicker.aqua","Selecting item at position " << nPos);
749             [pButton selectItemAtIndex:nPos];
750         }
751             break;
752         default:
753             SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
754             break;
755     }
757     layoutControls();
759     DBG_PRINT_EXIT(CLASS_NAME, __func__);
762 // cf. offapi/com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.idl
763 NSControl* ControlHelper::getControl( const sal_Int16 nControlId ) const
765     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "controlId", nControlId);
767     NSControl* pWidget = nil;
769 #define MAP_TOGGLE( elem ) \
770 case ExtendedFilePickerElementIds::CHECKBOX_##elem: \
771     pWidget = m_pToggles[elem]; \
772     break
774 #define MAP_LIST( elem ) \
775 case ExtendedFilePickerElementIds::LISTBOX_##elem: \
776     pWidget = m_pListControls[elem]; \
777     break
779 #define MAP_LIST_LABEL( elem ) \
780 case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \
781     pWidget = m_pListControls[elem]; \
782     break
784     switch( nControlId )
785     {
786             MAP_TOGGLE( AUTOEXTENSION );
787             MAP_TOGGLE( PASSWORD );
788             MAP_TOGGLE( FILTEROPTIONS );
789             MAP_TOGGLE( READONLY );
790             MAP_TOGGLE( LINK );
791             MAP_TOGGLE( PREVIEW );
792             MAP_TOGGLE( SELECTION );
793             //MAP_BUTTON( PLAY );
794             MAP_LIST( VERSION );
795             MAP_LIST( TEMPLATE );
796             MAP_LIST( IMAGE_TEMPLATE );
797             MAP_LIST_LABEL( VERSION );
798             MAP_LIST_LABEL( TEMPLATE );
799             MAP_LIST_LABEL( IMAGE_TEMPLATE );
800         default:
801             SAL_INFO("fpicker.aqua","Handle unknown control " << nControlId);
802             break;
803     }
804 #undef MAP
806     DBG_PRINT_EXIT(CLASS_NAME, __func__);
808     return pWidget;
811 void ControlHelper::layoutControls()
813     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
815     SolarMutexGuard aGuard;
817     if (nil == m_pUserPane) {
818         SAL_INFO("fpicker.aqua","no user pane to layout");
819         DBG_PRINT_EXIT(CLASS_NAME, __func__);
820         return;
821     }
823     if (m_bIsUserPaneLaidOut) {
824         SAL_INFO("fpicker.aqua","user pane already laid out");
825         DBG_PRINT_EXIT(CLASS_NAME, __func__);
826         return;
827     }
829     NSRect userPaneRect = [m_pUserPane frame];
830     SAL_INFO("fpicker.aqua","userPane frame: {" << userPaneRect.origin.x << ", " << userPaneRect.origin.y << ", " << userPaneRect.size.width << ", " << userPaneRect.size.height << "}");
832     int nUsableWidth = userPaneRect.size.width;
834     //NOTE: NSView's coordinate system starts in the lower left hand corner but we start adding controls from the top,
835     // so we subtract from the vertical position as we make our way down the pane.
836     int currenttop = userPaneRect.size.height;
837     int nCheckboxMaxWidth = 0;
838     int nPopupMaxWidth = 0;
839     int nPopupLabelMaxWidth = 0;
841     //first loop to determine max sizes
842     for (::std::list<NSControl*>::iterator child = m_aActiveControls.begin(); child != m_aActiveControls.end(); child++) {
843         NSControl* pControl = *child;
845         NSRect controlRect = [pControl frame];
846         int nControlWidth = controlRect.size.width;
848         Class aSubType = [pControl class];
849         if (aSubType == [NSPopUpButton class]) {
850             if (nPopupMaxWidth < nControlWidth) {
851                 nPopupMaxWidth = nControlWidth;
852             }
853             NSTextField *label = m_aMapListLabelFields[(NSPopUpButton*)pControl];
854             NSRect labelFrame = [label frame];
855             int nLabelWidth = labelFrame.size.width;
856             if (nPopupLabelMaxWidth < nLabelWidth) {
857                 nPopupLabelMaxWidth = nLabelWidth;
858             }
859         } else {
860             if (nCheckboxMaxWidth < nControlWidth) {
861                 nCheckboxMaxWidth = nControlWidth;
862             }
863         }
864     }
866     int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
867     SAL_INFO("fpicker.aqua","longest popup width: " << nLongestPopupWidth);
869     NSControl* previousControl = nil;
871     int nDistBetweenControls = 0;
873     for (::std::list<NSControl*>::iterator child = m_aActiveControls.begin(); child != m_aActiveControls.end(); child++) {
874         NSControl* pControl = *child;
876         //get the control's bounds
877         NSRect controlRect = [pControl frame];
878         int nControlHeight = controlRect.size.height;
879         int nControlWidth = controlRect.size.width;
881         //subtract the height from the current vertical position, because the control's bounds origin rect will be its lower left hand corner
882         currenttop -= nControlHeight;
884         Class aSubType = [pControl class];
886         //add space between the previous control and this control according to Apple's HIG
887         nDistBetweenControls = getVerticalDistance(previousControl, pControl);
888         SAL_INFO("fpicker.aqua","vertical distance: " << nDistBetweenControls);
889         currenttop -= nDistBetweenControls;
891         previousControl = pControl;
893         if (aSubType == [NSPopUpButton class]) {
894             //move vertically up some pixels to space the controls between their real (visual) bounds
895             currenttop += kAquaSpacePopupMenuFrameBoundsDiffTop;//from top
897             //get the corresponding popup label
898             NSTextField *label = m_aMapListLabelFields[(NSPopUpButton*)pControl];
899             NSRect labelFrame = [label frame];
900             int totalWidth = nPopupMaxWidth + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
901             SAL_INFO("fpicker.aqua","totalWidth: " << totalWidth);
902             //let's center popups
903             int left = (nUsableWidth + nLongestPopupWidth) / 2 - totalWidth;
904             SAL_INFO("fpicker.aqua","left: " << left);
905             labelFrame.origin.x = left;
906             labelFrame.origin.y = currenttop + kAquaSpaceLabelPopupDiffV;
907             SAL_INFO("fpicker.aqua","setting label at: {" << labelFrame.origin.x << ", " << labelFrame.origin.y << ", " << labelFrame.size.width << ", " << labelFrame.size.height << "}");
908             [label setFrame:labelFrame];
910             controlRect.origin.x = left + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
911             controlRect.origin.y = currenttop;
912             controlRect.size.width = nPopupMaxWidth;
913             SAL_INFO("fpicker.aqua","setting popup at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}");
914             [pControl setFrame:controlRect];
916             //add some space to place the vertical position right below the popup's visual bounds
917             currenttop += kAquaSpacePopupMenuFrameBoundsDiffBottom;
918         } else {
919             currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;//from top
921             nControlWidth = nCheckboxMaxWidth;
922             int left = (nUsableWidth - nCheckboxMaxWidth) / 2;
923             controlRect.origin.x = left;
924             controlRect.origin.y = currenttop;
925             controlRect.size.width = nPopupMaxWidth;
926             [pControl setFrame:controlRect];
927             SAL_INFO("fpicker.aqua","setting checkbox at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}");
929             currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;
930         }
931     }
933     m_bIsUserPaneLaidOut = true;
935     DBG_PRINT_EXIT(CLASS_NAME, __func__);
938 void ControlHelper::createFilterControl() {
939     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
941     CResourceProvider aResProvider;
942     NSString* sLabel = aResProvider.getResString(CommonFilePickerElementIds::LISTBOX_FILTER_LABEL);
944     m_pFilterControl = [NSPopUpButton new];
946     [m_pFilterControl setAction:@selector(filterSelectedAtIndex:)];
947     [m_pFilterControl setTarget:m_pDelegate];
949     NSMenu *menu = [m_pFilterControl menu];
951     for (NSStringList::iterator iter = m_pFilterHelper->getFilterNames()->begin(); iter != m_pFilterHelper->getFilterNames()->end(); iter++) {
952         NSString *filterName = *iter;
953         SAL_INFO("fpicker.aqua","adding filter name: " << [filterName UTF8String]);
954         if ([filterName isEqualToString:@"-"]) {
955             [menu addItem:[NSMenuItem separatorItem]];
956         }
957         else {
958             [m_pFilterControl addItemWithTitle:filterName];
959         }
960     }
962     // always add the filter as first item
963     m_aActiveControls.push_front(m_pFilterControl);
964     m_aMapListLabels[m_pFilterControl] = [sLabel retain];
966     DBG_PRINT_EXIT(CLASS_NAME, __func__);
969 int ControlHelper::getVerticalDistance(const NSControl* first, const NSControl* second)
971     if (first == nil) {
972         return kAquaSpaceBoxFrameViewDiffTop;
973     }
974     else if (second == nil) {
975         return kAquaSpaceBoxFrameViewDiffBottom;
976     }
977     else {
978         Class firstClass = [first class];
979         Class secondClass = [second class];
981         if (firstClass == [NSPopUpButton class]) {
982             if (secondClass == [NSPopUpButton class]) {
983                 return kAquaSpaceBetweenPopupMenus;
984             }
985             else {
986                 return kAquaSpaceAfterPopupButtonsV;
987             }
988         }
990         return kAquaSpaceBetweenControls;
991     }
994 void ControlHelper::updateFilterUI()
996     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
998     if (!m_bIsFilterControlNeeded || m_pFilterHelper == NULL) {
999         SAL_INFO("fpicker.aqua","no filter control needed or no filter helper present");
1000         DBG_PRINT_EXIT(CLASS_NAME, __func__);
1001         return;
1002     }
1004     int index = m_pFilterHelper->getCurrentFilterIndex();
1006     if (m_pFilterControl == nil) {
1007         createFilterControl();
1008     }
1010     [m_pFilterControl selectItemAtIndex:index];
1012     DBG_PRINT_EXIT(CLASS_NAME, __func__);
1015 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */