1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
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>
31 #include <osx/printview.h>
32 #include <osx/salinst.h>
33 #include <quartz/utils.h>
36 #include <strings.hrc>
37 #include <printaccessoryview.hrc>
39 #include <com/sun/star/i18n/XBreakIterator.hpp>
40 #include <com/sun/star/i18n/WordType.hpp>
45 using namespace com::sun::star;
46 using namespace com::sun::star::beans;
47 using namespace com::sun::star::uno;
49 class ControllerProperties;
51 @interface ControlTarget : NSObject
53 ControllerProperties* mpController;
55 -(id)initWithControllerMap: (ControllerProperties*)pController;
56 -(void)triggered:(id)pSender;
57 -(void)triggeredNumeric:(id)pSender;
61 @interface AquaPrintPanelAccessoryController : NSViewController< NSPrintPanelAccessorizing >
63 NSPrintOperation *mpPrintOperation;
64 vcl::PrinterController *mpPrinterController;
65 PrintAccessoryViewState *mpViewState;
68 -(void)forPrintOperation:(NSPrintOperation*)pPrintOp;
69 -(void)withPrinterController:(vcl::PrinterController*)pController;
70 -(void)withViewState:(PrintAccessoryViewState*)pState;
72 -(NSPrintOperation*)printOperation;
73 -(vcl::PrinterController*)printerController;
74 -(PrintAccessoryViewState*)viewState;
76 -(NSSet*)keyPathsForValuesAffectingPreview;
77 -(NSArray*)localizedSummaryItems;
79 -(sal_Int32)updatePrintOperation:(sal_Int32)pLastPageCount;
83 @implementation AquaPrintPanelAccessoryController
85 -(void)forPrintOperation:(NSPrintOperation*)pPrintOp
86 { mpPrintOperation = pPrintOp; }
88 -(void)withPrinterController:(vcl::PrinterController*)pController
89 { mpPrinterController = pController; }
91 -(void)withViewState:(PrintAccessoryViewState*)pState
92 { mpViewState = pState; }
94 -(NSPrintOperation*)printOperation
95 { return mpPrintOperation; }
97 -(vcl::PrinterController*)printerController
98 { return mpPrinterController; }
100 -(PrintAccessoryViewState*)viewState
101 { return mpViewState; }
103 -(NSSet*)keyPathsForValuesAffectingPreview
105 return [ NSSet setWithObject:@"updatePrintOperation" ];
108 -(NSArray*)localizedSummaryItems
110 return [ NSArray arrayWithObject:
111 [ NSDictionary dictionary ] ];
114 -(sal_Int32)updatePrintOperation:(sal_Int32)pLastPageCount
116 // page range may be changed by option choice
117 sal_Int32 nPages = mpPrinterController->getFilteredPageCount();
119 mpViewState->bNeedRestart = false;
120 if( nPages != pLastPageCount )
122 #if OSL_DEBUG_LEVEL > 1
123 SAL_INFO( "vcl.osx.print", "number of pages changed" <<
124 " from " << pLastPageCount << " to " << nPages );
126 mpViewState->bNeedRestart = true;
129 NSTabView* pTabView = [[[self view] subviews] objectAtIndex:0];
130 NSTabViewItem* pItem = [pTabView selectedTabViewItem];
132 mpViewState->nLastPage = [pTabView indexOfTabViewItem: pItem];
134 mpViewState->nLastPage = 0;
136 if( mpViewState->bNeedRestart )
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];
143 [pNSWindow cancelOperation: nil];
144 [[mpPrintOperation printInfo] setJobDisposition: NSPrintCancelJob];
152 class ControllerProperties
154 std::map< int, OUString > maTagToPropertyName;
155 std::map< int, sal_Int32 > maTagToValueInt;
156 std::map< NSView*, NSView* > maViewPairMap;
157 std::vector< NSObject* > maViews;
159 sal_Int32 mnLastPageCount;
160 AquaPrintPanelAccessoryController* mpAccessoryController;
163 ControllerProperties( AquaPrintPanelAccessoryController* i_pAccessoryController )
165 , mnLastPageCount( [i_pAccessoryController printerController]->getFilteredPageCount() )
166 , mpAccessoryController( i_pAccessoryController )
168 static_assert( SAL_N_ELEMENTS(SV_PRINT_NATIVE_STRINGS) == 5, "resources not found" );
171 static OUString getMoreString()
173 return VclResId(SV_PRINT_NATIVE_STRINGS[3]);
176 static OUString getPrintSelectionString()
178 return VclResId(SV_PRINT_NATIVE_STRINGS[4]);
181 int addNameTag( const OUString& i_rPropertyName )
183 int nNewTag = mnNextTag++;
184 maTagToPropertyName[ nNewTag ] = i_rPropertyName;
188 int addNameAndValueTag( const OUString& i_rPropertyName, sal_Int32 i_nValue )
190 int nNewTag = mnNextTag++;
191 maTagToPropertyName[ nNewTag ] = i_rPropertyName;
192 maTagToValueInt[ nNewTag ] = i_nValue;
196 void addObservedControl( NSObject* i_pView )
198 maViews.push_back( i_pView );
201 void addViewPair( NSView* i_pLeft, NSView* i_pRight )
203 maViewPairMap[ i_pLeft ] = i_pRight;
204 maViewPairMap[ i_pRight ] = i_pLeft;
207 NSView* getPair( NSView* i_pLeft ) const
209 NSView* pRight = nil;
210 std::map< NSView*, NSView* >::const_iterator it = maViewPairMap.find( i_pLeft );
211 if( it != maViewPairMap.end() )
216 void changePropertyWithIntValue( int i_nTag )
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() )
222 vcl::PrinterController * mpController = [mpAccessoryController printerController];
223 PropertyValue* pVal = mpController->getValue( name_it->second );
226 pVal->Value <<= value_it->second;
227 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount];
232 void changePropertyWithIntValue( int i_nTag, sal_Int64 i_nValue )
234 std::map< int, OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
235 if( name_it != maTagToPropertyName.end() )
237 vcl::PrinterController * mpController = [mpAccessoryController printerController];
238 PropertyValue* pVal = mpController->getValue( name_it->second );
241 pVal->Value <<= i_nValue;
242 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount];
247 void changePropertyWithBoolValue( int i_nTag, bool i_bValue )
249 std::map< int, OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
250 if( name_it != maTagToPropertyName.end() )
252 vcl::PrinterController * mpController = [mpAccessoryController printerController];
253 PropertyValue* pVal = mpController->getValue( name_it->second );
257 if( name_it->second == "PrintContent" )
258 pVal->Value <<= i_bValue ? sal_Int32(2) : sal_Int32(0);
260 pVal->Value <<= i_bValue;
262 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount];
267 void changePropertyWithStringValue( int i_nTag, const OUString& i_rValue )
269 std::map< int, OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
270 if( name_it != maTagToPropertyName.end() )
272 vcl::PrinterController * mpController = [mpAccessoryController printerController];
273 PropertyValue* pVal = mpController->getValue( name_it->second );
276 pVal->Value <<= i_rValue;
277 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount];
282 void updateEnableState()
284 for( std::vector< NSObject* >::iterator it = maViews.begin(); it != maViews.end(); ++it )
286 NSObject* pObj = *it;
287 NSControl* pCtrl = 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);
294 int nTag = pCtrl ? [pCtrl tag] :
295 pCell ? [pCell tag] :
298 std::map< int, OUString >::const_iterator name_it = maTagToPropertyName.find( nTag );
299 if( name_it != maTagToPropertyName.end() && name_it->second != "PrintContent" )
301 vcl::PrinterController * mpController = [mpAccessoryController printerController];
302 BOOL bEnabled = mpController->isUIOptionEnabled( name_it->second ) ? YES : NO;
305 [pCtrl setEnabled: bEnabled];
306 NSView* pOther = getPair( pCtrl );
307 if( pOther && [pOther isKindOfClass: [NSControl class]] )
308 [static_cast<NSControl*>(pOther) setEnabled: bEnabled];
311 [pCell setEnabled: bEnabled];
318 static OUString filterAccelerator( OUString const & rText )
320 OUStringBuffer aBuf( rText.getLength() );
321 for( sal_Int32 nIndex = 0; nIndex != -1; )
322 aBuf.append( rText.getToken( 0, '~', nIndex ) );
323 return aBuf.makeStringAndClear();
326 @implementation ControlTarget
328 -(id)initWithControllerMap: (ControllerProperties*)pController
330 if( (self = [super init]) )
332 mpController = pController;
337 -(void)triggered:(id)pSender
339 if( [pSender isMemberOfClass: [NSPopUpButton class]] )
341 NSPopUpButton* pBtn = static_cast<NSPopUpButton*>(pSender);
342 NSMenuItem* pSelected = [pBtn selectedItem];
345 int nTag = [pSelected tag];
346 mpController->changePropertyWithIntValue( nTag );
349 else if( [pSender isMemberOfClass: [NSButton class]] )
351 NSButton* pBtn = static_cast<NSButton*>(pSender);
352 int nTag = [pBtn tag];
353 mpController->changePropertyWithBoolValue( nTag, [pBtn state] == NSControlStateValueOn );
355 else if( [pSender isMemberOfClass: [NSMatrix class]] )
357 NSObject* pObj = [static_cast<NSMatrix*>(pSender) selectedCell];
358 if( [pObj isMemberOfClass: [NSButtonCell class]] )
360 NSButtonCell* pCell = static_cast<NSButtonCell*>(pObj);
361 int nTag = [pCell tag];
362 mpController->changePropertyWithIntValue( nTag );
365 else if( [pSender isMemberOfClass: [NSTextField class]] )
367 NSTextField* pField = static_cast<NSTextField*>(pSender);
368 int nTag = [pField tag];
369 OUString aValue = GetOUString( [pSender stringValue] );
370 mpController->changePropertyWithStringValue( nTag, aValue );
374 SAL_INFO( "vcl.osx.print", "Unsupported class" <<
375 ( [pSender class] ? [NSStringFromClass([pSender class]) UTF8String] : "nil" ) );
377 mpController->updateEnableState();
380 -(void)triggeredNumeric:(id)pSender
382 if( [pSender isMemberOfClass: [NSTextField class]] )
384 NSTextField* pField = static_cast<NSTextField*>(pSender);
385 int nTag = [pField tag];
386 sal_Int64 nValue = [pField intValue];
388 NSView* pOther = mpController->getPair( pField );
390 [static_cast<NSControl*>(pOther) setIntValue: nValue];
392 mpController->changePropertyWithIntValue( nTag, nValue );
394 else if( [pSender isMemberOfClass: [NSStepper class]] )
396 NSStepper* pStep = static_cast<NSStepper*>(pSender);
397 int nTag = [pStep tag];
398 sal_Int64 nValue = [pStep intValue];
400 NSView* pOther = mpController->getPair( pStep );
402 [static_cast<NSControl*>(pOther) setIntValue: nValue];
404 mpController->changePropertyWithIntValue( nTag, nValue );
408 SAL_INFO( "vcl.osx.print", "Unsupported class" <<
409 ([pSender class] ? [NSStringFromClass([pSender class]) UTF8String] : "nil") );
411 mpController->updateEnableState();
426 NSControl* pSubControl;
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 )
434 long getWidth() const
439 NSRect aCtrlRect = [pControl frame];
440 nWidth = aCtrlRect.size.width;
444 NSRect aSubRect = [pSubControl frame];
445 nWidth += aSubRect.size.width;
446 nWidth += aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width);
453 static void adjustViewAndChildren( NSView* pNSView, NSSize& rMaxSize,
454 std::vector< ColumnItem >& rLeftColumn,
455 std::vector< ColumnItem >& rRightColumn
460 // first get overall column widths
462 long nRightWidth = 0;
463 for( size_t i = 0; i < rLeftColumn.size(); i++ )
465 long nW = rLeftColumn[i].getWidth();
466 if( nW > nLeftWidth )
469 for( size_t i = 0; i < rRightColumn.size(); i++ )
471 long nW = rRightColumn[i].getWidth();
472 if( nW > nRightWidth )
476 // right align left column
477 for( size_t i = 0; i < rLeftColumn.size(); i++ )
479 if( rLeftColumn[i].pControl )
481 NSRect aCtrlRect = [rLeftColumn[i].pControl frame];
482 long nX = nLeftWidth - aCtrlRect.size.width;
483 if( rLeftColumn[i].pSubControl )
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];
490 aCtrlRect.origin.x = nX;
491 [rLeftColumn[i].pControl setFrame: aCtrlRect];
495 // left align right column
496 for( size_t i = 0; i < rRightColumn.size(); i++ )
498 if( rRightColumn[i].pControl )
500 NSRect aCtrlRect = [rRightColumn[i].pControl frame];
501 long nX = nLeftWidth + 3;
502 if( rRightColumn[i].pSubControl )
504 NSRect aSubRect = [rRightColumn[i].pSubControl frame];
505 aSubRect.origin.x = nX + aSubRect.origin.x - aCtrlRect.origin.x;
506 [rRightColumn[i].pSubControl setFrame: aSubRect];
508 aCtrlRect.origin.x = nX;
509 [rRightColumn[i].pControl setFrame: aCtrlRect];
513 NSArray* pSubViews = [pNSView subviews];
514 unsigned int nViews = [pSubViews count];
515 NSRect aUnion = NSZeroRect;
517 // get the combined frame of all subviews
518 for( unsigned int n = 0; n < nViews; n++ )
520 aUnion = NSUnionRect( aUnion, [[pSubViews objectAtIndex: n] frame] );
523 // move everything so it will fit
524 for( unsigned int n = 0; n < nViews; n++ )
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];
533 // resize the view itself
534 aUnion.size.height += 10;
535 aUnion.size.width += 20;
536 [pNSView setFrameSize: aUnion.size];
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;
544 static void adjustTabViews( NSTabView* pTabView, NSSize aTabSize )
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++ )
551 NSTabViewItem* pItem = static_cast<NSTabViewItem*>([pTabbedViews objectAtIndex: i]);
552 NSView* pNSView = [pItem view];
555 NSRect aRect = [pNSView frame];
556 double nDiff = aTabSize.height - aRect.size.height;
557 aRect.size = aTabSize;
558 [pNSView setFrame: aRect];
560 NSArray* pSubViews = [pNSView subviews];
561 unsigned int nSubViews = [pSubViews count];
563 // move everything up
564 for( unsigned int n = 0; n < nSubViews; n++ )
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]] )
573 aFrame.size.width = aTabSize.width - aFrame.origin.x - 10;
575 [pCurSubView setFrame: aFrame];
581 static NSControl* createLabel( const OUString& i_rText )
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];
597 static sal_Int32 findBreak( const OUString& i_rText, sal_Int32 i_nPos )
599 sal_Int32 nRet = i_rText.getLength();
600 Reference< i18n::XBreakIterator > xBI( vcl::unohelper::CreateBreakIterator() );
603 i18n::Boundary aBoundary =
604 xBI->getWordBoundary( i_rText, i_nPos,
605 Application::GetSettings().GetLanguageTag().getLocale(),
606 i18n::WordType::ANYWORD_IGNOREWHITESPACES,
608 nRet = aBoundary.endPos;
613 static void linebreakCell( NSCell* pBtn, const OUString& i_rText )
615 NSString* pText = CreateNSString( i_rText );
616 [pBtn setTitle: pText];
618 NSSize aSize = [pBtn cellSize];
619 if( aSize.width > 280 )
622 sal_Int32 nLen = i_rText.getLength();
623 sal_Int32 nIndex = nLen / 2;
624 nIndex = findBreak( i_rText, nIndex );
627 OUStringBuffer aBuf( i_rText );
629 pText = CreateNSString( aBuf.makeStringAndClear() );
630 [pBtn setTitle: pText];
636 static void addSubgroup( NSView* pCurParent, long& rCurY, const OUString& rText )
638 NSControl* pTextView = createLabel( rText );
639 [pCurParent addSubview: [pTextView autorelease]];
640 NSRect aTextRect = [pTextView frame];
642 aTextRect.origin.y = rCurY - aTextRect.size.height;
643 [pTextView setFrame: aTextRect];
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]];
651 rCurY = aTextRect.origin.y - 5;
654 static 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
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];
667 [pBtn setEnabled: NO];
668 linebreakCell( [pBtn cell], rText );
671 rRightColumn.push_back( ColumnItem( pBtn ) );
674 [pBtn setTarget: pCtrlTarget];
675 [pBtn setAction: @selector(triggered:)];
676 int nTag = pControllerProperties->addNameTag( rProperty );
677 pControllerProperties->addObservedControl( pBtn );
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
685 aCheckRect.size.width += 1;
688 aCheckRect.origin.y = rCurY - aCheckRect.size.height;
689 [pBtn setFrame: aCheckRect];
691 [pCurParent addSubview: [pBtn autorelease]];
694 rCurY = aCheckRect.origin.y - 5;
697 static 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
707 if( rText.getLength() )
710 NSControl* pTextView = createLabel( rText );
711 NSRect aTextRect = [pTextView frame];
712 aTextRect.origin.x = rCurX + nAttachOffset;
713 [pCurParent addSubview: [pTextView autorelease]];
715 rLeftColumn.push_back( ColumnItem( pTextView ) );
718 aTextRect.origin.y = rCurY - aTextRect.size.height;
719 [pTextView setFrame: aTextRect];
722 rCurY = aTextRect.origin.y - 5;
724 // indent the radio group relative to the text
728 // setup radio matrix
729 NSButtonCell* pProto = [[NSButtonCell alloc] init];
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()
741 // set individual titles
742 NSArray* pCells = [pMatrix cells];
743 for( sal_Int32 m = 0; m < rChoices.getLength(); m++ )
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];
758 aRadioRect = [pMatrix frame];
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]];
765 rRightColumn.push_back( ColumnItem( pMatrix ) );
768 rCurY = aRadioRect.origin.y - 5;
773 static 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
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*/;
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];
794 for( sal_Int32 m = 0; m < rChoices.getLength(); m++ )
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];
804 [pBtn selectItemAtIndex: nSelectValue];
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 )];
812 [pCurParent addSubview: [pBtn autorelease]];
814 rRightColumn.push_back( ColumnItem( pBtn ) );
816 // connect target and action
817 [pBtn setTarget: pCtrlTarget];
818 [pBtn setAction: @selector(triggered:)];
821 aBtnRect = [pBtn frame];
822 aBtnRect.origin.y = rCurY - aBtnRect.size.height;
823 [pBtn setFrame: aBtnRect];
826 aTextRect.origin.y = aBtnRect.origin.y + (aBtnRect.size.height - aTextRect.size.height)/2;
827 [pTextView setFrame: aTextRect];
830 rCurY = aBtnRect.origin.y - 5;
833 static 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
845 if( rText.getLength() )
848 NSControl* pTextView = createLabel( rText );
849 [pCurParent addSubview: [pTextView autorelease]];
851 rLeftColumn.push_back( ColumnItem( pTextView ) );
854 NSRect aTextRect = [pTextView frame];
855 aTextRect.origin.x = rCurX + nAttachOffset;
856 aTextRect.origin.y = rCurY - aTextRect.size.height;
857 [pTextView setFrame: aTextRect];
860 rCurY = aTextRect.origin.y - 5;
862 // and set the offset for the real edit field
863 nOff = aTextRect.size.width + 5;
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]];
874 rRightColumn.push_back( ColumnItem( pFieldView ) );
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 );
884 aFieldRect.origin.y = rCurY - aFieldRect.size.height;
885 [pFieldView setFrame: aFieldRect];
887 if( rCtrlType == "Range" )
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]];
899 rRightColumn.back().pSubControl = pStep;
901 pControllerProperties->addObservedControl( pStep );
902 [pStep setTarget: pCtrlTarget];
903 [pStep setAction: @selector(triggered:)];
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 )
913 [pFormatter setMinimum: [[NSNumber numberWithInt: nMinValue] autorelease]];
914 [pStep setMinValue: nMinValue];
915 [pFormatter setMaximum: [[NSNumber numberWithInt: nMaxValue] autorelease]];
916 [pStep setMaxValue: nMaxValue];
918 [pFieldView setFormatter: pFormatter];
920 sal_Int64 nSelectVal = 0;
921 if( pValue && pValue->Value.hasValue() )
922 pValue->Value >>= nSelectVal;
924 [pFieldView setIntValue: nSelectVal];
925 [pStep setIntValue: nSelectVal];
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:)];
936 // connect target and action
937 [pFieldView setTarget: pCtrlTarget];
938 [pFieldView setAction: @selector(triggered:)];
940 if( pValue && pValue->Value.hasValue() )
943 pValue->Value >>= aValue;
944 if( aValue.getLength() )
946 NSString* pText = CreateNSString( aValue );
947 [pFieldView setStringValue: pText];
954 rCurY = aFieldRect.origin.y - 5;
957 @implementation AquaPrintAccessoryView
959 +(NSObject*)setupPrinterPanel: (NSPrintOperation*)pOp
960 withController: (vcl::PrinterController*)pController
961 withState: (PrintAccessoryViewState*)pState
963 const Sequence< PropertyValue >& rOptions( pController->getUIOptions() );
964 if( rOptions.getLength() == 0 )
967 NSRect aViewFrame = { NSZeroPoint, { 600, 400 } };
968 NSRect aTabViewFrame = aViewFrame;
970 NSView* pAccessoryView = [[NSView alloc] initWithFrame: aViewFrame];
971 NSTabView* pTabView = [[NSTabView alloc] initWithFrame: aTabViewFrame];
972 [pAccessoryView addSubview: [pTabView autorelease]];
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];
982 NSView* pCurParent = nullptr;
985 NSSize aMaxTabSize = NSZeroSize;
987 ControllerProperties* pControllerProperties = new ControllerProperties( pAccessoryController );
988 ControlTarget* pCtrlTarget = [[ControlTarget alloc] initWithControllerMap: pControllerProperties];
990 std::vector< ColumnItem > aLeftColumn, aRightColumn;
993 // prepend a "selection" checkbox if the properties have such a selection in PrintContent
994 bool bAddSelectionCheckBox = false, bSelectionBoxEnabled = false, bSelectionBoxChecked = false;
996 for( int i = 0; i < rOptions.getLength(); i++ )
998 Sequence< beans::PropertyValue > aOptProp;
999 rOptions[i].Value >>= aOptProp;
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++ )
1008 const beans::PropertyValue& rEntry( aOptProp[ n ] );
1009 if( rEntry.Name == "ControlType" )
1011 rEntry.Value >>= aCtrlType;
1013 else if( rEntry.Name == "Choices" )
1015 rEntry.Value >>= aChoices;
1017 else if( rEntry.Name == "ChoicesDisabled" )
1019 rEntry.Value >>= aChoicesDisabled;
1021 else if( rEntry.Name == "Property" )
1024 rEntry.Value >>= aVal;
1025 aPropertyName = aVal.Name;
1026 if( aPropertyName == "PrintContent" )
1027 aVal.Value >>= aSelectionChecked;
1030 if( aCtrlType == "Radio" &&
1031 aPropertyName == "PrintContent" &&
1032 aChoices.getLength() > 2 )
1034 bAddSelectionCheckBox = true;
1035 bSelectionBoxEnabled = aChoicesDisabled.getLength() < 2 || ! aChoicesDisabled[2];
1036 bSelectionBoxChecked = (aSelectionChecked==2);
1041 for( int i = 0; i < rOptions.getLength(); i++ )
1043 Sequence< beans::PropertyValue > aOptProp;
1044 rOptions[i].Value >>= aOptProp;
1046 // extract ui element
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;
1057 for( int n = 0; n < aOptProp.getLength(); n++ )
1059 const beans::PropertyValue& rEntry( aOptProp[ n ] );
1060 if( rEntry.Name == "Text" )
1062 rEntry.Value >>= aText;
1063 aText = filterAccelerator( aText );
1065 else if( rEntry.Name == "ControlType" )
1067 rEntry.Value >>= aCtrlType;
1069 else if( rEntry.Name == "Choices" )
1071 rEntry.Value >>= aChoices;
1073 else if( rEntry.Name == "Property" )
1076 rEntry.Value >>= aVal;
1077 aPropertyName = aVal.Name;
1079 else if( rEntry.Name == "Enabled" )
1082 rEntry.Value >>= bValue;
1085 else if( rEntry.Name == "MinValue" )
1087 rEntry.Value >>= nMinValue;
1089 else if( rEntry.Name == "MaxValue" )
1091 rEntry.Value >>= nMaxValue;
1093 else if( rEntry.Name == "AttachToDependency" )
1097 else if( rEntry.Name == "InternalUIOnly" )
1099 bool bValue = false;
1100 rEntry.Value >>= bValue;
1103 else if( rEntry.Name == "GroupingHint" )
1105 rEntry.Value >>= aGroupHint;
1109 if( aCtrlType == "Group" ||
1110 aCtrlType == "Subgroup" ||
1111 aCtrlType == "Radio" ||
1112 aCtrlType == "List" ||
1113 aCtrlType == "Edit" ||
1114 aCtrlType == "Range" ||
1115 aCtrlType == "Bool" )
1117 bool bIgnoreSubgroup = false;
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"
1123 || ( aCtrlType == "Subgroup" && nCurY < -250 && ! bIgnore )
1126 OUString aGroupTitle( aText );
1127 if( aCtrlType == "Subgroup" )
1128 aGroupTitle = ControllerProperties::getMoreString();
1130 // set size of current parent
1132 adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn );
1135 if( ! aText.getLength() )
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];
1145 nCurX = 20; // reset indent
1146 nCurY = 0; // reset Y
1148 aLeftColumn.clear();
1149 aRightColumn.clear();
1151 if( bAddSelectionCheckBox )
1153 addBool( pCurParent, nCurX, nCurY, 0,
1154 ControllerProperties::getPrintSelectionString(), bSelectionBoxEnabled,
1155 "PrintContent", bSelectionBoxChecked,
1156 aRightColumn, pControllerProperties, pCtrlTarget );
1157 bAddSelectionCheckBox = false;
1161 if( aCtrlType == "Subgroup" && pCurParent )
1163 bIgnoreSubgroup = bIgnore;
1167 addSubgroup( pCurParent, nCurY, aText );
1169 else if( bIgnoreSubgroup || bIgnore )
1173 else if( aCtrlType == "Bool" && pCurParent )
1176 PropertyValue* pVal = pController->getValue( aPropertyName );
1178 pVal->Value >>= bVal;
1179 addBool( pCurParent, nCurX, nCurY, nAttachOffset,
1180 aText, true, aPropertyName, bVal,
1181 aRightColumn, pControllerProperties, pCtrlTarget );
1183 else if( aCtrlType == "Radio" && pCurParent )
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;
1191 addRadio( pCurParent, nCurX, nCurY, nAttachOffset,
1192 aText, aPropertyName, aChoices, nSelectVal,
1193 aLeftColumn, aRightColumn,
1194 pControllerProperties, pCtrlTarget );
1196 else if( aCtrlType == "List" && pCurParent )
1198 PropertyValue* pVal = pController->getValue( aPropertyName );
1199 sal_Int32 aSelectVal = 0;
1200 if( pVal && pVal->Value.hasValue() )
1201 pVal->Value >>= aSelectVal;
1203 addList( pCurParent, nCurX, nCurY, nAttachOffset,
1204 aText, aPropertyName, aChoices, aSelectVal,
1205 aLeftColumn, aRightColumn,
1206 pControllerProperties, pCtrlTarget );
1208 else if( (aCtrlType == "Edit"
1209 || aCtrlType == "Range") && pCurParent )
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 );
1222 SAL_INFO( "vcl.osx.print", "Unsupported UI option \"" << aCtrlType << "\"");
1226 pControllerProperties->updateEnableState();
1227 adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn );
1229 // now reposition everything again so it is upper bound
1230 adjustTabViews( pTabView, aMaxTabSize );
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];
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]];
1248 // set the current selected tab item
1249 if( pState->nLastPage >= 0 && pState->nLastPage < [pTabView numberOfTabViewItems] )
1250 [pTabView selectTabViewItemAtIndex: pState->nLastPage];
1257 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */