michael@0: /* michael@0: * Copyright (C) 2009 Apple Inc. All Rights Reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY michael@0: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE michael@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR michael@0: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR michael@0: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, michael@0: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, michael@0: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR michael@0: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY michael@0: * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: * michael@0: * Modified by Josh Aas of Mozilla Corporation. michael@0: */ michael@0: michael@0: #import "ComplexTextInputPanel.h" michael@0: michael@0: #include michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsChildView.h" michael@0: #include "nsCocoaFeatures.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #define kInputWindowHeight 20 michael@0: michael@0: @implementation ComplexTextInputPanel michael@0: michael@0: + (ComplexTextInputPanel*)sharedComplexTextInputPanel michael@0: { michael@0: static ComplexTextInputPanel *sComplexTextInputPanel; michael@0: if (!sComplexTextInputPanel) michael@0: sComplexTextInputPanel = [[ComplexTextInputPanel alloc] init]; michael@0: return sComplexTextInputPanel; michael@0: } michael@0: michael@0: - (id)init michael@0: { michael@0: // In the original Apple code the style mask is given by a function which is not open source. michael@0: // What could possibly be worth hiding in that function, I do not know. michael@0: // Courtesy of gdb: stylemask: 011000011111, 0x61f michael@0: self = [super initWithContentRect:NSZeroRect styleMask:0x61f backing:NSBackingStoreBuffered defer:YES]; michael@0: if (!self) michael@0: return nil; michael@0: michael@0: // Set the frame size. michael@0: NSRect visibleFrame = [[NSScreen mainScreen] visibleFrame]; michael@0: NSRect frame = NSMakeRect(visibleFrame.origin.x, visibleFrame.origin.y, visibleFrame.size.width, kInputWindowHeight); michael@0: michael@0: [self setFrame:frame display:NO]; michael@0: michael@0: mInputTextView = [[NSTextView alloc] initWithFrame:[self.contentView frame]]; michael@0: mInputTextView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable | NSViewMaxXMargin | NSViewMinXMargin | NSViewMaxYMargin | NSViewMinYMargin; michael@0: michael@0: NSScrollView* scrollView = [[NSScrollView alloc] initWithFrame:[self.contentView frame]]; michael@0: scrollView.documentView = mInputTextView; michael@0: self.contentView = scrollView; michael@0: [scrollView release]; michael@0: michael@0: [self setFloatingPanel:YES]; michael@0: michael@0: [[NSNotificationCenter defaultCenter] addObserver:self michael@0: selector:@selector(keyboardInputSourceChanged:) michael@0: name:NSTextInputContextKeyboardSelectionDidChangeNotification michael@0: object:nil]; michael@0: michael@0: return self; michael@0: } michael@0: michael@0: - (void)dealloc michael@0: { michael@0: [[NSNotificationCenter defaultCenter] removeObserver:self]; michael@0: michael@0: [mInputTextView release]; michael@0: michael@0: [super dealloc]; michael@0: } michael@0: michael@0: - (void)keyboardInputSourceChanged:(NSNotification *)notification michael@0: { michael@0: static int8_t sDoCancel = -1; michael@0: if (!sDoCancel || ![self inComposition]) { michael@0: return; michael@0: } michael@0: if (sDoCancel < 0) { michael@0: bool cancelComposition = false; michael@0: static const char* kPrefName = michael@0: "ui.plugin.cancel_composition_at_input_source_changed"; michael@0: nsresult rv = Preferences::GetBool(kPrefName, &cancelComposition); michael@0: NS_ENSURE_SUCCESS(rv, ); michael@0: sDoCancel = cancelComposition ? 1 : 0; michael@0: } michael@0: if (sDoCancel) { michael@0: [self cancelComposition]; michael@0: } michael@0: } michael@0: michael@0: - (BOOL)interpretKeyEvent:(NSEvent*)event string:(NSString**)string michael@0: { michael@0: BOOL hadMarkedText = [mInputTextView hasMarkedText]; michael@0: michael@0: *string = nil; michael@0: michael@0: if (![[mInputTextView inputContext] handleEvent:event]) michael@0: return NO; michael@0: michael@0: if ([mInputTextView hasMarkedText]) { michael@0: // Don't show the input method window for dead keys michael@0: if ([[event characters] length] > 0) michael@0: [self orderFront:nil]; michael@0: michael@0: return YES; michael@0: } else { michael@0: [self orderOut:nil]; michael@0: michael@0: NSString *text = [[mInputTextView textStorage] string]; michael@0: if ([text length] > 0) michael@0: *string = [[text copy] autorelease]; michael@0: } michael@0: michael@0: [mInputTextView setString:@""]; michael@0: return hadMarkedText; michael@0: } michael@0: michael@0: - (NSTextInputContext*)inputContext michael@0: { michael@0: return [mInputTextView inputContext]; michael@0: } michael@0: michael@0: - (void)cancelComposition michael@0: { michael@0: [mInputTextView setString:@""]; michael@0: [self orderOut:nil]; michael@0: } michael@0: michael@0: - (BOOL)inComposition michael@0: { michael@0: return [mInputTextView hasMarkedText]; michael@0: } michael@0: michael@0: - (void)adjustTo:(NSView*)view michael@0: { michael@0: NSRect viewRect = [view frame]; michael@0: viewRect.origin.x = 0; michael@0: viewRect.origin.y = 0; michael@0: viewRect = [view convertRect:viewRect toView:nil]; michael@0: if (nsCocoaFeatures::OnLionOrLater()) { michael@0: viewRect = [[view window] convertRectToScreen:viewRect]; michael@0: } else { michael@0: viewRect.origin = [[view window] convertBaseToScreen:viewRect.origin]; michael@0: } michael@0: NSRect selfRect = [self frame]; michael@0: CGFloat minWidth = static_cast( michael@0: Preferences::GetUint("ui.plugin.panel.min-width", 500)); michael@0: NSRect rect = NSMakeRect(viewRect.origin.x, michael@0: viewRect.origin.y - selfRect.size.height, michael@0: std::max(viewRect.size.width, minWidth), michael@0: selfRect.size.height); michael@0: michael@0: // Adjust to screen. michael@0: NSRect screenRect = [[NSScreen mainScreen] visibleFrame]; michael@0: if (rect.origin.x < screenRect.origin.x) { michael@0: rect.origin.x = screenRect.origin.x; michael@0: } michael@0: if (rect.origin.y < screenRect.origin.y) { michael@0: rect.origin.y = screenRect.origin.y; michael@0: } michael@0: CGFloat xMostOfScreen = screenRect.origin.x + screenRect.size.width; michael@0: CGFloat yMostOfScreen = screenRect.origin.y + screenRect.size.height; michael@0: CGFloat xMost = rect.origin.x + rect.size.width; michael@0: CGFloat yMost = rect.origin.y + rect.size.height; michael@0: if (xMostOfScreen < xMost) { michael@0: rect.origin.x -= xMost - xMostOfScreen; michael@0: } michael@0: if (yMostOfScreen < yMost) { michael@0: rect.origin.y -= yMost - yMostOfScreen; michael@0: } michael@0: michael@0: [self setFrame:rect display:[self isVisible]]; michael@0: } michael@0: michael@0: @end