From Apache OpenOffice Wiki
Jump to: navigation, search

Developer infos

Name: Ismael Merzaq

IRC Nickname: Ismael_


Current work

I have implemented key accelerators in native menus (like Cmd+N for new document instead of ctrl+N in the X11 version). This was my first contribution to native port and also to OOo. I also worked on graphics for the native port: U have corrected a bug on image mask to have correct images in the menu, I implemented functions in AquaSalGraphic too (drawAlphaImage() for example). After graphic functions, U worked on improving native windows and dialogs, and implementing missing functions of salframe.cxx (in vcl/aqua/source/window).

I'm also continuing the implementation of native controls for the Google Summer of Code 2007. For more informations, see:


The purpose of this page is to be a place for documentations on my work to help people who would continue my work or use it, or simply want to understand what i do. And, moreover, writing documentation is an important part of coding.

Apple Documentation and References

Apple Carbon/Quartz reference

  • about menus: [1]
  • about images (CGImage): [2]
  • about graphic contexts (CGContexts): [3]

Native windows

see Mac_OS_X_Porting_salframe

Image masks

Short intro

As I explained it on my blog, I have continued the work Pavel Janick did on image masks. At the beginning, my work was to correct a bug concerning images in menus. I firstly thought I would have to concentrate on AquaSalMenu::SetItemImage but the problem wasn't on this function. So my work mainly concerned AquaSalBitmap::CreateMask and AquaSalBitmap::CreateWithMask functions (both located in vcl/aqua/source/gdi/salbmp.cxx). The problem came from the CreateMask function which didn't work as expected. Indeed, it created a mask with random values and so images in the menus had some pixels transparent and some others not, making the image not looking as it should. AquaImageMenuBug.png

In the CreateMask function, the image mask is created from a RawMemorySharedArray (defined in the basebmp namespace, see in bitmapdevice.hxx. The RawMemorySharedArray uses the boost library (typedef boost::shared_array< sal_uInt8 > RawMemorySharedArray;)). Depending on the number of bits per component (colors+alpha), pixels have different values which are added to a RawMemorySharedArray. Then a CGDataprovider is created with the data of the RawMemorySharedArray (CGDataProviderRef xDataProvider( CGDataProviderCreateWithData(NULL, aMaskBuffer.get(), nHeight * nWidth, NULL) ); where aMaskBuffer is defined as basebmp::RawMemorySharedArray aMaskBuffer( new sal_uInt8[ nWidth * nHeight ] );). And finally, the image mask is created from this CGDataProvider (xMask = CGImageMaskCreate(nWidth, nHeight, 8, 8, nWidth, xDataProvider, NULL, true );).


Image and its mask (seen in Icon Composer)

The possible causes of the bug

So the problem may come from the RawMemorySharedArray, it may also come from the creation of the CGDataProvider from it, or from the creation of the imagemask from the cgdataprovider. What is sure is that the problem comes from the CreateMask function in salbmp.cxx, but I haven't been able to fix it. So I decided to do it by another way.

So, how did I do?

Instead of creating an image mask from the alpha of the original image, i created a CGImage from the alpha mask (passed as argument of the function as an AquaSalBitmap&), and i masked the original image with it, using context.

Here's the part of code which masks the image:

CGContextBeginTransparencyLayer(mxGraphicContext, NULL);
CGContextClipToMask(mxGraphicContext, CGRectMake( nX, nY, nDX, nDY), xMask);
CGContextDrawImage(mxGraphicContext, CGRectMake( nX, nY, nDX, nDY), xImage );

We firstly save the context. Then we call CGContextBeginTransparencyLayer(mxGraphicContext, NULL) to have transparency else the transparent regions are black. Then we use the function CGContextClipToMask(mxGraphicContext, CGRectMake( nX, nY, nDX, nDY), xMask). Here's what the AppleDoc says about it:

Maps a mask into the specified rectangle and intersects it with the current clipping
area of the graphics context.

