/************************************************************************ * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: aquaprintview.mm,v $ * $Revision: 1.5.56.1 $ * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_vcl.hxx" #include "aquaprintview.h" #include "salinst.h" #include "vcl/print.hxx" #include using namespace vcl; using namespace com::sun::star; using namespace com::sun::star::beans; using namespace com::sun::star::uno; #if 0 /* below is some dark magic to inherit an NSViewController if running on MacOS 10.5. However this is futile since as long as our base line is 10.4 can use the deprecated method NSPrintOperation:setAccessoryView anyway. The problem here is that as long as we're linked for 10.4, the print panel will never show a preview and our accessory view at the same time. This is awful, but since it was dictated by Apple that IT MUST BE SO it cannot be bad Anyway the code below (the load method) is really ugly, so perhaps it's better this way. */ #import APPKIT_EXTERN NSString *NSPrintPanelAccessorySummaryItemNameKey; APPKIT_EXTERN NSString *NSPrintPanelAccessorySummaryItemDescriptionKey; #include "osl/module.h" @interface AquaPrintViewController : NSObject { NSArray* pAry; } +(void)load; -(id)init; -(void)dealloc; -(NSArray *)localizedSummaryItems; -(NSSet*)keyPathsForValuesAffectingPreview; @end @implementation AquaPrintViewController +(void)load { struct objc_class *pClass = (struct objc_class *)[self class]; Class superclass = NSClassFromString(@"NSViewController"); if(superclass != NULL) { pClass->super_class = superclass; pClass->instance_size += superclass->instance_size; } } -(id)init { if( (self = [super performSelector: @selector(initWithNibName:bundle:) withObject: nil withObject: nil]) ) { NSObject* pKeys[] = { NSPrintPanelAccessorySummaryItemNameKey, NSPrintPanelAccessorySummaryItemDescriptionKey }; NSObject* pVals[] = { @"the Name", @"the summary value" }; NSDictionary* pDict = [NSDictionary dictionaryWithObjects: pVals forKeys: pKeys count: 2]; pAry = [NSArray arrayWithObject: pDict]; } return self; } -(void)dealloc { [pAry release]; [super dealloc]; } -(NSArray *)localizedSummaryItems { return pAry; } -(NSSet*)keyPathsForValuesAffectingPreview { return [NSSet set]; } @end #endif class ListenerProperties { vcl::PrinterListener* mpListener; std::map< int, rtl::OUString > maTagToPropertyName; std::map< int, rtl::OUString > maTagToValueName; int mnNextTag; public: ListenerProperties( vcl::PrinterListener* i_pListener ) : mpListener( i_pListener ), mnNextTag( 0 ) {} void updatePrintJob() { // TODO: refresh page count etc from mpListener } int addNameTag( const rtl::OUString& i_rPropertyName ) { int nNewTag = mnNextTag++; maTagToPropertyName[ nNewTag ] = i_rPropertyName; return nNewTag; } int addNameAndValueTag( const rtl::OUString& i_rPropertyName, const rtl::OUString& i_rValue ) { int nNewTag = mnNextTag++; maTagToPropertyName[ nNewTag ] = i_rPropertyName; maTagToValueName[ nNewTag ] = i_rValue; return nNewTag; } void changePropertyWithNamedValue( int i_nTag ) { std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag ); std::map< int, rtl::OUString >::const_iterator value_it = maTagToValueName.find( i_nTag ); if( name_it != maTagToPropertyName.end() && value_it != maTagToValueName.end() ) { PropertyValue* pVal = mpListener->getValue( name_it->second ); if( pVal ) { pVal->Value <<= value_it->second; updatePrintJob(); } } } void changePropertyWithBoolValue( int i_nTag, sal_Bool i_bValue ) { std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag ); if( name_it != maTagToPropertyName.end() ) { PropertyValue* pVal = mpListener->getValue( name_it->second ); if( pVal ) { pVal->Value <<= i_bValue; updatePrintJob(); } } } }; @interface ControlTarget : NSObject { ListenerProperties* mpListener; } -(id)initWithListenerMap: (ListenerProperties*)pListener; -(void)triggered:(id)pSender; -(void)dealloc; @end @implementation ControlTarget -(id)initWithListenerMap: (ListenerProperties*)pListener { if( (self = [super init]) ) { mpListener = pListener; } return self; } -(void)triggered:(id)pSender; { if( [pSender isMemberOfClass: [NSPopUpButton class]] ) { NSPopUpButton* pBtn = (NSPopUpButton*)pSender; NSMenuItem* pSelected = [pBtn selectedItem]; if( pSelected ) { int nTag = [pSelected tag]; mpListener->changePropertyWithNamedValue( nTag ); } } else if( [pSender isMemberOfClass: [NSButton class]] ) { NSButton* pBtn = (NSButton*)pSender; int nTag = [pBtn tag]; mpListener->changePropertyWithBoolValue( nTag, [pBtn state] == NSOnState ); } else if( [pSender isMemberOfClass: [NSMatrix class]] ) { NSObject* pObj = [(NSMatrix*)pSender selectedCell]; if( [pObj isMemberOfClass: [NSButtonCell class]] ) { NSButtonCell* pCell = (NSButtonCell*)pObj; int nTag = [pCell tag]; mpListener->changePropertyWithNamedValue( nTag ); } } else { DBG_ERROR( "unsupported class" ); } } -(void)dealloc { delete mpListener; [super dealloc]; } @end static void adjustViewAndChildren( NSView* pView, NSSize& rMaxSize ) { NSArray* pSubViews = [pView subviews]; unsigned int nViews = [pSubViews count]; NSRect aUnion = { { 0, 0 }, { 0, 0 } }; // get the combined frame of all subviews for( unsigned int n = 0; n < nViews; n++ ) { aUnion = NSUnionRect( aUnion, [[pSubViews objectAtIndex: n] frame] ); } // move everything so it will fit for( unsigned int n = 0; n < nViews; n++ ) { NSView* pCurSubView = [pSubViews objectAtIndex: n]; NSRect aFrame = [pCurSubView frame]; aFrame.origin.x -= aUnion.origin.x - 5; aFrame.origin.y -= aUnion.origin.y - 5; [pCurSubView setFrame: aFrame]; } // resize the view itself aUnion.size.height += 10; aUnion.size.width += 10; [pView setFrameSize: aUnion.size]; if( aUnion.size.width > rMaxSize.width ) rMaxSize.width = aUnion.size.width; if( aUnion.size.height > rMaxSize.height ) rMaxSize.height = aUnion.size.height; } @implementation AquaPrintAccessoryView +(NSObject*)setupPrinterPanel: (NSPrintOperation*)pOp withListener: (vcl::PrinterListener*)pListener; { ListenerProperties* pListenerProperties = new ListenerProperties( pListener ); ControlTarget* pCtrlTarget = [[ControlTarget alloc] initWithListenerMap: pListenerProperties]; NSView* pCurParent = 0; long nCurY = 0; long nCurX = 0; NSRect aViewFrame = { { 0, 0 }, {400, 400 } }; NSSize aMaxTabSize = { 0, 0 }; NSTabView* pTabView = [[NSTabView alloc] initWithFrame: aViewFrame]; const Sequence< PropertyValue >& rOptions( pListener->getUIOptions() ); for( int i = 0; i < rOptions.getLength(); i++ ) { Sequence< beans::PropertyValue > aOptProp; rOptions[i].Value >>= aOptProp; // extract ui element bool bEnabled = true; rtl::OUString aCtrlType; rtl::OUString aText; rtl::OUString aPropertyName; Sequence< rtl::OUString > aChoices; for( int n = 0; n < aOptProp.getLength(); n++ ) { const beans::PropertyValue& rEntry( aOptProp[ n ] ); if( rEntry.Name.equalsAscii( "Text" ) ) { rEntry.Value >>= aText; } else if( rEntry.Name.equalsAscii( "ControlType" ) ) { rEntry.Value >>= aCtrlType; } else if( rEntry.Name.equalsAscii( "Choices" ) ) { rEntry.Value >>= aChoices; } else if( rEntry.Name.equalsAscii( "Property" ) ) { PropertyValue aVal; rEntry.Value >>= aVal; aPropertyName = aVal.Name; } else if( rEntry.Name.equalsAscii( "Enabled" ) ) { sal_Bool bValue = sal_True; rEntry.Value >>= bValue; bEnabled = bValue; } } if( aCtrlType.equalsAscii( "Group" ) || aCtrlType.equalsAscii( "Subgroup" ) || aCtrlType.equalsAscii( "Radio" ) || aCtrlType.equalsAscii( "List" ) || aCtrlType.equalsAscii( "Bool" ) ) { // since our build target is MacOSX 10.4 we can have only one accessory view // so we have a single accessory view that is tabbed for grouping if( aCtrlType.equalsAscii( "Group" ) || ! pCurParent ) { // set size of current parent if( pCurParent ) adjustViewAndChildren( pCurParent, aMaxTabSize ); // new tab item if( ! aText.getLength() ) aText = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "OOo" ) ); NSString* pLabel = CreateNSString( aText ); NSTabViewItem* pItem = [[NSTabViewItem alloc] initWithIdentifier: pLabel ]; [pItem setLabel: pLabel]; [pTabView addTabViewItem: pItem]; pCurParent = [[NSView alloc] initWithFrame: aViewFrame]; [pItem setView: pCurParent]; [pLabel release]; // reset indent nCurX = 0; } if( aCtrlType.equalsAscii( "Subgroup" ) && pCurParent ) { NSString* pText = CreateNSString( aText ); NSRect aTextRect = { { 0, 0 }, { 300, 15 } }; NSTextView* pTextView = [[NSTextView alloc] initWithFrame: aTextRect]; [pTextView setEditable: NO]; [pTextView setSelectable: NO]; [pTextView setDrawsBackground: NO]; [pTextView setString: pText]; [pTextView sizeToFit]; // FIXME: this does nothing [pCurParent addSubview: pTextView]; aTextRect = [pTextView frame]; // move to nCurY aTextRect.origin.y = nCurY - aTextRect.size.height; [pTextView setFrame: aTextRect]; // update nCurY nCurY = aTextRect.origin.y - 5; // set indent nCurX = 20; // cleanup [pText release]; } else if( aCtrlType.equalsAscii( "Bool" ) && pCurParent ) { NSString* pText = CreateNSString( aText ); NSRect aCheckRect = { { nCurX, 0 }, { 0, 15 } }; NSButton* pBtn = [[NSButton alloc] initWithFrame: aCheckRect]; [pBtn setButtonType: NSSwitchButton]; [pBtn setTitle: pText]; sal_Bool bVal = sal_False; PropertyValue* pVal = pListener->getValue( aPropertyName ); if( pVal ) pVal->Value >>= bVal; [pBtn setState: bVal ? NSOnState : NSOffState]; [pBtn setEnabled: (pListener->isUIOptionEnabled( aPropertyName ) && pVal != NULL) ? YES : NO]; [pBtn sizeToFit]; [pCurParent addSubview: pBtn]; // connect target [pBtn setTarget: pCtrlTarget]; [pBtn setAction: @selector(triggered:)]; int nTag = pListenerProperties->addNameTag( aPropertyName ); [pBtn setTag: nTag]; aCheckRect = [pBtn frame]; // move to nCurY aCheckRect.origin.y = nCurY - aCheckRect.size.height; [pBtn setFrame: aCheckRect]; // update nCurY nCurY = aCheckRect.origin.y - 5; // cleanup [pText release]; } else if( aCtrlType.equalsAscii( "Radio" ) && pCurParent ) { if( aText.getLength() ) { // add a label NSString* pText = CreateNSString( aText ); NSRect aTextRect = { { nCurX, 0 }, { 300, 15 } }; NSTextView* pTextView = [[NSTextView alloc] initWithFrame: aTextRect]; [pTextView setEditable: NO]; [pTextView setSelectable: NO]; [pTextView setDrawsBackground: NO]; [pTextView setString: pText]; [pTextView sizeToFit]; // FIXME: this does nothing [pCurParent addSubview: pTextView]; // move to nCurY aTextRect.origin.y = nCurY - aTextRect.size.height; [pTextView setFrame: aTextRect]; // update nCurY nCurY = aTextRect.origin.y - 5; // cleanup [pText release]; } // setup radio matrix NSButtonCell* pProto = [[NSButtonCell alloc] init]; NSRect aRadioRect = { { nCurX + 20, 0 }, { 280 - nCurX, 5*aChoices.getLength() } }; [pProto setTitle: @"RadioButtonGroup"]; [pProto setButtonType: NSRadioButton]; NSMatrix* pMatrix = [[NSMatrix alloc] initWithFrame: aRadioRect mode: NSRadioModeMatrix prototype: (NSCell*)pProto numberOfRows: aChoices.getLength() numberOfColumns: 1]; // get currently selected value rtl::OUString aSelectVal; PropertyValue* pVal = pListener->getValue( aPropertyName ); if( pVal && pVal->Value.hasValue() ) pVal->Value >>= aSelectVal; // set individual titles NSArray* pCells = [pMatrix cells]; for( sal_Int32 m = 0; m < aChoices.getLength(); m++ ) { NSCell* pCell = [pCells objectAtIndex: m]; NSString* pTitle = CreateNSString( aChoices[m] ); [pCell setTitle: pTitle]; // connect target and action [pCell setTarget: pCtrlTarget]; [pCell setAction: @selector(triggered:)]; int nTag = pListenerProperties->addNameAndValueTag( aPropertyName, aChoices[ m ] ); [pCell setTag: nTag]; [pTitle release]; // set current selection if( aSelectVal == aChoices[m] ) [pMatrix selectCellAtRow: m column: 0]; } [pMatrix sizeToFit]; aRadioRect = [pMatrix frame]; // move it down, so it comes to the correct position aRadioRect.origin.y = nCurY - aRadioRect.size.height; [pMatrix setFrame: aRadioRect]; [pCurParent addSubview: pMatrix]; // update nCurY nCurY = aRadioRect.origin.y - 5; [pProto release]; } else if( aCtrlType.equalsAscii( "List" ) && pCurParent ) { NSString* pText = CreateNSString( aText ); // measure the text NSFont* pFont = [NSFont labelFontOfSize: 0]; NSDictionary* pDict = [NSDictionary dictionaryWithObject: pFont forKey: NSFontAttributeName]; NSSize aTextSize = [pText sizeWithAttributes: pDict]; // FIXME: the only thing reliable about sizeWithAttributes is // that the size it outputs is way too small for our NSTextView // that would not matter so much if NSTextView's fitToSize actually // did something out of the box, alas it doesn't. This probably needs more // fiddling with NSTextView's and NSTextContainer's parameters, however // since this already almost cost me my sanity a Murphy factor of 1.5 // will have to suffice for the time being. aTextSize.width *= 1.5; aTextSize.height += 3; NSRect aTextRect = { { nCurX, 0 }, aTextSize }; NSTextView* pTextView = [[NSTextView alloc] initWithFrame: aTextRect]; [pTextView setEditable: NO]; [pTextView setSelectable: NO]; [pTextView setDrawsBackground: NO]; [pTextView setString: pText]; [pTextView setVerticallyResizable: NO]; [pTextView setHorizontallyResizable: YES]; [pTextView sizeToFit]; // FIXME: this actually does nothing [pCurParent addSubview: pTextView]; aTextRect = [pTextView frame]; NSRect aBtnRect = { { nCurX + aTextRect.size.width, 0 }, { 0, 15 } }; NSPopUpButton* pBtn = [[NSPopUpButton alloc] initWithFrame: aBtnRect pullsDown: NO]; // iterate options for( sal_Int32 m = 0; m < aChoices.getLength(); m++ ) { NSString* pItemText = CreateNSString( aChoices[m] ); [pBtn addItemWithTitle: pItemText]; NSMenuItem* pItem = [pBtn itemWithTitle: pItemText]; int nTag = pListenerProperties->addNameAndValueTag( aPropertyName, aChoices[m] ); [pItem setTag: nTag]; [pItemText release]; } PropertyValue* pVal = pListener->getValue( aPropertyName ); rtl::OUString aSelectVal; if( pVal && pVal->Value.hasValue() ) pVal->Value >>= aSelectVal; NSString* pSelectText = CreateNSString( aSelectVal ); [pBtn setTitle: pSelectText]; [pSelectText release]; [pBtn setEnabled: (pListener->isUIOptionEnabled( aPropertyName ) && pVal != NULL) ? YES : NO]; [pBtn sizeToFit]; [pCurParent addSubview: pBtn]; // connect target and action [pBtn setTarget: pCtrlTarget]; [pBtn setAction: @selector(triggered:)]; // move to nCurY aBtnRect = [pBtn frame]; aBtnRect.origin.y = nCurY - aBtnRect.size.height; [pBtn setFrame: aBtnRect]; // align label aTextRect.origin.y = aBtnRect.origin.y + (aBtnRect.size.height - aTextRect.size.height)/2; [pTextView setFrame: aTextRect]; // update nCurY nCurY = aBtnRect.origin.y - 5; // cleanup [pText release]; } } else { DBG_ERROR( "Unsupported UI option" ); } } adjustViewAndChildren( pCurParent, aMaxTabSize ); // find the minimum needed tab size NSSize aTabCtrlSize = [pTabView minimumSize]; aTabCtrlSize.height += aMaxTabSize.height + 10; if( aTabCtrlSize.width < aMaxTabSize.width + 10 ) aTabCtrlSize.width = aMaxTabSize.width + 10; [pTabView setFrameSize: aTabCtrlSize]; // set the accessory view #if 0 NSPrintPanel* pPanel = [pOp printPanel]; if( [pPanel respondsToSelector: @selector(addAccessoryController:)] ) { // 10.5 and upward case AquaPrintViewController* pCtrl = [[AquaPrintViewController alloc] init]; [pCtrl performSelector: @selector(setView:) withObject: pTabView]; [pCtrl performSelector: @selector(setTitle:) withObject: @"Test OOOOO"]; [pPanel performSelector: @selector(addAccessoryController:) withObject: pCtrl]; [pPanel performSelector: @selector(setOptions:) withObject: (id)(0x20001)]; } else #endif [pOp setAccessoryView: pTabView]; return pCtrlTarget; } @end