Back to index

lightning-sunbird  0.9+nobinonly
nsMacWindow.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Mark Mentovai <mark@moxienet.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsMacWindow.h"
00040 #include "nsMacEventHandler.h"
00041 #include "nsToolkit.h"
00042 
00043 #include "nsIServiceManager.h"    // for drag and drop
00044 #include "nsWidgetsCID.h"
00045 #include "nsIDragHelperService.h"
00046 #include "nsIScreen.h"
00047 #include "nsIScreenManager.h"
00048 #include "nsGUIEvent.h"
00049 #include "nsCarbonHelpers.h"
00050 #include "nsGfxUtils.h"
00051 #include "nsMacResources.h"
00052 #include "nsIRollupListener.h"
00053 #include "nsCRT.h"
00054 #include "nsWidgetSupport.h"
00055 
00056 #include <CoreFoundation/CoreFoundation.h>
00057 #include <Carbon/Carbon.h>
00058 
00059 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_3
00060 #define kWindowFadeTransitionEffect 4
00061 #define kEventWindowTransitionCompleted 89
00062 
00063 typedef struct TransitionWindowOptions {
00064   UInt32    version;
00065   EventTime duration;
00066   WindowRef window;
00067   void*     userData;
00068 } TransitionWindowOptions;
00069 
00070 #define kEventParamWindowPartCode 'wpar'
00071 #define typeWindowPartCode        'wpar'
00072 #endif
00073 
00074 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
00075 // http://developer.apple.com/qa/qa2005/qa1453.html
00076 // These are not defined in early versions of the 10.4/10.4u SDK (as of Xcode
00077 // 2.2), but they may appear in later releases.  Since we can't check which
00078 // version of the SDK is in use beyond knowing that it's "10.4", define these
00079 // on 10.4.  #defines should override what's present in the SDK when defined,
00080 // because the system headers use enums.
00081 #define kEventParamMouseWheelSmoothVerticalDelta   'saxy'
00082 #define kEventParamMouseWheelSmoothHorizontalDelta 'saxx'
00083 #define kEventMouseScroll 11
00084 #endif
00085 
00086 typedef OSStatus (*TransitionWindowWithOptions_type) (WindowRef,
00087                                              WindowTransitionEffect,
00088                                              WindowTransitionAction,
00089                                              const HIRect*,
00090                                              Boolean,
00091                                              TransitionWindowOptions*);
00092 
00093 static const char sScreenManagerContractID[] = "@mozilla.org/gfx/screenmanager;1";
00094 
00095 // from MacHeaders.c
00096 #ifndef topLeft
00097   #define topLeft(r)  (((Point *) &(r))[0])
00098 #endif
00099 #ifndef botRight
00100   #define botRight(r) (((Point *) &(r))[1])
00101 #endif
00102 
00103 // externs defined in nsWindow.cpp
00104 extern nsIRollupListener * gRollupListener;
00105 extern nsIWidget         * gRollupWidget;
00106 
00107 
00108 #define kWindowPositionSlop 20
00109 
00110 // These are only initial guesses. Real values are filled in
00111 // after window creation.
00112 const short kWindowTitleBarHeight = 22;
00113 const short kWindowMarginWidth = 0;
00114 const short kDialogTitleBarHeight = 22;
00115 const short kDialogMarginWidth = 0;
00116 
00117 #if 0
00118 
00119 // routines for debugging port state issues (which are legion)
00120 static void PrintRgn(const char* inLabel, RgnHandle inRgn)
00121 {
00122   Rect regionBounds;
00123   GetRegionBounds(inRgn, &regionBounds);
00124   printf("%s left %d, top %d, right %d, bottom %d\n", inLabel,
00125     regionBounds.left, regionBounds.top, regionBounds.right, regionBounds.bottom);
00126 }
00127 
00128 static void PrintPortState(WindowPtr inWindow, const char* label)
00129 {
00130   CGrafPtr currentPort = CGrafPtr(GetQDGlobalsThePort());
00131   Rect bounds;
00132   GetPortBounds(currentPort, &bounds);
00133   printf("%s: Current port: %p, top, left = %d, %d\n", label, currentPort, bounds.top, bounds.left);
00134 
00135   StRegionFromPool savedClip;
00136   ::GetClip(savedClip);
00137   PrintRgn("  clip:", savedClip);
00138 
00139   StRegionFromPool updateRgn;
00140   ::GetWindowUpdateRegion(inWindow, updateRgn);
00141   Rect windowBounds;
00142   ::GetWindowBounds(inWindow, kWindowContentRgn, &windowBounds);
00143   ::OffsetRgn(updateRgn, -windowBounds.left, -windowBounds.top);
00144 
00145   PrintRgn("  update:", updateRgn);
00146 }
00147 
00148 #endif
00149 
00150 #pragma mark -
00151 
00152 pascal OSErr
00153 nsMacWindow::DragTrackingHandler ( DragTrackingMessage theMessage, WindowPtr theWindow, 
00154                     void *handlerRefCon, DragReference theDrag)
00155 {
00156   static nsCOMPtr<nsIDragHelperService> sDragHelper;
00157 
00158   nsCOMPtr<nsIEventSink> windowEventSink;
00159   nsToolkit::GetWindowEventSink(theWindow, getter_AddRefs(windowEventSink));
00160   if ( !theWindow || !windowEventSink )
00161     return dragNotAcceptedErr;
00162     
00163   switch ( theMessage ) {
00164   
00165     case kDragTrackingEnterHandler:
00166       break;
00167       
00168     case kDragTrackingEnterWindow:
00169     {
00170       sDragHelper = do_GetService ( "@mozilla.org/widget/draghelperservice;1" );
00171       NS_ASSERTION ( sDragHelper, "Couldn't get a drag service, we're in biiig trouble" );
00172       if ( sDragHelper )
00173         sDragHelper->Enter ( theDrag, windowEventSink );
00174       break;
00175     }
00176     
00177     case kDragTrackingInWindow:
00178     {
00179       if ( sDragHelper ) {
00180         PRBool dropAllowed = PR_FALSE;
00181         sDragHelper->Tracking ( theDrag, windowEventSink, &dropAllowed );
00182       }
00183       break;
00184     }
00185     
00186     case kDragTrackingLeaveWindow:
00187     {
00188       if ( sDragHelper ) {
00189         sDragHelper->Leave ( theDrag, windowEventSink );
00190         sDragHelper = nsnull;      
00191       }
00192       break;
00193     }
00194     
00195   } // case of each drag message
00196 
00197   return noErr;
00198   
00199 } // DragTrackingHandler
00200 
00201 
00202 pascal OSErr
00203 nsMacWindow::DragReceiveHandler (WindowPtr theWindow, void *handlerRefCon,
00204                   DragReference theDragRef)
00205 {
00206   nsCOMPtr<nsIEventSink> windowEventSink;
00207   nsToolkit::GetWindowEventSink(theWindow, getter_AddRefs(windowEventSink));
00208   if ( !theWindow || !windowEventSink )
00209     return dragNotAcceptedErr;
00210   
00211   // tell Gecko about the drop. It will tell us if one of the handlers
00212   // accepted it. If not, we need to tell the drag manager.
00213   OSErr result = noErr;
00214   nsCOMPtr<nsIDragHelperService> helper ( do_GetService("@mozilla.org/widget/draghelperservice;1") );
00215   if ( helper ) {
00216     PRBool dragAccepted = PR_FALSE;
00217     helper->Drop ( theDragRef, windowEventSink, &dragAccepted );
00218     if ( !dragAccepted )
00219       result = dragNotAcceptedErr;
00220   }
00221     
00222   return result;
00223   
00224 } // DragReceiveHandler
00225 
00226 
00227 NS_IMPL_ISUPPORTS_INHERITED5(nsMacWindow, Inherited, nsIEventSink, nsPIWidgetMac, nsPIWidgetMac_MOZILLA_1_8_BRANCH, nsPIEventSinkStandalone, 
00228                                           nsIMacTextInputEventSink)
00229 
00230 
00231 //-------------------------------------------------------------------------
00232 //
00233 // nsMacWindow constructor
00234 //
00235 //-------------------------------------------------------------------------
00236 nsMacWindow::nsMacWindow() : Inherited()
00237   , mWindowMadeHere(PR_FALSE)
00238   , mIsSheet(PR_FALSE)
00239   , mAcceptsActivation(PR_TRUE)
00240   , mIsActive(PR_FALSE)
00241   , mZoomOnShow(PR_FALSE)
00242   , mZooming(PR_FALSE)
00243   , mResizeIsFromUs(PR_FALSE)
00244   , mShown(PR_FALSE)
00245   , mSheetNeedsShow(PR_FALSE)
00246   , mSheetShown(PR_FALSE)
00247   , mInPixelMouseScroll(PR_FALSE)
00248   , mMacEventHandler(nsnull)
00249 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3
00250   , mNeedsResize(PR_FALSE)
00251 #endif
00252   , mScrollEventHandler(NULL)
00253   , mWindowEventHandler(NULL)
00254 {
00255   WIDGET_SET_CLASSNAME("nsMacWindow");  
00256 
00257   // create handlers for drag&drop
00258   mDragTrackingHandlerUPP = NewDragTrackingHandlerUPP(DragTrackingHandler);
00259   mDragReceiveHandlerUPP = NewDragReceiveHandlerUPP(DragReceiveHandler);
00260   mBoundsOffset.v = kWindowTitleBarHeight; // initial guesses
00261   mBoundsOffset.h = kWindowMarginWidth;
00262 }
00263 
00264 
00265 //-------------------------------------------------------------------------
00266 //
00267 // nsMacWindow destructor
00268 //
00269 //-------------------------------------------------------------------------
00270 nsMacWindow::~nsMacWindow()
00271 {
00272   if ( mWindowPtr && mWindowMadeHere ) {
00273     // clean up DragManager stuff
00274     if ( mDragTrackingHandlerUPP ) {
00275       ::RemoveTrackingHandler ( mDragTrackingHandlerUPP, mWindowPtr );
00276       ::DisposeDragTrackingHandlerUPP ( mDragTrackingHandlerUPP );
00277      }
00278     if ( mDragReceiveHandlerUPP ) {
00279       ::RemoveReceiveHandler ( mDragReceiveHandlerUPP, mWindowPtr );
00280       ::DisposeDragReceiveHandlerUPP ( mDragReceiveHandlerUPP );
00281     }
00282 
00283     // ensure we leave the port in a happy state
00284     CGrafPtr    curPort;
00285     CGrafPtr    windowPort = ::GetWindowPort(mWindowPtr);
00286     ::GetPort((GrafPtr*)&curPort);
00287     PRBool      mustResetPort = (curPort == windowPort);
00288 
00289     if (mScrollEventHandler)
00290       ::RemoveEventHandler(mScrollEventHandler);
00291     if (mWindowEventHandler)
00292       ::RemoveEventHandler(mWindowEventHandler);
00293 
00294     // Clear references in nsMacEventHandler to this window (bug 388181) and
00295     // null out mWindowPtr to prevent handling events beyond this point.
00296     if (mMacEventHandler.get())
00297       mMacEventHandler->ClearWindowRefs(this);
00298     WindowPtr w = mWindowPtr;
00299     mWindowPtr = nsnull;
00300     ::DisposeWindow(w);
00301     
00302     if (mustResetPort)
00303       nsGraphicsUtils::SetPortToKnownGoodPort();
00304   }
00305   else if ( mWindowPtr && !mWindowMadeHere ) {
00306     (void)::RemoveWindowProperty(mWindowPtr, kTopLevelWidgetPropertyCreator,
00307         kTopLevelWidgetRefPropertyTag);
00308   }
00309 
00310   // Explicitly reset mMacEventHandler to null so that any use after free of
00311   // this object crashes on a null-ptr deref rather than using freed memory.
00312   // ~auto_ptr() only deletes the pointer, it does not set it to null.
00313   mMacEventHandler.reset(nsnull);
00314 }
00315 
00316 //-------------------------------------------------------------------------
00317 //
00318 // Utility method for implementing both Create(nsIWidget ...) and
00319 // Create(nsNativeWidget...)
00320 //-------------------------------------------------------------------------
00321 
00322 nsresult nsMacWindow::StandardCreate(nsIWidget *aParent,
00323                         const nsRect &aRect,
00324                         EVENT_CALLBACK aHandleEventFunction,
00325                         nsIDeviceContext *aContext,
00326                         nsIAppShell *aAppShell,
00327                         nsIToolkit *aToolkit,
00328                         nsWidgetInitData *aInitData,
00329                         nsNativeWidget aNativeParent)
00330 {
00331   short bottomPinDelta = 0;     // # of pixels to subtract to pin window bottom
00332   nsCOMPtr<nsIToolkit> theToolkit = aToolkit;
00333 
00334   NS_ASSERTION(!aInitData || aInitData->mWindowType != eWindowType_popup ||
00335                !aParent, "Popups should not be hooked into nsIWidget hierarchy");
00336 
00337   nsMacEventDispatchHandler* eventDispatchHandler = nsnull;
00338 
00339   if (aInitData && aInitData->mWindowType == eWindowType_popup &&
00340       aNativeParent) {
00341     // For popup windows, use the same event dispatch handler as the
00342     // associated top-level window.  This ensures that enter/exit events
00343     // will be generated as the mouse moves between the top-level window
00344     // and the popup when both are "active."
00345 
00346     // When creating a popup window, aNativeParent is not a WindowPtr but
00347     // an nsIWidget, so walk up that tree to the top-level window and get
00348     // the dispatch handler.
00349     nsCOMPtr<nsIWidget> widget = NS_STATIC_CAST(nsIWidget*, aNativeParent);
00350     nsIWidget* topWidget = nsnull;
00351 
00352     while (widget && (widget = dont_AddRef(widget->GetParent())))
00353       topWidget = widget;
00354 
00355     if (topWidget) {
00356       nsCOMPtr<nsPIWidgetMac_MOZILLA_1_8_BRANCH> parentMacWindow =
00357        do_QueryInterface(topWidget);
00358 
00359       if (parentMacWindow)
00360         parentMacWindow->GetEventDispatchHandler(&eventDispatchHandler);
00361     }
00362   }
00363 
00364   mMacEventHandler.reset(new nsMacEventHandler(this, eventDispatchHandler));
00365 
00366   // build the main native window
00367   if (!aNativeParent || (aInitData && aInitData->mWindowType == eWindowType_popup))
00368   {
00369     PRBool allOrDefault;
00370 
00371     if (aInitData) {
00372       allOrDefault = aInitData->mBorderStyle == eBorderStyle_all ||
00373                      aInitData->mBorderStyle == eBorderStyle_default;
00374       mWindowType = aInitData->mWindowType;
00375       // if a toplevel window was requested without a titlebar, use a dialog windowproc
00376       if (aInitData->mWindowType == eWindowType_toplevel &&
00377           (aInitData->mBorderStyle == eBorderStyle_none ||
00378            !allOrDefault &&
00379            !(aInitData->mBorderStyle & eBorderStyle_title)))
00380         mWindowType = eWindowType_dialog;
00381     }
00382     else
00383     {
00384       allOrDefault = PR_TRUE;
00385       mWindowType = eWindowType_toplevel;
00386     }
00387 
00388     static const WindowAttributes kWindowResizableAttributes =
00389       kWindowResizableAttribute | kWindowLiveResizeAttribute;
00390 
00391     WindowClass windowClass;
00392     WindowAttributes attributes = kWindowNoAttributes;
00393     short hOffset = 0, vOffset = 0;
00394 
00395     switch (mWindowType)
00396     {
00397       case eWindowType_popup:
00398         // We're a popup, context menu, etc. Sets
00399         // mAcceptsActivation to false so we don't activate the window
00400         // when we show it.
00401         mOffsetParent = aParent;
00402         if( aParent )
00403           theToolkit = getter_AddRefs(aParent->GetToolkit());
00404 
00405         mAcceptsActivation = PR_FALSE;
00406 
00407         // XXX kSimpleWindowClass is only defined in the 10.3 (or
00408         //     higher) SDK but MacWindows.h claims it should work on 10.1
00409         //     and higher.
00410         windowClass = 18; // kSimpleWindowClass
00411         break;
00412 
00413       case eWindowType_child:
00414         windowClass = kPlainWindowClass;
00415         break;
00416 
00417       case eWindowType_dialog:
00418         mIsTopWidgetWindow = PR_TRUE;
00419         if (aInitData)
00420         {
00421           switch (aInitData->mBorderStyle)
00422           {
00423             case eBorderStyle_none:
00424               windowClass = kModalWindowClass;
00425               break;
00426 
00427             case eBorderStyle_default:
00428               windowClass = kDocumentWindowClass;
00429               break;
00430 
00431             case eBorderStyle_all:
00432               windowClass = kDocumentWindowClass;
00433               attributes = kWindowCollapseBoxAttribute |
00434                            kWindowResizableAttributes |
00435                            kWindowCloseBoxAttribute;
00436               break;
00437 
00438             default:
00439                 windowClass = kDocumentWindowClass;
00440 
00441                 switch(aInitData->mBorderStyle & (eBorderStyle_resizeh | eBorderStyle_title))
00442                 {
00443                   // combinations of individual options.
00444                   case eBorderStyle_title:
00445                     attributes = kWindowCollapseBoxAttribute;
00446                     break;
00447 
00448                   case eBorderStyle_resizeh:
00449                   case (eBorderStyle_title | eBorderStyle_resizeh):
00450                     attributes =
00451                       kWindowCollapseBoxAttribute | kWindowResizableAttributes;
00452                     break;
00453 
00454                   case eBorderStyle_none: // 0
00455                     // kSimpleWindowClass (18) is only defined in SDK >=
00456                     // 10.3, but it works with runtime >= 10.1.  It's
00457                     // borderless and chromeless, which is more visually
00458                     // appropriate in Aqua than kPlainWindowClass, which
00459                     // gives a 1px black border.
00460                     windowClass = 18; // kSimpleWindowClass
00461                     break;
00462 
00463                   default: // not reached
00464                     NS_WARNING("Unhandled combination of window flags");
00465                     break;
00466                 }
00467 
00468                 // if we set any attributes above then check the close flag and set it here
00469                 if (attributes != kWindowNoAttributes &&
00470                     aInitData->mBorderStyle & eBorderStyle_close)
00471                   attributes |= kWindowCloseBoxAttribute;
00472           }
00473         }
00474         else
00475         {
00476           windowClass = kMovableModalWindowClass;
00477           attributes = kWindowCollapseBoxAttribute;
00478         }
00479 
00480         hOffset = kDialogMarginWidth;
00481         vOffset = kDialogTitleBarHeight;
00482         break;
00483 
00484       case eWindowType_sheet:
00485         mIsTopWidgetWindow = PR_TRUE;
00486         if (aInitData)
00487         {
00488           nsWindowType parentType;
00489           aParent->GetWindowType(parentType);
00490           if (parentType != eWindowType_invisible)
00491           {
00492             // Mac OS X sheet support
00493             mIsSheet = PR_TRUE;
00494             windowClass = kSheetWindowClass;
00495             if (aInitData->mBorderStyle & eBorderStyle_resizeh)
00496             {
00497               attributes = kWindowResizableAttributes;
00498             }
00499           }
00500           else
00501           {
00502             windowClass = kDocumentWindowClass;
00503             attributes = kWindowCollapseBoxAttribute;
00504           }
00505         }
00506         else
00507         {
00508           windowClass = kMovableModalWindowClass;
00509           attributes = kWindowCollapseBoxAttribute;
00510         }
00511 
00512         hOffset = kDialogMarginWidth;
00513         vOffset = kDialogTitleBarHeight;
00514         break;
00515 
00516       case eWindowType_toplevel:
00517         mIsTopWidgetWindow = PR_TRUE;
00518         windowClass = kDocumentWindowClass;
00519         attributes =
00520           kWindowCollapseBoxAttribute | kWindowToolbarButtonAttribute;
00521 
00522         if (allOrDefault || aInitData->mBorderStyle & eBorderStyle_close)
00523           attributes |= kWindowCloseBoxAttribute;
00524 
00525         if (allOrDefault || aInitData->mBorderStyle & eBorderStyle_resizeh)
00526           attributes |= kWindowFullZoomAttribute | kWindowResizableAttributes;
00527 
00528         hOffset = kWindowMarginWidth;
00529         vOffset = kWindowTitleBarHeight;
00530         break;
00531 
00532       case eWindowType_invisible:
00533         // XXX Hack to make the hidden window not show up as a normal window in
00534         //     OS X (hide it from Expose, don't expose it through
00535         //     option-minimize, ...).
00536         windowClass = kPlainWindowClass;
00537 
00538         // XXX kWindowDoesNotCycleAttribute is only defined in the 10.3 (or
00539         //     higher) SDK but MacWindows.h claims it should work on 10.2
00540         //     and higher.
00541         if (nsToolkit::OSXVersion() >= MAC_OS_X_VERSION_10_2_HEX)
00542         {
00543           attributes = (1L << 15); // kWindowDoesNotCycleAttribute
00544         }
00545         break;
00546 
00547       default:
00548         NS_ERROR("Unhandled window type!");
00549 
00550         return NS_ERROR_FAILURE;
00551     }
00552 
00553     Rect wRect;
00554     nsRectToMacRect(aRect, wRect);
00555 
00556     if (eWindowType_popup != mWindowType)
00557       ::OffsetRect(&wRect, hOffset, vOffset + ::GetMBarHeight());
00558     else
00559       ::OffsetRect(&wRect, hOffset, vOffset);
00560 
00561     // If aRect is empty (zero width and/or zero height, as often happens at
00562     // least with eWindowType_popup windows), wRect.bottom can end up set here
00563     // to a a wildly inappropriate value.  This in turn can can cause
00564     // bottomPinDelta to to be set incorrectly, which leads to the bounds
00565     // being set incorrectly by the call to Inherited::StandardCreate() below.
00566     if (!aRect.IsEmpty()) {
00567       nsCOMPtr<nsIScreenManager> screenmgr = do_GetService(sScreenManagerContractID);
00568       if (screenmgr) {
00569         nsCOMPtr<nsIScreen> screen;
00570         //screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
00571         screenmgr->ScreenForRect(wRect.left, wRect.top,
00572                                    wRect.right - wRect.left, wRect.bottom - wRect.top,
00573                                    getter_AddRefs(screen));
00574         if (screen) {
00575           PRInt32 left, top, width, height;
00576           screen->GetAvailRect(&left, &top, &width, &height);
00577           if (wRect.bottom > top+height) {
00578             bottomPinDelta = wRect.bottom - (top+height);
00579             wRect.bottom -= bottomPinDelta;
00580           }
00581         }
00582       }
00583     }
00584 
00585     ::CreateNewWindow(windowClass, attributes, &wRect, &mWindowPtr);
00586 
00587     mWindowMadeHere = PR_TRUE;
00588 
00589     // If aRect is empty (zero width and/or zero height, as often happens at
00590     // least with eWindowType_popup windows), we've just created an empty
00591     // window.  This isn't a problem in itself.  But calling GetWindowBounds()
00592     // on an empty window can (and usually does) return a rectangle which,
00593     // though empty, has arbitrarily different dimensions than the rectangle
00594     // that was used to create the window (i.e. wRect).  If the upper-left
00595     // corners of these rectangles are different, this will cause mBoundsOffset
00596     // to be set incorrectly -- which is likely to cause trouble elsewhere.
00597     // It's possible that this never happened before OS X Leopard (10.5) was
00598     // released.  But it _can_ happen on Leopard, where it caused bmo bug
00599     // 400082 (and probably also other bugs).
00600     //
00601     // Bmo bug 400082 happened (on Leopard) as follows:  For some kinds of
00602     // (empty) popup windows (those that don't yet have any content?, that
00603     // aren't yet visible?), often (always?) structure.top != wRect.top.
00604     // This caused mBoundsOffset.v to be set incorrectly here, which caused
00605     // nsMacWindow::Resize() to always reset its height to 0, which in turn
00606     // made nsMacWindow::Show() always fail to display the window (because
00607     // the window's mBounds was always empty).
00608     if (aRect.IsEmpty()) {
00609       // An empty window's mBoundsOffset was (I think) always zeroed by the
00610       // pre-patch code below on Tiger.  So it makes sense to continue doing
00611       // this.
00612       mBoundsOffset.v = mBoundsOffset.h = 0;
00613     } else {
00614       // Getting kWindowContentRgn can give back bad values on Panther
00615       // (fixed on Tiger), but wRect is already set to the content rect anyway.
00616       Rect structure;
00617       ::GetWindowBounds(mWindowPtr, kWindowStructureRgn, &structure);
00618       mBoundsOffset.v = wRect.top - structure.top;
00619       mBoundsOffset.h = wRect.left - structure.left;
00620     }
00621   }
00622   else
00623   {
00624     mWindowPtr = (WindowPtr)aNativeParent;
00625     mWindowMadeHere = PR_FALSE;
00626     mVisible = PR_TRUE;
00627   }
00628 
00629   if (mWindowPtr == nsnull)
00630     return NS_ERROR_OUT_OF_MEMORY;
00631 
00632   // In order to get back to this nsIWidget from a WindowPtr, we hang
00633   // ourselves off a property of the window. This allows places like
00634   // event handlers to get our widget or event sink when all they have
00635   // is a native WindowPtr.
00636   nsIWidget* temp = NS_STATIC_CAST(nsIWidget*, this);
00637   OSStatus err = ::SetWindowProperty ( mWindowPtr,
00638                           kTopLevelWidgetPropertyCreator, kTopLevelWidgetRefPropertyTag,
00639                           sizeof(nsIWidget*), &temp );
00640   NS_ASSERTION ( err == noErr, "couldn't set a property on the window, event handling will fail" );
00641   if ( err != noErr )
00642     return NS_ERROR_FAILURE;
00643 
00644   // reset the coordinates to (0,0) because it's the top level widget
00645   // and adjust for any adjustment required to requested window bottom
00646   nsRect bounds(0, 0, aRect.width, aRect.height - bottomPinDelta);
00647 
00648   // We only need a valid aParent if we have a sheet
00649   if (!aInitData || aInitData->mWindowType != eWindowType_sheet)
00650     aParent = nil;
00651 
00652   // init base class
00653   // (note: aParent is ignored. Mac (real) windows don't want parents)
00654   Inherited::StandardCreate(aParent, bounds, aHandleEventFunction, aContext, aAppShell, theToolkit, aInitData);
00655 
00656   // there is a lot of work that we only want to do if we actually created
00657   // the window. Embedding apps would be really torqed off if we mucked around
00658   // with the window they gave us. Do things like tweak chrome, register
00659   // event handlers, and install root controls and hacked scrollbars.
00660   if ( mWindowMadeHere ) {
00661     if ( mWindowType == eWindowType_popup ) {
00662       // Here, we put popups in the same layer as native tooltips so that they float above every
00663       // type of window including modal dialogs. We also ensure that popups do not receive activate
00664       // events and do not steal focus from other windows.
00665       ::SetWindowGroup(mWindowPtr, ::GetWindowGroupOfClass(kHelpWindowClass));
00666       ::SetWindowActivationScope(mWindowPtr, kWindowActivationScopeNone);
00667     }
00668 
00669     if ( mWindowType != eWindowType_invisible &&
00670          mWindowType != eWindowType_plugin &&
00671          mWindowType != eWindowType_java) {
00672       const EventTypeSpec kScrollEventList[] = {
00673         { kEventClassMouse, kEventMouseWheelMoved },
00674 #if 0
00675         // Disabled due to bug 347626
00676         { kEventClassMouse, kEventMouseScroll },
00677 #endif
00678       };
00679 
00680       static EventHandlerUPP sScrollEventHandlerUPP;
00681       if (!sScrollEventHandlerUPP)
00682         sScrollEventHandlerUPP = ::NewEventHandlerUPP(ScrollEventHandler);
00683 
00684       err = ::InstallWindowEventHandler(mWindowPtr,
00685                                         sScrollEventHandlerUPP,
00686                                         GetEventTypeCount(kScrollEventList),
00687                                         kScrollEventList,
00688                                         (void*)this,
00689                                         &mScrollEventHandler);
00690       NS_ASSERTION(err == noErr, "Couldn't install scroll event handler");
00691     }
00692 
00693     // Window event handler
00694     const EventTypeSpec kWindowEventList[] = {
00695       // to enable live resizing
00696       { kEventClassWindow, kEventWindowBoundsChanged },
00697       // to roll up popups when we're minimized
00698       { kEventClassWindow, kEventWindowCollapsing },
00699       // to activate when restoring
00700       { kEventClassWindow, kEventWindowExpanded },
00701       // to keep invisible windows off the screen
00702       { kEventClassWindow, kEventWindowConstrain },
00703       // to handle update events
00704       { kEventClassWindow, kEventWindowUpdate },
00705       // to handle activation
00706       { kEventClassWindow, kEventWindowActivated },
00707       { kEventClassWindow, kEventWindowDeactivated },
00708       // to clean up after a transition is complete
00709       { kEventClassWindow, kEventWindowTransitionCompleted },
00710     };
00711 
00712     static EventHandlerUPP sWindowEventHandlerUPP;
00713     if (!sWindowEventHandlerUPP)
00714       sWindowEventHandlerUPP = ::NewEventHandlerUPP(WindowEventHandler);
00715 
00716     err = ::InstallWindowEventHandler(mWindowPtr,
00717                                       sWindowEventHandlerUPP,
00718                                       GetEventTypeCount(kWindowEventList),
00719                                       kWindowEventList,
00720                                       (void*)this,
00721                                       &mWindowEventHandler);
00722     NS_ASSERTION(err == noErr, "Couldn't install window event handler");
00723 
00724     // Key event handler
00725     const EventTypeSpec kKeyEventList[] = {
00726       { kEventClassKeyboard, kEventRawKeyDown },
00727       { kEventClassKeyboard, kEventRawKeyUp },
00728       { kEventClassKeyboard, kEventRawKeyModifiersChanged },
00729     };
00730 
00731     static EventHandlerUPP sKeyEventHandlerUPP;
00732     if (!sKeyEventHandlerUPP)
00733       sKeyEventHandlerUPP = ::NewEventHandlerUPP(KeyEventHandler);
00734 
00735     err = ::InstallWindowEventHandler(mWindowPtr,
00736                                       sKeyEventHandlerUPP,
00737                                       GetEventTypeCount(kKeyEventList),
00738                                       kKeyEventList,
00739                                       NS_STATIC_CAST(void*, this),
00740                                       NULL);
00741     NS_ASSERTION(err == noErr, "Couldn't install key event handler");
00742 
00743     // register tracking and receive handlers with the native Drag Manager
00744     if ( mDragTrackingHandlerUPP ) {
00745       err = ::InstallTrackingHandler ( mDragTrackingHandlerUPP, mWindowPtr, nsnull );
00746       NS_ASSERTION ( err == noErr, "can't install drag tracking handler");
00747     }
00748     if ( mDragReceiveHandlerUPP ) {
00749       err = ::InstallReceiveHandler ( mDragReceiveHandlerUPP, mWindowPtr, nsnull );
00750       NS_ASSERTION ( err == noErr, "can't install drag receive handler");
00751     }
00752 
00753     if (mWindowType == eWindowType_popup) {
00754       // Popup windows are used for contextual menus, pop-up/drop-down menus,
00755       // autocomplete dropdowns, and tooltips, all of which have a very slight
00756       // transparency when native.  Use 5%, which is almost imperceptible
00757       // and matches what Tiger does.
00758       const float kPopupWindowAlpha = 0.95;
00759       ::SetWindowAlpha(mWindowPtr, kPopupWindowAlpha);
00760     }
00761 
00762   } // if we created windowPtr
00763 
00764   nsGraphicsUtils::SafeSetPortWindowPort(mWindowPtr);
00765 
00766   return NS_OK;
00767 }
00768 
00769 
00770 
00771 pascal OSStatus
00772 nsMacWindow::ScrollEventHandler(EventHandlerCallRef aHandlerCallRef,
00773                                 EventRef            aEvent,
00774                                 void*               aUserData)
00775 {
00776   OSStatus retVal = eventNotHandledErr;
00777 
00778   Point mouseLoc;
00779   UInt32 modifiers = 0;
00780   if (::GetEventParameter(aEvent, kEventParamMouseLocation,
00781                           typeQDPoint, NULL,
00782                           sizeof(Point), NULL, &mouseLoc) != noErr ||
00783       ::GetEventParameter(aEvent, kEventParamKeyModifiers,
00784                           typeUInt32, NULL,
00785                           sizeof(UInt32), NULL, &modifiers) != noErr) {
00786     // Need both of these params regardless of event kind.
00787     return retVal;
00788   }
00789 
00790   SInt32 deltaY = 0, deltaX = 0;
00791   PRBool isPixels = PR_FALSE;
00792 
00793   nsMacWindow* self = NS_REINTERPRET_CAST(nsMacWindow*, aUserData);
00794 
00795   EventKind kind = ::GetEventKind(aEvent);
00796 
00797   switch (kind) {
00798     case kEventMouseWheelMoved: {
00799       // Notchy scrolling hardware, like conventional wheel mice, scroll
00800       // a line at a time.
00801 
00802       if (self->mInPixelMouseScroll) {
00803         // A smooth scroll was already done by pixels.  Don't scroll again by
00804         // lines.
00805         return noErr; 
00806       }
00807 
00808       EventMouseWheelAxis axis = kEventMouseWheelAxisY;
00809       SInt32 delta = 0;
00810       if (::GetEventParameter(aEvent, kEventParamMouseWheelAxis,
00811                               typeMouseWheelAxis, NULL,
00812                               sizeof(EventMouseWheelAxis), NULL,
00813                               &axis) != noErr ||
00814           ::GetEventParameter(aEvent, kEventParamMouseWheelDelta,
00815                               typeLongInteger, NULL,
00816                               sizeof(SInt32), NULL, &delta) != noErr) {
00817         // Need both of these params.
00818         return retVal;
00819       }
00820 
00821       if (axis == kEventMouseWheelAxisY)
00822         deltaY = delta;
00823       else
00824         deltaX = delta;
00825 
00826       break;
00827     }
00828 
00829     case kEventMouseScroll: {
00830       // On Tiger or later, smooth scrolling hardware, like Apple touchpads
00831       // and Mighty Mice, scroll a pixel at a time.  Handling this event means
00832       // that the system won't send a kEventMouseWheelMoved event.
00833       isPixels = PR_TRUE;
00834       OSErr errY, errX;
00835       errY = ::GetEventParameter(aEvent,
00836                                  kEventParamMouseWheelSmoothVerticalDelta,
00837                                  typeSInt32, NULL,
00838                                  sizeof(SInt32), NULL, &deltaY);
00839       errX = ::GetEventParameter(aEvent,
00840                                  kEventParamMouseWheelSmoothHorizontalDelta,
00841                                  typeSInt32, NULL,
00842                                  sizeof(SInt32), NULL, &deltaX);
00843       if (errY != noErr && errX != noErr) {
00844         // Need at least one of these params.
00845         return retVal;
00846       }
00847       if ((errY != noErr && errY != eventParameterNotFoundErr) ||
00848           (errX != noErr && errX != eventParameterNotFoundErr)) {
00849         // eventParameterNotFoundErr is the only permissible "error",
00850         // in that case, leave the delta set to 0.
00851         return retVal;
00852       }
00853       break;
00854     }
00855 
00856     default: {
00857       // What?
00858       return retVal;
00859       break;
00860     }
00861   }
00862 
00863   {
00864     // Convert mouse to local coordinates since that's how the event handler 
00865     // wants them
00866     StPortSetter portSetter(self->mWindowPtr);
00867     StOriginSetter originSetter(self->mWindowPtr);
00868     ::GlobalToLocal(&mouseLoc);
00869     self->mMacEventHandler->Scroll(deltaY, deltaX, isPixels, mouseLoc,
00870                                    self, modifiers);
00871     retVal = noErr;
00872   }
00873 
00874   if (kind == kEventMouseScroll && (deltaX != 0 || deltaY != 0)) {
00875     // Plug-ins might depend on kEventMouseWheelMoved, so if the event was
00876     // kEventMouseScroll, call to the next handler to give the system the
00877     // chance to turn one event into the other.  See
00878     // http://developer.apple.com/qa/qa2005/qa1453.html .  When the
00879     // new event reaches this handler, mInPixelMouseScroll will prevent
00880     // double-scrolling.
00881     PRBool lastInPixelMouseScroll = self->mInPixelMouseScroll;
00882     self->mInPixelMouseScroll = PR_TRUE;
00883     ::CallNextEventHandler(aHandlerCallRef, aEvent);
00884     self->mInPixelMouseScroll = lastInPixelMouseScroll;
00885   }
00886 
00887   return retVal;
00888 } // ScrollEventHandler
00889 
00890 
00891 pascal OSStatus
00892 nsMacWindow::WindowEventHandler ( EventHandlerCallRef inHandlerChain, EventRef inEvent, void* userData )
00893 {
00894   OSStatus retVal = eventNotHandledErr;  // Presume we won't consume the event
00895   nsMacWindow* self = NS_REINTERPRET_CAST(nsMacWindow*, userData);
00896   if (self) {
00897     UInt32 what = ::GetEventKind(inEvent);
00898     switch (what) {
00899     
00900       case kEventWindowBoundsChanged:
00901       {
00902         // are we moving or resizing the window? we only care about resize.
00903         UInt32 attributes = 0;
00904         ::GetEventParameter ( inEvent, kEventParamAttributes, typeUInt32, NULL, sizeof(attributes), NULL, &attributes );
00905         if ( attributes & kWindowBoundsChangeSizeChanged ) {
00906           WindowRef myWind = NULL;
00907           ::GetEventParameter(inEvent, kEventParamDirectObject, typeWindowRef, NULL, sizeof(myWind), NULL, &myWind);
00908           Rect bounds;
00909           ::InvalWindowRect(myWind, ::GetWindowPortBounds(myWind, &bounds));
00910           
00911           // resize the window and repaint
00912           NS_ENSURE_TRUE(self->mMacEventHandler.get(), eventNotHandledErr);
00913           if (!self->mResizeIsFromUs ) {
00914             self->mMacEventHandler->ResizeEvent(myWind);
00915             self->Update();
00916           }
00917           retVal = noErr;  // We did consume the resize event
00918         }
00919         break;
00920       }
00921       
00922       case kEventWindowConstrain:
00923       {
00924         // Ignore this event if we're an invisible window, otherwise pass along the
00925         // chain to ensure it's onscreen.
00926         if ( self->mWindowType != eWindowType_invisible )
00927           retVal = ::CallNextEventHandler(inHandlerChain, inEvent);
00928         else
00929           retVal = noErr;  // consume the event for the hidden window
00930         break;
00931       }      
00932 
00933       case kEventWindowUpdate:
00934       {
00935         self->Update();
00936         retVal = noErr; // consume
00937       }
00938       break;
00939 
00940       // roll up popups when we're minimized
00941       case kEventWindowCollapsing:
00942       {
00943         if ( gRollupListener && gRollupWidget )
00944           gRollupListener->Rollup();        
00945         self->mMacEventHandler->GetEventDispatchHandler()->DispatchGuiEvent(self, NS_DEACTIVATE);
00946         ::CallNextEventHandler(inHandlerChain, inEvent);
00947         retVal = noErr; // do default processing, but consume
00948       }
00949       break;
00950 
00951       // Activate when restoring from the minimized state.  This ensures that
00952       // the restored window will be able to take focus.
00953       case kEventWindowExpanded:
00954       {
00955         self->mMacEventHandler->GetEventDispatchHandler()->DispatchGuiEvent(self, NS_ACTIVATE);
00956         ::CallNextEventHandler(inHandlerChain, inEvent);
00957         retVal = noErr; // do default processing, but consume
00958       }
00959       break;
00960 
00961       case kEventWindowActivated:
00962       {
00963         // This cooperates with the sheet case in Show to avoid activating
00964         // sheets prematurely.  The activate event sent by ShowSheetWindow
00965         // will be ignored, but Show will set mSheetShown and send another
00966         // activate event to ensure that activation occurs.
00967         if (self->mWindowType != eWindowType_sheet ||
00968             self->mSheetShown) {
00969           self->mMacEventHandler->HandleActivateEvent(inEvent);
00970         }
00971         ::CallNextEventHandler(inHandlerChain, inEvent);
00972         retVal = noErr; // do default processing, but consume
00973       }
00974       break;
00975 
00976       case kEventWindowDeactivated:
00977       {
00978         self->mMacEventHandler->HandleActivateEvent(inEvent);
00979         ::CallNextEventHandler(inHandlerChain, inEvent);
00980         retVal = noErr; // do default processing, but consume
00981       }
00982       break;
00983 
00984       case kEventWindowTransitionCompleted: {
00985         self->mDeathGripDuringTransition = nsnull;
00986         retVal = noErr;
00987         break;
00988       }
00989 
00990     } // case of which event?
00991   }
00992   
00993   return retVal;
00994   
00995 } // WindowEventHandler
00996 
00997 
00998 
00999 //-------------------------------------------------------------------------
01000 //
01001 // Create a nsMacWindow using a native window provided by the application
01002 //
01003 //-------------------------------------------------------------------------
01004 NS_IMETHODIMP nsMacWindow::Create(nsNativeWidget aNativeParent,   // this is a windowPtr
01005                       const nsRect &aRect,
01006                       EVENT_CALLBACK aHandleEventFunction,
01007                       nsIDeviceContext *aContext,
01008                       nsIAppShell *aAppShell,
01009                       nsIToolkit *aToolkit,
01010                       nsWidgetInitData *aInitData)
01011 {
01012   return(StandardCreate(nsnull, aRect, aHandleEventFunction,
01013                           aContext, aAppShell, aToolkit, aInitData,
01014                             aNativeParent));
01015 }
01016 
01017 
01018 //-------------------------------------------------------------------------
01019 //
01020 // Hide or show this window
01021 //
01022 //-------------------------------------------------------------------------
01023 NS_IMETHODIMP nsMacWindow::Show(PRBool aState)
01024 {
01025   // Mac OS X sheet support
01026   nsIWidget *parentWidget = mParent;
01027   nsCOMPtr<nsPIWidgetMac_MOZILLA_1_8_BRANCH> piParentWidget ( do_QueryInterface(parentWidget) );
01028   WindowRef parentWindowRef = (parentWidget) ?
01029     reinterpret_cast<WindowRef>(parentWidget->GetNativeData(NS_NATIVE_DISPLAY)) : nsnull;
01030 
01031   Inherited::Show(aState);
01032   
01033   // we need to make sure we call ::Show/HideWindow() to generate the 
01034   // necessary activate/deactivate events. Calling ::ShowHide() is
01035   // not adequate, unless we don't want activation (popups). (pinkerton).
01036   if (aState && !mBounds.IsEmpty()) {
01037     if (mIsSheet) {
01038       if (parentWindowRef) {
01039         WindowPtr top = GetWindowTop(parentWindowRef);
01040         if (piParentWidget) {
01041           PRBool parentIsSheet = PR_FALSE;
01042           if (NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
01043               parentIsSheet) {
01044             // If this sheet is the child of another sheet, hide the parent
01045             // so that this sheet can be displayed.
01046             // Leave the parent's mShown and mSheetNeedsShow alone, those are
01047             // only used to handle sibling sheet contention.  The parent will
01048             // be displayed again when it has no more child sheets.
01049             ::GetSheetWindowParent(parentWindowRef, &top);
01050             ::HideSheetWindow(parentWindowRef);
01051             piParentWidget->SetSheetShown(PR_FALSE);
01052           }
01053         }
01054 
01055         nsMacWindow* sheetShown = nsnull;
01056         if (NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_TRUE,
01057                                                        &sheetShown)) &&
01058             (!sheetShown || sheetShown == this)) {
01059           if (!sheetShown) {
01060             // There is no sheet on the top-level window, show this one.
01061 
01062             // Important to set these member variables first, because
01063             // ShowSheetWindow may result in native event dispatch causing
01064             // reentrancy into this code for this window - if mSheetNeedsShow
01065             // is true, it's possible to show the same sheet twice, and that
01066             // will cause tremendous problems.
01067             mShown = PR_TRUE;
01068             mSheetNeedsShow = PR_FALSE;
01069 
01070             ::ShowSheetWindow(mWindowPtr, top);
01071 
01072             // ShowSheetWindow sends an activate event, which could potentially
01073             // cause the modal loop to run.  In some cases, this will occur
01074             // before the sheet is actually displayed, resulting in a loop
01075             // waiting for input from the user that is impossible to achieve.
01076             // As a workaround, the activate event sent by ShowSheetWindow
01077             // will not be handled by Gecko, and instead another activate
01078             // event will be sent once the sheet is known to be displayed.
01079             mSheetShown = PR_TRUE;
01080             ::ActivateWindow(mWindowPtr, true);
01081           }
01082           UpdateWindowMenubar(parentWindowRef, PR_FALSE);
01083           mMacEventHandler->GetEventDispatchHandler()->DispatchGuiEvent(this, NS_GOTFOCUS);
01084           mMacEventHandler->GetEventDispatchHandler()->DispatchGuiEvent(this, NS_ACTIVATE);
01085           ComeToFront();
01086         }
01087         else {
01088           // A sibling of this sheet is active, don't show this sheet yet.
01089           // When the active sheet hides, its brothers and sisters that have
01090           // mSheetNeedsShow set will have their opportunities to display.
01091           mSheetNeedsShow = PR_TRUE;
01092         }
01093       }
01094     }
01095     else {
01096       if (mAcceptsActivation)
01097         ::ShowWindow(mWindowPtr);
01098       else {
01099         ::ShowHide(mWindowPtr, true);
01100         // competes with ComeToFront, but makes popups work
01101         ::BringToFront(mWindowPtr);
01102       }
01103       ComeToFront();
01104       mShown = PR_TRUE;
01105     }
01106 
01107     if (mZoomOnShow) {
01108       SetSizeMode(nsSizeMode_Maximized);
01109       mZoomOnShow = PR_FALSE;
01110     }
01111 
01112     if (::IsWindowCollapsed(mWindowPtr))
01113       ::CollapseWindow(mWindowPtr, false);
01114   }
01115   else {
01116     // when a toplevel window goes away, make sure we rollup any popups that
01117     // may be lurking. We want to catch this here because we're guaranteed that
01118     // we hide a window before we destroy it, and doing it here more closely
01119     // approximates where we do the same thing on windows.
01120     if ( mWindowType == eWindowType_toplevel ) {
01121       if ( gRollupListener )
01122         gRollupListener->Rollup();
01123       NS_IF_RELEASE(gRollupListener);
01124       NS_IF_RELEASE(gRollupWidget);
01125     }
01126 
01127     // Mac OS X sheet support
01128     if (mIsSheet) {
01129       if (mShown) {
01130         // Set mShown here because a sibling sheet may want to be activated,
01131         // and mShown needs to be false in order for sibling sheets to display.
01132         mShown = PR_FALSE;
01133 
01134         // get sheet's parent *before* hiding the sheet (which breaks the
01135         // linkage)
01136         WindowPtr sheetParent = nsnull;
01137         ::GetSheetWindowParent(mWindowPtr, &sheetParent);
01138 
01139         ::HideSheetWindow(mWindowPtr);
01140         mSheetShown = PR_FALSE;
01141 
01142         mMacEventHandler->GetEventDispatchHandler()->DispatchGuiEvent(this, NS_DEACTIVATE);
01143 
01144         WindowPtr top = GetWindowTop(parentWindowRef);
01145         nsMacWindow* siblingSheetToShow = nsnull;
01146         PRBool parentIsSheet = PR_FALSE;
01147 
01148         if (parentWindowRef && piParentWidget &&
01149             NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_FALSE, 
01150                                                        &siblingSheetToShow)) &&
01151             siblingSheetToShow) {
01152           // First, give sibling sheets an opportunity to show.
01153           siblingSheetToShow->Show(PR_TRUE);
01154         }
01155         else if (parentWindowRef && piParentWidget &&
01156                  NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
01157                  parentIsSheet) {
01158           // If there are no sibling sheets, but the parent is a sheet, restore
01159           // it.  It wasn't sent any deactivate events when it was hidden, so
01160           // don't call through Show, just let the OS put it back up.
01161           ::ShowSheetWindow(parentWindowRef, sheetParent);
01162 
01163           // See the other ShowSheetWindow call above for an explanation of
01164           // why this is done.
01165           piParentWidget->SetSheetShown(PR_TRUE);
01166           ::ActivateWindow(parentWindowRef, true);
01167         }
01168         else {
01169           // Sheet, that was hard.  No more siblings or parents, going back
01170           // to a real window.
01171 
01172           // if we had several sheets open, when the last one goes away
01173           // we need to ensure that the top app window is active
01174           if (mAcceptsActivation)
01175             ::ShowWindow(top);
01176           else {
01177             ::ShowHide(top, true);
01178             // competes with ComeToFront, but makes popups work
01179             ::BringToFront(top);
01180           }
01181         }
01182         ComeToFront();
01183 
01184         if (top == parentWindowRef)
01185           UpdateWindowMenubar(parentWindowRef, PR_TRUE);
01186       }
01187       else if (mSheetNeedsShow) {
01188         // This is an attempt to hide a sheet that never had a chance to
01189         // be shown.  There's nothing to do other than make sure that it
01190         // won't show.
01191         mSheetNeedsShow = PR_FALSE;
01192       }
01193     }
01194     else {
01195       if (mWindowPtr) {
01196 #ifndef MOZ_SUNBIRD
01197 // XXX bug 348146 - Hiding and showing the popup rapidly screws up
01198 //                  Sunbird's datepicker as the hide isn't finished
01199 //                  before we try to show. We need to fix this.
01200 //
01201         static TransitionWindowWithOptions_type transitionFunc;
01202         if (mWindowType == eWindowType_popup) {
01203           // Popups will hide by fading out with TransitionWindowWithOptions,
01204           // if available.  It's present in 10.3 and later.  If that SDK or
01205           // newer is used, just grab the address of the function, otherwise,
01206           // dynamically look it up at runtime.  Fall back to calling
01207           // HideWindow if TransitionWindowWithOptions isn't available.
01208           static PRBool sChecked;
01209           if (!sChecked) {
01210             sChecked = PR_TRUE;
01211 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3
01212             transitionFunc = ::TransitionWindowWithOptions;
01213 #else
01214             CFBundleRef carbonBundle =
01215              ::CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon"));
01216             if (carbonBundle) {
01217               transitionFunc = (TransitionWindowWithOptions_type)
01218                ::CFBundleGetFunctionPointerForName(carbonBundle,
01219                                          CFSTR("TransitionWindowWithOptions"));
01220             }
01221 #endif
01222           }
01223         }
01224         // Bug 345388: hiding windows using the async
01225         // TransitionWindowWithOptions causes memory corruption on 10.3, even
01226         // though the API is supported there.  Only fade these windows out on
01227         // 10.4 or later, which are able to handle it without corruption or
01228         // crashing.
01229         if (mWindowType == eWindowType_popup && transitionFunc
01230 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
01231             && nsToolkit::OSXVersion() >= MAC_OS_X_VERSION_10_4_HEX
01232 #endif
01233            ) {
01234           mDeathGripDuringTransition = this;
01235           TransitionWindowOptions transitionOptions = { version  : 0,
01236                                                         duration : 0.2,
01237                                                         window   : nsnull,
01238                                                         userData : nsnull };
01239           transitionFunc(mWindowPtr, kWindowFadeTransitionEffect,
01240                          kWindowHideTransitionAction, nsnull,
01241                          PR_TRUE, &transitionOptions);
01242         }
01243         else
01244 #endif // ifndef MOZ_SUNBIRD
01245           ::HideWindow(mWindowPtr);
01246       }
01247       mShown = PR_FALSE;
01248     }
01249   }
01250 
01251   return NS_OK;
01252 }
01253 
01254 
01255 //-------------------------------------------------------------------------
01256 //
01257 // GetWindowTop
01258 //
01259 // find the topmost sheet currently assigned to a baseWindowRef or,
01260 // if no sheet is attached to it, just return the baseWindowRef itself
01261 //
01262 //-------------------------------------------------------------------------
01263 WindowPtr
01264 nsMacWindow::GetWindowTop(WindowPtr baseWindowRef)
01265 {
01266     if (!baseWindowRef) return(nsnull);
01267 
01268 /*
01269     Note: it would be nice to use window groups... something like the
01270           following;  however, ::CountWindowGroupContents() and
01271           ::GetIndexedWindow() are available on Mac OS X but NOT in
01272           CarbonLib on pre-Mac OS X systems which we need to support
01273 
01274     WindowGroupRef group = ::WindowGroupRef(baseWindowRef);
01275     if (group)
01276     {
01277         WindowGroupContentOptions options =
01278             kWindowGroupContentsReturnWindows | kWindowGroupContentsVisible;
01279 
01280         ItemCount numWins = ::CountWindowGroupContents(group, options);
01281         if (numWins >= 1)
01282         {
01283             ::GetIndexedWindow(group, 1, options, &baseWindowRef);
01284         }
01285     }
01286 */
01287 
01288     WindowPtr aSheet = ::GetFrontWindowOfClass(kSheetWindowClass, true);
01289     while(aSheet)
01290     {
01291         WindowRef   sheetParent;
01292         GetSheetWindowParent(aSheet, &sheetParent);
01293         if (sheetParent == baseWindowRef)
01294         {
01295             return(aSheet);
01296         }
01297         aSheet = GetNextWindowOfClass(aSheet, kSheetWindowClass, true);
01298     }
01299     return(baseWindowRef);
01300 }
01301 
01302 
01303 void
01304 nsMacWindow::UpdateWindowMenubar(WindowPtr nativeWindowPtr, PRBool enableFlag)
01305 {
01306     // Mac OS X sheet support
01307     // when a sheet is up, disable parent window's menus
01308     // (at least, until there is a better architecture for menus)
01309 
01310     if (!nativeWindowPtr) return;
01311 
01312     nsCOMPtr<nsIWidget> windowWidget;
01313     nsToolkit::GetTopWidget ( nativeWindowPtr, getter_AddRefs(windowWidget));
01314     if (!windowWidget) return;
01315 
01316     nsCOMPtr<nsPIWidgetMac> parentWindow ( do_QueryInterface(windowWidget) );
01317     if (!parentWindow)  return;
01318     nsCOMPtr<nsIMenuBar> menubar;
01319     parentWindow->GetMenuBar(getter_AddRefs(menubar));
01320     if (!menubar) return;
01321 
01322     PRUint32    numMenus=0;
01323     menubar->GetMenuCount(numMenus);
01324     for (PRInt32 i = numMenus-1; i >= 0; i--)
01325     {
01326         nsCOMPtr<nsIMenu> menu;
01327         menubar->GetMenuAt(i, *getter_AddRefs(menu));
01328         if (menu)
01329         {
01330             menu->SetEnabled(enableFlag);
01331         }
01332     }
01333 }
01334 
01335 
01336 /*
01337 NS_METHOD nsWindow::Minimize(void)
01338 {
01339   return NS_OK;
01340 }
01341 
01342 NS_METHOD nsWindow::Maximize(void)
01343 {
01344   return NS_OK;
01345 }
01346 
01347 NS_METHOD nsWindow::Restore(void)
01348 {
01349   return NS_OK;
01350 }
01351 */
01352 
01353 NS_IMETHODIMP nsMacWindow::ConstrainPosition(PRBool aAllowSlop,
01354                                              PRInt32 *aX, PRInt32 *aY)
01355 {
01356   if (eWindowType_popup == mWindowType || !mWindowMadeHere)
01357     return NS_OK;
01358 
01359   // Sanity check against screen size
01360   // make sure the window stays visible
01361 
01362   // get the window bounds
01363   Rect portBounds;
01364   ::GetWindowPortBounds(mWindowPtr, &portBounds);
01365   short windowWidth = portBounds.right - portBounds.left;
01366   short windowHeight = portBounds.bottom - portBounds.top;
01367 
01368   // now get our playing field. use the current screen, or failing that for any reason,
01369   // the GrayRgn (which of course is arguably more correct but has drawbacks as well)
01370   Rect screenRect;
01371   nsCOMPtr<nsIScreenManager> screenmgr = do_GetService(sScreenManagerContractID);
01372   if (screenmgr) {
01373     nsCOMPtr<nsIScreen> screen;
01374     PRInt32 left, top, width, height, fullHeight;
01375 
01376     // zero size rects can happen during window creation, and confuse
01377     // the screen manager
01378     width = windowWidth > 0 ? windowWidth : 1;
01379     height = windowHeight > 0 ? windowHeight : 1;
01380     screenmgr->ScreenForRect(*aX, *aY, width, height,
01381                             getter_AddRefs(screen));
01382     if (screen) {
01383       screen->GetAvailRect(&left, &top, &width, &height);
01384       screen->GetRect(&left, &top, &width, &fullHeight);
01385       screenRect.left = left;
01386       screenRect.right = left+width;
01387       screenRect.top = top;
01388       screenRect.bottom = top+height;
01389     }
01390   } else
01391     ::GetRegionBounds(::GetGrayRgn(), &screenRect);
01392 
01393   if (aAllowSlop) {
01394     short pos;
01395     pos = screenRect.left;
01396     if (windowWidth > kWindowPositionSlop)
01397       pos -= windowWidth - kWindowPositionSlop;
01398     if (*aX < pos)
01399       *aX = pos;
01400     else if (*aX >= screenRect.right - kWindowPositionSlop)
01401       *aX = screenRect.right - kWindowPositionSlop;
01402 
01403     pos = screenRect.top;
01404     if (windowHeight > kWindowPositionSlop)
01405       pos -= windowHeight - kWindowPositionSlop;
01406     if (*aY < pos)
01407       *aY = pos;
01408     else if (*aY >= screenRect.bottom - kWindowPositionSlop)
01409       *aY = screenRect.bottom - kWindowPositionSlop;
01410   } else {
01411     if (*aX < screenRect.left)
01412       *aX = screenRect.left;
01413     else if (*aX >= screenRect.right - windowWidth)
01414       *aX = screenRect.right - windowWidth;
01415 
01416     if (*aY < screenRect.top)
01417       *aY = screenRect.top;
01418     else if (*aY >= screenRect.bottom - windowHeight)
01419       *aY = screenRect.bottom - windowHeight;
01420   }
01421 
01422   return NS_OK;
01423 }
01424 
01425 //-------------------------------------------------------------------------
01426 //
01427 // Move this window
01428 //
01429 //-------------------------------------------------------------------------
01430 //-------------------------------------------------------------------------
01431 // Move
01432 //-------------------------------------------------------------------------
01433 NS_IMETHODIMP nsMacWindow::Move(PRInt32 aX, PRInt32 aY)
01434 {
01435   StPortSetter setOurPortForLocalToGlobal ( mWindowPtr );
01436   
01437   if (eWindowType_popup == mWindowType) {
01438     // there is a bug on OSX where if we call ::MoveWindow() with the same
01439     // coordinates (within a pixel or two) as a window's current location, it will 
01440     // move to (0,0,-1,-1). The fix is to not move the window if we're already
01441     // there. (radar# 2669004)
01442 
01443     const PRInt32 kMoveThreshold = 2;
01444     Rect currBounds;
01445     ::GetWindowBounds ( mWindowPtr, kWindowGlobalPortRgn, &currBounds );
01446     if ( abs(currBounds.left-aX) > kMoveThreshold || abs(currBounds.top-aY) > kMoveThreshold ) {
01447       ::MoveWindow(mWindowPtr, aX, aY, false);
01448       
01449       // update userstate to match, if appropriate
01450       PRInt32 sizeMode;
01451       nsBaseWidget::GetSizeMode ( &sizeMode );
01452       if ( sizeMode == nsSizeMode_Normal ) {
01453         ::GetWindowBounds ( mWindowPtr, kWindowGlobalPortRgn, &currBounds );
01454         ::SetWindowUserState ( mWindowPtr, &currBounds );
01455       }  
01456     }  
01457 
01458     return NS_OK;
01459   } else if (mWindowMadeHere) {
01460     Rect portBounds;
01461     ::GetWindowPortBounds(mWindowPtr, &portBounds);
01462 
01463     aX += mBoundsOffset.h;
01464     aY += mBoundsOffset.v;
01465 
01466     nsCOMPtr<nsIScreenManager> screenmgr = do_GetService(sScreenManagerContractID);
01467     if (screenmgr) {
01468       nsCOMPtr<nsIScreen> screen;
01469       PRInt32 left, top, width, height, fullTop;
01470       // adjust for unset bounds, which confuses the screen manager
01471       width = portBounds.right - portBounds.left;
01472       height = portBounds.bottom - portBounds.top;
01473       if (height <= 0) height = 1;
01474       if (width <= 0) width = 1;
01475 
01476       screenmgr->ScreenForRect(aX, aY, width, height,
01477                                getter_AddRefs(screen));
01478       if (screen) {
01479         screen->GetAvailRect(&left, &top, &width, &height);
01480         screen->GetRect(&left, &fullTop, &width, &height);
01481         aY += top-fullTop;
01482       }
01483     }
01484 
01485     // move the window if it has not been moved yet
01486     // (ie. if this function isn't called in response to a DragWindow event)
01487     ::LocalToGlobal((Point *) &portBounds.top);
01488     ::LocalToGlobal((Point *) &portBounds.bottom);
01489     if (portBounds.left != aX || portBounds.top != aY) {
01490       ::MoveWindow(mWindowPtr, aX, aY, false);
01491 
01492       // update userstate to match, if appropriate
01493       PRInt32 sizeMode;
01494       GetSizeMode(&sizeMode);
01495       if (sizeMode == nsSizeMode_Normal) {
01496         Rect newBounds;
01497         ::GetWindowBounds(mWindowPtr, kWindowGlobalPortRgn, &newBounds);
01498         ::SetWindowUserState(mWindowPtr, &newBounds);
01499       }
01500     }
01501 
01502     // propagate the event in global coordinates
01503     Inherited::Move(aX, aY);
01504 
01505     // reset the coordinates to (0,0) because it's the top level widget
01506     mBounds.x = 0;
01507     mBounds.y = 0;
01508   }
01509   return NS_OK;
01510 }
01511 
01512 //-------------------------------------------------------------------------
01513 //
01514 // Position the window behind the given window
01515 //
01516 //-------------------------------------------------------------------------
01517 NS_METHOD nsMacWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
01518                                    nsIWidget *aWidget, PRBool aActivate)
01519 {
01520   if (aWidget) {
01521     WindowPtr behind = (WindowPtr)aWidget->GetNativeData(NS_NATIVE_DISPLAY);
01522     ::SendBehind(mWindowPtr, behind);
01523     ::HiliteWindow(mWindowPtr, FALSE);
01524   } else {
01525     if (::FrontWindow() != mWindowPtr)
01526       ::SelectWindow(mWindowPtr);
01527   }
01528   return NS_OK;
01529 }
01530 
01531 //-------------------------------------------------------------------------
01532 //
01533 // zoom/restore
01534 //
01535 //-------------------------------------------------------------------------
01536 NS_METHOD nsMacWindow::SetSizeMode(PRInt32 aMode)
01537 {
01538   nsresult rv = NS_OK;
01539 
01540   // resize during zoom may attempt to unzoom us. here's where we put a stop to that.
01541   if (mZooming)
01542     return NS_OK;
01543 
01544   // even if our current mode is the same as aMode, keep going
01545   // because the window can be maximized and zoomed, but partially
01546   // offscreen, and we want a click of the maximize button to move
01547   // it back onscreen.
01548 
01549   if (!mVisible) {
01550     /* zooming on the Mac doesn't seem to work until the window is visible.
01551        the rest of the app is structured to zoom before the window is visible
01552        to avoid flashing. here's where we defeat that. */
01553     mZoomOnShow = aMode == nsSizeMode_Maximized;
01554   } else {
01555     PRInt32 previousMode;
01556     mZooming = PR_TRUE;
01557 
01558     nsBaseWidget::GetSizeMode(&previousMode);
01559     rv = nsBaseWidget::SetSizeMode(aMode);
01560     if (NS_SUCCEEDED(rv)) {
01561       if (aMode == nsSizeMode_Minimized) {
01562         ::CollapseWindow(mWindowPtr, true);
01563       } else {
01564         if (aMode == nsSizeMode_Maximized) {
01565           CalculateAndSetZoomedSize();
01566           ::ZoomWindow(mWindowPtr, inZoomOut, ::FrontWindow() == mWindowPtr);
01567         } else {
01568           // Only zoom in if the previous state was Maximized
01569           if (previousMode == nsSizeMode_Maximized)
01570             ::ZoomWindow(mWindowPtr, inZoomIn, ::FrontWindow() == mWindowPtr);
01571         }
01572 
01573         Rect macRect;
01574         ::GetWindowPortBounds(mWindowPtr, &macRect);
01575         Resize(macRect.right - macRect.left, macRect.bottom - macRect.top, PR_FALSE);
01576       }
01577     }
01578 
01579     mZooming = PR_FALSE;
01580   }
01581 
01582   return rv;
01583 }
01584 
01585 
01586 // we're resizing. if we happen to be zoomed ("standard" state),
01587 // switch to the user state without changing the window size.
01588 void
01589 nsMacWindow::UserStateForResize()
01590 {
01591   nsresult rv;
01592   PRInt32  currentMode;
01593 
01594   // but not if we're currently zooming
01595   if (mZooming)
01596     return;
01597 
01598   // nothing to do if we're already in the user state
01599   rv = nsBaseWidget::GetSizeMode(&currentMode);
01600   if (NS_SUCCEEDED(rv) && currentMode == nsSizeMode_Normal)
01601     return;
01602 
01603   // we've just finished sizing, so make our current size our user state,
01604   // and switch to the user state
01605   StPortSetter portState(mWindowPtr);
01606   StOriginSetter originState(mWindowPtr);
01607   Rect bounds;
01608 
01609   ::GetWindowPortBounds(mWindowPtr, &bounds);
01610   ::LocalToGlobal((Point *)&bounds.top);
01611   ::LocalToGlobal((Point *)&bounds.bottom);
01612   ::SetWindowUserState(mWindowPtr, &bounds);
01613   ::ZoomWindow(mWindowPtr, inZoomIn, false);
01614 
01615   // finally, reset our internal state to match
01616   nsBaseWidget::SetSizeMode(nsSizeMode_Normal);
01617   // Tell container window we've moved. We stopped reporting move events
01618   // when we were in the StandardState.
01619   ReportMoveEvent();
01620 }
01621 
01622 
01623 //
01624 // CalculateAndSetZoomedSize
01625 //
01626 // Recomputes the zoomed window size taking things such as window chrome,
01627 // dock position, menubar, and finder icons into account
01628 //
01629 NS_IMETHODIMP
01630 nsMacWindow::CalculateAndSetZoomedSize()
01631 {
01632   StPortSetter setOurPort(mWindowPtr);
01633 
01634   // calculate current window portbounds
01635   Rect windRect;
01636   ::GetWindowPortBounds(mWindowPtr, &windRect);
01637   ::LocalToGlobal((Point *)&windRect.top);
01638   ::LocalToGlobal((Point *)&windRect.bottom);
01639 
01640   // calculate window's borders on each side, these differ in Aqua / Platinum
01641   short wTitleHeight;
01642   short wLeftBorder;
01643   short wRightBorder;
01644   short wBottomBorder;
01645        
01646   RgnHandle structRgn = ::NewRgn();
01647   ::GetWindowRegion(mWindowPtr, kWindowStructureRgn, structRgn);
01648   Rect structRgnBounds;
01649   ::GetRegionBounds(structRgn, &structRgnBounds);
01650   wTitleHeight = windRect.top - structRgnBounds.top;
01651   wLeftBorder = windRect.left - structRgnBounds.left;
01652   wRightBorder =  structRgnBounds.right - windRect.right;
01653   wBottomBorder = structRgnBounds.bottom - windRect.bottom;
01654 
01655   ::DisposeRgn(structRgn);
01656 
01657   windRect.top -= wTitleHeight;
01658   windRect.bottom += wBottomBorder;
01659   windRect.right += wRightBorder;
01660   windRect.left -= wLeftBorder;
01661 
01662   // find which screen the window is (mostly) on and get its rect. GetAvailRect()
01663   // handles subtracting out the menubar and the dock for us. Set the zoom rect
01664   // to the screen rect, less some fudging and room for icons on the primary screen.
01665   nsCOMPtr<nsIScreenManager> screenMgr = do_GetService(sScreenManagerContractID);
01666   if ( screenMgr ) {
01667     nsCOMPtr<nsIScreen> screen;
01668     screenMgr->ScreenForRect ( windRect.left, windRect.top, windRect.right - windRect.left, windRect.bottom - windRect.top,
01669                                 getter_AddRefs(screen) );
01670     if ( screen ) {
01671       nsRect newWindowRect;
01672       screen->GetAvailRect ( &newWindowRect.x, &newWindowRect.y, &newWindowRect.width, &newWindowRect.height );
01673       
01674       // leave room for icons on primary screen
01675       nsCOMPtr<nsIScreen> primaryScreen;
01676       screenMgr->GetPrimaryScreen ( getter_AddRefs(primaryScreen) );
01677       if (screen == primaryScreen) {
01678         int iconSpace = 128;
01679         newWindowRect.width -= iconSpace;
01680       }
01681 
01682       Rect zoomRect;
01683       ::SetRect(&zoomRect,
01684                   newWindowRect.x + wLeftBorder,
01685                   newWindowRect.y + wTitleHeight,
01686                   newWindowRect.x + newWindowRect.width - wRightBorder,
01687                   newWindowRect.y + newWindowRect.height - wBottomBorder); 
01688       ::SetWindowStandardState ( mWindowPtr, &zoomRect );
01689     }
01690   }
01691   
01692   return NS_OK;
01693 
01694 } // CalculateAndSetZoomedSize
01695 
01696 
01697 //-------------------------------------------------------------------------
01698 //
01699 // Obtain the menubar for a window
01700 //
01701 //-------------------------------------------------------------------------
01702 NS_IMETHODIMP
01703 nsMacWindow::GetMenuBar(nsIMenuBar **_retval)
01704 {
01705   *_retval = mMenuBar;
01706   NS_IF_ADDREF(*_retval);
01707   return(NS_OK);
01708 }
01709 
01710 //-------------------------------------------------------------------------
01711 //
01712 // getter/setter for window to ignore the next deactivate event received
01713 // if a Mac OS X sheet is being opened
01714 //
01715 //-------------------------------------------------------------------------
01716 NS_IMETHODIMP
01717 nsMacWindow::GetIgnoreDeactivate(PRBool *_retval)
01718 {
01719   return NS_ERROR_NOT_IMPLEMENTED;
01720 }
01721 
01722 NS_IMETHODIMP
01723 nsMacWindow::SetIgnoreDeactivate(PRBool ignoreDeactivate)
01724 {
01725   return NS_ERROR_NOT_IMPLEMENTED;
01726 }
01727 
01728 NS_IMETHODIMP
01729 nsMacWindow::GetIsSheet(PRBool *_retval)
01730 {
01731   *_retval = mIsSheet;
01732   return(NS_OK);
01733 }
01734 
01735 
01736 //-------------------------------------------------------------------------
01737 //
01738 // Resize this window to a point given in global (screen) coordinates. This
01739 // differs from simple Move(): that method makes JavaScript place windows
01740 // like other browsers: it puts the top-left corner of the outer edge of the
01741 // window border at the given coordinates, offset from the menubar.
01742 // MoveToGlobalPoint expects the top-left corner of the portrect, which
01743 // is inside the border, and is not offset by the menubar height.
01744 //
01745 //-------------------------------------------------------------------------
01746 void nsMacWindow::MoveToGlobalPoint(PRInt32 aX, PRInt32 aY)
01747 {
01748   PRInt32 left, top, width, height, fullTop;
01749   Rect portBounds;
01750 
01751   StPortSetter doThatThingYouDo(mWindowPtr);
01752   ::GetWindowPortBounds(mWindowPtr, &portBounds);
01753 
01754   width = portBounds.right - portBounds.left;
01755   height = portBounds.bottom - portBounds.top;
01756   ::LocalToGlobal(&topLeft(portBounds));
01757 
01758   nsCOMPtr<nsIScreenManager> screenmgr = do_GetService(sScreenManagerContractID);
01759   if (screenmgr) {
01760     nsCOMPtr<nsIScreen> screen;
01761     //screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
01762     screenmgr->ScreenForRect(portBounds.left, portBounds.top, width, height,
01763                              getter_AddRefs(screen));
01764     if (screen) {
01765       screen->GetAvailRect(&left, &top, &width, &height);
01766       screen->GetRect(&left, &fullTop, &width, &height);
01767       aY -= top-fullTop;
01768     }
01769   }
01770 
01771   Move(aX-mBoundsOffset.h, aY-mBoundsOffset.v);
01772 }
01773 
01774 //-------------------------------------------------------------------------
01775 //
01776 // Resize this window
01777 //
01778 //-------------------------------------------------------------------------
01779 NS_IMETHODIMP nsMacWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
01780 {
01781   return Resize(aWidth, aHeight, aRepaint, PR_FALSE); // resize not from UI
01782 }
01783 
01784 nsresult nsMacWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint, PRBool aFromUI)
01785 {
01786   if (mWindowMadeHere) {
01787 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3
01788     if (mInUpdate && nsToolkit::OSXVersion() < MAC_OS_X_VERSION_10_3_HEX) {
01789       // Calling SizeWindow in the middle of an update will hang on 10.2,
01790       // even if care is taken to break out of the update with EndUpdate.
01791       // On 10.2, defer the resize until the update is done.  Update will
01792       // call back into Resize with these values after the update is
01793       // finished.
01794       mResizeTo.width = aWidth;
01795       mResizeTo.height = aHeight;
01796       mResizeTo.repaint = aRepaint;
01797       mResizeTo.fromUI = aFromUI;
01798 
01799       mNeedsResize = PR_TRUE;
01800 
01801       return NS_OK;
01802     }
01803 #endif
01804 
01805     Rect windowRect;
01806     if (::GetWindowBounds(mWindowPtr, kWindowContentRgn, &windowRect)
01807         != noErr) {
01808       NS_ERROR("::GetWindowBounds() didn't get window bounds");
01809       return NS_ERROR_FAILURE;
01810     }
01811 
01812     if (!aFromUI) {
01813       // If the resize came from the UI, trust the system's constraints.
01814       // If it came from the application, make sure it's rational.
01815       // @@@mm the ultimate goal is to separate UI, chrome, and content,
01816       // and enforce different restrictions in each case.
01817       Rect desktopRect;
01818       if (NS_FAILED(GetDesktopRect(&desktopRect))) {
01819         return NS_ERROR_FAILURE;
01820       }
01821 
01822 #if 1
01823       // Use the desktop's dimensions as the maximum size without taking
01824       // the window's position into account.  This is the code that will
01825       // eventually be used as a sanity check on chrome callers, which are
01826       // trusted to be able to resize windows to arbitrary sizes regardless
01827       // of their position on screen.  Since chrome and content can't be
01828       // separated here yet, use this less restrictive approach for both,
01829       // otherwise chrome would break.
01830       //
01831       // The maximum window dimensions are the desktop area, but if the
01832       // window is already larger than the desktop, don't force it to
01833       // shrink.  This doesn't take the window's origin into account,
01834       // but the window won't be able to expand so that it can't be
01835       // displayed entirely onscreen without being obscured by the dock
01836       // (unless the user already made it larger with UI resizing.)
01837       // Use mBoundsOffset to account for the difference between window
01838       // structure and content (like the title bar).
01839       short maxWidth  = PR_MAX(desktopRect.right  - desktopRect.left -
01840                                  mBoundsOffset.h,
01841                                windowRect.right   - windowRect.left);
01842       short maxHeight = PR_MAX(desktopRect.bottom - desktopRect.top  -
01843                                  mBoundsOffset.v,
01844                                windowRect.bottom  - windowRect.top);
01845 #else
01846       // This will eventually become the branch that is used for resizes
01847       // that come from content.  It's fairly restrictive and takes the
01848       // window's current position into account.
01849       //
01850       // Consider the window's position when computing the maximum size.
01851       // The goal is to use this on resizes that come from content, but
01852       // it's currently not possible to distinguish between resizes that
01853       // come from chrome and content.
01854       //
01855       // Use PR_MAX to pick the greatest (top,left) point.  If the window's
01856       // left edge is to the right of the desktop's left edge, the maximum
01857       // width needs to be smaller than the width of the desktop to avoid
01858       // resizing the window past the desktop during this operation.  If
01859       // the window's left edge is left of the desktop's left edge (offscreen),
01860       // use the desktop's left edge to ensure that the window, if moved,
01861       // will fit on the desktop.  The max of the desktop and window right
01862       // edges are used so that windows already offscreen aren't forced to
01863       // shrink during a resize.
01864       short maxWidth = PR_MAX(desktopRect.right, windowRect.right) -
01865                        PR_MAX(desktopRect.left, windowRect.left);
01866 
01867       // There shouldn't be any way for the window top to be above the desktop
01868       // top, but paranoia dictates that the same check should be done for
01869       // height as width.
01870       short maxHeight = PR_MAX(desktopRect.bottom, windowRect.bottom) -
01871                         PR_MAX(desktopRect.top, windowRect.top);
01872 #endif
01873 
01874       aWidth = PR_MIN(aWidth, maxWidth);
01875       aHeight = PR_MIN(aHeight, maxHeight);
01876     } // !aFromUI
01877 
01878     short currentWidth = windowRect.right - windowRect.left;
01879     short currentHeight = windowRect.bottom - windowRect.top;
01880 
01881     if (currentWidth != aWidth || currentHeight != aHeight) {
01882       if (aWidth != 0 && aHeight != 0) {
01883         // make sure that we don't infinitely recurse if live-resize is on
01884         mResizeIsFromUs = PR_TRUE;
01885         
01886         // If we're in the middle of a BeginUpdate/EndUpdate pair, SizeWindow
01887         // causes bad painting thereafter, because the EndUpdate restores a stale
01888         // visRgn on the window. So we have to temporarily break out of the
01889         // BeginUpdate/EndUpdate state.
01890         StRegionFromPool savedUpdateRgn;
01891         PRBool wasInUpdate = mInUpdate;
01892         if (mInUpdate)
01893         {
01894           // Get the visRgn (which, if we're inside BeginUpdate/EndUpdate,
01895           // is the intersection of the window visible region, and the
01896           // update region).
01897           ::GetPortVisibleRegion(::GetWindowPort(mWindowPtr), savedUpdateRgn);
01898           ::EndUpdate(mWindowPtr);
01899         }
01900           
01901         ::SizeWindow(mWindowPtr, aWidth, aHeight, aRepaint);
01902 
01903         if (wasInUpdate)
01904         {
01905           // restore the update region (note that is redundant
01906           // if aRepaint is true, because the SizeWindow will cause a full-window
01907           // invalidate anyway).
01908           ::InvalWindowRgn(mWindowPtr, savedUpdateRgn);
01909           ::BeginUpdate(mWindowPtr);
01910         }
01911 
01912         // update userstate to match, if appropriate
01913         PRInt32 sizeMode;
01914         GetSizeMode(&sizeMode);
01915         if (sizeMode == nsSizeMode_Normal) {
01916           Rect portBounds;
01917           ::GetWindowBounds(mWindowPtr, kWindowGlobalPortRgn, &portBounds);
01918           ::SetWindowUserState(mWindowPtr, &portBounds);
01919         }
01920 
01921         mResizeIsFromUs = PR_FALSE;
01922       } else {
01923         // If width or height are zero, then ::SizeWindow() has no effect. So
01924         // instead we just hide the window by calling Show(false), which sets
01925         // mVisible to false. But the window is still considered to be 'visible'
01926         // so we set that back to true.
01927         if (mVisible) {
01928           Show(PR_FALSE);
01929           mVisible = PR_TRUE;
01930         }
01931       }
01932     }
01933   } // mWindowMadeHere
01934   Inherited::Resize(aWidth, aHeight, aRepaint);
01935 
01936   // Make window visible.  Show() will not make a window visible if mBounds are
01937   // still empty.  So when resizing a window, we check if it is supposed to be
01938   // visible but has yet to be made so.  This needs to be called after
01939   // Inherited::Resize(), since that function sets mBounds.
01940   if (aWidth != 0 && aHeight != 0 && mVisible && !mShown)
01941     Show(PR_TRUE);
01942 
01943   return NS_OK;
01944 }
01945 
01946 NS_IMETHODIMP nsMacWindow::GetScreenBounds(nsRect &aRect) {
01947  
01948   nsRect localBounds;
01949   PRInt32 yAdjust = 0;
01950 
01951   GetBounds(localBounds);
01952   // nsMacWindow local bounds are always supposed to be local (0,0) but in the middle of a move
01953   // can be global. This next adjustment assures they are in local coordinates, even then.
01954   localBounds.MoveBy(-localBounds.x, -localBounds.y);
01955   WidgetToScreen(localBounds, aRect);
01956 
01957   nsCOMPtr<nsIScreenManager> screenmgr = do_GetService(sScreenManagerContractID);
01958   if (screenmgr) {
01959     nsCOMPtr<nsIScreen> screen;
01960     //screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
01961     screenmgr->ScreenForRect(aRect.x, aRect.y, aRect.width, aRect.height,
01962                              getter_AddRefs(screen));
01963     if (screen) {
01964       PRInt32 left, top, width, height, fullTop;
01965       screen->GetAvailRect(&left, &top, &width, &height);
01966       screen->GetRect(&left, &fullTop, &width, &height);
01967       yAdjust = top-fullTop;
01968     }
01969   }
01970  
01971   aRect.MoveBy(-mBoundsOffset.h, -mBoundsOffset.v-yAdjust);
01972 
01973   return NS_OK;
01974 }
01975 
01976 //-------------------------------------------------------------------------
01977 //
01978 //
01979 //-------------------------------------------------------------------------
01980 PRBool nsMacWindow::OnPaint(nsPaintEvent &event)
01981 {
01982   return PR_TRUE; // don't dispatch the update event
01983 }
01984 
01985 //-------------------------------------------------------------------------
01986 //
01987 // Set this window's title
01988 //
01989 //-------------------------------------------------------------------------
01990 NS_IMETHODIMP nsMacWindow::SetTitle(const nsAString& aTitle)
01991 {
01992   nsAString::const_iterator begin;
01993   const PRUnichar *strTitle = aTitle.BeginReading(begin).get();
01994   CFStringRef labelRef = ::CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)strTitle, aTitle.Length());
01995   if (labelRef) {
01996     ::SetWindowTitleWithCFString(mWindowPtr, labelRef);
01997     ::CFRelease(labelRef);
01998   }
01999 
02000   return NS_OK;
02001 }
02002 
02003 
02004 #pragma mark -
02005 
02006 //
02007 // impleemnt nsIMacTextInputEventSink: forward to the right method in nsMacEventHandler 
02008 //
02009 
02010 /* OSStatus HandleUpdateActiveInputArea (in wstring text, in long textLength, in short script, in short language, in long fixLen, in voidPtr hiliteRng); */
02011 NS_IMETHODIMP 
02012 nsMacWindow::HandleUpdateActiveInputArea(const nsAString & text, 
02013                                          PRInt16 script, PRInt16 language, PRInt32 fixLen, void * hiliteRng, 
02014                                          OSStatus *_retval)
02015 {
02016   *_retval = eventNotHandledErr;
02017   NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
02018   const nsPromiseFlatString& buffer = PromiseFlatString(text);
02019   // ignore script and langauge information for now. 
02020   nsresult res = mMacEventHandler->UnicodeHandleUpdateInputArea(buffer.get(), buffer.Length(), fixLen, (TextRangeArray*) hiliteRng);
02021   // we will lost the real OSStatus for now untill we change the nsMacEventHandler
02022   if (NS_SUCCEEDED(res))
02023     *_retval = noErr;
02024   return res;
02025 }
02026 
02027 /* OSStatus HandleUpdateActiveInputAreaForNonUnicode (in string text, in long textLength, in short script, in short language, in long fixLen, in voidPtr hiliteRng); */
02028 NS_IMETHODIMP 
02029 nsMacWindow::HandleUpdateActiveInputAreaForNonUnicode(const nsACString & text, 
02030                                                       PRInt16 script, PRInt16 language, 
02031                                                       PRInt32 fixLen, void * hiliteRng, 
02032                                                       OSStatus *_retval)
02033 {
02034   return NS_ERROR_NOT_IMPLEMENTED;
02035 }
02036 
02037 /* OSStatus HandleUnicodeForKeyEvent (in wstring text, in long textLength, in short script, in short language, in voidPtr keyboardEvent); */
02038 NS_IMETHODIMP 
02039 nsMacWindow::HandleUnicodeForKeyEvent(const nsAString & text, 
02040                                       PRInt16 script, PRInt16 language, void * keyboardEvent, 
02041                                       OSStatus *_retval)
02042 {
02043   *_retval = eventNotHandledErr;
02044   NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
02045   // ignore langauge information for now. 
02046   // we will lost the real OSStatus for now untill we change the nsMacEventHandler
02047   EventRecord* eventPtr = (EventRecord*)keyboardEvent;
02048   const nsPromiseFlatString& buffer = PromiseFlatString(text);
02049   nsresult res = mMacEventHandler->HandleUKeyEvent(buffer.get(), buffer.Length(), *eventPtr);
02050   // we will lost the real OSStatus for now untill we change the nsMacEventHandler
02051   if(NS_SUCCEEDED(res))
02052     *_retval = noErr;
02053   return res;
02054 }
02055 
02056 /* OSStatus HandleOffsetToPos (in long offset, out short pointX, out short pointY); */
02057 NS_IMETHODIMP 
02058 nsMacWindow::HandleOffsetToPos(PRInt32 offset, PRInt16 *pointX, PRInt16 *pointY, OSStatus *_retval)
02059 {
02060   *_retval = eventNotHandledErr;
02061   NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
02062   *pointX = *pointY = 0;
02063   Point thePoint = {0,0};
02064   nsresult res = mMacEventHandler->HandleOffsetToPosition(offset, &thePoint);
02065   // we will lost the real OSStatus for now untill we change the nsMacEventHandler
02066   if(NS_SUCCEEDED(res))
02067     *_retval = noErr;
02068   *pointX = thePoint.h;
02069   *pointY = thePoint.v;  
02070   return res;
02071 }
02072 
02073 /* OSStatus 
02074 HandlePosToOffset (in short currentPointX, in short currentPointY, out long offset, out short regionClass); */
02075 NS_IMETHODIMP 
02076 nsMacWindow::HandlePosToOffset(PRInt16 currentPointX, PRInt16 currentPointY, 
02077                                PRInt32 *offset, PRInt16 *regionClass, OSStatus *_retval)
02078 {
02079   *_retval = eventNotHandledErr;
02080   NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
02081   *_retval = noErr;
02082   Point thePoint;
02083   thePoint.h = currentPointX;
02084   thePoint.v = currentPointY;
02085   *offset = mMacEventHandler->HandlePositionToOffset(thePoint, regionClass);
02086   return NS_OK;
02087 }
02088 
02089 /* OSStatus HandleGetSelectedText (out AString selectedText); */
02090 NS_IMETHODIMP 
02091 nsMacWindow::HandleGetSelectedText(nsAString & selectedText, OSStatus *_retval)
02092 {
02093   *_retval = noErr;
02094   NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
02095   return mMacEventHandler->HandleUnicodeGetSelectedText(selectedText);
02096 }
02097 
02098 #pragma mark -
02099 
02100 
02101 //
02102 // DispatchEvent
02103 //
02104 // Handle an event coming into us and send it to gecko.
02105 //
02106 NS_IMETHODIMP
02107 nsMacWindow::DispatchEvent ( void* anEvent, PRBool *_retval )
02108 {
02109   *_retval = PR_FALSE;
02110 
02111   // This method is presently only used for mouse events, which are sent into
02112   // into nsMacEventHandler::HandleOSEvent.  Mouse events can't be sent to
02113   // windows that aren't shown, but in some cases, we'll see them anyway.  This
02114   // can happen when a pop-up window is logically hidden but is physically in
02115   // the process of fading out.
02116   //
02117   // If a pop-up is fading out because its parent widget hierarchy has
02118   // disappeared (such as when its parent window is closed), then the pop-up
02119   // will be destroyed as soon as it has faded out, and some of the objects
02120   // in the pop-up, including the event handler, will be invalid.  To avoid
02121   // definite crashes, filter out these events that logically should not
02122   // even exist.
02123   if (!mShown)
02124     return NS_OK;
02125 
02126   NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
02127   *_retval = mMacEventHandler->HandleOSEvent(*NS_REINTERPRET_CAST(EventRecord*,anEvent));
02128 
02129   return NS_OK;
02130 }
02131 
02132 
02133 //
02134 // DispatchEvent
02135 //
02136 // Handle an event coming into us and send it to gecko.
02137 //
02138 NS_IMETHODIMP
02139 nsMacWindow::DispatchMenuEvent ( void* anEvent, PRInt32 aNativeResult, PRBool *_retval )
02140 {
02141 #if USE_MENUSELECT
02142   *_retval = PR_FALSE;
02143   NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
02144   *_retval = mMacEventHandler->HandleMenuCommand(*NS_REINTERPRET_CAST(EventRecord*,anEvent), aNativeResult);
02145 #endif
02146 
02147   return NS_OK;
02148 }
02149 
02150 
02151 //
02152 // DragEvent
02153 //
02154 // The drag manager has let us know that something related to a drag has
02155 // occurred in this window. It could be any number of things, ranging from 
02156 // a drop, to a drag enter/leave, or a drag over event. The actual event
02157 // is passed in |aMessage| and is passed along to our event hanlder so Gecko
02158 // knows about it.
02159 //
02160 NS_IMETHODIMP
02161 nsMacWindow::DragEvent(PRUint32 aMessage, PRInt16 aMouseGlobalX, PRInt16 aMouseGlobalY,
02162                          PRUint16 aKeyModifiers, PRBool *_retval)
02163 {
02164   *_retval = PR_FALSE;
02165   NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
02166   Point globalPoint = {aMouseGlobalY, aMouseGlobalX};         // QD Point stored as v, h
02167   *_retval = mMacEventHandler->DragEvent(aMessage, globalPoint, aKeyModifiers);
02168   
02169   return NS_OK;
02170 }
02171 
02172 
02173 //
02174 // Scroll
02175 //
02176 // Someone wants us to scroll in the current window, probably as the result
02177 // of a scrollWheel event or external scrollbars. Pass along to the 
02178 // eventhandler.
02179 //
02180 NS_IMETHODIMP
02181 nsMacWindow::Scroll ( PRBool aVertical, PRInt16 aNumLines, PRInt16 aMouseLocalX, 
02182                         PRInt16 aMouseLocalY, PRBool *_retval )
02183 {
02184   *_retval = PR_FALSE;
02185   NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
02186   Point localPoint = {aMouseLocalY, aMouseLocalX};
02187   *_retval = mMacEventHandler->Scroll(aVertical ? aNumLines : 0,
02188                                       aVertical ? 0 : aNumLines,
02189                                       PR_FALSE, localPoint, this, 0);
02190   return NS_OK;
02191 }
02192 
02193 
02194 NS_IMETHODIMP
02195 nsMacWindow::Idle()
02196 {
02197   // do some idle stuff?
02198   return NS_ERROR_NOT_IMPLEMENTED;
02199 }
02200 
02201 
02202 #pragma mark -
02203 
02204 
02205 //-------------------------------------------------------------------------
02206 //
02207 // Like ::BringToFront, but constrains the window to its z-level
02208 //
02209 //-------------------------------------------------------------------------
02210 NS_IMETHODIMP
02211 nsMacWindow::ComeToFront()
02212 {
02213   nsZLevelEvent event(PR_TRUE, NS_SETZLEVEL, this);
02214 
02215   event.point.x = mBounds.x;
02216   event.point.y = mBounds.y;
02217   event.time = PR_IntervalNow();
02218 
02219   event.mImmediate = PR_TRUE;
02220 
02221   DispatchWindowEvent(event);
02222   
02223   return NS_OK;
02224 }
02225 
02226 
02227 NS_IMETHODIMP nsMacWindow::ResetInputState()
02228 {
02229   NS_ENSURE_TRUE(mMacEventHandler.get(), NS_ERROR_FAILURE);
02230   return mMacEventHandler->ResetInputState();
02231 }
02232 
02233 void nsMacWindow::SetIsActive(PRBool aActive)
02234 {
02235   mIsActive = aActive;
02236 }
02237 
02238 void nsMacWindow::IsActive(PRBool* aActive)
02239 {
02240   *aActive = mIsActive;
02241 }
02242 
02243 //
02244 // GetDesktopRect
02245 //
02246 // Determines a suitable desktop size for this window.  The desktop
02247 // size can be used to restrict resizes and moves.
02248 //
02249 // If a window already lies on a single screen (possibly with portions
02250 // beyond the visible desktop shown on any screen), then the containing
02251 // screen's bounds are the limiting factor.  If a window lies on multiple
02252 // multiple screens (or the resize comes from the UI as opposed to a
02253 // script), the rect encompassing all screens is the maximum limit.
02254 // That rect (screen size or desktop size) will be put into
02255 // desktopRect.  If the menu bar is at the top edge of desktopRect, or
02256 // the dock is at any other edge of desktopRect, they will be clipped out
02257 // of desktopRect, because scripted resizes should not be able to make
02258 // windows that don't fit in the visible area.
02259 typedef enum {
02260   kDockOrientationNone       = 0,
02261   kDockOrientationHorizontal = 1,
02262   kDockOrientationVertical   = 2
02263 } nsMacDockOrientation;
02264 
02265 nsresult nsMacWindow::GetDesktopRect(Rect* desktopRect)
02266 {
02267   // This is how to figure out where the dock and menu bar are, in global
02268   // coordinates, when there seems to be no way to get that information
02269   // directly from the system.  Compare the real dimensions of each screen
02270   // with the dimensions of the screen less the space used for the dock (as
02271   // given by GetAvailableWindowPositioningBounds) and apply deductive
02272   // reasoning.  While doing that, it's a convenient time to add each
02273   // screen's full rect to the union in desktopRect.
02274 
02275   ::SetRect(desktopRect, 0, 0, 0, 0);
02276   Rect menuBarRect = {0, 0, 0, 0};
02277   Rect dockRect = {0, 0, 0, 0};
02278 
02279   Rect windowRect;
02280   if (::GetWindowBounds(mWindowPtr, kWindowStructureRgn, &windowRect)
02281       != noErr) {
02282     NS_ERROR("::GetWindowBounds() didn't get window bounds");
02283     return NS_ERROR_FAILURE;
02284   }
02285 
02286   Rect tempRect; // for various short-lived purposes, such as holding
02287                  // unimportant overlap areas in ::SectRect calls
02288 
02289   PRUint32 windowOnScreens = 0; // number of screens containing window
02290   Rect windowOnScreenRect = {0, 0, 0, 0}; // only valid if windowOnScreens==1
02291   GDHandle screen = ::DMGetFirstScreenDevice(TRUE);
02292   nsMacDockOrientation dockOrientation = kDockOrientationNone;
02293   while (screen != NULL) {
02294     Rect screenRect = (*screen)->gdRect;
02295     if (::EmptyRect(&screenRect)) {
02296       NS_WARNING("Couldn't determine screen dimensions");
02297       continue;
02298     }
02299 
02300     ::UnionRect(desktopRect, &screenRect, desktopRect);
02301 
02302     if (::SectRect(&screenRect, &windowRect, &tempRect)) {
02303       // The window is at least partially on this screen.  Set
02304       // windowOnScreenRect to the bounds of this screen.
02305       // If, after checking any other screens, the window is only on one
02306       // screen, then windowOnScreenRect will be used as the bounds,
02307       // limiting the window to the screen that contains it.
02308       windowOnScreens++;
02309       windowOnScreenRect = screenRect;
02310     }
02311 
02312     // GetAvailableWindowPositioningBounds clips out the menu bar
02313     // and the dock.  Since the menu bar is clipped from the top,
02314     // and the dock can't be at the top, this suffices.
02315     Rect availableRect;
02316     if (::GetAvailableWindowPositioningBounds(screen, &availableRect)
02317         != noErr) {
02318       NS_ERROR("::GetAvailableWindowPositioningBounds couldn't determine" \
02319                "available screen dimensions");
02320       return NS_ERROR_FAILURE;
02321     }
02322 
02323     // The menu bar is at the top of the primary display.  Its bounds
02324     // could be retrieved by looking at the main display outside of the
02325     // screen iterator loop.
02326     if (availableRect.top > screenRect.top) {
02327       ::SetRect(&menuBarRect, screenRect.left, screenRect.top,
02328                               screenRect.right, availableRect.top);
02329     }
02330 
02331     // The dock, if it's not hidden, will be at the bottom, right,
02332     // or left edge of a screen.  Look for it on this screen.  If
02333     // it's found, set up dockRect to extend from the extremity of
02334     // availableRect to the extremity of screenRect.
02335     //
02336     // It seems that even if the dock is hidden, the system reserves
02337     // 4px at the screen edge for it.
02338     if (availableRect.bottom < screenRect.bottom) {
02339       // Dock on bottom
02340       ::SetRect(&dockRect, availableRect.left, availableRect.bottom,
02341                            availableRect.right, screenRect.bottom);
02342       dockOrientation = kDockOrientationHorizontal;
02343     }
02344     else if (availableRect.right < screenRect.right) {
02345       // Dock on right
02346       ::SetRect(&dockRect, availableRect.right, availableRect.top,
02347                            screenRect.right, availableRect.bottom);
02348       dockOrientation = kDockOrientationVertical;
02349     }
02350     else if (availableRect.left > screenRect.left) {
02351       // Dock on left
02352       ::SetRect(&dockRect, screenRect.left, availableRect.top,
02353                            availableRect.left, availableRect.bottom);
02354       dockOrientation = kDockOrientationVertical;
02355     }
02356 
02357     screen = ::DMGetNextScreenDevice(screen, TRUE);
02358   }
02359 
02360   if (windowOnScreens == 1) {
02361     // The window is contained entirely on a single screen.  It may actually
02362     // be partly offscreen, the key is that no part of the window is on a
02363     // screen other than the one with screen bounds in windowOnScreenRect.
02364     // Use that screen's bounds.
02365     //
02366     // It's tempting to save the screen's available positioning bounds and
02367     // return early here, but we still need to check to see if the window is
02368     // under the dock already, and permit it to remain there if so.
02369     //
02370     // Keep in mind that if the resize came from the UI as opposed to a
02371     // script, the window's dimensions will already appear to exceed the
02372     // screen it had formerly been contained within if the user has resized
02373     // it to cover multiple screens.  In other words, this won't constrain
02374     // UI resizes to a single screen.  That's perfect, because we only
02375     // want to police script resizing.
02376     *desktopRect = windowOnScreenRect;
02377   }
02378 
02379   if (::EmptyRect(desktopRect)) {
02380     NS_ERROR("Couldn't determine desktop dimensions\n");
02381     return NS_ERROR_FAILURE;
02382   }
02383 
02384   // *desktopRect is now the smallest rectangle enclosing all of
02385   // the screens, or if the window is on a single screen, the rectangle
02386   // corresponding to the screen itself, without any areas clipped out for
02387   // the menu bar or dock.
02388 
02389   // Clip where appropriate.
02390 
02391   if (::SectRect(desktopRect, &menuBarRect, &tempRect) &&
02392       menuBarRect.top == desktopRect->top) {
02393     // The menu bar is in the desktop rect at the very top.  Adjust the
02394     // top of the desktop to clip out the menu bar.
02395     // menuBarRect.bottom is the height of the menu bar because
02396     // menuBarRect.top is 0, so there's no need to subtract top
02397     // from bottom.  When using multiple monitors, the menu bar
02398     // might not be at the desktop top - in that case, don't clip it.
02399     desktopRect->top += menuBarRect.bottom;
02400   }
02401 
02402   if (dockOrientation != kDockOrientationNone &&
02403       ::SectRect(desktopRect, &dockRect, &tempRect)) {
02404     // There's a dock and it's somewhere in the desktop rect.
02405 
02406     // Determine the orientation of the dock before checking to see if
02407     // it's on an edge, so that the proper edge can be clipped.  If
02408     // the dock is on an edge corresponding to its orientation, clip
02409     // the dock out of the available desktop.
02410     switch (dockOrientation) {
02411       case kDockOrientationVertical:
02412         // The dock is oriented vertically, so check the left and right
02413         // edges.
02414         if (dockRect.left == desktopRect->left) {
02415           desktopRect->left += dockRect.right - dockRect.left;
02416         }
02417         else if (dockRect.right == desktopRect->right) {
02418           desktopRect->right -= dockRect.right - dockRect.left;
02419         }
02420         break;
02421 
02422       case kDockOrientationHorizontal:
02423         // The dock is oriented horizontally, so check the bottom edge.
02424         // The dock can't be at the top edge.
02425         if (dockRect.bottom == desktopRect->bottom) {
02426           desktopRect->bottom -= dockRect.bottom - dockRect.top;
02427         }
02428         break;
02429 
02430       default:
02431         break;
02432     } // switch
02433   } // dockRect overlaps *desktopRect
02434 
02435   // *desktopRect is now clear of the menu bar and dock, if they needed
02436   // to be cleared.
02437   return NS_OK;
02438 }
02439 
02440 //
02441 // GetChildSheet
02442 //
02443 // If aShown is true, returns the child widget associated with the sheet
02444 // attached to this nsMacWindow, or nsnull if none.
02445 //
02446 // If aShown is false, returns the first child widget of this nsMacWindow
02447 // corresponding to a sheet that wishes to be displayed, or nsnull if no
02448 // sheets are waiting to be shown.
02449 //
02450 NS_IMETHODIMP nsMacWindow::GetChildSheet(PRBool aShown, nsMacWindow** _retval)
02451 {
02452   nsIWidget* child = GetFirstChild();
02453 
02454   while (child) {
02455     // nsWindow is the base widget in the Carbon widget library, this is safe
02456     nsWindow* window = NS_STATIC_CAST(nsWindow*, child);
02457 
02458     nsWindowType type;
02459     if (NS_SUCCEEDED(window->GetWindowType(type)) &&
02460         type == eWindowType_sheet) {
02461       // if it's a sheet, it must be an nsMacWindow
02462       nsMacWindow* macWindow = NS_STATIC_CAST(nsMacWindow*, window);
02463 
02464       if ((aShown && macWindow->mShown) ||
02465           (!aShown && macWindow->mSheetNeedsShow)) {
02466         *_retval = macWindow;
02467         return NS_OK;
02468       }
02469     }
02470 
02471     child = child->GetNextSibling();
02472   }
02473 
02474   *_retval = nsnull;
02475   return NS_OK;
02476 }
02477 
02478 NS_IMETHODIMP nsMacWindow::Update()
02479 {
02480   nsresult rv = Inherited::Update();
02481 
02482 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3
02483   if (NS_SUCCEEDED(rv) && mNeedsResize) {
02484     // Something wanted to resize this window in the middle of the update
02485     // above, and the host OS is 10.2.  The resize would have caused a
02486     // hang in SizeWindow, so it was deferred.  Now that the update is
02487     // done, it's safe to resize.
02488     mNeedsResize = PR_FALSE;
02489 
02490     Resize(mResizeTo.width, mResizeTo.height,
02491            mResizeTo.repaint, mResizeTo.fromUI);
02492   }
02493 #endif
02494 
02495   return rv;
02496 }
02497 
02498 pascal OSStatus
02499 nsMacWindow::KeyEventHandler(EventHandlerCallRef aHandlerCallRef,
02500                              EventRef            aEvent,
02501                              void*               aUserData)
02502 {
02503   nsMacWindow* self = NS_STATIC_CAST(nsMacWindow*, aUserData);
02504   NS_ASSERTION(self, "No self?");
02505   NS_ASSERTION(self->mMacEventHandler.get(), "No mMacEventHandler?");
02506 
02507   PRBool handled = PR_FALSE;
02508 
02509   EventKind kind = ::GetEventKind(aEvent);
02510   if (kind == kEventRawKeyModifiersChanged)
02511     handled = self->mMacEventHandler->HandleKeyModifierEvent(aHandlerCallRef,
02512                                                              aEvent);
02513   else
02514     handled = self->mMacEventHandler->HandleKeyUpDownEvent(aHandlerCallRef,
02515                                                            aEvent);
02516 
02517   if (!handled)
02518     return eventNotHandledErr;
02519 
02520   return noErr;
02521 }
02522 
02523 NS_IMETHODIMP
02524 nsMacWindow::GetEventDispatchHandler(nsMacEventDispatchHandler** aEventDispatchHandler) {
02525   *aEventDispatchHandler = mMacEventHandler->GetEventDispatchHandler();
02526   return NS_OK;
02527 }
02528 
02529 NS_IMETHODIMP
02530 nsMacWindow::GetSheetShown(PRBool *aSheetShown)
02531 {
02532   *aSheetShown = mSheetShown;
02533   return NS_OK;
02534 }
02535 
02536 NS_IMETHODIMP
02537 nsMacWindow::SetSheetShown(PRBool aSheetShown)
02538 {
02539   mSheetShown = aSheetShown;
02540   return NS_OK;
02541 }