void CGContextClipToMask(
CGContextRef c, 
CGRect rect, 
CGImageRef mask
An image or an image mask.
If the mask parameter is an image mask, then Quartz clips in a manner identical to the 
behavior seen with the function CGContextDrawImage—the mask indicates an area to be left
unchanged when drawing. The source samples of the image mask determine which points of 
the clipping area are changed, acting as an "inverse alpha" value. If the value of a source
sample in the image mask is S, then the corresponding point in the current clipping area is
multiplied by an alpha value of (1–S). For example, if S is 1 then the point in the clipping
area becomes transparent. If S is 0, the point in the clipping area is unchanged.

Then we draw the image on the context (which has been masked) with CGContextDrawImage(mxGraphicContext, CGRectMake( nX, nY, nDX, nDY), xImage ). And we create the CGImage from this context with xMaskedImage=CGBitmapContextCreateImage(mxGraphicContext). Finally we restore the context (CGContextRestoreGState(mxGraphicContext);)

But this is not enough. Indeed, in the function CGContextClipToMask, the mask is a CGImage not the AquaSalBitmap of the arguments (CGImageRef AquaSalBitmap::CreateWithMask( const AquaSalBitmap& rMask, sal_uInt32 nX, sal_uInt32 nY, sal_uInt32 nDX, sal_uInt32 nDY )). So we have to transform the AquaSalBitmap rMask in CGImage. This is done thanks to the CreateCroppedImage function (CGImageRef AquaSalBitmap::CreateCroppedImage( int nX, int nY, int nWidth, int nHeight);) But the AquaSalBitmap rMask can't be a const, so we have 2 possibilities: remove the const in the declaration of the function, or copy the aquasalbitmap. The first solution can't be used because salgdi.cxx and other files need to have it defined with the const mask. So i used the second solution: copying the AquaSalBitmap (rMaskNew.Create( rMask ) creates a copy of rMask).

But there was still a problem. The image mask should be inverted, i.e. the black pixels should become white and the white pixels should become black.


Without inverting the mask, the parts which should be painted are transparent and those which should be transparent are painted.

So i looked for a function to do that in the OOo API. The AquaSalGraphics class provide one but it is not yet implemented. And as its implementation is, in my opinion, difficult and need some time, i didn't implement it yet and so I inverted the image mask using contexts in the function which need it. Instead of inverting the AquaSalBitmap with the OOo API, i inverted the CGImage with the Quartz API. But this solution should stay a temporary solution and, in my opinion, should be replaced as soon as possible by the one using OOo API, mainly for the clarity of code. Here's the code which invert the image thanks to context:

CGContextTranslateCTM (rMaskNew.mxGraphicContext, 0, mnHeight);
CGContextScaleCTM (rMaskNew.mxGraphicContext, 1.0, -1.0);
CGContextSetGrayFillColor(rMaskNew.mxGraphicContext, 1.0, 1.0);
CGContextFillRect(rMaskNew.mxGraphicContext, CGRectMake( nX, nY, nDX, nDY));
CGContextSetBlendMode(rMaskNew.mxGraphicContext,  kCGBlendModeDifference);
CGContextDrawImage(rMaskNew.mxGraphicContext, CGRectMake( nX, nY, nDX, nDY), xMask);

Key accelerators

Short introduction: presenting the basis

The function which interests us is
void AquaSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const KeyCode& rKeyCode, const XubString& rKeyName ) 
  • nPos is the menu item index, but nPos is 0 to n based whereas the carbon API we use for ooo macport is from 1 to n+1 e.g. for the first element nPos=0, and his carbon index will be 1
  • SalMenuItem* pSalMenuItem is a pointer to the menu item we want to add key accelerator
  • const KeyCode& rKeyCode is the keycode of the accelerator. We can get the keycode with rKeyCode.GetCode() and the key modifier(s) (like cmd, shift, ... for Mac OS) with rKeyCode.GetAllModifier()
  • const XubString& rKeyName is not used for the MacOs port


We will use the SetMenuItemModifiers function to chose the modifier(s) of the key accelerator like cmd, shift, alt... and we'll set the key accelerator with two different function depending of the situation: the SetMenuItemCommandKey function for visible key accelerator (like for the N of Cmd+N) and the SetMenuItemKeyGlyph function for non visible characters (like for F1, F2,... or space).

Setting the key accelerator

The difficulty is that the OOo keycodes are different of the ASCII keycodes used in Carbon. For example: A is 65 in ASCII but in OOo it's 512 (defined in Key.hdl) So we have to translate from ooo codes to ascii, but the order of the characters are not exactly the same as ASCII so we translate only by intervals. We obtain this code:

if ((ncode>=KEY_A) && (ncode<=KEY_Z)) {
else if ((ncode>=KEY_0) && (ncode<=KEY_9)) {

We do the same thing for non visible characters but instead of using the ascii code of the given character we use its carbon value.

else if((ncode>=KEY_F1) && (ncode<=KEY_F12)) {
    nresultcode=kMenuF1Glyph+ncode-KEY_F1; // kMenuFnGlyph=kMenuF1Glyph+n only for n<13 then it should be processed for each case
else if((ncode>=KEY_F13) && (ncode<=KEY_F15)) {
    nresultcode=kMenuF13Glyph+ncode-KEY_F13; //kMenuF13Glyph=0x87 and kMenuF12Glyph=0x7A
else if(ncode==KEY_SPACE) {

Then we only have to add the accelator to the menu:

  • For the non visible characters: The syntax of SetMenuItemKeyGlyph is:
OSErr SetMenuItemKeyGlyph (
   MenuRef inMenu,
   SInt16 inItem,
   SInt16 inGlyph

so we use it like this:

SetMenuItemKeyGlyph (
    mrMenuRef, //is the reference to the menu
    nPos+1, // for the reason of using nPos+1 instead of nPos see the short intro
  • For visible characters: The syntax of SetMenuItemCommandKey is:
OSStatus SetMenuItemCommandKey (
   MenuRef inMenu,
   MenuItemIndex inItem,
   Boolean inSetVirtualKey,
   UInt16 inKey

so we use it like this:

SetMenuItemCommandKey (

Key accelerators definitions in for MacOS X

See Mac OS X Porting - Keyboard Shortcuts.

--Ismael 16:43, 19 April 2007 (CEST)

Personal tools