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