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"
22 #include "tools/resary.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"
38 #include "com/sun/star/i18n/XBreakIterator.hpp"
39 #include "com/sun/star/i18n/WordType.hpp"
44 using namespace com::sun::star;
45 using namespace com::sun::star::beans;
46 using namespace com::sun::star::uno;
48 class ControllerProperties;
50 @interface ControlTarget : NSObject
52 ControllerProperties* mpController;
54 -(id)initWithControllerMap: (ControllerProperties*)pController;
55 -(void)triggered:(id)pSender;
56 -(void)triggeredNumeric:(id)pSender;
60 @interface AquaPrintPanelAccessoryController : NSViewController< NSPrintPanelAccessorizing >
62 NSPrintOperation *mpPrintOperation;
63 vcl::PrinterController *mpPrinterController;
64 PrintAccessoryViewState *mpViewState;
67 -(void)forPrintOperation:(NSPrintOperation*)pPrintOp;
68 -(void)withPrinterController:(vcl::PrinterController*)pController;
69 -(void)withViewState:(PrintAccessoryViewState*)pState;
71 -(NSPrintOperation*)printOperation;
72 -(vcl::PrinterController*)printerController;
73 -(PrintAccessoryViewState*)viewState;
75 -(NSSet*)keyPathsForValuesAffectingPreview;
76 -(NSArray*)localizedSummaryItems;
78 -(sal_Int32)updatePrintOperation:(sal_Int32)pLastPageCount;
82 @implementation AquaPrintPanelAccessoryController
84 -(void)forPrintOperation:(NSPrintOperation*)pPrintOp
85 { mpPrintOperation = pPrintOp; }
87 -(void)withPrinterController:(vcl::PrinterController*)pController
88 { mpPrinterController = pController; }
90 -(void)withViewState:(PrintAccessoryViewState*)pState
91 { mpViewState = pState; }
93 -(NSPrintOperation*)printOperation
94 { return mpPrintOperation; }
96 -(vcl::PrinterController*)printerController
97 { return mpPrinterController; }
99 -(PrintAccessoryViewState*)viewState
100 { return mpViewState; }
102 -(NSSet*)keyPathsForValuesAffectingPreview
104 return [ NSSet setWithObject:@"updatePrintOperation" ];
107 -(NSArray*)localizedSummaryItems
109 return [ NSArray arrayWithObject:
110 [ NSDictionary dictionary ] ];
113 -(sal_Int32)updatePrintOperation:(sal_Int32)pLastPageCount
115 // page range may be changed by option choice
116 sal_Int32 nPages = mpPrinterController->getFilteredPageCount();
118 mpViewState->bNeedRestart = false;
119 if( nPages != pLastPageCount )
121 #if OSL_DEBUG_LEVEL > 1
122 SAL_INFO( "vcl.osx.print", "number of pages changed" <<
123 " from " << pLastPageCount << " to " << nPages );
125 mpViewState->bNeedRestart = true;
128 NSTabView* pTabView = [[[self view] subviews] objectAtIndex:0];
129 NSTabViewItem* pItem = [pTabView selectedTabViewItem];
131 mpViewState->nLastPage = [pTabView indexOfTabViewItem: pItem];
133 mpViewState->nLastPage = 0;
135 if( mpViewState->bNeedRestart )
137 // AppKit does not give a chance of changing the page count
138 // and don't let cancel the dialog either
139 // hack: send a cancel message to the modal window displaying views
140 NSWindow* pNSWindow = [NSApp modalWindow];
142 [pNSWindow cancelOperation: nil];
143 [[mpPrintOperation printInfo] setJobDisposition: NSPrintCancelJob];
151 class ControllerProperties
153 std::map< int, rtl::OUString > maTagToPropertyName;
154 std::map< int, sal_Int32 > maTagToValueInt;
155 std::map< NSView*, NSView* > maViewPairMap;
156 std::vector< NSObject* > maViews;
158 sal_Int32 mnLastPageCount;
159 ResStringArray maLocalizedStrings;
160 AquaPrintPanelAccessoryController* mpAccessoryController;
163 ControllerProperties( AquaPrintPanelAccessoryController* i_pAccessoryController )
165 , mnLastPageCount( [i_pAccessoryController printerController]->getFilteredPageCount() )
166 , maLocalizedStrings( VclResId( SV_PRINT_NATIVE_STRINGS ) )
167 , mpAccessoryController( i_pAccessoryController )
169 assert( maLocalizedStrings.Count() >= 5 && "resources not found" );
172 rtl::OUString getMoreString()
174 return maLocalizedStrings.Count() >= 4
175 ? OUString( maLocalizedStrings.GetString( 3 ) )
176 : OUString( "More" );
179 rtl::OUString getPrintSelectionString()
181 return maLocalizedStrings.Count() >= 5
182 ? OUString( maLocalizedStrings.GetString( 4 ) )
183 : OUString( "Print selection only" );
186 int addNameTag( const rtl::OUString& i_rPropertyName )
188 int nNewTag = mnNextTag++;
189 maTagToPropertyName[ nNewTag ] = i_rPropertyName;
193 int addNameAndValueTag( const rtl::OUString& i_rPropertyName, sal_Int32 i_nValue )
195 int nNewTag = mnNextTag++;
196 maTagToPropertyName[ nNewTag ] = i_rPropertyName;
197 maTagToValueInt[ nNewTag ] = i_nValue;
201 void addObservedControl( NSObject* i_pView )
203 maViews.push_back( i_pView );
206 void addViewPair( NSView* i_pLeft, NSView* i_pRight )
208 maViewPairMap[ i_pLeft ] = i_pRight;
209 maViewPairMap[ i_pRight ] = i_pLeft;
212 NSView* getPair( NSView* i_pLeft ) const
214 NSView* pRight = nil;
215 std::map< NSView*, NSView* >::const_iterator it = maViewPairMap.find( i_pLeft );
216 if( it != maViewPairMap.end() )
221 void changePropertyWithIntValue( int i_nTag )
223 std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
224 std::map< int, sal_Int32 >::const_iterator value_it = maTagToValueInt.find( i_nTag );
225 if( name_it != maTagToPropertyName.end() && value_it != maTagToValueInt.end() )
227 vcl::PrinterController * mpController = [mpAccessoryController printerController];
228 PropertyValue* pVal = mpController->getValue( name_it->second );
231 pVal->Value <<= value_it->second;
232 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount];
237 void changePropertyWithIntValue( int i_nTag, sal_Int64 i_nValue )
239 std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
240 if( name_it != maTagToPropertyName.end() )
242 vcl::PrinterController * mpController = [mpAccessoryController printerController];
243 PropertyValue* pVal = mpController->getValue( name_it->second );
246 pVal->Value <<= i_nValue;
247 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount];
252 void changePropertyWithBoolValue( int i_nTag, bool i_bValue )
254 std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
255 if( name_it != maTagToPropertyName.end() )
257 vcl::PrinterController * mpController = [mpAccessoryController printerController];
258 PropertyValue* pVal = mpController->getValue( name_it->second );
262 if( name_it->second == "PrintContent" )
263 pVal->Value <<= i_bValue ? sal_Int32(2) : sal_Int32(0);
265 pVal->Value <<= i_bValue;
267 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount];
272 void changePropertyWithStringValue( int i_nTag, const rtl::OUString& i_rValue )
274 std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
275 if( name_it != maTagToPropertyName.end() )
277 vcl::PrinterController * mpController = [mpAccessoryController printerController];
278 PropertyValue* pVal = mpController->getValue( name_it->second );
281 pVal->Value <<= i_rValue;
282 mnLastPageCount = [mpAccessoryController updatePrintOperation: mnLastPageCount];
287 void updateEnableState()
289 for( std::vector< NSObject* >::iterator it = maViews.begin(); it != maViews.end(); ++it )
291 NSObject* pObj = *it;
292 NSControl* pCtrl = nil;
294 if( [pObj isKindOfClass: [NSControl class]] )
295 pCtrl = (NSControl*)pObj;
296 else if( [pObj isKindOfClass: [NSCell class]] )
297 pCell = (NSCell*)pObj;
299 int nTag = pCtrl ? [pCtrl tag] :
300 pCell ? [pCell tag] :
303 std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( nTag );
304 if( name_it != maTagToPropertyName.end() && name_it->second != "PrintContent" )
306 vcl::PrinterController * mpController = [mpAccessoryController printerController];
307 BOOL bEnabled = mpController->isUIOptionEnabled( name_it->second ) ? YES : NO;
310 [pCtrl setEnabled: bEnabled];
311 NSView* pOther = getPair( pCtrl );
312 if( pOther && [pOther isKindOfClass: [NSControl class]] )
313 [(NSControl*)pOther setEnabled: bEnabled];
316 [pCell setEnabled: bEnabled];
323 static OUString filterAccelerator( rtl::OUString const & rText )
325 rtl::OUStringBuffer aBuf( rText.getLength() );
326 for( sal_Int32 nIndex = 0; nIndex != -1; )
327 aBuf.append( rText.getToken( 0, '~', nIndex ) );
328 return aBuf.makeStringAndClear();
331 @implementation ControlTarget
333 -(id)initWithControllerMap: (ControllerProperties*)pController
335 if( (self = [super init]) )
337 mpController = pController;
342 -(void)triggered:(id)pSender
344 if( [pSender isMemberOfClass: [NSPopUpButton class]] )
346 NSPopUpButton* pBtn = (NSPopUpButton*)pSender;
347 NSMenuItem* pSelected = [pBtn selectedItem];
350 int nTag = [pSelected tag];
351 mpController->changePropertyWithIntValue( nTag );
354 else if( [pSender isMemberOfClass: [NSButton class]] )
356 NSButton* pBtn = (NSButton*)pSender;
357 int nTag = [pBtn tag];
358 mpController->changePropertyWithBoolValue( nTag, [pBtn state] == NSOnState );
360 else if( [pSender isMemberOfClass: [NSMatrix class]] )
362 NSObject* pObj = [(NSMatrix*)pSender selectedCell];
363 if( [pObj isMemberOfClass: [NSButtonCell class]] )
365 NSButtonCell* pCell = (NSButtonCell*)pObj;
366 int nTag = [pCell tag];
367 mpController->changePropertyWithIntValue( nTag );
370 else if( [pSender isMemberOfClass: [NSTextField class]] )
372 NSTextField* pField = (NSTextField*)pSender;
373 int nTag = [pField tag];
374 rtl::OUString aValue = GetOUString( [pSender stringValue] );
375 mpController->changePropertyWithStringValue( nTag, aValue );
379 SAL_INFO( "vcl.osx.print", "Unsupported class" <<
380 ( [pSender class] ? [NSStringFromClass([pSender class]) UTF8String] : "nil" ) );
382 mpController->updateEnableState();
385 -(void)triggeredNumeric:(id)pSender
387 if( [pSender isMemberOfClass: [NSTextField class]] )
389 NSTextField* pField = (NSTextField*)pSender;
390 int nTag = [pField tag];
391 sal_Int64 nValue = [pField intValue];
393 NSView* pOther = mpController->getPair( pField );
395 [(NSControl*)pOther setIntValue: nValue];
397 mpController->changePropertyWithIntValue( nTag, nValue );
399 else if( [pSender isMemberOfClass: [NSStepper class]] )
401 NSStepper* pStep = (NSStepper*)pSender;
402 int nTag = [pStep tag];
403 sal_Int64 nValue = [pStep intValue];
405 NSView* pOther = mpController->getPair( pStep );
407 [(NSControl*)pOther setIntValue: nValue];
409 mpController->changePropertyWithIntValue( nTag, nValue );
413 SAL_INFO( "vcl.osx.print", "Unsupported class" <<
414 ([pSender class] ? [NSStringFromClass([pSender class]) UTF8String] : "nil") );
416 mpController->updateEnableState();
431 NSControl* pSubControl;
433 ColumnItem( NSControl* i_pControl = nil, long i_nOffset = 0, NSControl* i_pSub = nil )
434 : pControl( i_pControl )
435 , nOffset( i_nOffset )
436 , pSubControl( i_pSub )
439 long getWidth() const
444 NSRect aCtrlRect = [pControl frame];
445 nWidth = aCtrlRect.size.width;
449 NSRect aSubRect = [pSubControl frame];
450 nWidth += aSubRect.size.width;
451 nWidth += aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width);
458 static void adjustViewAndChildren( NSView* pNSView, NSSize& rMaxSize,
459 std::vector< ColumnItem >& rLeftColumn,
460 std::vector< ColumnItem >& rRightColumn
465 // first get overall column widths
467 long nRightWidth = 0;
468 for( size_t i = 0; i < rLeftColumn.size(); i++ )
470 long nW = rLeftColumn[i].getWidth();
471 if( nW > nLeftWidth )
474 for( size_t i = 0; i < rRightColumn.size(); i++ )
476 long nW = rRightColumn[i].getWidth();
477 if( nW > nRightWidth )
481 // right align left column
482 for( size_t i = 0; i < rLeftColumn.size(); i++ )
484 if( rLeftColumn[i].pControl )
486 NSRect aCtrlRect = [rLeftColumn[i].pControl frame];
487 long nX = nLeftWidth - aCtrlRect.size.width;
488 if( rLeftColumn[i].pSubControl )
490 NSRect aSubRect = [rLeftColumn[i].pSubControl frame];
491 nX -= aSubRect.size.width + (aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width));
492 aSubRect.origin.x = nLeftWidth - aSubRect.size.width;
493 [rLeftColumn[i].pSubControl setFrame: aSubRect];
495 aCtrlRect.origin.x = nX;
496 [rLeftColumn[i].pControl setFrame: aCtrlRect];
500 // left align right column
501 for( size_t i = 0; i < rRightColumn.size(); i++ )
503 if( rRightColumn[i].pControl )
505 NSRect aCtrlRect = [rRightColumn[i].pControl frame];
506 long nX = nLeftWidth + 3;
507 if( rRightColumn[i].pSubControl )
509 NSRect aSubRect = [rRightColumn[i].pSubControl frame];
510 aSubRect.origin.x = nX + aSubRect.origin.x - aCtrlRect.origin.x;
511 [rRightColumn[i].pSubControl setFrame: aSubRect];
513 aCtrlRect.origin.x = nX;
514 [rRightColumn[i].pControl setFrame: aCtrlRect];
518 NSArray* pSubViews = [pNSView subviews];
519 unsigned int nViews = [pSubViews count];
520 NSRect aUnion = NSZeroRect;
522 // get the combined frame of all subviews
523 for( unsigned int n = 0; n < nViews; n++ )
525 aUnion = NSUnionRect( aUnion, [[pSubViews objectAtIndex: n] frame] );
528 // move everything so it will fit
529 for( unsigned int n = 0; n < nViews; n++ )
531 NSView* pCurSubView = [pSubViews objectAtIndex: n];
532 NSRect aFrame = [pCurSubView frame];
533 aFrame.origin.x -= aUnion.origin.x - 5;
534 aFrame.origin.y -= aUnion.origin.y - 5;
535 [pCurSubView setFrame: aFrame];
538 // resize the view itself
539 aUnion.size.height += 10;
540 aUnion.size.width += 20;
541 [pNSView setFrameSize: aUnion.size];
543 if( aUnion.size.width > rMaxSize.width )
544 rMaxSize.width = aUnion.size.width;
545 if( aUnion.size.height > rMaxSize.height )
546 rMaxSize.height = aUnion.size.height;
549 static void adjustTabViews( NSTabView* pTabView, NSSize aTabSize )
551 // loop over all contained tab pages
552 NSArray* pTabbedViews = [pTabView tabViewItems];
553 int nViews = [pTabbedViews count];
554 for( int i = 0; i < nViews; i++ )
556 NSTabViewItem* pItem = (NSTabViewItem*)[pTabbedViews objectAtIndex: i];
557 NSView* pNSView = [pItem view];
560 NSRect aRect = [pNSView frame];
561 double nDiff = aTabSize.height - aRect.size.height;
562 aRect.size = aTabSize;
563 [pNSView setFrame: aRect];
565 NSArray* pSubViews = [pNSView subviews];
566 unsigned int nSubViews = [pSubViews count];
568 // move everything up
569 for( unsigned int n = 0; n < nSubViews; n++ )
571 NSView* pCurSubView = [pSubViews objectAtIndex: n];
572 NSRect aFrame = [pCurSubView frame];
573 aFrame.origin.y += nDiff;
574 // give separators the correct width
575 // separators are currently the only NSBoxes we use
576 if( [pCurSubView isMemberOfClass: [NSBox class]] )
578 aFrame.size.width = aTabSize.width - aFrame.origin.x - 10;
580 [pCurSubView setFrame: aFrame];
586 static NSControl* createLabel( const rtl::OUString& i_rText )
588 NSString* pText = CreateNSString( i_rText );
589 NSRect aTextRect = { NSZeroPoint, {20, 15} };
590 NSTextField* pTextView = [[NSTextField alloc] initWithFrame: aTextRect];
591 [pTextView setFont: [NSFont controlContentFontOfSize: 0]];
592 [pTextView setEditable: NO];
593 [pTextView setSelectable: NO];
594 [pTextView setDrawsBackground: NO];
595 [pTextView setBordered: NO];
596 [pTextView setStringValue: pText];
597 [pTextView sizeToFit];
602 static sal_Int32 findBreak( const rtl::OUString& i_rText, sal_Int32 i_nPos )
604 sal_Int32 nRet = i_rText.getLength();
605 Reference< i18n::XBreakIterator > xBI( vcl::unohelper::CreateBreakIterator() );
608 i18n::Boundary aBoundary =
609 xBI->getWordBoundary( i_rText, i_nPos,
610 Application::GetSettings().GetLanguageTag().getLocale(),
611 i18n::WordType::ANYWORD_IGNOREWHITESPACES,
613 nRet = aBoundary.endPos;
618 static void linebreakCell( NSCell* pBtn, const rtl::OUString& i_rText )
620 NSString* pText = CreateNSString( i_rText );
621 [pBtn setTitle: pText];
623 NSSize aSize = [pBtn cellSize];
624 if( aSize.width > 280 )
627 sal_Int32 nLen = i_rText.getLength();
628 sal_Int32 nIndex = nLen / 2;
629 nIndex = findBreak( i_rText, nIndex );
632 rtl::OUStringBuffer aBuf( i_rText );
634 pText = CreateNSString( aBuf.makeStringAndClear() );
635 [pBtn setTitle: pText];
641 static void addSubgroup( NSView* pCurParent, long& rCurY, const rtl::OUString& rText )
643 NSControl* pTextView = createLabel( rText );
644 [pCurParent addSubview: [pTextView autorelease]];
645 NSRect aTextRect = [pTextView frame];
647 aTextRect.origin.y = rCurY - aTextRect.size.height;
648 [pTextView setFrame: aTextRect];
650 NSRect aSepRect = { { aTextRect.size.width + 1, aTextRect.origin.y }, { 100, 6 } };
651 NSBox* pBox = [[NSBox alloc] initWithFrame: aSepRect];
652 [pBox setBoxType: NSBoxSeparator];
653 [pCurParent addSubview: [pBox autorelease]];
656 rCurY = aTextRect.origin.y - 5;
659 static void addBool( NSView* pCurParent, long& rCurX, long& rCurY, long nAttachOffset,
660 const rtl::OUString& rText, bool bEnabled,
661 const rtl::OUString& rProperty, bool bValue,
662 std::vector<ColumnItem >& rRightColumn,
663 ControllerProperties* pControllerProperties,
664 ControlTarget* pCtrlTarget
667 NSRect aCheckRect = { { static_cast<CGFloat>(rCurX + nAttachOffset), 0 }, { 0, 15 } };
668 NSButton* pBtn = [[NSButton alloc] initWithFrame: aCheckRect];
669 [pBtn setButtonType: NSSwitchButton];
670 [pBtn setState: bValue ? NSOnState : NSOffState];
672 [pBtn setEnabled: NO];
673 linebreakCell( [pBtn cell], rText );
676 rRightColumn.push_back( ColumnItem( pBtn ) );
679 [pBtn setTarget: pCtrlTarget];
680 [pBtn setAction: @selector(triggered:)];
681 int nTag = pControllerProperties->addNameTag( rProperty );
682 pControllerProperties->addObservedControl( pBtn );
685 aCheckRect = [pBtn frame];
686 // #i115837# add a murphy factor; it can apparently occasionally happen
687 // that sizeToFit does not a perfect job and that the button linebreaks again
688 // if - and only if - there is already a '\n' contained in the text and the width
690 aCheckRect.size.width += 1;
693 aCheckRect.origin.y = rCurY - aCheckRect.size.height;
694 [pBtn setFrame: aCheckRect];
696 [pCurParent addSubview: [pBtn autorelease]];
699 rCurY = aCheckRect.origin.y - 5;
702 static void addRadio( NSView* pCurParent, long& rCurX, long& rCurY, long nAttachOffset,
703 const rtl::OUString& rText,
704 const rtl::OUString& rProperty, Sequence<rtl::OUString> const & rChoices, sal_Int32 nSelectValue,
705 std::vector<ColumnItem >& rLeftColumn,
706 std::vector<ColumnItem >& rRightColumn,
707 ControllerProperties* pControllerProperties,
708 ControlTarget* pCtrlTarget
712 if( rText.getLength() )
715 NSControl* pTextView = createLabel( rText );
716 NSRect aTextRect = [pTextView frame];
717 aTextRect.origin.x = rCurX + nAttachOffset;
718 [pCurParent addSubview: [pTextView autorelease]];
720 rLeftColumn.push_back( ColumnItem( pTextView ) );
723 aTextRect.origin.y = rCurY - aTextRect.size.height;
724 [pTextView setFrame: aTextRect];
727 rCurY = aTextRect.origin.y - 5;
729 // indent the radio group relative to the text
733 // setup radio matrix
734 NSButtonCell* pProto = [[NSButtonCell alloc] init];
736 NSRect aRadioRect = { { static_cast<CGFloat>(rCurX + nOff), 0 },
737 { static_cast<CGFloat>(280 - rCurX),
738 static_cast<CGFloat>(5*rChoices.getLength()) } };
739 [pProto setTitle: @"RadioButtonGroup"];
740 [pProto setButtonType: NSRadioButton];
741 NSMatrix* pMatrix = [[NSMatrix alloc] initWithFrame: aRadioRect
742 mode: NSRadioModeMatrix
743 prototype: (NSCell*)pProto
744 numberOfRows: rChoices.getLength()
746 // set individual titles
747 NSArray* pCells = [pMatrix cells];
748 for( sal_Int32 m = 0; m < rChoices.getLength(); m++ )
750 NSCell* pCell = [pCells objectAtIndex: m];
751 linebreakCell( pCell, filterAccelerator( rChoices[m] ) );
752 // connect target and action
753 [pCell setTarget: pCtrlTarget];
754 [pCell setAction: @selector(triggered:)];
755 int nTag = pControllerProperties->addNameAndValueTag( rProperty, m );
756 pControllerProperties->addObservedControl( pCell );
757 [pCell setTag: nTag];
758 // set current selection
759 if( nSelectValue == m )
760 [pMatrix selectCellAtRow: m column: 0];
763 aRadioRect = [pMatrix frame];
765 // move it down, so it comes to the correct position
766 aRadioRect.origin.y = rCurY - aRadioRect.size.height;
767 [pMatrix setFrame: aRadioRect];
768 [pCurParent addSubview: [pMatrix autorelease]];
770 rRightColumn.push_back( ColumnItem( pMatrix ) );
773 rCurY = aRadioRect.origin.y - 5;
778 static void addList( NSView* pCurParent, long& rCurX, long& rCurY, long /*nAttachOffset*/,
779 const rtl::OUString& rText,
780 const rtl::OUString& rProperty, Sequence<rtl::OUString> const & rChoices, sal_Int32 nSelectValue,
781 std::vector<ColumnItem >& rLeftColumn,
782 std::vector<ColumnItem >& rRightColumn,
783 ControllerProperties* pControllerProperties,
784 ControlTarget* pCtrlTarget
787 // don't indent attached lists, looks bad in the existing cases
788 NSControl* pTextView = createLabel( rText );
789 [pCurParent addSubview: [pTextView autorelease]];
790 rLeftColumn.push_back( ColumnItem( pTextView ) );
791 NSRect aTextRect = [pTextView frame];
792 aTextRect.origin.x = rCurX /* + nAttachOffset*/;
794 // don't indent attached lists, looks bad in the existing cases
795 NSRect aBtnRect = { { rCurX /*+ nAttachOffset*/ + aTextRect.size.width, 0 }, { 0, 15 } };
796 NSPopUpButton* pBtn = [[NSPopUpButton alloc] initWithFrame: aBtnRect pullsDown: NO];
799 for( sal_Int32 m = 0; m < rChoices.getLength(); m++ )
801 NSString* pItemText = CreateNSString( rChoices[m] );
802 [pBtn addItemWithTitle: pItemText];
803 NSMenuItem* pItem = [pBtn itemWithTitle: pItemText];
804 int nTag = pControllerProperties->addNameAndValueTag( rProperty, m );
805 [pItem setTag: nTag];
809 [pBtn selectItemAtIndex: nSelectValue];
811 // add the button to observed controls for enabled state changes
812 // also add a tag just for this purpose
813 pControllerProperties->addObservedControl( pBtn );
814 [pBtn setTag: pControllerProperties->addNameTag( rProperty )];
817 [pCurParent addSubview: [pBtn autorelease]];
819 rRightColumn.push_back( ColumnItem( pBtn ) );
821 // connect target and action
822 [pBtn setTarget: pCtrlTarget];
823 [pBtn setAction: @selector(triggered:)];
826 aBtnRect = [pBtn frame];
827 aBtnRect.origin.y = rCurY - aBtnRect.size.height;
828 [pBtn setFrame: aBtnRect];
831 aTextRect.origin.y = aBtnRect.origin.y + (aBtnRect.size.height - aTextRect.size.height)/2;
832 [pTextView setFrame: aTextRect];
835 rCurY = aBtnRect.origin.y - 5;
838 static void addEdit( NSView* pCurParent, long& rCurX, long& rCurY, long nAttachOffset,
839 const rtl::OUString& rCtrlType,
840 const rtl::OUString& rText,
841 const rtl::OUString& rProperty, const PropertyValue* pValue,
842 sal_Int64 nMinValue, sal_Int64 nMaxValue,
843 std::vector<ColumnItem >& rLeftColumn,
844 std::vector<ColumnItem >& rRightColumn,
845 ControllerProperties* pControllerProperties,
846 ControlTarget* pCtrlTarget
850 if( rText.getLength() )
853 NSControl* pTextView = createLabel( rText );
854 [pCurParent addSubview: [pTextView autorelease]];
856 rLeftColumn.push_back( ColumnItem( pTextView ) );
859 NSRect aTextRect = [pTextView frame];
860 aTextRect.origin.x = rCurX + nAttachOffset;
861 aTextRect.origin.y = rCurY - aTextRect.size.height;
862 [pTextView setFrame: aTextRect];
865 rCurY = aTextRect.origin.y - 5;
867 // and set the offset for the real edit field
868 nOff = aTextRect.size.width + 5;
871 NSRect aFieldRect = { { static_cast<CGFloat>(rCurX + nOff + nAttachOffset), 0 }, { 100, 25 } };
872 NSTextField* pFieldView = [[NSTextField alloc] initWithFrame: aFieldRect];
873 [pFieldView setEditable: YES];
874 [pFieldView setSelectable: YES];
875 [pFieldView setDrawsBackground: YES];
876 [pFieldView sizeToFit]; // FIXME: this does nothing
877 [pCurParent addSubview: [pFieldView autorelease]];
879 rRightColumn.push_back( ColumnItem( pFieldView ) );
881 // add the field to observed controls for enabled state changes
882 // also add a tag just for this purpose
883 pControllerProperties->addObservedControl( pFieldView );
884 int nTag = pControllerProperties->addNameTag( rProperty );
885 [pFieldView setTag: nTag];
886 // pControllerProperties->addNamedView( pFieldView, aPropertyName );
889 aFieldRect.origin.y = rCurY - aFieldRect.size.height;
890 [pFieldView setFrame: aFieldRect];
892 if( rCtrlType == "Range" )
894 // add a stepper control
895 NSRect aStepFrame = { { aFieldRect.origin.x + aFieldRect.size.width + 5,
896 aFieldRect.origin.y },
897 { 15, aFieldRect.size.height } };
898 NSStepper* pStep = [[NSStepper alloc] initWithFrame: aStepFrame];
899 [pStep setIncrement: 1];
900 [pStep setValueWraps: NO];
901 [pStep setTag: nTag];
902 [pCurParent addSubview: [pStep autorelease]];
904 rRightColumn.back().pSubControl = pStep;
906 pControllerProperties->addObservedControl( pStep );
907 [pStep setTarget: pCtrlTarget];
908 [pStep setAction: @selector(triggered:)];
910 // constrain the text field to decimal numbers
911 NSNumberFormatter* pFormatter = [[NSNumberFormatter alloc] init];
912 [pFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4];
913 [pFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
914 [pFormatter setAllowsFloats: NO];
915 [pFormatter setMaximumFractionDigits: 0];
916 if( nMinValue != nMaxValue )
918 [pFormatter setMinimum: [[NSNumber numberWithInt: nMinValue] autorelease]];
919 [pStep setMinValue: nMinValue];
920 [pFormatter setMaximum: [[NSNumber numberWithInt: nMaxValue] autorelease]];
921 [pStep setMaxValue: nMaxValue];
923 [pFieldView setFormatter: pFormatter];
925 sal_Int64 nSelectVal = 0;
926 if( pValue && pValue->Value.hasValue() )
927 pValue->Value >>= nSelectVal;
929 [pFieldView setIntValue: nSelectVal];
930 [pStep setIntValue: nSelectVal];
932 pControllerProperties->addViewPair( pFieldView, pStep );
933 // connect target and action
934 [pFieldView setTarget: pCtrlTarget];
935 [pFieldView setAction: @selector(triggeredNumeric:)];
936 [pStep setTarget: pCtrlTarget];
937 [pStep setAction: @selector(triggeredNumeric:)];
941 // connect target and action
942 [pFieldView setTarget: pCtrlTarget];
943 [pFieldView setAction: @selector(triggered:)];
945 if( pValue && pValue->Value.hasValue() )
947 rtl::OUString aValue;
948 pValue->Value >>= aValue;
949 if( aValue.getLength() )
951 NSString* pText = CreateNSString( aValue );
952 [pFieldView setStringValue: pText];
959 rCurY = aFieldRect.origin.y - 5;
962 @implementation AquaPrintAccessoryView
964 +(NSObject*)setupPrinterPanel: (NSPrintOperation*)pOp
965 withController: (vcl::PrinterController*)pController
966 withState: (PrintAccessoryViewState*)pState
968 const Sequence< PropertyValue >& rOptions( pController->getUIOptions() );
969 if( rOptions.getLength() == 0 )
972 NSRect aViewFrame = { NSZeroPoint, { 600, 400 } };
973 NSRect aTabViewFrame = aViewFrame;
975 NSView* pAccessoryView = [[NSView alloc] initWithFrame: aViewFrame];
976 NSTabView* pTabView = [[NSTabView alloc] initWithFrame: aTabViewFrame];
977 [pAccessoryView addSubview: [pTabView autorelease]];
979 // create the accessory controller
980 AquaPrintPanelAccessoryController* pAccessoryController =
981 [[AquaPrintPanelAccessoryController alloc] initWithNibName: nil bundle: nil];
982 [pAccessoryController setView: [pAccessoryView autorelease]];
983 [pAccessoryController forPrintOperation: pOp];
984 [pAccessoryController withPrinterController: pController];
985 [pAccessoryController withViewState: pState];
987 NSView* pCurParent = nullptr;
990 NSSize aMaxTabSize = NSZeroSize;
992 ControllerProperties* pControllerProperties = new ControllerProperties( pAccessoryController );
993 ControlTarget* pCtrlTarget = [[ControlTarget alloc] initWithControllerMap: pControllerProperties];
995 std::vector< ColumnItem > aLeftColumn, aRightColumn;
998 // prepend a "selection" checkbox if the properties have such a selection in PrintContent
999 bool bAddSelectionCheckBox = false, bSelectionBoxEnabled = false, bSelectionBoxChecked = false;
1001 for( int i = 0; i < rOptions.getLength(); i++ )
1003 Sequence< beans::PropertyValue > aOptProp;
1004 rOptions[i].Value >>= aOptProp;
1006 rtl::OUString aCtrlType;
1007 rtl::OUString aPropertyName;
1008 Sequence< rtl::OUString > aChoices;
1009 Sequence< sal_Bool > aChoicesDisabled;
1010 sal_Int32 aSelectionChecked = 0;
1011 for( int n = 0; n < aOptProp.getLength(); n++ )
1013 const beans::PropertyValue& rEntry( aOptProp[ n ] );
1014 if( rEntry.Name == "ControlType" )
1016 rEntry.Value >>= aCtrlType;
1018 else if( rEntry.Name == "Choices" )
1020 rEntry.Value >>= aChoices;
1022 else if( rEntry.Name == "ChoicesDisabled" )
1024 rEntry.Value >>= aChoicesDisabled;
1026 else if( rEntry.Name == "Property" )
1029 rEntry.Value >>= aVal;
1030 aPropertyName = aVal.Name;
1031 if( aPropertyName == "PrintContent" )
1032 aVal.Value >>= aSelectionChecked;
1035 if( aCtrlType == "Radio" &&
1036 aPropertyName == "PrintContent" &&
1037 aChoices.getLength() > 2 )
1039 bAddSelectionCheckBox = true;
1040 bSelectionBoxEnabled = aChoicesDisabled.getLength() < 2 || ! aChoicesDisabled[2];
1041 bSelectionBoxChecked = (aSelectionChecked==2);
1046 for( int i = 0; i < rOptions.getLength(); i++ )
1048 Sequence< beans::PropertyValue > aOptProp;
1049 rOptions[i].Value >>= aOptProp;
1051 // extract ui element
1052 rtl::OUString aCtrlType;
1053 rtl::OUString aText;
1054 rtl::OUString aPropertyName;
1055 rtl::OUString aGroupHint;
1056 Sequence< rtl::OUString > aChoices;
1057 bool bEnabled = true;
1058 sal_Int64 nMinValue = 0, nMaxValue = 0;
1059 long nAttachOffset = 0;
1060 bool bIgnore = false;
1062 for( int n = 0; n < aOptProp.getLength(); n++ )
1064 const beans::PropertyValue& rEntry( aOptProp[ n ] );
1065 if( rEntry.Name == "Text" )
1067 rEntry.Value >>= aText;
1068 aText = filterAccelerator( aText );
1070 else if( rEntry.Name == "ControlType" )
1072 rEntry.Value >>= aCtrlType;
1074 else if( rEntry.Name == "Choices" )
1076 rEntry.Value >>= aChoices;
1078 else if( rEntry.Name == "Property" )
1081 rEntry.Value >>= aVal;
1082 aPropertyName = aVal.Name;
1084 else if( rEntry.Name == "Enabled" )
1087 rEntry.Value >>= bValue;
1090 else if( rEntry.Name == "MinValue" )
1092 rEntry.Value >>= nMinValue;
1094 else if( rEntry.Name == "MaxValue" )
1096 rEntry.Value >>= nMaxValue;
1098 else if( rEntry.Name == "AttachToDependency" )
1102 else if( rEntry.Name == "InternalUIOnly" )
1104 bool bValue = false;
1105 rEntry.Value >>= bValue;
1108 else if( rEntry.Name == "GroupingHint" )
1110 rEntry.Value >>= aGroupHint;
1114 if( aCtrlType == "Group" ||
1115 aCtrlType == "Subgroup" ||
1116 aCtrlType == "Radio" ||
1117 aCtrlType == "List" ||
1118 aCtrlType == "Edit" ||
1119 aCtrlType == "Range" ||
1120 aCtrlType == "Bool" )
1122 bool bIgnoreSubgroup = false;
1124 // with `setAccessoryView' method only one accessory view can be set
1125 // so create this single accessory view as tabbed for grouping
1126 if( aCtrlType == "Group"
1128 || ( aCtrlType == "Subgroup" && nCurY < -250 && ! bIgnore )
1131 rtl::OUString aGroupTitle( aText );
1132 if( aCtrlType == "Subgroup" )
1133 aGroupTitle = pControllerProperties->getMoreString();
1135 // set size of current parent
1137 adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn );
1140 if( ! aText.getLength() )
1142 NSString* pLabel = CreateNSString( aGroupTitle );
1143 NSTabViewItem* pItem = [[NSTabViewItem alloc] initWithIdentifier: pLabel ];
1144 [pItem setLabel: pLabel];
1145 [pTabView addTabViewItem: pItem];
1146 pCurParent = [[NSView alloc] initWithFrame: aTabViewFrame];
1147 [pItem setView: pCurParent];
1150 nCurX = 20; // reset indent
1151 nCurY = 0; // reset Y
1153 aLeftColumn.clear();
1154 aRightColumn.clear();
1156 if( bAddSelectionCheckBox )
1158 addBool( pCurParent, nCurX, nCurY, 0,
1159 pControllerProperties->getPrintSelectionString(), bSelectionBoxEnabled,
1160 "PrintContent", bSelectionBoxChecked,
1161 aRightColumn, pControllerProperties, pCtrlTarget );
1162 bAddSelectionCheckBox = false;
1166 if( aCtrlType == "Subgroup" && pCurParent )
1168 bIgnoreSubgroup = bIgnore;
1172 addSubgroup( pCurParent, nCurY, aText );
1174 else if( bIgnoreSubgroup || bIgnore )
1178 else if( aCtrlType == "Bool" && pCurParent )
1181 PropertyValue* pVal = pController->getValue( aPropertyName );
1183 pVal->Value >>= bVal;
1184 addBool( pCurParent, nCurX, nCurY, nAttachOffset,
1185 aText, true, aPropertyName, bVal,
1186 aRightColumn, pControllerProperties, pCtrlTarget );
1188 else if( aCtrlType == "Radio" && pCurParent )
1190 // get currently selected value
1191 sal_Int32 nSelectVal = 0;
1192 PropertyValue* pVal = pController->getValue( aPropertyName );
1193 if( pVal && pVal->Value.hasValue() )
1194 pVal->Value >>= nSelectVal;
1196 addRadio( pCurParent, nCurX, nCurY, nAttachOffset,
1197 aText, aPropertyName, aChoices, nSelectVal,
1198 aLeftColumn, aRightColumn,
1199 pControllerProperties, pCtrlTarget );
1201 else if( aCtrlType == "List" && pCurParent )
1203 PropertyValue* pVal = pController->getValue( aPropertyName );
1204 sal_Int32 aSelectVal = 0;
1205 if( pVal && pVal->Value.hasValue() )
1206 pVal->Value >>= aSelectVal;
1208 addList( pCurParent, nCurX, nCurY, nAttachOffset,
1209 aText, aPropertyName, aChoices, aSelectVal,
1210 aLeftColumn, aRightColumn,
1211 pControllerProperties, pCtrlTarget );
1213 else if( (aCtrlType == "Edit"
1214 || aCtrlType == "Range") && pCurParent )
1217 PropertyValue* pVal = pController->getValue( aPropertyName );
1218 addEdit( pCurParent, nCurX, nCurY, nAttachOffset,
1219 aCtrlType, aText, aPropertyName, pVal,
1220 nMinValue, nMaxValue,
1221 aLeftColumn, aRightColumn,
1222 pControllerProperties, pCtrlTarget );
1227 SAL_INFO( "vcl.osx.print", "Unsupported UI option \"" << aCtrlType << "\"");
1231 pControllerProperties->updateEnableState();
1232 adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn );
1234 // now reposition everything again so it is upper bound
1235 adjustTabViews( pTabView, aMaxTabSize );
1237 // find the minimum needed tab size
1238 NSSize aTabCtrlSize = [pTabView minimumSize];
1239 aTabCtrlSize.height += aMaxTabSize.height + 10;
1240 if( aTabCtrlSize.width < aMaxTabSize.width + 10 )
1241 aTabCtrlSize.width = aMaxTabSize.width + 10;
1242 [pTabView setFrameSize: aTabCtrlSize];
1243 aViewFrame.size.width = aTabCtrlSize.width + aTabViewFrame.origin.x;
1244 aViewFrame.size.height = aTabCtrlSize.height + aTabViewFrame.origin.y;
1245 [pAccessoryView setFrameSize: aViewFrame.size];
1247 // get the print panel
1248 NSPrintPanel* pPrintPanel = [pOp printPanel];
1249 [pPrintPanel setOptions: [pPrintPanel options] | NSPrintPanelShowsPreview];
1250 // add the accessory controller to the panel
1251 [pPrintPanel addAccessoryController: [pAccessoryController autorelease]];
1253 // set the current selected tab item
1254 if( pState->nLastPage >= 0 && pState->nLastPage < [pTabView numberOfTabViewItems] )
1255 [pTabView selectTabViewItemAtIndex: pState->nLastPage];
1262 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */