Back to index

lightning-sunbird  0.9+nobinonly
nsMacControl.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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsIFontMetrics.h"
00039 #include "nsIDeviceContext.h"
00040 #include "nsFont.h"
00041 #include "nsFontUtils.h"
00042 #include "nsToolkit.h"
00043 #include "nsGfxUtils.h"
00044 
00045 #include "nsMacControl.h"
00046 #include "nsColor.h"
00047 #include "nsFontMetricsMac.h"
00048 
00049 #include "nsIServiceManager.h"
00050 #include "nsIPlatformCharset.h"
00051 
00052 #include <Carbon/Carbon.h>
00053 
00054 #if 0
00055 void DumpControlState(ControlHandle inControl, const char* message)
00056 {
00057   if (!message) message = "gdb called";
00058   
00059   CGrafPtr curPort;
00060   ::GetPort((GrafPtr*)&curPort);
00061   Rect portBounds;
00062   ::GetPortBounds(curPort, &portBounds);
00063 
00064   Rect controlBounds = {0, 0, 0, 0};
00065   if (inControl)
00066     ::GetControlBounds(inControl, &controlBounds);
00067     
00068   printf("%20s -- port %p bounds %d, %d, %d, %d, control bounds %d, %d, %d, %d\n", message, curPort,
00069     portBounds.left, portBounds.top, portBounds.right, portBounds.bottom,
00070     controlBounds.left, controlBounds.top, controlBounds.right, controlBounds.bottom);
00071 }
00072 #endif
00073 
00074 
00075 static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
00076 // TODO: leaks, need to release when unloading the dll
00077 nsIUnicodeEncoder * nsMacControl::mUnicodeEncoder = nsnull;
00078 nsIUnicodeDecoder * nsMacControl::mUnicodeDecoder = nsnull;
00079 
00080 #pragma mark -
00081 
00082 //-------------------------------------------------------------------------
00083 //
00084 //
00085 //-------------------------------------------------------------------------
00086 nsMacControl::nsMacControl()
00087 : mWidgetArmed(PR_FALSE)
00088 , mMouseInButton(PR_FALSE)
00089 , mValue(0)
00090 , mMin(0)
00091 , mMax(0)
00092 , mControl(nsnull)
00093 , mControlType(pushButProc)
00094 , mControlEventHandler(nsnull)
00095 , mWindowEventHandler(nsnull)
00096 , mLastValue(0)
00097 , mLastHilite(0)
00098 {
00099   AcceptFocusOnClick(PR_FALSE);
00100 }
00101 
00106 NS_IMETHODIMP nsMacControl::Create(nsIWidget *aParent,
00107                       const nsRect &aRect,
00108                       EVENT_CALLBACK aHandleEventFunction,
00109                       nsIDeviceContext *aContext,
00110                       nsIAppShell *aAppShell,
00111                       nsIToolkit *aToolkit,
00112                       nsWidgetInitData *aInitData) 
00113 {
00114        Inherited::Create(aParent, aRect, aHandleEventFunction,
00115                                           aContext, aAppShell, aToolkit, aInitData);
00116   
00117        // create native control. mBounds has been set up at this point
00118        nsresult             theResult = CreateOrReplaceMacControl(mControlType);
00119 
00120        mLastBounds = mBounds;
00121 
00122        return theResult;
00123 }
00124 
00125 //-------------------------------------------------------------------------
00126 //
00127 //
00128 //-------------------------------------------------------------------------
00129 nsMacControl::~nsMacControl()
00130 {
00131        if (mControl)
00132        {
00133               Show(PR_FALSE);
00134               ClearControl();
00135               mControl = nsnull;
00136        }
00137 }
00138 
00139 //-------------------------------------------------------------------------
00140 //
00141 //
00142 //-------------------------------------------------------------------------
00143 NS_IMETHODIMP
00144 nsMacControl::Destroy()
00145 {
00146        if (mOnDestroyCalled)
00147               return NS_OK;
00148 
00149        // Hide the control to avoid drawing.  Even if we're very careful
00150        // and avoid drawing the control ourselves after Destroy() is
00151        // called, the system still might draw it, and it might wind up
00152        // in the wrong location.
00153        Show(PR_FALSE);
00154 
00155        return Inherited::Destroy();
00156 }
00157 
00158 #pragma mark -
00159 //-------------------------------------------------------------------------
00160 //
00161 //
00162 //-------------------------------------------------------------------------
00163 PRBool nsMacControl::OnPaint(nsPaintEvent &aEvent)
00164 {
00165        if (mControl && mVisible)
00166        {
00167               // turn off drawing for setup to avoid ugliness
00168               Boolean              isVisible = ::IsControlVisible(mControl);
00169               ::SetControlVisibility(mControl, false, false);
00170 
00171               // update title
00172               if (mLabel != mLastLabel)
00173               {
00174                      NSStringSetControlTitle(mControl,mLabel);
00175               }
00176 
00177               // update bounds
00178               if (mBounds != mLastBounds)
00179               {
00180                      nsRect ctlRect;
00181                      GetRectForMacControl(ctlRect);
00182                      Rect macRect;
00183                      nsRectToMacRect(ctlRect, macRect);
00184 
00185                      ::SetControlBounds(mControl, &macRect);
00186 
00187                      mLastBounds = mBounds;
00188 
00189 #if 0
00190                      // The widget rect can be larger than the control
00191                      // so the rect can be erased here to set up the
00192                      // background.  Unfortunately, the background color
00193                      // isn't properly set up (bug 5685).
00194                      //
00195                      // Since the only native control in use at the moment
00196                      // is the scrollbar, and the scrollbar does occupy
00197                      // the entire widget rect, there's no need to erase
00198                      // at all.
00199                      nsRect bounds = mBounds;
00200                      bounds.x = bounds. y = 0;
00201                      nsRectToMacRect(bounds, macRect);
00202                      ::EraseRect(&macRect);
00203 #endif
00204               }
00205 
00206               // update value
00207               if (mValue != mLastValue)
00208               {
00209                      mLastValue = mValue;
00210                      ::SetControl32BitValue(mControl, mValue);
00211               }
00212 
00213               // update hilite
00214               SetupControlHiliteState();
00215 
00216               ::SetControlVisibility(mControl, isVisible, false);
00217 
00218               // Draw the control
00219               ::DrawOneControl(mControl);
00220        }
00221        return PR_FALSE;
00222 }
00223 
00224 //-------------------------------------------------------------------------
00225 //
00226 //
00227 //-------------------------------------------------------------------------
00228 PRBool  nsMacControl::DispatchMouseEvent(nsMouseEvent &aEvent)
00229 {
00230        PRBool eatEvent = PR_FALSE;
00231        switch (aEvent.message)
00232        {
00233               case NS_MOUSE_LEFT_DOUBLECLICK:
00234               case NS_MOUSE_LEFT_BUTTON_DOWN:
00235                      if (mEnabled)
00236                      {
00237                             mMouseInButton = PR_TRUE;
00238                             mWidgetArmed = PR_TRUE;
00239                             Invalidate(PR_TRUE);
00240                      }
00241                      break;
00242 
00243               case NS_MOUSE_LEFT_BUTTON_UP:
00244                      // if the widget was not armed, eat the event
00245                      if (!mWidgetArmed)
00246                             eatEvent = PR_TRUE;
00247                      // if the mouseUp happened on another widget, eat the event too
00248                      // (the widget which got the mouseDown is always notified of the mouseUp)
00249                      if (! mMouseInButton)
00250                             eatEvent = PR_TRUE;
00251                      mWidgetArmed = PR_FALSE;
00252                      if (mMouseInButton)
00253                             Invalidate(PR_TRUE);
00254                      break;
00255 
00256               case NS_MOUSE_EXIT:
00257                      mMouseInButton = PR_FALSE;
00258                      if (mWidgetArmed)
00259                             Invalidate(PR_TRUE);
00260                      break;
00261 
00262               case NS_MOUSE_ENTER:
00263                      mMouseInButton = PR_TRUE;
00264                      if (mWidgetArmed)
00265                             Invalidate(PR_TRUE);
00266                      break;
00267        }
00268        if (eatEvent)
00269               return PR_TRUE;
00270        return (Inherited::DispatchMouseEvent(aEvent));
00271 }
00272 
00273 //-------------------------------------------------------------------------
00274 //
00275 //
00276 //-------------------------------------------------------------------------
00277 void  nsMacControl::ControlChanged(PRInt32 aNewValue)
00278 {
00279        if (aNewValue != mValue)
00280        {
00281               mValue = aNewValue;
00282               mLastValue = mValue; // safely assume that the control has been repainted already
00283 
00284               nsGUIEvent guiEvent(PR_TRUE, NS_CONTROL_CHANGE, this);
00285               guiEvent.time        = PR_IntervalNow();
00286               Inherited::DispatchWindowEvent(guiEvent);
00287        }
00288 }
00289 
00290 
00291 #pragma mark -
00292 //-------------------------------------------------------------------------
00293 //
00294 //
00295 //-------------------------------------------------------------------------
00296 NS_IMETHODIMP nsMacControl::Enable(PRBool bState)
00297 {
00298   PRBool priorState = mEnabled;
00299   Inherited::Enable(bState);
00300   if ( priorState != bState )
00301     Invalidate(PR_FALSE);
00302   return NS_OK;
00303 }
00304 //-------------------------------------------------------------------------
00305 //
00306 //
00307 //-------------------------------------------------------------------------
00308 NS_IMETHODIMP nsMacControl::Show(PRBool bState)
00309 {
00310   Inherited::Show(bState);
00311   if (mControl)
00312   {
00313        ::SetControlVisibility(mControl, bState, false);        // don't redraw
00314   }
00315   return NS_OK;
00316 }
00317 
00318 //-------------------------------------------------------------------------
00319 //
00320 // Set this control font
00321 //
00322 //-------------------------------------------------------------------------
00323 NS_IMETHODIMP nsMacControl::SetFont(const nsFont &aFont)
00324 {
00325        Inherited::SetFont(aFont);  
00326 
00327        SetupMacControlFont();
00328        
00329        return NS_OK;
00330 }
00331 
00332 #pragma mark -
00333 
00334 //-------------------------------------------------------------------------
00335 //
00336 // Get the rect which the Mac control uses. This may be different for
00337 // different controls, so this method allows overriding
00338 //
00339 //-------------------------------------------------------------------------
00340 void nsMacControl::GetRectForMacControl(nsRect &outRect)
00341 {
00342               outRect = mBounds;
00343               outRect.x = outRect.y = 0;
00344 }
00345 
00346 //-------------------------------------------------------------------------
00347 //
00348 // Get the current hilite state of the control
00349 //
00350 //-------------------------------------------------------------------------
00351 ControlPartCode nsMacControl::GetControlHiliteState()
00352 {
00353   // update hilite
00354   PRInt16 curHilite = kControlInactivePart;
00355 
00356   // Popups don't show up as active to the window manager, but if there's
00357   // a popup visible, its UI elements want to have an active appearance.
00358   PRBool isPopup = PR_FALSE;
00359   nsCOMPtr<nsIWidget> windowWidget;
00360   nsToolkit::GetTopWidget(mWindowPtr, getter_AddRefs(windowWidget));
00361   if (windowWidget) {
00362     nsWindowType windowType;
00363     if (NS_SUCCEEDED(windowWidget->GetWindowType(windowType)) &&
00364         windowType == eWindowType_popup) {
00365       isPopup = PR_TRUE;
00366     }
00367   }
00368 
00369   if (mEnabled && (isPopup || ::IsWindowActive(mWindowPtr)))
00370     if (mWidgetArmed && mMouseInButton)
00371       curHilite = kControlLabelPart;
00372     else
00373       curHilite = kControlNoPart;
00374 
00375   return curHilite;
00376 }
00377 
00378 void
00379 nsMacControl::SetupControlHiliteState()
00380 {
00381   PRInt16 curHilite = GetControlHiliteState();
00382   if (curHilite != mLastHilite) {
00383     mLastHilite = curHilite;
00384     ::HiliteControl(mControl, curHilite);
00385   }
00386 }
00387 
00388 //-------------------------------------------------------------------------
00389 //
00390 //
00391 //-------------------------------------------------------------------------
00392 
00393 nsresult nsMacControl::CreateOrReplaceMacControl(short inControlType)
00394 {
00395   nsresult rv = NS_ERROR_NULL_POINTER;
00396   nsRect controlRect;
00397   GetRectForMacControl(controlRect);
00398   Rect macRect;
00399   nsRectToMacRect(controlRect, macRect);
00400 
00401   ClearControl();
00402 
00403   if (mWindowPtr) {
00404     mControl = ::NewControl(mWindowPtr, &macRect, "\p", PR_FALSE,
00405                             mValue, mMin, mMax, inControlType, nsnull);
00406 
00407     if (mControl) {
00408       InstallEventHandlerOnControl();
00409       SetupControlHiliteState();
00410 
00411       // need to reset the font now
00412       // XXX to do: transfer the text in the old control over too
00413       if (mFontMetrics)
00414         SetupMacControlFont();
00415 
00416       if (mVisible)
00417         ::ShowControl(mControl);
00418 
00419       rv = NS_OK;
00420     }
00421   }
00422 
00423   return rv;
00424 }
00425 
00426 //-------------------------------------------------------------------------
00427 //
00428 //
00429 //-------------------------------------------------------------------------
00430 void nsMacControl::ClearControl()
00431 {
00432        RemoveEventHandlerFromControl();
00433        if (mControl)
00434        {
00435               ::DisposeControl(mControl);
00436               mControl = nsnull;
00437        }
00438 }
00439 
00440 //-------------------------------------------------------------------------
00441 //
00442 //
00443 //-------------------------------------------------------------------------
00444 void nsMacControl::SetupMacControlFont()
00445 {
00446        NS_PRECONDITION(mFontMetrics != nsnull, "No font metrics in SetupMacControlFont");
00447        NS_PRECONDITION(mContext != nsnull, "No context metrics in SetupMacControlFont");
00448        
00449        TextStyle            theStyle;
00450        // if needed, impose a min size of 9pt on the control font
00451        if (theStyle.tsSize < 9)
00452               theStyle.tsSize = 9;
00453        
00454        ControlFontStyleRec fontStyleRec;
00455        fontStyleRec.flags = (kControlUseFontMask | kControlUseFaceMask | kControlUseSizeMask);
00456        fontStyleRec.font = theStyle.tsFont;
00457        fontStyleRec.size = theStyle.tsSize;
00458        fontStyleRec.style = theStyle.tsFace;
00459        ::SetControlFontStyle(mControl, &fontStyleRec);
00460 }
00461 
00462 #pragma mark -
00463 
00464 //-------------------------------------------------------------------------
00465 //
00466 //
00467 //-------------------------------------------------------------------------
00468 
00469 void nsMacControl::StringToStr255(const nsAString& aText, Str255& aStr255)
00470 {
00471        nsresult rv = NS_OK;
00472        nsAString::const_iterator begin;
00473        const PRUnichar *text = aText.BeginReading(begin).get();
00474 
00475        // get file system charset and create a unicode encoder
00476        if (nsnull == mUnicodeEncoder) {
00477               nsCAutoString fileSystemCharset;
00478               GetFileSystemCharset(fileSystemCharset);
00479 
00480               nsCOMPtr<nsICharsetConverterManager> ccm = 
00481                        do_GetService(kCharsetConverterManagerCID, &rv); 
00482               if (NS_SUCCEEDED(rv)) {
00483                      rv = ccm->GetUnicodeEncoderRaw(fileSystemCharset.get(), &mUnicodeEncoder);
00484             if (NS_SUCCEEDED(rv)) {
00485               rv = mUnicodeEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nsnull, (PRUnichar)'?');
00486             }
00487               }
00488        }
00489 
00490        // converts from unicode to the file system charset
00491        if (NS_SUCCEEDED(rv)) {
00492               PRInt32 inLength = aText.Length();
00493               PRInt32 outLength = 255;
00494               rv = mUnicodeEncoder->Convert(text, &inLength, (char *) &aStr255[1], &outLength);
00495               if (NS_SUCCEEDED(rv))
00496                      aStr255[0] = outLength;
00497        }
00498 
00499        if (NS_FAILED(rv)) {
00500 //            NS_ASSERTION(0, "error: charset conversion");
00501               NS_LossyConvertUCS2toASCII buffer(Substring(aText,0,254));
00502               PRInt32 len = buffer.Length();
00503               memcpy(&aStr255[1], buffer.get(), len);
00504               aStr255[0] = len;
00505        }
00506 }
00507 
00508 //-------------------------------------------------------------------------
00509 //
00510 //
00511 //-------------------------------------------------------------------------
00512 
00513 void nsMacControl::Str255ToString(const Str255& aStr255, nsString& aText)
00514 {
00515        nsresult rv = NS_OK;
00516        
00517        // get file system charset and create a unicode encoder
00518        if (nsnull == mUnicodeDecoder) {
00519               nsCAutoString fileSystemCharset;
00520               GetFileSystemCharset(fileSystemCharset);
00521 
00522               nsCOMPtr<nsICharsetConverterManager> ccm = 
00523                        do_GetService(kCharsetConverterManagerCID, &rv); 
00524               if (NS_SUCCEEDED(rv)) {
00525                      rv = ccm->GetUnicodeDecoderRaw(fileSystemCharset.get(), &mUnicodeDecoder);
00526               }
00527        }
00528   
00529        // converts from the file system charset to unicode
00530        if (NS_SUCCEEDED(rv)) {
00531               PRUnichar buffer[512];
00532               PRInt32 inLength = aStr255[0];
00533               PRInt32 outLength = 512;
00534               rv = mUnicodeDecoder->Convert((char *) &aStr255[1], &inLength, buffer, &outLength);
00535               if (NS_SUCCEEDED(rv)) {
00536                      aText.Assign(buffer, outLength);
00537               }
00538        }
00539        
00540        if (NS_FAILED(rv)) {
00541 //            NS_ASSERTION(0, "error: charset conversion");
00542               aText.AssignWithConversion((char *) &aStr255[1], aStr255[0]);
00543        }
00544 }
00545 
00546 //-------------------------------------------------------------------------
00547 //
00548 //
00549 //-------------------------------------------------------------------------
00550 
00551 void nsMacControl::NSStringSetControlTitle(ControlHandle theControl, nsString title)
00552 {      
00553   // wow, it sure is nice being able to use core foundation ;)
00554   CFStringRef str = CFStringCreateWithCharacters(NULL, (const UniChar*)title.get(), title.Length());
00555   SetControlTitleWithCFString(theControl, str);
00556   CFRelease(str);
00557 }
00558 //-------------------------------------------------------------------------
00559 //
00560 //
00561 //-------------------------------------------------------------------------
00562 void nsMacControl::SetupMacControlFontForScript(short theScript)
00563 {
00564        short                              themeFontID;
00565        Str255                             themeFontName;
00566        SInt16                             themeFontSize;
00567        Style                              themeFontStyle;
00568        TextStyle                          theStyle;
00569        OSErr                              err;
00570 
00571        NS_PRECONDITION(mFontMetrics != nsnull, "No font metrics in SetupMacControlFont");
00572        NS_PRECONDITION(mContext != nsnull, "No context metrics in SetupMacControlFont");
00573 
00574        //
00575        // take the script and select and override font
00576        //
00577        err = ::GetThemeFont(kThemeSystemFont,theScript,themeFontName,&themeFontSize,&themeFontStyle);
00578        NS_ASSERTION(err==noErr,"nsMenu::NSStringNewMenu: GetThemeFont failed.");
00579        ::GetFNum(themeFontName,&themeFontID);
00580        
00581        // if needed, impose a min size of 9pt on the control font
00582        if (theStyle.tsSize < 9)
00583               theStyle.tsSize = 9;
00584        
00585        ControlFontStyleRec fontStyleRec;
00586        fontStyleRec.flags = (kControlUseFontMask | kControlUseFaceMask | kControlUseSizeMask);
00587        fontStyleRec.font = themeFontID;
00588        fontStyleRec.size = theStyle.tsSize;
00589        fontStyleRec.style = theStyle.tsFace;
00590        ::SetControlFontStyle(mControl, &fontStyleRec);
00591 }
00592 //-------------------------------------------------------------------------
00593 //
00594 //
00595 //-------------------------------------------------------------------------
00596 void nsMacControl::GetFileSystemCharset(nsCString & fileSystemCharset)
00597 {
00598   static nsCAutoString aCharset;
00599   nsresult rv;
00600 
00601   if (aCharset.IsEmpty()) {
00602     nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
00603          if (NS_SUCCEEDED(rv)) 
00604                 rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, aCharset);
00605 
00606     NS_ASSERTION(NS_SUCCEEDED(rv), "error getting platform charset");
00607          if (NS_FAILED(rv)) 
00608                 aCharset.AssignLiteral("x-mac-roman");
00609   }
00610   fileSystemCharset = aCharset;
00611 }
00612 
00613 //-------------------------------------------------------------------------
00614 //
00615 //
00616 //-------------------------------------------------------------------------
00617 
00618 OSStatus nsMacControl::InstallEventHandlerOnControl()
00619 {
00620   const EventTypeSpec kControlEventList[] = {
00621     // Installing a kEventControlDraw handler causes harmless but ugly visual
00622     // imperfections in scrollbar tracks on Mac OS X 10.4.0 - 10.4.2.  This is
00623     // fixed in 10.4.3.  Bug 300058.
00624     { kEventClassControl, kEventControlDraw },
00625   };
00626 
00627   static EventHandlerUPP sControlEventHandlerUPP;
00628   if (!sControlEventHandlerUPP)
00629     sControlEventHandlerUPP = ::NewEventHandlerUPP(ControlEventHandler);
00630 
00631   OSStatus err =
00632    ::InstallControlEventHandler(mControl,
00633                                 sControlEventHandlerUPP,
00634                                 GetEventTypeCount(kControlEventList),
00635                                 kControlEventList,
00636                                 (void*)this,
00637                                 &mControlEventHandler);
00638   NS_ENSURE_TRUE(err == noErr, err);
00639 
00640   const EventTypeSpec kWindowEventList[] = {
00641     { kEventClassWindow, kEventWindowActivated },
00642     { kEventClassWindow, kEventWindowDeactivated },
00643   };
00644 
00645   static EventHandlerUPP sWindowEventHandlerUPP;
00646   if (!sWindowEventHandlerUPP)
00647     sWindowEventHandlerUPP = ::NewEventHandlerUPP(WindowEventHandler);
00648 
00649   err = ::InstallWindowEventHandler(mWindowPtr,
00650                                     sWindowEventHandlerUPP,
00651                                     GetEventTypeCount(kWindowEventList),
00652                                     kWindowEventList,
00653                                     (void*)this,
00654                                     &mWindowEventHandler);
00655   return err;
00656 }
00657 
00658 //-------------------------------------------------------------------------
00659 //
00660 //
00661 //-------------------------------------------------------------------------
00662 void nsMacControl::RemoveEventHandlerFromControl()
00663 {
00664   if (mControlEventHandler) {
00665     ::RemoveEventHandler(mControlEventHandler);
00666     mControlEventHandler = nsnull;
00667   }
00668 
00669   if (mWindowEventHandler) {
00670     ::RemoveEventHandler(mWindowEventHandler);
00671     mWindowEventHandler = nsnull;
00672   }
00673 }
00674 
00675 //-------------------------------------------------------------------------
00676 //
00677 // At present, this handles only { kEventClassControl, kEventControlDraw }.
00678 //
00679 //-------------------------------------------------------------------------
00680 // static
00681 pascal OSStatus
00682 nsMacControl::ControlEventHandler(EventHandlerCallRef aHandlerCallRef,
00683                                   EventRef            aEvent,
00684                                   void*               aUserData)
00685 {
00686   nsMacControl* self = NS_STATIC_CAST(nsMacControl*, aUserData);
00687 
00688   PRBool wasDrawing = self->IsDrawing();
00689 
00690   if (wasDrawing) {
00691     if (!self->IsQDStateOK()) {
00692       // If you're here, you must be drawing the control inside |TrackControl|.
00693       // The converse is not necessarily true.
00694       //
00695       // In the |TrackControl| loop, something on a PLEvent messed with the
00696       // QD state.  The state can be fixed so the control draws in the proper
00697       // place by setting the port and origin before calling the next handler,
00698       // but it's extremely likely that the QD state was wrong when the
00699       // Control Manager looked at the mouse position, so the control's
00700       // current value will be incorrect.  Nobody wants to draw a control
00701       // that shows the wrong value (ex. scroll thumb position), so don't
00702       // draw it.  The subclass is responsible for catching this case in
00703       // its TrackControl action proc and doing something smart about it,
00704       // like fixing the port state and posting a fake event to force the
00705       // Control Manager to reread the value.
00706       //
00707       // This works in concert with |nsNativeScrollBar::DoScrollAction|.
00708       return noErr;
00709     }
00710   }
00711   else {
00712     self->StartDraw();
00713   }
00714 
00715   OSStatus err = ::CallNextEventHandler(aHandlerCallRef, aEvent);
00716 
00717   if (!wasDrawing) {
00718     self->EndDraw();
00719   }
00720 
00721   return err;
00722 }
00723 
00724 //-------------------------------------------------------------------------
00725 //
00726 // Returns true if the port and origin are set properly for this control.
00727 // Useful to determine whether the Control Manager is likely to have been
00728 // confused when it calls back into nsMacControl or a subclass.
00729 //
00730 //-------------------------------------------------------------------------
00731 PRBool nsMacControl::IsQDStateOK()
00732 {
00733   CGrafPtr controlPort = ::GetWindowPort(mWindowPtr);
00734   CGrafPtr currentPort;
00735   ::GetPort(&currentPort);
00736 
00737   if (controlPort != currentPort) {
00738     return PR_FALSE;
00739   }
00740 
00741   nsRect controlBounds;
00742   GetBounds(controlBounds);
00743   LocalToWindowCoordinate(controlBounds);
00744   Rect currentBounds;
00745   ::GetPortBounds(currentPort, &currentBounds);
00746 
00747   if (-controlBounds.x != currentBounds.left ||
00748       -controlBounds.y != currentBounds.top) {
00749     return PR_FALSE;
00750   }
00751 
00752   return PR_TRUE;
00753 }
00754 
00755 //-------------------------------------------------------------------------
00756 //
00757 // At present, this handles only
00758 //  { kEventClassWindow, kEventWindowActivated },
00759 //  { kEventClassWindow, kEventWindowDeactivated }
00760 //
00761 //-------------------------------------------------------------------------
00762 // static
00763 pascal OSStatus
00764 nsMacControl::WindowEventHandler(EventHandlerCallRef aHandlerCallRef,
00765                                  EventRef            aEvent,
00766                                  void*               aUserData)
00767 {
00768   nsMacControl* self = NS_STATIC_CAST(nsMacControl*, aUserData);
00769 
00770   // HiliteControl will cause the control to draw, so take care to only
00771   // call SetupControlHiliteState if the control is supposed to be visible.
00772   if (self->mVisible && self->ContainerHierarchyIsVisible())
00773     self->SetupControlHiliteState();
00774 
00775   // We can't call CallNextEventHandler() here:  There can be _many_ controls
00776   // in a window, and this handler is installed on the same (window) target
00777   // for each of them.  So CallNextEventHandler() recurses through this
00778   // handler once for each "additional" control, and if there are too many
00779   // controls a stack overflow can result (bmo bug 398499).  Since we never
00780   // actually "consume" an activate or deactive event here, there should be
00781   // no problem alwaye returning eventNotHandledErr.
00782   return eventNotHandledErr;
00783 }