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