1/**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements.  See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership.  The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License.  You may obtain a copy of the License at
10 *
11 *   http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied.  See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24// MARKER(update_precomp.py): autogen include statement, do not remove
25#include "precompiled_vcl.hxx"
26
27#include "tools/resary.hxx"
28
29#include "vcl/print.hxx"
30#include "vcl/image.hxx"
31#include "vcl/virdev.hxx"
32#include "vcl/svapp.hxx"
33#include "vcl/unohelp.hxx"
34
35#include "aqua/aquaprintview.h"
36#include "aqua/salinst.h"
37
38#include "svdata.hxx"
39#include "svids.hrc"
40
41#include "com/sun/star/i18n/XBreakIterator.hpp"
42#include "com/sun/star/i18n/WordType.hpp"
43
44#include <map>
45
46using namespace vcl;
47using namespace com::sun::star;
48using namespace com::sun::star::beans;
49using namespace com::sun::star::uno;
50
51/* Note: the accesory view as implemented here is already deprecated in Leopard. Unfortunately
52   as long as our baseline is Tiger we cannot gain the advantages over multiple accessory views
53   as well havs having accessory views AND a preview (as long as you are linked vs. 10.4 libraries
54   the preview insists on not being present. This is unfortunate.
55*/
56
57class ControllerProperties;
58
59@interface ControlTarget : NSObject
60{
61    ControllerProperties* mpController;
62}
63-(id)initWithControllerMap: (ControllerProperties*)pController;
64-(void)triggered:(id)pSender;
65-(void)triggeredNumeric:(id)pSender;
66-(void)triggeredPreview:(id)pSender;
67-(void)dealloc;
68@end
69
70
71class ControllerProperties
72{
73    vcl::PrinterController*             mpController;
74    std::map< int, rtl::OUString >      maTagToPropertyName;
75    std::map< int, sal_Int32 >          maTagToValueInt;
76    std::map< NSView*, NSView* >        maViewPairMap;
77    std::vector< NSObject* >            maViews;
78    int                                 mnNextTag;
79    sal_Int32                           mnLastPageCount;
80    PrintAccessoryViewState*            mpState;
81    NSPrintOperation*                   mpOp;
82    NSView*                             mpAccessoryView;
83    NSTabView*                          mpTabView;
84    NSBox*                              mpPreviewBox;
85    NSImageView*                        mpPreview;
86    NSTextField*                        mpPageEdit;
87    NSStepper*                          mpStepper;
88    NSTextView*                         mpPagesLabel;
89    ResStringArray                      maLocalizedStrings;
90
91    public:
92    ControllerProperties( vcl::PrinterController* i_pController,
93                          NSPrintOperation* i_pOp,
94                          NSView* i_pAccessoryView,
95                          NSTabView* i_pTabView,
96                          PrintAccessoryViewState* i_pState )
97    : mpController( i_pController ),
98      mnNextTag( 0 ),
99      mnLastPageCount( i_pController->getFilteredPageCount() ),
100      mpState( i_pState ),
101      mpOp( i_pOp ),
102      mpAccessoryView( i_pAccessoryView ),
103      mpTabView( i_pTabView ),
104      mpPreviewBox( nil ),
105      mpPreview( nil ),
106      mpPageEdit( nil ),
107      mpStepper( nil ),
108      mpPagesLabel( nil ),
109      maLocalizedStrings( VclResId( SV_PRINT_NATIVE_STRINGS ) )
110    {
111        mpState->bNeedRestart = false;
112        DBG_ASSERT( maLocalizedStrings.Count() >= 5, "resources not found !" );
113    }
114
115    rtl::OUString getMoreString()
116    {
117        return maLocalizedStrings.Count() >= 4
118               ? rtl::OUString( maLocalizedStrings.GetString( 3 ) )
119               : rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "More" ) );
120    }
121
122    rtl::OUString getPrintSelectionString()
123    {
124        return maLocalizedStrings.Count() >= 5
125               ? rtl::OUString( maLocalizedStrings.GetString( 4 ) )
126               : rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Print selection only" ) );
127    }
128
129    void updatePrintJob()
130    {
131        // TODO: refresh page count etc from mpController
132
133        // page range may have changed depending on options
134        sal_Int32 nPages = mpController->getFilteredPageCount();
135        #if OSL_DEBUG_LEVEL > 1
136        if( nPages != mnLastPageCount )
137            fprintf( stderr, "trouble: number of pages changed from %ld to %ld !\n", mnLastPageCount, nPages );
138        #endif
139        mpState->bNeedRestart = (nPages != mnLastPageCount);
140        NSTabViewItem* pItem = [mpTabView selectedTabViewItem];
141        if( pItem )
142            mpState->nLastPage = [mpTabView indexOfTabViewItem: pItem];
143        else
144            mpState->nLastPage = 0;
145        mnLastPageCount = nPages;
146        if( mpState->bNeedRestart )
147        {
148            #if 0
149            // Warning: bad hack ahead
150            // Apple does not give us a chance of changing the page count,
151            // and they don't let us cancel the dialog either
152            // hack: send a cancel message to the window displaying our views.
153            // this is ugly.
154            for( std::vector< NSObject* >::iterator it = maViews.begin(); it != maViews.end(); ++it )
155            {
156                if( [*it isKindOfClass: [NSView class]] )
157                {
158                    NSView* pView = (NSView*)*it;
159                    NSWindow* pWindow = [pView window];
160                    if( pWindow )
161                    {
162                        [pWindow cancelOperation: nil];
163                        break;
164                    }
165                }
166            }
167            #else
168            NSWindow* pWindow = [NSApp modalWindow];
169            if( pWindow )
170                [pWindow cancelOperation: nil];
171            #endif
172            [[mpOp printInfo] setJobDisposition: NSPrintCancelJob];
173        }
174        else
175        {
176            sal_Int32 nPage = [mpStepper intValue];
177            updatePreviewImage( nPage-1 );
178        }
179    }
180
181    int addNameTag( const rtl::OUString& i_rPropertyName )
182    {
183        int nNewTag = mnNextTag++;
184        maTagToPropertyName[ nNewTag ] = i_rPropertyName;
185        return nNewTag;
186    }
187
188    int addNameAndValueTag( const rtl::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, rtl::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            PropertyValue* pVal = mpController->getValue( name_it->second );
223            if( pVal )
224            {
225                pVal->Value <<= value_it->second;
226                updatePrintJob();
227            }
228        }
229    }
230
231    void changePropertyWithIntValue( int i_nTag, sal_Int64 i_nValue )
232    {
233        std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
234        if( name_it != maTagToPropertyName.end() )
235        {
236            PropertyValue* pVal = mpController->getValue( name_it->second );
237            if( pVal )
238            {
239                pVal->Value <<= i_nValue;
240                updatePrintJob();
241            }
242        }
243    }
244
245    void changePropertyWithBoolValue( int i_nTag, sal_Bool i_bValue )
246    {
247        std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
248        if( name_it != maTagToPropertyName.end() )
249        {
250            PropertyValue* pVal = mpController->getValue( name_it->second );
251            if( pVal )
252            {
253                // ugly
254                if( name_it->second.equalsAscii( "PrintContent" ) )
255                   pVal->Value <<= i_bValue ? sal_Int32(2) : sal_Int32(0);
256               else
257                   pVal->Value <<= i_bValue;
258                updatePrintJob();
259            }
260        }
261    }
262
263    void changePropertyWithStringValue( int i_nTag, const rtl::OUString& i_rValue )
264    {
265        std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag );
266        if( name_it != maTagToPropertyName.end() )
267        {
268            PropertyValue* pVal = mpController->getValue( name_it->second );
269            if( pVal )
270            {
271                pVal->Value <<= i_rValue;
272                updatePrintJob();
273            }
274        }
275    }
276
277    void updateEnableState()
278    {
279        for( std::vector< NSObject* >::iterator it = maViews.begin(); it != maViews.end(); ++it )
280        {
281            NSObject* pObj = *it;
282            NSControl* pCtrl = nil;
283            NSCell* pCell = nil;
284            if( [pObj isKindOfClass: [NSControl class]] )
285                pCtrl = (NSControl*)pObj;
286            else if( [pObj isKindOfClass: [NSCell class]] )
287                pCell = (NSCell*)pObj;
288
289            int nTag = pCtrl ? [pCtrl tag] :
290                       pCell ? [pCell tag] :
291                       -1;
292
293            std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( nTag );
294            if( name_it != maTagToPropertyName.end() && ! name_it->second.equalsAscii( "PrintContent" ) )
295            {
296                BOOL bEnabled = mpController->isUIOptionEnabled( name_it->second ) ? YES : NO;
297                if( pCtrl )
298                {
299                    [pCtrl setEnabled: bEnabled];
300                    NSView* pOther = getPair( pCtrl );
301                    if( pOther && [pOther isKindOfClass: [NSControl class]] )
302                        [(NSControl*)pOther setEnabled: bEnabled];
303                }
304                else if( pCell )
305                    [pCell setEnabled: bEnabled];
306
307            }
308        }
309    }
310
311    void updatePreviewImage( sal_Int32 i_nPage )
312    {
313        sal_Int32 nPages = mpController->getFilteredPageCount();
314        NSRect aViewFrame = [mpPreview frame];
315        Size aPixelSize( static_cast<long>(aViewFrame.size.width),
316                         static_cast<long>(aViewFrame.size.height) );
317        if( i_nPage >= 0 && nPages > i_nPage )
318        {
319            GDIMetaFile aMtf;
320            PrinterController::PageSize aPageSize( mpController->getFilteredPageFile( i_nPage, aMtf, false ) );
321            VirtualDevice aDev;
322            if( mpController->getPrinter()->GetPrinterOptions().IsConvertToGreyscales() )
323                aDev.SetDrawMode( aDev.GetDrawMode() | ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
324                                                         DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
325            // see salprn.cxx, currently we pretend to be a 720dpi device on printers
326            aDev.SetReferenceDevice( 720, 720 );
327            aDev.EnableOutput( TRUE );
328            Size aLogicSize( aDev.PixelToLogic( aPixelSize, MapMode( MAP_100TH_MM ) ) );
329            double fScaleX = double(aLogicSize.Width())/double(aPageSize.aSize.Width());
330            double fScaleY = double(aLogicSize.Height())/double(aPageSize.aSize.Height());
331            double fScale = (fScaleX < fScaleY) ? fScaleX : fScaleY;
332            // #i104784# if we render the page too small then rounding issues result in
333            // layout artifacts looking really bad. So scale the page unto a device that is not
334            // full page size but not too small either. This also results in much better visual
335            // quality of the preview, e.g. when its height approaches the number of text lines
336            if( fScale < 0.1 )
337                fScale = 0.1;
338            aMtf.WindStart();
339            aMtf.Scale( fScale, fScale );
340            aMtf.WindStart();
341            aLogicSize.Width() = long(double(aPageSize.aSize.Width()) * fScale);
342            aLogicSize.Height() = long(double(aPageSize.aSize.Height()) * fScale);
343            aPixelSize = aDev.LogicToPixel( aLogicSize, MapMode( MAP_100TH_MM ) );
344            aDev.SetOutputSizePixel( aPixelSize );
345            aMtf.WindStart();
346            aDev.SetMapMode( MapMode( MAP_100TH_MM ) );
347            aMtf.Play( &aDev, Point( 0, 0 ), aLogicSize );
348            aDev.EnableMapMode( FALSE );
349            Image aImage( aDev.GetBitmap( Point( 0, 0 ), aPixelSize ) );
350            NSImage* pImage = CreateNSImage( aImage );
351            [mpPreview setImage: [pImage autorelease]];
352        }
353        else
354            [mpPreview setImage: nil];
355    }
356
357    void setupPreview( ControlTarget* i_pCtrlTarget )
358    {
359        if( maLocalizedStrings.Count() < 3 )
360            return;
361
362        // get the preview control
363        NSRect aPreviewFrame = [mpAccessoryView frame];
364        aPreviewFrame.origin.x = 0;
365        aPreviewFrame.origin.y = 5;
366        aPreviewFrame.size.width = 190;
367        aPreviewFrame.size.height -= 7;
368
369        // create a box to put the preview controls in
370        mpPreviewBox = [[NSBox alloc] initWithFrame: aPreviewFrame];
371        [mpPreviewBox setTitle: [CreateNSString( maLocalizedStrings.GetString( 0 ) ) autorelease]];
372        [mpAccessoryView addSubview: [mpPreviewBox autorelease]];
373
374        // now create the image view of the preview
375        NSSize aMargins = [mpPreviewBox contentViewMargins];
376        aPreviewFrame.origin.x = 0;
377        aPreviewFrame.origin.y = 34;
378        aPreviewFrame.size.width -= 2*(aMargins.width+1);
379        aPreviewFrame.size.height -= 61;
380        mpPreview = [[NSImageView alloc] initWithFrame: aPreviewFrame];
381        [mpPreview setImageScaling: NSScaleProportionally];
382        [mpPreview setImageAlignment: NSImageAlignCenter];
383        [mpPreview setImageFrameStyle: NSImageFrameNone];
384        [mpPreviewBox addSubview: [mpPreview autorelease]];
385
386        // add a label
387        sal_Int32 nPages = mpController->getFilteredPageCount();
388        rtl::OUStringBuffer aBuf( 16 );
389        aBuf.appendAscii( "/ " );
390        aBuf.append( rtl::OUString::valueOf( nPages ) );
391
392        NSString* pText = CreateNSString( aBuf.makeStringAndClear() );
393        NSRect aTextRect = { { 100, 5 }, { 100, 22 } };
394        mpPagesLabel = [[NSTextView alloc] initWithFrame: aTextRect];
395        [mpPagesLabel setFont: [NSFont controlContentFontOfSize: 0]];
396        [mpPagesLabel setEditable: NO];
397        [mpPagesLabel setSelectable: NO];
398        [mpPagesLabel setDrawsBackground: NO];
399        [mpPagesLabel setString: [pText autorelease]];
400        [mpPagesLabel setToolTip: [CreateNSString( maLocalizedStrings.GetString( 2 ) ) autorelease]];
401        [mpPreviewBox addSubview: [mpPagesLabel autorelease]];
402
403        NSRect aFieldRect = { { 45, 5 }, { 35, 25 } };
404        mpPageEdit = [[NSTextField alloc] initWithFrame: aFieldRect];
405        [mpPageEdit setEditable: YES];
406        [mpPageEdit setSelectable: YES];
407        [mpPageEdit setDrawsBackground: YES];
408        [mpPageEdit setToolTip: [CreateNSString( maLocalizedStrings.GetString( 1 ) ) autorelease]];
409        [mpPreviewBox addSubview: [mpPageEdit autorelease]];
410
411        // add a stepper control
412        NSRect aStepFrame = { { 85, 5 }, { 15, 25 } };
413        mpStepper = [[NSStepper alloc] initWithFrame: aStepFrame];
414        [mpStepper setIncrement: 1];
415        [mpStepper setValueWraps: NO];
416        [mpPreviewBox addSubview: [mpStepper autorelease]];
417
418        // constrain the text field to decimal numbers
419        NSNumberFormatter* pFormatter = [[NSNumberFormatter alloc] init];
420        [pFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4];
421        [pFormatter setMinimum: [[NSNumber numberWithInt: 1] autorelease]];
422        [pFormatter setMaximum: [[NSNumber numberWithInt: nPages] autorelease]];
423        [pFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
424        [pFormatter setAllowsFloats: NO];
425        [pFormatter setMaximumFractionDigits: 0];
426        [mpPageEdit setFormatter: pFormatter];
427        [mpStepper setMinValue: 1];
428        [mpStepper setMaxValue: nPages];
429
430        [mpPageEdit setIntValue: 1];
431        [mpStepper setIntValue: 1];
432
433        // connect target and action
434        [mpStepper setTarget: i_pCtrlTarget];
435        [mpStepper setAction: @selector(triggeredPreview:)];
436        [mpPageEdit setTarget: i_pCtrlTarget];
437        [mpPageEdit setAction: @selector(triggeredPreview:)];
438
439        // set first preview image
440        updatePreviewImage( 0 );
441    }
442
443    void changePreview( NSObject* i_pSender )
444    {
445        if( [i_pSender isMemberOfClass: [NSTextField class]] )
446        {
447            NSTextField* pField = (NSTextField*)i_pSender;
448            if( pField == mpPageEdit ) // sanity check
449            {
450                sal_Int32 nPage = [pField intValue];
451                [mpStepper setIntValue: nPage];
452                updatePreviewImage( nPage-1 );
453            }
454        }
455        else if( [i_pSender isMemberOfClass: [NSStepper class]] )
456        {
457            NSStepper* pStepper = (NSStepper*)i_pSender;
458            if( pStepper == mpStepper ) // sanity check
459            {
460                sal_Int32 nPage = [pStepper intValue];
461                [mpPageEdit setIntValue: nPage];
462                updatePreviewImage( nPage-1 );
463            }
464        }
465    }
466};
467
468static void filterAccelerator( rtl::OUString& io_rText )
469{
470    rtl::OUStringBuffer aBuf( io_rText.getLength() );
471    for( sal_Int32 nIndex = 0; nIndex != -1; )
472        aBuf.append( io_rText.getToken( 0, '~', nIndex ) );
473    io_rText = aBuf.makeStringAndClear();
474}
475
476@implementation ControlTarget
477-(id)initWithControllerMap: (ControllerProperties*)pController
478{
479    if( (self = [super init]) )
480    {
481        mpController = pController;
482    }
483    return self;
484}
485-(void)triggered:(id)pSender
486{
487    if( [pSender isMemberOfClass: [NSPopUpButton class]] )
488    {
489        NSPopUpButton* pBtn = (NSPopUpButton*)pSender;
490        NSMenuItem* pSelected = [pBtn selectedItem];
491        if( pSelected )
492        {
493            int nTag = [pSelected tag];
494            mpController->changePropertyWithIntValue( nTag );
495        }
496    }
497    else if( [pSender isMemberOfClass: [NSButton class]] )
498    {
499        NSButton* pBtn = (NSButton*)pSender;
500        int nTag = [pBtn tag];
501        mpController->changePropertyWithBoolValue( nTag, [pBtn state] == NSOnState );
502    }
503    else if( [pSender isMemberOfClass: [NSMatrix class]] )
504    {
505        NSObject* pObj = [(NSMatrix*)pSender selectedCell];
506        if( [pObj isMemberOfClass: [NSButtonCell class]] )
507        {
508            NSButtonCell* pCell = (NSButtonCell*)pObj;
509            int nTag = [pCell tag];
510            mpController->changePropertyWithIntValue( nTag );
511        }
512    }
513    else if( [pSender isMemberOfClass: [NSTextField class]] )
514    {
515        NSTextField* pField = (NSTextField*)pSender;
516        int nTag = [pField tag];
517        rtl::OUString aValue = GetOUString( [pSender stringValue] );
518        mpController->changePropertyWithStringValue( nTag, aValue );
519    }
520    else
521    {
522        DBG_ERROR( "unsupported class" );
523    }
524    mpController->updateEnableState();
525}
526-(void)triggeredNumeric:(id)pSender
527{
528    if( [pSender isMemberOfClass: [NSTextField class]] )
529    {
530        NSTextField* pField = (NSTextField*)pSender;
531        int nTag = [pField tag];
532        sal_Int64 nValue = [pField intValue];
533
534        NSView* pOther = mpController->getPair( pField );
535        if( pOther )
536            [(NSControl*)pOther setIntValue: nValue];
537
538        mpController->changePropertyWithIntValue( nTag, nValue );
539    }
540    else if( [pSender isMemberOfClass: [NSStepper class]] )
541    {
542        NSStepper* pStep = (NSStepper*)pSender;
543        int nTag = [pStep tag];
544        sal_Int64 nValue = [pStep intValue];
545
546        NSView* pOther = mpController->getPair( pStep );
547        if( pOther )
548            [(NSControl*)pOther setIntValue: nValue];
549
550        mpController->changePropertyWithIntValue( nTag, nValue );
551    }
552    else
553    {
554        DBG_ERROR( "unsupported class" );
555    }
556    mpController->updateEnableState();
557}
558-(void)triggeredPreview:(id)pSender
559{
560    mpController->changePreview( pSender );
561}
562-(void)dealloc
563{
564    delete mpController;
565    [super dealloc];
566}
567@end
568
569struct ColumnItem
570{
571    NSControl*      pControl;
572    long            nOffset;
573    NSControl*      pSubControl;
574
575    ColumnItem( NSControl* i_pControl = nil, long i_nOffset = 0, NSControl* i_pSub = nil )
576    : pControl( i_pControl )
577    , nOffset( i_nOffset )
578    , pSubControl( i_pSub )
579    {}
580
581    long getWidth() const
582    {
583        long nWidth = 0;
584        if( pControl )
585        {
586            NSRect aCtrlRect = [pControl frame];
587            nWidth = aCtrlRect.size.width;
588            nWidth += nOffset;
589            if( pSubControl )
590            {
591                NSRect aSubRect = [pSubControl frame];
592                nWidth += aSubRect.size.width;
593                nWidth += aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width);
594            }
595        }
596        return nWidth;
597    }
598};
599
600static void adjustViewAndChildren( NSView* pView, NSSize& rMaxSize,
601                                   std::vector< ColumnItem >& rLeftColumn,
602                                   std::vector< ColumnItem >& rRightColumn
603                                  )
604{
605    // balance columns
606
607    // first get overall column widths
608    long nLeftWidth = 0;
609    long nRightWidth = 0;
610    for( size_t i = 0; i < rLeftColumn.size(); i++ )
611    {
612        long nW = rLeftColumn[i].getWidth();
613        if( nW > nLeftWidth )
614            nLeftWidth = nW;
615    }
616    for( size_t i = 0; i < rRightColumn.size(); i++ )
617    {
618        long nW = rRightColumn[i].getWidth();
619        if( nW > nRightWidth )
620            nRightWidth = nW;
621    }
622
623    // right align left column
624    for( size_t i = 0; i < rLeftColumn.size(); i++ )
625    {
626        if( rLeftColumn[i].pControl )
627        {
628            NSRect aCtrlRect = [rLeftColumn[i].pControl frame];
629            long nX = nLeftWidth - aCtrlRect.size.width;
630            if( rLeftColumn[i].pSubControl )
631            {
632                NSRect aSubRect = [rLeftColumn[i].pSubControl frame];
633                nX -= aSubRect.size.width + (aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width));
634                aSubRect.origin.x = nLeftWidth - aSubRect.size.width;
635                [rLeftColumn[i].pSubControl setFrame: aSubRect];
636            }
637            aCtrlRect.origin.x = nX;
638            [rLeftColumn[i].pControl setFrame: aCtrlRect];
639        }
640    }
641
642    // left align right column
643    for( size_t i = 0; i < rRightColumn.size(); i++ )
644    {
645        if( rRightColumn[i].pControl )
646        {
647            NSRect aCtrlRect = [rRightColumn[i].pControl frame];
648            long nX = nLeftWidth + 3;
649            if( rRightColumn[i].pSubControl )
650            {
651                NSRect aSubRect = [rRightColumn[i].pSubControl frame];
652                aSubRect.origin.x = nX + aSubRect.origin.x - aCtrlRect.origin.x;
653                [rRightColumn[i].pSubControl setFrame: aSubRect];
654            }
655            aCtrlRect.origin.x = nX;
656            [rRightColumn[i].pControl setFrame: aCtrlRect];
657        }
658    }
659
660    NSArray* pSubViews = [pView subviews];
661    unsigned int nViews = [pSubViews count];
662    NSRect aUnion = { { 0, 0 }, { 0, 0 } };
663
664    // get the combined frame of all subviews
665    for( unsigned int n = 0; n < nViews; n++ )
666    {
667        aUnion = NSUnionRect( aUnion, [[pSubViews objectAtIndex: n] frame] );
668    }
669
670    // move everything so it will fit
671    for( unsigned int n = 0; n < nViews; n++ )
672    {
673        NSView* pCurSubView = [pSubViews objectAtIndex: n];
674        NSRect aFrame = [pCurSubView frame];
675        aFrame.origin.x -= aUnion.origin.x - 5;
676        aFrame.origin.y -= aUnion.origin.y - 5;
677        [pCurSubView setFrame: aFrame];
678    }
679
680    // resize the view itself
681    aUnion.size.height += 10;
682    aUnion.size.width += 20;
683    [pView setFrameSize: aUnion.size];
684
685    if( aUnion.size.width > rMaxSize.width )
686        rMaxSize.width = aUnion.size.width;
687    if( aUnion.size.height > rMaxSize.height )
688        rMaxSize.height = aUnion.size.height;
689}
690
691static void adjustTabViews( NSTabView* pTabView, NSSize aTabSize )
692{
693    // loop over all contained tab pages
694    NSArray* pTabbedViews = [pTabView tabViewItems];
695    int nViews = [pTabbedViews count];
696    for( int i = 0; i < nViews; i++ )
697    {
698        NSTabViewItem* pItem = (NSTabViewItem*)[pTabbedViews objectAtIndex: i];
699        NSView* pView = [pItem view];
700        if( pView )
701        {
702            NSRect aRect = [pView frame];
703            double nDiff = aTabSize.height - aRect.size.height;
704            aRect.size = aTabSize;
705            [pView setFrame: aRect];
706
707            NSArray* pSubViews = [pView subviews];
708            unsigned int nSubViews = [pSubViews count];
709
710            // move everything up
711            for( unsigned int n = 0; n < nSubViews; n++ )
712            {
713                NSView* pCurSubView = [pSubViews objectAtIndex: n];
714                NSRect aFrame = [pCurSubView frame];
715                aFrame.origin.y += nDiff;
716                // give separators the correct width
717                // separators are currently the only NSBoxes we use
718                if( [pCurSubView isMemberOfClass: [NSBox class]] )
719                {
720                    aFrame.size.width = aTabSize.width - aFrame.origin.x - 10;
721                }
722                [pCurSubView setFrame: aFrame];
723            }
724        }
725    }
726}
727
728static NSControl* createLabel( const rtl::OUString& i_rText )
729{
730    NSString* pText = CreateNSString( i_rText );
731    NSRect aTextRect = { { 0, 0 }, {20, 15} };
732    NSTextField* pTextView = [[NSTextField alloc] initWithFrame: aTextRect];
733    [pTextView setFont: [NSFont controlContentFontOfSize: 0]];
734    [pTextView setEditable: NO];
735    [pTextView setSelectable: NO];
736    [pTextView setDrawsBackground: NO];
737    [pTextView setBordered: NO];
738    [pTextView setStringValue: pText];
739    [pTextView sizeToFit];
740    [pText release];
741    return pTextView;
742}
743
744static sal_Int32 findBreak( const rtl::OUString& i_rText, sal_Int32 i_nPos )
745{
746    sal_Int32 nRet = i_rText.getLength();
747    Reference< i18n::XBreakIterator > xBI( vcl::unohelper::CreateBreakIterator() );
748    if( xBI.is() )
749    {
750        i18n::Boundary aBoundary = xBI->getWordBoundary( i_rText, i_nPos,
751                                                         Application::GetSettings().GetLocale(),
752                                                         i18n::WordType::ANYWORD_IGNOREWHITESPACES,
753                                                         sal_True );
754        nRet = aBoundary.endPos;
755    }
756    return nRet;
757}
758
759static void linebreakCell( NSCell* pBtn, const rtl::OUString& i_rText )
760{
761    NSString* pText = CreateNSString( i_rText );
762    [pBtn setTitle: pText];
763    [pText release];
764    NSSize aSize = [pBtn cellSize];
765    if( aSize.width > 280 )
766    {
767        // need two lines
768        sal_Int32 nLen = i_rText.getLength();
769        sal_Int32 nIndex = nLen / 2;
770        nIndex = findBreak( i_rText, nIndex );
771        if( nIndex < nLen )
772        {
773            rtl::OUStringBuffer aBuf( i_rText );
774            aBuf.setCharAt( nIndex, '\n' );
775            pText = CreateNSString( aBuf.makeStringAndClear() );
776            [pBtn setTitle: pText];
777            [pText release];
778        }
779    }
780}
781
782static void addSubgroup( NSView* pCurParent, long& rCurY, const rtl::OUString& rText )
783{
784    NSControl* pTextView = createLabel( rText );
785    [pCurParent addSubview: [pTextView autorelease]];
786    NSRect aTextRect = [pTextView frame];
787    // move to nCurY
788    aTextRect.origin.y = rCurY - aTextRect.size.height;
789    [pTextView setFrame: aTextRect];
790
791    NSRect aSepRect = { { aTextRect.size.width + 1, aTextRect.origin.y }, { 100, 6 } };
792    NSBox* pBox = [[NSBox alloc] initWithFrame: aSepRect];
793    [pBox setBoxType: NSBoxSeparator];
794    [pCurParent addSubview: [pBox autorelease]];
795
796    // update nCurY
797    rCurY = aTextRect.origin.y - 5;
798}
799
800static void addBool( NSView* pCurParent, long& rCurX, long& rCurY, long nAttachOffset,
801                    const rtl::OUString& rText, sal_Bool bEnabled,
802                    const rtl::OUString& rProperty, sal_Bool bValue,
803                    std::vector<ColumnItem >& rRightColumn,
804                    ControllerProperties* pControllerProperties,
805                    ControlTarget* pCtrlTarget
806                    )
807{
808    NSRect aCheckRect = NSMakeRect( rCurX + nAttachOffset, 0, 0, 15);
809    NSButton* pBtn = [[NSButton alloc] initWithFrame: aCheckRect];
810    [pBtn setButtonType: NSSwitchButton];
811    [pBtn setState: bValue ? NSOnState : NSOffState];
812    if( ! bEnabled )
813        [pBtn setEnabled: NO];
814    linebreakCell( [pBtn cell], rText );
815    [pBtn sizeToFit];
816
817    rRightColumn.push_back( ColumnItem( pBtn ) );
818
819    // connect target
820    [pBtn setTarget: pCtrlTarget];
821    [pBtn setAction: @selector(triggered:)];
822    int nTag = pControllerProperties->addNameTag( rProperty );
823    pControllerProperties->addObservedControl( pBtn );
824    [pBtn setTag: nTag];
825
826    aCheckRect = [pBtn frame];
827    // #i115837# add a murphy factor; it can apparently occasionally happen
828    // that sizeToFit does not a perfect job and that the button linebreaks again
829    // if - and only if - there is already a '\n' contained in the text and the width
830    // is minimally of
831    aCheckRect.size.width += 1;
832
833    // move to rCurY
834    aCheckRect.origin.y = rCurY - aCheckRect.size.height;
835    [pBtn setFrame: aCheckRect];
836
837    [pCurParent addSubview: [pBtn autorelease]];
838
839    // update rCurY
840    rCurY = aCheckRect.origin.y - 5;
841}
842
843static void addRadio( NSView* pCurParent, long& rCurX, long& rCurY, long nAttachOffset,
844                     const rtl::OUString& rText,
845                     const rtl::OUString& rProperty, Sequence< rtl::OUString > rChoices, sal_Int32 nSelectValue,
846                     std::vector<ColumnItem >& rLeftColumn,
847                     std::vector<ColumnItem >& rRightColumn,
848                     ControllerProperties* pControllerProperties,
849                     ControlTarget* pCtrlTarget
850                     )
851{
852    sal_Int32 nOff = 0;
853    if( rText.getLength() )
854    {
855        // add a label
856        NSControl* pTextView = createLabel( rText );
857        NSRect aTextRect = [pTextView frame];
858        aTextRect.origin.x = rCurX + nAttachOffset;
859        [pCurParent addSubview: [pTextView autorelease]];
860
861        rLeftColumn.push_back( ColumnItem( pTextView ) );
862
863        // move to nCurY
864        aTextRect.origin.y = rCurY - aTextRect.size.height;
865        [pTextView setFrame: aTextRect];
866
867        // update nCurY
868        rCurY = aTextRect.origin.y - 5;
869
870        // indent the radio group relative to the text
871        // nOff = 20;
872    }
873
874    // setup radio matrix
875    NSButtonCell* pProto = [[NSButtonCell alloc] init];
876
877    NSRect aRadioRect = NSMakeRect( rCurX + nOff, 0, 280 - rCurX, 5*rChoices.getLength());
878    [pProto setTitle: @"RadioButtonGroup"];
879    [pProto setButtonType: NSRadioButton];
880    NSMatrix* pMatrix = [[NSMatrix alloc] initWithFrame: aRadioRect
881                                          mode: NSRadioModeMatrix
882                                          prototype: (NSCell*)pProto
883                                          numberOfRows: rChoices.getLength()
884                                          numberOfColumns: 1];
885    // set individual titles
886    NSArray* pCells = [pMatrix cells];
887    for( sal_Int32 m = 0; m < rChoices.getLength(); m++ )
888    {
889        NSCell* pCell = [pCells objectAtIndex: m];
890        filterAccelerator( rChoices[m] );
891        linebreakCell( pCell, rChoices[m] );
892        // connect target and action
893        [pCell setTarget: pCtrlTarget];
894        [pCell setAction: @selector(triggered:)];
895        int nTag = pControllerProperties->addNameAndValueTag( rProperty, m );
896        pControllerProperties->addObservedControl( pCell );
897        [pCell setTag: nTag];
898        // set current selection
899        if( nSelectValue == m )
900            [pMatrix selectCellAtRow: m column: 0];
901    }
902    [pMatrix sizeToFit];
903    aRadioRect = [pMatrix frame];
904
905    // move it down, so it comes to the correct position
906    aRadioRect.origin.y = rCurY - aRadioRect.size.height;
907    [pMatrix setFrame: aRadioRect];
908    [pCurParent addSubview: [pMatrix autorelease]];
909
910    rRightColumn.push_back( ColumnItem( pMatrix ) );
911
912    // update nCurY
913    rCurY = aRadioRect.origin.y - 5;
914
915    [pProto release];
916}
917
918static void addList( NSView* pCurParent, long& rCurX, long& rCurY, long /*nAttachOffset*/,
919                    const rtl::OUString& rText,
920                    const rtl::OUString& rProperty, const Sequence< rtl::OUString > rChoices, sal_Int32 nSelectValue,
921                    std::vector<ColumnItem >& rLeftColumn,
922                    std::vector<ColumnItem >& rRightColumn,
923                    ControllerProperties* pControllerProperties,
924                    ControlTarget* pCtrlTarget
925                    )
926{
927    // don't indent attached lists, looks bad in the existing cases
928    NSControl* pTextView = createLabel( rText );
929    [pCurParent addSubview: [pTextView autorelease]];
930    rLeftColumn.push_back( ColumnItem( pTextView ) );
931    NSRect aTextRect = [pTextView frame];
932    aTextRect.origin.x = rCurX /* + nAttachOffset*/;
933
934    // don't indent attached lists, looks bad in the existing cases
935    NSRect aBtnRect = NSMakeRect( rCurX /*+ nAttachOffset*/ + aTextRect.size.width, 0, 0, 15);
936    NSPopUpButton* pBtn = [[NSPopUpButton alloc] initWithFrame: aBtnRect pullsDown: NO];
937
938    // iterate options
939    for( sal_Int32 m = 0; m < rChoices.getLength(); m++ )
940    {
941        NSString* pItemText = CreateNSString( rChoices[m] );
942        [pBtn addItemWithTitle: pItemText];
943        NSMenuItem* pItem = [pBtn itemWithTitle: pItemText];
944        int nTag = pControllerProperties->addNameAndValueTag( rProperty, m );
945        [pItem setTag: nTag];
946        [pItemText release];
947    }
948
949    [pBtn selectItemAtIndex: nSelectValue];
950
951    // add the button to observed controls for enabled state changes
952    // also add a tag just for this purpose
953    pControllerProperties->addObservedControl( pBtn );
954    [pBtn setTag: pControllerProperties->addNameTag( rProperty )];
955
956    [pBtn sizeToFit];
957    [pCurParent addSubview: [pBtn autorelease]];
958
959    rRightColumn.push_back( ColumnItem( pBtn ) );
960
961    // connect target and action
962    [pBtn setTarget: pCtrlTarget];
963    [pBtn setAction: @selector(triggered:)];
964
965    // move to nCurY
966    aBtnRect = [pBtn frame];
967    aBtnRect.origin.y = rCurY - aBtnRect.size.height;
968    [pBtn setFrame: aBtnRect];
969
970    // align label
971    aTextRect.origin.y = aBtnRect.origin.y + (aBtnRect.size.height - aTextRect.size.height)/2;
972    [pTextView setFrame: aTextRect];
973
974    // update rCurY
975    rCurY = aBtnRect.origin.y - 5;
976}
977
978static void addEdit( NSView* pCurParent, long& rCurX, long& rCurY, long nAttachOffset,
979                    const rtl::OUString rCtrlType,
980                    const rtl::OUString& rText,
981                    const rtl::OUString& rProperty, const PropertyValue* pValue,
982                    sal_Int64 nMinValue, sal_Int64 nMaxValue,
983                    std::vector<ColumnItem >& rLeftColumn,
984                    std::vector<ColumnItem >& rRightColumn,
985                    ControllerProperties* pControllerProperties,
986                    ControlTarget* pCtrlTarget
987                    )
988{
989    sal_Int32 nOff = 0;
990    if( rText.getLength() )
991    {
992        // add a label
993        NSControl* pTextView = createLabel( rText );
994        [pCurParent addSubview: [pTextView autorelease]];
995
996        rLeftColumn.push_back( ColumnItem( pTextView ) );
997
998        // move to nCurY
999        NSRect aTextRect = [pTextView frame];
1000        aTextRect.origin.x = rCurX + nAttachOffset;
1001        aTextRect.origin.y = rCurY - aTextRect.size.height;
1002        [pTextView setFrame: aTextRect];
1003
1004        // update nCurY
1005        rCurY = aTextRect.origin.y - 5;
1006
1007        // and set the offset for the real edit field
1008        nOff = aTextRect.size.width + 5;
1009    }
1010
1011    NSRect aFieldRect = NSMakeRect( rCurX + nOff + nAttachOffset, 0, 100, 25);
1012    NSTextField* pFieldView = [[NSTextField alloc] initWithFrame: aFieldRect];
1013    [pFieldView setEditable: YES];
1014    [pFieldView setSelectable: YES];
1015    [pFieldView setDrawsBackground: YES];
1016    [pFieldView sizeToFit]; // FIXME: this does nothing
1017    [pCurParent addSubview: [pFieldView autorelease]];
1018
1019    rRightColumn.push_back( ColumnItem( pFieldView ) );
1020
1021    // add the field to observed controls for enabled state changes
1022    // also add a tag just for this purpose
1023    pControllerProperties->addObservedControl( pFieldView );
1024    int nTag = pControllerProperties->addNameTag( rProperty );
1025    [pFieldView setTag: nTag];
1026    // pControllerProperties->addNamedView( pFieldView, aPropertyName );
1027
1028    // move to nCurY
1029    aFieldRect.origin.y = rCurY - aFieldRect.size.height;
1030    [pFieldView setFrame: aFieldRect];
1031
1032    if( rCtrlType.equalsAscii( "Range" ) )
1033    {
1034        // add a stepper control
1035        NSRect aStepFrame = NSMakeRect(
1036                                aFieldRect.origin.x + aFieldRect.size.width + 5, aFieldRect.origin.y,
1037                                15, aFieldRect.size.height);
1038        NSStepper* pStep = [[NSStepper alloc] initWithFrame: aStepFrame];
1039        [pStep setIncrement: 1];
1040        [pStep setValueWraps: NO];
1041        [pStep setTag: nTag];
1042        [pCurParent addSubview: [pStep autorelease]];
1043
1044        rRightColumn.back().pSubControl = pStep;
1045
1046        pControllerProperties->addObservedControl( pStep );
1047        [pStep setTarget: pCtrlTarget];
1048        [pStep setAction: @selector(triggered:)];
1049
1050        // constrain the text field to decimal numbers
1051        NSNumberFormatter* pFormatter = [[NSNumberFormatter alloc] init];
1052        [pFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4];
1053        [pFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
1054        [pFormatter setAllowsFloats: NO];
1055        [pFormatter setMaximumFractionDigits: 0];
1056        if( nMinValue != nMaxValue )
1057        {
1058            [pFormatter setMinimum: [[NSNumber numberWithInt: nMinValue] autorelease]];
1059            [pStep setMinValue: nMinValue];
1060            [pFormatter setMaximum: [[NSNumber numberWithInt: nMaxValue] autorelease]];
1061            [pStep setMaxValue: nMaxValue];
1062        }
1063        [pFieldView setFormatter: pFormatter];
1064
1065        sal_Int64 nSelectVal = 0;
1066        if( pValue && pValue->Value.hasValue() )
1067            pValue->Value >>= nSelectVal;
1068
1069        [pFieldView setIntValue: nSelectVal];
1070        [pStep setIntValue: nSelectVal];
1071
1072        pControllerProperties->addViewPair( pFieldView, pStep );
1073        // connect target and action
1074        [pFieldView setTarget: pCtrlTarget];
1075        [pFieldView setAction: @selector(triggeredNumeric:)];
1076        [pStep setTarget: pCtrlTarget];
1077        [pStep setAction: @selector(triggeredNumeric:)];
1078    }
1079    else
1080    {
1081        // connect target and action
1082        [pFieldView setTarget: pCtrlTarget];
1083        [pFieldView setAction: @selector(triggered:)];
1084
1085        if( pValue && pValue->Value.hasValue() )
1086        {
1087            rtl::OUString aValue;
1088            pValue->Value >>= aValue;
1089            if( aValue.getLength() )
1090            {
1091                NSString* pText = CreateNSString( aValue );
1092                [pFieldView setStringValue: pText];
1093                [pText release];
1094            }
1095        }
1096    }
1097
1098    // update nCurY
1099    rCurY = aFieldRect.origin.y - 5;
1100}
1101
1102@implementation AquaPrintAccessoryView
1103+(NSObject*)setupPrinterPanel: (NSPrintOperation*)pOp withController: (vcl::PrinterController*)pController  withState: (PrintAccessoryViewState*)pState
1104{
1105    const Sequence< PropertyValue >& rOptions( pController->getUIOptions() );
1106    if( rOptions.getLength() == 0 )
1107        return nil;
1108
1109    NSView* pCurParent = 0;
1110    long nCurY = 0;
1111    long nCurX = 0;
1112    NSRect aViewFrame = { { 0, 0 }, {600, 400 } };
1113    NSRect aTabViewFrame = { { 190, 0 }, {410, 400 } };
1114    NSSize aMaxTabSize = { 0, 0 };
1115    NSView* pAccessoryView = [[NSView alloc] initWithFrame: aViewFrame];
1116    NSTabView* pTabView = [[NSTabView alloc] initWithFrame: aTabViewFrame];
1117    [pAccessoryView addSubview: [pTabView autorelease]];
1118
1119    sal_Bool bIgnoreSubgroup = sal_False;
1120
1121    ControllerProperties* pControllerProperties = new ControllerProperties( pController, pOp, pAccessoryView, pTabView, pState );
1122    ControlTarget* pCtrlTarget = [[ControlTarget alloc] initWithControllerMap: pControllerProperties];
1123
1124    std::vector< ColumnItem > aLeftColumn, aRightColumn;
1125
1126    // ugly:
1127    // prepend a "selection" checkbox if the properties have such a selection in PrintContent
1128    bool bAddSelectionCheckBox = false, bSelectionBoxEnabled = false, bSelectionBoxChecked = false;
1129    for( int i = 0; i < rOptions.getLength(); i++ )
1130    {
1131        Sequence< beans::PropertyValue > aOptProp;
1132        rOptions[i].Value >>= aOptProp;
1133
1134        rtl::OUString aCtrlType;
1135        rtl::OUString aPropertyName;
1136        Sequence< rtl::OUString > aChoices;
1137        Sequence< sal_Bool > aChoicesDisabled;
1138        sal_Int32 aSelectionChecked = 0;
1139        for( int n = 0; n < aOptProp.getLength(); n++ )
1140        {
1141            const beans::PropertyValue& rEntry( aOptProp[ n ] );
1142            if( rEntry.Name.equalsAscii( "ControlType" ) )
1143            {
1144                rEntry.Value >>= aCtrlType;
1145            }
1146            else if( rEntry.Name.equalsAscii( "Choices" ) )
1147            {
1148                rEntry.Value >>= aChoices;
1149            }
1150            else if( rEntry.Name.equalsAscii( "ChoicesDisabled" ) )
1151            {
1152                rEntry.Value >>= aChoicesDisabled;
1153            }
1154            else if( rEntry.Name.equalsAscii( "Property" ) )
1155            {
1156                PropertyValue aVal;
1157                rEntry.Value >>= aVal;
1158                aPropertyName = aVal.Name;
1159                if( aPropertyName.equalsAscii( "PrintContent" ) )
1160                    aVal.Value >>= aSelectionChecked;
1161            }
1162        }
1163        if( aCtrlType.equalsAscii( "Radio" ) &&
1164            aPropertyName.equalsAscii( "PrintContent" ) &&
1165            aChoices.getLength() > 2 )
1166        {
1167            bAddSelectionCheckBox = true;
1168            bSelectionBoxEnabled = aChoicesDisabled.getLength() < 2 || ! aChoicesDisabled[2];
1169            bSelectionBoxChecked = (aSelectionChecked==2);
1170            break;
1171        }
1172    }
1173
1174    for( int i = 0; i < rOptions.getLength(); i++ )
1175    {
1176        Sequence< beans::PropertyValue > aOptProp;
1177        rOptions[i].Value >>= aOptProp;
1178
1179        // extract ui element
1180        bool bEnabled = true;
1181        rtl::OUString aCtrlType;
1182        rtl::OUString aText;
1183        rtl::OUString aPropertyName;
1184        rtl::OUString aGroupHint;
1185        Sequence< rtl::OUString > aChoices;
1186        sal_Int64 nMinValue = 0, nMaxValue = 0;
1187        long nAttachOffset = 0;
1188        sal_Bool bIgnore = sal_False;
1189
1190        for( int n = 0; n < aOptProp.getLength(); n++ )
1191        {
1192            const beans::PropertyValue& rEntry( aOptProp[ n ] );
1193            if( rEntry.Name.equalsAscii( "Text" ) )
1194            {
1195                rEntry.Value >>= aText;
1196                filterAccelerator( aText );
1197            }
1198            else if( rEntry.Name.equalsAscii( "ControlType" ) )
1199            {
1200                rEntry.Value >>= aCtrlType;
1201            }
1202            else if( rEntry.Name.equalsAscii( "Choices" ) )
1203            {
1204                rEntry.Value >>= aChoices;
1205            }
1206            else if( rEntry.Name.equalsAscii( "Property" ) )
1207            {
1208                PropertyValue aVal;
1209                rEntry.Value >>= aVal;
1210                aPropertyName = aVal.Name;
1211            }
1212            else if( rEntry.Name.equalsAscii( "Enabled" ) )
1213            {
1214                sal_Bool bValue = sal_True;
1215                rEntry.Value >>= bValue;
1216                bEnabled = bValue;
1217            }
1218            else if( rEntry.Name.equalsAscii( "MinValue" ) )
1219            {
1220                rEntry.Value >>= nMinValue;
1221            }
1222            else if( rEntry.Name.equalsAscii( "MaxValue" ) )
1223            {
1224                rEntry.Value >>= nMaxValue;
1225            }
1226            else if( rEntry.Name.equalsAscii( "AttachToDependency" ) )
1227            {
1228                nAttachOffset = 20;
1229            }
1230            else if( rEntry.Name.equalsAscii( "InternalUIOnly" ) )
1231            {
1232                rEntry.Value >>= bIgnore;
1233            }
1234            else if( rEntry.Name.equalsAscii( "GroupingHint" ) )
1235            {
1236                rEntry.Value >>= aGroupHint;
1237            }
1238        }
1239
1240        if( aCtrlType.equalsAscii( "Group" ) ||
1241            aCtrlType.equalsAscii( "Subgroup" ) ||
1242            aCtrlType.equalsAscii( "Radio" ) ||
1243            aCtrlType.equalsAscii( "List" )  ||
1244            aCtrlType.equalsAscii( "Edit" )  ||
1245            aCtrlType.equalsAscii( "Range" )  ||
1246            aCtrlType.equalsAscii( "Bool" ) )
1247        {
1248            // since our build target is MacOSX 10.4 we can have only one accessory view
1249            // so we have a single accessory view that is tabbed for grouping
1250            if( aCtrlType.equalsAscii( "Group" )
1251                || ! pCurParent
1252                || ( aCtrlType.equalsAscii( "Subgroup" ) && nCurY < -250 && ! bIgnore )
1253               )
1254            {
1255                rtl::OUString aGroupTitle( aText );
1256                if( aCtrlType.equalsAscii( "Subgroup" ) )
1257                    aGroupTitle = pControllerProperties->getMoreString();
1258                // set size of current parent
1259                if( pCurParent )
1260                    adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn );
1261
1262                // new tab item
1263                if( ! aText.getLength() )
1264                    aText = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "OOo" ) );
1265                NSString* pLabel = CreateNSString( aGroupTitle );
1266                NSTabViewItem* pItem = [[NSTabViewItem alloc] initWithIdentifier: pLabel ];
1267                [pItem setLabel: pLabel];
1268                [pTabView addTabViewItem: pItem];
1269                pCurParent = [[NSView alloc] initWithFrame: aTabViewFrame];
1270                [pItem setView: pCurParent];
1271                [pLabel release];
1272
1273                // reset indent
1274                nCurX = 20;
1275                // reset Y
1276                nCurY = 0;
1277                // clear columns
1278                aLeftColumn.clear();
1279                aRightColumn.clear();
1280
1281                if( bAddSelectionCheckBox )
1282                {
1283                    addBool( pCurParent, nCurX, nCurY, 0,
1284                             pControllerProperties->getPrintSelectionString(), bSelectionBoxEnabled,
1285                             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintContent" ) ), bSelectionBoxChecked,
1286                             aRightColumn, pControllerProperties, pCtrlTarget );
1287                    bAddSelectionCheckBox = false;
1288                }
1289            }
1290
1291            if( aCtrlType.equalsAscii( "Subgroup" ) && pCurParent )
1292            {
1293                bIgnoreSubgroup = bIgnore;
1294                if( bIgnore )
1295                    continue;
1296
1297                addSubgroup( pCurParent, nCurY, aText );
1298            }
1299            else if( bIgnoreSubgroup || bIgnore )
1300            {
1301                continue;
1302            }
1303            else if( aCtrlType.equalsAscii( "Bool" ) && pCurParent )
1304            {
1305                sal_Bool bVal = sal_False;
1306                PropertyValue* pVal = pController->getValue( aPropertyName );
1307                if( pVal )
1308                    pVal->Value >>= bVal;
1309                addBool( pCurParent, nCurX, nCurY, nAttachOffset,
1310                         aText, true, aPropertyName, bVal,
1311                         aRightColumn, pControllerProperties, pCtrlTarget );
1312            }
1313            else if( aCtrlType.equalsAscii( "Radio" ) && pCurParent )
1314            {
1315                // get currently selected value
1316                sal_Int32 nSelectVal = 0;
1317                PropertyValue* pVal = pController->getValue( aPropertyName );
1318                if( pVal && pVal->Value.hasValue() )
1319                    pVal->Value >>= nSelectVal;
1320
1321                addRadio( pCurParent, nCurX, nCurY, nAttachOffset,
1322                          aText, aPropertyName, aChoices, nSelectVal,
1323                          aLeftColumn, aRightColumn,
1324                          pControllerProperties, pCtrlTarget );
1325            }
1326            else if( aCtrlType.equalsAscii( "List" ) && pCurParent )
1327            {
1328                PropertyValue* pVal = pController->getValue( aPropertyName );
1329                sal_Int32 aSelectVal = 0;
1330                if( pVal && pVal->Value.hasValue() )
1331                    pVal->Value >>= aSelectVal;
1332
1333                addList( pCurParent, nCurX, nCurY, nAttachOffset,
1334                         aText, aPropertyName, aChoices, aSelectVal,
1335                         aLeftColumn, aRightColumn,
1336                         pControllerProperties, pCtrlTarget );
1337            }
1338            else if( (aCtrlType.equalsAscii( "Edit" ) || aCtrlType.equalsAscii( "Range" )) && pCurParent )
1339            {
1340                // current value
1341                PropertyValue* pVal = pController->getValue( aPropertyName );
1342                addEdit( pCurParent, nCurX, nCurY, nAttachOffset,
1343                         aCtrlType, aText, aPropertyName, pVal,
1344                         nMinValue, nMaxValue,
1345                         aLeftColumn, aRightColumn,
1346                         pControllerProperties, pCtrlTarget );
1347            }
1348        }
1349        else
1350        {
1351            DBG_ERROR( "Unsupported UI option" );
1352        }
1353    }
1354
1355    pControllerProperties->updateEnableState();
1356    adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn );
1357
1358    // leave some space for the preview
1359    if( aMaxTabSize.height < 200 )
1360        aMaxTabSize.height = 200;
1361
1362    // now reposition everything again so it is upper bound
1363    adjustTabViews( pTabView, aMaxTabSize );
1364
1365    // find the minimum needed tab size
1366    NSSize aTabCtrlSize = [pTabView minimumSize];
1367    aTabCtrlSize.height += aMaxTabSize.height + 10;
1368    if( aTabCtrlSize.width < aMaxTabSize.width + 10 )
1369        aTabCtrlSize.width = aMaxTabSize.width + 10;
1370    [pTabView setFrameSize: aTabCtrlSize];
1371    aViewFrame.size.width = aTabCtrlSize.width + aTabViewFrame.origin.x;
1372    aViewFrame.size.height = aTabCtrlSize.height + aTabViewFrame.origin.y;
1373    [pAccessoryView setFrameSize: aViewFrame.size];
1374
1375    pControllerProperties->setupPreview( pCtrlTarget );
1376
1377    // set the accessory view
1378    [pOp setAccessoryView: [pAccessoryView autorelease]];
1379
1380    // set the current selecte tab item
1381    if( pState->nLastPage >= 0 && pState->nLastPage < [pTabView numberOfTabViewItems] )
1382        [pTabView selectTabViewItemAtIndex: pState->nLastPage];
1383
1384    return pCtrlTarget;
1385}
1386
1387@end
1388