diff options
Diffstat (limited to 'vcl/aqua')
211 files changed, 30325 insertions, 0 deletions
diff --git a/vcl/aqua/inc/aqua11yfactory.h b/vcl/aqua/inc/aqua11yfactory.h new file mode 100644 index 000000000000..1982093f8bba --- /dev/null +++ b/vcl/aqua/inc/aqua11yfactory.h @@ -0,0 +1,47 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11FACTORY_H +#define _SV_AQUA11FACTORY_H + +#include "aquavcltypes.h" +#include "aqua11ywrapper.h" +#include <com/sun/star/accessibility/XAccessibleContext.hpp> + +@interface AquaA11yFactory : NSObject +{ +} ++(void)insertIntoWrapperRepository: (NSView *) viewElement forAccessibleContext: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) rxAccessibleContext; ++(AquaA11yWrapper *)wrapperForAccessible: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessible >) rxAccessible; ++(AquaA11yWrapper *)wrapperForAccessibleContext: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) rxAccessibleContext; ++(AquaA11yWrapper *)wrapperForAccessibleContext: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) rxAccessibleContext createIfNotExists:(MacOSBOOL) bCreate; ++(AquaA11yWrapper *)wrapperForAccessibleContext: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) rxAccessibleContext createIfNotExists:(MacOSBOOL) bCreate asRadioGroup:(MacOSBOOL) asRadioGroup; ++(void)removeFromWrapperRepositoryFor: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) rxAccessibleContext; ++(void)registerView: (NSView *) theView; ++(void)revokeView: (NSView *) theViewt; +@end +#endif // _SV_AQUA11FACTORY_H diff --git a/vcl/aqua/inc/aqua11yfocustracker.hxx b/vcl/aqua/inc/aqua11yfocustracker.hxx new file mode 100644 index 000000000000..1dbf68c4a5ef --- /dev/null +++ b/vcl/aqua/inc/aqua11yfocustracker.hxx @@ -0,0 +1,108 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _AQUA11YFOCUSTRACKER_HXX_ +#define _AQUA11YFOCUSTRACKER_HXX_ + +#ifndef _COM_SUN_STAR_ACCESSIBILITY_XACCESSIBLE_HPP_ +#include <com/sun/star/accessibility/XAccessible.hpp> +#endif + +#include "keyboardfocuslistener.hxx" + +#include <rtl/instance.hxx> + +#include <tools/link.hxx> +#include <vcl/vclevent.hxx> +#include <set> + +class Window; +class ToolBox; +class DocumentFocusListener; + +// ------------------------ +// - AquaA11yFocusTracker - +// ------------------------ + +class AquaA11yFocusTracker : public rtl::Static< AquaA11yFocusTracker, AquaA11yFocusTracker> +{ + +public: + AquaA11yFocusTracker(); + + ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > getFocusedObject() { return m_xFocusedObject; }; + + // sets the currently focus object and notifies the FocusEventListener (if any) + void setFocusedObject(const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible); + + // may evolve to add/remove later + void setFocusListener(const rtl::Reference< KeyboardFocusListener >& aFocusListener) { m_aFocusListener = aFocusListener; }; + +protected: + + // received a WINDOW_GETFOCUS event for this window + virtual void window_got_focus(Window *pWindow); + + // received a TOOLBOX_HIGHLIGHT event for this window + virtual void toolbox_highlight_on(Window *pWindow); + + // received a TOOLBOX_HIGHLIGHTOFF event for this window + virtual void toolbox_highlight_off(Window *pWindow); + + // received a TABPAGE_ACTIVATE event for this window + virtual void tabpage_activated(Window *pWindow); + + // received a MENU_HIGHLIGHT event for this window + virtual void menu_highlighted(const ::VclMenuEvent *pEvent); + + // toolbox items are widgets in gtk+ and Cocoa + virtual void notify_toolbox_item_focus(ToolBox *pToolBox); + + // toolbox item opened a floating window (e.g. color chooser) + virtual void toolbox_open_floater(Window *pWindow); + + // callback function for Application::addEventListener + static long WindowEventHandler(AquaA11yFocusTracker *pFocusTracker, ::VclSimpleEvent const *pEvent); + +private: + // the accessible object that has the keyboard focus (if any) + ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > m_xFocusedObject; + + // the listener for focus events + rtl::Reference< KeyboardFocusListener > m_aFocusListener; + + // the list of Windows that need deeper (focus) investigation + std::set< Window *> m_aDocumentWindowList; + + // the link object needed for Application::addEventListener + Link m_aWindowEventLink; + + // the UNO XAccessibilityEventListener for Documents and other non VCL objects + const ::com::sun::star::uno::Reference< DocumentFocusListener > m_xDocumentFocusListener; +}; + +#endif // _AQUA11YFOCUSTRACKER_HXX_ diff --git a/vcl/aqua/inc/aqua11ylistener.hxx b/vcl/aqua/inc/aqua11ylistener.hxx new file mode 100644 index 000000000000..461b149e4650 --- /dev/null +++ b/vcl/aqua/inc/aqua11ylistener.hxx @@ -0,0 +1,65 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _AQUA11YLISTENER_HXX_ +#define _AQUA11YLISTENER_HXX_ + +#include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#include <cppuhelper/implbase1.hxx> + +#include "aqua11yfocustracker.hxx" +#include "aquavcltypes.h" +#include <set> +#include <com/sun/star/awt/Rectangle.hpp> + +// ------------------------- +// - AquaA11yEventListener - +// ------------------------- + +class AquaA11yEventListener : + public ::cppu::WeakImplHelper1< ::com::sun::star::accessibility::XAccessibleEventListener > +{ + +public: + AquaA11yEventListener(id wrapperObject, sal_Int16 role); + virtual ~AquaA11yEventListener(); + + // XEventListener + virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source ) + throw (::com::sun::star::uno::RuntimeException); + + // XAccessibleEventListener + virtual void SAL_CALL notifyEvent( const ::com::sun::star::accessibility::AccessibleEventObject& aEvent ) + throw( ::com::sun::star::uno::RuntimeException ); + +private: + const id m_wrapperObject; + const sal_Int16 m_role; + ::com::sun::star::awt::Rectangle m_oldBounds; +}; + +#endif // _AQUA11YLISTENER_HXX_
\ No newline at end of file diff --git a/vcl/aqua/inc/aqua11ywrapper.h b/vcl/aqua/inc/aqua11ywrapper.h new file mode 100644 index 000000000000..079dcfe7f112 --- /dev/null +++ b/vcl/aqua/inc/aqua11ywrapper.h @@ -0,0 +1,119 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPER_H +#define _SV_AQUA11WRAPPER_H + +#include "aquavcltypes.h" +#include <com/sun/star/accessibility/XAccessibleAction.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleExtendedComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/accessibility/XAccessibleTable.hpp> +#include <com/sun/star/accessibility/XAccessibleText.hpp> +#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp> +#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> +#include <com/sun/star/accessibility/XAccessibleValue.hpp> +#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp> + +// rAccessibleXYZ as a field in an Objective-C-Class would not call Con-/Destructor, so use a struct instead +struct ReferenceWrapper +{ + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleAction > rAccessibleAction; + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext > rAccessibleContext; + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleComponent > rAccessibleComponent; + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleExtendedComponent > rAccessibleExtendedComponent; + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleSelection > rAccessibleSelection; + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleTable > rAccessibleTable; + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleText > rAccessibleText; + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleEditableText > rAccessibleEditableText; + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleValue > rAccessibleValue; + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleTextAttributes > rAccessibleTextAttributes; + ::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleMultiLineText > rAccessibleMultiLineText; +}; + +@interface AquaA11yWrapper : NSView +{ + ReferenceWrapper * mpReferenceWrapper; + NSString * mpDefaultFontname; + float mDefaultFontsize; + MacOSBOOL mActsAsRadioGroup; + MacOSBOOL mIsTableCell; +} +// NSAccessibility Protocol +-(id)accessibilityAttributeValue:(NSString *)attribute; +-(MacOSBOOL)accessibilityIsIgnored; +-(NSArray *)accessibilityAttributeNames; +-(MacOSBOOL)accessibilityIsAttributeSettable:(NSString *)attribute; +-(NSArray *)accessibilityParameterizedAttributeNames; +-(MacOSBOOL)accessibilitySetOverrideValue:(id)value forAttribute:(NSString *)attribute; +-(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute; +-(id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter; +-(id)accessibilityFocusedUIElement; +-(NSString *)accessibilityActionDescription:(NSString *)action; +-(void)accessibilityPerformAction:(NSString *)action; +-(NSArray *)accessibilityActionNames; +-(id)accessibilityHitTest:(NSPoint)point; +// Attribute values +-(id)parentAttribute; +-(id)valueAttribute; +-(id)titleAttribute; +-(id)helpAttribute; +-(id)numberOfCharactersAttribute; +-(id)selectedTextAttribute; +-(id)selectedTextRangeAttribute; +-(id)visibleCharacterRangeAttribute; +-(id)childrenAttribute; +-(id)orientationAttribute; +-(id)windowAttribute; +// Wrapper-specific +-(void)setActsAsRadioGroup:(MacOSBOOL)actsAsRadioGroup; +-(MacOSBOOL)actsAsRadioGroup; +-(NSView *)viewElementForParent; +-(id)initWithAccessibleContext: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) anAccessibleContext; +-(void) setDefaults: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) rxAccessibleContext; +-(void) dealloc; +-(void)setDefaultFontname:(NSString *)fontname; +-(NSString *)defaultFontname; +-(void)setDefaultFontsize:(float)fontsize; +-(float)defaultFontsize; ++(void)setPopupMenuOpen:(MacOSBOOL)popupMenuOpen; +-(::com::sun::star::accessibility::XAccessibleAction *)accessibleAction; +-(::com::sun::star::accessibility::XAccessibleContext *)accessibleContext; +-(::com::sun::star::accessibility::XAccessibleComponent *)accessibleComponent; +-(::com::sun::star::accessibility::XAccessibleExtendedComponent *)accessibleExtendedComponent; +-(::com::sun::star::accessibility::XAccessibleSelection *)accessibleSelection; +-(::com::sun::star::accessibility::XAccessibleTable *)accessibleTable; +-(::com::sun::star::accessibility::XAccessibleText *)accessibleText; +-(::com::sun::star::accessibility::XAccessibleEditableText *)accessibleEditableText; +-(::com::sun::star::accessibility::XAccessibleValue *)accessibleValue; +-(::com::sun::star::accessibility::XAccessibleTextAttributes *)accessibleTextAttributes; +-(::com::sun::star::accessibility::XAccessibleMultiLineText *)accessibleMultiLineText; +@end + +#endif // _SV_AQUA11WRAPPER_H diff --git a/vcl/aqua/inc/aquaprintview.h b/vcl/aqua/inc/aquaprintview.h new file mode 100755 index 000000000000..84d9dd54d6aa --- /dev/null +++ b/vcl/aqua/inc/aquaprintview.h @@ -0,0 +1,67 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _VCL_AQUAPRINTVIEW_H +#define _VCL_AQUAPRINTVIEW_H + +#include "premac.h" +#include <Cocoa/Cocoa.h> +#include "postmac.h" + +#include "vcl/print.hxx" + +class AquaSalInfoPrinter; + +struct PrintAccessoryViewState +{ + bool bNeedRestart; + sal_Int32 nLastPage; + + PrintAccessoryViewState() + : bNeedRestart( false ), nLastPage( 0 ) {} +}; + +@interface AquaPrintView : NSView +{ + vcl::PrinterController* mpController; + AquaSalInfoPrinter* mpInfoPrinter; +} +-(id)initWithController: (vcl::PrinterController*)pController withInfoPrinter: (AquaSalInfoPrinter*)pInfoPrinter; +-(MacOSBOOL)knowsPageRange: (NSRangePointer)range; +-(NSRect)rectForPage: (int)page; +-(NSPoint)locationOfPrintRect: (NSRect)aRect; +-(void)drawRect: (NSRect)rect; +@end + +@interface AquaPrintAccessoryView : NSObject +{ +} ++(NSObject*)setupPrinterPanel: (NSPrintOperation*)pOp withController: (vcl::PrinterController*)pController withState: (PrintAccessoryViewState*)pState; +@end + + +#endif diff --git a/vcl/aqua/inc/aquavclevents.hxx b/vcl/aqua/inc/aquavclevents.hxx new file mode 100644 index 000000000000..8e03c59d0954 --- /dev/null +++ b/vcl/aqua/inc/aquavclevents.hxx @@ -0,0 +1,96 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef INCLUDED_AQUAVCLEVENTS_HXX +#define INCLUDED_AQUAVCLEVENTS_HXX + +#include <premac.h> +#include <Carbon/Carbon.h> +#include <postmac.h> + +/* Definition of custom OpenOffice.org events. + + Avoid conflict with Apple defined event class and type + definitions by using uppercase letters. Lowercase + letter definitions are reserved for Apple! + */ +enum { + cOOoSalUserEventClass = 'OOUE' +}; + +enum { + cOOoSalEventUser = 'UEVT', + cOOoSalEventTimer = 'EVTT', + cOOoSalEventData = 'EVTD', + cOOoSalEventParamTypePtr = 'EPPT' +}; + +/* Definition of all necessary EventTypeSpec's */ + +const EventTypeSpec cWindowBoundsChangedEvent = { kEventClassWindow, kEventWindowBoundsChanged }; +const EventTypeSpec cWindowCloseEvent = { kEventClassWindow, kEventWindowClose }; +const EventTypeSpec cOOoSalUserEvent = { cOOoSalUserEventClass, cOOoSalEventUser }; +const EventTypeSpec cOOoSalTimerEvent = { cOOoSalUserEventClass, cOOoSalEventTimer }; +const EventTypeSpec cWindowActivatedEvent[] = { { kEventClassWindow, kEventWindowActivated }, + { kEventClassWindow, kEventWindowDeactivated } }; +const EventTypeSpec cWindowPaintEvent = { kEventClassWindow, kEventWindowPaint }; +const EventTypeSpec cWindowDrawContentEvent = { kEventClassWindow, kEventWindowDrawContent }; + +const EventTypeSpec cWindowFocusEvent[] = { { kEventClassWindow, kEventWindowFocusAcquired }, + { kEventClassWindow, kEventWindowFocusRelinquish } }; + +const EventTypeSpec cMouseEnterExitEvent[] = { { kEventClassControl, kEventControlTrackingAreaEntered }, + { kEventClassControl, kEventControlTrackingAreaExited } }; + +const EventTypeSpec cMouseEvent[] = { { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseDragged } }; +const EventTypeSpec cMouseWheelMovedEvent = { kEventClassMouse, kEventMouseWheelMoved }; +const EventTypeSpec cWindowResizeStarted = { kEventClassWindow, kEventWindowResizeStarted }; +const EventTypeSpec cWindowResizeCompleted = { kEventClassWindow, kEventWindowResizeCompleted }; + +/* Events for native menus */ +const EventTypeSpec cCommandProcessEvent = { kEventClassCommand, kEventCommandProcess }; +const EventTypeSpec cMenuPopulateEvent = { kEventClassMenu, kEventMenuPopulate }; +const EventTypeSpec cMenuClosedEvent = { kEventClassMenu, kEventMenuClosed }; +const EventTypeSpec cMenuTargetItemEvent = { kEventClassMenu, kEventMenuTargetItem }; + +/* Events for keyboard */ +const EventTypeSpec cKeyboardRawKeyEvents[] = { { kEventClassKeyboard, kEventRawKeyDown}, + { kEventClassKeyboard, kEventRawKeyUp}, + { kEventClassKeyboard, kEventRawKeyRepeat}, + { kEventClassKeyboard, kEventRawKeyModifiersChanged} }; + +const EventTypeSpec cTextInputEvents[] = { { kEventClassTextInput, kEventTextInputUpdateActiveInputArea}, + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent}, + { kEventClassTextInput, kEventTextInputOffsetToPos} }; + +/* Events for scrollbar */ + +const EventTypeSpec cAppearanceScrollbarVariantChangedEvent = { kEventClassAppearance, kEventAppearanceScrollBarVariantChanged }; + +#endif // INCLUDED_AQUAVCLEVENTS_HXX diff --git a/vcl/aqua/inc/aquavcltypes.h b/vcl/aqua/inc/aquavcltypes.h new file mode 100644 index 000000000000..7346282963d7 --- /dev/null +++ b/vcl/aqua/inc/aquavcltypes.h @@ -0,0 +1,36 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _AQUAVCLTYPES_H +#define _AQUAVCLTYPES_H + +#include "premac.h" +#import <Cocoa/Cocoa.h> +#import <AppKit/NSEvent.h> +#include "postmac.h" + +#endif _AQUAVCLTYPES_H diff --git a/vcl/aqua/inc/keyboardfocuslistener.hxx b/vcl/aqua/inc/keyboardfocuslistener.hxx new file mode 100644 index 000000000000..71eba2f46c02 --- /dev/null +++ b/vcl/aqua/inc/keyboardfocuslistener.hxx @@ -0,0 +1,47 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _KEYBOARDFOCUSLISTENER_HXX_ +#define _KEYBOARDFOCUSLISTENER_HXX_ + +#ifndef _COM_SUN_STAR_ACCESSIBILITY_XACCESSIBLE_HPP_ +#include <com/sun/star/accessibility/XAccessible.hpp> +#endif + +#include <rtl/ref.hxx> + +// ------------------------- +// - KeyboardFocusListener - +// ------------------------- + +class KeyboardFocusListener : public rtl::IReference +{ +public: + virtual void SAL_CALL focusedObjectChanged(const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible) = 0; +}; + +#endif // _KEYBOARDFOCUSLISTENER_HXX_
\ No newline at end of file diff --git a/vcl/aqua/inc/salatsuifontutils.hxx b/vcl/aqua/inc/salatsuifontutils.hxx new file mode 100644 index 000000000000..81e60871ae86 --- /dev/null +++ b/vcl/aqua/inc/salatsuifontutils.hxx @@ -0,0 +1,63 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALATSUIFONTUTILS_HXX +#define _SV_SALATSUIFONTUTILS_HXX + +class ImplMacFontData; +class ImplDevFontList; + +#include <premac.h> +#include <ApplicationServices/ApplicationServices.h> +#include <postmac.h> + +#include <map> + +/* This class has the responsibility of assembling a list + of atsui compatible fonts available on the system and + enabling access to that list. + */ +class SystemFontList +{ +public: + SystemFontList(); + ~SystemFontList(); + + void AnnounceFonts( ImplDevFontList& ) const; + ImplMacFontData* GetFontDataFromId( ATSUFontID ) const; + + ATSUFontFallbacks maFontFallbacks; + +private: + typedef std::hash_map<ATSUFontID,ImplMacFontData*> MacFontContainer; + MacFontContainer maFontContainer; + + void InitGlyphFallbacks(); +}; + +#endif // _SV_SALATSUIFONTUTILS_HXX + diff --git a/vcl/aqua/inc/salbmp.h b/vcl/aqua/inc/salbmp.h new file mode 100644 index 000000000000..1c427cce0cd5 --- /dev/null +++ b/vcl/aqua/inc/salbmp.h @@ -0,0 +1,107 @@ +/************************************************************************* +* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +#ifndef _SV_SALBMP_H +#define _SV_SALBMP_H + +#include "tools/gen.hxx" +#include "vcl/sv.h" +#include "vcl/salbtype.hxx" +#include "saldata.hxx" +#include "vcl/salinst.hxx" +#include "salconst.h" +#include "vcl/salvd.hxx" +#include "salcolorutils.hxx" +#include "vcl/salbmp.hxx" +#include "salgdi.h" +#include "basebmp/bitmapdevice.hxx" + +// -------------- +// - SalBitmap - +// -------------- + +struct BitmapBuffer; +class BitmapColor; +class BitmapPalette; +class AquaSalVirtualDevice; +class AquaSalGraphics; + +class AquaSalBitmap : public SalBitmap +{ +public: + CGContextRef mxGraphicContext; + mutable CGImageRef mxCachedImage; + BitmapPalette maPalette; + basebmp::RawMemorySharedArray maUserBuffer; + basebmp::RawMemorySharedArray maContextBuffer; + sal_uInt16 mnBits; + int mnWidth; + int mnHeight; + sal_uInt32 mnBytesPerRow; + +public: + AquaSalBitmap(); + virtual ~AquaSalBitmap(); + +public: + + // SalBitmap methods + bool Create( const Size& rSize, USHORT nBitCount, const BitmapPalette& rPal ); + bool Create( const SalBitmap& rSalBmp ); + bool Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics ); + bool Create( const SalBitmap& rSalBmp, USHORT nNewBitCount ); + + void Destroy(); + + Size GetSize() const; + USHORT GetBitCount() const; + + BitmapBuffer *AcquireBuffer( bool bReadOnly ); + void ReleaseBuffer( BitmapBuffer* pBuffer, bool bReadOnly ); + + bool GetSystemData( BitmapSystemData& rData ); + +private: + // quartz helper + bool CreateContext(); + void DestroyContext(); + bool AllocateUserData(); + + void ConvertBitmapData( sal_uInt32 nWidth, sal_uInt32 nHeight, + sal_uInt16 nDestBits, sal_uInt32 nDestBytesPerRow, const BitmapPalette& rDestPalette, sal_uInt8* pDestData, + sal_uInt16 nSrcBits, sal_uInt32 nSrcBytesPerRow, const BitmapPalette& rSrcPalette, sal_uInt8* pSrcData ); + +public: + bool Create( CGLayerRef xLayer, int nBitCount, int nX, int nY, int nWidth, int nHeight, bool bMirrorVert = true ); + +public: + CGImageRef CreateWithMask( const AquaSalBitmap& rMask, int nX, int nY, int nWidth, int nHeight ) const; + CGImageRef CreateColorMask( int nX, int nY, int nWidth, int nHeight, SalColor nMaskColor ) const; + CGImageRef CreateCroppedImage( int nX, int nY, int nWidth, int nHeight ) const; +}; + +#endif // _SV_SALBMP_HXX diff --git a/vcl/aqua/inc/salcolorutils.hxx b/vcl/aqua/inc/salcolorutils.hxx new file mode 100755 index 000000000000..74ccb69756fd --- /dev/null +++ b/vcl/aqua/inc/salcolorutils.hxx @@ -0,0 +1,52 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALCOLORUTILS_HXX +#define _SV_SALCOLORUTILS_HXX + +#ifndef _LIMITS_H + #include <limits.h> +#endif + +#include "premac.h" +#include <ApplicationServices/ApplicationServices.h> +#include "postmac.h" + +#include "vcl/salbtype.hxx" +#include "vcl/salgtype.hxx" +#include "salconst.h" +#include "salmathutils.hxx" + +// ------------------------------------------------------------------ + +SalColor GetSalColor( const float* pQuartzColor ); + +void SetSalColor( const SalColor& rColor, float* pQuartzColor ); + +// ------------------------------------------------------------------ + +#endif // _SV_SALCOLORUTILS_HXX diff --git a/vcl/aqua/inc/salconst.h b/vcl/aqua/inc/salconst.h new file mode 100755 index 000000000000..2046ec20a806 --- /dev/null +++ b/vcl/aqua/inc/salconst.h @@ -0,0 +1,68 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALCONST_H +#define _SV_SALCONST_H + +// ------------------- +// - Constants - +// ------------------- + +static const unsigned short kByteMask = 0xFF; + +static const unsigned short kOneByte = 8; +static const unsigned short kTwoBytes = 16; + +static const unsigned short kOneBit = 1; +static const unsigned short kFiveBits = 5; +static const unsigned short kEightBits = 8; +static const unsigned short kTenBits = 10; +static const unsigned short kElevenBits = 11; + +static const unsigned short kBlackAndWhite = 1; +static const unsigned short kFourBitColor = 4; +static const unsigned short kEightBitColor = 8; +static const unsigned short kThousandsColor = 16; +static const unsigned short kTrueColor = 32; + +static const unsigned long k16BitRedColorMask = 0x00007c00; +static const unsigned long k16BitGreenColorMask = 0x000003e0; +static const unsigned long k16BitBlueColorMask = 0x0000001f; + +static const unsigned long k32BitRedColorMask = 0x00ff0000; +static const unsigned long k32BitGreenColorMask = 0x0000ff00; +static const unsigned long k32BitBlueColorMask = 0x000000ff; + +static const unsigned short kPixMapCmpSizeOneBit = 1; +static const unsigned short kPixMapCmpSizeFourBits = 4; +static const unsigned short kPixMapCmpSizeFiveBits = 5; +static const unsigned short kPixMapCmpSizeEightBits = 8; + +static const long kPixMapHRes = 72; +static const long kPixMapVRes = 72; + +#endif // _SV_SALCONST_H diff --git a/vcl/aqua/inc/saldata.hxx b/vcl/aqua/inc/saldata.hxx new file mode 100644 index 000000000000..fb7c8cddd6d4 --- /dev/null +++ b/vcl/aqua/inc/saldata.hxx @@ -0,0 +1,138 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALDATA_HXX +#define _SV_SALDATA_HXX + +#include "premac.h" +#include <Cocoa/Cocoa.h> +#include "postmac.h" + +#include "com/sun/star/uno/Reference.hxx" + +#include "vcl/sv.h" +#include "vcl/svdata.hxx" +#include "vcl/salwtype.hxx" +#include "vcl/ptrstyle.hxx" + +#include <list> +#include <vector> +#include <map> +#include <hash_set> + +#include <cstdio> +#include <cstdarg> + +#include "apple_remote/RemoteMainController.h" + +class AquaSalInstance; +class SalObject; +class SalFrame; +class SalVirtualDevice; +class SalPrinter; +class SystemFontList; + +// ------------------ +// - Some constants - +// ------------------ + +#define SAL_CLIPRECT_COUNT 16 + +#define VER_TIGER 0x1040 +#define VER_LEOPARD 0x1050 + +// ----------- +// - SalData - +// ----------- + +class AquaSalFrame; +struct FrameHash : public std::hash<sal_IntPtr> +{ + size_t operator()(const AquaSalFrame* frame) const + { return std::hash<sal_IntPtr>::operator()( reinterpret_cast<const sal_IntPtr>(frame) ); } +}; + +#define INVALID_CURSOR_PTR (NSCursor*)0xdeadbeef + +struct SalData +{ + + SALTIMERPROC mpTimerProc; // timer callback proc + AquaSalInstance *mpFirstInstance; // pointer of first instance + std::list<AquaSalFrame*> maFrames; // pointer of first frame + std::hash_set<const AquaSalFrame*,FrameHash> maFrameCheck; // for fast check of frame existance + SalObject *mpFirstObject; // pointer of first object window + SalVirtualDevice *mpFirstVD; // first VirDev + SalPrinter *mpFirstPrinter; // first printing printer + SystemFontList *mpFontList; + NSStatusItem* mpStatusItem; // one status item that draws all our stati + // at the moment this is only one add menu button + + CGColorSpaceRef mxRGBSpace; + CGColorSpaceRef mxGraySpace; + CGColorSpaceRef mxP50Space; + CGPatternRef mxP50Pattern; + + std::vector< NSCursor* > maCursors; + std::vector< NSMenuItem* > maFallbackMenu; + std::map< NSEvent*, bool > maKeyEventAnswer; + + static oslThreadKey s_aAutoReleaseKey; + + bool mbIsScrollbarDoubleMax; // TODO: support DoubleMin and DoubleBoth too + SInt32 mnSystemVersion; // Store System Version + MainController* mpMainController; // Apple Remote + + NSObject* mpDockIconClickHandler; + long mnDPIX; // #i100617# read DPI only once per office life + long mnDPIY; // #i100617# read DPI only once per office life + + com::sun::star::uno::Reference< com::sun::star::uno::XInterface > + mxClipboard; + + SalData(); + ~SalData(); + + NSCursor* getCursor( PointerStyle i_eStyle ); + + static void ensureThreadAutoreleasePool(); + static void drainThreadAutoreleasePool(); + + static NSStatusItem* getStatusItem(); +}; + +inline void SetSalData( SalData* pData ) { ImplGetSVData()->mpSalData = (void*)pData; } +inline SalData *GetSalData() { return (SalData*)ImplGetSVData()->mpSalData; } +inline SalData *GetAppSalData() { return (SalData*)ImplGetAppSVData()->mpSalData; } + +// --- Prototypes --- + +BOOL ImplSalYieldMutexTryToAcquire(); +void ImplSalYieldMutexAcquire(); +void ImplSalYieldMutexRelease(); + +#endif // _SV_SALDATA_HXX diff --git a/vcl/aqua/inc/salfontutils.hxx b/vcl/aqua/inc/salfontutils.hxx new file mode 100644 index 000000000000..6f9f61efda70 --- /dev/null +++ b/vcl/aqua/inc/salfontutils.hxx @@ -0,0 +1,66 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +#ifndef _SV_SALFONTUTILS_HXX +#define _SV_SALFONTUTILS_HXX + +#include "vcl/outfont.hxx" + +static const char *kFontWeightThin1 = "Thin"; +static const char *kFontWeightThin2 = "thin"; + +static const char *kFontWeightLight1 = "Light"; +static const char *kFontWeightLight2 = "light"; + +static const char *kFontWeightBold1 = "Bold"; +static const char *kFontWeightBold2 = "bold"; + +static const char *kFontWeightUltra1 = "Ultra"; +static const char *kFontWeightUltra2 = "ultra"; + +static const char *kFontWeightSemi1 = "Semi"; +static const char *kFontWeightSemi2 = "semi"; + +static const char *kFontWeightNormal1 = "Normal"; +static const char *kFontWeightNormal2 = "normal"; + +static const char *kFontWeightMedium1 = "Medium"; +static const char *kFontWeightMedium2 = "medium"; + +static const char *kFontWeightBlack1 = "Black"; +static const char *kFontWeightBlack2 = "black"; + +static const char *kFontWeightRoman1 = "Roman"; +static const char *kFontWeightRoman2 = "roman"; + +static const char *kFontWeightRegular1 = "Regular"; +static const char *kFontWeightRegular2 = "regular"; + + +#endif // _SV_SALFONTUTILS_HXX + diff --git a/vcl/aqua/inc/salframe.h b/vcl/aqua/inc/salframe.h new file mode 100644 index 000000000000..c2ded3267f45 --- /dev/null +++ b/vcl/aqua/inc/salframe.h @@ -0,0 +1,220 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALFRAME_H +#define _SV_SALFRAME_H + + +#include "vcl/sv.h" +#include "vcl/salframe.hxx" +#include "vcl/sysdata.hxx" + +#include "salmenu.h" +#include "saldata.hxx" +#include "aquavcltypes.h" + +#include <vector> +#include <utility> +#include <stdexcept> + +#include <boost/shared_ptr.hpp> + +class AquaSalGraphics; +class AquaSalFrame; +class AquaSalTimer; +class AquaSalInstance; +class AquaSalMenu; +class AquaBlinker; + +typedef struct SalFrame::SalPointerState SalPointerState; + +// ---------------- +// - AquaSalFrame - +// ---------------- + +class AquaSalFrame : public SalFrame +{ +public: + NSWindow* mpWindow; // Cocoa window + NSView* mpView; // Cocoa view (actually a custom view, see below + NSMenuItem* mpDockMenuEntry; // entry in the dynamic dock menu + NSRect maScreenRect; // for mirroring purposes + AquaSalGraphics* mpGraphics; // current frame graphics + AquaSalFrame* mpParent; // pointer to parent frame + SystemEnvData maSysData; // system data + int mnMinWidth; // min. client width in pixels + int mnMinHeight; // min. client height in pixels + int mnMaxWidth; // max. client width in pixels + int mnMaxHeight; // max. client height in pixels + NSRect maFullScreenRect; // old window size when in FullScreen + bool mbGraphics:1; // is Graphics used? + bool mbFullScreen:1; // is Window in FullScreen? + bool mbShown:1; + bool mbInitShow:1; + bool mbPositioned:1; + bool mbSized:1; + bool mbPresentation:1; + + ULONG mnStyle; + unsigned int mnStyleMask; // our style mask from NSWindow creation + + ULONG mnLastEventTime; + unsigned int mnLastModifierFlags; + AquaSalMenu* mpMenu; + + SalExtStyle mnExtStyle; // currently document frames are marked this way + + PointerStyle mePointerStyle; // currently active pointer style + + NSTrackingRectTag mnTrackingRectTag; // used to get enter/leave messages + + CGMutablePathRef mrClippingPath; // used for "shaping" + std::vector< CGRect > maClippingRects; + + std::list<AquaBlinker*> maBlinkers; + + Rectangle maInvalidRect; + + ULONG mnICOptions; + + boost::shared_ptr< Timer > mpActivityTimer; // Timer to prevent system sleep during presentation +public: + /** Constructor + + Creates a system window and connects this frame with it. + + @throws std::runtime_error in case window creation fails + */ + AquaSalFrame( SalFrame* pParent, ULONG salFrameStyle ); + + virtual ~AquaSalFrame(); + + virtual SalGraphics* GetGraphics(); + virtual void ReleaseGraphics( SalGraphics* pGraphics ); + virtual BOOL PostEvent( void* pData ); + virtual void SetTitle( const XubString& rTitle ); + virtual void SetIcon( USHORT nIcon ); + virtual void SetRepresentedURL( const rtl::OUString& ); + virtual void SetMenu( SalMenu* pSalMenu ); + virtual void DrawMenuBar(); + virtual void Show( BOOL bVisible, BOOL bNoActivate = FALSE ); + virtual void Enable( BOOL bEnable ); + virtual void SetMinClientSize( long nWidth, long nHeight ); + virtual void SetMaxClientSize( long nWidth, long nHeight ); + virtual void SetPosSize( long nX, long nY, long nWidth, long nHeight, USHORT nFlags ); + virtual void GetClientSize( long& rWidth, long& rHeight ); + virtual void GetWorkArea( Rectangle& rRect ); + virtual SalFrame* GetParent() const; + virtual void SetWindowState( const SalFrameState* pState ); + virtual BOOL GetWindowState( SalFrameState* pState ); + virtual void ShowFullScreen( BOOL bFullScreen, sal_Int32 nDisplay ); + virtual void StartPresentation( BOOL bStart ); + virtual void SetAlwaysOnTop( BOOL bOnTop ); + virtual void ToTop( USHORT nFlags ); + virtual void SetPointer( PointerStyle ePointerStyle ); + virtual void CaptureMouse( BOOL bMouse ); + virtual void SetPointerPos( long nX, long nY ); + virtual void Flush( void ); + virtual void Flush( const Rectangle& ); + virtual void Sync(); + virtual void SetInputContext( SalInputContext* pContext ); + virtual void EndExtTextInput( USHORT nFlags ); + virtual String GetKeyName( USHORT nKeyCode ); + virtual String GetSymbolKeyName( const XubString& rFontName, USHORT nKeyCode ); + virtual BOOL MapUnicodeToKeyCode( sal_Unicode aUnicode, LanguageType aLangType, KeyCode& rKeyCode ); + virtual LanguageType GetInputLanguage(); + virtual SalBitmap* SnapShot(); + virtual void UpdateSettings( AllSettings& rSettings ); + virtual void Beep( SoundType eSoundType ); + virtual const SystemEnvData* GetSystemData() const; + virtual SalPointerState GetPointerState(); + virtual void SetParent( SalFrame* pNewParent ); + virtual bool SetPluginParent( SystemParentData* pNewParent ); + virtual void SetExtendedFrameStyle( SalExtStyle ); + virtual void SetBackgroundBitmap( SalBitmap* ); + virtual void SetScreenNumber(unsigned int); + + // shaped system windows + // set clip region to none (-> rectangular windows, normal state) + virtual void ResetClipRegion(); + // start setting the clipregion consisting of nRects rectangles + virtual void BeginSetClipRegion( ULONG nRects ); + // add a rectangle to the clip region + virtual void UnionClipRegion( long nX, long nY, long nWidth, long nHeight ); + // done setting up the clipregion + virtual void EndSetClipRegion(); + + virtual void SetClientSize( long nWidth, long nHeight ); + + void UpdateFrameGeometry(); + + // trigger painting of the window + void SendPaintEvent( const Rectangle* pRect = NULL ); + + static bool isAlive( const AquaSalFrame* pFrame ) + { return GetSalData()->maFrameCheck.find( pFrame ) != GetSalData()->maFrameCheck.end(); } + + static AquaSalFrame* GetCaptureFrame() { return s_pCaptureFrame; } + + NSWindow* getWindow() const { return mpWindow; } + NSView* getView() const { return mpView; } + unsigned int getStyleMask() const { return mnStyleMask; } + + void getResolution( long& o_rDPIX, long& o_rDPIY ); + + // actually the follwing methods do the same thing: flipping y coordinates + // but having two of them makes clearer what the coordinate system + // is supposed to be before and after + void VCLToCocoa( NSRect& io_rRect, bool bRelativeToScreen = true ); + void CocoaToVCL( NSRect& io_rRect, bool bRelativeToScreen = true ); + + void VCLToCocoa( NSPoint& io_rPoint, bool bRelativeToScreen = true ); + void CocoaToVCL( NSPoint& io_Point, bool bRelativeToScreen = true ); + + NSCursor* getCurrentCursor() const; + + CGMutablePathRef getClipPath() const { return mrClippingPath; } + + // called by VCL_NSApplication to indicate screen settings have changed + void screenParametersChanged(); + + private: // methods + /** do things on initial show (like centering on parent or on screen) + */ + void initShow(); + + void initWindowAndView(); + + private: // data + static AquaSalFrame* s_pCaptureFrame; + + // make AquaSalFrame non copyable + AquaSalFrame( const AquaSalFrame& ); + AquaSalFrame& operator=(const AquaSalFrame&); +}; + +#endif // _SV_SALFRAME_H diff --git a/vcl/aqua/inc/salframeview.h b/vcl/aqua/inc/salframeview.h new file mode 100755 index 000000000000..0174c1a68832 --- /dev/null +++ b/vcl/aqua/inc/salframeview.h @@ -0,0 +1,208 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _VCL_SALFRAMEVIEW_H +#define _VCL_SALFRAMEVIEW_H + +#include "aqua11ywrapper.h" + +@interface SalFrameWindow : NSWindow +{ + AquaSalFrame* mpFrame; + id mDraggingDestinationHandler; +} +-(id)initWithSalFrame: (AquaSalFrame*)pFrame; +-(MacOSBOOL)canBecomeKeyWindow; +-(void)windowDidBecomeKey: (NSNotification*)pNotification; +-(void)windowDidResignKey: (NSNotification*)pNotification; +-(void)windowDidChangeScreen: (NSNotification*)pNotification; +-(void)windowDidMove: (NSNotification*)pNotification; +-(void)windowDidResize: (NSNotification*)pNotification; +-(void)windowDidMiniaturize: (NSNotification*)pNotification; +-(void)windowDidDeminiaturize: (NSNotification*)pNotification; +-(MacOSBOOL)windowShouldClose: (NSNotification*)pNotification; +-(void)dockMenuItemTriggered: (id)sender; +-(AquaSalFrame*)getSalFrame; +-(MacOSBOOL)containsMouse; +-(::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >)accessibleContext; + +/* NSDraggingDestination protocol methods + */ +-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; +-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; +-(void)draggingExited:(id <NSDraggingInfo>)sender; +-(MacOSBOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender; +-(MacOSBOOL)performDragOperation:(id <NSDraggingInfo>)sender; +-(void)concludeDragOperation:(id <NSDraggingInfo>)sender; + +-(void)registerDraggingDestinationHandler:(id)theHandler; +-(void)unregisterDraggingDestinationHandler:(id)theHandler; +@end + +@interface SalFrameView : AquaA11yWrapper <NSTextInput> +{ + AquaSalFrame* mpFrame; + + // for NSTextInput + NSEvent* mpLastEvent; + BOOL mbNeedSpecialKeyHandle; + BOOL mbInKeyInput; + BOOL mbKeyHandled; + NSRange mMarkedRange; + NSRange mSelectedRange; + id mpMouseEventListener; + id mDraggingDestinationHandler; + NSEvent* mpLastSuperEvent; +} ++(void)unsetMouseFrame: (AquaSalFrame*)pFrame; +-(id)initWithSalFrame: (AquaSalFrame*)pFrame; +-(MacOSBOOL)acceptsFirstResponder; +-(MacOSBOOL)acceptsFirstMouse: (NSEvent *)pEvent; +-(MacOSBOOL)isOpaque; +-(void)drawRect: (NSRect)aRect; +-(void)mouseDown: (NSEvent*)pEvent; +-(void)mouseDragged: (NSEvent*)pEvent; +-(void)mouseUp: (NSEvent*)pEvent; +-(void)mouseMoved: (NSEvent*)pEvent; +-(void)mouseEntered: (NSEvent*)pEvent; +-(void)mouseExited: (NSEvent*)pEvent; +-(void)rightMouseDown: (NSEvent*)pEvent; +-(void)rightMouseDragged: (NSEvent*)pEvent; +-(void)rightMouseUp: (NSEvent*)pEvent; +-(void)otherMouseDown: (NSEvent*)pEvent; +-(void)otherMouseDragged: (NSEvent*)pEvent; +-(void)otherMouseUp: (NSEvent*)pEvent; +-(void)scrollWheel: (NSEvent*)pEvent; +-(void)magnifyWithEvent: (NSEvent*)pEvent; +-(void)rotateWithEvent: (NSEvent*)pEvent; +-(void)swipeWithEvent: (NSEvent*)pEvent; +-(void)keyDown: (NSEvent*)pEvent; +-(void)flagsChanged: (NSEvent*)pEvent; +-(void)sendMouseEventToFrame:(NSEvent*)pEvent button:(USHORT)nButton eventtype:(USHORT)nEvent; +-(MacOSBOOL)sendKeyInputAndReleaseToFrame: (USHORT)nKeyCode character: (sal_Unicode)aChar; +-(MacOSBOOL)sendKeyInputAndReleaseToFrame: (USHORT)nKeyCode character: (sal_Unicode)aChar modifiers: (unsigned int)nMod; +-(MacOSBOOL)sendKeyToFrameDirect: (USHORT)nKeyCode character: (sal_Unicode)aChar modifiers: (unsigned int)nMod; +-(MacOSBOOL)sendSingleCharacter:(NSEvent*)pEvent; +-(MacOSBOOL)handleKeyDownException:(NSEvent*)pEvent; +-(void)clearLastEvent; +/* + text action methods +*/ +-(void)insertText:(id)aString; +-(void)insertTab: (id)aSender; +-(void)insertBacktab: (id)aSender; +-(void)moveLeft: (id)aSender; +-(void)moveLeftAndModifySelection: (id)aSender; +-(void)moveBackwardAndModifySelection: (id)aSender; +-(void)moveRight: (id)aSender; +-(void)moveRightAndModifySelection: (id)aSender; +-(void)moveForwardAndModifySelection: (id)aSender; +-(void)moveUp: (id)aSender; +-(void)moveDown: (id)aSender; +-(void)moveWordBackward: (id)aSender; +-(void)moveWordBackwardAndModifySelection: (id)aSender; +-(void)moveWordLeftAndModifySelection: (id)aSender; +-(void)moveWordForward: (id)aSender; +-(void)moveWordForwardAndModifySelection: (id)aSender; +-(void)moveWordRightAndModifySelection: (id)aSender; +-(void)moveToEndOfLine: (id)aSender; +-(void)moveToRightEndOfLine: (id)aSender; +-(void)moveToLeftEndOfLine: (id)aSender; +-(void)moveToEndOfLineAndModifySelection: (id)aSender; +-(void)moveToRightEndOfLineAndModifySelection: (id)aSender; +-(void)moveToLeftEndOfLineAndModifySelection: (id)aSender; +-(void)moveToBeginningOfLine: (id)aSender; +-(void)moveToBeginningOfLineAndModifySelection: (id)aSender; +-(void)moveToEndOfParagraph: (id)aSender; +-(void)moveToEndOfParagraphAndModifySelection: (id)aSender; +-(void)moveToBeginningOfParagraph: (id)aSender; +-(void)moveToBeginningOfParagraphAndModifySelection: (id)aSender; +-(void)moveParagraphForward: (id)aSender; +-(void)moveParagraphForwardAndModifySelection: (id)aSender; +-(void)moveParagraphBackward: (id)aSender; +-(void)moveParagraphBackwardAndModifySelection: (id)aSender; +-(void)moveToEndOfDocument: (id)aSender; +-(void)scrollToEndOfDocument: (id)aSender; +-(void)moveToEndOfDocumentAndModifySelection: (id)aSender; +-(void)moveToBeginningOfDocument: (id)aSender; +-(void)scrollToBeginningOfDocument: (id)aSender; +-(void)moveToBeginningOfDocumentAndModifySelection: (id)aSender; +-(void)insertNewline: (id)aSender; +-(void)deleteBackward: (id)aSender; +-(void)deleteForward: (id)aSender; +-(void)cancelOperation: (id)aSender; +-(void)deleteBackwardByDecomposingPreviousCharacter: (id)aSender; +-(void)deleteWordBackward: (id)aSender; +-(void)deleteWordForward: (id)aSender; +-(void)deleteToBeginningOfLine: (id)aSender; +-(void)deleteToEndOfLine: (id)aSender; +-(void)deleteToBeginningOfParagraph: (id)aSender; +-(void)deleteToEndOfParagraph: (id)aSender; +-(void)insertLineBreak: (id)aSender; +-(void)insertParagraphSeparator: (id)aSender; +-(void)selectWord: (id)aSender; +-(void)selectLine: (id)aSender; +-(void)selectParagraph: (id)aSender; +-(void)selectAll: (id)aSender; +-(void)noop: (id)aSender; +/* set the correct pointer for our view */ +-(void)resetCursorRects; +-(::com::sun::star::accessibility::XAccessibleContext *)accessibleContext; +-(id)parentAttribute; +-(NSView *)viewElementForParent; +/* + Event hook for D&D service. + + A drag operation will be invoked on a NSView using + the method 'dragImage'. This method requires the + actual mouse event initiating this drag operation. + Mouse events can only be received by subclassing + NSView and overriding methods like 'mouseDown' etc. + hence we implement a event hook here so that the + D&D service can register as listener for mouse + messages and use the last 'mouseDown' or + 'mouseDragged' message to initiate the drag + operation. +*/ +-(void)registerMouseEventListener: (id)theListener; +-(void)unregisterMouseEventListener: (id)theListener; + +/* NSDraggingDestination protocol methods + */ +-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; +-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; +-(void)draggingExited:(id <NSDraggingInfo>)sender; +-(MacOSBOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender; +-(MacOSBOOL)performDragOperation:(id <NSDraggingInfo>)sender; +-(void)concludeDragOperation:(id <NSDraggingInfo>)sender; + +-(void)registerDraggingDestinationHandler:(id)theHandler; +-(void)unregisterDraggingDestinationHandler:(id)theHandler; + +@end + +#endif diff --git a/vcl/aqua/inc/salgdi.h b/vcl/aqua/inc/salgdi.h new file mode 100644 index 000000000000..8867d1d26078 --- /dev/null +++ b/vcl/aqua/inc/salgdi.h @@ -0,0 +1,417 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALGDI_H +#define _SV_SALGDI_H + +#include "premac.h" +#include <ApplicationServices/ApplicationServices.h> +#include "postmac.h" + +#include "vcl/sv.h" +#include "vcl/outfont.hxx" +#include "vcl/salgdi.hxx" +#include "aquavcltypes.h" + +#include "basegfx/polygon/b2dpolypolygon.hxx" + +#include <vector> + +class AquaSalFrame; +class AquaSalBitmap; +class ImplDevFontAttributes; + +class CGRect; + +// mac specific physically available font face +class ImplMacFontData : public ImplFontData +{ +public: + ImplMacFontData( const ImplDevFontAttributes&, ATSUFontID ); + + virtual ~ImplMacFontData(); + + virtual ImplFontData* Clone() const; + virtual ImplFontEntry* CreateFontInstance( ImplFontSelectData& ) const; + virtual sal_IntPtr GetFontId() const; + + ImplFontCharMap* GetImplFontCharMap() const; + bool HasChar( sal_uInt32 cChar ) const; + + void ReadOs2Table() const; + void ReadMacCmapEncoding() const; + bool HasCJKSupport() const; + +private: + const ATSUFontID mnFontId; + mutable ImplFontCharMap* mpCharMap; + mutable bool mbOs2Read; // true if OS2-table related info is valid + mutable bool mbHasOs2Table; + mutable bool mbCmapEncodingRead; // true if cmap encoding of Mac font is read + mutable bool mbHasCJKSupport; // #i78970# CJK fonts need extra leading +}; + +// abstracting quartz color instead of having to use an CGFloat[] array +class RGBAColor +{ +public: + RGBAColor( SalColor ); + RGBAColor( float fRed, float fGreen, float fBlue, float fAlpha ); //NOTUSEDYET + const float* AsArray() const { return &mfRed; } + bool IsVisible() const { return (mfAlpha > 0); } + void SetAlpha( float fAlpha ) { mfAlpha = fAlpha; } +private: + float mfRed, mfGreen, mfBlue, mfAlpha; +}; + +// ------------------- +// - AquaSalGraphics - +// ------------------- +class AquaSalGraphics : public SalGraphics +{ + friend class ATSLayout; +protected: + AquaSalFrame* mpFrame; + CGLayerRef mxLayer; // Quartz graphics layer + CGContextRef mrContext; // Quartz drawing context + class XorEmulation* mpXorEmulation; + int mnXorMode; // 0: off 1: on 2: invert only + int mnWidth; + int mnHeight; + int mnBitmapDepth; // zero unless bitmap + /// device resolution of this graphics + long mnRealDPIX; + long mnRealDPIY; + /// some graphics implementations (e.g. AquaSalInfoPrinter) scale + /// everything down by a factor (see SetupPrinterGraphics for details) + /// so we have to compensate for it with the inverse factor + double mfFakeDPIScale; + + /// path representing current clip region + CGMutablePathRef mxClipPath; + + /// Drawing colors + /// pen color RGBA + RGBAColor maLineColor; + /// brush color RGBA + RGBAColor maFillColor; + + // Device Font settings + const ImplMacFontData* mpMacFontData; + /// ATSU style object which carries all font attributes + ATSUStyle maATSUStyle; + /// text rotation as ATSU angle + Fixed mnATSUIRotation; + /// workaround to prevent ATSU overflows for huge font sizes + float mfFontScale; + /// <1.0: font is squeezed, >1.0 font is stretched, else 1.0 + float mfFontStretch; + /// allows text to be rendered without antialiasing + bool mbNonAntialiasedText; + + // Graphics types + + /// is this a printer graphics + bool mbPrinter; + /// is this a virtual device graphics + bool mbVirDev; + /// is this a window graphics + bool mbWindow; + +public: + AquaSalGraphics(); + virtual ~AquaSalGraphics(); + + bool IsPenVisible() const { return maLineColor.IsVisible(); } + bool IsBrushVisible() const { return maFillColor.IsVisible(); } + + void SetWindowGraphics( AquaSalFrame* pFrame ); + void SetPrinterGraphics( CGContextRef, long nRealDPIX, long nRealDPIY, double fFakeScale ); + void SetVirDevGraphics( CGLayerRef, CGContextRef, int nBitDepth = 0 ); + + void initResolution( NSWindow* ); + void copyResolution( AquaSalGraphics& ); + void updateResolution(); + + bool IsWindowGraphics() const { return mbWindow; } + bool IsPrinterGraphics() const { return mbPrinter; } + bool IsVirDevGraphics() const { return mbVirDev; } + AquaSalFrame* getGraphicsFrame() const { return mpFrame; } + void setGraphicsFrame( AquaSalFrame* pFrame ) { mpFrame = pFrame; } + + void ImplDrawPixel( long nX, long nY, const RGBAColor& ); // helper to draw single pixels + + bool CheckContext(); + void UpdateWindow( NSRect& ); // delivered in NSView coordinates + void RefreshRect( const CGRect& ); + void RefreshRect( const NSRect& ); + void RefreshRect(float lX, float lY, float lWidth, float lHeight); + + void SetState(); + void UnsetState(); + // InvalidateContext does an UnsetState and sets mrContext to 0 + void InvalidateContext(); + + virtual BOOL unionClipRegion( long nX, long nY, long nWidth, long nHeight ); + virtual bool unionClipRegion( const ::basegfx::B2DPolyPolygon& ); + + // draw --> LineColor and FillColor and RasterOp and ClipRegion + virtual void drawPixel( long nX, long nY ); + virtual void drawPixel( long nX, long nY, SalColor nSalColor ); + virtual void drawLine( long nX1, long nY1, long nX2, long nY2 ); + virtual void drawRect( long nX, long nY, long nWidth, long nHeight ); + virtual void drawPolyLine( ULONG nPoints, const SalPoint* pPtAry ); + virtual void drawPolygon( ULONG nPoints, const SalPoint* pPtAry ); + virtual void drawPolyPolygon( ULONG nPoly, const ULONG* pPoints, PCONSTSALPOINT* pPtAry ); + virtual bool drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double fTransparency ); + virtual sal_Bool drawPolyLineBezier( ULONG nPoints, const SalPoint* pPtAry, const BYTE* pFlgAry ); + virtual sal_Bool drawPolygonBezier( ULONG nPoints, const SalPoint* pPtAry, const BYTE* pFlgAry ); + virtual sal_Bool drawPolyPolygonBezier( ULONG nPoly, const ULONG* pPoints, const SalPoint* const* pPtAry, const BYTE* const* pFlgAry ); + virtual bool drawPolyLine( const ::basegfx::B2DPolygon&, double fTransparency, const ::basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin ); + + // CopyArea --> No RasterOp, but ClipRegion + virtual void copyArea( long nDestX, long nDestY, long nSrcX, long nSrcY, long nSrcWidth, + long nSrcHeight, USHORT nFlags ); + + // CopyBits and DrawBitmap --> RasterOp and ClipRegion + // CopyBits() --> pSrcGraphics == NULL, then CopyBits on same Graphics + virtual void copyBits( const SalTwoRect* pPosAry, SalGraphics* pSrcGraphics ); + virtual void drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap ); + virtual void drawBitmap( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, + SalColor nTransparentColor ); + virtual void drawBitmap( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, + const SalBitmap& rTransparentBitmap ); + virtual void drawMask( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, + SalColor nMaskColor ); + + virtual SalBitmap* getBitmap( long nX, long nY, long nWidth, long nHeight ); + virtual SalColor getPixel( long nX, long nY ); + + // invert --> ClipRegion (only Windows or VirDevs) + virtual void invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags); + virtual void invert( ULONG nPoints, const SalPoint* pPtAry, SalInvert nFlags ); + + virtual BOOL drawEPS( long nX, long nY, long nWidth, long nHeight, void* pPtr, ULONG nSize ); + + virtual bool drawAlphaBitmap( const SalTwoRect&, + const SalBitmap& rSourceBitmap, + const SalBitmap& rAlphaBitmap ); + + virtual bool drawAlphaRect( long nX, long nY, long nWidth, + long nHeight, sal_uInt8 nTransparency ); + + CGPoint* makeCGptArray(ULONG nPoints, const SalPoint* pPtAry); + // native widget rendering methods that require mirroring + virtual BOOL hitTestNativeControl( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, + const Point& aPos, BOOL& rIsInside ); + virtual BOOL drawNativeControl( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, + ControlState nState, const ImplControlValue& aValue, + const rtl::OUString& aCaption ); + virtual BOOL drawNativeControlText( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, + ControlState nState, const ImplControlValue& aValue, + const rtl::OUString& aCaption ); + virtual BOOL getNativeControlRegion( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, ControlState nState, + const ImplControlValue& aValue, const rtl::OUString& aCaption, + Rectangle &rNativeBoundingRegion, Rectangle &rNativeContentRegion ); + + // get device resolution + virtual void GetResolution( long& rDPIX, long& rDPIY ); + // get the depth of the device + virtual USHORT GetBitCount(); + // get the width of the device + virtual long GetGraphicsWidth() const; + + // set the clip region to empty + virtual void ResetClipRegion(); + // begin setting the clip region, add rectangles to the + // region with the UnionClipRegion call + virtual void BeginSetClipRegion( ULONG nCount ); + // all rectangles were added and the clip region should be set now + virtual void EndSetClipRegion(); + + // set the line color to transparent (= don't draw lines) + virtual void SetLineColor(); + // set the line color to a specific color + virtual void SetLineColor( SalColor nSalColor ); + // set the fill color to transparent (= don't fill) + virtual void SetFillColor(); + // set the fill color to a specific color, shapes will be + // filled accordingly + virtual void SetFillColor( SalColor nSalColor ); + // enable/disable XOR drawing + virtual void SetXORMode( bool bSet, bool bInvertOnly ); + // set line color for raster operations + virtual void SetROPLineColor( SalROPColor nROPColor ); + // set fill color for raster operations + virtual void SetROPFillColor( SalROPColor nROPColor ); + // set the text color to a specific color + virtual void SetTextColor( SalColor nSalColor ); + // set the font + virtual USHORT SetFont( ImplFontSelectData*, int nFallbackLevel ); + // get the current font's etrics + virtual void GetFontMetric( ImplFontMetricData* ); + // get kernign pairs of the current font + // return only PairCount if (pKernPairs == NULL) + virtual ULONG GetKernPairs( ULONG nPairs, ImplKernPairData* pKernPairs ); + // get the repertoire of the current font + virtual ImplFontCharMap* GetImplFontCharMap() const; + // graphics must fill supplied font list + virtual void GetDevFontList( ImplDevFontList* ); + // graphics should call ImplAddDevFontSubstitute on supplied + // OutputDevice for all its device specific preferred font substitutions + virtual void GetDevFontSubstList( OutputDevice* ); + virtual bool AddTempDevFont( ImplDevFontList*, const String& rFileURL, const String& rFontName ); + // CreateFontSubset: a method to get a subset of glyhps of a font + // inside a new valid font file + // returns TRUE if creation of subset was successfull + // parameters: rToFile: contains a osl file URL to write the subset to + // pFont: describes from which font to create a subset + // pGlyphIDs: the glyph ids to be extracted + // pEncoding: the character code corresponding to each glyph + // pWidths: the advance widths of the correspoding glyphs (in PS font units) + // nGlyphs: the number of glyphs + // rInfo: additional outgoing information + // implementation note: encoding 0 with glyph id 0 should be added implicitly + // as "undefined character" + virtual BOOL CreateFontSubset( const rtl::OUString& rToFile, + const ImplFontData* pFont, + long* pGlyphIDs, + sal_uInt8* pEncoding, + sal_Int32* pWidths, + int nGlyphs, + FontSubsetInfo& rInfo // out parameter + ); + + // GetFontEncodingVector: a method to get the encoding map Unicode + // to font encoded character; this is only used for type1 fonts and + // may return NULL in case of unknown encoding vector + // if ppNonEncoded is set and non encoded characters (that is type1 + // glyphs with only a name) exist it is set to the corresponding + // map for non encoded glyphs; the encoding vector contains -1 + // as encoding for these cases + virtual const Ucs2SIntMap* GetFontEncodingVector( const ImplFontData*, const Ucs2OStrMap** ppNonEncoded ); + + // GetEmbedFontData: gets the font data for a font marked + // embeddable by GetDevFontList or NULL in case of error + // parameters: pFont: describes the font in question + // pWidths: the widths of all glyphs from char code 0 to 255 + // pWidths MUST support at least 256 members; + // rInfo: additional outgoing information + // pDataLen: out parameter, contains the byte length of the returned buffer + virtual const void* GetEmbedFontData( const ImplFontData*, + const sal_Ucs* pUnicodes, + sal_Int32* pWidths, + FontSubsetInfo& rInfo, + long* pDataLen ); + // frees the font data again + virtual void FreeEmbedFontData( const void* pData, long nDataLen ); + + virtual void GetGlyphWidths( const ImplFontData*, + bool bVertical, + Int32Vector& rWidths, + Ucs2UIntMap& rUnicodeEnc ); + + virtual BOOL GetGlyphBoundRect( long nIndex, Rectangle& ); + virtual BOOL GetGlyphOutline( long nIndex, basegfx::B2DPolyPolygon& ); + + virtual SalLayout* GetTextLayout( ImplLayoutArgs&, int nFallbackLevel ); + virtual void DrawServerFontLayout( const ServerFontLayout& ); + virtual bool supportsOperation( OutDevSupportType ) const; + + // Query the platform layer for control support + virtual BOOL IsNativeControlSupported( ControlType nType, ControlPart nPart ); + + virtual SystemGraphicsData GetGraphicsData() const; + virtual SystemFontData GetSysFontData( int /* nFallbacklevel */ ) const; + +private: + // differences between VCL, Quartz and kHiThemeOrientation coordinate systems + // make some graphics seem to be vertically-mirrored from a VCL perspective + bool IsFlipped() const { return mbWindow; } + + void ApplyXorContext(); + void Pattern50Fill(); + UInt32 getState( ControlState nState ); + UInt32 getTrackState( ControlState nState ); +}; + +class XorEmulation +{ +public: + XorEmulation(); + /*final*/ ~XorEmulation(); + + void SetTarget( int nWidth, int nHeight, int nBitmapDepth, CGContextRef, CGLayerRef ); + bool UpdateTarget(); + void Enable() { mbIsEnabled = true; } + void Disable() { mbIsEnabled = false; } + bool IsEnabled() const { return mbIsEnabled; } + CGContextRef GetTargetContext() const { return mxTargetContext; } + CGContextRef GetMaskContext() const { return (mbIsEnabled ? mxMaskContext : NULL); } + +private: + CGLayerRef mxTargetLayer; + CGContextRef mxTargetContext; + CGContextRef mxMaskContext; + CGContextRef mxTempContext; + ULONG* mpMaskBuffer; + ULONG* mpTempBuffer; + int mnBufferLongs; + bool mbIsEnabled; +}; + + +// --- some trivial inlines + +inline void AquaSalGraphics::RefreshRect( const CGRect& rRect ) +{ + RefreshRect( rRect.origin.x, rRect.origin.y, rRect.size.width, rRect.size.height ); +} + +inline void AquaSalGraphics::RefreshRect( const NSRect& rRect ) +{ + RefreshRect( rRect.origin.x, rRect.origin.y, rRect.size.width, rRect.size.height ); +} + +inline RGBAColor::RGBAColor( SalColor nSalColor ) +: mfRed( SALCOLOR_RED(nSalColor) * (1.0/255)) +, mfGreen( SALCOLOR_GREEN(nSalColor) * (1.0/255)) +, mfBlue( SALCOLOR_BLUE(nSalColor) * (1.0/255)) +, mfAlpha( 1.0 ) // opaque +{} + +inline RGBAColor::RGBAColor( float fRed, float fGreen, float fBlue, float fAlpha ) +: mfRed( fRed ) +, mfGreen( fGreen ) +, mfBlue( fBlue ) +, mfAlpha( fAlpha ) +{} + +#endif // _SV_SALGDI_H diff --git a/vcl/aqua/inc/salinst.h b/vcl/aqua/inc/salinst.h new file mode 100644 index 000000000000..0bceb99d1d0e --- /dev/null +++ b/vcl/aqua/inc/salinst.h @@ -0,0 +1,202 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALINST_H +#define _SV_SALINST_H + +#include "vcl/sv.h" +#include "vos/mutex.hxx" +#include "vos/thread.hxx" +#include "vcl/salinst.hxx" +#include "osl/conditn.h" + +#include "aquavcltypes.h" + +#include <list> + +class AquaSalFrame; +class ApplicationEvent; +class Image; + +// ----------------- +// - SalYieldMutex - +// ----------------- + +class SalYieldMutex : public vos::OMutex +{ + ULONG mnCount; + vos::OThread::TThreadIdentifier mnThreadId; + +public: + SalYieldMutex(); + virtual void acquire(); + virtual void release(); + virtual sal_Bool tryToAcquire(); + ULONG GetAcquireCount() const { return mnCount; } + vos::OThread::TThreadIdentifier GetThreadId() const { return mnThreadId; } +}; + +#define YIELD_GUARD vos::OGuard aGuard( GetSalData()->mpFirstInstance->GetYieldMutex() ) + + +// ------------------- +// - SalInstanceData - +// ------------------- + +//struct SalInstanceData +//{ +//public: +//}; + +// ------------------ +// - AquaSalInstance - +// ------------------ + +class AquaSalInstance : public SalInstance +{ + struct SalUserEvent + { + AquaSalFrame* mpFrame; + void* mpData; + USHORT mnType; + + SalUserEvent( AquaSalFrame* pFrame, void* pData, USHORT nType ) : + mpFrame( pFrame ), mpData( pData ), mnType( nType ) + {} + }; + +public: + SalYieldMutex* mpSalYieldMutex; // Sal-Yield-Mutex + rtl::OUString maDefaultPrinter; + vos::OThread::TThreadIdentifier maMainThread; + bool mbWaitingYield; + int mnActivePrintJobs; + std::list< SalUserEvent > maUserEvents; + oslMutex maUserEventListMutex; + oslCondition maWaitingYieldCond; + + typedef std::list<const ApplicationEvent*> AppEventList; + static AppEventList aAppEventList; + +public: + AquaSalInstance(); + virtual ~AquaSalInstance(); + + virtual SalSystem* CreateSystem(); + virtual void DestroySystem(SalSystem*); + virtual SalFrame* CreateChildFrame( SystemParentData* pParent, ULONG nStyle ); + virtual SalFrame* CreateFrame( SalFrame* pParent, ULONG nStyle ); + virtual void DestroyFrame( SalFrame* pFrame ); + virtual SalObject* CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, BOOL bShow = TRUE ); + virtual void DestroyObject( SalObject* pObject ); + virtual SalVirtualDevice* CreateVirtualDevice( SalGraphics* pGraphics, + long nDX, long nDY, + USHORT nBitCount, const SystemGraphicsData *pData ); + virtual void DestroyVirtualDevice( SalVirtualDevice* pDevice ); + + virtual SalInfoPrinter* CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo, + ImplJobSetup* pSetupData ); + virtual void DestroyInfoPrinter( SalInfoPrinter* pPrinter ); + virtual SalPrinter* CreatePrinter( SalInfoPrinter* pInfoPrinter ); + virtual void DestroyPrinter( SalPrinter* pPrinter ); + virtual void GetPrinterQueueInfo( ImplPrnQueueList* pList ); + virtual void GetPrinterQueueState( SalPrinterQueueInfo* pInfo ); + virtual void DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo ); + virtual String GetDefaultPrinter(); + virtual SalTimer* CreateSalTimer(); + virtual SalI18NImeStatus* CreateI18NImeStatus(); + virtual SalSystem* CreateSalSystem(); + virtual SalBitmap* CreateSalBitmap(); + virtual vos::IMutex* GetYieldMutex(); + virtual ULONG ReleaseYieldMutex(); + virtual void AcquireYieldMutex( ULONG nCount ); + virtual void Yield( bool bWait, bool bHandleAllCurrentEvents ); + virtual bool AnyInput( USHORT nType ); + virtual SalMenu* CreateMenu( BOOL bMenuBar ); + virtual void DestroyMenu( SalMenu* ); + virtual SalMenuItem* CreateMenuItem( const SalItemParams* pItemData ); + virtual void DestroyMenuItem( SalMenuItem* ); + virtual SalSession* CreateSalSession(); + virtual void* GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes ); + virtual void AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& rMimeType); + virtual void SetEventCallback( void* pInstance, bool(*pCallback)(void*,void*,int) ); + virtual void SetErrorEventCallback( void* pInstance, bool(*pCallback)(void*,void*,int) ); + + // dtrans implementation + virtual com::sun::star::uno::Reference< com::sun::star::uno::XInterface > + CreateClipboard( const com::sun::star::uno::Sequence< com::sun::star::uno::Any >& i_rArguments ); + virtual com::sun::star::uno::Reference< com::sun::star::uno::XInterface > CreateDragSource(); + virtual com::sun::star::uno::Reference< com::sun::star::uno::XInterface > CreateDropTarget(); + + static void handleAppDefinedEvent( NSEvent* pEvent ); + + // check whether a particular string is passed on the command line + // this is needed to avoid duplicate open events through a) command line and b) NSApp's openFile + static bool isOnCommandLine( const rtl::OUString& ); + + void wakeupYield(); + + public: + friend class AquaSalFrame; + + void PostUserEvent( AquaSalFrame* pFrame, USHORT nType, void* pData ); + void delayedSettingsChanged( bool bInvalidate ); + + bool isNSAppThread() const; + + void startedPrintJob() { mnActivePrintJobs++; } + void endedPrintJob() { mnActivePrintJobs--; } + + // event subtypes for NSApplicationDefined events + static const short AppExecuteSVMain = 0x7fff; + static const short AppEndLoopEvent = 1; + static const short AppStartTimerEvent = 10; + static const short AppleRemoteEvent = 15; + static const short YieldWakeupEvent = 20; + + static NSMenu* GetDynamicDockMenu(); +}; + +// helper class: inverted solar guard +class YieldMutexReleaser +{ + ULONG mnCount; + public: + YieldMutexReleaser(); + ~YieldMutexReleaser(); +}; + +// helper class +rtl::OUString GetOUString( CFStringRef ); +rtl::OUString GetOUString( NSString* ); +CFStringRef CreateCFString( const rtl::OUString& ); +NSString* CreateNSString( const rtl::OUString& ); + +CGImageRef CreateCGImage( const Image& ); +NSImage* CreateNSImage( const Image& ); + +#endif // _SV_SALINST_H diff --git a/vcl/aqua/inc/salmathutils.hxx b/vcl/aqua/inc/salmathutils.hxx new file mode 100755 index 000000000000..6106dc328740 --- /dev/null +++ b/vcl/aqua/inc/salmathutils.hxx @@ -0,0 +1,87 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALMATHUTILS_HXX +#define _SV_SALMATHUTILS_HXX + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------------------------------------------------------ +// +// Structures +// +// ------------------------------------------------------------------ + +// LRectCoor is an abreviation for rectangular coordinates +// represented as long integers + +struct LRectCoor +{ + long x; + long y; + long z; +}; + +// ------------------------------------------------------------------ +// +// Type Definitions +// +// ------------------------------------------------------------------ + +// LRectCoorVec is an abreviation for vectors in rectangular +// coordinates represented as long integers + +typedef struct LRectCoor LRectCoor; +typedef LRectCoor *LRectCoorVector; +typedef LRectCoorVector *LRectCoorTensor; + +// ------------------------------------------------------------------ +// +// Function Headers +// +// ------------------------------------------------------------------ + +void CSwap ( char &rX, char &rY ); +void UCSwap ( unsigned char &rX, unsigned char &rY ); +void SSwap ( short &rX, short &rY ); +void USSwap ( unsigned short &rX, unsigned short &rY ); +void LSwap ( long &rX, long &rY ); +void ULSwap ( unsigned long &rX, unsigned long &rY ); + +// ------------------------------------------------------------------ + +unsigned long Euclidian2Norm ( const LRectCoorVector pVec ); + +// ------------------------------------------------------------------ + +#ifdef __cplusplus +} +#endif + +#endif // _SV_SALMATHUTILS_HXX diff --git a/vcl/aqua/inc/salmenu.h b/vcl/aqua/inc/salmenu.h new file mode 100644 index 000000000000..100e8c22972c --- /dev/null +++ b/vcl/aqua/inc/salmenu.h @@ -0,0 +1,121 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALMENU_H +#define _SV_SALMENU_H + +#include "premac.h" +#include <Cocoa/Cocoa.h> +#include "postmac.h" + +#include "vcl/sv.h" +#include "vcl/salmenu.hxx" + +#include <vector> + +class AquaSalFrame; +class AquaSalMenuItem; + +class AquaSalMenu : public SalMenu +{ + std::vector< AquaSalMenuItem* > maItems; + +public: // for OOStatusView + struct MenuBarButtonEntry + { + SalMenuButtonItem maButton; + NSImage* mpNSImage; // cached image + NSString* mpToolTipString; + + MenuBarButtonEntry() : mpNSImage( nil ), mpToolTipString( nil ) {} + MenuBarButtonEntry( const SalMenuButtonItem& i_rItem ) + : maButton( i_rItem), mpNSImage( nil ), mpToolTipString( nil ) {} + }; +private: + std::vector< MenuBarButtonEntry > maButtons; + + MenuBarButtonEntry* findButtonItem( USHORT i_nItemId ); + void releaseButtonEntry( MenuBarButtonEntry& i_rEntry ); + static void statusLayout(); +public: + AquaSalMenu( bool bMenuBar ); + virtual ~AquaSalMenu(); + + virtual BOOL VisibleMenuBar(); // must return TRUE to actually DISPLAY native menu bars + // otherwise only menu messages are processed (eg, OLE on Windows) + + virtual void InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos ); + virtual void RemoveItem( unsigned nPos ); + virtual void SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos ); + virtual void SetFrame( const SalFrame* pFrame ); + virtual void CheckItem( unsigned nPos, BOOL bCheck ); + virtual void EnableItem( unsigned nPos, BOOL bEnable ); + virtual void SetItemText( unsigned nPos, SalMenuItem* pSalMenuItem, const XubString& rText ); + virtual void SetItemImage( unsigned nPos, SalMenuItem* pSalMenuItem, const Image& rImage); + virtual void SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const KeyCode& rKeyCode, const XubString& rKeyName ); + virtual void GetSystemMenuData( SystemMenuData* pData ); + virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, ULONG nFlags); + virtual bool AddMenuBarButton( const SalMenuButtonItem& ); + virtual void RemoveMenuBarButton( USHORT nId ); + virtual Rectangle GetMenuBarButtonRectPixel( USHORT i_nItemId, SalFrame* i_pReferenceFrame ); + + int getItemIndexByPos( USHORT nPos ) const; + const AquaSalFrame* getFrame() const; + + void setMainMenu(); + static void unsetMainMenu(); + static void setDefaultMenu(); + static void enableMainMenu( bool bEnable ); + static void addFallbackMenuItem( NSMenuItem* NewItem ); + static void removeFallbackMenuItem( NSMenuItem* pOldItem ); + + const std::vector< MenuBarButtonEntry >& getButtons() const { return maButtons; } + + bool mbMenuBar; // true - Menubar, false - Menu + NSMenu* mpMenu; // The Carbon reference to this menu + Menu* mpVCLMenu; // the corresponding vcl Menu object + const AquaSalFrame* mpFrame; // the frame to dispatch the menu events to + AquaSalMenu* mpParentSalMenu; // the parent menu that contains us (and perhaps has a frame) + + static const AquaSalMenu* pCurrentMenuBar; + +}; + +class AquaSalMenuItem : public SalMenuItem +{ +public: + AquaSalMenuItem( const SalItemParams* ); + virtual ~AquaSalMenuItem(); + + USHORT mnId; // Item ID + Menu* mpVCLMenu; // VCL Menu into which this MenuItem is inserted + AquaSalMenu* mpParentMenu; // The menu in which this menu item is inserted + AquaSalMenu* mpSubMenu; // Sub menu of this item (if defined) + NSMenuItem* mpMenuItem; // The NSMenuItem +}; + +#endif // _SV_SALMENU_H diff --git a/vcl/aqua/inc/salnativewidgets.h b/vcl/aqua/inc/salnativewidgets.h new file mode 100755 index 000000000000..11d4ea5c1c62 --- /dev/null +++ b/vcl/aqua/inc/salnativewidgets.h @@ -0,0 +1,71 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_NATIVEWIDGETS_H +#define _SV_NATIVEWIDGETS_H + +// since 10.4 ... no metrics are returned, and we have to fix the values +#define BUTTON_WIDTH 16 +#define BUTTON_HEIGHT 17 + +//standard height of the AHIG +//tabs +#define TAB_HEIGHT_NORMAL 20 +#define TAB_HEIGHT_SMALL 17 +#define TAB_HEIGHT_MINI 15 + +#define TAB_TEXT_OFFSET 12 +#define VCL_TAB_TEXT_OFFSET 2 + +//listboxes, comboboxes (they have the same dimensions) +#define COMBOBOX_HEIGHT_NORMAL 20 +#define DROPDOWN_BUTTON_WIDTH 20 + +//text edit +#define TEXT_EDIT_HEIGHT_NORMAL 22 + +//spin box +#define SPIN_BUTTON_SPACE 2 +#define SPIN_BUTTON_WIDTH 13 +#define SPIN_UPPER_BUTTON_HEIGHT 11 +#define SPIN_LOWER_BUTTON_HEIGHT 10 +#define SPIN_TWO_BUTTONS_HEIGHT 21 + +// progress bar +#define INTRO_PROGRESS_HEIGHT 9 + +// for some controls, like spinbuttons + spinboxes, or listboxes +// we need it to adjust text position beside radio and check buttons + +#define TEXT_SEPARATOR 3 + +// extra border for focus ring +#define FOCUS_RING_WIDTH 4 + +#define CLIP_FUZZ 1 + +#endif // _SV_NATIVEWIDGETS_H diff --git a/vcl/aqua/inc/salnsmenu.h b/vcl/aqua/inc/salnsmenu.h new file mode 100755 index 000000000000..e9b2cbe922b8 --- /dev/null +++ b/vcl/aqua/inc/salnsmenu.h @@ -0,0 +1,68 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _VCL_SALNSMENU_H +#define _VCL_SALNSMENU_H + +class AquaSalMenu; +class AquaSalMenuItem; + +@interface OOStatusItemView : NSView +{ +} +-(void)drawRect: (NSRect)aRect; +-(void)layout; +-(void)mouseUp: (NSEvent *)pEvent; +@end + +@interface SalNSMenu : NSMenu +{ + /* Caution: SalNSMenu instances occasionally are binary copied + in AquaSalMenu::ShowNativePopupMenu. If any members are added, + please take this into account ! + */ + AquaSalMenu* mpMenu; +} +-(id)initWithMenu: (AquaSalMenu*)pMenu; +-(void)menuNeedsUpdate: (NSMenu*)pMenu; +-(void)setSalMenu: (AquaSalMenu*)pMenu; +@end + +@interface SalNSMenuItem : NSMenuItem +{ + /* Caution: SalNSMenuItem instances occasionally are binary copied + in AquaSalMenu::ShowNativePopupMenu. If any members are added, + please take this into account ! + */ + AquaSalMenuItem* mpMenuItem; +} +-(id)initWithMenuItem: (AquaSalMenuItem*)pMenuItem; +-(void)menuItemTriggered: (id)aSender; +@end + + +#endif diff --git a/vcl/aqua/inc/salnstimer.h b/vcl/aqua/inc/salnstimer.h new file mode 100755 index 000000000000..e29fef43b6b4 --- /dev/null +++ b/vcl/aqua/inc/salnstimer.h @@ -0,0 +1,40 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _VCL_SALNSTIMER_H +#define _VCL_SALNSTIMER_H + +#include "premac.h" +#include "Cocoa/Cocoa.h" +#include "postmac.h" + +@interface TimerCallbackCaller : NSObject +{ +} +-(void)timerElapsed:(NSTimer*)pTimer; +@end + +#endif diff --git a/vcl/aqua/inc/salobj.h b/vcl/aqua/inc/salobj.h new file mode 100644 index 000000000000..0041b22c16a0 --- /dev/null +++ b/vcl/aqua/inc/salobj.h @@ -0,0 +1,86 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALOBJ_H +#define _SV_SALOBJ_H + +#include "vcl/sv.h" +#include "vcl/sysdata.hxx" +#include "vcl/salobj.hxx" + +class AquaSalFrame; +class AquaSalObject; + + +// ----------------- +// - SalObjectData - +// ----------------- + +struct SalObjectData +{ +}; + +class AquaSalObject : public SalObject +{ +public: + AquaSalFrame* mpFrame; // parent frame + NSClipView* mpClipView; + SystemEnvData maSysData; + + long mnClipX; + long mnClipY; + long mnClipWidth; + long mnClipHeight; + bool mbClip; + + long mnX; + long mnY; + long mnWidth; + long mnHeight; + + + void setClippedPosSize(); + + + AquaSalObject( AquaSalFrame* pFrame ); + virtual ~AquaSalObject(); + + virtual void ResetClipRegion(); + virtual USHORT GetClipRegionType(); + virtual void BeginSetClipRegion( ULONG nRects ); + virtual void UnionClipRegion( long nX, long nY, long nWidth, long nHeight ); + virtual void EndSetClipRegion(); + virtual void SetPosSize( long nX, long nY, long nWidth, long nHeight ); + virtual void Show( BOOL bVisible ); + virtual void Enable( BOOL nEnable ); + virtual void GrabFocus(); + virtual void SetBackground(); + virtual void SetBackground( SalColor nSalColor ); + virtual const SystemEnvData* GetSystemData() const; +}; + +#endif // _SV_SALOBJ_H diff --git a/vcl/aqua/inc/salprn.h b/vcl/aqua/inc/salprn.h new file mode 100644 index 000000000000..6bcafa2ee2e3 --- /dev/null +++ b/vcl/aqua/inc/salprn.h @@ -0,0 +1,171 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALPRN_H +#define _SV_SALPRN_H + +#include "vcl/sv.h" +#include "aquavcltypes.h" +#include "vcl/salprn.hxx" + +#include <boost/shared_array.hpp> + + +// --------------------- +// - AquaSalInfoPrinter - +// --------------------- + +class AquaSalGraphics; + +class AquaSalInfoPrinter : public SalInfoPrinter +{ + /// Printer graphics + AquaSalGraphics* mpGraphics; + /// is Graphics used + bool mbGraphics; + /// job active ? + bool mbJob; + + /// cocoa printer object + NSPrinter* mpPrinter; + /// cocoa print info object + NSPrintInfo* mpPrintInfo; + + /// FIXME: get real printer context for infoprinter if possible + /// fake context for info printer + /// graphics context for Quartz 2D + CGContextRef mrContext; + /// memory for graphics bitmap context for querying metrics + boost::shared_array< sal_uInt8 > maContextMemory; + + // since changes to NSPrintInfo during a job are ignored + // we have to care for some settings ourselves + // currently we do this for orientation; + // really needed however is a solution for paper formats + Orientation mePageOrientation; + + int mnStartPageOffsetX; + int mnStartPageOffsetY; + sal_Int32 mnCurPageRangeStart; + sal_Int32 mnCurPageRangeCount; + + public: + AquaSalInfoPrinter( const SalPrinterQueueInfo& pInfo ); + virtual ~AquaSalInfoPrinter(); + + void SetupPrinterGraphics( CGContextRef i_xContext ) const; + + virtual SalGraphics* GetGraphics(); + virtual void ReleaseGraphics( SalGraphics* i_pGraphics ); + virtual BOOL Setup( SalFrame* i_pFrame, ImplJobSetup* i_pSetupData ); + virtual BOOL SetPrinterData( ImplJobSetup* pSetupData ); + virtual BOOL SetData( ULONG i_nFlags, ImplJobSetup* i_pSetupData ); + virtual void GetPageInfo( const ImplJobSetup* i_pSetupData, + long& o_rOutWidth, long& o_rOutHeight, + long& o_rPageOffX, long& o_rPageOffY, + long& o_rPageWidth, long& o_rPageHeight ); + virtual ULONG GetCapabilities( const ImplJobSetup* i_pSetupData, USHORT i_nType ); + virtual ULONG GetPaperBinCount( const ImplJobSetup* i_pSetupData ); + virtual String GetPaperBinName( const ImplJobSetup* i_pSetupData, ULONG i_nPaperBin ); + virtual void InitPaperFormats( const ImplJobSetup* i_pSetupData ); + virtual int GetLandscapeAngle( const ImplJobSetup* i_pSetupData ); + + // the artificial separation between InfoPrinter and Printer + // is not really useful for us + // so let's make AquaSalPrinter just a forwarder to AquaSalInfoPrinter + // and concentrate the real work in one class + // implement pull model print system + BOOL StartJob( const String* i_pFileName, + const String& rJobName, + const String& i_rAppName, + ImplJobSetup* i_pSetupData, + vcl::PrinterController& i_rController ); + BOOL EndJob(); + BOOL AbortJob(); + SalGraphics* StartPage( ImplJobSetup* i_pSetupData, BOOL i_bNewJobData ); + BOOL EndPage(); + ULONG GetErrorCode() const; + + NSPrintInfo* getPrintInfo() const { return mpPrintInfo; } + void setStartPageOffset( int nOffsetX, int nOffsetY ) { mnStartPageOffsetX = nOffsetX; mnStartPageOffsetY = nOffsetY; } + sal_Int32 getCurPageRangeStart() const { return mnCurPageRangeStart; } + sal_Int32 getCurPageRangeCount() const { return mnCurPageRangeCount; } + + // match width/height against known paper formats, possibly switching orientation + const PaperInfo* matchPaper( long i_nWidth, long i_nHeight, Orientation& o_rOrientation ) const; + void setPaperSize( long i_nWidth, long i_nHeight, Orientation i_eSetOrientation ); + + private: + AquaSalInfoPrinter( const AquaSalInfoPrinter& ); + AquaSalInfoPrinter& operator=(const AquaSalInfoPrinter&); +}; + +// ----------------- +// - AquaSalPrinter - +// ----------------- + +class AquaSalPrinter : public SalPrinter +{ + AquaSalInfoPrinter* mpInfoPrinter; // pointer to the compatible InfoPrinter + public: + AquaSalPrinter( AquaSalInfoPrinter* i_pInfoPrinter ); + virtual ~AquaSalPrinter(); + + virtual BOOL StartJob( const XubString* i_pFileName, + const XubString& i_rJobName, + const XubString& i_rAppName, + ULONG i_nCopies, + bool i_bCollate, + bool i_bDirect, + ImplJobSetup* i_pSetupData ); + // implement pull model print system + virtual BOOL StartJob( const String* i_pFileName, + const String& rJobName, + const String& i_rAppName, + ImplJobSetup* i_pSetupData, + vcl::PrinterController& i_rListener ); + + virtual BOOL EndJob(); + virtual BOOL AbortJob(); + virtual SalGraphics* StartPage( ImplJobSetup* i_pSetupData, BOOL i_bNewJobData ); + virtual BOOL EndPage(); + virtual ULONG GetErrorCode(); + + private: + AquaSalPrinter( const AquaSalPrinter& ); + AquaSalPrinter& operator=(const AquaSalPrinter&); +}; + +const double fPtTo100thMM = 35.27777778; + +inline int PtTo10Mu( double nPoints ) { return (int)(((nPoints)*fPtTo100thMM)+0.5); } + +inline double TenMuToPt( double nUnits ) { return floor(((nUnits)/fPtTo100thMM)+0.5); } + + + +#endif // _SV_SALPRN_H diff --git a/vcl/aqua/inc/salsys.h b/vcl/aqua/inc/salsys.h new file mode 100644 index 000000000000..6f5c45880e68 --- /dev/null +++ b/vcl/aqua/inc/salsys.h @@ -0,0 +1,70 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALSYS_H +#define _SV_SALSYS_H + +#include "vcl/sv.h" +#include "vcl/salsys.hxx" + +#include <list> + +// ----------------- +// - SalSystemData - +// ----------------- + +//struct SalSystemData +//{ +//}; + +class VCL_DLLPUBLIC AquaSalSystem : public SalSystem +{ +public: + AquaSalSystem() {} + virtual ~AquaSalSystem(); + + // get info about the display + virtual unsigned int GetDisplayScreenCount(); + virtual bool IsMultiDisplay(); + virtual unsigned int GetDefaultDisplayNumber(); + virtual Rectangle GetDisplayScreenPosSizePixel( unsigned int nScreen ); + virtual Rectangle GetDisplayWorkAreaPosSizePixel( unsigned int nScreen ); + + virtual rtl::OUString GetScreenName( unsigned int nScreen ); + // overload pure virtual methods + virtual int ShowNativeDialog( const String& rTitle, + const String& rMessage, + const std::list< String >& rButtons, + int nDefButton ); + virtual int ShowNativeMessageBox( const String& rTitle, + const String& rMessage, + int nButtonCombination, + int nDefaultButton); +}; + + +#endif // _SV_SALSYS_H diff --git a/vcl/aqua/inc/saltimer.h b/vcl/aqua/inc/saltimer.h new file mode 100644 index 000000000000..374b9c5a45c5 --- /dev/null +++ b/vcl/aqua/inc/saltimer.h @@ -0,0 +1,54 @@ +/************************************************************************* +* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +#ifndef _SV_SALTIMER_H +#define _SV_SALTIMER_H + +#include "premac.h" +#include <Cocoa/Cocoa.h> +#include "postmac.h" + +#include "vcl/saltimer.hxx" + +class AquaSalTimer : public SalTimer +{ + public: + + AquaSalTimer(); + virtual ~AquaSalTimer(); + + void Start( ULONG nMS ); + void Stop(); + + static void handleStartTimerEvent( NSEvent* pEvent ); + + + static NSTimer* pRunningTimer; + static bool bDispatchTimer; +}; + +#endif diff --git a/vcl/aqua/inc/salvd.h b/vcl/aqua/inc/salvd.h new file mode 100644 index 000000000000..865cb7b5b766 --- /dev/null +++ b/vcl/aqua/inc/salvd.h @@ -0,0 +1,94 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SALVD_H +#define _SV_SALVD_H + +#include "premac.h" +#include <ApplicationServices/ApplicationServices.h> +#include "postmac.h" + +#include "vcl/sv.h" +#include "vcl/salgdi.hxx" +#include "salconst.h" +#include "salcolorutils.hxx" +#include "vcl/salvd.hxx" +#include "salgdi.h" + +#if PRAGMA_ONCE + #pragma once +#endif + +// ======================================================================= + +// ======================================================================= + +// ----------------- +// - SalVirDevData - +// ----------------- + +struct SalVirDevData +{ +}; + +typedef struct SalVirDevData SalVirDevData; +typedef SalVirDevData *SalVirDevDataPtr; +typedef SalVirDevDataPtr *SalVirDevDataHandle; + +// ======================================================================= + +class AquaSalGraphics; + +// ----------------- +// - SalVirDevData - +// ----------------- + +class AquaSalVirtualDevice : public SalVirtualDevice +{ +private: + bool mbGraphicsUsed; // is Graphics used + bool mbForeignContext; // is mxContext from outside VCL + CGContextRef mxBitmapContext; + int mnBitmapDepth; + CGLayerRef mxLayer; // Quartz layer + AquaSalGraphics* mpGraphics; // current VirDev graphics + + void Destroy(); + +public: + AquaSalVirtualDevice( AquaSalGraphics* pGraphic, long nDX, long nDY, USHORT nBitCount, const SystemGraphicsData *pData ); + virtual ~AquaSalVirtualDevice(); + + virtual SalGraphics* GetGraphics(); + virtual void ReleaseGraphics( SalGraphics* pGraphics ); + virtual BOOL SetSize( long nNewDX, long nNewDY ); + virtual void GetSize( long& rWidth, long& rHeight ); +}; + +// ======================================================================= + +#endif // _SV_SALVD_H diff --git a/vcl/aqua/inc/svsys.h b/vcl/aqua/inc/svsys.h new file mode 100644 index 000000000000..1edce25cea28 --- /dev/null +++ b/vcl/aqua/inc/svsys.h @@ -0,0 +1,35 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_SVSYS_H +#define _SV_SVSYS_H + +#include "premac.h" +#include "Cocoa/Cocoa.h" +#include "postmac.h" + +#endif // _SV_SVSYS_H diff --git a/vcl/aqua/inc/vclnsapp.h b/vcl/aqua/inc/vclnsapp.h new file mode 100755 index 000000000000..59b070b421ea --- /dev/null +++ b/vcl/aqua/inc/vclnsapp.h @@ -0,0 +1,70 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _VCL_VCLNSAPP_H +#define _VCL_VCLNSAPP_H + +#include "premac.h" +#include "Cocoa/Cocoa.h" +#include "postmac.h" + +class AquaSalFrame; + +@interface CocoaThreadEnabler : NSObject +{ +} +-(void)enableCocoaThreads:(id)param; +@end + +// our very own application +@interface VCL_NSApplication : NSApplication +{ +} +-(void)sendEvent:(NSEvent*)pEvent; +-(void)sendSuperEvent:(NSEvent*)pEvent; +-(NSMenu*)applicationDockMenu:(NSApplication *)sender; +-(MacOSBOOL)application: (NSApplication*) app openFile: (NSString*)file; +-(void)application: (NSApplication*) app openFiles: (NSArray*)files; +-(MacOSBOOL)application: (NSApplication*) app printFile: (NSString*)file; +-(NSApplicationPrintReply)application: (NSApplication *) app printFiles:(NSArray *)files withSettings: (NSDictionary *)printSettings showPrintPanels:(MacOSBOOL)bShowPrintPanels; +-(NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *) app; +-(void)systemColorsChanged: (NSNotification*) pNotification; +-(void)screenParametersChanged: (NSNotification*) pNotification; +-(void)scrollbarVariantChanged: (NSNotification*) pNotification; +-(void)scrollbarSettingsChanged: (NSNotification*) pNotification; +-(void)addFallbackMenuItem: (NSMenuItem*)pNewItem; +-(void)removeFallbackMenuItem: (NSMenuItem*)pOldItem; +-(void)addDockMenuItem: (NSMenuItem*)pNewItem; +-(void)applicationWillBecomeActive: (NSNotification *)pNotification; +-(void)applicationWillResignActive: (NSNotification *)pNotification; +-(MacOSBOOL)applicationShouldHandleReopen: (NSApplication*)pApp hasVisibleWindows: (MacOSBOOL)bWinVisible; +-(void)setDockIconClickHandler: (NSObject*)pHandler; +-(void)cycleFrameForward: (AquaSalFrame*)pCurFrame; +-(void)cycleFrameBackward: (AquaSalFrame*)pCurFrame; +@end + +#endif diff --git a/vcl/aqua/source/a11y/aqua11yactionwrapper.h b/vcl/aqua/source/a11y/aqua11yactionwrapper.h new file mode 100644 index 000000000000..3a7f13f8a545 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yactionwrapper.h @@ -0,0 +1,41 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11ACTIONWRAPPER_H +#define _SV_AQUA11ACTIONWRAPPER_H + +#include "aquavcltypes.h" +#include "aqua11ywrapper.h" + +@interface AquaA11yActionWrapper : NSObject +{ +} ++(NSArray *)actionNamesForElement:(AquaA11yWrapper *)wrapper; ++(void)doAction:(NSString *)action ofElement:(AquaA11yWrapper *)wrapper; +@end + +#endif // _SV_AQUA11ACTIONWRAPPER_H diff --git a/vcl/aqua/source/a11y/aqua11yactionwrapper.mm b/vcl/aqua/source/a11y/aqua11yactionwrapper.mm new file mode 100644 index 000000000000..fcd49fd67ff4 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yactionwrapper.mm @@ -0,0 +1,83 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11yactionwrapper.h" + +// Wrapper for XAccessibleAction + +@implementation AquaA11yActionWrapper : NSObject + ++(NSString *)nativeActionNameFor:(NSString *)actionName { + // TODO: Optimize ? + // Use NSAccessibilityActionDescription + if ( [ actionName isEqualToString: @"click" ] ) { + return NSAccessibilityPressAction; + } else if ( [ actionName isEqualToString: @"togglePopup" ] ) { + return NSAccessibilityShowMenuAction; + } else if ( [ actionName isEqualToString: @"select" ] ) { + return NSAccessibilityPickAction; + } else if ( [ actionName isEqualToString: @"incrementLine" ] ) { + return NSAccessibilityIncrementAction; + } else if ( [ actionName isEqualToString: @"decrementLine" ] ) { + return NSAccessibilityDecrementAction; + } else if ( [ actionName isEqualToString: @"incrementBlock" ] ) { + return NSAccessibilityIncrementAction; // TODO ? + } else if ( [ actionName isEqualToString: @"decrementBlock" ] ) { + return NSAccessibilityDecrementAction; // TODO ? + } else if ( [ actionName isEqualToString: @"Browse" ] ) { + return NSAccessibilityPressAction; // TODO ? + } else { + return [ NSString string ]; + } +} + ++(NSArray *)actionNamesForElement:(AquaA11yWrapper *)wrapper { + NSMutableArray * actionNames = [ [ NSMutableArray alloc ] init ]; + if ( [ wrapper accessibleAction ] != nil ) { + for ( int cnt = 0; cnt < [ wrapper accessibleAction ] -> getAccessibleActionCount(); cnt++ ) { + [ actionNames addObject: [ AquaA11yActionWrapper nativeActionNameFor: CreateNSString ( [ wrapper accessibleAction ] -> getAccessibleActionDescription ( cnt ) ) ] ]; + } + } + return actionNames; +} + ++(void)doAction:(NSString *)action ofElement:(AquaA11yWrapper *)wrapper { + if ( [ wrapper accessibleAction ] != nil ) { + for ( int cnt = 0; cnt < [ wrapper accessibleAction ] -> getAccessibleActionCount(); cnt++ ) { + if ( [ action isEqualToString: [ AquaA11yActionWrapper nativeActionNameFor: CreateNSString ( [ wrapper accessibleAction ] -> getAccessibleActionDescription ( cnt ) ) ] ] ) { + [ wrapper accessibleAction ] -> doAccessibleAction ( cnt ); + break; + } + } + } +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ycomponentwrapper.h b/vcl/aqua/source/a11y/aqua11ycomponentwrapper.h new file mode 100644 index 000000000000..c1806054e253 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ycomponentwrapper.h @@ -0,0 +1,45 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11COMPONENTWRAPPER_H +#define _SV_AQUA11COMPONENTWRAPPER_H + +#include "aquavcltypes.h" +#include "aqua11ywrapper.h" + +@interface AquaA11yComponentWrapper : NSObject +{ +} ++(id)sizeAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)positionAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)descriptionAttributeForElement:(AquaA11yWrapper *)wrapper; ++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames; ++(MacOSBOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper; ++(void)setFocusedAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value; +@end + +#endif // _SV_AQUA11COMPONENTWRAPPER_H diff --git a/vcl/aqua/source/a11y/aqua11ycomponentwrapper.mm b/vcl/aqua/source/a11y/aqua11ycomponentwrapper.mm new file mode 100644 index 000000000000..a700b0b89ae9 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ycomponentwrapper.mm @@ -0,0 +1,110 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "aqua11ycomponentwrapper.h" +#include "aqua11yrolehelper.h" +#include <com/sun/star/accessibility/AccessibleRole.hpp> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::uno; + +// Wrapper for XAccessibleComponent and XAccessibleExtendedComponent + +@implementation AquaA11yComponentWrapper : NSObject + ++(id)sizeAttributeForElement:(AquaA11yWrapper *)wrapper { + Size size = [ wrapper accessibleComponent ] -> getSize(); + NSSize nsSize = NSMakeSize ( (float) size.Width, (float) size.Height ); + return [ NSValue valueWithSize: nsSize ]; +} + +// TODO: should be merged with AquaSalFrame::VCLToCocoa... to a general helper method ++(id)positionAttributeForElement:(AquaA11yWrapper *)wrapper { + // VCL coordinates are in upper-left-notation, Cocoa likes it the Cartesian way (lower-left) + NSRect screenRect = [ [ NSScreen mainScreen ] frame ]; + Size size = [ wrapper accessibleComponent ] -> getSize(); + Point location = [ wrapper accessibleComponent ] -> getLocationOnScreen(); + NSPoint nsPoint = NSMakePoint ( (float) location.X, (float) ( screenRect.size.height - size.Height - location.Y ) ); + return [ NSValue valueWithPoint: nsPoint ]; +} + ++(id)descriptionAttributeForElement:(AquaA11yWrapper *)wrapper { + if ( [ wrapper accessibleExtendedComponent ] != nil ) { + return CreateNSString ( [ wrapper accessibleExtendedComponent ] -> getToolTipText() ); + } else { + return nil; + } +} + ++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames { + NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ]; + [ attributeNames addObjectsFromArray: [ NSArray arrayWithObjects: + NSAccessibilitySizeAttribute, + NSAccessibilityPositionAttribute, + NSAccessibilityFocusedAttribute, + NSAccessibilityEnabledAttribute, + nil ] ]; + [ pool release ]; +} + ++(MacOSBOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper { + MacOSBOOL isSettable = NO; + NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ]; + if ( [ attribute isEqualToString: NSAccessibilityFocusedAttribute ] + && ! [ [ AquaA11yRoleHelper getNativeRoleFrom: [ wrapper accessibleContext ] ] isEqualToString: NSAccessibilityScrollBarRole ] + && ! [ [ AquaA11yRoleHelper getNativeRoleFrom: [ wrapper accessibleContext ] ] isEqualToString: NSAccessibilityStaticTextRole ] ) { + isSettable = YES; + } + [ pool release ]; + return isSettable; +} + ++(void)setFocusedAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value { + if ( [ value boolValue ] == YES ) { + if ( [ wrapper accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) { + // special treatment for comboboxes: find the corresponding PANEL and set focus to it + Reference < XAccessible > rxParent = [ wrapper accessibleContext ] -> getAccessibleParent(); + if ( rxParent.is() ) { + Reference < XAccessibleContext > rxContext = rxParent->getAccessibleContext(); + if ( rxContext.is() && rxContext -> getAccessibleRole() == AccessibleRole::PANEL ) { + Reference < XAccessibleComponent > rxComponent = Reference < XAccessibleComponent > ( rxParent -> getAccessibleContext(), UNO_QUERY ); + if ( rxComponent.is() ) { + rxComponent -> grabFocus(); + } + } + } + } else { + [ wrapper accessibleComponent ] -> grabFocus(); + } + } +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11yfactory.mm b/vcl/aqua/source/a11y/aqua11yfactory.mm new file mode 100644 index 000000000000..7732ce202cd2 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yfactory.mm @@ -0,0 +1,198 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11yfactory.h" +#include "aqua11yfocuslistener.hxx" +#include "aqua11yfocustracker.hxx" +#include "aqua11yrolehelper.h" +#include "aqua11ywrapperbutton.h" +#include "aqua11ywrapperstatictext.h" +#include "aqua11ywrappertextarea.h" +#include "aqua11ywrappercheckbox.h" +#include "aqua11ywrappercombobox.h" +#include "aqua11ywrappergroup.h" +#include "aqua11ywrapperlist.h" +#include "aqua11ywrapperradiobutton.h" +#include "aqua11ywrapperradiogroup.h" +#include "aqua11ywrapperrow.h" +#include "aqua11ywrapperscrollarea.h" +#include "aqua11ywrapperscrollbar.h" +#include "aqua11ywrappersplitter.h" +#include "aqua11ywrappertabgroup.h" +#include "aqua11ywrappertoolbar.h" +#include "aqua11ytablewrapper.h" +#include <com/sun/star/accessibility/AccessibleStateType.hpp> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + +static bool enabled = false; + +@implementation AquaA11yFactory : NSObject + +#pragma mark - +#pragma mark Wrapper Repository + ++(NSMutableDictionary *)allWrapper { + static NSMutableDictionary * mdAllWrapper = nil; + if ( mdAllWrapper == nil ) { + mdAllWrapper = [ [ [ NSMutableDictionary alloc ] init ] retain ]; + // initialize keyboard focus tracker + rtl::Reference< AquaA11yFocusListener > listener( AquaA11yFocusListener::get() ); + AquaA11yFocusTracker::get().setFocusListener(listener.get()); + enabled = true; + } + return mdAllWrapper; +} + ++(NSValue *)keyForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext { + return [ NSValue valueWithPointer: rxAccessibleContext.get() ]; +} + ++(NSValue *)keyForAccessibleContextAsRadioGroup: (Reference < XAccessibleContext >) rxAccessibleContext { + return [ NSValue valueWithPointer: ( rxAccessibleContext.get() + 2 ) ]; +} + ++(AquaA11yWrapper *)wrapperForAccessible: (Reference < XAccessible >) rxAccessible { + if ( rxAccessible.is() ) { + Reference< XAccessibleContext > xAccessibleContext = rxAccessible->getAccessibleContext(); + if( xAccessibleContext.is() ) { + return [ AquaA11yFactory wrapperForAccessibleContext: xAccessibleContext ]; + } + } + return nil; +} + ++(AquaA11yWrapper *)wrapperForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext { + return [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: YES asRadioGroup: NO ]; +} + ++(AquaA11yWrapper *)wrapperForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext createIfNotExists:(MacOSBOOL) bCreate { + return [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: bCreate asRadioGroup: NO ]; +} + ++(AquaA11yWrapper *)wrapperForAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext createIfNotExists:(MacOSBOOL) bCreate asRadioGroup:(MacOSBOOL) asRadioGroup{ + NSMutableDictionary * dAllWrapper = [ AquaA11yFactory allWrapper ]; + NSValue * nKey = nil; + if ( asRadioGroup ) { + nKey = [ AquaA11yFactory keyForAccessibleContextAsRadioGroup: rxAccessibleContext ]; + } else { + nKey = [ AquaA11yFactory keyForAccessibleContext: rxAccessibleContext ]; + } + AquaA11yWrapper * aWrapper = (AquaA11yWrapper *) [ dAllWrapper objectForKey: nKey ]; + if ( aWrapper != nil ) { + [ aWrapper retain ]; + } else if ( bCreate ) { + NSString * nativeRole = [ AquaA11yRoleHelper getNativeRoleFrom: rxAccessibleContext.get() ]; + // TODO: reflection + if ( [ nativeRole isEqualToString: NSAccessibilityButtonRole ] ) { + aWrapper = [ [ AquaA11yWrapperButton alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityTextAreaRole ] ) { + aWrapper = [ [ AquaA11yWrapperTextArea alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityStaticTextRole ] ) { + aWrapper = [ [ AquaA11yWrapperStaticText alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityComboBoxRole ] ) { + aWrapper = [ [ AquaA11yWrapperComboBox alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityGroupRole ] ) { + aWrapper = [ [ AquaA11yWrapperGroup alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityToolbarRole ] ) { + aWrapper = [ [ AquaA11yWrapperToolbar alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityScrollAreaRole ] ) { + aWrapper = [ [ AquaA11yWrapperScrollArea alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityTabGroupRole ] ) { + aWrapper = [ [ AquaA11yWrapperTabGroup alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityScrollBarRole ] ) { + aWrapper = [ [ AquaA11yWrapperScrollBar alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityCheckBoxRole ] ) { + aWrapper = [ [ AquaA11yWrapperCheckBox alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityRadioGroupRole ] ) { + aWrapper = [ [ AquaA11yWrapperRadioGroup alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityRadioButtonRole ] ) { + aWrapper = [ [ AquaA11yWrapperRadioButton alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityRowRole ] ) { + aWrapper = [ [ AquaA11yWrapperRow alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityListRole ] ) { + aWrapper = [ [ AquaA11yWrapperList alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilitySplitterRole ] ) { + aWrapper = [ [ AquaA11yWrapperSplitter alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else if ( [ nativeRole isEqualToString: NSAccessibilityTableRole ] ) { + aWrapper = [ [ AquaA11yTableWrapper alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } else { + aWrapper = [ [ AquaA11yWrapper alloc ] initWithAccessibleContext: rxAccessibleContext ]; + } + [ nativeRole release ]; + [ aWrapper setActsAsRadioGroup: asRadioGroup ]; + #if 0 + /* #i102033# NSAccessibility does not seemt to know an equivalent for transient children. + That means we need to cache this, else e.g. tree list boxes are not accessible (moreover + it crashes by notifying dead objects - which would seemt o be another bug) + + FIXME: + Unfortunately this can increase memory consumption drastically until the non transient parent + is destroyed an finally all the transients are released. + */ + if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::TRANSIENT ) ) + #endif + { + [ dAllWrapper setObject: aWrapper forKey: nKey ]; + } + } + return aWrapper; +} + ++(void)insertIntoWrapperRepository: (NSView *) viewElement forAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext { + NSMutableDictionary * dAllWrapper = [ AquaA11yFactory allWrapper ]; + [ dAllWrapper setObject: viewElement forKey: [ AquaA11yFactory keyForAccessibleContext: rxAccessibleContext ] ]; +} + ++(void)removeFromWrapperRepositoryFor: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) rxAccessibleContext { + // TODO: when RADIO_BUTTON search for associated RadioGroup-wrapper and delete that as well + AquaA11yWrapper * theWrapper = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: NO ]; + if ( theWrapper != nil ) { + [ [ AquaA11yFactory allWrapper ] removeObjectForKey: [ AquaA11yFactory keyForAccessibleContext: rxAccessibleContext ] ]; + [ theWrapper release ]; + } +} + ++(void)registerView: (NSView *) theView { + if ( enabled && [ theView isKindOfClass: [ AquaA11yWrapper class ] ] ) { + // insertIntoWrapperRepository gets called from SalFrameView itself to bootstrap the bridge initially + [ (AquaA11yWrapper *) theView accessibleContext ]; + } +} + ++(void)revokeView: (NSView *) theView { + if ( enabled && [ theView isKindOfClass: [ AquaA11yWrapper class ] ] ) { + [ AquaA11yFactory removeFromWrapperRepositoryFor: [ (AquaA11yWrapper *) theView accessibleContext ] ]; + } +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11yfocuslistener.cxx b/vcl/aqua/source/a11y/aqua11yfocuslistener.cxx new file mode 100644 index 000000000000..9ac9401abd62 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yfocuslistener.cxx @@ -0,0 +1,118 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "aqua11yfocuslistener.hxx" +#include "aqua11yfocustracker.hxx" +#include "aqua11yfactory.h" + +#include <salhelper/refobj.hxx> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + + +rtl::Reference< AquaA11yFocusListener > AquaA11yFocusListener::theListener; + +//------------------------------------------------------------------------------ + +rtl::Reference< AquaA11yFocusListener > AquaA11yFocusListener::get() +{ + if ( ! theListener.is() ) + theListener = new AquaA11yFocusListener(); + + return theListener; +} + +//------------------------------------------------------------------------------ + +AquaA11yFocusListener::AquaA11yFocusListener() : m_focusedObject(nil) +{ +} + +//------------------------------------------------------------------------------ + +id AquaA11yFocusListener::getFocusedUIElement() +{ + if ( nil == m_focusedObject ) { + Reference< XAccessible > xAccessible( AquaA11yFocusTracker::get().getFocusedObject() ); + try { + if( xAccessible.is() ) { + Reference< XAccessibleContext > xContext(xAccessible->getAccessibleContext()); + if( xContext.is() ) + m_focusedObject = [ AquaA11yFactory wrapperForAccessibleContext: xContext ]; + } + } catch( RuntimeException ) { + // intentionally do nothing .. + } + } + + return m_focusedObject; +} + +//------------------------------------------------------------------------------ + +void SAL_CALL +AquaA11yFocusListener::focusedObjectChanged(const Reference< XAccessible >& xAccessible) +{ + if ( nil != m_focusedObject ) { + [ m_focusedObject release ]; + m_focusedObject = nil; + } + + try { + if( xAccessible.is() ) { + Reference< XAccessibleContext > xContext(xAccessible->getAccessibleContext()); + if( xContext.is() ) + { + m_focusedObject = [ AquaA11yFactory wrapperForAccessibleContext: xContext ]; + NSAccessibilityPostNotification(m_focusedObject, NSAccessibilityFocusedUIElementChangedNotification); + } + } + } catch( RuntimeException ) { + // intentionally do nothing .. + } +} + +//------------------------------------------------------------------------------ + +oslInterlockedCount SAL_CALL +AquaA11yFocusListener::acquire() SAL_THROW(()) +{ + return ReferenceObject::acquire(); +} + +//------------------------------------------------------------------------------ + +oslInterlockedCount SAL_CALL +AquaA11yFocusListener::release() SAL_THROW(()) +{ + return ReferenceObject::release(); +} + diff --git a/vcl/aqua/source/a11y/aqua11yfocuslistener.hxx b/vcl/aqua/source/a11y/aqua11yfocuslistener.hxx new file mode 100644 index 000000000000..1fdd340c698e --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yfocuslistener.hxx @@ -0,0 +1,62 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _AQUA11YFOCUSLISTENER_HXX_ +#define _AQUA11YFOCUSLISTENER_HXX_ + +#include <salhelper/refobj.hxx> + +#include "keyboardfocuslistener.hxx" +#include "aquavcltypes.h" + +// #include <com/sun/star/accessibility/XAccessibleContext.hpp> + +class AquaA11yFocusListener : + public KeyboardFocusListener, + public salhelper::ReferenceObject +{ + id m_focusedObject; + + static rtl::Reference< AquaA11yFocusListener > theListener; + + AquaA11yFocusListener::AquaA11yFocusListener(); + virtual AquaA11yFocusListener::~AquaA11yFocusListener() {}; +public: + + static rtl::Reference< AquaA11yFocusListener > get(); + + id getFocusedUIElement(); + + // KeyboardFocusListener + virtual void SAL_CALL focusedObjectChanged(const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible); + + // rtl::IReference + virtual oslInterlockedCount SAL_CALL acquire() SAL_THROW(()); + virtual oslInterlockedCount SAL_CALL release() SAL_THROW(()); +}; + +#endif // _AQUA11YFOCUSLISTENER_HXX_
\ No newline at end of file diff --git a/vcl/aqua/source/a11y/aqua11yfocustracker.cxx b/vcl/aqua/source/a11y/aqua11yfocustracker.cxx new file mode 100644 index 000000000000..2a8ebb39bd80 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yfocustracker.cxx @@ -0,0 +1,278 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "aqua11yfocustracker.hxx" +#include "documentfocuslistener.hxx" + +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/accessibility/XAccessibleStateSet.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> + +#include "vcl/svapp.hxx" +#include "vcl/window.hxx" +#include "vcl/toolbox.hxx" +#include "vcl/menu.hxx" + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + +//------------------------------------------------------------------------------ + +static inline Window * +getWindow(const ::VclSimpleEvent *pEvent) +{ + return static_cast< const ::VclWindowEvent *> (pEvent)->GetWindow(); +} + + +//------------------------------------------------------------------------------ + +// callback function for Application::addEventListener + +long AquaA11yFocusTracker::WindowEventHandler(AquaA11yFocusTracker *pFocusTracker, ::VclSimpleEvent const *pEvent) +{ + switch (pEvent->GetId()) + { + case VCLEVENT_WINDOW_PAINT: + pFocusTracker-> toolbox_open_floater( getWindow(pEvent) ); + break; + case VCLEVENT_WINDOW_GETFOCUS: + pFocusTracker->window_got_focus( getWindow(pEvent) ); + break; + case VCLEVENT_OBJECT_DYING: + pFocusTracker->m_aDocumentWindowList.erase( getWindow(pEvent) ); + // intentional pass through .. + case VCLEVENT_TOOLBOX_HIGHLIGHTOFF: + pFocusTracker->toolbox_highlight_off( getWindow(pEvent) ); + break; + case VCLEVENT_TOOLBOX_HIGHLIGHT: + pFocusTracker->toolbox_highlight_on( getWindow(pEvent) ); + break; + case VCLEVENT_TABPAGE_ACTIVATE: + pFocusTracker->tabpage_activated( getWindow(pEvent) ); + break; + case VCLEVENT_MENU_HIGHLIGHT: + pFocusTracker->menu_highlighted( static_cast < const VclMenuEvent * > (pEvent) ); + break; + default: + break; + }; + + return 0; +} + +//------------------------------------------------------------------------------ + +AquaA11yFocusTracker::AquaA11yFocusTracker() : + m_aWindowEventLink(this, (PSTUB) WindowEventHandler), + m_xDocumentFocusListener(new DocumentFocusListener(*this)) +{ + Application::AddEventListener(m_aWindowEventLink); + window_got_focus(Application::GetFocusWindow()); +} + +//------------------------------------------------------------------------------ + +void AquaA11yFocusTracker::setFocusedObject(const Reference< XAccessible >& xAccessible) +{ + if( xAccessible != m_xFocusedObject ) + { + m_xFocusedObject = xAccessible; + + if( m_aFocusListener.is() ) + m_aFocusListener->focusedObjectChanged(xAccessible); + } +} + +//------------------------------------------------------------------------------ + +void AquaA11yFocusTracker::notify_toolbox_item_focus(ToolBox *pToolBox) +{ + Reference< XAccessible > xAccessible( pToolBox->GetAccessible() ); + + if( xAccessible.is() ) + { + Reference< XAccessibleContext > xContext(xAccessible->getAccessibleContext()); + + if( xContext.is() ) + { + sal_Int32 nPos = pToolBox->GetItemPos( pToolBox->GetHighlightItemId() ); + if( nPos != TOOLBOX_ITEM_NOTFOUND ) + setFocusedObject( xContext->getAccessibleChild( nPos ) ); + } + } +} + +//------------------------------------------------------------------------------ + +void AquaA11yFocusTracker::toolbox_open_floater(Window *pWindow) +{ + bool bToolboxFound = false; + bool bFloatingWindowFound = false; + Window * pFloatingWindow = NULL; + while ( pWindow != NULL ) { + if ( pWindow->GetType() == WINDOW_TOOLBOX ) { + bToolboxFound = true; + } else if ( pWindow->GetType() == WINDOW_FLOATINGWINDOW ) { + bFloatingWindowFound = true; + pFloatingWindow = pWindow; + } + pWindow = pWindow->GetParent(); + } + if ( bToolboxFound && bFloatingWindowFound ) { + Reference < XAccessible > rxAccessible = pFloatingWindow -> GetAccessible(); + if ( ! rxAccessible.is() ) { + return; + } + Reference < XAccessibleContext > rxContext = rxAccessible -> getAccessibleContext(); + if ( ! rxContext.is() ) { + return; + } + if ( rxContext -> getAccessibleChildCount() > 0 ) { + Reference < XAccessible > rxAccessibleChild = rxContext -> getAccessibleChild( 0 ); + if ( ! rxAccessibleChild.is() ) { + return; + } + setFocusedObject ( rxAccessibleChild ); + } + } +} + +//------------------------------------------------------------------------------ + +void AquaA11yFocusTracker::toolbox_highlight_on(Window *pWindow) +{ + // Make sure either the toolbox or its parent toolbox has the focus + if ( ! pWindow->HasFocus() ) + { + ToolBox* pToolBoxParent = dynamic_cast< ToolBox * >( pWindow->GetParent() ); + if ( ! pToolBoxParent || ! pToolBoxParent->HasFocus() ) + return; + } + + notify_toolbox_item_focus(static_cast <ToolBox *> (pWindow)); +} + +//------------------------------------------------------------------------------ + +void AquaA11yFocusTracker::toolbox_highlight_off(Window *pWindow) +{ + ToolBox* pToolBoxParent = dynamic_cast< ToolBox * >( pWindow->GetParent() ); + + // Notify when leaving sub toolboxes + if( pToolBoxParent && pToolBoxParent->HasFocus() ) + notify_toolbox_item_focus( pToolBoxParent ); +} + +//------------------------------------------------------------------------------ + +void AquaA11yFocusTracker::tabpage_activated(Window *pWindow) +{ + Reference< XAccessible > xAccessible( pWindow->GetAccessible() ); + + if( xAccessible.is() ) + { + Reference< XAccessibleSelection > xSelection(xAccessible->getAccessibleContext(), UNO_QUERY); + + if( xSelection.is() ) + setFocusedObject( xSelection->getSelectedAccessibleChild(0) ); + } +} + +//------------------------------------------------------------------------------ + +void AquaA11yFocusTracker::menu_highlighted(const VclMenuEvent *pEvent) +{ + Menu * pMenu = pEvent->GetMenu(); + + if( pMenu ) + { + Reference< XAccessible > xAccessible( pMenu->GetAccessible() ); + + if( xAccessible.is() ) + setFocusedObject( xAccessible ); + } +} + +//------------------------------------------------------------------------------ + +void AquaA11yFocusTracker::window_got_focus(Window *pWindow) +{ + // The menu bar is handled through VCLEVENT_MENU_HIGHLIGHTED + if( ! pWindow || !pWindow->IsReallyVisible() || pWindow->GetType() == WINDOW_MENUBARWINDOW ) + return; + + // ToolBoxes are handled through VCLEVENT_TOOLBOX_HIGHLIGHT + if( pWindow->GetType() == WINDOW_TOOLBOX ) + return; + + if( pWindow->GetType() == WINDOW_TABCONTROL ) + { + tabpage_activated( pWindow ); + return; + } + + Reference< XAccessible > xAccessible(pWindow->GetAccessible()); + + if( ! xAccessible.is() ) + return; + + Reference< XAccessibleContext > xContext = xAccessible->getAccessibleContext(); + + if( ! xContext.is() ) + return; + + Reference< XAccessibleStateSet > xStateSet = xContext->getAccessibleStateSet(); + + if( ! xStateSet.is() ) + return; + +/* the UNO ToolBox wrapper does not (yet?) support XAccessibleSelection, so we + * need to add listeners to the children instead of re-using the tabpage stuff + */ + if( xStateSet->contains(AccessibleStateType::FOCUSED) && (pWindow->GetType() != WINDOW_TREELISTBOX) ) + { + setFocusedObject( xAccessible ); + } + else + { + if( m_aDocumentWindowList.find(pWindow) == m_aDocumentWindowList.end() ) + { + m_aDocumentWindowList.insert(pWindow); + m_xDocumentFocusListener->attachRecursive(xAccessible, xContext, xStateSet); + } +#ifdef ENABLE_TRACING + else + fprintf(stderr, "Window %p already in the list\n", pWindow ); +#endif + } +} diff --git a/vcl/aqua/source/a11y/aqua11ylistener.cxx b/vcl/aqua/source/a11y/aqua11ylistener.cxx new file mode 100644 index 000000000000..7f680f43b3a6 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ylistener.cxx @@ -0,0 +1,158 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "aqua11ylistener.hxx" +#include "aqua11yfactory.h" +#include "aqua11yfocustracker.hxx" +#include "aqua11ytextwrapper.h" +#include "aqua11ywrapper.h" +#include "salinst.h" + +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; + +NSString * getTableNotification( const AccessibleEventObject& aEvent ) +{ + AccessibleTableModelChange aChange; + NSString * notification = nil; + + if( (aEvent.NewValue >>= aChange) && + ( AccessibleTableModelChangeType::INSERT == aChange.Type || AccessibleTableModelChangeType::DELETE == aChange.Type ) && + aChange.FirstRow != aChange.LastRow ) + { + notification = NSAccessibilityRowCountChangedNotification; + } + + return notification; +} + +//------------------------------------------------------------------------------ + +AquaA11yEventListener::AquaA11yEventListener(id wrapperObject, sal_Int16 role) : m_wrapperObject(wrapperObject), m_role(role) +{ + [ m_wrapperObject retain ]; +} + +//------------------------------------------------------------------------------ + +AquaA11yEventListener::~AquaA11yEventListener() +{ + [ m_wrapperObject release ]; +} + +//------------------------------------------------------------------------------ + +void SAL_CALL +AquaA11yEventListener::disposing( const EventObject& Source ) throw( RuntimeException ) +{ + [ AquaA11yFactory removeFromWrapperRepositoryFor: [ (AquaA11yWrapper *) m_wrapperObject accessibleContext ] ]; +} + +//------------------------------------------------------------------------------ + +void SAL_CALL +AquaA11yEventListener::notifyEvent( const AccessibleEventObject& aEvent ) throw( RuntimeException ) +{ + NSString * notification = nil; + id element = m_wrapperObject; + Rectangle bounds; + + // TODO: NSAccessibilityValueChanged, NSAccessibilitySelectedRowsChangedNotification + switch( aEvent.EventId ) + { + case AccessibleEventId::ACTIVE_DESCENDANT_CHANGED: + if( m_role != AccessibleRole::LIST ) { + Reference< XAccessible > xAccessible; + if( aEvent.NewValue >>= xAccessible ) + AquaA11yFocusTracker::get().setFocusedObject( xAccessible ); + } + break; + + case AccessibleEventId::NAME_CHANGED: + notification = NSAccessibilityTitleChangedNotification; + break; + + case AccessibleEventId::CHILD: + // only needed for tooltips (says Apple) + if ( m_role == AccessibleRole::TOOL_TIP ) { + if(aEvent.NewValue.hasValue()) { + notification = NSAccessibilityCreatedNotification; + } else if(aEvent.OldValue.hasValue()) { + notification = NSAccessibilityUIElementDestroyedNotification; + } + } + break; + + case AccessibleEventId::INVALIDATE_ALL_CHILDREN: + // TODO: depricate or remember all children + break; + + case AccessibleEventId::BOUNDRECT_CHANGED: + bounds = [ element accessibleComponent ] -> getBounds(); + if ( m_oldBounds.X != 0 && ( bounds.X != m_oldBounds.X || bounds.Y != m_oldBounds.Y ) ) { + NSAccessibilityPostNotification(element, NSAccessibilityMovedNotification); // post directly since both cases can happen simultaneously + } + if ( m_oldBounds.X != 0 && ( bounds.Width != m_oldBounds.Width || bounds.Height != m_oldBounds.Height ) ) { + NSAccessibilityPostNotification(element, NSAccessibilityResizedNotification); // post directly since both cases can happen simultaneously + } + m_oldBounds = bounds; + break; + + case AccessibleEventId::SELECTION_CHANGED: + notification = NSAccessibilitySelectedChildrenChangedNotification; + break; + + case AccessibleEventId::TEXT_SELECTION_CHANGED: + notification = NSAccessibilitySelectedTextChangedNotification; + break; + + case AccessibleEventId::TABLE_MODEL_CHANGED: + notification = getTableNotification(aEvent); + break; + + case AccessibleEventId::CARET_CHANGED: + notification = NSAccessibilitySelectedTextChangedNotification; + break; + + case AccessibleEventId::TEXT_CHANGED: + notification = NSAccessibilityValueChangedNotification; + break; + + default: + break; + } + + if( nil != notification ) + NSAccessibilityPostNotification(element, notification); +} diff --git a/vcl/aqua/source/a11y/aqua11yrolehelper.h b/vcl/aqua/source/a11y/aqua11yrolehelper.h new file mode 100644 index 000000000000..f847eb3f41c3 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yrolehelper.h @@ -0,0 +1,42 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11ROLEHELPER_H +#define _SV_AQUA11ROLEHELPER_H + +#include "salinst.h" +#include <com/sun/star/accessibility/XAccessibleContext.hpp> + +@interface AquaA11yRoleHelper : NSObject +{ +} ++(id)getNativeRoleFrom: (::com::sun::star::accessibility::XAccessibleContext *) accessibleContext; ++(id)getNativeSubroleFrom: (sal_Int16) nRole; ++(id)getRoleDescriptionFrom: (NSString *) role with: (NSString *) subRole; +@end + +#endif // _SV_AQUA11ROLEHELPER_H diff --git a/vcl/aqua/source/a11y/aqua11yrolehelper.mm b/vcl/aqua/source/a11y/aqua11yrolehelper.mm new file mode 100644 index 000000000000..b8ebdb08c3df --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yrolehelper.mm @@ -0,0 +1,272 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "aqua11yrolehelper.h" +#include "aqua11yfactory.h" +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + +@implementation AquaA11yRoleHelper + ++(id)simpleMapNativeRoleFrom: (XAccessibleContext *) accessibleContext { + id nativeRole = nil; + switch( accessibleContext -> getAccessibleRole() ) { +#define MAP(a,b) \ + case a: nativeRole = b; break + + MAP( AccessibleRole::UNKNOWN, NSAccessibilityUnknownRole ); + MAP( AccessibleRole::ALERT, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::COLUMN_HEADER, NSAccessibilityColumnRole ); + MAP( AccessibleRole::CANVAS, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::CHECK_BOX, NSAccessibilityCheckBoxRole ); + MAP( AccessibleRole::CHECK_MENU_ITEM, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::COLOR_CHOOSER, NSAccessibilityColorWellRole ); // FIXME + MAP( AccessibleRole::COMBO_BOX, NSAccessibilityComboBoxRole ); + MAP( AccessibleRole::DATE_EDITOR, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::DESKTOP_ICON, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::DESKTOP_PANE, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::DIRECTORY_PANE, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::DIALOG, NSAccessibilityGroupRole ); + MAP( AccessibleRole::DOCUMENT, NSAccessibilityGroupRole ); + MAP( AccessibleRole::EMBEDDED_OBJECT, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::END_NOTE, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::FILE_CHOOSER, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::FILLER, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::FONT_CHOOSER, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::FOOTER, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::FOOTNOTE, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::FRAME, NSAccessibilityWindowRole ); + MAP( AccessibleRole::GLASS_PANE, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::GRAPHIC, NSAccessibilityImageRole ); + MAP( AccessibleRole::GROUP_BOX, NSAccessibilityGroupRole ); + MAP( AccessibleRole::HEADER, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::HEADING, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::HYPER_LINK, NSAccessibilityLinkRole ); + MAP( AccessibleRole::ICON, NSAccessibilityImageRole ); + MAP( AccessibleRole::INTERNAL_FRAME, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::LABEL, NSAccessibilityStaticTextRole ); + MAP( AccessibleRole::LAYERED_PANE, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::LIST, NSAccessibilityMenuRole ); + MAP( AccessibleRole::LIST_ITEM, NSAccessibilityMenuItemRole ); + MAP( AccessibleRole::MENU, NSAccessibilityMenuRole ); + MAP( AccessibleRole::MENU_BAR, NSAccessibilityMenuBarRole ); + MAP( AccessibleRole::MENU_ITEM, NSAccessibilityMenuItemRole ); + MAP( AccessibleRole::OPTION_PANE, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::PAGE_TAB, NSAccessibilityButtonRole ); + MAP( AccessibleRole::PAGE_TAB_LIST, NSAccessibilityTabGroupRole ); + MAP( AccessibleRole::PANEL, NSAccessibilityGroupRole ); + MAP( AccessibleRole::PARAGRAPH, NSAccessibilityTextAreaRole ); + MAP( AccessibleRole::PASSWORD_TEXT, NSAccessibilityTextFieldRole ); + MAP( AccessibleRole::POPUP_MENU, NSAccessibilityMenuRole ); + MAP( AccessibleRole::PUSH_BUTTON, NSAccessibilityButtonRole ); + MAP( AccessibleRole::PROGRESS_BAR, NSAccessibilityProgressIndicatorRole ); + MAP( AccessibleRole::RADIO_BUTTON, NSAccessibilityRadioButtonRole ); + MAP( AccessibleRole::RADIO_MENU_ITEM, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::ROW_HEADER, NSAccessibilityRowRole ); + MAP( AccessibleRole::ROOT_PANE, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::SCROLL_BAR, NSAccessibilityScrollBarRole ); + MAP( AccessibleRole::SCROLL_PANE, NSAccessibilityScrollAreaRole ); + MAP( AccessibleRole::SHAPE, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::SEPARATOR, NSAccessibilitySplitterRole ); // FIXME + MAP( AccessibleRole::SLIDER, NSAccessibilitySliderRole ); + MAP( AccessibleRole::SPIN_BOX, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::SPLIT_PANE, NSAccessibilitySplitterRole ); + MAP( AccessibleRole::STATUS_BAR, NSAccessibilityGroupRole ); // FIXME + MAP( AccessibleRole::TABLE, NSAccessibilityTableRole ); + MAP( AccessibleRole::TABLE_CELL, NSAccessibilityTextFieldRole ); + MAP( AccessibleRole::TEXT, NSAccessibilityTextAreaRole ); + MAP( AccessibleRole::TEXT_FRAME, NSAccessibilityGroupRole ); + MAP( AccessibleRole::TOGGLE_BUTTON, NSAccessibilityCheckBoxRole ); + MAP( AccessibleRole::TOOL_BAR, NSAccessibilityToolbarRole ); + MAP( AccessibleRole::TOOL_TIP, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::TREE, NSAccessibilityGroupRole ); + MAP( AccessibleRole::VIEW_PORT, NSAccessibilityUnknownRole ); // FIXME + MAP( AccessibleRole::WINDOW, NSAccessibilityWindowRole ); + + MAP( AccessibleRole::BUTTON_DROPDOWN, NSAccessibilityMenuButtonRole ); + MAP( AccessibleRole::BUTTON_MENU, NSAccessibilityMenuButtonRole ); + MAP( AccessibleRole::CAPTION, NSAccessibilityUnknownRole ); + MAP( AccessibleRole::CHART, NSAccessibilityUnknownRole ); + MAP( AccessibleRole::FORM, NSAccessibilityUnknownRole ); + MAP( AccessibleRole::IMAGE_MAP, NSAccessibilityUnknownRole ); + MAP( AccessibleRole::NOTE, NSAccessibilityUnknownRole ); + MAP( AccessibleRole::PAGE, NSAccessibilityUnknownRole ); + MAP( AccessibleRole::RULER, NSAccessibilityUnknownRole ); + MAP( AccessibleRole::SECTION, NSAccessibilityUnknownRole ); + MAP( AccessibleRole::TREE_ITEM, NSAccessibilityUnknownRole ); + MAP( AccessibleRole::TREE_TABLE, NSAccessibilityUnknownRole ); + +#undef MAP + default: + break; + } + return nativeRole; +} + ++(id)getNativeRoleFrom: (XAccessibleContext *) accessibleContext { + id nativeRole = [ AquaA11yRoleHelper simpleMapNativeRoleFrom: accessibleContext ]; + if ( accessibleContext -> getAccessibleRole() == AccessibleRole::LABEL ) { + if ( accessibleContext -> getAccessibleChildCount() > 0 ) { + [ nativeRole release ]; + nativeRole = NSAccessibilityOutlineRole; + } else if ( accessibleContext -> getAccessibleParent().is() ) { + Reference < XAccessibleContext > rxParentContext = accessibleContext -> getAccessibleParent() -> getAccessibleContext(); + if ( rxParentContext.is() ) { + NSString * roleParent = (NSString *) [ AquaA11yRoleHelper simpleMapNativeRoleFrom: rxParentContext.get() ]; + if ( [ roleParent isEqualToString: NSAccessibilityOutlineRole ] ) { + [ nativeRole release ]; + nativeRole = NSAccessibilityRowRole; + } + [ roleParent release ]; + } + } + } else if ( accessibleContext -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) { + Reference < XAccessible > rxAccessible = accessibleContext -> getAccessibleChild(0); + if ( rxAccessible.is() ) { + Reference < XAccessibleContext > rxAccessibleContext = rxAccessible -> getAccessibleContext(); + if ( rxAccessibleContext.is() && rxAccessibleContext -> getAccessibleRole() == AccessibleRole::TEXT ) { + if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::EDITABLE ) ) { + [ nativeRole release ]; + nativeRole = NSAccessibilityPopUpButtonRole; + } + } + } + } + return nativeRole; +} + ++(id)getNativeSubroleFrom: (sal_Int16) nRole { + id nativeSubrole = nil; + switch( nRole ) { +#define MAP(a,b) \ + case a: nativeSubrole = b; break + + MAP( AccessibleRole::UNKNOWN, NSAccessibilityUnknownSubrole ); + MAP( AccessibleRole::ALERT, NSAccessibilitySystemDialogSubrole ); + MAP( AccessibleRole::COLUMN_HEADER, @"" ); + MAP( AccessibleRole::CANVAS, @"" ); + MAP( AccessibleRole::CHECK_BOX, @"" ); + MAP( AccessibleRole::CHECK_MENU_ITEM, @"" ); + MAP( AccessibleRole::COLOR_CHOOSER, @"" ); + MAP( AccessibleRole::COMBO_BOX, @"" ); + MAP( AccessibleRole::DATE_EDITOR, @"" ); + MAP( AccessibleRole::DESKTOP_ICON, @"" ); + MAP( AccessibleRole::DESKTOP_PANE, @"" ); + MAP( AccessibleRole::DIRECTORY_PANE, @"" ); + MAP( AccessibleRole::DIALOG, NSAccessibilityDialogSubrole ); + MAP( AccessibleRole::DOCUMENT, @"" ); + MAP( AccessibleRole::EMBEDDED_OBJECT, @"" ); + MAP( AccessibleRole::END_NOTE, @"" ); + MAP( AccessibleRole::FILE_CHOOSER, @"" ); + MAP( AccessibleRole::FILLER, @"" ); + MAP( AccessibleRole::FONT_CHOOSER, @"" ); + MAP( AccessibleRole::FOOTER, @"" ); + MAP( AccessibleRole::FOOTNOTE, @"" ); + MAP( AccessibleRole::FRAME, @"" ); + MAP( AccessibleRole::GLASS_PANE, @"" ); + MAP( AccessibleRole::GRAPHIC, @"" ); + MAP( AccessibleRole::GROUP_BOX, @"" ); + MAP( AccessibleRole::HEADER, @"" ); + MAP( AccessibleRole::HEADING, @"" ); + MAP( AccessibleRole::HYPER_LINK, NSAccessibilityTextLinkSubrole ); + MAP( AccessibleRole::ICON, @"" ); + MAP( AccessibleRole::INTERNAL_FRAME, @"" ); + MAP( AccessibleRole::LABEL, @"" ); + MAP( AccessibleRole::LAYERED_PANE, @"" ); + MAP( AccessibleRole::LIST, @"" ); + MAP( AccessibleRole::LIST_ITEM, NSAccessibilityOutlineRowSubrole ); + MAP( AccessibleRole::MENU, @"" ); + MAP( AccessibleRole::MENU_BAR, @"" ); + MAP( AccessibleRole::MENU_ITEM, @"" ); + MAP( AccessibleRole::OPTION_PANE, @"" ); + MAP( AccessibleRole::PAGE_TAB, @"" ); + MAP( AccessibleRole::PAGE_TAB_LIST, @"" ); + MAP( AccessibleRole::PANEL, @"" ); + MAP( AccessibleRole::PARAGRAPH, @"" ); + MAP( AccessibleRole::PASSWORD_TEXT, NSAccessibilitySecureTextFieldSubrole ); + MAP( AccessibleRole::POPUP_MENU, @"" ); + MAP( AccessibleRole::PUSH_BUTTON, @"" ); + MAP( AccessibleRole::PROGRESS_BAR, @"" ); + MAP( AccessibleRole::RADIO_BUTTON, @"" ); + MAP( AccessibleRole::RADIO_MENU_ITEM, @"" ); + MAP( AccessibleRole::ROW_HEADER, @"" ); + MAP( AccessibleRole::ROOT_PANE, @"" ); + MAP( AccessibleRole::SCROLL_BAR, @"" ); + MAP( AccessibleRole::SCROLL_PANE, @"" ); + MAP( AccessibleRole::SHAPE, @"" ); + MAP( AccessibleRole::SEPARATOR, @"" ); + MAP( AccessibleRole::SLIDER, @"" ); + MAP( AccessibleRole::SPIN_BOX, @"" ); + MAP( AccessibleRole::SPLIT_PANE, @"" ); + MAP( AccessibleRole::STATUS_BAR, @"" ); + MAP( AccessibleRole::TABLE, @"" ); + MAP( AccessibleRole::TABLE_CELL, @"" ); + MAP( AccessibleRole::TEXT, @"" ); + MAP( AccessibleRole::TEXT_FRAME, @"" ); + MAP( AccessibleRole::TOGGLE_BUTTON, @"" ); + MAP( AccessibleRole::TOOL_BAR, @"" ); + MAP( AccessibleRole::TOOL_TIP, @"" ); + MAP( AccessibleRole::TREE, @"" ); + MAP( AccessibleRole::VIEW_PORT, @"" ); + MAP( AccessibleRole::WINDOW, NSAccessibilityStandardWindowSubrole ); + + MAP( AccessibleRole::BUTTON_DROPDOWN, @"" ); + MAP( AccessibleRole::BUTTON_MENU, @"" ); + MAP( AccessibleRole::CAPTION, @"" ); + MAP( AccessibleRole::CHART, @"" ); + MAP( AccessibleRole::FORM, @"" ); + MAP( AccessibleRole::IMAGE_MAP, @"" ); + MAP( AccessibleRole::NOTE, @"" ); + MAP( AccessibleRole::PAGE, @"" ); + MAP( AccessibleRole::RULER, @"" ); + MAP( AccessibleRole::SECTION, @"" ); + MAP( AccessibleRole::TREE_ITEM, @"" ); + MAP( AccessibleRole::TREE_TABLE, @"" ); + +#undef MAP + default: + break; + } + return nativeSubrole; +} + ++(id)getRoleDescriptionFrom: (NSString *) role with: (NSString *) subRole { + id roleDescription; + if ( [ subRole length ] == 0 ) + roleDescription = NSAccessibilityRoleDescription( role, nil ); + else + roleDescription = NSAccessibilityRoleDescription( role, subRole ); + return roleDescription; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11yselectionwrapper.h b/vcl/aqua/source/a11y/aqua11yselectionwrapper.h new file mode 100644 index 000000000000..a88e6c71c04b --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yselectionwrapper.h @@ -0,0 +1,43 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11SELECTIONWRAPPER_H +#define _SV_AQUA11SELECTIONWRAPPER_H + +#include "aquavcltypes.h" +#include "aqua11ywrapper.h" + +@interface AquaA11ySelectionWrapper : NSObject +{ +} ++(id)selectedChildrenAttributeForElement:(AquaA11yWrapper *)wrapper; ++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames; ++(MacOSBOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper; ++(void)setSelectedChildrenAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value; +@end + +#endif // _SV_AQUA11SELECTIONWRAPPER_H diff --git a/vcl/aqua/source/a11y/aqua11yselectionwrapper.mm b/vcl/aqua/source/a11y/aqua11yselectionwrapper.mm new file mode 100644 index 000000000000..53ab6dd36128 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yselectionwrapper.mm @@ -0,0 +1,91 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11yfactory.h" +#include "aqua11yselectionwrapper.h" + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + +@implementation AquaA11ySelectionWrapper : NSObject + ++(id)selectedChildrenAttributeForElement:(AquaA11yWrapper *)wrapper +{ + Reference< XAccessibleSelection > xAccessibleSelection = [ wrapper accessibleSelection ]; + NSMutableArray * children = [ [ NSMutableArray alloc ] init ]; + + try { + sal_Int32 n = xAccessibleSelection -> getSelectedAccessibleChildCount(); + for ( sal_Int32 i=0 ; i < n ; ++i ) { + [ children addObject: [ AquaA11yFactory wrapperForAccessible: xAccessibleSelection -> getSelectedAccessibleChild( i ) ] ]; + } + + return children; + + } catch ( Exception& e) { + return nil; + } + +} + + ++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames +{ + [ attributeNames addObject: NSAccessibilitySelectedChildrenAttribute ]; +} + ++(MacOSBOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper +{ + if ( [ attribute isEqualToString: NSAccessibilitySelectedChildrenAttribute ] ) + { + return YES; + } + else + { + return NO; + } +} + ++(void)setSelectedChildrenAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value +{ + Reference< XAccessibleSelection > xAccessibleSelection = [ wrapper accessibleSelection ]; + try { + xAccessibleSelection -> clearAccessibleSelection(); + + unsigned c = [ value count ]; + for ( unsigned i = 0 ; i < c ; ++i ) { + xAccessibleSelection -> selectAccessibleChild( [ [ value objectAtIndex: i ] accessibleContext ] -> getAccessibleIndexInParent() ); + } + } catch ( Exception& e) { + } +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ytablewrapper.h b/vcl/aqua/source/a11y/aqua11ytablewrapper.h new file mode 100644 index 000000000000..7bf3e44a2945 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ytablewrapper.h @@ -0,0 +1,44 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11TABLEWRAPPER_H +#define _SV_AQUA11TABLEWRAPPER_H + +#include "aqua11ywrapper.h" + +#define MAXIMUM_ACCESSIBLE_TABLE_CELLS 1000 + +@interface AquaA11yTableWrapper : AquaA11yWrapper +{ +} ++(id)childrenAttributeForElement:(AquaA11yTableWrapper *)wrapper; ++(void)addAttributeNamesTo: (NSMutableArray *)attributeNames object: (AquaA11yWrapper*)pObject; + +-(id)rowsAttribute; +-(id)columnsAttribute; +@end +#endif // _SV_AQUA11TABLEWRAPPER_H diff --git a/vcl/aqua/source/a11y/aqua11ytablewrapper.mm b/vcl/aqua/source/a11y/aqua11ytablewrapper.mm new file mode 100644 index 000000000000..98454ab8d57b --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ytablewrapper.mm @@ -0,0 +1,211 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "aqua11ytablewrapper.h" +#include "aqua11yfactory.h" + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::uno; + +@implementation AquaA11yTableWrapper : AquaA11yWrapper + ++(id)childrenAttributeForElement:(AquaA11yTableWrapper *)wrapper +{ + XAccessibleTable * accessibleTable = [ wrapper accessibleTable ]; + NSArray* pResult = nil; + if( accessibleTable ) + { + NSMutableArray * cells = [ [ NSMutableArray alloc ] init ]; + try + { + sal_Int32 nRows = accessibleTable->getAccessibleRowCount(); + sal_Int32 nCols = accessibleTable->getAccessibleColumnCount(); + + if( nRows * nCols < MAXIMUM_ACCESSIBLE_TABLE_CELLS ) + { + // make all children visible to the hierarchy + for ( sal_Int32 rowCount = 0; rowCount < nRows; rowCount++ ) + { + for ( sal_Int32 columnCount = 0; columnCount < nCols; columnCount++ ) + { + Reference < XAccessible > rAccessibleCell = accessibleTable -> getAccessibleCellAt ( rowCount, columnCount ); + if ( rAccessibleCell.is() ) + { + id cell_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rAccessibleCell -> getAccessibleContext() ]; + [ cells addObject: cell_wrapper ]; + [ cell_wrapper release ]; + } + } + } + } + else + { + XAccessibleComponent * accessibleComponent = [ wrapper accessibleComponent ]; + // find out which cells are actually visible by determining the top-left-cell and the bottom-right-cell + Size tableSize = accessibleComponent -> getSize(); + Point point; + point.X = 0; + point.Y = 0; + Reference < XAccessible > rAccessibleTopLeft = accessibleComponent -> getAccessibleAtPoint ( point ); + point.X = tableSize.Width - 1; + point.Y = tableSize.Height - 1; + Reference < XAccessible > rAccessibleBottomRight = accessibleComponent -> getAccessibleAtPoint ( point ); + if ( rAccessibleTopLeft.is() && rAccessibleBottomRight.is() ) + { + sal_Int32 idxTopLeft = rAccessibleTopLeft -> getAccessibleContext() -> getAccessibleIndexInParent(); + sal_Int32 idxBottomRight = rAccessibleBottomRight -> getAccessibleContext() -> getAccessibleIndexInParent(); + sal_Int32 rowTopLeft = accessibleTable -> getAccessibleRow ( idxTopLeft ); + sal_Int32 columnTopLeft = accessibleTable -> getAccessibleColumn ( idxTopLeft ); + sal_Int32 rowBottomRight = accessibleTable -> getAccessibleRow ( idxBottomRight ); + sal_Int32 columnBottomRight = accessibleTable -> getAccessibleColumn ( idxBottomRight ); + // create an array containing the visible cells + for ( sal_Int32 rowCount = rowTopLeft; rowCount <= rowBottomRight; rowCount++ ) + { + for ( sal_Int32 columnCount = columnTopLeft; columnCount <= columnBottomRight; columnCount++ ) + { + Reference < XAccessible > rAccessibleCell = accessibleTable -> getAccessibleCellAt ( rowCount, columnCount ); + if ( rAccessibleCell.is() ) + { + id cell_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rAccessibleCell -> getAccessibleContext() ]; + [ cells addObject: cell_wrapper ]; + [ cell_wrapper release ]; + } + } + } + } + } + pResult = NSAccessibilityUnignoredChildren( cells ); + } + catch (const Exception &e) + { + } + [cells autorelease]; + } + + return pResult; +} + ++(void)addAttributeNamesTo: (NSMutableArray *)attributeNames object: (AquaA11yWrapper*)pObject +{ + XAccessibleTable * accessibleTable = [ pObject accessibleTable ]; + if( accessibleTable ) + { + sal_Int32 nRows = accessibleTable->getAccessibleRowCount(); + sal_Int32 nCols = accessibleTable->getAccessibleColumnCount(); + + + if( nRows*nCols < MAXIMUM_ACCESSIBLE_TABLE_CELLS ) + { + [ attributeNames addObject: NSAccessibilityRowsAttribute ]; + [ attributeNames addObject: NSAccessibilityColumnsAttribute ]; + } + } +} + +-(id)rowsAttribute +{ + NSArray* pResult = nil; + + XAccessibleTable * accessibleTable = [ self accessibleTable ]; + if( accessibleTable ) + { + sal_Int32 nRows = accessibleTable->getAccessibleRowCount(); + sal_Int32 nCols = accessibleTable->getAccessibleColumnCount(); + if( nRows * nCols < MAXIMUM_ACCESSIBLE_TABLE_CELLS ) + { + NSMutableArray * cells = [ [ NSMutableArray alloc ] init ]; + try + { + // find out number of rows + sal_Int32 nRows = accessibleTable->getAccessibleRowCount(); + for( sal_Int32 n = 0; n < nRows; n++ ) + { + Reference < XAccessible > rAccessibleCell = accessibleTable -> getAccessibleCellAt ( n, 0 ); + if ( rAccessibleCell.is() ) + { + id cell_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rAccessibleCell -> getAccessibleContext() ]; + [ cells addObject: cell_wrapper ]; + [ cell_wrapper release ]; + } + } + pResult = NSAccessibilityUnignoredChildren( cells ); + } + catch (const Exception &e) + { + pResult = nil; + } + [ cells autorelease ]; + } + } + + return pResult; +} + +-(id)columnsAttribute +{ + NSArray* pResult = nil; + + XAccessibleTable * accessibleTable = [ self accessibleTable ]; + + if( accessibleTable ) + { + sal_Int32 nRows = accessibleTable->getAccessibleRowCount(); + sal_Int32 nCols = accessibleTable->getAccessibleColumnCount(); + if( nRows * nCols < MAXIMUM_ACCESSIBLE_TABLE_CELLS ) + { + NSMutableArray * cells = [ [ NSMutableArray alloc ] init ]; + try + { + // find out number of columns + for( sal_Int32 n = 0; n < nCols; n++ ) + { + Reference < XAccessible > rAccessibleCell = accessibleTable -> getAccessibleCellAt ( 0, n ); + if ( rAccessibleCell.is() ) + { + id cell_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rAccessibleCell -> getAccessibleContext() ]; + [ cells addObject: cell_wrapper ]; + [ cell_wrapper release ]; + } + } + pResult = NSAccessibilityUnignoredChildren( cells ); + } + catch (const Exception &e) + { + pResult = nil; + } + [ cells autorelease ]; + } + } + + return pResult; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ytextattributeswrapper.h b/vcl/aqua/source/a11y/aqua11ytextattributeswrapper.h new file mode 100644 index 000000000000..fcf185ca5478 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ytextattributeswrapper.h @@ -0,0 +1,39 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11TEXTATTRIBUTESWRAPPER_H +#define _SV_AQUA11TEXTATTRIBUTESWRAPPER_H + +#include <com/sun/star/uno/Sequence.hxx> +#include "aqua11ywrapper.h" + +@interface AquaA11yTextAttributesWrapper : NSObject +{ +} ++(NSMutableAttributedString *)createAttributedStringForElement:(AquaA11yWrapper *)wrapper inOrigRange:(id)origRange; +@end +#endif // _SV_AQUA11TEXTATTRIBUTESWRAPPER_H diff --git a/vcl/aqua/source/a11y/aqua11ytextattributeswrapper.mm b/vcl/aqua/source/a11y/aqua11ytextattributeswrapper.mm new file mode 100644 index 000000000000..6577cebf295e --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ytextattributeswrapper.mm @@ -0,0 +1,254 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "aqua11ytextattributeswrapper.h" +#include "salinst.h" +#include <com/sun/star/accessibility/AccessibleTextType.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/awt/FontStrikeout.hpp> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::rtl; + +@implementation AquaA11yTextAttributesWrapper : NSObject + ++(int)convertUnderlineStyle:(PropertyValue)property { + int underlineStyle = NSNoUnderlineStyle; + sal_Int16 value = 0; + property.Value >>= value; + if ( value != FontUnderline::NONE + && value != FontUnderline::DONTKNOW) { + underlineStyle = NSSingleUnderlineStyle; + } + return underlineStyle; +} + ++(int)convertBoldStyle:(PropertyValue)property { + int boldStyle = 0; + float value = 0; + property.Value >>= value; + if ( value == FontWeight::SEMIBOLD + || value == FontWeight::BOLD + || value == FontWeight::ULTRABOLD + || value == FontWeight::BLACK ) { + boldStyle = NSBoldFontMask; + } + return boldStyle; +} + ++(int)convertItalicStyle:(PropertyValue)property { + int italicStyle = 0; + sal_Int16 value = property.Value.get<FontSlant>(); + if ( value == FontSlant_ITALIC ) { + italicStyle = NSItalicFontMask; + } + return italicStyle; +} + ++(MacOSBOOL)isStrikethrough:(PropertyValue)property { + MacOSBOOL strikethrough = NO; + sal_Int16 value = 0; + property.Value >>= value; + if ( value != FontStrikeout::NONE + && value != FontStrikeout::DONTKNOW ) { + strikethrough = YES; + } + return strikethrough; +} + ++(MacOSBOOL)convertBoolean:(PropertyValue)property { + MacOSBOOL myBoolean = NO; + bool value = sal_False; + property.Value >>= value; + if ( value ) { + myBoolean = YES; + } + return myBoolean; +} + ++(NSNumber *)convertShort:(PropertyValue)property { + sal_Int16 value = 0; + property.Value >>= value; + return [ NSNumber numberWithShort: value ]; +} + ++(void)addColor:(sal_Int32)salColor forAttribute:(NSString *)attribute andRange:(NSRange)range toString:(NSMutableAttributedString *)string { + if ( salColor != -1 ) { + float elements[] = { salColor & 0x00ff0000, salColor & 0x0000ff00, salColor & 0x000000ff }; + CGColorRef color = CGColorCreate ( CGColorSpaceCreateWithName ( kCGColorSpaceGenericRGB ), elements ); + [ string addAttribute: attribute value: (id) color range: range ]; + CGColorRelease ( color ); + } +} + ++(void)addFont:(NSFont *)font toString:(NSMutableAttributedString *)string forRange:(NSRange)range { + if ( font != nil ) { + NSDictionary * fontDictionary = [ NSDictionary dictionaryWithObjectsAndKeys: + [ font fontName ], NSAccessibilityFontNameKey, + [ font familyName ], NSAccessibilityFontFamilyKey, + [ font displayName ], NSAccessibilityVisibleNameKey, + [ NSNumber numberWithFloat: [ font pointSize ] ], NSAccessibilityFontSizeKey, + nil + ]; + [ string addAttribute: NSAccessibilityFontTextAttribute + value: fontDictionary + range: range + ]; + } +} + ++(void)applyAttributesFrom:(Sequence < PropertyValue >)attributes toString:(NSMutableAttributedString *)string forRange:(NSRange)range storeDefaultsTo:(AquaA11yWrapper *)wrapperStore getDefaultsFrom:(AquaA11yWrapper *)wrapper { + NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ]; + // constants + static const OUString attrUnderline = OUString::createFromAscii("CharUnderline"); + static const OUString attrBold = OUString::createFromAscii("CharWeight"); + static const OUString attrFontname = OUString::createFromAscii("CharFontName"); + static const OUString attrItalic = OUString::createFromAscii("CharPosture"); + static const OUString attrHeight = OUString::createFromAscii("CharHeight"); + static const OUString attrStrikethrough = OUString::createFromAscii("CharStrikeout"); + static const OUString attrShadow = OUString::createFromAscii("CharShadowed"); + static const OUString attrUnderlineColor = OUString::createFromAscii("CharUnderlineColor"); + static const OUString attrUnderlineHasColor = OUString::createFromAscii("CharUnderlineHasColor"); + static const OUString attrForegroundColor = OUString::createFromAscii("CharColor"); + static const OUString attrBackgroundColor = OUString::createFromAscii("CharBackColor"); + static const OUString attrSuperscript = OUString::createFromAscii("CharEscapement"); + // vars + OUString fontname; + int fonttraits = 0; + float fontsize = 0.0; + sal_Int32 underlineColor = 0; + MacOSBOOL underlineHasColor = NO; + // add attributes to string + for ( int attrIndex = 0; attrIndex < attributes.getLength(); attrIndex++ ) { + PropertyValue property = attributes [ attrIndex ]; + // TODO: NSAccessibilityMisspelledTextAttribute, NSAccessibilityAttachmentTextAttribute, NSAccessibilityLinkTextAttribute + // NSAccessibilityStrikethroughColorTextAttribute is unsupported by UNP-API + if ( property.Value.hasValue() ) { + if ( property.Name.equals ( attrUnderline ) ) { + int style = [ AquaA11yTextAttributesWrapper convertUnderlineStyle: property ]; + if ( style != NSNoUnderlineStyle ) { + [ string addAttribute: NSAccessibilityUnderlineTextAttribute value: [ NSNumber numberWithInt: style ] range: range ]; + } + } else if ( property.Name.equals ( attrFontname ) ) { + property.Value >>= fontname; + } else if ( property.Name.equals ( attrBold ) ) { + fonttraits |= [ AquaA11yTextAttributesWrapper convertBoldStyle: property ]; + } else if ( property.Name.equals ( attrItalic ) ) { + fonttraits |= [ AquaA11yTextAttributesWrapper convertItalicStyle: property ]; + } else if ( property.Name.equals ( attrHeight ) ) { + property.Value >>= fontsize; + } else if ( property.Name.equals ( attrStrikethrough ) ) { + if ( [ AquaA11yTextAttributesWrapper isStrikethrough: property ] ) { + [ string addAttribute: NSAccessibilityStrikethroughTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ]; + } + } else if ( property.Name.equals ( attrShadow ) ) { + if ( [ AquaA11yTextAttributesWrapper convertBoolean: property ] ) { + [ string addAttribute: NSAccessibilityShadowTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ]; + } + } else if ( property.Name.equals ( attrUnderlineColor ) ) { + property.Value >>= underlineColor; + } else if ( property.Name.equals ( attrUnderlineHasColor ) ) { + underlineHasColor = [ AquaA11yTextAttributesWrapper convertBoolean: property ]; + } else if ( property.Name.equals ( attrForegroundColor ) ) { + [ AquaA11yTextAttributesWrapper addColor: property.Value.get<sal_Int32>() forAttribute: NSAccessibilityForegroundColorTextAttribute andRange: range toString: string ]; + } else if ( property.Name.equals ( attrBackgroundColor ) ) { + [ AquaA11yTextAttributesWrapper addColor: property.Value.get<sal_Int32>() forAttribute: NSAccessibilityBackgroundColorTextAttribute andRange: range toString: string ]; + } else if ( property.Name.equals ( attrSuperscript ) ) { + // values < zero mean subscript + // values > zero mean superscript + // this is true for both NSAccessibility-API and UNO-API + NSNumber * number = [ AquaA11yTextAttributesWrapper convertShort: property ]; + if ( [ number shortValue ] != 0 ) { + [ string addAttribute: NSAccessibilitySuperscriptTextAttribute value: number range: range ]; + } + } + } + } + // add underline information + if ( underlineHasColor ) { + [ AquaA11yTextAttributesWrapper addColor: underlineColor forAttribute: NSAccessibilityUnderlineColorTextAttribute andRange: range toString: string ]; + } + // add font information + if ( wrapperStore != nil ) { // default + [ wrapperStore setDefaultFontname: CreateNSString ( fontname ) ]; + [ wrapperStore setDefaultFontsize: fontsize ]; + NSFont * font = [ [ NSFontManager sharedFontManager ] fontWithFamily: CreateNSString ( fontname ) traits: fonttraits weight: 0 size: fontsize ]; + [ AquaA11yTextAttributesWrapper addFont: font toString: string forRange: range ]; + } else if ( wrapper != nil && fonttraits != 0 ) { // attribute run and bold and/or italic was found + NSFont * font = [ [ NSFontManager sharedFontManager ] fontWithFamily: [ wrapper defaultFontname ] traits: fonttraits weight: 0 size: [ wrapper defaultFontsize ] ]; + [ AquaA11yTextAttributesWrapper addFont: font toString: string forRange: range ]; + } + [ pool release ]; +} + ++(NSMutableAttributedString *)createAttributedStringForElement:(AquaA11yWrapper *)wrapper inOrigRange:(id)origRange { + static const Sequence < OUString > emptySequence; + // vars + NSMutableAttributedString * string = nil; + int loc = [ origRange rangeValue ].location; + int len = [ origRange rangeValue ].length; + int endIndex = loc + len; + int currentIndex = loc; + try { + NSString * myString = CreateNSString ( [ wrapper accessibleText ] -> getText() ); // TODO: dirty fix for i87817 + string = [ [ NSMutableAttributedString alloc ] initWithString: CreateNSString ( [ wrapper accessibleText ] -> getTextRange ( loc, loc + len ) ) ]; + if ( [ wrapper accessibleTextAttributes ] != nil && [myString characterAtIndex:0] != 57361) { // TODO: dirty fix for i87817 + [ string beginEditing ]; + // add default attributes for whole string + Sequence < PropertyValue > defaultAttributes = [ wrapper accessibleTextAttributes ] -> getDefaultAttributes ( emptySequence ); + [ AquaA11yTextAttributesWrapper applyAttributesFrom: defaultAttributes toString: string forRange: [ origRange rangeValue ] storeDefaultsTo: wrapper getDefaultsFrom: nil ]; + // add attributes for attribute run(s) + while ( currentIndex < endIndex ) { + TextSegment textSegment = [ wrapper accessibleText ] -> getTextAtIndex ( currentIndex, AccessibleTextType::ATTRIBUTE_RUN ); + int endOfRange = endIndex > textSegment.SegmentEnd ? textSegment.SegmentEnd : endIndex; + NSRange rangeForAttributeRun = NSMakeRange ( currentIndex, endOfRange - currentIndex ); + // add run attributes + Sequence < PropertyValue > attributes = [ wrapper accessibleTextAttributes ] -> getRunAttributes ( currentIndex, emptySequence ); + [ AquaA11yTextAttributesWrapper applyAttributesFrom: attributes toString: string forRange: rangeForAttributeRun storeDefaultsTo: nil getDefaultsFrom: wrapper ]; + currentIndex = textSegment.SegmentEnd; + } + [ string endEditing ]; + } + } catch ( IllegalArgumentException & e ) { + // empty + } catch ( IndexOutOfBoundsException & e ) { + // empty + } catch ( RuntimeException& ) { + // at least don't crash + } + return string; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ytextwrapper.h b/vcl/aqua/source/a11y/aqua11ytextwrapper.h new file mode 100644 index 000000000000..dfdab349bafe --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ytextwrapper.h @@ -0,0 +1,64 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11TEXTWRAPPER_H +#define _SV_AQUA11TEXTWRAPPER_H + +#include "aquavcltypes.h" +#include "aqua11ywrapper.h" + +@interface AquaA11yTextWrapper : NSObject +{ +} ++(id)valueAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)numberOfCharactersAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)selectedTextAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)selectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)visibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)sharedTextUIElementsAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)sharedCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)stringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range; ++(id)attributedStringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range; ++(id)rangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index; ++(id)rangeForPositionAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)point; ++(id)boundsForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range; ++(id)styleRangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index; ++(id)rTFForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range; ++(id)lineForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index; ++(id)rangeForLineAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)line; ++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames; ++(void)addParameterizedAttributeNamesTo:(NSMutableArray *)attributeNames; ++(NSArray *)specialAttributeNames; ++(NSArray *)specialParameterizedAttributeNames; ++(MacOSBOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper; ++(void)setVisibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value; ++(void)setSelectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value; ++(void)setSelectedTextAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value; ++(void)setValueAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value; +@end + +#endif // _SV_AQUA11TEXTWRAPPER_H diff --git a/vcl/aqua/source/a11y/aqua11ytextwrapper.mm b/vcl/aqua/source/a11y/aqua11ytextwrapper.mm new file mode 100644 index 000000000000..2033135564d8 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ytextwrapper.mm @@ -0,0 +1,289 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ytextwrapper.h" +#include "aqua11ytextattributeswrapper.h" +#include "aqua11yutil.h" +#include <com/sun/star/accessibility/AccessibleTextType.hpp> +#include <com/sun/star/awt/Rectangle.hpp> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::rtl; + +// Wrapper for XAccessibleText, XAccessibleEditableText and XAccessibleMultiLineText + +@implementation AquaA11yTextWrapper : NSObject + ++(id)valueAttributeForElement:(AquaA11yWrapper *)wrapper { + return CreateNSString ( [ wrapper accessibleText ] -> getText() ); +} + ++(void)setValueAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value { + // TODO +} + ++(id)numberOfCharactersAttributeForElement:(AquaA11yWrapper *)wrapper { + return [ NSNumber numberWithLong: [ wrapper accessibleText ] -> getCharacterCount() ]; +} + ++(id)selectedTextAttributeForElement:(AquaA11yWrapper *)wrapper { + return CreateNSString ( [ wrapper accessibleText ] -> getSelectedText() ); +} + ++(void)setSelectedTextAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value { + if ( [ wrapper accessibleEditableText ] != nil ) { + NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ]; + OUString newText = GetOUString ( (NSString *) value ); + NSRange selectedTextRange = [ [ AquaA11yTextWrapper selectedTextRangeAttributeForElement: wrapper ] rangeValue ]; + try { + [ wrapper accessibleEditableText ] -> replaceText ( selectedTextRange.location, selectedTextRange.location + selectedTextRange.length, newText ); + } catch ( const Exception & e ) { + // empty + } + [ pool release ]; + } +} + ++(id)selectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper { + sal_Int32 start = [ wrapper accessibleText ] -> getSelectionStart(); + sal_Int32 end = [ wrapper accessibleText ] -> getSelectionEnd(); + if ( start != end ) { + return [ NSValue valueWithRange: NSMakeRange ( start, end - start ) ]; // true selection + } else { + long caretPos = [ wrapper accessibleText ] -> getCaretPosition(); + if ( caretPos < 0 || caretPos > [ wrapper accessibleText ] -> getCharacterCount() ) { + return nil; + } + return [ NSValue valueWithRange: NSMakeRange ( caretPos, 0 ) ]; // insertion point + } +} + ++(void)setSelectedTextRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value { + NSRange range = [ value rangeValue ]; + try { + [ wrapper accessibleText ] -> setSelection ( range.location, range.location + range.length ); + } catch ( const Exception & e ) { + // empty + } +} + ++(id)visibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper { + // the OOo a11y API returns only the visible portion... + return [ NSValue valueWithRange: NSMakeRange ( 0, [ wrapper accessibleText ] -> getCharacterCount() ) ]; +} + ++(void)setVisibleCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value { + // do nothing +} + ++(id)sharedTextUIElementsAttributeForElement:(AquaA11yWrapper *)wrapper { + return [ [ NSArray alloc ] init ]; // unsupported +} + ++(id)sharedCharacterRangeAttributeForElement:(AquaA11yWrapper *)wrapper { + return [ NSValue valueWithRange: NSMakeRange ( 0, 0 ) ]; // unsupported +} + ++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames { + [ attributeNames addObjectsFromArray: [ AquaA11yTextWrapper specialAttributeNames ] ]; +} + ++(NSArray *)specialAttributeNames { + return [ NSArray arrayWithObjects: + NSAccessibilityValueAttribute, + NSAccessibilityNumberOfCharactersAttribute, + NSAccessibilitySelectedTextAttribute, + NSAccessibilitySelectedTextRangeAttribute, + NSAccessibilityVisibleCharacterRangeAttribute, + NSAccessibilitySharedTextUIElementsAttribute, + NSAccessibilitySharedCharacterRangeAttribute, + nil ]; +} + ++(void)addParameterizedAttributeNamesTo:(NSMutableArray *)attributeNames { + [ attributeNames addObjectsFromArray: [ AquaA11yTextWrapper specialParameterizedAttributeNames ] ]; +} + ++(NSArray *)specialParameterizedAttributeNames { + return [ NSArray arrayWithObjects: + NSAccessibilityStringForRangeParameterizedAttribute, + NSAccessibilityAttributedStringForRangeParameterizedAttribute, + NSAccessibilityRangeForIndexParameterizedAttribute, + NSAccessibilityRangeForPositionParameterizedAttribute, + NSAccessibilityBoundsForRangeParameterizedAttribute, + NSAccessibilityStyleRangeForIndexParameterizedAttribute, + NSAccessibilityRTFForRangeParameterizedAttribute, + NSAccessibilityLineForIndexParameterizedAttribute, + NSAccessibilityRangeForLineParameterizedAttribute, + nil ]; +} + ++(id)lineForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index { + NSNumber * lineNumber = nil; + try { + sal_Int32 line = [ wrapper accessibleMultiLineText ] -> getLineNumberAtIndex ( (sal_Int32) [ index intValue ] ); + lineNumber = [ NSNumber numberWithInt: line ]; + } catch ( IndexOutOfBoundsException & e ) { + // empty + } + return lineNumber; +} + ++(id)rangeForLineAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)line { + NSValue * range = nil; + try { + TextSegment textSegment = [ wrapper accessibleMultiLineText ] -> getTextAtLineNumber ( [ line intValue ] ); + range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ]; + } catch ( IndexOutOfBoundsException & e ) { + // empty + } + return range; +} + ++(id)stringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range { + int loc = [ range rangeValue ].location; + int len = [ range rangeValue ].length; + NSMutableString * textRange = [ [ NSMutableString alloc ] init ]; + try { + [ textRange appendString: CreateNSString ( [ wrapper accessibleText ] -> getTextRange ( loc, loc + len ) ) ]; + } catch ( IndexOutOfBoundsException & e ) { + // empty + } + return textRange; +} + ++(id)attributedStringForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range { + return [ AquaA11yTextAttributesWrapper createAttributedStringForElement: wrapper inOrigRange: range ]; +} + ++(id)rangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index { + NSValue * range = nil; + try { + TextSegment textSegment = [ wrapper accessibleText ] -> getTextBeforeIndex ( [ index intValue ], AccessibleTextType::GLYPH ); + range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ]; + } catch ( IndexOutOfBoundsException & e ) { + // empty + } catch ( IllegalArgumentException & e ) { + // empty + } + return range; +} + ++(id)rangeForPositionAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)point { + NSValue * value = nil; + sal_Int32 index = [ wrapper accessibleText ] -> getIndexAtPoint ( [ AquaA11yUtil nsPointToVclPoint: point ] ); + if ( index > -1 ) { + value = [ AquaA11yTextWrapper rangeForIndexAttributeForElement: wrapper forParameter: [ NSNumber numberWithLong: index ] ]; + } + return value; +} + ++(id)boundsForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range { + NSValue * rect = nil; + try { + // TODO: this is ugly!!! + // the UNP-API can only return the bounds for a single character, not for a range + int loc = [ range rangeValue ].location; + int len = [ range rangeValue ].length; + int minx = 0x7fffffff, miny = 0x7fffffff, maxx = 0, maxy = 0; + for ( int i = 0; i < len; i++ ) { + Rectangle vclRect = [ wrapper accessibleText ] -> getCharacterBounds ( loc + i ); + if ( vclRect.X < minx ) { + minx = vclRect.X; + } + if ( vclRect.Y < miny ) { + miny = vclRect.Y; + } + if ( vclRect.Width + vclRect.X > maxx ) { + maxx = vclRect.Width + vclRect.X; + } + if ( vclRect.Height + vclRect.Y > maxy ) { + maxy = vclRect.Height + vclRect.Y; + } + } + if ( [ wrapper accessibleComponent ] != nil ) { + // get location on screen (must be added since get CharacterBounds returns values relative to parent) + Point screenPos = [ wrapper accessibleComponent ] -> getLocationOnScreen(); + Point pos ( minx + screenPos.X, miny + screenPos.Y ); + Point size ( maxx - minx, maxy - miny ); + NSValue * nsPos = [ AquaA11yUtil vclPointToNSPoint: pos ]; + rect = [ NSValue valueWithRect: NSMakeRect ( [ nsPos pointValue ].x, [ nsPos pointValue ].y - size.Y, size.X, size.Y ) ]; + //printf("Range: %s --- Rect: %s\n", [ NSStringFromRange ( [ range rangeValue ] ) UTF8String ], [ NSStringFromRect ( [ rect rectValue ] ) UTF8String ]); + } + } catch ( IndexOutOfBoundsException & e ) { + // empty + } + return rect; +} + ++(id)styleRangeForIndexAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)index { + NSValue * range = nil; + try { + TextSegment textSegment = [ wrapper accessibleText ] -> getTextAtIndex ( [ index intValue ], AccessibleTextType::ATTRIBUTE_RUN ); + range = [ NSValue valueWithRange: NSMakeRange ( textSegment.SegmentStart, textSegment.SegmentEnd - textSegment.SegmentStart ) ]; + } catch ( IndexOutOfBoundsException & e ) { + // empty + } catch ( IllegalArgumentException & e ) { + // empty + } + return range; +} + ++(id)rTFForRangeAttributeForElement:(AquaA11yWrapper *)wrapper forParameter:(id)range { + NSData * rtfData = nil; + NSAttributedString * attrString = (NSAttributedString *) [ AquaA11yTextWrapper attributedStringForRangeAttributeForElement: wrapper forParameter: range ]; + if ( attrString != nil ) { + @try { + rtfData = [ attrString RTFFromRange: [ range rangeValue ] documentAttributes: nil ]; + } @catch ( NSException * e) { + // emtpy + } + } + return rtfData; +} + ++(MacOSBOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper { + MacOSBOOL isSettable = NO; + if ( [ attribute isEqualToString: NSAccessibilityValueAttribute ] + || [ attribute isEqualToString: NSAccessibilitySelectedTextAttribute ] + || [ attribute isEqualToString: NSAccessibilitySelectedTextRangeAttribute ] + || [ attribute isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute ] ) { + if ( ! [ [ wrapper accessibilityAttributeValue: NSAccessibilityRoleAttribute ] isEqualToString: NSAccessibilityStaticTextRole ] ) { + isSettable = YES; + } + } + return isSettable; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11yutil.h b/vcl/aqua/source/a11y/aqua11yutil.h new file mode 100644 index 000000000000..adf565f4d9bb --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yutil.h @@ -0,0 +1,39 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11YUTIL_H +#define _SV_AQUA11YUTIL_H + +#include <com/sun/star/awt/Point.hpp> + +@interface AquaA11yUtil : NSObject { +} ++(NSValue *)vclPointToNSPoint:(::com::sun::star::awt::Point)vclPoint; ++(::com::sun::star::awt::Point)nsPointToVclPoint:(NSValue *)nsPoint; +@end + +#endif // _SV_AQUA11YUTIL_H
\ No newline at end of file diff --git a/vcl/aqua/source/a11y/aqua11yutil.mm b/vcl/aqua/source/a11y/aqua11yutil.mm new file mode 100644 index 000000000000..4749a3b40822 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yutil.mm @@ -0,0 +1,53 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "aquavcltypes.h" +#include "aqua11yutil.h" + +using namespace ::com::sun::star::awt; + +@implementation AquaA11yUtil : NSObject + +// TODO: should be merged with AquaSalFrame::VCLToCocoa... to a general helper method ++(NSValue *)vclPointToNSPoint:(Point)vclPoint { + // VCL coordinates are in upper-left-notation, Cocoa likes it the Cartesian way (lower-left) + NSRect screenRect = [ [ NSScreen mainScreen ] frame ]; + NSPoint nsPoint = NSMakePoint ( (float) vclPoint.X, (float) ( screenRect.size.height - vclPoint.Y ) ); + return [ NSValue valueWithPoint: nsPoint ]; +} + +// TODO: should be merged with AquaSalFrame::VCLToCocoa... to a general helper method ++(Point)nsPointToVclPoint:(NSValue *)nsPoint { + // VCL coordinates are in upper-left-notation, Cocoa likes it the Cartesian way (lower-left) + NSRect screenRect = [ [ NSScreen mainScreen ] frame ]; + return Point ( static_cast<long>([ nsPoint pointValue ].x), static_cast<long>(screenRect.size.height - [ nsPoint pointValue ].y) ); +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11yvaluewrapper.h b/vcl/aqua/source/a11y/aqua11yvaluewrapper.h new file mode 100644 index 000000000000..d3afebf7f828 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yvaluewrapper.h @@ -0,0 +1,46 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11VALUEWRAPPER_H +#define _SV_AQUA11VALUEWRAPPER_H + +#include "salinst.h" +#include "aquavcltypes.h" +#include "aqua11ywrapper.h" + +@interface AquaA11yValueWrapper : NSObject +{ +} ++(id)valueAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)minValueAttributeForElement:(AquaA11yWrapper *)wrapper; ++(id)maxValueAttributeForElement:(AquaA11yWrapper *)wrapper; ++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames; ++(MacOSBOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper; ++(void)setValueAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value; +@end + +#endif // _SV_AQUA11VALUEWRAPPER_H diff --git a/vcl/aqua/source/a11y/aqua11yvaluewrapper.mm b/vcl/aqua/source/a11y/aqua11yvaluewrapper.mm new file mode 100644 index 000000000000..85ef0041da95 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11yvaluewrapper.mm @@ -0,0 +1,95 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "aqua11yvaluewrapper.h" +#include "aqua11ywrapperstatictext.h" + +using namespace ::com::sun::star::uno; + +// Wrapper for XAccessibleValue +// Remember: A UNO-Value is a single numeric value. Regarding the Mac A11y-API, a value can be anything! + +@implementation AquaA11yValueWrapper : NSObject + ++(id)valueAttributeForElement:(AquaA11yWrapper *)wrapper { + // TODO: Detect Type from Any + if ( [ wrapper accessibleValue ] != nil ) { + long value = 0; + [ wrapper accessibleValue ] -> getCurrentValue() >>= value; + return [ NSNumber numberWithLong: value ]; + } + return [ NSNumber numberWithLong: 0 ]; +} + ++(id)minValueAttributeForElement:(AquaA11yWrapper *)wrapper { + // TODO: Detect Type from Any + if ( [ wrapper accessibleValue ] != nil ) { + long value = 0; + [ wrapper accessibleValue ] -> getMinimumValue() >>= value; + return [ NSNumber numberWithLong: value ]; + } + return [ NSNumber numberWithLong: 0 ]; +} + ++(id)maxValueAttributeForElement:(AquaA11yWrapper *)wrapper { + // TODO: Detect Type from Any + if ( [ wrapper accessibleValue ] != nil ) { + long value = 0; + [ wrapper accessibleValue ] -> getMaximumValue() >>= value; + return [ NSNumber numberWithLong: value ]; + } + return [ NSNumber numberWithLong: 0 ]; +} + ++(void)setValueAttributeForElement:(AquaA11yWrapper *)wrapper to:(id)value { + // TODO: Detect Type from NSNumber + if ( [ value isKindOfClass: [ NSNumber class ] ] + && [ wrapper accessibleValue ] != nil ) { + NSNumber * number = (NSNumber *) value; + Any numberAny ( [ number longValue ] ); + [ wrapper accessibleValue ] -> setCurrentValue ( numberAny ); + } +} + ++(void)addAttributeNamesTo:(NSMutableArray *)attributeNames { + [ attributeNames addObject: NSAccessibilityValueAttribute ]; +} + ++(MacOSBOOL)isAttributeSettable:(NSString *)attribute forElement:(AquaA11yWrapper *)wrapper { + MacOSBOOL isSettable = NO; + if ( [ wrapper accessibleValue ] != nil + && [ attribute isEqualToString: NSAccessibilityValueAttribute ] + && ! [ wrapper isKindOfClass: [ AquaA11yWrapperStaticText class ] ] ) { + isSettable = YES; + } + return isSettable; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrapper.mm b/vcl/aqua/source/a11y/aqua11ywrapper.mm new file mode 100644 index 000000000000..e86676e725f2 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapper.mm @@ -0,0 +1,1142 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "saldata.hxx" + +#include "aqua11ywrapper.h" +#include "aqua11yactionwrapper.h" +#include "aqua11ycomponentwrapper.h" +#include "aqua11ylistener.hxx" +#include "aqua11yselectionwrapper.h" +#include "aqua11ytablewrapper.h" +#include "aqua11ytextwrapper.h" +#include "aqua11yvaluewrapper.h" +#include "aqua11yfactory.h" +#include "aqua11yfocuslistener.hxx" +#include "aqua11yfocustracker.hxx" +#include "aqua11yrolehelper.h" + +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; + +@interface SalFrameWindow : NSWindow +{ +} +-(Reference<XAccessibleContext>)accessibleContext; +@end + +static MacOSBOOL isPopupMenuOpen = NO; + +@implementation AquaA11yWrapper : NSView + +#pragma mark - +#pragma mark Init and dealloc + +-(id)initWithAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext { + self = [ super init ]; + if ( self != nil ) { + [ self setDefaults: rxAccessibleContext ]; + } + return self; +} + +-(void) setDefaults: (Reference < XAccessibleContext >) rxAccessibleContext { + mDefaultFontsize = 0.0; + mpDefaultFontname = nil; + mpReferenceWrapper = new ReferenceWrapper; + mActsAsRadioGroup = NO; + mpReferenceWrapper -> rAccessibleContext = rxAccessibleContext; + mIsTableCell = NO; + // Querying all supported interfaces + try { + // XAccessibleComponent + mpReferenceWrapper -> rAccessibleComponent = Reference < XAccessibleComponent > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleExtendedComponent + mpReferenceWrapper -> rAccessibleExtendedComponent = Reference < XAccessibleExtendedComponent > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleSelection + mpReferenceWrapper -> rAccessibleSelection = Reference< XAccessibleSelection > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleTable + mpReferenceWrapper -> rAccessibleTable = Reference < XAccessibleTable > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleText + mpReferenceWrapper -> rAccessibleText = Reference < XAccessibleText > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleEditableText + mpReferenceWrapper -> rAccessibleEditableText = Reference < XAccessibleEditableText > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleValue + mpReferenceWrapper -> rAccessibleValue = Reference < XAccessibleValue > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleAction + mpReferenceWrapper -> rAccessibleAction = Reference < XAccessibleAction > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleTextAttributes + mpReferenceWrapper -> rAccessibleTextAttributes = Reference < XAccessibleTextAttributes > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleMultiLineText + mpReferenceWrapper -> rAccessibleMultiLineText = Reference < XAccessibleMultiLineText > ( rxAccessibleContext, UNO_QUERY ); + // XAccessibleEventBroadcaster + #if 0 + /* #i102033# NSAccessibility does not seemt to know an equivalent for transient children. + That means we need to cache this, else e.g. tree list boxes are not accessible (moreover + it crashes by notifying dead objects - which would seemt o be another bug) + + FIXME: + Unfortunately this can increase memory consumption drastically until the non transient parent + is destroyed an finally all the transients are released. + */ + if ( ! rxAccessibleContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::TRANSIENT ) ) + #endif + { + Reference< XAccessibleEventBroadcaster > xBroadcaster(rxAccessibleContext, UNO_QUERY); + if( xBroadcaster.is() ) { + /* + * We intentionally do not hold a reference to the event listener in the wrapper object, + * but let the listener control the life cycle of the wrapper instead .. + */ + xBroadcaster->addEventListener( new AquaA11yEventListener( self, rxAccessibleContext -> getAccessibleRole() ) ); + } + } + // TABLE_CELL + if ( rxAccessibleContext -> getAccessibleRole() == AccessibleRole::TABLE_CELL ) { + mIsTableCell = YES; + } + } catch ( const Exception ) { + } +} + +-(void)dealloc { + if ( mpReferenceWrapper != nil ) { + delete mpReferenceWrapper; + } + if ( mpDefaultFontname != nil ) { + [ mpDefaultFontname release ]; + } + [ super dealloc ]; +} + +#pragma mark - +#pragma mark Utility Section + +// generates selectors for attribute name AXAttributeNameHere +// (getter without parameter) attributeNameHereAttribute +// (getter with parameter) attributeNameHereAttributeForParameter: +// (setter) setAttributeNameHereAttributeForElement:to: +-(SEL)selectorForAttribute:(NSString *)attribute asGetter:(MacOSBOOL)asGetter withGetterParameter:(MacOSBOOL)withGetterParameter { + SEL selector = nil; + NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ]; + @try { + // step 1: create method name from attribute name + NSMutableString * methodName = [ NSMutableString string ]; + if ( ! asGetter ) { + [ methodName appendString: @"set" ]; + } + NSString * firstChar = [ attribute substringWithRange: NSMakeRange ( 2, 1 ) ]; // drop leading "AX" and get first char + if ( asGetter ) { + [ methodName appendString: [ firstChar lowercaseString ] ]; + } else { + [ methodName appendString: firstChar ]; + } + [ methodName appendString: [ attribute substringFromIndex: 3 ] ]; // append rest of attribute name + // append rest of method name + [ methodName appendString: @"Attribute" ]; + if ( ! asGetter ) { + [ methodName appendString: @"ForElement:to:" ]; + } else if ( asGetter && withGetterParameter ) { + [ methodName appendString: @"ForParameter:" ]; + } + // step 2: create selector + selector = NSSelectorFromString ( methodName ); + } @catch ( id exception ) { + selector = nil; + } + [ pool release ]; + return selector; +} + +-(Reference < XAccessible >)getFirstRadioButtonInGroup { + Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet(); + if( rxAccessibleRelationSet.is() ) + { + AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF ); + if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() ) + return Reference < XAccessible > ( relationMemberOf.TargetSet[0], UNO_QUERY ); + } + return Reference < XAccessible > (); +} + +-(MacOSBOOL)isFirstRadioButtonInGroup { + Reference < XAccessible > rFirstMateAccessible = [ self getFirstRadioButtonInGroup ]; + if ( rFirstMateAccessible.is() && rFirstMateAccessible -> getAccessibleContext().get() == [ self accessibleContext ] ) { + return YES; + } + return NO; +} + +#pragma mark - +#pragma mark Attribute Value Getters +// ( called via Reflection by accessibilityAttributeValue ) + +/* + Radiobutton grouping is done differently in NSAccessibility and the UNO-API. In UNO related radio buttons share an entry in their + RelationSet. In NSAccessibility the relationship is axpressed through the hierarchy. A AXRadioGroup contains two or more AXRadioButton + objects. Since this group is not available in the UNO hierarchy, an extra wrapper is used for it. This wrapper shares almost all + attributes with the first radio button of the group, except for the role, subrole, role description, parent and children attributes. + So in this five methods there is a special treatment for radio buttons and groups. +*/ + +-(id)roleAttribute { + if ( mActsAsRadioGroup ) { + return NSAccessibilityRadioGroupRole; + } + else { + return [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ]; + } +} + +-(id)subroleAttribute { + if ( mActsAsRadioGroup ) { + return @""; + } else { + NSString * subRole = [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ]; + if ( ! [ subRole isEqualToString: @"" ] ) { + return subRole; + } else { + [ subRole release ]; + return [ super accessibilityAttributeValue: NSAccessibilitySubroleAttribute ]; + } + } +} + +-(id)titleAttribute { + return CreateNSString ( [ self accessibleContext ] -> getAccessibleName() ); +} + +-(id)descriptionAttribute { + if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) { + return [ self titleAttribute ]; + } else if ( [ self accessibleExtendedComponent ] != nil ) { + return [ AquaA11yComponentWrapper descriptionAttributeForElement: self ]; + } else { + return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() ); + } +} + +-(id)enabledAttribute { + if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) { + return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::ENABLED ) ]; + } else { + return nil; + } +} + +-(id)focusedAttribute { + if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::COMBO_BOX ) { + id isFocused = nil; + Reference < XAccessible > rxParent = [ self accessibleContext ] -> getAccessibleParent(); + if ( rxParent.is() ) { + Reference < XAccessibleContext > rxContext = rxParent -> getAccessibleContext(); + if ( rxContext.is() && rxContext -> getAccessibleStateSet().is() ) { + isFocused = [ NSNumber numberWithBool: rxContext -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ]; + } + } + return isFocused; + } else if ( [ self accessibleContext ] -> getAccessibleStateSet().is() ) { + return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::FOCUSED ) ]; + } else { + return nil; + } +} + +-(id)parentAttribute { + if ( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON && ! mActsAsRadioGroup ) { + Reference < XAccessible > rxAccessible = [ self getFirstRadioButtonInGroup ]; + if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) { + Reference < XAccessibleContext > rxAccessibleContext = rxAccessible -> getAccessibleContext(); + id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessibleContext createIfNotExists: YES asRadioGroup: YES ]; + [ parent_wrapper autorelease ]; + return NSAccessibilityUnignoredAncestor( parent_wrapper ); + } + return nil; + } + try { + Reference< XAccessible > xParent( [ self accessibleContext ] -> getAccessibleParent() ); + if ( xParent.is() ) { + Reference< XAccessibleContext > xContext( xParent -> getAccessibleContext() ); + if ( xContext.is() ) { + id parent_wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xContext ]; + [ parent_wrapper autorelease ]; + return NSAccessibilityUnignoredAncestor( parent_wrapper ); + } + } + } catch (const Exception&) { + } + + OSL_ASSERT( 0 ); + return nil; +} + +-(id)childrenAttribute { + if ( mActsAsRadioGroup ) { + NSMutableArray * children = [ [ NSMutableArray alloc ] init ]; + Reference < XAccessibleRelationSet > rxAccessibleRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet(); + AccessibleRelation relationMemberOf = rxAccessibleRelationSet -> getRelationByType ( AccessibleRelationType::MEMBER_OF ); + if ( relationMemberOf.RelationType == AccessibleRelationType::MEMBER_OF && relationMemberOf.TargetSet.hasElements() ) { + for ( int index = 0; index < relationMemberOf.TargetSet.getLength(); index++ ) { + Reference < XAccessible > rMateAccessible = Reference < XAccessible > ( relationMemberOf.TargetSet[index], UNO_QUERY ); + if ( rMateAccessible.is() ) { + Reference< XAccessibleContext > rMateAccessibleContext( rMateAccessible -> getAccessibleContext() ); + if ( rMateAccessibleContext.is() ) { + id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: rMateAccessibleContext ]; + [ children addObject: wrapper ]; + [ wrapper release ]; + } + } + } + } + return children; + } else if ( [ self accessibleTable ] != nil ) + { + AquaA11yTableWrapper* pTable = [self isKindOfClass: [AquaA11yTableWrapper class]] ? (AquaA11yTableWrapper*)self : nil; + return [ AquaA11yTableWrapper childrenAttributeForElement: pTable ]; + } else { + try { + NSMutableArray * children = [ [ NSMutableArray alloc ] init ]; + Reference< XAccessibleContext > xContext( [ self accessibleContext ] ); + + sal_Int32 cnt = xContext -> getAccessibleChildCount(); + for ( sal_Int32 i = 0; i < cnt; i++ ) { + Reference< XAccessible > xChild( xContext -> getAccessibleChild( i ) ); + if( xChild.is() ) { + Reference< XAccessibleContext > xChildContext( xChild -> getAccessibleContext() ); + // the menubar is already accessible (including Apple- and Application-Menu) through NSApplication => omit it here + if ( xChildContext.is() && AccessibleRole::MENU_BAR != xChildContext -> getAccessibleRole() ) { + id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: xChildContext ]; + [ children addObject: wrapper ]; + [ wrapper release ]; + } + } + } + + // if not already acting as RadioGroup now is the time to replace RadioButtons with RadioGroups and remove RadioButtons + if ( ! mActsAsRadioGroup ) { + NSEnumerator * enumerator = [ children objectEnumerator ]; + AquaA11yWrapper * element; + while ( ( element = ( (AquaA11yWrapper *) [ enumerator nextObject ] ) ) ) { + if ( [ element accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) { + if ( [ element isFirstRadioButtonInGroup ] ) { + id wrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ element accessibleContext ] createIfNotExists: YES asRadioGroup: YES ]; + [ children replaceObjectAtIndex: [ children indexOfObjectIdenticalTo: element ] withObject: wrapper ]; + } + [ children removeObject: element ]; + } + } + } + + [ children autorelease ]; + return NSAccessibilityUnignoredChildren( children ); + } catch (const Exception &e) { + // TODO: Log + return nil; + } + } +} + +-(id)windowAttribute { + // go upstairs until reaching the broken connection + AquaA11yWrapper * aWrapper = self; + while ( [ aWrapper accessibleContext ] -> getAccessibleParent().is() ) { + aWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ aWrapper accessibleContext ] -> getAccessibleParent() -> getAccessibleContext() ]; + [ aWrapper autorelease ]; + } + // get associated NSWindow + NSView * theView = [ aWrapper viewElementForParent ]; + return theView; +} + +-(id)topLevelUIElementAttribute { + return [ self windowAttribute ]; +} + +-(id)sizeAttribute { + if ( [ self accessibleComponent ] != nil ) { + return [ AquaA11yComponentWrapper sizeAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)positionAttribute { + if ( [ self accessibleComponent ] != nil ) { + return [ AquaA11yComponentWrapper positionAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)helpAttribute { + return CreateNSString ( [ self accessibleContext ] -> getAccessibleDescription() ); +} + +-(id)roleDescriptionAttribute { + if ( mActsAsRadioGroup ) { + return [ AquaA11yRoleHelper getRoleDescriptionFrom: NSAccessibilityRadioGroupRole with: @"" ]; + } else if( [ self accessibleContext ] -> getAccessibleRole() == AccessibleRole::RADIO_BUTTON ) { + // FIXME: VO should read this because of hierarchy, this is just a workaround + // get parent and its children + AquaA11yWrapper * parent = [ self parentAttribute ]; + NSArray * children = [ parent childrenAttribute ]; + // find index of self + int index = 1; + NSEnumerator * enumerator = [ children objectEnumerator ]; + AquaA11yWrapper * child = nil; + while ( ( child = [ enumerator nextObject ] ) ) { + if ( self == child ) { + break; + } + index++; + } + // build string + NSNumber * nIndex = [ NSNumber numberWithInt: index ]; + NSNumber * nGroupsize = [ NSNumber numberWithInt: [ children count ] ]; + NSMutableString * value = [ [ NSMutableString alloc ] init ]; + [ value appendString: @"radio button " ]; + [ value appendString: [ nIndex stringValue ] ]; + [ value appendString: @" of " ]; + [ value appendString: [ nGroupsize stringValue ] ]; + // clean up and return string + [ nIndex release ]; + [ nGroupsize release ]; + [ children release ]; + return value; + } else { + return [ AquaA11yRoleHelper getRoleDescriptionFrom: + [ AquaA11yRoleHelper getNativeRoleFrom: [ self accessibleContext ] ] + with: [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ] ]; + } +} + +-(id)valueAttribute { + if ( [ [ self roleAttribute ] isEqualToString: NSAccessibilityMenuItemRole ] ) { + return nil; + } else if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper valueAttributeForElement: self ]; + } else if ( [ self accessibleValue ] != nil ) { + return [ AquaA11yValueWrapper valueAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)minValueAttribute { + if ( [ self accessibleValue ] != nil ) { + return [ AquaA11yValueWrapper minValueAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)maxValueAttribute { + if ( [ self accessibleValue ] != nil ) { + return [ AquaA11yValueWrapper maxValueAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)contentsAttribute { + return [ self childrenAttribute ]; +} + +-(id)selectedChildrenAttribute { + return [ AquaA11ySelectionWrapper selectedChildrenAttributeForElement: self ]; +} + +-(id)numberOfCharactersAttribute { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper numberOfCharactersAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)selectedTextAttribute { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper selectedTextAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)selectedTextRangeAttribute { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper selectedTextRangeAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)visibleCharacterRangeAttribute { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper visibleCharacterRangeAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)tabsAttribute { + return self; // TODO ??? +} + +-(id)sharedTextUIElementsAttribute { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper sharedTextUIElementsAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)sharedCharacterRangeAttribute { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper sharedCharacterRangeAttributeForElement: self ]; + } else { + return nil; + } +} + +-(id)expandedAttribute { + return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::EXPANDED ) ]; +} + +-(id)selectedAttribute { + return [ NSNumber numberWithBool: [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::SELECTED ) ]; +} + +-(id)stringForRangeAttributeForParameter:(id)range { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper stringForRangeAttributeForElement: self forParameter: range ]; + } else { + return nil; + } +} + +-(id)attributedStringForRangeAttributeForParameter:(id)range { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper attributedStringForRangeAttributeForElement: self forParameter: range ]; + } else { + return nil; + } +} + +-(id)rangeForIndexAttributeForParameter:(id)index { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper rangeForIndexAttributeForElement: self forParameter: index ]; + } else { + return nil; + } +} + +-(id)rangeForPositionAttributeForParameter:(id)point { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper rangeForPositionAttributeForElement: self forParameter: point ]; + } else { + return nil; + } +} + +-(id)boundsForRangeAttributeForParameter:(id)range { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper boundsForRangeAttributeForElement: self forParameter: range ]; + } else { + return nil; + } +} + +-(id)styleRangeForIndexAttributeForParameter:(id)index { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper styleRangeForIndexAttributeForElement: self forParameter: index ]; + } else { + return nil; + } +} + +-(id)rTFForRangeAttributeForParameter:(id)range { + if ( [ self accessibleText ] != nil ) { + return [ AquaA11yTextWrapper rTFForRangeAttributeForElement: self forParameter: range ]; + } else { + return nil; + } +} + +-(id)orientationAttribute { + NSString * orientation = nil; + Reference < XAccessibleStateSet > stateSet = [ self accessibleContext ] -> getAccessibleStateSet(); + if ( stateSet -> contains ( AccessibleStateType::HORIZONTAL ) ) { + orientation = NSAccessibilityHorizontalOrientationValue; + } else if ( stateSet -> contains ( AccessibleStateType::VERTICAL ) ) { + orientation = NSAccessibilityVerticalOrientationValue; + } + return orientation; +} + +-(id)titleUIElementAttribute { + if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) { + NSString * title = [ self titleAttribute ]; + id titleElement = nil; + if ( [ title length ] == 0 ) { + AccessibleRelation relationLabeledBy = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABELED_BY ); + if ( relationLabeledBy.RelationType == AccessibleRelationType::LABELED_BY && relationLabeledBy.TargetSet.hasElements() ) { + Reference < XAccessible > rxAccessible ( relationLabeledBy.TargetSet[0], UNO_QUERY ); + titleElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ]; + } + } + if ( title != nil ) { + [ title release ]; + } + return titleElement; + } else { + return nil; + } +} + +-(id)servesAsTitleForUIElementsAttribute { + if ( [ self accessibleContext ] -> getAccessibleRelationSet().is() ) { + id titleForElement = nil; + AccessibleRelation relationLabelFor = [ self accessibleContext ] -> getAccessibleRelationSet() -> getRelationByType ( AccessibleRelationType::LABEL_FOR ); + if ( relationLabelFor.RelationType == AccessibleRelationType::LABEL_FOR && relationLabelFor.TargetSet.hasElements() ) { + Reference < XAccessible > rxAccessible ( relationLabelFor.TargetSet[0], UNO_QUERY ); + titleForElement = [ AquaA11yFactory wrapperForAccessibleContext: rxAccessible -> getAccessibleContext() ]; + } + return titleForElement; + } else { + return nil; + } +} + +-(id)lineForIndexAttributeForParameter:(id)index { + if ( [ self accessibleMultiLineText ] != nil ) { + return [ AquaA11yTextWrapper lineForIndexAttributeForElement: self forParameter: index ]; + } else { + return nil; + } +} + +-(id)rangeForLineAttributeForParameter:(id)line { + if ( [ self accessibleMultiLineText ] != nil ) { + return [ AquaA11yTextWrapper rangeForLineAttributeForElement: self forParameter: line ]; + } else { + return nil; + } +} + +#pragma mark - +#pragma mark Accessibility Protocol + +-(id)accessibilityAttributeValue:(NSString *)attribute { + // #i90575# guard NSAccessibility protocol against unwanted access + if ( isPopupMenuOpen ) { + return nil; + } + + id value = nil; + // if we are no longer in the wrapper repository, we have been disposed + AquaA11yWrapper * theWrapper = [ AquaA11yFactory wrapperForAccessibleContext: [ self accessibleContext ] createIfNotExists: NO ]; + if ( theWrapper != nil || mIsTableCell ) { + try { + SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: NO ]; + if ( [ self respondsToSelector: methodSelector ] ) { + value = [ self performSelector: methodSelector ]; + } + } catch ( const DisposedException & e ) { + mIsTableCell = NO; // just to be sure + [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ]; + return nil; + } catch ( const Exception & e ) { + // empty + } + } + if ( theWrapper != nil ) { + [ theWrapper release ]; // the above called method calls retain on the returned Wrapper + } + return value; +} + +-(MacOSBOOL)accessibilityIsIgnored { + // #i90575# guard NSAccessibility protocol against unwanted access + if ( isPopupMenuOpen ) { + return nil; + } + MacOSBOOL ignored = NO; + sal_Int16 nRole = [ self accessibleContext ] -> getAccessibleRole(); + switch ( nRole ) { + case AccessibleRole::PANEL: + case AccessibleRole::FRAME: + case AccessibleRole::ROOT_PANE: + case AccessibleRole::SEPARATOR: + case AccessibleRole::FILLER: + case AccessibleRole::DIALOG: + ignored = YES; + break; + default: + ignored = ! ( [ self accessibleContext ] -> getAccessibleStateSet() -> contains ( AccessibleStateType::VISIBLE ) ); + break; + } + return ignored; // TODO: to be completed +} + +-(NSArray *)accessibilityAttributeNames { + // #i90575# guard NSAccessibility protocol against unwanted access + if ( isPopupMenuOpen ) { + return nil; + } + NSString * nativeSubrole = nil; + NSString * title = nil; + NSMutableArray * attributeNames = nil; + sal_Int32 nAccessibleChildren = 0; + try { + // Default Attributes + attributeNames = [ NSMutableArray arrayWithObjects: + NSAccessibilityRoleAttribute, + NSAccessibilityDescriptionAttribute, + NSAccessibilityParentAttribute, + NSAccessibilityWindowAttribute, + NSAccessibilityHelpAttribute, + NSAccessibilityTopLevelUIElementAttribute, + NSAccessibilityRoleDescriptionAttribute, + nil ]; + nativeSubrole = (NSString *) [ AquaA11yRoleHelper getNativeSubroleFrom: [ self accessibleContext ] -> getAccessibleRole() ]; + title = (NSString *) [ self titleAttribute ]; + Reference < XAccessibleRelationSet > rxRelationSet = [ self accessibleContext ] -> getAccessibleRelationSet(); + // Special Attributes depending on attribute values + if ( nativeSubrole != nil && ! [ nativeSubrole isEqualToString: @"" ] ) { + [ attributeNames addObject: NSAccessibilitySubroleAttribute ]; + } + try + { + nAccessibleChildren = [ self accessibleContext ] -> getAccessibleChildCount(); + if ( nAccessibleChildren > 0 ) { + [ attributeNames addObject: NSAccessibilityChildrenAttribute ]; + } + } + catch( DisposedException& ) {} + catch( RuntimeException& ) {} + + if ( title != nil && ! [ title isEqualToString: @"" ] ) { + [ attributeNames addObject: NSAccessibilityTitleAttribute ]; + } + if ( [ title length ] == 0 && rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABELED_BY ) ) { + [ attributeNames addObject: NSAccessibilityTitleUIElementAttribute ]; + } + if ( rxRelationSet.is() && rxRelationSet -> containsRelation ( AccessibleRelationType::LABEL_FOR ) ) { + [ attributeNames addObject: NSAccessibilityServesAsTitleForUIElementsAttribute ]; + } + // Special Attributes depending on interface + if( [self accessibleContext ] -> getAccessibleRole() == AccessibleRole::TABLE ) + [AquaA11yTableWrapper addAttributeNamesTo: attributeNames object: self]; + + if ( [ self accessibleText ] != nil ) { + [ AquaA11yTextWrapper addAttributeNamesTo: attributeNames ]; + } + if ( [ self accessibleComponent ] != nil ) { + [ AquaA11yComponentWrapper addAttributeNamesTo: attributeNames ]; + } + if ( [ self accessibleSelection ] != nil ) { + [ AquaA11ySelectionWrapper addAttributeNamesTo: attributeNames ]; + } + if ( [ self accessibleValue ] != nil ) { + [ AquaA11yValueWrapper addAttributeNamesTo: attributeNames ]; + } + [ nativeSubrole release ]; + [ title release ]; + return attributeNames; + } catch ( DisposedException & e ) { // Object is no longer available + if ( nativeSubrole != nil ) { + [ nativeSubrole release ]; + } + if ( title != nil ) { + [ title release ]; + } + if ( attributeNames != nil ) { + [ attributeNames release ]; + } + [ AquaA11yFactory removeFromWrapperRepositoryFor: [ self accessibleContext ] ]; + return [ [ NSArray alloc ] init ]; + } +} + +-(MacOSBOOL)accessibilityIsAttributeSettable:(NSString *)attribute { + MacOSBOOL isSettable = NO; + if ( [ self accessibleText ] != nil ) { + isSettable = [ AquaA11yTextWrapper isAttributeSettable: attribute forElement: self ]; + } + if ( ! isSettable && [ self accessibleComponent ] != nil ) { + isSettable = [ AquaA11yComponentWrapper isAttributeSettable: attribute forElement: self ]; + } + if ( ! isSettable && [ self accessibleSelection ] != nil ) { + isSettable = [ AquaA11ySelectionWrapper isAttributeSettable: attribute forElement: self ]; + } + if ( ! isSettable && [ self accessibleValue ] != nil ) { + isSettable = [ AquaA11yValueWrapper isAttributeSettable: attribute forElement: self ]; + } + return isSettable; // TODO: to be completed +} + +-(NSArray *)accessibilityParameterizedAttributeNames { + NSMutableArray * attributeNames = [ [ NSMutableArray alloc ] init ]; + // Special Attributes depending on interface + if ( [ self accessibleText ] != nil ) { + [ AquaA11yTextWrapper addParameterizedAttributeNamesTo: attributeNames ]; + } + return attributeNames; // TODO: to be completed +} + +-(id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter { + SEL methodSelector = [ self selectorForAttribute: attribute asGetter: YES withGetterParameter: YES ]; + if ( [ self respondsToSelector: methodSelector ] ) { + return [ self performSelector: methodSelector withObject: parameter ]; + } + return nil; // TODO: to be completed +} + +-(MacOSBOOL)accessibilitySetOverrideValue:(id)value forAttribute:(NSString *)attribute { + return NO; // TODO +} + +-(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute { + SEL methodSelector = [ self selectorForAttribute: attribute asGetter: NO withGetterParameter: NO ]; + if ( [ AquaA11yComponentWrapper respondsToSelector: methodSelector ] ) { + [ AquaA11yComponentWrapper performSelector: methodSelector withObject: self withObject: value ]; + } + if ( [ AquaA11yTextWrapper respondsToSelector: methodSelector ] ) { + [ AquaA11yTextWrapper performSelector: methodSelector withObject: self withObject: value ]; + } + if ( [ AquaA11ySelectionWrapper respondsToSelector: methodSelector ] ) { + [ AquaA11ySelectionWrapper performSelector: methodSelector withObject: self withObject: value ]; + } + if ( [ AquaA11yValueWrapper respondsToSelector: methodSelector ] ) { + [ AquaA11yValueWrapper performSelector: methodSelector withObject: self withObject: value ]; + } +} + +-(id)accessibilityFocusedUIElement { + // #i90575# guard NSAccessibility protocol against unwanted access + if ( isPopupMenuOpen ) { + return nil; + } + + // as this seems to be the first API call on a newly created SalFrameView object, + // make sure self gets registered in the repository .. + [ self accessibleContext ]; + + AquaA11yWrapper * focusedUIElement = AquaA11yFocusListener::get()->getFocusedUIElement(); +// AquaA11yWrapper * ancestor = focusedUIElement; + + // Make sure the focused object is a descendant of self +// do { +// if( self == ancestor ) + return focusedUIElement; + +// ancestor = [ ancestor accessibilityAttributeValue: NSAccessibilityParentAttribute ]; +// } while( nil != ancestor ); + + return self; +} + +// TODO: hard-coded like the role descriptions. is there a better way? +-(NSString *)accessibilityActionDescription:(NSString *)action { + if ( [ action isEqualToString: NSAccessibilityConfirmAction ] ) { + return @"confirm"; + } else if ( [ action isEqualToString: NSAccessibilityDecrementAction ] ) { + return @"decrement"; + } else if ( [ action isEqualToString: NSAccessibilityDeleteAction ] ) { + return @"delete"; + } else if ( [ action isEqualToString: NSAccessibilityIncrementAction ] ) { + return @"increment"; + } else if ( [ action isEqualToString: NSAccessibilityPickAction ] ) { + return @"pick"; + } else if ( [ action isEqualToString: NSAccessibilityPressAction ] ) { + return @"press"; + } else if ( [ action isEqualToString: NSAccessibilityCancelAction ] ) { + return @"cancel"; + } else if ( [ action isEqualToString: NSAccessibilityRaiseAction ] ) { + return @"raise"; + } else if ( [ action isEqualToString: NSAccessibilityShowMenuAction ] ) { + return @"show menu"; + } else { + return [ NSString string ]; + } +} + +-(AquaA11yWrapper *)actionResponder { + AquaA11yWrapper * wrapper = nil; + // get some information + NSString * role = (NSString *) [ self accessibilityAttributeValue: NSAccessibilityRoleAttribute ]; + id enabledAttr = [ self enabledAttribute ]; + MacOSBOOL enabled = [ enabledAttr boolValue ]; + NSView * parent = (NSView *) [ self accessibilityAttributeValue: NSAccessibilityParentAttribute ]; + AquaA11yWrapper * parentAsWrapper = nil; + if ( [ parent isKindOfClass: [ AquaA11yWrapper class ] ] ) { + parentAsWrapper = (AquaA11yWrapper *) parent; + } + NSString * parentRole = (NSString *) [ parent accessibilityAttributeValue: NSAccessibilityRoleAttribute ]; + // if we are a textarea inside a combobox, then the combobox is the action responder + if ( enabled + && [ role isEqualToString: NSAccessibilityTextAreaRole ] + && [ parentRole isEqualToString: NSAccessibilityComboBoxRole ] + && parentAsWrapper != nil ) { + wrapper = parentAsWrapper; + } else if ( enabled && [ self accessibleAction ] != nil ) { + wrapper = self ; + } + [ parentRole release ]; + [ enabledAttr release ]; + [ role release ]; + return wrapper; +} + +-(void)accessibilityPerformAction:(NSString *)action { + AquaA11yWrapper * actionResponder = [ self actionResponder ]; + if ( actionResponder != nil ) { + [ AquaA11yActionWrapper doAction: action ofElement: actionResponder ]; + } +} + +-(NSArray *)accessibilityActionNames { + NSArray * actionNames = nil; + AquaA11yWrapper * actionResponder = [ self actionResponder ]; + if ( actionResponder != nil ) { + actionNames = [ AquaA11yActionWrapper actionNamesForElement: actionResponder ]; + } else { + actionNames = [ [ NSArray alloc ] init ]; + } + return actionNames; +} + +#pragma mark - +#pragma mark Hit Test + +-(MacOSBOOL)isViewElement:(NSObject *)viewElement hitByPoint:(NSPoint)point { + MacOSBOOL hit = NO; + NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ]; + NSValue * position = [ viewElement accessibilityAttributeValue: NSAccessibilityPositionAttribute ]; + NSValue * size = [ viewElement accessibilityAttributeValue: NSAccessibilitySizeAttribute ]; + if ( position != nil && size != nil ) { + float minX = [ position pointValue ].x; + float minY = [ position pointValue ].y; + float maxX = minX + [ size sizeValue ].width; + float maxY = minY + [ size sizeValue ].height; + if ( minX < point.x && maxX > point.x && minY < point.y && maxY > point.y ) { + hit = YES; + } + } + [ pool release ]; + return hit; +} + +Reference < XAccessibleContext > hitTestRunner ( com::sun::star::awt::Point point, + Reference < XAccessibleContext > rxAccessibleContext ) { + Reference < XAccessibleContext > hitChild; + Reference < XAccessibleContext > emptyReference; + try { + Reference < XAccessibleComponent > rxAccessibleComponent ( rxAccessibleContext, UNO_QUERY ); + if ( rxAccessibleComponent.is() ) { + com::sun::star::awt::Point location = rxAccessibleComponent -> getLocationOnScreen(); + com::sun::star::awt::Point hitPoint ( point.X - location.X , point.Y - location.Y); + Reference < XAccessible > rxAccessible = rxAccessibleComponent -> getAccessibleAtPoint ( hitPoint ); + if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) { + if ( rxAccessible -> getAccessibleContext() -> getAccessibleChildCount() > 0 ) { + hitChild = hitTestRunner ( point, rxAccessible -> getAccessibleContext() ); + if ( ! hitChild.is() ) { + hitChild = rxAccessible -> getAccessibleContext(); + } + } else { + hitChild = rxAccessible -> getAccessibleContext(); + } + } + } + if ( !hitChild.is() && rxAccessibleContext -> getAccessibleChildCount() > 0 ) { // special treatment for e.g. comboboxes + for ( int i = 0; i < rxAccessibleContext -> getAccessibleChildCount(); i++ ) { + Reference < XAccessible > rxAccessibleChild = rxAccessibleContext -> getAccessibleChild ( i ); + if ( rxAccessibleChild.is() && rxAccessibleChild -> getAccessibleContext().is() && rxAccessibleChild -> getAccessibleContext() -> getAccessibleRole() != AccessibleRole::LIST ) { + Reference < XAccessibleContext > myHitChild = hitTestRunner ( point, rxAccessibleChild -> getAccessibleContext() ); + if ( myHitChild.is() ) { + hitChild = myHitChild; + break; + } + } + } + } + } catch ( RuntimeException ) { + return emptyReference; + } + return hitChild; +} + +-(id)accessibilityHitTest:(NSPoint)point { + static id wrapper = nil; + if ( nil != wrapper ) { + [ wrapper release ]; + wrapper = nil; + } + Reference < XAccessibleContext > hitChild; + NSRect screenRect = [ [ NSScreen mainScreen ] frame ]; + com::sun::star::awt::Point hitPoint ( static_cast<long>(point.x) , static_cast<long>(screenRect.size.height - point.y) ); + // check child windows first + NSWindow * window = (NSWindow *) [ self accessibilityAttributeValue: NSAccessibilityWindowAttribute ]; + NSArray * childWindows = [ window childWindows ]; + if ( [ childWindows count ] > 0 ) { + NSWindow * element = nil; + NSEnumerator * enumerator = [ childWindows objectEnumerator ]; + while ( ( element = [ enumerator nextObject ] ) && hitChild == nil ) { + if ( [ element isKindOfClass: [ SalFrameWindow class ] ] && [ self isViewElement: element hitByPoint: point ] ) { + // we have a child window that is hit + Reference < XAccessibleRelationSet > relationSet = [ ( ( SalFrameWindow * ) element ) accessibleContext ] -> getAccessibleRelationSet(); + if ( relationSet.is() && relationSet -> containsRelation ( AccessibleRelationType::SUB_WINDOW_OF )) { + // we have a valid relation to the parent element + AccessibleRelation relation = relationSet -> getRelationByType ( AccessibleRelationType::SUB_WINDOW_OF ); + for ( int i = 0; i < relation.TargetSet.getLength() && !hitChild.is(); i++ ) { + Reference < XAccessible > rxAccessible ( relation.TargetSet [ i ], UNO_QUERY ); + if ( rxAccessible.is() && rxAccessible -> getAccessibleContext().is() ) { + // hit test for children of parent + hitChild = hitTestRunner ( hitPoint, rxAccessible -> getAccessibleContext() ); + } + } + } + } + } + } + // nothing hit yet, so check ourself + if ( ! hitChild.is() ) { + if ( mpReferenceWrapper == nil ) { + [ self setDefaults: [ self accessibleContext ] ]; + } + hitChild = hitTestRunner ( hitPoint, mpReferenceWrapper -> rAccessibleContext ); + } + if ( hitChild.is() ) { + wrapper = [ AquaA11yFactory wrapperForAccessibleContext: hitChild ]; + } + if ( wrapper != nil ) { + [ wrapper retain ]; // TODO: retain only when transient ? + } + return wrapper; +} + +#pragma mark - +#pragma mark Access Methods + +-(XAccessibleAction *)accessibleAction { + return mpReferenceWrapper -> rAccessibleAction.get(); +} + +-(XAccessibleContext *)accessibleContext { + return mpReferenceWrapper -> rAccessibleContext.get(); +} + +-(XAccessibleComponent *)accessibleComponent { + return mpReferenceWrapper -> rAccessibleComponent.get(); +} + +-(XAccessibleExtendedComponent *)accessibleExtendedComponent { + return mpReferenceWrapper -> rAccessibleExtendedComponent.get(); +} + +-(XAccessibleSelection *)accessibleSelection { + return mpReferenceWrapper -> rAccessibleSelection.get(); +} + +-(XAccessibleTable *)accessibleTable { + return mpReferenceWrapper -> rAccessibleTable.get(); +} + +-(XAccessibleText *)accessibleText { + return mpReferenceWrapper -> rAccessibleText.get(); +} + +-(XAccessibleEditableText *)accessibleEditableText { + return mpReferenceWrapper -> rAccessibleEditableText.get(); +} + +-(XAccessibleValue *)accessibleValue { + return mpReferenceWrapper -> rAccessibleValue.get(); +} + +-(XAccessibleTextAttributes *)accessibleTextAttributes { + return mpReferenceWrapper -> rAccessibleTextAttributes.get(); +} + +-(XAccessibleMultiLineText *)accessibleMultiLineText { + return mpReferenceWrapper -> rAccessibleMultiLineText.get(); +} + +-(NSView *)viewElementForParent { + return self; +} + +// These four are for AXTextAreas only. They are needed, because bold and italic +// attributes have to be bound to a font on the Mac. Our UNO-API instead handles +// and reports them independently. When they occur we bundle them to a font with +// this information here to create a according NSFont. +-(void)setDefaultFontname:(NSString *)fontname { + if ( mpDefaultFontname != nil ) { + [ mpDefaultFontname release ]; + } + mpDefaultFontname = fontname; +} + +-(NSString *)defaultFontname { + return mpDefaultFontname; +} + +-(void)setDefaultFontsize:(float)fontsize { + mDefaultFontsize = fontsize; +} + +-(float)defaultFontsize { + return mDefaultFontsize; +} + +-(void)setActsAsRadioGroup:(MacOSBOOL)actsAsRadioGroup { + mActsAsRadioGroup = actsAsRadioGroup; +} + +-(MacOSBOOL)actsAsRadioGroup { + return mActsAsRadioGroup; +} + ++(void)setPopupMenuOpen:(MacOSBOOL)popupMenuOpen { + isPopupMenuOpen = popupMenuOpen; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrapperbutton.h b/vcl/aqua/source/a11y/aqua11ywrapperbutton.h new file mode 100644 index 000000000000..aa35062d15c4 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperbutton.h @@ -0,0 +1,41 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERBUTTON_H +#define _SV_AQUA11WRAPPERBUTTON_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperButton : AquaA11yWrapper +{ +} +-(id)valueAttribute; +-(id)descriptionAttribute; +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERBUTTON_H diff --git a/vcl/aqua/source/a11y/aqua11ywrapperbutton.mm b/vcl/aqua/source/a11y/aqua11ywrapperbutton.mm new file mode 100644 index 000000000000..48f1804c58a2 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperbutton.mm @@ -0,0 +1,61 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrapperbutton.h" +#include "aqua11ytextwrapper.h" + +// Wrapper for AXButton role + +@implementation AquaA11yWrapperButton : AquaA11yWrapper + +-(id)valueAttribute { + return [ NSString string ]; // we propagate AXTitle, that's enough +} + +-(id)descriptionAttribute { + return [ NSString string ]; // we propagate AXTitle, that's enough +} + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + if ( [ attributeNames containsObject: NSAccessibilityTitleAttribute ] ) { + [ attributeNames removeObject: NSAccessibilityDescriptionAttribute ]; + } else { + [ attributeNames addObject: NSAccessibilityTitleAttribute ]; + } + // Remove text-specific attributes + [ attributeNames removeObjectsInArray: [ AquaA11yTextWrapper specialAttributeNames ] ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrappercheckbox.h b/vcl/aqua/source/a11y/aqua11ywrappercheckbox.h new file mode 100644 index 000000000000..95fee9a3ec4b --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappercheckbox.h @@ -0,0 +1,41 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERCHECKBOX_H +#define _SV_AQUA11WRAPPERCHECKBOX_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperCheckBox : AquaA11yWrapper +{ +} +-(id)valueAttribute; +-(MacOSBOOL)accessibilityIsAttributeSettable:(NSString *)attribute; +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERCHECKOBOX_H diff --git a/vcl/aqua/source/a11y/aqua11ywrappercheckbox.mm b/vcl/aqua/source/a11y/aqua11ywrappercheckbox.mm new file mode 100644 index 000000000000..c4ac34dc5bce --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappercheckbox.mm @@ -0,0 +1,65 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrappercheckbox.h" +#include "aqua11yvaluewrapper.h" +#include "aqua11ytextwrapper.h" + +// Wrapper for AXCheckbox role + +@implementation AquaA11yWrapperCheckBox : AquaA11yWrapper + +-(id)valueAttribute { + if ( [ self accessibleValue ] != nil ) { + return [ AquaA11yValueWrapper valueAttributeForElement: self ]; + } + return [ NSNumber numberWithInt: 0 ]; +} + +-(MacOSBOOL)accessibilityIsAttributeSettable:(NSString *)attribute { + if ( [ attribute isEqualToString: NSAccessibilityValueAttribute ] ) { + return NO; + } + return [ super accessibilityIsAttributeSettable: attribute ]; +} + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Remove text-specific attributes + [ attributeNames removeObjectsInArray: [ AquaA11yTextWrapper specialAttributeNames ] ]; + [ attributeNames addObject: NSAccessibilityValueAttribute ]; + [ attributeNames addObject: NSAccessibilityMinValueAttribute ]; + [ attributeNames addObject: NSAccessibilityMaxValueAttribute ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrappercombobox.h b/vcl/aqua/source/a11y/aqua11ywrappercombobox.h new file mode 100644 index 000000000000..7ed76d607176 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappercombobox.h @@ -0,0 +1,50 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERCOMBOBOX_H +#define _SV_AQUA11WRAPPERCOMBOBOX_H + +#include "aqua11ywrapper.h" +#include <com/sun/star/accessibility/XAccessibleContext.hpp> + +@interface AquaA11yWrapperComboBox : AquaA11yWrapper +{ + AquaA11yWrapper * textArea; +} +-(id)initWithAccessibleContext: (::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >) anAccessibleContext; +-(id)valueAttribute; +-(id)numberOfCharactersAttribute; +-(id)selectedTextAttribute; +-(id)selectedTextRangeAttribute; +-(id)visibleCharacterRangeAttribute; +// Accessibility Protocol +-(MacOSBOOL)accessibilityIsAttributeSettable:(NSString *)attribute; +-(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute; +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERCOMBOBOX_H diff --git a/vcl/aqua/source/a11y/aqua11ywrappercombobox.mm b/vcl/aqua/source/a11y/aqua11ywrappercombobox.mm new file mode 100644 index 000000000000..85aed320e470 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappercombobox.mm @@ -0,0 +1,161 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrappercombobox.h" +#include "aqua11yrolehelper.h" +#include <com/sun/star/accessibility/AccessibleStateType.hpp> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + +// Wrapper for AXCombobox role + +@implementation AquaA11yWrapperComboBox : AquaA11yWrapper + +#pragma mark - +#pragma mark Specialized Init Method + +-(id)initWithAccessibleContext: (Reference < XAccessibleContext >) rxAccessibleContext { + self = [ super initWithAccessibleContext: rxAccessibleContext ]; + if ( self != nil ) + { + textArea = nil; + } + return self; +} + +#pragma mark - +#pragma mark Private Helper Method + +-(AquaA11yWrapper *)textArea { + // FIXME: May cause problems when stored. Then get dynamically each time (bad performance!) + if ( textArea == nil ) { + NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ]; + NSArray * elementChildren = [ super childrenAttribute ]; + if ( [ elementChildren count ] > 0 ) { + NSEnumerator * enumerator = [ elementChildren objectEnumerator ]; + id child; + while ( ( child = [ enumerator nextObject ] ) ) { + AquaA11yWrapper * element = ( AquaA11yWrapper * ) child; + if ( [ [ AquaA11yRoleHelper getNativeRoleFrom: [ element accessibleContext ] ] isEqualToString: NSAccessibilityTextAreaRole ] ) { + textArea = element; + break; + } + } + } + [ pool release ]; + } + return textArea; +} + +#pragma mark - +#pragma mark Wrapped Attributes From Contained Text Area + +-(id)valueAttribute { + if ( [ self textArea ] != nil ) { + return [ [ self textArea ] valueAttribute ]; + } + return @""; +} + +-(id)numberOfCharactersAttribute { + if ( [ self textArea ] != nil ) { + return [ [ self textArea ] numberOfCharactersAttribute ]; + } + return [ NSNumber numberWithInt: 0 ]; +} + +-(id)selectedTextAttribute { + if ( [ self textArea ] != nil ) { + return [ [ self textArea ] selectedTextAttribute ]; + } + return @""; +} + +-(id)selectedTextRangeAttribute { + if ( [ self textArea ] != nil ) { + return [ [ self textArea ] selectedTextRangeAttribute ]; + } + return [ NSValue valueWithRange: NSMakeRange ( 0, 0 ) ]; +} + +-(id)visibleCharacterRangeAttribute { + if ( [ self textArea ] != nil ) { + return [ [ self textArea ] visibleCharacterRangeAttribute ]; + } + return [ NSValue valueWithRange: NSMakeRange ( 0, 0 ) ]; +} + +#pragma mark - +#pragma mark Accessibility Protocol + +-(MacOSBOOL)accessibilityIsAttributeSettable:(NSString *)attribute { + if ( [ self textArea ] != nil && ( + [ attribute isEqualToString: NSAccessibilitySelectedTextAttribute ] + || [ attribute isEqualToString: NSAccessibilitySelectedTextRangeAttribute ] + || [ attribute isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute ] ) ) { + return [ [ self textArea ] accessibilityIsAttributeSettable: attribute ]; + } + return [ super accessibilityIsAttributeSettable: attribute ]; +} + +-(void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute { + if ( [ self textArea ] != nil && ( + [ attribute isEqualToString: NSAccessibilitySelectedTextAttribute ] + || [ attribute isEqualToString: NSAccessibilitySelectedTextRangeAttribute ] + || [ attribute isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute ] ) ) { + return [ [ self textArea ] accessibilitySetValue: value forAttribute: attribute ]; + } + return [ super accessibilitySetValue: value forAttribute: attribute ]; +} + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames removeObjectsInArray: [ NSArray arrayWithObjects: + NSAccessibilityTitleAttribute, + NSAccessibilityChildrenAttribute, + nil ] + ]; + [ attributeNames addObjectsFromArray: [ NSArray arrayWithObjects: + NSAccessibilityExpandedAttribute, + NSAccessibilityValueAttribute, + NSAccessibilityNumberOfCharactersAttribute, + NSAccessibilitySelectedTextAttribute, + NSAccessibilitySelectedTextRangeAttribute, + NSAccessibilityVisibleCharacterRangeAttribute, + nil ] + ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrappergroup.h b/vcl/aqua/source/a11y/aqua11ywrappergroup.h new file mode 100644 index 000000000000..7757e067ee22 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappergroup.h @@ -0,0 +1,40 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERGROUP_H +#define _SV_AQUA11WRAPPERGROUP_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperGroup : AquaA11yWrapper +{ +} +-(id)titleUIElementAttribute; +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERGROUP_H diff --git a/vcl/aqua/source/a11y/aqua11ywrappergroup.mm b/vcl/aqua/source/a11y/aqua11ywrappergroup.mm new file mode 100644 index 000000000000..42298f9c745b --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappergroup.mm @@ -0,0 +1,57 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrappergroup.h" + +// Wrapper for AXGroup role + +@implementation AquaA11yWrapperGroup : AquaA11yWrapper + +-(id)titleUIElementAttribute { + return self; // TODO +} + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames removeObjectsInArray: [ NSArray arrayWithObjects: + NSAccessibilityTitleAttribute, + NSAccessibilityEnabledAttribute, + NSAccessibilitySelectedChildrenAttribute, + nil ] + ]; + [ attributeNames addObject: NSAccessibilityContentsAttribute ]; + [ attributeNames addObject: NSAccessibilityTitleUIElementAttribute ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrapperlist.h b/vcl/aqua/source/a11y/aqua11ywrapperlist.h new file mode 100644 index 000000000000..95df8323467b --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperlist.h @@ -0,0 +1,39 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERLIST_H +#define _SV_AQUA11WRAPPERLIST_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperList : AquaA11yWrapper +{ +} +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERLIST_H diff --git a/vcl/aqua/source/a11y/aqua11ywrapperlist.mm b/vcl/aqua/source/a11y/aqua11ywrapperlist.mm new file mode 100644 index 000000000000..eeb210d70e65 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperlist.mm @@ -0,0 +1,48 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrapperlist.h" + +using namespace ::com::sun::star::accessibility; + +// Wrapper for AXList role + +@implementation AquaA11yWrapperList : AquaA11yWrapper + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames addObject: NSAccessibilityOrientationAttribute ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrapperradiobutton.h b/vcl/aqua/source/a11y/aqua11ywrapperradiobutton.h new file mode 100644 index 000000000000..13ceee6f826f --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperradiobutton.h @@ -0,0 +1,41 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERRADIOBUTTON_H +#define _SV_AQUA11WRAPPERRADIOBUTTON_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperRadioButton : AquaA11yWrapper +{ +} +-(id)valueAttribute; +-(MacOSBOOL)accessibilityIsAttributeSettable:(NSString *)attribute; +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERRADIOGROUP_H diff --git a/vcl/aqua/source/a11y/aqua11ywrapperradiobutton.mm b/vcl/aqua/source/a11y/aqua11ywrapperradiobutton.mm new file mode 100644 index 000000000000..54d6edac619a --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperradiobutton.mm @@ -0,0 +1,65 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrapperradiobutton.h" +#include "aqua11ytextwrapper.h" +#include "aqua11yvaluewrapper.h" + +// Wrapper for AXRadioButton role + +@implementation AquaA11yWrapperRadioButton : AquaA11yWrapper + +-(id)valueAttribute { + if ( [ self accessibleValue ] != nil ) { + return [ AquaA11yValueWrapper valueAttributeForElement: self ]; + } + return [ NSNumber numberWithInt: 0 ]; +} + +-(MacOSBOOL)accessibilityIsAttributeSettable:(NSString *)attribute { + if ( [ attribute isEqualToString: NSAccessibilityValueAttribute ] ) { + return NO; + } + return [ super accessibilityIsAttributeSettable: attribute ]; +} + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames removeObjectsInArray: [ AquaA11yTextWrapper specialAttributeNames ] ]; + [ attributeNames addObject: NSAccessibilityMinValueAttribute ]; + [ attributeNames addObject: NSAccessibilityMaxValueAttribute ]; + [ attributeNames addObject: NSAccessibilityValueAttribute ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrapperradiogroup.h b/vcl/aqua/source/a11y/aqua11ywrapperradiogroup.h new file mode 100644 index 000000000000..544b709223b3 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperradiogroup.h @@ -0,0 +1,39 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERRADIOGROUP_H +#define _SV_AQUA11WRAPPERRADIOGROUP_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperRadioGroup : AquaA11yWrapper +{ +} +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERRADIOGROUP_H diff --git a/vcl/aqua/source/a11y/aqua11ywrapperradiogroup.mm b/vcl/aqua/source/a11y/aqua11ywrapperradiogroup.mm new file mode 100644 index 000000000000..f89ac78b044c --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperradiogroup.mm @@ -0,0 +1,48 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrapperradiogroup.h" +#include "aqua11ytextwrapper.h" + +// Wrapper for AXRadioGroup role + +@implementation AquaA11yWrapperRadioGroup : AquaA11yWrapper + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames removeObjectsInArray: [ AquaA11yTextWrapper specialAttributeNames ] ]; + [ attributeNames removeObject: NSAccessibilityTitleAttribute ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrapperrow.h b/vcl/aqua/source/a11y/aqua11ywrapperrow.h new file mode 100644 index 000000000000..252af6f5987f --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperrow.h @@ -0,0 +1,40 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERROW_H +#define _SV_AQUA11WRAPPERROW_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperRow : AquaA11yWrapper +{ +} +-(id)disclosingAttribute; +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERROW_H diff --git a/vcl/aqua/source/a11y/aqua11ywrapperrow.mm b/vcl/aqua/source/a11y/aqua11ywrapperrow.mm new file mode 100644 index 000000000000..d49e229218bf --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperrow.mm @@ -0,0 +1,56 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrapperrow.h" +#include "aqua11ytextwrapper.h" + +// Wrapper for AXRow role + +@implementation AquaA11yWrapperRow : AquaA11yWrapper + +-(id)disclosingAttribute { + return NO; // TODO +} + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames removeObjectsInArray: [ AquaA11yTextWrapper specialAttributeNames ] ]; + [ attributeNames removeObject: NSAccessibilityTitleAttribute ]; + [ attributeNames removeObject: NSAccessibilityEnabledAttribute ]; + [ attributeNames removeObject: NSAccessibilityFocusedAttribute ]; + [ attributeNames addObject: NSAccessibilitySelectedAttribute ]; + [ attributeNames addObject: NSAccessibilityDisclosingAttribute ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrapperscrollarea.h b/vcl/aqua/source/a11y/aqua11ywrapperscrollarea.h new file mode 100644 index 000000000000..2c206fd0904b --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperscrollarea.h @@ -0,0 +1,41 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERSCROLLAREA_H +#define _SV_AQUA11WRAPPERSCROLLAREA_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperScrollArea : AquaA11yWrapper +{ +} +-(id)verticalScrollBarAttribute; +-(id)horizontalScrollBarAttribute; +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERSCROLLAREA_H diff --git a/vcl/aqua/source/a11y/aqua11ywrapperscrollarea.mm b/vcl/aqua/source/a11y/aqua11ywrapperscrollarea.mm new file mode 100644 index 000000000000..f375e5ce788d --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperscrollarea.mm @@ -0,0 +1,84 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrapperscrollarea.h" +#include "aqua11ywrapperscrollbar.h" +#include "aqua11yrolehelper.h" + +// Wrapper for AXScrollArea role + +@implementation AquaA11yWrapperScrollArea : AquaA11yWrapper + +-(id)scrollBarWithOrientation:(NSString *)orientation { + AquaA11yWrapper * theScrollBar = nil; + NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ]; + NSArray * elementChildren = [ self accessibilityAttributeValue: NSAccessibilityChildrenAttribute ]; + if ( [ elementChildren count ] > 0 ) { + NSEnumerator * enumerator = [ elementChildren objectEnumerator ]; + id child; + while ( ( child = [ enumerator nextObject ] ) ) { + AquaA11yWrapper * element = ( AquaA11yWrapper * ) child; + if ( [ element isKindOfClass: [ AquaA11yWrapperScrollBar class ] ] ) { + AquaA11yWrapperScrollBar * scrollBar = (AquaA11yWrapperScrollBar *) element; + if ( [ [ scrollBar orientationAttribute ] isEqualToString: orientation ] ) { + theScrollBar = scrollBar; + break; + } + } + } + } + [ pool release ]; + return theScrollBar; +} + +-(id)verticalScrollBarAttribute { + return [ self scrollBarWithOrientation: NSAccessibilityVerticalOrientationValue ]; +} + +-(id)horizontalScrollBarAttribute { + return [ self scrollBarWithOrientation: NSAccessibilityHorizontalOrientationValue ]; +} + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames removeObject: NSAccessibilityEnabledAttribute ]; + [ attributeNames addObjectsFromArray: [ NSArray arrayWithObjects: + NSAccessibilityContentsAttribute, + NSAccessibilityVerticalScrollBarAttribute, + NSAccessibilityHorizontalScrollBarAttribute, + nil ] + ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrapperscrollbar.h b/vcl/aqua/source/a11y/aqua11ywrapperscrollbar.h new file mode 100644 index 000000000000..1070c682cd5e --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperscrollbar.h @@ -0,0 +1,39 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERSCROLLBAR_H +#define _SV_AQUA11WRAPPERSCROLLBAR_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperScrollBar : AquaA11yWrapper +{ +} +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERSCROLLBAR_H diff --git a/vcl/aqua/source/a11y/aqua11ywrapperscrollbar.mm b/vcl/aqua/source/a11y/aqua11ywrapperscrollbar.mm new file mode 100644 index 000000000000..826da647055b --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperscrollbar.mm @@ -0,0 +1,49 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrapperscrollbar.h" +#include <com/sun/star/accessibility/AccessibleStateType.hpp> + +using namespace ::com::sun::star::accessibility; + +// Wrapper for AXScrollBar role + +@implementation AquaA11yWrapperScrollBar : AquaA11yWrapper + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames addObject: NSAccessibilityOrientationAttribute ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrappersplitter.h b/vcl/aqua/source/a11y/aqua11ywrappersplitter.h new file mode 100644 index 000000000000..084a72ea7a18 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappersplitter.h @@ -0,0 +1,39 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERSPLITTER_H +#define _SV_AQUA11WRAPPERSPLITTER_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperSplitter : AquaA11yWrapper +{ +} +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERSPLITTER_H diff --git a/vcl/aqua/source/a11y/aqua11ywrappersplitter.mm b/vcl/aqua/source/a11y/aqua11ywrappersplitter.mm new file mode 100644 index 000000000000..4dc645c006c8 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappersplitter.mm @@ -0,0 +1,48 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrappersplitter.h" + +using namespace ::com::sun::star::accessibility; + +// Wrapper for AXSplitter role + +@implementation AquaA11yWrapperSplitter : AquaA11yWrapper + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames addObject: NSAccessibilityOrientationAttribute ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrapperstatictext.h b/vcl/aqua/source/a11y/aqua11ywrapperstatictext.h new file mode 100644 index 000000000000..c21e5573d125 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperstatictext.h @@ -0,0 +1,40 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERSTATICTEXT_H +#define _SV_AQUA11WRAPPERSTATICTEXT_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperStaticText : AquaA11yWrapper +{ +} +-(id)titleAttribute; +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERSTATICTEXT_H diff --git a/vcl/aqua/source/a11y/aqua11ywrapperstatictext.mm b/vcl/aqua/source/a11y/aqua11ywrapperstatictext.mm new file mode 100644 index 000000000000..7192e64b2e7c --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrapperstatictext.mm @@ -0,0 +1,56 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrapperstatictext.h" + +// Wrapper for AXStaticText role + +@implementation AquaA11yWrapperStaticText : AquaA11yWrapper + +-(id)titleAttribute { + NSString * title = [ super titleAttribute ]; + if ( [ title isEqualToString: [ super valueAttribute ] ] ) { + return [ NSString string ]; + } + return title; +} + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames removeObject: NSAccessibilityTitleAttribute ]; + [ attributeNames removeObject: NSAccessibilitySharedTextUIElementsAttribute ]; + [ attributeNames removeObject: NSAccessibilitySharedCharacterRangeAttribute ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrappertabgroup.h b/vcl/aqua/source/a11y/aqua11ywrappertabgroup.h new file mode 100644 index 000000000000..be72b9e27396 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappertabgroup.h @@ -0,0 +1,39 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERTABGROUP_H +#define _SV_AQUA11WRAPPERTABGROUP_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperTabGroup : AquaA11yWrapper +{ +} +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERTABGROUP_H diff --git a/vcl/aqua/source/a11y/aqua11ywrappertabgroup.mm b/vcl/aqua/source/a11y/aqua11ywrappertabgroup.mm new file mode 100644 index 000000000000..708ae5440c4f --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappertabgroup.mm @@ -0,0 +1,50 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrappertabgroup.h" + +// Wrapper for AXTabGroup role + +@implementation AquaA11yWrapperTabGroup : AquaA11yWrapper + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames addObjectsFromArray: [ NSArray arrayWithObjects: + NSAccessibilityContentsAttribute, + NSAccessibilityTabsAttribute, + nil ] + ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrappertextarea.h b/vcl/aqua/source/a11y/aqua11ywrappertextarea.h new file mode 100644 index 000000000000..724f85994053 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappertextarea.h @@ -0,0 +1,39 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERTEXTAREA_H +#define _SV_AQUA11WRAPPERTEXTAREA_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperTextArea : AquaA11yWrapper +{ +} +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERTEXTAREA_H diff --git a/vcl/aqua/source/a11y/aqua11ywrappertextarea.mm b/vcl/aqua/source/a11y/aqua11ywrappertextarea.mm new file mode 100644 index 000000000000..9a425eb2b893 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappertextarea.mm @@ -0,0 +1,48 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrappertextarea.h" + +// Wrapper for AXTextArea role + +@implementation AquaA11yWrapperTextArea : AquaA11yWrapper + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames removeObject: NSAccessibilityTitleAttribute ]; + [ attributeNames removeObject: NSAccessibilityEnabledAttribute ]; + [ attributeNames addObject: NSAccessibilityChildrenAttribute ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/aqua11ywrappertoolbar.h b/vcl/aqua/source/a11y/aqua11ywrappertoolbar.h new file mode 100644 index 000000000000..e7ac0a25acec --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappertoolbar.h @@ -0,0 +1,39 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_AQUA11WRAPPERTOOLBAR_H +#define _SV_AQUA11WRAPPERTOOLBAR_H + +#include "aqua11ywrapper.h" + +@interface AquaA11yWrapperToolbar : AquaA11yWrapper +{ +} +-(NSArray *)accessibilityAttributeNames; +@end + +#endif // _SV_AQUA11WRAPPERTOOLBAR_H diff --git a/vcl/aqua/source/a11y/aqua11ywrappertoolbar.mm b/vcl/aqua/source/a11y/aqua11ywrappertoolbar.mm new file mode 100644 index 000000000000..28990355af55 --- /dev/null +++ b/vcl/aqua/source/a11y/aqua11ywrappertoolbar.mm @@ -0,0 +1,50 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "aqua11ywrappertoolbar.h" + +// Wrapper for AXToolbar role + +@implementation AquaA11yWrapperToolbar : AquaA11yWrapper + +-(NSArray *)accessibilityAttributeNames { + // Default Attributes + NSMutableArray * attributeNames = [ NSMutableArray arrayWithArray: [ super accessibilityAttributeNames ] ]; + // Special Attributes and removing unwanted attributes depending on role + [ attributeNames removeObjectsInArray: [ NSArray arrayWithObjects: + NSAccessibilityTitleAttribute, + NSAccessibilityEnabledAttribute, + nil ] + ]; + return attributeNames; +} + +@end diff --git a/vcl/aqua/source/a11y/documentfocuslistener.cxx b/vcl/aqua/source/a11y/documentfocuslistener.cxx new file mode 100644 index 000000000000..02a7337ce397 --- /dev/null +++ b/vcl/aqua/source/a11y/documentfocuslistener.cxx @@ -0,0 +1,253 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "documentfocuslistener.hxx" + +#ifndef _COM_SUN_STAR_ACCESSIBILITY_XACCESSIBLEEVENTBROADCASTER_HPP_ +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#endif + +#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLEEVENTID_HPP_ +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#endif + +#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLESTATETYPE_HPP_ +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#endif + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; + + +//------------------------------------------------------------------------------ + +DocumentFocusListener::DocumentFocusListener(AquaA11yFocusTracker& rTracker) : + m_aFocusTracker(rTracker) +{ +} + +//------------------------------------------------------------------------------ + +void SAL_CALL +DocumentFocusListener::disposing( const EventObject& aEvent ) + throw (RuntimeException) +{ + // Unref the object here, but do not remove as listener since the object + // might no longer be in a state that safely allows this. + if( aEvent.Source.is() ) + m_aRefList.erase(aEvent.Source); +} + +//------------------------------------------------------------------------------ + +void SAL_CALL +DocumentFocusListener::notifyEvent( const AccessibleEventObject& aEvent ) + throw( RuntimeException ) +{ + switch( aEvent.EventId ) + { + case AccessibleEventId::STATE_CHANGED: + try + { + sal_Int16 nState = AccessibleStateType::INVALID; + aEvent.NewValue >>= nState; + + if( AccessibleStateType::FOCUSED == nState ) + m_aFocusTracker.setFocusedObject( getAccessible(aEvent) ); + } + catch(IndexOutOfBoundsException e) + { + OSL_TRACE("Focused object has invalid index in parent"); + } + break; + + case AccessibleEventId::CHILD: + { + Reference< XAccessible > xChild; + if( (aEvent.OldValue >>= xChild) && xChild.is() ) + detachRecursive(xChild); + + if( (aEvent.NewValue >>= xChild) && xChild.is() ) + attachRecursive(xChild); + } + break; + + case AccessibleEventId::INVALIDATE_ALL_CHILDREN: + { + Reference< XAccessible > xAccessible( getAccessible(aEvent) ); + detachRecursive(xAccessible); + attachRecursive(xAccessible); + } + + OSL_TRACE( "Invalidate all children called\n" ); + break; + default: + break; + } +} + +//------------------------------------------------------------------------------ + +Reference< XAccessible > DocumentFocusListener::getAccessible(const EventObject& aEvent ) + throw (IndexOutOfBoundsException, RuntimeException) +{ + Reference< XAccessible > xAccessible(aEvent.Source, UNO_QUERY); + + if( xAccessible.is() ) + return xAccessible; + + Reference< XAccessibleContext > xContext(aEvent.Source, UNO_QUERY); + + if( xContext.is() ) + { + Reference< XAccessible > xParent( xContext->getAccessibleParent() ); + if( xParent.is() ) + { + Reference< XAccessibleContext > xParentContext( xParent->getAccessibleContext() ); + if( xParentContext.is() ) + { + return xParentContext->getAccessibleChild( xContext->getAccessibleIndexInParent() ); + } + } + } + + return Reference< XAccessible >(); +} + +//------------------------------------------------------------------------------ + +void DocumentFocusListener::attachRecursive(const Reference< XAccessible >& xAccessible) + throw (IndexOutOfBoundsException, RuntimeException) +{ + Reference< XAccessibleContext > xContext = xAccessible->getAccessibleContext(); + + if( xContext.is() ) + attachRecursive(xAccessible, xContext); +} + +//------------------------------------------------------------------------------ + +void DocumentFocusListener::attachRecursive( + const Reference< XAccessible >& xAccessible, + const Reference< XAccessibleContext >& xContext +) throw (IndexOutOfBoundsException, RuntimeException) +{ + if( xContext.is() ) + { + Reference< XAccessibleStateSet > xStateSet = xContext->getAccessibleStateSet(); + + if( xStateSet.is() ) + attachRecursive(xAccessible, xContext, xStateSet); + } +} + +//------------------------------------------------------------------------------ + +void DocumentFocusListener::attachRecursive( + const Reference< XAccessible >& xAccessible, + const Reference< XAccessibleContext >& xContext, + const Reference< XAccessibleStateSet >& xStateSet +) throw (IndexOutOfBoundsException,RuntimeException) +{ + if( xStateSet->contains(AccessibleStateType::FOCUSED ) ) + m_aFocusTracker.setFocusedObject( xAccessible ); + + Reference< XAccessibleEventBroadcaster > xBroadcaster = + Reference< XAccessibleEventBroadcaster >(xContext, UNO_QUERY); + + // If not already done, add the broadcaster to the list and attach as listener. + if( xBroadcaster.is() && m_aRefList.insert(xBroadcaster).second ) + { + xBroadcaster->addEventListener(static_cast< XAccessibleEventListener *>(this)); + + if( ! xStateSet->contains(AccessibleStateType::MANAGES_DESCENDANTS ) ) + { + sal_Int32 n, nmax = xContext->getAccessibleChildCount(); + for( n = 0; n < nmax; n++ ) + { + Reference< XAccessible > xChild( xContext->getAccessibleChild( n ) ); + + if( xChild.is() ) + attachRecursive(xChild); + } + } + } +} + +//------------------------------------------------------------------------------ + +void DocumentFocusListener::detachRecursive(const Reference< XAccessible >& xAccessible) + throw (IndexOutOfBoundsException, RuntimeException) +{ + Reference< XAccessibleContext > xContext = xAccessible->getAccessibleContext(); + + if( xContext.is() ) + detachRecursive(xAccessible, xContext); +} + +//------------------------------------------------------------------------------ + +void DocumentFocusListener::detachRecursive( + const Reference< XAccessible >& xAccessible, + const Reference< XAccessibleContext >& xContext +) throw (IndexOutOfBoundsException, RuntimeException) +{ + Reference< XAccessibleStateSet > xStateSet = xContext->getAccessibleStateSet(); + + if( xStateSet.is() ) + detachRecursive(xAccessible, xContext, xStateSet); +} + +//------------------------------------------------------------------------------ + +void DocumentFocusListener::detachRecursive( + const Reference< XAccessible >&, + const Reference< XAccessibleContext >& xContext, + const Reference< XAccessibleStateSet >& xStateSet +) throw (IndexOutOfBoundsException, RuntimeException) +{ + Reference< XAccessibleEventBroadcaster > xBroadcaster = + Reference< XAccessibleEventBroadcaster >(xContext, UNO_QUERY); + + if( xBroadcaster.is() && 0 < m_aRefList.erase(xBroadcaster) ) + { + xBroadcaster->removeEventListener(static_cast< XAccessibleEventListener *>(this)); + + if( ! xStateSet->contains(AccessibleStateType::MANAGES_DESCENDANTS ) ) + { + sal_Int32 n, nmax = xContext->getAccessibleChildCount(); + for( n = 0; n < nmax; n++ ) + { + Reference< XAccessible > xChild( xContext->getAccessibleChild( n ) ); + + if( xChild.is() ) + detachRecursive(xChild); + } + } + } +} diff --git a/vcl/aqua/source/a11y/documentfocuslistener.hxx b/vcl/aqua/source/a11y/documentfocuslistener.hxx new file mode 100644 index 000000000000..863bc59d173f --- /dev/null +++ b/vcl/aqua/source/a11y/documentfocuslistener.hxx @@ -0,0 +1,101 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _DOCUMENTFOCUSLISTENER_HXX_ +#define _DOCUMENTFOCUSLISTENER_HXX_ + +#ifndef _COM_SUN_STAR_ACCESSIBILITY_XACCESSIBLEEVENTLISTENER_HPP_ +#include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#endif + +#ifndef _CPPUHELPER_IMPLBASE1_HXX_ +#include <cppuhelper/implbase1.hxx> +#endif + +#include "aqua11yfocustracker.hxx" +#include <set> + +// ------------------------- +// - DocumentFocusListener - +// ------------------------- + +class DocumentFocusListener : + public ::cppu::WeakImplHelper1< ::com::sun::star::accessibility::XAccessibleEventListener > +{ + +public: + + DocumentFocusListener(AquaA11yFocusTracker& rTracker); + + void attachRecursive( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible + ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + + void attachRecursive( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible, + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& xContext + ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + + void attachRecursive( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible, + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& xContext, + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleStateSet >& xStateSet + ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + + void detachRecursive( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible + ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + + void detachRecursive( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible, + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& xContext + ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + + void detachRecursive( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& xAccessible, + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& xContext, + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleStateSet >& xStateSet + ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + + static ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > getAccessible(const ::com::sun::star::lang::EventObject& aEvent ) + throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + + // XEventListener + virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source ) + throw (::com::sun::star::uno::RuntimeException); + + // XAccessibleEventListener + virtual void SAL_CALL notifyEvent( const ::com::sun::star::accessibility::AccessibleEventObject& aEvent ) + throw( ::com::sun::star::uno::RuntimeException ); + +private: + std::set< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > > m_aRefList; + + AquaA11yFocusTracker& m_aFocusTracker; +}; + +#endif // _DOCUMENTFOCUSLISTENER_HXX_
\ No newline at end of file diff --git a/vcl/aqua/source/a11y/makefile.mk b/vcl/aqua/source/a11y/makefile.mk new file mode 100644 index 000000000000..0a16281e5a69 --- /dev/null +++ b/vcl/aqua/source/a11y/makefile.mk @@ -0,0 +1,88 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# 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 +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=vcl +TARGET=sala11y +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile2.pmk + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"!="aqua" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"!="aqua" + +SLOFILES= \ + $(SLO)$/aqua11ywrapper.obj \ + $(SLO)$/aqua11yfactory.obj \ + $(SLO)$/aqua11yfocuslistener.obj \ + $(SLO)$/aqua11yfocustracker.obj \ + $(SLO)$/aqua11ylistener.obj \ + $(SLO)$/aqua11yrolehelper.obj \ + $(SLO)$/aqua11yactionwrapper.obj \ + $(SLO)$/aqua11ycomponentwrapper.obj \ + $(SLO)$/aqua11yselectionwrapper.obj \ + $(SLO)$/aqua11ytablewrapper.obj \ + $(SLO)$/aqua11ytextattributeswrapper.obj \ + $(SLO)$/aqua11ytextwrapper.obj \ + $(SLO)$/aqua11yutil.obj \ + $(SLO)$/aqua11yvaluewrapper.obj \ + $(SLO)$/aqua11ywrapperbutton.obj \ + $(SLO)$/aqua11ywrappercheckbox.obj \ + $(SLO)$/aqua11ywrappercombobox.obj \ + $(SLO)$/aqua11ywrappergroup.obj \ + $(SLO)$/aqua11ywrapperlist.obj \ + $(SLO)$/aqua11ywrapperradiobutton.obj \ + $(SLO)$/aqua11ywrapperradiogroup.obj \ + $(SLO)$/aqua11ywrapperrow.obj \ + $(SLO)$/aqua11ywrapperscrollarea.obj \ + $(SLO)$/aqua11ywrapperscrollbar.obj \ + $(SLO)$/aqua11ywrappersplitter.obj \ + $(SLO)$/aqua11ywrapperstatictext.obj \ + $(SLO)$/aqua11ywrappertabgroup.obj \ + $(SLO)$/aqua11ywrappertextarea.obj \ + $(SLO)$/aqua11ywrappertoolbar.obj \ + $(SLO)$/documentfocuslistener.obj + +.ENDIF # "$(GUIBASE)"!="aqua" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.INCLUDE : $(PRJ)$/util$/target.pmk diff --git a/vcl/aqua/source/a11y/readme.txt b/vcl/aqua/source/a11y/readme.txt new file mode 100644 index 000000000000..19e80ab1a162 --- /dev/null +++ b/vcl/aqua/source/a11y/readme.txt @@ -0,0 +1,8 @@ +Naming scheme: + +aqua11yXYZhelper: Helper class providing static methods + +aqua11yXYZwrapper: Wrapper around one (or two) UNO-interfaces + +aqua11ywrapperXYZ: Subclass of aqua11ywrapper for a specific AXRole + diff --git a/vcl/aqua/source/app/makefile.mk b/vcl/aqua/source/app/makefile.mk new file mode 100644 index 000000000000..a0ddcbc02226 --- /dev/null +++ b/vcl/aqua/source/app/makefile.mk @@ -0,0 +1,63 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# 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 +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=vcl +TARGET=salapp +.INCLUDE : $(PRJ)$/util$/makefile.pmk +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile2.pmk + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"!="aqua" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"!="aqua" + +SLOFILES= $(SLO)$/salinst.obj \ + $(SLO)$/saldata.obj \ + $(SLO)$/vclnsapp.obj \ + $(SLO)$/saltimer.obj \ + $(SLO)$/salnstimer.obj \ + $(SLO)$/salsys.obj + +.ENDIF # "$(GUIBASE)"!="aqua" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.INCLUDE : $(PRJ)$/util$/target.pmk + diff --git a/vcl/aqua/source/app/saldata.cxx b/vcl/aqua/source/app/saldata.cxx new file mode 100644 index 000000000000..3cb878636ad3 --- /dev/null +++ b/vcl/aqua/source/app/saldata.cxx @@ -0,0 +1,293 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "saldata.hxx" +#include "salnsmenu.h" +#include "salinst.h" +#import "apple_remote/RemoteMainController.h" + +oslThreadKey SalData::s_aAutoReleaseKey = 0; + +static void SAL_CALL releasePool( void* pPool ) +{ + if( pPool ) + [(NSAutoreleasePool*)pPool release]; +} + +SalData::SalData() +: + mpTimerProc( NULL ), + mpFirstInstance( NULL ), + mpFirstObject( NULL ), + mpFirstVD( NULL ), + mpFirstPrinter( NULL ), + mpFontList( NULL ), + mpStatusItem( nil ), + mxRGBSpace( CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB) ), + mxGraySpace( CGColorSpaceCreateWithName(kCGColorSpaceGenericGray) ), + mxP50Space( NULL ), + mxP50Pattern( NULL ), + maCursors( POINTER_COUNT, INVALID_CURSOR_PTR ), + mbIsScrollbarDoubleMax( false ), + mnSystemVersion( VER_TIGER ), + mpMainController( NULL ), + mpDockIconClickHandler( nil ), + mnDPIX( 0 ), + mnDPIY( 0 ) +{ + if( s_aAutoReleaseKey == 0 ) + s_aAutoReleaseKey = osl_createThreadKey( releasePool ); +} + +SalData::~SalData() +{ + CGPatternRelease( mxP50Pattern ); + CGColorSpaceRelease( mxP50Space ); + CGColorSpaceRelease( mxRGBSpace ); + CGColorSpaceRelease( mxGraySpace ); + for( unsigned int i = 0; i < maCursors.size(); i++ ) + { + NSCursor* pCurs = maCursors[i]; + if( pCurs && pCurs != INVALID_CURSOR_PTR ) + [pCurs release]; + } + if( s_aAutoReleaseKey ) + { + // release the last pool + NSAutoreleasePool* pPool = nil; + pPool = reinterpret_cast<NSAutoreleasePool*>( osl_getThreadKeyData( s_aAutoReleaseKey ) ); + if( pPool ) + { + osl_setThreadKeyData( s_aAutoReleaseKey, NULL ); + [pPool release]; + } + + osl_destroyThreadKey( s_aAutoReleaseKey ); + s_aAutoReleaseKey = 0; + } + if ( mpMainController ) + [mpMainController release]; +} + +void SalData::ensureThreadAutoreleasePool() +{ + NSAutoreleasePool* pPool = nil; + if( s_aAutoReleaseKey ) + { + pPool = reinterpret_cast<NSAutoreleasePool*>( osl_getThreadKeyData( s_aAutoReleaseKey ) ); + if( ! pPool ) + { + pPool = [[NSAutoreleasePool alloc] init]; + osl_setThreadKeyData( s_aAutoReleaseKey, pPool ); + } + } + else + { + DBG_ERROR( "no autorelease key" ); + } +} + +void SalData::drainThreadAutoreleasePool() +{ + NSAutoreleasePool* pPool = nil; + if( s_aAutoReleaseKey ) + { + pPool = reinterpret_cast<NSAutoreleasePool*>( osl_getThreadKeyData( s_aAutoReleaseKey ) ); + if( pPool ) + { + // osl_setThreadKeyData( s_aAutoReleaseKey, NULL ); + // [pPool release]; + [pPool drain]; + } + else + { + pPool = [[NSAutoreleasePool alloc] init]; + osl_setThreadKeyData( s_aAutoReleaseKey, pPool ); + } + } + else + { + DBG_ERROR( "no autorelease key" ); + } +} + + +struct curs_ent +{ + const char* pBaseName; + const NSPoint aHotSpot; +} +const aCursorTab[ POINTER_COUNT ] = +{ +{ NULL, { 0, 0 } }, //POINTER_ARROW +{ "nullptr", { 16, 16 } }, //POINTER_NULL +{ "hourglass", { 15, 15 } }, //POINTER_WAIT +{ NULL, { 0, 0 } }, //POINTER_TEXT +{ "help", { 0, 0 } }, //POINTER_HELP +{ NULL, { 0, 0 } }, //POINTER_CROSS +{ NULL, { 0, 0 } }, //POINTER_MOVE +{ NULL, { 0, 0 } }, //POINTER_NSIZE +{ NULL, { 0, 0 } }, //POINTER_SSIZE +{ NULL, { 0, 0 } }, //POINTER_WSIZE +{ NULL, { 0, 0 } }, //POINTER_ESIZE +{ "nwsesize", { 15, 15 } }, //POINTER_NWSIZE +{ "neswsize", { 15, 15 } }, //POINTER_NESIZE +{ "neswsize", { 15, 15 } }, //POINTER_SWSIZE +{ "nwsesize", { 15, 15 } }, //POINTER_SESIZE +{ NULL, { 0, 0 } }, //POINTER_WINDOW_NSIZE +{ NULL, { 0, 0 } }, //POINTER_WINDOW_SSIZE +{ NULL, { 0, 0 } }, //POINTER_WINDOW_WSIZE +{ NULL, { 0, 0 } }, //POINTER_WINDOW_ESIZE +{ "nwsesize", { 15, 15 } }, //POINTER_WINDOW_NWSIZE +{ "neswsize", { 15, 15 } }, //POINTER_WINDOW_NESIZE +{ "neswsize", { 15, 15 } }, //POINTER_WINDOW_SWSIZE +{ "nwsesize", { 15, 15 } }, //POINTER_WINDOW_SESIZE +{ NULL, { 0, 0 } }, //POINTER_HSPLIT +{ NULL, { 0, 0 } }, //POINTER_VSPLIT +{ NULL, { 0, 0 } }, //POINTER_HSIZEBAR +{ NULL, { 0, 0 } }, //POINTER_VSIZEBAR +{ NULL, { 0, 0 } }, //POINTER_HAND +{ NULL, { 0, 0 } }, //POINTER_REFHAND +{ "pen", { 3, 27 } }, //POINTER_PEN +{ "magnify", { 12, 13 } }, //POINTER_MAGNIFY +{ "fill", { 10, 22 } }, //POINTER_FILL +{ "rotate", { 15, 15 } }, //POINTER_ROTATE +{ "hshear", { 15, 15 } }, //POINTER_HSHEAR +{ "vshear", { 15, 15 } }, //POINTER_VSHEAR +{ "mirror", { 14, 12 } }, //POINTER_MIRROR +{ "crook", { 15, 14 } }, //POINTER_CROOK +{ "crop", { 9, 9 } }, //POINTER_CROP +{ "movept", { 0, 0 } }, //POINTER_MOVEPOINT +{ "movebw", { 0, 0 } }, //POINTER_MOVEBEZIERWEIGHT +{ "movedata", { 0, 0 } }, //POINTER_MOVEDATA +{ "copydata", { 0, 0 } }, //POINTER_COPYDATA +{ "linkdata", { 0, 0 } }, //POINTER_LINKDATA +{ "movedlnk", { 0, 0 } }, //POINTER_MOVEDATALINK +{ "copydlnk", { 0, 0 } }, //POINTER_COPYDATALINK +{ "movef", { 8, 8 } }, //POINTER_MOVEFILE +{ "copyf", { 8, 8 } }, //POINTER_COPYFILE +{ "linkf", { 8, 8 } }, //POINTER_LINKFILE +{ "moveflnk", { 8, 8 } }, //POINTER_MOVEFILELINK +{ "copyflnk", { 8, 8 } }, //POINTER_COPYFILELINK +{ "movef2", { 7, 8 } }, //POINTER_MOVEFILES +{ "copyf2", { 7, 8 } }, //POINTER_COPYFILES +{ "notallow", { 15, 15 } }, //POINTER_NOTALLOWED +{ "dline", { 8, 8 } }, //POINTER_DRAW_LINE +{ "drect", { 8, 8 } }, //POINTER_DRAW_RECT +{ "dpolygon", { 8, 8 } }, //POINTER_DRAW_POLYGON +{ "dbezier", { 8, 8 } }, //POINTER_DRAW_BEZIER +{ "darc", { 8, 8 } }, //POINTER_DRAW_ARC +{ "dpie", { 8, 8 } }, //POINTER_DRAW_PIE +{ "dcirccut", { 8, 8 } }, //POINTER_DRAW_CIRCLECUT +{ "dellipse", { 8, 8 } }, //POINTER_DRAW_ELLIPSE +{ "dfree", { 8, 8 } }, //POINTER_DRAW_FREEHAND +{ "dconnect", { 8, 8 } }, //POINTER_DRAW_CONNECT +{ "dtext", { 8, 8 } }, //POINTER_DRAW_TEXT +{ "dcapt", { 8, 8 } }, //POINTER_DRAW_CAPTION +{ "chart", { 15, 16 } }, //POINTER_CHART +{ "detectiv", { 12, 13 } }, //POINTER_DETECTIVE +{ "pivotcol", { 7, 5 } }, //POINTER_PIVOT_COL +{ "pivotrow", { 8, 7 } }, //POINTER_PIVOT_ROW +{ "pivotfld", { 8, 7 } }, //POINTER_PIVOT_FIELD +{ "chain", { 0, 2 } }, //POINTER_CHAIN +{ "chainnot", { 2, 2 } }, //POINTER_CHAIN_NOTALLOWED +{ "timemove", { 16, 16 } }, //POINTER_TIMEEVENT_MOVE +{ "timesize", { 16, 17 } }, //POINTER_TIMEEVENT_SIZE +{ "asn", { 16, 12 } }, //POINTER_AUTOSCROLL_N +{ "ass", { 15, 19 } }, //POINTER_AUTOSCROLL_S +{ "asw", { 12, 15 } }, //POINTER_AUTOSCROLL_W +{ "ase", { 19, 16 } }, //POINTER_AUTOSCROLL_E +{ "asnw", { 10, 10 } }, //POINTER_AUTOSCROLL_NW +{ "asne", { 21, 10 } }, //POINTER_AUTOSCROLL_NE +{ "assw", { 21, 21 } }, //POINTER_AUTOSCROLL_SW +{ "asse", { 21, 21 } }, //POINTER_AUTOSCROLL_SE +{ "asns", { 15, 15 } }, //POINTER_AUTOSCROLL_NS +{ "aswe", { 15, 15 } }, //POINTER_AUTOSCROLL_WE +{ "asnswe", { 15, 15 } }, //POINTER_AUTOSCROLL_NSWE +{ "airbrush", { 5, 22 } }, //POINTER_AIRBRUSH +{ "vtext", { 15, 15 } }, //POINTER_TEXT_VERTICAL +{ "pivotdel", { 18, 15 } }, //POINTER_PIVOT_DELETE +{ "tblsels", { 15, 30 } }, //POINTER_TAB_SELECT_S +{ "tblsele", { 30, 16 } }, //POINTER_TAB_SELECT_E +{ "tblselse", { 30, 30 } }, //POINTER_TAB_SELECT_SE +{ "tblselw", { 1, 16 } }, //POINTER_TAB_SELECT_W +{ "tblselsw", { 1, 30 } }, //POINTER_TAB_SELECT_SW +{ "pntbrsh", { 9, 16 } } //POINTER_PAINTBRUSH +}; + +NSCursor* SalData::getCursor( PointerStyle i_eStyle ) +{ + if( i_eStyle >= POINTER_COUNT ) + return nil; + + NSCursor* pCurs = maCursors[ i_eStyle ]; + if( pCurs == INVALID_CURSOR_PTR ) + { + pCurs = nil; + if( aCursorTab[ i_eStyle ].pBaseName ) + { + NSPoint aHotSpot = aCursorTab[ i_eStyle ].aHotSpot; + CFStringRef pCursorName = + CFStringCreateWithCStringNoCopy( + kCFAllocatorDefault, + aCursorTab[ i_eStyle ].pBaseName, + kCFStringEncodingASCII, + kCFAllocatorNull ); + CFBundleRef hMain = CFBundleGetMainBundle(); + CFURLRef hURL = CFBundleCopyResourceURL( hMain, pCursorName, CFSTR("png"), CFSTR("cursors") ); + if( hURL ) + { + pCurs = [[NSCursor alloc] initWithImage: [[NSImage alloc] initWithContentsOfURL: (NSURL*)hURL] hotSpot: aHotSpot]; + CFRelease( hURL ); + } + CFRelease( pCursorName ); + } + maCursors[ i_eStyle ] = pCurs; + } + return pCurs; +} + +NSStatusItem* SalData::getStatusItem() +{ + SalData* pData = GetSalData(); + if( ! pData->mpStatusItem ) + { + NSStatusBar* pStatBar =[NSStatusBar systemStatusBar]; + if( pStatBar ) + { + pData->mpStatusItem = [pStatBar statusItemWithLength: NSVariableStatusItemLength]; + [pData->mpStatusItem retain]; + OOStatusItemView* pView = [[OOStatusItemView alloc] init]; + [pData->mpStatusItem setView: pView ]; + [pView display]; + } + } + return pData->mpStatusItem; +} diff --git a/vcl/aqua/source/app/salinst.cxx b/vcl/aqua/source/app/salinst.cxx new file mode 100644 index 000000000000..b8a2261ed9db --- /dev/null +++ b/vcl/aqua/source/app/salinst.cxx @@ -0,0 +1,1313 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> + +#include "tools/fsys.hxx" +#include "tools/getprocessworkingdir.hxx" +#include "osl/process.h" +#include "rtl/ustrbuf.hxx" +#include "vcl/svapp.hxx" +#include "vcl/print.h" +#include "vcl/salimestatus.hxx" +#include "vcl/window.hxx" +#include "vcl/timer.hxx" +#include "vcl/impbmp.hxx" + +#include "saldata.hxx" +#include "salinst.h" +#include "salframe.h" +#include "salobj.h" +#include "salsys.h" +#include "salvd.h" +#include "salbmp.h" +#include "salprn.h" +#include "saltimer.h" +#include "vclnsapp.h" + +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp> +#include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include "premac.h" +#include <Foundation/Foundation.h> +#include <ApplicationServices/ApplicationServices.h> +#import "apple_remote/RemoteMainController.h" +#include "apple_remote/RemoteControl.h" +#include "postmac.h" +#include <tools/solarmutex.hxx> + +using namespace std; +using namespace ::com::sun::star; + +extern BOOL ImplSVMain(); + +static BOOL* gpbInit = 0; +static NSMenu* pDockMenu = nil; +static bool bNoSVMain = true; +static bool bLeftMain = false; +// ----------------------------------------------------------------------- + +class AquaDelayedSettingsChanged : public Timer +{ + bool mbInvalidate; + public: + AquaDelayedSettingsChanged( bool bInvalidate ) : + mbInvalidate( bInvalidate ) + { + } + + virtual void Timeout() + { + SalData* pSalData = GetSalData(); + if( ! pSalData->maFrames.empty() ) + pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL ); + + if( mbInvalidate ) + { + for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin(); + it != pSalData->maFrames.end(); ++it ) + { + if( (*it)->mbShown ) + (*it)->SendPaintEvent( NULL ); + } + } + Stop(); + delete this; + } +}; + +void AquaSalInstance::delayedSettingsChanged( bool bInvalidate ) +{ + vos::OGuard aGuard( *mpSalYieldMutex ); + AquaDelayedSettingsChanged* pTimer = new AquaDelayedSettingsChanged( bInvalidate ); + pTimer->SetTimeout( 50 ); + pTimer->Start(); +} + + +// the AppEventList must be available before any SalData/SalInst/etc. objects are ready +typedef std::list<const ApplicationEvent*> AppEventList; +AppEventList AquaSalInstance::aAppEventList; + +NSMenu* AquaSalInstance::GetDynamicDockMenu() +{ + if( ! pDockMenu && ! bLeftMain ) + pDockMenu = [[NSMenu alloc] initWithTitle: @""]; + return pDockMenu; +} + +bool AquaSalInstance::isOnCommandLine( const rtl::OUString& rArg ) +{ + sal_uInt32 nArgs = osl_getCommandArgCount(); + for( sal_uInt32 i = 0; i < nArgs; i++ ) + { + rtl::OUString aArg; + osl_getCommandArg( i, &aArg.pData ); + if( aArg.equals( rArg ) ) + return true; + } + return false; +} + + +// initialize the cocoa VCL_NSApplication object +// returns an NSAutoreleasePool that must be released when the event loop begins +static void initNSApp() +{ + // create our cocoa NSApplication + [VCL_NSApplication sharedApplication]; + + SalData::ensureThreadAutoreleasePool(); + + // put cocoa into multithreaded mode + [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil]; + + // activate our delegate methods + [NSApp setDelegate: NSApp]; + + [[NSNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(systemColorsChanged:) + name: NSSystemColorsDidChangeNotification + object: nil ]; + [[NSNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(screenParametersChanged:) + name: NSApplicationDidChangeScreenParametersNotification + object: nil ]; + // add observers for some settings changes that affect vcl's settings + // scrollbar variant + [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(scrollbarVariantChanged:) + name: @"AppleAquaScrollBarVariantChanged" + object: nil ]; + // scrollbar page behavior ("jump to here" or not) + [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(scrollbarSettingsChanged:) + name: @"AppleNoRedisplayAppearancePreferenceChanged" + object: nil ]; + + // get System Version and store the value in GetSalData()->mnSystemVersion + OSErr err = noErr; + SInt32 systemVersion = VER_TIGER; // Initialize with minimal requirement + if( (err = Gestalt(gestaltSystemVersion, &systemVersion)) == noErr ) + { + GetSalData()->mnSystemVersion = systemVersion; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "System Version %x\n", (unsigned int)systemVersion); +#endif + } + else + NSLog(@"Unable to obtain system version: %ld", (long)err); + + // Initialize Apple Remote + GetSalData()->mpMainController = [[MainController alloc] init]; + + [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(applicationWillBecomeActive:) + name: @"AppleRemoteWillBecomeActive" + object: nil ]; + + [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp + selector: @selector(applicationWillResignActive:) + name: @"AppleRemoteWillResignActive" + object: nil ]; + + if( ImplGetSVData()->mbIsTestTool ) + [NSApp activateIgnoringOtherApps: YES]; +} + +BOOL ImplSVMainHook( BOOL * pbInit ) +{ + gpbInit = pbInit; + + bNoSVMain = false; + initNSApp(); + + NSPoint aPt = { 0, 0 }; + NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined + location: aPt + modifierFlags: 0 + timestamp: 0 + windowNumber: 0 + context: nil + subtype: AquaSalInstance::AppExecuteSVMain + data1: 0 + data2: 0 ]; + if( pEvent ) + { + [NSApp postEvent: pEvent atStart: NO]; + + rtl::OUString aExeURL, aExe; + osl_getExecutableFile( &aExeURL.pData ); + osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData ); + rtl::OString aByteExe( rtl::OUStringToOString( aExe, osl_getThreadTextEncoding() ) ); + +#ifdef DEBUG + aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" ); + const char* pArgv[] = { aByteExe.getStr(), NULL }; + NSApplicationMain( 3, pArgv ); +#else + const char* pArgv[] = { aByteExe.getStr(), NULL }; + NSApplicationMain( 1, pArgv ); +#endif + } + else + { + DBG_ERROR( "NSApplication initialization could not be done" ); + } + + return TRUE; // indicate that ImplSVMainHook is implemented +} + +// ======================================================================= + +void SalAbort( const XubString& rErrorText ) +{ + if( !rErrorText.Len() ) + fprintf( stderr, "Application Error " ); + else + fprintf( stderr, "%s ", + ByteString( rErrorText, gsl_getSystemTextEncoding() ).GetBuffer() ); + abort(); +} + +// ----------------------------------------------------------------------- + +void InitSalData() +{ + SalData *pSalData = new SalData; + SetSalData( pSalData ); +} + +// ----------------------------------------------------------------------- + +const ::rtl::OUString& SalGetDesktopEnvironment() +{ + static OUString aDesktopEnvironment(RTL_CONSTASCII_USTRINGPARAM( "MacOSX" )); + return aDesktopEnvironment; +} + +// ----------------------------------------------------------------------- + +void DeInitSalData() +{ + SalData *pSalData = GetSalData(); + if( pSalData->mpStatusItem ) + { + [pSalData->mpStatusItem release]; + pSalData->mpStatusItem = nil; + } + delete pSalData; + SetSalData( NULL ); +} + +// ----------------------------------------------------------------------- + +extern "C" { +#include <crt_externs.h> +} + +// ----------------------------------------------------------------------- + +void InitSalMain() +{ + rtl::OUString urlWorkDir; + rtl_uString *sysWorkDir = NULL; + if (tools::getProcessWorkingDir(&urlWorkDir)) + { + oslFileError err2 = osl_getSystemPathFromFileURL(urlWorkDir.pData, &sysWorkDir); + if (err2 == osl_File_E_None) + { + ByteString aPath( getenv( "PATH" ) ); + ByteString aResPath( getenv( "STAR_RESOURCEPATH" ) ); + ByteString aLibPath( getenv( "DYLD_LIBRARY_PATH" ) ); + ByteString aCmdPath( OUStringToOString(OUString(sysWorkDir), RTL_TEXTENCODING_UTF8).getStr() ); + ByteString aTmpPath; + // Get absolute path of command's directory + if ( aCmdPath.Len() ) { + DirEntry aCmdDirEntry( aCmdPath ); + aCmdDirEntry.ToAbs(); + aCmdPath = ByteString( aCmdDirEntry.GetPath().GetFull(), RTL_TEXTENCODING_ASCII_US ); + } + // Assign to PATH environment variable + if ( aCmdPath.Len() ) + { + aTmpPath = ByteString( "PATH=" ); + aTmpPath += aCmdPath; + if ( aPath.Len() ) + aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); + aTmpPath += aPath; + putenv( (char*)aTmpPath.GetBuffer() ); + } + // Assign to STAR_RESOURCEPATH environment variable + if ( aCmdPath.Len() ) + { + aTmpPath = ByteString( "STAR_RESOURCEPATH=" ); + aTmpPath += aCmdPath; + if ( aResPath.Len() ) + aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); + aTmpPath += aResPath; + putenv( (char*)aTmpPath.GetBuffer() ); + } + // Assign to DYLD_LIBRARY_PATH environment variable + if ( aCmdPath.Len() ) + { + aTmpPath = ByteString( "DYLD_LIBRARY_PATH=" ); + aTmpPath += aCmdPath; + if ( aLibPath.Len() ) + aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); + aTmpPath += aLibPath; + putenv( (char*)aTmpPath.GetBuffer() ); + } + } + } +} + +// ----------------------------------------------------------------------- + +void DeInitSalMain() +{ +} + +// ======================================================================= + +SalYieldMutex::SalYieldMutex() +{ + mnCount = 0; + mnThreadId = 0; +} + +void SalYieldMutex::acquire() +{ + OMutex::acquire(); + mnThreadId = NAMESPACE_VOS(OThread)::getCurrentIdentifier(); + mnCount++; +} + +void SalYieldMutex::release() +{ + if ( mnThreadId == NAMESPACE_VOS(OThread)::getCurrentIdentifier() ) + { + if ( mnCount == 1 ) + mnThreadId = 0; + mnCount--; + } + OMutex::release(); +} + +sal_Bool SalYieldMutex::tryToAcquire() +{ + if ( OMutex::tryToAcquire() ) + { + mnThreadId = NAMESPACE_VOS(OThread)::getCurrentIdentifier(); + mnCount++; + return sal_True; + } + else + return sal_False; +} + +// ----------------------------------------------------------------------- + +// some convenience functions regarding the yield mutex, aka solar mutex + +BOOL ImplSalYieldMutexTryToAcquire() +{ + AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; + if ( pInst ) + return pInst->mpSalYieldMutex->tryToAcquire(); + else + return FALSE; +} + +void ImplSalYieldMutexAcquire() +{ + AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; + if ( pInst ) + pInst->mpSalYieldMutex->acquire(); +} + +void ImplSalYieldMutexRelease() +{ + AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; + if ( pInst ) + pInst->mpSalYieldMutex->release(); +} + +// ======================================================================= + +SalInstance* CreateSalInstance() +{ + // this is the case for not using SVMain + // not so good + if( bNoSVMain ) + initNSApp(); + + SalData* pSalData = GetSalData(); + DBG_ASSERT( pSalData->mpFirstInstance == NULL, "more than one instance created" ); + AquaSalInstance* pInst = new AquaSalInstance; + + // init instance (only one instance in this version !!!) + pSalData->mpFirstInstance = pInst; + // this one is for outside AquaSalInstance::Yield + SalData::ensureThreadAutoreleasePool(); + // no focus rects on NWF aqua + ImplGetSVData()->maNWFData.mbNoFocusRects = true; + ImplGetSVData()->maNWFData.mbNoBoldTabFocus = true; + ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true; + ImplGetSVData()->maNWFData.mbCenteredTabs = true; + ImplGetSVData()->maNWFData.mbProgressNeedsErase = true; + ImplGetSVData()->maNWFData.mbCheckBoxNeedsErase = true; + ImplGetSVData()->maGDIData.mbPrinterPullModel = true; + ImplGetSVData()->maGDIData.mbNoXORClipping = true; + ImplGetSVData()->maWinData.mbNoSaveBackground = true; + + return pInst; +} + +// ----------------------------------------------------------------------- + +void DestroySalInstance( SalInstance* pInst ) +{ + delete pInst; +} + +// ----------------------------------------------------------------------- + +AquaSalInstance::AquaSalInstance() +{ + mpSalYieldMutex = new SalYieldMutex; + mpSalYieldMutex->acquire(); + ::tools::SolarMutex::SetSolarMutex( mpSalYieldMutex ); + maMainThread = vos::OThread::getCurrentIdentifier(); + mbWaitingYield = false; + maUserEventListMutex = osl_createMutex(); + mnActivePrintJobs = 0; + maWaitingYieldCond = osl_createCondition(); +} + +// ----------------------------------------------------------------------- + +AquaSalInstance::~AquaSalInstance() +{ + ::tools::SolarMutex::SetSolarMutex( 0 ); + mpSalYieldMutex->release(); + delete mpSalYieldMutex; + osl_destroyMutex( maUserEventListMutex ); + osl_destroyCondition( maWaitingYieldCond ); +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::wakeupYield() +{ + // wakeup :Yield + if( mbWaitingYield ) + { + SalData::ensureThreadAutoreleasePool(); + NSPoint aPt = { 0, 0 }; + NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined + location: aPt + modifierFlags: 0 + timestamp: 0 + windowNumber: 0 + context: nil + subtype: AquaSalInstance::YieldWakeupEvent + data1: 0 + data2: 0 ]; + if( pEvent ) + [NSApp postEvent: pEvent atStart: NO]; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::PostUserEvent( AquaSalFrame* pFrame, USHORT nType, void* pData ) +{ + osl_acquireMutex( maUserEventListMutex ); + maUserEvents.push_back( SalUserEvent( pFrame, pData, nType ) ); + osl_releaseMutex( maUserEventListMutex ); + + // notify main loop that an event has arrived + wakeupYield(); +} + +// ----------------------------------------------------------------------- + +vos::IMutex* AquaSalInstance::GetYieldMutex() +{ + return mpSalYieldMutex; +} + +// ----------------------------------------------------------------------- + +ULONG AquaSalInstance::ReleaseYieldMutex() +{ + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + if ( pYieldMutex->GetThreadId() == + NAMESPACE_VOS(OThread)::getCurrentIdentifier() ) + { + ULONG nCount = pYieldMutex->GetAcquireCount(); + ULONG n = nCount; + while ( n ) + { + pYieldMutex->release(); + n--; + } + + return nCount; + } + else + return 0; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::AcquireYieldMutex( ULONG nCount ) +{ + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + while ( nCount ) + { + pYieldMutex->acquire(); + nCount--; + } +} + +// ----------------------------------------------------------------------- + +bool AquaSalInstance::isNSAppThread() const +{ + return vos::OThread::getCurrentIdentifier() == maMainThread; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent ) +{ + switch( [pEvent subtype] ) + { + case AppStartTimerEvent: + AquaSalTimer::handleStartTimerEvent( pEvent ); + break; + case AppEndLoopEvent: + [NSApp stop: NSApp]; + break; + case AppExecuteSVMain: + { + BOOL bResult = ImplSVMain(); + if( gpbInit ) + *gpbInit = bResult; + [NSApp stop: NSApp]; + bLeftMain = true; + if( pDockMenu ) + { + [pDockMenu release]; + pDockMenu = nil; + } + } + break; + case AppleRemoteEvent: + { + sal_Int16 nCommand = 0; + SalData* pSalData = GetSalData(); + bool bIsFullScreenMode = false; + + std::list<AquaSalFrame*>::iterator it = pSalData->maFrames.begin(); + while( (*it) && ( (it != pSalData->maFrames.end() ) || ( (*it)->mbFullScreen == false ) ) ) + { + if ( ((*it)->mbFullScreen == true) ) + bIsFullScreenMode = true; + it++; + } + + switch ([pEvent data1]) + { + case kRemoteButtonPlay: + nCommand = ( bIsFullScreenMode == true ) ? MEDIA_COMMAND_PLAY_PAUSE : MEDIA_COMMAND_PLAY; + break; + + // kept for experimentation purpose (scheduled for future implementation) + // case kRemoteButtonMenu: nCommand = MEDIA_COMMAND_MENU; break; + + case kRemoteButtonPlus: nCommand = MEDIA_COMMAND_VOLUME_UP; break; + + case kRemoteButtonMinus: nCommand = MEDIA_COMMAND_VOLUME_DOWN; break; + + case kRemoteButtonRight: nCommand = MEDIA_COMMAND_NEXTTRACK; break; + + case kRemoteButtonRight_Hold: nCommand = MEDIA_COMMAND_NEXTTRACK_HOLD; break; + + case kRemoteButtonLeft: nCommand = MEDIA_COMMAND_PREVIOUSTRACK; break; + + case kRemoteButtonLeft_Hold: nCommand = MEDIA_COMMAND_REWIND; break; + + case kRemoteButtonPlay_Hold: nCommand = MEDIA_COMMAND_PLAY_HOLD; break; + + case kRemoteButtonMenu_Hold: nCommand = MEDIA_COMMAND_STOP; break; + + // FIXME : not detected + case kRemoteButtonPlus_Hold: + case kRemoteButtonMinus_Hold: + break; + + default: + break; + } + AquaSalFrame* pFrame = pSalData->maFrames.front(); + Window * pWindow = pFrame->GetWindow() ? pSalData->maFrames.front()->GetWindow() : NULL; + + if( pWindow ) + { + const Point aPoint; + CommandEvent aCEvt( aPoint, COMMAND_MEDIA, FALSE, &nCommand ); + NotifyEvent aNCmdEvt( EVENT_COMMAND, pWindow, &aCEvt ); + + if ( !ImplCallPreNotify( aNCmdEvt ) ) + pWindow->Command( aCEvt ); + } + + } + break; + + case YieldWakeupEvent: + // do nothing, fall out of Yield + break; + + default: + DBG_ERROR( "unhandled NSApplicationDefined event" ); + break; + }; +} + +// ----------------------------------------------------------------------- + +class ReleasePoolHolder +{ + NSAutoreleasePool* mpPool; + public: + ReleasePoolHolder() : mpPool( [[NSAutoreleasePool alloc] init] ) {} + ~ReleasePoolHolder() { [mpPool release]; } +}; + +void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents ) +{ + // ensure that the per thread autorelease pool is top level and + // will therefore not be destroyed by cocoa implicitly + SalData::ensureThreadAutoreleasePool(); + + // NSAutoreleasePool documentation suggests we should have + // an own pool for each yield level + ReleasePoolHolder aReleasePool; + + // Release all locks so that we don't deadlock when we pull pending + // events from the event queue + bool bDispatchUser = true; + while( bDispatchUser ) + { + ULONG nCount = ReleaseYieldMutex(); + + // get one user event + osl_acquireMutex( maUserEventListMutex ); + SalUserEvent aEvent( NULL, NULL, 0 ); + if( ! maUserEvents.empty() ) + { + aEvent = maUserEvents.front(); + maUserEvents.pop_front(); + } + else + bDispatchUser = false; + osl_releaseMutex( maUserEventListMutex ); + + AcquireYieldMutex( nCount ); + + // dispatch it + if( aEvent.mpFrame && AquaSalFrame::isAlive( aEvent.mpFrame ) ) + { + aEvent.mpFrame->CallCallback( aEvent.mnType, aEvent.mpData ); + osl_setCondition( maWaitingYieldCond ); + // return if only one event is asked for + if( ! bHandleAllCurrentEvents ) + return; + } + } + + // handle cocoa event queue + // cocoa events mye be only handled in the thread the NSApp was created + if( isNSAppThread() && mnActivePrintJobs == 0 ) + { + // we need to be woken up by a cocoa-event + // if a user event should be posted by the event handling below + bool bOldWaitingYield = mbWaitingYield; + mbWaitingYield = bWait; + + // handle available events + NSEvent* pEvent = nil; + bool bHadEvent = false; + do + { + ULONG nCount = ReleaseYieldMutex(); + + pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil + inMode: NSDefaultRunLoopMode dequeue: YES]; + if( pEvent ) + { + [NSApp sendEvent: pEvent]; + bHadEvent = true; + } + [NSApp updateWindows]; + + AcquireYieldMutex( nCount ); + } while( bHandleAllCurrentEvents && pEvent ); + + // if we had no event yet, wait for one if requested + if( bWait && ! bHadEvent ) + { + ULONG nCount = ReleaseYieldMutex(); + + NSDate* pDt = AquaSalTimer::pRunningTimer ? [AquaSalTimer::pRunningTimer fireDate] : [NSDate distantFuture]; + pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: pDt + inMode: NSDefaultRunLoopMode dequeue: YES]; + if( pEvent ) + [NSApp sendEvent: pEvent]; + [NSApp updateWindows]; + + AcquireYieldMutex( nCount ); + + // #i86581# + // FIXME: sometimes the NSTimer will never fire. Firing it by hand then + // fixes the problem even seems to set the correct next firing date + // Why oh why ? + if( ! pEvent && AquaSalTimer::pRunningTimer ) + { + // this cause crashes on MacOSX 10.4 + // [AquaSalTimer::pRunningTimer fire]; + ImplGetSVData()->mpSalTimer->CallCallback(); + } + } + + mbWaitingYield = bOldWaitingYield; + + // collect update rectangles + const std::list< AquaSalFrame* > rFrames( GetSalData()->maFrames ); + for( std::list< AquaSalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + if( (*it)->mbShown && ! (*it)->maInvalidRect.IsEmpty() ) + { + (*it)->Flush( (*it)->maInvalidRect ); + (*it)->maInvalidRect.SetEmpty(); + } + } + osl_setCondition( maWaitingYieldCond ); + } + else if( bWait ) + { + // #i103162# + // wait until any thread (most likely the main thread) + // has dispatched an event, cop out at 200 ms + osl_resetCondition( maWaitingYieldCond ); + TimeValue aVal = { 0, 200000000 }; + ULONG nCount = ReleaseYieldMutex(); + osl_waitCondition( maWaitingYieldCond, &aVal ); + AcquireYieldMutex( nCount ); + } + + // we get some apple events way too early + // before the application is ready to handle them, + // so their corresponding application events need to be delayed + // now is a good time to handle at least one of them + if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute ) + { + // make sure that only one application event is active at a time + static bool bInAppEvent = false; + if( !bInAppEvent ) + { + bInAppEvent = true; + // get the next delayed application event + const ApplicationEvent* pAppEvent = aAppEventList.front(); + aAppEventList.pop_front(); + // handle one application event (no recursion) + const ImplSVData* pSVData = ImplGetSVData(); + pSVData->mpApp->AppEvent( *pAppEvent ); + delete pAppEvent; + // allow the next delayed application event + bInAppEvent = false; + } + } +} + +// ----------------------------------------------------------------------- + +bool AquaSalInstance::AnyInput( USHORT nType ) +{ + if( nType & INPUT_APPEVENT ) + { + if( ! aAppEventList.empty() ) + return true; + if( nType == INPUT_APPEVENT ) + return false; + } + + if( nType & INPUT_TIMER ) + { + if( AquaSalTimer::pRunningTimer ) + { + NSDate* pDt = [AquaSalTimer::pRunningTimer fireDate]; + if( pDt && [pDt timeIntervalSinceNow] < 0 ) + { + return true; + } + } + } + + unsigned/*NSUInteger*/ nEventMask = 0; + if( nType & INPUT_MOUSE) + nEventMask |= + NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask | + NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask | + NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask | + NSScrollWheelMask | + // NSMouseMovedMask | + NSMouseEnteredMask | NSMouseExitedMask; + if( nType & INPUT_KEYBOARD) + nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask; + if( nType & INPUT_OTHER) + nEventMask |= NSTabletPoint; + // TODO: INPUT_PAINT / more INPUT_OTHER + if( !nType) + return false; + + NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil + inMode: NSDefaultRunLoopMode dequeue: NO]; + return (pEvent != NULL); +} + +// ----------------------------------------------------------------------- + +SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData* pSystemParentData, ULONG nSalFrameStyle ) +{ + return NULL; +} + +// ----------------------------------------------------------------------- + +SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, ULONG nSalFrameStyle ) +{ + SalData::ensureThreadAutoreleasePool(); + + SalFrame* pFrame = new AquaSalFrame( pParent, nSalFrameStyle ); + return pFrame; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroyFrame( SalFrame* pFrame ) +{ + delete pFrame; +} + +// ----------------------------------------------------------------------- + +SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* /* pWindowData */, BOOL /* bShow */ ) +{ + // SystemWindowData is meaningless on Mac OS X + AquaSalObject *pObject = NULL; + + if ( pParent ) + pObject = new AquaSalObject( static_cast<AquaSalFrame*>(pParent) ); + + return pObject; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroyObject( SalObject* pObject ) +{ + delete ( pObject ); +} + +// ----------------------------------------------------------------------- + +SalPrinter* AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) +{ + return new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) ); +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroyPrinter( SalPrinter* pPrinter ) +{ + delete pPrinter; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList ) +{ + NSArray* pNames = [NSPrinter printerNames]; + NSArray* pTypes = [NSPrinter printerTypes]; + unsigned int nNameCount = pNames ? [pNames count] : 0; + unsigned int nTypeCount = pTypes ? [pTypes count] : 0; + DBG_ASSERT( nTypeCount == nNameCount, "type count not equal to printer count" ); + for( unsigned int i = 0; i < nNameCount; i++ ) + { + NSString* pName = [pNames objectAtIndex: i]; + NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil; + if( pName ) + { + SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo; + pInfo->maPrinterName = GetOUString( pName ); + if( pType ) + pInfo->maDriver = GetOUString( pType ); + pInfo->mnStatus = 0; + pInfo->mnJobs = 0; + pInfo->mpSysData = NULL; + + pList->Add( pInfo ); + } + } +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* pInfo ) +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo ) +{ + delete pInfo; +} + +// ----------------------------------------------------------------------- + +XubString AquaSalInstance::GetDefaultPrinter() +{ + if( ! maDefaultPrinter.getLength() ) + { + NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo]; + DBG_ASSERT( pPI, "no print info" ); + if( pPI ) + { + NSPrinter* pPr = [pPI printer]; + DBG_ASSERT( pPr, "no printer in default info" ); + if( pPr ) + { + NSString* pDefName = [pPr name]; + DBG_ASSERT( pDefName, "printer has no name" ); + maDefaultPrinter = GetOUString( pDefName ); + } + } + } + return maDefaultPrinter; +} + +// ----------------------------------------------------------------------- + +SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo, + ImplJobSetup* pSetupData ) +{ + SalInfoPrinter* pNewInfoPrinter = NULL; + if( pQueueInfo ) + { + pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo ); + if( pSetupData ) + pNewInfoPrinter->SetPrinterData( pSetupData ); + } + + return pNewInfoPrinter; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter ) +{ + delete pPrinter; +} + +// ----------------------------------------------------------------------- + +SalSystem* AquaSalInstance::CreateSystem() +{ + return new AquaSalSystem(); +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroySystem( SalSystem* pSystem ) +{ + delete pSystem; +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::SetEventCallback( void* pInstance, bool(*pCallback)(void*,void*,int) ) +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::SetErrorEventCallback( void* pInstance, bool(*pCallback)(void*,void*,int) ) +{ +} + +// ----------------------------------------------------------------------- + +void* AquaSalInstance::GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes ) +{ + rReturnedBytes = 1; + rReturnedType = AsciiCString; + return (void*)""; +} + +// We need to re-encode file urls because osl_getFileURLFromSystemPath converts +// to UTF-8 before encoding non ascii characters, which is not what other apps expect. +static rtl::OUString translateToExternalUrl(const rtl::OUString& internalUrl) +{ + rtl::OUString extUrl; + + uno::Reference< lang::XMultiServiceFactory > sm = comphelper::getProcessServiceFactory(); + if (sm.is()) + { + uno::Reference< beans::XPropertySet > pset; + sm->queryInterface( getCppuType( &pset )) >>= pset; + if (pset.is()) + { + uno::Reference< uno::XComponentContext > context; + static const rtl::OUString DEFAULT_CONTEXT( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ) ); + pset->getPropertyValue(DEFAULT_CONTEXT) >>= context; + if (context.is()) + extUrl = uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl); + } + } + return extUrl; +} + +// #i104525# many versions of OSX have problems with some URLs: +// when an app requests OSX to add one of these URLs to the "Recent Items" list +// then this app gets killed (TextEdit, Preview, etc. and also OOo) +static bool isDangerousUrl( const rtl::OUString& rUrl ) +{ + // use a heuristic that detects all known cases since there is no official comment + // on the exact impact and root cause of the OSX bug + const int nLen = rUrl.getLength(); + const sal_Unicode* p = rUrl.getStr(); + for( int i = 0; i < nLen-3; ++i, ++p ) { + if( p[0] != '%' ) + continue; + // escaped percent? + if( (p[1] == '2') && (p[2] == '5') ) + return true; + // escapes are considered to be UTF-8 encoded + // => check for invalid UTF-8 leading byte + if( (p[1] != 'f') && (p[1] != 'F') ) + continue; + int cLowNibble = p[2]; + if( (cLowNibble >= '0' ) && (cLowNibble <= '9')) + return false; + if( cLowNibble >= 'a' ) + cLowNibble -= 'a' - 'A'; + if( (cLowNibble < 'A') || (cLowNibble >= 'C')) + return true; + } + + return false; +} + +void AquaSalInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& /*rMimeType*/) +{ + // Convert file URL for external use (see above) + rtl::OUString externalUrl = translateToExternalUrl(rFileUrl); + if( 0 == externalUrl.getLength() ) + externalUrl = rFileUrl; + + if( externalUrl.getLength() && !isDangerousUrl( externalUrl ) ) + { + NSString* pString = CreateNSString( externalUrl ); + NSURL* pURL = [NSURL URLWithString: pString]; + + if( pURL ) + { + NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController]; + [pCtrl noteNewRecentDocumentURL: pURL]; + } + if( pString ) + [pString release]; + } +} + + +// ----------------------------------------------------------------------- + +SalTimer* AquaSalInstance::CreateSalTimer() +{ + return new AquaSalTimer(); +} + +// ----------------------------------------------------------------------- + +SalSystem* AquaSalInstance::CreateSalSystem() +{ + return new AquaSalSystem(); +} + +// ----------------------------------------------------------------------- + +SalBitmap* AquaSalInstance::CreateSalBitmap() +{ + return new AquaSalBitmap(); +} + +// ----------------------------------------------------------------------- + +SalSession* AquaSalInstance::CreateSalSession() +{ + return NULL; +} + +// ----------------------------------------------------------------------- + +class MacImeStatus : public SalI18NImeStatus +{ +public: + MacImeStatus() {} + virtual ~MacImeStatus() {} + + // asks whether there is a status window available + // to toggle into menubar + virtual bool canToggle() { return false; } + virtual void toggle() {} +}; + +// ----------------------------------------------------------------------- + +SalI18NImeStatus* AquaSalInstance::CreateI18NImeStatus() +{ + return new MacImeStatus(); +} + +// YieldMutexReleaser +YieldMutexReleaser::YieldMutexReleaser() : mnCount( 0 ) +{ + SalData* pSalData = GetSalData(); + if( ! pSalData->mpFirstInstance->isNSAppThread() ) + { + SalData::ensureThreadAutoreleasePool(); + mnCount = pSalData->mpFirstInstance->ReleaseYieldMutex(); + } +} + +YieldMutexReleaser::~YieldMutexReleaser() +{ + if( mnCount != 0 ) + GetSalData()->mpFirstInstance->AcquireYieldMutex( mnCount ); +} + +////////////////////////////////////////////////////////////// +rtl::OUString GetOUString( CFStringRef rStr ) +{ + if( rStr == 0 ) + return rtl::OUString(); + CFIndex nLength = CFStringGetLength( rStr ); + if( nLength == 0 ) + return rtl::OUString(); + const UniChar* pConstStr = CFStringGetCharactersPtr( rStr ); + if( pConstStr ) + return rtl::OUString( pConstStr, nLength ); + UniChar* pStr = reinterpret_cast<UniChar*>( rtl_allocateMemory( sizeof(UniChar)*nLength ) ); + CFRange aRange = { 0, nLength }; + CFStringGetCharacters( rStr, aRange, pStr ); + rtl::OUString aRet( pStr, nLength ); + rtl_freeMemory( pStr ); + return aRet; +} + +rtl::OUString GetOUString( NSString* pStr ) +{ + if( ! pStr ) + return rtl::OUString(); + int nLen = [pStr length]; + if( nLen == 0 ) + return rtl::OUString(); + + rtl::OUStringBuffer aBuf( nLen+1 ); + aBuf.setLength( nLen ); + [pStr getCharacters: const_cast<sal_Unicode*>(aBuf.getStr())]; + return aBuf.makeStringAndClear(); +} + +CFStringRef CreateCFString( const rtl::OUString& rStr ) +{ + return CFStringCreateWithCharacters(kCFAllocatorDefault, rStr.getStr(), rStr.getLength() ); +} + +NSString* CreateNSString( const rtl::OUString& rStr ) +{ + return [[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()]; +} + +CGImageRef CreateCGImage( const Image& rImage ) +{ + BitmapEx aBmpEx( rImage.GetBitmapEx() ); + Bitmap aBmp( aBmpEx.GetBitmap() ); + + if( ! aBmp || ! aBmp.ImplGetImpBitmap() ) + return NULL; + + // simple case, no transparency + AquaSalBitmap* pSalBmp = static_cast<AquaSalBitmap*>(aBmp.ImplGetImpBitmap()->ImplGetSalBitmap()); + + if( ! pSalBmp ) + return NULL; + + CGImageRef xImage = NULL; + if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) ) + xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); + else if( aBmpEx.IsAlpha() ) + { + AlphaMask aAlphaMask( aBmpEx.GetAlpha() ); + Bitmap aMask( aAlphaMask.GetBitmap() ); + AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); + if( pMaskBmp ) + xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); + else + xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); + } + else if( aBmpEx.GetTransparentType() == TRANSPARENT_BITMAP ) + { + Bitmap aMask( aBmpEx.GetMask() ); + AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); + if( pMaskBmp ) + xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); + else + xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); + } + else if( aBmpEx.GetTransparentType() == TRANSPARENT_COLOR ) + { + Color aTransColor( aBmpEx.GetTransparentColor() ); + SalColor nTransColor = MAKE_SALCOLOR( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() ); + xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor ); + } + + return xImage; +} + +NSImage* CreateNSImage( const Image& rImage ) +{ + CGImageRef xImage = CreateCGImage( rImage ); + + if( ! xImage ) + return nil; + + Size aSize( rImage.GetSizePixel() ); + NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )]; + if( pImage ) + { + [pImage setFlipped: YES]; + [pImage lockFocus]; + + NSGraphicsContext* pContext = [NSGraphicsContext currentContext]; + CGContextRef rCGContext = reinterpret_cast<CGContextRef>([pContext graphicsPort]); + + const CGRect aDstRect = { {0, 0}, { aSize.Width(), aSize.Height() } }; + CGContextDrawImage( rCGContext, aDstRect, xImage ); + + [pImage unlockFocus]; + } + + CGImageRelease( xImage ); + + return pImage; +} diff --git a/vcl/aqua/source/app/salnstimer.mm b/vcl/aqua/source/app/salnstimer.mm new file mode 100755 index 000000000000..73e49fd99c61 --- /dev/null +++ b/vcl/aqua/source/app/salnstimer.mm @@ -0,0 +1,56 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "saltimer.h" +#include "salnstimer.h" +#include "salinst.h" +#include "saldata.hxx" + +#include "vcl/svdata.hxx" + +@implementation TimerCallbackCaller +-(void)timerElapsed:(NSTimer*)pTimer +{ + ImplSVData* pSVData = ImplGetSVData(); + if( AquaSalTimer::bDispatchTimer ) + { + if( pSVData->mpSalTimer ) + { + YIELD_GUARD; + pSVData->mpSalTimer->CallCallback(); + + // NSTimer does not end nextEventMatchingMask of NSApplication + // so we need to wakeup a waiting Yield to inform it something happened + GetSalData()->mpFirstInstance->wakeupYield(); + } + } +} +@end + diff --git a/vcl/aqua/source/app/salsys.cxx b/vcl/aqua/source/app/salsys.cxx new file mode 100644 index 000000000000..3b548099feef --- /dev/null +++ b/vcl/aqua/source/app/salsys.cxx @@ -0,0 +1,131 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "tools/rc.hxx" +#include "vcl/svids.hrc" + +#include "salsys.h" +#include "saldata.hxx" +#include "rtl/ustrbuf.hxx" + +using namespace rtl; + +// ======================================================================= + +AquaSalSystem::~AquaSalSystem() +{ +} + +unsigned int AquaSalSystem::GetDisplayScreenCount() +{ + NSArray* pScreens = [NSScreen screens]; + return pScreens ? [pScreens count] : 1; +} + +bool AquaSalSystem::IsMultiDisplay() +{ + return false; +} + +unsigned int AquaSalSystem::GetDefaultDisplayNumber() +{ + return 0; +} + +Rectangle AquaSalSystem::GetDisplayScreenPosSizePixel( unsigned int nScreen ) +{ + NSArray* pScreens = [NSScreen screens]; + Rectangle aRet; + NSScreen* pScreen = nil; + if( pScreens && nScreen < [pScreens count] ) + pScreen = [pScreens objectAtIndex: nScreen]; + else + pScreen = [NSScreen mainScreen]; + + if( pScreen ) + { + NSRect aFrame = [pScreen frame]; + aRet = Rectangle( Point( static_cast<long int>(aFrame.origin.x), static_cast<long int>(aFrame.origin.y) ), + Size( static_cast<long int>(aFrame.size.width), static_cast<long int>(aFrame.size.height) ) ); + } + return aRet; +} + +Rectangle AquaSalSystem::GetDisplayWorkAreaPosSizePixel( unsigned int nScreen ) +{ + NSArray* pScreens = [NSScreen screens]; + Rectangle aRet; + NSScreen* pScreen = nil; + if( pScreens && nScreen < [pScreens count] ) + pScreen = [pScreens objectAtIndex: nScreen]; + else + pScreen = [NSScreen mainScreen]; + + if( pScreen ) + { + NSRect aFrame = [pScreen visibleFrame]; + aRet = Rectangle( Point( static_cast<long int>(aFrame.origin.x), static_cast<long int>(aFrame.origin.y) ), + Size( static_cast<long int>(aFrame.size.width), static_cast<long int>(aFrame.size.height) ) ); + } + return aRet; +} + +rtl::OUString AquaSalSystem::GetScreenName( unsigned int nScreen ) +{ + NSArray* pScreens = [NSScreen screens]; + OUString aRet; + if( nScreen < [pScreens count] ) + { + ResMgr* pMgr = ImplGetResMgr(); + if( pMgr ) + { + String aScreenName( ResId( SV_MAC_SCREENNNAME, *pMgr ) ); + aScreenName.SearchAndReplaceAllAscii( "%d", String::CreateFromInt32( nScreen ) ); + aRet = aScreenName; + } + } + return aRet; +} + +int AquaSalSystem::ShowNativeDialog( const String& rTitle, + const String& rMessage, + const std::list< String >& rButtons, + int nDefButton ) +{ + return 0; +} + +int AquaSalSystem::ShowNativeMessageBox( const String& rTitle, + const String& rMessage, + int nButtonCombination, + int nDefaultButton) +{ + return 0; +} diff --git a/vcl/aqua/source/app/saltimer.cxx b/vcl/aqua/source/app/saltimer.cxx new file mode 100644 index 000000000000..724857e92a0c --- /dev/null +++ b/vcl/aqua/source/app/saltimer.cxx @@ -0,0 +1,135 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "saltimer.h" +#include "salnstimer.h" +#include "saldata.hxx" +#include "salframe.h" +#include "salinst.h" + +// ======================================================================= + +NSTimer* AquaSalTimer::pRunningTimer = nil; +bool AquaSalTimer::bDispatchTimer = false; + + +void ImplSalStartTimer( ULONG nMS ) +{ + SalData* pSalData = GetSalData(); + if( pSalData->mpFirstInstance->isNSAppThread() ) + { + AquaSalTimer::bDispatchTimer = true; + NSTimeInterval aTI = double(nMS)/1000.0; + if( AquaSalTimer::pRunningTimer != nil ) + { + if( [AquaSalTimer::pRunningTimer timeInterval] == aTI ) + // set new fire date + [AquaSalTimer::pRunningTimer setFireDate: [NSDate dateWithTimeIntervalSinceNow: aTI]]; + else + { + [AquaSalTimer::pRunningTimer invalidate]; + AquaSalTimer::pRunningTimer = nil; + } + } + if( AquaSalTimer::pRunningTimer == nil ) + { + AquaSalTimer::pRunningTimer = [NSTimer scheduledTimerWithTimeInterval: aTI + target: [[[TimerCallbackCaller alloc] init] autorelease] + selector: @selector(timerElapsed:) + userInfo: nil + repeats: YES]; + /* #i84055# add timer to tracking run loop mode, + so they also elapse while e.g. life resize + */ + [[NSRunLoop currentRunLoop] addTimer: AquaSalTimer::pRunningTimer forMode: NSEventTrackingRunLoopMode]; + } + } + else + { + SalData::ensureThreadAutoreleasePool(); + // post an event so we can get into the main thread + NSPoint aPt = { 0, 0 }; + NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined + location: aPt + modifierFlags: 0 + timestamp: [NSDate timeIntervalSinceReferenceDate] + windowNumber: 0 + context: nil + subtype: AquaSalInstance::AppStartTimerEvent + data1: (int)nMS + data2: 0 ]; + if( pEvent ) + [NSApp postEvent: pEvent atStart: YES]; + } +} + +void ImplSalStopTimer() +{ + AquaSalTimer::bDispatchTimer = false; +} + +void AquaSalTimer::handleStartTimerEvent( NSEvent* pEvent ) +{ + ImplSVData* pSVData = ImplGetSVData(); + if( pSVData->mpSalTimer ) + { + NSTimeInterval posted = [pEvent timestamp] + NSTimeInterval([pEvent data1])/1000.0; + NSTimeInterval current = [NSDate timeIntervalSinceReferenceDate]; + if( (posted - current) <= 0.0 ) + { + YIELD_GUARD; + // timer already elapsed since event posted + pSVData->mpSalTimer->CallCallback(); + } + ImplSalStartTimer( ULONG( [pEvent data1] ) ); + } + +} + +AquaSalTimer::AquaSalTimer( ) +{ +} + +AquaSalTimer::~AquaSalTimer() +{ + ImplSalStopTimer(); +} + +void AquaSalTimer::Start( ULONG nMS ) +{ + ImplSalStartTimer( nMS ); +} + +void AquaSalTimer::Stop() +{ + ImplSalStopTimer(); +} + + diff --git a/vcl/aqua/source/app/vclnsapp.mm b/vcl/aqua/source/app/vclnsapp.mm new file mode 100755 index 000000000000..f33599fa086e --- /dev/null +++ b/vcl/aqua/source/app/vclnsapp.mm @@ -0,0 +1,518 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + // MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "vclnsapp.h" +#include "salinst.h" +#include "saldata.hxx" +#include "salframe.h" +#include "salframeview.h" + +#include "vcl/window.hxx" +#include "vcl/svapp.hxx" +#include "vcl/cmdevt.hxx" +#include "rtl/ustrbuf.hxx" + +#include "premac.h" +#import "Carbon/Carbon.h" +#import "apple_remote/RemoteControl.h" +#include "postmac.h" + + +@implementation CocoaThreadEnabler +-(void)enableCocoaThreads:(id)param +{ + // do nothing, this is just to start an NSThread and therefore put + // Cocoa into multithread mode +} +@end + +@implementation VCL_NSApplication +-(void)sendEvent:(NSEvent*)pEvent +{ + NSEventType eType = [pEvent type]; + if( eType == NSApplicationDefined ) + GetSalData()->mpFirstInstance->handleAppDefinedEvent( pEvent ); + else if( eType == NSKeyDown && ([pEvent modifierFlags] & NSCommandKeyMask) != 0 ) + { + NSWindow* pKeyWin = [NSApp keyWindow]; + if( pKeyWin && [pKeyWin isKindOfClass: [SalFrameWindow class]] ) + { + AquaSalFrame* pFrame = [(SalFrameWindow*)pKeyWin getSalFrame]; + // handle Cmd-W + // FIXME: the correct solution would be to handle this in framework + // in the menu code + // however that is currently being revised, so let's use a preliminary solution here + // this hack is based on assumption + // a) Cmd-W is the same in all languages in OOo's menu conig + // b) Cmd-W is the same in all languages in on MacOS + // for now this seems to be true + unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask)); + if( (pFrame->mnStyleMask & NSClosableWindowMask) != 0 ) + { + if( nModMask == NSCommandKeyMask + && [[pEvent charactersIgnoringModifiers] isEqualToString: @"w"] ) + { + [pFrame->getWindow() windowShouldClose: nil]; + return; + } + } + + /* + * #i98949# - Cmd-M miniaturize window, Cmd-Option-M miniaturize all windows + */ + if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"m"] ) + { + if ( nModMask == NSCommandKeyMask && ([pFrame->getWindow() styleMask] & NSMiniaturizableWindowMask) ) + { + [pFrame->getWindow() performMiniaturize: nil]; + return; + } + + if ( nModMask == ( NSCommandKeyMask | NSAlternateKeyMask ) ) + { + [NSApp miniaturizeAll: nil]; + return; + } + } + + // #i90083# handle frame switching + // FIXME: lousy workaround + if( (nModMask & (NSControlKeyMask|NSAlternateKeyMask)) == 0 ) + { + if( [[pEvent characters] isEqualToString: @"<"] || + [[pEvent characters] isEqualToString: @"~"] ) + { + [self cycleFrameForward: pFrame]; + return; + } + else if( [[pEvent characters] isEqualToString: @">"] || + [[pEvent characters] isEqualToString: @"`"] ) + { + [self cycleFrameBackward: pFrame]; + return; + } + } + + // get information whether the event was handled; keyDown returns nothing + GetSalData()->maKeyEventAnswer[ pEvent ] = false; + bool bHandled = false; + + // dispatch to view directly to avoid the key event being consumed by the menubar + // popup windows do not get the focus, so they don't get these either + // simplest would be dispatch this to the key window always if it is without parent + // however e.g. in document we want the menu shortcut if e.g. the stylist has focus + if( pFrame->mpParent && (pFrame->mnStyle & SAL_FRAME_STYLE_FLOAT) == 0 ) + { + [[pKeyWin contentView] keyDown: pEvent]; + bHandled = GetSalData()->maKeyEventAnswer[ pEvent ]; + } + + // see whether the main menu consumes this event + // if not, we want to dispatch it ourselves. Unless we do this "trick" + // the main menu just beeps for an unknown or disabled key equivalent + // and swallows the event wholesale + NSMenu* pMainMenu = [NSApp mainMenu]; + if( ! bHandled && (pMainMenu == 0 || ! [pMainMenu performKeyEquivalent: pEvent]) ) + { + [[pKeyWin contentView] keyDown: pEvent]; + bHandled = GetSalData()->maKeyEventAnswer[ pEvent ]; + } + else + bHandled = true; // event handled already or main menu just handled it + + GetSalData()->maKeyEventAnswer.erase( pEvent ); + if( bHandled ) + return; + } + else if( pKeyWin ) + { + // #i94601# a window not of vcl's making has the focus. + // Since our menus do not invoke the usual commands + // try to play nice with native windows like the file dialog + // and emulate them + // precondition: this ONLY works because CMD-V (paste), CMD-C (copy) and CMD-X (cut) are + // NOT localized, that is the same in all locales. Should this be + // different in any locale, this hack will fail. + unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask)); + if( nModMask == NSCommandKeyMask ) + { + + if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"v"] ) + { + if( [NSApp sendAction: @selector(paste:) to: nil from: nil] ) + return; + } + else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"c"] ) + { + if( [NSApp sendAction: @selector(copy:) to: nil from: nil] ) + return; + } + else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"x"] ) + { + if( [NSApp sendAction: @selector(cut:) to: nil from: nil] ) + return; + } + } + } + } + else if( eType == NSScrollWheel && ( GetSalData()->mnSystemVersion < VER_LEOPARD /* fixed in Leopard and above */ ) ) + { + + NSWindow* pWin = [pEvent window]; + // on Tiger wheel events do not reach non key windows + // which probably should be considered a bug + if( [pWin isKindOfClass: [SalFrameWindow class]] && [pWin canBecomeKeyWindow] == NO ) + { + [[pWin contentView] scrollWheel: pEvent]; + return; + } + } + [super sendEvent: pEvent]; +} + +-(void)sendSuperEvent:(NSEvent*)pEvent +{ + [super sendEvent: pEvent]; +} + +-(void)cycleFrameForward: (AquaSalFrame*)pCurFrame +{ + // find current frame in list + std::list< AquaSalFrame* >& rFrames( GetSalData()->maFrames ); + std::list< AquaSalFrame* >::iterator it = rFrames.begin(); + for( ; it != rFrames.end() && *it != pCurFrame; ++it ) + ; + if( it != rFrames.end() ) + { + // now find the next frame (or end) + do + { + ++it; + if( it != rFrames.end() ) + { + if( (*it)->mpDockMenuEntry != NULL && + (*it)->mbShown ) + { + [(*it)->getWindow() makeKeyAndOrderFront: NSApp]; + return; + } + } + } while( it != rFrames.end() ); + // cycle around, find the next up to pCurFrame + it = rFrames.begin(); + while( *it != pCurFrame ) + { + if( (*it)->mpDockMenuEntry != NULL && + (*it)->mbShown ) + { + [(*it)->getWindow() makeKeyAndOrderFront: NSApp]; + return; + } + ++it; + } + } +} + +-(void)cycleFrameBackward: (AquaSalFrame*)pCurFrame +{ + // do the same as cycleFrameForward only with a reverse iterator + + // find current frame in list + std::list< AquaSalFrame* >& rFrames( GetSalData()->maFrames ); + std::list< AquaSalFrame* >::reverse_iterator it = rFrames.rbegin(); + for( ; it != rFrames.rend() && *it != pCurFrame; ++it ) + ; + if( it != rFrames.rend() ) + { + // now find the next frame (or end) + do + { + ++it; + if( it != rFrames.rend() ) + { + if( (*it)->mpDockMenuEntry != NULL && + (*it)->mbShown ) + { + [(*it)->getWindow() makeKeyAndOrderFront: NSApp]; + return; + } + } + } while( it != rFrames.rend() ); + // cycle around, find the next up to pCurFrame + it = rFrames.rbegin(); + while( *it != pCurFrame ) + { + if( (*it)->mpDockMenuEntry != NULL && + (*it)->mbShown ) + { + [(*it)->getWindow() makeKeyAndOrderFront: NSApp]; + return; + } + ++it; + } + } +} + +-(NSMenu*)applicationDockMenu:(NSApplication *)sender +{ + return AquaSalInstance::GetDynamicDockMenu(); +} + +-(MacOSBOOL)application: (NSApplication*)app openFile: (NSString*)pFile +{ + const rtl::OUString aFile( GetOUString( pFile ) ); + if( ! AquaSalInstance::isOnCommandLine( aFile ) ) + { + const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), + APPEVENT_OPEN_STRING, aFile ); + AquaSalInstance::aAppEventList.push_back( pAppEvent ); + } + return YES; +} + +-(void)application: (NSApplication*) app openFiles: (NSArray*)files +{ + rtl::OUStringBuffer aFileList( 256 ); + + NSEnumerator* it = [files objectEnumerator]; + NSString* pFile = nil; + + while( (pFile = [it nextObject]) != nil ) + { + const rtl::OUString aFile( GetOUString( pFile ) ); + if( ! AquaSalInstance::isOnCommandLine( aFile ) ) + { + if( aFileList.getLength() > 0 ) + aFileList.append( sal_Unicode( APPEVENT_PARAM_DELIMITER ) ); + aFileList.append( aFile ); + } + } + + if( aFileList.getLength() ) + { + // we have no back channel here, we have to assume success, in which case + // replyToOpenOrPrint does not need to be called according to documentation + // [app replyToOpenOrPrint: NSApplicationDelegateReplySuccess]; + const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), + APPEVENT_OPEN_STRING, aFileList.makeStringAndClear() ); + AquaSalInstance::aAppEventList.push_back( pAppEvent ); + } +} + +-(MacOSBOOL)application: (NSApplication*)app printFile: (NSString*)pFile +{ + const rtl::OUString aFile( GetOUString( pFile ) ); + const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), + APPEVENT_PRINT_STRING, aFile ); + AquaSalInstance::aAppEventList.push_back( pAppEvent ); + return YES; +} +-(NSApplicationPrintReply)application: (NSApplication *) app printFiles:(NSArray *)files withSettings: (NSDictionary *)printSettings showPrintPanels:(MacOSBOOL)bShowPrintPanels +{ + // currently ignores print settings an bShowPrintPanels + rtl::OUStringBuffer aFileList( 256 ); + + NSEnumerator* it = [files objectEnumerator]; + NSString* pFile = nil; + + while( (pFile = [it nextObject]) != nil ) + { + if( aFileList.getLength() > 0 ) + aFileList.append( sal_Unicode( APPEVENT_PARAM_DELIMITER ) ); + aFileList.append( GetOUString( pFile ) ); + } + const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), ApplicationAddress(), + APPEVENT_PRINT_STRING, aFileList.makeStringAndClear() ); + AquaSalInstance::aAppEventList.push_back( pAppEvent ); + // we have no back channel here, we have to assume success + // correct handling would be NSPrintingReplyLater and then send [app replyToOpenOrPrint] + return NSPrintingSuccess; +} + +-(NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *) app +{ + SalData* pSalData = GetSalData(); + #if 1 // currently do some really bad hack + if( ! pSalData->maFrames.empty() ) + { + /* #i92766# something really weird is going on with the retain count of + our windows; sometimes we get a duplicate free before exit on one of our + NSWindows. The reason is unclear; to avoid this currently we retain them once more + + FIXME: this is a really bad hack, relying on the system to catch the leaked + resources. Find out what really goes on here and fix it ! + */ + std::vector< NSWindow* > aHackRetainedWindows; + for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin(); + it != pSalData->maFrames.end(); ++it ) + { + #if OSL_DEBUG_LEVEL > 1 + Window* pWin = (*it)->GetWindow(); + String aTitle = pWin->GetText(); + Window* pClient = pWin->ImplGetClientWindow(); + fprintf( stderr, "retaining %p (old count %d) windowtype=%s clienttyp=%s title=%s\n", + (*it)->mpWindow, [(*it)->mpWindow retainCount], + typeid(*pWin).name(), pClient ? typeid(*pClient).name() : "<nil>", + rtl::OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ).getStr() + ); + #endif + [(*it)->mpWindow retain]; + aHackRetainedWindows.push_back( (*it)->mpWindow ); + } + if( pSalData->maFrames.front()->CallCallback( SALEVENT_SHUTDOWN, NULL ) ) + { + for( std::vector< NSWindow* >::iterator it = aHackRetainedWindows.begin(); + it != aHackRetainedWindows.end(); ++it ) + { + // clean up the retaing count again from the shutdown workaround + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "releasing %p\n", (*it) ); + #endif + [(*it) release]; + } + return NSTerminateCancel; + } + #if OSL_DEBUG_LEVEL > 1 + for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin(); + it != pSalData->maFrames.end(); ++it ) + { + Window* pWin = (*it)->GetWindow(); + String aTitle = pWin->GetText(); + Window* pClient = pWin->ImplGetClientWindow(); + fprintf( stderr, "frame still alive: NSWindow %p windowtype=%s clienttyp=%s title=%s\n", + (*it)->mpWindow, typeid(*pWin).name(), pClient ? typeid(*pClient).name() : "<nil>", + rtl::OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ).getStr() + ); + } + #endif + } + #else // the clean version follows + return pSalData->maFrames.front()->CallCallback( SALEVENT_SHUTDOWN, NULL ) ? NSTerminateCancel : NSTerminateNow; + #endif + return NSTerminateNow; +} + +-(void)systemColorsChanged: (NSNotification*) pNotification +{ + const SalData* pSalData = GetSalData(); + if( !pSalData->maFrames.empty() ) + pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL ); +} + +-(void)screenParametersChanged: (NSNotification*) pNotification +{ + SalData* pSalData = GetSalData(); + std::list< AquaSalFrame* >::iterator it; + for( it = pSalData->maFrames.begin(); it != pSalData->maFrames.end(); ++it ) + { + (*it)->screenParametersChanged(); + } +} + +-(void)scrollbarVariantChanged: (NSNotification*) pNotification +{ + GetSalData()->mpFirstInstance->delayedSettingsChanged( true ); +} + +-(void)scrollbarSettingsChanged: (NSNotification*) pNotification +{ + GetSalData()->mpFirstInstance->delayedSettingsChanged( false ); +} + +-(void)addFallbackMenuItem: (NSMenuItem*)pNewItem +{ + AquaSalMenu::addFallbackMenuItem( pNewItem ); +} + +-(void)removeFallbackMenuItem: (NSMenuItem*)pItem +{ + AquaSalMenu::removeFallbackMenuItem( pItem ); +} + +-(void)addDockMenuItem: (NSMenuItem*)pNewItem +{ + NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu(); + [pDock insertItem: pNewItem atIndex: [pDock numberOfItems]]; +} + +// for Apple Remote implementation + +#pragma mark - +#pragma mark NSApplication Delegates +- (void)applicationWillBecomeActive:(NSNotification *)aNotification { + if (GetSalData()->mpMainController->remoteControl) { + + // [remoteControl startListening: self]; + // does crash because the right thing to do is + // [GetSalData()->mpMainController->remoteControl startListening: self]; + // but the instance variable 'remoteControl' is declared protected + // workaround : declare remoteControl instance variable as public in RemoteMainController.m + + [GetSalData()->mpMainController->remoteControl startListening: self]; +#ifdef DEBUG + NSLog(@"Apple Remote will become active - Using remote controls"); +#endif + } +} + +- (void)applicationWillResignActive:(NSNotification *)aNotification { + if (GetSalData()->mpMainController->remoteControl) { + + // [remoteControl stopListening: self]; + // does crash because the right thing to do is + // [GetSalData()->mpMainController->remoteControl stopListening: self]; + // but the instance variable 'remoteControl' is declared protected + // workaround : declare remoteControl instance variable as public in RemoteMainController.m + + [GetSalData()->mpMainController->remoteControl stopListening: self]; +#ifdef DEBUG + NSLog(@"Apple Remote will resign active - Releasing remote controls"); +#endif + } +} + +- (MacOSBOOL)applicationShouldHandleReopen: (NSApplication*)pApp hasVisibleWindows: (MacOSBOOL) bWinVisible +{ + NSObject* pHdl = GetSalData()->mpDockIconClickHandler; + if( pHdl && [pHdl respondsToSelector: @selector(dockIconClicked:)] ) + { + [pHdl performSelector:@selector(dockIconClicked:) withObject: self]; + } + return YES; +} + +-(void)setDockIconClickHandler: (NSObject*)pHandler +{ + GetSalData()->mpDockIconClickHandler = pHandler; +} + + +@end + diff --git a/vcl/aqua/source/dtrans/DataFlavorMapping.cxx b/vcl/aqua/source/dtrans/DataFlavorMapping.cxx new file mode 100644 index 000000000000..e0a95a532bf8 --- /dev/null +++ b/vcl/aqua/source/dtrans/DataFlavorMapping.cxx @@ -0,0 +1,732 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "vcl/unohelp.hxx" +#include <DataFlavorMapping.hxx> +#include "HtmlFmtFlt.hxx" +#include "PictToBmpFlt.hxx" +#include "com/sun/star/datatransfer/UnsupportedFlavorException.hpp" +#include "com/sun/star/datatransfer/XMimeContentType.hpp" +#include "com/sun/star/lang/XMultiServiceFactory.hpp" +#include "com/sun/star/uno/Sequence.hxx" + +#include <rtl/ustring.hxx> +#include <rtl/memory.h> +#include <osl/endian.h> + +#include <vector> +#include <stdio.h> + +#include <premac.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> + +using namespace ::com::sun::star::datatransfer; +using namespace rtl; +using namespace ::com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace cppu; +using namespace std; + +namespace // private +{ + const Type CPPUTYPE_SEQINT8 = getCppuType((Sequence<sal_Int8>*)0); + const Type CPPUTYPE_OUSTRING = getCppuType( (OUString*)0 ); + + /* Determine whether or not a DataFlavor is valid. + */ + bool isValidFlavor(const DataFlavor& aFlavor) + { + size_t len = aFlavor.MimeType.getLength(); + Type dtype = aFlavor.DataType; + return ((len > 0) && ((dtype == CPPUTYPE_SEQINT8) || (dtype == CPPUTYPE_OUSTRING))); + } + + typedef vector<sal_Unicode> UnicodeBuffer; + + OUString NSStringToOUString(NSString* cfString) + { + BOOST_ASSERT(cfString && "Invalid parameter"); + + const char* utf8Str = [cfString UTF8String]; + unsigned int len = rtl_str_getLength(utf8Str); + + return OUString(utf8Str, len, RTL_TEXTENCODING_UTF8); + } + + NSString* OUStringToNSString(const OUString& ustring) + { + OString utf8Str = OUStringToOString(ustring, RTL_TEXTENCODING_UTF8); + return [NSString stringWithCString: utf8Str.getStr() encoding: NSUTF8StringEncoding]; + } + + + const NSString* PBTYPE_UT16 = @"CorePasteboardFlavorType 0x75743136"; + const NSString* PBTYPE_PICT = @"CorePasteboardFlavorType 0x50494354"; + const NSString* PBTYPE_HTML = @"CorePasteboardFlavorType 0x48544D4C"; + const NSString* PBTYPE_SODX = @"application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\""; + const NSString* PBTYPE_SESX = @"application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""; + const NSString* PBTYPE_SLSDX = @"application/x-openoffice-linksrcdescriptor-xml;windows_formatname=\"Star Link Source Descriptor (XML)\""; + const NSString* PBTYPE_ESX = @"application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""; + const NSString* PBTYPE_LSX = @"application/x-openoffice-link-source-xml;windows_formatname=\"Star Link Source (XML)\""; + const NSString* PBTYPE_EOX = @"application/x-openoffice-embedded-obj-xml;windows_formatname=\"Star Embedded Object (XML)\""; + const NSString* PBTYPE_SVXB = @"application/x-openoffice-svbx;windows_formatname=\"SVXB (StarView Bitmap/Animation)\""; + const NSString* PBTYPE_GDIMF = @"application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\""; + const NSString* PBTYPE_WMF = @"application/x-openoffice-wmf;windows_formatname=\"Image WMF\""; + const NSString* PBTYPE_EMF = @"application/x-openoffice-emf;windows_formatname=\"Image EMF\""; + + const NSString* PBTYPE_DUMMY_INTERNAL = @"application/x-openoffice-internal"; + + const char* FLAVOR_SODX = "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\""; + const char* FLAVOR_SESX = "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""; + const char* FLAVOR_SLSDX = "application/x-openoffice-linksrcdescriptor-xml;windows_formatname=\"Star Link Source Descriptor (XML)\""; + const char* FLAVOR_ESX = "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""; + const char* FLAVOR_LSX = "application/x-openoffice-link-source-xml;windows_formatname=\"Star Link Source (XML)\""; + const char* FLAVOR_EOX = "application/x-openoffice-embedded-obj-xml;windows_formatname=\"Star Embedded Object (XML)\""; + const char* FLAVOR_SVXB = "application/x-openoffice-svbx;windows_formatname=\"SVXB (StarView Bitmap/Animation)\""; + const char* FLAVOR_GDIMF = "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\""; + const char* FLAVOR_WMF = "application/x-openoffice-wmf;windows_formatname=\"Image WMF\""; + const char* FLAVOR_EMF = "application/x-openoffice-emf;windows_formatname=\"Image EMF\""; + + const char* FLAVOR_DUMMY_INTERNAL = "application/x-openoffice-internal"; + + + struct FlavorMap + { + NSString* SystemFlavor; + const char* OOoFlavor; + const char* HumanPresentableName; + Type DataType; + }; + + /* At the moment it appears as if only MS Office pastes "public.html" to the clipboard. + */ + FlavorMap flavorMap[] = + { + { NSStringPboardType, "text/plain;charset=utf-16", "Unicode Text (UTF-16)", CPPUTYPE_OUSTRING }, + { NSRTFPboardType, "text/richtext", "Rich Text Format", CPPUTYPE_SEQINT8 }, + { NSTIFFPboardType, "image/bmp", "Windows Bitmap", CPPUTYPE_SEQINT8 }, + { NSPICTPboardType, "image/bmp", "Windows Bitmap", CPPUTYPE_SEQINT8 }, + { NSHTMLPboardType, "text/html", "Plain Html", CPPUTYPE_SEQINT8 }, + { NSFilenamesPboardType, "application/x-openoffice-filelist;windows_formatname=\"FileList\"", "FileList", CPPUTYPE_SEQINT8 }, + { PBTYPE_SESX, FLAVOR_SESX, "Star Embed Source (XML)", CPPUTYPE_SEQINT8 }, + { PBTYPE_SLSDX, FLAVOR_SLSDX, "Star Link Source Descriptor (XML)", CPPUTYPE_SEQINT8 }, + { PBTYPE_ESX, FLAVOR_ESX, "Star Embed Source (XML)", CPPUTYPE_SEQINT8 }, + { PBTYPE_LSX, FLAVOR_LSX, "Star Link Source (XML)", CPPUTYPE_SEQINT8 }, + { PBTYPE_EOX, FLAVOR_EOX, "Star Embedded Object (XML)", CPPUTYPE_SEQINT8 }, + { PBTYPE_SVXB, FLAVOR_SVXB, "SVXB (StarView Bitmap/Animation", CPPUTYPE_SEQINT8 }, + { PBTYPE_GDIMF, FLAVOR_GDIMF, "GDIMetaFile", CPPUTYPE_SEQINT8 }, + { PBTYPE_WMF, FLAVOR_WMF, "Windows MetaFile", CPPUTYPE_SEQINT8 }, + { PBTYPE_EMF, FLAVOR_EMF, "Windows Enhanced MetaFile", CPPUTYPE_SEQINT8 }, + { PBTYPE_SODX, FLAVOR_SODX, "Star Object Descriptor (XML)", CPPUTYPE_SEQINT8 }, + { PBTYPE_DUMMY_INTERNAL, FLAVOR_DUMMY_INTERNAL, "internal data",CPPUTYPE_SEQINT8 } + }; + + + #define SIZE_FLAVOR_MAP (sizeof(flavorMap)/sizeof(FlavorMap)) + + + inline bool isByteSequenceType(const Type& theType) + { + return (theType == CPPUTYPE_SEQINT8); + } + + inline bool isOUStringType(const Type& theType) + { + return (theType == CPPUTYPE_OUSTRING); + } + +} // namespace private + + +//########################### + +/* A base class for other data provider. + */ +class DataProviderBaseImpl : public DataProvider +{ +public: + DataProviderBaseImpl(const Any& data); + DataProviderBaseImpl(id data); + virtual ~DataProviderBaseImpl(); + +protected: + Any mData; + //NSData* mSystemData; + id mSystemData; +}; + +DataProviderBaseImpl::DataProviderBaseImpl(const Any& data) : + mData(data), + mSystemData(nil) +{ +} + +DataProviderBaseImpl::DataProviderBaseImpl(id data) : + mSystemData(data) +{ + [mSystemData retain]; +} + + +DataProviderBaseImpl::~DataProviderBaseImpl() +{ + if (mSystemData) + { + [mSystemData release]; + } +} + +//################################# + +class UniDataProvider : public DataProviderBaseImpl +{ +public: + UniDataProvider(const Any& data); + + UniDataProvider(NSData* data); + + virtual NSData* getSystemData(); + + virtual Any getOOoData(); +}; + +UniDataProvider::UniDataProvider(const Any& data) : + DataProviderBaseImpl(data) +{ +} + +UniDataProvider::UniDataProvider(NSData* data) : + DataProviderBaseImpl(data) +{ +} + +NSData* UniDataProvider::getSystemData() +{ + OUString ustr; + mData >>= ustr; + + OString strUtf8; + ustr.convertToString(&strUtf8, RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS); + + return [NSData dataWithBytes: strUtf8.getStr() length: strUtf8.getLength()]; +} + +Any UniDataProvider::getOOoData() +{ + Any oOOData; + + if (mSystemData) + { + oOOData = makeAny(OUString(reinterpret_cast<const sal_Char*>([mSystemData bytes]), + [mSystemData length], + RTL_TEXTENCODING_UTF8)); + } + else + { + oOOData = mData; + } + + return oOOData; +} + +//########################### + +class ByteSequenceDataProvider : public DataProviderBaseImpl +{ +public: + ByteSequenceDataProvider(const Any& data); + + ByteSequenceDataProvider(NSData* data); + + virtual NSData* getSystemData(); + + virtual Any getOOoData(); +}; + +ByteSequenceDataProvider::ByteSequenceDataProvider(const Any& data) : + DataProviderBaseImpl(data) +{ +} + +ByteSequenceDataProvider::ByteSequenceDataProvider(NSData* data) : + DataProviderBaseImpl(data) +{ +} + + +NSData* ByteSequenceDataProvider::getSystemData() +{ + Sequence<sal_Int8> rawData; + mData >>= rawData; + + return [NSData dataWithBytes: rawData.getArray() length: rawData.getLength()]; +} + +Any ByteSequenceDataProvider::getOOoData() +{ + Any oOOData; + + if (mSystemData) + { + unsigned int flavorDataLength = [mSystemData length]; + Sequence<sal_Int8> byteSequence; + byteSequence.realloc(flavorDataLength); + memcpy(byteSequence.getArray(), [mSystemData bytes], flavorDataLength); + oOOData = makeAny(byteSequence); + } + else + { + oOOData = mData; + } + + return oOOData; +} + + +//########################### + +class HTMLFormatDataProvider : public DataProviderBaseImpl +{ +public: + HTMLFormatDataProvider(const Any& data); + + HTMLFormatDataProvider(NSData* data); + + virtual NSData* getSystemData(); + + virtual Any getOOoData(); +}; + +HTMLFormatDataProvider::HTMLFormatDataProvider(const Any& data) : + DataProviderBaseImpl(data) +{ +} + +HTMLFormatDataProvider::HTMLFormatDataProvider(NSData* data) : + DataProviderBaseImpl(data) +{ +} + +NSData* HTMLFormatDataProvider::getSystemData() +{ + Sequence<sal_Int8> textHtmlData; + mData >>= textHtmlData; + + Sequence<sal_Int8> htmlFormatData = TextHtmlToHTMLFormat(textHtmlData); + + return [NSData dataWithBytes: htmlFormatData.getArray() length: htmlFormatData.getLength()]; +} + +Any HTMLFormatDataProvider::getOOoData() +{ + Any oOOData; + + if (mSystemData) + { + unsigned int flavorDataLength = [mSystemData length]; + Sequence<sal_Int8> unkHtmlData; + + unkHtmlData.realloc(flavorDataLength); + memcpy(unkHtmlData.getArray(), [mSystemData bytes], flavorDataLength); + + Sequence<sal_Int8>* pPlainHtml = &unkHtmlData; + Sequence<sal_Int8> plainHtml; + + if (isHTMLFormat(unkHtmlData)) + { + plainHtml = HTMLFormatToTextHtml(unkHtmlData); + pPlainHtml = &plainHtml; + } + + oOOData = makeAny(*pPlainHtml); + } + else + { + oOOData = mData; + } + + return oOOData; +} + +//########################### + +class BMPDataProvider : public DataProviderBaseImpl +{ + NSBitmapImageFileType meImageType; +public: + BMPDataProvider(const Any& data, NSBitmapImageFileType eImageType ); + + BMPDataProvider(NSData* data, NSBitmapImageFileType eImageType); + + virtual NSData* getSystemData(); + + virtual Any getOOoData(); +}; + +BMPDataProvider::BMPDataProvider(const Any& data, NSBitmapImageFileType eImageType) : + DataProviderBaseImpl(data), + meImageType( eImageType ) +{ +} + +BMPDataProvider::BMPDataProvider(NSData* data, NSBitmapImageFileType eImageType) : + DataProviderBaseImpl(data), + meImageType( eImageType ) +{ +} + +NSData* BMPDataProvider::getSystemData() +{ + Sequence<sal_Int8> bmpData; + mData >>= bmpData; + + Sequence<sal_Int8> pictData; + NSData* sysData = NULL; + + if (BMPToImage(bmpData, pictData, meImageType)) + { + sysData = [NSData dataWithBytes: pictData.getArray() length: pictData.getLength()]; + } + + return sysData; +} + +/* At the moment the OOo 'PCT' filter is not good enough to be used + and there is no flavor defined for exchanging 'PCT' with OOo so + we will at the moment convert 'PCT' to a Windows BMP and provide + this to OOo +*/ +Any BMPDataProvider::getOOoData() +{ + Any oOOData; + + if (mSystemData) + { + unsigned int flavorDataLength = [mSystemData length]; + Sequence<sal_Int8> pictData(flavorDataLength); + + memcpy(pictData.getArray(), [mSystemData bytes], flavorDataLength); + + Sequence<sal_Int8> bmpData; + + if (ImageToBMP(pictData, bmpData, meImageType)) + { + oOOData = makeAny(bmpData); + } + } + else + { + oOOData = mData; + } + + return oOOData; +} + +//###################### + +class FileListDataProvider : public DataProviderBaseImpl +{ +public: + FileListDataProvider(const Any& data); + FileListDataProvider(NSArray* data); + + virtual NSData* getSystemData(); + virtual Any getOOoData(); +}; + +FileListDataProvider::FileListDataProvider(const Any& data) : + DataProviderBaseImpl(data) +{ +} + +FileListDataProvider::FileListDataProvider(NSArray* data) : + DataProviderBaseImpl(data) +{ +} + +NSData* FileListDataProvider::getSystemData() +{ + return [NSData data]; +} + +Any FileListDataProvider::getOOoData() +{ + Any oOOData; + + if (mSystemData) + { + size_t length = [mSystemData count]; + size_t lenSeqRequired = 0; + + for (size_t i = 0; i < length; i++) + { + NSString* fname = [mSystemData objectAtIndex: i]; + lenSeqRequired += [fname maximumLengthOfBytesUsingEncoding: NSUnicodeStringEncoding] + sizeof(unichar); + } + + Sequence<sal_Int8> oOOFileList(lenSeqRequired); + unichar* pBuffer = reinterpret_cast<unichar*>(oOOFileList.getArray()); + rtl_zeroMemory(pBuffer, lenSeqRequired); + + for (size_t i = 0; i < length; i++) + { + NSString* fname = [mSystemData objectAtIndex: i]; + [fname getCharacters: pBuffer]; + size_t l = [fname length]; + pBuffer += l + 1; + } + + oOOData = makeAny(oOOFileList); + } + else + { + oOOData = mData; + } + + return oOOData; +} + +//########################### + +DataFlavorMapper::DataFlavorMapper() +{ + Reference<XMultiServiceFactory> mrServiceManager = vcl::unohelper::GetMultiServiceFactory(); + mrXMimeCntFactory = Reference<XMimeContentTypeFactory>(mrServiceManager->createInstance( + OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.MimeContentTypeFactory"))), UNO_QUERY); + + if (!mrXMimeCntFactory.is()) + throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Cannot create com.sun.star.datatransfer.MimeContentTypeFactory")), NULL); +} + +DataFlavor DataFlavorMapper::systemToOpenOfficeFlavor(NSString* systemDataFlavor) const +{ + DataFlavor oOOFlavor; + + for (size_t i = 0; i < SIZE_FLAVOR_MAP; i++) + { + if ([systemDataFlavor caseInsensitiveCompare: flavorMap[i].SystemFlavor] == NSOrderedSame) + { + oOOFlavor.MimeType = OUString::createFromAscii(flavorMap[i].OOoFlavor); + oOOFlavor.HumanPresentableName = OUString(RTL_CONSTASCII_USTRINGPARAM(flavorMap[i].HumanPresentableName)); + oOOFlavor.DataType = flavorMap[i].DataType; + return oOOFlavor; + } + } // for + + return oOOFlavor; +} + +NSString* DataFlavorMapper::openOfficeToSystemFlavor(const DataFlavor& oOOFlavor) const +{ + NSString* sysFlavor = NULL; + + for (size_t i = 0; i < SIZE_FLAVOR_MAP; i++) + { + if (oOOFlavor.MimeType.compareToAscii(flavorMap[i].OOoFlavor, strlen(flavorMap[i].OOoFlavor)) == 0) + { + sysFlavor = flavorMap[i].SystemFlavor; + } + } + + return sysFlavor; +} + +NSString* DataFlavorMapper::openOfficeImageToSystemFlavor(NSPasteboard* pPasteboard) const +{ + NSArray *supportedTypes = [NSArray arrayWithObjects: NSTIFFPboardType, NSPICTPboardType, nil]; + NSString *sysFlavor = [pPasteboard availableTypeFromArray:supportedTypes]; + return sysFlavor; +} + +DataProviderPtr_t DataFlavorMapper::getDataProvider(NSString* systemFlavor, Reference<XTransferable> rTransferable) const +{ + DataProviderPtr_t dp; + + try + { + DataFlavor oOOFlavor = systemToOpenOfficeFlavor(systemFlavor); + + Any data = rTransferable->getTransferData(oOOFlavor); + + if (isByteSequenceType(data.getValueType())) + { + if ([systemFlavor caseInsensitiveCompare: NSHTMLPboardType] == NSOrderedSame) + { + dp = DataProviderPtr_t(new HTMLFormatDataProvider(data)); + } + else if ([systemFlavor caseInsensitiveCompare: NSPICTPboardType] == NSOrderedSame) + { + dp = DataProviderPtr_t(new BMPDataProvider(data, PICTImageFileType)); + } + else if ([systemFlavor caseInsensitiveCompare: NSTIFFPboardType] == NSOrderedSame) + { + dp = DataProviderPtr_t(new BMPDataProvider(data, NSTIFFFileType)); + } + else if ([systemFlavor caseInsensitiveCompare: NSFilenamesPboardType] == NSOrderedSame) + { + dp = DataProviderPtr_t(new FileListDataProvider(data)); + } + else + { + dp = DataProviderPtr_t(new ByteSequenceDataProvider(data)); + } + } + else // Must be OUString type + { + BOOST_ASSERT(isOUStringType(data.getValueType())); + dp = DataProviderPtr_t(new UniDataProvider(data)); + } + } + catch(UnsupportedFlavorException&) + { + // Somebody violates the contract of the clipboard + // interface @see XTransferable + } + + return dp; +} + +DataProviderPtr_t DataFlavorMapper::getDataProvider(const NSString* systemFlavor, NSArray* systemData) const +{ + return DataProviderPtr_t(new FileListDataProvider(systemData)); +} + +DataProviderPtr_t DataFlavorMapper::getDataProvider(const NSString* systemFlavor, NSData* systemData) const +{ + DataProviderPtr_t dp; + + if ([systemFlavor caseInsensitiveCompare: NSStringPboardType] == NSOrderedSame) + { + dp = DataProviderPtr_t(new UniDataProvider(systemData)); + } + else if ([systemFlavor caseInsensitiveCompare: NSHTMLPboardType] == NSOrderedSame) + { + dp = DataProviderPtr_t(new HTMLFormatDataProvider(systemData)); + } + else if ([systemFlavor caseInsensitiveCompare: NSPICTPboardType] == NSOrderedSame) + { + dp = DataProviderPtr_t(new BMPDataProvider(systemData, PICTImageFileType)); + } + else if ([systemFlavor caseInsensitiveCompare: NSTIFFPboardType] == NSOrderedSame) + { + dp = DataProviderPtr_t(new BMPDataProvider(systemData, NSTIFFFileType)); + } + else if ([systemFlavor caseInsensitiveCompare: NSFilenamesPboardType] == NSOrderedSame) + { + //dp = DataProviderPtr_t(new FileListDataProvider(systemData)); + } + else + { + dp = DataProviderPtr_t(new ByteSequenceDataProvider(systemData)); + } + + return dp; +} + +bool DataFlavorMapper::isValidMimeContentType(const rtl::OUString& contentType) const +{ + bool result = true; + + try + { + Reference<XMimeContentType> xCntType(mrXMimeCntFactory->createMimeContentType(contentType)); + } + catch( IllegalArgumentException& ) + { + result = false; + } + + return result; +} + +NSArray* DataFlavorMapper::flavorSequenceToTypesArray(const com::sun::star::uno::Sequence<com::sun::star::datatransfer::DataFlavor>& flavors) const +{ + sal_uInt32 nFlavors = flavors.getLength(); + NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity: 1]; + + for (sal_uInt32 i = 0; i < nFlavors; i++) + { + if( flavors[i].MimeType.compareToAscii( "image/bmp", 9 ) == 0 ) + { + [array addObject: NSTIFFPboardType]; + [array addObject: NSPICTPboardType]; + } + else + { + NSString* str = openOfficeToSystemFlavor(flavors[i]); + + if (str != NULL) + { + [array addObject: str]; + } + } + } + + // #i89462# #i90747# + // in case no system flavor was found to report + // report at least one so D&D between OOo targets works + if( [array count] == 0 ) + { + [array addObject: PBTYPE_DUMMY_INTERNAL]; + } + + return [array autorelease]; +} + +com::sun::star::uno::Sequence<com::sun::star::datatransfer::DataFlavor> DataFlavorMapper::typesArrayToFlavorSequence(NSArray* types) const +{ + int nFormats = [types count]; + Sequence<DataFlavor> flavors; + + for (int i = 0; i < nFormats; i++) + { + NSString* sysFormat = [types objectAtIndex: i]; + DataFlavor oOOFlavor = systemToOpenOfficeFlavor(sysFormat); + + if (isValidFlavor(oOOFlavor)) + { + flavors.realloc(flavors.getLength() + 1); + flavors[flavors.getLength() - 1] = oOOFlavor; + } + } + + return flavors; +} + + +NSArray* DataFlavorMapper::getAllSupportedPboardTypes() const +{ + NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity: SIZE_FLAVOR_MAP]; + + for (sal_uInt32 i = 0; i < SIZE_FLAVOR_MAP; i++) + { + [array addObject: flavorMap[i].SystemFlavor]; + } + + return [array autorelease]; +} diff --git a/vcl/aqua/source/dtrans/DataFlavorMapping.hxx b/vcl/aqua/source/dtrans/DataFlavorMapping.hxx new file mode 100644 index 000000000000..9847fcbd3987 --- /dev/null +++ b/vcl/aqua/source/dtrans/DataFlavorMapping.hxx @@ -0,0 +1,143 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef INCLUDED_DATAFLAVORMAPPING_HXX_ +#define INCLUDED_DATAFLAVORMAPPING_HXX_ + +#include <com/sun/star/datatransfer/DataFlavor.hpp> +#include <com/sun/star/datatransfer/XMimeContentTypeFactory.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> + +#include <premac.h> +#import <Cocoa/Cocoa.h> +#include <postmac.h> + +#include <memory> +#include <boost/shared_ptr.hpp> + + +/* An interface to get the clipboard data in either + system or OOo format. + */ +class DataProvider +{ +public: + virtual ~DataProvider() {}; + + /* Get the clipboard data in the system format. + The caller has to retain/release the returned + CFDataRef on demand. + */ + virtual NSData* getSystemData() = 0; + + /* Get the clipboard data in OOo format. + */ + virtual com::sun::star::uno::Any getOOoData() = 0; +}; + +typedef std::auto_ptr<DataProvider> DataProviderPtr_t; + + +//################################ + + +class DataFlavorMapper +{ +public: + /* Initialialize a DataFavorMapper instance. Throws a RuntimeException in case the XMimeContentTypeFactory service + cannot be created. + */ + DataFlavorMapper(); + + + /* Map a system data flavor to an OpenOffice data flavor. + Return an empty string if there is not suiteable + mapping from a system data flavor to a OpenOffice data + flavor. + */ + com::sun::star::datatransfer::DataFlavor systemToOpenOfficeFlavor(NSString* systemDataFlavor) const; + + + /* Map an OpenOffice data flavor to a system data flavor. + If there is no suiteable mapping available NULL will + be returned. + */ + NSString* openOfficeToSystemFlavor(const com::sun::star::datatransfer::DataFlavor& oooDataFlavor) const; + + /* Select the best available image data type + If there is no suiteable mapping available NULL will + be returned. + */ + NSString* openOfficeImageToSystemFlavor(NSPasteboard* pPasteboard) const; + + /* Get a data provider which is able to provide the data 'rTransferable' offers in a format that can + be put on to the system clipboard. + */ + DataProviderPtr_t getDataProvider(NSString* systemFlavor, + const com::sun::star::uno::Reference< com::sun::star::datatransfer::XTransferable > rTransferable) const; + + + + /* Get a data provider which is able to provide 'systemData' in the OOo expected format. + */ + DataProviderPtr_t getDataProvider(const NSString* systemFlavor, NSArray* systemData) const; + + + /* Get a data provider which is able to provide 'systemData' in the OOo expected format. + */ + DataProviderPtr_t getDataProvider(const NSString* systemFlavor, NSData* systemData) const; + + + /* Translate a sequence of DataFlavors into a NSArray of system types. + Only those DataFlavors for which a suitable mapping to a system + type exist will be contained in the returned types array. + */ + NSArray* flavorSequenceToTypesArray(const com::sun::star::uno::Sequence<com::sun::star::datatransfer::DataFlavor>& flavors) const; + + /* Translate a NSArray of system types into a sequence of DataFlavors. + Only those types for which a suitable mapping to a DataFlavor + exist will be contained in the new DataFlavor Sequence. + */ + com::sun::star::uno::Sequence<com::sun::star::datatransfer::DataFlavor> typesArrayToFlavorSequence(NSArray* types) const; + + /* Returns an NSArray containing all pasteboard types supported by OOo + */ + NSArray* DataFlavorMapper::getAllSupportedPboardTypes() const; + +private: + /* Determines if the provided Mime content type is valid. + */ + bool isValidMimeContentType(const rtl::OUString& contentType) const; + +private: + ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XMimeContentTypeFactory> mrXMimeCntFactory; +}; + +typedef boost::shared_ptr<DataFlavorMapper> DataFlavorMapperPtr_t; + +#endif diff --git a/vcl/aqua/source/dtrans/DragActionConversion.cxx b/vcl/aqua/source/dtrans/DragActionConversion.cxx new file mode 100644 index 000000000000..06ce57e8748f --- /dev/null +++ b/vcl/aqua/source/dtrans/DragActionConversion.cxx @@ -0,0 +1,92 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "DragActionConversion.hxx" +#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> + + +using namespace com::sun::star::datatransfer::dnd; + + +/* Convert office drag actions as defined in + <type>com::sun::star::datatransfer::dnd::DNDConstants</type> + into system conform drag actions. + */ +unsigned int OfficeToSystemDragActions(sal_Int8 dragActions) +{ + unsigned int actions = NSDragOperationNone; + + if (dragActions & DNDConstants::ACTION_COPY) + { + actions |= NSDragOperationCopy; + } + + if (dragActions & DNDConstants::ACTION_MOVE) + { + actions |= NSDragOperationMove; + } + + if (dragActions & DNDConstants::ACTION_LINK) + { + actions |= NSDragOperationLink; + } + + return actions; +} + +/* Convert system conform drag actions into office conform + drag actions as defined in + <type>com::sun::star::datatransfer::dnd::DNDConstants</type>. + */ +sal_Int8 SystemToOfficeDragActions(unsigned int dragActions) +{ + sal_Int8 actions = DNDConstants::ACTION_NONE; + + if (dragActions & NSDragOperationCopy) + { + actions |= DNDConstants::ACTION_COPY; + } + + if (dragActions & NSDragOperationMove) + { + actions |= DNDConstants::ACTION_MOVE; + } + + if (dragActions & NSDragOperationLink) + { + actions |= DNDConstants::ACTION_LINK; + } + + // We map NSDragOperationGeneric to ACTION_DEFAULT to + // signal that we have to decide for a drag action + if (dragActions & NSDragOperationGeneric) + { + actions |= DNDConstants::ACTION_DEFAULT; + } + + return actions; +} diff --git a/vcl/aqua/source/dtrans/DragActionConversion.hxx b/vcl/aqua/source/dtrans/DragActionConversion.hxx new file mode 100644 index 000000000000..7facfef794b6 --- /dev/null +++ b/vcl/aqua/source/dtrans/DragActionConversion.hxx @@ -0,0 +1,46 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +#include <sal/types.h> + +#include <premac.h> +#import <Cocoa/Cocoa.h> +#include <postmac.h> + + +/* Convert office drag actions as defined in + <type>com::sun::star::datatransfer::dnd::DNDConstants</type> + into system conform drag actions. + */ +unsigned int OfficeToSystemDragActions(sal_Int8 dragActions); + +/* Convert system conform drag actions into office conform + drag actions as defined in + <type>com::sun::star::datatransfer::dnd::DNDConstants</type>. + */ +sal_Int8 SystemToOfficeDragActions(unsigned int dragActions); diff --git a/vcl/aqua/source/dtrans/DragSource.cxx b/vcl/aqua/source/dtrans/DragSource.cxx new file mode 100644 index 000000000000..adb247d70711 --- /dev/null +++ b/vcl/aqua/source/dtrans/DragSource.cxx @@ -0,0 +1,363 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/awt/MouseButton.hpp> +#include <rtl/unload.h> + +#include "comphelper/makesequence.hxx" + +#include "DragSource.hxx" +#include "DragSourceContext.hxx" +#include "aqua_clipboard.hxx" +#include "DragActionConversion.hxx" + +#include <rtl/ustring.h> +#include <memory> + + +using namespace rtl; +using namespace cppu; +using namespace osl; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::datatransfer::dnd; +using namespace com::sun::star::datatransfer::dnd::DNDConstants; +using namespace com::sun::star::uno; +using namespace com::sun::star::awt::MouseButton; +using namespace com::sun::star::awt; +using namespace com::sun::star::lang; +using namespace comphelper; +using namespace std; + + +// For OOo internal D&D we provide the Transferable without NSDragPboard +// interference as a shortcut +Reference<XTransferable> DragSource::g_XTransferable = Reference<XTransferable>(); +NSView* DragSource::g_DragSourceView = nil; +bool DragSource::g_DropSuccessSet = false; +bool DragSource::g_DropSuccess = false; + + +OUString dragSource_getImplementationName() +{ + return OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.datatransfer.dnd.OleDragSource_V1")); +} + +Sequence<OUString> dragSource_getSupportedServiceNames() +{ + return makeSequence(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.OleDragSource"))); +} + + +@implementation DragSourceHelper; + +-(DragSourceHelper*)initWithDragSource: (DragSource*) pds +{ + self = [super init]; + + if (self) + { + mDragSource = pds; + } + + return self; +} + + +-(void)mouseDown: (NSEvent*)theEvent +{ + mDragSource->saveMouseEvent(theEvent); +} + + +-(void)mouseDragged: (NSEvent*)theEvent +{ + mDragSource->saveMouseEvent(theEvent); +} + + +-(unsigned int)draggingSourceOperationMaskForLocal: (MacOSBOOL)isLocal +{ + return mDragSource->getSupportedDragOperations(isLocal); +} + + +-(void)draggedImage:(NSImage*)anImage beganAt:(NSPoint)aPoint +{ + DragSourceDragEvent dsde(static_cast<OWeakObject*>(mDragSource), + new DragSourceContext(mDragSource), + mDragSource, + DNDConstants::ACTION_COPY, + DNDConstants::ACTION_COPY); + + mDragSource->mXDragSrcListener->dragEnter(dsde); +} + + +-(void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation +{ + // an internal drop can accept the drop but fail with dropComplete( false ) + // this is different than the Cocoa API + bool bDropSuccess = operation != NSDragOperationNone; + if( DragSource::g_DropSuccessSet ) + bDropSuccess = DragSource::g_DropSuccess; + + DragSourceDropEvent dsde(static_cast<OWeakObject*>(mDragSource), + new DragSourceContext(mDragSource), + static_cast< XDragSource* >(mDragSource), + SystemToOfficeDragActions(operation), + bDropSuccess ); + + mDragSource->mXDragSrcListener->dragDropEnd(dsde); + mDragSource->mXDragSrcListener = Reference<XDragSourceListener>(); +} + + +-(void)draggedImage:(NSImage *)draggedImage movedTo:(NSPoint)screenPoint +{ + DragSourceDragEvent dsde(static_cast<OWeakObject*>(mDragSource), + new DragSourceContext(mDragSource), + mDragSource, + DNDConstants::ACTION_COPY, + DNDConstants::ACTION_COPY); + + mDragSource->mXDragSrcListener->dragOver(dsde); +} + +@end + + +DragSource::DragSource(): + WeakComponentImplHelper3<XDragSource, XInitialization, XServiceInfo>(m_aMutex), + mView(NULL), + mLastMouseEventBeforeStartDrag(nil), + m_MouseButton(0) +{ +} + + +DragSource::~DragSource() +{ + [(id <MouseEventListener>)mView unregisterMouseEventListener: mDragSourceHelper]; + [mDragSourceHelper release]; +} + + +void SAL_CALL DragSource::initialize(const Sequence< Any >& aArguments) + throw(Exception) +{ + if (aArguments.getLength() < 2) + { + throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Not enough parameter.")), + static_cast<OWeakObject*>(this)); + } + + Any pNSView = aArguments[1]; + sal_uInt64 tmp = 0; + pNSView >>= tmp; + mView = (NSView*)tmp; + + /* All SalFrameView the base class for all VCL system views inherits from + NSView in order to get mouse and other events. This is the only way to + get these events. In order to start a drag operation we need to provide + the mouse event which was the trigger. SalFrameView therefor implements + a hook mechanism so that we can get mouse events for our purpose. + */ + if (![mView respondsToSelector: @selector(registerMouseEventListener:)] || + ![mView respondsToSelector: @selector(unregisterMouseEventListener:)]) + { + throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Provided view doesn't support mouse listener")), + static_cast<OWeakObject*>(this)); + } + + mDragSourceHelper = [[DragSourceHelper alloc] initWithDragSource: this]; + + if (mDragSourceHelper == nil) + { + throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Cannot initialize DragSource")), + static_cast<OWeakObject*>(this)); + } + + [(id <MouseEventListener>)mView registerMouseEventListener: mDragSourceHelper]; +} + + +//---------------------------------------------------- +// XDragSource +//---------------------------------------------------- + +sal_Bool SAL_CALL DragSource::isDragImageSupported( ) + throw(RuntimeException) +{ + return true; +} + + +sal_Int32 SAL_CALL DragSource::getDefaultCursor( sal_Int8 /*dragAction*/ ) + throw( IllegalArgumentException, RuntimeException) +{ + return 0; +} + + +void SAL_CALL DragSource::startDrag(const DragGestureEvent& trigger, + sal_Int8 sourceActions, + sal_Int32 cursor, + sal_Int32 image, + const Reference<XTransferable >& transferable, + const Reference<XDragSourceListener >& listener ) + throw( RuntimeException) +{ + MutexGuard guard(m_aMutex); + + OSL_ASSERT(listener.is() && "DragSource::startDrag: No XDragSourceListener provided\n"); + OSL_ASSERT(transferable.is() && "DragSource::startDrag: No transferable provided\n"); + + trigger.Event >>= mMouseEvent; + m_MouseButton= mMouseEvent.Buttons; + mXDragSrcListener = listener; + mXCurrentContext = static_cast<XDragSourceContext*>(new DragSourceContext(this)); + auto_ptr<AquaClipboard> clipb(new AquaClipboard(NULL, false)); + g_XTransferable = transferable; + clipb->setContents(g_XTransferable, Reference<XClipboardOwner>()); + mDragSourceActions = sourceActions; + g_DragSourceView = mView; + + NSSize sz; + sz.width = 5; + sz.height = 5; + + NSImage* dragImage; + dragImage = [[NSImage alloc] initWithSize: sz]; + + NSRect bounds; + bounds.origin = NSMakePoint(0,0); + bounds.size = sz; + + [dragImage lockFocus]; + [[NSColor blackColor] set]; + [NSBezierPath fillRect: bounds]; + [dragImage unlockFocus]; + + NSPoint pInWnd = [mLastMouseEventBeforeStartDrag locationInWindow]; + NSPoint p; + p = [mView convertPoint: pInWnd fromView: nil]; + p.x = p.x - sz.width/2; + p.y = p.y - sz.height/2; + + // reset drop success flags + g_DropSuccessSet = false; + g_DropSuccess = false; + + [mView dragImage: dragImage + at: p + offset: NSMakeSize(0,0) + event: mLastMouseEventBeforeStartDrag + pasteboard: clipb->getPasteboard() + source: mDragSourceHelper + slideBack: 1]; + + [dragImage release]; + + g_XTransferable = Reference<XTransferable>(); + g_DragSourceView = nil; + + // reset drop success flags + g_DropSuccessSet = false; + g_DropSuccess = false; +} + + +// In order to initiate a D&D operation we need to +// provide the triggering mouse event which we get +// from the SalFrameView that is associated with +// this DragSource +void DragSource::saveMouseEvent(NSEvent* theEvent) +{ + if (mLastMouseEventBeforeStartDrag != nil) + { + [mLastMouseEventBeforeStartDrag release]; + } + + mLastMouseEventBeforeStartDrag = theEvent; +} + + +/* isLocal indicates whether or not the DnD operation is OOo + internal. + */ +unsigned int DragSource::getSupportedDragOperations(bool isLocal) const +{ + unsigned int srcActions = OfficeToSystemDragActions(mDragSourceActions); + + if (isLocal) + { + // Support NSDragOperation generic which means we can + // decide which D&D operation to choose. We map + // NSDragOperationGenric to DNDConstants::ACTION_DEFAULT + // in SystemToOfficeDragActions to signal this and + // use it in DropTarget::determineDropAction + srcActions |= NSDragOperationGeneric; + } + else + { + // Mask out link and move operations on external DnD + srcActions &= ~(NSDragOperationMove | NSDragOperationLink); + } + + return srcActions; +} + + +//################################ +// XServiceInfo +//################################ + +OUString SAL_CALL DragSource::getImplementationName( ) throw (RuntimeException) +{ + return dragSource_getImplementationName(); +} + + +sal_Bool SAL_CALL DragSource::supportsService( const OUString& ServiceName ) throw (RuntimeException) +{ + return ServiceName.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.OleDragSource"))); +} + + +Sequence< OUString > SAL_CALL DragSource::getSupportedServiceNames() throw (RuntimeException) +{ + return dragSource_getSupportedServiceNames(); +} + + + + diff --git a/vcl/aqua/source/dtrans/DragSource.hxx b/vcl/aqua/source/dtrans/DragSource.hxx new file mode 100644 index 000000000000..5d02b9874149 --- /dev/null +++ b/vcl/aqua/source/dtrans/DragSource.hxx @@ -0,0 +1,140 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _DRAGSOURCE_HXX_ +#define _DRAGSOURCE_HXX_ + +#include <com/sun/star/datatransfer/dnd/XDragSource.hpp> +#include <com/sun/star/datatransfer/dnd/XDragSourceContext.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <cppuhelper/compbase3.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/basemutex.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <osl/thread.h> +#include <com/sun/star/awt/MouseEvent.hpp> + +#include <boost/utility.hpp> + +#include <premac.h> +#import <Cocoa/Cocoa.h> +#include <postmac.h> + + +class DragSource; + +/* The functions declared in this protocol are actually + declared in vcl/aqua/inc/salframe.h. Because we want + to avoid importing VCL headers in UNO services and + on the other hand want to avoid warnings caused by + gcc complaining about unknowness of these functions + we declare them in a protocol here and cast at the + appropriate places. +*/ +@protocol MouseEventListener +-(void)registerMouseEventListener:(id)theHandler; +-(void)unregisterMouseEventListener:(id)theHandler; +@end + + +@interface DragSourceHelper : NSObject +{ + DragSource* mDragSource; +} + +-(DragSourceHelper*)initWithDragSource: (DragSource*) pds; + +-(void)mouseDown: (NSEvent*)theEvent; +-(void)mouseDragged: (NSEvent*)theEvent; + +-(unsigned int)draggingSourceOperationMaskForLocal:(MacOSBOOL)isLocal; +-(void)draggedImage:(NSImage*)anImage beganAt:(NSPoint)aPoint; +-(void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation; +-(void)draggedImage:(NSImage *)draggedImage movedTo:(NSPoint)screenPoint; + +@end + + +class DragSource : public ::cppu::BaseMutex, + public ::cppu::WeakComponentImplHelper3< com::sun::star::datatransfer::dnd::XDragSource, + com::sun::star::lang::XInitialization, + com::sun::star::lang::XServiceInfo >, + private ::boost::noncopyable +{ +public: + DragSource(); + virtual ~DragSource(); + + // XInitialization + virtual void SAL_CALL initialize( const com::sun::star::uno::Sequence< com::sun::star::uno::Any >& aArguments ) + throw(com::sun::star::uno::Exception/*, com::sun::star::uno::RuntimeException*/); + + // XDragSource + virtual sal_Bool SAL_CALL isDragImageSupported( ) throw(com::sun::star::uno::RuntimeException); + + virtual sal_Int32 SAL_CALL getDefaultCursor(sal_Int8 dragAction) + throw(com::sun::star::lang::IllegalArgumentException, com::sun::star::uno::RuntimeException); + + virtual void SAL_CALL startDrag( const com::sun::star::datatransfer::dnd::DragGestureEvent& trigger, + sal_Int8 sourceActions, + sal_Int32 cursor, + sal_Int32 image, + const com::sun::star::uno::Reference< com::sun::star::datatransfer::XTransferable >& transferable, + const com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDragSourceListener >& listener ) + throw(com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual rtl::OUString SAL_CALL getImplementationName() throw (com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService(const rtl::OUString& ServiceName) throw (com::sun::star::uno::RuntimeException); + virtual com::sun::star::uno::Sequence< rtl::OUString > SAL_CALL getSupportedServiceNames() throw (com::sun::star::uno::RuntimeException); + + virtual void saveMouseEvent(NSEvent* theEvent); + virtual unsigned int getSupportedDragOperations(bool isLocal) const; + +public: + // The context notifies the XDragSourceListeners + com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDragSourceContext > mXCurrentContext; + + id mView; + NSEvent* mLastMouseEventBeforeStartDrag; + DragSourceHelper* mDragSourceHelper; + com::sun::star::awt::MouseEvent mMouseEvent; + com::sun::star::uno::Reference< com::sun::star::datatransfer::XTransferable > mXTransferable; + com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDragSourceListener > mXDragSrcListener; + // The mouse button that set off the drag and drop operation + short m_MouseButton; + sal_Int8 mDragSourceActions; + + static com::sun::star::uno::Reference< com::sun::star::datatransfer::XTransferable > g_XTransferable; + static NSView* g_DragSourceView; + static bool g_DropSuccessSet; + static bool g_DropSuccess; + +}; + + +#endif diff --git a/vcl/aqua/source/dtrans/DragSourceContext.cxx b/vcl/aqua/source/dtrans/DragSourceContext.cxx new file mode 100644 index 000000000000..cd70dc55c896 --- /dev/null +++ b/vcl/aqua/source/dtrans/DragSourceContext.cxx @@ -0,0 +1,74 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + + +#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> + +#include "DragSourceContext.hxx" +#include <rtl/unload.h> + + +using namespace com::sun::star::datatransfer::dnd; +using namespace com::sun::star::datatransfer::dnd::DNDConstants; +using namespace com::sun::star::uno; +using namespace cppu; + +DragSourceContext::DragSourceContext( DragSource* pSource) : + WeakComponentImplHelper1<XDragSourceContext>(m_aMutex), + m_pDragSource( pSource) +{ +} + +DragSourceContext::~DragSourceContext() +{ +} + +sal_Int32 SAL_CALL DragSourceContext::getCurrentCursor( ) + throw( RuntimeException) +{ + return 0; +} + +void SAL_CALL DragSourceContext::setCursor( sal_Int32 /*cursorId*/ ) + throw( RuntimeException) +{ +} + +void SAL_CALL DragSourceContext::setImage( sal_Int32 /*imageId*/ ) + throw( RuntimeException) +{ +} + +void SAL_CALL DragSourceContext::transferablesFlavorsChanged( ) + throw( RuntimeException) +{ +} + + diff --git a/vcl/aqua/source/dtrans/DragSourceContext.hxx b/vcl/aqua/source/dtrans/DragSourceContext.hxx new file mode 100644 index 000000000000..5d84c165d851 --- /dev/null +++ b/vcl/aqua/source/dtrans/DragSourceContext.hxx @@ -0,0 +1,72 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _DRAGSOURCECONTEXT_HXX_ +#define _DRAGSOURCECONTEXT_HXX_ + +#include <cppuhelper/implbase1.hxx> +#include <com/sun/star/datatransfer/dnd/XDragSourceContext.hpp> +#include <cppuhelper/compbase1.hxx> +#include <cppuhelper/basemutex.hxx> + +#include <boost/utility.hpp> + +#include "DragSource.hxx" + +// This class fires events to XDragSourceListener implementations. +// Of that interface only dragDropEnd and dropActionChanged are called. +// The functions dragEnter, dragExit and dragOver are not supported +// currently. +// An instance of SourceContext only lives as long as the drag and drop +// operation lasts. +class DragSourceContext: public cppu::BaseMutex, + public cppu::WeakComponentImplHelper1<com::sun::star::datatransfer::dnd::XDragSourceContext>, + private ::boost::noncopyable +{ +public: + DragSourceContext(DragSource* pSource); + ~DragSourceContext(); + + virtual sal_Int32 SAL_CALL getCurrentCursor( ) + throw( com::sun::star::uno::RuntimeException); + + virtual void SAL_CALL setCursor( sal_Int32 cursorId ) + throw( com::sun::star::uno::RuntimeException); + + virtual void SAL_CALL setImage( sal_Int32 imageId ) + throw( com::sun::star::uno::RuntimeException); + + virtual void SAL_CALL transferablesFlavorsChanged( ) + throw( com::sun::star::uno::RuntimeException); + +private: + DragSource* m_pDragSource; +}; + + + +#endif diff --git a/vcl/aqua/source/dtrans/DropTarget.cxx b/vcl/aqua/source/dtrans/DropTarget.cxx new file mode 100644 index 000000000000..c928d66e156d --- /dev/null +++ b/vcl/aqua/source/dtrans/DropTarget.cxx @@ -0,0 +1,599 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/datatransfer/dnd/DropTargetDragEnterEvent.hpp> +#include <rtl/unload.h> + +#ifndef COMPHELPER_MAKESEQUENCE_HXX_INCLUDED +#include "comphelper/makesequence.hxx" +#endif +#include <cppuhelper/interfacecontainer.hxx> + +#include "aqua_clipboard.hxx" +#include "DropTarget.hxx" +#include "DragActionConversion.hxx" + +#include "DragSource.hxx" + +#include <rtl/ustring.h> +#include <stdio.h> + +#include <premac.h> +#include <Carbon/Carbon.h> +#include <postmac.h> + + +using namespace rtl; +using namespace cppu; +using namespace osl; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::dnd; +using namespace com::sun::star::datatransfer::dnd::DNDConstants; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace comphelper; + +OUString dropTarget_getImplementationName() +{ + return OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1")); +} + + +Sequence<OUString> dropTarget_getSupportedServiceNames() +{ + return makeSequence(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.OleDropTarget"))); +} + + +namespace /* private */ +{ + // Cocoa's coordinate system has its origin lower-left, VCL's + // coordinate system upper-left hence we need to transform + // coordinates + + inline void CocoaToVCL(NSPoint& rPoint, const NSRect& bounds) + { + rPoint.y = bounds.size.height - rPoint.y; + } + + inline void CocoaToVCL(NSRect& rRect, const NSRect& bounds) + { + rRect.origin.y = bounds.size.height - (rRect.origin.y + rRect.size.height); + } +} + + +@implementation DropTargetHelper + + +-(DropTargetHelper*)initWithDropTarget:(DropTarget*)pdt +{ + self = [super init]; + + if (self) + { + mDropTarget = pdt; + } + + return self; +} + + +-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +{ + return mDropTarget->draggingEntered(sender); +} + + +-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender +{ + return mDropTarget->draggingUpdated(sender); +} + + +-(void)draggingExited:(id <NSDraggingInfo>)sender +{ + mDropTarget->draggingExited(sender); +} + + +-(MacOSBOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender +{ + return mDropTarget->prepareForDragOperation(sender); +} + + +-(MacOSBOOL)performDragOperation:(id <NSDraggingInfo>)sender +{ + return mDropTarget->performDragOperation(sender); +} + + +-(void)concludeDragOperation:(id <NSDraggingInfo>)sender +{ + mDropTarget->concludeDragOperation(sender); +} + + +@end + + +DropTarget::DropTarget() : + WeakComponentImplHelper5<XInitialization, XDropTarget, XDropTargetDragContext, XDropTargetDropContext, XServiceInfo>(m_aMutex), + mDropTargetHelper(nil), + mbActive(false), + mDragSourceSupportedActions(DNDConstants::ACTION_NONE), + mSelectedDropAction(DNDConstants::ACTION_NONE), + mDefaultActions(DNDConstants::ACTION_COPY_OR_MOVE | DNDConstants::ACTION_LINK | DNDConstants::ACTION_DEFAULT) +{ + mDataFlavorMapper = DataFlavorMapperPtr_t(new DataFlavorMapper()); +} + + +DropTarget::~DropTarget() +{ + [(id <DraggingDestinationHandler>)mView unregisterDraggingDestinationHandler:mDropTargetHelper]; + [mDropTargetHelper release]; +} + + +sal_Int8 DropTarget::determineDropAction(sal_Int8 dropActions, id sender) const +{ + sal_Int8 dropAct = dropActions; + bool srcAndDestEqual = false; + + if ([sender draggingSource] != nil) + { + // Internal DnD + NSView* destView = [[sender draggingDestinationWindow] contentView]; + srcAndDestEqual = (DragSource::g_DragSourceView == destView); + } + + // If ACTION_DEFAULT is set this means NSDragOperationGeneric + // has been set and we map this to ACTION_MOVE or ACTION_COPY + // depending on whether or not source and dest are equal, + // this hopefully satisfies all parties + if( (dropActions == DNDConstants::ACTION_DEFAULT) + || ((dropActions == mDragSourceSupportedActions) + && !(~mDragSourceSupportedActions & DNDConstants::ACTION_COPY_OR_MOVE ) ) ) + { + dropAct = srcAndDestEqual ? DNDConstants::ACTION_MOVE : + DNDConstants::ACTION_COPY; + } + // if more than one drop actions have been specified + // set ACTION_DEFAULT in order to let the drop target + // decide which one to use + else if (dropActions != DNDConstants::ACTION_NONE && + dropActions != DNDConstants::ACTION_MOVE && + dropActions != DNDConstants::ACTION_COPY && + dropActions != DNDConstants::ACTION_LINK) + { + if (srcAndDestEqual) + { + dropAct = dropActions; + } + else // source and destination are different + { + if (dropActions & DNDConstants::ACTION_COPY) + dropAct = DNDConstants::ACTION_COPY; + else if (dropActions & DNDConstants::ACTION_MOVE) + dropAct = DNDConstants::ACTION_MOVE; + else if (dropActions & DNDConstants::ACTION_LINK) + dropAct = DNDConstants::ACTION_LINK; + } + + dropAct |= DNDConstants::ACTION_DEFAULT; + } + + return dropAct; +} + + +NSDragOperation DropTarget::draggingEntered(id sender) +{ + // Initially when DnD will be started no modifier key can be pressed yet + // thus we are getting all actions that the drag source supports, we save + // this value because later the system masks the drag source actions if + // a modifier key will be pressed + mDragSourceSupportedActions = SystemToOfficeDragActions([sender draggingSourceOperationMask]); + + // Only if the drop target is really interessted in the drag actions + // supported by the source + if (mDragSourceSupportedActions & mDefaultActions) + { + sal_Int8 currentAction = determineDropAction(mDragSourceSupportedActions, sender); + + NSRect bounds = [mView bounds]; + NSPoint dragLocation = [sender draggedImageLocation]; + + CocoaToVCL(dragLocation, bounds); + + sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x); + sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y); + + NSPasteboard* dragPboard = [sender draggingPasteboard]; + mXCurrentDragClipboard = new AquaClipboard(dragPboard, false); + + Reference<XTransferable> xTransferable = DragSource::g_XTransferable.is() ? + DragSource::g_XTransferable : mXCurrentDragClipboard->getContents(); + + DropTargetDragEnterEvent dtdee(static_cast<OWeakObject*>(this), + 0, + this, + currentAction, + posX, + posY, + mDragSourceSupportedActions, + xTransferable->getTransferDataFlavors()); + + fire_dragEnter(dtdee); + } + + return OfficeToSystemDragActions(mSelectedDropAction); +} + + +NSDragOperation DropTarget::draggingUpdated(id sender) +{ + sal_Int8 currentDragSourceActions = + SystemToOfficeDragActions([sender draggingSourceOperationMask]); + NSDragOperation dragOp = NSDragOperationNone; + + if (currentDragSourceActions & mDefaultActions) + { + sal_Int8 currentAction = determineDropAction(currentDragSourceActions, sender); + NSRect bounds = [mView bounds]; + NSPoint dragLocation = [sender draggedImageLocation]; + + CocoaToVCL(dragLocation, bounds); + + sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x); + sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y); + + DropTargetDragEvent dtde(static_cast<OWeakObject*>(this), + 0, + this, + currentAction, + posX, + posY, + mDragSourceSupportedActions); + + fire_dragOver(dtde); + + // drag over callbacks likely have rendered something + [mView setNeedsDisplay: TRUE]; + + dragOp = OfficeToSystemDragActions(mSelectedDropAction); + + //NSLog(@"Drag update: Source actions: %x proposed action %x selected action %x", mDragSourceSupportedActions, currentAction, mSelectedDropAction); + } + + // Weird but it appears as if there is no method in Cocoa + // to create a kThemeCopyArrowCursor hence we have to use + // Carbon to do it + if (dragOp == NSDragOperationNone) + SetThemeCursor(kThemeNotAllowedCursor); + else if (dragOp == NSDragOperationCopy) + SetThemeCursor(kThemeCopyArrowCursor); + else + SetThemeCursor(kThemeArrowCursor); + + return dragOp; +} + + + void DropTarget::draggingExited(id sender) + { + DropTargetEvent dte(static_cast<OWeakObject*>(this), 0); + fire_dragExit(dte); + mDragSourceSupportedActions = DNDConstants::ACTION_NONE; + mSelectedDropAction = DNDConstants::ACTION_NONE; + SetThemeCursor(kThemeArrowCursor); + } + + + MacOSBOOL DropTarget::prepareForDragOperation(id sender) + { + return 1; + } + + +MacOSBOOL DropTarget::performDragOperation(id sender) +{ + bool bSuccess = false; + + if (mSelectedDropAction != DNDConstants::ACTION_NONE) + { + Reference<XTransferable> xTransferable = DragSource::g_XTransferable; + + if (!DragSource::g_XTransferable.is()) + { + xTransferable = mXCurrentDragClipboard->getContents(); + } + + NSRect bounds = [mView bounds]; + NSPoint dragLocation = [sender draggedImageLocation]; + + CocoaToVCL(dragLocation, bounds); + + sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x); + sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y); + + DropTargetDropEvent dtde(static_cast<OWeakObject*>(this), + 0, + this, + mSelectedDropAction, + posX, + posY, + mDragSourceSupportedActions, + xTransferable); + + fire_drop(dtde); + + bSuccess = true; + } + + return bSuccess; +} + + + void DropTarget::concludeDragOperation(id sender) + { + mDragSourceSupportedActions = DNDConstants::ACTION_NONE; + mSelectedDropAction = DNDConstants::ACTION_NONE; + mXCurrentDragClipboard = Reference<XClipboard>(); + SetThemeCursor(kThemeArrowCursor); + } + + + // called from WeakComponentImplHelperX::dispose + // WeakComponentImplHelper calls disposing before it destroys + // itself. + void SAL_CALL DropTarget::disposing() + { + } + + + void SAL_CALL DropTarget::initialize(const Sequence< Any >& aArguments) + throw(Exception) + { + if (aArguments.getLength() < 2) + { + throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM("DropTarget::initialize: Cannot install window event handler")), + static_cast<OWeakObject*>(this)); + } + + Any pNSView = aArguments[0]; + sal_uInt64 tmp = 0; + pNSView >>= tmp; + mView = (id)tmp; + + mDropTargetHelper = [[DropTargetHelper alloc] initWithDropTarget: this]; + + [(id <DraggingDestinationHandler>)mView registerDraggingDestinationHandler:mDropTargetHelper]; + [mView registerForDraggedTypes: mDataFlavorMapper->getAllSupportedPboardTypes()]; + + id wnd = [mView window]; + NSWindow* parentWnd = [wnd parentWindow]; + unsigned int topWndStyle = (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask); + unsigned int wndStyles = [wnd styleMask] & topWndStyle; + + if (parentWnd == nil && (wndStyles == topWndStyle)) + { + [wnd registerDraggingDestinationHandler:mDropTargetHelper]; + [wnd registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, nil]]; + } + } + + + void SAL_CALL DropTarget::addDropTargetListener(const Reference<XDropTargetListener>& dtl) + throw(RuntimeException) + { + rBHelper.addListener(::getCppuType(&dtl), dtl); + } + + + void SAL_CALL DropTarget::removeDropTargetListener(const Reference<XDropTargetListener>& dtl) + throw(RuntimeException) + { + rBHelper.removeListener(::getCppuType(&dtl), dtl); + } + + + sal_Bool SAL_CALL DropTarget::isActive( ) throw(RuntimeException) + { + return mbActive; + } + + + void SAL_CALL DropTarget::setActive(sal_Bool active) throw(RuntimeException) + { + mbActive = active; + } + + + sal_Int8 SAL_CALL DropTarget::getDefaultActions() throw(RuntimeException) + { + return mDefaultActions; + } + + + void SAL_CALL DropTarget::setDefaultActions(sal_Int8 actions) throw(RuntimeException) + { + OSL_ENSURE( actions < 8, "No valid default actions"); + mDefaultActions= actions; + } + + + // XDropTargetDragContext + + void SAL_CALL DropTarget::acceptDrag(sal_Int8 dragOperation) throw (RuntimeException) + { + mSelectedDropAction = dragOperation; + } + + + void SAL_CALL DropTarget::rejectDrag() throw (RuntimeException) + { + mSelectedDropAction = DNDConstants::ACTION_NONE; + } + + + //XDropTargetDropContext + + void SAL_CALL DropTarget::acceptDrop(sal_Int8 dropOperation) throw( RuntimeException) + { + mSelectedDropAction = dropOperation; + } + + + void SAL_CALL DropTarget::rejectDrop() throw (RuntimeException) + { + mSelectedDropAction = DNDConstants::ACTION_NONE; + } + + + void SAL_CALL DropTarget::dropComplete(sal_Bool success) throw (RuntimeException) + { + // Reset the internal transferable used as shortcut in case this is + // an internal D&D operation + DragSource::g_XTransferable = Reference<XTransferable>(); + DragSource::g_DropSuccessSet = true; + DragSource::g_DropSuccess = success; + } + + + void DropTarget::fire_drop( const DropTargetDropEvent& dte) + { + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (Reference<XDropTargetListener>* )0 ) ); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + + try { listener->drop( dte); } + catch(RuntimeException&) {} + } + } + } + + + void DropTarget::fire_dragEnter(const DropTargetDragEnterEvent& e) + { + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (Reference<XDropTargetListener>* )0 ) ); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + + try { listener->dragEnter( e); } + catch (RuntimeException&) {} + } + } + } + + + void DropTarget::fire_dragExit(const DropTargetEvent& dte) + { + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (Reference<XDropTargetListener>* )0 ) ); + + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + + try { listener->dragExit( dte); } + catch (RuntimeException&) {} + } + } + } + + + void DropTarget::fire_dragOver(const DropTargetDragEvent& dtde) + { + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (Reference<XDropTargetListener>* )0 ) ); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer ); + while( iter.hasMoreElements()) + { + Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + + try { listener->dragOver( dtde); } + catch (RuntimeException&) {} + } + } + } + + + void DropTarget::fire_dropActionChanged(const DropTargetDragEvent& dtde) + { + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (Reference<XDropTargetListener>* )0 ) ); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + + try { listener->dropActionChanged( dtde); } + catch (RuntimeException&) {} + } + } + } + + + // XServiceInfo + + OUString SAL_CALL DropTarget::getImplementationName() throw (RuntimeException) + { + return dropTarget_getImplementationName(); + } + + + sal_Bool SAL_CALL DropTarget::supportsService( const OUString& ServiceName ) throw (RuntimeException) + { + return ServiceName.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.OleDropTarget"))); + } + + + Sequence< OUString > SAL_CALL DropTarget::getSupportedServiceNames( ) throw (RuntimeException) + { + return dropTarget_getSupportedServiceNames(); + } + diff --git a/vcl/aqua/source/dtrans/DropTarget.hxx b/vcl/aqua/source/dtrans/DropTarget.hxx new file mode 100644 index 000000000000..6baa8bb69d01 --- /dev/null +++ b/vcl/aqua/source/dtrans/DropTarget.hxx @@ -0,0 +1,169 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _DROPTARGET_HXX_ +#define _DROPTARGET_HXX_ + +#include "DataFlavorMapping.hxx" +#include <cppuhelper/compbase5.hxx> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp> + +#ifndef _COM_SUN_STAR_DATATRANSFER_DND_XDROPTARGETLISTENR_HPP_ +#include <com/sun/star/datatransfer/dnd/XDropTargetListener.hpp> +#endif +#include <com/sun/star/datatransfer/dnd/DropTargetDragEnterEvent.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTargetDragContext.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTargetDropContext.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/basemutex.hxx> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> + +#include <boost/utility.hpp> + +#include <premac.h> +#import <Cocoa/Cocoa.h> +#include <postmac.h> + +class DropTarget; + +/* The functions declared in this protocol are actually + declared in vcl/aqua/inc/salframe.h. Because we want + to avoid importing VCL headers in UNO services and + on the other hand want to avoid warnings caused by + gcc complaining about unknowness of these functions + we declare them in a protocol here and cast at the + appropriate places. +*/ +@protocol DraggingDestinationHandler +-(void)registerDraggingDestinationHandler:(id)theHandler; +-(void)unregisterDraggingDestinationHandler:(id)theHandler; +@end + + +@interface DropTargetHelper : NSObject +{ + DropTarget* mDropTarget; +} + +-(DropTargetHelper*)initWithDropTarget:(DropTarget*)pdt; + +-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; +-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; +-(void)draggingExited:(id <NSDraggingInfo>)sender; +-(MacOSBOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender; +-(MacOSBOOL)performDragOperation:(id <NSDraggingInfo>)sender; +-(void)concludeDragOperation:(id <NSDraggingInfo>)sender; + +@end + + +class DropTarget: public cppu::BaseMutex, + public cppu::WeakComponentImplHelper5< com::sun::star::lang::XInitialization, + com::sun::star::datatransfer::dnd::XDropTarget, + com::sun::star::datatransfer::dnd::XDropTargetDragContext, + com::sun::star::datatransfer::dnd::XDropTargetDropContext, + com::sun::star::lang::XServiceInfo >, + private boost::noncopyable +{ +public: + DropTarget(); + virtual ~DropTarget(); + + // Overrides WeakComponentImplHelper::disposing which is called by + // WeakComponentImplHelper::dispose + // Must be called. + virtual void SAL_CALL disposing(); + + // XInitialization + virtual void SAL_CALL initialize( const com::sun::star::uno::Sequence< com::sun::star::uno::Any >& aArguments ) + throw(com::sun::star::uno::Exception); + + // XDropTarget + virtual void SAL_CALL addDropTargetListener( const com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDropTargetListener >& dtl ) + throw(com::sun::star::uno::RuntimeException); + + virtual void SAL_CALL removeDropTargetListener( const com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDropTargetListener >& dtl ) + throw(com::sun::star::uno::RuntimeException); + + // Default is not active + virtual sal_Bool SAL_CALL isActive() throw(com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setActive(sal_Bool isActive) throw(com::sun::star::uno::RuntimeException); + virtual sal_Int8 SAL_CALL getDefaultActions() throw(com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setDefaultActions(sal_Int8 actions) throw(com::sun::star::uno::RuntimeException); + + // XDropTargetDragContext + virtual void SAL_CALL acceptDrag(sal_Int8 dragOperation) throw(com::sun::star::uno::RuntimeException); + virtual void SAL_CALL rejectDrag() throw(com::sun::star::uno::RuntimeException); + + // XDropTargetDragContext + virtual void SAL_CALL acceptDrop(sal_Int8 dropOperation) throw (com::sun::star::uno::RuntimeException); + virtual void SAL_CALL rejectDrop() throw (com::sun::star::uno::RuntimeException); + virtual void SAL_CALL dropComplete(sal_Bool success) throw (com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual rtl::OUString SAL_CALL getImplementationName() throw (com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService(const rtl::OUString& ServiceName) throw (com::sun::star::uno::RuntimeException); + virtual com::sun::star::uno::Sequence< rtl::OUString > SAL_CALL getSupportedServiceNames() throw (com::sun::star::uno::RuntimeException); + + // NSDraggingDestination protocol functions + virtual NSDragOperation draggingEntered(id sender); + virtual NSDragOperation draggingUpdated(id sender); + virtual void draggingExited(id sender); + virtual MacOSBOOL prepareForDragOperation(id sender); + virtual MacOSBOOL performDragOperation(id sender); + virtual void concludeDragOperation(id sender); + + /* If multiple actions are supported by the drag source and + the user did not choose a specific action by pressing a + modifier key choose a default action to be proposed to + the application. + */ + sal_Int8 determineDropAction(sal_Int8 dropActions, id sender) const; + +private: + void fire_drop(const com::sun::star::datatransfer::dnd::DropTargetDropEvent& dte); + void fire_dragEnter(const com::sun::star::datatransfer::dnd::DropTargetDragEnterEvent& dtdee); + void fire_dragExit(const com::sun::star::datatransfer::dnd::DropTargetEvent& dte); + void fire_dragOver(const com::sun::star::datatransfer::dnd::DropTargetDragEvent& dtde); + void fire_dropActionChanged(const com::sun::star::datatransfer::dnd::DropTargetDragEvent& dtde); + +private: + com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDropTargetDragContext > mXCurrentDragContext; + com::sun::star::uno::Reference< com::sun::star::datatransfer::dnd::XDropTargetDropContext > mXCurrentDropContext; + com::sun::star::uno::Reference< com::sun::star::datatransfer::clipboard::XClipboard > mXCurrentDragClipboard; + DataFlavorMapperPtr_t mDataFlavorMapper; + id mView; + DropTargetHelper* mDropTargetHelper; + bool mbActive; + sal_Int8 mDragSourceSupportedActions; + sal_Int8 mSelectedDropAction; + sal_Int8 mDefaultActions; +}; + +#endif diff --git a/vcl/aqua/source/dtrans/HtmlFmtFlt.cxx b/vcl/aqua/source/dtrans/HtmlFmtFlt.cxx new file mode 100644 index 000000000000..3f558b0a5b4f --- /dev/null +++ b/vcl/aqua/source/dtrans/HtmlFmtFlt.cxx @@ -0,0 +1,147 @@ +#include "HtmlFmtFlt.hxx" + +#include <rtl/string.h> + +#include <string> +#include <sstream> +#include <vector> +#include <iomanip> + +#include <boost/assert.hpp> + +using namespace com::sun::star::uno; + +//------------------------------------------------------------------------------ +// converts the openoffice text/html clipboard format to the HTML Format +// well known under MS Windows +// the MS HTML Format has a header before the real html data +// +// Version:1.0 Version number of the clipboard. Staring is 0.9 +// StartHTML: Byte count from the beginning of the clipboard to the start +// of the context, or -1 if no context +// EndHTML: Byte count from the beginning of the clipboard to the end +// of the context, or -1 if no context +// StartFragment: Byte count from the beginning of the clipboard to the +// start of the fragment +// EndFragment: Byte count from the beginning of the clipboard to the +// end of the fragment +// StartSelection: Byte count from the beginning of the clipboard to the +// start of the selection +// EndSelection: Byte count from the beginning of the clipboard to the +// end of the selection +// +// StartSelection and EndSelection are optional +// The fragment should be preceded and followed by the HTML comments +// <!--StartFragment--> and <!--EndFragment--> (no space between !-- and the +// text +//------------------------------------------------------------------------------ + +namespace // private +{ +std::string GetHtmlFormatHeader(size_t startHtml, size_t endHtml, size_t startFragment, size_t endFragment) +{ + std::ostringstream htmlHeader; + htmlHeader << "Version:1.0" << '\r' << '\n'; + htmlHeader << "StartHTML:" << std::setw(10) << std::setfill('0') << std::dec << startHtml << '\r' << '\n'; + htmlHeader << "EndHTML:" << std::setw(10) << std::setfill('0') << std::dec << endHtml << '\r' << '\n'; + htmlHeader << "StartFragment:" << std::setw(10) << std::setfill('0') << std::dec << startFragment << '\r' << '\n'; + htmlHeader << "EndFragment:" << std::setw(10) << std::setfill('0') << std::dec << endFragment << '\r' << '\n'; + return htmlHeader.str(); +} + +} // namespace private + + +// the office allways writes the start and end html tag in upper cases and +// without spaces both tags don't allow parameters +const std::string TAG_HTML = std::string("<HTML>"); +const std::string TAG_END_HTML = std::string("</HTML>"); + +// The body tag may have parameters so we need to search for the +// closing '>' manually e.g. <BODY param> #92840# +const std::string TAG_BODY = std::string("<BODY"); +const std::string TAG_END_BODY = std::string("</BODY"); + +Sequence<sal_Int8> SAL_CALL TextHtmlToHTMLFormat(Sequence<sal_Int8>& aTextHtml) +{ + OSL_ASSERT(aTextHtml.getLength() > 0); + + if (!(aTextHtml.getLength() > 0)) + return Sequence<sal_Int8>(); + + // fill the buffer with dummy values to calc the exact length + std::string dummyHtmlHeader = GetHtmlFormatHeader(0, 0, 0, 0); + size_t lHtmlFormatHeader = dummyHtmlHeader.length(); + + std::string textHtml( + reinterpret_cast<const sal_Char*>(aTextHtml.getConstArray()), + reinterpret_cast<const sal_Char*>(aTextHtml.getConstArray()) + aTextHtml.getLength()); + + std::string::size_type nStartHtml = textHtml.find(TAG_HTML) + lHtmlFormatHeader - 1; // we start one before '<HTML>' Word 2000 does also so + std::string::size_type nEndHtml = textHtml.find(TAG_END_HTML) + lHtmlFormatHeader + TAG_END_HTML.length() + 1; // our SOffice 5.2 wants 2 behind </HTML>? + + // The body tag may have parameters so we need to search for the + // closing '>' manually e.g. <BODY param> #92840# + std::string::size_type nStartFragment = textHtml.find(">", textHtml.find(TAG_BODY)) + lHtmlFormatHeader + 1; + std::string::size_type nEndFragment = textHtml.find(TAG_END_BODY) + lHtmlFormatHeader; + + std::string htmlFormat = GetHtmlFormatHeader(nStartHtml, nEndHtml, nStartFragment, nEndFragment); + htmlFormat += textHtml; + + Sequence<sal_Int8> byteSequence(htmlFormat.length() + 1); // space the trailing '\0' + rtl_zeroMemory(byteSequence.getArray(), byteSequence.getLength()); + + rtl_copyMemory( + static_cast<void*>(byteSequence.getArray()), + static_cast<const void*>(htmlFormat.c_str()), + htmlFormat.length()); + + return byteSequence; +} + +const char* HtmlStartTag = "<html"; + +Sequence<sal_Int8> HTMLFormatToTextHtml(const Sequence<sal_Int8>& aHTMLFormat) +{ + BOOST_ASSERT(isHTMLFormat(aHTMLFormat) && "No HTML Format provided"); + + Sequence<sal_Int8>& nonconstHTMLFormatRef = const_cast< Sequence<sal_Int8>& >(aHTMLFormat); + sal_Char* dataStart = reinterpret_cast<sal_Char*>(nonconstHTMLFormatRef.getArray()); + sal_Char* dataEnd = dataStart + nonconstHTMLFormatRef.getLength() - 1; + const sal_Char* htmlStartTag = strcasestr(dataStart, HtmlStartTag); + + BOOST_ASSERT(htmlStartTag && "Seems to be no HTML at all"); + + // It doesn't seem to be HTML? Well then simply return what has been + // provided in non-debug builds + if (htmlStartTag == NULL) + { + return aHTMLFormat; + } + + sal_Int32 len = dataEnd - htmlStartTag; + Sequence<sal_Int8> plainHtmlData(len); + + rtl_copyMemory(static_cast<void*>(plainHtmlData.getArray()), htmlStartTag, len); + + return plainHtmlData; +} + +/* A simple format detection. We are just comparing the first few bytes + of the provided byte sequence to see whether or not it is the MS + Office Html format. If it shows that this is not reliable enough we + can improve this +*/ +const char HtmlFormatStart[] = "Version:"; +int HtmlFormatStartLen = (sizeof(HtmlFormatStart) - 1); + +bool isHTMLFormat(const Sequence<sal_Int8>& aHtmlSequence) +{ + if (aHtmlSequence.getLength() < HtmlFormatStartLen) + return false; + + return rtl_str_compareIgnoreAsciiCase_WithLength(HtmlFormatStart, + HtmlFormatStartLen, + reinterpret_cast<const sal_Char*>(aHtmlSequence.getConstArray()), + HtmlFormatStartLen) == 0; +} diff --git a/vcl/aqua/source/dtrans/HtmlFmtFlt.hxx b/vcl/aqua/source/dtrans/HtmlFmtFlt.hxx new file mode 100644 index 000000000000..49f0cc70590c --- /dev/null +++ b/vcl/aqua/source/dtrans/HtmlFmtFlt.hxx @@ -0,0 +1,20 @@ +#ifndef INCLUDED_HTMLFMTFLT_HXX +#define INCLUDED_HTMLFMTFLT_HXX + +#include <com/sun/star/uno/Sequence.hxx> + +/* Transform plain HTML into the format expected by MS Office. + */ +com::sun::star::uno::Sequence<sal_Int8> TextHtmlToHTMLFormat(com::sun::star::uno::Sequence<sal_Int8>& aTextHtml); + +/* Transform the MS Office HTML format into plain HTML. + */ +com::sun::star::uno::Sequence<sal_Int8> HTMLFormatToTextHtml(const com::sun::star::uno::Sequence<sal_Int8>& aHTMLFormat); + +/* Detects whether the given byte sequence contains the MS Office Html format. + + @returns True if the MS Office Html format will be detected False otherwise. + */ +bool isHTMLFormat (const com::sun::star::uno::Sequence<sal_Int8>& aHtmlSequence); + +#endif diff --git a/vcl/aqua/source/dtrans/OSXTransferable.cxx b/vcl/aqua/source/dtrans/OSXTransferable.cxx new file mode 100644 index 000000000000..2e6b327de446 --- /dev/null +++ b/vcl/aqua/source/dtrans/OSXTransferable.cxx @@ -0,0 +1,215 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <sal/types.h> + +#ifndef _TRANSFERABLE_HXX_ +#include "OSXTransferable.hxx" +#endif + +#include "DataFlavorMapping.hxx" + +using namespace rtl; +using namespace std; +using namespace osl; +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::io; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; + +const Type CPPUTYPE_SEQINT8 = getCppuType((Sequence<sal_Int8>*)0); +const Type CPPUTYPE_OUSTRING = getCppuType((OUString*)0); + +namespace // private +{ + bool isValidFlavor( const DataFlavor& aFlavor ) + { + size_t len = aFlavor.MimeType.getLength(); + Type dtype = aFlavor.DataType; + return ((len > 0) && ((dtype == CPPUTYPE_SEQINT8) || (dtype == CPPUTYPE_OUSTRING))); + } + +} // namespace private + + +OSXTransferable::OSXTransferable(const Reference<XMimeContentTypeFactory> rXMimeCntFactory, + DataFlavorMapperPtr_t pDataFlavorMapper, + NSPasteboard* pasteboard) : + mrXMimeCntFactory(rXMimeCntFactory), + mDataFlavorMapper(pDataFlavorMapper), + mPasteboard(pasteboard) +{ + [mPasteboard retain]; + + initClipboardItemList(); +} + + +OSXTransferable::~OSXTransferable() +{ + [mPasteboard release]; +} + + +Any SAL_CALL OSXTransferable::getTransferData( const DataFlavor& aFlavor ) + throw( UnsupportedFlavorException, IOException, RuntimeException ) +{ + if (!isValidFlavor(aFlavor) || !isDataFlavorSupported(aFlavor)) + { + throw UnsupportedFlavorException(OUString(RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Unsupported data flavor")), + static_cast<XTransferable*>(this)); + } + + NSString* sysFormat = + (aFlavor.MimeType.compareToAscii( "image/bmp", 9 ) == 0) + ? mDataFlavorMapper->openOfficeImageToSystemFlavor( mPasteboard ) + : mDataFlavorMapper->openOfficeToSystemFlavor(aFlavor); + DataProviderPtr_t dp; + + if ([sysFormat caseInsensitiveCompare: NSFilenamesPboardType] == NSOrderedSame) + { + NSArray* sysData = [mPasteboard propertyListForType: sysFormat]; + dp = mDataFlavorMapper->getDataProvider(sysFormat, sysData); + } + else + { + NSData* sysData = [mPasteboard dataForType: sysFormat]; + dp = mDataFlavorMapper->getDataProvider(sysFormat, sysData); + } + + if (dp.get() == NULL) + { + throw UnsupportedFlavorException(OUString(RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Unsupported data flavor")), + static_cast<XTransferable*>(this)); + } + + return dp->getOOoData(); +} + + +bool OSXTransferable::isUnicodeText(const DataFlavor& flavor) +{ + return (flavor.DataType == CPPUTYPE_OUSTRING); +} + + +Sequence< DataFlavor > SAL_CALL OSXTransferable::getTransferDataFlavors( ) + throw( RuntimeException ) +{ + return mFlavorList; +} + + +sal_Bool SAL_CALL OSXTransferable::isDataFlavorSupported(const DataFlavor& aFlavor) + throw( RuntimeException ) +{ + for (sal_Int32 i = 0; i < mFlavorList.getLength(); i++) + if (compareDataFlavors(aFlavor, mFlavorList[i])) + return sal_True; + + return sal_False; +} + + +void OSXTransferable::initClipboardItemList() +{ + NSArray* pboardFormats = [mPasteboard types]; + + if (pboardFormats == NULL) + { + throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Cannot get clipboard data")), + static_cast<XTransferable*>(this)); + } + + mFlavorList = mDataFlavorMapper->typesArrayToFlavorSequence(pboardFormats); +} + + +/* Compares two DataFlavors. Returns true if both DataFlavor have the same media type + and the number of parameter and all parameter values do match otherwise false + is returned. + */ +bool OSXTransferable::compareDataFlavors(const DataFlavor& lhs, const DataFlavor& rhs ) +{ + try + { + Reference<XMimeContentType> xLhs(mrXMimeCntFactory->createMimeContentType(lhs.MimeType)); + Reference<XMimeContentType> xRhs(mrXMimeCntFactory->createMimeContentType(rhs.MimeType)); + + if (!xLhs->getFullMediaType().equalsIgnoreAsciiCase(xRhs->getFullMediaType()) || + !cmpAllContentTypeParameter(xLhs, xRhs)) + { + return false; + } + } + catch( IllegalArgumentException& ) + { + OSL_ENSURE( sal_False, "Invalid content type detected" ); + return false; + } + + return true; +} + + +bool OSXTransferable::cmpAllContentTypeParameter(const Reference<XMimeContentType> xLhs, + const Reference<XMimeContentType> xRhs) const +{ + Sequence<OUString> xLhsFlavors = xLhs->getParameters(); + Sequence<OUString> xRhsFlavors = xRhs->getParameters(); + + // Stop here if the number of parameters is different already + if (xLhsFlavors.getLength() != xRhsFlavors.getLength()) + return false; + + try + { + OUString pLhs; + OUString pRhs; + + for (sal_Int32 i = 0; i < xLhsFlavors.getLength(); i++) + { + pLhs = xLhs->getParameterValue(xLhsFlavors[i]); + pRhs = xRhs->getParameterValue(xLhsFlavors[i]); + + if (!pLhs.equalsIgnoreAsciiCase(pRhs)) + { + return false; + } + } + } + catch(IllegalArgumentException&) + { + return false; + } + + return true; +} diff --git a/vcl/aqua/source/dtrans/OSXTransferable.hxx b/vcl/aqua/source/dtrans/OSXTransferable.hxx new file mode 100644 index 000000000000..6601905f1610 --- /dev/null +++ b/vcl/aqua/source/dtrans/OSXTransferable.hxx @@ -0,0 +1,100 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +#ifndef _TRANSFERABLE_HXX_ +#define _TRANSFERABLE_HXX_ + +//------------------------------------------------------------------------ +// includes +//------------------------------------------------------------------------ + +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <cppuhelper/implbase1.hxx> +#include <com/sun/star/datatransfer/XMimeContentTypeFactory.hpp> +#include <com/sun/star/datatransfer/XMimeContentType.hpp> + +#include "DataFlavorMapping.hxx" + +#include <premac.h> +#import <Cocoa/Cocoa.h> +#include <postmac.h> + +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> + + +class OSXTransferable : public ::cppu::WeakImplHelper1<com::sun::star::datatransfer::XTransferable>, + private ::boost::noncopyable +{ +public: + typedef com::sun::star::uno::Sequence< sal_Int8 > ByteSequence_t; + + explicit OSXTransferable(com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XMimeContentTypeFactory> rXMimeCntFactory, + DataFlavorMapperPtr_t pDataFlavorMapper, + NSPasteboard* pasteboard); + + virtual ~OSXTransferable(); + + //------------------------------------------------------------------------ + // XTransferable + //------------------------------------------------------------------------ + + virtual ::com::sun::star::uno::Any SAL_CALL getTransferData( const ::com::sun::star::datatransfer::DataFlavor& aFlavor ) + throw( ::com::sun::star::datatransfer::UnsupportedFlavorException, ::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException ); + + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors( ) + throw( ::com::sun::star::uno::RuntimeException ); + + virtual sal_Bool SAL_CALL isDataFlavorSupported( const ::com::sun::star::datatransfer::DataFlavor& aFlavor ) + throw( ::com::sun::star::uno::RuntimeException ); + + //------------------------------------------------------------------------ + // Helper functions not part of the XTransferable interface + //------------------------------------------------------------------------ + + void initClipboardItemList(); + + //com::sun::star::uno::Any getClipboardItemData(ClipboardItemPtr_t clipboardItem); + + bool isUnicodeText(const com::sun::star::datatransfer::DataFlavor& flavor); + + bool compareDataFlavors( const com::sun::star::datatransfer::DataFlavor& lhs, + const com::sun::star::datatransfer::DataFlavor& rhs ); + + bool cmpAllContentTypeParameter( const com::sun::star::uno::Reference< com::sun::star::datatransfer::XMimeContentType > xLhs, + const com::sun::star::uno::Reference< com::sun::star::datatransfer::XMimeContentType > xRhs ) const; + +private: + com::sun::star::uno::Sequence< com::sun::star::datatransfer::DataFlavor > mFlavorList; + ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XMimeContentTypeFactory> mrXMimeCntFactory; + DataFlavorMapperPtr_t mDataFlavorMapper; + NSPasteboard* mPasteboard; +}; + +#endif diff --git a/vcl/aqua/source/dtrans/PictToBmpFlt.cxx b/vcl/aqua/source/dtrans/PictToBmpFlt.cxx new file mode 100644 index 000000000000..1410fc2bd66d --- /dev/null +++ b/vcl/aqua/source/dtrans/PictToBmpFlt.cxx @@ -0,0 +1,201 @@ +/************************************************************************* + * + * 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: OSXTransferable.hxx,v $ + * $Revision: 1.4 $ + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +/* This is a work-around to prevent 'deprecated' warning for 'KillPicture' API + Hopefully we can get rid of this whole code again when the OOo PICT filter + are good enough to be used see #i78953 thus this hack would vanish to again. + */ +#include <premac.h> +#include <AvailabilityMacros.h> +#undef DEPRECATED_ATTRIBUTE +#define DEPRECATED_ATTRIBUTE + +#include <Carbon/Carbon.h> +#include <QuickTime/QuickTime.h> +#include <postmac.h> + +#include "PictToBmpFlt.hxx" + +bool PICTtoBMP(com::sun::star::uno::Sequence<sal_Int8>& aPict, + com::sun::star::uno::Sequence<sal_Int8>& aBmp) +{ + + bool result = false; + + ComponentInstance bmpExporter; + if (OpenADefaultComponent(GraphicsExporterComponentType, + kQTFileTypeBMP, + &bmpExporter) != noErr) + { + return result; + } + + Handle hPict; + if (PtrToHand(aPict.getArray(), &hPict, aPict.getLength()) != noErr) + { + return result; + } + + Handle hBmp; + if ((GraphicsExportSetInputPicture(bmpExporter, (PicHandle)hPict) != noErr) || + ((hBmp = NewHandleClear(0)) == NULL)) + { + CloseComponent(bmpExporter); + DisposeHandle(hPict); + return result; + } + + if ((GraphicsExportSetOutputHandle(bmpExporter, hBmp) == noErr) && + (GraphicsExportDoExport(bmpExporter, NULL) == noErr)) + { + size_t sz = GetHandleSize(hBmp); + aBmp.realloc(sz); + + HLock(hBmp); + rtl_copyMemory(aBmp.getArray(), ((sal_Int8*)*hBmp), sz); + HUnlock(hBmp); + + result = true; + } + + DisposeHandle(hPict); + DisposeHandle(hBmp); + CloseComponent(bmpExporter); + + return result; +} + +bool BMPtoPICT(com::sun::star::uno::Sequence<sal_Int8>& aBmp, + com::sun::star::uno::Sequence<sal_Int8>& aPict) +{ + bool result = false; + + Handle hBmp; + ComponentInstance pictExporter; + if ((PtrToHand(aBmp.getArray(), &hBmp, aBmp.getLength()) != noErr)) + { + return result; + } + + if (OpenADefaultComponent(GraphicsImporterComponentType, + kQTFileTypeBMP, + &pictExporter) != noErr) + { + DisposeHandle(hBmp); + return result; + } + + if (GraphicsImportSetDataHandle(pictExporter, hBmp) != noErr) + { + DisposeHandle(hBmp); + CloseComponent(pictExporter); + return result; + } + + PicHandle hPict; + if (GraphicsImportGetAsPicture(pictExporter, &hPict) == noErr) + { + size_t sz = GetHandleSize((Handle)hPict); + aPict.realloc(sz); + + HLock((Handle)hPict); + rtl_copyMemory(aPict.getArray(), ((sal_Int8*)*hPict), sz); + HUnlock((Handle)hPict); + + // Release the data associated with the picture + // Note: This function is deprecated in Mac OS X + // 10.4. + KillPicture(hPict); + + result = true; + } + + DisposeHandle(hBmp); + CloseComponent(pictExporter); + + return result; +} + +bool ImageToBMP( com::sun::star::uno::Sequence<sal_Int8>& aPict, + com::sun::star::uno::Sequence<sal_Int8>& aBmp, + NSBitmapImageFileType eInFormat) +{ + if( eInFormat == PICTImageFileType ) + return PICTtoBMP( aPict, aBmp ); + + bool bResult = false; + + NSData* pData = [NSData dataWithBytesNoCopy: (void*)aPict.getConstArray() length: aPict.getLength() freeWhenDone: 0]; + if( pData ) + { + NSBitmapImageRep* pRep = [NSBitmapImageRep imageRepWithData: pData]; + if( pRep ) + { + NSData* pOut = [pRep representationUsingType: NSBMPFileType properties: nil]; + if( pOut ) + { + aBmp.realloc( [pOut length] ); + [pOut getBytes: aBmp.getArray() length: aBmp.getLength()]; + bResult = (aBmp.getLength() != 0); + } + } + } + + return bResult; +} + +bool BMPToImage( com::sun::star::uno::Sequence<sal_Int8>& aBmp, + com::sun::star::uno::Sequence<sal_Int8>& aPict, + NSBitmapImageFileType eOutFormat + ) +{ + if( eOutFormat == PICTImageFileType ) + return BMPtoPICT( aBmp, aPict ); + + bool bResult = false; + + NSData* pData = [NSData dataWithBytesNoCopy: const_cast<sal_Int8*>(aBmp.getConstArray()) length: aBmp.getLength() freeWhenDone: 0]; + if( pData ) + { + NSBitmapImageRep* pRep = [NSBitmapImageRep imageRepWithData: pData]; + if( pRep ) + { + NSData* pOut = [pRep representationUsingType: eOutFormat properties: nil]; + if( pOut ) + { + aPict.realloc( [pOut length] ); + [pOut getBytes: aPict.getArray() length: aPict.getLength()]; + bResult = (aPict.getLength() != 0); + } + } + } + + return bResult; +} diff --git a/vcl/aqua/source/dtrans/PictToBmpFlt.hxx b/vcl/aqua/source/dtrans/PictToBmpFlt.hxx new file mode 100644 index 000000000000..12a73452ad7b --- /dev/null +++ b/vcl/aqua/source/dtrans/PictToBmpFlt.hxx @@ -0,0 +1,37 @@ +#ifndef INCLUDED_PICTTOBMPFLT_HXX +#define INCLUDED_PICTTOBMPFLT_HXX + +#include <com/sun/star/uno/Sequence.hxx> + +#include <premac.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> + +/* Transform PICT into the a Window BMP. + + Returns true if the conversion was successful false + otherwise. + */ +bool PICTtoBMP(com::sun::star::uno::Sequence<sal_Int8>& aPict, + com::sun::star::uno::Sequence<sal_Int8>& aBmp); + +/* Transform a Windows BMP to a PICT. + + Returns true if the conversion was successful false + otherwise. + */ +bool BMPtoPICT(com::sun::star::uno::Sequence<sal_Int8>& aBmp, + com::sun::star::uno::Sequence<sal_Int8>& aPict); + +#define PICTImageFileType ((NSBitmapImageFileType)~0) + +bool ImageToBMP( com::sun::star::uno::Sequence<sal_Int8>& aPict, + com::sun::star::uno::Sequence<sal_Int8>& aBmp, + NSBitmapImageFileType eInFormat); + +bool BMPToImage( com::sun::star::uno::Sequence<sal_Int8>& aBmp, + com::sun::star::uno::Sequence<sal_Int8>& aPict, + NSBitmapImageFileType eOutFormat + ); + +#endif diff --git a/vcl/aqua/source/dtrans/aqua_clipboard.cxx b/vcl/aqua/source/dtrans/aqua_clipboard.cxx new file mode 100644 index 000000000000..52fb13e1e11f --- /dev/null +++ b/vcl/aqua/source/dtrans/aqua_clipboard.cxx @@ -0,0 +1,387 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include "aqua_clipboard.hxx" + +#include "DataFlavorMapping.hxx" +#include "OSXTransferable.hxx" + +#include "vcl/unohelp.hxx" + +#include "comphelper/makesequence.hxx" + +#include <boost/assert.hpp> + +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace cppu; +using namespace osl; +using namespace rtl; +using namespace std; +using namespace comphelper; + + +@implementation EventListener; + +-(EventListener*)initWithAquaClipboard: (AquaClipboard*) pcb +{ + self = [super init]; + + if (self) + pAquaClipboard = pcb; + + return self; +} + +-(void)pasteboard:(NSPasteboard*)sender provideDataForType:(NSString*)type +{ + if( pAquaClipboard ) + pAquaClipboard->provideDataForType(sender, type); +} + +-(void)applicationDidBecomeActive:(NSNotification*)aNotification +{ + if( pAquaClipboard ) + pAquaClipboard->applicationDidBecomeActive(aNotification); +} + +-(void)disposing +{ + pAquaClipboard = NULL; +} + +@end + + +OUString clipboard_getImplementationName() +{ + return OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.clipboard.AquaClipboard")); +} + +Sequence<OUString> clipboard_getSupportedServiceNames() +{ + return makeSequence(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.clipboard.SystemClipboard"))); +} + + +AquaClipboard::AquaClipboard(NSPasteboard* pasteboard, bool bUseSystemPasteboard) : + WeakComponentImplHelper4<XClipboardEx, XClipboardNotifier, XFlushableClipboard, XServiceInfo>(m_aMutex), + mIsSystemPasteboard(bUseSystemPasteboard) +{ + Reference<XMultiServiceFactory> mrServiceMgr = vcl::unohelper::GetMultiServiceFactory(); + + mrXMimeCntFactory = Reference<XMimeContentTypeFactory>(mrServiceMgr->createInstance( + OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.MimeContentTypeFactory"))), UNO_QUERY); + + if (!mrXMimeCntFactory.is()) + { + throw RuntimeException(OUString( + RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Cannot create com.sun.star.datatransfer.MimeContentTypeFactory")), + static_cast<XClipboardEx*>(this)); + } + + mpDataFlavorMapper = DataFlavorMapperPtr_t(new DataFlavorMapper()); + + if (pasteboard != NULL) + { + mPasteboard = pasteboard; + mIsSystemPasteboard = false; + } + else + { + mPasteboard = bUseSystemPasteboard ? [NSPasteboard generalPasteboard] : + [NSPasteboard pasteboardWithName: NSDragPboard]; + + if (mPasteboard == nil) + { + throw RuntimeException(OUString( + RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Cannot create Cocoa pasteboard")), + static_cast<XClipboardEx*>(this)); + } + } + + [mPasteboard retain]; + + mEventListener = [[EventListener alloc] initWithAquaClipboard: this]; + + if (mEventListener == nil) + { + [mPasteboard release]; + + throw RuntimeException( + OUString(RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Cannot create pasteboard change listener")), + static_cast<XClipboardEx*>(this)); + } + + if (mIsSystemPasteboard) + { + NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter addObserver: mEventListener + selector: @selector(applicationDidBecomeActive:) + name: @"NSApplicationDidBecomeActiveNotification" + object: [NSApplication sharedApplication]]; + } + + mPasteboardChangeCount = [mPasteboard changeCount]; +} + + +AquaClipboard::~AquaClipboard() +{ + if (mIsSystemPasteboard) + { + [[NSNotificationCenter defaultCenter] removeObserver: mEventListener]; + } + + [mEventListener disposing]; + [mEventListener release]; + [mPasteboard release]; +} + + +Reference<XTransferable> SAL_CALL AquaClipboard::getContents() throw(RuntimeException) +{ + MutexGuard aGuard(m_aMutex); + + // Shortcut: If we are clipboard owner already we don't need + // to drag the data through the system clipboard + if (mXClipboardContent.is()) + { + return mXClipboardContent; + } + + return Reference<XTransferable>(new OSXTransferable(mrXMimeCntFactory, + mpDataFlavorMapper, + mPasteboard)); +} + + +void SAL_CALL AquaClipboard::setContents(const Reference<XTransferable>& xTransferable, + const Reference<XClipboardOwner>& xClipboardOwner) + throw( RuntimeException ) +{ + NSArray* types = xTransferable.is() ? + mpDataFlavorMapper->flavorSequenceToTypesArray(xTransferable->getTransferDataFlavors()) : + [NSArray array]; + + ClearableMutexGuard aGuard(m_aMutex); + + Reference<XClipboardOwner> oldOwner(mXClipboardOwner); + mXClipboardOwner = xClipboardOwner; + + Reference<XTransferable> oldContent(mXClipboardContent); + mXClipboardContent = xTransferable; + + mPasteboardChangeCount = [mPasteboard declareTypes: types owner: mEventListener]; + + aGuard.clear(); + + // if we are already the owner of the clipboard + // then fire lost ownership event + if (oldOwner.is()) + { + fireLostClipboardOwnershipEvent(oldOwner, oldContent); + } + + fireClipboardChangedEvent(); +} + + +OUString SAL_CALL AquaClipboard::getName() throw( RuntimeException ) +{ + return OUString(); +} + + +sal_Int8 SAL_CALL AquaClipboard::getRenderingCapabilities() throw( RuntimeException ) +{ + return 0; +} + + +void SAL_CALL AquaClipboard::addClipboardListener(const Reference< XClipboardListener >& listener) + throw( RuntimeException ) +{ + MutexGuard aGuard(m_aMutex); + + if (!listener.is()) + throw IllegalArgumentException(OUString(RTL_CONSTASCII_USTRINGPARAM("empty reference")), + static_cast<XClipboardEx*>(this), 1); + + mClipboardListeners.push_back(listener); +} + + +void SAL_CALL AquaClipboard::removeClipboardListener(const Reference< XClipboardListener >& listener) + throw( RuntimeException ) +{ + MutexGuard aGuard(m_aMutex); + + if (!listener.is()) + throw IllegalArgumentException(OUString(RTL_CONSTASCII_USTRINGPARAM("empty reference")), + static_cast<XClipboardEx*>(this), 1); + + mClipboardListeners.remove(listener); +} + + +void AquaClipboard::applicationDidBecomeActive(NSNotification* aNotification) +{ + ClearableMutexGuard aGuard(m_aMutex); + + int currentPboardChgCount = [mPasteboard changeCount]; + + if (currentPboardChgCount != mPasteboardChangeCount) + { + mPasteboardChangeCount = currentPboardChgCount; + + // Clear clipboard content and owner and send lostOwnership + // notification to the old clipboard owner as well as + // ClipboardChanged notification to any clipboard listener + Reference<XClipboardOwner> oldOwner(mXClipboardOwner); + mXClipboardOwner = Reference<XClipboardOwner>(); + + Reference<XTransferable> oldContent(mXClipboardContent); + mXClipboardContent = Reference<XTransferable>(); + + aGuard.clear(); + + if (oldOwner.is()) + { + fireLostClipboardOwnershipEvent(oldOwner, oldContent); + } + + fireClipboardChangedEvent(); + } +} + + +void AquaClipboard::fireClipboardChangedEvent() +{ + ClearableMutexGuard aGuard(m_aMutex); + + list<Reference< XClipboardListener > > listeners(mClipboardListeners); + ClipboardEvent aEvent; + + if (listeners.begin() != listeners.end()) + { + aEvent = ClipboardEvent(static_cast<OWeakObject*>(this), getContents()); + } + + aGuard.clear(); + + while (listeners.begin() != listeners.end()) + { + if (listeners.front().is()) + { + try { listeners.front()->changedContents(aEvent); } + catch (RuntimeException&) { } + } + listeners.pop_front(); + } +} + + +void AquaClipboard::fireLostClipboardOwnershipEvent(Reference<XClipboardOwner> oldOwner, Reference<XTransferable> oldContent) +{ + BOOST_ASSERT(oldOwner.is()); + + try { oldOwner->lostOwnership(static_cast<XClipboardEx*>(this), oldContent); } + catch(RuntimeException&) { } +} + + +void AquaClipboard::provideDataForType(NSPasteboard* sender, NSString* type) +{ + DataProviderPtr_t dp = mpDataFlavorMapper->getDataProvider(type, mXClipboardContent); + NSData* pBoardData = NULL; + + if (dp.get() != NULL) + { + pBoardData = (NSData*)dp->getSystemData(); + [sender setData: pBoardData forType: type]; + } +} + + +//------------------------------------------------ +// XFlushableClipboard +//------------------------------------------------ + +void SAL_CALL AquaClipboard::flushClipboard() + throw(RuntimeException) +{ + if (mXClipboardContent.is()) + { + Sequence<DataFlavor> flavorList = mXClipboardContent->getTransferDataFlavors(); + sal_uInt32 nFlavors = flavorList.getLength(); + + for (sal_uInt32 i = 0; i < nFlavors; i++) + { + NSString* sysType = mpDataFlavorMapper->openOfficeToSystemFlavor(flavorList[i]); + + if (sysType != NULL) + { + provideDataForType(mPasteboard, sysType); + } + } + } +} + + +NSPasteboard* AquaClipboard::getPasteboard() const +{ + return mPasteboard; +} + + +//------------------------------------------------- +// XServiceInfo +//------------------------------------------------- + +OUString SAL_CALL AquaClipboard::getImplementationName() throw( RuntimeException ) +{ + return clipboard_getImplementationName(); +} + + +sal_Bool SAL_CALL AquaClipboard::supportsService( const OUString& ServiceName ) throw( RuntimeException ) +{ + return sal_False; +} + + +Sequence< OUString > SAL_CALL AquaClipboard::getSupportedServiceNames() throw( RuntimeException ) +{ + return clipboard_getSupportedServiceNames(); +} + diff --git a/vcl/aqua/source/dtrans/aqua_clipboard.hxx b/vcl/aqua/source/dtrans/aqua_clipboard.hxx new file mode 100644 index 000000000000..8f45f50717f3 --- /dev/null +++ b/vcl/aqua/source/dtrans/aqua_clipboard.hxx @@ -0,0 +1,181 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _AQUA_CLIPBOARD_HXX_ +#define _AQUA_CLIPBOARD_HXX_ + +#include "DataFlavorMapping.hxx" +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <cppuhelper/compbase4.hxx> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp> +#include <com/sun/star/datatransfer/XMimeContentTypeFactory.hpp> +#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/basemutex.hxx> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> + +#include <boost/utility.hpp> +#include <list> + +#include <premac.h> +#import <Cocoa/Cocoa.h> +#include <postmac.h> + +class AquaClipboard; + +@interface EventListener : NSObject +{ + AquaClipboard* pAquaClipboard; +} + +// Init the pasteboard change listener with a reference to the OfficeClipboard +// instance +- (EventListener*)initWithAquaClipboard: (AquaClipboard*) pcb; + +// Promiss resolver function +- (void)pasteboard:(NSPasteboard*)sender provideDataForType:(NSString *)type; + +-(void)applicationDidBecomeActive:(NSNotification*)aNotification; + +-(void)disposing; +@end + + +class AquaClipboard : public ::cppu::BaseMutex, + public ::cppu::WeakComponentImplHelper4< com::sun::star::datatransfer::clipboard::XClipboardEx, + com::sun::star::datatransfer::clipboard::XClipboardNotifier, + com::sun::star::datatransfer::clipboard::XFlushableClipboard, + com::sun::star::lang::XServiceInfo >, + private ::boost::noncopyable +{ +public: + /* Create a clipboard instance. + + @param pasteboard + If not equal NULL the instance will be instantiated with the provided + pasteboard reference and 'bUseSystemClipboard' will be ignored + + @param bUseSystemClipboard + If 'pasteboard' is NULL 'bUseSystemClipboard' determines whether the + system clipboard will be created (bUseSystemClipboard == true) or if + the DragPasteboard if bUseSystemClipboard == false + */ + AquaClipboard(NSPasteboard* pasteboard = NULL, + bool bUseSystemClipboard = true); + + ~AquaClipboard(); + + //------------------------------------------------ + // XClipboard + //------------------------------------------------ + + virtual ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > SAL_CALL getContents() + throw( ::com::sun::star::uno::RuntimeException ); + + virtual void SAL_CALL setContents( const ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable >& xTransferable, + const ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner ) + throw( ::com::sun::star::uno::RuntimeException ); + + virtual ::rtl::OUString SAL_CALL getName() + throw( ::com::sun::star::uno::RuntimeException ); + + //------------------------------------------------ + // XClipboardEx + //------------------------------------------------ + + virtual sal_Int8 SAL_CALL getRenderingCapabilities() + throw( ::com::sun::star::uno::RuntimeException ); + + //------------------------------------------------ + // XClipboardNotifier + //------------------------------------------------ + + virtual void SAL_CALL addClipboardListener( const ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardListener >& listener ) + throw( ::com::sun::star::uno::RuntimeException ); + + virtual void SAL_CALL removeClipboardListener( const ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardListener >& listener ) + throw( ::com::sun::star::uno::RuntimeException ); + + //------------------------------------------------ + // XFlushableClipboard + //------------------------------------------------ + + virtual void SAL_CALL flushClipboard( ) throw( com::sun::star::uno::RuntimeException ); + + //------------------------------------------------ + // XServiceInfo + //------------------------------------------------ + + virtual ::rtl::OUString SAL_CALL getImplementationName() + throw(::com::sun::star::uno::RuntimeException); + + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) + throw(::com::sun::star::uno::RuntimeException); + + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() + throw(::com::sun::star::uno::RuntimeException); + + /* Get a reference to the used pastboard. + */ + NSPasteboard* getPasteboard() const; + + /* Notify the current clipboard owner that he is no longer the clipboard owner. + */ + void fireLostClipboardOwnershipEvent(::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardOwner> oldOwner, + ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > oldContent); + + void pasteboardChangedOwner(); + + void provideDataForType(NSPasteboard* sender, NSString* type); + + void applicationDidBecomeActive(NSNotification* aNotification); + +private: + + /* Notify all registered XClipboardListener that the clipboard content + has changed. + */ + void fireClipboardChangedEvent(); + +private: + ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XMimeContentTypeFactory > mrXMimeCntFactory; + ::std::list< ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardListener > > mClipboardListeners; + ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > mXClipboardContent; + com::sun::star::uno::Reference< com::sun::star::datatransfer::clipboard::XClipboardOwner > mXClipboardOwner; + DataFlavorMapperPtr_t mpDataFlavorMapper; + bool mIsSystemPasteboard; + NSPasteboard* mPasteboard; + int mPasteboardChangeCount; + EventListener* mEventListener; +}; + +#endif diff --git a/vcl/aqua/source/dtrans/aqua_service.cxx b/vcl/aqua/source/dtrans/aqua_service.cxx new file mode 100644 index 000000000000..571bea2e554f --- /dev/null +++ b/vcl/aqua/source/dtrans/aqua_service.cxx @@ -0,0 +1,108 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include "aqua_clipboard.hxx" +#include <cppuhelper/factory.hxx> +#include <com/sun/star/container/XSet.hpp> +#include <osl/diagnose.h> + +using namespace rtl; +using namespace com::sun::star::uno; +using namespace com::sun::star::registry; +using namespace cppu; +using namespace com::sun::star::lang; +using namespace com::sun::star::datatransfer::clipboard; +using namespace aqua; + +namespace aqua { + +Reference< XInterface > SAL_CALL createInstance( const Reference< XMultiServiceFactory >& rServiceManager ) +{ + return Reference< XInterface >( static_cast< XClipboard* >( new AquaClipboard(rServiceManager) ) ); +} + +} // namespace aqua + +extern "C" +{ + +void SAL_CALL component_getImplementationEnvironment( + const sal_Char ** ppEnvTypeName, uno_Environment ** ppEnv ) +{ + *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; +} + +sal_Bool SAL_CALL component_writeInfo( void* pServiceManager, void* pRegistryKey ) +{ + sal_Bool bRetVal = sal_False; + + if ( pRegistryKey ) + { + try + { + Reference< XRegistryKey > pXNewKey( static_cast< XRegistryKey* >( pRegistryKey ) ); + pXNewKey->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM( AQUA_CLIPBOARD_REGKEY_NAME ) ) ); + bRetVal = sal_True; + } + catch( InvalidRegistryException& ) + { + OSL_ENSURE(sal_False, "InvalidRegistryException caught"); + bRetVal = sal_False; + } + } + + return bRetVal; +} + +void* SAL_CALL component_getFactory( const sal_Char* pImplName, uno_Interface* pSrvManager, uno_Interface* pRegistryKey ) +{ + void* pRet = 0; + + if ( pSrvManager && ( 0 == rtl_str_compare( pImplName, AQUA_CLIPBOARD_IMPL_NAME ) ) ) + { + Sequence< OUString > aSNS( 1 ); + aSNS.getArray()[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( AQUA_CLIPBOARD_SERVICE_NAME ) ); + + //OUString( RTL_CONSTASCII_USTRINGPARAM( FPS_IMPL_NAME ) ) + Reference< XSingleServiceFactory > xFactory ( createOneInstanceFactory( + reinterpret_cast< XMultiServiceFactory* > ( pSrvManager ), + OUString::createFromAscii( pImplName ), + createInstance, + aSNS ) ); + if ( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + } + + return pRet; +} + +} // extern "C" diff --git a/vcl/aqua/source/dtrans/makefile.mk b/vcl/aqua/source/dtrans/makefile.mk new file mode 100644 index 000000000000..369799c9a260 --- /dev/null +++ b/vcl/aqua/source/dtrans/makefile.mk @@ -0,0 +1,68 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# 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 +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=vcl +TARGET=dtransaqua +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# ------------------------------------------------------------------ + +.IF "$(OS)"!="MACOSX" +dummy: + @echo "Nothing to build for this platform" +.ELSE # "$(OS)"!="MACOSX" +.IF "$(GUIBASE)"!="aqua" +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" +.ELSE + +CFLAGSCXX+=-fconstant-cfstrings -x objective-c++ -fobjc-exceptions + +SLOFILES= \ + $(SLO)$/aqua_clipboard.obj \ + $(SLO)$/DataFlavorMapping.obj \ + $(SLO)$/OSXTransferable.obj \ + $(SLO)$/HtmlFmtFlt.obj \ + $(SLO)$/PictToBmpFlt.obj \ + $(SLO)$/DropTarget.obj \ + $(SLO)$/DragSource.obj \ + $(SLO)$/service_entry.obj \ + $(SLO)$/DragSourceContext.obj \ + $(SLO)$/DragActionConversion.obj + +# --- Targets ------------------------------------------------------ +.INCLUDE : target.mk + +.ENDIF # "$(GUIBASE)"!="aqua" +.ENDIF # "$(OS)"!="MACOSX" + diff --git a/vcl/aqua/source/dtrans/service_entry.cxx b/vcl/aqua/source/dtrans/service_entry.cxx new file mode 100644 index 000000000000..16308951bcfe --- /dev/null +++ b/vcl/aqua/source/dtrans/service_entry.cxx @@ -0,0 +1,64 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "saldata.hxx" +#include "salinst.h" +#include "DragSource.hxx" +#include "DropTarget.hxx" +#include "aqua_clipboard.hxx" +#include "osl/diagnose.h" + +using namespace ::osl; +using namespace ::rtl; +using namespace ::com::sun::star::uno; +using namespace ::cppu; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::datatransfer::clipboard; + + +Reference< XInterface > AquaSalInstance::CreateClipboard( const Sequence< Any >& i_rArguments ) +{ + SalData* pSalData = GetSalData(); + if( ! pSalData->mxClipboard.is() ) + pSalData->mxClipboard = Reference<XInterface>(static_cast< XClipboard* >(new AquaClipboard()), UNO_QUERY); + return pSalData->mxClipboard; +} + + +Reference<XInterface> AquaSalInstance::CreateDragSource() +{ + return Reference<XInterface>(static_cast< XInitialization* >(new DragSource()), UNO_QUERY); +} + +Reference<XInterface> AquaSalInstance::CreateDropTarget() +{ + return Reference<XInterface>(static_cast< XInitialization* >(new DropTarget()), UNO_QUERY); +} + diff --git a/vcl/aqua/source/dtrans/test_aquacb.cxx b/vcl/aqua/source/dtrans/test_aquacb.cxx new file mode 100644 index 000000000000..85c87c6b9ba9 --- /dev/null +++ b/vcl/aqua/source/dtrans/test_aquacb.cxx @@ -0,0 +1,208 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "aqua_clipboard.hxx" +#include <cppuhelper/servicefactory.hxx> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +#ifndef _CPPUHELPER_IMPLBASE1_HXX_ +#include <cppuhelper/implbase2.hxx> +#endif +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <osl/diagnose.h> + +#include <stdio.h> + +using namespace ::rtl; +using namespace ::std; +using namespace ::cppu; +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::datatransfer::clipboard; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; + +Reference< XTransferable > rXTransfRead; + +class TestTransferable : public WeakImplHelper2< XClipboardOwner, XTransferable > +{ +public: + TestTransferable(); + virtual Any SAL_CALL getTransferData( const DataFlavor& aFlavor ) throw(UnsupportedFlavorException, IOException, RuntimeException); + virtual Sequence< DataFlavor > SAL_CALL getTransferDataFlavors() throw(RuntimeException); + virtual sal_Bool SAL_CALL isDataFlavorSupported( const DataFlavor& aFlavor ) throw(RuntimeException); + virtual void SAL_CALL lostOwnership( const Reference< XClipboard >& xClipboard, const Reference< XTransferable >& xTrans ) throw(RuntimeException); + +private: + Sequence< DataFlavor > m_seqDFlv; + OUString m_Data; +}; + +TestTransferable::TestTransferable() : + m_seqDFlv( 1 ), + m_Data( RTL_CONSTASCII_USTRINGPARAM( "This is a test string" ) ) +{ + DataFlavor df; + + df.MimeType = OUString::createFromAscii( "text/html" ); + df.DataType = getCppuType( ( Sequence< sal_Int8 >* )0 ); + + m_seqDFlv[0] = df; +} + +Any SAL_CALL TestTransferable::getTransferData( const DataFlavor& aFlavor ) + throw(UnsupportedFlavorException, IOException, RuntimeException) +{ + Any anyData; + + if ( aFlavor.MimeType == m_seqDFlv[0].MimeType ) + { + OString aStr( m_Data.getStr(), m_Data.getLength(), 1252 ); + Sequence< sal_Int8 > sOfChars( aStr.getLength() ); + sal_Int32 lenStr = aStr.getLength(); + + for ( sal_Int32 i = 0; i < lenStr; ++i ) + sOfChars[i] = aStr[i]; + + anyData = makeAny( sOfChars ); + } + + return anyData; +} + +Sequence< DataFlavor > SAL_CALL TestTransferable::getTransferDataFlavors() + throw(RuntimeException) +{ + return m_seqDFlv; +} + +sal_Bool SAL_CALL TestTransferable::isDataFlavorSupported( const DataFlavor& aFlavor ) + throw(RuntimeException) +{ + sal_Int32 nLength = m_seqDFlv.getLength(); + sal_Bool bRet = sal_False; + + for ( sal_Int32 i = 0; i < nLength; ++i ) + { + if ( m_seqDFlv[i].MimeType == aFlavor.MimeType ) + { + bRet = sal_True; + break; + } + } + + return bRet; +} + +void SAL_CALL TestTransferable::lostOwnership( const Reference< XClipboard >& xClipboard, const Reference< XTransferable >& xTrans ) + throw(RuntimeException) +{ +} + +int SAL_CALL main( int argc, char** argv ) +{ + if(argc != 2) + { + fprintf( stderr, "usage: %s <my rdb file>\n", argv[0] ); + return 1; + } + + //------------------------------------------------- + // get the global service-manager + //------------------------------------------------- + OUString rdbName = OUString::createFromAscii( argv[1] ); + Reference< XMultiServiceFactory > g_xFactory( createRegistryServiceFactory( rdbName ) ); + + // Print a message if an error occured. + if ( !g_xFactory.is() ) + { + OSL_ENSURE(sal_False, "Can't create RegistryServiceFactory"); + return(-1); + } + + //------------------------------------------------- + // try to get an Interface to a XFilePicker Service + //------------------------------------------------- + + Reference< XTransferable > rXTransf( static_cast< XTransferable* >( new TestTransferable ) ); + + Reference< XClipboard > xClipboard( g_xFactory->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( AQUA_CLIPBOARD_SERVICE_NAME ) ) ), UNO_QUERY ); + if ( !xClipboard.is() ) + { + OSL_ENSURE( sal_False, "Error creating FolderPicker Service" ); + return(-1); + } + + Reference< XTypeProvider > rXTypProv( xClipboard, UNO_QUERY ); + + if ( rXTypProv.is() ) + { + Sequence< Type > seqType = rXTypProv->getTypes(); + sal_Int32 nLen = seqType.getLength(); + for ( sal_Int32 i = 0; i < nLen; i++ ) + { + Type nxtType = seqType[i]; + } + + Sequence< sal_Int8 > seqInt8 = rXTypProv->getImplementationId(); + } + + xClipboard->setContents( rXTransf, Reference< XClipboardOwner >( rXTransf, UNO_QUERY ) ); + + rXTransfRead = xClipboard->getContents(); + + // destroy the transferable explicitly + rXTransfRead = Reference< XTransferable>(); + + // destroy the clipboard + xClipboard = Reference< XClipboard >(); + + //-------------------------------------------------- + // shutdown the service manager + //-------------------------------------------------- + + // Cast factory to XComponent + Reference< XComponent > xComponent( g_xFactory, UNO_QUERY ); + + if ( !xComponent.is() ) + OSL_ENSURE(sal_False, "Error shuting down"); + + // Dispose and clear factory + xComponent->dispose(); + g_xFactory.clear(); + g_xFactory = Reference< XMultiServiceFactory >(); + + return 0; +} diff --git a/vcl/aqua/source/gdi/aquaprintaccessoryview.mm b/vcl/aqua/source/gdi/aquaprintaccessoryview.mm new file mode 100644 index 000000000000..d00fc9a6cd0e --- /dev/null +++ b/vcl/aqua/source/gdi/aquaprintaccessoryview.mm @@ -0,0 +1,1241 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * 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 "vcl/image.hxx" +#include "vcl/virdev.hxx" +#include "vcl/svdata.hxx" +#include "vcl/svapp.hxx" +#include "vcl/unohelp.hxx" + +#include "vcl/svids.hrc" + +#include "tools/resary.hxx" + +#include "com/sun/star/i18n/XBreakIterator.hpp" +#include "com/sun/star/i18n/WordType.hpp" + +#include <map> + +using namespace vcl; +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::uno; + +/* Note: the accesory view as implemented here is already deprecated in Leopard. Unfortunately + as long as our baseline is Tiger we cannot gain the advantages over multiple accessory views + as well havs having accessory views AND a preview (as long as you are linked vs. 10.4 libraries + the preview insists on not being present. This is unfortunate. +*/ + +class ControllerProperties; + +@interface ControlTarget : NSObject +{ + ControllerProperties* mpController; +} +-(id)initWithControllerMap: (ControllerProperties*)pController; +-(void)triggered:(id)pSender; +-(void)triggeredNumeric:(id)pSender; +-(void)triggeredPreview:(id)pSender; +-(void)dealloc; +@end + + +class ControllerProperties +{ + vcl::PrinterController* mpController; + std::map< int, rtl::OUString > maTagToPropertyName; + std::map< int, sal_Int32 > maTagToValueInt; + std::map< NSView*, NSView* > maViewPairMap; + std::vector< NSObject* > maViews; + int mnNextTag; + sal_Int32 mnLastPageCount; + PrintAccessoryViewState* mpState; + NSPrintOperation* mpOp; + NSView* mpAccessoryView; + NSTabView* mpTabView; + NSBox* mpPreviewBox; + NSImageView* mpPreview; + NSTextField* mpPageEdit; + NSStepper* mpStepper; + NSTextView* mpPagesLabel; + ResStringArray maLocalizedStrings; + + public: + ControllerProperties( vcl::PrinterController* i_pController, + NSPrintOperation* i_pOp, + NSView* i_pAccessoryView, + NSTabView* i_pTabView, + PrintAccessoryViewState* i_pState ) + : mpController( i_pController ), + mnNextTag( 0 ), + mnLastPageCount( i_pController->getFilteredPageCount() ), + mpState( i_pState ), + mpOp( i_pOp ), + mpAccessoryView( i_pAccessoryView ), + mpTabView( i_pTabView ), + mpPreviewBox( nil ), + mpPreview( nil ), + mpPageEdit( nil ), + mpStepper( nil ), + mpPagesLabel( nil ), + maLocalizedStrings( VclResId( SV_PRINT_NATIVE_STRINGS ) ) + { + mpState->bNeedRestart = false; + DBG_ASSERT( maLocalizedStrings.Count() >= 4, "resources not found !" ); + } + + rtl::OUString getMoreString() + { + return maLocalizedStrings.Count() >= 4 + ? rtl::OUString( maLocalizedStrings.GetString( 3 ) ) + : rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "More" ) ); + } + + void updatePrintJob() + { + // TODO: refresh page count etc from mpController + + // page range may have changed depending on options + sal_Int32 nPages = mpController->getFilteredPageCount(); + #if OSL_DEBUG_LEVEL > 1 + if( nPages != mnLastPageCount ) + fprintf( stderr, "trouble: number of pages changed from %ld to %ld !\n", mnLastPageCount, nPages ); + #endif + mpState->bNeedRestart = (nPages != mnLastPageCount); + NSTabViewItem* pItem = [mpTabView selectedTabViewItem]; + if( pItem ) + mpState->nLastPage = [mpTabView indexOfTabViewItem: pItem]; + else + mpState->nLastPage = 0; + mnLastPageCount = nPages; + if( mpState->bNeedRestart ) + { + #if 0 + // Warning: bad hack ahead + // Apple does not give us a chance of changing the page count, + // and they don't let us cancel the dialog either + // hack: send a cancel message to the window displaying our views. + // this is ugly. + for( std::vector< NSObject* >::iterator it = maViews.begin(); it != maViews.end(); ++it ) + { + if( [*it isKindOfClass: [NSView class]] ) + { + NSView* pView = (NSView*)*it; + NSWindow* pWindow = [pView window]; + if( pWindow ) + { + [pWindow cancelOperation: nil]; + break; + } + } + } + #else + NSWindow* pWindow = [NSApp modalWindow]; + if( pWindow ) + [pWindow cancelOperation: nil]; + #endif + [[mpOp printInfo] setJobDisposition: NSPrintCancelJob]; + } + else + { + sal_Int32 nPage = [mpStepper intValue]; + updatePreviewImage( nPage-1 ); + } + } + + int addNameTag( const rtl::OUString& i_rPropertyName ) + { + int nNewTag = mnNextTag++; + maTagToPropertyName[ nNewTag ] = i_rPropertyName; + return nNewTag; + } + + int addNameAndValueTag( const rtl::OUString& i_rPropertyName, sal_Int32 i_nValue ) + { + int nNewTag = mnNextTag++; + maTagToPropertyName[ nNewTag ] = i_rPropertyName; + maTagToValueInt[ nNewTag ] = i_nValue; + return nNewTag; + } + + void addObservedControl( NSObject* i_pView ) + { + maViews.push_back( i_pView ); + } + + void addViewPair( NSView* i_pLeft, NSView* i_pRight ) + { + maViewPairMap[ i_pLeft ] = i_pRight; + maViewPairMap[ i_pRight ] = i_pLeft; + } + + NSView* getPair( NSView* i_pLeft ) const + { + NSView* pRight = nil; + std::map< NSView*, NSView* >::const_iterator it = maViewPairMap.find( i_pLeft ); + if( it != maViewPairMap.end() ) + pRight = it->second; + return pRight; + } + + void changePropertyWithIntValue( int i_nTag ) + { + std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag ); + std::map< int, sal_Int32 >::const_iterator value_it = maTagToValueInt.find( i_nTag ); + if( name_it != maTagToPropertyName.end() && value_it != maTagToValueInt.end() ) + { + PropertyValue* pVal = mpController->getValue( name_it->second ); + if( pVal ) + { + pVal->Value <<= value_it->second; + updatePrintJob(); + } + } + } + + void changePropertyWithIntValue( int i_nTag, sal_Int64 i_nValue ) + { + std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag ); + if( name_it != maTagToPropertyName.end() ) + { + PropertyValue* pVal = mpController->getValue( name_it->second ); + if( pVal ) + { + pVal->Value <<= i_nValue; + 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 = mpController->getValue( name_it->second ); + if( pVal ) + { + pVal->Value <<= i_bValue; + updatePrintJob(); + } + } + } + + void changePropertyWithStringValue( int i_nTag, const rtl::OUString& i_rValue ) + { + std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( i_nTag ); + if( name_it != maTagToPropertyName.end() ) + { + PropertyValue* pVal = mpController->getValue( name_it->second ); + if( pVal ) + { + pVal->Value <<= i_rValue; + updatePrintJob(); + } + } + } + + void updateEnableState() + { + for( std::vector< NSObject* >::iterator it = maViews.begin(); it != maViews.end(); ++it ) + { + NSObject* pObj = *it; + NSControl* pCtrl = nil; + NSCell* pCell = nil; + if( [pObj isKindOfClass: [NSControl class]] ) + pCtrl = (NSControl*)pObj; + else if( [pObj isKindOfClass: [NSCell class]] ) + pCell = (NSCell*)pObj; + + int nTag = pCtrl ? [pCtrl tag] : + pCell ? [pCell tag] : + -1; + + std::map< int, rtl::OUString >::const_iterator name_it = maTagToPropertyName.find( nTag ); + if( name_it != maTagToPropertyName.end() ) + { + MacOSBOOL bEnabled = mpController->isUIOptionEnabled( name_it->second ) ? YES : NO; + if( pCtrl ) + { + [pCtrl setEnabled: bEnabled]; + NSView* pOther = getPair( pCtrl ); + if( pOther && [pOther isKindOfClass: [NSControl class]] ) + [(NSControl*)pOther setEnabled: bEnabled]; + } + else if( pCell ) + [pCell setEnabled: bEnabled]; + + } + } + } + + void updatePreviewImage( sal_Int32 i_nPage ) + { + sal_Int32 nPages = mpController->getFilteredPageCount(); + NSRect aViewFrame = [mpPreview frame]; + Size aPixelSize( static_cast<long>(aViewFrame.size.width), + static_cast<long>(aViewFrame.size.height) ); + if( i_nPage >= 0 && nPages > i_nPage ) + { + GDIMetaFile aMtf; + PrinterController::PageSize aPageSize( mpController->getFilteredPageFile( i_nPage, aMtf, false ) ); + VirtualDevice aDev; + // see salprn.cxx, currently we pretend to be a 720dpi device on printers + aDev.SetReferenceDevice( 720, 720 ); + aDev.EnableOutput( TRUE ); + Size aLogicSize( aDev.PixelToLogic( aPixelSize, MapMode( MAP_100TH_MM ) ) ); + double fScaleX = double(aLogicSize.Width())/double(aPageSize.aSize.Width()); + double fScaleY = double(aLogicSize.Height())/double(aPageSize.aSize.Height()); + double fScale = (fScaleX < fScaleY) ? fScaleX : fScaleY; + // #i104784# if we render the page too small then rounding issues result in + // layout artifacts looking really bad. So scale the page unto a device that is not + // full page size but not too small either. This also results in much better visual + // quality of the preview, e.g. when its height approaches the number of text lines + if( fScale < 0.1 ) + fScale = 0.1; + aMtf.WindStart(); + aMtf.Scale( fScale, fScale ); + aMtf.WindStart(); + aLogicSize.Width() = long(double(aPageSize.aSize.Width()) * fScale); + aLogicSize.Height() = long(double(aPageSize.aSize.Height()) * fScale); + aPixelSize = aDev.LogicToPixel( aLogicSize, MapMode( MAP_100TH_MM ) ); + aDev.SetOutputSizePixel( aPixelSize ); + aMtf.WindStart(); + aDev.SetMapMode( MapMode( MAP_100TH_MM ) ); + aMtf.Play( &aDev, Point( 0, 0 ), aLogicSize ); + aDev.EnableMapMode( FALSE ); + Image aImage( aDev.GetBitmap( Point( 0, 0 ), aPixelSize ) ); + NSImage* pImage = CreateNSImage( aImage ); + [mpPreview setImage: [pImage autorelease]]; + } + else + [mpPreview setImage: nil]; + } + + void setupPreview( ControlTarget* i_pCtrlTarget ) + { + if( maLocalizedStrings.Count() < 3 ) + return; + + // get the preview control + NSRect aPreviewFrame = [mpAccessoryView frame]; + aPreviewFrame.origin.x = 0; + aPreviewFrame.origin.y = 5; + aPreviewFrame.size.width = 190; + aPreviewFrame.size.height -= 7; + + // create a box to put the preview controls in + mpPreviewBox = [[NSBox alloc] initWithFrame: aPreviewFrame]; + [mpPreviewBox setTitle: [CreateNSString( maLocalizedStrings.GetString( 0 ) ) autorelease]]; + [mpAccessoryView addSubview: [mpPreviewBox autorelease]]; + + // now create the image view of the preview + NSSize aMargins = [mpPreviewBox contentViewMargins]; + aPreviewFrame.origin.x = 0; + aPreviewFrame.origin.y = 34; + aPreviewFrame.size.width -= 2*(aMargins.width+1); + aPreviewFrame.size.height -= 61; + mpPreview = [[NSImageView alloc] initWithFrame: aPreviewFrame]; + [mpPreview setImageScaling: NSScaleProportionally]; + [mpPreview setImageAlignment: NSImageAlignCenter]; + [mpPreview setImageFrameStyle: NSImageFrameNone]; + [mpPreviewBox addSubview: [mpPreview autorelease]]; + + // add a label + sal_Int32 nPages = mpController->getFilteredPageCount(); + rtl::OUStringBuffer aBuf( 16 ); + aBuf.appendAscii( "/ " ); + aBuf.append( rtl::OUString::valueOf( nPages ) ); + + NSString* pText = CreateNSString( aBuf.makeStringAndClear() ); + NSRect aTextRect = { { 100, 5 }, { 100, 22 } }; + mpPagesLabel = [[NSTextView alloc] initWithFrame: aTextRect]; + [mpPagesLabel setFont: [NSFont controlContentFontOfSize: 0]]; + [mpPagesLabel setEditable: NO]; + [mpPagesLabel setSelectable: NO]; + [mpPagesLabel setDrawsBackground: NO]; + [mpPagesLabel setString: [pText autorelease]]; + [mpPagesLabel setToolTip: [CreateNSString( maLocalizedStrings.GetString( 2 ) ) autorelease]]; + [mpPreviewBox addSubview: [mpPagesLabel autorelease]]; + + NSRect aFieldRect = { { 45, 5 }, { 35, 25 } }; + mpPageEdit = [[NSTextField alloc] initWithFrame: aFieldRect]; + [mpPageEdit setEditable: YES]; + [mpPageEdit setSelectable: YES]; + [mpPageEdit setDrawsBackground: YES]; + [mpPageEdit setToolTip: [CreateNSString( maLocalizedStrings.GetString( 1 ) ) autorelease]]; + [mpPreviewBox addSubview: [mpPageEdit autorelease]]; + + // add a stepper control + NSRect aStepFrame = { { 85, 5 }, { 15, 25 } }; + mpStepper = [[NSStepper alloc] initWithFrame: aStepFrame]; + [mpStepper setIncrement: 1]; + [mpStepper setValueWraps: NO]; + [mpPreviewBox addSubview: [mpStepper autorelease]]; + + // constrain the text field to decimal numbers + NSNumberFormatter* pFormatter = [[NSNumberFormatter alloc] init]; + [pFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4]; + [pFormatter setMinimum: [[NSNumber numberWithInt: 1] autorelease]]; + [pFormatter setMaximum: [[NSNumber numberWithInt: nPages] autorelease]]; + [pFormatter setNumberStyle: NSNumberFormatterDecimalStyle]; + [pFormatter setAllowsFloats: NO]; + [pFormatter setMaximumFractionDigits: 0]; + [mpPageEdit setFormatter: pFormatter]; + [mpStepper setMinValue: 1]; + [mpStepper setMaxValue: nPages]; + + [mpPageEdit setIntValue: 1]; + [mpStepper setIntValue: 1]; + + // connect target and action + [mpStepper setTarget: i_pCtrlTarget]; + [mpStepper setAction: @selector(triggeredPreview:)]; + [mpPageEdit setTarget: i_pCtrlTarget]; + [mpPageEdit setAction: @selector(triggeredPreview:)]; + + // set first preview image + updatePreviewImage( 0 ); + } + + void changePreview( NSObject* i_pSender ) + { + if( [i_pSender isMemberOfClass: [NSTextField class]] ) + { + NSTextField* pField = (NSTextField*)i_pSender; + if( pField == mpPageEdit ) // sanity check + { + sal_Int32 nPage = [pField intValue]; + [mpStepper setIntValue: nPage]; + updatePreviewImage( nPage-1 ); + } + } + else if( [i_pSender isMemberOfClass: [NSStepper class]] ) + { + NSStepper* pStepper = (NSStepper*)i_pSender; + if( pStepper == mpStepper ) // sanity check + { + sal_Int32 nPage = [pStepper intValue]; + [mpPageEdit setIntValue: nPage]; + updatePreviewImage( nPage-1 ); + } + } + } +}; + +static void filterAccelerator( rtl::OUString& io_rText ) +{ + rtl::OUStringBuffer aBuf( io_rText.getLength() ); + for( sal_Int32 nIndex = 0; nIndex != -1; ) + aBuf.append( io_rText.getToken( 0, '~', nIndex ) ); + io_rText = aBuf.makeStringAndClear(); +} + +@implementation ControlTarget +-(id)initWithControllerMap: (ControllerProperties*)pController +{ + if( (self = [super init]) ) + { + mpController = pController; + } + 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]; + mpController->changePropertyWithIntValue( nTag ); + } + } + else if( [pSender isMemberOfClass: [NSButton class]] ) + { + NSButton* pBtn = (NSButton*)pSender; + int nTag = [pBtn tag]; + mpController->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]; + mpController->changePropertyWithIntValue( nTag ); + } + } + else if( [pSender isMemberOfClass: [NSTextField class]] ) + { + NSTextField* pField = (NSTextField*)pSender; + int nTag = [pField tag]; + rtl::OUString aValue = GetOUString( [pSender stringValue] ); + mpController->changePropertyWithStringValue( nTag, aValue ); + } + else + { + DBG_ERROR( "unsupported class" ); + } + mpController->updateEnableState(); +} +-(void)triggeredNumeric:(id)pSender; +{ + if( [pSender isMemberOfClass: [NSTextField class]] ) + { + NSTextField* pField = (NSTextField*)pSender; + int nTag = [pField tag]; + sal_Int64 nValue = [pField intValue]; + + NSView* pOther = mpController->getPair( pField ); + if( pOther ) + [(NSControl*)pOther setIntValue: nValue]; + + mpController->changePropertyWithIntValue( nTag, nValue ); + } + else if( [pSender isMemberOfClass: [NSStepper class]] ) + { + NSStepper* pStep = (NSStepper*)pSender; + int nTag = [pStep tag]; + sal_Int64 nValue = [pStep intValue]; + + NSView* pOther = mpController->getPair( pStep ); + if( pOther ) + [(NSControl*)pOther setIntValue: nValue]; + + mpController->changePropertyWithIntValue( nTag, nValue ); + } + else + { + DBG_ERROR( "unsupported class" ); + } + mpController->updateEnableState(); +} +-(void)triggeredPreview:(id)pSender +{ + mpController->changePreview( pSender ); +} +-(void)dealloc +{ + delete mpController; + [super dealloc]; +} +@end + +struct ColumnItem +{ + NSControl* pControl; + long nOffset; + NSControl* pSubControl; + + ColumnItem( NSControl* i_pControl = nil, long i_nOffset = 0, NSControl* i_pSub = nil ) + : pControl( i_pControl ) + , nOffset( i_nOffset ) + , pSubControl( i_pSub ) + {} + + long getWidth() const + { + long nWidth = 0; + if( pControl ) + { + NSRect aCtrlRect = [pControl frame]; + nWidth = aCtrlRect.size.width; + nWidth += nOffset; + if( pSubControl ) + { + NSRect aSubRect = [pSubControl frame]; + nWidth += aSubRect.size.width; + nWidth += aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width); + } + } + return nWidth; + } +}; + +static void adjustViewAndChildren( NSView* pView, NSSize& rMaxSize, + std::vector< ColumnItem >& rLeftColumn, + std::vector< ColumnItem >& rRightColumn + ) +{ + // balance columns + + // first get overall column widths + long nLeftWidth = 0; + long nRightWidth = 0; + for( size_t i = 0; i < rLeftColumn.size(); i++ ) + { + long nW = rLeftColumn[i].getWidth(); + if( nW > nLeftWidth ) + nLeftWidth = nW; + } + for( size_t i = 0; i < rRightColumn.size(); i++ ) + { + long nW = rRightColumn[i].getWidth(); + if( nW > nRightWidth ) + nRightWidth = nW; + } + + // right align left column + for( size_t i = 0; i < rLeftColumn.size(); i++ ) + { + if( rLeftColumn[i].pControl ) + { + NSRect aCtrlRect = [rLeftColumn[i].pControl frame]; + long nX = nLeftWidth - aCtrlRect.size.width; + if( rLeftColumn[i].pSubControl ) + { + NSRect aSubRect = [rLeftColumn[i].pSubControl frame]; + nX -= aSubRect.size.width + (aSubRect.origin.x - (aCtrlRect.origin.x + aCtrlRect.size.width)); + aSubRect.origin.x = nLeftWidth - aSubRect.size.width; + [rLeftColumn[i].pSubControl setFrame: aSubRect]; + } + aCtrlRect.origin.x = nX; + [rLeftColumn[i].pControl setFrame: aCtrlRect]; + } + } + + // left align right column + for( size_t i = 0; i < rRightColumn.size(); i++ ) + { + if( rRightColumn[i].pControl ) + { + NSRect aCtrlRect = [rRightColumn[i].pControl frame]; + long nX = nLeftWidth + 3; + if( rRightColumn[i].pSubControl ) + { + NSRect aSubRect = [rRightColumn[i].pSubControl frame]; + aSubRect.origin.x = nX + aSubRect.origin.x - aCtrlRect.origin.x; + [rRightColumn[i].pSubControl setFrame: aSubRect]; + } + aCtrlRect.origin.x = nX; + [rRightColumn[i].pControl setFrame: aCtrlRect]; + } + } + + 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 += 20; + [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; +} + +static void adjustTabViews( NSTabView* pTabView, NSSize aTabSize ) +{ + // loop over all contained tab pages + NSArray* pTabbedViews = [pTabView tabViewItems]; + int nViews = [pTabbedViews count]; + for( int i = 0; i < nViews; i++ ) + { + NSTabViewItem* pItem = (NSTabViewItem*)[pTabbedViews objectAtIndex: i]; + NSView* pView = [pItem view]; + if( pView ) + { + NSRect aRect = [pView frame]; + double nDiff = aTabSize.height - aRect.size.height; + aRect.size = aTabSize; + [pView setFrame: aRect]; + + NSArray* pSubViews = [pView subviews]; + unsigned int nSubViews = [pSubViews count]; + + // move everything up + for( unsigned int n = 0; n < nSubViews; n++ ) + { + NSView* pCurSubView = [pSubViews objectAtIndex: n]; + NSRect aFrame = [pCurSubView frame]; + aFrame.origin.y += nDiff; + // give separators the correct width + // separators are currently the only NSBoxes we use + if( [pCurSubView isMemberOfClass: [NSBox class]] ) + { + aFrame.size.width = aTabSize.width - aFrame.origin.x - 10; + } + [pCurSubView setFrame: aFrame]; + } + } + } +} + +static NSControl* createLabel( const rtl::OUString& i_rText ) +{ + NSString* pText = CreateNSString( i_rText ); + NSRect aTextRect = { { 0, 0 }, {20, 15} }; + NSTextField* pTextView = [[NSTextField alloc] initWithFrame: aTextRect]; + [pTextView setFont: [NSFont controlContentFontOfSize: 0]]; + [pTextView setEditable: NO]; + [pTextView setSelectable: NO]; + [pTextView setDrawsBackground: NO]; + [pTextView setBordered: NO]; + [pTextView setStringValue: pText]; + [pTextView sizeToFit]; + [pText release]; + return pTextView; +} + +static sal_Int32 findBreak( const rtl::OUString& i_rText, sal_Int32 i_nPos ) +{ + sal_Int32 nRet = i_rText.getLength(); + Reference< i18n::XBreakIterator > xBI( vcl::unohelper::CreateBreakIterator() ); + if( xBI.is() ) + { + i18n::Boundary aBoundary = xBI->getWordBoundary( i_rText, i_nPos, + Application::GetSettings().GetLocale(), + i18n::WordType::ANYWORD_IGNOREWHITESPACES, + sal_True ); + nRet = aBoundary.endPos; + } + return nRet; +} + +static void linebreakCell( NSCell* pBtn, const rtl::OUString& i_rText ) +{ + NSString* pText = CreateNSString( i_rText ); + [pBtn setTitle: pText]; + [pText release]; + NSSize aSize = [pBtn cellSize]; + if( aSize.width > 280 ) + { + // need two lines + sal_Int32 nLen = i_rText.getLength(); + sal_Int32 nIndex = nLen / 2; + nIndex = findBreak( i_rText, nIndex ); + if( nIndex < nLen ) + { + rtl::OUStringBuffer aBuf( i_rText ); + aBuf.setCharAt( nIndex, '\n' ); + pText = CreateNSString( aBuf.makeStringAndClear() ); + [pBtn setTitle: pText]; + [pText release]; + } + } +} + + +@implementation AquaPrintAccessoryView ++(NSObject*)setupPrinterPanel: (NSPrintOperation*)pOp withController: (vcl::PrinterController*)pController withState: (PrintAccessoryViewState*)pState; +{ + const Sequence< PropertyValue >& rOptions( pController->getUIOptions() ); + if( rOptions.getLength() == 0 ) + return nil; + + NSView* pCurParent = 0; + long nCurY = 0; + long nCurX = 0; + NSRect aViewFrame = { { 0, 0 }, {600, 400 } }; + NSRect aTabViewFrame = { { 190, 0 }, {410, 400 } }; + NSSize aMaxTabSize = { 0, 0 }; + NSView* pAccessoryView = [[NSView alloc] initWithFrame: aViewFrame]; + NSTabView* pTabView = [[NSTabView alloc] initWithFrame: aTabViewFrame]; + [pAccessoryView addSubview: [pTabView autorelease]]; + + sal_Bool bIgnoreSubgroup = sal_False; + + ControllerProperties* pControllerProperties = new ControllerProperties( pController, pOp, pAccessoryView, pTabView, pState ); + ControlTarget* pCtrlTarget = [[ControlTarget alloc] initWithControllerMap: pControllerProperties]; + + std::vector< ColumnItem > aLeftColumn, aRightColumn; + + 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; + sal_Int64 nMinValue = 0, nMaxValue = 0; + long nAttachOffset = 0; + sal_Bool bIgnore = sal_False; + + for( int n = 0; n < aOptProp.getLength(); n++ ) + { + const beans::PropertyValue& rEntry( aOptProp[ n ] ); + if( rEntry.Name.equalsAscii( "Text" ) ) + { + rEntry.Value >>= aText; + filterAccelerator( 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; + } + else if( rEntry.Name.equalsAscii( "MinValue" ) ) + { + rEntry.Value >>= nMinValue; + } + else if( rEntry.Name.equalsAscii( "MaxValue" ) ) + { + rEntry.Value >>= nMaxValue; + } + else if( rEntry.Name.equalsAscii( "AttachToDependency" ) ) + { + nAttachOffset = 20; + } + else if( rEntry.Name.equalsAscii( "InternalUIOnly" ) ) + { + rEntry.Value >>= bIgnore; + } + } + + if( aCtrlType.equalsAscii( "Group" ) || + aCtrlType.equalsAscii( "Subgroup" ) || + aCtrlType.equalsAscii( "Radio" ) || + aCtrlType.equalsAscii( "List" ) || + aCtrlType.equalsAscii( "Edit" ) || + aCtrlType.equalsAscii( "Range" ) || + 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 + || ( aCtrlType.equalsAscii( "Subgroup" ) && nCurY < -250 && ! bIgnore ) + ) + { + rtl::OUString aGroupTitle( aText ); + if( aCtrlType.equalsAscii( "Subgroup" ) ) + aGroupTitle = pControllerProperties->getMoreString(); + // set size of current parent + if( pCurParent ) + adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn ); + + // new tab item + if( ! aText.getLength() ) + aText = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "OOo" ) ); + NSString* pLabel = CreateNSString( aGroupTitle ); + NSTabViewItem* pItem = [[NSTabViewItem alloc] initWithIdentifier: pLabel ]; + [pItem setLabel: pLabel]; + [pTabView addTabViewItem: pItem]; + pCurParent = [[NSView alloc] initWithFrame: aTabViewFrame]; + [pItem setView: pCurParent]; + [pLabel release]; + + // reset indent + nCurX = 20; + // reset Y + nCurY = 0; + // clear columns + aLeftColumn.clear(); + aRightColumn.clear(); + } + + if( aCtrlType.equalsAscii( "Subgroup" ) && pCurParent ) + { + bIgnoreSubgroup = bIgnore; + if( bIgnore ) + continue; + + NSControl* pTextView = createLabel( aText ); + [pCurParent addSubview: [pTextView autorelease]]; + NSRect aTextRect = [pTextView frame]; + // move to nCurY + aTextRect.origin.y = nCurY - aTextRect.size.height; + [pTextView setFrame: aTextRect]; + + NSRect aSepRect = { { aTextRect.size.width + 1, aTextRect.origin.y }, { 100, 6 } }; + NSBox* pBox = [[NSBox alloc] initWithFrame: aSepRect]; + [pBox setBoxType: NSBoxSeparator]; + [pCurParent addSubview: [pBox autorelease]]; + + // update nCurY + nCurY = aTextRect.origin.y - 5; + } + else if( bIgnoreSubgroup || bIgnore ) + continue; + else if( aCtrlType.equalsAscii( "Bool" ) && pCurParent ) + { + NSRect aCheckRect = { { nCurX + nAttachOffset, 0 }, { 0, 15 } }; + NSButton* pBtn = [[NSButton alloc] initWithFrame: aCheckRect]; + [pBtn setButtonType: NSSwitchButton]; + sal_Bool bVal = sal_False; + PropertyValue* pVal = pController->getValue( aPropertyName ); + if( pVal ) + pVal->Value >>= bVal; + [pBtn setState: bVal ? NSOnState : NSOffState]; + linebreakCell( [pBtn cell], aText ); + [pBtn sizeToFit]; + [pCurParent addSubview: [pBtn autorelease]]; + + aRightColumn.push_back( ColumnItem( pBtn ) ); + + // connect target + [pBtn setTarget: pCtrlTarget]; + [pBtn setAction: @selector(triggered:)]; + int nTag = pControllerProperties->addNameTag( aPropertyName ); + pControllerProperties->addObservedControl( pBtn ); + [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; + } + else if( aCtrlType.equalsAscii( "Radio" ) && pCurParent ) + { + sal_Int32 nOff = 0; + if( aText.getLength() ) + { + // add a label + NSControl* pTextView = createLabel( aText ); + NSRect aTextRect = [pTextView frame]; + aTextRect.origin.x = nCurX + nAttachOffset; + [pCurParent addSubview: [pTextView autorelease]]; + + aLeftColumn.push_back( ColumnItem( pTextView ) ); + + // move to nCurY + aTextRect.origin.y = nCurY - aTextRect.size.height; + [pTextView setFrame: aTextRect]; + + // update nCurY + nCurY = aTextRect.origin.y - 5; + + // indent the radio group relative to the text + // nOff = 20; + } + + // setup radio matrix + NSButtonCell* pProto = [[NSButtonCell alloc] init]; + + NSRect aRadioRect = { { nCurX + nOff, 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 + sal_Int32 nSelectVal = 0; + PropertyValue* pVal = pController->getValue( aPropertyName ); + if( pVal && pVal->Value.hasValue() ) + pVal->Value >>= nSelectVal; + // set individual titles + NSArray* pCells = [pMatrix cells]; + for( sal_Int32 m = 0; m < aChoices.getLength(); m++ ) + { + NSCell* pCell = [pCells objectAtIndex: m]; + filterAccelerator( aChoices[m] ); + linebreakCell( pCell, aChoices[m] ); + //NSString* pTitle = CreateNSString( aChoices[m] ); + //[pCell setTitle: pTitle]; + // connect target and action + [pCell setTarget: pCtrlTarget]; + [pCell setAction: @selector(triggered:)]; + int nTag = pControllerProperties->addNameAndValueTag( aPropertyName, m ); + pControllerProperties->addObservedControl( pCell ); + [pCell setTag: nTag]; + //[pTitle release]; + // set current selection + if( nSelectVal == 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 autorelease]]; + + aRightColumn.push_back( ColumnItem( pMatrix ) ); + + // update nCurY + nCurY = aRadioRect.origin.y - 5; + + [pProto release]; + } + else if( aCtrlType.equalsAscii( "List" ) && pCurParent ) + { + // don't indent attached lists, looks bad in the existing cases + NSControl* pTextView = createLabel( aText ); + [pCurParent addSubview: [pTextView autorelease]]; + aLeftColumn.push_back( ColumnItem( pTextView ) ); + NSRect aTextRect = [pTextView frame]; + aTextRect.origin.x = nCurX /* + nAttachOffset*/; + + // don't indent attached lists, looks bad in the existing cases + NSRect aBtnRect = { { nCurX /*+ nAttachOffset*/ + 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 = pControllerProperties->addNameAndValueTag( aPropertyName, m ); + [pItem setTag: nTag]; + [pItemText release]; + } + + PropertyValue* pVal = pController->getValue( aPropertyName ); + sal_Int32 aSelectVal = 0; + if( pVal && pVal->Value.hasValue() ) + pVal->Value >>= aSelectVal; + [pBtn selectItemAtIndex: aSelectVal]; + + // add the button to observed controls for enabled state changes + // also add a tag just for this purpose + pControllerProperties->addObservedControl( pBtn ); + [pBtn setTag: pControllerProperties->addNameTag( aPropertyName )]; + + [pBtn sizeToFit]; + [pCurParent addSubview: [pBtn autorelease]]; + + aRightColumn.push_back( ColumnItem( 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; + } + else if( (aCtrlType.equalsAscii( "Edit" ) || aCtrlType.equalsAscii( "Range" )) && pCurParent ) + { + sal_Int32 nOff = 0; + if( aText.getLength() ) + { + // add a label + NSControl* pTextView = createLabel( aText ); + [pCurParent addSubview: [pTextView autorelease]]; + + aLeftColumn.push_back( ColumnItem( pTextView ) ); + + // move to nCurY + NSRect aTextRect = [pTextView frame]; + aTextRect.origin.x = nCurX + nAttachOffset; + aTextRect.origin.y = nCurY - aTextRect.size.height; + [pTextView setFrame: aTextRect]; + + // update nCurY + nCurY = aTextRect.origin.y - 5; + + // and set the offset for the real edit field + nOff = aTextRect.size.width + 5; + } + + NSRect aFieldRect = { { nCurX + nOff + nAttachOffset, 0 }, { 100, 25 } }; + NSTextField* pFieldView = [[NSTextField alloc] initWithFrame: aFieldRect]; + [pFieldView setEditable: YES]; + [pFieldView setSelectable: YES]; + [pFieldView setDrawsBackground: YES]; + [pFieldView sizeToFit]; // FIXME: this does nothing + [pCurParent addSubview: [pFieldView autorelease]]; + + aRightColumn.push_back( ColumnItem( pFieldView ) ); + + // add the field to observed controls for enabled state changes + // also add a tag just for this purpose + pControllerProperties->addObservedControl( pFieldView ); + int nTag = pControllerProperties->addNameTag( aPropertyName ); + [pFieldView setTag: nTag]; + // pControllerProperties->addNamedView( pFieldView, aPropertyName ); + + // move to nCurY + aFieldRect.origin.y = nCurY - aFieldRect.size.height; + [pFieldView setFrame: aFieldRect]; + + // current value + PropertyValue* pVal = pController->getValue( aPropertyName ); + if( aCtrlType.equalsAscii( "Range" ) ) + { + // add a stepper control + NSRect aStepFrame = { { aFieldRect.origin.x + aFieldRect.size.width + 5, + aFieldRect.origin.y }, + { 15, aFieldRect.size.height } }; + NSStepper* pStep = [[NSStepper alloc] initWithFrame: aStepFrame]; + [pStep setIncrement: 1]; + [pStep setValueWraps: NO]; + [pStep setTag: nTag]; + [pCurParent addSubview: [pStep autorelease]]; + + aRightColumn.back().pSubControl = pStep; + + pControllerProperties->addObservedControl( pStep ); + [pStep setTarget: pCtrlTarget]; + [pStep setAction: @selector(triggered:)]; + + // constrain the text field to decimal numbers + NSNumberFormatter* pFormatter = [[NSNumberFormatter alloc] init]; + [pFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4]; + [pFormatter setNumberStyle: NSNumberFormatterDecimalStyle]; + [pFormatter setAllowsFloats: NO]; + [pFormatter setMaximumFractionDigits: 0]; + if( nMinValue != nMaxValue ) + { + [pFormatter setMinimum: [[NSNumber numberWithInt: nMinValue] autorelease]]; + [pStep setMinValue: nMinValue]; + [pFormatter setMaximum: [[NSNumber numberWithInt: nMaxValue] autorelease]]; + [pStep setMaxValue: nMaxValue]; + } + [pFieldView setFormatter: pFormatter]; + + sal_Int64 nSelectVal = 0; + if( pVal && pVal->Value.hasValue() ) + pVal->Value >>= nSelectVal; + + [pFieldView setIntValue: nSelectVal]; + [pStep setIntValue: nSelectVal]; + + pControllerProperties->addViewPair( pFieldView, pStep ); + // connect target and action + [pFieldView setTarget: pCtrlTarget]; + [pFieldView setAction: @selector(triggeredNumeric:)]; + [pStep setTarget: pCtrlTarget]; + [pStep setAction: @selector(triggeredNumeric:)]; + } + else + { + // connect target and action + [pFieldView setTarget: pCtrlTarget]; + [pFieldView setAction: @selector(triggered:)]; + + if( pVal && pVal->Value.hasValue() ) + { + rtl::OUString aValue; + pVal->Value >>= aValue; + if( aValue.getLength() ) + { + NSString* pText = CreateNSString( aValue ); + [pFieldView setStringValue: pText]; + [pText release]; + } + } + } + + // update nCurY + nCurY = aFieldRect.origin.y - 5; + + } + } + else + { + DBG_ERROR( "Unsupported UI option" ); + } + } + + pControllerProperties->updateEnableState(); + adjustViewAndChildren( pCurParent, aMaxTabSize, aLeftColumn, aRightColumn ); + + // leave some space for the preview + if( aMaxTabSize.height < 200 ) + aMaxTabSize.height = 200; + + // now reposition everything again so it is upper bound + adjustTabViews( pTabView, 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]; + aViewFrame.size.width = aTabCtrlSize.width + aTabViewFrame.origin.x; + aViewFrame.size.height = aTabCtrlSize.height + aTabViewFrame.origin.y; + [pAccessoryView setFrameSize: aViewFrame.size]; + + pControllerProperties->setupPreview( pCtrlTarget ); + + // set the accessory view + [pOp setAccessoryView: [pAccessoryView autorelease]]; + + // set the current selecte tab item + if( pState->nLastPage >= 0 && pState->nLastPage < [pTabView numberOfTabViewItems] ) + [pTabView selectTabViewItemAtIndex: pState->nLastPage]; + + return pCtrlTarget; +} + +@end diff --git a/vcl/aqua/source/gdi/aquaprintview.mm b/vcl/aqua/source/gdi/aquaprintview.mm new file mode 100755 index 000000000000..ae42c5c09e8d --- /dev/null +++ b/vcl/aqua/source/gdi/aquaprintview.mm @@ -0,0 +1,82 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * 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 "salprn.h" +#include "vcl/print.hxx" + +@implementation AquaPrintView +-(id)initWithController: (vcl::PrinterController*)pController withInfoPrinter: (AquaSalInfoPrinter*)pInfoPrinter +{ + NSRect aRect = { { 0, 0 }, [pInfoPrinter->getPrintInfo() paperSize] }; + if( (self = [super initWithFrame: aRect]) != nil ) + { + mpController = pController; + mpInfoPrinter = pInfoPrinter; + } + return self; +} + +-(MacOSBOOL)knowsPageRange: (NSRangePointer)range +{ + range->location = 1; + range->length = mpInfoPrinter->getCurPageRangeCount(); + return YES; +} + +-(NSRect)rectForPage: (int)page +{ + NSSize aPaperSize = [mpInfoPrinter->getPrintInfo() paperSize]; + int nWidth = (int)aPaperSize.width; + // #i101108# sanity check + if( nWidth < 1 ) + nWidth = 1; + NSRect aRect = { { page % nWidth, page / nWidth }, aPaperSize }; + return aRect; +} + +-(NSPoint)locationOfPrintRect: (NSRect)aRect +{ + NSPoint aPoint = { 0, 0 }; + return aPoint; +} + +-(void)drawRect: (NSRect)rect +{ + NSPoint aPoint = [self locationOfPrintRect: rect]; + mpInfoPrinter->setStartPageOffset( static_cast<int>(rect.origin.x), static_cast<int>(rect.origin.y) ); + NSSize aPaperSize = [mpInfoPrinter->getPrintInfo() paperSize]; + int nPage = (int)(aPaperSize.width * rect.origin.y + rect.origin.x); + + // page count is 1 based + if( nPage - 1 < (mpInfoPrinter->getCurPageRangeStart() + mpInfoPrinter->getCurPageRangeCount() ) ) + mpController->printFilteredPage( nPage-1 ); +} +@end diff --git a/vcl/aqua/source/gdi/makefile.mk b/vcl/aqua/source/gdi/makefile.mk new file mode 100644 index 000000000000..2aea58e49250 --- /dev/null +++ b/vcl/aqua/source/gdi/makefile.mk @@ -0,0 +1,74 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# 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 +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=vcl +TARGET=salgdi +ENABLE_EXCEPTIONS=TRUE + +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile2.pmk + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"!="aqua" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"!="aqua" + +SLOFILES= $(SLO)$/salmathutils.obj \ + $(SLO)$/salcolorutils.obj \ + $(SLO)$/salgdiutils.obj \ + $(SLO)$/salnativewidgets.obj \ + $(SLO)$/salatsuifontutils.obj \ + $(SLO)$/salatslayout.obj \ + $(SLO)$/salgdi.obj \ + $(SLO)$/salvd.obj \ + $(SLO)$/salprn.obj \ + $(SLO)$/aquaprintview.obj \ + $(SLO)$/aquaprintaccessoryview.obj \ + $(SLO)$/salbmp.obj + +.IF "$(ENABLE_CAIRO)" == "TRUE" +CDEFS+= -DCAIRO +.ENDIF + +.ENDIF # "$(GUIBASE)"!="aqua" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.INCLUDE : $(PRJ)$/util$/target.pmk + diff --git a/vcl/aqua/source/gdi/salatslayout.cxx b/vcl/aqua/source/gdi/salatslayout.cxx new file mode 100755 index 000000000000..335505de85ac --- /dev/null +++ b/vcl/aqua/source/gdi/salatslayout.cxx @@ -0,0 +1,1264 @@ +/************************************************************************* +* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +#include "vcl/salgdi.hxx" +#include "saldata.hxx" +#include "salgdi.h" +#include "vcl/sallayout.hxx" +#include "salatsuifontutils.hxx" +#include "tools/debug.hxx" + +#include <math.h> + +// ======================================================================= + +class ATSLayout : public SalLayout +{ +public: + ATSLayout( ATSUStyle&, float fFontScale ); + virtual ~ATSLayout(); + + virtual bool LayoutText( ImplLayoutArgs& ); + virtual void AdjustLayout( ImplLayoutArgs& ); + virtual void DrawText( SalGraphics& ) const; + + virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&, + sal_Int32* pGlyphAdvances, int* pCharIndexes ) const; + + virtual long GetTextWidth() const; + virtual long FillDXArray( long* pDXArray ) const; + virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const; + virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const; + virtual bool GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const; + virtual bool GetBoundRect( SalGraphics&, Rectangle& ) const; + + const ImplFontData* GetFallbackFontData( sal_GlyphId ) const; + + virtual void InitFont(); + virtual void MoveGlyph( int nStart, long nNewXPos ); + virtual void DropGlyph( int nStart ); + virtual void Simplify( bool bIsBase ); + +private: + ATSUStyle& mrATSUStyle; + ATSUTextLayout maATSULayout; + int mnCharCount; // ==mnEndCharPos-mnMinCharPos + // to prevent ATS overflowing the Fixed16.16 values + // ATS font requests get size limited by downscaling huge fonts + // in these cases the font scale becomes something bigger than 1.0 + float mfFontScale; + +private: + bool InitGIA( ImplLayoutArgs* pArgs = NULL ) const; + bool GetIdealX() const; + bool GetDeltaY() const; + void InvalidateMeasurements(); + + int Fixed2Vcl( Fixed ) const; // convert ATSU-Fixed units to VCL units + int AtsuPix2Vcl( int ) const; // convert ATSU-Pixel units to VCL units + Fixed Vcl2Fixed( int ) const; // convert VCL units to ATSU-Fixed units + + // cached details about the resulting layout + // mutable members since these details are all lazy initialized + mutable int mnGlyphCount; // glyph count + mutable Fixed mnCachedWidth; // cached value of resulting typographical width + int mnTrailingSpaceWidth; // in Pixels + + mutable ATSGlyphRef* mpGlyphIds; // ATSU glyph ids + mutable Fixed* mpCharWidths; // map relative charpos to charwidth + mutable int* mpChars2Glyphs; // map relative charpos to absolute glyphpos + mutable int* mpGlyphs2Chars; // map absolute glyphpos to absolute charpos + mutable bool* mpGlyphRTLFlags; // BiDi status for glyphs: true if RTL + mutable Fixed* mpGlyphAdvances; // contains glyph widths for the justified layout + mutable Fixed* mpGlyphOrigAdvs; // contains glyph widths for the unjustified layout + mutable Fixed* mpDeltaY; // vertical offset from the baseline + + struct SubPortion { int mnMinCharPos, mnEndCharPos; Fixed mnXOffset; }; + typedef std::vector<SubPortion> SubPortionVector; + mutable SubPortionVector maSubPortions; // Writer&ATSUI layouts can differ quite a bit... + + // storing details about fonts used in glyph-fallback for this layout + mutable class FallbackInfo* mpFallbackInfo; + + // x-offset relative to layout origin + // currently only used in RTL-layouts + mutable Fixed mnBaseAdv; +}; + +class FallbackInfo +{ +public: + FallbackInfo() : mnMaxLevel(0) {} + int AddFallback( ATSUFontID ); + const ImplFontData* GetFallbackFontData( int nLevel ) const; + +private: + const ImplMacFontData* maFontData[ MAX_FALLBACK ]; + ATSUFontID maATSUFontId[ MAX_FALLBACK ]; + int mnMaxLevel; +}; + +// ======================================================================= + +ATSLayout::ATSLayout( ATSUStyle& rATSUStyle, float fFontScale ) +: mrATSUStyle( rATSUStyle ), + maATSULayout( NULL ), + mnCharCount( 0 ), + mfFontScale( fFontScale ), + mnGlyphCount( -1 ), + mnCachedWidth( 0 ), + mnTrailingSpaceWidth( 0 ), + mpGlyphIds( NULL ), + mpCharWidths( NULL ), + mpChars2Glyphs( NULL ), + mpGlyphs2Chars( NULL ), + mpGlyphRTLFlags( NULL ), + mpGlyphAdvances( NULL ), + mpGlyphOrigAdvs( NULL ), + mpDeltaY( NULL ), + mpFallbackInfo( NULL ), + mnBaseAdv( 0 ) +{} + +// ----------------------------------------------------------------------- + +ATSLayout::~ATSLayout() +{ + if( mpDeltaY ) + ATSUDirectReleaseLayoutDataArrayPtr( NULL, + kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY ); + + if( maATSULayout ) + ATSUDisposeTextLayout( maATSULayout ); + + delete[] mpGlyphRTLFlags; + delete[] mpGlyphs2Chars; + delete[] mpChars2Glyphs; + if( mpCharWidths != mpGlyphAdvances ) + delete[] mpCharWidths; + delete[] mpGlyphIds; + delete[] mpGlyphOrigAdvs; + delete[] mpGlyphAdvances; + + delete mpFallbackInfo; +} + +// ----------------------------------------------------------------------- + +inline int ATSLayout::Fixed2Vcl( Fixed nFixed ) const +{ + float fFloat = mfFontScale * FixedToFloat( nFixed ); + return static_cast<int>(fFloat + 0.5); +} + +// ----------------------------------------------------------------------- + +inline int ATSLayout::AtsuPix2Vcl( int nAtsuPixel) const +{ + float fVclPixel = mfFontScale * nAtsuPixel; + fVclPixel += (fVclPixel>=0) ? +0.5 : -0.5; // prepare rounding to int + int nVclPixel = static_cast<int>( fVclPixel); + return nVclPixel; +} + +// ----------------------------------------------------------------------- + +inline Fixed ATSLayout::Vcl2Fixed( int nPixel ) const +{ + return FloatToFixed( nPixel / mfFontScale ); +} + +// ----------------------------------------------------------------------- +/** + * ATSLayout::LayoutText : Manage text layouting + * + * @param rArgs: contains array of char to be layouted, starting and ending position of the text to layout + * + * Typographic layout of text by using the style maATSUStyle + * + * @return : true if everything is ok +**/ +bool ATSLayout::LayoutText( ImplLayoutArgs& rArgs ) +{ + if( maATSULayout ) + ATSUDisposeTextLayout( maATSULayout ); + + maATSULayout = NULL; + + // Layout text + // set up our locals, verify parameters... + DBG_ASSERT( (rArgs.mpStr!=NULL), "ATSLayout::LayoutText() with rArgs.mpStr==NULL !!!"); + DBG_ASSERT( (mrATSUStyle!=NULL), "ATSLayout::LayoutText() with ATSUStyle==NULL !!!"); + + SalLayout::AdjustLayout( rArgs ); + mnCharCount = mnEndCharPos - mnMinCharPos; + + // Workaround a bug in ATSUI with empty string + if( mnCharCount<=0 ) + return false; + +#if (OSL_DEBUG_LEVEL > 3) + Fixed fFontSize = 0; + ByteCount nDummy; + ATSUGetAttribute( mrATSUStyle, kATSUSizeTag, sizeof(fFontSize), &fFontSize, &nDummy); + String aUniName( &rArgs.mpStr[rArgs.mnMinCharPos], mnCharCount ); + ByteString aCName( aUniName, RTL_TEXTENCODING_UTF8 ); + fprintf( stderr, "ATSLayout( \"%s\" %d..%d of %d) with h=%4.1f\n", + aCName.GetBuffer(),rArgs.mnMinCharPos,rArgs.mnEndCharPos,rArgs.mnLength,Fix2X(fFontSize) ); +#endif + + // create the ATSUI layout + UniCharCount nRunLengths[1] = { mnCharCount }; + const int nRunCount = sizeof(nRunLengths)/sizeof(*nRunLengths); + OSStatus eStatus = ATSUCreateTextLayoutWithTextPtr( rArgs.mpStr, + rArgs.mnMinCharPos, mnCharCount, rArgs.mnLength, + nRunCount, &nRunLengths[0], &mrATSUStyle, + &maATSULayout); + + DBG_ASSERT( (eStatus==noErr), "ATSUCreateTextLayoutWithTextPtr failed\n"); + if( eStatus != noErr ) + return false; + + // prepare setting of layout controls + static const int nMaxTagCount = 1; + ATSUAttributeTag aTagAttrs[ nMaxTagCount ]; + ByteCount aTagSizes[ nMaxTagCount ]; + ATSUAttributeValuePtr aTagValues[ nMaxTagCount ]; + + // prepare control of "glyph fallback" + const SalData* pSalData = GetSalData(); + ATSUFontFallbacks aFontFallbacks = pSalData->mpFontList->maFontFallbacks; + aTagAttrs[0] = kATSULineFontFallbacksTag; + aTagSizes[0] = sizeof( ATSUFontFallbacks ); + aTagValues[0] = &aFontFallbacks; + + // set paragraph layout controls + ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues ); + + // enable "glyph fallback" + ATSUSetTransientFontMatching( maATSULayout, true ); + + // control run-specific layout controls + if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG) != 0 ) + { + // control BiDi defaults + MacOSBOOL nLineDirTag = kATSULeftToRightBaseDirection; + if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) != 0 ) + nLineDirTag = kATSURightToLeftBaseDirection; + aTagAttrs[0] = kATSULineDirectionTag; + aTagSizes[0] = sizeof( nLineDirTag ); + aTagValues[0] = &nLineDirTag; + // set run-specific layout controls +#if 0 // why don't line-controls work as reliably as layout-controls??? + ATSUSetLineControls( maATSULayout, rArgs.mnMinCharPos, 1, aTagAttrs, aTagSizes, aTagValues ); +#else + ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues ); +#endif + } + + return true; +} + +// ----------------------------------------------------------------------- +/** + * ATSLayout::AdjustLayout : Adjust layout style + * + * @param rArgs: contains attributes relevant to do a text specific layout + * + * Adjust text layout by moving glyphs to match the requested logical widths + * + * @return : none +**/ +void ATSLayout::AdjustLayout( ImplLayoutArgs& rArgs ) +{ + int nOrigWidth = GetTextWidth(); + int nPixelWidth = rArgs.mnLayoutWidth; + if( !nPixelWidth && rArgs.mpDXArray ) { + // for now we are only interested in the layout width + // TODO: use all mpDXArray elements for layouting + nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 ]; + + // workaround for ATSUI not using trailing spaces for justification + int i = mnCharCount; + while( (--i >= 0) && IsSpacingGlyph( rArgs.mpStr[mnMinCharPos+i]|GF_ISCHAR ) ) {} + if( i < 0 ) // nothing to do if the text is all spaces + return; + // #i91685# trailing letters are left aligned (right aligned for RTL) + mnTrailingSpaceWidth = rArgs.mpDXArray[ mnCharCount-1 ]; + if( i > 0 ) + mnTrailingSpaceWidth -= rArgs.mpDXArray[ i-1 ]; + InitGIA(); // ensure valid mpCharWidths[], TODO: use GetIdealX() instead? + mnTrailingSpaceWidth -= Fixed2Vcl( mpCharWidths[i] ); + // ignore trailing space for calculating the available width + nOrigWidth -= mnTrailingSpaceWidth; + nPixelWidth -= mnTrailingSpaceWidth; + // in RTL-layouts trailing spaces are leftmost + // TODO: use BiDi-algorithm to thoroughly check this assumption + if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) + mnBaseAdv = mnTrailingSpaceWidth; + } + // return early if there is nothing to do + if( !nPixelWidth ) + return; + + // HACK: justification requests which change the width by just one pixel were probably + // #i86038# introduced by lossy conversions between integer based coordinate system + // => ignoring such requests has many more benefits than eventual drawbacks + if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) ) + return; + + // changing the layout will make all previous measurements invalid + InvalidateMeasurements(); + + ATSUAttributeTag nTags[3]; + ATSUAttributeValuePtr nVals[3]; + ByteCount nBytes[3]; + + Fixed nFixedWidth = Vcl2Fixed( nPixelWidth ); + mnCachedWidth = nFixedWidth; + Fract nFractFactor = kATSUFullJustification; + ATSLineLayoutOptions nLineLayoutOptions = kATSLineHasNoHangers | kATSLineHasNoOpticalAlignment | kATSLineBreakToNearestCharacter; + + nTags[0] = kATSULineWidthTag; + nVals[0] = &nFixedWidth; + nBytes[0] = sizeof(Fixed); + nTags[1] = kATSULineLayoutOptionsTag; + nVals[1] = &nLineLayoutOptions; + nBytes[1] = sizeof(ATSLineLayoutOptions); + nTags[2] = kATSULineJustificationFactorTag; + nVals[2] = &nFractFactor; + nBytes[2] = sizeof(Fract); + + OSStatus eStatus = ATSUSetLayoutControls( maATSULayout, 3, nTags, nBytes, nVals ); + if( eStatus != noErr ) + return; + + // update the measurements of the justified layout to match the justification request + if( rArgs.mpDXArray ) + InitGIA( &rArgs ); +} + +// ----------------------------------------------------------------------- +/** + * ATSLayout::DrawText : Draw text to screen + * + * @param rGraphics: device to draw to + * + * Draw the layouted text to the CGContext + * + * @return : none +**/ +void ATSLayout::DrawText( SalGraphics& rGraphics ) const +{ + AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics); + + // short circuit if there is nothing to do + if( (mnCharCount <= 0) + || !rAquaGraphics.CheckContext() ) + return; + + // the view is vertically flipped => flipped glyphs + // so apply a temporary transformation that it flips back + // also compensate if the font was size limited + CGContextSaveGState( rAquaGraphics.mrContext ); + CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale ); + CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText ); + + // prepare ATSUI drawing attributes + static const ItemCount nMaxControls = 8; + ATSUAttributeTag theTags[ nMaxControls ]; + ByteCount theSizes[ nMaxControls]; + ATSUAttributeValuePtr theValues[ nMaxControls ]; + ItemCount numcontrols = 0; + + // Tell ATSUI to use CoreGraphics + theTags[numcontrols] = kATSUCGContextTag; + theSizes[numcontrols] = sizeof( CGContextRef ); + theValues[numcontrols++] = &rAquaGraphics.mrContext; + + // Rotate if necessary + if( rAquaGraphics.mnATSUIRotation != 0 ) + { + Fixed theAngle = rAquaGraphics.mnATSUIRotation; + theTags[numcontrols] = kATSULineRotationTag; + theSizes[numcontrols] = sizeof( Fixed ); + theValues[numcontrols++] = &theAngle; + } + + DBG_ASSERT( (numcontrols <= nMaxControls), "ATSLayout::DrawText() numcontrols overflow" ); + OSStatus theErr = ATSUSetLayoutControls (maATSULayout, numcontrols, theTags, theSizes, theValues); + DBG_ASSERT( (theErr==noErr), "ATSLayout::DrawText ATSUSetLayoutControls failed!\n" ); + + // Draw the text + const Point aPos = GetDrawPosition( Point(mnBaseAdv,0) ); + const Fixed nFixedX = Vcl2Fixed( +aPos.X() ); + const Fixed nFixedY = Vcl2Fixed( -aPos.Y() ); // adjusted for y-mirroring + if( maSubPortions.empty() ) + ATSUDrawText( maATSULayout, mnMinCharPos, mnCharCount, nFixedX, nFixedY ); + else + { + // draw the sub-portions and apply individual adjustments + SubPortionVector::const_iterator it = maSubPortions.begin(); + for(; it != maSubPortions.end(); ++it ) + { + const SubPortion& rSubPortion = *it; + // calculate sub-portion offset for rotated text + Fixed nXOfsFixed = 0, nYOfsFixed = 0; + if( rAquaGraphics.mnATSUIRotation != 0 ) + { + const double fRadians = rAquaGraphics.mnATSUIRotation * (M_PI/0xB40000); + nXOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * cos( fRadians )); + nYOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * sin( fRadians )); + } + + // draw sub-portions + ATSUDrawText( maATSULayout, + rSubPortion.mnMinCharPos, rSubPortion.mnEndCharPos - rSubPortion.mnMinCharPos, + nFixedX + nXOfsFixed, nFixedY + nYOfsFixed ); + } + } + + // request an update of the changed window area + if( rAquaGraphics.IsWindowGraphics() ) + { + Rect drawRect; // rectangle of the changed area + theErr = ATSUMeasureTextImage( maATSULayout, + mnMinCharPos, mnCharCount, nFixedX, nFixedY, &drawRect ); + if( theErr == noErr ) + { + // FIXME: transformation from baseline to top left + // with the simple approach below we invalidate too much + short d = drawRect.bottom - drawRect.top; + drawRect.top -= d; + drawRect.bottom += d; + CGRect aRect = CGRectMake( drawRect.left, drawRect.top, + drawRect.right - drawRect.left, + drawRect.bottom - drawRect.top ); + aRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aRect ); + rAquaGraphics.RefreshRect( aRect ); + } + } + + // restore the original graphic context transformations + CGContextRestoreGState( rAquaGraphics.mrContext ); +} + +// ----------------------------------------------------------------------- +/** + * ATSLayout::GetNextGlyphs : Get info about next glyphs in the layout + * + * @param nLen: max number of char + * @param pGlyphs: returned array of glyph ids + * @param rPos: returned x starting position + * @param nStart: index of the first requested glyph + * @param pGlyphAdvances: returned array of glyphs advances + * @param pCharIndexes: returned array of char indexes + * + * Returns infos about the next glyphs in the text layout + * + * @return : number of glyph details that were provided +**/ +int ATSLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIDs, Point& rPos, int& nStart, + sal_Int32* pGlyphAdvances, int* pCharIndexes ) const +{ + if( nStart < 0 ) // first glyph requested? + nStart = 0; + + // get glyph measurements + InitGIA(); + // some measurements are only needed for multi-glyph results + if( nLen > 1 ) + { + GetIdealX(); + GetDeltaY(); + } + + if( nStart >= mnGlyphCount ) // no glyph left? + return 0; + + // calculate glyph position relative to layout base + // TODO: avoid for nStart!=0 case by reusing rPos + Fixed nXOffset = mnBaseAdv; + for( int i = 0; i < nStart; ++i ) + nXOffset += mpGlyphAdvances[ i ]; + // if sub-portion offsets are involved there is an additional x-offset + if( !maSubPortions.empty() ) + { + // prepare to find the sub-portion + int nCharPos = nStart + mnMinCharPos; + if( mpGlyphs2Chars ) + nCharPos = mpGlyphs2Chars[nStart]; + + // find the matching subportion + // TODO: is a non-linear search worth it? + SubPortionVector::const_iterator it = maSubPortions.begin(); + for(; it != maSubPortions.end(); ++it) { + const SubPortion& r = *it; + if( nCharPos < r.mnMinCharPos ) + continue; + if( nCharPos >= r.mnEndCharPos ) + continue; + // apply the sub-portion xoffset + nXOffset += r.mnXOffset; + break; + } + } + + Fixed nYOffset = 0; + if( mpDeltaY ) + nYOffset = mpDeltaY[ nStart ]; + + // calculate absolute position in pixel units + const Point aRelativePos( Fix2Long(static_cast<Fixed>(nXOffset*mfFontScale)), Fix2Long(static_cast<Fixed>(nYOffset*mfFontScale)) ); + rPos = GetDrawPosition( aRelativePos ); + + // update return values + int nCount = 0; + while( nCount < nLen ) + { + ++nCount; + sal_GlyphId nGlyphId = mpGlyphIds[nStart]; + + // check if glyph fallback is needed for this glyph + // TODO: use ATSUDirectGetLayoutDataArrayPtrFromTextLayout(kATSUDirectDataStyleIndex) API instead? + const int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[nStart] : nStart + mnMinCharPos; + ATSUFontID nFallbackFontID = kATSUInvalidFontID; + UniCharArrayOffset nChangedOffset = 0; + UniCharCount nChangedLength = 0; + OSStatus eStatus = ATSUMatchFontsToText( maATSULayout, nCharPos, kATSUToTextEnd, + &nFallbackFontID, &nChangedOffset, &nChangedLength ); + if( (eStatus == kATSUFontsMatched) && ((int)nChangedOffset == nCharPos) ) + { + // fallback is needed + if( !mpFallbackInfo ) + mpFallbackInfo = new FallbackInfo; + // register fallback font + const int nLevel = mpFallbackInfo->AddFallback( nFallbackFontID ); + // update sal_GlyphId with fallback level + nGlyphId |= (nLevel << GF_FONTSHIFT); + } + + // update resulting glyphid array + *(pGlyphIDs++) = nGlyphId; + + // update returned glyph advance array + if( pGlyphAdvances ) + *(pGlyphAdvances++) = Fixed2Vcl( mpGlyphAdvances[nStart] ); + + // update returned index-into-string array + if( pCharIndexes ) + { + int nCharPos; + if( mpGlyphs2Chars ) + nCharPos = mpGlyphs2Chars[nStart]; + else + nCharPos = nStart + mnMinCharPos; + *(pCharIndexes++) = nCharPos; + } + + // stop at last glyph + if( ++nStart >= mnGlyphCount ) + break; + + // stop when next the x-position is unexpected + if( !maSubPortions.empty() ) + break; // TODO: finish the complete sub-portion + if( !pGlyphAdvances && mpGlyphOrigAdvs ) + if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] ) + break; + + // stop when the next y-position is unexpected + if( mpDeltaY ) + if( mpDeltaY[nStart-1] != mpDeltaY[nStart] ) + break; + } + + return nCount; +} + +// ----------------------------------------------------------------------- +/** + * ATSLayout::GetTextWidth : Get typographic width of layouted text + * + * Get typographic bounds of the text + * + * @return : text width +**/ +long ATSLayout::GetTextWidth() const +{ + if( mnCharCount <= 0 ) + return 0; + + DBG_ASSERT( (maATSULayout!=NULL), "ATSLayout::GetTextWidth() with maATSULayout==NULL !\n"); + if( !maATSULayout ) + return 0; + + if( !mnCachedWidth ) + { + // prepare precise measurements on pixel based or reference-device + const UInt16 eTypeOfBounds = kATSUseFractionalOrigins; + + // determine number of needed measurement trapezoids + ItemCount nMaxBounds = 0; + OSStatus err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount, + eTypeOfBounds, 0, NULL, &nMaxBounds ); + if( (err != noErr) + || (nMaxBounds <= 0) ) + return 0; + + // get the trapezoids + typedef std::vector<ATSTrapezoid> TrapezoidVector; + TrapezoidVector aTrapezoidVector( nMaxBounds ); + ItemCount nBoundsCount = 0; + err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount, + eTypeOfBounds, nMaxBounds, &aTrapezoidVector[0], &nBoundsCount ); + if( err != noErr ) + return 0; + + DBG_ASSERT( (nBoundsCount <= nMaxBounds), "ATSLayout::GetTextWidth() : too many trapezoids !\n"); + + // find the bound extremas + Fixed nLeftBound = 0; + Fixed nRightBound = 0; + for( ItemCount i = 0; i < nBoundsCount; ++i ) + { + const ATSTrapezoid& rTrap = aTrapezoidVector[i]; + if( (i == 0) || (nLeftBound < rTrap.lowerLeft.x) ) + nLeftBound = rTrap.lowerLeft.x; + if( (i == 0) || (nRightBound > rTrap.lowerRight.x) ) + nRightBound = rTrap.lowerRight.x; + } + + // measure the bound extremas + mnCachedWidth = nRightBound - nLeftBound; + // adjust for eliminated trailing space widths + } + + int nScaledWidth = Fixed2Vcl( mnCachedWidth ); + nScaledWidth += mnTrailingSpaceWidth; + return nScaledWidth; +} + +// ----------------------------------------------------------------------- +/** + * ATSLayout::FillDXArray : Get Char widths + * + * @param pDXArray: array to be filled with x-advances + * + * Fill the pDXArray with horizontal deltas : CharWidths + * + * @return : typographical width of the complete text layout +**/ +long ATSLayout::FillDXArray( long* pDXArray ) const +{ + // short circuit requests which don't need full details + if( !pDXArray ) + return GetTextWidth(); + + // check assumptions + DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::FillDXArray() with nTSW!=0" ); + + // initialize details about the resulting layout + InitGIA(); + + // distribute the widths among the string elements + int nPixWidth = 0; + mnCachedWidth = 0; + for( int i = 0; i < mnCharCount; ++i ) + { + // convert and adjust for accumulated rounding errors + mnCachedWidth += mpCharWidths[i]; + const int nOldPixWidth = nPixWidth; + nPixWidth = Fixed2Vcl( mnCachedWidth ); + pDXArray[i] = nPixWidth - nOldPixWidth; + } + + return nPixWidth; +} + +// ----------------------------------------------------------------------- +/** + * ATSLayout::GetTextBreak : Find line break depending on width + * + * @param nMaxWidth : maximal logical text width in subpixel units + * @param nCharExtra: expanded/condensed spacing in subpixel units + * @param nFactor: number of subpixel units per pixel + * + * Measure the layouted text to find the typographical line break + * the result is needed by the language specific line breaking + * + * @return : string index corresponding to the suggested line break +**/ +int ATSLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const +{ + if( !maATSULayout ) + return STRING_LEN; + + // the semantics of the legacy use case (nCharExtra!=0) cannot be mapped to ATSUBreakLine() + if( nCharExtra != 0 ) + { + // prepare the measurement by layouting and measuring the un-expanded/un-condensed text + if( !InitGIA() ) + return STRING_LEN; + + // TODO: use a better way than by testing each the char position + ATSUTextMeasurement nATSUSumWidth = 0; + const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nMaxWidth / nFactor ); + const ATSUTextMeasurement nATSUExtraWidth = Vcl2Fixed( nCharExtra ) / nFactor; + for( int i = 0; i < mnCharCount; ++i ) + { + nATSUSumWidth += mpCharWidths[i]; + if( nATSUSumWidth >= nATSUMaxWidth ) + return (mnMinCharPos + i); + nATSUSumWidth += nATSUExtraWidth; + if( nATSUSumWidth >= nATSUMaxWidth ) + if( i+1 < mnCharCount ) + return (mnMinCharPos + i); + } + + return STRING_LEN; + } + + // get a quick overview on what could fit + const long nPixelWidth = (nMaxWidth - (nCharExtra * mnCharCount)) / nFactor; + if( nPixelWidth <= 0 ) + return mnMinCharPos; + + // check assumptions + DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::GetTextBreak() with nTSW!=0" ); + + // initial measurement of text break position + UniCharArrayOffset nBreakPos = mnMinCharPos; + const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nPixelWidth ); + OSStatus eStatus = ATSUBreakLine( maATSULayout, mnMinCharPos, + nATSUMaxWidth, false, &nBreakPos ); + + if( (eStatus != noErr) && (eStatus != kATSULineBreakInWord) ) + return STRING_LEN; + + // the result from ATSUBreakLine() doesn't match the semantics expected by its + // application layer callers from SW+SVX+I18N. Adjust the results to the expectations: + + // ATSU reports that everything fits even when trailing spaces would break the line + // #i89789# OOo's application layers expect STRING_LEN if everything fits + if( nBreakPos >= static_cast<UniCharArrayOffset>(mnEndCharPos) ) + return STRING_LEN; + + // GetTextBreak()'s callers expect it to return the "stupid visual line break". + // Returning anything else result.s in subtle problems in the application layers. + static const bool bInWord = true; // TODO: add as argument to GetTextBreak() method + if( !bInWord ) + return nBreakPos; + + // emulate stupid visual line breaking by line breaking for the remaining width + ATSUTextMeasurement nLeft, nRight, nDummy; + eStatus = ATSUGetUnjustifiedBounds( maATSULayout, mnMinCharPos, nBreakPos-mnMinCharPos, + &nLeft, &nRight, &nDummy, &nDummy ); + if( eStatus != noErr ) + return nBreakPos; + const ATSUTextMeasurement nATSURemWidth = nATSUMaxWidth - (nRight - nLeft); + if( nATSURemWidth <= 0 ) + return nBreakPos; + UniCharArrayOffset nBreakPosInWord = nBreakPos; + eStatus = ATSUBreakLine( maATSULayout, nBreakPos, nATSURemWidth, false, &nBreakPosInWord ); + return nBreakPosInWord; +} + +// ----------------------------------------------------------------------- +/** + * ATSLayout::GetCaretPositions : Find positions of carets + * + * @param nMaxIndex position to which we want to find the carets + * + * Fill the array of positions of carets (for cursors and selections) + * + * @return : none +**/ +void ATSLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const +{ + DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)), + "ATSLayout::GetCaretPositions() : invalid number of caret pairs requested"); + + // initialize the caret positions + for( int i = 0; i < nMaxIndex; ++i ) + pCaretXArray[ i ] = -1; + + for( int n = 0; n <= mnCharCount; ++n ) + { + // measure the characters cursor position + typedef unsigned char Boolean; + const Boolean bIsLeading = true; + ATSUCaret aCaret0, aCaret1; + Boolean bIsSplit; + OSStatus eStatus = ATSUOffsetToCursorPosition( maATSULayout, + mnMinCharPos + n, bIsLeading, kATSUByCharacter, + &aCaret0, &aCaret1, &bIsSplit ); + if( eStatus != noErr ) + continue; + const Fixed nFixedPos = mnBaseAdv + aCaret0.fX; + // convert the measurement to pixel units + const int nPixelPos = Fixed2Vcl( nFixedPos ); + // update previous trailing position + if( n > 0 ) + pCaretXArray[2*n-1] = nPixelPos; + // update current leading position + if( 2*n >= nMaxIndex ) + break; + pCaretXArray[2*n+0] = nPixelPos; + } +} + +// ----------------------------------------------------------------------- +/** + * ATSLayout::GetBoundRect : Get rectangle dim containing the layouted text + * + * @param rVCLRect: rectangle of text image (layout) measures + * + * Get ink bounds of the text + * + * @return : measurement valid +**/ +bool ATSLayout::GetBoundRect( SalGraphics&, Rectangle& rVCLRect ) const +{ + const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) ); + const Fixed nFixedX = Vcl2Fixed( +aPos.X() ); + const Fixed nFixedY = Vcl2Fixed( +aPos.Y() ); + + Rect aMacRect; + OSStatus eStatus = ATSUMeasureTextImage( maATSULayout, + mnMinCharPos, mnCharCount, nFixedX, nFixedY, &aMacRect ); + if( eStatus != noErr ) + return false; + + // ATSU top-bottom are vertically flipped from a VCL aspect + rVCLRect.Left() = AtsuPix2Vcl( aMacRect.left ); + rVCLRect.Top() = AtsuPix2Vcl( aMacRect.top ); + rVCLRect.Right() = AtsuPix2Vcl( aMacRect.right ); + rVCLRect.Bottom() = AtsuPix2Vcl( aMacRect.bottom ); + return true; +} + +// ----------------------------------------------------------------------- +/** + * ATSLayout::InitGIA() : get many informations about layouted text + * + * Fills arrays of information about the gylph layout previously done + * in ASTLayout::LayoutText() : glyph advance (width), glyph delta Y (from baseline), + * mapping between glyph index and character index, chars widths + * + * @return : true if everything could be computed, otherwise false +**/ +bool ATSLayout::InitGIA( ImplLayoutArgs* pArgs ) const +{ + // no need to run InitGIA more than once on the same ATSLayout object + if( mnGlyphCount >= 0 ) + return true; + mnGlyphCount = 0; + + // Workaround a bug in ATSUI with empty string + if( mnCharCount <= 0 ) + return false; + + // initialize character details + mpCharWidths = new Fixed[ mnCharCount ]; + mpChars2Glyphs = new int[ mnCharCount ]; + for( int n = 0; n < mnCharCount; ++n ) + { + mpCharWidths[ n ] = 0; + mpChars2Glyphs[ n ] = -1; + } + + // get details about the glyph layout + ItemCount iLayoutDataCount; + const ATSLayoutRecord* pALR; + OSStatus eStatus = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( + maATSULayout, mnMinCharPos, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, + (void**)&pALR, &iLayoutDataCount ); + DBG_ASSERT( (eStatus==noErr), "ATSLayout::InitGIA() : no ATSLayoutRecords!\n"); + if( (eStatus != noErr) + || (iLayoutDataCount <= 1) ) + return false; + + // initialize glyph details + mpGlyphIds = new ATSGlyphRef[ iLayoutDataCount ]; + mpGlyphAdvances = new Fixed[ iLayoutDataCount ]; + mpGlyphs2Chars = new int[ iLayoutDataCount ]; + + // measure details of the glyph layout + Fixed nLeftPos = 0; + for( ItemCount i = 0; i < iLayoutDataCount; ++i ) + { + const ATSLayoutRecord& rALR = pALR[i]; + + // distribute the widths as fairly as possible among the chars + const int nRelativeIdx = (rALR.originalOffset / 2); + if( i+1 < iLayoutDataCount ) + mpCharWidths[ nRelativeIdx ] += pALR[i+1].realPos - rALR.realPos; + + // new glyph is available => finish measurement of old glyph + if( mnGlyphCount > 0 ) + mpGlyphAdvances[ mnGlyphCount-1 ] = rALR.realPos - nLeftPos; + + // ignore marker or deleted glyphs + enum { MARKED_OUTGLYPH=0xFFFE, DROPPED_OUTGLYPH=0xFFFF}; + if( rALR.glyphID >= MARKED_OUTGLYPH ) + continue; + + DBG_ASSERT( !(rALR.flags & kATSGlyphInfoTerminatorGlyph), + "ATSLayout::InitGIA(): terminator glyph not marked as deleted!" ); + + // store details of the visible glyphs + nLeftPos = rALR.realPos; + mpGlyphIds[ mnGlyphCount ] = rALR.glyphID; + + // map visible glyphs to their counterparts in the UTF16-character array + mpGlyphs2Chars[ mnGlyphCount ] = nRelativeIdx + mnMinCharPos; + mpChars2Glyphs[ nRelativeIdx ] = mnGlyphCount; + + ++mnGlyphCount; + } + + // measure complete width + mnCachedWidth = mnBaseAdv; + mnCachedWidth += pALR[iLayoutDataCount-1].realPos - pALR[0].realPos; + +#if (OSL_DEBUG_LEVEL > 1) + Fixed nWidthSum = mnBaseAdv; + for( int n = 0; n < mnCharCount; ++n ) + nWidthSum += mpCharWidths[ n ]; + DBG_ASSERT( (nWidthSum==mnCachedWidth), + "ATSLayout::InitGIA(): measured widths do not match!\n" ); +#endif + + // #i91183# we need to split up the portion into sub-portions + // if the ATSU-layout differs too much from the requested layout + if( pArgs && pArgs->mpDXArray ) + { + // TODO: non-strong-LTR case cases should be handled too + if( (pArgs->mnFlags & TEXT_LAYOUT_BIDI_STRONG) + && !(pArgs->mnFlags & TEXT_LAYOUT_BIDI_RTL) ) + { + Fixed nSumCharWidths = 0; + SubPortion aSubPortion = { mnMinCharPos, 0, 0 }; + for( int i = 0; i < mnCharCount; ++i ) + { + // calculate related logical position + nSumCharWidths += mpCharWidths[i]; + + // start new sub-portion if needed + const Fixed nNextXPos = Vcl2Fixed(pArgs->mpDXArray[i]); + const Fixed nNextXOffset = nNextXPos - nSumCharWidths; + const Fixed nFixedDiff = aSubPortion.mnXOffset - nNextXOffset; + if( (nFixedDiff < -0xC000) || (nFixedDiff > +0xC000) ) { + // get to the end of the current sub-portion + // prevent splitting up at diacritics etc. + int j = i; + while( (++j < mnCharCount) && !mpCharWidths[j] ); + aSubPortion.mnEndCharPos = mnMinCharPos + j; + // emit current sub-portion + maSubPortions.push_back( aSubPortion ); + // prepare next sub-portion + aSubPortion.mnMinCharPos = aSubPortion.mnEndCharPos; + aSubPortion.mnXOffset = nNextXOffset; + } + } + + // emit the remaining sub-portion + if( !maSubPortions.empty() ) + { + aSubPortion.mnEndCharPos = mnEndCharPos; + if( aSubPortion.mnEndCharPos != aSubPortion.mnMinCharPos ) + maSubPortions.push_back( aSubPortion ); + } + } + + // override layouted charwidths with requested charwidths + for( int n = 0; n < mnCharCount; ++n ) + mpCharWidths[ n ] = pArgs->mpDXArray[ n ]; + } + + // release the ATSU layout records + ATSUDirectReleaseLayoutDataArrayPtr(NULL, + kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void**)&pALR ); + + return true; +} + +// ----------------------------------------------------------------------- + +bool ATSLayout::GetIdealX() const +{ + // compute the ideal advance widths only once + if( mpGlyphOrigAdvs != NULL ) + return true; + + DBG_ASSERT( (mpGlyphIds!=NULL), "GetIdealX() called with mpGlyphIds==NULL !" ); + DBG_ASSERT( (mrATSUStyle!=NULL), "GetIdealX called with mrATSUStyle==NULL !" ); + + // TODO: cache ideal metrics per glyph? + std::vector<ATSGlyphIdealMetrics> aIdealMetrics; + aIdealMetrics.resize( mnGlyphCount ); + OSStatus theErr = ATSUGlyphGetIdealMetrics( mrATSUStyle, + mnGlyphCount, &mpGlyphIds[0], sizeof(*mpGlyphIds), &aIdealMetrics[0] ); + DBG_ASSERT( (theErr==noErr), "ATSUGlyphGetIdealMetrics failed!"); + if( theErr != noErr ) + return false; + + mpGlyphOrigAdvs = new Fixed[ mnGlyphCount ]; + for( int i = 0;i < mnGlyphCount;++i ) + mpGlyphOrigAdvs[i] = FloatToFixed( aIdealMetrics[i].advance.x ); + + return true; +} + +// ----------------------------------------------------------------------- + +bool ATSLayout::GetDeltaY() const +{ + // don't bother to get the same delta-y-array more than once + if( mpDeltaY != NULL ) + return true; + +#if 1 + if( !maATSULayout ) + return false; + + // get and keep the y-deltas in the mpDeltaY member variable + // => release it in the destructor + ItemCount nDeltaCount = 0; + OSStatus theErr = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( + maATSULayout, mnMinCharPos, kATSUDirectDataBaselineDeltaFixedArray, + (void**)&mpDeltaY, &nDeltaCount ); + + DBG_ASSERT( (theErr==noErr ), "mpDeltaY - ATSUDirectGetLayoutDataArrayPtrFromTextLayout failed!\n"); + if( theErr != noErr ) + return false; + + if( mpDeltaY == NULL ) + return true; + + if( nDeltaCount != (ItemCount)mnGlyphCount ) + { + DBG_WARNING( "ATSLayout::GetDeltaY() : wrong deltaY count!" ); + ATSUDirectReleaseLayoutDataArrayPtr( NULL, + kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY ); + mpDeltaY = NULL; + return false; + } +#endif + + return true; +} + +// ----------------------------------------------------------------------- + +#define DELETEAZ( X ) { delete[] X; X = NULL; } + +void ATSLayout::InvalidateMeasurements() +{ + mnGlyphCount = -1; + DELETEAZ( mpGlyphIds ); + DELETEAZ( mpCharWidths ); + DELETEAZ( mpChars2Glyphs ); + DELETEAZ( mpGlyphs2Chars ); + DELETEAZ( mpGlyphRTLFlags ); + DELETEAZ( mpGlyphAdvances ); + DELETEAZ( mpGlyphOrigAdvs ); + DELETEAZ( mpDeltaY ); +} + +// ======================================================================= + +#if 0 +// helper class to convert ATSUI outlines to VCL PolyPolygons +class PolyArgs +{ +public: + PolyArgs(); + ~PolyArgs(); + + void Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset ); + void AddPoint( const Float32Point&, PolyFlags ); + void ClosePolygon(); + +private: + PolyPolygon* mpPolyPoly; + long mnXOffset, mnYOffset; + + Point* mpPointAry; + BYTE* mpFlagAry; + USHORT mnMaxPoints; + + USHORT mnPointCount; + USHORT mnPolyCount; + bool mbHasOffline; +}; + +// ----------------------------------------------------------------------- + +PolyArgs::PolyArgs() +: mpPolyPoly(NULL), + mnPointCount(0), + mnPolyCount(0), + mbHasOffline(false) +{ + mnMaxPoints = 256; + mpPointAry = new Point[ mnMaxPoints ]; + mpFlagAry = new BYTE [ mnMaxPoints ]; +} + +// ----------------------------------------------------------------------- + +PolyArgs::~PolyArgs() +{ + delete[] mpFlagAry; + delete[] mpPointAry; +} + +// ----------------------------------------------------------------------- + +void PolyArgs::Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset ) +{ + mnXOffset = nXOffset; + mnYOffset = nYOffset; + mpPolyPoly = pPolyPoly; + + mpPolyPoly->Clear(); + mnPointCount = 0; + mnPolyCount = 0; +} + +// ----------------------------------------------------------------------- + +void PolyArgs::AddPoint( const Float32Point& rPoint, PolyFlags eFlags ) +{ + if( mnPointCount >= mnMaxPoints ) + { + // resize if needed (TODO: use STL?) + mnMaxPoints *= 4; + Point* mpNewPoints = new Point[ mnMaxPoints ]; + BYTE* mpNewFlags = new BYTE[ mnMaxPoints ]; + for( int i = 0; i < mnPointCount; ++i ) + { + mpNewPoints[ i ] = mpPointAry[ i ]; + mpNewFlags[ i ] = mpFlagAry[ i ]; + } + delete[] mpFlagAry; + delete[] mpPointAry; + mpPointAry = mpNewPoints; + mpFlagAry = mpNewFlags; + } + + // convert to pixels and add startpoint offset + int nXPos = Float32ToInt( rPoint.x ); + int nYPos = Float32ToInt( rPoint.y ); + mpPointAry[ mnPointCount ] = Point( nXPos + mnXOffset, nYPos + mnYOffset ); + // set point flags + mpFlagAry[ mnPointCount++ ]= eFlags; + mbHasOffline |= (eFlags != POLY_NORMAL); +} + +// ----------------------------------------------------------------------- + +void PolyArgs::ClosePolygon() +{ + if( !mnPolyCount++ ) + return; + + // append finished polygon + Polygon aPoly( mnPointCount, mpPointAry, (mbHasOffline ? mpFlagAry : NULL) ); + mpPolyPoly->Insert( aPoly ); + + // prepare for new polygon + mnPointCount = 0; + mbHasOffline = false; +} +#endif +// ======================================================================= + +// glyph fallback is supported directly by Aqua +// so methods used only by MultiSalLayout can be dummy implementated +bool ATSLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& rPPV ) const { return false; } +void ATSLayout::InitFont() {} +void ATSLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {} +void ATSLayout::DropGlyph( int /*nStart*/ ) {} +void ATSLayout::Simplify( bool /*bIsBase*/ ) {} + +// get the ImplFontData for a glyph fallback font +// for a glyphid that was returned by ATSLayout::GetNextGlyphs() +const ImplFontData* ATSLayout::GetFallbackFontData( sal_GlyphId nGlyphId ) const +{ + // check if any fallback fonts were needed + if( !mpFallbackInfo ) + return NULL; + // check if the current glyph needs a fallback font + int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT; + if( !nFallbackLevel ) + return NULL; + return mpFallbackInfo->GetFallbackFontData( nFallbackLevel ); +} + +// ======================================================================= + +int FallbackInfo::AddFallback( ATSUFontID nFontId ) +{ + // check if the fallback font is already known + for( int nLevel = 0; nLevel < mnMaxLevel; ++nLevel ) + if( maATSUFontId[ nLevel ] == nFontId ) + return (nLevel + 1); + + // append new fallback font if possible + if( mnMaxLevel >= MAX_FALLBACK-1 ) + return 0; + // keep ATSU font id of fallback font + maATSUFontId[ mnMaxLevel ] = nFontId; + // find and cache the corresponding ImplFontData pointer + const SystemFontList* pSFL = GetSalData()->mpFontList; + const ImplMacFontData* pFontData = pSFL->GetFontDataFromId( nFontId ); + maFontData[ mnMaxLevel ] = pFontData; + // increase fallback level by one + return (++mnMaxLevel); +} + +// ----------------------------------------------------------------------- + +const ImplFontData* FallbackInfo::GetFallbackFontData( int nFallbackLevel ) const +{ + const ImplMacFontData* pFallbackFont = maFontData[ nFallbackLevel-1 ]; + return pFallbackFont; +} + +// ======================================================================= + +SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel ) +{ + ATSLayout* pATSLayout = new ATSLayout( maATSUStyle, mfFontScale ); + return pATSLayout; +} + +// ======================================================================= + diff --git a/vcl/aqua/source/gdi/salatsuifontutils.cxx b/vcl/aqua/source/gdi/salatsuifontutils.cxx new file mode 100644 index 000000000000..8281c41ceeab --- /dev/null +++ b/vcl/aqua/source/gdi/salatsuifontutils.cxx @@ -0,0 +1,552 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <boost/assert.hpp> +#include <vector> +#include <set> + +#include "salgdi.h" +#include "saldata.hxx" +#include "vcl/svapp.hxx" +#include "salatsuifontutils.hxx" + +// we have to get the font attributes from the name table +// since neither head's macStyle nor OS/2's panose are easily available +// during font enumeration. macStyle bits would be not sufficient anyway +// and SFNT fonts on Mac usually do not contain an OS/2 table. +static void UpdateAttributesFromPSName( const String& rPSName, ImplDevFontAttributes& rDFA ) +{ + ByteString aPSName( rPSName, RTL_TEXTENCODING_UTF8 ); + aPSName.ToLowerAscii(); + + // TODO: use a multi-string ignore-case matcher once it becomes available + if( (aPSName.Search("regular") != STRING_NOTFOUND) + || (aPSName.Search("normal") != STRING_NOTFOUND) + || (aPSName.Search("roman") != STRING_NOTFOUND) + || (aPSName.Search("medium") != STRING_NOTFOUND) + || (aPSName.Search("plain") != STRING_NOTFOUND) + || (aPSName.Search("standard") != STRING_NOTFOUND) + || (aPSName.Search("std") != STRING_NOTFOUND) ) + { + rDFA.meWidthType = WIDTH_NORMAL; + rDFA.meWeight = WEIGHT_NORMAL; + rDFA.meItalic = ITALIC_NONE; + } + + // heuristics for font weight + if (aPSName.Search("extrablack") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_BLACK; + else if (aPSName.Search("black") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_BLACK; + //else if (aPSName.Search("book") != STRING_NOTFOUND) + // rDFA.meWeight = WEIGHT_SEMIBOLD; + else if( (aPSName.Search("semibold") != STRING_NOTFOUND) + || (aPSName.Search("smbd") != STRING_NOTFOUND)) + rDFA.meWeight = WEIGHT_SEMIBOLD; + else if (aPSName.Search("ultrabold") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_ULTRABOLD; + else if (aPSName.Search("extrabold") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_BLACK; + else if( (aPSName.Search("bold") != STRING_NOTFOUND) + || (aPSName.Search("-bd") != STRING_NOTFOUND)) + rDFA.meWeight = WEIGHT_BOLD; + else if (aPSName.Search("extralight") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_ULTRALIGHT; + else if (aPSName.Search("ultralight") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_ULTRALIGHT; + else if (aPSName.Search("light") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_LIGHT; + else if (aPSName.Search("thin") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_THIN; + else if (aPSName.Search("-w3") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_LIGHT; + else if (aPSName.Search("-w4") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_SEMILIGHT; + else if (aPSName.Search("-w5") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_NORMAL; + else if (aPSName.Search("-w6") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_SEMIBOLD; + else if (aPSName.Search("-w7") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_BOLD; + else if (aPSName.Search("-w8") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_ULTRABOLD; + else if (aPSName.Search("-w9") != STRING_NOTFOUND) + rDFA.meWeight = WEIGHT_BLACK; + + // heuristics for font slant + if( (aPSName.Search("italic") != STRING_NOTFOUND) + || (aPSName.Search(" ital") != STRING_NOTFOUND) + || (aPSName.Search("cursive") != STRING_NOTFOUND) + || (aPSName.Search("-it") != STRING_NOTFOUND) + || (aPSName.Search("lightit") != STRING_NOTFOUND) + || (aPSName.Search("mediumit") != STRING_NOTFOUND) + || (aPSName.Search("boldit") != STRING_NOTFOUND) + || (aPSName.Search("cnit") != STRING_NOTFOUND) + || (aPSName.Search("bdcn") != STRING_NOTFOUND) + || (aPSName.Search("bdit") != STRING_NOTFOUND) + || (aPSName.Search("condit") != STRING_NOTFOUND) + || (aPSName.Search("bookit") != STRING_NOTFOUND) + || (aPSName.Search("blackit") != STRING_NOTFOUND) ) + rDFA.meItalic = ITALIC_NORMAL; + if( (aPSName.Search("oblique") != STRING_NOTFOUND) + || (aPSName.Search("inclined") != STRING_NOTFOUND) + || (aPSName.Search("slanted") != STRING_NOTFOUND) ) + rDFA.meItalic = ITALIC_OBLIQUE; + + // heuristics for font width + if( (aPSName.Search("condensed") != STRING_NOTFOUND) + || (aPSName.Search("-cond") != STRING_NOTFOUND) + || (aPSName.Search("boldcond") != STRING_NOTFOUND) + || (aPSName.Search("boldcn") != STRING_NOTFOUND) + || (aPSName.Search("cnit") != STRING_NOTFOUND) ) + rDFA.meWidthType = WIDTH_CONDENSED; + else if (aPSName.Search("narrow") != STRING_NOTFOUND) + rDFA.meWidthType = WIDTH_SEMI_CONDENSED; + else if (aPSName.Search("expanded") != STRING_NOTFOUND) + rDFA.meWidthType = WIDTH_EXPANDED; + else if (aPSName.Search("wide") != STRING_NOTFOUND) + rDFA.meWidthType = WIDTH_EXPANDED; + + // heuristics for font pitch + if( (aPSName.Search("mono") != STRING_NOTFOUND) + || (aPSName.Search("courier") != STRING_NOTFOUND) + || (aPSName.Search("monaco") != STRING_NOTFOUND) + || (aPSName.Search("typewriter") != STRING_NOTFOUND) ) + rDFA.mePitch = PITCH_FIXED; + + // heuristics for font family type + if( (aPSName.Search("script") != STRING_NOTFOUND) + || (aPSName.Search("chancery") != STRING_NOTFOUND) + || (aPSName.Search("zapfino") != STRING_NOTFOUND)) + rDFA.meFamily = FAMILY_SCRIPT; + else if( (aPSName.Search("comic") != STRING_NOTFOUND) + || (aPSName.Search("outline") != STRING_NOTFOUND) + || (aPSName.Search("pinpoint") != STRING_NOTFOUND) ) + rDFA.meFamily = FAMILY_DECORATIVE; + else if( (aPSName.Search("sans") != STRING_NOTFOUND) + || (aPSName.Search("arial") != STRING_NOTFOUND) ) + rDFA.meFamily = FAMILY_SWISS; + else if( (aPSName.Search("roman") != STRING_NOTFOUND) + || (aPSName.Search("times") != STRING_NOTFOUND) ) + rDFA.meFamily = FAMILY_ROMAN; + + // heuristics for codepoint semantic + if( (aPSName.Search("symbol") != STRING_NOTFOUND) + || (aPSName.Search("dings") != STRING_NOTFOUND) + || (aPSName.Search("dingbats") != STRING_NOTFOUND) + || (aPSName.Search("ornaments") != STRING_NOTFOUND) + || (aPSName.Search("embellishments") != STRING_NOTFOUND) ) + rDFA.mbSymbolFlag = true; + + // #i100020# special heuristic for names with single-char styles + // NOTE: we are checking name that hasn't been lower-cased + if( rPSName.Len() > 3 ) + { + int i = rPSName.Len(); + sal_Unicode c = rPSName.GetChar( --i ); + if( c == 'C' ) { // "capitals" + rDFA.meFamily = FAMILY_DECORATIVE; + c = rPSName.GetChar( --i ); + } + if( c == 'O' ) { // CFF-based OpenType + c = rPSName.GetChar( --i ); + } + if( c == 'I' ) { // "italic" + rDFA.meItalic = ITALIC_NORMAL; + c = rPSName.GetChar( --i ); + } + if( c == 'B' ) // "bold" + rDFA.meWeight = WEIGHT_BOLD; + if( c == 'C' ) // "capitals" + rDFA.meFamily = FAMILY_DECORATIVE; + // TODO: check that all single-char styles have been resolved? + } +} + +// ----------------------------------------------------------------------- + +static bool GetDevFontAttributes( ATSUFontID nFontID, ImplDevFontAttributes& rDFA ) +{ + // all ATSU fonts are device fonts that can be directly rotated + rDFA.mbOrientation = true; + rDFA.mbDevice = true; + rDFA.mnQuality = 0; + + // reset the attributes + rDFA.meFamily = FAMILY_DONTKNOW; + rDFA.mePitch = PITCH_VARIABLE; + rDFA.meWidthType = WIDTH_NORMAL; + rDFA.meWeight = WEIGHT_NORMAL; + rDFA.meItalic = ITALIC_NONE; + rDFA.mbSymbolFlag = false; + + // ignore bitmap fonts + ATSFontRef rATSFontRef = FMGetATSFontRefFromFont( nFontID ); + ByteCount nHeadLen = 0; + OSStatus rc = ATSFontGetTable( rATSFontRef, 0x68656164/*head*/, 0, 0, NULL, &nHeadLen ); + if( (rc != noErr) || (nHeadLen <= 0) ) + return false; + + // all scalable fonts on this platform are subsettable + rDFA.mbSubsettable = true; + rDFA.mbEmbeddable = false; + + // prepare iterating over all name strings of the font + ItemCount nFontNameCount = 0; + rc = ATSUCountFontNames( nFontID, &nFontNameCount ); + if( rc != noErr ) + return false; + int nBestNameValue = 0; + int nBestStyleValue = 0; + FontLanguageCode eBestLangCode = 0; + const FontLanguageCode eUILangCode = Application::GetSettings().GetUILanguage(); + typedef std::vector<char> NameBuffer; + NameBuffer aNameBuffer( 256 ); + + // iterate over all available name strings of the font + for( ItemCount nNameIndex = 0; nNameIndex < nFontNameCount; ++nNameIndex ) + { + ByteCount nNameLength = 0; + + FontNameCode eFontNameCode; + FontPlatformCode eFontNamePlatform; + FontScriptCode eFontNameScript; + FontLanguageCode eFontNameLanguage; + rc = ATSUGetIndFontName( nFontID, nNameIndex, 0, NULL, + &nNameLength, &eFontNameCode, &eFontNamePlatform, &eFontNameScript, &eFontNameLanguage ); + if( rc != noErr ) + continue; + + // ignore non-interesting name entries + if( (eFontNameCode != kFontFamilyName) + && (eFontNameCode != kFontStyleName) + && (eFontNameCode != kFontPostscriptName) ) + continue; + + // heuristic to find the most common font name + // prefering default language names or even better the names matching to the UI language + int nNameValue = (eFontNameLanguage==eUILangCode) ? 0 : ((eFontNameLanguage==0) ? -10 : -20); + rtl_TextEncoding eEncoding = RTL_TEXTENCODING_UNICODE; + const int nPlatformEncoding = ((int)eFontNamePlatform << 8) + (int)eFontNameScript; + switch( nPlatformEncoding ) + { + case 0x000: nNameValue += 23; break; // Unicode 1.0 + case 0x001: nNameValue += 24; break; // Unicode 1.1 + case 0x002: nNameValue += 25; break; // iso10646_1993 + case 0x003: nNameValue += 26; break; // UCS-2 + case 0x301: nNameValue += 27; break; // Win UCS-2 + case 0x004: // UCS-4 + case 0x30A: nNameValue += 0; // Win-UCS-4 + eEncoding = RTL_TEXTENCODING_UCS4; + break; + case 0x100: nNameValue += 21; // Mac Roman + eEncoding = RTL_TEXTENCODING_APPLE_ROMAN; + break; + case 0x300: nNameValue = 0; // Win Symbol encoded name! + rDFA.mbSymbolFlag = true; // (often seen for symbol fonts) + break; + default: nNameValue = 0; // ignore other encodings + break; + } + + // ignore name entries with no useful encoding + if( nNameValue <= 0 ) + continue; + if( nNameLength >= aNameBuffer.size() ) + continue; + + // get the encoded name + aNameBuffer.reserve( nNameLength+1 ); // extra byte helps for debugging + rc = ATSUGetIndFontName( nFontID, nNameIndex, nNameLength, &aNameBuffer[0], + &nNameLength, &eFontNameCode, &eFontNamePlatform, &eFontNameScript, &eFontNameLanguage ); + if( rc != noErr ) + continue; + + // convert to unicode name + UniString aUtf16Name; + if( eEncoding == RTL_TEXTENCODING_UNICODE ) // we are just interested in UTF16 encoded names + aUtf16Name = UniString( (const sal_Unicode*)&aNameBuffer[0], nNameLength/2 ); + else if( eEncoding == RTL_TEXTENCODING_UCS4 ) + aUtf16Name = UniString(); // TODO + else // assume the non-unicode encoded names are byte encoded + aUtf16Name = UniString( &aNameBuffer[0], nNameLength, eEncoding ); + + // ignore empty strings + if( aUtf16Name.Len() <= 0 ) + continue; + + // handle the name depending on its namecode + switch( eFontNameCode ) + { + case kFontFamilyName: + // ignore font names starting with '.' + if( aUtf16Name.GetChar(0) == '.' ) + nNameValue = 0; + else if( rDFA.maName.Len() ) + { + // even if a family name is not the one we are looking for + // it is still useful as a font name alternative + if( rDFA.maMapNames.Len() ) + rDFA.maMapNames += ';'; + rDFA.maMapNames += (nBestNameValue < nNameValue) ? rDFA.maName : aUtf16Name; + } + if( nBestNameValue < nNameValue ) + { + // get the best family name + nBestNameValue = nNameValue; + eBestLangCode = eFontNameLanguage; + rDFA.maName = aUtf16Name; + } + break; + case kFontStyleName: + // get a style name matching to the family name + if( nBestStyleValue < nNameValue ) + { + nBestStyleValue = nNameValue; + rDFA.maStyleName = aUtf16Name; + } + break; + case kFontPostscriptName: + // use the postscript name to get some useful info + UpdateAttributesFromPSName( aUtf16Name, rDFA ); + break; + default: + // TODO: use other name entries too? + break; + } + } + +#if 0 // multiple-master fonts are mostly obsolete nowadays + // if we still want to support them this should probably be done one frame higher + ItemCount nMaxInstances = 0; + rc = ATSUCountFontInstances ( nFontID, &nMaxInstances ); + for( ItemCount nInstanceIndex = 0; nInstanceIndex < nMaxInstances; ++nInstanceIndex ) + { + ItemCount nMaxVariations = 0; + rc = ATSUGetFontInstance( nFontID, nInstanceIndex, 0, NULL, NULL, &nMaxVariations ); + if( (rc == noErr) && (nMaxVariations > 0) ) + { + fprintf(stderr,"\tnMaxVariations=%d\n",(int)nMaxVariations); + typedef ::std::vector<ATSUFontVariationAxis> VariationAxisVector; + typedef ::std::vector<ATSUFontVariationValue> VariationValueVector; + VariationAxisVector aVariationAxes( nMaxVariations ); + VariationValueVector aVariationValues( nMaxVariations ); + ItemCount nVariationCount = 0; + rc = ATSUGetFontInstance ( nFontID, nInstanceIndex, nMaxVariations, + &aVariationAxes[0], &aVariationValues[0], &nVariationCount ); + fprintf(stderr,"\tnVariationCount=%d\n",(int)nVariationCount); + for( ItemCount nVariationIndex = 0; nVariationIndex < nMaxVariations; ++nVariationIndex ) + { + const char* pTag = (const char*)&aVariationAxes[nVariationIndex]; + fprintf(stderr,"\tvariation[%d] \'%c%c%c%c\' is %d\n", (int)nVariationIndex, + pTag[3],pTag[2],pTag[1],pTag[0], (int)aVariationValues[nVariationIndex]); + } + } + } +#endif + +#if 0 // selecting non-defaulted font features is not enabled yet + ByteString aFName( rDFA.maName, RTL_TEXTENCODING_UTF8 ); + ByteString aSName( rDFA.maStyleName, RTL_TEXTENCODING_UTF8 ); + ItemCount nMaxFeatures = 0; + rc = ATSUCountFontFeatureTypes( nFontID, &nMaxFeatures ); + fprintf(stderr,"Font \"%s\" \"%s\" has %d features\n",aFName.GetBuffer(),aSName.GetBuffer(),rc); + if( (rc == noErr) && (nMaxFeatures > 0) ) + { + typedef std::vector<ATSUFontFeatureType> FeatureVector; + FeatureVector aFeatureVector( nMaxFeatures ); + ItemCount nFeatureCount = 0; + rc = ATSUGetFontFeatureTypes( nFontID, nMaxFeatures, &aFeatureVector[0], &nFeatureCount ); + fprintf(stderr,"nFeatureCount=%d\n",(int)nFeatureCount); + for( ItemCount nFeatureIndex = 0; nFeatureIndex < nFeatureCount; ++nFeatureIndex ) + { + ItemCount nMaxSelectors = 0; + rc = ATSUCountFontFeatureSelectors( nFontID, aFeatureVector[nFeatureIndex], &nMaxSelectors ); + fprintf(stderr,"\tFeature[%d] = %d has %d selectors\n", + (int)nFeatureIndex, (int)aFeatureVector[nFeatureIndex], (int)nMaxSelectors ); + typedef std::vector<ATSUFontFeatureSelector> SelectorVector; + SelectorVector aSelectorVector( nMaxSelectors ); + typedef std::vector<MacOSBoolean> BooleanVector; + BooleanVector aEnabledVector( nMaxSelectors ); + BooleanVector aExclusiveVector( nMaxSelectors ); + ItemCount nSelectorCount = 0; + rc = ATSUGetFontFeatureSelectors ( nFontID, aFeatureVector[nFeatureIndex], nMaxSelectors, + &aSelectorVector[0], &aEnabledVector[0], &nSelectorCount, &aExclusiveVector[0]); + for( ItemCount nSelectorIndex = 0; nSelectorIndex < nSelectorCount; ++nSelectorIndex ) + { + FontNameCode eFontNameCode; + rc = ATSUGetFontFeatureNameCode( nFontID, aFeatureVector[nFeatureIndex], + aSelectorVector[nSelectorIndex], &eFontNameCode ); + fprintf(stderr,"\t\tselector[%d] n=%d e=%d, x=%d\n", + (int)nSelectorIndex, (int)eFontNameCode, + aEnabledVector[nSelectorIndex], aExclusiveVector[nSelectorIndex] ); + } + } + } +#endif + + bool bRet = (rDFA.maName.Len() > 0); + return bRet; +} + +// ======================================================================= + +SystemFontList::SystemFontList() +{ + // count available system fonts + ItemCount nATSUICompatibleFontsAvailable = 0; + if( ATSUFontCount(&nATSUICompatibleFontsAvailable) != noErr ) + return; + if( nATSUICompatibleFontsAvailable <= 0 ) + return; + + // enumerate available system fonts + typedef std::vector<ATSUFontID> AtsFontIDVector; + AtsFontIDVector aFontIDVector( nATSUICompatibleFontsAvailable ); + ItemCount nFontItemsCount = 0; + if( ATSUGetFontIDs( &aFontIDVector[0], aFontIDVector.capacity(), &nFontItemsCount ) != noErr ) + return; + + BOOST_ASSERT(nATSUICompatibleFontsAvailable == nFontItemsCount && "Strange I would expect them to be equal"); + + // prepare use of the available fonts + AtsFontIDVector::const_iterator it = aFontIDVector.begin(); + for(; it != aFontIDVector.end(); ++it ) + { + const ATSUFontID nFontID = *it; + ImplDevFontAttributes aDevFontAttr; + if( !GetDevFontAttributes( nFontID, aDevFontAttr ) ) + continue; + ImplMacFontData* pFontData = new ImplMacFontData( aDevFontAttr, nFontID ); + maFontContainer[ nFontID ] = pFontData; + } + + InitGlyphFallbacks(); +} + +// ----------------------------------------------------------------------- + +SystemFontList::~SystemFontList() +{ + MacFontContainer::const_iterator it = maFontContainer.begin(); + for(; it != maFontContainer.end(); ++it ) + delete (*it).second; + maFontContainer.clear(); + + ATSUDisposeFontFallbacks( maFontFallbacks ); +} + +// ----------------------------------------------------------------------- + +void SystemFontList::AnnounceFonts( ImplDevFontList& rFontList ) const +{ + MacFontContainer::const_iterator it = maFontContainer.begin(); + for(; it != maFontContainer.end(); ++it ) + rFontList.Add( (*it).second->Clone() ); +} + +// ----------------------------------------------------------------------- + +// not all fonts are suitable for glyph fallback => sort them +struct GfbCompare{ bool operator()(const ImplMacFontData*, const ImplMacFontData*); }; + +inline bool GfbCompare::operator()( const ImplMacFontData* pA, const ImplMacFontData* pB ) +{ + // use symbol fonts only as last resort + bool bPreferA = !pA->IsSymbolFont(); + bool bPreferB = !pB->IsSymbolFont(); + if( bPreferA != bPreferB ) + return bPreferA; + // prefer scalable fonts + bPreferA = pA->IsScalable(); + bPreferB = pB->IsScalable(); + if( bPreferA != bPreferB ) + return bPreferA; + // prefer non-slanted fonts + bPreferA = (pA->GetSlant() == ITALIC_NONE); + bPreferB = (pB->GetSlant() == ITALIC_NONE); + if( bPreferA != bPreferB ) + return bPreferA; + // prefer normal weight fonts + bPreferA = (pA->GetWeight() == WEIGHT_NORMAL); + bPreferB = (pB->GetWeight() == WEIGHT_NORMAL); + if( bPreferA != bPreferB ) + return bPreferA; + // prefer normal width fonts + bPreferA = (pA->GetWidthType() == WIDTH_NORMAL); + bPreferB = (pB->GetWidthType() == WIDTH_NORMAL); + if( bPreferA != bPreferB ) + return bPreferA; + return false; +} + +void SystemFontList::InitGlyphFallbacks() +{ + // sort fonts for "glyph fallback" + typedef std::multiset<const ImplMacFontData*,GfbCompare> FallbackSet; + FallbackSet aFallbackSet; + MacFontContainer::const_iterator it = maFontContainer.begin(); + for(; it != maFontContainer.end(); ++it ) + { + const ImplMacFontData* pIFD = (*it).second; + // TODO: subsettable/embeddable glyph fallback only for PDF export? + if( pIFD->IsSubsettable() || pIFD->IsEmbeddable() ) + aFallbackSet.insert( pIFD ); + } + + // tell ATSU about font preferences for "glyph fallback" + typedef std::vector<ATSUFontID> AtsFontIDVector; + AtsFontIDVector aFallbackVector; + aFallbackVector.reserve( maFontContainer.size() ); + FallbackSet::const_iterator itFData = aFallbackSet.begin(); + for(; itFData != aFallbackSet.end(); ++itFData ) + { + const ImplMacFontData* pFontData = (*itFData); + ATSUFontID nFontID = (ATSUFontID)pFontData->GetFontId(); + aFallbackVector.push_back( nFontID ); + } + + ATSUCreateFontFallbacks( &maFontFallbacks ); + ATSUSetObjFontFallbacks( maFontFallbacks, + aFallbackVector.size(), &aFallbackVector[0], kATSUSequentialFallbacksPreferred ); +} + +// ----------------------------------------------------------------------- + +ImplMacFontData* SystemFontList::GetFontDataFromId( ATSUFontID nFontId ) const +{ + MacFontContainer::const_iterator it = maFontContainer.find( nFontId ); + if( it == maFontContainer.end() ) + return NULL; + return (*it).second; +} + +// ----------------------------------------------------------------------- + diff --git a/vcl/aqua/source/gdi/salbmp.cxx b/vcl/aqua/source/gdi/salbmp.cxx new file mode 100644 index 000000000000..0e58c35b5fad --- /dev/null +++ b/vcl/aqua/source/gdi/salbmp.cxx @@ -0,0 +1,904 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "tools/color.hxx" +#include "salbmp.h" +#include "vcl/bitmap.hxx" // for BitmapSystemData +#include "vcl/salbtype.hxx" +#include "vcl/bmpfast.hxx" + +#include "basebmp/scanlineformats.hxx" +#include "basebmp/color.hxx" +#include "basegfx/vector/b2ivector.hxx" + +#include <boost/bind.hpp> + +#include "salinst.h" + +// ======================================================================= + +static bool isValidBitCount( sal_uInt16 nBitCount ) +{ + return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 16) || (nBitCount == 24) || (nBitCount == 32); +} + +// ======================================================================= + +AquaSalBitmap::AquaSalBitmap() +: mxGraphicContext( NULL ) +, mxCachedImage( NULL ) +, mnBits(0) +, mnWidth(0) +, mnHeight(0) +, mnBytesPerRow(0) +{ +} + +// ------------------------------------------------------------------ + +AquaSalBitmap::~AquaSalBitmap() +{ + Destroy(); +} + +// ------------------------------------------------------------------ + +bool AquaSalBitmap::Create( CGLayerRef xLayer, int nBitmapBits, + int nX, int nY, int nWidth, int nHeight, bool /*bMirrorVert*/ ) +{ + DBG_ASSERT( xLayer, "AquaSalBitmap::Create() from non-layered context" ); + + // sanitize input parameters + if( nX < 0 ) + nWidth += nX, nX = 0; + if( nY < 0 ) + nHeight += nY, nY = 0; + const CGSize aLayerSize = CGLayerGetSize( xLayer ); + if( nWidth >= (int)aLayerSize.width - nX ) + nWidth = (int)aLayerSize.width - nX; + if( nHeight >= (int)aLayerSize.height - nY ) + nHeight = (int)aLayerSize.height - nY; + if( (nWidth < 0) || (nHeight < 0) ) + nWidth = nHeight = 0; + + // initialize properties + mnWidth = nWidth; + mnHeight = nHeight; + mnBits = nBitmapBits ? nBitmapBits : 32; + + // initialize drawing context + CreateContext(); + + // copy layer content into the bitmap buffer + const CGPoint aSrcPoint = { -nX, -nY }; + ::CGContextDrawLayerAtPoint( mxGraphicContext, aSrcPoint, xLayer ); + return true; +} + +// ------------------------------------------------------------------ + +bool AquaSalBitmap::Create( const Size& rSize, USHORT nBits, const BitmapPalette& rBitmapPalette ) +{ + if( !isValidBitCount( nBits ) ) + return false; + maPalette = rBitmapPalette; + mnBits = nBits; + mnWidth = rSize.Width(); + mnHeight = rSize.Height(); + return AllocateUserData(); +} + +// ------------------------------------------------------------------ + +bool AquaSalBitmap::Create( const SalBitmap& rSalBmp ) +{ + return Create( rSalBmp, rSalBmp.GetBitCount() ); +} + +// ------------------------------------------------------------------ + +bool AquaSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics ) +{ + return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() ); +} + +// ------------------------------------------------------------------ + +bool AquaSalBitmap::Create( const SalBitmap& rSalBmp, USHORT nNewBitCount ) +{ + const AquaSalBitmap& rSourceBitmap = static_cast<const AquaSalBitmap&>(rSalBmp); + + if( isValidBitCount( nNewBitCount ) && rSourceBitmap.maUserBuffer.get() ) + { + mnBits = nNewBitCount; + mnWidth = rSourceBitmap.mnWidth; + mnHeight = rSourceBitmap.mnHeight; + maPalette = rSourceBitmap.maPalette; + + if( AllocateUserData() ) + { + ConvertBitmapData( mnWidth, mnHeight, mnBits, mnBytesPerRow, maPalette, maUserBuffer.get(), rSourceBitmap.mnBits, rSourceBitmap.mnBytesPerRow, rSourceBitmap.maPalette, rSourceBitmap.maUserBuffer.get() ); + return true; + } + } + return false; +} + +// ------------------------------------------------------------------ + +void AquaSalBitmap::Destroy() +{ + DestroyContext(); + maUserBuffer.reset(); +} + +// ------------------------------------------------------------------ + +void AquaSalBitmap::DestroyContext() +{ + CGImageRelease( mxCachedImage ); + mxCachedImage = NULL; + + if( mxGraphicContext ) + { + CGContextRelease( mxGraphicContext ); + mxGraphicContext = NULL; + maContextBuffer.reset(); + } +} + +// ------------------------------------------------------------------ + +bool AquaSalBitmap::CreateContext() +{ + DestroyContext(); + + // prepare graphics context + // convert image from user input if available + const bool bSkipConversion = !maUserBuffer; + if( bSkipConversion ) + AllocateUserData(); + + // default to RGBA color space + CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; + CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst; + + // convert data into something accepted by CGBitmapContextCreate() + size_t bitsPerComponent = (mnBits == 16) ? 5 : 8; + sal_uInt32 nContextBytesPerRow = mnBytesPerRow; + if( (mnBits == 16) || (mnBits == 32) ) + { + // no conversion needed for truecolor + maContextBuffer = maUserBuffer; + } + else if( (mnBits == 8) && maPalette.IsGreyPalette() ) + { + // no conversion needed for grayscale + maContextBuffer = maUserBuffer; + aCGColorSpace = GetSalData()->mxGraySpace; + aCGBmpInfo = kCGImageAlphaNone; + bitsPerComponent = mnBits; + } + // TODO: is special handling for 1bit input buffers worth it? + else + { + // convert user data to 32 bit + nContextBytesPerRow = mnWidth << 2; + try + { + maContextBuffer.reset( new sal_uInt8[ mnHeight * nContextBytesPerRow ] ); + + if( !bSkipConversion ) + ConvertBitmapData( mnWidth, mnHeight, + 32, nContextBytesPerRow, maPalette, maContextBuffer.get(), + mnBits, mnBytesPerRow, maPalette, maUserBuffer.get() ); + } + catch( std::bad_alloc ) + { + mxGraphicContext = 0; + } + } + + if( maContextBuffer.get() ) + { + mxGraphicContext = ::CGBitmapContextCreate( maContextBuffer.get(), mnWidth, mnHeight, + bitsPerComponent, nContextBytesPerRow, aCGColorSpace, aCGBmpInfo ); + } + + if( !mxGraphicContext ) + maContextBuffer.reset(); + + return mxGraphicContext != NULL; +} + +// ------------------------------------------------------------------ + +bool AquaSalBitmap::AllocateUserData() +{ + Destroy(); + + if( mnWidth && mnHeight ) + { + mnBytesPerRow = 0; + + switch( mnBits ) + { + case 1: mnBytesPerRow = (mnWidth + 7) >> 3; break; + case 4: mnBytesPerRow = (mnWidth + 1) >> 1; break; + case 8: mnBytesPerRow = mnWidth; break; + case 16: mnBytesPerRow = mnWidth << 1; break; + case 24: mnBytesPerRow = (mnWidth << 1) + mnWidth; break; + case 32: mnBytesPerRow = mnWidth << 2; break; + default: + DBG_ERROR("vcl::AquaSalBitmap::AllocateUserData(), illegal bitcount!"); + } + } + + try + { + if( mnBytesPerRow ) + maUserBuffer.reset( new sal_uInt8[mnBytesPerRow * mnHeight] ); + } + catch( const std::bad_alloc& ) + { + DBG_ERROR( "vcl::AquaSalBitmap::AllocateUserData: bad alloc" ); + maUserBuffer.reset( NULL ); + mnBytesPerRow = 0; + } + + return maUserBuffer.get() != 0; +} + +// ------------------------------------------------------------------ + +class ImplPixelFormat +{ +protected: + sal_uInt8* pData; +public: + static ImplPixelFormat* GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette ); + + virtual void StartLine( sal_uInt8* pLine ) { pData = pLine; } + virtual void SkipPixel( sal_uInt32 nPixel ) = 0; + virtual ColorData ReadPixel() = 0; + virtual void WritePixel( ColorData nColor ) = 0; +}; + +class ImplPixelFormat32 : public ImplPixelFormat +// currently ARGB-format for 32bit depth +{ +public: + virtual void SkipPixel( sal_uInt32 nPixel ) + { + pData += nPixel << 2; + } + virtual ColorData ReadPixel() + { + const ColorData c = RGB_COLORDATA( pData[1], pData[2], pData[3] ); + pData += 4; + return c; + } + virtual void WritePixel( ColorData nColor ) + { + *pData++ = 0; + *pData++ = COLORDATA_RED( nColor ); + *pData++ = COLORDATA_GREEN( nColor ); + *pData++ = COLORDATA_BLUE( nColor ); + } +}; + +class ImplPixelFormat24 : public ImplPixelFormat +// currently BGR-format for 24bit depth +{ +public: + virtual void SkipPixel( sal_uInt32 nPixel ) + { + pData += (nPixel << 1) + nPixel; + } + virtual ColorData ReadPixel() + { + const ColorData c = RGB_COLORDATA( pData[2], pData[1], pData[0] ); + pData += 3; + return c; + } + virtual void WritePixel( ColorData nColor ) + { + *pData++ = COLORDATA_BLUE( nColor ); + *pData++ = COLORDATA_GREEN( nColor ); + *pData++ = COLORDATA_RED( nColor ); + } +}; + +class ImplPixelFormat16 : public ImplPixelFormat +// currently R5G6B5-format for 16bit depth +{ +protected: + sal_uInt16* pData16; +public: + + virtual void StartLine( sal_uInt8* pLine ) + { + pData16 = (sal_uInt16*)pLine; + } + virtual void SkipPixel( sal_uInt32 nPixel ) + { + pData += nPixel; + } + virtual ColorData ReadPixel() + { + const ColorData c = RGB_COLORDATA( (*pData & 0x7c00) >> 7, (*pData & 0x03e0) >> 2 , (*pData & 0x001f) << 3 ); + pData++; + return c; + } + virtual void WritePixel( ColorData nColor ) + { + *pData++ = ((COLORDATA_RED( nColor ) & 0xf8 ) << 7 ) || + ((COLORDATA_GREEN( nColor ) & 0xf8 ) << 2 ) || + ((COLORDATA_BLUE( nColor ) & 0xf8 ) >> 3 ); + } +}; + +class ImplPixelFormat8 : public ImplPixelFormat +{ +private: + const BitmapPalette& mrPalette; + +public: + ImplPixelFormat8( const BitmapPalette& rPalette ) + : mrPalette( rPalette ) + { + } + virtual void SkipPixel( sal_uInt32 nPixel ) + { + pData += nPixel; + } + virtual ColorData ReadPixel() + { + return mrPalette[ *pData++ ].operator Color().GetColor(); + } + virtual void WritePixel( ColorData nColor ) + { + const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) ); + *pData++ = static_cast< sal_uInt8 >( mrPalette.GetBestIndex( aColor ) ); + } +}; + +class ImplPixelFormat4 : public ImplPixelFormat +{ +private: + const BitmapPalette& mrPalette; + sal_uInt32 mnX; + sal_uInt32 mnShift; + +public: + ImplPixelFormat4( const BitmapPalette& rPalette ) + : mrPalette( rPalette ) + { + } + virtual void SkipPixel( sal_uInt32 nPixel ) + { + mnX += nPixel; + if( (nPixel & 1) ) + mnShift ^= 4; + } + virtual void StartLine( sal_uInt8* pLine ) + { + pData = pLine; + mnX = 0; + mnShift = 4; + } + virtual ColorData ReadPixel() + { + const BitmapColor& rColor = mrPalette[( pData[mnX >> 1] >> mnShift) & 0x0f]; + mnX++; + mnShift ^= 4; + return rColor.operator Color().GetColor(); + } + virtual void WritePixel( ColorData nColor ) + { + const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) ); + pData[mnX>>1] &= (0xf0 >> mnShift); + pData[mnX>>1] |= (static_cast< sal_uInt8 >( mrPalette.GetBestIndex( aColor ) ) & 0x0f); + mnX++; + mnShift ^= 4; + } +}; + +class ImplPixelFormat1 : public ImplPixelFormat +{ +private: + const BitmapPalette& mrPalette; + sal_uInt32 mnX; + +public: + ImplPixelFormat1( const BitmapPalette& rPalette ) + : mrPalette( rPalette ) + { + } + virtual void SkipPixel( sal_uInt32 nPixel ) + { + mnX += nPixel; + } + virtual void StartLine( sal_uInt8* pLine ) + { + pData = pLine; + mnX = 0; + } + virtual ColorData ReadPixel() + { + const BitmapColor& rColor = mrPalette[ (pData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1]; + mnX++; + return rColor.operator Color().GetColor(); + } + virtual void WritePixel( ColorData nColor ) + { + const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) ); + if( mrPalette.GetBestIndex( aColor ) & 1 ) + pData[ mnX >> 3 ] |= 1 << ( 7 - ( mnX & 7 ) ); + else + pData[ mnX >> 3 ] &= ~( 1 << ( 7 - ( mnX & 7 ) ) ); + mnX++; + } +}; + +ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette ) +{ + switch( nBits ) + { + case 1: return new ImplPixelFormat1( rPalette ); + case 4: return new ImplPixelFormat4( rPalette ); + case 8: return new ImplPixelFormat8( rPalette ); + case 16: return new ImplPixelFormat16; + case 24: return new ImplPixelFormat24; + case 32: return new ImplPixelFormat32; + } + + return 0; +} + +void AquaSalBitmap::ConvertBitmapData( sal_uInt32 nWidth, sal_uInt32 nHeight, + sal_uInt16 nDestBits, sal_uInt32 nDestBytesPerRow, const BitmapPalette& rDestPalette, sal_uInt8* pDestData, + sal_uInt16 nSrcBits, sal_uInt32 nSrcBytesPerRow, const BitmapPalette& rSrcPalette, sal_uInt8* pSrcData ) + +{ + if( (nDestBytesPerRow == nSrcBytesPerRow) && (nDestBits == nSrcBits) && ((nSrcBits != 8) || (rDestPalette.operator==( rSrcPalette ))) ) + { + // simple case, same format, so just copy + memcpy( pDestData, pSrcData, nHeight * nDestBytesPerRow ); + return; + } + + // try accelerated conversion if possible + // TODO: are other truecolor conversions except BGR->ARGB worth it? + bool bConverted = false; + if( (nSrcBits == 24) && (nDestBits == 32) ) + { + // TODO: extend bmpfast.cxx with a method that can be directly used here + BitmapBuffer aSrcBuf; + aSrcBuf.mnFormat = BMP_FORMAT_24BIT_TC_BGR; + aSrcBuf.mpBits = pSrcData; + aSrcBuf.mnBitCount = nSrcBits; + aSrcBuf.mnScanlineSize = nSrcBytesPerRow; + BitmapBuffer aDstBuf; + aDstBuf.mnFormat = BMP_FORMAT_32BIT_TC_ARGB; + aDstBuf.mpBits = pDestData; + aSrcBuf.mnBitCount = nDestBits; + aDstBuf.mnScanlineSize = nDestBytesPerRow; + + aSrcBuf.mnWidth = aDstBuf.mnWidth = nWidth; + aSrcBuf.mnHeight = aDstBuf.mnHeight = nHeight; + + SalTwoRect aTwoRects; + aTwoRects.mnSrcX = aTwoRects.mnDestX = 0; + aTwoRects.mnSrcY = aTwoRects.mnDestY = 0; + aTwoRects.mnSrcWidth = aTwoRects.mnDestWidth = mnWidth; + aTwoRects.mnSrcHeight = aTwoRects.mnDestHeight = mnHeight; + bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects ); + } + + if( !bConverted ) + { + // TODO: this implementation is for clarety, not for speed + + ImplPixelFormat* pD = ImplPixelFormat::GetFormat( nDestBits, rDestPalette ); + ImplPixelFormat* pS = ImplPixelFormat::GetFormat( nSrcBits, rSrcPalette ); + + if( pD && pS ) + { + sal_uInt32 nY = nHeight; + while( nY-- ) + { + pD->StartLine( pDestData ); + pS->StartLine( pSrcData ); + + sal_uInt32 nX = nWidth; + while( nX-- ) + pD->WritePixel( pS->ReadPixel() ); + + pSrcData += nSrcBytesPerRow; + pDestData += nDestBytesPerRow; + } + } + delete pS; + delete pD; + } +} + +// ------------------------------------------------------------------ + +Size AquaSalBitmap::GetSize() const +{ + return Size( mnWidth, mnHeight ); +} + +// ------------------------------------------------------------------ + +USHORT AquaSalBitmap::GetBitCount() const +{ + return mnBits; +} + +// ------------------------------------------------------------------ + +static struct pal_entry +{ + BYTE mnRed; + BYTE mnGreen; + BYTE mnBlue; +} +const aImplSalSysPalEntryAry[ 16 ] = +{ +{ 0, 0, 0 }, +{ 0, 0, 0x80 }, +{ 0, 0x80, 0 }, +{ 0, 0x80, 0x80 }, +{ 0x80, 0, 0 }, +{ 0x80, 0, 0x80 }, +{ 0x80, 0x80, 0 }, +{ 0x80, 0x80, 0x80 }, +{ 0xC0, 0xC0, 0xC0 }, +{ 0, 0, 0xFF }, +{ 0, 0xFF, 0 }, +{ 0, 0xFF, 0xFF }, +{ 0xFF, 0, 0 }, +{ 0xFF, 0, 0xFF }, +{ 0xFF, 0xFF, 0 }, +{ 0xFF, 0xFF, 0xFF } +}; + +const BitmapPalette& GetDefaultPalette( int mnBits, bool bMonochrome ) +{ + if( bMonochrome ) + return Bitmap::GetGreyPalette( 1U << mnBits ); + + // at this point we should provide some kind of default palette + // since all other platforms do so, too. + static bool bDefPalInit = false; + static BitmapPalette aDefPalette256; + static BitmapPalette aDefPalette16; + static BitmapPalette aDefPalette2; + if( ! bDefPalInit ) + { + bDefPalInit = true; + aDefPalette256.SetEntryCount( 256 ); + aDefPalette16.SetEntryCount( 16 ); + aDefPalette2.SetEntryCount( 2 ); + + // Standard colors + unsigned int i; + for( i = 0; i < 16; i++ ) + { + aDefPalette16[i] = + aDefPalette256[i] = BitmapColor( aImplSalSysPalEntryAry[i].mnRed, + aImplSalSysPalEntryAry[i].mnGreen, + aImplSalSysPalEntryAry[i].mnBlue ); + } + + aDefPalette2[0] = BitmapColor( 0, 0, 0 ); + aDefPalette2[1] = BitmapColor( 0xff, 0xff, 0xff ); + + // own palette (6/6/6) + const int DITHER_PAL_STEPS = 6; + const BYTE DITHER_PAL_DELTA = 51; + int nB, nG, nR; + BYTE nRed, nGreen, nBlue; + for( nB=0, nBlue=0; nB < DITHER_PAL_STEPS; nB++, nBlue += DITHER_PAL_DELTA ) + { + for( nG=0, nGreen=0; nG < DITHER_PAL_STEPS; nG++, nGreen += DITHER_PAL_DELTA ) + { + for( nR=0, nRed=0; nR < DITHER_PAL_STEPS; nR++, nRed += DITHER_PAL_DELTA ) + { + aDefPalette256[ i ] = BitmapColor( nRed, nGreen, nBlue ); + i++; + } + } + } + } + + // now fill in appropriate palette + switch( mnBits ) + { + case 1: return aDefPalette2; + case 4: return aDefPalette16; + case 8: return aDefPalette256; + default: break; + } + + const static BitmapPalette aEmptyPalette; + return aEmptyPalette; +} + +BitmapBuffer* AquaSalBitmap::AcquireBuffer( bool bReadOnly ) +{ + if( !maUserBuffer.get() ) +// || maContextBuffer.get() && (maUserBuffer.get() != maContextBuffer.get()) ) + { + fprintf(stderr,"ASB::Acq(%dx%d,d=%d)\n",mnWidth,mnHeight,mnBits); + // TODO: AllocateUserData(); + return NULL; + } + + BitmapBuffer* pBuffer = new BitmapBuffer; + pBuffer->mnWidth = mnWidth; + pBuffer->mnHeight = mnHeight; + pBuffer->maPalette = maPalette; + pBuffer->mnScanlineSize = mnBytesPerRow; + pBuffer->mpBits = maUserBuffer.get(); + pBuffer->mnBitCount = mnBits; + switch( mnBits ) + { + case 1: pBuffer->mnFormat = BMP_FORMAT_1BIT_MSB_PAL; break; + case 4: pBuffer->mnFormat = BMP_FORMAT_4BIT_MSN_PAL; break; + case 8: pBuffer->mnFormat = BMP_FORMAT_8BIT_PAL; break; + case 16: pBuffer->mnFormat = BMP_FORMAT_16BIT_TC_MSB_MASK; + pBuffer->maColorMask = ColorMask( k16BitRedColorMask, k16BitGreenColorMask, k16BitBlueColorMask ); + break; + case 24: pBuffer->mnFormat = BMP_FORMAT_24BIT_TC_BGR; break; + case 32: pBuffer->mnFormat = BMP_FORMAT_32BIT_TC_ARGB; + pBuffer->maColorMask = ColorMask( k32BitRedColorMask, k32BitGreenColorMask, k32BitBlueColorMask ); + break; + } + pBuffer->mnFormat |= BMP_FORMAT_BOTTOM_UP; + + // some BitmapBuffer users depend on a complete palette + if( (mnBits <= 8) && !maPalette ) + pBuffer->maPalette = GetDefaultPalette( mnBits, true ); + + return pBuffer; +} + +// ------------------------------------------------------------------ + +void AquaSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, bool bReadOnly ) +{ + // invalidate graphic context if we have different data + if( !bReadOnly ) + { + maPalette = pBuffer->maPalette; + if( mxGraphicContext ) + DestroyContext(); + } + + delete pBuffer; +} + +// ------------------------------------------------------------------ + +CGImageRef AquaSalBitmap::CreateCroppedImage( int nX, int nY, int nNewWidth, int nNewHeight ) const +{ + if( !mxCachedImage ) + { + if( !mxGraphicContext ) + if( !const_cast<AquaSalBitmap*>(this)->CreateContext() ) + return NULL; + + mxCachedImage = CGBitmapContextCreateImage( mxGraphicContext ); + } + + CGImageRef xCroppedImage = NULL; + // short circuit if there is nothing to crop + if( !nX && !nY && (mnWidth == nNewWidth) && (mnHeight == nNewHeight) ) + { + xCroppedImage = mxCachedImage; + CFRetain( xCroppedImage ); + } + else + { + nY = mnHeight - (nY + nNewHeight); // adjust for y-mirrored context + const CGRect aCropRect = {{nX, nY}, {nNewWidth, nNewHeight}}; + xCroppedImage = CGImageCreateWithImageInRect( mxCachedImage, aCropRect ); + } + + return xCroppedImage; +} + +// ------------------------------------------------------------------ + +static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/) +{ + rtl_freeMemory( const_cast<void*>(data) ); +} + +CGImageRef AquaSalBitmap::CreateWithMask( const AquaSalBitmap& rMask, + int nX, int nY, int nWidth, int nHeight ) const +{ + CGImageRef xImage( CreateCroppedImage( nX, nY, nWidth, nHeight ) ); + if( !xImage ) + return NULL; + + CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight ); + if( !xMask ) + return xImage; + + // CGImageCreateWithMask() only likes masks or greyscale images => convert if needed + // TODO: isolate in an extra method? + if( !CGImageIsMask(xMask) || (CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) ) + { + const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect has no offset + + // create the alpha mask image fitting our image + // TODO: is caching the full mask or the subimage mask worth it? + int nMaskBytesPerRow = ((nWidth + 3) & ~3); + void* pMaskMem = rtl_allocateMemory( nMaskBytesPerRow * nHeight ); + CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem, + nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, kCGImageAlphaNone ); + CGContextDrawImage( xMaskContext, xImageRect, xMask ); + CFRelease( xMask ); + CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( NULL, + pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) ); + static const float* pDecode = NULL; + xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, xDataProvider, pDecode, false ); + CFRelease( xDataProvider ); + CFRelease( xMaskContext ); + } + + if( !xMask ) + return xImage; + + // combine image and alpha mask + CGImageRef xMaskedImage = CGImageCreateWithMask( xImage, xMask ); + CFRelease( xMask ); + CFRelease( xImage ); + return xMaskedImage; +} + +// ------------------------------------------------------------------ + +/** creates an image from the given rectangle, replacing all black pixels with nMaskColor and make all other full transparent */ +CGImageRef AquaSalBitmap::CreateColorMask( int nX, int nY, int nWidth, int nHeight, SalColor nMaskColor ) const +{ + CGImageRef xMask = 0; + if( maUserBuffer.get() && (nX + nWidth <= mnWidth) && (nY + nHeight <= mnHeight) ) + { + const sal_uInt32 nDestBytesPerRow = nWidth << 2; + sal_uInt32* pMaskBuffer = static_cast<sal_uInt32*>( rtl_allocateMemory( nHeight * nDestBytesPerRow ) ); + sal_uInt32* pDest = pMaskBuffer; + + ImplPixelFormat* pSourcePixels = ImplPixelFormat::GetFormat( mnBits, maPalette ); + + if( pMaskBuffer && pSourcePixels ) + { + sal_uInt32 nColor; + reinterpret_cast<sal_uInt8*>(&nColor)[0] = 0xff; + reinterpret_cast<sal_uInt8*>(&nColor)[1] = SALCOLOR_RED( nMaskColor ); + reinterpret_cast<sal_uInt8*>(&nColor)[2] = SALCOLOR_GREEN( nMaskColor ); + reinterpret_cast<sal_uInt8*>(&nColor)[3] = SALCOLOR_BLUE( nMaskColor ); + + sal_uInt8* pSource = maUserBuffer.get(); + if( nY ) + pSource += nY * mnBytesPerRow; + + int y = nHeight; + while( y-- ) + { + pSourcePixels->StartLine( pSource ); + pSourcePixels->SkipPixel(nX); + sal_uInt32 x = nWidth; + while( x-- ) + { + *pDest++ = ( pSourcePixels->ReadPixel() == 0 ) ? nColor : 0; + } + pSource += mnBytesPerRow; + } + + CGDataProviderRef xDataProvider( CGDataProviderCreateWithData(NULL, pMaskBuffer, nHeight * nDestBytesPerRow, &CFRTLFree) ); + xMask = CGImageCreate(nWidth, nHeight, 8, 32, nDestBytesPerRow, GetSalData()->mxRGBSpace, kCGImageAlphaPremultipliedFirst, xDataProvider, NULL, true, kCGRenderingIntentDefault); + CFRelease(xDataProvider); + } + else + { + free(pMaskBuffer); + } + + delete pSourcePixels; + } + return xMask; +} + +// ======================================================================= + +/** AquaSalBitmap::GetSystemData Get platform native image data from existing image + * + * @param rData struct BitmapSystemData, defined in vcl/inc/bitmap.hxx + * @return true if successful +**/ +bool AquaSalBitmap::GetSystemData( BitmapSystemData& rData ) +{ + bool bRet = false; + + if( !mxGraphicContext ) + CreateContext(); + + if ( mxGraphicContext ) + { + bRet = true; + +#ifdef CAIRO + if ((CGBitmapContextGetBitsPerPixel(mxGraphicContext) == 32) && + (CGBitmapContextGetBitmapInfo(mxGraphicContext) & kCGBitmapByteOrderMask) != kCGBitmapByteOrder32Host) { + /** + * We need to hack things because VCL does not use kCGBitmapByteOrder32Host, while Cairo requires it. + */ + OSL_TRACE("AquaSalBitmap::%s(): kCGBitmapByteOrder32Host not found => inserting it.",__func__); + + CGImageRef xImage = CGBitmapContextCreateImage (mxGraphicContext); + + // re-create the context with single change: include kCGBitmapByteOrder32Host flag. + CGContextRef mxGraphicContextNew = CGBitmapContextCreate( CGBitmapContextGetData(mxGraphicContext), + CGBitmapContextGetWidth(mxGraphicContext), + CGBitmapContextGetHeight(mxGraphicContext), + CGBitmapContextGetBitsPerComponent(mxGraphicContext), + CGBitmapContextGetBytesPerRow(mxGraphicContext), + CGBitmapContextGetColorSpace(mxGraphicContext), + CGBitmapContextGetBitmapInfo(mxGraphicContext) | kCGBitmapByteOrder32Host); + CFRelease(mxGraphicContext); + + // Needs to be flipped + CGContextSaveGState( mxGraphicContextNew ); + CGContextTranslateCTM (mxGraphicContextNew, 0, CGBitmapContextGetHeight(mxGraphicContextNew)); + CGContextScaleCTM (mxGraphicContextNew, 1.0, -1.0); + + CGContextDrawImage(mxGraphicContextNew, CGRectMake( 0, 0, CGImageGetWidth(xImage), CGImageGetHeight(xImage)), xImage); + + // Flip back + CGContextRestoreGState( mxGraphicContextNew ); + + CGImageRelease( xImage ); + mxGraphicContext = mxGraphicContextNew; + } +#endif + + rData.rImageContext = (void *) mxGraphicContext; + rData.mnWidth = mnWidth; + rData.mnHeight = mnHeight; + } + + return bRet; +} diff --git a/vcl/aqua/source/gdi/salcolorutils.cxx b/vcl/aqua/source/gdi/salcolorutils.cxx new file mode 100755 index 000000000000..ec33b2dd8f8d --- /dev/null +++ b/vcl/aqua/source/gdi/salcolorutils.cxx @@ -0,0 +1,50 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salcolorutils.hxx" +#include "vcl/salbtype.hxx" + +// ======================================================================= + +SalColor GetSalColor( const float* pQuartzColor ) +{ + return MAKE_SALCOLOR( sal_uInt8( pQuartzColor[0] * 255.0), sal_uInt8( pQuartzColor[1] * 255.0 ), sal_uInt8( pQuartzColor[2] * 255.0 ) ); +} + +void SetSalColor( const SalColor& rColor, float* pQuartzColor ) +{ + pQuartzColor[0] = (float) SALCOLOR_RED(rColor) / 255.0; + pQuartzColor[1] = (float) SALCOLOR_GREEN(rColor) / 255.0; + pQuartzColor[2] = (float) SALCOLOR_BLUE(rColor) / 255.0; + pQuartzColor[3] = 1.0; +} + +// ======================================================================= + diff --git a/vcl/aqua/source/gdi/salgdi.cxx b/vcl/aqua/source/gdi/salgdi.cxx new file mode 100644 index 000000000000..8a4744d1efcd --- /dev/null +++ b/vcl/aqua/source/gdi/salgdi.cxx @@ -0,0 +1,2672 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salconst.h" +#include "salgdi.h" +#include "salbmp.h" +#include "salframe.h" +#include "salcolorutils.hxx" +#include "sft.hxx" +#include "salatsuifontutils.hxx" + +#include "vcl/impfont.hxx" +#include "vcl/fontsubset.hxx" +#include "vcl/sysdata.hxx" +#include "vcl/sallayout.hxx" +#include "vcl/svapp.hxx" + +#include "osl/file.hxx" +#include "osl/process.h" + +#include "vos/mutex.hxx" + +#include "rtl/bootstrap.h" +#include "rtl/strbuf.hxx" + +#include "basegfx/range/b2drectangle.hxx" +#include "basegfx/polygon/b2dpolygon.hxx" +#include "basegfx/polygon/b2dpolygontools.hxx" +#include "basegfx/matrix/b2dhommatrix.hxx" +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +using namespace vcl; + +typedef unsigned char Boolean; // copied from MacTypes.h, should be properly included +typedef std::vector<unsigned char> ByteVector; + + +// ======================================================================= + +ImplMacFontData::ImplMacFontData( const ImplDevFontAttributes& rDFA, ATSUFontID nFontId ) +: ImplFontData( rDFA, 0 ) +, mnFontId( nFontId ) +, mpCharMap( NULL ) +, mbOs2Read( false ) +, mbHasOs2Table( false ) +, mbCmapEncodingRead( false ) +, mbHasCJKSupport( false ) +{} + +// ----------------------------------------------------------------------- + +ImplMacFontData::~ImplMacFontData() +{ + if( mpCharMap ) + mpCharMap->DeReference(); +} + +// ----------------------------------------------------------------------- + +sal_IntPtr ImplMacFontData::GetFontId() const +{ + return (sal_IntPtr)mnFontId; +} + +// ----------------------------------------------------------------------- + +ImplFontData* ImplMacFontData::Clone() const +{ + ImplMacFontData* pClone = new ImplMacFontData(*this); + if( mpCharMap ) + mpCharMap->AddReference(); + return pClone; +} + +// ----------------------------------------------------------------------- + +ImplFontEntry* ImplMacFontData::CreateFontInstance(ImplFontSelectData& rFSD) const +{ + return new ImplFontEntry(rFSD); +} + +// ----------------------------------------------------------------------- + +inline FourCharCode GetTag(const char aTagName[5]) +{ + return (aTagName[0]<<24)+(aTagName[1]<<16)+(aTagName[2]<<8)+(aTagName[3]); +} + +static unsigned GetUShort( const unsigned char* p ){return((p[0]<<8)+p[1]);} +static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);} + +ImplFontCharMap* ImplMacFontData::GetImplFontCharMap() const +{ + if( mpCharMap ) + { + // return the cached charmap + mpCharMap->AddReference(); + return mpCharMap; + } + + // set the default charmap + mpCharMap = ImplFontCharMap::GetDefaultMap(); + + // get the CMAP byte size + ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId ); + ByteCount nBufSize = 0; + OSStatus eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nBufSize ); + DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::GetImplFontCharMap : ATSFontGetTable1 failed!\n"); + if( eStatus != noErr ) + return mpCharMap; + + // allocate a buffer for the CMAP raw data + ByteVector aBuffer( nBufSize ); + + // get the CMAP raw data + ByteCount nRawLength = 0; + eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength ); + DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::GetImplFontCharMap : ATSFontGetTable2 failed!\n"); + if( eStatus != noErr ) + return mpCharMap; + DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::GetImplFontCharMap : ByteCount mismatch!\n"); + + // parse the CMAP + CmapResult aCmapResult; + if( !ParseCMAP( &aBuffer[0], nRawLength, aCmapResult ) ) + return mpCharMap; + + mpCharMap = new ImplFontCharMap( aCmapResult ); + return mpCharMap; +} + +// ----------------------------------------------------------------------- + +void ImplMacFontData::ReadOs2Table( void ) const +{ + // read this only once per font + if( mbOs2Read ) + return; + mbOs2Read = true; + + // prepare to get the OS/2 table raw data + ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId ); + ByteCount nBufSize = 0; + OSStatus eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, 0, NULL, &nBufSize ); + DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadOs2Table : ATSFontGetTable1 failed!\n"); + if( eStatus != noErr ) + return; + + // allocate a buffer for the OS/2 raw data + ByteVector aBuffer( nBufSize ); + + // get the OS/2 raw data + ByteCount nRawLength = 0; + eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength ); + DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadOs2Table : ATSFontGetTable2 failed!\n"); + if( eStatus != noErr ) + return; + DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadOs2Table : ByteCount mismatch!\n"); + mbHasOs2Table = true; + + // parse the OS/2 raw data + // TODO: also analyze panose info, etc. + + // check if the fonts needs the "CJK extra leading" heuristic + const unsigned char* pOS2map = &aBuffer[0]; + const sal_uInt32 nVersion = GetUShort( pOS2map ); + if( nVersion >= 0x0001 ) + { + sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 ); + if( ulUnicodeRange2 & 0x2DF00000 ) + mbHasCJKSupport = true; + } +} + +void ImplMacFontData::ReadMacCmapEncoding( void ) const +{ + // read this only once per font + if( mbCmapEncodingRead ) + return; + mbCmapEncodingRead = true; + + ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId ); + ByteCount nBufSize = 0; + OSStatus eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nBufSize ); + DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadMacCmapEncoding : ATSFontGetTable1 failed!\n"); + if( eStatus != noErr ) + return; + + ByteVector aBuffer( nBufSize ); + + ByteCount nRawLength = 0; + eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength ); + DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadMacCmapEncoding : ATSFontGetTable2 failed!\n"); + if( eStatus != noErr ) + return; + DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadMacCmapEncoding : ByteCount mismatch!\n"); + + const unsigned char* pCmap = &aBuffer[0]; + + if (nRawLength < 24 ) + return; + if( GetUShort( pCmap ) != 0x0000 ) + return; + + // check if the fonts needs the "CJK extra leading" heuristic + int nSubTables = GetUShort( pCmap + 2 ); + + for( const unsigned char* p = pCmap + 4; --nSubTables >= 0; p += 8 ) + { + int nPlatform = GetUShort( p ); + if( nPlatform == kFontMacintoshPlatform ) { + int nEncoding = GetUShort (p + 2 ); + if( nEncoding == kFontJapaneseScript || + nEncoding == kFontTraditionalChineseScript || + nEncoding == kFontKoreanScript || + nEncoding == kFontSimpleChineseScript ) + { + mbHasCJKSupport = true; + break; + } + } + } +} + +// ----------------------------------------------------------------------- + +bool ImplMacFontData::HasCJKSupport( void ) const +{ + ReadOs2Table(); + if( !mbHasOs2Table ) + ReadMacCmapEncoding(); + + return mbHasCJKSupport; +} + +// ======================================================================= + +AquaSalGraphics::AquaSalGraphics() + : mpFrame( NULL ) + , mxLayer( NULL ) + , mrContext( NULL ) + , mpXorEmulation( NULL ) + , mnXorMode( 0 ) + , mnWidth( 0 ) + , mnHeight( 0 ) + , mnBitmapDepth( 0 ) + , mnRealDPIX( 0 ) + , mnRealDPIY( 0 ) + , mfFakeDPIScale( 1.0 ) + , mxClipPath( NULL ) + , maLineColor( COL_WHITE ) + , maFillColor( COL_BLACK ) + , mpMacFontData( NULL ) + , mnATSUIRotation( 0 ) + , mfFontScale( 1.0 ) + , mfFontStretch( 1.0 ) + , mbNonAntialiasedText( false ) + , mbPrinter( false ) + , mbVirDev( false ) + , mbWindow( false ) +{ + // create the style object for font attributes + ATSUCreateStyle( &maATSUStyle ); +} + +// ----------------------------------------------------------------------- + +AquaSalGraphics::~AquaSalGraphics() +{ +/* + if( mnUpdateGraphicsEvent ) + { + Application::RemoveUserEvent( mnUpdateGraphicsEvent ); + } +*/ + CGPathRelease( mxClipPath ); + ATSUDisposeStyle( maATSUStyle ); + + if( mpXorEmulation ) + delete mpXorEmulation; + + if( mxLayer ) + CGLayerRelease( mxLayer ); + else if( mrContext && mbWindow ) + { + // destroy backbuffer bitmap context that we created ourself + CGContextRelease( mrContext ); + mrContext = NULL; + // memory is freed automatically by maOwnContextMemory + } +} + +bool AquaSalGraphics::supportsOperation( OutDevSupportType eType ) const +{ + bool bRet = false; + switch( eType ) + { + case OutDevSupport_TransparentRect: + case OutDevSupport_B2DClip: + case OutDevSupport_B2DDraw: + bRet = true; + break; + default: break; + } + return bRet; +} + +// ======================================================================= + +void AquaSalGraphics::updateResolution() +{ + DBG_ASSERT( mbWindow, "updateResolution on inappropriate graphics" ); + + initResolution( (mbWindow && mpFrame) ? mpFrame->mpWindow : nil ); +} + +void AquaSalGraphics::initResolution( NSWindow* pWin ) +{ + // #i100617# read DPI only once; there is some kind of weird caching going on + // if the main screen changes + // FIXME: this is really unfortunate and needs to be investigated + + SalData* pSalData = GetSalData(); + if( pSalData->mnDPIX == 0 || pSalData->mnDPIY == 0 ) + { + NSScreen* pScreen = nil; + + /* #i91301# + many woes went into the try to have different resolutions + on different screens. The result of these trials is that OOo is not ready + for that yet, vcl and applications would need to be adapted. + + Unfortunately this is not possible in the 3.0 timeframe. + So let's stay with one resolution for all Windows and VirtualDevices + which is the resolution of the main screen + + This of course also means that measurements are exact only on the main screen. + For activating different resolutions again just comment out the two lines below. + + if( pWin ) + pScreen = [pWin screen]; + */ + if( pScreen == nil ) + { + NSArray* pScreens = [NSScreen screens]; + if( pScreens ) + pScreen = [pScreens objectAtIndex: 0]; + } + + mnRealDPIX = mnRealDPIY = 96; + if( pScreen ) + { + NSDictionary* pDev = [pScreen deviceDescription]; + if( pDev ) + { + NSNumber* pVal = [pDev objectForKey: @"NSScreenNumber"]; + if( pVal ) + { + // FIXME: casting a long to CGDirectDisplayID is evil, but + // Apple suggest to do it this way + const CGDirectDisplayID nDisplayID = (CGDirectDisplayID)[pVal longValue]; + const CGSize aSize = CGDisplayScreenSize( nDisplayID ); // => result is in millimeters + mnRealDPIX = static_cast<long>((CGDisplayPixelsWide( nDisplayID ) * 25.4) / aSize.width); + mnRealDPIY = static_cast<long>((CGDisplayPixelsHigh( nDisplayID ) * 25.4) / aSize.height); + } + else + { + DBG_ERROR( "no resolution found in device description" ); + } + } + else + { + DBG_ERROR( "no device description" ); + } + } + else + { + DBG_ERROR( "no screen found" ); + } + + // #i107076# maintaining size-WYSIWYG-ness causes many problems for + // low-DPI, high-DPI or for mis-reporting devices + // => it is better to limit the calculation result then + static const int nMinDPI = 72; + if( (mnRealDPIX < nMinDPI) || (mnRealDPIY < nMinDPI) ) + mnRealDPIX = mnRealDPIY = nMinDPI; + static const int nMaxDPI = 200; + if( (mnRealDPIX > nMaxDPI) || (mnRealDPIY > nMaxDPI) ) + mnRealDPIX = mnRealDPIY = nMaxDPI; + + // for OSX any anisotropy reported for the display resolution is best ignored (e.g. TripleHead2Go) + mnRealDPIX = mnRealDPIY = (mnRealDPIX + mnRealDPIY + 1) / 2; + + pSalData->mnDPIX = mnRealDPIX; + pSalData->mnDPIY = mnRealDPIY; + } + else + { + mnRealDPIX = pSalData->mnDPIX; + mnRealDPIY = pSalData->mnDPIY; + } + + mfFakeDPIScale = 1.0; +} + +void AquaSalGraphics::GetResolution( long& rDPIX, long& rDPIY ) +{ + if( !mnRealDPIY ) + initResolution( (mbWindow && mpFrame) ? mpFrame->mpWindow : nil ); + + rDPIX = static_cast<long>(mfFakeDPIScale * mnRealDPIX); + rDPIY = static_cast<long>(mfFakeDPIScale * mnRealDPIY); +} + +void AquaSalGraphics::copyResolution( AquaSalGraphics& rGraphics ) +{ + if( !rGraphics.mnRealDPIY && rGraphics.mbWindow && rGraphics.mpFrame ) + rGraphics.initResolution( rGraphics.mpFrame->mpWindow ); + + mnRealDPIX = rGraphics.mnRealDPIX; + mnRealDPIY = rGraphics.mnRealDPIY; + mfFakeDPIScale = rGraphics.mfFakeDPIScale; +} + +// ----------------------------------------------------------------------- + +USHORT AquaSalGraphics::GetBitCount() +{ + USHORT nBits = mnBitmapDepth ? mnBitmapDepth : 32;//24; + return nBits; +} + +// ----------------------------------------------------------------------- + +static const basegfx::B2DPoint aHalfPointOfs ( 0.5, 0.5 ); + +static void AddPolygonToPath( CGMutablePathRef xPath, + const ::basegfx::B2DPolygon& rPolygon, bool bClosePath, bool bPixelSnap, bool bLineDraw ) +{ + // short circuit if there is nothing to do + const int nPointCount = rPolygon.count(); + if( nPointCount <= 0 ) + return; + + (void)bPixelSnap; // TODO + const CGAffineTransform* pTransform = NULL; + + const bool bHasCurves = rPolygon.areControlPointsUsed(); + for( int nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++ ) + { + int nClosedIdx = nPointIdx; + if( nPointIdx >= nPointCount ) + { + // prepare to close last curve segment if needed + if( bClosePath && (nPointIdx == nPointCount) ) + nClosedIdx = 0; + else + break; + } + + ::basegfx::B2DPoint aPoint = rPolygon.getB2DPoint( nClosedIdx ); + + if( bPixelSnap) + { + // snap device coordinates to full pixels + aPoint.setX( basegfx::fround( aPoint.getX() ) ); + aPoint.setY( basegfx::fround( aPoint.getY() ) ); + } + + if( bLineDraw ) + aPoint += aHalfPointOfs; + + if( !nPointIdx ) { // first point => just move there + CGPathMoveToPoint( xPath, pTransform, aPoint.getX(), aPoint.getY() ); + continue; + } + + bool bPendingCurve = false; + if( bHasCurves ) + { + bPendingCurve = rPolygon.isNextControlPointUsed( nPrevIdx ); + bPendingCurve |= rPolygon.isPrevControlPointUsed( nClosedIdx ); + } + + if( !bPendingCurve ) // line segment + CGPathAddLineToPoint( xPath, pTransform, aPoint.getX(), aPoint.getY() ); + else // cubic bezier segment + { + basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint( nPrevIdx ); + basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint( nClosedIdx ); + if( bLineDraw ) + { + aCP1 += aHalfPointOfs; + aCP2 += aHalfPointOfs; + } + CGPathAddCurveToPoint( xPath, pTransform, aCP1.getX(), aCP1.getY(), + aCP2.getX(), aCP2.getY(), aPoint.getX(), aPoint.getY() ); + } + } + + if( bClosePath ) + CGPathCloseSubpath( xPath ); +} + +static void AddPolyPolygonToPath( CGMutablePathRef xPath, + const ::basegfx::B2DPolyPolygon& rPolyPoly, bool bPixelSnap, bool bLineDraw ) +{ + // short circuit if there is nothing to do + const int nPolyCount = rPolyPoly.count(); + if( nPolyCount <= 0 ) + return; + + for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) + { + const ::basegfx::B2DPolygon rPolygon = rPolyPoly.getB2DPolygon( nPolyIdx ); + AddPolygonToPath( xPath, rPolygon, true, bPixelSnap, bLineDraw ); + } +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::ResetClipRegion() +{ + // release old path and indicate no clipping + if( mxClipPath ) + { + CGPathRelease( mxClipPath ); + mxClipPath = NULL; + } + if( CheckContext() ) + SetState(); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::BeginSetClipRegion( ULONG nRectCount ) +{ + // release old clip path + if( mxClipPath ) + { + CGPathRelease( mxClipPath ); + mxClipPath = NULL; + } +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalGraphics::unionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + if( (nWidth <= 0) || (nHeight <= 0) ) + return TRUE; + + if( !mxClipPath ) + mxClipPath = CGPathCreateMutable(); + const CGRect aClipRect = {{nX,nY},{nWidth,nHeight}}; + CGPathAddRect( mxClipPath, NULL, aClipRect ); + return TRUE; +} + +// ----------------------------------------------------------------------- + +bool AquaSalGraphics::unionClipRegion( const ::basegfx::B2DPolyPolygon& rPolyPolygon ) +{ + if( rPolyPolygon.count() <= 0 ) + return true; + + if( !mxClipPath ) + mxClipPath = CGPathCreateMutable(); + AddPolyPolygonToPath( mxClipPath, rPolyPolygon, !getAntiAliasB2DDraw(), false ); + return true; +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::EndSetClipRegion() +{ + if( CheckContext() ) + SetState(); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::SetLineColor() +{ + maLineColor.SetAlpha( 0.0 ); // transparent + if( CheckContext() ) + CGContextSetStrokeColor( mrContext, maLineColor.AsArray() ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::SetLineColor( SalColor nSalColor ) +{ + maLineColor = RGBAColor( nSalColor ); + if( CheckContext() ) + CGContextSetStrokeColor( mrContext, maLineColor.AsArray() ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::SetFillColor() +{ + maFillColor.SetAlpha( 0.0 ); // transparent + if( CheckContext() ) + CGContextSetFillColor( mrContext, maFillColor.AsArray() ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::SetFillColor( SalColor nSalColor ) +{ + maFillColor = RGBAColor( nSalColor ); + if( CheckContext() ) + CGContextSetFillColor( mrContext, maFillColor.AsArray() ); +} + +// ----------------------------------------------------------------------- + +static SalColor ImplGetROPSalColor( SalROPColor nROPColor ) +{ + SalColor nSalColor; + if ( nROPColor == SAL_ROP_0 ) + nSalColor = MAKE_SALCOLOR( 0, 0, 0 ); + else + nSalColor = MAKE_SALCOLOR( 255, 255, 255 ); + return nSalColor; +} + +void AquaSalGraphics::SetROPLineColor( SalROPColor nROPColor ) +{ + if( ! mbPrinter ) + SetLineColor( ImplGetROPSalColor( nROPColor ) ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::SetROPFillColor( SalROPColor nROPColor ) +{ + if( ! mbPrinter ) + SetFillColor( ImplGetROPSalColor( nROPColor ) ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::ImplDrawPixel( long nX, long nY, const RGBAColor& rColor ) +{ + if( !CheckContext() ) + return; + + // overwrite the fill color + CGContextSetFillColor( mrContext, rColor.AsArray() ); + // draw 1x1 rect, there is no pixel drawing in Quartz + CGRect aDstRect = {{nX,nY,},{1,1}}; + CGContextFillRect( mrContext, aDstRect ); + RefreshRect( aDstRect ); + // reset the fill color + CGContextSetFillColor( mrContext, maFillColor.AsArray() ); +} + +void AquaSalGraphics::drawPixel( long nX, long nY ) +{ + // draw pixel with current line color + ImplDrawPixel( nX, nY, maLineColor ); +} + +void AquaSalGraphics::drawPixel( long nX, long nY, SalColor nSalColor ) +{ + const RGBAColor aPixelColor( nSalColor ); + ImplDrawPixel( nX, nY, aPixelColor ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 ) +{ + if( nX1 == nX2 && nY1 == nY2 ) + { + // #i109453# platform independent code expects at least one pixel to be drawn + drawPixel( nX1, nY1 ); + return; + } + + if( !CheckContext() ) + return; + + CGContextBeginPath( mrContext ); + CGContextMoveToPoint( mrContext, static_cast<float>(nX1)+0.5, static_cast<float>(nY1)+0.5 ); + CGContextAddLineToPoint( mrContext, static_cast<float>(nX2)+0.5, static_cast<float>(nY2)+0.5 ); + CGContextDrawPath( mrContext, kCGPathStroke ); + + Rectangle aRefreshRect( nX1, nY1, nX2, nY2 ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::drawRect( long nX, long nY, long nWidth, long nHeight ) +{ + if( !CheckContext() ) + return; + + CGRect aRect( CGRectMake(nX, nY, nWidth, nHeight) ); + if( IsPenVisible() ) + { + aRect.origin.x += 0.5; + aRect.origin.y += 0.5; + aRect.size.width -= 1; + aRect.size.height -= 1; + } + + if( IsBrushVisible() ) + CGContextFillRect( mrContext, aRect ); + + if( IsPenVisible() ) + CGContextStrokeRect( mrContext, aRect ); + + RefreshRect( nX, nY, nWidth, nHeight ); +} + +// ----------------------------------------------------------------------- + +static void getBoundRect( ULONG nPoints, const SalPoint *pPtAry, long &rX, long& rY, long& rWidth, long& rHeight ) +{ + long nX1 = pPtAry->mnX; + long nX2 = nX1; + long nY1 = pPtAry->mnY; + long nY2 = nY1; + for( ULONG n = 1; n < nPoints; n++ ) + { + if( pPtAry[n].mnX < nX1 ) + nX1 = pPtAry[n].mnX; + else if( pPtAry[n].mnX > nX2 ) + nX2 = pPtAry[n].mnX; + + if( pPtAry[n].mnY < nY1 ) + nY1 = pPtAry[n].mnY; + else if( pPtAry[n].mnY > nY2 ) + nY2 = pPtAry[n].mnY; + } + rX = nX1; + rY = nY1; + rWidth = nX2 - nX1 + 1; + rHeight = nY2 - nY1 + 1; +} + +static inline void alignLinePoint( const SalPoint* i_pIn, float& o_fX, float& o_fY ) +{ + o_fX = static_cast<float>(i_pIn->mnX ) + 0.5; + o_fY = static_cast<float>(i_pIn->mnY ) + 0.5; +} + +void AquaSalGraphics::drawPolyLine( ULONG nPoints, const SalPoint *pPtAry ) +{ + if( nPoints < 1 ) + return; + if( !CheckContext() ) + return; + + long nX = 0, nY = 0, nWidth = 0, nHeight = 0; + getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight ); + + float fX, fY; + + CGContextBeginPath( mrContext ); + alignLinePoint( pPtAry, fX, fY ); + CGContextMoveToPoint( mrContext, fX, fY ); + pPtAry++; + for( ULONG nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) + { + alignLinePoint( pPtAry, fX, fY ); + CGContextAddLineToPoint( mrContext, fX, fY ); + } + CGContextDrawPath( mrContext, kCGPathStroke ); + + RefreshRect( nX, nY, nWidth, nHeight ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::drawPolygon( ULONG nPoints, const SalPoint *pPtAry ) +{ + if( nPoints <= 1 ) + return; + if( !CheckContext() ) + return; + + long nX = 0, nY = 0, nWidth = 0, nHeight = 0; + getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight ); + + CGPathDrawingMode eMode; + if( IsBrushVisible() && IsPenVisible() ) + eMode = kCGPathEOFillStroke; + else if( IsPenVisible() ) + eMode = kCGPathStroke; + else if( IsBrushVisible() ) + eMode = kCGPathEOFill; + else + return; + + CGContextBeginPath( mrContext ); + + if( IsPenVisible() ) + { + float fX, fY; + alignLinePoint( pPtAry, fX, fY ); + CGContextMoveToPoint( mrContext, fX, fY ); + pPtAry++; + for( ULONG nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) + { + alignLinePoint( pPtAry, fX, fY ); + CGContextAddLineToPoint( mrContext, fX, fY ); + } + } + else + { + CGContextMoveToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); + pPtAry++; + for( ULONG nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) + CGContextAddLineToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); + } + + CGContextDrawPath( mrContext, eMode ); + RefreshRect( nX, nY, nWidth, nHeight ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::drawPolyPolygon( ULONG nPolyCount, const ULONG *pPoints, PCONSTSALPOINT *ppPtAry ) +{ + if( nPolyCount <= 0 ) + return; + if( !CheckContext() ) + return; + + // find bound rect + long leftX = 0, topY = 0, maxWidth = 0, maxHeight = 0; + getBoundRect( pPoints[0], ppPtAry[0], leftX, topY, maxWidth, maxHeight ); + for( ULONG n = 1; n < nPolyCount; n++ ) + { + long nX = leftX, nY = topY, nW = maxWidth, nH = maxHeight; + getBoundRect( pPoints[n], ppPtAry[n], nX, nY, nW, nH ); + if( nX < leftX ) + { + maxWidth += leftX - nX; + leftX = nX; + } + if( nY < topY ) + { + maxHeight += topY - nY; + topY = nY; + } + if( nX + nW > leftX + maxWidth ) + maxWidth = nX + nW - leftX; + if( nY + nH > topY + maxHeight ) + maxHeight = nY + nH - topY; + } + + // prepare drawing mode + CGPathDrawingMode eMode; + if( IsBrushVisible() && IsPenVisible() ) + eMode = kCGPathEOFillStroke; + else if( IsPenVisible() ) + eMode = kCGPathStroke; + else if( IsBrushVisible() ) + eMode = kCGPathEOFill; + else + return; + + // convert to CGPath + CGContextBeginPath( mrContext ); + if( IsPenVisible() ) + { + for( ULONG nPoly = 0; nPoly < nPolyCount; nPoly++ ) + { + const ULONG nPoints = pPoints[nPoly]; + if( nPoints > 1 ) + { + const SalPoint *pPtAry = ppPtAry[nPoly]; + float fX, fY; + alignLinePoint( pPtAry, fX, fY ); + CGContextMoveToPoint( mrContext, fX, fY ); + pPtAry++; + for( ULONG nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) + { + alignLinePoint( pPtAry, fX, fY ); + CGContextAddLineToPoint( mrContext, fX, fY ); + } + CGContextClosePath(mrContext); + } + } + } + else + { + for( ULONG nPoly = 0; nPoly < nPolyCount; nPoly++ ) + { + const ULONG nPoints = pPoints[nPoly]; + if( nPoints > 1 ) + { + const SalPoint *pPtAry = ppPtAry[nPoly]; + CGContextMoveToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); + pPtAry++; + for( ULONG nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) + CGContextAddLineToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); + CGContextClosePath(mrContext); + } + } + } + + CGContextDrawPath( mrContext, eMode ); + + RefreshRect( leftX, topY, maxWidth, maxHeight ); +} + +// ----------------------------------------------------------------------- + +bool AquaSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly, + double fTransparency ) +{ + // short circuit if there is nothing to do + const int nPolyCount = rPolyPoly.count(); + if( nPolyCount <= 0 ) + return true; + + // ignore invisible polygons + if( (fTransparency >= 1.0) || (fTransparency < 0) ) + return true; + + // setup poly-polygon path + CGMutablePathRef xPath = CGPathCreateMutable(); + for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) + { + const ::basegfx::B2DPolygon rPolygon = rPolyPoly.getB2DPolygon( nPolyIdx ); + AddPolygonToPath( xPath, rPolygon, true, !getAntiAliasB2DDraw(), IsPenVisible() ); + } + + const CGRect aRefreshRect = CGPathGetBoundingBox( xPath ); +#ifndef NO_I97317_WORKAROUND + // #i97317# workaround for Quartz having problems with drawing small polygons + if( ! ((aRefreshRect.size.width <= 0.125) && (aRefreshRect.size.height <= 0.125)) ) +#endif + { + // use the path to prepare the graphics context + CGContextSaveGState( mrContext ); + CGContextBeginPath( mrContext ); + CGContextAddPath( mrContext, xPath ); + + // draw path with antialiased polygon + CGContextSetShouldAntialias( mrContext, true ); + CGContextSetAlpha( mrContext, 1.0 - fTransparency ); + CGContextDrawPath( mrContext, kCGPathEOFillStroke ); + CGContextRestoreGState( mrContext ); + + // mark modified rectangle as updated + RefreshRect( aRefreshRect ); + } + + CGPathRelease( xPath ); + + return true; +} + +// ----------------------------------------------------------------------- + +bool AquaSalGraphics::drawPolyLine( const ::basegfx::B2DPolygon& rPolyLine, + double fTransparency, + const ::basegfx::B2DVector& rLineWidths, + basegfx::B2DLineJoin eLineJoin ) +{ + // short circuit if there is nothing to do + const int nPointCount = rPolyLine.count(); + if( nPointCount <= 0 ) + return true; + + // reject requests that cannot be handled yet + if( rLineWidths.getX() != rLineWidths.getY() ) + return false; + + // #i101491# Aqua does not support B2DLINEJOIN_NONE; return false to use + // the fallback (own geometry preparation) + // #i104886# linejoin-mode and thus the above only applies to "fat" lines + if( (basegfx::B2DLINEJOIN_NONE == eLineJoin) + && (rLineWidths.getX() > 1.3) ) + return false; + + // setup line attributes + CGLineJoin aCGLineJoin = kCGLineJoinMiter; + switch( eLineJoin ) { + case ::basegfx::B2DLINEJOIN_NONE: aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break; + case ::basegfx::B2DLINEJOIN_MIDDLE: aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break; + case ::basegfx::B2DLINEJOIN_BEVEL: aCGLineJoin = kCGLineJoinBevel; break; + case ::basegfx::B2DLINEJOIN_MITER: aCGLineJoin = kCGLineJoinMiter; break; + case ::basegfx::B2DLINEJOIN_ROUND: aCGLineJoin = kCGLineJoinRound; break; + } + + // setup poly-polygon path + CGMutablePathRef xPath = CGPathCreateMutable(); + AddPolygonToPath( xPath, rPolyLine, rPolyLine.isClosed(), !getAntiAliasB2DDraw(), true ); + + const CGRect aRefreshRect = CGPathGetBoundingBox( xPath ); +#ifndef NO_I97317_WORKAROUND + // #i97317# workaround for Quartz having problems with drawing small polygons + if( ! ((aRefreshRect.size.width <= 0.125) && (aRefreshRect.size.height <= 0.125)) ) +#endif + { + // use the path to prepare the graphics context + CGContextSaveGState( mrContext ); + CGContextAddPath( mrContext, xPath ); + // draw path with antialiased line + CGContextSetShouldAntialias( mrContext, true ); + CGContextSetAlpha( mrContext, 1.0 - fTransparency ); + CGContextSetLineJoin( mrContext, aCGLineJoin ); + CGContextSetLineWidth( mrContext, rLineWidths.getX() ); + CGContextDrawPath( mrContext, kCGPathStroke ); + CGContextRestoreGState( mrContext ); + + // mark modified rectangle as updated + RefreshRect( aRefreshRect ); + } + + CGPathRelease( xPath ); + + return true; +} + +// ----------------------------------------------------------------------- + +sal_Bool AquaSalGraphics::drawPolyLineBezier( ULONG nPoints, const SalPoint* pPtAry, const BYTE* pFlgAry ) +{ + return sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool AquaSalGraphics::drawPolygonBezier( ULONG nPoints, const SalPoint* pPtAry, const BYTE* pFlgAry ) +{ + return sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool AquaSalGraphics::drawPolyPolygonBezier( ULONG nPoly, const ULONG* pPoints, + const SalPoint* const* pPtAry, const BYTE* const* pFlgAry ) +{ + return sal_False; +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::copyBits( const SalTwoRect *pPosAry, SalGraphics *pSrcGraphics ) +{ + if( !pSrcGraphics ) + pSrcGraphics = this; + + //from unix salgdi2.cxx + //[FIXME] find a better way to prevent calc from crashing when width and height are negative + if( pPosAry->mnSrcWidth <= 0 + || pPosAry->mnSrcHeight <= 0 + || pPosAry->mnDestWidth <= 0 + || pPosAry->mnDestHeight <= 0 ) + { + return; + } + + // accelerate trivial operations + /*const*/ AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); + const bool bSameGraphics = (this == pSrc) || (mbWindow && mpFrame && pSrc->mbWindow && (mpFrame == pSrc->mpFrame)); + if( bSameGraphics + && (pPosAry->mnSrcWidth == pPosAry->mnDestWidth) + && (pPosAry->mnSrcHeight == pPosAry->mnDestHeight)) + { + // short circuit if there is nothing to do + if( (pPosAry->mnSrcX == pPosAry->mnDestX) + && (pPosAry->mnSrcY == pPosAry->mnDestY)) + return; + // use copyArea() if source and destination context are identical + copyArea( pPosAry->mnDestX, pPosAry->mnDestY, pPosAry->mnSrcX, pPosAry->mnSrcY, + pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, 0 ); + return; + } + + ApplyXorContext(); + pSrc->ApplyXorContext(); + + DBG_ASSERT( pSrc->mxLayer!=NULL, "AquaSalGraphics::copyBits() from non-layered graphics" ); + + const CGPoint aDstPoint = { +pPosAry->mnDestX - pPosAry->mnSrcX, pPosAry->mnDestY - pPosAry->mnSrcY }; + if( (pPosAry->mnSrcWidth == pPosAry->mnDestWidth && pPosAry->mnSrcHeight == pPosAry->mnDestHeight) && + (!mnBitmapDepth || (aDstPoint.x + pSrc->mnWidth) <= mnWidth) ) // workaround a Quartz crasher + { + // in XOR mode the drawing context is redirected to the XOR mask + // if source and target are identical then copyBits() paints onto the target context though + CGContextRef xCopyContext = mrContext; + if( mpXorEmulation && mpXorEmulation->IsEnabled() ) + if( pSrcGraphics == this ) + xCopyContext = mpXorEmulation->GetTargetContext(); + + CGContextSaveGState( xCopyContext ); + const CGRect aDstRect = { {pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight} }; + CGContextClipToRect( xCopyContext, aDstRect ); + + // draw at new destination + // NOTE: flipped drawing gets disabled for this, else the subimage would be drawn upside down + if( pSrc->IsFlipped() ) + { CGContextTranslateCTM( xCopyContext, 0, +mnHeight ); CGContextScaleCTM( xCopyContext, +1, -1 ); } + // TODO: pSrc->size() != this->size() + ::CGContextDrawLayerAtPoint( xCopyContext, aDstPoint, pSrc->mxLayer ); + CGContextRestoreGState( xCopyContext ); + // mark the destination rectangle as updated + RefreshRect( aDstRect ); + } + else + { + SalBitmap* pBitmap = pSrc->getBitmap( pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight ); + + if( pBitmap ) + { + SalTwoRect aPosAry( *pPosAry ); + aPosAry.mnSrcX = 0; + aPosAry.mnSrcY = 0; + drawBitmap( &aPosAry, *pBitmap ); + delete pBitmap; + } + } +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::copyArea( long nDstX, long nDstY,long nSrcX, long nSrcY, long nSrcWidth, long nSrcHeight, USHORT nFlags ) +{ + ApplyXorContext(); + +#if 0 // TODO: make AquaSalBitmap as fast as the alternative implementation below + SalBitmap* pBitmap = getBitmap( nSrcX, nSrcY, nSrcWidth, nSrcHeight ); + if( pBitmap ) + { + SalTwoRect aPosAry; + aPosAry.mnSrcX = 0; + aPosAry.mnSrcY = 0; + aPosAry.mnSrcWidth = nSrcWidth; + aPosAry.mnSrcHeight = nSrcHeight; + aPosAry.mnDestX = nDstX; + aPosAry.mnDestY = nDstY; + aPosAry.mnDestWidth = nSrcWidth; + aPosAry.mnDestHeight = nSrcHeight; + drawBitmap( &aPosAry, *pBitmap ); + delete pBitmap; + } +#else + DBG_ASSERT( mxLayer!=NULL, "AquaSalGraphics::copyArea() for non-layered graphics" ); + + // in XOR mode the drawing context is redirected to the XOR mask + // copyArea() always works on the target context though + CGContextRef xCopyContext = mrContext; + if( mpXorEmulation && mpXorEmulation->IsEnabled() ) + xCopyContext = mpXorEmulation->GetTargetContext(); + + // drawing a layer onto its own context causes trouble on OSX => copy it first + // TODO: is it possible to get rid of this unneeded copy more often? + // e.g. on OSX>=10.5 only this situation causes problems: + // mnBitmapDepth && (aDstPoint.x + pSrc->mnWidth) > mnWidth + CGLayerRef xSrcLayer = mxLayer; + // TODO: if( mnBitmapDepth > 0 ) + { + const CGSize aSrcSize = { nSrcWidth, nSrcHeight }; + xSrcLayer = ::CGLayerCreateWithContext( xCopyContext, aSrcSize, NULL ); + const CGContextRef xSrcContext = CGLayerGetContext( xSrcLayer ); + CGPoint aSrcPoint = { -nSrcX, -nSrcY }; + if( IsFlipped() ) + { + ::CGContextTranslateCTM( xSrcContext, 0, +nSrcHeight ); + ::CGContextScaleCTM( xSrcContext, +1, -1 ); + aSrcPoint.y = (nSrcY + nSrcHeight) - mnHeight; + } + ::CGContextDrawLayerAtPoint( xSrcContext, aSrcPoint, mxLayer ); + } + + // draw at new destination + const CGPoint aDstPoint = { +nDstX, +nDstY }; + ::CGContextDrawLayerAtPoint( xCopyContext, aDstPoint, xSrcLayer ); + + // cleanup + if( xSrcLayer != mxLayer ) + CGLayerRelease( xSrcLayer ); + + // mark the destination rectangle as updated + RefreshRect( nDstX, nDstY, nSrcWidth, nSrcHeight ); +#endif +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap ) +{ + if( !CheckContext() ) + return; + + const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap); + CGImageRef xImage = rBitmap.CreateCroppedImage( (int)pPosAry->mnSrcX, (int)pPosAry->mnSrcY, (int)pPosAry->mnSrcWidth, (int)pPosAry->mnSrcHeight ); + if( !xImage ) + return; + + const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}}; + CGContextDrawImage( mrContext, aDstRect, xImage ); + CGImageRelease( xImage ); + RefreshRect( aDstRect ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap,SalColor nTransparentColor ) +{ + DBG_ERROR("not implemented for color masking!"); + drawBitmap( pPosAry, rSalBitmap ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap, const SalBitmap& rTransparentBitmap ) +{ + if( !CheckContext() ) + return; + + const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap); + const AquaSalBitmap& rMask = static_cast<const AquaSalBitmap&>(rTransparentBitmap); + CGImageRef xMaskedImage( rBitmap.CreateWithMask( rMask, pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight ) ); + if( !xMaskedImage ) + return; + + const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}}; + CGContextDrawImage( mrContext, aDstRect, xMaskedImage ); + CGImageRelease( xMaskedImage ); + RefreshRect( aDstRect ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::drawMask( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap, SalColor nMaskColor ) +{ + if( !CheckContext() ) + return; + + const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap); + CGImageRef xImage = rBitmap.CreateColorMask( pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, nMaskColor ); + if( !xImage ) + return; + + const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}}; + CGContextDrawImage( mrContext, aDstRect, xImage ); + CGImageRelease( xImage ); + RefreshRect( aDstRect ); +} + +// ----------------------------------------------------------------------- + +SalBitmap* AquaSalGraphics::getBitmap( long nX, long nY, long nDX, long nDY ) +{ + DBG_ASSERT( mxLayer, "AquaSalGraphics::getBitmap() with no layer" ); + + ApplyXorContext(); + + AquaSalBitmap* pBitmap = new AquaSalBitmap; + if( !pBitmap->Create( mxLayer, mnBitmapDepth, nX, nY, nDX, nDY, !mbWindow ) ) + { + delete pBitmap; + pBitmap = NULL; + } + + return pBitmap; +} + +// ----------------------------------------------------------------------- + +SalColor AquaSalGraphics::getPixel( long nX, long nY ) +{ + // return default value on printers or when out of bounds + if( !mxLayer + || (nX < 0) || (nX >= mnWidth) + || (nY < 0) || (nY >= mnHeight)) + return COL_BLACK; + + // prepare creation of matching a CGBitmapContext + CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; + CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; +#if __BIG_ENDIAN__ + struct{ unsigned char b, g, r, a; } aPixel; +#else + struct{ unsigned char a, r, g, b; } aPixel; +#endif + + // create a one-pixel bitmap context + // TODO: is it worth to cache it? + CGContextRef xOnePixelContext = ::CGBitmapContextCreate( &aPixel, + 1, 1, 8, sizeof(aPixel), aCGColorSpace, aCGBmpInfo ); + + // update this graphics layer + ApplyXorContext(); + + // copy the requested pixel into the bitmap context + if( IsFlipped() ) + nY = mnHeight - nY; + const CGPoint aCGPoint = {-nX, -nY}; + CGContextDrawLayerAtPoint( xOnePixelContext, aCGPoint, mxLayer ); + CGContextRelease( xOnePixelContext ); + + SalColor nSalColor = MAKE_SALCOLOR( aPixel.r, aPixel.g, aPixel.b ); + return nSalColor; +} + +// ----------------------------------------------------------------------- + + +static void DrawPattern50( void* info, CGContextRef rContext ) +{ + static const CGRect aRects[2] = { { {0,0}, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } }; + CGContextAddRects( rContext, aRects, 2 ); + CGContextFillPath( rContext ); +} + +void AquaSalGraphics::Pattern50Fill() +{ + static const float aFillCol[4] = { 1,1,1,1 }; + static const CGPatternCallbacks aCallback = { 0, &DrawPattern50, NULL }; + if( ! GetSalData()->mxP50Space ) + GetSalData()->mxP50Space = CGColorSpaceCreatePattern( GetSalData()->mxRGBSpace ); + if( ! GetSalData()->mxP50Pattern ) + GetSalData()->mxP50Pattern = CGPatternCreate( NULL, CGRectMake( 0, 0, 4, 4 ), + CGAffineTransformIdentity, 4, 4, + kCGPatternTilingConstantSpacing, + false, &aCallback ); + + CGContextSetFillColorSpace( mrContext, GetSalData()->mxP50Space ); + CGContextSetFillPattern( mrContext, GetSalData()->mxP50Pattern, aFillCol ); + CGContextFillPath( mrContext ); +} + +void AquaSalGraphics::invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags ) +{ + if ( CheckContext() ) + { + CGRect aCGRect = CGRectMake( nX, nY, nWidth, nHeight); + CGContextSaveGState(mrContext); + + if ( nFlags & SAL_INVERT_TRACKFRAME ) + { + const float dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line + CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); + CGContextSetRGBStrokeColor ( mrContext, 1.0, 1.0, 1.0, 1.0 ); + CGContextSetLineDash ( mrContext, 0, dashLengths, 2 ); + CGContextSetLineWidth( mrContext, 2.0); + CGContextStrokeRect ( mrContext, aCGRect ); + } + else if ( nFlags & SAL_INVERT_50 ) + { + //CGContextSetAllowsAntialiasing( mrContext, false ); + CGContextSetBlendMode(mrContext, kCGBlendModeDifference); + CGContextAddRect( mrContext, aCGRect ); + Pattern50Fill(); + } + else // just invert + { + CGContextSetBlendMode(mrContext, kCGBlendModeDifference); + CGContextSetRGBFillColor ( mrContext,1.0, 1.0, 1.0 , 1.0 ); + CGContextFillRect ( mrContext, aCGRect ); + } + CGContextRestoreGState( mrContext); + RefreshRect( aCGRect ); + } +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::invert( ULONG nPoints, const SalPoint* pPtAry, SalInvert nSalFlags ) +{ + CGPoint* CGpoints ; + if ( CheckContext() ) + { + CGContextSaveGState(mrContext); + CGpoints = makeCGptArray(nPoints,pPtAry); + CGContextAddLines ( mrContext, CGpoints, nPoints ); + if ( nSalFlags & SAL_INVERT_TRACKFRAME ) + { + const float dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line + CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); + CGContextSetRGBStrokeColor ( mrContext, 1.0, 1.0, 1.0, 1.0 ); + CGContextSetLineDash ( mrContext, 0, dashLengths, 2 ); + CGContextSetLineWidth( mrContext, 2.0); + CGContextStrokePath ( mrContext ); + } + else if ( nSalFlags & SAL_INVERT_50 ) + { + CGContextSetBlendMode(mrContext, kCGBlendModeDifference); + Pattern50Fill(); + } + else // just invert + { + CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); + CGContextSetRGBFillColor( mrContext, 1.0, 1.0, 1.0, 1.0 ); + CGContextFillPath( mrContext ); + } + const CGRect aRefreshRect = CGContextGetClipBoundingBox(mrContext); + CGContextRestoreGState( mrContext); + delete [] CGpoints; + RefreshRect( aRefreshRect ); + } +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalGraphics::drawEPS( long nX, long nY, long nWidth, long nHeight, + void* pEpsData, ULONG nByteCount ) +{ + // convert the raw data to an NSImageRef + NSData* xNSData = [NSData dataWithBytes:(void*)pEpsData length:(int)nByteCount]; + NSImageRep* xEpsImage = [NSEPSImageRep imageRepWithData: xNSData]; + if( !xEpsImage ) + return false; + + // get the target context + if( !CheckContext() ) + return false; + + // NOTE: flip drawing, else the nsimage would be drawn upside down + CGContextSaveGState( mrContext ); +// CGContextTranslateCTM( mrContext, 0, +mnHeight ); + CGContextScaleCTM( mrContext, +1, -1 ); + nY = /*mnHeight*/ - (nY + nHeight); + + // prepare the target context + NSGraphicsContext* pOrigNSCtx = [NSGraphicsContext currentContext]; + NSGraphicsContext* pDrawNSCtx = [NSGraphicsContext graphicsContextWithGraphicsPort: mrContext flipped: IsFlipped()]; + [NSGraphicsContext setCurrentContext: pDrawNSCtx]; + // draw the EPS + const NSRect aDstRect = {{nX,nY},{nWidth,nHeight}}; + const BOOL bOK = [xEpsImage drawInRect: aDstRect]; + CGContextRestoreGState( mrContext ); + // mark the destination rectangle as updated + RefreshRect( aDstRect ); + // restore the NSGraphicsContext, TODO: do we need this? + [NSGraphicsContext setCurrentContext: pOrigNSCtx]; + + return bOK; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +bool AquaSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR, + const SalBitmap& rSrcBitmap, const SalBitmap& rAlphaBmp ) +{ + // An image mask can't have a depth > 8 bits (should be 1 to 8 bits) + if( rAlphaBmp.GetBitCount() > 8 ) + return false; + + // are these two tests really necessary? (see vcl/unx/source/gdi/salgdi2.cxx) + // horizontal/vertical mirroring not implemented yet + if( rTR.mnDestWidth < 0 || rTR.mnDestHeight < 0 ) + return false; + + const AquaSalBitmap& rSrcSalBmp = static_cast<const AquaSalBitmap&>(rSrcBitmap); + const AquaSalBitmap& rMaskSalBmp = static_cast<const AquaSalBitmap&>(rAlphaBmp); + + CGImageRef xMaskedImage = rSrcSalBmp.CreateWithMask( rMaskSalBmp, rTR.mnSrcX, rTR.mnSrcY, rTR.mnSrcWidth, rTR.mnSrcHeight ); + if( !xMaskedImage ) + return false; + + if ( CheckContext() ) + { + const CGRect aDstRect = {{rTR.mnDestX, rTR.mnDestY}, {rTR.mnDestWidth, rTR.mnDestHeight}}; + CGContextDrawImage( mrContext, aDstRect, xMaskedImage ); + RefreshRect( aDstRect ); + } + + CGImageRelease(xMaskedImage); + return true; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +bool AquaSalGraphics::drawAlphaRect( long nX, long nY, long nWidth, + long nHeight, sal_uInt8 nTransparency ) +{ + if( !CheckContext() ) + return true; + + // save the current state + CGContextSaveGState( mrContext ); + CGContextSetAlpha( mrContext, (100-nTransparency) * (1.0/100) ); + + CGRect aRect = {{nX,nY},{nWidth-1,nHeight-1}}; + if( IsPenVisible() ) + { + aRect.origin.x += 0.5; + aRect.origin.y += 0.5; + } + + CGContextBeginPath( mrContext ); + CGContextAddRect( mrContext, aRect ); + CGContextDrawPath( mrContext, kCGPathFill ); + + // restore state + CGContextRestoreGState(mrContext); + RefreshRect( aRect ); + return true; +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::SetTextColor( SalColor nSalColor ) +{ + RGBColor color; + color.red = (unsigned short) ( SALCOLOR_RED(nSalColor) * 65535.0 / 255.0 ); + color.green = (unsigned short) ( SALCOLOR_GREEN(nSalColor) * 65535.0 / 255.0 ); + color.blue = (unsigned short) ( SALCOLOR_BLUE(nSalColor) * 65535.0 / 255.0 ); + + ATSUAttributeTag aTag = kATSUColorTag; + ByteCount aValueSize = sizeof( color ); + ATSUAttributeValuePtr aValue = &color; + + OSStatus err = ATSUSetAttributes( maATSUStyle, 1, &aTag, &aValueSize, &aValue ); + DBG_ASSERT( (err==noErr), "AquaSalGraphics::SetTextColor() : Could not set font attributes!\n"); + if( err != noErr ) + return; +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::GetFontMetric( ImplFontMetricData* pMetric ) +{ + // get the ATSU font metrics (in point units) + // of the font that has eventually been size-limited + + ATSUFontID fontId; + OSStatus err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(ATSUFontID), &fontId, 0 ); + DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font id\n"); + + ATSFontMetrics aMetrics; + ATSFontRef rFont = FMGetATSFontRefFromFont( fontId ); + err = ATSFontGetHorizontalMetrics ( rFont, kATSOptionFlagsDefault, &aMetrics ); + DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font metrics\n"); + if( err != noErr ) + return; + + // all ATS fonts are scalable fonts + pMetric->mbScalableFont = true; + // TODO: check if any kerning is possible + pMetric->mbKernableFont = true; + + // convert into VCL font metrics (in unscaled pixel units) + + Fixed ptSize; + err = ATSUGetAttribute( maATSUStyle, kATSUSizeTag, sizeof(Fixed), &ptSize, 0); + DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font size\n"); + const double fPointSize = Fix2X( ptSize ); + + // convert quartz units to pixel units + // please see the comment in AquaSalGraphics::SetFont() for details + const double fPixelSize = (mfFontScale * mfFakeDPIScale * fPointSize); + pMetric->mnAscent = static_cast<long>(+aMetrics.ascent * fPixelSize + 0.5); + pMetric->mnDescent = static_cast<long>(-aMetrics.descent * fPixelSize + 0.5); + const long nExtDescent = static_cast<long>((-aMetrics.descent + aMetrics.leading) * fPixelSize + 0.5); + pMetric->mnExtLeading = nExtDescent - pMetric->mnDescent; + pMetric->mnIntLeading = 0; + // ATSFontMetrics.avgAdvanceWidth is obsolete, so it is usually set to zero + // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts + // setting this width to the pixel height of the fontsize is good enough + // it also makes the calculation of the stretch factor simple + pMetric->mnWidth = static_cast<long>(mfFontStretch * fPixelSize + 0.5); +} + +// ----------------------------------------------------------------------- + +ULONG AquaSalGraphics::GetKernPairs( ULONG nPairs, ImplKernPairData* pKernPairs ) +{ + return 0; +} + +// ----------------------------------------------------------------------- + +static bool AddTempFontDir( const char* pDir ) +{ + FSRef aPathFSRef; + Boolean bIsDirectory = true; + OSStatus eStatus = FSPathMakeRef( reinterpret_cast<const UInt8*>(pDir), &aPathFSRef, &bIsDirectory ); + DBG_ASSERTWARNING( (eStatus==noErr) && bIsDirectory, "vcl AddTempFontDir() with invalid directory name!" ); + if( eStatus != noErr ) + return false; + + // TODO: deactivate ATSFontContainerRef when closing app + ATSFontContainerRef aATSFontContainer; + + const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global??? +#if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + eStatus = ::ATSFontActivateFromFileReference( &aPathFSRef, + eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, + &aATSFontContainer ); +#else + FSSpec aPathFSSpec; + eStatus = ::FSGetCatalogInfo( &aPathFSRef, kFSCatInfoNone, + NULL, NULL, &aPathFSSpec, NULL ); + if( eStatus != noErr ) + return false; + + eStatus = ::ATSFontActivateFromFileSpecification( &aPathFSSpec, + eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, + &aATSFontContainer ); +#endif + if( eStatus != noErr ) + return false; + + return true; +} + +static bool AddLocalTempFontDirs( void ) +{ + static bool bFirst = true; + if( !bFirst ) + return false; + bFirst = false; + + // add private font files found in brand and base layer + + rtl::OUString aBrandStr( RTL_CONSTASCII_USTRINGPARAM( "$BRAND_BASE_DIR" ) ); + rtl_bootstrap_expandMacros( &aBrandStr.pData ); + rtl::OUString aBrandSysPath; + OSL_VERIFY( osl_getSystemPathFromFileURL( aBrandStr.pData, &aBrandSysPath.pData ) == osl_File_E_None ); + + rtl::OStringBuffer aBrandFontDir( aBrandSysPath.getLength()*2 ); + aBrandFontDir.append( rtl::OUStringToOString( aBrandSysPath, RTL_TEXTENCODING_UTF8 ) ); + aBrandFontDir.append( "/share/fonts/truetype/" ); + bool bBrandSuccess = AddTempFontDir( aBrandFontDir.getStr() ); + + rtl::OUString aBaseStr( RTL_CONSTASCII_USTRINGPARAM( "$OOO_BASE_DIR" ) ); + rtl_bootstrap_expandMacros( &aBaseStr.pData ); + rtl::OUString aBaseSysPath; + OSL_VERIFY( osl_getSystemPathFromFileURL( aBaseStr.pData, &aBaseSysPath.pData ) == osl_File_E_None ); + + rtl::OStringBuffer aBaseFontDir( aBaseSysPath.getLength()*2 ); + aBaseFontDir.append( rtl::OUStringToOString( aBaseSysPath, RTL_TEXTENCODING_UTF8 ) ); + aBaseFontDir.append( "/share/fonts/truetype/" ); + bool bBaseSuccess = AddTempFontDir( aBaseFontDir.getStr() ); + + return bBrandSuccess && bBaseSuccess; +} + +void AquaSalGraphics::GetDevFontList( ImplDevFontList* pFontList ) +{ + DBG_ASSERT( pFontList, "AquaSalGraphics::GetDevFontList(NULL) !"); + + AddLocalTempFontDirs(); + + // The idea is to cache the list of system fonts once it has been generated. + // SalData seems to be a good place for this caching. However we have to + // carefully make the access to the font list thread-safe. If we register + // a font-change event handler to update the font list in case fonts have + // changed on the system we have to lock access to the list. The right + // way to do that is the solar mutex since GetDevFontList is protected + // through it as should be all event handlers + + SalData* pSalData = GetSalData(); + if (pSalData->mpFontList == NULL) + pSalData->mpFontList = new SystemFontList(); + + // Copy all ImplFontData objects contained in the SystemFontList + pSalData->mpFontList->AnnounceFonts( *pFontList ); +} + +// ----------------------------------------------------------------------- + +bool AquaSalGraphics::AddTempDevFont( ImplDevFontList* pFontList, + const String& rFontFileURL, const String& rFontName ) +{ + ::rtl::OUString aUSytemPath; + OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) ); + + FSRef aNewRef; + Boolean bIsDirectory = true; + ::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, RTL_TEXTENCODING_UTF8 ); + OSStatus eStatus = FSPathMakeRef( (UInt8*)aCFileName.getStr(), &aNewRef, &bIsDirectory ); + DBG_ASSERT( (eStatus==noErr) && !bIsDirectory, "vcl AddTempDevFont() with invalid fontfile name!" ); + if( eStatus != noErr ) + return false; + + ATSFontContainerRef oContainer; + + const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global??? +#if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + eStatus = ::ATSFontActivateFromFileReference( &aNewRef, + eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, + &oContainer ); +#else + FSSpec aFontFSSpec; + eStatus = ::FSGetCatalogInfo( &aNewRef, kFSCatInfoNone, + NULL, NULL, &aFontFSSpec, NULL ); + if( eStatus != noErr ) + return false; + + eStatus = ::ATSFontActivateFromFileSpecification( &aFontFSSpec, + eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, + &oContainer ); +#endif + if( eStatus != noErr ) + return false; + + // TODO: ATSFontDeactivate( oContainer ) when fonts are no longer needed + // TODO: register new ImplMacFontdata in pFontList + return true; +} + +// ----------------------------------------------------------------------- + +// callbacks from ATSUGlyphGetCubicPaths() fore GetGlyphOutline() +struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; }; + +static OSStatus GgoLineToProc( const Float32Point* pPoint, void* pData ) +{ + basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; + const basegfx::B2DPoint aB2DPoint( pPoint->x, pPoint->y ); + rPolygon.append( aB2DPoint ); + return noErr; +} + +static OSStatus GgoCurveToProc( const Float32Point* pCP1, const Float32Point* pCP2, + const Float32Point* pPoint, void* pData ) +{ + basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; + const sal_uInt32 nPointCount = rPolygon.count(); + const basegfx::B2DPoint aB2DControlPoint1( pCP1->x, pCP1->y ); + rPolygon.setNextControlPoint( nPointCount-1, aB2DControlPoint1 ); + const basegfx::B2DPoint aB2DEndPoint( pPoint->x, pPoint->y ); + rPolygon.append( aB2DEndPoint ); + const basegfx::B2DPoint aB2DControlPoint2( pCP2->x, pCP2->y ); + rPolygon.setPrevControlPoint( nPointCount, aB2DControlPoint2 ); + return noErr; +} + +static OSStatus GgoClosePathProc( void* pData ) +{ + GgoData* pGgoData = static_cast<GgoData*>(pData); + basegfx::B2DPolygon& rPolygon = pGgoData->maPolygon; + if( rPolygon.count() > 0 ) + pGgoData->mpPolyPoly->append( rPolygon ); + rPolygon.clear(); + return noErr; +} + +static OSStatus GgoMoveToProc( const Float32Point* pPoint, void* pData ) +{ + GgoClosePathProc( pData ); + OSStatus eStatus = GgoLineToProc( pPoint, pData ); + return eStatus; +} + +BOOL AquaSalGraphics::GetGlyphOutline( long nGlyphId, basegfx::B2DPolyPolygon& rPolyPoly ) +{ + GgoData aGgoData; + aGgoData.mpPolyPoly = &rPolyPoly; + rPolyPoly.clear(); + + ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback when CWS pdffix02 is integrated + OSStatus eGgoStatus = noErr; + OSStatus eStatus = ATSUGlyphGetCubicPaths( rATSUStyle, nGlyphId, + GgoMoveToProc, GgoLineToProc, GgoCurveToProc, GgoClosePathProc, + &aGgoData, &eGgoStatus ); + if( (eStatus != noErr) ) // TODO: why is (eGgoStatus!=noErr) when curves are involved? + return false; + + GgoClosePathProc( &aGgoData ); + if( mfFontScale != 1.0 ) { + rPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix(+mfFontScale, +mfFontScale)); + } + return true; +} + +// ----------------------------------------------------------------------- + +long AquaSalGraphics::GetGraphicsWidth() const +{ + long w = 0; + if( mrContext && (mbWindow || mbVirDev) ) + { + w = mnWidth; + } + + if( w == 0 ) + { + if( mbWindow && mpFrame ) + w = mpFrame->maGeometry.nWidth; + } + + return w; +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalGraphics::GetGlyphBoundRect( long nGlyphId, Rectangle& rRect ) +{ + ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback + GlyphID aGlyphId = nGlyphId; + ATSGlyphScreenMetrics aGlyphMetrics; + OSStatus eStatus = ATSUGlyphGetScreenMetrics( rATSUStyle, + 1, &aGlyphId, 0, FALSE, !mbNonAntialiasedText, &aGlyphMetrics ); + if( eStatus != noErr ) + return false; + + const long nMinX = (long)(+aGlyphMetrics.topLeft.x * mfFontScale - 0.5); + const long nMaxX = (long)(aGlyphMetrics.width * mfFontScale + 0.5) + nMinX; + const long nMinY = (long)(-aGlyphMetrics.topLeft.y * mfFontScale - 0.5); + const long nMaxY = (long)(aGlyphMetrics.height * mfFontScale + 0.5) + nMinY; + rRect = Rectangle( nMinX, nMinY, nMaxX, nMaxY ); + return true; +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::GetDevFontSubstList( OutputDevice* ) +{ + // nothing to do since there are no device-specific fonts on Aqua +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::DrawServerFontLayout( const ServerFontLayout& ) +{ +} + +// ----------------------------------------------------------------------- + +USHORT AquaSalGraphics::SetFont( ImplFontSelectData* pReqFont, int nFallbackLevel ) +{ + if( !pReqFont ) + { + ATSUClearStyle( maATSUStyle ); + mpMacFontData = NULL; + return 0; + } + + // store the requested device font entry + const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>( pReqFont->mpFontData ); + mpMacFontData = pMacFont; + + // convert pixel units (as seen by upper layers) to typographic point units + double fScaledAtsHeight = pReqFont->mfExactHeight; + // avoid Fixed16.16 overflows by limiting the ATS font size + static const float fMaxAtsHeight = 144.0; + if( fScaledAtsHeight <= fMaxAtsHeight ) + mfFontScale = 1.0; + else + { + mfFontScale = fScaledAtsHeight / fMaxAtsHeight; + fScaledAtsHeight = fMaxAtsHeight; + } + Fixed fFixedSize = FloatToFixed( fScaledAtsHeight ); + // enable bold-emulation if needed + Boolean bFakeBold = FALSE; + if( (pReqFont->GetWeight() >= WEIGHT_BOLD) + && (pMacFont->GetWeight() < WEIGHT_SEMIBOLD) ) + bFakeBold = TRUE; + // enable italic-emulation if needed + Boolean bFakeItalic = FALSE; + if( ((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE)) + && !((pMacFont->GetSlant() == ITALIC_NORMAL) || (pMacFont->GetSlant() == ITALIC_OBLIQUE)) ) + bFakeItalic = TRUE; + + // enable/disable antialiased text + mbNonAntialiasedText = pReqFont->mbNonAntialiased; + UInt32 nStyleRenderingOptions = kATSStyleNoOptions; + if( pReqFont->mbNonAntialiased ) + nStyleRenderingOptions |= kATSStyleNoAntiAliasing; + + // set horizontal/vertical mode + ATSUVerticalCharacterType aVerticalCharacterType = kATSUStronglyHorizontal; + if( pReqFont->mbVertical ) + aVerticalCharacterType = kATSUStronglyVertical; + + // prepare ATS-fontid as type matching to the kATSUFontTag request + ATSUFontID nFontID = static_cast<ATSUFontID>(pMacFont->GetFontId()); + + // update ATSU style attributes with requested font parameters + // TODO: no need to set styles which are already defaulted + + const ATSUAttributeTag aTag[] = + { + kATSUFontTag, + kATSUSizeTag, + kATSUQDBoldfaceTag, + kATSUQDItalicTag, + kATSUStyleRenderingOptionsTag, + kATSUVerticalCharacterTag + }; + + const ByteCount aValueSize[] = + { + sizeof(ATSUFontID), + sizeof(fFixedSize), + sizeof(bFakeBold), + sizeof(bFakeItalic), + sizeof(nStyleRenderingOptions), + sizeof(aVerticalCharacterType) + }; + + const ATSUAttributeValuePtr aValue[] = + { + &nFontID, + &fFixedSize, + &bFakeBold, + &bFakeItalic, + &nStyleRenderingOptions, + &aVerticalCharacterType + }; + + static const int nTagCount = sizeof(aTag) / sizeof(*aTag); + OSStatus eStatus = ATSUSetAttributes( maATSUStyle, nTagCount, + aTag, aValueSize, aValue ); + // reset ATSUstyle if there was an error + if( eStatus != noErr ) + { + DBG_WARNING( "AquaSalGraphics::SetFont() : Could not set font attributes!\n"); + ATSUClearStyle( maATSUStyle ); + mpMacFontData = NULL; + return 0; + } + + // prepare font stretching + const ATSUAttributeTag aMatrixTag = kATSUFontMatrixTag; + if( (pReqFont->mnWidth == 0) || (pReqFont->mnWidth == pReqFont->mnHeight) ) + { + mfFontStretch = 1.0; + ATSUClearAttributes( maATSUStyle, 1, &aMatrixTag ); + } + else + { + mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight; + CGAffineTransform aMatrix = CGAffineTransformMakeScale( mfFontStretch, 1.0F ); + const ATSUAttributeValuePtr aAttr = &aMatrix; + const ByteCount aMatrixBytes = sizeof(aMatrix); + eStatus = ATSUSetAttributes( maATSUStyle, 1, &aMatrixTag, &aMatrixBytes, &aAttr ); + DBG_ASSERT( (eStatus==noErr), "AquaSalGraphics::SetFont() : Could not set font matrix\n"); + } + + // prepare font rotation + mnATSUIRotation = FloatToFixed( pReqFont->mnOrientation / 10.0 ); + +#if OSL_DEBUG_LEVEL > 3 + fprintf( stderr, "SetFont to (\"%s\", \"%s\", fontid=%d) for (\"%s\" \"%s\" weight=%d, slant=%d size=%dx%d orientation=%d)\n", + ::rtl::OUStringToOString( pMacFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(), + ::rtl::OUStringToOString( pMacFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(), + (int)nFontID, + ::rtl::OUStringToOString( pReqFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(), + ::rtl::OUStringToOString( pReqFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(), + pReqFont->GetWeight(), + pReqFont->GetSlant(), + pReqFont->mnHeight, + pReqFont->mnWidth, + pReqFont->mnOrientation); +#endif + + return 0; +} + +// ----------------------------------------------------------------------- + +ImplFontCharMap* AquaSalGraphics::GetImplFontCharMap() const +{ + if( !mpMacFontData ) + return ImplFontCharMap::GetDefaultMap(); + + return mpMacFontData->GetImplFontCharMap(); +} + +// ----------------------------------------------------------------------- + +// fake a SFNT font directory entry for a font table +// see http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html#Directory +static void FakeDirEntry( FourCharCode eFCC, ByteCount nOfs, ByteCount nLen, + const unsigned char* /*pData*/, unsigned char*& rpDest ) +{ + // write entry tag + rpDest[ 0] = (char)(eFCC >> 24); + rpDest[ 1] = (char)(eFCC >> 16); + rpDest[ 2] = (char)(eFCC >> 8); + rpDest[ 3] = (char)(eFCC >> 0); + // TODO: get entry checksum and write it + // not too important since the subsetter doesn't care currently + // for( pData+nOfs ... pData+nOfs+nLen ) + // write entry offset + rpDest[ 8] = (char)(nOfs >> 24); + rpDest[ 9] = (char)(nOfs >> 16); + rpDest[10] = (char)(nOfs >> 8); + rpDest[11] = (char)(nOfs >> 0); + // write entry length + rpDest[12] = (char)(nLen >> 24); + rpDest[13] = (char)(nLen >> 16); + rpDest[14] = (char)(nLen >> 8); + rpDest[15] = (char)(nLen >> 0); + // advance to next entry + rpDest += 16; +} + +static bool GetRawFontData( const ImplFontData* pFontData, + ByteVector& rBuffer, bool* pJustCFF ) +{ + const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>(pFontData); + const ATSUFontID nFontId = static_cast<ATSUFontID>(pMacFont->GetFontId()); + ATSFontRef rFont = FMGetATSFontRefFromFont( nFontId ); + + ByteCount nCffLen = 0; + OSStatus eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, 0, NULL, &nCffLen); + if( pJustCFF != NULL ) + { + *pJustCFF = (eStatus == noErr) && (nCffLen > 0); + if( *pJustCFF ) + { + rBuffer.resize( nCffLen ); + eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[0], &nCffLen); + if( (eStatus != noErr) || (nCffLen <= 0) ) + return false; + return true; + } + } + + // get font table availability and size in bytes + ByteCount nHeadLen = 0; + eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, 0, NULL, &nHeadLen); + if( (eStatus != noErr) || (nHeadLen <= 0) ) + return false; + ByteCount nMaxpLen = 0; + eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, 0, NULL, &nMaxpLen); + if( (eStatus != noErr) || (nMaxpLen <= 0) ) + return false; + ByteCount nCmapLen = 0; + eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nCmapLen); + if( (eStatus != noErr) || (nCmapLen <= 0) ) + return false; + ByteCount nNameLen = 0; + eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, 0, NULL, &nNameLen); + if( (eStatus != noErr) || (nNameLen <= 0) ) + return false; + ByteCount nHheaLen = 0; + eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, 0, NULL, &nHheaLen); + if( (eStatus != noErr) || (nHheaLen <= 0) ) + return false; + ByteCount nHmtxLen = 0; + eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, 0, NULL, &nHmtxLen); + if( (eStatus != noErr) || (nHmtxLen <= 0) ) + return false; + + // get the glyph outline tables + ByteCount nLocaLen = 0; + ByteCount nGlyfLen = 0; + if( (eStatus != noErr) || (nCffLen <= 0) ) + { + eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, 0, NULL, &nLocaLen); + if( (eStatus != noErr) || (nLocaLen <= 0) ) + return false; + eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, 0, NULL, &nGlyfLen); + if( (eStatus != noErr) || (nGlyfLen <= 0) ) + return false; + } + + ByteCount nPrepLen=0, nCvtLen=0, nFpgmLen=0; + if( nGlyfLen ) // TODO: reduce PDF size by making hint subsetting optional + { + eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, 0, NULL, &nPrepLen); + eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, 0, NULL, &nCvtLen); + eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, 0, NULL, &nFpgmLen); + } + + // prepare a byte buffer for a fake font + int nTableCount = 7; + nTableCount += (nPrepLen>0) + (nCvtLen>0) + (nFpgmLen>0) + (nGlyfLen>0); + const ByteCount nFdirLen = 12 + 16*nTableCount; + ByteCount nTotalLen = nFdirLen; + nTotalLen += nHeadLen + nMaxpLen + nNameLen + nCmapLen; + if( nGlyfLen ) + nTotalLen += nLocaLen + nGlyfLen; + else + nTotalLen += nCffLen; + nTotalLen += nHheaLen + nHmtxLen; + nTotalLen += nPrepLen + nCvtLen + nFpgmLen; + rBuffer.resize( nTotalLen ); + + // fake a SFNT font directory header + if( nTableCount < 16 ) + { + int nLog2 = 0; + while( (nTableCount >> nLog2) > 1 ) ++nLog2; + rBuffer[ 1] = 1; // Win-TTF style scaler + rBuffer[ 5] = nTableCount; // table count + rBuffer[ 7] = nLog2*16; // searchRange + rBuffer[ 9] = nLog2; // entrySelector + rBuffer[11] = (nTableCount-nLog2)*16; // rangeShift + } + + // get font table raw data and update the fake directory entries + ByteCount nOfs = nFdirLen; + unsigned char* pFakeEntry = &rBuffer[12]; + eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nCmapLen, (void*)&rBuffer[nOfs], &nCmapLen); + FakeDirEntry( GetTag("cmap"), nOfs, nCmapLen, &rBuffer[0], pFakeEntry ); + nOfs += nCmapLen; + if( nCvtLen ) { + eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, nCvtLen, (void*)&rBuffer[nOfs], &nCvtLen); + FakeDirEntry( GetTag("cvt "), nOfs, nCvtLen, &rBuffer[0], pFakeEntry ); + nOfs += nCvtLen; + } + if( nFpgmLen ) { + eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, nFpgmLen, (void*)&rBuffer[nOfs], &nFpgmLen); + FakeDirEntry( GetTag("fpgm"), nOfs, nFpgmLen, &rBuffer[0], pFakeEntry ); + nOfs += nFpgmLen; + } + if( nCffLen ) { + eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[nOfs], &nCffLen); + FakeDirEntry( GetTag("CFF "), nOfs, nCffLen, &rBuffer[0], pFakeEntry ); + nOfs += nGlyfLen; + } else { + eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, nGlyfLen, (void*)&rBuffer[nOfs], &nGlyfLen); + FakeDirEntry( GetTag("glyf"), nOfs, nGlyfLen, &rBuffer[0], pFakeEntry ); + nOfs += nGlyfLen; + eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, nLocaLen, (void*)&rBuffer[nOfs], &nLocaLen); + FakeDirEntry( GetTag("loca"), nOfs, nLocaLen, &rBuffer[0], pFakeEntry ); + nOfs += nLocaLen; + } + eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, nHeadLen, (void*)&rBuffer[nOfs], &nHeadLen); + FakeDirEntry( GetTag("head"), nOfs, nHeadLen, &rBuffer[0], pFakeEntry ); + nOfs += nHeadLen; + eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, nHheaLen, (void*)&rBuffer[nOfs], &nHheaLen); + FakeDirEntry( GetTag("hhea"), nOfs, nHheaLen, &rBuffer[0], pFakeEntry ); + nOfs += nHheaLen; + eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, nHmtxLen, (void*)&rBuffer[nOfs], &nHmtxLen); + FakeDirEntry( GetTag("hmtx"), nOfs, nHmtxLen, &rBuffer[0], pFakeEntry ); + nOfs += nHmtxLen; + eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, nMaxpLen, (void*)&rBuffer[nOfs], &nMaxpLen); + FakeDirEntry( GetTag("maxp"), nOfs, nMaxpLen, &rBuffer[0], pFakeEntry ); + nOfs += nMaxpLen; + eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, nNameLen, (void*)&rBuffer[nOfs], &nNameLen); + FakeDirEntry( GetTag("name"), nOfs, nNameLen, &rBuffer[0], pFakeEntry ); + nOfs += nNameLen; + if( nPrepLen ) { + eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, nPrepLen, (void*)&rBuffer[nOfs], &nPrepLen); + FakeDirEntry( GetTag("prep"), nOfs, nPrepLen, &rBuffer[0], pFakeEntry ); + nOfs += nPrepLen; + } + + DBG_ASSERT( (nOfs==nTotalLen), "AquaSalGraphics::CreateFontSubset (nOfs!=nTotalLen)"); + + return true; +} + +BOOL AquaSalGraphics::CreateFontSubset( const rtl::OUString& rToFile, + const ImplFontData* pFontData, long* pGlyphIDs, sal_uInt8* pEncoding, + sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo ) +{ + // TODO: move more of the functionality here into the generic subsetter code + + // prepare the requested file name for writing the font-subset file + rtl::OUString aSysPath; + if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) ) + return FALSE; + const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding(); + const ByteString aToFile( rtl::OUStringToOString( aSysPath, aThreadEncoding ) ); + + // get the raw-bytes from the font to be subset + ByteVector aBuffer; + bool bCffOnly = false; + if( !GetRawFontData( pFontData, aBuffer, &bCffOnly ) ) + return sal_False; + + // handle CFF-subsetting + if( bCffOnly ) + { + // provide the raw-CFF data to the subsetter + ByteCount nCffLen = aBuffer.size(); + rInfo.LoadFont( FontSubsetInfo::CFF_FONT, &aBuffer[0], nCffLen ); + + // NOTE: assuming that all glyphids requested on Aqua are fully translated + + // make the subsetter provide the requested subset + FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" ); + bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL, + pGlyphIDs, pEncoding, nGlyphCount, pGlyphWidths ); + fclose( pOutFile ); + return bRC; + } + + // TODO: modernize psprint's horrible fontsubset C-API + // this probably only makes sense after the switch to another SCM + // that can preserve change history after file renames + + // prepare data for psprint's font subsetter + TrueTypeFont* pSftFont = NULL; + int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont); + if( nRC != SF_OK ) + return sal_False; + + // get details about the subsetted font + TTGlobalFontInfo aTTInfo; + ::GetTTGlobalFontInfo( pSftFont, &aTTInfo ); + rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; + rInfo.m_aPSName = String( aTTInfo.psname, RTL_TEXTENCODING_UTF8 ); + rInfo.m_aFontBBox = Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ), + Point( aTTInfo.xMax, aTTInfo.yMax ) ); + rInfo.m_nCapHeight = aTTInfo.yMax; // Well ... + rInfo.m_nAscent = +aTTInfo.winAscent; + rInfo.m_nDescent = -aTTInfo.winDescent; + // mac fonts usually do not have an OS2-table + // => get valid ascent/descent values from other tables + if( !rInfo.m_nAscent ) + rInfo.m_nAscent = +aTTInfo.typoAscender; + if( !rInfo.m_nAscent ) + rInfo.m_nAscent = +aTTInfo.ascender; + if( !rInfo.m_nDescent ) + rInfo.m_nDescent = +aTTInfo.typoDescender; + if( !rInfo.m_nDescent ) + rInfo.m_nDescent = -aTTInfo.descender; + + // subset glyphs and get their properties + // take care that subset fonts require the NotDef glyph in pos 0 + int nOrigCount = nGlyphCount; + USHORT aShortIDs[ 256 ]; + sal_uInt8 aTempEncs[ 256 ]; + + int nNotDef = -1; + for( int i = 0; i < nGlyphCount; ++i ) + { + aTempEncs[i] = pEncoding[i]; + sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK; + if( pGlyphIDs[i] & GF_ISCHAR ) + { + bool bVertical = (pGlyphIDs[i] & GF_ROTMASK) != 0; + nGlyphIdx = ::MapChar( pSftFont, static_cast<sal_uInt16>(nGlyphIdx), bVertical ); + if( nGlyphIdx == 0 && pFontData->IsSymbolFont() ) + { + // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX + nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK; + nGlyphIdx = (nGlyphIdx & 0xF000) ? (nGlyphIdx & 0x00FF) : (nGlyphIdx | 0xF000 ); + nGlyphIdx = ::MapChar( pSftFont, static_cast<sal_uInt16>(nGlyphIdx), bVertical ); + } + } + aShortIDs[i] = static_cast<USHORT>( nGlyphIdx ); + if( !nGlyphIdx ) + if( nNotDef < 0 ) + nNotDef = i; // first NotDef glyph found + } + + if( nNotDef != 0 ) + { + // add fake NotDef glyph if needed + if( nNotDef < 0 ) + nNotDef = nGlyphCount++; + + // NotDef glyph must be in pos 0 => swap glyphids + aShortIDs[ nNotDef ] = aShortIDs[0]; + aTempEncs[ nNotDef ] = aTempEncs[0]; + aShortIDs[0] = 0; + aTempEncs[0] = 0; + } + DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" ); + + // TODO: where to get bVertical? + const bool bVertical = false; + + // fill the pGlyphWidths array + // while making sure that the NotDef glyph is at index==0 + TTSimpleGlyphMetrics* pGlyphMetrics = + ::GetTTSimpleGlyphMetrics( pSftFont, aShortIDs, nGlyphCount, bVertical ); + if( !pGlyphMetrics ) + return FALSE; + sal_uInt16 nNotDefAdv = pGlyphMetrics[0].adv; + pGlyphMetrics[0].adv = pGlyphMetrics[nNotDef].adv; + pGlyphMetrics[nNotDef].adv = nNotDefAdv; + for( int i = 0; i < nOrigCount; ++i ) + pGlyphWidths[i] = pGlyphMetrics[i].adv; + free( pGlyphMetrics ); + + // write subset into destination file + nRC = ::CreateTTFromTTGlyphs( pSftFont, aToFile.GetBuffer(), aShortIDs, + aTempEncs, nGlyphCount, 0, NULL, 0 ); + ::CloseTTFont(pSftFont); + return (nRC == SF_OK); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::GetGlyphWidths( const ImplFontData* pFontData, bool bVertical, + Int32Vector& rGlyphWidths, Ucs2UIntMap& rUnicodeEnc ) +{ + rGlyphWidths.clear(); + rUnicodeEnc.clear(); + + if( pFontData->IsSubsettable() ) + { + ByteVector aBuffer; + if( !GetRawFontData( pFontData, aBuffer, NULL ) ) + return; + + // TODO: modernize psprint's horrible fontsubset C-API + // this probably only makes sense after the switch to another SCM + // that can preserve change history after file renames + + // use the font subsetter to get the widths + TrueTypeFont* pSftFont = NULL; + int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont); + if( nRC != SF_OK ) + return; + + const int nGlyphCount = ::GetTTGlyphCount( pSftFont ); + if( nGlyphCount > 0 ) + { + // get glyph metrics + rGlyphWidths.resize(nGlyphCount); + std::vector<sal_uInt16> aGlyphIds(nGlyphCount); + for( int i = 0; i < nGlyphCount; i++ ) + aGlyphIds[i] = static_cast<sal_uInt16>(i); + const TTSimpleGlyphMetrics* pGlyphMetrics = ::GetTTSimpleGlyphMetrics( + pSftFont, &aGlyphIds[0], nGlyphCount, bVertical ); + if( pGlyphMetrics ) + { + for( int i = 0; i < nGlyphCount; ++i ) + rGlyphWidths[i] = pGlyphMetrics[i].adv; + free( (void*)pGlyphMetrics ); + } + + const ImplFontCharMap* pMap = mpMacFontData->GetImplFontCharMap(); + DBG_ASSERT( pMap && pMap->GetCharCount(), "no charmap" ); + + // get unicode<->glyph encoding + int nCharCount = pMap->GetCharCount(); + sal_uInt32 nChar = pMap->GetFirstChar(); + for(; --nCharCount >= 0; nChar = pMap->GetNextChar( nChar ) ) + { + if( nChar > 0xFFFF ) // TODO: allow UTF-32 chars + break; + sal_Ucs nUcsChar = static_cast<sal_Ucs>(nChar); + sal_uInt32 nGlyph = ::MapChar( pSftFont, nUcsChar, bVertical ); + if( nGlyph > 0 ) + rUnicodeEnc[ nUcsChar ] = nGlyph; + } + } + + ::CloseTTFont( pSftFont ); + } + else if( pFontData->IsEmbeddable() ) + { + // get individual character widths +#if 0 // FIXME + rWidths.reserve( 224 ); + for( sal_Unicode i = 32; i < 256; ++i ) + { + int nCharWidth = 0; + if( ::GetCharWidth32W( mhDC, i, i, &nCharWidth ) ) + { + rUnicodeEnc[ i ] = rWidths.size(); + rWidths.push_back( nCharWidth ); + } + } +#else + DBG_ERROR("not implemented for non-subsettable fonts!\n"); +#endif + } +} + +// ----------------------------------------------------------------------- + +const Ucs2SIntMap* AquaSalGraphics::GetFontEncodingVector( + const ImplFontData* pFontData, const Ucs2OStrMap** ppNonEncoded ) +{ + return NULL; +} + +// ----------------------------------------------------------------------- + +const void* AquaSalGraphics::GetEmbedFontData( const ImplFontData* pFontData, + const sal_Ucs* pUnicodes, + sal_Int32* pWidths, + FontSubsetInfo& rInfo, + long* pDataLen ) +{ + return NULL; +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::FreeEmbedFontData( const void* pData, long nDataLen ) +{ + // TODO: implementing this only makes sense when the implementation of + // AquaSalGraphics::GetEmbedFontData() returns non-NULL + DBG_ASSERT( (pData!=NULL), "AquaSalGraphics::FreeEmbedFontData() is not implemented\n"); +} + +// ----------------------------------------------------------------------- + +SystemFontData AquaSalGraphics::GetSysFontData( int /* nFallbacklevel */ ) const +{ + SystemFontData aSysFontData; + OSStatus err; + aSysFontData.nSize = sizeof( SystemFontData ); + + // NOTE: Native ATSU font fallbacks are used, not the VCL fallbacks. + ATSUFontID fontId; + err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(fontId), &fontId, 0 ); + if (err) fontId = 0; + aSysFontData.aATSUFontID = (void *) fontId; + + Boolean bFbold; + err = ATSUGetAttribute( maATSUStyle, kATSUQDBoldfaceTag, sizeof(bFbold), &bFbold, 0 ); + if (err) bFbold = FALSE; + aSysFontData.bFakeBold = (bool) bFbold; + + Boolean bFItalic; + err = ATSUGetAttribute( maATSUStyle, kATSUQDItalicTag, sizeof(bFItalic), &bFItalic, 0 ); + if (err) bFItalic = FALSE; + aSysFontData.bFakeItalic = (bool) bFItalic; + + ATSUVerticalCharacterType aVerticalCharacterType; + err = ATSUGetAttribute( maATSUStyle, kATSUVerticalCharacterTag, sizeof(aVerticalCharacterType), &aVerticalCharacterType, 0 ); + if (!err && aVerticalCharacterType == kATSUStronglyVertical) { + aSysFontData.bVerticalCharacterType = true; + } else { + aSysFontData.bVerticalCharacterType = false; + } + + aSysFontData.bAntialias = !mbNonAntialiasedText; + + return aSysFontData; +} + +// ----------------------------------------------------------------------- + +SystemGraphicsData AquaSalGraphics::GetGraphicsData() const +{ + SystemGraphicsData aRes; + aRes.nSize = sizeof(aRes); + aRes.rCGContext = mrContext; + return aRes; +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::SetXORMode( bool bSet, bool bInvertOnly ) +{ + // return early if XOR mode remains unchanged + if( mbPrinter ) + return; + + if( ! bSet && mnXorMode == 2 ) + { + CGContextSetBlendMode( mrContext, kCGBlendModeNormal ); + mnXorMode = 0; + return; + } + else if( bSet && bInvertOnly && mnXorMode == 0) + { + CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); + mnXorMode = 2; + return; + } + + if( (mpXorEmulation == NULL) && !bSet ) + return; + if( (mpXorEmulation != NULL) && (bSet == mpXorEmulation->IsEnabled()) ) + return; + if( !CheckContext() ) + return; + + // prepare XOR emulation + if( !mpXorEmulation ) + { + mpXorEmulation = new XorEmulation(); + mpXorEmulation->SetTarget( mnWidth, mnHeight, mnBitmapDepth, mrContext, mxLayer ); + } + + // change the XOR mode + if( bSet ) + { + mpXorEmulation->Enable(); + mrContext = mpXorEmulation->GetMaskContext(); + mnXorMode = 1; + } + else + { + mpXorEmulation->UpdateTarget(); + mpXorEmulation->Disable(); + mrContext = mpXorEmulation->GetTargetContext(); + mnXorMode = 0; + } +} + +// ----------------------------------------------------------------------- + +// apply the XOR mask to the target context if active and dirty +void AquaSalGraphics::ApplyXorContext() +{ + if( !mpXorEmulation ) + return; + if( mpXorEmulation->UpdateTarget() ) + RefreshRect( 0, 0, mnWidth, mnHeight ); // TODO: refresh minimal changerect +} + +// ====================================================================== + +XorEmulation::XorEmulation() +: mxTargetLayer( NULL ) +, mxTargetContext( NULL ) +, mxMaskContext( NULL ) +, mxTempContext( NULL ) +, mpMaskBuffer( NULL ) +, mpTempBuffer( NULL ) +, mnBufferLongs( 0 ) +, mbIsEnabled( false ) +{} + +// ---------------------------------------------------------------------- + +XorEmulation::~XorEmulation() +{ + Disable(); + SetTarget( 0, 0, 0, NULL, NULL ); +} + +// ----------------------------------------------------------------------- + +void XorEmulation::SetTarget( int nWidth, int nHeight, int nTargetDepth, + CGContextRef xTargetContext, CGLayerRef xTargetLayer ) +{ + // prepare to replace old mask+temp context + if( mxMaskContext ) + { + // cleanup the mask context + CGContextRelease( mxMaskContext ); + delete[] mpMaskBuffer; + mxMaskContext = NULL; + mpMaskBuffer = NULL; + + // cleanup the temp context if needed + if( mxTempContext ) + { + CGContextRelease( mxTempContext ); + delete[] mpTempBuffer; + mxTempContext = NULL; + mpTempBuffer = NULL; + } + } + + // return early if there is nothing more to do + if( !xTargetContext ) + return; + + // retarget drawing operations to the XOR mask + mxTargetLayer = xTargetLayer; + mxTargetContext = xTargetContext; + + // prepare creation of matching CGBitmaps + CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; + CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst; + int nBitDepth = nTargetDepth; + if( !nBitDepth ) + nBitDepth = 32; + int nBytesPerRow = (nBitDepth == 16) ? 2 : 4; + const size_t nBitsPerComponent = (nBitDepth == 16) ? 5 : 8; + if( nBitDepth <= 8 ) + { + aCGColorSpace = GetSalData()->mxGraySpace; + aCGBmpInfo = kCGImageAlphaNone; + nBytesPerRow = 1; + } + nBytesPerRow *= nWidth; + mnBufferLongs = (nHeight * nBytesPerRow + sizeof(ULONG)-1) / sizeof(ULONG); + + // create a XorMask context + mpMaskBuffer = new ULONG[ mnBufferLongs ]; + mxMaskContext = ::CGBitmapContextCreate( mpMaskBuffer, + nWidth, nHeight, nBitsPerComponent, nBytesPerRow, + aCGColorSpace, aCGBmpInfo ); + // reset the XOR mask to black + memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(ULONG) ); + + // a bitmap context will be needed for manual XORing + // create one unless the target context is a bitmap context + if( nTargetDepth ) + mpTempBuffer = (ULONG*)CGBitmapContextGetData( mxTargetContext ); + if( !mpTempBuffer ) + { + // create a bitmap context matching to the target context + mpTempBuffer = new ULONG[ mnBufferLongs ]; + mxTempContext = ::CGBitmapContextCreate( mpTempBuffer, + nWidth, nHeight, nBitsPerComponent, nBytesPerRow, + aCGColorSpace, aCGBmpInfo ); + } + + // initialize XOR mask context for drawing + CGContextSetFillColorSpace( mxMaskContext, aCGColorSpace ); + CGContextSetStrokeColorSpace( mxMaskContext, aCGColorSpace ); + CGContextSetShouldAntialias( mxMaskContext, false ); + + // improve the XorMask's XOR emulation a litte + // NOTE: currently only enabled for monochrome contexts + if( aCGColorSpace == GetSalData()->mxGraySpace ) + CGContextSetBlendMode( mxMaskContext, kCGBlendModeDifference ); + + // intialize the transformation matrix to the drawing target + const CGAffineTransform aCTM = CGContextGetCTM( xTargetContext ); + CGContextConcatCTM( mxMaskContext, aCTM ); + if( mxTempContext ) + CGContextConcatCTM( mxTempContext, aCTM ); + + // initialize the default XorMask graphics state + CGContextSaveGState( mxMaskContext ); +} + +// ---------------------------------------------------------------------- + +bool XorEmulation::UpdateTarget() +{ + if( !IsEnabled() ) + return false; + + // update the temp bitmap buffer if needed + if( mxTempContext ) + CGContextDrawLayerAtPoint( mxTempContext, CGPointZero, mxTargetLayer ); + + // do a manual XOR with the XorMask + // this approach suffices for simple color manipulations + // and also the complex-clipping-XOR-trick used in metafiles + const ULONG* pSrc = mpMaskBuffer; + ULONG* pDst = mpTempBuffer; + for( int i = mnBufferLongs; --i >= 0;) + *(pDst++) ^= *(pSrc++); + + // write back the XOR results to the target context + if( mxTempContext ) + { + CGImageRef xXorImage = CGBitmapContextCreateImage( mxTempContext ); + const int nWidth = (int)CGImageGetWidth( xXorImage ); + const int nHeight = (int)CGImageGetHeight( xXorImage ); + // TODO: update minimal changerect + const CGRect aFullRect = {{0,0},{nWidth,nHeight}}; + CGContextDrawImage( mxTargetContext, aFullRect, xXorImage ); + CGImageRelease( xXorImage ); + } + + // reset the XorMask to black again + // TODO: not needed for last update + memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(ULONG) ); + + // TODO: return FALSE if target was not changed + return true; +} + +// ======================================================================= + diff --git a/vcl/aqua/source/gdi/salgdiutils.cxx b/vcl/aqua/source/gdi/salgdiutils.cxx new file mode 100755 index 000000000000..f7c234d2c4c4 --- /dev/null +++ b/vcl/aqua/source/gdi/salgdiutils.cxx @@ -0,0 +1,300 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salgdi.h" +#include "salframe.h" + +#include "basebmp/scanlineformats.hxx" +#include "basebmp/color.hxx" +#include "basegfx/range/b2drectangle.hxx" +#include "basegfx/range/b2irange.hxx" +#include "basegfx/vector/b2ivector.hxx" +#include "basegfx/polygon/b2dpolygon.hxx" +#include "basegfx/polygon/b2dpolygontools.hxx" +#include <boost/bind.hpp> + +#include "vcl/svapp.hxx" +#include "saldata.hxx" + +// ---------------------------------------------------------------------- + +void AquaSalGraphics::SetWindowGraphics( AquaSalFrame* pFrame ) +{ + mpFrame = pFrame; + + mbWindow = true; + mbPrinter = false; + mbVirDev = false; +} + +void AquaSalGraphics::SetPrinterGraphics( CGContextRef xContext, long nDPIX, long nDPIY, double fScale ) +{ + mbWindow = false; + mbPrinter = true; + mbVirDev = false; + + mrContext = xContext; + mfFakeDPIScale = fScale; + mnRealDPIX = nDPIX; + mnRealDPIY = nDPIY; + + // a previously set clip path is now invalid + if( mxClipPath ) + { + CGPathRelease( mxClipPath ); + mxClipPath = NULL; + } + + if( mrContext ) + { + CGContextSetFillColorSpace( mrContext, GetSalData()->mxRGBSpace ); + CGContextSetStrokeColorSpace( mrContext, GetSalData()->mxRGBSpace ); + CGContextSaveGState( mrContext ); + SetState(); + } +} + +void AquaSalGraphics::SetVirDevGraphics( CGLayerRef xLayer, CGContextRef xContext, + int nBitmapDepth ) +{ + mbWindow = false; + mbPrinter = false; + mbVirDev = true; + + // set graphics properties + mxLayer = xLayer; + mrContext = xContext; + mnBitmapDepth = nBitmapDepth; + + // return early if the virdev is being destroyed + if( !xContext ) + return; + + // get new graphics properties + if( !mxLayer ) + { + mnWidth = CGBitmapContextGetWidth( mrContext ); + mnHeight = CGBitmapContextGetHeight( mrContext ); + } + else + { + const CGSize aSize = CGLayerGetSize( mxLayer ); + mnWidth = static_cast<int>(aSize.width); + mnHeight = static_cast<int>(aSize.height); + } + + // prepare graphics for drawing + const CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; + CGContextSetFillColorSpace( mrContext, aCGColorSpace ); + CGContextSetStrokeColorSpace( mrContext, aCGColorSpace ); + + // re-enable XorEmulation for the new context + if( mpXorEmulation ) + { + mpXorEmulation->SetTarget( mnWidth, mnHeight, mnBitmapDepth, mrContext, mxLayer ); + if( mpXorEmulation->IsEnabled() ) + mrContext = mpXorEmulation->GetMaskContext(); + } + + // initialize stack of CGContext states + CGContextSaveGState( mrContext ); + SetState(); +} + +// ---------------------------------------------------------------------- + +void AquaSalGraphics::InvalidateContext() +{ + UnsetState(); + mrContext = 0; +} + +// ---------------------------------------------------------------------- + +void AquaSalGraphics::UnsetState() +{ + if( mrContext ) + { + CGContextRestoreGState( mrContext ); + mrContext = 0; + } + if( mxClipPath ) + { + CGPathRelease( mxClipPath ); + mxClipPath = NULL; + } +} + +void AquaSalGraphics::SetState() +{ + CGContextRestoreGState( mrContext ); + CGContextSaveGState( mrContext ); + + // setup clipping + if( mxClipPath ) + { + CGContextBeginPath( mrContext ); // discard any existing path + CGContextAddPath( mrContext, mxClipPath ); // set the current path to the clipping path + CGContextClip( mrContext ); // use it for clipping + } + + // set RGB colorspace and line and fill colors + CGContextSetFillColor( mrContext, maFillColor.AsArray() ); + CGContextSetStrokeColor( mrContext, maLineColor.AsArray() ); + CGContextSetShouldAntialias( mrContext, false ); + if( mnXorMode == 2 ) + CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); +} + +// ---------------------------------------------------------------------- + +bool AquaSalGraphics::CheckContext() +{ + if( mbWindow && mpFrame != NULL ) + { + const unsigned int nWidth = mpFrame->maGeometry.nWidth; + const unsigned int nHeight = mpFrame->maGeometry.nHeight; + + CGContextRef rReleaseContext = 0; + CGLayerRef rReleaseLayer = NULL; + + // check if a new drawing context is needed (e.g. after a resize) + if( (unsigned(mnWidth) != nWidth) || (unsigned(mnHeight) != nHeight) ) + { + mnWidth = nWidth; + mnHeight = nHeight; + // prepare to release the corresponding resources + rReleaseContext = mrContext; + rReleaseLayer = mxLayer; + mrContext = NULL; + mxLayer = NULL; + } + + if( !mrContext ) + { + const CGSize aLayerSize = {nWidth,nHeight}; + NSGraphicsContext* pNSGContext = [NSGraphicsContext graphicsContextWithWindow: mpFrame->getWindow()]; + CGContextRef xCGContext = reinterpret_cast<CGContextRef>([pNSGContext graphicsPort]); + mxLayer = CGLayerCreateWithContext( xCGContext, aLayerSize, NULL ); + if( mxLayer ) + mrContext = CGLayerGetContext( mxLayer ); + + if( mrContext ) + { + // copy original layer to resized layer + if( rReleaseLayer ) + CGContextDrawLayerAtPoint( mrContext, CGPointZero, rReleaseLayer ); + + CGContextTranslateCTM( mrContext, 0, nHeight ); + CGContextScaleCTM( mrContext, 1.0, -1.0 ); + CGContextSetFillColorSpace( mrContext, GetSalData()->mxRGBSpace ); + CGContextSetStrokeColorSpace( mrContext, GetSalData()->mxRGBSpace ); + CGContextSaveGState( mrContext ); + SetState(); + + // re-enable XOR emulation for the new context + if( mpXorEmulation ) + mpXorEmulation->SetTarget( mnWidth, mnHeight, mnBitmapDepth, mrContext, mxLayer ); + } + } + + if( rReleaseLayer ) + CGLayerRelease( rReleaseLayer ); + else if( rReleaseContext ) + CGContextRelease( rReleaseContext ); + } + + DBG_ASSERT( mrContext || mbPrinter, "<<<WARNING>>> AquaSalGraphics::CheckContext() FAILED!!!!\n" ); + return (mrContext != NULL); +} + + +void AquaSalGraphics::RefreshRect(float lX, float lY, float lWidth, float lHeight) +{ + if( ! mbWindow ) // view only on Window graphics + return; + + if( mpFrame ) + { + // update a little more around the designated rectangle + // this helps with antialiased rendering + const Rectangle aVclRect(Point(static_cast<long int>(lX-1), + static_cast<long int>(lY-1) ), + Size( static_cast<long int>(lWidth+2), + static_cast<long int>(lHeight+2) ) ); + mpFrame->maInvalidRect.Union( aVclRect ); + } +} + +CGPoint* AquaSalGraphics::makeCGptArray(ULONG nPoints, const SalPoint* pPtAry) +{ + CGPoint *CGpoints = new (CGPoint[nPoints]); + if ( CGpoints ) + { + for(ULONG i=0;i<nPoints;i++) + { + CGpoints[i].x = (float)(pPtAry[i].mnX); + CGpoints[i].y = (float)(pPtAry[i].mnY); + } + } + return CGpoints; +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::UpdateWindow( NSRect& rRect ) +{ + if( !mpFrame ) + return; + NSGraphicsContext* pContext = [NSGraphicsContext currentContext]; + if( (mxLayer != NULL) && (pContext != NULL) ) + { + CGContextRef rCGContext = reinterpret_cast<CGContextRef>([pContext graphicsPort]); + + CGMutablePathRef rClip = mpFrame->getClipPath(); + if( rClip ) + { + CGContextSaveGState( rCGContext ); + CGContextBeginPath( rCGContext ); + CGContextAddPath( rCGContext, rClip ); + CGContextClip( rCGContext ); + } + + ApplyXorContext(); + CGContextDrawLayerAtPoint( rCGContext, CGPointZero, mxLayer ); + if( rClip ) // cleanup clipping + CGContextRestoreGState( rCGContext ); + } + else + DBG_ASSERT( mpFrame->mbInitShow, "UpdateWindow called on uneligible graphics" ); +} + +// ----------------------------------------------------------------------- + diff --git a/vcl/aqua/source/gdi/salmathutils.cxx b/vcl/aqua/source/gdi/salmathutils.cxx new file mode 100755 index 000000000000..8df44acbf730 --- /dev/null +++ b/vcl/aqua/source/gdi/salmathutils.cxx @@ -0,0 +1,163 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salmathutils.hxx" + +#include <stdlib.h> + +// ======================================================================= + +// ======================================================================= + +#define Swap( x, y ) { x ^= y; y ^= x; x ^= y; } + +// ======================================================================= + +// ======================================================================= + +// Storage free swapping using XOR + +void CSwap ( char &rX, char &rY ) +{ + Swap( rX, rY ); +} // CSwap + +// ----------------------------------------------------------------------- + +// Storage free swapping using XOR + +void UCSwap ( unsigned char &rX, unsigned char &rY ) +{ + Swap( rX, rY ); +} // UCSwap + +// ----------------------------------------------------------------------- + +// Storage free swapping using XOR + +void SSwap ( short &rX, short &rY ) +{ + Swap( rX, rY ); +} // SSwap + +// ----------------------------------------------------------------------- + +// Storage free swapping using XOR + +void USSwap ( unsigned short &rX, unsigned short &rY ) +{ + Swap( rX, rY ); +} // USSwap + +// ----------------------------------------------------------------------- + +// Storage free swapping using XOR + +void LSwap ( long &rX, long &rY ) +{ + Swap( rX, rY ); +} // LSwap + +// ----------------------------------------------------------------------- + +// Storage free swapping using XOR + +void ULSwap ( unsigned long &rX, unsigned long &rY ) +{ + Swap( rX, rY ); +} // ULSwap + +// ======================================================================= + +// ======================================================================= + +// ----------------------------------------------------------------------- +// +// This way of measuring distance is also called the "Manhattan distance." +// Manhattan distance takes advantage of the fact that the sum of the +// lengths of the three components of a 3D vector is a rough approxima- +// tion of the vector's length. +// +// ----------------------------------------------------------------------- + +unsigned long Euclidian2Norm ( const LRectCoorVector pVec ) +{ + unsigned long ndist = 0; + + if ( pVec ) + { + long nDX = 0; + long nDY = 0; + long nDZ = 0; + unsigned long nMax = 0; + unsigned long nMed = 0; + unsigned long nMin = 0; + + // Find |x'-x|, |y'-y|, and |z'-z| from (x,y,z) and (x',y',z') + + nDX = pVec[1].x - pVec[0].x; + nDY = pVec[1].y - pVec[0].y; + nDZ = pVec[1].z - pVec[0].z; + + nMax = (unsigned long)abs( nDX ); + nMed = (unsigned long)abs( nDY ); + nMin = (unsigned long)abs( nDZ ); + + // Sort them (3 compares, 0-3 swaps) + + if ( nMax < nMed ) + { + Swap( nMax, nMed ); + } // if + + if ( nMax < nMin ) + { + Swap( nMax, nMin ); + } // if + + // Approximate Euclidian distance: + // + // d = max + (11/32)*med + (1/4)*min + // + // with +/- 8% error, where the exact formulae for d is + // + // || (x',y',z') - (x,y,z) || = { |x'-x|^2 + |y'-y|^2 + |z'-z|^2 }^(1/2) + + ndist = nMax + ( nMin >> 2UL ) + + ( ( ( nMed << 3UL ) + ( nMed << 1UL ) + nMed ) >> 5UL ); + } // if + + return ndist; +} // RGBDistance + +// ======================================================================= + +// ======================================================================= + diff --git a/vcl/aqua/source/gdi/salnativewidgets.cxx b/vcl/aqua/source/gdi/salnativewidgets.cxx new file mode 100644 index 000000000000..6e206977b5c4 --- /dev/null +++ b/vcl/aqua/source/gdi/salnativewidgets.cxx @@ -0,0 +1,1527 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "salconst.h" +#include "salgdi.h" +#include "salnativewidgets.h" +#include "saldata.hxx" +#include "salframe.h" + +#include "vcl/salnativewidgets.hxx" +#include "vcl/decoview.hxx" +#include "vcl/svapp.hxx" +#include "vcl/timer.hxx" + +#include "premac.h" +#include <Carbon/Carbon.h> +#include "postmac.h" + +class AquaBlinker : public Timer +{ + AquaSalFrame* mpFrame; + Rectangle maInvalidateRect; + + AquaBlinker( AquaSalFrame* pFrame, const Rectangle& rRect ) + : mpFrame( pFrame ), maInvalidateRect( rRect ) + { + mpFrame->maBlinkers.push_back( this ); + } + + public: + + static void Blink( AquaSalFrame*, const Rectangle&, int nTimeout = 80 ); + + virtual void Timeout() + { + Stop(); + if( AquaSalFrame::isAlive( mpFrame ) && mpFrame->mbShown ) + { + mpFrame->maBlinkers.remove( this ); + mpFrame->SendPaintEvent( &maInvalidateRect ); + } + delete this; + } +}; + +void AquaBlinker::Blink( AquaSalFrame* pFrame, const Rectangle& rRect, int nTimeout ) +{ + // prevent repeated paints from triggering themselves all the time + for( std::list< AquaBlinker* >::const_iterator it = pFrame->maBlinkers.begin(); + it != pFrame->maBlinkers.end(); ++it ) + { + if( (*it)->maInvalidateRect == rRect ) + return; + } + AquaBlinker* pNew = new AquaBlinker( pFrame, rRect ); + pNew->SetTimeout( nTimeout ); + pNew->Start(); +} + +ControlPart ImplgetCounterPart( ControlPart nPart ) +{ + ControlPart nCounterPart = 0; + switch (nPart) + { + case PART_BUTTON_UP: + nCounterPart = PART_BUTTON_DOWN; + break; + case PART_BUTTON_DOWN: + nCounterPart = PART_BUTTON_UP; + break; + case PART_BUTTON_LEFT: + nCounterPart = PART_BUTTON_RIGHT; + break; + case PART_BUTTON_RIGHT: + nCounterPart = PART_BUTTON_LEFT; + break; + } + return nCounterPart; +} + + +// Helper returns an HIRect + +static HIRect ImplGetHIRectFromRectangle(Rectangle aRect) +{ + HIRect aHIRect; + aHIRect.origin.x = static_cast<float>(aRect.Left()); + aHIRect.origin.y = static_cast<float>(aRect.Top()); + aHIRect.size.width = static_cast<float>(aRect.GetWidth()); + aHIRect.size.height = static_cast<float>(aRect.GetHeight()); + return aHIRect; +} + +static ThemeButtonValue ImplGetButtonValue( ButtonValue aButtonValue ) +{ + switch( aButtonValue ) + { + case BUTTONVALUE_ON: + return kThemeButtonOn; + break; + + case BUTTONVALUE_OFF: + return kThemeButtonOff; + break; + + case BUTTONVALUE_MIXED: + case BUTTONVALUE_DONTKNOW: + default: + return kThemeButtonMixed; + break; + } +} + +static bool AquaGetScrollRect( /* TODO: int nScreen, */ ControlPart nPart, + const Rectangle& rControlRect, Rectangle& rResultRect ) +{ + bool bRetVal = true; + rResultRect = rControlRect; + + switch( nPart ) + { + case PART_BUTTON_UP: + if( GetSalData()->mbIsScrollbarDoubleMax ) + rResultRect.Top() = rControlRect.Bottom() - 2*BUTTON_HEIGHT; + rResultRect.Bottom() = rResultRect.Top() + BUTTON_HEIGHT; + break; + + case PART_BUTTON_DOWN: + rResultRect.Top() = rControlRect.Bottom() - BUTTON_HEIGHT; + break; + + case PART_BUTTON_LEFT: + if( GetSalData()->mbIsScrollbarDoubleMax ) + rResultRect.Left() = rControlRect.Right() - 2*BUTTON_WIDTH; + rResultRect.Right() = rResultRect.Left() + BUTTON_WIDTH; + break; + + case PART_BUTTON_RIGHT: + rResultRect.Left() = rControlRect.Right() - BUTTON_WIDTH; + break; + + case PART_TRACK_HORZ_AREA: + rResultRect.Right() -= BUTTON_WIDTH + 1; + if( GetSalData()->mbIsScrollbarDoubleMax ) + rResultRect.Right() -= BUTTON_WIDTH; + else + rResultRect.Left() += BUTTON_WIDTH + 1; + break; + + case PART_TRACK_VERT_AREA: + rResultRect.Bottom() -= BUTTON_HEIGHT + 1; + if( GetSalData()->mbIsScrollbarDoubleMax ) + rResultRect.Bottom() -= BUTTON_HEIGHT; + else + rResultRect.Top() += BUTTON_HEIGHT + 1; + break; + case PART_THUMB_HORZ: + if( GetSalData()->mbIsScrollbarDoubleMax ) + { + rResultRect.Left() += 8; + rResultRect.Right() += 6; + } + else + { + rResultRect.Left() += 4; + rResultRect.Right() += 4; + } + break; + case PART_THUMB_VERT: + if( GetSalData()->mbIsScrollbarDoubleMax ) + { + rResultRect.Top() += 8; + rResultRect.Bottom() += 8; + } + else + { + rResultRect.Top() += 4; + rResultRect.Bottom() += 4; + } + break; + case PART_TRACK_HORZ_LEFT: + if( GetSalData()->mbIsScrollbarDoubleMax ) + rResultRect.Right() += 8; + else + rResultRect.Right() += 4; + break; + case PART_TRACK_HORZ_RIGHT: + if( GetSalData()->mbIsScrollbarDoubleMax ) + rResultRect.Left() += 6; + else + rResultRect.Left() += 4; + break; + case PART_TRACK_VERT_UPPER: + if( GetSalData()->mbIsScrollbarDoubleMax ) + rResultRect.Bottom() += 8; + else + rResultRect.Bottom() += 4; + break; + case PART_TRACK_VERT_LOWER: + if( GetSalData()->mbIsScrollbarDoubleMax ) + rResultRect.Top() += 8; + else + rResultRect.Top() += 4; + break; + default: + bRetVal = false; + } + + return bRetVal; +} + +/* + * IsNativeControlSupported() + * -------------------------- + * Returns TRUE if the platform supports native + * drawing of the control defined by nPart. + * + */ +BOOL AquaSalGraphics::IsNativeControlSupported( ControlType nType, ControlPart nPart ) +{ + bool bOk = FALSE; + + // Native controls are now defaults + // If you want to disable experimental native controls code, + // just set the environment variable SAL_NO_NWF to something + // and vcl controls will be used as default again. + + switch( nType ) + { + case CTRL_PUSHBUTTON: + case CTRL_RADIOBUTTON: + case CTRL_CHECKBOX: + case CTRL_LISTNODE: + if( nPart == PART_ENTIRE_CONTROL ) + return true; + break; + + case CTRL_SCROLLBAR: + if( nPart == PART_DRAW_BACKGROUND_HORZ || + nPart == PART_DRAW_BACKGROUND_VERT || + nPart == PART_ENTIRE_CONTROL || + nPart == HAS_THREE_BUTTONS ) + return true; + break; + + case CTRL_SLIDER: + if( nPart == PART_TRACK_HORZ_AREA || nPart == PART_TRACK_VERT_AREA ) + return true; + break; + + case CTRL_EDITBOX: + if( nPart == PART_ENTIRE_CONTROL || + nPart == HAS_BACKGROUND_TEXTURE ) + return true; + break; + + case CTRL_MULTILINE_EDITBOX: + if( nPart == PART_ENTIRE_CONTROL || + nPart == HAS_BACKGROUND_TEXTURE ) + return true; + break; + + case CTRL_SPINBOX: + if( nPart == PART_ENTIRE_CONTROL || + nPart == PART_ALL_BUTTONS || + nPart == HAS_BACKGROUND_TEXTURE ) + return true; + break; + + case CTRL_SPINBUTTONS: + return false; + break; + + case CTRL_COMBOBOX: + if( nPart == PART_ENTIRE_CONTROL || + nPart == HAS_BACKGROUND_TEXTURE ) + return true; + break; + + case CTRL_LISTBOX: + if( nPart == PART_ENTIRE_CONTROL || + nPart == PART_WINDOW || + nPart == HAS_BACKGROUND_TEXTURE || + nPart == PART_SUB_EDIT + ) + return true; + break; + + case CTRL_TAB_ITEM: + case CTRL_TAB_PANE: + case CTRL_TAB_BODY: // see vcl/source/window/tabpage.cxx + case CTRL_FIXEDBORDER: + if( nPart == PART_ENTIRE_CONTROL || + nPart == PART_TABS_DRAW_RTL || + nPart == HAS_BACKGROUND_TEXTURE ) + return true; + break; + + // when PART_BUTTON is used, toolbar icons are not highlighted when mouse rolls over. + // More Aqua compliant + case CTRL_TOOLBAR: + if( nPart == PART_ENTIRE_CONTROL || + nPart == PART_DRAW_BACKGROUND_HORZ || + nPart == PART_DRAW_BACKGROUND_VERT) + return true; + break; + + case CTRL_WINDOW_BACKGROUND: + if ( nPart == PART_BACKGROUND_WINDOW || + nPart == PART_BACKGROUND_DIALOG ) + return true; + break; + + case CTRL_MENUBAR: + if( nPart == PART_ENTIRE_CONTROL ) + return true; + break; + + case CTRL_TOOLTIP: // ** TO DO + #if 0 + if( nPart == PART_ENTIRE_CONTROL ) // we don't currently support the tooltip + return true; + #endif + break; + + case CTRL_MENU_POPUP: + if( nPart == PART_ENTIRE_CONTROL || + nPart == PART_MENU_ITEM || + nPart == PART_MENU_ITEM_CHECK_MARK || + nPart == PART_MENU_ITEM_RADIO_MARK) + return true; + break; + case CTRL_PROGRESS: + case CTRL_INTROPROGRESS: + if( nPart == PART_ENTIRE_CONTROL ) + return true; + break; + case CTRL_FRAME: + if( nPart == PART_BORDER ) + return true; + break; + case CTRL_LISTNET: + if( nPart == PART_ENTIRE_CONTROL ) + return true; + break; + } + + return bOk; +} + +/* + * HitTestNativeControl() + * + * If the return value is TRUE, bIsInside contains information whether + * aPos was or was not inside the native widget specified by the + * nType/nPart combination. + */ +BOOL AquaSalGraphics::hitTestNativeControl( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, + const Point& rPos, BOOL& rIsInside ) +{ + if ( nType == CTRL_SCROLLBAR ) + { + Rectangle aRect; + bool bValid = AquaGetScrollRect( /* TODO: m_nScreen */ nPart, rControlRegion, aRect ); + rIsInside = bValid ? aRect.IsInside( rPos ) : FALSE; + if( GetSalData()->mbIsScrollbarDoubleMax ) + { + // in double max mode the actual trough is a little smaller than the track + // there is some visual filler that is not sensitive + if( bValid && rIsInside ) + { + if( nPart == PART_TRACK_HORZ_AREA ) + { + // the left 4 pixels are not hit sensitive + if( rPos.X() - aRect.Left() < 4 ) + rIsInside = FALSE; + } + else if( nPart == PART_TRACK_VERT_AREA ) + { + // the top 4 pixels are not hit sensitive + if( rPos.Y() - aRect.Top() < 4 ) + rIsInside = FALSE; + } + } + } + return bValid; + } // CTRL_SCROLLBAR + + return FALSE; +} + +/* + kThemeStateInactive = 0, + kThemeStateActive = 1, + kThemeStatePressed = 2, + kThemeStateRollover = 6, + kThemeStateUnavailable = 7, + kThemeStateUnavailableInactive = 8 + kThemeStatePressedUp = 2, + kThemeStatePressedDown = 3 + +#define CTRL_STATE_ENABLED 0x0001 +#define CTRL_STATE_FOCUSED 0x0002 +#define CTRL_STATE_PRESSED 0x0004 +#define CTRL_STATE_ROLLOVER 0x0008 +#define CTRL_STATE_HIDDEN 0x0010 +#define CTRL_STATE_DEFAULT 0x0020 +#define CTRL_STATE_SELECTED 0x0040 +#define CTRL_CACHING_ALLOWED 0x8000 // set when the control is completely visible (i.e. not clipped) +*/ +UInt32 AquaSalGraphics::getState( ControlState nState ) +{ + bool bDrawActive = mpFrame ? ([mpFrame->getWindow() isKeyWindow] ? true : false) : true; + if( (nState & CTRL_STATE_ENABLED) == 0 || ! bDrawActive ) + { + if( (nState & CTRL_STATE_HIDDEN) == 0 ) + return kThemeStateInactive; + else + return kThemeStateUnavailableInactive; + } + + if( (nState & CTRL_STATE_HIDDEN) != 0 ) + return kThemeStateUnavailable; + + if( (nState & CTRL_STATE_PRESSED) != 0 ) + return kThemeStatePressed; + + return kThemeStateActive; +} + +UInt32 AquaSalGraphics::getTrackState( ControlState nState ) +{ + bool bDrawActive = mpFrame ? ([mpFrame->getWindow() isKeyWindow] ? true : false) : true; + if( (nState & CTRL_STATE_ENABLED) == 0 || ! bDrawActive ) + return kThemeTrackInactive; + + return kThemeTrackActive; +} + +/* + * DrawNativeControl() + * + * Draws the requested control described by nPart/nState. + * + * rControlRegion: The bounding region of the complete control in VCL frame coordinates. + * aValue: An optional value (tristate/numerical/string) + * aCaption: A caption or title string (like button text etc) + */ +BOOL AquaSalGraphics::drawNativeControl(ControlType nType, + ControlPart nPart, + const Rectangle& rControlRegion, + ControlState nState, + const ImplControlValue& aValue, + const rtl::OUString& aCaption ) +{ + BOOL bOK = FALSE; + + if( ! CheckContext() ) + return false; + + CGContextSaveGState( mrContext ); + + Rectangle buttonRect = rControlRegion; + HIRect rc = ImplGetHIRectFromRectangle(buttonRect); + + /** Scrollbar parts code equivalent ** + PART_BUTTON_UP 101 + PART_BUTTON_DOWN 102 + PART_THUMB_VERT 211 + PART_TRACK_VERT_UPPER 201 + PART_TRACK_VERT_LOWER 203 + + PART_DRAW_BACKGROUND_HORZ 1000 + PART_DRAW_BACKGROUND_VERT 1001 + **/ + + switch( nType ) + { + + case CTRL_COMBOBOX: + if ( nPart == HAS_BACKGROUND_TEXTURE || + nPart == PART_ENTIRE_CONTROL ) + { + HIThemeButtonDrawInfo aComboInfo; + aComboInfo.version = 0; + aComboInfo.kind = kThemeComboBox; + aComboInfo.state = getState( nState ); + aComboInfo.value = kThemeButtonOn; + aComboInfo.adornment = kThemeAdornmentNone; + + if( (nState & CTRL_STATE_FOCUSED) != 0 ) + aComboInfo.adornment |= kThemeAdornmentFocus; + + HIThemeDrawButton(&rc, &aComboInfo, mrContext, kHIThemeOrientationNormal,&rc); + bOK = true; + } + break; + + case CTRL_FIXEDBORDER: + case CTRL_TOOLBAR: + { + HIThemeMenuItemDrawInfo aMenuItemDrawInfo; + aMenuItemDrawInfo.version = 0; + aMenuItemDrawInfo.state = kThemeMenuActive; + aMenuItemDrawInfo.itemType = kThemeMenuItemHierBackground; + HIThemeDrawMenuItem(&rc,&rc,&aMenuItemDrawInfo,mrContext,kHIThemeOrientationNormal,NULL); + bOK = true; + } + break; + + case CTRL_WINDOW_BACKGROUND: + { + HIThemeBackgroundDrawInfo aThemeBackgroundInfo; + aThemeBackgroundInfo.version = 0; + aThemeBackgroundInfo.state = getState( nState ); + aThemeBackgroundInfo.kind = kThemeBrushDialogBackgroundInactive; + // FIXME: without this magical offset there is a 2 pixel black border on the right and bottom + rc.size.width += 2; + rc.size.height += 2; + + HIThemeApplyBackground( &rc, &aThemeBackgroundInfo, mrContext, kHIThemeOrientationNormal); + CGContextFillRect( mrContext, rc ); + bOK = true; + } + break; + + case CTRL_MENUBAR: + case CTRL_MENU_POPUP: + { + if ((nPart == PART_ENTIRE_CONTROL) || (nPart == PART_MENU_ITEM )|| (nPart == HAS_BACKGROUND_TEXTURE )) + { + // FIXME: without this magical offset there is a 2 pixel black border on the right + rc.size.width += 2; + + HIThemeMenuDrawInfo aMenuInfo; + aMenuInfo.version = 0; + aMenuInfo.menuType = kThemeMenuTypePullDown; + + HIThemeMenuItemDrawInfo aMenuItemDrawInfo; + // the Aqua grey theme when the item is selected is drawn here. + aMenuItemDrawInfo.itemType = kThemeMenuItemPlain; + + if ((nPart == PART_MENU_ITEM ) && (nState & CTRL_STATE_SELECTED)) + { + // the blue theme when the item is selected is drawn here. + aMenuItemDrawInfo.state = kThemeMenuSelected; + } + else + { + // normal color for non selected item + aMenuItemDrawInfo.state = kThemeMenuActive; + } + + // repaints the background of the pull down menu + HIThemeDrawMenuBackground(&rc,&aMenuInfo,mrContext,kHIThemeOrientationNormal); + + // repaints the item either blue (selected) and/or Aqua grey (active only) + HIThemeDrawMenuItem(&rc,&rc,&aMenuItemDrawInfo,mrContext,kHIThemeOrientationNormal,&rc); + + bOK = true; + } + else if(( nPart == PART_MENU_ITEM_CHECK_MARK )||( nPart == PART_MENU_ITEM_RADIO_MARK )) { + if( nState & CTRL_STATE_PRESSED ) {//checked, else it is not displayed (see vcl/source/window/menu.cxx) + HIThemeTextInfo aTextInfo; + aTextInfo.version = 0; + aTextInfo.state = ((nState & CTRL_STATE_ENABLED)==0) ? kThemeStateInactive: kThemeStateActive; + aTextInfo.fontID = kThemeMenuItemMarkFont; + aTextInfo.horizontalFlushness=kHIThemeTextHorizontalFlushCenter; + aTextInfo.verticalFlushness=kHIThemeTextVerticalFlushTop; + aTextInfo.options=kHIThemeTextBoxOptionNone; + aTextInfo.truncationPosition=kHIThemeTextTruncationNone; + //aTextInfo.truncationMaxLines unused because of kHIThemeTextTruncationNone + + if( nState & CTRL_STATE_SELECTED) aTextInfo.state = kThemeStatePressed; //item highlighted + + UniChar mark=( nPart == PART_MENU_ITEM_CHECK_MARK ) ? kCheckUnicode: kBulletUnicode;//0x2713; + CFStringRef cfString = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, &mark, 1, kCFAllocatorNull); + HIThemeDrawTextBox(cfString, &rc, &aTextInfo, mrContext, kHIThemeOrientationNormal); + if (cfString) + CFRelease(cfString); + + bOK = true; + } + } + } + break; + + case CTRL_PUSHBUTTON: + { + // [ FIXME] : instead of use a value, vcl can retrieve corect values on the fly (to be implemented) + const int PB_Mini_Height = 15; + const int PB_Norm_Height = 21; + + HIThemeButtonDrawInfo aPushInfo; + aPushInfo.version = 0; + + // no animation + aPushInfo.animation.time.start = 0; + aPushInfo.animation.time.current = 0; + PushButtonValue* pPBVal = aValue.getType() == CTRL_PUSHBUTTON ? (PushButtonValue*)&aValue : NULL; + int nPaintHeight = static_cast<int>(rc.size.height); + + if( pPBVal && pPBVal->mbBevelButton ) + { + aPushInfo.kind = kThemeRoundedBevelButton; + } + else if( rc.size.height <= PB_Norm_Height ) + { + aPushInfo.kind = kThemePushButtonMini; + nPaintHeight = PB_Mini_Height; + } + else if( pPBVal->mbSingleLine || rc.size.height < (PB_Norm_Height + PB_Norm_Height/2) ) + { + aPushInfo.kind = kThemePushButtonNormal; + nPaintHeight = PB_Norm_Height; + + // avoid clipping when focused + rc.origin.x += FOCUS_RING_WIDTH/2; + rc.size.width -= FOCUS_RING_WIDTH; + + if( (nState & CTRL_STATE_DEFAULT) != 0 ) + { + AquaBlinker::Blink( mpFrame, buttonRect ); + // show correct animation phase + aPushInfo.animation.time.current = CFAbsoluteTimeGetCurrent(); + } + } + else + aPushInfo.kind = kThemeBevelButton; + + // translate the origin for controls with fixed paint height + // so content ends up somewhere sensible + int delta_y = static_cast<int>(rc.size.height) - nPaintHeight; + rc.origin.y += delta_y/2; + + aPushInfo.state = getState( nState ); + aPushInfo.value = ImplGetButtonValue( aValue.getTristateVal() ); + + aPushInfo.adornment = (( nState & CTRL_STATE_DEFAULT ) != 0) ? + kThemeAdornmentDefault : + kThemeAdornmentNone; + if( (nState & CTRL_STATE_FOCUSED) != 0 ) + aPushInfo.adornment |= kThemeAdornmentFocus; + + HIThemeDrawButton( &rc, &aPushInfo, mrContext, kHIThemeOrientationNormal, NULL ); + bOK = true; + } + break; + + case CTRL_RADIOBUTTON: + case CTRL_CHECKBOX: + { + HIThemeButtonDrawInfo aInfo; + aInfo.version = 0; + switch( nType ) + { + case CTRL_RADIOBUTTON: if(rc.size.width >= BUTTON_HEIGHT) aInfo.kind = kThemeRadioButton; + else aInfo.kind = kThemeSmallRadioButton; + break; + case CTRL_CHECKBOX: if(rc.size.width >= BUTTON_HEIGHT) aInfo.kind = kThemeCheckBox; + else aInfo.kind = kThemeSmallCheckBox; + break; + } + + aInfo.state = getState( nState ); + + ButtonValue aButtonValue = aValue.getTristateVal(); + aInfo.value = ImplGetButtonValue( aButtonValue ); + + aInfo.adornment = (( nState & CTRL_STATE_DEFAULT ) != 0) ? + kThemeAdornmentDefault : + kThemeAdornmentNone; + if( (nState & CTRL_STATE_FOCUSED) != 0 ) + aInfo.adornment |= kThemeAdornmentFocus; + HIThemeDrawButton( &rc, &aInfo, mrContext, kHIThemeOrientationNormal, NULL ); + bOK = true; + } + break; + + case CTRL_LISTNODE: + { + ButtonValue aButtonValue = aValue.getTristateVal(); + + if( Application::GetSettings().GetLayoutRTL() && aButtonValue == BUTTONVALUE_OFF ) + { + // FIXME: a value of kThemeDisclosureLeft + // should draw a theme compliant left disclosure triangle + // sadly this does not seem to work, so we'll draw a left + // grey equilateral triangle here ourselves. + // Perhaps some other HIThemeButtonDrawInfo setting would do the trick ? + + CGContextSetShouldAntialias( mrContext, true ); + float aGrey[] = { 0.45, 0.45, 0.45, 1.0 }; + CGContextSetFillColor( mrContext, aGrey ); + CGContextBeginPath( mrContext ); + float x = rc.origin.x + rc.size.width; + float y = rc.origin.y; + CGContextMoveToPoint( mrContext, x, y ); + y += rc.size.height; + CGContextAddLineToPoint( mrContext, x, y ); + x -= rc.size.height * 0.866; // cos( 30 degree ) is approx. 0.866 + y -= rc.size.height/2; + CGContextAddLineToPoint( mrContext, x, y ); + CGContextDrawPath( mrContext, kCGPathEOFill ); + } + else + { + HIThemeButtonDrawInfo aInfo; + aInfo.version = 0; + aInfo.kind = kThemeDisclosureTriangle; + aInfo.value = kThemeDisclosureRight; + aInfo.state = getState( nState ); + + aInfo.adornment = kThemeAdornmentNone; + + switch( aButtonValue ) { + case BUTTONVALUE_ON: aInfo.value = kThemeDisclosureDown;//expanded + break; + case BUTTONVALUE_OFF: + // FIXME: this should have drawn a theme compliant disclosure triangle + // (see above) + if( Application::GetSettings().GetLayoutRTL() ) + { + aInfo.value = kThemeDisclosureLeft;//collapsed, RTL + } + break; + case BUTTONVALUE_DONTKNOW: //what to do? + default: + break; + } + + HIThemeDrawButton( &rc, &aInfo, mrContext, kHIThemeOrientationNormal, NULL ); + } + bOK = true; + } + break; + + case CTRL_PROGRESS: + case CTRL_INTROPROGRESS: + { + long nProgressWidth = aValue.getNumericVal(); + HIThemeTrackDrawInfo aTrackInfo; + aTrackInfo.version = 0; + aTrackInfo.kind = (rc.size.height > 10) ? kThemeProgressBarLarge : kThemeProgressBarMedium; + aTrackInfo.bounds = rc; + aTrackInfo.min = 0; + aTrackInfo.max = static_cast<SInt32>(rc.size.width); + aTrackInfo.value = nProgressWidth; + aTrackInfo.reserved = 0; + aTrackInfo.bounds.origin.y -= 2; // FIXME: magic for shadow + aTrackInfo.bounds.size.width -= 2; // FIXME: magic for shadow + aTrackInfo.attributes = kThemeTrackHorizontal; + if( Application::GetSettings().GetLayoutRTL() ) + aTrackInfo.attributes |= kThemeTrackRightToLeft; + aTrackInfo.enableState = getTrackState( nState ); + // the intro bitmap never gets key anyway; we want to draw that enabled + if( nType == CTRL_INTROPROGRESS ) + aTrackInfo.enableState = kThemeTrackActive; + aTrackInfo.filler1 = 0; + aTrackInfo.trackInfo.progress.phase = static_cast<UInt8>(CFAbsoluteTimeGetCurrent()*10.0); + + HIThemeDrawTrack( &aTrackInfo, NULL, mrContext, kHIThemeOrientationNormal ); + bOK = true; + } + break; + + case CTRL_SLIDER: + { + SliderValue* pSLVal = (SliderValue*)&aValue; + + HIThemeTrackDrawInfo aTrackDraw; + aTrackDraw.kind = kThemeSliderMedium; + if( nPart == PART_TRACK_HORZ_AREA || nPart == PART_TRACK_VERT_AREA ) + { + aTrackDraw.bounds = rc; + aTrackDraw.min = pSLVal->mnMin; + aTrackDraw.max = pSLVal->mnMax;; + aTrackDraw.value = pSLVal->mnCur; + aTrackDraw.reserved = 0; + aTrackDraw.attributes = kThemeTrackShowThumb; + if( nPart == PART_TRACK_HORZ_AREA ) + aTrackDraw.attributes |= kThemeTrackHorizontal; + aTrackDraw.enableState = (nState & CTRL_STATE_ENABLED) + ? kThemeTrackActive : kThemeTrackInactive; + + SliderTrackInfo aSlideInfo; + aSlideInfo.thumbDir = kThemeThumbUpward; + aSlideInfo.pressState = 0; + aTrackDraw.trackInfo.slider = aSlideInfo; + + HIThemeDrawTrack( &aTrackDraw, NULL, mrContext, kHIThemeOrientationNormal ); + bOK = true; + } + } + break; + + case CTRL_SCROLLBAR: + { + ScrollbarValue* pScrollbarVal = (ScrollbarValue *)&aValue; + + if( nPart == PART_DRAW_BACKGROUND_VERT || + nPart == PART_DRAW_BACKGROUND_HORZ ) + { + HIThemeTrackDrawInfo aTrackDraw; + aTrackDraw.kind = kThemeMediumScrollBar; + // FIXME: the scrollbar length must be adjusted + if (nPart == PART_DRAW_BACKGROUND_VERT) + rc.size.height += 2; + else + rc.size.width += 2; + + aTrackDraw.bounds = rc; + aTrackDraw.min = pScrollbarVal->mnMin; + aTrackDraw.max = pScrollbarVal->mnMax - pScrollbarVal->mnVisibleSize; + aTrackDraw.value = pScrollbarVal->mnCur; + aTrackDraw.reserved = 0; + aTrackDraw.attributes = kThemeTrackShowThumb; + if( nPart == PART_DRAW_BACKGROUND_HORZ ) + aTrackDraw.attributes |= kThemeTrackHorizontal; + aTrackDraw.enableState = getTrackState( nState ); + + ScrollBarTrackInfo aScrollInfo; + aScrollInfo.viewsize = pScrollbarVal->mnVisibleSize; + aScrollInfo.pressState = 0; + + if ( pScrollbarVal->mnButton1State & CTRL_STATE_ENABLED ) + { + if ( pScrollbarVal->mnButton1State & CTRL_STATE_PRESSED ) + aScrollInfo.pressState = kThemeTopOutsideArrowPressed; + } + + if ( pScrollbarVal->mnButton2State & CTRL_STATE_ENABLED ) + { + if ( pScrollbarVal->mnButton2State & CTRL_STATE_PRESSED ) + aScrollInfo.pressState = kThemeBottomOutsideArrowPressed; + } + + if ( pScrollbarVal->mnThumbState & CTRL_STATE_ENABLED ) + { + if ( pScrollbarVal->mnThumbState & CTRL_STATE_PRESSED ) + aScrollInfo.pressState = kThemeThumbPressed; + } + + aTrackDraw.trackInfo.scrollbar = aScrollInfo; + + HIThemeDrawTrack( &aTrackDraw, NULL, mrContext, kHIThemeOrientationNormal ); + bOK = true; + } + } + break; + +//#define OLD_TAB_STYLE +#ifdef OLD_TAB_STYLE + case CTRL_TAB_PANE: + { + HIThemeTabPaneDrawInfo aTabPaneDrawInfo; + aTabPaneDrawInfo.version = 0; + aTabPaneDrawInfo.state = kThemeStateActive; + aTabPaneDrawInfo.direction=kThemeTabNorth; + aTabPaneDrawInfo.size=kHIThemeTabSizeNormal; + + //the border is outside the rect rc for Carbon + //but for VCL it should be inside + rc.origin.x+=1; + rc.size.width-=2; + + HIThemeDrawTabPane(&rc, &aTabPaneDrawInfo, mrContext, kHIThemeOrientationNormal); + bOK = true; + } + break; + + case CTRL_TAB_ITEM: + { + HIThemeTabDrawInfo aTabItemDrawInfo; + aTabItemDrawInfo.version=0; + aTabItemDrawInfo.style=kThemeTabNonFront; + aTabItemDrawInfo.direction=kThemeTabNorth; + aTabItemDrawInfo.size=kHIThemeTabSizeNormal; + aTabItemDrawInfo.adornment=kHIThemeTabAdornmentNone; + + if(nState & CTRL_STATE_SELECTED) { + aTabItemDrawInfo.style=kThemeTabFront; + } + if(nState & CTRL_STATE_FOCUSED) { + aTabItemDrawInfo.adornment=kHIThemeTabAdornmentFocus; + } + + /*if(rc.size.height>=TAB_HEIGHT_NORMAL) rc.size.height=TAB_HEIGHT_NORMAL; + else if(rc.size.height>=TAB_HEIGHT_SMALL) rc.size.height=TAB_HEIGHT_SMALL; + else rc.size.height=TAB_HEIGHT_MINI;*/ + //now we only use the default size + rc.size.height=TAB_HEIGHT_NORMAL; + + HIThemeDrawTab(&rc, &aTabItemDrawInfo, mrContext, kHIThemeOrientationNormal, &rc ); + + bOK=true; + } + break; +#else + case CTRL_TAB_PANE: + { + HIThemeTabPaneDrawInfo aTabPaneDrawInfo; + aTabPaneDrawInfo.version = 1; + aTabPaneDrawInfo.state = kThemeStateActive; + aTabPaneDrawInfo.direction=kThemeTabNorth; + aTabPaneDrawInfo.size=kHIThemeTabSizeNormal; + aTabPaneDrawInfo.kind=kHIThemeTabKindNormal; + + //the border is outside the rect rc for Carbon + //but for VCL it should be inside + rc.origin.x+=1; + rc.origin.y-=TAB_HEIGHT_NORMAL/2; + rc.size.height+=TAB_HEIGHT_NORMAL/2; + rc.size.width-=2; + + HIThemeDrawTabPane(&rc, &aTabPaneDrawInfo, mrContext, kHIThemeOrientationNormal); + + bOK = true; + } + break; + + case CTRL_TAB_ITEM: + { + HIThemeTabDrawInfo aTabItemDrawInfo; + aTabItemDrawInfo.version=1; + aTabItemDrawInfo.style=kThemeTabNonFront; + aTabItemDrawInfo.direction=kThemeTabNorth; + aTabItemDrawInfo.size=kHIThemeTabSizeNormal; + aTabItemDrawInfo.adornment=kHIThemeTabAdornmentTrailingSeparator; + //State + if(nState & CTRL_STATE_SELECTED) { + aTabItemDrawInfo.style=kThemeTabFront; + } + if(nState & CTRL_STATE_FOCUSED) { + aTabItemDrawInfo.adornment|=kHIThemeTabAdornmentFocus; + } + + //first, last or middle tab + aTabItemDrawInfo.position=kHIThemeTabPositionMiddle; + + TabitemValue* pTabValue = (TabitemValue *) &aValue; + unsigned int nAlignment = pTabValue->mnAlignment; + //TABITEM_LEFTALIGNED (and TABITEM_RIGHTALIGNED) for the leftmost (or rightmost) tab + //when there are several lines of tabs because there is only one first tab and one + //last tab and TABITEM_FIRST_IN_GROUP (and TABITEM_LAST_IN_GROUP) because when the + //line width is different from window width, there may not be TABITEM_RIGHTALIGNED + if( ( (nAlignment & TABITEM_LEFTALIGNED)&&(nAlignment & TABITEM_RIGHTALIGNED) ) || + ( (nAlignment & TABITEM_FIRST_IN_GROUP)&&(nAlignment & TABITEM_LAST_IN_GROUP) ) + ) //tab alone + aTabItemDrawInfo.position=kHIThemeTabPositionOnly; + else if((nAlignment & TABITEM_LEFTALIGNED)||(nAlignment & TABITEM_FIRST_IN_GROUP)) + aTabItemDrawInfo.position=kHIThemeTabPositionFirst; + else if((nAlignment & TABITEM_RIGHTALIGNED)||(nAlignment & TABITEM_LAST_IN_GROUP)) + aTabItemDrawInfo.position=kHIThemeTabPositionLast; + + //support for RTL + //see issue 79748 + if( Application::GetSettings().GetLayoutRTL() ) { + if( aTabItemDrawInfo.position == kHIThemeTabPositionFirst ) + aTabItemDrawInfo.position = kHIThemeTabPositionLast; + else if( aTabItemDrawInfo.position == kHIThemeTabPositionLast ) + aTabItemDrawInfo.position = kHIThemeTabPositionFirst; + } + + rc.size.width+=2;//because VCL has 2 empty pixels between 2 tabs + rc.origin.x-=1; + + HIThemeDrawTab(&rc, &aTabItemDrawInfo, mrContext, kHIThemeOrientationNormal, &rc ); + + bOK=true; + } + break; +#endif + + case CTRL_LISTBOX: + switch( nPart) + { + case PART_ENTIRE_CONTROL: + case PART_BUTTON_DOWN: + { + HIThemeButtonDrawInfo aListInfo; + aListInfo.version = 0; + aListInfo.kind = kThemePopupButton; + aListInfo.state = getState( nState );//kThemeStateInactive -> greyed + aListInfo.value = kThemeButtonOn; + + aListInfo.adornment = kThemeAdornmentDefault; + if( (nState & CTRL_STATE_FOCUSED) != 0 ) + aListInfo.adornment |= kThemeAdornmentFocus; + + HIThemeDrawButton(&rc, &aListInfo, mrContext, kHIThemeOrientationNormal,&rc); + bOK = true; + break; + } + case PART_WINDOW: + { + HIThemeFrameDrawInfo aTextDrawInfo; + aTextDrawInfo.version=0; + aTextDrawInfo.kind=kHIThemeFrameTextFieldSquare; + aTextDrawInfo.state=getState( nState ); + aTextDrawInfo.isFocused=false; + + rc.size.width+=1;//else there's a white space because aqua theme hasn't a 3D border + rc.size.height+=1; + HIThemeDrawFrame(&rc, &aTextDrawInfo, mrContext, kHIThemeOrientationNormal); + + if(nState & CTRL_STATE_FOCUSED) HIThemeDrawFocusRect(&rc, true, mrContext, kHIThemeOrientationNormal); + + bOK=true; + break; + } + } + break; + + case CTRL_EDITBOX: + case CTRL_MULTILINE_EDITBOX: + { + HIThemeFrameDrawInfo aTextDrawInfo; + aTextDrawInfo.version=0; + aTextDrawInfo.kind=kHIThemeFrameTextFieldSquare; + aTextDrawInfo.state=getState( nState ); + aTextDrawInfo.isFocused=false; + + rc.size.width += 1; // else there may be a white space because aqua theme hasn't a 3D border + // change rc so that the frame will encompass only the content region + // see counterpart in GetNativeControlRegion + rc.size.width += 2; + rc.size.height += 2; + + //CGContextSetFillColorWithColor + CGContextFillRect (mrContext, CGRectMake(rc.origin.x, rc.origin.y, rc.size.width, rc.size.height)); + //fill a white background, because drawFrame only draws the border + + HIThemeDrawFrame(&rc, &aTextDrawInfo, mrContext, kHIThemeOrientationNormal); + + if(nState & CTRL_STATE_FOCUSED) HIThemeDrawFocusRect(&rc, true, mrContext, kHIThemeOrientationNormal); + + bOK=true; + } + break; + + case CTRL_SPINBOX: + { + if(nPart == PART_ENTIRE_CONTROL) + { + //text field: + HIThemeFrameDrawInfo aTextDrawInfo; + aTextDrawInfo.version=0; + aTextDrawInfo.kind=kHIThemeFrameTextFieldSquare; + aTextDrawInfo.state=getState( nState ); + aTextDrawInfo.isFocused=false; + + //rc.size.width contains the full size of the spinbox ie textfield + button + //so we remove the button width and the space between the button and the textfield + rc.size.width -= SPIN_BUTTON_SPACE + SPIN_BUTTON_WIDTH + 2*FOCUS_RING_WIDTH; + rc.origin.x += FOCUS_RING_WIDTH; + rc.origin.y += FOCUS_RING_WIDTH; + + //CGContextSetFillColorWithColor + CGContextFillRect (mrContext, CGRectMake(rc.origin.x, rc.origin.y, rc.size.width, rc.size.height)); + //fill a white background, because drawFrame only draws the border + + HIThemeDrawFrame(&rc, &aTextDrawInfo, mrContext, kHIThemeOrientationNormal); + + if(nState & CTRL_STATE_FOCUSED) HIThemeDrawFocusRect(&rc, true, mrContext, kHIThemeOrientationNormal); + + //buttons: + SpinbuttonValue* pSpinButtonVal = (SpinbuttonValue *)&aValue; + ControlState nUpperState = CTRL_STATE_ENABLED;//state of the upper button + ControlState nLowerState = CTRL_STATE_ENABLED;//and of the lower button + if(pSpinButtonVal) {//pSpinButtonVal is sometimes null + nUpperState = (ControlState) pSpinButtonVal->mnUpperState; + nLowerState = (ControlState) pSpinButtonVal->mnLowerState; + } + + if( pSpinButtonVal ) + { + HIThemeButtonDrawInfo aSpinInfo; + aSpinInfo.kind = kThemeIncDecButton; + aSpinInfo.state = kThemeStateActive; + if(nUpperState & CTRL_STATE_PRESSED) + aSpinInfo.state = kThemeStatePressedUp; + else if(nLowerState & CTRL_STATE_PRESSED) + aSpinInfo.state = kThemeStatePressedDown; + else if((nUpperState & ~CTRL_STATE_ENABLED)||(nLowerState & ~CTRL_STATE_ENABLED)) + aSpinInfo.state = kThemeStateInactive; + else if((nUpperState & CTRL_STATE_ROLLOVER)||(nLowerState & CTRL_STATE_ROLLOVER)) + aSpinInfo.state = kThemeStateRollover; + + Rectangle aSpinRect( pSpinButtonVal->maUpperRect ); + aSpinRect.Union( pSpinButtonVal->maLowerRect ); + HIRect buttonRc = ImplGetHIRectFromRectangle(aSpinRect); + + // FIXME: without this fuzz factor there is some unwanted clipping + if( Application::GetSettings().GetLayoutRTL() ) + buttonRc.origin.x -= FOCUS_RING_WIDTH - CLIP_FUZZ; + else + buttonRc.origin.x += FOCUS_RING_WIDTH + CLIP_FUZZ; + + switch( aValue.getTristateVal() ) + { + case BUTTONVALUE_ON: aSpinInfo.value = kThemeButtonOn; + break; + case BUTTONVALUE_OFF: aSpinInfo.value = kThemeButtonOff; + break; + case BUTTONVALUE_MIXED: + case BUTTONVALUE_DONTKNOW: + default: aSpinInfo.value = kThemeButtonMixed; + break; + } + + aSpinInfo.adornment = ( ((nUpperState & CTRL_STATE_DEFAULT) != 0 ) || + ((nLowerState & CTRL_STATE_DEFAULT) != 0 )) ? + kThemeAdornmentDefault : + kThemeAdornmentNone; + if( ((nUpperState & CTRL_STATE_FOCUSED) != 0 ) || ((nLowerState & CTRL_STATE_FOCUSED) != 0 )) + aSpinInfo.adornment |= kThemeAdornmentFocus; + + HIThemeDrawButton( &buttonRc, &aSpinInfo, mrContext, kHIThemeOrientationNormal, NULL ); + } + + bOK=true; + } + + } + break; + + case CTRL_FRAME: + { + USHORT nStyle = aValue.getNumericVal(); + if( nPart == PART_BORDER ) { + if(!( nStyle & FRAME_DRAW_MENU ) && !(nStyle & FRAME_DRAW_WINDOWBORDER) ) + { + // #i84756# strange effects start to happen when HIThemeDrawFrame + // meets the border of the window. These can be avoided by clipping + // to the boundary of the frame + if( rc.origin.y + rc.size.height >= mpFrame->maGeometry.nHeight-3 ) + { + CGMutablePathRef rPath = CGPathCreateMutable(); + CGPathAddRect( rPath, NULL, CGRectMake( 0, 0, mpFrame->maGeometry.nWidth-1, mpFrame->maGeometry.nHeight-1 ) ); + + CGContextBeginPath( mrContext ); + CGContextAddPath( mrContext, rPath ); + CGContextClip( mrContext ); + CGPathRelease( rPath ); + } + + HIThemeFrameDrawInfo aTextDrawInfo; + aTextDrawInfo.version=0; + aTextDrawInfo.kind=kHIThemeFrameListBox; + aTextDrawInfo.state=kThemeStateActive; + aTextDrawInfo.isFocused=false; + + HIThemeDrawFrame(&rc, &aTextDrawInfo, mrContext, kHIThemeOrientationNormal); + + bOK=true; + } + } + } + break; + + case CTRL_LISTNET: + { + //do nothing as there isn't net for listviews on macos + bOK=true; + } + break; + + } + + CGContextRestoreGState( mrContext ); + + /* #i90291# in most cases invalidating the whole control region instead + of just the unclipped part of it is sufficient (and probably faster). + However for the window background we should not unnecessarily enlarge + the really changed rectangle since the difference is usually quite high + (the background is always drawn as a whole since we don't know anything + about its possible contents) + */ + if( nType == CTRL_WINDOW_BACKGROUND ) + { + CGRect aRect = { { 0, 0 }, { 0, 0 } }; + if( mxClipPath ) + aRect = CGPathGetBoundingBox( mxClipPath ); + if( aRect.size.width != 0 && aRect.size.height != 0 ) + buttonRect.Intersection( Rectangle( Point( static_cast<long int>(aRect.origin.x), + static_cast<long int>(aRect.origin.y) ), + Size( static_cast<long int>(aRect.size.width), + static_cast<long int>(aRect.size.height) ) ) ); + } + + RefreshRect( buttonRect.Left(), buttonRect.Top(), buttonRect.GetWidth(), buttonRect.GetHeight() ); + + return bOK; +} + +/* + * DrawNativeControlText() + * + * OPTIONAL. Draws the requested text for the control described by nPart/nState. + * Used if text not drawn by DrawNativeControl(). + * + * rControlRegion: The bounding region of the complete control in VCL frame coordinates. + * aValue: An optional value (tristate/numerical/string) + * aCaption: A caption or title string (like button text etc) + */ +BOOL AquaSalGraphics::drawNativeControlText( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, + ControlState nState, const ImplControlValue& aValue, + const rtl::OUString& aCaption ) +{ + return( FALSE ); +} + + +/* + * GetNativeControlRegion() + * + * If the return value is TRUE, rNativeBoundingRegion + * contains the true bounding region covered by the control + * including any adornment, while rNativeContentRegion contains the area + * within the control that can be safely drawn into without drawing over + * the borders of the control. + * + * rControlRegion: The bounding region of the control in VCL frame coordinates. + * aValue: An optional value (tristate/numerical/string) + * aCaption: A caption or title string (like button text etc) + */ +BOOL AquaSalGraphics::getNativeControlRegion( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, ControlState nState, + const ImplControlValue& aValue, const rtl::OUString& aCaption, + Rectangle &rNativeBoundingRegion, Rectangle &rNativeContentRegion ) + +{ + BOOL toReturn = FALSE; + + Rectangle aCtrlBoundRect( rControlRegion ); + short x = aCtrlBoundRect.Left(); + short y = aCtrlBoundRect.Top(); + short w, h; + + sal_uInt8 nBorderCleanup = 0; + + switch (nType) + { + case CTRL_SLIDER: + { + if( nPart == PART_THUMB_HORZ ) + { + w = 19; // taken from HIG + h = aCtrlBoundRect.GetHeight(); + rNativeBoundingRegion = rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) ); + toReturn = true; + } + else if( nPart == PART_THUMB_VERT ) + { + w = aCtrlBoundRect.GetWidth(); + h = 18; // taken from HIG + rNativeBoundingRegion = rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) ); + toReturn = true; + } + } + break; + + case CTRL_SCROLLBAR: + { + Rectangle aRect; + if( AquaGetScrollRect( /* m_nScreen */ nPart, aCtrlBoundRect, aRect ) ) + { + toReturn = TRUE; + rNativeBoundingRegion = aRect; + rNativeContentRegion = aRect; + } + } + break; + + case CTRL_PUSHBUTTON: + case CTRL_RADIOBUTTON: + case CTRL_CHECKBOX: + { + if ( nType == CTRL_PUSHBUTTON ) + { + w = aCtrlBoundRect.GetWidth(); + h = aCtrlBoundRect.GetHeight(); + } + else + { + // checkbox and radio borders need cleanup after unchecking them + nBorderCleanup = 4; + + // TEXT_SEPARATOR to respect Aqua HIG + w = BUTTON_WIDTH + TEXT_SEPARATOR; + h = BUTTON_HEIGHT; + + } + + rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h + nBorderCleanup) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) ); + + toReturn = TRUE; + } + break; + case CTRL_PROGRESS: + { + Rectangle aRect( aCtrlBoundRect ); + if( aRect.GetHeight() < 16 ) + aRect.Bottom() = aRect.Top() + 9; // values taken from HIG for medium progress + else + aRect.Bottom() = aRect.Top() + 15; // values taken from HIG for large progress + rNativeBoundingRegion = aRect; + rNativeContentRegion = aRect; + toReturn = TRUE; + } + break; + + case CTRL_INTROPROGRESS: + { + Rectangle aRect( aCtrlBoundRect ); + aRect.Bottom() = aRect.Top() + INTRO_PROGRESS_HEIGHT; // values taken from HIG for medium progress + rNativeBoundingRegion = aRect; + rNativeContentRegion = aRect; + toReturn = TRUE; + } + break; + + case CTRL_TAB_ITEM: + + w = aCtrlBoundRect.GetWidth() + 2*TAB_TEXT_OFFSET - 2*VCL_TAB_TEXT_OFFSET; + +#ifdef OLD_TAB_STYLE + h = TAB_HEIGHT_NORMAL; +#else + h = TAB_HEIGHT_NORMAL+2; +#endif + rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) ); + + toReturn = TRUE; + + break; + + case CTRL_EDITBOX: + { + w = aCtrlBoundRect.GetWidth(); + if( w < 3+2*FOCUS_RING_WIDTH ) + w = 3+2*FOCUS_RING_WIDTH; + h = TEXT_EDIT_HEIGHT_NORMAL; + + rNativeContentRegion = Rectangle( Point( x+FOCUS_RING_WIDTH, y+FOCUS_RING_WIDTH ), Size( w-2*FOCUS_RING_WIDTH-2, h-2 ) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h+2*FOCUS_RING_WIDTH ) ); + + toReturn = TRUE; + } + break; + case CTRL_LISTBOX: + case CTRL_COMBOBOX: + { + if( nPart == PART_ENTIRE_CONTROL ) + { + w = aCtrlBoundRect.GetWidth(); + h = COMBOBOX_HEIGHT_NORMAL;//listboxes and comboxes have the same height + + rNativeContentRegion = Rectangle( Point( x+FOCUS_RING_WIDTH, y+FOCUS_RING_WIDTH ), Size( w-2*FOCUS_RING_WIDTH, h ) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h+2*FOCUS_RING_WIDTH ) ); + + toReturn = TRUE; + } + else if( nPart == PART_BUTTON_DOWN ) + { + w = aCtrlBoundRect.GetWidth(); + if( w < 3+2*FOCUS_RING_WIDTH ) + w = 3+2*FOCUS_RING_WIDTH; + h = COMBOBOX_HEIGHT_NORMAL;//listboxes and comboxes have the same height + + x += w-DROPDOWN_BUTTON_WIDTH - FOCUS_RING_WIDTH; + y += FOCUS_RING_WIDTH; + w = DROPDOWN_BUTTON_WIDTH; + + rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w+FOCUS_RING_WIDTH, h+2*FOCUS_RING_WIDTH ) ); + + toReturn = true; + } + else if( nPart == PART_SUB_EDIT ) + { + w = aCtrlBoundRect.GetWidth(); + h = COMBOBOX_HEIGHT_NORMAL;//listboxes and comboxes have the same height + + x += FOCUS_RING_WIDTH; + x += 3; // add an offset for rounded borders + y += 2; // don't draw into upper border + y += FOCUS_RING_WIDTH; + w -= 3 + DROPDOWN_BUTTON_WIDTH + 2*FOCUS_RING_WIDTH; + if( nType == CTRL_LISTBOX ) + w -= 9; // HIG specifies 9 units distance between dropdown button area and content + h -= 4; // don't draw into lower border + + rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w+FOCUS_RING_WIDTH, h+2*FOCUS_RING_WIDTH ) ); + + toReturn = true; + } + } + break; + case CTRL_SPINBOX: + if( nPart == PART_ENTIRE_CONTROL ) { + w = aCtrlBoundRect.GetWidth(); + if( w < 3+2*FOCUS_RING_WIDTH+SPIN_BUTTON_SPACE+SPIN_BUTTON_WIDTH ) + w = 3+2*FOCUS_RING_WIDTH+SPIN_BUTTON_SPACE+SPIN_BUTTON_WIDTH; + h = TEXT_EDIT_HEIGHT_NORMAL; + + rNativeContentRegion = Rectangle( Point( x+FOCUS_RING_WIDTH, y ), Size( w-2*FOCUS_RING_WIDTH, h ) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h+2*FOCUS_RING_WIDTH ) ); + + toReturn = TRUE; + } + else if( nPart == PART_SUB_EDIT ) { + w = aCtrlBoundRect.GetWidth() - SPIN_BUTTON_SPACE - SPIN_BUTTON_WIDTH; + h = TEXT_EDIT_HEIGHT_NORMAL; + x += 4; // add an offset for rounded borders + y += 2; // don't draw into upper border + w -= 8; // offset for left and right rounded border + h -= 4; // don't draw into upper or ower border + + rNativeContentRegion = Rectangle( Point( x + FOCUS_RING_WIDTH, y + FOCUS_RING_WIDTH ), Size( w - 2* FOCUS_RING_WIDTH, h ) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h+2*FOCUS_RING_WIDTH ) ); + + toReturn = TRUE; + } + else if( nPart == PART_BUTTON_UP ) { + //aCtrlBoundRect.GetWidth() contains the width of the full control + //ie the width of the textfield + button + //x is the position of the left corner of the full control + x += aCtrlBoundRect.GetWidth() - SPIN_BUTTON_WIDTH - SPIN_BUTTON_SPACE - CLIP_FUZZ; + y += FOCUS_RING_WIDTH - CLIP_FUZZ; + w = SPIN_BUTTON_WIDTH + 2*CLIP_FUZZ; + h = SPIN_UPPER_BUTTON_HEIGHT + 2*CLIP_FUZZ; + + rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) ); + + toReturn = TRUE; + } + else if( nPart == PART_BUTTON_DOWN ) { + x += aCtrlBoundRect.GetWidth() - SPIN_BUTTON_WIDTH - SPIN_BUTTON_SPACE - CLIP_FUZZ; + y += SPIN_UPPER_BUTTON_HEIGHT + FOCUS_RING_WIDTH - CLIP_FUZZ; + w = SPIN_BUTTON_WIDTH + 2*CLIP_FUZZ; + h = SPIN_LOWER_BUTTON_HEIGHT + 2*CLIP_FUZZ; + + rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) ); + + toReturn = TRUE; + } + break; + case CTRL_FRAME: + { + USHORT nStyle = aValue.getNumericVal(); + if( ( nPart == PART_BORDER ) && + !( nStyle & (FRAME_DRAW_MENU | FRAME_DRAW_WINDOWBORDER | FRAME_DRAW_BORDERWINDOWBORDER) ) ) + { + Rectangle aRect(aCtrlBoundRect); + if( nStyle & FRAME_DRAW_DOUBLEIN ) + { + aRect.Left() += 1; + aRect.Top() += 1; + //rRect.Right() -= 1; + //rRect.Bottom() -= 1; + } + else + { + aRect.Left() += 1; + aRect.Top() += 1; + aRect.Right() -= 1; + aRect.Bottom() -= 1; + } + + rNativeContentRegion = aRect; + rNativeBoundingRegion = aRect; + + toReturn = TRUE; + } + } + break; + + case CTRL_MENUBAR: + case CTRL_MENU_POPUP: + { + if(( nPart == PART_MENU_ITEM_CHECK_MARK )||( nPart == PART_MENU_ITEM_RADIO_MARK )) { + + w=10; + h=10;//dimensions of the mark (10px font) + + rNativeContentRegion = Rectangle( Point( x, y ), Size( w, h ) ); + rNativeBoundingRegion = Rectangle( Point( x, y ), Size( w, h ) ); + + toReturn = TRUE; + } + } + break; + + } + + return toReturn; +} diff --git a/vcl/aqua/source/gdi/salprn.cxx b/vcl/aqua/source/gdi/salprn.cxx new file mode 100644 index 000000000000..1c0401f769b5 --- /dev/null +++ b/vcl/aqua/source/gdi/salprn.cxx @@ -0,0 +1,875 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "salprn.h" +#include "aquaprintview.h" +#include "salgdi.h" +#include "saldata.hxx" +#include "vcl/jobset.h" +#include "vcl/salptype.hxx" +#include "vcl/print.hxx" +#include "vcl/unohelp.hxx" + +#include <boost/bind.hpp> + +#include "com/sun/star/lang/XMultiServiceFactory.hpp" +#include "com/sun/star/container/XNameAccess.hpp" +#include "com/sun/star/beans/PropertyValue.hpp" +#include "com/sun/star/awt/Size.hpp" + +#include <algorithm> + +using namespace rtl; +using namespace vcl; +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; + +// ======================================================================= + +AquaSalInfoPrinter::AquaSalInfoPrinter( const SalPrinterQueueInfo& i_rQueue ) : + mpGraphics( 0 ), + mbGraphics( false ), + mbJob( false ), + mpPrinter( nil ), + mpPrintInfo( nil ), + mePageOrientation( ORIENTATION_PORTRAIT ), + mnStartPageOffsetX( 0 ), + mnStartPageOffsetY( 0 ), + mnCurPageRangeStart( 0 ), + mnCurPageRangeCount( 0 ) +{ + NSString* pStr = CreateNSString( i_rQueue.maPrinterName ); + mpPrinter = [NSPrinter printerWithName: pStr]; + [pStr release]; + + NSPrintInfo* pShared = [NSPrintInfo sharedPrintInfo]; + if( pShared ) + { + mpPrintInfo = [pShared copy]; + [mpPrintInfo setPrinter: mpPrinter]; + mePageOrientation = ([mpPrintInfo orientation] == NSLandscapeOrientation) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; + [mpPrintInfo setOrientation: NSPortraitOrientation]; + } + + mpGraphics = new AquaSalGraphics(); + + const int nWidth = 100, nHeight = 100; + maContextMemory.reset( reinterpret_cast<sal_uInt8*>( rtl_allocateMemory( nWidth * 4 * nHeight ) ), + boost::bind( rtl_freeMemory, _1 ) ); + + if( maContextMemory ) + { + mrContext = CGBitmapContextCreate( maContextMemory.get(), nWidth, nHeight, 8, nWidth * 4, GetSalData()->mxRGBSpace, kCGImageAlphaNoneSkipFirst ); + if( mrContext ) + SetupPrinterGraphics( mrContext ); + } +} + +// ----------------------------------------------------------------------- + +AquaSalInfoPrinter::~AquaSalInfoPrinter() +{ + delete mpGraphics; + if( mpPrintInfo ) + [mpPrintInfo release]; + #if 0 + // FIXME: verify that NSPrintInfo releases the printer + // else we have a leak here + if( mpPrinter ) + [mpPrinter release]; + #endif + if( mrContext ) + CFRelease( mrContext ); +} + +// ----------------------------------------------------------------------- + +void AquaSalInfoPrinter::SetupPrinterGraphics( CGContextRef i_rContext ) const +{ + if( mpGraphics ) + { + if( mpPrintInfo ) + { + // FIXME: get printer resolution + long nDPIX = 720, nDPIY = 720; + NSSize aPaperSize = [mpPrintInfo paperSize]; + + NSRect aImageRect = [mpPrintInfo imageablePageBounds]; + if( mePageOrientation == ORIENTATION_PORTRAIT ) + { + // move mirrored CTM back into paper + double dX = 0, dY = aPaperSize.height; + // move CTM to reflect imageable area + dX += aImageRect.origin.x; + dY -= aPaperSize.height - aImageRect.size.height - aImageRect.origin.y; + CGContextTranslateCTM( i_rContext, dX + mnStartPageOffsetX, dY - mnStartPageOffsetY ); + // scale to be top/down and reflect our "virtual" DPI + CGContextScaleCTM( i_rContext, 72.0/double(nDPIX), -(72.0/double(nDPIY)) ); + } + else + { + // move CTM to reflect imageable area + double dX = aImageRect.origin.x, dY = aPaperSize.height - aImageRect.size.height - aImageRect.origin.y; + CGContextTranslateCTM( i_rContext, -dX, -dY ); + // turn by 90 degree + CGContextRotateCTM( i_rContext, M_PI/2 ); + // move turned CTM back into paper + dX = aPaperSize.height; + dY = -aPaperSize.width; + CGContextTranslateCTM( i_rContext, dX + mnStartPageOffsetY, dY - mnStartPageOffsetX ); + // scale to be top/down and reflect our "virtual" DPI + CGContextScaleCTM( i_rContext, -(72.0/double(nDPIY)), (72.0/double(nDPIX)) ); + } + mpGraphics->SetPrinterGraphics( i_rContext, nDPIX, nDPIY, 1.0 ); + } + else + DBG_ERROR( "no print info in SetupPrinterGraphics" ); + } +} + +// ----------------------------------------------------------------------- + +SalGraphics* AquaSalInfoPrinter::GetGraphics() +{ + SalGraphics* pGraphics = mbGraphics ? NULL : mpGraphics; + mbGraphics = true; + return pGraphics; +} + +// ----------------------------------------------------------------------- + +void AquaSalInfoPrinter::ReleaseGraphics( SalGraphics* ) +{ + mbGraphics = false; +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalInfoPrinter::Setup( SalFrame* i_pFrame, ImplJobSetup* i_pSetupData ) +{ + return FALSE; +} + +// ----------------------------------------------------------------------- + +static struct PaperSizeEntry +{ + double fWidth; + double fHeight; + Paper nPaper; +} aPaperSizes[] = +{ + { 842, 1191, PAPER_A3 }, + { 595, 842, PAPER_A4 }, + { 420, 595, PAPER_A5 }, + { 612, 792, PAPER_LETTER }, + { 612, 1008, PAPER_LEGAL }, + { 728, 1032, PAPER_B4_JIS }, + { 516, 729, PAPER_B5_JIS }, + { 792, 1224, PAPER_TABLOID } +}; + +static bool getPaperSize( double& o_fWidth, double& o_fHeight, const Paper i_ePaper ) +{ + for(unsigned int i = 0; i < sizeof(aPaperSizes)/sizeof(aPaperSizes[0]); i++ ) + { + if( aPaperSizes[i].nPaper == i_ePaper ) + { + o_fWidth = aPaperSizes[i].fWidth; + o_fHeight = aPaperSizes[i].fHeight; + return true; + } + } + return false; +} + +static Paper recognizePaper( double i_fWidth, double i_fHeight ) +{ + Paper aPaper = PAPER_USER; + sal_uInt64 nPaperDesc = 1000000*sal_uInt64(i_fWidth) + sal_uInt64(i_fHeight); + switch( nPaperDesc ) + { + case 842001191: aPaper = PAPER_A3; break; + case 595000842: aPaper = PAPER_A4; break; + case 420000595: aPaper = PAPER_A5; break; + case 612000792: aPaper = PAPER_LETTER; break; + case 728001032: aPaper = PAPER_B4_JIS; break; + case 516000729: aPaper = PAPER_B5_JIS; break; + case 612001008: aPaper = PAPER_LEGAL; break; + case 792001224: aPaper = PAPER_TABLOID; break; + default: + aPaper = PAPER_USER; + break; + } + + if( aPaper == PAPER_USER ) + { + // search with fuzz factor + for( unsigned int i = 0; i < sizeof(aPaperSizes)/sizeof(aPaperSizes[0]); i++ ) + { + double w = (i_fWidth > aPaperSizes[i].fWidth) ? i_fWidth - aPaperSizes[i].fWidth : aPaperSizes[i].fWidth - i_fWidth; + double h = (i_fHeight > aPaperSizes[i].fHeight) ? i_fHeight - aPaperSizes[i].fHeight : aPaperSizes[i].fHeight - i_fHeight; + if( w < 3 && h < 3 ) + { + aPaper = aPaperSizes[i].nPaper; + break; + } + } + } + + return aPaper; +} + +BOOL AquaSalInfoPrinter::SetPrinterData( ImplJobSetup* io_pSetupData ) +{ + // FIXME: implement driver data + if( io_pSetupData && io_pSetupData->mpDriverData ) + return SetData( ~0, io_pSetupData ); + + + BOOL bSuccess = TRUE; + + // set system type + io_pSetupData->mnSystem = JOBSETUP_SYSTEM_MAC; + + // get paper format + if( mpPrintInfo ) + { + NSSize aPaperSize = [mpPrintInfo paperSize]; + double width = aPaperSize.width, height = aPaperSize.height; + // set paper + io_pSetupData->mePaperFormat = recognizePaper( width, height ); + if( io_pSetupData->mePaperFormat == PAPER_USER ) + { + io_pSetupData->mnPaperWidth = PtTo10Mu( width ); + io_pSetupData->mnPaperHeight = PtTo10Mu( height ); + } + else + { + io_pSetupData->mnPaperWidth = 0; + io_pSetupData->mnPaperHeight = 0; + } + + // set orientation + io_pSetupData->meOrientation = mePageOrientation; + + io_pSetupData->mnPaperBin = 0; + io_pSetupData->mpDriverData = reinterpret_cast<BYTE*>(rtl_allocateMemory( 4 )); + io_pSetupData->mnDriverDataLen = 4; + } + else + bSuccess = FALSE; + + + return bSuccess; +} + +// ----------------------------------------------------------------------- + +void AquaSalInfoPrinter::setPaperSize( long i_nWidth, long i_nHeight, Orientation i_eSetOrientation ) +{ + + Orientation ePaperOrientation = ORIENTATION_PORTRAIT; + const PaperInfo* pPaper = matchPaper( i_nWidth, i_nHeight, ePaperOrientation ); + + if( pPaper ) + { + NSString* pPaperName = [CreateNSString( rtl::OStringToOUString(PaperInfo::toPSName(pPaper->getPaper()), RTL_TEXTENCODING_ASCII_US) ) autorelease]; + [mpPrintInfo setPaperName: pPaperName]; + } + else if( i_nWidth > 0 && i_nHeight > 0 ) + { + NSSize aPaperSize = { TenMuToPt(i_nWidth), TenMuToPt(i_nHeight) }; + [mpPrintInfo setPaperSize: aPaperSize]; + } + // this seems counterintuitive + mePageOrientation = i_eSetOrientation; +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalInfoPrinter::SetData( ULONG i_nFlags, ImplJobSetup* io_pSetupData ) +{ + if( ! io_pSetupData || io_pSetupData->mnSystem != JOBSETUP_SYSTEM_MAC ) + return FALSE; + + + if( mpPrintInfo ) + { + if( (i_nFlags & SAL_JOBSET_ORIENTATION) != 0 ) + mePageOrientation = io_pSetupData->meOrientation; + + if( (i_nFlags & SAL_JOBSET_PAPERSIZE) != 0) + { + // set paper format + long width = 21000, height = 29700; + if( io_pSetupData->mePaperFormat == PAPER_USER ) + { + // #i101108# sanity check + if( io_pSetupData->mnPaperWidth && io_pSetupData->mnPaperHeight ) + { + width = io_pSetupData->mnPaperWidth; + height = io_pSetupData->mnPaperHeight; + } + } + else + { + double w = 595, h = 842; + getPaperSize( w, h, io_pSetupData->mePaperFormat ); + width = static_cast<long>(PtTo10Mu( w )); + height = static_cast<long>(PtTo10Mu( h )); + } + + setPaperSize( width, height, mePageOrientation ); + } + } + + return mpPrintInfo != nil; +} + +// ----------------------------------------------------------------------- + +ULONG AquaSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* i_pSetupData ) +{ + return 0; +} + +// ----------------------------------------------------------------------- + +XubString AquaSalInfoPrinter::GetPaperBinName( const ImplJobSetup* i_pSetupData, ULONG i_nPaperBin ) +{ + return XubString(); +} + +// ----------------------------------------------------------------------- + +static bool getUseNativeDialog() +{ + bool bNative = true; + try + { + // get service provider + Reference< XMultiServiceFactory > xSMgr( unohelper::GetMultiServiceFactory() ); + // create configuration hierachical access name + if( xSMgr.is() ) + { + try + { + Reference< XMultiServiceFactory > xConfigProvider( + Reference< XMultiServiceFactory >( + xSMgr->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.configuration.ConfigurationProvider" ))), + UNO_QUERY ) + ); + if( xConfigProvider.is() ) + { + Sequence< Any > aArgs(1); + PropertyValue aVal; + aVal.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "nodepath" ) ); + aVal.Value <<= OUString( RTL_CONSTASCII_USTRINGPARAM( "/org.openoffice.Office.Common/Misc" ) ); + aArgs.getArray()[0] <<= aVal; + Reference< XNameAccess > xConfigAccess( + Reference< XNameAccess >( + xConfigProvider->createInstanceWithArguments( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.configuration.ConfigurationAccess" )), + aArgs ), + UNO_QUERY ) + ); + if( xConfigAccess.is() ) + { + try + { + sal_Bool bValue = sal_False; + Any aAny = xConfigAccess->getByName( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "UseSystemPrintDialog" ) ) ); + if( aAny >>= bValue ) + bNative = bValue; + } + catch( NoSuchElementException& ) + { + } + catch( WrappedTargetException& ) + { + } + } + } + } + catch( Exception& ) + { + } + } + } + catch( WrappedTargetException& ) + { + } + + return bNative; +} + +ULONG AquaSalInfoPrinter::GetCapabilities( const ImplJobSetup* i_pSetupData, USHORT i_nType ) +{ + switch( i_nType ) + { + case PRINTER_CAPABILITIES_SUPPORTDIALOG: + return 0; + case PRINTER_CAPABILITIES_COPIES: + return 0xffff; + case PRINTER_CAPABILITIES_COLLATECOPIES: + return 0; + case PRINTER_CAPABILITIES_SETORIENTATION: + return 1; + case PRINTER_CAPABILITIES_SETDUPLEX: + return 0; + case PRINTER_CAPABILITIES_SETPAPERBIN: + return 0; + case PRINTER_CAPABILITIES_SETPAPERSIZE: + return 1; + case PRINTER_CAPABILITIES_SETPAPER: + return 1; + case PRINTER_CAPABILITIES_EXTERNALDIALOG: + return getUseNativeDialog() ? 1 : 0; + case PRINTER_CAPABILITIES_PDF: + return 1; + default: break; + }; + return 0; +} + +// ----------------------------------------------------------------------- + +void AquaSalInfoPrinter::GetPageInfo( const ImplJobSetup*, + long& o_rOutWidth, long& o_rOutHeight, + long& o_rPageOffX, long& o_rPageOffY, + long& o_rPageWidth, long& o_rPageHeight ) +{ + if( mpPrintInfo ) + { + long nDPIX = 72, nDPIY = 72; + mpGraphics->GetResolution( nDPIX, nDPIY ); + const double fXScaling = static_cast<double>(nDPIX)/72.0, + fYScaling = static_cast<double>(nDPIY)/72.0; + + NSSize aPaperSize = [mpPrintInfo paperSize]; + o_rPageWidth = static_cast<long>( double(aPaperSize.width) * fXScaling ); + o_rPageHeight = static_cast<long>( double(aPaperSize.height) * fYScaling ); + + NSRect aImageRect = [mpPrintInfo imageablePageBounds]; + o_rPageOffX = static_cast<long>( aImageRect.origin.x * fXScaling ); + o_rPageOffY = static_cast<long>( (aPaperSize.height - aImageRect.size.height - aImageRect.origin.y) * fYScaling ); + o_rOutWidth = static_cast<long>( aImageRect.size.width * fXScaling ); + o_rOutHeight = static_cast<long>( aImageRect.size.height * fYScaling ); + + if( mePageOrientation == ORIENTATION_LANDSCAPE ) + { + std::swap( o_rOutWidth, o_rOutHeight ); + std::swap( o_rPageWidth, o_rPageHeight ); + std::swap( o_rPageOffX, o_rPageOffY ); + } + } +} + +static Size getPageSize( vcl::PrinterController& i_rController, sal_Int32 i_nPage ) +{ + Size aPageSize; + Sequence< PropertyValue > aPageParms( i_rController.getPageParameters( i_nPage ) ); + for( sal_Int32 nProperty = 0, nPropertyCount = aPageParms.getLength(); nProperty < nPropertyCount; ++nProperty ) + { + if( aPageParms[ nProperty ].Name.equalsAscii( "PageSize" ) ) + { + awt::Size aSize; + aPageParms[ nProperty].Value >>= aSize; + aPageSize.Width() = aSize.Width; + aPageSize.Height() = aSize.Height; + break; + } + } + return aPageSize; +} + +BOOL AquaSalInfoPrinter::StartJob( const String* i_pFileName, + const String& i_rJobName, + const String& i_rAppName, + ImplJobSetup* i_pSetupData, + vcl::PrinterController& i_rController + ) +{ + if( mbJob ) + return FALSE; + + BOOL bSuccess = FALSE; + bool bWasAborted = false; + AquaSalInstance* pInst = GetSalData()->mpFirstInstance; + PrintAccessoryViewState aAccViewState; + sal_Int32 nAllPages = 0; + + // reset IsLastPage + i_rController.setLastPage( sal_False ); + + // update job data + if( i_pSetupData ) + SetData( ~0, i_pSetupData ); + + // do we want a progress panel ? + sal_Bool bShowProgressPanel = sal_True; + beans::PropertyValue* pMonitor = i_rController.getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MonitorVisible" ) ) ); + if( pMonitor ) + pMonitor->Value >>= bShowProgressPanel; + if( ! i_rController.isShowDialogs() ) + bShowProgressPanel = sal_False; + + // possibly create one job for collated output + sal_Bool bSinglePrintJobs = sal_False; + beans::PropertyValue* pSingleValue = i_rController.getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ) ); + if( pSingleValue ) + { + pSingleValue->Value >>= bSinglePrintJobs; + } + + // FIXME: jobStarted() should be done after the print dialog has ended (if there is one) + // how do I know when that might be ? + i_rController.jobStarted(); + + + int nCopies = i_rController.getPrinter()->GetCopyCount(); + int nJobs = 1; + if( bSinglePrintJobs ) + { + nJobs = nCopies; + nCopies = 1; + } + + for( int nCurJob = 0; nCurJob < nJobs; nCurJob++ ) + { + aAccViewState.bNeedRestart = true; + do + { + if( aAccViewState.bNeedRestart ) + { + mnCurPageRangeStart = 0; + mnCurPageRangeCount = 0; + nAllPages = i_rController.getFilteredPageCount(); + } + + aAccViewState.bNeedRestart = false; + + Size aCurSize( 21000, 29700 ); + if( nAllPages > 0 ) + { + mnCurPageRangeCount = 1; + aCurSize = getPageSize( i_rController, mnCurPageRangeStart ); + Size aNextSize( aCurSize ); + + // print pages up to a different size + while( mnCurPageRangeCount + mnCurPageRangeStart < nAllPages ) + { + aNextSize = getPageSize( i_rController, mnCurPageRangeStart + mnCurPageRangeCount ); + if( aCurSize == aNextSize // same page size + || + (aCurSize.Width() == aNextSize.Height() && aCurSize.Height() == aNextSize.Width()) // same size, but different orientation + ) + { + mnCurPageRangeCount++; + } + else + break; + } + } + else + mnCurPageRangeCount = 0; + + // now for the current run + mnStartPageOffsetX = mnStartPageOffsetY = 0; + // setup the paper size and orientation + // do this on our associated Printer object, since that is + // out interface to the applications which occasionally rely on the paper + // information (e.g. brochure printing scales to the found paper size) + // also SetPaperSizeUser has the advantage that we can share a + // platform independent paper matching algorithm + boost::shared_ptr<Printer> pPrinter( i_rController.getPrinter() ); + pPrinter->SetMapMode( MapMode( MAP_100TH_MM ) ); + pPrinter->SetPaperSizeUser( aCurSize, true ); + + // create view + NSView* pPrintView = [[AquaPrintView alloc] initWithController: &i_rController withInfoPrinter: this]; + + NSMutableDictionary* pPrintDict = [mpPrintInfo dictionary]; + + // set filename + if( i_pFileName ) + { + [mpPrintInfo setJobDisposition: NSPrintSaveJob]; + NSString* pPath = CreateNSString( *i_pFileName ); + [pPrintDict setObject: pPath forKey: NSPrintSavePath]; + [pPath release]; + } + + [pPrintDict setObject: [[NSNumber numberWithInt: nCopies] autorelease] forKey: NSPrintCopies]; + [pPrintDict setObject: [[NSNumber numberWithBool: YES] autorelease] forKey: NSPrintDetailedErrorReporting]; + [pPrintDict setObject: [[NSNumber numberWithInt: 1] autorelease] forKey: NSPrintFirstPage]; + // #i103253# weird: for some reason, autoreleasing the value below like the others above + // leads do a double free malloc error. Why this value should behave differently from all the others + // is a mystery. + [pPrintDict setObject: [NSNumber numberWithInt: mnCurPageRangeCount] forKey: NSPrintLastPage]; + + + // create print operation + NSPrintOperation* pPrintOperation = [NSPrintOperation printOperationWithView: pPrintView printInfo: mpPrintInfo]; + + if( pPrintOperation ) + { + NSObject* pReleaseAfterUse = nil; + bool bShowPanel = (! i_rController.isDirectPrint() && getUseNativeDialog() && i_rController.isShowDialogs() ); + [pPrintOperation setShowsPrintPanel: bShowPanel ? YES : NO ]; + [pPrintOperation setShowsProgressPanel: bShowProgressPanel ? YES : NO]; + + // set job title (since MacOSX 10.5) + if( [pPrintOperation respondsToSelector: @selector(setJobTitle:)] ) + [pPrintOperation performSelector: @selector(setJobTitle:) withObject: [CreateNSString( i_rJobName ) autorelease]]; + + if( bShowPanel && mnCurPageRangeStart == 0 && nCurJob == 0) // only the first range of pages (in the first job) gets the accesory view + pReleaseAfterUse = [AquaPrintAccessoryView setupPrinterPanel: pPrintOperation withController: &i_rController withState: &aAccViewState]; + + bSuccess = TRUE; + mbJob = true; + pInst->startedPrintJob(); + [pPrintOperation runOperation]; + pInst->endedPrintJob(); + bWasAborted = [[[pPrintOperation printInfo] jobDisposition] compare: NSPrintCancelJob] == NSOrderedSame; + mbJob = false; + if( pReleaseAfterUse ) + [pReleaseAfterUse release]; + } + + mnCurPageRangeStart += mnCurPageRangeCount; + mnCurPageRangeCount = 1; + } while( aAccViewState.bNeedRestart || mnCurPageRangeStart + mnCurPageRangeCount < nAllPages ); + } + + // inform application that it can release its data + // this is awkward, but the XRenderable interface has no method for this, + // so we need to call XRenderadble::render one last time with IsLastPage = TRUE + i_rController.setLastPage( sal_True ); + GDIMetaFile aPageFile; + if( mrContext ) + SetupPrinterGraphics( mrContext ); + i_rController.getFilteredPageFile( 0, aPageFile ); + + i_rController.setJobState( bWasAborted + ? view::PrintableState_JOB_ABORTED + : view::PrintableState_JOB_SPOOLED ); + + mnCurPageRangeStart = mnCurPageRangeCount = 0; + + return bSuccess; +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalInfoPrinter::EndJob() +{ + mnStartPageOffsetX = mnStartPageOffsetY = 0; + mbJob = false; + return TRUE; +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalInfoPrinter::AbortJob() +{ + mbJob = false; + + // FIXME: implementation + return FALSE; +} + +// ----------------------------------------------------------------------- + +SalGraphics* AquaSalInfoPrinter::StartPage( ImplJobSetup* i_pSetupData, BOOL i_bNewJobData ) +{ + if( i_bNewJobData && i_pSetupData ) + SetPrinterData( i_pSetupData ); + + CGContextRef rContext = reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); + + SetupPrinterGraphics( rContext ); + + return mpGraphics; +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalInfoPrinter::EndPage() +{ + mpGraphics->InvalidateContext(); + return TRUE; +} + +// ----------------------------------------------------------------------- + +ULONG AquaSalInfoPrinter::GetErrorCode() const +{ + return 0; +} + +// ======================================================================= + +AquaSalPrinter::AquaSalPrinter( AquaSalInfoPrinter* i_pInfoPrinter ) : + mpInfoPrinter( i_pInfoPrinter ) +{ +} + +// ----------------------------------------------------------------------- + +AquaSalPrinter::~AquaSalPrinter() +{ +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalPrinter::StartJob( const String* i_pFileName, + const String& i_rJobName, + const String& i_rAppName, + ImplJobSetup* i_pSetupData, + vcl::PrinterController& i_rController ) +{ + return mpInfoPrinter->StartJob( i_pFileName, i_rJobName, i_rAppName, i_pSetupData, i_rController ); +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalPrinter::StartJob( const XubString* i_pFileName, + const XubString& i_rJobName, + const XubString& i_rAppName, + ULONG i_nCopies, + bool i_bCollate, + bool i_bDirect, + ImplJobSetup* i_pSetupData ) +{ + DBG_ERROR( "should never be called" ); + return FALSE; +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalPrinter::EndJob() +{ + return mpInfoPrinter->EndJob(); +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalPrinter::AbortJob() +{ + return mpInfoPrinter->AbortJob(); +} + +// ----------------------------------------------------------------------- + +SalGraphics* AquaSalPrinter::StartPage( ImplJobSetup* i_pSetupData, BOOL i_bNewJobData ) +{ + return mpInfoPrinter->StartPage( i_pSetupData, i_bNewJobData ); +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalPrinter::EndPage() +{ + return mpInfoPrinter->EndPage(); +} + +// ----------------------------------------------------------------------- + +ULONG AquaSalPrinter::GetErrorCode() +{ + return mpInfoPrinter->GetErrorCode(); +} + +void AquaSalInfoPrinter::InitPaperFormats( const ImplJobSetup* i_pSetupData ) +{ + m_aPaperFormats.clear(); + m_bPapersInit = true; + + if( mpPrinter ) + { + if( [mpPrinter statusForTable: @"PPD"] == NSPrinterTableOK ) + { + NSArray* pPaperNames = [mpPrinter stringListForKey: @"PageSize" inTable: @"PPD"]; + if( pPaperNames ) + { + unsigned int nPapers = [pPaperNames count]; + for( unsigned int i = 0; i < nPapers; i++ ) + { + NSString* pPaper = [pPaperNames objectAtIndex: i]; + NSSize aPaperSize = [mpPrinter pageSizeForPaper: pPaper]; + if( aPaperSize.width > 0 && aPaperSize.height > 0 ) + { + PaperInfo aInfo( PtTo10Mu( aPaperSize.width ), + PtTo10Mu( aPaperSize.height ) ); + m_aPaperFormats.push_back( aInfo ); + } + } + } + } + } +} + +const PaperInfo* AquaSalInfoPrinter::matchPaper( long i_nWidth, long i_nHeight, Orientation& o_rOrientation ) const +{ + if( ! m_bPapersInit ) + const_cast<AquaSalInfoPrinter*>(this)->InitPaperFormats( NULL ); + + const PaperInfo* pMatch = NULL; + o_rOrientation = ORIENTATION_PORTRAIT; + for( int n = 0; n < 2 ; n++ ) + { + for( size_t i = 0; i < m_aPaperFormats.size(); i++ ) + { + if( abs( m_aPaperFormats[i].getWidth() - i_nWidth ) < 50 && + abs( m_aPaperFormats[i].getHeight() - i_nHeight ) < 50 ) + { + pMatch = &m_aPaperFormats[i]; + return pMatch; + } + } + o_rOrientation = ORIENTATION_LANDSCAPE; + std::swap( i_nWidth, i_nHeight ); + } + return pMatch; +} + +int AquaSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* i_pSetupData ) +{ + return 900; +} + + diff --git a/vcl/aqua/source/gdi/salvd.cxx b/vcl/aqua/source/gdi/salvd.cxx new file mode 100644 index 000000000000..eb09a44f5edd --- /dev/null +++ b/vcl/aqua/source/gdi/salvd.cxx @@ -0,0 +1,236 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salvd.h" +#include "salinst.h" +#include "salgdi.h" +#include "saldata.hxx" +#include "salframe.h" + +#include "vcl/sysdata.hxx" + +// ----------------------------------------------------------------------- + +SalVirtualDevice* AquaSalInstance::CreateVirtualDevice( SalGraphics* pGraphics, + long nDX, long nDY, USHORT nBitCount, const SystemGraphicsData *pData ) +{ + // #i92075# can be called first in a thread + SalData::ensureThreadAutoreleasePool(); + + return new AquaSalVirtualDevice( static_cast< AquaSalGraphics* >( pGraphics ), nDX, nDY, nBitCount, pData ); +} + +// ----------------------------------------------------------------------- + +void AquaSalInstance::DestroyVirtualDevice( SalVirtualDevice* pDevice ) +{ + delete pDevice; +} + +// ======================================================================= + +AquaSalVirtualDevice::AquaSalVirtualDevice( AquaSalGraphics* pGraphic, long nDX, long nDY, USHORT nBitCount, const SystemGraphicsData *pData ) +: mbGraphicsUsed( false ) +, mxBitmapContext( NULL ) +, mnBitmapDepth( 0 ) +, mxLayer( NULL ) +{ + if( pGraphic && pData && pData->rCGContext ) + { + // Create virtual device based on existing SystemGraphicsData + // We ignore nDx and nDY, as the desired size comes from the SystemGraphicsData + mbForeignContext = true; // the mxContext is from pData + mpGraphics = new AquaSalGraphics( /*pGraphic*/ ); + mpGraphics->SetVirDevGraphics( mxLayer, pData->rCGContext ); + } + else + { + // create empty new virtual device + mbForeignContext = false; // the mxContext is created within VCL + mpGraphics = new AquaSalGraphics(); // never fails + mnBitmapDepth = nBitCount; + + // inherit resolution from reference device + if( pGraphic ) + { + AquaSalFrame* pFrame = pGraphic->getGraphicsFrame(); + if( pFrame && AquaSalFrame::isAlive( pFrame ) ) + { + mpGraphics->setGraphicsFrame( pFrame ); + mpGraphics->copyResolution( *pGraphic ); + } + } + + if( nDX && nDY ) + SetSize( nDX, nDY ); + + // NOTE: if SetSize does not succeed, we just ignore the nDX and nDY + } +} + +// ----------------------------------------------------------------------- + +AquaSalVirtualDevice::~AquaSalVirtualDevice() +{ + if( mpGraphics ) + { + mpGraphics->SetVirDevGraphics( NULL, NULL ); + delete mpGraphics; + mpGraphics = 0; + } + Destroy(); +} + +// ----------------------------------------------------------------------- + +void AquaSalVirtualDevice::Destroy() +{ + if( mbForeignContext ) { + // Do not delete mxContext that we have received from outside VCL + mxLayer = NULL; + return; + } + + if( mxLayer ) + { + if( mpGraphics ) + mpGraphics->SetVirDevGraphics( NULL, NULL ); + CGLayerRelease( mxLayer ); + mxLayer = NULL; + } + + if( mxBitmapContext ) + { + void* pRawData = CGBitmapContextGetData( mxBitmapContext ); + rtl_freeMemory( pRawData ); + CGContextRelease( mxBitmapContext ); + mxBitmapContext = NULL; + } +} + +// ----------------------------------------------------------------------- + +SalGraphics* AquaSalVirtualDevice::GetGraphics() +{ + if( mbGraphicsUsed || !mpGraphics ) + return 0; + + mbGraphicsUsed = true; + return mpGraphics; +} + +// ----------------------------------------------------------------------- + +void AquaSalVirtualDevice::ReleaseGraphics( SalGraphics *pGraphics ) +{ + mbGraphicsUsed = false; +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalVirtualDevice::SetSize( long nDX, long nDY ) +{ + if( mbForeignContext ) + { + // Do not delete/resize mxContext that we have received from outside VCL + return true; + } + + if( mxLayer ) + { + const CGSize aSize = CGLayerGetSize( mxLayer ); + if( (nDX == aSize.width) && (nDY == aSize.height) ) + { + // Yay, we do not have to do anything :) + return true; + } + } + + Destroy(); + + // create a Quartz layer matching to the intended virdev usage + CGContextRef xCGContext = NULL; + if( mnBitmapDepth && (mnBitmapDepth < 16) ) + { + mnBitmapDepth = 8; // TODO: are 1bit vdevs worth it? + const CGColorSpaceRef aCGColorSpace = GetSalData()->mxGraySpace; + const CGBitmapInfo aCGBmpInfo = kCGImageAlphaNone; + const int nBytesPerRow = (mnBitmapDepth * nDX + 7) / 8; + + void* pRawData = rtl_allocateMemory( nBytesPerRow * nDY ); + mxBitmapContext = ::CGBitmapContextCreate( pRawData, nDX, nDY, + mnBitmapDepth, nBytesPerRow, aCGColorSpace, aCGBmpInfo ); + xCGContext = mxBitmapContext; + } + else + { + // default to a NSView target context + AquaSalFrame* pSalFrame = mpGraphics->getGraphicsFrame(); + if( !pSalFrame && !GetSalData()->maFrames.empty() ) + pSalFrame = *GetSalData()->maFrames.begin(); + if( pSalFrame ) + { + NSGraphicsContext* pNSContext = [NSGraphicsContext graphicsContextWithWindow: pSalFrame->getWindow()]; + if( pNSContext ) + xCGContext = reinterpret_cast<CGContextRef>([pNSContext graphicsPort]); + } + } + + DBG_ASSERT( xCGContext, "no context" ); + + const CGSize aNewSize = { nDX, nDY }; + mxLayer = CGLayerCreateWithContext( xCGContext, aNewSize, NULL ); + + if( mxLayer && mpGraphics ) + { + // get the matching Quartz context + CGContextRef xDrawContext = CGLayerGetContext( mxLayer ); + mpGraphics->SetVirDevGraphics( mxLayer, xDrawContext, mnBitmapDepth ); + } + + return (mxLayer != NULL); +} + +// ----------------------------------------------------------------------- + +void AquaSalVirtualDevice::GetSize( long& rWidth, long& rHeight ) +{ + if( mxLayer ) + { + const CGSize aSize = CGLayerGetSize( mxLayer ); + rWidth = static_cast<long>(aSize.width); + rHeight = static_cast<long>(aSize.height); + } + else + { + rWidth = 0; + rHeight = 0; + } +} diff --git a/vcl/aqua/source/res/MainMenu.nib/classes.nib b/vcl/aqua/source/res/MainMenu.nib/classes.nib new file mode 100644 index 000000000000..b9b4b09f6b0d --- /dev/null +++ b/vcl/aqua/source/res/MainMenu.nib/classes.nib @@ -0,0 +1,4 @@ +{ + IBClasses = ({CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }); + IBVersion = 1; +}
\ No newline at end of file diff --git a/vcl/aqua/source/res/MainMenu.nib/info.nib b/vcl/aqua/source/res/MainMenu.nib/info.nib new file mode 100644 index 000000000000..856429aee5bd --- /dev/null +++ b/vcl/aqua/source/res/MainMenu.nib/info.nib @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IBDocumentLocation</key> + <string>135 107 356 240 0 0 1680 1028 </string> + <key>IBEditorPositions</key> + <dict> + <key>29</key> + <string>132 352 141 44 0 0 1680 1028 </string> + </dict> + <key>IBFramework Version</key> + <string>446.1</string> + <key>IBOpenObjects</key> + <array> + <integer>29</integer> + </array> + <key>IBSystem Version</key> + <string>8R2218</string> +</dict> +</plist> diff --git a/vcl/aqua/source/res/MainMenu.nib/keyedobjects.nib b/vcl/aqua/source/res/MainMenu.nib/keyedobjects.nib Binary files differnew file mode 100644 index 000000000000..d39d10119c0c --- /dev/null +++ b/vcl/aqua/source/res/MainMenu.nib/keyedobjects.nib diff --git a/vcl/aqua/source/res/cursors/airbrush.png b/vcl/aqua/source/res/cursors/airbrush.png Binary files differnew file mode 100644 index 000000000000..7ec780c4f9f9 --- /dev/null +++ b/vcl/aqua/source/res/cursors/airbrush.png diff --git a/vcl/aqua/source/res/cursors/ase.png b/vcl/aqua/source/res/cursors/ase.png Binary files differnew file mode 100644 index 000000000000..a3a30e0bcdce --- /dev/null +++ b/vcl/aqua/source/res/cursors/ase.png diff --git a/vcl/aqua/source/res/cursors/asn.png b/vcl/aqua/source/res/cursors/asn.png Binary files differnew file mode 100644 index 000000000000..7a140b1ec926 --- /dev/null +++ b/vcl/aqua/source/res/cursors/asn.png diff --git a/vcl/aqua/source/res/cursors/asne.png b/vcl/aqua/source/res/cursors/asne.png Binary files differnew file mode 100644 index 000000000000..311506aeb349 --- /dev/null +++ b/vcl/aqua/source/res/cursors/asne.png diff --git a/vcl/aqua/source/res/cursors/asns.png b/vcl/aqua/source/res/cursors/asns.png Binary files differnew file mode 100644 index 000000000000..1c8950eb28bc --- /dev/null +++ b/vcl/aqua/source/res/cursors/asns.png diff --git a/vcl/aqua/source/res/cursors/asnswe.png b/vcl/aqua/source/res/cursors/asnswe.png Binary files differnew file mode 100644 index 000000000000..aae5246fbbc0 --- /dev/null +++ b/vcl/aqua/source/res/cursors/asnswe.png diff --git a/vcl/aqua/source/res/cursors/asnw.png b/vcl/aqua/source/res/cursors/asnw.png Binary files differnew file mode 100644 index 000000000000..9fd0036df077 --- /dev/null +++ b/vcl/aqua/source/res/cursors/asnw.png diff --git a/vcl/aqua/source/res/cursors/ass.png b/vcl/aqua/source/res/cursors/ass.png Binary files differnew file mode 100644 index 000000000000..bee09e736ad1 --- /dev/null +++ b/vcl/aqua/source/res/cursors/ass.png diff --git a/vcl/aqua/source/res/cursors/asse.png b/vcl/aqua/source/res/cursors/asse.png Binary files differnew file mode 100644 index 000000000000..d7883211d44f --- /dev/null +++ b/vcl/aqua/source/res/cursors/asse.png diff --git a/vcl/aqua/source/res/cursors/assw.png b/vcl/aqua/source/res/cursors/assw.png Binary files differnew file mode 100644 index 000000000000..0b0a496a52ec --- /dev/null +++ b/vcl/aqua/source/res/cursors/assw.png diff --git a/vcl/aqua/source/res/cursors/asw.png b/vcl/aqua/source/res/cursors/asw.png Binary files differnew file mode 100644 index 000000000000..5a4b9519e075 --- /dev/null +++ b/vcl/aqua/source/res/cursors/asw.png diff --git a/vcl/aqua/source/res/cursors/aswe.png b/vcl/aqua/source/res/cursors/aswe.png Binary files differnew file mode 100644 index 000000000000..b9c5afaac043 --- /dev/null +++ b/vcl/aqua/source/res/cursors/aswe.png diff --git a/vcl/aqua/source/res/cursors/chain.png b/vcl/aqua/source/res/cursors/chain.png Binary files differnew file mode 100644 index 000000000000..dbf069924d73 --- /dev/null +++ b/vcl/aqua/source/res/cursors/chain.png diff --git a/vcl/aqua/source/res/cursors/chainnot.png b/vcl/aqua/source/res/cursors/chainnot.png Binary files differnew file mode 100644 index 000000000000..547703edf12c --- /dev/null +++ b/vcl/aqua/source/res/cursors/chainnot.png diff --git a/vcl/aqua/source/res/cursors/chart.png b/vcl/aqua/source/res/cursors/chart.png Binary files differnew file mode 100644 index 000000000000..de5514006e1f --- /dev/null +++ b/vcl/aqua/source/res/cursors/chart.png diff --git a/vcl/aqua/source/res/cursors/copydata.png b/vcl/aqua/source/res/cursors/copydata.png Binary files differnew file mode 100644 index 000000000000..b6202fd9144f --- /dev/null +++ b/vcl/aqua/source/res/cursors/copydata.png diff --git a/vcl/aqua/source/res/cursors/copydlnk.png b/vcl/aqua/source/res/cursors/copydlnk.png Binary files differnew file mode 100644 index 000000000000..fab24c9f8f7c --- /dev/null +++ b/vcl/aqua/source/res/cursors/copydlnk.png diff --git a/vcl/aqua/source/res/cursors/copyf.png b/vcl/aqua/source/res/cursors/copyf.png Binary files differnew file mode 100644 index 000000000000..70546d0c0c22 --- /dev/null +++ b/vcl/aqua/source/res/cursors/copyf.png diff --git a/vcl/aqua/source/res/cursors/copyf2.png b/vcl/aqua/source/res/cursors/copyf2.png Binary files differnew file mode 100644 index 000000000000..b6f76051f10f --- /dev/null +++ b/vcl/aqua/source/res/cursors/copyf2.png diff --git a/vcl/aqua/source/res/cursors/copyflnk.png b/vcl/aqua/source/res/cursors/copyflnk.png Binary files differnew file mode 100644 index 000000000000..23561e484e36 --- /dev/null +++ b/vcl/aqua/source/res/cursors/copyflnk.png diff --git a/vcl/aqua/source/res/cursors/crook.png b/vcl/aqua/source/res/cursors/crook.png Binary files differnew file mode 100644 index 000000000000..4378f8df8351 --- /dev/null +++ b/vcl/aqua/source/res/cursors/crook.png diff --git a/vcl/aqua/source/res/cursors/crop.png b/vcl/aqua/source/res/cursors/crop.png Binary files differnew file mode 100644 index 000000000000..92a778ada31a --- /dev/null +++ b/vcl/aqua/source/res/cursors/crop.png diff --git a/vcl/aqua/source/res/cursors/darc.png b/vcl/aqua/source/res/cursors/darc.png Binary files differnew file mode 100644 index 000000000000..9772a1c6b85a --- /dev/null +++ b/vcl/aqua/source/res/cursors/darc.png diff --git a/vcl/aqua/source/res/cursors/dbezier.png b/vcl/aqua/source/res/cursors/dbezier.png Binary files differnew file mode 100644 index 000000000000..988498137e9a --- /dev/null +++ b/vcl/aqua/source/res/cursors/dbezier.png diff --git a/vcl/aqua/source/res/cursors/dcapt.png b/vcl/aqua/source/res/cursors/dcapt.png Binary files differnew file mode 100644 index 000000000000..d1ef82818735 --- /dev/null +++ b/vcl/aqua/source/res/cursors/dcapt.png diff --git a/vcl/aqua/source/res/cursors/dcirccut.png b/vcl/aqua/source/res/cursors/dcirccut.png Binary files differnew file mode 100644 index 000000000000..cb4ed0e85ecd --- /dev/null +++ b/vcl/aqua/source/res/cursors/dcirccut.png diff --git a/vcl/aqua/source/res/cursors/dconnect.png b/vcl/aqua/source/res/cursors/dconnect.png Binary files differnew file mode 100644 index 000000000000..e4a43bdbe021 --- /dev/null +++ b/vcl/aqua/source/res/cursors/dconnect.png diff --git a/vcl/aqua/source/res/cursors/dellipse.png b/vcl/aqua/source/res/cursors/dellipse.png Binary files differnew file mode 100644 index 000000000000..319c4574c7c1 --- /dev/null +++ b/vcl/aqua/source/res/cursors/dellipse.png diff --git a/vcl/aqua/source/res/cursors/detectiv.png b/vcl/aqua/source/res/cursors/detectiv.png Binary files differnew file mode 100644 index 000000000000..abe93f263d4d --- /dev/null +++ b/vcl/aqua/source/res/cursors/detectiv.png diff --git a/vcl/aqua/source/res/cursors/dfree.png b/vcl/aqua/source/res/cursors/dfree.png Binary files differnew file mode 100644 index 000000000000..2de92942adde --- /dev/null +++ b/vcl/aqua/source/res/cursors/dfree.png diff --git a/vcl/aqua/source/res/cursors/dline.png b/vcl/aqua/source/res/cursors/dline.png Binary files differnew file mode 100644 index 000000000000..6afb670ef8a8 --- /dev/null +++ b/vcl/aqua/source/res/cursors/dline.png diff --git a/vcl/aqua/source/res/cursors/dpie.png b/vcl/aqua/source/res/cursors/dpie.png Binary files differnew file mode 100644 index 000000000000..44a9474846b9 --- /dev/null +++ b/vcl/aqua/source/res/cursors/dpie.png diff --git a/vcl/aqua/source/res/cursors/dpolygon.png b/vcl/aqua/source/res/cursors/dpolygon.png Binary files differnew file mode 100644 index 000000000000..847e6ad9bea5 --- /dev/null +++ b/vcl/aqua/source/res/cursors/dpolygon.png diff --git a/vcl/aqua/source/res/cursors/drect.png b/vcl/aqua/source/res/cursors/drect.png Binary files differnew file mode 100644 index 000000000000..ff3dcbba07b4 --- /dev/null +++ b/vcl/aqua/source/res/cursors/drect.png diff --git a/vcl/aqua/source/res/cursors/dtext.png b/vcl/aqua/source/res/cursors/dtext.png Binary files differnew file mode 100644 index 000000000000..ee375f0e47a0 --- /dev/null +++ b/vcl/aqua/source/res/cursors/dtext.png diff --git a/vcl/aqua/source/res/cursors/fill.png b/vcl/aqua/source/res/cursors/fill.png Binary files differnew file mode 100644 index 000000000000..220641b9beb4 --- /dev/null +++ b/vcl/aqua/source/res/cursors/fill.png diff --git a/vcl/aqua/source/res/cursors/help.png b/vcl/aqua/source/res/cursors/help.png Binary files differnew file mode 100644 index 000000000000..e29c19eccd22 --- /dev/null +++ b/vcl/aqua/source/res/cursors/help.png diff --git a/vcl/aqua/source/res/cursors/hourglass.png b/vcl/aqua/source/res/cursors/hourglass.png Binary files differnew file mode 100644 index 000000000000..07bb5af73e6e --- /dev/null +++ b/vcl/aqua/source/res/cursors/hourglass.png diff --git a/vcl/aqua/source/res/cursors/hshear.png b/vcl/aqua/source/res/cursors/hshear.png Binary files differnew file mode 100644 index 000000000000..b45beded2d93 --- /dev/null +++ b/vcl/aqua/source/res/cursors/hshear.png diff --git a/vcl/aqua/source/res/cursors/linkdata.png b/vcl/aqua/source/res/cursors/linkdata.png Binary files differnew file mode 100644 index 000000000000..6432db0155b6 --- /dev/null +++ b/vcl/aqua/source/res/cursors/linkdata.png diff --git a/vcl/aqua/source/res/cursors/linkf.png b/vcl/aqua/source/res/cursors/linkf.png Binary files differnew file mode 100644 index 000000000000..e17107fec9ff --- /dev/null +++ b/vcl/aqua/source/res/cursors/linkf.png diff --git a/vcl/aqua/source/res/cursors/magnify.png b/vcl/aqua/source/res/cursors/magnify.png Binary files differnew file mode 100644 index 000000000000..4e73146b91e4 --- /dev/null +++ b/vcl/aqua/source/res/cursors/magnify.png diff --git a/vcl/aqua/source/res/cursors/mirror.png b/vcl/aqua/source/res/cursors/mirror.png Binary files differnew file mode 100644 index 000000000000..8fac93f0b6df --- /dev/null +++ b/vcl/aqua/source/res/cursors/mirror.png diff --git a/vcl/aqua/source/res/cursors/movebw.png b/vcl/aqua/source/res/cursors/movebw.png Binary files differnew file mode 100644 index 000000000000..63bf76ad3942 --- /dev/null +++ b/vcl/aqua/source/res/cursors/movebw.png diff --git a/vcl/aqua/source/res/cursors/movedata.png b/vcl/aqua/source/res/cursors/movedata.png Binary files differnew file mode 100644 index 000000000000..60ece8a53e59 --- /dev/null +++ b/vcl/aqua/source/res/cursors/movedata.png diff --git a/vcl/aqua/source/res/cursors/movedlnk.png b/vcl/aqua/source/res/cursors/movedlnk.png Binary files differnew file mode 100644 index 000000000000..6951cd718d97 --- /dev/null +++ b/vcl/aqua/source/res/cursors/movedlnk.png diff --git a/vcl/aqua/source/res/cursors/movef.png b/vcl/aqua/source/res/cursors/movef.png Binary files differnew file mode 100644 index 000000000000..97a01c88fa4d --- /dev/null +++ b/vcl/aqua/source/res/cursors/movef.png diff --git a/vcl/aqua/source/res/cursors/movef2.png b/vcl/aqua/source/res/cursors/movef2.png Binary files differnew file mode 100644 index 000000000000..2cdddb410aae --- /dev/null +++ b/vcl/aqua/source/res/cursors/movef2.png diff --git a/vcl/aqua/source/res/cursors/moveflnk.png b/vcl/aqua/source/res/cursors/moveflnk.png Binary files differnew file mode 100644 index 000000000000..53301ba58c50 --- /dev/null +++ b/vcl/aqua/source/res/cursors/moveflnk.png diff --git a/vcl/aqua/source/res/cursors/movept.png b/vcl/aqua/source/res/cursors/movept.png Binary files differnew file mode 100644 index 000000000000..41945deb1916 --- /dev/null +++ b/vcl/aqua/source/res/cursors/movept.png diff --git a/vcl/aqua/source/res/cursors/neswsize.png b/vcl/aqua/source/res/cursors/neswsize.png Binary files differnew file mode 100644 index 000000000000..91b89b5803ec --- /dev/null +++ b/vcl/aqua/source/res/cursors/neswsize.png diff --git a/vcl/aqua/source/res/cursors/notallow.png b/vcl/aqua/source/res/cursors/notallow.png Binary files differnew file mode 100644 index 000000000000..df770a495194 --- /dev/null +++ b/vcl/aqua/source/res/cursors/notallow.png diff --git a/vcl/aqua/source/res/cursors/nullptr.png b/vcl/aqua/source/res/cursors/nullptr.png Binary files differnew file mode 100644 index 000000000000..489636595bec --- /dev/null +++ b/vcl/aqua/source/res/cursors/nullptr.png diff --git a/vcl/aqua/source/res/cursors/nwsesize.png b/vcl/aqua/source/res/cursors/nwsesize.png Binary files differnew file mode 100644 index 000000000000..fc6a33288ef2 --- /dev/null +++ b/vcl/aqua/source/res/cursors/nwsesize.png diff --git a/vcl/aqua/source/res/cursors/pen.png b/vcl/aqua/source/res/cursors/pen.png Binary files differnew file mode 100644 index 000000000000..81b583086778 --- /dev/null +++ b/vcl/aqua/source/res/cursors/pen.png diff --git a/vcl/aqua/source/res/cursors/pivotcol.png b/vcl/aqua/source/res/cursors/pivotcol.png Binary files differnew file mode 100644 index 000000000000..1c38b915b886 --- /dev/null +++ b/vcl/aqua/source/res/cursors/pivotcol.png diff --git a/vcl/aqua/source/res/cursors/pivotdel.png b/vcl/aqua/source/res/cursors/pivotdel.png Binary files differnew file mode 100644 index 000000000000..fbd663ee36c1 --- /dev/null +++ b/vcl/aqua/source/res/cursors/pivotdel.png diff --git a/vcl/aqua/source/res/cursors/pivotfld.png b/vcl/aqua/source/res/cursors/pivotfld.png Binary files differnew file mode 100644 index 000000000000..04375de1efe6 --- /dev/null +++ b/vcl/aqua/source/res/cursors/pivotfld.png diff --git a/vcl/aqua/source/res/cursors/pivotrow.png b/vcl/aqua/source/res/cursors/pivotrow.png Binary files differnew file mode 100644 index 000000000000..18ef0e8e59ba --- /dev/null +++ b/vcl/aqua/source/res/cursors/pivotrow.png diff --git a/vcl/aqua/source/res/cursors/pntbrsh.png b/vcl/aqua/source/res/cursors/pntbrsh.png Binary files differnew file mode 100644 index 000000000000..ec8d799f66c2 --- /dev/null +++ b/vcl/aqua/source/res/cursors/pntbrsh.png diff --git a/vcl/aqua/source/res/cursors/rotate.png b/vcl/aqua/source/res/cursors/rotate.png Binary files differnew file mode 100644 index 000000000000..a8137e077e56 --- /dev/null +++ b/vcl/aqua/source/res/cursors/rotate.png diff --git a/vcl/aqua/source/res/cursors/tblsele.png b/vcl/aqua/source/res/cursors/tblsele.png Binary files differnew file mode 100644 index 000000000000..a2da05e009d1 --- /dev/null +++ b/vcl/aqua/source/res/cursors/tblsele.png diff --git a/vcl/aqua/source/res/cursors/tblsels.png b/vcl/aqua/source/res/cursors/tblsels.png Binary files differnew file mode 100644 index 000000000000..ba20589c794f --- /dev/null +++ b/vcl/aqua/source/res/cursors/tblsels.png diff --git a/vcl/aqua/source/res/cursors/tblselse.png b/vcl/aqua/source/res/cursors/tblselse.png Binary files differnew file mode 100644 index 000000000000..4ee7f62b304b --- /dev/null +++ b/vcl/aqua/source/res/cursors/tblselse.png diff --git a/vcl/aqua/source/res/cursors/tblselsw.png b/vcl/aqua/source/res/cursors/tblselsw.png Binary files differnew file mode 100644 index 000000000000..11d7cb34d64d --- /dev/null +++ b/vcl/aqua/source/res/cursors/tblselsw.png diff --git a/vcl/aqua/source/res/cursors/tblselw.png b/vcl/aqua/source/res/cursors/tblselw.png Binary files differnew file mode 100644 index 000000000000..62813a975855 --- /dev/null +++ b/vcl/aqua/source/res/cursors/tblselw.png diff --git a/vcl/aqua/source/res/cursors/timemove.png b/vcl/aqua/source/res/cursors/timemove.png Binary files differnew file mode 100644 index 000000000000..3dae038a2011 --- /dev/null +++ b/vcl/aqua/source/res/cursors/timemove.png diff --git a/vcl/aqua/source/res/cursors/timesize.png b/vcl/aqua/source/res/cursors/timesize.png Binary files differnew file mode 100644 index 000000000000..22178c9b8d0e --- /dev/null +++ b/vcl/aqua/source/res/cursors/timesize.png diff --git a/vcl/aqua/source/res/cursors/vshear.png b/vcl/aqua/source/res/cursors/vshear.png Binary files differnew file mode 100644 index 000000000000..b01cb6c935e1 --- /dev/null +++ b/vcl/aqua/source/res/cursors/vshear.png diff --git a/vcl/aqua/source/res/cursors/vtext.png b/vcl/aqua/source/res/cursors/vtext.png Binary files differnew file mode 100644 index 000000000000..2d6c847c9fc7 --- /dev/null +++ b/vcl/aqua/source/res/cursors/vtext.png diff --git a/vcl/aqua/source/res/delzip b/vcl/aqua/source/res/delzip new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/vcl/aqua/source/res/delzip diff --git a/vcl/aqua/source/res/makefile.mk b/vcl/aqua/source/res/makefile.mk new file mode 100644 index 000000000000..2043504450e7 --- /dev/null +++ b/vcl/aqua/source/res/makefile.mk @@ -0,0 +1,54 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# 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 +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. +TARGET=aquares + +# --- Settings ------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Files ---------------------------------------------------- + +.IF "$(OS)"!="MACOSX" + +dummy: + @echo "Nothing to build for OS $(OS)" + +.ELSE # "$(OS)"!="MACOSX" + +ZIPFLAGS = -r +ZIP1TARGET = osxres +#ZIP1DIR = +ZIP1LIST = MainMenu.nib/*.nib cursors/*.png + +# --- Targets -------------------------------------------------- + +.INCLUDE : target.mk + +.ENDIF # "$(OS)"!="MACOSX" + diff --git a/vcl/aqua/source/window/makefile.mk b/vcl/aqua/source/window/makefile.mk new file mode 100644 index 000000000000..7afbce885e4a --- /dev/null +++ b/vcl/aqua/source/window/makefile.mk @@ -0,0 +1,63 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# 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 +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=vcl +TARGET=salwin +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile2.pmk + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"!="aqua" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"!="aqua" + +SLOFILES= \ + $(SLO)/salframe.obj \ + $(SLO)/salframeview.obj \ + $(SLO)/salmenu.obj \ + $(SLO)/salnsmenu.obj \ + $(SLO)/salobj.obj + +.ENDIF # "$(GUIBASE)"!="aqua" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.INCLUDE : $(PRJ)$/util$/target.pmk diff --git a/vcl/aqua/source/window/salframe.cxx b/vcl/aqua/source/window/salframe.cxx new file mode 100644 index 000000000000..b14354e1b4bd --- /dev/null +++ b/vcl/aqua/source/window/salframe.cxx @@ -0,0 +1,1636 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string> + +#include "saldata.hxx" +#include "salgdi.h" +#include "salframe.h" +#include "salmenu.h" +#include "saltimer.h" +#include "salinst.h" +#include "salframeview.h" +#include "aqua11yfactory.h" +#include "vcl/salwtype.hxx" +#include "vcl/window.hxx" +#include "vcl/timer.hxx" + +#include "premac.h" +// needed for theming +// FIXME: move theming code to salnativewidgets.cxx +#include <Carbon/Carbon.h> +#include "postmac.h" + +#include "boost/assert.hpp" +#include "vcl/svapp.hxx" +#include "rtl/ustrbuf.hxx" +#include "osl/file.h" + +using namespace std; + +// ======================================================================= + +AquaSalFrame* AquaSalFrame::s_pCaptureFrame = NULL; + +// ======================================================================= + +AquaSalFrame::AquaSalFrame( SalFrame* pParent, ULONG salFrameStyle ) : + mpWindow(nil), + mpView(nil), + mpDockMenuEntry(nil), + mpGraphics(NULL), + mpParent(NULL), + mnMinWidth(0), + mnMinHeight(0), + mnMaxWidth(0), + mnMaxHeight(0), + mbGraphics(false), + mbFullScreen( false ), + mbShown(false), + mbInitShow(true), + mbPositioned(false), + mbSized(false), + mbPresentation( false ), + mnStyle( salFrameStyle ), + mnStyleMask( 0 ), + mnLastEventTime( 0 ), + mnLastModifierFlags( 0 ), + mpMenu( NULL ), + mnExtStyle( 0 ), + mePointerStyle( POINTER_ARROW ), + mnTrackingRectTag( 0 ), + mrClippingPath( 0 ), + mnICOptions( 0 ) +{ + maSysData.nSize = sizeof( SystemEnvData ); + + mpParent = dynamic_cast<AquaSalFrame*>(pParent); + + initWindowAndView(); + + SalData* pSalData = GetSalData(); + pSalData->maFrames.push_front( this ); + pSalData->maFrameCheck.insert( this ); +} + +// ----------------------------------------------------------------------- + +AquaSalFrame::~AquaSalFrame() +{ + // if the frame is destroyed and has the current menubar + // set the default menubar + if( mpMenu && mpMenu->mbMenuBar && AquaSalMenu::pCurrentMenuBar == mpMenu ) + AquaSalMenu::setDefaultMenu(); + + // cleanup clipping stuff + ResetClipRegion(); + + [SalFrameView unsetMouseFrame: this]; + + SalData* pSalData = GetSalData(); + pSalData->maFrames.remove( this ); + pSalData->maFrameCheck.erase( this ); + + DBG_ASSERT( this != s_pCaptureFrame, "capture frame destroyed" ); + if( this == s_pCaptureFrame ) + s_pCaptureFrame = NULL; + + if ( mpGraphics ) + delete mpGraphics; + + if( mpDockMenuEntry ) + // life cycle comment: the menu has ownership of the item, so no release + [AquaSalInstance::GetDynamicDockMenu() removeItem: mpDockMenuEntry]; + if ( mpView ) { + [AquaA11yFactory revokeView: mpView]; + [mpView release]; + } + if ( mpWindow ) + [mpWindow release]; +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::initWindowAndView() +{ + // initialize mirroring parameters + // FIXME: screens changing + NSScreen * pScreen = [mpWindow screen]; + if( pScreen == nil ) + pScreen = [NSScreen mainScreen]; + maScreenRect = [pScreen frame]; + + // calculate some default geometry + NSRect aVisibleRect = [pScreen visibleFrame]; + CocoaToVCL( aVisibleRect ); + + maGeometry.nX = static_cast<int>(aVisibleRect.origin.x + aVisibleRect.size.width / 10); + maGeometry.nY = static_cast<int>(aVisibleRect.origin.y + aVisibleRect.size.height / 10); + maGeometry.nWidth = static_cast<unsigned int>(aVisibleRect.size.width * 0.8); + maGeometry.nHeight = static_cast<unsigned int>(aVisibleRect.size.height * 0.8); + + // calculate style mask + if( (mnStyle & SAL_FRAME_STYLE_FLOAT) || + (mnStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) + mnStyleMask = NSBorderlessWindowMask; + else if( mnStyle & SAL_FRAME_STYLE_DEFAULT ) + { + mnStyleMask = NSTitledWindowMask | + NSMiniaturizableWindowMask | + NSResizableWindowMask | + NSClosableWindowMask; + // make default window "maximized" + maGeometry.nX = static_cast<int>(aVisibleRect.origin.x); + maGeometry.nY = static_cast<int>(aVisibleRect.origin.y); + maGeometry.nWidth = static_cast<int>(aVisibleRect.size.width); + maGeometry.nHeight = static_cast<int>(aVisibleRect.size.height); + mbPositioned = mbSized = true; + } + else + { + if( (mnStyle & SAL_FRAME_STYLE_MOVEABLE) ) + { + mnStyleMask |= NSTitledWindowMask; + if( mpParent == NULL ) + mnStyleMask |= NSMiniaturizableWindowMask; + } + if( (mnStyle & SAL_FRAME_STYLE_SIZEABLE) ) + mnStyleMask |= NSResizableWindowMask; + if( (mnStyle & SAL_FRAME_STYLE_CLOSEABLE) ) + mnStyleMask |= NSClosableWindowMask; + // documentation says anything other than NSBorderlessWindowMask (=0) + // should also include NSTitledWindowMask; + if( mnStyleMask != 0 ) + mnStyleMask |= NSTitledWindowMask; + } + + mpWindow = [[SalFrameWindow alloc] initWithSalFrame: this]; + mpView = [[SalFrameView alloc] initWithSalFrame: this]; + if( (mnStyle & SAL_FRAME_STYLE_TOOLTIP) ) + [mpWindow setIgnoresMouseEvents: YES]; + else + [mpWindow setAcceptsMouseMovedEvents: YES]; + [mpWindow setHasShadow: YES]; + [mpWindow setDelegate: mpWindow]; + + NSRect aRect = { { 0,0 }, { maGeometry.nWidth, maGeometry.nHeight } }; + mnTrackingRectTag = [mpView addTrackingRect: aRect owner: mpView userData: nil assumeInside: NO]; + + maSysData.pView = mpView; + + UpdateFrameGeometry(); + + // setContentView causes a display; in multithreaded use this can deadlock + //YieldMutexReleaser aRel; + [mpWindow setContentView: mpView]; +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::CocoaToVCL( NSRect& io_rRect, bool bRelativeToScreen ) +{ + if( bRelativeToScreen ) + io_rRect.origin.y = maScreenRect.size.height - (io_rRect.origin.y+io_rRect.size.height); + else + io_rRect.origin.y = maGeometry.nHeight - (io_rRect.origin.y+io_rRect.size.height); +} + +void AquaSalFrame::VCLToCocoa( NSRect& io_rRect, bool bRelativeToScreen ) +{ + if( bRelativeToScreen ) + io_rRect.origin.y = maScreenRect.size.height - (io_rRect.origin.y+io_rRect.size.height); + else + io_rRect.origin.y = maGeometry.nHeight - (io_rRect.origin.y+io_rRect.size.height); +} + +void AquaSalFrame::CocoaToVCL( NSPoint& io_rPoint, bool bRelativeToScreen ) +{ + if( bRelativeToScreen ) + io_rPoint.y = maScreenRect.size.height - io_rPoint.y; + else + io_rPoint.y = maGeometry.nHeight - io_rPoint.y; +} + +void AquaSalFrame::VCLToCocoa( NSPoint& io_rPoint, bool bRelativeToScreen ) +{ + if( bRelativeToScreen ) + io_rPoint.y = maScreenRect.size.height - io_rPoint.y; + else + io_rPoint.y = maGeometry.nHeight - io_rPoint.y; +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::screenParametersChanged() +{ + UpdateFrameGeometry(); + + if( mpGraphics ) + mpGraphics->updateResolution(); + CallCallback( SALEVENT_DISPLAYCHANGED, 0 ); +} + +// ----------------------------------------------------------------------- + +SalGraphics* AquaSalFrame::GetGraphics() +{ + if ( mbGraphics ) + return NULL; + + if ( !mpGraphics ) + { + mpGraphics = new AquaSalGraphics; + mpGraphics->SetWindowGraphics( this ); + } + + mbGraphics = TRUE; + return mpGraphics; +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::ReleaseGraphics( SalGraphics *pGraphics ) +{ + DBG_ASSERT( pGraphics == mpGraphics, "graphics released on wrong frame" ); + mbGraphics = FALSE; +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalFrame::PostEvent( void *pData ) +{ + GetSalData()->mpFirstInstance->PostUserEvent( this, SALEVENT_USEREVENT, pData ); + return TRUE; +} + +// ----------------------------------------------------------------------- +void AquaSalFrame::SetTitle(const XubString& rTitle) +{ + NSString* pTitle = CreateNSString( rTitle ); + [mpWindow setTitle: pTitle]; + + // create an entry in the dock menu + const ULONG nAppWindowStyle = (SAL_FRAME_STYLE_CLOSEABLE | SAL_FRAME_STYLE_MOVEABLE); + if( mpParent == NULL && + (mnStyle & nAppWindowStyle) == nAppWindowStyle ) + { + if( mpDockMenuEntry == NULL ) + { + NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu(); + mpDockMenuEntry = [pDock insertItemWithTitle: pTitle + action: @selector(dockMenuItemTriggered:) + keyEquivalent: @"" + atIndex: 0]; + [mpDockMenuEntry setTarget: mpWindow]; + + // TODO: image (either the generic window image or an icon + // check mark (for "main" window ?) + } + else + [mpDockMenuEntry setTitle: pTitle]; + } + + if (pTitle) + [pTitle release]; +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetIcon( USHORT ) +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetRepresentedURL( const rtl::OUString& i_rDocURL ) +{ + if( i_rDocURL.indexOfAsciiL( "file:", 5 ) == 0 ) + { + rtl::OUString aSysPath; + osl_getSystemPathFromFileURL( i_rDocURL.pData, &aSysPath.pData ); + NSString* pStr = CreateNSString( aSysPath ); + if( pStr ) + { + [pStr autorelease]; + [mpWindow setRepresentedFilename: pStr]; + } + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::initShow() +{ + mbInitShow = false; + if( ! mbPositioned && ! mbFullScreen ) + { + Rectangle aScreenRect; + GetWorkArea( aScreenRect ); + if( mpParent ) // center relative to parent + { + // center on parent + long nNewX = mpParent->maGeometry.nX + (mpParent->maGeometry.nWidth - maGeometry.nWidth)/2; + if( nNewX < aScreenRect.Left() ) + nNewX = aScreenRect.Left(); + if( long(nNewX + maGeometry.nWidth) > aScreenRect.Right() ) + nNewX = aScreenRect.Right() - maGeometry.nWidth-1; + long nNewY = mpParent->maGeometry.nY + (mpParent->maGeometry.nHeight - maGeometry.nHeight)/2; + if( nNewY < aScreenRect.Top() ) + nNewY = aScreenRect.Top(); + if( nNewY > aScreenRect.Bottom() ) + nNewY = aScreenRect.Bottom() - maGeometry.nHeight-1; + SetPosSize( nNewX - mpParent->maGeometry.nX, + nNewY - mpParent->maGeometry.nY, + 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ); + } + else if( ! (mnStyle & SAL_FRAME_STYLE_SIZEABLE) ) + { + // center on screen + long nNewX = (aScreenRect.GetWidth() - maGeometry.nWidth)/2; + long nNewY = (aScreenRect.GetHeight() - maGeometry.nHeight)/2; + SetPosSize( nNewX, nNewY, 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ); + } + } + + // make sure the view is present in the wrapper list before any children receive focus + [AquaA11yFactory registerView: mpView]; +} + +void AquaSalFrame::SendPaintEvent( const Rectangle* pRect ) +{ + SalPaintEvent aPaintEvt( 0, 0, maGeometry.nWidth, maGeometry.nHeight, true ); + if( pRect ) + { + aPaintEvt.mnBoundX = pRect->Left(); + aPaintEvt.mnBoundY = pRect->Top(); + aPaintEvt.mnBoundWidth = pRect->GetWidth(); + aPaintEvt.mnBoundHeight = pRect->GetHeight(); + } + + CallCallback(SALEVENT_PAINT, &aPaintEvt); +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::Show(BOOL bVisible, BOOL bNoActivate) +{ + mbShown = bVisible; + if(bVisible) + { + if( mbInitShow ) + initShow(); + + CallCallback(SALEVENT_RESIZE, 0); + // trigger filling our backbuffer + SendPaintEvent(); + + //YieldMutexReleaser aRel; + + if( bNoActivate || [mpWindow canBecomeKeyWindow] == NO ) + [mpWindow orderFront: NSApp]; + else + [mpWindow makeKeyAndOrderFront: NSApp]; + + if( mpParent ) + { + /* #i92674# #i96433# we do not want an invisible parent to show up (which adding a visible + child implicitly does). However we also do not want a parentless toolbar. + + HACK: try to decide when we should not insert a child to its parent + floaters and ownerdraw windows have not yet shown up in cases where + we don't want the parent to become visible + */ + if( mpParent->mbShown || (mnStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION | SAL_FRAME_STYLE_FLOAT) ) ) + { + [mpParent->mpWindow addChildWindow: mpWindow ordered: NSWindowAbove]; + } + } + + if( mbPresentation ) + [mpWindow makeMainWindow]; + } + else + { + // if the frame holding the current menubar gets hidden + // show the default menubar + if( mpMenu && mpMenu->mbMenuBar && AquaSalMenu::pCurrentMenuBar == mpMenu ) + AquaSalMenu::setDefaultMenu(); + + //YieldMutexReleaser aRel; + + // #i90440# #i94443# work around the focus going back to some other window + // if a child gets hidden for a parent window + if( mpParent && mpParent->mbShown && [mpWindow isKeyWindow] ) + [mpParent->mpWindow makeKeyAndOrderFront: NSApp]; + + [SalFrameView unsetMouseFrame: this]; + if( mpParent && [mpWindow parentWindow] == mpParent->mpWindow ) + [mpParent->mpWindow removeChildWindow: mpWindow]; + + [mpWindow orderOut: NSApp]; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::Enable( BOOL bEnable ) +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetMinClientSize( long nWidth, long nHeight ) +{ + mnMinWidth = nWidth; + mnMinHeight = nHeight; + + if( mpWindow ) + { + // Always add the decoration as the dimension concerns only + // the content rectangle + nWidth += maGeometry.nLeftDecoration + maGeometry.nRightDecoration; + nHeight += maGeometry.nTopDecoration + maGeometry.nBottomDecoration; + + NSSize aSize = { nWidth, nHeight }; + + // Size of full window (content+structure) although we only + // have the client size in arguments + [mpWindow setMinSize: aSize]; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetMaxClientSize( long nWidth, long nHeight ) +{ + mnMaxWidth = nWidth; + mnMaxHeight = nHeight; + + if( mpWindow ) + { + // Always add the decoration as the dimension concerns only + // the content rectangle + nWidth += maGeometry.nLeftDecoration + maGeometry.nRightDecoration; + nHeight += maGeometry.nTopDecoration + maGeometry.nBottomDecoration; + + // Carbon windows can't have a size greater than 32767x32767 + if (nWidth>32767) nWidth=32767; + if (nHeight>32767) nHeight=32767; + + NSSize aSize = { nWidth, nHeight }; + + // Size of full window (content+structure) although we only + // have the client size in arguments + [mpWindow setMaxSize: aSize]; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetClientSize( long nWidth, long nHeight ) +{ + if( mpWindow ) + { + NSSize aSize = { nWidth, nHeight }; + + [mpWindow setContentSize: aSize]; + UpdateFrameGeometry(); + if( mbShown ) + // trigger filling our backbuffer + SendPaintEvent(); + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::GetClientSize( long& rWidth, long& rHeight ) +{ + if( mbShown || mbInitShow ) + { + rWidth = maGeometry.nWidth; + rHeight = maGeometry.nHeight; + } + else + { + rWidth = 0; + rHeight = 0; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetWindowState( const SalFrameState* pState ) +{ + // set normal state + NSRect aStateRect = [mpWindow frame]; + aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: mnStyleMask]; + CocoaToVCL( aStateRect ); + if( pState->mnMask & SAL_FRAMESTATE_MASK_X ) + aStateRect.origin.x = float(pState->mnX); + if( pState->mnMask & SAL_FRAMESTATE_MASK_Y ) + aStateRect.origin.y = float(pState->mnY); + if( pState->mnMask & SAL_FRAMESTATE_MASK_WIDTH ) + aStateRect.size.width = float(pState->mnWidth); + if( pState->mnMask & SAL_FRAMESTATE_MASK_HEIGHT ) + aStateRect.size.height = float(pState->mnHeight); + VCLToCocoa( aStateRect ); + aStateRect = [NSWindow frameRectForContentRect: aStateRect styleMask: mnStyleMask]; + + // relase and acquire mutex again since this call can block waiting for an internal lock + { + //YieldMutexReleaser aRel; + [mpWindow setFrame: aStateRect display: NO]; + } + + // FIXME: HTH maximized state ? + + // get new geometry + UpdateFrameGeometry(); + + USHORT nEvent = 0; + if( pState->mnMask & (SAL_FRAMESTATE_MASK_X | SAL_FRAMESTATE_MASK_X) ) + { + mbPositioned = true; + nEvent = SALEVENT_MOVE; + } + + if( pState->mnMask & (SAL_FRAMESTATE_MASK_WIDTH | SAL_FRAMESTATE_MASK_HEIGHT) ) + { + mbSized = true; + nEvent = (nEvent == SALEVENT_MOVE) ? SALEVENT_MOVERESIZE : SALEVENT_RESIZE; + } + // send event that we were moved/sized + if( nEvent ) + CallCallback( nEvent, NULL ); + + if( mbShown ) + { + // trigger filling our backbuffer + SendPaintEvent(); + + // tell the system the views need to be updated + //YieldMutexReleaser aRel; + + [mpWindow display]; + } +} + +// ----------------------------------------------------------------------- + +BOOL AquaSalFrame::GetWindowState( SalFrameState* pState ) +{ + pState->mnMask = SAL_FRAMESTATE_MASK_X | + SAL_FRAMESTATE_MASK_Y | + SAL_FRAMESTATE_MASK_WIDTH | + SAL_FRAMESTATE_MASK_HEIGHT | + #if 0 + SAL_FRAMESTATE_MASK_MAXIMIZED_X | + SAL_FRAMESTATE_MASK_MAXIMIZED_Y | + SAL_FRAMESTATE_MASK_MAXIMIZED_WIDTH | + SAL_FRAMESTATE_MASK_MAXIMIZED_HEIGHT | + #endif + SAL_FRAMESTATE_MASK_STATE; + + NSRect aStateRect = [mpWindow frame]; + aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: mnStyleMask]; + CocoaToVCL( aStateRect ); + pState->mnX = long(aStateRect.origin.x); + pState->mnY = long(aStateRect.origin.y); + pState->mnWidth = long(aStateRect.size.width); + pState->mnHeight = long(aStateRect.size.height); + + // FIXME: HTH maximized state ? + + if( [mpWindow isMiniaturized] ) + pState->mnState = SAL_FRAMESTATE_MINIMIZED; + else if( ! [mpWindow isZoomed] ) + pState->mnState = SAL_FRAMESTATE_NORMAL; + else + pState->mnState = SAL_FRAMESTATE_MAXIMIZED; + + return TRUE; +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetScreenNumber(unsigned int nScreen) +{ + NSArray* pScreens = [NSScreen screens]; + Rectangle aRet; + NSScreen* pScreen = nil; + if( pScreens && nScreen < [pScreens count] ) + { + // get new screen frame + pScreen = [pScreens objectAtIndex: nScreen]; + NSRect aNewScreen = [pScreen frame]; + + // get current screen frame + pScreen = [mpWindow screen]; + if( pScreen ) + { + NSRect aCurScreen = [pScreen frame]; + if( aCurScreen.origin.x != aNewScreen.origin.x || + aCurScreen.origin.y != aNewScreen.origin.y ) + { + NSRect aFrameRect = [mpWindow frame]; + aFrameRect.origin.x += aNewScreen.origin.x - aCurScreen.origin.x; + aFrameRect.origin.y += aNewScreen.origin.y - aCurScreen.origin.y; + [mpWindow setFrame: aFrameRect display: NO]; + UpdateFrameGeometry(); + } + } + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::ShowFullScreen( BOOL bFullScreen, sal_Int32 nDisplay ) +{ + if( mbFullScreen == bFullScreen ) + return; + + mbFullScreen = bFullScreen; + if( bFullScreen ) + { + // hide the dock and the menubar if we are on the menu screen + // which is always on index 0 according to documentation + bool bHideMenu = (nDisplay == 0); + + NSRect aNewContentRect = { { 0, 0 }, { 0, 0 } }; + // get correct screen + NSScreen* pScreen = nil; + NSArray* pScreens = [NSScreen screens]; + if( pScreens ) + { + if( nDisplay >= 0 && (unsigned int)nDisplay < [pScreens count] ) + pScreen = [pScreens objectAtIndex: nDisplay]; + else + { + // this means span all screens + bHideMenu = true; + NSEnumerator* pEnum = [pScreens objectEnumerator]; + while( (pScreen = [pEnum nextObject]) != nil ) + { + NSRect aScreenRect = [pScreen frame]; + if( aScreenRect.origin.x < aNewContentRect.origin.x ) + { + aNewContentRect.size.width += aNewContentRect.origin.x - aScreenRect.origin.x; + aNewContentRect.origin.x = aScreenRect.origin.x; + } + if( aScreenRect.origin.y < aNewContentRect.origin.y ) + { + aNewContentRect.size.height += aNewContentRect.origin.y - aScreenRect.origin.y; + aNewContentRect.origin.y = aScreenRect.origin.y; + } + if( aScreenRect.origin.x + aScreenRect.size.width > aNewContentRect.origin.x + aNewContentRect.size.width ) + aNewContentRect.size.width = aScreenRect.origin.x + aScreenRect.size.width - aNewContentRect.origin.x; + if( aScreenRect.origin.y + aScreenRect.size.height > aNewContentRect.origin.y + aNewContentRect.size.height ) + aNewContentRect.size.height = aScreenRect.origin.y + aScreenRect.size.height - aNewContentRect.origin.y; + } + } + } + if( aNewContentRect.size.width == 0 && aNewContentRect.size.height == 0 ) + { + if( pScreen == nil ) + pScreen = [mpWindow screen]; + if( pScreen == nil ) + pScreen = [NSScreen mainScreen]; + + aNewContentRect = [pScreen frame]; + } + + if( bHideMenu ) + [NSMenu setMenuBarVisible:NO]; + + maFullScreenRect = [mpWindow frame]; + { + //YieldMutexReleaser aRel; + [mpWindow setFrame: [NSWindow frameRectForContentRect: aNewContentRect styleMask: mnStyleMask] display: mbShown ? YES : NO]; + } + + UpdateFrameGeometry(); + + if( mbShown ) + CallCallback( SALEVENT_MOVERESIZE, NULL ); + } + else + { + { + //YieldMutexReleaser aRel; + [mpWindow setFrame: maFullScreenRect display: mbShown ? YES : NO]; + } + UpdateFrameGeometry(); + + if( mbShown ) + CallCallback( SALEVENT_MOVERESIZE, NULL ); + + // show the dock and the menubar + [NSMenu setMenuBarVisible:YES]; + } + if( mbShown ) + // trigger filling our backbuffer + SendPaintEvent(); +} + +// ----------------------------------------------------------------------- + +class PreventSleepTimer : public AutoTimer +{ +public: + PreventSleepTimer() + { + SetTimeout( 30000 ); + Start(); + } + + virtual ~PreventSleepTimer() + { + } + + virtual void Timeout() + { + UpdateSystemActivity(OverallAct); + } +}; + +void AquaSalFrame::StartPresentation( BOOL bStart ) +{ + if( bStart ) + { + mpActivityTimer.reset( new PreventSleepTimer() ); + [mpWindow setLevel: NSScreenSaverWindowLevel]; + if( mbShown ) + [mpWindow makeMainWindow]; + } + else + { + mpActivityTimer.reset(); + [mpWindow setLevel: NSNormalWindowLevel]; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetAlwaysOnTop( BOOL bOnTop ) +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::ToTop(USHORT nFlags) +{ + if( ! (nFlags & SAL_FRAME_TOTOP_RESTOREWHENMIN) ) + { + if( ! [mpWindow isVisible] || [mpWindow isMiniaturized] ) + return; + } + if( nFlags & SAL_FRAME_TOTOP_GRABFOCUS ) + [mpWindow makeKeyAndOrderFront: NSApp]; + else + [mpWindow orderFront: NSApp]; +} + +// ----------------------------------------------------------------------- + +NSCursor* AquaSalFrame::getCurrentCursor() const +{ + NSCursor* pCursor = nil; + switch( mePointerStyle ) + { + case POINTER_TEXT: pCursor = [NSCursor IBeamCursor]; break; + case POINTER_CROSS: pCursor = [NSCursor crosshairCursor]; break; + case POINTER_HAND: + case POINTER_MOVE: pCursor = [NSCursor openHandCursor]; break; + case POINTER_NSIZE: pCursor = [NSCursor resizeUpCursor]; break; + case POINTER_SSIZE: pCursor = [NSCursor resizeDownCursor]; break; + case POINTER_ESIZE: pCursor = [NSCursor resizeRightCursor]; break; + case POINTER_WSIZE: pCursor = [NSCursor resizeLeftCursor]; break; + case POINTER_ARROW: pCursor = [NSCursor arrowCursor]; break; + case POINTER_VSPLIT: + case POINTER_VSIZEBAR: + case POINTER_WINDOW_NSIZE: + case POINTER_WINDOW_SSIZE: + pCursor = [NSCursor resizeUpDownCursor]; break; + case POINTER_HSPLIT: + case POINTER_HSIZEBAR: + case POINTER_WINDOW_ESIZE: + case POINTER_WINDOW_WSIZE: + pCursor = [NSCursor resizeLeftRightCursor]; break; + case POINTER_REFHAND: pCursor = [NSCursor pointingHandCursor]; break; + + default: + pCursor = GetSalData()->getCursor( mePointerStyle ); + if( pCursor == nil ) + { + DBG_ERROR( "unmapped cursor" ); + pCursor = [NSCursor arrowCursor]; + } + break; + } + return pCursor; +} + +void AquaSalFrame::SetPointer( PointerStyle ePointerStyle ) +{ + if( ePointerStyle >= POINTER_COUNT || ePointerStyle == mePointerStyle ) + return; + mePointerStyle = ePointerStyle; + + [mpWindow invalidateCursorRectsForView: mpView]; +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetPointerPos( long nX, long nY ) +{ + // FIXME: use Cocoa functions + + // FIXME: multiscreen support + CGPoint aPoint = { nX + maGeometry.nX, nY + maGeometry.nY }; + CGDirectDisplayID mainDisplayID = CGMainDisplayID(); + CGDisplayMoveCursorToPoint( mainDisplayID, aPoint ); +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::Flush( void ) +{ + if( !(mbGraphics && mpGraphics && mpView && mbShown) ) + return; + + [mpView setNeedsDisplay: YES]; + + // outside of the application's event loop (e.g. IntroWindow) + // nothing would trigger paint event handling + // => fall back to synchronous painting + if( ImplGetSVData()->maAppData.mnDispatchLevel <= 0 ) + { + [mpView display]; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::Flush( const Rectangle& rRect ) +{ + if( !(mbGraphics && mpGraphics && mpView && mbShown) ) + return; + + NSRect aNSRect = { {rRect.Left(), rRect.Top()}, { rRect.GetWidth(), rRect.GetHeight() } }; + VCLToCocoa( aNSRect, false ); + [mpView setNeedsDisplayInRect: aNSRect]; + + // outside of the application's event loop (e.g. IntroWindow) + // nothing would trigger paint event handling + // => fall back to synchronous painting + if( ImplGetSVData()->maAppData.mnDispatchLevel <= 0 ) + { + [mpView display]; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::Sync() +{ + if( mbGraphics && mpGraphics && mpView && mbShown ) + { + //YieldMutexReleaser aRel; + + [mpView setNeedsDisplay: YES]; + [mpView display]; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetInputContext( SalInputContext* pContext ) +{ + if (!pContext) + { + mnICOptions = 0; + return; + } + + mnICOptions = pContext->mnOptions; + + if(!(pContext->mnOptions & SAL_INPUTCONTEXT_TEXT)) + return; +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::EndExtTextInput( USHORT nFlags ) +{ +} + +// ----------------------------------------------------------------------- + +XubString AquaSalFrame::GetKeyName( USHORT nKeyCode ) +{ + static std::map< USHORT, rtl::OUString > aKeyMap; + if( aKeyMap.empty() ) + { + USHORT i; + for( i = KEY_A; i <= KEY_Z; i++ ) + aKeyMap[ i ] = rtl::OUString( sal_Unicode( 'A' + (i - KEY_A) ) ); + for( i = KEY_0; i <= KEY_9; i++ ) + aKeyMap[ i ] = rtl::OUString( sal_Unicode( '0' + (i - KEY_0) ) ); + for( i = KEY_F1; i <= KEY_F26; i++ ) + { + rtl::OUStringBuffer aKey( 3 ); + aKey.append( sal_Unicode( 'F' ) ); + aKey.append( sal_Int32( i - KEY_F1 + 1 ) ); + aKeyMap[ i ] = aKey.makeStringAndClear(); + } + + aKeyMap[ KEY_DOWN ] = rtl::OUString( sal_Unicode( 0x21e3 ) ); + aKeyMap[ KEY_UP ] = rtl::OUString( sal_Unicode( 0x21e1 ) ); + aKeyMap[ KEY_LEFT ] = rtl::OUString( sal_Unicode( 0x21e0 ) ); + aKeyMap[ KEY_RIGHT ] = rtl::OUString( sal_Unicode( 0x21e2 ) ); + aKeyMap[ KEY_HOME ] = rtl::OUString( sal_Unicode( 0x2196 ) ); + aKeyMap[ KEY_END ] = rtl::OUString( sal_Unicode( 0x2198 ) ); + aKeyMap[ KEY_PAGEUP ] = rtl::OUString( sal_Unicode( 0x21de ) ); + aKeyMap[ KEY_PAGEDOWN ] = rtl::OUString( sal_Unicode( 0x21df ) ); + aKeyMap[ KEY_RETURN ] = rtl::OUString( sal_Unicode( 0x21a9 ) ); + aKeyMap[ KEY_ESCAPE ] = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "esc" ) ); + aKeyMap[ KEY_TAB ] = rtl::OUString( sal_Unicode( 0x21e5 ) ); + aKeyMap[ KEY_BACKSPACE ]= rtl::OUString( sal_Unicode( 0x232b ) ); + aKeyMap[ KEY_SPACE ] = rtl::OUString( sal_Unicode( 0x2423 ) ); + aKeyMap[ KEY_DELETE ] = rtl::OUString( sal_Unicode( 0x2326 ) ); + aKeyMap[ KEY_ADD ] = rtl::OUString( sal_Unicode( '+' ) ); + aKeyMap[ KEY_SUBTRACT ] = rtl::OUString( sal_Unicode( '-' ) ); + aKeyMap[ KEY_DIVIDE ] = rtl::OUString( sal_Unicode( '/' ) ); + aKeyMap[ KEY_MULTIPLY ] = rtl::OUString( sal_Unicode( '*' ) ); + aKeyMap[ KEY_POINT ] = rtl::OUString( sal_Unicode( '.' ) ); + aKeyMap[ KEY_COMMA ] = rtl::OUString( sal_Unicode( ',' ) ); + aKeyMap[ KEY_LESS ] = rtl::OUString( sal_Unicode( '<' ) ); + aKeyMap[ KEY_GREATER ] = rtl::OUString( sal_Unicode( '>' ) ); + aKeyMap[ KEY_EQUAL ] = rtl::OUString( sal_Unicode( '=' ) ); + aKeyMap[ KEY_OPEN ] = rtl::OUString( sal_Unicode( 0x23cf ) ); + + /* yet unmapped KEYCODES: + aKeyMap[ KEY_INSERT ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_CUT ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_COPY ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_PASTE ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_UNDO ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_REPEAT ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_FIND ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_PROPERTIES ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_FRONT ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_CONTEXTMENU ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_MENU ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_HELP ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_HANGUL_HANJA ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_DECIMAL ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_TILDE ] = rtl::OUString( sal_Unicode( ) ); + aKeyMap[ KEY_QUOTELEFT ]= rtl::OUString( sal_Unicode( ) ); + */ + + } + + rtl::OUStringBuffer aResult( 16 ); + + USHORT nUnmodifiedCode = (nKeyCode & KEY_CODE); + std::map< USHORT, rtl::OUString >::const_iterator it = aKeyMap.find( nUnmodifiedCode ); + if( it != aKeyMap.end() ) + { + if( (nKeyCode & KEY_SHIFT) != 0 ) + aResult.append( sal_Unicode( 0x21e7 ) ); + if( (nKeyCode & KEY_MOD1) != 0 ) + aResult.append( sal_Unicode( 0x2318 ) ); + // we do not really handle Alt (see below) + // we map it to MOD3, whichis actually Command + if( (nKeyCode & (KEY_MOD2|KEY_MOD3)) != 0 ) + aResult.append( sal_Unicode( 0x2303 ) ); + + aResult.append( it->second ); + } + + return aResult.makeStringAndClear(); +} + +// ----------------------------------------------------------------------- + +XubString AquaSalFrame::GetSymbolKeyName( const XubString&, USHORT nKeyCode ) +{ + return GetKeyName( nKeyCode ); +} + +// ----------------------------------------------------------------------- + +static void getAppleScrollBarVariant(void) +{ + bool bIsScrollbarDoubleMax = true; // default is DoubleMax + + CFStringRef AppleScrollBarType = CFSTR("AppleScrollBarVariant"); + if( AppleScrollBarType ) + { + CFStringRef ScrollBarVariant = ((CFStringRef)CFPreferencesCopyAppValue( AppleScrollBarType, kCFPreferencesCurrentApplication )); + if( ScrollBarVariant ) + { + if( CFGetTypeID( ScrollBarVariant ) == CFStringGetTypeID() ) + { + // TODO: check for the less important variants "DoubleMin" and "DoubleBoth" too + CFStringRef DoubleMax = CFSTR("DoubleMax"); + if (DoubleMax) + { + if ( !CFStringCompare(ScrollBarVariant, DoubleMax, kCFCompareCaseInsensitive) ) + bIsScrollbarDoubleMax = true; + else + bIsScrollbarDoubleMax = false; + CFRelease(DoubleMax); + } + } + CFRelease( ScrollBarVariant ); + } + CFRelease(AppleScrollBarType); + } + + GetSalData()->mbIsScrollbarDoubleMax = bIsScrollbarDoubleMax; + + CFStringRef jumpScroll = CFSTR("AppleScrollerPagingBehavior"); + if( jumpScroll ) + { + CFBooleanRef jumpStr = ((CFBooleanRef)CFPreferencesCopyAppValue( jumpScroll, kCFPreferencesCurrentApplication )); + if( jumpStr ) + { + if( CFGetTypeID( jumpStr ) == CFBooleanGetTypeID() ) + ImplGetSVData()->maNWFData.mbScrollbarJumpPage = (jumpStr == kCFBooleanTrue); + CFRelease( jumpStr ); + } + CFRelease( jumpScroll ); + } +} + +static Color getColor( NSColor* pSysColor, const Color& rDefault, NSWindow* pWin ) +{ + Color aRet( rDefault ); + if( pSysColor ) + { + // transform to RGB + NSColor* pRBGColor = [pSysColor colorUsingColorSpaceName: NSDeviceRGBColorSpace device: [pWin deviceDescription]]; + if( pRBGColor ) + { + float r = 0, g = 0, b = 0, a = 0; + [pRBGColor getRed: &r green: &g blue: &b alpha: &a]; + aRet = Color( int(r*255.999), int(g*255.999), int(b*255.999) ); + /* + do not release here; leads to duplicate free in yield + it seems the converted color comes out autoreleased, although this + is not documented + [pRBGColor release]; + */ + } + } + return aRet; +} + +static Font getFont( NSFont* pFont, long nDPIY, const Font& rDefault ) +{ + Font aResult( rDefault ); + if( pFont ) + { + aResult.SetName( GetOUString( [pFont familyName] ) ); + aResult.SetHeight( static_cast<int>(([pFont pointSize] * 72.0 / (float)nDPIY)+0.5) ); + aResult.SetItalic( ([pFont italicAngle] != 0.0) ? ITALIC_NORMAL : ITALIC_NONE ); + // FIMXE: bold ? + } + + return aResult; +} + +void AquaSalFrame::getResolution( long& o_rDPIX, long& o_rDPIY ) +{ + if( ! mpGraphics ) + { + GetGraphics(); + ReleaseGraphics( mpGraphics ); + } + mpGraphics->GetResolution( o_rDPIX, o_rDPIY ); +} + +// on OSX-Aqua the style settings are independent of the frame, so it does +// not really belong here. Since the connection to the Appearance_Manager +// is currently done in salnativewidgets.cxx this would be a good place. +// On the other hand VCL's platform independent code currently only asks +// SalFrames for system settings anyway, so moving the code somewhere else +// doesn't make the anything cleaner for now +void AquaSalFrame::UpdateSettings( AllSettings& rSettings ) +{ + [mpView lockFocus]; + + StyleSettings aStyleSettings = rSettings.GetStyleSettings(); + + // Background Color + Color aBackgroundColor = Color( 0xEC, 0xEC, 0xEC ); + aStyleSettings.Set3DColors( aBackgroundColor ); + aStyleSettings.SetFaceColor( aBackgroundColor ); + Color aInactiveTabColor( aBackgroundColor ); + aInactiveTabColor.DecreaseLuminance( 32 ); + aStyleSettings.SetInactiveTabColor( aInactiveTabColor ); + + aStyleSettings.SetDialogColor( aBackgroundColor ); + aStyleSettings.SetLightBorderColor( aBackgroundColor ); + Color aShadowColor( aStyleSettings.GetShadowColor() ); + aStyleSettings.SetDarkShadowColor( aShadowColor ); + aShadowColor.IncreaseLuminance( 32 ); + aStyleSettings.SetShadowColor( aShadowColor ); + + // get the system font settings + Font aAppFont = aStyleSettings.GetAppFont(); + long nDPIX = 72, nDPIY = 72; + getResolution( nDPIX, nDPIY ); + aAppFont = getFont( [NSFont systemFontOfSize: 0], nDPIY, aAppFont ); + + // TODO: better mapping of aqua<->ooo font settings + aStyleSettings.SetAppFont( aAppFont ); + aStyleSettings.SetHelpFont( aAppFont ); + aStyleSettings.SetPushButtonFont( aAppFont ); + + Font aTitleFont( getFont( [NSFont titleBarFontOfSize: 0], nDPIY, aAppFont ) ); + aStyleSettings.SetTitleFont( aTitleFont ); + aStyleSettings.SetFloatTitleFont( aTitleFont ); + + Font aMenuFont( getFont( [NSFont menuFontOfSize: 0], nDPIY, aAppFont ) ); + aStyleSettings.SetMenuFont( aMenuFont ); + + aStyleSettings.SetToolFont( aAppFont ); + + Font aLabelFont( getFont( [NSFont labelFontOfSize: 0], nDPIY, aAppFont ) ); + aStyleSettings.SetLabelFont( aLabelFont ); + aStyleSettings.SetInfoFont( aLabelFont ); + aStyleSettings.SetRadioCheckFont( aLabelFont ); + aStyleSettings.SetFieldFont( aLabelFont ); + aStyleSettings.SetGroupFont( aLabelFont ); + aStyleSettings.SetIconFont( aLabelFont ); + + Color aHighlightColor( getColor( [NSColor selectedTextBackgroundColor], + aStyleSettings.GetHighlightColor(), mpWindow ) ); + aStyleSettings.SetHighlightColor( aHighlightColor ); + Color aHighlightTextColor( getColor( [NSColor selectedTextColor], + aStyleSettings.GetHighlightTextColor(), mpWindow ) ); + aStyleSettings.SetHighlightTextColor( aHighlightTextColor ); + + Color aMenuHighlightColor( getColor( [NSColor selectedMenuItemColor], + aStyleSettings.GetMenuHighlightColor(), mpWindow ) ); + aStyleSettings.SetMenuHighlightColor( aMenuHighlightColor ); + Color aMenuHighlightTextColor( getColor( [NSColor selectedMenuItemTextColor], + aStyleSettings.GetMenuHighlightTextColor(), mpWindow ) ); + aStyleSettings.SetMenuHighlightTextColor( aMenuHighlightTextColor ); + + aStyleSettings.SetMenuColor( aBackgroundColor ); + Color aMenuTextColor( getColor( [NSColor textColor], + aStyleSettings.GetMenuTextColor(), mpWindow ) ); + aStyleSettings.SetMenuTextColor( aMenuTextColor ); + aStyleSettings.SetMenuBarTextColor( aMenuTextColor ); + + aStyleSettings.SetCursorBlinkTime( 500 ); + + // no mnemonics on aqua + aStyleSettings.SetOptions( aStyleSettings.GetOptions() | STYLE_OPTION_NOMNEMONICS ); + + getAppleScrollBarVariant(); + + // set scrollbar size + aStyleSettings.SetScrollBarSize( static_cast<long int>([NSScroller scrollerWidth]) ); + + // images in menus false for MacOSX + aStyleSettings.SetUseImagesInMenus( false ); + + rSettings.SetStyleSettings( aStyleSettings ); + + [mpView unlockFocus]; +} + +// ----------------------------------------------------------------------- + +const SystemEnvData* AquaSalFrame::GetSystemData() const +{ + return &maSysData; +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::Beep( SoundType eSoundType ) +{ + switch( eSoundType ) + { + case SOUND_DISABLE: + // don't beep + break; + default: + NSBeep(); + break; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::SetPosSize(long nX, long nY, long nWidth, long nHeight, USHORT nFlags) +{ + USHORT nEvent = 0; + + if( [mpWindow isMiniaturized] ) + [mpWindow deminiaturize: NSApp]; // expand the window + + if (nFlags & (SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y)) + { + mbPositioned = true; + nEvent = SALEVENT_MOVE; + } + + if (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT)) + { + mbSized = true; + nEvent = (nEvent == SALEVENT_MOVE) ? SALEVENT_MOVERESIZE : SALEVENT_RESIZE; + } + + NSRect aFrameRect = [mpWindow frame]; + NSRect aContentRect = [NSWindow contentRectForFrameRect: aFrameRect styleMask: mnStyleMask]; + + // position is always relative to parent frame + NSRect aParentContentRect; + + if( mpParent ) + { + if( Application::GetSettings().GetLayoutRTL() ) + { + if( (nFlags & SAL_FRAME_POSSIZE_WIDTH) != 0 ) + nX = mpParent->maGeometry.nWidth - nWidth-1 - nX; + else + nX = mpParent->maGeometry.nWidth - static_cast<long int>( aContentRect.size.width-1) - nX; + } + NSRect aParentFrameRect = [mpParent->mpWindow frame]; + aParentContentRect = [NSWindow contentRectForFrameRect: aParentFrameRect styleMask: mpParent->mnStyleMask]; + } + else + aParentContentRect = maScreenRect; // use screen if no parent + + CocoaToVCL( aContentRect ); + CocoaToVCL( aParentContentRect ); + + bool bPaint = false; + if( (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT)) != 0 ) + { + if( nWidth != aContentRect.size.width || nHeight != aContentRect.size.height ) + bPaint = true; + } + + // use old window pos if no new pos requested + if( (nFlags & SAL_FRAME_POSSIZE_X) != 0 ) + aContentRect.origin.x = nX + aParentContentRect.origin.x; + if( (nFlags & SAL_FRAME_POSSIZE_Y) != 0) + aContentRect.origin.y = nY + aParentContentRect.origin.y; + + // use old size if no new size requested + if( (nFlags & SAL_FRAME_POSSIZE_WIDTH) != 0 ) + aContentRect.size.width = nWidth; + if( (nFlags & SAL_FRAME_POSSIZE_HEIGHT) != 0) + aContentRect.size.height = nHeight; + + VCLToCocoa( aContentRect ); + + // do not display yet, we need to update our backbuffer + { + //YieldMutexReleaser aRel; + [mpWindow setFrame: [NSWindow frameRectForContentRect: aContentRect styleMask: mnStyleMask] display: NO]; + } + + UpdateFrameGeometry(); + + if (nEvent) + CallCallback(nEvent, NULL); + + if( mbShown && bPaint ) + { + // trigger filling our backbuffer + SendPaintEvent(); + + // now inform the system that the views need to be drawn + //YieldMutexReleaser aRel; + [mpWindow display]; + } +} + +void AquaSalFrame::GetWorkArea( Rectangle& rRect ) +{ + NSScreen* pScreen = [mpWindow screen]; + if( pScreen == nil ) + pScreen = [NSScreen mainScreen]; + NSRect aRect = [pScreen visibleFrame]; + CocoaToVCL( aRect ); + rRect.nLeft = static_cast<long>(aRect.origin.x); + rRect.nTop = static_cast<long>(aRect.origin.y); + rRect.nRight = static_cast<long>(aRect.origin.x + aRect.size.width - 1); + rRect.nBottom = static_cast<long>(aRect.origin.y + aRect.size.height - 1); +} + +SalPointerState AquaSalFrame::GetPointerState() +{ + SalPointerState state; + state.mnState = 0; + + // get position + NSPoint aPt = [mpWindow mouseLocationOutsideOfEventStream]; + CocoaToVCL( aPt, false ); + state.maPos = Point(static_cast<long>(aPt.x), static_cast<long>(aPt.y)); + + NSEvent* pCur = [NSApp currentEvent]; + bool bMouseEvent = false; + if( pCur ) + { + bMouseEvent = true; + switch( [pCur type] ) + { + case NSLeftMouseDown: state.mnState |= MOUSE_LEFT; break; + case NSLeftMouseUp: break; + case NSRightMouseDown: state.mnState |= MOUSE_RIGHT; break; + case NSRightMouseUp: break; + case NSOtherMouseDown: state.mnState |= ([pCur buttonNumber] == 2) ? MOUSE_MIDDLE : 0; break; + case NSOtherMouseUp: break; + case NSMouseMoved: break; + case NSLeftMouseDragged: state.mnState |= MOUSE_LEFT; break; + case NSRightMouseDragged: state.mnState |= MOUSE_RIGHT; break; + case NSOtherMouseDragged: state.mnState |= ([pCur buttonNumber] == 2) ? MOUSE_MIDDLE : 0; break; + break; + default: + bMouseEvent = false; + break; + } + } + if( bMouseEvent ) + { + unsigned int nMask = (unsigned int)[pCur modifierFlags]; + if( (nMask & NSShiftKeyMask) != 0 ) + state.mnState |= KEY_SHIFT; + if( (nMask & NSControlKeyMask) != 0 ) + state.mnState |= KEY_MOD3; + if( (nMask & NSAlternateKeyMask) != 0 ) + state.mnState |= KEY_MOD2; + if( (nMask & NSCommandKeyMask) != 0 ) + state.mnState |= KEY_MOD1; + + } + else + { + // FIXME: replace Carbon by Cocoa + // Cocoa does not have an equivalent for GetCurrentEventButtonState + // and GetCurrentEventKeyModifiers. + // we could try to get away with tracking all events for modifierKeys + // and all mouse events for button state in VCL_NSApllication::sendEvent, + // but it is unclear whether this will get us the same result. + // leave in GetCurrentEventButtonState and GetCurrentEventKeyModifiers for now + + // fill in button state + UInt32 nState = GetCurrentEventButtonState(); + state.mnState = 0; + if( nState & 1 ) + state.mnState |= MOUSE_LEFT; // primary button + if( nState & 2 ) + state.mnState |= MOUSE_RIGHT; // secondary button + if( nState & 4 ) + state.mnState |= MOUSE_MIDDLE; // tertiary button + + // fill in modifier state + nState = GetCurrentEventKeyModifiers(); + if( nState & shiftKey ) + state.mnState |= KEY_SHIFT; + if( nState & controlKey ) + state.mnState |= KEY_MOD3; + if( nState & optionKey ) + state.mnState |= KEY_MOD2; + if( nState & cmdKey ) + state.mnState |= KEY_MOD1; + } + + + return state; +} + +bool AquaSalFrame::SetPluginParent( SystemParentData* pNewParent ) +{ + // plugin parent may be killed unexpectedly by + // plugging process; + + //TODO: implement + return sal_False; +} + +BOOL AquaSalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , KeyCode& ) +{ + // not supported yet + return FALSE; +} + +LanguageType AquaSalFrame::GetInputLanguage() +{ + //TODO: implement + return LANGUAGE_DONTKNOW; +} + +void AquaSalFrame::DrawMenuBar() +{ +} + +void AquaSalFrame::SetMenu( SalMenu* pSalMenu ) +{ + AquaSalMenu* pMenu = static_cast<AquaSalMenu*>(pSalMenu); + DBG_ASSERT( ! pMenu || pMenu->mbMenuBar, "setting non menubar on frame" ); + mpMenu = pMenu; + if( mpMenu ) + mpMenu->setMainMenu(); +} + +void AquaSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle ) +{ + if( (mnExtStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) != (nStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) ) + [mpWindow setDocumentEdited: (nStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) ? YES : NO]; + mnExtStyle = nStyle; +} + +void AquaSalFrame::SetBackgroundBitmap( SalBitmap* ) +{ + //TODO: implement +} + +SalBitmap* AquaSalFrame::SnapShot() +{ + return mpGraphics ? mpGraphics->getBitmap( 0, 0, maGeometry.nWidth, maGeometry.nHeight ) : NULL; +} + +SalFrame* AquaSalFrame::GetParent() const +{ + return mpParent; +} + +void AquaSalFrame::SetParent( SalFrame* pNewParent ) +{ + bool bShown = mbShown; + // remove from child list + Show( FALSE ); + mpParent = (AquaSalFrame*)pNewParent; + // insert to correct parent and paint + Show( bShown ); +} + +void AquaSalFrame::UpdateFrameGeometry() +{ + // keep in mind that view and window coordinates are lower left + // whereas vcl's are upper left + + // update screen rect + NSScreen * pScreen = [mpWindow screen]; + if( pScreen ) + { + maScreenRect = [pScreen frame]; + NSArray* pScreens = [NSScreen screens]; + if( pScreens ) + maGeometry.nScreenNumber = [pScreens indexOfObject: pScreen]; + } + + NSRect aFrameRect = [mpWindow frame]; + NSRect aContentRect = [NSWindow contentRectForFrameRect: aFrameRect styleMask: mnStyleMask]; + + // release old track rect + [mpView removeTrackingRect: mnTrackingRectTag]; + // install the new track rect + NSRect aTrackRect = { { 0, 0 }, aContentRect.size }; + mnTrackingRectTag = [mpView addTrackingRect: aTrackRect owner: mpView userData: nil assumeInside: NO]; + + // convert to vcl convention + CocoaToVCL( aFrameRect ); + CocoaToVCL( aContentRect ); + + maGeometry.nX = static_cast<int>(aContentRect.origin.x); + maGeometry.nY = static_cast<int>(aContentRect.origin.y); + + maGeometry.nLeftDecoration = static_cast<unsigned int>(aContentRect.origin.x - aFrameRect.origin.x); + maGeometry.nRightDecoration = static_cast<unsigned int>((aFrameRect.origin.x + aFrameRect.size.width) - + (aContentRect.origin.x + aContentRect.size.width)); + + maGeometry.nTopDecoration = static_cast<unsigned int>(aContentRect.origin.y - aFrameRect.origin.y); + maGeometry.nBottomDecoration = static_cast<unsigned int>((aFrameRect.origin.y + aFrameRect.size.height) - + (aContentRect.origin.y + aContentRect.size.height)); + + maGeometry.nWidth = static_cast<unsigned int>(aContentRect.size.width); + maGeometry.nHeight = static_cast<unsigned int>(aContentRect.size.height); +} + +// ----------------------------------------------------------------------- + +void AquaSalFrame::CaptureMouse( BOOL bCapture ) +{ + /* Remark: + we'll try to use a pidgin version of capture mouse + on MacOSX (neither carbon nor cocoa) there is a + CaptureMouse equivalent (in Carbon there is TrackMouseLocation + but this is useless to use since it is blocking) + + However on cocoa the active frame seems to get mouse events + also outside the window, so we'll try to forward mouse events + to the capture frame in the hope that one of our frames + gets a mouse event. + + This will break as soon as the user activates another app, but + a mouse click will normally lead to a release of the mouse anyway. + + Let's see how far we get this way. Alternatively we could use one + large overlay window like we did for the carbon implementation, + however that is resource intensive. + */ + + if( bCapture ) + s_pCaptureFrame = this; + else if( ! bCapture && s_pCaptureFrame == this ) + s_pCaptureFrame = NULL; +} + +void AquaSalFrame::ResetClipRegion() +{ + // release old path and indicate no clipping + CGPathRelease( mrClippingPath ); + mrClippingPath = NULL; + + if( mpView && mbShown ) + [mpView setNeedsDisplay: YES]; + if( mpWindow ) + { + [mpWindow setOpaque: YES]; + [mpWindow invalidateShadow]; + } +} + +void AquaSalFrame::BeginSetClipRegion( ULONG nRects ) +{ + // release old path + if( mrClippingPath ) + { + CGPathRelease( mrClippingPath ); + mrClippingPath = NULL; + } + + if( maClippingRects.size() > SAL_CLIPRECT_COUNT && nRects < maClippingRects.size() ) + { + std::vector<CGRect> aEmptyVec; + maClippingRects.swap( aEmptyVec ); + } + maClippingRects.clear(); + maClippingRects.reserve( nRects ); +} + +void AquaSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + if( nWidth && nHeight ) + { + NSRect aRect = { { nX, nY }, { nWidth, nHeight } }; + VCLToCocoa( aRect, false ); + maClippingRects.push_back( CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height) ); + } +} + +void AquaSalFrame::EndSetClipRegion() +{ + if( ! maClippingRects.empty() ) + { + mrClippingPath = CGPathCreateMutable(); + CGPathAddRects( mrClippingPath, NULL, &maClippingRects[0], maClippingRects.size() ); + } + if( mpView && mbShown ) + [mpView setNeedsDisplay: YES]; + if( mpWindow ) + { + [mpWindow setOpaque: (mrClippingPath != NULL) ? NO : YES]; + [mpWindow setBackgroundColor: [NSColor clearColor]]; + // shadow is invalidated when view gets drawn again + } +} + diff --git a/vcl/aqua/source/window/salframeview.mm b/vcl/aqua/source/window/salframeview.mm new file mode 100755 index 000000000000..25dadf0e592b --- /dev/null +++ b/vcl/aqua/source/window/salframeview.mm @@ -0,0 +1,1609 @@ +/*n*********************************************************************** + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "salgdi.h" +#include "salframe.h" +#include "salframeview.h" +#include "aqua11yfactory.h" +#include <sal/alloca.h> +#include "vcl/window.hxx" + +#include "vcl/svapp.hxx" + +#define WHEEL_EVENT_FACTOR 1.5 + +static USHORT ImplGetModifierMask( unsigned int nMask ) +{ + USHORT nRet = 0; + if( (nMask & NSShiftKeyMask) != 0 ) + nRet |= KEY_SHIFT; + if( (nMask & NSControlKeyMask) != 0 ) + nRet |= KEY_MOD3; + if( (nMask & NSAlternateKeyMask) != 0 ) + nRet |= KEY_MOD2; + if( (nMask & NSCommandKeyMask) != 0 ) + nRet |= KEY_MOD1; + return nRet; +} + +static USHORT ImplMapCharCode( sal_Unicode aCode ) +{ + static USHORT aKeyCodeMap[ 128 ] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + KEY_BACKSPACE, KEY_TAB, KEY_RETURN, 0, 0, KEY_RETURN, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, KEY_TAB, 0, KEY_ESCAPE, 0, 0, 0, 0, + KEY_SPACE, 0, 0, 0, 0, 0, 0, 0, + 0, 0, KEY_MULTIPLY, KEY_ADD, KEY_COMMA, KEY_SUBTRACT, KEY_POINT, KEY_DIVIDE, + KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, + KEY_8, KEY_9, 0, 0, KEY_LESS, KEY_EQUAL, KEY_GREATER, 0, + 0, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, + KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, + KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, + KEY_X, KEY_Y, KEY_Z, 0, 0, 0, 0, 0, + KEY_QUOTELEFT, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, + KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, + KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, + KEY_X, KEY_Y, KEY_Z, 0, 0, 0, KEY_TILDE, KEY_BACKSPACE + }; + + // Note: the mapping 0x7f should by rights be KEY_DELETE + // however if you press "backspace" 0x7f is reported + // whereas for "delete" 0xf728 gets reported + + // Note: the mapping of 0x19 to KEY_TAB is because for unknown reasons + // tab alone is reported as 0x09 (as expected) but shift-tab is + // reported as 0x19 (end of medium) + + static USHORT aFunctionKeyCodeMap[ 128 ] = + { + KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_F1, KEY_F2, KEY_F3, KEY_F4, + KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, + KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, + KEY_F21, KEY_F22, KEY_F23, KEY_F24, KEY_F25, KEY_F26, 0, 0, + 0, 0, 0, 0, 0, 0, 0, KEY_INSERT, + KEY_DELETE, KEY_HOME, 0, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN, 0, 0, + 0, 0, 0, 0, 0, KEY_MENU, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, KEY_UNDO, KEY_REPEAT, KEY_FIND, KEY_HELP, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + USHORT nKeyCode = 0; + if( aCode < sizeof( aKeyCodeMap) / sizeof( aKeyCodeMap[0] ) ) + nKeyCode = aKeyCodeMap[ aCode ]; + else if( aCode >= 0xf700 && aCode < 0xf780 ) + nKeyCode = aFunctionKeyCodeMap[ aCode - 0xf700 ]; + return nKeyCode; +} + +// store the frame the mouse last entered +static AquaSalFrame* s_pMouseFrame = NULL; +// store the last pressed button for enter/exit events +// which lack that information +static USHORT s_nLastButton = 0; + +// combinations of keys we need to handle ourselves +static const struct ExceptionalKey +{ + const USHORT nKeyCode; + const unsigned int nModifierMask; +} aExceptionalKeys[] = +{ + { KEY_D, NSControlKeyMask | NSShiftKeyMask | NSAlternateKeyMask }, + { KEY_D, NSCommandKeyMask | NSShiftKeyMask | NSAlternateKeyMask } +}; + +static AquaSalFrame* getMouseContainerFrame() +{ + int nWindows = 0; + NSCountWindows( &nWindows ); + int* pWindows = (int*)alloca( nWindows * sizeof(int) ); + // note: NSWindowList is supposed to be in z-order front to back + NSWindowList( nWindows, pWindows ); + AquaSalFrame* pDispatchFrame = NULL; + for(int i = 0; i < nWindows && ! pDispatchFrame; i++ ) + { + NSWindow* pWin = [NSApp windowWithWindowNumber: pWindows[i]]; + if( pWin && [pWin isMemberOfClass: [SalFrameWindow class]] && [(SalFrameWindow*)pWin containsMouse] ) + pDispatchFrame = [(SalFrameWindow*)pWin getSalFrame]; + } + return pDispatchFrame; +} + +@implementation SalFrameWindow +-(id)initWithSalFrame: (AquaSalFrame*)pFrame +{ + mDraggingDestinationHandler = nil; + mpFrame = pFrame; + NSRect aRect = { { pFrame->maGeometry.nX, pFrame->maGeometry.nY }, + { pFrame->maGeometry.nWidth, pFrame->maGeometry.nHeight } }; + pFrame->VCLToCocoa( aRect ); + return [super initWithContentRect: aRect styleMask: mpFrame->getStyleMask() backing: NSBackingStoreBuffered defer: NO ]; +} + +-(AquaSalFrame*)getSalFrame +{ + return mpFrame; +} + +-(MacOSBOOL)containsMouse +{ + // is this event actually inside that NSWindow ? + NSPoint aPt = [NSEvent mouseLocation]; + NSRect aFrameRect = [self frame]; + MacOSBOOL bInRect = NSPointInRect( aPt, aFrameRect ); + return bInRect; +} + +-(MacOSBOOL)canBecomeKeyWindow +{ + if( (mpFrame->mnStyle & + ( SAL_FRAME_STYLE_FLOAT | + SAL_FRAME_STYLE_TOOLTIP | + SAL_FRAME_STYLE_INTRO + )) == 0 ) + return YES; + if( (mpFrame->mnStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) != 0 ) + return YES; + if( mpFrame->mbFullScreen ) + return YES; + if( (mpFrame->mnStyle & SAL_FRAME_STYLE_FLOAT_FOCUSABLE) ) + return YES; + return [super canBecomeKeyWindow]; +} + +-(void)windowDidBecomeKey: (NSNotification*)pNotification +{ + YIELD_GUARD; + + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + { + static const ULONG nGuessDocument = SAL_FRAME_STYLE_MOVEABLE| + SAL_FRAME_STYLE_SIZEABLE| + SAL_FRAME_STYLE_CLOSEABLE; + + if( mpFrame->mpMenu ) + mpFrame->mpMenu->setMainMenu(); + else if( ! mpFrame->mpParent && + ( (mpFrame->mnStyle & nGuessDocument) == nGuessDocument || // set default menu for e.g. help + mpFrame->mbFullScreen ) ) // ser default menu for e.g. presentation + { + AquaSalMenu::setDefaultMenu(); + } + #if 0 + // FIXME: we should disable menus while in modal mode + // however from down here there is currently no reliable way to + // find out when to do this + if( (mpFrame->mpParent && mpFrame->mpParent->GetWindow()->IsInModalMode()) ) + AquaSalMenu::enableMainMenu( false ); + #endif + mpFrame->CallCallback( SALEVENT_GETFOCUS, 0 ); + mpFrame->SendPaintEvent(); // repaint controls as active + } +} + +-(void)windowDidResignKey: (NSNotification*)pNotification +{ + YIELD_GUARD; + + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + { + mpFrame->CallCallback(SALEVENT_LOSEFOCUS, 0); + mpFrame->SendPaintEvent(); // repaint controls as inactive + } +} + +-(void)windowDidChangeScreen: (NSNotification*)pNotification +{ + YIELD_GUARD; + + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + mpFrame->screenParametersChanged(); +} + +-(void)windowDidMove: (NSNotification*)pNotification +{ + YIELD_GUARD; + + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + { + mpFrame->UpdateFrameGeometry(); + mpFrame->CallCallback( SALEVENT_MOVE, 0 ); + } +} + +-(void)windowDidResize: (NSNotification*)pNotification +{ + YIELD_GUARD; + + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + { + mpFrame->UpdateFrameGeometry(); + mpFrame->CallCallback( SALEVENT_RESIZE, 0 ); + mpFrame->SendPaintEvent(); + } +} + +-(void)windowDidMiniaturize: (NSNotification*)pNotification +{ + YIELD_GUARD; + + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + { + mpFrame->mbShown = false; + mpFrame->UpdateFrameGeometry(); + mpFrame->CallCallback( SALEVENT_RESIZE, 0 ); + } +} + +-(void)windowDidDeminiaturize: (NSNotification*)pNotification +{ + YIELD_GUARD; + + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + { + mpFrame->mbShown = true; + mpFrame->UpdateFrameGeometry(); + mpFrame->CallCallback( SALEVENT_RESIZE, 0 ); + } +} + +-(MacOSBOOL)windowShouldClose: (NSNotification*)pNotification +{ + YIELD_GUARD; + + MacOSBOOL bRet = YES; + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + { + // #i84461# end possible input + mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, 0 ); + if( AquaSalFrame::isAlive( mpFrame ) ) + { + mpFrame->CallCallback( SALEVENT_CLOSE, 0 ); + bRet = NO; // application will close the window or not, AppKit shouldn't + } + } + + return bRet; +} + +-(void)dockMenuItemTriggered: (id)sender +{ + YIELD_GUARD; + + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + mpFrame->ToTop( SAL_FRAME_TOTOP_RESTOREWHENMIN | SAL_FRAME_TOTOP_GRABFOCUS ); +} + +-(::com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessibleContext >)accessibleContext +{ + return mpFrame -> GetWindow() -> GetAccessible() -> getAccessibleContext(); +} + +-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +{ + return [mDraggingDestinationHandler draggingEntered: sender]; +} + +-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender +{ + return [mDraggingDestinationHandler draggingUpdated: sender]; +} + +-(void)draggingExited:(id <NSDraggingInfo>)sender +{ + [mDraggingDestinationHandler draggingExited: sender]; +} + +-(MacOSBOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender +{ + return [mDraggingDestinationHandler prepareForDragOperation: sender]; +} + +-(MacOSBOOL)performDragOperation:(id <NSDraggingInfo>)sender +{ + return [mDraggingDestinationHandler performDragOperation: sender]; +} + +-(void)concludeDragOperation:(id <NSDraggingInfo>)sender +{ + [mDraggingDestinationHandler concludeDragOperation: sender]; +} + +-(void)registerDraggingDestinationHandler:(id)theHandler +{ + mDraggingDestinationHandler = theHandler; +} + +-(void)unregisterDraggingDestinationHandler:(id)theHandler +{ + mDraggingDestinationHandler = nil; +} + +@end + +@implementation SalFrameView ++(void)unsetMouseFrame: (AquaSalFrame*)pFrame +{ + if( pFrame == s_pMouseFrame ) + s_pMouseFrame = NULL; +} + +-(id)initWithSalFrame: (AquaSalFrame*)pFrame +{ + if ((self = [super initWithFrame: [NSWindow contentRectForFrameRect: [pFrame->getWindow() frame] styleMask: pFrame->mnStyleMask]]) != nil) + { + mDraggingDestinationHandler = nil; + mpFrame = pFrame; + mMarkedRange = NSMakeRange(NSNotFound, 0); + mSelectedRange = NSMakeRange(NSNotFound, 0); + mpReferenceWrapper = nil; + mpMouseEventListener = nil; + mpLastSuperEvent = nil; + } + + return self; +} + +-(void)resetCursorRects +{ + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + { + // FIXME: does this leak the returned NSCursor of getCurrentCursor ? + NSRect aRect = { { 0, 0 }, { mpFrame->maGeometry.nWidth, mpFrame->maGeometry.nHeight } }; + [self addCursorRect: aRect cursor: mpFrame->getCurrentCursor()]; + } +} + +-(MacOSBOOL)acceptsFirstResponder +{ + return YES; +} + +-(MacOSBOOL)acceptsFirstMouse: (NSEvent*)pEvent +{ + return YES; +} + +-(MacOSBOOL)isOpaque +{ + return mpFrame ? (mpFrame->getClipPath() != 0 ? NO : YES) : YES; +} + +// helper class similar to a vos::OGuard for the SalYieldMutex +// the difference is that it only does tryToAcquire instead of aquire +// so dreaded deadlocks like #i93512# are prevented +class TryGuard +{ +public: + TryGuard() { mbGuarded = ImplSalYieldMutexTryToAcquire(); } + ~TryGuard() { if( mbGuarded ) ImplSalYieldMutexRelease(); } + bool IsGuarded() { return mbGuarded; } +private: + bool mbGuarded; +}; + +-(void)drawRect: (NSRect)aRect +{ + // HOTFIX: #i93512# prevent deadlocks if any other thread already has the SalYieldMutex + TryGuard aTryGuard; + if( !aTryGuard.IsGuarded() ) + { + // NOTE: the mpFrame access below is not guarded yet! + // TODO: mpFrame et al need to be guarded by an independent mutex + AquaSalGraphics* pGraphics = (mpFrame && AquaSalFrame::isAlive(mpFrame)) ? mpFrame->mpGraphics : NULL; + if( pGraphics ) + { + // we did not get the mutex so we cannot draw now => request to redraw later + // convert the NSRect to a CGRect for Refreshrect() + const CGRect aCGRect = {{aRect.origin.x,aRect.origin.y},{aRect.size.width,aRect.size.height}}; + pGraphics->RefreshRect( aCGRect ); + } + return; + } + + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) + { + if( mpFrame->mpGraphics ) + { + mpFrame->mpGraphics->UpdateWindow( aRect ); + if( mpFrame->getClipPath() ) + [mpFrame->getWindow() invalidateShadow]; + } + } +} + +-(void)sendMouseEventToFrame: (NSEvent*)pEvent button:(USHORT)nButton eventtype:(USHORT)nEvent +{ + YIELD_GUARD; + + AquaSalFrame* pDispatchFrame = AquaSalFrame::GetCaptureFrame(); + bool bIsCaptured = false; + if( pDispatchFrame ) + { + bIsCaptured = true; + if( nEvent == SALEVENT_MOUSELEAVE ) // no leave events if mouse is captured + nEvent = SALEVENT_MOUSEMOVE; + } + else if( s_pMouseFrame ) + pDispatchFrame = s_pMouseFrame; + else + pDispatchFrame = mpFrame; + + /* #i81645# Cocoa reports mouse events while a button is pressed + to the window in which it was first pressed. This is reasonable and fine and + gets one around most cases where on other platforms one uses CaptureMouse or XGrabPointer, + however vcl expects mouse events to occur in the window the mouse is over, unless the + mouse is explicitly captured. So we need to find the window the mouse is actually + over for conformance with other platforms. + */ + if( ! bIsCaptured && nButton && pDispatchFrame && AquaSalFrame::isAlive( pDispatchFrame ) ) + { + // is this event actually inside that NSWindow ? + NSPoint aPt = [NSEvent mouseLocation]; + NSRect aFrameRect = [pDispatchFrame->getWindow() frame]; + + if ( ! NSPointInRect( aPt, aFrameRect ) ) + { + // no, it is not + // now we need to find the one it may be in + /* #i93756# we ant to get enumerate the application windows in z-order + to check if any contains the mouse. This could be elegantly done with this + code: + + // use NSApp to check windows in ZOrder whether they contain the mouse pointer + NSWindow* pWindow = [NSApp makeWindowsPerform: @selector(containsMouse) inOrder: YES]; + if( pWindow && [pWindow isMemberOfClass: [SalFrameWindow class]] ) + pDispatchFrame = [(SalFrameWindow*)pWindow getSalFrame]; + + However if a non SalFrameWindow is on screen (like e.g. the file dialog) + it can be hit with the containsMouse selector, which it doesn't support. + Sadly NSApplication:makeWindowsPerform does not check (for performance reasons + I assume) whether a window supports a selector before sending it. + */ + AquaSalFrame* pMouseFrame = getMouseContainerFrame(); + if( pMouseFrame ) + pDispatchFrame = pMouseFrame; + } + } + + if( pDispatchFrame && AquaSalFrame::isAlive( pDispatchFrame ) ) + { + pDispatchFrame->mnLastEventTime = static_cast<ULONG>( [pEvent timestamp] * 1000.0 ); + pDispatchFrame->mnLastModifierFlags = [pEvent modifierFlags]; + + NSPoint aPt = [NSEvent mouseLocation]; + pDispatchFrame->CocoaToVCL( aPt ); + + USHORT nModMask = ImplGetModifierMask( [pEvent modifierFlags] ); + // #i82284# emulate ctrl left + if( nModMask == KEY_MOD3 && nButton == MOUSE_LEFT ) + { + nModMask = 0; + nButton = MOUSE_RIGHT; + } + + SalMouseEvent aEvent; + aEvent.mnTime = pDispatchFrame->mnLastEventTime; + aEvent.mnX = static_cast<long>(aPt.x) - pDispatchFrame->maGeometry.nX; + aEvent.mnY = static_cast<long>(aPt.y) - pDispatchFrame->maGeometry.nY; + aEvent.mnButton = nButton; + aEvent.mnCode = aEvent.mnButton | nModMask; + + // --- RTL --- (mirror mouse pos) + if( Application::GetSettings().GetLayoutRTL() ) + aEvent.mnX = pDispatchFrame->maGeometry.nWidth-1-aEvent.mnX; + + pDispatchFrame->CallCallback( nEvent, &aEvent ); + } +} + +-(void)mouseDown: (NSEvent*)pEvent +{ + if ( mpMouseEventListener != nil && + [mpMouseEventListener respondsToSelector: @selector(mouseDown:)]) + { + [mpMouseEventListener mouseDown: [pEvent copyWithZone: NULL]]; + } + + s_nLastButton = MOUSE_LEFT; + [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SALEVENT_MOUSEBUTTONDOWN]; +} + +-(void)mouseDragged: (NSEvent*)pEvent +{ + if ( mpMouseEventListener != nil && + [mpMouseEventListener respondsToSelector: @selector(mouseDragged:)]) + { + [mpMouseEventListener mouseDragged: [pEvent copyWithZone: NULL]]; + } + s_nLastButton = MOUSE_LEFT; + [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SALEVENT_MOUSEMOVE]; +} + +-(void)mouseUp: (NSEvent*)pEvent +{ + s_nLastButton = 0; + [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SALEVENT_MOUSEBUTTONUP]; +} + +-(void)mouseMoved: (NSEvent*)pEvent +{ + s_nLastButton = 0; + [self sendMouseEventToFrame:pEvent button:0 eventtype:SALEVENT_MOUSEMOVE]; +} + +-(void)mouseEntered: (NSEvent*)pEvent +{ + s_pMouseFrame = mpFrame; + + [self sendMouseEventToFrame:pEvent button:s_nLastButton eventtype:SALEVENT_MOUSEMOVE]; +} + +-(void)mouseExited: (NSEvent*)pEvent +{ + if( s_pMouseFrame == mpFrame ) + s_pMouseFrame = NULL; + + [self sendMouseEventToFrame:pEvent button:s_nLastButton eventtype:SALEVENT_MOUSELEAVE]; +} + +-(void)rightMouseDown: (NSEvent*)pEvent +{ + s_nLastButton = MOUSE_RIGHT; + [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SALEVENT_MOUSEBUTTONDOWN]; +} + +-(void)rightMouseDragged: (NSEvent*)pEvent +{ + s_nLastButton = MOUSE_RIGHT; + [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SALEVENT_MOUSEMOVE]; +} + +-(void)rightMouseUp: (NSEvent*)pEvent +{ + s_nLastButton = 0; + [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SALEVENT_MOUSEBUTTONUP]; +} + +-(void)otherMouseDown: (NSEvent*)pEvent +{ + if( [pEvent buttonNumber] == 2 ) + { + s_nLastButton = MOUSE_MIDDLE; + [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SALEVENT_MOUSEBUTTONDOWN]; + } + else + s_nLastButton = 0; +} + +-(void)otherMouseDragged: (NSEvent*)pEvent +{ + if( [pEvent buttonNumber] == 2 ) + { + s_nLastButton = MOUSE_MIDDLE; + [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SALEVENT_MOUSEMOVE]; + } + else + s_nLastButton = 0; +} + +-(void)otherMouseUp: (NSEvent*)pEvent +{ + s_nLastButton = 0; + if( [pEvent buttonNumber] == 2 ) + [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SALEVENT_MOUSEBUTTONUP]; +} + +- (void)magnifyWithEvent: (NSEvent*)pEvent +{ + YIELD_GUARD; + + // TODO: ?? -(float)magnification; + if( AquaSalFrame::isAlive( mpFrame ) ) + { + mpFrame->mnLastEventTime = static_cast<ULONG>( [pEvent timestamp] * 1000.0 ); + mpFrame->mnLastModifierFlags = [pEvent modifierFlags]; + + float dZ = 0.0; + for(;;) + { + dZ += [pEvent deltaZ]; + NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSScrollWheelMask + untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ]; + if( !pNextEvent ) + break; + pEvent = pNextEvent; + } + + NSPoint aPt = [NSEvent mouseLocation]; + mpFrame->CocoaToVCL( aPt ); + + SalWheelMouseEvent aEvent; + aEvent.mnTime = mpFrame->mnLastEventTime; + aEvent.mnX = static_cast<long>(aPt.x) - mpFrame->maGeometry.nX; + aEvent.mnY = static_cast<long>(aPt.y) - mpFrame->maGeometry.nY; + aEvent.mnCode = ImplGetModifierMask( mpFrame->mnLastModifierFlags ); + aEvent.mnCode |= KEY_MOD1; // we want zooming, no scrolling + aEvent.mbDeltaIsPixel = TRUE; + + // --- RTL --- (mirror mouse pos) + if( Application::GetSettings().GetLayoutRTL() ) + aEvent.mnX = mpFrame->maGeometry.nWidth-1-aEvent.mnX; + + if( dZ != 0.0 ) + { + aEvent.mnDelta = static_cast<long>(floor(dZ)); + aEvent.mnNotchDelta = dZ < 0 ? -1 : 1; + if( aEvent.mnDelta == 0 ) + aEvent.mnDelta = aEvent.mnNotchDelta; + aEvent.mbHorz = FALSE; + aEvent.mnScrollLines = dZ > 0 ? dZ/WHEEL_EVENT_FACTOR : -dZ/WHEEL_EVENT_FACTOR; + if( aEvent.mnScrollLines == 0 ) + aEvent.mnScrollLines = 1; + mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); + } + } +} + +- (void)rotateWithEvent: (NSEvent*)pEvent +{ + //Rotation : -(float)rotation; + // TODO: create new CommandType so rotation is available to the applications +} + +- (void)swipeWithEvent: (NSEvent*)pEvent +{ + YIELD_GUARD; + + if( AquaSalFrame::isAlive( mpFrame ) ) + { + mpFrame->mnLastEventTime = static_cast<ULONG>( [pEvent timestamp] * 1000.0 ); + mpFrame->mnLastModifierFlags = [pEvent modifierFlags]; + + // merge pending scroll wheel events + float dX = 0.0; + float dY = 0.0; + for(;;) + { + dX += [pEvent deltaX]; + dY += [pEvent deltaY]; + NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSScrollWheelMask + untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ]; + if( !pNextEvent ) + break; + pEvent = pNextEvent; + } + + NSPoint aPt = [NSEvent mouseLocation]; + mpFrame->CocoaToVCL( aPt ); + + SalWheelMouseEvent aEvent; + aEvent.mnTime = mpFrame->mnLastEventTime; + aEvent.mnX = static_cast<long>(aPt.x) - mpFrame->maGeometry.nX; + aEvent.mnY = static_cast<long>(aPt.y) - mpFrame->maGeometry.nY; + aEvent.mnCode = ImplGetModifierMask( mpFrame->mnLastModifierFlags ); + aEvent.mbDeltaIsPixel = TRUE; + + // --- RTL --- (mirror mouse pos) + if( Application::GetSettings().GetLayoutRTL() ) + aEvent.mnX = mpFrame->maGeometry.nWidth-1-aEvent.mnX; + + if( dX != 0.0 ) + { + aEvent.mnDelta = static_cast<long>(floor(dX)); + aEvent.mnNotchDelta = dX < 0 ? -1 : 1; + if( aEvent.mnDelta == 0 ) + aEvent.mnDelta = aEvent.mnNotchDelta; + aEvent.mbHorz = TRUE; + aEvent.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL; + mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); + } + if( dY != 0.0 && AquaSalFrame::isAlive( mpFrame )) + { + aEvent.mnDelta = static_cast<long>(floor(dY)); + aEvent.mnNotchDelta = dY < 0 ? -1 : 1; + if( aEvent.mnDelta == 0 ) + aEvent.mnDelta = aEvent.mnNotchDelta; + aEvent.mbHorz = FALSE; + aEvent.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL; + mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); + } + } +} + +-(void)scrollWheel: (NSEvent*)pEvent +{ + YIELD_GUARD; + + if( AquaSalFrame::isAlive( mpFrame ) ) + { + mpFrame->mnLastEventTime = static_cast<ULONG>( [pEvent timestamp] * 1000.0 ); + mpFrame->mnLastModifierFlags = [pEvent modifierFlags]; + + // merge pending scroll wheel events + float dX = 0.0; + float dY = 0.0; + for(;;) + { + dX += [pEvent deltaX]; + dY += [pEvent deltaY]; + NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSScrollWheelMask + untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ]; + if( !pNextEvent ) + break; + pEvent = pNextEvent; + } + + NSPoint aPt = [NSEvent mouseLocation]; + mpFrame->CocoaToVCL( aPt ); + + SalWheelMouseEvent aEvent; + aEvent.mnTime = mpFrame->mnLastEventTime; + aEvent.mnX = static_cast<long>(aPt.x) - mpFrame->maGeometry.nX; + aEvent.mnY = static_cast<long>(aPt.y) - mpFrame->maGeometry.nY; + aEvent.mnCode = ImplGetModifierMask( mpFrame->mnLastModifierFlags ); + aEvent.mbDeltaIsPixel = TRUE; + + // --- RTL --- (mirror mouse pos) + if( Application::GetSettings().GetLayoutRTL() ) + aEvent.mnX = mpFrame->maGeometry.nWidth-1-aEvent.mnX; + + if( dX != 0.0 ) + { + aEvent.mnDelta = static_cast<long>(floor(dX)); + aEvent.mnNotchDelta = dX < 0 ? -1 : 1; + if( aEvent.mnDelta == 0 ) + aEvent.mnDelta = aEvent.mnNotchDelta; + aEvent.mbHorz = TRUE; + aEvent.mnScrollLines = dY > 0 ? dX/WHEEL_EVENT_FACTOR : -dX/WHEEL_EVENT_FACTOR; + if( aEvent.mnScrollLines == 0 ) + aEvent.mnScrollLines = 1; + + mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); + } + if( dY != 0.0 && AquaSalFrame::isAlive( mpFrame ) ) + { + aEvent.mnDelta = static_cast<long>(floor(dY)); + aEvent.mnNotchDelta = dY < 0 ? -1 : 1; + if( aEvent.mnDelta == 0 ) + aEvent.mnDelta = aEvent.mnNotchDelta; + aEvent.mbHorz = FALSE; + aEvent.mnScrollLines = dY > 0 ? dY/WHEEL_EVENT_FACTOR : -dY/WHEEL_EVENT_FACTOR; + if( aEvent.mnScrollLines < 1 ) + aEvent.mnScrollLines = 1; + + mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); + } + } +} + + +-(void)keyDown: (NSEvent*)pEvent +{ + YIELD_GUARD; + + if( AquaSalFrame::isAlive( mpFrame ) ) + { + mpLastEvent = pEvent; + mbInKeyInput = true; + mbNeedSpecialKeyHandle = false; + mbKeyHandled = false; + + mpFrame->mnLastEventTime = static_cast<ULONG>( [pEvent timestamp] * 1000.0 ); + mpFrame->mnLastModifierFlags = [pEvent modifierFlags]; + + if( ! [self handleKeyDownException: pEvent] ) + { + NSArray* pArray = [NSArray arrayWithObject: pEvent]; + [self interpretKeyEvents: pArray]; + } + + mbInKeyInput = false; + } +} + +-(MacOSBOOL)handleKeyDownException:(NSEvent*)pEvent +{ + // check for a very special set of modified characters + NSString* pUnmodifiedString = [pEvent charactersIgnoringModifiers]; + + if( pUnmodifiedString && [pUnmodifiedString length] == 1 ) + { + /* #i103102# key events with command and alternate don't make it through + interpretKeyEvents (why ?). Try to dispatch them here first, + if not successful continue normally + */ + if( (mpFrame->mnLastModifierFlags & (NSAlternateKeyMask | NSCommandKeyMask)) + == (NSAlternateKeyMask | NSCommandKeyMask) ) + { + if( [self sendSingleCharacter: mpLastEvent] ) + return YES; + } + unichar keyChar = [pUnmodifiedString characterAtIndex: 0]; + USHORT nKeyCode = ImplMapCharCode( keyChar ); + + // Caution: should the table grow to more than 5 or 6 entries, + // we must consider moving it to a kind of hash map + const unsigned int nExceptions = sizeof( aExceptionalKeys ) / sizeof( aExceptionalKeys[0] ); + for( unsigned int i = 0; i < nExceptions; i++ ) + { + if( nKeyCode == aExceptionalKeys[i].nKeyCode && + (mpFrame->mnLastModifierFlags & aExceptionalKeys[i].nModifierMask) + == aExceptionalKeys[i].nModifierMask ) + { + [self sendKeyInputAndReleaseToFrame: nKeyCode character: 0]; + + return YES; + } + } + } + return NO; +} + +-(void)flagsChanged: (NSEvent*)pEvent +{ + YIELD_GUARD; + + if( AquaSalFrame::isAlive( mpFrame ) ) + { + mpFrame->mnLastEventTime = static_cast<ULONG>( [pEvent timestamp] * 1000.0 ); + mpFrame->mnLastModifierFlags = [pEvent modifierFlags]; + } +} + +-(void)insertText:(id)aString +{ + YIELD_GUARD; + + if( AquaSalFrame::isAlive( mpFrame ) ) + { + NSString* pInsert = nil; + if( [aString isMemberOfClass: [NSAttributedString class]] ) + pInsert = [aString string]; + else + pInsert = aString; + + int nLen = 0; + if( pInsert && ( nLen = [pInsert length] ) > 0 ) + { + OUString aInsertString( GetOUString( pInsert ) ); + // aCharCode initializer is safe since aInsertString will at least contain '\0' + sal_Unicode aCharCode = *aInsertString.getStr(); + + if( nLen == 1 && + aCharCode < 0x80 && + aCharCode > 0x1f && + ! [self hasMarkedText ] + ) + { + USHORT nKeyCode = ImplMapCharCode( aCharCode ); + unsigned int nLastModifiers = mpFrame->mnLastModifierFlags; + + // #i99567# + // find out the unmodified key code + + // sanity check + if( mpLastEvent && ( [mpLastEvent type] == NSKeyDown || [mpLastEvent type] == NSKeyUp ) ) + { + // get unmodified string + NSString* pUnmodifiedString = [mpLastEvent charactersIgnoringModifiers]; + if( pUnmodifiedString && [pUnmodifiedString length] == 1 ) + { + // map the unmodified key code + unichar keyChar = [pUnmodifiedString characterAtIndex: 0]; + nKeyCode = ImplMapCharCode( keyChar ); + } + nLastModifiers = [mpLastEvent modifierFlags]; + + } + // #i99567# + // applications and vcl's edit fields ignore key events with ALT + // however we're at a place where we know text should be inserted + // so it seems we need to strip the Alt modifier here + if( (nLastModifiers & (NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask)) + == NSAlternateKeyMask ) + { + nLastModifiers = 0; + } + [self sendKeyInputAndReleaseToFrame: nKeyCode character: aCharCode modifiers: nLastModifiers]; + } + else + { + SalExtTextInputEvent aEvent; + aEvent.mnTime = mpFrame->mnLastEventTime; + aEvent.maText = aInsertString; + aEvent.mpTextAttr = NULL; + aEvent.mnCursorPos = aInsertString.getLength(); + aEvent.mnDeltaStart = 0; + aEvent.mnCursorFlags = 0; + aEvent.mbOnlyCursor = FALSE; + mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, &aEvent ); + if( AquaSalFrame::isAlive( mpFrame ) ) + mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, 0 ); + } + } + else + { + SalExtTextInputEvent aEvent; + aEvent.mnTime = mpFrame->mnLastEventTime; + aEvent.maText = String(); + aEvent.mpTextAttr = NULL; + aEvent.mnCursorPos = 0; + aEvent.mnDeltaStart = 0; + aEvent.mnCursorFlags = 0; + aEvent.mbOnlyCursor = FALSE; + mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, &aEvent ); + if( AquaSalFrame::isAlive( mpFrame ) ) + mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, 0 ); + + } + mbKeyHandled = true; + [self unmarkText]; + } +} + +-(void)insertTab: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_TAB character: '\t' modifiers: 0]; +} + +-(void)insertBacktab: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: (KEY_TAB | KEY_SHIFT) character: '\t' modifiers: 0]; +} + +-(void)moveLeft: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_LEFT character: 0 modifiers: 0]; +} + +-(void)moveLeftAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_LEFT character: 0 modifiers: NSShiftKeyMask]; +} + +-(void)moveBackwardAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_BACKWARD character: 0 modifiers: 0]; +} + +-(void)moveRight: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_RIGHT character: 0 modifiers: 0]; +} + +-(void)moveRightAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_RIGHT character: 0 modifiers: NSShiftKeyMask]; +} + +-(void)moveForwardAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_FORWARD character: 0 modifiers: 0]; +} + +-(void)moveWordLeft: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_WORD_BACKWARD character: 0 modifiers: 0]; +} + +-(void)moveWordBackward: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_WORD_BACKWARD character: 0 modifiers: 0]; +} + +-(void)moveWordBackwardAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_WORD_BACKWARD character: 0 modifiers: 0]; +} + +-(void)moveWordLeftAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_WORD_BACKWARD character: 0 modifiers: 0]; +} + +-(void)moveWordRight: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_WORD_FORWARD character: 0 modifiers: 0]; +} + +-(void)moveWordForward: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_WORD_FORWARD character: 0 modifiers: 0]; +} + +-(void)moveWordForwardAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_WORD_FORWARD character: 0 modifiers: 0]; +} + +-(void)moveWordRightAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_WORD_FORWARD character: 0 modifiers: 0]; +} + +-(void)moveToEndOfLine: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_LINE character: 0 modifiers: 0]; +} + +-(void)moveToRightEndOfLine: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_LINE character: 0 modifiers: 0]; +} + +-(void)moveToEndOfLineAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_END_OF_LINE character: 0 modifiers: 0]; +} + +-(void)moveToRightEndOfLineAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_END_OF_LINE character: 0 modifiers: 0]; +} + +-(void)moveToBeginningOfLine: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE character: 0 modifiers: 0]; +} + +-(void)moveToLeftEndOfLine: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE character: 0 modifiers: 0]; +} + +-(void)moveToBeginningOfLineAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE character: 0 modifiers: 0]; +} + +-(void)moveToLeftEndOfLineAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE character: 0 modifiers: 0]; +} + +-(void)moveToEndOfParagraph: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)moveToEndOfParagraphAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)moveParagraphForward: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)moveParagraphForwardAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)moveToBeginningOfParagraph: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)moveParagraphBackward: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)moveToBeginningOfParagraphAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)moveParagraphBackwardAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)moveToEndOfDocument: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT character: 0 modifiers: 0]; +} + +-(void)scrollToEndOfDocument: (id)aSender +{ + // this is not exactly what we should do, but it makes "End" and "Shift-End" behave consistent + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT character: 0 modifiers: 0]; +} + +-(void)moveToEndOfDocumentAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT character: 0 modifiers: 0]; +} + +-(void)moveToBeginningOfDocument: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT character: 0 modifiers: 0]; +} + +-(void)scrollToBeginningOfDocument: (id)aSender +{ + // this is not exactly what we should do, but it makes "Home" and "Shift-Home" behave consistent + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT character: 0 modifiers: 0]; +} + +-(void)moveToBeginningOfDocumentAndModifySelection: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT character: 0 modifiers: 0]; +} + +-(void)moveUp: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_UP character: 0 modifiers: 0]; +} + +-(void)moveDown: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_DOWN character: 0 modifiers: 0]; +} + +-(void)insertNewline: (id)aSender +{ + // #i91267# make enter and shift-enter work by evaluating the modifiers + [self sendKeyInputAndReleaseToFrame: KEY_RETURN character: '\n' modifiers: mpFrame->mnLastModifierFlags]; +} + +-(void)deleteBackward: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_BACKSPACE character: '\b' modifiers: 0]; +} + +-(void)deleteForward: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_DELETE character: 0x7f modifiers: 0]; +} + +-(void)deleteBackwardByDecomposingPreviousCharacter: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_BACKSPACE character: '\b' modifiers: 0]; +} + +-(void)deleteWordBackward: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_WORD_BACKWARD character: 0 modifiers: 0]; +} + +-(void)deleteWordForward: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_WORD_FORWARD character: 0 modifiers: 0]; +} + +-(void)deleteToBeginningOfLine: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_LINE character: 0 modifiers: 0]; +} + +-(void)deleteToEndOfLine: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_TO_END_OF_LINE character: 0 modifiers: 0]; +} + +-(void)deleteToBeginningOfParagraph: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)deleteToEndOfParagraph: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::DELETE_TO_END_OF_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)insertLineBreak: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::INSERT_LINEBREAK character: 0 modifiers: 0]; +} + +-(void)insertParagraphSeparator: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::INSERT_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)selectWord: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_WORD character: 0 modifiers: 0]; +} + +-(void)selectLine: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_LINE character: 0 modifiers: 0]; +} + +-(void)selectParagraph: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_PARAGRAPH character: 0 modifiers: 0]; +} + +-(void)selectAll: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: com::sun::star::awt::Key::SELECT_ALL character: 0 modifiers: 0]; +} + +-(void)cancelOperation: (id)aSender +{ + [self sendKeyInputAndReleaseToFrame: KEY_ESCAPE character: 0x1b modifiers: 0]; +} + +-(void)noop: (id)aSender +{ + if( ! mbKeyHandled ) + { + if( ! [self sendSingleCharacter:mpLastEvent] ) + { + /* prevent recursion */ + if( mpLastEvent != mpLastSuperEvent && [NSApp respondsToSelector: @selector(sendSuperEvent:)] ) + { + id pLastSuperEvent = mpLastSuperEvent; + mpLastSuperEvent = mpLastEvent; + [NSApp performSelector:@selector(sendSuperEvent:) withObject: mpLastEvent]; + mpLastSuperEvent = pLastSuperEvent; + + std::map< NSEvent*, bool >::iterator it = GetSalData()->maKeyEventAnswer.find( mpLastEvent ); + if( it != GetSalData()->maKeyEventAnswer.end() ) + it->second = true; + } + } + } +} + +-(MacOSBOOL)sendKeyInputAndReleaseToFrame: (USHORT)nKeyCode character: (sal_Unicode)aChar +{ + return [self sendKeyInputAndReleaseToFrame: nKeyCode character: aChar modifiers: mpFrame->mnLastModifierFlags]; +} + +-(MacOSBOOL)sendKeyInputAndReleaseToFrame: (USHORT)nKeyCode character: (sal_Unicode)aChar modifiers: (unsigned int)nMod +{ + return [self sendKeyToFrameDirect: nKeyCode character: aChar modifiers: nMod] || + [self sendSingleCharacter: mpLastEvent]; +} + +-(MacOSBOOL)sendKeyToFrameDirect: (USHORT)nKeyCode character: (sal_Unicode)aChar modifiers: (unsigned int)nMod +{ + YIELD_GUARD; + + long nRet = 0; + if( AquaSalFrame::isAlive( mpFrame ) ) + { + SalKeyEvent aEvent; + aEvent.mnTime = mpFrame->mnLastEventTime; + aEvent.mnCode = nKeyCode | ImplGetModifierMask( nMod ); + aEvent.mnCharCode = aChar; + aEvent.mnRepeat = FALSE; + nRet = mpFrame->CallCallback( SALEVENT_KEYINPUT, &aEvent ); + std::map< NSEvent*, bool >::iterator it = GetSalData()->maKeyEventAnswer.find( mpLastEvent ); + if( it != GetSalData()->maKeyEventAnswer.end() ) + it->second = nRet ? true : false; + if( AquaSalFrame::isAlive( mpFrame ) ) + mpFrame->CallCallback( SALEVENT_KEYUP, &aEvent ); + } + return nRet ? YES : NO; +} + + +-(MacOSBOOL)sendSingleCharacter: (NSEvent *)pEvent +{ + NSString* pUnmodifiedString = [pEvent charactersIgnoringModifiers]; + + if( pUnmodifiedString && [pUnmodifiedString length] == 1 ) + { + unichar keyChar = [pUnmodifiedString characterAtIndex: 0]; + USHORT nKeyCode = ImplMapCharCode( keyChar ); + if( nKeyCode != 0 ) + { + // don't send unicodes in the private use area + if( keyChar >= 0xf700 && keyChar < 0xf780 ) + keyChar = 0; + MacOSBOOL bRet = [self sendKeyToFrameDirect: nKeyCode character: keyChar modifiers: mpFrame->mnLastModifierFlags]; + mbInKeyInput = false; + + return bRet; + } + } + return NO; +} + + +// NSTextInput protocol +- (NSArray *)validAttributesForMarkedText +{ + return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, nil]; +} + +- (MacOSBOOL)hasMarkedText +{ + MacOSBOOL bHasMarkedText; + + bHasMarkedText = ( mMarkedRange.location != NSNotFound ) && + ( mMarkedRange.length != 0 ); + // hack to check keys like "Control-j" + if( mbInKeyInput ) + { + mbNeedSpecialKeyHandle = true; + } + + // FIXME: + // #i106901# + // if we come here outside of mbInKeyInput, this is likely to be because + // of the keyboard viewer. For unknown reasons having no marked range + // in this case causes a crash. So we say we have a marked range anyway + // This is a hack, since it is not understood what a) causes that crash + // and b) why we should have a marked range at this point. + if( ! mbInKeyInput ) + bHasMarkedText = YES; + + return bHasMarkedText; +} + +- (NSRange)markedRange +{ + // FIXME: + // #i106901# + // if we come here outside of mbInKeyInput, this is likely to be because + // of the keyboard viewer. For unknown reasons having no marked range + // in this case causes a crash. So we say we have a marked range anyway + // This is a hack, since it is not understood what a) causes that crash + // and b) why we should have a marked range at this point. + if( ! mbInKeyInput ) + return NSMakeRange( 0, 0 ); + + return [self hasMarkedText] ? mMarkedRange : NSMakeRange( NSNotFound, 0 ); +} + +- (NSRange)selectedRange +{ + return mSelectedRange; +} + +- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange +{ + if( ![aString isKindOfClass:[NSAttributedString class]] ) + aString = [[[NSAttributedString alloc] initWithString:aString] autorelease]; + NSRange rangeToReplace = [self hasMarkedText] ? [self markedRange] : [self selectedRange]; + if( rangeToReplace.location == NSNotFound ) + { + mMarkedRange = NSMakeRange( selRange.location, [aString length] ); + mSelectedRange = NSMakeRange( selRange.location, selRange.length ); + } + else + { + mMarkedRange = NSMakeRange( rangeToReplace.location, [aString length] ); + mSelectedRange = NSMakeRange( rangeToReplace.location + selRange.location, selRange.length ); + } + + int len = [aString length]; + SalExtTextInputEvent aInputEvent; + aInputEvent.mnTime = mpFrame->mnLastEventTime; + aInputEvent.mnDeltaStart = 0; + aInputEvent.mbOnlyCursor = FALSE; + if( len > 0 ) { + NSString *pString = [aString string]; + OUString aInsertString( GetOUString( pString ) ); + std::vector<USHORT> aInputFlags = std::vector<USHORT>( std::max( 1, len ), 0 ); + for ( int i = 0; i < len; i++ ) + { + unsigned int nUnderlineValue; + NSRange effectiveRange; + + effectiveRange = NSMakeRange(i, 1); + nUnderlineValue = [[aString attribute:NSUnderlineStyleAttributeName atIndex:i effectiveRange:&effectiveRange] unsignedIntValue]; + + switch (nUnderlineValue & 0xff) { + case NSUnderlineStyleSingle: + aInputFlags[i] = SAL_EXTTEXTINPUT_ATTR_UNDERLINE; + break; + case NSUnderlineStyleThick: + aInputFlags[i] = SAL_EXTTEXTINPUT_ATTR_UNDERLINE | SAL_EXTTEXTINPUT_ATTR_HIGHLIGHT; + break; + case NSUnderlineStyleDouble: + aInputFlags[i] = SAL_EXTTEXTINPUT_ATTR_BOLDUNDERLINE; + break; + default: + aInputFlags[i] = SAL_EXTTEXTINPUT_ATTR_HIGHLIGHT; + break; + } + } + + aInputEvent.maText = aInsertString; + aInputEvent.mnCursorPos = selRange.location; + aInputEvent.mpTextAttr = &aInputFlags[0]; + mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void *)&aInputEvent ); + } else { + aInputEvent.maText = String(); + aInputEvent.mnCursorPos = 0; + aInputEvent.mnCursorFlags = 0; + aInputEvent.mpTextAttr = 0; + mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void *)&aInputEvent ); + mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, 0 ); + } + mbKeyHandled= true; +} + +- (void)unmarkText +{ + mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0); +} + +- (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange +{ + // FIXME + return nil; +} + +- (unsigned int)characterIndexForPoint:(NSPoint)thePoint +{ + // FIXME + return 0; +} + +#if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) +/* build target 10.5 or greater */ +- (NSInteger)conversationIdentifier +#else +/* build target 10.4 */ +- (long)conversationIdentifier +#endif +{ + return (long)self; +} + +- (void)doCommandBySelector:(SEL)aSelector +{ + if( AquaSalFrame::isAlive( mpFrame ) ) + { + #if OSL_DEBUG_LEVEL > 1 + // fprintf( stderr, "SalFrameView: doCommandBySelector %s\n", (char*)aSelector ); + #endif + if( (mpFrame->mnICOptions & SAL_INPUTCONTEXT_TEXT) != 0 && + aSelector != NULL && [self respondsToSelector: aSelector] ) + { + [self performSelector: aSelector]; + } + else + { + [self sendSingleCharacter:mpLastEvent]; + } + } + + mbKeyHandled = true; +} + +-(void)clearLastEvent +{ + mpLastEvent = nil; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)theRange +{ + SalExtTextInputPosEvent aPosEvent; + mpFrame->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void *)&aPosEvent ); + + NSRect rect; + + rect.origin.x = aPosEvent.mnX + mpFrame->maGeometry.nX; + rect.origin.y = aPosEvent.mnY + mpFrame->maGeometry.nY + 4; // add some space for underlines + rect.size.width = aPosEvent.mnWidth; + rect.size.height = aPosEvent.mnHeight; + + mpFrame->VCLToCocoa( rect ); + return rect; +} + +-(id)parentAttribute { + return (NSView *) mpFrame -> mpWindow; +} + +-(::com::sun::star::accessibility::XAccessibleContext *)accessibleContext +{ + if ( mpReferenceWrapper == nil ) { + // some frames never become visible .. + Window *pWindow = mpFrame -> GetWindow(); + if ( ! pWindow ) + return nil; + + mpReferenceWrapper = new ReferenceWrapper; + mpReferenceWrapper -> rAccessibleContext = pWindow -> /*GetAccessibleChildWindow( 0 ) ->*/ GetAccessible() -> getAccessibleContext(); + [ AquaA11yFactory insertIntoWrapperRepository: self forAccessibleContext: mpReferenceWrapper -> rAccessibleContext ]; + } + return [ super accessibleContext ]; +} + +-(NSView *)viewElementForParent +{ + return (NSView *) mpFrame -> mpWindow; +} + +-(void)registerMouseEventListener: (id)theListener +{ + mpMouseEventListener = theListener; +} + +-(void)unregisterMouseEventListener: (id)theListener +{ + mpMouseEventListener = nil; +} + +-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +{ + return [mDraggingDestinationHandler draggingEntered: sender]; +} + +-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender +{ + return [mDraggingDestinationHandler draggingUpdated: sender]; +} + +-(void)draggingExited:(id <NSDraggingInfo>)sender +{ + [mDraggingDestinationHandler draggingExited: sender]; +} + +-(MacOSBOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender +{ + return [mDraggingDestinationHandler prepareForDragOperation: sender]; +} + +-(MacOSBOOL)performDragOperation:(id <NSDraggingInfo>)sender +{ + return [mDraggingDestinationHandler performDragOperation: sender]; +} + +-(void)concludeDragOperation:(id <NSDraggingInfo>)sender +{ + [mDraggingDestinationHandler concludeDragOperation: sender]; +} + +-(void)registerDraggingDestinationHandler:(id)theHandler +{ + mDraggingDestinationHandler = theHandler; +} + +-(void)unregisterDraggingDestinationHandler:(id)theHandler +{ + mDraggingDestinationHandler = nil; +} + +@end + diff --git a/vcl/aqua/source/window/salmenu.cxx b/vcl/aqua/source/window/salmenu.cxx new file mode 100644 index 000000000000..ed3086d8506f --- /dev/null +++ b/vcl/aqua/source/window/salmenu.cxx @@ -0,0 +1,958 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "saldata.hxx" +#include "salinst.h" +#include "salmenu.h" +#include "salnsmenu.h" +#include "salframe.h" +#include "salbmp.h" +#include "vcl/svids.hrc" +#include "vcl/cmdevt.hxx" +#include "vcl/floatwin.hxx" +#include "vcl/window.h" +#include "vcl/window.hxx" +#include "vcl/svapp.hxx" + +#include "rtl/ustrbuf.hxx" +#include "aqua11ywrapper.h" + +const AquaSalMenu* AquaSalMenu::pCurrentMenuBar = NULL; + +@interface MainMenuSelector : NSObject +{ +} +-(void)showDialog: (int)nDialog; +-(void)showPreferences: (id)sender; +-(void)showAbout: (id)sender; +@end + +@implementation MainMenuSelector +-(void)showDialog: (int)nDialog +{ + if( AquaSalMenu::pCurrentMenuBar ) + { + const AquaSalFrame* pFrame = AquaSalMenu::pCurrentMenuBar->mpFrame; + if( pFrame && AquaSalFrame::isAlive( pFrame ) ) + { + pFrame->CallCallback( SALEVENT_SHOWDIALOG, reinterpret_cast<void*>(nDialog) ); + } + } + else + { + String aDialog; + if( nDialog == SHOWDIALOG_ID_ABOUT ) + aDialog = String( RTL_CONSTASCII_USTRINGPARAM( "ABOUT" ) ); + else if( nDialog == SHOWDIALOG_ID_PREFERENCES ) + aDialog = String( RTL_CONSTASCII_USTRINGPARAM( "PREFERENCES" ) ); + const ApplicationEvent* pAppEvent = new ApplicationEvent( String(), + ApplicationAddress(), + ByteString( "SHOWDIALOG" ), + aDialog ); + AquaSalInstance::aAppEventList.push_back( pAppEvent ); + } +} + +-(void)showPreferences: (id) sender +{ + [self showDialog: SHOWDIALOG_ID_PREFERENCES]; +} +-(void)showAbout: (id) sender +{ + [self showDialog: SHOWDIALOG_ID_ABOUT]; +} +@end + + +// FIXME: currently this is leaked +static MainMenuSelector* pMainMenuSelector = nil; + +static void initAppMenu() +{ + static bool bOnce = true; + if( bOnce ) + { + bOnce = false; + + ResMgr* pMgr = ImplGetResMgr(); + if( pMgr ) + { + // get the main menu + NSMenu* pMainMenu = [NSApp mainMenu]; + if( pMainMenu != nil ) + { + // create the action selector + pMainMenuSelector = [[MainMenuSelector alloc] init]; + + // get the proper submenu + NSMenu* pAppMenu = [[pMainMenu itemAtIndex: 0] submenu]; + if( pAppMenu ) + { + // insert about entry + String aAbout( ResId( SV_STDTEXT_ABOUT, *pMgr ) ); + NSString* pString = CreateNSString( aAbout ); + NSMenuItem* pNewItem = [pAppMenu insertItemWithTitle: pString + action: @selector(showAbout:) + keyEquivalent: @"" + atIndex: 0]; + if (pString) + [pString release]; + if( pNewItem ) + { + [pNewItem setTarget: pMainMenuSelector]; + [pAppMenu insertItem: [NSMenuItem separatorItem] atIndex: 1]; + } + + // insert preferences entry + String aPref( ResId( SV_STDTEXT_PREFERENCES, *pMgr ) ); + pString = CreateNSString( aPref ); + pNewItem = [pAppMenu insertItemWithTitle: pString + action: @selector(showPreferences:) + keyEquivalent: @"," + atIndex: 2]; + if (pString) + [pString release]; + if( pNewItem ) + { + [pNewItem setKeyEquivalentModifierMask: NSCommandKeyMask]; + [pNewItem setTarget: pMainMenuSelector]; + [pAppMenu insertItem: [NSMenuItem separatorItem] atIndex: 3]; + } + + // WARNING: ultra ugly code ahead + + // rename standard entries + // rename "Services" + pNewItem = [pAppMenu itemAtIndex: 4]; + if( pNewItem ) + { + pString = CreateNSString( String( ResId( SV_MENU_MAC_SERVICES, *pMgr ) ) ); + [pNewItem setTitle: pString]; + if( pString ) + [pString release]; + } + + // rename "Hide NewApplication" + pNewItem = [pAppMenu itemAtIndex: 6]; + if( pNewItem ) + { + pString = CreateNSString( String( ResId( SV_MENU_MAC_HIDEAPP, *pMgr ) ) ); + [pNewItem setTitle: pString]; + if( pString ) + [pString release]; + } + + // rename "Hide Others" + pNewItem = [pAppMenu itemAtIndex: 7]; + if( pNewItem ) + { + pString = CreateNSString( String( ResId( SV_MENU_MAC_HIDEALL, *pMgr ) ) ); + [pNewItem setTitle: pString]; + if( pString ) + [pString release]; + } + + // rename "Show all" + pNewItem = [pAppMenu itemAtIndex: 8]; + if( pNewItem ) + { + pString = CreateNSString( String( ResId( SV_MENU_MAC_SHOWALL, *pMgr ) ) ); + [pNewItem setTitle: pString]; + if( pString ) + [pString release]; + } + + // rename "Quit NewApplication" + pNewItem = [pAppMenu itemAtIndex: 10]; + if( pNewItem ) + { + pString = CreateNSString( String( ResId( SV_MENU_MAC_QUITAPP, *pMgr ) ) ); + [pNewItem setTitle: pString]; + if( pString ) + [pString release]; + } + } + } + } + } +} + +// ======================================================================= + +SalMenu* AquaSalInstance::CreateMenu( BOOL bMenuBar ) +{ + initAppMenu(); + + AquaSalMenu *pAquaSalMenu = new AquaSalMenu( bMenuBar ); + + return pAquaSalMenu; +} + +void AquaSalInstance::DestroyMenu( SalMenu* pSalMenu ) +{ + delete pSalMenu; +} + +SalMenuItem* AquaSalInstance::CreateMenuItem( const SalItemParams* pItemData ) +{ + if( !pItemData ) + return NULL; + + AquaSalMenuItem *pSalMenuItem = new AquaSalMenuItem( pItemData ); + + return pSalMenuItem; +} + +void AquaSalInstance::DestroyMenuItem( SalMenuItem* pSalMenuItem ) +{ + delete pSalMenuItem; +} + + +// ======================================================================= + + +/* + * AquaSalMenu + */ + +AquaSalMenu::AquaSalMenu( bool bMenuBar ) : + mbMenuBar( bMenuBar ), + mpMenu( nil ), + mpVCLMenu( NULL ), + mpFrame( NULL ), + mpParentSalMenu( NULL ) +{ + if( ! mbMenuBar ) + { + mpMenu = [[SalNSMenu alloc] initWithMenu: this]; + [mpMenu setDelegate: mpMenu]; + } + else + { + mpMenu = [NSApp mainMenu]; + } + [mpMenu setAutoenablesItems: NO]; +} + +AquaSalMenu::~AquaSalMenu() +{ + // actually someone should have done AquaSalFrame::SetMenu( NULL ) + // on our frame, alas it is not so + if( mpFrame && AquaSalFrame::isAlive( mpFrame ) && mpFrame->mpMenu == this ) + const_cast<AquaSalFrame*>(mpFrame)->mpMenu = NULL; + + // this should normally be empty already, but be careful... + for( size_t i = 0; i < maButtons.size(); i++ ) + releaseButtonEntry( maButtons[i] ); + maButtons.clear(); + + // is this leaking in some cases ? the release often leads to a duplicate release + // it seems the parent item gets ownership of the menu + if( mpMenu ) + { + if( mbMenuBar ) + { + if( pCurrentMenuBar == this ) + { + // if the current menubar gets destroyed, set the default menubar + setDefaultMenu(); + } + } + else + // the system may still hold a reference on mpMenu + { + // so set the pointer to this AquaSalMenu to NULL + // to protect from calling a dead object + + // in ! mbMenuBar case our mpMenu is actually a SalNSMenu* + // so we can safely cast here + [static_cast<SalNSMenu*>(mpMenu) setSalMenu: NULL]; + /* #i89860# FIXME: + using [autorelease] here (and in AquaSalMenuItem::~AquaSalMenuItem) + instead of [release] fixes an occasional crash. That should + indicate that we release menus / menu items in the wrong order + somewhere, but I could not find that case. + */ + [mpMenu autorelease]; + } + } +} + +sal_Int32 removeUnusedItemsRunner(NSMenu * pMenu) +{ + NSArray * elements = [pMenu itemArray]; + NSEnumerator * it = [elements objectEnumerator]; + id elem; + NSMenuItem * lastDisplayedMenuItem = nil; + sal_Int32 drawnItems = 0; + bool firstEnabledItemIsNoSeparator = false; + while((elem=[it nextObject]) != nil) { + NSMenuItem * item = static_cast<NSMenuItem *>(elem); + if( (![item isEnabled] && ![item isSeparatorItem]) || ([item isSeparatorItem] && (lastDisplayedMenuItem != nil && [lastDisplayedMenuItem isSeparatorItem])) ) { + [[item menu]removeItem:item]; + } else { + if( ! firstEnabledItemIsNoSeparator && [item isSeparatorItem] ) { + [[item menu]removeItem:item]; + } else { + firstEnabledItemIsNoSeparator = true; + lastDisplayedMenuItem = item; + drawnItems++; + if( [item hasSubmenu] ) { + removeUnusedItemsRunner( [item submenu] ); + } + } + } + } + if( lastDisplayedMenuItem != nil && [lastDisplayedMenuItem isSeparatorItem]) { + [[lastDisplayedMenuItem menu]removeItem:lastDisplayedMenuItem]; + } + return drawnItems; +} + +bool AquaSalMenu::ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, ULONG nFlags) +{ + // do not use native popup menu when AQUA_NATIVE_MENUS is set to FALSE + if( ! VisibleMenuBar() ) { + return false; + } + + // set offsets for positioning + const float offset = 9.0; + + // get the pointers + AquaSalFrame * pParentAquaSalFrame = (AquaSalFrame *) pWin->ImplGetWindowImpl()->mpRealParent->ImplGetFrame(); + NSWindow * pParentNSWindow = pParentAquaSalFrame->mpWindow; + NSView * pParentNSView = [pParentNSWindow contentView]; + NSView * pPopupNSView = ((AquaSalFrame *) pWin->ImplGetWindow()->ImplGetFrame())->mpView; + NSRect popupFrame = [pPopupNSView frame]; + + // since we manipulate the menu below (removing entries) + // let's rather make a copy here and work with that + NSMenu* pCopyMenu = [mpMenu copy]; + + // filter disabled elements + removeUnusedItemsRunner( pCopyMenu ); + + // create frame rect + NSRect displayPopupFrame = NSMakeRect( rRect.nLeft+(offset-1), rRect.nTop+(offset+1), popupFrame.size.width, 0 ); + pParentAquaSalFrame->VCLToCocoa(displayPopupFrame, false); + + // do the same strange semantics as vcl popup windows to arrive at a frame geometry + // in mirrored UI case; best done by actually executing the same code + USHORT nArrangeIndex; + pWin->SetPosPixel( pWin->ImplCalcPos( pWin, rRect, nFlags, nArrangeIndex ) ); + displayPopupFrame.origin.x = pWin->ImplGetFrame()->maGeometry.nX - pParentAquaSalFrame->maGeometry.nX + offset; + displayPopupFrame.origin.y = pWin->ImplGetFrame()->maGeometry.nY - pParentAquaSalFrame->maGeometry.nY + offset; + pParentAquaSalFrame->VCLToCocoa(displayPopupFrame, false); + + // #i111992# if this menu was opened due to a key event, prevent dispatching that yet again + if( [pParentNSView respondsToSelector: @selector(clearLastEvent)] ) + [pParentNSView performSelector:@selector(clearLastEvent)]; + + // open popup menu + NSPopUpButtonCell * pPopUpButtonCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]; + [pPopUpButtonCell setMenu: pCopyMenu]; + [pPopUpButtonCell selectItem:nil]; + [AquaA11yWrapper setPopupMenuOpen: YES]; + [pPopUpButtonCell performClickWithFrame:displayPopupFrame inView:pParentNSView]; + [pPopUpButtonCell release]; + [AquaA11yWrapper setPopupMenuOpen: NO]; + + // clean up the copy + [pCopyMenu release]; + return true; +} + +int AquaSalMenu::getItemIndexByPos( USHORT nPos ) const +{ + int nIndex = 0; + if( nPos == MENU_APPEND ) + nIndex = [mpMenu numberOfItems]; + else + nIndex = sal::static_int_cast<int>( mbMenuBar ? nPos+1 : nPos ); + return nIndex; +} + +const AquaSalFrame* AquaSalMenu::getFrame() const +{ + const AquaSalMenu* pMenu = this; + while( pMenu && ! pMenu->mpFrame ) + pMenu = pMenu->mpParentSalMenu; + return pMenu ? pMenu->mpFrame : NULL; +} + +void AquaSalMenu::unsetMainMenu() +{ + pCurrentMenuBar = NULL; + + // remove items from main menu + NSMenu* pMenu = [NSApp mainMenu]; + for( int nItems = [pMenu numberOfItems]; nItems > 1; nItems-- ) + [pMenu removeItemAtIndex: 1]; +} + +void AquaSalMenu::setMainMenu() +{ + DBG_ASSERT( mbMenuBar, "setMainMenu on non menubar" ); + if( mbMenuBar ) + { + if( pCurrentMenuBar != this ) + { + unsetMainMenu(); + // insert our items + for( unsigned int i = 0; i < maItems.size(); i++ ) + { + NSMenuItem* pItem = maItems[i]->mpMenuItem; + [mpMenu insertItem: pItem atIndex: i+1]; + } + pCurrentMenuBar = this; + + // change status item + statusLayout(); + } + enableMainMenu( true ); + } +} + +void AquaSalMenu::setDefaultMenu() +{ + NSMenu* pMenu = [NSApp mainMenu]; + + unsetMainMenu(); + + // insert default items + std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu ); + for( unsigned int i = 0, nAddItems = rFallbackMenu.size(); i < nAddItems; i++ ) + { + NSMenuItem* pItem = rFallbackMenu[i]; + if( [pItem menu] == nil ) + [pMenu insertItem: pItem atIndex: i+1]; + } +} + +void AquaSalMenu::enableMainMenu( bool bEnable ) +{ + NSMenu* pMainMenu = [NSApp mainMenu]; + if( pMainMenu ) + { + // enable/disable items from main menu + int nItems = [pMainMenu numberOfItems]; + for( int n = 1; n < nItems; n++ ) + { + NSMenuItem* pItem = [pMainMenu itemAtIndex: n]; + [pItem setEnabled: bEnable ? YES : NO]; + } + } +} + +void AquaSalMenu::addFallbackMenuItem( NSMenuItem* pNewItem ) +{ + initAppMenu(); + + std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu ); + + // prevent duplicate insertion + int nItems = rFallbackMenu.size(); + for( int i = 0; i < nItems; i++ ) + { + if( rFallbackMenu[i] == pNewItem ) + return; + } + + // push the item to the back and retain it + [pNewItem retain]; + rFallbackMenu.push_back( pNewItem ); + + if( pCurrentMenuBar == NULL ) + setDefaultMenu(); +} + +void AquaSalMenu::removeFallbackMenuItem( NSMenuItem* pOldItem ) +{ + std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu ); + + // find item + unsigned int nItems = rFallbackMenu.size(); + for( unsigned int i = 0; i < nItems; i++ ) + { + if( rFallbackMenu[i] == pOldItem ) + { + // remove item and release + rFallbackMenu.erase( rFallbackMenu.begin() + i ); + [pOldItem release]; + + if( pCurrentMenuBar == NULL ) + setDefaultMenu(); + + return; + } + } +} + +BOOL AquaSalMenu::VisibleMenuBar() +{ + // Enable/disable experimental native menus code? + // + // To disable native menus, set the environment variable AQUA_NATIVE_MENUS to FALSE + + static const char *pExperimental = getenv ("AQUA_NATIVE_MENUS"); + + if ( ImplGetSVData()->mbIsTestTool || (pExperimental && !strcasecmp(pExperimental, "FALSE")) ) + return FALSE; + + // End of experimental code enable/disable part + + return TRUE; +} + +void AquaSalMenu::SetFrame( const SalFrame *pFrame ) +{ + mpFrame = static_cast<const AquaSalFrame*>(pFrame); +} + +void AquaSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos ) +{ + AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem*>(pSalMenuItem); + + pAquaSalMenuItem->mpParentMenu = this; + DBG_ASSERT( pAquaSalMenuItem->mpVCLMenu == NULL || + pAquaSalMenuItem->mpVCLMenu == mpVCLMenu || + mpVCLMenu == NULL, + "resetting menu ?" ); + if( pAquaSalMenuItem->mpVCLMenu ) + mpVCLMenu = pAquaSalMenuItem->mpVCLMenu; + + if( nPos == MENU_APPEND || nPos == maItems.size() ) + maItems.push_back( pAquaSalMenuItem ); + else if( nPos < maItems.size() ) + maItems.insert( maItems.begin() + nPos, pAquaSalMenuItem ); + else + { + DBG_ERROR( "invalid item index in insert" ); + return; + } + + if( ! mbMenuBar || pCurrentMenuBar == this ) + [mpMenu insertItem: pAquaSalMenuItem->mpMenuItem atIndex: getItemIndexByPos(nPos)]; +} + +void AquaSalMenu::RemoveItem( unsigned nPos ) +{ + AquaSalMenuItem* pRemoveItem = NULL; + if( nPos == MENU_APPEND || nPos == (maItems.size()-1) ) + { + pRemoveItem = maItems.back(); + maItems.pop_back(); + } + else if( nPos < maItems.size() ) + { + pRemoveItem = maItems[ nPos ]; + maItems.erase( maItems.begin()+nPos ); + } + else + { + DBG_ERROR( "invalid item index in remove" ); + return; + } + + pRemoveItem->mpParentMenu = NULL; + + if( ! mbMenuBar || pCurrentMenuBar == this ) + [mpMenu removeItemAtIndex: getItemIndexByPos(nPos)]; +} + +void AquaSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos ) +{ + AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem*>(pSalMenuItem); + AquaSalMenu *subAquaSalMenu = static_cast<AquaSalMenu*>(pSubMenu); + + if (subAquaSalMenu) + { + pAquaSalMenuItem->mpSubMenu = subAquaSalMenu; + if( subAquaSalMenu->mpParentSalMenu == NULL ) + { + subAquaSalMenu->mpParentSalMenu = this; + [pAquaSalMenuItem->mpMenuItem setSubmenu: subAquaSalMenu->mpMenu]; + + // set title of submenu + [subAquaSalMenu->mpMenu setTitle: [pAquaSalMenuItem->mpMenuItem title]]; + } + else if( subAquaSalMenu->mpParentSalMenu != this ) + { + // cocoa doesn't allow menus to be submenus of multiple + // menu items, so place a copy in the menu item instead ? + // let's hope that NSMenu copy does the right thing + NSMenu* pCopy = [subAquaSalMenu->mpMenu copy]; + [pAquaSalMenuItem->mpMenuItem setSubmenu: pCopy]; + + // set title of submenu + [pCopy setTitle: [pAquaSalMenuItem->mpMenuItem title]]; + } + } + else + { + if( pAquaSalMenuItem->mpSubMenu ) + { + if( pAquaSalMenuItem->mpSubMenu->mpParentSalMenu == this ) + pAquaSalMenuItem->mpSubMenu->mpParentSalMenu = NULL; + } + pAquaSalMenuItem->mpSubMenu = NULL; + [pAquaSalMenuItem->mpMenuItem setSubmenu: nil]; + } +} + +void AquaSalMenu::CheckItem( unsigned nPos, BOOL bCheck ) +{ + if( nPos < maItems.size() ) + { + NSMenuItem* pItem = maItems[nPos]->mpMenuItem; + [pItem setState: bCheck ? NSOnState : NSOffState]; + } +} + +void AquaSalMenu::EnableItem( unsigned nPos, BOOL bEnable ) +{ + if( nPos < maItems.size() ) + { + NSMenuItem* pItem = maItems[nPos]->mpMenuItem; + [pItem setEnabled: bEnable ? YES : NO]; + } +} + +void AquaSalMenu::SetItemImage( unsigned nPos, SalMenuItem* pSMI, const Image& rImage ) +{ + AquaSalMenuItem* pSalMenuItem = static_cast<AquaSalMenuItem*>( pSMI ); + if( ! pSalMenuItem || ! pSalMenuItem->mpMenuItem ) + return; + + NSImage* pImage = CreateNSImage( rImage ); + + [pSalMenuItem->mpMenuItem setImage: pImage]; + if( pImage ) + [pImage release]; +} + +void AquaSalMenu::SetItemText( unsigned i_nPos, SalMenuItem* i_pSalMenuItem, const XubString& i_rText ) +{ + if (!i_pSalMenuItem) + return; + + AquaSalMenuItem *pAquaSalMenuItem = (AquaSalMenuItem *) i_pSalMenuItem; + + String aText( i_rText ); + + // Delete mnemonics + aText.EraseAllChars( '~' ); + + /* #i90015# until there is a correct solution + strip out any appended (.*) in menubar entries + */ + if( mbMenuBar ) + { + xub_StrLen nPos = aText.SearchBackward( sal_Unicode( '(' ) ); + if( nPos != STRING_NOTFOUND ) + { + xub_StrLen nPos2 = aText.Search( sal_Unicode( ')' ) ); + if( nPos2 != STRING_NOTFOUND ) + aText.Erase( nPos, nPos2-nPos+1 ); + } + } + + NSString* pString = CreateNSString( aText ); + if (pString) + { + [pAquaSalMenuItem->mpMenuItem setTitle: pString]; + // if the menu item has a submenu, change its title as well + if (pAquaSalMenuItem->mpSubMenu) + [pAquaSalMenuItem->mpSubMenu->mpMenu setTitle: pString]; + [pString release]; + } +} + +void AquaSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const KeyCode& rKeyCode, const XubString& rKeyName ) +{ + USHORT nModifier; + sal_Unicode nCommandKey = 0; + + USHORT nKeyCode=rKeyCode.GetCode(); + if( nKeyCode ) + { + if ((nKeyCode>=KEY_A) && (nKeyCode<=KEY_Z)) // letter A..Z + nCommandKey = nKeyCode-KEY_A + 'a'; + else if ((nKeyCode>=KEY_0) && (nKeyCode<=KEY_9)) // numbers 0..9 + nCommandKey = nKeyCode-KEY_0 + '0'; + else if ((nKeyCode>=KEY_F1) && (nKeyCode<=KEY_F26)) // function keys F1..F26 + nCommandKey = nKeyCode-KEY_F1 + NSF1FunctionKey; + else if( nKeyCode == KEY_REPEAT ) + nCommandKey = NSRedoFunctionKey; + else if( nKeyCode == KEY_SPACE ) + nCommandKey = ' '; + else + { + switch (nKeyCode) + { + case KEY_ADD: + nCommandKey='+'; + break; + case KEY_SUBTRACT: + nCommandKey='-'; + break; + case KEY_MULTIPLY: + nCommandKey='*'; + break; + case KEY_DIVIDE: + nCommandKey='/'; + break; + case KEY_POINT: + nCommandKey='.'; + break; + case KEY_LESS: + nCommandKey='<'; + break; + case KEY_GREATER: + nCommandKey='>'; + break; + case KEY_EQUAL: + nCommandKey='='; + break; + } + } + } + else // not even a code ? nonsense -> ignore + return; + + DBG_ASSERT( nCommandKey, "unmapped accelerator key" ); + + nModifier=rKeyCode.GetAllModifier(); + + // should always use the command key + int nItemModifier = 0; + + if (nModifier & KEY_SHIFT) + { + nItemModifier |= NSShiftKeyMask; // actually useful only for function keys + if( nKeyCode >= KEY_A && nKeyCode <= KEY_Z ) + nCommandKey = nKeyCode - KEY_A + 'A'; + } + + if (nModifier & KEY_MOD1) + nItemModifier |= NSCommandKeyMask; + + if(nModifier & KEY_MOD2) + nItemModifier |= NSAlternateKeyMask; + + if(nModifier & KEY_MOD3) + nItemModifier |= NSControlKeyMask; + + AquaSalMenuItem *pAquaSalMenuItem = (AquaSalMenuItem *) pSalMenuItem; + NSString* pString = CreateNSString( rtl::OUString( &nCommandKey, 1 ) ); + [pAquaSalMenuItem->mpMenuItem setKeyEquivalent: pString]; + [pAquaSalMenuItem->mpMenuItem setKeyEquivalentModifierMask: nItemModifier]; + if (pString) + [pString release]; +} + +void AquaSalMenu::GetSystemMenuData( SystemMenuData* pData ) +{ +} + +AquaSalMenu::MenuBarButtonEntry* AquaSalMenu::findButtonItem( USHORT i_nItemId ) +{ + for( size_t i = 0; i < maButtons.size(); ++i ) + { + if( maButtons[i].maButton.mnId == i_nItemId ) + return &maButtons[i]; + } + return NULL; +} + +void AquaSalMenu::statusLayout() +{ + if( GetSalData()->mpStatusItem ) + { + NSView* pView = [GetSalData()->mpStatusItem view]; + if( [pView isMemberOfClass: [OOStatusItemView class]] ) // well of course it is + [(OOStatusItemView*)pView layout]; + else + DBG_ERROR( "someone stole our status view" ); + } +} + +void AquaSalMenu::releaseButtonEntry( MenuBarButtonEntry& i_rEntry ) +{ + if( i_rEntry.mpNSImage ) + { + [i_rEntry.mpNSImage release]; + i_rEntry.mpNSImage = nil; + } + if( i_rEntry.mpToolTipString ) + { + [i_rEntry.mpToolTipString release]; + i_rEntry.mpToolTipString = nil; + } +} + +bool AquaSalMenu::AddMenuBarButton( const SalMenuButtonItem& i_rNewItem ) +{ + if( ! mbMenuBar || ! VisibleMenuBar() ) + return false; + + MenuBarButtonEntry* pEntry = findButtonItem( i_rNewItem.mnId ); + if( pEntry ) + { + releaseButtonEntry( *pEntry ); + pEntry->maButton = i_rNewItem; + pEntry->mpNSImage = CreateNSImage( i_rNewItem.maImage ); + if( i_rNewItem.maToolTipText.getLength() ) + pEntry->mpToolTipString = CreateNSString( i_rNewItem.maToolTipText ); + } + else + { + maButtons.push_back( MenuBarButtonEntry( i_rNewItem ) ); + maButtons.back().mpNSImage = CreateNSImage( i_rNewItem.maImage ); + maButtons.back().mpToolTipString = CreateNSString( i_rNewItem.maToolTipText ); + } + + // lazy create status item + SalData::getStatusItem(); + + if( pCurrentMenuBar == this ) + statusLayout(); + + return true; +} + +void AquaSalMenu::RemoveMenuBarButton( USHORT i_nId ) +{ + MenuBarButtonEntry* pEntry = findButtonItem( i_nId ); + if( pEntry ) + { + releaseButtonEntry( *pEntry ); + // note: vector guarantees that its contents are in a plain array + maButtons.erase( maButtons.begin() + (pEntry - &maButtons[0]) ); + } + + if( pCurrentMenuBar == this ) + statusLayout(); +} + +Rectangle AquaSalMenu::GetMenuBarButtonRectPixel( USHORT i_nItemId, SalFrame* i_pReferenceFrame ) +{ + if( GetSalData()->mnSystemVersion < VER_LEOPARD ) + return Rectangle( Point( -1, -1 ), Size( 1, 1 ) ); + + if( ! i_pReferenceFrame || ! AquaSalFrame::isAlive( static_cast<AquaSalFrame*>(i_pReferenceFrame) ) ) + return Rectangle(); + + MenuBarButtonEntry* pEntry = findButtonItem( i_nItemId ); + + if( ! pEntry ) + return Rectangle(); + + NSStatusItem* pItem = SalData::getStatusItem(); + if( ! pItem ) + return Rectangle(); + + NSView* pView = [pItem view]; + if( ! pView ) + return Rectangle(); + NSWindow* pWin = [pView window]; + if( ! pWin ) + return Rectangle(); + + NSRect aRect = [pWin frame]; + aRect.origin = [pWin convertBaseToScreen: NSMakePoint( 0, 0 )]; + + // make coordinates relative to reference frame + static_cast<AquaSalFrame*>(i_pReferenceFrame)->CocoaToVCL( aRect.origin ); + aRect.origin.x -= i_pReferenceFrame->maGeometry.nX; + aRect.origin.y -= i_pReferenceFrame->maGeometry.nY + aRect.size.height; + + return Rectangle( Point(static_cast<long int>(aRect.origin.x), + static_cast<long int>(aRect.origin.y) + ), + Size( static_cast<long int>(aRect.size.width), + static_cast<long int>(aRect.size.height) + ) + ); +} + +// ======================================================================= + +/* + * SalMenuItem + */ + +AquaSalMenuItem::AquaSalMenuItem( const SalItemParams* pItemData ) : + mnId( pItemData->nId ), + mpVCLMenu( pItemData->pMenu ), + mpParentMenu( NULL ), + mpSubMenu( NULL ), + mpMenuItem( nil ) +{ + String aText( pItemData->aText ); + + // Delete mnemonics + aText.EraseAllChars( '~' ); + + if (pItemData->eType == MENUITEM_SEPARATOR) + { + mpMenuItem = [NSMenuItem separatorItem]; + // these can go occasionally go in and out of a menu, ensure their lifecycle + // also for the release in AquaSalMenuItem destructor + [mpMenuItem retain]; + } + else + { + mpMenuItem = [[SalNSMenuItem alloc] initWithMenuItem: this]; + [mpMenuItem setEnabled: YES]; + NSString* pString = CreateNSString( aText ); + if (pString) + { + [mpMenuItem setTitle: pString]; + [pString release]; + } + // anything but a separator should set a menu to dispatch to + DBG_ASSERT( mpVCLMenu, "no menu" ); + } +} + +AquaSalMenuItem::~AquaSalMenuItem() +{ + /* #i89860# FIXME: + using [autorelease] here (and in AquaSalMenu:::~AquaSalMenu) instead of + [release] fixes an occasional crash. That should indicate that we release + menus / menu items in the wrong order somewhere, but I + could not find that case. + */ + if( mpMenuItem ) + [mpMenuItem autorelease]; +} + +// ------------------------------------------------------------------- + diff --git a/vcl/aqua/source/window/salnsmenu.mm b/vcl/aqua/source/window/salnsmenu.mm new file mode 100755 index 000000000000..015c43aed70f --- /dev/null +++ b/vcl/aqua/source/window/salnsmenu.mm @@ -0,0 +1,213 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "salinst.h" +#include "saldata.hxx" +#include "salframe.h" +#include "salmenu.h" +#include "salnsmenu.h" + +#include "vcl/window.hxx" + +@implementation SalNSMenu +-(id)initWithMenu: (AquaSalMenu*)pMenu +{ + mpMenu = pMenu; + return [super initWithTitle: [NSString string]]; +} + +-(void)menuNeedsUpdate: (NSMenu*)pMenu +{ + YIELD_GUARD; + + if( mpMenu ) + { + const AquaSalFrame* pFrame = mpMenu->getFrame(); + if( pFrame && AquaSalFrame::isAlive( pFrame ) ) + { + SalMenuEvent aMenuEvt; + aMenuEvt.mnId = 0; + aMenuEvt.mpMenu = mpMenu->mpVCLMenu; + if( aMenuEvt.mpMenu ) + { + pFrame->CallCallback(SALEVENT_MENUACTIVATE, &aMenuEvt); + pFrame->CallCallback(SALEVENT_MENUDEACTIVATE, &aMenuEvt); + } + else + DBG_ERROR( "unconnected menu" ); + } + } +} + +-(void)setSalMenu: (AquaSalMenu*)pMenu +{ + mpMenu = pMenu; +} +@end + +@implementation SalNSMenuItem +-(id)initWithMenuItem: (AquaSalMenuItem*)pMenuItem +{ + mpMenuItem = pMenuItem; + id ret = [super initWithTitle: [NSString string] + action: @selector(menuItemTriggered:) + keyEquivalent: [NSString string]]; + [ret setTarget: self]; + return ret; +} +-(void)menuItemTriggered: (id)aSender +{ + YIELD_GUARD; + + const AquaSalFrame* pFrame = mpMenuItem->mpParentMenu ? mpMenuItem->mpParentMenu->getFrame() : NULL; + if( pFrame && AquaSalFrame::isAlive( pFrame ) && ! pFrame->GetWindow()->IsInModalMode() ) + { + SalMenuEvent aMenuEvt( mpMenuItem->mnId, mpMenuItem->mpVCLMenu ); + pFrame->CallCallback(SALEVENT_MENUCOMMAND, &aMenuEvt); + } + else if( mpMenuItem->mpVCLMenu ) + { + // if an item from submenu was selected. the corresponding Window does not exist because + // we use native popup menus, so we have to set the selected menuitem directly + // incidentally this of course works for top level popup menus, too + PopupMenu * pPopupMenu = dynamic_cast<PopupMenu *>(mpMenuItem->mpVCLMenu); + if( pPopupMenu ) + { + // FIXME: revise this ugly code + + // select handlers in vcl are dispatch on the original menu + // if not consumed by the select handler of the current menu + // however since only the starting menu ever came into Execute + // the hierarchy is not build up. Workaround this by getting + // the menu it should have been + + // get started from hierarchy in vcl menus + AquaSalMenu* pParentMenu = mpMenuItem->mpParentMenu; + Menu* pCurMenu = mpMenuItem->mpVCLMenu; + while( pParentMenu && pParentMenu->mpVCLMenu ) + { + pCurMenu = pParentMenu->mpVCLMenu; + pParentMenu = pParentMenu->mpParentSalMenu; + } + + pPopupMenu->SetSelectedEntry( mpMenuItem->mnId ); + pPopupMenu->ImplSelectWithStart( pCurMenu ); + } + else + DBG_ERROR( "menubar item without frame !" ); + } +} +@end + +@implementation OOStatusItemView +-(void)drawRect: (NSRect)aRect +{ + NSGraphicsContext* pContext = [NSGraphicsContext currentContext]; + [pContext saveGraphicsState]; + [SalData::getStatusItem() drawStatusBarBackgroundInRect: aRect withHighlight: NO]; + if( AquaSalMenu::pCurrentMenuBar ) + { + const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() ); + NSRect aFrame = [self frame]; + NSRect aImgRect = { { 2, 0 }, { 0, 0 } }; + for( size_t i = 0; i < rButtons.size(); ++i ) + { + NSRect aFromRect = { { 0, 0 }, + { rButtons[i].maButton.maImage.GetSizePixel().Width(), + rButtons[i].maButton.maImage.GetSizePixel().Height() } }; + aImgRect.origin.y = floor((aFrame.size.height - aFromRect.size.height)/2); + aImgRect.size = aFromRect.size; + if( rButtons[i].mpNSImage ) + [rButtons[i].mpNSImage drawInRect: aImgRect fromRect: aFromRect operation: NSCompositeSourceOver fraction: 1.0]; + aImgRect.origin.x += aFromRect.size.width + 2; + } + } + [pContext restoreGraphicsState]; +} + +-(void)mouseUp: (NSEvent *)pEvent +{ + /* check if button goes up inside one of our status buttons */ + if( AquaSalMenu::pCurrentMenuBar ) + { + const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() ); + NSRect aFrame = [self frame]; + NSRect aImgRect = { { 2, 0 }, { 0, 0 } }; + NSPoint aMousePt = [pEvent locationInWindow]; + for( size_t i = 0; i < rButtons.size(); ++i ) + { + NSRect aFromRect = { { 0, 0 }, + { rButtons[i].maButton.maImage.GetSizePixel().Width(), + rButtons[i].maButton.maImage.GetSizePixel().Height() } }; + aImgRect.origin.y = (aFrame.size.height - aFromRect.size.height)/2; + aImgRect.size = aFromRect.size; + if( aMousePt.x >= aImgRect.origin.x && aMousePt.x <= (aImgRect.origin.x+aImgRect.size.width) && + aMousePt.y >= aImgRect.origin.y && aMousePt.y <= (aImgRect.origin.y+aImgRect.size.height) ) + { + if( AquaSalMenu::pCurrentMenuBar->mpFrame && AquaSalFrame::isAlive( AquaSalMenu::pCurrentMenuBar->mpFrame ) ) + { + SalMenuEvent aMenuEvt( rButtons[i].maButton.mnId, AquaSalMenu::pCurrentMenuBar->mpVCLMenu ); + AquaSalMenu::pCurrentMenuBar->mpFrame->CallCallback(SALEVENT_MENUBUTTONCOMMAND, &aMenuEvt); + } + return; + } + + aImgRect.origin.x += aFromRect.size.width + 2; + } + } +} + +-(void)layout +{ + NSStatusBar* pStatBar = [NSStatusBar systemStatusBar]; + NSSize aSize = { 0, [pStatBar thickness] }; + [self removeAllToolTips]; + if( AquaSalMenu::pCurrentMenuBar ) + { + const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() ); + if( ! rButtons.empty() ) + { + aSize.width = 2; + for( size_t i = 0; i < rButtons.size(); ++i ) + { + NSRect aImgRect = { { aSize.width, floor((aSize.height-rButtons[i].maButton.maImage.GetSizePixel().Height())/2) }, + { rButtons[i].maButton.maImage.GetSizePixel().Width(), + rButtons[i].maButton.maImage.GetSizePixel().Height() } }; + if( rButtons[i].mpToolTipString ) + [self addToolTipRect: aImgRect owner: rButtons[i].mpToolTipString userData: NULL]; + aSize.width += 2 + aImgRect.size.width; + } + } + } + [self setFrameSize: aSize]; +} +@end + + diff --git a/vcl/aqua/source/window/salobj.cxx b/vcl/aqua/source/window/salobj.cxx new file mode 100644 index 000000000000..07d337dcc81a --- /dev/null +++ b/vcl/aqua/source/window/salobj.cxx @@ -0,0 +1,239 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> + +#include "saldata.hxx" +#include "salobj.h" +#include "salframe.h" + +// ======================================================================= + +AquaSalObject::AquaSalObject( AquaSalFrame* pFrame ) : + mpFrame( pFrame ), + mnClipX( -1 ), + mnClipY( -1 ), + mnClipWidth( -1 ), + mnClipHeight( -1 ), + mbClip( false ), + mnX( 0 ), + mnY( 0 ), + mnWidth( 20 ), + mnHeight( 20 ) +{ + maSysData.nSize = sizeof( maSysData ); + maSysData.pView = NULL; + + NSRect aInitFrame = { { 0, 0 }, { 20, 20 } }; + mpClipView = [[NSClipView alloc] initWithFrame: aInitFrame ]; + if( mpClipView ) + { + [mpFrame->getView() addSubview: mpClipView]; + [mpClipView setHidden: YES]; + } + maSysData.pView = [[NSView alloc] initWithFrame: aInitFrame]; + if( maSysData.pView ) + { + if( mpClipView ) + [mpClipView setDocumentView: maSysData.pView]; + } +} + +// ----------------------------------------------------------------------- + +AquaSalObject::~AquaSalObject() +{ + if( maSysData.pView ) + { + NSView *pView = maSysData.pView; + [pView removeFromSuperview]; + [pView release]; + } + if( mpClipView ) + { + [mpClipView removeFromSuperview]; + [mpClipView release]; + } +} + +/* + sadly there seems to be no way to impose clipping on a child view, + especially a QTMovieView which seems to ignore the current context + completely. Also there is no real way to shape a window; on Aqua a + similar effect to non-rectangular windows is achieved by using a + non-opaque window and not painting where one wants the background + to shine through. + + With respect to SalObject this leaves us to having an NSClipView + containing the child view. Even a QTMovieView respects the boundaries of + that, which gives us a clip "region" consisting of one rectangle. + This is gives us an 80% solution only, though. +*/ + +// ----------------------------------------------------------------------- + +void AquaSalObject::ResetClipRegion() +{ + mbClip = false; + setClippedPosSize(); +} + +// ----------------------------------------------------------------------- + +USHORT AquaSalObject::GetClipRegionType() +{ + return SAL_OBJECT_CLIP_INCLUDERECTS; +} + +// ----------------------------------------------------------------------- + +void AquaSalObject::BeginSetClipRegion( ULONG nRectCount ) +{ + mbClip = false; +} + +// ----------------------------------------------------------------------- + +void AquaSalObject::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + if( mbClip ) + { + if( nX < mnClipX ) + { + mnClipWidth += mnClipX - nX; + mnClipX = nX; + } + if( nX + nWidth > mnClipX + mnClipWidth ) + mnClipWidth = nX + nWidth - mnClipX; + if( nY < mnClipY ) + { + mnClipHeight += mnClipY - nY; + mnClipY = nY; + } + if( nY + nHeight > mnClipY + mnClipHeight ) + mnClipHeight = nY + nHeight - mnClipY; + } + else + { + mnClipX = nX; + mnClipY = nY; + mnClipWidth = nWidth; + mnClipHeight = nHeight; + mbClip = true; + } +} + +// ----------------------------------------------------------------------- + +void AquaSalObject::EndSetClipRegion() +{ + setClippedPosSize(); +} + +// ----------------------------------------------------------------------- + +void AquaSalObject::SetPosSize( long nX, long nY, long nWidth, long nHeight ) +{ + mnX = nX; + mnY = nY; + mnWidth = nWidth; + mnHeight = nHeight; + setClippedPosSize(); +} + +// ----------------------------------------------------------------------- + +void AquaSalObject::setClippedPosSize() +{ + NSRect aViewRect = { { 0, 0 }, { mnWidth, mnHeight } }; + if( maSysData.pView ) + { + NSView *pView = maSysData.pView; + [pView setFrame: aViewRect]; + } + + NSRect aClipViewRect = { { mnX, mnY }, { mnWidth, mnHeight } }; + NSPoint aClipPt = { 0, 0 }; + if( mbClip ) + { + aClipViewRect.origin.x += mnClipX; + aClipViewRect.origin.y += mnClipY; + aClipViewRect.size.width = mnClipWidth; + aClipViewRect.size.height = mnClipHeight; + aClipPt.x = mnClipX; + if( mnClipY == 0 ) + aClipPt.y = mnHeight - mnClipHeight;; + } + + mpFrame->VCLToCocoa( aClipViewRect, false ); + [mpClipView setFrame: aClipViewRect]; + + [mpClipView scrollToPoint: aClipPt]; +} + +// ----------------------------------------------------------------------- + +void AquaSalObject::Show( BOOL bVisible ) +{ + if( mpClipView ) + [mpClipView setHidden: (bVisible ? NO : YES)]; +} + +// ----------------------------------------------------------------------- + +void AquaSalObject::Enable( BOOL bEnable ) +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalObject::GrabFocus() +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalObject::SetBackground() +{ +} + +// ----------------------------------------------------------------------- + +void AquaSalObject::SetBackground( SalColor nSalColor ) +{ +} + +// ----------------------------------------------------------------------- + +const SystemEnvData* AquaSalObject::GetSystemData() const +{ + return &maSysData; +} + |