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" 28#include "NSString_OOoAdditions.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 37using namespace ::com::sun::star::ui::dialogs; 38using namespace ::com::sun::star::ui::dialogs::TemplateDescription; 39using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds; 40using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds; 41 42namespace { 43 44uno::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 109NSTextField* 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]; 118 SAL_WNODEPRECATED_DECLARATIONS_POP 119 120 return textField; 121} 122 123} 124 125#pragma mark Constructor / Destructor 126 127// Constructor / Destructor 128 129ControlHelper::ControlHelper() 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 148ControlHelper::~ControlHelper() 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 182void 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 239void 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 262OUString 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]; 282 SAL_WNODEPRECATED_DECLARATIONS_POP 283 retVal = [sLabel OUString]; 284 } 285 286 return retVal; 287} 288 289void 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]; 308 SAL_WNODEPRECATED_DECLARATIONS_POP 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 319void 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 347uno::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 370void ControlHelper::createUserPane() 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 382 if (m_bIsFilterControlNeeded && m_pFilterControl == nil) { 383 createFilterControl(); 384 } 385 386 NSRect minRect = NSMakeRect(0,0,300,33); 387 m_pUserPane = [[NSView alloc] initWithFrame:minRect]; 388 389 int currentHeight = kAquaSpaceBoxFrameViewDiffTop + kAquaSpaceBoxFrameViewDiffBottom; 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 443 frame.origin.x += (kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft) + tfWidth; 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 462 nControlWidth += tfWidth + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft; 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 530void ControlHelper::createControls() 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) { 547 MAP_LIST_(VERSION); 548 MAP_LIST_(TEMPLATE); 549 MAP_LIST_(IMAGE_TEMPLATE); 550 MAP_LIST_(IMAGE_ANCHOR); 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 ) \ 595case elem: \ 596 nReturn = CHECKBOX_##elem; \ 597 return nReturn 598#define LIST_ELEMENT( elem ) \ 599case elem: \ 600 nReturn = LISTBOX_##elem##_LABEL; \ 601 return nReturn 602 603int ControlHelper::getControlElementName(const Class aClazz, const int nControlId) 604{ 605 int nReturn = -1; 606 if (aClazz == [NSButton class]) 607 { 608 switch (nControlId) { 609 TOGGLE_ELEMENT( AUTOEXTENSION ); 610 TOGGLE_ELEMENT( PASSWORD ); 611 TOGGLE_ELEMENT( FILTEROPTIONS ); 612 TOGGLE_ELEMENT( READONLY ); 613 TOGGLE_ELEMENT( LINK ); 614 TOGGLE_ELEMENT( PREVIEW ); 615 TOGGLE_ELEMENT( SELECTION ); 616 } 617 } 618 else if (aClazz == [NSPopUpButton class]) 619 { 620 switch (nControlId) { 621 LIST_ELEMENT( VERSION ); 622 LIST_ELEMENT( TEMPLATE ); 623 LIST_ELEMENT( IMAGE_TEMPLATE ); 624 LIST_ELEMENT( IMAGE_ANCHOR ); 625 } 626 } 627 628 return nReturn; 629} 630 631void 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 711NSControl* ControlHelper::getControl( const sal_Int16 nControlId ) const 712{ 713 NSControl* pWidget = nil; 714 715#define MAP_TOGGLE( elem ) \ 716case ExtendedFilePickerElementIds::CHECKBOX_##elem: \ 717 pWidget = m_pToggles[elem]; \ 718 break 719 720#define MAP_LIST( elem ) \ 721case ExtendedFilePickerElementIds::LISTBOX_##elem: \ 722 pWidget = m_pListControls[elem]; \ 723 break 724 725#define MAP_LIST_LABEL( elem ) \ 726case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \ 727 pWidget = m_pListControls[elem]; \ 728 break 729 730 switch( nControlId ) 731 { 732 MAP_TOGGLE( AUTOEXTENSION ); 733 MAP_TOGGLE( PASSWORD ); 734 MAP_TOGGLE( FILTEROPTIONS ); 735 MAP_TOGGLE( READONLY ); 736 MAP_TOGGLE( LINK ); 737 MAP_TOGGLE( PREVIEW ); 738 MAP_TOGGLE( SELECTION ); 739 //MAP_BUTTON( PLAY ); 740 MAP_LIST( VERSION ); 741 MAP_LIST( TEMPLATE ); 742 MAP_LIST( IMAGE_TEMPLATE ); 743 MAP_LIST( IMAGE_ANCHOR ); 744 MAP_LIST_LABEL( VERSION ); 745 MAP_LIST_LABEL( TEMPLATE ); 746 MAP_LIST_LABEL( IMAGE_TEMPLATE ); 747 MAP_LIST_LABEL( IMAGE_ANCHOR ); 748 default: 749 SAL_INFO("fpicker.aqua","Handle unknown control " << nControlId); 750 break; 751 } 752#undef MAP 753 754 return pWidget; 755} 756 757void ControlHelper::layoutControls() 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 821 //subtract the height from the current vertical position, because the control's bounds origin rect will be its lower left hand corner 822 currenttop -= nControlHeight; 823 824 Class aSubType = [activeControl class]; 825 826 //add space between the previous control and this control according to Apple's HIG 827 nDistBetweenControls = getVerticalDistance(previousControl, activeControl); 828 SAL_INFO("fpicker.aqua","vertical distance: " << nDistBetweenControls); 829 currenttop -= nDistBetweenControls; 830 831 previousControl = activeControl; 832 833 if (aSubType == [NSPopUpButton class]) { 834 //move vertically up some pixels to space the controls between their real (visual) bounds 835 currenttop += kAquaSpacePopupMenuFrameBoundsDiffTop;//from top 836 837 //get the corresponding popup label 838 NSTextField *label = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)]; 839 NSRect labelFrame = [label frame]; 840 int totalWidth = nPopupMaxWidth + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH; 841 SAL_INFO("fpicker.aqua","totalWidth: " << totalWidth); 842 //let's center popups 843 int left = (nUsableWidth + nLongestPopupWidth) / 2 - totalWidth; 844 SAL_INFO("fpicker.aqua","left: " << left); 845 labelFrame.origin.x = left; 846 labelFrame.origin.y = currenttop + kAquaSpaceLabelPopupDiffV; 847 SAL_INFO("fpicker.aqua","setting label at: {" << labelFrame.origin.x << ", " << labelFrame.origin.y << ", " << labelFrame.size.width << ", " << labelFrame.size.height << "}"); 848 [label setFrame:labelFrame]; 849 850 controlRect.origin.x = left + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft; 851 controlRect.origin.y = currenttop; 852 controlRect.size.width = nPopupMaxWidth; 853 SAL_INFO("fpicker.aqua","setting popup at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}"); 854 [activeControl setFrame:controlRect]; 855 856 //add some space to place the vertical position right below the popup's visual bounds 857 currenttop += kAquaSpacePopupMenuFrameBoundsDiffBottom; 858 } else { 859 currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;//from top 860 861 int left = (nUsableWidth - nCheckboxMaxWidth) / 2; 862 controlRect.origin.x = left; 863 controlRect.origin.y = currenttop; 864 controlRect.size.width = nPopupMaxWidth; 865 [activeControl setFrame:controlRect]; 866 SAL_INFO("fpicker.aqua","setting checkbox at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}"); 867 868 currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff; 869 } 870 } 871 872 m_bIsUserPaneLaidOut = true; 873} 874 875void ControlHelper::createFilterControl() 876{ 877 NSString* sLabel = CResourceProvider::getResString(CommonFilePickerElementIds::LISTBOX_FILTER_LABEL); 878 879 m_pFilterControl = [NSPopUpButton new]; 880 881 [m_pFilterControl setAction:@selector(filterSelectedAtIndex:)]; 882 [m_pFilterControl setTarget:m_pDelegate]; 883 884 NSMenu *menu = [m_pFilterControl menu]; 885 886 for (auto const& filterName : *m_pFilterHelper->getFilterNames()) 887 { 888 SAL_INFO("fpicker.aqua","adding filter name: " << [filterName UTF8String]); 889 if ([filterName isEqualToString:@"-"]) { 890 [menu addItem:[NSMenuItem separatorItem]]; 891 } 892 else { 893 [m_pFilterControl addItemWithTitle:filterName]; 894 } 895 } 896 897 // always add the filter as first item 898 m_aActiveControls.push_front(m_pFilterControl); 899 m_aMapListLabels[m_pFilterControl] = [sLabel retain]; 900} 901 902int ControlHelper::getVerticalDistance(const NSControl* first, const NSControl* second) 903{ 904 if (first == nil) { 905 return kAquaSpaceBoxFrameViewDiffTop; 906 } 907 else if (second == nil) { 908 return kAquaSpaceBoxFrameViewDiffBottom; 909 } 910 else { 911 Class firstClass = [first class]; 912 Class secondClass = [second class]; 913 914 if (firstClass == [NSPopUpButton class]) { 915 if (secondClass == [NSPopUpButton class]) { 916 return kAquaSpaceBetweenPopupMenus; 917 } 918 else { 919 return kAquaSpaceAfterPopupButtonsV; 920 } 921 } 922 923 return kAquaSpaceBetweenControls; 924 } 925} 926 927void ControlHelper::updateFilterUI() 928{ 929 if (!m_bIsFilterControlNeeded || m_pFilterHelper == nullptr) { 930 SAL_INFO("fpicker.aqua","no filter control needed or no filter helper present"); 931 return; 932 } 933 934 int index = m_pFilterHelper->getCurrentFilterIndex(); 935 936 if (m_pFilterControl == nil) { 937 createFilterControl(); 938 } 939 940 [m_pFilterControl selectItemAtIndex:index]; 941} 942 943/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 944