Back to index

lightning-sunbird  0.9+nobinonly
nsChildView.mm
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
00006  * Version 1.1 (the "License"); you may not use this file except in
00007  * compliance with 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  *   Stuart Morgan <stuart.morgan@alumni.case.edu>
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 <unistd.h>
00040 
00041 #include "nsChildView.h"
00042 
00043 #include "nsCOMPtr.h"
00044 #include "nsToolkit.h"
00045 #include "prmem.h"
00046 #include "nsCRT.h"
00047 #include "nsplugindefs.h"
00048 
00049 #include "nsIFontMetrics.h"
00050 #include "nsIDeviceContext.h"
00051 #include "nsIEnumerator.h"
00052 #include "nsIRegion.h"
00053 #include "nsIRollupListener.h"
00054 #include "nsIEventSink.h"
00055 #include "nsIScrollableView.h"
00056 #include "nsIInterfaceRequestor.h"
00057 #include "nsIServiceManager.h"
00058 
00059 #include "nsCarbonHelpers.h"
00060 #include "nsGfxUtils.h"
00061 #include "nsMacResources.h"
00062 #include "nsIQDFlushManager.h"
00063 
00064 #import "nsCursorManager.h"
00065 #import "nsWindowMap.h"
00066 
00067 #define NSAppKitVersionNumber10_2 663
00068 
00069 // category of NSView methods to quiet warnings
00070 @interface NSView(ChildViewExtensions)
00071 
00072 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_3
00073 - (void)getRectsBeingDrawn:(const NSRect **)rects count:(int *)count;
00074 - (BOOL)needsToDrawRect:(NSRect)aRect;
00075 - (BOOL)wantsDefaultClipping;
00076 #endif
00077 
00078 @end
00079 
00080 // This mask is only defined on 10.4 and up.
00081 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_4
00082 enum {
00083   NSDeviceIndependentModifierFlagsMask    = 0xffff0000U
00084 };
00085 #endif
00086 
00087 //#define DEBUG_IME 1
00088 
00089 @interface ChildView(Private)
00090 
00091   // sets up our view, attaching it to its owning gecko view
00092 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild eventSink:(nsIEventSink*)inSink;
00093 
00094 // sends gecko an ime composition event
00095 - (nsRect) sendCompositionEvent:(PRInt32)aEventType;
00096 
00097 // sends gecko an ime text event
00098 - (void) sendTextEvent:(PRUnichar*) aBuffer 
00099                        attributedString:(NSAttributedString*) aString
00100                        selectedRange:(NSRange)selRange
00101                        markedRange:(NSRange)markRange
00102                        doCommit:(BOOL)doCommit;
00103 
00104   // convert from one event system to the other for event dispatching
00105 - (void) convertEvent:(NSEvent*)inEvent message:(PRInt32)inMsg toGeckoEvent:(nsInputEvent*)outGeckoEvent;
00106 
00107   // create a gecko key event out of a cocoa event
00108 - (void) convertKeyEvent:(NSEvent*)aKeyEvent message:(PRUint32)aMessage
00109            toGeckoEvent:(nsKeyEvent*)outGeckoEvent;
00110 - (void) convertLocation:(NSPoint)inPoint message:(PRInt32)inMsg
00111           modifiers:(unsigned int)inMods toGeckoEvent:(nsInputEvent*)outGeckoEvent;
00112 
00113 - (NSMenu*)getContextMenu;
00114 - (TopLevelWindowData*)ensureWindowData;
00115 
00116 - (void)setIsPluginView:(BOOL)aIsPlugin;
00117 - (BOOL)getIsPluginView;
00118 
00119 - (BOOL)childViewHasPlugin;
00120 
00121 - (void)flushRect:(NSRect)inRect;
00122 - (BOOL)isRectObscuredBySubview:(NSRect)inRect;
00123 
00124 - (void)processPendingRedraws;
00125 
00126 #if USE_CLICK_HOLD_CONTEXTMENU
00127  // called on a timer two seconds after a mouse down to see if we should display
00128  // a context menu (click-hold)
00129 - (void)clickHoldCallback:(id)inEvent;
00130 #endif
00131 
00132 @end
00133 
00134 #pragma mark -
00135 
00137 nsIRollupListener * gRollupListener = nsnull;
00138 nsIWidget         * gRollupWidget   = nsnull;
00139 
00140 #pragma mark -
00141 
00142 //#define PAINT_DEBUGGING         // flash areas as they are painted
00143 //#define INVALIDATE_DEBUGGING    // flash areas as they are invalidated
00144 
00145 #if defined(INVALIDATE_DEBUGGING) || defined(PAINT_DEBUGGING)
00146 static void blinkRect(Rect* r);
00147 static void blinkRgn(RgnHandle rgn);
00148 #endif
00149 
00150 
00151 #pragma mark -
00152 
00153 //
00154 // Convenience routines to go from a gecko rect to cocoa NSRects and back
00155 //
00156 
00157 static inline void
00158 ConvertGeckoToCocoaRect ( const nsRect & inGeckoRect, NSRect & outCocoaRect )
00159 {
00160   outCocoaRect.origin.x = inGeckoRect.x;
00161   outCocoaRect.origin.y = inGeckoRect.y;
00162   outCocoaRect.size.width = inGeckoRect.width;
00163   outCocoaRect.size.height = inGeckoRect.height;
00164 }
00165 
00166 static inline void
00167 ConvertCocoaToGeckoRect ( const NSRect & inCocoaRect, nsRect & outGeckoRect )
00168 {
00169   outGeckoRect.x = NS_STATIC_CAST(nscoord, inCocoaRect.origin.x);
00170   outGeckoRect.y = NS_STATIC_CAST(nscoord, inCocoaRect.origin.y);
00171   outGeckoRect.width = NS_STATIC_CAST(nscoord, inCocoaRect.size.width);
00172   outGeckoRect.height = NS_STATIC_CAST(nscoord, inCocoaRect.size.height);
00173 }
00174 
00175 
00176 static inline void 
00177 ConvertGeckoRectToMacRect(const nsRect& aRect, Rect& outMacRect)
00178 {
00179   outMacRect.left = aRect.x;
00180   outMacRect.top = aRect.y;
00181   outMacRect.right = aRect.x + aRect.width;
00182   outMacRect.bottom = aRect.y + aRect.height;
00183 }
00184 
00185 static PRUint32
00186 UnderlineAttributeToTextRangeType(PRUint32 aUnderlineStyle, NSRange selRange)
00187 {
00188 #ifdef DEBUG_IME
00189   NSLog(@"****in underlineAttributeToTextRangeType = %d", aUnderlineStyle);
00190 #endif
00191 
00192   // For more info on the underline attribute, please see: 
00193   // http://developer.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/ProgrammingTopics/AttributedStrings/Tasks/AccessingAttrs.html
00194   // We are not clear where the define for value 2 is right now. 
00195   // To see this value in japanese ime, type 'aaaaaaaaa' and hit space to make the
00196   // ime send you some part of text in 1 (NSSingleUnderlineStyle) and some part in 2. 
00197   // ftang will ask apple for more details
00198   //
00199   // it probably means show 1-pixel thickness underline vs 2-pixel thickness
00200   
00201   PRUint32 attr;
00202   if (selRange.length == 0) {
00203     switch (aUnderlineStyle) {
00204       case 1:
00205         attr = NS_TEXTRANGE_RAWINPUT;
00206         break;
00207       case 2:
00208       default:
00209         attr = NS_TEXTRANGE_SELECTEDRAWTEXT;
00210         break;
00211     }
00212   }
00213   else {
00214     switch (aUnderlineStyle) {
00215       case 1:
00216         attr = NS_TEXTRANGE_CONVERTEDTEXT;
00217         break;
00218       case 2:
00219       default:
00220         attr = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
00221         break;
00222     }
00223   }
00224   return attr;
00225 }
00226 
00227 static PRUint32
00228 CountRanges(NSAttributedString *aString)
00229 {
00230   // Iterate through aString for the NSUnderlineStyleAttributeName and count the 
00231   // different segments adjusting limitRange as we go.
00232   PRUint32 count = 0;
00233   NSRange effectiveRange;
00234   NSRange limitRange = NSMakeRange(0, [aString length]);
00235   while (limitRange.length > 0) {
00236     [aString attribute:NSUnderlineStyleAttributeName 
00237              atIndex:limitRange.location 
00238              longestEffectiveRange:&effectiveRange
00239              inRange:limitRange];
00240     limitRange = NSMakeRange(NSMaxRange(effectiveRange), 
00241                              NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
00242     count++;
00243   }
00244   return count;
00245 }
00246 
00247 static void
00248 ConvertAttributeToGeckoRange(NSAttributedString *aString, NSRange markRange, NSRange selRange, PRUint32 inCount, nsTextRange* aRanges)
00249 {
00250   // Convert the Cocoa range into the nsTextRange Array used in Gecko.
00251   // Iterate through the attributed string and map the underline attribute to Gecko IME textrange attributes.
00252   // We may need to change the code here if we change the implementation of validAttributesForMarkedText.
00253   PRUint32 i = 0;
00254   NSRange effectiveRange;
00255   NSRange limitRange = NSMakeRange(0, [aString length]);
00256   while ((limitRange.length > 0) && (i < inCount)) {
00257     id attributeValue = [aString attribute:NSUnderlineStyleAttributeName 
00258                               atIndex:limitRange.location 
00259                               longestEffectiveRange:&effectiveRange
00260                               inRange:limitRange];
00261     aRanges[i].mStartOffset = effectiveRange.location;                         
00262     aRanges[i].mEndOffset = NSMaxRange(effectiveRange);                         
00263     aRanges[i].mRangeType = UnderlineAttributeToTextRangeType([attributeValue intValue], selRange); 
00264     limitRange = NSMakeRange(NSMaxRange(effectiveRange), 
00265                              NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
00266     i++;
00267   }
00268   // Get current caret position.
00269   // Caret is indicator of insertion point, so mEndOffset = 0.
00270   aRanges[i].mStartOffset = selRange.location + selRange.length;                         
00271   aRanges[i].mEndOffset = 0;                         
00272   aRanges[i].mRangeType = NS_TEXTRANGE_CARETPOSITION;
00273 }
00274 
00275 static void
00276 FillTextRangeInTextEvent(nsTextEvent *aTextEvent, NSAttributedString* aString, NSRange markRange, NSRange selRange)
00277 { 
00278   // Count the number of segments in the attributed string and add one more count for sending current caret position to Gecko.
00279   // Allocate the right size of nsTextRange and draw caret at right position.
00280   // Convert the attributed string into an array of nsTextRange and get current caret position by calling above functions.
00281   PRUint32 count = CountRanges(aString) + 1;
00282   aTextEvent->rangeArray = new nsTextRange[count];
00283   if (aTextEvent->rangeArray)
00284   {
00285     aTextEvent->rangeCount = count;
00286     ConvertAttributeToGeckoRange(aString, markRange, selRange, aTextEvent->rangeCount,  aTextEvent->rangeArray);
00287   } 
00288 }
00289 
00290 #pragma mark -
00291 
00292 //-------------------------------------------------------------------------
00293 //
00294 // nsChildView constructor
00295 //
00296 //-------------------------------------------------------------------------
00297 nsChildView::nsChildView() : nsBaseWidget()
00298 , mView(nsnull)
00299 , mParentView(nsnull)
00300 , mParentWidget(nsnull)
00301 , mFontMetrics(nsnull)
00302 , mTempRenderingContext(nsnull)
00303 , mDestroyCalled(PR_FALSE)
00304 , mDestructorCalled(PR_FALSE)
00305 , mVisible(PR_FALSE)
00306 , mDrawing(PR_FALSE)
00307 , mTempRenderingContextMadeHere(PR_FALSE)
00308 , mAcceptFocusOnClick(PR_TRUE)
00309 , mLiveResizeInProgress(PR_FALSE)
00310 , mPluginDrawing(PR_FALSE)
00311 , mPluginPort(nsnull)
00312 , mVisRgn(nsnull)
00313 {
00314   SetBackgroundColor(NS_RGB(255, 255, 255));
00315   SetForegroundColor(NS_RGB(0, 0, 0));
00316 }
00317 
00318 
00319 //-------------------------------------------------------------------------
00320 //
00321 // nsChildView destructor
00322 //
00323 //-------------------------------------------------------------------------
00324 nsChildView::~nsChildView()
00325 {
00326   // notify the children that we're gone
00327   for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
00328     nsChildView* childView = NS_STATIC_CAST(nsChildView*, kid);
00329     childView->mParentWidget = nsnull;
00330   }
00331 
00332   TearDownView(); // should have already been done from Destroy
00333   
00334   NS_IF_RELEASE(mTempRenderingContext); 
00335   NS_IF_RELEASE(mFontMetrics);
00336   
00337   delete mPluginPort;
00338 
00339   if (mVisRgn)
00340   {
00341     ::DisposeRgn(mVisRgn);
00342     mVisRgn = nsnull;
00343   }
00344 }
00345 
00346 NS_IMPL_ISUPPORTS_INHERITED3(nsChildView, nsBaseWidget, nsIPluginWidget, nsIKBStateControl, nsIEventSink)
00347 
00348 //-------------------------------------------------------------------------
00349 //
00350 // Utility method for implementing both Create(nsIWidget ...) and
00351 // Create(nsNativeWidget...)
00352 //-------------------------------------------------------------------------
00353 
00354 nsresult nsChildView::StandardCreate(nsIWidget *aParent,
00355                       const nsRect &aRect,
00356                       EVENT_CALLBACK aHandleEventFunction,
00357                       nsIDeviceContext *aContext,
00358                       nsIAppShell *aAppShell,
00359                       nsIToolkit *aToolkit,
00360                       nsWidgetInitData *aInitData,
00361                       nsNativeWidget aNativeParent)
00362 {
00363   mBounds = aRect;
00364 
00365 //  CalcWindowRegions();
00366 
00367   BaseCreate(aParent, aRect, aHandleEventFunction, 
00368               aContext, aAppShell, aToolkit, aInitData);
00369 
00370   // inherit things from the parent view and create our parallel 
00371   // NSView in the Cocoa display system
00372   mParentView = nil;
00373   if ( aParent ) {
00374     SetBackgroundColor(aParent->GetBackgroundColor());
00375     SetForegroundColor(aParent->GetForegroundColor());
00376 
00377     // inherit the top-level window. NS_NATIVE_WIDGET is always a NSView
00378     // regardless of if we're asking a window or a view (for compatibility
00379     // with windows).
00380     mParentView = (NSView*)aParent->GetNativeData(NS_NATIVE_WIDGET); 
00381     mParentWidget = aParent;   
00382   }
00383   else
00384     mParentView = NS_REINTERPRET_CAST(NSView*,aNativeParent);
00385   
00386   // create our parallel NSView and hook it up to our parent. Recall
00387   // that NS_NATIVE_WIDGET is the NSView.
00388   NSRect r;
00389   ConvertGeckoToCocoaRect(mBounds, r);
00390   mView = [CreateCocoaView(r) retain];
00391   if (!mView) return NS_ERROR_FAILURE;
00392   
00393 #if DEBUG
00394   // if our parent is a popup window, we're most certainly coming from a <select> list dropdown which
00395   // we handle in a different way than other platforms. It's ok that we don't have a parent
00396   // view because we bailed before even creating the cocoa widgetry and as a result, we
00397   // don't need to assert. However, if that's not the case, we definately want to assert
00398   // to show views aren't getting correctly parented.
00399   if ( aParent ) {
00400     nsWindowType windowType;
00401     aParent->GetWindowType(windowType);
00402     if ( windowType != eWindowType_popup )
00403       NS_ASSERTION(mParentView && mView, "couldn't hook up new NSView in hierarchy");
00404   }
00405   else
00406     NS_ASSERTION(mParentView && mView, "couldn't hook up new NSView in hierarchy");
00407 #endif
00408 
00409   if (mParentView)
00410   {
00411     if (![mParentView isKindOfClass: [ChildView class]])
00412     {
00413       [mParentView addSubview:mView];
00414       mVisible = PR_TRUE;
00415       NSWindow* window = [mParentView window];
00416       if (!window) {
00417         // The enclosing view that embeds Gecko is actually hidden
00418         // right now!  This can happen when Gecko is embedded in the
00419         // tab of a Cocoa tab view.  See if the parent view responds
00420         // to our special getNativeWindow selector, and if it does,
00421         // use that to get the window instead.
00422         //if ([mParentView respondsToSelector: @selector(getNativeWindow:)])
00423         [mView setNativeWindow: [mParentView getNativeWindow]];
00424       }
00425       else
00426         [mView setNativeWindow: window];
00427 
00428     }
00429     else
00430     {
00431       [mView setNativeWindow: [mParentView getNativeWindow]];
00432     }
00433   }
00434   
00435   // if this is a ChildView, make sure that our per-window data
00436   // is set up
00437   if ([mView isKindOfClass:[ChildView class]])
00438     [(ChildView*)mView ensureWindowData];
00439 
00440   return NS_OK;
00441 }
00442 
00443 
00444 //
00445 // CreateCocoaView
00446 //
00447 // Creates the appropriate child view. Override to create something other than
00448 // our |ChildView| object. Autoreleases, so caller must retain.
00449 //
00450 NSView*
00451 nsChildView::CreateCocoaView(NSRect inFrame)
00452 {
00453   return [[[ChildView alloc] initWithFrame:inFrame geckoChild:this eventSink:nsnull] autorelease];
00454 }
00455 
00456 //-------------------------------------------------------------------------
00457 //
00458 //
00459 //-------------------------------------------------------------------------
00460 void nsChildView::TearDownView()
00461 {
00462   if (mView)
00463   {
00464     NSWindow* win = [mView window];
00465     NSResponder* responder = [win firstResponder];
00466 
00467     // We're being unhooked from the view hierarchy, don't leave our view
00468     // or a child view as the window first responder.
00469     if (responder && [responder isKindOfClass:[NSView class]] &&
00470         [(NSView*)responder isDescendantOf:mView])
00471       [win makeFirstResponder: [mView superview]];
00472 
00473     GrafPtr curPort = GetChildViewQuickDrawPort();
00474     if (curPort)
00475     {
00476       nsCOMPtr<nsIQDFlushManager> qdFlushManager =
00477        do_GetService("@mozilla.org/gfx/qdflushmanager;1");
00478       if (qdFlushManager)
00479         qdFlushManager->RemovePort(curPort);
00480     }
00481 
00482     [mView removeFromSuperviewWithoutNeedingDisplay];
00483     [mView release];
00484     mView = nil;
00485   }
00486 }
00487 
00488 //-------------------------------------------------------------------------
00489 //
00490 // create a nsChildView
00491 //
00492 //-------------------------------------------------------------------------
00493 NS_IMETHODIMP nsChildView::Create(nsIWidget *aParent,
00494                       const nsRect &aRect,
00495                       EVENT_CALLBACK aHandleEventFunction,
00496                       nsIDeviceContext *aContext,
00497                       nsIAppShell *aAppShell,
00498                       nsIToolkit *aToolkit,
00499                       nsWidgetInitData *aInitData)
00500 {  
00501   return(StandardCreate(aParent, aRect, aHandleEventFunction,
00502                           aContext, aAppShell, aToolkit, aInitData,
00503                             nsnull));
00504 }
00505 
00506 //-------------------------------------------------------------------------
00507 //
00508 // Creates a main nsChildView using a native widget (an NSView)
00509 //
00510 //-------------------------------------------------------------------------
00511 NS_IMETHODIMP nsChildView::Create(nsNativeWidget aNativeParent,
00512                       const nsRect &aRect,
00513                       EVENT_CALLBACK aHandleEventFunction,
00514                       nsIDeviceContext *aContext,
00515                       nsIAppShell *aAppShell,
00516                       nsIToolkit *aToolkit,
00517                       nsWidgetInitData *aInitData)
00518 {
00519   // what we're passed in |aNativeParent| is an NSView. 
00520   return(StandardCreate(nsnull, aRect, aHandleEventFunction,
00521                   aContext, aAppShell, aToolkit, aInitData, aNativeParent));
00522 }
00523 
00524 //-------------------------------------------------------------------------
00525 //
00526 // Close this nsChildView
00527 //
00528 //-------------------------------------------------------------------------
00529 NS_IMETHODIMP nsChildView::Destroy()
00530 {
00531   if (mDestroyCalled)
00532     return NS_OK;
00533   mDestroyCalled = PR_TRUE;
00534 
00535   [mView widgetDestroyed];
00536 
00537   nsBaseWidget::OnDestroy();
00538   nsBaseWidget::Destroy();
00539 
00540   // just to be safe. If we're going away and for some reason we're still
00541   // the rollup widget, rollup and turn off capture.
00542   if ( this == gRollupWidget ) {
00543     if ( gRollupListener )
00544       gRollupListener->Rollup();
00545     CaptureRollupEvents(nsnull, PR_FALSE, PR_TRUE);
00546   }
00547 
00548   ReportDestroyEvent(); // beard: this seems to cause the window to be deleted. moved all release code to destructor.
00549   mParentWidget = nil;
00550 
00551   TearDownView();
00552 
00553   return NS_OK;
00554 }
00555 
00556 #pragma mark -
00557 
00558 #if DEBUG
00559 static void PrintViewHierarcy(NSView *view)
00560 {
00561   while (view)
00562   {
00563     NSLog(@"  view is %@, frame %@", view, NSStringFromRect([view frame]));
00564     view = [view superview];
00565   }
00566 }
00567 #endif
00568 
00569 //-------------------------------------------------------------------------
00570 //
00571 // Return some native data according to aDataType
00572 //
00573 //-------------------------------------------------------------------------
00574 void* nsChildView::GetNativeData(PRUint32 aDataType)
00575 {
00576   void* retVal = nsnull;
00577 
00578   switch (aDataType) 
00579   {
00580     case NS_NATIVE_WIDGET:            // the NSView
00581     case NS_NATIVE_DISPLAY:
00582       retVal = (void*)mView;
00583       break;
00584 
00585     case NS_NATIVE_WINDOW:
00586       retVal = [mView getNativeWindow];
00587       break;
00588       
00589     case NS_NATIVE_GRAPHIC:           // quickdraw port
00590       // XXX qdPort is invalid if we have not locked focus
00591       retVal = GetChildViewQuickDrawPort();
00592       break;
00593       
00594     case NS_NATIVE_REGION:
00595     {
00596       if (!mVisRgn)
00597         mVisRgn = ::NewRgn();
00598 
00599       // XXX qdPort is invalid if we have not locked focus
00600       GrafPtr grafPort = GetChildViewQuickDrawPort();
00601       if (grafPort && mVisRgn)
00602         ::GetPortVisibleRegion(grafPort, mVisRgn);
00603       retVal = (void*)mVisRgn;
00604       break;
00605     }
00606       
00607     case NS_NATIVE_OFFSETX:
00608       retVal = 0;
00609       break;
00610 
00611     case NS_NATIVE_OFFSETY:
00612       retVal = 0;
00613       break;
00614     
00615 #if 0
00616     case NS_NATIVE_COLORMAP:
00617       //ĄTODO
00618       break;
00619 #endif
00620 
00621     case NS_NATIVE_PLUGIN_PORT:
00622       // this needs to be a combination of the port and the offsets.
00623       if (mPluginPort == nsnull)
00624       {
00625         mPluginPort = new nsPluginPort;
00626         if ([mView isKindOfClass:[ChildView class]])
00627           [(ChildView*)mView setIsPluginView: YES];
00628       }
00629 
00630       NSWindow* window = [mView getNativeWindow];
00631       if (window)
00632       {
00633         WindowRef topLevelWindow = (WindowRef)[window windowRef];
00634         if (topLevelWindow)
00635         {
00636           mPluginPort->port = ::GetWindowPort(topLevelWindow);
00637 
00638           NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
00639           NSRect frame = [[window contentView] frame];
00640           viewOrigin.y = frame.size.height - viewOrigin.y;
00641           
00642           // need to convert view's origin to window coordinates.
00643           // then, encode as "SetOrigin" ready values.
00644           mPluginPort->portx = (PRInt32)-viewOrigin.x;
00645           mPluginPort->porty = (PRInt32)-viewOrigin.y;
00646 
00647         }
00648       }
00649       else
00650       {
00651 #ifdef DEBUG
00652         printf("@@@@ Couldn't get NSWindow for plugin port. @@@@\n");
00653 #endif
00654       }
00655 
00656       retVal = (void*)mPluginPort;
00657       break;
00658   }
00659 
00660   return retVal;
00661 }
00662 
00663 #pragma mark -
00664 
00665 
00666 //-------------------------------------------------------------------------
00667 //
00668 // Return PR_TRUE if the whether the component is visible, PR_FALSE otherwise
00669 //
00670 //-------------------------------------------------------------------------
00671 NS_METHOD nsChildView::IsVisible(PRBool & bState)
00672 {
00673   if (!mVisible) {
00674     bState = mVisible;
00675   } else {
00676     // mVisible does not accurately reflect the state of a hidden tabbed view
00677     // so verify that the view has a window as well
00678     bState = ([mView window] != nil);
00679   }
00680   return NS_OK;
00681 }
00682 
00683 //-------------------------------------------------------------------------
00684 //
00685 // Hide or show this component
00686 //
00687 //-------------------------------------------------------------------------
00688 NS_IMETHODIMP nsChildView::Show(PRBool bState)
00689 {    
00690   if (bState != mVisible) {
00691     if (bState) 
00692       [mParentView addSubview: mView];
00693     else
00694       [mView removeFromSuperview];
00695   }
00696   mVisible = bState;
00697   return NS_OK;
00698 }
00699 
00700 nsIWidget*
00701 nsChildView::GetParent(void)
00702 {
00703   NS_IF_ADDREF(mParentWidget);
00704   return mParentWidget;
00705 }
00706     
00707 NS_IMETHODIMP nsChildView::ModalEventFilter(PRBool aRealEvent, void *aEvent,
00708                                          PRBool *aForWindow)
00709 {
00710   if (aForWindow)
00711     *aForWindow = PR_FALSE;
00712   return NS_ERROR_NOT_IMPLEMENTED;
00713 }
00714 
00715 
00716 //
00717 // Enable
00718 //
00719 // Enable/disable this view
00720 //
00721 NS_IMETHODIMP nsChildView::Enable(PRBool aState)
00722 {
00723   // unimplemented;
00724   return NS_OK;
00725 }
00726 
00727 
00728 NS_IMETHODIMP nsChildView::IsEnabled(PRBool *aState)
00729 {
00730   // unimplemented
00731   if (aState)
00732    *aState = PR_TRUE;
00733   return NS_OK;
00734 }
00735 
00736 //-------------------------------------------------------------------------
00737 //
00738 // Set the focus on this component
00739 //
00740 //-------------------------------------------------------------------------
00741 NS_IMETHODIMP nsChildView::SetFocus(PRBool aRaise)
00742 {
00743   NSWindow* window = [mView window];
00744   if (window)
00745     [window makeFirstResponder: mView];
00746   return NS_OK;
00747 }
00748 
00749 //-------------------------------------------------------------------------
00750 //
00751 // Get this component font
00752 //
00753 //-------------------------------------------------------------------------
00754 nsIFontMetrics* nsChildView::GetFont(void)
00755 {
00756   return mFontMetrics;
00757 }
00758 
00759     
00760 //-------------------------------------------------------------------------
00761 //
00762 // Set this component font
00763 //
00764 //-------------------------------------------------------------------------
00765 NS_IMETHODIMP nsChildView::SetFont(const nsFont &aFont)
00766 {
00767   NS_IF_RELEASE(mFontMetrics);
00768   if (mContext)
00769     mContext->GetMetricsFor(aFont, mFontMetrics);
00770   return NS_OK;
00771 }
00772 
00773 
00774 //-------------------------------------------------------------------------
00775 //
00776 // Set the colormap of the window
00777 //
00778 //-------------------------------------------------------------------------
00779 NS_IMETHODIMP nsChildView::SetColorMap(nsColorMap *aColorMap)
00780 {
00781   return NS_OK;
00782 }
00783 
00784 
00785 //
00786 // SetMenuBar
00787 // ShowMenuBar
00788 // GetMenuBar
00789 //
00790 // Meaningless in this context. A subview doesn't have a menubar.
00791 //
00792 
00793 NS_IMETHODIMP nsChildView::SetMenuBar(nsIMenuBar * aMenuBar)
00794 {
00795   return NS_ERROR_FAILURE;
00796 }
00797 
00798 NS_IMETHODIMP nsChildView::ShowMenuBar(PRBool aShow)
00799 {
00800   return NS_ERROR_FAILURE;
00801 }
00802 
00803 nsIMenuBar* nsChildView::GetMenuBar()
00804 {
00805   return nsnull;
00806 }
00807 
00808 
00809 //
00810 // SetCursor
00811 //
00812 // Override to set the cursor on the mac
00813 //
00814 NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor)
00815 {
00816   nsBaseWidget::SetCursor(aCursor);
00817   [[nsCursorManager sharedInstance] setCursor: aCursor];
00818   return NS_OK;
00819 }
00820 
00821 // implement to fix "hidden virtual function" warning
00822 NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor,
00823                                       PRUint32 aHotspotX, PRUint32 aHotspotY)
00824 {
00825   return nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY);
00826 }
00827 
00828 
00829 #pragma mark -
00830 //-------------------------------------------------------------------------
00831 //
00832 // Get this component dimension
00833 //
00834 //-------------------------------------------------------------------------
00835 NS_IMETHODIMP nsChildView::GetBounds(nsRect &aRect)
00836 {
00837   aRect = mBounds;
00838   return NS_OK;
00839 }
00840 
00841 
00842 NS_METHOD nsChildView::SetBounds(const nsRect &aRect)
00843 {
00844   nsresult rv = Inherited::SetBounds(aRect);
00845   if ( NS_SUCCEEDED(rv) ) {
00846     //CalcWindowRegions();
00847     NSRect r;
00848     ConvertGeckoToCocoaRect(aRect, r);
00849     [mView setFrame:r];
00850   }
00851 
00852   return rv;
00853 }
00854 
00855 
00856 NS_IMETHODIMP nsChildView::ConstrainPosition(PRBool aAllowSlop,
00857                                              PRInt32 *aX, PRInt32 *aY)
00858 {
00859   return NS_OK;
00860 }
00861 
00862 //-------------------------------------------------------------------------
00863 //
00864 // Move this component
00865 // aX and aY are in the parent widget coordinate system
00866 //-------------------------------------------------------------------------
00867 NS_IMETHODIMP nsChildView::Move(PRInt32 aX, PRInt32 aY)
00868 {
00869   return MoveWithRepaintOption(aX, aY, PR_TRUE);
00870 }
00871 
00872 NS_IMETHODIMP nsChildView::MoveWithRepaintOption(PRInt32 aX, PRInt32 aY, PRBool aRepaint)
00873 {
00874   if ((mBounds.x != aX) || (mBounds.y != aY))
00875   {
00876     // Invalidate the current location
00877     if (mVisible && aRepaint)
00878       [[mView superview] setNeedsDisplayInRect: [mView frame]];    //XXX needed?
00879         
00880     // Set the bounds
00881     mBounds.x = aX;
00882     mBounds.y = aY;
00883    
00884     NSRect r;
00885     ConvertGeckoToCocoaRect(mBounds, r);
00886     [mView setFrame:r];
00887 
00888     if (mVisible && aRepaint)
00889       [mView setNeedsDisplay:YES];
00890 
00891     
00892     // Report the event
00893     ReportMoveEvent();
00894   }
00895   return NS_OK;
00896 }
00897 
00898 //-------------------------------------------------------------------------
00899 //
00900 // Resize this component
00901 //
00902 //-------------------------------------------------------------------------
00903 NS_IMETHODIMP nsChildView::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
00904 {
00905   if ((mBounds.width != aWidth) || (mBounds.height != aHeight))
00906   {
00907     // Set the bounds
00908     mBounds.width  = aWidth;
00909     mBounds.height = aHeight;
00910 
00911     if (mVisible && aRepaint)
00912       [[mView superview] setNeedsDisplayInRect: [mView frame]];    //XXX needed?
00913     
00914     NSRect r;
00915     ConvertGeckoToCocoaRect(mBounds, r);
00916     [mView setFrame:r];
00917 
00918     if (mVisible && aRepaint)
00919       [mView setNeedsDisplay:YES];
00920 
00921     // Report the event
00922     ReportSizeEvent();
00923   }
00924  
00925   return NS_OK;
00926 }
00927 
00928 //-------------------------------------------------------------------------
00929 //
00930 // Resize this component
00931 //
00932 //-------------------------------------------------------------------------
00933 NS_IMETHODIMP nsChildView::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
00934 {
00935   MoveWithRepaintOption(aX, aY, aRepaint);
00936   Resize(aWidth, aHeight, aRepaint);
00937   return NS_OK;
00938 }
00939 
00940 
00941 //
00942 // GetPreferredSize
00943 // SetPreferredSize
00944 //
00945 // Nobody even calls these aywhere in the code
00946 //
00947 NS_METHOD nsChildView::GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight)
00948 {
00949   return NS_ERROR_FAILURE;
00950 }
00951 
00952 NS_METHOD nsChildView::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight)
00953 {
00954   return NS_ERROR_FAILURE;
00955 }
00956 
00957 
00958 //-------------------------------------------------------------------------
00959 // 
00960 //
00961 //-------------------------------------------------------------------------
00962 NS_IMETHODIMP nsChildView::BeginResizingChildren(void)
00963 {
00964   return NS_OK;
00965 }
00966 
00967 //-------------------------------------------------------------------------
00968 // 
00969 //
00970 //-------------------------------------------------------------------------
00971 NS_IMETHODIMP nsChildView::EndResizingChildren(void)
00972 {
00973   return NS_OK;
00974 }
00975 
00976 //-------------------------------------------------------------------------
00977 // 
00978 //
00979 //-------------------------------------------------------------------------
00980 NS_IMETHODIMP nsChildView::GetPluginClipRect(nsRect& outClipRect, nsPoint& outOrigin, PRBool& outWidgetVisible)
00981 {
00982   NS_ASSERTION(mPluginPort, "GetPluginClipRect must only be called on a plugin widget");
00983   if (!mPluginPort) return NS_ERROR_FAILURE;
00984   
00985   NSWindow* window = [mView getNativeWindow];
00986   if (!window) return NS_ERROR_FAILURE;
00987   
00988   NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
00989   NSRect frame = [[window contentView] frame];
00990   viewOrigin.y = frame.size.height - viewOrigin.y;
00991   
00992   // set up the clipping region for plugins.
00993   NSRect visibleBounds = [mView visibleRect];
00994   NSPoint clipOrigin   = [mView convertPoint:visibleBounds.origin toView:nil];
00995   
00996   // Convert from cocoa to QuickDraw coordinates
00997   clipOrigin.y = frame.size.height - clipOrigin.y;
00998   
00999   outClipRect.x      = (nscoord)clipOrigin.x;
01000   outClipRect.y      = (nscoord)clipOrigin.y;
01001   
01002   if ([mView window] != nil)
01003   {
01004     outClipRect.width  = (nscoord)visibleBounds.size.width;
01005     outClipRect.height = (nscoord)visibleBounds.size.height;
01006     outWidgetVisible = PR_TRUE;
01007   }
01008   else
01009   {
01010     outClipRect.width = 0;
01011     outClipRect.height = 0;
01012     outWidgetVisible = PR_FALSE;
01013   }
01014 
01015   // need to convert view's origin to window coordinates.
01016   // then, encode as "SetOrigin" ready values.
01017   outOrigin.x = (nscoord)-viewOrigin.x;
01018   outOrigin.y = (nscoord)-viewOrigin.y;
01019   
01020   return NS_OK;
01021 }
01022 
01023 
01024 //-------------------------------------------------------------------------
01025 // 
01026 //
01027 //-------------------------------------------------------------------------
01028 NS_IMETHODIMP nsChildView::StartDrawPlugin()
01029 {
01030   NS_ASSERTION(mPluginPort, "StartDrawPlugin must only be called on a plugin widget");
01031   if (!mPluginPort)
01032     return NS_ERROR_FAILURE;
01033   
01034   // prevent reentrant drawing
01035   if (mPluginDrawing)
01036     return NS_ERROR_FAILURE;
01037 
01038   NSWindow* window = [mView getNativeWindow];
01039   if (!window) return NS_ERROR_FAILURE;
01040 
01041   if (window /* [mView lockFocusIfCanDraw] */)
01042   {
01043     // It appears that the WindowRef from which we get the plugin port undergoes the
01044     // traditional BeginUpdate/EndUpdate cycle, which, if you recall, sets the visible
01045     // region to the intersection of the visible region and the update region. Since
01046     // we don't know here if we're being drawn inside a BeginUpdate/EndUpdate pair
01047     // (which seem to occur in [NSWindow display]), and we don't want to have the burden
01048     // of correctly doing Carbon invalidates of the plugin rect, we manually set the
01049     // visible region to be the entire port every time.
01050     RgnHandle pluginRegion = ::NewRgn();
01051     if (pluginRegion)
01052     {
01053       StPortSetter setter(mPluginPort->port);
01054       ::SetOrigin(0, 0);
01055 
01056       nsRect  clipRect;   // this is in native window coordinates
01057       nsPoint origin;
01058       PRBool visible;
01059       GetPluginClipRect(clipRect, origin, visible);
01060       
01061       // XXX if we're not visible, set an empty clip region?
01062       Rect pluginRect;
01063       ConvertGeckoRectToMacRect(clipRect, pluginRect);
01064       
01065       ::RectRgn(pluginRegion, &pluginRect);
01066       ::SetPortVisibleRegion(mPluginPort->port, pluginRegion);
01067       ::SetPortClipRegion(mPluginPort->port, pluginRegion);
01068       
01069       // now set up the origin for the plugin
01070       ::SetOrigin(origin.x, origin.y);
01071 
01072       ::DisposeRgn(pluginRegion);
01073     }
01074 
01075     mPluginDrawing = PR_TRUE;
01076     return NS_OK;
01077   }
01078   
01079   NS_ASSERTION(0, "lockFocusIfCanDraw returned false\n");
01080   return NS_ERROR_FAILURE;
01081 }
01082 
01083 //-------------------------------------------------------------------------
01084 // 
01085 //
01086 //-------------------------------------------------------------------------
01087 NS_IMETHODIMP nsChildView::EndDrawPlugin()
01088 {
01089   NS_ASSERTION(mPluginPort, "EndDrawPlugin must only be called on a plugin widget");
01090   //[mView unlockFocus];
01091   mPluginDrawing = PR_FALSE;
01092   return NS_OK;
01093 }
01094 
01095 //-------------------------------------------------------------------------
01096 // 
01097 //
01098 //-------------------------------------------------------------------------
01099 void nsChildView::LiveResizeStarted()
01100 {
01101   // XXX todo. Use this to disable Java async redraw during resize
01102   mLiveResizeInProgress = PR_TRUE;
01103 }
01104 
01105 //-------------------------------------------------------------------------
01106 // 
01107 //
01108 //-------------------------------------------------------------------------
01109 void nsChildView::LiveResizeEnded()
01110 {
01111   mLiveResizeInProgress = PR_FALSE;
01112 }
01113 
01114 
01115 #pragma mark -
01116 
01117 #if defined(INVALIDATE_DEBUGGING) || defined(PAINT_DEBUGGING)
01118 
01119 static Boolean KeyDown(const UInt8 theKey)
01120 {
01121   KeyMap map;
01122   GetKeys(map);
01123   return ((*((UInt8 *)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0;
01124 }
01125 
01126 static Boolean caps_lock()
01127 {
01128   return KeyDown(0x39);
01129 }
01130 
01131 static void blinkRect(Rect* r)
01132 {
01133   StRegionFromPool oldClip;
01134   if (oldClip != NULL)
01135     ::GetClip(oldClip);
01136 
01137   ::ClipRect(r);
01138   ::InvertRect(r);
01139   UInt32 end = ::TickCount() + 5;
01140   while (::TickCount() < end) ;
01141   ::InvertRect(r);
01142 
01143   if (oldClip != NULL)
01144     ::SetClip(oldClip);
01145 }
01146 
01147 static void blinkRgn(RgnHandle rgn)
01148 {
01149   StRegionFromPool oldClip;
01150   if (oldClip != NULL)
01151     ::GetClip(oldClip);
01152 
01153   ::SetClip(rgn);
01154   ::InvertRgn(rgn);
01155   UInt32 end = ::TickCount() + 5;
01156   while (::TickCount() < end) ;
01157   ::InvertRgn(rgn);
01158 
01159   if (oldClip != NULL)
01160     ::SetClip(oldClip);
01161 }
01162 
01163 #endif
01164 
01165 //-------------------------------------------------------------------------
01166 //
01167 // Invalidate this component's visible area
01168 //
01169 //-------------------------------------------------------------------------
01170 NS_IMETHODIMP nsChildView::Invalidate(PRBool aIsSynchronous)
01171 {
01172   if (!mView || !mVisible)
01173     return NS_OK;
01174 
01175   if (aIsSynchronous)
01176     [mView display];
01177   else if ([NSView focusView])
01178   {
01179     // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
01180     // don't lose it.
01181     [mView setNeedsPendingDisplay];
01182   }
01183   else
01184     [mView setNeedsDisplay:YES];
01185 
01186   return NS_OK;
01187 }
01188 
01189 //-------------------------------------------------------------------------
01190 //
01191 // Invalidate this component's visible area
01192 //
01193 //-------------------------------------------------------------------------
01194 NS_IMETHODIMP nsChildView::Invalidate(const nsRect &aRect, PRBool aIsSynchronous)
01195 {
01196   if (!mView || !mVisible)
01197     return NS_OK;
01198 
01199   NSRect r;
01200   ConvertGeckoToCocoaRect ( aRect, r );
01201   
01202   if (aIsSynchronous)
01203     [mView displayRect:r];
01204   else if ([NSView focusView])
01205   {
01206     // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
01207     // don't lose it.
01208     [mView setNeedsPendingDisplayInRect:r];
01209   }
01210   else
01211     [mView setNeedsDisplayInRect:r];
01212   
01213   return NS_OK;
01214 }
01215 
01216 
01217 //-------------------------------------------------------------------------
01218 //
01219 // Validate the widget
01220 //
01221 //-------------------------------------------------------------------------
01222 NS_IMETHODIMP nsChildView::Validate()
01223 {
01224   [mView setNeedsDisplay:NO];
01225   return NS_OK;
01226 }
01227 
01228 
01229 //-------------------------------------------------------------------------
01230 //
01231 // Invalidate this component's visible area
01232 //
01233 //-------------------------------------------------------------------------
01234 
01235 NS_IMETHODIMP nsChildView::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous)
01236 {
01237   if ( !mView || !mVisible)
01238     return NS_OK;
01239     
01240 //FIXME rewrite to use a Cocoa region when nsIRegion isn't a QD Region
01241   NSRect r;
01242   nsRect bounds;
01243   nsIRegion* region = NS_CONST_CAST(nsIRegion*, aRegion);     // ugh. this method should be const
01244   region->GetBoundingBox ( &bounds.x, &bounds.y, &bounds.width, &bounds.height );
01245   ConvertGeckoToCocoaRect(bounds, r);
01246   
01247   if ( aIsSynchronous )
01248     [mView displayRect:r];
01249   else
01250     [mView setNeedsDisplayInRect:r];
01251 
01252   return NS_OK;
01253 }
01254 
01255 inline PRUint16 COLOR8TOCOLOR16(PRUint8 color8)
01256 {
01257   // return (color8 == 0xFF ? 0xFFFF : (color8 << 8));
01258   return (color8 << 8) | color8;  /* (color8 * 257) == (color8 * 0x0101) */
01259 }
01260 
01261 //-------------------------------------------------------------------------
01262 //  StartDraw
01263 //
01264 //-------------------------------------------------------------------------
01265 void nsChildView::StartDraw(nsIRenderingContext* aRenderingContext)
01266 {
01267   if (mDrawing)
01268     return;
01269   mDrawing = PR_TRUE;
01270 
01271   if (aRenderingContext == nsnull)
01272   {
01273     // make sure we have a rendering context
01274     mTempRenderingContext = GetRenderingContext();
01275     mTempRenderingContextMadeHere = PR_TRUE;
01276   }
01277   else
01278   {
01279     // if we already have a rendering context, save its state
01280     NS_IF_ADDREF(aRenderingContext);
01281     mTempRenderingContext = aRenderingContext;
01282     mTempRenderingContextMadeHere = PR_FALSE;
01283     mTempRenderingContext->PushState();
01284 
01285     // set the environment to the current widget
01286     mTempRenderingContext->Init(mContext, this);
01287   }
01288 
01289   // set the widget font. nsMacControl implements SetFont, which is where
01290   // the font should get set.
01291   if (mFontMetrics)
01292   {
01293     mTempRenderingContext->SetFont(mFontMetrics);
01294   }
01295 
01296   // set the widget background and foreground colors
01297   nscolor color = GetBackgroundColor();
01298   RGBColor macColor;
01299   macColor.red   = COLOR8TOCOLOR16(NS_GET_R(color));
01300   macColor.green = COLOR8TOCOLOR16(NS_GET_G(color));
01301   macColor.blue  = COLOR8TOCOLOR16(NS_GET_B(color));
01302   ::RGBBackColor(&macColor);
01303 
01304   color = GetForegroundColor();
01305   macColor.red   = COLOR8TOCOLOR16(NS_GET_R(color));
01306   macColor.green = COLOR8TOCOLOR16(NS_GET_G(color));
01307   macColor.blue  = COLOR8TOCOLOR16(NS_GET_B(color));
01308   ::RGBForeColor(&macColor);
01309 
01310   mTempRenderingContext->SetColor(color);       // just in case, set the rendering context color too
01311 }
01312 
01313 
01314 //-------------------------------------------------------------------------
01315 //  EndDraw
01316 //
01317 //-------------------------------------------------------------------------
01318 void nsChildView::EndDraw()
01319 {
01320   if (! mDrawing)
01321     return;
01322   mDrawing = PR_FALSE;
01323 
01324   if (mTempRenderingContextMadeHere)
01325     mTempRenderingContext->PopState();
01326   NS_RELEASE(mTempRenderingContext);
01327 }
01328 
01329 
01330 //-------------------------------------------------------------------------
01331 //
01332 //
01333 //-------------------------------------------------------------------------
01334 void
01335 nsChildView::Flash(nsPaintEvent &aEvent)
01336 {
01337 #if DEBUG
01338   Rect flashRect;
01339   if (debug_WantPaintFlashing() && aEvent.rect ) {
01340     ::SetRect ( &flashRect, aEvent.rect->x, aEvent.rect->y, aEvent.rect->x + aEvent.rect->width,
01341             aEvent.rect->y + aEvent.rect->height );
01342     StPortSetter portSetter(GetQuickDrawPort());
01343     unsigned long endTicks;
01344     ::InvertRect ( &flashRect );
01345     ::Delay(10, &endTicks);
01346     ::InvertRect ( &flashRect );
01347   }
01348 #endif
01349 }
01350 
01351 
01352 //
01353 // OnPaint
01354 //
01355 // Dummy impl, meant to be overridden
01356 //
01357 PRBool
01358 nsChildView::OnPaint(nsPaintEvent &event)
01359 {
01360   return PR_TRUE;
01361 }
01362 
01363 
01364 //
01365 // Update
01366 //
01367 // this is handled for us by UpdateWidget
01368 // 
01369 NS_IMETHODIMP nsChildView::Update()
01370 {
01371   // Update means "Flush any pending changes right now."  It does *not* mean
01372   // repaint the world. :) -- dwh
01373   [mView displayIfNeeded];
01374   return NS_OK;
01375 }
01376 
01377 
01378 #pragma mark -
01379 
01380 
01381 //
01382 // UpdateWidget
01383 //
01384 // Dispatches the Paint event into Gecko. Usually called from our 
01385 // NSView in response to the display system noticing that something
01386 // needs repainting. We don't have to worry about painting our child views
01387 // because the display system will take care of that for us.
01388 //
01389 void 
01390 nsChildView::UpdateWidget(nsRect& aRect, nsIRenderingContext* aContext)
01391 {
01392   if (! mVisible)
01393     return;
01394   
01395   // For updating widgets, we _always_ want to use the NSQuickDrawView's port,
01396   // since that's the correct port for gecko to use to make rendering contexts.
01397   // The plugin is the only thing that uses the plugin port.
01398   GrafPtr curPort = GetChildViewQuickDrawPort();
01399   if (!curPort) return;
01400 
01401   StPortSetter port(curPort);
01402   
01403   // initialize the paint event
01404   nsPaintEvent paintEvent(PR_TRUE, NS_PAINT, this);
01405   paintEvent.renderingContext = aContext;       // nsPaintEvent
01406   paintEvent.rect             = &aRect;
01407 
01408   // offscreen drawing is pointless.
01409   if (paintEvent.rect->x < 0)
01410     paintEvent.rect->x = 0;
01411   if (paintEvent.rect->y < 0)
01412     paintEvent.rect->y = 0;
01413     
01414   // draw the widget
01415   StartDraw(aContext);
01416   if ( OnPaint(paintEvent) ) {
01417     nsEventStatus eventStatus;
01418     DispatchWindowEvent(paintEvent,eventStatus);
01419     if(eventStatus != nsEventStatus_eIgnore)
01420       Flash(paintEvent);
01421   }
01422   EndDraw();
01423 }
01424 
01425 
01426 //
01427 // Scroll
01428 //
01429 // Scroll the bits of a view and its children
01430 //
01431 // FIXME: I'm sure the invalidating can be optimized, just no time now.
01432 //
01433 NS_IMETHODIMP nsChildView::Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect)
01434 {
01435   BOOL viewWasDirty = NO;
01436   if (mVisible)
01437   {
01438     viewWasDirty = [mView needsDisplay];
01439 
01440     NSSize scrollVector = {aDx,aDy};
01441     [mView scrollRect: [mView visibleRect] by:scrollVector];
01442   }
01443   
01444   // Scroll the children (even if the widget is not visible)
01445   for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
01446     // We use resize rather than move since it gives us control
01447     // over repainting.  In the case of blitting, Quickdraw views
01448     // draw their child widgets on the blit, so we can scroll
01449     // like a bat out of hell by not wasting time invalidating
01450     // the widgets, since it's completely unnecessary to do so.
01451     nsRect bounds;
01452     kid->GetBounds(bounds);
01453     kid->Resize(bounds.x + aDx, bounds.y + aDy, bounds.width, bounds.height, PR_FALSE);
01454   }
01455 
01456   if (mVisible)
01457   {
01458     if (viewWasDirty)
01459     {
01460       [mView setNeedsDisplay:YES];
01461     }
01462     else
01463     {
01464       NSRect frame = [mView visibleRect];
01465       NSRect horizInvalid = frame;
01466       NSRect vertInvalid = frame;
01467   
01468       if (aDx != 0) {
01469         horizInvalid.size.width = abs(aDx);
01470         if (aDx < 0)
01471           horizInvalid.origin.x = frame.origin.x + frame.size.width + aDx;
01472         [mView setNeedsDisplayInRect: horizInvalid];
01473       }
01474 
01475       if (aDy != 0) {
01476         vertInvalid.size.height = abs(aDy);
01477         if (aDy < 0)
01478           vertInvalid.origin.y = frame.origin.y + frame.size.height + aDy;
01479         [mView setNeedsDisplayInRect: vertInvalid];
01480       }
01481 
01482       // We also need to check for any ChildViews which overlap this widget
01483       // but are not descendent widgets.  If there are any, we need to
01484       // invalidate the area of this view that these ChildViews will have been
01485       // blitted into, since these widgets aren't supposed to scroll with
01486       // this widget.
01487 
01488       // To do this, start at the root Gecko NSView, and walk down along
01489       // our ancestor view chain, looking at all the subviews in each level
01490       // of the hierarchy.  If we find a non-ancestor view that overlaps
01491       // this view, invalidate the area around it.
01492 
01493       // We need to convert all rects to a common ancestor view to intersect
01494       // them, since a view's frame is in the coordinate space of its parent.
01495       // Use mParentView as the frame of reference.
01496       NSRect selfFrame = [mParentView convertRect:[mView frame] fromView:[mView superview]];
01497       NSView* view = mParentView;
01498       BOOL selfLevel = NO;
01499 
01500       while (!selfLevel) {
01501         NSView* nextAncestorView;
01502         NSArray* subviews = [view subviews];
01503         for (unsigned int i = 0; i < [subviews count]; ++i) {
01504           NSView* subView = [subviews objectAtIndex: i];
01505           if (subView == mView)
01506             selfLevel = YES;
01507           else if ([mView isDescendantOf:subView])
01508             nextAncestorView = subView;
01509           else {
01510             NSRect intersectArea = NSIntersectionRect([mParentView convertRect:[subView frame] fromView:[subView superview]], selfFrame);
01511             if (!NSIsEmptyRect(intersectArea)) {
01512               NSPoint origin = [mView convertPoint:intersectArea.origin fromView:mParentView];
01513 
01514               if (aDy != 0) {
01515                 vertInvalid.origin.x = origin.x;
01516                 if (aDy < 0)  // scrolled down, invalidate above
01517                   vertInvalid.origin.y = origin.y + aDy;
01518                 else          // invalidate below
01519                   vertInvalid.origin.y = origin.y + intersectArea.size.height;
01520                 vertInvalid.size.width = intersectArea.size.width;
01521                 [mView setNeedsDisplayInRect: vertInvalid];
01522               }
01523 
01524               if (aDx != 0) {
01525                 horizInvalid.origin.y = origin.y;
01526                 if (aDx < 0)  // scrolled right, invalidate to the left
01527                   horizInvalid.origin.x = origin.x + aDx;
01528                 else          // invalidate to the right
01529                   horizInvalid.origin.x = origin.x + intersectArea.size.width;
01530                 horizInvalid.size.height = intersectArea.size.height;
01531                 [mView setNeedsDisplayInRect: horizInvalid];
01532               }
01533             }
01534           }
01535         }
01536         view = nextAncestorView;
01537       }
01538     }
01539   }
01540   
01541   // This is an evil hack that doesn't always work.
01542   // 
01543   // Drawing plugins in a Cocoa environment is tricky, because the
01544   // plugins are living in a Carbon WindowRef/BeginUpdate/EndUpdate
01545   // world, and Cocoa has its own notion of dirty rectangles. Throw
01546   // Quartz Extreme and QuickTime into the mix, and things get bad.
01547   // 
01548   // This code is working around a cosmetic issue seen when Quartz Extreme
01549   // is active, and you're scrolling a page with a QuickTime plugin; areas
01550   // outside the plugin fail to scroll properly. This [display] ensures that
01551   // the view is properly drawn before the next Scroll call.
01552   //
01553   // The time this doesn't work is when you're scrolling a page containing
01554   // an iframe which in turn contains a plugin.
01555   //
01556   // This is turned off because it makes scrolling pages with plugins slow.
01557   // 
01558   //if ([mView childViewHasPlugin])
01559   //  [mView display];
01560 
01561   return NS_OK;
01562 }
01563 
01564 
01565 //-------------------------------------------------------------------------
01566 //
01567 //
01568 //-------------------------------------------------------------------------
01569 
01570 PRBool nsChildView::ConvertStatus(nsEventStatus aStatus)
01571 {
01572   switch (aStatus)
01573   {
01574     case nsEventStatus_eIgnore:             return(PR_FALSE);
01575     case nsEventStatus_eConsumeNoDefault:   return(PR_TRUE);  // don't do default processing
01576     case nsEventStatus_eConsumeDoDefault:   return(PR_FALSE);
01577     default:
01578       NS_ASSERTION(0, "Illegal nsEventStatus enumeration value");
01579       break;
01580   }
01581   return(PR_FALSE);
01582 }
01583 
01584 //-------------------------------------------------------------------------
01585 //
01586 // Invokes callback and  ProcessEvent method on Event Listener object
01587 //
01588 //-------------------------------------------------------------------------
01589 NS_IMETHODIMP nsChildView::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
01590 {
01591   aStatus = nsEventStatus_eIgnore;
01592   if (! mDestructorCalled)
01593   {
01594     nsIWidget* aWidget = event->widget;
01595     NS_IF_ADDREF(aWidget);
01596     
01597     if (nsnull != mMenuListener){
01598       if (NS_MENU_EVENT == event->eventStructType)
01599         aStatus = mMenuListener->MenuSelected( static_cast<nsMenuEvent&>(*event) );
01600     }
01601     if (mEventCallback)
01602       aStatus = (*mEventCallback)(event);
01603 
01604     // Dispatch to event listener if event was not consumed
01605     if ((aStatus != nsEventStatus_eConsumeNoDefault) && (mEventListener != nsnull))
01606       aStatus = mEventListener->ProcessEvent(*event);
01607 
01608     NS_IF_RELEASE(aWidget);
01609   }
01610   return NS_OK;
01611 }
01612 
01613 //-------------------------------------------------------------------------
01614 PRBool nsChildView::DispatchWindowEvent(nsGUIEvent &event)
01615 {
01616   nsEventStatus status;
01617   DispatchEvent(&event, status);
01618   return ConvertStatus(status);
01619 }
01620 
01621 //-------------------------------------------------------------------------
01622 PRBool nsChildView::DispatchWindowEvent(nsGUIEvent &event,nsEventStatus &aStatus)
01623 {
01624   DispatchEvent(&event, aStatus);
01625   return ConvertStatus(aStatus);
01626 }
01627 
01628 //-------------------------------------------------------------------------
01629 //
01630 // Deal with all sort of mouse event
01631 //
01632 //-------------------------------------------------------------------------
01633 PRBool nsChildView::DispatchMouseEvent(nsMouseEvent &aEvent)
01634 {
01635 
01636   PRBool result = PR_FALSE;
01637   if (nsnull == mEventCallback && nsnull == mMouseListener) {
01638     return result;
01639   }
01640 
01641   // call the event callback 
01642   if (nsnull != mEventCallback) 
01643     {
01644     result = (DispatchWindowEvent(aEvent));
01645     return result;
01646     }
01647 
01648   if (nsnull != mMouseListener) {
01649     switch (aEvent.message) {
01650       case NS_MOUSE_MOVE: {
01651         result = ConvertStatus(mMouseListener->MouseMoved(aEvent));
01652         nsRect rect;
01653         GetBounds(rect);
01654         if (rect.Contains(aEvent.point.x, aEvent.point.y)) 
01655           {
01656           //if (mWindowPtr == NULL || mWindowPtr != this) 
01657             //{
01658             // printf("Mouse enter");
01659             //mCurrentWindow = this;
01660             //}
01661           } 
01662         else 
01663           {
01664           // printf("Mouse exit");
01665           }
01666 
01667       } break;
01668 
01669       case NS_MOUSE_LEFT_BUTTON_DOWN:
01670       case NS_MOUSE_MIDDLE_BUTTON_DOWN:
01671       case NS_MOUSE_RIGHT_BUTTON_DOWN:
01672         result = ConvertStatus(mMouseListener->MousePressed(aEvent));
01673         break;
01674 
01675       case NS_MOUSE_LEFT_BUTTON_UP:
01676       case NS_MOUSE_MIDDLE_BUTTON_UP:
01677       case NS_MOUSE_RIGHT_BUTTON_UP:
01678         result = ConvertStatus(mMouseListener->MouseReleased(aEvent));
01679         result = ConvertStatus(mMouseListener->MouseClicked(aEvent));
01680         break;
01681     } // switch
01682   } 
01683   return result;
01684 }
01685 
01686 #pragma mark -
01687 
01688 //-------------------------------------------------------------------------
01689 //
01690 //
01691 //-------------------------------------------------------------------------
01692 PRBool nsChildView::ReportDestroyEvent()
01693 {
01694   // nsEvent
01695   nsGUIEvent event(PR_TRUE, NS_DESTROY, this);
01696   event.time        = PR_IntervalNow();
01697 
01698   // dispatch event
01699   return (DispatchWindowEvent(event));
01700 }
01701 
01702 //-------------------------------------------------------------------------
01703 //
01704 //
01705 //-------------------------------------------------------------------------
01706 PRBool nsChildView::ReportMoveEvent()
01707 {
01708   // nsEvent
01709   nsGUIEvent moveEvent(PR_TRUE, NS_MOVE, this);
01710   moveEvent.point.x     = mBounds.x;
01711   moveEvent.point.y     = mBounds.y;
01712   moveEvent.time        = PR_IntervalNow();
01713 
01714   // dispatch event
01715   return (DispatchWindowEvent(moveEvent));
01716 }
01717 
01718 //-------------------------------------------------------------------------
01719 //
01720 //
01721 //-------------------------------------------------------------------------
01722 PRBool nsChildView::ReportSizeEvent()
01723 {
01724   // nsEvent
01725   nsSizeEvent sizeEvent(PR_TRUE, NS_SIZE, this);
01726   sizeEvent.time        = PR_IntervalNow();
01727 
01728   // nsSizeEvent
01729   sizeEvent.windowSize  = &mBounds;
01730   sizeEvent.mWinWidth   = mBounds.width;
01731   sizeEvent.mWinHeight  = mBounds.height;
01732   
01733   // dispatch event
01734   return(DispatchWindowEvent(sizeEvent));
01735 }
01736 
01737 
01738 
01739 #pragma mark -
01740 
01741 
01742 //-------------------------------------------------------------------------
01743 //
01744 //
01745 //-------------------------------------------------------------------------
01746 void nsChildView::CalcWindowRegions()
01747 {
01748   // i don't think this is necessary anymore...
01749 }
01750 
01751 
01752 
01753 //-------------------------------------------------------------------------
01754 /*  Calculate the x and y offsets for this particular widget
01755  *  @update  ps 09/22/98
01756  *  @param   aX -- x offset amount
01757  *  @param   aY -- y offset amount 
01758  *  @return  NOTHING
01759  */
01760  
01761 NS_IMETHODIMP nsChildView::CalcOffset(PRInt32 &aX,PRInt32 &aY)
01762 {
01763   aX = aY = 0;
01764   NSRect bounds = {{0, 0}, {0, 0}};
01765   bounds = [mView convertRect:bounds toView:nil];
01766   aX += NS_STATIC_CAST(PRInt32, bounds.origin.x);
01767   aY += NS_STATIC_CAST(PRInt32, bounds.origin.y);
01768 
01769   return NS_OK;
01770 }
01771 
01772 
01773 //-------------------------------------------------------------------------
01774 // PointInWidget
01775 //    Find if a point in local coordinates is inside this object
01776 //-------------------------------------------------------------------------
01777 PRBool nsChildView::PointInWidget(Point aThePoint)
01778 {
01779   // get the origin in local coordinates
01780   nsPoint widgetOrigin(0, 0);
01781   LocalToWindowCoordinate(widgetOrigin);
01782 
01783   // get rectangle relatively to the parent
01784   nsRect widgetRect;
01785   GetBounds(widgetRect);
01786 
01787   // convert the topLeft corner to local coordinates
01788   widgetRect.MoveBy(widgetOrigin.x, widgetOrigin.y);
01789 
01790   // finally tell whether it's a hit
01791   return widgetRect.Contains(aThePoint.h, aThePoint.v);
01792 }
01793 
01794 #pragma mark -
01795 
01796 
01797 //-------------------------------------------------------------------------
01798 // WidgetToScreen
01799 //    Convert the given rect to global coordinates.
01800 //    @param aLocalRect  -- rect in local coordinates of this widget
01801 //    @param aGlobalRect -- |aLocalRect| in global coordinates
01802 //-------------------------------------------------------------------------
01803 NS_IMETHODIMP nsChildView::WidgetToScreen(const nsRect& aLocalRect, nsRect& aGlobalRect)
01804 {
01805   NSRect temp;
01806   ConvertGeckoToCocoaRect(aLocalRect, temp);
01807   temp = [mView convertRect:temp toView:nil];                       // convert to window coords
01808   temp.origin = [[mView getNativeWindow] convertBaseToScreen:temp.origin];   // convert to screen coords
01809   
01810   // need to flip the point relative to the main screen
01811   if ([[NSScreen screens] count] > 0)   // paranoia
01812   {
01813     // "global" coords are relative to the upper left of the main screen,
01814     // which is the first screen in the array (not [NSScreen mainScreen]).
01815     NSRect mainScreenFrame = [[[NSScreen screens] objectAtIndex:0] frame];
01816     temp.origin.y = NSMaxY(mainScreenFrame) - temp.origin.y;
01817   }
01818   
01819   // If this is rect has a size (and is not simply a point), it is important to account 
01820   // for the fact that convertRect:toView:nil thought our passed-in point was in bottom-left 
01821   // coords. Thus, we subtract the rect's height, to get the top-left rect's origin 
01822   // where we want it.
01823   temp.origin.y -= temp.size.height;
01824 
01825   ConvertCocoaToGeckoRect(temp, aGlobalRect);
01826   
01827   return NS_OK;
01828 }
01829 
01830 
01831 
01832 //-------------------------------------------------------------------------
01833 // ScreenToWidget
01834 //    Convert the given rect to local coordinates.
01835 //    @param aGlobalRect  -- rect in screen coordinates 
01836 //    @param aLocalRect -- |aGlobalRect| in coordinates of this widget
01837 //-------------------------------------------------------------------------
01838 NS_IMETHODIMP nsChildView::ScreenToWidget(const nsRect& aGlobalRect, nsRect& aLocalRect)
01839 {
01840   NSRect temp;
01841   ConvertGeckoToCocoaRect(aGlobalRect, temp);
01842 
01843   // need to flip the point relative to the main screen
01844   if ([[NSScreen screens] count] > 0)   // paranoia
01845   {
01846     // "global" coords are relative to the upper left of the main screen,
01847     // which is the first screen in the array (not [NSScreen mainScreen]).
01848     NSRect mainScreenFrame = [[[NSScreen screens] objectAtIndex:0] frame];
01849     temp.origin.y = NSMaxY(mainScreenFrame) - temp.origin.y;
01850   }
01851 
01852   temp.origin = [[mView getNativeWindow] convertScreenToBase:temp.origin];   // convert to screen coords
01853   temp = [mView convertRect:temp fromView:nil];                     // convert to window coords
01854 
01855   ConvertCocoaToGeckoRect(temp, aLocalRect);
01856   
01857   return NS_OK;
01858 } 
01859 
01860 
01861 //=================================================================
01862 /*  Convert the coordinates to some device coordinates so GFX can draw.
01863  *  @update  dc 09/16/98
01864  *  @param   nscoord -- X coordinate to convert
01865  *  @param   nscoord -- Y coordinate to convert
01866  *  @return  NONE
01867  */
01868 void nsChildView::ConvertToDeviceCoordinates(nscoord &aX, nscoord &aY)
01869 {
01870   PRInt32 offX = 0, offY = 0;
01871   this->CalcOffset(offX,offY);
01872 
01873   aX += offX;
01874   aY += offY;
01875 }
01876 
01877 
01878 NS_IMETHODIMP nsChildView::CaptureRollupEvents(nsIRollupListener * aListener, 
01879                                             PRBool aDoCapture, 
01880                                             PRBool aConsumeRollupEvent)
01881 {
01882   if (aDoCapture) {
01883     NS_IF_RELEASE(gRollupListener);
01884     NS_IF_RELEASE(gRollupWidget);
01885     gRollupListener = aListener;
01886     NS_ADDREF(aListener);
01887     gRollupWidget = this;
01888     NS_ADDREF(this);
01889   } else {
01890     NS_IF_RELEASE(gRollupListener);
01891     //gRollupListener = nsnull;
01892     NS_IF_RELEASE(gRollupWidget);
01893   }
01894 
01895   return NS_OK;
01896 }
01897 
01898 
01899 NS_IMETHODIMP nsChildView::SetTitle(const nsAString& title)
01900 {
01901   // nothing to do here
01902   return NS_OK;
01903 }
01904 
01905 
01906 NS_IMETHODIMP nsChildView::GetAttention(PRInt32 aCycleCount)
01907 {
01908   [NSApp requestUserAttention:NSCriticalRequest];
01909   return NS_OK;
01910 }
01911 
01912 #pragma mark -
01913 
01914 //
01915 // Force Input Method Editor to commit the uncommited input
01916 // Note that this and other nsIKBStateControl methods don't necessarily
01917 // get called on the same ChildView that input is going through.
01918 //
01919 NS_IMETHODIMP nsChildView::ResetInputState()
01920 {
01921 #ifdef DEBUG_IME
01922   NSLog(@"**** ResetInputState");
01923 #endif
01924 
01925   NSInputManager *currentIM = [NSInputManager currentInputManager];
01926   
01927   // commit the current text
01928   [currentIM unmarkText];
01929 
01930   // and clear the input manager's string
01931   [currentIM markedTextAbandoned:mView];
01932   
01933   return NS_OK;
01934 }
01935 
01936 //
01937 // 'open' means that it can take non-ASCII chars
01938 //
01939 NS_IMETHODIMP nsChildView::SetIMEOpenState(PRBool aState)
01940 {
01941   return NS_ERROR_NOT_IMPLEMENTED;
01942 }
01943 
01944 //
01945 // 'open' means that it can take non-ASCII chars
01946 //
01947 NS_IMETHODIMP nsChildView::GetIMEOpenState(PRBool* aState)
01948 {
01949   return NS_ERROR_NOT_IMPLEMENTED;
01950 }
01951 
01952 //
01953 // Destruct and don't commit the IME composition string.
01954 //
01955 NS_IMETHODIMP nsChildView::CancelIMEComposition()
01956 {
01957 #ifdef DEBUG_IME
01958   NSLog(@"**** CancelIMEComposition");
01959 #endif
01960   NSInputManager *currentIM = [NSInputManager currentInputManager];
01961   [currentIM markedTextAbandoned:mView];
01962   
01963   return NS_OK;
01964 }
01965 
01966 
01967 //
01968 // GetQuickDrawPort
01969 //
01970 // Find a quickdraw port in which to draw (needed by GFX until it
01971 // is converted to Cocoa). This must be overridden if CreateCocoaView()
01972 // does not create something that inherits from NSQuickDrawView!
01973 //
01974 GrafPtr
01975 nsChildView::GetQuickDrawPort()
01976 {
01977   if (mPluginPort)
01978     return mPluginPort->port;
01979 
01980   return GetChildViewQuickDrawPort();
01981 }
01982 
01983 GrafPtr
01984 nsChildView::GetChildViewQuickDrawPort()
01985 {
01986   if ([mView isKindOfClass:[ChildView class]])
01987     return (GrafPtr)[(ChildView*)mView qdPort];
01988 
01989   return nsnull;
01990 }
01991 
01992 #pragma mark -
01993 
01994 
01995 //
01996 // DispatchEvent
01997 //
01998 // Handle an event coming into us and send it to gecko.
01999 //
02000 NS_IMETHODIMP
02001 nsChildView::DispatchEvent ( void* anEvent, PRBool *_retval )
02002 {
02003   return NS_OK;
02004 }
02005 
02006 
02007 //
02008 // DragEvent
02009 //
02010 // The drag manager has let us know that something related to a drag has
02011 // occurred in this window. It could be any number of things, ranging from 
02012 // a drop, to a drag enter/leave, or a drag over event. The actual event
02013 // is passed in |aMessage| and is passed along to our event hanlder so Gecko
02014 // knows about it.
02015 //
02016 NS_IMETHODIMP
02017 nsChildView::DragEvent(PRUint32 aMessage, PRInt16 aMouseGlobalX, PRInt16 aMouseGlobalY,
02018                          PRUint16 aKeyModifiers, PRBool *_retval)
02019 {
02020   // ensure that this is going to a ChildView (not something else like a
02021   // scrollbar). I think it's safe to just bail at this point if it's not
02022   // what we expect it to be
02023   if (![mView isKindOfClass:[ChildView class]]) {
02024     *_retval = PR_FALSE;
02025     return NS_OK;
02026   }
02027   
02028   nsMouseEvent geckoEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
02029   
02030   // we're given the point in global coordinates. We need to convert it to
02031   // window coordinates for convert:message:toGeckoEvent
02032 
02033   NSPoint dragLoc = NSMakePoint(aMouseGlobalX, aMouseGlobalY);
02034 
02035   // need to flip the point relative to the main screen
02036   if ([[NSScreen screens] count] > 0)   // paranoia
02037   {
02038     // "global" coords are relative to the upper left of the main screen,
02039     // which is the first screen in the array (not [NSScreen mainScreen]).
02040     NSRect mainScreenFrame = [[[NSScreen screens] objectAtIndex:0] frame];
02041     dragLoc.y = NSMaxY(mainScreenFrame) - dragLoc.y;
02042   }
02043 
02044   // convert to window coords
02045   dragLoc = [[mView window] convertScreenToBase:dragLoc];
02046   // and fill in the event
02047   [(ChildView*)mView convertLocation:dragLoc message:aMessage modifiers:0 toGeckoEvent:&geckoEvent];
02048 
02049   DispatchWindowEvent(geckoEvent);
02050   
02051   // we handled the event
02052   *_retval = PR_TRUE;
02053   return NS_OK;
02054 }
02055 
02056 
02057 //
02058 // Scroll
02059 //
02060 // The cocoa view calls DispatchWindowEvent() directly, so no need for this
02061 //
02062 NS_IMETHODIMP
02063 nsChildView::Scroll ( PRBool aVertical, PRInt16 aNumLines, PRInt16 aMouseLocalX, 
02064                         PRInt16 aMouseLocalY, PRBool *_retval )
02065 {
02066   return NS_ERROR_NOT_IMPLEMENTED;
02067 }
02068 
02069 
02070 NS_IMETHODIMP
02071 nsChildView::Idle()
02072 {
02073   // do some idle stuff?
02074   return NS_ERROR_NOT_IMPLEMENTED;
02075 }
02076 
02077 
02078 #pragma mark -
02079 
02080 
02081 @implementation ChildView
02082 
02083 //
02084 // initWithFrame:geckoChild:eventSink:
02085 //
02086 // do init stuff
02087 //
02088 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild eventSink:(nsIEventSink*)inSink
02089 {
02090   if ((self = [super initWithFrame:inFrame]))
02091   {
02092     mGeckoChild = inChild;
02093     mEventSink = inSink;
02094     mIsPluginView = NO;
02095     mCurKeyEvent = nil;
02096 
02097     // See if hack code for enabling and disabling mouse move
02098     // events is necessary. Fixed by at least 10.2.8
02099     long version = 0;
02100     ::Gestalt(gestaltSystemVersion, &version);
02101     mToggleMouseMoveEventWatching = (version < 0x00001028);
02102     
02103     // initialization for NSTextInput
02104     mMarkedRange.location = NSNotFound;
02105     mMarkedRange.length = 0;
02106     mSelectedRange.location = NSNotFound;
02107     mSelectedRange.length = 0;
02108     mInComposition = NO;
02109   }
02110   
02111   return self;
02112 }
02113 
02114 - (void)dealloc
02115 {
02116   [super dealloc];    // This sets the current port to _savePort (which should be
02117                       // a valid port, checked with the assertion above.
02118   SetPort(NULL);      // Bullet-proof against future changes in NSQDView
02119 }
02120 
02121 - (void)widgetDestroyed
02122 {
02123   mGeckoChild = nsnull;
02124   mEventSink = nsnull;
02125 }
02126 
02127 //
02128 // -widget
02129 // mozView method
02130 //
02131 // return our gecko child view widget. Note this does not AddRef.
02132 //
02133 - (nsIWidget*) widget
02134 {
02135   return NS_STATIC_CAST(nsIWidget*, mGeckoChild);
02136 }
02137 
02138 //
02139 // -getNativeWindow
02140 // mozView method
02141 //
02142 // get the window that this view is associated with
02143 //
02144 - (NSWindow*)getNativeWindow
02145 {
02146   NSWindow* currWin = [self window];
02147   if (currWin)
02148      return currWin;
02149   else
02150      return mWindow;
02151 }
02152 
02153 //
02154 // -setNativeWindow:
02155 // mozView method
02156 //
02157 // set the NSWindow that this view is associated with (even when not in the view
02158 // hierarchy).
02159 //
02160 - (void)setNativeWindow:(NSWindow*)aWindow
02161 {
02162   mWindow = aWindow;
02163 }
02164 
02165 - (void)setNeedsPendingDisplay
02166 {
02167   mPendingFullDisplay = YES;
02168   [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
02169 }
02170 
02171 - (void)setNeedsPendingDisplayInRect:(NSRect)invalidRect
02172 {
02173   if (!mPendingDirtyRects)
02174     mPendingDirtyRects = [[NSMutableArray alloc] initWithCapacity:1];
02175   [mPendingDirtyRects addObject:[NSValue valueWithRect:invalidRect]];
02176   [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
02177 }
02178 
02179 //
02180 // -processPendingRedraws
02181 //
02182 // Clears the queue of any pending invalides
02183 //
02184 - (void)processPendingRedraws
02185 {
02186   if (mPendingFullDisplay) {
02187     [self setNeedsDisplay:YES];
02188   }
02189   else {
02190     unsigned int count = [mPendingDirtyRects count];
02191     for (unsigned int i = 0; i < count; ++i) {
02192       [self setNeedsDisplayInRect:[[mPendingDirtyRects objectAtIndex:i] rectValue]];
02193     }
02194   }
02195   mPendingFullDisplay = NO;
02196   [mPendingDirtyRects release];
02197   mPendingDirtyRects = nil;
02198 }
02199 
02200 - (NSString*)description
02201 {
02202   return [NSString stringWithFormat:@"ChildView %p, gecko child %p, frame %@", self, mGeckoChild, NSStringFromRect([self frame])];
02203 }
02204 
02205 // Find the nearest scrollable view for this ChildView
02206 // (recall that views are not refcounted)
02207 - (nsIScrollableView*) getScrollableView
02208 {
02209   nsIScrollableView* scrollableView = nsnull;
02210 
02211   ChildView* currView = self;
02212   // we have to loop up through superviews in case the view that received the
02213   // mouseDown is in fact a plugin view with no scrollbars
02214   while (currView) {
02215 
02216     // This is a hack I learned in nsView::GetViewFor(nsIWidget* aWidget)
02217     // that I'm not sure is kosher. If anyone knows a better way to get
02218     // the view for a widget, I'd love to hear it. --Nathan
02219 
02220     void* clientData;
02221     [currView widget]->GetClientData(clientData);
02222 
02223     nsISupports* data = (nsISupports*)clientData;
02224     nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(data));
02225     if (req)
02226     {
02227       req->GetInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollableView);
02228       if (scrollableView)
02229         break;
02230     }
02231 
02232     if ([[currView superview] isMemberOfClass:[ChildView class]])
02233         currView = [currView superview];
02234     else
02235         currView = nil;
02236   }
02237 
02238   return scrollableView;
02239 }
02240 
02241 // set the closed hand cursor and record the starting scroll positions
02242 - (void) startHandScroll:(NSEvent*)theEvent
02243 {
02244   if (!mGeckoChild)
02245     return;
02246 
02247   mHandScrollStartMouseLoc = [[self window] convertBaseToScreen: [theEvent locationInWindow]];
02248 
02249   nsIScrollableView* aScrollableView = [self getScrollableView]; 
02250 
02251   // if we succeeded in getting aScrollableView
02252   if (aScrollableView) {
02253     aScrollableView->GetScrollPosition(mHandScrollStartScrollX, mHandScrollStartScrollY);
02254     mGeckoChild->SetCursor(eCursor_grabbing);
02255     mInHandScroll = TRUE;
02256   }
02257 }
02258 
02259 // update the scroll position based on the new mouse coordinates
02260 - (void) updateHandScroll:(NSEvent*)theEvent
02261 {
02262   nsIScrollableView* aScrollableView = [self getScrollableView];
02263   if (!aScrollableView || !mGeckoChild)
02264     return;
02265   
02266   NSPoint newMouseLoc = [[self window] convertBaseToScreen: [theEvent locationInWindow]];
02267 
02268   PRInt32 deltaX = (PRInt32)(mHandScrollStartMouseLoc.x - newMouseLoc.x);
02269   PRInt32 deltaY = (PRInt32)(newMouseLoc.y - mHandScrollStartMouseLoc.y);
02270 
02271   // convert to the nsIView coordinates
02272   float mPixelsToTwips = 1.0;
02273   mPixelsToTwips = mGeckoChild->GetDeviceContext()->DevUnitsToAppUnits();
02274   nscoord newX = mHandScrollStartScrollX +
02275     NSIntPixelsToTwips(deltaX, mPixelsToTwips);
02276   nscoord newY = mHandScrollStartScrollY +
02277     NSIntPixelsToTwips(deltaY, mPixelsToTwips);
02278   aScrollableView->ScrollTo(newX, newY, NS_VMREFRESH_IMMEDIATE);
02279 }
02280 
02281 // Return true if the correct modifiers are pressed to perform hand scrolling.
02282 + (BOOL) areHandScrollModifiers:(unsigned int)modifiers
02283 {
02284   // The command and option key should be held down.  Ignore capsLock by
02285   // setting it explicitly to match.
02286   modifiers |= NSAlphaShiftKeyMask;
02287   return (modifiers & NSDeviceIndependentModifierFlagsMask) ==
02288       (NSAlphaShiftKeyMask | NSCommandKeyMask | NSAlternateKeyMask);
02289 }
02290 
02291 // If the user is pressing the hand scroll modifiers, then set
02292 // the hand scroll cursor.
02293 - (void) setHandScrollCursor:(NSEvent*)theEvent
02294 {
02295   if (!mGeckoChild)
02296     return;
02297 
02298   BOOL inMouseView = NO;
02299 
02300   // check to see if the user has hand scroll modifiers held down; if so, 
02301   // find out if the cursor is in an ChildView
02302   if ([ChildView areHandScrollModifiers:[theEvent modifierFlags]]) {
02303     NSPoint pointInWindow = [[self window] mouseLocationOutsideOfEventStream];
02304 
02305     NSView* mouseView = [[[self window] contentView] hitTest:pointInWindow];
02306     inMouseView = (mouseView != nil && [mouseView isMemberOfClass:[ChildView class]]);   
02307   }
02308   if (inMouseView) {
02309       mGeckoChild->SetCursor(eCursor_grab);
02310   } else {
02311     nsCursor cursor = mGeckoChild->GetCursor();
02312     if (!mInHandScroll) {
02313       if (cursor == eCursor_grab || cursor == eCursor_grabbing)
02314         mGeckoChild->SetCursor(eCursor_standard);
02315     }
02316   }
02317 }
02318 
02319 // reset the scroll flag and cursor
02320 - (void) stopHandScroll:(NSEvent*)theEvent
02321 {
02322   mInHandScroll = FALSE;
02323   [self setHandScrollCursor:theEvent];
02324 }
02325 
02326 //
02327 // -setFrame
02328 //
02329 // Override in order to keep our mouse enter/exit tracking rect in sync with
02330 // the frame of the view
02331 //
02332 - (void)setFrame:(NSRect)frameRect
02333 {  
02334   [super setFrame:frameRect];
02335   if (mMouseEnterExitTag)
02336     [self removeTrackingRect:mMouseEnterExitTag];
02337 
02338   if ([self window])
02339     mMouseEnterExitTag = [self addTrackingRect:[self bounds] owner:self
02340                                       userData:nil assumeInside: [[self window]
02341                                       acceptsMouseMovedEvents]];
02342 }
02343 
02344 
02345 // 
02346 // -isFlipped
02347 //
02348 // Make the origin of this view the topLeft corner (gecko origin) rather
02349 // than the bottomLeft corner (standard cocoa origin).
02350 //
02351 - (BOOL)isFlipped
02352 {
02353   return YES;
02354 }
02355 
02356 // -isOpaque
02357 //
02358 // NSQuickDrawViews do not correctly update if opaque, because of a known incompatibility
02359 // between the way that NSQuickDrawView is implemented, and the NSWindow update mechanism.
02360 // This is unlikely to change in future.
02361 // 
02362 // It's unfortunate, because it's expensive to redraw every parent view when updating
02363 // a portion of any given NSQDView. However, there is no efficient workaround. See
02364 // bug 166932.
02365 // 
02366 - (BOOL)isOpaque
02367 {
02368   return mIsPluginView;
02369 }
02370 
02371 -(void)setIsPluginView:(BOOL)aIsPlugin
02372 {
02373   mIsPluginView = aIsPlugin;
02374 }
02375 
02376 -(BOOL)getIsPluginView
02377 {
02378   return mIsPluginView;
02379 }
02380 
02381 - (BOOL)childViewHasPlugin
02382 {
02383   NSArray* subviews = [self subviews];
02384   for (unsigned int i = 0; i < [subviews count]; i ++)
02385   {
02386     id subview = [subviews objectAtIndex:i];
02387     if ([subview respondsToSelector:@selector(getIsPluginView)] && [subview getIsPluginView])
02388         return YES;
02389   }
02390   
02391   return NO;
02392 }
02393 
02394 //
02395 // -acceptsFirstResponder
02396 //
02397 // We accept key and mouse events, so don't keep passing them up the chain. Allow
02398 // this to be a 'focussed' widget for event dispatch
02399 //
02400 - (BOOL)acceptsFirstResponder
02401 {
02402   return YES;
02403 }
02404 
02405 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
02406 {
02407   if (mMouseEnterExitTag)
02408     [self removeTrackingRect:mMouseEnterExitTag];
02409 
02410   [super viewWillMoveToWindow:newWindow];
02411 }
02412 
02413 - (void)viewDidMoveToWindow
02414 {
02415   if ([self window])
02416     mMouseEnterExitTag = [self addTrackingRect:[self bounds] owner:self
02417                                       userData:nil assumeInside: [[self window]
02418                                       acceptsMouseMovedEvents]];
02419 
02420   [super viewDidMoveToWindow];
02421 }
02422 
02423 - (void)viewWillStartLiveResize
02424 {
02425   if (mGeckoChild && mIsPluginView)
02426     mGeckoChild->LiveResizeStarted();
02427   
02428   [super viewWillStartLiveResize];
02429 }
02430 
02431 - (void)viewDidEndLiveResize
02432 {
02433   if (mGeckoChild && mIsPluginView)
02434     mGeckoChild->LiveResizeEnded();
02435 
02436   [super viewDidEndLiveResize];
02437 }
02438 
02439 - (void)scrollRect:(NSRect)aRect by:(NSSize)offset
02440 {
02441   // Update any pending dirty rects to reflect the new scroll position
02442   if (mPendingDirtyRects) {
02443     unsigned int count = [mPendingDirtyRects count];
02444     for (unsigned int i = 0; i < count; ++i) {
02445       NSRect oldRect = [[mPendingDirtyRects objectAtIndex:i] rectValue];
02446       NSRect newRect = NSOffsetRect(oldRect, offset.width, offset.height);
02447       [mPendingDirtyRects replaceObjectAtIndex:i
02448                                     withObject:[NSValue valueWithRect:newRect]];
02449     }
02450   }
02451   [super scrollRect:aRect by:offset];
02452 }
02453 
02454 - (BOOL)mouseDownCanMoveWindow
02455 {
02456   return NO;
02457 }
02458 
02459 - (void)lockFocus
02460 {
02461   // Set the current GrafPort to a "safe" port before calling [NSQuickDrawView lockFocus],
02462   // so that the NSQuickDrawView stashes a pointer to this known-good port internally.
02463   // It will set the port back to this port on destruction.
02464   SetPort(NULL);
02465   [super lockFocus];
02466 }
02467 
02468 //
02469 // -drawRect:
02470 //
02471 // The display system has told us that a portion of our view is dirty. Tell
02472 // gecko to paint it
02473 //
02474 - (void)drawRect:(NSRect)aRect
02475 {
02476   PRBool isVisible;
02477   if (!mGeckoChild || NS_FAILED(mGeckoChild->IsVisible(isVisible)) || !isVisible)
02478     return;
02479   
02480   // Workaround for the fact that NSQuickDrawViews can't be opaque; see if the rect
02481   // being drawn is covered by a subview, and, if so, just bail.
02482   if ([self isRectObscuredBySubview:aRect])
02483     return;
02484   
02485   // tell gecko to paint.
02486   // If < 10.3, just paint the rect
02487   if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_2) {
02488     nsRect r;
02489     ConvertCocoaToGeckoRect(aRect, r);
02490     nsCOMPtr<nsIRenderingContext> rendContext = getter_AddRefs(mGeckoChild->GetRenderingContext());
02491     mGeckoChild->UpdateWidget(r, rendContext);
02492   }
02493   // If >10.3, only paint the sub-rects that need it. This avoids the
02494   // nasty coalesced updates that result in big white areas.
02495   else {
02496     const NSRect *rects;
02497     int count, i;
02498     [self getRectsBeingDrawn:&rects count:&count];
02499     for (i = 0; i < count; ++i) {
02500       nsRect r;
02501       ConvertCocoaToGeckoRect(rects[i], r);
02502       nsCOMPtr<nsIRenderingContext> rendContext = getter_AddRefs(mGeckoChild->GetRenderingContext());
02503       mGeckoChild->UpdateWidget(r, rendContext);
02504     }
02505   }
02506 }
02507 
02508 - (BOOL)isRectObscuredBySubview:(NSRect)inRect
02509 {
02510   unsigned int numSubviews = [[self subviews] count];
02511   for (unsigned int i = 0; i < numSubviews; i++)
02512   {
02513     NSRect subviewFrame = [[[self subviews] objectAtIndex:i] frame];
02514     if (NSContainsRect(subviewFrame, inRect))
02515       return YES;
02516   }
02517   
02518   return NO;
02519 }
02520 
02521 - (void)flushRect:(NSRect)inRect
02522 {
02523   Rect updateRect;
02524   updateRect.left   = (short)inRect.origin.x;
02525   updateRect.top    = (short)inRect.origin.y;
02526   updateRect.right  = updateRect.left + (short)inRect.size.width;
02527   updateRect.bottom = updateRect.top +  (short)inRect.size.height;
02528 
02529   RgnHandle updateRgn = ::NewRgn();
02530   RectRgn(updateRgn, &updateRect);
02531   ::QDFlushPortBuffer((CGrafPtr)[self qdPort], updateRgn);
02532   ::DisposeRgn(updateRgn);
02533 }
02534 
02535 //
02536 // -wantsDefaultClipping
02537 //
02538 // A panther-only method, allows us to turn off setting up the clip region
02539 // before each drawRect. We already clip within gecko.
02540 //
02541 - (BOOL)wantsDefaultClipping
02542 {
02543   return NO;
02544 }
02545 
02546 #if USE_CLICK_HOLD_CONTEXTMENU
02547 //
02548 // -clickHoldCallback:
02549 //
02550 // called from a timer two seconds after a mouse down to see if we should display
02551 // a context menu (click-hold). |anEvent| is the original mouseDown event. If we're
02552 // still in that mouseDown by this time, put up the context menu, otherwise just
02553 // fuhgeddaboutit. |anEvent| has been retained by the OS until after this callback
02554 // fires so we're ok there.
02555 //
02556 // This code currently messes in a bunch of edge cases (bugs 234751, 232964, 232314)
02557 // so removing it until we get it straightened out.
02558 //
02559 - (void)clickHoldCallback:(id)theEvent;
02560 {
02561   if( theEvent == [NSApp currentEvent] ) {
02562     // we're still in the middle of the same mousedown event here, activate
02563     // click-hold context menu by triggering the right mouseDown action.
02564     NSEvent* clickHoldEvent = [NSEvent mouseEventWithType:NSRightMouseDown
02565                                                   location:[theEvent locationInWindow]
02566                                              modifierFlags:[theEvent modifierFlags]
02567                                                  timestamp:[theEvent timestamp]
02568                                               windowNumber:[theEvent windowNumber]
02569                                                    context:[theEvent context]
02570                                                eventNumber:[theEvent eventNumber]
02571                                                 clickCount:[theEvent clickCount]
02572                                                   pressure:[theEvent pressure]];
02573     [self rightMouseDown:clickHoldEvent];
02574   }
02575 }
02576 #endif
02577 
02578 - (void)mouseDown:(NSEvent *)theEvent
02579 {
02580   // if the command and alt keys are held down, initiate hand scrolling
02581   if ([ChildView areHandScrollModifiers:[theEvent modifierFlags]]) {
02582     [self startHandScroll: theEvent];
02583     // needed to change the focus, among other things, since we don't
02584     // get to do that below.
02585     [super mouseDown:theEvent];
02586     return;                     // do not pass this mousedown event to gecko
02587   }
02588 
02589 #if USE_CLICK_HOLD_CONTEXTMENU
02590   // fire off timer to check for click-hold after two seconds. retains |theEvent|
02591   [self performSelector:@selector(clickHoldCallback:) withObject:theEvent afterDelay:2.0];
02592 #endif
02593 
02594   nsMouseEvent geckoEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
02595   [self convertEvent:theEvent message:NS_MOUSE_LEFT_BUTTON_DOWN toGeckoEvent:&geckoEvent];
02596   geckoEvent.clickCount = [theEvent clickCount];
02597   
02598   NSPoint mouseLoc = [theEvent locationInWindow];
02599   NSPoint screenLoc = [[self window] convertBaseToScreen: mouseLoc];
02600 
02601   EventRecord macEvent;
02602   macEvent.what = mouseDown;
02603   macEvent.message = 0;
02604   macEvent.when = ::TickCount();
02605   GetGlobalMouse(&macEvent.where);
02606   macEvent.modifiers = GetCurrentKeyModifiers();
02607   geckoEvent.nativeMsg = &macEvent;
02608 
02609   // send event into Gecko by going directly to the
02610   // the widget.
02611   mGeckoChild->DispatchMouseEvent(geckoEvent);
02612   
02613   // XXX maybe call markedTextSelectionChanged:client: here?
02614 }
02615 
02616 - (void)mouseUp:(NSEvent *)theEvent
02617 {
02618   if (mInHandScroll) {
02619     [self updateHandScroll:theEvent];
02620     [self stopHandScroll:theEvent];
02621     return;
02622   }
02623   if (!mGeckoChild)
02624     return;
02625 
02626   nsMouseEvent geckoEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
02627   [self convertEvent:theEvent message:NS_MOUSE_LEFT_BUTTON_UP toGeckoEvent:&geckoEvent];
02628   
02629   NSPoint mouseLoc = [theEvent locationInWindow];
02630   NSPoint screenLoc = [[self window] convertBaseToScreen: mouseLoc];
02631 
02632   EventRecord macEvent;
02633   macEvent.what = mouseUp;
02634   macEvent.message = 0;
02635   macEvent.when = ::TickCount();
02636   GetGlobalMouse(&macEvent.where);
02637   macEvent.modifiers = GetCurrentKeyModifiers();
02638   geckoEvent.nativeMsg = &macEvent;
02639 
02640   // send event into Gecko by going directly to the
02641   // the widget.
02642   mGeckoChild->DispatchMouseEvent(geckoEvent);
02643   
02644 }
02645 
02646 - (void)mouseMoved:(NSEvent*)theEvent
02647 {
02648   NSView* view = [[[self window] contentView] hitTest: [theEvent locationInWindow]];
02649   if (view != (NSView*)self) {
02650     // We shouldn't handle this.  Send it to the right view.
02651     [view mouseMoved: theEvent];
02652     return;
02653   }
02654   // check if we are in a hand scroll or if the user
02655   // has command and alt held down; if so,  we do not want
02656   // gecko messing with the cursor.
02657   if ([ChildView  areHandScrollModifiers:[theEvent modifierFlags]])
02658     return;
02659   if (!mGeckoChild)
02660     return;
02661 
02662   nsMouseEvent geckoEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
02663   [self convertEvent:theEvent message:NS_MOUSE_MOVE toGeckoEvent:&geckoEvent];
02664 
02665   NSPoint mouseLoc = [theEvent locationInWindow];
02666   NSPoint screenLoc = [[self window] convertBaseToScreen: mouseLoc];
02667 
02668   EventRecord macEvent;
02669   macEvent.what = nullEvent;
02670   macEvent.message = 0;
02671   macEvent.when = ::TickCount();
02672   GetGlobalMouse(&macEvent.where);
02673   
02674   macEvent.modifiers = GetCurrentKeyModifiers();
02675   geckoEvent.nativeMsg = &macEvent;
02676 
02677   // send event into Gecko by going directly to the
02678   // the widget.
02679   mGeckoChild->DispatchMouseEvent(geckoEvent);
02680 }
02681 
02682 - (void)mouseDragged:(NSEvent*)theEvent
02683 {
02684   if (!mGeckoChild)
02685     return;
02686   // if the handscroll flag is set, steal this event
02687   if (mInHandScroll) {
02688     [self updateHandScroll:theEvent];
02689     return;
02690   }
02691 
02692   nsMouseEvent geckoEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
02693   [self convertEvent:theEvent message:NS_MOUSE_MOVE toGeckoEvent:&geckoEvent];
02694 
02695   EventRecord macEvent;
02696   macEvent.what = nullEvent;
02697   macEvent.message = 0;
02698   macEvent.when = ::TickCount();
02699   GetGlobalMouse(&macEvent.where);
02700   macEvent.modifiers = btnState | GetCurrentKeyModifiers();
02701   geckoEvent.nativeMsg = &macEvent;
02702   
02703   // send event into Gecko by going directly to the
02704   // the widget.
02705   mGeckoChild->DispatchMouseEvent(geckoEvent);    
02706 
02707   // XXX maybe call markedTextSelectionChanged:client: here?
02708 }
02709 
02710 - (void)mouseEntered:(NSEvent*)theEvent
02711 {
02712   // checks to see if we should change to the hand cursor
02713   [self setHandScrollCursor:theEvent];
02714   
02715   // we need to forward mouse move events to gecko when the mouse
02716   // is over a gecko view
02717   if (mToggleMouseMoveEventWatching)
02718     [[self window] setAcceptsMouseMovedEvents: YES];
02719 }
02720 
02721 - (void)mouseExited:(NSEvent*)theEvent
02722 {
02723   // Gecko may have set the cursor to ibeam or link hand, or handscroll may
02724   // have set it to the open hand cursor. Cocoa won't call this during a drag.
02725   if (mGeckoChild)
02726     mGeckoChild->SetCursor(eCursor_standard);
02727    
02728   // no need to monitor mouse movements outside of the gecko view,
02729   // but make sure we are not a plugin view.
02730   if (mToggleMouseMoveEventWatching && ![[self superview] isKindOfClass: [ChildView class]])
02731     [[self window] setAcceptsMouseMovedEvents: NO];
02732 }
02733 
02734 - (void)rightMouseDown:(NSEvent *)theEvent
02735 {
02736   if (!mGeckoChild)
02737     [super rightMouseDown:theEvent];
02738 
02739   // The right mouse went down.  Fire off a right mouse down and
02740   // then send the context menu event.
02741   nsMouseEvent geckoEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
02742   [self convertEvent:theEvent message:NS_MOUSE_RIGHT_BUTTON_DOWN toGeckoEvent:&geckoEvent];
02743 
02744   // plugins need a native event here
02745   EventRecord macEvent;
02746   macEvent.what = mouseDown;
02747   macEvent.message = 0;
02748   macEvent.when = ::TickCount();
02749   GetGlobalMouse(&macEvent.where);
02750   macEvent.modifiers = controlKey;  // fake a context menu click
02751   geckoEvent.nativeMsg = &macEvent;
02752 
02753   geckoEvent.clickCount = [theEvent clickCount];
02754   PRBool handled = mGeckoChild->DispatchMouseEvent(geckoEvent);
02755   if (!handled)
02756     [super rightMouseDown:theEvent];    // let the superview do context menu stuff
02757 }
02758 
02759 - (void)rightMouseUp:(NSEvent *)theEvent
02760 {
02761   if (!mGeckoChild)
02762     [super rightMouseUp:theEvent];
02763 
02764   nsMouseEvent geckoEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
02765   [self convertEvent:theEvent message:NS_MOUSE_RIGHT_BUTTON_UP toGeckoEvent:&geckoEvent];
02766 
02767   // plugins need a native event here
02768   EventRecord macEvent;
02769   macEvent.what = mouseUp;
02770   macEvent.message = 0;
02771   macEvent.when = ::TickCount();
02772   GetGlobalMouse(&macEvent.where);
02773   macEvent.modifiers = controlKey;  // fake a context menu click
02774   geckoEvent.nativeMsg = &macEvent;
02775 
02776   geckoEvent.clickCount = [theEvent clickCount];
02777   PRBool handled = mGeckoChild->DispatchMouseEvent(geckoEvent);
02778   if (!handled)
02779     [super rightMouseUp:theEvent];
02780 }
02781 
02782 - (void)otherMouseDown:(NSEvent *)theEvent
02783 {
02784   if (!mGeckoChild)
02785     return;
02786 
02787   nsMouseEvent geckoEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
02788   [self convertEvent:theEvent message:NS_MOUSE_MIDDLE_BUTTON_DOWN toGeckoEvent:&geckoEvent];
02789   geckoEvent.clickCount = [theEvent clickCount];
02790   
02791   // send event into Gecko by going directly to the
02792   // the widget.
02793   mGeckoChild->DispatchMouseEvent(geckoEvent);
02794   
02795 } // otherMouseDown
02796 
02797 
02798 - (void)otherMouseUp:(NSEvent *)theEvent
02799 {
02800   if (!mGeckoChild)
02801     return;
02802 
02803   nsMouseEvent geckoEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
02804   [self convertEvent:theEvent message:NS_MOUSE_MIDDLE_BUTTON_UP toGeckoEvent:&geckoEvent];
02805   
02806   // send event into Gecko by going directly to the
02807   // the widget.
02808   mGeckoChild->DispatchMouseEvent(geckoEvent);
02809   
02810 } // mouseUp
02811 
02812 //
02813 // -scrollWheel:forAxis:
02814 //
02815 // Handle an NSScrollWheel event for a single axis only.
02816 //
02817 -(void)scrollWheel:(NSEvent*)theEvent forAxis:(enum nsMouseScrollEvent::nsMouseScrollFlags)inAxis
02818 {
02819   if (!mGeckoChild)
02820     return;
02821 
02822   float scrollDelta;
02823 
02824   if (inAxis & nsMouseScrollEvent::kIsVertical)
02825     scrollDelta = -[theEvent deltaY];
02826   else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
02827     scrollDelta = -[theEvent deltaX];
02828   else
02829     // Caller screwed up
02830     return;
02831 
02832   if (scrollDelta == 0)
02833     // No sense in firing off a Gecko event.  Note that as of 10.4 Tiger,
02834     // a single NSScrollWheel event might result in deltaX = deltaY = 0.
02835     return;
02836 
02837   nsMouseScrollEvent geckoEvent(PR_TRUE, 0, nsnull);
02838   [self convertEvent:theEvent message:NS_MOUSE_SCROLL toGeckoEvent:&geckoEvent];
02839   geckoEvent.scrollFlags |= inAxis;
02840 
02841   // Gecko only understands how to scroll by an integer value.  Using floor
02842   // and ceil is better than truncating the fraction, especially when
02843   // |delta| < 1.
02844   if (scrollDelta < 0)
02845     geckoEvent.delta = (PRInt32)floorf(scrollDelta);
02846   else
02847     geckoEvent.delta = (PRInt32)ceilf(scrollDelta);
02848 
02849   mGeckoChild->DispatchWindowEvent(geckoEvent);
02850 
02851   // dispatch scroll wheel carbon event for plugins
02852   {
02853     EventRef theEvent;
02854     OSStatus err = ::MacCreateEvent(NULL,
02855                           kEventClassMouse,
02856                           kEventMouseWheelMoved,
02857                           TicksToEventTime(TickCount()),
02858                           kEventAttributeUserEvent,
02859                           &theEvent);
02860     if (err == noErr)
02861     {
02862       EventMouseWheelAxis axis;
02863       if (inAxis & nsMouseScrollEvent::kIsVertical)
02864         axis = kEventMouseWheelAxisY;
02865       else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
02866         axis = kEventMouseWheelAxisX;
02867       
02868       SetEventParameter(theEvent,
02869                             kEventParamMouseWheelAxis,
02870                             typeMouseWheelAxis,
02871                             sizeof(EventMouseWheelAxis),
02872                             &axis);
02873 
02874       SInt32 delta = (SInt32)-geckoEvent.delta;
02875       SetEventParameter(theEvent,
02876                             kEventParamMouseWheelDelta,
02877                             typeLongInteger,
02878                             sizeof(SInt32),
02879                             &delta);
02880 
02881       Point mouseLoc;
02882       GetGlobalMouse(&mouseLoc);
02883       SetEventParameter(theEvent,
02884                             kEventParamMouseLocation,
02885                             typeQDPoint,
02886                             sizeof(Point),
02887                             &mouseLoc);
02888       
02889       SendEventToWindow(theEvent, (WindowRef)[[self window] windowRef]);
02890       ReleaseEvent(theEvent);
02891     }
02892   }
02893 }
02894 
02895 -(void)scrollWheel:(NSEvent*)theEvent
02896 {
02897   // It's possible for a single NSScrollWheel event to carry both useful
02898   // deltaX and deltaY, for example, when the "wheel" is a trackpad.
02899   // NSMouseScrollEvent can only carry one axis at a time, so the system
02900   // event will be split into two Gecko events if necessary.
02901   [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsVertical];
02902   [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsHorizontal];
02903 }
02904 
02905 -(NSMenu*)menuForEvent:(NSEvent*)theEvent
02906 {
02907   if ([self getIsPluginView] || !mGeckoChild)
02908     return nil;
02909   
02910   // Fire the context menu event into Gecko.
02911   nsMouseEvent geckoEvent(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
02912   [self convertEvent:theEvent message:NS_CONTEXTMENU toGeckoEvent:&geckoEvent];
02913   
02914   // send event into Gecko by going directly to the
02915   // the widget.
02916   mGeckoChild->DispatchMouseEvent(geckoEvent);
02917   
02918   // Go up our view chain to fetch the correct menu to return.
02919   return [self getContextMenu];
02920 }
02921 
02922 - (NSMenu*)getContextMenu
02923 {
02924   NSView* superView = [self superview];
02925   if ([superView respondsToSelector:@selector(getContextMenu)])
02926     return [(NSView<mozView>*)superView getContextMenu];
02927 
02928   return nil;
02929 }
02930 
02931 - (TopLevelWindowData*)ensureWindowData
02932 {
02933   WindowDataMap* windowMap = [WindowDataMap sharedWindowDataMap];
02934 
02935   TopLevelWindowData* windowData = [windowMap dataForWindow:mWindow];
02936   if (mWindow && !windowData)
02937   {
02938     windowData = [[TopLevelWindowData alloc] initWithWindow:mWindow];
02939     [windowMap setData:windowData forWindow:mWindow]; // takes ownership
02940     [windowData release];
02941   }
02942   return windowData;
02943 }
02944 
02945 //
02946 // -convertEvent:message:toGeckoEvent:
02947 //
02948 // convert from one event system to the other for event dispatching
02949 //
02950 - (void) convertEvent:(NSEvent*)inEvent message:(PRInt32)inMsg toGeckoEvent:(nsInputEvent*)outGeckoEvent
02951 {
02952   outGeckoEvent->nativeMsg = inEvent;
02953   [self convertLocation:[inEvent locationInWindow] message:inMsg modifiers:[inEvent modifierFlags]
02954           toGeckoEvent:outGeckoEvent];
02955 }
02956 
02957 - (void) convertLocation:(NSPoint)inPoint message:(PRInt32)inMsg modifiers:(unsigned int)inMods toGeckoEvent:(nsInputEvent*)outGeckoEvent
02958 {
02959   outGeckoEvent->message = inMsg;
02960   outGeckoEvent->widget = [self widget];
02961   outGeckoEvent->time = PR_IntervalNow();
02962   
02963   if (outGeckoEvent->eventStructType != NS_KEY_EVENT) {
02964     NSPoint mouseLoc = inPoint;
02965     
02966     // convert point to view coordinate system
02967     NSPoint localPoint = [self convertPoint:mouseLoc fromView:nil];
02968     
02969     outGeckoEvent->refPoint.x = outGeckoEvent->point.x = NS_STATIC_CAST(nscoord, localPoint.x);
02970     outGeckoEvent->refPoint.y = outGeckoEvent->point.y = NS_STATIC_CAST(nscoord, localPoint.y);
02971   }
02972   
02973   // set up modifier keys
02974   outGeckoEvent->isShift    = ((inMods & NSShiftKeyMask) != 0);
02975   outGeckoEvent->isControl  = ((inMods & NSControlKeyMask) != 0);
02976   outGeckoEvent->isAlt      = ((inMods & NSAlternateKeyMask) != 0);
02977   outGeckoEvent->isMeta     = ((inMods & NSCommandKeyMask) != 0);
02978 }
02979 
02980  
02981 static PRBool ConvertUnicodeToCharCode(PRUnichar inUniChar, unsigned char* outChar)
02982 {
02983   UnicodeToTextInfo converterInfo;
02984   TextEncoding      systemEncoding;
02985   Str255            convertedString;
02986   OSStatus          err;
02987   
02988   *outChar = 0;
02989   
02990   err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &systemEncoding);
02991   if (err != noErr)
02992     return PR_FALSE;
02993   
02994   err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo);
02995   if (err != noErr)
02996     return PR_FALSE;
02997   
02998   err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar), &inUniChar, convertedString);
02999   if (err != noErr)
03000     return PR_FALSE;
03001 
03002   *outChar = convertedString[1];
03003   ::DisposeUnicodeToTextInfo(&converterInfo);
03004   return PR_TRUE;
03005 }
03006 
03007 static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& macEvent, PRUint32 keyType = 0)
03008 {
03009     UInt32 charCode = 0;
03010     if ([cocoaEvent type] == NSFlagsChanged) {
03011       macEvent.what = keyType == NS_KEY_DOWN ? keyDown : keyUp;
03012     } else {
03013       charCode = [[cocoaEvent characters] characterAtIndex:0];
03014       if ([cocoaEvent type] == NSKeyDown)
03015         macEvent.what = [cocoaEvent isARepeat] ? autoKey : keyDown;
03016       else
03017         macEvent.what = keyUp;
03018     }
03019 
03020     if (charCode >= 0x0080)
03021     {
03022         switch (charCode) {
03023         case NSUpArrowFunctionKey:
03024             charCode = kUpArrowCharCode;
03025             break;
03026         case NSDownArrowFunctionKey:
03027             charCode = kDownArrowCharCode;
03028             break;
03029         case NSLeftArrowFunctionKey:
03030             charCode = kLeftArrowCharCode;
03031             break;
03032         case NSRightArrowFunctionKey:
03033             charCode = kRightArrowCharCode;
03034             break;
03035         default:
03036             unsigned char convertedCharCode;
03037             if (ConvertUnicodeToCharCode(charCode, &convertedCharCode))
03038               charCode = convertedCharCode;
03039             //NSLog(@"charcode is %d, converted to %c, char is %@", charCode, convertedCharCode, [cocoaEvent characters]);
03040             break;
03041         }
03042     }
03043     macEvent.message = (charCode & 0x00FF) | ([cocoaEvent keyCode] << 8);
03044     macEvent.when = ::TickCount();
03045     GetGlobalMouse(&macEvent.where);
03046     macEvent.modifiers = ::GetCurrentKeyModifiers();
03047 }
03048 
03049 - (nsRect)sendCompositionEvent:(PRInt32) aEventType
03050 {
03051   if (!mGeckoChild)
03052     return nsRect(0, 0, 0, 0);
03053 
03054 #ifdef DEBUG_IME
03055   NSLog(@"****in sendCompositionEvent; type = %d", aEventType);
03056 #endif
03057 
03058   // static void init_composition_event( *aEvent, int aType)
03059   nsCompositionEvent event(PR_TRUE, aEventType, mGeckoChild);
03060   event.time = PR_IntervalNow();
03061   mGeckoChild->DispatchWindowEvent(event);
03062   return event.theReply.mCursorPosition;
03063 }
03064 
03065 - (void)sendTextEvent:(PRUnichar*) aBuffer 
03066                       attributedString:(NSAttributedString*) aString  
03067                       selectedRange:(NSRange) selRange 
03068                       markedRange:(NSRange) markRange
03069                       doCommit:(BOOL) doCommit
03070 {
03071   if (!mGeckoChild)
03072     return;
03073 
03074 #ifdef DEBUG_IME
03075   NSLog(@"****in sendTextEvent; string = '%@'", aString);
03076   NSLog(@" markRange = %d, %d;  selRange = %d, %d", markRange.location, markRange.length, selRange.location, selRange.length);
03077 #endif
03078 
03079   nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, mGeckoChild);
03080   textEvent.time = PR_IntervalNow();
03081   textEvent.theText = aBuffer;
03082   if (!doCommit)
03083     FillTextRangeInTextEvent(&textEvent, aString, markRange, selRange);
03084 
03085   mGeckoChild->DispatchWindowEvent(textEvent);
03086   if ( textEvent.rangeArray )
03087     delete [] textEvent.rangeArray;
03088 }
03089 
03090 #define MAX_BUFFER_SIZE 32
03091 
03092 // NSTextInput implementation
03093 
03094 - (void)insertText:(id)insertString
03095 {
03096   if (!mGeckoChild)
03097     return;
03098 
03099 #if DEBUG_IME
03100   NSLog(@"****in insertText: '%@'", insertString);
03101   NSLog(@" markRange = %d, %d;  selRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length);
03102 #endif
03103 
03104   if (![insertString isKindOfClass:[NSAttributedString class]])
03105     insertString = [[[NSAttributedString alloc] initWithString:insertString] autorelease];
03106 
03107   NSString *tmpStr = [insertString string];
03108   unsigned int len = [tmpStr length];
03109   PRUnichar buffer[MAX_BUFFER_SIZE];
03110   PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
03111   [tmpStr getCharacters: bufPtr];
03112   bufPtr[len] = (PRUnichar)'\0';
03113 
03114   if (len == 1 && !mInComposition)
03115   {
03116     // dispatch keypress event with char instead of textEvent
03117     nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, mGeckoChild);
03118     geckoEvent.time      = PR_IntervalNow();
03119     geckoEvent.charCode  = bufPtr[0]; // gecko expects OS-translated unicode
03120     geckoEvent.isChar    = PR_TRUE;
03121     geckoEvent.isShift   = ([mCurKeyEvent modifierFlags] & NSShiftKeyMask) != 0;
03122     // don't set other modifiers from the current event, because here in
03123     // -insertText: they've already been taken into account in creating
03124     // the input string.
03125         
03126     // plugins need a native keyDown or autoKey event here
03127     EventRecord macEvent;
03128     if (mCurKeyEvent)
03129     {
03130       ConvertCocoaKeyEventToMacEvent(mCurKeyEvent, macEvent);
03131       geckoEvent.nativeMsg = &macEvent;
03132     }
03133 
03134     mGeckoChild->DispatchWindowEvent(geckoEvent);
03135   }
03136   else
03137   {
03138     if (!mInComposition)
03139     {
03140       // send start composition event to gecko
03141       [self sendCompositionEvent: NS_COMPOSITION_START];
03142       mInComposition = YES;
03143     }
03144 
03145     // dispatch textevent (is this redundant?)
03146     [self sendTextEvent:bufPtr attributedString:insertString
03147                                selectedRange:NSMakeRange(0, len)
03148                                markedRange:mMarkedRange
03149                                doCommit:YES];
03150 
03151     // send end composition event to gecko
03152     [self sendCompositionEvent: NS_COMPOSITION_END];
03153     mInComposition = NO;
03154     mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0);
03155   }
03156 
03157   if (bufPtr != buffer)
03158     delete[] bufPtr;
03159 }
03160 
03161 - (void)insertNewline:(id)sender
03162 {
03163   // dummy impl, does nothing (other than stop the beeping when hitting return)
03164 }
03165 
03166 - (void) doCommandBySelector:(SEL)aSelector
03167 { 
03168 #if DEBUG_IME 
03169   NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mIgnoreDoCommand);
03170 #endif
03171   if (!mIgnoreDoCommand)
03172     [super doCommandBySelector:aSelector];
03173 }
03174 
03175 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
03176 {
03177 #if DEBUG_IME 
03178   NSLog(@"****in setMarkedText location: %d, length: %d", selRange.location, selRange.length);
03179   NSLog(@" markRange = %d, %d;  selRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length);
03180   NSLog(@" aString = '%@'", aString);
03181 #endif
03182 
03183   if ( ![aString isKindOfClass:[NSAttributedString class]] )
03184     aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
03185 
03186   mSelectedRange = selRange;
03187 
03188   NSMutableAttributedString *mutableAttribStr = aString;
03189   NSString *tmpStr = [mutableAttribStr string];
03190   unsigned int len = [tmpStr length];
03191   PRUnichar buffer[MAX_BUFFER_SIZE];
03192   PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
03193   [tmpStr getCharacters: bufPtr];
03194   bufPtr[len] = (PRUnichar)'\0';
03195 
03196 #if DEBUG_IME 
03197   printf("****in setMarkedText, len = %d, text = ", len);
03198   PRUint32 n = 0;
03199   PRUint32 maxlen = len > 12 ? 12 : len;
03200   for (PRUnichar *a = bufPtr; (*a != (PRUnichar)'\0') && n<maxlen; a++, n++) printf((*a&0xff80) ? "\\u%4X" : "%c", *a); 
03201   printf("\n");
03202 #endif
03203 
03204   mMarkedRange.location = 0;
03205   mMarkedRange.length = len;
03206 
03207   if (!mInComposition)
03208   {
03209     [self sendCompositionEvent:NS_COMPOSITION_START];
03210     mInComposition = YES;
03211   }
03212 
03213   [self sendTextEvent:bufPtr attributedString:aString
03214                              selectedRange:selRange
03215                              markedRange:mMarkedRange
03216                              doCommit:NO];
03217 
03218   if (mInComposition && len == 0)
03219     [self unmarkText];
03220   
03221   if (bufPtr != buffer)
03222     delete[] bufPtr;
03223 }
03224 
03225 - (void) unmarkText
03226 {
03227 #if DEBUG_IME
03228   NSLog(@"****in unmarkText");
03229   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
03230   NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
03231 #endif
03232 
03233   mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0);
03234   if (mInComposition) {
03235     [self sendCompositionEvent: NS_COMPOSITION_END];
03236     mInComposition = NO;  // brade: do we need to send an end composition event?
03237   }
03238 }
03239 
03240 - (BOOL) hasMarkedText
03241 {
03242   return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0);
03243 }
03244 
03245 - (long) conversationIdentifier
03246 {
03247   return (long)self;
03248 }
03249 
03250 - (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
03251 {
03252   if (!mGeckoChild)
03253     return nil;
03254 
03255 #if DEBUG_IME
03256   NSLog(@"****in attributedSubstringFromRange");
03257   NSLog(@" theRange      = %d, %d", theRange.location, theRange.length);
03258   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
03259   NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
03260 #endif
03261 
03262   nsReconversionEvent reconversionEvent(PR_TRUE, NS_RECONVERSION_QUERY,
03263                                         mGeckoChild);
03264   reconversionEvent.time = PR_IntervalNow();
03265 
03266   nsresult rv = mGeckoChild->DispatchWindowEvent(reconversionEvent);
03267   PRUnichar* reconvstr;
03268   if (NS_SUCCEEDED(rv) && (reconvstr = reconversionEvent.theReply.mReconversionString))
03269   {
03270     NSAttributedString* result = [[[NSAttributedString alloc] initWithString:[NSString stringWithCharacters:reconvstr length:nsCRT::strlen(reconvstr)]
03271                                                                   attributes:nil] autorelease];
03272     nsMemory::Free(reconvstr);
03273     return result;
03274   }
03275 
03276   return nil;
03277 }
03278 
03279 - (NSRange) markedRange
03280 {
03281 #if DEBUG_IME
03282   NSLog(@"****in markedRange");
03283   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
03284   NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
03285 #endif
03286 
03287   if (![self hasMarkedText]) {
03288     return NSMakeRange(NSNotFound, 0);
03289   }
03290 
03291   return mMarkedRange;
03292 }
03293 
03294 - (NSRange) selectedRange
03295 {
03296 #if DEBUG_IME
03297   NSLog(@"****in selectedRange");
03298   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
03299   NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
03300 #endif
03301 
03302   return mSelectedRange;
03303 }
03304 
03305 
03306 - (NSRect) firstRectForCharacterRange:(NSRange)theRange
03307 {
03308 #if DEBUG_IME
03309   NSLog(@"****in firstRectForCharacterRange");
03310   NSLog(@" theRange      = %d, %d", theRange.location, theRange.length);
03311   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
03312   NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
03313 #endif
03314 
03315   nsRect compositionRect = [self sendCompositionEvent:NS_COMPOSITION_QUERY];
03316 
03317   NSRect rangeRect;
03318   ConvertGeckoToCocoaRect(compositionRect, rangeRect);
03319 
03320   // convert to window coords
03321   rangeRect = [self convertRect:rangeRect toView:nil];
03322   // convert to cocoa screen coords
03323   rangeRect.origin = [[self getNativeWindow] convertBaseToScreen:rangeRect.origin];
03324   return rangeRect;
03325 }
03326 
03327 
03328 - (unsigned int)characterIndexForPoint:(NSPoint)thePoint
03329 {
03330 #if DEBUG_IME
03331   NSLog(@"****in characterIndexForPoint");
03332   NSLog(@" markRange = %d, %d;  selectRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length);
03333 #endif
03334 
03335   // To implement this, we'd have to grovel in text frames looking at text offsets.
03336   return 0;
03337 }
03338 
03339 - (NSArray*) validAttributesForMarkedText
03340 {
03341 #if DEBUG_IME
03342   NSLog(@"****in validAttributesForMarkedText");
03343   NSLog(@" markRange = %d, %d;  selectRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length);
03344 #endif
03345 
03346   return [NSArray array]; // empty array; we don't support any attributes right now
03347 }
03348 // end NSTextInput
03349 
03350 //
03351 // keyDown:
03352 //
03353 // Handle matching cocoa IME with gecko key events. Sends a key down and key press
03354 // event to gecko.
03355 //
03356 - (void)keyDown:(NSEvent*)theEvent
03357 {
03358   PRBool isKeyDownEventHandled = PR_TRUE;
03359   PRBool isKeyEventHandled = PR_FALSE;
03360   BOOL  isARepeat = [theEvent isARepeat];
03361 
03362   mCurKeyEvent = theEvent;
03363   
03364   // if we have a dead-key event, we won't get a character
03365   // since we have no character, there isn't any point to generating
03366   // a gecko event until they have dead key events
03367   BOOL nonDeadKeyPress = [[theEvent characters] length] > 0;
03368   if (!isARepeat && nonDeadKeyPress && mGeckoChild)
03369   {
03370     // Fire a key down. We'll fire key presses via -insertText:
03371     nsKeyEvent geckoEvent(PR_TRUE, 0, nsnull);
03372     geckoEvent.point.x = geckoEvent.point.y = 0;
03373     [self convertKeyEvent:theEvent
03374                   message:NS_KEY_DOWN
03375              toGeckoEvent:&geckoEvent];
03376 
03377     // XXX Maybe we should only do this when there is a plugin present.
03378     EventRecord macEvent;
03379     ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
03380     geckoEvent.nativeMsg = &macEvent;
03381     isKeyDownEventHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
03382   }
03383   
03384   // Check to see if we are still the first responder.
03385   // The key down event may have shifted the focus, in which
03386   // case we should not fire the key press.
03387   NSResponder* resp = [[self window] firstResponder];
03388   if (resp != (NSResponder*)self) {
03389 #if DEBUG
03390     printf("We are no longer the responder. Bailing.\n");
03391 #endif
03392     mCurKeyEvent = nil;
03393     return;
03394   }
03395     
03396   if (nonDeadKeyPress && mGeckoChild)
03397   {
03398     nsKeyEvent geckoEvent(PR_TRUE, 0, nsnull);
03399     geckoEvent.point.x = geckoEvent.point.y = 0;
03400 
03401     [self convertKeyEvent:theEvent
03402                   message:NS_KEY_PRESS
03403              toGeckoEvent:&geckoEvent];
03404     
03405     // if this is a non-letter keypress, or the control key is down,
03406     // dispatch the keydown to gecko, so that we trap delete,
03407     // control-letter combinations etc before Cocoa tries to use
03408     // them for keybindings.
03409     if ((!geckoEvent.isChar || geckoEvent.isControl) && !mInComposition)
03410     {
03411       // plugins need a native event, it will either be keyDown or autoKey
03412       EventRecord macEvent;
03413       ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
03414       geckoEvent.nativeMsg = &macEvent;
03415       
03416       isKeyEventHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
03417       mIgnoreDoCommand = isKeyEventHandled;
03418     }
03419   }
03420 
03421   [super interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
03422 
03423   mIgnoreDoCommand = NO;
03424   mCurKeyEvent = nil;
03425 }
03426 
03427 - (void)keyUp:(NSEvent*)theEvent
03428 {
03429   // if we don't have any characters we can't generate a keyUp event
03430   if (!mGeckoChild || [[theEvent characters] length] == 0)
03431     return;
03432 
03433   // Fire a key up.
03434   nsKeyEvent geckoEvent(PR_TRUE, 0, nsnull);
03435   geckoEvent.point.x = geckoEvent.point.y = 0;
03436 
03437   [self convertKeyEvent:theEvent
03438                 message:NS_KEY_UP
03439            toGeckoEvent:&geckoEvent];
03440 
03441   // As an optimisation, only do this when there is a plugin present.
03442   EventRecord macEvent;
03443   ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
03444   geckoEvent.nativeMsg = &macEvent;
03445 
03446   mGeckoChild->DispatchWindowEvent(geckoEvent);
03447 }
03448 
03449 // Fire key up/down events for the modifier keys (shift, alt, ctrl, command).
03450 - (void)flagsChanged:(NSEvent*)theEvent
03451 {
03452   if (!mGeckoChild)
03453     return;
03454 
03455   if ([theEvent type] == NSFlagsChanged) {
03456     unsigned int modifiers =
03457       [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
03458     const PRUint32 kModifierMaskTable[] =
03459       {NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask, NSCommandKeyMask};
03460     const PRUint32 kModifierCount = sizeof(kModifierMaskTable) /
03461                                     sizeof(kModifierMaskTable[0]);
03462 
03463     for(PRUint32 i = 0 ; i < kModifierCount ; i++) {
03464       PRUint32 modifierBit = kModifierMaskTable[i];
03465       if ((modifiers & modifierBit) != (mLastModifierState & modifierBit)) {
03466         PRUint32 message = ((modifiers & modifierBit) != 0 ? NS_KEY_DOWN :
03467                                                              NS_KEY_UP);
03468 
03469         // Fire a key event.
03470         nsKeyEvent geckoEvent(PR_TRUE, 0, nsnull);
03471         geckoEvent.point.x = geckoEvent.point.y = 0;
03472         [self convertKeyEvent:theEvent
03473                       message:message
03474                  toGeckoEvent:&geckoEvent];
03475 
03476         EventRecord macEvent;
03477         ConvertCocoaKeyEventToMacEvent(theEvent, macEvent, message);
03478         geckoEvent.nativeMsg = &macEvent;
03479         mGeckoChild->DispatchWindowEvent(geckoEvent);
03480 
03481         // Stop if focus has changed.
03482         // Check to see if we are still the first responder.
03483         NSResponder* resp = [[self window] firstResponder];
03484         if (resp != (NSResponder*)self)
03485           break;
03486       }
03487     }
03488 
03489     mLastModifierState = modifiers;
03490   }
03491 
03492   // check if the hand scroll cursor needs to be set/unset
03493   [self setHandScrollCursor:theEvent];
03494 }
03495 
03496 // This method is called when we are about to be focused.
03497 - (BOOL)becomeFirstResponder
03498 {
03499   if (!mGeckoChild) return NO;   // we've been destroyed
03500 
03501   nsFocusEvent event(PR_TRUE, NS_GOTFOCUS, mGeckoChild);
03502   mGeckoChild->DispatchWindowEvent(event);
03503 
03504   return [super becomeFirstResponder];
03505 }
03506 
03507 // This method is called when are are about to lose focus.
03508 - (BOOL)resignFirstResponder
03509 {
03510   if (!mGeckoChild) return NO;   // we've been destroyed
03511 
03512   nsFocusEvent event(PR_TRUE, NS_LOSTFOCUS, mGeckoChild);
03513   mGeckoChild->DispatchWindowEvent(event);
03514 
03515   return [super resignFirstResponder];
03516 }
03517 
03518 - (void)viewsWindowDidBecomeKey
03519 {
03520   if (!mGeckoChild)
03521     return;   // we've been destroyed (paranoia)
03522   
03523   // check to see if the window implements the mozWindow protocol. This
03524   // allows embedders to avoid re-entrant calls to -makeKeyAndOrderFront,
03525   // which can happen because these activate/focus calls propagate out
03526   // to the embedder via nsIEmbeddingSiteWindow::SetFocus().
03527   BOOL isMozWindow = [[self window] respondsToSelector:@selector(setSuppressMakeKeyFront:)];
03528   if (isMozWindow)
03529     [[self window] setSuppressMakeKeyFront:YES];
03530 
03531   nsFocusEvent focusEvent(PR_TRUE, NS_GOTFOCUS, mGeckoChild);
03532   mGeckoChild->DispatchWindowEvent(focusEvent);
03533 
03534   nsFocusEvent activateEvent(PR_TRUE, NS_ACTIVATE, mGeckoChild);
03535   mGeckoChild->DispatchWindowEvent(activateEvent);
03536 
03537   if (isMozWindow)
03538     [[self window] setSuppressMakeKeyFront:NO];
03539 }
03540 
03541 - (void)viewsWindowDidResignKey
03542 {
03543   if (!mGeckoChild)
03544     return;   // we've been destroyed
03545   
03546   nsFocusEvent deactivateEvent(PR_TRUE, NS_DEACTIVATE, mGeckoChild);
03547   mGeckoChild->DispatchWindowEvent(deactivateEvent);
03548 
03549   nsFocusEvent unfocusEvent(PR_TRUE, NS_LOSTFOCUS, mGeckoChild);
03550   mGeckoChild->DispatchWindowEvent(unfocusEvent);
03551 }
03552 
03553 //-------------------------------------------------------------------------
03554 //
03555 // ConvertMacToRaptorKeyCode
03556 //
03557 //-------------------------------------------------------------------------
03558 
03559 
03560 // Key code constants
03561 enum
03562 {
03563   kEscapeKeyCode      = 0x35,
03564   kCommandKeyCode     = 0x37,
03565   kShiftKeyCode       = 0x38,
03566   kCapsLockKeyCode    = 0x39,
03567   kControlKeyCode     = 0x3B,
03568   kOptionkeyCode      = 0x3A,   // left and right option keys
03569   kClearKeyCode       = 0x47,
03570   
03571   // function keys
03572   kF1KeyCode          = 0x7A,
03573   kF2KeyCode          = 0x78,
03574   kF3KeyCode          = 0x63,
03575   kF4KeyCode          = 0x76,
03576   kF5KeyCode          = 0x60,
03577   kF6KeyCode          = 0x61,
03578   kF7KeyCode          = 0x62,
03579   kF8KeyCode          = 0x64,
03580   kF9KeyCode          = 0x65,
03581   kF10KeyCode         = 0x6D,
03582   kF11KeyCode         = 0x67,
03583   kF12KeyCode         = 0x6F,
03584   kF13KeyCode         = 0x69,
03585   kF14KeyCode         = 0x6B,
03586   kF15KeyCode         = 0x71,
03587   
03588   kPrintScreenKeyCode = kF13KeyCode,
03589   kScrollLockKeyCode  = kF14KeyCode,
03590   kPauseKeyCode       = kF15KeyCode,
03591   
03592   // keypad
03593   kKeypad0KeyCode     = 0x52,
03594   kKeypad1KeyCode     = 0x53,
03595   kKeypad2KeyCode     = 0x54,
03596   kKeypad3KeyCode     = 0x55,
03597   kKeypad4KeyCode     = 0x56,
03598   kKeypad5KeyCode     = 0x57,
03599   kKeypad6KeyCode     = 0x58,
03600   kKeypad7KeyCode     = 0x59,
03601   kKeypad8KeyCode     = 0x5B,
03602   kKeypad9KeyCode     = 0x5C,
03603   
03604   kKeypadMultiplyKeyCode  = 0x43,
03605   kKeypadAddKeyCode       = 0x45,
03606   kKeypadSubtractKeyCode  = 0x4E,
03607   kKeypadDecimalKeyCode   = 0x41,
03608   kKeypadDivideKeyCode    = 0x4B,
03609   kKeypadEqualsKeyCode    = 0x51,     // no correpsonding raptor key code
03610   kEnterKeyCode           = 0x4C,
03611   kReturnKeyCode          = 0x24,
03612   kPowerbookEnterKeyCode  = 0x34,     // Enter on Powerbook's keyboard is different
03613   
03614   kInsertKeyCode          = 0x72,       // also help key
03615   kDeleteKeyCode          = 0x75,       // also forward delete key
03616   kTabKeyCode             = 0x30,
03617   kBackspaceKeyCode       = 0x33,
03618   kHomeKeyCode            = 0x73, 
03619   kEndKeyCode             = 0x77,
03620   kPageUpKeyCode          = 0x74,
03621   kPageDownKeyCode        = 0x79,
03622   kLeftArrowKeyCode       = 0x7B,
03623   kRightArrowKeyCode      = 0x7C,
03624   kUpArrowKeyCode         = 0x7E,
03625   kDownArrowKeyCode       = 0x7D
03626   
03627 };
03628 
03629 static PRUint32 ConvertMacToRaptorKeyCode(UInt32 keyCode, nsKeyEvent* aKeyEvent, NSString* characters)
03630 {
03631   PRUint32 raptorKeyCode = 0;
03632   PRUint8 charCode;
03633   if ([characters length])
03634     charCode = [characters characterAtIndex: 0];
03635   else
03636     charCode = 0;
03637 
03638   switch (keyCode)
03639   {
03640 //  case ??             :       raptorKeyCode = NS_VK_CANCEL;   break;      // don't know what this key means. Nor does joki
03641 
03642 // modifiers. We don't get separate events for these
03643     case kEscapeKeyCode:        raptorKeyCode = NS_VK_ESCAPE;         break;
03644     case kShiftKeyCode:         raptorKeyCode = NS_VK_SHIFT;          break;
03645     case kCommandKeyCode:       raptorKeyCode = NS_VK_META;           break;
03646     case kCapsLockKeyCode:      raptorKeyCode = NS_VK_CAPS_LOCK;      break;
03647     case kControlKeyCode:       raptorKeyCode = NS_VK_CONTROL;        break;
03648     case kOptionkeyCode:        raptorKeyCode = NS_VK_ALT;            break;
03649     case kClearKeyCode:         raptorKeyCode = NS_VK_CLEAR;          break;
03650 
03651 // function keys
03652     case kF1KeyCode:            raptorKeyCode = NS_VK_F1;             break;
03653     case kF2KeyCode:            raptorKeyCode = NS_VK_F2;             break;
03654     case kF3KeyCode:            raptorKeyCode = NS_VK_F3;             break;
03655     case kF4KeyCode:            raptorKeyCode = NS_VK_F4;             break;
03656     case kF5KeyCode:            raptorKeyCode = NS_VK_F5;             break;
03657     case kF6KeyCode:            raptorKeyCode = NS_VK_F6;             break;
03658     case kF7KeyCode:            raptorKeyCode = NS_VK_F7;             break;
03659     case kF8KeyCode:            raptorKeyCode = NS_VK_F8;             break;
03660     case kF9KeyCode:            raptorKeyCode = NS_VK_F9;             break;
03661     case kF10KeyCode:           raptorKeyCode = NS_VK_F10;            break;
03662     case kF11KeyCode:           raptorKeyCode = NS_VK_F11;            break;
03663     case kF12KeyCode:           raptorKeyCode = NS_VK_F12;            break;
03664 //  case kF13KeyCode:           raptorKeyCode = NS_VK_F13;            break;    // clash with the 3 below
03665 //  case kF14KeyCode:           raptorKeyCode = NS_VK_F14;            break;
03666 //  case kF15KeyCode:           raptorKeyCode = NS_VK_F15;            break;
03667     case kPauseKeyCode:         raptorKeyCode = NS_VK_PAUSE;          break;
03668     case kScrollLockKeyCode:    raptorKeyCode = NS_VK_SCROLL_LOCK;    break;
03669     case kPrintScreenKeyCode:   raptorKeyCode = NS_VK_PRINTSCREEN;    break;
03670   
03671 // keypad
03672     case kKeypad0KeyCode:       raptorKeyCode = NS_VK_NUMPAD0;        break;
03673     case kKeypad1KeyCode:       raptorKeyCode = NS_VK_NUMPAD1;        break;
03674     case kKeypad2KeyCode:       raptorKeyCode = NS_VK_NUMPAD2;        break;
03675     case kKeypad3KeyCode:       raptorKeyCode = NS_VK_NUMPAD3;        break;
03676     case kKeypad4KeyCode:       raptorKeyCode = NS_VK_NUMPAD4;        break;
03677     case kKeypad5KeyCode:       raptorKeyCode = NS_VK_NUMPAD5;        break;
03678     case kKeypad6KeyCode:       raptorKeyCode = NS_VK_NUMPAD6;        break;
03679     case kKeypad7KeyCode:       raptorKeyCode = NS_VK_NUMPAD7;        break;
03680     case kKeypad8KeyCode:       raptorKeyCode = NS_VK_NUMPAD8;        break;
03681     case kKeypad9KeyCode:       raptorKeyCode = NS_VK_NUMPAD9;        break;
03682 
03683     case kKeypadMultiplyKeyCode:  raptorKeyCode = NS_VK_MULTIPLY;     break;
03684     case kKeypadAddKeyCode:       raptorKeyCode = NS_VK_ADD;          break;
03685     case kKeypadSubtractKeyCode:  raptorKeyCode = NS_VK_SUBTRACT;     break;
03686     case kKeypadDecimalKeyCode:   raptorKeyCode = NS_VK_DECIMAL;      break;
03687     case kKeypadDivideKeyCode:    raptorKeyCode = NS_VK_DIVIDE;       break;
03688 //  case ??               :       raptorKeyCode = NS_VK_SEPARATOR;    break;
03689 
03690 
03691 // these may clash with forward delete and help
03692     case kInsertKeyCode:        raptorKeyCode = NS_VK_INSERT;         break;
03693     case kDeleteKeyCode:        raptorKeyCode = NS_VK_DELETE;         break;
03694 
03695     case kBackspaceKeyCode:     raptorKeyCode = NS_VK_BACK;           break;
03696     case kTabKeyCode:           raptorKeyCode = NS_VK_TAB;            break;
03697     case kHomeKeyCode:          raptorKeyCode = NS_VK_HOME;           break;
03698     case kEndKeyCode:           raptorKeyCode = NS_VK_END;            break;
03699     case kPageUpKeyCode:        raptorKeyCode = NS_VK_PAGE_UP;        break;
03700     case kPageDownKeyCode:      raptorKeyCode = NS_VK_PAGE_DOWN;      break;
03701     case kLeftArrowKeyCode:     raptorKeyCode = NS_VK_LEFT;           break;
03702     case kRightArrowKeyCode:    raptorKeyCode = NS_VK_RIGHT;          break;
03703     case kUpArrowKeyCode:       raptorKeyCode = NS_VK_UP;             break;
03704     case kDownArrowKeyCode:     raptorKeyCode = NS_VK_DOWN;           break;
03705 
03706     default:
03707         if (aKeyEvent->isControl)
03708           charCode += 64;
03709       
03710         // if we haven't gotten the key code already, look at the char code
03711         switch (charCode)
03712         {
03713           case kReturnCharCode:       raptorKeyCode = NS_VK_RETURN;       break;
03714           case kEnterCharCode:        raptorKeyCode = NS_VK_RETURN;       break;      // fix me!
03715           case ' ':                   raptorKeyCode = NS_VK_SPACE;        break;
03716           case ';':                   raptorKeyCode = NS_VK_SEMICOLON;    break;
03717           case '=':                   raptorKeyCode = NS_VK_EQUALS;       break;
03718           case ',':                   raptorKeyCode = NS_VK_COMMA;        break;
03719           case '.':                   raptorKeyCode = NS_VK_PERIOD;       break;
03720           case '/':                   raptorKeyCode = NS_VK_SLASH;        break;
03721           case '`':                   raptorKeyCode = NS_VK_BACK_QUOTE;   break;
03722           case '{':
03723           case '[':                   raptorKeyCode = NS_VK_OPEN_BRACKET; break;
03724           case '\\':                  raptorKeyCode = NS_VK_BACK_SLASH;   break;
03725           case '}':
03726           case ']':                   raptorKeyCode = NS_VK_CLOSE_BRACKET;  break;
03727           case '\'':
03728           case '"':                   raptorKeyCode = NS_VK_QUOTE;        break;
03729           
03730           default:
03731             
03732             if (charCode >= '0' && charCode <= '9')   // numerals
03733             {
03734               raptorKeyCode = charCode;
03735             }
03736             else if (charCode >= 'a' && charCode <= 'z')    // lowercase
03737             {
03738               raptorKeyCode = toupper(charCode);
03739             }
03740             else if (charCode >= 'A' && charCode <= 'Z')    // uppercase
03741             {
03742               raptorKeyCode = charCode;
03743             }
03744 
03745             break;
03746         }
03747   }
03748 
03749   return raptorKeyCode;
03750 }
03751 
03752 static PRBool IsSpecialRaptorKey(UInt32 macKeyCode)
03753 {
03754   PRBool  isSpecial;
03755 
03756   // 
03757   // this table is used to determine which keys are special and should not generate a charCode
03758   //  
03759   switch (macKeyCode)
03760   {
03761 // modifiers. We don't get separate events for these
03762 // yet
03763     case kEscapeKeyCode:        isSpecial = PR_TRUE; break;
03764     case kShiftKeyCode:         isSpecial = PR_TRUE; break;
03765     case kCommandKeyCode:       isSpecial = PR_TRUE; break;
03766     case kCapsLockKeyCode:      isSpecial = PR_TRUE; break;
03767     case kControlKeyCode:       isSpecial = PR_TRUE; break;
03768     case kOptionkeyCode:        isSpecial = PR_TRUE; break;
03769     case kClearKeyCode:         isSpecial = PR_TRUE; break;
03770 
03771 // function keys
03772     case kF1KeyCode:            isSpecial = PR_TRUE; break;
03773     case kF2KeyCode:            isSpecial = PR_TRUE; break;
03774     case kF3KeyCode:            isSpecial = PR_TRUE; break;
03775     case kF4KeyCode:            isSpecial = PR_TRUE; break;
03776     case kF5KeyCode:            isSpecial = PR_TRUE; break;
03777     case kF6KeyCode:            isSpecial = PR_TRUE; break;
03778     case kF7KeyCode:            isSpecial = PR_TRUE; break;
03779     case kF8KeyCode:            isSpecial = PR_TRUE; break;
03780     case kF9KeyCode:            isSpecial = PR_TRUE; break;
03781     case kF10KeyCode:           isSpecial = PR_TRUE; break;
03782     case kF11KeyCode:           isSpecial = PR_TRUE; break;
03783     case kF12KeyCode:           isSpecial = PR_TRUE; break;
03784     case kPauseKeyCode:         isSpecial = PR_TRUE; break;
03785     case kScrollLockKeyCode:    isSpecial = PR_TRUE; break;
03786     case kPrintScreenKeyCode:   isSpecial = PR_TRUE; break;
03787 
03788     case kInsertKeyCode:        isSpecial = PR_TRUE; break;
03789     case kDeleteKeyCode:        isSpecial = PR_TRUE; break;
03790     case kTabKeyCode:           isSpecial = PR_TRUE; break;
03791     case kBackspaceKeyCode:     isSpecial = PR_TRUE; break;
03792 
03793     case kHomeKeyCode:          isSpecial = PR_TRUE; break; 
03794     case kEndKeyCode:           isSpecial = PR_TRUE; break;
03795     case kPageUpKeyCode:        isSpecial = PR_TRUE; break;
03796     case kPageDownKeyCode:      isSpecial = PR_TRUE; break;
03797     case kLeftArrowKeyCode:     isSpecial = PR_TRUE; break;
03798     case kRightArrowKeyCode:    isSpecial = PR_TRUE; break;
03799     case kUpArrowKeyCode:       isSpecial = PR_TRUE; break;
03800     case kDownArrowKeyCode:     isSpecial = PR_TRUE; break;
03801     case kReturnKeyCode:        isSpecial = PR_TRUE; break;
03802     case kEnterKeyCode:         isSpecial = PR_TRUE; break;
03803     case kPowerbookEnterKeyCode: isSpecial = PR_TRUE; break;
03804 
03805     default:                    isSpecial = PR_FALSE; break;
03806   }
03807   return isSpecial;
03808 }
03809 
03810 - (void) convertKeyEvent:(NSEvent*)aKeyEvent message:(PRUint32)aMessage 
03811             toGeckoEvent:(nsKeyEvent*)outGeckoEvent
03812 {
03813   [self convertEvent:aKeyEvent message:aMessage toGeckoEvent:outGeckoEvent];
03814 
03815   // Initialize whether or not we are using charCodes to false.
03816   outGeckoEvent->isChar = PR_FALSE;
03817     
03818   // Check to see if the message is a key press that does not involve
03819   // one of our special key codes.
03820   if (aMessage == NS_KEY_PRESS && !IsSpecialRaptorKey([aKeyEvent keyCode])) 
03821   {
03822     if (!outGeckoEvent->isControl && !outGeckoEvent->isMeta)
03823       outGeckoEvent->isControl = outGeckoEvent->isAlt = outGeckoEvent->isMeta = 0;
03824     
03825     outGeckoEvent->charCode = 0;
03826     outGeckoEvent->keyCode  = 0;
03827 
03828     NSString* unmodifiedChars = [aKeyEvent charactersIgnoringModifiers];
03829     if ([unmodifiedChars length] > 0)
03830       outGeckoEvent->charCode = [unmodifiedChars characterAtIndex:0];
03831     
03832     // We're not a special key.
03833     outGeckoEvent->isChar = PR_TRUE;
03834 
03835     // convert control-modified charCode to raw charCode (with appropriate case)
03836     if (outGeckoEvent->isControl && outGeckoEvent->charCode <= 26)
03837       outGeckoEvent->charCode += (outGeckoEvent->isShift) ? ('A' - 1) : ('a' - 1);
03838 
03839     // gecko also wants charCode to be in the appropriate case
03840     if (outGeckoEvent->isShift && (outGeckoEvent->charCode >= 'a' && outGeckoEvent->charCode <= 'z'))
03841       outGeckoEvent->charCode -= 32;    // convert to uppercase
03842   }
03843   else
03844   {
03845     NSString* characters = nil;
03846     if ([aKeyEvent type] != NSFlagsChanged)
03847       characters = [aKeyEvent characters];
03848   
03849     outGeckoEvent->keyCode = ConvertMacToRaptorKeyCode([aKeyEvent keyCode], outGeckoEvent, characters);
03850     outGeckoEvent->charCode = 0;
03851   } 
03852   
03853   if (aMessage == NS_KEY_PRESS && !outGeckoEvent->isMeta)
03854     ::ObscureCursor();
03855 }
03856 
03857 //
03858 // -_destinationFloatValueForScroller
03859 //
03860 // When smooth scrolling is turned on on panther, the parent of a scrollbar (which
03861 // I guess they assume is a NSScrollView) gets called with this method. I have no
03862 // idea what the correct return value is, but we have to have this otherwise the scrollbar
03863 // will not continuously respond when the mouse is held down in the pageup/down area.
03864 //
03865 -(float)_destinationFloatValueForScroller:(id)scroller
03866 {
03867   return [scroller floatValue];
03868 }
03869 
03870 @end