1 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
15 * The Original Code is mozilla.org code.
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.
23 * Josh Aas <josh@mozilla.com>
24 * Colin Barrett <cbarrett@mozilla.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsCocoaWindow.h"
42 #include "nsObjCExceptions.h"
44 #include "nsWidgetsCID.h"
45 #include "nsGUIEvent.h"
46 #include "nsIRollupListener.h"
47 #include "nsCocoaUtils.h"
48 #include "nsChildView.h"
49 #include "nsWindowMap.h"
50 #include "nsIAppShell.h"
51 #include "nsIAppShellService.h"
52 #include "nsIBaseWindow.h"
53 #include "nsIInterfaceRequestorUtils.h"
54 #include "nsIXULWindow.h"
55 #include "nsIPrefService.h"
56 #include "nsIPrefBranch.h"
57 #include "nsToolkit.h"
58 #include "nsPrintfCString.h"
59 #include "nsThreadUtils.h"
60 #include "nsMenuBarX.h"
61 #include "nsMenuUtilsX.h"
62 #include "nsStyleConsts.h"
63 #include "nsNativeThemeColors.h"
65 #include "gfxPlatform.h"
68 PRInt32 gXULModalLevel = 0;
69 // In principle there should be only one app-modal window at any given time.
70 // But sometimes, despite our best efforts, another window appears above the
71 // current app-modal window. So we need to keep a linked list of app-modal
72 // windows. (A non-sheet window that appears above an app-modal window is
73 // also made app-modal.) See nsCocoaWindow::SetModal().
74 nsCocoaWindowList *gAppModalWindowList = NULL;
76 PRBool gCocoaWindowMethodsSwizzled = PR_FALSE;
78 // defined in nsMenuBarX.mm
79 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
81 // defined in nsChildView.mm
82 extern nsIRollupListener * gRollupListener;
83 extern nsIWidget * gRollupWidget;
84 extern BOOL gSomeMenuBarPainted;
86 #define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1"
88 NS_IMPL_ISUPPORTS_INHERITED1(nsCocoaWindow, Inherited, nsPIWidgetCocoa)
91 // A note on testing to see if your object is a sheet...
92 // |mWindowType == eWindowType_sheet| is true if your gecko nsIWidget is a sheet
93 // widget - whether or not the sheet is showing. |[mWindow isSheet]| will return
94 // true *only when the sheet is actually showing*. Choose your test wisely.
97 // roll up any popup windows
98 static void RollUpPopups()
100 if (gRollupListener && gRollupWidget)
101 gRollupListener->Rollup(nsnull);
105 nsCocoaWindow::nsCocoaWindow()
109 , mSheetWindowParent(nil)
110 , mPopupContentView(nil)
111 , mIsResizing(PR_FALSE)
112 , mWindowMadeHere(PR_FALSE)
113 , mSheetNeedsShow(PR_FALSE)
115 , mNumModalDescendents(0)
121 nsCocoaWindow::~nsCocoaWindow()
123 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
125 // notify the children that we're gone
126 for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
127 nsCocoaWindow* childWindow = static_cast<nsCocoaWindow*>(kid);
128 childWindow->mParent = nsnull;
131 if (mWindow && mWindowMadeHere) {
132 // we want to unhook the delegate here because we don't want events
133 // sent to it after this object has been destroyed
134 [mWindow setDelegate:nil];
135 [mWindow autorelease];
136 [mDelegate autorelease];
139 NS_IF_RELEASE(mPopupContentView);
141 // Deal with the possiblity that we're being destroyed while running modal.
142 NS_ASSERTION(!mModal, "Widget destroyed while running modal!");
145 NS_ASSERTION(gXULModalLevel >= 0, "Wierdness setting modality!");
148 NS_OBJC_END_TRY_ABORT_BLOCK;
152 // Very large windows work in Cocoa, but can take a long time to
153 // process (multiple minutes), during which time the system is
154 // unresponsive and seems hung. Although it's likely that windows
155 // much larger than screen size are bugs, be conservative and only
156 // intervene if the values are so large as to hog the cpu.
157 #define SIZE_LIMIT 100000
158 static bool WindowSizeAllowed(PRInt32 aWidth, PRInt32 aHeight)
160 if (aWidth > SIZE_LIMIT) {
161 NS_ERROR(nsPrintfCString(256, "Requested Cocoa window width of %d is too much, max allowed is %d\n",
162 aWidth, SIZE_LIMIT).get());
165 if (aHeight > SIZE_LIMIT) {
166 NS_ERROR(nsPrintfCString(256, "Requested Cocoa window height of %d is too much, max allowed is %d\n",
167 aHeight, SIZE_LIMIT).get());
174 // Utility method for implementing both Create(nsIWidget ...) and
175 // Create(nsNativeWidget...)
176 nsresult nsCocoaWindow::StandardCreate(nsIWidget *aParent,
178 EVENT_CALLBACK aHandleEventFunction,
179 nsIDeviceContext *aContext,
180 nsIAppShell *aAppShell,
181 nsIToolkit *aToolkit,
182 nsWidgetInitData *aInitData,
183 nsNativeWidget aNativeWindow)
185 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
187 if (!WindowSizeAllowed(aRect.width, aRect.height))
188 return NS_ERROR_FAILURE;
190 Inherited::BaseCreate(aParent, aRect, aHandleEventFunction, aContext, aAppShell,
191 aToolkit, aInitData);
195 // create a window if we aren't given one, always create if this should be a popup
196 if (!aNativeWindow || (aInitData && aInitData->mWindowType == eWindowType_popup)) {
197 // decide on a window type
198 PRBool allOrDefault = PR_FALSE;
200 allOrDefault = aInitData->mBorderStyle == eBorderStyle_all ||
201 aInitData->mBorderStyle == eBorderStyle_default;
202 mWindowType = aInitData->mWindowType;
203 // if a toplevel window was requested without a titlebar, use a dialog
204 if (mWindowType == eWindowType_toplevel &&
205 (aInitData->mBorderStyle == eBorderStyle_none ||
207 !(aInitData->mBorderStyle & eBorderStyle_title)))
208 mWindowType = eWindowType_dialog;
211 allOrDefault = PR_TRUE;
212 mWindowType = eWindowType_toplevel;
215 // Some applications like Camino use native popup windows
216 // (native context menus, native tooltips)
217 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
219 PRBool useNativeContextMenus;
220 nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows", &useNativeContextMenus);
221 if (NS_SUCCEEDED(rv) && useNativeContextMenus && mWindowType == eWindowType_popup)
225 // we default to NSBorderlessWindowMask, add features if needed
226 unsigned int features = NSBorderlessWindowMask;
228 // Configure the window we will create based on the window type
231 case eWindowType_invisible:
232 case eWindowType_child:
234 case eWindowType_dialog:
236 switch (aInitData->mBorderStyle)
238 case eBorderStyle_none:
240 case eBorderStyle_default:
241 features |= NSTitledWindowMask;
243 case eBorderStyle_all:
244 features |= NSClosableWindowMask;
245 features |= NSTitledWindowMask;
246 features |= NSResizableWindowMask;
247 features |= NSMiniaturizableWindowMask;
250 if (aInitData->mBorderStyle & eBorderStyle_title) {
251 features |= NSTitledWindowMask;
252 features |= NSMiniaturizableWindowMask;
254 if (aInitData->mBorderStyle & eBorderStyle_resizeh)
255 features |= NSResizableWindowMask;
256 if (aInitData->mBorderStyle & eBorderStyle_close)
257 features |= NSClosableWindowMask;
262 features |= NSTitledWindowMask;
263 features |= NSMiniaturizableWindowMask;
266 case eWindowType_sheet:
268 nsWindowType parentType;
269 aParent->GetWindowType(parentType);
270 if (parentType != eWindowType_invisible &&
271 aInitData->mBorderStyle & eBorderStyle_resizeh) {
272 features = NSResizableWindowMask;
275 features = NSMiniaturizableWindowMask;
279 features = NSMiniaturizableWindowMask;
281 features |= NSTitledWindowMask;
283 case eWindowType_popup:
284 features |= NSBorderlessWindowMask;
286 case eWindowType_toplevel:
287 features |= NSTitledWindowMask;
288 features |= NSMiniaturizableWindowMask;
289 if (allOrDefault || aInitData->mBorderStyle & eBorderStyle_close)
290 features |= NSClosableWindowMask;
291 if (allOrDefault || aInitData->mBorderStyle & eBorderStyle_resizeh)
292 features |= NSResizableWindowMask;
295 NS_ERROR("Unhandled window type!");
296 return NS_ERROR_FAILURE;
299 /* Apple's docs on NSWindow styles say that "a window's style mask should
300 * include NSTitledWindowMask if it includes any of the others [besides
301 * NSBorderlessWindowMask]". This implies that a borderless window
302 * shouldn't have any other styles than NSBorderlessWindowMask.
304 if (!(features & NSTitledWindowMask))
305 features = NSBorderlessWindowMask;
308 * We pass a content area rect to initialize the native Cocoa window. The
309 * content rect we give is the same size as the size we're given by gecko.
310 * The origin we're given for non-popup windows is moved down by the height
311 * of the menu bar so that an origin of (0,100) from gecko puts the window
312 * 100 pixels below the top of the available desktop area. We also move the
313 * origin down by the height of a title bar if it exists. This is so the
314 * origin that gecko gives us for the top-left of the window turns out to
315 * be the top-left of the window we create. This is how it was done in
316 * Carbon. If it ought to be different we'll probably need to look at all
319 * Note: This means that if you put a secondary screen on top of your main
320 * screen and open a window in the top screen, it'll be incorrectly shifted
321 * down by the height of the menu bar. Same thing would happen in Carbon.
323 * Note: If you pass a rect with 0,0 for an origin, the window ends up in a
324 * weird place for some reason. This stops that without breaking popups.
326 NSRect rect = nsCocoaUtils::GeckoRectToCocoaRect(aRect);
328 // compensate for difference between frame and content area height (e.g. title bar)
329 NSRect newWindowFrame = [NSWindow frameRectForContentRect:rect styleMask:features];
331 rect.origin.y -= (newWindowFrame.size.height - rect.size.height);
333 if (mWindowType != eWindowType_popup)
334 rect.origin.y -= ::GetMBarHeight();
336 // NSLog(@"Top-level window being created at Cocoa rect: %f, %f, %f, %f\n",
337 // rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
339 Class windowClass = [NSWindow class];
340 // If we have a titlebar on a top-level window, we want to be able to control the
341 // titlebar color (for unified windows), so use the special ToolbarWindow class.
342 // Note that we need to check the window type because we mark sheets sheets as
344 if (mWindowType == eWindowType_toplevel &&
345 (features & NSTitledWindowMask))
346 windowClass = [ToolbarWindow class];
347 // If we're a popup window we need to use the PopupWindow class.
348 else if (mWindowType == eWindowType_popup)
349 windowClass = [PopupWindow class];
350 // If we're a non-popup borderless window we need to use the
351 // BorderlessWindow class.
352 else if (features == NSBorderlessWindowMask)
353 windowClass = [BorderlessWindow class];
356 mWindow = [[windowClass alloc] initWithContentRect:rect styleMask:features
357 backing:NSBackingStoreBuffered defer:YES];
359 if (mWindowType == eWindowType_popup) {
360 [mWindow setLevel:NSPopUpMenuWindowLevel];
361 [mWindow setHasShadow:YES];
363 // we need to make our content view a ChildView
364 mPopupContentView = new nsChildView();
365 if (mPopupContentView) {
366 NS_ADDREF(mPopupContentView);
368 nsIWidget* thisAsWidget = static_cast<nsIWidget*>(this);
369 mPopupContentView->StandardCreate(thisAsWidget, aRect, aHandleEventFunction,
370 aContext, aAppShell, aToolkit, nsnull, nsnull);
372 ChildView* newContentView = (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET);
373 [mWindow setContentView:newContentView];
376 else if (mWindowType == eWindowType_invisible) {
377 [mWindow setLevel:kCGDesktopWindowLevelKey];
380 [mWindow setBackgroundColor:[NSColor whiteColor]];
381 [mWindow setContentMinSize:NSMakeSize(60, 60)];
382 [mWindow setReleasedWhenClosed:NO];
384 // setup our notification delegate. Note that setDelegate: does NOT retain.
385 mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
386 [mWindow setDelegate:mDelegate];
388 mWindowMadeHere = PR_TRUE;
391 mWindow = (NSWindow*)aNativeWindow;
396 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
400 // Create a nsCocoaWindow using a native window provided by the application
401 NS_IMETHODIMP nsCocoaWindow::Create(nsNativeWidget aNativeWindow,
403 EVENT_CALLBACK aHandleEventFunction,
404 nsIDeviceContext *aContext,
405 nsIAppShell *aAppShell,
406 nsIToolkit *aToolkit,
407 nsWidgetInitData *aInitData)
409 return(StandardCreate(nsnull, aRect, aHandleEventFunction, aContext,
410 aAppShell, aToolkit, aInitData, aNativeWindow));
414 NS_IMETHODIMP nsCocoaWindow::Create(nsIWidget* aParent,
416 EVENT_CALLBACK aHandleEventFunction,
417 nsIDeviceContext *aContext,
418 nsIAppShell *aAppShell,
419 nsIToolkit *aToolkit,
420 nsWidgetInitData *aInitData)
422 return(StandardCreate(aParent, aRect, aHandleEventFunction, aContext,
423 aAppShell, aToolkit, aInitData, nsnull));
427 NS_IMETHODIMP nsCocoaWindow::Destroy()
429 if (mPopupContentView)
430 mPopupContentView->Destroy();
432 nsBaseWidget::OnDestroy();
433 nsBaseWidget::Destroy();
439 nsIWidget* nsCocoaWindow::GetSheetWindowParent(void)
441 if (mWindowType != eWindowType_sheet)
443 nsCocoaWindow *parent = static_cast<nsCocoaWindow*>(mParent);
444 while (parent && (parent->mWindowType == eWindowType_sheet))
445 parent = static_cast<nsCocoaWindow*>(parent->mParent);
450 void* nsCocoaWindow::GetNativeData(PRUint32 aDataType)
452 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
454 void* retVal = nsnull;
457 // to emulate how windows works, we always have to return a NSView
458 // for NS_NATIVE_WIDGET
459 case NS_NATIVE_WIDGET:
460 case NS_NATIVE_DISPLAY:
461 retVal = [mWindow contentView];
464 case NS_NATIVE_WINDOW:
468 case NS_NATIVE_GRAPHIC:
469 // There isn't anything that makes sense to return here,
470 // and it doesn't matter so just return nsnull.
471 NS_ASSERTION(0, "Requesting NS_NATIVE_GRAPHIC on a top-level window!");
477 NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
481 NS_IMETHODIMP nsCocoaWindow::IsVisible(PRBool & aState)
483 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
485 aState = ([mWindow isVisible] || mSheetNeedsShow);
488 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
492 NS_IMETHODIMP nsCocoaWindow::SetModal(PRBool aState)
495 nsCocoaWindow *aParent = static_cast<nsCocoaWindow*>(mParent);
498 // When a non-sheet window gets "set modal", make the window(s) that it
499 // appears over behave as they should. We can't rely on native methods to
500 // do this, for the following reason: The OS runs modal non-sheet windows
501 // in an event loop (using [NSApplication runModalForWindow:] or similar
502 // methods) that's incompatible with the modal event loop in nsXULWindow::
503 // ShowModal() (each of these event loops is "exclusive", and can't run at
504 // the same time as other (similar) event loops).
505 if (mWindowType != eWindowType_sheet) {
507 if (aParent->mNumModalDescendents++ == 0) {
508 NSWindow *aWindow = aParent->GetCocoaWindow();
509 if (aParent->mWindowType != eWindowType_invisible) {
510 [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:NO];
511 [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:NO];
512 [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:NO];
515 aParent = static_cast<nsCocoaWindow*>(aParent->mParent);
517 [mWindow setLevel:NSModalPanelWindowLevel];
518 nsCocoaWindowList *windowList = new nsCocoaWindowList;
520 windowList->window = this; // Don't ADDREF
521 windowList->prev = gAppModalWindowList;
522 gAppModalWindowList = windowList;
528 NS_ASSERTION(gXULModalLevel >= 0, "Mismatched call to nsCocoaWindow::SetModal(PR_FALSE)!");
529 if (mWindowType != eWindowType_sheet) {
531 if (--aParent->mNumModalDescendents == 0) {
532 NSWindow *aWindow = aParent->GetCocoaWindow();
533 if (aParent->mWindowType != eWindowType_invisible) {
534 [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:YES];
535 [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:YES];
536 [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:YES];
539 NS_ASSERTION(aParent->mNumModalDescendents >= 0, "Widget hierarchy changed while modal!");
540 aParent = static_cast<nsCocoaWindow*>(aParent->mParent);
542 if (gAppModalWindowList) {
543 NS_ASSERTION(gAppModalWindowList->window == this, "Widget hierarchy changed while modal!");
544 nsCocoaWindowList *saved = gAppModalWindowList;
545 gAppModalWindowList = gAppModalWindowList->prev;
546 delete saved; // "window" not ADDREFed
548 if (mWindowType == eWindowType_popup)
549 [mWindow setLevel:NSPopUpMenuWindowLevel];
551 [mWindow setLevel:NSNormalWindowLevel];
558 // Hide or show this window
559 NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
561 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
563 // We need to re-execute sometimes in order to bring already-visible
565 if (!mSheetNeedsShow && !bState && ![mWindow isVisible])
568 nsIWidget* parentWidget = mParent;
569 nsCOMPtr<nsPIWidgetCocoa> piParentWidget(do_QueryInterface(parentWidget));
570 NSWindow* nativeParentWindow = (parentWidget) ?
571 (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW) : nil;
573 if (bState && !mBounds.IsEmpty()) {
574 if (mWindowType == eWindowType_sheet) {
575 // bail if no parent window (its basically what we do in Carbon)
576 if (!nativeParentWindow || !piParentWidget)
577 return NS_ERROR_FAILURE;
579 NSWindow* topNonSheetWindow = nativeParentWindow;
581 // If this sheet is the child of another sheet, hide the parent so that
582 // this sheet can be displayed. Leave the parent mSheetNeedsShow alone,
583 // that is only used to handle sibling sheet contention. The parent will
584 // return once there are no more child sheets.
585 PRBool parentIsSheet = PR_FALSE;
586 if (NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
588 piParentWidget->GetSheetWindowParent(&topNonSheetWindow);
589 [NSApp endSheet:nativeParentWindow];
590 [nativeParentWindow setAcceptsMouseMovedEvents:NO];
593 nsCocoaWindow* sheetShown = nsnull;
594 if (NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_TRUE, &sheetShown)) &&
595 (!sheetShown || sheetShown == this)) {
596 // If this sheet is already the sheet actually being shown, don't
597 // tell it to show again. Otherwise the number of calls to
598 // [NSApp beginSheet...] won't match up with [NSApp endSheet...].
599 if (![mWindow isVisible]) {
600 mSheetNeedsShow = PR_FALSE;
601 mSheetWindowParent = topNonSheetWindow;
602 // Only set contextInfo if our parent isn't a sheet.
603 NSWindow* contextInfo = parentIsSheet ? nil : mSheetWindowParent;
604 [TopLevelWindowData deactivateInWindow:mSheetWindowParent];
605 [mWindow setAcceptsMouseMovedEvents:YES];
606 [NSApp beginSheet:mWindow
607 modalForWindow:mSheetWindowParent
608 modalDelegate:mDelegate
609 didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
610 contextInfo:contextInfo];
611 [TopLevelWindowData activateInWindow:mWindow];
612 SendSetZLevelEvent();
616 // A sibling of this sheet is active, don't show this sheet yet.
617 // When the active sheet hides, its brothers and sisters that have
618 // mSheetNeedsShow set will have their opportunities to display.
619 mSheetNeedsShow = PR_TRUE;
622 else if (mWindowType == eWindowType_popup) {
623 // If a popup window is shown after being hidden, it needs to be "reset"
624 // for it to receive any mouse events aside from mouse-moved events
625 // (because it was removed from the "window cache" when it was hidden
626 // -- see below). Setting the window number to -1 and then back to its
627 // original value seems to accomplish this. The idea was "borrowed"
628 // from the Java Embedding Plugin.
629 int windowNumber = [mWindow windowNumber];
630 [mWindow _setWindowNumber:-1];
631 [mWindow _setWindowNumber:windowNumber];
632 [mWindow setAcceptsMouseMovedEvents:YES];
633 [mWindow orderFront:nil];
634 SendSetZLevelEvent();
635 // If our popup window is a non-native context menu, tell the OS (and
636 // other programs) that a menu has opened. This is how the OS knows to
637 // close other programs' context menus when ours open.
638 if ([mWindow isKindOfClass:[PopupWindow class]] &&
639 [(PopupWindow*) mWindow isContextMenu]) {
640 [[NSDistributedNotificationCenter defaultCenter]
641 postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
642 object:@"org.mozilla.gecko.PopupWindow"];
645 // if a parent was supplied, set its child window. This will cause the
646 // child window to appear above the parent and move when the parent
647 // does. Setting this needs to happen after the _setWindowNumber calls
648 // above, otherwise the window doesn't focus properly.
649 if (nativeParentWindow)
650 [nativeParentWindow addChildWindow:mWindow
651 ordered:NSWindowAbove];
654 [mWindow setAcceptsMouseMovedEvents:YES];
655 [mWindow makeKeyAndOrderFront:nil];
656 SendSetZLevelEvent();
660 // roll up any popups if a top-level window is going away
661 if (mWindowType == eWindowType_toplevel)
664 // now get rid of the window/sheet
665 if (mWindowType == eWindowType_sheet) {
666 if (mSheetNeedsShow) {
667 // This is an attempt to hide a sheet that never had a chance to
668 // be shown. There's nothing to do other than make sure that it
670 mSheetNeedsShow = PR_FALSE;
673 // get sheet's parent *before* hiding the sheet (which breaks the linkage)
674 NSWindow* sheetParent = mSheetWindowParent;
677 [NSApp endSheet:mWindow];
679 [mWindow setAcceptsMouseMovedEvents:NO];
681 [TopLevelWindowData deactivateInWindow:mWindow];
683 nsCocoaWindow* siblingSheetToShow = nsnull;
684 PRBool parentIsSheet = PR_FALSE;
686 if (nativeParentWindow && piParentWidget &&
687 NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_FALSE, &siblingSheetToShow)) &&
688 siblingSheetToShow) {
689 // First, give sibling sheets an opportunity to show.
690 siblingSheetToShow->Show(PR_TRUE);
692 else if (nativeParentWindow && piParentWidget &&
693 NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
695 // Only set contextInfo if the parent of the parent sheet we're about
696 // to restore isn't itself a sheet.
697 NSWindow* contextInfo = sheetParent;
698 nsIWidget* grandparentWidget = nil;
699 if (NS_SUCCEEDED(piParentWidget->GetRealParent(&grandparentWidget)) && grandparentWidget) {
700 nsCOMPtr<nsPIWidgetCocoa> piGrandparentWidget(do_QueryInterface(grandparentWidget));
701 PRBool grandparentIsSheet = PR_FALSE;
702 if (piGrandparentWidget && NS_SUCCEEDED(piGrandparentWidget->GetIsSheet(&grandparentIsSheet)) &&
703 grandparentIsSheet) {
707 // If there are no sibling sheets, but the parent is a sheet, restore
708 // it. It wasn't sent any deactivate events when it was hidden, so
709 // don't call through Show, just let the OS put it back up.
710 [nativeParentWindow setAcceptsMouseMovedEvents:YES];
711 [NSApp beginSheet:nativeParentWindow
712 modalForWindow:sheetParent
713 modalDelegate:[nativeParentWindow delegate]
714 didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
715 contextInfo:contextInfo];
718 // Sheet, that was hard. No more siblings or parents, going back
720 [sheetParent makeKeyAndOrderFront:nil];
721 [sheetParent setAcceptsMouseMovedEvents:YES];
723 SendSetZLevelEvent();
727 // If the window is a popup window with a parent window we need to
728 // unhook it here before ordering it out. When you order out the child
729 // of a window it hides the parent window.
730 if (mWindowType == eWindowType_popup && nativeParentWindow)
731 [nativeParentWindow removeChildWindow:mWindow];
733 [mWindow orderOut:nil];
734 // Unless it's explicitly removed from NSApp's "window cache", a popup
735 // window will keep receiving mouse-moved events even after it's been
736 // "ordered out" (instead of the browser window that was underneath it,
737 // until you click on that window). This is bmo bug 378645, but it's
738 // surely an Apple bug. The "window cache" is an undocumented subsystem,
739 // all of whose methods are included in the NSWindowCache category of
740 // the NSApplication class (in header files generated using class-dump).
741 // This workaround was "borrowed" from the Java Embedding Plugin (which
742 // uses it for a different purpose).
743 if (mWindowType == eWindowType_popup) {
744 [NSApp _removeWindowFromCache:mWindow];
745 // Apple's focus ring APIs sometimes clip themselves when they draw under
746 // other windows. Redraw the window that was likely under the popup to
747 // get focus rings to draw correctly. Sometimes the window is not properly
748 // the parent of the popup, so we can't just tell the parent to redraw.
749 // We only have this problem on 10.4. See bug 417124.
750 if (!nsToolkit::OnLeopardOrLater()) {
751 NSWindow* keyWindow = [NSApp keyWindow];
754 NSWindow* mainWindow = [NSApp mainWindow];
755 if (mainWindow && mainWindow != keyWindow)
756 [mainWindow display];
760 // it's very important to turn off mouse moved events when hiding a window, otherwise
761 // the windows' tracking rects will interfere with each other. (bug 356528)
762 [mWindow setAcceptsMouseMovedEvents:NO];
764 // If our popup window is a non-native context menu, tell the OS (and
765 // other programs) that a menu has closed.
766 if ([mWindow isKindOfClass:[PopupWindow class]] &&
767 [(PopupWindow*) mWindow isContextMenu]) {
768 [[NSDistributedNotificationCenter defaultCenter]
769 postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification"
770 object:@"org.mozilla.gecko.PopupWindow"];
775 if (mPopupContentView)
776 mPopupContentView->Show(bState);
780 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
784 void nsCocoaWindow::MakeBackgroundTransparent(PRBool aTransparent)
786 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
788 BOOL currentTransparency = ![mWindow isOpaque];
789 if (aTransparent != currentTransparency) {
790 [mWindow setOpaque:!aTransparent];
791 [mWindow setBackgroundColor:(aTransparent ? [NSColor clearColor] : [NSColor whiteColor])];
794 NS_OBJC_END_TRY_ABORT_BLOCK;
798 nsTransparencyMode nsCocoaWindow::GetTransparencyMode()
800 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
802 return [mWindow isOpaque] ? eTransparencyOpaque : eTransparencyTransparent;
804 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(eTransparencyOpaque);
808 // This is called from nsMenuPopupFrame when making a popup transparent.
809 // For other window types, nsChildView::SetTransparencyMode is used.
810 void nsCocoaWindow::SetTransparencyMode(nsTransparencyMode aMode)
812 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
814 BOOL isTransparent = aMode == eTransparencyTransparent;
816 BOOL currentTransparency = ![mWindow isOpaque];
817 if (isTransparent != currentTransparency) {
818 // Take care of window transparency
819 MakeBackgroundTransparent(isTransparent);
820 // Make sure our content view is also transparent
821 if (mPopupContentView) {
822 ChildView *childView = (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET);
824 [childView setTransparent:isTransparent];
829 NS_OBJC_END_TRY_ABORT_BLOCK;
833 NS_METHOD nsCocoaWindow::AddEventListener(nsIEventListener * aListener)
835 nsBaseWidget::AddEventListener(aListener);
837 if (mPopupContentView)
838 mPopupContentView->AddEventListener(aListener);
844 NS_IMETHODIMP nsCocoaWindow::Enable(PRBool aState)
850 NS_IMETHODIMP nsCocoaWindow::IsEnabled(PRBool *aState)
858 NS_IMETHODIMP nsCocoaWindow::ConstrainPosition(PRBool aAllowSlop,
859 PRInt32 *aX, PRInt32 *aY)
865 NS_IMETHODIMP nsCocoaWindow::Move(PRInt32 aX, PRInt32 aY)
867 if (!mWindow || (mBounds.x == aX && mBounds.y == aY))
870 // The point we have is in Gecko coordinates (origin top-left). Convert
871 // it to Cocoa ones (origin bottom-left).
872 NSPoint coord = {aX, nsCocoaUtils::FlippedScreenY(aY)};
873 [mWindow setFrameTopLeftPoint:coord];
879 // Position the window behind the given window
880 NS_METHOD nsCocoaWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
881 nsIWidget *aWidget, PRBool aActivate)
887 // Note bug 278777, we need to update state when the window is unminimized
888 // from the dock by users.
889 NS_METHOD nsCocoaWindow::SetSizeMode(PRInt32 aMode)
891 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
893 PRInt32 previousMode;
894 nsBaseWidget::GetSizeMode(&previousMode);
896 nsresult rv = nsBaseWidget::SetSizeMode(aMode);
897 NS_ENSURE_SUCCESS(rv, rv);
899 if (aMode == nsSizeMode_Normal) {
900 if ([mWindow isMiniaturized])
901 [mWindow deminiaturize:nil];
902 else if (previousMode == nsSizeMode_Maximized && [mWindow isZoomed])
905 else if (aMode == nsSizeMode_Minimized) {
906 if (![mWindow isMiniaturized])
907 [mWindow miniaturize:nil];
909 else if (aMode == nsSizeMode_Maximized) {
910 if ([mWindow isMiniaturized])
911 [mWindow deminiaturize:nil];
912 if (![mWindow isZoomed])
918 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
922 NS_IMETHODIMP nsCocoaWindow::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
924 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
926 if (!WindowSizeAllowed(aWidth, aHeight))
927 return NS_ERROR_FAILURE;
929 nsRect windowBounds(nsCocoaUtils::CocoaRectToGeckoRect([mWindow frame]));
930 BOOL isMoving = (windowBounds.x != aX || windowBounds.y != aY);
931 BOOL isResizing = (windowBounds.width != aWidth || windowBounds.height != aHeight);
933 if (IsResizing() || !mWindow || (!isMoving && !isResizing))
936 nsRect geckoRect(aX, aY, aWidth, aHeight);
937 NSRect newFrame = nsCocoaUtils::GeckoRectToCocoaRect(geckoRect);
939 // We have to report the size event -first-, to make sure that content
940 // repositions itself. Cocoa views are anchored at the bottom left,
941 // so if we don't do this our child view will end up being stuck in the
942 // wrong place during a resize.
944 ReportSizeEvent(&newFrame);
947 // We ignore aRepaint -- we have to call display:YES, otherwise the
948 // title bar doesn't immediately get repainted and is displayed in
949 // the wrong place, leading to a visual jump.
950 [mWindow setFrame:newFrame display:YES];
953 // now, check whether we got the frame that we wanted
954 NSRect actualFrame = [mWindow frame];
955 if (newFrame.size.width != actualFrame.size.width || newFrame.size.height != actualFrame.size.height) {
956 // We didn't; the window must have been too big or otherwise invalid.
957 // Report -another- resize in this case, to make sure things are in
958 // the right place. This will cause some visual jitter, but
959 // shouldn't happen often.
965 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
969 NS_IMETHODIMP nsCocoaWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
971 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
973 if (!WindowSizeAllowed(aWidth, aHeight))
974 return NS_ERROR_FAILURE;
976 nsRect windowBounds(nsCocoaUtils::CocoaRectToGeckoRect([mWindow frame]));
977 return Resize(windowBounds.x, windowBounds.y, aWidth, aHeight, aRepaint);
979 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
983 NS_IMETHODIMP nsCocoaWindow::GetScreenBounds(nsRect &aRect)
985 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
987 nsRect windowFrame = nsCocoaUtils::CocoaRectToGeckoRect([mWindow frame]);
988 aRect.x = windowFrame.x;
989 aRect.y = windowFrame.y;
990 aRect.width = windowFrame.width;
991 aRect.height = windowFrame.height;
992 // printf("GetScreenBounds: output: %d,%d,%d,%d\n", aRect.x, aRect.y, aRect.width, aRect.height);
995 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
999 PRBool nsCocoaWindow::OnPaint(nsPaintEvent &event)
1001 return PR_TRUE; // don't dispatch the update event
1005 NS_IMETHODIMP nsCocoaWindow::SetTitle(const nsAString& aTitle)
1007 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1009 const nsString& strTitle = PromiseFlatString(aTitle);
1010 NSString* title = [NSString stringWithCharacters:strTitle.get() length:strTitle.Length()];
1011 [mWindow setTitle:title];
1015 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1019 NS_IMETHODIMP nsCocoaWindow::Invalidate(const nsRect & aRect, PRBool aIsSynchronous)
1021 if (mPopupContentView)
1022 return mPopupContentView->Invalidate(aRect, aIsSynchronous);
1028 NS_IMETHODIMP nsCocoaWindow::Invalidate(PRBool aIsSynchronous)
1030 if (mPopupContentView)
1031 return mPopupContentView->Invalidate(aIsSynchronous);
1037 NS_IMETHODIMP nsCocoaWindow::Update()
1039 if (mPopupContentView)
1040 return mPopupContentView->Update();
1046 // Pass notification of some drag event to Gecko
1048 // The drag manager has let us know that something related to a drag has
1049 // occurred in this window. It could be any number of things, ranging from
1050 // a drop, to a drag enter/leave, or a drag over event. The actual event
1051 // is passed in |aMessage| and is passed along to our event hanlder so Gecko
1053 PRBool nsCocoaWindow::DragEvent(unsigned int aMessage, Point aMouseGlobal, UInt16 aKeyModifiers)
1059 NS_IMETHODIMP nsCocoaWindow::SendSetZLevelEvent()
1061 nsZLevelEvent event(PR_TRUE, NS_SETZLEVEL, this);
1063 event.refPoint.x = mBounds.x;
1064 event.refPoint.y = mBounds.y;
1065 event.time = PR_IntervalNow();
1067 event.mImmediate = PR_TRUE;
1069 nsEventStatus status = nsEventStatus_eIgnore;
1070 DispatchEvent(&event, status);
1076 NS_IMETHODIMP nsCocoaWindow::GetChildSheet(PRBool aShown, nsCocoaWindow** _retval)
1078 nsIWidget* child = GetFirstChild();
1082 if (NS_SUCCEEDED(child->GetWindowType(type)) && type == eWindowType_sheet) {
1083 // if it's a sheet, it must be an nsCocoaWindow
1084 nsCocoaWindow* cocoaWindow = static_cast<nsCocoaWindow*>(child);
1085 if ((aShown && [cocoaWindow->mWindow isVisible]) ||
1086 (!aShown && cocoaWindow->mSheetNeedsShow)) {
1087 *_retval = cocoaWindow;
1091 child = child->GetNextSibling();
1100 NS_IMETHODIMP nsCocoaWindow::GetRealParent(nsIWidget** parent)
1107 NS_IMETHODIMP nsCocoaWindow::GetIsSheet(PRBool* isSheet)
1109 mWindowType == eWindowType_sheet ? *isSheet = PR_TRUE : *isSheet = PR_FALSE;
1114 NS_IMETHODIMP nsCocoaWindow::GetSheetWindowParent(NSWindow** sheetWindowParent)
1116 *sheetWindowParent = mSheetWindowParent;
1121 NS_IMETHODIMP nsCocoaWindow::ResetInputState()
1127 // Invokes callback and ProcessEvent methods on Event Listener object
1129 nsCocoaWindow::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
1131 aStatus = nsEventStatus_eIgnore;
1133 nsIWidget* aWidget = event->widget;
1134 NS_IF_ADDREF(aWidget);
1137 aStatus = (*mEventCallback)(event);
1139 // Dispatch to event listener if event was not consumed
1140 if (mEventListener && aStatus != nsEventStatus_eConsumeNoDefault)
1141 aStatus = mEventListener->ProcessEvent(*event);
1143 NS_IF_RELEASE(aWidget);
1150 nsCocoaWindow::DispatchSizeModeEvent(nsSizeMode aSizeMode)
1152 nsSizeModeEvent event(PR_TRUE, NS_SIZEMODE, this);
1153 event.mSizeMode = aSizeMode;
1154 event.time = PR_IntervalNow();
1156 nsEventStatus status = nsEventStatus_eIgnore;
1157 DispatchEvent(&event, status);
1161 nsCocoaWindow::ReportSizeEvent(NSRect *r)
1163 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1167 windowFrame = [mWindow contentRectForFrameRect:(*r)];
1169 windowFrame = [mWindow contentRectForFrameRect:[mWindow frame]];
1170 mBounds.width = nscoord(windowFrame.size.width);
1171 mBounds.height = nscoord(windowFrame.size.height);
1173 nsSizeEvent sizeEvent(PR_TRUE, NS_SIZE, this);
1174 sizeEvent.time = PR_IntervalNow();
1176 sizeEvent.windowSize = &mBounds;
1177 sizeEvent.mWinWidth = mBounds.width;
1178 sizeEvent.mWinHeight = mBounds.height;
1180 nsEventStatus status = nsEventStatus_eIgnore;
1181 DispatchEvent(&sizeEvent, status);
1183 NS_OBJC_END_TRY_ABORT_BLOCK;
1187 NS_IMETHODIMP nsCocoaWindow::SetMenuBar(void *aMenuBar)
1190 mMenuBar->SetParent(nsnull);
1191 mMenuBar = static_cast<nsMenuBarX*>(aMenuBar);
1193 // We paint the hidden window menu bar if no other menu bar has been painted
1194 // yet so that some reasonable menu bar is displayed when the app starts up.
1195 if (!gSomeMenuBarPainted && mMenuBar && (nsMenuUtilsX::GetHiddenWindowMenuBar() == mMenuBar))
1202 NS_IMETHODIMP nsCocoaWindow::SetFocus(PRBool aState)
1204 if (mPopupContentView)
1205 mPopupContentView->SetFocus(aState);
1211 NS_IMETHODIMP nsCocoaWindow::ShowMenuBar(PRBool aShow)
1213 return NS_ERROR_FAILURE;
1217 NS_IMETHODIMP nsCocoaWindow::WidgetToScreen(const nsRect& aOldRect, nsRect& aNewRect)
1219 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1221 nsRect r = nsCocoaUtils::CocoaRectToGeckoRect([mWindow contentRectForFrameRect:[mWindow frame]]);
1223 aNewRect.x = r.x + aOldRect.x;
1224 aNewRect.y = r.y + aOldRect.y;
1225 aNewRect.width = aOldRect.width;
1226 aNewRect.height = aOldRect.height;
1230 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1234 NS_IMETHODIMP nsCocoaWindow::ScreenToWidget(const nsRect& aOldRect, nsRect& aNewRect)
1236 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1238 nsRect r = nsCocoaUtils::CocoaRectToGeckoRect([mWindow contentRectForFrameRect:[mWindow frame]]);
1240 aNewRect.x = aOldRect.x - r.x;
1241 aNewRect.y = aOldRect.y - r.y;
1242 aNewRect.width = aOldRect.width;
1243 aNewRect.height = aOldRect.height;
1247 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1251 nsMenuBarX* nsCocoaWindow::GetMenuBar()
1257 NS_IMETHODIMP nsCocoaWindow::CaptureRollupEvents(nsIRollupListener * aListener,
1259 PRBool aConsumeRollupEvent)
1261 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1263 NS_IF_RELEASE(gRollupListener);
1264 NS_IF_RELEASE(gRollupWidget);
1267 gRollupListener = aListener;
1268 NS_ADDREF(aListener);
1269 gRollupWidget = this;
1271 // Sometimes more than one popup window can be visible at the same time
1272 // (e.g. nested non-native context menus, or the test case (attachment
1273 // 276885) for bmo bug 392389, which displays a non-native combo-box in
1274 // a non-native popup window). In these cases the "active" popup window
1275 // (the one that corresponds to the current gRollupWidget) should be the
1276 // topmost -- the (nested) context menu the mouse is currently over, or
1277 // the combo-box's drop-down list (when it's displayed). But (among
1278 // windows that have the same "level") OS X makes topmost the window that
1279 // last received a mouse-down event, which may be incorrect (in the combo-
1280 // box case, it makes topmost the window containing the combo-box). So
1281 // here we fiddle with a non-native popup window's level to make sure the
1282 // "active" one is always above any other non-native popup windows that
1284 if (mWindow && (mWindowType == eWindowType_popup))
1285 [mWindow setLevel:NSPopUpMenuWindowLevel];
1287 if (mWindow && (mWindowType == eWindowType_popup))
1288 [mWindow setLevel:NSModalPanelWindowLevel];
1293 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1297 NS_IMETHODIMP nsCocoaWindow::GetAttention(PRInt32 aCycleCount)
1299 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1301 [NSApp requestUserAttention:NSInformationalRequest];
1304 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1308 NS_IMETHODIMP nsCocoaWindow::SetWindowShadowStyle(PRInt32 aStyle)
1310 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1312 if ([mWindow hasShadow] != (aStyle != NS_STYLE_WINDOW_SHADOW_NONE))
1313 [mWindow setHasShadow:(aStyle != NS_STYLE_WINDOW_SHADOW_NONE)];
1316 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1320 NS_IMETHODIMP nsCocoaWindow::SetWindowTitlebarColor(nscolor aColor, PRBool aActive)
1322 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1324 // If our cocoa window isn't a ToolbarWindow, something is wrong.
1325 if (![mWindow isKindOfClass:[ToolbarWindow class]]) {
1326 // Don't output a warning for the hidden window.
1327 NS_WARN_IF_FALSE(SameCOMIdentity(nsCocoaUtils::GetHiddenWindowWidget(), (nsIWidget*)this),
1328 "Calling SetWindowTitlebarColor on window that isn't of the ToolbarWindow class.");
1329 return NS_ERROR_FAILURE;
1332 // If they pass a color with a complete transparent alpha component, use the
1333 // native titlebar appearance.
1334 if (NS_GET_A(aColor) == 0) {
1335 [(ToolbarWindow*)mWindow setTitlebarColor:nil forActiveWindow:(BOOL)aActive];
1337 // Transform from sRGBA to monitor RGBA. This seems like it would make trying
1338 // to match the system appearance lame, so probably we just shouldn't color
1340 if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
1341 cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBATransform();
1344 color[0] = NS_GET_R(aColor);
1345 color[1] = NS_GET_G(aColor);
1346 color[2] = NS_GET_B(aColor);
1347 cmsDoTransform(transform, color, color, 1);
1348 aColor = NS_RGB(color[0], color[1], color[2]);
1352 [(ToolbarWindow*)mWindow setTitlebarColor:[NSColor colorWithDeviceRed:NS_GET_R(aColor)/255.0
1353 green:NS_GET_G(aColor)/255.0
1354 blue:NS_GET_B(aColor)/255.0
1355 alpha:NS_GET_A(aColor)/255.0]
1356 forActiveWindow:(BOOL)aActive];
1360 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1364 gfxASurface* nsCocoaWindow::GetThebesSurface()
1366 if (mPopupContentView)
1367 return mPopupContentView->GetThebesSurface();
1372 NS_IMETHODIMP nsCocoaWindow::BeginSecureKeyboardInput()
1374 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1376 nsresult rv = nsBaseWidget::BeginSecureKeyboardInput();
1377 if (NS_SUCCEEDED(rv))
1378 ::EnableSecureEventInput();
1381 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1385 NS_IMETHODIMP nsCocoaWindow::EndSecureKeyboardInput()
1387 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1389 nsresult rv = nsBaseWidget::EndSecureKeyboardInput();
1390 if (NS_SUCCEEDED(rv))
1391 ::DisableSecureEventInput();
1394 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1398 // Callback used by the default titlebar and toolbar shading.
1399 // *aIn == 0 at the top of the titlebar/toolbar, *aIn == 1 at the bottom
1401 nsCocoaWindow::UnifiedShading(void* aInfo, const float* aIn, float* aOut)
1403 UnifiedGradientInfo* info = (UnifiedGradientInfo*)aInfo;
1404 // The gradient percentage at the bottom of the titlebar / top of the toolbar
1405 float start = info->titlebarHeight / (info->titlebarHeight + info->toolbarHeight - 1);
1406 const float startGrey = NativeGreyColorAsFloat(headerStartGrey, info->windowIsMain);
1407 const float endGrey = NativeGreyColorAsFloat(headerEndGrey, info->windowIsMain);
1408 // *aIn is the gradient percentage of the titlebar or toolbar gradient,
1409 // a is the gradient percentage of the whole unified gradient.
1410 float a = info->drawTitlebar ? *aIn * start : start + *aIn * (1 - start);
1411 float result = (1.0f - a) * startGrey + a * endGrey;
1419 @implementation WindowDelegate
1422 // We try to find a gecko menu bar to paint. If one does not exist, just paint
1423 // the application menu by itself so that a window doesn't have some other
1424 // window's menu bar.
1425 + (void)paintMenubarForWindow:(NSWindow*)aWindow
1427 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1429 // make sure we only act on windows that have this kind of
1430 // object as a delegate
1431 id windowDelegate = [aWindow delegate];
1432 if ([windowDelegate class] != [self class])
1435 nsCocoaWindow* geckoWidget = [windowDelegate geckoWidget];
1436 NS_ASSERTION(geckoWidget, "Window delegate not returning a gecko widget!");
1438 nsMenuBarX* geckoMenuBar = geckoWidget->GetMenuBar();
1440 geckoMenuBar->Paint();
1443 // sometimes we don't have a native application menu early in launching
1444 if (!sApplicationMenu)
1447 NSMenu* mainMenu = [NSApp mainMenu];
1448 NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
1450 // Create a new menu bar.
1451 // We create a GeckoNSMenu because all menu bar NSMenu objects should use that subclass for
1452 // key handling reasons.
1453 GeckoNSMenu* newMenuBar = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"];
1455 // move the application menu from the existing menu bar to the new one
1456 NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
1457 [mainMenu removeItemAtIndex:0];
1458 [newMenuBar insertItem:firstMenuItem atIndex:0];
1459 [firstMenuItem release];
1461 // set our new menu bar as the main menu
1462 [NSApp setMainMenu:newMenuBar];
1463 [newMenuBar release];
1466 NS_OBJC_END_TRY_ABORT_BLOCK;
1470 - (id)initWithGeckoWindow:(nsCocoaWindow*)geckoWind
1472 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1475 mGeckoWindow = geckoWind;
1476 mToplevelActiveState = PR_FALSE;
1479 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1483 - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize
1487 return proposedFrameSize;
1491 - (void)windowDidResize:(NSNotification *)aNotification
1493 if (!mGeckoWindow || mGeckoWindow->IsResizing())
1496 mGeckoWindow->ReportSizeEvent();
1500 - (void)windowDidBecomeMain:(NSNotification *)aNotification
1502 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1506 // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
1507 // app modally. If one of those is up then we want it to retain its menu bar.
1508 if ([NSApp _isRunningAppModal])
1510 NSWindow* window = [aNotification object];
1512 [WindowDelegate paintMenubarForWindow:window];
1514 NS_OBJC_END_TRY_ABORT_BLOCK;
1518 - (void)windowDidResignMain:(NSNotification *)aNotification
1522 // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
1523 // app modally. If one of those is up then we want it to retain its menu bar.
1524 if ([NSApp _isRunningAppModal])
1526 nsRefPtr<nsMenuBarX> hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
1527 if (hiddenWindowMenuBar) {
1528 // printf("painting hidden window menu bar due to window losing main status\n");
1529 hiddenWindowMenuBar->Paint();
1534 - (void)windowDidBecomeKey:(NSNotification *)aNotification
1536 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1538 NSWindow* window = [aNotification object];
1539 if ([window isSheet])
1540 [WindowDelegate paintMenubarForWindow:window];
1542 NS_OBJC_END_TRY_ABORT_BLOCK;
1546 - (void)windowDidResignKey:(NSNotification *)aNotification
1548 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1550 // If a sheet just resigned key then we should paint the menu bar
1551 // for whatever window is now main.
1552 NSWindow* window = [aNotification object];
1553 if ([window isSheet])
1554 [WindowDelegate paintMenubarForWindow:[NSApp mainWindow]];
1556 NS_OBJC_END_TRY_ABORT_BLOCK;
1560 - (void)windowWillMove:(NSNotification *)aNotification
1566 - (void)windowDidMove:(NSNotification *)aNotification
1568 // Dispatch the move event to Gecko
1569 nsGUIEvent guiEvent(PR_TRUE, NS_MOVE, mGeckoWindow);
1571 mGeckoWindow->GetScreenBounds(rect);
1572 guiEvent.refPoint.x = rect.x;
1573 guiEvent.refPoint.y = rect.y;
1574 guiEvent.time = PR_IntervalNow();
1575 nsEventStatus status = nsEventStatus_eIgnore;
1576 mGeckoWindow->DispatchEvent(&guiEvent, status);
1580 - (BOOL)windowShouldClose:(id)sender
1582 // We only want to send NS_XUL_CLOSE and let gecko close the window
1583 nsGUIEvent guiEvent(PR_TRUE, NS_XUL_CLOSE, mGeckoWindow);
1584 guiEvent.time = PR_IntervalNow();
1585 nsEventStatus status = nsEventStatus_eIgnore;
1586 mGeckoWindow->DispatchEvent(&guiEvent, status);
1587 return NO; // gecko will do it
1591 - (void)windowWillClose:(NSNotification *)aNotification
1597 - (void)windowWillMiniaturize:(NSNotification *)aNotification
1603 - (void)windowDidMiniaturize:(NSNotification *)aNotification
1606 mGeckoWindow->DispatchSizeModeEvent(nsSizeMode_Minimized);
1610 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
1613 mGeckoWindow->DispatchSizeModeEvent(nsSizeMode_Normal);
1617 - (void)sendFocusEvent:(PRUint32)eventType
1622 nsEventStatus status = nsEventStatus_eIgnore;
1623 nsGUIEvent focusGuiEvent(PR_TRUE, eventType, mGeckoWindow);
1624 focusGuiEvent.time = PR_IntervalNow();
1625 mGeckoWindow->DispatchEvent(&focusGuiEvent, status);
1629 - (void)didEndSheet:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo
1631 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1633 // Note: 'contextInfo' (if it is set) is the window that is the parent of
1634 // the sheet. The value of contextInfo is determined in
1635 // nsCocoaWindow::Show(). If it's set, 'contextInfo' is always the top-
1636 // level window, not another sheet itself. But 'contextInfo' is nil if
1637 // our parent window is also a sheet -- in that case we shouldn't send
1638 // the top-level window any activate events (because it's our parent
1639 // window that needs to get these events, not the top-level window).
1640 [TopLevelWindowData deactivateInWindow:sheet];
1641 [sheet orderOut:self];
1643 [TopLevelWindowData activateInWindow:(NSWindow*)contextInfo];
1645 NS_OBJC_END_TRY_ABORT_BLOCK;
1649 - (nsCocoaWindow*)geckoWidget
1651 return mGeckoWindow;
1655 - (PRBool)toplevelActiveState
1657 return mToplevelActiveState;
1661 - (void)sendToplevelActivateEvents
1663 if (!mToplevelActiveState) {
1664 [self sendFocusEvent:NS_GOTFOCUS];
1665 [self sendFocusEvent:NS_ACTIVATE];
1666 mToplevelActiveState = PR_TRUE;
1671 - (void)sendToplevelDeactivateEvents
1673 if (mToplevelActiveState) {
1674 [self sendFocusEvent:NS_DEACTIVATE];
1675 [self sendFocusEvent:NS_LOSTFOCUS];
1676 mToplevelActiveState = PR_FALSE;
1683 // Category on NSWindow so callers can use the same method on both ToolbarWindows
1684 // and NSWindows for accessing the background color.
1685 @implementation NSWindow(ToolbarWindowCompat)
1687 - (NSColor*)windowBackgroundColor
1689 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1691 return [self backgroundColor];
1693 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1699 @interface ToolbarWindow(Private)
1701 - (void)redrawTitlebar;
1706 // This class allows us to have a "unified toolbar" style window. It works like this:
1707 // 1) We set the window's style to textured.
1708 // 2) Because of this, the background color applies to the entire window, including
1709 // the titlebar area. For normal textured windows, the default pattern is a
1710 // "brushed metal" image on Tiger and a unified gradient on Leopard.
1711 // 3) We set the background color to a custom NSColor subclass that knows how tall the window is.
1712 // When -set is called on it, it sets a pattern (with a draw callback) as the fill. In that callback,
1713 // it paints the the titlebar and background colors in the correct areas of the context it's given,
1714 // which will fill the entire window (CG will tile it horizontally for us).
1715 // 4) Whenever the window's main state changes and when [window display] is called,
1716 // Cocoa redraws the titlebar using the patternDraw callback function.
1718 // This class also provides us with a pill button to show/hide the toolbar.
1720 // Drawing the unified gradient in the titlebar and the toolbar works like this:
1721 // 1) In the style sheet we set the toolbar's -moz-appearance to -moz-mac-unified-toolbar.
1722 // 2) When the toolbar is drawn, Gecko calls nsNativeThemeCocoa::DrawWidgetBackground
1723 // for the widget type NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR.
1724 // 3) This calls DrawUnifiedToolbar which finds the toolbar frame's ToolbarWindow
1725 // and passes the toolbar frame's height to setUnifiedToolbarHeight.
1726 // 4) If the toolbar height has changed, a titlebar redraw is triggered by
1727 // [self display] and the upper part of the unified gradient is drawn in the
1729 // 5) DrawUnifiedToolbar draws the lower part of the unified gradient in the toolbar.
1731 // Whenever the unified gradient is drawn in the titlebar or the toolbar, both
1732 // titlebar height and toolbar height must be known in order to construct the
1733 // correct gradient (which is a linear gradient with the length
1734 // titlebarHeight + toolbarHeight - 1). But you can only get from the toolbar frame
1735 // to the containing window - the other direction doesn't work. That's why the
1736 // toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply
1737 // query the window for its titlebar height when drawing the toolbar.
1738 @implementation ToolbarWindow
1740 - (id)initWithContentRect:(NSRect)aContentRect styleMask:(unsigned int)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
1742 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1744 aStyle = aStyle | NSTexturedBackgroundWindowMask;
1745 if ((self = [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag])) {
1746 mColor = [[TitlebarAndBackgroundColor alloc] initWithActiveTitlebarColor:nil
1747 inactiveTitlebarColor:nil
1748 backgroundColor:[NSColor whiteColor]
1750 // Call the superclass's implementation, to avoid our guard method below.
1751 [super setBackgroundColor:mColor];
1753 mUnifiedToolbarHeight = 0.0f;
1754 mSuppressPainting = NO;
1756 // setBottomCornerRounded: is a private API call, so we check to make sure
1757 // we respond to it just in case.
1758 if ([self respondsToSelector:@selector(setBottomCornerRounded:)])
1759 [self setBottomCornerRounded:NO];
1763 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1769 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1774 NS_OBJC_END_TRY_ABORT_BLOCK;
1778 // We don't provide our own implementation of -backgroundColor because NSWindow
1779 // looks at it, apparently. This is here to keep someone from messing with our
1780 // custom NSColor subclass.
1781 - (void)setBackgroundColor:(NSColor*)aColor
1783 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1785 [mColor setBackgroundColor:aColor];
1787 NS_OBJC_END_TRY_ABORT_BLOCK;
1791 // If you need to get at the background color of the window (in the traditional
1792 // sense) use this method instead.
1793 - (NSColor*)windowBackgroundColor
1795 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1797 return [mColor backgroundColor];
1799 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1803 // Pass nil here to get the default appearance.
1804 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive
1806 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1808 [mColor setTitlebarColor:aColor forActiveWindow:aActive];
1810 NS_OBJC_END_TRY_ABORT_BLOCK;
1813 // This is called by nsNativeThemeCocoa.mm's DrawUnifiedToolbar.
1814 // We need to know the toolbar's height in order to draw the correct
1815 // unified gradient in the titlebar.
1816 - (void)setUnifiedToolbarHeight:(float)aToolbarHeight
1818 if (mUnifiedToolbarHeight == aToolbarHeight)
1820 mUnifiedToolbarHeight = aToolbarHeight;
1821 [self redrawTitlebar];
1825 - (float)unifiedToolbarHeight
1827 return mUnifiedToolbarHeight;
1830 - (float)titlebarHeight
1832 NSRect frameRect = [self frame];
1833 return frameRect.size.height - [self contentRectForFrameRect:frameRect].size.height;
1836 - (BOOL)isPaintingSuppressed
1838 return mSuppressPainting;
1841 // Always show the toolbar pill button.
1848 // Dispatch a toolbar pill button clicked message to Gecko.
1849 - (void)_toolbarPillButtonClicked:(id)sender
1851 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1853 nsCocoaWindow *geckoWindow = [[self delegate] geckoWidget];
1856 nsEventStatus status = nsEventStatus_eIgnore;
1857 nsGUIEvent guiEvent(PR_TRUE, NS_OS_TOOLBAR, geckoWindow);
1858 guiEvent.time = PR_IntervalNow();
1859 geckoWindow->DispatchEvent(&guiEvent, status);
1861 NS_OBJC_END_TRY_ABORT_BLOCK;
1864 // Retain and release "self" to avoid crashes when our widget (and its native
1865 // window) is closed as a result of processing a key equivalent (e.g.
1866 // Command+w or Command+q). This workaround is only needed for a window
1867 // that can become key.
1868 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
1870 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
1872 NSWindow *nativeWindow = [self retain];
1873 BOOL retval = [super performKeyEquivalent:theEvent];
1874 [nativeWindow release];
1877 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
1880 - (void)sendEvent:(NSEvent *)anEvent
1882 NSEventType type = [anEvent type];
1886 case NSLeftMouseDown:
1888 case NSRightMouseDown:
1889 case NSRightMouseUp:
1890 case NSOtherMouseDown:
1891 case NSOtherMouseUp:
1893 case NSLeftMouseDragged:
1894 case NSRightMouseDragged:
1895 case NSOtherMouseDragged:
1897 // Drop all mouse events if a modal window has appeared above us.
1898 // This helps make us behave as if the OS were running a "real" modal
1900 id delegate = [self delegate];
1901 if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
1902 nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
1904 if (gAppModalWindowList && (widget != gAppModalWindowList->window))
1906 if (widget->HasModalDescendents())
1916 [super sendEvent:anEvent];
1921 @implementation ToolbarWindow(Private)
1923 // [self display] seems to be the only way to repaint a window's titlebar.
1924 // The bad thing about it is that it repaints all the window's subviews as well.
1925 // So we use a guard to prevent unnecessary redrawing.
1926 - (void)redrawTitlebar
1928 mSuppressPainting = YES;
1930 mSuppressPainting = NO;
1935 // Custom NSColor subclass where most of the work takes place for drawing in
1936 // the titlebar area.
1937 @implementation TitlebarAndBackgroundColor
1939 - (id)initWithActiveTitlebarColor:(NSColor*)aActiveTitlebarColor
1940 inactiveTitlebarColor:(NSColor*)aInactiveTitlebarColor
1941 backgroundColor:(NSColor*)aBackgroundColor
1942 forWindow:(NSWindow*)aWindow
1944 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1946 if ((self = [super init])) {
1947 mActiveTitlebarColor = [aActiveTitlebarColor retain];
1948 mInactiveTitlebarColor = [aInactiveTitlebarColor retain];
1949 mBackgroundColor = [aBackgroundColor retain];
1950 mWindow = aWindow; // weak ref to avoid a cycle
1954 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1960 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1962 [mActiveTitlebarColor release];
1963 [mInactiveTitlebarColor release];
1964 [mBackgroundColor release];
1967 NS_OBJC_END_TRY_ABORT_BLOCK;
1970 // Our pattern width is 1 pixel. CoreGraphics can cache and tile for us.
1971 static const float sPatternWidth = 1.0f;
1973 // Callback where all of the drawing for this color takes place.
1974 void patternDraw(void* aInfo, CGContextRef aContext)
1976 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1978 TitlebarAndBackgroundColor *color = (TitlebarAndBackgroundColor*)aInfo;
1979 NSColor *backgroundColor = [color backgroundColor];
1980 ToolbarWindow *window = (ToolbarWindow*)[color window];
1981 BOOL isMain = [window isMainWindow];
1982 NSColor *titlebarColor = isMain ? [color activeTitlebarColor] : [color inactiveTitlebarColor];
1984 // Remember: this context is NOT flipped, so the origin is in the bottom left.
1985 float titlebarHeight = [window titlebarHeight];
1986 float titlebarOrigin = [window frame].size.height - titlebarHeight;
1988 UnifiedGradientInfo info = { titlebarHeight, [window unifiedToolbarHeight], isMain, YES };
1990 [NSGraphicsContext saveGraphicsState];
1991 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:aContext flipped:NO]];
1993 // If the titlebar color is nil, draw the default titlebar shading.
1994 if (!titlebarColor) {
1995 // Create and draw a CGShading that uses nsCocoaWindow::UnifiedShading() as its callback.
1996 CGFunctionCallbacks callbacks = {0, nsCocoaWindow::UnifiedShading, NULL};
1997 CGFunctionRef function = CGFunctionCreate(&info, 1, NULL, 4, NULL, &callbacks);
1998 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
1999 CGShadingRef shading = CGShadingCreateAxial(colorSpace,
2000 CGPointMake(0.0f, titlebarOrigin + titlebarHeight),
2001 CGPointMake(0.0f, titlebarOrigin),
2003 CGColorSpaceRelease(colorSpace);
2004 CGFunctionRelease(function);
2005 CGContextDrawShading(aContext, shading);
2006 CGShadingRelease(shading);
2008 // Draw the one pixel border at the bottom of the titlebar.
2009 if ([window unifiedToolbarHeight] == 0) {
2010 [NativeGreyColorAsNSColor(headerBorderGrey, isMain) set];
2011 NSRectFill(NSMakeRect(0.0f, titlebarOrigin, sPatternWidth, 1.0f));
2014 // if the titlebar color is not nil, just set and draw it normally.
2015 [titlebarColor set];
2016 NSRectFill(NSMakeRect(0.0f, titlebarOrigin, sPatternWidth, titlebarHeight));
2019 // Draw the background color of the window everywhere but where the titlebar is.
2020 [backgroundColor set];
2021 NSRectFill(NSMakeRect(0.0f, 0.0f, 1.0f, titlebarOrigin));
2023 [NSGraphicsContext restoreGraphicsState];
2025 NS_OBJC_END_TRY_ABORT_BLOCK;
2031 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2033 CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2035 // Set up the pattern to be as tall as our window, and one pixel wide.
2036 // CoreGraphics can cache and tile us quickly.
2037 CGPatternCallbacks callbacks = {0, &patternDraw, NULL};
2038 CGPatternRef pattern = CGPatternCreate(self, CGRectMake(0.0f, 0.0f, sPatternWidth, [mWindow frame].size.height),
2039 CGAffineTransformIdentity, 1, [mWindow frame].size.height,
2040 kCGPatternTilingConstantSpacing, true, &callbacks);
2042 // Set the pattern as the fill, which is what we were asked to do. All our
2043 // drawing will take place in the patternDraw callback.
2044 CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
2045 CGContextSetFillColorSpace(context, patternSpace);
2046 CGColorSpaceRelease(patternSpace);
2047 float component = 1.0f;
2048 CGContextSetFillPattern(context, pattern, &component);
2049 CGPatternRelease(pattern);
2051 NS_OBJC_END_TRY_ABORT_BLOCK;
2055 // Pass nil here to get the default appearance.
2056 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive
2058 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2061 [mActiveTitlebarColor autorelease];
2062 mActiveTitlebarColor = [aColor retain];
2064 [mInactiveTitlebarColor autorelease];
2065 mInactiveTitlebarColor = [aColor retain];
2068 NS_OBJC_END_TRY_ABORT_BLOCK;
2072 - (NSColor*)activeTitlebarColor
2074 return mActiveTitlebarColor;
2078 - (NSColor*)inactiveTitlebarColor
2080 return mInactiveTitlebarColor;
2084 - (void)setBackgroundColor:(NSColor*)aColor
2086 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2088 [mBackgroundColor autorelease];
2089 mBackgroundColor = [aColor retain];
2091 NS_OBJC_END_TRY_ABORT_BLOCK;
2095 - (NSColor*)backgroundColor
2097 return mBackgroundColor;
2107 - (NSString*)colorSpaceName
2109 return NSDeviceRGBColorSpace;
2115 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2119 NS_OBJC_END_TRY_ABORT_BLOCK;
2125 @interface NSWindow (MethodSwizzling)
2126 - (void)nsCocoaWindow_NSWindow_sendEvent:(NSEvent *)anEvent;
2129 @implementation NSWindow (MethodSwizzling)
2131 // An NSLeftMouseDown event can change the focus, but it doesn't always do
2132 // this correctly/appropriately. As a result, Gecko keyboard events may be
2133 // sent to the wrong nsChildView object, and thereby "lost". So if we know
2134 // which ChildView object should have the focus in our NSWindow, and if
2135 // processing an NSLeftMouseDown event has caused the focus to be set
2136 // incorrectly, we change the focus back to where it belongs. This resolves
2137 // bmo bugs 314160, 403232, 404433 and 357535/418031.
2139 // Only check for incorrect focus if our NSWindow's "new" first responder is
2140 // also a ChildView object: Embedders (like Camino) sometimes legitimately
2141 // use NSView objects which are not ChildView objects (which don't have
2142 // corresponding child widgets), and expect them to be focusable via an
2143 // NSLeftMouseDown event.
2145 // For non-embedders (e.g. Firefox, Thunderbird and Seamonkey), it would
2146 // probably only be necessary to add a sendEvent: method to the ToolbarWindow
2147 // class. But embedders (like Camino) generally create their own NSWindows.
2148 // So in order to fix this problem everywhere, it's necessary to "hook"
2149 // NSWindow's own sendEvent: method.
2150 - (void)nsCocoaWindow_NSWindow_sendEvent:(NSEvent *)anEvent
2152 // Since we've hooked a "system call" ([NSWindow sendEvent:]), we're always
2153 // called from system code (not browser code). So avoid crashing on any
2154 // Objective-C exceptions that occur here.
2155 NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
2157 [self nsCocoaWindow_NSWindow_sendEvent:anEvent];
2159 if ([anEvent type] == NSLeftMouseDown) {
2160 NSResponder *newFirstResponder = [self firstResponder];
2161 if ([newFirstResponder isKindOfClass:[ChildView class]]) {
2162 WindowDataMap *windowMap = [WindowDataMap sharedWindowDataMap];
2163 TopLevelWindowData *windowData = [windowMap dataForWindow:self];
2165 ChildView *shouldFocusView = [windowData getShouldFocusView];
2166 if (shouldFocusView && (shouldFocusView != newFirstResponder))
2167 [self makeFirstResponder:shouldFocusView];
2172 NS_OBJC_END_TRY_LOGONLY_BLOCK;
2177 @implementation PopupWindow
2179 // The OS treats our custom popup windows very strangely -- many mouse events
2180 // sent to them never reach their target NSView objects. (That these windows
2181 // are borderless and of level NSPopUpMenuWindowLevel may have something to do
2182 // with it.) The best solution is to pre-empt the OS, as follows. (All
2183 // events for a given NSWindow object go through its sendEvent: method.)
2184 - (void)sendEvent:(NSEvent *)anEvent
2186 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2188 NSView *target = nil;
2189 NSView *contentView = nil;
2190 NSEventType type = [anEvent type];
2191 NSPoint windowLocation = NSZeroPoint;
2194 case NSLeftMouseDown:
2196 case NSRightMouseDown:
2197 case NSRightMouseUp:
2198 case NSOtherMouseDown:
2199 case NSOtherMouseUp:
2201 case NSLeftMouseDragged:
2202 case NSRightMouseDragged:
2203 case NSOtherMouseDragged:
2204 if ((contentView = [self contentView])) {
2205 // Since [anEvent window] might not be us, we can't use [anEvent locationInWindow].
2206 windowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, self);
2207 target = [contentView hitTest:[contentView convertPoint:windowLocation fromView:nil]];
2208 // If the hit test failed, the event is targeted here but is not over the window.
2209 // Target it at the first responder.
2211 target = (NSView*)[self firstResponder];
2220 [target scrollWheel:anEvent];
2222 case NSLeftMouseDown:
2223 if ([NSApp isActive]) {
2224 [target mouseDown:anEvent];
2225 } else if (mIsContextMenu) {
2226 [target mouseDown:anEvent];
2227 // If we're in a context menu and our NSApp isn't active (i.e. if
2228 // we're in a context menu raised by a right mouse-down event), we
2229 // don't want the OS to send the coming NSLeftMouseUp event to NSApp
2230 // via the window server, but we do want our ChildView to receive an
2231 // NSLeftMouseUp event (and to send a Gecko NS_MOUSE_BUTTON_UP event
2232 // to the corresponding nsChildView object). If our NSApp isn't
2233 // active when it receives the coming NSLeftMouseUp via the window
2234 // server, our app will (in effect) become partially activated,
2235 // which has strange side effects: For example, if another app's
2236 // window had the focus, that window will lose the focus and the
2237 // other app's main menu will be completely disabled (though it will
2238 // continue to be displayed).
2239 // A side effect of not allowing the coming NSLeftMouseUp event to be
2240 // sent to NSApp via the window server is that our custom context
2241 // menus will roll up whenever the user left-clicks on them, whether
2242 // or not the left-click hit an active menu item. This is how native
2243 // context menus behave, but wasn't how our custom context menus
2244 // behaved previously (on the trunk or e.g. in Firefox 2.0.0.4).
2245 // If our ChildView's corresponding nsChildView object doesn't
2246 // dispatch an NS_MOUSE_BUTTON_UP event, none of our active menu items
2247 // will "work" on an NSLeftMouseUp.
2248 NSEvent *newEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2249 location:windowLocation
2250 modifierFlags:nsCocoaUtils::GetCocoaEventModifierFlags(anEvent)
2251 timestamp:GetCurrentEventTime()
2252 windowNumber:[self windowNumber]
2257 [target mouseUp:newEvent];
2260 // If our NSApp isn't active and we're not a context menu (i.e. if
2261 // we're an ordinary popup window), activate us before sending the
2262 // event to its target. This prevents us from being used in the
2263 // background, and resolves bmo bug 434097 (another app focus
2265 [NSApp activateIgnoringOtherApps:YES];
2266 [target mouseDown:anEvent];
2270 [target mouseUp:anEvent];
2272 case NSRightMouseDown:
2273 [target rightMouseDown:anEvent];
2275 case NSRightMouseUp:
2276 [target rightMouseUp:anEvent];
2278 case NSOtherMouseDown:
2279 [target otherMouseDown:anEvent];
2281 case NSOtherMouseUp:
2282 [target otherMouseUp:anEvent];
2285 [target mouseMoved:anEvent];
2287 case NSLeftMouseDragged:
2288 [target mouseDragged:anEvent];
2290 case NSRightMouseDragged:
2291 [target rightMouseDragged:anEvent];
2293 case NSOtherMouseDragged:
2294 [target otherMouseDragged:anEvent];
2297 [super sendEvent:anEvent];
2301 [super sendEvent:anEvent];
2304 NS_OBJC_END_TRY_ABORT_BLOCK;
2308 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
2309 backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
2311 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2313 mIsContextMenu = false;
2314 return [super initWithContentRect:contentRect styleMask:styleMask
2315 backing:bufferingType defer:deferCreation];
2317 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2321 - (BOOL)isContextMenu
2323 return mIsContextMenu;
2327 - (void)setIsContextMenu:(BOOL)flag
2329 mIsContextMenu = flag;
2335 // According to Apple's docs on [NSWindow canBecomeKeyWindow] and [NSWindow
2336 // canBecomeMainWindow], windows without a title bar or resize bar can't (by
2337 // default) become key or main. But if a window can't become key, it can't
2338 // accept keyboard input (bmo bug 393250). And it should also be possible for
2339 // an otherwise "ordinary" window to become main. We need to override these
2340 // two methods to make this happen.
2341 @implementation BorderlessWindow
2343 - (BOOL)canBecomeKeyWindow
2349 - (void)sendEvent:(NSEvent *)anEvent
2351 NSEventType type = [anEvent type];
2355 case NSLeftMouseDown:
2357 case NSRightMouseDown:
2358 case NSRightMouseUp:
2359 case NSOtherMouseDown:
2360 case NSOtherMouseUp:
2362 case NSLeftMouseDragged:
2363 case NSRightMouseDragged:
2364 case NSOtherMouseDragged:
2366 // Drop all mouse events if a modal window has appeared above us.
2367 // This helps make us behave as if the OS were running a "real" modal
2369 id delegate = [self delegate];
2370 if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
2371 nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
2373 if (gAppModalWindowList && (widget != gAppModalWindowList->window))
2375 if (widget->HasModalDescendents())
2385 [super sendEvent:anEvent];
2389 // Apple's doc on this method says that the NSWindow class's default is not to
2390 // become main if the window isn't "visible" -- so we should replicate that
2391 // behavior here. As best I can tell, the [NSWindow isVisible] method is an
2392 // accurate test of what Apple means by "visibility".
2393 - (BOOL)canBecomeMainWindow
2395 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2397 if (![self isVisible])
2401 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
2404 // Retain and release "self" to avoid crashes when our widget (and its native
2405 // window) is closed as a result of processing a key equivalent (e.g.
2406 // Command+w or Command+q). This workaround is only needed for a window
2407 // that can become key.
2408 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
2410 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2412 NSWindow *nativeWindow = [self retain];
2413 BOOL retval = [super performKeyEquivalent:theEvent];
2414 [nativeWindow release];
2417 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);