From 3c621d4c4e3991855a20206383c6bf25f4e18376 Mon Sep 17 00:00:00 2001 From: "smfr%smfr.org" Date: Sun, 26 Jun 2005 03:25:52 +0000 Subject: [PATCH] Fix bug 182783, bug 292473 and bug 280894. Add a per-window class that registers for window activate/deactivate notifications, and sends appropriate activate events to gecko. Also fix up interaction with the NSInputManager (aka IME/TSM). r=pinkerton. Only affects Camino. --- widget/src/cocoa/Makefile.in | 1 + widget/src/cocoa/nsChildView.h | 5 + widget/src/cocoa/nsChildView.mm | 184 +++++++++++++++++++------------- widget/src/cocoa/nsWindowMap.h | 90 ++++++++++++++++ widget/src/cocoa/nsWindowMap.mm | 173 ++++++++++++++++++++++++++++++ 5 files changed, 381 insertions(+), 72 deletions(-) create mode 100644 widget/src/cocoa/nsWindowMap.h create mode 100644 widget/src/cocoa/nsWindowMap.mm diff --git a/widget/src/cocoa/Makefile.in b/widget/src/cocoa/Makefile.in index 9910a311419..9225fe8c0f6 100644 --- a/widget/src/cocoa/Makefile.in +++ b/widget/src/cocoa/Makefile.in @@ -111,6 +111,7 @@ CMMSRCS = \ nsAppShellCocoa.mm \ nsCocoaWindow.mm \ nsChildView.mm \ + nsWindowMap.mm \ nsWidgetFactory.mm \ nsNativeScrollbar.mm \ nsCursorManager.mm \ diff --git a/widget/src/cocoa/nsChildView.h b/widget/src/cocoa/nsChildView.h index 15536ebf6da..aa7af2e9cb9 100644 --- a/widget/src/cocoa/nsChildView.h +++ b/widget/src/cocoa/nsChildView.h @@ -102,6 +102,11 @@ class nsChildView; nscoord mHandScrollStartScrollX, mHandScrollStartScrollY; } +// these are sent to the first responder when the window key status +// changes +- (void)viewsWindowDidBecomeKey; +- (void)viewsWindowDidResignKey; + @end diff --git a/widget/src/cocoa/nsChildView.mm b/widget/src/cocoa/nsChildView.mm index 2fe5a918dc7..9d9ee47ab69 100644 --- a/widget/src/cocoa/nsChildView.mm +++ b/widget/src/cocoa/nsChildView.mm @@ -35,22 +35,19 @@ * * ***** END LICENSE BLOCK ***** */ +#include + #include "nsChildView.h" -#include "nsIFontMetrics.h" -#include "nsIDeviceContext.h" + #include "nsCOMPtr.h" #include "nsToolkit.h" -#include "nsIEnumerator.h" #include "prmem.h" #include "nsCRT.h" - -#include -#include -#include -#include - #include "nsplugindefs.h" -#include "nsMacResources.h" + +#include "nsIFontMetrics.h" +#include "nsIDeviceContext.h" +#include "nsIEnumerator.h" #include "nsIRegion.h" #include "nsIRollupListener.h" #include "nsIEventSink.h" @@ -59,13 +56,10 @@ #include "nsCarbonHelpers.h" #include "nsGfxUtils.h" +#include "nsMacResources.h" -#if PINK_PROFILING -#include "profilerutils.h" -#endif - -#include -#include "nsCursorManager.h" +#import "nsCursorManager.h" +#import "nsWindowMap.h" #define NSAppKitVersionNumber10_2 663 @@ -80,8 +74,7 @@ @end - - +//#define DEBUG_IME 1 @interface ChildView(Private) @@ -109,6 +102,7 @@ modifiers:(unsigned int)inMods toGeckoEvent:(nsInputEvent*)outGeckoEvent; - (NSMenu*)getContextMenu; +- (TopLevelWindowData*)ensureWindowData; - (void)setIsPluginView:(BOOL)aIsPlugin; - (BOOL)getIsPluginView; @@ -125,6 +119,7 @@ @end +#pragma mark - //////////////////////////////////////////////////// nsIRollupListener * gRollupListener = nsnull; @@ -435,6 +430,11 @@ nsresult nsChildView::StandardCreate(nsIWidget *aParent, mInWindow = PR_FALSE; } + // if this is a ChildView, make sure that our per-window data + // is set up + if ([mView isKindOfClass:[ChildView class]]) + [mView ensureWindowData]; + return NS_OK; } @@ -2044,45 +2044,52 @@ NS_IMETHODIMP nsChildView::GetAttention(PRInt32 aCycleCount) #pragma mark - - +// +// Force Input Method Editor to commit the uncommited input +// NS_IMETHODIMP nsChildView::ResetInputState() { -#if 0 - // currently, the nsMacEventHandler is owned by nsCocoaWindow, which is the top level window - // we delegate this call to its parent - nsCOMPtr parent = getter_AddRefs(GetParent()); - NS_ASSERTION(parent, "cannot get parent"); - if(parent) - { - nsCOMPtr kb = do_QueryInterface(parent); - NS_ASSERTION(kb, "cannot get parent"); - if(kb) { - return kb->ResetInputState(); - } - } +#ifdef DEBUG_IME + NSLog(@"**** ResetInputState"); #endif - return NS_ERROR_ABORT; + + NSInputManager *currentIM = [NSInputManager currentInputManager]; + + // commit the current text + [currentIM unmarkText]; + + // and clear the input manager's string + [currentIM markedTextAbandoned:mView]; + + return NS_OK; } +// +// 'open' means that it can take non-ASCII chars +// NS_IMETHODIMP nsChildView::SetIMEOpenState(PRBool aState) { return NS_ERROR_NOT_IMPLEMENTED; } +// +// 'open' means that it can take non-ASCII chars +// NS_IMETHODIMP nsChildView::GetIMEOpenState(PRBool* aState) { return NS_ERROR_NOT_IMPLEMENTED; } +// +// Destruct and don't commit the IME composition string. +// NS_IMETHODIMP nsChildView::CancelIMEComposition() { #ifdef DEBUG_IME - NSLog(@"**** CancelIMEComposition\n"); + NSLog(@"**** CancelIMEComposition"); #endif - // Flush InputManager's markedText NSInputManager *currentIM = [NSInputManager currentInputManager]; [currentIM markedTextAbandoned:mView]; - [currentIM unmarkText]; return NS_OK; } @@ -2633,7 +2640,8 @@ nsChildView::Idle() // the widget. mGeckoChild->DispatchMouseEvent(geckoEvent); -} // mouseDown + // XXX maybe call markedTextSelectionChanged:client: here? +} - (void)mouseUp:(NSEvent *)theEvent @@ -2662,7 +2670,7 @@ nsChildView::Idle() // the widget. mGeckoChild->DispatchMouseEvent(geckoEvent); -} // mouseUp +} - (void)mouseMoved:(NSEvent*)theEvent { @@ -2721,6 +2729,8 @@ nsChildView::Idle() // send event into Gecko by going directly to the // the widget. mGeckoChild->DispatchMouseEvent(geckoEvent); + + // XXX maybe call markedTextSelectionChanged:client: here? } - (void)mouseEntered:(NSEvent*)theEvent @@ -2866,6 +2876,20 @@ const PRInt32 kNumLines = 4; return [[self superview] getContextMenu]; } +- (TopLevelWindowData*)ensureWindowData +{ + 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; +} + // // -convert:message:toGeckoEvent: // @@ -2968,7 +2992,7 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (nsRect)sendCompositionEvent:(PRInt32) aEventType { #ifdef DEBUG_IME - NSLog(@"****in sendCompositionEvent; type = %d\n", aEventType); + NSLog(@"****in sendCompositionEvent; type = %d", aEventType); #endif // static void init_composition_event( *aEvent, int aType) @@ -2985,8 +3009,8 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac doCommit:(BOOL) doCommit { #ifdef DEBUG_IME - NSLog(@"****in sendTextEvent; string = %@\n", aString); - NSLog(@" markRange = %d, %d; selRange = %d, %d\n", markRange.location, markRange.length, selRange.location, selRange.length); + NSLog(@"****in sendTextEvent; string = %@", aString); + NSLog(@" markRange = %d, %d; selRange = %d, %d", markRange.location, markRange.length, selRange.location, selRange.length); #endif nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, mGeckoChild); @@ -3007,13 +3031,13 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (void)insertText:(id)insertString { #if DEBUG_IME - NSLog(@"****in insertText: %@\n", insertString); - NSLog(@" markRange = %d, %d; selRange = %d, %d\n", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length); + NSLog(@"****in insertText: %@", insertString); + NSLog(@" markRange = %d, %d; selRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length); #endif if ( ! [insertString isKindOfClass:[NSAttributedString class]]) insertString = [[[NSAttributedString alloc] initWithString:insertString] autorelease]; - + NSString *tmpStr = [insertString string]; unsigned int len = [tmpStr length]; PRUnichar buffer[MAX_BUFFER_SIZE]; @@ -3078,9 +3102,9 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange { #if DEBUG_IME - NSLog(@"****in setMarkedText location: %d, length: %d\n", selRange.location, selRange.length); - NSLog(@" markRange = %d, %d; selRange = %d, %d\n", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length); - NSLog(@" aString = %@\n", aString); + NSLog(@"****in setMarkedText location: %d, length: %d", selRange.location, selRange.length); + NSLog(@" markRange = %d, %d; selRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length); + NSLog(@" aString = %@", aString); #endif if ( ![aString isKindOfClass:[NSAttributedString class]] ) @@ -3128,9 +3152,9 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (void) unmarkText { #if DEBUG_IME - NSLog(@"****in unmarkText\n"); - NSLog(@" markedRange = %d, %d\n", mMarkedRange.location, mMarkedRange.length); - NSLog(@" selectedRange = %d, %d\n", mSelectedRange.location, mSelectedRange.length); + NSLog(@"****in unmarkText"); + NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length); + NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length); #endif mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0); @@ -3142,7 +3166,7 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (BOOL) hasMarkedText { - return mMarkedRange.location != NSNotFound && mMarkedRange.length != 0; + return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0); } - (long) conversationIdentifier @@ -3153,10 +3177,10 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange { #if DEBUG_IME - NSLog(@"****in attributedSubstringFromRange\n"); - NSLog(@" theRange = %d, %d\n", theRange.location, theRange.length); - NSLog(@" markedRange = %d, %d\n", mMarkedRange.location, mMarkedRange.length); - NSLog(@" selectedRange = %d, %d\n", mSelectedRange.location, mSelectedRange.length); + NSLog(@"****in attributedSubstringFromRange"); + NSLog(@" theRange = %d, %d", theRange.location, theRange.length); + NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length); + NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length); #endif nsReconversionEvent reconversionEvent(PR_TRUE, NS_RECONVERSION_QUERY, @@ -3178,9 +3202,9 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (NSRange) markedRange { #if DEBUG_IME - NSLog(@"****in markedRange\n"); - NSLog(@" markedRange = %d, %d\n", mMarkedRange.location, mMarkedRange.length); - NSLog(@" selectedRange = %d, %d\n", mSelectedRange.location, mSelectedRange.length); + NSLog(@"****in markedRange"); + NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length); + NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length); #endif if (![self hasMarkedText]) { @@ -3193,9 +3217,9 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (NSRange) selectedRange { #if DEBUG_IME - NSLog(@"****in selectedRange\n"); - NSLog(@" markedRange = %d, %d\n", mMarkedRange.location, mMarkedRange.length); - NSLog(@" selectedRange = %d, %d\n", mSelectedRange.location, mSelectedRange.length); + NSLog(@"****in selectedRange"); + NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length); + NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length); #endif return mSelectedRange; @@ -3205,10 +3229,10 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (NSRect) firstRectForCharacterRange:(NSRange)theRange { #if DEBUG_IME - NSLog(@"****in firstRectForCharacterRange\n"); - NSLog(@" theRange = %d, %d\n", theRange.location, theRange.length); - NSLog(@" markedRange = %d, %d\n", mMarkedRange.location, mMarkedRange.length); - NSLog(@" selectedRange = %d, %d\n", mSelectedRange.location, mSelectedRange.length); + NSLog(@"****in firstRectForCharacterRange"); + NSLog(@" theRange = %d, %d", theRange.location, theRange.length); + NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length); + NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length); #endif #if BRADE_GETS_THIS_WORKING @@ -3226,7 +3250,7 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac #endif #if DEBUG_IME - NSLog(@"********** cocoa rect (x, y, w, h): %f %f, %f, %f\n", temp.origin.x, temp.origin.y, temp.size.width, temp.size.height); + NSLog(@"********** cocoa rect (x, y, w, h): %f %f, %f, %f", temp.origin.x, temp.origin.y, temp.size.width, temp.size.height); #endif return temp; } @@ -3235,8 +3259,8 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (unsigned int)characterIndexForPoint:(NSPoint)thePoint { #if DEBUG_IME - NSLog(@"****in characterIndexForPoint\n"); - NSLog(@" markRange = %d, %d; selectRange = %d, %d\n", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length); + NSLog(@"****in characterIndexForPoint"); + NSLog(@" markRange = %d, %d; selectRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length); #endif // short regionClass; @@ -3247,8 +3271,8 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (NSArray*) validAttributesForMarkedText { #if DEBUG_IME - NSLog(@"****in validAttributesForMarkedText\n"); - NSLog(@" markRange = %d, %d; selectRange = %d, %d\n", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length); + NSLog(@"****in validAttributesForMarkedText"); + NSLog(@" markRange = %d, %d; selectRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length); #endif return [NSArray array]; // empty array; we don't support any attributes right now @@ -3408,8 +3432,8 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (BOOL)becomeFirstResponder { nsFocusEvent event(PR_TRUE, NS_GOTFOCUS, mGeckoChild); - mGeckoChild->DispatchWindowEvent(event); + return [super becomeFirstResponder]; } @@ -3417,12 +3441,28 @@ static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& mac - (BOOL)resignFirstResponder { nsFocusEvent event(PR_TRUE, NS_LOSTFOCUS, mGeckoChild); - mGeckoChild->DispatchWindowEvent(event); return [super resignFirstResponder]; } +- (void)viewsWindowDidBecomeKey +{ + nsFocusEvent focusEvent(PR_TRUE, NS_GOTFOCUS, mGeckoChild); + mGeckoChild->DispatchWindowEvent(focusEvent); + + nsFocusEvent activateEvent(PR_TRUE, NS_ACTIVATE, mGeckoChild); + mGeckoChild->DispatchWindowEvent(activateEvent); + +} + +- (void)viewsWindowDidResignKey +{ + nsFocusEvent event(PR_TRUE, NS_LOSTFOCUS, mGeckoChild); + mGeckoChild->DispatchWindowEvent(event); +} + + //------------------------------------------------------------------------- // // ConvertMacToRaptorKeyCode diff --git a/widget/src/cocoa/nsWindowMap.h b/widget/src/cocoa/nsWindowMap.h new file mode 100644 index 00000000000..5b9530cc50d --- /dev/null +++ b/widget/src/cocoa/nsWindowMap.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; 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): + * Simon Fraser + * + * 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 ***** */ + +#import + +// +// WindowDataMap +// +// In both mozilla and embedding apps, we need to have a place to put +// per-top-level-window logic and data, to handle such things as IME +// commit when the window gains/loses focus. We can't use a window +// delegate, because an embeddor probably already has one. Nor can we +// subclass NSWindow, again because we can't impose that burden on the +// embeddor. +// +// So we have a global map of NSWindow -> TopLevelWindowData, and set +// up TopLevelWindowData as a notification observer etc. +// + +@interface WindowDataMap : NSObject +{ +@private + NSMutableDictionary* mWindowMap; // dict of TopLevelWindowData keyed by address of NSWindow +} + ++ (WindowDataMap*)sharedWindowDataMap; + +- (id)dataForWindow:(NSWindow*)inWindow; + +// set data for a given window. inData is retained (and any previously set data +// is released). +- (void)setData:(id)inData forWindow:(NSWindow*)inWindow; + +// remove the data for the given window. the data is released. +- (void)removeDataForWindow:(NSWindow*)inWindow; + +@end + + +// +// TopLevelWindowData +// +// Class to hold per-window data, and handle window state changes. +// + +@interface TopLevelWindowData : NSObject +{ +@private + +} + +- (id)initWithWindow:(NSWindow*)inWindow; + +@end + diff --git a/widget/src/cocoa/nsWindowMap.mm b/widget/src/cocoa/nsWindowMap.mm new file mode 100644 index 00000000000..1a00ad205b8 --- /dev/null +++ b/widget/src/cocoa/nsWindowMap.mm @@ -0,0 +1,173 @@ +/* -*- Mode: C++; 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): + * Simon Fraser + * + * 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 ***** */ + +#import "nsWindowMap.h" + +#import "nsChildView.h" + +@interface WindowDataMap(Private) + +- (NSString*)keyForWindow:(NSWindow*)inWindow; + +@end + +@interface TopLevelWindowData(Private) + +- (void)windowResignedKey:(NSNotification*)inNotification; +- (void)windowBecameKey:(NSNotification*)inNotification; +- (void)windowWillClose:(NSNotification*)inNotification; + +@end + +#pragma mark - + +@implementation WindowDataMap + ++ (WindowDataMap*)sharedWindowDataMap +{ + static WindowDataMap* sWindowMap = nil; + if (!sWindowMap) + sWindowMap = [[WindowDataMap alloc] init]; + + return sWindowMap; +} + +- (id)init +{ + if ((self = [super init])) + { + mWindowMap = [[NSMutableDictionary alloc] initWithCapacity:10]; + } + return self; +} + +- (void)dealloc +{ + [mWindowMap release]; + [super dealloc]; +} + +- (id)dataForWindow:(NSWindow*)inWindow +{ + return [mWindowMap objectForKey:[self keyForWindow:inWindow]]; +} + +- (void)setData:(id)inData forWindow:(NSWindow*)inWindow +{ + [mWindowMap setObject:inData forKey:[self keyForWindow:inWindow]]; +} + +- (void)removeDataForWindow:(NSWindow*)inWindow +{ + [mWindowMap removeObjectForKey:[self keyForWindow:inWindow]]; +} + +- (NSString*)keyForWindow:(NSWindow*)inWindow +{ + return [NSString stringWithFormat:@"%p", inWindow]; +} + +@end + + +// +// TopLevelWindowData +// +// This class holds data about top-level windows. We can't use a window +// delegate, because an embedder may already have one. +// + +@implementation TopLevelWindowData + +- (id)initWithWindow:(NSWindow*)inWindow +{ + if ((self = [super init])) + { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowBecameKey:) + name:NSWindowDidBecomeKeyNotification + object:inWindow]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowResignedKey:) + name:NSWindowDidResignKeyNotification + object:inWindow]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowWillClose:) + name:NSWindowWillCloseNotification + object:inWindow]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (void)windowBecameKey:(NSNotification*)inNotification +{ + id firstResponder = [[inNotification object] firstResponder]; + if ([firstResponder isKindOfClass:[ChildView class]]) + { + [firstResponder viewsWindowDidBecomeKey]; + } +} + +- (void)windowResignedKey:(NSNotification*)inNotification +{ + id firstResponder = [[inNotification object] firstResponder]; + if ([firstResponder isKindOfClass:[ChildView class]]) + { + [firstResponder viewsWindowDidResignKey]; + } +} + +- (void)windowWillClose:(NSNotification*)inNotification +{ + // postpone our destruction + [[self retain] autorelease]; + + // remove ourselves from the window map (which owns us) + [[WindowDataMap sharedWindowDataMap] removeDataForWindow:[inNotification object]]; +} + +@end +