2007-03-22 10:30:00 -07:00
|
|
|
/* -*- Mode: objc; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Josh Aas <josh@mozilla.com>
|
|
|
|
* Mark Mentovai <mark@moxienet.com>
|
|
|
|
* HÃ¥kan Waara <hwaara@gmail.com>
|
|
|
|
* Stuart Morgan <stuart.morgan@alumni.case.edu>
|
2008-01-15 15:11:55 -08:00
|
|
|
* Mats Palmgren <mats.palmgren@bredband.net>
|
2007-03-22 10:30:00 -07:00
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "nsChildView.h"
|
2007-07-17 13:29:39 -07:00
|
|
|
#include "nsCocoaWindow.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-02-20 15:47:05 -08:00
|
|
|
#include "nsObjCExceptions.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "nsToolkit.h"
|
|
|
|
#include "nsCRT.h"
|
|
|
|
#include "nsplugindefs.h"
|
2008-02-28 07:58:33 -08:00
|
|
|
#include "nsIPrefService.h"
|
|
|
|
#include "nsIPrefBranch.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "nsIFontMetrics.h"
|
|
|
|
#include "nsIDeviceContext.h"
|
|
|
|
#include "nsIRegion.h"
|
|
|
|
#include "nsIRollupListener.h"
|
|
|
|
#include "nsIScrollableView.h"
|
|
|
|
#include "nsIViewManager.h"
|
|
|
|
#include "nsIInterfaceRequestor.h"
|
|
|
|
#include "nsIServiceManager.h"
|
2007-07-16 19:24:05 -07:00
|
|
|
#include "nsILocalFile.h"
|
|
|
|
#include "nsILocalFileMac.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsGfxCIID.h"
|
2007-11-26 15:19:04 -08:00
|
|
|
#include "nsIMenuRollup.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "nsDragService.h"
|
|
|
|
#include "nsCursorManager.h"
|
|
|
|
#include "nsWindowMap.h"
|
|
|
|
#include "nsCocoaUtils.h"
|
2008-01-28 22:11:06 -08:00
|
|
|
#include "nsMenuBarX.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "gfxContext.h"
|
|
|
|
#include "gfxQuartzSurface.h"
|
|
|
|
|
2008-05-08 15:03:46 -07:00
|
|
|
#include <dlfcn.h>
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#undef DEBUG_IME
|
|
|
|
#undef DEBUG_UPDATE
|
|
|
|
#undef INVALIDATE_DEBUGGING // flash areas as they are invalidated
|
|
|
|
|
2007-09-04 23:58:16 -07:00
|
|
|
#ifdef MOZ_LOGGING
|
|
|
|
#define FORCE_PR_LOG
|
|
|
|
#endif
|
|
|
|
#include "prlog.h"
|
|
|
|
|
2007-07-16 19:24:05 -07:00
|
|
|
#ifdef PR_LOGGING
|
|
|
|
PRLogModuleInfo* sCocoaLog = nsnull;
|
|
|
|
#endif
|
|
|
|
|
2007-09-05 22:11:35 -07:00
|
|
|
// npapi.h defines NPEventType_AdjustCursorEvent but we don't want to include npapi.h here.
|
|
|
|
// We need to send this in the "what" field for certain native plugin events. WebKit does
|
|
|
|
// this as well.
|
|
|
|
#define adjustCursorEvent 33
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
extern "C" {
|
|
|
|
CG_EXTERN void CGContextResetCTM(CGContextRef);
|
|
|
|
CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
|
|
|
|
CG_EXTERN void CGContextResetClip(CGContextRef);
|
|
|
|
}
|
|
|
|
|
2008-05-08 15:03:46 -07:00
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
|
|
|
|
struct __TISInputSource;
|
|
|
|
typedef __TISInputSource* TISInputSourceRef;
|
|
|
|
#endif
|
|
|
|
TISInputSourceRef (*Leopard_TISCopyCurrentKeyboardLayoutInputSource)() = NULL;
|
|
|
|
void* (*Leopard_TISGetInputSourceProperty)(TISInputSourceRef inputSource, CFStringRef propertyKey) = NULL;
|
2008-05-09 16:14:57 -07:00
|
|
|
CFArrayRef (*Leopard_TISCreateInputSourceList)(CFDictionaryRef properties, Boolean includeAllInstalled) = NULL;
|
2008-05-08 15:03:46 -07:00
|
|
|
CFStringRef kOurTISPropertyUnicodeKeyLayoutData = NULL;
|
2008-05-09 16:14:57 -07:00
|
|
|
CFStringRef kOurTISPropertyInputSourceID = NULL;
|
2008-05-08 15:03:46 -07:00
|
|
|
|
2008-02-13 07:57:12 -08:00
|
|
|
extern PRBool gCocoaWindowMethodsSwizzled; // Defined in nsCocoaWindow.mm
|
|
|
|
|
2007-07-17 16:02:40 -07:00
|
|
|
extern nsISupportsArray *gDraggedTransferables;
|
|
|
|
|
2007-04-15 06:43:55 -07:00
|
|
|
PRBool nsTSMManager::sIsIMEEnabled = PR_TRUE;
|
|
|
|
PRBool nsTSMManager::sIsRomanKeyboardsOnly = PR_FALSE;
|
2007-08-16 13:30:50 -07:00
|
|
|
PRBool nsTSMManager::sIgnoreCommit = PR_FALSE;
|
2007-04-15 06:43:55 -07:00
|
|
|
NSView<mozView>* nsTSMManager::sComposingView = nsnull;
|
2007-08-16 13:30:50 -07:00
|
|
|
TSMDocumentID nsTSMManager::sDocumentID = nsnull;
|
|
|
|
NSString* nsTSMManager::sComposingString = nsnull;
|
2007-04-15 06:43:55 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
|
|
|
|
static NSView* sLastViewEntered = nil;
|
|
|
|
#ifdef INVALIDATE_DEBUGGING
|
|
|
|
static void blinkRect(Rect* r);
|
|
|
|
static void blinkRgn(RgnHandle rgn);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsIRollupListener * gRollupListener = nsnull;
|
|
|
|
nsIWidget * gRollupWidget = nsnull;
|
|
|
|
|
|
|
|
|
|
|
|
@interface ChildView(Private)
|
|
|
|
|
|
|
|
// sets up our view, attaching it to its owning gecko view
|
2007-10-09 11:46:30 -07:00
|
|
|
- (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// sends gecko an ime composition event
|
|
|
|
- (nsRect) sendCompositionEvent:(PRInt32)aEventType;
|
|
|
|
|
|
|
|
// sends gecko an ime text event
|
|
|
|
- (void) sendTextEvent:(PRUnichar*) aBuffer
|
|
|
|
attributedString:(NSAttributedString*) aString
|
|
|
|
selectedRange:(NSRange)selRange
|
|
|
|
markedRange:(NSRange)markRange
|
|
|
|
doCommit:(BOOL)doCommit;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// do generic gecko event setup with a generic cocoa event. accepts nil inEvent.
|
|
|
|
- (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// set up a gecko mouse event based on a cocoa mouse event
|
|
|
|
- (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent;
|
|
|
|
|
|
|
|
// set up a gecko key event based on a cocoa key event
|
|
|
|
- (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
- (NSMenu*)contextMenu;
|
|
|
|
- (TopLevelWindowData*)ensureWindowData;
|
|
|
|
|
|
|
|
- (void)setIsPluginView:(BOOL)aIsPlugin;
|
2007-03-26 18:07:57 -07:00
|
|
|
- (BOOL)isPluginView;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
- (BOOL)childViewHasPlugin;
|
|
|
|
|
|
|
|
- (BOOL)isRectObscuredBySubview:(NSRect)inRect;
|
|
|
|
|
|
|
|
- (void)processPendingRedraws;
|
|
|
|
|
2008-05-05 16:01:07 -07:00
|
|
|
- (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv;
|
|
|
|
|
2007-11-06 23:53:24 -08:00
|
|
|
- (BOOL)ensureCorrectMouseEventTarget:(NSEvent *)anEvent;
|
2007-07-17 13:29:39 -07:00
|
|
|
|
|
|
|
- (void)maybeInitContextMenuTracking;
|
|
|
|
|
2008-05-05 16:01:07 -07:00
|
|
|
+ (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#if USE_CLICK_HOLD_CONTEXTMENU
|
|
|
|
// called on a timer two seconds after a mouse down to see if we should display
|
|
|
|
// a context menu (click-hold)
|
|
|
|
- (void)clickHoldCallback:(id)inEvent;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
- (id<mozAccessible>)accessible;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
/* Convenience routines to go from a gecko rect to cocoa NSRects and back
|
|
|
|
*
|
|
|
|
* Gecko rects (nsRect) contain an origin (x,y) in a coordinate
|
|
|
|
* system with (0,0) in the top-left of the screen. Cocoa rects
|
|
|
|
* (NSRect) contain an origin (x,y) in a coordinate system with
|
|
|
|
* (0,0) in the bottom-left of the screen. Both nsRect and NSRect
|
|
|
|
* contain width/height info, with no difference in their use.
|
|
|
|
* If a Cocoa rect is from a flipped view, there is no need to
|
|
|
|
* convert coordinate systems.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
GeckoRectToNSRect(const nsRect & inGeckoRect, NSRect & outCocoaRect)
|
|
|
|
{
|
|
|
|
outCocoaRect.origin.x = inGeckoRect.x;
|
|
|
|
outCocoaRect.origin.y = inGeckoRect.y;
|
|
|
|
outCocoaRect.size.width = inGeckoRect.width;
|
|
|
|
outCocoaRect.size.height = inGeckoRect.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
NSRectToGeckoRect(const NSRect & inCocoaRect, nsRect & outGeckoRect)
|
|
|
|
{
|
2007-07-08 00:08:04 -07:00
|
|
|
outGeckoRect.x = static_cast<nscoord>(inCocoaRect.origin.x);
|
|
|
|
outGeckoRect.y = static_cast<nscoord>(inCocoaRect.origin.y);
|
|
|
|
outGeckoRect.width = static_cast<nscoord>(inCocoaRect.size.width);
|
|
|
|
outGeckoRect.height = static_cast<nscoord>(inCocoaRect.size.height);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
ConvertGeckoRectToMacRect(const nsRect& aRect, Rect& outMacRect)
|
|
|
|
{
|
|
|
|
outMacRect.left = aRect.x;
|
|
|
|
outMacRect.top = aRect.y;
|
|
|
|
outMacRect.right = aRect.x + aRect.width;
|
|
|
|
outMacRect.bottom = aRect.y + aRect.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flips a screen coordinate from a point in the cocoa coordinate system (bottom-left rect) to a point
|
|
|
|
// that is a "flipped" cocoa coordinate system (starts in the top-left).
|
|
|
|
static inline void
|
|
|
|
FlipCocoaScreenCoordinate (NSPoint &inPoint)
|
|
|
|
{
|
2007-12-05 15:17:08 -08:00
|
|
|
inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static PRUint32
|
|
|
|
UnderlineAttributeToTextRangeType(PRUint32 aUnderlineStyle, NSRange selRange)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_IME
|
|
|
|
NSLog(@"****in underlineAttributeToTextRangeType = %d", aUnderlineStyle);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// For more info on the underline attribute, please see:
|
|
|
|
// http://developer.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/ProgrammingTopics/AttributedStrings/Tasks/AccessingAttrs.html
|
|
|
|
// We are not clear where the define for value 2 is right now.
|
|
|
|
// To see this value in japanese ime, type 'aaaaaaaaa' and hit space to make the
|
|
|
|
// ime send you some part of text in 1 (NSSingleUnderlineStyle) and some part in 2.
|
|
|
|
// ftang will ask apple for more details
|
|
|
|
//
|
|
|
|
// it probably means show 1-pixel thickness underline vs 2-pixel thickness
|
|
|
|
|
|
|
|
PRUint32 attr;
|
|
|
|
if (selRange.length == 0) {
|
|
|
|
switch (aUnderlineStyle) {
|
|
|
|
case 1:
|
|
|
|
attr = NS_TEXTRANGE_RAWINPUT;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
default:
|
|
|
|
attr = NS_TEXTRANGE_SELECTEDRAWTEXT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
switch (aUnderlineStyle) {
|
|
|
|
case 1:
|
|
|
|
attr = NS_TEXTRANGE_CONVERTEDTEXT;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
default:
|
|
|
|
attr = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static PRUint32
|
|
|
|
CountRanges(NSAttributedString *aString)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Iterate through aString for the NSUnderlineStyleAttributeName and count the
|
|
|
|
// different segments adjusting limitRange as we go.
|
|
|
|
PRUint32 count = 0;
|
|
|
|
NSRange effectiveRange;
|
|
|
|
NSRange limitRange = NSMakeRange(0, [aString length]);
|
|
|
|
while (limitRange.length > 0) {
|
|
|
|
[aString attribute:NSUnderlineStyleAttributeName
|
|
|
|
atIndex:limitRange.location
|
|
|
|
longestEffectiveRange:&effectiveRange
|
|
|
|
inRange:limitRange];
|
|
|
|
limitRange = NSMakeRange(NSMaxRange(effectiveRange),
|
|
|
|
NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
return count;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
ConvertAttributeToGeckoRange(NSAttributedString *aString, NSRange markRange, NSRange selRange, PRUint32 inCount, nsTextRange* aRanges)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Convert the Cocoa range into the nsTextRange Array used in Gecko.
|
|
|
|
// Iterate through the attributed string and map the underline attribute to Gecko IME textrange attributes.
|
|
|
|
// We may need to change the code here if we change the implementation of validAttributesForMarkedText.
|
|
|
|
PRUint32 i = 0;
|
|
|
|
NSRange effectiveRange;
|
|
|
|
NSRange limitRange = NSMakeRange(0, [aString length]);
|
|
|
|
while ((limitRange.length > 0) && (i < inCount)) {
|
|
|
|
id attributeValue = [aString attribute:NSUnderlineStyleAttributeName
|
|
|
|
atIndex:limitRange.location
|
|
|
|
longestEffectiveRange:&effectiveRange
|
|
|
|
inRange:limitRange];
|
|
|
|
aRanges[i].mStartOffset = effectiveRange.location;
|
|
|
|
aRanges[i].mEndOffset = NSMaxRange(effectiveRange);
|
|
|
|
aRanges[i].mRangeType = UnderlineAttributeToTextRangeType([attributeValue intValue], selRange);
|
|
|
|
limitRange = NSMakeRange(NSMaxRange(effectiveRange),
|
|
|
|
NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
// Get current caret position.
|
|
|
|
// Caret is indicator of insertion point, so mEndOffset = 0.
|
|
|
|
aRanges[i].mStartOffset = selRange.location + selRange.length;
|
|
|
|
aRanges[i].mEndOffset = 0;
|
|
|
|
aRanges[i].mRangeType = NS_TEXTRANGE_CARETPOSITION;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
FillTextRangeInTextEvent(nsTextEvent *aTextEvent, NSAttributedString* aString, NSRange markRange, NSRange selRange)
|
2008-02-20 15:47:05 -08:00
|
|
|
{
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Count the number of segments in the attributed string and add one more count for sending current caret position to Gecko.
|
|
|
|
// Allocate the right size of nsTextRange and draw caret at right position.
|
|
|
|
// Convert the attributed string into an array of nsTextRange and get current caret position by calling above functions.
|
|
|
|
PRUint32 count = CountRanges(aString) + 1;
|
|
|
|
aTextEvent->rangeArray = new nsTextRange[count];
|
2008-02-20 15:47:05 -08:00
|
|
|
if (aTextEvent->rangeArray) {
|
2007-03-22 10:30:00 -07:00
|
|
|
aTextEvent->rangeCount = count;
|
|
|
|
ConvertAttributeToGeckoRange(aString, markRange, selRange, aTextEvent->rangeCount, aTextEvent->rangeArray);
|
2008-02-20 15:47:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
nsChildView::nsChildView() : nsBaseWidget()
|
|
|
|
, mView(nsnull)
|
|
|
|
, mParentView(nsnull)
|
|
|
|
, mParentWidget(nsnull)
|
|
|
|
, mVisible(PR_FALSE)
|
|
|
|
, mDrawing(PR_FALSE)
|
|
|
|
, mLiveResizeInProgress(PR_FALSE)
|
2007-03-26 18:07:57 -07:00
|
|
|
, mIsPluginView(PR_FALSE)
|
2007-03-22 10:30:00 -07:00
|
|
|
, mPluginDrawing(PR_FALSE)
|
2007-03-26 18:07:57 -07:00
|
|
|
, mPluginIsCG(PR_FALSE)
|
2008-01-17 08:58:29 -08:00
|
|
|
, mInSetFocus(PR_FALSE)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-07-16 19:24:05 -07:00
|
|
|
#ifdef PR_LOGGING
|
2008-05-08 15:41:13 -07:00
|
|
|
if (!sCocoaLog) {
|
2007-07-16 19:24:05 -07:00
|
|
|
sCocoaLog = PR_NewLogModule("nsCocoaWidgets");
|
2008-05-08 15:41:13 -07:00
|
|
|
CFIndex idx;
|
|
|
|
KLGetKeyboardLayoutCount(&idx);
|
|
|
|
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("Keyboard layout configuration:"));
|
|
|
|
for (CFIndex i = 0; i < idx; ++i) {
|
|
|
|
KeyboardLayoutRef curKL;
|
|
|
|
if (KLGetKeyboardLayoutAtIndex(i, &curKL) == noErr) {
|
|
|
|
CFStringRef name;
|
|
|
|
if (KLGetKeyboardLayoutProperty(curKL, kKLName, (const void**)&name) == noErr) {
|
|
|
|
int idn;
|
|
|
|
KLGetKeyboardLayoutProperty(curKL, kKLIdentifier, (const void**)&idn);
|
|
|
|
int kind;
|
|
|
|
KLGetKeyboardLayoutProperty(curKL, kKLKind, (const void**)&kind);
|
|
|
|
char buf[256];
|
|
|
|
CFStringGetCString(name, buf, 256, kCFStringEncodingASCII);
|
|
|
|
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, (" %d,%s,%d\n", idn, buf, kind));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-07-16 19:24:05 -07:00
|
|
|
#endif
|
|
|
|
|
2007-09-04 23:58:16 -07:00
|
|
|
SetBackgroundColor(NS_RGB(255, 255, 255));
|
|
|
|
SetForegroundColor(NS_RGB(0, 0, 0));
|
2008-05-08 15:03:46 -07:00
|
|
|
|
|
|
|
if (nsToolkit::OnLeopardOrLater() && !Leopard_TISCopyCurrentKeyboardLayoutInputSource) {
|
2008-07-07 19:19:56 -07:00
|
|
|
// This libary would already be open for LMGetKbdType (and probably other
|
|
|
|
// symbols), so merely using RTLD_DEFAULT in dlsym would be sufficient,
|
|
|
|
// but man dlsym says: "all mach-o images in the process (except ...) are
|
|
|
|
// searched in the order they were loaded. This can be a costly search
|
|
|
|
// and should be avoided."
|
2008-05-08 15:03:46 -07:00
|
|
|
void* hitoolboxHandle = dlopen("/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/Versions/A/HIToolbox", RTLD_LAZY);
|
|
|
|
if (hitoolboxHandle) {
|
|
|
|
*(void **)(&Leopard_TISCopyCurrentKeyboardLayoutInputSource) = dlsym(hitoolboxHandle, "TISCopyCurrentKeyboardLayoutInputSource");
|
|
|
|
*(void **)(&Leopard_TISGetInputSourceProperty) = dlsym(hitoolboxHandle, "TISGetInputSourceProperty");
|
2008-05-09 16:14:57 -07:00
|
|
|
*(void **)(&Leopard_TISCreateInputSourceList) = dlsym(hitoolboxHandle, "TISCreateInputSourceList");
|
2008-05-08 15:03:46 -07:00
|
|
|
kOurTISPropertyUnicodeKeyLayoutData = *static_cast<CFStringRef*>(dlsym(hitoolboxHandle, "kTISPropertyUnicodeKeyLayoutData"));
|
2008-05-09 16:14:57 -07:00
|
|
|
kOurTISPropertyInputSourceID = *static_cast<CFStringRef*>(dlsym(hitoolboxHandle, "kTISPropertyInputSourceID"));
|
2008-05-08 15:03:46 -07:00
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsChildView::~nsChildView()
|
|
|
|
{
|
|
|
|
// notify the children that we're gone
|
|
|
|
for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
|
2007-07-08 00:08:04 -07:00
|
|
|
nsChildView* childView = static_cast<nsChildView*>(kid);
|
2007-03-22 10:30:00 -07:00
|
|
|
childView->mParentWidget = nsnull;
|
|
|
|
}
|
|
|
|
|
2008-06-27 10:11:24 -07:00
|
|
|
NS_WARN_IF_FALSE(mOnDestroyCalled, "nsChildView object destroyed without calling Destroy()");
|
|
|
|
|
|
|
|
// An nsChildView object that was in use can be destroyed without Destroy()
|
|
|
|
// ever being called on it. So we also need to do a quick, safe cleanup
|
|
|
|
// here (it's too late to just call Destroy(), which can cause crashes).
|
|
|
|
// It's particularly important to make sure widgetDestroyed is called on our
|
|
|
|
// mView -- this method NULLs mView's mGeckoChild, and NULL checks on
|
|
|
|
// mGeckoChild are used throughout the ChildView class to tell if it's safe
|
|
|
|
// to use a ChildView object.
|
|
|
|
[mView widgetDestroyed]; // Safe if mView is nil.
|
|
|
|
mParentWidget = nil;
|
|
|
|
TearDownView(); // Safe if called twice.
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-13 19:56:18 -07:00
|
|
|
NS_IMPL_ISUPPORTS_INHERITED1(nsChildView, nsBaseWidget, nsIPluginWidget)
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
|
|
|
|
// Utility method for implementing both Create(nsIWidget ...)
|
|
|
|
// and Create(nsNativeWidget...)
|
|
|
|
nsresult nsChildView::StandardCreate(nsIWidget *aParent,
|
|
|
|
const nsRect &aRect,
|
|
|
|
EVENT_CALLBACK aHandleEventFunction,
|
|
|
|
nsIDeviceContext *aContext,
|
|
|
|
nsIAppShell *aAppShell,
|
|
|
|
nsIToolkit *aToolkit,
|
|
|
|
nsWidgetInitData *aInitData,
|
|
|
|
nsNativeWidget aNativeParent)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2008-02-13 07:57:12 -08:00
|
|
|
// See NSWindow (MethodSwizzling) in nsCocoaWindow.mm.
|
|
|
|
if (!gCocoaWindowMethodsSwizzled) {
|
|
|
|
nsToolkit::SwizzleMethods([NSWindow class], @selector(sendEvent:),
|
|
|
|
@selector(nsCocoaWindow_NSWindow_sendEvent:));
|
|
|
|
gCocoaWindowMethodsSwizzled = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mBounds = aRect;
|
|
|
|
|
|
|
|
BaseCreate(aParent, aRect, aHandleEventFunction,
|
|
|
|
aContext, aAppShell, aToolkit, aInitData);
|
|
|
|
|
|
|
|
// inherit things from the parent view and create our parallel
|
|
|
|
// NSView in the Cocoa display system
|
|
|
|
mParentView = nil;
|
|
|
|
if (aParent) {
|
|
|
|
SetBackgroundColor(aParent->GetBackgroundColor());
|
|
|
|
SetForegroundColor(aParent->GetForegroundColor());
|
|
|
|
|
|
|
|
// inherit the top-level window. NS_NATIVE_WIDGET is always a NSView
|
|
|
|
// regardless of if we're asking a window or a view (for compatibility
|
|
|
|
// with windows).
|
|
|
|
mParentView = (NSView*)aParent->GetNativeData(NS_NATIVE_WIDGET);
|
|
|
|
mParentWidget = aParent;
|
|
|
|
}
|
|
|
|
else
|
2007-07-08 00:08:04 -07:00
|
|
|
mParentView = reinterpret_cast<NSView*>(aNativeParent);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// create our parallel NSView and hook it up to our parent. Recall
|
|
|
|
// that NS_NATIVE_WIDGET is the NSView.
|
|
|
|
NSRect r;
|
|
|
|
GeckoRectToNSRect(mBounds, r);
|
|
|
|
mView = [CreateCocoaView(r) retain];
|
|
|
|
if (!mView) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
// if our parent is a popup window, we're most certainly coming from a <select> list dropdown which
|
|
|
|
// we handle in a different way than other platforms. It's ok that we don't have a parent
|
|
|
|
// view because we bailed before even creating the cocoa widgetry and as a result, we
|
|
|
|
// don't need to assert. However, if that's not the case, we definitely want to assert
|
|
|
|
// to show views aren't getting correctly parented.
|
|
|
|
if (aParent) {
|
|
|
|
nsWindowType windowType;
|
|
|
|
aParent->GetWindowType(windowType);
|
|
|
|
if (windowType != eWindowType_popup)
|
|
|
|
NS_ASSERTION(mParentView && mView, "couldn't hook up new NSView in hierarchy");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
NS_ASSERTION(mParentView && mView, "couldn't hook up new NSView in hierarchy");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// If this view was created in a Gecko view hierarchy, the initial state
|
|
|
|
// is hidden. If the view is attached only to a native NSView but has
|
|
|
|
// no Gecko parent (as in embedding), the initial state is visible.
|
|
|
|
if (mParentWidget)
|
|
|
|
[mView setHidden:YES];
|
|
|
|
else
|
|
|
|
mVisible = PR_TRUE;
|
|
|
|
|
|
|
|
// Hook it up in the NSView hierarchy.
|
|
|
|
if (mParentView) {
|
|
|
|
NSWindow* window = [mParentView window];
|
|
|
|
if (!window &&
|
|
|
|
[mParentView respondsToSelector:@selector(nativeWindow)])
|
|
|
|
window = [mParentView nativeWindow];
|
|
|
|
|
|
|
|
[mView setNativeWindow:window];
|
|
|
|
|
|
|
|
[mParentView addSubview:mView];
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this is a ChildView, make sure that our per-window data
|
|
|
|
// is set up
|
|
|
|
if ([mView isKindOfClass:[ChildView class]])
|
|
|
|
[(ChildView*)mView ensureWindowData];
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Creates the appropriate child view. Override to create something other than
|
|
|
|
// our |ChildView| object. Autoreleases, so caller must retain.
|
|
|
|
NSView*
|
|
|
|
nsChildView::CreateCocoaView(NSRect inFrame)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-10-09 11:46:30 -07:00
|
|
|
return [[[ChildView alloc] initWithFrame:inFrame geckoChild:this] autorelease];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nsChildView::TearDownView()
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mView)
|
|
|
|
return;
|
|
|
|
|
|
|
|
NSWindow* win = [mView window];
|
|
|
|
NSResponder* responder = [win firstResponder];
|
|
|
|
|
|
|
|
// We're being unhooked from the view hierarchy, don't leave our view
|
|
|
|
// or a child view as the window first responder.
|
|
|
|
if (responder && [responder isKindOfClass:[NSView class]] &&
|
|
|
|
[(NSView*)responder isDescendantOf:mView]) {
|
2008-01-15 15:11:55 -08:00
|
|
|
[win makeFirstResponder:[mView superview]];
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-05-30 11:25:44 -07:00
|
|
|
|
|
|
|
// If mView is win's contentView, win (mView's NSWindow) "owns" mView --
|
|
|
|
// win has retained mView, and will detach it from the view hierarchy and
|
|
|
|
// release it when necessary (when win is itself destroyed (in a call to
|
|
|
|
// [win dealloc])). So all we need to do here is call [mView release] (to
|
|
|
|
// match the call to [mView retain] in nsChildView::StandardCreate()).
|
|
|
|
// Also calling [mView removeFromSuperviewWithoutNeedingDisplay] causes
|
|
|
|
// mView to be released again and dealloced, while remaining win's
|
|
|
|
// contentView. So if we do that here, win will (for a short while) have
|
|
|
|
// an invalid contentView (for the consequences see bmo bugs 381087 and
|
|
|
|
// 374260).
|
|
|
|
if ([mView isEqual:[win contentView]]) {
|
|
|
|
[mView release];
|
|
|
|
} else {
|
|
|
|
// Stop NSView hierarchy being changed during [ChildView drawRect:]
|
|
|
|
[mView performSelectorOnMainThread:@selector(delayedTearDown) withObject:nil waitUntilDone:false];
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
mView = nil;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// create a nsChildView
|
|
|
|
NS_IMETHODIMP nsChildView::Create(nsIWidget *aParent,
|
|
|
|
const nsRect &aRect,
|
|
|
|
EVENT_CALLBACK aHandleEventFunction,
|
|
|
|
nsIDeviceContext *aContext,
|
|
|
|
nsIAppShell *aAppShell,
|
|
|
|
nsIToolkit *aToolkit,
|
|
|
|
nsWidgetInitData *aInitData)
|
|
|
|
{
|
|
|
|
return(StandardCreate(aParent, aRect, aHandleEventFunction, aContext,
|
|
|
|
aAppShell, aToolkit, aInitData, nsnull));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Creates a main nsChildView using a native widget (an NSView)
|
|
|
|
NS_IMETHODIMP nsChildView::Create(nsNativeWidget aNativeParent,
|
|
|
|
const nsRect &aRect,
|
|
|
|
EVENT_CALLBACK aHandleEventFunction,
|
|
|
|
nsIDeviceContext *aContext,
|
|
|
|
nsIAppShell *aAppShell,
|
|
|
|
nsIToolkit *aToolkit,
|
|
|
|
nsWidgetInitData *aInitData)
|
|
|
|
{
|
|
|
|
// what we're passed in |aNativeParent| is an NSView.
|
|
|
|
return(StandardCreate(nsnull, aRect, aHandleEventFunction, aContext,
|
|
|
|
aAppShell, aToolkit, aInitData, aNativeParent));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::Destroy()
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mOnDestroyCalled)
|
|
|
|
return NS_OK;
|
|
|
|
mOnDestroyCalled = PR_TRUE;
|
|
|
|
|
|
|
|
[mView widgetDestroyed];
|
|
|
|
|
|
|
|
nsBaseWidget::OnDestroy();
|
|
|
|
nsBaseWidget::Destroy();
|
|
|
|
|
2008-01-15 15:11:55 -08:00
|
|
|
ReportDestroyEvent();
|
2007-03-22 10:30:00 -07:00
|
|
|
mParentWidget = nil;
|
|
|
|
|
|
|
|
TearDownView();
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
static void PrintViewHierarchy(NSView *view)
|
|
|
|
{
|
|
|
|
while (view) {
|
|
|
|
NSLog(@" view is %x, frame %@", view, NSStringFromRect([view frame]));
|
|
|
|
view = [view superview];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Return native data according to aDataType
|
|
|
|
void* nsChildView::GetNativeData(PRUint32 aDataType)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void* retVal = nsnull;
|
|
|
|
|
|
|
|
switch (aDataType)
|
|
|
|
{
|
2007-09-17 15:55:20 -07:00
|
|
|
case NS_NATIVE_WIDGET:
|
2007-03-22 10:30:00 -07:00
|
|
|
case NS_NATIVE_DISPLAY:
|
|
|
|
retVal = (void*)mView;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NS_NATIVE_WINDOW:
|
|
|
|
retVal = [mView nativeWindow];
|
|
|
|
break;
|
2007-09-17 15:55:20 -07:00
|
|
|
|
|
|
|
case NS_NATIVE_GRAPHIC:
|
|
|
|
NS_ASSERTION(0, "Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!");
|
|
|
|
retVal = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
break;
|
2007-09-17 15:55:20 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
case NS_NATIVE_OFFSETX:
|
|
|
|
retVal = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NS_NATIVE_OFFSETY:
|
|
|
|
retVal = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NS_NATIVE_PLUGIN_PORT:
|
2007-03-26 18:07:57 -07:00
|
|
|
#ifndef NP_NO_QUICKDRAW
|
|
|
|
case NS_NATIVE_PLUGIN_PORT_QD:
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-03-26 18:07:57 -07:00
|
|
|
mPluginIsCG = PR_FALSE;
|
|
|
|
mIsPluginView = PR_TRUE;
|
|
|
|
if ([mView isKindOfClass:[ChildView class]])
|
|
|
|
[(ChildView*)mView setIsPluginView:YES];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NSWindow* window = [mView nativeWindow];
|
|
|
|
if (window) {
|
|
|
|
WindowRef topLevelWindow = (WindowRef)[window windowRef];
|
|
|
|
if (topLevelWindow) {
|
2007-03-26 18:07:57 -07:00
|
|
|
mPluginPort.qdPort.port = ::GetWindowPort(topLevelWindow);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
|
|
|
|
NSRect frame = [[window contentView] frame];
|
|
|
|
viewOrigin.y = frame.size.height - viewOrigin.y;
|
|
|
|
|
|
|
|
// need to convert view's origin to window coordinates.
|
|
|
|
// then, encode as "SetOrigin" ready values.
|
2007-03-26 18:07:57 -07:00
|
|
|
mPluginPort.qdPort.portx = (PRInt32)-viewOrigin.x;
|
|
|
|
mPluginPort.qdPort.porty = (PRInt32)-viewOrigin.y;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-26 18:07:57 -07:00
|
|
|
retVal = (void*)&mPluginPort;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
case NS_NATIVE_PLUGIN_PORT_CG:
|
|
|
|
{
|
|
|
|
mPluginIsCG = PR_TRUE;
|
|
|
|
mIsPluginView = PR_TRUE;
|
|
|
|
if ([mView isKindOfClass:[ChildView class]])
|
|
|
|
[(ChildView*)mView setIsPluginView:YES];
|
|
|
|
|
2008-02-20 03:33:27 -08:00
|
|
|
mPluginPort.cgPort.context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
2007-03-26 18:07:57 -07:00
|
|
|
|
|
|
|
NSWindow* window = [mView nativeWindow];
|
|
|
|
if (window) {
|
|
|
|
WindowRef topLevelWindow = (WindowRef)[window windowRef];
|
|
|
|
mPluginPort.cgPort.window = topLevelWindow;
|
|
|
|
}
|
|
|
|
|
|
|
|
retVal = (void*)&mPluginPort;
|
2007-03-22 10:30:00 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return retVal;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
2008-08-12 17:44:14 -07:00
|
|
|
nsTransparencyMode nsChildView::GetTransparencyMode()
|
2007-12-19 11:40:18 -08:00
|
|
|
{
|
2008-08-12 17:44:14 -07:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
2008-08-12 17:44:14 -07:00
|
|
|
return [mView isOpaque] ? eTransparencyOpaque : eTransparencyTransparent;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
2008-08-12 17:44:14 -07:00
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
|
|
return eTransparencyOpaque;
|
2007-12-19 11:40:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This is called by nsContainerFrame on the root widget for all window types
|
2008-08-12 17:44:14 -07:00
|
|
|
// except popup windows (when nsCocoaWindow::SetTransparencyMode is used instead).
|
|
|
|
void nsChildView::SetTransparencyMode(nsTransparencyMode aMode)
|
2007-12-19 11:40:18 -08:00
|
|
|
{
|
2008-08-12 17:44:14 -07:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
2008-08-12 17:44:14 -07:00
|
|
|
BOOL isTransparent = aMode == eTransparencyTransparent;
|
2007-12-19 11:40:18 -08:00
|
|
|
BOOL currentTransparency = ![[mView nativeWindow] isOpaque];
|
2008-08-12 17:44:14 -07:00
|
|
|
if (isTransparent != currentTransparency) {
|
2007-12-19 11:40:18 -08:00
|
|
|
// Find out if this is a window we created by seeing if the delegate is WindowDelegate. If it is,
|
|
|
|
// tell the nsCocoaWindow to set its background to transparent.
|
|
|
|
id windowDelegate = [[mView nativeWindow] delegate];
|
|
|
|
if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
|
|
|
|
nsCocoaWindow *widget = [(WindowDelegate *)windowDelegate geckoWidget];
|
|
|
|
if (widget) {
|
2008-08-12 17:44:14 -07:00
|
|
|
widget->MakeBackgroundTransparent(aMode);
|
|
|
|
[(ChildView*)mView setTransparent:isTransparent];
|
2007-12-19 11:40:18 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
2008-08-12 17:44:14 -07:00
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-12-19 11:40:18 -08:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::IsVisible(PRBool& outState)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mVisible) {
|
|
|
|
outState = mVisible;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// mVisible does not accurately reflect the state of a hidden tabbed view
|
|
|
|
// so verify that the view has a window as well
|
|
|
|
outState = ([mView window] != nil);
|
|
|
|
// now check native widget hierarchy visibility
|
|
|
|
if (outState && NSIsEmptyRect([mView visibleRect])) {
|
|
|
|
outState = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-23 15:56:40 -07:00
|
|
|
void nsChildView::HidePlugin()
|
|
|
|
{
|
|
|
|
NS_ASSERTION(mIsPluginView, "HidePlugin called on non-plugin view");
|
|
|
|
|
|
|
|
if (mPluginInstanceOwner && !mPluginIsCG) {
|
|
|
|
nsPluginWindow* window;
|
|
|
|
mPluginInstanceOwner->GetWindow(window);
|
|
|
|
nsCOMPtr<nsIPluginInstance> instance;
|
|
|
|
mPluginInstanceOwner->GetInstance(*getter_AddRefs(instance));
|
|
|
|
if (window && instance) {
|
|
|
|
window->clipRect.top = 0;
|
|
|
|
window->clipRect.left = 0;
|
|
|
|
window->clipRect.bottom = 0;
|
|
|
|
window->clipRect.right = 0;
|
|
|
|
instance->SetWindow(window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void HideChildPluginViews(NSView* aView)
|
|
|
|
{
|
|
|
|
NSArray* subviews = [aView subviews];
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < [subviews count]; ++i) {
|
|
|
|
NSView* view = [subviews objectAtIndex: i];
|
|
|
|
|
|
|
|
if (![view isKindOfClass:[ChildView class]])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ChildView* childview = static_cast<ChildView*>(view);
|
|
|
|
if ([childview isPluginView]) {
|
|
|
|
nsChildView* widget = static_cast<nsChildView*>([childview widget]);
|
|
|
|
if (widget) {
|
|
|
|
widget->HidePlugin();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
HideChildPluginViews(view);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Hide or show this component
|
|
|
|
NS_IMETHODIMP nsChildView::Show(PRBool aState)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (aState != mVisible) {
|
|
|
|
[mView setHidden:!aState];
|
|
|
|
mVisible = aState;
|
2008-04-23 15:56:40 -07:00
|
|
|
if (!mVisible)
|
|
|
|
HideChildPluginViews(mView);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsIWidget*
|
|
|
|
nsChildView::GetParent(void)
|
|
|
|
{
|
|
|
|
return mParentWidget;
|
|
|
|
}
|
|
|
|
|
2008-02-19 23:40:04 -08:00
|
|
|
nsIWidget*
|
|
|
|
nsChildView::GetTopLevelWidget()
|
|
|
|
{
|
|
|
|
nsIWidget* current = this;
|
|
|
|
for (nsIWidget* parent = GetParent(); parent ; parent = parent->GetParent())
|
|
|
|
current = parent;
|
|
|
|
return current;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::ModalEventFilter(PRBool aRealEvent, void *aEvent,
|
|
|
|
PRBool *aForWindow)
|
|
|
|
{
|
|
|
|
if (aForWindow)
|
|
|
|
*aForWindow = PR_FALSE;
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::Enable(PRBool aState)
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::IsEnabled(PRBool *aState)
|
|
|
|
{
|
|
|
|
// unimplemented
|
|
|
|
if (aState)
|
|
|
|
*aState = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::SetFocus(PRBool aRaise)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2008-01-17 08:58:29 -08:00
|
|
|
// Don't do anything if SetFocus() has been called reentrantly on the same
|
|
|
|
// object. Sometimes calls to nsChildView::DispatchEvent() can get
|
|
|
|
// temporarily stuck, causing calls to [ChildView sendFocusEvent:] and
|
|
|
|
// SetFocus() to be reentered. These reentrant calls are probably the
|
|
|
|
// result of one or more bugs, and doing things on a reentrant call can
|
|
|
|
// cause problems: For example if mView is already the first responder and
|
|
|
|
// we send it an NS_GOTFOCUS event (see below), this causes the Mochitests
|
|
|
|
// to get stuck in the toolkit/content/tests/widgets/test_popup_button.xul
|
|
|
|
// test.
|
|
|
|
if (mInSetFocus)
|
|
|
|
return NS_OK;
|
|
|
|
mInSetFocus = PR_TRUE;
|
2007-03-22 10:30:00 -07:00
|
|
|
NSWindow* window = [mView window];
|
2008-01-17 08:58:29 -08:00
|
|
|
if (window) {
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(mView);
|
2008-01-17 08:58:29 -08:00
|
|
|
// For reasons that aren't yet clear, focus changes within a window (as
|
|
|
|
// opposed to those between windows or between apps) should only trigger
|
|
|
|
// NS_LOSTFOCUS and NS_GOTFOCUS events (sent to Gecko) in the context of
|
|
|
|
// a call to nsChildView::SetFocus() (or nsCocoaWindow::SetFocus(), which
|
|
|
|
// in any case re-routes to nsChildView::SetFocus()). If we send these
|
|
|
|
// events on every intra-window focus change (on every call to
|
|
|
|
// [ChildView becomeFirstResponder:] or [ChildView resignFirstResponder:]),
|
|
|
|
// the result will be strange focus bugs (like bmo bugs 399471, 403232,
|
|
|
|
// 404433 and 408266).
|
|
|
|
NSResponder* firstResponder = [window firstResponder];
|
|
|
|
if ([mView isEqual:firstResponder]) {
|
|
|
|
// Sometimes SetFocus() is called on an nsChildView object that's
|
|
|
|
// already focused. In principle this shouldn't happen, and in any
|
|
|
|
// case we shouldn't have to dispatch any events. But if we don't, we
|
|
|
|
// sometimes get text-input cursors blinking in more than one text
|
|
|
|
// field, or still blinking when the browser is no longer active. For
|
|
|
|
// reasons that aren't at all clear, this problem can be avoided by
|
|
|
|
// always sending an NS_GOTFOCUS message here.
|
|
|
|
if ([mView isKindOfClass:[ChildView class]])
|
|
|
|
[(ChildView *)mView sendFocusEvent:NS_GOTFOCUS];
|
|
|
|
} else {
|
|
|
|
// Retain and release firstResponder around the call to
|
|
|
|
// makeFirstResponder.
|
|
|
|
[firstResponder retain];
|
|
|
|
if ([window makeFirstResponder:mView]) {
|
|
|
|
if ([firstResponder isKindOfClass:[ChildView class]])
|
|
|
|
[(ChildView *)firstResponder sendFocusEvent:NS_LOSTFOCUS];
|
|
|
|
if ([mView isKindOfClass:[ChildView class]])
|
|
|
|
[(ChildView *)mView sendFocusEvent:NS_GOTFOCUS];
|
|
|
|
}
|
|
|
|
[firstResponder release];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mInSetFocus = PR_FALSE;
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Set the colormap of the window
|
|
|
|
NS_IMETHODIMP nsChildView::SetColorMap(nsColorMap *aColorMap)
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-06-28 00:55:30 -07:00
|
|
|
NS_IMETHODIMP nsChildView::SetMenuBar(void* aMenuBar)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
return NS_ERROR_FAILURE; // subviews don't have menu bars
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::ShowMenuBar(PRBool aShow)
|
|
|
|
{
|
|
|
|
return NS_ERROR_FAILURE; // subviews don't have menu bars
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Override to set the cursor on the mac
|
|
|
|
NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsBaseWidget::SetCursor(aCursor);
|
|
|
|
[[nsCursorManager sharedInstance] setCursor: aCursor];
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// implement to fix "hidden virtual function" warning
|
|
|
|
NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor,
|
|
|
|
PRUint32 aHotspotX, PRUint32 aHotspotY)
|
|
|
|
{
|
|
|
|
return nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
// Get this component dimension
|
|
|
|
NS_IMETHODIMP nsChildView::GetBounds(nsRect &aRect)
|
|
|
|
{
|
|
|
|
aRect = mBounds;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_IMETHODIMP nsChildView::SetBounds(const nsRect &aRect)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsresult rv = Inherited::SetBounds(aRect);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
NSRect r;
|
|
|
|
GeckoRectToNSRect(aRect, r);
|
|
|
|
[mView setFrame:r];
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::ConstrainPosition(PRBool aAllowSlop,
|
|
|
|
PRInt32 *aX, PRInt32 *aY)
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Move this component, aX and aY are in the parent widget coordinate system
|
|
|
|
NS_IMETHODIMP nsChildView::Move(PRInt32 aX, PRInt32 aY)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-11-06 23:36:10 -08:00
|
|
|
if (!mView || (mBounds.x == aX && mBounds.y == aY))
|
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-06 23:36:10 -08:00
|
|
|
mBounds.x = aX;
|
|
|
|
mBounds.y = aY;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-06 23:36:10 -08:00
|
|
|
NSRect r;
|
|
|
|
GeckoRectToNSRect(mBounds, r);
|
|
|
|
[mView setFrame:r];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-06 23:36:10 -08:00
|
|
|
if (mVisible)
|
|
|
|
[mView setNeedsDisplay:YES];
|
|
|
|
|
|
|
|
ReportMoveEvent();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-11-06 23:36:10 -08:00
|
|
|
if (!mView || (mBounds.width == aWidth && mBounds.height == aHeight))
|
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-06 23:36:10 -08:00
|
|
|
mBounds.width = aWidth;
|
|
|
|
mBounds.height = aHeight;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-06 23:36:10 -08:00
|
|
|
NSRect r;
|
|
|
|
GeckoRectToNSRect(mBounds, r);
|
|
|
|
[mView setFrame:r];
|
|
|
|
|
|
|
|
if (mVisible && aRepaint)
|
|
|
|
[mView setNeedsDisplay:YES];
|
|
|
|
|
|
|
|
ReportSizeEvent();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-11-06 23:36:10 -08:00
|
|
|
BOOL isMoving = (mBounds.x != aX || mBounds.y != aY);
|
|
|
|
BOOL isResizing = (mBounds.width != aWidth || mBounds.height != aHeight);
|
|
|
|
if (!mView || (!isMoving && !isResizing))
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
if (isMoving) {
|
|
|
|
mBounds.x = aX;
|
|
|
|
mBounds.y = aY;
|
|
|
|
}
|
|
|
|
if (isResizing) {
|
|
|
|
mBounds.width = aWidth;
|
|
|
|
mBounds.height = aHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSRect r;
|
|
|
|
GeckoRectToNSRect(mBounds, r);
|
|
|
|
[mView setFrame:r];
|
|
|
|
|
|
|
|
if (mVisible && aRepaint)
|
|
|
|
[mView setNeedsDisplay:YES];
|
|
|
|
|
2008-01-15 15:11:55 -08:00
|
|
|
if (isMoving) {
|
2007-11-06 23:36:10 -08:00
|
|
|
ReportMoveEvent();
|
2008-01-15 15:11:55 -08:00
|
|
|
if (mOnDestroyCalled)
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2007-11-06 23:36:10 -08:00
|
|
|
if (isResizing)
|
|
|
|
ReportSizeEvent();
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_METHOD nsChildView::GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight)
|
|
|
|
{
|
|
|
|
return NS_ERROR_FAILURE; // nobody call this anywhere in the code
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_METHOD nsChildView::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight)
|
|
|
|
{
|
|
|
|
return NS_ERROR_FAILURE; // nobody call this anywhere in the code
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::BeginResizingChildren(void)
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::EndResizingChildren(void)
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-08-08 09:11:54 -07:00
|
|
|
|
|
|
|
static const PRInt32 resizeIndicatorWidth = 15;
|
|
|
|
static const PRInt32 resizeIndicatorHeight = 15;
|
|
|
|
PRBool nsChildView::ShowsResizeIndicator(nsIntRect* aResizerRect)
|
|
|
|
{
|
|
|
|
NSView *topLevelView = mView, *superView = nil;
|
|
|
|
while (superView = [topLevelView superview])
|
|
|
|
topLevelView = superView;
|
|
|
|
|
|
|
|
if (![[topLevelView window] showsResizeIndicator])
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
if (aResizerRect) {
|
|
|
|
NSSize bounds = [topLevelView bounds].size;
|
|
|
|
NSPoint corner = NSMakePoint(bounds.width, [topLevelView isFlipped] ? bounds.height : 0);
|
|
|
|
corner = [topLevelView convertPoint:corner toView:mView];
|
|
|
|
aResizerRect->SetRect(NSToIntRound(corner.x) - resizeIndicatorWidth,
|
|
|
|
NSToIntRound(corner.y) - resizeIndicatorHeight,
|
|
|
|
resizeIndicatorWidth, resizeIndicatorHeight);
|
|
|
|
}
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::GetPluginClipRect(nsRect& outClipRect, nsPoint& outOrigin, PRBool& outWidgetVisible)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-26 18:07:57 -07:00
|
|
|
NS_ASSERTION(mIsPluginView, "GetPluginClipRect must only be called on a plugin widget");
|
|
|
|
if (!mIsPluginView) return NS_ERROR_FAILURE;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NSWindow* window = [mView nativeWindow];
|
|
|
|
if (!window) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
|
|
|
|
NSRect frame = [[window contentView] frame];
|
|
|
|
viewOrigin.y = frame.size.height - viewOrigin.y;
|
|
|
|
|
|
|
|
// set up the clipping region for plugins.
|
|
|
|
NSRect visibleBounds = [mView visibleRect];
|
|
|
|
NSPoint clipOrigin = [mView convertPoint:visibleBounds.origin toView:nil];
|
|
|
|
|
|
|
|
// Convert from cocoa to QuickDraw coordinates
|
|
|
|
clipOrigin.y = frame.size.height - clipOrigin.y;
|
|
|
|
|
|
|
|
outClipRect.x = (nscoord)clipOrigin.x;
|
|
|
|
outClipRect.y = (nscoord)clipOrigin.y;
|
|
|
|
|
|
|
|
|
|
|
|
PRBool isVisible;
|
|
|
|
IsVisible(isVisible);
|
|
|
|
if (isVisible && [mView window] != nil) {
|
|
|
|
outClipRect.width = (nscoord)visibleBounds.size.width;
|
|
|
|
outClipRect.height = (nscoord)visibleBounds.size.height;
|
|
|
|
outWidgetVisible = PR_TRUE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
outClipRect.width = 0;
|
|
|
|
outClipRect.height = 0;
|
|
|
|
outWidgetVisible = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// need to convert view's origin to window coordinates.
|
|
|
|
// then, encode as "SetOrigin" ready values.
|
|
|
|
outOrigin.x = (nscoord)-viewOrigin.x;
|
|
|
|
outOrigin.y = (nscoord)-viewOrigin.y;
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::StartDrawPlugin()
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-26 18:07:57 -07:00
|
|
|
NS_ASSERTION(mIsPluginView, "StartDrawPlugin must only be called on a plugin widget");
|
|
|
|
if (!mIsPluginView) return NS_ERROR_FAILURE;
|
|
|
|
|
2008-03-17 08:37:05 -07:00
|
|
|
// Prevent reentrant "drawing" (or in fact reentrant handling of any plugin
|
|
|
|
// event). Doing this for both CoreGraphics and QuickDraw plugins restores
|
|
|
|
// the 1.8-branch behavior wrt reentrancy, and fixes (or works around) bugs
|
|
|
|
// caused by plugins depending on the old behavior -- e.g. bmo bug 409615.
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mPluginDrawing)
|
|
|
|
return NS_ERROR_FAILURE;
|
2008-03-17 08:37:05 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NSWindow* window = [mView nativeWindow];
|
|
|
|
if (!window)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// It appears that the WindowRef from which we get the plugin port undergoes the
|
|
|
|
// traditional BeginUpdate/EndUpdate cycle, which, if you recall, sets the visible
|
|
|
|
// region to the intersection of the visible region and the update region. Since
|
|
|
|
// we don't know here if we're being drawn inside a BeginUpdate/EndUpdate pair
|
|
|
|
// (which seem to occur in [NSWindow display]), and we don't want to have the burden
|
|
|
|
// of correctly doing Carbon invalidates of the plugin rect, we manually set the
|
2008-04-07 22:45:06 -07:00
|
|
|
// visible region to be the entire port every time. It is necessary to set up our
|
|
|
|
// window's port even for CoreGraphics plugins, because they may still use Carbon
|
|
|
|
// internally (see bug #420527 for details).
|
|
|
|
CGrafPtr port = ::GetWindowPort(WindowRef([window windowRef]));
|
|
|
|
if (!mPluginIsCG)
|
|
|
|
port = mPluginPort.qdPort.port;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
RgnHandle pluginRegion = ::NewRgn();
|
|
|
|
if (pluginRegion) {
|
2008-04-07 22:45:06 -07:00
|
|
|
PRBool portChanged = (port != CGrafPtr(GetQDGlobalsThePort()));
|
2007-03-22 10:30:00 -07:00
|
|
|
CGrafPtr oldPort;
|
|
|
|
GDHandle oldDevice;
|
|
|
|
|
|
|
|
if (portChanged) {
|
|
|
|
::GetGWorld(&oldPort, &oldDevice);
|
2008-04-07 22:45:06 -07:00
|
|
|
::SetGWorld(port, ::IsPortOffscreen(port) ? nsnull : ::GetMainDevice());
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
::SetOrigin(0, 0);
|
|
|
|
|
|
|
|
nsRect clipRect; // this is in native window coordinates
|
|
|
|
nsPoint origin;
|
|
|
|
PRBool visible;
|
|
|
|
GetPluginClipRect(clipRect, origin, visible);
|
|
|
|
|
|
|
|
// XXX if we're not visible, set an empty clip region?
|
|
|
|
Rect pluginRect;
|
|
|
|
ConvertGeckoRectToMacRect(clipRect, pluginRect);
|
|
|
|
|
|
|
|
::RectRgn(pluginRegion, &pluginRect);
|
2008-04-07 22:45:06 -07:00
|
|
|
::SetPortVisibleRegion(port, pluginRegion);
|
|
|
|
::SetPortClipRegion(port, pluginRegion);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// now set up the origin for the plugin
|
|
|
|
::SetOrigin(origin.x, origin.y);
|
|
|
|
|
|
|
|
::DisposeRgn(pluginRegion);
|
|
|
|
|
|
|
|
if (portChanged)
|
|
|
|
::SetGWorld(oldPort, oldDevice);
|
|
|
|
}
|
|
|
|
|
|
|
|
mPluginDrawing = PR_TRUE;
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::EndDrawPlugin()
|
|
|
|
{
|
2007-03-26 18:07:57 -07:00
|
|
|
NS_ASSERTION(mIsPluginView, "EndDrawPlugin must only be called on a plugin widget");
|
|
|
|
if (!mIsPluginView) return NS_ERROR_FAILURE;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mPluginDrawing = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-23 15:56:40 -07:00
|
|
|
NS_IMETHODIMP nsChildView::SetPluginInstanceOwner(nsIPluginInstanceOwner* aInstanceOwner)
|
|
|
|
{
|
|
|
|
mPluginInstanceOwner = aInstanceOwner;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void nsChildView::LiveResizeStarted()
|
|
|
|
{
|
|
|
|
// XXX todo. Use this to disable Java async redraw during resize
|
|
|
|
mLiveResizeInProgress = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nsChildView::LiveResizeEnded()
|
|
|
|
{
|
|
|
|
mLiveResizeInProgress = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2008-05-05 16:01:07 -07:00
|
|
|
static NSString* ToNSString(const nsAString& aString)
|
|
|
|
{
|
|
|
|
return [NSString stringWithCharacters:aString.BeginReading()
|
|
|
|
length:aString.Length()];
|
|
|
|
}
|
|
|
|
|
2008-05-09 16:14:57 -07:00
|
|
|
struct KeyboardLayoutOverride {
|
|
|
|
PRInt32 mKeyboardLayout;
|
|
|
|
PRBool mOverrideEnabled;
|
|
|
|
};
|
|
|
|
|
|
|
|
static KeyboardLayoutOverride gOverrideKeyboardLayout;
|
2008-05-05 16:01:07 -07:00
|
|
|
|
|
|
|
static const PRUint32 sModifierFlagMap[][2] = {
|
|
|
|
{ nsIWidget::CAPS_LOCK, NSAlphaShiftKeyMask },
|
|
|
|
{ nsIWidget::SHIFT_L, NSShiftKeyMask },
|
|
|
|
{ nsIWidget::CTRL_L, NSControlKeyMask },
|
|
|
|
{ nsIWidget::ALT_L, NSAlternateKeyMask },
|
|
|
|
{ nsIWidget::COMMAND, NSCommandKeyMask },
|
|
|
|
{ nsIWidget::NUMERIC_KEY_PAD, NSNumericPadKeyMask },
|
|
|
|
{ nsIWidget::HELP, NSHelpKeyMask },
|
|
|
|
{ nsIWidget::FUNCTION, NSFunctionKeyMask }
|
|
|
|
};
|
2008-05-06 22:17:40 -07:00
|
|
|
nsresult nsChildView::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
|
|
|
PRInt32 aNativeKeyCode,
|
|
|
|
PRUint32 aModifierFlags,
|
|
|
|
const nsAString& aCharacters,
|
|
|
|
const nsAString& aUnmodifiedCharacters)
|
2008-05-05 16:01:07 -07:00
|
|
|
{
|
2008-05-06 22:17:40 -07:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
2008-05-05 16:01:07 -07:00
|
|
|
|
|
|
|
PRUint32 modifierFlags = 0;
|
|
|
|
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(sModifierFlagMap); ++i) {
|
|
|
|
if (aModifierFlags & sModifierFlagMap[i][0]) {
|
|
|
|
modifierFlags |= sModifierFlagMap[i][1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int windowNumber = [[mView window] windowNumber];
|
|
|
|
NSEvent* downEvent = [NSEvent keyEventWithType:NSKeyDown
|
|
|
|
location:NSMakePoint(0,0)
|
|
|
|
modifierFlags:modifierFlags
|
|
|
|
timestamp:0
|
|
|
|
windowNumber:windowNumber
|
|
|
|
context:[NSGraphicsContext currentContext]
|
|
|
|
characters:ToNSString(aCharacters)
|
|
|
|
charactersIgnoringModifiers:ToNSString(aUnmodifiedCharacters)
|
|
|
|
isARepeat:NO
|
|
|
|
keyCode:aNativeKeyCode];
|
|
|
|
|
|
|
|
NSEvent* upEvent = [ChildView makeNewCocoaEventWithType:NSKeyUp
|
|
|
|
fromEvent:downEvent];
|
|
|
|
|
|
|
|
if (downEvent && upEvent) {
|
2008-05-09 16:14:57 -07:00
|
|
|
KeyboardLayoutOverride currentLayout = gOverrideKeyboardLayout;
|
|
|
|
gOverrideKeyboardLayout.mKeyboardLayout = aNativeKeyboardLayout;
|
|
|
|
gOverrideKeyboardLayout.mOverrideEnabled = PR_TRUE;
|
2008-05-06 14:00:59 -07:00
|
|
|
[NSApp sendEvent:downEvent];
|
|
|
|
[NSApp sendEvent:upEvent];
|
2008-05-05 16:01:07 -07:00
|
|
|
// processKeyDownEvent and keyUp block exceptions so we're sure to
|
|
|
|
// reach here to restore gOverrideKeyboardLayout
|
|
|
|
gOverrideKeyboardLayout = currentLayout;
|
|
|
|
}
|
|
|
|
|
2008-05-06 22:17:40 -07:00
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2008-05-05 16:01:07 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-06-28 00:55:30 -07:00
|
|
|
// Used for testing native menu system structure and event handling.
|
|
|
|
NS_IMETHODIMP nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString)
|
2008-07-27 21:46:33 -07:00
|
|
|
{
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2008-06-28 00:55:30 -07:00
|
|
|
NSString* title = [NSString stringWithCharacters:indexString.BeginReading() length:indexString.Length()];
|
|
|
|
NSArray* indexes = [title componentsSeparatedByString:@"|"];
|
|
|
|
unsigned int indexCount = [indexes count];
|
|
|
|
if (indexCount == 0)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
NSMenu* currentSubmenu = [NSApp mainMenu];
|
|
|
|
for (unsigned int i = 0; i < (indexCount - 1); i++) {
|
|
|
|
NSMenu* newSubmenu = nil;
|
|
|
|
int targetIndex;
|
|
|
|
// We remove the application menu from consideration for the top-level menu
|
|
|
|
if (i == 0)
|
|
|
|
targetIndex = [[indexes objectAtIndex:i] intValue] + 1;
|
|
|
|
else
|
|
|
|
targetIndex = [[indexes objectAtIndex:i] intValue];
|
|
|
|
int itemCount = [currentSubmenu numberOfItems];
|
|
|
|
if (targetIndex < itemCount) {
|
|
|
|
NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
|
|
|
|
if ([menuItem hasSubmenu])
|
|
|
|
newSubmenu = [menuItem submenu];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newSubmenu)
|
|
|
|
currentSubmenu = newSubmenu;
|
|
|
|
else
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int itemCount = [currentSubmenu numberOfItems];
|
|
|
|
int targetIndex = [[indexes objectAtIndex:(indexCount - 1)] intValue];
|
2008-07-27 21:46:33 -07:00
|
|
|
// We can't perform an action on an item with a submenu, that will raise
|
|
|
|
// an obj-c exception.
|
|
|
|
if (targetIndex < itemCount && ![[currentSubmenu itemAtIndex:targetIndex] hasSubmenu]) {
|
|
|
|
// NSLog(@"Performing action for native menu item titled: %@\n",
|
|
|
|
// [[currentSubmenu itemAtIndex:targetIndex] title]);
|
|
|
|
[currentSubmenu performActionForItemAtIndex:targetIndex];
|
2008-06-28 00:55:30 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-07-27 21:46:33 -07:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::ForceNativeMenuReload()
|
|
|
|
{
|
|
|
|
id windowDelegate = [[mView nativeWindow] delegate];
|
|
|
|
if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
|
|
|
|
nsCocoaWindow *widget = [(WindowDelegate *)windowDelegate geckoWidget];
|
|
|
|
if (widget) {
|
|
|
|
nsMenuBarX* mb = widget->GetMenuBar();
|
|
|
|
if (mb) {
|
|
|
|
mb->ForceNativeMenuReload();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-06-28 00:55:30 -07:00
|
|
|
}
|
|
|
|
|
2008-07-27 21:46:33 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef INVALIDATE_DEBUGGING
|
|
|
|
|
|
|
|
static Boolean KeyDown(const UInt8 theKey)
|
|
|
|
{
|
|
|
|
KeyMap map;
|
|
|
|
GetKeys(map);
|
|
|
|
return ((*((UInt8 *)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Boolean caps_lock()
|
|
|
|
{
|
|
|
|
return KeyDown(0x39);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void blinkRect(Rect* r)
|
|
|
|
{
|
|
|
|
StRegionFromPool oldClip;
|
|
|
|
if (oldClip != NULL)
|
|
|
|
::GetClip(oldClip);
|
|
|
|
|
|
|
|
::ClipRect(r);
|
|
|
|
::InvertRect(r);
|
|
|
|
UInt32 end = ::TickCount() + 5;
|
|
|
|
while (::TickCount() < end) ;
|
|
|
|
::InvertRect(r);
|
|
|
|
|
|
|
|
if (oldClip != NULL)
|
|
|
|
::SetClip(oldClip);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void blinkRgn(RgnHandle rgn)
|
|
|
|
{
|
|
|
|
StRegionFromPool oldClip;
|
|
|
|
if (oldClip != NULL)
|
|
|
|
::GetClip(oldClip);
|
|
|
|
|
|
|
|
::SetClip(rgn);
|
|
|
|
::InvertRgn(rgn);
|
|
|
|
UInt32 end = ::TickCount() + 5;
|
|
|
|
while (::TickCount() < end) ;
|
|
|
|
::InvertRgn(rgn);
|
|
|
|
|
|
|
|
if (oldClip != NULL)
|
|
|
|
::SetClip(oldClip);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// Invalidate this component's visible area
|
|
|
|
NS_IMETHODIMP nsChildView::Invalidate(PRBool aIsSynchronous)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mView || !mVisible)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
if (aIsSynchronous) {
|
|
|
|
[mView display];
|
|
|
|
}
|
|
|
|
else if ([NSView focusView]) {
|
|
|
|
// if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
|
|
|
|
// don't lose it.
|
|
|
|
[mView setNeedsPendingDisplay];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
[mView setNeedsDisplay:YES];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Invalidate this component's visible area
|
|
|
|
NS_IMETHODIMP nsChildView::Invalidate(const nsRect &aRect, PRBool aIsSynchronous)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mView || !mVisible)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
NSRect r;
|
|
|
|
GeckoRectToNSRect(aRect, r);
|
|
|
|
|
|
|
|
if (aIsSynchronous) {
|
|
|
|
[mView displayRect:r];
|
|
|
|
}
|
|
|
|
else if ([NSView focusView]) {
|
|
|
|
// if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
|
|
|
|
// don't lose it.
|
|
|
|
[mView setNeedsPendingDisplayInRect:r];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
[mView setNeedsDisplayInRect:r];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Validate the widget
|
|
|
|
NS_IMETHODIMP nsChildView::Validate()
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
[mView setNeedsDisplay:NO];
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Invalidate this component's visible area
|
|
|
|
NS_IMETHODIMP nsChildView::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mView || !mVisible)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// FIXME rewrite to use a Cocoa region when nsIRegion isn't a QD Region
|
|
|
|
NSRect r;
|
|
|
|
nsRect bounds;
|
2007-07-08 00:08:04 -07:00
|
|
|
nsIRegion* region = const_cast<nsIRegion*>(aRegion); // ugh. this method should be const
|
2008-01-15 15:11:55 -08:00
|
|
|
region->GetBoundingBox(&bounds.x, &bounds.y, &bounds.width, &bounds.height);
|
2007-03-22 10:30:00 -07:00
|
|
|
GeckoRectToNSRect(bounds, r);
|
|
|
|
|
2008-01-15 15:11:55 -08:00
|
|
|
if (aIsSynchronous)
|
2007-03-22 10:30:00 -07:00
|
|
|
[mView displayRect:r];
|
|
|
|
else
|
|
|
|
[mView setNeedsDisplayInRect:r];
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline PRUint16 COLOR8TOCOLOR16(PRUint8 color8)
|
|
|
|
{
|
|
|
|
// return (color8 == 0xFF ? 0xFFFF : (color8 << 8));
|
|
|
|
return (color8 << 8) | color8; /* (color8 * 257) == (color8 * 0x0101) */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Dummy impl, meant to be overridden
|
|
|
|
PRBool
|
|
|
|
nsChildView::OnPaint(nsPaintEvent &event)
|
|
|
|
{
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// this is handled for us by UpdateWidget
|
|
|
|
NS_IMETHODIMP nsChildView::Update()
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Update means "Flush any pending changes right now." It does *not* mean
|
|
|
|
// repaint the world. :) -- dwh
|
|
|
|
[mView displayIfNeeded];
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
// Scroll the bits of a view and its children
|
|
|
|
// FIXME: I'm sure the invalidating can be optimized, just no time now.
|
|
|
|
NS_IMETHODIMP nsChildView::Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
BOOL viewWasDirty = NO;
|
|
|
|
if (mVisible) {
|
|
|
|
viewWasDirty = [mView needsDisplay];
|
|
|
|
|
|
|
|
NSSize scrollVector = {aDx,aDy};
|
|
|
|
[mView scrollRect: [mView visibleRect] by:scrollVector];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scroll the children (even if the widget is not visible)
|
|
|
|
for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
|
|
|
|
// We use resize rather than move since it gives us control
|
|
|
|
// over repainting. We can scroll like a bat out of hell
|
|
|
|
// by not wasting time invalidating the widgets, since it's
|
|
|
|
// completely unnecessary to do so.
|
|
|
|
nsRect bounds;
|
|
|
|
kid->GetBounds(bounds);
|
|
|
|
kid->Resize(bounds.x + aDx, bounds.y + aDy, bounds.width, bounds.height, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
2008-01-15 15:11:55 -08:00
|
|
|
if (mOnDestroyCalled)
|
|
|
|
return NS_OK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mVisible) {
|
|
|
|
if (viewWasDirty) {
|
|
|
|
[mView setNeedsDisplay:YES];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
NSRect frame = [mView visibleRect];
|
|
|
|
NSRect horizInvalid = frame;
|
|
|
|
NSRect vertInvalid = frame;
|
|
|
|
|
|
|
|
if (aDx != 0) {
|
|
|
|
horizInvalid.size.width = abs(aDx);
|
|
|
|
if (aDx < 0)
|
|
|
|
horizInvalid.origin.x = frame.origin.x + frame.size.width + aDx;
|
|
|
|
[mView setNeedsDisplayInRect: horizInvalid];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aDy != 0) {
|
|
|
|
vertInvalid.size.height = abs(aDy);
|
|
|
|
if (aDy < 0)
|
|
|
|
vertInvalid.origin.y = frame.origin.y + frame.size.height + aDy;
|
|
|
|
[mView setNeedsDisplayInRect: vertInvalid];
|
|
|
|
}
|
|
|
|
|
|
|
|
// We also need to check for any ChildViews which overlap this widget
|
|
|
|
// but are not descendent widgets. If there are any, we need to
|
|
|
|
// invalidate the area of this view that these ChildViews will have been
|
|
|
|
// blitted into, since these widgets aren't supposed to scroll with
|
|
|
|
// this widget.
|
|
|
|
|
|
|
|
// To do this, start at the root Gecko NSView, and walk down along
|
|
|
|
// our ancestor view chain, looking at all the subviews in each level
|
|
|
|
// of the hierarchy. If we find a non-ancestor view that overlaps
|
|
|
|
// this view, invalidate the area around it.
|
|
|
|
|
|
|
|
// We need to convert all rects to a common ancestor view to intersect
|
|
|
|
// them, since a view's frame is in the coordinate space of its parent.
|
|
|
|
// Use mParentView as the frame of reference.
|
|
|
|
NSRect selfFrame = [mParentView convertRect:[mView frame] fromView:[mView superview]];
|
|
|
|
NSView* view = mParentView;
|
|
|
|
BOOL selfLevel = NO;
|
|
|
|
|
|
|
|
while (!selfLevel) {
|
|
|
|
NSView* nextAncestorView = nil;
|
|
|
|
NSArray* subviews = [view subviews];
|
|
|
|
for (unsigned int i = 0; i < [subviews count]; ++i) {
|
|
|
|
NSView* subView = [subviews objectAtIndex: i];
|
|
|
|
if (subView == mView)
|
|
|
|
selfLevel = YES;
|
|
|
|
else if ([mView isDescendantOf:subView])
|
|
|
|
nextAncestorView = subView;
|
|
|
|
else {
|
|
|
|
NSRect intersectArea = NSIntersectionRect([mParentView convertRect:[subView frame] fromView:[subView superview]], selfFrame);
|
|
|
|
if (!NSIsEmptyRect(intersectArea)) {
|
|
|
|
NSPoint origin = [mView convertPoint:intersectArea.origin fromView:mParentView];
|
|
|
|
|
|
|
|
if (aDy != 0) {
|
|
|
|
vertInvalid.origin.x = origin.x;
|
|
|
|
if (aDy < 0) // scrolled down, invalidate above
|
|
|
|
vertInvalid.origin.y = origin.y + aDy;
|
|
|
|
else // invalidate below
|
|
|
|
vertInvalid.origin.y = origin.y + intersectArea.size.height;
|
|
|
|
vertInvalid.size.width = intersectArea.size.width;
|
|
|
|
[mView setNeedsDisplayInRect: vertInvalid];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aDx != 0) {
|
|
|
|
horizInvalid.origin.y = origin.y;
|
|
|
|
if (aDx < 0) // scrolled right, invalidate to the left
|
|
|
|
horizInvalid.origin.x = origin.x + aDx;
|
|
|
|
else // invalidate to the right
|
|
|
|
horizInvalid.origin.x = origin.x + intersectArea.size.width;
|
|
|
|
horizInvalid.size.height = intersectArea.size.height;
|
|
|
|
[mView setNeedsDisplayInRect: horizInvalid];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
view = nextAncestorView;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is an evil hack that doesn't always work.
|
|
|
|
//
|
|
|
|
// Drawing plugins in a Cocoa environment is tricky, because the
|
|
|
|
// plugins are living in a Carbon WindowRef/BeginUpdate/EndUpdate
|
|
|
|
// world, and Cocoa has its own notion of dirty rectangles. Throw
|
|
|
|
// Quartz Extreme and QuickTime into the mix, and things get bad.
|
|
|
|
//
|
|
|
|
// This code is working around a cosmetic issue seen when Quartz Extreme
|
|
|
|
// is active, and you're scrolling a page with a QuickTime plugin; areas
|
|
|
|
// outside the plugin fail to scroll properly. This [display] ensures that
|
|
|
|
// the view is properly drawn before the next Scroll call.
|
|
|
|
//
|
|
|
|
// The time this doesn't work is when you're scrolling a page containing
|
|
|
|
// an iframe which in turn contains a plugin.
|
|
|
|
//
|
|
|
|
// This is turned off because it makes scrolling pages with plugins slow.
|
|
|
|
//
|
|
|
|
//if ([mView childViewHasPlugin])
|
|
|
|
// [mView display];
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-19 15:15:48 -07:00
|
|
|
// Invokes callback and ProcessEvent methods on Event Listener object
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP nsChildView::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
|
|
|
|
{
|
2008-03-12 15:44:45 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
debug_DumpEvent(stdout, event->widget, event, nsCAutoString("something"), 0);
|
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
aStatus = nsEventStatus_eIgnore;
|
|
|
|
|
2008-02-06 20:04:58 -08:00
|
|
|
nsCOMPtr<nsIWidget> kungFuDeathGrip(mParentWidget ? mParentWidget : this);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mParentWidget) {
|
|
|
|
nsWindowType type;
|
|
|
|
mParentWidget->GetWindowType(type);
|
|
|
|
if (type == eWindowType_popup) {
|
2007-08-02 11:43:31 -07:00
|
|
|
// use the parent popup's widget if there is no view
|
|
|
|
void* clientData = nsnull;
|
|
|
|
if (event->widget)
|
|
|
|
event->widget->GetClientData(clientData);
|
2008-01-15 15:11:55 -08:00
|
|
|
if (!clientData)
|
2007-08-02 11:43:31 -07:00
|
|
|
event->widget = mParentWidget;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2007-09-20 01:56:42 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mEventCallback)
|
|
|
|
aStatus = (*mEventCallback)(event);
|
2007-09-20 01:56:42 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// dispatch to event listener if event was not consumed
|
|
|
|
if (mEventListener && aStatus != nsEventStatus_eConsumeNoDefault)
|
2007-09-19 15:15:48 -07:00
|
|
|
aStatus = mEventListener->ProcessEvent(*event);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PRBool nsChildView::DispatchWindowEvent(nsGUIEvent &event)
|
|
|
|
{
|
|
|
|
nsEventStatus status;
|
|
|
|
DispatchEvent(&event, status);
|
|
|
|
return ConvertStatus(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Deal with all sort of mouse event
|
|
|
|
PRBool nsChildView::DispatchMouseEvent(nsMouseEvent &aEvent)
|
|
|
|
{
|
|
|
|
PRBool result = PR_FALSE;
|
|
|
|
|
|
|
|
// call the event callback
|
|
|
|
if (mEventCallback)
|
|
|
|
return DispatchWindowEvent(aEvent);
|
|
|
|
|
|
|
|
if (mMouseListener) {
|
2008-01-15 15:11:55 -08:00
|
|
|
nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
|
2007-03-22 10:30:00 -07:00
|
|
|
switch (aEvent.message) {
|
|
|
|
case NS_MOUSE_MOVE:
|
|
|
|
result = ConvertStatus(mMouseListener->MouseMoved(aEvent));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NS_MOUSE_BUTTON_DOWN:
|
|
|
|
result = ConvertStatus(mMouseListener->MousePressed(aEvent));
|
|
|
|
break;
|
|
|
|
|
2008-01-15 15:11:55 -08:00
|
|
|
case NS_MOUSE_BUTTON_UP: {
|
2007-03-22 10:30:00 -07:00
|
|
|
result = ConvertStatus(mMouseListener->MouseReleased(aEvent));
|
2008-01-15 15:11:55 -08:00
|
|
|
if (mMouseListener)
|
|
|
|
result = ConvertStatus(mMouseListener->MouseClicked(aEvent));
|
2007-03-22 10:30:00 -07:00
|
|
|
break;
|
2008-01-15 15:11:55 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
} // switch
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
PRBool nsChildView::ReportDestroyEvent()
|
|
|
|
{
|
|
|
|
nsGUIEvent event(PR_TRUE, NS_DESTROY, this);
|
|
|
|
event.time = PR_IntervalNow();
|
2007-07-18 17:56:33 -07:00
|
|
|
return DispatchWindowEvent(event);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PRBool nsChildView::ReportMoveEvent()
|
|
|
|
{
|
|
|
|
nsGUIEvent moveEvent(PR_TRUE, NS_MOVE, this);
|
2007-07-18 17:56:33 -07:00
|
|
|
moveEvent.refPoint.x = mBounds.x;
|
|
|
|
moveEvent.refPoint.y = mBounds.y;
|
|
|
|
moveEvent.time = PR_IntervalNow();
|
|
|
|
return DispatchWindowEvent(moveEvent);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PRBool nsChildView::ReportSizeEvent()
|
|
|
|
{
|
|
|
|
nsSizeEvent sizeEvent(PR_TRUE, NS_SIZE, this);
|
|
|
|
sizeEvent.time = PR_IntervalNow();
|
|
|
|
sizeEvent.windowSize = &mBounds;
|
|
|
|
sizeEvent.mWinWidth = mBounds.width;
|
|
|
|
sizeEvent.mWinHeight = mBounds.height;
|
2007-07-18 17:56:33 -07:00
|
|
|
return DispatchWindowEvent(sizeEvent);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
/* Calculate the x and y offsets for this particular widget
|
|
|
|
* @update ps 09/22/98
|
|
|
|
* @param aX -- x offset amount
|
|
|
|
* @param aY -- y offset amount
|
|
|
|
* @return NOTHING
|
|
|
|
*/
|
|
|
|
NS_IMETHODIMP nsChildView::CalcOffset(PRInt32 &aX,PRInt32 &aY)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
aX = aY = 0;
|
|
|
|
NSRect bounds = {{0, 0}, {0, 0}};
|
|
|
|
bounds = [mView convertRect:bounds toView:nil];
|
2007-07-08 00:08:04 -07:00
|
|
|
aX += static_cast<PRInt32>(bounds.origin.x);
|
|
|
|
aY += static_cast<PRInt32>(bounds.origin.y);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Find if a point in local coordinates is inside this object
|
|
|
|
PRBool nsChildView::PointInWidget(Point aThePoint)
|
|
|
|
{
|
|
|
|
// get the origin in local coordinates
|
|
|
|
nsPoint widgetOrigin(0, 0);
|
|
|
|
LocalToWindowCoordinate(widgetOrigin);
|
|
|
|
|
|
|
|
// get rectangle relatively to the parent
|
|
|
|
nsRect widgetRect;
|
|
|
|
GetBounds(widgetRect);
|
|
|
|
|
|
|
|
// convert the topLeft corner to local coordinates
|
|
|
|
widgetRect.MoveBy(widgetOrigin.x, widgetOrigin.y);
|
|
|
|
|
|
|
|
// finally tell whether it's a hit
|
|
|
|
return widgetRect.Contains(aThePoint.h, aThePoint.v);
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
// Convert the given rect to global coordinates.
|
|
|
|
// @param aLocalRect -- rect in local coordinates of this widget
|
|
|
|
// @param aGlobalRect -- |aLocalRect| in global coordinates
|
|
|
|
NS_IMETHODIMP nsChildView::WidgetToScreen(const nsRect& aLocalRect, nsRect& aGlobalRect)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NSRect temp;
|
|
|
|
GeckoRectToNSRect(aLocalRect, temp);
|
|
|
|
|
|
|
|
// 1. First translate this rect into window coords. The returned rect is always in
|
|
|
|
// bottom-left coordinates.
|
|
|
|
//
|
|
|
|
// NOTE: convertRect:toView:nil doesn't care if |mView| is a flipped view (with
|
|
|
|
// top-left coords) and so assumes that our passed-in rect's origin is in
|
|
|
|
// bottom-left coordinates. We adjust this further down, by subtracting
|
|
|
|
// the final screen rect's origin by the rect's height, to get the origo
|
|
|
|
// where we want it.
|
|
|
|
temp = [mView convertRect:temp toView:nil];
|
|
|
|
|
|
|
|
// 2. We turn the window-coord rect's origin into screen (still bottom-left) coords.
|
|
|
|
temp.origin = [[mView nativeWindow] convertBaseToScreen:temp.origin];
|
|
|
|
|
|
|
|
// 3. Since we're dealing in bottom-left coords, we need to make it top-left coords
|
|
|
|
// before we pass it back to Gecko.
|
|
|
|
FlipCocoaScreenCoordinate(temp.origin);
|
|
|
|
|
|
|
|
// 4. If this is rect has a size (and is not simply a point), it is important to account
|
|
|
|
// for the fact that convertRect:toView:nil thought our passed-in point was in bottom-left
|
|
|
|
// coords in step #1. Thus, we subtract the rect's height, to get the top-left rect's origin
|
|
|
|
// where we want it.
|
|
|
|
temp.origin.y -= temp.size.height;
|
|
|
|
|
|
|
|
NSRectToGeckoRect(temp, aGlobalRect);
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Convert the given rect to local coordinates.
|
|
|
|
// @param aGlobalRect -- rect in screen coordinates
|
|
|
|
// @param aLocalRect -- |aGlobalRect| in coordinates of this widget
|
|
|
|
NS_IMETHODIMP nsChildView::ScreenToWidget(const nsRect& aGlobalRect, nsRect& aLocalRect)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NSRect temp;
|
|
|
|
GeckoRectToNSRect(aGlobalRect, temp);
|
|
|
|
FlipCocoaScreenCoordinate(temp.origin);
|
|
|
|
|
|
|
|
temp.origin = [[mView nativeWindow] convertScreenToBase:temp.origin]; // convert to screen coords
|
|
|
|
temp = [mView convertRect:temp fromView:nil]; // convert to window coords
|
|
|
|
|
|
|
|
NSRectToGeckoRect(temp, aLocalRect);
|
|
|
|
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Convert the coordinates to some device coordinates so GFX can draw.
|
|
|
|
void nsChildView::ConvertToDeviceCoordinates(nscoord &aX, nscoord &aY)
|
|
|
|
{
|
|
|
|
PRInt32 offX = 0, offY = 0;
|
|
|
|
this->CalcOffset(offX,offY);
|
|
|
|
|
|
|
|
aX += offX;
|
|
|
|
aY += offY;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::CaptureRollupEvents(nsIRollupListener * aListener,
|
|
|
|
PRBool aDoCapture,
|
|
|
|
PRBool aConsumeRollupEvent)
|
|
|
|
{
|
|
|
|
// this never gets called, only top-level windows can be rollup widgets
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::SetTitle(const nsAString& title)
|
|
|
|
{
|
2007-07-18 17:56:33 -07:00
|
|
|
// child views don't have titles
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsChildView::GetAttention(PRInt32 aCycleCount)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
[NSApp requestUserAttention:NSInformationalRequest];
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
// Force Input Method Editor to commit the uncommited input
|
2008-07-13 19:56:18 -07:00
|
|
|
// Note that this and other IME methods don't necessarily
|
2007-03-22 10:30:00 -07:00
|
|
|
// get called on the same ChildView that input is going through.
|
|
|
|
NS_IMETHODIMP nsChildView::ResetInputState()
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_IME
|
|
|
|
NSLog(@"**** ResetInputState");
|
|
|
|
#endif
|
|
|
|
|
2007-04-15 06:43:55 -07:00
|
|
|
nsTSMManager::CommitIME();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 'open' means that it can take non-ASCII chars
|
|
|
|
NS_IMETHODIMP nsChildView::SetIMEOpenState(PRBool aState)
|
|
|
|
{
|
2007-04-15 06:43:55 -07:00
|
|
|
#ifdef DEBUG_IME
|
|
|
|
NSLog(@"**** SetIMEOpenState aState = %d", aState);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsTSMManager::SetIMEOpenState(aState);
|
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 'open' means that it can take non-ASCII chars
|
|
|
|
NS_IMETHODIMP nsChildView::GetIMEOpenState(PRBool* aState)
|
|
|
|
{
|
2007-04-15 06:43:55 -07:00
|
|
|
#ifdef DEBUG_IME
|
|
|
|
NSLog(@"**** GetIMEOpenState");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
*aState = nsTSMManager::GetIMEOpenState();
|
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-15 06:43:55 -07:00
|
|
|
NS_IMETHODIMP nsChildView::SetIMEEnabled(PRUint32 aState)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-04-15 06:43:55 -07:00
|
|
|
#ifdef DEBUG_IME
|
|
|
|
NSLog(@"**** SetIMEEnabled aState = %d", aState);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch (aState) {
|
2008-07-13 19:56:18 -07:00
|
|
|
case nsIWidget::IME_STATUS_ENABLED:
|
2007-04-15 06:43:55 -07:00
|
|
|
nsTSMManager::SetRomanKeyboardsOnly(PR_FALSE);
|
|
|
|
nsTSMManager::EnableIME(PR_TRUE);
|
|
|
|
break;
|
2008-07-13 19:56:18 -07:00
|
|
|
case nsIWidget::IME_STATUS_DISABLED:
|
2007-04-15 06:43:55 -07:00
|
|
|
nsTSMManager::SetRomanKeyboardsOnly(PR_FALSE);
|
|
|
|
nsTSMManager::EnableIME(PR_FALSE);
|
|
|
|
break;
|
2008-07-13 19:56:18 -07:00
|
|
|
case nsIWidget::IME_STATUS_PASSWORD:
|
2007-04-15 06:43:55 -07:00
|
|
|
nsTSMManager::SetRomanKeyboardsOnly(PR_TRUE);
|
|
|
|
nsTSMManager::EnableIME(PR_FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NS_ERROR("not implemented!");
|
|
|
|
}
|
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-15 06:43:55 -07:00
|
|
|
NS_IMETHODIMP nsChildView::GetIMEEnabled(PRUint32* aState)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-04-15 06:43:55 -07:00
|
|
|
#ifdef DEBUG_IME
|
|
|
|
NSLog(@"**** GetIMEEnabled");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (nsTSMManager::IsIMEEnabled())
|
2008-07-13 19:56:18 -07:00
|
|
|
*aState = nsIWidget::IME_STATUS_ENABLED;
|
2007-04-15 06:43:55 -07:00
|
|
|
else if (nsTSMManager::IsRomanKeyboardsOnly())
|
2008-07-13 19:56:18 -07:00
|
|
|
*aState = nsIWidget::IME_STATUS_PASSWORD;
|
2007-04-15 06:43:55 -07:00
|
|
|
else
|
2008-07-13 19:56:18 -07:00
|
|
|
*aState = nsIWidget::IME_STATUS_DISABLED;
|
2007-04-15 06:43:55 -07:00
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Destruct and don't commit the IME composition string.
|
|
|
|
NS_IMETHODIMP nsChildView::CancelIMEComposition()
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_IME
|
|
|
|
NSLog(@"**** CancelIMEComposition");
|
|
|
|
#endif
|
|
|
|
|
2007-04-15 06:43:55 -07:00
|
|
|
nsTSMManager::CancelIME();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-16 12:19:46 -07:00
|
|
|
NS_IMETHODIMP nsChildView::GetToggledKeyState(PRUint32 aKeyCode,
|
|
|
|
PRBool* aLEDState)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-06-16 12:19:46 -07:00
|
|
|
#ifdef DEBUG_IME
|
|
|
|
NSLog(@"**** GetToggledKeyState");
|
|
|
|
#endif
|
|
|
|
NS_ENSURE_ARG_POINTER(aLEDState);
|
|
|
|
PRUint32 key;
|
|
|
|
switch (aKeyCode) {
|
|
|
|
case NS_VK_CAPS_LOCK:
|
|
|
|
key = alphaLock;
|
|
|
|
break;
|
|
|
|
case NS_VK_NUM_LOCK:
|
|
|
|
key = kEventKeyModifierNumLockMask;
|
|
|
|
break;
|
|
|
|
// Mac doesn't support SCROLL_LOCK state.
|
|
|
|
default:
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
2008-03-04 09:58:52 -08:00
|
|
|
PRUint32 modifierFlags = ::GetCurrentEventKeyModifiers();
|
2007-06-16 12:19:46 -07:00
|
|
|
*aLEDState = (modifierFlags & key) != 0;
|
|
|
|
return NS_OK;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-06-16 12:19:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
gfxASurface*
|
|
|
|
nsChildView::GetThebesSurface()
|
|
|
|
{
|
|
|
|
if (!mTempThebesSurface) {
|
|
|
|
mTempThebesSurface = new gfxQuartzSurface(gfxSize(1, 1), gfxASurface::ImageFormatARGB32);
|
|
|
|
}
|
|
|
|
|
2007-05-16 23:52:35 -07:00
|
|
|
return mTempThebesSurface;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-27 09:01:32 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsChildView::BeginSecureKeyboardInput()
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-09-27 09:01:32 -07:00
|
|
|
nsresult rv = nsBaseWidget::BeginSecureKeyboardInput();
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
::EnableSecureEventInput();
|
|
|
|
return rv;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-09-27 09:01:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsChildView::EndSecureKeyboardInput()
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
|
2007-09-27 09:01:32 -07:00
|
|
|
nsresult rv = nsBaseWidget::EndSecureKeyboardInput();
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
::DisableSecureEventInput();
|
|
|
|
return rv;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
2007-09-27 09:01:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
void
|
|
|
|
nsChildView::GetDocumentAccessible(nsIAccessible** aAccessible)
|
|
|
|
{
|
|
|
|
*aAccessible = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAccessible> accessible = do_QueryReferent(mAccessible);
|
|
|
|
if (!mAccessible) {
|
|
|
|
// need to fetch the accessible anew, because it has gone away.
|
|
|
|
nsEventStatus status;
|
|
|
|
nsAccessibleEvent event(PR_TRUE, NS_GETACCESSIBLE, this);
|
|
|
|
DispatchEvent(&event, status);
|
|
|
|
|
|
|
|
// cache the accessible in our weak ptr
|
|
|
|
mAccessible = do_GetWeakReference(event.accessible);
|
|
|
|
|
|
|
|
// now try again
|
|
|
|
accessible = do_QueryReferent(mAccessible);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IF_ADDREF(*aAccessible = accessible.get());
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
@implementation ChildView
|
|
|
|
|
|
|
|
|
|
|
|
// globalDragPboard is non-null during native drag sessions that did not originate
|
|
|
|
// in our native NSView (it is set in |draggingEntered:|). It is unset when the
|
|
|
|
// drag session ends for this view, either with the mouse exiting or when a drop
|
|
|
|
// occurs in this view.
|
|
|
|
NSPasteboard* globalDragPboard = nil;
|
|
|
|
|
|
|
|
|
2007-09-23 17:01:04 -07:00
|
|
|
// gLastDragView and gLastDragEvent are only non-null during calls to |mouseDragged:|
|
2007-03-22 10:30:00 -07:00
|
|
|
// in our native NSView. They are used to communicate information to the drag service
|
|
|
|
// during drag invocation (starting a drag in from the view). All drag service drag
|
|
|
|
// invocations happen only while these two global variables are non-null, while |mouseDragged:|
|
|
|
|
// is on the stack.
|
2007-09-23 17:01:04 -07:00
|
|
|
NSView* gLastDragView = nil;
|
|
|
|
NSEvent* gLastDragEvent = nil;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
|
2007-10-09 11:46:30 -07:00
|
|
|
// initWithFrame:geckoChild:
|
|
|
|
- (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if ((self = [super initWithFrame:inFrame])) {
|
2007-10-02 17:52:19 -07:00
|
|
|
mWindow = nil;
|
2007-03-22 10:30:00 -07:00
|
|
|
mGeckoChild = inChild;
|
|
|
|
mIsPluginView = NO;
|
2008-02-28 21:47:41 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mCurKeyEvent = nil;
|
2007-11-26 10:09:38 -08:00
|
|
|
mKeyDownHandled = PR_FALSE;
|
2008-03-13 18:08:04 -07:00
|
|
|
mKeyPressHandled = NO;
|
2008-02-28 21:47:41 -08:00
|
|
|
mKeyPressSent = NO;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// initialization for NSTextInput
|
|
|
|
mMarkedRange.location = NSNotFound;
|
|
|
|
mMarkedRange.length = 0;
|
2008-02-28 21:47:41 -08:00
|
|
|
|
2008-04-06 16:52:05 -07:00
|
|
|
mLastMouseDownEvent = nil;
|
2007-03-22 10:30:00 -07:00
|
|
|
mDragService = nsnull;
|
2008-06-30 09:30:22 -07:00
|
|
|
|
|
|
|
mPluginTSMDoc = nil;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// register for things we'll take from other applications
|
2007-07-16 19:24:05 -07:00
|
|
|
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
|
2007-03-22 10:30:00 -07:00
|
|
|
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
|
|
|
|
NSStringPboardType,
|
|
|
|
NSURLPboardType,
|
2007-07-16 19:24:05 -07:00
|
|
|
NSFilesPromisePboardType,
|
2007-05-28 22:06:12 -07:00
|
|
|
kWildcardPboardType,
|
2007-09-24 15:41:10 -07:00
|
|
|
kCorePboardType_url,
|
2007-09-27 18:50:09 -07:00
|
|
|
kCorePboardType_urld,
|
2007-09-24 15:41:10 -07:00
|
|
|
kCorePboardType_urln,
|
2007-03-22 10:30:00 -07:00
|
|
|
nil]];
|
|
|
|
|
|
|
|
return self;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)dealloc
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
[mPendingDirtyRects release];
|
2008-04-06 16:52:05 -07:00
|
|
|
[mLastMouseDownEvent release];
|
2008-06-30 09:30:22 -07:00
|
|
|
if (mPluginTSMDoc)
|
|
|
|
::DeleteTSMDocument(mPluginTSMDoc);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (sLastViewEntered == self)
|
|
|
|
sLastViewEntered = nil;
|
2007-09-17 15:55:20 -07:00
|
|
|
|
|
|
|
[super dealloc];
|
|
|
|
|
|
|
|
// This sets the current port to _savePort.
|
|
|
|
// todo: Only do if a Quickdraw plugin is present in the hierarchy!
|
|
|
|
::SetPort(NULL);
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)widgetDestroyed
|
|
|
|
{
|
2008-07-14 07:38:49 -07:00
|
|
|
nsTSMManager::OnDestroyView(self);
|
2007-03-22 10:30:00 -07:00
|
|
|
mGeckoChild = nsnull;
|
2007-10-10 05:19:47 -07:00
|
|
|
// Just in case we're destroyed abruptly and missed the draggingExited
|
|
|
|
// or performDragOperation message.
|
|
|
|
NS_IF_RELEASE(mDragService);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// mozView method, return our gecko child view widget. Note this does not AddRef.
|
|
|
|
- (nsIWidget*) widget
|
|
|
|
{
|
2007-07-08 00:08:04 -07:00
|
|
|
return static_cast<nsIWidget*>(mGeckoChild);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// mozView method, get the window that this view is associated with
|
|
|
|
- (NSWindow*)nativeWindow
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NSWindow* currWin = [self window];
|
|
|
|
if (currWin)
|
|
|
|
return currWin;
|
|
|
|
else
|
|
|
|
return mWindow;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// mozView method, set the NSWindow that this view is associated with (even when
|
|
|
|
// not in the view hierarchy).
|
|
|
|
- (void)setNativeWindow:(NSWindow*)aWindow
|
|
|
|
{
|
|
|
|
mWindow = aWindow;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)setNeedsPendingDisplay
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mPendingFullDisplay = YES;
|
|
|
|
[self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)setNeedsPendingDisplayInRect:(NSRect)invalidRect
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mPendingDirtyRects)
|
|
|
|
mPendingDirtyRects = [[NSMutableArray alloc] initWithCapacity:1];
|
|
|
|
[mPendingDirtyRects addObject:[NSValue valueWithRect:invalidRect]];
|
|
|
|
[self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Clears the queue of any pending invalides
|
|
|
|
- (void)processPendingRedraws
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mPendingFullDisplay) {
|
|
|
|
[self setNeedsDisplay:YES];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
unsigned int count = [mPendingDirtyRects count];
|
|
|
|
for (unsigned int i = 0; i < count; ++i) {
|
|
|
|
[self setNeedsDisplayInRect:[[mPendingDirtyRects objectAtIndex:i] rectValue]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mPendingFullDisplay = NO;
|
|
|
|
[mPendingDirtyRects release];
|
|
|
|
mPendingDirtyRects = nil;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSString*)description
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return [NSString stringWithFormat:@"ChildView %p, gecko child %p, frame %@", self, mGeckoChild, NSStringFromRect([self frame])];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Find the nearest scrollable view for this ChildView
|
|
|
|
// (recall that views are not refcounted)
|
|
|
|
- (nsIScrollableView*) getScrollableView
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIScrollableView* scrollableView = nsnull;
|
|
|
|
|
|
|
|
ChildView* currView = self;
|
|
|
|
// we have to loop up through superviews in case the view that received the
|
|
|
|
// mouseDown is in fact a plugin view with no scrollbars
|
|
|
|
while (currView) {
|
|
|
|
// This is a hack I learned in nsView::GetViewFor(nsIWidget* aWidget)
|
|
|
|
// that I'm not sure is kosher. If anyone knows a better way to get
|
|
|
|
// the view for a widget, I'd love to hear it. --Nathan
|
|
|
|
|
|
|
|
void* clientData;
|
|
|
|
[currView widget]->GetClientData(clientData);
|
|
|
|
|
|
|
|
nsISupports* data = (nsISupports*)clientData;
|
|
|
|
nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(data));
|
|
|
|
if (req) {
|
|
|
|
req->GetInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollableView);
|
|
|
|
if (scrollableView)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([[currView superview] isMemberOfClass:[ChildView class]])
|
|
|
|
currView = (ChildView*)[currView superview];
|
|
|
|
else
|
|
|
|
currView = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return scrollableView;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// set the closed hand cursor and record the starting scroll positions
|
|
|
|
- (void) startHandScroll:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mHandScrollStartMouseLoc = [[self window] convertBaseToScreen:[theEvent locationInWindow]];
|
|
|
|
|
|
|
|
nsIScrollableView* aScrollableView = [self getScrollableView];
|
|
|
|
|
|
|
|
// if we succeeded in getting aScrollableView
|
|
|
|
if (aScrollableView) {
|
|
|
|
aScrollableView->GetScrollPosition(mHandScrollStartScrollX, mHandScrollStartScrollY);
|
|
|
|
mGeckoChild->SetCursor(eCursor_grabbing);
|
|
|
|
mInHandScroll = TRUE;
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// update the scroll position based on the new mouse coordinates
|
|
|
|
- (void) updateHandScroll:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIScrollableView* aScrollableView = [self getScrollableView];
|
|
|
|
if (!aScrollableView)
|
|
|
|
return;
|
2007-06-15 15:34:48 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NSPoint newMouseLoc = [[self window] convertBaseToScreen:[theEvent locationInWindow]];
|
|
|
|
|
|
|
|
PRInt32 deltaX = (PRInt32)(mHandScrollStartMouseLoc.x - newMouseLoc.x);
|
|
|
|
PRInt32 deltaY = (PRInt32)(newMouseLoc.y - mHandScrollStartMouseLoc.y);
|
|
|
|
|
|
|
|
// convert to the nsIView coordinates
|
|
|
|
PRInt32 p2a = mGeckoChild->GetDeviceContext()->AppUnitsPerDevPixel();
|
|
|
|
nscoord newX = mHandScrollStartScrollX + NSIntPixelsToAppUnits(deltaX, p2a);
|
|
|
|
nscoord newY = mHandScrollStartScrollY + NSIntPixelsToAppUnits(deltaY, p2a);
|
|
|
|
aScrollableView->ScrollTo(newX, newY, NS_VMREFRESH_IMMEDIATE);
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Return true if the correct modifiers are pressed to perform hand scrolling.
|
|
|
|
+ (BOOL) areHandScrollModifiers:(unsigned int)modifiers
|
|
|
|
{
|
|
|
|
// The command and option key should be held down. Ignore capsLock by
|
|
|
|
// setting it explicitly to match.
|
|
|
|
modifiers |= NSAlphaShiftKeyMask;
|
|
|
|
return (modifiers & NSDeviceIndependentModifierFlagsMask) ==
|
|
|
|
(NSAlphaShiftKeyMask | NSCommandKeyMask | NSAlternateKeyMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If the user is pressing the hand scroll modifiers, then set
|
|
|
|
// the hand scroll cursor.
|
|
|
|
- (void) setHandScrollCursor:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
BOOL inMouseView = NO;
|
|
|
|
|
|
|
|
// check to see if the user has hand scroll modifiers held down; if so,
|
|
|
|
// find out if the cursor is in an ChildView
|
|
|
|
if ([ChildView areHandScrollModifiers:[theEvent modifierFlags]]) {
|
|
|
|
NSPoint pointInWindow = [[self window] mouseLocationOutsideOfEventStream];
|
|
|
|
|
|
|
|
NSView* mouseView = [[[self window] contentView] hitTest:pointInWindow];
|
|
|
|
inMouseView = (mouseView != nil && [mouseView isMemberOfClass:[ChildView class]]);
|
|
|
|
}
|
|
|
|
if (inMouseView) {
|
|
|
|
mGeckoChild->SetCursor(eCursor_grab);
|
|
|
|
} else {
|
|
|
|
nsCursor cursor = mGeckoChild->GetCursor();
|
|
|
|
if (!mInHandScroll) {
|
|
|
|
if (cursor == eCursor_grab || cursor == eCursor_grabbing)
|
|
|
|
mGeckoChild->SetCursor(eCursor_standard);
|
|
|
|
}
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// reset the scroll flag and cursor
|
|
|
|
- (void) stopHandScroll:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mInHandScroll = FALSE;
|
|
|
|
[self setHandScrollCursor:theEvent];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// When smooth scrolling is turned on on panther, the parent of a scrollbar (which
|
|
|
|
// I guess they assume is a NSScrollView) gets called with this method. I have no
|
|
|
|
// idea what the correct return value is, but we have to have this otherwise the scrollbar
|
|
|
|
// will not continuously respond when the mouse is held down in the pageup/down area.
|
|
|
|
-(float)_destinationFloatValueForScroller:(id)scroller
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return [scroller floatValue];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0.0);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Override in order to keep our mouse enter/exit tracking rect in sync with
|
|
|
|
// the frame of the view
|
|
|
|
- (void)setFrame:(NSRect)frameRect
|
2008-02-20 15:47:05 -08:00
|
|
|
{
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
[super setFrame:frameRect];
|
|
|
|
if (mMouseEnterExitTag)
|
|
|
|
[self removeTrackingRect:mMouseEnterExitTag];
|
|
|
|
|
|
|
|
if ([self window])
|
|
|
|
mMouseEnterExitTag = [self addTrackingRect:[self bounds]
|
|
|
|
owner:self
|
|
|
|
userData:nil
|
|
|
|
assumeInside:[[self window] acceptsMouseMovedEvents]];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Make the origin of this view the topLeft corner (gecko origin) rather
|
|
|
|
// than the bottomLeft corner (standard cocoa origin).
|
|
|
|
- (BOOL)isFlipped
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-19 11:40:18 -08:00
|
|
|
- (void)setTransparent:(BOOL)transparent
|
|
|
|
{
|
|
|
|
mIsTransparent = transparent;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
- (BOOL)isOpaque
|
|
|
|
{
|
2007-12-19 11:40:18 -08:00
|
|
|
return !mIsTransparent;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-(void)setIsPluginView:(BOOL)aIsPlugin
|
|
|
|
{
|
|
|
|
mIsPluginView = aIsPlugin;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-26 18:07:57 -07:00
|
|
|
-(BOOL)isPluginView
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
return mIsPluginView;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (BOOL)childViewHasPlugin
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NSArray* subviews = [self subviews];
|
|
|
|
for (unsigned int i = 0; i < [subviews count]; i ++) {
|
|
|
|
id subview = [subviews objectAtIndex:i];
|
2007-03-26 18:07:57 -07:00
|
|
|
if ([subview respondsToSelector:@selector(isPluginView)] && [subview isPluginView])
|
2007-03-22 10:30:00 -07:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-02 16:01:32 -07:00
|
|
|
- (void)sendFocusEvent:(PRUint32)eventType
|
|
|
|
{
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
|
|
nsGUIEvent focusGuiEvent(PR_TRUE, eventType, mGeckoChild);
|
|
|
|
focusGuiEvent.time = PR_IntervalNow();
|
|
|
|
mGeckoChild->DispatchEvent(&focusGuiEvent, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// We accept key and mouse events, so don't keep passing them up the chain. Allow
|
|
|
|
// this to be a 'focussed' widget for event dispatch
|
|
|
|
- (BOOL)acceptsFirstResponder
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewWillMoveToWindow:(NSWindow *)newWindow
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mMouseEnterExitTag)
|
|
|
|
[self removeTrackingRect:mMouseEnterExitTag];
|
|
|
|
|
|
|
|
[super viewWillMoveToWindow:newWindow];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewDidMoveToWindow
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if ([self window])
|
|
|
|
mMouseEnterExitTag = [self addTrackingRect:[self bounds] owner:self
|
|
|
|
userData:nil assumeInside: [[self window]
|
|
|
|
acceptsMouseMovedEvents]];
|
|
|
|
|
|
|
|
[super viewDidMoveToWindow];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewWillStartLiveResize
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mGeckoChild && mIsPluginView)
|
|
|
|
mGeckoChild->LiveResizeStarted();
|
|
|
|
|
|
|
|
[super viewWillStartLiveResize];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewDidEndLiveResize
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mGeckoChild && mIsPluginView)
|
|
|
|
mGeckoChild->LiveResizeEnded();
|
|
|
|
|
|
|
|
[super viewDidEndLiveResize];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)scrollRect:(NSRect)aRect by:(NSSize)offset
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Update any pending dirty rects to reflect the new scroll position
|
|
|
|
if (mPendingDirtyRects) {
|
|
|
|
unsigned int count = [mPendingDirtyRects count];
|
|
|
|
for (unsigned int i = 0; i < count; ++i) {
|
|
|
|
NSRect oldRect = [[mPendingDirtyRects objectAtIndex:i] rectValue];
|
|
|
|
NSRect newRect = NSOffsetRect(oldRect, offset.width, offset.height);
|
|
|
|
[mPendingDirtyRects replaceObjectAtIndex:i
|
|
|
|
withObject:[NSValue valueWithRect:newRect]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
[super scrollRect:aRect by:offset];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (BOOL)mouseDownCanMoveWindow
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)lockFocus
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Set the current GrafPort to a "safe" port before calling [NSQuickDrawView lockFocus],
|
|
|
|
// so that the NSQuickDrawView stashes a pointer to this known-good port internally.
|
|
|
|
// It will set the port back to this port on destruction.
|
2007-09-17 15:55:20 -07:00
|
|
|
::SetPort(NULL); // todo: only do if a Quickdraw plugin is present in the hierarchy!
|
2007-03-22 10:30:00 -07:00
|
|
|
[super lockFocus];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The display system has told us that a portion of our view is dirty. Tell
|
|
|
|
// gecko to paint it
|
|
|
|
- (void)drawRect:(NSRect)aRect
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRBool isVisible;
|
|
|
|
if (!mGeckoChild || NS_FAILED(mGeckoChild->IsVisible(isVisible)) || !isVisible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
|
|
|
|
|
|
|
nsRect geckoBounds;
|
|
|
|
mGeckoChild->GetBounds(geckoBounds);
|
|
|
|
|
|
|
|
NSRect bounds = [self bounds];
|
|
|
|
nsRefPtr<gfxQuartzSurface> targetSurface =
|
2007-04-03 18:09:15 -07:00
|
|
|
new gfxQuartzSurface(cgContext, gfxSize(bounds.size.width, bounds.size.height));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#ifdef DEBUG_UPDATE
|
|
|
|
fprintf (stderr, "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n gecko bounds: [%d %d %d %d]\n",
|
|
|
|
self, mGeckoChild,
|
|
|
|
aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, cgContext,
|
|
|
|
geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height);
|
|
|
|
|
|
|
|
CGAffineTransform xform = CGContextGetCTM(cgContext);
|
|
|
|
fprintf (stderr, " xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsRefPtr<gfxContext> targetContext = new gfxContext(targetSurface);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIRenderingContext> rc;
|
|
|
|
mGeckoChild->GetDeviceContext()->CreateRenderingContextInstance(*getter_AddRefs(rc));
|
|
|
|
rc->Init(mGeckoChild->GetDeviceContext(), targetContext);
|
|
|
|
|
|
|
|
/* clip and build a region */
|
|
|
|
nsCOMPtr<nsIRegion> rgn(do_CreateInstance(kRegionCID));
|
|
|
|
if (rgn)
|
|
|
|
rgn->Init();
|
|
|
|
|
|
|
|
const NSRect *rects;
|
|
|
|
int count, i;
|
|
|
|
[self getRectsBeingDrawn:&rects count:&count];
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
const NSRect& r = rects[i];
|
|
|
|
|
|
|
|
// add to the region
|
|
|
|
if (rgn)
|
|
|
|
rgn->Union((PRInt32)r.origin.x, (PRInt32)r.origin.y, (PRInt32)r.size.width, (PRInt32)r.size.height);
|
|
|
|
|
|
|
|
// to the context for clipping
|
|
|
|
targetContext->Rectangle(gfxRect(r.origin.x, r.origin.y, r.size.width, r.size.height));
|
|
|
|
}
|
|
|
|
targetContext->Clip();
|
|
|
|
|
|
|
|
// bounding box of the dirty area
|
|
|
|
nsRect fullRect;
|
|
|
|
NSRectToGeckoRect(aRect, fullRect);
|
|
|
|
|
|
|
|
nsPaintEvent paintEvent(PR_TRUE, NS_PAINT, mGeckoChild);
|
|
|
|
paintEvent.renderingContext = rc;
|
|
|
|
paintEvent.rect = &fullRect;
|
|
|
|
paintEvent.region = rgn;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2007-03-22 10:30:00 -07:00
|
|
|
mGeckoChild->DispatchWindowEvent(paintEvent);
|
2008-01-15 15:11:55 -08:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
paintEvent.renderingContext = nsnull;
|
|
|
|
paintEvent.region = nsnull;
|
|
|
|
|
|
|
|
targetContext = nsnull;
|
|
|
|
targetSurface = nsnull;
|
|
|
|
|
|
|
|
// note that the cairo surface *MUST* be destroyed at this point,
|
|
|
|
// or bad things will happen (since we can't keep the cgContext around
|
|
|
|
// beyond this drawRect message handler)
|
|
|
|
|
|
|
|
#ifdef DEBUG_UPDATE
|
|
|
|
fprintf (stderr, " window coords: [%d %d %d %d]\n", fullRect.x, fullRect.y, fullRect.width, fullRect.height);
|
|
|
|
fprintf (stderr, "---- update done ----\n");
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
CGContextSetRGBStrokeColor (cgContext,
|
|
|
|
((((unsigned long)self) & 0xff)) / 255.0,
|
|
|
|
((((unsigned long)self) & 0xff00) >> 8) / 255.0,
|
|
|
|
((((unsigned long)self) & 0xff0000) >> 16) / 255.0,
|
|
|
|
0.5);
|
|
|
|
#endif
|
|
|
|
CGContextSetRGBStrokeColor (cgContext, 1, 0, 0, 0.8);
|
|
|
|
CGContextSetLineWidth (cgContext, 4.0);
|
|
|
|
CGContextStrokeRect (cgContext,
|
|
|
|
CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height));
|
|
|
|
#endif
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-17 17:07:36 -07:00
|
|
|
// Allows us to turn off setting up the clip region
|
2007-03-22 10:30:00 -07:00
|
|
|
// before each drawRect. We already clip within gecko.
|
|
|
|
- (BOOL)wantsDefaultClipping
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if USE_CLICK_HOLD_CONTEXTMENU
|
|
|
|
//
|
|
|
|
// -clickHoldCallback:
|
|
|
|
//
|
|
|
|
// called from a timer two seconds after a mouse down to see if we should display
|
|
|
|
// a context menu (click-hold). |anEvent| is the original mouseDown event. If we're
|
|
|
|
// still in that mouseDown by this time, put up the context menu, otherwise just
|
|
|
|
// fuhgeddaboutit. |anEvent| has been retained by the OS until after this callback
|
|
|
|
// fires so we're ok there.
|
|
|
|
//
|
|
|
|
// This code currently messes in a bunch of edge cases (bugs 234751, 232964, 232314)
|
|
|
|
// so removing it until we get it straightened out.
|
|
|
|
//
|
|
|
|
- (void)clickHoldCallback:(id)theEvent;
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if( theEvent == [NSApp currentEvent] ) {
|
|
|
|
// we're still in the middle of the same mousedown event here, activate
|
|
|
|
// click-hold context menu by triggering the right mouseDown action.
|
|
|
|
NSEvent* clickHoldEvent = [NSEvent mouseEventWithType:NSRightMouseDown
|
|
|
|
location:[theEvent locationInWindow]
|
|
|
|
modifierFlags:[theEvent modifierFlags]
|
|
|
|
timestamp:[theEvent timestamp]
|
|
|
|
windowNumber:[theEvent windowNumber]
|
|
|
|
context:[theEvent context]
|
|
|
|
eventNumber:[theEvent eventNumber]
|
|
|
|
clickCount:[theEvent clickCount]
|
|
|
|
pressure:[theEvent pressure]];
|
|
|
|
[self rightMouseDown:clickHoldEvent];
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2007-12-17 20:44:30 -08:00
|
|
|
// We sometimes need to reroute events when there is a rollup widget and the
|
|
|
|
// event isn't targeted at it.
|
2007-12-05 15:17:08 -08:00
|
|
|
//
|
|
|
|
// Rerouting may be needed when the user tries to navigate a context menu while
|
|
|
|
// keeping the mouse-button down (left or right mouse button) -- the OS thinks this
|
|
|
|
// is a dragging operation, so it sends events (mouseMoved and mouseUp) to the
|
2007-07-17 13:29:39 -07:00
|
|
|
// window where the dragging operation started (the parent of the context
|
2007-11-06 23:53:24 -08:00
|
|
|
// menu window). It also works around a bizarre Apple bug - if (while a context
|
|
|
|
// menu is open) you move the mouse over another app's window and then back over
|
|
|
|
// the context menu, mouseMoved events will be sent to the window underneath the
|
|
|
|
// context menu.
|
2007-12-05 15:17:08 -08:00
|
|
|
- (BOOL)ensureCorrectMouseEventTarget:(NSEvent*)anEvent
|
2007-07-17 13:29:39 -07:00
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-12-17 20:44:30 -08:00
|
|
|
// If there is no rollup widget we assume the OS routed the event correctly.
|
|
|
|
if (!gRollupWidget)
|
2007-11-06 23:53:24 -08:00
|
|
|
return YES;
|
2007-07-17 13:29:39 -07:00
|
|
|
|
2007-12-17 20:44:30 -08:00
|
|
|
// If this is the rollup widget and the event is not a mouse move then trust the OS routing.
|
|
|
|
// The reason for this trust is complicated.
|
|
|
|
//
|
|
|
|
// There are three types of mouse events that can legitimately need to be targeted at a window
|
|
|
|
// that they are not over. Mouse moves, mouse drags, and mouse ups. Anything else our app wouldn't
|
|
|
|
// handle (if the mouse was not over any window) or it would go to the appropriate window.
|
|
|
|
//
|
|
|
|
// We need to do manual event rerouting for mouse moves because we know that in some cases, like
|
|
|
|
// when there is a submenu opened from a popup window, the OS will route mouse move events to the
|
|
|
|
// submenu even if the mouse is over the parent. Mouse move events are never tied to a particular
|
|
|
|
// window because of some originating action like the starting point of a drag for drag events or
|
|
|
|
// a mouse down event for mouse up events, so it is always safe to do our own routing on them here.
|
|
|
|
//
|
|
|
|
// As for mouse drags and mouse ups, they have originating actions that tie them to windows they
|
|
|
|
// may no longer be over. If there is a rollup window present when one of these events is getting
|
|
|
|
// processed but we are not it, we are probably the window where the action originated, and that
|
|
|
|
// action must have caused the rollup window to come into existence. In that case, we might need
|
|
|
|
// to reroute the event if it is over the rollup window. That is why if we're not the rollup window
|
|
|
|
// we don't return YES here.
|
|
|
|
NSWindow* rollupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
|
|
|
|
if (mWindow == rollupWindow && [anEvent type] != NSMouseMoved)
|
|
|
|
return YES;
|
2007-07-17 13:29:39 -07:00
|
|
|
|
2007-12-17 20:44:30 -08:00
|
|
|
// Find the window that the event is over.
|
|
|
|
NSWindow* targetWindow = nsCocoaUtils::FindWindowUnderPoint(nsCocoaUtils::ScreenLocationForEvent(anEvent));
|
2007-07-17 13:29:39 -07:00
|
|
|
|
2007-12-17 20:44:30 -08:00
|
|
|
// If the event was not over any window, send it to the rollup window.
|
|
|
|
if (!targetWindow)
|
|
|
|
targetWindow = rollupWindow;
|
2007-07-17 13:29:39 -07:00
|
|
|
|
2007-12-17 20:44:30 -08:00
|
|
|
// At this point we've resolved a target window, if we are it then just return
|
|
|
|
// yes so we handle it. No need to redirect.
|
|
|
|
if (targetWindow == mWindow)
|
|
|
|
return YES;
|
2007-07-17 13:29:39 -07:00
|
|
|
|
2007-12-17 20:44:30 -08:00
|
|
|
// Send the event to its new destination.
|
|
|
|
NSPoint newWindowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, targetWindow);
|
|
|
|
NSEvent *newEvent = [NSEvent mouseEventWithType:[anEvent type]
|
|
|
|
location:newWindowLocation
|
|
|
|
modifierFlags:[anEvent modifierFlags]
|
|
|
|
timestamp:GetCurrentEventTime()
|
|
|
|
windowNumber:[targetWindow windowNumber]
|
|
|
|
context:nil
|
|
|
|
eventNumber:0
|
|
|
|
clickCount:1
|
|
|
|
pressure:0.0];
|
|
|
|
[targetWindow sendEvent:newEvent];
|
|
|
|
|
|
|
|
// Return NO because we just sent the event somewhere else.
|
2007-11-06 23:53:24 -08:00
|
|
|
return NO;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
2007-07-17 13:29:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we've just created a non-native context menu, we need to mark it as
|
|
|
|
// such and let the OS (and other programs) know when it opens and closes
|
|
|
|
// (this is how the OS knows to close other programs' context menus when
|
|
|
|
// ours open). We send the initial notification here, but others are sent
|
|
|
|
// in nsCocoaWindow::Show().
|
|
|
|
- (void)maybeInitContextMenuTracking
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
if (!gRollupWidget)
|
|
|
|
return;
|
2008-03-04 00:06:47 -08:00
|
|
|
|
|
|
|
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
|
|
|
if (prefs) {
|
|
|
|
PRBool useNativeContextMenus;
|
|
|
|
nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows", &useNativeContextMenus);
|
|
|
|
if (NS_SUCCEEDED(rv) && useNativeContextMenus)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-11-06 23:53:24 -08:00
|
|
|
NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
|
|
|
|
if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]])
|
2007-07-17 13:29:39 -07:00
|
|
|
return;
|
2007-11-06 23:53:24 -08:00
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
[[NSDistributedNotificationCenter defaultCenter]
|
|
|
|
postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
|
|
|
|
object:@"org.mozilla.gecko.PopupWindow"];
|
2007-11-06 23:53:24 -08:00
|
|
|
[(PopupWindow*)popupWindow setIsContextMenu:YES];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-07-17 13:29:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-26 15:19:04 -08:00
|
|
|
- (BOOL)maybeRollup:(NSEvent*)theEvent
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-11-26 15:19:04 -08:00
|
|
|
PRBool retVal = PR_FALSE;
|
|
|
|
if (gRollupWidget && gRollupListener) {
|
|
|
|
NSWindow* currentPopup = static_cast<NSWindow*>(gRollupWidget->GetNativeData(NS_NATIVE_WINDOW));
|
2007-12-05 15:17:08 -08:00
|
|
|
if (!nsCocoaUtils::IsEventOverWindow(theEvent, currentPopup)) {
|
2007-11-26 15:19:04 -08:00
|
|
|
PRBool rollup = PR_TRUE;
|
|
|
|
if ([theEvent type] == NSScrollWheel) {
|
|
|
|
gRollupListener->ShouldRollupOnMouseWheelEvent(&rollup);
|
|
|
|
// We don't want the event passed on for scrollwheel events if we're
|
|
|
|
// not supposed to close the popup. Otherwise the background window
|
|
|
|
// will scroll when a custom context menu or the autoscroll popup is
|
|
|
|
// open (and the mouse isn't over the popup) -- which doesn't seem right.
|
|
|
|
// This change resolves bmo bug 344367.
|
|
|
|
retVal = PR_TRUE;
|
|
|
|
}
|
|
|
|
// if we're dealing with menus, we probably have submenus and
|
2008-04-06 16:52:05 -07:00
|
|
|
// we don't want to rollup if the click is in a parent menu of
|
2007-11-26 15:19:04 -08:00
|
|
|
// the current submenu
|
|
|
|
nsCOMPtr<nsIMenuRollup> menuRollup;
|
|
|
|
menuRollup = (do_QueryInterface(gRollupListener));
|
|
|
|
if (menuRollup) {
|
2008-01-16 22:57:13 -08:00
|
|
|
nsAutoTArray<nsIWidget*, 5> widgetChain;
|
|
|
|
menuRollup->GetSubmenuWidgetChain(&widgetChain);
|
2008-01-16 23:06:43 -08:00
|
|
|
for (PRUint32 i = 0; i < widgetChain.Length(); i++) {
|
|
|
|
nsIWidget* widget = widgetChain[i];
|
|
|
|
NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
|
|
|
|
if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
|
|
|
|
rollup = PR_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} // foreach parent menu widget
|
2007-11-26 15:19:04 -08:00
|
|
|
} // if rollup listener knows about menus
|
2007-11-06 23:53:24 -08:00
|
|
|
|
2007-11-26 15:19:04 -08:00
|
|
|
// if we've determined that we should still rollup, do it.
|
|
|
|
if (rollup) {
|
2007-12-03 08:33:42 -08:00
|
|
|
gRollupListener->Rollup(nsnull);
|
2007-11-26 15:19:04 -08:00
|
|
|
retVal = PR_TRUE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-11-26 15:19:04 -08:00
|
|
|
|
2008-02-20 15:47:05 -08:00
|
|
|
return retVal;
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
2007-11-26 15:19:04 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)mouseDown:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2008-04-06 16:52:05 -07:00
|
|
|
// If we've already seen this event due to direct dispatch from menuForEvent:
|
|
|
|
// just bail; if not, remember it.
|
|
|
|
if (mLastMouseDownEvent == theEvent) {
|
|
|
|
[mLastMouseDownEvent release];
|
|
|
|
mLastMouseDownEvent = nil;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
[mLastMouseDownEvent release];
|
|
|
|
mLastMouseDownEvent = [theEvent retain];
|
|
|
|
}
|
|
|
|
|
2007-11-26 15:19:04 -08:00
|
|
|
if (![self ensureCorrectMouseEventTarget:theEvent])
|
|
|
|
return;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2007-11-26 15:19:04 -08:00
|
|
|
if ([self maybeRollup:theEvent])
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
unsigned int modifierFlags = [theEvent modifierFlags];
|
2007-11-26 15:19:04 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// if the command and alt keys are held down, initiate hand scrolling
|
|
|
|
if ([ChildView areHandScrollModifiers:modifierFlags]) {
|
2007-11-26 15:19:04 -08:00
|
|
|
[self startHandScroll:theEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
// needed to change the focus, among other things, since we don't
|
|
|
|
// get to do that below.
|
|
|
|
[super mouseDown:theEvent];
|
|
|
|
return; // do not pass this mousedown event to gecko
|
|
|
|
}
|
|
|
|
|
|
|
|
#if USE_CLICK_HOLD_CONTEXTMENU
|
|
|
|
// fire off timer to check for click-hold after two seconds. retains |theEvent|
|
|
|
|
[self performSelector:@selector(clickHoldCallback:) withObject:theEvent afterDelay:2.0];
|
|
|
|
#endif
|
|
|
|
|
2007-04-19 13:18:46 -07:00
|
|
|
// in order to send gecko events we'll need a gecko widget
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoEvent.clickCount = [theEvent clickCount];
|
|
|
|
if (modifierFlags & NSControlKeyMask)
|
|
|
|
geckoEvent.button = nsMouseEvent::eRightButton;
|
|
|
|
else
|
|
|
|
geckoEvent.button = nsMouseEvent::eLeftButton;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// create native EventRecord for use by plugins
|
2007-03-22 10:30:00 -07:00
|
|
|
EventRecord macEvent;
|
|
|
|
macEvent.what = mouseDown;
|
|
|
|
macEvent.message = 0;
|
|
|
|
macEvent.when = ::TickCount();
|
|
|
|
::GetGlobalMouse(&macEvent.where);
|
2008-03-04 09:58:52 -08:00
|
|
|
macEvent.modifiers = ::GetCurrentEventKeyModifiers();
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
|
|
|
|
2007-04-19 13:18:46 -07:00
|
|
|
mGeckoChild->DispatchMouseEvent(geckoEvent);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// XXX maybe call markedTextSelectionChanged:client: here?
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)mouseUp:(NSEvent *)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mInHandScroll) {
|
|
|
|
[self updateHandScroll:theEvent];
|
|
|
|
[self stopHandScroll:theEvent];
|
|
|
|
return;
|
|
|
|
}
|
2007-06-12 13:28:26 -07:00
|
|
|
|
2007-11-06 23:53:24 -08:00
|
|
|
if (![self ensureCorrectMouseEventTarget:theEvent])
|
2007-07-17 13:29:39 -07:00
|
|
|
return;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// create native EventRecord for use by plugins
|
2007-03-22 10:30:00 -07:00
|
|
|
EventRecord macEvent;
|
|
|
|
macEvent.what = mouseUp;
|
|
|
|
macEvent.message = 0;
|
|
|
|
macEvent.when = ::TickCount();
|
|
|
|
::GetGlobalMouse(&macEvent.where);
|
2008-03-04 09:58:52 -08:00
|
|
|
macEvent.modifiers = ::GetCurrentEventKeyModifiers();
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
mGeckoChild->DispatchMouseEvent(geckoEvent);
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-05 22:11:35 -07:00
|
|
|
// sends a mouse enter or exit event into gecko
|
|
|
|
static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted,
|
|
|
|
PRUint32 msg,
|
|
|
|
nsIWidget *widget,
|
|
|
|
nsMouseEvent::reasonType aReason,
|
2008-03-12 15:44:45 -07:00
|
|
|
NSPoint* localEventLocation,
|
|
|
|
nsMouseEvent::exitType type)
|
2007-04-10 13:07:49 -07:00
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-04-10 13:07:49 -07:00
|
|
|
if (!widget || !localEventLocation)
|
|
|
|
return nsEventStatus_eIgnore;
|
2007-06-12 13:28:26 -07:00
|
|
|
|
2007-04-10 13:07:49 -07:00
|
|
|
nsMouseEvent event(isTrusted, msg, widget, aReason);
|
|
|
|
event.refPoint.x = nscoord((PRInt32)localEventLocation->x);
|
|
|
|
event.refPoint.y = nscoord((PRInt32)localEventLocation->y);
|
2007-06-12 13:28:26 -07:00
|
|
|
|
2007-09-05 22:11:35 -07:00
|
|
|
EventRecord macEvent;
|
|
|
|
macEvent.what = adjustCursorEvent;
|
|
|
|
macEvent.message = 0;
|
|
|
|
macEvent.when = ::TickCount();
|
|
|
|
::GetGlobalMouse(&macEvent.where);
|
2008-03-04 09:58:52 -08:00
|
|
|
macEvent.modifiers = ::GetCurrentEventKeyModifiers();
|
2007-09-05 22:11:35 -07:00
|
|
|
event.nativeMsg = &macEvent;
|
|
|
|
|
2008-03-12 15:44:45 -07:00
|
|
|
event.exit = type;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
nsEventStatus status;
|
2007-04-10 13:07:49 -07:00
|
|
|
widget->DispatchEvent(&event, status);
|
2008-04-09 10:23:34 -07:00
|
|
|
|
|
|
|
// After the cursor exits a view set it to a visible regular arrow cursor.
|
|
|
|
// This lets us recover from plugins that mess with it.
|
|
|
|
if (msg == NS_MOUSE_EXIT) {
|
|
|
|
[NSCursor unhide];
|
|
|
|
[[NSCursor arrowCursor] set];
|
|
|
|
}
|
|
|
|
|
2007-04-10 13:07:49 -07:00
|
|
|
return status;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsEventStatus_eIgnore);
|
2007-04-10 13:07:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
- (void)mouseMoved:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2008-01-15 16:45:54 -08:00
|
|
|
// Work around an Apple bug that causes the OS to continue sending
|
|
|
|
// mouseMoved events to a window for a while after it's been miniaturized.
|
|
|
|
// This may be related to a similar problem with popup windows (bmo bug
|
|
|
|
// 378645, popup windows continue to receive mouseMoved events after having
|
|
|
|
// been "ordered out"), which is worked around in nsCocoaWindow::Show()
|
|
|
|
// (search on 378645 in nsCocoaWindow.mm). This problem is bmo bug 410219,
|
|
|
|
// and exists in both OS X 10.4 and 10.5.
|
|
|
|
if ([[self window] isMiniaturized])
|
|
|
|
return;
|
|
|
|
|
2007-12-05 15:17:08 -08:00
|
|
|
NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(theEvent, mWindow);
|
2007-04-10 13:07:49 -07:00
|
|
|
NSPoint viewEventLocation = [self convertPoint:windowEventLocation fromView:nil];
|
2007-04-24 12:02:31 -07:00
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
// Installing a mouseMoved handler on the EventMonitor target (in
|
|
|
|
// nsToolkit::RegisterForAllProcessMouseEvents()) means that some of the
|
|
|
|
// events received here come from other processes. For this reason we need
|
|
|
|
// to avoid processing them unless they're over a context menu -- otherwise
|
|
|
|
// tooltips and other mouse-hover effects will "work" even when our app
|
|
|
|
// doesn't have the focus.
|
2007-11-26 15:19:04 -08:00
|
|
|
BOOL mouseEventIsOverRollupWidget = NO;
|
|
|
|
if (gRollupWidget) {
|
|
|
|
NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
|
2007-12-05 15:17:08 -08:00
|
|
|
mouseEventIsOverRollupWidget = nsCocoaUtils::IsEventOverWindow(theEvent, popupWindow);
|
2007-11-26 15:19:04 -08:00
|
|
|
}
|
2007-12-05 15:17:08 -08:00
|
|
|
|
2007-11-26 15:19:04 -08:00
|
|
|
if (![NSApp isActive] && !mouseEventIsOverRollupWidget) {
|
2007-07-17 13:29:39 -07:00
|
|
|
if (sLastViewEntered) {
|
|
|
|
nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
|
2008-01-29 13:12:00 -08:00
|
|
|
NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
|
2008-03-12 15:44:45 -07:00
|
|
|
SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
|
2008-04-04 16:17:36 -07:00
|
|
|
&exitEventLocation, nsMouseEvent::eTopLevel);
|
2007-07-17 13:29:39 -07:00
|
|
|
sLastViewEntered = nil;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-11-06 23:53:24 -08:00
|
|
|
if (![self ensureCorrectMouseEventTarget:theEvent])
|
2007-07-17 13:29:39 -07:00
|
|
|
return;
|
|
|
|
|
2007-04-10 13:07:49 -07:00
|
|
|
NSView* view = [[mWindow contentView] hitTest:windowEventLocation];
|
|
|
|
if (view) {
|
|
|
|
// we shouldn't handle this if the hit view is not us
|
|
|
|
if (view != (NSView*)self) {
|
|
|
|
[view mouseMoved:theEvent];
|
2007-12-05 15:17:08 -08:00
|
|
|
return;
|
2007-04-10 13:07:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// If the hit test returned nil then the mouse isn't over the window. If thse mouse
|
|
|
|
// exited the window then send mouse exit to the last view in the window it was over.
|
|
|
|
if (sLastViewEntered) {
|
2008-01-29 13:12:00 -08:00
|
|
|
NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
|
|
|
|
// NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
|
2007-04-10 13:07:49 -07:00
|
|
|
nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
|
2008-03-12 15:44:45 -07:00
|
|
|
SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
|
|
|
|
&exitEventLocation, nsMouseEvent::eTopLevel);
|
2008-01-15 15:11:55 -08:00
|
|
|
sLastViewEntered = nil;
|
2007-04-10 13:07:49 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
|
|
|
}
|
2007-04-10 13:07:49 -07:00
|
|
|
|
|
|
|
// At this point we are supposed to handle this event. If we were not the last view entered, then
|
|
|
|
// we should send an exit event to the last view entered and an enter event to ourselves.
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2007-04-10 13:07:49 -07:00
|
|
|
if (sLastViewEntered != self) {
|
2007-06-15 15:34:48 -07:00
|
|
|
if (sLastViewEntered) {
|
2008-01-29 13:12:00 -08:00
|
|
|
NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
|
|
|
|
// NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
|
2007-04-10 13:07:49 -07:00
|
|
|
nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
|
2008-03-12 15:44:45 -07:00
|
|
|
SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
|
|
|
|
&exitEventLocation, nsMouseEvent::eChild);
|
2007-11-28 10:49:55 -08:00
|
|
|
|
|
|
|
// The mouse exit event we just sent may have destroyed this widget, bail if that happened.
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
2007-04-10 13:07:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// NSLog(@"sending NS_MOUSE_ENTER event with point %f,%f\n", viewEventLocation.x, viewEventLocation.y);
|
2008-03-12 15:44:45 -07:00
|
|
|
SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_ENTER, mGeckoChild, nsMouseEvent::eReal,
|
|
|
|
&viewEventLocation, nsMouseEvent::eChild);
|
2007-04-10 13:07:49 -07:00
|
|
|
|
2007-11-28 10:49:55 -08:00
|
|
|
// The mouse enter event we just sent may have destroyed this widget, bail if that happened.
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-04-10 13:07:49 -07:00
|
|
|
// mark this view as the last view entered
|
|
|
|
sLastViewEntered = (NSView*)self;
|
|
|
|
|
|
|
|
// checks to see if we should change to the hand cursor
|
|
|
|
[self setHandScrollCursor:theEvent];
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// check if we are in a hand scroll or if the user
|
|
|
|
// has command and alt held down; if so, we do not want
|
|
|
|
// gecko messing with the cursor.
|
|
|
|
if ([ChildView areHandScrollModifiers:[theEvent modifierFlags]])
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// create native EventRecord for use by plugins
|
2007-03-22 10:30:00 -07:00
|
|
|
EventRecord macEvent;
|
2007-09-05 22:11:35 -07:00
|
|
|
macEvent.what = adjustCursorEvent;
|
2007-03-22 10:30:00 -07:00
|
|
|
macEvent.message = 0;
|
|
|
|
macEvent.when = ::TickCount();
|
|
|
|
::GetGlobalMouse(&macEvent.where);
|
2008-03-04 09:58:52 -08:00
|
|
|
macEvent.modifiers = ::GetCurrentEventKeyModifiers();
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
|
|
|
|
|
|
|
mGeckoChild->DispatchMouseEvent(geckoEvent);
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)mouseDragged:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-11-06 23:53:24 -08:00
|
|
|
if (![self ensureCorrectMouseEventTarget:theEvent])
|
2007-07-17 13:29:39 -07:00
|
|
|
return;
|
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// if the handscroll flag is set, steal this event
|
|
|
|
if (mInHandScroll) {
|
|
|
|
[self updateHandScroll:theEvent];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-23 17:01:04 -07:00
|
|
|
gLastDragView = self;
|
|
|
|
gLastDragEvent = theEvent;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// create native EventRecord for use by plugins
|
2007-03-22 10:30:00 -07:00
|
|
|
EventRecord macEvent;
|
|
|
|
macEvent.what = nullEvent;
|
|
|
|
macEvent.message = 0;
|
|
|
|
macEvent.when = ::TickCount();
|
|
|
|
::GetGlobalMouse(&macEvent.where);
|
2008-03-04 09:58:52 -08:00
|
|
|
macEvent.modifiers = btnState | ::GetCurrentEventKeyModifiers();
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
2007-06-12 13:28:26 -07:00
|
|
|
|
2008-01-15 15:11:55 -08:00
|
|
|
mGeckoChild->DispatchMouseEvent(geckoEvent);
|
|
|
|
|
|
|
|
// Note, sending the above event might have destroyed our widget since we didn't retain.
|
|
|
|
// Fine so long as we don't access any local variables from here on.
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-09-23 17:01:04 -07:00
|
|
|
gLastDragView = nil;
|
|
|
|
gLastDragEvent = nil;
|
2007-03-22 10:30:00 -07:00
|
|
|
// XXX maybe call markedTextSelectionChanged:client: here?
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)rightMouseDown:(NSEvent *)theEvent
|
2007-11-06 23:53:24 -08:00
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-11-06 23:53:24 -08:00
|
|
|
if (![self ensureCorrectMouseEventTarget:theEvent])
|
|
|
|
return;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-01-15 15:11:21 -08:00
|
|
|
|
2008-01-15 15:11:55 -08:00
|
|
|
[self maybeRollup:theEvent];
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
2007-11-26 15:19:04 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// The right mouse went down, fire off a right mouse down event to gecko
|
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoEvent.button = nsMouseEvent::eRightButton;
|
2007-06-12 13:28:26 -07:00
|
|
|
geckoEvent.clickCount = [theEvent clickCount];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// create native EventRecord for use by plugins
|
2007-03-22 10:30:00 -07:00
|
|
|
EventRecord macEvent;
|
|
|
|
macEvent.what = mouseDown;
|
|
|
|
macEvent.message = 0;
|
|
|
|
macEvent.when = ::TickCount();
|
|
|
|
::GetGlobalMouse(&macEvent.where);
|
|
|
|
macEvent.modifiers = controlKey; // fake a context menu click
|
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
|
|
|
|
|
|
|
PRBool handled = mGeckoChild->DispatchMouseEvent(geckoEvent);
|
2008-01-15 15:11:55 -08:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!handled)
|
|
|
|
[super rightMouseDown:theEvent]; // let the superview do context menu stuff
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)rightMouseUp:(NSEvent *)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-11-06 23:53:24 -08:00
|
|
|
if (![self ensureCorrectMouseEventTarget:theEvent])
|
2007-07-17 13:29:39 -07:00
|
|
|
return;
|
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoEvent.button = nsMouseEvent::eRightButton;
|
2007-06-12 13:28:26 -07:00
|
|
|
geckoEvent.clickCount = [theEvent clickCount];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// create native EventRecord for use by plugins
|
2007-03-22 10:30:00 -07:00
|
|
|
EventRecord macEvent;
|
|
|
|
macEvent.what = mouseUp;
|
|
|
|
macEvent.message = 0;
|
|
|
|
macEvent.when = ::TickCount();
|
|
|
|
::GetGlobalMouse(&macEvent.where);
|
|
|
|
macEvent.modifiers = controlKey; // fake a context menu click
|
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-04-08 12:56:13 -07:00
|
|
|
mGeckoChild->DispatchMouseEvent(geckoEvent);
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
- (void)rightMouseDragged:(NSEvent*)theEvent
|
|
|
|
{
|
2007-11-06 23:53:24 -08:00
|
|
|
if (![self ensureCorrectMouseEventTarget:theEvent])
|
2007-07-17 13:29:39 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
|
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
|
|
|
geckoEvent.button = nsMouseEvent::eRightButton;
|
|
|
|
|
|
|
|
// send event into Gecko by going directly to the
|
|
|
|
// the widget.
|
|
|
|
mGeckoChild->DispatchMouseEvent(geckoEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
- (void)otherMouseDown:(NSEvent *)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-11-06 23:53:24 -08:00
|
|
|
if (![self ensureCorrectMouseEventTarget:theEvent])
|
|
|
|
return;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2007-11-26 15:19:04 -08:00
|
|
|
if ([self maybeRollup:theEvent])
|
|
|
|
return;
|
2007-07-17 13:29:39 -07:00
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoEvent.button = nsMouseEvent::eMiddleButton;
|
2007-06-12 13:28:26 -07:00
|
|
|
geckoEvent.clickCount = [theEvent clickCount];
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mGeckoChild->DispatchMouseEvent(geckoEvent);
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)otherMouseUp:(NSEvent *)theEvent
|
|
|
|
{
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoEvent.button = nsMouseEvent::eMiddleButton;
|
|
|
|
|
|
|
|
mGeckoChild->DispatchMouseEvent(geckoEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-17 13:29:39 -07:00
|
|
|
- (void)otherMouseDragged:(NSEvent*)theEvent
|
|
|
|
{
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
|
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
|
|
|
geckoEvent.button = nsMouseEvent::eMiddleButton;
|
|
|
|
|
|
|
|
// send event into Gecko by going directly to the
|
|
|
|
// the widget.
|
|
|
|
mGeckoChild->DispatchMouseEvent(geckoEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Handle an NSScrollWheel event for a single axis only.
|
|
|
|
-(void)scrollWheel:(NSEvent*)theEvent forAxis:(enum nsMouseScrollEvent::nsMouseScrollFlags)inAxis
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
float scrollDelta;
|
|
|
|
|
|
|
|
if (inAxis & nsMouseScrollEvent::kIsVertical)
|
|
|
|
scrollDelta = -[theEvent deltaY];
|
|
|
|
else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
|
|
|
|
scrollDelta = -[theEvent deltaX];
|
|
|
|
else
|
2008-01-15 15:11:55 -08:00
|
|
|
return; // caller screwed up
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (scrollDelta == 0)
|
|
|
|
// No sense in firing off a Gecko event. Note that as of 10.4 Tiger,
|
|
|
|
// a single NSScrollWheel event might result in deltaX = deltaY = 0.
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsMouseScrollEvent geckoEvent(PR_TRUE, NS_MOUSE_SCROLL, nsnull);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
geckoEvent.scrollFlags |= inAxis;
|
|
|
|
|
|
|
|
// Gecko only understands how to scroll by an integer value. Using floor
|
|
|
|
// and ceil is better than truncating the fraction, especially when
|
|
|
|
// |delta| < 1.
|
|
|
|
if (scrollDelta < 0)
|
|
|
|
geckoEvent.delta = (PRInt32)floorf(scrollDelta);
|
|
|
|
else
|
|
|
|
geckoEvent.delta = (PRInt32)ceilf(scrollDelta);
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2007-03-22 10:30:00 -07:00
|
|
|
mGeckoChild->DispatchWindowEvent(geckoEvent);
|
2008-01-15 15:11:55 -08:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// dispatch scroll wheel carbon event for plugins
|
|
|
|
{
|
|
|
|
EventRef theEvent;
|
|
|
|
OSStatus err = ::MacCreateEvent(NULL,
|
|
|
|
kEventClassMouse,
|
|
|
|
kEventMouseWheelMoved,
|
|
|
|
TicksToEventTime(TickCount()),
|
|
|
|
kEventAttributeUserEvent,
|
|
|
|
&theEvent);
|
|
|
|
if (err == noErr) {
|
|
|
|
EventMouseWheelAxis axis;
|
|
|
|
if (inAxis & nsMouseScrollEvent::kIsVertical)
|
|
|
|
axis = kEventMouseWheelAxisY;
|
|
|
|
else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
|
|
|
|
axis = kEventMouseWheelAxisX;
|
|
|
|
|
|
|
|
SetEventParameter(theEvent,
|
|
|
|
kEventParamMouseWheelAxis,
|
|
|
|
typeMouseWheelAxis,
|
|
|
|
sizeof(EventMouseWheelAxis),
|
|
|
|
&axis);
|
|
|
|
|
|
|
|
SInt32 delta = (SInt32)-geckoEvent.delta;
|
|
|
|
SetEventParameter(theEvent,
|
|
|
|
kEventParamMouseWheelDelta,
|
|
|
|
typeLongInteger,
|
|
|
|
sizeof(SInt32),
|
|
|
|
&delta);
|
|
|
|
|
|
|
|
Point mouseLoc;
|
|
|
|
::GetGlobalMouse(&mouseLoc);
|
|
|
|
SetEventParameter(theEvent,
|
|
|
|
kEventParamMouseLocation,
|
|
|
|
typeQDPoint,
|
|
|
|
sizeof(Point),
|
|
|
|
&mouseLoc);
|
|
|
|
|
2007-05-02 12:22:18 -07:00
|
|
|
::SendEventToEventTarget(theEvent, GetWindowEventTarget((WindowRef)[[self window] windowRef]));
|
2007-03-22 10:30:00 -07:00
|
|
|
ReleaseEvent(theEvent);
|
|
|
|
}
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-(void)scrollWheel:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2007-11-26 15:19:04 -08:00
|
|
|
if ([self maybeRollup:theEvent])
|
|
|
|
return;
|
|
|
|
|
2008-01-15 15:11:55 -08:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// It's possible for a single NSScrollWheel event to carry both useful
|
|
|
|
// deltaX and deltaY, for example, when the "wheel" is a trackpad.
|
|
|
|
// NSMouseScrollEvent can only carry one axis at a time, so the system
|
|
|
|
// event will be split into two Gecko events if necessary.
|
|
|
|
[self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsVertical];
|
2008-01-15 15:11:55 -08:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
[self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsHorizontal];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-(NSMenu*)menuForEvent:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild || [self isPluginView])
|
2007-03-22 10:30:00 -07:00
|
|
|
return nil;
|
2007-08-20 18:02:09 -07:00
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2007-11-26 15:19:04 -08:00
|
|
|
[self maybeRollup:theEvent];
|
2008-01-15 15:11:55 -08:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return nil;
|
2007-11-26 15:19:04 -08:00
|
|
|
|
2008-04-06 16:52:05 -07:00
|
|
|
// Cocoa doesn't always dispatch a mouseDown: for a control-click event,
|
|
|
|
// depends on what we return from menuForEvent:. Gecko always expects one
|
|
|
|
// and expects the mouse down event before the context menu event, so
|
|
|
|
// get that event sent first if this is a left mouse click.
|
|
|
|
if ([theEvent type] == NSLeftMouseDown) {
|
|
|
|
[self mouseDown:theEvent];
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return nil;
|
|
|
|
}
|
2008-01-16 20:59:43 -08:00
|
|
|
|
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, NS_CONTEXTMENU, nsnull, nsMouseEvent::eReal);
|
|
|
|
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
|
|
|
geckoEvent.button = nsMouseEvent::eRightButton;
|
|
|
|
mGeckoChild->DispatchMouseEvent(geckoEvent);
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return nil;
|
|
|
|
|
2008-03-04 00:06:47 -08:00
|
|
|
[self maybeInitContextMenuTracking];
|
2008-02-28 07:58:33 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Go up our view chain to fetch the correct menu to return.
|
|
|
|
return [self contextMenu];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSMenu*)contextMenu
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NSView* superView = [self superview];
|
|
|
|
if ([superView respondsToSelector:@selector(contextMenu)])
|
|
|
|
return [(NSView<mozView>*)superView contextMenu];
|
|
|
|
|
|
|
|
return nil;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (TopLevelWindowData*)ensureWindowData
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
WindowDataMap* windowMap = [WindowDataMap sharedWindowDataMap];
|
|
|
|
|
|
|
|
TopLevelWindowData* windowData = [windowMap dataForWindow:mWindow];
|
|
|
|
if (mWindow && !windowData)
|
|
|
|
{
|
|
|
|
windowData = [[TopLevelWindowData alloc] initWithWindow:mWindow];
|
|
|
|
[windowMap setData:windowData forWindow:mWindow]; // takes ownership
|
|
|
|
[windowData release];
|
|
|
|
}
|
|
|
|
return windowData;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static PRBool ConvertUnicodeToCharCode(PRUnichar inUniChar, unsigned char* outChar)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
UnicodeToTextInfo converterInfo;
|
|
|
|
TextEncoding systemEncoding;
|
|
|
|
Str255 convertedString;
|
|
|
|
OSStatus err;
|
|
|
|
|
|
|
|
*outChar = 0;
|
|
|
|
|
|
|
|
err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &systemEncoding);
|
|
|
|
if (err != noErr)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo);
|
|
|
|
if (err != noErr)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar), &inUniChar, convertedString);
|
|
|
|
if (err != noErr)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
*outChar = convertedString[1];
|
|
|
|
::DisposeUnicodeToTextInfo(&converterInfo);
|
|
|
|
return PR_TRUE;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& macEvent, PRUint32 keyType = 0)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
UInt32 charCode = 0;
|
|
|
|
if ([cocoaEvent type] == NSFlagsChanged) {
|
|
|
|
macEvent.what = keyType == NS_KEY_DOWN ? keyDown : keyUp;
|
|
|
|
} else {
|
2008-03-25 13:44:06 -07:00
|
|
|
if ([[cocoaEvent characters] length] > 0)
|
|
|
|
charCode = [[cocoaEvent characters] characterAtIndex:0];
|
2007-03-22 10:30:00 -07:00
|
|
|
if ([cocoaEvent type] == NSKeyDown)
|
|
|
|
macEvent.what = [cocoaEvent isARepeat] ? autoKey : keyDown;
|
|
|
|
else
|
|
|
|
macEvent.what = keyUp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (charCode >= 0x0080) {
|
|
|
|
switch (charCode) {
|
|
|
|
case NSUpArrowFunctionKey:
|
|
|
|
charCode = kUpArrowCharCode;
|
|
|
|
break;
|
|
|
|
case NSDownArrowFunctionKey:
|
|
|
|
charCode = kDownArrowCharCode;
|
|
|
|
break;
|
|
|
|
case NSLeftArrowFunctionKey:
|
|
|
|
charCode = kLeftArrowCharCode;
|
|
|
|
break;
|
|
|
|
case NSRightArrowFunctionKey:
|
|
|
|
charCode = kRightArrowCharCode;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
unsigned char convertedCharCode;
|
|
|
|
if (ConvertUnicodeToCharCode(charCode, &convertedCharCode))
|
|
|
|
charCode = convertedCharCode;
|
|
|
|
//NSLog(@"charcode is %d, converted to %c, char is %@", charCode, convertedCharCode, [cocoaEvent characters]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
macEvent.message = (charCode & 0x00FF) | ([cocoaEvent keyCode] << 8);
|
|
|
|
macEvent.when = ::TickCount();
|
|
|
|
::GetGlobalMouse(&macEvent.where);
|
2008-03-04 09:58:52 -08:00
|
|
|
macEvent.modifiers = ::GetCurrentEventKeyModifiers();
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// Key code constants
|
|
|
|
enum
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-06-12 13:28:26 -07:00
|
|
|
kEscapeKeyCode = 0x35,
|
2007-11-06 23:40:58 -08:00
|
|
|
kRCommandKeyCode = 0x36, // right command key
|
2007-06-12 13:28:26 -07:00
|
|
|
kCommandKeyCode = 0x37,
|
|
|
|
kShiftKeyCode = 0x38,
|
|
|
|
kCapsLockKeyCode = 0x39,
|
2007-11-06 23:40:58 -08:00
|
|
|
kOptionkeyCode = 0x3A,
|
2007-06-12 13:28:26 -07:00
|
|
|
kControlKeyCode = 0x3B,
|
2007-11-06 23:40:58 -08:00
|
|
|
kRShiftKeyCode = 0x3C, // right shift key
|
|
|
|
kROptionKeyCode = 0x3D, // right option key
|
|
|
|
kRControlKeyCode = 0x3E, // right control key
|
2007-06-12 13:28:26 -07:00
|
|
|
kClearKeyCode = 0x47,
|
2007-10-24 23:51:10 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// function keys
|
|
|
|
kF1KeyCode = 0x7A,
|
|
|
|
kF2KeyCode = 0x78,
|
|
|
|
kF3KeyCode = 0x63,
|
|
|
|
kF4KeyCode = 0x76,
|
|
|
|
kF5KeyCode = 0x60,
|
|
|
|
kF6KeyCode = 0x61,
|
|
|
|
kF7KeyCode = 0x62,
|
|
|
|
kF8KeyCode = 0x64,
|
|
|
|
kF9KeyCode = 0x65,
|
|
|
|
kF10KeyCode = 0x6D,
|
|
|
|
kF11KeyCode = 0x67,
|
|
|
|
kF12KeyCode = 0x6F,
|
|
|
|
kF13KeyCode = 0x69,
|
|
|
|
kF14KeyCode = 0x6B,
|
|
|
|
kF15KeyCode = 0x71,
|
|
|
|
|
|
|
|
kPrintScreenKeyCode = kF13KeyCode,
|
|
|
|
kScrollLockKeyCode = kF14KeyCode,
|
|
|
|
kPauseKeyCode = kF15KeyCode,
|
|
|
|
|
|
|
|
// keypad
|
|
|
|
kKeypad0KeyCode = 0x52,
|
|
|
|
kKeypad1KeyCode = 0x53,
|
|
|
|
kKeypad2KeyCode = 0x54,
|
|
|
|
kKeypad3KeyCode = 0x55,
|
|
|
|
kKeypad4KeyCode = 0x56,
|
|
|
|
kKeypad5KeyCode = 0x57,
|
|
|
|
kKeypad6KeyCode = 0x58,
|
|
|
|
kKeypad7KeyCode = 0x59,
|
|
|
|
kKeypad8KeyCode = 0x5B,
|
|
|
|
kKeypad9KeyCode = 0x5C,
|
2007-06-29 20:26:42 -07:00
|
|
|
|
|
|
|
// The following key codes are not defined until Mac OS X 10.5
|
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
|
|
|
|
kVK_ANSI_1 = 0x12,
|
|
|
|
kVK_ANSI_2 = 0x13,
|
|
|
|
kVK_ANSI_3 = 0x14,
|
|
|
|
kVK_ANSI_4 = 0x15,
|
|
|
|
kVK_ANSI_5 = 0x17,
|
|
|
|
kVK_ANSI_6 = 0x16,
|
|
|
|
kVK_ANSI_7 = 0x1A,
|
|
|
|
kVK_ANSI_8 = 0x1C,
|
|
|
|
kVK_ANSI_9 = 0x19,
|
|
|
|
kVK_ANSI_0 = 0x1D,
|
|
|
|
#endif
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
kKeypadMultiplyKeyCode = 0x43,
|
|
|
|
kKeypadAddKeyCode = 0x45,
|
|
|
|
kKeypadSubtractKeyCode = 0x4E,
|
|
|
|
kKeypadDecimalKeyCode = 0x41,
|
|
|
|
kKeypadDivideKeyCode = 0x4B,
|
|
|
|
kKeypadEqualsKeyCode = 0x51, // no correpsonding gecko key code
|
|
|
|
kEnterKeyCode = 0x4C,
|
|
|
|
kReturnKeyCode = 0x24,
|
|
|
|
kPowerbookEnterKeyCode = 0x34, // Enter on Powerbook's keyboard is different
|
|
|
|
|
|
|
|
kInsertKeyCode = 0x72, // also help key
|
|
|
|
kDeleteKeyCode = 0x75, // also forward delete key
|
|
|
|
kTabKeyCode = 0x30,
|
2008-01-28 22:11:06 -08:00
|
|
|
kTildeKeyCode = 0x32,
|
2007-06-12 13:28:26 -07:00
|
|
|
kBackspaceKeyCode = 0x33,
|
|
|
|
kHomeKeyCode = 0x73,
|
|
|
|
kEndKeyCode = 0x77,
|
|
|
|
kPageUpKeyCode = 0x74,
|
|
|
|
kPageDownKeyCode = 0x79,
|
|
|
|
kLeftArrowKeyCode = 0x7B,
|
|
|
|
kRightArrowKeyCode = 0x7C,
|
|
|
|
kUpArrowKeyCode = 0x7E,
|
|
|
|
kDownArrowKeyCode = 0x7D
|
|
|
|
};
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
|
2007-10-24 23:51:10 -07:00
|
|
|
static PRBool IsPrintableChar(PRUnichar aChar)
|
|
|
|
{
|
|
|
|
return (aChar >= 0x20 && aChar <= 0x7E) || aChar >= 0xA0;
|
|
|
|
}
|
|
|
|
|
2007-10-11 01:23:10 -07:00
|
|
|
static PRUint32 GetGeckoKeyCodeFromChar(PRUnichar aChar)
|
|
|
|
{
|
2007-10-15 05:35:57 -07:00
|
|
|
// We don't support the key code for non-ASCII characters
|
2007-10-24 23:51:10 -07:00
|
|
|
if (aChar > 0x7E)
|
2007-10-15 05:35:57 -07:00
|
|
|
return 0;
|
|
|
|
|
2007-10-24 23:51:10 -07:00
|
|
|
if (aChar >= 'a' && aChar <= 'z') // lowercase
|
|
|
|
return PRUint32(toupper(aChar));
|
|
|
|
else if (aChar >= 'A' && aChar <= 'Z') // uppercase
|
|
|
|
return PRUint32(aChar);
|
|
|
|
else if (aChar >= '0' && aChar <= '9')
|
|
|
|
return PRUint32(aChar - '0' + NS_VK_0);
|
|
|
|
|
2007-10-11 01:23:10 -07:00
|
|
|
switch (aChar)
|
|
|
|
{
|
|
|
|
case kReturnCharCode:
|
|
|
|
case kEnterCharCode:
|
|
|
|
case '\n':
|
|
|
|
return NS_VK_RETURN;
|
|
|
|
case '{':
|
|
|
|
case '[':
|
|
|
|
return NS_VK_OPEN_BRACKET;
|
|
|
|
case '}':
|
|
|
|
case ']':
|
|
|
|
return NS_VK_CLOSE_BRACKET;
|
|
|
|
case '\'':
|
|
|
|
case '"':
|
|
|
|
return NS_VK_QUOTE;
|
|
|
|
|
|
|
|
case '\\': return NS_VK_BACK_SLASH;
|
|
|
|
case ' ': return NS_VK_SPACE;
|
|
|
|
case ';': return NS_VK_SEMICOLON;
|
|
|
|
case '=': return NS_VK_EQUALS;
|
|
|
|
case ',': return NS_VK_COMMA;
|
|
|
|
case '.': return NS_VK_PERIOD;
|
|
|
|
case '/': return NS_VK_SLASH;
|
|
|
|
case '`': return NS_VK_BACK_QUOTE;
|
|
|
|
case '\t': return NS_VK_TAB;
|
2007-10-24 23:51:10 -07:00
|
|
|
case '-': return NS_VK_SUBTRACT;
|
|
|
|
case '+': return NS_VK_ADD;
|
2007-10-11 01:23:10 -07:00
|
|
|
|
|
|
|
default:
|
2007-10-24 23:51:10 -07:00
|
|
|
if (!IsPrintableChar(aChar))
|
|
|
|
NS_WARNING("GetGeckoKeyCodeFromChar has failed.");
|
|
|
|
return 0;
|
|
|
|
}
|
2007-10-11 01:23:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
static PRUint32 ConvertMacToGeckoKeyCode(UInt32 keyCode, nsKeyEvent* aKeyEvent, NSString* characters)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
PRUint32 geckoKeyCode = 0;
|
2007-10-15 05:35:57 -07:00
|
|
|
PRUnichar charCode = 0;
|
2007-06-12 13:28:26 -07:00
|
|
|
if ([characters length])
|
2007-11-26 15:19:04 -08:00
|
|
|
charCode = [characters characterAtIndex:0];
|
2007-06-29 20:26:42 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
switch (keyCode)
|
|
|
|
{
|
|
|
|
// modifiers. We don't get separate events for these
|
|
|
|
case kEscapeKeyCode: geckoKeyCode = NS_VK_ESCAPE; break;
|
2007-11-06 23:40:58 -08:00
|
|
|
case kRCommandKeyCode:
|
2007-06-12 13:28:26 -07:00
|
|
|
case kCommandKeyCode: geckoKeyCode = NS_VK_META; break;
|
2007-11-06 23:40:58 -08:00
|
|
|
case kRShiftKeyCode:
|
2007-06-12 13:28:26 -07:00
|
|
|
case kShiftKeyCode: geckoKeyCode = NS_VK_SHIFT; break;
|
|
|
|
case kCapsLockKeyCode: geckoKeyCode = NS_VK_CAPS_LOCK; break;
|
2007-11-06 23:40:58 -08:00
|
|
|
case kRControlKeyCode:
|
2007-06-12 13:28:26 -07:00
|
|
|
case kControlKeyCode: geckoKeyCode = NS_VK_CONTROL; break;
|
2007-11-06 23:40:58 -08:00
|
|
|
case kROptionKeyCode:
|
2007-06-12 13:28:26 -07:00
|
|
|
case kOptionkeyCode: geckoKeyCode = NS_VK_ALT; break;
|
|
|
|
case kClearKeyCode: geckoKeyCode = NS_VK_CLEAR; break;
|
2007-06-29 20:26:42 -07:00
|
|
|
|
|
|
|
// function keys
|
2007-06-12 13:28:26 -07:00
|
|
|
case kF1KeyCode: geckoKeyCode = NS_VK_F1; break;
|
|
|
|
case kF2KeyCode: geckoKeyCode = NS_VK_F2; break;
|
|
|
|
case kF3KeyCode: geckoKeyCode = NS_VK_F3; break;
|
|
|
|
case kF4KeyCode: geckoKeyCode = NS_VK_F4; break;
|
|
|
|
case kF5KeyCode: geckoKeyCode = NS_VK_F5; break;
|
|
|
|
case kF6KeyCode: geckoKeyCode = NS_VK_F6; break;
|
|
|
|
case kF7KeyCode: geckoKeyCode = NS_VK_F7; break;
|
|
|
|
case kF8KeyCode: geckoKeyCode = NS_VK_F8; break;
|
|
|
|
case kF9KeyCode: geckoKeyCode = NS_VK_F9; break;
|
|
|
|
case kF10KeyCode: geckoKeyCode = NS_VK_F10; break;
|
|
|
|
case kF11KeyCode: geckoKeyCode = NS_VK_F11; break;
|
|
|
|
case kF12KeyCode: geckoKeyCode = NS_VK_F12; break;
|
2007-06-29 20:26:42 -07:00
|
|
|
// case kF13KeyCode: geckoKeyCode = NS_VK_F13; break; // clash with the 3 below
|
|
|
|
// case kF14KeyCode: geckoKeyCode = NS_VK_F14; break;
|
|
|
|
// case kF15KeyCode: geckoKeyCode = NS_VK_F15; break;
|
2007-06-12 13:28:26 -07:00
|
|
|
case kPauseKeyCode: geckoKeyCode = NS_VK_PAUSE; break;
|
|
|
|
case kScrollLockKeyCode: geckoKeyCode = NS_VK_SCROLL_LOCK; break;
|
|
|
|
case kPrintScreenKeyCode: geckoKeyCode = NS_VK_PRINTSCREEN; break;
|
2007-06-29 20:26:42 -07:00
|
|
|
|
|
|
|
// keypad
|
2007-06-12 13:28:26 -07:00
|
|
|
case kKeypad0KeyCode: geckoKeyCode = NS_VK_NUMPAD0; break;
|
|
|
|
case kKeypad1KeyCode: geckoKeyCode = NS_VK_NUMPAD1; break;
|
|
|
|
case kKeypad2KeyCode: geckoKeyCode = NS_VK_NUMPAD2; break;
|
|
|
|
case kKeypad3KeyCode: geckoKeyCode = NS_VK_NUMPAD3; break;
|
|
|
|
case kKeypad4KeyCode: geckoKeyCode = NS_VK_NUMPAD4; break;
|
|
|
|
case kKeypad5KeyCode: geckoKeyCode = NS_VK_NUMPAD5; break;
|
|
|
|
case kKeypad6KeyCode: geckoKeyCode = NS_VK_NUMPAD6; break;
|
|
|
|
case kKeypad7KeyCode: geckoKeyCode = NS_VK_NUMPAD7; break;
|
|
|
|
case kKeypad8KeyCode: geckoKeyCode = NS_VK_NUMPAD8; break;
|
|
|
|
case kKeypad9KeyCode: geckoKeyCode = NS_VK_NUMPAD9; break;
|
2007-06-29 20:26:42 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
case kKeypadMultiplyKeyCode: geckoKeyCode = NS_VK_MULTIPLY; break;
|
|
|
|
case kKeypadAddKeyCode: geckoKeyCode = NS_VK_ADD; break;
|
|
|
|
case kKeypadSubtractKeyCode: geckoKeyCode = NS_VK_SUBTRACT; break;
|
|
|
|
case kKeypadDecimalKeyCode: geckoKeyCode = NS_VK_DECIMAL; break;
|
|
|
|
case kKeypadDivideKeyCode: geckoKeyCode = NS_VK_DIVIDE; break;
|
2007-06-29 20:26:42 -07:00
|
|
|
|
|
|
|
// these may clash with forward delete and help
|
2007-06-12 13:28:26 -07:00
|
|
|
case kInsertKeyCode: geckoKeyCode = NS_VK_INSERT; break;
|
|
|
|
case kDeleteKeyCode: geckoKeyCode = NS_VK_DELETE; break;
|
2007-06-29 20:26:42 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
case kBackspaceKeyCode: geckoKeyCode = NS_VK_BACK; break;
|
|
|
|
case kTabKeyCode: geckoKeyCode = NS_VK_TAB; break;
|
|
|
|
case kHomeKeyCode: geckoKeyCode = NS_VK_HOME; break;
|
|
|
|
case kEndKeyCode: geckoKeyCode = NS_VK_END; break;
|
|
|
|
case kPageUpKeyCode: geckoKeyCode = NS_VK_PAGE_UP; break;
|
|
|
|
case kPageDownKeyCode: geckoKeyCode = NS_VK_PAGE_DOWN; break;
|
|
|
|
case kLeftArrowKeyCode: geckoKeyCode = NS_VK_LEFT; break;
|
|
|
|
case kRightArrowKeyCode: geckoKeyCode = NS_VK_RIGHT; break;
|
|
|
|
case kUpArrowKeyCode: geckoKeyCode = NS_VK_UP; break;
|
|
|
|
case kDownArrowKeyCode: geckoKeyCode = NS_VK_DOWN; break;
|
2007-06-29 20:26:42 -07:00
|
|
|
case kVK_ANSI_1: geckoKeyCode = NS_VK_1; break;
|
|
|
|
case kVK_ANSI_2: geckoKeyCode = NS_VK_2; break;
|
|
|
|
case kVK_ANSI_3: geckoKeyCode = NS_VK_3; break;
|
|
|
|
case kVK_ANSI_4: geckoKeyCode = NS_VK_4; break;
|
|
|
|
case kVK_ANSI_5: geckoKeyCode = NS_VK_5; break;
|
|
|
|
case kVK_ANSI_6: geckoKeyCode = NS_VK_6; break;
|
|
|
|
case kVK_ANSI_7: geckoKeyCode = NS_VK_7; break;
|
|
|
|
case kVK_ANSI_8: geckoKeyCode = NS_VK_8; break;
|
|
|
|
case kVK_ANSI_9: geckoKeyCode = NS_VK_9; break;
|
|
|
|
case kVK_ANSI_0: geckoKeyCode = NS_VK_0; break;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
default:
|
|
|
|
// if we haven't gotten the key code already, look at the char code
|
2007-10-11 01:23:10 -07:00
|
|
|
geckoKeyCode = GetGeckoKeyCodeFromChar(charCode);
|
2007-06-12 13:28:26 -07:00
|
|
|
}
|
2007-06-29 20:26:42 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
return geckoKeyCode;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-06-12 13:28:26 -07:00
|
|
|
PRBool isSpecial;
|
|
|
|
|
|
|
|
// this table is used to determine which keys are special and should not generate a charCode
|
|
|
|
switch (macKeyCode)
|
|
|
|
{
|
|
|
|
// modifiers - we don't get separate events for these yet
|
|
|
|
case kEscapeKeyCode:
|
|
|
|
case kShiftKeyCode:
|
2007-11-06 23:40:58 -08:00
|
|
|
case kRShiftKeyCode:
|
2007-06-12 13:28:26 -07:00
|
|
|
case kCommandKeyCode:
|
2007-11-06 23:40:58 -08:00
|
|
|
case kRCommandKeyCode:
|
2007-06-12 13:28:26 -07:00
|
|
|
case kCapsLockKeyCode:
|
|
|
|
case kControlKeyCode:
|
2007-11-06 23:40:58 -08:00
|
|
|
case kRControlKeyCode:
|
2007-06-12 13:28:26 -07:00
|
|
|
case kOptionkeyCode:
|
2007-11-06 23:40:58 -08:00
|
|
|
case kROptionKeyCode:
|
2007-06-12 13:28:26 -07:00
|
|
|
case kClearKeyCode:
|
|
|
|
|
|
|
|
// function keys
|
|
|
|
case kF1KeyCode:
|
|
|
|
case kF2KeyCode:
|
|
|
|
case kF3KeyCode:
|
|
|
|
case kF4KeyCode:
|
|
|
|
case kF5KeyCode:
|
|
|
|
case kF6KeyCode:
|
|
|
|
case kF7KeyCode:
|
|
|
|
case kF8KeyCode:
|
|
|
|
case kF9KeyCode:
|
|
|
|
case kF10KeyCode:
|
|
|
|
case kF11KeyCode:
|
|
|
|
case kF12KeyCode:
|
|
|
|
case kPauseKeyCode:
|
|
|
|
case kScrollLockKeyCode:
|
|
|
|
case kPrintScreenKeyCode:
|
|
|
|
|
|
|
|
case kInsertKeyCode:
|
|
|
|
case kDeleteKeyCode:
|
|
|
|
case kTabKeyCode:
|
|
|
|
case kBackspaceKeyCode:
|
|
|
|
|
|
|
|
case kHomeKeyCode:
|
|
|
|
case kEndKeyCode:
|
|
|
|
case kPageUpKeyCode:
|
|
|
|
case kPageDownKeyCode:
|
|
|
|
case kLeftArrowKeyCode:
|
|
|
|
case kRightArrowKeyCode:
|
|
|
|
case kUpArrowKeyCode:
|
|
|
|
case kDownArrowKeyCode:
|
|
|
|
case kReturnKeyCode:
|
|
|
|
case kEnterKeyCode:
|
|
|
|
case kPowerbookEnterKeyCode:
|
|
|
|
isSpecial = PR_TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
isSpecial = PR_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return isSpecial;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-30 02:17:50 -07:00
|
|
|
static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
|
|
|
|
{
|
|
|
|
// this is not character inputting event, simply.
|
|
|
|
if (!aEvent.isChar || !aEvent.charCode)
|
|
|
|
return PR_FALSE;
|
|
|
|
// if this is unicode char inputting event, we don't need to check
|
|
|
|
// ctrl/alt/command keys
|
|
|
|
if (aEvent.charCode > 0x7F)
|
|
|
|
return PR_TRUE;
|
|
|
|
// ASCII chars should be inputted without ctrl/alt/command keys
|
|
|
|
return !aEvent.isControl && !aEvent.isAlt && !aEvent.isMeta;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// Basic conversion for cocoa to gecko events, common to all conversions.
|
|
|
|
// Note that it is OK for inEvent to be nil.
|
|
|
|
- (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
NS_ASSERTION(outGeckoEvent, "convertGenericCocoaEvent:toGeckoEvent: requires non-null outGeckoEvent");
|
|
|
|
if (!outGeckoEvent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
outGeckoEvent->widget = [self widget];
|
|
|
|
outGeckoEvent->time = PR_IntervalNow();
|
|
|
|
outGeckoEvent->nativeMsg = inEvent;
|
|
|
|
|
|
|
|
if (inEvent) {
|
|
|
|
unsigned int modifiers = [inEvent modifierFlags];
|
|
|
|
outGeckoEvent->isShift = ((modifiers & NSShiftKeyMask) != 0);
|
|
|
|
outGeckoEvent->isControl = ((modifiers & NSControlKeyMask) != 0);
|
|
|
|
outGeckoEvent->isAlt = ((modifiers & NSAlternateKeyMask) != 0);
|
|
|
|
outGeckoEvent->isMeta = ((modifiers & NSCommandKeyMask) != 0);
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-06-12 13:28:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
NS_ASSERTION(aMouseEvent && outGeckoEvent, "convertCocoaMouseEvent:toGeckoEvent: requires non-null arguments");
|
|
|
|
if (!aMouseEvent || !outGeckoEvent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
[self convertGenericCocoaEvent:aMouseEvent toGeckoEvent:outGeckoEvent];
|
|
|
|
|
|
|
|
// convert point to view coordinate system
|
|
|
|
NSPoint localPoint = [self convertPoint:[aMouseEvent locationInWindow] fromView:nil];
|
2007-07-08 00:08:04 -07:00
|
|
|
outGeckoEvent->refPoint.x = static_cast<nscoord>(localPoint.x);
|
|
|
|
outGeckoEvent->refPoint.y = static_cast<nscoord>(localPoint.y);
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-06-12 13:28:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-05-07 14:54:21 -07:00
|
|
|
#define CHARCODE_MASK_1 0x00FF0000
|
|
|
|
#define CHARCODE_MASK_2 0x000000FF
|
|
|
|
#define CHARCODE_MASK 0x00FF00FF
|
|
|
|
//#define DEBUG_KB 1
|
|
|
|
|
|
|
|
static PRUint32
|
|
|
|
KeyTranslateToUnicode(Handle aHandle, UInt32 aKeyCode, UInt32 aModifiers,
|
|
|
|
TextEncoding aEncoding)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_KB
|
|
|
|
NSLog(@"**** KeyTranslateToUnicode: aHandle: %p, aKeyCode: %X, aModifiers: %X, aEncoding: %X",
|
|
|
|
aHandle, aKeyCode, aModifiers, aEncoding);
|
|
|
|
PRBool isShift = aModifiers & shiftKey;
|
|
|
|
PRBool isCtrl = aModifiers & controlKey;
|
|
|
|
PRBool isOpt = aModifiers & optionKey;
|
|
|
|
PRBool isCmd = aModifiers & cmdKey;
|
|
|
|
PRBool isCL = aModifiers & alphaLock;
|
|
|
|
PRBool isNL = aModifiers & kEventKeyModifierNumLockMask;
|
|
|
|
NSLog(@" Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
|
|
|
|
isShift ? "ON" : "off", isCtrl ? "ON" : "off", isOpt ? "ON" : "off",
|
|
|
|
isCmd ? "ON" : "off", isCL ? "ON" : "off", isNL ? "ON" : "off");
|
|
|
|
#endif
|
|
|
|
UInt32 state = 0;
|
|
|
|
UInt32 val =
|
|
|
|
::KeyTranslate(aHandle, aKeyCode | aModifiers, &state) & CHARCODE_MASK;
|
|
|
|
// If state is not zero, it is in dead key state. Then, we need to recall
|
|
|
|
// KeyTranslate for getting the actual character.
|
|
|
|
if (state) {
|
|
|
|
val =
|
|
|
|
::KeyTranslate(aHandle, aKeyCode | aModifiers, &state) & CHARCODE_MASK;
|
|
|
|
}
|
|
|
|
PRUint32 ch = 0;
|
|
|
|
UInt8 buf[2];
|
|
|
|
CFIndex len = 0;
|
|
|
|
if (val & CHARCODE_MASK_1)
|
|
|
|
buf[len++] = (val & CHARCODE_MASK_1) >> 16;
|
|
|
|
buf[len++] = val & CHARCODE_MASK_2;
|
|
|
|
|
|
|
|
CFStringRef str =
|
|
|
|
::CFStringCreateWithBytes(kCFAllocatorDefault, buf, len,
|
|
|
|
(CFStringEncoding)aEncoding, false);
|
|
|
|
ch = ::CFStringGetLength(str) == 1 ?
|
|
|
|
::CFStringGetCharacterAtIndex(str, 0) : 0;
|
|
|
|
::CFRelease(str);
|
|
|
|
#ifdef DEBUG_KB
|
|
|
|
NSLog(@" result: %X(%C)", ch, ch > ' ' ? ch : ' ');
|
|
|
|
#endif
|
|
|
|
return ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRUint32
|
2008-05-08 15:03:46 -07:00
|
|
|
UCKeyTranslateToUnicode(const UCKeyboardLayout* aHandle, UInt32 aKeyCode, UInt32 aModifiers,
|
2008-05-07 14:54:21 -07:00
|
|
|
UInt32 aKbType)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_KB
|
|
|
|
NSLog(@"**** UCKeyTranslateToUnicode: aHandle: %p, aKeyCode: %X, aModifiers: %X, aKbType: %X",
|
|
|
|
aHandle, aKeyCode, aModifiers, aKbType);
|
|
|
|
PRBool isShift = aModifiers & shiftKey;
|
|
|
|
PRBool isCtrl = aModifiers & controlKey;
|
|
|
|
PRBool isOpt = aModifiers & optionKey;
|
|
|
|
PRBool isCmd = aModifiers & cmdKey;
|
|
|
|
PRBool isCL = aModifiers & alphaLock;
|
|
|
|
PRBool isNL = aModifiers & kEventKeyModifierNumLockMask;
|
|
|
|
NSLog(@" Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
|
|
|
|
isShift ? "ON" : "off", isCtrl ? "ON" : "off", isOpt ? "ON" : "off",
|
|
|
|
isCmd ? "ON" : "off", isCL ? "ON" : "off", isNL ? "ON" : "off");
|
|
|
|
#endif
|
|
|
|
UInt32 deadKeyState = 0;
|
|
|
|
UniCharCount len;
|
|
|
|
UniChar chars[5];
|
|
|
|
OSStatus err = ::UCKeyTranslate(aHandle, aKeyCode,
|
|
|
|
kUCKeyActionDown, aModifiers >> 8,
|
|
|
|
aKbType, kUCKeyTranslateNoDeadKeysMask,
|
|
|
|
&deadKeyState, 5, &len, chars);
|
|
|
|
PRUint32 ch = (err == noErr && len == 1) ? PRUint32(chars[0]) : 0;
|
|
|
|
#ifdef DEBUG_KB
|
|
|
|
NSLog(@" result: %X(%C)", ch, ch > ' ' ? ch : ' ');
|
|
|
|
#endif
|
|
|
|
return ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct KeyTranslateData {
|
|
|
|
KeyTranslateData() {
|
|
|
|
mUchr.mLayout = nsnull;
|
|
|
|
mUchr.mKbType = 0;
|
|
|
|
mKchr.mHandle = nsnull;
|
|
|
|
mKchr.mEncoding = nsnull;
|
|
|
|
}
|
|
|
|
|
2008-07-07 19:19:56 -07:00
|
|
|
// The script of the layout determines the encoding of characters obtained
|
|
|
|
// from kchr resources.
|
2008-05-07 14:54:21 -07:00
|
|
|
SInt16 mScript;
|
2008-07-07 19:19:56 -07:00
|
|
|
// The keyboard layout identifier
|
2008-05-07 14:54:21 -07:00
|
|
|
SInt32 mLayoutID;
|
|
|
|
|
|
|
|
struct {
|
2008-05-08 15:03:46 -07:00
|
|
|
const UCKeyboardLayout* mLayout;
|
2008-05-07 14:54:21 -07:00
|
|
|
UInt32 mKbType;
|
|
|
|
} mUchr;
|
|
|
|
struct {
|
|
|
|
Handle mHandle;
|
|
|
|
TextEncoding mEncoding;
|
|
|
|
} mKchr;
|
|
|
|
};
|
|
|
|
|
|
|
|
static PRUint32
|
|
|
|
GetUniCharFromKeyTranslate(KeyTranslateData& aData,
|
|
|
|
UInt32 aKeyCode, UInt32 aModifiers)
|
|
|
|
{
|
|
|
|
if (aData.mUchr.mLayout) {
|
|
|
|
return UCKeyTranslateToUnicode(aData.mUchr.mLayout, aKeyCode, aModifiers,
|
|
|
|
aData.mUchr.mKbType);
|
|
|
|
}
|
|
|
|
if (aData.mKchr.mHandle) {
|
|
|
|
return KeyTranslateToUnicode(aData.mKchr.mHandle, aKeyCode, aModifiers,
|
|
|
|
aData.mKchr.mEncoding);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SInt32
|
|
|
|
GetScriptFromKeyboardLayout(SInt32 aLayoutID)
|
|
|
|
{
|
|
|
|
switch (aLayoutID) {
|
|
|
|
case 3: // German
|
|
|
|
case -2: return smRoman; // US-Extended
|
|
|
|
case -18944: return smGreek; // Greek
|
|
|
|
default: NS_NOTREACHED("unknown keyboard layout");
|
|
|
|
}
|
|
|
|
return smRoman;
|
|
|
|
}
|
|
|
|
|
2008-05-09 16:14:57 -07:00
|
|
|
static CFStringRef
|
|
|
|
GetInputSourceIDFromKeyboardLayout(SInt32 aLayoutID)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(nsToolkit::OnLeopardOrLater() &&
|
|
|
|
Leopard_TISCopyCurrentKeyboardLayoutInputSource &&
|
|
|
|
Leopard_TISGetInputSourceProperty &&
|
|
|
|
Leopard_TISCreateInputSourceList &&
|
|
|
|
kOurTISPropertyUnicodeKeyLayoutData &&
|
|
|
|
kOurTISPropertyInputSourceID,
|
|
|
|
"GetInputSourceIDFromKeyboardLayout should only be used on Leopard or later.");
|
|
|
|
|
|
|
|
KeyboardLayoutRef keylayout;
|
|
|
|
if (KLGetKeyboardLayoutWithIdentifier(aLayoutID, &keylayout) != noErr)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
const void* uchrFromID;
|
|
|
|
if (KLGetKeyboardLayoutProperty(keylayout, kKLuchrData, &uchrFromID) != noErr)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, NULL, NULL);
|
|
|
|
CFArrayRef inputSources = Leopard_TISCreateInputSourceList(dict, true);
|
|
|
|
CFRelease(dict);
|
|
|
|
|
|
|
|
CFStringRef sourceID = nsnull;
|
|
|
|
for (CFIndex i = 0; i < CFArrayGetCount(inputSources); ++i) {
|
|
|
|
TISInputSourceRef tis = static_cast<TISInputSourceRef>(const_cast<void *>(CFArrayGetValueAtIndex(inputSources, i)));
|
|
|
|
CFDataRef data = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
|
|
|
|
if (!data)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const UCKeyboardLayout* uchr = reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(data));
|
|
|
|
if (uchr == uchrFromID) {
|
|
|
|
sourceID = static_cast<CFStringRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyInputSourceID));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CFRelease(inputSources);
|
|
|
|
|
|
|
|
return sourceID;
|
|
|
|
}
|
|
|
|
|
2008-05-08 17:01:29 -07:00
|
|
|
static PRUint32
|
|
|
|
GetUSLayoutCharFromKeyTranslate(UInt32 aKeyCode, UInt32 aModifiers)
|
|
|
|
{
|
|
|
|
KeyTranslateData kt;
|
|
|
|
Handle handle = ::GetResource('uchr', kKLUSKeyboard); // US keyboard layout
|
|
|
|
if (!handle || !(*handle)) {
|
|
|
|
NS_ERROR("US keyboard layout doesn't have uchr resource");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
UInt32 kbType = 40; // ANSI, don't use actual layout
|
|
|
|
return UCKeyTranslateToUnicode((UCKeyboardLayout*)(*handle), aKeyCode,
|
|
|
|
aModifiers, kbType);
|
|
|
|
}
|
2008-05-07 14:54:21 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
- (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
NS_ASSERTION(aKeyEvent && outGeckoEvent, "convertCocoaKeyEvent:toGeckoEvent: requires non-null arguments");
|
|
|
|
if (!aKeyEvent || !outGeckoEvent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
[self convertGenericCocoaEvent:aKeyEvent toGeckoEvent:outGeckoEvent];
|
|
|
|
|
|
|
|
// coords for key events are always 0,0
|
|
|
|
outGeckoEvent->refPoint.x = outGeckoEvent->refPoint.y = 0;
|
|
|
|
|
|
|
|
// Initialize whether or not we are using charCodes to false.
|
|
|
|
outGeckoEvent->isChar = PR_FALSE;
|
|
|
|
|
|
|
|
// Check to see if the message is a key press that does not involve
|
|
|
|
// one of our special key codes.
|
|
|
|
if (outGeckoEvent->message == NS_KEY_PRESS && !IsSpecialGeckoKey([aKeyEvent keyCode])) {
|
|
|
|
outGeckoEvent->isChar = PR_TRUE; // this is not a special key
|
|
|
|
|
|
|
|
outGeckoEvent->charCode = 0;
|
2008-02-28 21:47:41 -08:00
|
|
|
outGeckoEvent->keyCode = 0; // not set for key press events
|
2007-06-12 13:28:26 -07:00
|
|
|
|
2008-04-14 21:16:24 -07:00
|
|
|
NSString* chars = [aKeyEvent characters];
|
|
|
|
if ([chars length] > 0)
|
|
|
|
outGeckoEvent->charCode = [chars characterAtIndex:0];
|
2007-06-12 13:28:26 -07:00
|
|
|
|
|
|
|
// convert control-modified charCode to raw charCode (with appropriate case)
|
|
|
|
if (outGeckoEvent->isControl && outGeckoEvent->charCode <= 26)
|
|
|
|
outGeckoEvent->charCode += (outGeckoEvent->isShift) ? ('A' - 1) : ('a' - 1);
|
2008-07-07 19:19:56 -07:00
|
|
|
|
|
|
|
// Accel and access key handling needs to know the characters that this
|
|
|
|
// key produces with Shift up or down. So, provide this information
|
|
|
|
// when Ctrl or Command or Alt is pressed.
|
2008-04-19 08:09:50 -07:00
|
|
|
if (outGeckoEvent->isControl || outGeckoEvent->isMeta ||
|
|
|
|
outGeckoEvent->isAlt) {
|
2008-05-07 14:54:21 -07:00
|
|
|
KeyTranslateData kt;
|
2008-05-08 15:03:46 -07:00
|
|
|
|
2008-05-09 16:14:57 -07:00
|
|
|
if (gOverrideKeyboardLayout.mOverrideEnabled) {
|
|
|
|
kt.mLayoutID = gOverrideKeyboardLayout.mKeyboardLayout;
|
|
|
|
kt.mScript = GetScriptFromKeyboardLayout(kt.mLayoutID);
|
|
|
|
} else {
|
2008-07-07 19:19:56 -07:00
|
|
|
// GetScriptManagerVariable and GetScriptVariable are both deprecated.
|
|
|
|
// KLGetCurrentKeyboardLayout is newer but also deprecated in OS X
|
|
|
|
// 10.5. It's not clear from the documentation but it seems that
|
|
|
|
// KLGetKeyboardLayoutProperty with kKLGroupIdentifier may provide the
|
|
|
|
// script identifier for a KeyboardLayoutRef (bug 432388 comment 6).
|
|
|
|
// The "Text Input Source Services" API is not available prior to OS X
|
|
|
|
// 10.5.
|
2008-05-09 16:14:57 -07:00
|
|
|
kt.mScript = ::GetScriptManagerVariable(smKeyScript);
|
|
|
|
kt.mLayoutID = ::GetScriptVariable(kt.mScript, smScriptKeys);
|
|
|
|
}
|
|
|
|
|
2008-05-08 15:03:46 -07:00
|
|
|
CFDataRef uchr = NULL;
|
2008-07-07 19:19:56 -07:00
|
|
|
// GetResource('uchr', kt.mLayoutID) fails on OS X 10.5
|
2008-05-08 15:03:46 -07:00
|
|
|
if (nsToolkit::OnLeopardOrLater() &&
|
|
|
|
Leopard_TISCopyCurrentKeyboardLayoutInputSource &&
|
|
|
|
Leopard_TISGetInputSourceProperty &&
|
2008-05-09 16:14:57 -07:00
|
|
|
Leopard_TISCreateInputSourceList &&
|
|
|
|
kOurTISPropertyUnicodeKeyLayoutData &&
|
|
|
|
kOurTISPropertyInputSourceID) {
|
|
|
|
if (gOverrideKeyboardLayout.mOverrideEnabled) {
|
|
|
|
CFStringRef sourceID = GetInputSourceIDFromKeyboardLayout(kt.mLayoutID);
|
|
|
|
NS_ASSERTION(sourceID, "unable to map keyboard layout ID to input source ID");
|
|
|
|
const void* keys[] = { kOurTISPropertyInputSourceID };
|
|
|
|
const void* vals[] = { sourceID };
|
|
|
|
CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 1, NULL, NULL);
|
|
|
|
CFArrayRef inputSources = Leopard_TISCreateInputSourceList(dict, true);
|
|
|
|
CFRelease(dict);
|
|
|
|
if (CFArrayGetCount(inputSources) == 1) {
|
|
|
|
TISInputSourceRef tis = static_cast<TISInputSourceRef>(const_cast<void *>(CFArrayGetValueAtIndex(inputSources, 0)));
|
|
|
|
uchr = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
|
|
|
|
}
|
|
|
|
CFRelease(inputSources);
|
|
|
|
} else {
|
|
|
|
TISInputSourceRef tis = Leopard_TISCopyCurrentKeyboardLayoutInputSource();
|
|
|
|
uchr = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
|
|
|
|
}
|
2008-05-08 15:03:46 -07:00
|
|
|
}
|
|
|
|
|
2008-07-07 19:19:56 -07:00
|
|
|
// This fails for Azeri on 10.4 even though kKLKind (2) indicates that
|
|
|
|
// the layout has a uchr resource. Perhaps KLGetKeyboardLayoutProperty
|
|
|
|
// with kKLuchrData would be helpful here.
|
2008-05-07 14:54:21 -07:00
|
|
|
Handle handle = ::GetResource('uchr', kt.mLayoutID);
|
2008-05-08 15:03:46 -07:00
|
|
|
if (uchr) {
|
2008-07-07 19:19:56 -07:00
|
|
|
// We should be here on OS X 10.5 for any Apple provided layout, as
|
|
|
|
// they are all uchr. It may be possible to still use kchr resources
|
|
|
|
// from elsewhere, so they may be picked by
|
|
|
|
// GetScriptManagerVariable(smKCHRCache) below
|
2008-05-08 15:03:46 -07:00
|
|
|
kt.mUchr.mLayout = reinterpret_cast<const UCKeyboardLayout*>
|
|
|
|
(CFDataGetBytePtr(uchr));
|
|
|
|
} else if (handle) {
|
2008-07-07 19:19:56 -07:00
|
|
|
// uchr (Unicode) keyboard layout resource prior to 10.5.
|
2008-05-07 14:54:21 -07:00
|
|
|
kt.mUchr.mLayout = *((UCKeyboardLayout**)handle);
|
2008-05-05 16:01:07 -07:00
|
|
|
} else {
|
2008-07-07 19:19:56 -07:00
|
|
|
// kchr (non-Unicode) keyboard layout resource.
|
|
|
|
|
|
|
|
// There are no know cases where GetResource succeeds here, and so
|
|
|
|
// tests (gOverrideKeyboardLayout.mOverrideEnabled) currently end up
|
|
|
|
// with no keyboard layout. KLGetKeyboardLayoutWithIdentifier and
|
|
|
|
// KLGetKeyboardLayoutProperty with kKLKCHRData would be useful here.
|
2008-05-07 14:54:21 -07:00
|
|
|
kt.mKchr.mHandle = ::GetResource('kchr', kt.mLayoutID);
|
2008-07-07 19:19:56 -07:00
|
|
|
|
2008-05-09 16:14:57 -07:00
|
|
|
if (!kt.mKchr.mHandle && !gOverrideKeyboardLayout.mOverrideEnabled)
|
2008-05-07 14:54:21 -07:00
|
|
|
kt.mKchr.mHandle = (char**)::GetScriptManagerVariable(smKCHRCache);
|
|
|
|
if (kt.mKchr.mHandle) {
|
|
|
|
OSStatus err =
|
|
|
|
::GetTextEncodingFromScriptInfo(kt.mScript, kTextLanguageDontCare,
|
|
|
|
kTextRegionDontCare,
|
|
|
|
&kt.mKchr.mEncoding);
|
|
|
|
if (err != noErr)
|
|
|
|
kt.mKchr.mHandle = nsnull;
|
2008-05-05 16:01:07 -07:00
|
|
|
}
|
2008-04-14 21:16:24 -07:00
|
|
|
}
|
2008-05-07 14:54:21 -07:00
|
|
|
|
2008-05-09 16:14:57 -07:00
|
|
|
// If a keyboard layout override is set, we also need to force the
|
|
|
|
// keyboard type to something ANSI to avoid test failures on machines
|
|
|
|
// with JIS keyboards (since the pair of keyboard layout and physical
|
|
|
|
// keyboard type form the actual key layout). This assumes that the
|
|
|
|
// test setting the override was written assuming an ANSI keyboard.
|
|
|
|
if (kt.mUchr.mLayout)
|
|
|
|
kt.mUchr.mKbType = gOverrideKeyboardLayout.mOverrideEnabled ? 40 : ::LMGetKbdType();
|
|
|
|
|
2008-05-07 14:54:21 -07:00
|
|
|
UInt32 key = [aKeyEvent keyCode];
|
|
|
|
|
2008-05-08 18:51:46 -07:00
|
|
|
// Caps lock and num lock modifier state:
|
|
|
|
UInt32 lockState = 0;
|
2008-05-08 17:01:29 -07:00
|
|
|
if ([aKeyEvent modifierFlags] & NSAlphaShiftKeyMask)
|
2008-05-08 18:51:46 -07:00
|
|
|
lockState |= alphaLock;
|
2008-05-08 17:01:29 -07:00
|
|
|
if ([aKeyEvent modifierFlags] & NSNumericPadKeyMask)
|
2008-05-08 18:51:46 -07:00
|
|
|
lockState |= kEventKeyModifierNumLockMask;
|
2008-05-08 17:01:29 -07:00
|
|
|
|
2008-05-07 14:54:21 -07:00
|
|
|
// normal chars
|
2008-05-08 18:51:46 -07:00
|
|
|
PRUint32 unshiftedChar = GetUniCharFromKeyTranslate(kt, key, lockState);
|
2008-05-08 18:58:17 -07:00
|
|
|
UInt32 shiftLockMod = shiftKey | lockState;
|
2008-05-08 18:51:46 -07:00
|
|
|
PRUint32 shiftedChar = GetUniCharFromKeyTranslate(kt, key, shiftLockMod);
|
|
|
|
|
|
|
|
// characters generated with Cmd key
|
|
|
|
// XXX we should remove CapsLock state, which changes characters from
|
|
|
|
// Latin to Cyrillic with Russian layout on 10.4 only when Cmd key
|
2008-05-08 17:01:29 -07:00
|
|
|
// is pressed.
|
2008-05-08 18:56:36 -07:00
|
|
|
UInt32 numState = (lockState & ~alphaLock); // only num lock state
|
2008-05-08 18:51:46 -07:00
|
|
|
PRUint32 uncmdedChar = GetUniCharFromKeyTranslate(kt, key, numState);
|
2008-05-09 16:10:40 -07:00
|
|
|
UInt32 shiftNumMod = numState | shiftKey;
|
|
|
|
PRUint32 uncmdedShiftChar =
|
|
|
|
GetUniCharFromKeyTranslate(kt, key, shiftNumMod);
|
2008-05-08 18:51:46 -07:00
|
|
|
PRUint32 uncmdedUSChar = GetUSLayoutCharFromKeyTranslate(key, numState);
|
2008-05-08 18:56:36 -07:00
|
|
|
UInt32 cmdNumMod = cmdKey | numState;
|
2008-05-08 18:51:46 -07:00
|
|
|
PRUint32 cmdedChar = GetUniCharFromKeyTranslate(kt, key, cmdNumMod);
|
2008-05-08 18:56:36 -07:00
|
|
|
UInt32 cmdShiftNumMod = shiftKey | cmdNumMod;
|
2008-05-08 18:51:46 -07:00
|
|
|
PRUint32 cmdedShiftChar =
|
|
|
|
GetUniCharFromKeyTranslate(kt, key, cmdShiftNumMod);
|
2008-05-07 14:54:21 -07:00
|
|
|
|
|
|
|
// Is the keyboard layout changed by Cmd key?
|
|
|
|
// E.g., Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
|
2008-05-08 17:01:29 -07:00
|
|
|
PRBool isCmdSwitchLayout = uncmdedChar != cmdedChar;
|
2008-05-07 14:54:21 -07:00
|
|
|
// Is the keyboard layout for Latin, but Cmd key switches the layout?
|
|
|
|
// I.e., Dvorak-QWERTY
|
2008-05-08 17:01:29 -07:00
|
|
|
PRBool isDvorakQWERTY = isCmdSwitchLayout && kt.mScript == smRoman;
|
2008-05-07 14:54:21 -07:00
|
|
|
|
|
|
|
// If the current keyboard is not Dvorak-QWERTY or Cmd is not pressed,
|
|
|
|
// we should append unshiftedChar and shiftedChar for handling the
|
2008-07-07 19:19:56 -07:00
|
|
|
// normal characters. These are the characters that the user is most
|
|
|
|
// likely to associate with this key.
|
2008-04-14 21:16:24 -07:00
|
|
|
if ((unshiftedChar || shiftedChar) &&
|
2008-05-07 14:54:21 -07:00
|
|
|
(!outGeckoEvent->isMeta || !isDvorakQWERTY)) {
|
2008-04-14 21:16:24 -07:00
|
|
|
nsAlternativeCharCode altCharCodes(unshiftedChar, shiftedChar);
|
|
|
|
outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
|
|
|
|
}
|
2008-05-08 17:01:29 -07:00
|
|
|
|
2008-07-07 19:19:56 -07:00
|
|
|
// Most keyboard layouts provide the same characters in the NSEvents
|
|
|
|
// with Command+Shift as with Command. However, with Command+Shift we
|
|
|
|
// want the character on the second level. e.g. With a US QWERTY
|
|
|
|
// layout, we want "?" when the "/","?" key is pressed with
|
|
|
|
// Command+Shift.
|
2008-05-09 16:10:40 -07:00
|
|
|
|
|
|
|
// On a German layout, the OS gives us '/' with Cmd+Shift+SS(eszett)
|
|
|
|
// even though Cmd+SS is 'SS' and Shift+'SS' is '?'. This '/' seems
|
|
|
|
// like a hack to make the Cmd+"?" event look the same as the Cmd+"?"
|
|
|
|
// event on a US keyboard. The user thinks they are typing Cmd+"?", so
|
|
|
|
// we'll prefer the "?" character, replacing charCode with shiftedChar
|
|
|
|
// when Shift is pressed. However, in case there is a layout where the
|
|
|
|
// character unique to Cmd+Shift is the character that the user expects,
|
|
|
|
// we'll send it as an alternative char.
|
|
|
|
PRBool hasCmdShiftOnlyChar =
|
|
|
|
cmdedChar != cmdedShiftChar && uncmdedShiftChar != cmdedShiftChar;
|
|
|
|
PRUint32 originalCmdedShiftChar = cmdedShiftChar;
|
|
|
|
|
2008-07-07 19:19:56 -07:00
|
|
|
// If we can make a good guess at the characters that the user would
|
|
|
|
// expect this key combination to produce (with and without Shift) then
|
|
|
|
// use those characters. This also corrects for CapsLock, which was
|
|
|
|
// ignored above.
|
2008-05-08 17:01:29 -07:00
|
|
|
if (!isCmdSwitchLayout) {
|
2008-07-07 19:19:56 -07:00
|
|
|
// The characters produced with Command seem similar to those without
|
|
|
|
// Command.
|
2008-05-08 18:51:46 -07:00
|
|
|
if (unshiftedChar)
|
|
|
|
cmdedChar = unshiftedChar;
|
2008-05-09 16:10:40 -07:00
|
|
|
if (shiftedChar)
|
2008-05-08 17:01:29 -07:00
|
|
|
cmdedShiftChar = shiftedChar;
|
|
|
|
} else if (uncmdedUSChar == cmdedChar) {
|
2008-07-07 19:19:56 -07:00
|
|
|
// It looks like characters from a US layout are provided when Command
|
|
|
|
// is down.
|
2008-05-08 18:51:46 -07:00
|
|
|
PRUint32 ch = GetUSLayoutCharFromKeyTranslate(key, lockState);
|
2008-05-08 17:01:29 -07:00
|
|
|
if (ch)
|
|
|
|
cmdedChar = ch;
|
2008-05-08 18:51:46 -07:00
|
|
|
ch = GetUSLayoutCharFromKeyTranslate(key, shiftLockMod);
|
2008-05-09 16:10:40 -07:00
|
|
|
if (ch)
|
2008-05-08 18:51:46 -07:00
|
|
|
cmdedShiftChar = ch;
|
2008-05-08 17:01:29 -07:00
|
|
|
}
|
|
|
|
|
2008-07-07 19:19:56 -07:00
|
|
|
// Only charCode (not alternativeCharCodes) is available to javascript,
|
|
|
|
// so attempt to set this to the most likely intended (or most useful)
|
|
|
|
// character. Note that cmdedChar and cmdedShiftChar are usually
|
|
|
|
// Latin/ASCII characters and that is what is wanted here as accel
|
|
|
|
// keys are expected to be Latin characters.
|
|
|
|
//
|
2008-05-08 17:01:29 -07:00
|
|
|
// XXX We should do something similar when Control is down (bug 429510).
|
|
|
|
if (outGeckoEvent->isMeta &&
|
|
|
|
!(outGeckoEvent->isControl || outGeckoEvent->isAlt)) {
|
|
|
|
|
|
|
|
// The character to use for charCode.
|
|
|
|
PRUint32 preferredCharCode = 0;
|
|
|
|
preferredCharCode = outGeckoEvent->isShift ? cmdedShiftChar : cmdedChar;
|
|
|
|
|
|
|
|
if (preferredCharCode) {
|
|
|
|
#ifdef DEBUG_KB
|
|
|
|
if (outGeckoEvent->charCode != preferredCharCode) {
|
|
|
|
NSLog(@" charCode replaced: %X(%C) to %X(%C)",
|
|
|
|
outGeckoEvent->charCode,
|
|
|
|
outGeckoEvent->charCode > ' ' ? outGeckoEvent->charCode : ' ',
|
|
|
|
preferredCharCode,
|
|
|
|
preferredCharCode > ' ' ? preferredCharCode : ' ');
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
outGeckoEvent->charCode = preferredCharCode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-07 14:54:21 -07:00
|
|
|
// If the current keyboard layout is switched by the Cmd key,
|
2008-05-08 17:01:29 -07:00
|
|
|
// we should append cmdedChar and shiftedCmdChar that are
|
2008-05-07 14:54:21 -07:00
|
|
|
// Latin char for the key. But don't append at Dvorak-QWERTY.
|
2008-05-08 17:01:29 -07:00
|
|
|
if ((cmdedChar || cmdedShiftChar) &&
|
2008-05-07 14:54:21 -07:00
|
|
|
isCmdSwitchLayout && !isDvorakQWERTY) {
|
2008-05-08 17:01:29 -07:00
|
|
|
nsAlternativeCharCode altCharCodes(cmdedChar, cmdedShiftChar);
|
2008-05-07 14:54:21 -07:00
|
|
|
outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
|
|
|
|
}
|
2008-05-09 16:10:40 -07:00
|
|
|
// Special case for 'SS' key of German layout. See the comment of
|
|
|
|
// hasCmdShiftOnlyChar definition for the detail.
|
|
|
|
if (hasCmdShiftOnlyChar && originalCmdedShiftChar) {
|
|
|
|
nsAlternativeCharCode altCharCodes(0, originalCmdedShiftChar);
|
|
|
|
outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
|
|
|
|
}
|
2008-04-14 21:16:24 -07:00
|
|
|
}
|
2007-06-12 13:28:26 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
NSString* characters = nil;
|
|
|
|
if ([aKeyEvent type] != NSFlagsChanged)
|
2007-06-29 20:26:42 -07:00
|
|
|
characters = [aKeyEvent charactersIgnoringModifiers];
|
2007-06-12 13:28:26 -07:00
|
|
|
|
|
|
|
outGeckoEvent->keyCode = ConvertMacToGeckoKeyCode([aKeyEvent keyCode], outGeckoEvent, characters);
|
|
|
|
outGeckoEvent->charCode = 0;
|
|
|
|
}
|
|
|
|
|
2008-01-05 15:33:53 -08:00
|
|
|
if (outGeckoEvent->message == NS_KEY_PRESS && !outGeckoEvent->isMeta)
|
2007-06-12 13:28:26 -07:00
|
|
|
[NSCursor setHiddenUntilMouseMoves:YES];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-06-12 13:28:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-06-30 09:30:22 -07:00
|
|
|
// Called from PluginKeyEventsHandler() (a handler for Carbon TSM events) to
|
|
|
|
// process a Carbon key event for the currently focused plugin. Both Unicode
|
|
|
|
// characters and "Mac encoding characters" (in the MBCS or "multibyte
|
|
|
|
// character system") are (or should be) available from aKeyEvent, but here we
|
|
|
|
// use the MCBS characters. This is how the WebKit does things, and seems to
|
|
|
|
// be what plugins expect.
|
|
|
|
- (void) processPluginKeyEvent:(EventRef)aKeyEvent
|
|
|
|
{
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
|
|
|
|
|
|
|
UInt32 numCharCodes;
|
|
|
|
OSStatus status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
|
|
|
|
typeChar, NULL, 0, &numCharCodes, NULL);
|
|
|
|
if (status != noErr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsAutoTArray<unsigned char, 3> charCodes;
|
|
|
|
charCodes.SetLength(numCharCodes);
|
|
|
|
status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
|
|
|
|
typeChar, NULL, numCharCodes, NULL, charCodes.Elements());
|
|
|
|
if (status != noErr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
UInt32 modifiers;
|
|
|
|
status = ::GetEventParameter(aKeyEvent, kEventParamKeyModifiers,
|
|
|
|
typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
|
|
|
|
if (status != noErr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
UInt32 macKeyCode;
|
|
|
|
status = ::GetEventParameter(aKeyEvent, kEventParamKeyCode,
|
|
|
|
typeUInt32, NULL, sizeof(macKeyCode), NULL, &macKeyCode);
|
|
|
|
if (status != noErr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
EventRef cloneEvent = ::CopyEvent(aKeyEvent);
|
|
|
|
for (unsigned int i = 0; i < numCharCodes; ++i) {
|
|
|
|
status = ::SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes,
|
|
|
|
typeChar, 1, charCodes.Elements() + i);
|
|
|
|
if (status != noErr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
EventRecord eventRec;
|
|
|
|
if (::ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
|
|
|
|
nsKeyEvent keyDownEvent(PR_TRUE, NS_KEY_DOWN, mGeckoChild);
|
|
|
|
|
|
|
|
PRUint32 keyCode(ConvertMacToGeckoKeyCode(macKeyCode, &keyDownEvent, @""));
|
|
|
|
PRUint32 charCode(charCodes.ElementAt(i));
|
|
|
|
|
|
|
|
keyDownEvent.time = PR_IntervalNow();
|
|
|
|
keyDownEvent.nativeMsg = &eventRec;
|
|
|
|
if (IsSpecialGeckoKey(macKeyCode)) {
|
|
|
|
keyDownEvent.keyCode = keyCode;
|
|
|
|
} else {
|
|
|
|
keyDownEvent.charCode = charCode;
|
|
|
|
keyDownEvent.isChar = PR_TRUE;
|
|
|
|
}
|
|
|
|
keyDownEvent.isShift = ((modifiers & shiftKey) != 0);
|
|
|
|
keyDownEvent.isControl = ((modifiers & controlKey) != 0);
|
|
|
|
keyDownEvent.isAlt = ((modifiers & optionKey) != 0);
|
|
|
|
keyDownEvent.isMeta = ((modifiers & cmdKey) != 0); // Should never happen
|
|
|
|
mGeckoChild->DispatchWindowEvent(keyDownEvent);
|
|
|
|
if (!mGeckoChild)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
::ReleaseEvent(cloneEvent);
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
- (nsRect)sendCompositionEvent:(PRInt32) aEventType
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_IME
|
|
|
|
NSLog(@"****in sendCompositionEvent; type = %d", aEventType);
|
|
|
|
#endif
|
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return nsRect(0, 0, 0, 0);
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// static void init_composition_event( *aEvent, int aType)
|
|
|
|
nsCompositionEvent event(PR_TRUE, aEventType, mGeckoChild);
|
|
|
|
event.time = PR_IntervalNow();
|
|
|
|
mGeckoChild->DispatchWindowEvent(event);
|
|
|
|
return event.theReply.mCursorPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)sendTextEvent:(PRUnichar*) aBuffer
|
|
|
|
attributedString:(NSAttributedString*) aString
|
|
|
|
selectedRange:(NSRange) selRange
|
|
|
|
markedRange:(NSRange) markRange
|
|
|
|
doCommit:(BOOL) doCommit
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_IME
|
|
|
|
NSLog(@"****in sendTextEvent; string = '%@'", aString);
|
|
|
|
NSLog(@" markRange = %d, %d; selRange = %d, %d", markRange.location, markRange.length, selRange.location, selRange.length);
|
|
|
|
#endif
|
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, mGeckoChild);
|
|
|
|
textEvent.time = PR_IntervalNow();
|
|
|
|
textEvent.theText = aBuffer;
|
|
|
|
if (!doCommit)
|
|
|
|
FillTextRangeInTextEvent(&textEvent, aString, markRange, selRange);
|
|
|
|
|
|
|
|
mGeckoChild->DispatchWindowEvent(textEvent);
|
|
|
|
if (textEvent.rangeArray)
|
|
|
|
delete [] textEvent.rangeArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
// NSTextInput implementation
|
|
|
|
|
|
|
|
#define MAX_BUFFER_SIZE 32
|
|
|
|
|
|
|
|
|
|
|
|
- (void)insertText:(id)insertString
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in insertText: '%@'", insertString);
|
2008-02-19 23:40:04 -08:00
|
|
|
NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
|
2007-06-12 13:28:26 -07:00
|
|
|
#endif
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
2007-06-12 13:28:26 -07:00
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2007-08-16 13:30:50 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
if (![insertString isKindOfClass:[NSAttributedString class]])
|
|
|
|
insertString = [[[NSAttributedString alloc] initWithString:insertString] autorelease];
|
|
|
|
|
|
|
|
NSString *tmpStr = [insertString string];
|
|
|
|
unsigned int len = [tmpStr length];
|
2008-04-15 21:56:51 -07:00
|
|
|
if (!nsTSMManager::IsComposing() && len == 0)
|
2007-08-16 13:30:50 -07:00
|
|
|
return; // nothing to do
|
2007-06-12 13:28:26 -07:00
|
|
|
PRUnichar buffer[MAX_BUFFER_SIZE];
|
|
|
|
PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
|
2007-11-26 15:19:04 -08:00
|
|
|
[tmpStr getCharacters:bufPtr];
|
2007-08-16 13:30:50 -07:00
|
|
|
bufPtr[len] = PRUnichar('\0');
|
2007-06-12 13:28:26 -07:00
|
|
|
|
|
|
|
if (len == 1 && !nsTSMManager::IsComposing()) {
|
2008-03-02 22:29:16 -08:00
|
|
|
// don't let the same event be fired twice when hitting
|
|
|
|
// enter/return! (Bug 420502)
|
|
|
|
if (mKeyPressSent)
|
|
|
|
return;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// dispatch keypress event with char instead of textEvent
|
|
|
|
nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, mGeckoChild);
|
|
|
|
geckoEvent.time = PR_IntervalNow();
|
|
|
|
geckoEvent.charCode = bufPtr[0]; // gecko expects OS-translated unicode
|
2007-10-24 23:51:10 -07:00
|
|
|
geckoEvent.keyCode = 0;
|
2007-06-12 13:28:26 -07:00
|
|
|
geckoEvent.isChar = PR_TRUE;
|
2007-11-26 10:09:38 -08:00
|
|
|
if (mKeyDownHandled)
|
2007-06-12 13:28:26 -07:00
|
|
|
geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
|
|
|
|
// don't set other modifiers from the current event, because here in
|
|
|
|
// -insertText: they've already been taken into account in creating
|
|
|
|
// the input string.
|
|
|
|
|
|
|
|
// create native EventRecord for use by plugins
|
|
|
|
EventRecord macEvent;
|
|
|
|
if (mCurKeyEvent) {
|
2008-03-25 13:44:06 -07:00
|
|
|
// XXX The ASCII characters inputting mode of egbridge (Japanese IME)
|
|
|
|
// might send the keyDown event with wrong keyboard layout if other
|
|
|
|
// keyboard layouts are already loaded. In that case, the native event
|
|
|
|
// doesn't match to this gecko event...
|
2007-06-12 13:28:26 -07:00
|
|
|
ConvertCocoaKeyEventToMacEvent(mCurKeyEvent, macEvent);
|
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
2007-10-11 01:23:10 -07:00
|
|
|
geckoEvent.isShift = ([mCurKeyEvent modifierFlags] & NSShiftKeyMask) != 0;
|
2007-10-24 23:51:10 -07:00
|
|
|
if (!IsPrintableChar(geckoEvent.charCode)) {
|
|
|
|
geckoEvent.keyCode =
|
|
|
|
ConvertMacToGeckoKeyCode([mCurKeyEvent keyCode], &geckoEvent,
|
|
|
|
[mCurKeyEvent charactersIgnoringModifiers]);
|
|
|
|
geckoEvent.charCode = 0;
|
|
|
|
}
|
2007-10-11 01:23:10 -07:00
|
|
|
} else {
|
|
|
|
// Note that insertText is not called only at key pressing.
|
2007-10-24 23:51:10 -07:00
|
|
|
if (!IsPrintableChar(geckoEvent.charCode)) {
|
|
|
|
geckoEvent.keyCode = GetGeckoKeyCodeFromChar(geckoEvent.charCode);
|
|
|
|
geckoEvent.charCode = 0;
|
|
|
|
}
|
2007-06-12 13:28:26 -07:00
|
|
|
}
|
|
|
|
|
2008-03-13 18:08:04 -07:00
|
|
|
mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
|
2008-02-28 21:47:41 -08:00
|
|
|
mKeyPressSent = YES;
|
2007-06-12 13:28:26 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!nsTSMManager::IsComposing()) {
|
2007-11-26 15:19:04 -08:00
|
|
|
[self sendCompositionEvent:NS_COMPOSITION_START];
|
2008-01-15 15:11:55 -08:00
|
|
|
// Note: mGeckoChild might have become null here. Don't count on it from here on.
|
2007-04-15 06:43:55 -07:00
|
|
|
nsTSMManager::StartComposing(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
// Note: mGeckoChild might have become null here. Don't count on it from here on.
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-08-16 13:30:50 -07:00
|
|
|
if (nsTSMManager::IgnoreCommit()) {
|
|
|
|
tmpStr = [tmpStr init];
|
|
|
|
len = 0;
|
|
|
|
bufPtr[0] = PRUnichar('\0');
|
|
|
|
insertString =
|
|
|
|
[[[NSAttributedString alloc] initWithString:tmpStr] autorelease];
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
[self sendTextEvent:bufPtr attributedString:insertString
|
|
|
|
selectedRange:NSMakeRange(0, len)
|
|
|
|
markedRange:mMarkedRange
|
|
|
|
doCommit:YES];
|
2008-01-15 15:11:55 -08:00
|
|
|
// Note: mGeckoChild might have become null here. Don't count on it from here on.
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-26 15:19:04 -08:00
|
|
|
[self sendCompositionEvent:NS_COMPOSITION_END];
|
2008-01-15 15:11:55 -08:00
|
|
|
// Note: mGeckoChild might have become null here. Don't count on it from here on.
|
2007-04-15 06:43:55 -07:00
|
|
|
nsTSMManager::EndComposing();
|
2008-02-19 23:40:04 -08:00
|
|
|
mMarkedRange = NSMakeRange(NSNotFound, 0);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bufPtr != buffer)
|
|
|
|
delete[] bufPtr;
|
2007-08-16 13:30:50 -07:00
|
|
|
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)insertNewline:(id)sender
|
|
|
|
{
|
2008-03-04 14:32:55 -08:00
|
|
|
[self insertText:@"\n"];
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void) doCommandBySelector:(SEL)aSelector
|
2008-02-20 15:47:05 -08:00
|
|
|
{
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
2008-03-04 14:32:55 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#if DEBUG_IME
|
2008-03-13 18:08:04 -07:00
|
|
|
NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mKeyPressHandled);
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
2007-04-21 17:37:27 -07:00
|
|
|
|
2008-03-13 18:08:04 -07:00
|
|
|
if (!mKeyPressHandled)
|
2008-03-04 14:32:55 -08:00
|
|
|
[super doCommandBySelector:aSelector];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in setMarkedText location: %d, length: %d", selRange.location, selRange.length);
|
2008-02-19 23:40:04 -08:00
|
|
|
NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
|
2007-03-22 10:30:00 -07:00
|
|
|
NSLog(@" aString = '%@'", aString);
|
|
|
|
#endif
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2007-08-16 13:30:50 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (![aString isKindOfClass:[NSAttributedString class]])
|
|
|
|
aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
|
|
|
|
|
|
|
|
NSMutableAttributedString *mutableAttribStr = aString;
|
|
|
|
NSString *tmpStr = [mutableAttribStr string];
|
|
|
|
unsigned int len = [tmpStr length];
|
|
|
|
PRUnichar buffer[MAX_BUFFER_SIZE];
|
|
|
|
PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
|
2007-11-26 15:19:04 -08:00
|
|
|
[tmpStr getCharacters:bufPtr];
|
2007-08-16 13:30:50 -07:00
|
|
|
bufPtr[len] = PRUnichar('\0');
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#if DEBUG_IME
|
|
|
|
printf("****in setMarkedText, len = %d, text = ", len);
|
|
|
|
PRUint32 n = 0;
|
|
|
|
PRUint32 maxlen = len > 12 ? 12 : len;
|
2008-02-19 23:40:04 -08:00
|
|
|
for (PRUnichar *a = bufPtr; (*a != PRUnichar('\0')) && n<maxlen; a++, n++)
|
|
|
|
printf((*a&0xff80) ? "\\u%4X" : "%c", *a);
|
2007-03-22 10:30:00 -07:00
|
|
|
printf("\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
mMarkedRange.length = len;
|
|
|
|
|
2008-04-21 20:30:45 -07:00
|
|
|
if (!nsTSMManager::IsComposing() && len > 0) {
|
2008-02-19 23:40:04 -08:00
|
|
|
nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
|
|
|
|
mGeckoChild->DispatchWindowEvent(selection);
|
|
|
|
mMarkedRange.location = selection.mSucceeded ? selection.mReply.mOffset : 0;
|
2007-03-22 10:30:00 -07:00
|
|
|
[self sendCompositionEvent:NS_COMPOSITION_START];
|
2008-01-15 15:11:55 -08:00
|
|
|
// Note: mGeckoChild might have become null here. Don't count on it from here on.
|
2007-04-15 06:43:55 -07:00
|
|
|
nsTSMManager::StartComposing(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
// Note: mGeckoChild might have become null here. Don't count on it from here on.
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-04-21 20:30:45 -07:00
|
|
|
if (nsTSMManager::IsComposing()) {
|
|
|
|
nsTSMManager::UpdateComposing(tmpStr);
|
2007-08-16 13:30:50 -07:00
|
|
|
|
2008-04-21 20:30:45 -07:00
|
|
|
BOOL commit = len == 0;
|
|
|
|
[self sendTextEvent:bufPtr attributedString:aString
|
|
|
|
selectedRange:selRange
|
|
|
|
markedRange:mMarkedRange
|
|
|
|
doCommit:commit];
|
2008-01-15 15:11:55 -08:00
|
|
|
// Note: mGeckoChild might have become null here. Don't count on it from here on.
|
2008-04-21 20:30:45 -07:00
|
|
|
|
|
|
|
if (commit) {
|
|
|
|
[self sendCompositionEvent:NS_COMPOSITION_END];
|
|
|
|
nsTSMManager::EndComposing();
|
|
|
|
}
|
2008-01-15 15:11:55 -08:00
|
|
|
}
|
2008-04-21 20:30:45 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (bufPtr != buffer)
|
|
|
|
delete[] bufPtr;
|
2007-08-16 13:30:50 -07:00
|
|
|
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void) unmarkText
|
|
|
|
{
|
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in unmarkText");
|
|
|
|
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
|
|
|
|
#endif
|
2007-08-16 13:30:50 -07:00
|
|
|
nsTSMManager::CommitIME();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (BOOL) hasMarkedText
|
|
|
|
{
|
2008-02-19 23:40:04 -08:00
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in hasMarkText");
|
|
|
|
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
|
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (long) conversationIdentifier
|
|
|
|
{
|
2008-02-19 23:40:04 -08:00
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in conversationIdentifier");
|
|
|
|
#endif
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return (long)self;
|
|
|
|
nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
|
|
|
|
textContent.InitForQueryTextContent(0, 0);
|
|
|
|
mGeckoChild->DispatchWindowEvent(textContent);
|
|
|
|
if (!textContent.mSucceeded)
|
|
|
|
return (long)self;
|
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@" the ID = %ld", (long)textContent.mReply.mContentsRoot);
|
|
|
|
#endif
|
|
|
|
return (long)textContent.mReply.mContentsRoot;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in attributedSubstringFromRange");
|
|
|
|
NSLog(@" theRange = %d, %d", theRange.location, theRange.length);
|
|
|
|
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
|
|
|
|
#endif
|
2008-02-19 23:40:04 -08:00
|
|
|
if (!mGeckoChild || theRange.length == 0)
|
2007-06-15 15:34:48 -07:00
|
|
|
return nil;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-02-19 23:40:04 -08:00
|
|
|
nsAutoString str;
|
|
|
|
nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
|
|
|
|
textContent.InitForQueryTextContent(theRange.location, theRange.length);
|
|
|
|
mGeckoChild->DispatchWindowEvent(textContent);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-02-19 23:40:04 -08:00
|
|
|
if (!textContent.mSucceeded || textContent.mReply.mString.IsEmpty())
|
|
|
|
return nil;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-05-05 16:01:07 -07:00
|
|
|
NSString* nsstr = ToNSString(textContent.mReply.mString);
|
2008-02-19 23:40:04 -08:00
|
|
|
NSAttributedString* result =
|
|
|
|
[[[NSAttributedString alloc] initWithString:nsstr
|
|
|
|
attributes:nil] autorelease];
|
|
|
|
return result;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSRange) markedRange
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in markedRange");
|
|
|
|
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (![self hasMarkedText]) {
|
|
|
|
return NSMakeRange(NSNotFound, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return mMarkedRange;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSRange) selectedRange
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in selectedRange");
|
|
|
|
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
|
|
|
|
#endif
|
2008-02-19 23:40:04 -08:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return NSMakeRange(NSNotFound, 0);
|
|
|
|
nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
|
|
|
|
mGeckoChild->DispatchWindowEvent(selection);
|
|
|
|
if (!selection.mSucceeded)
|
|
|
|
return NSMakeRange(NSNotFound, 0);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-02-19 23:40:04 -08:00
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@" result of selectedRange = %d, %d",
|
|
|
|
selection.mReply.mOffset, selection.mReply.mString.Length());
|
|
|
|
#endif
|
|
|
|
return NSMakeRange(selection.mReply.mOffset,
|
|
|
|
selection.mReply.mString.Length());
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSRect) firstRectForCharacterRange:(NSRange)theRange
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in firstRectForCharacterRange");
|
|
|
|
NSLog(@" theRange = %d, %d", theRange.location, theRange.length);
|
|
|
|
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
|
|
|
|
#endif
|
2008-02-19 23:40:04 -08:00
|
|
|
// XXX this returns first character rect or caret rect, it is limitation of
|
|
|
|
// now. We need more work for returns first line rect. But current
|
|
|
|
// implementation is enough for IMEs.
|
|
|
|
|
|
|
|
NSRect rect;
|
|
|
|
if (!mGeckoChild || theRange.location == NSNotFound)
|
|
|
|
return rect;
|
|
|
|
|
|
|
|
nsRect r;
|
|
|
|
PRBool useCaretRect = theRange.length == 0;
|
|
|
|
if (!useCaretRect) {
|
|
|
|
nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, mGeckoChild);
|
|
|
|
charRect.InitForQueryCharacterRect(theRange.location);
|
|
|
|
mGeckoChild->DispatchWindowEvent(charRect);
|
|
|
|
if (charRect.mSucceeded)
|
|
|
|
r = charRect.mReply.mRect;
|
|
|
|
else
|
|
|
|
useCaretRect = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (useCaretRect) {
|
|
|
|
nsQueryContentEvent caretRect(PR_TRUE, NS_QUERY_CARET_RECT, mGeckoChild);
|
|
|
|
caretRect.InitForQueryCaretRect(theRange.location);
|
|
|
|
mGeckoChild->DispatchWindowEvent(caretRect);
|
|
|
|
if (!caretRect.mSucceeded)
|
|
|
|
return rect;
|
|
|
|
r = caretRect.mReply.mRect;
|
|
|
|
r.width = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIWidget* rootWidget = mGeckoChild->GetTopLevelWidget();
|
|
|
|
NSWindow* rootWindow =
|
|
|
|
static_cast<NSWindow*>(rootWidget->GetNativeData(NS_NATIVE_WINDOW));
|
|
|
|
NSView* rootView =
|
|
|
|
static_cast<NSView*>(rootWidget->GetNativeData(NS_NATIVE_WIDGET));
|
|
|
|
if (!rootWindow || !rootView)
|
|
|
|
return rect;
|
|
|
|
GeckoRectToNSRect(r, rect);
|
|
|
|
rect = [rootView convertRect:rect toView:nil];
|
|
|
|
rect.origin = [rootWindow convertBaseToScreen:rect.origin];
|
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@" result rect (x,y,w,h) = %f, %f, %f, %f",
|
|
|
|
rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
|
|
|
|
#endif
|
|
|
|
return rect;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRect(0.0, 0.0, 0.0, 0.0));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (unsigned int)characterIndexForPoint:(NSPoint)thePoint
|
|
|
|
{
|
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in characterIndexForPoint");
|
2008-02-19 23:40:04 -08:00
|
|
|
NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// To implement this, we'd have to grovel in text frames looking at text offsets.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSArray*) validAttributesForMarkedText
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#if DEBUG_IME
|
|
|
|
NSLog(@"****in validAttributesForMarkedText");
|
2008-02-19 23:40:04 -08:00
|
|
|
NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
//return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
|
|
|
|
return [NSArray array]; // empty array; we don't support any attributes right now
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
2007-10-22 20:27:48 -07:00
|
|
|
+ (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-10-22 20:27:48 -07:00
|
|
|
NSEvent* newEvent = [NSEvent keyEventWithType:type
|
|
|
|
location:[theEvent locationInWindow]
|
|
|
|
modifierFlags:[theEvent modifierFlags]
|
|
|
|
timestamp:[theEvent timestamp]
|
|
|
|
windowNumber:[theEvent windowNumber]
|
|
|
|
context:[theEvent context]
|
|
|
|
characters:[theEvent characters]
|
|
|
|
charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers]
|
|
|
|
isARepeat:[theEvent isARepeat]
|
|
|
|
keyCode:[theEvent keyCode]];
|
|
|
|
return newEvent;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-10-22 20:27:48 -07:00
|
|
|
}
|
|
|
|
|
2008-05-05 16:01:07 -07:00
|
|
|
#ifdef PR_LOGGING
|
|
|
|
static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
|
|
|
|
{
|
|
|
|
for (PRUint32 i = 0; i < [aString length]; ++i) {
|
|
|
|
unichar ch = [aString characterAtIndex:i];
|
|
|
|
if (ch >= 32 && ch < 128) {
|
|
|
|
aBuf.Append(char(ch));
|
|
|
|
} else {
|
|
|
|
aBuf += nsPrintfCString("\\u%04x", ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return aBuf.get();
|
|
|
|
}
|
|
|
|
#endif
|
2007-10-22 20:27:48 -07:00
|
|
|
|
2008-03-13 18:08:04 -07:00
|
|
|
// Returns PR_TRUE if Gecko claims to have handled the event, PR_FALSE otherwise.
|
|
|
|
- (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-03-13 18:08:04 -07:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
2008-03-13 18:08:04 -07:00
|
|
|
return NO;
|
2007-06-15 15:34:48 -07:00
|
|
|
|
2008-05-05 16:01:07 -07:00
|
|
|
#ifdef PR_LOGGING
|
|
|
|
nsCAutoString str1;
|
|
|
|
nsCAutoString str2;
|
|
|
|
#endif
|
|
|
|
PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
|
|
|
|
("ChildView processKeyDownEvent: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
|
|
|
|
[theEvent keyCode], [theEvent modifierFlags],
|
|
|
|
ToEscapedString([theEvent characters], str1),
|
|
|
|
ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2007-03-22 10:30:00 -07:00
|
|
|
mCurKeyEvent = theEvent;
|
|
|
|
|
|
|
|
BOOL nonDeadKeyPress = [[theEvent characters] length] > 0;
|
2007-11-26 10:09:38 -08:00
|
|
|
if (nonDeadKeyPress) {
|
|
|
|
if (![theEvent isARepeat]) {
|
|
|
|
NSResponder* firstResponder = [[self window] firstResponder];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-26 10:09:38 -08:00
|
|
|
nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_DOWN, nsnull);
|
|
|
|
[self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-26 10:09:38 -08:00
|
|
|
// create native EventRecord for use by plugins
|
|
|
|
EventRecord macEvent;
|
|
|
|
ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
|
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
2008-02-28 21:47:41 -08:00
|
|
|
|
2007-11-26 10:09:38 -08:00
|
|
|
mKeyDownHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
|
|
|
|
if (!mGeckoChild)
|
2008-03-13 18:08:04 -07:00
|
|
|
return mKeyDownHandled;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-26 10:09:38 -08:00
|
|
|
// The key down event may have shifted the focus, in which
|
|
|
|
// case we should not fire the key press.
|
|
|
|
if (firstResponder != [[self window] firstResponder]) {
|
2008-03-13 18:08:04 -07:00
|
|
|
PRBool handled = mKeyDownHandled;
|
2007-11-26 10:09:38 -08:00
|
|
|
mCurKeyEvent = nil;
|
|
|
|
mKeyDownHandled = PR_FALSE;
|
2008-03-13 18:08:04 -07:00
|
|
|
return handled;
|
2007-11-26 10:09:38 -08:00
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-03-04 00:06:47 -08:00
|
|
|
// If this is the context menu key command, send a context menu key event.
|
|
|
|
unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
|
|
|
|
if (modifierFlags == NSControlKeyMask && [[theEvent charactersIgnoringModifiers] isEqualToString:@" "]) {
|
|
|
|
nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, [self widget], nsMouseEvent::eReal, nsMouseEvent::eContextMenuKey);
|
|
|
|
contextMenuEvent.isShift = contextMenuEvent.isControl = contextMenuEvent.isAlt = contextMenuEvent.isMeta = PR_FALSE;
|
2008-03-13 18:08:04 -07:00
|
|
|
PRBool cmEventHandled = mGeckoChild->DispatchWindowEvent(contextMenuEvent);
|
2008-03-04 00:06:47 -08:00
|
|
|
[self maybeInitContextMenuTracking];
|
|
|
|
// Bail, there is nothing else to do here.
|
2008-03-13 18:08:04 -07:00
|
|
|
PRBool handled = (cmEventHandled || mKeyDownHandled);
|
2008-03-04 00:06:47 -08:00
|
|
|
mCurKeyEvent = nil;
|
|
|
|
mKeyDownHandled = PR_FALSE;
|
2008-03-13 18:08:04 -07:00
|
|
|
return handled;
|
2008-03-04 00:06:47 -08:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// if this is a non-letter keypress, or the control key is down,
|
|
|
|
// dispatch the keydown to gecko, so that we trap delete,
|
|
|
|
// control-letter combinations etc before Cocoa tries to use
|
|
|
|
// them for keybindings.
|
2007-04-15 06:43:55 -07:00
|
|
|
if ((!geckoEvent.isChar || geckoEvent.isControl) &&
|
|
|
|
!nsTSMManager::IsComposing()) {
|
2008-02-28 21:47:41 -08:00
|
|
|
if (mKeyDownHandled)
|
|
|
|
geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// create native EventRecord for use by plugins
|
2007-03-22 10:30:00 -07:00
|
|
|
EventRecord macEvent;
|
|
|
|
ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
|
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
|
|
|
|
2008-03-13 18:08:04 -07:00
|
|
|
mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
|
2008-02-28 21:47:41 -08:00
|
|
|
mKeyPressSent = YES;
|
2008-03-13 18:08:04 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return (mKeyDownHandled || mKeyPressHandled);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-11 01:27:59 -07:00
|
|
|
// We need to initialize the TSMDocument *before* interpretKeyEvents when
|
|
|
|
// IME is enabled.
|
|
|
|
if (!isKeyEquiv && nsTSMManager::IsIMEEnabled()) {
|
|
|
|
// We need to get actual focused view. E.g., the view is in bookmark dialog
|
|
|
|
// that is <panel> element. Then, the key events are processed the parent
|
|
|
|
// window's view that has native focus.
|
|
|
|
nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT,
|
|
|
|
mGeckoChild);
|
|
|
|
textContent.InitForQueryTextContent(0, 0);
|
|
|
|
mGeckoChild->DispatchWindowEvent(textContent);
|
|
|
|
NSView<mozView>* focusedView = self;
|
|
|
|
if (textContent.mSucceeded && textContent.mReply.mFocusedWidget) {
|
|
|
|
NSView<mozView>* view =
|
|
|
|
static_cast<NSView<mozView>*>(textContent.mReply.mFocusedWidget->
|
|
|
|
GetNativeData(NS_NATIVE_WIDGET));
|
|
|
|
if (view)
|
|
|
|
focusedView = view;
|
|
|
|
}
|
|
|
|
nsTSMManager::InitTSMDocument(focusedView);
|
|
|
|
}
|
|
|
|
|
2008-02-28 21:47:41 -08:00
|
|
|
// Let Cocoa interpret the key events, caching IsComposing first.
|
|
|
|
// We don't do it if this came from performKeyEquivalent because
|
|
|
|
// interpretKeyEvents isn't set up to handle those key combinations.
|
|
|
|
PRBool wasComposing = nsTSMManager::IsComposing();
|
2008-03-30 02:17:50 -07:00
|
|
|
PRBool interpretKeyEventsCalled = PR_FALSE;
|
2008-04-03 04:41:35 -07:00
|
|
|
if (!isKeyEquiv &&
|
|
|
|
(nsTSMManager::IsIMEEnabled() || nsTSMManager::IsRomanKeyboardsOnly())) {
|
2008-02-28 21:47:41 -08:00
|
|
|
[super interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
|
2008-03-30 02:17:50 -07:00
|
|
|
interpretKeyEventsCalled = PR_TRUE;
|
|
|
|
}
|
2008-02-28 21:47:41 -08:00
|
|
|
|
|
|
|
if (!mGeckoChild)
|
2008-03-13 18:08:04 -07:00
|
|
|
return (mKeyDownHandled || mKeyPressHandled);;
|
2008-02-28 21:47:41 -08:00
|
|
|
|
|
|
|
if (!mKeyPressSent && nonDeadKeyPress && !wasComposing && !nsTSMManager::IsComposing()) {
|
|
|
|
nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
|
|
|
|
[self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
|
|
|
|
|
2008-03-30 02:17:50 -07:00
|
|
|
// If we called interpretKeyEvents and this isn't normal character input
|
|
|
|
// then IME probably ate the event for some reason. We do not want to
|
|
|
|
// send a key press event in that case.
|
|
|
|
if (!(interpretKeyEventsCalled && IsNormalCharInputtingEvent(geckoEvent))) {
|
|
|
|
if (mKeyDownHandled)
|
|
|
|
geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
|
2008-02-28 21:47:41 -08:00
|
|
|
|
2008-03-30 02:17:50 -07:00
|
|
|
// create native EventRecord for use by plugins
|
|
|
|
EventRecord macEvent;
|
|
|
|
ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
|
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
|
|
|
|
|
|
|
mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
|
|
|
|
}
|
2007-11-12 09:16:15 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-01-15 15:11:55 -08:00
|
|
|
// Note: mGeckoChild might have become null here. Don't count on it from here on.
|
|
|
|
|
2008-03-13 18:08:04 -07:00
|
|
|
PRBool handled = (mKeyDownHandled || mKeyPressHandled);
|
|
|
|
|
2008-02-28 21:47:41 -08:00
|
|
|
// See note about nested event loops where these variables are declared in header.
|
2008-03-13 18:08:04 -07:00
|
|
|
mKeyPressHandled = NO;
|
2008-02-28 21:47:41 -08:00
|
|
|
mKeyPressSent = NO;
|
2007-03-22 10:30:00 -07:00
|
|
|
mCurKeyEvent = nil;
|
2007-11-26 10:09:38 -08:00
|
|
|
mKeyDownHandled = PR_FALSE;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
2008-03-13 18:08:04 -07:00
|
|
|
return handled;
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-11-26 10:09:38 -08:00
|
|
|
|
2008-06-30 09:30:22 -07:00
|
|
|
// Create a TSM document for use with plugins, so that we can support IME in
|
|
|
|
// them. Once it's created, if need be (re)activate it. Some plugins (e.g.
|
|
|
|
// the Flash plugin running in Camino) don't create their own TSM document --
|
|
|
|
// without which IME can't work. Others (e.g. the Flash plugin running in
|
|
|
|
// Firefox) create a TSM document that (somehow) makes the input window behave
|
|
|
|
// badly when it contains more than one kind of input (say Hiragana and
|
|
|
|
// Romaji). (We can't just use the per-NSView TSM documents that Cocoa
|
|
|
|
// provices (those created and managed by the NSTSMInputContext class) -- for
|
|
|
|
// some reason TSMProcessRawKeyEvent() doesn't work with them.)
|
|
|
|
- (void)activatePluginTSMDoc
|
|
|
|
{
|
|
|
|
if (!mPluginTSMDoc) {
|
|
|
|
// Create a TSM document that supports both non-Unicode and Unicode input.
|
|
|
|
// Though [ChildView processPluginKeyEvent:] only sends Mac char codes to
|
|
|
|
// the plugin, this makes the input window behave better when it contains
|
|
|
|
// more than one kind of input (say Hiragana and Romaji). This is what
|
|
|
|
// the OS does when it creates a TSM document for use by an
|
|
|
|
// NSTSMInputContext class.
|
|
|
|
InterfaceTypeList supportedServices;
|
|
|
|
supportedServices[0] = kTextServiceDocumentInterfaceType;
|
|
|
|
supportedServices[1] = kUnicodeDocumentInterfaceType;
|
|
|
|
::NewTSMDocument(2, supportedServices, &mPluginTSMDoc, 0);
|
|
|
|
// We'll need to use the "input window".
|
|
|
|
::UseInputWindow(mPluginTSMDoc, YES);
|
|
|
|
::ActivateTSMDocument(mPluginTSMDoc);
|
|
|
|
} else if (::TSMGetActiveDocument() != mPluginTSMDoc) {
|
|
|
|
::ActivateTSMDocument(mPluginTSMDoc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 21:47:41 -08:00
|
|
|
- (void)keyDown:(NSEvent*)theEvent
|
|
|
|
{
|
2008-06-30 09:30:22 -07:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
|
|
|
// If a plugin has the focus, we need to use an alternate method for
|
|
|
|
// handling NSKeyDown and NSKeyUp events (otherwise Carbon-based IME won't
|
|
|
|
// work in plugins like the Flash plugin). The same strategy is used by the
|
|
|
|
// WebKit. See PluginKeyEventsHandler() and [ChildView processPluginKeyEvent:]
|
|
|
|
// for more info.
|
|
|
|
if (mGeckoChild && mIsPluginView) {
|
|
|
|
[self activatePluginTSMDoc];
|
|
|
|
// We use the active TSM document to pass a pointer to ourselves (the
|
|
|
|
// currently focused ChildView) to PluginKeyEventsHandler(). Because this
|
|
|
|
// pointer is weak, we should retain and release ourselves around the call
|
|
|
|
// to TSMProcessRawKeyEvent().
|
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
|
|
|
::TSMSetDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag,
|
|
|
|
sizeof(ChildView *), &self);
|
|
|
|
::TSMProcessRawKeyEvent([theEvent _eventRef]);
|
|
|
|
::TSMRemoveDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-02-28 21:47:41 -08:00
|
|
|
[self processKeyDownEvent:theEvent keyEquiv:NO];
|
2008-06-30 09:30:22 -07:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2008-02-28 21:47:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-22 20:27:48 -07:00
|
|
|
static BOOL keyUpAlreadySentKeyDown = NO;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
- (void)keyUp:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2008-05-05 16:01:07 -07:00
|
|
|
#ifdef PR_LOGGING
|
|
|
|
nsCAutoString str1;
|
|
|
|
nsCAutoString str2;
|
|
|
|
#endif
|
|
|
|
PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
|
|
|
|
("ChildView keyUp: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
|
|
|
|
[theEvent keyCode], [theEvent modifierFlags],
|
|
|
|
ToEscapedString([theEvent characters], str1),
|
|
|
|
ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
|
|
|
|
|
2008-06-30 09:30:22 -07:00
|
|
|
if (!mGeckoChild)
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2008-06-30 09:30:22 -07:00
|
|
|
if (mIsPluginView) {
|
|
|
|
// I'm not sure the call to TSMProcessRawKeyEvent() is needed here (though
|
|
|
|
// WebKit makes one).
|
|
|
|
::TSMProcessRawKeyEvent([theEvent _eventRef]);
|
|
|
|
|
|
|
|
// Don't send a keyUp event if the corresponding keyDown event(s) is/are
|
|
|
|
// still being processed (idea borrowed from WebKit).
|
|
|
|
ChildView *keyDownTarget = nil;
|
|
|
|
OSStatus status = ::TSMGetDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag,
|
|
|
|
sizeof(ChildView *), nil, &keyDownTarget);
|
|
|
|
if (status != noErr)
|
|
|
|
keyDownTarget = nil;
|
|
|
|
if (keyDownTarget == self)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// PluginKeyEventsHandler() never sends keyUp events to [ChildView
|
|
|
|
// processPluginKeyEvent:], so we need to send them to Gecko here. (This
|
|
|
|
// means that when commiting text from IME, several keyDown events may be
|
|
|
|
// sent to Gecko (in processPluginKeyEvent) for one keyUp event here.
|
|
|
|
// But this is how the WebKit does it, and games expect a keyUp event to
|
|
|
|
// be sent when it actually happens (they need to be able to detect how
|
|
|
|
// long a key has been held down) -- which wouldn't be possible if we sent
|
|
|
|
// them from processPluginKeyEvent.)
|
|
|
|
nsKeyEvent keyUpEvent(PR_TRUE, NS_KEY_UP, nsnull);
|
|
|
|
[self convertCocoaKeyEvent:theEvent toGeckoEvent:&keyUpEvent];
|
|
|
|
EventRecord macKeyUpEvent;
|
|
|
|
ConvertCocoaKeyEventToMacEvent(theEvent, macKeyUpEvent);
|
|
|
|
keyUpEvent.nativeMsg = &macKeyUpEvent;
|
|
|
|
mGeckoChild->DispatchWindowEvent(keyUpEvent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we don't have any characters we can't generate a keyUp event
|
|
|
|
if ([[theEvent characters] length] == 0)
|
|
|
|
return;
|
|
|
|
|
2007-11-01 13:49:58 -07:00
|
|
|
// Cocoa doesn't send an NSKeyDown event for control-tab on 10.4, so if this
|
2007-10-22 20:27:48 -07:00
|
|
|
// is an NSKeyUp event for control-tab, send a down event to gecko first.
|
2007-11-01 13:48:24 -07:00
|
|
|
if (!nsToolkit::OnLeopardOrLater() && !keyUpAlreadySentKeyDown &&
|
|
|
|
[theEvent modifierFlags] & NSControlKeyMask && [theEvent keyCode] == kTabKeyCode) {
|
2007-10-22 20:27:48 -07:00
|
|
|
// We'll need an NSKeyDown copy of our native event so we convert to a gecko event correctly.
|
|
|
|
NSEvent* nativeKeyDownEvent = [ChildView makeNewCocoaEventWithType:NSKeyDown fromEvent:theEvent];
|
|
|
|
|
|
|
|
// send a key down event if we should
|
|
|
|
PRBool keyDownHandled = PR_FALSE;
|
|
|
|
if (![nativeKeyDownEvent isARepeat]) {
|
|
|
|
nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_DOWN, nsnull);
|
|
|
|
[self convertCocoaKeyEvent:nativeKeyDownEvent toGeckoEvent:&geckoEvent];
|
|
|
|
|
|
|
|
// create native EventRecord for use by plugins
|
|
|
|
EventRecord macEvent;
|
|
|
|
ConvertCocoaKeyEventToMacEvent(nativeKeyDownEvent, macEvent);
|
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
2008-02-28 21:47:41 -08:00
|
|
|
|
2007-10-22 20:27:48 -07:00
|
|
|
keyDownHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
|
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check to see if we are still the first responder.
|
|
|
|
// The key down event may have shifted the focus, in which
|
|
|
|
// case we should not fire the key press.
|
|
|
|
NSResponder* resp = [[self window] firstResponder];
|
|
|
|
if (resp != (NSResponder*)self) {
|
|
|
|
keyUpAlreadySentKeyDown = YES;
|
|
|
|
[resp keyUp:theEvent];
|
|
|
|
keyUpAlreadySentKeyDown = NO;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// now send a key press event if we should
|
|
|
|
if (!nsTSMManager::IsComposing()) {
|
|
|
|
nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
|
|
|
|
[self convertCocoaKeyEvent:nativeKeyDownEvent toGeckoEvent:&geckoEvent];
|
|
|
|
|
|
|
|
if (keyDownHandled)
|
|
|
|
geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
|
|
|
|
|
|
|
|
// create native EventRecord for use by plugins
|
|
|
|
EventRecord macEvent;
|
|
|
|
ConvertCocoaKeyEventToMacEvent(nativeKeyDownEvent, macEvent);
|
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
|
|
|
|
2007-11-26 10:09:38 -08:00
|
|
|
mGeckoChild->DispatchWindowEvent(geckoEvent);
|
2007-10-22 20:27:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_UP, nsnull);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// create native EventRecord for use by plugins
|
2007-03-22 10:30:00 -07:00
|
|
|
EventRecord macEvent;
|
|
|
|
ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
|
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
|
|
|
|
|
|
|
mGeckoChild->DispatchWindowEvent(geckoEvent);
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (BOOL)performKeyEquivalent:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2008-02-28 21:47:41 -08:00
|
|
|
// don't do anything if we don't have a gecko widget
|
2008-01-30 21:26:55 -08:00
|
|
|
if (!mGeckoChild)
|
2008-02-28 21:47:41 -08:00
|
|
|
return NO;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-03-01 11:04:33 -08:00
|
|
|
|
2008-06-27 13:48:03 -07:00
|
|
|
// If we're not the first responder and the first responder is an NSView
|
|
|
|
// object, pass the event on. Otherwise (if, for example, the first
|
|
|
|
// responder is an NSWindow object) we should trust the OS to have called
|
|
|
|
// us correctly.
|
2008-04-07 21:38:52 -07:00
|
|
|
id firstResponder = [[self window] firstResponder];
|
|
|
|
if (firstResponder != self) {
|
2008-06-27 13:48:03 -07:00
|
|
|
// Special handling if the other first responder is a ChildView.
|
2008-04-07 21:38:52 -07:00
|
|
|
if ([firstResponder isKindOfClass:[ChildView class]])
|
|
|
|
return [(ChildView *)firstResponder performKeyEquivalent:theEvent];
|
2008-06-27 13:48:03 -07:00
|
|
|
if ([firstResponder isKindOfClass:[NSView class]])
|
2008-04-07 21:38:52 -07:00
|
|
|
return [super performKeyEquivalent:theEvent];
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-02-28 21:47:41 -08:00
|
|
|
// don't process if we're composing, but don't consume the event
|
2008-02-10 23:13:18 -08:00
|
|
|
if (nsTSMManager::IsComposing())
|
2008-01-30 21:26:55 -08:00
|
|
|
return NO;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
// Perform native menu UI feedback even if we stop the event from propagating to it normally.
|
|
|
|
// Recall that the menu system won't actually execute any commands for keyboard command invocations.
|
2008-04-28 10:43:34 -07:00
|
|
|
//
|
|
|
|
// If this is a plugin, we do actually perform the action on keyboard commands. See bug 428047.
|
|
|
|
// If the action on plugins here changes the first responder, don't continue.
|
2008-04-07 21:38:52 -07:00
|
|
|
NSMenu* mainMenu = [NSApp mainMenu];
|
2008-04-28 10:43:34 -07:00
|
|
|
if (mIsPluginView) {
|
|
|
|
if ([mainMenu isKindOfClass:[GeckoNSMenu class]])
|
|
|
|
[(GeckoNSMenu*)mainMenu actOnKeyEquivalent:theEvent];
|
|
|
|
else
|
|
|
|
[mainMenu performKeyEquivalent:theEvent];
|
|
|
|
if ([[self window] firstResponder] != self)
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ([mainMenu isKindOfClass:[GeckoNSMenu class]])
|
|
|
|
[(GeckoNSMenu*)mainMenu performMenuUserInterfaceEffectsForEvent:theEvent];
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-05-01 21:30:38 -07:00
|
|
|
// With Cmd key or Ctrl+Tab or Ctrl+Esc, keyDown will be never called.
|
|
|
|
// Therefore, we need to call processKeyDownEvent from performKeyEquivalent.
|
|
|
|
UInt32 modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
|
|
|
|
UInt32 keyCode = [theEvent keyCode];
|
|
|
|
PRBool keyDownNeverFiredEvent = (modifierFlags & NSCommandKeyMask) ||
|
|
|
|
((modifierFlags & NSControlKeyMask) &&
|
|
|
|
(keyCode == kEscapeKeyCode || keyCode == kTabKeyCode));
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// don't handle this if certain modifiers are down - those should
|
|
|
|
// be sent as normal key up/down events and cocoa will do so automatically
|
|
|
|
// if we reject here
|
2008-05-01 21:30:38 -07:00
|
|
|
if (!keyDownNeverFiredEvent &&
|
|
|
|
(modifierFlags & (NSFunctionKeyMask| NSNumericPadKeyMask)))
|
2007-03-22 10:30:00 -07:00
|
|
|
return NO;
|
|
|
|
|
2008-04-22 20:41:50 -07:00
|
|
|
// Control and option modifiers are used when changing input sources in the
|
|
|
|
// input menu. We need to send such key events via "keyDown:", which will
|
|
|
|
// happen if we return NO here. This only applies to Mac OS X 10.5 and higher,
|
|
|
|
// previous OS versions just call "keyDown:" and not "performKeyEquivalent:"
|
|
|
|
// for such events.
|
2008-05-01 21:30:38 -07:00
|
|
|
if (!keyDownNeverFiredEvent &&
|
|
|
|
(modifierFlags & (NSControlKeyMask | NSAlternateKeyMask)))
|
2008-04-22 20:41:50 -07:00
|
|
|
return NO;
|
|
|
|
|
2008-03-13 18:08:04 -07:00
|
|
|
if ([theEvent type] == NSKeyDown) {
|
|
|
|
// We trust the Gecko handled status for cmd key events. See bug 417466 for more info.
|
|
|
|
if (modifierFlags & NSCommandKeyMask)
|
|
|
|
return [self processKeyDownEvent:theEvent keyEquiv:YES];
|
|
|
|
else
|
|
|
|
[self processKeyDownEvent:theEvent keyEquiv:YES];
|
|
|
|
}
|
2008-01-28 22:11:06 -08:00
|
|
|
|
|
|
|
return YES;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)flagsChanged:(NSEvent*)theEvent
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Fire key up/down events for the modifier keys (shift, alt, ctrl, command).
|
|
|
|
if ([theEvent type] == NSFlagsChanged) {
|
|
|
|
unsigned int modifiers =
|
|
|
|
[theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
|
|
|
|
const PRUint32 kModifierMaskTable[] =
|
|
|
|
{ NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask, NSCommandKeyMask };
|
|
|
|
const PRUint32 kModifierCount = sizeof(kModifierMaskTable) /
|
|
|
|
sizeof(kModifierMaskTable[0]);
|
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
for (PRUint32 i = 0; i < kModifierCount; i++) {
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32 modifierBit = kModifierMaskTable[i];
|
|
|
|
if ((modifiers & modifierBit) != (mLastModifierState & modifierBit)) {
|
|
|
|
PRUint32 message = ((modifiers & modifierBit) != 0 ? NS_KEY_DOWN :
|
|
|
|
NS_KEY_UP);
|
|
|
|
|
|
|
|
// Fire a key event.
|
|
|
|
nsKeyEvent geckoEvent(PR_TRUE, message, nsnull);
|
2007-06-12 13:28:26 -07:00
|
|
|
[self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-12 13:28:26 -07:00
|
|
|
// create native EventRecord for use by plugins
|
2007-03-22 10:30:00 -07:00
|
|
|
EventRecord macEvent;
|
|
|
|
ConvertCocoaKeyEventToMacEvent(theEvent, macEvent, message);
|
|
|
|
geckoEvent.nativeMsg = &macEvent;
|
2007-06-12 13:28:26 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mGeckoChild->DispatchWindowEvent(geckoEvent);
|
2008-01-15 15:11:55 -08:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Stop if focus has changed.
|
|
|
|
// Check to see if we are still the first responder.
|
|
|
|
NSResponder* resp = [[self window] firstResponder];
|
|
|
|
if (resp != (NSResponder*)self)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mLastModifierState = modifiers;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if the hand scroll cursor needs to be set/unset
|
|
|
|
[self setHandScrollCursor:theEvent];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This method is called when are are about to lose focus.
|
2007-06-21 16:58:16 -07:00
|
|
|
// We must always call through to our superclass, even when mGeckoChild is
|
|
|
|
// nil -- otherwise the keyboard focus can end up in the wrong NSView.
|
2007-03-22 10:30:00 -07:00
|
|
|
- (BOOL)resignFirstResponder
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return [super resignFirstResponder];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewsWindowDidBecomeKey
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mGeckoChild)
|
2007-06-15 15:34:48 -07:00
|
|
|
return;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// check to see if the window implements the mozWindow protocol. This
|
|
|
|
// allows embedders to avoid re-entrant calls to -makeKeyAndOrderFront,
|
|
|
|
// which can happen because these activate/focus calls propagate out
|
|
|
|
// to the embedder via nsIEmbeddingSiteWindow::SetFocus().
|
|
|
|
BOOL isMozWindow = [[self window] respondsToSelector:@selector(setSuppressMakeKeyFront:)];
|
|
|
|
if (isMozWindow)
|
|
|
|
[[self window] setSuppressMakeKeyFront:YES];
|
|
|
|
|
2007-08-02 16:01:32 -07:00
|
|
|
[self sendFocusEvent:NS_GOTFOCUS];
|
|
|
|
[self sendFocusEvent:NS_ACTIVATE];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (isMozWindow)
|
|
|
|
[[self window] setSuppressMakeKeyFront:NO];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewsWindowDidResignKey
|
|
|
|
{
|
|
|
|
if (!mGeckoChild)
|
2007-06-15 15:34:48 -07:00
|
|
|
return;
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2007-08-02 16:01:32 -07:00
|
|
|
[self sendFocusEvent:NS_DEACTIVATE];
|
|
|
|
[self sendFocusEvent:NS_LOSTFOCUS];
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-21 16:58:16 -07:00
|
|
|
// If the call to removeFromSuperview isn't delayed from nsChildView::
|
|
|
|
// TearDownView(), the NSView hierarchy might get changed during calls to
|
|
|
|
// [ChildView drawRect:], which leads to "beyond bounds" exceptions in
|
|
|
|
// NSCFArray. For more info see bmo bug 373122. Apple's docs claim that
|
2007-05-30 11:25:44 -07:00
|
|
|
// removeFromSuperviewWithoutNeedingDisplay "can be safely invoked during
|
|
|
|
// display" (whatever "display" means). But it's _not_ true that it can be
|
2007-06-21 16:58:16 -07:00
|
|
|
// safely invoked during calls to [NSView drawRect:]. We use
|
|
|
|
// removeFromSuperview here because there's no longer any danger of being
|
|
|
|
// "invoked during display", and because doing do clears up bmo bug 384343.
|
2007-05-30 11:25:44 -07:00
|
|
|
- (void)delayedTearDown
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-06-21 16:58:16 -07:00
|
|
|
[self removeFromSuperview];
|
2007-05-30 11:25:44 -07:00
|
|
|
[self release];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-05-30 11:25:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
// drag'n'drop stuff
|
|
|
|
#define kDragServiceContractID "@mozilla.org/widget/dragservice;1"
|
|
|
|
|
|
|
|
|
|
|
|
// This is a utility function used by NSView drag event methods
|
|
|
|
// to send events. It contains all of the logic needed for Gecko
|
|
|
|
// dragging to work. Returns YES if the event was handled, NO
|
|
|
|
// if it wasn't.
|
|
|
|
- (BOOL)doDragAction:(PRUint32)aMessage sender:(id)aSender
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-06-29 20:19:41 -07:00
|
|
|
if (!mGeckoChild)
|
2007-03-22 10:30:00 -07:00
|
|
|
return NO;
|
|
|
|
|
2007-07-16 19:24:05 -07:00
|
|
|
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView doDragAction: entered\n"));
|
|
|
|
|
2007-06-29 20:19:41 -07:00
|
|
|
if (!mDragService) {
|
|
|
|
CallGetService(kDragServiceContractID, &mDragService);
|
|
|
|
NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
|
|
|
|
if (!mDragService)
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (aMessage == NS_DRAGDROP_ENTER)
|
|
|
|
mDragService->StartDragSession();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDragSession> dragSession;
|
|
|
|
mDragService->GetCurrentSession(getter_AddRefs(dragSession));
|
|
|
|
if (dragSession) {
|
2007-04-11 21:37:39 -07:00
|
|
|
if (aMessage == NS_DRAGDROP_OVER) {
|
|
|
|
// fire the drag event at the source. Just ignore whether it was
|
|
|
|
// cancelled or not as there isn't actually a means to stop the drag
|
|
|
|
mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
|
2007-03-22 10:30:00 -07:00
|
|
|
dragSession->SetCanDrop(PR_FALSE);
|
2007-04-11 21:37:39 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
else if (aMessage == NS_DRAGDROP_DROP) {
|
2008-07-21 06:26:34 -07:00
|
|
|
// We make the assumption that the dragOver handlers have correctly set
|
2007-03-22 10:30:00 -07:00
|
|
|
// the |canDrop| property of the Drag Session.
|
|
|
|
PRBool canDrop = PR_FALSE;
|
|
|
|
if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop)
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags];
|
|
|
|
PRUint32 action = nsIDragService::DRAGDROP_ACTION_MOVE;
|
|
|
|
// force copy = option, alias = cmd-option, default is move
|
|
|
|
if (modifierFlags & NSAlternateKeyMask) {
|
|
|
|
if (modifierFlags & NSCommandKeyMask)
|
|
|
|
action = nsIDragService::DRAGDROP_ACTION_LINK;
|
|
|
|
else
|
|
|
|
action = nsIDragService::DRAGDROP_ACTION_COPY;
|
|
|
|
}
|
|
|
|
dragSession->SetDragAction(action);
|
|
|
|
}
|
|
|
|
|
2007-10-09 11:46:30 -07:00
|
|
|
// set up gecko event
|
|
|
|
nsMouseEvent geckoEvent(PR_TRUE, aMessage, nsnull, nsMouseEvent::eReal);
|
|
|
|
[self convertGenericCocoaEvent:nil toGeckoEvent:&geckoEvent];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-10-09 11:46:30 -07:00
|
|
|
// Use our own coordinates in the gecko event.
|
|
|
|
// Convert event from gecko global coords to gecko view coords.
|
|
|
|
NSPoint localPoint = [self convertPoint:[aSender draggingLocation] fromView:nil];
|
|
|
|
geckoEvent.refPoint.x = static_cast<nscoord>(localPoint.x);
|
|
|
|
geckoEvent.refPoint.y = static_cast<nscoord>(localPoint.y);
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2007-10-09 11:46:30 -07:00
|
|
|
mGeckoChild->DispatchWindowEvent(geckoEvent);
|
2008-01-15 15:11:55 -08:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return YES;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (aMessage == NS_DRAGDROP_EXIT && dragSession) {
|
|
|
|
nsCOMPtr<nsIDOMNode> sourceNode;
|
|
|
|
dragSession->GetSourceNode(getter_AddRefs(sourceNode));
|
|
|
|
if (!sourceNode) {
|
|
|
|
// We're leaving a window while doing a drag that was
|
|
|
|
// initiated in a different app. End the drag session,
|
|
|
|
// since we're done with it for now (until the user
|
|
|
|
// drags back into mozilla).
|
2007-04-11 21:37:39 -07:00
|
|
|
mDragService->EndDragSession(PR_FALSE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-09 11:46:30 -07:00
|
|
|
return YES;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-07-16 19:24:05 -07:00
|
|
|
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingEntered: entered\n"));
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// there should never be a globalDragPboard when "draggingEntered:" is
|
|
|
|
// called, but just in case we'll take care of it here.
|
|
|
|
[globalDragPboard release];
|
|
|
|
|
|
|
|
// Set the global drag pasteboard that will be used for this drag session.
|
|
|
|
// This will be set back to nil when the drag session ends (mouse exits
|
|
|
|
// the view or a drop happens within the view).
|
|
|
|
globalDragPboard = [[sender draggingPasteboard] retain];
|
|
|
|
|
|
|
|
BOOL handled = [self doDragAction:NS_DRAGDROP_ENTER sender:sender];
|
|
|
|
|
|
|
|
return handled ? NSDragOperationGeneric : NSDragOperationNone;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
|
|
|
|
{
|
2007-07-16 19:24:05 -07:00
|
|
|
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingUpdated: entered\n"));
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
BOOL handled = [self doDragAction:NS_DRAGDROP_OVER sender:sender];
|
|
|
|
return handled ? NSDragOperationGeneric : NSDragOperationNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)draggingExited:(id <NSDraggingInfo>)sender
|
|
|
|
{
|
2007-07-16 19:24:05 -07:00
|
|
|
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingExited: entered\n"));
|
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2007-03-22 10:30:00 -07:00
|
|
|
[self doDragAction:NS_DRAGDROP_EXIT sender:sender];
|
2007-10-10 05:19:47 -07:00
|
|
|
NS_IF_RELEASE(mDragService);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
|
|
|
{
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2007-10-10 05:19:47 -07:00
|
|
|
BOOL handled = [self doDragAction:NS_DRAGDROP_DROP sender:sender];
|
|
|
|
NS_IF_RELEASE(mDragService);
|
|
|
|
return handled;
|
2007-06-29 20:19:41 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-29 20:19:41 -07:00
|
|
|
// NSDraggingSource
|
|
|
|
- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-07-17 16:02:40 -07:00
|
|
|
gDraggedTransferables = nsnull;
|
|
|
|
|
2007-06-29 20:19:41 -07:00
|
|
|
if (!mDragService) {
|
|
|
|
CallGetService(kDragServiceContractID, &mDragService);
|
|
|
|
NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mDragService) {
|
|
|
|
mDragService->EndDragSession(PR_TRUE);
|
|
|
|
NS_RELEASE(mDragService);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
[globalDragPboard release];
|
|
|
|
globalDragPboard = nil;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-06-29 20:19:41 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-29 20:19:41 -07:00
|
|
|
|
|
|
|
// NSDraggingSource
|
|
|
|
// this is just implemented so we comply with the NSDraggingSource informal protocol
|
|
|
|
- (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal
|
|
|
|
{
|
|
|
|
return UINT_MAX;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-07-16 19:24:05 -07:00
|
|
|
// This method is a callback typically invoked in response to a drag ending on the desktop
|
|
|
|
// or a Findow folder window; the argument passed is a path to the drop location, to be used
|
|
|
|
// in constructing a complete pathname for the file(s) we want to create as a result of
|
|
|
|
// the drag.
|
|
|
|
- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(id <NSDraggingInfo>)dropDestination
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-07-16 19:24:05 -07:00
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView namesOfPromisedFilesDroppedAtDestination: entering callback for promised files\n"));
|
|
|
|
|
|
|
|
nsCOMPtr<nsILocalFile> targFile;
|
|
|
|
NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(targFile));
|
|
|
|
nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(targFile);
|
|
|
|
if (!macLocalFile) {
|
|
|
|
NS_ERROR("No Mac local file");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)dropDestination))) {
|
|
|
|
NS_ERROR("failed InitWithCFURL");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2007-07-17 16:02:40 -07:00
|
|
|
if (!gDraggedTransferables)
|
|
|
|
return nil;
|
2007-07-16 19:24:05 -07:00
|
|
|
|
|
|
|
PRUint32 transferableCount;
|
2007-07-17 16:02:40 -07:00
|
|
|
rv = gDraggedTransferables->Count(&transferableCount);
|
2007-07-16 19:24:05 -07:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
for (PRUint32 i = 0; i < transferableCount; i++) {
|
|
|
|
nsCOMPtr<nsISupports> genericItem;
|
2007-07-17 16:02:40 -07:00
|
|
|
gDraggedTransferables->GetElementAt(i, getter_AddRefs(genericItem));
|
2007-07-16 19:24:05 -07:00
|
|
|
nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
|
|
|
|
if (!item) {
|
|
|
|
NS_ERROR("no transferable");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile, sizeof(nsILocalFile*));
|
|
|
|
|
|
|
|
// now request the kFilePromiseMime data, which will invoke the data provider
|
|
|
|
// If successful, the returned data is a reference to the resulting file.
|
|
|
|
nsCOMPtr<nsISupports> fileDataPrimitive;
|
|
|
|
PRUint32 dataSize = 0;
|
|
|
|
item->GetTransferData(kFilePromiseMime, getter_AddRefs(fileDataPrimitive), &dataSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
NSPasteboard* generalPboard = [NSPasteboard pasteboardWithName:NSDragPboard];
|
|
|
|
NSData* data = [generalPboard dataForType:@"application/x-moz-file-promise-dest-filename"];
|
|
|
|
NSString* name = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
|
|
NSArray* rslt = [NSArray arrayWithObject:name];
|
|
|
|
|
|
|
|
[name release];
|
|
|
|
|
|
|
|
return rslt;
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-07-16 19:24:05 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
|
|
|
|
/* Every ChildView has a corresponding mozDocAccessible object that is doing all
|
|
|
|
the heavy lifting. The topmost ChildView corresponds to a mozRootAccessible
|
|
|
|
object.
|
|
|
|
|
|
|
|
All ChildView needs to do is to route all accessibility calls (from the NSAccessibility APIs)
|
|
|
|
down to its object, pretending that they are the same.
|
|
|
|
*/
|
|
|
|
- (id<mozAccessible>)accessible
|
|
|
|
{
|
2007-06-15 15:34:48 -07:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return nil;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
id<mozAccessible> nativeAccessible = nil;
|
2008-01-15 15:11:55 -08:00
|
|
|
|
2008-04-07 21:38:52 -07:00
|
|
|
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
2008-01-15 15:11:55 -08:00
|
|
|
nsCOMPtr<nsIWidget> kungFuDeathGrip2(mGeckoChild);
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIAccessible> accessible;
|
|
|
|
mGeckoChild->GetDocumentAccessible(getter_AddRefs(accessible));
|
2008-01-15 15:11:55 -08:00
|
|
|
if (!mGeckoChild)
|
|
|
|
return nil;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (accessible)
|
|
|
|
accessible->GetNativeInterface((void**)&nativeAccessible);
|
|
|
|
|
|
|
|
#ifdef DEBUG_hakan
|
|
|
|
NSAssert(![nativeAccessible isExpired], @"native acc is expired!!!");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return nativeAccessible;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Implementation of formal mozAccessible formal protocol (enabling mozViews
|
|
|
|
to talk to mozAccessible objects in the accessibility module). */
|
|
|
|
|
|
|
|
- (BOOL)hasRepresentedView
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)representedView
|
|
|
|
{
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isRoot
|
|
|
|
{
|
|
|
|
return [[self accessible] isRoot];
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
- (void)printHierarchy
|
|
|
|
{
|
|
|
|
[[self accessible] printHierarchy];
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
// general
|
|
|
|
|
|
|
|
- (BOOL)accessibilityIsIgnored
|
|
|
|
{
|
|
|
|
return [[self accessible] accessibilityIsIgnored];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)accessibilityHitTest:(NSPoint)point
|
|
|
|
{
|
|
|
|
return [[self accessible] accessibilityHitTest:point];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)accessibilityFocusedUIElement
|
|
|
|
{
|
|
|
|
return [[self accessible] accessibilityFocusedUIElement];
|
|
|
|
}
|
|
|
|
|
|
|
|
// actions
|
|
|
|
|
|
|
|
- (NSArray*)accessibilityActionNames
|
|
|
|
{
|
|
|
|
return [[self accessible] accessibilityActionNames];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)accessibilityActionDescription:(NSString*)action
|
|
|
|
{
|
|
|
|
return [[self accessible] accessibilityActionDescription:action];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)accessibilityPerformAction:(NSString*)action
|
|
|
|
{
|
|
|
|
return [[self accessible] accessibilityPerformAction:action];
|
|
|
|
}
|
|
|
|
|
|
|
|
// attributes
|
|
|
|
|
|
|
|
- (NSArray*)accessibilityAttributeNames
|
|
|
|
{
|
|
|
|
return [[self accessible] accessibilityAttributeNames];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
|
|
|
|
{
|
|
|
|
return [[self accessible] accessibilityIsAttributeSettable:attribute];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)accessibilityAttributeValue:(NSString*)attribute
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
id<mozAccessible> accessible = [self accessible];
|
|
|
|
|
|
|
|
// if we're the root (topmost) accessible, we need to return our native AXParent as we
|
|
|
|
// traverse outside to the hierarchy of whoever embeds us. thus, fall back on NSView's
|
|
|
|
// default implementation for this attribute.
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityParentAttribute] && [accessible isRoot]) {
|
|
|
|
id parentAccessible = [super accessibilityAttributeValue:attribute];
|
|
|
|
return parentAccessible;
|
|
|
|
}
|
|
|
|
|
|
|
|
return [accessible accessibilityAttributeValue:attribute];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* ACCESSIBILITY */
|
|
|
|
|
|
|
|
@end
|
2007-04-15 06:43:55 -07:00
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
2008-07-14 07:38:49 -07:00
|
|
|
void
|
|
|
|
nsTSMManager::OnDestroyView(NSView<mozView>* aDestroyingView)
|
|
|
|
{
|
|
|
|
if (aDestroyingView != sComposingView)
|
|
|
|
return;
|
|
|
|
if (IsComposing()) {
|
|
|
|
CancelIME(); // XXX Might CancelIME() fail because sComposingView is being destroyed?
|
|
|
|
EndComposing();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-15 06:43:55 -07:00
|
|
|
PRBool
|
|
|
|
nsTSMManager::GetIMEOpenState()
|
|
|
|
{
|
|
|
|
return GetScriptManagerVariable(smKeyScript) != smRoman ? PR_TRUE : PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-11 01:27:59 -07:00
|
|
|
void
|
|
|
|
nsTSMManager::InitTSMDocument(NSView<mozView>* aViewForCaret)
|
|
|
|
{
|
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
|
|
|
sDocumentID = ::TSMGetActiveDocument();
|
|
|
|
if (!sDocumentID)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// We need to set the focused window level to TSMDocument. Then, the popup
|
|
|
|
// windows of IME (E.g., a candidate list window) will be over the focused
|
|
|
|
// view. See http://developer.apple.com/technotes/tn2005/tn2128.html#TNTAG1
|
|
|
|
NSInteger TSMLevel, windowLevel;
|
|
|
|
UInt32 size = sizeof(TSMLevel);
|
|
|
|
|
|
|
|
OSStatus err =
|
|
|
|
::TSMGetDocumentProperty(sDocumentID, kTSMDocumentWindowLevelPropertyTag,
|
|
|
|
size, &size, &TSMLevel);
|
|
|
|
windowLevel = [[aViewForCaret window] level];
|
|
|
|
|
|
|
|
// Chinese IMEs on 10.5 don't work fine if the level is NSNormalWindowLevel,
|
|
|
|
// then, we need to increment the value.
|
|
|
|
if (windowLevel == NSNormalWindowLevel)
|
|
|
|
windowLevel++;
|
|
|
|
|
|
|
|
if (err == noErr && TSMLevel >= windowLevel)
|
|
|
|
return;
|
|
|
|
::TSMSetDocumentProperty(sDocumentID, kTSMDocumentWindowLevelPropertyTag,
|
|
|
|
sizeof(windowLevel), &windowLevel);
|
|
|
|
|
|
|
|
// ATOK (Japanese IME) updates the window level at activating,
|
|
|
|
// we need to notify the change with this hack.
|
|
|
|
::DeactivateTSMDocument(sDocumentID);
|
|
|
|
::ActivateTSMDocument(sDocumentID);
|
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-15 06:43:55 -07:00
|
|
|
void
|
|
|
|
nsTSMManager::StartComposing(NSView<mozView>* aComposingView)
|
|
|
|
{
|
|
|
|
if (sComposingView && sComposingView != sComposingView)
|
|
|
|
CommitIME();
|
|
|
|
sComposingView = aComposingView;
|
2008-08-11 01:27:59 -07:00
|
|
|
NS_ASSERTION(::TSMGetActiveDocument() == sDocumentID,
|
|
|
|
"We didn't initialize the TSMDocument");
|
2007-08-16 13:30:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSMManager::UpdateComposing(NSString* aComposingString)
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2008-04-17 05:57:44 -07:00
|
|
|
if (sComposingString)
|
|
|
|
[sComposingString release];
|
|
|
|
sComposingString = [aComposingString retain];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-04-15 06:43:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSMManager::EndComposing()
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-04-15 06:43:55 -07:00
|
|
|
sComposingView = nsnull;
|
2007-08-16 13:30:50 -07:00
|
|
|
if (sComposingString) {
|
|
|
|
[sComposingString release];
|
|
|
|
sComposingString = nsnull;
|
|
|
|
}
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-04-15 06:43:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSMManager::EnableIME(PRBool aEnable)
|
|
|
|
{
|
|
|
|
if (aEnable == sIsIMEEnabled)
|
|
|
|
return;
|
|
|
|
CommitIME();
|
|
|
|
sIsIMEEnabled = aEnable;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSMManager::SetIMEOpenState(PRBool aOpen)
|
|
|
|
{
|
|
|
|
if (aOpen == GetIMEOpenState())
|
|
|
|
return;
|
|
|
|
CommitIME();
|
|
|
|
KeyScript(aOpen ? smKeySwapScript : smKeyRoman);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define ENABLE_ROMAN_KYBDS_ONLY -23
|
|
|
|
void
|
|
|
|
nsTSMManager::SetRomanKeyboardsOnly(PRBool aRomanOnly)
|
|
|
|
{
|
|
|
|
if (aRomanOnly == sIsRomanKeyboardsOnly)
|
|
|
|
return;
|
|
|
|
CommitIME();
|
|
|
|
KeyScript(aRomanOnly ? ENABLE_ROMAN_KYBDS_ONLY : smKeyEnableKybds);
|
|
|
|
sIsRomanKeyboardsOnly = aRomanOnly;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2007-08-16 13:30:50 -07:00
|
|
|
nsTSMManager::KillComposing()
|
2007-04-15 06:43:55 -07:00
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-08-16 13:30:50 -07:00
|
|
|
// Force commit the current composition
|
|
|
|
// XXX Don't use NSInputManager. Because it cannot control the non-forcused
|
|
|
|
// input manager, therefore, on deactivating a window, it does not work fine.
|
|
|
|
NS_ASSERTION(sDocumentID, "The TSMDocumentID is null");
|
|
|
|
::FixTSMDocument(sDocumentID);
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-08-16 13:30:50 -07:00
|
|
|
}
|
2007-04-15 06:43:55 -07:00
|
|
|
|
|
|
|
|
2007-08-16 13:30:50 -07:00
|
|
|
void
|
|
|
|
nsTSMManager::CommitIME()
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-08-16 13:30:50 -07:00
|
|
|
if (!IsComposing())
|
|
|
|
return;
|
|
|
|
KillComposing();
|
|
|
|
if (!IsComposing())
|
|
|
|
return;
|
|
|
|
// If the composing transaction is still there, KillComposing only kills the
|
|
|
|
// composing in TSM. We also need to kill the our composing transaction too.
|
|
|
|
NSAttributedString* str =
|
|
|
|
[[NSAttributedString alloc] initWithString:sComposingString];
|
|
|
|
[sComposingView insertText:str];
|
|
|
|
[str release];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-04-15 06:43:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTSMManager::CancelIME()
|
|
|
|
{
|
2008-02-20 15:47:05 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-08-16 13:30:50 -07:00
|
|
|
if (!IsComposing())
|
2007-04-15 06:43:55 -07:00
|
|
|
return;
|
2007-08-16 13:30:50 -07:00
|
|
|
// For canceling the current composing, we need to ignore the param of
|
|
|
|
// insertText. But this code is ugly...
|
|
|
|
sIgnoreCommit = PR_TRUE;
|
|
|
|
KillComposing();
|
|
|
|
sIgnoreCommit = PR_FALSE;
|
|
|
|
if (!IsComposing())
|
|
|
|
return;
|
|
|
|
// If the composing transaction is still there, KillComposing only kills the
|
|
|
|
// composing in TSM. We also need to kill the our composing transaction too.
|
|
|
|
NSAttributedString* str = [[NSAttributedString alloc] initWithString:@""];
|
|
|
|
[sComposingView insertText:str];
|
|
|
|
[str release];
|
2008-02-20 15:47:05 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-04-15 06:43:55 -07:00
|
|
|
}
|
2008-06-30 09:30:22 -07:00
|
|
|
|
|
|
|
|
|
|
|
// Target for text services events sent as the result of calls made to
|
|
|
|
// TSMProcessRawKeyEvent() in [ChildView keyDown:] (above) when a plugin has
|
|
|
|
// the focus. The calls to TSMProcessRawKeyEvent() short-circuit Cocoa-based
|
|
|
|
// IME (which would otherwise interfere with our efforts) and allow Carbon-
|
|
|
|
// based IME to work in plugins (via the NPAPI). This strategy doesn't cause
|
|
|
|
// trouble for plugins that (like the Java Embedding Plugin) bypass the NPAPI
|
|
|
|
// to get their keyboard events and do their own Cocoa-based IME.
|
|
|
|
OSStatus PluginKeyEventsHandler(EventHandlerCallRef inHandlerRef,
|
|
|
|
EventRef inEvent, void *userData)
|
|
|
|
{
|
|
|
|
id arp = [[NSAutoreleasePool alloc] init];
|
|
|
|
|
|
|
|
TSMDocumentID activeDoc = ::TSMGetActiveDocument();
|
|
|
|
if (!activeDoc) {
|
|
|
|
[arp release];
|
|
|
|
return eventNotHandledErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ChildView *target = nil;
|
|
|
|
OSStatus status = ::TSMGetDocumentProperty(activeDoc, kFocusedChildViewTSMDocPropertyTag,
|
|
|
|
sizeof(ChildView *), nil, &target);
|
|
|
|
if (status != noErr)
|
|
|
|
target = nil;
|
|
|
|
if (!target) {
|
|
|
|
[arp release];
|
|
|
|
return eventNotHandledErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
EventRef keyEvent = NULL;
|
|
|
|
status = ::GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent,
|
|
|
|
typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
|
|
|
|
if ((status != noErr) || !keyEvent) {
|
|
|
|
[arp release];
|
|
|
|
return eventNotHandledErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
[target processPluginKeyEvent:keyEvent];
|
|
|
|
|
|
|
|
[arp release];
|
|
|
|
return noErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static EventHandlerRef gPluginKeyEventsHandler = NULL;
|
|
|
|
|
|
|
|
// Called from nsAppShell::Init()
|
|
|
|
void NS_InstallPluginKeyEventsHandler()
|
|
|
|
{
|
|
|
|
if (gPluginKeyEventsHandler)
|
|
|
|
return;
|
|
|
|
static const EventTypeSpec sTSMEvents[] =
|
|
|
|
{ { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } };
|
|
|
|
::InstallEventHandler(::GetEventDispatcherTarget(),
|
|
|
|
::NewEventHandlerUPP(PluginKeyEventsHandler),
|
|
|
|
GetEventTypeCount(sTSMEvents),
|
|
|
|
sTSMEvents,
|
|
|
|
NULL,
|
|
|
|
&gPluginKeyEventsHandler);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called from nsAppShell::Exit()
|
|
|
|
void NS_RemovePluginKeyEventsHandler()
|
|
|
|
{
|
|
|
|
if (!gPluginKeyEventsHandler)
|
|
|
|
return;
|
|
|
|
::RemoveEventHandler(gPluginKeyEventsHandler);
|
|
|
|
gPluginKeyEventsHandler = NULL;
|
|
|
|
}
|