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