2007-03-22 10:30:00 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Josh Aas <josh@mozilla.com>
|
|
|
|
* Sylvain Pasche <sylvain.pasche@gmail.com>
|
2007-08-21 09:55:18 -07:00
|
|
|
* Stuart Morgan <stuart.morgan@alumni.case.edu>
|
2007-03-22 10:30:00 -07:00
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsCocoaUtils.h"
|
2008-03-26 20:42:57 -07:00
|
|
|
#include "nsMenuBarX.h"
|
|
|
|
#include "nsCocoaWindow.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
|
|
#include "nsIAppShellService.h"
|
|
|
|
#include "nsIXULWindow.h"
|
|
|
|
#include "nsIBaseWindow.h"
|
|
|
|
#include "nsIServiceManager.h"
|
2008-06-28 00:55:30 -07:00
|
|
|
#include "nsMenuUtilsX.h"
|
2008-09-25 07:54:01 -07:00
|
|
|
#include "nsToolkit.h"
|
2007-12-05 15:17:08 -08:00
|
|
|
|
|
|
|
float nsCocoaUtils::MenuBarScreenHeight()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-02-18 09:30:59 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-08-21 09:55:18 -07:00
|
|
|
NSArray* allScreens = [NSScreen screens];
|
|
|
|
if ([allScreens count])
|
|
|
|
return [[allScreens objectAtIndex:0] frame].size.height;
|
|
|
|
else
|
2008-02-18 09:30:59 -08:00
|
|
|
return 0.0; // If there are no screens, there's not much we can say.
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0.0);
|
2007-08-21 09:55:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-05 15:17:08 -08:00
|
|
|
float nsCocoaUtils::FlippedScreenY(float y)
|
2007-08-21 09:55:18 -07:00
|
|
|
{
|
|
|
|
return MenuBarScreenHeight() - y;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-09-28 03:13:58 -07:00
|
|
|
NSRect nsCocoaUtils::GeckoRectToCocoaRect(const nsRect &geckoRect)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-02-18 09:30:59 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-08-21 09:55:18 -07:00
|
|
|
// We only need to change the Y coordinate by starting with the primary screen
|
2007-03-22 10:30:00 -07:00
|
|
|
// height, subtracting the gecko Y coordinate, and subtracting the height.
|
|
|
|
return NSMakeRect(geckoRect.x,
|
2007-08-21 09:55:18 -07:00
|
|
|
MenuBarScreenHeight() - (geckoRect.y + geckoRect.height),
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoRect.width,
|
|
|
|
geckoRect.height);
|
2008-02-18 09:30:59 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRect(0.0, 0.0, 0.0, 0.0));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-09-28 03:13:58 -07:00
|
|
|
nsRect nsCocoaUtils::CocoaRectToGeckoRect(const NSRect &cocoaRect)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-21 09:55:18 -07:00
|
|
|
// We only need to change the Y coordinate by starting with the primary screen
|
2007-03-22 10:30:00 -07:00
|
|
|
// height and subtracting both the cocoa y origin and the height of the
|
|
|
|
// cocoa rect.
|
2008-09-28 03:13:58 -07:00
|
|
|
return nsRect((nscoord)cocoaRect.origin.x,
|
|
|
|
(nscoord)(MenuBarScreenHeight() - (cocoaRect.origin.y + cocoaRect.size.height)),
|
|
|
|
(nscoord)cocoaRect.size.width,
|
|
|
|
(nscoord)cocoaRect.size.height);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-12-05 15:17:08 -08:00
|
|
|
|
|
|
|
|
|
|
|
NSPoint nsCocoaUtils::ScreenLocationForEvent(NSEvent* anEvent)
|
|
|
|
{
|
2008-02-18 09:30:59 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-12-05 15:17:08 -08:00
|
|
|
return [[anEvent window] convertBaseToScreen:[anEvent locationInWindow]];
|
2008-02-18 09:30:59 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0));
|
2007-12-05 15:17:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOL nsCocoaUtils::IsEventOverWindow(NSEvent* anEvent, NSWindow* aWindow)
|
|
|
|
{
|
2008-02-18 09:30:59 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-12-05 15:17:08 -08:00
|
|
|
return NSPointInRect(ScreenLocationForEvent(anEvent), [aWindow frame]);
|
2008-02-18 09:30:59 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
2007-12-05 15:17:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSPoint nsCocoaUtils::EventLocationForWindow(NSEvent* anEvent, NSWindow* aWindow)
|
|
|
|
{
|
2008-02-18 09:30:59 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-12-05 15:17:08 -08:00
|
|
|
return [aWindow convertScreenToBase:ScreenLocationForEvent(anEvent)];
|
2008-02-18 09:30:59 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0));
|
2007-12-05 15:17:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSWindow* nsCocoaUtils::FindWindowUnderPoint(NSPoint aPoint)
|
|
|
|
{
|
2008-02-18 09:30:59 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-12-05 15:17:08 -08:00
|
|
|
int windowCount;
|
|
|
|
NSCountWindows(&windowCount);
|
|
|
|
int* windowList = (int*)malloc(sizeof(int) * windowCount);
|
|
|
|
if (!windowList)
|
|
|
|
return nil;
|
|
|
|
// The list we get back here is in order from front to back.
|
|
|
|
NSWindowList(windowCount, windowList);
|
|
|
|
|
|
|
|
for (int i = 0; i < windowCount; i++) {
|
|
|
|
NSWindow* currentWindow = [NSApp windowWithWindowNumber:windowList[i]];
|
|
|
|
if (currentWindow && NSPointInRect(aPoint, [currentWindow frame])) {
|
|
|
|
free(windowList);
|
|
|
|
return currentWindow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(windowList);
|
|
|
|
return nil;
|
2008-02-18 09:30:59 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-12-05 15:17:08 -08:00
|
|
|
}
|
2008-03-26 20:42:57 -07:00
|
|
|
|
|
|
|
|
|
|
|
#define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1"
|
|
|
|
nsIWidget* nsCocoaUtils::GetHiddenWindowWidget()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
|
|
|
|
if (!appShell) {
|
|
|
|
NS_WARNING("Couldn't get AppShellService in order to get hidden window ref");
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIXULWindow> hiddenWindow;
|
|
|
|
appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
|
|
|
|
if (!hiddenWindow) {
|
|
|
|
// Don't warn, this happens during shutdown, bug 358607.
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
|
|
|
|
baseHiddenWindow = do_GetInterface(hiddenWindow);
|
|
|
|
if (!baseHiddenWindow) {
|
|
|
|
NS_WARNING("Couldn't get nsIBaseWindow from hidden window (nsIXULWindow)");
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWidget> hiddenWindowWidget;
|
|
|
|
if (NS_FAILED(baseHiddenWindow->GetMainWidget(getter_AddRefs(hiddenWindowWidget)))) {
|
|
|
|
NS_WARNING("Couldn't get nsIWidget from hidden window (nsIBaseWindow)");
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hiddenWindowWidget;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nsCocoaUtils::PrepareForNativeAppModalDialog()
|
|
|
|
{
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
2008-04-02 20:58:45 -07:00
|
|
|
|
|
|
|
// Don't do anything if this is embedding. We'll assume that if there is no hidden
|
|
|
|
// window we shouldn't do anything, and that should cover the embedding case.
|
2008-06-28 00:55:30 -07:00
|
|
|
nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
|
2008-04-02 20:58:45 -07:00
|
|
|
if (!hiddenWindowMenuBar)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// First put up the hidden window menu bar so that app menu event handling is correct.
|
|
|
|
hiddenWindowMenuBar->Paint();
|
|
|
|
|
2008-03-26 20:42:57 -07:00
|
|
|
NSMenu* mainMenu = [NSApp mainMenu];
|
|
|
|
NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
|
|
|
|
|
|
|
|
// Create new menu bar for use with modal dialog
|
|
|
|
NSMenu* newMenuBar = [[NSMenu alloc] initWithTitle:@""];
|
|
|
|
|
|
|
|
// Swap in our app menu. Note that the event target is whatever window is up when
|
|
|
|
// the app modal dialog goes up.
|
|
|
|
NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
|
|
|
|
[mainMenu removeItemAtIndex:0];
|
|
|
|
[newMenuBar insertItem:firstMenuItem atIndex:0];
|
|
|
|
[firstMenuItem release];
|
|
|
|
|
|
|
|
// Add standard edit menu
|
2008-06-28 00:55:30 -07:00
|
|
|
[newMenuBar addItem:nsMenuUtilsX::GetStandardEditMenuItem()];
|
2008-03-26 20:42:57 -07:00
|
|
|
|
|
|
|
// Show the new menu bar
|
|
|
|
[NSApp setMainMenu:newMenuBar];
|
|
|
|
[newMenuBar release];
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nsCocoaUtils::CleanUpAfterNativeAppModalDialog()
|
|
|
|
{
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
2008-04-02 20:58:45 -07:00
|
|
|
|
|
|
|
// Don't do anything if this is embedding. We'll assume that if there is no hidden
|
|
|
|
// window we shouldn't do anything, and that should cover the embedding case.
|
2008-06-28 00:55:30 -07:00
|
|
|
nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
|
2008-04-02 20:58:45 -07:00
|
|
|
if (!hiddenWindowMenuBar)
|
|
|
|
return;
|
|
|
|
|
2008-03-26 20:42:57 -07:00
|
|
|
NSWindow* mainWindow = [NSApp mainWindow];
|
2008-04-02 20:58:45 -07:00
|
|
|
if (!mainWindow)
|
|
|
|
hiddenWindowMenuBar->Paint();
|
|
|
|
else
|
2008-03-26 20:42:57 -07:00
|
|
|
[WindowDelegate paintMenubarForWindow:mainWindow];
|
2008-04-02 20:58:45 -07:00
|
|
|
|
2008-03-26 20:42:57 -07:00
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
|
|
}
|
2008-09-25 07:54:01 -07:00
|
|
|
|
|
|
|
unsigned short nsCocoaUtils::GetCocoaEventKeyCode(NSEvent *theEvent)
|
|
|
|
{
|
|
|
|
unsigned short keyCode = [theEvent keyCode];
|
|
|
|
if (nsToolkit::OnLeopardOrLater())
|
|
|
|
return keyCode;
|
|
|
|
NSEventType type = [theEvent type];
|
|
|
|
// GetCocoaEventKeyCode() can get called with theEvent set to a FlagsChanged
|
|
|
|
// event, which triggers an NSInternalInconsistencyException when
|
|
|
|
// charactersIgnoringModifiers is called on it. For some reason there's no
|
|
|
|
// problem calling keyCode on it (as we do above).
|
|
|
|
if ((type != NSKeyDown) && (type != NSKeyUp))
|
|
|
|
return keyCode;
|
|
|
|
NSString *unmodchars = [theEvent charactersIgnoringModifiers];
|
|
|
|
if (!keyCode && ([unmodchars length] == 1)) {
|
|
|
|
// An OS-X-10.4.X-specific Apple bug causes the 'theEvent' parameter of
|
|
|
|
// all calls to performKeyEquivalent: (whether on NSMenu, NSWindow or
|
|
|
|
// NSView objects) to have most of its fields zeroed on a ctrl-ESC event.
|
|
|
|
// These include its keyCode and modifierFlags fields, but fortunately
|
|
|
|
// not its characters and charactersIgnoringModifiers fields. So if
|
|
|
|
// charactersIgnoringModifiers has length == 1 and corresponds to the ESC
|
|
|
|
// character (0x1b), we correct keyCode to 0x35 (kEscapeKeyCode).
|
|
|
|
if ([unmodchars characterAtIndex:0] == 0x1b)
|
|
|
|
keyCode = 0x35;
|
|
|
|
}
|
|
|
|
return keyCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSUInteger nsCocoaUtils::GetCocoaEventModifierFlags(NSEvent *theEvent)
|
|
|
|
{
|
|
|
|
NSUInteger modifierFlags = [theEvent modifierFlags];
|
|
|
|
if (nsToolkit::OnLeopardOrLater())
|
|
|
|
return modifierFlags;
|
|
|
|
NSEventType type = [theEvent type];
|
|
|
|
if ((type != NSKeyDown) && (type != NSKeyUp))
|
|
|
|
return modifierFlags;
|
|
|
|
NSString *unmodchars = [theEvent charactersIgnoringModifiers];
|
|
|
|
if (!modifierFlags && ([unmodchars length] == 1)) {
|
|
|
|
// An OS-X-10.4.X-specific Apple bug causes the 'theEvent' parameter of
|
|
|
|
// all calls to performKeyEquivalent: (whether on NSMenu, NSWindow or
|
|
|
|
// NSView objects) to have most of its fields zeroed on a ctrl-ESC event.
|
|
|
|
// These include its keyCode and modifierFlags fields, but fortunately
|
|
|
|
// not its characters and charactersIgnoringModifiers fields. So if
|
|
|
|
// charactersIgnoringModifiers has length == 1 and corresponds to the ESC
|
|
|
|
// character (0x1b), we correct modifierFlags to NSControlKeyMask. (ESC
|
|
|
|
// key events don't get messed up (anywhere they're sent) on opt-ESC,
|
|
|
|
// shift-ESC or cmd-ESC.)
|
|
|
|
if ([unmodchars characterAtIndex:0] == 0x1b)
|
|
|
|
modifierFlags = NSControlKeyMask;
|
|
|
|
}
|
|
|
|
return modifierFlags;
|
|
|
|
}
|
|
|
|
|