CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / widget / src / cocoa / nsCocoaUtils.mm
blobcf96348338087c444d805cab267316b241f84393
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is mozilla.org code.
16  *
17  * The Initial Developer of the Original Code is 
18  * Netscape Communications Corporation.
19  * Portions created by the Initial Developer are Copyright (C) 1998
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *   Josh Aas <josh@mozilla.com>
24  *   Sylvain Pasche <sylvain.pasche@gmail.com>
25  *   Stuart Morgan <stuart.morgan@alumni.case.edu>
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either the GNU General Public License Version 2 or later (the "GPL"), or
29  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
41 #include "gfxImageSurface.h"
42 #include "nsCocoaUtils.h"
43 #include "nsMenuBarX.h"
44 #include "nsCocoaWindow.h"
45 #include "nsCOMPtr.h"
46 #include "nsIInterfaceRequestorUtils.h"
47 #include "nsIAppShellService.h"
48 #include "nsIXULWindow.h"
49 #include "nsIBaseWindow.h"
50 #include "nsIServiceManager.h"
51 #include "nsMenuUtilsX.h"
52 #include "nsToolkit.h"
54 float nsCocoaUtils::MenuBarScreenHeight()
56   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
58   NSArray* allScreens = [NSScreen screens];
59   if ([allScreens count])
60     return [[allScreens objectAtIndex:0] frame].size.height;
61   else
62     return 0.0; // If there are no screens, there's not much we can say.
64   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0.0);
67 float nsCocoaUtils::FlippedScreenY(float y)
69   return MenuBarScreenHeight() - y;
72 NSRect nsCocoaUtils::GeckoRectToCocoaRect(const nsIntRect &geckoRect)
74   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
76   // We only need to change the Y coordinate by starting with the primary screen
77   // height, subtracting the gecko Y coordinate, and subtracting the height.
78   return NSMakeRect(geckoRect.x,
79                     MenuBarScreenHeight() - (geckoRect.y + geckoRect.height),
80                     geckoRect.width,
81                     geckoRect.height);
83   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRect(0.0, 0.0, 0.0, 0.0));
86 nsIntRect nsCocoaUtils::CocoaRectToGeckoRect(const NSRect &cocoaRect)
88   // We only need to change the Y coordinate by starting with the primary screen
89   // height and subtracting both the cocoa y origin and the height of the
90   // cocoa rect.
91   nsIntRect rect;
92   rect.x = NSToIntRound(cocoaRect.origin.x);
93   rect.y = NSToIntRound(FlippedScreenY(cocoaRect.origin.y + cocoaRect.size.height));
94   rect.width = NSToIntRound(cocoaRect.origin.x + cocoaRect.size.width) - rect.x;
95   rect.height = NSToIntRound(FlippedScreenY(cocoaRect.origin.y)) - rect.y;
96   return rect;
99 NSPoint nsCocoaUtils::ScreenLocationForEvent(NSEvent* anEvent)
101   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
103   // Don't trust mouse locations of mouse move events, see bug 443178.
104   if (!anEvent || [anEvent type] == NSMouseMoved)
105     return [NSEvent mouseLocation];
107   return [[anEvent window] convertBaseToScreen:[anEvent locationInWindow]];
109   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0));
112 BOOL nsCocoaUtils::IsEventOverWindow(NSEvent* anEvent, NSWindow* aWindow)
114   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
116   return NSPointInRect(ScreenLocationForEvent(anEvent), [aWindow frame]);
118   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
121 NSPoint nsCocoaUtils::EventLocationForWindow(NSEvent* anEvent, NSWindow* aWindow)
123   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
125   return [aWindow convertScreenToBase:ScreenLocationForEvent(anEvent)];
127   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0));
130 void nsCocoaUtils::HideOSChromeOnScreen(PRBool aShouldHide, NSScreen* aScreen)
132   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
134   // Keep track of how many hiding requests have been made, so that they can
135   // be nested.
136   static int sMenuBarHiddenCount = 0, sDockHiddenCount = 0;
138   // Always hide the Dock, since it's not necessarily on the primary screen.
139   sDockHiddenCount += aShouldHide ? 1 : -1;
140   NS_ASSERTION(sMenuBarHiddenCount >= 0, "Unbalanced HideMenuAndDockForWindow calls");
142   // Only hide the menu bar if the window is on the same screen.
143   // The menu bar is always on the first screen in the screen list.
144   if (aScreen == [[NSScreen screens] objectAtIndex:0]) {
145     sMenuBarHiddenCount += aShouldHide ? 1 : -1;
146     NS_ASSERTION(sDockHiddenCount >= 0, "Unbalanced HideMenuAndDockForWindow calls");
147   }
149   // TODO This should be upgraded to use [NSApplication setPresentationOptions:]
150   // when support for 10.5 is dropped.
151   if (sMenuBarHiddenCount > 0) {
152     ::SetSystemUIMode(kUIModeAllHidden, 0);
153   } else if (sDockHiddenCount > 0) {
154     ::SetSystemUIMode(kUIModeContentHidden, 0);
155   } else {
156     ::SetSystemUIMode(kUIModeNormal, 0);
157   }
159   NS_OBJC_END_TRY_ABORT_BLOCK;
163 #define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1"
164 nsIWidget* nsCocoaUtils::GetHiddenWindowWidget()
166   nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
167   if (!appShell) {
168     NS_WARNING("Couldn't get AppShellService in order to get hidden window ref");
169     return nsnull;
170   }
171   
172   nsCOMPtr<nsIXULWindow> hiddenWindow;
173   appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
174   if (!hiddenWindow) {
175     // Don't warn, this happens during shutdown, bug 358607.
176     return nsnull;
177   }
178   
179   nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
180   baseHiddenWindow = do_GetInterface(hiddenWindow);
181   if (!baseHiddenWindow) {
182     NS_WARNING("Couldn't get nsIBaseWindow from hidden window (nsIXULWindow)");
183     return nsnull;
184   }
185   
186   nsCOMPtr<nsIWidget> hiddenWindowWidget;
187   if (NS_FAILED(baseHiddenWindow->GetMainWidget(getter_AddRefs(hiddenWindowWidget)))) {
188     NS_WARNING("Couldn't get nsIWidget from hidden window (nsIBaseWindow)");
189     return nsnull;
190   }
191   
192   return hiddenWindowWidget;
195 void nsCocoaUtils::PrepareForNativeAppModalDialog()
197   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
199   // Don't do anything if this is embedding. We'll assume that if there is no hidden
200   // window we shouldn't do anything, and that should cover the embedding case.
201   nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
202   if (!hiddenWindowMenuBar)
203     return;
205   // First put up the hidden window menu bar so that app menu event handling is correct.
206   hiddenWindowMenuBar->Paint();
208   NSMenu* mainMenu = [NSApp mainMenu];
209   NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
210   
211   // Create new menu bar for use with modal dialog
212   NSMenu* newMenuBar = [[NSMenu alloc] initWithTitle:@""];
213   
214   // Swap in our app menu. Note that the event target is whatever window is up when
215   // the app modal dialog goes up.
216   NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
217   [mainMenu removeItemAtIndex:0];
218   [newMenuBar insertItem:firstMenuItem atIndex:0];
219   [firstMenuItem release];
220   
221   // Add standard edit menu
222   [newMenuBar addItem:nsMenuUtilsX::GetStandardEditMenuItem()];
223   
224   // Show the new menu bar
225   [NSApp setMainMenu:newMenuBar];
226   [newMenuBar release];
227   
228   NS_OBJC_END_TRY_ABORT_BLOCK;
231 void nsCocoaUtils::CleanUpAfterNativeAppModalDialog()
233   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
235   // Don't do anything if this is embedding. We'll assume that if there is no hidden
236   // window we shouldn't do anything, and that should cover the embedding case.
237   nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
238   if (!hiddenWindowMenuBar)
239     return;
241   NSWindow* mainWindow = [NSApp mainWindow];
242   if (!mainWindow)
243     hiddenWindowMenuBar->Paint();
244   else
245     [WindowDelegate paintMenubarForWindow:mainWindow];
247   NS_OBJC_END_TRY_ABORT_BLOCK;
250 nsresult nsCocoaUtils::CreateCGImageFromSurface(gfxImageSurface *aFrame, CGImageRef *aResult)
253   PRInt32 width = aFrame->Width();
254   PRInt32 stride = aFrame->Stride();
255   PRInt32 height = aFrame->Height();
256   if ((stride % 4 != 0) || (height < 1) || (width < 1)) {
257     return NS_ERROR_FAILURE;
258   }
260   // Create a CGImageRef with the bits from the image, taking into account
261   // the alpha ordering and endianness of the machine so we don't have to
262   // touch the bits ourselves.
263   CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(NULL,
264                                                                   aFrame->Data(),
265                                                                   stride * height,
266                                                                   NULL);
267   CGColorSpaceRef colorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
268   *aResult = ::CGImageCreate(width,
269                              height,
270                              8,
271                              32,
272                              stride,
273                              colorSpace,
274                              kCGBitmapByteOrder32Host | kCGImageAlphaFirst,
275                              dataProvider,
276                              NULL,
277                              0,
278                              kCGRenderingIntentDefault);
279   ::CGColorSpaceRelease(colorSpace);
280   ::CGDataProviderRelease(dataProvider);
281   return *aResult ? NS_OK : NS_ERROR_FAILURE;
284 nsresult nsCocoaUtils::CreateNSImageFromCGImage(CGImageRef aInputImage, NSImage **aResult)
286   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
288   PRInt32 width = ::CGImageGetWidth(aInputImage);
289   PRInt32 height = ::CGImageGetHeight(aInputImage);
290   NSRect imageRect = ::NSMakeRect(0.0, 0.0, width, height);
292   // Create a new image to receive the Quartz image data.
293   *aResult = [[NSImage alloc] initWithSize:imageRect.size];
295   [*aResult lockFocus];
297   // Get the Quartz context and draw.
298   CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
299   ::CGContextDrawImage(imageContext, *(CGRect*)&imageRect, aInputImage);
301   [*aResult unlockFocus];
302   return NS_OK;
304   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
307 nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, PRUint32 aWhichFrame, NSImage **aResult)
309   nsRefPtr<gfxImageSurface> frame;
310   nsresult rv = aImage->CopyFrame(aWhichFrame,
311                                   imgIContainer::FLAG_SYNC_DECODE,
312                                   getter_AddRefs(frame));
313   if (NS_FAILED(rv) || !frame) {
314     return NS_ERROR_FAILURE;
315   }
316   CGImageRef imageRef = NULL;
317   rv = nsCocoaUtils::CreateCGImageFromSurface(frame, &imageRef);
318   if (NS_FAILED(rv) || !imageRef) {
319     return NS_ERROR_FAILURE;
320   }
322   rv = nsCocoaUtils::CreateNSImageFromCGImage(imageRef, aResult);
323   if (NS_FAILED(rv) || !aResult) {
324     return NS_ERROR_FAILURE;
325   }
326   ::CGImageRelease(imageRef);
327   return NS_OK;