diff options
Diffstat (limited to 'widget/cocoa/ComplexTextInputPanel.mm')
-rw-r--r-- | widget/cocoa/ComplexTextInputPanel.mm | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/widget/cocoa/ComplexTextInputPanel.mm b/widget/cocoa/ComplexTextInputPanel.mm new file mode 100644 index 0000000000..a4b58955e2 --- /dev/null +++ b/widget/cocoa/ComplexTextInputPanel.mm @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Modified by Josh Aas of Mozilla Corporation. + */ + +#import "ComplexTextInputPanel.h" + +#import <Cocoa/Cocoa.h> + +#include <algorithm> +#include "mozilla/Preferences.h" +#include "nsChildView.h" + +using namespace mozilla; + +extern "C" OSStatus TSMProcessRawKeyEvent(EventRef anEvent); + +#define kInputWindowHeight 20 + +@interface ComplexTextInputPanelImpl : NSPanel { + NSTextView *mInputTextView; +} + ++ (ComplexTextInputPanelImpl*)sharedComplexTextInputPanelImpl; + +- (NSTextInputContext*)inputContext; +- (void)interpretKeyEvent:(NSEvent*)event string:(NSString**)string; +- (void)cancelComposition; +- (BOOL)inComposition; + +// This places the text input panel fully onscreen and below the lower left +// corner of the focused plugin. +- (void)adjustTo:(NSPoint)point; + +@end + +@implementation ComplexTextInputPanelImpl + ++ (ComplexTextInputPanelImpl*)sharedComplexTextInputPanelImpl +{ + static ComplexTextInputPanelImpl *sComplexTextInputPanelImpl; + if (!sComplexTextInputPanelImpl) + sComplexTextInputPanelImpl = [[ComplexTextInputPanelImpl alloc] init]; + return sComplexTextInputPanelImpl; +} + +- (id)init +{ + // In the original Apple code the style mask is given by a function which is not open source. + // What could possibly be worth hiding in that function, I do not know. + // Courtesy of gdb: stylemask: 011000011111, 0x61f + self = [super initWithContentRect:NSZeroRect styleMask:0x61f backing:NSBackingStoreBuffered defer:YES]; + if (!self) + return nil; + + // Set the frame size. + NSRect visibleFrame = [[NSScreen mainScreen] visibleFrame]; + NSRect frame = NSMakeRect(visibleFrame.origin.x, visibleFrame.origin.y, visibleFrame.size.width, kInputWindowHeight); + + [self setFrame:frame display:NO]; + + mInputTextView = [[NSTextView alloc] initWithFrame:[self.contentView frame]]; + mInputTextView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable | NSViewMaxXMargin | NSViewMinXMargin | NSViewMaxYMargin | NSViewMinYMargin; + + NSScrollView* scrollView = [[NSScrollView alloc] initWithFrame:[self.contentView frame]]; + scrollView.documentView = mInputTextView; + self.contentView = scrollView; + [scrollView release]; + + [self setFloatingPanel:YES]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardInputSourceChanged:) + name:NSTextInputContextKeyboardSelectionDidChangeNotification + object:nil]; + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [mInputTextView release]; + + [super dealloc]; +} + +- (void)keyboardInputSourceChanged:(NSNotification *)notification +{ + static int8_t sDoCancel = -1; + if (!sDoCancel || ![self inComposition]) { + return; + } + if (sDoCancel < 0) { + bool cancelComposition = false; + static const char* kPrefName = + "ui.plugin.cancel_composition_at_input_source_changed"; + nsresult rv = Preferences::GetBool(kPrefName, &cancelComposition); + NS_ENSURE_SUCCESS(rv, ); + sDoCancel = cancelComposition ? 1 : 0; + } + if (sDoCancel) { + [self cancelComposition]; + } +} + +- (void)interpretKeyEvent:(NSEvent*)event string:(NSString**)string +{ + *string = nil; + + if (![[mInputTextView inputContext] handleEvent:event]) { + return; + } + + if ([mInputTextView hasMarkedText]) { + // Don't show the input method window for dead keys + if ([[event characters] length] > 0) { + [self orderFront:nil]; + } + return; + } else { + [self orderOut:nil]; + + NSString *text = [[mInputTextView textStorage] string]; + if ([text length] > 0) { + *string = [[text copy] autorelease]; + } + } + + [mInputTextView setString:@""]; +} + +- (NSTextInputContext*)inputContext +{ + return [mInputTextView inputContext]; +} + +- (void)cancelComposition +{ + [mInputTextView setString:@""]; + [self orderOut:nil]; +} + +- (BOOL)inComposition +{ + return [mInputTextView hasMarkedText]; +} + +- (void)adjustTo:(NSPoint)point +{ + NSRect selfRect = [self frame]; + NSRect rect = NSMakeRect(point.x, + point.y - selfRect.size.height, + 500, + selfRect.size.height); + + // Adjust to screen. + NSRect screenRect = [[NSScreen mainScreen] visibleFrame]; + if (rect.origin.x < screenRect.origin.x) { + rect.origin.x = screenRect.origin.x; + } + if (rect.origin.y < screenRect.origin.y) { + rect.origin.y = screenRect.origin.y; + } + CGFloat xMostOfScreen = screenRect.origin.x + screenRect.size.width; + CGFloat yMostOfScreen = screenRect.origin.y + screenRect.size.height; + CGFloat xMost = rect.origin.x + rect.size.width; + CGFloat yMost = rect.origin.y + rect.size.height; + if (xMostOfScreen < xMost) { + rect.origin.x -= xMost - xMostOfScreen; + } + if (yMostOfScreen < yMost) { + rect.origin.y -= yMost - yMostOfScreen; + } + + [self setFrame:rect display:[self isVisible]]; +} + +@end + +class ComplexTextInputPanelPrivate : public ComplexTextInputPanel +{ +public: + ComplexTextInputPanelPrivate(); + + virtual void InterpretKeyEvent(void* aEvent, nsAString& aOutText); + virtual bool IsInComposition(); + virtual void PlacePanel(int32_t x, int32_t y); + virtual void* GetInputContext() { return [mPanel inputContext]; } + virtual void CancelComposition() { [mPanel cancelComposition]; } + +private: + ~ComplexTextInputPanelPrivate(); + ComplexTextInputPanelImpl* mPanel; +}; + +ComplexTextInputPanelPrivate::ComplexTextInputPanelPrivate() +{ + mPanel = [[ComplexTextInputPanelImpl alloc] init]; +} + +ComplexTextInputPanelPrivate::~ComplexTextInputPanelPrivate() +{ + [mPanel release]; +} + +ComplexTextInputPanel* +ComplexTextInputPanel::GetSharedComplexTextInputPanel() +{ + static ComplexTextInputPanelPrivate *sComplexTextInputPanelPrivate; + if (!sComplexTextInputPanelPrivate) { + sComplexTextInputPanelPrivate = new ComplexTextInputPanelPrivate(); + } + return sComplexTextInputPanelPrivate; +} + +void +ComplexTextInputPanelPrivate::InterpretKeyEvent(void* aEvent, nsAString& aOutText) +{ + NSString* textString = nil; + [mPanel interpretKeyEvent:(NSEvent*)aEvent string:&textString]; + + if (textString) { + nsCocoaUtils::GetStringForNSString(textString, aOutText); + } +} + +bool +ComplexTextInputPanelPrivate::IsInComposition() +{ + return !![mPanel inComposition]; +} + +void +ComplexTextInputPanelPrivate::PlacePanel(int32_t x, int32_t y) +{ + [mPanel adjustTo:NSMakePoint(x, y)]; +} |