1 /* -*- Mode: objc; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License
6 * Version 1.1 (the "License"); you may not use this file except in
7 * compliance with the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Josh Aas <josh@mozilla.com>
24 * Mark Mentovai <mark@moxienet.com>
25 * HÃ¥kan Waara <hwaara@gmail.com>
26 * Stuart Morgan <stuart.morgan@alumni.case.edu>
27 * Mats Palmgren <mats.palmgren@bredband.net>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either the GNU General Public License Version 2 or later (the "GPL"), or
31 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
45 #include "nsChildView.h"
46 #include "nsCocoaWindow.h"
48 #include "nsObjCExceptions.h"
50 #include "nsToolkit.h"
52 #include "nsplugindefs.h"
53 #include "nsIPrefService.h"
54 #include "nsIPrefBranch.h"
56 #include "nsIFontMetrics.h"
57 #include "nsIDeviceContext.h"
58 #include "nsIRegion.h"
59 #include "nsIRollupListener.h"
60 #include "nsIScrollableView.h"
61 #include "nsIViewManager.h"
62 #include "nsIInterfaceRequestor.h"
63 #include "nsIServiceManager.h"
64 #include "nsILocalFile.h"
65 #include "nsILocalFileMac.h"
66 #include "nsGfxCIID.h"
67 #include "nsIMenuRollup.h"
69 #include "nsDragService.h"
70 #include "nsCursorManager.h"
71 #include "nsWindowMap.h"
72 #include "nsCocoaUtils.h"
73 #include "nsMenuBarX.h"
75 #include "gfxContext.h"
76 #include "gfxQuartzSurface.h"
82 #undef INVALIDATE_DEBUGGING // flash areas as they are invalidated
90 PRLogModuleInfo* sCocoaLog = nsnull;
93 // npapi.h defines NPEventType_AdjustCursorEvent but we don't want to include npapi.h here.
94 // We need to send this in the "what" field for certain native plugin events. WebKit does
96 #define adjustCursorEvent 33
99 CG_EXTERN void CGContextResetCTM(CGContextRef);
100 CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
101 CG_EXTERN void CGContextResetClip(CGContextRef);
104 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
105 struct __TISInputSource;
106 typedef __TISInputSource* TISInputSourceRef;
108 TISInputSourceRef (*Leopard_TISCopyCurrentKeyboardLayoutInputSource)() = NULL;
109 void* (*Leopard_TISGetInputSourceProperty)(TISInputSourceRef inputSource, CFStringRef propertyKey) = NULL;
110 CFArrayRef (*Leopard_TISCreateInputSourceList)(CFDictionaryRef properties, Boolean includeAllInstalled) = NULL;
111 CFStringRef kOurTISPropertyUnicodeKeyLayoutData = NULL;
112 CFStringRef kOurTISPropertyInputSourceID = NULL;
114 extern PRBool gCocoaWindowMethodsSwizzled; // Defined in nsCocoaWindow.mm
116 extern nsISupportsArray *gDraggedTransferables;
118 PRBool nsTSMManager::sIsIMEEnabled = PR_TRUE;
119 PRBool nsTSMManager::sIsRomanKeyboardsOnly = PR_FALSE;
120 PRBool nsTSMManager::sIgnoreCommit = PR_FALSE;
121 NSView<mozView>* nsTSMManager::sComposingView = nsnull;
122 TSMDocumentID nsTSMManager::sDocumentID = nsnull;
123 NSString* nsTSMManager::sComposingString = nsnull;
125 static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
126 static NSView* sLastViewEntered = nil;
127 #ifdef INVALIDATE_DEBUGGING
128 static void blinkRect(Rect* r);
129 static void blinkRgn(RgnHandle rgn);
132 nsIRollupListener * gRollupListener = nsnull;
133 nsIWidget * gRollupWidget = nsnull;
136 @interface ChildView(Private)
138 // sets up our view, attaching it to its owning gecko view
139 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
141 // sends gecko an ime composition event
142 - (nsRect) sendCompositionEvent:(PRInt32)aEventType;
144 // sends gecko an ime text event
145 - (void) sendTextEvent:(PRUnichar*) aBuffer
146 attributedString:(NSAttributedString*) aString
147 selectedRange:(NSRange)selRange
148 markedRange:(NSRange)markRange
149 doCommit:(BOOL)doCommit;
151 // do generic gecko event setup with a generic cocoa event. accepts nil inEvent.
152 - (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent;
154 // set up a gecko mouse event based on a cocoa mouse event
155 - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent;
157 // set up a gecko key event based on a cocoa key event
158 - (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent;
160 - (NSMenu*)contextMenu;
161 - (TopLevelWindowData*)ensureWindowData;
163 - (void)setIsPluginView:(BOOL)aIsPlugin;
164 - (BOOL)isPluginView;
166 - (BOOL)childViewHasPlugin;
168 - (BOOL)isRectObscuredBySubview:(NSRect)inRect;
170 - (void)processPendingRedraws;
172 - (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv;
174 - (BOOL)ensureCorrectMouseEventTarget:(NSEvent *)anEvent;
176 - (void)maybeInitContextMenuTracking;
178 + (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent;
180 #if USE_CLICK_HOLD_CONTEXTMENU
181 // called on a timer two seconds after a mouse down to see if we should display
182 // a context menu (click-hold)
183 - (void)clickHoldCallback:(id)inEvent;
187 - (id<mozAccessible>)accessible;
196 /* Convenience routines to go from a gecko rect to cocoa NSRects and back
198 * Gecko rects (nsRect) contain an origin (x,y) in a coordinate
199 * system with (0,0) in the top-left of the screen. Cocoa rects
200 * (NSRect) contain an origin (x,y) in a coordinate system with
201 * (0,0) in the bottom-left of the screen. Both nsRect and NSRect
202 * contain width/height info, with no difference in their use.
203 * If a Cocoa rect is from a flipped view, there is no need to
204 * convert coordinate systems.
209 GeckoRectToNSRect(const nsRect & inGeckoRect, NSRect & outCocoaRect)
211 outCocoaRect.origin.x = inGeckoRect.x;
212 outCocoaRect.origin.y = inGeckoRect.y;
213 outCocoaRect.size.width = inGeckoRect.width;
214 outCocoaRect.size.height = inGeckoRect.height;
218 NSRectToGeckoRect(const NSRect & inCocoaRect, nsRect & outGeckoRect)
220 outGeckoRect.x = static_cast<nscoord>(inCocoaRect.origin.x);
221 outGeckoRect.y = static_cast<nscoord>(inCocoaRect.origin.y);
222 outGeckoRect.width = static_cast<nscoord>(inCocoaRect.size.width);
223 outGeckoRect.height = static_cast<nscoord>(inCocoaRect.size.height);
228 ConvertGeckoRectToMacRect(const nsRect& aRect, Rect& outMacRect)
230 outMacRect.left = aRect.x;
231 outMacRect.top = aRect.y;
232 outMacRect.right = aRect.x + aRect.width;
233 outMacRect.bottom = aRect.y + aRect.height;
236 // Flips a screen coordinate from a point in the cocoa coordinate system (bottom-left rect) to a point
237 // that is a "flipped" cocoa coordinate system (starts in the top-left).
239 FlipCocoaScreenCoordinate (NSPoint &inPoint)
241 inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y);
246 UnderlineAttributeToTextRangeType(PRUint32 aUnderlineStyle, NSRange selRange)
249 NSLog(@"****in underlineAttributeToTextRangeType = %d", aUnderlineStyle);
252 // For more info on the underline attribute, please see:
253 // http://developer.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/ProgrammingTopics/AttributedStrings/Tasks/AccessingAttrs.html
254 // We are not clear where the define for value 2 is right now.
255 // To see this value in japanese ime, type 'aaaaaaaaa' and hit space to make the
256 // ime send you some part of text in 1 (NSSingleUnderlineStyle) and some part in 2.
257 // ftang will ask apple for more details
259 // it probably means show 1-pixel thickness underline vs 2-pixel thickness
262 if (selRange.length == 0) {
263 switch (aUnderlineStyle) {
265 attr = NS_TEXTRANGE_RAWINPUT;
269 attr = NS_TEXTRANGE_SELECTEDRAWTEXT;
274 switch (aUnderlineStyle) {
276 attr = NS_TEXTRANGE_CONVERTEDTEXT;
280 attr = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
289 CountRanges(NSAttributedString *aString)
291 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
293 // Iterate through aString for the NSUnderlineStyleAttributeName and count the
294 // different segments adjusting limitRange as we go.
296 NSRange effectiveRange;
297 NSRange limitRange = NSMakeRange(0, [aString length]);
298 while (limitRange.length > 0) {
299 [aString attribute:NSUnderlineStyleAttributeName
300 atIndex:limitRange.location
301 longestEffectiveRange:&effectiveRange
303 limitRange = NSMakeRange(NSMaxRange(effectiveRange),
304 NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
309 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
314 ConvertAttributeToGeckoRange(NSAttributedString *aString, NSRange markRange, NSRange selRange, PRUint32 inCount, nsTextRange* aRanges)
316 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
318 // Convert the Cocoa range into the nsTextRange Array used in Gecko.
319 // Iterate through the attributed string and map the underline attribute to Gecko IME textrange attributes.
320 // We may need to change the code here if we change the implementation of validAttributesForMarkedText.
322 NSRange effectiveRange;
323 NSRange limitRange = NSMakeRange(0, [aString length]);
324 while ((limitRange.length > 0) && (i < inCount)) {
325 id attributeValue = [aString attribute:NSUnderlineStyleAttributeName
326 atIndex:limitRange.location
327 longestEffectiveRange:&effectiveRange
329 aRanges[i].mStartOffset = effectiveRange.location;
330 aRanges[i].mEndOffset = NSMaxRange(effectiveRange);
331 aRanges[i].mRangeType = UnderlineAttributeToTextRangeType([attributeValue intValue], selRange);
332 limitRange = NSMakeRange(NSMaxRange(effectiveRange),
333 NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
336 // Get current caret position.
337 // Caret is indicator of insertion point, so mEndOffset = 0.
338 aRanges[i].mStartOffset = selRange.location + selRange.length;
339 aRanges[i].mEndOffset = 0;
340 aRanges[i].mRangeType = NS_TEXTRANGE_CARETPOSITION;
342 NS_OBJC_END_TRY_ABORT_BLOCK;
347 FillTextRangeInTextEvent(nsTextEvent *aTextEvent, NSAttributedString* aString, NSRange markRange, NSRange selRange)
349 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
351 // Count the number of segments in the attributed string and add one more count for sending current caret position to Gecko.
352 // Allocate the right size of nsTextRange and draw caret at right position.
353 // Convert the attributed string into an array of nsTextRange and get current caret position by calling above functions.
354 PRUint32 count = CountRanges(aString) + 1;
355 aTextEvent->rangeArray = new nsTextRange[count];
356 if (aTextEvent->rangeArray) {
357 aTextEvent->rangeCount = count;
358 ConvertAttributeToGeckoRange(aString, markRange, selRange, aTextEvent->rangeCount, aTextEvent->rangeArray);
361 NS_OBJC_END_TRY_ABORT_BLOCK;
367 nsChildView::nsChildView() : nsBaseWidget()
369 , mParentView(nsnull)
370 , mParentWidget(nsnull)
373 , mLiveResizeInProgress(PR_FALSE)
374 , mIsPluginView(PR_FALSE)
375 , mPluginDrawing(PR_FALSE)
376 , mPluginIsCG(PR_FALSE)
377 , mInSetFocus(PR_FALSE)
381 sCocoaLog = PR_NewLogModule("nsCocoaWidgets");
383 KLGetKeyboardLayoutCount(&idx);
384 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("Keyboard layout configuration:"));
385 for (CFIndex i = 0; i < idx; ++i) {
386 KeyboardLayoutRef curKL;
387 if (KLGetKeyboardLayoutAtIndex(i, &curKL) == noErr) {
389 if (KLGetKeyboardLayoutProperty(curKL, kKLName, (const void**)&name) == noErr) {
391 KLGetKeyboardLayoutProperty(curKL, kKLIdentifier, (const void**)&idn);
393 KLGetKeyboardLayoutProperty(curKL, kKLKind, (const void**)&kind);
395 CFStringGetCString(name, buf, 256, kCFStringEncodingASCII);
396 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, (" %d,%s,%d\n", idn, buf, kind));
403 SetBackgroundColor(NS_RGB(255, 255, 255));
404 SetForegroundColor(NS_RGB(0, 0, 0));
406 if (nsToolkit::OnLeopardOrLater() && !Leopard_TISCopyCurrentKeyboardLayoutInputSource) {
407 // This libary would already be open for LMGetKbdType (and probably other
408 // symbols), so merely using RTLD_DEFAULT in dlsym would be sufficient,
409 // but man dlsym says: "all mach-o images in the process (except ...) are
410 // searched in the order they were loaded. This can be a costly search
411 // and should be avoided."
412 void* hitoolboxHandle = dlopen("/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/Versions/A/HIToolbox", RTLD_LAZY);
413 if (hitoolboxHandle) {
414 *(void **)(&Leopard_TISCopyCurrentKeyboardLayoutInputSource) = dlsym(hitoolboxHandle, "TISCopyCurrentKeyboardLayoutInputSource");
415 *(void **)(&Leopard_TISGetInputSourceProperty) = dlsym(hitoolboxHandle, "TISGetInputSourceProperty");
416 *(void **)(&Leopard_TISCreateInputSourceList) = dlsym(hitoolboxHandle, "TISCreateInputSourceList");
417 kOurTISPropertyUnicodeKeyLayoutData = *static_cast<CFStringRef*>(dlsym(hitoolboxHandle, "kTISPropertyUnicodeKeyLayoutData"));
418 kOurTISPropertyInputSourceID = *static_cast<CFStringRef*>(dlsym(hitoolboxHandle, "kTISPropertyInputSourceID"));
424 nsChildView::~nsChildView()
426 // notify the children that we're gone
427 for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
428 nsChildView* childView = static_cast<nsChildView*>(kid);
429 childView->mParentWidget = nsnull;
432 NS_WARN_IF_FALSE(mOnDestroyCalled, "nsChildView object destroyed without calling Destroy()");
434 // An nsChildView object that was in use can be destroyed without Destroy()
435 // ever being called on it. So we also need to do a quick, safe cleanup
436 // here (it's too late to just call Destroy(), which can cause crashes).
437 // It's particularly important to make sure widgetDestroyed is called on our
438 // mView -- this method NULLs mView's mGeckoChild, and NULL checks on
439 // mGeckoChild are used throughout the ChildView class to tell if it's safe
440 // to use a ChildView object.
441 [mView widgetDestroyed]; // Safe if mView is nil.
443 TearDownView(); // Safe if called twice.
447 NS_IMPL_ISUPPORTS_INHERITED1(nsChildView, nsBaseWidget, nsIPluginWidget)
450 // Utility method for implementing both Create(nsIWidget ...)
451 // and Create(nsNativeWidget...)
452 nsresult nsChildView::StandardCreate(nsIWidget *aParent,
454 EVENT_CALLBACK aHandleEventFunction,
455 nsIDeviceContext *aContext,
456 nsIAppShell *aAppShell,
457 nsIToolkit *aToolkit,
458 nsWidgetInitData *aInitData,
459 nsNativeWidget aNativeParent)
461 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
463 // See NSWindow (MethodSwizzling) in nsCocoaWindow.mm.
464 if (!gCocoaWindowMethodsSwizzled) {
465 nsToolkit::SwizzleMethods([NSWindow class], @selector(sendEvent:),
466 @selector(nsCocoaWindow_NSWindow_sendEvent:));
467 gCocoaWindowMethodsSwizzled = PR_TRUE;
472 BaseCreate(aParent, aRect, aHandleEventFunction,
473 aContext, aAppShell, aToolkit, aInitData);
475 // inherit things from the parent view and create our parallel
476 // NSView in the Cocoa display system
479 SetBackgroundColor(aParent->GetBackgroundColor());
480 SetForegroundColor(aParent->GetForegroundColor());
482 // inherit the top-level window. NS_NATIVE_WIDGET is always a NSView
483 // regardless of if we're asking a window or a view (for compatibility
485 mParentView = (NSView*)aParent->GetNativeData(NS_NATIVE_WIDGET);
486 mParentWidget = aParent;
489 mParentView = reinterpret_cast<NSView*>(aNativeParent);
491 // create our parallel NSView and hook it up to our parent. Recall
492 // that NS_NATIVE_WIDGET is the NSView.
494 GeckoRectToNSRect(mBounds, r);
495 mView = [CreateCocoaView(r) retain];
496 if (!mView) return NS_ERROR_FAILURE;
499 // if our parent is a popup window, we're most certainly coming from a <select> list dropdown which
500 // we handle in a different way than other platforms. It's ok that we don't have a parent
501 // view because we bailed before even creating the cocoa widgetry and as a result, we
502 // don't need to assert. However, if that's not the case, we definitely want to assert
503 // to show views aren't getting correctly parented.
505 nsWindowType windowType;
506 aParent->GetWindowType(windowType);
507 if (windowType != eWindowType_popup)
508 NS_ASSERTION(mParentView && mView, "couldn't hook up new NSView in hierarchy");
511 NS_ASSERTION(mParentView && mView, "couldn't hook up new NSView in hierarchy");
515 // If this view was created in a Gecko view hierarchy, the initial state
516 // is hidden. If the view is attached only to a native NSView but has
517 // no Gecko parent (as in embedding), the initial state is visible.
519 [mView setHidden:YES];
523 // Hook it up in the NSView hierarchy.
525 NSWindow* window = [mParentView window];
527 [mParentView respondsToSelector:@selector(nativeWindow)])
528 window = [mParentView nativeWindow];
530 [mView setNativeWindow:window];
532 [mParentView addSubview:mView];
535 // if this is a ChildView, make sure that our per-window data
537 if ([mView isKindOfClass:[ChildView class]])
538 [(ChildView*)mView ensureWindowData];
542 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
546 // Creates the appropriate child view. Override to create something other than
547 // our |ChildView| object. Autoreleases, so caller must retain.
549 nsChildView::CreateCocoaView(NSRect inFrame)
551 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
553 return [[[ChildView alloc] initWithFrame:inFrame geckoChild:this] autorelease];
555 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
559 void nsChildView::TearDownView()
561 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
566 NSWindow* win = [mView window];
567 NSResponder* responder = [win firstResponder];
569 // We're being unhooked from the view hierarchy, don't leave our view
570 // or a child view as the window first responder.
571 if (responder && [responder isKindOfClass:[NSView class]] &&
572 [(NSView*)responder isDescendantOf:mView]) {
573 [win makeFirstResponder:[mView superview]];
576 // If mView is win's contentView, win (mView's NSWindow) "owns" mView --
577 // win has retained mView, and will detach it from the view hierarchy and
578 // release it when necessary (when win is itself destroyed (in a call to
579 // [win dealloc])). So all we need to do here is call [mView release] (to
580 // match the call to [mView retain] in nsChildView::StandardCreate()).
581 // Also calling [mView removeFromSuperviewWithoutNeedingDisplay] causes
582 // mView to be released again and dealloced, while remaining win's
583 // contentView. So if we do that here, win will (for a short while) have
584 // an invalid contentView (for the consequences see bmo bugs 381087 and
586 if ([mView isEqual:[win contentView]]) {
589 // Stop NSView hierarchy being changed during [ChildView drawRect:]
590 [mView performSelectorOnMainThread:@selector(delayedTearDown) withObject:nil waitUntilDone:false];
594 NS_OBJC_END_TRY_ABORT_BLOCK;
598 // create a nsChildView
599 NS_IMETHODIMP nsChildView::Create(nsIWidget *aParent,
601 EVENT_CALLBACK aHandleEventFunction,
602 nsIDeviceContext *aContext,
603 nsIAppShell *aAppShell,
604 nsIToolkit *aToolkit,
605 nsWidgetInitData *aInitData)
607 return(StandardCreate(aParent, aRect, aHandleEventFunction, aContext,
608 aAppShell, aToolkit, aInitData, nsnull));
612 // Creates a main nsChildView using a native widget (an NSView)
613 NS_IMETHODIMP nsChildView::Create(nsNativeWidget aNativeParent,
615 EVENT_CALLBACK aHandleEventFunction,
616 nsIDeviceContext *aContext,
617 nsIAppShell *aAppShell,
618 nsIToolkit *aToolkit,
619 nsWidgetInitData *aInitData)
621 // what we're passed in |aNativeParent| is an NSView.
622 return(StandardCreate(nsnull, aRect, aHandleEventFunction, aContext,
623 aAppShell, aToolkit, aInitData, aNativeParent));
627 NS_IMETHODIMP nsChildView::Destroy()
629 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
631 if (mOnDestroyCalled)
633 mOnDestroyCalled = PR_TRUE;
635 [mView widgetDestroyed];
637 nsBaseWidget::OnDestroy();
638 nsBaseWidget::Destroy();
640 ReportDestroyEvent();
647 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
655 static void PrintViewHierarchy(NSView *view)
658 NSLog(@" view is %x, frame %@", view, NSStringFromRect([view frame]));
659 view = [view superview];
666 // Return native data according to aDataType
667 void* nsChildView::GetNativeData(PRUint32 aDataType)
669 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
671 void* retVal = nsnull;
675 case NS_NATIVE_WIDGET:
676 case NS_NATIVE_DISPLAY:
677 retVal = (void*)mView;
680 case NS_NATIVE_WINDOW:
681 retVal = [mView nativeWindow];
684 case NS_NATIVE_GRAPHIC:
685 NS_ASSERTION(0, "Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!");
689 case NS_NATIVE_OFFSETX:
693 case NS_NATIVE_OFFSETY:
697 case NS_NATIVE_PLUGIN_PORT:
698 #ifndef NP_NO_QUICKDRAW
699 case NS_NATIVE_PLUGIN_PORT_QD:
701 mPluginIsCG = PR_FALSE;
702 mIsPluginView = PR_TRUE;
703 if ([mView isKindOfClass:[ChildView class]])
704 [(ChildView*)mView setIsPluginView:YES];
706 NSWindow* window = [mView nativeWindow];
708 WindowRef topLevelWindow = (WindowRef)[window windowRef];
709 if (topLevelWindow) {
710 mPluginPort.qdPort.port = ::GetWindowPort(topLevelWindow);
712 NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
713 NSRect frame = [[window contentView] frame];
714 viewOrigin.y = frame.size.height - viewOrigin.y;
716 // need to convert view's origin to window coordinates.
717 // then, encode as "SetOrigin" ready values.
718 mPluginPort.qdPort.portx = (PRInt32)-viewOrigin.x;
719 mPluginPort.qdPort.porty = (PRInt32)-viewOrigin.y;
723 retVal = (void*)&mPluginPort;
728 case NS_NATIVE_PLUGIN_PORT_CG:
730 mPluginIsCG = PR_TRUE;
731 mIsPluginView = PR_TRUE;
732 if ([mView isKindOfClass:[ChildView class]])
733 [(ChildView*)mView setIsPluginView:YES];
735 mPluginPort.cgPort.context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
737 NSWindow* window = [mView nativeWindow];
739 WindowRef topLevelWindow = (WindowRef)[window windowRef];
740 mPluginPort.cgPort.window = topLevelWindow;
743 retVal = (void*)&mPluginPort;
750 NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
755 nsTransparencyMode nsChildView::GetTransparencyMode()
757 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
759 return [mView isOpaque] ? eTransparencyOpaque : eTransparencyTransparent;
761 NS_OBJC_END_TRY_ABORT_BLOCK;
762 return eTransparencyOpaque;
766 // This is called by nsContainerFrame on the root widget for all window types
767 // except popup windows (when nsCocoaWindow::SetTransparencyMode is used instead).
768 void nsChildView::SetTransparencyMode(nsTransparencyMode aMode)
770 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
772 BOOL isTransparent = aMode == eTransparencyTransparent;
773 BOOL currentTransparency = ![[mView nativeWindow] isOpaque];
774 if (isTransparent != currentTransparency) {
775 // Find out if this is a window we created by seeing if the delegate is WindowDelegate. If it is,
776 // tell the nsCocoaWindow to set its background to transparent.
777 id windowDelegate = [[mView nativeWindow] delegate];
778 if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
779 nsCocoaWindow *widget = [(WindowDelegate *)windowDelegate geckoWidget];
781 widget->MakeBackgroundTransparent(aMode);
782 [(ChildView*)mView setTransparent:isTransparent];
787 NS_OBJC_END_TRY_ABORT_BLOCK;
791 NS_IMETHODIMP nsChildView::IsVisible(PRBool& outState)
793 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
799 // mVisible does not accurately reflect the state of a hidden tabbed view
800 // so verify that the view has a window as well
801 outState = ([mView window] != nil);
802 // now check native widget hierarchy visibility
803 if (outState && NSIsEmptyRect([mView visibleRect])) {
810 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
814 void nsChildView::HidePlugin()
816 NS_ASSERTION(mIsPluginView, "HidePlugin called on non-plugin view");
818 if (mPluginInstanceOwner && !mPluginIsCG) {
819 nsPluginWindow* window;
820 mPluginInstanceOwner->GetWindow(window);
821 nsCOMPtr<nsIPluginInstance> instance;
822 mPluginInstanceOwner->GetInstance(*getter_AddRefs(instance));
823 if (window && instance) {
824 window->clipRect.top = 0;
825 window->clipRect.left = 0;
826 window->clipRect.bottom = 0;
827 window->clipRect.right = 0;
828 instance->SetWindow(window);
834 static void HideChildPluginViews(NSView* aView)
836 NSArray* subviews = [aView subviews];
838 for (unsigned int i = 0; i < [subviews count]; ++i) {
839 NSView* view = [subviews objectAtIndex: i];
841 if (![view isKindOfClass:[ChildView class]])
844 ChildView* childview = static_cast<ChildView*>(view);
845 if ([childview isPluginView]) {
846 nsChildView* widget = static_cast<nsChildView*>([childview widget]);
848 widget->HidePlugin();
851 HideChildPluginViews(view);
857 // Hide or show this component
858 NS_IMETHODIMP nsChildView::Show(PRBool aState)
860 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
862 if (aState != mVisible) {
863 [mView setHidden:!aState];
866 HideChildPluginViews(mView);
870 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
875 nsChildView::GetParent(void)
877 return mParentWidget;
881 NS_IMETHODIMP nsChildView::ModalEventFilter(PRBool aRealEvent, void *aEvent,
885 *aForWindow = PR_FALSE;
886 return NS_ERROR_NOT_IMPLEMENTED;
890 NS_IMETHODIMP nsChildView::Enable(PRBool aState)
896 NS_IMETHODIMP nsChildView::IsEnabled(PRBool *aState)
905 NS_IMETHODIMP nsChildView::SetFocus(PRBool aRaise)
907 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
909 // Don't do anything if SetFocus() has been called reentrantly on the same
910 // object. Sometimes calls to nsChildView::DispatchEvent() can get
911 // temporarily stuck, causing calls to [ChildView sendFocusEvent:] and
912 // SetFocus() to be reentered. These reentrant calls are probably the
913 // result of one or more bugs, and doing things on a reentrant call can
914 // cause problems: For example if mView is already the first responder and
915 // we send it an NS_GOTFOCUS event (see below), this causes the Mochitests
916 // to get stuck in the toolkit/content/tests/widgets/test_popup_button.xul
920 mInSetFocus = PR_TRUE;
921 NSWindow* window = [mView window];
923 nsAutoRetainCocoaObject kungFuDeathGrip(mView);
924 // For reasons that aren't yet clear, focus changes within a window (as
925 // opposed to those between windows or between apps) should only trigger
926 // NS_LOSTFOCUS and NS_GOTFOCUS events (sent to Gecko) in the context of
927 // a call to nsChildView::SetFocus() (or nsCocoaWindow::SetFocus(), which
928 // in any case re-routes to nsChildView::SetFocus()). If we send these
929 // events on every intra-window focus change (on every call to
930 // [ChildView becomeFirstResponder:] or [ChildView resignFirstResponder:]),
931 // the result will be strange focus bugs (like bmo bugs 399471, 403232,
932 // 404433 and 408266).
933 NSResponder* firstResponder = [window firstResponder];
934 if ([mView isEqual:firstResponder]) {
935 // Sometimes SetFocus() is called on an nsChildView object that's
936 // already focused. In principle this shouldn't happen, and in any
937 // case we shouldn't have to dispatch any events. But if we don't, we
938 // sometimes get text-input cursors blinking in more than one text
939 // field, or still blinking when the browser is no longer active. For
940 // reasons that aren't at all clear, this problem can be avoided by
941 // always sending an NS_GOTFOCUS message here.
942 if ([mView isKindOfClass:[ChildView class]])
943 [(ChildView *)mView sendFocusEvent:NS_GOTFOCUS];
945 // Retain and release firstResponder around the call to
946 // makeFirstResponder.
947 [firstResponder retain];
948 if ([window makeFirstResponder:mView]) {
949 if ([firstResponder isKindOfClass:[ChildView class]])
950 [(ChildView *)firstResponder sendFocusEvent:NS_LOSTFOCUS];
951 if ([mView isKindOfClass:[ChildView class]])
952 [(ChildView *)mView sendFocusEvent:NS_GOTFOCUS];
954 [firstResponder release];
957 mInSetFocus = PR_FALSE;
960 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
964 // Set the colormap of the window
965 NS_IMETHODIMP nsChildView::SetColorMap(nsColorMap *aColorMap)
971 NS_IMETHODIMP nsChildView::SetMenuBar(void* aMenuBar)
973 return NS_ERROR_FAILURE; // subviews don't have menu bars
977 NS_IMETHODIMP nsChildView::ShowMenuBar(PRBool aShow)
979 return NS_ERROR_FAILURE; // subviews don't have menu bars
983 // Override to set the cursor on the mac
984 NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor)
986 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
988 nsBaseWidget::SetCursor(aCursor);
989 [[nsCursorManager sharedInstance] setCursor: aCursor];
992 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
996 // implement to fix "hidden virtual function" warning
997 NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor,
998 PRUint32 aHotspotX, PRUint32 aHotspotY)
1000 return nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY);
1007 // Get this component dimension
1008 NS_IMETHODIMP nsChildView::GetBounds(nsRect &aRect)
1015 NS_IMETHODIMP nsChildView::SetBounds(const nsRect &aRect)
1017 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1019 nsresult rv = Inherited::SetBounds(aRect);
1020 if (NS_SUCCEEDED(rv)) {
1022 GeckoRectToNSRect(aRect, r);
1028 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1032 NS_IMETHODIMP nsChildView::ConstrainPosition(PRBool aAllowSlop,
1033 PRInt32 *aX, PRInt32 *aY)
1039 // Move this component, aX and aY are in the parent widget coordinate system
1040 NS_IMETHODIMP nsChildView::Move(PRInt32 aX, PRInt32 aY)
1042 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1044 if (!mView || (mBounds.x == aX && mBounds.y == aY))
1051 GeckoRectToNSRect(mBounds, r);
1055 [mView setNeedsDisplay:YES];
1061 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1065 NS_IMETHODIMP nsChildView::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
1067 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1069 if (!mView || (mBounds.width == aWidth && mBounds.height == aHeight))
1072 mBounds.width = aWidth;
1073 mBounds.height = aHeight;
1076 GeckoRectToNSRect(mBounds, r);
1079 if (mVisible && aRepaint)
1080 [mView setNeedsDisplay:YES];
1086 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1090 NS_IMETHODIMP nsChildView::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
1092 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1094 BOOL isMoving = (mBounds.x != aX || mBounds.y != aY);
1095 BOOL isResizing = (mBounds.width != aWidth || mBounds.height != aHeight);
1096 if (!mView || (!isMoving && !isResizing))
1104 mBounds.width = aWidth;
1105 mBounds.height = aHeight;
1109 GeckoRectToNSRect(mBounds, r);
1112 if (mVisible && aRepaint)
1113 [mView setNeedsDisplay:YES];
1117 if (mOnDestroyCalled)
1125 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1129 NS_METHOD nsChildView::GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight)
1131 return NS_ERROR_FAILURE; // nobody call this anywhere in the code
1135 NS_METHOD nsChildView::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight)
1137 return NS_ERROR_FAILURE; // nobody call this anywhere in the code
1141 NS_IMETHODIMP nsChildView::BeginResizingChildren(void)
1147 NS_IMETHODIMP nsChildView::EndResizingChildren(void)
1153 static const PRInt32 resizeIndicatorWidth = 15;
1154 static const PRInt32 resizeIndicatorHeight = 15;
1155 PRBool nsChildView::ShowsResizeIndicator(nsIntRect* aResizerRect)
1157 NSView *topLevelView = mView, *superView = nil;
1158 while ((superView = [topLevelView superview]))
1159 topLevelView = superView;
1161 if (![[topLevelView window] showsResizeIndicator])
1165 NSSize bounds = [topLevelView bounds].size;
1166 NSPoint corner = NSMakePoint(bounds.width, [topLevelView isFlipped] ? bounds.height : 0);
1167 corner = [topLevelView convertPoint:corner toView:mView];
1168 aResizerRect->SetRect(NSToIntRound(corner.x) - resizeIndicatorWidth,
1169 NSToIntRound(corner.y) - resizeIndicatorHeight,
1170 resizeIndicatorWidth, resizeIndicatorHeight);
1176 NS_IMETHODIMP nsChildView::GetPluginClipRect(nsRect& outClipRect, nsPoint& outOrigin, PRBool& outWidgetVisible)
1178 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1180 NS_ASSERTION(mIsPluginView, "GetPluginClipRect must only be called on a plugin widget");
1181 if (!mIsPluginView) return NS_ERROR_FAILURE;
1183 NSWindow* window = [mView nativeWindow];
1184 if (!window) return NS_ERROR_FAILURE;
1186 NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
1187 NSRect frame = [[window contentView] frame];
1188 viewOrigin.y = frame.size.height - viewOrigin.y;
1190 // set up the clipping region for plugins.
1191 NSRect visibleBounds = [mView visibleRect];
1192 NSPoint clipOrigin = [mView convertPoint:visibleBounds.origin toView:nil];
1194 // Convert from cocoa to QuickDraw coordinates
1195 clipOrigin.y = frame.size.height - clipOrigin.y;
1197 outClipRect.x = (nscoord)clipOrigin.x;
1198 outClipRect.y = (nscoord)clipOrigin.y;
1202 IsVisible(isVisible);
1203 if (isVisible && [mView window] != nil) {
1204 outClipRect.width = (nscoord)visibleBounds.size.width;
1205 outClipRect.height = (nscoord)visibleBounds.size.height;
1206 outWidgetVisible = PR_TRUE;
1209 outClipRect.width = 0;
1210 outClipRect.height = 0;
1211 outWidgetVisible = PR_FALSE;
1214 // need to convert view's origin to window coordinates.
1215 // then, encode as "SetOrigin" ready values.
1216 outOrigin.x = (nscoord)-viewOrigin.x;
1217 outOrigin.y = (nscoord)-viewOrigin.y;
1221 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1225 NS_IMETHODIMP nsChildView::StartDrawPlugin()
1227 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1229 NS_ASSERTION(mIsPluginView, "StartDrawPlugin must only be called on a plugin widget");
1230 if (!mIsPluginView) return NS_ERROR_FAILURE;
1232 // Prevent reentrant "drawing" (or in fact reentrant handling of any plugin
1233 // event). Doing this for both CoreGraphics and QuickDraw plugins restores
1234 // the 1.8-branch behavior wrt reentrancy, and fixes (or works around) bugs
1235 // caused by plugins depending on the old behavior -- e.g. bmo bug 409615.
1237 return NS_ERROR_FAILURE;
1239 NSWindow* window = [mView nativeWindow];
1241 return NS_ERROR_FAILURE;
1243 // It appears that the WindowRef from which we get the plugin port undergoes the
1244 // traditional BeginUpdate/EndUpdate cycle, which, if you recall, sets the visible
1245 // region to the intersection of the visible region and the update region. Since
1246 // we don't know here if we're being drawn inside a BeginUpdate/EndUpdate pair
1247 // (which seem to occur in [NSWindow display]), and we don't want to have the burden
1248 // of correctly doing Carbon invalidates of the plugin rect, we manually set the
1249 // visible region to be the entire port every time. It is necessary to set up our
1250 // window's port even for CoreGraphics plugins, because they may still use Carbon
1251 // internally (see bug #420527 for details).
1252 CGrafPtr port = ::GetWindowPort(WindowRef([window windowRef]));
1254 port = mPluginPort.qdPort.port;
1256 RgnHandle pluginRegion = ::NewRgn();
1258 PRBool portChanged = (port != CGrafPtr(GetQDGlobalsThePort()));
1263 ::GetGWorld(&oldPort, &oldDevice);
1264 ::SetGWorld(port, ::IsPortOffscreen(port) ? nsnull : ::GetMainDevice());
1269 nsRect clipRect; // this is in native window coordinates
1272 GetPluginClipRect(clipRect, origin, visible);
1274 // XXX if we're not visible, set an empty clip region?
1276 ConvertGeckoRectToMacRect(clipRect, pluginRect);
1278 ::RectRgn(pluginRegion, &pluginRect);
1279 ::SetPortVisibleRegion(port, pluginRegion);
1280 ::SetPortClipRegion(port, pluginRegion);
1282 // now set up the origin for the plugin
1283 ::SetOrigin(origin.x, origin.y);
1285 ::DisposeRgn(pluginRegion);
1288 ::SetGWorld(oldPort, oldDevice);
1291 mPluginDrawing = PR_TRUE;
1294 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1298 NS_IMETHODIMP nsChildView::EndDrawPlugin()
1300 NS_ASSERTION(mIsPluginView, "EndDrawPlugin must only be called on a plugin widget");
1301 if (!mIsPluginView) return NS_ERROR_FAILURE;
1303 mPluginDrawing = PR_FALSE;
1308 NS_IMETHODIMP nsChildView::SetPluginInstanceOwner(nsIPluginInstanceOwner* aInstanceOwner)
1310 mPluginInstanceOwner = aInstanceOwner;
1316 void nsChildView::LiveResizeStarted()
1318 // XXX todo. Use this to disable Java async redraw during resize
1319 mLiveResizeInProgress = PR_TRUE;
1323 void nsChildView::LiveResizeEnded()
1325 mLiveResizeInProgress = PR_FALSE;
1328 static NSString* ToNSString(const nsAString& aString)
1330 return [NSString stringWithCharacters:aString.BeginReading()
1331 length:aString.Length()];
1334 struct KeyboardLayoutOverride {
1335 PRInt32 mKeyboardLayout;
1336 PRBool mOverrideEnabled;
1339 static KeyboardLayoutOverride gOverrideKeyboardLayout;
1341 static const PRUint32 sModifierFlagMap[][2] = {
1342 { nsIWidget::CAPS_LOCK, NSAlphaShiftKeyMask },
1343 { nsIWidget::SHIFT_L, NSShiftKeyMask },
1344 { nsIWidget::CTRL_L, NSControlKeyMask },
1345 { nsIWidget::ALT_L, NSAlternateKeyMask },
1346 { nsIWidget::COMMAND, NSCommandKeyMask },
1347 { nsIWidget::NUMERIC_KEY_PAD, NSNumericPadKeyMask },
1348 { nsIWidget::HELP, NSHelpKeyMask },
1349 { nsIWidget::FUNCTION, NSFunctionKeyMask }
1351 nsresult nsChildView::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
1352 PRInt32 aNativeKeyCode,
1353 PRUint32 aModifierFlags,
1354 const nsAString& aCharacters,
1355 const nsAString& aUnmodifiedCharacters)
1357 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1359 PRUint32 modifierFlags = 0;
1360 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(sModifierFlagMap); ++i) {
1361 if (aModifierFlags & sModifierFlagMap[i][0]) {
1362 modifierFlags |= sModifierFlagMap[i][1];
1365 int windowNumber = [[mView window] windowNumber];
1366 NSEvent* downEvent = [NSEvent keyEventWithType:NSKeyDown
1367 location:NSMakePoint(0,0)
1368 modifierFlags:modifierFlags
1370 windowNumber:windowNumber
1371 context:[NSGraphicsContext currentContext]
1372 characters:ToNSString(aCharacters)
1373 charactersIgnoringModifiers:ToNSString(aUnmodifiedCharacters)
1375 keyCode:aNativeKeyCode];
1377 NSEvent* upEvent = [ChildView makeNewCocoaEventWithType:NSKeyUp
1378 fromEvent:downEvent];
1380 if (downEvent && upEvent) {
1381 KeyboardLayoutOverride currentLayout = gOverrideKeyboardLayout;
1382 gOverrideKeyboardLayout.mKeyboardLayout = aNativeKeyboardLayout;
1383 gOverrideKeyboardLayout.mOverrideEnabled = PR_TRUE;
1384 [NSApp sendEvent:downEvent];
1385 [NSApp sendEvent:upEvent];
1386 // processKeyDownEvent and keyUp block exceptions so we're sure to
1387 // reach here to restore gOverrideKeyboardLayout
1388 gOverrideKeyboardLayout = currentLayout;
1393 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1396 // Used for testing native menu system structure and event handling.
1397 NS_IMETHODIMP nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString)
1399 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1401 NSString* title = [NSString stringWithCharacters:indexString.BeginReading() length:indexString.Length()];
1402 NSArray* indexes = [title componentsSeparatedByString:@"|"];
1403 unsigned int indexCount = [indexes count];
1404 if (indexCount == 0)
1407 NSMenu* currentSubmenu = [NSApp mainMenu];
1408 for (unsigned int i = 0; i < (indexCount - 1); i++) {
1409 NSMenu* newSubmenu = nil;
1411 // We remove the application menu from consideration for the top-level menu
1413 targetIndex = [[indexes objectAtIndex:i] intValue] + 1;
1415 targetIndex = [[indexes objectAtIndex:i] intValue];
1416 int itemCount = [currentSubmenu numberOfItems];
1417 if (targetIndex < itemCount) {
1418 NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
1419 if ([menuItem hasSubmenu])
1420 newSubmenu = [menuItem submenu];
1424 currentSubmenu = newSubmenu;
1426 return NS_ERROR_FAILURE;
1429 int itemCount = [currentSubmenu numberOfItems];
1430 int targetIndex = [[indexes objectAtIndex:(indexCount - 1)] intValue];
1431 // We can't perform an action on an item with a submenu, that will raise
1432 // an obj-c exception.
1433 if (targetIndex < itemCount && ![[currentSubmenu itemAtIndex:targetIndex] hasSubmenu]) {
1434 // NSLog(@"Performing action for native menu item titled: %@\n",
1435 // [[currentSubmenu itemAtIndex:targetIndex] title]);
1436 [currentSubmenu performActionForItemAtIndex:targetIndex];
1439 return NS_ERROR_FAILURE;
1444 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1448 NS_IMETHODIMP nsChildView::ForceNativeMenuReload()
1450 id windowDelegate = [[mView nativeWindow] delegate];
1451 if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
1452 nsCocoaWindow *widget = [(WindowDelegate *)windowDelegate geckoWidget];
1454 nsMenuBarX* mb = widget->GetMenuBar();
1456 mb->ForceNativeMenuReload();
1468 #ifdef INVALIDATE_DEBUGGING
1470 static Boolean KeyDown(const UInt8 theKey)
1474 return ((*((UInt8 *)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0;
1477 static Boolean caps_lock()
1479 return KeyDown(0x39);
1482 static void blinkRect(Rect* r)
1484 StRegionFromPool oldClip;
1485 if (oldClip != NULL)
1490 UInt32 end = ::TickCount() + 5;
1491 while (::TickCount() < end) ;
1494 if (oldClip != NULL)
1498 static void blinkRgn(RgnHandle rgn)
1500 StRegionFromPool oldClip;
1501 if (oldClip != NULL)
1506 UInt32 end = ::TickCount() + 5;
1507 while (::TickCount() < end) ;
1510 if (oldClip != NULL)
1517 // Invalidate this component's visible area
1518 NS_IMETHODIMP nsChildView::Invalidate(PRBool aIsSynchronous)
1520 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1522 if (!mView || !mVisible)
1525 if (aIsSynchronous) {
1528 else if ([NSView focusView]) {
1529 // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
1531 [mView setNeedsPendingDisplay];
1534 [mView setNeedsDisplay:YES];
1539 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1543 // Invalidate this component's visible area
1544 NS_IMETHODIMP nsChildView::Invalidate(const nsRect &aRect, PRBool aIsSynchronous)
1546 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1548 if (!mView || !mVisible)
1552 GeckoRectToNSRect(aRect, r);
1554 if (aIsSynchronous) {
1555 [mView displayRect:r];
1557 else if ([NSView focusView]) {
1558 // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
1560 [mView setNeedsPendingDisplayInRect:r];
1563 [mView setNeedsDisplayInRect:r];
1568 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1572 // Validate the widget
1573 NS_IMETHODIMP nsChildView::Validate()
1575 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1577 [mView setNeedsDisplay:NO];
1580 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1584 // Invalidate this component's visible area
1585 NS_IMETHODIMP nsChildView::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous)
1587 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1589 if (!mView || !mVisible)
1592 // FIXME rewrite to use a Cocoa region when nsIRegion isn't a QD Region
1595 nsIRegion* region = const_cast<nsIRegion*>(aRegion); // ugh. this method should be const
1596 region->GetBoundingBox(&bounds.x, &bounds.y, &bounds.width, &bounds.height);
1597 GeckoRectToNSRect(bounds, r);
1600 [mView displayRect:r];
1602 [mView setNeedsDisplayInRect:r];
1606 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1610 inline PRUint16 COLOR8TOCOLOR16(PRUint8 color8)
1612 // return (color8 == 0xFF ? 0xFFFF : (color8 << 8));
1613 return (color8 << 8) | color8; /* (color8 * 257) == (color8 * 0x0101) */
1617 // Dummy impl, meant to be overridden
1619 nsChildView::OnPaint(nsPaintEvent &event)
1625 // this is handled for us by UpdateWidget
1626 NS_IMETHODIMP nsChildView::Update()
1628 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1630 // Update means "Flush any pending changes right now." It does *not* mean
1631 // repaint the world. :) -- dwh
1632 [mView displayIfNeeded];
1635 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1642 // Scroll the bits of a view and its children
1643 // FIXME: I'm sure the invalidating can be optimized, just no time now.
1644 NS_IMETHODIMP nsChildView::Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect)
1646 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1648 BOOL viewWasDirty = NO;
1650 viewWasDirty = [mView needsDisplay];
1652 NSSize scrollVector = {aDx,aDy};
1653 [mView scrollRect: [mView visibleRect] by:scrollVector];
1656 // Scroll the children (even if the widget is not visible)
1657 for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
1658 // We use resize rather than move since it gives us control
1659 // over repainting. We can scroll like a bat out of hell
1660 // by not wasting time invalidating the widgets, since it's
1661 // completely unnecessary to do so.
1663 kid->GetBounds(bounds);
1664 kid->Resize(bounds.x + aDx, bounds.y + aDy, bounds.width, bounds.height, PR_FALSE);
1667 if (mOnDestroyCalled)
1672 [mView setNeedsDisplay:YES];
1675 NSRect frame = [mView visibleRect];
1676 NSRect horizInvalid = frame;
1677 NSRect vertInvalid = frame;
1680 horizInvalid.size.width = abs(aDx);
1682 horizInvalid.origin.x = frame.origin.x + frame.size.width + aDx;
1683 [mView setNeedsDisplayInRect: horizInvalid];
1687 vertInvalid.size.height = abs(aDy);
1689 vertInvalid.origin.y = frame.origin.y + frame.size.height + aDy;
1690 [mView setNeedsDisplayInRect: vertInvalid];
1693 // We also need to check for any ChildViews which overlap this widget
1694 // but are not descendent widgets. If there are any, we need to
1695 // invalidate the area of this view that these ChildViews will have been
1696 // blitted into, since these widgets aren't supposed to scroll with
1699 // To do this, start at the root Gecko NSView, and walk down along
1700 // our ancestor view chain, looking at all the subviews in each level
1701 // of the hierarchy. If we find a non-ancestor view that overlaps
1702 // this view, invalidate the area around it.
1704 // We need to convert all rects to a common ancestor view to intersect
1705 // them, since a view's frame is in the coordinate space of its parent.
1706 // Use mParentView as the frame of reference.
1707 NSRect selfFrame = [mParentView convertRect:[mView frame] fromView:[mView superview]];
1708 NSView* view = mParentView;
1709 BOOL selfLevel = NO;
1711 while (!selfLevel) {
1712 NSView* nextAncestorView = nil;
1713 NSArray* subviews = [view subviews];
1714 for (unsigned int i = 0; i < [subviews count]; ++i) {
1715 NSView* subView = [subviews objectAtIndex: i];
1716 if (subView == mView)
1718 else if ([mView isDescendantOf:subView])
1719 nextAncestorView = subView;
1721 NSRect intersectArea = NSIntersectionRect([mParentView convertRect:[subView frame] fromView:[subView superview]], selfFrame);
1722 if (!NSIsEmptyRect(intersectArea)) {
1723 NSPoint origin = [mView convertPoint:intersectArea.origin fromView:mParentView];
1726 vertInvalid.origin.x = origin.x;
1727 if (aDy < 0) // scrolled down, invalidate above
1728 vertInvalid.origin.y = origin.y + aDy;
1729 else // invalidate below
1730 vertInvalid.origin.y = origin.y + intersectArea.size.height;
1731 vertInvalid.size.width = intersectArea.size.width;
1732 [mView setNeedsDisplayInRect: vertInvalid];
1736 horizInvalid.origin.y = origin.y;
1737 if (aDx < 0) // scrolled right, invalidate to the left
1738 horizInvalid.origin.x = origin.x + aDx;
1739 else // invalidate to the right
1740 horizInvalid.origin.x = origin.x + intersectArea.size.width;
1741 horizInvalid.size.height = intersectArea.size.height;
1742 [mView setNeedsDisplayInRect: horizInvalid];
1747 view = nextAncestorView;
1752 // This is an evil hack that doesn't always work.
1754 // Drawing plugins in a Cocoa environment is tricky, because the
1755 // plugins are living in a Carbon WindowRef/BeginUpdate/EndUpdate
1756 // world, and Cocoa has its own notion of dirty rectangles. Throw
1757 // Quartz Extreme and QuickTime into the mix, and things get bad.
1759 // This code is working around a cosmetic issue seen when Quartz Extreme
1760 // is active, and you're scrolling a page with a QuickTime plugin; areas
1761 // outside the plugin fail to scroll properly. This [display] ensures that
1762 // the view is properly drawn before the next Scroll call.
1764 // The time this doesn't work is when you're scrolling a page containing
1765 // an iframe which in turn contains a plugin.
1767 // This is turned off because it makes scrolling pages with plugins slow.
1769 //if ([mView childViewHasPlugin])
1774 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1778 // Invokes callback and ProcessEvent methods on Event Listener object
1779 NS_IMETHODIMP nsChildView::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
1782 debug_DumpEvent(stdout, event->widget, event, nsCAutoString("something"), 0);
1785 aStatus = nsEventStatus_eIgnore;
1787 nsCOMPtr<nsIWidget> kungFuDeathGrip(mParentWidget ? mParentWidget : this);
1788 if (mParentWidget) {
1790 mParentWidget->GetWindowType(type);
1791 if (type == eWindowType_popup) {
1792 // use the parent popup's widget if there is no view
1793 void* clientData = nsnull;
1795 event->widget->GetClientData(clientData);
1797 event->widget = mParentWidget;
1802 aStatus = (*mEventCallback)(event);
1804 // dispatch to event listener if event was not consumed
1805 if (mEventListener && aStatus != nsEventStatus_eConsumeNoDefault)
1806 aStatus = mEventListener->ProcessEvent(*event);
1812 PRBool nsChildView::DispatchWindowEvent(nsGUIEvent &event)
1814 nsEventStatus status;
1815 DispatchEvent(&event, status);
1816 return ConvertStatus(status);
1823 PRBool nsChildView::ReportDestroyEvent()
1825 nsGUIEvent event(PR_TRUE, NS_DESTROY, this);
1826 event.time = PR_IntervalNow();
1827 return DispatchWindowEvent(event);
1831 PRBool nsChildView::ReportMoveEvent()
1833 nsGUIEvent moveEvent(PR_TRUE, NS_MOVE, this);
1834 moveEvent.refPoint.x = mBounds.x;
1835 moveEvent.refPoint.y = mBounds.y;
1836 moveEvent.time = PR_IntervalNow();
1837 return DispatchWindowEvent(moveEvent);
1841 PRBool nsChildView::ReportSizeEvent()
1843 nsSizeEvent sizeEvent(PR_TRUE, NS_SIZE, this);
1844 sizeEvent.time = PR_IntervalNow();
1845 sizeEvent.windowSize = &mBounds;
1846 sizeEvent.mWinWidth = mBounds.width;
1847 sizeEvent.mWinHeight = mBounds.height;
1848 return DispatchWindowEvent(sizeEvent);
1855 /* Calculate the x and y offsets for this particular widget
1856 * @update ps 09/22/98
1857 * @param aX -- x offset amount
1858 * @param aY -- y offset amount
1861 NS_IMETHODIMP nsChildView::CalcOffset(PRInt32 &aX,PRInt32 &aY)
1863 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1866 NSRect bounds = {{0, 0}, {0, 0}};
1867 bounds = [mView convertRect:bounds toView:nil];
1868 aX += static_cast<PRInt32>(bounds.origin.x);
1869 aY += static_cast<PRInt32>(bounds.origin.y);
1873 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1877 // Find if a point in local coordinates is inside this object
1878 PRBool nsChildView::PointInWidget(Point aThePoint)
1880 // get the origin in local coordinates
1881 nsPoint widgetOrigin(0, 0);
1882 LocalToWindowCoordinate(widgetOrigin);
1884 // get rectangle relatively to the parent
1886 GetBounds(widgetRect);
1888 // convert the topLeft corner to local coordinates
1889 widgetRect.MoveBy(widgetOrigin.x, widgetOrigin.y);
1891 // finally tell whether it's a hit
1892 return widgetRect.Contains(aThePoint.h, aThePoint.v);
1898 // Convert the given rect to global coordinates.
1899 // @param aLocalRect -- rect in local coordinates of this widget
1900 // @param aGlobalRect -- |aLocalRect| in global coordinates
1901 NS_IMETHODIMP nsChildView::WidgetToScreen(const nsRect& aLocalRect, nsRect& aGlobalRect)
1903 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1906 GeckoRectToNSRect(aLocalRect, temp);
1908 // 1. First translate this rect into window coords. The returned rect is always in
1909 // bottom-left coordinates.
1911 // NOTE: convertRect:toView:nil doesn't care if |mView| is a flipped view (with
1912 // top-left coords) and so assumes that our passed-in rect's origin is in
1913 // bottom-left coordinates. We adjust this further down, by subtracting
1914 // the final screen rect's origin by the rect's height, to get the origo
1915 // where we want it.
1916 temp = [mView convertRect:temp toView:nil];
1918 // 2. We turn the window-coord rect's origin into screen (still bottom-left) coords.
1919 temp.origin = [[mView nativeWindow] convertBaseToScreen:temp.origin];
1921 // 3. Since we're dealing in bottom-left coords, we need to make it top-left coords
1922 // before we pass it back to Gecko.
1923 FlipCocoaScreenCoordinate(temp.origin);
1925 // 4. If this is rect has a size (and is not simply a point), it is important to account
1926 // for the fact that convertRect:toView:nil thought our passed-in point was in bottom-left
1927 // coords in step #1. Thus, we subtract the rect's height, to get the top-left rect's origin
1928 // where we want it.
1929 temp.origin.y -= temp.size.height;
1931 NSRectToGeckoRect(temp, aGlobalRect);
1934 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1938 // Convert the given rect to local coordinates.
1939 // @param aGlobalRect -- rect in screen coordinates
1940 // @param aLocalRect -- |aGlobalRect| in coordinates of this widget
1941 NS_IMETHODIMP nsChildView::ScreenToWidget(const nsRect& aGlobalRect, nsRect& aLocalRect)
1943 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1946 GeckoRectToNSRect(aGlobalRect, temp);
1947 FlipCocoaScreenCoordinate(temp.origin);
1949 temp.origin = [[mView nativeWindow] convertScreenToBase:temp.origin]; // convert to screen coords
1950 temp = [mView convertRect:temp fromView:nil]; // convert to window coords
1952 NSRectToGeckoRect(temp, aLocalRect);
1956 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1960 // Convert the coordinates to some device coordinates so GFX can draw.
1961 void nsChildView::ConvertToDeviceCoordinates(nscoord &aX, nscoord &aY)
1963 PRInt32 offX = 0, offY = 0;
1964 this->CalcOffset(offX,offY);
1971 NS_IMETHODIMP nsChildView::CaptureRollupEvents(nsIRollupListener * aListener,
1973 PRBool aConsumeRollupEvent)
1975 // this never gets called, only top-level windows can be rollup widgets
1980 NS_IMETHODIMP nsChildView::SetTitle(const nsAString& title)
1982 // child views don't have titles
1987 NS_IMETHODIMP nsChildView::GetAttention(PRInt32 aCycleCount)
1989 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1991 [NSApp requestUserAttention:NSInformationalRequest];
1994 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2001 // Force Input Method Editor to commit the uncommited input
2002 // Note that this and other IME methods don't necessarily
2003 // get called on the same ChildView that input is going through.
2004 NS_IMETHODIMP nsChildView::ResetInputState()
2007 NSLog(@"**** ResetInputState");
2010 nsTSMManager::CommitIME();
2015 // 'open' means that it can take non-ASCII chars
2016 NS_IMETHODIMP nsChildView::SetIMEOpenState(PRBool aState)
2019 NSLog(@"**** SetIMEOpenState aState = %d", aState);
2022 nsTSMManager::SetIMEOpenState(aState);
2027 // 'open' means that it can take non-ASCII chars
2028 NS_IMETHODIMP nsChildView::GetIMEOpenState(PRBool* aState)
2031 NSLog(@"**** GetIMEOpenState");
2034 *aState = nsTSMManager::GetIMEOpenState();
2039 NS_IMETHODIMP nsChildView::SetIMEEnabled(PRUint32 aState)
2042 NSLog(@"**** SetIMEEnabled aState = %d", aState);
2046 case nsIWidget::IME_STATUS_ENABLED:
2047 nsTSMManager::SetRomanKeyboardsOnly(PR_FALSE);
2048 nsTSMManager::EnableIME(PR_TRUE);
2050 case nsIWidget::IME_STATUS_DISABLED:
2051 nsTSMManager::SetRomanKeyboardsOnly(PR_FALSE);
2052 nsTSMManager::EnableIME(PR_FALSE);
2054 case nsIWidget::IME_STATUS_PASSWORD:
2055 nsTSMManager::SetRomanKeyboardsOnly(PR_TRUE);
2056 nsTSMManager::EnableIME(PR_FALSE);
2059 NS_ERROR("not implemented!");
2065 NS_IMETHODIMP nsChildView::GetIMEEnabled(PRUint32* aState)
2068 NSLog(@"**** GetIMEEnabled");
2071 if (nsTSMManager::IsIMEEnabled())
2072 *aState = nsIWidget::IME_STATUS_ENABLED;
2073 else if (nsTSMManager::IsRomanKeyboardsOnly())
2074 *aState = nsIWidget::IME_STATUS_PASSWORD;
2076 *aState = nsIWidget::IME_STATUS_DISABLED;
2081 // Destruct and don't commit the IME composition string.
2082 NS_IMETHODIMP nsChildView::CancelIMEComposition()
2085 NSLog(@"**** CancelIMEComposition");
2088 nsTSMManager::CancelIME();
2093 NS_IMETHODIMP nsChildView::GetToggledKeyState(PRUint32 aKeyCode,
2096 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2099 NSLog(@"**** GetToggledKeyState");
2101 NS_ENSURE_ARG_POINTER(aLEDState);
2104 case NS_VK_CAPS_LOCK:
2107 case NS_VK_NUM_LOCK:
2108 key = kEventKeyModifierNumLockMask;
2110 // Mac doesn't support SCROLL_LOCK state.
2112 return NS_ERROR_NOT_IMPLEMENTED;
2114 PRUint32 modifierFlags = ::GetCurrentEventKeyModifiers();
2115 *aLEDState = (modifierFlags & key) != 0;
2118 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2126 nsChildView::GetThebesSurface()
2128 if (!mTempThebesSurface) {
2129 mTempThebesSurface = new gfxQuartzSurface(gfxSize(1, 1), gfxASurface::ImageFormatARGB32);
2132 return mTempThebesSurface;
2137 nsChildView::BeginSecureKeyboardInput()
2139 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2141 nsresult rv = nsBaseWidget::BeginSecureKeyboardInput();
2142 if (NS_SUCCEEDED(rv))
2143 ::EnableSecureEventInput();
2146 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2151 nsChildView::EndSecureKeyboardInput()
2153 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2155 nsresult rv = nsBaseWidget::EndSecureKeyboardInput();
2156 if (NS_SUCCEEDED(rv))
2157 ::DisableSecureEventInput();
2160 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2164 #ifdef ACCESSIBILITY
2166 nsChildView::GetDocumentAccessible(nsIAccessible** aAccessible)
2168 *aAccessible = nsnull;
2170 nsCOMPtr<nsIAccessible> accessible = do_QueryReferent(mAccessible);
2172 // need to fetch the accessible anew, because it has gone away.
2173 nsEventStatus status;
2174 nsAccessibleEvent event(PR_TRUE, NS_GETACCESSIBLE, this);
2175 DispatchEvent(&event, status);
2177 // cache the accessible in our weak ptr
2178 mAccessible = do_GetWeakReference(event.accessible);
2181 accessible = do_QueryReferent(mAccessible);
2184 NS_IF_ADDREF(*aAccessible = accessible.get());
2194 @implementation ChildView
2197 // globalDragPboard is non-null during native drag sessions that did not originate
2198 // in our native NSView (it is set in |draggingEntered:|). It is unset when the
2199 // drag session ends for this view, either with the mouse exiting or when a drop
2200 // occurs in this view.
2201 NSPasteboard* globalDragPboard = nil;
2204 // gLastDragView and gLastDragEvent are only non-null during calls to |mouseDragged:|
2205 // in our native NSView. They are used to communicate information to the drag service
2206 // during drag invocation (starting a drag in from the view). All drag service drag
2207 // invocations happen only while these two global variables are non-null, while |mouseDragged:|
2209 NSView* gLastDragView = nil;
2210 NSEvent* gLastDragEvent = nil;
2213 // initWithFrame:geckoChild:
2214 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
2216 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2218 if ((self = [super initWithFrame:inFrame])) {
2220 mGeckoChild = inChild;
2224 mKeyDownHandled = PR_FALSE;
2225 mKeyPressHandled = NO;
2228 // initialization for NSTextInput
2229 mMarkedRange.location = NSNotFound;
2230 mMarkedRange.length = 0;
2232 mLastMouseDownEvent = nil;
2233 mDragService = nsnull;
2235 mPluginTSMDoc = nil;
2238 // register for things we'll take from other applications
2239 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
2240 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
2243 NSFilesPromisePboardType,
2244 kWildcardPboardType,
2245 kCorePboardType_url,
2246 kCorePboardType_urld,
2247 kCorePboardType_urln,
2252 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2258 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2260 [mPendingDirtyRects release];
2261 [mLastMouseDownEvent release];
2263 ::DeleteTSMDocument(mPluginTSMDoc);
2265 if (sLastViewEntered == self)
2266 sLastViewEntered = nil;
2270 // This sets the current port to _savePort.
2271 // todo: Only do if a Quickdraw plugin is present in the hierarchy!
2274 NS_OBJC_END_TRY_ABORT_BLOCK;
2278 - (void)widgetDestroyed
2280 nsTSMManager::OnDestroyView(self);
2281 mGeckoChild = nsnull;
2282 // Just in case we're destroyed abruptly and missed the draggingExited
2283 // or performDragOperation message.
2284 NS_IF_RELEASE(mDragService);
2288 // mozView method, return our gecko child view widget. Note this does not AddRef.
2289 - (nsIWidget*) widget
2291 return static_cast<nsIWidget*>(mGeckoChild);
2295 // mozView method, get the window that this view is associated with
2296 - (NSWindow*)nativeWindow
2298 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2300 NSWindow* currWin = [self window];
2306 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2310 // mozView method, set the NSWindow that this view is associated with (even when
2311 // not in the view hierarchy).
2312 - (void)setNativeWindow:(NSWindow*)aWindow
2318 - (void)setNeedsPendingDisplay
2320 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2322 mPendingFullDisplay = YES;
2323 [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
2325 NS_OBJC_END_TRY_ABORT_BLOCK;
2329 - (void)setNeedsPendingDisplayInRect:(NSRect)invalidRect
2331 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2333 if (!mPendingDirtyRects)
2334 mPendingDirtyRects = [[NSMutableArray alloc] initWithCapacity:1];
2335 [mPendingDirtyRects addObject:[NSValue valueWithRect:invalidRect]];
2336 [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
2338 NS_OBJC_END_TRY_ABORT_BLOCK;
2342 // Clears the queue of any pending invalides
2343 - (void)processPendingRedraws
2345 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2347 if (mPendingFullDisplay) {
2348 [self setNeedsDisplay:YES];
2351 unsigned int count = [mPendingDirtyRects count];
2352 for (unsigned int i = 0; i < count; ++i) {
2353 [self setNeedsDisplayInRect:[[mPendingDirtyRects objectAtIndex:i] rectValue]];
2356 mPendingFullDisplay = NO;
2357 [mPendingDirtyRects release];
2358 mPendingDirtyRects = nil;
2360 NS_OBJC_END_TRY_ABORT_BLOCK;
2364 - (NSString*)description
2366 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2368 return [NSString stringWithFormat:@"ChildView %p, gecko child %p, frame %@", self, mGeckoChild, NSStringFromRect([self frame])];
2370 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2374 // Find the nearest scrollable view for this ChildView
2375 // (recall that views are not refcounted)
2376 - (nsIScrollableView*) getScrollableView
2378 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
2380 nsIScrollableView* scrollableView = nsnull;
2382 ChildView* currView = self;
2383 // we have to loop up through superviews in case the view that received the
2384 // mouseDown is in fact a plugin view with no scrollbars
2386 // This is a hack I learned in nsView::GetViewFor(nsIWidget* aWidget)
2387 // that I'm not sure is kosher. If anyone knows a better way to get
2388 // the view for a widget, I'd love to hear it. --Nathan
2391 [currView widget]->GetClientData(clientData);
2393 nsISupports* data = (nsISupports*)clientData;
2394 nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(data));
2396 req->GetInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollableView);
2401 if ([[currView superview] isMemberOfClass:[ChildView class]])
2402 currView = (ChildView*)[currView superview];
2407 return scrollableView;
2409 NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
2413 // set the closed hand cursor and record the starting scroll positions
2414 - (void) startHandScroll:(NSEvent*)theEvent
2416 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2421 mHandScrollStartMouseLoc = [[self window] convertBaseToScreen:[theEvent locationInWindow]];
2423 nsIScrollableView* aScrollableView = [self getScrollableView];
2425 // if we succeeded in getting aScrollableView
2426 if (aScrollableView) {
2427 aScrollableView->GetScrollPosition(mHandScrollStartScrollX, mHandScrollStartScrollY);
2428 mGeckoChild->SetCursor(eCursor_grabbing);
2429 mInHandScroll = TRUE;
2432 NS_OBJC_END_TRY_ABORT_BLOCK;
2436 // update the scroll position based on the new mouse coordinates
2437 - (void) updateHandScroll:(NSEvent*)theEvent
2439 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2444 nsIScrollableView* aScrollableView = [self getScrollableView];
2445 if (!aScrollableView)
2448 NSPoint newMouseLoc = [[self window] convertBaseToScreen:[theEvent locationInWindow]];
2450 PRInt32 deltaX = (PRInt32)(mHandScrollStartMouseLoc.x - newMouseLoc.x);
2451 PRInt32 deltaY = (PRInt32)(newMouseLoc.y - mHandScrollStartMouseLoc.y);
2453 // convert to the nsIView coordinates
2454 PRInt32 p2a = mGeckoChild->GetDeviceContext()->AppUnitsPerDevPixel();
2455 nscoord newX = mHandScrollStartScrollX + NSIntPixelsToAppUnits(deltaX, p2a);
2456 nscoord newY = mHandScrollStartScrollY + NSIntPixelsToAppUnits(deltaY, p2a);
2457 aScrollableView->ScrollTo(newX, newY, NS_VMREFRESH_IMMEDIATE);
2459 NS_OBJC_END_TRY_ABORT_BLOCK;
2463 // Return true if the correct modifiers are pressed to perform hand scrolling.
2464 + (BOOL) areHandScrollModifiers:(unsigned int)modifiers
2466 // The command and option key should be held down. Ignore capsLock by
2467 // setting it explicitly to match.
2468 modifiers |= NSAlphaShiftKeyMask;
2469 return (modifiers & NSDeviceIndependentModifierFlagsMask) ==
2470 (NSAlphaShiftKeyMask | NSCommandKeyMask | NSAlternateKeyMask);
2474 // If the user is pressing the hand scroll modifiers, then set
2475 // the hand scroll cursor.
2476 - (void) setHandScrollCursor:(NSEvent*)theEvent
2478 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2483 BOOL inMouseView = NO;
2485 // check to see if the user has hand scroll modifiers held down; if so,
2486 // find out if the cursor is in an ChildView
2487 if ([ChildView areHandScrollModifiers:[theEvent modifierFlags]]) {
2488 NSPoint pointInWindow = [[self window] mouseLocationOutsideOfEventStream];
2490 NSView* mouseView = [[[self window] contentView] hitTest:pointInWindow];
2491 inMouseView = (mouseView != nil && [mouseView isMemberOfClass:[ChildView class]]);
2494 mGeckoChild->SetCursor(eCursor_grab);
2496 nsCursor cursor = mGeckoChild->GetCursor();
2497 if (!mInHandScroll) {
2498 if (cursor == eCursor_grab || cursor == eCursor_grabbing)
2499 mGeckoChild->SetCursor(eCursor_standard);
2503 NS_OBJC_END_TRY_ABORT_BLOCK;
2507 // reset the scroll flag and cursor
2508 - (void) stopHandScroll:(NSEvent*)theEvent
2510 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2512 mInHandScroll = FALSE;
2513 [self setHandScrollCursor:theEvent];
2515 NS_OBJC_END_TRY_ABORT_BLOCK;
2519 // When smooth scrolling is turned on on panther, the parent of a scrollbar (which
2520 // I guess they assume is a NSScrollView) gets called with this method. I have no
2521 // idea what the correct return value is, but we have to have this otherwise the scrollbar
2522 // will not continuously respond when the mouse is held down in the pageup/down area.
2523 -(float)_destinationFloatValueForScroller:(id)scroller
2525 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2527 return [scroller floatValue];
2529 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0.0);
2533 // Override in order to keep our mouse enter/exit tracking rect in sync with
2534 // the frame of the view
2535 - (void)setFrame:(NSRect)frameRect
2537 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2539 [super setFrame:frameRect];
2540 if (mMouseEnterExitTag)
2541 [self removeTrackingRect:mMouseEnterExitTag];
2544 mMouseEnterExitTag = [self addTrackingRect:[self bounds]
2547 assumeInside:[[self window] acceptsMouseMovedEvents]];
2549 NS_OBJC_END_TRY_ABORT_BLOCK;
2553 // Make the origin of this view the topLeft corner (gecko origin) rather
2554 // than the bottomLeft corner (standard cocoa origin).
2561 - (void)setTransparent:(BOOL)transparent
2563 mIsTransparent = transparent;
2569 return !mIsTransparent;
2573 -(void)setIsPluginView:(BOOL)aIsPlugin
2575 mIsPluginView = aIsPlugin;
2581 return mIsPluginView;
2585 - (BOOL)childViewHasPlugin
2587 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2589 NSArray* subviews = [self subviews];
2590 for (unsigned int i = 0; i < [subviews count]; i ++) {
2591 id subview = [subviews objectAtIndex:i];
2592 if ([subview respondsToSelector:@selector(isPluginView)] && [subview isPluginView])
2598 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
2602 - (void)sendFocusEvent:(PRUint32)eventType
2607 nsEventStatus status = nsEventStatus_eIgnore;
2608 nsGUIEvent focusGuiEvent(PR_TRUE, eventType, mGeckoChild);
2609 focusGuiEvent.time = PR_IntervalNow();
2610 mGeckoChild->DispatchEvent(&focusGuiEvent, status);
2614 // We accept key and mouse events, so don't keep passing them up the chain. Allow
2615 // this to be a 'focussed' widget for event dispatch
2616 - (BOOL)acceptsFirstResponder
2622 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
2624 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2626 if (mMouseEnterExitTag)
2627 [self removeTrackingRect:mMouseEnterExitTag];
2629 [super viewWillMoveToWindow:newWindow];
2631 NS_OBJC_END_TRY_ABORT_BLOCK;
2635 - (void)viewDidMoveToWindow
2637 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2640 mMouseEnterExitTag = [self addTrackingRect:[self bounds] owner:self
2641 userData:nil assumeInside: [[self window]
2642 acceptsMouseMovedEvents]];
2644 [super viewDidMoveToWindow];
2646 NS_OBJC_END_TRY_ABORT_BLOCK;
2650 - (void)viewWillStartLiveResize
2652 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2654 if (mGeckoChild && mIsPluginView)
2655 mGeckoChild->LiveResizeStarted();
2657 [super viewWillStartLiveResize];
2659 NS_OBJC_END_TRY_ABORT_BLOCK;
2663 - (void)viewDidEndLiveResize
2665 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2667 if (mGeckoChild && mIsPluginView)
2668 mGeckoChild->LiveResizeEnded();
2670 [super viewDidEndLiveResize];
2672 NS_OBJC_END_TRY_ABORT_BLOCK;
2676 - (void)scrollRect:(NSRect)aRect by:(NSSize)offset
2678 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2680 // Update any pending dirty rects to reflect the new scroll position
2681 if (mPendingDirtyRects) {
2682 unsigned int count = [mPendingDirtyRects count];
2683 for (unsigned int i = 0; i < count; ++i) {
2684 NSRect oldRect = [[mPendingDirtyRects objectAtIndex:i] rectValue];
2685 NSRect newRect = NSOffsetRect(oldRect, offset.width, offset.height);
2686 [mPendingDirtyRects replaceObjectAtIndex:i
2687 withObject:[NSValue valueWithRect:newRect]];
2690 [super scrollRect:aRect by:offset];
2692 NS_OBJC_END_TRY_ABORT_BLOCK;
2696 - (BOOL)mouseDownCanMoveWindow
2704 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2706 // Set the current GrafPort to a "safe" port before calling [NSQuickDrawView lockFocus],
2707 // so that the NSQuickDrawView stashes a pointer to this known-good port internally.
2708 // It will set the port back to this port on destruction.
2709 ::SetPort(NULL); // todo: only do if a Quickdraw plugin is present in the hierarchy!
2712 NS_OBJC_END_TRY_ABORT_BLOCK;
2716 static BOOL IsPaintingSuppressed(nsIWidget* aWidget)
2718 nsIWidget* topLevelWidget = aWidget->GetTopLevelWidget();
2719 NSWindow* win = (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW);
2720 return ([win isKindOfClass:[ToolbarWindow class]] &&
2721 [(ToolbarWindow*)win isPaintingSuppressed]);
2725 // The display system has told us that a portion of our view is dirty. Tell
2726 // gecko to paint it
2727 - (void)drawRect:(NSRect)aRect
2729 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2732 if (!mGeckoChild || NS_FAILED(mGeckoChild->IsVisible(isVisible)) ||
2733 !isVisible || IsPaintingSuppressed(mGeckoChild))
2736 CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2739 mGeckoChild->GetBounds(geckoBounds);
2741 NSRect bounds = [self bounds];
2742 nsRefPtr<gfxQuartzSurface> targetSurface =
2743 new gfxQuartzSurface(cgContext, gfxSize(bounds.size.width, bounds.size.height));
2746 fprintf (stderr, "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n gecko bounds: [%d %d %d %d]\n",
2748 aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, cgContext,
2749 geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height);
2751 CGAffineTransform xform = CGContextGetCTM(cgContext);
2752 fprintf (stderr, " xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty);
2755 nsRefPtr<gfxContext> targetContext = new gfxContext(targetSurface);
2757 nsCOMPtr<nsIRenderingContext> rc;
2758 mGeckoChild->GetDeviceContext()->CreateRenderingContextInstance(*getter_AddRefs(rc));
2759 rc->Init(mGeckoChild->GetDeviceContext(), targetContext);
2761 /* clip and build a region */
2762 nsCOMPtr<nsIRegion> rgn(do_CreateInstance(kRegionCID));
2766 const NSRect *rects;
2768 [self getRectsBeingDrawn:&rects count:&count];
2769 for (i = 0; i < count; ++i) {
2770 const NSRect& r = rects[i];
2772 // add to the region
2774 rgn->Union((PRInt32)r.origin.x, (PRInt32)r.origin.y, (PRInt32)r.size.width, (PRInt32)r.size.height);
2776 // to the context for clipping
2777 targetContext->Rectangle(gfxRect(r.origin.x, r.origin.y, r.size.width, r.size.height));
2779 targetContext->Clip();
2781 // bounding box of the dirty area
2783 NSRectToGeckoRect(aRect, fullRect);
2785 nsPaintEvent paintEvent(PR_TRUE, NS_PAINT, mGeckoChild);
2786 paintEvent.renderingContext = rc;
2787 paintEvent.rect = &fullRect;
2788 paintEvent.region = rgn;
2790 nsAutoRetainCocoaObject kungFuDeathGrip(self);
2791 mGeckoChild->DispatchWindowEvent(paintEvent);
2795 paintEvent.renderingContext = nsnull;
2796 paintEvent.region = nsnull;
2798 targetContext = nsnull;
2799 targetSurface = nsnull;
2801 // note that the cairo surface *MUST* be destroyed at this point,
2802 // or bad things will happen (since we can't keep the cgContext around
2803 // beyond this drawRect message handler)
2806 fprintf (stderr, " window coords: [%d %d %d %d]\n", fullRect.x, fullRect.y, fullRect.width, fullRect.height);
2807 fprintf (stderr, "---- update done ----\n");
2810 CGContextSetRGBStrokeColor (cgContext,
2811 ((((unsigned long)self) & 0xff)) / 255.0,
2812 ((((unsigned long)self) & 0xff00) >> 8) / 255.0,
2813 ((((unsigned long)self) & 0xff0000) >> 16) / 255.0,
2816 CGContextSetRGBStrokeColor (cgContext, 1, 0, 0, 0.8);
2817 CGContextSetLineWidth (cgContext, 4.0);
2818 CGContextStrokeRect (cgContext,
2819 CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height));
2822 NS_OBJC_END_TRY_ABORT_BLOCK;
2826 // Allows us to turn off setting up the clip region
2827 // before each drawRect. We already clip within gecko.
2828 - (BOOL)wantsDefaultClipping
2834 #if USE_CLICK_HOLD_CONTEXTMENU
2836 // -clickHoldCallback:
2838 // called from a timer two seconds after a mouse down to see if we should display
2839 // a context menu (click-hold). |anEvent| is the original mouseDown event. If we're
2840 // still in that mouseDown by this time, put up the context menu, otherwise just
2841 // fuhgeddaboutit. |anEvent| has been retained by the OS until after this callback
2842 // fires so we're ok there.
2844 // This code currently messes in a bunch of edge cases (bugs 234751, 232964, 232314)
2845 // so removing it until we get it straightened out.
2847 - (void)clickHoldCallback:(id)theEvent;
2849 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2851 if( theEvent == [NSApp currentEvent] ) {
2852 // we're still in the middle of the same mousedown event here, activate
2853 // click-hold context menu by triggering the right mouseDown action.
2854 NSEvent* clickHoldEvent = [NSEvent mouseEventWithType:NSRightMouseDown
2855 location:[theEvent locationInWindow]
2856 modifierFlags:[theEvent modifierFlags]
2857 timestamp:[theEvent timestamp]
2858 windowNumber:[theEvent windowNumber]
2859 context:[theEvent context]
2860 eventNumber:[theEvent eventNumber]
2861 clickCount:[theEvent clickCount]
2862 pressure:[theEvent pressure]];
2863 [self rightMouseDown:clickHoldEvent];
2866 NS_OBJC_END_TRY_ABORT_BLOCK;
2871 // We sometimes need to reroute events when there is a rollup widget and the
2872 // event isn't targeted at it.
2874 // Rerouting may be needed when the user tries to navigate a context menu while
2875 // keeping the mouse-button down (left or right mouse button) -- the OS thinks this
2876 // is a dragging operation, so it sends events (mouseMoved and mouseUp) to the
2877 // window where the dragging operation started (the parent of the context
2878 // menu window). It also works around a bizarre Apple bug - if (while a context
2879 // menu is open) you move the mouse over another app's window and then back over
2880 // the context menu, mouseMoved events will be sent to the window underneath the
2882 - (BOOL)ensureCorrectMouseEventTarget:(NSEvent*)anEvent
2884 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2886 // If there is no rollup widget we assume the OS routed the event correctly.
2890 // If this is the rollup widget and the event is not a mouse move then trust the OS routing.
2891 // The reason for this trust is complicated.
2893 // There are three types of mouse events that can legitimately need to be targeted at a window
2894 // that they are not over. Mouse moves, mouse drags, and mouse ups. Anything else our app wouldn't
2895 // handle (if the mouse was not over any window) or it would go to the appropriate window.
2897 // We need to do manual event rerouting for mouse moves because we know that in some cases, like
2898 // when there is a submenu opened from a popup window, the OS will route mouse move events to the
2899 // submenu even if the mouse is over the parent. Mouse move events are never tied to a particular
2900 // window because of some originating action like the starting point of a drag for drag events or
2901 // a mouse down event for mouse up events, so it is always safe to do our own routing on them here.
2903 // As for mouse drags and mouse ups, they have originating actions that tie them to windows they
2904 // may no longer be over. If there is a rollup window present when one of these events is getting
2905 // processed but we are not it, we are probably the window where the action originated, and that
2906 // action must have caused the rollup window to come into existence. In that case, we might need
2907 // to reroute the event if it is over the rollup window. That is why if we're not the rollup window
2908 // we don't return YES here.
2909 NSWindow* rollupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
2910 if (mWindow == rollupWindow && [anEvent type] != NSMouseMoved)
2913 // Find the window that the event is over.
2914 NSWindow* targetWindow = nsCocoaUtils::FindWindowUnderPoint(nsCocoaUtils::ScreenLocationForEvent(anEvent));
2916 // If the event was not over any window, send it to the rollup window.
2918 targetWindow = rollupWindow;
2920 // At this point we've resolved a target window, if we are it then just return
2921 // yes so we handle it. No need to redirect.
2922 if (targetWindow == mWindow)
2925 // Send the event to its new destination.
2926 NSPoint newWindowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, targetWindow);
2927 NSEvent *newEvent = [NSEvent mouseEventWithType:[anEvent type]
2928 location:newWindowLocation
2929 modifierFlags:[anEvent modifierFlags]
2930 timestamp:GetCurrentEventTime()
2931 windowNumber:[targetWindow windowNumber]
2936 [targetWindow sendEvent:newEvent];
2938 // Return NO because we just sent the event somewhere else.
2941 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
2945 // If we've just created a non-native context menu, we need to mark it as
2946 // such and let the OS (and other programs) know when it opens and closes
2947 // (this is how the OS knows to close other programs' context menus when
2948 // ours open). We send the initial notification here, but others are sent
2949 // in nsCocoaWindow::Show().
2950 - (void)maybeInitContextMenuTracking
2952 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2957 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
2959 PRBool useNativeContextMenus;
2960 nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows", &useNativeContextMenus);
2961 if (NS_SUCCEEDED(rv) && useNativeContextMenus)
2965 NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
2966 if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]])
2969 [[NSDistributedNotificationCenter defaultCenter]
2970 postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
2971 object:@"org.mozilla.gecko.PopupWindow"];
2972 [(PopupWindow*)popupWindow setIsContextMenu:YES];
2974 NS_OBJC_END_TRY_ABORT_BLOCK;
2978 - (BOOL)maybeRollup:(NSEvent*)theEvent
2980 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2982 PRBool retVal = PR_FALSE;
2983 if (gRollupWidget && gRollupListener) {
2984 NSWindow* currentPopup = static_cast<NSWindow*>(gRollupWidget->GetNativeData(NS_NATIVE_WINDOW));
2985 if (!nsCocoaUtils::IsEventOverWindow(theEvent, currentPopup)) {
2986 PRBool rollup = PR_TRUE;
2987 if ([theEvent type] == NSScrollWheel) {
2988 gRollupListener->ShouldRollupOnMouseWheelEvent(&rollup);
2989 // We don't want the event passed on for scrollwheel events if we're
2990 // not supposed to close the popup. Otherwise the background window
2991 // will scroll when a custom context menu or the autoscroll popup is
2992 // open (and the mouse isn't over the popup) -- which doesn't seem right.
2993 // This change resolves bmo bug 344367.
2996 // if we're dealing with menus, we probably have submenus and
2997 // we don't want to rollup if the click is in a parent menu of
2998 // the current submenu
2999 nsCOMPtr<nsIMenuRollup> menuRollup;
3000 menuRollup = (do_QueryInterface(gRollupListener));
3002 nsAutoTArray<nsIWidget*, 5> widgetChain;
3003 menuRollup->GetSubmenuWidgetChain(&widgetChain);
3004 for (PRUint32 i = 0; i < widgetChain.Length(); i++) {
3005 nsIWidget* widget = widgetChain[i];
3006 NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
3007 if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
3011 } // foreach parent menu widget
3012 } // if rollup listener knows about menus
3014 // if we've determined that we should still rollup, do it.
3016 gRollupListener->Rollup(nsnull);
3024 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
3028 - (void)mouseDown:(NSEvent*)theEvent
3030 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3032 // If we've already seen this event due to direct dispatch from menuForEvent:
3033 // just bail; if not, remember it.
3034 if (mLastMouseDownEvent == theEvent) {
3035 [mLastMouseDownEvent release];
3036 mLastMouseDownEvent = nil;
3040 [mLastMouseDownEvent release];
3041 mLastMouseDownEvent = [theEvent retain];
3044 if (![self ensureCorrectMouseEventTarget:theEvent])
3047 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3049 if ([self maybeRollup:theEvent])
3052 unsigned int modifierFlags = [theEvent modifierFlags];
3054 // if the command and alt keys are held down, initiate hand scrolling
3055 if ([ChildView areHandScrollModifiers:modifierFlags]) {
3056 [self startHandScroll:theEvent];
3057 // needed to change the focus, among other things, since we don't
3058 // get to do that below.
3059 [super mouseDown:theEvent];
3060 return; // do not pass this mousedown event to gecko
3063 #if USE_CLICK_HOLD_CONTEXTMENU
3064 // fire off timer to check for click-hold after two seconds. retains |theEvent|
3065 [self performSelector:@selector(clickHoldCallback:) withObject:theEvent afterDelay:2.0];
3068 // in order to send gecko events we'll need a gecko widget
3072 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
3073 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3074 geckoEvent.clickCount = [theEvent clickCount];
3075 if (modifierFlags & NSControlKeyMask)
3076 geckoEvent.button = nsMouseEvent::eRightButton;
3078 geckoEvent.button = nsMouseEvent::eLeftButton;
3080 // create native EventRecord for use by plugins
3081 EventRecord macEvent;
3082 macEvent.what = mouseDown;
3083 macEvent.message = 0;
3084 macEvent.when = ::TickCount();
3085 ::GetGlobalMouse(&macEvent.where);
3086 macEvent.modifiers = ::GetCurrentEventKeyModifiers();
3087 geckoEvent.nativeMsg = &macEvent;
3089 mGeckoChild->DispatchWindowEvent(geckoEvent);
3091 // XXX maybe call markedTextSelectionChanged:client: here?
3093 NS_OBJC_END_TRY_ABORT_BLOCK;
3097 - (void)mouseUp:(NSEvent *)theEvent
3099 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3101 if (mInHandScroll) {
3102 [self updateHandScroll:theEvent];
3103 [self stopHandScroll:theEvent];
3107 if (![self ensureCorrectMouseEventTarget:theEvent])
3113 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
3114 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3116 // create native EventRecord for use by plugins
3117 EventRecord macEvent;
3118 macEvent.what = mouseUp;
3119 macEvent.message = 0;
3120 macEvent.when = ::TickCount();
3121 ::GetGlobalMouse(&macEvent.where);
3122 macEvent.modifiers = ::GetCurrentEventKeyModifiers();
3123 geckoEvent.nativeMsg = &macEvent;
3125 mGeckoChild->DispatchWindowEvent(geckoEvent);
3127 NS_OBJC_END_TRY_ABORT_BLOCK;
3131 // sends a mouse enter or exit event into gecko
3132 static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted,
3135 nsMouseEvent::reasonType aReason,
3136 NSPoint* localEventLocation,
3137 nsMouseEvent::exitType type)
3139 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
3141 if (!widget || !localEventLocation)
3142 return nsEventStatus_eIgnore;
3144 nsMouseEvent event(isTrusted, msg, widget, aReason);
3145 event.refPoint.x = nscoord((PRInt32)localEventLocation->x);
3146 event.refPoint.y = nscoord((PRInt32)localEventLocation->y);
3148 EventRecord macEvent;
3149 macEvent.what = adjustCursorEvent;
3150 macEvent.message = 0;
3151 macEvent.when = ::TickCount();
3152 ::GetGlobalMouse(&macEvent.where);
3153 macEvent.modifiers = ::GetCurrentEventKeyModifiers();
3154 event.nativeMsg = &macEvent;
3158 nsEventStatus status;
3159 widget->DispatchEvent(&event, status);
3161 // After the cursor exits a view set it to a visible regular arrow cursor.
3162 // This lets us recover from plugins that mess with it.
3163 if (msg == NS_MOUSE_EXIT) {
3165 [[NSCursor arrowCursor] set];
3170 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsEventStatus_eIgnore);
3174 - (void)mouseMoved:(NSEvent*)theEvent
3176 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3178 // Work around an Apple bug that causes the OS to continue sending
3179 // mouseMoved events to a window for a while after it's been miniaturized.
3180 // This may be related to a similar problem with popup windows (bmo bug
3181 // 378645, popup windows continue to receive mouseMoved events after having
3182 // been "ordered out"), which is worked around in nsCocoaWindow::Show()
3183 // (search on 378645 in nsCocoaWindow.mm). This problem is bmo bug 410219,
3184 // and exists in both OS X 10.4 and 10.5.
3185 if ([[self window] isMiniaturized])
3188 NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(theEvent, mWindow);
3189 NSPoint viewEventLocation = [self convertPoint:windowEventLocation fromView:nil];
3191 // Installing a mouseMoved handler on the EventMonitor target (in
3192 // nsToolkit::RegisterForAllProcessMouseEvents()) means that some of the
3193 // events received here come from other processes. For this reason we need
3194 // to avoid processing them unless they're over a context menu -- otherwise
3195 // tooltips and other mouse-hover effects will "work" even when our app
3196 // doesn't have the focus.
3197 BOOL mouseEventIsOverRollupWidget = NO;
3198 if (gRollupWidget) {
3199 NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
3200 mouseEventIsOverRollupWidget = nsCocoaUtils::IsEventOverWindow(theEvent, popupWindow);
3203 if (![NSApp isActive] && !mouseEventIsOverRollupWidget) {
3204 if (sLastViewEntered) {
3205 nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
3206 NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
3207 SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
3208 &exitEventLocation, nsMouseEvent::eTopLevel);
3209 sLastViewEntered = nil;
3214 if (![self ensureCorrectMouseEventTarget:theEvent])
3217 NSView* view = [[mWindow contentView] hitTest:windowEventLocation];
3219 // we shouldn't handle this if the hit view is not us
3220 if (view != (NSView*)self) {
3221 [view mouseMoved:theEvent];
3226 // If the hit test returned nil then the mouse isn't over the window. If thse mouse
3227 // exited the window then send mouse exit to the last view in the window it was over.
3228 if (sLastViewEntered) {
3229 NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
3230 // NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
3231 nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
3232 SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
3233 &exitEventLocation, nsMouseEvent::eTopLevel);
3234 sLastViewEntered = nil;
3239 // At this point we are supposed to handle this event. If we were not the last view entered, then
3240 // we should send an exit event to the last view entered and an enter event to ourselves.
3244 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3245 if (sLastViewEntered != self) {
3246 if (sLastViewEntered) {
3247 NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
3248 // NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
3249 nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
3250 SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
3251 &exitEventLocation, nsMouseEvent::eChild);
3253 // The mouse exit event we just sent may have destroyed this widget, bail if that happened.
3258 // NSLog(@"sending NS_MOUSE_ENTER event with point %f,%f\n", viewEventLocation.x, viewEventLocation.y);
3259 SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_ENTER, mGeckoChild, nsMouseEvent::eReal,
3260 &viewEventLocation, nsMouseEvent::eChild);
3262 // The mouse enter event we just sent may have destroyed this widget, bail if that happened.
3266 // mark this view as the last view entered
3267 sLastViewEntered = (NSView*)self;
3269 // checks to see if we should change to the hand cursor
3270 [self setHandScrollCursor:theEvent];
3273 // check if we are in a hand scroll or if the user
3274 // has command and alt held down; if so, we do not want
3275 // gecko messing with the cursor.
3276 if ([ChildView areHandScrollModifiers:[theEvent modifierFlags]])
3279 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3280 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3282 // create native EventRecord for use by plugins
3283 EventRecord macEvent;
3284 macEvent.what = adjustCursorEvent;
3285 macEvent.message = 0;
3286 macEvent.when = ::TickCount();
3287 ::GetGlobalMouse(&macEvent.where);
3288 macEvent.modifiers = ::GetCurrentEventKeyModifiers();
3289 geckoEvent.nativeMsg = &macEvent;
3291 mGeckoChild->DispatchWindowEvent(geckoEvent);
3293 NS_OBJC_END_TRY_ABORT_BLOCK;
3297 - (void)mouseDragged:(NSEvent*)theEvent
3299 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3301 if (![self ensureCorrectMouseEventTarget:theEvent])
3307 // if the handscroll flag is set, steal this event
3308 if (mInHandScroll) {
3309 [self updateHandScroll:theEvent];
3313 gLastDragView = self;
3314 gLastDragEvent = theEvent;
3316 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3317 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3319 // create native EventRecord for use by plugins
3320 EventRecord macEvent;
3321 macEvent.what = nullEvent;
3322 macEvent.message = 0;
3323 macEvent.when = ::TickCount();
3324 ::GetGlobalMouse(&macEvent.where);
3325 macEvent.modifiers = btnState | ::GetCurrentEventKeyModifiers();
3326 geckoEvent.nativeMsg = &macEvent;
3328 mGeckoChild->DispatchWindowEvent(geckoEvent);
3330 // Note, sending the above event might have destroyed our widget since we didn't retain.
3331 // Fine so long as we don't access any local variables from here on.
3333 gLastDragView = nil;
3334 gLastDragEvent = nil;
3335 // XXX maybe call markedTextSelectionChanged:client: here?
3337 NS_OBJC_END_TRY_ABORT_BLOCK;
3341 - (void)rightMouseDown:(NSEvent *)theEvent
3343 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3345 if (![self ensureCorrectMouseEventTarget:theEvent])
3348 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3350 [self maybeRollup:theEvent];
3354 // The right mouse went down, fire off a right mouse down event to gecko
3355 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
3356 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3357 geckoEvent.button = nsMouseEvent::eRightButton;
3358 geckoEvent.clickCount = [theEvent clickCount];
3360 // create native EventRecord for use by plugins
3361 EventRecord macEvent;
3362 macEvent.what = mouseDown;
3363 macEvent.message = 0;
3364 macEvent.when = ::TickCount();
3365 ::GetGlobalMouse(&macEvent.where);
3366 macEvent.modifiers = controlKey; // fake a context menu click
3367 geckoEvent.nativeMsg = &macEvent;
3369 PRBool handled = mGeckoChild->DispatchWindowEvent(geckoEvent);
3374 [super rightMouseDown:theEvent]; // let the superview do context menu stuff
3376 NS_OBJC_END_TRY_ABORT_BLOCK;
3380 - (void)rightMouseUp:(NSEvent *)theEvent
3382 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3384 if (![self ensureCorrectMouseEventTarget:theEvent])
3390 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
3391 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3392 geckoEvent.button = nsMouseEvent::eRightButton;
3393 geckoEvent.clickCount = [theEvent clickCount];
3395 // create native EventRecord for use by plugins
3396 EventRecord macEvent;
3397 macEvent.what = mouseUp;
3398 macEvent.message = 0;
3399 macEvent.when = ::TickCount();
3400 ::GetGlobalMouse(&macEvent.where);
3401 macEvent.modifiers = controlKey; // fake a context menu click
3402 geckoEvent.nativeMsg = &macEvent;
3404 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3405 mGeckoChild->DispatchWindowEvent(geckoEvent);
3407 NS_OBJC_END_TRY_ABORT_BLOCK;
3411 - (void)rightMouseDragged:(NSEvent*)theEvent
3413 if (![self ensureCorrectMouseEventTarget:theEvent])
3419 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3420 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3421 geckoEvent.button = nsMouseEvent::eRightButton;
3423 // send event into Gecko by going directly to the
3425 mGeckoChild->DispatchWindowEvent(geckoEvent);
3429 - (void)otherMouseDown:(NSEvent *)theEvent
3431 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3433 if (![self ensureCorrectMouseEventTarget:theEvent])
3436 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3438 if ([self maybeRollup:theEvent])
3444 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
3445 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3446 geckoEvent.button = nsMouseEvent::eMiddleButton;
3447 geckoEvent.clickCount = [theEvent clickCount];
3449 mGeckoChild->DispatchWindowEvent(geckoEvent);
3451 NS_OBJC_END_TRY_ABORT_BLOCK;
3455 - (void)otherMouseUp:(NSEvent *)theEvent
3460 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
3461 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3462 geckoEvent.button = nsMouseEvent::eMiddleButton;
3464 mGeckoChild->DispatchWindowEvent(geckoEvent);
3468 - (void)otherMouseDragged:(NSEvent*)theEvent
3473 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3474 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3475 geckoEvent.button = nsMouseEvent::eMiddleButton;
3477 // send event into Gecko by going directly to the
3479 mGeckoChild->DispatchWindowEvent(geckoEvent);
3483 // Handle an NSScrollWheel event for a single axis only.
3484 -(void)scrollWheel:(NSEvent*)theEvent forAxis:(enum nsMouseScrollEvent::nsMouseScrollFlags)inAxis
3486 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3493 if (inAxis & nsMouseScrollEvent::kIsVertical)
3494 scrollDelta = -[theEvent deltaY];
3495 else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
3496 scrollDelta = -[theEvent deltaX];
3498 return; // caller screwed up
3500 if (scrollDelta == 0)
3501 // No sense in firing off a Gecko event. Note that as of 10.4 Tiger,
3502 // a single NSScrollWheel event might result in deltaX = deltaY = 0.
3505 nsMouseScrollEvent geckoEvent(PR_TRUE, NS_MOUSE_SCROLL, nsnull);
3506 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3507 geckoEvent.scrollFlags |= inAxis;
3509 // Gecko only understands how to scroll by an integer value. Using floor
3510 // and ceil is better than truncating the fraction, especially when
3512 if (scrollDelta < 0)
3513 geckoEvent.delta = (PRInt32)floorf(scrollDelta);
3515 geckoEvent.delta = (PRInt32)ceilf(scrollDelta);
3517 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3518 mGeckoChild->DispatchWindowEvent(geckoEvent);
3522 // dispatch scroll wheel carbon event for plugins
3525 OSStatus err = ::MacCreateEvent(NULL,
3527 kEventMouseWheelMoved,
3528 TicksToEventTime(TickCount()),
3529 kEventAttributeUserEvent,
3532 EventMouseWheelAxis axis;
3533 if (inAxis & nsMouseScrollEvent::kIsVertical)
3534 axis = kEventMouseWheelAxisY;
3535 else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
3536 axis = kEventMouseWheelAxisX;
3538 SetEventParameter(theEvent,
3539 kEventParamMouseWheelAxis,
3541 sizeof(EventMouseWheelAxis),
3544 SInt32 delta = (SInt32)-geckoEvent.delta;
3545 SetEventParameter(theEvent,
3546 kEventParamMouseWheelDelta,
3552 ::GetGlobalMouse(&mouseLoc);
3553 SetEventParameter(theEvent,
3554 kEventParamMouseLocation,
3559 ::SendEventToEventTarget(theEvent, GetWindowEventTarget((WindowRef)[[self window] windowRef]));
3560 ReleaseEvent(theEvent);
3564 NS_OBJC_END_TRY_ABORT_BLOCK;
3568 -(void)scrollWheel:(NSEvent*)theEvent
3570 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3572 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3574 if ([self maybeRollup:theEvent])
3580 // It's possible for a single NSScrollWheel event to carry both useful
3581 // deltaX and deltaY, for example, when the "wheel" is a trackpad.
3582 // NSMouseScrollEvent can only carry one axis at a time, so the system
3583 // event will be split into two Gecko events if necessary.
3584 [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsVertical];
3587 [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsHorizontal];
3589 NS_OBJC_END_TRY_ABORT_BLOCK;
3593 -(NSMenu*)menuForEvent:(NSEvent*)theEvent
3595 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
3597 if (!mGeckoChild || [self isPluginView])
3600 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3602 [self maybeRollup:theEvent];
3606 // Cocoa doesn't always dispatch a mouseDown: for a control-click event,
3607 // depends on what we return from menuForEvent:. Gecko always expects one
3608 // and expects the mouse down event before the context menu event, so
3609 // get that event sent first if this is a left mouse click.
3610 if ([theEvent type] == NSLeftMouseDown) {
3611 [self mouseDown:theEvent];
3616 nsMouseEvent geckoEvent(PR_TRUE, NS_CONTEXTMENU, nsnull, nsMouseEvent::eReal);
3617 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3618 geckoEvent.button = nsMouseEvent::eRightButton;
3619 mGeckoChild->DispatchWindowEvent(geckoEvent);
3623 [self maybeInitContextMenuTracking];
3625 // Go up our view chain to fetch the correct menu to return.
3626 return [self contextMenu];
3628 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
3632 - (NSMenu*)contextMenu
3634 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
3636 NSView* superView = [self superview];
3637 if ([superView respondsToSelector:@selector(contextMenu)])
3638 return [(NSView<mozView>*)superView contextMenu];
3642 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
3646 - (TopLevelWindowData*)ensureWindowData
3648 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
3650 WindowDataMap* windowMap = [WindowDataMap sharedWindowDataMap];
3652 TopLevelWindowData* windowData = [windowMap dataForWindow:mWindow];
3653 if (mWindow && !windowData)
3655 windowData = [[TopLevelWindowData alloc] initWithWindow:mWindow];
3656 [windowMap setData:windowData forWindow:mWindow]; // takes ownership
3657 [windowData release];
3661 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
3665 static PRBool ConvertUnicodeToCharCode(PRUnichar inUniChar, unsigned char* outChar)
3667 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
3669 UnicodeToTextInfo converterInfo;
3670 TextEncoding systemEncoding;
3671 Str255 convertedString;
3676 err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &systemEncoding);
3680 err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo);
3684 err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar), &inUniChar, convertedString);
3688 *outChar = convertedString[1];
3689 ::DisposeUnicodeToTextInfo(&converterInfo);
3692 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE);
3696 static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& macEvent, PRUint32 keyType = 0)
3698 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3700 UInt32 charCode = 0;
3701 if ([cocoaEvent type] == NSFlagsChanged) {
3702 macEvent.what = keyType == NS_KEY_DOWN ? keyDown : keyUp;
3704 if ([[cocoaEvent characters] length] > 0)
3705 charCode = [[cocoaEvent characters] characterAtIndex:0];
3706 if ([cocoaEvent type] == NSKeyDown)
3707 macEvent.what = [cocoaEvent isARepeat] ? autoKey : keyDown;
3709 macEvent.what = keyUp;
3712 if (charCode >= 0x0080) {
3714 case NSUpArrowFunctionKey:
3715 charCode = kUpArrowCharCode;
3717 case NSDownArrowFunctionKey:
3718 charCode = kDownArrowCharCode;
3720 case NSLeftArrowFunctionKey:
3721 charCode = kLeftArrowCharCode;
3723 case NSRightArrowFunctionKey:
3724 charCode = kRightArrowCharCode;
3727 unsigned char convertedCharCode;
3728 if (ConvertUnicodeToCharCode(charCode, &convertedCharCode))
3729 charCode = convertedCharCode;
3730 //NSLog(@"charcode is %d, converted to %c, char is %@", charCode, convertedCharCode, [cocoaEvent characters]);
3734 macEvent.message = (charCode & 0x00FF) | ([cocoaEvent keyCode] << 8);
3735 macEvent.when = ::TickCount();
3736 ::GetGlobalMouse(&macEvent.where);
3737 macEvent.modifiers = ::GetCurrentEventKeyModifiers();
3739 NS_OBJC_END_TRY_ABORT_BLOCK;
3742 // Key code constants
3745 kEscapeKeyCode = 0x35,
3746 kRCommandKeyCode = 0x36, // right command key
3747 kCommandKeyCode = 0x37,
3748 kShiftKeyCode = 0x38,
3749 kCapsLockKeyCode = 0x39,
3750 kOptionkeyCode = 0x3A,
3751 kControlKeyCode = 0x3B,
3752 kRShiftKeyCode = 0x3C, // right shift key
3753 kROptionKeyCode = 0x3D, // right option key
3754 kRControlKeyCode = 0x3E, // right control key
3755 kClearKeyCode = 0x47,
3774 kPrintScreenKeyCode = kF13KeyCode,
3775 kScrollLockKeyCode = kF14KeyCode,
3776 kPauseKeyCode = kF15KeyCode,
3779 kKeypad0KeyCode = 0x52,
3780 kKeypad1KeyCode = 0x53,
3781 kKeypad2KeyCode = 0x54,
3782 kKeypad3KeyCode = 0x55,
3783 kKeypad4KeyCode = 0x56,
3784 kKeypad5KeyCode = 0x57,
3785 kKeypad6KeyCode = 0x58,
3786 kKeypad7KeyCode = 0x59,
3787 kKeypad8KeyCode = 0x5B,
3788 kKeypad9KeyCode = 0x5C,
3790 // The following key codes are not defined until Mac OS X 10.5
3791 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
3804 kKeypadMultiplyKeyCode = 0x43,
3805 kKeypadAddKeyCode = 0x45,
3806 kKeypadSubtractKeyCode = 0x4E,
3807 kKeypadDecimalKeyCode = 0x41,
3808 kKeypadDivideKeyCode = 0x4B,
3809 kKeypadEqualsKeyCode = 0x51, // no correpsonding gecko key code
3810 kEnterKeyCode = 0x4C,
3811 kReturnKeyCode = 0x24,
3812 kPowerbookEnterKeyCode = 0x34, // Enter on Powerbook's keyboard is different
3814 kInsertKeyCode = 0x72, // also help key
3815 kDeleteKeyCode = 0x75, // also forward delete key
3817 kTildeKeyCode = 0x32,
3818 kBackspaceKeyCode = 0x33,
3819 kHomeKeyCode = 0x73,
3821 kPageUpKeyCode = 0x74,
3822 kPageDownKeyCode = 0x79,
3823 kLeftArrowKeyCode = 0x7B,
3824 kRightArrowKeyCode = 0x7C,
3825 kUpArrowKeyCode = 0x7E,
3826 kDownArrowKeyCode = 0x7D
3830 static PRBool IsPrintableChar(PRUnichar aChar)
3832 return (aChar >= 0x20 && aChar <= 0x7E) || aChar >= 0xA0;
3835 static PRUint32 GetGeckoKeyCodeFromChar(PRUnichar aChar)
3837 // We don't support the key code for non-ASCII characters
3841 if (aChar >= 'a' && aChar <= 'z') // lowercase
3842 return PRUint32(toupper(aChar));
3843 else if (aChar >= 'A' && aChar <= 'Z') // uppercase
3844 return PRUint32(aChar);
3845 else if (aChar >= '0' && aChar <= '9')
3846 return PRUint32(aChar - '0' + NS_VK_0);
3850 case kReturnCharCode:
3851 case kEnterCharCode:
3853 return NS_VK_RETURN;
3856 return NS_VK_OPEN_BRACKET;
3859 return NS_VK_CLOSE_BRACKET;
3864 case '\\': return NS_VK_BACK_SLASH;
3865 case ' ': return NS_VK_SPACE;
3866 case ';': return NS_VK_SEMICOLON;
3867 case '=': return NS_VK_EQUALS;
3868 case ',': return NS_VK_COMMA;
3869 case '.': return NS_VK_PERIOD;
3870 case '/': return NS_VK_SLASH;
3871 case '`': return NS_VK_BACK_QUOTE;
3872 case '\t': return NS_VK_TAB;
3873 case '-': return NS_VK_SUBTRACT;
3874 case '+': return NS_VK_ADD;
3877 if (!IsPrintableChar(aChar))
3878 NS_WARNING("GetGeckoKeyCodeFromChar has failed.");
3884 static PRUint32 ConvertMacToGeckoKeyCode(UInt32 keyCode, nsKeyEvent* aKeyEvent, NSString* characters)
3886 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
3888 PRUint32 geckoKeyCode = 0;
3889 PRUnichar charCode = 0;
3890 if ([characters length])
3891 charCode = [characters characterAtIndex:0];
3895 // modifiers. We don't get separate events for these
3896 case kEscapeKeyCode: geckoKeyCode = NS_VK_ESCAPE; break;
3897 case kRCommandKeyCode:
3898 case kCommandKeyCode: geckoKeyCode = NS_VK_META; break;
3899 case kRShiftKeyCode:
3900 case kShiftKeyCode: geckoKeyCode = NS_VK_SHIFT; break;
3901 case kCapsLockKeyCode: geckoKeyCode = NS_VK_CAPS_LOCK; break;
3902 case kRControlKeyCode:
3903 case kControlKeyCode: geckoKeyCode = NS_VK_CONTROL; break;
3904 case kROptionKeyCode:
3905 case kOptionkeyCode: geckoKeyCode = NS_VK_ALT; break;
3906 case kClearKeyCode: geckoKeyCode = NS_VK_CLEAR; break;
3909 case kF1KeyCode: geckoKeyCode = NS_VK_F1; break;
3910 case kF2KeyCode: geckoKeyCode = NS_VK_F2; break;
3911 case kF3KeyCode: geckoKeyCode = NS_VK_F3; break;
3912 case kF4KeyCode: geckoKeyCode = NS_VK_F4; break;
3913 case kF5KeyCode: geckoKeyCode = NS_VK_F5; break;
3914 case kF6KeyCode: geckoKeyCode = NS_VK_F6; break;
3915 case kF7KeyCode: geckoKeyCode = NS_VK_F7; break;
3916 case kF8KeyCode: geckoKeyCode = NS_VK_F8; break;
3917 case kF9KeyCode: geckoKeyCode = NS_VK_F9; break;
3918 case kF10KeyCode: geckoKeyCode = NS_VK_F10; break;
3919 case kF11KeyCode: geckoKeyCode = NS_VK_F11; break;
3920 case kF12KeyCode: geckoKeyCode = NS_VK_F12; break;
3921 // case kF13KeyCode: geckoKeyCode = NS_VK_F13; break; // clash with the 3 below
3922 // case kF14KeyCode: geckoKeyCode = NS_VK_F14; break;
3923 // case kF15KeyCode: geckoKeyCode = NS_VK_F15; break;
3924 case kPauseKeyCode: geckoKeyCode = NS_VK_PAUSE; break;
3925 case kScrollLockKeyCode: geckoKeyCode = NS_VK_SCROLL_LOCK; break;
3926 case kPrintScreenKeyCode: geckoKeyCode = NS_VK_PRINTSCREEN; break;
3929 case kKeypad0KeyCode: geckoKeyCode = NS_VK_NUMPAD0; break;
3930 case kKeypad1KeyCode: geckoKeyCode = NS_VK_NUMPAD1; break;
3931 case kKeypad2KeyCode: geckoKeyCode = NS_VK_NUMPAD2; break;
3932 case kKeypad3KeyCode: geckoKeyCode = NS_VK_NUMPAD3; break;
3933 case kKeypad4KeyCode: geckoKeyCode = NS_VK_NUMPAD4; break;
3934 case kKeypad5KeyCode: geckoKeyCode = NS_VK_NUMPAD5; break;
3935 case kKeypad6KeyCode: geckoKeyCode = NS_VK_NUMPAD6; break;
3936 case kKeypad7KeyCode: geckoKeyCode = NS_VK_NUMPAD7; break;
3937 case kKeypad8KeyCode: geckoKeyCode = NS_VK_NUMPAD8; break;
3938 case kKeypad9KeyCode: geckoKeyCode = NS_VK_NUMPAD9; break;
3940 case kKeypadMultiplyKeyCode: geckoKeyCode = NS_VK_MULTIPLY; break;
3941 case kKeypadAddKeyCode: geckoKeyCode = NS_VK_ADD; break;
3942 case kKeypadSubtractKeyCode: geckoKeyCode = NS_VK_SUBTRACT; break;
3943 case kKeypadDecimalKeyCode: geckoKeyCode = NS_VK_DECIMAL; break;
3944 case kKeypadDivideKeyCode: geckoKeyCode = NS_VK_DIVIDE; break;
3946 // these may clash with forward delete and help
3947 case kInsertKeyCode: geckoKeyCode = NS_VK_INSERT; break;
3948 case kDeleteKeyCode: geckoKeyCode = NS_VK_DELETE; break;
3950 case kBackspaceKeyCode: geckoKeyCode = NS_VK_BACK; break;
3951 case kTabKeyCode: geckoKeyCode = NS_VK_TAB; break;
3952 case kHomeKeyCode: geckoKeyCode = NS_VK_HOME; break;
3953 case kEndKeyCode: geckoKeyCode = NS_VK_END; break;
3954 case kPageUpKeyCode: geckoKeyCode = NS_VK_PAGE_UP; break;
3955 case kPageDownKeyCode: geckoKeyCode = NS_VK_PAGE_DOWN; break;
3956 case kLeftArrowKeyCode: geckoKeyCode = NS_VK_LEFT; break;
3957 case kRightArrowKeyCode: geckoKeyCode = NS_VK_RIGHT; break;
3958 case kUpArrowKeyCode: geckoKeyCode = NS_VK_UP; break;
3959 case kDownArrowKeyCode: geckoKeyCode = NS_VK_DOWN; break;
3960 case kVK_ANSI_1: geckoKeyCode = NS_VK_1; break;
3961 case kVK_ANSI_2: geckoKeyCode = NS_VK_2; break;
3962 case kVK_ANSI_3: geckoKeyCode = NS_VK_3; break;
3963 case kVK_ANSI_4: geckoKeyCode = NS_VK_4; break;
3964 case kVK_ANSI_5: geckoKeyCode = NS_VK_5; break;
3965 case kVK_ANSI_6: geckoKeyCode = NS_VK_6; break;
3966 case kVK_ANSI_7: geckoKeyCode = NS_VK_7; break;
3967 case kVK_ANSI_8: geckoKeyCode = NS_VK_8; break;
3968 case kVK_ANSI_9: geckoKeyCode = NS_VK_9; break;
3969 case kVK_ANSI_0: geckoKeyCode = NS_VK_0; break;
3972 // if we haven't gotten the key code already, look at the char code
3973 geckoKeyCode = GetGeckoKeyCodeFromChar(charCode);
3976 return geckoKeyCode;
3978 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
3982 static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
3986 // this table is used to determine which keys are special and should not generate a charCode
3989 // modifiers - we don't get separate events for these yet
3990 case kEscapeKeyCode:
3992 case kRShiftKeyCode:
3993 case kCommandKeyCode:
3994 case kRCommandKeyCode:
3995 case kCapsLockKeyCode:
3996 case kControlKeyCode:
3997 case kRControlKeyCode:
3998 case kOptionkeyCode:
3999 case kROptionKeyCode:
4016 case kScrollLockKeyCode:
4017 case kPrintScreenKeyCode:
4019 case kInsertKeyCode:
4020 case kDeleteKeyCode:
4022 case kBackspaceKeyCode:
4026 case kPageUpKeyCode:
4027 case kPageDownKeyCode:
4028 case kLeftArrowKeyCode:
4029 case kRightArrowKeyCode:
4030 case kUpArrowKeyCode:
4031 case kDownArrowKeyCode:
4032 case kReturnKeyCode:
4034 case kPowerbookEnterKeyCode:
4035 isSpecial = PR_TRUE;
4039 isSpecial = PR_FALSE;
4047 static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
4049 // this is not character inputting event, simply.
4050 if (!aEvent.isChar || !aEvent.charCode)
4052 // if this is unicode char inputting event, we don't need to check
4053 // ctrl/alt/command keys
4054 if (aEvent.charCode > 0x7F)
4056 // ASCII chars should be inputted without ctrl/alt/command keys
4057 return !aEvent.isControl && !aEvent.isAlt && !aEvent.isMeta;
4061 // Basic conversion for cocoa to gecko events, common to all conversions.
4062 // Note that it is OK for inEvent to be nil.
4063 - (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
4065 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4067 NS_ASSERTION(outGeckoEvent, "convertGenericCocoaEvent:toGeckoEvent: requires non-null outGeckoEvent");
4071 outGeckoEvent->widget = [self widget];
4072 outGeckoEvent->time = PR_IntervalNow();
4073 outGeckoEvent->nativeMsg = inEvent;
4076 unsigned int modifiers = [inEvent modifierFlags];
4077 outGeckoEvent->isShift = ((modifiers & NSShiftKeyMask) != 0);
4078 outGeckoEvent->isControl = ((modifiers & NSControlKeyMask) != 0);
4079 outGeckoEvent->isAlt = ((modifiers & NSAlternateKeyMask) != 0);
4080 outGeckoEvent->isMeta = ((modifiers & NSCommandKeyMask) != 0);
4083 NS_OBJC_END_TRY_ABORT_BLOCK;
4087 - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
4089 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4091 NS_ASSERTION(aMouseEvent && outGeckoEvent, "convertCocoaMouseEvent:toGeckoEvent: requires non-null arguments");
4092 if (!aMouseEvent || !outGeckoEvent)
4095 [self convertGenericCocoaEvent:aMouseEvent toGeckoEvent:outGeckoEvent];
4097 // convert point to view coordinate system
4098 NSPoint localPoint = [self convertPoint:[aMouseEvent locationInWindow] fromView:nil];
4099 outGeckoEvent->refPoint.x = static_cast<nscoord>(localPoint.x);
4100 outGeckoEvent->refPoint.y = static_cast<nscoord>(localPoint.y);
4102 NS_OBJC_END_TRY_ABORT_BLOCK;
4106 #define CHARCODE_MASK_1 0x00FF0000
4107 #define CHARCODE_MASK_2 0x000000FF
4108 #define CHARCODE_MASK 0x00FF00FF
4109 //#define DEBUG_KB 1
4112 KeyTranslateToUnicode(Handle aHandle, UInt32 aKeyCode, UInt32 aModifiers,
4113 TextEncoding aEncoding)
4116 NSLog(@"**** KeyTranslateToUnicode: aHandle: %p, aKeyCode: %X, aModifiers: %X, aEncoding: %X",
4117 aHandle, aKeyCode, aModifiers, aEncoding);
4118 PRBool isShift = aModifiers & shiftKey;
4119 PRBool isCtrl = aModifiers & controlKey;
4120 PRBool isOpt = aModifiers & optionKey;
4121 PRBool isCmd = aModifiers & cmdKey;
4122 PRBool isCL = aModifiers & alphaLock;
4123 PRBool isNL = aModifiers & kEventKeyModifierNumLockMask;
4124 NSLog(@" Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
4125 isShift ? "ON" : "off", isCtrl ? "ON" : "off", isOpt ? "ON" : "off",
4126 isCmd ? "ON" : "off", isCL ? "ON" : "off", isNL ? "ON" : "off");
4130 ::KeyTranslate(aHandle, aKeyCode | aModifiers, &state) & CHARCODE_MASK;
4131 // If state is not zero, it is in dead key state. Then, we need to recall
4132 // KeyTranslate for getting the actual character.
4135 ::KeyTranslate(aHandle, aKeyCode | aModifiers, &state) & CHARCODE_MASK;
4140 if (val & CHARCODE_MASK_1)
4141 buf[len++] = (val & CHARCODE_MASK_1) >> 16;
4142 buf[len++] = val & CHARCODE_MASK_2;
4145 ::CFStringCreateWithBytes(kCFAllocatorDefault, buf, len,
4146 (CFStringEncoding)aEncoding, false);
4147 ch = ::CFStringGetLength(str) == 1 ?
4148 ::CFStringGetCharacterAtIndex(str, 0) : 0;
4151 NSLog(@" result: %X(%C)", ch, ch > ' ' ? ch : ' ');
4157 UCKeyTranslateToUnicode(const UCKeyboardLayout* aHandle, UInt32 aKeyCode, UInt32 aModifiers,
4161 NSLog(@"**** UCKeyTranslateToUnicode: aHandle: %p, aKeyCode: %X, aModifiers: %X, aKbType: %X",
4162 aHandle, aKeyCode, aModifiers, aKbType);
4163 PRBool isShift = aModifiers & shiftKey;
4164 PRBool isCtrl = aModifiers & controlKey;
4165 PRBool isOpt = aModifiers & optionKey;
4166 PRBool isCmd = aModifiers & cmdKey;
4167 PRBool isCL = aModifiers & alphaLock;
4168 PRBool isNL = aModifiers & kEventKeyModifierNumLockMask;
4169 NSLog(@" Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
4170 isShift ? "ON" : "off", isCtrl ? "ON" : "off", isOpt ? "ON" : "off",
4171 isCmd ? "ON" : "off", isCL ? "ON" : "off", isNL ? "ON" : "off");
4173 UInt32 deadKeyState = 0;
4176 OSStatus err = ::UCKeyTranslate(aHandle, aKeyCode,
4177 kUCKeyActionDown, aModifiers >> 8,
4178 aKbType, kUCKeyTranslateNoDeadKeysMask,
4179 &deadKeyState, 5, &len, chars);
4180 PRUint32 ch = (err == noErr && len == 1) ? PRUint32(chars[0]) : 0;
4182 NSLog(@" result: %X(%C)", ch, ch > ' ' ? ch : ' ');
4187 struct KeyTranslateData {
4188 KeyTranslateData() {
4189 mUchr.mLayout = nsnull;
4191 mKchr.mHandle = nsnull;
4192 mKchr.mEncoding = nsnull;
4195 // The script of the layout determines the encoding of characters obtained
4196 // from kchr resources.
4198 // The keyboard layout identifier
4202 const UCKeyboardLayout* mLayout;
4207 TextEncoding mEncoding;
4212 GetUniCharFromKeyTranslate(KeyTranslateData& aData,
4213 UInt32 aKeyCode, UInt32 aModifiers)
4215 if (aData.mUchr.mLayout) {
4216 return UCKeyTranslateToUnicode(aData.mUchr.mLayout, aKeyCode, aModifiers,
4217 aData.mUchr.mKbType);
4219 if (aData.mKchr.mHandle) {
4220 return KeyTranslateToUnicode(aData.mKchr.mHandle, aKeyCode, aModifiers,
4221 aData.mKchr.mEncoding);
4227 GetScriptFromKeyboardLayout(SInt32 aLayoutID)
4229 switch (aLayoutID) {
4231 case -2: return smRoman; // US-Extended
4232 case -18944: return smGreek; // Greek
4233 default: NS_NOTREACHED("unknown keyboard layout");
4239 GetInputSourceIDFromKeyboardLayout(SInt32 aLayoutID)
4241 NS_ASSERTION(nsToolkit::OnLeopardOrLater() &&
4242 Leopard_TISCopyCurrentKeyboardLayoutInputSource &&
4243 Leopard_TISGetInputSourceProperty &&
4244 Leopard_TISCreateInputSourceList &&
4245 kOurTISPropertyUnicodeKeyLayoutData &&
4246 kOurTISPropertyInputSourceID,
4247 "GetInputSourceIDFromKeyboardLayout should only be used on Leopard or later.");
4249 KeyboardLayoutRef keylayout;
4250 if (KLGetKeyboardLayoutWithIdentifier(aLayoutID, &keylayout) != noErr)
4253 const void* uchrFromID;
4254 if (KLGetKeyboardLayoutProperty(keylayout, kKLuchrData, &uchrFromID) != noErr)
4257 CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, NULL, NULL);
4258 CFArrayRef inputSources = Leopard_TISCreateInputSourceList(dict, true);
4261 CFStringRef sourceID = nsnull;
4262 for (CFIndex i = 0; i < CFArrayGetCount(inputSources); ++i) {
4263 TISInputSourceRef tis = static_cast<TISInputSourceRef>(const_cast<void *>(CFArrayGetValueAtIndex(inputSources, i)));
4264 CFDataRef data = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
4268 const UCKeyboardLayout* uchr = reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(data));
4269 if (uchr == uchrFromID) {
4270 sourceID = static_cast<CFStringRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyInputSourceID));
4275 CFRelease(inputSources);
4281 GetUSLayoutCharFromKeyTranslate(UInt32 aKeyCode, UInt32 aModifiers)
4283 KeyTranslateData kt;
4284 Handle handle = ::GetResource('uchr', kKLUSKeyboard); // US keyboard layout
4285 if (!handle || !(*handle)) {
4286 NS_ERROR("US keyboard layout doesn't have uchr resource");
4289 UInt32 kbType = 40; // ANSI, don't use actual layout
4290 return UCKeyTranslateToUnicode((UCKeyboardLayout*)(*handle), aKeyCode,
4291 aModifiers, kbType);
4294 - (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent
4296 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4298 NS_ASSERTION(aKeyEvent && outGeckoEvent, "convertCocoaKeyEvent:toGeckoEvent: requires non-null arguments");
4299 if (!aKeyEvent || !outGeckoEvent)
4302 [self convertGenericCocoaEvent:aKeyEvent toGeckoEvent:outGeckoEvent];
4304 // coords for key events are always 0,0
4305 outGeckoEvent->refPoint.x = outGeckoEvent->refPoint.y = 0;
4307 // Initialize whether or not we are using charCodes to false.
4308 outGeckoEvent->isChar = PR_FALSE;
4310 // Check to see if the message is a key press that does not involve
4311 // one of our special key codes.
4312 if (outGeckoEvent->message == NS_KEY_PRESS && !IsSpecialGeckoKey([aKeyEvent keyCode])) {
4313 outGeckoEvent->isChar = PR_TRUE; // this is not a special key
4315 outGeckoEvent->charCode = 0;
4316 outGeckoEvent->keyCode = 0; // not set for key press events
4318 NSString* chars = [aKeyEvent characters];
4319 if ([chars length] > 0)
4320 outGeckoEvent->charCode = [chars characterAtIndex:0];
4322 // convert control-modified charCode to raw charCode (with appropriate case)
4323 if (outGeckoEvent->isControl && outGeckoEvent->charCode <= 26)
4324 outGeckoEvent->charCode += (outGeckoEvent->isShift) ? ('A' - 1) : ('a' - 1);
4326 // Accel and access key handling needs to know the characters that this
4327 // key produces with Shift up or down. So, provide this information
4328 // when Ctrl or Command or Alt is pressed.
4329 if (outGeckoEvent->isControl || outGeckoEvent->isMeta ||
4330 outGeckoEvent->isAlt) {
4331 KeyTranslateData kt;
4333 if (gOverrideKeyboardLayout.mOverrideEnabled) {
4334 kt.mLayoutID = gOverrideKeyboardLayout.mKeyboardLayout;
4335 kt.mScript = GetScriptFromKeyboardLayout(kt.mLayoutID);
4337 // GetScriptManagerVariable and GetScriptVariable are both deprecated.
4338 // KLGetCurrentKeyboardLayout is newer but also deprecated in OS X
4339 // 10.5. It's not clear from the documentation but it seems that
4340 // KLGetKeyboardLayoutProperty with kKLGroupIdentifier may provide the
4341 // script identifier for a KeyboardLayoutRef (bug 432388 comment 6).
4342 // The "Text Input Source Services" API is not available prior to OS X
4344 kt.mScript = ::GetScriptManagerVariable(smKeyScript);
4345 kt.mLayoutID = ::GetScriptVariable(kt.mScript, smScriptKeys);
4348 CFDataRef uchr = NULL;
4349 // GetResource('uchr', kt.mLayoutID) fails on OS X 10.5
4350 if (nsToolkit::OnLeopardOrLater() &&
4351 Leopard_TISCopyCurrentKeyboardLayoutInputSource &&
4352 Leopard_TISGetInputSourceProperty &&
4353 Leopard_TISCreateInputSourceList &&
4354 kOurTISPropertyUnicodeKeyLayoutData &&
4355 kOurTISPropertyInputSourceID) {
4356 if (gOverrideKeyboardLayout.mOverrideEnabled) {
4357 CFStringRef sourceID = GetInputSourceIDFromKeyboardLayout(kt.mLayoutID);
4358 NS_ASSERTION(sourceID, "unable to map keyboard layout ID to input source ID");
4359 const void* keys[] = { kOurTISPropertyInputSourceID };
4360 const void* vals[] = { sourceID };
4361 CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 1, NULL, NULL);
4362 CFArrayRef inputSources = Leopard_TISCreateInputSourceList(dict, true);
4364 if (CFArrayGetCount(inputSources) == 1) {
4365 TISInputSourceRef tis = static_cast<TISInputSourceRef>(const_cast<void *>(CFArrayGetValueAtIndex(inputSources, 0)));
4366 uchr = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
4368 CFRelease(inputSources);
4370 TISInputSourceRef tis = Leopard_TISCopyCurrentKeyboardLayoutInputSource();
4371 uchr = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
4375 // This fails for Azeri on 10.4 even though kKLKind (2) indicates that
4376 // the layout has a uchr resource. Perhaps KLGetKeyboardLayoutProperty
4377 // with kKLuchrData would be helpful here.
4378 Handle handle = ::GetResource('uchr', kt.mLayoutID);
4380 // We should be here on OS X 10.5 for any Apple provided layout, as
4381 // they are all uchr. It may be possible to still use kchr resources
4382 // from elsewhere, so they may be picked by
4383 // GetScriptManagerVariable(smKCHRCache) below
4384 kt.mUchr.mLayout = reinterpret_cast<const UCKeyboardLayout*>
4385 (CFDataGetBytePtr(uchr));
4386 } else if (handle) {
4387 // uchr (Unicode) keyboard layout resource prior to 10.5.
4388 kt.mUchr.mLayout = *((UCKeyboardLayout**)handle);
4390 // kchr (non-Unicode) keyboard layout resource.
4392 // There are no know cases where GetResource succeeds here, and so
4393 // tests (gOverrideKeyboardLayout.mOverrideEnabled) currently end up
4394 // with no keyboard layout. KLGetKeyboardLayoutWithIdentifier and
4395 // KLGetKeyboardLayoutProperty with kKLKCHRData would be useful here.
4396 kt.mKchr.mHandle = ::GetResource('kchr', kt.mLayoutID);
4398 if (!kt.mKchr.mHandle && !gOverrideKeyboardLayout.mOverrideEnabled)
4399 kt.mKchr.mHandle = (char**)::GetScriptManagerVariable(smKCHRCache);
4400 if (kt.mKchr.mHandle) {
4402 ::GetTextEncodingFromScriptInfo(kt.mScript, kTextLanguageDontCare,
4403 kTextRegionDontCare,
4404 &kt.mKchr.mEncoding);
4406 kt.mKchr.mHandle = nsnull;
4410 // If a keyboard layout override is set, we also need to force the
4411 // keyboard type to something ANSI to avoid test failures on machines
4412 // with JIS keyboards (since the pair of keyboard layout and physical
4413 // keyboard type form the actual key layout). This assumes that the
4414 // test setting the override was written assuming an ANSI keyboard.
4415 if (kt.mUchr.mLayout)
4416 kt.mUchr.mKbType = gOverrideKeyboardLayout.mOverrideEnabled ? 40 : ::LMGetKbdType();
4418 UInt32 key = [aKeyEvent keyCode];
4420 // Caps lock and num lock modifier state:
4421 UInt32 lockState = 0;
4422 if ([aKeyEvent modifierFlags] & NSAlphaShiftKeyMask)
4423 lockState |= alphaLock;
4424 if ([aKeyEvent modifierFlags] & NSNumericPadKeyMask)
4425 lockState |= kEventKeyModifierNumLockMask;
4428 PRUint32 unshiftedChar = GetUniCharFromKeyTranslate(kt, key, lockState);
4429 UInt32 shiftLockMod = shiftKey | lockState;
4430 PRUint32 shiftedChar = GetUniCharFromKeyTranslate(kt, key, shiftLockMod);
4432 // characters generated with Cmd key
4433 // XXX we should remove CapsLock state, which changes characters from
4434 // Latin to Cyrillic with Russian layout on 10.4 only when Cmd key
4436 UInt32 numState = (lockState & ~alphaLock); // only num lock state
4437 PRUint32 uncmdedChar = GetUniCharFromKeyTranslate(kt, key, numState);
4438 UInt32 shiftNumMod = numState | shiftKey;
4439 PRUint32 uncmdedShiftChar =
4440 GetUniCharFromKeyTranslate(kt, key, shiftNumMod);
4441 PRUint32 uncmdedUSChar = GetUSLayoutCharFromKeyTranslate(key, numState);
4442 UInt32 cmdNumMod = cmdKey | numState;
4443 PRUint32 cmdedChar = GetUniCharFromKeyTranslate(kt, key, cmdNumMod);
4444 UInt32 cmdShiftNumMod = shiftKey | cmdNumMod;
4445 PRUint32 cmdedShiftChar =
4446 GetUniCharFromKeyTranslate(kt, key, cmdShiftNumMod);
4448 // Is the keyboard layout changed by Cmd key?
4449 // E.g., Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
4450 PRBool isCmdSwitchLayout = uncmdedChar != cmdedChar;
4451 // Is the keyboard layout for Latin, but Cmd key switches the layout?
4452 // I.e., Dvorak-QWERTY
4453 PRBool isDvorakQWERTY = isCmdSwitchLayout && kt.mScript == smRoman;
4455 // If the current keyboard is not Dvorak-QWERTY or Cmd is not pressed,
4456 // we should append unshiftedChar and shiftedChar for handling the
4457 // normal characters. These are the characters that the user is most
4458 // likely to associate with this key.
4459 if ((unshiftedChar || shiftedChar) &&
4460 (!outGeckoEvent->isMeta || !isDvorakQWERTY)) {
4461 nsAlternativeCharCode altCharCodes(unshiftedChar, shiftedChar);
4462 outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
4465 // Most keyboard layouts provide the same characters in the NSEvents
4466 // with Command+Shift as with Command. However, with Command+Shift we
4467 // want the character on the second level. e.g. With a US QWERTY
4468 // layout, we want "?" when the "/","?" key is pressed with
4471 // On a German layout, the OS gives us '/' with Cmd+Shift+SS(eszett)
4472 // even though Cmd+SS is 'SS' and Shift+'SS' is '?'. This '/' seems
4473 // like a hack to make the Cmd+"?" event look the same as the Cmd+"?"
4474 // event on a US keyboard. The user thinks they are typing Cmd+"?", so
4475 // we'll prefer the "?" character, replacing charCode with shiftedChar
4476 // when Shift is pressed. However, in case there is a layout where the
4477 // character unique to Cmd+Shift is the character that the user expects,
4478 // we'll send it as an alternative char.
4479 PRBool hasCmdShiftOnlyChar =
4480 cmdedChar != cmdedShiftChar && uncmdedShiftChar != cmdedShiftChar;
4481 PRUint32 originalCmdedShiftChar = cmdedShiftChar;
4483 // If we can make a good guess at the characters that the user would
4484 // expect this key combination to produce (with and without Shift) then
4485 // use those characters. This also corrects for CapsLock, which was
4487 if (!isCmdSwitchLayout) {
4488 // The characters produced with Command seem similar to those without
4491 cmdedChar = unshiftedChar;
4493 cmdedShiftChar = shiftedChar;
4494 } else if (uncmdedUSChar == cmdedChar) {
4495 // It looks like characters from a US layout are provided when Command
4497 PRUint32 ch = GetUSLayoutCharFromKeyTranslate(key, lockState);
4500 ch = GetUSLayoutCharFromKeyTranslate(key, shiftLockMod);
4502 cmdedShiftChar = ch;
4505 // Only charCode (not alternativeCharCodes) is available to javascript,
4506 // so attempt to set this to the most likely intended (or most useful)
4507 // character. Note that cmdedChar and cmdedShiftChar are usually
4508 // Latin/ASCII characters and that is what is wanted here as accel
4509 // keys are expected to be Latin characters.
4511 // XXX We should do something similar when Control is down (bug 429510).
4512 if (outGeckoEvent->isMeta &&
4513 !(outGeckoEvent->isControl || outGeckoEvent->isAlt)) {
4515 // The character to use for charCode.
4516 PRUint32 preferredCharCode = 0;
4517 preferredCharCode = outGeckoEvent->isShift ? cmdedShiftChar : cmdedChar;
4519 if (preferredCharCode) {
4521 if (outGeckoEvent->charCode != preferredCharCode) {
4522 NSLog(@" charCode replaced: %X(%C) to %X(%C)",
4523 outGeckoEvent->charCode,
4524 outGeckoEvent->charCode > ' ' ? outGeckoEvent->charCode : ' ',
4526 preferredCharCode > ' ' ? preferredCharCode : ' ');
4529 outGeckoEvent->charCode = preferredCharCode;
4533 // If the current keyboard layout is switched by the Cmd key,
4534 // we should append cmdedChar and shiftedCmdChar that are
4535 // Latin char for the key. But don't append at Dvorak-QWERTY.
4536 if ((cmdedChar || cmdedShiftChar) &&
4537 isCmdSwitchLayout && !isDvorakQWERTY) {
4538 nsAlternativeCharCode altCharCodes(cmdedChar, cmdedShiftChar);
4539 outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
4541 // Special case for 'SS' key of German layout. See the comment of
4542 // hasCmdShiftOnlyChar definition for the detail.
4543 if (hasCmdShiftOnlyChar && originalCmdedShiftChar) {
4544 nsAlternativeCharCode altCharCodes(0, originalCmdedShiftChar);
4545 outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
4550 NSString* characters = nil;
4551 if ([aKeyEvent type] != NSFlagsChanged)
4552 characters = [aKeyEvent charactersIgnoringModifiers];
4554 outGeckoEvent->keyCode = ConvertMacToGeckoKeyCode([aKeyEvent keyCode], outGeckoEvent, characters);
4555 outGeckoEvent->charCode = 0;
4558 if (outGeckoEvent->message == NS_KEY_PRESS && !outGeckoEvent->isMeta)
4559 [NSCursor setHiddenUntilMouseMoves:YES];
4561 NS_OBJC_END_TRY_ABORT_BLOCK;
4565 // Called from PluginKeyEventsHandler() (a handler for Carbon TSM events) to
4566 // process a Carbon key event for the currently focused plugin. Both Unicode
4567 // characters and "Mac encoding characters" (in the MBCS or "multibyte
4568 // character system") are (or should be) available from aKeyEvent, but here we
4569 // use the MCBS characters. This is how the WebKit does things, and seems to
4570 // be what plugins expect.
4571 - (void) processPluginKeyEvent:(EventRef)aKeyEvent
4573 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4578 nsAutoRetainCocoaObject kungFuDeathGrip(self);
4580 UInt32 numCharCodes;
4581 OSStatus status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
4582 typeChar, NULL, 0, &numCharCodes, NULL);
4583 if (status != noErr)
4586 nsAutoTArray<unsigned char, 3> charCodes;
4587 charCodes.SetLength(numCharCodes);
4588 status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
4589 typeChar, NULL, numCharCodes, NULL, charCodes.Elements());
4590 if (status != noErr)
4594 status = ::GetEventParameter(aKeyEvent, kEventParamKeyModifiers,
4595 typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
4596 if (status != noErr)
4600 status = ::GetEventParameter(aKeyEvent, kEventParamKeyCode,
4601 typeUInt32, NULL, sizeof(macKeyCode), NULL, &macKeyCode);
4602 if (status != noErr)
4605 EventRef cloneEvent = ::CopyEvent(aKeyEvent);
4606 for (unsigned int i = 0; i < numCharCodes; ++i) {
4607 status = ::SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes,
4608 typeChar, 1, charCodes.Elements() + i);
4609 if (status != noErr)
4612 EventRecord eventRec;
4613 if (::ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
4614 nsKeyEvent keyDownEvent(PR_TRUE, NS_KEY_DOWN, mGeckoChild);
4616 PRUint32 keyCode(ConvertMacToGeckoKeyCode(macKeyCode, &keyDownEvent, @""));
4617 PRUint32 charCode(charCodes.ElementAt(i));
4619 keyDownEvent.time = PR_IntervalNow();
4620 keyDownEvent.nativeMsg = &eventRec;
4621 if (IsSpecialGeckoKey(macKeyCode)) {
4622 keyDownEvent.keyCode = keyCode;
4624 keyDownEvent.charCode = charCode;
4625 keyDownEvent.isChar = PR_TRUE;
4627 keyDownEvent.isShift = ((modifiers & shiftKey) != 0);
4628 keyDownEvent.isControl = ((modifiers & controlKey) != 0);
4629 keyDownEvent.isAlt = ((modifiers & optionKey) != 0);
4630 keyDownEvent.isMeta = ((modifiers & cmdKey) != 0); // Should never happen
4631 mGeckoChild->DispatchWindowEvent(keyDownEvent);
4637 ::ReleaseEvent(cloneEvent);
4639 NS_OBJC_END_TRY_ABORT_BLOCK;
4643 - (nsRect)sendCompositionEvent:(PRInt32) aEventType
4646 NSLog(@"****in sendCompositionEvent; type = %d", aEventType);
4650 return nsRect(0, 0, 0, 0);
4652 // static void init_composition_event( *aEvent, int aType)
4653 nsCompositionEvent event(PR_TRUE, aEventType, mGeckoChild);
4654 event.time = PR_IntervalNow();
4655 mGeckoChild->DispatchWindowEvent(event);
4656 return event.theReply.mCursorPosition;
4660 - (void)sendTextEvent:(PRUnichar*) aBuffer
4661 attributedString:(NSAttributedString*) aString
4662 selectedRange:(NSRange) selRange
4663 markedRange:(NSRange) markRange
4664 doCommit:(BOOL) doCommit
4667 NSLog(@"****in sendTextEvent; string = '%@'", aString);
4668 NSLog(@" markRange = %d, %d; selRange = %d, %d", markRange.location, markRange.length, selRange.location, selRange.length);
4674 nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, mGeckoChild);
4675 textEvent.time = PR_IntervalNow();
4676 textEvent.theText = aBuffer;
4678 FillTextRangeInTextEvent(&textEvent, aString, markRange, selRange);
4680 mGeckoChild->DispatchWindowEvent(textEvent);
4681 if (textEvent.rangeArray)
4682 delete [] textEvent.rangeArray;
4687 // NSTextInput implementation
4689 #define MAX_BUFFER_SIZE 32
4692 - (void)insertText:(id)insertString
4694 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4697 NSLog(@"****in insertText: '%@'", insertString);
4698 NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4703 nsAutoRetainCocoaObject kungFuDeathGrip(self);
4705 if (![insertString isKindOfClass:[NSAttributedString class]])
4706 insertString = [[[NSAttributedString alloc] initWithString:insertString] autorelease];
4708 NSString *tmpStr = [insertString string];
4709 unsigned int len = [tmpStr length];
4710 if (!nsTSMManager::IsComposing() && len == 0)
4711 return; // nothing to do
4712 PRUnichar buffer[MAX_BUFFER_SIZE];
4713 PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
4714 [tmpStr getCharacters:bufPtr];
4715 bufPtr[len] = PRUnichar('\0');
4717 if (len == 1 && !nsTSMManager::IsComposing()) {
4718 // don't let the same event be fired twice when hitting
4719 // enter/return! (Bug 420502)
4723 // dispatch keypress event with char instead of textEvent
4724 nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, mGeckoChild);
4725 geckoEvent.time = PR_IntervalNow();
4726 geckoEvent.charCode = bufPtr[0]; // gecko expects OS-translated unicode
4727 geckoEvent.keyCode = 0;
4728 geckoEvent.isChar = PR_TRUE;
4729 if (mKeyDownHandled)
4730 geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
4731 // don't set other modifiers from the current event, because here in
4732 // -insertText: they've already been taken into account in creating
4733 // the input string.
4735 // create native EventRecord for use by plugins
4736 EventRecord macEvent;
4738 // XXX The ASCII characters inputting mode of egbridge (Japanese IME)
4739 // might send the keyDown event with wrong keyboard layout if other
4740 // keyboard layouts are already loaded. In that case, the native event
4741 // doesn't match to this gecko event...
4742 ConvertCocoaKeyEventToMacEvent(mCurKeyEvent, macEvent);
4743 geckoEvent.nativeMsg = &macEvent;
4744 geckoEvent.isShift = ([mCurKeyEvent modifierFlags] & NSShiftKeyMask) != 0;
4745 if (!IsPrintableChar(geckoEvent.charCode)) {
4746 geckoEvent.keyCode =
4747 ConvertMacToGeckoKeyCode([mCurKeyEvent keyCode], &geckoEvent,
4748 [mCurKeyEvent charactersIgnoringModifiers]);
4749 geckoEvent.charCode = 0;
4752 // Note that insertText is not called only at key pressing.
4753 if (!IsPrintableChar(geckoEvent.charCode)) {
4754 geckoEvent.keyCode = GetGeckoKeyCodeFromChar(geckoEvent.charCode);
4755 geckoEvent.charCode = 0;
4759 mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
4760 mKeyPressSent = YES;
4763 if (!nsTSMManager::IsComposing()) {
4764 [self sendCompositionEvent:NS_COMPOSITION_START];
4765 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4766 nsTSMManager::StartComposing(self);
4767 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4770 if (nsTSMManager::IgnoreCommit()) {
4771 tmpStr = [tmpStr init];
4773 bufPtr[0] = PRUnichar('\0');
4775 [[[NSAttributedString alloc] initWithString:tmpStr] autorelease];
4777 [self sendTextEvent:bufPtr attributedString:insertString
4778 selectedRange:NSMakeRange(0, len)
4779 markedRange:mMarkedRange
4781 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4783 [self sendCompositionEvent:NS_COMPOSITION_END];
4784 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4785 nsTSMManager::EndComposing();
4786 mMarkedRange = NSMakeRange(NSNotFound, 0);
4789 if (bufPtr != buffer)
4792 NS_OBJC_END_TRY_ABORT_BLOCK;
4796 - (void)insertNewline:(id)sender
4798 [self insertText:@"\n"];
4802 - (void) doCommandBySelector:(SEL)aSelector
4804 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4807 NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mKeyPressHandled);
4810 if (!mKeyPressHandled)
4811 [super doCommandBySelector:aSelector];
4813 NS_OBJC_END_TRY_ABORT_BLOCK;
4817 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
4819 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4822 NSLog(@"****in setMarkedText location: %d, length: %d", selRange.location, selRange.length);
4823 NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4824 NSLog(@" aString = '%@'", aString);
4827 nsAutoRetainCocoaObject kungFuDeathGrip(self);
4829 if (![aString isKindOfClass:[NSAttributedString class]])
4830 aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
4832 NSMutableAttributedString *mutableAttribStr = aString;
4833 NSString *tmpStr = [mutableAttribStr string];
4834 unsigned int len = [tmpStr length];
4835 PRUnichar buffer[MAX_BUFFER_SIZE];
4836 PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
4837 [tmpStr getCharacters:bufPtr];
4838 bufPtr[len] = PRUnichar('\0');
4841 printf("****in setMarkedText, len = %d, text = ", len);
4843 PRUint32 maxlen = len > 12 ? 12 : len;
4844 for (PRUnichar *a = bufPtr; (*a != PRUnichar('\0')) && n<maxlen; a++, n++)
4845 printf((*a&0xff80) ? "\\u%4X" : "%c", *a);
4849 mMarkedRange.length = len;
4851 if (!nsTSMManager::IsComposing() && len > 0) {
4852 nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
4853 mGeckoChild->DispatchWindowEvent(selection);
4854 mMarkedRange.location = selection.mSucceeded ? selection.mReply.mOffset : 0;
4855 [self sendCompositionEvent:NS_COMPOSITION_START];
4856 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4857 nsTSMManager::StartComposing(self);
4858 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4861 if (nsTSMManager::IsComposing()) {
4862 nsTSMManager::UpdateComposing(tmpStr);
4864 BOOL commit = len == 0;
4865 [self sendTextEvent:bufPtr attributedString:aString
4866 selectedRange:selRange
4867 markedRange:mMarkedRange
4869 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4872 [self sendCompositionEvent:NS_COMPOSITION_END];
4873 nsTSMManager::EndComposing();
4877 if (bufPtr != buffer)
4880 NS_OBJC_END_TRY_ABORT_BLOCK;
4887 NSLog(@"****in unmarkText");
4888 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4890 nsTSMManager::CommitIME();
4894 - (BOOL) hasMarkedText
4897 NSLog(@"****in hasMarkText");
4898 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4900 return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0);
4904 - (long) conversationIdentifier
4907 NSLog(@"****in conversationIdentifier");
4911 nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
4912 textContent.InitForQueryTextContent(0, 0);
4913 mGeckoChild->DispatchWindowEvent(textContent);
4914 if (!textContent.mSucceeded)
4917 NSLog(@" the ID = %ld", (long)textContent.mReply.mContentsRoot);
4919 return (long)textContent.mReply.mContentsRoot;
4923 - (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
4925 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
4928 NSLog(@"****in attributedSubstringFromRange");
4929 NSLog(@" theRange = %d, %d", theRange.location, theRange.length);
4930 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4932 if (!mGeckoChild || theRange.length == 0)
4936 nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
4937 textContent.InitForQueryTextContent(theRange.location, theRange.length);
4938 mGeckoChild->DispatchWindowEvent(textContent);
4940 if (!textContent.mSucceeded || textContent.mReply.mString.IsEmpty())
4943 NSString* nsstr = ToNSString(textContent.mReply.mString);
4944 NSAttributedString* result =
4945 [[[NSAttributedString alloc] initWithString:nsstr
4946 attributes:nil] autorelease];
4949 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
4953 - (NSRange) markedRange
4955 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
4958 NSLog(@"****in markedRange");
4959 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4962 if (![self hasMarkedText]) {
4963 return NSMakeRange(NSNotFound, 0);
4966 return mMarkedRange;
4968 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
4972 - (NSRange) selectedRange
4974 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
4977 NSLog(@"****in selectedRange");
4978 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4981 return NSMakeRange(NSNotFound, 0);
4982 nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
4983 mGeckoChild->DispatchWindowEvent(selection);
4984 if (!selection.mSucceeded)
4985 return NSMakeRange(NSNotFound, 0);
4988 NSLog(@" result of selectedRange = %d, %d",
4989 selection.mReply.mOffset, selection.mReply.mString.Length());
4991 return NSMakeRange(selection.mReply.mOffset,
4992 selection.mReply.mString.Length());
4994 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
4998 - (NSRect) firstRectForCharacterRange:(NSRange)theRange
5000 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5003 NSLog(@"****in firstRectForCharacterRange");
5004 NSLog(@" theRange = %d, %d", theRange.location, theRange.length);
5005 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5007 // XXX this returns first character rect or caret rect, it is limitation of
5008 // now. We need more work for returns first line rect. But current
5009 // implementation is enough for IMEs.
5012 if (!mGeckoChild || theRange.location == NSNotFound)
5016 PRBool useCaretRect = theRange.length == 0;
5017 if (!useCaretRect) {
5018 nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, mGeckoChild);
5019 charRect.InitForQueryCharacterRect(theRange.location);
5020 mGeckoChild->DispatchWindowEvent(charRect);
5021 if (charRect.mSucceeded)
5022 r = charRect.mReply.mRect;
5024 useCaretRect = PR_TRUE;
5028 nsQueryContentEvent caretRect(PR_TRUE, NS_QUERY_CARET_RECT, mGeckoChild);
5029 caretRect.InitForQueryCaretRect(theRange.location);
5030 mGeckoChild->DispatchWindowEvent(caretRect);
5031 if (!caretRect.mSucceeded)
5033 r = caretRect.mReply.mRect;
5037 nsIWidget* rootWidget = mGeckoChild->GetTopLevelWidget();
5038 NSWindow* rootWindow =
5039 static_cast<NSWindow*>(rootWidget->GetNativeData(NS_NATIVE_WINDOW));
5041 static_cast<NSView*>(rootWidget->GetNativeData(NS_NATIVE_WIDGET));
5042 if (!rootWindow || !rootView)
5044 GeckoRectToNSRect(r, rect);
5045 rect = [rootView convertRect:rect toView:nil];
5046 rect.origin = [rootWindow convertBaseToScreen:rect.origin];
5048 NSLog(@" result rect (x,y,w,h) = %f, %f, %f, %f",
5049 rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
5053 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRect(0.0, 0.0, 0.0, 0.0));
5057 - (unsigned int)characterIndexForPoint:(NSPoint)thePoint
5060 NSLog(@"****in characterIndexForPoint");
5061 NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5064 // To implement this, we'd have to grovel in text frames looking at text offsets.
5069 - (NSArray*) validAttributesForMarkedText
5071 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5074 NSLog(@"****in validAttributesForMarkedText");
5075 NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5078 //return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
5079 return [NSArray array]; // empty array; we don't support any attributes right now
5081 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5088 + (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent
5090 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5092 NSEvent* newEvent = [NSEvent keyEventWithType:type
5093 location:[theEvent locationInWindow]
5094 modifierFlags:[theEvent modifierFlags]
5095 timestamp:[theEvent timestamp]
5096 windowNumber:[theEvent windowNumber]
5097 context:[theEvent context]
5098 characters:[theEvent characters]
5099 charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers]
5100 isARepeat:[theEvent isARepeat]
5101 keyCode:[theEvent keyCode]];
5104 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5108 static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
5110 for (PRUint32 i = 0; i < [aString length]; ++i) {
5111 unichar ch = [aString characterAtIndex:i];
5112 if (ch >= 32 && ch < 128) {
5113 aBuf.Append(char(ch));
5115 aBuf += nsPrintfCString("\\u%04x", ch);
5122 // Returns PR_TRUE if Gecko claims to have handled the event, PR_FALSE otherwise.
5123 - (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv
5125 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5134 PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
5135 ("ChildView processKeyDownEvent: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
5136 [theEvent keyCode], [theEvent modifierFlags],
5137 ToEscapedString([theEvent characters], str1),
5138 ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
5140 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5141 mCurKeyEvent = theEvent;
5143 BOOL nonDeadKeyPress = [[theEvent characters] length] > 0;
5144 if (nonDeadKeyPress) {
5145 if (![theEvent isARepeat]) {
5146 NSResponder* firstResponder = [[self window] firstResponder];
5148 nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_DOWN, nsnull);
5149 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5151 // create native EventRecord for use by plugins
5152 EventRecord macEvent;
5153 ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
5154 geckoEvent.nativeMsg = &macEvent;
5156 mKeyDownHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
5158 return mKeyDownHandled;
5160 // The key down event may have shifted the focus, in which
5161 // case we should not fire the key press.
5162 if (firstResponder != [[self window] firstResponder]) {
5163 PRBool handled = mKeyDownHandled;
5165 mKeyDownHandled = PR_FALSE;
5170 // If this is the context menu key command, send a context menu key event.
5171 unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
5172 if (modifierFlags == NSControlKeyMask && [[theEvent charactersIgnoringModifiers] isEqualToString:@" "]) {
5173 nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, [self widget], nsMouseEvent::eReal, nsMouseEvent::eContextMenuKey);
5174 contextMenuEvent.isShift = contextMenuEvent.isControl = contextMenuEvent.isAlt = contextMenuEvent.isMeta = PR_FALSE;
5175 PRBool cmEventHandled = mGeckoChild->DispatchWindowEvent(contextMenuEvent);
5176 [self maybeInitContextMenuTracking];
5177 // Bail, there is nothing else to do here.
5178 PRBool handled = (cmEventHandled || mKeyDownHandled);
5180 mKeyDownHandled = PR_FALSE;
5184 nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
5185 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5187 // if this is a non-letter keypress, or the control key is down,
5188 // dispatch the keydown to gecko, so that we trap delete,
5189 // control-letter combinations etc before Cocoa tries to use
5190 // them for keybindings.
5191 if ((!geckoEvent.isChar || geckoEvent.isControl) &&
5192 !nsTSMManager::IsComposing()) {
5193 if (mKeyDownHandled)
5194 geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
5196 // create native EventRecord for use by plugins
5197 EventRecord macEvent;
5198 ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
5199 geckoEvent.nativeMsg = &macEvent;
5201 mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
5202 mKeyPressSent = YES;
5204 return (mKeyDownHandled || mKeyPressHandled);
5208 // We need to initialize the TSMDocument *before* interpretKeyEvents when
5210 if (!isKeyEquiv && nsTSMManager::IsIMEEnabled()) {
5211 // We need to get actual focused view. E.g., the view is in bookmark dialog
5212 // that is <panel> element. Then, the key events are processed the parent
5213 // window's view that has native focus.
5214 nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT,
5216 textContent.InitForQueryTextContent(0, 0);
5217 mGeckoChild->DispatchWindowEvent(textContent);
5218 NSView<mozView>* focusedView = self;
5219 if (textContent.mSucceeded && textContent.mReply.mFocusedWidget) {
5220 NSView<mozView>* view =
5221 static_cast<NSView<mozView>*>(textContent.mReply.mFocusedWidget->
5222 GetNativeData(NS_NATIVE_WIDGET));
5226 nsTSMManager::InitTSMDocument(focusedView);
5229 // Let Cocoa interpret the key events, caching IsComposing first.
5230 // We don't do it if this came from performKeyEquivalent because
5231 // interpretKeyEvents isn't set up to handle those key combinations.
5232 PRBool wasComposing = nsTSMManager::IsComposing();
5233 PRBool interpretKeyEventsCalled = PR_FALSE;
5235 (nsTSMManager::IsIMEEnabled() || nsTSMManager::IsRomanKeyboardsOnly())) {
5236 [super interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
5237 interpretKeyEventsCalled = PR_TRUE;
5241 return (mKeyDownHandled || mKeyPressHandled);;
5243 if (!mKeyPressSent && nonDeadKeyPress && !wasComposing && !nsTSMManager::IsComposing()) {
5244 nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
5245 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5247 // If we called interpretKeyEvents and this isn't normal character input
5248 // then IME probably ate the event for some reason. We do not want to
5249 // send a key press event in that case.
5250 if (!(interpretKeyEventsCalled && IsNormalCharInputtingEvent(geckoEvent))) {
5251 if (mKeyDownHandled)
5252 geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
5254 // create native EventRecord for use by plugins
5255 EventRecord macEvent;
5256 ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
5257 geckoEvent.nativeMsg = &macEvent;
5259 mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
5263 // Note: mGeckoChild might have become null here. Don't count on it from here on.
5265 PRBool handled = (mKeyDownHandled || mKeyPressHandled);
5267 // See note about nested event loops where these variables are declared in header.
5268 mKeyPressHandled = NO;
5271 mKeyDownHandled = PR_FALSE;
5275 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5279 // Create a TSM document for use with plugins, so that we can support IME in
5280 // them. Once it's created, if need be (re)activate it. Some plugins (e.g.
5281 // the Flash plugin running in Camino) don't create their own TSM document --
5282 // without which IME can't work. Others (e.g. the Flash plugin running in
5283 // Firefox) create a TSM document that (somehow) makes the input window behave
5284 // badly when it contains more than one kind of input (say Hiragana and
5285 // Romaji). (We can't just use the per-NSView TSM documents that Cocoa
5286 // provices (those created and managed by the NSTSMInputContext class) -- for
5287 // some reason TSMProcessRawKeyEvent() doesn't work with them.)
5288 - (void)activatePluginTSMDoc
5290 if (!mPluginTSMDoc) {
5291 // Create a TSM document that supports both non-Unicode and Unicode input.
5292 // Though [ChildView processPluginKeyEvent:] only sends Mac char codes to
5293 // the plugin, this makes the input window behave better when it contains
5294 // more than one kind of input (say Hiragana and Romaji). This is what
5295 // the OS does when it creates a TSM document for use by an
5296 // NSTSMInputContext class.
5297 InterfaceTypeList supportedServices;
5298 supportedServices[0] = kTextServiceDocumentInterfaceType;
5299 supportedServices[1] = kUnicodeDocumentInterfaceType;
5300 ::NewTSMDocument(2, supportedServices, &mPluginTSMDoc, 0);
5301 // We'll need to use the "input window".
5302 ::UseInputWindow(mPluginTSMDoc, YES);
5303 ::ActivateTSMDocument(mPluginTSMDoc);
5304 } else if (::TSMGetActiveDocument() != mPluginTSMDoc) {
5305 ::ActivateTSMDocument(mPluginTSMDoc);
5310 - (void)keyDown:(NSEvent*)theEvent
5312 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5314 // If a plugin has the focus, we need to use an alternate method for
5315 // handling NSKeyDown and NSKeyUp events (otherwise Carbon-based IME won't
5316 // work in plugins like the Flash plugin). The same strategy is used by the
5317 // WebKit. See PluginKeyEventsHandler() and [ChildView processPluginKeyEvent:]
5319 if (mGeckoChild && mIsPluginView) {
5320 [self activatePluginTSMDoc];
5321 // We use the active TSM document to pass a pointer to ourselves (the
5322 // currently focused ChildView) to PluginKeyEventsHandler(). Because this
5323 // pointer is weak, we should retain and release ourselves around the call
5324 // to TSMProcessRawKeyEvent().
5325 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5326 ::TSMSetDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag,
5327 sizeof(ChildView *), &self);
5328 ::TSMProcessRawKeyEvent([theEvent _eventRef]);
5329 ::TSMRemoveDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag);
5333 [self processKeyDownEvent:theEvent keyEquiv:NO];
5335 NS_OBJC_END_TRY_ABORT_BLOCK;
5339 static BOOL keyUpAlreadySentKeyDown = NO;
5341 - (void)keyUp:(NSEvent*)theEvent
5343 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5349 PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
5350 ("ChildView keyUp: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
5351 [theEvent keyCode], [theEvent modifierFlags],
5352 ToEscapedString([theEvent characters], str1),
5353 ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
5358 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5360 if (mIsPluginView) {
5361 // I'm not sure the call to TSMProcessRawKeyEvent() is needed here (though
5362 // WebKit makes one).
5363 ::TSMProcessRawKeyEvent([theEvent _eventRef]);
5365 // Don't send a keyUp event if the corresponding keyDown event(s) is/are
5366 // still being processed (idea borrowed from WebKit).
5367 ChildView *keyDownTarget = nil;
5368 OSStatus status = ::TSMGetDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag,
5369 sizeof(ChildView *), nil, &keyDownTarget);
5370 if (status != noErr)
5371 keyDownTarget = nil;
5372 if (keyDownTarget == self)
5375 // PluginKeyEventsHandler() never sends keyUp events to [ChildView
5376 // processPluginKeyEvent:], so we need to send them to Gecko here. (This
5377 // means that when commiting text from IME, several keyDown events may be
5378 // sent to Gecko (in processPluginKeyEvent) for one keyUp event here.
5379 // But this is how the WebKit does it, and games expect a keyUp event to
5380 // be sent when it actually happens (they need to be able to detect how
5381 // long a key has been held down) -- which wouldn't be possible if we sent
5382 // them from processPluginKeyEvent.)
5383 nsKeyEvent keyUpEvent(PR_TRUE, NS_KEY_UP, nsnull);
5384 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&keyUpEvent];
5385 EventRecord macKeyUpEvent;
5386 ConvertCocoaKeyEventToMacEvent(theEvent, macKeyUpEvent);
5387 keyUpEvent.nativeMsg = &macKeyUpEvent;
5388 mGeckoChild->DispatchWindowEvent(keyUpEvent);
5392 // if we don't have any characters we can't generate a keyUp event
5393 if ([[theEvent characters] length] == 0)
5396 // Cocoa doesn't send an NSKeyDown event for control-tab on 10.4, so if this
5397 // is an NSKeyUp event for control-tab, send a down event to gecko first.
5398 if (!nsToolkit::OnLeopardOrLater() && !keyUpAlreadySentKeyDown &&
5399 [theEvent modifierFlags] & NSControlKeyMask && [theEvent keyCode] == kTabKeyCode) {
5400 // We'll need an NSKeyDown copy of our native event so we convert to a gecko event correctly.
5401 NSEvent* nativeKeyDownEvent = [ChildView makeNewCocoaEventWithType:NSKeyDown fromEvent:theEvent];
5403 // send a key down event if we should
5404 PRBool keyDownHandled = PR_FALSE;
5405 if (![nativeKeyDownEvent isARepeat]) {
5406 nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_DOWN, nsnull);
5407 [self convertCocoaKeyEvent:nativeKeyDownEvent toGeckoEvent:&geckoEvent];
5409 // create native EventRecord for use by plugins
5410 EventRecord macEvent;
5411 ConvertCocoaKeyEventToMacEvent(nativeKeyDownEvent, macEvent);
5412 geckoEvent.nativeMsg = &macEvent;
5414 keyDownHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
5419 // Check to see if we are still the first responder.
5420 // The key down event may have shifted the focus, in which
5421 // case we should not fire the key press.
5422 NSResponder* resp = [[self window] firstResponder];
5423 if (resp != (NSResponder*)self) {
5424 keyUpAlreadySentKeyDown = YES;
5425 [resp keyUp:theEvent];
5426 keyUpAlreadySentKeyDown = NO;
5430 // now send a key press event if we should
5431 if (!nsTSMManager::IsComposing()) {
5432 nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
5433 [self convertCocoaKeyEvent:nativeKeyDownEvent toGeckoEvent:&geckoEvent];
5436 geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
5438 // create native EventRecord for use by plugins
5439 EventRecord macEvent;
5440 ConvertCocoaKeyEventToMacEvent(nativeKeyDownEvent, macEvent);
5441 geckoEvent.nativeMsg = &macEvent;
5443 mGeckoChild->DispatchWindowEvent(geckoEvent);
5449 nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_UP, nsnull);
5450 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5452 // create native EventRecord for use by plugins
5453 EventRecord macEvent;
5454 ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
5455 geckoEvent.nativeMsg = &macEvent;
5457 mGeckoChild->DispatchWindowEvent(geckoEvent);
5459 NS_OBJC_END_TRY_ABORT_BLOCK;
5463 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
5465 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5467 // don't do anything if we don't have a gecko widget
5471 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5473 // If we're not the first responder and the first responder is an NSView
5474 // object, pass the event on. Otherwise (if, for example, the first
5475 // responder is an NSWindow object) we should trust the OS to have called
5477 id firstResponder = [[self window] firstResponder];
5478 if (firstResponder != self) {
5479 // Special handling if the other first responder is a ChildView.
5480 if ([firstResponder isKindOfClass:[ChildView class]])
5481 return [(ChildView *)firstResponder performKeyEquivalent:theEvent];
5482 if ([firstResponder isKindOfClass:[NSView class]])
5483 return [super performKeyEquivalent:theEvent];
5486 // don't process if we're composing, but don't consume the event
5487 if (nsTSMManager::IsComposing())
5490 // Set to true if embedding menus handled the event when a plugin has focus.
5491 // We give menus a crack at handling commands before Gecko in the plugin case.
5492 BOOL handledByEmbedding = NO;
5494 // Perform native menu UI feedback even if we stop the event from propagating to it normally.
5495 // Recall that the menu system won't actually execute any commands for keyboard command invocations.
5497 // If this is a plugin, we do actually perform the action on keyboard commands. See bug 428047.
5498 // If the action on plugins here changes the first responder, don't continue.
5499 NSMenu* mainMenu = [NSApp mainMenu];
5500 if (mIsPluginView) {
5501 if ([mainMenu isKindOfClass:[GeckoNSMenu class]]) {
5502 [(GeckoNSMenu*)mainMenu actOnKeyEquivalent:theEvent];
5505 // This is probably an embedding situation. If the native menu handle the event
5506 // then return YES from pKE no matter what Gecko or the plugin does.
5507 handledByEmbedding = [mainMenu performKeyEquivalent:theEvent];
5509 if ([[self window] firstResponder] != self)
5513 if ([mainMenu isKindOfClass:[GeckoNSMenu class]])
5514 [(GeckoNSMenu*)mainMenu performMenuUserInterfaceEffectsForEvent:theEvent];
5517 // With Cmd key or Ctrl+Tab or Ctrl+Esc, keyDown will be never called.
5518 // Therefore, we need to call processKeyDownEvent from performKeyEquivalent.
5519 UInt32 modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
5520 UInt32 keyCode = [theEvent keyCode];
5521 PRBool keyDownNeverFiredEvent = (modifierFlags & NSCommandKeyMask) ||
5522 ((modifierFlags & NSControlKeyMask) &&
5523 (keyCode == kEscapeKeyCode || keyCode == kTabKeyCode));
5525 // don't handle this if certain modifiers are down - those should
5526 // be sent as normal key up/down events and cocoa will do so automatically
5527 // if we reject here
5528 if (!keyDownNeverFiredEvent &&
5529 (modifierFlags & (NSFunctionKeyMask| NSNumericPadKeyMask)))
5530 return handledByEmbedding;
5532 // Control and option modifiers are used when changing input sources in the
5533 // input menu. We need to send such key events via "keyDown:", which will
5534 // happen if we return NO here. This only applies to Mac OS X 10.5 and higher,
5535 // previous OS versions just call "keyDown:" and not "performKeyEquivalent:"
5537 if (!keyDownNeverFiredEvent &&
5538 (modifierFlags & (NSControlKeyMask | NSAlternateKeyMask)))
5539 return handledByEmbedding;
5541 if ([theEvent type] == NSKeyDown) {
5542 // We trust the Gecko handled status for cmd key events. See bug 417466 for more info.
5543 if (modifierFlags & NSCommandKeyMask)
5544 return ([self processKeyDownEvent:theEvent keyEquiv:YES] || handledByEmbedding);
5546 [self processKeyDownEvent:theEvent keyEquiv:YES];
5551 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5555 - (void)flagsChanged:(NSEvent*)theEvent
5557 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5562 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5564 // Fire key up/down events for the modifier keys (shift, alt, ctrl, command).
5565 if ([theEvent type] == NSFlagsChanged) {
5566 unsigned int modifiers =
5567 [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
5568 const PRUint32 kModifierMaskTable[] =
5569 { NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask, NSCommandKeyMask };
5570 const PRUint32 kModifierCount = sizeof(kModifierMaskTable) /
5571 sizeof(kModifierMaskTable[0]);
5573 for (PRUint32 i = 0; i < kModifierCount; i++) {
5574 PRUint32 modifierBit = kModifierMaskTable[i];
5575 if ((modifiers & modifierBit) != (mLastModifierState & modifierBit)) {
5576 PRUint32 message = ((modifiers & modifierBit) != 0 ? NS_KEY_DOWN :
5579 // Fire a key event.
5580 nsKeyEvent geckoEvent(PR_TRUE, message, nsnull);
5581 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5583 // create native EventRecord for use by plugins
5584 EventRecord macEvent;
5585 ConvertCocoaKeyEventToMacEvent(theEvent, macEvent, message);
5586 geckoEvent.nativeMsg = &macEvent;
5588 mGeckoChild->DispatchWindowEvent(geckoEvent);
5592 // Stop if focus has changed.
5593 // Check to see if we are still the first responder.
5594 NSResponder* resp = [[self window] firstResponder];
5595 if (resp != (NSResponder*)self)
5600 mLastModifierState = modifiers;
5603 // check if the hand scroll cursor needs to be set/unset
5604 [self setHandScrollCursor:theEvent];
5606 NS_OBJC_END_TRY_ABORT_BLOCK;
5610 // This method is called when are are about to lose focus.
5611 // We must always call through to our superclass, even when mGeckoChild is
5612 // nil -- otherwise the keyboard focus can end up in the wrong NSView.
5613 - (BOOL)resignFirstResponder
5615 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5617 return [super resignFirstResponder];
5619 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5623 - (void)viewsWindowDidBecomeKey
5625 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5630 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5632 // check to see if the window implements the mozWindow protocol. This
5633 // allows embedders to avoid re-entrant calls to -makeKeyAndOrderFront,
5634 // which can happen because these activate/focus calls propagate out
5635 // to the embedder via nsIEmbeddingSiteWindow::SetFocus().
5636 BOOL isMozWindow = [[self window] respondsToSelector:@selector(setSuppressMakeKeyFront:)];
5638 [[self window] setSuppressMakeKeyFront:YES];
5640 [self sendFocusEvent:NS_GOTFOCUS];
5641 [self sendFocusEvent:NS_ACTIVATE];
5644 [[self window] setSuppressMakeKeyFront:NO];
5646 NS_OBJC_END_TRY_ABORT_BLOCK;
5650 - (void)viewsWindowDidResignKey
5655 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5657 [self sendFocusEvent:NS_DEACTIVATE];
5658 [self sendFocusEvent:NS_LOSTFOCUS];
5662 // If the call to removeFromSuperview isn't delayed from nsChildView::
5663 // TearDownView(), the NSView hierarchy might get changed during calls to
5664 // [ChildView drawRect:], which leads to "beyond bounds" exceptions in
5665 // NSCFArray. For more info see bmo bug 373122. Apple's docs claim that
5666 // removeFromSuperviewWithoutNeedingDisplay "can be safely invoked during
5667 // display" (whatever "display" means). But it's _not_ true that it can be
5668 // safely invoked during calls to [NSView drawRect:]. We use
5669 // removeFromSuperview here because there's no longer any danger of being
5670 // "invoked during display", and because doing do clears up bmo bug 384343.
5671 - (void)delayedTearDown
5673 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5675 [self removeFromSuperview];
5678 NS_OBJC_END_TRY_ABORT_BLOCK;
5685 // drag'n'drop stuff
5686 #define kDragServiceContractID "@mozilla.org/widget/dragservice;1"
5689 // This is a utility function used by NSView drag event methods
5690 // to send events. It contains all of the logic needed for Gecko
5691 // dragging to work. Returns YES if the event was handled, NO
5693 - (BOOL)doDragAction:(PRUint32)aMessage sender:(id)aSender
5695 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5700 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView doDragAction: entered\n"));
5702 if (!mDragService) {
5703 CallGetService(kDragServiceContractID, &mDragService);
5704 NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
5709 if (aMessage == NS_DRAGDROP_ENTER)
5710 mDragService->StartDragSession();
5712 nsCOMPtr<nsIDragSession> dragSession;
5713 mDragService->GetCurrentSession(getter_AddRefs(dragSession));
5715 if (aMessage == NS_DRAGDROP_OVER) {
5716 // fire the drag event at the source. Just ignore whether it was
5717 // cancelled or not as there isn't actually a means to stop the drag
5718 mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
5719 dragSession->SetCanDrop(PR_FALSE);
5721 else if (aMessage == NS_DRAGDROP_DROP) {
5722 // We make the assumption that the dragOver handlers have correctly set
5723 // the |canDrop| property of the Drag Session.
5724 PRBool canDrop = PR_FALSE;
5725 if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop) {
5726 nsCOMPtr<nsIDOMNode> sourceNode;
5727 dragSession->GetSourceNode(getter_AddRefs(sourceNode));
5729 mDragService->EndDragSession(PR_FALSE);
5735 unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags];
5736 PRUint32 action = nsIDragService::DRAGDROP_ACTION_MOVE;
5737 // force copy = option, alias = cmd-option, default is move
5738 if (modifierFlags & NSAlternateKeyMask) {
5739 if (modifierFlags & NSCommandKeyMask)
5740 action = nsIDragService::DRAGDROP_ACTION_LINK;
5742 action = nsIDragService::DRAGDROP_ACTION_COPY;
5744 dragSession->SetDragAction(action);
5747 // set up gecko event
5748 nsDragEvent geckoEvent(PR_TRUE, aMessage, nsnull);
5749 [self convertGenericCocoaEvent:nil toGeckoEvent:&geckoEvent];
5751 // Use our own coordinates in the gecko event.
5752 // Convert event from gecko global coords to gecko view coords.
5753 NSPoint localPoint = [self convertPoint:[aSender draggingLocation] fromView:nil];
5754 geckoEvent.refPoint.x = static_cast<nscoord>(localPoint.x);
5755 geckoEvent.refPoint.y = static_cast<nscoord>(localPoint.y);
5757 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5758 mGeckoChild->DispatchWindowEvent(geckoEvent);
5762 if ((aMessage == NS_DRAGDROP_EXIT || aMessage == NS_DRAGDROP_DROP) &&
5764 nsCOMPtr<nsIDOMNode> sourceNode;
5765 dragSession->GetSourceNode(getter_AddRefs(sourceNode));
5767 // We're leaving a window while doing a drag that was
5768 // initiated in a different app. End the drag session,
5769 // since we're done with it for now (until the user
5770 // drags back into mozilla).
5771 mDragService->EndDragSession(PR_FALSE);
5777 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5781 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
5783 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5785 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingEntered: entered\n"));
5787 // there should never be a globalDragPboard when "draggingEntered:" is
5788 // called, but just in case we'll take care of it here.
5789 [globalDragPboard release];
5791 // Set the global drag pasteboard that will be used for this drag session.
5792 // This will be set back to nil when the drag session ends (mouse exits
5793 // the view or a drop happens within the view).
5794 globalDragPboard = [[sender draggingPasteboard] retain];
5796 BOOL handled = [self doDragAction:NS_DRAGDROP_ENTER sender:sender];
5798 return handled ? NSDragOperationGeneric : NSDragOperationNone;
5800 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
5804 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
5806 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingUpdated: entered\n"));
5808 BOOL handled = [self doDragAction:NS_DRAGDROP_OVER sender:sender];
5809 return handled ? NSDragOperationGeneric : NSDragOperationNone;
5813 - (void)draggingExited:(id <NSDraggingInfo>)sender
5815 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingExited: entered\n"));
5817 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5818 [self doDragAction:NS_DRAGDROP_EXIT sender:sender];
5819 NS_IF_RELEASE(mDragService);
5823 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
5825 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5826 BOOL handled = [self doDragAction:NS_DRAGDROP_DROP sender:sender];
5827 NS_IF_RELEASE(mDragService);
5833 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
5835 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5837 gDraggedTransferables = nsnull;
5839 if (!mDragService) {
5840 CallGetService(kDragServiceContractID, &mDragService);
5841 NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
5845 mDragService->EndDragSession(PR_TRUE);
5846 NS_RELEASE(mDragService);
5849 [globalDragPboard release];
5850 globalDragPboard = nil;
5852 NS_OBJC_END_TRY_ABORT_BLOCK;
5857 // this is just implemented so we comply with the NSDraggingSource informal protocol
5858 - (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal
5863 // This method is a callback typically invoked in response to a drag ending on the desktop
5864 // or a Findow folder window; the argument passed is a path to the drop location, to be used
5865 // in constructing a complete pathname for the file(s) we want to create as a result of
5867 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(id <NSDraggingInfo>)dropDestination
5869 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5873 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView namesOfPromisedFilesDroppedAtDestination: entering callback for promised files\n"));
5875 nsCOMPtr<nsILocalFile> targFile;
5876 NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(targFile));
5877 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(targFile);
5878 if (!macLocalFile) {
5879 NS_ERROR("No Mac local file");
5883 if (!NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)dropDestination))) {
5884 NS_ERROR("failed InitWithCFURL");
5888 if (!gDraggedTransferables)
5891 PRUint32 transferableCount;
5892 rv = gDraggedTransferables->Count(&transferableCount);
5896 for (PRUint32 i = 0; i < transferableCount; i++) {
5897 nsCOMPtr<nsISupports> genericItem;
5898 gDraggedTransferables->GetElementAt(i, getter_AddRefs(genericItem));
5899 nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
5901 NS_ERROR("no transferable");
5905 item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile, sizeof(nsILocalFile*));
5907 // now request the kFilePromiseMime data, which will invoke the data provider
5908 // If successful, the returned data is a reference to the resulting file.
5909 nsCOMPtr<nsISupports> fileDataPrimitive;
5910 PRUint32 dataSize = 0;
5911 item->GetTransferData(kFilePromiseMime, getter_AddRefs(fileDataPrimitive), &dataSize);
5914 NSPasteboard* generalPboard = [NSPasteboard pasteboardWithName:NSDragPboard];
5915 NSData* data = [generalPboard dataForType:@"application/x-moz-file-promise-dest-filename"];
5916 NSString* name = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
5917 NSArray* rslt = [NSArray arrayWithObject:name];
5923 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5929 #ifdef ACCESSIBILITY
5931 /* Every ChildView has a corresponding mozDocAccessible object that is doing all
5932 the heavy lifting. The topmost ChildView corresponds to a mozRootAccessible
5935 All ChildView needs to do is to route all accessibility calls (from the NSAccessibility APIs)
5936 down to its object, pretending that they are the same.
5938 - (id<mozAccessible>)accessible
5943 id<mozAccessible> nativeAccessible = nil;
5945 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5946 nsCOMPtr<nsIWidget> kungFuDeathGrip2(mGeckoChild);
5947 nsCOMPtr<nsIAccessible> accessible;
5948 mGeckoChild->GetDocumentAccessible(getter_AddRefs(accessible));
5953 accessible->GetNativeInterface((void**)&nativeAccessible);
5956 NSAssert(![nativeAccessible isExpired], @"native acc is expired!!!");
5959 return nativeAccessible;
5962 /* Implementation of formal mozAccessible formal protocol (enabling mozViews
5963 to talk to mozAccessible objects in the accessibility module). */
5965 - (BOOL)hasRepresentedView
5970 - (id)representedView
5977 return [[self accessible] isRoot];
5981 - (void)printHierarchy
5983 [[self accessible] printHierarchy];
5991 - (BOOL)accessibilityIsIgnored
5993 return [[self accessible] accessibilityIsIgnored];
5996 - (id)accessibilityHitTest:(NSPoint)point
5998 return [[self accessible] accessibilityHitTest:point];
6001 - (id)accessibilityFocusedUIElement
6003 return [[self accessible] accessibilityFocusedUIElement];
6008 - (NSArray*)accessibilityActionNames
6010 return [[self accessible] accessibilityActionNames];
6013 - (NSString*)accessibilityActionDescription:(NSString*)action
6015 return [[self accessible] accessibilityActionDescription:action];
6018 - (void)accessibilityPerformAction:(NSString*)action
6020 return [[self accessible] accessibilityPerformAction:action];
6025 - (NSArray*)accessibilityAttributeNames
6027 return [[self accessible] accessibilityAttributeNames];
6030 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
6032 return [[self accessible] accessibilityIsAttributeSettable:attribute];
6035 - (id)accessibilityAttributeValue:(NSString*)attribute
6037 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
6039 id<mozAccessible> accessible = [self accessible];
6041 // if we're the root (topmost) accessible, we need to return our native AXParent as we
6042 // traverse outside to the hierarchy of whoever embeds us. thus, fall back on NSView's
6043 // default implementation for this attribute.
6044 if ([attribute isEqualToString:NSAccessibilityParentAttribute] && [accessible isRoot]) {
6045 id parentAccessible = [super accessibilityAttributeValue:attribute];
6046 return parentAccessible;
6049 return [accessible accessibilityAttributeValue:attribute];
6051 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
6054 #endif /* ACCESSIBILITY */
6063 nsTSMManager::OnDestroyView(NSView<mozView>* aDestroyingView)
6065 if (aDestroyingView != sComposingView)
6067 if (IsComposing()) {
6068 CancelIME(); // XXX Might CancelIME() fail because sComposingView is being destroyed?
6075 nsTSMManager::GetIMEOpenState()
6077 return GetScriptManagerVariable(smKeyScript) != smRoman ? PR_TRUE : PR_FALSE;
6082 nsTSMManager::InitTSMDocument(NSView<mozView>* aViewForCaret)
6084 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6086 sDocumentID = ::TSMGetActiveDocument();
6090 // We need to set the focused window level to TSMDocument. Then, the popup
6091 // windows of IME (E.g., a candidate list window) will be over the focused
6092 // view. See http://developer.apple.com/technotes/tn2005/tn2128.html#TNTAG1
6093 NSInteger TSMLevel, windowLevel;
6094 UInt32 size = sizeof(TSMLevel);
6097 ::TSMGetDocumentProperty(sDocumentID, kTSMDocumentWindowLevelPropertyTag,
6098 size, &size, &TSMLevel);
6099 windowLevel = [[aViewForCaret window] level];
6101 // Chinese IMEs on 10.5 don't work fine if the level is NSNormalWindowLevel,
6102 // then, we need to increment the value.
6103 if (windowLevel == NSNormalWindowLevel)
6106 if (err == noErr && TSMLevel >= windowLevel)
6108 ::TSMSetDocumentProperty(sDocumentID, kTSMDocumentWindowLevelPropertyTag,
6109 sizeof(windowLevel), &windowLevel);
6111 // ATOK (Japanese IME) updates the window level at activating,
6112 // we need to notify the change with this hack.
6113 ::DeactivateTSMDocument(sDocumentID);
6114 ::ActivateTSMDocument(sDocumentID);
6116 NS_OBJC_END_TRY_ABORT_BLOCK;
6121 nsTSMManager::StartComposing(NSView<mozView>* aComposingView)
6123 if (sComposingView && sComposingView != sComposingView)
6125 sComposingView = aComposingView;
6126 NS_ASSERTION(::TSMGetActiveDocument() == sDocumentID,
6127 "We didn't initialize the TSMDocument");
6132 nsTSMManager::UpdateComposing(NSString* aComposingString)
6134 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6136 if (sComposingString)
6137 [sComposingString release];
6138 sComposingString = [aComposingString retain];
6140 NS_OBJC_END_TRY_ABORT_BLOCK;
6145 nsTSMManager::EndComposing()
6147 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6149 sComposingView = nsnull;
6150 if (sComposingString) {
6151 [sComposingString release];
6152 sComposingString = nsnull;
6155 NS_OBJC_END_TRY_ABORT_BLOCK;
6160 nsTSMManager::EnableIME(PRBool aEnable)
6162 if (aEnable == sIsIMEEnabled)
6165 sIsIMEEnabled = aEnable;
6170 nsTSMManager::SetIMEOpenState(PRBool aOpen)
6172 if (aOpen == GetIMEOpenState())
6175 KeyScript(aOpen ? smKeySwapScript : smKeyRoman);
6179 #define ENABLE_ROMAN_KYBDS_ONLY -23
6181 nsTSMManager::SetRomanKeyboardsOnly(PRBool aRomanOnly)
6183 if (aRomanOnly == sIsRomanKeyboardsOnly)
6186 KeyScript(aRomanOnly ? ENABLE_ROMAN_KYBDS_ONLY : smKeyEnableKybds);
6187 sIsRomanKeyboardsOnly = aRomanOnly;
6192 nsTSMManager::KillComposing()
6194 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6196 // Force commit the current composition
6197 // XXX Don't use NSInputManager. Because it cannot control the non-forcused
6198 // input manager, therefore, on deactivating a window, it does not work fine.
6199 NS_ASSERTION(sDocumentID, "The TSMDocumentID is null");
6200 ::FixTSMDocument(sDocumentID);
6202 NS_OBJC_END_TRY_ABORT_BLOCK;
6207 nsTSMManager::CommitIME()
6209 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6216 // If the composing transaction is still there, KillComposing only kills the
6217 // composing in TSM. We also need to kill the our composing transaction too.
6218 NSAttributedString* str =
6219 [[NSAttributedString alloc] initWithString:sComposingString];
6220 [sComposingView insertText:str];
6223 NS_OBJC_END_TRY_ABORT_BLOCK;
6228 nsTSMManager::CancelIME()
6230 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6234 // For canceling the current composing, we need to ignore the param of
6235 // insertText. But this code is ugly...
6236 sIgnoreCommit = PR_TRUE;
6238 sIgnoreCommit = PR_FALSE;
6241 // If the composing transaction is still there, KillComposing only kills the
6242 // composing in TSM. We also need to kill the our composing transaction too.
6243 NSAttributedString* str = [[NSAttributedString alloc] initWithString:@""];
6244 [sComposingView insertText:str];
6247 NS_OBJC_END_TRY_ABORT_BLOCK;
6251 // Target for text services events sent as the result of calls made to
6252 // TSMProcessRawKeyEvent() in [ChildView keyDown:] (above) when a plugin has
6253 // the focus. The calls to TSMProcessRawKeyEvent() short-circuit Cocoa-based
6254 // IME (which would otherwise interfere with our efforts) and allow Carbon-
6255 // based IME to work in plugins (via the NPAPI). This strategy doesn't cause
6256 // trouble for plugins that (like the Java Embedding Plugin) bypass the NPAPI
6257 // to get their keyboard events and do their own Cocoa-based IME.
6258 OSStatus PluginKeyEventsHandler(EventHandlerCallRef inHandlerRef,
6259 EventRef inEvent, void *userData)
6261 id arp = [[NSAutoreleasePool alloc] init];
6263 TSMDocumentID activeDoc = ::TSMGetActiveDocument();
6266 return eventNotHandledErr;
6269 ChildView *target = nil;
6270 OSStatus status = ::TSMGetDocumentProperty(activeDoc, kFocusedChildViewTSMDocPropertyTag,
6271 sizeof(ChildView *), nil, &target);
6272 if (status != noErr)
6276 return eventNotHandledErr;
6279 EventRef keyEvent = NULL;
6280 status = ::GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent,
6281 typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
6282 if ((status != noErr) || !keyEvent) {
6284 return eventNotHandledErr;
6287 [target processPluginKeyEvent:keyEvent];
6293 static EventHandlerRef gPluginKeyEventsHandler = NULL;
6295 // Called from nsAppShell::Init()
6296 void NS_InstallPluginKeyEventsHandler()
6298 if (gPluginKeyEventsHandler)
6300 static const EventTypeSpec sTSMEvents[] =
6301 { { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } };
6302 ::InstallEventHandler(::GetEventDispatcherTarget(),
6303 ::NewEventHandlerUPP(PluginKeyEventsHandler),
6304 GetEventTypeCount(sTSMEvents),
6307 &gPluginKeyEventsHandler);
6310 // Called from nsAppShell::Exit()
6311 void NS_RemovePluginKeyEventsHandler()
6313 if (!gPluginKeyEventsHandler)
6315 ::RemoveEventHandler(gPluginKeyEventsHandler);
6316 gPluginKeyEventsHandler = NULL;