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