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 <sal/config.h> 21#include <sal/log.hxx> 22 23#include <i18nlangtag/languagetag.hxx> 24#include <vcl/print.hxx> 25#include <vcl/image.hxx> 26#include <vcl/virdev.hxx> 27#include <vcl/svapp.hxx> 28#include <vcl/unohelp.hxx> 29#include <vcl/settings.hxx> 30 31#include <osx/printview.h> 32#include <osx/salinst.h> 33#include <quartz/utils.h> 34 35#include <svdata.hxx> 36#include <strings.hrc> 37#include <printaccessoryview.hrc> 38 39#include <com/sun/star/i18n/XBreakIterator.hpp> 40#include <com/sun/star/i18n/WordType.hpp> 41 42#include <map> 43 44using namespace vcl; 45using namespace com::sun::star; 46using namespace com::sun::star::beans; 47using namespace com::sun::star::uno; 48 49class ControllerProperties; 50 51@interface ControlTarget : NSObject 52{ 53 ControllerProperties* mpController; 54} 55-(id)initWithControllerMap: (ControllerProperties*)pController; 56-(void)triggered:(id)pSender; 57-(void)triggeredNumeric:(id)pSender; 58-(void)dealloc; 59@end 60 61@interface AquaPrintPanelAccessoryController : NSViewController< NSPrintPanelAccessorizing > 62{ 63 NSPrintOperation *mpPrintOperation; 64 vcl::PrinterController *mpPrinterController; 65 PrintAccessoryViewState *mpViewState; 66} 67 68-(void)forPrintOperation:(NSPrintOperation*)pPrintOp; 69-(void)withPrinterController:(vcl::PrinterController*)pController; 70-(void)withViewState:(PrintAccessoryViewState*)pState; 71 72-(NSPrintOperation*)printOperation; 73-(vcl::PrinterController*)printerController; 74-(PrintAccessoryViewState*)viewState; 75 76-(NSSet*)keyPathsForValuesAffectingPreview; 77-(NSArray*)localizedSummaryItems; 78 79-(sal_Int32)updatePrintOperation:(sal_Int32)pLastPageCount; 80 81@end 82 83@implementation AquaPrintPanelAccessoryController 84 85-(void)forPrintOperation:(NSPrintOperation*)pPrintOp 86 { mpPrintOperation = pPrintOp; } 87 88-(void)withPrinterController:(vcl::PrinterController*)pController 89 { mpPrinterController = pController; } 90 91-(void)withViewState:(PrintAccessoryViewState*)pState 92 { mpViewState = pState; } 93 94-(NSPrintOperation*)printOperation 95 { return mpPrintOperation; } 96 97-(vcl::PrinterController*)printerController 98 { return mpPrinterController; } 99 100-(PrintAccessoryViewState*)viewState 101 { return mpViewState; } 102 103-(NSSet*)keyPathsForValuesAffectingPreview 104{ 105 return [ NSSet setWithObject:@"updatePrintOperation" ]; 106} 107 108-(NSArray*)localizedSummaryItems 109{ 110 return [ NSArray arrayWithObject: 111 [ NSDictionary dictionary ] ]; 112} 113 114-(sal_Int32)updatePrintOperation:(sal_Int32)pLastPageCount 115{ 116 // page range may be changed by option choice 117 sal_Int32 nPages = mpPrinterController->getFilteredPageCount(); 118 119 mpViewState->bNeedRestart = false; 120 if( nPages != pLastPageCount ) 121 { 122 #if OSL_DEBUG_LEVEL > 1 123 SAL_INFO( "vcl.osx.print", "number of pages changed" << 124 " from " << pLastPageCount << " to " << nPages ); 125 #endif 126 mpViewState->bNeedRestart = true; 127 } 128 129 NSTabView* pTabView = [[[self view] subviews] objectAtIndex:0]; 130 NSTabViewItem* pItem = [pTabView selectedTabViewItem]; 131 if( pItem ) 132 mpViewState->nLastPage = [pTabView indexOfTabViewItem: pItem]; 133 else 134 mpViewState->nLastPage = 0; 135 136 if( mpViewState->bNeedRestart ) 137 { 138 // AppKit does not give a chance of changing the page count 139 // and don't let cancel the dialog either 140 // hack: send a cancel message to the modal window displaying views 141 NSWindow* pNSWindow = [NSApp modalWindow]; 142 if( pNSWindow ) 143 [pNSWindow cancelOperation: nil]; 144 [[mpPrintOperation printInfo] setJobDisposition: NSPrintCancelJob]; 145 } 146 147 return nPages; 148} 149 150@end 151 152class ControllerProperties 153{ 154 std::map< int, OUString > maTagToPropertyName; 155 std::map< int, sal_Int32 > maTagToValueInt; 156 std::map< NSView*, NSView* > maViewPairMap; 157 std::vector< NSObject* > maViews; 158 int mnNextTag; 159 sal_Int32 mnLastPageCount; 160 AquaPrintPanelAccessoryController* mpAccessoryController; 161 162public: 163 ControllerProperties( AquaPrintPanelAccessoryController* i_pAccessoryController ) 164 : mnNextTag( 0 ) 165 , mnLastPageCount( [i_pAccessoryController printerController]->getFilteredPageCount() ) 166 , mpAccessoryController( i_pAccessoryController ) 167 { 168 static_assert( SAL_N_ELEMENTS(SV_PRINT_NATIVE_STRINGS) == 5, "resources not found" ); 169 } 170 171 static OUString getMoreString() 172 { 173 return VclResId(SV_PRINT_NATIVE_STRINGS[3]); 174 } 175 176 static OUString getPrintSelectionString() 177 { 178 return VclResId(SV_PRINT_NATIVE_STRINGS[4]); 179 } 180 181 int addNameTag( const OUString& i_rPropertyName ) 182 { 183 int nNewTag = mnNextTag++; 184 maTagToPropertyName[ nNewTag ] = i_rPropertyName; 185 return nNewTag; 186 } 187 188 int addNameAndValueTag( const OUString& i_rPropertyName, sal_Int32 i_nValue ) 189 { 190 int nNewTag = mnNextTag++; 191 maTagToPropertyName[ nNewTag ] = i_rPropertyName; 192 maTagToValueInt[ nNewTag ] = i_nValue; 193 return nNewTag; 194 } 195 196 void addObservedControl( NSObject* i_pView ) 197 { 198 maViews.push_back( i_pView ); 199 } 200 201 void addViewPair( NSView* i_pLeft, NSView* i_pRight ) 202 { 203 maViewPairMap[ i_pLeft ] = i_pRight; 204 maViewPairMap[ i_pRight ] = i_pLeft; 205 } 206 207 NSView* getPair( NSView* i_pLeft ) const 208 { 209 NSView* pRight = nil; 210 std::map< NSView*, NSView* >::const_iterator it = maViewPairMap.find( i_pLeft ); 211 if( it != maViewPairMap.end() ) 212 pRight = it->second; 213 return pRight; 214 } 215 216 void changePropertyWithIntValue( int i_nTag ) 217 { 218 std::map< int, OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag ); 219 std::map< int, sal_Int32 >::const_iterator value_it = maTagToValueInt.find( i_nTag ); 220 if( name_it != maTagToPropertyName.end() && value_it != maTagToValueInt.end() ) 221 { 222 vcl::PrinterController * mpController = [mpAccessoryController printerController]; 223 PropertyValue* pVal = mpController->getValue( name_it->second ); 224 if( pVal ) 225 { 226 pVal->Value <<= value_it->second; 227 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount]; 228 } 229 } 230 } 231 232 void changePropertyWithIntValue( int i_nTag, sal_Int64 i_nValue ) 233 { 234 std::map< int, OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag ); 235 if( name_it != maTagToPropertyName.end() ) 236 { 237 vcl::PrinterController * mpController = [mpAccessoryController printerController]; 238 PropertyValue* pVal = mpController->getValue( name_it->second ); 239 if( pVal ) 240 { 241 pVal->Value <<= i_nValue; 242 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount]; 243 } 244 } 245 } 246 247 void changePropertyWithBoolValue( int i_nTag, bool i_bValue ) 248 { 249 std::map< int, OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag ); 250 if( name_it != maTagToPropertyName.end() ) 251 { 252 vcl::PrinterController * mpController = [mpAccessoryController printerController]; 253 PropertyValue* pVal = mpController->getValue( name_it->second ); 254 if( pVal ) 255 { 256 // ugly 257 if( name_it->second == "PrintContent" ) 258 pVal->Value <<= i_bValue ? sal_Int32(2) : sal_Int32(0); 259 else 260 pVal->Value <<= i_bValue; 261 262 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount]; 263 } 264 } 265 } 266 267 void changePropertyWithStringValue( int i_nTag, const OUString& i_rValue ) 268 { 269 std::map< int, OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag ); 270 if( name_it != maTagToPropertyName.end() ) 271 { 272 vcl::PrinterController * mpController = [mpAccessoryController printerController]; 273 PropertyValue* pVal = mpController->getValue( name_it->second ); 274 if( pVal ) 275 { 276 pVal->Value <<= i_rValue; 277 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount]; 278 } 279 } 280 } 281 282 void updateEnableState() 283 { 284 for( std::vector< NSObject* >::iterator it = maViews.begin(); it != maViews.end(); ++it ) 285 { 286 NSObject* pObj = *it; 287 NSControl* pCtrl = nil; 288 NSCell* pCell = nil; 289 if( [pObj isKindOfClass: [NSControl class]] ) 290 pCtrl = static_cast<NSControl*>(pObj); 291 else if( [pObj isKindOfClass: [NSCell class]] ) 292 pCell = static_cast<NSCell*>(pObj); 293 294 int nTag = pCtrl ? [pCtrl tag] : 295 pCell ? [pCell tag] : 296 -1; 297 298 std::map< int, OUString >::const_iterator name_it = maTagToPropertyName.find( nTag ); 299 if( name_it != maTagToPropertyName.end() && name_it->second != "PrintContent" ) 300 { 301 vcl::PrinterController * mpController = [mpAccessoryController printerController]; 302 BOOL bEnabled = mpController->isUIOptionEnabled( name_it->second ) ? YES : NO; 303 if( pCtrl ) 304 { 305 [pCtrl setEnabled: bEnabled]; 306 NSView* pOther = getPair( pCtrl ); 307 if( pOther && [pOther isKindOfClass: [NSControl class]] ) 308 [static_cast<NSControl*>(pOther) setEnabled: bEnabled]; 309 } 310 else if( pCell ) 311 [pCell setEnabled: bEnabled]; 312 } 313 } 314 } 315 316}; 317 318static OUString filterAccelerator( OUString const & rText ) 319{ 320 OUStringBuffer aBuf( rText.getLength() ); 321 for( sal_Int32 nIndex = 0; nIndex != -1; ) 322 aBuf.append( rText.getToken( 0, '~', nIndex ) ); 323 return aBuf.makeStringAndClear(); 324} 325 326@implementation ControlTarget 327 328-(id)initWithControllerMap: (ControllerProperties*)pController 329{ 330 if( (self = [super init]) ) 331 { 332 mpController = pController; 333 } 334 return self; 335} 336 337-(void)triggered:(id)pSender 338{ 339 if( [pSender isMemberOfClass: [NSPopUpButton class]] ) 340 { 341 NSPopUpButton* pBtn = static_cast<NSPopUpButton*>(pSender); 342 NSMenuItem* pSelected = [pBtn selectedItem]; 343 if( pSelected ) 344 { 345 int nTag = [pSelected tag]; 346 mpController->changePropertyWithIntValue( nTag ); 347 } 348 } 349 else if( [pSender isMemberOfClass: [NSButton class]] ) 350 { 351 NSButton* pBtn = static_cast<NSButton*>(pSender); 352 int nTag = [pBtn tag]; 353 mpController->changePropertyWithBoolValue( nTag, [pBtn state] == NSControlStateValueOn ); 354 } 355 else if( [pSender isMemberOfClass: [NSMatrix class]] ) 356 { 357 NSObject* pObj = [static_cast<NSMatrix*>(pSender) selectedCell]; 358 if( [pObj isMemberOfClass: [NSButtonCell class]] ) 359 { 360 NSButtonCell* pCell = static_cast<NSButtonCell*>(pObj); 361 int nTag = [pCell tag]; 362 mpController->changePropertyWithIntValue( nTag ); 363 } 364 } 365 else if( [pSender isMemberOfClass: [NSTextField class]] ) 366 { 367 NSTextField* pField = static_cast<NSTextField*>(pSender); 368 int nTag = [pField tag]; 369 OUString aValue = GetOUString( [pSender stringValue] ); 370 mpController->changePropertyWithStringValue( nTag, aValue ); 371 } 372 else 373 { 374 SAL_INFO( "vcl.osx.print", "Unsupported class" << 375 ( [pSender class] ? [NSStringFromClass([pSender class]) UTF8String] : "nil" ) ); 376 } 377 mpController->updateEnableState(); 378} 379 380-(void)triggeredNumeric:(id)pSender 381{ 382 if( [pSender isMemberOfClass: [NSTextField class]] ) 383 { 384 NSTextField* pField = static_cast<NSTextField*>(pSender); 385 int nTag = [pField tag]; 386 sal_Int64 nValue = [pField intValue]; 387 388 NSView* pOther = mpController->getPair( pField ); 389 if( pOther ) 390 [static_cast<NSControl*>(pOther) setIntValue: nValue]; 391 392 mpController->changePropertyWithIntValue( nTag, nValue ); 393 } 394 else if( [pSender isMemberOfClass: [NSStepper class]] ) 395 { 396 NSStepper* pStep = static_cast<NSStepper*>(pSender); 397 int nTag = [pStep tag]; 398 sal_Int64 nValue = [pStep intValue]; 399 400 NSView* pOther = mpController->getPair( pStep ); 401 if( pOther ) 402 [static_cast<NSControl*>(pOther) setIntValue: nValue]; 403 404 mpController->changePropertyWithIntValue( nTag, nValue ); 405 } 406 else 407 { 408 SAL_INFO( "vcl.osx.print", "Unsupported class" << 409 ([pSender class] ? [NSStringFromClass([pSender class]) UTF8String] : "nil") ); 410 } 411 mpController->updateEnableState(); 412} 413 414-(void)dealloc 415{ 416 delete mpController; 417 [super dealloc]; 418} 419 420@end 421 422struct ColumnItem 423{ 424 NSControl* pControl; 425 long nOffset; 426 NSControl* pSubControl; 427 428 ColumnItem( NSControl* i_pControl = nil, long i_nOffset = 0, NSControl* i_pSub = nil ) 429 : pControl( i_pControl ) 430 , nOffset( i_nOffset ) 431 , pSubControl( i_pSub ) 432 {} 433 434 long getWidth() const 435 { 436 long nWidth = 0; 437 if( pControl ) 438 { 439 NSRect aCtrlRect = [pControl frame]; 440 nWidth = aCtrlRect.size.width; 441 nWidth += nOffset; 442 if( pSubControl ) 443 { 444 NSRect aSubRect = [pSubControl frame]; 445 nWidth += aSubRect.size.width; 446 nWidth += aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width); 447 } 448 } 449 return nWidth; 450 } 451}; 452 453static void adjustViewAndChildren( NSView* pNSView, NSSize& rMaxSize, 454 std::vector< ColumnItem >& rLeftColumn, 455 std::vector< ColumnItem >& rRightColumn 456 ) 457{ 458 // balance columns 459 460 // first get overall column widths 461 long nLeftWidth = 0; 462 long nRightWidth = 0; 463 for( size_t i = 0; i < rLeftColumn.size(); i++ ) 464 { 465 long nW = rLeftColumn[i].getWidth(); 466 if( nW > nLeftWidth ) 467 nLeftWidth = nW; 468 } 469 for( size_t i = 0; i < rRightColumn.size(); i++ ) 470 { 471 long nW = rRightColumn[i].getWidth(); 472 if( nW > nRightWidth ) 473 nRightWidth = nW; 474 } 475 476 // right align left column 477 for( size_t i = 0; i < rLeftColumn.size(); i++ ) 478 { 479 if( rLeftColumn[i].pControl ) 480 { 481 NSRect aCtrlRect = [rLeftColumn[i].pControl frame]; 482 long nX = nLeftWidth - aCtrlRect.size.width; 483 if( rLeftColumn[i].pSubControl ) 484 { 485 NSRect aSubRect = [rLeftColumn[i].pSubControl frame]; 486 nX -= aSubRect.size.width + (aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width)); 487 aSubRect.origin.x = nLeftWidth - aSubRect.size.width; 488 [rLeftColumn[i].pSubControl setFrame: aSubRect]; 489 } 490 aCtrlRect.origin.x = nX; 491 [rLeftColumn[i].pControl setFrame: aCtrlRect]; 492 } 493 } 494 495 // left align right column 496 for( size_t i = 0; i < rRightColumn.size(); i++ ) 497 { 498 if( rRightColumn[i].pControl ) 499 { 500 NSRect aCtrlRect = [rRightColumn[i].pControl frame]; 501 long nX = nLeftWidth + 3; 502 if( rRightColumn[i].pSubControl ) 503 { 504 NSRect aSubRect = [rRightColumn[i].pSubControl frame]; 505 aSubRect.origin.x = nX + aSubRect.origin.x - aCtrlRect.origin.x; 506 [rRightColumn[i].pSubControl setFrame: aSubRect]; 507 } 508 aCtrlRect.origin.x = nX; 509 [rRightColumn[i].pControl setFrame: aCtrlRect]; 510 } 511 } 512 513 NSArray* pSubViews = [pNSView subviews]; 514 unsigned int nViews = [pSubViews count]; 515 NSRect aUnion = NSZeroRect; 516 517 // get the combined frame of all subviews 518 for( unsigned int n = 0; n < nViews; n++ ) 519 { 520 aUnion = NSUnionRect( aUnion, [[pSubViews objectAtIndex: n] frame] ); 521 } 522 523 // move everything so it will fit 524 for( unsigned int n = 0; n < nViews; n++ ) 525 { 526 NSView* pCurSubView = [pSubViews objectAtIndex: n]; 527 NSRect aFrame = [pCurSubView frame]; 528 aFrame.origin.x -= aUnion.origin.x - 5; 529 aFrame.origin.y -= aUnion.origin.y - 5; 530 [pCurSubView setFrame: aFrame]; 531 } 532 533 // resize the view itself 534 aUnion.size.height += 10; 535 aUnion.size.width += 20; 536 [pNSView setFrameSize: aUnion.size]; 537 538 if( aUnion.size.width > rMaxSize.width ) 539 rMaxSize.width = aUnion.size.width; 540 if( aUnion.size.height > rMaxSize.height ) 541 rMaxSize.height = aUnion.size.height; 542} 543 544static void adjustTabViews( NSTabView* pTabView, NSSize aTabSize ) 545{ 546 // loop over all contained tab pages 547 NSArray* pTabbedViews = [pTabView tabViewItems]; 548 int nViews = [pTabbedViews count]; 549 for( int i = 0; i < nViews; i++ ) 550 { 551 NSTabViewItem* pItem = static_cast<NSTabViewItem*>([pTabbedViews objectAtIndex: i]); 552 NSView* pNSView = [pItem view]; 553 if( pNSView ) 554 { 555 NSRect aRect = [pNSView frame]; 556 double nDiff = aTabSize.height - aRect.size.height; 557 aRect.size = aTabSize; 558 [pNSView setFrame: aRect]; 559 560 NSArray* pSubViews = [pNSView subviews]; 561 unsigned int nSubViews = [pSubViews count]; 562 563 // move everything up 564 for( unsigned int n = 0; n < nSubViews; n++ ) 565 { 566 NSView* pCurSubView = [pSubViews objectAtIndex: n]; 567 NSRect aFrame = [pCurSubView frame]; 568 aFrame.origin.y += nDiff; 569 // give separators the correct width 570 // separators are currently the only NSBoxes we use 571 if( [pCurSubView isMemberOfClass: [NSBox class]] ) 572 { 573 aFrame.size.width = aTabSize.width - aFrame.origin.x - 10; 574 } 575 [pCurSubView setFrame: aFrame]; 576 } 577 } 578 } 579} 580 581static NSControl* createLabel( const OUString& i_rText ) 582{ 583 NSString* pText = CreateNSString( i_rText ); 584 NSRect aTextRect = { NSZeroPoint, {20, 15} }; 585 NSTextField* pTextView = [[NSTextField alloc] initWithFrame: aTextRect]; 586 [pTextView setFont: [NSFont controlContentFontOfSize: 0]]; 587 [pTextView setEditable: NO]; 588 [pTextView setSelectable: NO]; 589 [pTextView setDrawsBackground: NO]; 590 [pTextView setBordered: NO]; 591 [pTextView setStringValue: pText]; 592 [pTextView sizeToFit]; 593 [pText release]; 594 return pTextView; 595} 596 597static sal_Int32 findBreak( const OUString& i_rText, sal_Int32 i_nPos ) 598{ 599 sal_Int32 nRet = i_rText.getLength(); 600 Reference< i18n::XBreakIterator > xBI( vcl::unohelper::CreateBreakIterator() ); 601 if( xBI.is() ) 602 { 603 i18n::Boundary aBoundary = 604 xBI->getWordBoundary( i_rText, i_nPos, 605 Application::GetSettings().GetLanguageTag().getLocale(), 606 i18n::WordType::ANYWORD_IGNOREWHITESPACES, 607 true ); 608 nRet = aBoundary.endPos; 609 } 610 return nRet; 611} 612 613static void linebreakCell( NSCell* pBtn, const OUString& i_rText ) 614{ 615 NSString* pText = CreateNSString( i_rText ); 616 [pBtn setTitle: pText]; 617 [pText release]; 618 NSSize aSize = [pBtn cellSize]; 619 if( aSize.width > 280 ) 620 { 621 // need two lines 622 sal_Int32 nLen = i_rText.getLength(); 623 sal_Int32 nIndex = nLen / 2; 624 nIndex = findBreak( i_rText, nIndex ); 625 if( nIndex < nLen ) 626 { 627 OUStringBuffer aBuf( i_rText ); 628 aBuf[nIndex] = '\n'; 629 pText = CreateNSString( aBuf.makeStringAndClear() ); 630 [pBtn setTitle: pText]; 631 [pText release]; 632 } 633 } 634} 635 636static void addSubgroup( NSView* pCurParent, long& rCurY, const OUString& rText ) 637{ 638 NSControl* pTextView = createLabel( rText ); 639 [pCurParent addSubview: [pTextView autorelease]]; 640 NSRect aTextRect = [pTextView frame]; 641 // move to nCurY 642 aTextRect.origin.y = rCurY - aTextRect.size.height; 643 [pTextView setFrame: aTextRect]; 644 645 NSRect aSepRect = { { aTextRect.size.width + 1, aTextRect.origin.y }, { 100, 6 } }; 646 NSBox* pBox = [[NSBox alloc] initWithFrame: aSepRect]; 647 [pBox setBoxType: NSBoxSeparator]; 648 [pCurParent addSubview: [pBox autorelease]]; 649 650 // update nCurY 651 rCurY = aTextRect.origin.y - 5; 652} 653 654static void addBool( NSView* pCurParent, long rCurX, long& rCurY, long nAttachOffset, 655 const OUString& rText, bool bEnabled, 656 const OUString& rProperty, bool bValue, 657 std::vector<ColumnItem >& rRightColumn, 658 ControllerProperties* pControllerProperties, 659 ControlTarget* pCtrlTarget 660 ) 661{ 662 NSRect aCheckRect = { { static_cast<CGFloat>(rCurX + nAttachOffset), 0 }, { 0, 15 } }; 663 NSButton* pBtn = [[NSButton alloc] initWithFrame: aCheckRect]; 664 [pBtn setButtonType: NSButtonTypeSwitch]; 665 [pBtn setState: bValue ? NSControlStateValueOn : NSControlStateValueOff]; 666 if( ! bEnabled ) 667 [pBtn setEnabled: NO]; 668 linebreakCell( [pBtn cell], rText ); 669 [pBtn sizeToFit]; 670 671 rRightColumn.push_back( ColumnItem( pBtn ) ); 672 673 // connect target 674 [pBtn setTarget: pCtrlTarget]; 675 [pBtn setAction: @selector(triggered:)]; 676 int nTag = pControllerProperties->addNameTag( rProperty ); 677 pControllerProperties->addObservedControl( pBtn ); 678 [pBtn setTag: nTag]; 679 680 aCheckRect = [pBtn frame]; 681 // #i115837# add a murphy factor; it can apparently occasionally happen 682 // that sizeToFit does not a perfect job and that the button linebreaks again 683 // if - and only if - there is already a '\n' contained in the text and the width 684 // is minimally of 685 aCheckRect.size.width += 1; 686 687 // move to rCurY 688 aCheckRect.origin.y = rCurY - aCheckRect.size.height; 689 [pBtn setFrame: aCheckRect]; 690 691 [pCurParent addSubview: [pBtn autorelease]]; 692 693 // update rCurY 694 rCurY = aCheckRect.origin.y - 5; 695} 696 697static void addRadio( NSView* pCurParent, long rCurX, long& rCurY, long nAttachOffset, 698 const OUString& rText, 699 const OUString& rProperty, Sequence<OUString> const & rChoices, sal_Int32 nSelectValue, 700 std::vector<ColumnItem >& rLeftColumn, 701 std::vector<ColumnItem >& rRightColumn, 702 ControllerProperties* pControllerProperties, 703 ControlTarget* pCtrlTarget 704 ) 705{ 706 sal_Int32 nOff = 0; 707 if( rText.getLength() ) 708 { 709 // add a label 710 NSControl* pTextView = createLabel( rText ); 711 NSRect aTextRect = [pTextView frame]; 712 aTextRect.origin.x = rCurX + nAttachOffset; 713 [pCurParent addSubview: [pTextView autorelease]]; 714 715 rLeftColumn.push_back( ColumnItem( pTextView ) ); 716 717 // move to nCurY 718 aTextRect.origin.y = rCurY - aTextRect.size.height; 719 [pTextView setFrame: aTextRect]; 720 721 // update nCurY 722 rCurY = aTextRect.origin.y - 5; 723 724 // indent the radio group relative to the text 725 // nOff = 20; 726 } 727 728 // setup radio matrix 729 NSButtonCell* pProto = [[NSButtonCell alloc] init]; 730 731 NSRect aRadioRect = { { static_cast<CGFloat>(rCurX + nOff), 0 }, 732 { static_cast<CGFloat>(280 - rCurX), 733 static_cast<CGFloat>(5*rChoices.getLength()) } }; 734 [pProto setTitle: @"RadioButtonGroup"]; 735 [pProto setButtonType: NSButtonTypeRadio]; 736 NSMatrix* pMatrix = [[NSMatrix alloc] initWithFrame: aRadioRect 737 mode: NSRadioModeMatrix 738 prototype: static_cast<NSCell*>(pProto) 739 numberOfRows: rChoices.getLength() 740 numberOfColumns: 1]; 741 // set individual titles 742 NSArray* pCells = [pMatrix cells]; 743 for( sal_Int32 m = 0; m < rChoices.getLength(); m++ ) 744 { 745 NSCell* pCell = [pCells objectAtIndex: m]; 746 linebreakCell( pCell, filterAccelerator( rChoices[m] ) ); 747 // connect target and action 748 [pCell setTarget: pCtrlTarget]; 749 [pCell setAction: @selector(triggered:)]; 750 int nTag = pControllerProperties->addNameAndValueTag( rProperty, m ); 751 pControllerProperties->addObservedControl( pCell ); 752 [pCell setTag: nTag]; 753 // set current selection 754 if( nSelectValue == m ) 755 [pMatrix selectCellAtRow: m column: 0]; 756 } 757 [pMatrix sizeToFit]; 758 aRadioRect = [pMatrix frame]; 759 760 // move it down, so it comes to the correct position 761 aRadioRect.origin.y = rCurY - aRadioRect.size.height; 762 [pMatrix setFrame: aRadioRect]; 763 [pCurParent addSubview: [pMatrix autorelease]]; 764 765 rRightColumn.push_back( ColumnItem( pMatrix ) ); 766 767 // update nCurY 768 rCurY = aRadioRect.origin.y - 5; 769 770 [pProto release]; 771} 772 773static void addList( NSView* pCurParent, long& rCurX, long& rCurY, long /*nAttachOffset*/, 774 const OUString& rText, 775 const OUString& rProperty, Sequence<OUString> const & rChoices, sal_Int32 nSelectValue, 776 std::vector<ColumnItem >& rLeftColumn, 777 std::vector<ColumnItem >& rRightColumn, 778 ControllerProperties* pControllerProperties, 779 ControlTarget* pCtrlTarget 780 ) 781{ 782 // don't indent attached lists, looks bad in the existing cases 783 NSControl* pTextView = createLabel( rText ); 784 [pCurParent addSubview: [pTextView autorelease]]; 785 rLeftColumn.push_back( ColumnItem( pTextView ) ); 786 NSRect aTextRect = [pTextView frame]; 787 aTextRect.origin.x = rCurX /* + nAttachOffset*/; 788 789 // don't indent attached lists, looks bad in the existing cases 790 NSRect aBtnRect = { { rCurX /*+ nAttachOffset*/ + aTextRect.size.width, 0 }, { 0, 15 } }; 791 NSPopUpButton* pBtn = [[NSPopUpButton alloc] initWithFrame: aBtnRect pullsDown: NO]; 792 793 // iterate options 794 for( sal_Int32 m = 0; m < rChoices.getLength(); m++ ) 795 { 796 NSString* pItemText = CreateNSString( rChoices[m] ); 797 [pBtn addItemWithTitle: pItemText]; 798 NSMenuItem* pItem = [pBtn itemWithTitle: pItemText]; 799 int nTag = pControllerProperties->addNameAndValueTag( rProperty, m ); 800 [pItem setTag: nTag]; 801 [pItemText release]; 802 } 803 804 [pBtn selectItemAtIndex: nSelectValue]; 805 806 // add the button to observed controls for enabled state changes 807 // also add a tag just for this purpose 808 pControllerProperties->addObservedControl( pBtn ); 809 [pBtn setTag: pControllerProperties->addNameTag( rProperty )]; 810 811 [pBtn sizeToFit]; 812 [pCurParent addSubview: [pBtn autorelease]]; 813 814 rRightColumn.push_back( ColumnItem( pBtn ) ); 815 816 // connect target and action 817 [pBtn setTarget: pCtrlTarget]; 818 [pBtn setAction: @selector(triggered:)]; 819 820 // move to nCurY 821 aBtnRect = [pBtn frame]; 822 aBtnRect.origin.y = rCurY - aBtnRect.size.height; 823 [pBtn setFrame: aBtnRect]; 824 825 // align label 826 aTextRect.origin.y = aBtnRect.origin.y + (aBtnRect.size.height - aTextRect.size.height)/2; 827 [pTextView setFrame: aTextRect]; 828 829 // update rCurY 830 rCurY = aBtnRect.origin.y - 5; 831} 832 833static void addEdit( NSView* pCurParent, long rCurX, long& rCurY, long nAttachOffset, 834 const OUString& rCtrlType, 835 const OUString& rText, 836 const OUString& rProperty, const PropertyValue* pValue, 837 sal_Int64 nMinValue, sal_Int64 nMaxValue, 838 std::vector<ColumnItem >& rLeftColumn, 839 std::vector<ColumnItem >& rRightColumn, 840 ControllerProperties* pControllerProperties, 841 ControlTarget* pCtrlTarget 842 ) 843{ 844 sal_Int32 nOff = 0; 845 if( rText.getLength() ) 846 { 847 // add a label 848 NSControl* pTextView = createLabel( rText ); 849 [pCurParent addSubview: [pTextView autorelease]]; 850 851 rLeftColumn.push_back( ColumnItem( pTextView ) ); 852 853 // move to nCurY 854 NSRect aTextRect = [pTextView frame]; 855 aTextRect.origin.x = rCurX + nAttachOffset; 856 aTextRect.origin.y = rCurY - aTextRect.size.height; 857 [pTextView setFrame: aTextRect]; 858 859 // update nCurY 860 rCurY = aTextRect.origin.y - 5; 861 862 // and set the offset for the real edit field 863 nOff = aTextRect.size.width + 5; 864 } 865 866 NSRect aFieldRect = { { static_cast<CGFloat>(rCurX + nOff + nAttachOffset), 0 }, { 100, 25 } }; 867 NSTextField* pFieldView = [[NSTextField alloc] initWithFrame: aFieldRect]; 868 [pFieldView setEditable: YES]; 869 [pFieldView setSelectable: YES]; 870 [pFieldView setDrawsBackground: YES]; 871 [pFieldView sizeToFit]; // FIXME: this does nothing 872 [pCurParent addSubview: [pFieldView autorelease]]; 873 874 rRightColumn.push_back( ColumnItem( pFieldView ) ); 875 876 // add the field to observed controls for enabled state changes 877 // also add a tag just for this purpose 878 pControllerProperties->addObservedControl( pFieldView ); 879 int nTag = pControllerProperties->addNameTag( rProperty ); 880 [pFieldView setTag: nTag]; 881 // pControllerProperties->addNamedView( pFieldView, aPropertyName ); 882 883 // move to nCurY 884 aFieldRect.origin.y = rCurY - aFieldRect.size.height; 885 [pFieldView setFrame: aFieldRect]; 886 887 if( rCtrlType == "Range" ) 888 { 889 // add a stepper control 890 NSRect aStepFrame = { { aFieldRect.origin.x + aFieldRect.size.width + 5, 891 aFieldRect.origin.y }, 892 { 15, aFieldRect.size.height } }; 893 NSStepper* pStep = [[NSStepper alloc] initWithFrame: aStepFrame]; 894 [pStep setIncrement: 1]; 895 [pStep setValueWraps: NO]; 896 [pStep setTag: nTag]; 897 [pCurParent addSubview: [pStep autorelease]]; 898 899 rRightColumn.back().pSubControl = pStep; 900 901 pControllerProperties->addObservedControl( pStep ); 902 [pStep setTarget: pCtrlTarget]; 903 [pStep setAction: @selector(triggered:)]; 904 905 // constrain the text field to decimal numbers 906 NSNumberFormatter* pFormatter = [[NSNumberFormatter alloc] init]; 907 [pFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4]; 908 [pFormatter setNumberStyle: NSNumberFormatterDecimalStyle]; 909 [pFormatter setAllowsFloats: NO]; 910 [pFormatter setMaximumFractionDigits: 0]; 911 if( nMinValue != nMaxValue ) 912 { 913 [pFormatter setMinimum: [[NSNumber numberWithInt: nMinValue] autorelease]]; 914 [pStep setMinValue: nMinValue]; 915 [pFormatter setMaximum: [[NSNumber numberWithInt: nMaxValue] autorelease]]; 916 [pStep setMaxValue: nMaxValue]; 917 } 918 [pFieldView setFormatter: pFormatter]; 919 920 sal_Int64 nSelectVal = 0; 921 if( pValue && pValue->Value.hasValue() ) 922 pValue->Value >>= nSelectVal; 923 924 [pFieldView setIntValue: nSelectVal]; 925 [pStep setIntValue: nSelectVal]; 926 927 pControllerProperties->addViewPair( pFieldView, pStep ); 928 // connect target and action 929 [pFieldView setTarget: pCtrlTarget]; 930 [pFieldView setAction: @selector(triggeredNumeric:)]; 931 [pStep setTarget: pCtrlTarget]; 932 [pStep setAction: @selector(triggeredNumeric:)]; 933 } 934 else 935 { 936 // connect target and action 937 [pFieldView setTarget: pCtrlTarget]; 938 [pFieldView setAction: @selector(triggered:)]; 939 940 if( pValue && pValue->Value.hasValue() ) 941 { 942 OUString aValue; 943 pValue->Value >>= aValue; 944 if( aValue.getLength() ) 945 { 946 NSString* pText = CreateNSString( aValue ); 947 [pFieldView setStringValue: pText]; 948 [pText release]; 949 } 950 } 951 } 952 953 // update nCurY 954 rCurY = aFieldRect.origin.y - 5; 955} 956 957@implementation AquaPrintAccessoryView 958 959+(NSObject*)setupPrinterPanel: (NSPrintOperation*)pOp 960 withController: (vcl::PrinterController*)pController 961 withState: (PrintAccessoryViewState*)pState 962{ 963 const Sequence< PropertyValue >& rOptions( pController->getUIOptions() ); 964 if( rOptions.getLength() == 0 ) 965 return nil; 966 967 NSRect aViewFrame = { NSZeroPoint, { 600, 400 } }; 968 NSRect aTabViewFrame = aViewFrame; 969 970 NSView* pAccessoryView = [[NSView alloc] initWithFrame: aViewFrame]; 971 NSTabView* pTabView = [[NSTabView alloc] initWithFrame: aTabViewFrame]; 972 [pAccessoryView addSubview: [pTabView autorelease]]; 973 974 // create the accessory controller 975 AquaPrintPanelAccessoryController* pAccessoryController = 976 [[AquaPrintPanelAccessoryController alloc] initWithNibName: nil bundle: nil]; 977 [pAccessoryController setView: [pAccessoryView autorelease]]; 978 [pAccessoryController forPrintOperation: pOp]; 979 [pAccessoryController withPrinterController: pController]; 980 [pAccessoryController withViewState: pState]; 981 982 NSView* pCurParent = nullptr; 983 long nCurY = 0; 984 long nCurX = 0; 985 NSSize aMaxTabSize = NSZeroSize; 986 987 ControllerProperties* pControllerProperties = new ControllerProperties( pAccessoryController ); 988 ControlTarget* pCtrlTarget = [[ControlTarget alloc] initWithControllerMap: pControllerProperties]; 989 990 std::vector< ColumnItem > aLeftColumn, aRightColumn; 991 992 // ugly: 993 // prepend a "selection" checkbox if the properties have such a selection in PrintContent 994 bool bAddSelectionCheckBox = false, bSelectionBoxEnabled = false, bSelectionBoxChecked = false; 995 996 for( int i = 0; i < rOptions.getLength(); i++ ) 997 { 998 Sequence< beans::PropertyValue > aOptProp; 999 rOptions[i].Value >>= aOptProp; 1000 1001 OUString aCtrlType; 1002 OUString aPropertyName; 1003 Sequence< OUString > aChoices; 1004 Sequence< sal_Bool > aChoicesDisabled; 1005 sal_Int32 aSelectionChecked = 0; 1006 for( int n = 0; n < aOptProp.getLength(); n++ ) 1007 { 1008 const beans::PropertyValue& rEntry( aOptProp[ n ] ); 1009 if( rEntry.Name == "ControlType" ) 1010 { 1011 rEntry.Value >>= aCtrlType; 1012 } 1013 else if( rEntry.Name == "Choices" ) 1014 { 1015 rEntry.Value >>= aChoices; 1016 } 1017 else if( rEntry.Name == "ChoicesDisabled" ) 1018 { 1019 rEntry.Value >>= aChoicesDisabled; 1020 } 1021 else if( rEntry.Name == "Property" ) 1022 { 1023 PropertyValue aVal; 1024 rEntry.Value >>= aVal; 1025 aPropertyName = aVal.Name; 1026 if( aPropertyName == "PrintContent" ) 1027 aVal.Value >>= aSelectionChecked; 1028 } 1029 } 1030 if( aCtrlType == "Radio" && 1031 aPropertyName == "PrintContent" && 1032 aChoices.getLength() > 2 ) 1033 { 1034 bAddSelectionCheckBox = true; 1035 bSelectionBoxEnabled = aChoicesDisabled.getLength() < 2 || ! aChoicesDisabled[2]; 1036 bSelectionBoxChecked = (aSelectionChecked==2); 1037 break; 1038 } 1039 } 1040 1041 for( int i = 0; i < rOptions.getLength(); i++ ) 1042 { 1043 Sequence< beans::PropertyValue > aOptProp; 1044 rOptions[i].Value >>= aOptProp; 1045 1046 // extract ui element 1047 OUString aCtrlType; 1048 OUString aText; 1049 OUString aPropertyName; 1050 OUString aGroupHint; 1051 Sequence< OUString > aChoices; 1052 bool bEnabled = true; 1053 sal_Int64 nMinValue = 0, nMaxValue = 0; 1054 long nAttachOffset = 0; 1055 bool bIgnore = false; 1056 1057 for( int n = 0; n < aOptProp.getLength(); n++ ) 1058 { 1059 const beans::PropertyValue& rEntry( aOptProp[ n ] ); 1060 if( rEntry.Name == "Text" ) 1061 { 1062 rEntry.Value >>= aText; 1063 aText = filterAccelerator( aText ); 1064 } 1065 else if( rEntry.Name == "ControlType" ) 1066 { 1067 rEntry.Value >>= aCtrlType; 1068 } 1069 else if( rEntry.Name == "Choices" ) 1070 { 1071 rEntry.Value >>= aChoices; 1072 } 1073 else if( rEntry.Name == "Property" ) 1074 { 1075 PropertyValue aVal; 1076 rEntry.Value >>= aVal; 1077 aPropertyName = aVal.Name; 1078 } 1079 else if( rEntry.Name == "Enabled" ) 1080 { 1081 bool bValue = true; 1082 rEntry.Value >>= bValue; 1083 bEnabled = bValue; 1084 } 1085 else if( rEntry.Name == "MinValue" ) 1086 { 1087 rEntry.Value >>= nMinValue; 1088 } 1089 else if( rEntry.Name == "MaxValue" ) 1090 { 1091 rEntry.Value >>= nMaxValue; 1092 } 1093 else if( rEntry.Name == "AttachToDependency" ) 1094 { 1095 nAttachOffset = 20; 1096 } 1097 else if( rEntry.Name == "InternalUIOnly" ) 1098 { 1099 bool bValue = false; 1100 rEntry.Value >>= bValue; 1101 bIgnore = bValue; 1102 } 1103 else if( rEntry.Name == "GroupingHint" ) 1104 { 1105 rEntry.Value >>= aGroupHint; 1106 } 1107 } 1108 1109 if( aCtrlType == "Group" || 1110 aCtrlType == "Subgroup" || 1111 aCtrlType == "Radio" || 1112 aCtrlType == "List" || 1113 aCtrlType == "Edit" || 1114 aCtrlType == "Range" || 1115 aCtrlType == "Bool" ) 1116 { 1117 bool bIgnoreSubgroup = false; 1118 1119 // with `setAccessoryView' method only one accessory view can be set 1120 // so create this single accessory view as tabbed for grouping 1121 if( aCtrlType == "Group" 1122 || ! pCurParent 1123 || ( aCtrlType == "Subgroup" && nCurY < -250 && ! bIgnore ) 1124 ) 1125 { 1126 OUString aGroupTitle( aText ); 1127 if( aCtrlType == "Subgroup" ) 1128 aGroupTitle = ControllerProperties::getMoreString(); 1129 1130 // set size of current parent 1131 if( pCurParent ) 1132 adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn ); 1133 1134 // new tab item 1135 if( ! aText.getLength() ) 1136 aText = "OOo"; 1137 NSString* pLabel = CreateNSString( aGroupTitle ); 1138 NSTabViewItem* pItem = [[NSTabViewItem alloc] initWithIdentifier: pLabel ]; 1139 [pItem setLabel: pLabel]; 1140 [pTabView addTabViewItem: pItem]; 1141 pCurParent = [[NSView alloc] initWithFrame: aTabViewFrame]; 1142 [pItem setView: pCurParent]; 1143 [pLabel release]; 1144 1145 nCurX = 20; // reset indent 1146 nCurY = 0; // reset Y 1147 // clear columns 1148 aLeftColumn.clear(); 1149 aRightColumn.clear(); 1150 1151 if( bAddSelectionCheckBox ) 1152 { 1153 addBool( pCurParent, nCurX, nCurY, 0, 1154 ControllerProperties::getPrintSelectionString(), bSelectionBoxEnabled, 1155 "PrintContent", bSelectionBoxChecked, 1156 aRightColumn, pControllerProperties, pCtrlTarget ); 1157 bAddSelectionCheckBox = false; 1158 } 1159 } 1160 1161 if( aCtrlType == "Subgroup" && pCurParent ) 1162 { 1163 bIgnoreSubgroup = bIgnore; 1164 if( bIgnore ) 1165 continue; 1166 1167 addSubgroup( pCurParent, nCurY, aText ); 1168 } 1169 else if( bIgnoreSubgroup || bIgnore ) 1170 { 1171 continue; 1172 } 1173 else if( aCtrlType == "Bool" && pCurParent ) 1174 { 1175 bool bVal = false; 1176 PropertyValue* pVal = pController->getValue( aPropertyName ); 1177 if( pVal ) 1178 pVal->Value >>= bVal; 1179 addBool( pCurParent, nCurX, nCurY, nAttachOffset, 1180 aText, true, aPropertyName, bVal, 1181 aRightColumn, pControllerProperties, pCtrlTarget ); 1182 } 1183 else if( aCtrlType == "Radio" && pCurParent ) 1184 { 1185 // get currently selected value 1186 sal_Int32 nSelectVal = 0; 1187 PropertyValue* pVal = pController->getValue( aPropertyName ); 1188 if( pVal && pVal->Value.hasValue() ) 1189 pVal->Value >>= nSelectVal; 1190 1191 addRadio( pCurParent, nCurX, nCurY, nAttachOffset, 1192 aText, aPropertyName, aChoices, nSelectVal, 1193 aLeftColumn, aRightColumn, 1194 pControllerProperties, pCtrlTarget ); 1195 } 1196 else if( aCtrlType == "List" && pCurParent ) 1197 { 1198 PropertyValue* pVal = pController->getValue( aPropertyName ); 1199 sal_Int32 aSelectVal = 0; 1200 if( pVal && pVal->Value.hasValue() ) 1201 pVal->Value >>= aSelectVal; 1202 1203 addList( pCurParent, nCurX, nCurY, nAttachOffset, 1204 aText, aPropertyName, aChoices, aSelectVal, 1205 aLeftColumn, aRightColumn, 1206 pControllerProperties, pCtrlTarget ); 1207 } 1208 else if( (aCtrlType == "Edit" 1209 || aCtrlType == "Range") && pCurParent ) 1210 { 1211 // current value 1212 PropertyValue* pVal = pController->getValue( aPropertyName ); 1213 addEdit( pCurParent, nCurX, nCurY, nAttachOffset, 1214 aCtrlType, aText, aPropertyName, pVal, 1215 nMinValue, nMaxValue, 1216 aLeftColumn, aRightColumn, 1217 pControllerProperties, pCtrlTarget ); 1218 } 1219 } 1220 else 1221 { 1222 SAL_INFO( "vcl.osx.print", "Unsupported UI option \"" << aCtrlType << "\""); 1223 } 1224 } 1225 1226 pControllerProperties->updateEnableState(); 1227 adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn ); 1228 1229 // now reposition everything again so it is upper bound 1230 adjustTabViews( pTabView, aMaxTabSize ); 1231 1232 // find the minimum needed tab size 1233 NSSize aTabCtrlSize = [pTabView minimumSize]; 1234 aTabCtrlSize.height += aMaxTabSize.height + 10; 1235 if( aTabCtrlSize.width < aMaxTabSize.width + 10 ) 1236 aTabCtrlSize.width = aMaxTabSize.width + 10; 1237 [pTabView setFrameSize: aTabCtrlSize]; 1238 aViewFrame.size.width = aTabCtrlSize.width + aTabViewFrame.origin.x; 1239 aViewFrame.size.height = aTabCtrlSize.height + aTabViewFrame.origin.y; 1240 [pAccessoryView setFrameSize: aViewFrame.size]; 1241 1242 // get the print panel 1243 NSPrintPanel* pPrintPanel = [pOp printPanel]; 1244 [pPrintPanel setOptions: [pPrintPanel options] | NSPrintPanelShowsPreview]; 1245 // add the accessory controller to the panel 1246 [pPrintPanel addAccessoryController: [pAccessoryController autorelease]]; 1247 1248 // set the current selected tab item 1249 if( pState->nLastPage >= 0 && pState->nLastPage < [pTabView numberOfTabViewItems] ) 1250 [pTabView selectTabViewItemAtIndex: pState->nLastPage]; 1251 1252 return pCtrlTarget; 1253} 1254 1255@end 1256 1257/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1258